rjweb-server
Version:
Easy and Robust Way to create a Web Server with Many Easy-to-use Features in NodeJS
166 lines (165 loc) • 5.73 kB
JavaScript
import parseURL from "../parseURL";
import { getFilesRecursively } from "rjutils-collection";
import { getPreviousHours } from "../../classes/dataStat";
import Path from "../../classes/path";
import { Readable } from "stream";
import { Version } from "../../index";
import fs from "fs/promises";
import os from "os";
const dashboardIndexRoute = (ctg) => ({
type: "http",
method: "GET",
path: new Path("GET", "/"),
onRequest: async (ctr) => await statsRoute(ctr, ctg, "http"),
data: {
validations: [],
headers: {}
},
context: { data: {}, keep: true }
});
const dashboardWsRoute = (ctg) => ({
type: "websocket",
path: new Path("GET", "/"),
onConnect: async (ctr) => await statsRoute(ctr, ctg, "socket"),
data: {
validations: [],
headers: {}
},
context: { data: {}, keep: true }
});
const hashCode = (value) => {
return value.split("").reduce((a, b) => {
a = (a << 5) - a + b.charCodeAt(0);
return a & a;
}, 0).toString(16).replace("-", "M");
};
const runStats = async (ctg) => {
const date = /* @__PURE__ */ new Date();
const startTime = date.getTime();
const startUsage = process.cpuUsage();
const previousHours = getPreviousHours();
const staticFiles = await new Promise(async (resolve) => {
let staticFiles2 = 0;
for (let staticNumber = 0; staticNumber < ctg.routes.static.length; staticNumber++) {
staticFiles2 += (await getFilesRecursively(ctg.routes.static[staticNumber].location, true)).length;
}
resolve(staticFiles2);
});
const cpuUsage = await new Promise((resolve) => setTimeout(() => {
const currentUsage = process.cpuUsage(startUsage);
const currentTime = (/* @__PURE__ */ new Date()).getTime();
const timeDelta = (currentTime - startTime) * 5 * coreCount;
resolve((currentUsage.system + currentUsage.user) / timeDelta);
}, 500));
return {
requests: {
total: ctg.requests.stats.total,
perSecond: ctg.requests.stats.perSecond,
hours: Array.from({ length: 5 }, (value, index) => ({
hour: previousHours[index],
amount: ctg.requests.stats[previousHours[index]]
}))
},
webSockets: {
opened: {
total: ctg.webSockets.opened.stats.total,
perSecond: ctg.webSockets.opened.stats.perSecond,
hours: Array.from({ length: 5 }, (value, index) => ({
hour: previousHours[index],
amount: ctg.webSockets.opened.stats[previousHours[index]]
}))
},
messages: {
incoming: {
total: ctg.webSockets.messages.incoming.stats.total,
perSecond: ctg.webSockets.messages.incoming.stats.perSecond,
hours: Array.from({ length: 5 }, (value, index) => ({
hour: previousHours[index],
amount: ctg.webSockets.messages.incoming.stats[previousHours[index]]
}))
},
outgoing: {
total: ctg.webSockets.messages.outgoing.stats.total,
perSecond: ctg.webSockets.messages.outgoing.stats.perSecond,
hours: Array.from({ length: 5 }, (value, index) => ({
hour: previousHours[index],
amount: ctg.webSockets.messages.outgoing.stats[previousHours[index]]
}))
}
}
},
data: {
incoming: {
total: ctg.data.incoming.stats.total,
perSecond: ctg.data.incoming.stats.perSecond,
hours: Array.from({ length: 5 }, (value, index) => ({
hour: previousHours[index],
amount: ctg.data.incoming.stats[previousHours[index]]
}))
},
outgoing: {
total: ctg.data.outgoing.stats.total,
perSecond: ctg.data.outgoing.stats.perSecond,
hours: Array.from({ length: 5 }, (value, index) => ({
hour: previousHours[index],
amount: ctg.data.outgoing.stats[previousHours[index]]
}))
}
},
cpu: {
time: `${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`,
usage: cpuUsage.toFixed(2)
},
memory: {
time: `${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}`,
usage: process.memoryUsage().heapUsed + process.memoryUsage().external
},
routes: {
user: ctg.routes.normal.length + ctg.routes.websocket.length,
automatic: ctg.routes.htmlBuilder.length
},
staticFiles,
internalLogs: ctg.logger["logs"],
middlewares: ctg.middlewares.length,
cached: ctg.cache.files.objectCount + ctg.cache.middlewares.objectCount + ctg.cache.routes.objectCount
};
};
const coreCount = os.cpus().length;
async function statsRoute(ctr, ctg, type) {
switch (type) {
case "http": {
if (ctr.type !== "http")
return;
const dashboard = (await fs.readFile(`${__dirname}/dashboard.html`, "utf8")).replaceAll("/rjweb-dashboard", parseURL(ctg.options.dashboard.path).path).replace("1.1.1", Version);
ctr.headers.set("content-type", "text/html");
return ctr.print(dashboard);
}
case "socket": {
if (ctr.type !== "connect")
return;
if (ctg.options.dashboard.password && ctr.queries.get("password") !== hashCode(ctg.options.dashboard.password))
return ctr.close(1, {
error: "password"
});
let interval = null;
ctr.printStream((() => {
const readable = new Readable({
objectMode: true,
read() {
},
destroy() {
clearInterval(interval);
}
});
interval = setInterval(() => readable.push(runStats(ctg)), ctg.options.dashboard.updateInterval);
setImmediate(() => readable.push(runStats(ctg)));
return readable;
})());
}
}
}
export {
dashboardIndexRoute,
dashboardWsRoute,
statsRoute as default
};