From 96c005d47944adac6af6ee7a4ff77472a63d367d Mon Sep 17 00:00:00 2001 From: duanshuwen Date: Sat, 11 Jul 2026 20:49:12 +0800 Subject: [PATCH] feat: add admin auth session expiration handling Add auth session utilities and event handling in src/lib/auth-session.ts Integrate auth expiration checks in API request and uploadMediaAsset functions Update App component to subscribe to auth expired events and update auth state Add test suite for the auth session utility function Adjust maintenance grid CSS layout Remove unused UI components and imports in StructurePage --- src/App.tsx | 7 ++++++- src/api.ts | 13 +++++++++++++ src/lib/auth-session.ts | 14 ++++++++++++++ src/pages/structure/StructurePage.tsx | 17 +---------------- src/styles.css | 5 +---- tests/auth-session.test.ts | 11 +++++++++++ 6 files changed, 46 insertions(+), 21 deletions(-) create mode 100644 src/lib/auth-session.ts create mode 100644 tests/auth-session.test.ts diff --git a/src/App.tsx b/src/App.tsx index f19291b..4731747 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,12 +1,17 @@ -import { useState } from "react"; +import { useEffect, useState } from "react"; import { getToken } from "./api"; import { AdminShell } from "@/components/admin/AdminShell"; +import { subscribeAuthExpired } from "@/lib/auth-session"; import { LoginPage } from "@/pages/login/LoginPage"; export default function App() { const [authed, setAuthed] = useState(!!getToken()); + useEffect(() => { + return subscribeAuthExpired(() => setAuthed(false)); + }, []); + if (!authed) { return setAuthed(true)} />; } diff --git a/src/api.ts b/src/api.ts index 566719e..2a30ee5 100644 --- a/src/api.ts +++ b/src/api.ts @@ -1,3 +1,5 @@ +import { notifyAuthExpired, shouldExpireAdminSession } from "@/lib/auth-session"; + export type Dashboard = { stats: { productCount: number; @@ -319,6 +321,11 @@ export function clearToken() { window.localStorage.removeItem(TOKEN_KEY); } +function handleAuthExpired() { + clearToken(); + notifyAuthExpired(); +} + async function request(path: string, options: RequestInit = {}): Promise { const headers = new Headers(options.headers); headers.set("Content-Type", "application/json"); @@ -331,6 +338,9 @@ async function request(path: string, options: RequestInit = {}): Promise { }); if (!response.ok) { + if (path !== "/api/admin/auth/login" && shouldExpireAdminSession(response.status, Boolean(token))) { + handleAuthExpired(); + } const body = await response.json().catch(() => ({})); throw new Error(body.message ?? `请求失败:${response.status}`); } @@ -387,6 +397,9 @@ export async function uploadMediaAsset(file: File, group = "site-config") { }); if (!response.ok) { + if (shouldExpireAdminSession(response.status, Boolean(token))) { + handleAuthExpired(); + } const body = await response.json().catch(() => ({})); throw new Error(body.message ?? `请求失败:${response.status}`); } diff --git a/src/lib/auth-session.ts b/src/lib/auth-session.ts new file mode 100644 index 0000000..cbc274c --- /dev/null +++ b/src/lib/auth-session.ts @@ -0,0 +1,14 @@ +export const AUTH_EXPIRED_EVENT = "wonderq-admin-auth-expired"; + +export function shouldExpireAdminSession(status: number, hadToken: boolean) { + return hadToken && status === 401; +} + +export function notifyAuthExpired() { + window.dispatchEvent(new Event(AUTH_EXPIRED_EVENT)); +} + +export function subscribeAuthExpired(callback: () => void) { + window.addEventListener(AUTH_EXPIRED_EVENT, callback); + return () => window.removeEventListener(AUTH_EXPIRED_EVENT, callback); +} diff --git a/src/pages/structure/StructurePage.tsx b/src/pages/structure/StructurePage.tsx index 8f18d51..764cdb8 100644 --- a/src/pages/structure/StructurePage.tsx +++ b/src/pages/structure/StructurePage.tsx @@ -3,7 +3,6 @@ import { ArrowDown, ArrowUp, ChevronRight, - Eye, Plus, RefreshCcw, Save, @@ -666,22 +665,8 @@ export function StructurePage({ ))} - ) : ( -
- 当前维护页面 -

{activePage.title}

-

{activePage.frontPath}

- {activePage.subtitle} -
- )} + ) : null}
-
- - - {activePage.frontPath} - {activePage.subtitle} - -

页面模块

{activePage.modules.map((module, index) => (
{ + assert.equal(shouldExpireAdminSession(401, true), true); + assert.equal(shouldExpireAdminSession(401, false), false); + assert.equal(shouldExpireAdminSession(403, true), false); + assert.equal(shouldExpireAdminSession(500, true), false); +});