import { and, desc, eq } from "drizzle-orm"; import { NextRequest, NextResponse } from "next/server"; import { auth } from "@/app/(auth)/auth"; import { duplicateVideoCanvasData, getExplicitVideoCanvasWorkspaceMeta, getNextVideoCanvasName, getVideoCanvasCopyName, getVideoCanvasWorkspaceProjects, normalizeVideoCanvasName, resolveVideoCanvasWorkspaceMeta, withVideoCanvasWorkspaceMeta, } from "@/lib/canvas/video-workspace"; import { db } from "@/lib/db"; import { syncCanvasNodesForProject } from "@/lib/db/queries"; import { canvasProject } from "@/lib/db/schema"; type CanvasProjectRow = typeof canvasProject.$inferSelect; function toCanvasItem(project: CanvasProjectRow, currentId: string) { const meta = resolveVideoCanvasWorkspaceMeta(project); return { createdAt: project.createdAt.toISOString(), id: project.id, isCurrent: project.id === currentId, name: meta.canvasName, }; } async function getWorkspaceContext(userId: string, currentId: string) { const projects = await db .select() .from(canvasProject) .where( and( eq(canvasProject.userId, userId), eq(canvasProject.workspaceType, "video"), ), ) .orderBy(desc(canvasProject.createdAt)); const currentProject = projects.find((project) => project.id === currentId); if (!currentProject) return null; return { currentProject, projects: getVideoCanvasWorkspaceProjects(projects, currentProject), }; } async function ensureExplicitWorkspaceMetadata(projects: CanvasProjectRow[]) { for (const project of projects) { if (getExplicitVideoCanvasWorkspaceMeta(project.canvasData)) continue; const canvasData = withVideoCanvasWorkspaceMeta( project.canvasData, resolveVideoCanvasWorkspaceMeta(project), ); await db .update(canvasProject) .set({ canvasData }) .where(eq(canvasProject.id, project.id)); project.canvasData = canvasData; } } export async function GET( _request: NextRequest, { params }: { params: Promise<{ id: string }> }, ) { const session = await auth(); if (!session?.user?.id) { return NextResponse.json({ success: false, error: "未登录" }, { status: 401 }); } const { id } = await params; const context = await getWorkspaceContext(session.user.id, id); if (!context) { return NextResponse.json( { success: false, error: "画布不存在" }, { status: 404 }, ); } return NextResponse.json({ success: true, data: { canvases: context.projects.map((project) => toCanvasItem(project, id)), currentCanvasId: id, workspaceTitle: context.currentProject.title, }, }); } export async function POST( request: NextRequest, { params }: { params: Promise<{ id: string }> }, ) { const session = await auth(); if (!session?.user?.id) { return NextResponse.json({ success: false, error: "未登录" }, { status: 401 }); } const { id } = await params; const body = await request.json(); if (body?.action !== "create" && body?.action !== "copy") { return NextResponse.json( { success: false, error: "不支持的画布操作" }, { status: 400 }, ); } const action = body.action; const context = await getWorkspaceContext(session.user.id, id); if (!context) { return NextResponse.json( { success: false, error: "画布不存在" }, { status: 404 }, ); } await ensureExplicitWorkspaceMetadata(context.projects); const currentMeta = resolveVideoCanvasWorkspaceMeta(context.currentProject); const existingNames = context.projects.map( (project) => resolveVideoCanvasWorkspaceMeta(project).canvasName, ); const source = action === "copy" ? context.projects.find((project) => project.id === body?.targetId) : context.currentProject; if (!source) { return NextResponse.json( { success: false, error: "要复制的画布不存在" }, { status: 404 }, ); } const canvasName = action === "copy" ? getVideoCanvasCopyName( resolveVideoCanvasWorkspaceMeta(source).canvasName, existingNames, ) : getNextVideoCanvasName(existingNames); const meta = { canvasName, workspaceId: currentMeta.workspaceId, }; const canvasData = action === "copy" ? duplicateVideoCanvasData(source.canvasData, meta) : withVideoCanvasWorkspaceMeta(null, meta); const [insertedProject] = await db .insert(canvasProject) .values({ canvasData, description: context.currentProject.description, title: context.currentProject.title, userId: session.user.id, workspaceType: "video", }) .returning(); const syncedCanvasData = await syncCanvasNodesForProject({ canvasData, projectId: insertedProject.id, userId: session.user.id, }); const [createdProject] = await db .update(canvasProject) .set({ canvasData: syncedCanvasData }) .where(eq(canvasProject.id, insertedProject.id)) .returning(); return NextResponse.json({ success: true, data: toCanvasItem(createdProject || insertedProject, id), }); } export async function PATCH( request: NextRequest, { params }: { params: Promise<{ id: string }> }, ) { const session = await auth(); if (!session?.user?.id) { return NextResponse.json({ success: false, error: "未登录" }, { status: 401 }); } const { id } = await params; const body = await request.json(); const context = await getWorkspaceContext(session.user.id, id); if (!context) { return NextResponse.json( { success: false, error: "画布不存在" }, { status: 404 }, ); } const target = context.projects.find((project) => project.id === body?.targetId); if (!target) { return NextResponse.json( { success: false, error: "画布不存在" }, { status: 404 }, ); } const currentMeta = resolveVideoCanvasWorkspaceMeta(context.currentProject); const targetMeta = resolveVideoCanvasWorkspaceMeta(target); const canvasName = normalizeVideoCanvasName(body?.name, targetMeta.canvasName); const [updatedProject] = await db .update(canvasProject) .set({ canvasData: withVideoCanvasWorkspaceMeta(target.canvasData, { canvasName, workspaceId: currentMeta.workspaceId, }), updatedAt: new Date(), }) .where( and( eq(canvasProject.id, target.id), eq(canvasProject.userId, session.user.id), ), ) .returning(); return NextResponse.json({ success: true, data: toCanvasItem(updatedProject, id), }); } export async function DELETE( request: NextRequest, { params }: { params: Promise<{ id: string }> }, ) { const session = await auth(); if (!session?.user?.id) { return NextResponse.json({ success: false, error: "未登录" }, { status: 401 }); } const { id } = await params; const body = await request.json(); const context = await getWorkspaceContext(session.user.id, id); if (!context) { return NextResponse.json( { success: false, error: "画布不存在" }, { status: 404 }, ); } const target = context.projects.find((project) => project.id === body?.targetId); if (!target) { return NextResponse.json( { success: false, error: "画布不存在" }, { status: 404 }, ); } if (context.projects.length <= 1) { return NextResponse.json( { success: false, error: "至少保留一个画布" }, { status: 409 }, ); } await db .delete(canvasProject) .where( and( eq(canvasProject.id, target.id), eq(canvasProject.userId, session.user.id), ), ); const remaining = context.projects.filter((project) => project.id !== target.id); return NextResponse.json({ success: true, data: { nextCanvasId: target.id === id ? remaining[0]?.id || null : id, }, }); }