From 7d4129747a3faefb6476b2742d08ec7c2d529ba8 Mon Sep 17 00:00:00 2001 From: infosave2007 Date: Fri, 30 Jan 2026 15:14:29 +0300 Subject: [PATCH] fix: Update vpn_clients speed columns in ServerMonitoring for real-time display --- inc/ServerMonitoring.php | 206 +++++++++++++++++++++++---------------- 1 file changed, 121 insertions(+), 85 deletions(-) diff --git a/inc/ServerMonitoring.php b/inc/ServerMonitoring.php index 7ac24c6..28ac4cc 100644 --- a/inc/ServerMonitoring.php +++ b/inc/ServerMonitoring.php @@ -14,13 +14,13 @@ class ServerMonitoring { private VpnServer $server; private array $serverData; - + public function __construct(int $serverId) { $this->server = new VpnServer($serverId); $this->serverData = $this->server->getData(); } - + /** * Collect all server metrics */ @@ -35,12 +35,12 @@ class ServerMonitoring 'network_rx_mbps' => $this->getNetworkRxSpeed(), 'network_tx_mbps' => $this->getNetworkTxSpeed(), ]; - + $this->saveServerMetrics($metrics); - + return $metrics; } - + /** * Collect client traffic metrics */ @@ -48,10 +48,11 @@ class ServerMonitoring { $clients = VpnClient::listByServer($this->serverData['id']); $results = []; - + foreach ($clients as $client) { - if ($client['status'] !== 'active') continue; - + if ($client['status'] !== 'active') + continue; + $stats = $this->getClientStats($client); if ($stats) { $this->saveClientMetrics($client['id'], $stats); @@ -63,10 +64,10 @@ class ServerMonitoring ]; } } - + return $results; } - + /** * Get CPU usage percentage */ @@ -74,10 +75,10 @@ class ServerMonitoring { $cmd = "top -bn1 | grep 'Cpu(s)' | sed 's/.*, *\\([0-9.]*\\)%* id.*/\\1/' | awk '{print 100 - \$1}'"; $result = $this->execSSH($cmd); - - return $result ? (float)trim($result) : null; + + return $result ? (float) trim($result) : null; } - + /** * Get RAM used in MB */ @@ -85,10 +86,10 @@ class ServerMonitoring { $cmd = "free -m | grep Mem | awk '{print \$3}'"; $result = $this->execSSH($cmd); - - return $result ? (int)trim($result) : null; + + return $result ? (int) trim($result) : null; } - + /** * Get total RAM in MB */ @@ -96,10 +97,10 @@ class ServerMonitoring { $cmd = "free -m | grep Mem | awk '{print \$2}'"; $result = $this->execSSH($cmd); - - return $result ? (int)trim($result) : null; + + return $result ? (int) trim($result) : null; } - + /** * Get disk used in GB */ @@ -107,10 +108,10 @@ class ServerMonitoring { $cmd = "df -BG / | tail -1 | awk '{print \$3}' | sed 's/G//'"; $result = $this->execSSH($cmd); - - return $result ? (float)trim($result) : null; + + return $result ? (float) trim($result) : null; } - + /** * Get total disk in GB */ @@ -118,10 +119,10 @@ class ServerMonitoring { $cmd = "df -BG / | tail -1 | awk '{print \$2}' | sed 's/G//'"; $result = $this->execSSH($cmd); - - return $result ? (float)trim($result) : null; + + return $result ? (float) trim($result) : null; } - + /** * Get network RX speed in Mbps */ @@ -130,22 +131,24 @@ class ServerMonitoring // Get bytes received on main interface $cmd = "cat /sys/class/net/\$(ip route | grep default | awk '{print \$5}' | head -1)/statistics/rx_bytes"; $bytes1 = $this->execSSH($cmd); - - if (!$bytes1) return null; - + + if (!$bytes1) + return null; + sleep(1); // Wait 1 second - + $bytes2 = $this->execSSH($cmd); - - if (!$bytes2) return null; - + + if (!$bytes2) + return null; + // Calculate speed in Mbps - $bytesPerSec = (int)$bytes2 - (int)$bytes1; + $bytesPerSec = (int) $bytes2 - (int) $bytes1; $mbps = ($bytesPerSec * 8) / 1000000; - + return round($mbps, 2); } - + /** * Get network TX speed in Mbps */ @@ -154,40 +157,43 @@ class ServerMonitoring // Get bytes transmitted on main interface $cmd = "cat /sys/class/net/\$(ip route | grep default | awk '{print \$5}' | head -1)/statistics/tx_bytes"; $bytes1 = $this->execSSH($cmd); - - if (!$bytes1) return null; - + + if (!$bytes1) + return null; + sleep(1); // Wait 1 second - + $bytes2 = $this->execSSH($cmd); - - if (!$bytes2) return null; - + + if (!$bytes2) + return null; + // Calculate speed in Mbps - $bytesPerSec = (int)$bytes2 - (int)$bytes1; + $bytesPerSec = (int) $bytes2 - (int) $bytes1; $mbps = ($bytesPerSec * 8) / 1000000; - + return round($mbps, 2); } - + /** * Get client current stats and calculate speed */ private function getClientStats(array $client): ?array { $db = DB::conn(); - + // Get current stats from server $containerName = $this->serverData['container_name']; $publicKey = $client['public_key']; - + $cmd = "docker exec {$containerName} wg show all dump | grep '{$publicKey}' | awk '{print \$6, \$7}'"; $result = $this->execSSH($cmd); - - if (!$result) return null; - + + if (!$result) + return null; + list($bytesReceived, $bytesSent) = explode(' ', trim($result)); - + // Get previous metrics (30 seconds ago) $stmt = $db->prepare(" SELECT bytes_sent, bytes_received, collected_at @@ -198,43 +204,43 @@ class ServerMonitoring "); $stmt->execute([$client['id']]); $previous = $stmt->fetch(PDO::FETCH_ASSOC); - + $speedUp = 0; $speedDown = 0; - + if ($previous) { $timeDiff = time() - strtotime($previous['collected_at']); if ($timeDiff > 0) { // Calculate speed in Kbps - $bytesDiffSent = (int)$bytesSent - (int)$previous['bytes_sent']; - $bytesDiffReceived = (int)$bytesReceived - (int)$previous['bytes_received']; - + $bytesDiffSent = (int) $bytesSent - (int) $previous['bytes_sent']; + $bytesDiffReceived = (int) $bytesReceived - (int) $previous['bytes_received']; + $speedUp = round(($bytesDiffSent * 8) / $timeDiff / 1000, 2); $speedDown = round(($bytesDiffReceived * 8) / $timeDiff / 1000, 2); } } - + return [ - 'bytes_sent' => (int)$bytesSent, - 'bytes_received' => (int)$bytesReceived, + 'bytes_sent' => (int) $bytesSent, + 'bytes_received' => (int) $bytesReceived, 'speed_up_kbps' => $speedUp, 'speed_down_kbps' => $speedDown, ]; } - + /** * Save server metrics to database */ private function saveServerMetrics(array $metrics): void { $db = DB::conn(); - + $stmt = $db->prepare(" INSERT INTO server_metrics (server_id, cpu_percent, ram_used_mb, ram_total_mb, disk_used_gb, disk_total_gb, network_rx_mbps, network_tx_mbps) VALUES (?, ?, ?, ?, ?, ?, ?, ?) "); - + $stmt->execute([ $this->serverData['id'], $metrics['cpu_percent'], @@ -246,20 +252,20 @@ class ServerMonitoring $metrics['network_tx_mbps'], ]); } - + /** * Save client metrics to database */ private function saveClientMetrics(int $clientId, array $stats): void { $db = DB::conn(); - + $stmt = $db->prepare(" INSERT INTO client_metrics (client_id, bytes_sent, bytes_received, speed_up_kbps, speed_down_kbps) VALUES (?, ?, ?, ?, ?) "); - + $stmt->execute([ $clientId, $stats['bytes_sent'], @@ -267,24 +273,54 @@ class ServerMonitoring $stats['speed_up_kbps'], $stats['speed_down_kbps'], ]); - - // Update last_handshake in vpn_clients table + + // Update vpn_clients table with latest stats $stmt = $db->prepare(" UPDATE vpn_clients - SET last_handshake = NOW() + SET bytes_sent = ?, bytes_received = ?, speed_up = ?, speed_down = ?, current_speed = ?, last_handshake = NOW(), last_sync_at = NOW() WHERE id = ? "); - - $stmt->execute([$clientId]); + + $currentSpeed = $stats['speed_up_kbps'] + $stats['speed_down_kbps']; // Total speed in Kbps? Or bytes/s? + // Note: speed_up_kbps is in Kbps (kilobits?). + // VpnClient stores speed in Bytes/s (based on my previous edit: bytesDiff/timeDiff). + // ServerMonitoring calculates: round(($bytesDiffSent * 8) / $timeDiff / 1000, 2) -> Kbps + + // Wait! VpnClient implementation I did: + // $speedUp = (int) ($sentDiff / $timeDiff); // Bytes per second + + // ServerMonitoring implementation: + // $speedUp = round(($bytesDiffSent * 8) / $timeDiff / 1000, 2); // Kilobits per second + + // I need to be consistent. + // Frontend expects KB/s (KiloBYTES). + // VpnClient stores BYTES per second. Twig does `speed / 1024` -> KB/s. + + // So I should convert ServerMonitoring stats to Bytes/s before saving to vpn_clients. + // ServerMonitoring $stats['speed_up_kbps'] is Kbps. + // Bytes/s = Kbps * 1000 / 8. + + $speedUpBytes = (int) ($stats['speed_up_kbps'] * 1000 / 8); + $speedDownBytes = (int) ($stats['speed_down_kbps'] * 1000 / 8); + $totalSpeedBytes = $speedUpBytes + $speedDownBytes; + + $stmt->execute([ + $stats['bytes_sent'], + $stats['bytes_received'], + $speedUpBytes, + $speedDownBytes, + $totalSpeedBytes, + $clientId + ]); } - + /** * Get server metrics for last 24 hours */ public static function getServerMetrics(int $serverId, int $hours = 24): array { $db = DB::conn(); - + $stmt = $db->prepare(" SELECT * FROM server_metrics @@ -292,19 +328,19 @@ class ServerMonitoring AND collected_at >= DATE_SUB(NOW(), INTERVAL ? HOUR) ORDER BY collected_at ASC "); - + $stmt->execute([$serverId, $hours]); - + return $stmt->fetchAll(PDO::FETCH_ASSOC); } - + /** * Get client metrics for last 24 hours */ public static function getClientMetrics(int $clientId, int $hours = 24): array { $db = DB::conn(); - + $stmt = $db->prepare(" SELECT * FROM client_metrics @@ -312,26 +348,26 @@ class ServerMonitoring AND collected_at >= DATE_SUB(NOW(), INTERVAL ? HOUR) ORDER BY collected_at ASC "); - + $stmt->execute([$clientId, $hours]); - + return $stmt->fetchAll(PDO::FETCH_ASSOC); } - + /** * Clean old metrics (older than 24 hours) */ public static function cleanOldMetrics(): void { $db = DB::conn(); - + // Clean server metrics $db->exec("DELETE FROM server_metrics WHERE collected_at < DATE_SUB(NOW(), INTERVAL 24 HOUR)"); - + // Clean client metrics $db->exec("DELETE FROM client_metrics WHERE collected_at < DATE_SUB(NOW(), INTERVAL 24 HOUR)"); } - + /** * Execute SSH command on server */ @@ -341,7 +377,7 @@ class ServerMonitoring $port = $this->serverData['port']; $username = $this->serverData['username']; $password = $this->serverData['password']; - + $sshCmd = sprintf( 'sshpass -p %s ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p %d %s@%s %s 2>/dev/null', escapeshellarg($password), @@ -350,9 +386,9 @@ class ServerMonitoring escapeshellarg($host), escapeshellarg($cmd) ); - + $output = shell_exec($sshCmd); - + return $output ?: null; } }