From e5ff86e6acc000477cb500739242e7b7dae717da Mon Sep 17 00:00:00 2001 From: Qi Zhao Date: Wed, 12 Jun 2024 18:17:16 +0800 Subject: [PATCH 1/7] =?UTF-8?q?fix:=20=F0=9F=90=9B=20status=20&=20ping=20b?= =?UTF-8?q?adge=20duration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/routers/api-router.js | 16 ++++------------ server/uptime-calculator.js | 22 +++++++++++++--------- 2 files changed, 17 insertions(+), 21 deletions(-) diff --git a/server/routers/api-router.js b/server/routers/api-router.js index 9c572f686..761b488e4 100644 --- a/server/routers/api-router.js +++ b/server/routers/api-router.js @@ -229,13 +229,9 @@ router.get("/api/badge/:id/uptime/:duration?", cache("5 minutes"), async (reques try { const requestedMonitorId = parseInt(request.params.id, 10); // if no duration is given, set value to 24 (h) - let requestedDuration = request.params.duration !== undefined ? request.params.duration : "24h"; + let requestedDuration = request.params.duration !== undefined ? request.params.duration : "24"; const overrideValue = value && parseFloat(value); - if (requestedDuration === "24") { - requestedDuration = "24h"; - } - let publicMonitor = await R.getRow(` SELECT monitor_group.monitor_id FROM monitor_group, \`group\` WHERE monitor_group.group_id = \`group\`.id @@ -253,7 +249,7 @@ router.get("/api/badge/:id/uptime/:duration?", cache("5 minutes"), async (reques badgeValues.color = badgeConstants.naColor; } else { const uptimeCalculator = await UptimeCalculator.getUptimeCalculator(requestedMonitorId); - const uptime = overrideValue ?? uptimeCalculator.getDataByDuration(requestedDuration).uptime; + const uptime = overrideValue ?? uptimeCalculator.getDataByDuration(`${requestedDuration}h`).uptime; // limit the displayed uptime percentage to four (two, when displayed as percent) decimal digits const cleanUptime = (uptime * 100).toPrecision(4); @@ -299,17 +295,13 @@ router.get("/api/badge/:id/ping/:duration?", cache("5 minutes"), async (request, const requestedMonitorId = parseInt(request.params.id, 10); // Default duration is 24 (h) if not defined in queryParam, limited to 720h (30d) - let requestedDuration = request.params.duration !== undefined ? request.params.duration : "24h"; + let requestedDuration = request.params.duration !== undefined ? request.params.duration : "24"; const overrideValue = value && parseFloat(value); - if (requestedDuration === "24") { - requestedDuration = "24h"; - } - // Check if monitor is public const uptimeCalculator = await UptimeCalculator.getUptimeCalculator(requestedMonitorId); - const publicAvgPing = uptimeCalculator.getDataByDuration(requestedDuration).avgPing; + const publicAvgPing = uptimeCalculator.getDataByDuration(`${requestedDuration}h`).avgPing; const badgeValues = { style }; diff --git a/server/uptime-calculator.js b/server/uptime-calculator.js index 55059e960..37dfd6771 100644 --- a/server/uptime-calculator.js +++ b/server/uptime-calculator.js @@ -741,21 +741,25 @@ class UptimeCalculator { } /** - * Get the uptime data by duration - * @param {'24h'|'30d'|'1y'} duration Only accept 24h, 30d, 1y + * Get the uptime data for given duration. + * @param {string} duration A string with a number and a unit (h, d, or y), such as 24h, 30d, 1y. * @returns {UptimeDataResult} UptimeDataResult * @throws {Error} Invalid duration */ getDataByDuration(duration) { - if (duration === "24h") { - return this.get24Hour(); - } else if (duration === "30d") { - return this.get30Day(); - } else if (duration === "1y") { - return this.get1Year(); - } else { + const unit = duration.substring(duration.length - 1); + const num = parseInt(duration.substring(0, duration.length - 1)); + + const typeMap = { + h: "hour", + d: "day", + y: "year" + }; + + if (!Object.keys(typeMap).includes(unit)) { throw new Error("Invalid duration"); } + return this.getData(num, typeMap[unit]); } /** From 53c4bba387fd5246c7044cb1a61227f215983813 Mon Sep 17 00:00:00 2001 From: Qi Zhao Date: Thu, 13 Jun 2024 15:29:35 +0800 Subject: [PATCH 2/7] =?UTF-8?q?fix:=20=F0=9F=90=9B=20=20badge=20requestedD?= =?UTF-8?q?uration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/routers/api-router.js | 31 +++++++++++++++++---- server/uptime-calculator.js | 53 +++++++++++++++++++++++++----------- src/util.js | 16 +++++++++-- src/util.ts | 28 +++++++++++++++---- 4 files changed, 98 insertions(+), 30 deletions(-) diff --git a/server/routers/api-router.js b/server/routers/api-router.js index 761b488e4..c33ef78c1 100644 --- a/server/routers/api-router.js +++ b/server/routers/api-router.js @@ -11,7 +11,17 @@ const { R } = require("redbean-node"); const apicache = require("../modules/apicache"); const Monitor = require("../model/monitor"); const dayjs = require("dayjs"); -const { UP, MAINTENANCE, DOWN, PENDING, flipStatus, log, badgeConstants } = require("../../src/util"); +const { + UP, + MAINTENANCE, + DOWN, + PENDING, + flipStatus, + log, + badgeConstants, + durationUnits, + isNumeric, +} = require("../../src/util"); const StatusPage = require("../model/status_page"); const { UptimeKumaServer } = require("../uptime-kuma-server"); const { makeBadge } = require("badge-maker"); @@ -232,6 +242,11 @@ router.get("/api/badge/:id/uptime/:duration?", cache("5 minutes"), async (reques let requestedDuration = request.params.duration !== undefined ? request.params.duration : "24"; const overrideValue = value && parseFloat(value); + if (isNumeric(requestedDuration)) { // all numeric only + requestedDuration = `${requestedDuration}${durationUnits.HOUR}`; + } + const duration = requestedDuration.slice(0, -1); + let publicMonitor = await R.getRow(` SELECT monitor_group.monitor_id FROM monitor_group, \`group\` WHERE monitor_group.group_id = \`group\`.id @@ -249,7 +264,7 @@ router.get("/api/badge/:id/uptime/:duration?", cache("5 minutes"), async (reques badgeValues.color = badgeConstants.naColor; } else { const uptimeCalculator = await UptimeCalculator.getUptimeCalculator(requestedMonitorId); - const uptime = overrideValue ?? uptimeCalculator.getDataByDuration(`${requestedDuration}h`).uptime; + const uptime = overrideValue ?? uptimeCalculator.getDataByDuration(requestedDuration).uptime; // limit the displayed uptime percentage to four (two, when displayed as percent) decimal digits const cleanUptime = (uptime * 100).toPrecision(4); @@ -261,7 +276,7 @@ router.get("/api/badge/:id/uptime/:duration?", cache("5 minutes"), async (reques // build a label string. If a custom label is given, override the default one (requestedDuration) badgeValues.label = filterAndJoin([ labelPrefix, - label ?? `Uptime (${requestedDuration}${labelSuffix})`, + label ?? `Uptime (${duration}${labelSuffix})`, ]); badgeValues.message = filterAndJoin([ prefix, cleanUptime, suffix ]); } @@ -298,13 +313,17 @@ router.get("/api/badge/:id/ping/:duration?", cache("5 minutes"), async (request, let requestedDuration = request.params.duration !== undefined ? request.params.duration : "24"; const overrideValue = value && parseFloat(value); + if (isNumeric(requestedDuration)) { // all numeric only + requestedDuration = `${requestedDuration}${durationUnits.HOUR}`; + } + const duration = requestedDuration.slice(0, -1); + // Check if monitor is public const uptimeCalculator = await UptimeCalculator.getUptimeCalculator(requestedMonitorId); - const publicAvgPing = uptimeCalculator.getDataByDuration(`${requestedDuration}h`).avgPing; + const publicAvgPing = uptimeCalculator.getDataByDuration(requestedDuration).avgPing; const badgeValues = { style }; - if (!publicAvgPing) { // return a "N/A" badge in naColor (grey), if monitor is not public / not available / non exsitant @@ -317,7 +336,7 @@ router.get("/api/badge/:id/ping/:duration?", cache("5 minutes"), async (request, // use a given, custom labelColor or use the default badge label color (defined by badge-maker) badgeValues.labelColor = labelColor ?? ""; // build a lable string. If a custom label is given, override the default one (requestedDuration) - badgeValues.label = filterAndJoin([ labelPrefix, label ?? `Avg. Ping (${requestedDuration}${labelSuffix})` ]); + badgeValues.label = filterAndJoin([ labelPrefix, label ?? `Avg. Ping (${duration}${labelSuffix})` ]); badgeValues.message = filterAndJoin([ prefix, avgPing, suffix ]); } diff --git a/server/uptime-calculator.js b/server/uptime-calculator.js index 37dfd6771..df9c0d14f 100644 --- a/server/uptime-calculator.js +++ b/server/uptime-calculator.js @@ -1,5 +1,12 @@ const dayjs = require("dayjs"); -const { UP, MAINTENANCE, DOWN, PENDING } = require("../src/util"); +const { + UP, + MAINTENANCE, + DOWN, + PENDING, + durationUnits, + isNumeric +} = require("../src/util"); const { LimitQueue } = require("./utils/limit-queue"); const { log } = require("../src/util"); const { R } = require("redbean-node"); @@ -543,7 +550,9 @@ class UptimeCalculator { if (type === "minute" && num > 24 * 60) { throw new Error("The maximum number of minutes is 1440"); } - + if (type === "day" && num > 365) { + throw new Error("The maximum number of days is 365"); + } // Get the current time period key based on the type let key = this.getKey(this.getCurrentDate(), type); @@ -742,24 +751,36 @@ class UptimeCalculator { /** * Get the uptime data for given duration. - * @param {string} duration A string with a number and a unit (h, d, or y), such as 24h, 30d, 1y. + * @param {string} duration A string with a number and a unit (m,h,d,w,M,y), such as 24h, 30d, 1y. * @returns {UptimeDataResult} UptimeDataResult - * @throws {Error} Invalid duration + * @throws {Error} Invalid duration / Unsupported unit */ getDataByDuration(duration) { - const unit = duration.substring(duration.length - 1); - const num = parseInt(duration.substring(0, duration.length - 1)); - - const typeMap = { - h: "hour", - d: "day", - y: "year" - }; - - if (!Object.keys(typeMap).includes(unit)) { - throw new Error("Invalid duration"); + const durationNumStr = duration.slice(0, -1); + + if (!isNumeric(durationNumStr)) { + throw new Error(`Invalid duration: ${duration}`); + } + const num = Number(durationNumStr); + const unit = duration.slice(-1); + + switch (unit) { + case durationUnits.MINUTE: + return this.getData(num, "minute"); + case durationUnits.HOUR: + return this.getData(num, "hour"); + case durationUnits.DAY: + return this.getData(num, "day"); + case durationUnits.WEEK: + return this.getData(7 * num, "day"); + case durationUnits.MONTH: + return this.getData(30 * num, "day"); + case durationUnits.YEAR: + return this.getData(365 * num, "day"); + default: + throw new Error(`Unsupported unit (${unit}) for badge duration ${duration}` + ); } - return this.getData(num, typeMap[unit]); } /** diff --git a/src/util.js b/src/util.js index b3706ddbf..63f403dd0 100644 --- a/src/util.js +++ b/src/util.js @@ -10,8 +10,8 @@ */ var _a; Object.defineProperty(exports, "__esModule", { value: true }); -exports.sleep = exports.flipStatus = exports.badgeConstants = exports.CONSOLE_STYLE_BgGray = exports.CONSOLE_STYLE_BgWhite = exports.CONSOLE_STYLE_BgCyan = exports.CONSOLE_STYLE_BgMagenta = exports.CONSOLE_STYLE_BgBlue = exports.CONSOLE_STYLE_BgYellow = exports.CONSOLE_STYLE_BgGreen = exports.CONSOLE_STYLE_BgRed = exports.CONSOLE_STYLE_BgBlack = exports.CONSOLE_STYLE_FgPink = exports.CONSOLE_STYLE_FgBrown = exports.CONSOLE_STYLE_FgViolet = exports.CONSOLE_STYLE_FgLightBlue = exports.CONSOLE_STYLE_FgLightGreen = exports.CONSOLE_STYLE_FgOrange = exports.CONSOLE_STYLE_FgGray = exports.CONSOLE_STYLE_FgWhite = exports.CONSOLE_STYLE_FgCyan = exports.CONSOLE_STYLE_FgMagenta = exports.CONSOLE_STYLE_FgBlue = exports.CONSOLE_STYLE_FgYellow = exports.CONSOLE_STYLE_FgGreen = exports.CONSOLE_STYLE_FgRed = exports.CONSOLE_STYLE_FgBlack = exports.CONSOLE_STYLE_Hidden = exports.CONSOLE_STYLE_Reverse = exports.CONSOLE_STYLE_Blink = exports.CONSOLE_STYLE_Underscore = exports.CONSOLE_STYLE_Dim = exports.CONSOLE_STYLE_Bright = exports.CONSOLE_STYLE_Reset = exports.MIN_INTERVAL_SECOND = exports.MAX_INTERVAL_SECOND = exports.SQL_DATETIME_FORMAT_WITHOUT_SECOND = exports.SQL_DATETIME_FORMAT = exports.SQL_DATE_FORMAT = exports.STATUS_PAGE_MAINTENANCE = exports.STATUS_PAGE_PARTIAL_DOWN = exports.STATUS_PAGE_ALL_UP = exports.STATUS_PAGE_ALL_DOWN = exports.MAINTENANCE = exports.PENDING = exports.UP = exports.DOWN = exports.appName = exports.isNode = exports.isDev = void 0; -exports.intHash = exports.localToUTC = exports.utcToLocal = exports.utcToISODateTime = exports.isoToUTCDateTime = exports.parseTimeFromTimeObject = exports.parseTimeObject = exports.getMaintenanceRelativeURL = exports.getMonitorRelativeURL = exports.genSecret = exports.getCryptoRandomInt = exports.getRandomInt = exports.getRandomArbitrary = exports.TimeLogger = exports.polyfill = exports.log = exports.debug = exports.ucfirst = void 0; +exports.flipStatus = exports.durationUnits = exports.badgeConstants = exports.CONSOLE_STYLE_BgGray = exports.CONSOLE_STYLE_BgWhite = exports.CONSOLE_STYLE_BgCyan = exports.CONSOLE_STYLE_BgMagenta = exports.CONSOLE_STYLE_BgBlue = exports.CONSOLE_STYLE_BgYellow = exports.CONSOLE_STYLE_BgGreen = exports.CONSOLE_STYLE_BgRed = exports.CONSOLE_STYLE_BgBlack = exports.CONSOLE_STYLE_FgPink = exports.CONSOLE_STYLE_FgBrown = exports.CONSOLE_STYLE_FgViolet = exports.CONSOLE_STYLE_FgLightBlue = exports.CONSOLE_STYLE_FgLightGreen = exports.CONSOLE_STYLE_FgOrange = exports.CONSOLE_STYLE_FgGray = exports.CONSOLE_STYLE_FgWhite = exports.CONSOLE_STYLE_FgCyan = exports.CONSOLE_STYLE_FgMagenta = exports.CONSOLE_STYLE_FgBlue = exports.CONSOLE_STYLE_FgYellow = exports.CONSOLE_STYLE_FgGreen = exports.CONSOLE_STYLE_FgRed = exports.CONSOLE_STYLE_FgBlack = exports.CONSOLE_STYLE_Hidden = exports.CONSOLE_STYLE_Reverse = exports.CONSOLE_STYLE_Blink = exports.CONSOLE_STYLE_Underscore = exports.CONSOLE_STYLE_Dim = exports.CONSOLE_STYLE_Bright = exports.CONSOLE_STYLE_Reset = exports.MIN_INTERVAL_SECOND = exports.MAX_INTERVAL_SECOND = exports.SQL_DATETIME_FORMAT_WITHOUT_SECOND = exports.SQL_DATETIME_FORMAT = exports.SQL_DATE_FORMAT = exports.STATUS_PAGE_MAINTENANCE = exports.STATUS_PAGE_PARTIAL_DOWN = exports.STATUS_PAGE_ALL_UP = exports.STATUS_PAGE_ALL_DOWN = exports.MAINTENANCE = exports.PENDING = exports.UP = exports.DOWN = exports.appName = exports.isNode = exports.isDev = void 0; +exports.isNumeric = exports.intHash = exports.localToUTC = exports.utcToLocal = exports.utcToISODateTime = exports.isoToUTCDateTime = exports.parseTimeFromTimeObject = exports.parseTimeObject = exports.getMaintenanceRelativeURL = exports.getMonitorRelativeURL = exports.genSecret = exports.getCryptoRandomInt = exports.getRandomInt = exports.getRandomArbitrary = exports.TimeLogger = exports.polyfill = exports.log = exports.debug = exports.ucfirst = exports.sleep = void 0; const dayjs = require("dayjs"); exports.isDev = process.env.NODE_ENV === "development"; exports.isNode = typeof process !== "undefined" && ((_a = process === null || process === void 0 ? void 0 : process.versions) === null || _a === void 0 ? void 0 : _a.node); @@ -96,6 +96,14 @@ exports.badgeConstants = { defaultCertExpireWarnDays: "14", defaultCertExpireDownDays: "7" }; +exports.durationUnits = { + MINUTE: "m", + HOUR: "h", + DAY: "d", + WEEK: "w", + MONTH: "M", + YEAR: "y" +}; function flipStatus(s) { if (s === exports.UP) { return exports.DOWN; @@ -396,3 +404,7 @@ function intHash(str, length = 10) { return (hash % length + length) % length; } exports.intHash = intHash; +function isNumeric(str) { + return /^([0-9]+)$/.test(str); +} +exports.isNumeric = isNumeric; diff --git a/src/util.ts b/src/util.ts index b4a52f508..75d01c789 100644 --- a/src/util.ts +++ b/src/util.ts @@ -93,11 +93,6 @@ const consoleLevelColors : Record = { "DEBUG": CONSOLE_STYLE_FgGray, }; -/** - * Flip the status of s - * @param s input status: UP or DOWN - * @returns {number} UP or DOWN - */ export const badgeConstants = { naColor: "#999", defaultUpColor: "#66c20a", @@ -118,7 +113,20 @@ export const badgeConstants = { defaultCertExpireDownDays: "7" }; -/** Flip the status of s */ +export const durationUnits = { + MINUTE: "m", + HOUR: "h", + DAY: "d", + WEEK: "w", + MONTH: "M", + YEAR: "y" +}; + +/** + * Flip the status of s + * @param s input status: UP or DOWN + * @returns {number} UP or DOWN + */ export function flipStatus(s: number) { if (s === UP) { return DOWN; @@ -643,3 +651,11 @@ export function intHash(str : string, length = 10) : number { return (hash % length + length) % length; // Ensure the result is non-negative } +/** + * Check if a string is numeric. + * @param {string} str - The input string to check + * @returns {boolean} Returns true if the input string is numeric, false otherwise + */ +export function isNumeric(str: string): boolean { + return /^([0-9]+)$/.test(str); +} From 7270caccae84d8185fd49e92e1eec19506890899 Mon Sep 17 00:00:00 2001 From: Qi Zhao <956361916@qq.com> Date: Thu, 13 Jun 2024 23:28:22 +0800 Subject: [PATCH 3/7] Update server/routers/api-router.js Co-authored-by: Frank Elsinga --- server/routers/api-router.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/routers/api-router.js b/server/routers/api-router.js index c33ef78c1..6bc4b91ee 100644 --- a/server/routers/api-router.js +++ b/server/routers/api-router.js @@ -313,8 +313,8 @@ router.get("/api/badge/:id/ping/:duration?", cache("5 minutes"), async (request, let requestedDuration = request.params.duration !== undefined ? request.params.duration : "24"; const overrideValue = value && parseFloat(value); - if (isNumeric(requestedDuration)) { // all numeric only - requestedDuration = `${requestedDuration}${durationUnits.HOUR}`; + if (/^[0-9]+$/.test(requestedDuration)) { + requestedDuration = `${requestedDuration}h`; } const duration = requestedDuration.slice(0, -1); From f63d36478d55377f36990e6401d95f16ab2dff9b Mon Sep 17 00:00:00 2001 From: Qi Zhao <956361916@qq.com> Date: Thu, 13 Jun 2024 23:28:36 +0800 Subject: [PATCH 4/7] Update server/routers/api-router.js Co-authored-by: Frank Elsinga --- server/routers/api-router.js | 1 - 1 file changed, 1 deletion(-) diff --git a/server/routers/api-router.js b/server/routers/api-router.js index 6bc4b91ee..45e726732 100644 --- a/server/routers/api-router.js +++ b/server/routers/api-router.js @@ -316,7 +316,6 @@ router.get("/api/badge/:id/ping/:duration?", cache("5 minutes"), async (request, if (/^[0-9]+$/.test(requestedDuration)) { requestedDuration = `${requestedDuration}h`; } - const duration = requestedDuration.slice(0, -1); // Check if monitor is public From 3f425dc1606cb6b8d8eaea62ca2b38f2d6e71014 Mon Sep 17 00:00:00 2001 From: Qi Zhao <956361916@qq.com> Date: Thu, 13 Jun 2024 23:28:51 +0800 Subject: [PATCH 5/7] Update server/routers/api-router.js Co-authored-by: Frank Elsinga --- server/routers/api-router.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/routers/api-router.js b/server/routers/api-router.js index 45e726732..bb8b75695 100644 --- a/server/routers/api-router.js +++ b/server/routers/api-router.js @@ -335,7 +335,7 @@ router.get("/api/badge/:id/ping/:duration?", cache("5 minutes"), async (request, // use a given, custom labelColor or use the default badge label color (defined by badge-maker) badgeValues.labelColor = labelColor ?? ""; // build a lable string. If a custom label is given, override the default one (requestedDuration) - badgeValues.label = filterAndJoin([ labelPrefix, label ?? `Avg. Ping (${duration}${labelSuffix})` ]); + badgeValues.label = filterAndJoin([ labelPrefix, label ?? `Avg. Ping (${requestedDuration.slice(0, -1)}${labelSuffix})` ]); badgeValues.message = filterAndJoin([ prefix, avgPing, suffix ]); } From 58533e8f06e9be6e1687d29aee6ff74bc6b2dea1 Mon Sep 17 00:00:00 2001 From: Qi Zhao <956361916@qq.com> Date: Thu, 13 Jun 2024 23:29:05 +0800 Subject: [PATCH 6/7] Update server/routers/api-router.js Co-authored-by: Frank Elsinga --- server/routers/api-router.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/routers/api-router.js b/server/routers/api-router.js index bb8b75695..7817da046 100644 --- a/server/routers/api-router.js +++ b/server/routers/api-router.js @@ -310,7 +310,7 @@ router.get("/api/badge/:id/ping/:duration?", cache("5 minutes"), async (request, const requestedMonitorId = parseInt(request.params.id, 10); // Default duration is 24 (h) if not defined in queryParam, limited to 720h (30d) - let requestedDuration = request.params.duration !== undefined ? request.params.duration : "24"; + let requestedDuration = request.params.duration !== undefined ? request.params.duration : "24h"; const overrideValue = value && parseFloat(value); if (/^[0-9]+$/.test(requestedDuration)) { From 4941b17a46513ca1f7775cedd7a65877cdb23330 Mon Sep 17 00:00:00 2001 From: Qi Zhao Date: Thu, 13 Jun 2024 23:39:12 +0800 Subject: [PATCH 7/7] =?UTF-8?q?perf:=20=E2=9A=A1=EF=B8=8F=20revert=20some?= =?UTF-8?q?=20overengineer=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/routers/api-router.js | 22 ++++++---------------- server/uptime-calculator.js | 23 ++++++++--------------- src/util.js | 16 ++-------------- src/util.ts | 28 ++++++---------------------- 4 files changed, 22 insertions(+), 67 deletions(-) diff --git a/server/routers/api-router.js b/server/routers/api-router.js index 7817da046..ed6db2cd1 100644 --- a/server/routers/api-router.js +++ b/server/routers/api-router.js @@ -11,17 +11,7 @@ const { R } = require("redbean-node"); const apicache = require("../modules/apicache"); const Monitor = require("../model/monitor"); const dayjs = require("dayjs"); -const { - UP, - MAINTENANCE, - DOWN, - PENDING, - flipStatus, - log, - badgeConstants, - durationUnits, - isNumeric, -} = require("../../src/util"); +const { UP, MAINTENANCE, DOWN, PENDING, flipStatus, log, badgeConstants } = require("../../src/util"); const StatusPage = require("../model/status_page"); const { UptimeKumaServer } = require("../uptime-kuma-server"); const { makeBadge } = require("badge-maker"); @@ -239,13 +229,12 @@ router.get("/api/badge/:id/uptime/:duration?", cache("5 minutes"), async (reques try { const requestedMonitorId = parseInt(request.params.id, 10); // if no duration is given, set value to 24 (h) - let requestedDuration = request.params.duration !== undefined ? request.params.duration : "24"; + let requestedDuration = request.params.duration !== undefined ? request.params.duration : "24h"; const overrideValue = value && parseFloat(value); - if (isNumeric(requestedDuration)) { // all numeric only - requestedDuration = `${requestedDuration}${durationUnits.HOUR}`; + if (/^[0-9]+$/.test(requestedDuration)) { + requestedDuration = `${requestedDuration}h`; } - const duration = requestedDuration.slice(0, -1); let publicMonitor = await R.getRow(` SELECT monitor_group.monitor_id FROM monitor_group, \`group\` @@ -276,7 +265,7 @@ router.get("/api/badge/:id/uptime/:duration?", cache("5 minutes"), async (reques // build a label string. If a custom label is given, override the default one (requestedDuration) badgeValues.label = filterAndJoin([ labelPrefix, - label ?? `Uptime (${duration}${labelSuffix})`, + label ?? `Uptime (${requestedDuration.slice(0, -1)}${labelSuffix})`, ]); badgeValues.message = filterAndJoin([ prefix, cleanUptime, suffix ]); } @@ -323,6 +312,7 @@ router.get("/api/badge/:id/ping/:duration?", cache("5 minutes"), async (request, const publicAvgPing = uptimeCalculator.getDataByDuration(requestedDuration).avgPing; const badgeValues = { style }; + if (!publicAvgPing) { // return a "N/A" badge in naColor (grey), if monitor is not public / not available / non exsitant diff --git a/server/uptime-calculator.js b/server/uptime-calculator.js index df9c0d14f..f2738b96a 100644 --- a/server/uptime-calculator.js +++ b/server/uptime-calculator.js @@ -1,12 +1,5 @@ const dayjs = require("dayjs"); -const { - UP, - MAINTENANCE, - DOWN, - PENDING, - durationUnits, - isNumeric -} = require("../src/util"); +const { UP, MAINTENANCE, DOWN, PENDING } = require("../src/util"); const { LimitQueue } = require("./utils/limit-queue"); const { log } = require("../src/util"); const { R } = require("redbean-node"); @@ -758,24 +751,24 @@ class UptimeCalculator { getDataByDuration(duration) { const durationNumStr = duration.slice(0, -1); - if (!isNumeric(durationNumStr)) { + if (!/^[0-9]+$/.test(durationNumStr)) { throw new Error(`Invalid duration: ${duration}`); } const num = Number(durationNumStr); const unit = duration.slice(-1); switch (unit) { - case durationUnits.MINUTE: + case "m": return this.getData(num, "minute"); - case durationUnits.HOUR: + case "h": return this.getData(num, "hour"); - case durationUnits.DAY: + case "d": return this.getData(num, "day"); - case durationUnits.WEEK: + case "w": return this.getData(7 * num, "day"); - case durationUnits.MONTH: + case "M": return this.getData(30 * num, "day"); - case durationUnits.YEAR: + case "y": return this.getData(365 * num, "day"); default: throw new Error(`Unsupported unit (${unit}) for badge duration ${duration}` diff --git a/src/util.js b/src/util.js index 63f403dd0..b3706ddbf 100644 --- a/src/util.js +++ b/src/util.js @@ -10,8 +10,8 @@ */ var _a; Object.defineProperty(exports, "__esModule", { value: true }); -exports.flipStatus = exports.durationUnits = exports.badgeConstants = exports.CONSOLE_STYLE_BgGray = exports.CONSOLE_STYLE_BgWhite = exports.CONSOLE_STYLE_BgCyan = exports.CONSOLE_STYLE_BgMagenta = exports.CONSOLE_STYLE_BgBlue = exports.CONSOLE_STYLE_BgYellow = exports.CONSOLE_STYLE_BgGreen = exports.CONSOLE_STYLE_BgRed = exports.CONSOLE_STYLE_BgBlack = exports.CONSOLE_STYLE_FgPink = exports.CONSOLE_STYLE_FgBrown = exports.CONSOLE_STYLE_FgViolet = exports.CONSOLE_STYLE_FgLightBlue = exports.CONSOLE_STYLE_FgLightGreen = exports.CONSOLE_STYLE_FgOrange = exports.CONSOLE_STYLE_FgGray = exports.CONSOLE_STYLE_FgWhite = exports.CONSOLE_STYLE_FgCyan = exports.CONSOLE_STYLE_FgMagenta = exports.CONSOLE_STYLE_FgBlue = exports.CONSOLE_STYLE_FgYellow = exports.CONSOLE_STYLE_FgGreen = exports.CONSOLE_STYLE_FgRed = exports.CONSOLE_STYLE_FgBlack = exports.CONSOLE_STYLE_Hidden = exports.CONSOLE_STYLE_Reverse = exports.CONSOLE_STYLE_Blink = exports.CONSOLE_STYLE_Underscore = exports.CONSOLE_STYLE_Dim = exports.CONSOLE_STYLE_Bright = exports.CONSOLE_STYLE_Reset = exports.MIN_INTERVAL_SECOND = exports.MAX_INTERVAL_SECOND = exports.SQL_DATETIME_FORMAT_WITHOUT_SECOND = exports.SQL_DATETIME_FORMAT = exports.SQL_DATE_FORMAT = exports.STATUS_PAGE_MAINTENANCE = exports.STATUS_PAGE_PARTIAL_DOWN = exports.STATUS_PAGE_ALL_UP = exports.STATUS_PAGE_ALL_DOWN = exports.MAINTENANCE = exports.PENDING = exports.UP = exports.DOWN = exports.appName = exports.isNode = exports.isDev = void 0; -exports.isNumeric = exports.intHash = exports.localToUTC = exports.utcToLocal = exports.utcToISODateTime = exports.isoToUTCDateTime = exports.parseTimeFromTimeObject = exports.parseTimeObject = exports.getMaintenanceRelativeURL = exports.getMonitorRelativeURL = exports.genSecret = exports.getCryptoRandomInt = exports.getRandomInt = exports.getRandomArbitrary = exports.TimeLogger = exports.polyfill = exports.log = exports.debug = exports.ucfirst = exports.sleep = void 0; +exports.sleep = exports.flipStatus = exports.badgeConstants = exports.CONSOLE_STYLE_BgGray = exports.CONSOLE_STYLE_BgWhite = exports.CONSOLE_STYLE_BgCyan = exports.CONSOLE_STYLE_BgMagenta = exports.CONSOLE_STYLE_BgBlue = exports.CONSOLE_STYLE_BgYellow = exports.CONSOLE_STYLE_BgGreen = exports.CONSOLE_STYLE_BgRed = exports.CONSOLE_STYLE_BgBlack = exports.CONSOLE_STYLE_FgPink = exports.CONSOLE_STYLE_FgBrown = exports.CONSOLE_STYLE_FgViolet = exports.CONSOLE_STYLE_FgLightBlue = exports.CONSOLE_STYLE_FgLightGreen = exports.CONSOLE_STYLE_FgOrange = exports.CONSOLE_STYLE_FgGray = exports.CONSOLE_STYLE_FgWhite = exports.CONSOLE_STYLE_FgCyan = exports.CONSOLE_STYLE_FgMagenta = exports.CONSOLE_STYLE_FgBlue = exports.CONSOLE_STYLE_FgYellow = exports.CONSOLE_STYLE_FgGreen = exports.CONSOLE_STYLE_FgRed = exports.CONSOLE_STYLE_FgBlack = exports.CONSOLE_STYLE_Hidden = exports.CONSOLE_STYLE_Reverse = exports.CONSOLE_STYLE_Blink = exports.CONSOLE_STYLE_Underscore = exports.CONSOLE_STYLE_Dim = exports.CONSOLE_STYLE_Bright = exports.CONSOLE_STYLE_Reset = exports.MIN_INTERVAL_SECOND = exports.MAX_INTERVAL_SECOND = exports.SQL_DATETIME_FORMAT_WITHOUT_SECOND = exports.SQL_DATETIME_FORMAT = exports.SQL_DATE_FORMAT = exports.STATUS_PAGE_MAINTENANCE = exports.STATUS_PAGE_PARTIAL_DOWN = exports.STATUS_PAGE_ALL_UP = exports.STATUS_PAGE_ALL_DOWN = exports.MAINTENANCE = exports.PENDING = exports.UP = exports.DOWN = exports.appName = exports.isNode = exports.isDev = void 0; +exports.intHash = exports.localToUTC = exports.utcToLocal = exports.utcToISODateTime = exports.isoToUTCDateTime = exports.parseTimeFromTimeObject = exports.parseTimeObject = exports.getMaintenanceRelativeURL = exports.getMonitorRelativeURL = exports.genSecret = exports.getCryptoRandomInt = exports.getRandomInt = exports.getRandomArbitrary = exports.TimeLogger = exports.polyfill = exports.log = exports.debug = exports.ucfirst = void 0; const dayjs = require("dayjs"); exports.isDev = process.env.NODE_ENV === "development"; exports.isNode = typeof process !== "undefined" && ((_a = process === null || process === void 0 ? void 0 : process.versions) === null || _a === void 0 ? void 0 : _a.node); @@ -96,14 +96,6 @@ exports.badgeConstants = { defaultCertExpireWarnDays: "14", defaultCertExpireDownDays: "7" }; -exports.durationUnits = { - MINUTE: "m", - HOUR: "h", - DAY: "d", - WEEK: "w", - MONTH: "M", - YEAR: "y" -}; function flipStatus(s) { if (s === exports.UP) { return exports.DOWN; @@ -404,7 +396,3 @@ function intHash(str, length = 10) { return (hash % length + length) % length; } exports.intHash = intHash; -function isNumeric(str) { - return /^([0-9]+)$/.test(str); -} -exports.isNumeric = isNumeric; diff --git a/src/util.ts b/src/util.ts index 75d01c789..b4a52f508 100644 --- a/src/util.ts +++ b/src/util.ts @@ -93,6 +93,11 @@ const consoleLevelColors : Record = { "DEBUG": CONSOLE_STYLE_FgGray, }; +/** + * Flip the status of s + * @param s input status: UP or DOWN + * @returns {number} UP or DOWN + */ export const badgeConstants = { naColor: "#999", defaultUpColor: "#66c20a", @@ -113,20 +118,7 @@ export const badgeConstants = { defaultCertExpireDownDays: "7" }; -export const durationUnits = { - MINUTE: "m", - HOUR: "h", - DAY: "d", - WEEK: "w", - MONTH: "M", - YEAR: "y" -}; - -/** - * Flip the status of s - * @param s input status: UP or DOWN - * @returns {number} UP or DOWN - */ +/** Flip the status of s */ export function flipStatus(s: number) { if (s === UP) { return DOWN; @@ -651,11 +643,3 @@ export function intHash(str : string, length = 10) : number { return (hash % length + length) % length; // Ensure the result is non-negative } -/** - * Check if a string is numeric. - * @param {string} str - The input string to check - * @returns {boolean} Returns true if the input string is numeric, false otherwise - */ -export function isNumeric(str: string): boolean { - return /^([0-9]+)$/.test(str); -}