Fix: Mailbox Stats über Dovecot mit config/mailpool.php

main v1.0.77
boban 2025-10-28 18:53:11 +01:00
parent 703843a9c2
commit 2423efb212
3 changed files with 310 additions and 190 deletions

View File

@ -26,9 +26,9 @@ class StorageProbe extends Command
$data['free_gb'], $data['free_gb'],
$data['free_plus_reserve_gb'], $data['free_plus_reserve_gb'],
$data['percent_used_total'], $data['percent_used_total'],
$data['breakdown']['system_gb'], $data['breakdown_bytes']['system'],
$data['breakdown']['mails_gb'], $data['breakdown_bytes']['mails'],
$data['breakdown']['backup_gb'], $data['breakdown_bytes']['backup'],
)); ));
return self::SUCCESS; return self::SUCCESS;
@ -64,21 +64,40 @@ class StorageProbe extends Command
$percentUsed = $totalGb > 0 ? (int)round($usedGb * 100 / $totalGb) : 0; $percentUsed = $totalGb > 0 ? (int)round($usedGb * 100 / $totalGb) : 0;
// --- du: reale Verbräuche bestimmter Bäume (KB) ----------------------- // --- du: reale Verbräuche bestimmter Bäume (KB) -----------------------
$duKb = function (string $path): int { // $duKb = function (string $path): int {
// if (!is_dir($path)) return 0;
// $kb = (int)trim((string)@shell_exec('LC_ALL=C du -sk --apparent-size ' . escapeshellarg($path) . ' 2>/dev/null | cut -f1'));
// return max(0, $kb);
// };
//
// $kbMails = $duKb('/var/mail/vhosts');
// $kbBackup = $duKb('/var/backups/mailwolt');
//
// // „System“ = alles übrige, was nicht Mails/Backups ist (OS, App, Logs, DB-Daten, …)
// $gbMails = $toGiB_f($kbMails);
// $gbBackup = $toGiB_f($kbBackup);
//
// // system_gb aus „usedGb (mails+backup)“, nie negativ
// $gbSystem = max(0, round($usedGb - ($gbMails + $gbBackup), 1));
$duBytes = function (string $path): int {
if (!is_dir($path)) return 0; if (!is_dir($path)) return 0;
$kb = (int)trim((string)@shell_exec('LC_ALL=C du -sk --apparent-size ' . escapeshellarg($path) . ' 2>/dev/null | cut -f1')); $b = (int) trim((string) @shell_exec(
return max(0, $kb); 'LC_ALL=C du -sb --apparent-size ' . escapeshellarg($path) . ' 2>/dev/null | cut -f1'
));
return max(0, $b);
}; };
$kbMails = $duKb('/var/mail/vhosts'); $bytesMails = $duBytes('/var/mail/vhosts');
$kbBackup = $duKb('/var/backups/mailwolt'); $bytesBackup = $duBytes('/var/backups/mailwolt');
// „System“ = alles übrige, was nicht Mails/Backups ist (OS, App, Logs, DB-Daten, …) // used_gb in Bytes nachrechnen (aus df)
$gbMails = $toGiB_f($kbMails); $totalBytes = (int) $totalKb * 1024;
$gbBackup = $toGiB_f($kbBackup); $freeBytes = (int) $availKb * 1024;
$usedBytes = max(0, $totalBytes - $freeBytes);
// system_gb aus „usedGb (mails+backup)“, nie negativ // „System“ = Rest
$gbSystem = max(0, round($usedGb - ($gbMails + $gbBackup), 1)); $bytesSystem = max(0, $usedBytes - ($bytesMails + $bytesBackup));
return [ return [
'device' => $device ?: 'unknown', 'device' => $device ?: 'unknown',
@ -93,10 +112,10 @@ class StorageProbe extends Command
'percent_used_total' => $percentUsed, 'percent_used_total' => $percentUsed,
// Reale Breakdown-Werte // Reale Breakdown-Werte
'breakdown' => [ 'breakdown_bytes' => [
'system_gb' => $gbSystem, 'system' => $bytesSystem,
'mails_gb' => $gbMails, 'mails' => $bytesMails,
'backup_gb' => $gbBackup, 'backup' => $bytesBackup,
], ],
]; ];
} }

View File

@ -11,22 +11,25 @@ class StorageCard extends Component
{ {
public string $target = '/'; public string $target = '/';
// Summen // Summen für die Zahlenanzeige (GB)
public ?int $diskTotalGb = null; public ?int $diskTotalGb = null;
public ?int $diskUsedGb = null; public ?int $diskUsedGb = null;
public ?int $diskFreeGb = null; public ?int $diskFreeGb = null;
// Donut // Donut
public array $diskSegments = []; public array $diskSegments = []; // [{angle,class}]
public int $diskSegOuterRadius = 92; public int $diskSegOuterRadius = 92;
public int $diskInnerSize = 160; public int $diskInnerSize = 160;
public array $diskCenterText = ['percent' => '', 'label' => 'SPEICHER BELEGT']; public array $diskCenterText = ['percent' => '', 'label' => 'SPEICHER BELEGT'];
// Stacked-Bar + Legende // Legende/Bar (GENAU wie dein Blade es erwartet)
public array $barSegments = []; // [{label,color,gb,percent}] // [{label,color,gb,percent}]
public array $barSegments = [];
public ?string $measuredAt = null; public ?string $measuredAt = null;
protected int $segCount = 100; protected int $segCount = 100; // Donut-Segmente
private int $legendMinBytes = 1_048_576; // 1 MiB darunter blenden wir aus
public function mount(string $target = '/'): void public function mount(string $target = '/'): void
{ {
@ -45,118 +48,87 @@ class StorageCard extends Component
$this->loadFromSettings(); $this->loadFromSettings();
} }
// protected function loadFromSettings(): void // ─────────────────────────────────────────────────────────────
// {
// $disk = Setting::get('health.disk', []);
// if (!is_array($disk) || empty($disk)) {
// $this->resetUi();
// return;
// }
//
// $this->diskTotalGb = self::intOrNull($disk['total_gb'] ?? null);
// $this->diskUsedGb = self::intOrNull($disk['used_gb'] ?? null);
// $this->diskFreeGb = self::intOrNull($disk['free_gb'] ?? ($disk['free_plus_reserve_gb'] ?? null));
//
// $percent = $disk['percent_used_total'] ?? null;
// $this->diskCenterText = [
// 'percent' => is_numeric($percent) ? (string)round($percent) . '%' : '',
// 'label' => 'SPEICHER BELEGT',
// ];
// $this->diskSegments = $this->buildSegments(is_numeric($percent) ? (int)$percent : null);
//
// $this->barSegments = $this->buildBar($disk);
// $this->measuredAt = Setting::get('health.disk_updated_at', null);
// }
protected function loadFromSettings(): void protected function loadFromSettings(): void
{ {
$disk = Setting::get('health.disk', []); $disk = Setting::get('health.disk', []);
if (!is_array($disk) || empty($disk)) { $this->resetUi(); return; } if (!is_array($disk) || empty($disk)) {
$this->resetUi();
return;
}
// Summen (GB) so wie dein Blade sie zeigt
$this->diskTotalGb = self::intOrNull($disk['total_gb'] ?? null); $this->diskTotalGb = self::intOrNull($disk['total_gb'] ?? null);
$this->diskUsedGb = self::intOrNull($disk['used_gb'] ?? null); $this->diskUsedGb = self::intOrNull($disk['used_gb'] ?? null);
$this->diskFreeGb = self::intOrNull($disk['free_gb'] ?? ($disk['free_plus_reserve_gb'] ?? null)); $this->diskFreeGb = self::intOrNull($disk['free_gb'] ?? ($disk['free_plus_reserve_gb'] ?? null));
$percent = $disk['percent_used_total'] ?? null; $percent = $disk['percent_used_total'] ?? null;
$this->diskCenterText = [ $this->diskCenterText = [
'percent' => is_numeric($percent) ? (string)round($percent).'%' : '', 'percent' => is_numeric($percent) ? (string)round($percent) . '%' : '',
'label' => 'SPEICHER BELEGT', 'label' => 'SPEICHER BELEGT',
]; ];
// Donut farbig aus Breakdown // Breakdown in BYTES (kommt aus StorageProbe)
$this->diskSegments = $this->buildDonutSegmentsFromBreakdown($disk); $bdBytes = $disk['breakdown_bytes'] ?? ['system' => 0, 'mails' => 0, 'backup' => 0];
// Legende unten (gleiche Farben wie Donut) // Total/Free in BYTES ableiten
$total = max(1, (float)($disk['total_gb'] ?? 1)); $totalBytes = (int)round(((int)($disk['total_gb'] ?? 0)) * 1024 ** 3);
$bd = $disk['breakdown'] ?? []; $freeGb = $disk['free_gb'] ?? ($disk['free_plus_reserve_gb'] ?? 0);
$sys = (float)($bd['system_gb'] ?? 0); $freeBytes = (int)round(((float)$freeGb) * 1024 ** 3);
$mails = (float)($bd['mails_gb'] ?? 0);
$backup = (float)($bd['backup_gb'] ?? 0);
$free = (float)($disk['free_gb'] ?? ($disk['free_plus_reserve_gb'] ?? 0));
$p = fn(float $gb) => max(0, min(100, round($gb * 100 / $total)));
$this->barSegments = [ // reportetes Used (Bytes)
['label' => 'System', 'gb' => round($sys,1), 'percent' => $p($sys), 'color' => 'bg-emerald-400'], $usedReportedBytes = max(0, $totalBytes - $freeBytes);
['label' => 'Mails', 'gb' => round($mails,1), 'percent' => $p($mails), 'color' => 'bg-rose-400'],
['label' => 'Backups', 'gb' => round($backup,1),'percent' => $p($backup), 'color' => 'bg-sky-400'],
['label' => 'Frei', 'gb' => round($free,1), 'percent' => $p($free), 'color' => 'bg-white/20'],
];
$this->barSegments = array_values(array_filter( // Summe Breakdown angleichen (Rest → System), damit Donut/Prozente stimmig sind
$this->barSegments, $sumUsedBreakdown = max(0, (int)$bdBytes['system'] + (int)$bdBytes['mails'] + (int)$bdBytes['backup']);
fn($b) => ($b['gb'] ?? 0) > 0 // nur Einträge mit realer Größe if ($usedReportedBytes > $sumUsedBreakdown) {
)); $bdBytes['system'] += ($usedReportedBytes - $sumUsedBreakdown);
}
// Donut färben
$this->diskSegments = $this->buildDonutSegmentsFromBytes($bdBytes, $totalBytes, $freeBytes);
// Legende/Bar (GB + Prozent; KEIN „text“-Feld)
$this->barSegments = $this->buildLegendGbFromBytes($bdBytes, $totalBytes, $freeBytes);
$this->measuredAt = Setting::get('health.disk_updated_at', null); $this->measuredAt = Setting::get('health.disk_updated_at', null);
} }
// NEU: ersetzt buildSegments() + nutzt Breakdown protected function buildDonutSegmentsFromBytes(array $bdBytes, int $totalBytes, int $freeBytes): array
protected function buildDonutSegmentsFromBreakdown(array $disk): array
{ {
$total = (float)($disk['total_gb'] ?? 0); if ($totalBytes <= 0) return [];
if ($total <= 0) return [];
// Breakdown lesen $order = [
$bd = $disk['breakdown'] ?? []; ['key' => 'system', 'class' => 'bg-emerald-400'],
$sys = (float)($bd['system_gb'] ?? 0); ['key' => 'mails', 'class' => 'bg-rose-400'],
$mails = (float)($bd['mails_gb'] ?? 0); ['key' => 'backup', 'class' => 'bg-sky-400'],
$backup = (float)($bd['backup_gb'] ?? 0); ];
$free = (float)($disk['free_gb'] ?? ($disk['free_plus_reserve_gb'] ?? 0));
// Robust machen: falls used aus Settings größer ist als Breakdown-Summe → auf System draufschlagen $counts = [];
$usedReported = (float)($disk['used_gb'] ?? ($total - $free)); foreach ($order as $d) {
$sumUsedBd = $sys + $mails + $backup; $bytes = max(0, (int)($bdBytes[$d['key']] ?? 0));
if ($usedReported > $sumUsedBd && $usedReported <= $total) { $cnt = (int)round($this->segCount * ($bytes / $totalBytes));
$sys += ($usedReported - $sumUsedBd); if ($bytes > 0 && $cnt === 0) $cnt = 1; // min. 1 Segment, wenn vorhanden
$counts[$d['key']] = $cnt;
} }
// Grenzen
$sys = max(0, min($sys, $total));
$mails = max(0, min($mails, $total));
$backup = max(0, min($backup, $total));
$free = max(0, min($free, $total));
// Segmente verteilen $usedCount = array_sum($counts);
$mkCount = function (float $gb) use ($total) { $freeCount = max(0, $this->segCount - $usedCount);
return (int) round($this->segCount * $gb / $total);
};
$segments = []; $segments = [];
$add = function (int $count, string $class) use (&$segments) { foreach ($order as $d) {
for ($i = 0; $i < $count; $i++) { for ($i = 0; $i < $counts[$d['key']]; $i++) {
$segments[] = ['class' => $class]; $segments[] = ['class' => $d['class']];
}
}
for ($i = 0; $i < $freeCount; $i++) {
$segments[] = ['class' => 'bg-white/15'];
} }
};
$add($mkCount($sys), 'bg-emerald-400'); // System
$add($mkCount($mails), 'bg-rose-400'); // Mails
$add($mkCount($backup), 'bg-sky-400'); // Backups
// Rest = Frei (grau)
while (count($segments) < $this->segCount) { while (count($segments) < $this->segCount) {
$segments[] = ['class' => 'bg-white/15']; $segments[] = ['class' => 'bg-white/15'];
} }
// Winkel setzen (gleich wie vorher)
$out = []; $out = [];
for ($i = 0; $i < $this->segCount; $i++) { for ($i = 0; $i < $this->segCount; $i++) {
$angle = (360 / $this->segCount) * $i - 90; $angle = (360 / $this->segCount) * $i - 90;
@ -165,67 +137,41 @@ class StorageCard extends Component
return $out; return $out;
} }
// protected function buildSegments(?int $percent): array protected function buildLegendGbFromBytes(array $bdBytes, int $totalBytes, int $freeBytes): array
// {
// $segments = [];
// $active = is_int($percent) ? (int)round($this->segCount * $percent / 100) : 0;
//
// $activeClass = match (true) {
// !is_int($percent) => 'bg-white/15',
// $percent >= 90 => 'bg-rose-400',
// $percent >= 70 => 'bg-amber-300',
// default => 'bg-emerald-400',
// };
//
// for ($i = 0; $i < $this->segCount; $i++) {
// $angle = (360 / $this->segCount) * $i - 90;
// $segments[] = [
// 'angle' => $angle,
// 'class' => $i < $active ? $activeClass : 'bg-white/15'
// ];
// }
// return $segments;
// }
protected function buildBar(array $disk): array
{ {
$total = (float)($disk['total_gb'] ?? 0); $defs = [
if ($total <= 0) return []; ['key' => 'system', 'label' => 'System', 'class' => 'bg-emerald-400'],
['key' => 'mails', 'label' => 'Mails', 'class' => 'bg-rose-400'],
['key' => 'backup', 'label' => 'Backups', 'class' => 'bg-sky-400'],
];
// Breakdown lesen + normalisieren $toPercent = function (int $bytes) use ($totalBytes): int {
[$sys, $mails, $backups] = $this->readBreakdown($disk); if ($totalBytes <= 0) return 0;
return (int)max(0, min(100, round($bytes * 100 / $totalBytes)));
};
$toGb = fn(int $bytes) => round($bytes / (1024 ** 3), 1);
$free = max(0.0, (float)($disk['free_gb'] ?? ($disk['free_plus_reserve_gb'] ?? 0))); $out = [];
$used = min($total, $sys + $mails + $backups); // robust bei Messrauschen foreach ($defs as $d) {
$val = max(0, (int)($bdBytes[$d['key']] ?? 0));
// Falls Breakdown kleiner ist als used_gb: Rest als “System” draufschlagen, if ($val < $this->legendMinBytes) continue; // zu klein → nicht anzeigen
// damit die Prozent-Summe 100 ergibt. $out[] = [
$usedReported = (float)($disk['used_gb'] ?? ($total - $free)); 'label' => $d['label'],
if ($usedReported > 0 && $used < $usedReported) { 'color' => $d['class'],
$sys += ($usedReported - $used); 'gb' => $toGb($val), // ← genau das Feld, das dein Blade nutzt
$used = $usedReported; 'percent' => $toPercent($val),
}
// Prozent berechnen
$p = fn(float $gb) => max(0, min(100, round($gb * 100 / $total)));
return [
['label' => 'System', 'gb' => round($sys, 1), 'percent' => $p($sys), 'color' => 'bg-emerald-400'],
['label' => 'Mails', 'gb' => round($mails, 1), 'percent' => $p($mails), 'color' => 'bg-rose-400'],
['label' => 'Backups', 'gb' => round($backups, 1), 'percent' => $p($backups), 'color' => 'bg-sky-400'],
['label' => 'Frei', 'gb' => round($free, 1), 'percent' => $p($free), 'color' => 'bg-white/20'],
]; ];
} }
protected function readBreakdown(array $disk): array // „Frei“ immer anzeigen
{ $out[] = [
// Deine Keys ggf. hier mappen 'label' => 'Frei',
$bd = $disk['breakdown'] ?? []; 'color' => 'bg-white/20',
$sys = (float)($bd['system_gb'] ?? 0); 'gb' => $toGb($freeBytes),
$mails = (float)($bd['mails_gb'] ?? 0); 'percent' => $toPercent($freeBytes),
$backup = (float)($bd['backup_gb'] ?? 0); ];
return [$sys, $mails, $backup]; return $out;
} }
protected function resetUi(): void protected function resetUi(): void
@ -244,7 +190,8 @@ class StorageCard extends Component
return is_numeric($v) ? (int)round($v) : null; return is_numeric($v) ? (int)round($v) : null;
} }
} }
//
//
//namespace App\Livewire\Ui\System; //namespace App\Livewire\Ui\System;
// //
//use Livewire\Component; //use Livewire\Component;
@ -255,17 +202,24 @@ class StorageCard extends Component
//{ //{
// public string $target = '/'; // public string $target = '/';
// //
// // Summen
// public ?int $diskTotalGb = null; // public ?int $diskTotalGb = null;
// public ?int $diskUsedGb = null; // inkl. Reserve (passt zum Donut) // public ?int $diskUsedGb = null;
// public ?int $diskFreeGb = null; // wird unten auf free_plus_reserve_gb gesetzt // public ?int $diskFreeGb = null;
// //
// // Donut
// public array $diskSegments = []; // public array $diskSegments = [];
// public int $diskSegOuterRadius = 92; // public int $diskSegOuterRadius = 92;
// public int $diskInnerSize = 160; // public int $diskInnerSize = 160;
// public array $diskCenterText = ['percent' => '', 'label' => 'SPEICHER BELEGT']; // public array $diskCenterText = ['percent' => '', 'label' => 'SPEICHER BELEGT'];
//
// private int $legendMinBytes = 1 * 1024 * 1024; // 1 MiB Schwelle
//
// // Stacked-Bar + Legende
// public array $barSegments = []; // [{label,color,gb,percent}]
// public ?string $measuredAt = null; // public ?string $measuredAt = null;
// //
// protected int $segCount = 48; // protected int $segCount = 100;
// //
// public function mount(string $target = '/'): void // public function mount(string $target = '/'): void
// { // {
@ -273,7 +227,10 @@ class StorageCard extends Component
// $this->loadFromSettings(); // $this->loadFromSettings();
// } // }
// //
// public function render() { return view('livewire.ui.system.storage-card'); } // public function render()
// {
// return view('livewire.ui.system.storage-card');
// }
// //
// public function refresh(): void // public function refresh(): void
// { // {
@ -281,41 +238,187 @@ class StorageCard extends Component
// $this->loadFromSettings(); // $this->loadFromSettings();
// } // }
// //
// private function humanSize(int $bytes): string
// {
// if ($bytes >= 1024**3) return number_format($bytes / 1024**3, 1) . ' GB';
// if ($bytes >= 1024**2) return number_format($bytes / 1024**2, 2) . ' MiB';
// if ($bytes >= 1024) return number_format($bytes / 1024, 0) . ' KiB';
// return $bytes . ' B';
// }
//
// protected function loadFromSettings(): void // protected function loadFromSettings(): void
// { // {
// $disk = Setting::get('health.disk', []); // $disk = Setting::get('health.disk', []);
// if (!is_array($disk) || empty($disk)) return; // if (!is_array($disk) || empty($disk)) { $this->resetUi(); return; }
// //
// $this->diskTotalGb = $disk['total_gb'] ?? null; // $this->diskTotalGb = self::intOrNull($disk['total_gb'] ?? null);
// $this->diskUsedGb = $disk['used_gb'] ?? null; // $this->diskUsedGb = self::intOrNull($disk['used_gb'] ?? null);
// $this->diskFreeGb = $disk['free_gb'] ?? ($disk['free_plus_reserve_gb'] ?? null); // $this->diskFreeGb = self::intOrNull($disk['free_gb'] ?? ($disk['free_plus_reserve_gb'] ?? null));
// //
// $percent = $disk['percent_used_total'] ?? null; // $percent = $disk['percent_used_total'] ?? null;
// $this->diskCenterText = [ // $this->diskCenterText = [
// 'percent' => is_numeric($percent) ? $percent.'%' : '', // 'percent' => is_numeric($percent) ? (string)round($percent).'%' : '',
// 'label' => 'SPEICHER BELEGT', // 'label' => 'SPEICHER BELEGT',
// ]; // ];
// $this->diskSegments = $this->buildSegments($percent); //
// // Donut farbig aus Breakdown
// $this->diskSegments = $this->buildDonutSegmentsFromBreakdown($disk);
//
// // Legende unten (gleiche Farben wie Donut)
// $total = max(1, (float)($disk['total_gb'] ?? 1));
// $bd = $disk['breakdown'] ?? [];
// $sys = (float)($bd['system_gb'] ?? 0);
// $mails = (float)($bd['mails_gb'] ?? 0);
// $backup = (float)($bd['backup_gb'] ?? 0);
// $free = (float)($disk['free_gb'] ?? ($disk['free_plus_reserve_gb'] ?? 0));
// $p = fn(float $gb) => max(0, min(100, round($gb * 100 / $total)));
//
// $this->barSegments = [
// ['label' => 'System', 'gb' => round($sys,1), 'percent' => $p($sys), 'color' => 'bg-emerald-400'],
// ['label' => 'Mails', 'gb' => round($mails,1), 'percent' => $p($mails), 'color' => 'bg-rose-400'],
// ['label' => 'Backups', 'gb' => round($backup,1),'percent' => $p($backup), 'color' => 'bg-sky-400'],
// ['label' => 'Frei', 'gb' => round($free,1), 'percent' => $p($free), 'color' => 'bg-white/20'],
// ];
//
// $this->barSegments = array_values(array_filter(
// $this->barSegments,
// fn($b) => ($b['gb'] ?? 0) > 0 // nur Einträge mit realer Größe
// ));
// //
// $this->measuredAt = Setting::get('health.disk_updated_at', null); // $this->measuredAt = Setting::get('health.disk_updated_at', null);
// } // }
// //
// protected function buildSegments(?int $percent): array // // NEU: ersetzt buildSegments() + nutzt Breakdown
// protected function buildDonutSegmentsFromBreakdown(array $disk): array
// { // {
// $segments = []; // $total = (float)($disk['total_gb'] ?? 0);
// $active = is_int($percent) ? (int) round($this->segCount * $percent / 100) : 0; // if ($total <= 0) return [];
// //
// $activeClass = match (true) { // // Breakdown lesen
// !is_int($percent) => 'bg-white/15', // $bd = $disk['breakdown'] ?? [];
// $percent >= 90 => 'bg-rose-400', // $sys = (float)($bd['system_gb'] ?? 0);
// $percent >= 70 => 'bg-amber-300', // $mails = (float)($bd['mails_gb'] ?? 0);
// default => 'bg-emerald-400', // $backup = (float)($bd['backup_gb'] ?? 0);
// $free = (float)($disk['free_gb'] ?? ($disk['free_plus_reserve_gb'] ?? 0));
//
// // Robust machen: falls used aus Settings größer ist als Breakdown-Summe → auf System draufschlagen
// $usedReported = (float)($disk['used_gb'] ?? ($total - $free));
// $sumUsedBd = $sys + $mails + $backup;
// if ($usedReported > $sumUsedBd && $usedReported <= $total) {
// $sys += ($usedReported - $sumUsedBd);
// }
// // Grenzen
// $sys = max(0, min($sys, $total));
// $mails = max(0, min($mails, $total));
// $backup = max(0, min($backup, $total));
// $free = max(0, min($free, $total));
//
// // Segmente verteilen
// $mkCount = function (float $gb) use ($total) {
// return (int) round($this->segCount * $gb / $total);
// }; // };
// //
// $segments = [];
// $add = function (int $count, string $class) use (&$segments) {
// for ($i = 0; $i < $count; $i++) {
// $segments[] = ['class' => $class];
// }
// };
//
// $add($mkCount($sys), 'bg-emerald-400'); // System
// $add($mkCount($mails), 'bg-rose-400'); // Mails
// $add($mkCount($backup), 'bg-sky-400'); // Backups
//
// // Rest = Frei (grau)
// while (count($segments) < $this->segCount) {
// $segments[] = ['class' => 'bg-white/15'];
// }
//
// // Winkel setzen (gleich wie vorher)
// $out = [];
// for ($i = 0; $i < $this->segCount; $i++) { // for ($i = 0; $i < $this->segCount; $i++) {
// $angle = (360 / $this->segCount) * $i - 90; // $angle = (360 / $this->segCount) * $i - 90;
// $segments[] = ['angle' => $angle, 'class' => $i < $active ? $activeClass : 'bg-white/15']; // $out[] = ['angle' => $angle, 'class' => $segments[$i]['class']];
// } // }
// return $segments; // return $out;
// }
//
//// protected function buildSegments(?int $percent): array
//// {
//// $segments = [];
//// $active = is_int($percent) ? (int)round($this->segCount * $percent / 100) : 0;
////
//// $activeClass = match (true) {
//// !is_int($percent) => 'bg-white/15',
//// $percent >= 90 => 'bg-rose-400',
//// $percent >= 70 => 'bg-amber-300',
//// default => 'bg-emerald-400',
//// };
////
//// for ($i = 0; $i < $this->segCount; $i++) {
//// $angle = (360 / $this->segCount) * $i - 90;
//// $segments[] = [
//// 'angle' => $angle,
//// 'class' => $i < $active ? $activeClass : 'bg-white/15'
//// ];
//// }
//// return $segments;
//// }
//
// protected function buildBar(array $disk): array
// {
// $total = (float)($disk['total_gb'] ?? 0);
// if ($total <= 0) return [];
//
// // Breakdown lesen + normalisieren
// [$sys, $mails, $backups] = $this->readBreakdown($disk);
//
// $free = max(0.0, (float)($disk['free_gb'] ?? ($disk['free_plus_reserve_gb'] ?? 0)));
// $used = min($total, $sys + $mails + $backups); // robust bei Messrauschen
//
// // Falls Breakdown kleiner ist als used_gb: Rest als “System” draufschlagen,
// // damit die Prozent-Summe 100 ergibt.
// $usedReported = (float)($disk['used_gb'] ?? ($total - $free));
// if ($usedReported > 0 && $used < $usedReported) {
// $sys += ($usedReported - $used);
// $used = $usedReported;
// }
//
// // Prozent berechnen
// $p = fn(float $gb) => max(0, min(100, round($gb * 100 / $total)));
//
// return [
// ['label' => 'System', 'gb' => round($sys, 1), 'percent' => $p($sys), 'color' => 'bg-emerald-400'],
// ['label' => 'Mails', 'gb' => round($mails, 1), 'percent' => $p($mails), 'color' => 'bg-rose-400'],
// ['label' => 'Backups', 'gb' => round($backups, 1), 'percent' => $p($backups), 'color' => 'bg-sky-400'],
// ['label' => 'Frei', 'gb' => round($free, 1), 'percent' => $p($free), 'color' => 'bg-white/20'],
// ];
// }
//
// protected function readBreakdown(array $disk): array
// {
// // Deine Keys ggf. hier mappen
// $bd = $disk['breakdown'] ?? [];
// $sys = (float)($bd['system_gb'] ?? 0);
// $mails = (float)($bd['mails_gb'] ?? 0);
// $backup = (float)($bd['backup_gb'] ?? 0);
//
// return [$sys, $mails, $backup];
// }
//
// protected function resetUi(): void
// {
// $this->diskTotalGb = null;
// $this->diskUsedGb = null;
// $this->diskFreeGb = null;
// $this->diskSegments = [];
// $this->barSegments = [];
// $this->diskCenterText = ['percent' => '', 'label' => 'SPEICHER BELEGT'];
// $this->measuredAt = null;
// }
//
// protected static function intOrNull($v): ?int
// {
// return is_numeric($v) ? (int)round($v) : null;
// } // }
//} //}

View File

@ -20,20 +20,18 @@
<div class="absolute inset-0 flex flex-col items-center justify-center"> <div class="absolute inset-0 flex flex-col items-center justify-center">
<div class="text-2xl md:text-3xl font-semibold leading-none tracking-tight"> <div class="text-2xl md:text-3xl font-semibold leading-none tracking-tight">
{{ $diskCenterText['percent'] }} <small class="text-sm text-white/60 font-light ">/ {{ is_numeric($diskTotalGb) ? $diskTotalGb.' GB' : '' }}</small> {{ $diskCenterText['percent'] }}
{{-- <small class="text-sm text-white/60 font-light ">/ {{ is_numeric($diskTotalGb) ? $diskTotalGb.' GB' : '' }}</small>--}}
</div> </div>
<div class="text-[10px] md:text-[11px] text-white/60 mt-1 uppercase tracking-wide"> <div class="text-[10px] md:text-[11px] text-white/60 mt-1 uppercase tracking-wide">
{{ $diskCenterText['label'] }} {{ $diskCenterText['label'] }}
</div> </div>
{{-- @if($measuredAt)--}} {{-- @if($measuredAt)--}}
{{-- <div class="absolute bottom-12 mt-2 text-[10px] text-white/45 text-center">--}} <div class="#absolute bottom-14 mt-2 text-[10px] text-white/45 text-center inline-flex items-center gap-0.5 rounded-full bg-white/5 border border-white/10 px-2.5 py-1">
{{-- <dd class="font-medium tabular-nums text-[10px] md:text-[11px]">Gesamt</dd>--}} <dd class="font-medium tabular-nums text-[10px] md:text-[11px]">Gesamt</dd>
{{-- <dd class="font-medium tabular-nums text-[10px] md:text-[11px]">{{ is_numeric($diskTotalGb) ? $diskTotalGb.' GB' : '' }}</dd>--}} <dd class="font-medium tabular-nums text-[10px] md:text-[11px]">{{ is_numeric($diskTotalGb) ? $diskTotalGb.' GB' : '' }}</dd>
</div>
{{-- --}}{{----}}
{{-- zuletzt aktualisiert:<br>{{ \Carbon\Carbon::parse($measuredAt)->diffForHumans() }}--}}
{{-- </div>--}}
{{-- @endif--}} {{-- @endif--}}
</div> </div>