// middleware.ts import { NextResponse } from "next/server"; import type { NextRequest } from "next/server"; function redirectToLogin(req: NextRequest) { const url = req.nextUrl.clone(); url.pathname = "/login"; url.searchParams.set("next", req.nextUrl.pathname); return NextResponse.redirect(url); } function isPublicPath(pathname: string) { // Public pages if (pathname === "/") return true; if (pathname === "/login") return true; if (pathname === "/register") return true; if (pathname === "/tos") return true; if (pathname === "/privacy") return true; if (pathname === "/beta") return true; if (pathname.startsWith("/beta/")) return true; if (pathname.startsWith("/auth")) return true; if (pathname.startsWith("/api/auth")) return true; // Content / guides — always public, no auth required if (pathname === "/guides") return true; if (pathname.startsWith("/guides/")) return true; // Static / framework assets if (pathname.startsWith("/_next")) return true; if (pathname === "/favicon.ico") return true; if (pathname === "/robots.txt") return true; if (pathname === "/sitemap.xml") return true; // Static images in /public if (pathname.startsWith("/battl/")) return true; // Health / misc APIs you may want public if (pathname === "/api/health") return true; return false; } function isProtectedLaunchPath(pathname: string) { return ( pathname === "/builder" || pathname.startsWith("/builder/") || pathname === "/builds" || pathname.startsWith("/builds/") || pathname === "/vault" || pathname.startsWith("/vault/") ); } export function middleware(req: NextRequest) { const { pathname } = req.nextUrl; // Short-circuit: always let public paths through without any token checks. if (isPublicPath(pathname)) return NextResponse.next(); const token = req.cookies.get("session_token")?.value; const isLoggedIn = Boolean(token); // IMPORTANT: Use a server-only env var for middleware behavior. // Set LAUNCH_ONLY_ROOT=true in your Docker compose. const launchOnlyRoot = process.env.LAUNCH_ONLY_ROOT === "true"; // In "launch only" mode, anonymous users are blocked only from gated app areas. if (launchOnlyRoot && !isLoggedIn && isProtectedLaunchPath(pathname)) { return redirectToLogin(req); } // --- Protected API routes --- if (pathname.startsWith("/api/admin")) { if (!isLoggedIn) { return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); } } if (pathname.startsWith("/api/builds")) { if (!isLoggedIn) { return NextResponse.json({ error: "Unauthorized" }, { status: 401 }); } } // --- Protected pages --- if (pathname.startsWith("/admin")) { if (!isLoggedIn) return redirectToLogin(req); } return NextResponse.next(); } export const config = { // Run middleware for all application routes, but avoid static assets. matcher: [ "/((?!_next/static|_next/image|favicon.ico|robots.txt|sitemap.xml).*)", ], };