From 35360e2069b04466dbaee87d522b97736d9d53aa Mon Sep 17 00:00:00 2001 From: Jens Neuber Date: Mon, 3 Jan 2022 15:48:52 +0100 Subject: [PATCH 01/20] add badges --- package-lock.json | 182 ++++++++++++++++++++++++++++++++--- package.json | 2 + server/routers/api-router.js | 62 +++++++++++- server/util-server.js | 10 ++ 4 files changed, 239 insertions(+), 17 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6b6c75cc..219b940f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,6 +17,7 @@ "@popperjs/core": "~2.10.2", "args-parser": "~1.3.0", "axios": "~0.21.4", + "badge-maker": "^3.3.1", "bcryptjs": "~2.4.3", "bootstrap": "5.1.3", "bree": "~7.1.0", @@ -24,6 +25,7 @@ "chart.js": "~3.6.0", "chartjs-adapter-dayjs": "~1.0.0", "check-password-strength": "^2.0.3", + "chroma-js": "^2.1.2", "command-exists": "~1.2.9", "compare-versions": "~3.6.0", "dayjs": "~1.10.7", @@ -3552,6 +3554,14 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/anafanafo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anafanafo/-/anafanafo-2.0.0.tgz", + "integrity": "sha512-Nlfq7NC4AOkTJerWRIZcOAiMNtIDVIGWGvQ98O7Jl6Kr2Dk0dX5u4MqN778kSRTy5KRqchpLdF2RtLFEz9FVkQ==", + "dependencies": { + "char-width-table-consumer": "^1.0.0" + } + }, "node_modules/ansi-colors": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", @@ -4016,6 +4026,22 @@ "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=" }, + "node_modules/badge-maker": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/badge-maker/-/badge-maker-3.3.1.tgz", + "integrity": "sha512-OO/PS7Zg2E6qaUWzHEHt21Q5VjcFBAJVA8ztgT/fIdSZFBUwoyeo0ZhA6V5tUM8Vcjq8DJl6jfGhpjESssyqMQ==", + "dependencies": { + "anafanafo": "2.0.0", + "css-color-converter": "^2.0.0" + }, + "bin": { + "badge": "lib/badge-cli.js" + }, + "engines": { + "node": ">= 10", + "npm": ">= 5" + } + }, "node_modules/bail": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.5.tgz", @@ -4098,6 +4124,11 @@ "node": ">=8" } }, + "node_modules/binary-search": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/binary-search/-/binary-search-1.3.6.tgz", + "integrity": "sha512-nbE1WxOTTrUWIfsfZ4aHGYu5DOuNkbxGokjV6Z2kxfJK3uaAb8zNK1muzOeipoLHZjInT4Br88BHpzevc681xA==" + }, "node_modules/bintrees": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.1.tgz", @@ -4422,6 +4453,14 @@ "node": ">=10" } }, + "node_modules/char-width-table-consumer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/char-width-table-consumer/-/char-width-table-consumer-1.0.0.tgz", + "integrity": "sha512-Fz4UD0LBpxPgL9i29CJ5O4KANwaMnX/OhhbxzvNa332h+9+nRKyeuLw4wA51lt/ex67+/AdsoBQJF3kgX2feYQ==", + "dependencies": { + "binary-search": "^1.3.5" + } + }, "node_modules/character-entities": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz", @@ -4505,6 +4544,29 @@ "node": ">=10" } }, + "node_modules/chroma-js": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/chroma-js/-/chroma-js-2.1.2.tgz", + "integrity": "sha512-ri/ouYDWuxfus3UcaMxC1Tfp3IE9K5iQzxc2hSxbBRVNQFut1UuGAsZmiAf2mOUubzGJwgMSv9lHg+XqLaz1QQ==", + "dependencies": { + "cross-env": "^6.0.3" + } + }, + "node_modules/chroma-js/node_modules/cross-env": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-6.0.3.tgz", + "integrity": "sha512-+KqxF6LCvfhWvADcDPqo64yVIB31gv/jQulX2NGzKS/g3GEVz6/pt4wjHFtFWsHMddebWD/sDthJemzM4MaAag==", + "dependencies": { + "cross-spawn": "^7.0.0" + }, + "bin": { + "cross-env": "src/bin/cross-env.js", + "cross-env-shell": "src/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=8.0" + } + }, "node_modules/ci-info": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.2.0.tgz", @@ -4796,7 +4858,6 @@ "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -4806,6 +4867,31 @@ "node": ">= 8" } }, + "node_modules/css-color-converter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/css-color-converter/-/css-color-converter-2.0.0.tgz", + "integrity": "sha512-oLIG2soZz3wcC3aAl/7Us5RS8Hvvc6I8G8LniF/qfMmrm7fIKQ8RIDDRZeKyGL2SrWfNqYspuLShbnjBMVWm8g==", + "dependencies": { + "color-convert": "^0.5.2", + "color-name": "^1.1.4", + "css-unit-converter": "^1.1.2" + } + }, + "node_modules/css-color-converter/node_modules/color-convert": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-0.5.3.tgz", + "integrity": "sha1-vbbGnOZg+t/+CwAHzER+G59ygr0=" + }, + "node_modules/css-color-converter/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/css-unit-converter": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.2.tgz", + "integrity": "sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA==" + }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -7516,8 +7602,7 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "devOptional": true + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" }, "node_modules/isobject": { "version": "3.0.1", @@ -11074,7 +11159,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, "engines": { "node": ">=8" } @@ -12505,7 +12589,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -12517,7 +12600,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, "engines": { "node": ">=8" } @@ -14401,7 +14483,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "devOptional": true, "dependencies": { "isexe": "^2.0.0" }, @@ -17293,6 +17374,14 @@ "uri-js": "^4.2.2" } }, + "anafanafo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anafanafo/-/anafanafo-2.0.0.tgz", + "integrity": "sha512-Nlfq7NC4AOkTJerWRIZcOAiMNtIDVIGWGvQ98O7Jl6Kr2Dk0dX5u4MqN778kSRTy5KRqchpLdF2RtLFEz9FVkQ==", + "requires": { + "char-width-table-consumer": "^1.0.0" + } + }, "ansi-colors": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", @@ -17659,6 +17748,15 @@ "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=" }, + "badge-maker": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/badge-maker/-/badge-maker-3.3.1.tgz", + "integrity": "sha512-OO/PS7Zg2E6qaUWzHEHt21Q5VjcFBAJVA8ztgT/fIdSZFBUwoyeo0ZhA6V5tUM8Vcjq8DJl6jfGhpjESssyqMQ==", + "requires": { + "anafanafo": "2.0.0", + "css-color-converter": "^2.0.0" + } + }, "bail": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/bail/-/bail-1.0.5.tgz", @@ -17711,6 +17809,11 @@ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true }, + "binary-search": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/binary-search/-/binary-search-1.3.6.tgz", + "integrity": "sha512-nbE1WxOTTrUWIfsfZ4aHGYu5DOuNkbxGokjV6Z2kxfJK3uaAb8zNK1muzOeipoLHZjInT4Br88BHpzevc681xA==" + }, "bintrees": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.1.tgz", @@ -17963,6 +18066,14 @@ "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", "dev": true }, + "char-width-table-consumer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/char-width-table-consumer/-/char-width-table-consumer-1.0.0.tgz", + "integrity": "sha512-Fz4UD0LBpxPgL9i29CJ5O4KANwaMnX/OhhbxzvNa332h+9+nRKyeuLw4wA51lt/ex67+/AdsoBQJF3kgX2feYQ==", + "requires": { + "binary-search": "^1.3.5" + } + }, "character-entities": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-1.2.4.tgz", @@ -18023,6 +18134,24 @@ "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" }, + "chroma-js": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/chroma-js/-/chroma-js-2.1.2.tgz", + "integrity": "sha512-ri/ouYDWuxfus3UcaMxC1Tfp3IE9K5iQzxc2hSxbBRVNQFut1UuGAsZmiAf2mOUubzGJwgMSv9lHg+XqLaz1QQ==", + "requires": { + "cross-env": "^6.0.3" + }, + "dependencies": { + "cross-env": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-6.0.3.tgz", + "integrity": "sha512-+KqxF6LCvfhWvADcDPqo64yVIB31gv/jQulX2NGzKS/g3GEVz6/pt4wjHFtFWsHMddebWD/sDthJemzM4MaAag==", + "requires": { + "cross-spawn": "^7.0.0" + } + } + } + }, "ci-info": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.2.0.tgz", @@ -18258,13 +18387,39 @@ "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, "requires": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, + "css-color-converter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/css-color-converter/-/css-color-converter-2.0.0.tgz", + "integrity": "sha512-oLIG2soZz3wcC3aAl/7Us5RS8Hvvc6I8G8LniF/qfMmrm7fIKQ8RIDDRZeKyGL2SrWfNqYspuLShbnjBMVWm8g==", + "requires": { + "color-convert": "^0.5.2", + "color-name": "^1.1.4", + "css-unit-converter": "^1.1.2" + }, + "dependencies": { + "color-convert": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-0.5.3.tgz", + "integrity": "sha1-vbbGnOZg+t/+CwAHzER+G59ygr0=" + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + } + } + }, + "css-unit-converter": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.2.tgz", + "integrity": "sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA==" + }, "cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -20275,8 +20430,7 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "devOptional": true + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" }, "isobject": { "version": "3.0.1", @@ -22957,8 +23111,7 @@ "path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" }, "path-parse": { "version": "1.0.7", @@ -24042,7 +24195,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "requires": { "shebang-regex": "^3.0.0" } @@ -24050,8 +24202,7 @@ "shebang-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" }, "signal-exit": { "version": "3.0.6", @@ -25468,7 +25619,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "devOptional": true, "requires": { "isexe": "^2.0.0" } diff --git a/package.json b/package.json index 32c51176..cc9d005f 100644 --- a/package.json +++ b/package.json @@ -62,6 +62,7 @@ "@popperjs/core": "~2.10.2", "args-parser": "~1.3.0", "axios": "~0.21.4", + "badge-maker": "^3.3.1", "bcryptjs": "~2.4.3", "bootstrap": "5.1.3", "bree": "~7.1.0", @@ -69,6 +70,7 @@ "chart.js": "~3.6.0", "chartjs-adapter-dayjs": "~1.0.0", "check-password-strength": "^2.0.3", + "chroma-js": "^2.1.2", "command-exists": "~1.2.9", "compare-versions": "~3.6.0", "dayjs": "~1.10.7", diff --git a/server/routers/api-router.js b/server/routers/api-router.js index 1920cef7..8573226f 100644 --- a/server/routers/api-router.js +++ b/server/routers/api-router.js @@ -1,11 +1,12 @@ let express = require("express"); -const { allowDevAllOrigin, getSettings, setting } = require("../util-server"); +const { allowDevAllOrigin, getSettings, setting, percentageToColor } = require("../util-server"); const { R } = require("redbean-node"); const server = require("../server"); const apicache = require("../modules/apicache"); const Monitor = require("../model/monitor"); const dayjs = require("dayjs"); const { UP, flipStatus, debug } = require("../../src/util"); +const { makeBadge } = require("badge-maker"); let router = express.Router(); let cache = apicache.middleware; @@ -214,6 +215,65 @@ router.get("/api/status-page/heartbeat", cache("5 minutes"), async (_request, re } }); +router.get("/api/badge/:id/:type", cache("5 minutes"), async (request, response) => { + allowDevAllOrigin(response); + + const { + label, + labelPrefix, + labelSuffix = "h", + prefix, + suffix, + } = request.query; + + try { + await checkPublished(); + + const requestedMonitorId = parseInt(request.params.id, 10); + const requestedType = parseInt(request.params.type, 10) ?? 24; + + let publicMonitor = await R.getRow(` + SELECT monitor_group.monitor_id FROM monitor_group, \`group\` + WHERE monitor_group.group_id = \`group\`.id + AND monitor_group.monitor_id = ? + AND public = 1 + `, + [requestedMonitorId] + ); + + const badgeValues = {}; + + if (!publicMonitor) { + badgeValues.message = "N/A"; + badgeValues.color = "#CCCCCC"; + } else { + const uptime = await Monitor.calcUptime( + requestedType, + requestedMonitorId + ); + + badgeValues.color = percentageToColor(uptime); + + badgeValues.label = [labelPrefix, label ?? requestedType, labelSuffix] + .filter((part) => part ?? part !== "") + .join(""); + + badgeValues.message = [prefix, `${uptime * 100} %`, suffix] + .filter((part) => part ?? part !== "") + .join(""); + + } + + const svg = makeBadge(badgeValues); + + response.type("image/svg+xml"); + response.send(svg); + } catch (error) { + send403(response, error.message); + } +} +); + async function checkPublished() { if (! await isPublished()) { throw new Error("The status page is not published"); diff --git a/server/util-server.js b/server/util-server.js index 68f59f67..3f291893 100644 --- a/server/util-server.js +++ b/server/util-server.js @@ -10,6 +10,7 @@ const iconv = require("iconv-lite"); const chardet = require("chardet"); const fs = require("fs"); const nodeJsUtil = require("util"); +const chroma = require("chroma-js"); // From ping-lite exports.WIN = /^win/.test(process.platform); @@ -370,3 +371,12 @@ exports.errorLog = (error, outputToConsole = true) => { } } catch (_) { } }; + +exports.percentageToColor = (percentage, maxHue = 90, minHue = 10) => { + const hue = percentage * (maxHue - minHue) + minHue; + try { + return chroma(`hsl(${hue}, 90%, 40%)`).hex(); + } catch (err) { + return "grey"; + } +}; From 1c5bce8afa4f37f88a1495ae57b60209c2fe9180 Mon Sep 17 00:00:00 2001 From: Jens Neuber Date: Mon, 3 Jan 2022 16:04:37 +0100 Subject: [PATCH 02/20] a little documentation --- server/routers/api-router.js | 4 +++- server/util-server.js | 10 ++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/server/routers/api-router.js b/server/routers/api-router.js index 8573226f..d5f8aaa7 100644 --- a/server/routers/api-router.js +++ b/server/routers/api-router.js @@ -244,6 +244,8 @@ router.get("/api/badge/:id/:type", cache("5 minutes"), async (request, response) const badgeValues = {}; if (!publicMonitor) { + // return a "n/a" badge in grey, if monitor is not public / not available / non exsitant + badgeValues.message = "N/A"; badgeValues.color = "#CCCCCC"; } else { @@ -261,9 +263,9 @@ router.get("/api/badge/:id/:type", cache("5 minutes"), async (request, response) badgeValues.message = [prefix, `${uptime * 100} %`, suffix] .filter((part) => part ?? part !== "") .join(""); - } + // build the svg based on given values const svg = makeBadge(badgeValues); response.type("image/svg+xml"); diff --git a/server/util-server.js b/server/util-server.js index 3f291893..838e597d 100644 --- a/server/util-server.js +++ b/server/util-server.js @@ -372,6 +372,16 @@ exports.errorLog = (error, outputToConsole = true) => { } catch (_) { } }; +/** + * Returns a color code in hex format based on a given percentage: + * 0% => hue = 10 => red + * 100% => hue = 90 => green + * + * @param {number} percentage, float, 0 to 1 + * @param {number} maxHue, int + * @param {number} minHue, int + * @returns {string}, hex value + */ exports.percentageToColor = (percentage, maxHue = 90, minHue = 10) => { const hue = percentage * (maxHue - minHue) + minHue; try { From d74404e106773fd3fe6cd82b9cfe167664503eab Mon Sep 17 00:00:00 2001 From: Jens Neuber Date: Mon, 3 Jan 2022 16:23:23 +0100 Subject: [PATCH 03/20] minor fixes --- server/routers/api-router.js | 2 +- server/util-server.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/server/routers/api-router.js b/server/routers/api-router.js index d5f8aaa7..e36f1f26 100644 --- a/server/routers/api-router.js +++ b/server/routers/api-router.js @@ -247,7 +247,7 @@ router.get("/api/badge/:id/:type", cache("5 minutes"), async (request, response) // return a "n/a" badge in grey, if monitor is not public / not available / non exsitant badgeValues.message = "N/A"; - badgeValues.color = "#CCCCCC"; + badgeValues.color = "#999"; } else { const uptime = await Monitor.calcUptime( requestedType, diff --git a/server/util-server.js b/server/util-server.js index 838e597d..55b368a1 100644 --- a/server/util-server.js +++ b/server/util-server.js @@ -387,6 +387,6 @@ exports.percentageToColor = (percentage, maxHue = 90, minHue = 10) => { try { return chroma(`hsl(${hue}, 90%, 40%)`).hex(); } catch (err) { - return "grey"; + return "#999"; } }; From 3625915a8504deac161f805d5ed0b9ae46c84c58 Mon Sep 17 00:00:00 2001 From: Jens Neuber Date: Tue, 4 Jan 2022 12:21:53 +0100 Subject: [PATCH 04/20] add ping, status badge --- server/config.js | 10 ++- server/routers/api-router.js | 158 +++++++++++++++++++++++++++++++---- server/util-server.js | 3 +- 3 files changed, 152 insertions(+), 19 deletions(-) diff --git a/server/config.js b/server/config.js index 24ccfaa1..8a9a67f8 100644 --- a/server/config.js +++ b/server/config.js @@ -1,7 +1,15 @@ const args = require("args-parser")(process.argv); const demoMode = args["demo"] || false; +const badgeConstants = { + naColor: "#999", + defaultUpColor: "#66c20a", + defaultDownColor: "#c2290a", + defaultPingColor: "blue", // as defined by badge-maker / shields.io +}; + module.exports = { args, - demoMode + demoMode, + badgeConstants }; diff --git a/server/routers/api-router.js b/server/routers/api-router.js index e36f1f26..a68ea050 100644 --- a/server/routers/api-router.js +++ b/server/routers/api-router.js @@ -1,5 +1,5 @@ let express = require("express"); -const { allowDevAllOrigin, getSettings, setting, percentageToColor } = require("../util-server"); +const { allowDevAllOrigin, getSettings, setting, percentageToColor, allowAllOrigin } = require("../util-server"); const { R } = require("redbean-node"); const server = require("../server"); const apicache = require("../modules/apicache"); @@ -7,6 +7,7 @@ const Monitor = require("../model/monitor"); const dayjs = require("dayjs"); const { UP, flipStatus, debug } = require("../../src/util"); const { makeBadge } = require("badge-maker"); +const { badgeConstants } = require("../config"); let router = express.Router(); let cache = apicache.middleware; @@ -215,22 +216,23 @@ router.get("/api/status-page/heartbeat", cache("5 minutes"), async (_request, re } }); -router.get("/api/badge/:id/:type", cache("5 minutes"), async (request, response) => { - allowDevAllOrigin(response); +router.get("/api/badge/:id/status", cache("5 minutes"), async (request, response) => { + allowAllOrigin(response); const { label, - labelPrefix, - labelSuffix = "h", - prefix, - suffix, + upLabel = "Up", + downLabel = "Down", + upColor = badgeConstants.defaultUpColor, + downColor = badgeConstants.defaultDownColor, + value // for demo purpose only } = request.query; try { await checkPublished(); const requestedMonitorId = parseInt(request.params.id, 10); - const requestedType = parseInt(request.params.type, 10) ?? 24; + const overrideValue = value !== undefined ? parseInt(value) : undefined; let publicMonitor = await R.getRow(` SELECT monitor_group.monitor_id FROM monitor_group, \`group\` @@ -244,23 +246,146 @@ router.get("/api/badge/:id/:type", cache("5 minutes"), async (request, response) const badgeValues = {}; if (!publicMonitor) { - // return a "n/a" badge in grey, if monitor is not public / not available / non exsitant + // return a "N/A" badge in naColor (grey), if monitor is not public / not available / non exsitant badgeValues.message = "N/A"; - badgeValues.color = "#999"; + badgeValues.color = badgeConstants.naColor; } else { - const uptime = await Monitor.calcUptime( - requestedType, + const heartbeat = await Monitor.getPreviousHeartbeat(requestedMonitorId); + const state = overrideValue !== undefined ? overrideValue : heartbeat.status === 1; + + badgeValues.color = state ? upColor : downColor; + badgeValues.message = label ?? state ? upLabel : downLabel; + } + + // build the svg based on given values + const svg = makeBadge(badgeValues); + + response.type("image/svg+xml"); + response.send(svg); + } catch (error) { + send403(response, error.message); + } +}); + +router.get("/api/badge/:id/uptime/:duration?", cache("5 minutes"), async (request, response) => { + allowAllOrigin(response); + + const { + label, + labelPrefix, + labelSuffix = "h", + prefix, + suffix = "%", + color, + labelColor, + value // for demo purpose only + } = request.query; + + try { + await checkPublished(); + + const requestedMonitorId = parseInt(request.params.id, 10); + // if no duration is given, set value to 24 (h) + const requestedDuration = request.params.duration !== undefined ? parseInt(request.params.duration, 10) : 24; + const overrideValue = value && parseFloat(value); + + let publicMonitor = await R.getRow(` + SELECT monitor_group.monitor_id FROM monitor_group, \`group\` + WHERE monitor_group.group_id = \`group\`.id + AND monitor_group.monitor_id = ? + AND public = 1 + `, + [requestedMonitorId] + ); + + const badgeValues = {}; + + if (!publicMonitor) { + // return a "N/A" badge in naColor (grey), if monitor is not public / not available / non exsitant + + badgeValues.message = "N/A"; + badgeValues.color = badgeConstants.naColor; + } else { + const uptime = overrideValue ?? await Monitor.calcUptime( + requestedDuration, requestedMonitorId ); - badgeValues.color = percentageToColor(uptime); + badgeValues.color = color ?? percentageToColor(uptime); + badgeValues.labelColor = labelColor ?? ""; - badgeValues.label = [labelPrefix, label ?? requestedType, labelSuffix] + badgeValues.label = [labelPrefix, label ?? requestedDuration, labelSuffix] .filter((part) => part ?? part !== "") .join(""); - badgeValues.message = [prefix, `${uptime * 100} %`, suffix] + badgeValues.message = [prefix, `${uptime * 100}`, suffix] + .filter((part) => part ?? part !== "") + .join(""); + } + + // build the SVG based on given values + const svg = makeBadge(badgeValues); + + response.type("image/svg+xml"); + response.send(svg); + } catch (error) { + send403(response, error.message); + } +}); + +router.get("/api/badge/:id/ping/:duration?", cache("5 minutes"), async (request, response) => { + allowAllOrigin(response); + + const { + label, + labelPrefix, + labelSuffix = "h", + prefix, + suffix = "ms", + color = badgeConstants.defaultPingColor, + labelColor, + value + } = request.query; + + try { + await checkPublished(); + + const requestedMonitorId = parseInt(request.params.id, 10); + + // Default duration is 24 (h) if not defined in queryParam, limited to 720h (30d) + const requestedDuration = Math.min(request.params.duration ? parseInt(request.params.duration, 10) : 24, 720); + const overrideValue = value && parseFloat(value); + + const publicAvgPing = parseInt(await R.getCell(` + SELECT AVG(ping) FROM monitor_group, \`group\`, heartbeat + WHERE monitor_group.group_id = \`group\`.id + AND heartbeat.time > DATETIME('now', ? || ' hours') + AND heartbeat.ping IS NOT NULL + AND public = 1 + AND heartbeat.monitor_id = ? + `, + [-requestedDuration, requestedMonitorId] + )); + + const badgeValues = {}; + + if (!publicAvgPing) { + // return a "N/A" badge in naColor (grey), if monitor is not public / not available / non exsitant + + badgeValues.message = "N/A"; + badgeValues.color = badgeConstants.naColor; + } else { + const avgPing = parseInt(overrideValue ?? publicAvgPing); + + badgeValues.color = color; + badgeValues.labelColor = labelColor ?? ""; + + badgeValues.label = [labelPrefix, label ?? requestedDuration, labelSuffix] + .filter((part) => part ?? part !== "") + .join(""); + + badgeValues.message = [prefix, avgPing, suffix] .filter((part) => part ?? part !== "") .join(""); } @@ -273,8 +398,7 @@ router.get("/api/badge/:id/:type", cache("5 minutes"), async (request, response) } catch (error) { send403(response, error.message); } -} -); +}); async function checkPublished() { if (! await isPublished()) { diff --git a/server/util-server.js b/server/util-server.js index 55b368a1..0bcc4dd5 100644 --- a/server/util-server.js +++ b/server/util-server.js @@ -11,6 +11,7 @@ const chardet = require("chardet"); const fs = require("fs"); const nodeJsUtil = require("util"); const chroma = require("chroma-js"); +const { badgeConstants } = require("./config"); // From ping-lite exports.WIN = /^win/.test(process.platform); @@ -387,6 +388,6 @@ exports.percentageToColor = (percentage, maxHue = 90, minHue = 10) => { try { return chroma(`hsl(${hue}, 90%, 40%)`).hex(); } catch (err) { - return "#999"; + return badgeConstants.naColor; } }; From 7abbf421d02cdb076e7dda1a4e03d8f055c739ac Mon Sep 17 00:00:00 2001 From: Jens Neuber Date: Tue, 4 Jan 2022 12:23:16 +0100 Subject: [PATCH 05/20] PR feedback --- 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 a68ea050..1bd1c13b 100644 --- a/server/routers/api-router.js +++ b/server/routers/api-router.js @@ -390,7 +390,7 @@ router.get("/api/badge/:id/ping/:duration?", cache("5 minutes"), async (request, .join(""); } - // build the svg based on given values + // build the SVG based on given values const svg = makeBadge(badgeValues); response.type("image/svg+xml"); From f455e3a45439c516e0bf5295bdc8bd50ce450dcd Mon Sep 17 00:00:00 2001 From: Jens Neuber Date: Tue, 4 Jan 2022 13:40:53 +0100 Subject: [PATCH 06/20] add shields.io 'style' parameter --- server/routers/api-router.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/server/routers/api-router.js b/server/routers/api-router.js index 1bd1c13b..8264800b 100644 --- a/server/routers/api-router.js +++ b/server/routers/api-router.js @@ -225,6 +225,7 @@ router.get("/api/badge/:id/status", cache("5 minutes"), async (request, response downLabel = "Down", upColor = badgeConstants.defaultUpColor, downColor = badgeConstants.defaultDownColor, + style = "flat", value // for demo purpose only } = request.query; @@ -243,7 +244,7 @@ router.get("/api/badge/:id/status", cache("5 minutes"), async (request, response [requestedMonitorId] ); - const badgeValues = {}; + const badgeValues = { style }; if (!publicMonitor) { // return a "N/A" badge in naColor (grey), if monitor is not public / not available / non exsitant @@ -279,6 +280,7 @@ router.get("/api/badge/:id/uptime/:duration?", cache("5 minutes"), async (reques suffix = "%", color, labelColor, + style = "flat", value // for demo purpose only } = request.query; @@ -299,7 +301,7 @@ router.get("/api/badge/:id/uptime/:duration?", cache("5 minutes"), async (reques [requestedMonitorId] ); - const badgeValues = {}; + const badgeValues = { style }; if (!publicMonitor) { // return a "N/A" badge in naColor (grey), if monitor is not public / not available / non exsitant @@ -345,7 +347,8 @@ router.get("/api/badge/:id/ping/:duration?", cache("5 minutes"), async (request, suffix = "ms", color = badgeConstants.defaultPingColor, labelColor, - value + style = "flat", + value // for demo purpose only } = request.query; try { @@ -368,7 +371,7 @@ router.get("/api/badge/:id/ping/:duration?", cache("5 minutes"), async (request, [-requestedDuration, requestedMonitorId] )); - const badgeValues = {}; + const badgeValues = { style }; if (!publicAvgPing) { // return a "N/A" badge in naColor (grey), if monitor is not public / not available / non exsitant From 6acc9546a00bb14d2a5bb3f721238072cae327b3 Mon Sep 17 00:00:00 2001 From: Jens Neuber Date: Tue, 4 Jan 2022 16:00:21 +0100 Subject: [PATCH 07/20] PR feedback + remove redundant code + add a test --- server/config.js | 5 ++++ server/routers/api-router.js | 48 +++++++++++++++--------------------- server/util-server.js | 11 +++++++++ test/backend.spec.js | 22 +++++++++++++++++ 4 files changed, 58 insertions(+), 28 deletions(-) diff --git a/server/config.js b/server/config.js index 8a9a67f8..f4776507 100644 --- a/server/config.js +++ b/server/config.js @@ -6,6 +6,11 @@ const badgeConstants = { defaultUpColor: "#66c20a", defaultDownColor: "#c2290a", defaultPingColor: "blue", // as defined by badge-maker / shields.io + defaultStyle: "flat", + defaultPingValueSuffix: "ms", + defaultPingLabelSuffix: "h", + defaultUptimeValueSuffix: "%", + defaultUptimeLabelSuffix: "h", }; module.exports = { diff --git a/server/routers/api-router.js b/server/routers/api-router.js index 8264800b..6e0a2dec 100644 --- a/server/routers/api-router.js +++ b/server/routers/api-router.js @@ -1,5 +1,5 @@ let express = require("express"); -const { allowDevAllOrigin, getSettings, setting, percentageToColor, allowAllOrigin } = require("../util-server"); +const { allowDevAllOrigin, getSettings, setting, percentageToColor, allowAllOrigin, filterAndJoin } = require("../util-server"); const { R } = require("redbean-node"); const server = require("../server"); const apicache = require("../modules/apicache"); @@ -225,8 +225,8 @@ router.get("/api/badge/:id/status", cache("5 minutes"), async (request, response downLabel = "Down", upColor = badgeConstants.defaultUpColor, downColor = badgeConstants.defaultDownColor, - style = "flat", - value // for demo purpose only + style = badgeConstants.defaultStyle, + value, // for demo purpose only } = request.query; try { @@ -275,13 +275,13 @@ router.get("/api/badge/:id/uptime/:duration?", cache("5 minutes"), async (reques const { label, labelPrefix, - labelSuffix = "h", + labelSuffix = badgeConstants.defaultUptimeLabelSuffix, prefix, - suffix = "%", + suffix = badgeConstants.defaultUptimeValueSuffix, color, labelColor, - style = "flat", - value // for demo purpose only + style = badgeConstants.defaultStyle, + value, // for demo purpose only } = request.query; try { @@ -305,7 +305,6 @@ router.get("/api/badge/:id/uptime/:duration?", cache("5 minutes"), async (reques if (!publicMonitor) { // return a "N/A" badge in naColor (grey), if monitor is not public / not available / non exsitant - badgeValues.message = "N/A"; badgeValues.color = badgeConstants.naColor; } else { @@ -314,16 +313,13 @@ router.get("/api/badge/:id/uptime/:duration?", cache("5 minutes"), async (reques requestedMonitorId ); + // use a given, custom color or calculate one based on the uptime value badgeValues.color = color ?? percentageToColor(uptime); + // use a given, custom labelColor or use the default badge label color ( defined by badge-maker) badgeValues.labelColor = labelColor ?? ""; - - badgeValues.label = [labelPrefix, label ?? requestedDuration, labelSuffix] - .filter((part) => part ?? part !== "") - .join(""); - - badgeValues.message = [prefix, `${uptime * 100}`, suffix] - .filter((part) => part ?? part !== "") - .join(""); + // build a lable string. If a custom label is given, override the default one ( requestedDuration ) + badgeValues.label = filterAndJoin([labelPrefix, label ?? requestedDuration, labelSuffix]); + badgeValues.message = filterAndJoin([prefix, `${uptime * 100}`, suffix]); } // build the SVG based on given values @@ -342,13 +338,13 @@ router.get("/api/badge/:id/ping/:duration?", cache("5 minutes"), async (request, const { label, labelPrefix, - labelSuffix = "h", + labelSuffix = badgeConstants.defaultPingLabelSuffix, prefix, - suffix = "ms", + suffix = badgeConstants.defaultPingValueSuffix, color = badgeConstants.defaultPingColor, labelColor, - style = "flat", - value // for demo purpose only + style = badgeConstants.defaultStyle, + value, // for demo purpose only } = request.query; try { @@ -382,15 +378,11 @@ router.get("/api/badge/:id/ping/:duration?", cache("5 minutes"), async (request, const avgPing = parseInt(overrideValue ?? publicAvgPing); badgeValues.color = color; + // use a given, custom labelColor or use the default badge label color ( defined by badge-maker) badgeValues.labelColor = labelColor ?? ""; - - badgeValues.label = [labelPrefix, label ?? requestedDuration, labelSuffix] - .filter((part) => part ?? part !== "") - .join(""); - - badgeValues.message = [prefix, avgPing, suffix] - .filter((part) => part ?? part !== "") - .join(""); + // build a lable string. If a custom label is given, override the default one ( requestedDuration ) + badgeValues.label = filterAndJoin([labelPrefix, label ?? requestedDuration, labelSuffix]); + badgeValues.message = filterAndJoin([prefix, avgPing, suffix]); } // build the SVG based on given values diff --git a/server/util-server.js b/server/util-server.js index 0bcc4dd5..8cc92fac 100644 --- a/server/util-server.js +++ b/server/util-server.js @@ -391,3 +391,14 @@ exports.percentageToColor = (percentage, maxHue = 90, minHue = 10) => { return badgeConstants.naColor; } }; + +/** + * Joins and array of string to one string after filtering out empty values + * + * @param {string[]} parts + * @param {string} connector + * @returns {string} + */ +exports.filterAndJoin = (parts, connector = "") => { + return parts.filter((part) => !!part && part !== "").join(connector); +}; diff --git a/test/backend.spec.js b/test/backend.spec.js index bbfc6897..220b5f35 100644 --- a/test/backend.spec.js +++ b/test/backend.spec.js @@ -164,3 +164,25 @@ describe("Test reset-password", () => { }, 120000); }); +describe("The function filterAndJoin", () => { + it("should join and array of strings to one string", () => { + const result = utilServerRewire.filterAndJoin(["one", "two", "three"]); + expect(result).toBe("onetwothree"); + }); + + it("should join strings using a given connector", () => { + const result = utilServerRewire.filterAndJoin(["one", "two", "three"], "-"); + expect(result).toBe("one-two-three"); + }); + + it("should filter null, undefined and empty strings before joining", () => { + const result = utilServerRewire.filterAndJoin([undefined, "", "three"], "--"); + expect(result).toBe("three"); + }); + + it("should return an empty string if all parts are filtered out", () => { + const result = utilServerRewire.filterAndJoin([undefined, "", ""], "--"); + expect(result).toBe(""); + }); +}); + From 28c0e16a0ca569770036c635bdb15a6958e9a017 Mon Sep 17 00:00:00 2001 From: Jens Neuber Date: Tue, 4 Jan 2022 16:01:40 +0100 Subject: [PATCH 08/20] PR feedback --- server/config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/config.js b/server/config.js index f4776507..d46f24b7 100644 --- a/server/config.js +++ b/server/config.js @@ -16,5 +16,5 @@ const badgeConstants = { module.exports = { args, demoMode, - badgeConstants + badgeConstants, }; From df8f93f0c206b2e34b1c6c29f420e1283cb2fb6b Mon Sep 17 00:00:00 2001 From: Jens Neuber Date: Wed, 5 Jan 2022 11:48:25 +0100 Subject: [PATCH 09/20] clean uptime percentage display --- server/routers/api-router.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/server/routers/api-router.js b/server/routers/api-router.js index 6e0a2dec..3b8ebe82 100644 --- a/server/routers/api-router.js +++ b/server/routers/api-router.js @@ -313,13 +313,16 @@ router.get("/api/badge/:id/uptime/:duration?", cache("5 minutes"), async (reques requestedMonitorId ); + // limit the displayed uptime percentage to four ( two, when displayed as percent ) decimal digits + const cleanUptime = parseFloat(uptime.toPrecision(4)); + // use a given, custom color or calculate one based on the uptime value badgeValues.color = color ?? percentageToColor(uptime); // 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 ?? requestedDuration, labelSuffix]); - badgeValues.message = filterAndJoin([prefix, `${uptime * 100}`, suffix]); + badgeValues.message = filterAndJoin([prefix, `${cleanUptime * 100}`, suffix]); } // build the SVG based on given values From a9d264ccfc1c8c1c350d0b301650b689af0c74a9 Mon Sep 17 00:00:00 2001 From: Jens Neuber Date: Wed, 5 Jan 2022 15:25:42 +0100 Subject: [PATCH 10/20] PR feedback: remove spaces in comments Co-authored-by: Adam Stachowicz --- 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 3b8ebe82..45d416a8 100644 --- a/server/routers/api-router.js +++ b/server/routers/api-router.js @@ -313,7 +313,7 @@ router.get("/api/badge/:id/uptime/:duration?", cache("5 minutes"), async (reques requestedMonitorId ); - // limit the displayed uptime percentage to four ( two, when displayed as percent ) decimal digits + // limit the displayed uptime percentage to four (two, when displayed as percent) decimal digits const cleanUptime = parseFloat(uptime.toPrecision(4)); // use a given, custom color or calculate one based on the uptime value From faf6719e7c3ea3dc3981878db1dd983b4d1b4dd3 Mon Sep 17 00:00:00 2001 From: Jens Neuber Date: Wed, 5 Jan 2022 15:25:56 +0100 Subject: [PATCH 11/20] PR feedback: remove spaces in parenthesis Co-authored-by: Adam Stachowicz --- 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 45d416a8..4ba51dbf 100644 --- a/server/routers/api-router.js +++ b/server/routers/api-router.js @@ -318,7 +318,7 @@ router.get("/api/badge/:id/uptime/:duration?", cache("5 minutes"), async (reques // use a given, custom color or calculate one based on the uptime value badgeValues.color = color ?? percentageToColor(uptime); - // use a given, custom labelColor or use the default badge label color ( defined by badge-maker) + // 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 ?? requestedDuration, labelSuffix]); From 499042504fae637ae745eaf53f00c676b3db1160 Mon Sep 17 00:00:00 2001 From: Jens Neuber Date: Wed, 5 Jan 2022 15:26:07 +0100 Subject: [PATCH 12/20] PR feedback: remove spaces in parenthesis Co-authored-by: Adam Stachowicz --- 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 4ba51dbf..18da6b47 100644 --- a/server/routers/api-router.js +++ b/server/routers/api-router.js @@ -320,7 +320,7 @@ router.get("/api/badge/:id/uptime/:duration?", cache("5 minutes"), async (reques badgeValues.color = color ?? percentageToColor(uptime); // 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 ) + // build a lable string. If a custom label is given, override the default one (requestedDuration) badgeValues.label = filterAndJoin([labelPrefix, label ?? requestedDuration, labelSuffix]); badgeValues.message = filterAndJoin([prefix, `${cleanUptime * 100}`, suffix]); } From 43f8fc701c2ea779652107a4b801bd6ed6946b84 Mon Sep 17 00:00:00 2001 From: Jens Neuber Date: Wed, 5 Jan 2022 15:26:23 +0100 Subject: [PATCH 13/20] PR feedback: remove spaces in parenthesis Co-authored-by: Adam Stachowicz --- 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 18da6b47..81423407 100644 --- a/server/routers/api-router.js +++ b/server/routers/api-router.js @@ -381,7 +381,7 @@ router.get("/api/badge/:id/ping/:duration?", cache("5 minutes"), async (request, const avgPing = parseInt(overrideValue ?? publicAvgPing); badgeValues.color = color; - // use a given, custom labelColor or use the default badge label color ( defined by badge-maker) + // 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 ?? requestedDuration, labelSuffix]); From f00ec4dfef0f9be5174c170c17ac0d6d13a6c6e4 Mon Sep 17 00:00:00 2001 From: Jens Neuber Date: Wed, 5 Jan 2022 15:26:29 +0100 Subject: [PATCH 14/20] PR feedback: remove spaces in parenthesis Co-authored-by: Adam Stachowicz --- 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 81423407..42bf5fed 100644 --- a/server/routers/api-router.js +++ b/server/routers/api-router.js @@ -383,7 +383,7 @@ router.get("/api/badge/:id/ping/:duration?", cache("5 minutes"), async (request, badgeValues.color = color; // 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 ) + // build a lable string. If a custom label is given, override the default one (requestedDuration) badgeValues.label = filterAndJoin([labelPrefix, label ?? requestedDuration, labelSuffix]); badgeValues.message = filterAndJoin([prefix, avgPing, suffix]); } From 64a33d7455fca793b88a549f77b06a1e2e5f658b Mon Sep 17 00:00:00 2001 From: Jens Neuber Date: Fri, 22 Apr 2022 07:54:13 +0200 Subject: [PATCH 15/20] Update server/util-server.js Co-authored-by: Adam Stachowicz --- server/util-server.js | 1 - 1 file changed, 1 deletion(-) diff --git a/server/util-server.js b/server/util-server.js index 8289ae94..60a97239 100644 --- a/server/util-server.js +++ b/server/util-server.js @@ -13,7 +13,6 @@ const mqtt = require("mqtt"); const chroma = require("chroma-js"); const { badgeConstants } = require("./config"); - // From ping-lite exports.WIN = /^win/.test(process.platform); exports.LIN = /^linux/.test(process.platform); From 9f8b3151d8587113d1ea19e93dbd6181238e419b Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Sat, 30 Apr 2022 21:36:00 +0800 Subject: [PATCH 16/20] Update server/util-server.js Co-authored-by: Matthew Nickson --- server/util-server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/util-server.js b/server/util-server.js index 60a97239..a98129bc 100644 --- a/server/util-server.js +++ b/server/util-server.js @@ -457,7 +457,7 @@ exports.errorLog = (error, outputToConsole = true) => { * 0% => hue = 10 => red * 100% => hue = 90 => green * - * @param {number} percentage, float, 0 to 1 + * @param {number} percentage float, 0 to 1 * @param {number} maxHue, int * @param {number} minHue, int * @returns {string}, hex value From 42ea3fb412c2bafd7e45245555f4ee46ca507a4c Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Sat, 30 Apr 2022 21:36:07 +0800 Subject: [PATCH 17/20] Update server/util-server.js Co-authored-by: Matthew Nickson --- server/util-server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/util-server.js b/server/util-server.js index a98129bc..4d806a4c 100644 --- a/server/util-server.js +++ b/server/util-server.js @@ -458,7 +458,7 @@ exports.errorLog = (error, outputToConsole = true) => { * 100% => hue = 90 => green * * @param {number} percentage float, 0 to 1 - * @param {number} maxHue, int + * @param {number} maxHue * @param {number} minHue, int * @returns {string}, hex value */ From 96536ae3919f0f927d4aa3aded49a5d7280a5a0f Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Mon, 2 May 2022 12:36:12 +0800 Subject: [PATCH 18/20] Rebuild package-lock.json --- package-lock.json | 98 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 96 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0af5ed24..cd8167cf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "uptime-kuma", - "version": "1.15.0", + "version": "1.15.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "uptime-kuma", - "version": "1.15.0", + "version": "1.15.1", "license": "MIT", "dependencies": { "@fortawesome/fontawesome-svg-core": "~1.2.36", @@ -3941,6 +3941,14 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/anafanafo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anafanafo/-/anafanafo-2.0.0.tgz", + "integrity": "sha512-Nlfq7NC4AOkTJerWRIZcOAiMNtIDVIGWGvQ98O7Jl6Kr2Dk0dX5u4MqN778kSRTy5KRqchpLdF2RtLFEz9FVkQ==", + "dependencies": { + "char-width-table-consumer": "^1.0.0" + } + }, "node_modules/ansi-align": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", @@ -4461,6 +4469,11 @@ "node": ">=8" } }, + "node_modules/binary-search": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/binary-search/-/binary-search-1.3.6.tgz", + "integrity": "sha512-nbE1WxOTTrUWIfsfZ4aHGYu5DOuNkbxGokjV6Z2kxfJK3uaAb8zNK1muzOeipoLHZjInT4Br88BHpzevc681xA==" + }, "node_modules/bintrees": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.1.tgz", @@ -4963,6 +4976,14 @@ "node": ">=10" } }, + "node_modules/char-width-table-consumer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/char-width-table-consumer/-/char-width-table-consumer-1.0.0.tgz", + "integrity": "sha512-Fz4UD0LBpxPgL9i29CJ5O4KANwaMnX/OhhbxzvNa332h+9+nRKyeuLw4wA51lt/ex67+/AdsoBQJF3kgX2feYQ==", + "dependencies": { + "binary-search": "^1.3.5" + } + }, "node_modules/chardet": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-1.4.0.tgz", @@ -5614,6 +5635,26 @@ "node": ">=8" } }, + "node_modules/css-color-converter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/css-color-converter/-/css-color-converter-2.0.0.tgz", + "integrity": "sha512-oLIG2soZz3wcC3aAl/7Us5RS8Hvvc6I8G8LniF/qfMmrm7fIKQ8RIDDRZeKyGL2SrWfNqYspuLShbnjBMVWm8g==", + "dependencies": { + "color-convert": "^0.5.2", + "color-name": "^1.1.4", + "css-unit-converter": "^1.1.2" + } + }, + "node_modules/css-color-converter/node_modules/color-convert": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-0.5.3.tgz", + "integrity": "sha1-vbbGnOZg+t/+CwAHzER+G59ygr0=" + }, + "node_modules/css-color-converter/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, "node_modules/css-functions-list": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.0.1.tgz", @@ -5623,6 +5664,11 @@ "node": ">=12.22" } }, + "node_modules/css-unit-converter": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.2.tgz", + "integrity": "sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA==" + }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -19780,6 +19826,14 @@ "uri-js": "^4.2.2" } }, + "anafanafo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anafanafo/-/anafanafo-2.0.0.tgz", + "integrity": "sha512-Nlfq7NC4AOkTJerWRIZcOAiMNtIDVIGWGvQ98O7Jl6Kr2Dk0dX5u4MqN778kSRTy5KRqchpLdF2RtLFEz9FVkQ==", + "requires": { + "char-width-table-consumer": "^1.0.0" + } + }, "ansi-align": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", @@ -20187,6 +20241,11 @@ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true }, + "binary-search": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/binary-search/-/binary-search-1.3.6.tgz", + "integrity": "sha512-nbE1WxOTTrUWIfsfZ4aHGYu5DOuNkbxGokjV6Z2kxfJK3uaAb8zNK1muzOeipoLHZjInT4Br88BHpzevc681xA==" + }, "bintrees": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.1.tgz", @@ -20554,6 +20613,14 @@ "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", "dev": true }, + "char-width-table-consumer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/char-width-table-consumer/-/char-width-table-consumer-1.0.0.tgz", + "integrity": "sha512-Fz4UD0LBpxPgL9i29CJ5O4KANwaMnX/OhhbxzvNa332h+9+nRKyeuLw4wA51lt/ex67+/AdsoBQJF3kgX2feYQ==", + "requires": { + "binary-search": "^1.3.5" + } + }, "chardet": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/chardet/-/chardet-1.4.0.tgz", @@ -21067,12 +21134,39 @@ "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", "dev": true }, + "css-color-converter": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/css-color-converter/-/css-color-converter-2.0.0.tgz", + "integrity": "sha512-oLIG2soZz3wcC3aAl/7Us5RS8Hvvc6I8G8LniF/qfMmrm7fIKQ8RIDDRZeKyGL2SrWfNqYspuLShbnjBMVWm8g==", + "requires": { + "color-convert": "^0.5.2", + "color-name": "^1.1.4", + "css-unit-converter": "^1.1.2" + }, + "dependencies": { + "color-convert": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-0.5.3.tgz", + "integrity": "sha1-vbbGnOZg+t/+CwAHzER+G59ygr0=" + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + } + } + }, "css-functions-list": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.0.1.tgz", "integrity": "sha512-PriDuifDt4u4rkDgnqRCLnjfMatufLmWNfQnGCq34xZwpY3oabwhB9SqRBmuvWUgndbemCFlKqg+nO7C2q0SBw==", "dev": true }, + "css-unit-converter": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.2.tgz", + "integrity": "sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA==" + }, "cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", From c6ba5b621c2cb4a3d04c518904747f365c6bfc3b Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Mon, 2 May 2022 13:32:19 +0800 Subject: [PATCH 19/20] Remove isPublished, checkPublished which had been removed in upstream. --- server/routers/api-router.js | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/server/routers/api-router.js b/server/routers/api-router.js index 1d621519..8cf73e16 100644 --- a/server/routers/api-router.js +++ b/server/routers/api-router.js @@ -214,8 +214,6 @@ router.get("/api/badge/:id/status", cache("5 minutes"), async (request, response } = request.query; try { - await checkPublished(); - const requestedMonitorId = parseInt(request.params.id, 10); const overrideValue = value !== undefined ? parseInt(value) : undefined; @@ -269,8 +267,6 @@ router.get("/api/badge/:id/uptime/:duration?", cache("5 minutes"), async (reques } = request.query; try { - await checkPublished(); - const requestedMonitorId = parseInt(request.params.id, 10); // if no duration is given, set value to 24 (h) const requestedDuration = request.params.duration !== undefined ? parseInt(request.params.duration, 10) : 24; @@ -335,8 +331,6 @@ router.get("/api/badge/:id/ping/:duration?", cache("5 minutes"), async (request, } = request.query; try { - await checkPublished(); - const requestedMonitorId = parseInt(request.params.id, 10); // Default duration is 24 (h) if not defined in queryParam, limited to 720h (30d) @@ -382,24 +376,6 @@ router.get("/api/badge/:id/ping/:duration?", cache("5 minutes"), async (request, } }); -async function checkPublished() { - if (! await isPublished()) { - throw new Error("The status page is not published"); - } -} - -/** - * Default is published - * @returns {Promise} - */ -async function isPublished() { - const value = await setting("statusPagePublished"); - if (value === null) { - return true; - } - return value; -} - /** * Send a 403 response * @param {Object} res Express response object From 4c2753af46cf89dee7a562ac7bb3ca50b0f89f18 Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Mon, 2 May 2022 13:36:35 +0800 Subject: [PATCH 20/20] Remove an unused variable --- 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 8cf73e16..872d6d8c 100644 --- a/server/routers/api-router.js +++ b/server/routers/api-router.js @@ -1,5 +1,5 @@ let express = require("express"); -const { allowDevAllOrigin, allowAllOrigin, percentageToColor, filterAndJoin, setting } = require("../util-server"); +const { allowDevAllOrigin, allowAllOrigin, percentageToColor, filterAndJoin } = require("../util-server"); const { R } = require("redbean-node"); const apicache = require("../modules/apicache"); const Monitor = require("../model/monitor");