/** @type {import('next').NextConfig} */ const nextConfig = { reactStrictMode: true, output: "standalone", images: { remotePatterns: [ // Allow image optimisation from any host. Lock this down once we // know exactly which sources we'll pull attachments from. { protocol: "http", hostname: "**" }, { protocol: "https", hostname: "**" }, ], }, // Allow any host / IP / tunneled domain so the app is reachable from // LAN devices, reverse proxies and custom hostnames. Next 16's // internal matcher rejects the literal "*", so we also rely on the // `scripts/patch-next-dev-blocker.cjs` postinstall hook to neuter // the dev-only cross-site guard. Production behaviour is unaffected // because the patch only runs in development. allowedDevOrigins: ["*", "192.168.50.126"], experimental: { serverActions: { allowedOrigins: ["*"], }, }, // pdfmake's server-side PdfPrinter pulls in pdfkit -> @foliojs-fork/fontkit, // which loads font-shaping tables (data.trie etc.) from a path relative to // its own installed location at runtime. Webpack-bundled into a Server // Component/Route Handler chunk, that relative lookup 404s (ENOENT) because // the .trie files never get copied alongside the bundle. Left as native // Node `require`, the real node_modules layout resolves them correctly — // the same reason `sharp` and `@react-pdf/renderer` ship in Next's own // default external-packages list. serverExternalPackages: ["pdfmake", "pdfkit", "@foliojs-fork/fontkit", "unpdf"], // Maps moved from Sites to Surveying; keep old bookmarks working. async redirects() { return [ { source: "/dashboard/sites/maps", destination: "/dashboard/surveying/maps", permanent: false }, { source: "/dashboard/sites/:id/:map(map|map2|map3)", destination: "/dashboard/surveying/maps/:id/:map", permanent: false }, ]; }, }; const withPWA = require("@ducanh2912/next-pwa").default({ // Development and production share public/sw.js. Never let a build-generated // worker control HMR assets or rewrite the worker during local recompilation. disable: process.env.NODE_ENV === "development", dest: "public", cacheOnFrontEndNav: false, aggressiveFrontEndNavCaching: false, reloadOnOnline: true, register: true, extendDefaultRuntimeCaching: true, workboxOptions: { cleanupOutdatedCaches: true, clientsClaim: true, skipWaiting: true, runtimeCaching: [ // Account and site access can change at any time. A service worker // must not replay another user's dashboard after sign-out. { urlPattern: ({ sameOrigin, url }) => sameOrigin && (/^\/(dashboard|clock|login|change-password)(\/|$)/.test(url.pathname) || url.pathname.startsWith("/api/")), handler: "NetworkOnly", }, // The QR confirmation must always use the server's current local-day // decision. A cached yesterday lookup can say OUT while a POST writes IN. { urlPattern: ({ sameOrigin, url }) => sameOrigin && url.pathname === "/api/clock/employee-lookup", handler: "NetworkOnly", }, ], }, // The Web App Manifest lives at public/manifest.json (static file). // next-pwa does NOT generate it — the `manifest` option was silently // ignored in @ducanh2912/next-pwa@10.2.9. The dashboard metadata lives // in public/manifest.json; the chat metadata lives in // public/chat.webmanifest. Both are referenced from the per-route // metadata blocks in src/app/layout.tsx and src/app/dashboard/chat/page.tsx. }); module.exports = withPWA(nextConfig);