feat(install): fixed default admin e-mail admin@clusev.local

Default the first-admin login e-mail to a predictable admin@clusev.local
when no --email / CLUSEV_ADMIN_EMAIL is given, instead of a host-derived
admin@<host> address the operator would have to guess. Combined with the
default password 'clusev' (forced change on first login), a fresh install
now has a fully predictable initial login. Override at install time or
change it later under Settings -> Profile.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
feat/v1-foundation
boban 2026-06-19 16:54:03 +02:00
parent 9215a5b908
commit b1fb0e766a
4 changed files with 29 additions and 10 deletions

View File

@ -44,8 +44,9 @@ The installer is idempotent (safe to re-run) and, in one pass:
1. Installs **Docker** (Debian/Ubuntu, from Docker's official repository) if it isn't already present.
2. Creates a dedicated **`clusev`** system user — in the `docker` group, owning and running the stack —
with a random password.
3. Asks for a **domain** (leave empty for IP access) and an **admin e-mail**, or reads `CLUSEV_DOMAIN`
/ `CLUSEV_ADMIN_EMAIL` from the environment for an unattended install. If you give a domain it
3. Asks for a **domain** (leave empty for IP access) and an **admin e-mail** (leave empty for the
default **`admin@clusev.local`**), or reads `CLUSEV_DOMAIN` / `CLUSEV_ADMIN_EMAIL` from the
environment for an unattended install. If you give a domain it
checks whether DNS already points here: if so a certificate is obtained automatically; if not, it
warns you and lets you take the domain anyway (HTTP until DNS is correct) or continue on the IP.
4. Generates all secrets (once — never regenerated on re-run), builds the image, starts the stack,
@ -54,12 +55,13 @@ The installer is idempotent (safe to re-run) and, in one pass:
5. Installs a themed host login banner (MOTD) showing the dashboard address and live stack status.
When it finishes, the terminal prints a single summary: the **dashboard URL**, the **admin login**
(your e-mail + the default password **`clusev`**), and the **`clusev` host user** + its random
password (shown **only once** — note it down).
(your e-mail `admin@clusev.local` if you left it empty — plus the default password **`clusev`**),
and the **`clusev` host user** + its random password (shown **only once** — note it down).
Log in with your admin e-mail and the password **`clusev`** — the panel then **forces you to set a
new password immediately**. Do this right away: `clusev` is a known default, so the account is only
safe once you've changed it. 2FA is **optional but recommended** — enable TOTP and/or a security key
Log in with your admin e-mail (default **`admin@clusev.local`**) and the password **`clusev`** — the
panel then **forces you to set a new password immediately**. Do this right away: `clusev` is a known
default, so the account is only safe once you've changed it. You can change the login e-mail later
under **Settings → Profile**. 2FA is **optional but recommended** — enable TOTP and/or a security key
from **Settings → Security** whenever you like.
## Access, domain & TLS

View File

@ -20,6 +20,14 @@ class Install extends Command
/** Standard default admin password — forced to change on first login (must_change_password). */
private const DEFAULT_PASSWORD = 'clusev';
/**
* Standard default admin e-mail (login + Let's-Encrypt contact) when none is given. A fixed,
* predictable value so a fresh, unattended install always logs in at admin@clusev.local
* not a host-derived address the operator would have to guess. Override with --email /
* CLUSEV_ADMIN_EMAIL, or change it later under Settings Profile.
*/
private const DEFAULT_EMAIL = 'admin@clusev.local';
protected $signature = "clusev:install
{--email= : Admin-Login + Let's-Encrypt-E-Mail}
{--name=Administrator : Anzeigename des Admins}";
@ -34,8 +42,7 @@ class Install extends Command
return self::SUCCESS;
}
$host = parse_url((string) config('app.url'), PHP_URL_HOST) ?: 'localhost';
$email = (string) ($this->option('email') ?: "admin@{$host}");
$email = (string) ($this->option('email') ?: self::DEFAULT_EMAIL);
if (! filter_var($email, FILTER_VALIDATE_EMAIL)) {
$this->error("Ungueltige Admin-E-Mail: {$email}");

View File

@ -117,7 +117,7 @@ if [ -t 0 ]; then
printf '%s' "HTTP-Port (Standard 80): "; read -r in_port || true
[ -n "${in_port:-}" ] && HTTP_PORT="$in_port"
fi
printf '%s' "Admin E-Mail (Login + Let's Encrypt): "; read -r in_email || true
printf '%s' "Admin E-Mail (leer = admin@clusev.local): "; read -r in_email || true
[ -n "${in_email:-}" ] && ADMIN_EMAIL="$in_email"
fi

View File

@ -26,6 +26,16 @@ class InstallCommandTest extends TestCase
$this->assertTrue((bool) $user->must_change_password, 'first login must be forced to change it');
}
public function test_uses_the_fixed_default_email_when_none_is_given(): void
{
$this->artisan('clusev:install')->assertSuccessful();
$user = User::sole();
$this->assertSame('admin@clusev.local', $user->email, 'a fresh install must default to the predictable admin@clusev.local');
$this->assertTrue(Hash::check('clusev', $user->password));
$this->assertTrue((bool) $user->must_change_password);
}
public function test_is_a_noop_when_an_admin_already_exists(): void
{
User::factory()->create();