fix(dashboard): restore tokenized control ui links

This commit is contained in:
Peter Steinberger
2026-02-06 22:16:53 -08:00
parent e78ae48e69
commit c5194d8148
6 changed files with 65 additions and 22 deletions

View File

@@ -82,18 +82,26 @@ export function setLastActiveSessionKey(host: SettingsHost, next: string) {
}
export function applySettingsFromUrl(host: SettingsHost) {
if (!window.location.search) {
if (!window.location.search && !window.location.hash) {
return;
}
const params = new URLSearchParams(window.location.search);
const tokenRaw = params.get("token");
const passwordRaw = params.get("password");
const sessionRaw = params.get("session");
const gatewayUrlRaw = params.get("gatewayUrl");
const url = new URL(window.location.href);
const params = new URLSearchParams(url.search);
const hashParams = new URLSearchParams(url.hash.startsWith("#") ? url.hash.slice(1) : url.hash);
const tokenRaw = params.get("token") ?? hashParams.get("token");
const passwordRaw = params.get("password") ?? hashParams.get("password");
const sessionRaw = params.get("session") ?? hashParams.get("session");
const gatewayUrlRaw = params.get("gatewayUrl") ?? hashParams.get("gatewayUrl");
let shouldCleanUrl = false;
if (tokenRaw != null) {
const token = tokenRaw.trim();
if (token && token !== host.settings.token) {
applySettings(host, { ...host.settings, token });
}
params.delete("token");
hashParams.delete("token");
shouldCleanUrl = true;
}
@@ -103,6 +111,7 @@ export function applySettingsFromUrl(host: SettingsHost) {
(host as { password: string }).password = password;
}
params.delete("password");
hashParams.delete("password");
shouldCleanUrl = true;
}
@@ -124,14 +133,16 @@ export function applySettingsFromUrl(host: SettingsHost) {
host.pendingGatewayUrl = gatewayUrl;
}
params.delete("gatewayUrl");
hashParams.delete("gatewayUrl");
shouldCleanUrl = true;
}
if (!shouldCleanUrl) {
return;
}
const url = new URL(window.location.href);
url.search = params.toString();
const nextHash = hashParams.toString();
url.hash = nextHash ? `#${nextHash}` : "";
window.history.replaceState({}, "", url.toString());
}

View File

@@ -151,11 +151,11 @@ describe("control UI routing", () => {
expect(container.scrollTop).toBe(maxScroll);
});
it("strips token URL params without importing them", async () => {
it("hydrates token from URL params and strips it", async () => {
const app = mountApp("/ui/overview?token=abc123");
await app.updateComplete;
expect(app.settings.token).toBe("");
expect(app.settings.token).toBe("abc123");
expect(window.location.pathname).toBe("/ui/overview");
expect(window.location.search).toBe("");
});
@@ -169,7 +169,7 @@ describe("control UI routing", () => {
expect(window.location.search).toBe("");
});
it("does not override stored settings from URL token params", async () => {
it("hydrates token from URL params even when settings already set", async () => {
localStorage.setItem(
"openclaw.control.settings.v1",
JSON.stringify({ token: "existing-token" }),
@@ -177,8 +177,17 @@ describe("control UI routing", () => {
const app = mountApp("/ui/overview?token=abc123");
await app.updateComplete;
expect(app.settings.token).toBe("existing-token");
expect(app.settings.token).toBe("abc123");
expect(window.location.pathname).toBe("/ui/overview");
expect(window.location.search).toBe("");
});
it("hydrates token from URL hash and strips it", async () => {
const app = mountApp("/ui/overview#token=abc123");
await app.updateComplete;
expect(app.settings.token).toBe("abc123");
expect(window.location.pathname).toBe("/ui/overview");
expect(window.location.hash).toBe("");
});
});