'–%','label'=>'Speicher belegt']; // // // Anzeige oben // public ?string $ramSummary = null; // public ?string $loadText = null; // public ?string $uptimeText = null; // public ?int $cpuPercent = null; // public ?int $ramPercent = null; // public ?string $updatedAtHuman = null; // // public $guardOk = []; // public function mount(): void // { // $this->loadData(); // } // // public function loadData(): void // { // $this->services = Cache::get('health:services', []); // $this->meta = Cache::get('health:meta', []); // // $this->hydrateSystem(); // $this->hydrateDisk(); // $this->hydrateUpdatedAt(); // $this->decorateServicesCompact(); // $this->decorateDisk(); // } // // public function render() // { // return view('livewire.ui.dashboard.health-card'); // } // // /* ---------------- Aufbereitung ---------------- */ // // protected function hydrateSystem(): void // { // $sys = is_array($this->meta['system'] ?? null) ? $this->meta['system'] : []; // // // RAM % // $this->ramPercent = $this->numInt($this->pick($sys, ['ram','ram_percent','mem_percent','memory'])); // if ($this->ramPercent === null && is_array($sys['ram'] ?? null)) { // $this->ramPercent = $this->numInt($this->pick($sys['ram'], ['percent','used_percent'])); // } // // // RAM Summary "used / total" // $used = $this->numInt($this->pick($sys['ram'] ?? [], ['used_gb','used','usedGB'])); // $tot = $this->numInt($this->pick($sys['ram'] ?? [], ['total_gb','total','totalGB'])); // $this->ramSummary = ($used !== null && $tot !== null) ? "{$used} GB / {$tot} GB" : null; // // // Load (1/5/15) // $load1 = $this->numFloat($this->pick($sys, ['cpu_load_1','load1','load_1'])); // $load5 = $this->numFloat($this->pick($sys, ['cpu_load_5','load5','load_5'])); // $load15 = $this->numFloat($this->pick($sys, ['cpu_load_15','load15','load_15'])); // // $loadMixed = $this->pick($sys, ['load','loadavg','load_avg','load_text']); // if (is_array($loadMixed)) { // $vals = array_values($loadMixed); // $load1 ??= $this->numFloat($vals[0] ?? null); // $load5 ??= $this->numFloat($vals[1] ?? null); // $load15 ??= $this->numFloat($vals[2] ?? null); // } // $this->loadText = $this->fmtLoad($load1, $load5, $load15, is_string($loadMixed) ? $loadMixed : null); // // // CPU % // $this->cpuPercent = $this->numInt($this->pick($sys, ['cpu','cpu_percent','cpuUsage','cpu_usage'])); // if ($this->cpuPercent === null && $load1 !== null) { // $cores = $this->numInt($this->pick($sys, ['cores','cpu_cores','num_cores'])) ?: 1; // $this->cpuPercent = (int) round(min(100, max(0, ($load1 / max(1,$cores)) * 100))); // } // // // Uptime // $this->uptimeText = $this->pick($sys, ['uptime_human','uptimeText','uptime']); // if (!$this->uptimeText) { // $uptimeSec = $this->numInt($this->pick($sys, ['uptime_seconds','uptime_sec','uptime_s'])); // if ($uptimeSec !== null) $this->uptimeText = $this->secondsToHuman($uptimeSec); // } // // // Segment-Balken // $this->cpuSeg = $this->buildSegments($this->cpuPercent); // $this->ramSeg = $this->buildSegments($this->ramPercent); // // // Load: Dots (1/5/15 relativ zu Kernen) // $cores = max(1, (int)($this->pick($sys, ['cores','cpu_cores','num_cores']) ?? 1)); // $ratio1 = $load1 !== null ? $load1 / $cores : null; // $ratio5 = $load5 !== null ? $load5 / $cores : null; // $ratio15 = $load15 !== null ? $load15 / $cores : null; // // $this->loadBadgeText = $this->loadText ?? '–'; // $this->loadDots = [ // ['label'=>'1m', 'cls'=>$this->loadDotClass($ratio1)], // ['label'=>'5m', 'cls'=>$this->loadDotClass($ratio5)], // ['label'=>'15m', 'cls'=>$this->loadDotClass($ratio15)], // ]; // // // Uptime Chips // $chips = []; // if ($this->uptimeText) { // $d = $h = $m = null; // if (preg_match('/(\d+)d/i', $this->uptimeText, $m1)) $d = (int)$m1[1]; // if (preg_match('/(\d+)h/i', $this->uptimeText, $m2)) $h = (int)$m2[1]; // if (preg_match('/(\d+)m/i', $this->uptimeText, $m3)) $m = (int)$m3[1]; // if ($d !== null) $chips[] = ['v'=>$d, 'u'=>'Tage']; // if ($h !== null) $chips[] = ['v'=>$h, 'u'=>'Stunden']; // if ($m !== null) $chips[] = ['v'=>$m, 'u'=>'Minuten']; // } // $this->uptimeChips = $chips ?: [['v'=>'–','u'=>'']]; // } // // protected function hydrateDisk(): void // { // $disk = is_array($this->meta['disk'] ?? null) ? $this->meta['disk'] : []; // // $this->diskPercent = $this->numInt($this->pick($disk, ['percent','usage'])); // $this->diskFreeGb = $this->numInt($this->pick($disk, ['free_gb','free'])); // // // total/used berechnen, falls nicht geliefert // if ($this->diskFreeGb !== null && $this->diskPercent !== null && $this->diskPercent < 100) { // $p = max(0, min(100, $this->diskPercent)) / 100; // $estTotal = (int) round($this->diskFreeGb / (1 - $p)); // $this->diskTotalGb = $estTotal > 0 ? $estTotal : null; // } // if ($this->diskTotalGb !== null && $this->diskFreeGb !== null) { // $u = $this->diskTotalGb - $this->diskFreeGb; // $this->diskUsedGb = $u >= 0 ? $u : null; // } // } // // protected function hydrateUpdatedAt(): void // { // $updated = $this->meta['updated_at'] ?? null; // try { $this->updatedAtHuman = $updated ? Carbon::parse($updated)->diffForHumans() : '–'; // } catch (\Throwable) { $this->updatedAtHuman = '–'; } // } // // protected function decorateServicesCompact(): void // { // $nameMap = [ // 'postfix' => ['label' => 'Postfix', 'hint' => 'MTA'], // 'dovecot' => ['label' => 'Dovecot', 'hint' => 'IMAP/POP3'], // 'rspamd' => ['label' => 'Rspamd', 'hint' => 'Spamfilter'], // 'db' => ['label' => 'Datenbank', 'hint' => 'MySQL/MariaDB'], // '127.0.0.1:6379' => ['label' => 'Redis', 'hint' => 'Cache/Queue'], // '127.0.0.1:8080' => ['label' => 'Reverb', 'hint' => 'WebSocket'], // ]; // //// $nameMap = [ //// // --- Mail --- //// 'postfix' => ['label' => 'Postfix', 'hint' => 'MTA / Versand'], //// 'dovecot' => ['label' => 'Dovecot', 'hint' => 'IMAP / POP3'], //// 'rspamd' => ['label' => 'Rspamd', 'hint' => 'Spamfilter'], //// 'clamav' => ['label' => 'ClamAV', 'hint' => 'Virenscanner'], //// //// // --- Daten & Cache --- //// 'db' => ['label' => 'Datenbank', 'hint' => 'MySQL / MariaDB'], //// '127.0.0.1:6379' => ['label' => 'Redis', 'hint' => 'Cache / Queue'], //// //// // --- Web / PHP --- //// 'php8.2-fpm' => ['label' => 'PHP-FPM', 'hint' => 'PHP Runtime'], //// 'nginx' => ['label' => 'Nginx', 'hint' => 'Webserver'], //// //// // --- MailWolt spezifisch --- //// 'mailwolt-queue' => ['label' => 'MailWolt Queue', 'hint' => 'Job Worker'], //// 'mailwolt-schedule'=> ['label' => 'MailWolt Schedule','hint' => 'Task Scheduler'], //// //// // --- Sonstige Infrastruktur --- //// 'fail2ban' => ['label' => 'Fail2Ban', 'hint' => 'SSH / Mail Protection'], //// 'systemd-journald'=> ['label' => 'System Logs', 'hint' => 'Logging / Journal'], //// //// // --- WebSocket & Echtzeit --- //// '127.0.0.1:8080' => ['label' => 'Reverb', 'hint' => 'WebSocket Server'], //// ]; // // $existing = collect($this->services)->keyBy('name'); // // $this->servicesCompact = collect($nameMap) // ->map(function ($meta, $key) use ($existing) { // $srv = $existing->get($key, []); // $ok = (bool)($srv['ok'] ?? false); // // return [ // 'label' => $meta['label'], // 'hint' => $meta['hint'], // 'ok' => $ok, // // // Punktfarbe // 'dotClass' => $ok ? 'bg-emerald-400' : 'bg-rose-400', // // // ✅ Bessere Status-Texte // 'pillText' => $ok ? 'Aktiv' : 'Offline', // // // Farbe für Pill // 'pillClass' => $ok // ? 'text-emerald-300 border-emerald-400/30 bg-emerald-500/10' // : 'text-rose-300 border-rose-400/30 bg-rose-500/10', // ]; // }) // ->values() // ->all(); // // $this->guardOk = collect($this->services)->every( // fn($s) => (bool)($s['ok'] ?? false) // ); //// $this->servicesCompact = collect($this->services) //// ->map(function ($srv) use ($nameMap) { //// $key = (string)($srv['name'] ?? ''); //// $ok = (bool) ($srv['ok'] ?? false); //// $label = $nameMap[$key]['label'] ?? ucfirst($key); //// $hint = $nameMap[$key]['hint'] ?? (str_contains($key, ':') ? $key : ''); //// //// var_dump($srv); //// return [ //// 'label' => $label, //// 'hint' => $hint, //// 'ok' => $ok, //// 'dotClass' => $ok ? 'bg-emerald-400' : 'bg-rose-400', //// 'pillText' => $ok ? 'Läuft' : 'Down', //// 'pillClass' => $ok //// ? 'text-emerald-300 border-emerald-400/30 bg-emerald-500/10' //// : 'text-rose-300 border-rose-400/30 bg-rose-500/10', //// ]; //// }) //// ->values() //// ->all(); // } // // protected function decorateDisk(): void // { // $percent = (int) max(0, min(100, (int)($this->diskPercent ?? 0))); // $this->diskCenterText = [ // 'percent' => is_numeric($this->diskPercent) ? $this->diskPercent.'%' : '–%', // 'label' => 'Speicher belegt', // ]; // // $count = 48; // $activeN = is_numeric($this->diskPercent) ? (int) round($count * $percent / 100) : 0; // $step = 360 / $count; // // $activeClass = match (true) { // $percent >= 90 => 'bg-rose-400', // $percent >= 70 => 'bg-amber-300', // default => 'bg-emerald-400', // }; // // $this->diskSegments = []; // for ($i = 0; $i < $count; $i++) { // $angle = ($i * $step) - 90; // Start bei 12 Uhr // $this->diskSegments[] = [ // 'angle' => $angle, // 'class' => $i < $activeN ? $activeClass : 'bg-white/16', // ]; // } // } // // /* ---------------- Helpers ---------------- */ // // protected function pick(array $arr, array $keys) // { // foreach ($keys as $k) { // if (array_key_exists($k, $arr) && $arr[$k] !== null && $arr[$k] !== '') { // return $arr[$k]; // } // } // return null; // } // // protected function numInt($v): ?int { return is_numeric($v) ? (int) round($v) : null; } // protected function numFloat($v): ?float{ return is_numeric($v) ? (float)$v : null; } // // protected function fmtLoad(?float $l1, ?float $l5, ?float $l15, ?string $fallback): ?string // { // if ($l1 !== null || $l5 !== null || $l15 !== null) { // $fmt = fn($x) => $x === null ? '–' : number_format($x, 2); // return "{$fmt($l1)} / {$fmt($l5)} / {$fmt($l15)}"; // } // return $fallback ?: null; // } // // protected function secondsToHuman(int $s): string // { // $d = intdiv($s, 86400); $s %= 86400; // $h = intdiv($s, 3600); $s %= 3600; // $m = intdiv($s, 60); // if ($d > 0) return "{$d}d {$h}h"; // if ($h > 0) return "{$h}h {$m}m"; // return "{$m}m"; // } // // protected function toneByPercent(?int $p): string { // if ($p === null) return 'white'; // if ($p >= 90) return 'rose'; // if ($p >= 70) return 'amber'; // return 'emerald'; // } // // protected function buildSegments(?int $percent): array { // $n = max(6, $this->barSegments); // $filled = is_numeric($percent) ? (int) round($n * max(0, min(100, $percent)) / 100) : 0; // $tone = $this->toneByPercent($percent); // $fillCls = match($tone) { // 'rose' => 'bg-rose-500/80', // 'amber' => 'bg-amber-400/80', // 'emerald'=> 'bg-emerald-500/80', // default => 'bg-white/20', // }; // return array_map(fn($i) => $i < $filled ? $fillCls : 'bg-white/10', range(0, $n-1)); // } // // protected function loadDotClass(?float $ratio): string { // if ($ratio === null) return 'bg-white/25'; // if ($ratio >= 0.9) return 'bg-rose-500'; // if ($ratio >= 0.7) return 'bg-amber-400'; // return 'bg-emerald-400'; // } //}