| |
| import axios, { AxiosError, AxiosRequestConfig } from 'axios'; |
| import * as endpoints from './api-endpoints'; |
| import { setTokenHeader } from './headers-helpers'; |
| import type * as t from './types'; |
|
|
| async function _get<T>(url: string, options?: AxiosRequestConfig): Promise<T> { |
| const response = await axios.get(url, { ...options }); |
| return response.data; |
| } |
|
|
| async function _getResponse<T>(url: string, options?: AxiosRequestConfig): Promise<T> { |
| return await axios.get(url, { ...options }); |
| } |
|
|
| 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 _postTTS(url: string, formData: FormData, options?: AxiosRequestConfig) { |
| const response = await axios.post(url, formData, { |
| ...options, |
| headers: { 'Content-Type': 'multipart/form-data' }, |
| responseType: 'arraybuffer', |
| }); |
| 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<T>(url: string): Promise<T> { |
| const response = await axios.delete(url); |
| return response.data; |
| } |
|
|
| async function _deleteWithOptions<T>(url: string, options?: AxiosRequestConfig): Promise<T> { |
| 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; |
| } |
|
|
| let isRefreshing = false; |
| let failedQueue: { resolve: (value?: any) => void; reject: (reason?: any) => void }[] = []; |
|
|
| const refreshToken = (retry?: boolean): Promise<t.TRefreshTokenResponse | undefined> => |
| _post(endpoints.refreshToken(retry)); |
|
|
| const dispatchTokenUpdatedEvent = (token: string) => { |
| setTokenHeader(token); |
| window.dispatchEvent(new CustomEvent('tokenUpdated', { detail: token })); |
| }; |
|
|
| const processQueue = (error: AxiosError | null, token: string | null = null) => { |
| failedQueue.forEach((prom) => { |
| if (error) { |
| prom.reject(error); |
| } else { |
| prom.resolve(token); |
| } |
| }); |
| failedQueue = []; |
| }; |
|
|
| if (typeof window !== 'undefined') { |
| axios.interceptors.response.use( |
| (response) => response, |
| async (error) => { |
| const originalRequest = error.config; |
| if (!error.response) { |
| return Promise.reject(error); |
| } |
|
|
| if (originalRequest.url?.includes('/api/auth/2fa') === true) { |
| return Promise.reject(error); |
| } |
| if (originalRequest.url?.includes('/api/auth/logout') === true) { |
| return Promise.reject(error); |
| } |
|
|
| if (error.response.status === 401 && !originalRequest._retry) { |
| console.warn('401 error, refreshing token'); |
| 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 response = await refreshToken( |
| |
| originalRequest.url?.includes('api/auth/refresh') === true ? true : false, |
| ); |
|
|
| const token = response?.token ?? ''; |
|
|
| if (token) { |
| originalRequest.headers['Authorization'] = 'Bearer ' + token; |
| dispatchTokenUpdatedEvent(token); |
| processQueue(null, token); |
| return await axios(originalRequest); |
| } else if (window.location.href.includes('share/')) { |
| console.log( |
| `Refresh token failed from shared link, attempting request to ${originalRequest.url}`, |
| ); |
| } else { |
| window.location.href = endpoints.loginPage(); |
| } |
| } catch (err) { |
| processQueue(err as AxiosError, null); |
| return Promise.reject(err); |
| } finally { |
| isRefreshing = false; |
| } |
| } |
|
|
| return Promise.reject(error); |
| }, |
| ); |
| } |
|
|
| export default { |
| get: _get, |
| getResponse: _getResponse, |
| post: _post, |
| postMultiPart: _postMultiPart, |
| postTTS: _postTTS, |
| put: _put, |
| delete: _delete, |
| deleteWithOptions: _deleteWithOptions, |
| patch: _patch, |
| refreshToken, |
| dispatchTokenUpdatedEvent, |
| }; |
|
|