Skip to content

Commit 26f7ba6

Browse files
committed
Write the config before the account, so a reinstall onto live data works
install() created the Super Admin account and only then wrote config.php. That breaks the one case this file is now most often opened for: a database that still holds every booking and account, and a config.php that a deploy erased. In that state create_user() fails on the duplicate email, install() returns early, and write_config() never runs -- so the panel is still unconfigured, still shows "not set up yet", and the installer sends you round the same loop with no way out. The data was never at risk, but there was no route back in. Now the config is written as soon as the database details are known to work, and an account count decides what happens next: rows already there means this is a reconnection rather than an installation, so no second Super Admin is made and the last screen says to sign in with the existing one. An empty database still gets the account it asked for, and if creating it fails the message says the settings were saved and a reload should let you in -- because by then it is true. Verified by extracting the call order from the parsed function with comments stripped: db_connect, migrate, write_config, the account count, create_user, with the only early return between the write and the account being the write failing itself. Checking it by eye first gave the wrong answer -- the regex matched the words create_user() and write_config() in the comment explaining the bug. The full path could not be run here: this sandbox has no MySQL server, so the installer's last four UI tests cannot pass and the reconnect branch is unexercised against a live database. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0128YzbhrfGdegUSc9RrARRf
1 parent b2c4a4d commit 26f7ba6

1 file changed

Lines changed: 46 additions & 11 deletions

File tree

‎admin/install.php‎

Lines changed: 46 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -170,17 +170,46 @@ function install(array $form): array
170170
return ['Setting up the tables failed: ' . $e->getMessage()];
171171
}
172172

173+
// The config is written before the account, and that order matters.
174+
//
175+
// It used to be the other way round, which broke the one case this file is
176+
// now most often opened for: a database that still holds everything, and a
177+
// config.php that a deploy erased. create_user() then failed on the
178+
// duplicate email, returned early, and write_config() never ran -- so the
179+
// panel stayed dead and the installer sent you round the same loop with no
180+
// way out. Writing the config first means a problem creating an account is
181+
// a problem creating an account, not a panel that will not start.
182+
if (!write_config($config)) {
183+
return ['The database details work, but config.php could not be written to '
184+
. h(dirname(install_config_path())) . '. Create it there by hand '
185+
. 'from config.sample.php, using the details above.'];
186+
}
187+
188+
// An account already in this database is the signal that the database is
189+
// not new -- so this is a reconnection, not an installation, and making a
190+
// second Super Admin would be wrong.
191+
$existingUsers = 0;
192+
try {
193+
$existingUsers = (int) (fetch_one('SELECT COUNT(*) AS n FROM users')['n'] ?? 0);
194+
} catch (Throwable $e) {
195+
// A fresh database that has only just been migrated; treat it as empty.
196+
}
197+
198+
if ($existingUsers > 0) {
199+
$GLOBALS['__applied'] = $applied;
200+
$GLOBALS['__reconnect'] = true;
201+
return [];
202+
}
203+
173204
try {
174205
require_once __DIR__ . '/src/audit.php';
175206
require_once __DIR__ . '/src/auth.php';
176207
create_user($form['admin_name'], $form['admin_email'], $password, 'super_admin');
177208
} catch (Throwable $e) {
178-
return ['Creating your account failed: ' . $e->getMessage()];
179-
}
180-
181-
if (!write_config($config)) {
182-
return ['Everything else worked, but config.php could not be written. '
183-
. 'Create it by hand from config.sample.php, using the details above.'];
209+
return ['The settings were saved, but creating your account failed: '
210+
. $e->getMessage() . ' If you already have an account in this database, '
211+
. 'reload this page -- the panel is configured now and should let you '
212+
. 'sign in with it.'];
184213
}
185214

186215
$GLOBALS['__applied'] = $applied;
@@ -306,13 +335,19 @@ function page(string $title, string $body): void
306335
// ---------------------------------------------------------------------------
307336
if ($done) {
308337
$applied = $GLOBALS['__applied'] ?? [];
309-
page('Ready', '
310-
<p class="install-note">The panel is set up. Sign in with the account you
311-
just made.</p>
338+
page('Ready',
339+
'<p class="install-note">The panel is set up. '
340+
. (empty($GLOBALS['__reconnect'])
341+
? 'Sign in with the account you just made.'
342+
: 'Your existing data and accounts were already there and were left alone.')
343+
. '</p>
312344
<ul class="install-checks">'
313345
. ($applied === [] ? '' : '<li class="ok">Tables created (' . h(implode(', ', $applied)) . ')</li>')
314-
. '<li class="ok">Your Super Admin account was created</li>
315-
<li class="ok">config.php was written to <code>'
346+
. (empty($GLOBALS['__reconnect'])
347+
? '<li class="ok">Your Super Admin account was created</li>'
348+
: '<li class="ok">This database already had accounts in it, so no new one was
349+
made &mdash; sign in with the one you already use</li>')
350+
. '<li class="ok">config.php was written to <code>'
316351
. h((string) ($GLOBALS['__config_written_to'] ?? 'the panel folder')) . '</code></li>'
317352
. (str_contains((string) ($GLOBALS['__config_written_to'] ?? ''), 'nitesha-config')
318353
? ''

0 commit comments

Comments
 (0)