import type { NextConfig } from "next"; const isDev = process.env.NODE_ENV === "development"; /** * Content-Security-Policy (nonce'suz): Cache Components'in statik kabuğu ile uyumlu. * 'unsafe-inline' script: Next.js'in satır içi hydration verisi ve next-themes'in tema betiği için. * Grafikler (Recharts, SVG) ve carousel (Embla) ek kaynak gerektirmez; ek dosyalar * kendi origin'imizden (/api/files/...) ve blob: önizlemelerle servis edilir. */ const csp = [ "default-src 'self'", `script-src 'self' 'unsafe-inline'${isDev ? " 'unsafe-eval'" : ""}`, "style-src 'self' 'unsafe-inline'", "img-src 'self' blob: data:", "font-src 'self'", "connect-src 'self'", "media-src 'self' blob:", "worker-src 'self' blob:", "manifest-src 'self'", "object-src 'none'", "base-uri 'self'", "form-action 'self'", "frame-ancestors 'none'", ...(isDev ? [] : ["upgrade-insecure-requests"]), ].join("; "); const securityHeaders = [ { key: "Content-Security-Policy", value: csp }, { key: "X-Content-Type-Options", value: "nosniff" }, { key: "Referrer-Policy", value: "strict-origin-when-cross-origin" }, { key: "X-Frame-Options", value: "DENY" }, { key: "Cross-Origin-Opener-Policy", value: "same-origin" }, // Kamera: Faz 6'da fiş fotoğrafı için yalnızca kendi origin'imiz. { key: "Permissions-Policy", value: "camera=(self), microphone=(), geolocation=(), payment=(), usb=()", }, ...(isDev ? [] : // includeSubDomains/preload bilinçli olarak yok: ta1sph.website'in diğer alt alanlarını bağlamasın. [{ key: "Strict-Transport-Security", value: "max-age=31536000" }]), ]; /** * /api/files: kullanıcı dosyaları (WebP görsel, PDF). Sayfa CSP'si yerine daha sıkı bir politika: * betik yok; PDF'in tarayıcı görüntüleyicisinde açılabilmesi için object-src 'self'. */ const fileCsp = [ "default-src 'none'", "img-src 'self'", "style-src 'unsafe-inline'", "object-src 'self'", "frame-ancestors 'self'", ].join("; "); const nextConfig: NextConfig = { // Docker imajı için yalnızca gerekli dosyaları .next/standalone altına kopyala. output: "standalone", poweredByHeader: false, // HEIC çözücü (libheif WASM) bundle'a girmesin; Node require ile yüklensin. serverExternalPackages: ["heic-decode", "libheif-js"], experimental: { agentFeedback: true, }, cacheComponents: true, partialPrefetching: true, turbopack: { rules: { "*.css": { loaders: ["@tailwindcss/turbopack"], as: "*.css", }, }, }, async headers() { return [ { source: "/:path((?!api/files/).*)", headers: securityHeaders }, { source: "/api/files/:path*", headers: [ ...securityHeaders.filter((h) => h.key !== "Content-Security-Policy"), { key: "Content-Security-Policy", value: fileCsp }, ], }, ]; }, }; export default nextConfig;