import { preflight, withCors } from "@/lib/admin"; import { sql } from "@/lib/db"; import { requireAuth } from "@/lib/jwt"; import { isStoredUploadName, readUpload, uploadMimeType } from "@/lib/uploads"; // GET /(api)/driver/documents?name=… — serve one stored document scan. // // These are identity documents, so they are not static files: every read is // authenticated and authorised here. Exactly two principals may fetch a scan — // the driver it belongs to, and an owner reviewing that driver. Knowing the // (unguessable) file name is not itself permission. // // The name travels as a query parameter rather than a path segment because it // ends in .jpg/.png/.webp, and a dotted final segment is exactly what static // asset middleware tends to claim before the router ever sees it. A query // parameter cannot be mistaken for a file on disk. // // CORS is applied because the admin dashboard is a separate origin; it fetches // the bytes with its bearer token and renders them from a blob URL, since an // cannot carry an Authorization header. export async function OPTIONS(request: Request) { return preflight(request); } const notFound = (request: Request) => withCors(request, Response.json({ error: "Not found." }, { status: 404 })); export async function GET(request: Request) { const auth = requireAuth(request); if ("error" in auth) return withCors(request, auth.error); const name = new URL(request.url).searchParams.get("name"); // Rejecting the name before it reaches the filesystem is what keeps a // crafted "../../.env" from ever being joined onto the upload directory. if (!isStoredUploadName(name)) return notFound(request); try { const rows = await sql<{ role: string | null; owns: boolean }>` SELECT (SELECT role FROM users WHERE id = ${auth.userId}) AS role, EXISTS ( SELECT 1 FROM drivers WHERE user_id = ${auth.userId} AND ${name} IN ( license_image_url, id_image_url, vehicle_reg_image_url ) ) AS owns `; const allowed = rows[0]?.role === "owner" || rows[0]?.owns === true; // A 404 rather than a 403: a caller who is not entitled to the document // shouldn't learn whether it exists. if (!allowed) return notFound(request); const bytes = await readUpload(name, "document"); if (!bytes) return notFound(request); return withCors( request, new Response(new Uint8Array(bytes), { headers: { "Content-Type": uploadMimeType(name), "Content-Length": String(bytes.length), // Never let a shared cache hold somebody's ID card. "Cache-Control": "private, no-store", "Content-Disposition": `inline; filename="${name}"`, "X-Content-Type-Options": "nosniff", }, }), ); } catch (error) { console.error("[DRIVER_DOCUMENT_GET]: ", error); return withCors( request, Response.json({ error: "Internal Server Error" }, { status: 500 }), ); } }