diff --git a/src/components/frontend/src/app.tsx b/src/components/frontend/src/app.tsx index bb62d3c0..66d7da0c 100644 --- a/src/components/frontend/src/app.tsx +++ b/src/components/frontend/src/app.tsx @@ -2465,6 +2465,11 @@ function App() { } useEffect(() => { loadSession(); }, []); + useEffect(() => { + const onAuthRequired = () => setSession(null); + window.addEventListener("unidesk-auth-required", onAuthRequired); + return () => window.removeEventListener("unidesk-auth-required", onAuthRequired); + }, []); if (checking) return h("main", { className: "loading-screen" }, h("div", { className: "brand-mark" }, "UD"), h("span", null, "加载会话")); if (!session) return h(LoginScreen, { onLogin: setSession }); diff --git a/src/components/frontend/src/unidesk-error.ts b/src/components/frontend/src/unidesk-error.ts index 835b7175..3001a456 100644 --- a/src/components/frontend/src/unidesk-error.ts +++ b/src/components/frontend/src/unidesk-error.ts @@ -104,6 +104,17 @@ function responseMessage(body: any): string { return ""; } +function emitAuthRequired(path: string, method: string, occurredAt: Date): void { + if (typeof window === "undefined" || typeof window.dispatchEvent !== "function") return; + window.dispatchEvent(new CustomEvent("unidesk-auth-required", { + detail: { path: requestUrl(path), method, occurredAt: occurredAt.toISOString() }, + })); +} + +function isAuthRequiredResponse(status: number, message: string): boolean { + return status === 401 && message.trim().toLowerCase() === "authentication required"; +} + function bodyIndicatesFailure(body: any, fields: UniDeskFailureField[]): boolean { if (!body || typeof body !== "object" || Array.isArray(body)) return false; return fields.some((field) => field !== false && body[field] === false); @@ -183,6 +194,7 @@ export async function requestJson(path: string, options: UniDeskRequestJsonOptio if (!response.ok || bodyIndicatesFailure(parsed.body, failureFields)) { const upstreamMessage = responseMessage(parsed.body); const message = upstreamMessage || httpTitle(response.status, response.statusText); + if (isAuthRequiredResponse(response.status, message)) emitAuthRequired(path, method, occurredAt); throw new UniDeskRequestError( message, requestMeta("http", path, method, occurredAt, { @@ -213,6 +225,7 @@ export async function requestBlob(path: string, options: RequestInit = {}): Prom const parsed = parseJson(text); const upstreamMessage = responseMessage(parsed.body); const message = upstreamMessage || httpTitle(response.status, response.statusText); + if (isAuthRequiredResponse(response.status, message)) emitAuthRequired(path, method, occurredAt); throw new UniDeskRequestError( message, requestMeta("http", path, method, occurredAt, {