55 lines
1.6 KiB
TypeScript
55 lines
1.6 KiB
TypeScript
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/(.*)",
|
|
],
|
|
};
|