Files
sofa/app/api/images/[...path]/route.ts
T
jakeandClaude Opus 4.6 cd5d773e98 Add actor/cast information with person pages and search support
Integrate TMDB credits data into the app: cast carousels on title detail
pages, person detail pages with biography and filmography, and person
search results in the command palette. Includes new persons/titleCast
DB tables, profile image caching, and a nightly credits refresh cron job.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 14:36:39 -05:00

63 lines
1.6 KiB
TypeScript

import path from "node:path";
import { type NextRequest, NextResponse } from "next/server";
import {
fetchAndMaybeCache,
type ImageCategory,
imageCacheEnabled,
} from "@/lib/services/image-cache";
const VALID_CATEGORIES = new Set<ImageCategory>([
"posters",
"backdrops",
"stills",
"logos",
"profiles",
]);
const IMMUTABLE_CACHE = "public, max-age=31536000, immutable";
export async function GET(
_req: NextRequest,
{ params }: { params: Promise<{ path: string[] }> },
) {
const segments = await params;
if (!imageCacheEnabled()) {
return NextResponse.json(
{ error: "Image cache disabled" },
{ status: 404 },
);
}
if (segments.path.length !== 2) {
return NextResponse.json({ error: "Invalid path" }, { status: 400 });
}
const [category, rawFilename] = segments.path;
if (!VALID_CATEGORIES.has(category as ImageCategory)) {
return NextResponse.json({ error: "Invalid category" }, { status: 400 });
}
// Sanitize filename — only allow basename to prevent path traversal
const filename = path.basename(rawFilename);
if (!filename || filename !== rawFilename || filename.includes("..")) {
return NextResponse.json({ error: "Invalid filename" }, { status: 400 });
}
const tmdbPath = `/${filename}`;
const result = await fetchAndMaybeCache(tmdbPath, category as ImageCategory);
if (!result) {
return NextResponse.json({ error: "Not found" }, { status: 404 });
}
return new NextResponse(new Uint8Array(result.buffer), {
status: 200,
headers: {
"Content-Type": result.contentType,
"Cache-Control": IMMUTABLE_CACHE,
},
});
}