From 0201d21c38b8a903a098afa40bfccd43e7ac2441 Mon Sep 17 00:00:00 2001 From: openhands Date: Tue, 25 Aug 2026 23:01:54 +0200 Subject: [PATCH] Fix site crash by restoring next-intl plugin and lost next.config.ts options Recent optimization commits accidentally gutted next.config.ts, removing the createNextIntlPlugin wrapper. This caused every page to crash at runtime with 'Couldn't find next-intl config file', showing the error page after a successful build. Restores: - next-intl plugin (./src/i18n/request.ts) - Security headers (HSTS, X-Frame-Options, nosniff, etc.) - Redirects from /admin/import/* to /admin/studio/* - Cache headers for /assets and /images, AVIF/WebP image formats - compress and productionBrowserSourceMaps Keeps recent improvements: reactStrictMode and optimizePackageImports. --- next.config.ts | 121 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 119 insertions(+), 2 deletions(-) diff --git a/next.config.ts b/next.config.ts index 9e47970dc6..68fbf94513 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,5 +1,6 @@ import { execSync } from "node:child_process"; import type { NextConfig } from "next"; +import createNextIntlPlugin from "next-intl/plugin"; const getGitCommit = () => { try { @@ -9,13 +10,129 @@ const getGitCommit = () => { } }; +const securityHeaders = [ + { key: "X-DNS-Prefetch-Control", value: "on" }, + { + 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=(), interest-cohort=()", + }, +]; + const nextConfig: NextConfig = { deploymentId: process.env.NEXT_DEPLOYMENT_ID?.trim() || getGitCommit(), reactStrictMode: true, - serverExternalPackages: ["lzma", "sharp"], + compress: true, + productionBrowserSourceMaps: false, + serverExternalPackages: ["lzma", "sharp", "pino", "pino-pretty"], + + async redirects() { + return [ + { + source: "/admin/import", + destination: "/admin/studio/furni", + permanent: true, + }, + { + source: "/admin/import/badges", + destination: "/admin/studio/badges", + permanent: true, + }, + { + source: "/admin/import/furni", + destination: "/admin/studio/furni", + permanent: true, + }, + { + source: "/admin/import/furni/upload", + destination: "/admin/studio/upload", + permanent: true, + }, + { + source: "/admin/import/clothing", + destination: "/admin/studio/clothing", + permanent: true, + }, + { + source: "/admin/import/effects", + destination: "/admin/studio/effects", + permanent: true, + }, + { + source: "/admin/import/pets", + destination: "/admin/studio/pets", + permanent: true, + }, + { + source: "/admin/import/clone", + destination: "/admin/studio/clone", + permanent: true, + }, + { + source: "/admin/import/sync", + destination: "/admin/studio/sync", + permanent: true, + }, + { + source: "/admin/import/repair-icons", + destination: "/admin/studio/repair-icons", + permanent: true, + }, + { + source: "/admin/import/audit", + destination: "/admin/studio/audit", + permanent: true, + }, + ]; + }, + + turbopack: { + ignoreIssue: [ + { + path: "**/src/lib/**", + }, + ], + }, + experimental: { optimizePackageImports: ["lucide-react", "date-fns"], + useTypeScriptCli: true, + hideLogsAfterAbort: true, + }, + + images: { + formats: ["image/avif", "image/webp"], + }, + + async headers() { + return [ + { + source: "/(.*)", + headers: securityHeaders, + }, + { + source: "/assets/(.*)", + headers: [ + { + key: "Cache-Control", + value: "public, max-age=31536000, immutable", + }, + ], + }, + { + source: "/images/(.*)", + headers: [{ key: "Cache-Control", value: "public, max-age=86400" }], + }, + ]; }, }; -export default nextConfig; +const withNextIntl = createNextIntlPlugin("./src/i18n/request.ts"); + +export default withNextIntl(nextConfig);