diff --git a/docker-compose.yaml b/docker-compose.yaml index 0bd27178..83ee5405 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -211,9 +211,13 @@ services: ui: image: ${REGISTRY:-dograhai}/dograh-ui:latest environment: + # Bind the Next.js standalone server to all interfaces + HOSTNAME: "0.0.0.0" + # Server-side URL (SSR, internal Docker network) BACKEND_URL: "${BACKEND_URL:-http://api:8000}" NODE_ENV: "oss" + # Flag to enable/ disable posthog ENABLE_TELEMETRY: "${ENABLE_TELEMETRY:-true}" @@ -229,7 +233,7 @@ services: test: [ "CMD-SHELL", - "wget --no-verbose --tries=1 --spider http://localhost:3010 || exit 1", + "wget --no-verbose --tries=1 --spider http://127.0.0.1:3010 || exit 1", ] interval: 30s timeout: 10s diff --git a/ui/src/lib/auth/config.ts b/ui/src/lib/auth/config.ts index 70d03bfb..30cb44d1 100644 --- a/ui/src/lib/auth/config.ts +++ b/ui/src/lib/auth/config.ts @@ -49,11 +49,14 @@ async function resolveAuthConfig(): Promise { return cachedConfig; } } catch { - // Backend not reachable — fall back to local + // Backend not reachable — fall through without caching so we retry next request. } - cachedConfig = { authProvider: "local", stackConfig: null }; - return cachedConfig; + // Unknown (backend unreachable). Return the local fallback for THIS request but + // do NOT cache it: caching here would pin the entire UI to local auth until a + // container restart if the first resolution loses the startup race with the api + // service. Leaving it uncached means the next request retries and self-heals. + return { authProvider: "local", stackConfig: null }; } /** diff --git a/ui/src/middleware.ts b/ui/src/middleware.ts index e510ca97..be7c81ea 100644 --- a/ui/src/middleware.ts +++ b/ui/src/middleware.ts @@ -20,15 +20,23 @@ async function fetchAuthProvider(): Promise { const res = await fetch(`${backendUrl}/api/v1/health`); if (res.ok) { const data = await res.json(); + // Only cache a DEFINITIVE answer from the backend. Never cache a failure: + // this is a module-scoped cache with no TTL, so a single early request + // during container startup (before the api service is reachable) would + // otherwise poison it to 'local' for the life of the worker — redirecting + // every Stack user to the local /auth/login form even though the backend + // reports `stack`. cachedAuthProvider = (data.auth_provider as string) || 'local'; return cachedAuthProvider; } } catch { - // Backend not reachable — fall back to local + // Backend not reachable — fall through without caching so we retry next request. } - cachedAuthProvider = 'local'; - return cachedAuthProvider; + // Provider unknown (backend unreachable). Return a non-'local' sentinel so the + // middleware does NOT guard/redirect: assuming 'local' here would bounce Stack + // users to /auth/login. Deliberately not cached — the next request retries. + return 'unknown'; } export async function middleware(request: NextRequest) {