import { NextRequest, NextResponse } from 'next/server'; import { requireAdminAuth, getClientIP } from '@/lib/admin/session'; import { auditLog } from '@/lib/admin/audit'; import { logger } from '@/lib/logger'; import { getEntitlementState, setSeatTotal, revokeSeat, readMeteringLedger } from '@/lib/ai/entitlement'; export const runtime = 'nodejs'; /** * Admin-only data endpoints for the `server` AI class's real entitlement * enforcement (lib/ai/entitlement.ts). This is the data plumbing only — the * visual admin console (docs/AI-ASSISTANT-CONCEPT.md §6) is a separate, * not-yet-built UI on top of these same endpoints. */ export async function GET(request: NextRequest) { const result = await requireAdminAuth(request); if ('error' in result) return result.error; try { const [state, ledger] = await Promise.all([getEntitlementState(), readMeteringLedger()]); return NextResponse.json({ ...state, recentUsage: ledger }, { headers: { 'Cache-Control': 'no-store' } }); } catch (error) { logger.error('ai entitlement read error', { error: error instanceof Error ? error.message : String(error) }); return NextResponse.json({ error: 'Internal server error' }, { status: 500 }); } } export async function PUT(request: NextRequest) { const result = await requireAdminAuth(request); if ('error' in result) return result.error; const ip = getClientIP(request); let body: { seatsTotal?: unknown; revokeUsername?: unknown }; try { body = await request.json(); } catch { return NextResponse.json({ error: 'invalid JSON body' }, { status: 400 }); } try { if (typeof body.seatsTotal === 'number') { const state = await setSeatTotal(body.seatsTotal); await auditLog('ai.entitlement.seats_total', { seatsTotal: state.seatsTotal }, ip); return NextResponse.json(state); } if (typeof body.revokeUsername === 'string' && body.revokeUsername) { const state = await revokeSeat(body.revokeUsername); await auditLog('ai.entitlement.revoke_seat', { username: body.revokeUsername }, ip); return NextResponse.json(state); } return NextResponse.json({ error: 'seatsTotal or revokeUsername is required' }, { status: 400 }); } catch (error) { logger.error('ai entitlement update error', { error: error instanceof Error ? error.message : String(error) }); return NextResponse.json({ error: 'Internal server error' }, { status: 500 }); } }