import { NextRequest, NextResponse } from 'next/server'; /** * Defense-in-depth guard for authenticated areas. * * Auth is a Better Auth httpOnly session cookie, which IS visible to this * server-side middleware (unlike client JS). The API remains the authoritative * gate — cookie presence is not validated here; this only keeps clearly * unauthenticated visitors from loading the admin/dashboard JS shell. */ const SESSION_COOKIES = [ '__Secure-spanglish.session_token', // production (useSecureCookies) 'spanglish.session_token', // development ]; // Matched as a fallback so a change to Better Auth's `advanced.cookiePrefix` cannot // silently lock every user out of these routes. const SESSION_COOKIE_SUFFIX = '.session_token'; function hasSessionCookie(request: NextRequest): boolean { if (SESSION_COOKIES.some((name) => !!request.cookies.get(name)?.value)) return true; return request.cookies .getAll() .some((cookie) => cookie.name.endsWith(SESSION_COOKIE_SUFFIX) && !!cookie.value); } export function middleware(request: NextRequest) { const { pathname, search } = request.nextUrl; if (pathname.startsWith('/admin') || pathname.startsWith('/dashboard')) { if (!hasSessionCookie(request)) { const loginUrl = new URL('/login', request.url); // Keep the query string, so a bounced /admin/photos?page=3 resumes where it was. loginUrl.searchParams.set('redirect', `${pathname}${search}`); return NextResponse.redirect(loginUrl); } } return NextResponse.next(); } export const config = { matcher: ['/admin/:path*', '/dashboard/:path*'], };