Files
vfxreview/app/api/ext/shots/lookup/route.ts
T
twotalesanimation d40ca8eb55
Deploy / deploy (push) Successful in 2m45s
API Updates
2026-07-08 14:17:03 +02:00

109 lines
3.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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/shots/lookup ─────────────────────────────────────────────────
//
// Look up a single shot by its shot code and (optionally) project code.
// Designed for use in Nuke/After Effects/Blender pipeline tools.
//
// Query params:
// shotCode (required) e.g. UNG_108_004_010
// projectCode (recommended) e.g. UNG_108 disambiguates if same code appears across projects
//
// Example:
// GET /api/ext/shots/lookup?shotCode=UNG_108_004_010&projectCode=UNG_108
// Authorization: Bearer <API_SECRET_KEY>
export async function GET(req: NextRequest) {
if (!isAuthorized(req)) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
const { searchParams } = new URL(req.url);
const shotCode = searchParams.get("shotCode")?.trim();
const projectCode = searchParams.get("projectCode")?.trim();
if (!shotCode) {
return NextResponse.json({ error: "shotCode query param is required" }, { status: 400 });
}
const shot = await db.shot.findFirst({
where: {
shotCode,
...(projectCode ? { project: { code: projectCode } } : {}),
},
select: {
id: true,
shotCode: true,
scene: true,
episode: true,
sequence: true,
shotNumber: true,
description: true,
notes: 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,
seqTimecodeStart: true,
seqTimecodeEnd: true,
shotVersion: true,
thumbnailUrl: 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,
},
},
versions: {
where: { isLatest: true },
take: 1,
select: {
id: true,
versionNumber: true,
approvalStatus: true,
reviewStatus: true,
fileUrl: true,
thumbnailUrl: true,
createdAt: true,
},
},
},
});
if (!shot) {
return NextResponse.json({ error: "Shot not found" }, { status: 404 });
}
return NextResponse.json({ shot });
}