import type { AxiosError, AxiosRequestConfig } from 'axios'; import type { JsonValue } from 'type-fest'; import axios from 'axios'; import { isNil } from 'lodash-es'; import { router } from '@/lib/router'; import { clearTokens, doRefreshToken, getRefreshToken, getToken, setRefreshToken, setToken } from './token'; export const HEADER_API_VERSION = { 'X-Api-Version': 'latest', }; export const axiosClient = axios.create({ baseURL: '/api/v1/', headers: HEADER_API_VERSION, }); axiosClient.interceptors.request.use((config) => { const token = getToken(); if (token !== null) { config.headers = config.headers ?? {}; config.headers.Authorization = `Bearer ${token}`; } return config; }); type RetryConfig = AxiosRequestConfig & { _retry?: boolean }; interface ResponseData { code: number; error_id: string; status: string; data: JsonValue; } axiosClient.interceptors.response.use(async (response) => { const data = response.data as ResponseData; if (data.code !== 200) { return Promise.reject(data); } response.data = data.data; return response; }, async (error: AxiosError) => { const originalRequest = error.config as RetryConfig | undefined; if (!error.response || error.response.status !== 401 || !originalRequest) { return Promise.reject(error); } if (error.response.status === 401 && originalRequest.url !== '/auth/refresh' && !isNil(getRefreshToken())) { try { const maybeRefreshTokenResponse = await doRefreshToken(); if (maybeRefreshTokenResponse.status !== 200) { throw new Error('Failed to refresh token'); } const { access_token, refresh_token } = maybeRefreshTokenResponse.data; originalRequest.headers = originalRequest.headers ?? {}; originalRequest.headers.Authorization = `Bearer ${access_token}`; setToken(access_token); setRefreshToken(refresh_token); return await axiosClient(originalRequest); } // eslint-disable-next-line unused-imports/no-unused-vars catch (e) { // Should remove token (tokens are out of date) clearTokens(); await router.navigate({ to: '/authorize' }); } } });