import { execSync } from 'child_process'; const SSH_CONFIG: Record = { 'proxmox': { host: '192.168.178.32', user: 'root' }, 'n8n': { host: '192.168.178.129', user: 'basti' }, 'webserver': { host: '192.168.178.130', user: 'basti' } }; const SSH_KEY_PATH = '/home/basti/.ssh/id_ed25519'; function executeSSHCommand(host: string, command: string): string { try { const config = SSH_CONFIG[host]; const sshHost = config ? `${config.user}@${config.host}` : host; const fullCommand = `ssh -o StrictHostKeyChecking=no -i ${SSH_KEY_PATH} ${sshHost} "${command.replace(/"/g, '\\"')}"`; const stdout = execSync(fullCommand, { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], timeout: 10000, env: { ...process.env, HOME: '/home/basti' } }); return stdout.trim(); } catch (error: any) { console.error('SSH Command Error:', error.message); throw new Error(`SSH command failed: ${error.message}`); } } export async function getVMList() { const output = executeSSHCommand('proxmox', 'qm list'); const lines = output.split('\n').slice(1); // Skip header return lines .map(line => { const parts = line.trim().split(/\s+/); if (!parts[0] || parts[0] === '') return null; return { vmid: parts[0], name: parts[1] || 'unknown', status: parts[2] || 'unknown', mem: parts[3] || '0', bootdisk: parts[4] || '0', pid: parts[5] || null }; }) .filter((vm): vm is NonNullable => vm !== null); } export async function getVMConfig(vmid: string) { const output = executeSSHCommand('proxmox', `qm config ${vmid}`); const config: Record = {}; output.split('\n').forEach(line => { const [key, ...valueParts] = line.split(':'); if (key && valueParts.length) { config[key.trim()] = valueParts.join(':').trim(); } }); return config; } export async function getVMStatus(vmid: string) { const output = executeSSHCommand('proxmox', `qm status ${vmid}`); const match = output.match(/status:\s*(\w+)/); return match ? match[1] : 'unknown'; } export async function controlVM(vmid: string, action: 'start' | 'stop' | 'restart') { return executeSSHCommand('proxmox', `qm ${action} ${vmid}`); } // Get VM stats via SSH export async function getVMStats(vmid: string, vmName: string) { try { // Use the webserver directly since we're running on VM 200 if (vmName === 'webserver') { const cpuCmd = execSync("top -bn1 | grep 'Cpu(s)' | awk '{print 100 - $8}'", { encoding: 'utf-8' }); const memCmd = execSync("free -m | awk 'NR==2{printf \"%d/%d\", $3, $2}'", { encoding: 'utf-8' }); const diskCmd = execSync("df -h / | awk 'NR==2{print $5}'", { encoding: 'utf-8' }); const [memUsed, memTotal] = memCmd.trim().split('/').map(Number); return { cpu: Math.round(parseFloat(cpuCmd.trim()) || 0), mem: { used: memUsed, total: memTotal, percent: Math.round((memUsed / memTotal) * 100) }, disk: { percent: parseInt(diskCmd.replace('%', '').trim()) || 0 } }; } // For other VMs, use SSH const cpuCmd = executeSSHCommand(vmName, "top -bn1 | grep 'Cpu(s)' | awk '{print 100 - \\$8}'"); const memCmd = executeSSHCommand(vmName, "free -m | awk 'NR==2{printf \"%d/%d\", \\$3, \\$2}'"); const diskCmd = executeSSHCommand(vmName, "df -h / | awk 'NR==2{print \\$5}'"); const [memUsed, memTotal] = memCmd.split('/').map(Number); return { cpu: Math.round(parseFloat(cpuCmd) || 0), mem: { used: memUsed, total: memTotal, percent: Math.round((memUsed / memTotal) * 100) }, disk: { percent: parseInt(diskCmd.replace('%', '')) || 0 } }; } catch (error) { console.error('Error fetching VM stats:', error); // If VM is not running or SSH fails, return zeros return { cpu: 0, mem: { used: 0, total: 0, percent: 0 }, disk: { percent: 0 } }; } }