import { NextResponse } from "next/server"; // There used to be a cookie-based redirect-to-/login check here, gated on // the "refreshToken" cookie. It could never work: that cookie is set by the // BACKEND's own origin (a different domain, apicorebackend.* vs apicore.*), // scoped to /api/v1/auth - the browser never sends it on requests to the // frontend, so this proxy (running on the frontend's origin) can never see // it. The check always evaluated "no session", harmless while this file // sat in the wrong directory and never ran at all, but once relocated so // Next.js actually picked it up, it started force-redirecting every visit // to /settings, /apiaries, /colonies, /queens straight back to /login, // regardless of whether the user was genuinely logged in - login state for // this app is only observable client-side (in-memory access token, // cross-origin refresh cookie), which is exactly what // src/app/(app)/layout.tsx already correctly redirects on. Removed rather // than patched: there is no way to make a cross-origin cookie visible to // this server without weakening the cookie itself (e.g. dropping its path // scope), which isn't worth it for a redirect the client already handles. // Deliberately computed here, not in next.config.ts: that file's headers() // is resolved once at build time and frozen into the standalone output, so // process.env.API_URL there is whatever was (or wasn't) set during the // Docker build - never the container's actual runtime value. Proxy runs the // Node.js runtime fresh on every request, so this reads the real deployed // API_URL correctly instead of silently falling back to localhost forever. function apiOrigin(): string { try { return new URL(process.env.API_URL ?? "http://localhost:3001").origin; } catch { return ""; } } function cspHeader(): string { return ` default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' blob: data:; media-src 'self' blob:; font-src 'self'; connect-src 'self' ${apiOrigin()}; object-src 'none'; base-uri 'self'; form-action 'self'; frame-ancestors 'self'; upgrade-insecure-requests; ` .replace(/\s{2,}/g, " ") .trim(); } function withSecurityHeaders(response: NextResponse): NextResponse { response.headers.set("Content-Security-Policy", cspHeader()); response.headers.set("X-Frame-Options", "SAMEORIGIN"); response.headers.set("X-Content-Type-Options", "nosniff"); response.headers.set("Referrer-Policy", "no-referrer"); response.headers.set( "Strict-Transport-Security", "max-age=31536000; includeSubDomains", ); return response; } export function proxy() { return withSecurityHeaders(NextResponse.next()); } // Static files without security headers: build assets, the worker, the // manifest and the app icons (public/icons/*.png, src/app/apple-icon.png). // Dots are escaped and file names end at `$`, so no page that merely starts // like one of them (`/iconsx`, `/favicon.icox`, `/icons/x/y`) loses its // headers; manifest.spec checks it. export const config = { matcher: [ "/((?!_next/static/|_next/image$|favicon\\.ico$|sw\\.js$|manifest\\.webmanifest$|icon\\.svg$|apple-icon\\.png$|icons/[\\w-]+\\.png$).*)", ], };