From 175ebeede49ebc37b27fef292ad789f43def900f Mon Sep 17 00:00:00 2001 From: "Gan, Jimmy" Date: Sat, 7 Mar 2026 11:02:53 +0800 Subject: [PATCH] fix: sync dashboard page query state Keep the selected dashboard page in sync with lightweight URL query params so direct terminal links can land on the right page without exposing extra client state. Co-Authored-By: Claude Opus 4.6 (1M context) --- dashboard/frontend/src/App.svelte | 44 +++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/dashboard/frontend/src/App.svelte b/dashboard/frontend/src/App.svelte index 68bf340..c877037 100644 --- a/dashboard/frontend/src/App.svelte +++ b/dashboard/frontend/src/App.svelte @@ -16,6 +16,21 @@ import { onMount } from "svelte"; import { getToken, checkAuth, setToken, setRefreshToken, currentUser, setCurrentUser, getPreferences, savePreferences } from "./lib/api.js"; + const KNOWN_PAGES = new Set([ + "dashboard", + "docker", + "gitea", + "files", + "terminal", + "openclaw", + "chat-digest", + "settings", + "security", + "litellm", + "cc-connect", + "info-engine", + ]); + let page = $state("dashboard"); let dark = $state(false); let sidebarOpen = $state(false); @@ -27,6 +42,26 @@ let sidebarOrder = $state(null); let orderSaveTimer = null; + function getRequestedPage() { + const requestedPage = new URLSearchParams(window.location.search).get("page"); + return KNOWN_PAGES.has(requestedPage) ? requestedPage : null; + } + + function canOpenPage(pageId) { + if (!pageId || !KNOWN_PAGES.has(pageId)) return false; + if (pageId === "settings") return userRole === "admin"; + if (["litellm", "cc-connect", "info-engine"].includes(pageId)) return hasPageAccess("dashboard"); + return hasPageAccess(pageId); + } + + function syncPageQuery(pageId) { + const url = new URL(window.location.href); + if (pageId === "dashboard") url.searchParams.delete("page"); + else url.searchParams.set("page", pageId); + if (pageId !== "terminal") url.searchParams.delete("host"); + history.replaceState({}, "", `${url.pathname}${url.search}${url.hash}`); + } + async function fetchUserInfo() { try { const data = await checkAuth(); @@ -78,6 +113,8 @@ setRefreshToken(data.refresh_token); authorized = true; await fetchUserInfo(); + const requestedPage = getRequestedPage(); + if (canOpenPage(requestedPage)) page = requestedPage; loading = false; return; } @@ -96,6 +133,8 @@ try { await fetchUserInfo(); authorized = true; + const requestedPage = getRequestedPage(); + if (canOpenPage(requestedPage)) page = requestedPage; } catch (e) { console.error("Auth check failed:", e); setToken(""); @@ -105,6 +144,11 @@ } }); + $effect(() => { + if (!authorized || loading || !canOpenPage(page)) return; + syncPageQuery(page); + }); + function toggleTheme() { dark = !dark; if (dark) {