import type { NextRequest } from "next/server"; import { NextResponse } from "next/server"; import { logAuthorizationEvent } from "@/lib/admin/authorization-events"; import { canAccess, getApiAdminContext } from "@/lib/permissions"; import { logServerError } from "@/lib/server-log"; import { validateCsrfToken } from "@/lib/foundation/security"; const MUTATING_METHODS = new Set(["POST", "PUT", "PATCH", "DELETE"]); type AdminContext = NonNullable>>; type RouteContext = { params?: Promise> }; type AdminHandler = ( request: NextRequest, context: AdminContext, routeContext: RouteContext, ) => Promise | Response; export function withAdmin(options: { permission?: string; requireCsrf?: boolean }, handler: AdminHandler) { return async (request: NextRequest, routeContext: RouteContext = {}) => { if (options.requireCsrf === true && MUTATING_METHODS.has(request.method)) { const csrfToken = request.headers.get("x-csrf-token") ?? request.headers.get("csrf-token") ?? ""; const valid = await validateCsrfToken(csrfToken); if (!valid) { return NextResponse.json({ ok: false, error: "Invalid or missing CSRF token" }, { status: 403 }); } } const context = await getApiAdminContext(); if (!context) return NextResponse.json({ ok: false, error: "Unauthorized" }, { status: 401 }); if ( options.permission && !canAccess(context.permissions, options.permission, context.session.user.rank) ) { await logAuthorizationEvent({ kind: "permission.denied", userId: context.session.user.id, username: context.session.user.username, rank: context.session.user.rank, permission: options.permission, source: request.nextUrl.pathname, reason: "API permission check denied", }); return NextResponse.json({ ok: false, error: "Forbidden" }, { status: 403 }); } try { return await handler(request, context, routeContext); } catch (error) { logServerError("admin.api_failed", error, { path: request.nextUrl.pathname, userId: context.session.user.id, }); return NextResponse.json({ ok: false, error: "Internal server error" }, { status: 500 }); } }; }