diff --git a/apps/admin/src/components/sidebar.tsx b/apps/admin/src/components/sidebar.tsx index 79bc063..cfee4b0 100644 --- a/apps/admin/src/components/sidebar.tsx +++ b/apps/admin/src/components/sidebar.tsx @@ -15,6 +15,7 @@ import { Zap, } from "lucide-react"; import { useAccount } from "../features/auth/hooks/useAccount"; +import { clearTokens } from "../utils/api"; interface MenuItemProps { icon: React.ReactNode; @@ -38,7 +39,7 @@ export const Sidebar = () => { const { account } = useAccount(); const handleLogout = () => { - localStorage.removeItem("token"); + clearTokens(); window.location.href = "/"; }; diff --git a/apps/admin/src/utils/api.ts b/apps/admin/src/utils/api.ts index 852e1d4..90f4c60 100644 --- a/apps/admin/src/utils/api.ts +++ b/apps/admin/src/utils/api.ts @@ -1,12 +1,14 @@ import { createApi } from "@opencircle/core"; -import ky from "ky"; +import ky, { HTTPError } from "ky"; const API_URL = import.meta.env.VITE_API_URL || "http://localhost:8000/api"; let isRefreshing = false; let refreshPromise: Promise | null = null; -async function refreshAccessToken(): Promise { +const MAX_REFRESH_RETRIES = 2; + +async function refreshAccessToken(retryCount = 0): Promise { const refreshToken = localStorage.getItem("refreshToken"); if (!refreshToken) { return null; @@ -21,9 +23,21 @@ async function refreshAccessToken(): Promise { localStorage.setItem("token", response.access_token); return response.access_token; - } catch { + } catch (error) { + if (error instanceof HTTPError && error.response.status === 401) { + localStorage.removeItem("token"); + localStorage.removeItem("refreshToken"); + return null; + } + + if (retryCount < MAX_REFRESH_RETRIES) { + await new Promise((resolve) => + setTimeout(resolve, 1000 * (retryCount + 1)), + ); + return refreshAccessToken(retryCount + 1); + } + localStorage.removeItem("token"); - localStorage.removeItem("refreshToken"); return null; } } diff --git a/apps/platform/src/utils/api.ts b/apps/platform/src/utils/api.ts index 8c537bd..0a1b1da 100644 --- a/apps/platform/src/utils/api.ts +++ b/apps/platform/src/utils/api.ts @@ -1,12 +1,14 @@ import { createApi } from "@opencircle/core"; -import ky from "ky"; +import ky, { HTTPError } from "ky"; const API_URL = import.meta.env.VITE_API_URL || "http://localhost:8000/api"; let isRefreshing = false; let refreshPromise: Promise | null = null; -async function refreshAccessToken(): Promise { +const MAX_REFRESH_RETRIES = 2; + +async function refreshAccessToken(retryCount = 0): Promise { const refreshToken = localStorage.getItem("refreshToken"); if (!refreshToken) { return null; @@ -21,9 +23,21 @@ async function refreshAccessToken(): Promise { localStorage.setItem("token", response.access_token); return response.access_token; - } catch { + } catch (error) { + if (error instanceof HTTPError && error.response.status === 401) { + localStorage.removeItem("token"); + localStorage.removeItem("refreshToken"); + return null; + } + + if (retryCount < MAX_REFRESH_RETRIES) { + await new Promise((resolve) => + setTimeout(resolve, 1000 * (retryCount + 1)), + ); + return refreshAccessToken(retryCount + 1); + } + localStorage.removeItem("token"); - localStorage.removeItem("refreshToken"); return null; } }