diff --git a/src/api_pve.php b/src/api_pve.php index 4720b8e..e67051b 100644 --- a/src/api_pve.php +++ b/src/api_pve.php @@ -18,7 +18,6 @@ if ($action === 'get_stats') { $nData = getProxmoxData($node['ip_address'], $node['token_id'], $node['token_secret'], "/api2/json/nodes"); if (isset($nData['data'])) { foreach ($nData['data'] as $nInfo) { - // Duplikate (z.B. in Clustern) vermeiden if (!isset($seenNodes[$nInfo['node']])) { $seenNodes[$nInfo['node']] = true; @@ -48,7 +47,6 @@ if ($action === 'get_stats') { } } - // VMs von allen angebundenen Hosts ziehen und Duplikate filtern $vms = getProxmoxData($node['ip_address'], $node['token_id'], $node['token_secret'], "/api2/json/cluster/resources?type=vm"); if (isset($vms['data'])) { foreach($vms['data'] as $vm) { @@ -324,13 +322,14 @@ if ($action === 'restore_backup') { if ($action === 'get_node_status' || $action === 'get_vm_status') { $stmt = $pdo->prepare("SELECT * FROM nodes WHERE id = ?"); $stmt->execute([$_GET['node_id']]); $node = $stmt->fetch(PDO::FETCH_ASSOC); + $type = $node['type'] ?? 'pve'; if ($action === 'get_node_status') { - $nData = getProxmoxData($node['ip_address'], $node['token_id'], $node['token_secret'], "/api2/json/nodes"); - $internalName = $nData['data'][0]['node'] ?? 'pve'; + $nData = getProxmoxData($node['ip_address'], $node['token_id'], $node['token_secret'], "/api2/json/nodes", $type); + $internalName = $nData['data'][0]['node'] ?? ($type === 'pbs' ? 'localhost' : 'pve'); - $res = getProxmoxData($node['ip_address'], $node['token_id'], $node['token_secret'], "/api2/json/nodes/{$internalName}/status"); - $rrd = getProxmoxData($node['ip_address'], $node['token_id'], $node['token_secret'], "/api2/json/nodes/{$internalName}/rrddata?timeframe=hour&cf=AVERAGE"); + $res = getProxmoxData($node['ip_address'], $node['token_id'], $node['token_secret'], "/api2/json/nodes/{$internalName}/status", $type); + $rrd = getProxmoxData($node['ip_address'], $node['token_id'], $node['token_secret'], "/api2/json/nodes/{$internalName}/rrddata?timeframe=hour&cf=AVERAGE", $type); $netin = 0; $netout = 0; if (isset($rrd['data']) && is_array($rrd['data'])) { @@ -351,7 +350,7 @@ if ($action === 'get_node_status' || $action === 'get_vm_status') { echo json_encode(['success' => true, 'data' => $data]); exit; } else { $endpoint = "/api2/json/nodes/{$_GET['host']}/{$_GET['type']}/{$_GET['vmid']}/status/current"; - $res = getProxmoxData($node['ip_address'], $node['token_id'], $node['token_secret'], $endpoint); + $res = getProxmoxData($node['ip_address'], $node['token_id'], $node['token_secret'], $endpoint, $type); echo json_encode(['success' => true, 'data' => $res['data'] ?? []]); exit; } } @@ -364,7 +363,6 @@ if ($action === 'get_historical_rrd') { $type = $node['type'] ?? 'pve'; if ($_GET['target_mode'] === 'node') { - // Internen Node-Namen von Proxmox auflösen, da der Datenbank-Name sonst zu einem 404 Fehler führt $nData = getProxmoxData($node['ip_address'], $node['token_id'], $node['token_secret'], "/api2/json/nodes", $type); $internalName = $nData['data'][0]['node'] ?? 'localhost'; $endpoint = "/api2/json/nodes/{$internalName}/rrddata?timeframe={$timeframe}&cf=AVERAGE"; diff --git a/src/app.js b/src/app.js index 84765dc..c9e6de7 100644 --- a/src/app.js +++ b/src/app.js @@ -43,7 +43,6 @@ if (window.APP.isLoggedIn && window.APP.nodeCount > 0) { if(tab === 'pbs') fetchPbsStats(); if(tab === 'pmg') fetchPmgStats(); } - // HAUPT-GRAPHEN WIEDER HERGESTELLT const ctx = document.getElementById('liveChart')?.getContext('2d'); let liveChart; if(ctx) { liveChart = new Chart(ctx, { type: 'line', data: { labels: [], datasets: [{ label: 'CPU (%)', borderColor: '#3b82f6', backgroundColor: 'rgba(59, 130, 246, 0.1)', borderWidth: 2, tension: 0.4, fill: true, data: [] }, { label: 'RAM (%)', borderColor: '#E57000', backgroundColor: 'rgba(229, 112, 0, 0.1)', borderWidth: 2, tension: 0.4, fill: true, data: [] }] }, options: { responsive: true, maintainAspectRatio: false, animation: { duration: 500 }, scales: { x: { ticks: { color: '#9ca3af' }, grid: { color: '#33334d' } }, y: { min: 0, max: 100, ticks: { color: '#9ca3af', callback: v => v + '%' }, grid: { color: '#33334d' } } }, plugins: { legend: { labels: { color: '#e2e8f0', usePointStyle: true } } } } }); } @@ -103,7 +102,6 @@ if (window.APP.isLoggedIn && window.APP.nodeCount > 0) { async function fetchTopVms() { if(!document.getElementById('top-vms-container')) return; try { const res = await (await fetch('api.php?action=get_top_vms')).json(); if(res.success && res.data) { const container = document.getElementById('top-vms-container'); container.innerHTML = ''; if(res.data.length === 0) { container.innerHTML = '
Keine aktiven VMs.
'; return; } res.data.forEach((vm, i) => { const cpuPercent = ((vm.cpu || 0) * 100).toFixed(1); const ramUsed = formatBytes(vm.mem || 0); const icon = vm.type === 'lxc' ? '📦' : '🖥️'; const numberColor = i === 0 ? 'text-red-500' : (i === 1 ? 'text-orange-400' : (i === 2 ? 'text-yellow-400' : 'text-gray-400')); container.innerHTML += `Host: ${vm.host}
${cpuPercent}% CPU
${ramUsed} RAM
Keine aktuellen Jobs.
'; return; } res.data.forEach(job => { const jobTypeStr = job.type || job.worker_type || 'unknown'; let statusColor = 'text-gray-400', statusIcon = '⏳', statusText = job.status || 'running...'; if(statusText.toLowerCase() === 'ok') { statusColor = 'text-green-500'; statusIcon = '✅'; } else if(statusText !== 'running...') { statusColor = 'text-red-500'; statusIcon = '❌'; } else { statusColor = 'text-blue-400'; statusIcon = '🔄'; } const date = new Date(job.starttime * 1000); const timeStr = date.toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' }), dateStr = date.toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit' }); const isBackup = jobTypeStr.includes('sync') || jobTypeStr.includes('prune') || jobTypeStr.includes('garbage_collection') || jobTypeStr.includes('vzdump') || jobTypeStr.includes('verify'); const jobTypeColor = isBackup ? 'text-purple-400' : 'text-white'; container.innerHTML += `${jobTypeStr}
Host: ${job.node_name}
${statusText}
${dateStr} - ${timeStr}