diff --git a/dev-dist/sw.js b/dev-dist/sw.js index cbdba1e..d8627db 100644 --- a/dev-dist/sw.js +++ b/dev-dist/sw.js @@ -82,7 +82,7 @@ define(['./workbox-e7681877'], (function (workbox) { 'use strict'; "revision": "3ca0b8505b4bec776b69afdba2768812" }, { "url": "index.html", - "revision": "0.se5chuts08o" + "revision": "0.mgpgjruthpo" }], {}); workbox.cleanupOutdatedCaches(); workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), { diff --git a/src/App.tsx b/src/App.tsx index 678c05d..a12c823 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -10,12 +10,20 @@ import { QueryCache, QueryClient, QueryClientProvider } from '@tanstack/react-qu import { ToastContainer } from 'react-toastify' import FaJson from './langs/fa.json' import { IApiErrorRepsonse } from './types/error.types' + +// Extend Window interface to include isRefreshingToken property +declare global { + interface Window { + isRefreshingToken?: boolean; + } +} import MainRouter from './router/Main' import AuthRouter from './router/Auth' import { Pages } from './config/Pages' import useConvertNumbers from './hooks/useConvertNumbers'; import { Helmet, HelmetProvider } from 'react-helmet-async'; import moment from 'moment-jalaali'; +import { refreshToken } from './pages/auth/service/AuthService'; i18next.init({ interpolation: { escapeValue: false }, @@ -29,19 +37,65 @@ i18next.init({ const queryClient = new QueryClient({ queryCache: new QueryCache({ - onError: (error: IApiErrorRepsonse) => { + onError: async (error: IApiErrorRepsonse) => { if (error?.response?.status === 401) { - localStorage.removeItem(import.meta.env.VITE_TOKEN_NAME) - window.location.href = '/auth/login'; + try { + // Use a flag to track if refresh is in progress + 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; + } + + // Set the flag to indicate refresh is in progress + window.isRefreshingToken = true; + + try { + const { data } = await refreshToken({ refreshToken: localStorage.getItem(import.meta.env.VITE_REFRESH_TOKEN_NAME) || '' }); + console.log('data', data); + + if (data?.accessToken?.token) { + // Save the new token + localStorage.setItem(import.meta.env.VITE_TOKEN_NAME, data?.accessToken?.token); + + // If refresh token also returned, update it + if (data.refreshToken?.token) { + localStorage.setItem(import.meta.env.VITE_REFRESH_TOKEN_NAME, data.refreshToken?.token); + } + + // Retry the failed request + queryClient.invalidateQueries(); + return; + } else { + throw new Error('Invalid token response'); + } + } finally { + window.isRefreshingToken = false; + } + } catch (refreshError: unknown) { + console.error('Token refresh failed:', refreshError); + // Clear tokens and redirect to login + localStorage.removeItem(import.meta.env.VITE_TOKEN_NAME); + localStorage.removeItem(import.meta.env.VITE_REFRESH_TOKEN_NAME); + window.location.href = '/auth/login'; + } } - }, + } }), defaultOptions: { queries: { refetchOnWindowFocus: false, + retry: false // staleTime: 86400000 // 1 day in milliseconds - }, - }, + } + } }); const App: FC = () => { diff --git a/src/config/axios.ts b/src/config/axios.ts index db51774..200d1bd 100644 --- a/src/config/axios.ts +++ b/src/config/axios.ts @@ -1,5 +1,5 @@ import axios from "axios"; -import { Pages } from "./Pages"; +// import { Pages } from "./Pages"; const axiosInstance = axios.create({ baseURL: import.meta.env.VITE_BASE_URL, @@ -8,10 +8,10 @@ const axiosInstance = axios.create({ axiosInstance.interceptors.response.use( (response) => response, (error) => { - if (error.response.status === 401) { - localStorage.removeItem(import.meta.env.VITE_TOKEN_NAME); - window.location.href = Pages.auth.login; - } + // if (error.response.status === 401) { + // localStorage.removeItem(import.meta.env.VITE_TOKEN_NAME); + // window.location.href = Pages.auth.login; + // } return Promise.reject(error); } ); diff --git a/src/pages/auth/service/AuthService.ts b/src/pages/auth/service/AuthService.ts index 1c7f34f..da477ec 100644 --- a/src/pages/auth/service/AuthService.ts +++ b/src/pages/auth/service/AuthService.ts @@ -5,6 +5,7 @@ import { LoginWithOtpType, LoginWithPasswordType, OtpVerifyType, + RefreshTokenType, RegisterType, } from "../types/AuthTypes"; @@ -37,3 +38,8 @@ export const register = async (params: RegisterType) => { const { data } = await axios.post(`/auth/register/complete`, params); return data; }; + +export const refreshToken = async (params: RefreshTokenType) => { + const { data } = await axios.post(`/auth/refresh`, params); + return data; +}; diff --git a/src/pages/auth/types/AuthTypes.ts b/src/pages/auth/types/AuthTypes.ts index 5d86165..8ab41b8 100644 --- a/src/pages/auth/types/AuthTypes.ts +++ b/src/pages/auth/types/AuthTypes.ts @@ -46,3 +46,7 @@ export type RegisterType = { password: string; code: number; }; + +export type RefreshTokenType = { + refreshToken: string; +};