fix(frontend): auth error handler 5xx

This commit is contained in:
sky5454 2026-04-08 20:38:31 +08:00
parent 4d6145abc2
commit 13c9a2087a

View file

@ -1,6 +1,6 @@
import { Outlet, createRootRoute, useRouterState } from "@tanstack/react-router" import { Outlet, createRootRoute, useRouterState } from "@tanstack/react-router"
import { TanStackRouterDevtools } from "@tanstack/react-router-devtools" import { TanStackRouterDevtools } from "@tanstack/react-router-devtools"
import { useEffect } from "react" import { useEffect, useState } from "react"
import { getLauncherAuthStatus } from "@/api/launcher-auth" import { getLauncherAuthStatus } from "@/api/launcher-auth"
import { AppLayout } from "@/components/app-layout" import { AppLayout } from "@/components/app-layout"
@ -30,6 +30,8 @@ const RootLayout = () => {
(m) => m.routeId === "/launcher-login" || m.routeId === "/launcher-setup", (m) => m.routeId === "/launcher-login" || m.routeId === "/launcher-setup",
) )
const [authError, setAuthError] = useState<string | null>(null)
// Session guard: proactively check auth status on every page load. // Session guard: proactively check auth status on every page load.
// This catches the case where ?token= auto-login bypassed the login/setup UI. // This catches the case where ?token= auto-login bypassed the login/setup UI.
useEffect(() => { useEffect(() => {
@ -43,12 +45,16 @@ const RootLayout = () => {
} }
}) })
.catch((err: unknown) => { .catch((err: unknown) => {
// For real HTTP errors (e.g. 503 when the auth store is unavailable), // On 401/403, redirect to login — the session is invalid.
// redirect to login so the user can re-authenticate rather than being // On 5xx (e.g. 503 when the auth store is unavailable) or network errors,
// silently stranded on the dashboard. Network failures (no response) // do NOT redirect: a subsequent successful login would loop straight back here.
// are left alone — launcherFetch handles the 401 redirect on real calls. // launcherFetch handles 401 on real API calls regardless.
if (err instanceof Error && /^status \d+$/.test(err.message)) { if (err instanceof Error && /^status 40[13]$/.test(err.message)) {
globalThis.location.assign("/launcher-login") globalThis.location.assign("/launcher-login")
} else {
setAuthError(
err instanceof Error ? err.message : "Auth service unavailable, please try to delete the launcher-auth.db at picoclaw home directory and restart the application.",
)
} }
}) })
}, [isAuthPage]) }, [isAuthPage])
@ -70,10 +76,24 @@ const RootLayout = () => {
} }
return ( return (
<>
{authError && (
<div className="bg-destructive text-destructive-foreground fixed inset-x-0 top-0 z-[100] flex items-center justify-between px-4 py-2 text-sm shadow-md">
<span>Auth service error: {authError}</span>
<button
className="ml-4 opacity-70 hover:opacity-100"
onClick={() => setAuthError(null)}
aria-label="Dismiss"
>
</button>
</div>
)}
<AppLayout> <AppLayout>
<Outlet /> <Outlet />
{import.meta.env.DEV ? <TanStackRouterDevtools /> : null} {import.meta.env.DEV ? <TanStackRouterDevtools /> : null}
</AppLayout> </AppLayout>
</>
) )
} }