Files
gridpilot.gg/apps/website/middleware.ts
2026-01-17 22:55:03 +01:00

127 lines
4.3 KiB
TypeScript

import type { NextRequest } from 'next/server';
import { NextResponse } from 'next/server';
import { SessionGateway } from '@/lib/gateways/SessionGateway';
import { handleAuthFlow } from '@/lib/auth/AuthFlowRouter';
import { ConsoleLogger } from '@/lib/infrastructure/logging/ConsoleLogger';
import { routeMatchers } from '@/lib/routing/RouteConfig';
import { SearchParamBuilder } from '@/lib/routing/search-params/SearchParamBuilder';
const logger = new ConsoleLogger();
/**
* Server-side route protection middleware
*
* Uses UnifiedLoginStateMachine for deterministic, type-safe authentication flow
*/
export async function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;
const cookieHeader = request.headers.get('cookie') || '';
logger.info('[MIDDLEWARE] ========== REQUEST START ==========');
logger.info('[MIDDLEWARE] Request details', {
pathname,
method: request.method,
url: request.url,
cookieHeaderLength: cookieHeader.length,
cookiePreview: cookieHeader.substring(0, 100) + (cookieHeader.length > 100 ? '...' : '')
});
// Set x-pathname header for layout-level protection
const response = NextResponse.next();
response.headers.set('x-pathname', pathname);
// Get session
logger.info('[MIDDLEWARE] Fetching session...');
const sessionGateway = new SessionGateway();
const session = await sessionGateway.getSessionFromRequest(request);
logger.info('[MIDDLEWARE] Session fetched', {
hasSession: !!session,
userId: session?.user?.userId,
role: session?.user?.role,
sessionData: session ? JSON.stringify(session, null, 2) : 'null'
});
// Convert session to state machine format
const authSession = session ? {
userId: session.user?.userId || '',
role: session.user?.role || 'driver',
roles: session.user?.role ? [session.user.role] : ['driver']
} : null;
logger.info('[MIDDLEWARE] Auth session converted', {
authSession: authSession ? JSON.stringify(authSession, null, 2) : 'null'
});
// Debug: Log route classification
const isPublic = routeMatchers.isPublic(pathname);
const requiresRole = routeMatchers.requiresRole(pathname);
logger.info('[MIDDLEWARE] Route classification', {
path: pathname,
isPublic,
requiresRole
});
// Use state machine to determine action
let result;
try {
logger.info('[MIDDLEWARE] Calling handleAuthFlow...');
result = handleAuthFlow(authSession, pathname);
logger.info('[MIDDLEWARE] handleAuthFlow result', {
result: JSON.stringify(result, null, 2)
});
} catch (error) {
logger.error('[MIDDLEWARE] Error in auth flow', error instanceof Error ? error : new Error(String(error)));
// Fallback: redirect to login if there's an error
return NextResponse.redirect(new URL(`/auth/login${SearchParamBuilder.auth(pathname)}`, request.url));
}
logger.info('[MIDDLEWARE] Decision summary', {
pathname,
hasSession: !!authSession,
role: authSession?.role,
shouldRedirect: result.shouldRedirect,
redirectUrl: result.redirectUrl
});
if (result.shouldRedirect && result.redirectUrl) {
const redirectUrl = new URL(result.redirectUrl, request.url);
logger.info('[MIDDLEWARE] REDIRECTING', {
from: pathname,
to: redirectUrl.toString()
});
const redirectResponse = NextResponse.redirect(redirectUrl);
logger.info('[MIDDLEWARE] ========== REQUEST END (REDIRECT) ==========');
return redirectResponse;
}
// Handle /sponsor root redirect (preserves cookies)
// Only reached if authenticated and allowed
if (pathname === '/sponsor') {
logger.info('[MIDDLEWARE] Redirecting /sponsor → /sponsor/dashboard');
return NextResponse.redirect(new URL('/sponsor/dashboard', request.url));
}
// All checks passed
logger.info('[MIDDLEWARE] ALLOWING ACCESS', { pathname });
logger.info('[MIDDLEWARE] ========== REQUEST END (ALLOW) ==========');
return response;
}
/**
* Configure which routes the middleware should run on
*/
export const config = {
matcher: [
/*
* Match all request paths except:
* - _next/static (static files)
* - _next/image (image optimization files)
* - _next/data (Next.js data requests)
* - favicon.ico (favicon file)
* - Files with extensions (static assets)
*/
'/((?!_next/static|_next/image|_next/data|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp|mp4|webm|mov|avi)$).*)',
],
};