ai-wpa/next.config.js

172 lines
3.7 KiB
JavaScript

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))