/**
* !Logger
*/
const os = require("node:os");
const chalk = require("chalk");
const winston = require("winston");
const { combine, timestamp, json, printf, label, errors } = winston.format;
/**
* !console
*/
const {
ConsoleManager,
OptionPopup,
InputPopup,
PageBuilder,
ButtonPopup,
ConfirmPopup,
CustomPopup,
FileSelectorPopup,
Progress,
Control,
InPageWidgetBuilder,
Button,
Box,
EOL,
} = require("console-gui-tools");
const myFormat = printf(({ level, message, label, timestamp }) => {
if (level === "info") {
console.log(
`${chalk.blue(timestamp)} ${chalk.yellow(
"[" + label + "]"
)} ${chalk.green(level)}: ${message}`
);
} else if (level === "warn") {
console.log(
`${chalk.blue(timestamp)} ${chalk.yellow("[" + label + "]")} ${chalk.red(
level
)}: ${message}`
);
} else {
console.log(
`${chalk.blue(timestamp)} ${chalk.yellow("[" + label + "]")} ${chalk.red(
level
)}: ${message}`
);
}
return `${timestamp} [${label}] ${level}: ${message}`;
});
const logger = winston.createLogger({
level: "info",
format: combine(label({ label: "radiman" }), timestamp(), myFormat),
transports: [
//new winston.transports.Console(),
new winston.transports.File({
filename: "radiman.log",
}),
//new LogtailTransport(logtail), // onlien logging
],
});
const opt = {
title: "Radiffy Radius Manager", // Title of the console
logPageSize: 30, // Number of lines to show in logs page
logLocation: 1, // Location of the logs page
enableMouse: false, // Enable mouse support
layoutOptions: {
boxed: true, // Set to true to enable boxed layout
showTitle: true, // Set to false to hide title
//changeFocusKey: "ctrl+l", // Change layout with ctrl+l to switch to the logs page
type: "double", // Set to 'double' to enable double layout
direction: "vertical", // Set to 'horizontal' to enable horizontal layout
boxColor: "yellow",
boxStyle: "bold",
},
};
const GUI = new ConsoleManager(opt);
let period = 2000;
let mode = "debug";
let valueEmitter = null;
let min = 9;
let max = 12;
let cpu_brand = "";
let cpu_speed = 0;
let cpu_cores = 0;
const periodList = [
2000, 5000, 10000, 20000, 30000, 60000, 120000, 300000, 600000, 900000,
1800000, 3600000, 7200000, 14400000, 28800000, 43200000, 86400000,
];
const numberOfCores = os.cpus().length;
const cores = [];
for (let i = 0; i < numberOfCores; i++) {
cores.push(
new Progress({
id: `htop-cpu-${i}`,
x: 2,
y: 1 + i,
interactive: false,
draggable: false,
label: i > 9 ? `${i} ` : `${i} `,
length: 40,
min: 0,
max: 100,
style: {
boxed: true,
theme: "htop",
showMinMax: false,
showValue: false,
},
})
);
}
const mem = new Progress({
id: "htop-mem",
x: 2,
y: 1 + numberOfCores,
label: "Mem",
length: 40,
min: 0,
max: os.totalmem() / (1024 * 1024 * 1024),
unit: "G",
style: {
boxed: true,
theme: "htop",
showMinMax: false,
},
});
//Create function to get CPU information
function cpuAverage(core) {
//Initialise sum of idle and time of cores and fetch CPU info
let totalIdle = 0,
totalTick = 0;
//Select CPU core
const cpu = os.cpus()[core];
//Total up the time in the cores tick
for (let i = 0, len = Object.keys(cpu.times).length; i < len; i++) {
totalTick += Object.values(cpu.times)[i];
}
//Total up the idle time of the core
totalIdle += cpu.times.idle;
//Return the average Idle and Tick times
return { idle: totalIdle, total: totalTick };
}
function getCPULoadAVG(core, avgTime = 1000, delay = 500) {
return new Promise((resolve, reject) => {
const n = ~~(avgTime / delay);
if (n <= 1) {
reject("Error: interval to small");
}
let i = 0;
const samples = [];
const avg1 = cpuAverage(core);
const interval = setInterval(() => {
//GUI.log("CPU Interval: " + i)
if (i >= n) {
clearInterval(interval);
resolve(
~~((samples.reduce((a, b) => a + b, 0) / samples.length) * 100)
);
}
const avg2 = cpuAverage(core);
const totalDiff = avg2.total - avg1.total;
const idleDiff = avg2.idle - avg1.idle;
samples[i] = 1 - idleDiff / totalDiff;
i++;
}, delay);
});
}
const getSystemInfo = async () => {
const coresPercent = [];
for (let i = 0; i < numberOfCores; i++) {
const load = await getCPULoadAVG(i).catch((err) => console.error(err));
coresPercent.push(load);
}
const memUsage = os.totalmem() - os.freemem();
return {
cpuUsage: coresPercent,
memUsage,
};
};
const modeList = [
"debug",
"info",
"error",
"warning",
"notice",
"crit",
"alert",
"emerg",
];
const fetchSystemInformation = async () => {
getSystemInfo().then((info) => {
info.cpuUsage.forEach((core, i) => {
cores[i].setValue(core);
});
mem.setValue(info.memUsage / (1024 * 1024 * 1024));
});
drawGui();
};
const frame = async () => {
await fetchSystemInformation();
};
/**
* @description Updates the console screen
*
*/
const updateConsole = async () => {
const uptime = os.uptime();
const hours = Math.floor(uptime / 3600);
const minutes = Math.floor((uptime % 3600) / 60);
const seconds = Math.floor(uptime % 60);
const uptimeText = `${hours}:${minutes}:${seconds}s`;
const p = new PageBuilder();
p.addSpacer(numberOfCores + 1);
if (!valueEmitter) {
p.addRow(
{ text: "Sysinfo is not running! ", color: "red" },
{ text: "press 'space' to start", color: "white" }
);
} else {
p.addRow(
{ text: "Sysinfo is running! ", color: "green" },
{ text: "press 'space' to stop", color: "white" }
);
}
// Print mode:
p.addRow(
{ text: "Logging Mode: ", color: "magenta" },
{ text: `${mode}`, color: "white" }
);
GUI.setPage(p, 0);
GUI.refresh();
};
GUI.on("exit", () => {
closeApp();
});
GUI.on("keypressed", (key) => {
switch (key.name) {
case "space":
if (valueEmitter) {
clearInterval(valueEmitter);
valueEmitter = null;
drawGui();
} else {
valueEmitter = setInterval(frame, period);
drawGui();
}
break;
case "m":
new OptionPopup({
id: "popupSelectMode",
title: "Select logging mode",
options: modeList,
selected: mode,
})
.show()
.on("confirm", (_mode) => {
mode = _mode;
//logger.transports.console.level = mode;
GUI.warn(`NEW MODE: ${mode}`);
drawGui();
});
break;
case "s":
new OptionPopup({
id: "popupSelectPeriod",
title: "Select update period",
options: periodList,
selected: period,
})
.show()
.on("confirm", (_period) => {
const msgMultiLine = `Changing period from ${period} to ${_period} ms.${EOL}This will restart the monitor.${EOL}Do you want to continue?`;
new ButtonPopup({
id: "popupConfirmPeriod",
title: "Confirm period",
message: msgMultiLine,
buttons: ["Yes", "No", "?"],
})
.show()
.on("confirm", (answer) => {
if (answer === "Yes") {
period = _period;
GUI.warn(`NEW PERIOD: ${period}`);
} else if (answer === "?") {
GUI.info("Choose ok to confirm period");
}
drawGui();
});
});
break;
case "a":
new InputPopup({
id: "popupTypeMax",
title: "Type max value",
value: max,
numeric: true,
})
.show()
.on("confirm", (_max) => {
max = _max;
GUI.warn(`NEW MAX VALUE: ${max}`);
drawGui();
});
break;
case "d":
new InputPopup({
id: "popupTypeMin",
title: "Type min value",
value: min,
numeric: true,
})
.show()
.on("confirm", (_min) => {
min = _min;
GUI.warn(`NEW MIN VALUE: ${min}`);
drawGui();
});
break;
case "1":
{
const p = new PageBuilder(5); // Add a scroll limit so it will be scrollable with up and down
p.addRow({
text: "Example of a custom popup content!",
color: "yellow",
});
p.addRow({ text: "This is a custom popup!", color: "green" });
p.addRow({
text: "It can be used to show a message,",
color: "green",
});
p.addRow({ text: "or to show variables.", color: "green" });
p.addRow(
{ text: "TCP Message sent: ", color: "green" },
{ text: `${12}`, color: "white" }
);
p.addRow(
{ text: "Connected clients: ", color: "green" },
{ text: `${20}`, color: "white" }
);
p.addRow(
{ text: "Mode: ", color: "green" },
{ text: `${"radsec"}`, color: "white" }
);
p.addRow(
{ text: "Message period: ", color: "green" },
{ text: `${100} ms`, color: "white" }
);
new CustomPopup({
id: "popupCustom1",
title: "See that values",
content: p,
width: 32,
}).show();
}
break;
case "q":
new ConfirmPopup({
id: "popupQuit",
title: "Are you sure you want to quit?",
})
.show()
.on("confirm", () => closeApp());
break;
default:
break;
}
});
const closeApp = () => {
console.clear();
process.exit();
};
GUI.refresh();
const footer = new Box({
id: "footer",
x: 0,
y: GUI.Screen.height - 1,
width: GUI.Screen.width,
height: 1,
});
const row_footer = new InPageWidgetBuilder(1);
row_footer.addRow(
{ text: "F1:", color: "white", bold: true },
{ text: "Help ", color: "black", bg: "bgCyan", bold: false },
{ text: "F6:", color: "white", bold: true },
{ text: "SortBy", color: "black", bg: "bgCyan", bold: false },
{ text: "F9:", color: "white", bold: true },
{ text: "Kill ", color: "black", bg: "bgCyan", bold: false },
{ text: "F10:", color: "white", bold: true },
{ text: "Quit ", color: "black", bg: "bgCyan", bold: false }
);
footer.setContent(row_footer);
GUI.on("resize", () => {
footer.absoluteValues = {
x: 0,
y: GUI.Screen.height - 2,
width: GUI.Screen.width,
height: 1,
};
GUI.refresh();
});
const drawGui = () => {
updateConsole();
};
drawGui();
valueEmitter = setInterval(frame, 2000);
setInterval(() => {
logger.info("test");
}, 5000);