import createIntlMiddleware from "next-intl/middleware"; import { auth } from "@/lib/auth"; import { NextResponse } from "next/server"; import type { NextRequest } from "next/server"; import { routing } from "@/i18n/routing"; const intlMiddleware = createIntlMiddleware(routing); const publicPaths = ["/login", "/register", "/api/auth", "/api/register"]; function isPublicPath(pathname: string): boolean { // Strip locale prefix for comparison const stripped = pathname.replace(/^\/(de|fr|it|en)/, "") || "/"; return ( publicPaths.some((p) => stripped === p || stripped.startsWith(`${p}/`)) || pathname.startsWith("/api/auth") || pathname.startsWith("/api/register") ); } export default async function middleware(request: NextRequest) { const { pathname } = request.nextUrl; // NextAuth API routes and register API pass through directly if (pathname.startsWith("/api/auth") || pathname.startsWith("/api/register")) { return NextResponse.next(); } // Auth guard for protected paths if (!isPublicPath(pathname)) { const session = await auth(); if (!session) { const loginUrl = new URL("/login", request.url); loginUrl.searchParams.set("callbackUrl", pathname); return NextResponse.redirect(loginUrl); } } return intlMiddleware(request); } export const config = { // Excludes _next/* internal routes, the favicon, api routes, AND any // path containing a dot (covers all static files served from public/, // e.g. /threema/qr_code_AIAGENT.png). Without the dot exclusion, the // i18n middleware prepends the locale ("/en/threema/qr_code_AIAGENT.png") // and the file is not found. matcher: ["/((?!_next|favicon.ico|api|.*\\..*).*)"], };