import "server-only"; import { mkdir, readFile, unlink, writeFile } from "node:fs/promises"; import path from "node:path"; // Ersetzt den früheren Supabase-Storage-Bucket "branding" durch ein // lokales Verzeichnis (Docker-Volume in Produktion, s. docker-compose.yml). export function brandingDir(): string { // turbopackIgnore: process.cwd() ist hier dynamisch, aber der Pfad // bleibt statisch unter "data/branding" – ohne den Kommentar versucht // Turbopack sonst, das gesamte Projektverzeichnis nach möglichen // dynamischen require()-Zielen zu durchsuchen (harmlos, aber unnötig). return ( process.env.BRANDING_DIR ?? path.join(/*turbopackIgnore: true*/ process.cwd(), "data", "branding") ); } // Nur einfache, von uns selbst vergebene Dateinamen zulassen // (`${kind}-${Date.now()}.${ext}`) – verhindert Path-Traversal über // Dateinamen aus der DB-Spalte logo_path/favicon_path. const SAFE_FILENAME = /^[a-zA-Z0-9_.-]+$/; export function isSafeFilename(filename: string): boolean { return SAFE_FILENAME.test(filename) && !filename.includes(".."); } export async function saveBrandingFile(filename: string, data: Buffer): Promise { const dir = brandingDir(); await mkdir(dir, { recursive: true }); await writeFile(path.join(dir, filename), data); } export async function readBrandingFile(filename: string): Promise { if (!isSafeFilename(filename)) return null; try { return await readFile(path.join(brandingDir(), filename)); } catch { return null; } } export async function removeBrandingFile(filename: string): Promise { try { await unlink(path.join(brandingDir(), filename)); } catch (err) { if ((err as NodeJS.ErrnoException).code !== "ENOENT") throw err; } }