From deb99ec1c95f2e6856e86514ddae2d045d345943 Mon Sep 17 00:00:00 2001 From: Codex Date: Wed, 3 Jun 2026 18:33:50 +0800 Subject: [PATCH] fix(web): defer workbench fetches until auth ready --- web/hwlab-cloud-web/scripts/check.ts | 4 ++++ web/hwlab-cloud-web/src/App.tsx | 2 +- web/hwlab-cloud-web/src/services/api/client.ts | 2 +- web/hwlab-cloud-web/src/state/workbench.ts | 8 +++++--- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/web/hwlab-cloud-web/scripts/check.ts b/web/hwlab-cloud-web/scripts/check.ts index e5f4d98a..8f8e0895 100644 --- a/web/hwlab-cloud-web/scripts/check.ts +++ b/web/hwlab-cloud-web/scripts/check.ts @@ -36,6 +36,8 @@ for (const file of requiredFiles) { console.log("hwlab-cloud-web check: React module assets present"); const html = readWeb("index.html"); +const appTsx = readWeb("src/App.tsx"); +const apiClientSource = readWeb("src/services/api/client.ts"); const appSource = readCloudWebAppSource(rootDir); const css = readWeb("src/styles/workbench.css"); @@ -53,6 +55,8 @@ assertIncludes(appSource, "DevicePodSidebar", "Device Pod sidebar component must assertIncludes(appSource, "CommandBar", "command bar component must exist"); assertIncludes(appSource, "SkillsView", "skills component must exist"); assertIncludes(appSource, "SettingsView", "settings component must exist"); +assertIncludes(appTsx, "useWorkbenchStore(auth.authState === \"authenticated\")", "Workbench data requests must wait until auth is authenticated"); +assertIncludes(apiClientSource, "adapter: (): Promise> => fetchJson(\"/v1/rpc/system.health\", { method: \"POST\"", "adapter health must use POST /v1/rpc/system.health"); assertIncludes(appSource, "id=\"command-input\"", "React runtime must render command input selector"); assertIncludes(appSource, "id=\"conversation-list\"", "React runtime must render conversation list selector"); assertIncludes(appSource, "id=\"device-pod-sidebar\"", "React runtime must render Device Pod sidebar selector"); diff --git a/web/hwlab-cloud-web/src/App.tsx b/web/hwlab-cloud-web/src/App.tsx index b3028a28..a77423a5 100644 --- a/web/hwlab-cloud-web/src/App.tsx +++ b/web/hwlab-cloud-web/src/App.tsx @@ -16,7 +16,7 @@ import { fetchText } from "./services/api/client"; export function App(): ReactElement { const auth = useAuth(); - const store = useWorkbenchStore(); + const store = useWorkbenchStore(auth.authState === "authenticated"); const [route, setRoute] = useState(() => routeFromLocation()); const [leftCollapsed, setLeftCollapsed] = useState(false); const [rightCollapsed, setRightCollapsed] = useState(false); diff --git a/web/hwlab-cloud-web/src/services/api/client.ts b/web/hwlab-cloud-web/src/services/api/client.ts index d4eccfb4..e2e1ddf0 100644 --- a/web/hwlab-cloud-web/src/services/api/client.ts +++ b/web/hwlab-cloud-web/src/services/api/client.ts @@ -87,7 +87,7 @@ export const api = { healthLive: (): Promise> => fetchJson("/health/live", { timeoutMs: 12000, timeoutName: "health/live" }), health: (): Promise> => fetchJson("/health", { timeoutMs: 12000, timeoutName: "health" }), restIndex: (): Promise> => fetchJson("/v1", { timeoutMs: 12000, timeoutName: "REST index" }), - adapter: (): Promise> => fetchJson("/v1/rpc/system.health", { timeoutMs: 12000, timeoutName: "adapter" }), + adapter: (): Promise> => fetchJson("/v1/rpc/system.health", { method: "POST", body: JSON.stringify({}), timeoutMs: 12000, timeoutName: "adapter" }), devicePods: (): Promise> => fetchJson("/v1/device-pods", { timeoutMs: 12000, timeoutName: "Device Pods" }), devicePodStatus: (devicePodId: string): Promise> => fetchJson(`/v1/device-pods/${encodeURIComponent(devicePodId)}/status`, { timeoutMs: 12000, timeoutName: "Device Pod status" }), devicePodEvents: (devicePodId: string): Promise> => fetchJson(`/v1/device-pods/${encodeURIComponent(devicePodId)}/events?limit=120`, { timeoutMs: 12000, timeoutName: "Device Pod events" }), diff --git a/web/hwlab-cloud-web/src/state/workbench.ts b/web/hwlab-cloud-web/src/state/workbench.ts index 405897c0..c1aaaf69 100644 --- a/web/hwlab-cloud-web/src/state/workbench.ts +++ b/web/hwlab-cloud-web/src/state/workbench.ts @@ -95,7 +95,7 @@ export interface WorkbenchStore { selectDevicePod(devicePodId: string): void; } -export function useWorkbenchStore(): WorkbenchStore { +export function useWorkbenchStore(enabled: boolean): WorkbenchStore { const [state, dispatch] = useReducer(reducer, initialState); const activeConversationId = firstNonEmptyString( @@ -144,14 +144,16 @@ export function useWorkbenchStore(): WorkbenchStore { }, [state.selectedDevicePodId]); useEffect(() => { + if (!enabled) return; void hydrate(); - }, [hydrate]); + }, [enabled, hydrate]); useEffect(() => { + if (!enabled) return; void refreshLive(); const timer = window.setInterval(() => void refreshLive(), 30_000); return () => window.clearInterval(timer); - }, [refreshLive]); + }, [enabled, refreshLive]); const createSession = useCallback(async () => { const workspace = await ensureWorkspace(state.workspace);