From 0bf2eb37f6b37068349ec6e0790a2d2736d6483e Mon Sep 17 00:00:00 2001 From: ciregenz Date: Wed, 10 Jun 2026 17:37:57 -0700 Subject: [PATCH] [eric] desktop: renderer self-heals a stale backend port, fixes the post-signin CORS wall --- electron/preload.js | 6 ++++++ frontend/src/shared/config.ts | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/electron/preload.js b/electron/preload.js index 6bffbaa1..e7e70ab2 100644 --- a/electron/preload.js +++ b/electron/preload.js @@ -22,6 +22,12 @@ contextBridge.exposeInMainWorld('__OPENSWARM_PORT__', port); contextBridge.exposeInMainWorld('openswarm', { getBackendPort: () => port, + // Fresh re-query of the LIVE backend port (not the cached preload value). + // Used by the renderer to self-heal if its cached port ever resolved wrong + // (raced null -> 8324, or backend on a fallback port because 8324 was held). + getBackendPortLive: () => { + try { return ipcRenderer.sendSync('get-backend-port-sync'); } catch (_) { return port; } + }, getWebviewPreloadPath: () => webviewPreloadPath, // Per-install auth token required for WS + HTTP calls to the diff --git a/frontend/src/shared/config.ts b/frontend/src/shared/config.ts index 00b47eaf..1e9f031e 100644 --- a/frontend/src/shared/config.ts +++ b/frontend/src/shared/config.ts @@ -1,4 +1,14 @@ -const port = (window as any).__OPENSWARM_PORT__ || 8324; +const _w = window as any; +// Prefer the preload-injected port; if it's missing (preload raced the backend +// port being picked), re-query the live value before falling back to 8324. The +// bare 8324 guess is wrong on any machine where the backend landed on a fallback +// port (e.g. 8324 was held by a leftover backend); see the self-heal below. +const port = + _w.__OPENSWARM_PORT__ || + (_w.openswarm && typeof _w.openswarm.getBackendPortLive === 'function' + ? _w.openswarm.getBackendPortLive() + : 0) || + 8324; const host = window.location.hostname || 'localhost'; export const API_BASE = `http://${host}:${port}/api`; @@ -46,6 +56,25 @@ export function ensureAuthToken(): Promise { return _authTokenPromise; } +// Self-heal: if our backend calls start failing with a network error (the +// renderer is pinned to a stale/wrong port), re-query the live port and reload +// once onto it. Guarded to one attempt per page-load and only when the live +// port actually differs, so a genuinely-down backend can't cause a reload loop. +let _portHealTried = false; +function _maybeHealBackendPort(): void { + if (_portHealTried) return; + try { + const ow = (window as any).openswarm; + const live = ow && typeof ow.getBackendPortLive === 'function' ? ow.getBackendPortLive() : null; + if (typeof live === 'number' && live > 0 && live !== port) { + _portHealTried = true; + window.location.reload(); + } + } catch { + /* never let a heal attempt throw into the fetch path */ + } +} + // Global fetch interceptor: attaches bearer for our API + dedupes/caches GETs in a 1s window. // Cache is keyed `METHOD URL`, GET-only (mutations pass through); non-2xx never cached. const _inflightFetches = new Map>(); @@ -117,6 +146,8 @@ function _installAuthFetchInterceptor() { _inflightFetches.delete(cacheKey); } } catch { + // A network failure reaching our backend may mean we're on a stale port. + _maybeHealBackendPort(); return originalFetch(input, init); } };