import { useNotification, useTranslation } from "@sforsoftware/inhandel-components"; import axios, { AxiosError, AxiosInstance, AxiosResponse } from "axios"; import { TokenPrefix } from "~/types"; import { useCurrentTenant } from "~/utils"; import { getAppIdFromTokenPrefix, useToken } from "~/utils/token"; import { useAuth0 } from '@auth0/auth0-vue'; /** * Creates an axios instance with the given base url and token prefix * @param baseUrl The base url for the axios instance * @param tokenPrefix The token prefix to use for the axios instance * @param managementApiUrl The management api url * @returns The axios instance */ export function createAxios(baseUrl: string, tokenPrefix: TokenPrefix, managementApiUrl: string, translationCallback?: (msg: string) => string): AxiosInstance { const instance = axios.create({ baseURL: baseUrl, // This makes sure any status code goes to the success function // This is because we want to handle 401s in the success function validateStatus: () => true, }); instance.interceptors.request.use(async (request) => { const token = await useToken(managementApiUrl, tokenPrefix); if (token) { request.headers.Authorization = `Bearer ${token}`; } return request; }); instance.interceptors.response.use((x) => onSuccess(x, tokenPrefix, managementApiUrl, translationCallback), onError); return instance; } /** * Handles the success of an axios request * @param response The axios response * @param tokenPrefix The token prefix * @param managementApiUrl The management api url * @returns The response */ async function onSuccess(response: AxiosResponse, tokenPrefix: TokenPrefix, managementApiUrl: string, translationCallback?: (msg: string) => string) { // if 401, retry if (response?.status === 401 && response.config) { // Get the auth0 token from the auth0 instance let auth0Token = localStorage.getItem("auth0token"); if (!auth0Token) { const { getAccessTokenSilently, logout } = useAuth0(); try { auth0Token = await getAccessTokenSilently(); if (auth0Token) { localStorage.setItem("auth0token", auth0Token); } } catch { sessionStorage.clear(); localStorage.clear(); await logout({ logoutParams: { returnTo: location.origin } }); } } const { getCurrentTenantAsync } = useCurrentTenant(); const currentTenant = await getCurrentTenantAsync(); const newToken = await axios.get(`token/${currentTenant}/${getAppIdFromTokenPrefix(tokenPrefix)}`, { headers: { Authorization: `Bearer ${auth0Token}` }, baseURL: managementApiUrl, }).then(x => x.data); // If we have a new token, save it and retry the request if (newToken) { localStorage.setItem(`${tokenPrefix}${currentTenant}`, newToken); response.config.headers.Authorization = `Bearer ${newToken}`; return axios.request(response.config); } } // Error notification if (response.status >= 400) { const notification = useNotification(); if (!translationCallback) { translationCallback = (msg) => msg; } const errorNotification = notification.open({ title: "Error", message: translationCallback(response.data?.translationKey ?? response.data?.message ?? response.statusText), duration: 5000, type: "error", }); setTimeout(() => errorNotification.close, 5000); } return response; } /** * Handles the error of an axios request if the request fails * @param error The axios error * @returns The error */ async function onError(error: AxiosError) { // Contains things like network errors const message = error.message; const notification = useNotification(); const errorNotification = notification.open({ title: "Error", message: message, duration: 5000, type: "error", }); setTimeout(() => errorNotification.close, 5000); return Promise.reject(error); }