/* eslint-disable @typescript-eslint/no-explicit-any */ import axios, { AxiosRequestConfig, AxiosError } from 'axios'; /* TODO: fix dependency cycle */ // eslint-disable-next-line import/no-cycle import { refreshToken } from './data-service'; import { setTokenHeader } from './headers-helpers'; let isRefreshing = false; let failedQueue: { resolve: (value?: any) => void; reject: (reason?: any) => void }[] = []; const processQueue = (error: AxiosError | null, token: string | null = null) => { failedQueue.forEach((prom) => { if (error) { prom.reject(error); } else { prom.resolve(token); } }); failedQueue = []; }; axios.interceptors.response.use( (response) => response, async (error) => { const originalRequest = error.config; if (error.response.status === 401 && !originalRequest._retry) { originalRequest._retry = true; if (isRefreshing) { try { const token = await new Promise((resolve, reject) => { failedQueue.push({ resolve, reject }); }); originalRequest.headers['Authorization'] = 'Bearer ' + token; return await axios(originalRequest); } catch (err) { return Promise.reject(err); } } isRefreshing = true; try { const { token } = await refreshToken( // Handle edge case where we get a blank screen if the initial 401 error is from a refresh token request originalRequest.url?.includes('api/auth/refresh') ? true : false, ); if (token) { originalRequest.headers['Authorization'] = 'Bearer ' + token; setTokenHeader(token); window.dispatchEvent(new CustomEvent('tokenUpdated', { detail: token })); processQueue(null, token); return await axios(originalRequest); } else { window.location.href = '/login'; } } catch (err) { processQueue(err as AxiosError, null); return Promise.reject(err); } finally { isRefreshing = false; } } return Promise.reject(error); }, ); async function _get(url: string, options?: AxiosRequestConfig): Promise { const response = await axios.get(url, { ...options }); return response.data; } async function _post(url: string, data?: any) { const response = await axios.post(url, JSON.stringify(data), { headers: { 'Content-Type': 'application/json' }, }); return response.data; } async function _postMultiPart(url: string, formData: FormData, options?: AxiosRequestConfig) { const response = await axios.post(url, formData, { ...options, headers: { 'Content-Type': 'multipart/form-data' }, }); return response.data; } async function _put(url: string, data?: any) { const response = await axios.put(url, JSON.stringify(data), { headers: { 'Content-Type': 'application/json' }, }); return response.data; } async function _delete(url: string): Promise { const response = await axios.delete(url); return response.data; } async function _deleteWithOptions(url: string, options?: AxiosRequestConfig): Promise { const response = await axios.delete(url, { ...options }); return response.data; } async function _patch(url: string, data?: any) { const response = await axios.patch(url, JSON.stringify(data), { headers: { 'Content-Type': 'application/json' }, }); return response.data; } export default { get: _get, post: _post, postMultiPart: _postMultiPart, put: _put, delete: _delete, deleteWithOptions: _deleteWithOptions, patch: _patch, };