Files
pn-new-crm/src/middleware.ts

65 lines
1.7 KiB
TypeScript
Raw Normal View History

import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
/**
* Paths that do not require an authenticated session.
* Checked with startsWith, so /auth/ covers /auth/callback etc.
*/
const PUBLIC_PATHS: string[] = [
'/login',
'/auth/',
'/api/auth/',
'/api/public/',
'/api/health',
'/api/webhooks/',
'/scan',
'/portal/',
'/api/portal/',
];
function isPublicPath(pathname: string): boolean {
return PUBLIC_PATHS.some((prefix) => pathname === prefix || pathname.startsWith(prefix));
}
function isApiRoute(pathname: string): boolean {
return pathname.startsWith('/api/');
}
export function middleware(request: NextRequest): NextResponse {
const { pathname } = request.nextUrl;
// Always allow public paths through
if (isPublicPath(pathname)) {
return NextResponse.next();
}
const sessionToken = request.cookies.get('pn-crm.session_token');
if (!sessionToken?.value) {
if (isApiRoute(pathname)) {
// API routes return 401 JSON — never redirect
return NextResponse.json({ error: 'Authentication required' }, { status: 401 });
}
// Page routes redirect to /login, preserving the intended destination
const loginUrl = new URL('/login', request.url);
loginUrl.searchParams.set('redirect', pathname + request.nextUrl.search);
return NextResponse.redirect(loginUrl);
}
return NextResponse.next();
}
export const config = {
matcher: [
/*
* Match all request paths except:
* - _next/static (static files)
* - _next/image (Next.js image optimisation)
* - favicon.ico
* - /images/ (public image assets)
*/
'/((?!_next/static|_next/image|favicon\\.ico|images/).*)',
],
};