198 lines
5.9 KiB
TypeScript
198 lines
5.9 KiB
TypeScript
import { NextRequest, NextResponse } from 'next/server';
|
||
import { auth } from '@/app/(auth)/auth';
|
||
import { db } from '@/lib/db';
|
||
import { canvasProject } from '@/lib/db/schema';
|
||
import { syncCanvasNodesForProject } from '@/lib/db/queries';
|
||
import { eq, and } from 'drizzle-orm';
|
||
import {
|
||
buildStaleCanvasSaveConflictPayload,
|
||
isStaleCanvasSaveBaseRevision,
|
||
} from '@/lib/canvas/canvas-save-conflict';
|
||
|
||
function resolveWorkspaceType(input: unknown) {
|
||
return input === "agent" || input === "video" ? input : "canvas";
|
||
}
|
||
|
||
function getDefaultWorkspaceTitle(workspaceType: ReturnType<typeof resolveWorkspaceType>) {
|
||
return workspaceType === "video" ? "未命名视频画布" : "未命名画布";
|
||
}
|
||
|
||
// 获取单个画布
|
||
export async function GET(
|
||
request: NextRequest,
|
||
{ params }: { params: Promise<{ id: string }> }
|
||
) {
|
||
try {
|
||
const session = await auth();
|
||
if (!session?.user?.id) {
|
||
return NextResponse.json({ success: false, error: '未登录' }, { status: 401 });
|
||
}
|
||
|
||
const { id } = await params;
|
||
|
||
const [project] = await db
|
||
.select()
|
||
.from(canvasProject)
|
||
.where(
|
||
and(
|
||
eq(canvasProject.id, id),
|
||
eq(canvasProject.userId, session.user.id)
|
||
)
|
||
);
|
||
|
||
if (!project) {
|
||
return NextResponse.json({ success: false, error: '画布不存在' }, { status: 404 });
|
||
}
|
||
|
||
return NextResponse.json({ success: true, data: project });
|
||
} catch (error) {
|
||
console.error('获取画布失败:', error);
|
||
return NextResponse.json({ success: false, error: '获取画布失败' }, { status: 500 });
|
||
}
|
||
}
|
||
|
||
// 更新画布(支持 upsert:如果不存在则创建)
|
||
export async function PUT(
|
||
request: NextRequest,
|
||
{ params }: { params: Promise<{ id: string }> }
|
||
) {
|
||
try {
|
||
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 { title, description, thumbnail, canvasData } = body;
|
||
const baseCanvasRevision = body?.baseCanvasUpdatedAt || body?.baseCanvasRevision;
|
||
const workspaceType =
|
||
body && Object.prototype.hasOwnProperty.call(body, "workspaceType")
|
||
? resolveWorkspaceType(body.workspaceType)
|
||
: undefined;
|
||
|
||
// 先检查画布是否存在
|
||
const [existingProject] = await db
|
||
.select()
|
||
.from(canvasProject)
|
||
.where(
|
||
and(
|
||
eq(canvasProject.id, id),
|
||
eq(canvasProject.userId, session.user.id)
|
||
)
|
||
);
|
||
|
||
if (existingProject) {
|
||
if (
|
||
canvasData !== undefined &&
|
||
baseCanvasRevision &&
|
||
isStaleCanvasSaveBaseRevision({
|
||
baseCanvasRevision: body?.baseCanvasRevision,
|
||
baseCanvasUpdatedAt: body?.baseCanvasUpdatedAt,
|
||
currentUpdatedAt: existingProject.updatedAt,
|
||
})
|
||
) {
|
||
return NextResponse.json(
|
||
buildStaleCanvasSaveConflictPayload(existingProject.updatedAt),
|
||
{ status: 409 }
|
||
);
|
||
}
|
||
|
||
const syncedCanvasData =
|
||
canvasData !== undefined
|
||
? await syncCanvasNodesForProject({
|
||
projectId: id,
|
||
userId: session.user.id,
|
||
canvasData,
|
||
})
|
||
: undefined;
|
||
|
||
// 更新现有画布
|
||
const updateData: any = { updatedAt: new Date() };
|
||
if (title !== undefined) updateData.title = title;
|
||
if (description !== undefined) updateData.description = description;
|
||
if (thumbnail !== undefined) updateData.thumbnail = thumbnail;
|
||
if (workspaceType !== undefined) updateData.workspaceType = workspaceType;
|
||
if (syncedCanvasData !== undefined) updateData.canvasData = syncedCanvasData;
|
||
|
||
const [updatedProject] = await db
|
||
.update(canvasProject)
|
||
.set(updateData)
|
||
.where(
|
||
and(
|
||
eq(canvasProject.id, id),
|
||
eq(canvasProject.userId, session.user.id)
|
||
)
|
||
)
|
||
.returning();
|
||
|
||
return NextResponse.json({ success: true, data: updatedProject });
|
||
} else {
|
||
const syncedCanvasData =
|
||
canvasData !== undefined
|
||
? await syncCanvasNodesForProject({
|
||
projectId: id,
|
||
userId: session.user.id,
|
||
canvasData,
|
||
})
|
||
: undefined;
|
||
|
||
// 创建新画布(使用指定 ID)
|
||
const [newProject] = await db
|
||
.insert(canvasProject)
|
||
.values({
|
||
id, // 使用指定的 ID
|
||
userId: session.user.id,
|
||
title: title || getDefaultWorkspaceTitle(workspaceType || "canvas"),
|
||
description,
|
||
workspaceType: workspaceType || "canvas",
|
||
canvasData: syncedCanvasData || {
|
||
nodes: [],
|
||
edges: [],
|
||
viewport: { x: 0, y: 0, zoom: 1 },
|
||
},
|
||
})
|
||
.returning();
|
||
|
||
return NextResponse.json({ success: true, data: newProject });
|
||
}
|
||
} catch (error) {
|
||
console.error('更新/创建画布失败:', error);
|
||
return NextResponse.json({ success: false, error: '更新画布失败' }, { status: 500 });
|
||
}
|
||
}
|
||
|
||
// 删除画布
|
||
export async function DELETE(
|
||
request: NextRequest,
|
||
{ params }: { params: Promise<{ id: string }> }
|
||
) {
|
||
try {
|
||
const session = await auth();
|
||
if (!session?.user?.id) {
|
||
return NextResponse.json({ success: false, error: '未登录' }, { status: 401 });
|
||
}
|
||
|
||
const { id } = await params;
|
||
|
||
const [deletedProject] = await db
|
||
.delete(canvasProject)
|
||
.where(
|
||
and(
|
||
eq(canvasProject.id, id),
|
||
eq(canvasProject.userId, session.user.id)
|
||
)
|
||
)
|
||
.returning();
|
||
|
||
if (!deletedProject) {
|
||
return NextResponse.json({ success: false, error: '画布不存在' }, { status: 404 });
|
||
}
|
||
|
||
return NextResponse.json({ success: true, data: deletedProject });
|
||
} catch (error) {
|
||
console.error('删除画布失败:', error);
|
||
return NextResponse.json({ success: false, error: '删除画布失败' }, { status: 500 });
|
||
}
|
||
}
|