mirror of
https://github.com/HirziDevs/PteroStats.git
synced 2026-03-31 06:24:10 -04:00
feat: add uptime tracking for panel and nodes
This commit is contained in:
@@ -76,10 +76,12 @@ nodes_settings:
|
||||
servers: false # Show server details (true/false).
|
||||
allocations_as_max_servers: false # Show allocations as max servers (true/false).
|
||||
unit: "byte" # Unit for node usage, Available types: "byte" or "percentage".
|
||||
uptime: true # Enable or disable node uptime (true/false).
|
||||
limit: 100 # Node limit for usage statistics display.
|
||||
|
||||
# Panel Users and Servers Settings
|
||||
panel_settings:
|
||||
uptime: true # Enable or disable node uptime (true/false).
|
||||
status: true # Display panel stats under node stats (true/false).
|
||||
servers: true # Display servers count (true/false).
|
||||
users: true # Display users count (true/false).
|
||||
|
||||
13
handlers/UptimeFormatter.js
Normal file
13
handlers/UptimeFormatter.js
Normal file
@@ -0,0 +1,13 @@
|
||||
module.exports = function UptimeFormatter(time) {
|
||||
let text = []
|
||||
const days = Math.floor(time / 86400000);
|
||||
const hours = Math.floor(time / 3600000) % 24;
|
||||
const minutes = Math.floor(time / 60000) % 60;
|
||||
const seconds = Math.floor(time / 1000) % 60;
|
||||
if (days > 0) text.push(`${days} days`)
|
||||
if (hours > 0) text.push(`${hours} hours`)
|
||||
if (minutes > 0) text.push(`${minutes} minutes`)
|
||||
if (text.length > 0) text.push(`and ${seconds} seconds`)
|
||||
else text.push(`${seconds} seconds`)
|
||||
return text.join(", ").replace(", and", " and")
|
||||
}
|
||||
@@ -2,7 +2,6 @@ require("dotenv").config()
|
||||
const { Client, GatewayIntentBits, EmbedBuilder, time, ActivityType, ActionRowBuilder, ButtonBuilder, ButtonStyle } = require("discord.js");
|
||||
const fs = require("node:fs");
|
||||
const cliColor = require("cli-color");
|
||||
const cachePath = require('node:path').join(__dirname, "../cache.json");
|
||||
const config = require("./config.js");
|
||||
const convertUnits = require("./convertUnits.js");
|
||||
const getStats = require("./getStats.js");
|
||||
@@ -20,6 +19,7 @@ module.exports = function App() {
|
||||
const results = await getStats();
|
||||
createMessage({
|
||||
panel: true,
|
||||
uptime: results.uptime,
|
||||
nodes: results.nodes,
|
||||
servers: results.servers,
|
||||
users: results.users,
|
||||
@@ -27,7 +27,7 @@ module.exports = function App() {
|
||||
} catch {
|
||||
console.log(cliColor.cyanBright("[PteroStats] ") + cliColor.redBright("Panel is currently offline."));
|
||||
|
||||
fs.readFile(cachePath, (err, data) => {
|
||||
fs.readFile(require('node:path').join(__dirname, "../cache.json"), (err, data) => {
|
||||
if (err) {
|
||||
createMessage({ cache: false, panel: false });
|
||||
return console.log(cliColor.cyanBright("[PteroStats] ") + cliColor.redBright("Last cache was not found!"));
|
||||
@@ -35,6 +35,8 @@ module.exports = function App() {
|
||||
|
||||
try {
|
||||
const results = JSON.parse(data);
|
||||
results.uptime = false
|
||||
fs.writeFileSync("cache.json", JSON.stringify(results, null, 2), "utf8");
|
||||
createMessage({
|
||||
cache: true,
|
||||
panel: false,
|
||||
@@ -87,7 +89,7 @@ module.exports = function App() {
|
||||
startGetStatus();
|
||||
});
|
||||
|
||||
async function createMessage({ cache, panel, nodes, servers, users }) {
|
||||
async function createMessage({ cache, panel, uptime, nodes, servers, users }) {
|
||||
let embed = new EmbedBuilder()
|
||||
.setAuthor({
|
||||
name: config.embed.nodes.author.name || null,
|
||||
@@ -116,6 +118,7 @@ module.exports = function App() {
|
||||
`Disk : ${convertUnits(node.attributes.allocated_resources.disk, node.attributes.disk, config.nodes_settings.unit)}` +
|
||||
(node.attributes?.allocated_resources?.cpu ? `\nCPU : ${node.attributes?.allocated_resources?.cpu || 0}%` : "") +
|
||||
(config.nodes_settings.servers ? `\nServers: ${node.attributes.relationships.servers}${config.nodes_settings.allocations_as_max_servers ? ` / ${node.attributes.relationships.allocations}` : ""}` : "") +
|
||||
(config.nodes_settings.uptime ? `\nUptime : ${node.uptime ? require("./UptimeFormatter.js")(Date.now() - node.uptime) : "N/A"}` : "") +
|
||||
"```"
|
||||
});
|
||||
});
|
||||
@@ -152,6 +155,7 @@ module.exports = function App() {
|
||||
`Nodes : ${nodes.length}\n` +
|
||||
(config.panel_settings.servers ? `Servers: ${servers || "Unknown"}\n` : "") +
|
||||
(config.panel_settings.users ? `Users : ${users || "Unknown"}\n` : "") +
|
||||
(config.panel_settings.uptime ? `Uptime : ${uptime ? require("./UptimeFormatter.js")(Date.now() - uptime) : "N/A"}\n` : "") +
|
||||
"```"
|
||||
});
|
||||
|
||||
@@ -209,22 +213,27 @@ module.exports = function App() {
|
||||
}
|
||||
|
||||
function DiscordErrorHandler(error) {
|
||||
if (error.rawError?.code === 429) {
|
||||
console.log(cliColor.cyanBright("[PteroStats] ") + cliColor.redBright("Error 429 | Your IP has been rate limited by either Discord or your website. If it's a rate limit with Discord, you must wait. If it's a issue with your website, consider whitelisting your server IP."));
|
||||
} else if (error.rawError?.code === 403) {
|
||||
console.log(cliColor.cyanBright("[PteroStats] ") + cliColor.redBright("FORBIDDEN | The channel ID you provided is incorrect. Please double check you have the right ID. If you're not sure, read our documentation: https://github.com/HirziDevs/PteroStats#getting-channel-id"));
|
||||
} else if (error.code === "ENOTFOUND") {
|
||||
console.log(cliColor.cyanBright("[PteroStats] ") + cliColor.redBright("ENOTFOUND | DNS Error. Ensure your network connection and DNS server are functioning correctly."));
|
||||
} else if (error.rawError?.code === 50001) {
|
||||
console.log(cliColor.cyanBright("[PteroStats] ") + cliColor.redBright("Discord Error | Your discord bot doesn't have access to see/send message/edit message in the channel!"));
|
||||
} else if (error.rawError?.errors && Object?.values(error.rawError.errors)[0]._errors[0].code === "MAX_EMBED_SIZE_EXCEEDED") {
|
||||
console.log(cliColor.cyanBright("[PteroStats] ") + cliColor.redBright("Discord Error | Embed message limit exceeded! Please limit or decrease the nodes that need to be shown in the config!"));
|
||||
} else if (error.rawError?.errors && Object?.values(error.rawError.errors)[0]._errors[0].code) {
|
||||
console.log(Object.values(error.rawError.errors)[0]._errors[0].message);
|
||||
} else {
|
||||
console.error(cliColor.cyanBright("[PteroStats] ") + cliColor.redBright("Discord Error"), error);
|
||||
try {
|
||||
if (error.rawError?.code === 429) {
|
||||
console.log(cliColor.cyanBright("[PteroStats] ") + cliColor.redBright("Error 429 | Your IP has been rate limited by either Discord or your website. If it's a rate limit with Discord, you must wait. If it's a issue with your website, consider whitelisting your server IP."));
|
||||
} else if (error.rawError?.code === 403) {
|
||||
console.log(cliColor.cyanBright("[PteroStats] ") + cliColor.redBright("FORBIDDEN | The channel ID you provided is incorrect. Please double check you have the right ID. If you're not sure, read our documentation: https://github.com/HirziDevs/PteroStats#getting-channel-id"));
|
||||
} else if (error.code === "ENOTFOUND") {
|
||||
console.log(cliColor.cyanBright("[PteroStats] ") + cliColor.redBright("ENOTFOUND | DNS Error. Ensure your network connection and DNS server are functioning correctly."));
|
||||
} else if (error.rawError?.code === 50001) {
|
||||
console.log(cliColor.cyanBright("[PteroStats] ") + cliColor.redBright("Discord Error | Your discord bot doesn't have access to see/send message/edit message in the channel!"));
|
||||
} else if (error.rawError?.errors && Object?.values(error.rawError.errors)[0]?._errors[0]?.code === "MAX_EMBED_SIZE_EXCEEDED") {
|
||||
console.log(cliColor.cyanBright("[PteroStats] ") + cliColor.redBright("Discord Error | Embed message limit exceeded! Please limit or decrease the nodes that need to be shown in the config!"));
|
||||
} else if (error.rawError?.errors && Object?.values(error.rawError.errors)[0]?._errors[0]?.code) {
|
||||
console.log(Object.values(error.rawError.errors)[0]._errors[0].message);
|
||||
} else {
|
||||
console.error(cliColor.cyanBright("[PteroStats] ") + cliColor.redBright("Discord Error"), error);
|
||||
}
|
||||
process.exit();
|
||||
} catch (err) {
|
||||
console.log(error)
|
||||
process.exit();
|
||||
}
|
||||
process.exit();
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
@@ -1,28 +1,40 @@
|
||||
const config = require("./config.js");
|
||||
const fs = require("node:fs");
|
||||
const getNodesDetails = require("./getNodesDetails.js");
|
||||
const getNodeConfiguration = require("./getNodeConfiguration.js");
|
||||
const getWingsStatus = require("./getWingsStatus.js");
|
||||
const getServers = require("./getServers.js");
|
||||
const getUsers = require("./getUsers.js");
|
||||
const promiseTimeout = require("./promiseTimeout.js");
|
||||
const cliColor = require("cli-color");
|
||||
|
||||
module.exports = async function getStats() {
|
||||
let cache = (() => {
|
||||
try {
|
||||
return JSON.parse(fs.readFileSync(require('node:path').join(__dirname, "../cache.json")))
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
})()
|
||||
|
||||
console.log(cliColor.cyanBright("[PteroStats] ") + cliColor.yellow("Retrieving panel nodes..."))
|
||||
const nodesStats = await getNodesDetails();
|
||||
const nodesStats = await require("./getNodesDetails.js")();
|
||||
if (!nodesStats) throw new Error("Failed to get nodes attributes");
|
||||
|
||||
const statusPromises = nodesStats.slice(0, config.nodes_settings.limit).map(async (node) => {
|
||||
console.log(cliColor.cyanBright("[PteroStats] ") + cliColor.yellow(`Fetching ${cliColor.blueBright(node.attributes.name)} configuration...`))
|
||||
const nodeConfig = await getNodeConfiguration(node.attributes.id);
|
||||
const nodeConfig = await require("./getNodeConfiguration.js")(node.attributes.id);
|
||||
console.log(cliColor.cyanBright("[PteroStats] ") + cliColor.yellow(`Checking ${cliColor.blueBright(node.attributes.name)} wings status...`))
|
||||
const nodeStatus = await promiseTimeout(getWingsStatus(node, nodeConfig.token), config.timeout * 1000);
|
||||
const nodeStatus = await require("./promiseTimeout.js")(require("./getWingsStatus.js")(node, nodeConfig.token), config.timeout * 1000);
|
||||
|
||||
if (!nodeStatus)
|
||||
let nodeUptime = cache ? (() => {
|
||||
return cache.nodes.find((n) => n.attributes.id === node.attributes.id)?.uptime || Date.now()
|
||||
})() : Date.now()
|
||||
|
||||
if (!nodeUptime && nodeStatus) nodeUptime = Date.now()
|
||||
|
||||
if (!nodeStatus) {
|
||||
nodeUptime = false
|
||||
console.log(cliColor.cyanBright("[PteroStats] ") + cliColor.redBright(`Node ${cliColor.blueBright(node.attributes.name)} is currently offline.`))
|
||||
}
|
||||
|
||||
return {
|
||||
attributes: {
|
||||
id: node.attributes.id,
|
||||
name: node.attributes.name,
|
||||
memory: node.attributes.memory,
|
||||
disk: node.attributes.disk,
|
||||
@@ -33,13 +45,17 @@ module.exports = async function getStats() {
|
||||
servers: node.attributes.relationships.servers.data.length
|
||||
}
|
||||
},
|
||||
uptime: nodeUptime,
|
||||
status: nodeStatus
|
||||
};
|
||||
});
|
||||
|
||||
const data = {
|
||||
servers: await getServers(),
|
||||
users: await getUsers(),
|
||||
uptime: cache ? (() => {
|
||||
return cache.uptime || Date.now()
|
||||
})() : Date.now(),
|
||||
servers: await require("./getServers.js")(),
|
||||
users: await require("./getUsers.js")(),
|
||||
nodes: await Promise.all(statusPromises),
|
||||
timestamp: Date.now()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user