// next.config.js const nextConfig = { // Add other configurations below reactStrictMode: true, // متادیتا (canonical/OG/keywords) باید داخل رندر شود نه استریم در . // از Next 15.2 استریم متادیتا پیش‌فرض است؛ این regex همه‌ی UAها را blocking می‌کند. htmlLimitedBots: /.*/, // React Compiler (پایدار در Next 16) — memoization خودکار کامپوننت‌ها. reactCompiler: true, // نکته: cacheComponents فعال نشد. معماری multi-domain به headers() (هدر host) در // layout و generateMetadata وابسته است؛ با cacheComponents هر دسترسی uncached به // headers() باید داخل بسته شود (build روی /_not-found شکست خورد). این کار // نیازمند Suspenseگذاری گسترده و ریسک نشت محتوای شهر بین دامنه‌هاست — فعلاً امن نیست. images: { remotePatterns: [ { protocol: 'https', hostname: 'clinic-pro.ir', }, { protocol: 'https', hostname: 'clinic-pro.ddev.site', }, ], }, env: { // Make DEV_MODE available to the application DEV_MODE: process.env.DEV_MODE, }, // Additional configuration for better Docker support trailingSlash: false, compress: true, poweredByHeader: false, // Enable standalone output for Docker output: 'standalone', async headers() { const isDev = process.env.NODE_ENV !== 'production'; let apiOrigin = ''; try { apiOrigin = new URL(process.env.NEXT_PUBLIC_API_URL).origin; } catch { apiOrigin = ''; } const connectSrc = [ "'self'", apiOrigin, 'https://clinic-pro.ir', 'https://back-dev.clinic-pro.ir', 'http://clinic-pro.ddev.site', // Google Analytics / Tag Manager endpoints 'https://www.google-analytics.com', 'https://*.google-analytics.com', 'https://www.googletagmanager.com', 'https://*.analytics.google.com', 'https://stats.g.doubleclick.net', ...(isDev ? ['ws:', 'wss:'] : []), ] .filter(Boolean) .join(' '); const csp = [ "default-src 'self'", "img-src 'self' https: data: blob:", "script-src 'self' 'unsafe-inline' 'unsafe-eval' https://www.googletagmanager.com https://www.google-analytics.com", // ALTCHA کپچا برای حل proof-of-work از Web Workerِ blob استفاده می‌کند؛ // بدون این، worker بلاک و کپچا «احراز هویت ناموفق» می‌دهد. "worker-src 'self' blob:", "style-src 'self' 'unsafe-inline'", "font-src 'self' data:", 'connect-src ' + connectSrc, "frame-ancestors 'none'", "object-src 'none'", "base-uri 'self'", "form-action 'self'", ].join('; '); return [ { source: '/(.*)', headers: [ { key: 'Strict-Transport-Security', value: 'max-age=63072000; includeSubDomains; preload' }, { key: 'X-Frame-Options', value: 'DENY' }, { key: 'X-Content-Type-Options', value: 'nosniff' }, { key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' }, { key: 'Permissions-Policy', value: 'camera=(), microphone=(), geolocation=()' }, { key: 'Content-Security-Policy', value: csp }, ], }, ]; }, }; module.exports = nextConfig;