Fix: SSL-Cert-Status robuster + Provisioning-State nach Reload erhalten
- loadSslStatus(): clearstatcache() + renewal-Conf als primärer Existenzcheck (renewal/ ist immer 755, zuverlässiger als is_dir auf live/ das 750 sein kann) + /usr/bin/openssl (voller Pfad für sudoers-Matching) - restoreSslProvisioningState(): stellt letzten Provisioning-Zustand aus State-Dateien wieder her, so dass Status nach Page-Reload sichtbar bleibt - Button zeigt nach Abschluss "Erneut einrichten" statt disabled zu bleiben Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>main v1.1.275
parent
17ed5b18d9
commit
bd7bb50a52
|
|
@ -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()); }
|
||||
|
|
|
|||
|
|
@ -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">
|
||||
<svg width="11" height="11" viewBox="0 0 14 14" fill="none"><rect x="1.5" y="6" width="11" height="7.5" rx="1.5" stroke="currentColor" stroke-width="1.2"/><path d="M4.5 6V4.5a2.5 2.5 0 0 1 5 0V6" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/></svg>
|
||||
<span wire:loading.remove wire:target="startSslProvisioning">
|
||||
{{ $sslProvisioning ? 'Läuft…' : 'Let\'s Encrypt Zertifikate einrichten' }}
|
||||
@if($sslProvisioning) Läuft…
|
||||
@elseif($sslDone) Erneut einrichten
|
||||
@else Let's Encrypt Zertifikate einrichten
|
||||
@endif
|
||||
</span>
|
||||
<span wire:loading wire:target="startSslProvisioning">Startet…</span>
|
||||
</button>
|
||||
|
|
|
|||
Loading…
Reference in New Issue