285 lines
14 KiB
HTML
285 lines
14 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>System Monitor</title>
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<style>
|
|
body { font-family: Arial, sans-serif; background: #f4f4f4; margin: 0; padding: 0; }
|
|
.container { max-width: 600px; margin: 40px auto; background: #fff; padding: 24px; border-radius: 8px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);}
|
|
h1 { text-align: center; }
|
|
.stat { display: flex; justify-content: space-between; margin: 16px 0; font-size: 1.2em; }
|
|
.label { color: #555; }
|
|
.value { font-weight: bold; }
|
|
.refresh-btn { display: block; margin: 24px auto 0; padding: 8px 24px; font-size: 1em; border: none; border-radius: 4px; background: #0078d4; color: #fff; cursor: pointer; }
|
|
.refresh-btn:hover { background: #005fa3; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<h1>NUIKS SysMonitor</h1>
|
|
<div class="stat"><span class="label">CPU Usage:</span> <span class="value" id="cpu">--</span></div>
|
|
<div class="stat"><span class="label">RAM Usage:</span> <span class="value" id="ram">--</span></div>
|
|
<div class="stat"><span class="label">SSD Usage:</span> <span class="value" id="disk">--</span></div>
|
|
<div class="stat"><span class="label">Up since:</span> <span class="value" id="uptime">--</span></div>
|
|
</div>
|
|
|
|
<div class="container" style="margin-top:24px;">
|
|
<h1>Limit overages</h1>
|
|
<ul id="messages" style="list-style:none;padding:0;"></ul>
|
|
</div>
|
|
|
|
<div class="container" style="margin-top:24px;">
|
|
<h1>Resource Limits</h1>
|
|
<script>
|
|
|
|
</script>
|
|
<div class="stat">
|
|
<span class="label">CPU Limit:</span>
|
|
<input type="range" id="cpuLimit" min="1" max="100" value="50" style="flex:1;margin:0 12px;">
|
|
<span class="value" id="cpuLimitValue">50%</span>
|
|
</div>
|
|
<div class="stat">
|
|
<span class="label">RAM Limit:</span>
|
|
<input type="range" id="ramLimit" min="1" max="100" value="50" style="flex:1;margin:0 12px;">
|
|
<span class="value" id="ramLimitValue">50%</span>
|
|
</div>
|
|
<div class="stat">
|
|
<span class="label">SSD Limit:</span>
|
|
<input type="range" id="diskLimit" min="1" max="100" value="50" style="flex:1;margin:0 12px;">
|
|
<span class="value" id="diskLimitValue">50%</span>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
async function fetchOverages() {
|
|
try {
|
|
const res = await fetch('/overages');
|
|
const data = await res.json();
|
|
const messages = document.getElementById('messages');
|
|
// Replace <ul> with a table for better alignment
|
|
// If table doesn't exist, create it
|
|
let table = document.getElementById('overagesTable');
|
|
if (!table) {
|
|
table = document.createElement('table');
|
|
table.id = 'overagesTable';
|
|
table.style.width = '100%';
|
|
table.style.borderCollapse = 'collapse';
|
|
table.innerHTML = `
|
|
<thead>
|
|
<tr>
|
|
<th style="text-align:left;padding:8px 4px;">ID</th>
|
|
<th style="text-align:center;padding:8px 4px;">CPU</th>
|
|
<th style="text-align:center;padding:8px 4px;">RAM</th>
|
|
<th style="text-align:center;padding:8px 4px;">DISK</th>
|
|
<th style="text-align:left;padding:8px 4px;">Timestamp</th>
|
|
<th style="text-align:center;padding:8px 4px;">Action</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody></tbody>
|
|
`;
|
|
messages.innerHTML = '';
|
|
messages.appendChild(table);
|
|
}
|
|
const tbody = table.querySelector('tbody');
|
|
tbody.innerHTML = '';
|
|
data.forEach(item => {
|
|
const tr = document.createElement('tr');
|
|
tr.style.borderBottom = '1px solid #eee';
|
|
|
|
// ID
|
|
const idTd = document.createElement('td');
|
|
idTd.textContent = `#${item.id}`;
|
|
idTd.style.padding = '8px 4px';
|
|
|
|
// CPU
|
|
const cpuTd = document.createElement('td');
|
|
cpuTd.style.textAlign = 'center';
|
|
cpuTd.style.padding = '8px 4px';
|
|
cpuTd.textContent = item.cpu_overage === 1 ? '↑' : '↓';
|
|
cpuTd.style.color = item.cpu_overage === 1 ? 'red' : 'green';
|
|
|
|
// RAM
|
|
const ramTd = document.createElement('td');
|
|
ramTd.style.textAlign = 'center';
|
|
ramTd.style.padding = '8px 4px';
|
|
ramTd.textContent = item.ram_overage === 1 ? '↑' : '↓';
|
|
ramTd.style.color = item.ram_overage === 1 ? 'red' : 'green';
|
|
|
|
// DISK
|
|
const diskTd = document.createElement('td');
|
|
diskTd.style.textAlign = 'center';
|
|
diskTd.style.padding = '8px 4px';
|
|
diskTd.textContent = item.disk_overage === 1 ? '↑' : '↓';
|
|
diskTd.style.color = item.disk_overage === 1 ? 'red' : 'green';
|
|
|
|
// Timestamp
|
|
const tsTd = document.createElement('td');
|
|
const date = new Date(item.timestamp);
|
|
tsTd.textContent = date.toLocaleString();
|
|
tsTd.style.padding = '8px 4px';
|
|
|
|
// Delete button
|
|
const delTd = document.createElement('td');
|
|
delTd.style.textAlign = 'center';
|
|
delTd.style.padding = '8px 4px';
|
|
const delBtn = document.createElement('button');
|
|
delBtn.textContent = 'Delete';
|
|
delBtn.style.background = '#e74c3c';
|
|
delBtn.style.color = '#fff';
|
|
delBtn.style.border = 'none';
|
|
delBtn.style.borderRadius = '4px';
|
|
delBtn.style.padding = '4px 12px';
|
|
delBtn.style.cursor = 'pointer';
|
|
delBtn.onclick = async () => {
|
|
await fetch(`/overages/${item.id}`, { method: 'DELETE' });
|
|
fetchOverages();
|
|
};
|
|
delTd.appendChild(delBtn);
|
|
|
|
tr.appendChild(idTd);
|
|
tr.appendChild(cpuTd);
|
|
tr.appendChild(ramTd);
|
|
tr.appendChild(diskTd);
|
|
tr.appendChild(tsTd);
|
|
tr.appendChild(delTd);
|
|
|
|
tbody.appendChild(tr);
|
|
});
|
|
// If no data, show a message
|
|
if (data.length === 0) {
|
|
const tr = document.createElement('tr');
|
|
const td = document.createElement('td');
|
|
td.colSpan = 6;
|
|
td.style.textAlign = 'center';
|
|
td.style.padding = '12px';
|
|
td.textContent = 'No overages.';
|
|
tr.appendChild(td);
|
|
tbody.appendChild(tr);
|
|
}
|
|
} catch (e) {
|
|
console.error('Error fetching overages:', e);
|
|
document.getElementById('messages').innerHTML = '<span style="color:red;">Error loading overages</span>';
|
|
}
|
|
}
|
|
|
|
setInterval(fetchOverages, 5000);
|
|
window.addEventListener('load', fetchOverages);
|
|
|
|
|
|
async function fetchStats() {
|
|
try {
|
|
const sysInfo = await fetch('/info');
|
|
const data = await sysInfo.json();
|
|
// Format timestamps
|
|
data.uptime = formatTimestamp(data.uptime);
|
|
|
|
document.getElementById('cpu').textContent = data.cpu + '%';
|
|
document.getElementById('ram').textContent = data.ram + '%';
|
|
document.getElementById('disk').textContent = data.disk + '%';
|
|
document.getElementById('uptime').textContent = data.uptime + ' s';
|
|
} catch (e) {
|
|
console.error('Error fetching system stats:', e);
|
|
document.getElementById('cpu').textContent = 'Error';
|
|
document.getElementById('ram').textContent = 'Error';
|
|
document.getElementById('disk').textContent = 'Error';
|
|
document.getElementById('uptime').textContent = 'Error';
|
|
}
|
|
}
|
|
function formatTimestamp(ts) {
|
|
// Try to parse as ISO string, fallback to Date.parse
|
|
let date = new Date(ts);
|
|
if (isNaN(date.getTime())) {
|
|
// If ts is seconds since epoch
|
|
date = new Date(Number(ts) * 1000);
|
|
}
|
|
return date.toLocaleString();
|
|
}
|
|
|
|
// --- Resource Limits Sliders ---
|
|
async function fetchLimits() {
|
|
try {
|
|
const res = await fetch('/limits');
|
|
const limits = await res.json();
|
|
document.getElementById('cpuLimit').value = limits.cpu_threshold;
|
|
document.getElementById('ramLimit').value = limits.ram_threshold;
|
|
document.getElementById('diskLimit').value = limits.disk_threshold;
|
|
document.getElementById('cpuLimitValue').textContent = limits.cpu_threshold + '%';
|
|
document.getElementById('ramLimitValue').textContent = limits.ram_threshold + '%';
|
|
document.getElementById('diskLimitValue').textContent = limits.disk_threshold + '%';
|
|
} catch (e) {
|
|
console.error('Error fetching limits:', e);
|
|
// fallback: set to 50 if error
|
|
document.getElementById('cpuLimit').value = 50;
|
|
document.getElementById('ramLimit').value = 50;
|
|
document.getElementById('diskLimit').value = 50;
|
|
document.getElementById('cpuLimitValue').textContent = '50%';
|
|
document.getElementById('ramLimitValue').textContent = '50%';
|
|
document.getElementById('diskLimitValue').textContent = '50%';
|
|
}
|
|
}
|
|
|
|
function setupLimitSliders() {
|
|
['cpu', 'ram', 'disk'].forEach(resource => {
|
|
const slider = document.getElementById(resource + 'Limit');
|
|
const valueSpan = document.getElementById(resource + 'LimitValue');
|
|
slider.oninput = function() {
|
|
valueSpan.textContent = slider.value + '%';
|
|
};
|
|
slider.onchange = function() {
|
|
updateLimits();
|
|
};
|
|
});
|
|
}
|
|
|
|
async function updateLimits() {
|
|
const cpu = parseInt(document.getElementById('cpuLimit').value, 10);
|
|
const ram = parseInt(document.getElementById('ramLimit').value, 10);
|
|
const disk = parseInt(document.getElementById('diskLimit').value, 10);
|
|
const requestBody = JSON.stringify({
|
|
cpu_threshold: cpu,
|
|
ram_threshold: ram,
|
|
disk_threshold: disk
|
|
});
|
|
console.log(requestBody);
|
|
await fetch('/limits', {
|
|
method: 'POST',
|
|
headers: {'Content-Type': 'application/json'},
|
|
body: requestBody
|
|
}).then(async (response) => {
|
|
if (response.ok) {
|
|
try {
|
|
const limits = await response.json();
|
|
document.getElementById('cpuLimit').value = limits.cpu;
|
|
document.getElementById('ramLimit').value = limits.ram;
|
|
document.getElementById('diskLimit').value = limits.disk;
|
|
document.getElementById('cpuLimitValue').textContent = limits.cpu + '%';
|
|
document.getElementById('ramLimitValue').textContent = limits.ram + '%';
|
|
document.getElementById('diskLimitValue').textContent = limits.disk + '%';
|
|
} catch (e) {
|
|
console.error('Error fetching limits:', e);
|
|
// fallback: set to 50 if error
|
|
document.getElementById('cpuLimit').value = 50;
|
|
document.getElementById('ramLimit').value = 50;
|
|
document.getElementById('diskLimit').value = 50;
|
|
document.getElementById('cpuLimitValue').textContent = '50%';
|
|
document.getElementById('ramLimitValue').textContent = '50%';
|
|
document.getElementById('diskLimitValue').textContent = '50%';
|
|
}
|
|
} else {
|
|
console.error('Error updating limits:', response.statusText);
|
|
}
|
|
}).catch(error => {
|
|
console.error('Error updating limits:', error);
|
|
});
|
|
};
|
|
|
|
window.onload = function() {
|
|
fetchStats();
|
|
setInterval(fetchStats, 15000); // Fetch every 5 seconds
|
|
fetchLimits();
|
|
setupLimitSliders();
|
|
};
|
|
</script>
|
|
</body>
|
|
</html> |