diff --git a/web/src/lib/auth.svelte.ts b/web/src/lib/auth.svelte.ts index 56e15ed..01b97a0 100644 --- a/web/src/lib/auth.svelte.ts +++ b/web/src/lib/auth.svelte.ts @@ -24,17 +24,39 @@ class AuthStore { return this.state.mode === "authenticated"; } + /** + * Attempt to load the current user session from the backend. + * Only transitions to "unauthenticated" on a definitive 401/403. + * Retries indefinitely on transient failures (network errors, 5xx) + * so that a slow backend startup doesn't kick the user to login. + */ async init() { - try { - const response = await fetch("/api/auth/me"); - if (response.ok) { - const user: User = await response.json(); - this.state = { mode: "authenticated", user }; - } else { - this.state = { mode: "unauthenticated" }; + const MAX_DELAY_MS = 7_000; + let delayMs = 500; + + for (;;) { + try { + const response = await fetch("/api/auth/me"); + + if (response.ok) { + const user: User = await response.json(); + this.state = { mode: "authenticated", user }; + return; + } + + // Definitive rejection — no session or not authorized + if (response.status === 401 || response.status === 403) { + this.state = { mode: "unauthenticated" }; + return; + } + + // Server error (5xx) or unexpected status — retry + } catch { + // Network error (backend not up yet) — retry } - } catch { - this.state = { mode: "unauthenticated" }; + + await new Promise((r) => setTimeout(r, delayMs)); + delayMs = Math.min(delayMs * 2, MAX_DELAY_MS); } }