const TOKEN_KEY = "sac_token"; export function getToken(): string | null { return localStorage.getItem(TOKEN_KEY); } export function setToken(token: string): void { localStorage.setItem(TOKEN_KEY, token); } export function clearToken(): void { localStorage.removeItem(TOKEN_KEY); } export async function apiFetch(path: string, init: RequestInit = {}): Promise { const headers = new Headers(init.headers); if (!headers.has("Content-Type") && init.body) { headers.set("Content-Type", "application/json"); } const token = getToken(); if (token) { headers.set("Authorization", `Bearer ${token}`); } const res = await fetch(path, { ...init, headers }); if (res.status === 401) { clearToken(); window.location.href = "/login"; throw new Error("Unauthorized"); } if (!res.ok) { const text = await res.text(); throw new Error(text || res.statusText); } return res.json() as Promise; } export interface TokenResponse { access_token: string; token_type: string; } export interface EventSummary { id: number; event_id: string; host_id: number; hostname: string; occurred_at: string; received_at: string; category: string; type: string; severity: string; title: string; summary: string; } export interface EventListResponse { items: EventSummary[]; total: number; page: number; page_size: number; } export interface EventDetail extends EventSummary { details: Record | null; raw: Record | null; payload: Record; } export interface HostSummary { id: number; hostname: string; display_name: string | null; os_family: string; product: string; product_version: string | null; ipv4: string | null; last_seen_at: string; event_count: number | null; last_heartbeat_at: string | null; last_daily_report_at: string | null; agent_status: "online" | "stale" | "unknown"; } export interface HostListResponse { items: HostSummary[]; total: number; page: number; page_size: number; } export interface ProblemSummary { id: number; host_id: number | null; hostname: string | null; title: string; summary: string; severity: string; status: string; rule_id: string | null; fingerprint?: string; event_count?: number; last_seen_at?: string; created_at: string; updated_at: string; } export interface ProblemListResponse { items: ProblemSummary[]; total: number; page: number; page_size: number; } export interface ProblemEventItem { id: number; event_id: string; occurred_at: string; type: string; severity: string; title: string; summary: string; } export interface ProblemDetail extends ProblemSummary { events: ProblemEventItem[]; } export function fetchProblem(id: number): Promise { return apiFetch(`/api/v1/problems/${id}`); } export async function ackProblem(problemId: number): Promise<{ id: number; status: string }> { return apiFetch(`/api/v1/problems/${problemId}/ack`, { method: "POST" }); } export async function resolveProblem(problemId: number): Promise<{ id: number; status: string }> { return apiFetch(`/api/v1/problems/${problemId}/resolve`, { method: "POST" }); } export interface DashboardSummary { events_last_24h: number; hosts_total: number; hosts_stale: number; heartbeats_24h: number; daily_reports_24h: number; problems_open: number; severity_24h: Record; recent_events: EventSummary[]; }