const API_BASE = '/api'; function getToken(): string | null { return sessionStorage.getItem('sub-router-token'); } export function setToken(token: string) { sessionStorage.setItem('sub-router-token', token); } export function clearToken() { sessionStorage.removeItem('sub-router-token'); } async function request(path: string, options: RequestInit = {}): Promise { const token = getToken(); const headers: Record = { 'Content-Type': 'application/json', ...(options.headers as Record || {}), }; if (token) { headers['Authorization'] = `Bearer ${token}`; } const res = await fetch(`${API_BASE}${path}`, { ...options, headers }); if (res.status === 401) { clearToken(); window.location.reload(); throw new Error('Unauthorized'); } if (!res.ok) { const body = await res.json().catch(() => ({ error: res.statusText })); throw new Error(body.error || res.statusText); } return res.json(); } // Auth export const auth = { status: () => request<{ hasPassword: boolean }>('/auth/status'), login: (password: string) => request<{ ok: boolean }>('/auth/login', { method: 'POST', body: JSON.stringify({ password }), }), }; // Subscriptions export const subscriptions = { list: () => request('/subscriptions'), create: (name: string, url: string, urlClash?: string, urlSsr?: string) => request<{ id: number }>('/subscriptions', { method: 'POST', body: JSON.stringify({ name, url, url_clash: urlClash || null, url_ssr: urlSsr || null }), }), update: (id: number, data: any) => request<{ ok: boolean }>(`/subscriptions/${id}`, { method: 'PUT', body: JSON.stringify(data), }), delete: (id: number) => request<{ ok: boolean }>(`/subscriptions/${id}`, { method: 'DELETE', }), fetch: (id: number) => request<{ nodeCount: number }>(`/subscriptions/${id}/fetch`, { method: 'POST', }), nodes: (id: number) => request(`/subscriptions/${id}/nodes`), }; // Nodes export const nodes = { fetchedToggle: (id: number, enabled: boolean) => request<{ ok: boolean }>(`/nodes/fetched/${id}`, { method: 'PUT', body: JSON.stringify({ enabled }), }), fetchedBatch: (ids: number[], enabled: boolean) => request<{ ok: boolean }>('/nodes/fetched/batch', { method: 'PUT', body: JSON.stringify({ ids, enabled }), }), staticList: () => request('/nodes/static'), staticCreate: (uri: string, name?: string) => request('/nodes/static', { method: 'POST', body: JSON.stringify({ uri, name }), }), staticUpdate: (id: number, data: any) => request<{ ok: boolean }>(`/nodes/static/${id}`, { method: 'PUT', body: JSON.stringify(data), }), staticDelete: (id: number) => request<{ ok: boolean }>(`/nodes/static/${id}`, { method: 'DELETE', }), }; // Rules export const rules = { list: () => request('/rules'), create: (data: any) => request<{ id: number }>('/rules', { method: 'POST', body: JSON.stringify(data), }), update: (id: number, data: any) => request<{ ok: boolean }>(`/rules/${id}`, { method: 'PUT', body: JSON.stringify(data), }), delete: (id: number) => request<{ ok: boolean }>(`/rules/${id}`, { method: 'DELETE', }), reorder: (ids: number[]) => request<{ ok: boolean }>('/rules/reorder', { method: 'PUT', body: JSON.stringify({ ids }), }), }; // Config export const config = { preview: () => request<{ config: string }>('/config/preview'), getSurgeToken: () => request<{ token: string }>('/config/surge-token'), regenerateSurgeToken: () => request<{ token: string }>('/config/surge-token', { method: 'POST' }), }; // Stats export const stats = { get: () => request('/stats'), };