const withBundleAnalyzer = require('@next/bundle-analyzer')({ enabled: process.env.ANALYZE === 'true', }) const withPWA = require('next-pwa')({ dest: 'public', disable: false, // Enable PWA in all environments register: true, skipWaiting: true, // Disable PWA in development buildExcludes: [/middleware-manifest.json$/], // Enable PWA in production runtimeCaching: [ { urlPattern: /^https?:\/\//, handler: 'StaleWhileRevalidate', options: { cacheName: 'static-resources', expiration: { maxEntries: 60, maxAgeSeconds: 30 * 24 * 60 * 60, // 30 days }, }, }, ], }) // Security headers for production const ContentSecurityPolicy = ` default-src 'self'; script-src 'self' 'unsafe-eval' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src * blob: data:; media-src 'self' blob: data:; connect-src *; font-src 'self'; ` const securityHeaders = [ { key: 'Content-Security-Policy', value: ContentSecurityPolicy.replace(/\n/g, ''), }, { key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin', }, { key: 'X-Frame-Options', value: 'DENY', }, { key: 'X-Content-Type-Options', value: 'nosniff', }, { key: 'X-DNS-Prefetch-Control', value: 'on', }, { key: 'Strict-Transport-Security', value: 'max-age=31536000; includeSubDomains', }, { key: 'Permissions-Policy', value: 'camera=(), microphone=(self), geolocation=()', }, ] /** @type {import('next').NextConfig} */ const nextConfig = { output: 'standalone', reactStrictMode: true, trailingSlash: false, pageExtensions: ['ts', 'tsx', 'js', 'jsx'], eslint: { dirs: ['app', 'components', 'lib', 'hooks'], }, images: { remotePatterns: [ { protocol: 'https', hostname: 'picsum.photos', }, { protocol: 'https', hostname: 'images.unsplash.com', }, { protocol: 'https', hostname: 'lh3.googleusercontent.com', }, { protocol: 'https', hostname: 'via.placeholder.com', }, { protocol: 'http', hostname: 'minio-api.3027622.siliconpin.com', port: '9000', }, { protocol: 'https', hostname: 'file-delivery-prod-pdgctwgsnkiy.siliconpin.com', }, ], formats: ['image/webp', 'image/avif'], minimumCacheTTL: 60, deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840], imageSizes: [16, 32, 48, 64, 96, 128, 256, 384], }, async headers() { return [ { source: '/(.*)', headers: securityHeaders, }, // Cache static assets { source: '/static/(.*)', headers: [ { key: 'Cache-Control', value: 'public, max-age=31536000, immutable', }, ], }, // Cache images { source: '/_next/image(.*)', headers: [ { key: 'Cache-Control', value: 'public, max-age=31536000, immutable', }, ], }, // Cache Next.js static files { source: '/_next/static/(.*)', headers: [ { key: 'Cache-Control', value: 'public, max-age=31536000, immutable', }, ], }, // API routes cache control { source: '/api/(.*)', headers: [ { key: 'Cache-Control', value: 'no-store, max-age=0', }, ], }, ] }, webpack: (config, { dev, isServer }) => { // SVG support config.module.rules.push({ test: /\.svg$/, use: ['@svgr/webpack'], }) return config }, } module.exports = withBundleAnalyzer(withPWA(nextConfig))