export const API_BASE = '/api'; type ApiResponse = { success: boolean; data?: T; error?: { code: string; message: string }; }; let accessToken: string | null = localStorage.getItem('accessToken'); let refreshToken: string | null = localStorage.getItem('refreshToken'); export function setTokens(access: string, refresh: string) { accessToken = access; refreshToken = refresh; localStorage.setItem('accessToken', access); localStorage.setItem('refreshToken', refresh); } export function clearTokens() { accessToken = null; refreshToken = null; localStorage.removeItem('accessToken'); localStorage.removeItem('refreshToken'); } export function getAccessToken() { return accessToken; } async function refreshAccessToken(): Promise { if (!refreshToken) return false; try { const res = await fetch(`${API_BASE}/auth/refresh`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ refreshToken }), }); if (!res.ok) return false; const json: ApiResponse<{ accessToken: string; refreshToken: string }> = await res.json(); if (json.success && json.data) { setTokens(json.data.accessToken, json.data.refreshToken); return true; } return false; } catch { return false; } } export async function apiFetch(path: string, options: RequestInit = {}): Promise { const headers = new Headers(options.headers); if (!headers.has('Content-Type') && !(options.body instanceof FormData)) { headers.set('Content-Type', 'application/json'); } if (accessToken) { headers.set('Authorization', `Bearer ${accessToken}`); } let res = await fetch(`${API_BASE}${path}`, { ...options, headers }); if (res.status === 401 && refreshToken) { const refreshed = await refreshAccessToken(); if (refreshed) { headers.set('Authorization', `Bearer ${accessToken}`); res = await fetch(`${API_BASE}${path}`, { ...options, headers }); } } const text = await res.text(); let json: ApiResponse; try { json = JSON.parse(text); } catch { throw new Error(`Server error (${res.status})`); } if (!json.success) { throw new Error(json.error?.message || 'Request failed'); } return json.data as T; }