Upload files to "/"
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
error_reporting(0);
|
||||
header('Content-Type: application/json');
|
||||
$config = parse_ini_file('config.ini', true);
|
||||
|
||||
$base_url = "https://{$config['proxmox']['host']}:{$config['proxmox']['port']}/api2/json/nodes/{$config['proxmox']['node']}";
|
||||
$token = "PVEAPIToken={$config['proxmox']['user']}!{$config['proxmox']['token_name']}={$config['proxmox']['token_value']}";
|
||||
|
||||
function pve_get($url, $token) {
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: $token"]);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||
curl_setopt($ch, CURLOPT_TIMEOUT, 2);
|
||||
$res = curl_exec($ch);
|
||||
curl_close($ch);
|
||||
return json_decode($res, true)['data'];
|
||||
}
|
||||
|
||||
$qemu = pve_get("$base_url/qemu", $token) ?? [];
|
||||
$lxc = pve_get("$base_url/lxc", $token) ?? [];
|
||||
$results = [];
|
||||
|
||||
foreach ($qemu as $node) {
|
||||
$ips = [];
|
||||
$agent = pve_get("$base_url/qemu/{$node['vmid']}/agent/network-get-interfaces", $token);
|
||||
if ($agent && isset($agent['result'])) {
|
||||
foreach($agent['result'] as $iface) {
|
||||
foreach($iface['ip-addresses'] as $addr) {
|
||||
if ($addr['ip-address-type'] == 'ipv4' && $addr['ip-address'] != '127.0.0.1') $ips[] = $addr['ip-address'];
|
||||
}
|
||||
}
|
||||
}
|
||||
$results[] = [
|
||||
'vmid' => $node['vmid'], 'name' => $node['name'], 'status' => $node['status'], 'type' => 'qemu',
|
||||
'ip' => !empty($ips) ? implode(", ", array_unique($ips)) : "N/D",
|
||||
'cpu' => $node['cpu'], 'mem' => $node['mem'], 'maxmem' => $node['maxmem'],
|
||||
'disk' => $node['disk'], 'maxdisk' => $node['maxdisk'],
|
||||
'netin' => $node['netin'], 'netout' => $node['netout']
|
||||
];
|
||||
}
|
||||
|
||||
foreach ($lxc as $node) {
|
||||
$ip = "N/D";
|
||||
if (!empty($node['tags']) && preg_match('/(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/', $node['tags'], $matches)) $ip = $matches[1];
|
||||
$results[] = [
|
||||
'vmid' => $node['vmid'], 'name' => $node['name'], 'status' => $node['status'], 'type' => 'lxc', 'ip' => $ip,
|
||||
'cpu' => $node['cpu'], 'mem' => $node['mem'], 'maxmem' => $node['maxmem'],
|
||||
'disk' => $node['disk'], 'maxdisk' => $node['maxdisk'],
|
||||
'netin' => $node['netin'], 'netout' => $node['netout']
|
||||
];
|
||||
}
|
||||
|
||||
echo json_encode($results);
|
||||
?>
|
||||
@@ -0,0 +1,7 @@
|
||||
[proxmox]
|
||||
host = "IP_Proxmox"
|
||||
node = "Name_Node"
|
||||
user = "root@pam"
|
||||
token_name = "dashboard"
|
||||
token_value = "SEGRET"
|
||||
port = 8006
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="it">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Proxmox Pro Dashboard</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<script src="https://unpkg.com/lucide@latest"></script>
|
||||
<style>
|
||||
body { background-color: #020617; }
|
||||
.progress-bar { transition: width 1s cubic-bezier(0.4, 0, 0.2, 1); }
|
||||
</style>
|
||||
</head>
|
||||
<body class="text-slate-100 p-4 md:p-8">
|
||||
<header class="max-w-7xl mx-auto mb-8 flex justify-between items-end border-b border-slate-800 pb-6">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="p-2 bg-blue-600 rounded-lg"><i data-lucide="server" class="w-6 h-6 text-white"></i></div>
|
||||
<div>
|
||||
<h1 class="text-xl font-black text-white uppercase tracking-tighter">Proxmox Pro Dashboard</h1>
|
||||
<p class="text-slate-500 text-[10px] uppercase font-bold tracking-widest">Live Node Stats</p>
|
||||
</div>
|
||||
</div>
|
||||
<div id="last-update" class="text-[9px] font-mono text-slate-600 italic">Inizializzazione...</div>
|
||||
</header>
|
||||
|
||||
<div id="vm-grid" class="max-w-7xl mx-auto grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-5"></div>
|
||||
|
||||
<script>
|
||||
let prevNet = {};
|
||||
async function update() {
|
||||
try {
|
||||
const r = await fetch('api.php');
|
||||
const vms = await r.json();
|
||||
const grid = document.getElementById('vm-grid');
|
||||
vms.sort((a, b) => a.vmid - b.vmid);
|
||||
grid.innerHTML = '';
|
||||
|
||||
vms.forEach(vm => {
|
||||
const speed = Math.max(0, (vm.netin - (prevNet[vm.vmid] || vm.netin)) / 5);
|
||||
prevNet[vm.vmid] = vm.netin;
|
||||
|
||||
const cpu = (vm.cpu * 100).toFixed(1);
|
||||
const ram = ((vm.mem / vm.maxmem) * 100).toFixed(1);
|
||||
const disk = ((vm.disk / vm.maxdisk) * 100).toFixed(1);
|
||||
const isRunning = vm.status === 'running';
|
||||
|
||||
const typeIcon = vm.type === 'qemu' ? 'monitor' : 'container';
|
||||
|
||||
let ipButtons = '';
|
||||
if (vm.ip !== 'N/D') {
|
||||
vm.ip.split(', ').forEach(ip => {
|
||||
ipButtons += `<a href="http://${ip}" target="_blank" class="bg-blue-900/30 border border-blue-500/30 text-blue-400 px-2 py-0.5 rounded text-[9px] font-bold hover:bg-blue-500/20 transition-all uppercase">🔗 ${ip}</a>`;
|
||||
});
|
||||
}
|
||||
|
||||
grid.innerHTML += `
|
||||
<div class="bg-slate-900/50 border border-slate-800 rounded-2xl p-5 shadow-2xl hover:border-slate-700 transition-all">
|
||||
<div class="flex justify-between items-start mb-4">
|
||||
<div class="flex items-center gap-3">
|
||||
<div class="p-2 bg-slate-800 rounded-lg text-slate-400">
|
||||
<i data-lucide="${typeIcon}" class="w-5 h-5"></i>
|
||||
</div>
|
||||
<div class="truncate w-24 sm:w-32">
|
||||
<h3 class="font-bold text-sm text-slate-200 truncate" title="${vm.name}">${vm.name}</h3>
|
||||
<span class="text-[8px] text-slate-600 font-mono">ID ${vm.vmid} | ${vm.type.toUpperCase()}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-1.5 bg-slate-800 px-2 py-1 rounded-full border border-slate-700">
|
||||
<div class="h-1.5 w-1.5 rounded-full ${isRunning ? 'bg-emerald-500 shadow-[0_0_8px_#10b981]' : 'bg-rose-600'}"></div>
|
||||
<span class="text-[8px] font-black uppercase text-slate-500">${vm.status}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-5 flex flex-wrap gap-1 justify-center min-h-[28px]">
|
||||
${ipButtons || '<span class="text-[9px] text-slate-700 font-bold italic">OFFLINE / NO IP</span>'}
|
||||
</div>
|
||||
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<div class="flex justify-between text-[9px] font-bold mb-1.5 px-0.5 text-slate-400 uppercase tracking-tighter">
|
||||
<div class="flex items-center gap-1.5"><i data-lucide="cpu" class="w-3 h-3"></i><span>CPU</span></div>
|
||||
<span class="text-blue-400">${cpu}%</span>
|
||||
</div>
|
||||
<div class="w-full bg-slate-800 h-1.5 rounded-full overflow-hidden"><div class="progress-bar bg-blue-500 h-full" style="width: ${cpu}%"></div></div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="flex justify-between text-[9px] font-bold mb-1.5 px-0.5 text-slate-400 uppercase tracking-tighter">
|
||||
<div class="flex items-center gap-1.5"><i data-lucide="layers" class="w-3 h-3"></i><span>RAM</span></div>
|
||||
<span class="text-emerald-500">${ram}%</span>
|
||||
</div>
|
||||
<div class="w-full bg-slate-800 h-1.5 rounded-full overflow-hidden"><div class="progress-bar bg-emerald-500 h-full" style="width: ${ram}%"></div></div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="flex justify-between text-[9px] font-bold mb-1.5 px-0.5 text-slate-400 uppercase tracking-tighter">
|
||||
<div class="flex items-center gap-1.5"><i data-lucide="hard-drive" class="w-3 h-3"></i><span>DISK</span></div>
|
||||
<span class="text-purple-400">${disk}%</span>
|
||||
</div>
|
||||
<div class="w-full bg-slate-800 h-1.5 rounded-full overflow-hidden"><div class="progress-bar bg-purple-500 h-full" style="width: ${disk}%"></div></div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="flex justify-between text-[9px] font-bold mb-1.5 px-0.5 text-slate-400 uppercase tracking-tighter">
|
||||
<div class="flex items-center gap-1.5"><i data-lucide="activity" class="w-3 h-3"></i><span>NET</span></div>
|
||||
<span class="text-amber-500 font-mono">${(speed/1024).toFixed(1)} KB/s</span>
|
||||
</div>
|
||||
<div class="w-full bg-slate-800 h-1.5 rounded-full overflow-hidden"><div class="progress-bar bg-amber-500 h-full" style="width: ${Math.min((speed/524288)*10, 100)}%"></div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
lucide.createIcons();
|
||||
document.getElementById('last-update').innerText = "DATA SYNC: " + new Date().toLocaleTimeString();
|
||||
} catch (e) { console.error(e); }
|
||||
}
|
||||
setInterval(update, 5000);
|
||||
update();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user