import axios from 'axios' const api = axios.create({ baseURL: import.meta.env.VITE_API_URL || '/api', timeout: 15000, headers: { 'Content-Type': 'application/json' }, withCredentials: true, }) // ─── Request interceptor: attach JWT ───────────────────────────────────────── api.interceptors.request.use(config => { const token = localStorage.getItem('access_token') if (token) config.headers.Authorization = `Bearer ${token}` return config }, error => Promise.reject(error)) // ─── Response interceptor: auto-refresh token ──────────────────────────────── let isRefreshing = false let failedQueue = [] const processQueue = (error, token = null) => { failedQueue.forEach(prom => error ? prom.reject(error) : prom.resolve(token)) failedQueue = [] } api.interceptors.response.use( response => response, async error => { const originalRequest = error.config if (error.response?.status === 401 && !originalRequest._retry) { if (isRefreshing) { return new Promise((resolve, reject) => { failedQueue.push({ resolve, reject }) }).then(token => { originalRequest.headers.Authorization = `Bearer ${token}` return api(originalRequest) }) } originalRequest._retry = true isRefreshing = true try { const refresh = localStorage.getItem('refresh_token') if (!refresh) throw new Error('No refresh token') const { data } = await axios.post('/api/auth/token/refresh/', { refresh }) localStorage.setItem('access_token', data.access) processQueue(null, data.access) originalRequest.headers.Authorization = `Bearer ${data.access}` return api(originalRequest) } catch (err) { processQueue(err, null) localStorage.removeItem('access_token') localStorage.removeItem('refresh_token') window.location.href = '/login' return Promise.reject(err) } finally { isRefreshing = false } } return Promise.reject(error) } ) export default api