makearmy-app/app/api/files/get/route.ts
2025-09-22 10:37:53 -04:00

50 lines
1.5 KiB
TypeScript

import { NextResponse } from "next/server";
import fs from "node:fs/promises";
import path from "node:path";
export const runtime = "nodejs";
export const dynamic = "force-dynamic";
const BASE = "/app/files";
function safeJoin(base: string, reqPath: string) {
const rel = reqPath.startsWith("/") ? reqPath : `/${reqPath}`;
const full = path.resolve(base, "." + rel);
if (!full.startsWith(base)) throw new Error("Outside base");
return full;
}
const CONTENT_MAP: Record<string, string> = {
".png": "image/png",
".jpg": "image/jpeg",
".jpeg": "image/jpeg",
".gif": "image/gif",
".webp": "image/webp",
".svg": "image/svg+xml",
".txt": "text/plain; charset=utf-8",
".json": "application/json; charset=utf-8",
".pdf": "application/pdf",
};
export async function GET(req: Request) {
try {
const url = new URL(req.url);
const reqPath = url.searchParams.get("path");
if (!reqPath) return NextResponse.json({ error: "Missing path" }, { status: 400 });
const full = safeJoin(BASE, reqPath);
const stat = await fs.stat(full);
if (!stat.isFile()) return NextResponse.json({ error: "Not a file" }, { status: 400 });
const data = await fs.readFile(full);
const ext = path.extname(full).toLowerCase();
const type = CONTENT_MAP[ext] ?? "application/octet-stream";
return new NextResponse(data, {
status: 200,
headers: { "Content-Type": type, "Cache-Control": "public, max-age=300" },
});
} catch (e: any) {
return NextResponse.json({ error: e?.message ?? "Unknown" }, { status: 400 });
}
}