import type { LogEntry, RequestSummary, Stats, Payload, HotConfig, SaveConfigResult } from './types'; import { useAuthStore } from './stores/auth'; import { getActivePinia } from 'pinia'; function getAuthHeader(): Record { const token = localStorage.getItem('cursor2api_token'); return token ? { Authorization: `Bearer ${token}` } : {}; } async function apiFetch(path: string): Promise { const res = await fetch(path, { headers: getAuthHeader() }); if (res.status === 401) { const pinia = getActivePinia(); if (pinia) useAuthStore(pinia).logout(); throw new Error('HTTP 401'); } if (!res.ok) throw new Error(`HTTP ${res.status}`); return res.json() as Promise; } export function fetchLogs(params?: { requestId?: string; since?: number }): Promise { const q = new URLSearchParams(); if (params?.requestId) q.set('requestId', params.requestId); if (params?.since != null) q.set('since', String(params.since)); const qs = q.toString() ? '?' + q.toString() : ''; return apiFetch(`/api/logs${qs}`); } export function fetchRequests(limit = 50): Promise { return apiFetch(`/api/requests?limit=${limit}`); } export function fetchStats(since?: number): Promise { const qs = since !== undefined ? `?since=${since}` : ''; return apiFetch(`/api/vue/stats${qs}`); } export function fetchPayload(requestId: string): Promise { return apiFetch(`/api/payload/${requestId}`); } export async function clearLogs(): Promise { const res = await fetch('/api/logs/clear', { method: 'POST', headers: getAuthHeader(), }); if (!res.ok) throw new Error(`HTTP ${res.status}`); } export function fetchConfig(): Promise { return apiFetch('/api/config'); } export async function saveConfig(cfg: Partial): Promise { const res = await fetch('/api/config', { method: 'POST', headers: { 'Content-Type': 'application/json', ...getAuthHeader() }, body: JSON.stringify(cfg), }); if (res.status === 401) { const pinia = getActivePinia(); if (pinia) useAuthStore(pinia).logout(); throw new Error('HTTP 401'); } if (!res.ok) throw new Error(`HTTP ${res.status}`); return res.json() as Promise; } export interface RequestsPage { summaries: RequestSummary[]; hasMore: boolean; total: number; statusCounts: Record; } export interface RequestsFilter { limit?: number; before?: number; status?: string; keyword?: string; since?: number; } export function fetchMoreRequests(filter: RequestsFilter = {}): Promise { const q = new URLSearchParams({ limit: String(filter.limit ?? 50) }); if (filter.before !== undefined) q.set('before', String(filter.before)); if (filter.since !== undefined) q.set('since', String(filter.since)); if (filter.status) q.set('status', filter.status); if (filter.keyword) q.set('keyword', filter.keyword); return apiFetch(`/api/requests/more?${q.toString()}`); } export function createSSEConnection(onMessage: (event: string, data: unknown) => void): EventSource { const token = localStorage.getItem('cursor2api_token'); const url = token ? `/api/logs/stream?token=${encodeURIComponent(token)}` : '/api/logs/stream'; const es = new EventSource(url); es.onmessage = (e) => { try { onMessage('message', JSON.parse(e.data)); } catch { /* ignore */ } }; const events = ['log', 'summary', 'stats']; for (const ev of events) { es.addEventListener(ev, (e) => { try { onMessage(ev, JSON.parse((e as MessageEvent).data)); } catch { /* ignore */ } }); } return es; }