172 lines
3.7 KiB
JavaScript
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))
|