diff --git a/app/Console/Commands/WizardDomains.php b/app/Console/Commands/WizardDomains.php new file mode 100644 index 0000000..633aaf7 --- /dev/null +++ b/app/Console/Commands/WizardDomains.php @@ -0,0 +1,86 @@ +option('ui'); + $mail = $this->option('mail'); + $webmail = $this->option('webmail'); + $ssl = (bool)(int)$this->option('ssl'); + + @mkdir(self::STATE_DIR, 0755, true); + + // Start: alle auf pending + foreach (['ui', 'mail', 'webmail'] as $key) { + file_put_contents(self::STATE_DIR . "/{$key}", 'pending'); + } + + $domains = ['ui' => $ui, 'mail' => $mail, 'webmail' => $webmail]; + $allOk = true; + + foreach ($domains as $key => $domain) { + if (!$domain) { + file_put_contents(self::STATE_DIR . "/{$key}", 'skip'); + continue; + } + + file_put_contents(self::STATE_DIR . "/{$key}", 'running'); + + // DNS prüfen + $hasDns = checkdnsrr($domain, 'A') || checkdnsrr($domain, 'AAAA'); + if (!$hasDns) { + file_put_contents(self::STATE_DIR . "/{$key}", 'nodns'); + $allOk = false; + continue; + } + + // SSL-Zertifikat anfordern + if ($ssl) { + $out = shell_exec(sprintf( + 'sudo -n certbot certonly --nginx --non-interactive --agree-tos -m root@%s -d %s 2>&1', + escapeshellarg($domain), + escapeshellarg($domain) + )); + $certOk = str_contains((string) $out, 'Successfully') || str_contains((string) $out, 'Certificate not yet due for renewal'); + if (!$certOk) { + file_put_contents(self::STATE_DIR . "/{$key}", 'error'); + $allOk = false; + continue; + } + } + + file_put_contents(self::STATE_DIR . "/{$key}", 'done'); + } + + // Nginx neu konfigurieren (alle Domains auf einmal) + if ($allOk) { + $helper = '/usr/local/sbin/mailwolt-apply-domains'; + shell_exec(sprintf( + 'sudo -n %s --ui-host %s --webmail-host %s --mail-host %s --ssl-auto %d 2>&1', + escapeshellarg($helper), + escapeshellarg($ui), + escapeshellarg($webmail), + escapeshellarg($mail), + $ssl ? 1 : 0, + )); + } + + file_put_contents(self::STATE_DIR . '/done', $allOk ? '1' : '0'); + return self::SUCCESS; + } +} diff --git a/app/Livewire/Setup/Wizard.php b/app/Livewire/Setup/Wizard.php index 893ac09..9af8d2c 100644 --- a/app/Livewire/Setup/Wizard.php +++ b/app/Livewire/Setup/Wizard.php @@ -13,8 +13,8 @@ use Livewire\Component; #[Title('Einrichtung · Mailwolt')] class Wizard extends Component { - public int $step = 1; - public int $totalSteps = 4; + public int $step = 1; + public int $totalSteps = 5; // Schritt 1 — System public string $instance_name = 'Mailwolt'; @@ -32,13 +32,23 @@ class Wizard extends Component public string $admin_password = ''; public string $admin_password_confirmation = ''; + // Schritt 5 — Domain-Setup Status + public array $domainStatus = [ + 'ui' => 'pending', + 'mail' => 'pending', + 'webmail' => 'pending', + ]; + public bool $setupDone = false; + + private const STATE_DIR = '/var/lib/mailwolt/wizard'; + public function mount(): void { - $this->instance_name = config('app.name', 'Mailwolt'); - $this->timezone = Setting::get('timezone', 'Europe/Berlin'); - $this->locale = Setting::get('locale', 'de'); - $this->ui_domain = Setting::get('ui_domain', ''); - $this->mail_domain = Setting::get('mail_domain', ''); + $this->instance_name = config('app.name', 'Mailwolt'); + $this->timezone = Setting::get('timezone', 'Europe/Berlin'); + $this->locale = Setting::get('locale', 'de'); + $this->ui_domain = Setting::get('ui_domain', ''); + $this->mail_domain = Setting::get('mail_domain', ''); $this->webmail_domain = Setting::get('webmail_domain', ''); } @@ -82,16 +92,15 @@ class Wizard extends Component $this->step = max($this->step - 1, 1); } - public function finish(): mixed + public function finish(): void { - // Schritt-3-Validierung nochmals sicherstellen $this->validate([ 'admin_name' => 'required|string|min:2|max:64', 'admin_email' => 'required|email|max:190', 'admin_password' => 'required|string|min:10|same:admin_password_confirmation', ]); - // Settings speichern + // Settings + .env speichern Setting::setMany([ 'locale' => $this->locale, 'timezone' => $this->timezone, @@ -101,23 +110,61 @@ class Wizard extends Component 'setup_completed' => '1', ]); - // .env aktualisieren $this->writeEnv([ - 'APP_NAME' => $this->instance_name, - 'APP_HOST' => $this->ui_domain, - 'APP_URL' => 'https://' . $this->ui_domain, - 'MTA_SUB' => explode('.', $this->mail_domain)[0] ?? '', + 'APP_NAME' => $this->instance_name, + 'APP_HOST' => $this->ui_domain, + 'APP_URL' => 'https://' . $this->ui_domain, + 'MTA_SUB' => explode('.', $this->mail_domain)[0] ?? '', 'WEBMAIL_DOMAIN' => $this->webmail_domain, ]); - // Admin anlegen oder aktualisieren - $user = User::where('email', $this->admin_email)->first() ?? new User(); + // Admin anlegen + $user = User::where('email', $this->admin_email)->first() ?? new User(); $user->name = $this->admin_name; $user->email = $this->admin_email; $user->password = Hash::make($this->admin_password); $user->role = 'admin'; $user->save(); + // Status-Verzeichnis leeren und Domain-Setup im Hintergrund starten + @mkdir(self::STATE_DIR, 0755, true); + @unlink(self::STATE_DIR . '/done'); + foreach (['ui', 'mail', 'webmail'] as $k) { + file_put_contents(self::STATE_DIR . "/{$k}", 'pending'); + } + + $ssl = app()->isProduction() ? 1 : 0; + $artisan = base_path('artisan'); + $cmd = sprintf( + 'nohup php %s mailwolt:wizard-domains --ui=%s --mail=%s --webmail=%s --ssl=%d > /dev/null 2>&1 &', + escapeshellarg($artisan), + escapeshellarg($this->ui_domain), + escapeshellarg($this->mail_domain), + escapeshellarg($this->webmail_domain), + $ssl, + ); + @shell_exec($cmd); + + $this->step = 5; + } + + public function pollSetup(): void + { + foreach (['ui', 'mail', 'webmail'] as $key) { + $file = self::STATE_DIR . "/{$key}"; + $this->domainStatus[$key] = is_readable($file) + ? trim(@file_get_contents($file)) + : 'pending'; + } + + $done = @file_get_contents(self::STATE_DIR . '/done'); + if ($done !== false) { + $this->setupDone = true; + } + } + + public function goToLogin(): mixed + { return redirect()->route('login')->with('setup_done', true); } diff --git a/resources/views/livewire/setup/wizard.blade.php b/resources/views/livewire/setup/wizard.blade.php index c15308f..1f9131a 100644 --- a/resources/views/livewire/setup/wizard.blade.php +++ b/resources/views/livewire/setup/wizard.blade.php @@ -15,6 +15,7 @@ {{-- ═══ Schritt-Indikator ═══ --}} + @if($step < 5)
http:// am Anfang.