From 1c62c474758d4f41e9f3e1e3332f499e79431d2b Mon Sep 17 00:00:00 2001 From: Dmitrii Iurco Date: Mon, 25 May 2026 16:41:33 -0400 Subject: [PATCH] fix: 500 on setup complete + wizard shows all 7 steps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two bugs: 1. AttributeError: AuthManager.update_password does not exist — the fallback when create_user fails should call set_password_admin(). This caused a 500 on every setup submit when an admin user already existed (e.g. from a previous install attempt). 2. Wizard was jumping to step 2 and skipping domain steps 3-4 when preconfigured data existed in cell_config.json. Since the installer no longer sets that data, and the wizard must always show all steps, the installerConfigured state and all step-skipping navigation is removed. Values are still pre-filled if found in config. Co-Authored-By: Claude Sonnet 4.6 --- api/setup_manager.py | 2 +- tests/test_setup_manager.py | 2 +- webui/src/pages/Setup.jsx | 21 +++++---------------- 3 files changed, 7 insertions(+), 18 deletions(-) diff --git a/api/setup_manager.py b/api/setup_manager.py index e3f54d9..5d5c6bb 100644 --- a/api/setup_manager.py +++ b/api/setup_manager.py @@ -223,7 +223,7 @@ class SetupManager: role='admin', ) if not ok: - ok = self.auth_manager.update_password('admin', password) + ok = self.auth_manager.set_password_admin('admin', password) if not ok: return {'success': False, 'errors': ['Failed to set admin password.']} diff --git a/tests/test_setup_manager.py b/tests/test_setup_manager.py index 77405f8..f3e9ff0 100644 --- a/tests/test_setup_manager.py +++ b/tests/test_setup_manager.py @@ -252,7 +252,7 @@ def test_complete_setup_returns_error_when_create_user_fails( setup_manager, mock_config_manager, mock_auth_manager, tmp_path): mock_config_manager.get_identity.return_value = {} mock_auth_manager.create_user.return_value = False - mock_auth_manager.update_password.return_value = False + mock_auth_manager.set_password_admin.return_value = False with patch.dict(os.environ, {'DATA_DIR': str(tmp_path)}): result = setup_manager.complete_setup(_valid_payload()) assert result['success'] is False diff --git a/webui/src/pages/Setup.jsx b/webui/src/pages/Setup.jsx index 1cd4998..8830878 100644 --- a/webui/src/pages/Setup.jsx +++ b/webui/src/pages/Setup.jsx @@ -831,10 +831,7 @@ export default function Setup() { const [submitting, setSubmitting] = useState(false); const [submitError, setSubmitError] = useState(''); - // True when the bash installer already configured cell identity - const [installerConfigured, setInstallerConfigured] = useState(false); - - // Pre-fill from installer config; if cell + domain already set, jump to password step + // Pre-fill from any existing config (e.g. from a previous partial run) useEffect(() => { setupAPI.getStatus() .then(res => { @@ -849,12 +846,8 @@ export default function Setup() { if (pre.domain_name) setCustomDomain(pre.domain_name); if (pre.cloudflare_api_token) setCloudflareToken(pre.cloudflare_api_token); if (pre.duckdns_token) setDuckdnsToken(pre.duckdns_token); - if (pre.cell_name && pre.domain_mode) { - setInstallerConfigured(true); - setStep(2); // skip cell name + domain steps — already done by installer - } }) - .catch(() => {}); // fail silently — wizard works from scratch + .catch(() => {}); }, []); const skipStep4 = domainType === 'lan'; @@ -862,13 +855,9 @@ export default function Setup() { const goNext = () => setStep(s => Math.min(s + 1, TOTAL_STEPS)); const goBack = () => setStep(s => Math.max(s - 1, 1)); - // When installer pre-configured identity: step 2 → step 5 (skip domain steps) - const handleStep2Next = () => installerConfigured ? setStep(5) : goNext(); - const handleStep2Back = () => installerConfigured ? setStep(1) : goBack(); - const handleStep3Next = () => skipStep4 ? setStep(5) : setStep(4); const handleStep4Back = () => setStep(3); - const handleStep5Back = () => installerConfigured ? setStep(2) : skipStep4 ? setStep(3) : setStep(4); + const handleStep5Back = () => skipStep4 ? setStep(3) : setStep(4); const handleSubmit = async () => { setSubmitError(''); @@ -949,8 +938,8 @@ export default function Setup() { confirm={passwordConfirm} onChangePassword={setPassword} onChangeConfirm={setPasswordConfirm} - onNext={handleStep2Next} - onBack={handleStep2Back} + onNext={goNext} + onBack={goBack} /> )} {step === 3 && (