// middleware.ts import { NextResponse, NextRequest } from "next/server"; const PUBLIC_PATHS = new Set([ "/auth/sign-in", "/auth/sign-up", ]); export function middleware(req: NextRequest) { const url = req.nextUrl.clone(); const { pathname } = url; // ── 1) Legacy → Portal mapping (runs before auth gating) const mapped = legacyMap(pathname); if (mapped) { url.pathname = mapped.pathname; // keep existing query, add/override provided params (e.g., ?t=fiber) if (mapped.query) { for (const [k, v] of Object.entries(mapped.query)) { url.searchParams.set(k, v); } } return NextResponse.redirect(url); } // ── 2) Auth gating const token = req.cookies.get("ma_at")?.value ?? ""; const isAuthRoute = pathname.startsWith("/auth/"); // Authed users on any /auth/* route → /portal if (token && isAuthRoute) { url.pathname = "/portal"; url.search = ""; return NextResponse.redirect(url); } // Unauthed users on protected paths → sign-in (no ?next=) if (!token && !isPublicPath(pathname)) { url.pathname = "/auth/sign-in"; url.search = ""; return NextResponse.redirect(url); } return NextResponse.next(); } type MapResult = { pathname: string; query?: Record }; function legacyMap(pathname: string): MapResult | null { switch (pathname) { // Laser settings (old links) case "/fiber-settings": return { pathname: "/portal/laser-settings", query: { t: "fiber" } }; case "/uv-settings": return { pathname: "/portal/laser-settings", query: { t: "uv" } }; case "/co2-galvo-settings": return { pathname: "/portal/laser-settings", query: { t: "co2-galvo" } }; case "/co2-gantry-settings": return { pathname: "/portal/laser-settings", query: { t: "co2-gantry" } }; // Materials (both legacy structures) case "/materials": return { pathname: "/portal/materials", query: { t: "materials" } }; case "/materials/materials": return { pathname: "/portal/materials", query: { t: "materials" } }; case "/materials/materials-coatings": return { pathname: "/portal/materials", query: { t: "materials-coatings" } }; case "/materials-coatings": return { pathname: "/portal/materials", query: { t: "materials-coatings" } }; // Lasers / Projects / Rigs (legacy) case "/lasers": return { pathname: "/portal/laser-sources" }; case "/projects": return { pathname: "/portal/projects" }; case "/my/rigs": return { pathname: "/portal/rigs", query: { t: "my" } }; default: return null; } } function isPublicPath(pathname: string): boolean { if (PUBLIC_PATHS.has(pathname)) return true; // Static assets / internals if ( pathname.startsWith("/_next/") || pathname.startsWith("/static/") || pathname.startsWith("/images/") || pathname === "/favicon.ico" || pathname === "/robots.txt" || pathname === "/sitemap.xml" ) return true; // API routes aren't gated here; each route should enforce auth as needed if (pathname.startsWith("/api/")) return true; // Everything else is protected return false; } export const config = { matcher: [ "/((?!_next/static|_next/image|favicon.ico|robots.txt|sitemap.xml|images|static).*)", ], };