fix: scope localStorage settings key by basePath to prevent cross-deployment conflicts

- Add settingsKeyForGateway() function similar to tokenSessionKeyForGateway()
- Use scoped key format: openclaw.control.settings.v1:https://example.com/gateway-a
- Add migration from legacy static key on load
- Fixes #47481
This commit is contained in:
ObitaBot 2026-03-16 13:43:26 +08:00 committed by Peter Steinberger
parent 7cdd8a84a6
commit 5ece9afa8b

View File

@ -1,8 +1,12 @@
const KEY = "openclaw.control.settings.v1"; const SETTINGS_KEY_PREFIX = "openclaw.control.settings.v1:";
const LEGACY_TOKEN_SESSION_KEY = "openclaw.control.token.v1"; const LEGACY_TOKEN_SESSION_KEY = "openclaw.control.token.v1";
const TOKEN_SESSION_KEY_PREFIX = "openclaw.control.token.v1:"; const TOKEN_SESSION_KEY_PREFIX = "openclaw.control.token.v1:";
const MAX_SCOPED_SESSION_ENTRIES = 10; const MAX_SCOPED_SESSION_ENTRIES = 10;
function settingsKeyForGateway(gatewayUrl: string): string {
return `${SETTINGS_KEY_PREFIX}${normalizeGatewayTokenScope(gatewayUrl)}`;
}
type ScopedSessionSelection = { type ScopedSessionSelection = {
sessionKey: string; sessionKey: string;
lastActiveSessionKey: string; lastActiveSessionKey: string;
@ -188,7 +192,9 @@ export function loadSettings(): UiSettings {
}; };
try { try {
const raw = storage?.getItem(KEY); // First check for legacy key (no scope), then check for scoped key
const scopedKey = settingsKeyForGateway(defaults.gatewayUrl);
const raw = storage?.getItem(scopedKey) ?? storage?.getItem(SETTINGS_KEY_PREFIX + "default") ?? storage?.getItem("openclaw.control.settings.v1");
if (!raw) { if (!raw) {
return defaults; return defaults;
} }
@ -256,9 +262,11 @@ function persistSettings(next: UiSettings) {
persistSessionToken(next.gatewayUrl, next.token); persistSessionToken(next.gatewayUrl, next.token);
const storage = getSafeLocalStorage(); const storage = getSafeLocalStorage();
const scope = normalizeGatewayTokenScope(next.gatewayUrl); const scope = normalizeGatewayTokenScope(next.gatewayUrl);
const scopedKey = settingsKeyForGateway(next.gatewayUrl);
let existingSessionsByGateway: Record<string, ScopedSessionSelection> = {}; let existingSessionsByGateway: Record<string, ScopedSessionSelection> = {};
try { try {
const raw = storage?.getItem(KEY); // Try to migrate from legacy key or other scopes
const raw = storage?.getItem(scopedKey) ?? storage?.getItem(SETTINGS_KEY_PREFIX + "default") ?? storage?.getItem("openclaw.control.settings.v1");
if (raw) { if (raw) {
const parsed = JSON.parse(raw) as PersistedUiSettings; const parsed = JSON.parse(raw) as PersistedUiSettings;
if (parsed.sessionsByGateway && typeof parsed.sessionsByGateway === "object") { if (parsed.sessionsByGateway && typeof parsed.sessionsByGateway === "object") {
@ -294,5 +302,5 @@ function persistSettings(next: UiSettings) {
sessionsByGateway, sessionsByGateway,
...(next.locale ? { locale: next.locale } : {}), ...(next.locale ? { locale: next.locale } : {}),
}; };
storage?.setItem(KEY, JSON.stringify(persisted)); storage?.setItem(scopedKey, JSON.stringify(persisted));
} }