From 11f55d9c1c2fb8edb57a408261278d0b70fc5f61 Mon Sep 17 00:00:00 2001 From: hamid zarghami Date: Sat, 14 Mar 2026 10:42:47 +0330 Subject: [PATCH] fx token --- .env | 4 +- src/config/axios.ts | 114 +++++++++++++++++++++++++- src/pages/auth/service/AuthService.ts | 12 +++ 3 files changed, 124 insertions(+), 6 deletions(-) create mode 100644 src/pages/auth/service/AuthService.ts diff --git a/.env b/.env index e68a554..1d1a307 100644 --- a/.env +++ b/.env @@ -1,3 +1,3 @@ VITE_API_URL = 'http://10.165.85.88:4000' -VITE_TOKEN_NAME = 'dpage-editor-t' -VITE_REFRESH_TOKEN_NAME = 'dpage-editor-t' \ No newline at end of file +VITE_TOKEN_NAME = 'dpage-editor-t' +VITE_REFRESH_TOKEN_NAME = 'dpage-editor-refresh-t' \ No newline at end of file diff --git a/src/config/axios.ts b/src/config/axios.ts index 9d529a0..c5ae12b 100644 --- a/src/config/axios.ts +++ b/src/config/axios.ts @@ -1,6 +1,20 @@ import axios from "axios"; -import { getToken } from "./func"; -// import { Pages } from "./Pages"; +import { + getToken, + getRefreshToken, + setToken, + setRefreshToken, + removeToken, + removeRefreshToken, +} from "./func"; +import { refreshToken } from "@/pages/auth/service/AuthService"; + +declare global { + interface Window { + isRefreshingToken?: boolean; + isRefreshTokenExpired?: boolean; + } +} const axiosInstance = axios.create({ baseURL: import.meta.env.VITE_API_URL, @@ -8,7 +22,100 @@ const axiosInstance = axios.create({ axiosInstance.interceptors.response.use( (response) => response, - (error) => { + async (error) => { + const originalRequest = error.config; + + if (error?.response?.status === 401 && !originalRequest._retry) { + // اگر refresh token منقضی شده باشد، دیگر تلاش نکن + if (window.isRefreshTokenExpired) { + return Promise.reject(error); + } + + // بررسی کن که آیا خطا از refresh endpoint است + const isRefreshEndpoint = originalRequest?.url?.includes( + "/admin/auth/refresh", + ); + + // اگر خطا از refresh endpoint است، refresh token منقضی شده + if (isRefreshEndpoint) { + window.isRefreshTokenExpired = true; + await removeToken(); + await removeRefreshToken(); + window.location.href = `/`; + return Promise.reject(error); + } + + if (window.isRefreshingToken) { + // Wait for the refresh to complete + await new Promise((resolve) => { + const checkComplete = setInterval(() => { + if (!window.isRefreshingToken) { + clearInterval(checkComplete); + resolve(true); + } + }, 100); + }); + return axiosInstance(originalRequest); + } + + originalRequest._retry = true; + window.isRefreshingToken = true; + + try { + const refreshTokenValue = await getRefreshToken(); + if (!refreshTokenValue) { + window.isRefreshTokenExpired = true; + await removeRefreshToken(); + await removeToken(); + window.location.href = `/`; + return Promise.reject(error); + } + const data = await refreshToken({ + refreshToken: refreshTokenValue, + }); + + if (data?.data?.accessToken?.token) { + await setToken(data.data.accessToken.token); + if (data?.data?.refreshToken?.token) { + await setRefreshToken(data.data.refreshToken.token); + } + + // Reset expired flag if refresh was successful + window.isRefreshTokenExpired = false; + + originalRequest.headers.Authorization = `Bearer ${data.data.accessToken.token}`; + return axiosInstance(originalRequest); + } else { + window.isRefreshTokenExpired = true; + await removeRefreshToken(); + await removeToken(); + window.location.href = `/`; + return Promise.reject(error); + } + } catch (refreshError) { + console.error("Token refresh failed:", refreshError); + + // بررسی کن که آیا خطا از refresh endpoint است + const isRefreshError = (refreshError as { + response?: { config?: { url?: string }; status?: number }; + })?.response?.config?.url?.includes("/admin/auth/refresh"); + const is401Error = + (refreshError as { response?: { status?: number } })?.response + ?.status === 401; + + // اگر refresh token خودش 401 خورده، آن را منقضی شده علامت بزن + if (isRefreshError || is401Error) { + window.isRefreshTokenExpired = true; + } + + await removeToken(); + await removeRefreshToken(); + window.location.href = `/`; + return Promise.reject(refreshError); + } finally { + window.isRefreshingToken = false; + } + } return Promise.reject(error); }, ); @@ -16,7 +123,6 @@ axiosInstance.interceptors.response.use( axiosInstance.interceptors.request.use(async function (config) { const tokenValue = await getToken(); const token = tokenValue ? tokenValue : ""; - config.headers.Authorization = "Bearer " + token; return config; }); diff --git a/src/pages/auth/service/AuthService.ts b/src/pages/auth/service/AuthService.ts new file mode 100644 index 0000000..2bd2e27 --- /dev/null +++ b/src/pages/auth/service/AuthService.ts @@ -0,0 +1,12 @@ +import axios from "@/config/axios"; + +export const refreshToken = async ({ + refreshToken, +}: { + refreshToken: string; +}) => { + const { data } = await axios.post(`/admin/auth/refresh`, { + refreshToken: refreshToken, + }); + return data; +};