export interface ChannelStatus { id: string; name: string; isActive: boolean; lastCheckedAt: string | null; recording: boolean; activeSessionId: string | null; lastPollError: string | null; } export const API_DAEMON_URL = process.env.API_DAEMON_URL ?? "http://localhost:4001"; /** POSTs to a state-changing api-daemon endpoint with the shared internal auth header (see INTERNAL_API_TOKEN in .env.example). */ export async function postToApiDaemon(path: string, body?: unknown): Promise { const res = await fetch(`${API_DAEMON_URL}${path}`, { method: "POST", headers: { "content-type": "application/json", ...(process.env.INTERNAL_API_TOKEN ? { authorization: `Bearer ${process.env.INTERNAL_API_TOKEN}` } : {}), }, body: body ? JSON.stringify(body) : undefined, cache: "no-store", }); if (!res.ok) { const text = await res.text().catch(() => ""); throw new Error(`api-daemon ${path} failed (${res.status}): ${text.slice(0, 300)}`); } return res.json() as Promise; } /** * lastPollError only exists in api-daemon's in-memory map (not persisted to * the DB), so the panel fetches it live over HTTP rather than via Prisma. * Failures here (api-daemon down, network hiccup) shouldn't break the page — * they just mean live-status fields fall back to unknown. */ export async function fetchChannelStatuses(): Promise> { try { const res = await fetch(`${API_DAEMON_URL}/status`, { cache: "no-store" }); if (!res.ok) return new Map(); const data = (await res.json()) as { channels: ChannelStatus[] }; return new Map(data.channels.map((c) => [c.id, c])); } catch { return new Map(); } }