59 lines
3.2 KiB
TypeScript
59 lines
3.2 KiB
TypeScript
import { randomBytes } from "node:crypto";
|
|
import { and, eq } from "drizzle-orm";
|
|
import { NextResponse } from "next/server";
|
|
import { z } from "zod";
|
|
import { auth } from "@/app/(auth)/auth";
|
|
import { db } from "@/lib/db";
|
|
import { tvstarSession } from "@/lib/db/schema";
|
|
import { ownsTaskProject } from "@/lib/video-canvas/task-store";
|
|
import { publicTVstarSnapshot, tvstarSessionSchema } from "@/lib/video-canvas/tvstar-session";
|
|
|
|
export const dynamic = "force-dynamic";
|
|
|
|
async function owner(projectId: string) {
|
|
const session = await auth();
|
|
if (!session?.user?.id) return { error: NextResponse.json({ error: "未登录" }, { status: 401 }) };
|
|
if (!z.string().uuid().safeParse(projectId).success || !await ownsTaskProject(session.user.id, projectId)) {
|
|
return { error: NextResponse.json({ error: "画布不存在" }, { status: 404 }) };
|
|
}
|
|
return { userId: session.user.id };
|
|
}
|
|
|
|
export async function POST(request: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
const { id } = await params;
|
|
const access = await owner(id);
|
|
if (access.error) return access.error;
|
|
const raw = await request.text();
|
|
if (raw.length > 300_000) return NextResponse.json({ error: "对话记录过大" }, { status: 413 });
|
|
let body: unknown;
|
|
try { body = JSON.parse(raw); } catch { return NextResponse.json({ error: "请求格式无效" }, { status: 400 }); }
|
|
const parsed = z.object({ session: tvstarSessionSchema }).safeParse(body);
|
|
if (!parsed.success) return NextResponse.json({ error: "对话记录格式无效" }, { status: 400 });
|
|
const current = parsed.data.session;
|
|
const condition = and(eq(tvstarSession.id, current.id), eq(tvstarSession.projectId, id), eq(tvstarSession.userId, access.userId!));
|
|
await db.insert(tvstarSession).values({
|
|
id: current.id, projectId: id, userId: access.userId!, title: current.title,
|
|
snapshot: current, clientUpdatedAt: new Date(Math.min(current.updatedAt, Date.now() + 60_000)),
|
|
}).onConflictDoNothing({ target: tvstarSession.id });
|
|
const existing = (await db.select({ shareToken: tvstarSession.shareToken }).from(tvstarSession).where(condition).limit(1))[0];
|
|
if (!existing) return NextResponse.json({ error: "对话不存在" }, { status: 404 });
|
|
const token = existing.shareToken || randomBytes(24).toString("base64url");
|
|
if (!existing.shareToken) {
|
|
await db.update(tvstarSession).set({
|
|
shareToken: token, sharedSnapshot: publicTVstarSnapshot(current), sharedAt: new Date(), updatedAt: new Date(),
|
|
}).where(condition);
|
|
}
|
|
return NextResponse.json({ url: `/tvstar/share/${token}`, token });
|
|
}
|
|
|
|
export async function DELETE(request: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
const { id } = await params;
|
|
const access = await owner(id);
|
|
if (access.error) return access.error;
|
|
const sessionId = new URL(request.url).searchParams.get("sessionId") || "";
|
|
if (!z.string().uuid().safeParse(sessionId).success) return NextResponse.json({ error: "对话 ID 无效" }, { status: 400 });
|
|
await db.update(tvstarSession).set({ shareToken: null, sharedSnapshot: null, sharedAt: null, updatedAt: new Date() })
|
|
.where(and(eq(tvstarSession.id, sessionId), eq(tvstarSession.projectId, id), eq(tvstarSession.userId, access.userId!)));
|
|
return NextResponse.json({ ok: true });
|
|
}
|