/** * Middleware * * Composes: * 1. next-intl locale detection & prefix enforcement (/fr/..., /en/...) * 2. Auth protection — redirects unauthenticated users to /{locale}/login * * All URLs are locale-prefixed (localePrefix: 'always' in routing.ts). */ import createMiddleware from 'next-intl/middleware'; import { NextResponse } from 'next/server'; import type { NextRequest } from 'next/server'; import { routing } from './i18n/routing'; const intlMiddleware = createMiddleware(routing); // Paths that do not require authentication (matched AFTER locale stripping) const exactPublicPaths = ['/']; const prefixPublicPaths = [ '/login', '/register', '/forgot-password', '/reset-password', '/verify-email', '/about', '/careers', '/blog', '/press', '/contact', '/carrier', '/pricing', '/docs', ]; function stripLocale(pathname: string): { locale: string | null; pathWithoutLocale: string } { for (const locale of routing.locales) { if (pathname === `/${locale}`) return { locale, pathWithoutLocale: '/' }; if (pathname.startsWith(`/${locale}/`)) { return { locale, pathWithoutLocale: pathname.slice(`/${locale}`.length) }; } } return { locale: null, pathWithoutLocale: pathname }; } export default function middleware(request: NextRequest) { const { pathname } = request.nextUrl; // Step 1: let next-intl handle locale redirection (e.g. `/dashboard` → `/fr/dashboard`) const intlResponse = intlMiddleware(request); // If next-intl already issued a redirect, honor it (user will land on the // locale-prefixed URL and the middleware will run again with auth intact). if (intlResponse.status >= 300 && intlResponse.status < 400) { return intlResponse; } // Step 2: auth protection const { locale, pathWithoutLocale } = stripLocale(pathname); const resolvedLocale = locale || routing.defaultLocale; const isPublicPath = exactPublicPaths.includes(pathWithoutLocale) || prefixPublicPaths.some( p => pathWithoutLocale === p || pathWithoutLocale.startsWith(p + '/') ); const token = request.cookies.get('accessToken')?.value; if (!isPublicPath && !token) { const loginUrl = new URL(`/${resolvedLocale}/login`, request.url); loginUrl.searchParams.set('redirect', pathname); return NextResponse.redirect(loginUrl); } return intlResponse; } export const config = { // Exclude Next.js internals, API routes, and static assets matcher: [ '/((?!_next/static|_next/image|api|assets|favicon\\.ico|manifest\\.json|.*\\.(?:png|jpg|jpeg|gif|webp|svg|ico|mp4|mp3|pdf|txt|xml|csv|json)$).*)', ], };