import { createServerClient } from "@supabase/ssr"; import { NextResponse, type NextRequest } from "next/server"; // Next.js 16 renamed Middleware to Proxy (same mechanism, new filename/export). // This is the app's single entry-point gate (spec // ยง2.2): unauthenticated users are sent to /login, and โ€” this is the part // that used to be missing โ€” authenticated users who are NOT an active, // explicitly-provisioned HR user are sent to /login too, with an error // message, instead of being let through. Previously this only checked for // a Supabase Auth session at all, which meant any signed-in user (even one // with no profile row, or the old "manager" role) could open the app. // This UI-layer gate is defense in depth, not the real boundary โ€” every // table is independently RLS-gated on is_hr_user() regardless of what this // proxy does. export async function proxy(request: NextRequest) { let response = NextResponse.next({ request }); const supabase = createServerClient( process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, { cookies: { getAll() { return request.cookies.getAll(); }, setAll(cookiesToSet) { cookiesToSet.forEach(({ name, value }) => request.cookies.set(name, value)); response = NextResponse.next({ request }); cookiesToSet.forEach(({ name, value, options }) => response.cookies.set(name, value, options)); }, }, } ); const { data: { user }, } = await supabase.auth.getUser(); const isLoginRoute = request.nextUrl.pathname.startsWith("/login"); if (!user) { if (isLoginRoute) return response; const url = request.nextUrl.clone(); url.pathname = "/login"; return NextResponse.redirect(url); } const { data: profile } = await supabase.from("profiles").select("role, is_active").eq("id", user.id).maybeSingle(); const isActiveHr = profile?.role === "hr" && profile?.is_active === true; if (!isActiveHr) { if (isLoginRoute) return response; const url = request.nextUrl.clone(); url.pathname = "/login"; url.searchParams.set("error", "Kein HR-Zugriff. Bitte wenden Sie sich an eine:n bestehende:n HR-Benutzer:in."); return NextResponse.redirect(url); } if (isLoginRoute) { const url = request.nextUrl.clone(); url.pathname = "/"; return NextResponse.redirect(url); } return response; } export const config = { matcher: ["/((?!_next/static|_next/image|favicon.ico).*)"], };