import { NextRequest, NextResponse } from "next/server"; // Public marketing pages — no session required. Keep this in sync with // MARKETING_PATHS in components/ChromeSwitcher.tsx (that one picks page // chrome; this one decides whether to redirect at all). const PUBLIC_PATHS = new Set(["/", "/features", "/pricing", "/faq", "/terms", "/privacy", "/waitlist"]); // /signup and /onboarding are deliberately NOT gated by the drb_session // cookie here, even though they aren't "public" in the sense of not needing // an account — AuthProvider only sets that cookie once a user has an org_id // claim (SAAS_PLAN.md B3's no-claim guard), and /onboarding exists // specifically for a signed-in user who doesn't have one yet. Gating it on // the same cookie would bounce the exact users who need it back to /login // before ChromeSwitcher's client-side redirect ever runs. Both pages do // their own client-side auth check (redirect to /login if genuinely signed // out) instead. const NO_SESSION_COOKIE_GATE = new Set(["/signup", "/onboarding"]); // NOTE: this is a UX redirect only, not a security boundary — it just checks // a client-set cookie's presence. Real enforcement is server-side, in // drb-c2-core/app/internal/auth.py. See CLAUDE.md. export function middleware(request: NextRequest) { const session = request.cookies.get("drb_session"); const { pathname } = request.nextUrl; if (PUBLIC_PATHS.has(pathname) || NO_SESSION_COOKIE_GATE.has(pathname)) { return NextResponse.next(); } if (pathname === "/login") { if (session) return NextResponse.redirect(new URL("/dashboard", request.url)); return NextResponse.next(); } if (!session) { return NextResponse.redirect(new URL("/login", request.url)); } return NextResponse.next(); } export const config = { matcher: ["/((?!_next/static|_next/image|favicon\\.ico).*)"], };