/** * Was Server und Browser über Uploads gleichermaßen wissen müssen. * * Getrennt von `uploads.ts`, weil das dort liegende Ablegen und Aufräumen * `server-only` ist — die Formulare brauchen aber die Liste der erlaubten * Dateiarten und die Größenformatierung. */ /** Bilder, die die Oberfläche selbst anzeigt. */ const BILD_TYPEN = [ "image/png", "image/jpeg", "image/webp", "image/gif", "image/avif", ] as const; export const UPLOAD_KINDS = { pdf: { label: "PDF-Dokument", mimeTypes: ["application/pdf"], accept: "application/pdf,.pdf", }, image: { label: "Bild", mimeTypes: BILD_TYPEN, accept: "image/png,image/jpeg,image/webp,image/gif,image/avif", }, logo: { label: "Logo", mimeTypes: BILD_TYPEN, accept: "image/png,image/jpeg,image/webp,image/avif", // Ein Logo in der Kopfzeile ist ein paar Dutzend Kilobyte groß. Die // Grenze soll verhindern, dass jemand ein Foto aus der Kamera hochlädt, // das dann bei jedem Seitenaufruf mitgeht. maxBytes: 2 * 1024 * 1024, }, favicon: { label: "Favicon", // Zusätzlich ICO — viele haben genau das noch herumliegen. mimeTypes: [...BILD_TYPEN, "image/x-icon"], accept: "image/png,image/x-icon,.ico,image/webp", maxBytes: 512 * 1024, }, } as const; /** * Wozu die Datei gehört. * * `code` hängt an einem QR-Code und verschwindet mit ihm; `branding` gehört * zur Oberfläche und darf deshalb nicht als herrenlos weggeräumt werden. */ export const UPLOAD_PURPOSES = ["code", "branding"] as const; export type UploadPurpose = (typeof UPLOAD_PURPOSES)[number]; export function uploadPurposeOf(kind: UploadKind): UploadPurpose { return kind === "logo" || kind === "favicon" ? "branding" : "code"; } /** Obergrenze dieser Art — sonst gilt die allgemeine aus MAX_UPLOAD_MB. */ export function kindMaxBytes(kind: UploadKind): number | null { const eintrag = UPLOAD_KINDS[kind] as { maxBytes?: number }; return eintrag.maxBytes ?? null; } export type UploadKind = keyof typeof UPLOAD_KINDS; export function isUploadKind(value: unknown): value is UploadKind { return ( typeof value === "string" && Object.hasOwn(UPLOAD_KINDS, value) ); } export function formatBytes(bytes: number): string { if (bytes < 1024) return `${bytes} B`; if (bytes < 1024 * 1024) return `${Math.round(bytes / 1024)} KB`; return `${(bytes / (1024 * 1024)).toFixed(1).replace(".", ",")} MB`; }