Initial commit

This commit is contained in:
twotalesanimation
2026-05-19 22:20:29 +02:00
commit 0fbe856dce
173 changed files with 38316 additions and 0 deletions
@@ -0,0 +1,89 @@
import { NextRequest, NextResponse } from "next/server";
import { db } from "@/lib/db";
async function validateToken(token: string) {
const session = await db.reviewSession.findUnique({ where: { token } });
if (!session || !session.isActive) return null;
if (session.expiresAt && session.expiresAt < new Date()) return null;
return session;
}
/** GET /api/client/[token]/versions/[versionId] — returns version + comments for client portal */
export async function GET(
req: NextRequest,
{ params }: { params: Promise<{ token: string; versionId: string }> }
) {
const { token, versionId } = await params;
const session = await validateToken(token);
if (!session) {
return NextResponse.json({ error: "Invalid or expired review link" }, { status: 403 });
}
const version = await db.version.findUnique({
where: { id: versionId },
include: {
shot: {
include: {
project: { select: { id: true, name: true, code: true } },
versions: {
orderBy: { versionNumber: "desc" },
select: {
id: true,
versionNumber: true,
approvalStatus: true,
isLatest: true,
fps: true,
duration: true,
createdAt: true,
},
},
},
},
task: {
include: {
project: { select: { id: true, name: true, code: true } },
shot: { select: { shotCode: true } },
asset: { select: { assetCode: true, name: true } },
},
},
artist: { select: { id: true, name: true, image: true, email: true } },
approvals: {
orderBy: { createdAt: "desc" },
include: { user: { select: { id: true, name: true } } },
},
},
});
// Resolve project: from shot or from task
const projectId = version?.shot?.projectId ?? version?.task?.projectId;
if (!version || projectId !== session.projectId) {
return NextResponse.json({ error: "Version not found" }, { status: 404 });
}
// For task-only versions (no shot), require explicit client sharing
if (!version.shot && !version.isClientVisible) {
return NextResponse.json({ error: "Version not found" }, { status: 404 });
}
const comments = await db.comment.findMany({
where: { versionId },
orderBy: { frameNumber: "asc" },
include: {
author: { select: { id: true, name: true, image: true, email: true } },
replies: {
orderBy: { createdAt: "asc" },
include: {
author: { select: { id: true, name: true, image: true, email: true } },
},
},
},
});
const serializedVersion = {
...version,
fileSize: version.fileSize?.toString() ?? null,
};
return NextResponse.json({ version: serializedVersion, comments });
}