import axios from 'axios' import { useAuthStore } from '../store/auth' export const api = axios.create({ baseURL: '/', paramsSerializer: (params) => { const sp = new URLSearchParams() for (const [key, val] of Object.entries(params)) { if (val === undefined || val === null) continue if (Array.isArray(val)) { val.forEach((v) => sp.append(key, String(v))) } else { sp.append(key, String(val)) } } return sp.toString() }, }) // Inject access token on every request api.interceptors.request.use((config) => { const token = useAuthStore.getState().accessToken if (token) config.headers.Authorization = `Bearer ${token}` return config }) // On 401 — try refresh once, then logout let refreshing: Promise | null = null api.interceptors.response.use( (r) => r, async (error) => { const original = error.config if (error.response?.status !== 401 || original._retried) { return Promise.reject(error) } original._retried = true const { refreshToken, setTokens, clearTokens, username } = useAuthStore.getState() if (!refreshToken) { clearTokens() return Promise.reject(error) } if (!refreshing) { refreshing = axios .post('/api/v1/auth/refresh', { refresh_token: refreshToken }) .then((r) => { setTokens(r.data.access_token, r.data.refresh_token, username ?? '') return r.data.access_token }) .catch(() => { clearTokens() throw new Error('Session expired') }) .finally(() => { refreshing = null }) } const newToken = await refreshing original.headers.Authorization = `Bearer ${newToken}` return api(original) } )