import { stat } from "fs/promises"; import { createReadStream } from "fs"; import path from "path"; import { NextResponse } from "next/server"; import { getUserBearerFromRequest } from "@/lib/directus"; export const runtime = "nodejs"; export const dynamic = "force-dynamic"; export const revalidate = 0; const ROOT = process.env.FILES_ROOT || "/app/files"; function safeJoin(root: string, p: string) { const rootResolved = path.resolve(root); const raw = path.normalize("/" + (p || "/")); const abs = path.resolve(rootResolved, "." + raw); if (abs === rootResolved) return abs; if (abs.startsWith(rootResolved + path.sep)) return abs; throw new Error("Invalid path"); } export async function GET(req: Request) { try { // Auth gate: require ma_at const bearer = getUserBearerFromRequest(req); if (!bearer) { return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); } const { searchParams } = new URL(req.url); const p = searchParams.get("path"); if (!p) { return NextResponse.json({ error: "Missing path" }, { status: 400 }); } const abs = safeJoin(ROOT, p); const s = await stat(abs); if (s.isDirectory()) { return NextResponse.json({ error: "Is a directory" }, { status: 400 }); } const stream = createReadStream(abs); const fileName = path.basename(abs); return new Response(stream as any, { headers: { "Content-Type": "application/octet-stream", "Content-Length": String(s.size), "Content-Disposition": `attachment; filename*=UTF-8''${encodeURIComponent(fileName)}`, "Cache-Control": "no-store", }, }); } catch (e: any) { const msg = e?.message || "Not found"; const code = msg === "Invalid path" ? 400 : 404; return NextResponse.json({ error: msg }, { status: code, headers: { "Cache-Control": "no-store" } }); } }