import { useAuthStore } from '@/stores/authStore.js' import { BASE_URL } from '@/main.js' let isRefreshing = false let refreshPromise = null function createConfig(options = {}) { const authStore = useAuthStore() const headers = { 'Content-Type': 'application/json', ...options.headers, } if (authStore.accessToken) { headers['Authorization'] = `Bearer ${authStore.accessToken}` } return { headers, credentials: 'include', ...options, } } async function refreshAccessToken() { const authStore = useAuthStore() if (isRefreshing) return refreshPromise isRefreshing = true refreshPromise = fetch(`${BASE_URL}/user/auth/refresh`, { method: 'POST', credentials: 'include', }) .then(async (res) => { if (!res.ok) throw new Error('Failed to refresh access token') return await res.json() }) .then((data) => { const token = data.access_token if (!token) { throw new Error('No access_token in refresh response') } authStore.setToken(data.access_token) return data }) .catch((error) => { throw error }) .finally(() => { isRefreshing = false refreshPromise = null }) return refreshPromise } async function request(url, options = {}, isRetry = false) { const config = createConfig(options) const response = await fetch(`${BASE_URL}${url}`, config) if (response.status === 401 && !isRetry) { try { const data = await refreshAccessToken() const token = data.access_token if (!token) { throw new Error('Refresh response did not contain access_token') } const newOptions = { ...options, headers: { ...options.headers, 'Authorization': `Bearer ${token}`, }, } return await request(url, newOptions, true) } catch (e) { const authStore = useAuthStore() authStore.forceLogout() throw e } } if (!response.ok) { let errorData try { errorData = await response.json() } catch { errorData = {} } throw new Error(errorData.message || `HTTP Error: ${response.status}`) } let data = null const contentType = response.headers.get('content-type') if (contentType?.includes('application/json')) { try { data = await response.json() } catch (e) { console.warn('Failed to parse JSON response', e) } } return { status: response.status, ok: response.ok, data, } } export const apiClient = { get: (url, params = {}) => { const queryString = new URLSearchParams(params).toString(); const fullUrl = queryString ? `${url}?${queryString}` : url; return request(fullUrl, { method: 'GET' }); }, post: (url, data) => { const isFormData = data instanceof FormData return request(url, { method: 'POST', body: isFormData ? data : JSON.stringify(data), headers: isFormData ? {} : { 'Content-Type': 'application/json' } }) }, put: (url, data) => request(url, { method: 'PUT', body: JSON.stringify(data), }), delete: (url) => request(url, { method: 'DELETE' }), }