import { useAuthStore } from '@/stores/auth' import router from '@/router' const BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:8000/api/v1' export function getToken(): string | null { try { const authStore = useAuthStore() // In Pinia setup stores, refs are auto-unwrapped on store access // But be defensive: handle both unwrapped string and raw ref const token = authStore.token if (!token) return null // If token is a ref (rare case), access .value return (token as unknown as { value?: string }).value ?? token } catch { return null } } export function useApi() { async function refreshAccessToken(): Promise { try { const response = await fetch(`${BASE_URL}/auth/refresh`, { method: 'POST', credentials: 'include' // Include cookies for refresh token }) if (response.ok) { const data = await response.json() const authStore = useAuthStore() authStore.token = data.access_token localStorage.setItem('access_token', data.access_token) return data.access_token } } catch { // Refresh failed } return null } async function request(method: string, endpoint: string, body?: unknown, retryCount = 0): Promise { const headers: Record = { 'Content-Type': 'application/json', } const token = getToken() console.log(`[useApi] ${method} ${endpoint} - token: ${token ? 'present' : 'null'}`) // Only add Authorization header for non-auth endpoints if (token && !endpoint.includes('/auth/')) { headers['Authorization'] = `Bearer ${token}` console.log(`[useApi] Added Authorization header: Bearer ${token.substring(0, 10)}...`) } else { console.log(`[useApi] NO Authorization header - token=${token}, isAuthEndpoint=${endpoint.includes('/auth/')}`) } const response = await fetch(`${BASE_URL}${endpoint}`, { method, headers, body: body ? JSON.stringify(body) : undefined, credentials: 'include' }) if (response.status === 401) { // Try to refresh token once if (retryCount === 0 && !endpoint.includes('/auth/')) { const newToken = await refreshAccessToken() if (newToken) { // Retry original request with new token headers['Authorization'] = `Bearer ${newToken}` const retryResponse = await fetch(`${BASE_URL}${endpoint}`, { method, headers, credentials: 'include', body: body ? JSON.stringify(body) : undefined }) if (retryResponse.ok) { if (retryResponse.status === 204) { return {} as T } return retryResponse.json() } } } const authStore = useAuthStore() authStore.logout() router.push('/login') throw new Error('Unauthorized') } if (!response.ok) { const error = await response.json().catch(() => ({ detail: 'Request failed' })) throw new Error(error.detail || `HTTP ${response.status}`) } if (response.status === 204) { return {} as T } return response.json() } return { get: (endpoint: string) => request('GET', endpoint), // eslint-disable-next-line @typescript-eslint/no-explicit-any post: (endpoint: string, body?: any) => request('POST', endpoint, body), // eslint-disable-next-line @typescript-eslint/no-explicit-any put: (endpoint: string, body?: any) => request('PUT', endpoint, body), delete: (endpoint: string) => request('DELETE', endpoint) } }