import axios, { AxiosError, type InternalAxiosRequestConfig } from 'axios' import router from '../router' type RetryableConfig = InternalAxiosRequestConfig & { _retry?: boolean } let isRefreshing = false let pendingQueue: Array<(token: string | null) => void> = [] function processQueue(token: string | null) { pendingQueue.forEach((cb) => cb(token)) pendingQueue = [] } const client = axios.create({ baseURL: '/api/v1', timeout: 10000, withCredentials: true, }) client.interceptors.request.use((config) => { const token = localStorage.getItem('asset_tracker_token') if (token) { config.headers = config.headers || {} config.headers.Authorization = `Bearer ${token}` } return config }) async function refreshAccessToken(): Promise { const resp = await axios.post('/api/v1/auth/refresh', {}, { withCredentials: true, timeout: 10000 }) const token = resp?.data?.access_token || '' if (!token) throw new Error('refresh failed') localStorage.setItem('asset_tracker_token', token) return token } function redirectSessionExpired() { localStorage.removeItem('asset_tracker_token') if (router.currentRoute.value.path !== '/session-expired') { router.push('/session-expired') } } client.interceptors.response.use( (resp) => resp, async (err: AxiosError) => { const status = err?.response?.status const original = (err.config || {}) as RetryableConfig if (status !== 401 || original._retry) { return Promise.reject(err) } original._retry = true if (isRefreshing) { return new Promise((resolve, reject) => { pendingQueue.push((token) => { if (!token) return reject(err) original.headers = original.headers || {} original.headers.Authorization = `Bearer ${token}` resolve(client(original)) }) }) } isRefreshing = true try { const token = await refreshAccessToken() processQueue(token) original.headers = original.headers || {} original.headers.Authorization = `Bearer ${token}` return client(original) } catch (e) { processQueue(null) redirectSessionExpired() return Promise.reject(e) } finally { isRefreshing = false } }, ) export default client