From 9c675e5c7d0f06befe536594e5cdcb10ab495ea2 Mon Sep 17 00:00:00 2001 From: archipelago Date: Sat, 15 Aug 2026 09:36:37 -0400 Subject: [PATCH] fix(login): backend-unreachable no longer masquerades as a fresh node MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit auth.isSetup failing (backend warming up after boot, transient proxy blip) dropped Login.vue into its catch and showed 'Set Up Your Node' on a fully-onboarded node — seen on framework-pt right after its network move, and the same fail-open class RootRedirect already fixed for the intro flash. Errors now fail toward the ordinary login form and a background probe re-asks until the backend answers; a genuinely fresh node flips to the setup form on the first successful probe. Co-Authored-By: Claude Fable 5 --- neode-ui/src/views/Login.vue | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/neode-ui/src/views/Login.vue b/neode-ui/src/views/Login.vue index 3395cdf1..1af96dc2 100644 --- a/neode-ui/src/views/Login.vue +++ b/neode-ui/src/views/Login.vue @@ -381,19 +381,44 @@ onMounted(async () => { await pollServerStartup() } - // Check if password has been set up — show setup form if not + // Check if password has been set up — show setup form if not. + // + // An RPC failure is NOT evidence of a fresh node: a backend still warming + // up (or a transient proxy blip) used to land here and flip a + // fully-onboarded node into "Set Up Your Node" — seen on framework-pt + // right after a network move (2026-08-15), the same fail-open class + // RootRedirect fixed for the intro. Fail toward the ordinary login form + // and keep re-probing until the backend answers; a genuinely fresh node + // flips to setup the moment the first probe lands (and could not have + // completed setup against a dead backend anyway). try { const result = await rpcClient.call({ method: 'auth.isSetup', params: {}, timeout: 8000 }) isSetup.value = Boolean(result) isSetupMode.value = !isSetup.value } catch { - isSetup.value = false - isSetupMode.value = true + isSetup.value = true + isSetupMode.value = false + void retrySetupProbe() } finally { isCheckingSetup.value = false } }) +/** Re-probe auth.isSetup until it answers, then honour the real state. */ +async function retrySetupProbe() { + for (let i = 0; i < 10; i++) { + await new Promise((r) => setTimeout(r, Math.min(2000 * (i + 1), 8000))) + try { + const result = await rpcClient.call({ method: 'auth.isSetup', params: {}, timeout: 8000 }) + isSetup.value = Boolean(result) + isSetupMode.value = !isSetup.value + return + } catch { + // still unreachable — keep the login form up and try again + } + } +} + function handleSetupWithSound() { if (!loading.value && password.value && password.value === confirmPassword.value) {