import { NextResponse } from 'next/server' import type { NextRequest } from 'next/server' import createIntlMiddleware from 'next-intl/middleware' import { locales } from './i18n' // Internationalization configuration const intlMiddleware = createIntlMiddleware({ locales: [...locales], defaultLocale: 'en', localePrefix: 'always' }) // Note: Avoid using Prisma or any Node-only APIs in Middleware. // Middleware runs on the Edge runtime, where Prisma is not supported. // If rate limiting is needed, implement it inside API route handlers // (Node.js runtime) or via an external service (e.g., Upstash Redis). export async function middleware(request: NextRequest) { // Skip admin routes from internationalization if (request.nextUrl.pathname.startsWith('/admin')) { return NextResponse.next() } // Handle internationalization for non-API routes if (!request.nextUrl.pathname.startsWith('/api')) { return intlMiddleware(request) } // Skip API rate limiting here to stay Edge-safe // Security headers for all responses const response = NextResponse.next() // Security headers response.headers.set('X-Content-Type-Options', 'nosniff') response.headers.set('X-Frame-Options', 'DENY') response.headers.set('X-XSS-Protection', '1; mode=block') response.headers.set('Referrer-Policy', 'origin-when-cross-origin') response.headers.set('Permissions-Policy', 'camera=(), microphone=(), geolocation=()') // CSRF protection for state-changing operations if (['POST', 'PUT', 'DELETE', 'PATCH'].includes(request.method)) { const origin = request.headers.get('origin') const host = request.headers.get('host') if (origin && host && !origin.endsWith(host)) { return new NextResponse('Forbidden', { status: 403 }) } } // Authentication: perform only lightweight checks in Middleware (Edge). // Defer full JWT verification to API route handlers (Node runtime). const protectedPaths = ['/dashboard', '/profile', '/settings'] const isProtectedPath = protectedPaths.some(path => request.nextUrl.pathname.startsWith(path) ) if (isProtectedPath) { const token = request.cookies.get('authToken')?.value || request.headers.get('authorization')?.replace('Bearer ', '') if (!token) { // Extract locale from pathname for redirect const locale = request.nextUrl.pathname.split('/')[1] const isValidLocale = ['ro', 'en'].includes(locale) const redirectLocale = isValidLocale ? locale : 'en' return NextResponse.redirect(new URL(`/${redirectLocale}/auth/login`, request.url)) } } return response } export const config = { matcher: [ // Match all pathnames except for // - api routes // - admin routes // - _next (Next.js internals) // - _vercel // - static files (images, etc.) // - favicon.ico, robots.txt, sitemap.xml '/((?!api|admin|_next|_vercel|.*\\..*|favicon.ico|robots.txt|sitemap.xml).*)', // Match internationalized pathnames '/(ro|en)/:path*' ], }