'use client'; /** Tiny client for /api/admin/* — token lives in sessionStorage only and travels as X-IP-Admin-Token. */ const KEY = 'ip.admin-token'; export function getAdminToken(): string { try { return window.sessionStorage.getItem(KEY) ?? ''; } catch { return ''; } } export function setAdminToken(token: string) { try { if (token) window.sessionStorage.setItem(KEY, token); else window.sessionStorage.removeItem(KEY); } catch { /* ignore */ } } export class AdminError extends Error { constructor( public status: number, public body: unknown, ) { super(status === 401 ? 'Unauthorized' : `Admin API error ${status}`); } } export async function adminFetch(path: string, init?: { method?: string; body?: unknown; params?: Record }): Promise { const qs = init?.params ? '?' + Object.entries(init.params) .filter(([, v]) => v !== undefined && v !== '') .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`) .join('&') : ''; const res = await fetch(`/api/admin${path}${qs}`, { method: init?.method ?? 'GET', headers: { 'X-IP-Admin-Token': getAdminToken(), ...(init?.body !== undefined ? { 'Content-Type': 'application/json' } : {}) }, body: init?.body !== undefined ? JSON.stringify(init.body) : undefined, cache: 'no-store', }); if (!res.ok) { let body: unknown = null; try { body = await res.json(); } catch { /* no body */ } throw new AdminError(res.status, body); } return (await res.json()) as T; }