Files
agologum/client/src/api/axios.ts
Blitblank 317a7bce9d
All checks were successful
Build and Deploy Frontend / build-and-deploy (push) Successful in 6s
Build and Deploy API / build-and-deploy (push) Successful in 9s
comments galore
2026-04-23 00:15:49 -05:00

94 lines
3.1 KiB
TypeScript

// http service hub
// handles interceptors and such
import axios from "axios";
const baseUrl: string = import.meta.env.DEV ? import.meta.env.VITE_DEV_API_URL : "https://app.vxbard.net/api"
export const api = axios.create({
baseURL: baseUrl
});
type FailedRequest = { resolve: (token: string) => void, reject: (error: unknown) => void}
let isRefreshing: boolean = false;
let failedQueue: FailedRequest[] = [];
export const authStorage = {
getAccessToken: () => localStorage.getItem("accessToken"),
getRefreshToken: () => localStorage.getItem("refreshToken"),
setTokens: ({ accessToken, refreshToken } : { accessToken: string, refreshToken: string }) => {
localStorage.setItem("accessToken", accessToken)
localStorage.setItem("refreshToken", refreshToken)
},
clear: () => {
localStorage.removeItem("accessToken")
localStorage.removeItem("refreshToken")
}
}
const processQueue = (error: unknown, token: string | null = null): void => {
failedQueue.forEach(prom => {
if (error) prom.reject(error);
else prom.resolve(token as string);
})
failedQueue = [];
}
// intercept on each request
api.interceptors.request.use(config => { // add access token to request headers
const token = authStorage.getAccessToken();
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
// intercept on each response
api.interceptors.response.use(response => response, async error => { // mainly for authentication refreshTokens
const originalRequest = error.config;
// if un authorized then refresh the token
if(error.response?.status === 401 && !originalRequest._retry) {
if(isRefreshing) {
return new Promise((resolve, reject) => {
failedQueue.push({ resolve, reject })
}).then(token => {
originalRequest.headers.Authorization = `Bearer ${token}`;
return api(originalRequest);
}).catch(err => Promise.reject(err));
}
originalRequest._retry = true;
isRefreshing = true;
const refreshToken = authStorage.getRefreshToken();
try {
// request refresh endpoint get back a new accessToken
const res = await axios.post(`${baseUrl}/auth/refresh`, { refreshToken });
const { accessToken, refreshToken: newRefresh } = res.data;
authStorage.setTokens({ accessToken, refreshToken: newRefresh });
processQueue(null, accessToken);
originalRequest.headers.Authorization = `Bearer ${accessToken}`;
return api(originalRequest);
} catch (err) {
processQueue(err, null);
authStorage.clear()
window.location.href = "/login";
return Promise.reject(err);
} finally {
isRefreshing = false;
}
}
return Promise.reject(error);
})
// TODO: if you get a 403 while navigating then redirect to the last authenticated page
// if you gert a 403 on a form submissio nthen do like an unauthorized popup (message: stale session <login link>) (or redirect to login like i said elsewhere)
export default api;