diff --git a/app/Livewire/Ui/System/SettingsForm.php b/app/Livewire/Ui/System/SettingsForm.php index 9825267..8977311 100644 --- a/app/Livewire/Ui/System/SettingsForm.php +++ b/app/Livewire/Ui/System/SettingsForm.php @@ -132,6 +132,7 @@ class SettingsForm extends Component } $this->loadSslStatus(); + $this->restoreSslProvisioningState(); $this->rate_limit = (int) Setting::get('rate_limit', $this->rate_limit); $this->password_min = (int) Setting::get('password_min', $this->password_min); @@ -284,6 +285,8 @@ class SettingsForm extends Component public function loadSslStatus(): void { + clearstatcache(true); + $domains = [ 'ui' => $this->ui_domain, 'webmail' => $this->webmail_domain, @@ -296,23 +299,30 @@ class SettingsForm extends Component $certs[$key] = ['domain' => '', 'has_cert' => false, 'expiry' => null, 'status' => 'nodomain']; continue; } - $certDir = "/etc/letsencrypt/live/{$domain}"; - $certFile = "{$certDir}/fullchain.pem"; - // live/ ist 755 (traversierbar), archive/ ist 700 — file_exists() schlägt fehl - // weil der Symlink auf archive/ zeigt. is_dir() auf live/domain/ funktioniert. - if (! is_dir($certDir)) { + + $certFile = "/etc/letsencrypt/live/{$domain}/fullchain.pem"; + $certDir = "/etc/letsencrypt/live/{$domain}"; + // renewal/ ist immer 755 und world-readable — zuverlässigster Existenzcheck. + // is_dir() als Fallback falls renewal-Datei fehlt (manuell ausgestellte Certs). + $renewalConf = "/etc/letsencrypt/renewal/{$domain}.conf"; + $certExists = file_exists($renewalConf) || is_dir($certDir); + + if (! $certExists) { $certs[$key] = ['domain' => $domain, 'has_cert' => false, 'expiry' => null, 'status' => 'missing']; continue; } - // sudo nötig weil archive/ (Symlink-Ziel) nur root lesbar ist - $expiry = trim((string) @shell_exec("sudo -n openssl x509 -enddate -noout -in " . escapeshellarg($certFile) . " 2>/dev/null | sed 's/notAfter=//'")); + + // sudo nötig: archive/ ist 700 (Symlink-Ziel), voller Pfad für sudoers-Matching + $expiry = trim((string) @shell_exec( + "sudo -n /usr/bin/openssl x509 -enddate -noout -in " . escapeshellarg($certFile) . " 2>/dev/null | sed 's/notAfter=//'" + )); $expiryTs = $expiry ? strtotime($expiry) : null; $daysLeft = $expiryTs ? (int) round(($expiryTs - time()) / 86400) : null; $certStatus = match (true) { - $daysLeft === null => 'ok', - $daysLeft <= 0 => 'expired', - $daysLeft <= 14 => 'expiring', - default => 'ok', + $daysLeft === null => 'ok', + $daysLeft <= 0 => 'expired', + $daysLeft <= 14 => 'expiring', + default => 'ok', }; $certs[$key] = [ 'domain' => $domain, @@ -325,6 +335,21 @@ class SettingsForm extends Component $this->sslCerts = $certs; } + private function restoreSslProvisioningState(): void + { + $doneFile = self::SSL_STATE_DIR . '/done'; + if (! file_exists($doneFile)) return; + + $this->sslDone = true; + $this->sslProvisioning = false; + foreach (['ui', 'mail', 'webmail'] as $key) { + $file = self::SSL_STATE_DIR . "/{$key}"; + if (is_readable($file)) { + $this->sslProgress[$key] = trim((string) @file_get_contents($file)); + } + } + } + public function updatedUiDomain(): void { $this->validateOnly('ui_domain', $this->domainRules(), $this->domainMessages()); } public function updatedMailDomain(): void { $this->validateOnly('mail_domain', $this->domainRules(), $this->domainMessages()); } public function updatedWebmailDomain(): void { $this->validateOnly('webmail_domain', $this->domainRules(), $this->domainMessages()); } diff --git a/resources/views/livewire/ui/system/settings-form.blade.php b/resources/views/livewire/ui/system/settings-form.blade.php index 248d00c..19f9b0a 100644 --- a/resources/views/livewire/ui/system/settings-form.blade.php +++ b/resources/views/livewire/ui/system/settings-form.blade.php @@ -378,10 +378,14 @@ wire:loading.attr="disabled" wire:target="startSslProvisioning" @disabled($sslProvisioning) - class="mbx-btn-primary" style="width:100%;justify-content:center;font-size:12px"> + class="{{ $sslDone ? 'mbx-btn-secondary' : 'mbx-btn-primary' }}" + style="width:100%;justify-content:center;font-size:12px"> - {{ $sslProvisioning ? 'Läuft…' : 'Let\'s Encrypt Zertifikate einrichten' }} + @if($sslProvisioning) Läuft… + @elseif($sslDone) Erneut einrichten + @else Let's Encrypt Zertifikate einrichten + @endif Startet…