From 63aaeba73efc30798c3f66b64ca7b3d1bcd2a8de Mon Sep 17 00:00:00 2001 From: navaneethkrishnansuresh Date: Fri, 1 May 2026 18:38:54 +0530 Subject: [PATCH 1/2] config-adapter: retry getConfig before falling through to local mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Defaulting to mode='local' on the first failed /api/config response caused a confusing login screen for managed-mode users post-relogin: the user was already authenticated via the gateway, but BD Core booted faster than its own fastify routes responded, getConfig() failed open to local, and App.tsx routed to the auth screen. Retry budget: 5 attempts with exponential backoff (~7.5s total). After that we still fall through to local — that's correct for genuine local installs where /api/config legitimately 404s. The gateway side also probes /api/config before redirecting (defense-in-depth), so reaching the fallback at all should be rare in managed mode. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../client_web/src/api/config-adapter.ts | 84 ++++++++++++------- 1 file changed, 55 insertions(+), 29 deletions(-) diff --git a/builds/typescript/client_web/src/api/config-adapter.ts b/builds/typescript/client_web/src/api/config-adapter.ts index d715cd6..4b7221c 100644 --- a/builds/typescript/client_web/src/api/config-adapter.ts +++ b/builds/typescript/client_web/src/api/config-adapter.ts @@ -21,41 +21,67 @@ export type GatewayClientConfig = { appVersion: string; }; +const DEFAULT_LOCAL_CONFIG: GatewayClientConfig = { + mode: "local", + gatewayUrl: "/api", + billingUrl: "https://my.braindrive.ai/credits", + installMode: "unknown", + installLocation: "unknown", + appVersion: "unknown", +}; + export async function getConfig(): Promise { - try { - const response = await fetch("/api/config", { - headers: buildLocalOwnerHeaders(), - }); - if (!response.ok) { + // Retry on failure with exponential backoff before falling through to + // the local-mode default. This endpoint determines whether BD Core + // shows its own login screen or trusts gateway-injected auth (managed + // mode); falling through to "local" prematurely on a transient network + // hiccup or a still-booting container produces a confusing login screen + // for users who are already authenticated via the gateway. After this + // retry budget is exhausted, we still default to "local" — that's the + // safe choice for genuine local installs where /api/config legitimately + // 404s. The handoff path on the gateway side also probes this endpoint + // before redirecting, so reaching this fallback at all should be rare. + const attempts = [0, 500, 1000, 2000, 4000]; // ms backoff between attempts (5 total tries) + let lastError: unknown = null; + + for (let i = 0; i < attempts.length; i++) { + if (attempts[i] > 0) { + await new Promise((r) => setTimeout(r, attempts[i])); + } + try { + const response = await fetch("/api/config", { + headers: buildLocalOwnerHeaders(), + }); + if (!response.ok) { + // Non-200: server reachable but unhappy. Could be 404 (genuine + // local install with no managed gateway) or 502/503 (managed + // container still booting). Retry — only fall through if we run + // out of attempts. + lastError = new Error(`HTTP ${response.status}`); + continue; + } + const payload = (await response.json()) as GatewayConfig; return { - mode: "local", - gatewayUrl: "/api", - billingUrl: "https://my.braindrive.ai/credits", - installMode: "unknown", - installLocation: "unknown", - appVersion: "unknown" + mode: toDeploymentMode(payload.mode), + gatewayUrl: payload.gateway_url || "/api", + billingUrl: payload.billing_url ?? "https://my.braindrive.ai/credits", + installMode: toInstallMode(payload.install_mode), + installLocation: toInstallLocation(payload.install_location), + appVersion: toAppVersion(payload.app_version), }; + } catch (err) { + lastError = err; } + } - const payload = (await response.json()) as GatewayConfig; - return { - mode: toDeploymentMode(payload.mode), - gatewayUrl: payload.gateway_url || "/api", - billingUrl: payload.billing_url ?? "https://my.braindrive.ai/credits", - installMode: toInstallMode(payload.install_mode), - installLocation: toInstallLocation(payload.install_location), - appVersion: toAppVersion(payload.app_version), - }; - } catch { - return { - mode: "local", - gatewayUrl: "/api", - billingUrl: "https://my.braindrive.ai/credits", - installMode: "unknown", - installLocation: "unknown", - appVersion: "unknown" - }; + // All attempts exhausted — log and fall through to local-mode default. + // For a true local install this is correct; for a managed install it + // means the gateway is genuinely down and the user can't authenticate + // anyway, so showing the login screen is acceptable degraded UX. + if (lastError) { + console.warn("[BD Core] getConfig failed after retries, defaulting to local mode:", lastError); } + return DEFAULT_LOCAL_CONFIG; } function toDeploymentMode(value: unknown): "local" | "managed" { From 96d8dc0aff59a2b36ed5f2f4023fe692ad4b9aeb Mon Sep 17 00:00:00 2001 From: navaneethkrishnansuresh Date: Fri, 1 May 2026 18:40:57 +0530 Subject: [PATCH 2/2] settings: style 'Need more credits?' as amber button MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Was rendering as plain muted text — easy to miss next to the credits balance. Match the existing Buy Credits button's golden accent theme (bg-bd-amber + bg-bd-bg-primary text + amber-hover) so it reads as a clear affordance to expand the topup form. Same Tailwind tokens as the actual Buy Credits button (slightly less prominent — no extra padding, just the inline + glyph) so visual hierarchy still distinguishes "show form" from "complete purchase". Co-Authored-By: Claude Opus 4.7 (1M context) --- .../client_web/src/components/settings/SettingsModal.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/builds/typescript/client_web/src/components/settings/SettingsModal.tsx b/builds/typescript/client_web/src/components/settings/SettingsModal.tsx index 25c0731..1e637bc 100644 --- a/builds/typescript/client_web/src/components/settings/SettingsModal.tsx +++ b/builds/typescript/client_web/src/components/settings/SettingsModal.tsx @@ -2563,8 +2563,9 @@ function AccountSection() { ) : (