119 lines
7.4 KiB
HTML
119 lines
7.4 KiB
HTML
<!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>
|