public/stats/stats.js
2024-05-22 04:03:59 -07:00

164 lines
5.1 KiB
JavaScript

function fmt(units, overflow, multiplier = 1) {
return function(val) {
let max = 1;
for(let i = 0; i < units.length; i++) {
let next = max * units[i][1];
if(val < next) {
const number = Math.floor((val) / max);
const unit = units[i][0];
return number + " " + (number === 1 ? unit.slice(0, -1) : unit);
}
max = next;
}
return overflow;
}
}
const fmtSize = fmt([["B", 1024], ["KiB", 1024], ["MiB", 1024], ["GiB", 1024], ["TiB", 1024], ["PiB", 1024], ["ZiB", 1024], ["YiB", 1024]], "quite a lot", 1);
const fmtTime = fmt([["seconds", 60], ["minutes", 60], ["hours", 24], ["days", 60], ["weeks", 7], ["months", 4]], "an eternity");
function createGraph(color, max) {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
const ogWidth = canvas.clientWidth;
const ogHeight = canvas.clientHeight;
const points = [];
function update(val) {
points.unshift(val);
if(points.length > 100) points.pop();
canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;
ctx.width = canvas.clientWidth;
ctx.height = canvas.clientHeight;
ctx.strokeStyle = color;
ctx.fillStyle = color + "66";
ctx.lineWidth = 3;
ctx.clearRect(0, 0, 4000, 4000);
const firsth = canvas.clientHeight - (points[0] / max) * canvas.clientHeight;
ctx.moveTo(canvas.clientWidth, firsth);
for(let i = 1; i < points.length; i++) {
ctx.lineTo(canvas.clientWidth - i * 10, canvas.clientHeight - (points[i] / max) * canvas.clientHeight);
}
ctx.stroke();
ctx.lineTo(canvas.clientWidth - points.length * 10 + 10, canvas.clientHeight);
ctx.lineTo(canvas.clientWidth, canvas.clientHeight);
ctx.fill();
}
return { canvas, update };
}
function createMeter(name, color, max, fmt) {
const wrapper = document.createElement("div");
const side = document.createElement("div");
const title = document.createElement("div");
const display = document.createElement("div");
const subtitle = document.createElement("div");
const graph = createGraph(color, max || 100);
wrapper.style.color = color;
wrapper.classList.add("block");
subtitle.style.fontSize = "0.5em";
subtitle.innerText = max !== 0 ? "of " + fmt(max || 100) : "";
title.style.fontSize = "0.5em";
title.innerText = name;
side.append(title);
side.append(display);
side.append(subtitle);
wrapper.append(graph.canvas);
wrapper.append(side);
function update(val) {
graph.update(val);
display.innerText = fmt ? fmt(val) : Math.floor((val / (max || 100)) * 1e4) / 1e2 + "%";
}
return { element: wrapper, update }
}
function main() {
let ws = null;
let netintotal = 0;
let netouttotal = 0;
let refresh = () => {};
const stats = {};
function init() {
// document.querySelector("main").innerHTML = '<p>The server<span id="server"></span> has been <span id="online">online</span> for <span id="time">a while</span>.</p><p>It\'s sending <span id="netout">stuff</span> and recieving <span id="netin">more stuff</span>, for a total of <span id="netouttotal">???</span> out and <span id="netintotal">???</span> in.</p>';
document.querySelector("main").innerHTML = '<p>The server<span id="server"></span> has been <span id="online">online</span> for <span id="time">a while</span>.</p>';
const memory = createMeter("used memory", "#41ead4", stats.memtotal, fmtSize);
const cpu = createMeter("avg. cpu usage", "#8963ba", 0);
const disk = createMeter("disk usage", "#f06449", stats.disktotal, fmtSize);
document.body.append(memory.element);
document.body.append(cpu.element);
document.body.append(disk.element);
function update() {
memory.update(stats.memused);
cpu.update(stats.cpuusage);
disk.update(stats.diskused);
netintotal += stats.netin;
netouttotal += stats.netout;
document.getElementById("time").innerText = fmtTime(stats.uptime);
document.getElementById("server").innerText = ` (${stats.hostname})`;
// document.getElementById("netin").innerText = fmtSize(stats.netin);
// document.getElementById("netout").innerText = fmtSize(stats.netout);
document.getElementById("netintotal").innerText = fmtSize(netintotal);
document.getElementById("netouttotal").innerText = fmtSize(netouttotal);
}
setInterval(update, 1000);
refresh = update;
update();
}
function reconnect() {
document.getElementById("online").innerText = "offline";
stats.uptime = 0;
stats.netin = 0;
stats.netout = 0;
const wait = setInterval(() => stats.uptime += 1, 1000);
function retry() {
ws = new WebSocket(`wss://${location.host}/stats/live`);
ws.onmessage = (msg) => Object.assign(stats, JSON.parse(msg.data));
ws.onclose = retry;
ws.onopen = () => {
ws.send("sync");
document.getElementById("online").innerText = "online";
clearInterval(wait);
refresh();
};
}
retry();
}
function connect() {
ws = new WebSocket(`wss://${location.host}/stats/live`);
ws.onmessage = (msg) => Object.assign(stats, JSON.parse(msg.data));
ws.onclose = reconnect;
ws.onopen = () => {
ws.send("sync");
const wait = setInterval(() => {
if(!stats.memtotal) return;
clearInterval(wait);
init();
}, 100);
};
}
connect();
}
main();