/** @type {import('next').NextConfig} */ import {withSentryConfig} from '@sentry/nextjs' // 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 ?? '' // Le SDK Directus dérive l'URL WebSocket depuis apiUrl (https→wss, http→ws). // On l'inclut toujours dans connect-src pour garantir que CSP autorise la connexion, // même si NEXT_PUBLIC_DIRECTUS_API_WS_URL pointe vers un hôte différent. const derivedWsUrl = apiUrl .replace(/^https:\/\//, 'wss://') .replace(/^http:\/\//, 'ws://') // 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} ${derivedWsUrl}`.trim().replace(/ {2,}/g, ' '), `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 = { // Active le mode standalone : bundle minimal autonome pour Docker output: 'standalone', 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 withSentryConfig(nextConfig, { // Organisation et projet Sentry pour l'upload des source maps // Nécessite SENTRY_AUTH_TOKEN en CI pour activer l'upload silent: !process.env.CI, // Ne pas uploader les source maps si le token est absent (dev local) sourcemaps: { disable: !process.env.SENTRY_AUTH_TOKEN, }, // Évite d'exposer les source maps aux utilisateurs finaux hideSourceMaps: true, // Tunnel Sentry via notre propre domaine (contourne les adblockers) // tunnelRoute: '/monitoring', })