import { clerkMiddleware, createRouteMatcher } from "@clerk/nextjs/server"; import { NextResponse } from "next/server"; const isPublicRoute = createRouteMatcher([ "/sign-up(.*)", "/log-in(.*)", "/forgot-password(.*)", "/reset-password(.*)", "/verify-email(.*)", "/health-card/(.*)", "/impressum", "/datenschutz", "/nutzungsbedingungen", "/join(.*)", "/api/webhooks/(.*)", "/api/auth/(.*)", ]); export default clerkMiddleware(async (auth, request) => { const { pathname } = request.nextUrl; // Invite gate: block /sign-up unless the visitor has a valid invite cookie. // Set INVITE_REQUIRED=false in env to disable for the live/post-beta launch. if (pathname.startsWith("/sign-up")) { const inviteRequired = process.env.INVITE_REQUIRED !== "false"; if (inviteRequired && !request.cookies.has("pf_invite")) { return NextResponse.redirect(new URL("/join", request.url)); } } // Admin panel: validate secret path segment before anything else. // Returns 404 (not 403) — the panel's existence must not be revealed. if (pathname.startsWith("/p/")) { const segments = pathname.split("/"); const providedSecret = segments[2] ?? ""; const adminSecret = process.env.ADMIN_SECRET ?? ""; if (!adminSecret || providedSecret !== adminSecret) { return new NextResponse(null, { status: 404 }); } } if (!isPublicRoute(request)) { await auth.protect(); } }); export const config = { matcher: [ "/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)", "/(api|trpc)(.*)", "/__clerk/(.*)", ], };