import { existsSync } from "node:fs"; import { readFile } from "node:fs/promises"; import path from "node:path"; import { NextResponse } from "next/server"; import { MEDIA_ROOT, resolveMediaPath } from "@/lib/media-storage"; const ALLOWED_EXT = [".png", ".jpg", ".jpeg", ".gif", ".webp", ".svg", ".bmp"]; export async function GET( _request: Request, { params }: { params: Promise<{ path: string[] }> }, ) { const { path: segments } = await params; const name = segments.join("/"); // Prevent path traversal if (name.includes("..") || name.includes("\\")) { return new NextResponse("Forbidden", { status: 403 }); } const ext = path.extname(name).toLowerCase(); if (!ALLOWED_EXT.includes(ext)) { return new NextResponse("Forbidden", { status: 403 }); } const baseDir = MEDIA_ROOT; const filePath = resolveMediaPath(name); if (!filePath.startsWith(baseDir + path.sep)) { return new NextResponse("Forbidden", { status: 403 }); } // eslint-disable-next-line security/detect-non-literal-fs-filename if (!existsSync(filePath)) { return new NextResponse("Not found", { status: 404 }); } // eslint-disable-next-line security/detect-non-literal-fs-filename const bytes = await readFile(filePath); const mime: Record = { ".png": "image/png", ".jpg": "image/jpeg", ".jpeg": "image/jpeg", ".gif": "image/gif", ".webp": "image/webp", ".svg": "image/svg+xml", ".bmp": "image/bmp", }; return new NextResponse(bytes, { headers: { // eslint-disable-next-line security/detect-object-injection -- ext validated against ALLOWED_EXT "Content-Type": mime[ext] ?? "application/octet-stream", "X-Content-Type-Options": "nosniff", ...(ext === ".svg" ? { "Content-Security-Policy": "sandbox; default-src 'none'; style-src 'unsafe-inline'", } : {}), "Cache-Control": "public, max-age=3600, must-revalidate", }, }); }