fix(control-ui): include basePath in default WebSocket URL (#30228)

Merged via squash.

Prepared head SHA: a56d8d441c
Co-authored-by: gittb <8284364+gittb@users.noreply.github.com>
Co-authored-by: velvet-shark <126378+velvet-shark@users.noreply.github.com>
Reviewed-by: @velvet-shark
This commit is contained in:
Ben Gitter
2026-03-01 15:01:43 -05:00
committed by GitHub
parent 907c09e1d5
commit 5d7314db22
3 changed files with 73 additions and 1 deletions

View File

@@ -0,0 +1,63 @@
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
function createStorageMock(): Storage {
const store = new Map<string, string>();
return {
get length() {
return store.size;
},
clear() {
store.clear();
},
getItem(key: string) {
return store.get(key) ?? null;
},
key(index: number) {
return Array.from(store.keys())[index] ?? null;
},
removeItem(key: string) {
store.delete(key);
},
setItem(key: string, value: string) {
store.set(key, String(value));
},
};
}
describe("loadSettings default gateway URL derivation", () => {
beforeEach(() => {
vi.resetModules();
vi.stubGlobal("localStorage", createStorageMock());
vi.stubGlobal("navigator", { language: "en-US" } as Navigator);
});
afterEach(() => {
vi.restoreAllMocks();
vi.unstubAllGlobals();
});
it("uses configured base path and normalizes trailing slash", async () => {
vi.stubGlobal("location", {
protocol: "https:",
host: "gateway.example:8443",
pathname: "/ignored/path",
} as Location);
vi.stubGlobal("window", { __OPENCLAW_CONTROL_UI_BASE_PATH__: " /openclaw/ " } as Window &
typeof globalThis);
const { loadSettings } = await import("./storage.ts");
expect(loadSettings().gatewayUrl).toBe("wss://gateway.example:8443/openclaw");
});
it("infers base path from nested pathname when configured base path is not set", async () => {
vi.stubGlobal("location", {
protocol: "http:",
host: "gateway.example:18789",
pathname: "/apps/openclaw/chat",
} as Location);
vi.stubGlobal("window", {} as Window & typeof globalThis);
const { loadSettings } = await import("./storage.ts");
expect(loadSettings().gatewayUrl).toBe("ws://gateway.example:18789/apps/openclaw");
});
});

View File

@@ -1,6 +1,7 @@
const KEY = "openclaw.control.settings.v1";
import { isSupportedLocale } from "../i18n/index.ts";
import { inferBasePathFromPathname, normalizeBasePath } from "./navigation.ts";
import type { ThemeMode } from "./theme.ts";
export type UiSettings = {
@@ -20,7 +21,14 @@ export type UiSettings = {
export function loadSettings(): UiSettings {
const defaultUrl = (() => {
const proto = location.protocol === "https:" ? "wss" : "ws";
return `${proto}://${location.host}`;
const configured =
typeof window !== "undefined" &&
typeof window.__OPENCLAW_CONTROL_UI_BASE_PATH__ === "string" &&
window.__OPENCLAW_CONTROL_UI_BASE_PATH__.trim();
const basePath = configured
? normalizeBasePath(configured)
: inferBasePathFromPathname(location.pathname);
return `${proto}://${location.host}${basePath}`;
})();
const defaults: UiSettings = {