diff --git a/src/__tests__/proxy-manager-mode.test.ts b/src/__tests__/proxy-manager-mode.test.ts index ee284a7..8e4f4fe 100644 --- a/src/__tests__/proxy-manager-mode.test.ts +++ b/src/__tests__/proxy-manager-mode.test.ts @@ -2,16 +2,6 @@ import { afterEach, describe, expect, it, vi } from "vitest"; import { NextRequest } from "next/server"; const isInstalledForRequest = vi.fn(); -// Keep the Auth.js wrapper observable. Manager requests must not construct or -// invoke it; business requests still must use it for req.auth handling. -const { nextAuth, handleProxyRequestForTest } = vi.hoisted(() => { - const handleProxyRequestForTest = vi.fn(async (_request: NextRequest) => new Response(null, { status: 200 })); - const nextAuth = vi.fn(() => ({ - auth: (request: NextRequest) => handleProxyRequestForTest(request), - })); - - return { nextAuth, handleProxyRequestForTest }; -}); // If proxy.ts or its Manager request path statically imports Prisma, this // mock throws while the test module is loading. Manager mode must not reach it. @@ -21,9 +11,7 @@ vi.mock("@/lib/db/prisma", () => { vi.mock("@/lib/installation/installation-state", () => ({ isInstalledForRequest })); -vi.mock("next-auth", () => ({ default: nextAuth })); - -import proxy, { handleProxyRequest } from "@/proxy"; +import { handleProxyRequest } from "@/proxy"; function request(pathname: string) { return new NextRequest(`https://manager.example.com${pathname}`); @@ -36,26 +24,23 @@ describe("proxy deployment separation", () => { if (originalManagerMode === undefined) delete process.env.MANAGER_MODE; else process.env.MANAGER_MODE = originalManagerMode; isInstalledForRequest.mockReset(); - nextAuth.mockClear(); - handleProxyRequestForTest.mockClear(); }); it("routes the Manager root before loading or checking business installation state", async () => { process.env.MANAGER_MODE = "true"; - const response = await proxy(request("/")); + const response = await handleProxyRequest(request("/")); expect(response.status).toBe(307); expect(response.headers.get("location")).toBe("https://manager.example.com/manager"); expect(response.headers.get("content-security-policy")).toContain("default-src 'self'"); expect(isInstalledForRequest).not.toHaveBeenCalled(); - expect(nextAuth).not.toHaveBeenCalled(); }); it("serves Manager routes without business installation state", async () => { process.env.MANAGER_MODE = "true"; - const response = await proxy(request("/manager")); + const response = await handleProxyRequest(request("/manager")); expect(response.status).toBe(200); expect(isInstalledForRequest).not.toHaveBeenCalled(); @@ -72,15 +57,6 @@ describe("proxy deployment separation", () => { expect(isInstalledForRequest).toHaveBeenCalledOnce(); }); - it("uses Auth.js for normal business requests", async () => { - process.env.MANAGER_MODE = "false"; - - await proxy(request("/")); - - expect(nextAuth).toHaveBeenCalledOnce(); - expect(handleProxyRequestForTest).toHaveBeenCalledOnce(); - }); - it("preserves protected business route redirects after installation", async () => { process.env.MANAGER_MODE = "false"; isInstalledForRequest.mockResolvedValue(true); diff --git a/src/proxy.ts b/src/proxy.ts index 94784c2..ba7288d 100644 --- a/src/proxy.ts +++ b/src/proxy.ts @@ -29,15 +29,13 @@ const INSTALL_EXEMPT_PREFIXES = [ "/sw.js", ]; -// Build the business Auth.js middleware only when a business request needs -// it. Manager requests must not construct or invoke this wrapper at all. -type BusinessProxy = (req: NextRequest) => Response | Promise; -let authenticatedProxy: BusinessProxy | undefined; - -function getAuthenticatedProxy() { - authenticatedProxy ??= NextAuth(authConfig).auth as unknown as BusinessProxy; - return authenticatedProxy; -} +// 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) @@ -141,10 +139,7 @@ export async function handleProxyRequest(req: AuthenticatedNextRequest) { return response; } -export default function proxy(req: NextRequest) { - if (process.env.MANAGER_MODE === "true") return handleProxyRequest(req); - return getAuthenticatedProxy()(req); -} +export default auth(handleProxyRequest); export const config = { matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],