fix: 500 on setup complete + wizard shows all 7 steps
Unit Tests / test (push) Successful in 15m41s

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 <noreply@anthropic.com>
This commit is contained in:
2026-05-25 16:41:33 -04:00
parent 4a42ff5dcc
commit 1c62c47475
3 changed files with 7 additions and 18 deletions
+1 -1
View File
@@ -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.']}
+1 -1
View File
@@ -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
+5 -16
View File
@@ -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 && (