201 lines
5.7 KiB
TypeScript
201 lines
5.7 KiB
TypeScript
import { NextResponse } from "next/server";
|
|
import { createSupabaseRouteClient } from "@/lib/supabase/server";
|
|
import { promises as fs } from "fs";
|
|
import path from "path";
|
|
import { getDocumentsBaseDir, getLegacyMindmapsBaseDir } from "@/lib/server/local-paths";
|
|
import { isConvexEnabled } from "@/lib/convex/enabled";
|
|
import { getAuthedConvexClient } from "@/lib/convex/route";
|
|
import { api } from "@/lib/convex/api";
|
|
|
|
const defaultMindmapData = {
|
|
data: { text: "中心主题" },
|
|
children: [],
|
|
};
|
|
|
|
// 新版:与页面文件夹(index.md 所在处)对齐,放在 public/documents/<docId>/mindmap.json
|
|
// 旧版遗留:public/mindmaps/<docId>/mindmap.json
|
|
const preferredBaseDir = getDocumentsBaseDir();
|
|
const legacyBaseDir = getLegacyMindmapsBaseDir();
|
|
|
|
async function ensureDir(dir: string) {
|
|
await fs.mkdir(dir, { recursive: true });
|
|
}
|
|
|
|
async function removeFileSafe(file: string) {
|
|
try {
|
|
await fs.rm(file, { force: true });
|
|
} catch {
|
|
// 忽略删除失败(例如文件不存在)
|
|
}
|
|
}
|
|
|
|
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");
|
|
}
|
|
}
|
|
|
|
export async function GET(
|
|
_req: Request,
|
|
{ params }: { params: Promise<{ docId: string }> },
|
|
) {
|
|
const { docId: id } = await params;
|
|
|
|
if (isConvexEnabled()) {
|
|
const { auth, client } = getAuthedConvexClient();
|
|
const mindmapId = `legacy-${id}`;
|
|
const res = await client.query(api.mindmaps.get, {
|
|
userId: auth.userId,
|
|
docId: id,
|
|
mindmapId,
|
|
});
|
|
return NextResponse.json({ data: res?.data ?? defaultMindmapData, source: "convex" });
|
|
}
|
|
|
|
const supabase = await createSupabaseRouteClient();
|
|
const {
|
|
data: { session },
|
|
} = await supabase.auth.getSession();
|
|
if (!session) {
|
|
return NextResponse.json({ error: "未登录" }, { status: 401 });
|
|
}
|
|
|
|
const preferredFolder = path.join(preferredBaseDir, id);
|
|
const preferredFile = path.join(preferredFolder, "mindmap.json");
|
|
const legacyFolder = path.join(legacyBaseDir, id);
|
|
const legacyFile = path.join(legacyFolder, "mindmap.json");
|
|
|
|
const tryRead = async (file: string) => {
|
|
try {
|
|
const content = await fs.readFile(file, "utf8");
|
|
return JSON.parse(content);
|
|
} catch {
|
|
return null;
|
|
}
|
|
};
|
|
|
|
const localData =
|
|
(await tryRead(preferredFile)) ??
|
|
(await tryRead(legacyFile));
|
|
|
|
if (localData) {
|
|
return NextResponse.json({ data: localData, source: "local" });
|
|
}
|
|
|
|
// fallback Supabase
|
|
const { data, error } = await supabase
|
|
.from("documents")
|
|
.select("mindmap_data")
|
|
.eq("id", id)
|
|
.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 }> },
|
|
) {
|
|
const { docId: id } = await params;
|
|
|
|
if (isConvexEnabled()) {
|
|
const { auth, client } = getAuthedConvexClient();
|
|
const mindmapId = `legacy-${id}`;
|
|
const { data } = await request.json();
|
|
const result = await client.mutation(api.mindmaps.put, {
|
|
userId: auth.userId,
|
|
docId: id,
|
|
mindmapId,
|
|
data: data ?? defaultMindmapData,
|
|
});
|
|
return NextResponse.json({ ok: true, ...(result ?? {}) });
|
|
}
|
|
|
|
const supabase = await createSupabaseRouteClient();
|
|
const {
|
|
data: { session },
|
|
} = await supabase.auth.getSession();
|
|
if (!session) {
|
|
return NextResponse.json({ error: "未登录" }, { status: 401 });
|
|
}
|
|
|
|
const { data } = await request.json();
|
|
const folder = path.join(preferredBaseDir, id);
|
|
const file = path.join(folder, "mindmap.json");
|
|
try {
|
|
await ensureDir(folder);
|
|
await ensureIndexFile(folder);
|
|
await fs.writeFile(
|
|
file,
|
|
JSON.stringify(data ?? defaultMindmapData, null, 2),
|
|
"utf8",
|
|
);
|
|
} catch (error) {
|
|
return NextResponse.json({ error: String(error) }, { status: 500 });
|
|
}
|
|
|
|
const { error } = await supabase
|
|
.from("documents")
|
|
.update({ mindmap_data: data ?? defaultMindmapData })
|
|
.eq("id", id)
|
|
.eq("user_id", session.user.id);
|
|
|
|
if (error) {
|
|
return NextResponse.json({ error: error.message }, { status: 400 });
|
|
}
|
|
|
|
return NextResponse.json({ ok: true });
|
|
}
|
|
|
|
export async function DELETE(
|
|
_req: Request,
|
|
{ params }: { params: Promise<{ docId: string }> },
|
|
) {
|
|
const { docId: id } = await params;
|
|
|
|
if (isConvexEnabled()) {
|
|
const { auth, client } = getAuthedConvexClient();
|
|
const mindmapId = `legacy-${id}`;
|
|
const result = await client.mutation(api.mindmaps.softDelete, {
|
|
userId: auth.userId,
|
|
docId: id,
|
|
mindmapId,
|
|
});
|
|
return NextResponse.json({ ok: true, ...(result ?? {}) });
|
|
}
|
|
|
|
const supabase = await createSupabaseRouteClient();
|
|
const {
|
|
data: { session },
|
|
} = await supabase.auth.getSession();
|
|
if (!session) {
|
|
return NextResponse.json({ error: "未登录" }, { status: 401 });
|
|
}
|
|
|
|
const preferredFolder = path.join(preferredBaseDir, id);
|
|
const preferredFile = path.join(preferredFolder, "mindmap.json");
|
|
const legacyFolder = path.join(legacyBaseDir, id);
|
|
const legacyFile = path.join(legacyFolder, "mindmap.json");
|
|
|
|
await Promise.all([removeFileSafe(preferredFile), removeFileSafe(legacyFile)]);
|
|
|
|
const { error } = await supabase
|
|
.from("documents")
|
|
.update({ mindmap_data: null })
|
|
.eq("id", id)
|
|
.eq("user_id", session.user.id);
|
|
|
|
if (error) {
|
|
return NextResponse.json({ error: error.message }, { status: 500 });
|
|
}
|
|
|
|
return NextResponse.json({ ok: true });
|
|
}
|