import 'dotenv/config'; import pkg from './package.json' with { type: 'json' }; const TRACKER_SCRIPT = '/script.js'; const basePath = process.env.BASE_PATH || ''; const cloudMode = process.env.CLOUD_MODE || ''; const cloudUrl = process.env.CLOUD_URL || ''; const collectApiEndpoint = process.env.COLLECT_API_ENDPOINT || ''; const corsMaxAge = process.env.CORS_MAX_AGE || ''; const defaultLocale = process.env.DEFAULT_LOCALE || ''; const forceSSL = process.env.FORCE_SSL || ''; const frameAncestors = process.env.ALLOWED_FRAME_URLS || ''; const trackerScriptName = process.env.TRACKER_SCRIPT_NAME || ''; const trackerScriptURL = process.env.TRACKER_SCRIPT_URL || ''; const contentSecurityPolicy = ` default-src 'self'; img-src 'self' https: data:; script-src 'self' 'unsafe-eval' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; connect-src 'self' https:; frame-ancestors 'self' ${frameAncestors}; `; const defaultHeaders = [ { key: 'X-DNS-Prefetch-Control', value: 'on', }, { key: 'Content-Security-Policy', value: contentSecurityPolicy.replace(/\s{2,}/g, ' ').trim(), }, ]; if (forceSSL) { defaultHeaders.push({ key: 'Strict-Transport-Security', value: 'max-age=63072000; includeSubDomains; preload', }); } const trackerHeaders = [ { key: 'Access-Control-Allow-Origin', value: '*', }, { key: 'Cache-Control', value: 'public, max-age=86400, must-revalidate', }, ]; const apiHeaders = [ { key: 'Access-Control-Allow-Origin', value: '*', }, { key: 'Access-Control-Allow-Headers', value: '*', }, { key: 'Access-Control-Allow-Methods', value: 'GET, DELETE, POST, PUT', }, { key: 'Access-Control-Max-Age', value: corsMaxAge || '86400', }, { key: 'Cache-Control', value: 'no-cache', }, ]; const headers = [ { source: '/api/:path*', headers: apiHeaders, }, { source: '/:path*', headers: defaultHeaders, }, { source: TRACKER_SCRIPT, headers: trackerHeaders, }, ]; const rewrites = []; if (trackerScriptURL) { rewrites.push({ source: TRACKER_SCRIPT, destination: trackerScriptURL, }); } if (collectApiEndpoint) { headers.push({ source: collectApiEndpoint, headers: apiHeaders, }); rewrites.push({ source: collectApiEndpoint, destination: '/api/send', }); } const redirects = [ { source: '/settings', destination: '/settings/preferences', permanent: false, }, { source: '/teams/:id', destination: '/teams/:id/websites', permanent: false, }, { source: '/teams/:id/settings', destination: '/teams/:id/settings/preferences', permanent: false, }, { source: '/admin', destination: '/admin/users', permanent: false, }, ]; // Adding rewrites + headers for all alternative tracker script names. if (trackerScriptName) { const names = trackerScriptName?.split(',').map(name => name.trim()); if (names) { names.forEach(name => { const normalizedSource = `/${name.replace(/^\/+/, '')}`; rewrites.push({ source: normalizedSource, destination: TRACKER_SCRIPT, }); headers.push({ source: normalizedSource, headers: trackerHeaders, }); }); } } if (cloudMode) { rewrites.push({ source: '/script.js', destination: 'https://cloud.umami.is/script.js', }); } /** @type {import('next').NextConfig} */ export default { reactStrictMode: false, env: { basePath, cloudMode, cloudUrl, currentVersion: pkg.version, defaultLocale, }, basePath, output: 'standalone', eslint: { ignoreDuringBuilds: true, }, typescript: { ignoreBuildErrors: true, }, async headers() { return headers; }, async rewrites() { return [ ...rewrites, { source: '/telemetry.js', destination: '/api/scripts/telemetry', }, { source: '/teams/:teamId/:path*', destination: '/:path*', }, ]; }, async redirects() { return [...redirects]; }, };