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…