import { createContext, useContext, useMemo, useState, useCallback, type ReactNode } from 'react'; import { createEffigenixClient, type EffigenixClient, type TokenProvider } from '@effigenix/api-client'; import { fetch as tauriFetch } from '@tauri-apps/plugin-http'; const API_BASE = import.meta.env.VITE_API_URL || 'http://localhost:8080'; /** * Axios adapter that delegates to Tauri's HTTP plugin. * Bypasses CORS restrictions on mobile webviews. */ // eslint-disable-next-line @typescript-eslint/no-explicit-any const tauriAdapter = async (config: any) => { const url = new URL(config.url || '', config.baseURL); const headers: Record = {}; if (config.headers) { const raw = typeof config.headers.toJSON === 'function' ? config.headers.toJSON(false) : config.headers; for (const [key, value] of Object.entries(raw)) { if (value != null) headers[key] = String(value); } } const response = await tauriFetch(url.toString(), { method: (config.method || 'GET').toUpperCase(), headers, body: config.data != null ? (typeof config.data === 'string' ? config.data : JSON.stringify(config.data)) : undefined, }); let data: unknown; const contentType = response.headers.get('content-type') || ''; if (contentType.includes('application/json')) { data = await response.json(); } else { data = await response.text(); } return { data, status: response.status, statusText: response.statusText, headers: Object.fromEntries(response.headers.entries()), config, }; }; interface ApiState { client: EffigenixClient; login: (username: string, password: string) => Promise; isAuthenticated: boolean; } const ApiContext = createContext(null); export function ApiProvider({ children }: { children: ReactNode }) { const [tokens, setTokens] = useState<{ access: string; refresh: string } | null>(null); const tokenProvider = useMemo(() => ({ getAccessToken: async () => tokens?.access ?? null, getRefreshToken: async () => tokens?.refresh ?? null, saveTokens: async (accessToken, refreshToken) => { setTokens({ access: accessToken, refresh: refreshToken }); }, clearTokens: async () => setTokens(null), }), [tokens]); const client = useMemo(() => createEffigenixClient(tokenProvider, { baseUrl: API_BASE, adapter: tauriAdapter, }), [tokenProvider]); const login = useCallback(async (username: string, password: string) => { const res = await tauriFetch(`${API_BASE}/api/auth/login`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, password }), }); if (!res.ok) { const text = await res.text(); throw new Error(`${res.status}: ${text}`); } const data = await res.json(); setTokens({ access: data.accessToken, refresh: data.refreshToken }); }, []); return ( {children} ); } export function useApi() { const ctx = useContext(ApiContext); if (!ctx) throw new Error('useApi must be used within ApiProvider'); return ctx; }