88 lines
2.4 KiB
TypeScript
88 lines
2.4 KiB
TypeScript
import axios, { type AxiosError, type InternalAxiosRequestConfig } from 'axios';
|
|
import { AUTH_SCOPE } from '@/constants/auth';
|
|
import { env } from '@/config/env';
|
|
import { getAccessToken, getRefreshToken, storeAuthTokens, clearAuthTokens } from '@/utils/authToken';
|
|
import { logout } from '@/utils/logout';
|
|
import { buildAuthUrl } from '@/utils/authUrl';
|
|
import { createBasicAuthHeader } from '@/config/env';
|
|
import type { TokenResponse } from '@/types/auth';
|
|
|
|
const apiBaseUrl = env.apiBaseUrl;
|
|
|
|
const api = axios.create({
|
|
baseURL: apiBaseUrl,
|
|
timeout: 10000,
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
});
|
|
|
|
let refreshPromise: Promise<void> | null = null;
|
|
|
|
api.interceptors.request.use((config) => {
|
|
const token = getAccessToken();
|
|
|
|
if (token && config.headers) {
|
|
config.headers.Authorization = `Bearer ${token}`;
|
|
}
|
|
|
|
return config;
|
|
});
|
|
|
|
api.interceptors.response.use(
|
|
(response) => response,
|
|
async (error: AxiosError) => {
|
|
const originalRequest = error.config as InternalAxiosRequestConfig & { _retry?: boolean };
|
|
|
|
if (!error.response || error.response.status !== 401 || originalRequest._retry) {
|
|
return Promise.reject(error);
|
|
}
|
|
|
|
if (!refreshPromise) {
|
|
refreshPromise = (async () => {
|
|
const refreshToken = getRefreshToken();
|
|
|
|
if (!refreshToken) {
|
|
throw new Error('Missing refresh token');
|
|
}
|
|
|
|
const params = new URLSearchParams();
|
|
params.append('grant_type', 'refresh_token');
|
|
params.append('refresh_token', refreshToken);
|
|
params.append('scope', AUTH_SCOPE);
|
|
|
|
const response = await axios.post<TokenResponse>(buildAuthUrl('/accounts/oauth2/token/'), params, {
|
|
headers: {
|
|
'Content-Type': 'application/x-www-form-urlencoded',
|
|
Authorization: createBasicAuthHeader(),
|
|
},
|
|
});
|
|
|
|
storeAuthTokens({
|
|
access_token: response.data.access_token,
|
|
refresh_token: response.data.refresh_token,
|
|
});
|
|
})().catch(() => {
|
|
clearAuthTokens();
|
|
logout();
|
|
return undefined;
|
|
});
|
|
}
|
|
|
|
try {
|
|
await refreshPromise;
|
|
originalRequest._retry = true;
|
|
const token = getAccessToken();
|
|
|
|
if (token && originalRequest.headers) {
|
|
originalRequest.headers.Authorization = `Bearer ${token}`;
|
|
}
|
|
|
|
return api(originalRequest);
|
|
} finally {
|
|
refreshPromise = null;
|
|
}
|
|
},
|
|
);
|
|
|
|
export default api;
|