From 826f554ae1b26a0d813b03b8dba8793bf6da06c8 Mon Sep 17 00:00:00 2001 From: Nora Date: Fri, 26 Jun 2026 17:08:22 -0600 Subject: [PATCH] chore(infra): auth fast-path, pg pool tuning, react-doctor devdep MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three coordinated changes that improve CI/build ergonomics and dev-mode performance. None change database schema; all are reversible by env var. src/lib/auth.ts Wrap Neon Auth getSession() with a fast-path that returns null when NEON_AUTH_BASE_URL is unset/placeholder (CI build-time value) OR when the dev_session cookie is set with a recognized role. The real getSession() does a network fetch and pays a 30s DNS timeout when the env var is unset. Admin pages each call getSession (directly or via getAdminUser()) — paying that tax per page load is unacceptable. src/lib/db.ts + db/client.ts Raise PG_POOL_MAX default 10 -> 50 (Vercel function concurrency can exceed 10 with parallel RPCs). Lower PG_POOL_CONN_TIMEOUT_MS default 30s -> 5s in src/lib/db.ts and 10s -> 5s in db/client.ts so a bad Neon branch fails fast instead of hanging requests. src/lib/admin-permissions.ts + src/proxy.ts Add PERF_TEST_AUTH=1 env var that lets dev_session cookies bypass Neon Auth in production mode. Used by Playwright perf benchmarks against authenticated admin pages. NEVER set this in real production — the dev_session cookie then grants platform_admin / brand_admin / store_employee with no password check. The middleware guard in proxy.ts and getAdminUser() both honor the flag; the flag is the only toggle needed. package.json Add react-doctor ^0.5.8 to devDependencies (used to drive the lint cleanup that landed in the 38 commits already on this branch). --- db/client.ts | 4 +-- package.json | 1 + src/lib/admin-permissions.ts | 6 ++-- src/lib/auth.ts | 57 ++++++++++++++++++++++++++++++++++-- src/lib/db.ts | 4 +-- src/proxy.ts | 9 ++++-- 6 files changed, 70 insertions(+), 11 deletions(-) diff --git a/db/client.ts b/db/client.ts index ee14c55..2778d69 100644 --- a/db/client.ts +++ b/db/client.ts @@ -74,10 +74,10 @@ function getPool(): Pool { } _pool = new Pool({ connectionString, - max: parseInt(process.env.PG_POOL_MAX ?? "10", 10), + max: parseInt(process.env.PG_POOL_MAX ?? "50", 10), idleTimeoutMillis: parseInt(process.env.PG_POOL_IDLE_MS ?? "30000", 10), connectionTimeoutMillis: parseInt( - process.env.PG_POOL_CONN_TIMEOUT_MS ?? "10000", + process.env.PG_POOL_CONN_TIMEOUT_MS ?? "5000", 10, ), allowExitOnIdle: false, diff --git a/package.json b/package.json index 5309c49..34f9a81 100644 --- a/package.json +++ b/package.json @@ -72,6 +72,7 @@ "jsdom": "^25.0.1", "pg": "^8.20.0", "playwright": "^1.59.1", + "react-doctor": "^0.5.8", "tailwindcss": "^4", "tsx": "^4.22.4", "typescript": "^5", diff --git a/src/lib/admin-permissions.ts b/src/lib/admin-permissions.ts index a108f57..c09bdab 100644 --- a/src/lib/admin-permissions.ts +++ b/src/lib/admin-permissions.ts @@ -29,8 +29,10 @@ import type { AdminRole, AdminUser, TenantContext } from "@/lib/admin-permission * shows "Access Denied" — correct behavior. */ export async function getAdminUser(): Promise { - // Check for dev_session cookie in development mode - if (process.env.NODE_ENV !== "production") { + // Check for dev_session cookie in development mode, or when + // PERF_TEST_AUTH=1 (for production-mode perf benchmarks against + // authenticated admin pages — never enable in real production). + if (process.env.NODE_ENV !== "production" || process.env.PERF_TEST_AUTH === "1") { const cookieStore = await cookies(); const devSession = cookieStore.get("dev_session")?.value; if (devSession === "platform_admin" || devSession === "brand_admin" || devSession === "store_employee") { diff --git a/src/lib/auth.ts b/src/lib/auth.ts index 631be8c..b3eda90 100644 --- a/src/lib/auth.ts +++ b/src/lib/auth.ts @@ -19,14 +19,67 @@ import "server-only"; import { createNeonAuth } from "@neondatabase/auth/next/server"; import { getNeonAuthConfig } from "@/auth.config"; +import { cookies } from "next/headers"; const auth = createNeonAuth({ baseUrl: getNeonAuthConfig().baseUrl, cookies: { secret: getNeonAuthConfig().cookieSecret }, }); -export const { getSession, signIn, signOut, resetPassword, requestPasswordReset } = auth; -export const { listUsers, createUser, setRole, setUserPassword, updateUser } = auth.admin; +const { getSession: _realGetSession, signIn, signOut, resetPassword, requestPasswordReset } = auth; +const { listUsers, createUser, setRole, setUserPassword, updateUser } = auth.admin; + +export { signIn, signOut, resetPassword, requestPasswordReset }; +export { listUsers, createUser, setRole, setUserPassword, updateUser }; + +/** + * Fast-path wrapper around Neon Auth's `getSession()`. + * + * Neon Auth's real `getSession()` does a network fetch to the configured + * Auth base URL. When the env var is unset (or set to the CI build-time + * `placeholder.local`), every call hits a 30s DNS timeout before + * returning `null`. Admin pages each call `getSession()` (directly or + * via `getAdminUser()`) — paying that 30s tax on every page load is + * unacceptable. + * + * Short-circuits when: + * 1. `NEON_AUTH_BASE_URL` is unset / placeholder — Neon Auth can't + * resolve anything useful; return `null` immediately. + * 2. The `dev_session` cookie is set with a recognized role — we + * trust it as a development bypass and skip the network fetch. + * + * In production with a real `NEON_AUTH_BASE_URL` and no dev_session, + * falls through to the real Neon Auth lookup unchanged. + */ +function isAuthConfigured(): boolean { + const url = process.env.NEON_AUTH_BASE_URL; + if (!url || url === "placeholder" || url === "https://placeholder.local") return false; + return true; +} + +async function isDevSession(): Promise { + if (process.env.NODE_ENV === "production" && process.env.PERF_TEST_AUTH !== "1") return false; + try { + const store = await cookies(); + const v = store.get("dev_session")?.value; + return v === "platform_admin" || v === "brand_admin" || v === "store_employee"; + } catch { + return false; + } +} + +export async function getSession(): Promise>> { + if (!isAuthConfigured()) { + if (process.env.PERF_TEST_AUTH === "1") console.log("[auth] getSession short-circuit: not configured"); + return { data: null, error: null } as Awaited>; + } + if (await isDevSession()) { + if (process.env.PERF_TEST_AUTH === "1") console.log("[auth] getSession short-circuit: dev_session"); + return { data: null, error: null } as Awaited>; + } + if (process.env.PERF_TEST_AUTH === "1") console.log("[auth] getSession calling real Neon Auth"); + return _realGetSession(); +} /** * Re-exported for the API route handler compatibility. diff --git a/src/lib/db.ts b/src/lib/db.ts index f902c2e..971e97a 100644 --- a/src/lib/db.ts +++ b/src/lib/db.ts @@ -48,10 +48,10 @@ function buildPool(): Pool { // PG_POOL_MAX (default 10) // PG_POOL_IDLE_MS (default 30s) // PG_POOL_CONN_TIMEOUT_MS (default 10s) - max: parseInt(process.env.PG_POOL_MAX ?? "10", 10), + max: parseInt(process.env.PG_POOL_MAX ?? "50", 10), idleTimeoutMillis: parseInt(process.env.PG_POOL_IDLE_MS ?? "30000", 10), connectionTimeoutMillis: parseInt( - process.env.PG_POOL_CONN_TIMEOUT_MS ?? "30000", + process.env.PG_POOL_CONN_TIMEOUT_MS ?? "5000", 10, ), // Vercel/serverless recycling: keep the pool hot for warm invocations. diff --git a/src/proxy.ts b/src/proxy.ts index ba559e5..702fd97 100644 --- a/src/proxy.ts +++ b/src/proxy.ts @@ -104,14 +104,17 @@ function hasNeonAuthSession(request: NextRequest): boolean { } // Development bypass: allow dev_session cookie for local testing - // This bypass only works when NODE_ENV is not "production" - if (process.env.NODE_ENV !== "production") { + // This bypass works when NODE_ENV is not "production", OR when + // PERF_TEST_AUTH=1 is set (for production-mode perf benchmarks against + // authenticated admin pages — never enable in real production). + const perfTestAuth = process.env.PERF_TEST_AUTH === "1"; + if (process.env.NODE_ENV !== "production" || perfTestAuth) { const devSession = cookies.get("dev_session")?.value; if (devSession === "platform_admin" || devSession === "brand_admin" || devSession === "store_employee") { return true; } } - + return false; }