// middleware.ts import { NextResponse, NextRequest } from "next/server"; const PUBLIC_PATHS = new Set([ "/auth/sign-in", "/auth/sign-up", ]); // If you have additional public pages (e.g., marketing), add them here. // Keep API endpoints out of this middleware unless you explicitly want to block them. export function middleware(req: NextRequest) { const { pathname, search } = req.nextUrl; const isPublic = isPublicPath(pathname); const isAuthRoute = pathname.startsWith("/auth/"); const token = req.cookies.get("ma_at")?.value ?? ""; // 1) If already authed and on an auth route, dump to /portal if (token && isAuthRoute) { const url = req.nextUrl.clone(); url.pathname = "/portal"; url.search = ""; return NextResponse.redirect(url); } // 2) If not authed and path is protected → send to sign-in with next= if (!token && !isPublic) { const url = req.nextUrl.clone(); url.pathname = "/auth/sign-in"; // Default to /portal after login, but preserve deep-link if present const next = pathname + (search || ""); url.search = next ? `?next=${encodeURIComponent(next)}` : `?next=${encodeURIComponent("/portal")}`; return NextResponse.redirect(url); } // 3) Otherwise, allow through return NextResponse.next(); } // Helpers function isPublicPath(pathname: string): boolean { // Public routes if (PUBLIC_PATHS.has(pathname)) return true; // Static assets and framework internals if ( pathname.startsWith("/_next/") || pathname.startsWith("/static/") || pathname.startsWith("/images/") || pathname === "/favicon.ico" || pathname === "/robots.txt" || pathname === "/sitemap.xml" ) return true; // API routes: by default we *do not* block /api/* in middleware (let routes handle auth) if (pathname.startsWith("/api/")) return true; // Everything else is protected return false; } export const config = { // Run middleware for all paths except the most common static files (belt & suspenders) matcher: [ "/((?!_next/static|_next/image|favicon.ico|robots.txt|sitemap.xml|images|static).*)", ], };