diff --git a/Setting::get('woltguard.services') b/Setting::get('woltguard.services') new file mode 100644 index 0000000..457148f --- /dev/null +++ b/Setting::get('woltguard.services') @@ -0,0 +1,9 @@ += App\Models\Setting {#6409 + id: 13, + group: "woltguard", + key: "services", + value: "{"ts":1761504019,"rows":[{"name":"postfix","ok":true},{"name":"dovecot","ok":true},{"name":"rspamd","ok":true},{"name":"clamav","ok":true},{"name":"db","ok":true},{"name":"redis","ok":true},{"name":"php-fpm","ok":true},{"name":"nginx","ok":true},{"name":"mw-queue","ok":true},{"name":"mw-schedule","ok":true},{"name":"mw-ws","ok":true},{"name":"fail2ban","ok":true},{"name":"journal","ok":true}]}", + created_at: "2025-10-26 19:40:19", + updated_at: "2025-10-26 19:40:19", + } + diff --git a/app/Jobs/RunHealthChecks.php b/app/Jobs/RunHealthChecks.php index fdc796c..1276871 100644 --- a/app/Jobs/RunHealthChecks.php +++ b/app/Jobs/RunHealthChecks.php @@ -33,13 +33,11 @@ class RunHealthChecks implements ShouldQueue } // Cache::put(CacheVer::k('health:services'), $svcRows, 60); - Cache::put(CacheVer::k('health:services'), [ - 'ts' => time(), - 'rows' => $svcRows, - ], 300); - Setting::set('woltguard.services', ['ts' => time(), 'rows' => $svcRows]); - Cache::forget('health:services'); - } + + $payload = ['ts' => time(), 'rows' => $svcRows]; + Cache::put(CacheVer::k('health:services'), $payload, 300); + Setting::set('woltguard.services', $payload); + Cache::forget('health:services'); } /** Wraps a probe; logs and returns fallback on error */ protected function safe(callable $fn, $fallback = null) diff --git a/app/Livewire/Ui/System/WoltguardCard.php b/app/Livewire/Ui/System/WoltguardCard.php index 9359be7..f66be06 100644 --- a/app/Livewire/Ui/System/WoltguardCard.php +++ b/app/Livewire/Ui/System/WoltguardCard.php @@ -48,57 +48,110 @@ class WoltguardCard extends Component protected function load(): void { - // 1) Primär: Redis + // 1) Primär: versionierter Cache-Key (mit ts/rows) $data = Cache::get(CacheVer::k('health:services')); - // 2) Fallback: Settings (DB) + // 2) Fallback: Settings (DB) wenn Cache leer/fehlend if (!is_array($data) || empty($data['rows'])) { - $data = Setting::get('woltguard.services', []); + $data = \App\Models\Setting::get('woltguard.services', []); } - // 3) Falls beides leer → letzter bekannter Zustand beibehalten + // 3) Letzter bekannter Zustand als Notanker (kein Flackern) $rows = $data['rows'] ?? []; if (empty($rows) && !empty($this->services)) { $rows = $this->services; } - // Mapping - $this->services = $rows; + // ---- Mapping in UI-Props ---- + $this->services = $rows; $this->totalCount = count($rows); - $this->okCount = collect($rows)->where('ok', true)->count(); - $this->downCount = max(0, $this->totalCount - $this->okCount); - $this->guardOk = ($this->totalCount > 0) && ($this->downCount === 0); + $this->okCount = collect($rows)->where('ok', true)->count(); + $this->downCount = max(0, $this->totalCount - $this->okCount); + $this->guardOk = ($this->totalCount > 0) && ($this->downCount === 0); $this->downServices = collect($rows) - ->filter(fn($s) => !($s['ok'] ?? false)) - ->map(fn($s) => (string)($s['name'] ?? 'unbekannt')) + ->filter(fn ($s) => !($s['ok'] ?? false)) + ->map(fn ($s) => (string)($s['name'] ?? 'unbekannt')) ->values() ->all(); - // Badges + // Badge if ($this->totalCount === 0) { - $this->badgeText = 'keine Daten'; - $this->badgeIcon = 'ph ph-warning-circle'; + $this->badgeText = 'keine Daten'; + $this->badgeIcon = 'ph ph-warning-circle'; $this->badgeClass = 'text-amber-300 border-amber-400/30 bg-amber-500/10'; return; } if ($this->guardOk) { - $this->badgeText = 'alle Dienste OK'; - $this->badgeIcon = 'ph ph-check-circle'; + $this->badgeText = 'alle Dienste OK'; + $this->badgeIcon = 'ph ph-check-circle'; $this->badgeClass = 'text-emerald-300 border-emerald-400/30 bg-emerald-500/10'; } else { if ($this->downCount >= 3) { - $this->badgeText = "{$this->downCount} Dienste down"; - $this->badgeIcon = 'ph ph-x-circle'; + $this->badgeText = "{$this->downCount} Dienste down"; + $this->badgeIcon = 'ph ph-x-circle'; $this->badgeClass = 'text-rose-300 border-rose-400/30 bg-rose-500/10'; } else { - $this->badgeText = 'Störung erkannt'; - $this->badgeIcon = 'ph ph-warning-circle'; + $this->badgeText = 'Störung erkannt'; + $this->badgeIcon = 'ph ph-warning-circle'; $this->badgeClass = 'text-amber-300 border-amber-400/30 bg-amber-500/10'; } } } +// protected function load(): void +// { +// // 1) Primär: Redis +// $data = Cache::get(CacheVer::k('health:services')); +// +// // 2) Fallback: Settings (DB) +// if (!is_array($data) || empty($data['rows'])) { +// $data = Setting::get('woltguard.services', []); +// } +// +// // 3) Falls beides leer → letzter bekannter Zustand beibehalten +// $rows = $data['rows'] ?? []; +// if (empty($rows) && !empty($this->services)) { +// $rows = $this->services; +// } +// +// // Mapping +// $this->services = $rows; +// $this->totalCount = count($rows); +// $this->okCount = collect($rows)->where('ok', true)->count(); +// $this->downCount = max(0, $this->totalCount - $this->okCount); +// $this->guardOk = ($this->totalCount > 0) && ($this->downCount === 0); +// +// $this->downServices = collect($rows) +// ->filter(fn($s) => !($s['ok'] ?? false)) +// ->map(fn($s) => (string)($s['name'] ?? 'unbekannt')) +// ->values() +// ->all(); +// +// // Badges +// if ($this->totalCount === 0) { +// $this->badgeText = 'keine Daten'; +// $this->badgeIcon = 'ph ph-warning-circle'; +// $this->badgeClass = 'text-amber-300 border-amber-400/30 bg-amber-500/10'; +// return; +// } +// +// if ($this->guardOk) { +// $this->badgeText = 'alle Dienste OK'; +// $this->badgeIcon = 'ph ph-check-circle'; +// $this->badgeClass = 'text-emerald-300 border-emerald-400/30 bg-emerald-500/10'; +// } else { +// if ($this->downCount >= 3) { +// $this->badgeText = "{$this->downCount} Dienste down"; +// $this->badgeIcon = 'ph ph-x-circle'; +// $this->badgeClass = 'text-rose-300 border-rose-400/30 bg-rose-500/10'; +// } else { +// $this->badgeText = 'Störung erkannt'; +// $this->badgeIcon = 'ph ph-warning-circle'; +// $this->badgeClass = 'text-amber-300 border-amber-400/30 bg-amber-500/10'; +// } +// } +// } } // protected function load(): void // {