Files
vfxreview/app/api/ext/projects/[projectCode]/shots/route.ts
T
twotalesanimation 1bdb147d24
Deploy / deploy (push) Successful in 2m38s
API UPdate
2026-06-12 19:20:58 +02:00

111 lines
3.6 KiB
TypeScript

import { NextRequest, NextResponse } from "next/server";
import { db } from "@/lib/db";
// ── Auth ─────────────────────────────────────────────────────────────────────
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;
return (req.headers.get("x-api-key") ?? "") === apiKey;
}
// ── GET /api/ext/projects/[projectCode]/shots ─────────────────────────────────
//
// Returns all shots for a project, optionally filtered by episode and/or sequence.
// Uses the human-readable project code (e.g. UNG_108) not the DB id.
//
// Query params (all optional):
// episode e.g. 108
// sequence e.g. 004
// status e.g. IN_PROGRESS
// page (default 1)
// limit (default 200, max 500)
//
// Example:
// GET /api/ext/projects/UNG_108/shots?episode=108&sequence=004
// GET /api/ext/projects/UNG_108/shots
export async function GET(
req: NextRequest,
{ params }: { params: Promise<{ projectCode: string }> }
) {
if (!isAuthorized(req)) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
const { projectCode } = await params;
const { searchParams } = new URL(req.url);
const episode = searchParams.get("episode")?.trim() || undefined;
const sequence = searchParams.get("sequence")?.trim() || undefined;
const status = searchParams.get("status")?.trim() || undefined;
const page = Math.max(1, parseInt(searchParams.get("page") ?? "1", 10));
const limit = Math.min(500, Math.max(1, parseInt(searchParams.get("limit") ?? "200", 10)));
const skip = (page - 1) * limit;
const project = await db.project.findUnique({
where: { code: projectCode },
select: { id: true, name: true, code: true, showId: true },
});
if (!project) {
return NextResponse.json({ error: "Project not found" }, { status: 404 });
}
const [shots, total] = await Promise.all([
db.shot.findMany({
where: {
projectId: project.id,
...(episode ? { episode } : {}),
...(sequence ? { sequence } : {}),
...(status ? { status: status as never } : {}),
},
orderBy: [{ episode: "asc" }, { shotCode: "asc" }],
skip,
take: limit,
select: {
id: true,
shotCode: true,
scene: true,
episode: true,
sequence: true,
shotNumber: true,
description: true,
status: true,
priority: true,
frameStart: true,
frameEnd: true,
fps: true,
dueDate: true,
// EDL / pull CSV fields
sourceClip: true,
timecodeStart: true,
timecodeEnd: true,
clipDuration: true,
exrOutput: true,
thumbnailUrl: true,
artist: {
select: { id: true, name: true, email: true },
},
_count: { select: { versions: true, tasks: true } },
},
}),
db.shot.count({
where: {
projectId: project.id,
...(episode ? { episode } : {}),
...(sequence ? { sequence } : {}),
...(status ? { status: status as never } : {}),
},
}),
]);
return NextResponse.json({
project,
pagination: { page, limit, total, pages: Math.ceil(total / limit) },
shots,
});
}