Add Markdown import from Obsidian (Stage 8)
- md-to-tiptap.ts: remark-based converter (headings, lists, blockquotes, code blocks, bold/italic/strike, links, images, hr) - Obsidian ![[wikilink]] stripped, [[link|alias]] → plain text - POST /api/admin/import-md: parses frontmatter (gray-matter) + converts content - LessonEditor: "Импорт .md" button populates editor without auto-save - ROADMAP: marked Stages 2, 3, 5, 6, 7, 8 as complete, fixed numbering Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { headers } from "next/headers";
|
||||
import { auth } from "@/lib/auth";
|
||||
import matter from "gray-matter";
|
||||
import { mdToTiptap } from "@/lib/md-to-tiptap";
|
||||
|
||||
export async function POST(req: NextRequest) {
|
||||
const session = await auth.api.getSession({ headers: await headers() });
|
||||
if (!session || session.user.role !== "admin") {
|
||||
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||
}
|
||||
|
||||
const formData = await req.formData();
|
||||
const file = formData.get("file") as File | null;
|
||||
|
||||
if (!file) {
|
||||
return NextResponse.json({ error: "No file provided" }, { status: 400 });
|
||||
}
|
||||
if (!file.name.endsWith(".md")) {
|
||||
return NextResponse.json({ error: "Only .md files are supported" }, { status: 400 });
|
||||
}
|
||||
|
||||
const raw = await file.text();
|
||||
const { data: fm, content } = matter(raw);
|
||||
|
||||
// Extract known frontmatter fields (Obsidian-compatible naming)
|
||||
const title =
|
||||
typeof fm.title === "string" ? fm.title.trim() : null;
|
||||
const kinescopeId =
|
||||
(fm.kinescopeId ?? fm.kinescope_id ?? fm.videoId ?? fm.video_id ?? "") as string;
|
||||
const order =
|
||||
typeof fm.order === "number" ? fm.order : null;
|
||||
const published =
|
||||
typeof fm.published === "boolean" ? fm.published : null;
|
||||
|
||||
const tiptapContent = mdToTiptap(content);
|
||||
|
||||
return NextResponse.json({ title, kinescopeId, order, published, content: tiptapContent });
|
||||
}
|
||||
Reference in New Issue
Block a user