| const BASE = import.meta.env.BASE_URL.replace(/\/$/, '');
|
| const TOKEN_KEY = 'freellmapi_dashboard_token';
|
|
|
|
|
|
|
| export function getToken(): string | null {
|
| try { return localStorage.getItem(TOKEN_KEY); } catch { return null; }
|
| }
|
| export function setToken(token: string): void {
|
| try { localStorage.setItem(TOKEN_KEY, token); } catch { }
|
| }
|
| export function clearToken(): void {
|
| try { localStorage.removeItem(TOKEN_KEY); } catch { }
|
| }
|
|
|
| export const UNAUTHORIZED_EVENT = 'freellmapi:unauthorized';
|
|
|
| export async function apiFetch<T>(path: string, options?: RequestInit): Promise<T> {
|
| const token = getToken();
|
| const res = await fetch(`${BASE}${path}`, {
|
| headers: {
|
| 'Content-Type': 'application/json',
|
| ...(token ? { Authorization: `Bearer ${token}` } : {}),
|
| ...options?.headers,
|
| },
|
| ...options,
|
| });
|
| if (res.status === 401) {
|
|
|
| clearToken();
|
| window.dispatchEvent(new CustomEvent(UNAUTHORIZED_EVENT));
|
| }
|
| if (!res.ok) {
|
| const body = await res.json().catch(() => ({ error: { message: res.statusText } }));
|
| throw new Error(body.error?.message ?? `HTTP ${res.status}`);
|
| }
|
| return res.json();
|
| }
|
|
|
| export async function logout(): Promise<void> {
|
| try { await apiFetch('/api/auth/logout', { method: 'POST' }); } catch { }
|
| clearToken();
|
| window.dispatchEvent(new CustomEvent(UNAUTHORIZED_EVENT));
|
| }
|
|
|