db = Database::getInstance(); $this->logger = Logger::getInstance(); } public function collectMetrics(int $serverId): ?array { $server = $this->db->fetch('SELECT * FROM servers WHERE id = ? AND status = \'active\'', [$serverId]); if (!$server || !$server['is_active']) { return null; } $server = App::getEncryption()->decryptArray($server, ['ssh_password', 'ssh_key']); try { $ssh = new SSHService(); if (!$ssh->connect($server, true)) { return ['status' => 'offline']; } $metrics = [ 'server_id' => $serverId, 'status' => 'online', 'cpu_usage' => $this->getCPUUsage($ssh), 'ram_usage' => $this->getRAMUsage($ssh), 'disk_usage' => $this->getDiskUsage($ssh), 'load_average' => $this->getLoadAverage($ssh), 'uptime' => $this->getUptime($ssh), ]; $this->saveMetrics($metrics); $ssh->disconnect(); return $metrics; } catch (\Throwable $e) { $this->logger->warning("Failed to collect metrics for server {$serverId}: " . $e->getMessage()); $this->saveMetrics([ 'server_id' => $serverId, 'status' => 'offline', 'cpu_usage' => 0, 'ram_usage' => 0, 'disk_usage' => 0, 'load_average' => 0, 'uptime' => '', ]); return ['status' => 'offline']; } } public function collectAllMetrics(): array { $servers = $this->db->fetchAll('SELECT * FROM servers WHERE status = \'active\' AND is_active = 1'); $results = []; foreach ($servers as $server) { $results[$server['id']] = $this->collectMetrics($server['id']); } return $results; } private function getCPUUsage(SSHService $ssh): float { $result = $ssh->exec("top -bn1 | grep 'Cpu(s)' | awk '{print $2}' | cut -d'%' -f1"); return (float) trim($result['output']); } private function getRAMUsage(SSHService $ssh): float { $result = $ssh->exec("free | grep Mem | awk '{printf \"%.2f\", $3/$2 * 100}'"); return (float) trim($result['output']); } private function getDiskUsage(SSHService $ssh): float { $result = $ssh->exec("df / | tail -1 | awk '{print $5}' | sed 's/%//'"); return (float) trim($result['output']); } private function getLoadAverage(SSHService $ssh): float { $result = $ssh->exec("cat /proc/loadavg | awk '{print $1}'"); return (float) trim($result['output']); } private function getUptime(SSHService $ssh): string { $result = $ssh->exec("uptime -p"); return trim(str_replace('up ', '', $result['output'])); } public function saveMetrics(array $metrics): void { $now = date('Y-m-d H:i:s'); $this->db->insert('monitoring_history', [ 'server_id' => $metrics['server_id'], 'status' => $metrics['status'], 'cpu_usage' => $metrics['cpu_usage'], 'ram_usage' => $metrics['ram_usage'], 'disk_usage' => $metrics['disk_usage'], 'load_average' => $metrics['load_average'], 'uptime' => $metrics['uptime'] ?? '', 'created_at' => $now, ]); $this->db->query( "UPDATE servers SET last_check_at = ?, current_status = ?, cpu_usage = ?, ram_usage = ?, disk_usage = ?, load_average = ?, uptime = ? WHERE id = ?", [ $now, $metrics['status'], $metrics['cpu_usage'], $metrics['ram_usage'], $metrics['disk_usage'], $metrics['load_average'], $metrics['uptime'] ?? '', $metrics['server_id'], ] ); $retention = App::getConfig()['monitoring']['retention_days'] ?? 30; $this->db->query( "DELETE FROM monitoring_history WHERE created_at < DATE_SUB(NOW(), INTERVAL ? DAY)", [$retention] ); } public function getLatestMetrics(int $serverId): ?array { return $this->db->fetch( 'SELECT * FROM monitoring_history WHERE server_id = ? ORDER BY created_at DESC LIMIT 1', [$serverId] ); } public function getHistoricalMetrics(int $serverId, int $hours = 24): array { return $this->db->fetchAll( 'SELECT * FROM monitoring_history WHERE server_id = ? AND created_at >= DATE_SUB(NOW(), INTERVAL ? HOUR) ORDER BY created_at ASC', [$serverId, $hours] ); } public function getAggregatedHistoricalMetrics(array $serverIds, int $hours = 24): array { if (empty($serverIds)) { return []; } $placeholders = implode(',', array_fill(0, count($serverIds), '?')); $params = $serverIds; $params[] = $hours; return $this->db->fetchAll( "SELECT DATE_FORMAT(created_at, '%Y-%m-%d %H:%i:00') as time_bucket, AVG(cpu_usage) as avg_cpu, AVG(ram_usage) as avg_ram, AVG(disk_usage) as avg_disk FROM monitoring_history WHERE server_id IN ({$placeholders}) AND created_at >= DATE_SUB(NOW(), INTERVAL ? HOUR) GROUP BY time_bucket ORDER BY time_bucket ASC", $params ); } public function getDashboardStats(?array $serverIds = null): array { $where = ''; $params = []; if ($serverIds !== null) { if (empty($serverIds)) { $serverIds = [-1]; } $placeholders = implode(',', array_fill(0, count($serverIds), '?')); $where = " AND s.id IN ({$placeholders})"; $params = $serverIds; } $totalServers = $this->db->fetch("SELECT COUNT(*) as total FROM servers s WHERE s.status = 'active'{$where}", $params); $onlineServers = $this->db->fetch( "SELECT COUNT(*) as total FROM servers s WHERE s.current_status = 'online' AND s.status = 'active' AND s.is_active = 1{$where}", $params ); $offlineServers = $this->db->fetch( "SELECT COUNT(*) as total FROM servers s WHERE s.current_status = 'offline' AND s.status = 'active' AND s.is_active = 1{$where}", $params ); $avgCpu = $this->db->fetch( "SELECT AVG(s.cpu_usage) as avg FROM servers s WHERE s.status = 'active' AND s.is_active = 1 AND s.current_status = 'online'{$where}", $params ); $avgRam = $this->db->fetch( "SELECT AVG(s.ram_usage) as avg FROM servers s WHERE s.status = 'active' AND s.is_active = 1 AND s.current_status = 'online'{$where}", $params ); $avgDisk = $this->db->fetch( "SELECT AVG(s.disk_usage) as avg FROM servers s WHERE s.status = 'active' AND s.is_active = 1 AND s.current_status = 'online'{$where}", $params ); return [ 'total_servers' => (int) ($totalServers['total'] ?? 0), 'online_servers' => (int) ($onlineServers['total'] ?? 0), 'offline_servers' => (int) ($offlineServers['total'] ?? 0), 'avg_cpu' => round((float) ($avgCpu['avg'] ?? 0), 1), 'avg_ram' => round((float) ($avgRam['avg'] ?? 0), 1), 'avg_disk' => round((float) ($avgDisk['avg'] ?? 0), 1), ]; } }