/** @type {import('next').NextConfig} */ // Les URL Directus sont lues à l'exécution — elles s'adaptent à l'environnement // (dev local ou production) sans rebuild. const apiUrl = process.env.NEXT_PUBLIC_DIRECTUS_API_URL ?? '' const wsUrl = process.env.NEXT_PUBLIC_DIRECTUS_API_WS_URL ?? '' // Tokens CSP — les guillemets simples font partie de la spec CSP, pas de JS const SELF = '\'self\'' const NONE = '\'none\'' const UNSAFE_INLINE = '\'unsafe-inline\'' // Content Security Policy // - unsafe-inline sur script-src : requis par Next.js App Router (hydratation inline) // - unsafe-inline sur style-src : requis par Emotion/MUI (styles injectés dynamiquement) // - data: blob: sur img-src : requis par html2canvas (export PDF) // - frame-ancestors 'none' : anti-clickjacking (complète X-Frame-Options) const cspDirectives = [ `default-src ${SELF}`, `script-src ${SELF} ${UNSAFE_INLINE}`, `style-src ${SELF} ${UNSAFE_INLINE}`, `connect-src ${SELF} ${apiUrl} ${wsUrl}`.trim(), `img-src ${SELF} data: blob:`, `font-src ${SELF}`, `object-src ${NONE}`, `base-uri ${SELF}`, `form-action ${SELF}`, `frame-ancestors ${NONE}`, ] const securityHeaders = [ { key: 'Content-Security-Policy', value: cspDirectives.join('; '), }, { key: 'X-Frame-Options', value: 'DENY', }, { key: 'X-Content-Type-Options', value: 'nosniff', }, { key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin', }, { key: 'Permissions-Policy', value: 'camera=(), microphone=(), geolocation=()', }, ] const nextConfig = { experimental: { // Optimiser les imports pour réduire la mémoire optimizePackageImports: ['@mui/material', '@mui/icons-material', '@emotion/react', '@emotion/styled'], }, // Réduire l'utilisation mémoire compress: true, // Désactiver les source maps en dev pour économiser la mémoire productionBrowserSourceMaps: false, async headers() { return [ { source: '/(.*)', headers: securityHeaders, }, ] }, } export default nextConfig