39 lines
1.7 KiB
TypeScript
39 lines
1.7 KiB
TypeScript
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'
|
|
|
|
type AdminContext = NonNullable<Awaited<ReturnType<typeof getApiAdminContext>>>
|
|
type RouteContext = { params?: Promise<Record<string, string | string[]>> }
|
|
type AdminHandler = (
|
|
request: NextRequest,
|
|
context: AdminContext,
|
|
routeContext: RouteContext,
|
|
) => Promise<Response> | Response
|
|
|
|
export function withAdmin(options: { permission?: string }, handler: AdminHandler) {
|
|
return async (request: NextRequest, routeContext: RouteContext = {}) => {
|
|
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 })
|
|
}
|
|
}
|
|
}
|