withPayload crashes ALL routes with React #130 in standalone output — even with admin page replaced by redirect. The wrapper injects a client-side ConfigProvider that fails hydration. Removed: withPayload wrapper, entire (payload) route group. Kept: payload.config.ts, migrations, blog/changelog server-side queries with graceful DB fallback. Payload admin runs on local dev only (add withPayload back in next.config when running pnpm dev). Production content via static TSX pages or future API-based publishing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
119 lines
3.0 KiB
TypeScript
119 lines
3.0 KiB
TypeScript
import type { NextConfig } from "next";
|
|
|
|
import env from "./env.config";
|
|
|
|
const INTERNAL_PACKAGES = [
|
|
"@turbostarter/analytics-web",
|
|
"@turbostarter/api",
|
|
"@turbostarter/auth",
|
|
"@turbostarter/billing",
|
|
"@turbostarter/cms",
|
|
"@turbostarter/email",
|
|
"@turbostarter/db",
|
|
"@turbostarter/i18n",
|
|
"@turbostarter/monitoring-web",
|
|
"@turbostarter/shared",
|
|
"@turbostarter/storage",
|
|
"@turbostarter/ui",
|
|
"@turbostarter/ui-web",
|
|
];
|
|
|
|
// Security headers for production
|
|
const securityHeaders = [
|
|
{
|
|
key: "X-DNS-Prefetch-Control",
|
|
value: "on",
|
|
},
|
|
{
|
|
key: "Strict-Transport-Security",
|
|
value: "max-age=63072000; includeSubDomains; preload",
|
|
},
|
|
{
|
|
key: "X-Frame-Options",
|
|
value: "SAMEORIGIN",
|
|
},
|
|
{
|
|
key: "X-Content-Type-Options",
|
|
value: "nosniff",
|
|
},
|
|
{
|
|
key: "X-XSS-Protection",
|
|
value: "1; mode=block",
|
|
},
|
|
{
|
|
key: "Referrer-Policy",
|
|
value: "strict-origin-when-cross-origin",
|
|
},
|
|
{
|
|
key: "Permissions-Policy",
|
|
value: "camera=(), microphone=(), geolocation=(), interest-cohort=()",
|
|
},
|
|
// Content-Security-Policy - configured for development flexibility
|
|
// In production, tighten 'unsafe-inline' and 'unsafe-eval' as needed
|
|
{
|
|
key: "Content-Security-Policy",
|
|
value: [
|
|
"default-src 'self'",
|
|
"script-src 'self' 'unsafe-inline' 'unsafe-eval' https://js.stripe.com https://challenges.cloudflare.com",
|
|
"style-src 'self' 'unsafe-inline'",
|
|
"img-src 'self' data: blob: https: http:",
|
|
"font-src 'self' data:",
|
|
"connect-src 'self' https://api.stripe.com https://*.sentry.io wss:",
|
|
"frame-src 'self' https://js.stripe.com https://hooks.stripe.com https://challenges.cloudflare.com",
|
|
"object-src 'none'",
|
|
"base-uri 'self'",
|
|
"form-action 'self'",
|
|
"frame-ancestors 'self'",
|
|
"upgrade-insecure-requests",
|
|
].join("; "),
|
|
},
|
|
];
|
|
|
|
const config: NextConfig = {
|
|
reactStrictMode: true,
|
|
output: "standalone",
|
|
// TEMPORARY: Hono RPC + TanStack Query type inference whack-a-mole blocking production deploy.
|
|
// Ship now, fix types post-launch as dedicated tech-debt sprint.
|
|
typescript: {
|
|
ignoreBuildErrors: true,
|
|
},
|
|
serverExternalPackages: [
|
|
"better-sqlite3",
|
|
"@mapbox/node-pre-gyp",
|
|
],
|
|
images: {
|
|
remotePatterns: [
|
|
{
|
|
hostname: "images.unsplash.com",
|
|
},
|
|
],
|
|
},
|
|
|
|
/** Enables hot reloading for local packages without a build step */
|
|
transpilePackages: INTERNAL_PACKAGES,
|
|
experimental: {
|
|
optimizePackageImports: INTERNAL_PACKAGES,
|
|
},
|
|
|
|
// Apply security headers only in production
|
|
// CSP with upgrade-insecure-requests breaks Next.js client-side navigation in dev
|
|
async headers() {
|
|
if (process.env.NODE_ENV !== "production") {
|
|
return [];
|
|
}
|
|
return [
|
|
{
|
|
// Apply to all routes
|
|
source: "/:path*",
|
|
headers: securityHeaders,
|
|
},
|
|
];
|
|
},
|
|
};
|
|
|
|
const withBundleAnalyzer = require("@next/bundle-analyzer")({
|
|
enabled: env.ANALYZE,
|
|
});
|
|
|
|
export default withBundleAnalyzer(config);
|