import 'server-only'; import { ApiError, API_URL } from './api'; import { adminToken } from './admin-auth'; import type { Problem } from './types'; import type { AdminCoverageResponse, AdminIssuesResponse, AdminOverview, AdminRawResponse, AdminRunsResponse } from './types-explore'; /** * Admin endpoints (`X-Admin-Token`). Server components / actions only; never cached. `ADMIN_API_URL` lets * the panel target a second API instance (e.g. one started with a token while the main one has none). */ const ADMIN_BASE = `${(process.env.ADMIN_API_URL ?? API_URL).replace(/\/$/, '')}/api/v1/admin`; type Query = Record; export async function adminRequest(path: string, query?: Query, init: { method?: 'GET' | 'POST' } = {}): Promise { const token = adminToken(); if (!token) throw new ApiError(503, path, { title: 'Admin disabled', status: 503, detail: 'CA_ADMIN_TOKEN is not configured on the web server.' }); const p = new URLSearchParams(); for (const [k, v] of Object.entries(query ?? {})) if (v !== undefined && v !== null && v !== '') p.set(k, String(v)); const qs = p.toString(); const url = `${ADMIN_BASE}${path}${qs ? `?${qs}` : ''}`; let res: Response; try { res = await fetch(url, { method: init.method ?? 'GET', headers: { accept: 'application/json', 'X-Admin-Token': token }, cache: 'no-store' }); } catch (e) { throw new ApiError(0, path, null, `Admin API unreachable at ${ADMIN_BASE} (${(e as Error).message})`); } if (!res.ok) { let problem: Problem | null = null; try { const body = (await res.json()) as unknown; if (body && typeof body === 'object' && 'status' in body && 'title' in body) problem = body as Problem; } catch { /* non-JSON */ } throw new ApiError(res.status, path, problem); } return (await res.json()) as T; } export const adminApi = { overview: () => adminRequest('/overview'), runs: (q: { limit?: number; connector?: string; status?: string } = {}) => adminRequest('/runs', { limit: 200, ...q }), issues: (q: { limit?: number; severity?: string; connector?: string; indicator?: string; code?: string; run_id?: string } = {}) => adminRequest('/issues', { limit: 300, ...q }), coverage: () => adminRequest('/coverage'), raw: (runId: string) => adminRequest('/raw', { run_id: runId }), refresh: () => adminRequest<{ ok: boolean; pid: number; signal: string; sent_at: string }>('/refresh', undefined, { method: 'POST' }), clearCache: () => adminRequest<{ ok: boolean; before: Record }>('/cache/clear', undefined, { method: 'POST' }), }; /** Turn an admin fetch failure into a short message for the UI. */ export function adminErrorMessage(e: unknown): string { if (e instanceof ApiError) return `${e.status || 'network'} — ${e.problem?.detail ?? e.problem?.title ?? e.message}`; return e instanceof Error ? e.message : String(e); }