159 lines
4.6 KiB
TypeScript
159 lines
4.6 KiB
TypeScript
import { NextResponse } from "next/server";
|
|||
|
|
import { createSupabaseRouteClient } from "@/lib/supabase/server";
|
||
|
|
import { promises as fs } from "fs";
|
||
|
|
import path from "path";
|
||
|
|
|
||
|
|
const defaultMindmapData = {
|
||
|
|
data: { text: "中心主题" },
|
||
|
|
children: [],
|
||
|
|
};
|
||
|
|
|
||
|
|
const documentsBaseDir = path.join(process.cwd(), "public", "documents");
|
||
|
|
|
||
|
|
async function ensureDir(dir: string) {
|
||
|
|
await fs.mkdir(dir, { recursive: true });
|
||
|
|
}
|
||
|
|
|
||
|
|
async function removeFileSafe(file: string) {
|
||
|
|
try {
|
||
|
|
await fs.rm(file, { force: true });
|
||
|
|
} catch {
|
||
|
|
// ignore
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
async function ensureIndexFile(folder: string, title = "无标题") {
|
||
|
|
const indexFile = path.join(folder, "index.md");
|
||
|
|
try {
|
||
|
|
await fs.access(indexFile);
|
||
|
|
} catch {
|
||
|
|
await fs.writeFile(indexFile, `# ${title}\n`, "utf8");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function resolveMindmapFileName(mindmapId: string) {
|
||
|
|
if (mindmapId === "legacy" || mindmapId.startsWith("legacy-")) {
|
||
|
|
return "mindmap.json";
|
||
|
|
}
|
||
|
|
return `mindmap-${mindmapId}.json`;
|
||
|
|
}
|
||
|
|
|
||
|
|
async function tryReadJson(file: string) {
|
||
|
|
try {
|
||
|
|
const content = await fs.readFile(file, "utf8");
|
||
|
|
return JSON.parse(content);
|
||
|
|
} catch {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function GET(
|
||
|
|
_req: Request,
|
||
|
|
{ params }: { params: Promise<{ docId: string; mindmapId: string }> },
|
||
|
|
) {
|
||
|
|
const { docId, mindmapId } = await params;
|
||
|
|
const supabase = await createSupabaseRouteClient();
|
||
|
|
const {
|
||
|
|
data: { session },
|
||
|
|
} = await supabase.auth.getSession();
|
||
|
|
if (!session) {
|
||
|
|
return NextResponse.json({ error: "未登录" }, { status: 401 });
|
||
|
|
}
|
||
|
|
|
||
|
|
const folder = path.join(documentsBaseDir, docId);
|
||
|
|
const file = path.join(folder, resolveMindmapFileName(mindmapId));
|
||
|
|
const legacyFile = path.join(folder, "mindmap.json");
|
||
|
|
|
||
|
|
const localData = (await tryReadJson(file)) ?? (await tryReadJson(legacyFile));
|
||
|
|
if (localData) {
|
||
|
|
return NextResponse.json({ data: localData, source: "local" });
|
||
|
|
}
|
||
|
|
|
||
|
|
// 兼容旧版:没有本地文件时,回退到 documents.mindmap_data(仅能表示单个旧导图)
|
||
|
|
const { data, error } = await supabase
|
||
|
|
.from("documents")
|
||
|
|
.select("mindmap_data")
|
||
|
|
.eq("id", docId)
|
||
|
|
.eq("user_id", session.user.id)
|
||
|
|
.maybeSingle();
|
||
|
|
if (error) {
|
||
|
|
return NextResponse.json({ error: error.message }, { status: 400 });
|
||
|
|
}
|
||
|
|
const payload = data?.mindmap_data ?? defaultMindmapData;
|
||
|
|
return NextResponse.json({ data: payload, source: "supabase" });
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function POST(
|
||
|
|
request: Request,
|
||
|
|
{ params }: { params: Promise<{ docId: string; mindmapId: string }> },
|
||
|
|
) {
|
||
|
|
const { docId, mindmapId } = await params;
|
||
|
|
const supabase = await createSupabaseRouteClient();
|
||
|
|
const {
|
||
|
|
data: { session },
|
||
|
|
} = await supabase.auth.getSession();
|
||
|
|
if (!session) {
|
||
|
|
return NextResponse.json({ error: "未登录" }, { status: 401 });
|
||
|
|
}
|
||
|
|
|
||
|
|
// 校验页面归属(避免任意写入)
|
||
|
|
const { data: doc, error: docError } = await supabase
|
||
|
|
.from("documents")
|
||
|
|
.select("id,title")
|
||
|
|
.eq("id", docId)
|
||
|
|
.eq("user_id", session.user.id)
|
||
|
|
.maybeSingle();
|
||
|
|
if (docError) {
|
||
|
|
return NextResponse.json({ error: docError.message }, { status: 400 });
|
||
|
|
}
|
||
|
|
if (!doc) {
|
||
|
|
return NextResponse.json({ error: "页面不存在" }, { status: 404 });
|
||
|
|
}
|
||
|
|
|
||
|
|
const { data } = await request.json().catch(() => ({ data: null }));
|
||
|
|
const folder = path.join(documentsBaseDir, docId);
|
||
|
|
const file = path.join(folder, resolveMindmapFileName(mindmapId));
|
||
|
|
try {
|
||
|
|
await ensureDir(folder);
|
||
|
|
await ensureIndexFile(folder, doc.title ?? "无标题");
|
||
|
|
await fs.writeFile(file, JSON.stringify(data ?? defaultMindmapData, null, 2), "utf8");
|
||
|
|
} catch (error) {
|
||
|
|
return NextResponse.json({ error: String(error) }, { status: 500 });
|
||
|
|
}
|
||
|
|
|
||
|
|
return NextResponse.json({ ok: true });
|
||
|
|
}
|
||
|
|
|
||
|
|
export async function DELETE(
|
||
|
|
_req: Request,
|
||
|
|
{ params }: { params: Promise<{ docId: string; mindmapId: string }> },
|
||
|
|
) {
|
||
|
|
const { docId, mindmapId } = await params;
|
||
|
|
const supabase = await createSupabaseRouteClient();
|
||
|
|
const {
|
||
|
|
data: { session },
|
||
|
|
} = await supabase.auth.getSession();
|
||
|
|
if (!session) {
|
||
|
|
return NextResponse.json({ error: "未登录" }, { status: 401 });
|
||
|
|
}
|
||
|
|
|
||
|
|
// 校验页面归属
|
||
|
|
const { data: doc, error: docError } = await supabase
|
||
|
|
.from("documents")
|
||
|
|
.select("id")
|
||
|
|
.eq("id", docId)
|
||
|
|
.eq("user_id", session.user.id)
|
||
|
|
.maybeSingle();
|
||
|
|
if (docError) {
|
||
|
|
return NextResponse.json({ error: docError.message }, { status: 400 });
|
||
|
|
}
|
||
|
|
if (!doc) {
|
||
|
|
return NextResponse.json({ error: "页面不存在" }, { status: 404 });
|
||
|
|
}
|
||
|
|
|
||
|
|
const folder = path.join(documentsBaseDir, docId);
|
||
|
|
const file = path.join(folder, resolveMindmapFileName(mindmapId));
|
||
|
|
await removeFileSafe(file);
|
||
|
|
return NextResponse.json({ ok: true });
|
||
|
|
}
|