2026-04-13 21:55:40 +04:00
|
|
|
/** @type {import('next').NextConfig} */
|
2026-04-14 06:48:55 +04:00
|
|
|
import {withSentryConfig} from '@sentry/nextjs'
|
2026-04-13 21:55:40 +04:00
|
|
|
|
|
|
|
|
// 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 ?? ''
|
|
|
|
|
|
2026-04-14 17:38:23 +04:00
|
|
|
// 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://')
|
|
|
|
|
|
2026-04-13 21:55:40 +04:00
|
|
|
// 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}`,
|
2026-04-14 17:38:23 +04:00
|
|
|
`connect-src ${SELF} ${apiUrl} ${wsUrl} ${derivedWsUrl}`.trim().replace(/ {2,}/g, ' '),
|
2026-04-13 21:55:40 +04:00
|
|
|
`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 = {
|
2026-04-14 06:55:41 +04:00
|
|
|
// Active le mode standalone : bundle minimal autonome pour Docker
|
|
|
|
|
output: 'standalone',
|
2026-04-13 21:55:40 +04:00
|
|
|
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,
|
|
|
|
|
},
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-14 06:48:55 +04:00
|
|
|
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',
|
|
|
|
|
})
|