mailwolt/app/Console/Commands/StorageProbe.php

185 lines
6.8 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Models\Setting;
class StorageProbe extends Command
{
protected $signature = 'health:probe-disk {target=/}';
protected $description = 'Speichert Storage-Werte (inkl. Breakdown) in settings:health.disk';
public function handle(): int
{
$target = $this->argument('target') ?: '/';
$data = $this->probe($target);
Setting::set('health.disk', $data);
Setting::set('health.disk_updated_at', now()->toIso8601String());
$this->info(sprintf(
'Storage %s → total:%dGB used:%dGB free_user:%dGB free+5%%:%dGB (%%used:%d) breakdown: system=%.1fGB mails=%.1fGB backups=%.1fGB',
$data['mount'],
$data['total_gb'],
$data['used_gb'],
$data['free_gb'],
$data['free_plus_reserve_gb'],
$data['percent_used_total'],
$data['breakdown']['system_gb'],
$data['breakdown']['mails_gb'],
$data['breakdown']['backup_gb'],
));
return self::SUCCESS;
}
protected function probe(string $target): array
{
// --- df: Gesamtdaten des Filesystems (inkl. Reserve) -----------------
$line = trim((string)@shell_exec('LC_ALL=C df -kP ' . escapeshellarg($target) . ' 2>/dev/null | tail -n1'));
$device = $mount = '';
$totalKb = $usedKb = $availKb = 0;
if ($line !== '') {
$p = preg_split('/\s+/', $line);
if (count($p) >= 6) {
$device = $p[0];
$totalKb = (int)$p[1]; // TOTAL (inkl. Reserve)
$usedKb = (int)$p[2]; // Used
$availKb = (int)$p[3]; // Avail (User-sicht)
$mount = $p[5];
}
}
$toGiB_i = static fn($kb) => (int)round(max(0, (int)$kb) / (1024 * 1024)); // ganzzahlig (UI: Gesamt/Genutzt/Frei)
$toGiB_f = static fn($kb) => round(max(0, (int)$kb) / (1024 * 1024), 1); // eine Nachkommastelle (Breakdown/Legende)
$totalGb = $toGiB_i($totalKb);
$freeGb = $toGiB_i($availKb); // user-verfügbar
$usedGb = max(0, $totalGb - $freeGb); // belegt inkl. Reserve
$res5Gb = (int)round($totalGb * 0.05); // 5% von Gesamt
$freePlusReserveGb = min($totalGb, $freeGb + $res5Gb);
$percentUsed = $totalGb > 0 ? (int)round($usedGb * 100 / $totalGb) : 0;
// --- du: reale Verbräuche bestimmter Bäume (KB) -----------------------
$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));
return [
'device' => $device ?: 'unknown',
'mount' => $mount ?: $target,
'total_gb' => $totalGb,
'used_gb' => $usedGb, // inkl. Reserve
'free_gb' => $freeGb, // User-sicht
'reserve5_gb' => $res5Gb, // Info
'free_plus_reserve_gb' => $freePlusReserveGb, // Anzeige „Frei“
'percent_used_total' => $percentUsed,
// Reale Breakdown-Werte
'breakdown' => [
'system_gb' => $gbSystem,
'mails_gb' => $gbMails,
'backup_gb' => $gbBackup,
],
];
}
}
//
//namespace App\Console\Commands;
//
//use Illuminate\Console\Command;
//use App\Models\Setting;
//
//class StorageProbe extends Command
//{
// protected $signature = 'health:probe-disk {target=/}';
// protected $description = 'Speichert Storage-Werte (inkl. Frei+5%) in settings:health.disk';
//
// public function handle(): int
// {
// $target = $this->argument('target') ?: '/';
// $data = $this->probe($target);
//
// // Persistiert (DB + Redis) über dein Settings-Model
// Setting::set('health.disk', $data);
// Setting::set('health.disk_updated_at', now()->toIso8601String());
//
// $this->info(sprintf(
// 'Storage %s → total:%dGB used:%dGB free_user:%dGB free+5%%:%dGB (%%used:%d)',
// $data['mount'],
// $data['total_gb'],
// $data['used_gb'],
// $data['free_gb'],
// $data['free_plus_reserve_gb'],
// $data['percent_used_total'],
// ));
//
// return self::SUCCESS;
// }
//
// protected function probe(string $target): array
// {
// $line = trim((string) @shell_exec('df -kP ' . escapeshellarg($target) . ' 2>/dev/null | tail -n1'));
//
// $device = $mount = '';
// $totalKb = $usedKb = $availKb = 0;
//
// if ($line !== '') {
// $p = preg_split('/\s+/', $line);
// if (count($p) >= 6) {
// $device = $p[0];
// $totalKb = (int) $p[1]; // TOTAL (inkl. Reserve)
// $usedKb = (int) $p[2]; // Used
// $availKb = (int) $p[3]; // Avail (User-sicht)
// $mount = $p[5];
// }
// }
//
// $toGiB = static fn($kb) => (int) round(max(0, (int)$kb) / (1024*1024));
//
// $totalGb = $toGiB($totalKb);
// $freeGb = $toGiB($availKb); // user-verfügbar
// $usedGb = max(0, $totalGb - $freeGb); // belegt inkl. Reserve
// $res5Gb = (int) round($totalGb * 0.05); // 5% von Gesamt
// $freePlusReserveGb = min($totalGb, $freeGb + $res5Gb);
//
// $percentUsed = $totalGb > 0 ? (int) round($usedGb * 100 / $totalGb) : 0;
//
// return [
// 'device' => $device ?: 'unknown',
// 'mount' => $mount ?: $target,
//
// 'total_gb' => $totalGb,
// 'used_gb' => $usedGb, // inkl. Reserve
// 'free_gb' => $freeGb, // User-sicht
// 'reserve5_gb' => $res5Gb, // Info
// 'free_plus_reserve_gb' => $freePlusReserveGb, // ← das willst du anzeigen
//
// 'percent_used_total' => $percentUsed, // fürs Donut (~15%)
// 'breakdown' => [
// 'system_gb' => 5.2, // OS, App, Logs …
// 'mails_gb' => 2.8, // /var/mail/vhosts
// 'backup_gb' => 1.0, // /var/backups/mailwolt (oder wohin du sicherst)
// ],
// ];
// }
//}