clusev/app/Livewire/Dashboard.php

122 lines
3.8 KiB
PHP

<?php
namespace App\Livewire;
use App\Livewire\Concerns\WithFleetContext;
use App\Models\AuditEvent;
use App\Models\Server;
use App\Services\FleetService;
use Illuminate\Support\Facades\Cache;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Title;
use Livewire\Component;
use Throwable;
#[Layout('layouts.app')]
#[Title('Clusev — Dashboard')]
class Dashboard extends Component
{
use WithFleetContext;
/** Deterministic wavy series around a base — seeds the sparklines/chart. */
private function series(float $base, int $n = 40, float $amp = 9): array
{
$out = [];
for ($i = 0; $i < $n; $i++) {
$v = $base + sin($i / 3.0) * $amp + sin($i / 6.5) * ($amp * 0.55) + cos($i / 2.0) * 2;
$out[] = (int) max(2, min(98, round($v)));
}
return $out;
}
/** @param array<int,int> $series */
private function trend(array $series, string $suffix = '%'): array
{
$cur = (int) (end($series) ?: 0);
$prev = (int) ($series[max(0, count($series) - 8)] ?? $cur);
$d = $cur - $prev;
return [
'dir' => $d > 1 ? 'up' : ($d < -1 ? 'down' : 'flat'),
'text' => ($d >= 0 ? '+' : '').$d.$suffix,
];
}
/** Live metrics for the active server (briefly cached), DB values as fallback. */
private function liveMetrics(FleetService $fleet, ?Server $active): array
{
$fallback = [
'cpu' => (int) ($active?->cpu ?? 0),
'mem' => (int) ($active?->mem ?? 0),
'disk' => (int) ($active?->disk ?? 0),
'load' => 0.0,
];
if (! $active || ! $active->credential_exists) {
return $fallback;
}
try {
return Cache::remember("metrics:{$active->id}", 10, fn () => $fleet->metrics($active));
} catch (Throwable) {
return $fallback;
}
}
/** A handful of notable systemd units (failed + running first). */
private function liveServices(FleetService $fleet, ?Server $active): array
{
if (! $active || ! $active->credential_exists) {
return [];
}
try {
$svc = Cache::remember("svc:{$active->id}", 15, fn () => $fleet->systemd($active, 1)['services']);
} catch (Throwable) {
return [];
}
$rank = fn (array $s): int => match ($s['sub']) {
'failed' => 0,
'running' => 1,
default => 2,
};
usort($svc, fn ($a, $b) => [$rank($a), $a['name']] <=> [$rank($b), $b['name']]);
return array_slice($svc, 0, 8);
}
public function render(FleetService $fleet)
{
$active = $this->activeServer();
$m = $this->liveMetrics($fleet, $active);
$cpu = $m['cpu'];
$mem = $m['mem'];
$disk = $m['disk'];
$cpuSeries = $this->series(max(8, $cpu), 40, 10);
$memSeries = $this->series(max(8, $mem), 40, 7);
$diskSeries = $this->series(max(4, $disk), 40, 2);
$loadSeries = $this->series(max(8, (int) round($cpu * 0.8)), 40, 12);
return view('livewire.dashboard', [
'active' => $active,
'cpu' => $cpu,
'mem' => $mem,
'disk' => $disk,
'load' => round($m['load'], 2),
'cpuSeries' => $cpuSeries,
'memSeries' => $memSeries,
'diskSeries' => $diskSeries,
'loadSeries' => $loadSeries,
'cpuTrend' => $this->trend($cpuSeries),
'memTrend' => $this->trend($memSeries),
'diskTrend' => $this->trend($diskSeries),
'loadTrend' => $this->trend($loadSeries, ''),
'services' => $this->liveServices($fleet, $active),
'events' => AuditEvent::with('server')->latest()->limit(6)->get(),
]);
}
}