import { Router } from "@oak/oak"; import { APIErrorCode, APIException } from "../model/interfaces.ts"; import { DUMPS_DIR } from "../lib/upload.ts"; import { authenticateMedia } from "../lib/auth.ts"; import { serveFile } from "../lib/serve-file.ts"; import { dumpFileVersion, getDump } from "../services/dump-service.ts"; const router = new Router({ prefix: "/api/files" }); /** * Builds an RFC 6266 `Content-Disposition` value that is always a valid * ByteString. `headers.set` throws a TypeError if the value contains any * character > U+00FF, so a filename with an emoji / accent / CJK character * (e.g. an uploaded file) would otherwise crash the response. We provide an * ASCII-only `filename="…"` fallback plus an RFC 5987 `filename*` with the * full UTF-8 name for modern browsers. */ function contentDisposition( type: "inline" | "attachment", name: string, ): string { const asciiFallback = name .replace(/["\\]/g, "_") .replace(/[^\x20-\x7e]/g, "_"); return `${type}; filename="${asciiFallback}"; filename*=UTF-8''${ encodeURIComponent(name) }`; } // The MIME type of an upload is whatever the uploader's browser claimed, and // these responses come from the app's own origin — where the session token // lives. Only types a browser renders without running the file's own code are // served inline; anything else (text/html, image/svg+xml, XML, …) downloads. // ,