/** @type {import('next').NextConfig} */ // Backend origin for API/upload proxying. Configurable per environment instead of // being hardcoded to localhost. const BACKEND_URL = process.env.BACKEND_URL || 'http://localhost:3001'; // The standalone photo gallery service (photo-api/). Must be rewritten // before the generic /api rule below — Next rewrites are order-sensitive. const PHOTO_API_URL = process.env.PHOTO_API_URL || 'http://localhost:3003'; // Extra image hosts can be allowed via a comma-separated env var (e.g. a CDN). const extraImageHosts = (process.env.NEXT_PUBLIC_IMAGE_HOSTS || '') .split(',') .map((h) => h.trim()) .filter(Boolean) .map((hostname) => ({ protocol: 'https', hostname })); const securityHeaders = [ { key: 'X-Content-Type-Options', value: 'nosniff' }, { key: 'X-Frame-Options', value: 'SAMEORIGIN' }, { key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' }, { key: 'X-XSS-Protection', value: '0' }, { key: 'Permissions-Policy', value: 'camera=(self), microphone=(), geolocation=()' }, ]; const nextConfig = { // Overridable so a production build can run while `next dev` holds .next // (e.g. NEXT_DIST_DIR=.next-build npm run build). distDir: process.env.NEXT_DIST_DIR || '.next', images: { // Restrict remote image sources to a known allowlist instead of allowing any // https host (which let the Next image optimizer be used as an open proxy). remotePatterns: [ { protocol: 'https', hostname: 'images.unsplash.com' }, { protocol: 'http', hostname: 'localhost', port: '3001' }, ...extraImageHosts, ], }, async headers() { return [ { source: '/:path*', headers: securityHeaders, }, ]; }, async rewrites() { return [ { source: '/api/photos/:path*', destination: `${PHOTO_API_URL}/api/photos/:path*`, }, { source: '/api/:path*', destination: `${BACKEND_URL}/api/:path*`, }, { source: '/uploads/:path*', destination: `${BACKEND_URL}/uploads/:path*`, }, ]; }, }; module.exports = nextConfig;