Files
mnote/wolai-frontend/src/app/api/documents/duplicate/route.ts
T

119 lines
3.7 KiB
TypeScript

import { NextResponse } from "next/server";
import { createSupabaseRouteClient } from "@/lib/supabase/server";
import path from "path";
import { promises as fs } from "fs";
interface DuplicatePayload {
documentId: string;
}
const documentsBaseDir = path.join(process.cwd(), "public", "documents");
async function ensureDocumentScaffold(id: string, title: string | null) {
const folder = path.join(documentsBaseDir, id);
const indexFile = path.join(folder, "index.md");
await fs.mkdir(folder, { recursive: true });
const safeTitle = title && title.trim() ? title.trim() : "无标题";
const content = `# ${safeTitle}\n`;
await fs.writeFile(indexFile, content, "utf8");
}
async function copyMindmapIfExists(sourceId: string, targetId: string) {
try {
const srcDir = path.join(documentsBaseDir, sourceId);
const destDir = path.join(documentsBaseDir, targetId);
const entries = await fs.readdir(srcDir);
const mindmapFiles = entries.filter((name) => name === "mindmap.json" || /^mindmap-.+\\.json$/i.test(name));
if (mindmapFiles.length === 0) return;
await fs.mkdir(destDir, { recursive: true });
await Promise.all(
mindmapFiles.map(async (name) => {
try {
const buf = await fs.readFile(path.join(srcDir, name));
await fs.writeFile(path.join(destDir, name), buf);
} catch {
// ignore
}
}),
);
} catch {
// 如果源不存在则忽略
}
}
export async function POST(request: Request) {
const supabase = await createSupabaseRouteClient();
const {
data: { session },
} = await supabase.auth.getSession();
if (!session) {
return NextResponse.json({ error: "未登录" }, { status: 401 });
}
const { documentId }: DuplicatePayload = await request.json();
if (!documentId) {
return NextResponse.json({ error: "缺少 documentId" }, { status: 400 });
}
const { data: sourceDoc, error: sourceError } = await supabase
.from("documents")
.select("id,title,content,parent_id,workspace_id,access_scope")
.eq("id", documentId)
.eq("user_id", session.user.id)
.single();
if (sourceError || !sourceDoc) {
return NextResponse.json({ error: "页面不存在或无权访问" }, { status: 404 });
}
const siblingQuery = supabase
.from("documents")
.select("id", { head: true, count: "exact" })
.eq("workspace_id", sourceDoc.workspace_id);
if (sourceDoc.parent_id) {
siblingQuery.eq("parent_id", sourceDoc.parent_id);
} else {
siblingQuery.is("parent_id", null);
}
const { count: rawSiblingCount, error: countError } = await siblingQuery;
const siblingCount = rawSiblingCount ?? 0;
if (countError) {
return NextResponse.json({ error: countError.message }, { status: 500 });
}
const fallbackTitle =
sourceDoc.title?.trim() && sourceDoc.title.trim().length > 0
? sourceDoc.title.trim()
: "无标题";
const duplicatedTitle = `${fallbackTitle} 副本`;
const { data: duplicated, error: duplicateError } = await supabase
.from("documents")
.insert({
user_id: session.user.id,
workspace_id: sourceDoc.workspace_id,
parent_id: sourceDoc.parent_id,
access_scope: sourceDoc.access_scope ?? "private",
title: duplicatedTitle,
content: sourceDoc.content,
sort_order: siblingCount,
})
.select("id,title,parent_id,sort_order")
.single();
if (duplicateError || !duplicated) {
return NextResponse.json(
{ error: duplicateError?.message ?? "复制失败" },
{ status: 500 },
);
}
await ensureDocumentScaffold(duplicated.id, duplicated.title ?? duplicatedTitle);
await copyMindmapIfExists(sourceDoc.id, duplicated.id);
return NextResponse.json(duplicated);
}