import type { NextConfig } from "next"; import createNextIntlPlugin from "next-intl/plugin"; // Im Dev-Betrieb braucht Turbopack/HMR 'unsafe-eval' und WebSocket-Verbindungen // zum Dev-Server. Diese Lockerungen gelten NUR in der Entwicklung, nie im Build. const isDev = process.env.NODE_ENV !== "production"; // Content-Security-Policy (F-07). // // Hinweis Skripte: 'unsafe-inline' ist ein bewusster Zwischenstand. Next.js liefert // seinen Hydration-Bootstrap als Inline-Skript aus; eine Nonce-basierte CSP (Nonce in // proxy.ts erzeugen und durchreichen) ist ein eigenes Folgepaket. const csp = [ "default-src 'self'", `script-src 'self' 'unsafe-inline'${isDev ? " 'unsafe-eval'" : ""}`, // Next.js und Tailwind v4 setzen Inline-Styles. "style-src 'self' 'unsafe-inline'", // data: für den MFA-QR-Code; blob: für clientseitige Foto-Vorschauen (Einsatz-Fotos). "img-src 'self' data: blob:", // blob: für lokale Wiedergabe von Sprachnotizen vor dem Upload. "media-src 'self' blob:", "font-src 'self' data:", // Im Dev zusätzlich der HMR-WebSocket des Dev-Servers. `connect-src 'self'${isDev ? " ws: wss:" : ""}`, // PWA: Service Worker nur vom eigenen Ursprung. "worker-src 'self'", "manifest-src 'self'", "frame-ancestors 'none'", "form-action 'self'", "base-uri 'self'", "object-src 'none'", ].join("; "); const securityHeaders = [ { key: "Content-Security-Policy", value: csp }, // HSTS bewusst zusätzlich in der App (neben dem Coolify-/Traefik-Proxy). { key: "Strict-Transport-Security", value: "max-age=63072000; includeSubDomains; preload" }, { key: "X-Content-Type-Options", value: "nosniff" }, { key: "X-Frame-Options", value: "DENY" }, { key: "Referrer-Policy", value: "strict-origin-when-cross-origin" }, // Einsatz-App: Kamera (Fotos), Mikrofon (Sprachnotizen), Standort (Einsatzstart) — // nur für den eigenen Ursprung. Zahlungs-API bleibt aus. { key: "Permissions-Policy", value: "camera=(self), microphone=(self), geolocation=(self), payment=()" }, ]; const nextConfig: NextConfig = { // Standalone-Output für den Docker-Multi-Stage-Build (siehe Dockerfile) output: "standalone", // i18n-Kataloge werden zur Laufzeit per fs geladen (src/i18n/request.ts) — für den // standalone-Output explizit mitkopieren. outputFileTracingIncludes: { "/*": ["./messages/**/*.json"], }, async headers() { return [ { source: "/:path*", headers: securityHeaders, }, ]; }, }; const withNextIntl = createNextIntlPlugin(); export default withNextIntl(nextConfig);