118 lines
2.8 KiB
TypeScript
118 lines
2.8 KiB
TypeScript
import { ENV_CONSTANT } from '@/constants/secrect.constant';
|
|
import { useAppStore } from '@/store/app.store';
|
|
import { useAuthStore } from '@/store/auth.store';
|
|
import axios from 'axios';
|
|
|
|
const BASE_URL = ENV_CONSTANT.BASE_API_URL;
|
|
|
|
const axiosClient = axios.create({
|
|
baseURL: BASE_URL,
|
|
withCredentials: true,
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
});
|
|
|
|
export const axiosAuth = axios.create({
|
|
baseURL: BASE_URL,
|
|
withCredentials: true,
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
});
|
|
|
|
let refreshPromise: Promise<string> | null = null;
|
|
|
|
const isInvalidSessionResponse = (error: unknown) => {
|
|
if (!axios.isAxiosError(error)) return false;
|
|
|
|
return error.response?.status === 401 || error.response?.status === 403;
|
|
};
|
|
|
|
const refreshAccessToken = () => {
|
|
if (!refreshPromise) {
|
|
refreshPromise = axiosAuth
|
|
.post('api/auth/refresh', {})
|
|
.then((response) => {
|
|
const accessToken = response.data?.access_token;
|
|
|
|
if (!accessToken) {
|
|
throw new Error('Refresh failed: no access token');
|
|
}
|
|
|
|
useAuthStore.getState().setAccessToken(accessToken);
|
|
|
|
return accessToken;
|
|
})
|
|
.catch((error: unknown) => {
|
|
if (isInvalidSessionResponse(error)) {
|
|
useAuthStore.getState().logout();
|
|
useAppStore.getState().clear();
|
|
|
|
if (typeof window !== 'undefined') {
|
|
window.location.href = '/login';
|
|
}
|
|
}
|
|
|
|
throw error;
|
|
})
|
|
.finally(() => {
|
|
refreshPromise = null;
|
|
});
|
|
}
|
|
|
|
return refreshPromise;
|
|
};
|
|
|
|
axiosClient.interceptors.request.use(
|
|
(config) => {
|
|
const token = useAuthStore.getState().accessToken;
|
|
|
|
if (token && config.headers) {
|
|
config.headers.Authorization = `Bearer ${token}`;
|
|
}
|
|
|
|
return config;
|
|
},
|
|
(error) => {
|
|
return Promise.reject(error);
|
|
},
|
|
);
|
|
|
|
axiosClient.interceptors.response.use(
|
|
(response) => response,
|
|
async (error) => {
|
|
const originalRequest = error?.config;
|
|
const status = error?.response?.status;
|
|
const requestUrl = originalRequest?.url ?? '';
|
|
const isAuthRoute =
|
|
requestUrl.includes('api/auth/login') ||
|
|
requestUrl.includes('api/auth/refresh') ||
|
|
requestUrl.includes('api/auth/forget-password') ||
|
|
requestUrl.includes('api/auth/reset-password') ||
|
|
requestUrl.includes('api/auth/set-password');
|
|
|
|
if (
|
|
status === 401 &&
|
|
originalRequest &&
|
|
!originalRequest._retry &&
|
|
!isAuthRoute
|
|
) {
|
|
originalRequest._retry = true;
|
|
|
|
try {
|
|
const accessToken = await refreshAccessToken();
|
|
originalRequest.headers.Authorization = `Bearer ${accessToken}`;
|
|
|
|
return axiosClient(originalRequest);
|
|
} catch (refreshError) {
|
|
return Promise.reject(refreshError);
|
|
}
|
|
}
|
|
|
|
return Promise.reject(error);
|
|
},
|
|
);
|
|
|
|
export default axiosClient;
|