import { mkdir } from "node:fs/promises"; import { resolve, extname } from "node:path"; /** * Filesystem storage for uploads, under backend/uploads/. * Files are stored under a UUID-prefixed name so a malicious client filename * can never traverse paths or overwrite another file. The raw bytes are never * executed server-side — we only ever read them back to serve downloads. */ const UPLOADS_DIR = resolve(import.meta.dir, "../../uploads"); let ensured = false; async function ensureDir(): Promise { if (ensured) return; await mkdir(UPLOADS_DIR, { recursive: true }); ensured = true; } /** Keep only a safe, short suffix of the original name for readability. */ function safeSuffix(filename: string): string { const ext = extname(filename).slice(0, 12).replace(/[^a-zA-Z0-9.]/g, ""); return ext || ""; } export interface StoredFile { storagePath: string; // relative name under uploads/ absolutePath: string; } /** Persist a File/Blob, returning its storage path. id should be a fresh uuid. */ export async function storeFile(id: string, file: File): Promise { await ensureDir(); const name = `${id}${safeSuffix(file.name)}`; const absolutePath = resolve(UPLOADS_DIR, name); // Extra guard: the resolved path must stay inside UPLOADS_DIR. if (!absolutePath.startsWith(UPLOADS_DIR)) { throw new Error("Invalid storage path"); } await Bun.write(absolutePath, file); return { storagePath: name, absolutePath }; } export function absolutePathFor(storagePath: string): string { const abs = resolve(UPLOADS_DIR, storagePath); if (!abs.startsWith(UPLOADS_DIR)) throw new Error("Invalid storage path"); return abs; }