import NextAuth from "next-auth"; import { NextResponse } from "next/server"; import { authConfig } from "@/auth.config"; import { isInstalledForRequest } from "@/lib/installation/installation-state"; // /home, /appointments and /settings are intentionally NOT hard-gated here // as of Phase 4 — Home must work for guests (public business browsing + // booking), and /appointments and /settings each show their own friendly // in-context sign-in prompt for a guest rather than a silent redirect with // no explanation. Only /staff and /owner remain hard-gated: those are // permission-based tooling with no meaningful "guest" experience at all. const PROTECTED_PREFIXES = ["/staff", "/owner"]; // Reachable before installation completes: the installer itself, and the // small set of static/manifest assets a browser requests independently of // the page it's rendering (favicon, PWA manifest/icons, the offline // fallback, the service worker script). Everything else redirects to // /install until ApplicationInstallation flips to INSTALLED — see // src/lib/installation/installation-state.ts. `/api/*` never reaches this // file at all (excluded by `config.matcher` below); API routes that need // business data guard themselves (see /api/health and // /api/cron/process-notifications). const INSTALL_EXEMPT_PREFIXES = [ "/install", "/offline", "/manifest.webmanifest", "/icon.png", "/apple-icon.png", "/icons/", "/sw.js", ]; // Lightweight instance built from the DB-free config — see auth.config.ts. // Next.js 16 renamed this file convention from `middleware` to `proxy` // (https://nextjs.org/docs/messages/middleware-to-proxy); it now always // runs on the Node.js runtime rather than Edge, but this stays split from // the full `@/auth` regardless, so checking "is there a session" doesn't // spin up a Postgres connection pool on every request. const { auth } = NextAuth(authConfig); /** * Nonce-gated 'strict-dynamic' script-src (Next's own documented pattern) * was tried here first and reverted: verified live against a production * build, it broke every page — Next's own inline RSC-hydration payload * scripts (the `self.__next_f.push(...)` chunks each page ships to * stream in its data) never actually receive the nonce this generates, * so 'strict-dynamic' refuses to run them and the app never hydrates * (confirmed via BAILOUT_TO_CLIENT_SIDE_RENDERING markers with * `"nonce":"$undefined"` in the served HTML). `script-src 'self' * 'unsafe-inline'` is the reliable fallback: Next's own *code* all loads * via external `