db = Database::getInstance(); $this->logger = Logger::getInstance(); $this->basePath = dirname(__DIR__, 2); } public function install(int $serverId): array { $server = $this->db->fetch( 'SELECT * FROM servers WHERE id = ? AND status = \'active\' AND is_active = 1', [$serverId] ); if (!$server) { return ['success' => false, 'error' => 'Server not found or inactive']; } $server = App::getEncryption()->decryptArray($server, ['ssh_password', 'ssh_key']); $agentKey = $server['agent_key']; $serverUrl = rtrim((App::getConfig()['app']['url'] ?? ''), '/'); if (empty($serverUrl)) { $serverUrl = ($_SERVER['HTTPS'] ?? '' === 'on' ? 'https://' : 'http://') . ($_SERVER['HTTP_HOST'] ?? 'localhost'); } $scriptPath = $this->basePath . '/install/agent-install.sh'; if (!file_exists($scriptPath)) { return ['success' => false, 'error' => 'Agent install script not found']; } $script = file_get_contents($scriptPath); $b64 = base64_encode($script); $remoteCommand = sprintf( 'echo %s | base64 -d > /tmp/_sm_install.sh 2>/dev/null && chmod +x /tmp/_sm_install.sh && bash /tmp/_sm_install.sh %s %s 2>&1; rc=$?; rm -f /tmp/_sm_install.sh 2>/dev/null; exit $rc', escapeshellarg($b64), escapeshellarg($serverUrl), escapeshellarg($agentKey) ); try { $ssh = new SSHService(); if (!$ssh->connect($server, true)) { return ['success' => false, 'error' => 'SSH connection failed']; } $result = $ssh->exec($remoteCommand, 120); $ssh->disconnect(); $output = trim($result['output']); $exitStatus = $result['exit_status'] ?? -1; if ($exitStatus !== 0) { $lastLines = implode("\n", array_slice(explode("\n", $output), -20)); return [ 'success' => false, 'error' => 'Agent installation failed on remote server', 'output' => $lastLines, ]; } $path = '/usr/local/bin/servermanager-agent'; if (preg_match('/Binary:\s*(\S+)/', $output, $m)) { $path = $m[1]; } $serverModel = new \ServerManager\Models\Server(); $serverModel->setAgentInstalled($serverId, true, $path); $auditService = new AuditService(); $auditService->log('agent_installed', 'server', $serverId, [ 'name' => $server['name'], 'path' => $path, ]); return ['success' => true, 'path' => $path, 'output' => $output]; } catch (\Throwable $e) { $this->logger->error("Agent install failed for server {$serverId}: " . $e->getMessage()); return ['success' => false, 'error' => $e->getMessage()]; } } public function uninstall(int $serverId): array { $server = $this->db->fetch( 'SELECT * FROM servers WHERE id = ? AND status = \'active\' AND is_active = 1', [$serverId] ); if (!$server) { return ['success' => false, 'error' => 'Server not found or inactive']; } $server = App::getEncryption()->decryptArray($server, ['ssh_password', 'ssh_key']); $scriptPath = $this->basePath . '/install/agent-uninstall.sh'; if (!file_exists($scriptPath)) { return ['success' => false, 'error' => 'Agent uninstall script not found']; } $script = file_get_contents($scriptPath); $remoteCommand = sprintf( 'echo %s | base64 -d > /tmp/_sm_uninstall.sh 2>/dev/null && chmod +x /tmp/_sm_uninstall.sh && bash /tmp/_sm_uninstall.sh 2>&1; rc=$?; rm -f /tmp/_sm_uninstall.sh 2>/dev/null; exit $rc', escapeshellarg(base64_encode($script)) ); try { $ssh = new SSHService(); if (!$ssh->connect($server, true)) { return ['success' => false, 'error' => 'SSH connection failed']; } $result = $ssh->exec($remoteCommand, 60); $ssh->disconnect(); $output = trim($result['output']); $exitStatus = $result['exit_status'] ?? -1; $serverModel = new \ServerManager\Models\Server(); $serverModel->setAgentInstalled($serverId, false, null); $auditService = new AuditService(); $auditService->log('agent_uninstalled', 'server', $serverId, [ 'name' => $server['name'], ]); if ($exitStatus !== 0) { return [ 'success' => false, 'error' => 'Agent uninstallation may have failed on remote server', 'output' => $output, ]; } return ['success' => true, 'output' => $output]; } catch (\Throwable $e) { $this->logger->error("Agent uninstall failed for server {$serverId}: " . $e->getMessage()); $serverModel = new \ServerManager\Models\Server(); $serverModel->setAgentInstalled($serverId, false, null); return ['success' => false, 'error' => $e->getMessage()]; } } }