From 15046892d14d8084bb2699e262f843b4b1f6b928 Mon Sep 17 00:00:00 2001 From: twotalesanimation <80506065+twotalesanimation@users.noreply.github.com> Date: Sat, 30 May 2026 10:19:35 +0200 Subject: [PATCH] api update --- app/api/ext/shots/[shotId]/route.ts | 97 +++++++++++++++++++++++++++++ app/api/ext/shots/route.ts | 46 ++++++++++++-- 2 files changed, 138 insertions(+), 5 deletions(-) create mode 100644 app/api/ext/shots/[shotId]/route.ts diff --git a/app/api/ext/shots/[shotId]/route.ts b/app/api/ext/shots/[shotId]/route.ts new file mode 100644 index 0000000..0974f37 --- /dev/null +++ b/app/api/ext/shots/[shotId]/route.ts @@ -0,0 +1,97 @@ +import { NextRequest, NextResponse } from "next/server"; +import { db } from "@/lib/db"; + +function isAuthorized(req: NextRequest): boolean { + const apiKey = process.env.API_SECRET_KEY; + if (!apiKey) return false; + + const authHeader = req.headers.get("authorization") ?? ""; + if (authHeader.startsWith("Bearer ")) { + return authHeader.slice(7) === apiKey; + } + const headerKey = req.headers.get("x-api-key") ?? ""; + return headerKey === apiKey; +} + +// ── GET /api/ext/shots/[shotId] ─────────────────────────────────────────────── +// +// Returns full shot detail including tasks and latest version status. +// Accepts either the database id or the shotCode via query param: +// /api/ext/shots/ +// /api/ext/shots/?byCode=1&projectId= + +export async function GET( + req: NextRequest, + { params }: { params: Promise<{ shotId: string }> } +) { + if (!isAuthorized(req)) { + return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); + } + + const { shotId } = await params; + const { searchParams } = new URL(req.url); + const byCode = searchParams.get("byCode") === "1"; + const projectId = searchParams.get("projectId") ?? undefined; + + const shot = await db.shot.findFirst({ + where: byCode + ? { shotCode: shotId, ...(projectId ? { projectId } : {}) } + : { id: shotId }, + select: { + id: true, + shotCode: true, + scene: true, + episode: true, + shotNumber: true, + description: true, + status: true, + priority: true, + frameStart: true, + frameEnd: true, + fps: true, + dueDate: true, + createdAt: true, + updatedAt: true, + project: { + select: { id: true, name: true, code: true, showId: true }, + }, + artist: { + select: { id: true, name: true, email: true }, + }, + tasks: { + orderBy: { sortOrder: "asc" }, + select: { + id: true, + title: true, + type: true, + status: true, + priority: true, + estimatedHours: true, + dueDate: true, + assignedArtist: { select: { id: true, name: true, email: true } }, + _count: { select: { versions: true } }, + }, + }, + versions: { + orderBy: { versionNumber: "desc" }, + take: 1, + select: { + id: true, + versionNumber: true, + approvalStatus: true, + createdAt: true, + artist: { select: { id: true, name: true, email: true } }, + }, + }, + _count: { + select: { versions: true, tasks: true }, + }, + }, + }); + + if (!shot) { + return NextResponse.json({ error: "Shot not found" }, { status: 404 }); + } + + return NextResponse.json({ shot }); +} diff --git a/app/api/ext/shots/route.ts b/app/api/ext/shots/route.ts index 0ae0340..1f11755 100644 --- a/app/api/ext/shots/route.ts +++ b/app/api/ext/shots/route.ts @@ -237,7 +237,13 @@ export async function POST(req: NextRequest) { } } -// ── GET /api/ext/shots?projectId=... ───────────────────────────────────────── +// ── GET /api/ext/shots?projectId=...&episode=...&status=...&shotCode=... ────── +// +// Query params: +// projectId (required) CUID of the project +// episode (optional) filter by episode, e.g. 103 +// status (optional) filter by ShotStatus, e.g. COMPLETE +// shotCode (optional) return a single shot by exact code export async function GET(req: NextRequest) { if (!isAuthorized(req)) { @@ -250,11 +256,41 @@ export async function GET(req: NextRequest) { return NextResponse.json({ error: "projectId is required" }, { status: 400 }); } + const episode = searchParams.get("episode") ?? undefined; + const status = searchParams.get("status") ?? undefined; + const shotCode = searchParams.get("shotCode") ?? undefined; + const shots = await db.shot.findMany({ - where: { projectId }, - orderBy: { createdAt: "asc" }, - select: { id: true, shotCode: true, scene: true, episode: true, status: true }, + where: { + projectId, + ...(episode ? { episode } : {}), + ...(status ? { status: status as any } : {}), + ...(shotCode ? { shotCode } : {}), + }, + orderBy: [{ episode: "asc" }, { scene: "asc" }, { shotNumber: "asc" }], + select: { + id: true, + shotCode: true, + scene: true, + episode: true, + shotNumber: true, + description: true, + status: true, + priority: true, + frameStart: true, + frameEnd: true, + fps: true, + dueDate: true, + createdAt: true, + updatedAt: true, + artist: { + select: { id: true, name: true, email: true }, + }, + _count: { + select: { versions: true, tasks: true }, + }, + }, }); - return NextResponse.json({ shots }); + return NextResponse.json({ shots, total: shots.length }); }