import type { NextConfig } from "next"; import { defaultLocale, locales } from "./src/i18n/routes"; const PUBLIC_TO_INTERNAL: Record = { "/": "/de", "/ueber-mich": "/de/about", "/projekte": "/de/projects", "/kontakt": "/de/contact", "/impressum": "/de/legal", "/datenschutz": "/de/privacy", "/en": "/en", "/en/about": "/en/about", "/en/projects": "/en/projects", "/en/contact": "/en/contact", "/en/legal": "/en/legal", "/en/privacy": "/en/privacy", }; function projectItemRewrite(srcPrefix: string, destLocale: "de" | "en") { return { source: `${srcPrefix}/:slug`, destination: `/${destLocale}/projects/:slug`, }; } // Content-Security-Policy. Bewusst moderat, weil Next.js inline-styles und // inline-scripts für Hydration + RSC braucht. 'unsafe-inline' ist hier // akzeptabel, weil wir kein User-Generated-Content haben und alle Skripte // aus dem eigenen Build kommen — XSS via externe Quelle ist trotzdem geblockt. // TODO: Migration zu Nonces sobald der RSC-Stream nonces unterstützt. const CSP = [ "default-src 'self'", "script-src 'self' 'unsafe-inline' 'unsafe-eval'", "style-src 'self' 'unsafe-inline'", "img-src 'self' data: https:", "font-src 'self' data:", "connect-src 'self'", "frame-ancestors 'none'", "base-uri 'self'", "form-action 'self'", "object-src 'none'", ].join("; "); const config: NextConfig = { serverExternalPackages: ["better-sqlite3"], images: { unoptimized: true }, output: "standalone", // View-Transitions-API wird via CSS ::view-transition-* in theme.css // genutzt (siehe dort). Next.js 16 hat keinen experimental.viewTransition- // Switch — die CSS-Regeln reichen aus, der Browser aktiviert die // Transition automatisch beim ersten Cross-Document-Navigation mit dem // Meta-View-Transition-Tag. async rewrites() { return [ ...Object.entries(PUBLIC_TO_INTERNAL).map(([from, to]) => ({ source: from, destination: to, })), projectItemRewrite("/projekte", "de"), projectItemRewrite("/en/projects", "en"), ]; }, async headers() { return [ { source: "/:path*", headers: [ { key: "Content-Security-Policy", value: CSP }, { key: "X-DNS-Prefetch-Control", value: "off" }, { key: "Strict-Transport-Security", value: "max-age=63072000; includeSubDomains; preload" }, { key: "X-Content-Type-Options", value: "nosniff" }, { key: "Referrer-Policy", value: "strict-origin-when-cross-origin" }, { key: "Permissions-Policy", value: "camera=(), microphone=(), geolocation=(), payment=()" }, ], }, ]; }, }; export default config;