import { NextRequest, NextResponse } from 'next/server' import { jwtVerify } from 'jose' const PUBLIC_PATHS = [ '/auth', '/api/auth', '/api/health', '/api/admin', '/_next', '/manifest.json', '/sw.js', '/icons', '/favicon.ico', ] function isPublicPath(pathname: string): boolean { return PUBLIC_PATHS.some(p => pathname === p || pathname.startsWith(p + '/')) } export async function middleware(request: NextRequest) { const { pathname } = request.nextUrl // Allow public paths if (isPublicPath(pathname)) { return NextResponse.next() } // Check for session cookie const token = request.cookies.get('pitch_session')?.value if (!token) { return NextResponse.redirect(new URL('/auth', request.url)) } // Verify JWT const secret = process.env.PITCH_JWT_SECRET if (!secret) { return NextResponse.redirect(new URL('/auth', request.url)) } try { const { payload } = await jwtVerify(token, new TextEncoder().encode(secret)) // Add investor info to headers for downstream use const response = NextResponse.next() response.headers.set('x-investor-id', payload.sub as string) response.headers.set('x-investor-email', payload.email as string) response.headers.set('x-session-id', payload.sessionId as string) // Auto-refresh JWT if within last 15 minutes of expiry const exp = payload.exp as number const now = Math.floor(Date.now() / 1000) const timeLeft = exp - now if (timeLeft < 900 && timeLeft > 0) { // Import dynamically to avoid Edge runtime issues with pg // The actual refresh happens server-side in the API routes response.headers.set('x-token-refresh-needed', 'true') } return response } catch { // Invalid or expired JWT const response = NextResponse.redirect(new URL('/auth', request.url)) response.cookies.delete('pitch_session') return response } } export const config = { matcher: [ /* * Match all request paths except: * - _next/static (static files) * - _next/image (image optimization files) * - favicon.ico (favicon file) */ '/((?!_next/static|_next/image).*)', ], }