feat: add visual progress bars to resource usage and improve CPU detection

Resource Usage improvements:
- Added visual progress bars for CPU, RAM, and Disk usage
- Progress bars use Bootstrap styling (success/info/warning colors)
- Display percentage and detailed metrics (e.g., RAM 4 / 128 MB (3%))
- Bars animate smoothly with CSS transitions

CPU detection enhancements:
- Added fallback to /proc/stat parsing when shell commands unavailable
- Added fallback to PHP getrusage() for CPU time estimation
- CPU now displays even when shell_exec is disabled
- Graceful fallback chain ensures data is always available

Progress bar styling:
- Success (green) for CPU
- Info (blue) for RAM
- Warning (orange/yellow) for Disk
- All bars styled with height, rounded corners, and smooth transitions
This commit is contained in:
DevSoftTech Server 2026-08-03 16:56:50 +05:30
parent 232bf3ef8a
commit 3720da9b16
2 changed files with 111 additions and 9 deletions

View File

@ -229,18 +229,91 @@ class SystemOperations
private function readCpuUsage(): ?float
{
// Try shell command first (if available)
$result = $this->safeCommand('top -bn1 | grep "Cpu(s)" | tail -n 1');
if ($result === 'Unavailable') {
return null;
if ($result !== 'Unavailable' && trim($result) !== '') {
if (preg_match('/(\d+(?:\.\d+)?)%us/', $result, $matches)) {
return (float) $matches[1];
}
}
if (preg_match('/(\d+(?:\.\d+)?)%us/', $result, $matches)) {
return (float) $matches[1];
// Fallback: read from /proc/stat if on Linux
if (file_exists('/proc/stat')) {
return $this->readCpuUsageFromProcStat();
}
// Fallback: use PHP getrusage if available
if (function_exists('getrusage')) {
return $this->readCpuUsageFromRusage();
}
return null;
}
private function readCpuUsageFromProcStat(): ?float
{
static $lastStat = null;
static $lastTime = null;
$statFile = @file_get_contents('/proc/stat');
if ($statFile === false) {
return null;
}
$lines = explode("\n", $statFile);
$cpuLine = $lines[0];
if (! str_starts_with($cpuLine, 'cpu ')) {
return null;
}
$parts = preg_split('/\s+/', trim($cpuLine));
if (count($parts) < 5) {
return null;
}
$user = (int) $parts[1];
$nice = (int) $parts[2];
$system = (int) $parts[3];
$idle = (int) $parts[4];
$iowait = isset($parts[5]) ? (int) $parts[5] : 0;
$totalTime = microtime(true);
$total = $user + $nice + $system + $idle + $iowait;
$work = $user + $nice + $system;
if ($lastStat === null || $lastTime === null) {
$lastStat = ['total' => $total, 'work' => $work];
$lastTime = $totalTime;
return 0.0;
}
$diffTotal = $total - $lastStat['total'];
$diffWork = $work - $lastStat['work'];
$lastStat = ['total' => $total, 'work' => $work];
$lastTime = $totalTime;
if ($diffTotal <= 0) {
return 0.0;
}
return min(100.0, round(($diffWork / $diffTotal) * 100, 2));
}
private function readCpuUsageFromRusage(): ?float
{
$rusage = getrusage();
if (! is_array($rusage)) {
return null;
}
$userTime = ($rusage['ru_utime.tv_sec'] ?? 0) + (($rusage['ru_utime.tv_usec'] ?? 0) / 1000000);
$systemTime = ($rusage['ru_stime.tv_sec'] ?? 0) + (($rusage['ru_stime.tv_usec'] ?? 0) / 1000000);
$totalTime = $userTime + $systemTime;
return min(100.0, (float) ($totalTime * 100));
}
private function readMemoryUsage(): array
{
$result = $this->safeCommand('free -m');

View File

@ -57,14 +57,43 @@
<div class="card ops-card">
<div class="card-header"><h5 class="mb-0">Resource Usage</h5></div>
<div class="card-body">
<div class="mb-3">
<div class="d-flex justify-content-between small text-muted"><span>CPU</span><span><?= esc((string) ($status['cpu']['used_percent'] ?? 'N/A')) ?>%</span></div>
<div class="mb-4">
<div class="d-flex justify-content-between small text-muted mb-1"><span>CPU</span><span id="cpuPercent"><?= esc((string) ($status['cpu']['used_percent'] ?? 'N/A')) ?>%</span></div>
<div class="progress" style="height: 20px; border-radius: 4px; overflow: hidden;">
<div class="progress-bar bg-success" id="cpuBar" role="progressbar" style="width: <?= (float) ($status['cpu']['used_percent'] ?? 0) ?>%; transition: width 0.3s ease;" aria-valuenow="<?= (float) ($status['cpu']['used_percent'] ?? 0) ?>" aria-valuemin="0" aria-valuemax="100"></div>
</div>
</div>
<div class="mb-3">
<div class="d-flex justify-content-between small text-muted"><span>RAM</span><span><?= esc((string) ($status['memory']['used_mb'] ?? 'N/A')) ?> / <?= esc((string) ($status['memory']['total_mb'] ?? 'N/A')) ?> MB</span></div>
<div class="mb-4">
<div class="d-flex justify-content-between small text-muted mb-1">
<span>RAM</span>
<span id="ramPercent">
<?php
$ramTotal = (int) ($status['memory']['total_mb'] ?? 0);
$ramUsed = (int) ($status['memory']['used_mb'] ?? 0);
$ramPercent = $ramTotal > 0 ? round(($ramUsed / $ramTotal) * 100) : 0;
echo esc((string) $ramUsed) . ' / ' . esc((string) $ramTotal) . ' MB (' . $ramPercent . '%)';
?>
</span>
</div>
<div class="progress" style="height: 20px; border-radius: 4px; overflow: hidden;">
<div class="progress-bar bg-info" id="ramBar" role="progressbar" style="width: <?= $ramPercent ?>%; transition: width 0.3s ease;" aria-valuenow="<?= $ramPercent ?>" aria-valuemin="0" aria-valuemax="100"></div>
</div>
</div>
<div>
<div class="d-flex justify-content-between small text-muted"><span>Disk</span><span><?= esc((string) ($status['disk']['used_gb'] ?? 'N/A')) ?> / <?= esc((string) ($status['disk']['total_gb'] ?? 'N/A')) ?> GB</span></div>
<div class="d-flex justify-content-between small text-muted mb-1">
<span>Disk</span>
<span id="diskPercent">
<?php
$diskTotal = (float) ($status['disk']['total_gb'] ?? 0);
$diskUsed = (float) ($status['disk']['used_gb'] ?? 0);
$diskPercent = $diskTotal > 0 ? round(($diskUsed / $diskTotal) * 100) : 0;
echo esc((string) $diskUsed) . ' / ' . esc((string) $diskTotal) . ' GB (' . $diskPercent . '%)';
?>
</span>
</div>
<div class="progress" style="height: 20px; border-radius: 4px; overflow: hidden;">
<div class="progress-bar bg-warning" id="diskBar" role="progressbar" style="width: <?= $diskPercent ?>%; transition: width 0.3s ease;" aria-valuenow="<?= $diskPercent ?>" aria-valuemin="0" aria-valuemax="100"></div>
</div>
</div>
</div>
</div>