From 97bc3a29f785e789a8b7d91a30b5afcf34ca877a Mon Sep 17 00:00:00 2001 From: xrpbanks <126300068+xrpbanks@users.noreply.github.com> Date: Sat, 29 Aug 2026 06:05:08 +0200 Subject: [PATCH 1/2] Wake Render backend without blocking frontend --- README.md | 7 ++++++- frontend/.env.example | 4 ++++ frontend/components/XamanLoginPanel.tsx | 17 ++++++++++++++++- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 93de6a2..cbf7445 100644 --- a/README.md +++ b/README.md @@ -105,10 +105,15 @@ Frontend environment setup: - Preferred variable: BACKEND_URL - Existing deployments may continue using NEXT_PUBLIC_BACKEND_URL as a fallback - Local development value: http://localhost:8000 +- Optional browser-visible health-only variable: NEXT_PUBLIC_BACKEND_HEALTH_URL + (set this to the public backend origin; never include credentials or secrets) The browser calls the frontend's same-origin `/api/backend` proxy. The proxy forwards only the supported CalorieApp endpoints to the configured backend and -keeps mobile authentication sessions first-party. +keeps mobile authentication sessions first-party. The Xaman startup flow may +probe the public backend `/health` endpoint directly so a Render cold start does +not occupy the frontend proxy long enough to trigger frontend 429 responses. +All authenticated requests continue through the same-origin proxy. Xaman sign-in opens in a separate tab while the original CalorieApp tab waits for a short-lived, one-time browser handoff. Every CalorieApp-owned Xaman login diff --git a/frontend/.env.example b/frontend/.env.example index 020d35d..1d078cf 100644 --- a/frontend/.env.example +++ b/frontend/.env.example @@ -4,6 +4,10 @@ BACKEND_URL=http://localhost:8000 # Supported as a deployment-compatible fallback for existing environments. NEXT_PUBLIC_BACKEND_URL=http://localhost:8000 +# Optional direct, public health origin used only to wake a sleeping backend +# without holding the same-origin frontend proxy open. Do not include secrets. +NEXT_PUBLIC_BACKEND_HEALTH_URL=http://localhost:8000 + # Optional: frontend-only post-login fallback route if you need custom UX. # Keep this app-local (starts with /) and do not put secrets here. # NEXT_PUBLIC_POST_LOGIN_FALLBACK=/ diff --git a/frontend/components/XamanLoginPanel.tsx b/frontend/components/XamanLoginPanel.tsx index 3b241dc..12e4cab 100644 --- a/frontend/components/XamanLoginPanel.tsx +++ b/frontend/components/XamanLoginPanel.tsx @@ -27,6 +27,8 @@ type LoginStatusResponse = { }; const BACKEND_BASE_URL = "/api/backend"; +const RENDER_BACKEND_HEALTH_URL = + "https://calorieapp-backend-rvul.onrender.com"; const LOGIN_STATUS_POLL_INTERVAL_MS = 5_000; const LOGIN_STATUS_FALLBACK_LIFETIME_MS = 5 * 60_000; const LOGIN_STATUS_RATE_LIMIT_DELAY_MS = 15_000; @@ -36,6 +38,19 @@ const LOGIN_START_RETRY_DELAY_MS = 15_000; const XAMAN_LAUNCH_MESSAGE_TYPE = "calorieapp-xaman-navigate"; const XAMAN_LAUNCH_ERROR_TYPE = "calorieapp-xaman-error"; +function backendHealthBaseUrl(): string { + const configuredUrl = process.env.NEXT_PUBLIC_BACKEND_HEALTH_URL?.trim(); + if (configuredUrl) { + return configuredUrl.replace(/\/$/, ""); + } + + if (window.location.hostname === "calorieapp-frontend.onrender.com") { + return RENDER_BACKEND_HEALTH_URL; + } + + return BACKEND_BASE_URL; +} + function delay(milliseconds: number, signal: AbortSignal) { return new Promise((resolve, reject) => { if (signal.aborted) { @@ -261,7 +276,7 @@ export function XamanLoginPanel() { ); try { - await waitForBackendReady(BACKEND_BASE_URL, controller.signal); + await waitForBackendReady(backendHealthBaseUrl(), controller.signal); setLoginStatus("Service ready. Opening Xaman..."); const data = await startLoginWithRetry(controller.signal, () => { From 570b3e68f4f8b030f6a9f233dc141b229334c573 Mon Sep 17 00:00:00 2001 From: xrpbanks <126300068+xrpbanks@users.noreply.github.com> Date: Sat, 29 Aug 2026 06:13:14 +0200 Subject: [PATCH 2/2] Keep public health probes credential-free --- frontend/lib/backendRequest.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/lib/backendRequest.ts b/frontend/lib/backendRequest.ts index a8f8694..e91a4ec 100644 --- a/frontend/lib/backendRequest.ts +++ b/frontend/lib/backendRequest.ts @@ -43,7 +43,7 @@ export async function backendRequest( try { return await fetch(input, { ...init, - credentials: "include", + credentials: init.credentials ?? "include", signal: controller.signal, }); } catch (error) { @@ -140,7 +140,7 @@ export async function waitForBackendReady( try { const response = await backendRequest( `${backendBaseUrl}/health`, - { cache: "no-store", signal }, + { cache: "no-store", credentials: "omit", signal }, attemptTimeoutMs );