This commit is contained in:
hamid zarghami
2025-05-27 09:00:58 +03:30
parent 495e59bec4
commit aecaf193ce
9 changed files with 176 additions and 73 deletions
Vendored
BIN
View File
Binary file not shown.
+136
View File
@@ -1 +1,137 @@
node_modules
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
.cache
# vitepress build output
**/.vitepress/dist
# vitepress cache directory
**/.vitepress/cache
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
+1 -61
View File
@@ -5,11 +5,8 @@ import { BrowserRouter, Routes, Route } from 'react-router-dom'
import 'swiper/swiper-bundle.css';
import FaJson from './langs/fa.json'
import AuthRouter from './router/Auth';
import { QueryCache, QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { IApiErrorRepsonse } from './types/error.types';
import { refreshToken } from './pages/auth/service/AuthService';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import ToastContainer from './components/Toast';
import { getRefreshToken, removeRefreshToken, removeToken, setRefreshToken, setToken } from './config/func';
i18next.init({
interpolation: { escapeValue: false },
@@ -29,63 +26,6 @@ declare global {
function App() {
const queryClient = new QueryClient({
queryCache: new QueryCache({
onError: async (error: IApiErrorRepsonse, query) => {
if (error?.response?.status === 401) {
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 refreshTokenValue = await getRefreshToken();
const { data } = await refreshToken({ refreshToken: refreshTokenValue || '' });
if (data?.accessToken?.token) {
// Save the new token
await setToken(data?.accessToken?.token);
// If refresh token also returned, update it
if (data.refreshToken?.token) {
await setRefreshToken(data.refreshToken?.token);
}
// Retry the failed request, even if it was muted
if (query) {
queryClient.invalidateQueries({ queryKey: query.queryKey });
} else {
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
await removeToken();
await removeRefreshToken();
// window.location.href = '/auth/login';
}
}
}
}),
defaultOptions: {
queries: {
refetchOnWindowFocus: false,
+11 -1
View File
@@ -38,11 +38,19 @@ axiosInstance.interceptors.response.use(
try {
const refreshTokenValue = await getRefreshToken();
if (!refreshTokenValue) {
removeRefreshToken();
removeToken();
window.location.href = buildPath("auth/login");
return;
}
const { data } = await refreshToken({
refreshToken: refreshTokenValue || "",
refreshToken: refreshTokenValue,
});
alert("1");
if (data?.accessToken?.token) {
alert("1");
await setToken(data?.accessToken?.token);
if (data.refreshToken?.token) {
await setRefreshToken(data.refreshToken?.token);
@@ -50,6 +58,8 @@ axiosInstance.interceptors.response.use(
originalRequest.headers.Authorization = `Bearer ${data.accessToken.token}`;
return axiosInstance(originalRequest);
} else {
alert("2");
}
} catch (refreshError) {
console.error("Token refresh failed:", refreshError);
+22 -5
View File
@@ -36,26 +36,43 @@ export const getToken = async () => {
return Cookie.get(import.meta.env.VITE_TOKEN_NAME);
};
export const getRefreshToken = () => {
export const getRefreshToken = async () => {
return Cookie.get(import.meta.env.VITE_REFRESH_TOKEN_NAME);
};
const isProduction = import.meta.env.MODE === "production";
const domain = window.location.hostname;
export const setToken = (token: string) => {
Cookie.set(import.meta.env.VITE_TOKEN_NAME, token, { domain });
Cookie.set(import.meta.env.VITE_TOKEN_NAME, token, {
domain,
secure: isProduction,
sameSite: "Lax",
});
};
export const setRefreshToken = (refreshToken: string) => {
Cookie.set(import.meta.env.VITE_REFRESH_TOKEN_NAME, refreshToken, { domain });
Cookie.set(import.meta.env.VITE_REFRESH_TOKEN_NAME, refreshToken, {
domain,
secure: isProduction,
sameSite: "Lax",
});
};
export const removeToken = () => {
Cookie.remove(import.meta.env.VITE_TOKEN_NAME, { domain, path: "/" });
Cookie.remove(import.meta.env.VITE_TOKEN_NAME, {
domain,
secure: isProduction,
sameSite: "Lax",
});
};
export const removeRefreshToken = () => {
Cookie.remove(import.meta.env.VITE_REFRESH_TOKEN_NAME, { domain, path: "/" });
Cookie.remove(import.meta.env.VITE_REFRESH_TOKEN_NAME, {
domain,
secure: isProduction,
sameSite: "Lax",
});
};
export const getRedirectUrl = () => {
+1 -1
View File
@@ -60,7 +60,7 @@ const Username: FC<Props> = (props: Props) => {
setStatus('')
},
onError(error: ErrorType) {
toast(error?.response?.data?.message || 'خطا در تغییر نام کاربری', 'error')
toast(error?.response?.data?.error?.message[0] || 'خطا در تغییر نام کاربری', 'error')
},
})
}
+2 -2
View File
@@ -38,7 +38,7 @@ const ReceiptsDetail: FC = () => {
getInvoce.refetch()
},
onError: (error: ErrorType) => {
toast(error?.response?.data?.message || 'خطا در پرداخت فاکتور', 'error')
toast(error?.response?.data?.error?.message[0], 'error')
}
})
}
@@ -50,7 +50,7 @@ const ReceiptsDetail: FC = () => {
getInvoce.refetch()
},
onError: (error: ErrorType) => {
toast(error?.response?.data?.message || 'خطا در پرداخت فاکتور', 'error')
toast(error?.response?.data?.error?.message[0], 'error')
}
})
}
@@ -36,7 +36,7 @@ const ApproveInvoice: FC<Props> = ({ id, refetch, status }) => {
setShowModal(false)
},
onError: (error: ErrorType) => {
toast(error?.response?.data?.message || 'خطا در تایید فاکتور', 'error')
toast(error?.response?.data?.error?.message[0], 'error')
}
})
}
@@ -52,7 +52,7 @@ const ApproveInvoice: FC<Props> = ({ id, refetch, status }) => {
reset()
},
onError: (error: ErrorType) => {
toast(error?.response?.data?.message || 'خطا در تایید فاکتور', 'error')
toast(error?.response?.data?.error?.message[0] || 'خطا در تایید فاکتور', 'error')
}
})
}
+1 -1
View File
@@ -116,7 +116,7 @@ const Request: FC = () => {
navigate(buildPath(Pages.dashboard, slug))
},
onError: (error: ErrorType) => {
toast(error?.response?.data?.message || 'خطا در ارسال درخواست', 'error')
toast(error?.response?.data?.error?.message[0], 'error')
}
})
}