From 3d002b3ce95d51ead40b71c8c31cbe0bb3522e91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Bla=C5=BEej?= <trogper@gmail.com> Date: Sat, 25 Dec 2021 20:25:21 +0100 Subject: [PATCH 001/124] add status boolean parameter to push monitor --- server/routers/api-router.js | 5 +++-- src/pages/EditMonitor.vue | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/server/routers/api-router.js b/server/routers/api-router.js index 1920cef7..1b313828 100644 --- a/server/routers/api-router.js +++ b/server/routers/api-router.js @@ -5,7 +5,7 @@ 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 { UP, DOWN, flipStatus, debug } = require("../../src/util"); let router = express.Router(); let cache = apicache.middleware; @@ -22,6 +22,8 @@ router.get("/api/push/:pushToken", async (request, response) => { let pushToken = request.params.pushToken; let msg = request.query.msg || "OK"; let ping = request.query.ping || null; + let status = request.query.status || "true"; + status = status === "true" ? UP : DOWN; let monitor = await R.findOne("monitor", " push_token = ? AND active = 1 ", [ pushToken @@ -33,7 +35,6 @@ router.get("/api/push/:pushToken", async (request, response) => { const previousHeartbeat = await Monitor.getPreviousHeartbeat(monitor.id); - let status = UP; if (monitor.isUpsideDown()) { status = flipStatus(status); } diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue index 4b6a920c..7d31c7da 100644 --- a/src/pages/EditMonitor.vue +++ b/src/pages/EditMonitor.vue @@ -53,7 +53,7 @@ <CopyableInput id="push-url" v-model="pushURL" type="url" disabled="disabled" /> <div class="form-text"> {{ $t("needPushEvery", [monitor.interval]) }}<br /> - {{ $t("pushOptionalParams", ["msg, ping"]) }} + {{ $t("pushOptionalParams", ["status, msg, ping"]) }} </div> </div> @@ -349,7 +349,7 @@ export default { }, pushURL() { - return this.$root.baseURL + "/api/push/" + this.monitor.pushToken + "?msg=OK&ping="; + return this.$root.baseURL + "/api/push/" + this.monitor.pushToken + "?status=true&msg=OK&ping="; }, bodyPlaceholder() { From 35360e2069b04466dbaee87d522b97736d9d53aa Mon Sep 17 00:00:00 2001 From: Jens Neuber <mail@jensneuber.de> Date: Mon, 3 Jan 2022 15:48:52 +0100 Subject: [PATCH 002/124] 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 <mail@jensneuber.de> Date: Mon, 3 Jan 2022 16:04:37 +0100 Subject: [PATCH 003/124] 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 <mail@jensneuber.de> Date: Mon, 3 Jan 2022 16:23:23 +0100 Subject: [PATCH 004/124] 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 <mail@jensneuber.de> Date: Tue, 4 Jan 2022 12:21:53 +0100 Subject: [PATCH 005/124] 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 <mail@jensneuber.de> Date: Tue, 4 Jan 2022 12:23:16 +0100 Subject: [PATCH 006/124] 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 <mail@jensneuber.de> Date: Tue, 4 Jan 2022 13:40:53 +0100 Subject: [PATCH 007/124] 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 <mail@jensneuber.de> Date: Tue, 4 Jan 2022 16:00:21 +0100 Subject: [PATCH 008/124] 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 <mail@jensneuber.de> Date: Tue, 4 Jan 2022 16:01:40 +0100 Subject: [PATCH 009/124] 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 <mail@jensneuber.de> Date: Wed, 5 Jan 2022 11:48:25 +0100 Subject: [PATCH 010/124] 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 <jensneuber@users.noreply.github.com> Date: Wed, 5 Jan 2022 15:25:42 +0100 Subject: [PATCH 011/124] PR feedback: remove spaces in comments Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com> --- 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 <jensneuber@users.noreply.github.com> Date: Wed, 5 Jan 2022 15:25:56 +0100 Subject: [PATCH 012/124] PR feedback: remove spaces in parenthesis Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com> --- 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 <jensneuber@users.noreply.github.com> Date: Wed, 5 Jan 2022 15:26:07 +0100 Subject: [PATCH 013/124] PR feedback: remove spaces in parenthesis Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com> --- 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 <jensneuber@users.noreply.github.com> Date: Wed, 5 Jan 2022 15:26:23 +0100 Subject: [PATCH 014/124] PR feedback: remove spaces in parenthesis Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com> --- 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 <jensneuber@users.noreply.github.com> Date: Wed, 5 Jan 2022 15:26:29 +0100 Subject: [PATCH 015/124] PR feedback: remove spaces in parenthesis Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com> --- 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 3dda5938f2180c4af2b9edd6f165c196c707da1c Mon Sep 17 00:00:00 2001 From: Raphael Bernhart <mail@raphaelbernhart.at> Date: Fri, 21 Jan 2022 15:39:49 +0100 Subject: [PATCH 016/124] =?UTF-8?q?=F0=9F=92=84=20Add=20condition=20to=20d?= =?UTF-8?q?iv=20tag=20for=20styling=20reasons?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/PublicGroupList.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/PublicGroupList.vue b/src/components/PublicGroupList.vue index f30edcef..bd68e94e 100644 --- a/src/components/PublicGroupList.vue +++ b/src/components/PublicGroupList.vue @@ -41,7 +41,7 @@ <Uptime :monitor="monitor.element" type="24" :pill="true" /> {{ monitor.element.name }} </div> - <div class="tags"> + <div v-if="monitor.element.tags.length" class="tags"> <Tag v-for="tag in monitor.element.tags" :key="tag" :item="tag" :size="'sm'" /> </div> </div> From c57b2c4d287ac1ac9242ca3bc99a72236bc0606c Mon Sep 17 00:00:00 2001 From: Raphael Bernhart <mail@raphaelbernhart.at> Date: Fri, 21 Jan 2022 17:13:24 +0100 Subject: [PATCH 017/124] =?UTF-8?q?=F0=9F=92=84=20Fix=20spacing=20to=20get?= =?UTF-8?q?=20pixel=20perfectness?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/assets/app.scss | 2 +- src/components/PublicGroupList.vue | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/assets/app.scss b/src/assets/app.scss index cec64467..f4d99462 100644 --- a/src/assets/app.scss +++ b/src/assets/app.scss @@ -339,7 +339,7 @@ textarea.form-control { .item { display: block; text-decoration: none; - padding: 13px 15px 10px 15px; + padding: 15px; border-radius: 10px; transition: all ease-in-out 0.15s; diff --git a/src/components/PublicGroupList.vue b/src/components/PublicGroupList.vue index bd68e94e..334282d8 100644 --- a/src/components/PublicGroupList.vue +++ b/src/components/PublicGroupList.vue @@ -33,8 +33,8 @@ <template #item="monitor"> <div class="item"> <div class="row"> - <div class="col-9 col-md-8 small-padding"> - <div class="info"> + <div class="col-9 col-md-8 small-padding d-flex align-items-center flex-wrap"> + <div class="info d-flex align-items-center gap-3 w-100"> <font-awesome-icon v-if="editMode" icon="arrows-alt-v" class="action drag me-3" /> <font-awesome-icon v-if="editMode" icon="times" class="action remove me-3" @click="removeMonitor(group.index, monitor.index)" /> @@ -45,7 +45,7 @@ <Tag v-for="tag in monitor.element.tags" :key="tag" :item="tag" :size="'sm'" /> </div> </div> - <div :key="$root.userHeartbeatBar" class="col-3 col-md-4"> + <div :key="$root.userHeartbeatBar" class="col-3 col-md-4 d-flex align-items-center"> <HeartbeatBar size="small" :monitor-id="monitor.element.id" /> </div> </div> From cd19b9fc49965732a4dd1def35fd695587101165 Mon Sep 17 00:00:00 2001 From: Raphael Bernhart <mail@raphaelbernhart.at> Date: Fri, 21 Jan 2022 17:13:38 +0100 Subject: [PATCH 018/124] =?UTF-8?q?=F0=9F=92=AB=20Improve=20hearbeat=20ani?= =?UTF-8?q?mation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/HeartbeatBar.vue | 47 +++++++++++++++++++++++++++++---- 1 file changed, 42 insertions(+), 5 deletions(-) diff --git a/src/components/HeartbeatBar.vue b/src/components/HeartbeatBar.vue index be0b122e..7143e716 100644 --- a/src/components/HeartbeatBar.vue +++ b/src/components/HeartbeatBar.vue @@ -1,6 +1,6 @@ <template> <div ref="wrap" class="wrap" :style="wrapStyle"> - <div class="hp-bar-big" :style="barStyle"> + <div class="hp-bar-big d-flex" :style="barStyle"> <div v-for="(beat, index) in shortBeatList" :key="index" @@ -8,7 +8,11 @@ :class="{ 'empty' : (beat === 0), 'down' : (beat.status === 0), 'pending' : (beat.status === 2) }" :style="beatStyle" :title="getBeatTitle(beat)" - /> + @mouseenter="toggleActivateSibling" + @mouseleave="toggleActivateSibling" + > + <div class="beat-inner" /> + </div> </div> </div> </template> @@ -168,9 +172,30 @@ export default { getBeatTitle(beat) { return `${this.$root.datetime(beat.time)}` + ((beat.msg) ? ` - ${beat.msg}` : ``); + }, + // Toggling the activeSibling class on hover over the current hover item + toggleActivateSibling(e) { + // Variable definition + const element = e.target; + const previous = element.previousSibling; + const next = element.nextSibling; + + // Return if the hovered element has empty class + if (element.classList.contains("empty")) { + return; + } + + // Check if Previous Sibling is heartbar element and doesn't have the empty class + if (previous.children && !previous.classList.contains("empty")) { + previous.classList.toggle("active-sibling"); + } + // Check if Next Sibling is heartbar element and doesn't have the empty class + if (next.children && next.classList.contains("empty")) { + next.classList.toggle("active-sibling"); + } } }, -} +}; </script> <style lang="scss" scoped> @@ -184,9 +209,10 @@ export default { .hp-bar-big { .beat { - display: inline-block; background-color: $primary; border-radius: $border-radius; + display: inline-block; + transition: all ease 0.6s; &.empty { background-color: aliceblue; @@ -200,11 +226,22 @@ export default { background-color: $warning; } + .beat-inner { + border-radius: $border-radius; + display: inline-block; + height: 100%; + width: 5px; + } + &:not(.empty):hover { - transition: all ease-in-out 0.15s; + transition: all ease 0.15s; opacity: 0.8; transform: scale(var(--hover-scale)); } + &.active-sibling { + transform: scale(1.3); + transition: all ease 0.15s; + } } } From 0313acd4c5bef4fce5c01cc6a7e7d6fe27439150 Mon Sep 17 00:00:00 2001 From: Raphael Bernhart <mail@raphaelbernhart.at> Date: Fri, 21 Jan 2022 17:22:30 +0100 Subject: [PATCH 019/124] =?UTF-8?q?=F0=9F=90=9B=20Fix=20bug=20where=20a=20?= =?UTF-8?q?condition=20was=20wrong-false?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/HeartbeatBar.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/HeartbeatBar.vue b/src/components/HeartbeatBar.vue index 7143e716..d7f352cc 100644 --- a/src/components/HeartbeatBar.vue +++ b/src/components/HeartbeatBar.vue @@ -190,7 +190,7 @@ export default { previous.classList.toggle("active-sibling"); } // Check if Next Sibling is heartbar element and doesn't have the empty class - if (next.children && next.classList.contains("empty")) { + if (next.children && !next.classList.contains("empty")) { next.classList.toggle("active-sibling"); } } From dd3992063eb680ba6233b146a6b1c7a879d6e031 Mon Sep 17 00:00:00 2001 From: Raphael Bernhart <48283236+raphaelbernhart@users.noreply.github.com> Date: Sat, 22 Jan 2022 13:59:36 +0100 Subject: [PATCH 020/124] Apply suggestions from code review Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com> --- src/components/HeartbeatBar.vue | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/HeartbeatBar.vue b/src/components/HeartbeatBar.vue index d7f352cc..bbc7a40f 100644 --- a/src/components/HeartbeatBar.vue +++ b/src/components/HeartbeatBar.vue @@ -173,6 +173,7 @@ export default { getBeatTitle(beat) { return `${this.$root.datetime(beat.time)}` + ((beat.msg) ? ` - ${beat.msg}` : ``); }, + // Toggling the activeSibling class on hover over the current hover item toggleActivateSibling(e) { // Variable definition @@ -189,6 +190,7 @@ export default { if (previous.children && !previous.classList.contains("empty")) { previous.classList.toggle("active-sibling"); } + // Check if Next Sibling is heartbar element and doesn't have the empty class if (next.children && !next.classList.contains("empty")) { next.classList.toggle("active-sibling"); @@ -238,6 +240,7 @@ export default { opacity: 0.8; transform: scale(var(--hover-scale)); } + &.active-sibling { transform: scale(1.3); transition: all ease 0.15s; From ce7d8c38c522a6f6b77a5bd19ce5e800f4769b5d Mon Sep 17 00:00:00 2001 From: jordanbertasso <36979824+jordanbertasso@users.noreply.github.com> Date: Sun, 10 Apr 2022 21:43:52 +1000 Subject: [PATCH 021/124] [empty commit] pull request for issue #1448 From 0961c6d9b3b73db5446faa77bbd1173be6b4e979 Mon Sep 17 00:00:00 2001 From: jordanbertasso <36979824+jordanbertasso@users.noreply.github.com> Date: Sun, 10 Apr 2022 21:45:07 +1000 Subject: [PATCH 022/124] Check for ping and port type in discord notifs --- server/notification-providers/discord.js | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/server/notification-providers/discord.js b/server/notification-providers/discord.js index 881ad211..39eeef5a 100644 --- a/server/notification-providers/discord.js +++ b/server/notification-providers/discord.js @@ -24,14 +24,17 @@ class Discord extends NotificationProvider { let url; - if (monitorJSON["type"] === "port") { - url = monitorJSON["hostname"]; - if (monitorJSON["port"]) { - url += ":" + monitorJSON["port"]; - } - - } else { - url = monitorJSON["url"]; + switch (monitorJSON["type"]) { + case "port": + case "ping": + url = monitorJSON["hostname"]; + if (monitorJSON["port"]) { + url += ":" + monitorJSON["port"]; + } + break; + default: + url = monitorJSON["url"]; + break; } // If heartbeatJSON is not null, we go into the normal alerting loop. From 1b1e0f6dd942d25c4b97a549ef6f1b28870b95d5 Mon Sep 17 00:00:00 2001 From: jordanbertasso <36979824+jordanbertasso@users.noreply.github.com> Date: Sun, 10 Apr 2022 21:45:19 +1000 Subject: [PATCH 023/124] Add Discord tests --- test/backend.spec.js | 44 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/test/backend.spec.js b/test/backend.spec.js index bbfc6897..7c02d342 100644 --- a/test/backend.spec.js +++ b/test/backend.spec.js @@ -1,5 +1,9 @@ -const { genSecret, sleep } = require("../src/util"); +const { genSecret, sleep, DOWN } = require("../src/util"); const utilServerRewire = require("../server/util-server"); +const Discord = require("../server/notification-providers/discord"); +const axios = require("axios"); + +jest.mock("axios"); describe("Test parseCertificateInfo", () => { it("should handle undefined", async () => { @@ -164,3 +168,41 @@ describe("Test reset-password", () => { }, 120000); }); +describe("Test Discord Notification Provider", () => { + const sendNotification = async (type) => { + const discordProvider = new Discord(); + + axios.post.mockResolvedValue({}); + + await discordProvider.send( + { + discordUsername: "Uptime Kuma", + discordWebhookUrl: "https://discord.com", + }, + "test message", + { + type, + hostname: "discord.com" + (type === "port" ? ":1337" : ""), + }, + { + status: DOWN, + } + ); + }; + + it("should send hostname for ping monitors", async () => { + await sendNotification("ping"); + + expect(axios.post.mock.lastCall[1].embeds[0].fields[1].value).toBe( + "discord.com" + ); + }); + + it("should send hostname for port monitors", async () => { + await sendNotification("port"); + + expect(axios.post.mock.lastCall[1].embeds[0].fields[1].value).toBe( + "discord.com:1337" + ); + }); +}); From e38742a2d0d4846cd72df127833dd6dcf87ba28d Mon Sep 17 00:00:00 2001 From: Matthew Nickson <mnickson@sidingsmedia.com> Date: Mon, 11 Apr 2022 19:58:31 +0100 Subject: [PATCH 024/124] [empty commit] pull request for #1059 From 2638d68c97d4c06a2d4f2545e4bc2a5b74eef9da Mon Sep 17 00:00:00 2001 From: jordanbertasso <36979824+jordanbertasso@users.noreply.github.com> Date: Tue, 12 Apr 2022 09:52:07 +1000 Subject: [PATCH 025/124] Cover dns and steam types in Discord notifs --- server/notification-providers/discord.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/server/notification-providers/discord.js b/server/notification-providers/discord.js index 39eeef5a..c5b95c60 100644 --- a/server/notification-providers/discord.js +++ b/server/notification-providers/discord.js @@ -25,8 +25,10 @@ class Discord extends NotificationProvider { let url; switch (monitorJSON["type"]) { - case "port": + case "dns": case "ping": + case "port": + case "steam": url = monitorJSON["hostname"]; if (monitorJSON["port"]) { url += ":" + monitorJSON["port"]; From 0765f0509065692085becffd506f65349d35175f Mon Sep 17 00:00:00 2001 From: jordanbertasso <36979824+jordanbertasso@users.noreply.github.com> Date: Tue, 12 Apr 2022 09:52:16 +1000 Subject: [PATCH 026/124] Update Discord tests --- test/backend.spec.js | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/test/backend.spec.js b/test/backend.spec.js index 7c02d342..27aaf9e2 100644 --- a/test/backend.spec.js +++ b/test/backend.spec.js @@ -169,7 +169,7 @@ describe("Test reset-password", () => { }); describe("Test Discord Notification Provider", () => { - const sendNotification = async (type) => { + const sendNotification = async (hostname, port, type) => { const discordProvider = new Discord(); axios.post.mockResolvedValue({}); @@ -182,7 +182,8 @@ describe("Test Discord Notification Provider", () => { "test message", { type, - hostname: "discord.com" + (type === "port" ? ":1337" : ""), + hostname, + port, }, { status: DOWN, @@ -190,19 +191,41 @@ describe("Test Discord Notification Provider", () => { ); }; - it("should send hostname for ping monitors", async () => { - await sendNotification("ping"); + it("should send hostname for dns monitors", async () => { + const hostname = "discord.com"; + await sendNotification(hostname, null, "dns"); expect(axios.post.mock.lastCall[1].embeds[0].fields[1].value).toBe( - "discord.com" + hostname + ); + }); + + it("should send hostname for ping monitors", async () => { + const hostname = "discord.com"; + await sendNotification(hostname, null, "ping"); + + expect(axios.post.mock.lastCall[1].embeds[0].fields[1].value).toBe( + hostname ); }); it("should send hostname for port monitors", async () => { - await sendNotification("port"); + const hostname = "discord.com"; + const port = 1337; + await sendNotification(hostname, port, "port"); expect(axios.post.mock.lastCall[1].embeds[0].fields[1].value).toBe( - "discord.com:1337" + `${hostname}:${port}` + ); + }); + + it("should send hostname for steam monitors", async () => { + const hostname = "discord.com"; + const port = 1337; + await sendNotification(hostname, port, "steam"); + + expect(axios.post.mock.lastCall[1].embeds[0].fields[1].value).toBe( + `${hostname}:${port}` ); }); }); From b893d50e450dd9bfbe956cceafd9398df1236274 Mon Sep 17 00:00:00 2001 From: Matthew Nickson <mnickson@sidingsmedia.com> Date: Wed, 13 Apr 2022 21:02:19 +0100 Subject: [PATCH 027/124] Implement specify Port for DNS Monitor #1059 This commit should fully implement #1059. When the user selects the DNS monitor option, a new input box has been added below the resolver address allowing the user to implement the port to access the resolver on. This uses the same `monitor.port` as the TCP monitor but a monitor has been added to prefill the port value to the default of `53` if the value in this field has not already been set. This is then cleared if the user selects a different monitor type and has not changed the port value. A translation has also been added explaining what this field does in order to reduce any confusion. JSDoc documentation has also been added to the `dnsResolve` function in `util-server.js`. Signed-off-by: Matthew Nickson <mnickson@sidingsmedia.com> --- server/model/monitor.js | 2 +- server/util-server.js | 12 ++++++++++-- src/languages/en.js | 1 + src/pages/EditMonitor.vue | 18 ++++++++++++++++++ 4 files changed, 30 insertions(+), 3 deletions(-) diff --git a/server/model/monitor.js b/server/model/monitor.js index c4441d63..682fd72e 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -249,7 +249,7 @@ class Monitor extends BeanModel { let startTime = dayjs().valueOf(); let dnsMessage = ""; - let dnsRes = await dnsResolve(this.hostname, this.dns_resolve_server, this.dns_resolve_type); + let dnsRes = await dnsResolve(this.hostname, this.dns_resolve_server, this.port, this.dns_resolve_type); bean.ping = dayjs().valueOf() - startTime; if (this.dns_resolve_type == "A" || this.dns_resolve_type == "AAAA" || this.dns_resolve_type == "TXT") { diff --git a/server/util-server.js b/server/util-server.js index 2264ebea..ad30f610 100644 --- a/server/util-server.js +++ b/server/util-server.js @@ -89,9 +89,17 @@ exports.pingAsync = function (hostname, ipv6 = false) { }); }; -exports.dnsResolve = function (hostname, resolver_server, rrtype) { +/** + * Resolves a given record using the specified DNS server + * @param {string} hostname The hostname of the record to lookup + * @param {string} resolver_server The DNS server to use + * @param {string} resolver_port The port the DNS server is listening on + * @param {string} rrtype The type of record to request + * @returns {Promise} Promise object represents DNS lookup result + */ +exports.dnsResolve = function (hostname, resolver_server, resolver_port, rrtype) { const resolver = new Resolver(); - resolver.setServers([resolver_server]); + resolver.setServers([`${resolver_server}:${resolver_port}`]); return new Promise((resolve, reject) => { if (rrtype == "PTR") { resolver.reverse(hostname, (err, records) => { diff --git a/src/languages/en.js b/src/languages/en.js index 47513466..3f7f7c7e 100644 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -14,6 +14,7 @@ export default { deleteMonitorMsg: "Are you sure want to delete this monitor?", deleteNotificationMsg: "Are you sure want to delete this notification for all monitors?", resoverserverDescription: "Cloudflare is the default server. You can change the resolver server anytime.", + dnsPortDescription: "DNS server port. Defaults to 53. You can change the port at any time.", rrtypeDescription: "Select the RR type you want to monitor", pauseMonitorMsg: "Are you sure want to pause?", enableDefaultNotificationDescription: "This notification will be enabled by default for new monitors. You can still disable the notification separately for each monitor.", diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue index 4b6a920c..f52eac8f 100644 --- a/src/pages/EditMonitor.vue +++ b/src/pages/EditMonitor.vue @@ -91,6 +91,15 @@ </div> </div> + <!-- Port --> + <div class="my-3"> + <label for="port" class="form-label">{{ $t("Port") }}</label> + <input id="port" v-model="monitor.port" type="number" class="form-control" required min="0" max="65535" step="1"> + <div class="form-text"> + {{ $t("dnsPortDescription") }} + </div> + </div> + <div class="my-3"> <label for="dns_resolve_type" class="form-label">{{ $t("Resource Record Type") }}</label> @@ -386,6 +395,15 @@ export default { this.monitor.pushToken = genSecret(10); } } + + // Set default port for DNS if not already defined + if (! this.monitor.port || this.monitor.port === "53") { + if (this.monitor.type === "dns") { + this.monitor.port = "53"; + } else { + this.monitor.port = ""; + } + } } }, From a680331dd77902a160e7876f650f5204e26d0499 Mon Sep 17 00:00:00 2001 From: Matthew Nickson <mnickson@sidingsmedia.com> Date: Fri, 15 Apr 2022 19:59:32 +0100 Subject: [PATCH 028/124] Fixes issue with ::1 port 5300 requests Now the address is wrapped in `[]` in order to prevent ::1 port 5300 being interpreted as ::1:5300. Wrapping the IPv4 address in `[]` does seem to have any effect on correct domain name resolution. In order to prevent issues if a user inputs an address with brackets, they are removed from the string if present before being re-added when it is passed to `setServers`. I have also removed the JSDoc comment as this will be added in a seperate PR Signed-off-by: Matthew Nickson <mnickson@sidingsmedia.com> --- server/util-server.js | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/server/util-server.js b/server/util-server.js index 367f4ffb..f7c0ce02 100644 --- a/server/util-server.js +++ b/server/util-server.js @@ -88,17 +88,12 @@ exports.pingAsync = function (hostname, ipv6 = false) { }); }; -/** - * Resolves a given record using the specified DNS server - * @param {string} hostname The hostname of the record to lookup - * @param {string} resolver_server The DNS server to use - * @param {string} resolver_port The port the DNS server is listening on - * @param {string} rrtype The type of record to request - * @returns {Promise} Promise object represents DNS lookup result - */ exports.dnsResolve = function (hostname, resolver_server, resolver_port, rrtype) { const resolver = new Resolver(); - resolver.setServers([`${resolver_server}:${resolver_port}`]); + // Remove brackets from IPv6 addresses so we can re-add them to + // prevent issues with ::1:5300 (::1 port 5300) + resolver_server = resolver_server.replace("[", "").replace("]", ""); + resolver.setServers([`[${resolver_server}]:${resolver_port}`]); return new Promise((resolve, reject) => { if (rrtype == "PTR") { resolver.reverse(hostname, (err, records) => { From 6fb66728e6f4045ba684e1c5e3912be7883e1647 Mon Sep 17 00:00:00 2001 From: Matthew Nickson <mnickson@sidingsmedia.com> Date: Fri, 15 Apr 2022 20:08:54 +0100 Subject: [PATCH 029/124] [empty commit] pull request for Add JSDoc comments From 60c63cc18ef0b8d2228f2927e3e8872a3851017d Mon Sep 17 00:00:00 2001 From: Matthew Nickson <mnickson@sidingsmedia.com> Date: Sat, 16 Apr 2022 11:10:51 +0100 Subject: [PATCH 030/124] Add JSDoc to server/jobs/* Signed-off-by: Matthew Nickson <mnickson@sidingsmedia.com> --- server/jobs/util-worker.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/server/jobs/util-worker.js b/server/jobs/util-worker.js index 9426840d..52ad8f9a 100644 --- a/server/jobs/util-worker.js +++ b/server/jobs/util-worker.js @@ -2,12 +2,20 @@ const { parentPort, workerData } = require("worker_threads"); const Database = require("../database"); const path = require("path"); +/** + * Send message to parent process + * @param {any} any The message to log + */ const log = function (any) { if (parentPort) { parentPort.postMessage(any); } }; +/** + * Exit the worker process + * @param {number} error The status code to exit + */ const exit = function (error) { if (error && error != 0) { process.exit(error); @@ -20,6 +28,7 @@ const exit = function (error) { } }; +/** Connects to the database */ const connectDb = async function () { const dbPath = path.join( process.env.DATA_DIR || workerData["data-dir"] || "./data/" From 5a209c74e1411d2729e3ab06bb180048fd1cdc24 Mon Sep 17 00:00:00 2001 From: Matthew Nickson <mnickson@sidingsmedia.com> Date: Sat, 16 Apr 2022 20:24:53 +0100 Subject: [PATCH 031/124] Add JSDoc to server/notification-providers/* Signed-off-by: Matthew Nickson <mnickson@sidingsmedia.com> --- server/notification-providers/aliyun-sms.js | 18 ++++++++++- server/notification-providers/bark.js | 20 +++++++++++-- server/notification-providers/dingding.js | 19 +++++++++++- .../notification-provider.js | 16 ++++++---- server/notification-providers/slack.js | 1 + server/notification-providers/teams.js | 30 +++++++++++++++++++ server/notification-providers/wecom.js | 6 ++++ 7 files changed, 101 insertions(+), 9 deletions(-) diff --git a/server/notification-providers/aliyun-sms.js b/server/notification-providers/aliyun-sms.js index 6a206320..1a64690d 100644 --- a/server/notification-providers/aliyun-sms.js +++ b/server/notification-providers/aliyun-sms.js @@ -37,6 +37,12 @@ class AliyunSMS extends NotificationProvider { } } + /** + * Send the SMS notification + * @param {BeanModel} notification Notification details + * @param {string} msgbody Message template + * @returns {boolean} True if successful else false + */ async sendSms(notification, msgbody) { let params = { PhoneNumbers: notification.phonenumber, @@ -70,7 +76,12 @@ class AliyunSMS extends NotificationProvider { return false; } - /** Aliyun request sign */ + /** + * Aliyun request sign + * @param {Object} param Parameters object to sign + * @param {string} AccessKeySecret Secret key to sign parameters with + * @returns {string} + */ sign(param, AccessKeySecret) { let param2 = {}; let data = []; @@ -93,6 +104,11 @@ class AliyunSMS extends NotificationProvider { .digest("base64"); } + /** + * Convert status constant to string + * @param {const} status The status constant + * @returns {string} + */ statusToString(status) { switch (status) { case DOWN: diff --git a/server/notification-providers/bark.js b/server/notification-providers/bark.js index 4ebe978a..b2f25612 100644 --- a/server/notification-providers/bark.js +++ b/server/notification-providers/bark.js @@ -49,7 +49,12 @@ class Bark extends NotificationProvider { } } - // add additional parameter for better on device styles (iOS 15 optimized) + /** + * Add additional parameter for better on device styles (iOS 15 + * optimized) + * @param {string} postUrl URL to append parameters to + * @returns {string} + */ appendAdditionalParameters(postUrl) { // grouping all our notifications postUrl += "?group=" + barkNotificationGroup; @@ -60,7 +65,11 @@ class Bark extends NotificationProvider { return postUrl; } - // thrown if failed to check result, result code should be in range 2xx + /** + * Check if result is successful + * @param {Object} result Axios response object + * @throws {Error} The status code is not in range 2xx + */ checkResult(result) { if (result.status == null) { throw new Error("Bark notification failed with invalid response!"); @@ -70,6 +79,13 @@ class Bark extends NotificationProvider { } } + /** + * Send the message + * @param {string} title Message title + * @param {string} subtitle Message + * @param {string} endpoint Endpoint to send request to + * @returns {string} + */ async postNotification(title, subtitle, endpoint) { // url encode title and subtitle title = encodeURIComponent(title); diff --git a/server/notification-providers/dingding.js b/server/notification-providers/dingding.js index cf08f14b..f3946cf8 100644 --- a/server/notification-providers/dingding.js +++ b/server/notification-providers/dingding.js @@ -37,6 +37,12 @@ class DingDing extends NotificationProvider { } } + /** + * Send message to DingDing + * @param {BeanModel} notification + * @param {Object} params Parameters of message + * @returns {boolean} True if successful else false + */ async sendToDingDing(notification, params) { let timestamp = Date.now(); @@ -56,7 +62,12 @@ class DingDing extends NotificationProvider { return false; } - /** DingDing sign */ + /** + * DingDing sign + * @param {Date} timestamp Timestamp of message + * @param {string} secretKey Secret key to sign data with + * @returns {string} + */ sign(timestamp, secretKey) { return Crypto .createHmac("sha256", Buffer.from(secretKey, "utf8")) @@ -64,7 +75,13 @@ class DingDing extends NotificationProvider { .digest("base64"); } + /** + * Convert status constant to string + * @param {const} status The status constant + * @returns {string} + */ statusToString(status) { + // TODO: Move to notification-provider.js to avoid repetition in classes switch (status) { case DOWN: return "DOWN"; diff --git a/server/notification-providers/notification-provider.js b/server/notification-providers/notification-provider.js index 61c6242d..6765633d 100644 --- a/server/notification-providers/notification-provider.js +++ b/server/notification-providers/notification-provider.js @@ -7,17 +7,23 @@ class NotificationProvider { name = undefined; /** - * @param notification : BeanModel - * @param msg : string General Message - * @param monitorJSON : object Monitor details (For Up/Down only) - * @param heartbeatJSON : object Heartbeat details (For Up/Down only) + * Send a notification + * @param {BeanModel} notification + * @param {string} msg General Message + * @param {?Object} monitorJSON Monitor details (For Up/Down only) + * @param {?Object} heartbeatJSON Heartbeat details (For Up/Down only) * @returns {Promise<string>} Return Successful Message - * Throw Error with fail msg + * @throws Error with fail msg */ async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { throw new Error("Have to override Notification.send(...)"); } + /** + * Throws an error + * @param {any} error The error to throw + * @throws {any} The error specified + */ throwGeneralAxiosError(error) { let msg = "Error: " + error + " "; diff --git a/server/notification-providers/slack.js b/server/notification-providers/slack.js index b4dad6fe..da1d6e66 100644 --- a/server/notification-providers/slack.js +++ b/server/notification-providers/slack.js @@ -10,6 +10,7 @@ class Slack extends NotificationProvider { /** * Deprecated property notification.slackbutton * Set it as primary base url if this is not yet set. + * @param {string} url The primary base URL to use */ static async deprecateURL(url) { let currentPrimaryBaseURL = await setting("primaryBaseURL"); diff --git a/server/notification-providers/teams.js b/server/notification-providers/teams.js index 859af569..c398e03c 100644 --- a/server/notification-providers/teams.js +++ b/server/notification-providers/teams.js @@ -5,6 +5,12 @@ const { DOWN, UP } = require("../../src/util"); class Teams extends NotificationProvider { name = "teams"; + /** + * Generate the message to send + * @param {const} status The status constant + * @param {string} monitorName Name of monitor + * @returns {string} + */ _statusMessageFactory = (status, monitorName) => { if (status === DOWN) { return `🔴 Application [${monitorName}] went down`; @@ -14,6 +20,11 @@ class Teams extends NotificationProvider { return "Notification"; }; + /** + * Select theme color to use based on status + * @param {const} status The status constant + * @returns {string} Selected color in hex RGB format + */ _getThemeColor = (status) => { if (status === DOWN) { return "ff0000"; @@ -24,6 +35,14 @@ class Teams extends NotificationProvider { return "008cff"; }; + /** + * Generate payload for notification + * @param {const} status The status of the monitor + * @param {string} monitorMessage Message to send + * @param {string} monitorName Name of monitor affected + * @param {string} monitorUrl URL of monitor affected + * @returns {Object} + */ _notificationPayloadFactory = ({ status, monitorMessage, @@ -74,10 +93,21 @@ class Teams extends NotificationProvider { }; }; + /** + * Send the notification + * @param {string} webhookUrl URL to send the request to + * @param {Object} payload Payload generated by _notificationPayloadFactory + */ _sendNotification = async (webhookUrl, payload) => { await axios.post(webhookUrl, payload); }; + /** + * Send a general notification + * @param {string} webhookUrl URL to send request to + * @param {string} msg Message to send + * @returns {Promise<void>} + */ _handleGeneralNotification = (webhookUrl, msg) => { const payload = this._notificationPayloadFactory({ monitorMessage: msg diff --git a/server/notification-providers/wecom.js b/server/notification-providers/wecom.js index 7ba8c378..4da478ad 100644 --- a/server/notification-providers/wecom.js +++ b/server/notification-providers/wecom.js @@ -24,6 +24,12 @@ class WeCom extends NotificationProvider { } } + /** + * Generate the message to send + * @param {Object} heartbeatJSON Heartbeat details (For Up/Down only) + * @param {string} msg General message + * @returns {Object} + */ composeMessage(heartbeatJSON, msg) { let title; if (msg != null && heartbeatJSON != null && heartbeatJSON['status'] == UP) { From 45f44b183d706aadc6c9af901e0e68fb2e73946e Mon Sep 17 00:00:00 2001 From: Matthew Nickson <mnickson@sidingsmedia.com> Date: Sat, 16 Apr 2022 21:11:45 +0100 Subject: [PATCH 032/124] Add JSDoc to server/model/* Signed-off-by: Matthew Nickson <mnickson@sidingsmedia.com> --- server/model/group.js | 9 +++++ server/model/heartbeat.js | 9 +++++ server/model/incident.js | 5 +++ server/model/monitor.js | 74 +++++++++++++++++++++++++++++++++++---- server/model/tag.js | 5 +++ 5 files changed, 96 insertions(+), 6 deletions(-) diff --git a/server/model/group.js b/server/model/group.js index 567f3865..eb5006e1 100644 --- a/server/model/group.js +++ b/server/model/group.js @@ -3,6 +3,11 @@ const { R } = require("redbean-node"); class Group extends BeanModel { + /** + * Return a object that ready to parse to JSON for public + * Only show necessary data to public + * @returns {Object} + */ async toPublicJSON() { let monitorBeanList = await this.getMonitorList(); let monitorList = []; @@ -19,6 +24,10 @@ class Group extends BeanModel { }; } + /** + * Get all monitors + * @returns {Array<Bean>} + */ async getMonitorList() { return R.convertToBeans("monitor", await R.getAll(` SELECT monitor.* FROM monitor, monitor_group diff --git a/server/model/heartbeat.js b/server/model/heartbeat.js index e0a77c06..38ba6f46 100644 --- a/server/model/heartbeat.js +++ b/server/model/heartbeat.js @@ -13,6 +13,11 @@ const { BeanModel } = require("redbean-node/dist/bean-model"); */ class Heartbeat extends BeanModel { + /** + * Return a object that ready to parse to JSON for public + * Only show necessary data to public + * @returns {Object} + */ toPublicJSON() { return { status: this.status, @@ -22,6 +27,10 @@ class Heartbeat extends BeanModel { }; } + /** + * Return a object that ready to parse to JSON + * @returns {Object} + */ toJSON() { return { monitorID: this.monitor_id, diff --git a/server/model/incident.js b/server/model/incident.js index 89c117e9..e28478f7 100644 --- a/server/model/incident.js +++ b/server/model/incident.js @@ -2,6 +2,11 @@ const { BeanModel } = require("redbean-node/dist/bean-model"); class Incident extends BeanModel { + /** + * Return a object that ready to parse to JSON for public + * Only show necessary data to public + * @returns {Object} + */ toPublicJSON() { return { id: this.id, diff --git a/server/model/monitor.js b/server/model/monitor.js index c4441d63..31c5a9cb 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -26,6 +26,7 @@ class Monitor extends BeanModel { /** * Return a object that ready to parse to JSON for public * Only show necessary data to public + * @returns {Object} */ async toPublicJSON() { return { @@ -36,6 +37,7 @@ class Monitor extends BeanModel { /** * Return a object that ready to parse to JSON + * @returns {Object} */ async toJSON() { @@ -107,10 +109,18 @@ class Monitor extends BeanModel { return Boolean(this.upsideDown); } + /** + * Get status codes that are acceptable + * @returns {Object} + */ getAcceptedStatuscodes() { return JSON.parse(this.accepted_statuscodes_json); } + /** + * Start monitor + * @param {Server} io Socket server instance + */ start(io) { let previousBeat = null; let retries = 0; @@ -463,6 +473,7 @@ class Monitor extends BeanModel { } } + /** Stop monitor */ stop() { clearTimeout(this.heartbeatInterval); this.isStop = true; @@ -472,7 +483,7 @@ class Monitor extends BeanModel { * Helper Method: * returns URL object for further usage * returns null if url is invalid - * @returns {null|URL} + * @returns {(null|URL)} */ getUrl() { try { @@ -485,7 +496,7 @@ class Monitor extends BeanModel { /** * Store TLS info to database * @param checkCertificateResult - * @returns {Promise<object>} + * @returns {Promise<Object>} */ async updateTlsInfo(checkCertificateResult) { let tls_info_bean = await R.findOne("monitor_tls_info", "monitor_id = ?", [ @@ -527,6 +538,12 @@ class Monitor extends BeanModel { return checkCertificateResult; } + /** + * Send statistics to clients + * @param {Server} io Socket server instance + * @param {number} monitorID ID of monitor to send + * @param {number} userID ID of user to send to + */ static async sendStats(io, monitorID, userID) { const hasClients = getTotalClientInRoom(io, userID) > 0; @@ -541,8 +558,8 @@ class Monitor extends BeanModel { } /** - * - * @param duration : int Hours + * Send the average ping to user + * @param {number} duration Hours */ static async sendAvgPing(duration, io, monitorID, userID) { const timeLogger = new TimeLogger(); @@ -562,6 +579,12 @@ class Monitor extends BeanModel { io.to(userID).emit("avgPing", monitorID, avgPing); } + /** + * Send certificate information to information + * @param {Server} io Socket server instance + * @param {number} monitorID ID of monitor to send + * @param {number} userID ID of user to send to + */ static async sendCertInfo(io, monitorID, userID) { let tls_info = await R.findOne("monitor_tls_info", "monitor_id = ?", [ monitorID, @@ -575,7 +598,8 @@ class Monitor extends BeanModel { * Uptime with calculation * Calculation based on: * https://www.uptrends.com/support/kb/reporting/calculation-of-uptime-and-downtime - * @param duration : int Hours + * @param {number} duration Hours + * @param {number} monitorID ID of monitor to calculate */ static async calcUptime(duration, monitorID) { const timeLogger = new TimeLogger(); @@ -641,13 +665,23 @@ class Monitor extends BeanModel { /** * Send Uptime - * @param duration : int Hours + * @param {number} duration Hours + * @param {Server} io Socket server instance + * @param {number} monitorID ID of monitor to send + * @param {number} userID ID of user to send to */ static async sendUptime(duration, io, monitorID, userID) { const uptime = await this.calcUptime(duration, monitorID); io.to(userID).emit("uptime", monitorID, duration, uptime); } + /** + * Has status of monitor changed since last beat? + * @param {boolean} isFirstBeat Is this the first beat of this monitor? + * @param {const} previousBeatStatus Status of the previous beat + * @param {const} currentBeatStatus Status of the current beat + * @returns {boolean} True if is an important beat else false + */ static isImportantBeat(isFirstBeat, previousBeatStatus, currentBeatStatus) { // * ? -> ANY STATUS = important [isFirstBeat] // UP -> PENDING = not important @@ -666,6 +700,12 @@ class Monitor extends BeanModel { return isImportant; } + /** + * Send a notification about a monitor + * @param {boolean} isFirstBeat Is this beat the first of this monitor? + * @param {Monitor} monitor The monitor to send a notificaton about + * @param {Bean} bean Status information about monitor + */ static async sendNotification(isFirstBeat, monitor, bean) { if (!isFirstBeat || bean.status === DOWN) { const notificationList = await Monitor.getNotificationList(monitor); @@ -690,6 +730,11 @@ class Monitor extends BeanModel { } } + /** + * Get list of notification providers for a given monitor + * @param {Monitor} monitor Monitor to get notification providers for + * @returns {Promise<LooseObject<any>[]>} + */ static async getNotificationList(monitor) { let notificationList = await R.getAll("SELECT notification.* FROM notification, monitor_notification WHERE monitor_id = ? AND monitor_notification.notification_id = notification.id ", [ monitor.id, @@ -697,6 +742,10 @@ class Monitor extends BeanModel { return notificationList; } + /** + * Send notification about a certificate + * @param {Object} tlsInfoObject Information about certificate + */ async sendCertNotification(tlsInfoObject) { if (tlsInfoObject && tlsInfoObject.certInfo && tlsInfoObject.certInfo.daysRemaining) { const notificationList = await Monitor.getNotificationList(this); @@ -708,6 +757,14 @@ class Monitor extends BeanModel { } } + /** + * Send a certificate notification when certificate expires in less + * than target days + * @param {number} daysRemaining Number of days remaining on certifcate + * @param {number} targetDays Number of days to alert after + * @param {Array<LooseObject<any>>} notificationList List of notification providers + * @returns {Promise<void>} + */ async sendCertNotificationByTargetDays(daysRemaining, targetDays, notificationList) { if (daysRemaining > targetDays) { @@ -755,6 +812,11 @@ class Monitor extends BeanModel { } } + /** + * Get the status of the previous heartbeat + * @param {number} monitorID ID of monitor to check + * @returns {Promise<LooseObject<any>>} + */ static async getPreviousHeartbeat(monitorID) { return await R.getRow(` SELECT status, time FROM heartbeat diff --git a/server/model/tag.js b/server/model/tag.js index 748280a7..5a8d97d3 100644 --- a/server/model/tag.js +++ b/server/model/tag.js @@ -1,6 +1,11 @@ const { BeanModel } = require("redbean-node/dist/bean-model"); class Tag extends BeanModel { + + /** + * Return a object that ready to parse to JSON + * @returns {Object} + */ toJSON() { return { id: this._id, From 03b2d8d5210b354562e73136934eeb849c90f35b Mon Sep 17 00:00:00 2001 From: Matthew Nickson <mnickson@sidingsmedia.com> Date: Wed, 20 Apr 2022 19:56:40 +0100 Subject: [PATCH 033/124] Add JSDoc to server/* Signed-off-by: Matthew Nickson <mnickson@sidingsmedia.com> --- server/2fa.js | 6 +- server/auth.js | 21 +++++-- server/check-version.js | 6 ++ server/client.js | 26 +++++++-- server/database.js | 37 ++++++++++--- server/image-data-uri.js | 17 ++++++ server/jobs.js | 5 ++ server/notification.js | 58 ++++++++++++++------ server/password-hash.js | 28 ++++++++-- server/ping-lite.js | 29 +++++++--- server/prometheus.js | 8 +++ server/rate-limiter.js | 20 +++++++ server/server.js | 64 +++++++++++++++++++++- server/util-server.js | 115 +++++++++++++++++++++++++++++++++++---- 14 files changed, 380 insertions(+), 60 deletions(-) diff --git a/server/2fa.js b/server/2fa.js index bc8145cf..c7076da2 100644 --- a/server/2fa.js +++ b/server/2fa.js @@ -1,8 +1,12 @@ -const { checkLogin } = require("./util-server"); const { R } = require("redbean-node"); class TwoFA { + /** + * Disable 2FA for specified user + * @param {number} userID ID of user to disable + * @returns {Promise<void>} + */ static async disable2FA(userID) { return await R.exec("UPDATE `user` SET twofa_status = 0 WHERE id = ? ", [ userID, diff --git a/server/auth.js b/server/auth.js index c476ea1e..422dd666 100644 --- a/server/auth.js +++ b/server/auth.js @@ -6,10 +6,10 @@ const { debug } = require("../src/util"); const { loginRateLimiter } = require("./rate-limiter"); /** - * - * @param username : string - * @param password : string - * @returns {Promise<Bean|null>} + * Login to web app + * @param {string} username + * @param {string} password + * @returns {Promise<(Bean|null)>} */ exports.login = async function (username, password) { let user = await R.findOne("user", " username = ? AND active = 1 ", [ @@ -30,6 +30,19 @@ exports.login = async function (username, password) { return null; }; +/** + * Callback for myAuthorizer + * @callback myAuthorizerCB + * @param {any} err Any error encountered + * @param {boolean} authorized Is the client authorized? + */ + +/** + * Custom authorizer for express-basic-auth + * @param {string} username + * @param {string} password + * @param {myAuthorizerCB} callback + */ function myAuthorizer(username, password, callback) { setting("disableAuth").then((result) => { if (result) { diff --git a/server/check-version.js b/server/check-version.js index a3465ddf..daec4975 100644 --- a/server/check-version.js +++ b/server/check-version.js @@ -6,6 +6,7 @@ exports.latestVersion = null; let interval; +/** Start 48 hour check interval */ exports.startInterval = () => { let check = async () => { try { @@ -28,6 +29,11 @@ exports.startInterval = () => { interval = setInterval(check, 3600 * 1000 * 48); }; +/** + * Enable the check update feature + * @param {boolean} value Should the check update feature be enabled? + * @returns {Promise<void>} + */ exports.enableCheckUpdate = async (value) => { await setSetting("checkUpdate", value); diff --git a/server/client.js b/server/client.js index c7b3bc16..8b0f7520 100644 --- a/server/client.js +++ b/server/client.js @@ -7,6 +7,11 @@ const { io } = require("./server"); const { setting } = require("./util-server"); const checkVersion = require("./check-version"); +/** + * Send list of notification providers to client + * @param {Socket} socket Socket.io socket instance + * @returns {Promise<Bean[]>} + */ async function sendNotificationList(socket) { const timeLogger = new TimeLogger(); @@ -28,8 +33,11 @@ async function sendNotificationList(socket) { /** * Send Heartbeat History list to socket - * @param toUser True = send to all browsers with the same user id, False = send to the current browser only - * @param overwrite Overwrite client-side's heartbeat list + * @param {Socket} socket Socket.io instance + * @param {number} monitorID ID of monitor to send heartbeat history + * @param {boolean} [toUser=false] True = send to all browsers with the same user id, False = send to the current browser only + * @param {boolean} [overwrite=false] Overwrite client-side's heartbeat list + * @returns {Promise<void>} */ async function sendHeartbeatList(socket, monitorID, toUser = false, overwrite = false) { const timeLogger = new TimeLogger(); @@ -56,10 +64,11 @@ async function sendHeartbeatList(socket, monitorID, toUser = false, overwrite = /** * Important Heart beat list (aka event list) - * @param socket - * @param monitorID - * @param toUser True = send to all browsers with the same user id, False = send to the current browser only - * @param overwrite Overwrite client-side's heartbeat list + * @param {Socket} socket Socket.io instance + * @param {number} monitorID ID of monitor to send heartbeat history + * @param {boolean} [toUser=false] True = send to all browsers with the same user id, False = send to the current browser only + * @param {boolean} [overwrite=false] Overwrite client-side's heartbeat list + * @returns {Promise<void>} */ async function sendImportantHeartbeatList(socket, monitorID, toUser = false, overwrite = false) { const timeLogger = new TimeLogger(); @@ -83,6 +92,11 @@ async function sendImportantHeartbeatList(socket, monitorID, toUser = false, ove } +/** + * Send application info + * @param {Socket} socket Socket.io socket instance + * @returns {Promise<void>} + */ async function sendInfo(socket) { socket.emit("info", { version: checkVersion.version, diff --git a/server/database.js b/server/database.js index afcace70..04c51c21 100644 --- a/server/database.js +++ b/server/database.js @@ -63,6 +63,10 @@ class Database { static noReject = true; + /** + * Initialize the database + * @param {Object} args Arguments to initialize DB with + */ static init(args) { // Data Directory (must be end with "/") Database.dataDir = process.env.DATA_DIR || args["data-dir"] || "./data/"; @@ -80,6 +84,12 @@ class Database { console.log(`Data Dir: ${Database.dataDir}`); } + /** + * Connect to the database + * @param {boolean} [testMode=false] Should the connection be + * started in test mode? + * @returns {Promise<void>} + */ static async connect(testMode = false) { const acquireConnectionTimeout = 120 * 1000; @@ -129,6 +139,7 @@ class Database { console.log("SQLite Version: " + await R.getCell("SELECT sqlite_version()")); } + /** Patch the database */ static async patch() { let version = parseInt(await setting("database_version")); @@ -173,7 +184,9 @@ class Database { } /** + * Patch DB using new process * Call it from patch() only + * @private * @returns {Promise<void>} */ static async patch2() { @@ -212,9 +225,12 @@ class Database { } /** + * Patch database using new patching process * Used it patch2() only + * @private * @param sqlFilename * @param databasePatchedFiles + * @returns {Promise<void>} */ static async patch2Recursion(sqlFilename, databasePatchedFiles) { let value = this.patchList[sqlFilename]; @@ -249,12 +265,12 @@ class Database { } /** - * Sadly, multi sql statements is not supported by many sqlite libraries, I have to implement it myself - * @param filename + * Load an SQL file and execute it + * @param filename Filename of SQL file to import * @returns {Promise<void>} */ static async importSQLFile(filename) { - + // Sadly, multi sql statements is not supported by many sqlite libraries, I have to implement it myself await R.getCell("SELECT 1"); let text = fs.readFileSync(filename).toString(); @@ -282,6 +298,10 @@ class Database { } } + /** + * Aquire a direct connection to database + * @returns {any} + */ static getBetterSQLite3Database() { return R.knex.client.acquireConnection(); } @@ -317,7 +337,7 @@ class Database { /** * One backup one time in this process. * Reset this.backupPath if you want to backup again - * @param version + * @param {string} version Version code of backup */ static backup(version) { if (! this.backupPath) { @@ -339,9 +359,7 @@ class Database { } } - /** - * - */ + /** Restore from most recent backup */ static restore() { if (this.backupPath) { console.error("Patching the database failed!!! Restoring the backup"); @@ -383,6 +401,7 @@ class Database { } } + /** Get the size of the database */ static getSize() { debug("Database.getSize()"); let stats = fs.statSync(Database.path); @@ -390,6 +409,10 @@ class Database { return stats.size; } + /** + * Shrink the database + * @returns {Promise<void>} + */ static async shrink() { await R.exec("VACUUM"); } diff --git a/server/image-data-uri.js b/server/image-data-uri.js index 3ccaab7d..b5b6f2d6 100644 --- a/server/image-data-uri.js +++ b/server/image-data-uri.js @@ -6,6 +6,11 @@ let fs = require("fs"); let ImageDataURI = (() => { + /** + * Decode the data:image/ URI + * @param {string} dataURI data:image/ URI to decode + * @returns {Object} + */ function decode(dataURI) { if (!/data:image\//.test(dataURI)) { console.log("ImageDataURI :: Error :: It seems that it is not an Image Data URI. Couldn't match \"data:image/\""); @@ -20,6 +25,12 @@ let ImageDataURI = (() => { }; } + /** + * Endcode an image into data:image/ URI + * @param {(Buffer|string)} data Data to encode + * @param {string} mediaType Media type of data + * @returns {(string|null)} + */ function encode(data, mediaType) { if (!data || !mediaType) { console.log("ImageDataURI :: Error :: Missing some of the required params: data, mediaType "); @@ -33,6 +44,12 @@ let ImageDataURI = (() => { return dataImgBase64; } + /** + * Write data URI to file + * @param {string} dataURI data:image/ URI + * @param {string} filePath Path to write file to + * @returns {Promise<string>} + */ function outputFile(dataURI, filePath) { filePath = filePath || "./"; return new Promise((resolve, reject) => { diff --git a/server/jobs.js b/server/jobs.js index 0469d5ca..e4e7514b 100644 --- a/server/jobs.js +++ b/server/jobs.js @@ -9,6 +9,11 @@ const jobs = [ }, ]; +/** + * Initialize background jobs + * @param {Object} args Arguments to pass to workers + * @returns {Bree} + */ const initBackgroundJobs = function (args) { const bree = new Bree({ root: path.resolve("server", "jobs"), diff --git a/server/notification.js b/server/notification.js index 4d72c81c..a95af022 100644 --- a/server/notification.js +++ b/server/notification.js @@ -32,6 +32,7 @@ class Notification { providerList = {}; + /** Initialize the notification providers */ static init() { console.log("Prepare Notification Providers"); @@ -81,13 +82,13 @@ class Notification { } /** - * - * @param notification : BeanModel - * @param msg : string General Message - * @param monitorJSON : object Monitor details (For Up/Down only) - * @param heartbeatJSON : object Heartbeat details (For Up/Down only) + * Send a notification + * @param {BeanModel} notification + * @param {string} msg General Message + * @param {Object} monitorJSON Monitor details (For Up/Down only) + * @param {Object} heartbeatJSON Heartbeat details (For Up/Down only) * @returns {Promise<string>} Successful msg - * Throw Error with fail msg + * @throws Error with fail msg */ static async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { if (this.providerList[notification.type]) { @@ -97,28 +98,35 @@ class Notification { } } + /** + * Save a notification + * @param {Object} notification Notification to save + * @param {?number} notificationID ID of notification to update + * @param {number} userID ID of user who adds notification + * @returns {Promise<Bean>} + */ static async save(notification, notificationID, userID) { - let bean + let bean; if (notificationID) { bean = await R.findOne("notification", " id = ? AND user_id = ? ", [ notificationID, userID, - ]) + ]); if (! bean) { - throw new Error("notification not found") + throw new Error("notification not found"); } } else { - bean = R.dispense("notification") + bean = R.dispense("notification"); } bean.name = notification.name; bean.user_id = userID; bean.config = JSON.stringify(notification); bean.is_default = notification.isDefault || false; - await R.store(bean) + await R.store(bean); if (notification.applyExisting) { await applyNotificationEveryMonitor(bean.id, userID); @@ -127,19 +135,29 @@ class Notification { return bean; } + /** + * Delete a notification + * @param {number} notificationID ID of notification to delete + * @param {number} userID ID of user who created notification + * @returns {Promise<void>} + */ static async delete(notificationID, userID) { let bean = await R.findOne("notification", " id = ? AND user_id = ? ", [ notificationID, userID, - ]) + ]); if (! bean) { - throw new Error("notification not found") + throw new Error("notification not found"); } - await R.trash(bean) + await R.trash(bean); } + /** + * Check if apprise exists + * @returns {boolean} Does the command apprise exist? + */ static checkApprise() { let commandExistsSync = require("command-exists").sync; let exists = commandExistsSync("apprise"); @@ -148,6 +166,12 @@ class Notification { } +/** + * Apply the notification to every monitor + * @param {number} notificationID ID of notification to apply + * @param {number} userID ID of user who created notification + * @returns {Promise<void>} + */ async function applyNotificationEveryMonitor(notificationID, userID) { let monitors = await R.getAll("SELECT id FROM monitor WHERE user_id = ?", [ userID @@ -157,17 +181,17 @@ async function applyNotificationEveryMonitor(notificationID, userID) { let checkNotification = await R.findOne("monitor_notification", " monitor_id = ? AND notification_id = ? ", [ monitors[i].id, notificationID, - ]) + ]); if (! checkNotification) { let relation = R.dispense("monitor_notification"); relation.monitor_id = monitors[i].id; relation.notification_id = notificationID; - await R.store(relation) + await R.store(relation); } } } module.exports = { Notification, -} +}; diff --git a/server/password-hash.js b/server/password-hash.js index 91e5e1ad..482aa475 100644 --- a/server/password-hash.js +++ b/server/password-hash.js @@ -2,22 +2,42 @@ const passwordHashOld = require("password-hash"); const bcrypt = require("bcryptjs"); const saltRounds = 10; +/** + * Hash a password + * @param {string} password + * @returns {string} + */ exports.generate = function (password) { return bcrypt.hashSync(password, saltRounds); -} +}; +/** + * Verify a password against a hash + * @param {string} password + * @param {string} hash + * @returns {boolean} Does the password match the hash? + */ exports.verify = function (password, hash) { if (isSHA1(hash)) { - return passwordHashOld.verify(password, hash) + return passwordHashOld.verify(password, hash); } return bcrypt.compareSync(password, hash); -} +}; +/** + * Is the hash a SHA1 hash + * @param {string} hash + * @returns {boolean} + */ function isSHA1(hash) { return (typeof hash === "string" && hash.startsWith("sha1")) } +/** + * Does the hash need to be rehashed? + * @returns {boolean} + */ exports.needRehash = function (hash) { return isSHA1(hash); -} +}; diff --git a/server/ping-lite.js b/server/ping-lite.js index 0075e80b..2c84a473 100644 --- a/server/ping-lite.js +++ b/server/ping-lite.js @@ -8,6 +8,11 @@ const util = require("./util-server"); module.exports = Ping; +/** + * Constructor for ping class + * @param {string} host Host to ping + * @param {string} options Command line options for ping + */ function Ping(host, options) { if (!host) { throw new Error("You must specify a host to ping!"); @@ -75,8 +80,17 @@ function Ping(host, options) { Ping.prototype.__proto__ = events.EventEmitter.prototype; -// SEND A PING -// =========== +/** + * Callback for send + * @callback pingCB + * @param {any} err Any error encountered + * @param {number} ms Ping time in ms + */ + +/** + * Send a ping + * @param {pingCB} callback Callback to call with results + */ Ping.prototype.send = function (callback) { let self = this; callback = callback || function (err, ms) { @@ -145,8 +159,10 @@ Ping.prototype.send = function (callback) { } }; -// CALL Ping#send(callback) ON A TIMER -// =================================== +/** + * Ping every interval + * @param {pingCB} callback Callback to call with results + */ Ping.prototype.start = function (callback) { let self = this; this._i = setInterval(function () { @@ -155,8 +171,7 @@ Ping.prototype.start = function (callback) { self.send(callback); }; -// STOP SENDING PINGS -// ================== +/** Stop sending pings */ Ping.prototype.stop = function () { clearInterval(this._i); }; @@ -165,7 +180,7 @@ Ping.prototype.stop = function () { * Try to convert to UTF-8 for Windows, as the ping's output on Windows is not UTF-8 and could be in other languages * Thank @pemassi * https://github.com/louislam/uptime-kuma/issues/570#issuecomment-941984094 - * @param data + * @param {any} data * @returns {string} */ function convertOutput(data) { diff --git a/server/prometheus.js b/server/prometheus.js index f91b0a13..acf2302b 100644 --- a/server/prometheus.js +++ b/server/prometheus.js @@ -34,6 +34,9 @@ const monitor_status = new PrometheusClient.Gauge({ class Prometheus { monitorLabelValues = {} + /** + * @param {Object} monitor Monitor object to monitor + */ constructor(monitor) { this.monitorLabelValues = { monitor_name: monitor.name, @@ -44,6 +47,11 @@ class Prometheus { }; } + /** + * Update the metrics page + * @param {Object} heartbeat Heartbeat details + * @param {Object} tlsInfo TLS details + */ update(heartbeat, tlsInfo) { if (typeof tlsInfo !== "undefined") { diff --git a/server/rate-limiter.js b/server/rate-limiter.js index 0bacc14c..2df956c0 100644 --- a/server/rate-limiter.js +++ b/server/rate-limiter.js @@ -2,11 +2,26 @@ const { RateLimiter } = require("limiter"); const { debug } = require("../src/util"); class KumaRateLimiter { + /** + * @param {Object} config Rate limiter configuration object + */ constructor(config) { this.errorMessage = config.errorMessage; this.rateLimiter = new RateLimiter(config); } + /** + * Callback for pass + * @callback passCB + * @param {Object} err Too many requests + */ + + /** + * Should the request be passed through + * @param {passCB} callback + * @param {number} [num=1] Number of tokens to remove + * @returns {Promise<boolean>} + */ async pass(callback, num = 1) { const remainingRequests = await this.removeTokens(num); debug("Rate Limit (remainingRequests):" + remainingRequests); @@ -22,6 +37,11 @@ class KumaRateLimiter { return true; } + /** + * Remove a given number of tokens + * @param {number} [num=1] Number of tokens to remove + * @returns {Promise<number>} + */ async removeTokens(num = 1) { return await this.rateLimiter.removeTokens(num); } diff --git a/server/server.js b/server/server.js index 153cac4f..88394472 100644 --- a/server/server.js +++ b/server/server.js @@ -1362,6 +1362,13 @@ exports.entryPage = "dashboard"; })(); +/** + * Update notifications for a given monitor + * @param {number} monitorID ID of monitor to update + * @param {Array<number>} notificationIDList List of new notification + * providers to add + * @returns {Promise<void>} + */ async function updateMonitorNotification(monitorID, notificationIDList) { await R.exec("DELETE FROM monitor_notification WHERE monitor_id = ? ", [ monitorID, @@ -1377,6 +1384,13 @@ async function updateMonitorNotification(monitorID, notificationIDList) { } } +/** + * Check if a given user owns a specific monitor + * @param {number} userID + * @param {number} monitorID + * @returns {Promise<void>} + * @throws {Error} The specified user does not own the monitor + */ async function checkOwner(userID, monitorID) { let row = await R.getRow("SELECT id FROM monitor WHERE id = ? AND user_id = ? ", [ monitorID, @@ -1388,12 +1402,23 @@ async function checkOwner(userID, monitorID) { } } +/** + * Send the monitor list to clients + * @param {Socket} socket Socket.io instance + * @returns {Object} + */ async function sendMonitorList(socket) { let list = await getMonitorJSONList(socket.userID); io.to(socket.userID).emit("monitorList", list); return list; } +/** + * Function called after user login + * @param {Socket} socket Socket.io instance + * @param {Object} user User object + * @returns {Promise<void>} + */ async function afterLogin(socket, user) { socket.userID = user.id; socket.join(user.id); @@ -1416,6 +1441,11 @@ async function afterLogin(socket, user) { } } +/** + * Get a JSON representation of monitor list + * @param {number} userID + * @returns {Promise<Object>} + */ async function getMonitorJSONList(userID) { let result = {}; @@ -1430,6 +1460,12 @@ async function getMonitorJSONList(userID) { return result; } +/** + * Initialize the database + * @param {boolean} [testMode=false] Should the connection be + * started in test mode? + * @returns {Promise<void>} + */ async function initDatabase(testMode = false) { if (! fs.existsSync(Database.path)) { console.log("Copying Database"); @@ -1464,6 +1500,12 @@ async function initDatabase(testMode = false) { jwtSecret = jwtSecretBean.value; } +/** + * Start the specified monitor + * @param {number} userID ID of user who owns monitor + * @param {number} monitorID ID of monitor to start + * @returns {Promise<void>} + */ async function startMonitor(userID, monitorID) { await checkOwner(userID, monitorID); @@ -1486,10 +1528,22 @@ async function startMonitor(userID, monitorID) { monitor.start(io); } +/** + * Restart a given monitor + * @param {number} userID ID of user who owns monitor + * @param {number} monitorID ID of monitor to start + * @returns {Promise<void>} + */ async function restartMonitor(userID, monitorID) { return await startMonitor(userID, monitorID); } +/** + * Pause a given monitor +* @param {number} userID ID of user who owns monitor + * @param {number} monitorID ID of monitor to start + * @returns {Promise<void>} + */ async function pauseMonitor(userID, monitorID) { await checkOwner(userID, monitorID); @@ -1505,9 +1559,7 @@ async function pauseMonitor(userID, monitorID) { } } -/** - * Resume active monitors - */ +/** Resume active monitors */ async function startMonitors() { let list = await R.find("monitor", " active = 1 "); @@ -1522,6 +1574,11 @@ async function startMonitors() { } } +/** + * Shutdown the application + * @param {string} signal Shutdown signal + * @returns {Promise<void>} + */ async function shutdownFunction(signal) { console.log("Shutdown requested"); console.log("Called signal: " + signal); @@ -1535,6 +1592,7 @@ async function shutdownFunction(signal) { await Database.close(); } +/** Final function called before application exits */ function finalFunction() { console.log("Graceful shutdown successful!"); } diff --git a/server/util-server.js b/server/util-server.js index 2264ebea..8f48505d 100644 --- a/server/util-server.js +++ b/server/util-server.js @@ -37,6 +37,12 @@ exports.initJWTSecret = async () => { return jwtSecretBean; }; +/** + * Send TCP request to specified hostname and port + * @param {string} hostname Hostname / address of machine + * @param {number} port TCP port to test + * @returns {Promise<number>} Maximum time in ms rounded to nearest integer + */ exports.tcping = function (hostname, port) { return new Promise((resolve, reject) => { tcpp.ping({ @@ -58,6 +64,11 @@ exports.tcping = function (hostname, port) { }); }; +/** + * Ping the specified machine + * @param {string} hostname Hostname / address of machine + * @returns {Promise<number>} Time for ping in ms rounded to nearest integer + */ exports.ping = async (hostname) => { try { return await exports.pingAsync(hostname); @@ -71,6 +82,12 @@ exports.ping = async (hostname) => { } }; +/** + * Ping the specified machine + * @param {string} hostname Hostname / address of machine to ping + * @param {boolean} ipv6 Should IPv6 be used? + * @returns {Promise<number>} Time for ping in ms rounded to nearest integer + */ exports.pingAsync = function (hostname, ipv6 = false) { return new Promise((resolve, reject) => { const ping = new Ping(hostname, { @@ -89,6 +106,14 @@ exports.pingAsync = function (hostname, ipv6 = false) { }); }; +// `string[]`, `Object[]` and `Object`. +/** + * Resolves a given record using the specified DNS server + * @param {string} hostname The hostname of the record to lookup + * @param {string} resolver_server The DNS server to use + * @param {string} rrtype The type of record to request + * @returns {Promise<(string[]|Object[]|Object)>} + */ exports.dnsResolve = function (hostname, resolver_server, rrtype) { const resolver = new Resolver(); resolver.setServers([resolver_server]); @@ -113,6 +138,11 @@ exports.dnsResolve = function (hostname, resolver_server, rrtype) { }); }; +/** + * Retrieve value of setting based on key + * @param {string} key Key of setting to retrieve + * @returns {Promise<Object>} Object representation of setting + */ exports.setting = async function (key) { let value = await R.getCell("SELECT `value` FROM setting WHERE `key` = ? ", [ key, @@ -127,6 +157,13 @@ exports.setting = async function (key) { } }; +/** + * Sets the specified setting to specifed value + * @param {string} key Key of setting to set + * @param {any} value Value to set to + * @param {?string} type Type of setting + * @returns {Promise<void>} + */ exports.setSetting = async function (key, value, type = null) { let bean = await R.findOne("setting", " `key` = ? ", [ key, @@ -140,6 +177,11 @@ exports.setSetting = async function (key, value, type = null) { await R.store(bean); }; +/** + * Get settings based on type + * @param {?string} type The type of setting + * @returns {Promise<Bean>} + */ exports.getSettings = async function (type) { let list = await R.getAll("SELECT `key`, `value` FROM setting WHERE `type` = ? ", [ type, @@ -158,6 +200,12 @@ exports.getSettings = async function (type) { return result; }; +/** + * Set settings based on type + * @param {?string} type Type of settings to set + * @param {Object} data Values of settings + * @returns {Promise<void>} + */ exports.setSettings = async function (type, data) { let keyList = Object.keys(data); @@ -184,12 +232,23 @@ exports.setSettings = async function (type, data) { }; // ssl-checker by @dyaa -// param: res - response object from axios -// return an object containing the certificate information +//https://github.com/dyaa/ssl-checker/blob/master/src/index.ts +/** + * Get number of days between two dates + * @param {Date} validFrom Start date + * @param {Date} validTo End date + * @returns {number} + */ const getDaysBetween = (validFrom, validTo) => Math.round(Math.abs(+validFrom - +validTo) / 8.64e7); +/** + * Get days remaining from a time range + * @param {Date} validFrom Start date + * @param {Date} validTo End date + * @returns {number} + */ const getDaysRemaining = (validFrom, validTo) => { const daysRemaining = getDaysBetween(validFrom, validTo); if (new Date(validTo).getTime() < new Date().getTime()) { @@ -198,8 +257,11 @@ const getDaysRemaining = (validFrom, validTo) => { return daysRemaining; }; -// Fix certificate Info for display -// param: info - the chain obtained from getPeerCertificate() +/** + * Fix certificate info for display + * @param {Object} info The chain obtained from getPeerCertificate() + * @returns {Object} An object representing certificate information + */ const parseCertificateInfo = function (info) { let link = info; let i = 0; @@ -239,6 +301,11 @@ const parseCertificateInfo = function (info) { return info; }; +/** + * Check if certificate is valid + * @param {Object} res Response object from axios + * @returns {Object} Object containing certificate information + */ exports.checkCertificate = function (res) { const info = res.request.res.socket.getPeerCertificate(true); const valid = res.request.res.socket.authorized || false; @@ -252,12 +319,13 @@ exports.checkCertificate = function (res) { }; }; -// Check if the provided status code is within the accepted ranges -// Param: status - the status code to check -// Param: accepted_codes - an array of accepted status codes -// Return: true if the status code is within the accepted ranges, false otherwise -// Will throw an error if the provided status code is not a valid range string or code string - +/** + * Check if the provided status code is within the accepted ranges + * @param {string} status The status code to check + * @param {Array<string>} accepted_codes An array of accepted status codes + * @returns {boolean} True if status code within range, false otherwise + * @throws {Error} Will throw an error if the provided status code is not a valid range string or code string + */ exports.checkStatusCode = function (status, accepted_codes) { if (accepted_codes == null || accepted_codes.length === 0) { return false; @@ -281,6 +349,12 @@ exports.checkStatusCode = function (status, accepted_codes) { return false; }; +/** + * Get total number of clients in room + * @param {Server} io Socket server instance + * @param {string} roomName Name of room to check + * @returns {number} + */ exports.getTotalClientInRoom = (io, roomName) => { const sockets = io.sockets; @@ -304,23 +378,36 @@ exports.getTotalClientInRoom = (io, roomName) => { } }; +/** + * Allow CORS all origins if development + * @param {Object} res Response object from axios + */ exports.allowDevAllOrigin = (res) => { if (process.env.NODE_ENV === "development") { exports.allowAllOrigin(res); } }; +/** + * Allow CORS all origins + * @param {Object} res Response object from axios + */ exports.allowAllOrigin = (res) => { res.header("Access-Control-Allow-Origin", "*"); res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); }; +/** + * Check if a user is logged in + * @param {Socket} socket Socket instance + */ exports.checkLogin = (socket) => { if (! socket.userID) { throw new Error("You are not logged in."); } }; +/** Start Unit tests */ exports.startUnitTest = async () => { console.log("Starting unit test..."); const npm = /^win/.test(process.platform) ? "npm.cmd" : "npm"; @@ -341,7 +428,8 @@ exports.startUnitTest = async () => { }; /** - * @param body : Buffer + * Convert unknown string to UTF8 + * @param {Uint8Array} body Buffer * @returns {string} */ exports.convertToUTF8 = (body) => { @@ -359,6 +447,11 @@ try { }); } catch (_) { } +/** + * Write error to log file + * @param {any} error The error to write + * @param {boolean} outputToConsole Should the error also be output to console? + */ exports.errorLog = (error, outputToConsole = true) => { try { if (logFile) { From 3d04befc1f1f547eb366056003126ffd56aa8be3 Mon Sep 17 00:00:00 2001 From: Matthew Nickson <mnickson@sidingsmedia.com> Date: Thu, 21 Apr 2022 16:43:24 +0100 Subject: [PATCH 034/124] Add JSDoc to server/routers/* Signed-off-by: Matthew Nickson <mnickson@sidingsmedia.com> --- server/routers/api-router.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/server/routers/api-router.js b/server/routers/api-router.js index 578655e2..6d1a8d7b 100644 --- a/server/routers/api-router.js +++ b/server/routers/api-router.js @@ -196,6 +196,11 @@ router.get("/api/status-page/heartbeat/:slug", cache("1 minutes"), async (reques } }); +/** + * Send a 403 response + * @param {Object} res Express response object + * @param {string} [msg=""] Message to send + */ function send403(res, msg = "") { res.status(403).json({ "status": "fail", From 068b9205537cb2fb20677f9b6daf9b16d4a0087b Mon Sep 17 00:00:00 2001 From: Matthew Nickson <mnickson@sidingsmedia.com> Date: Thu, 21 Apr 2022 17:10:23 +0100 Subject: [PATCH 035/124] Add JSDoc to server/socket-handlers/* Signed-off-by: Matthew Nickson <mnickson@sidingsmedia.com> --- .../cloudflared-socket-handler.js | 18 ++++++++++++++++++ .../socket-handlers/database-socket-handler.js | 4 ++++ server/socket-handlers/proxy-socket-handler.js | 4 ++++ .../status-page-socket-handler.js | 5 +++++ 4 files changed, 31 insertions(+) diff --git a/server/socket-handlers/cloudflared-socket-handler.js b/server/socket-handlers/cloudflared-socket-handler.js index fa20dff8..4c34f52f 100644 --- a/server/socket-handlers/cloudflared-socket-handler.js +++ b/server/socket-handlers/cloudflared-socket-handler.js @@ -6,15 +6,28 @@ const io = UptimeKumaServer.getInstance().io; const prefix = "cloudflared_"; const cloudflared = new CloudflaredTunnel(); +/** + * Change running state + * @param {string} running Is it running? + * @param {string} message Message to pass + */ cloudflared.change = (running, message) => { io.to("cloudflared").emit(prefix + "running", running); io.to("cloudflared").emit(prefix + "message", message); }; +/** + * Emit an error message + * @param {string} errorMessage + */ cloudflared.error = (errorMessage) => { io.to("cloudflared").emit(prefix + "errorMessage", errorMessage); }; +/** + * Handler for cloudflared + * @param {Socket} socket Socket.io instance + */ module.exports.cloudflaredSocketHandler = (socket) => { socket.on(prefix + "join", async () => { @@ -69,6 +82,10 @@ module.exports.cloudflaredSocketHandler = (socket) => { }; +/** + * Automatically start cloudflared + * @param {string} token Cloudflared tunnel token + */ module.exports.autoStart = async (token) => { if (!token) { token = await setting("cloudflaredTunnelToken"); @@ -85,6 +102,7 @@ module.exports.autoStart = async (token) => { } }; +/** Stop cloudflared */ module.exports.stop = async () => { console.log("Stop cloudflared"); if (cloudflared) { diff --git a/server/socket-handlers/database-socket-handler.js b/server/socket-handlers/database-socket-handler.js index 42fdb129..041cbba0 100644 --- a/server/socket-handlers/database-socket-handler.js +++ b/server/socket-handlers/database-socket-handler.js @@ -1,6 +1,10 @@ const { checkLogin } = require("../util-server"); const Database = require("../database"); +/** + * Handlers for database + * @param {Socket} socket Socket.io instance + */ module.exports = (socket) => { // Post or edit incident diff --git a/server/socket-handlers/proxy-socket-handler.js b/server/socket-handlers/proxy-socket-handler.js index 7862ff16..e67a829f 100644 --- a/server/socket-handlers/proxy-socket-handler.js +++ b/server/socket-handlers/proxy-socket-handler.js @@ -4,6 +4,10 @@ const { sendProxyList } = require("../client"); const { UptimeKumaServer } = require("../uptime-kuma-server"); const server = UptimeKumaServer.getInstance(); +/** + * Handlers for proxy + * @param {Socket} socket Socket.io instance + */ module.exports.proxySocketHandler = (socket) => { socket.on("addProxy", async (proxy, proxyID, callback) => { try { diff --git a/server/socket-handlers/status-page-socket-handler.js b/server/socket-handlers/status-page-socket-handler.js index 20d19382..0a0dc686 100644 --- a/server/socket-handlers/status-page-socket-handler.js +++ b/server/socket-handlers/status-page-socket-handler.js @@ -8,6 +8,10 @@ const apicache = require("../modules/apicache"); const StatusPage = require("../model/status_page"); const { UptimeKumaServer } = require("../uptime-kuma-server"); +/** + * Socket handlers for status page + * @param {Socket} socket Socket.io instance to add listeners on + */ module.exports.statusPageSocketHandler = (socket) => { // Post or edit incident @@ -338,6 +342,7 @@ module.exports.statusPageSocketHandler = (socket) => { /** * Check slug a-z, 0-9, - only * Regex from: https://stackoverflow.com/questions/22454258/js-regex-string-validation-for-slug + * @param {string} slug Slug to test */ function checkSlug(slug) { if (typeof slug !== "string") { From 4ddbf7192099fc2c271761b0dc3080cb9954fa26 Mon Sep 17 00:00:00 2001 From: Matthew Nickson <mnickson@sidingsmedia.com> Date: Thu, 21 Apr 2022 17:15:39 +0100 Subject: [PATCH 036/124] Fixed trailing spaces in database.js Signed-off-by: Matthew Nickson <mnickson@sidingsmedia.com> --- server/database.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/database.js b/server/database.js index 9cfcd7b8..4189754a 100644 --- a/server/database.js +++ b/server/database.js @@ -93,7 +93,7 @@ class Database { * Connect to the database * @param {boolean} [testMode=false] Should the connection be * started in test mode? - * @param {boolean} [autoloadModels=true] Should models be + * @param {boolean} [autoloadModels=true] Should models be * automatically loaded? * @param {boolean} [noLog=false] Should logs not be output? * @returns {Promise<void>} @@ -243,7 +243,7 @@ class Database { await setSetting("databasePatchedFiles", databasePatchedFiles); } - /** + /** * Migrate status page value in setting to "status_page" table * @returns {Promise<void>} */ From 0083485d4caa06c15064c06e6bc42f7ad2e2d190 Mon Sep 17 00:00:00 2001 From: Matthew Nickson <mnickson@sidingsmedia.com> Date: Thu, 21 Apr 2022 18:30:04 +0100 Subject: [PATCH 037/124] Updated server/model/* JSDoc to match new methods Signed-off-by: Matthew Nickson <mnickson@sidingsmedia.com> --- server/model/monitor.js | 13 +++++++++++++ server/model/proxy.js | 4 ++++ server/model/status_page.js | 33 +++++++++++++++++++++++++++++++++ server/model/user.js | 11 +++++------ 4 files changed, 55 insertions(+), 6 deletions(-) diff --git a/server/model/monitor.js b/server/model/monitor.js index 00da052f..d5dc1d90 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -103,6 +103,10 @@ class Monitor extends BeanModel { return data; } + /** + * Get all tags applied to this monitor + * @returns {Promise<LooseObject<any>[]>} + */ async getTags() { return await R.getAll("SELECT mt.*, tag.name, tag.color FROM monitor_tag mt JOIN tag ON mt.tag_id = tag.id WHERE mt.monitor_id = ?", [ this.id ]); } @@ -116,6 +120,10 @@ class Monitor extends BeanModel { return Buffer.from(user + ":" + pass).toString("base64"); } + /** + * Is the TLS expiry notification enabled? + * @returns {boolean} + */ isEnabledExpiryNotification() { return Boolean(this.expiryNotification); } @@ -507,6 +515,7 @@ class Monitor extends BeanModel { }; + /** Get a heartbeat and handle errors */ const safeBeat = async () => { try { await beat(); @@ -540,6 +549,10 @@ class Monitor extends BeanModel { this.prometheus().remove(); } + /** + * Get a new prometheus instance + * @returns {Prometheus} + */ prometheus() { return new Prometheus(this); } diff --git a/server/model/proxy.js b/server/model/proxy.js index 7ddec434..34a55d2f 100644 --- a/server/model/proxy.js +++ b/server/model/proxy.js @@ -1,6 +1,10 @@ const { BeanModel } = require("redbean-node/dist/bean-model"); class Proxy extends BeanModel { + /** + * Return a object that ready to parse to JSON + * @returns {Object} + */ toJSON() { return { id: this._id, diff --git a/server/model/status_page.js b/server/model/status_page.js index b1befc25..605bdd3e 100644 --- a/server/model/status_page.js +++ b/server/model/status_page.js @@ -6,6 +6,7 @@ class StatusPage extends BeanModel { static domainMappingList = { }; /** + * Loads domain mapping from DB * Return object like this: { "test-uptime.kuma.pet": "default" } * @returns {Promise<void>} */ @@ -17,6 +18,12 @@ class StatusPage extends BeanModel { `); } + /** + * Send status page list to client + * @param {Server} io io Socket server instance + * @param {Socket} socket Socket.io instance + * @returns {Promise<Bean[]>} + */ static async sendStatusPageList(io, socket) { let result = {}; @@ -30,6 +37,11 @@ class StatusPage extends BeanModel { return list; } + /** + * Update list of domain names + * @param {Array<string>} domainNameList + * @returns {Promise<void>} + */ async updateDomainNameList(domainNameList) { if (!Array.isArray(domainNameList)) { @@ -69,6 +81,10 @@ class StatusPage extends BeanModel { } } + /** + * Get list of domain names + * @returns {Array<Object>} + */ getDomainNameList() { let domainList = []; for (let domain in StatusPage.domainMappingList) { @@ -81,6 +97,10 @@ class StatusPage extends BeanModel { return domainList; } + /** + * Return a object that ready to parse to JSON + * @returns {Object} + */ async toJSON() { return { id: this.id, @@ -98,6 +118,11 @@ class StatusPage extends BeanModel { }; } + /** + * Return a object that ready to parse to JSON for public + * Only show necessary data to public + * @returns {Object} + */ async toPublicJSON() { return { slug: this.slug, @@ -113,12 +138,20 @@ class StatusPage extends BeanModel { }; } + /** + * Convert slug to status page ID + * @param {string} slug + */ static async slugToID(slug) { return await R.getCell("SELECT id FROM status_page WHERE slug = ? ", [ slug ]); } + /** + * Get path to the icon for the page + * @returns {string} + */ getIcon() { if (!this.icon) { return "/icon.svg"; diff --git a/server/model/user.js b/server/model/user.js index b243f87f..fc619c74 100644 --- a/server/model/user.js +++ b/server/model/user.js @@ -3,12 +3,11 @@ const passwordHash = require("../password-hash"); const { R } = require("redbean-node"); class User extends BeanModel { - /** - * + * Reset user password * Fix #1510, as in the context reset-password.js, there is no auto model mapping. Call this static function instead. - * @param userID - * @param newPassword + * @param {number} userID ID of user to update + * @param {string} newPassword * @returns {Promise<void>} */ static async resetPassword(userID, newPassword) { @@ -19,8 +18,8 @@ class User extends BeanModel { } /** - * - * @param newPassword + * Reset this users password + * @param {string} newPassword * @returns {Promise<void>} */ async resetPassword(newPassword) { From c2f6c5b42e58c12666ca01312454285c059313fc Mon Sep 17 00:00:00 2001 From: Matthew Nickson <mnickson@sidingsmedia.com> Date: Thu, 21 Apr 2022 18:53:07 +0100 Subject: [PATCH 038/124] Added JSDoc docs for mqttAsync Signed-off-by: Matthew Nickson <mnickson@sidingsmedia.com> --- server/util-server.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/server/util-server.js b/server/util-server.js index a6d1fbb2..f7c8b967 100644 --- a/server/util-server.js +++ b/server/util-server.js @@ -108,7 +108,12 @@ exports.pingAsync = function (hostname, ipv6 = false) { /** * MQTT Monitor - * TODO: Add docs for MQTT monitor + * @param {string} hostname Hostname / address of machine to test + * @param {string} topic MQTT topic + * @param {string} okMessage Expected result + * @param {Object} [options={}] MQTT options. Contains port, username, + * password and interval (interval defaults to 20) + * @returns {Promise<string>} */ exports.mqttAsync = function (hostname, topic, okMessage, options = {}) { return new Promise((resolve, reject) => { From 9996ba1636e7ba679091dab3e0e2a5b47357ef10 Mon Sep 17 00:00:00 2001 From: Matthew Nickson <mnickson@sidingsmedia.com> Date: Thu, 21 Apr 2022 19:55:01 +0100 Subject: [PATCH 039/124] Add JSDoc to server/modules/apicache/* Signed-off-by: Matthew Nickson <mnickson@sidingsmedia.com> --- server/modules/apicache/apicache.js | 258 ++++++++++++++++-------- server/modules/apicache/memory-cache.js | 28 +++ 2 files changed, 206 insertions(+), 80 deletions(-) diff --git a/server/modules/apicache/apicache.js b/server/modules/apicache/apicache.js index 25f0a54f..5d600500 100644 --- a/server/modules/apicache/apicache.js +++ b/server/modules/apicache/apicache.js @@ -13,27 +13,49 @@ let t = { let instances = []; +/** + * Does a === b + * @param {any} a + * @returns {function(any): boolean} + */ let matches = function (a) { return function (b) { return a === b; }; }; +/** + * Does a!==b + * @param {any} a + * @returns {function(any): boolean} + */ let doesntMatch = function (a) { return function (b) { return !matches(a)(b); }; }; +/** + * Get log duration + * @param {number} d Time in ms + * @param {string} prefix Prefix for log + * @returns {string} Coloured log string + */ let logDuration = function (d, prefix) { let str = d > 1000 ? (d / 1000).toFixed(2) + "sec" : d + "ms"; return "\x1b[33m- " + (prefix ? prefix + " " : "") + str + "\x1b[0m"; }; +/** + * Get safe headers + * @param {Object} res Express response object + * @returns {Object} + */ function getSafeHeaders(res) { return res.getHeaders ? res.getHeaders() : res._headers; } +/** Constructor for ApiCache instance */ function ApiCache() { let memCache = new MemoryCache(); @@ -70,10 +92,10 @@ function ApiCache() { /** * Logs a message to the console if the `DEBUG` environment variable is set. - * @param {string} a - The first argument to log. - * @param {string} b - The second argument to log. - * @param {string} c - The third argument to log. - * @param {string} d - The fourth argument to log, and so on... (optional) + * @param {string} a The first argument to log. + * @param {string} b The second argument to log. + * @param {string} c The third argument to log. + * @param {string} d The fourth argument to log, and so on... (optional) * * Generated by Trelent */ @@ -90,8 +112,8 @@ function ApiCache() { * Returns true if the given request and response should be logged. * @param {Object} request The HTTP request object. * @param {Object} response The HTTP response object. - * - * Generated by Trelent + * @param {function(Object, Object):boolean} toggle + * @returns {boolean} */ function shouldCacheResponse(request, response, toggle) { let opt = globalOptions; @@ -116,10 +138,9 @@ function ApiCache() { } /** - * Adds a key to the index. - * @param {string} key The key to add. - * - * Generated by Trelent + * Add key to index array + * @param {string} key Key to add + * @param {Object} req Express request object */ function addIndexEntries(key, req) { let groupName = req.apicacheGroup; @@ -135,8 +156,11 @@ function ApiCache() { /** * Returns a new object containing only the whitelisted headers. - * @param {Object} headers The original object of header names and values. - * @param {Array.<string>} globalOptions.headerWhitelist An array of strings representing the whitelisted header names to keep in the output object. + * @param {Object} headers The original object of header names and + * values. + * @param {string[]} globalOptions.headerWhitelist An array of + * strings representing the whitelisted header names to keep in the + * output object. * * Generated by Trelent */ @@ -152,8 +176,10 @@ function ApiCache() { } /** + * Create a cache object * @param {Object} headers The response headers to filter. - * @returns {Object} A new object containing only the whitelisted response headers. + * @returns {Object} A new object containing only the whitelisted + * response headers. * * Generated by Trelent */ @@ -170,8 +196,9 @@ function ApiCache() { /** * Sets a cache value for the given key. * @param {string} key The cache key to set. - * @param {*} value The cache value to set. - * @param {number} duration How long in milliseconds the cached response should be valid for (defaults to 1 hour). + * @param {any} value The cache value to set. + * @param {number} duration How long in milliseconds the cached + * response should be valid for (defaults to 1 hour). * * Generated by Trelent */ @@ -199,7 +226,8 @@ function ApiCache() { /** * Appends content to the response. - * @param {string|Buffer} content The content to append. + * @param {Object} res Express response object + * @param {(string|Buffer)} content The content to append. * * Generated by Trelent */ @@ -229,11 +257,15 @@ function ApiCache() { } /** - * Monkeypatches the response object to add cache control headers and create a cache object. - * @param {Object} req - The request object. - * @param {Object} res - The response object. - * - * Generated by Trelent + * Monkeypatches the response object to add cache control headers + * and create a cache object. + * @param {Object} req Express request object + * @param {Object} res Express response object + * @param {function} next Function to call next + * @param {string} key Key to add response as + * @param {number} duration Time to cache response for + * @param {string} strDuration Duration in string form + * @param {function(Object, Object):boolean} toggle */ function makeResponseCacheable(req, res, next, key, duration, strDuration, toggle) { // monkeypatch res.end to create cache object @@ -302,11 +334,15 @@ function ApiCache() { } /** - * @param {Request} request - * @param {Response} response - * @returns {boolean|undefined} true if the request should be cached, false otherwise. If undefined, defaults to true. - * - * Generated by Trelent + * Send a cached response to client + * @param {Request} request Express request object + * @param {Response} response Express response object + * @param {object} cacheObject Cache object to send + * @param {function(Object, Object):boolean} toggle + * @param {function} next Function to call next + * @param {number} duration Not used + * @returns {boolean|undefined} true if the request should be + * cached, false otherwise. If undefined, defaults to true. */ function sendCachedResponse(request, response, cacheObject, toggle, next, duration) { if (toggle && !toggle(request, response)) { @@ -348,12 +384,19 @@ function ApiCache() { return response.end(data, cacheObject.encoding); } + /** Sync caching options */ function syncOptions() { for (let i in middlewareOptions) { Object.assign(middlewareOptions[i].options, globalOptions, middlewareOptions[i].localOptions); } } + /** + * Clear key from cache + * @param {string} target Key to clear + * @param {boolean} isAutomatic Is the key being cleared automatically + * @returns {number} + */ this.clear = function (target, isAutomatic) { let group = index.groups[target]; let redis = globalOptions.redisClient; @@ -430,10 +473,11 @@ function ApiCache() { /** * Converts a duration string to an integer number of milliseconds. - * @param {string} duration - The string to convert. - * @returns {number} The converted value in milliseconds, or the defaultDuration if it can't be parsed. - * - * Generated by Trelent + * @param {(string|number)} duration The string to convert. + * @param {number} defaultDuration The default duration to return if + * can't parse duration + * @returns {number} The converted value in milliseconds, or the + * defaultDuration if it can't be parsed. */ function parseDuration(duration, defaultDuration) { if (typeof duration === "number") { @@ -457,17 +501,24 @@ function ApiCache() { return defaultDuration; } + /** + * Parse duration + * @param {(number|string)} duration + * @returns {number} Duration parsed to a number + */ this.getDuration = function (duration) { return parseDuration(duration, globalOptions.defaultDuration); }; /** - * Return cache performance statistics (hit rate). Suitable for putting into a route: + * Return cache performance statistics (hit rate). Suitable for + * putting into a route: * <code> * app.get('/api/cache/performance', (req, res) => { * res.json(apicache.getPerformance()) * }) * </code> + * @returns {any[]} */ this.getPerformance = function () { return performanceArray.map(function (p) { @@ -475,6 +526,11 @@ function ApiCache() { }); }; + /** + * Get index of a group + * @param {string} group + * @returns {number} + */ this.getIndex = function (group) { if (group) { return index.groups[group]; @@ -483,6 +539,14 @@ function ApiCache() { } }; + /** + * Express middleware + * @param {(string|number)} strDuration Duration to cache responses + * for. + * @param {function(Object, Object):boolean} middlewareToggle + * @param {Object} localOptions Options for APICache + * @returns + */ this.middleware = function cache(strDuration, middlewareToggle, localOptions) { let duration = instance.getDuration(strDuration); let opt = {}; @@ -506,63 +570,72 @@ function ApiCache() { options(localOptions); /** - * A Function for non tracking performance - */ + * A Function for non tracking performance + */ function NOOPCachePerformance() { this.report = this.hit = this.miss = function () {}; // noop; } /** - * A function for tracking and reporting hit rate. These statistics are returned by the getPerformance() call above. - */ + * A function for tracking and reporting hit rate. These + * statistics are returned by the getPerformance() call above. + */ function CachePerformance() { /** - * Tracks the hit rate for the last 100 requests. - * If there have been fewer than 100 requests, the hit rate just considers the requests that have happened. - */ + * Tracks the hit rate for the last 100 requests. If there + * have been fewer than 100 requests, the hit rate just + * considers the requests that have happened. + */ this.hitsLast100 = new Uint8Array(100 / 4); // each hit is 2 bits /** - * Tracks the hit rate for the last 1000 requests. - * If there have been fewer than 1000 requests, the hit rate just considers the requests that have happened. - */ + * Tracks the hit rate for the last 1000 requests. If there + * have been fewer than 1000 requests, the hit rate just + * considers the requests that have happened. + */ this.hitsLast1000 = new Uint8Array(1000 / 4); // each hit is 2 bits /** - * Tracks the hit rate for the last 10000 requests. - * If there have been fewer than 10000 requests, the hit rate just considers the requests that have happened. - */ + * Tracks the hit rate for the last 10000 requests. If there + * have been fewer than 10000 requests, the hit rate just + * considers the requests that have happened. + */ this.hitsLast10000 = new Uint8Array(10000 / 4); // each hit is 2 bits /** - * Tracks the hit rate for the last 100000 requests. - * If there have been fewer than 100000 requests, the hit rate just considers the requests that have happened. - */ + * Tracks the hit rate for the last 100000 requests. If + * there have been fewer than 100000 requests, the hit rate + * just considers the requests that have happened. + */ this.hitsLast100000 = new Uint8Array(100000 / 4); // each hit is 2 bits /** - * The number of calls that have passed through the middleware since the server started. - */ + * The number of calls that have passed through the + * middleware since the server started. + */ this.callCount = 0; /** - * The total number of hits since the server started - */ + * The total number of hits since the server started + */ this.hitCount = 0; /** - * The key from the last cache hit. This is useful in identifying which route these statistics apply to. - */ + * The key from the last cache hit. This is useful in + * identifying which route these statistics apply to. + */ this.lastCacheHit = null; /** - * The key from the last cache miss. This is useful in identifying which route these statistics apply to. - */ + * The key from the last cache miss. This is useful in + * identifying which route these statistics apply to. + */ this.lastCacheMiss = null; /** - * Return performance statistics - */ + * Return performance statistics + * @returns {Object} + */ this.report = function () { return { lastCacheHit: this.lastCacheHit, @@ -579,10 +652,13 @@ function ApiCache() { }; /** - * Computes a cache hit rate from an array of hits and misses. - * @param {Uint8Array} array An array representing hits and misses. - * @returns a number between 0 and 1, or null if the array has no hits or misses - */ + * Computes a cache hit rate from an array of hits and + * misses. + * @param {Uint8Array} array An array representing hits and + * misses. + * @returns {number} a number between 0 and 1, or null if + * the array has no hits or misses + */ this.hitRate = function (array) { let hits = 0; let misses = 0; @@ -608,16 +684,17 @@ function ApiCache() { }; /** - * Record a hit or miss in the given array. It will be recorded at a position determined - * by the current value of the callCount variable. - * @param {Uint8Array} array An array representing hits and misses. - * @param {boolean} hit true for a hit, false for a miss - * Each element in the array is 8 bits, and encodes 4 hit/miss records. - * Each hit or miss is encoded as to bits as follows: - * 00 means no hit or miss has been recorded in these bits - * 01 encodes a hit - * 10 encodes a miss - */ + * Record a hit or miss in the given array. It will be + * recorded at a position determined by the current value of + * the callCount variable. + * @param {Uint8Array} array An array representing hits and + * misses. + * @param {boolean} hit true for a hit, false for a miss + * Each element in the array is 8 bits, and encodes 4 + * hit/miss records. Each hit or miss is encoded as to bits + * as follows: 00 means no hit or miss has been recorded in + * these bits 01 encodes a hit 10 encodes a miss + */ this.recordHitInArray = function (array, hit) { let arrayIndex = ~~(this.callCount / 4) % array.length; let bitOffset = (this.callCount % 4) * 2; // 2 bits per record, 4 records per uint8 array element @@ -627,9 +704,11 @@ function ApiCache() { }; /** - * Records the hit or miss in the tracking arrays and increments the call count. - * @param {boolean} hit true records a hit, false records a miss - */ + * Records the hit or miss in the tracking arrays and + * increments the call count. + * @param {boolean} hit true records a hit, false records a + * miss + */ this.recordHit = function (hit) { this.recordHitInArray(this.hitsLast100, hit); this.recordHitInArray(this.hitsLast1000, hit); @@ -642,18 +721,18 @@ function ApiCache() { }; /** - * Records a hit event, setting lastCacheMiss to the given key - * @param {string} key The key that had the cache hit - */ + * Records a hit event, setting lastCacheMiss to the given key + * @param {string} key The key that had the cache hit + */ this.hit = function (key) { this.recordHit(true); this.lastCacheHit = key; }; /** - * Records a miss event, setting lastCacheMiss to the given key - * @param {string} key The key that had the cache miss - */ + * Records a miss event, setting lastCacheMiss to the given key + * @param {string} key The key that had the cache miss + */ this.miss = function (key) { this.recordHit(false); this.lastCacheMiss = key; @@ -664,6 +743,13 @@ function ApiCache() { performanceArray.push(perf); + /** + * Cache a request + * @param {Object} req Express request object + * @param {Object} res Express response object + * @param {function} next Function to call next + * @returns {any} + */ let cache = function (req, res, next) { function bypass() { debug("bypass detected, skipping cache."); @@ -771,6 +857,11 @@ function ApiCache() { return cache; }; + /** + * Process options + * @param {Object} options + * @returns {Object} + */ this.options = function (options) { if (options) { Object.assign(globalOptions, options); @@ -791,6 +882,7 @@ function ApiCache() { } }; + /** Reset the index */ this.resetIndex = function () { index = { all: [], @@ -798,6 +890,11 @@ function ApiCache() { }; }; + /** + * Create a new instance of ApiCache + * @param {Object} config Config to pass + * @returns {ApiCache} + */ this.newInstance = function (config) { let instance = new ApiCache(); @@ -808,6 +905,7 @@ function ApiCache() { return instance; }; + /** Clone this instance */ this.clone = function () { return this.newInstance(this.options()); }; diff --git a/server/modules/apicache/memory-cache.js b/server/modules/apicache/memory-cache.js index ad831e2e..a91eee32 100644 --- a/server/modules/apicache/memory-cache.js +++ b/server/modules/apicache/memory-cache.js @@ -3,6 +3,15 @@ function MemoryCache() { this.size = 0; } +/** + * + * @param {string} key Key to store cache as + * @param {any} value Value to store + * @param {number} time Time to store for + * @param {function(any, string)} timeoutCallback Callback to call in + * case of timeout + * @returns {Object} + */ MemoryCache.prototype.add = function (key, value, time, timeoutCallback) { let old = this.cache[key]; let instance = this; @@ -22,6 +31,11 @@ MemoryCache.prototype.add = function (key, value, time, timeoutCallback) { return entry; }; +/** + * Delete a cache entry + * @param {string} key Key to delete + * @returns {null} + */ MemoryCache.prototype.delete = function (key) { let entry = this.cache[key]; @@ -36,18 +50,32 @@ MemoryCache.prototype.delete = function (key) { return null; }; +/** + * Get value of key + * @param {string} key + * @returns {Object} + */ MemoryCache.prototype.get = function (key) { let entry = this.cache[key]; return entry; }; +/** + * Get value of cache entry + * @param {string} key + * @returns {any} + */ MemoryCache.prototype.getValue = function (key) { let entry = this.get(key); return entry && entry.value; }; +/** + * Clear cache + * @returns {boolean} + */ MemoryCache.prototype.clear = function () { Object.keys(this.cache).forEach(function (key) { this.delete(key); From 09e61d9d6321076313790d606282bfb83a8c4065 Mon Sep 17 00:00:00 2001 From: Matthew Nickson <mnickson@sidingsmedia.com> Date: Thu, 21 Apr 2022 20:02:18 +0100 Subject: [PATCH 040/124] Changed Array<T> to type[] Signed-off-by: Matthew Nickson <mnickson@sidingsmedia.com> --- server/model/group.js | 2 +- server/model/monitor.js | 2 +- server/model/status_page.js | 4 ++-- server/server.js | 2 +- server/util-server.js | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/server/model/group.js b/server/model/group.js index 40fb1c7b..f24fe99d 100644 --- a/server/model/group.js +++ b/server/model/group.js @@ -27,7 +27,7 @@ class Group extends BeanModel { /** * Get all monitors - * @returns {Array<Bean>} + * @returns {Bean[]} */ async getMonitorList() { return R.convertToBeans("monitor", await R.getAll(` diff --git a/server/model/monitor.js b/server/model/monitor.js index d5dc1d90..b97e424d 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -840,7 +840,7 @@ class Monitor extends BeanModel { * than target days * @param {number} daysRemaining Number of days remaining on certifcate * @param {number} targetDays Number of days to alert after - * @param {Array<LooseObject<any>>} notificationList List of notification providers + * @param {LooseObject<any>[]} notificationList List of notification providers * @returns {Promise<void>} */ async sendCertNotificationByTargetDays(daysRemaining, targetDays, notificationList) { diff --git a/server/model/status_page.js b/server/model/status_page.js index 605bdd3e..a48dc7b2 100644 --- a/server/model/status_page.js +++ b/server/model/status_page.js @@ -39,7 +39,7 @@ class StatusPage extends BeanModel { /** * Update list of domain names - * @param {Array<string>} domainNameList + * @param {string[]} domainNameList * @returns {Promise<void>} */ async updateDomainNameList(domainNameList) { @@ -83,7 +83,7 @@ class StatusPage extends BeanModel { /** * Get list of domain names - * @returns {Array<Object>} + * @returns {Object[]} */ getDomainNameList() { let domainList = []; diff --git a/server/server.js b/server/server.js index 944a2fff..1c7a2b6e 100644 --- a/server/server.js +++ b/server/server.js @@ -1475,7 +1475,7 @@ try { /** * Update notifications for a given monitor * @param {number} monitorID ID of monitor to update - * @param {Array<number>} notificationIDList List of new notification + * @param {number[]} notificationIDList List of new notification * providers to add * @returns {Promise<void>} */ diff --git a/server/util-server.js b/server/util-server.js index f7c8b967..72f633a2 100644 --- a/server/util-server.js +++ b/server/util-server.js @@ -387,7 +387,7 @@ exports.checkCertificate = function (res) { /** * Check if the provided status code is within the accepted ranges * @param {string} status The status code to check - * @param {Array<string>} acceptedCodes An array of accepted status codes + * @param {string[]} acceptedCodes An array of accepted status codes * @returns {boolean} True if status code within range, false otherwise * @throws {Error} Will throw an error if the provided status code is not a valid range string or code string */ From 64a33d7455fca793b88a549f77b06a1e2e5f658b Mon Sep 17 00:00:00 2001 From: Jens Neuber <jensneuber@users.noreply.github.com> Date: Fri, 22 Apr 2022 07:54:13 +0200 Subject: [PATCH 041/124] Update server/util-server.js Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com> --- 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 1eecdec2d96ac54a3da55e4e99dfd7a7c672728f Mon Sep 17 00:00:00 2001 From: Matthew Nickson <mnickson@sidingsmedia.com> Date: Fri, 22 Apr 2022 18:42:47 +0100 Subject: [PATCH 042/124] Update JSDoc for better clarity Co-authored-by: Nelson Chan <chakflying@hotmail.com> Signed-off-by: Matthew Nickson <mnickson@sidingsmedia.com> --- server/jobs/util-worker.js | 4 +++- server/model/monitor.js | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/server/jobs/util-worker.js b/server/jobs/util-worker.js index 89629a2f..1aeec794 100644 --- a/server/jobs/util-worker.js +++ b/server/jobs/util-worker.js @@ -3,7 +3,9 @@ const Database = require("../database"); const path = require("path"); /** - * Send message to parent process + * Send message to parent process for logging + * since worker_thread does not have access to stdout, this is used + * instead of console.log() * @param {any} any The message to log */ const log = function (any) { diff --git a/server/model/monitor.js b/server/model/monitor.js index b97e424d..60c984f3 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -145,7 +145,7 @@ class Monitor extends BeanModel { } /** - * Get status codes that are acceptable + * Get accepted status codes * @returns {Object} */ getAcceptedStatuscodes() { @@ -658,7 +658,7 @@ class Monitor extends BeanModel { } /** - * Send certificate information to information + * Send certificate information to client * @param {Server} io Socket server instance * @param {number} monitorID ID of monitor to send * @param {number} userID ID of user to send to From 46da5e51be43330d8f194186b50cd9b9b03dfa24 Mon Sep 17 00:00:00 2001 From: Matthew Nickson <mnickson@sidingsmedia.com> Date: Fri, 22 Apr 2022 19:10:13 +0100 Subject: [PATCH 043/124] Fix JSDoc grammar Signed-off-by: Matthew Nickson <mnickson@sidingsmedia.com> --- server/model/group.js | 2 +- server/model/heartbeat.js | 4 ++-- server/model/incident.js | 2 +- server/model/monitor.js | 2 +- server/model/proxy.js | 2 +- server/model/status_page.js | 4 ++-- server/model/tag.js | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/server/model/group.js b/server/model/group.js index f24fe99d..599b758b 100644 --- a/server/model/group.js +++ b/server/model/group.js @@ -4,7 +4,7 @@ const { R } = require("redbean-node"); class Group extends BeanModel { /** - * Return a object that ready to parse to JSON for public + * Return an object that ready to parse to JSON for public * Only show necessary data to public * @param {boolean} [showTags=false] Should the JSON include monitor tags * @returns {Object} diff --git a/server/model/heartbeat.js b/server/model/heartbeat.js index 38ba6f46..a0b40d08 100644 --- a/server/model/heartbeat.js +++ b/server/model/heartbeat.js @@ -14,7 +14,7 @@ const { BeanModel } = require("redbean-node/dist/bean-model"); class Heartbeat extends BeanModel { /** - * Return a object that ready to parse to JSON for public + * Return an object that ready to parse to JSON for public * Only show necessary data to public * @returns {Object} */ @@ -28,7 +28,7 @@ class Heartbeat extends BeanModel { } /** - * Return a object that ready to parse to JSON + * Return an object that ready to parse to JSON * @returns {Object} */ toJSON() { diff --git a/server/model/incident.js b/server/model/incident.js index e28478f7..f954cc74 100644 --- a/server/model/incident.js +++ b/server/model/incident.js @@ -3,7 +3,7 @@ const { BeanModel } = require("redbean-node/dist/bean-model"); class Incident extends BeanModel { /** - * Return a object that ready to parse to JSON for public + * Return an object that ready to parse to JSON for public * Only show necessary data to public * @returns {Object} */ diff --git a/server/model/monitor.js b/server/model/monitor.js index 60c984f3..e1a2b80b 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -41,7 +41,7 @@ class Monitor extends BeanModel { } /** - * Return a object that ready to parse to JSON + * Return an object that ready to parse to JSON * @returns {Object} */ async toJSON(includeSensitiveData = true) { diff --git a/server/model/proxy.js b/server/model/proxy.js index 34a55d2f..4517b11b 100644 --- a/server/model/proxy.js +++ b/server/model/proxy.js @@ -2,7 +2,7 @@ const { BeanModel } = require("redbean-node/dist/bean-model"); class Proxy extends BeanModel { /** - * Return a object that ready to parse to JSON + * Return an object that ready to parse to JSON * @returns {Object} */ toJSON() { diff --git a/server/model/status_page.js b/server/model/status_page.js index a48dc7b2..99b284ab 100644 --- a/server/model/status_page.js +++ b/server/model/status_page.js @@ -98,7 +98,7 @@ class StatusPage extends BeanModel { } /** - * Return a object that ready to parse to JSON + * Return an object that ready to parse to JSON * @returns {Object} */ async toJSON() { @@ -119,7 +119,7 @@ class StatusPage extends BeanModel { } /** - * Return a object that ready to parse to JSON for public + * Return an object that ready to parse to JSON for public * Only show necessary data to public * @returns {Object} */ diff --git a/server/model/tag.js b/server/model/tag.js index 5a8d97d3..b31eb868 100644 --- a/server/model/tag.js +++ b/server/model/tag.js @@ -3,7 +3,7 @@ const { BeanModel } = require("redbean-node/dist/bean-model"); class Tag extends BeanModel { /** - * Return a object that ready to parse to JSON + * Return an object that ready to parse to JSON * @returns {Object} */ toJSON() { From 587faecf87a87ffc513c8b2ef0010ba64a84dc14 Mon Sep 17 00:00:00 2001 From: Matthew Nickson <mnickson@sidingsmedia.com> Date: Fri, 22 Apr 2022 19:42:42 +0100 Subject: [PATCH 044/124] Made value nullable in apicache JSDoc Signed-off-by: Matthew Nickson <mnickson@sidingsmedia.com> --- server/modules/apicache/apicache.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/modules/apicache/apicache.js b/server/modules/apicache/apicache.js index 5d600500..41930b24 100644 --- a/server/modules/apicache/apicache.js +++ b/server/modules/apicache/apicache.js @@ -656,7 +656,7 @@ function ApiCache() { * misses. * @param {Uint8Array} array An array representing hits and * misses. - * @returns {number} a number between 0 and 1, or null if + * @returns {?number} a number between 0 and 1, or null if * the array has no hits or misses */ this.hitRate = function (array) { From 9130b3762cc328f914d8d004cec8eb013bbbf5e2 Mon Sep 17 00:00:00 2001 From: Matthew Nickson <mnickson@sidingsmedia.com> Date: Fri, 22 Apr 2022 23:05:18 +0100 Subject: [PATCH 045/124] [empty commit] pull request for JSDoc test/* From b641c8a8785d06a80bb702dae4e7ac4c11dd7f06 Mon Sep 17 00:00:00 2001 From: Matthew Nickson <mnickson@sidingsmedia.com> Date: Fri, 22 Apr 2022 23:24:14 +0100 Subject: [PATCH 046/124] Add JSDoc to test/* Signed-off-by: Matthew Nickson <mnickson@sidingsmedia.com> --- test/e2e.spec.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test/e2e.spec.js b/test/e2e.spec.js index 5535f57d..8de8ceb1 100644 --- a/test/e2e.spec.js +++ b/test/e2e.spec.js @@ -284,6 +284,11 @@ describe("Init", () => { }); }); +/** + * Test login + * @param {string} username + * @param {string} password + */ async function login(username, password) { await input(page, "#floatingInput", username); await input(page, "#floatingPassword", password); @@ -291,6 +296,13 @@ async function login(username, password) { await sleep(5000); } +/** + * Click on an element on the page + * @param {Page} page Puppeteer page instance + * @param {string} selector + * @param {number} elementIndex + * @returns {Promise<any>} + */ async function click(page, selector, elementIndex = 0) { await page.waitForSelector(selector, { timeout: 5000, @@ -300,6 +312,12 @@ async function click(page, selector, elementIndex = 0) { }, selector, elementIndex); } +/** + * Input text into selected field + * @param {Page} page Puppeteer page instance + * @param {string} selector + * @param {string} text Text to input + */ async function input(page, selector, text) { await page.waitForSelector(selector, { timeout: 5000, From 4c8da89c36146e2bd24b10643c9f23602ff94ae5 Mon Sep 17 00:00:00 2001 From: MrEddX <66828538+MrEddX@users.noreply.github.com> Date: Mon, 25 Apr 2022 07:26:40 +0300 Subject: [PATCH 047/124] Update bg-BG.js Translation fixes --- src/languages/bg-BG.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/languages/bg-BG.js b/src/languages/bg-BG.js index 7a219a2a..a419e88e 100644 --- a/src/languages/bg-BG.js +++ b/src/languages/bg-BG.js @@ -411,8 +411,8 @@ export default { Running: "Работи", "Not running": "Не работи", "Remove Token": "Премахни токен", - Start: "Старт", - Stop: "Стоп", + Start: "Стартирай", + Stop: "Спри", "Uptime Kuma": "Uptime Kuma", "Add New Status Page": "Добави нова статус страница", Slug: "Слъг", From 5fbfacf5ce21dc06e202cd183383ed4f1a18a91e Mon Sep 17 00:00:00 2001 From: Adam Stachowicz <adam.stachowicz@fingo.info> Date: Tue, 26 Apr 2022 00:26:26 +0200 Subject: [PATCH 048/124] Fix ESlint warnings --- server/model/monitor.js | 2 +- server/server.js | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/server/model/monitor.js b/server/model/monitor.js index 2abf4be3..ee1dc84d 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -694,7 +694,7 @@ class Monitor extends BeanModel { } else { // Handle new monitor with only one beat, because the beat's duration = 0 - let status = parseInt(await R.getCell("SELECT `status` FROM heartbeat WHERE monitor_id = ?", [monitorID])); + let status = parseInt(await R.getCell("SELECT `status` FROM heartbeat WHERE monitor_id = ?", [ monitorID ])); if (status === UP) { uptime = 1; diff --git a/server/server.js b/server/server.js index 5e704c33..7bee5252 100644 --- a/server/server.js +++ b/server/server.js @@ -327,7 +327,7 @@ try { let user = await login(data.username, data.password); if (user) { - if (user.twofa_status == 0) { + if (user.twofa_status === 0) { afterLogin(socket, user); log.info("auth", `Successfully logged in user ${data.username}. IP=${getClientIp(socket)}`); @@ -340,7 +340,7 @@ try { }); } - if (user.twofa_status == 1 && !data.token) { + if (user.twofa_status === 1 && !data.token) { log.info("auth", `2FA token required for user ${data.username}. IP=${getClientIp(socket)}`); @@ -417,7 +417,7 @@ try { socket.userID, ]); - if (user.twofa_status == 0) { + if (user.twofa_status === 0) { let newSecret = genSecret(); let encodedSecret = base32.encode(newSecret); @@ -548,7 +548,7 @@ try { socket.userID, ]); - if (user.twofa_status == 1) { + if (user.twofa_status === 1) { callback({ ok: true, status: true, @@ -1169,7 +1169,7 @@ try { let version17x = compareVersions.compare(backupData.version, "1.7.0", ">="); // If the import option is "overwrite" it'll clear most of the tables, except "settings" and "user" - if (importHandle == "overwrite") { + if (importHandle === "overwrite") { // Stops every monitor first, so it doesn't execute any heartbeat while importing for (let id in server.monitorList) { let monitor = server.monitorList[id]; @@ -1193,7 +1193,7 @@ try { for (let i = 0; i < notificationListData.length; i++) { // Only starts importing the notification if the import option is "overwrite", "keep" or "skip" but the notification doesn't exists - if ((importHandle == "skip" && notificationNameListString.includes(notificationListData[i].name) == false) || importHandle == "keep" || importHandle == "overwrite") { + if ((importHandle === "skip" && notificationNameListString.includes(notificationListData[i].name) === false) || importHandle === "keep" || importHandle === "overwrite") { let notification = JSON.parse(notificationListData[i].config); await Notification.save(notification, null, socket.userID); From 1e595eaa76cf35bdb8e8be24c56ede595dd1f38a Mon Sep 17 00:00:00 2001 From: Adam Stachowicz <adam.stachowicz@fingo.info> Date: Tue, 26 Apr 2022 01:26:57 +0200 Subject: [PATCH 049/124] Update linters --- .eslintrc.js | 1 + package.json | 12 +++---- server/database.js | 2 +- server/prometheus.js | 2 +- server/proxy.js | 2 +- server/server.js | 17 ++------- server/util-server.js | 2 +- src/components/Datetime.vue | 2 +- src/components/MonitorList.vue | 10 ++---- src/components/PingChart.vue | 10 +++--- src/components/Tag.vue | 21 +++++------ src/components/TagsManager.vue | 66 ++++++++++++++++++---------------- src/pages/EditMonitor.vue | 9 +++-- 13 files changed, 72 insertions(+), 84 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 38a16e64..4713799d 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -48,6 +48,7 @@ module.exports = { "vue/html-self-closing": "off", "vue/require-component-is": "off", // not allow is="style" https://github.com/vuejs/eslint-plugin-vue/issues/462#issuecomment-430234675 "vue/attribute-hyphenation": "off", // This change noNL to "no-n-l" unexpectedly + "vue/multi-word-component-names": "off", "no-multi-spaces": [ "error", { ignoreEOLComments: true, }], diff --git a/package.json b/package.json index 8a8bd1eb..3096dfdb 100644 --- a/package.json +++ b/package.json @@ -123,7 +123,7 @@ }, "devDependencies": { "@actions/github": "~5.0.1", - "@babel/eslint-parser": "~7.15.8", + "@babel/eslint-parser": "~7.17.0", "@babel/preset-env": "^7.15.8", "@types/bootstrap": "~5.1.9", "@vitejs/plugin-legacy": "~1.6.4", @@ -135,16 +135,16 @@ "core-js": "~3.18.3", "cross-env": "~7.0.3", "dns2": "~2.0.1", - "eslint": "~7.32.0", - "eslint-plugin-vue": "~7.18.0", + "eslint": "~8.14.0", + "eslint-plugin-vue": "~8.7.1", "jest": "~27.2.5", "jest-puppeteer": "~6.0.3", - "npm-check-updates": "^12.5.5", + "npm-check-updates": "^12.5.9", "postcss-html": "^1.3.1", "puppeteer": "~13.1.3", "sass": "~1.42.1", - "stylelint": "~14.2.0", - "stylelint-config-standard": "~24.0.0", + "stylelint": "~14.7.1", + "stylelint-config-standard": "~25.0.0", "typescript": "~4.4.4", "vite": "~2.6.14", "wait-on": "^6.0.1" diff --git a/server/database.js b/server/database.js index 099d15d5..6ed11c3b 100644 --- a/server/database.js +++ b/server/database.js @@ -58,7 +58,7 @@ class Database { "patch-monitor-expiry-notification.sql": true, "patch-status-page-footer-css.sql": true, "patch-added-mqtt-monitor.sql": true, - } + }; /** * The final version should be 10 after merged tag feature diff --git a/server/prometheus.js b/server/prometheus.js index fe0896f6..9bf623fb 100644 --- a/server/prometheus.js +++ b/server/prometheus.js @@ -33,7 +33,7 @@ const monitorStatus = new PrometheusClient.Gauge({ }); class Prometheus { - monitorLabelValues = {} + monitorLabelValues = {}; constructor(monitor) { this.monitorLabelValues = { diff --git a/server/proxy.js b/server/proxy.js index 621f24d0..22c63b39 100644 --- a/server/proxy.js +++ b/server/proxy.js @@ -7,7 +7,7 @@ const { UptimeKumaServer } = require("./uptime-kuma-server"); class Proxy { - static SUPPORTED_PROXY_PROTOCOLS = [ "http", "https", "socks", "socks5", "socks4" ] + static SUPPORTED_PROXY_PROTOCOLS = [ "http", "https", "socks", "socks5", "socks4" ]; /** * Saves and updates given proxy entity diff --git a/server/server.js b/server/server.js index 7bee5252..4bd0b481 100644 --- a/server/server.js +++ b/server/server.js @@ -136,13 +136,6 @@ app.use(function (req, res, next) { next(); }); -/** - * Total WebSocket client connected to server currently, no actual use - * - * @type {number} - */ -let totalClient = 0; - /** * Use for decode the auth object * @type {null} @@ -248,17 +241,11 @@ try { sendInfo(socket); - totalClient++; - if (needSetup) { log.info("server", "Redirect to setup page"); socket.emit("setup"); } - socket.on("disconnect", () => { - totalClient--; - }); - // *************************** // Public Socket API // *************************** @@ -1228,7 +1215,7 @@ try { for (let i = 0; i < monitorListData.length; i++) { // Only starts importing the monitor if the import option is "overwrite", "keep" or "skip" but the notification doesn't exists - if ((importHandle == "skip" && monitorNameListString.includes(monitorListData[i].name) == false) || importHandle == "keep" || importHandle == "overwrite") { + if ((importHandle === "skip" && monitorNameListString.includes(monitorListData[i].name) === false) || importHandle === "keep" || importHandle === "overwrite") { // Define in here every new variable for monitors which where implemented after the first version of the Import/Export function (1.6.0) // --- Start --- @@ -1325,7 +1312,7 @@ try { await updateMonitorNotification(bean.id, notificationIDList); // If monitor was active start it immediately, otherwise pause it - if (monitorListData[i].active == 1) { + if (monitorListData[i].active === 1) { await startMonitor(socket.userID, bean.id); } else { await pauseMonitor(socket.userID, bean.id); diff --git a/server/util-server.js b/server/util-server.js index 7a9d3e7d..edbad589 100644 --- a/server/util-server.js +++ b/server/util-server.js @@ -132,7 +132,7 @@ exports.mqttAsync = function (hostname, topic, okMessage, options = {}) { }); client.on("message", (messageTopic, message) => { - if (messageTopic == topic) { + if (messageTopic === topic) { client.end(); clearTimeout(timeoutID); if (okMessage != null && okMessage !== "" && message.toString() !== okMessage) { diff --git a/src/components/Datetime.vue b/src/components/Datetime.vue index 8662e6d8..ed38c434 100644 --- a/src/components/Datetime.vue +++ b/src/components/Datetime.vue @@ -5,8 +5,8 @@ <script> import dayjs from "dayjs"; import relativeTime from "dayjs/plugin/relativeTime"; -import utc from "dayjs/plugin/utc"; import timezone from "dayjs/plugin/timezone"; // dependent on utc plugin +import utc from "dayjs/plugin/utc"; dayjs.extend(utc); dayjs.extend(timezone); dayjs.extend(relativeTime); diff --git a/src/components/MonitorList.vue b/src/components/MonitorList.vue index c8de6a6f..7aeadd1e 100644 --- a/src/components/MonitorList.vue +++ b/src/components/MonitorList.vue @@ -47,8 +47,8 @@ <script> import HeartbeatBar from "../components/HeartbeatBar.vue"; -import Uptime from "../components/Uptime.vue"; import Tag from "../components/Tag.vue"; +import Uptime from "../components/Uptime.vue"; import { getMonitorRelativeURL } from "../util.ts"; export default { @@ -105,7 +105,7 @@ export default { // Simple filter by search text // finds monitor name, tag name or tag value - if (this.searchText != "") { + if (this.searchText !== "") { const loweredSearchText = this.searchText.toLowerCase(); result = result.filter(monitor => { return monitor.name.toLowerCase().includes(loweredSearchText) @@ -170,12 +170,6 @@ export default { } } -.dark { - .footer { - // background-color: $dark-bg; - } -} - @media (max-width: 770px) { .list-header { margin: -20px; diff --git a/src/components/PingChart.vue b/src/components/PingChart.vue index 9132b4e9..d4f94a19 100644 --- a/src/components/PingChart.vue +++ b/src/components/PingChart.vue @@ -18,10 +18,10 @@ <script lang="ts"> import { BarController, BarElement, Chart, Filler, LinearScale, LineController, LineElement, PointElement, TimeScale, Tooltip } from "chart.js"; -import dayjs from "dayjs"; -import utc from "dayjs/plugin/utc"; -import timezone from "dayjs/plugin/timezone"; import "chartjs-adapter-dayjs"; +import dayjs from "dayjs"; +import timezone from "dayjs/plugin/timezone"; +import utc from "dayjs/plugin/utc"; import { LineChart } from "vue-chart-3"; import { useToast } from "vue-toastification"; import { DOWN } from "../util.ts"; @@ -217,7 +217,7 @@ export default { watch: { // Update chart data when the selected chart period changes chartPeriodHrs: function (newPeriod) { - if (newPeriod == "0") { + if (newPeriod === "0") { newPeriod = null; this.heartbeatList = null; this.$root.storage().removeItem(`chart-period-${this.monitorId}`); @@ -241,7 +241,7 @@ export default { // And mirror latest change to this.heartbeatList this.$watch(() => this.$root.heartbeatList[this.monitorId], (heartbeatList) => { - if (this.chartPeriodHrs != 0) { + if (this.chartPeriodHrs !== 0) { const newBeat = heartbeatList.at(-1); if (newBeat && dayjs.utc(newBeat.time) > dayjs.utc(this.heartbeatList.at(-1)?.time)) { this.heartbeatList.push(heartbeatList.at(-1)); diff --git a/src/components/Tag.vue b/src/components/Tag.vue index 364a05c6..3c966850 100644 --- a/src/components/Tag.vue +++ b/src/components/Tag.vue @@ -1,13 +1,14 @@ <template> - <div class="tag-wrapper rounded d-inline-flex" - :class="{ 'px-3': size == 'normal', - 'py-1': size == 'normal', - 'm-2': size == 'normal', - 'px-2': size == 'sm', - 'py-0': size == 'sm', - 'm-1': size == 'sm', - }" - :style="{ backgroundColor: item.color, fontSize: size == 'sm' ? '0.7em' : '1em' }" + <div + class="tag-wrapper rounded d-inline-flex" + :class="{ 'px-3': size == 'normal', + 'py-1': size == 'normal', + 'm-2': size == 'normal', + 'px-2': size == 'sm', + 'py-0': size == 'sm', + 'm-1': size == 'sm', + }" + :style="{ backgroundColor: item.color, fontSize: size == 'sm' ? '0.7em' : '1em' }" > <span class="tag-text">{{ displayText }}</span> <span v-if="remove != null" class="ps-1 btn-remove" @click="remove(item)"> @@ -34,7 +35,7 @@ export default { }, computed: { displayText() { - if (this.item.value == "") { + if (this.item.value === "") { return this.item.name; } else { return `${this.item.name}: ${this.item.value}`; diff --git a/src/components/TagsManager.vue b/src/components/TagsManager.vue index 2412f7e8..1a9212ca 100644 --- a/src/components/TagsManager.vue +++ b/src/components/TagsManager.vue @@ -34,18 +34,20 @@ label="name" > <template #option="{ option }"> - <div class="mx-2 py-1 px-3 rounded d-inline-flex" - style="margin-top: -5px; margin-bottom: -5px; height: 24px;" - :style="{ color: textColor(option), backgroundColor: option.color + ' !important' }" + <div + class="mx-2 py-1 px-3 rounded d-inline-flex" + style="margin-top: -5px; margin-bottom: -5px; height: 24px;" + :style="{ color: textColor(option), backgroundColor: option.color + ' !important' }" > <span> {{ option.name }}</span> </div> </template> <template #singleLabel="{ option }"> - <div class="py-1 px-3 rounded d-inline-flex" - style="height: 24px;" - :style="{ color: textColor(option), backgroundColor: option.color + ' !important' }" + <div + class="py-1 px-3 rounded d-inline-flex" + style="height: 24px;" + :style="{ color: textColor(option), backgroundColor: option.color + ' !important' }" > <span>{{ option.name }}</span> </div> @@ -53,10 +55,11 @@ </vue-multiselect> <div v-if="newDraftTag.select?.name == null" class="d-flex mb-2"> <div class="w-50 pe-2"> - <input v-model="newDraftTag.name" class="form-control" - :class="{'is-invalid': validateDraftTag.nameInvalid}" - :placeholder="$t('Name')" - @keydown.enter.prevent="onEnter" + <input + v-model="newDraftTag.name" class="form-control" + :class="{'is-invalid': validateDraftTag.nameInvalid}" + :placeholder="$t('Name')" + @keydown.enter.prevent="onEnter" /> <div class="invalid-feedback"> {{ $t("Tag with this name already exist.") }} @@ -75,17 +78,19 @@ deselect-label="" > <template #option="{ option }"> - <div class="mx-2 py-1 px-3 rounded d-inline-flex" - style="height: 24px; color: white;" - :style="{ backgroundColor: option.color + ' !important' }" + <div + class="mx-2 py-1 px-3 rounded d-inline-flex" + style="height: 24px; color: white;" + :style="{ backgroundColor: option.color + ' !important' }" > <span>{{ option.name }}</span> </div> </template> <template #singleLabel="{ option }"> - <div class="py-1 px-3 rounded d-inline-flex" - style="height: 24px; color: white;" - :style="{ backgroundColor: option.color + ' !important' }" + <div + class="py-1 px-3 rounded d-inline-flex" + style="height: 24px; color: white;" + :style="{ backgroundColor: option.color + ' !important' }" > <span>{{ option.name }}</span> </div> @@ -94,10 +99,11 @@ </div> </div> <div class="mb-2"> - <input v-model="newDraftTag.value" class="form-control" - :class="{'is-invalid': validateDraftTag.valueInvalid}" - :placeholder="$t('value (optional)')" - @keydown.enter.prevent="onEnter" + <input + v-model="newDraftTag.value" class="form-control" + :class="{'is-invalid': validateDraftTag.valueInvalid}" + :placeholder="$t('value (optional)')" + @keydown.enter.prevent="onEnter" /> <div class="invalid-feedback"> {{ $t("Tag with this value already exist.") }} @@ -123,8 +129,8 @@ <script> import { Modal } from "bootstrap"; import VueMultiselect from "vue-multiselect"; -import Tag from "../components/Tag.vue"; import { useToast } from "vue-toastification"; +import Tag from "../components/Tag.vue"; const toast = useToast(); export default { @@ -159,14 +165,14 @@ export default { tagOptions() { const tagOptions = this.existingTags; for (const tag of this.newTags) { - if (!tagOptions.find(t => t.name == tag.name && t.color == tag.color)) { + if (!tagOptions.find(t => t.name === tag.name && t.color === tag.color)) { tagOptions.push(tag); } } return tagOptions; }, selectedTags() { - return this.preSelectedTags.concat(this.newTags).filter(tag => !this.deleteTags.find(monitorTag => monitorTag.id == tag.id)); + return this.preSelectedTags.concat(this.newTags).filter(tag => !this.deleteTags.find(monitorTag => monitorTag.id === tag.id)); }, colorOptions() { return [ @@ -192,7 +198,7 @@ export default { let nameInvalid = false; let valueInvalid = false; let invalid = true; - if (this.deleteTags.find(tag => tag.name == this.newDraftTag.select?.name && tag.value == this.newDraftTag.value)) { + if (this.deleteTags.find(tag => tag.name === this.newDraftTag.select?.name && tag.value === this.newDraftTag.value)) { // Undo removing a Tag nameInvalid = false; valueInvalid = false; @@ -202,9 +208,9 @@ export default { nameInvalid = true; invalid = true; } else if (this.newTags.concat(this.preSelectedTags).filter(tag => ( - tag.name == this.newDraftTag.select?.name && tag.value == this.newDraftTag.value + tag.name === this.newDraftTag.select?.name && tag.value === this.newDraftTag.value ) || ( - tag.name == this.newDraftTag.name && tag.value == this.newDraftTag.value + tag.name === this.newDraftTag.name && tag.value === this.newDraftTag.value )).length > 0) { // Try to add a tag with existing name and value valueInvalid = true; @@ -250,7 +256,7 @@ export default { deleteTag(item) { if (item.new) { // Undo Adding a new Tag - this.newTags = this.newTags.filter(tag => !(tag.name == item.name && tag.value == item.value)); + this.newTags = this.newTags.filter(tag => !(tag.name === item.name && tag.value === item.value)); } else { // Remove an Existing Tag this.deleteTags.push(item); @@ -266,9 +272,9 @@ export default { addDraftTag() { console.log("Adding Draft Tag: ", this.newDraftTag); if (this.newDraftTag.select != null) { - if (this.deleteTags.find(tag => tag.name == this.newDraftTag.select.name && tag.value == this.newDraftTag.value)) { + if (this.deleteTags.find(tag => tag.name === this.newDraftTag.select.name && tag.value === this.newDraftTag.value)) { // Undo removing a tag - this.deleteTags = this.deleteTags.filter(tag => !(tag.name == this.newDraftTag.select.name && tag.value == this.newDraftTag.value)); + this.deleteTags = this.deleteTags.filter(tag => !(tag.name === this.newDraftTag.select.name && tag.value === this.newDraftTag.value)); } else { // Add an existing Tag this.newTags.push({ @@ -345,7 +351,7 @@ export default { tagId = newTagResult.id; // Assign the new ID to the tags of the same name & color this.newTags.map(tag => { - if (tag.name == newTag.name && tag.color == newTag.color) { + if (tag.name === newTag.name && tag.color === newTag.color) { tag.id = newTagResult.id; } }); diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue index cd3bff56..3a49c829 100644 --- a/src/pages/EditMonitor.vue +++ b/src/pages/EditMonitor.vue @@ -361,13 +361,12 @@ </template> <script> +import VueMultiselect from "vue-multiselect"; +import { useToast } from "vue-toastification"; +import CopyableInput from "../components/CopyableInput.vue"; import NotificationDialog from "../components/NotificationDialog.vue"; import ProxyDialog from "../components/ProxyDialog.vue"; import TagsManager from "../components/TagsManager.vue"; -import CopyableInput from "../components/CopyableInput.vue"; - -import { useToast } from "vue-toastification"; -import VueMultiselect from "vue-multiselect"; import { genSecret, isDev } from "../util.ts"; const toast = useToast(); @@ -540,7 +539,7 @@ export default { } for (let i = 0; i < this.$root.notificationList.length; i++) { - if (this.$root.notificationList[i].isDefault == true) { + if (this.$root.notificationList[i].isDefault === true) { this.monitor.notificationIDList[this.$root.notificationList[i].id] = true; } } From b874ea8b28d8ea8fc261a1ca2b843249003bd447 Mon Sep 17 00:00:00 2001 From: Adam Stachowicz <adam.stachowicz@fingo.info> Date: Tue, 26 Apr 2022 01:37:20 +0200 Subject: [PATCH 050/124] Add missing lint-fix:style command --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 3096dfdb..764fb0f9 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "lint:js": "eslint --ext \".js,.vue\" --ignore-path .gitignore .", "lint-fix:js": "eslint --ext \".js,.vue\" --fix --ignore-path .gitignore .", "lint:style": "stylelint \"**/*.{vue,css,scss}\" --ignore-path .gitignore", + "lint-fix:style": "stylelint \"**/*.{vue,css,scss}\" --fix --ignore-path .gitignore", "lint": "npm run lint:js && npm run lint:style", "dev": "concurrently -k -r \"wait-on tcp:3000 && npm run start-server-dev \" \"npm run start-frontend-dev\"", "start-frontend-dev": "cross-env NODE_ENV=development vite --host --config ./config/vite.config.js", From dcdbb7be8b019731ec9fb15a4ff9453049e7fd71 Mon Sep 17 00:00:00 2001 From: Adam Stachowicz <adam.stachowicz@fingo.info> Date: Tue, 26 Apr 2022 01:57:49 +0200 Subject: [PATCH 051/124] Use separate job for linters --- .github/workflows/auto-test.yml | 16 +++++++++++++++- package.json | 2 +- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/.github/workflows/auto-test.yml b/.github/workflows/auto-test.yml index ea7fd79c..69ef622e 100644 --- a/.github/workflows/auto-test.yml +++ b/.github/workflows/auto-test.yml @@ -24,7 +24,7 @@ jobs: - uses: actions/checkout@v3 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v2 + uses: actions/setup-node@v3 with: node-version: ${{ matrix.node-version }} cache: 'npm' @@ -34,3 +34,17 @@ jobs: env: HEADLESS_TEST: 1 JUST_FOR_TEST: ${{ secrets.JUST_FOR_TEST }} + check-linters: + runs-on: ubuntu-latest + + steps: + - run: git config --global core.autocrlf false # Mainly for Windows + - uses: actions/checkout@v3 + + - name: Use Node.js LTS + uses: actions/setup-node@v3 + with: + node-version: 16 + cache: 'npm' + - run: npm run install-legacy + - run: npm run lint diff --git a/package.json b/package.json index 8a8bd1eb..937d932d 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "start-server": "node server/server.js", "start-server-dev": "cross-env NODE_ENV=development node server/server.js", "build": "vite build --config ./config/vite.config.js", - "test": "npm run lint && node test/prepare-test-server.js && node server/server.js --port=3002 --data-dir=./data/test/ --test", + "test": "node test/prepare-test-server.js && node server/server.js --port=3002 --data-dir=./data/test/ --test", "test-with-build": "npm run build && npm test", "jest": "node test/prepare-jest.js && npm run jest-frontend && npm run jest-backend", "jest-frontend": "cross-env TEST_FRONTEND=1 jest --config=./config/jest-frontend.config.js", From 3019d5dd6462a3d73886c0573cb670b610d1c15c Mon Sep 17 00:00:00 2001 From: Adam Stachowicz <adam.stachowicz@fingo.info> Date: Tue, 26 Apr 2022 02:03:47 +0200 Subject: [PATCH 052/124] Shorter node matrix --- .github/workflows/auto-test.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/auto-test.yml b/.github/workflows/auto-test.yml index 69ef622e..c0efc64a 100644 --- a/.github/workflows/auto-test.yml +++ b/.github/workflows/auto-test.yml @@ -16,17 +16,17 @@ jobs: strategy: matrix: os: [macos-latest, ubuntu-latest, windows-latest] - node-version: [14.x, 16.x, 17.x] + node: [14, 16, 17] # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ steps: - run: git config --global core.autocrlf false # Mainly for Windows - uses: actions/checkout@v3 - - name: Use Node.js ${{ matrix.node-version }} + - name: Use Node.js ${{ matrix.node }} uses: actions/setup-node@v3 with: - node-version: ${{ matrix.node-version }} + node-version: ${{ matrix.node }} cache: 'npm' - run: npm run install-legacy - run: npm run build From dcec53a755a6bb3f7d5e8ea8833e32f3f2b87d44 Mon Sep 17 00:00:00 2001 From: Markus Dick <47363631+MarkusDick@users.noreply.github.com> Date: Tue, 26 Apr 2022 20:27:00 +0200 Subject: [PATCH 053/124] lang: update german translations --- src/languages/de-DE.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/languages/de-DE.js b/src/languages/de-DE.js index 1dd86e23..e679937c 100644 --- a/src/languages/de-DE.js +++ b/src/languages/de-DE.js @@ -179,7 +179,7 @@ export default { "Edit Status Page": "Bearbeite Status-Seite", "Go to Dashboard": "Gehe zum Dashboard", "Status Page": "Status-Seite", - "Status Pages": "Status-Seite", + "Status Pages": "Status-Seiten", telegram: "Telegram", webhook: "Webhook", smtp: "E-Mail (SMTP)", @@ -403,8 +403,8 @@ export default { "WeCom Bot Key": "WeCom Bot Schlüssel", "Setup Proxy": "Proxy einrichten", "Proxy Protocol": "Proxy Protokoll", - "Proxy Server": "Proxy Server", - "Proxy server has authentication": "Proxy server hat Authentifizierung", + "Proxy Server": "Proxy-Server", + "Proxy server has authentication": "Proxy-Server hat Authentifizierung", User: "Benutzer", Installed: "Installiert", "Not installed": "Nicht installiert", @@ -442,7 +442,14 @@ export default { "Issuer:": "Aussteller:", "Fingerprint:": "Fingerabdruck:", "No status pages": "Keine Status-Seiten", + "Domain Name Expiry Notification": "Benachrichtigung bei Ablauf des Domainnamens", Customize: "Anpassen", "Custom Footer": "Eigener Footer", "Custom CSS": "Eigenes CSS", + "Footer Text": "Fußzeile", + "Show Powered By": "Zeige 'Powered By'", + "Date Created": "Erstellt am", + "Domain Names": "Domainnamen", + signedInDisp: "Angemeldet als {0}", + signedInDispDisabled: "Authentifizierung deaktiviert.", }; From 03e6f0a6c818a51c37fa94722b86b22e8d3a1f43 Mon Sep 17 00:00:00 2001 From: MrEddX <66828538+MrEddX@users.noreply.github.com> Date: Tue, 26 Apr 2022 21:39:14 +0300 Subject: [PATCH 054/124] Update bg-BG.js Translation fixes. --- src/languages/bg-BG.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/languages/bg-BG.js b/src/languages/bg-BG.js index a419e88e..30afe420 100644 --- a/src/languages/bg-BG.js +++ b/src/languages/bg-BG.js @@ -78,7 +78,7 @@ export default { Save: "Запази", Notifications: "Известявания", "Not available, please setup.": "Не са налични. Моля, настройте.", - "Setup Notification": "Настройки за известявания", + "Setup Notification": "Настрой известяване", Light: "Светла", Dark: "Тъмна", Auto: "Автоматично", @@ -401,7 +401,7 @@ export default { Retry: "Повтори", Topic: "Тема", "WeCom Bot Key": "WeCom бот ключ", - "Setup Proxy": "Настройка за прокси", + "Setup Proxy": "Настрой прокси", "Proxy Protocol": "Прокси протокол", "Proxy Server": "Прокси сървър", "Proxy server has authentication": "Прокси сървърът е с удостоверяване", From 372bf57e9fb388f858005f598c4ec375d24e5b89 Mon Sep 17 00:00:00 2001 From: MrEddX <66828538+MrEddX@users.noreply.github.com> Date: Wed, 27 Apr 2022 09:19:13 +0300 Subject: [PATCH 055/124] Update bg-BG.js Translation fixes --- src/languages/bg-BG.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/languages/bg-BG.js b/src/languages/bg-BG.js index 30afe420..03fd288b 100644 --- a/src/languages/bg-BG.js +++ b/src/languages/bg-BG.js @@ -353,8 +353,8 @@ export default { serwersmsSenderName: "SMS Подател име (регистриран през клиентския портал)", stackfield: "Stackfield", smtpDkimSettings: "DKIM Настройки", - smtpDkimDesc: "Моля, вижте Nodemailer DKIM {0} за инструкции.", - documentation: "документация", + smtpDkimDesc: "Моля, вижте {0} на Nodemailer DKIM за инструкции.", + documentation: "документацията", smtpDkimDomain: "Домейн", smtpDkimKeySelector: "Селектор на ключ", smtpDkimPrivateKey: "Частен ключ", From a3baa3c149612d7bfd893f8b363bdcfaae6ede39 Mon Sep 17 00:00:00 2001 From: Phuong Nguyen Minh <mrphuongbn@gmail.com> Date: Wed, 27 Apr 2022 14:07:42 +0700 Subject: [PATCH 056/124] Update language vi.js (#1572) Update vi-VN.js --- src/languages/vi-VN.js | 102 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/src/languages/vi-VN.js b/src/languages/vi-VN.js index 4ef5a54e..9005c393 100644 --- a/src/languages/vi-VN.js +++ b/src/languages/vi-VN.js @@ -239,6 +239,7 @@ export default { "rocket.chat": "Rocket.chat", pushover: "Pushover", pushy: "Pushy", + PushByTechulus: "Push by Techulus", octopush: "Octopush", promosms: "PromoSMS", clicksendsms: "ClickSend SMS", @@ -308,6 +309,10 @@ export default { "One record": "One record", steamApiKeyDescription: "Để theo dõi các Steam Game Server bạn cần một Steam Web-API key. Bạn có thể đăng ký API key tại đây: ", "Current User": "User hiện tại", + topic: "Topic", + topicExplanation: "MQTT topic to monitor", + successMessage: "Success Message", + successMessageExplanation: "MQTT message that will be considered as success", recent: "Gần đây", Done: "Hoàn thành", Info: "Thông tin", @@ -353,6 +358,9 @@ export default { serwersmsPhoneNumber: "Số điện thoại", serwersmsSenderName: "Tên người gửi SMS (Đã đăng ký qua portal)", "stackfield": "Stackfield", + Customize: "Customize", + "Custom Footer": "Custom Footer", + "Custom CSS": "Custom CSS", smtpDkimSettings: "Cài đặt xác thực Email(DKIM)", smtpDkimDesc: "Xem hướng dẫn tại {0}.", documentation: "Nodemailer DKIM", @@ -362,4 +370,98 @@ export default { smtpDkimHashAlgo: "Hash Algorithm (Tuỳ chọn)", smtpDkimheaderFieldNames: "Header Keys to sign (Tuỳ chọn)", smtpDkimskipFields: "Header Keys not to sign (Tuỳ chọn)", + gorush: "Gorush", + alerta: "Alerta", + alertaApiEndpoint: "API Endpoint", + alertaEnvironment: "Environment", + alertaApiKey: "API Key", + alertaAlertState: "Alert State", + alertaRecoverState: "Recover State", + deleteStatusPageMsg: "Bạn có chắc chắn muốn xoá trang status này?", + Proxies: "Proxies", + default: "Mặc định", + enabled: "Enabled", + setAsDefault: "Set As Default", + deleteProxyMsg: "Bạn muốn xoá proxy này cho tất cả monitors?", + proxyDescription: "Proxies must be assigned to a monitor to function.", + enableProxyDescription: "Proxy này chưa ảnh hưởng tới monitor requests cho tới khi được activated. Bạn có thể tạm thời tắt proxy cho tất cả monitors bằng trạng thái activation.", + setAsDefaultProxyDescription: "Proxy này sẽ bật mặc định cho tất cả monitors mới. Bạn có thể tắt riêng lẻ proxy trên mỗi monitor.", + "Certificate Chain": "Certificate Chain", + Valid: "Hợp lệ", + Invalid: "Không hợp lệ", + AccessKeyId: "AccessKey ID", + SecretAccessKey: "AccessKey Secret", + PhoneNumbers: "PhoneNumbers", + TemplateCode: "TemplateCode", + SignName: "SignName", + "Sms template must contain parameters: ": "Sms template must contain parameters: ", + "Bark Endpoint": "Bark Endpoint", + WebHookUrl: "WebHookUrl", + SecretKey: "SecretKey", + "For safety, must use secret key": "Để an toàn, hãy dùng secret key", + "Device Token": "Device Token", + Platform: "Platform", + iOS: "iOS", + Android: "Android", + Huawei: "Huawei", + High: "High", + Retry: "Retry", + Topic: "Topic", + "WeCom Bot Key": "WeCom Bot Key", + "Setup Proxy": "Setup Proxy", + "Proxy Protocol": "Proxy Protocol", + "Proxy Server": "Proxy Server", + "Proxy server has authentication": "Proxy server has authentication", + User: "User", + Installed: "Installed", + "Not installed": "Not installed", + Running: "Running", + "Not running": "Not running", + "Remove Token": "Remove Token", + Start: "Start", + Stop: "Stop", + "Uptime Kuma": "Uptime Kuma", + "Add New Status Page": "Thêm mới Status Page", + Slug: "Slug", + "Accept characters:": "Accept characters:", + startOrEndWithOnly: "Start or end with {0} only", + "No consecutive dashes": "No consecutive dashes", + Next: "Next", + "The slug is already taken. Please choose another slug.": "The slug is already taken. Please choose another slug.", + "No Proxy": "No Proxy", + "HTTP Basic Auth": "HTTP Basic Auth", + "New Status Page": "New Status Page", + "Page Not Found": "Page Not Found", + "Reverse Proxy": "Reverse Proxy", + Backup: "Backup", + About: "About", + wayToGetCloudflaredURL: "(Download cloudflared from {0})", + cloudflareWebsite: "Cloudflare Website", + "Message:": "Message:", + "Don't know how to get the token? Please read the guide:": "Chưa biết cách lấy token? Xem hướng dẫn tại:", + "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "Nếu bạn đang dùng Cloudflare Tunnel, kết nối hiện tại có thể đang bị mất. Bạn có muốn dừng lại? Nhập lại password để xác nhận.", + "Other Software": "Phần mềm khác", + "For example: nginx, Apache and Traefik.": "Ví dụ: Nginx, Apache hay Traefik.", + "Please read": "Hãy xem qua", + "Subject:": "Subject:", + "Valid To:": "Valid To:", + "Days Remaining:": "Số ngày còn lại:", + "Issuer:": "Issuer:", + "Fingerprint:": "Fingerprint:", + "No status pages": "No status pages", + "Domain Name Expiry Notification": "Cảnh báo hạn hạn Domain Name", + Proxy: "Proxy", + "Date Created": "Ngày khởi tạo", + onebotHttpAddress: "OneBot HTTP Address", + onebotMessageType: "OneBot Message Type", + onebotGroupMessage: "Group", + onebotPrivateMessage: "Private", + onebotUserOrGroupId: "Group/User ID", + onebotSafetyTips: "Để đảm bảo an toàn, hãy thiết lập access token", + "PushDeer Key": "PushDeer Key", + "Footer Text": "Footer Text", + "Show Powered By": "Show Powered By", + "Domain Names": "Domain Names", + signedInDisp: "Signed in as {0}", + signedInDispDisabled: "Auth Disabled.", }; From b50f1bb7e849164b4065ad5f3f5541db8dd43869 Mon Sep 17 00:00:00 2001 From: Louis Lam <louislam@users.noreply.github.com> Date: Wed, 27 Apr 2022 15:29:54 +0800 Subject: [PATCH 057/124] No more opening browser --- config/vite.config.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/config/vite.config.js b/config/vite.config.js index e2f63902..9fdc5fab 100644 --- a/config/vite.config.js +++ b/config/vite.config.js @@ -21,7 +21,4 @@ export default defineConfig({ "plugins": [ postcssRTLCSS ] } }, - server: { - open: "/" - } }); From db646aa40bb37010d2ef0443bb91e5bf6beb6dab Mon Sep 17 00:00:00 2001 From: Dmitry Yaskovich <kadavr95@outlook.com> Date: Wed, 27 Apr 2022 21:10:47 +0300 Subject: [PATCH 058/124] Fix typo in README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 38a1bfcb..e43beed1 100644 --- a/README.md +++ b/README.md @@ -162,7 +162,7 @@ https://www.reddit.com/r/UptimeKuma/ Check out the latest beta release here: https://github.com/louislam/uptime-kuma/releases ### Bug Reports / Feature Requests -If you want to report a bug or request a new feature. Free feel to open a [new issue](https://github.com/louislam/uptime-kuma/issues). +If you want to report a bug or request a new feature, feel free to open a [new issue](https://github.com/louislam/uptime-kuma/issues). ### Translations If you want to translate Uptime Kuma into your language, please read: https://github.com/louislam/uptime-kuma/tree/master/src/languages From d3bd2976c52c6501c70eff79835c00b06cd5b0fd Mon Sep 17 00:00:00 2001 From: Dmitry Yaskovich <kadavr95@outlook.com> Date: Wed, 27 Apr 2022 21:18:47 +0300 Subject: [PATCH 059/124] Fix typo in CONTRIBUTING.md --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0ee006df..9dcf760e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -44,7 +44,7 @@ My long story here: https://www.reddit.com/r/UptimeKuma/comments/t1t6or/comment/ ### Recommended Pull Request Guideline -Before deep into coding, disscussion first is preferred. Creating an empty pull request for disscussion would be recommended. +Before deep into coding, discussion first is preferred. Creating an empty pull request for discussion would be recommended. 1. Fork the project 1. Clone your fork repo to local From 7078b062729f27bdb12255ce51d8a05cbd6c551b Mon Sep 17 00:00:00 2001 From: Dmitry Yaskovich <kadavr95@outlook.com> Date: Wed, 27 Apr 2022 21:22:42 +0300 Subject: [PATCH 060/124] Fix typo in Russian localization --- src/languages/ru-RU.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/languages/ru-RU.js b/src/languages/ru-RU.js index 5e889e52..ae655e7d 100644 --- a/src/languages/ru-RU.js +++ b/src/languages/ru-RU.js @@ -381,7 +381,7 @@ export default { smtpDkimPrivateKey: "Приватный ключ", smtpDkimHashAlgo: "Алгоритм хэша (опционально)", smtpDkimheaderFieldNames: "Заголовок ключей для подписи (опционально)", - smtpDkimskipFields: "Заколовок ключей не для подписи (опционально)", + smtpDkimskipFields: "Заголовок ключей не для подписи (опционально)", gorush: "Gorush", alerta: "Alerta", alertaApiEndpoint: "Конечная точка API", From 09fdef9bdc254482197b58e3b245d29e9f7171b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Szczepa=C5=84ski?= <l.szczepanski@webd.pl> Date: Thu, 28 Apr 2022 11:14:02 +0200 Subject: [PATCH 061/124] Update polish translation --- src/languages/pl.js | 114 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 108 insertions(+), 6 deletions(-) diff --git a/src/languages/pl.js b/src/languages/pl.js index fb377148..ab2480d3 100644 --- a/src/languages/pl.js +++ b/src/languages/pl.js @@ -66,7 +66,7 @@ export default { Keyword: "Słowo kluczowe", "Friendly Name": "Przyjazna nazwa", URL: "URL", - Hostname: "Hostname", + Hostname: "Nazwa hosta", Port: "Port", "Heartbeat Interval": "Częstotliwość bicia serca", Retries: "Prób", @@ -216,7 +216,7 @@ export default { signal: "Signal", Number: "Numer", Recipients: "Odbiorcy", - needSignalAPI: "Musisz posiadać klienta Signal z REST API.", + needSignalAPI: "Musisz mieć klienta Signal z REST API.", wayToCheckSignalURL: "W celu dowiedzenia się, jak go skonfigurować, odwiedź poniższy link:", signalImportant: "UWAGA: Nie można mieszać nazw grup i numerów odbiorców!", gotify: "Gotify", @@ -234,6 +234,7 @@ export default { "rocket.chat": "Rocket.chat", pushover: "Pushover", pushy: "Pushy", + PushByTechulus: "Push od Techulus", octopush: "Octopush", promosms: "PromoSMS", lunasea: "LunaSea", @@ -278,7 +279,7 @@ export default { promosmsTypeEco: "SMS ECO - tanie, lecz wolne. Dostępne tylko w Polsce", promosmsTypeFlash: "SMS FLASH - wiadomość automatycznie wyświetli się na urządzeniu. Dostępne tylko w Polsce.", promosmsTypeFull: "SMS FULL - szybkie i dostępne międzynarodowo. Wersja premium usługi, która pozwala min. ustawić własną nazwę nadawcy.", - promosmsTypeSpeed: "SMS SPEED - wysyłka priorytetowa, posiada wszystkie zalety SMS FULL", + promosmsTypeSpeed: "SMS SPEED - wysyłka priorytetowa, ma wszystkie zalety SMS FULL", promosmsPhoneNumber: "Numer odbiorcy", promosmsSMSSender: "Nadawca SMS (wcześniej zatwierdzone nazwy z panelu PromoSMS)", "Primary Base URL": "Główny URL", @@ -306,6 +307,10 @@ export default { "One record": "Jeden rekord", steamApiKeyDescription: "Do monitorowania serwera gier Steam potrzebny jest klucz Steam Web-API. Możesz zarejestrować swój klucz API tutaj: ", "Current User": "Aktualny użytkownik", + topic: "Temat", + topicExplanation: "Temat MQTT do monitorowania", + successMessage: "Komunikat o powodzeniu", + successMessageExplanation: "Komunikat MQTT, który zostanie uznany za powodzenie", recent: "Ostatnie", Done: "Zrobione", Info: "Info", @@ -344,7 +349,7 @@ export default { Discard: "Odrzuć", Cancel: "Anuluj", "Powered by": "Napędzane przez", - shrinkDatabaseDescription: "Uruchom VACUUM na bazie SQLite. Jeżeli twoja baza została stworzona po wersji 1.10.0, to posiada już włączoną opcję AUTO_VACUUM i stosowanie ręcznego oczyszczania nie jest potrzebne.", + shrinkDatabaseDescription: "Uruchom VACUUM na bazie SQLite. Jeżeli twoja baza została stworzona po wersji 1.10.0, to ma już włączoną opcję AUTO_VACUUM i stosowanie ręcznego oczyszczania nie jest potrzebne.", clicksendsms: "ClickSend SMS", apiCredentials: "Poświadczenia API", serwersms: "SerwerSMS.pl", @@ -352,14 +357,111 @@ export default { serwersmsAPIPassword: "Hasło API", serwersmsPhoneNumber: "Numer telefonu", serwersmsSenderName: "Nazwa nadawcy (zatwierdzona w panelu klienta)", - "stackfield": "Stackfield", + stackfield: "Stackfield", + Customize: "Dostosuj", + "Custom Footer": "Niestandardowa stopka", + "Custom CSS": "Niestandardowy CSS", smtpDkimSettings: "Ustawienia DKIM", smtpDkimDesc: "Zapoznaj się z Nodemailer DKIM {0}, aby dowiedzieć się więcej", documentation: "dokumentacja", smtpDkimDomain: "Nazwa domeny", smtpDkimKeySelector: "Selektor klucza", smtpDkimPrivateKey: "Klucz prywatny", - smtpDkimHashAlgo: "Algorytm Hashowania (opcjonalne)", + smtpDkimHashAlgo: "Algorytm haszujący (opcjonalne)", smtpDkimheaderFieldNames: "Klucze nagłówka do podpisu (opcjonalne)", smtpDkimskipFields: "Klucze nagłówka do pominięcia (opcjonalne)", + gorush: "Gorush", + alerta: "Alerta", + alertaApiEndpoint: "Punkt końcowy API", + alertaEnvironment: "Środowisko", + alertaApiKey: "Klucz API", + alertaAlertState: "Alert State", + alertaRecoverState: "Recover State", + deleteStatusPageMsg: "Jesteś pewien, że chcesz usunąć tę stronę statusów?", + Proxies: "Proxy", + default: "Domyślny", + enabled: "Włączony", + setAsDefault: "Ustaw jako domyślny", + deleteProxyMsg: "Jesteś pewien, że chcesz usunąć proxy ze wszystkich monitorów?", + proxyDescription: "Proxy muszą być przypisane do monitora, aby działały.", + enableProxyDescription: "Ten serwer proxy nie będzie miał wpływu na żądania monitorów, dopóki nie zostanie aktywowany. Możesz kontrolować tymczasowe wyłączenie serwera proxy ze wszystkich monitorów za pomocą statusu aktywacji.", + setAsDefaultProxyDescription: "Ten serwer proxy będzie domyślnie włączony dla nowych monitorów. Można go jednak wyłączyć osobno dla każdego monitora.", + "Certificate Chain": "Łańcuch certyfikatów", + Valid: "Ważny", + Invalid: "Nieważny", + AccessKeyId: "AccessKey ID", + SecretAccessKey: "AccessKey Sekret", + PhoneNumbers: "Numery telefonów", + TemplateCode: "Kod szablonu", + SignName: "Podpis", + "Sms template must contain parameters: ": "Szablon sms musi posiadać parametry: ", + "Bark Endpoint": "Punkt końcowy Bark", + WebHookUrl: "WebHookUrl", + SecretKey: "Tajny klucz", + "For safety, must use secret key": "Ze względów bezpieczeństwa musisz użyć tajnego klucza", + "Device Token": "Device Token", + Platform: "Platforma", + iOS: "iOS", + Android: "Android", + Huawei: "Huawei", + High: "Wysoki", + Retry: "Ponów", + Topic: "Temat", + "WeCom Bot Key": "Klucz bota WeCom", + "Setup Proxy": "Skonfiguruj proxy", + "Proxy Protocol": "Protokół proxy", + "Proxy Server": "Serwer proxy", + "Proxy server has authentication": "Serwer proxy ma autoryzację", + User: "Użytkownik", + Installed: "Zainstalowany", + "Not installed": "Nie zainstalowany", + Running: "Działa", + "Not running": "Nie działa", + "Remove Token": "Usuń token", + Start: "Start", + Stop: "Stop", + "Uptime Kuma": "Uptime Kuma", + "Add New Status Page": "Dodaj nową stronę statusów", + Slug: "Symbol", + "Accept characters:": "Dozwolone znaki:", + startOrEndWithOnly: "Zaczynające się i kończące wyłącznie {0} znakami", + "No consecutive dashes": "Bez powtarzających się myślników", + Next: "Dalej", + "The slug is already taken. Please choose another slug.": "Ten symbol jest już zajęty. Proszę, wybierz inny.", + "No Proxy": "Bez proxy", + "HTTP Basic Auth": "Podstawowa autoryzacja HTTP", + "New Status Page": "Nowa strona statusu", + "Page Not Found": "Strona nie została znaleziona", + "Reverse Proxy": "Odwrotne Proxy", + Backup: "Backup", + About: "O skrypcie", + wayToGetCloudflaredURL: "(Pobierz cloudflared z {0})", + cloudflareWebsite: "Strona Cloudflare", + "Message:": "Wiadomość:", + "Don't know how to get the token? Please read the guide:": "Nie wiesz jak uzyksać token? Przeczytaj proszę poradnik:", + "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "Bieżące połączenie może zostać utracone, jeśli aktualnie łączysz się przez tunel Cloudflare. Czy na pewno chcesz to przerwać? Wpisz swoje aktualne hasło, aby je potwierdzić.", + "Other Software": "Inne oprogramowanie", + "For example: nginx, Apache and Traefik.": "Na przykład: nginx, Apache i Traefik.", + "Please read": "Przeczytaj proszę", + "Subject:": "Temat:", + "Valid To:": "Ważdny do:", + "Days Remaining:": "Pozostało dni:", + "Issuer:": "Wydawca:", + "Fingerprint:": "Odcisk palca:", + "No status pages": "Brak stron statusów", + "Domain Name Expiry Notification": "Powiadomienie o wygasaniu domeny", + Proxy: "Proxy", + "Date Created": "Data stworzenia", + onebotHttpAddress: "Adres HTTP OneBot", + onebotMessageType: "Rodzaj wiadomości OneBot", + onebotGroupMessage: "Grupowa", + onebotPrivateMessage: "Prywatna", + onebotUserOrGroupId: "ID Grupy/Użytkownika", + onebotSafetyTips: "Ze względów bezpieczeństwa musisz ustawić token dostępu", + "PushDeer Key": "Klucz PushDeer", + "Footer Text": "Treść stopki", + "Show Powered By": "Pokaż co napędza stronę", + "Domain Names": "Domeny", + signedInDisp: "Zalogowany jako {0}", + signedInDispDisabled: "Autoryzacja wyłączona.", }; From 023db1450d5c93dc14383bf7ed9f7ea7b68bffd0 Mon Sep 17 00:00:00 2001 From: Louis Lam <louislam@users.noreply.github.com> Date: Thu, 28 Apr 2022 23:12:16 +0800 Subject: [PATCH 062/124] Fix #1577 --- package-lock.json | 869 +++++++++++++++++++++++++++------------------- server/server.js | 8 +- 2 files changed, 526 insertions(+), 351 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9bc402c0..9bedab9f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -73,7 +73,7 @@ }, "devDependencies": { "@actions/github": "~5.0.1", - "@babel/eslint-parser": "~7.15.8", + "@babel/eslint-parser": "~7.17.0", "@babel/preset-env": "^7.15.8", "@types/bootstrap": "~5.1.9", "@vitejs/plugin-legacy": "~1.6.4", @@ -85,16 +85,16 @@ "core-js": "~3.18.3", "cross-env": "~7.0.3", "dns2": "~2.0.1", - "eslint": "~7.32.0", - "eslint-plugin-vue": "~7.18.0", + "eslint": "~8.14.0", + "eslint-plugin-vue": "~8.7.1", "jest": "~27.2.5", "jest-puppeteer": "~6.0.3", - "npm-check-updates": "^12.5.5", + "npm-check-updates": "^12.5.9", "postcss-html": "^1.3.1", "puppeteer": "~13.1.3", "sass": "~1.42.1", - "stylelint": "~14.2.0", - "stylelint-config-standard": "~24.0.0", + "stylelint": "~14.7.1", + "stylelint-config-standard": "~25.0.0", "typescript": "~4.4.4", "vite": "~2.6.14", "wait-on": "^6.0.1" @@ -188,9 +188,9 @@ } }, "node_modules/@babel/eslint-parser": { - "version": "7.15.8", - "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.15.8.tgz", - "integrity": "sha512-fYP7QFngCvgxjUuw8O057SVH5jCXsbFFOoE77CFDcvzwBVgTOkMD/L4mIC5Ud1xf8chK/no2fRbSSn1wvNmKuQ==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.17.0.tgz", + "integrity": "sha512-PUEJ7ZBXbRkbq3qqM/jZ2nIuakUBqCYc7Qf52Lj7dlZ6zERnqisdHioL0l4wwQZnmskMeasqUNzLBFKs3nylXA==", "dev": true, "dependencies": { "eslint-scope": "^5.1.1", @@ -202,7 +202,7 @@ }, "peerDependencies": { "@babel/core": ">=7.11.0", - "eslint": ">=7.5.0" + "eslint": "^7.5.0 || ^8.0.0" } }, "node_modules/@babel/generator": { @@ -1771,25 +1771,31 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", - "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.2.tgz", + "integrity": "sha512-lTVWHs7O2hjBFZunXTZYnYqtB9GakA1lnxIf+gKq2nY5gxkkNi/lQvveW6t8gFdOHTg6nG50Xs95PrLqVpcaLg==", "dev": true, "dependencies": { "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", + "debug": "^4.3.2", + "espree": "^9.3.1", "globals": "^13.9.0", - "ignore": "^4.0.6", + "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", + "js-yaml": "^4.1.0", "minimatch": "^3.0.4", "strip-json-comments": "^3.1.1" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@eslint/eslintrc/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, "node_modules/@eslint/eslintrc/node_modules/globals": { "version": "13.13.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.13.0.tgz", @@ -1805,6 +1811,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/@eslint/eslintrc/node_modules/type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", @@ -1893,12 +1911,12 @@ } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", - "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", + "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^1.2.0", + "@humanwhocodes/object-schema": "^1.2.1", "debug": "^4.1.1", "minimatch": "^3.0.4" }, @@ -3930,15 +3948,6 @@ "string-width": "^4.1.0" } }, - "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -4493,6 +4502,12 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", + "dev": true + }, "node_modules/boolean": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", @@ -5559,6 +5574,15 @@ "node": ">=8" } }, + "node_modules/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, + "engines": { + "node": ">=12.22" + } + }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -6156,18 +6180,6 @@ } } }, - "node_modules/enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "dependencies": { - "ansi-colors": "^4.1.1" - }, - "engines": { - "node": ">=8.6" - } - }, "node_modules/entities": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", @@ -6578,49 +6590,44 @@ } }, "node_modules/eslint": { - "version": "7.32.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", - "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.14.0.tgz", + "integrity": "sha512-3/CE4aJX7LNEiE3i6FeodHmI/38GZtWCsAtsymScmzYapx8q1nVVb+eLcLSzATmCPXw5pT4TqVs1E0OmxAd9tw==", "dev": true, "dependencies": { - "@babel/code-frame": "7.12.11", - "@eslint/eslintrc": "^0.4.3", - "@humanwhocodes/config-array": "^0.5.0", + "@eslint/eslintrc": "^1.2.2", + "@humanwhocodes/config-array": "^0.9.2", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", - "debug": "^4.0.1", + "debug": "^4.3.2", "doctrine": "^3.0.0", - "enquirer": "^2.3.5", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.1", + "eslint-scope": "^7.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.1", "esquery": "^1.4.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.1.2", + "glob-parent": "^6.0.1", "globals": "^13.6.0", - "ignore": "^4.0.6", + "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", + "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.0.4", "natural-compare": "^1.4.0", "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", - "table": "^6.0.9", "text-table": "^0.2.0", "v8-compile-cache": "^2.0.3" }, @@ -6628,28 +6635,45 @@ "eslint": "bin/eslint.js" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/eslint-plugin-vue": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-7.18.0.tgz", - "integrity": "sha512-ceDXlXYMMPMSXw7tdKUR42w9jlzthJGJ3Kvm3YrZ0zuQfvAySNxe8sm6VHuksBW0+060GzYXhHJG6IHVOfF83Q==", + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-8.7.1.tgz", + "integrity": "sha512-28sbtm4l4cOzoO1LtzQPxfxhQABararUb1JtqusQqObJpWX2e/gmVyeYVfepizPFne0Q5cILkYGiBoV36L12Wg==", "dev": true, "dependencies": { - "eslint-utils": "^2.1.0", + "eslint-utils": "^3.0.0", "natural-compare": "^1.4.0", - "semver": "^6.3.0", - "vue-eslint-parser": "^7.10.0" + "nth-check": "^2.0.1", + "postcss-selector-parser": "^6.0.9", + "semver": "^7.3.5", + "vue-eslint-parser": "^8.0.1" }, "engines": { - "node": ">=8.10" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "peerDependencies": { - "eslint": "^6.2.0 || ^7.0.0 || ^8.0.0-0" + "eslint": "^6.2.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/eslint-plugin-vue/node_modules/semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/eslint-scope": { @@ -6666,27 +6690,21 @@ } }, "node_modules/eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", "dev": true, "dependencies": { - "eslint-visitor-keys": "^1.1.0" + "eslint-visitor-keys": "^2.0.0" }, "engines": { - "node": ">=6" + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" }, "funding": { "url": "https://github.com/sponsors/mysticatea" - } - }, - "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true, - "engines": { - "node": ">=4" + }, + "peerDependencies": { + "eslint": ">=5" } }, "node_modules/eslint-visitor-keys": { @@ -6698,15 +6716,6 @@ "node": ">=10" } }, - "node_modules/eslint/node_modules/@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.10.4" - } - }, "node_modules/eslint/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -6722,6 +6731,12 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/eslint/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, "node_modules/eslint/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -6768,6 +6783,49 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/eslint/node_modules/globals": { "version": "13.13.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.13.0.tgz", @@ -6792,19 +6850,16 @@ "node": ">=8" } }, - "node_modules/eslint/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "node_modules/eslint/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "dependencies": { - "lru-cache": "^6.0.0" + "argparse": "^2.0.1" }, "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" + "js-yaml": "bin/js-yaml.js" } }, "node_modules/eslint/node_modules/supports-color": { @@ -6840,26 +6895,38 @@ } }, "node_modules/espree": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", - "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.1.tgz", + "integrity": "sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ==", "dev": true, "dependencies": { - "acorn": "^7.4.0", + "acorn": "^8.7.0", "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^1.3.0" + "eslint-visitor-keys": "^3.3.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/espree/node_modules/acorn": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", + "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" } }, "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", "dev": true, "engines": { - "node": ">=4" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/esprima": { @@ -7846,15 +7913,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/globby/node_modules/ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, "node_modules/globjoin": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/globjoin/-/globjoin-0.1.4.tgz", @@ -8045,12 +8103,15 @@ "dev": true }, "node_modules/html-tags": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.1.0.tgz", - "integrity": "sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.2.0.tgz", + "integrity": "sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg==", "dev": true, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/htmlparser2": { @@ -8211,9 +8272,9 @@ ] }, "node_modules/ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", "dev": true, "engines": { "node": ">= 4" @@ -12121,15 +12182,15 @@ } }, "node_modules/npm-check-updates": { - "version": "12.5.5", - "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-12.5.5.tgz", - "integrity": "sha512-7LH6KN6F1fZMtY4zNYAQPpJU1ToxZ6sSCxk948vrLIz97aNqmPLSX72MrmbOWwpyBgLCPbFJWY/k3zE18pmxfw==", + "version": "12.5.9", + "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-12.5.9.tgz", + "integrity": "sha512-l9iOvD7EsQb96gFJL45V01YG6bP8+dmobYnSguvehPuNwgdWNMrE8RC8bSfURX5iUmX4bkobN4T8XMHXN9GMHA==", "dev": true, "dependencies": { "chalk": "^4.1.2", "cint": "^8.2.1", "cli-table": "^0.3.11", - "commander": "^9.0.0", + "commander": "^9.1.0", "fast-memoize": "^2.5.2", "find-up": "5.0.0", "fp-and-or": "^0.1.3", @@ -12142,11 +12203,11 @@ "lodash": "^4.17.21", "minimatch": "^5.0.1", "p-map": "^4.0.0", - "pacote": "^13.0.3", + "pacote": "^13.0.5", "parse-github-url": "^1.0.2", "progress": "^2.0.3", "prompts": "^2.4.2", - "rc-config-loader": "^4.0.0", + "rc-config-loader": "^4.1.0", "remote-git-tags": "^3.0.0", "rimraf": "^3.0.2", "semver": "^7.3.5", @@ -12613,6 +12674,18 @@ "set-blocking": "^2.0.0" } }, + "node_modules/nth-check": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz", + "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, "node_modules/number-allocator": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/number-allocator/-/number-allocator-1.0.10.tgz", @@ -13753,9 +13826,9 @@ } }, "node_modules/rc-config-loader": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/rc-config-loader/-/rc-config-loader-4.0.0.tgz", - "integrity": "sha512-//LRTblJEcqbmmro1GCmZ39qZXD+JqzuD8Y5/IZU3Dhp3A1Yr0Xn68ks8MQ6qKfKvYCWDveUmRDKDA40c+sCXw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/rc-config-loader/-/rc-config-loader-4.1.0.tgz", + "integrity": "sha512-aW+kX4qy0CiM9L4fG4Us3oEOpIrOrXzWykAn+xldD07Y9PXWjTH744oHbv0Kc9ZwWaylw3jMjxaf14RgStrNrA==", "dev": true, "dependencies": { "debug": "^4.1.1", @@ -15091,24 +15164,25 @@ "dev": true }, "node_modules/stylelint": { - "version": "14.2.0", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-14.2.0.tgz", - "integrity": "sha512-i0DrmDXFNpDsWiwx6SPRs4/pyw4kvZgqpDGvsTslQMY7hpUl6r33aQvNSn6cnTg2wtZ9rreFElI7XAKpOWi1vQ==", + "version": "14.7.1", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-14.7.1.tgz", + "integrity": "sha512-rUOWm67hrzGXXyO/cInENEejF4urh1dLgOb9cr/3XLDb/t/A+rXQp3p6+no8o8QCKTgBUdhVUq/bXMgE988PJw==", "dev": true, "dependencies": { "balanced-match": "^2.0.0", "colord": "^2.9.2", "cosmiconfig": "^7.0.1", - "debug": "^4.3.3", + "css-functions-list": "^3.0.1", + "debug": "^4.3.4", "execall": "^2.0.0", - "fast-glob": "^3.2.7", + "fast-glob": "^3.2.11", "fastest-levenshtein": "^1.0.12", "file-entry-cache": "^6.0.1", "get-stdin": "^8.0.0", "global-modules": "^2.0.0", - "globby": "^11.0.4", + "globby": "^11.1.0", "globjoin": "^0.1.4", - "html-tags": "^3.1.0", + "html-tags": "^3.2.0", "ignore": "^5.2.0", "import-lazy": "^4.0.0", "imurmurhash": "^0.1.4", @@ -15116,25 +15190,26 @@ "known-css-properties": "^0.24.0", "mathml-tag-names": "^2.1.3", "meow": "^9.0.0", - "micromatch": "^4.0.4", + "micromatch": "^4.0.5", "normalize-path": "^3.0.0", "normalize-selector": "^0.2.0", "picocolors": "^1.0.0", - "postcss": "^8.3.11", + "postcss": "^8.4.12", "postcss-media-query-parser": "^0.2.3", "postcss-resolve-nested-selector": "^0.1.1", "postcss-safe-parser": "^6.0.0", - "postcss-selector-parser": "^6.0.7", - "postcss-value-parser": "^4.1.0", + "postcss-selector-parser": "^6.0.10", + "postcss-value-parser": "^4.2.0", "resolve-from": "^5.0.0", "specificity": "^0.4.1", "string-width": "^4.2.3", "strip-ansi": "^6.0.1", "style-search": "^0.1.0", + "supports-hyperlinks": "^2.2.0", "svg-tags": "^1.0.0", - "table": "^6.7.5", + "table": "^6.8.0", "v8-compile-cache": "^2.3.0", - "write-file-atomic": "^3.0.3" + "write-file-atomic": "^4.0.1" }, "bin": { "stylelint": "bin/stylelint.js" @@ -15148,24 +15223,24 @@ } }, "node_modules/stylelint-config-recommended": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-6.0.0.tgz", - "integrity": "sha512-ZorSSdyMcxWpROYUvLEMm0vSZud2uB7tX1hzBZwvVY9SV/uly4AvvJPPhCcymZL3fcQhEQG5AELmrxWqtmzacw==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-7.0.0.tgz", + "integrity": "sha512-yGn84Bf/q41J4luis1AZ95gj0EQwRX8lWmGmBwkwBNSkpGSpl66XcPTulxGa/Z91aPoNGuIGBmFkcM1MejMo9Q==", "dev": true, "peerDependencies": { - "stylelint": "^14.0.0" + "stylelint": "^14.4.0" } }, "node_modules/stylelint-config-standard": { - "version": "24.0.0", - "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-24.0.0.tgz", - "integrity": "sha512-+RtU7fbNT+VlNbdXJvnjc3USNPZRiRVp/d2DxOF/vBDDTi0kH5RX2Ny6errdtZJH3boO+bmqIYEllEmok4jiuw==", + "version": "25.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-25.0.0.tgz", + "integrity": "sha512-21HnP3VSpaT1wFjFvv9VjvOGDtAviv47uTp3uFmzcN+3Lt+RYRv6oAplLaV51Kf792JSxJ6svCJh/G18E9VnCA==", "dev": true, "dependencies": { - "stylelint-config-recommended": "^6.0.0" + "stylelint-config-recommended": "^7.0.0" }, "peerDependencies": { - "stylelint": "^14.0.0" + "stylelint": "^14.4.0" } }, "node_modules/stylelint/node_modules/balanced-match": { @@ -15200,15 +15275,6 @@ "node": ">=6" } }, - "node_modules/stylelint/node_modules/ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, "node_modules/stylelint/node_modules/kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -15239,6 +15305,19 @@ "which": "bin/which" } }, + "node_modules/stylelint/node_modules/write-file-atomic": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.1.tgz", + "integrity": "sha512-nSKUxgAbyioruk6hU87QzVbY279oYT6uiwgDoujth2ju4mJ+TZau7SQBhtbTmUyuNYTuXnSyRn66FV0+eCgcrQ==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16" + } + }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -16175,50 +16254,73 @@ } }, "node_modules/vue-eslint-parser": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-7.11.0.tgz", - "integrity": "sha512-qh3VhDLeh773wjgNTl7ss0VejY9bMMa0GoDG2fQVyDzRFdiU3L7fw74tWZDHNQXdZqxO3EveQroa9ct39D2nqg==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-8.3.0.tgz", + "integrity": "sha512-dzHGG3+sYwSf6zFBa0Gi9ZDshD7+ad14DGOdTLjruRVgZXe2J+DcZ9iUhyR48z5g1PqRa20yt3Njna/veLJL/g==", "dev": true, "dependencies": { - "debug": "^4.1.1", - "eslint-scope": "^5.1.1", - "eslint-visitor-keys": "^1.1.0", - "espree": "^6.2.1", + "debug": "^4.3.2", + "eslint-scope": "^7.0.0", + "eslint-visitor-keys": "^3.1.0", + "espree": "^9.0.0", "esquery": "^1.4.0", "lodash": "^4.17.21", - "semver": "^6.3.0" + "semver": "^7.3.5" }, "engines": { - "node": ">=8.10" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/mysticatea" }, "peerDependencies": { - "eslint": ">=5.0.0" + "eslint": ">=6.0.0" + } + }, + "node_modules/vue-eslint-parser/node_modules/eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/vue-eslint-parser/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", "dev": true, "engines": { - "node": ">=4" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/vue-eslint-parser/node_modules/espree": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-6.2.1.tgz", - "integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==", + "node_modules/vue-eslint-parser/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/vue-eslint-parser/node_modules/semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", "dev": true, "dependencies": { - "acorn": "^7.1.1", - "acorn-jsx": "^5.2.0", - "eslint-visitor-keys": "^1.1.0" + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" }, "engines": { - "node": ">=6.0.0" + "node": ">=10" } }, "node_modules/vue-i18n": { @@ -16763,9 +16865,9 @@ } }, "@babel/eslint-parser": { - "version": "7.15.8", - "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.15.8.tgz", - "integrity": "sha512-fYP7QFngCvgxjUuw8O057SVH5jCXsbFFOoE77CFDcvzwBVgTOkMD/L4mIC5Ud1xf8chK/no2fRbSSn1wvNmKuQ==", + "version": "7.17.0", + "resolved": "https://registry.npmjs.org/@babel/eslint-parser/-/eslint-parser-7.17.0.tgz", + "integrity": "sha512-PUEJ7ZBXbRkbq3qqM/jZ2nIuakUBqCYc7Qf52Lj7dlZ6zERnqisdHioL0l4wwQZnmskMeasqUNzLBFKs3nylXA==", "dev": true, "requires": { "eslint-scope": "^5.1.1", @@ -17859,22 +17961,28 @@ "integrity": "sha512-QgGnZ9b7o4k0Ai1ZbTJWwZpZcFK9d+Gb+DyNt4UT9x6IEIs5HVu0iIlmgzGqN+t9MoJSpSPo9S/Mm51UtHr3JA==" }, "@eslint/eslintrc": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", - "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.2.2.tgz", + "integrity": "sha512-lTVWHs7O2hjBFZunXTZYnYqtB9GakA1lnxIf+gKq2nY5gxkkNi/lQvveW6t8gFdOHTg6nG50Xs95PrLqVpcaLg==", "dev": true, "requires": { "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", + "debug": "^4.3.2", + "espree": "^9.3.1", "globals": "^13.9.0", - "ignore": "^4.0.6", + "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", + "js-yaml": "^4.1.0", "minimatch": "^3.0.4", "strip-json-comments": "^3.1.1" }, "dependencies": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, "globals": { "version": "13.13.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.13.0.tgz", @@ -17884,6 +17992,15 @@ "type-fest": "^0.20.2" } }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, "type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", @@ -17948,12 +18065,12 @@ } }, "@humanwhocodes/config-array": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", - "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", + "version": "0.9.5", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.9.5.tgz", + "integrity": "sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==", "dev": true, "requires": { - "@humanwhocodes/object-schema": "^1.2.0", + "@humanwhocodes/object-schema": "^1.2.1", "debug": "^4.1.1", "minimatch": "^3.0.4" } @@ -19637,12 +19754,6 @@ "string-width": "^4.1.0" } }, - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true - }, "ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -20087,6 +20198,12 @@ } } }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", + "dev": true + }, "boolean": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", @@ -20889,6 +21006,12 @@ "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", "dev": true }, + "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 + }, "cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -21350,15 +21473,6 @@ "@socket.io/base64-arraybuffer": "~1.0.2" } }, - "enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "requires": { - "ansi-colors": "^4.1.1" - } - }, "entities": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", @@ -21620,62 +21734,48 @@ } }, "eslint": { - "version": "7.32.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", - "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.14.0.tgz", + "integrity": "sha512-3/CE4aJX7LNEiE3i6FeodHmI/38GZtWCsAtsymScmzYapx8q1nVVb+eLcLSzATmCPXw5pT4TqVs1E0OmxAd9tw==", "dev": true, "requires": { - "@babel/code-frame": "7.12.11", - "@eslint/eslintrc": "^0.4.3", - "@humanwhocodes/config-array": "^0.5.0", + "@eslint/eslintrc": "^1.2.2", + "@humanwhocodes/config-array": "^0.9.2", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", - "debug": "^4.0.1", + "debug": "^4.3.2", "doctrine": "^3.0.0", - "enquirer": "^2.3.5", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.1", + "eslint-scope": "^7.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.1", "esquery": "^1.4.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.1.2", + "glob-parent": "^6.0.1", "globals": "^13.6.0", - "ignore": "^4.0.6", + "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", + "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.0.4", "natural-compare": "^1.4.0", "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", "strip-json-comments": "^3.1.0", - "table": "^6.0.9", "text-table": "^0.2.0", "v8-compile-cache": "^2.0.3" }, "dependencies": { - "@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "dev": true, - "requires": { - "@babel/highlight": "^7.10.4" - } - }, "ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -21685,6 +21785,12 @@ "color-convert": "^2.0.1" } }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -21716,6 +21822,37 @@ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true }, + "eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + } + }, "globals": { "version": "13.13.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.13.0.tgz", @@ -21731,13 +21868,13 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "requires": { - "lru-cache": "^6.0.0" + "argparse": "^2.0.1" } }, "supports-color": { @@ -21758,15 +21895,28 @@ } }, "eslint-plugin-vue": { - "version": "7.18.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-7.18.0.tgz", - "integrity": "sha512-ceDXlXYMMPMSXw7tdKUR42w9jlzthJGJ3Kvm3YrZ0zuQfvAySNxe8sm6VHuksBW0+060GzYXhHJG6IHVOfF83Q==", + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-8.7.1.tgz", + "integrity": "sha512-28sbtm4l4cOzoO1LtzQPxfxhQABararUb1JtqusQqObJpWX2e/gmVyeYVfepizPFne0Q5cILkYGiBoV36L12Wg==", "dev": true, "requires": { - "eslint-utils": "^2.1.0", + "eslint-utils": "^3.0.0", "natural-compare": "^1.4.0", - "semver": "^6.3.0", - "vue-eslint-parser": "^7.10.0" + "nth-check": "^2.0.1", + "postcss-selector-parser": "^6.0.9", + "semver": "^7.3.5", + "vue-eslint-parser": "^8.0.1" + }, + "dependencies": { + "semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } } }, "eslint-scope": { @@ -21780,20 +21930,12 @@ } }, "eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", "dev": true, "requires": { - "eslint-visitor-keys": "^1.1.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - } + "eslint-visitor-keys": "^2.0.0" } }, "eslint-visitor-keys": { @@ -21808,20 +21950,26 @@ "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==" }, "espree": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", - "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.3.1.tgz", + "integrity": "sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ==", "dev": true, "requires": { - "acorn": "^7.4.0", + "acorn": "^8.7.0", "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^1.3.0" + "eslint-visitor-keys": "^3.3.0" }, "dependencies": { + "acorn": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.7.1.tgz", + "integrity": "sha512-Xx54uLJQZ19lKygFXOWsscKUbsBZW0CPykPhVQdhIeIwrbPmJzqeASDInc8nKBnp/JT6igTs82qPXz069H8I/A==", + "dev": true + }, "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", "dev": true } } @@ -22598,14 +22746,6 @@ "ignore": "^5.2.0", "merge2": "^1.4.1", "slash": "^3.0.0" - }, - "dependencies": { - "ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", - "dev": true - } } }, "globjoin": { @@ -22759,9 +22899,9 @@ "dev": true }, "html-tags": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.1.0.tgz", - "integrity": "sha512-1qYz89hW3lFDEazhjW0yVAV87lw8lVkrJocr72XmBkMKsoSVJCQx3W8BXsC7hO2qAt8BoVjYjtAcZ9perqGnNg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/html-tags/-/html-tags-3.2.0.tgz", + "integrity": "sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg==", "dev": true }, "htmlparser2": { @@ -22879,9 +23019,9 @@ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" }, "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", "dev": true }, "ignore-walk": { @@ -25874,15 +26014,15 @@ } }, "npm-check-updates": { - "version": "12.5.5", - "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-12.5.5.tgz", - "integrity": "sha512-7LH6KN6F1fZMtY4zNYAQPpJU1ToxZ6sSCxk948vrLIz97aNqmPLSX72MrmbOWwpyBgLCPbFJWY/k3zE18pmxfw==", + "version": "12.5.9", + "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-12.5.9.tgz", + "integrity": "sha512-l9iOvD7EsQb96gFJL45V01YG6bP8+dmobYnSguvehPuNwgdWNMrE8RC8bSfURX5iUmX4bkobN4T8XMHXN9GMHA==", "dev": true, "requires": { "chalk": "^4.1.2", "cint": "^8.2.1", "cli-table": "^0.3.11", - "commander": "^9.0.0", + "commander": "^9.1.0", "fast-memoize": "^2.5.2", "find-up": "5.0.0", "fp-and-or": "^0.1.3", @@ -25895,11 +26035,11 @@ "lodash": "^4.17.21", "minimatch": "^5.0.1", "p-map": "^4.0.0", - "pacote": "^13.0.3", + "pacote": "^13.0.5", "parse-github-url": "^1.0.2", "progress": "^2.0.3", "prompts": "^2.4.2", - "rc-config-loader": "^4.0.0", + "rc-config-loader": "^4.1.0", "remote-git-tags": "^3.0.0", "rimraf": "^3.0.2", "semver": "^7.3.5", @@ -26249,6 +26389,15 @@ "set-blocking": "^2.0.0" } }, + "nth-check": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.0.1.tgz", + "integrity": "sha512-it1vE95zF6dTT9lBsYbxvqh0Soy4SPowchj0UBGj/V6cTPnXXtQOPUbhZ6CmGzAD/rW22LQK6E96pcdJXk4A4w==", + "dev": true, + "requires": { + "boolbase": "^1.0.0" + } + }, "number-allocator": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/number-allocator/-/number-allocator-1.0.10.tgz", @@ -27082,9 +27231,9 @@ } }, "rc-config-loader": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/rc-config-loader/-/rc-config-loader-4.0.0.tgz", - "integrity": "sha512-//LRTblJEcqbmmro1GCmZ39qZXD+JqzuD8Y5/IZU3Dhp3A1Yr0Xn68ks8MQ6qKfKvYCWDveUmRDKDA40c+sCXw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/rc-config-loader/-/rc-config-loader-4.1.0.tgz", + "integrity": "sha512-aW+kX4qy0CiM9L4fG4Us3oEOpIrOrXzWykAn+xldD07Y9PXWjTH744oHbv0Kc9ZwWaylw3jMjxaf14RgStrNrA==", "dev": true, "requires": { "debug": "^4.1.1", @@ -28121,24 +28270,25 @@ "dev": true }, "stylelint": { - "version": "14.2.0", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-14.2.0.tgz", - "integrity": "sha512-i0DrmDXFNpDsWiwx6SPRs4/pyw4kvZgqpDGvsTslQMY7hpUl6r33aQvNSn6cnTg2wtZ9rreFElI7XAKpOWi1vQ==", + "version": "14.7.1", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-14.7.1.tgz", + "integrity": "sha512-rUOWm67hrzGXXyO/cInENEejF4urh1dLgOb9cr/3XLDb/t/A+rXQp3p6+no8o8QCKTgBUdhVUq/bXMgE988PJw==", "dev": true, "requires": { "balanced-match": "^2.0.0", "colord": "^2.9.2", "cosmiconfig": "^7.0.1", - "debug": "^4.3.3", + "css-functions-list": "^3.0.1", + "debug": "^4.3.4", "execall": "^2.0.0", - "fast-glob": "^3.2.7", + "fast-glob": "^3.2.11", "fastest-levenshtein": "^1.0.12", "file-entry-cache": "^6.0.1", "get-stdin": "^8.0.0", "global-modules": "^2.0.0", - "globby": "^11.0.4", + "globby": "^11.1.0", "globjoin": "^0.1.4", - "html-tags": "^3.1.0", + "html-tags": "^3.2.0", "ignore": "^5.2.0", "import-lazy": "^4.0.0", "imurmurhash": "^0.1.4", @@ -28146,25 +28296,26 @@ "known-css-properties": "^0.24.0", "mathml-tag-names": "^2.1.3", "meow": "^9.0.0", - "micromatch": "^4.0.4", + "micromatch": "^4.0.5", "normalize-path": "^3.0.0", "normalize-selector": "^0.2.0", "picocolors": "^1.0.0", - "postcss": "^8.3.11", + "postcss": "^8.4.12", "postcss-media-query-parser": "^0.2.3", "postcss-resolve-nested-selector": "^0.1.1", "postcss-safe-parser": "^6.0.0", - "postcss-selector-parser": "^6.0.7", - "postcss-value-parser": "^4.1.0", + "postcss-selector-parser": "^6.0.10", + "postcss-value-parser": "^4.2.0", "resolve-from": "^5.0.0", "specificity": "^0.4.1", "string-width": "^4.2.3", "strip-ansi": "^6.0.1", "style-search": "^0.1.0", + "supports-hyperlinks": "^2.2.0", "svg-tags": "^1.0.0", - "table": "^6.7.5", + "table": "^6.8.0", "v8-compile-cache": "^2.3.0", - "write-file-atomic": "^3.0.3" + "write-file-atomic": "^4.0.1" }, "dependencies": { "balanced-match": { @@ -28193,12 +28344,6 @@ "which": "^1.3.1" } }, - "ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", - "dev": true - }, "kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -28219,22 +28364,32 @@ "requires": { "isexe": "^2.0.0" } + }, + "write-file-atomic": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.1.tgz", + "integrity": "sha512-nSKUxgAbyioruk6hU87QzVbY279oYT6uiwgDoujth2ju4mJ+TZau7SQBhtbTmUyuNYTuXnSyRn66FV0+eCgcrQ==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + } } } }, "stylelint-config-recommended": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-6.0.0.tgz", - "integrity": "sha512-ZorSSdyMcxWpROYUvLEMm0vSZud2uB7tX1hzBZwvVY9SV/uly4AvvJPPhCcymZL3fcQhEQG5AELmrxWqtmzacw==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-recommended/-/stylelint-config-recommended-7.0.0.tgz", + "integrity": "sha512-yGn84Bf/q41J4luis1AZ95gj0EQwRX8lWmGmBwkwBNSkpGSpl66XcPTulxGa/Z91aPoNGuIGBmFkcM1MejMo9Q==", "dev": true }, "stylelint-config-standard": { - "version": "24.0.0", - "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-24.0.0.tgz", - "integrity": "sha512-+RtU7fbNT+VlNbdXJvnjc3USNPZRiRVp/d2DxOF/vBDDTi0kH5RX2Ny6errdtZJH3boO+bmqIYEllEmok4jiuw==", + "version": "25.0.0", + "resolved": "https://registry.npmjs.org/stylelint-config-standard/-/stylelint-config-standard-25.0.0.tgz", + "integrity": "sha512-21HnP3VSpaT1wFjFvv9VjvOGDtAviv47uTp3uFmzcN+3Lt+RYRv6oAplLaV51Kf792JSxJ6svCJh/G18E9VnCA==", "dev": true, "requires": { - "stylelint-config-recommended": "^6.0.0" + "stylelint-config-recommended": "^7.0.0" } }, "supports-color": { @@ -28945,35 +29100,49 @@ "integrity": "sha512-/3xFwzSykLW2HiiLie43a+FFgNOcokbBJ+fzvFXd0r2T8MYohqvphUyDQ8lbAwzQ3Dlcrb1c9ykifGkhSIAk6A==" }, "vue-eslint-parser": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-7.11.0.tgz", - "integrity": "sha512-qh3VhDLeh773wjgNTl7ss0VejY9bMMa0GoDG2fQVyDzRFdiU3L7fw74tWZDHNQXdZqxO3EveQroa9ct39D2nqg==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-8.3.0.tgz", + "integrity": "sha512-dzHGG3+sYwSf6zFBa0Gi9ZDshD7+ad14DGOdTLjruRVgZXe2J+DcZ9iUhyR48z5g1PqRa20yt3Njna/veLJL/g==", "dev": true, "requires": { - "debug": "^4.1.1", - "eslint-scope": "^5.1.1", - "eslint-visitor-keys": "^1.1.0", - "espree": "^6.2.1", + "debug": "^4.3.2", + "eslint-scope": "^7.0.0", + "eslint-visitor-keys": "^3.1.0", + "espree": "^9.0.0", "esquery": "^1.4.0", "lodash": "^4.17.21", - "semver": "^6.3.0" + "semver": "^7.3.5" }, "dependencies": { - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - }, - "espree": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-6.2.1.tgz", - "integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==", + "eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", "dev": true, "requires": { - "acorn": "^7.1.1", - "acorn-jsx": "^5.2.0", - "eslint-visitor-keys": "^1.1.0" + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + }, + "semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" } } } diff --git a/server/server.js b/server/server.js index 73ea3f55..11c59edb 100644 --- a/server/server.js +++ b/server/server.js @@ -1047,7 +1047,13 @@ try { try { checkLogin(socket); - if (data.disableAuth) { + // If currently is disabled auth, don't need to check + // Disabled Auth + Want to Disable Auth => No Check + // Disabled Auth + Want to Enable Auth => No Check + // Enabled Auth + Want to Disable Auth => Check!! + // Enabled Auth + Want to Enable Auth => No Check + const currentDisabledAuth = await setting("disableAuth"); + if (!currentDisabledAuth && data.disableAuth) { await doubleCheckPassword(socket, currentPassword); } From 60538036c60d43799a52c02b37c2da30df405bff Mon Sep 17 00:00:00 2001 From: Louis Lam <louislam@users.noreply.github.com> Date: Thu, 28 Apr 2022 23:44:08 +0800 Subject: [PATCH 063/124] [#1108] Change "true" to "up" --- 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 73c728b8..8f5f6d6f 100644 --- a/server/routers/api-router.js +++ b/server/routers/api-router.js @@ -34,8 +34,8 @@ router.get("/api/push/:pushToken", async (request, response) => { let pushToken = request.params.pushToken; let msg = request.query.msg || "OK"; let ping = request.query.ping || null; - let status = request.query.status || "true"; - status = status === "true" ? UP : DOWN; + let statusString = request.query.status || "up"; + let status = (statusString === "up") ? UP : DOWN; let monitor = await R.findOne("monitor", " push_token = ? AND active = 1 ", [ pushToken From bddd5de22bd31e51e5b7bc6ba45c3f8f95dd6b01 Mon Sep 17 00:00:00 2001 From: Louis Lam <louislam@users.noreply.github.com> Date: Fri, 29 Apr 2022 11:35:03 +0800 Subject: [PATCH 064/124] Update CONTRIBUTING.md --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9dcf760e..2f1e3f9b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -115,8 +115,8 @@ npm run dev ## Backend Server -For development, it binds to `0.0.0.0:3001` by default. -For production, it binds to `0.0.0.0:3000` by default. +It binds to `0.0.0.0:3001` by default. + It is mainly a socket.io app + express.js. From d7f16908d8b73dd053d99d9894163b61aa0f8c57 Mon Sep 17 00:00:00 2001 From: Louis Lam <louislam@users.noreply.github.com> Date: Fri, 29 Apr 2022 13:39:30 +0800 Subject: [PATCH 065/124] Update CONTRIBUTING.md --- CONTRIBUTING.md | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9dcf760e..51145908 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -27,20 +27,16 @@ The frontend code build into "dist" directory. The server (express.js) exposes t ## Can I create a pull request for Uptime Kuma? -⚠️ 2022-03-02 Update: - -Since I found that merging pull requests is a pretty heavy task for me, I try to rearrange it. +(Updated 2022-04-24) Since I don't want to waste your time, be sure to create empty draft pull request, so we can discuss first. ✅ Accept: - Bug/Security fix - Translations - Adding notification providers -❌ Avoid: +⚠️ Discuss First - Large pull requests -- New big features - -My long story here: https://www.reddit.com/r/UptimeKuma/comments/t1t6or/comment/hynyijx/ +- New features ### Recommended Pull Request Guideline From 82aa52b330c3acb5306471770afb71f17ff81bd2 Mon Sep 17 00:00:00 2001 From: Louis Lam <louislam@users.noreply.github.com> Date: Fri, 29 Apr 2022 13:49:32 +0800 Subject: [PATCH 066/124] Update Apprise to 0.9.8.3 --- docker/alpine-base.dockerfile | 2 +- docker/debian-base.dockerfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/alpine-base.dockerfile b/docker/alpine-base.dockerfile index c7ce195a..1e1643ca 100644 --- a/docker/alpine-base.dockerfile +++ b/docker/alpine-base.dockerfile @@ -4,5 +4,5 @@ WORKDIR /app # Install apprise, iputils for non-root ping, setpriv RUN apk add --no-cache iputils setpriv dumb-init python3 py3-cryptography py3-pip py3-six py3-yaml py3-click py3-markdown py3-requests py3-requests-oauthlib && \ - pip3 --no-cache-dir install apprise==0.9.8 && \ + pip3 --no-cache-dir install apprise==0.9.8.3 && \ rm -rf /root/.cache diff --git a/docker/debian-base.dockerfile b/docker/debian-base.dockerfile index 5502ec11..f2016b80 100644 --- a/docker/debian-base.dockerfile +++ b/docker/debian-base.dockerfile @@ -11,7 +11,7 @@ WORKDIR /app RUN apt update && \ apt --yes --no-install-recommends install python3 python3-pip python3-cryptography python3-six python3-yaml python3-click python3-markdown python3-requests python3-requests-oauthlib \ sqlite3 iputils-ping util-linux dumb-init && \ - pip3 --no-cache-dir install apprise==0.9.8 && \ + pip3 --no-cache-dir install apprise==0.9.8.3 && \ rm -rf /var/lib/apt/lists/* # Install cloudflared From 26230a3d3afae2df7574cdb28d3ea52c8053501d Mon Sep 17 00:00:00 2001 From: Louis Lam <louislam@users.noreply.github.com> Date: Fri, 29 Apr 2022 13:52:14 +0800 Subject: [PATCH 067/124] Update to 1.15.1 --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index a3c720c1..06c2a671 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "uptime-kuma", - "version": "1.15.0", + "version": "1.15.1", "license": "MIT", "repository": { "type": "git", @@ -39,7 +39,7 @@ "build-docker-nightly-alpine": "docker buildx build -f docker/dockerfile-alpine --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:nightly-alpine --target nightly . --push", "build-docker-nightly-amd64": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:nightly-amd64 --target nightly . --push --progress plain", "upload-artifacts": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:upload-artifact --build-arg VERSION --build-arg GITHUB_TOKEN --target upload-artifact . --progress plain", - "setup": "git checkout 1.15.0 && npm ci --production && npm run download-dist", + "setup": "git checkout 1.15.1 && npm ci --production && npm run download-dist", "download-dist": "node extra/download-dist.js", "mark-as-nightly": "node extra/mark-as-nightly.js", "reset-password": "node extra/reset-password.js", From 45da7c543193d6a355305830b1c9c8122b030941 Mon Sep 17 00:00:00 2001 From: AnnAngela-work <naganjue@vip.qq.com> Date: Fri, 29 Apr 2022 20:17:15 +0800 Subject: [PATCH 068/124] Improve translation work --- src/components/notifications/ClickSendSMS.vue | 12 ++--- src/components/notifications/Line.vue | 2 +- src/components/notifications/Octopush.vue | 10 ++-- src/components/notifications/PromoSMS.vue | 4 +- src/components/notifications/Pushover.vue | 45 +++++++++-------- src/components/notifications/Pushy.vue | 4 +- src/components/notifications/TechulusPush.vue | 2 +- src/components/settings/About.vue | 4 +- src/languages/en.js | 50 +++++++++++++++++++ src/languages/zh-CN.js | 50 +++++++++++++++++++ src/layouts/Layout.vue | 2 +- src/pages/EditMonitor.vue | 2 +- src/pages/NotFound.vue | 12 ++--- src/pages/StatusPage.vue | 2 +- 14 files changed, 151 insertions(+), 50 deletions(-) diff --git a/src/components/notifications/ClickSendSMS.vue b/src/components/notifications/ClickSendSMS.vue index 4fbb2f41..09d3201c 100644 --- a/src/components/notifications/ClickSendSMS.vue +++ b/src/components/notifications/ClickSendSMS.vue @@ -1,12 +1,12 @@ <template> <div class="mb-3"> - <label for="clicksendsms-login" class="form-label">API Username</label> + <label for="clicksendsms-login" class="form-label">{{ $t("API Username") }}</label> <div class="form-text"> {{ $t("apiCredentials") }} <a href="http://dashboard.clicksend.com/account/subaccounts" target="_blank">{{ $t("here") }}</a> </div> <input id="clicksendsms-login" v-model="$parent.notification.clicksendsmsLogin" type="text" class="form-control" required> - <label for="clicksendsms-key" class="form-label">API Key</label> + <label for="clicksendsms-key" class="form-label">{{ $t("API Key") }}</label> <HiddenInput id="clicksendsms-key" v-model="$parent.notification.clicksendsmsPassword" :required="true" autocomplete="one-time-code"></HiddenInput> </div> <div class="mb-3"> @@ -16,15 +16,15 @@ </div> </div> <div class="mb-3"> - <label for="clicksendsms-to-number" class="form-label">Recipient Number</label> + <label for="clicksendsms-to-number" class="form-label">{{ $t("Recipient Number") }}</label> <input id="clicksendsms-to-number" v-model="$parent.notification.clicksendsmsToNumber" type="text" minlength="8" maxlength="14" class="form-control" required> </div> <div class="mb-3"> - <label for="clicksendsms-sender-name" class="form-label">From Name/Number - - <a href="https://help.clicksend.com/article/4kgj7krx00-what-is-a-sender-id-or-sender-number" target="_blank">More Info</a> + <label for="clicksendsms-sender-name" class="form-label">{{ $t("From Name/Number") }} - + <i18n-t tag="span" keypath="Read more:"><a href="https://help.clicksend.com/article/4kgj7krx00-what-is-a-sender-id-or-sender-number" target="_blank">https://help.clicksend.com/article/4kgj7krx00-what-is-a-sender-id-or-sender-number</a></i18n-t> </label> <input id="clicksendsms-sender-name" v-model="$parent.notification.clicksendsmsSenderName" type="text" minlength="3" maxlength="11" class="form-control"> - <div class="form-text">Leave blank to use a shared sender number.</div> + <div class="form-text">{{ $t("Leave blank to use a shared sender number.") }}</div> </div> </template> <script> diff --git a/src/components/notifications/Line.vue b/src/components/notifications/Line.vue index cb52c0c1..34ceb4ac 100644 --- a/src/components/notifications/Line.vue +++ b/src/components/notifications/Line.vue @@ -7,7 +7,7 @@ <b>{{ $t("Basic Settings") }}</b> </i18n-t> <div class="mb-3" style="margin-top: 12px;"> - <label for="line-user-id" class="form-label">User ID</label> + <label for="line-user-id" class="form-label">{{ $t("User ID") }}</label> <input id="line-user-id" v-model="$parent.notification.lineUserID" type="text" class="form-control" required> </div> <i18n-t tag="div" keypath="lineDevConsoleTo" class="form-text"> diff --git a/src/components/notifications/Octopush.vue b/src/components/notifications/Octopush.vue index 37629d39..7d5fe469 100644 --- a/src/components/notifications/Octopush.vue +++ b/src/components/notifications/Octopush.vue @@ -1,18 +1,18 @@ <template> <div class="mb-3"> - <label for="octopush-version" class="form-label">Octopush API Version</label> + <label for="octopush-version" class="form-label">{{ $t("Octopush API Version") }}</label> <select id="octopush-version" v-model="$parent.notification.octopushVersion" class="form-select"> - <option value="2">Octopush (endpoint: api.octopush.com)</option> - <option value="1">Legacy Octopush-DM (endpoint: www.octopush-dm.com)</option> + <option value="2">{{ $t("octopush") }} ({{ $t("endpoint") }}: api.octopush.com)</option> + <option value="1">{{ $t("Legacy Octopush-DM") }} ({{ $t("endpoint") }}: www.octopush-dm.com)</option> </select> <div class="form-text"> {{ $t("octopushLegacyHint") }} </div> </div> <div class="mb-3"> - <label for="octopush-key" class="form-label">API KEY</label> + <label for="octopush-key" class="form-label">{{ $t("octopushAPIKey") }}</label> <HiddenInput id="octopush-key" v-model="$parent.notification.octopushAPIKey" :required="true" autocomplete="one-time-code"></HiddenInput> - <label for="octopush-login" class="form-label">API LOGIN</label> + <label for="octopush-login" class="form-label">{{ $t("octopushLogin") }}</label> <input id="octopush-login" v-model="$parent.notification.octopushLogin" type="text" class="form-control" required> </div> <div class="mb-3"> diff --git a/src/components/notifications/PromoSMS.vue b/src/components/notifications/PromoSMS.vue index 61e61a93..c11ed559 100644 --- a/src/components/notifications/PromoSMS.vue +++ b/src/components/notifications/PromoSMS.vue @@ -1,8 +1,8 @@ <template> <div class="mb-3"> - <label for="promosms-login" class="form-label">API LOGIN</label> + <label for="promosms-login" class="form-label">{{$("promosmsLogin")}}</label> <input id="promosms-login" v-model="$parent.notification.promosmsLogin" type="text" class="form-control" required> - <label for="promosms-key" class="form-label">API PASSWORD</label> + <label for="promosms-key" class="form-label">{{$("promosmsPassword")}}</label> <HiddenInput id="promosms-key" v-model="$parent.notification.promosmsPassword" :required="true" autocomplete="one-time-code"></HiddenInput> </div> <div class="mb-3"> diff --git a/src/components/notifications/Pushover.vue b/src/components/notifications/Pushover.vue index ee6276dd..83261deb 100644 --- a/src/components/notifications/Pushover.vue +++ b/src/components/notifications/Pushover.vue @@ -18,28 +18,29 @@ </select> <label for="pushover-sound" class="form-label">{{ $t("Notification Sound") }}</label> <select id="pushover-sound" v-model="$parent.notification.pushoversounds" class="form-select"> - <option>pushover</option> - <option>bike</option> - <option>bugle</option> - <option>cashregister</option> - <option>classical</option> - <option>cosmic</option> - <option>falling</option> - <option>gamelan</option> - <option>incoming</option> - <option>intermission</option> - <option>mechanical</option> - <option>pianobar</option> - <option>siren</option> - <option>spacealarm</option> - <option>tugboat</option> - <option>alien</option> - <option>climb</option> - <option>persistent</option> - <option>echo</option> - <option>updown</option> - <option>vibrate</option> - <option>none</option> + <option>{{ $t("pushoversounds pushover") }}</option> + <option>{{ $t("pushoversounds bike") }}</option> + <option>{{ $t("pushoversounds bugle") }}</option> + <option>{{ $t("pushoversounds cashregister") }}</option> + <option>{{ $t("pushoversounds classical") }}</option> + <option>{{ $t("pushoversounds cosmic") }}</option> + <option>{{ $t("pushoversounds falling") }}</option> + <option>{{ $t("pushoversounds gamelan") }}</option> + <option>{{ $t("pushoversounds incoming") }}</option> + <option>{{ $t("pushoversounds intermission") }}</option> + <option>{{ $t("pushoversounds magic") }}</option> + <option>{{ $t("pushoversounds mechanical") }}</option> + <option>{{ $t("pushoversounds pianobar") }}</option> + <option>{{ $t("pushoversounds siren") }}</option> + <option>{{ $t("pushoversounds spacealarm") }}</option> + <option>{{ $t("pushoversounds tugboat") }}</option> + <option>{{ $t("pushoversounds alien") }}</option> + <option>{{ $t("pushoversounds climb") }}</option> + <option>{{ $t("pushoversounds persistent") }}</option> + <option>{{ $t("pushoversounds echo") }}</option> + <option>{{ $t("pushoversounds updown") }}</option> + <option>{{ $t("pushoversounds vibrate") }}</option> + <option>{{ $t("pushoversounds none") }}</option> </select> <div class="form-text"> <span style="color: red;"><sup>*</sup></span>{{ $t("Required") }} diff --git a/src/components/notifications/Pushy.vue b/src/components/notifications/Pushy.vue index 26f404d2..3537eb4f 100644 --- a/src/components/notifications/Pushy.vue +++ b/src/components/notifications/Pushy.vue @@ -1,11 +1,11 @@ <template> <div class="mb-3"> - <label for="pushy-app-token" class="form-label">API_KEY</label> + <label for="pushy-app-token" class="form-label">{{ $t("pushyAPIKey") }}</label> <HiddenInput id="pushy-app-token" v-model="$parent.notification.pushyAPIKey" :required="true" autocomplete="one-time-code"></HiddenInput> </div> <div class="mb-3"> - <label for="pushy-user-key" class="form-label">USER_TOKEN</label> + <label for="pushy-user-key" class="form-label">{{ $t("pushyToken") }}</label> <div class="input-group mb-3"> <HiddenInput id="pushy-user-key" v-model="$parent.notification.pushyToken" :required="true" autocomplete="one-time-code"></HiddenInput> </div> diff --git a/src/components/notifications/TechulusPush.vue b/src/components/notifications/TechulusPush.vue index 918f8be6..86d4e5fe 100644 --- a/src/components/notifications/TechulusPush.vue +++ b/src/components/notifications/TechulusPush.vue @@ -1,6 +1,6 @@ <template> <div class="mb-3"> - <label for="push-api-key" class="form-label">API_KEY</label> + <label for="push-api-key" class="form-label">{{ $t("API Key") }}</label> <HiddenInput id="push-api-key" v-model="$parent.notification.pushAPIKey" :required="true" autocomplete="one-time-code"></HiddenInput> </div> diff --git a/src/components/settings/About.vue b/src/components/settings/About.vue index b4d7b659..a71e38af 100644 --- a/src/components/settings/About.vue +++ b/src/components/settings/About.vue @@ -9,11 +9,11 @@ <div class="mt-1"> <div class="form-check"> - <label><input v-model="settings.checkUpdate" type="checkbox" @change="saveSettings()" /> Show update if available</label> + <label><input v-model="settings.checkUpdate" type="checkbox" @change="saveSettings()" /> {{ $t("Show update if available") }}</label> </div> <div class="form-check"> - <label><input v-model="settings.checkBeta" type="checkbox" :disabled="!settings.checkUpdate" @change="saveSettings()" /> Also check beta release</label> + <label><input v-model="settings.checkBeta" type="checkbox" :disabled="!settings.checkUpdate" @change="saveSettings()" /> {{ $t("Also check beta release") }}</label> </div> </div> </div> diff --git a/src/languages/en.js b/src/languages/en.js index ab73ce35..ad97cbef 100644 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -464,4 +464,54 @@ export default { "Domain Names": "Domain Names", signedInDisp: "Signed in as {0}", signedInDispDisabled: "Auth Disabled.", + "Certificate Expiry Notification": "Certificate Expiry Notification", + "API Username": "API Username", + "API Key": "API Key", + "Recipient Number": "Recipient Number", + "From Name/Number": "From Name/Number", + "Leave blank to use a shared sender number.": "Leave blank to use a shared sender number.", + "Octopush API Version": "Octopush API Version", + "Legacy Octopush-DM": "Legacy Octopush-DM", + "endpoint": "endpoint", + octopushAPIKey: "\"API key\" from HTTP API credentials in control panel", + octopushLogin: "\"Login\" from HTTP API credentials in control panel", + promosmsLogin: "API Login Name", + promosmsPassword: "API Password", + "pushoversounds pushover": "Pushover (default)", + "pushoversounds bike": "Bike", + "pushoversounds bugle": "Bugle", + "pushoversounds cashregister": "Cash Register", + "pushoversounds classical": "Classical", + "pushoversounds cosmic": "Cosmic", + "pushoversounds falling": "Falling", + "pushoversounds gamelan": "Gamelan", + "pushoversounds incoming": "Incoming", + "pushoversounds intermission": "Intermission", + "pushoversounds magic": "Magic", + "pushoversounds mechanical": "Mechanical", + "pushoversounds pianobar": "Piano Bar", + "pushoversounds siren": "Siren", + "pushoversounds spacealarm": "Space Alarm", + "pushoversounds tugboat": "Tug Boat", + "pushoversounds alien": "Alien Alarm (long)", + "pushoversounds climb": "Climb (long)", + "pushoversounds persistent": "Persistent (long)", + "pushoversounds echo": "Pushover Echo (long)", + "pushoversounds updown": "Up Down (long)", + "pushoversounds vibrate": "Vibrate Only", + "pushoversounds none": "None (silent)", + pushyAPIKey: "Secret API Key", + pushyToken: "Device token", + "Show update if available": "Show update if available", + "Also check beta release": "Also check beta release", + "Using a Reverse Proxy?": "Using a Reverse Proxy?", + "Check how to config it for WebSocket": "Check how to config it for WebSocket", + "Steam Game Server": "Steam Game Server", + "Most likely causes:": "Most likely causes:", + "The resource is no longer available.": "The resource is no longer available.", + "There might be a typing error in the address.": "There might be a typing error in the address.", + "What you can try:": "What you can try:", + "Retype the address.": "Retype the address.", + "Go back to the previous page.": "Go back to the previous page.", + "Coming Soon": "Coming Soon", }; diff --git a/src/languages/zh-CN.js b/src/languages/zh-CN.js index f50f50f1..9e5fcba1 100644 --- a/src/languages/zh-CN.js +++ b/src/languages/zh-CN.js @@ -469,4 +469,54 @@ export default { "Footer Text": "底部自定义文本", "Show Powered By": "显示 Powered By", "Domain Names": "域名", + "Certificate Expiry Notification": "证书到期时通知", + "API Username": "API 凭证 Username", + "API Key": "API 凭证 Key", + "Recipient Number": "收件人手机号码", + "From Name/Number": "发件人名称/手机号码", + "Leave blank to use a shared sender number.": "留空以使用平台共享的发件人手机号码", + "Octopush API Version": "Octopush API 版本", + "Legacy Octopush-DM": "旧版本 Octopush-DM", + "endpoint": "接入点", + octopushAPIKey: "控制台 HTTP API credentials 里的 \"API key\"", + octopushLogin: "控制台 HTTP API credentials 里的 \"Login\"", + promosmsLogin: "API 登录名", + promosmsPassword: "API 密码", + "pushoversounds pushover": "Pushover(默认)", + "pushoversounds bike": "Bike", + "pushoversounds bugle": "Bugle", + "pushoversounds cashregister": "Cash Register", + "pushoversounds classical": "Classical", + "pushoversounds cosmic": "Cosmic", + "pushoversounds falling": "Falling", + "pushoversounds gamelan": "Gamelan", + "pushoversounds incoming": "Incoming", + "pushoversounds intermission": "Intermission", + "pushoversounds magic": "Magic", + "pushoversounds mechanical": "Mechanical", + "pushoversounds pianobar": "Piano Bar", + "pushoversounds siren": "Siren", + "pushoversounds spacealarm": "Space Alarm", + "pushoversounds tugboat": "Tug Boat", + "pushoversounds alien": "Alien Alarm(长铃声)", + "pushoversounds climb": "Climb(长铃声)", + "pushoversounds persistent": "Persistent(长铃声)", + "pushoversounds echo": "Pushover Echo(长铃声)", + "pushoversounds updown": "Up Down(长铃声)", + "pushoversounds vibrate": "仅震动", + "pushoversounds none": "无(禁音)", + pushyAPIKey: "API 密钥", + pushyToken: "设备 Token", + "Show update if available": "有更新时通知", + "Also check beta release": "一并检查 Beta 版更新", + "Using a Reverse Proxy?": "正在使用反向代理?", + "Check how to config it for WebSocket": "查看如何将反向代理与 WebSocket 一起使用", + "Steam Game Server": "Steam 游戏服务器", + "Most likely causes:": "最可能的原因:", + "The resource is no longer available.": "您所请求的资源已不再可用;", + "There might be a typing error in the address.": "您输入的地址可能有误。", + "What you can try:": "您可以尝试以下操作:", + "Retype the address.": "重新输入地址;", + "Go back to the previous page.": "返回到上一页面。", + "Coming Soon": "即将推出", }; diff --git a/src/layouts/Layout.vue b/src/layouts/Layout.vue index 7f9598bf..38addb37 100644 --- a/src/layouts/Layout.vue +++ b/src/layouts/Layout.vue @@ -4,7 +4,7 @@ <div class="container-fluid"> {{ $root.connectionErrorMsg }} <div v-if="$root.showReverseProxyGuide"> - Using a Reverse Proxy? <a href="https://github.com/louislam/uptime-kuma/wiki/Reverse-Proxy" target="_blank">Check how to config it for WebSocket</a> + {{ $t("Using a Reverse Proxy?") }} <a href="https://github.com/louislam/uptime-kuma/wiki/Reverse-Proxy" target="_blank">{{ $t("Check how to config it for WebSocket") }}</a> </div> </div> </div> diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue index 39c114ad..0a753410 100644 --- a/src/pages/EditMonitor.vue +++ b/src/pages/EditMonitor.vue @@ -30,7 +30,7 @@ Push </option> <option value="steam"> - Steam Game Server + {{ $t("Steam Game Server") }} </option> <option value="mqtt"> MQTT diff --git a/src/pages/NotFound.vue b/src/pages/NotFound.vue index 16ba8a55..410c16a8 100644 --- a/src/pages/NotFound.vue +++ b/src/pages/NotFound.vue @@ -22,16 +22,16 @@ </div> <div class="guide"> - Most likely causes: + {{ $t("Most likely causes:") }} <ul> - <li>The resource is no longer available.</li> - <li>There might be a typing error in the address.</li> + <li>{{ $t("The resource is no longer available.") }}</li> + <li>{{ $t("There might be a typing error in the address.") }}</li> </ul> - What you can try:<br /> + {{ $t("What you can try:") }}<br /> <ul> - <li>Retype the address.</li> - <li><a href="#" class="go-back" @click="goBack()">Go back to the previous page.</a></li> + <li>{{ $t("Retype the address.") }}</li> + <li><a href="#" class="go-back" @click="goBack()">{{ $t("Go back to the previous page.") }}</a></li> </ul> </div> </div> diff --git a/src/pages/StatusPage.vue b/src/pages/StatusPage.vue index 8cda7ebb..546aa942 100644 --- a/src/pages/StatusPage.vue +++ b/src/pages/StatusPage.vue @@ -45,7 +45,7 @@ </div> <div v-if="false" class="my-3"> - <label for="password" class="form-label">{{ $t("Password") }} <sup>Coming Soon</sup></label> + <label for="password" class="form-label">{{ $t("Password") }} <sup>{{ $t("Coming Soon") }}</sup></label> <input id="password" v-model="config.password" disabled type="password" autocomplete="new-password" class="form-control"> </div> From 7dd5f5ea0dad64b7b65628a8f44b4fe2bbad9845 Mon Sep 17 00:00:00 2001 From: AnnAngela-work <naganjue@vip.qq.com> Date: Fri, 29 Apr 2022 20:26:56 +0800 Subject: [PATCH 069/124] Improve translation --- src/components/notifications/ClickSendSMS.vue | 7 +++---- src/languages/en.js | 1 + src/languages/zh-CN.js | 3 ++- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/components/notifications/ClickSendSMS.vue b/src/components/notifications/ClickSendSMS.vue index 09d3201c..dbd4d0aa 100644 --- a/src/components/notifications/ClickSendSMS.vue +++ b/src/components/notifications/ClickSendSMS.vue @@ -1,10 +1,9 @@ <template> <div class="mb-3"> <label for="clicksendsms-login" class="form-label">{{ $t("API Username") }}</label> - <div class="form-text"> - {{ $t("apiCredentials") }} + <i18n-t tag="div" class="form-text" keypath="wayToGetClickSendSMSToken"> <a href="http://dashboard.clicksend.com/account/subaccounts" target="_blank">{{ $t("here") }}</a> - </div> + </i18n-t> <input id="clicksendsms-login" v-model="$parent.notification.clicksendsmsLogin" type="text" class="form-control" required> <label for="clicksendsms-key" class="form-label">{{ $t("API Key") }}</label> <HiddenInput id="clicksendsms-key" v-model="$parent.notification.clicksendsmsPassword" :required="true" autocomplete="one-time-code"></HiddenInput> @@ -21,7 +20,7 @@ </div> <div class="mb-3"> <label for="clicksendsms-sender-name" class="form-label">{{ $t("From Name/Number") }} - - <i18n-t tag="span" keypath="Read more:"><a href="https://help.clicksend.com/article/4kgj7krx00-what-is-a-sender-id-or-sender-number" target="_blank">https://help.clicksend.com/article/4kgj7krx00-what-is-a-sender-id-or-sender-number</a></i18n-t> + <a href="https://help.clicksend.com/article/4kgj7krx00-what-is-a-sender-id-or-sender-number" target="_blank">{{ $t("Read more") }}</a> </label> <input id="clicksendsms-sender-name" v-model="$parent.notification.clicksendsmsSenderName" type="text" minlength="3" maxlength="11" class="form-control"> <div class="form-text">{{ $t("Leave blank to use a shared sender number.") }}</div> diff --git a/src/languages/en.js b/src/languages/en.js index ad97cbef..af6ec32b 100644 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -514,4 +514,5 @@ export default { "Retype the address.": "Retype the address.", "Go back to the previous page.": "Go back to the previous page.", "Coming Soon": "Coming Soon", + wayToGetClickSendSMSToken: "You can get API Username and API Key from {0} .", }; diff --git a/src/languages/zh-CN.js b/src/languages/zh-CN.js index 9e5fcba1..691ec120 100644 --- a/src/languages/zh-CN.js +++ b/src/languages/zh-CN.js @@ -441,7 +441,7 @@ export default { "No Proxy": "无代理", "HTTP Basic Auth": "HTTP 基础身份验证", "New Status Page": "新的状态页", - "Page Not Found": "状态页未找到", + "Page Not Found": "未找到该页面", "Reverse Proxy": "反向代理", "Subject:": "颁发给:", "Valid To:": "有效期至:", @@ -519,4 +519,5 @@ export default { "Retype the address.": "重新输入地址;", "Go back to the previous page.": "返回到上一页面。", "Coming Soon": "即将推出", + wayToGetClickSendSMSToken: "您可以从 {0} 获取 API 凭证 Username 和 凭证 Key。", }; From e82fc1df61f800098315f5937e667ae810e523b9 Mon Sep 17 00:00:00 2001 From: AnnAngela-work <naganjue@vip.qq.com> Date: Fri, 29 Apr 2022 20:31:36 +0800 Subject: [PATCH 070/124] Match up en lang file --- src/languages/zh-CN.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/languages/zh-CN.js b/src/languages/zh-CN.js index 691ec120..52050a46 100644 --- a/src/languages/zh-CN.js +++ b/src/languages/zh-CN.js @@ -88,7 +88,7 @@ export default { Dark: "黑暗", Auto: "自动", "Theme - Heartbeat Bar": "主题 - 心跳栏", - Normal: "正常", // 此处还供 Gorush 的通知优先级功能使用,不应翻译为“正常显示” + Normal: "正常", Bottom: "靠下", None: "不显示", Timezone: "时区", @@ -398,11 +398,9 @@ export default { Invalid: "无效", AccessKeyId: "AccessKey ID", SecretAccessKey: "AccessKey Secret", - /* 以下为阿里云短信服务 API Dysms#SendSms 的参数 */ PhoneNumbers: "PhoneNumbers", TemplateCode: "TemplateCode", SignName: "SignName", - /* 以上为阿里云短信服务 API Dysms#SendSms 的参数 */ "Bark Endpoint": "Bark 接入点", "Device Token": "Apple Device Token", Platform: "平台", @@ -477,7 +475,7 @@ export default { "Leave blank to use a shared sender number.": "留空以使用平台共享的发件人手机号码", "Octopush API Version": "Octopush API 版本", "Legacy Octopush-DM": "旧版本 Octopush-DM", - "endpoint": "接入点", + endpoint: "接入点", octopushAPIKey: "控制台 HTTP API credentials 里的 \"API key\"", octopushLogin: "控制台 HTTP API credentials 里的 \"Login\"", promosmsLogin: "API 登录名", @@ -520,4 +518,6 @@ export default { "Go back to the previous page.": "返回到上一页面。", "Coming Soon": "即将推出", wayToGetClickSendSMSToken: "您可以从 {0} 获取 API 凭证 Username 和 凭证 Key。", + signedInDisp: "当前用户: {0}", + signedInDispDisabled: "已禁用身份验证", }; From 65ea2e6aeb1ecb1bb626be36f176e48a78e9408d Mon Sep 17 00:00:00 2001 From: AnnAngela <naganjue@vip.qq.com> Date: Fri, 29 Apr 2022 21:42:48 +0800 Subject: [PATCH 071/124] Update src/components/notifications/PromoSMS.vue Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com> --- src/components/notifications/PromoSMS.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/notifications/PromoSMS.vue b/src/components/notifications/PromoSMS.vue index c11ed559..29192aab 100644 --- a/src/components/notifications/PromoSMS.vue +++ b/src/components/notifications/PromoSMS.vue @@ -1,6 +1,6 @@ <template> <div class="mb-3"> - <label for="promosms-login" class="form-label">{{$("promosmsLogin")}}</label> + <label for="promosms-login" class="form-label">{{ $("promosmsLogin") }}</label> <input id="promosms-login" v-model="$parent.notification.promosmsLogin" type="text" class="form-control" required> <label for="promosms-key" class="form-label">{{$("promosmsPassword")}}</label> <HiddenInput id="promosms-key" v-model="$parent.notification.promosmsPassword" :required="true" autocomplete="one-time-code"></HiddenInput> From 5a069b278deafc766e37b1ef989920e993bf1c58 Mon Sep 17 00:00:00 2001 From: AnnAngela <naganjue@vip.qq.com> Date: Fri, 29 Apr 2022 21:42:54 +0800 Subject: [PATCH 072/124] Update src/components/notifications/PromoSMS.vue Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com> --- src/components/notifications/PromoSMS.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/notifications/PromoSMS.vue b/src/components/notifications/PromoSMS.vue index 29192aab..91b4d96d 100644 --- a/src/components/notifications/PromoSMS.vue +++ b/src/components/notifications/PromoSMS.vue @@ -2,7 +2,7 @@ <div class="mb-3"> <label for="promosms-login" class="form-label">{{ $("promosmsLogin") }}</label> <input id="promosms-login" v-model="$parent.notification.promosmsLogin" type="text" class="form-control" required> - <label for="promosms-key" class="form-label">{{$("promosmsPassword")}}</label> + <label for="promosms-key" class="form-label">{{ $("promosmsPassword") }}</label> <HiddenInput id="promosms-key" v-model="$parent.notification.promosmsPassword" :required="true" autocomplete="one-time-code"></HiddenInput> </div> <div class="mb-3"> From b9e72b9645765b364f1b6d64d291ab1e979c3fcf Mon Sep 17 00:00:00 2001 From: GOGOsu <m@mmmm.mn> Date: Sat, 30 Apr 2022 05:56:10 +0800 Subject: [PATCH 073/124] Update aliyun-sms.js MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit aliyun-sms.js: escape more characters than encodeURIComponent see https://help.aliyun.com/document_detail/315526.html 字符A~Z、a~z、0~9以及字符-、_、.、~不编码。对其它ASCII码字符进行编码。 --- server/notification-providers/aliyun-sms.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/server/notification-providers/aliyun-sms.js b/server/notification-providers/aliyun-sms.js index fc281511..325d2214 100644 --- a/server/notification-providers/aliyun-sms.js +++ b/server/notification-providers/aliyun-sms.js @@ -92,9 +92,20 @@ class AliyunSMS extends NotificationProvider { let key = oa[i]; param2[key] = param[key]; } + + let moreEscapesTable = function(m) { + return { + "!": "%21", + "*": "%2A", + "'": "%27", + "(": "%28", + ")": "%29" + }[m] + }; for (let key in param2) { - data.push(`${encodeURIComponent(key)}=${encodeURIComponent(param2[key])}`); + let value = encodeURIComponent(param2[key]).replace(/[!*'()]/g, moreEscapesTable); + data.push(`${encodeURIComponent(key)}=${value}`); } let StringToSign = `POST&${encodeURIComponent("/")}&${encodeURIComponent(data.join("&"))}`; From ae2c49a72904aed503959b4cf7b595b53c5b4bab Mon Sep 17 00:00:00 2001 From: GOGOsu <m@mmmm.mn> Date: Sat, 30 Apr 2022 06:28:16 +0800 Subject: [PATCH 074/124] Update aliyun-sms.js --- server/notification-providers/aliyun-sms.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/server/notification-providers/aliyun-sms.js b/server/notification-providers/aliyun-sms.js index 325d2214..65843ebe 100644 --- a/server/notification-providers/aliyun-sms.js +++ b/server/notification-providers/aliyun-sms.js @@ -92,15 +92,15 @@ class AliyunSMS extends NotificationProvider { let key = oa[i]; param2[key] = param[key]; } - - let moreEscapesTable = function(m) { + + let moreEscapesTable = function (m) { return { - "!": "%21", - "*": "%2A", - "'": "%27", - "(": "%28", + "!": "%21", + "*": "%2A", + "'": "%27", + "(": "%28", ")": "%29" - }[m] + }[m]; }; for (let key in param2) { From c114c053d633243f6343e52360c2e5a4b8e6f062 Mon Sep 17 00:00:00 2001 From: Adam Stachowicz <adam.stachowicz@fingo.info> Date: Sat, 30 Apr 2022 03:51:14 +0200 Subject: [PATCH 075/124] Fix ESLint warnings and errors --- src/components/CountUp.vue | 5 ++++- src/components/Datetime.vue | 5 ++++- src/components/Status.vue | 5 ++++- src/components/Uptime.vue | 10 ++++++++-- src/pages/StatusPage.vue | 40 ++++++++++++++++++------------------- 5 files changed, 40 insertions(+), 25 deletions(-) diff --git a/src/components/CountUp.vue b/src/components/CountUp.vue index 41edc4a0..df1d1ac6 100644 --- a/src/components/CountUp.vue +++ b/src/components/CountUp.vue @@ -10,7 +10,10 @@ import { sleep } from "../util.ts"; export default { props: { - value: [ String, Number ], + value: { + type: [ String, Number ], + default: 0, + }, time: { type: Number, default: 0.3, diff --git a/src/components/Datetime.vue b/src/components/Datetime.vue index ed38c434..fa68d02c 100644 --- a/src/components/Datetime.vue +++ b/src/components/Datetime.vue @@ -13,7 +13,10 @@ dayjs.extend(relativeTime); export default { props: { - value: String, + value: { + type: String, + default: null, + }, dateOnly: { type: Boolean, default: false, diff --git a/src/components/Status.vue b/src/components/Status.vue index a3916adc..1985d851 100644 --- a/src/components/Status.vue +++ b/src/components/Status.vue @@ -5,7 +5,10 @@ <script> export default { props: { - status: Number, + status: { + type: Number, + default: 0, + } }, computed: { diff --git a/src/components/Uptime.vue b/src/components/Uptime.vue index 3a63c6d0..487d62b7 100644 --- a/src/components/Uptime.vue +++ b/src/components/Uptime.vue @@ -5,8 +5,14 @@ <script> export default { props: { - monitor: Object, - type: String, + monitor: { + type: Object, + default: null, + }, + type: { + type: String, + default: null, + }, pill: { type: Boolean, default: false, diff --git a/src/pages/StatusPage.vue b/src/pages/StatusPage.vue index 8cda7ebb..34c71e06 100644 --- a/src/pages/StatusPage.vue +++ b/src/pages/StatusPage.vue @@ -104,15 +104,16 @@ <!-- Uploader --> <!-- url="/api/status-page/upload-logo" --> - <ImageCropUpload v-model="showImageCropUpload" - field="img" - :width="128" - :height="128" - :langType="$i18n.locale" - img-format="png" - :noCircle="true" - :noSquare="false" - @crop-success="cropSuccess" + <ImageCropUpload + v-model="showImageCropUpload" + field="img" + :width="128" + :height="128" + :langType="$i18n.locale" + img-format="png" + :noCircle="true" + :noSquare="false" + @crop-success="cropSuccess" /> <!-- Title --> @@ -273,7 +274,7 @@ {{ $t("deleteStatusPageMsg") }} </Confirm> - <component is="style" v-if="config.customCSS" type="text/css"> + <component :is="style" v-if="config.customCSS" type="text/css"> {{ config.customCSS }} </component> </div> @@ -281,22 +282,21 @@ <script> import axios from "axios"; -import PublicGroupList from "../components/PublicGroupList.vue"; -import ImageCropUpload from "vue-image-crop-upload"; -import { STATUS_PAGE_ALL_DOWN, STATUS_PAGE_ALL_UP, STATUS_PAGE_PARTIAL_DOWN, UP } from "../util.ts"; -import { useToast } from "vue-toastification"; import dayjs from "dayjs"; import Favico from "favico.js"; -import { getResBaseURL } from "../util-frontend"; -import Confirm from "../components/Confirm.vue"; -// import Prism Editor -import { PrismEditor } from "vue-prism-editor"; -import "vue-prism-editor/dist/prismeditor.min.css"; // import the styles somewhere - // import highlighting library (you can use any library you want just return html string) import { highlight, languages } from "prismjs/components/prism-core"; import "prismjs/components/prism-css"; import "prismjs/themes/prism-tomorrow.css"; // import syntax highlighting styles +import ImageCropUpload from "vue-image-crop-upload"; +// import Prism Editor +import { PrismEditor } from "vue-prism-editor"; +import "vue-prism-editor/dist/prismeditor.min.css"; // import the styles somewhere +import { useToast } from "vue-toastification"; +import Confirm from "../components/Confirm.vue"; +import PublicGroupList from "../components/PublicGroupList.vue"; +import { getResBaseURL } from "../util-frontend"; +import { STATUS_PAGE_ALL_DOWN, STATUS_PAGE_ALL_UP, STATUS_PAGE_PARTIAL_DOWN, UP } from "../util.ts"; const toast = useToast(); From 2347a01f7c6e0362c4c92081836c930d202dd9ff Mon Sep 17 00:00:00 2001 From: GOGOsu <m@mmmm.mn> Date: Sat, 30 Apr 2022 10:42:59 +0800 Subject: [PATCH 076/124] Update aliyun-sms.js Add comments for the changed code. --- server/notification-providers/aliyun-sms.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/server/notification-providers/aliyun-sms.js b/server/notification-providers/aliyun-sms.js index 65843ebe..11575df3 100644 --- a/server/notification-providers/aliyun-sms.js +++ b/server/notification-providers/aliyun-sms.js @@ -92,6 +92,10 @@ class AliyunSMS extends NotificationProvider { let key = oa[i]; param2[key] = param[key]; } + + // Escape more characters than encodeURIComponent does. + // For generating Aliyun signature, all characters except A-Za-z0-9~-._ are encoded. + // See https://help.aliyun.com/document_detail/315526.html let moreEscapesTable = function (m) { return { From 369477b4b920e14b177b1ab9f2779d1a58aa4d86 Mon Sep 17 00:00:00 2001 From: GOGOsu <m@mmmm.mn> Date: Sat, 30 Apr 2022 10:45:38 +0800 Subject: [PATCH 077/124] Update aliyun-sms.js --- server/notification-providers/aliyun-sms.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/notification-providers/aliyun-sms.js b/server/notification-providers/aliyun-sms.js index 11575df3..2574cd3c 100644 --- a/server/notification-providers/aliyun-sms.js +++ b/server/notification-providers/aliyun-sms.js @@ -92,7 +92,7 @@ class AliyunSMS extends NotificationProvider { let key = oa[i]; param2[key] = param[key]; } - + // Escape more characters than encodeURIComponent does. // For generating Aliyun signature, all characters except A-Za-z0-9~-._ are encoded. // See https://help.aliyun.com/document_detail/315526.html From 73e38a13d2e1bf19c64edd063fb77fe323f6aa9b Mon Sep 17 00:00:00 2001 From: GOGOsu <m@mmmm.mn> Date: Sat, 30 Apr 2022 21:08:35 +0800 Subject: [PATCH 078/124] Update server/notification-providers/aliyun-sms.js Co-authored-by: Adam Stachowicz <saibamenppl@gmail.com> --- server/notification-providers/aliyun-sms.js | 1 - 1 file changed, 1 deletion(-) diff --git a/server/notification-providers/aliyun-sms.js b/server/notification-providers/aliyun-sms.js index 2574cd3c..79f0dd30 100644 --- a/server/notification-providers/aliyun-sms.js +++ b/server/notification-providers/aliyun-sms.js @@ -96,7 +96,6 @@ class AliyunSMS extends NotificationProvider { // Escape more characters than encodeURIComponent does. // For generating Aliyun signature, all characters except A-Za-z0-9~-._ are encoded. // See https://help.aliyun.com/document_detail/315526.html - let moreEscapesTable = function (m) { return { "!": "%21", From 9f8b3151d8587113d1ea19e93dbd6181238e419b Mon Sep 17 00:00:00 2001 From: Louis Lam <louislam@users.noreply.github.com> Date: Sat, 30 Apr 2022 21:36:00 +0800 Subject: [PATCH 079/124] Update server/util-server.js Co-authored-by: Matthew Nickson <mnickson@sidingsmedia.com> --- 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 <louislam@users.noreply.github.com> Date: Sat, 30 Apr 2022 21:36:07 +0800 Subject: [PATCH 080/124] Update server/util-server.js Co-authored-by: Matthew Nickson <mnickson@sidingsmedia.com> --- 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 db50ba91cc12e63341fdfc9eb06d6131dd75c4f0 Mon Sep 17 00:00:00 2001 From: Louis Lam <louislam@users.noreply.github.com> Date: Sat, 30 Apr 2022 21:44:03 +0800 Subject: [PATCH 081/124] Fix #1593 --- src/pages/EditMonitor.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue index 39c114ad..43f34527 100644 --- a/src/pages/EditMonitor.vue +++ b/src/pages/EditMonitor.vue @@ -421,7 +421,7 @@ export default { }, pushURL() { - return this.$root.baseURL + "/api/push/" + this.monitor.pushToken + "?status=true&msg=OK&ping="; + return this.$root.baseURL + "/api/push/" + this.monitor.pushToken + "?status=up&msg=OK&ping="; }, bodyPlaceholder() { From 272956025cb6b323c1d214bac85c7f2b5a0ede09 Mon Sep 17 00:00:00 2001 From: Nelson Chan <chakflying@hotmail.com> Date: Sun, 1 May 2022 05:18:08 +0800 Subject: [PATCH 082/124] Fix: Fix chart error on switch back to recent --- src/components/PingChart.vue | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/PingChart.vue b/src/components/PingChart.vue index d4f94a19..eb907e2d 100644 --- a/src/components/PingChart.vue +++ b/src/components/PingChart.vue @@ -218,7 +218,6 @@ export default { // Update chart data when the selected chart period changes chartPeriodHrs: function (newPeriod) { if (newPeriod === "0") { - newPeriod = null; this.heartbeatList = null; this.$root.storage().removeItem(`chart-period-${this.monitorId}`); } else { @@ -241,7 +240,7 @@ export default { // And mirror latest change to this.heartbeatList this.$watch(() => this.$root.heartbeatList[this.monitorId], (heartbeatList) => { - if (this.chartPeriodHrs !== 0) { + if (this.chartPeriodHrs !== "0") { const newBeat = heartbeatList.at(-1); if (newBeat && dayjs.utc(newBeat.time) > dayjs.utc(this.heartbeatList.at(-1)?.time)) { this.heartbeatList.push(heartbeatList.at(-1)); From 53b98ad3e488611bc426547c9de271411e6942b7 Mon Sep 17 00:00:00 2001 From: Louis Lam <louislam@users.noreply.github.com> Date: Sun, 1 May 2022 12:10:47 +0800 Subject: [PATCH 083/124] Add more comment for aliyun-sms fix --- server/notification-providers/aliyun-sms.js | 1 + 1 file changed, 1 insertion(+) diff --git a/server/notification-providers/aliyun-sms.js b/server/notification-providers/aliyun-sms.js index 79f0dd30..fa73ffb1 100644 --- a/server/notification-providers/aliyun-sms.js +++ b/server/notification-providers/aliyun-sms.js @@ -96,6 +96,7 @@ class AliyunSMS extends NotificationProvider { // Escape more characters than encodeURIComponent does. // For generating Aliyun signature, all characters except A-Za-z0-9~-._ are encoded. // See https://help.aliyun.com/document_detail/315526.html + // This encoding methods as known as RFC 3986 (https://tools.ietf.org/html/rfc3986) let moreEscapesTable = function (m) { return { "!": "%21", From 254a6bfd36a065475a0ab44a37cb511f0409df78 Mon Sep 17 00:00:00 2001 From: Louis Lam <louislam@users.noreply.github.com> Date: Sun, 1 May 2022 12:23:28 +0800 Subject: [PATCH 084/124] [CI] Run check linters first --- .github/workflows/auto-test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/auto-test.yml b/.github/workflows/auto-test.yml index c0efc64a..1b6a8e04 100644 --- a/.github/workflows/auto-test.yml +++ b/.github/workflows/auto-test.yml @@ -11,6 +11,7 @@ on: jobs: auto-test: + needs: [ check-linters ] runs-on: ${{ matrix.os }} strategy: From 63c8d24d6f2322121f50628a26e652605aef5c86 Mon Sep 17 00:00:00 2001 From: Louis Lam <louislam@users.noreply.github.com> Date: Sun, 1 May 2022 12:32:06 +0800 Subject: [PATCH 085/124] As legacy-peer-deps is specified in .npmrc, `install-legacy` and `update-legacy` are not actually needed. --- .github/workflows/auto-test.yml | 4 ++-- package.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/auto-test.yml b/.github/workflows/auto-test.yml index 1b6a8e04..82451f37 100644 --- a/.github/workflows/auto-test.yml +++ b/.github/workflows/auto-test.yml @@ -29,7 +29,7 @@ jobs: with: node-version: ${{ matrix.node }} cache: 'npm' - - run: npm run install-legacy + - run: npm install - run: npm run build - run: npm test env: @@ -47,5 +47,5 @@ jobs: with: node-version: 16 cache: 'npm' - - run: npm run install-legacy + - run: npm install - run: npm run lint diff --git a/package.json b/package.json index 06c2a671..6b21096b 100644 --- a/package.json +++ b/package.json @@ -10,8 +10,8 @@ "node": "14.* || >=16.*" }, "scripts": { - "install-legacy": "npm install --legacy-peer-deps", - "update-legacy": "npm update --legacy-peer-deps", + "install-legacy": "npm install", + "update-legacy": "npm update", "lint:js": "eslint --ext \".js,.vue\" --ignore-path .gitignore .", "lint-fix:js": "eslint --ext \".js,.vue\" --fix --ignore-path .gitignore .", "lint:style": "stylelint \"**/*.{vue,css,scss}\" --ignore-path .gitignore", From 02a8147f22c7200f5f2d35e2c29f6dcc55084831 Mon Sep 17 00:00:00 2001 From: Louis Lam <louislam@users.noreply.github.com> Date: Sun, 1 May 2022 17:31:58 +0800 Subject: [PATCH 086/124] Remove undefined variable forceShowContent --- src/layouts/Layout.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/layouts/Layout.vue b/src/layouts/Layout.vue index 7f9598bf..c7076221 100644 --- a/src/layouts/Layout.vue +++ b/src/layouts/Layout.vue @@ -71,7 +71,7 @@ </header> <main> - <router-view v-if="$root.loggedIn || forceShowContent" /> + <router-view v-if="$root.loggedIn" /> <Login v-if="! $root.loggedIn && $root.allowLoginDialog" /> </main> From 410805052e48776dd83bf3973619e38f61030c35 Mon Sep 17 00:00:00 2001 From: Louis Lam <louislam@users.noreply.github.com> Date: Sun, 1 May 2022 17:46:43 +0800 Subject: [PATCH 087/124] Log this.chartPeriodHrs --- src/components/PingChart.vue | 5 ++++- src/util.js | 2 +- src/util.ts | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/components/PingChart.vue b/src/components/PingChart.vue index eb907e2d..b35d999c 100644 --- a/src/components/PingChart.vue +++ b/src/components/PingChart.vue @@ -24,7 +24,7 @@ import timezone from "dayjs/plugin/timezone"; import utc from "dayjs/plugin/utc"; import { LineChart } from "vue-chart-3"; import { useToast } from "vue-toastification"; -import { DOWN } from "../util.ts"; +import { DOWN, log } from "../util.ts"; dayjs.extend(utc); dayjs.extend(timezone); @@ -240,6 +240,9 @@ export default { // And mirror latest change to this.heartbeatList this.$watch(() => this.$root.heartbeatList[this.monitorId], (heartbeatList) => { + + log.debug("ping_chart", `this.chartPeriodHrs type ${typeof this.chartPeriodHrs}, value: ${this.chartPeriodHrs}`); + if (this.chartPeriodHrs !== "0") { const newBeat = heartbeatList.at(-1); if (newBeat && dayjs.utc(newBeat.time) > dayjs.utc(this.heartbeatList.at(-1)?.time)) { diff --git a/src/util.js b/src/util.js index b1f55201..ee6095e0 100644 --- a/src/util.js +++ b/src/util.js @@ -102,7 +102,7 @@ class Logger { } else if (level === "DEBUG") { if (exports.isDev) { - console.debug(formattedMessage); + console.log(formattedMessage); } } else { diff --git a/src/util.ts b/src/util.ts index 75abd662..057090b7 100644 --- a/src/util.ts +++ b/src/util.ts @@ -113,7 +113,7 @@ class Logger { console.error(formattedMessage); } else if (level === "DEBUG") { if (isDev) { - console.debug(formattedMessage); + console.log(formattedMessage); } } else { console.log(formattedMessage); From a12dffd1bc6bb9955f1d66ab1f9c1d269c051bb5 Mon Sep 17 00:00:00 2001 From: Louis Lam <louislam@users.noreply.github.com> Date: Sun, 1 May 2022 17:56:42 +0800 Subject: [PATCH 088/124] Fallback to eqeq for PingChart.vue --- src/components/PingChart.vue | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/PingChart.vue b/src/components/PingChart.vue index b35d999c..67bdc00c 100644 --- a/src/components/PingChart.vue +++ b/src/components/PingChart.vue @@ -217,7 +217,9 @@ export default { watch: { // Update chart data when the selected chart period changes chartPeriodHrs: function (newPeriod) { - if (newPeriod === "0") { + + // eslint-disable-next-line eqeqeq + if (newPeriod == "0") { this.heartbeatList = null; this.$root.storage().removeItem(`chart-period-${this.monitorId}`); } else { @@ -243,7 +245,8 @@ export default { log.debug("ping_chart", `this.chartPeriodHrs type ${typeof this.chartPeriodHrs}, value: ${this.chartPeriodHrs}`); - if (this.chartPeriodHrs !== "0") { + // eslint-disable-next-line eqeqeq + if (this.chartPeriodHrs != "0") { const newBeat = heartbeatList.at(-1); if (newBeat && dayjs.utc(newBeat.time) > dayjs.utc(this.heartbeatList.at(-1)?.time)) { this.heartbeatList.push(heartbeatList.at(-1)); From 000cbeb0ce1921f36314ea904fcf5b23e61bd179 Mon Sep 17 00:00:00 2001 From: Louis Lam <louislam@users.noreply.github.com> Date: Sun, 1 May 2022 18:25:52 +0800 Subject: [PATCH 089/124] Lower check-linters node version to 14, add Node.js 18, set timeout 15mins for each job --- .github/workflows/auto-test.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/auto-test.yml b/.github/workflows/auto-test.yml index 82451f37..a7a01684 100644 --- a/.github/workflows/auto-test.yml +++ b/.github/workflows/auto-test.yml @@ -13,11 +13,12 @@ jobs: auto-test: needs: [ check-linters ] runs-on: ${{ matrix.os }} + timeout-minutes: 15 strategy: matrix: os: [macos-latest, ubuntu-latest, windows-latest] - node: [14, 16, 17] + node: [ 14, 16, 17, 18 ] # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ steps: @@ -45,7 +46,7 @@ jobs: - name: Use Node.js LTS uses: actions/setup-node@v3 with: - node-version: 16 + node-version: 14 cache: 'npm' - run: npm install - run: npm run lint From 15820c69379b66ad032524223871b9c0f9b1a637 Mon Sep 17 00:00:00 2001 From: Louis Lam <louislam@users.noreply.github.com> Date: Sun, 1 May 2022 19:45:00 +0800 Subject: [PATCH 090/124] Update SQLite --- package-lock.json | 18 +++++++++--------- package.json | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9bedab9f..729a1c4c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,19 +1,19 @@ { "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", "@fortawesome/free-regular-svg-icons": "~5.15.4", "@fortawesome/free-solid-svg-icons": "~5.15.4", "@fortawesome/vue-fontawesome": "~3.0.0-5", - "@louislam/sqlite3": "~15.0.3", + "@louislam/sqlite3": "~15.0.6", "@popperjs/core": "~2.10.2", "args-parser": "~1.3.0", "axios": "~0.26.1", @@ -2697,9 +2697,9 @@ } }, "node_modules/@louislam/sqlite3": { - "version": "15.0.3", - "resolved": "https://registry.npmjs.org/@louislam/sqlite3/-/sqlite3-15.0.3.tgz", - "integrity": "sha512-rCH6PIaa+TgBzpTRnqBKUa4H/5G2hIk5ukYK5rXxK+8hVGykRin3UMGzGejrPzIKzDnZGByIF0XD4ndi6lprRQ==", + "version": "15.0.6", + "resolved": "https://registry.npmjs.org/@louislam/sqlite3/-/sqlite3-15.0.6.tgz", + "integrity": "sha512-+HF/4OEy+yakYzJlSPJbLDtf499t0s0eaglXC9y3Oa9OBZ+dKAaTW5+Ft1RCvfUJLFw/oyYjHtMsg9V+7NT05g==", "hasInstallScript": true, "dependencies": { "@mapbox/node-pre-gyp": "^1.0.0", @@ -18665,9 +18665,9 @@ } }, "@louislam/sqlite3": { - "version": "15.0.3", - "resolved": "https://registry.npmjs.org/@louislam/sqlite3/-/sqlite3-15.0.3.tgz", - "integrity": "sha512-rCH6PIaa+TgBzpTRnqBKUa4H/5G2hIk5ukYK5rXxK+8hVGykRin3UMGzGejrPzIKzDnZGByIF0XD4ndi6lprRQ==", + "version": "15.0.6", + "resolved": "https://registry.npmjs.org/@louislam/sqlite3/-/sqlite3-15.0.6.tgz", + "integrity": "sha512-+HF/4OEy+yakYzJlSPJbLDtf499t0s0eaglXC9y3Oa9OBZ+dKAaTW5+Ft1RCvfUJLFw/oyYjHtMsg9V+7NT05g==", "requires": { "@mapbox/node-pre-gyp": "^1.0.0", "node-addon-api": "^4.2.0", diff --git a/package.json b/package.json index 6b21096b..52e30e9e 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,7 @@ "@fortawesome/free-regular-svg-icons": "~5.15.4", "@fortawesome/free-solid-svg-icons": "~5.15.4", "@fortawesome/vue-fontawesome": "~3.0.0-5", - "@louislam/sqlite3": "~15.0.3", + "@louislam/sqlite3": "~15.0.6", "@popperjs/core": "~2.10.2", "args-parser": "~1.3.0", "axios": "~0.26.1", From 069d3765f0b582a35237857431bf47a11456946f Mon Sep 17 00:00:00 2001 From: Adam Stachowicz <adam.stachowicz@fingo.info> Date: Mon, 2 May 2022 01:13:17 +0200 Subject: [PATCH 091/124] Revert change for StatusPage --- src/pages/StatusPage.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/StatusPage.vue b/src/pages/StatusPage.vue index 34c71e06..1a64d0fa 100644 --- a/src/pages/StatusPage.vue +++ b/src/pages/StatusPage.vue @@ -274,7 +274,7 @@ {{ $t("deleteStatusPageMsg") }} </Confirm> - <component :is="style" v-if="config.customCSS" type="text/css"> + <component is="style" v-if="config.customCSS" type="text/css"> {{ config.customCSS }} </component> </div> From 5c852db1cfc809063d88f86f5bc4d9fc1164a298 Mon Sep 17 00:00:00 2001 From: Adam Stachowicz <adam.stachowicz@fingo.info> Date: Mon, 2 May 2022 01:23:05 +0200 Subject: [PATCH 092/124] Fix actions/setup-node task name Addendum to 000cbeb0ce1921f36314ea904fcf5b23e61bd179 --- .github/workflows/auto-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/auto-test.yml b/.github/workflows/auto-test.yml index a7a01684..d26b76e6 100644 --- a/.github/workflows/auto-test.yml +++ b/.github/workflows/auto-test.yml @@ -43,7 +43,7 @@ jobs: - run: git config --global core.autocrlf false # Mainly for Windows - uses: actions/checkout@v3 - - name: Use Node.js LTS + - name: Use Node.js 14 uses: actions/setup-node@v3 with: node-version: 14 From 96536ae3919f0f927d4aa3aded49a5d7280a5a0f Mon Sep 17 00:00:00 2001 From: Louis Lam <louislam@users.noreply.github.com> Date: Mon, 2 May 2022 12:36:12 +0800 Subject: [PATCH 093/124] 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 <louislam@users.noreply.github.com> Date: Mon, 2 May 2022 13:32:19 +0800 Subject: [PATCH 094/124] 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<boolean>} - */ -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 <louislam@users.noreply.github.com> Date: Mon, 2 May 2022 13:36:35 +0800 Subject: [PATCH 095/124] 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"); From bc174c33257009cae257ccd303135ca2ebb54fbf Mon Sep 17 00:00:00 2001 From: Clemens Wolff <clemens@justamouse.com> Date: Mon, 2 May 2022 11:00:14 -0400 Subject: [PATCH 096/124] Extract child process args into variable --- server/notification-providers/apprise.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/notification-providers/apprise.js b/server/notification-providers/apprise.js index 2d795d4e..5e73a47c 100644 --- a/server/notification-providers/apprise.js +++ b/server/notification-providers/apprise.js @@ -6,7 +6,8 @@ class Apprise extends NotificationProvider { name = "apprise"; async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { - let s = childProcess.spawnSync("apprise", [ "-vv", "-b", msg, notification.appriseURL ]); + let args = [ "-vv", "-b", msg, notification.appriseURL ]; + let s = childProcess.spawnSync("apprise", args); let output = (s.stdout) ? s.stdout.toString() : "ERROR: maybe apprise not found"; From f9004bcbed4c8fe4d4ce17dd3c6dd0677bf3b49b Mon Sep 17 00:00:00 2001 From: Clemens Wolff <clemens@justamouse.com> Date: Mon, 2 May 2022 11:00:31 -0400 Subject: [PATCH 097/124] Add optional title to apprise notification --- server/notification-providers/apprise.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/server/notification-providers/apprise.js b/server/notification-providers/apprise.js index 5e73a47c..76e1e3c2 100644 --- a/server/notification-providers/apprise.js +++ b/server/notification-providers/apprise.js @@ -1,12 +1,23 @@ const NotificationProvider = require("./notification-provider"); const childProcess = require("child_process"); +/** + * If you use an apprise backend that requires the notification title to + * be set (such as for example messaging a Zulip Stream), you can use this + * environment variable to configure the title. + */ +const { APPRISE_NOTIFICATION_TITLE } = process.env; + class Apprise extends NotificationProvider { name = "apprise"; async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { let args = [ "-vv", "-b", msg, notification.appriseURL ]; + if (APPRISE_NOTIFICATION_TITLE) { + args.push("-t"); + args.push(APPRISE_NOTIFICATION_TITLE); + } let s = childProcess.spawnSync("apprise", args); let output = (s.stdout) ? s.stdout.toString() : "ERROR: maybe apprise not found"; From 4b9dc2890d078f46dc89860a61700d74674e7068 Mon Sep 17 00:00:00 2001 From: Clemens Wolff <clemens@justamouse.com> Date: Mon, 2 May 2022 11:16:08 -0400 Subject: [PATCH 098/124] Convert let to const --- server/notification-providers/apprise.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/notification-providers/apprise.js b/server/notification-providers/apprise.js index 76e1e3c2..acbc0567 100644 --- a/server/notification-providers/apprise.js +++ b/server/notification-providers/apprise.js @@ -13,14 +13,14 @@ class Apprise extends NotificationProvider { name = "apprise"; async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { - let args = [ "-vv", "-b", msg, notification.appriseURL ]; + const args = [ "-vv", "-b", msg, notification.appriseURL ]; if (APPRISE_NOTIFICATION_TITLE) { args.push("-t"); args.push(APPRISE_NOTIFICATION_TITLE); } - let s = childProcess.spawnSync("apprise", args); + const s = childProcess.spawnSync("apprise", args); - let output = (s.stdout) ? s.stdout.toString() : "ERROR: maybe apprise not found"; + const output = (s.stdout) ? s.stdout.toString() : "ERROR: maybe apprise not found"; if (output) { From e0a0a5db4cde0e86e0591fc60d61c58c36b6847a Mon Sep 17 00:00:00 2001 From: MrEddX <66828538+MrEddX@users.noreply.github.com> Date: Thu, 5 May 2022 22:46:34 +0300 Subject: [PATCH 099/124] Update bg-BG.js Translation Fixes --- src/languages/bg-BG.js | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/languages/bg-BG.js b/src/languages/bg-BG.js index 03fd288b..94553bd0 100644 --- a/src/languages/bg-BG.js +++ b/src/languages/bg-BG.js @@ -12,15 +12,15 @@ export default { keywordDescription: "Търси ключова дума в чист html или JSON отговор - чувствителна е към регистъра", pauseDashboardHome: "Пауза", deleteMonitorMsg: "Наистина ли желаете да изтриете този монитор?", - deleteNotificationMsg: "Наистина ли желаете да изтриете това известяване за всички монитори?", + deleteNotificationMsg: "Наистина ли желаете да изтриете това известие за всички монитори?", resolverserverDescription: "Cloudflare е сървърът по подразбиране, но можете да го промените по всяко време.", rrtypeDescription: "Изберете ресурсния запис, който желаете да наблюдавате", pauseMonitorMsg: "Наистина ли желаете да поставите в режим пауза?", - enableDefaultNotificationDescription: "За всеки нов монитор това известяване ще бъде активирано по подразбиране. Можете да го изключите за всеки отделен монитор.", + enableDefaultNotificationDescription: "За всеки нов монитор това известие ще бъде активирано по подразбиране. Можете да го изключите за всеки отделен монитор.", clearEventsMsg: "Наистина ли желаете да изтриете всички събития за този монитор?", clearHeartbeatsMsg: "Наистина ли желаете да изтриете всички записи за честотни проверки на този монитор?", confirmClearStatisticsMsg: "Наистина ли желаете да изтриете всички статистически данни?", - importHandleDescription: "Изберете 'Пропусни съществуващите', ако желаете да пропуснете всеки монитор или известяване със същото име. 'Презапис' ще изтрие всеки съществуващ монитор и известяване.", + importHandleDescription: "Изберете 'Пропусни съществуващите', ако желаете да пропуснете всеки монитор или известие със същото име. 'Презапис' ще изтрие всеки съществуващ монитор и известие.", confirmImportMsg: "Сигурни ли сте, че желаете импортирането на архива? Моля, уверете се, че сте избрали правилната опция за импортиране.", twoFAVerifyLabel: "Моля, въведете вашия токен код, за да проверите дали 2FA работи", tokenValidSettingsMsg: "Токен кодът е валиден! Вече можете да запазите настройките за 2FA.", @@ -76,9 +76,9 @@ export default { "Max. Redirects": "Макс. брой пренасочвания", "Accepted Status Codes": "Допустими статус кодове", Save: "Запази", - Notifications: "Известявания", + Notifications: "Известия", "Not available, please setup.": "Не са налични. Моля, настройте.", - "Setup Notification": "Настрой известяване", + "Setup Notification": "Настрой известие", Light: "Светла", Dark: "Тъмна", Auto: "Автоматично", @@ -109,7 +109,7 @@ export default { Login: "Вход", "No Monitors, please": "Все още няма монитори. Моля, добавете поне ", "add one": "един.", - "Notification Type": "Тип известяване", + "Notification Type": "Тип известие", Email: "Имейл", Test: "Тест", "Certificate Info": "Информация за сертификат", @@ -131,9 +131,9 @@ export default { Events: "Събития", Heartbeats: "Проверки", "Auto Get": "Авт. попълване", - backupDescription: "Можете да архивирате всички монитори и всички известявания в JSON файл.", + backupDescription: "Можете да архивирате всички монитори и всички известия в JSON файл.", backupDescription2: "PS: Имайте предвид, че данните за история и събития няма да бъдат включени.", - backupDescription3: "Чувствителни данни, като токен кодове за известяване, се съдържат в експортирания файл. Моля, бъдете внимателни с неговото съхранение.", + backupDescription3: "Чувствителни данни, като токен кодове за известия, се съдържат в експортирания файл. Моля, бъдете внимателни с неговото съхранение.", alertNoFile: "Моля, изберете файл за импортиране.", alertWrongFileType: "Моля, изберете JSON файл.", "Clear all statistics": "Изтрий цялата статистика", @@ -202,7 +202,7 @@ export default { "Push URL": "Генериран Push URL адрес", needPushEvery: "Необходимо е да извършвате заявка към този URL адрес на всеки {0} секунди", pushOptionalParams: "Допълнителни, но не задължителни параметри: {0}", - defaultNotificationName: "Моето {notification} известяване ({number})", + defaultNotificationName: "Моето {notification} известие ({number})", here: "тук", Required: "Задължително поле", "Bot Token": "Бот токен", @@ -252,7 +252,7 @@ export default { "Notification Sound": "Звуков сигнал", "More info on:": "Повече информация на: {0}", pushoverDesc1: "Приоритет Спешно (2) по подразбиране изчаква 30 секунди между повторните опити и изтича след 1 час.", - pushoverDesc2: "Ако желаете да изпратите известявания до различни устройства, попълнете полето Устройство.", + pushoverDesc2: "Ако желаете да изпратите известия до различни устройства, попълнете полето Устройство.", "SMS Type": "SMS тип", octopushTypePremium: "Премиум (Бърз - препоръчителен в случай на тревога)", octopushTypeLowCost: "Евтин (Бавен - понякога бива блокиран от оператора)", @@ -275,7 +275,7 @@ export default { lineDevConsoleTo: "Line - Конзола за разработчици - {0}", "Basic Settings": "Основни настройки", "User ID": "Потребител ID", - "Messaging API": "API за известяване", + "Messaging API": "API за съобщаване", wayToGetLineChannelToken: "Необходимо е първо да посетите {0}, за да създадете (Messaging API) за доставчик и канал, след което може да вземете токен кода за канал и потребителско ID от споменатите по-горе елементи на менюто.", "Icon URL": "URL адрес за иконка", aboutIconURL: "Може да предоставите линк към картинка в поле \"URL Адрес за иконка\" за да отмените картинката на профила по подразбиране. Няма да се използва, ако вече сте настроили емотикон.", @@ -291,7 +291,7 @@ export default { matrixHomeserverURL: "Сървър URL адрес (започва с http(s):// и порт по желание)", "Internal Room Id": "ID на вътрешна стая", matrixDesc1: "Може да намерите \"ID на вътрешна стая\" в разширените настройки на стаята във вашия Matrix клиент. Примерен изглед: !QMdRCpUIfLwsfjxye6:home.server.", - matrixDesc2: "Силно препоръчваме да създадете НОВ потребител и да НЕ използвате токен кодът на вашия личен Matrix потребирел, т.к. той позволява пълен достъп до вашия акаунт и всички стаи към които сте се присъединили. Вместо това създайте нов потребител и го поканете само в стаята, където желаете да получавате известяванията. Токен код за достъп ще получите изпълнявайки {0}", + matrixDesc2: "Силно препоръчваме да създадете НОВ потребител и да НЕ използвате токен кодът на вашия личен Matrix потребирел, т.к. той позволява пълен достъп до вашия акаунт и всички стаи към които сте се присъединили. Вместо това създайте нов потребител и го поканете само в стаята, където желаете да получавате известията. Токен код за достъп ще получите изпълнявайки {0}", Method: "Метод", Body: "Съобщение", Headers: "Хедъри", @@ -449,7 +449,7 @@ export default { Customize: "Персонализирай", "Custom Footer": "Персонализиран долен колонтитул", "Custom CSS": "Потребителски CSS", - "Domain Name Expiry Notification": "Известяване при изтичащ домейн", + "Domain Name Expiry Notification": "Известие при изтичащ домейн", Proxy: "Прокси", "Date Created": "Дата на създаване", onebotHttpAddress: "OneBot HTTP адрес", From 429ad384d081ee8e228b192ff82ae27c45dfdd70 Mon Sep 17 00:00:00 2001 From: Louis Lam <louislam@users.noreply.github.com> Date: Fri, 6 May 2022 14:41:34 +0800 Subject: [PATCH 100/124] Fix hardcoded path for error.log and move errorLog() to UptimeKumaServer.errorLog() --- server/model/monitor.js | 5 +++-- server/server.js | 4 ++-- server/uptime-kuma-server.js | 29 ++++++++++++++++++++++++++++- server/util-server.js | 30 +----------------------------- 4 files changed, 34 insertions(+), 34 deletions(-) diff --git a/server/model/monitor.js b/server/model/monitor.js index e106f7ab..f2d16524 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -7,7 +7,7 @@ dayjs.extend(timezone); const axios = require("axios"); const { Prometheus } = require("../prometheus"); const { log, UP, DOWN, PENDING, flipStatus, TimeLogger } = require("../../src/util"); -const { tcping, ping, dnsResolve, checkCertificate, checkStatusCode, getTotalClientInRoom, setting, errorLog, mqttAsync } = require("../util-server"); +const { tcping, ping, dnsResolve, checkCertificate, checkStatusCode, getTotalClientInRoom, setting, mqttAsync } = require("../util-server"); const { R } = require("redbean-node"); const { BeanModel } = require("redbean-node/dist/bean-model"); const { Notification } = require("../notification"); @@ -15,6 +15,7 @@ const { Proxy } = require("../proxy"); const { demoMode } = require("../config"); const version = require("../../package.json").version; const apicache = require("../modules/apicache"); +const { UptimeKumaServer } = require("../uptime-kuma-server"); /** * status: @@ -521,7 +522,7 @@ class Monitor extends BeanModel { await beat(); } catch (e) { console.trace(e); - errorLog(e, false); + UptimeKumaServer.errorLog(e, false); log.error("monitor", "Please report to https://github.com/louislam/uptime-kuma/issues"); if (! this.isStop) { diff --git a/server/server.js b/server/server.js index 11c59edb..79cb2102 100644 --- a/server/server.js +++ b/server/server.js @@ -60,7 +60,7 @@ log.info("server", "Importing this project modules"); log.debug("server", "Importing Monitor"); const Monitor = require("./model/monitor"); log.debug("server", "Importing Settings"); -const { getSettings, setSettings, setting, initJWTSecret, checkLogin, startUnitTest, FBSD, errorLog, doubleCheckPassword } = require("./util-server"); +const { getSettings, setSettings, setting, initJWTSecret, checkLogin, startUnitTest, FBSD, doubleCheckPassword } = require("./util-server"); log.debug("server", "Importing Notification"); const { Notification } = require("./notification"); @@ -1694,6 +1694,6 @@ gracefulShutdown(server.httpServer, { // Catch unexpected errors here process.addListener("unhandledRejection", (error, promise) => { console.trace(error); - errorLog(error, false); + UptimeKumaServer.errorLog(error, false); console.error("If you keep encountering errors, please report to https://github.com/louislam/uptime-kuma/issues"); }); diff --git a/server/uptime-kuma-server.js b/server/uptime-kuma-server.js index 1cc74064..d0c968e7 100644 --- a/server/uptime-kuma-server.js +++ b/server/uptime-kuma-server.js @@ -5,13 +5,14 @@ const http = require("http"); const { Server } = require("socket.io"); const { R } = require("redbean-node"); const { log } = require("../src/util"); +const Database = require("./database"); +const util = require("util"); /** * `module.exports` (alias: `server`) should be inside this class, in order to avoid circular dependency issue. * @type {UptimeKumaServer} */ class UptimeKumaServer { - /** * * @type {UptimeKumaServer} @@ -83,6 +84,32 @@ class UptimeKumaServer { return result; } + + /** + * Write error to log file + * @param {any} error The error to write + * @param {boolean} outputToConsole Should the error also be output to console? + */ + static errorLog(error, outputToConsole = true) { + const errorLogStream = fs.createWriteStream(Database.dataDir + "/error.log", { + flags: "a" + }); + + errorLogStream.on("error", () => { + log.info("", "Cannot write to error.log"); + }); + + if (errorLogStream) { + const dateTime = R.isoDateTime(); + errorLogStream.write(`[${dateTime}] ` + util.format(error) + "\n"); + + if (outputToConsole) { + console.error(error); + } + } + + errorLogStream.end(); + } } module.exports = { diff --git a/server/util-server.js b/server/util-server.js index 39a2d904..985143d5 100644 --- a/server/util-server.js +++ b/server/util-server.js @@ -7,8 +7,6 @@ const { Resolver } = require("dns"); const childProcess = require("child_process"); const iconv = require("iconv-lite"); const chardet = require("chardet"); -const fs = require("fs"); -const nodeJsUtil = require("util"); const mqtt = require("mqtt"); // From ping-lite @@ -206,7 +204,7 @@ exports.dnsResolve = function (hostname, resolverServer, rrtype) { /** * Retrieve value of setting based on key * @param {string} key Key of setting to retrieve - * @returns {Promise<Object>} Object representation of setting + * @returns {Promise<any>} Value */ exports.setting = async function (key) { let value = await R.getCell("SELECT `value` FROM setting WHERE `key` = ? ", [ @@ -524,29 +522,3 @@ exports.convertToUTF8 = (body) => { const str = iconv.decode(body, guessEncoding); return str.toString(); }; - -let logFile; - -try { - logFile = fs.createWriteStream("./data/error.log", { - flags: "a" - }); -} catch (_) { } - -/** - * Write error to log file - * @param {any} error The error to write - * @param {boolean} outputToConsole Should the error also be output to console? - */ -exports.errorLog = (error, outputToConsole = true) => { - try { - if (logFile) { - const dateTime = R.isoDateTime(); - logFile.write(`[${dateTime}] ` + nodeJsUtil.format(error) + "\n"); - - if (outputToConsole) { - console.error(error); - } - } - } catch (_) { } -}; From 8b0813ceff9f47672a5ddfc7e020ce27ee37e7ec Mon Sep 17 00:00:00 2001 From: Louis Lam <louislam@users.noreply.github.com> Date: Fri, 6 May 2022 14:52:09 +0800 Subject: [PATCH 101/124] Fix #1596 hopefully --- src/layouts/Layout.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/layouts/Layout.vue b/src/layouts/Layout.vue index c7076221..9688cdc2 100644 --- a/src/layouts/Layout.vue +++ b/src/layouts/Layout.vue @@ -33,7 +33,7 @@ </li> <li v-if="$root.loggedIn" class="nav-item"> <div class="dropdown dropdown-profile-pic"> - <div type="button" class="nav-link" data-bs-toggle="dropdown"> + <div class="nav-link" data-bs-toggle="dropdown"> <div class="profile-pic">{{ $root.usernameFirstChar }}</div> <font-awesome-icon icon="angle-down" /> </div> From aa8ea6d3984fdf49d380f90ec749226b3e08e8c7 Mon Sep 17 00:00:00 2001 From: MrEddX <66828538+MrEddX@users.noreply.github.com> Date: Sat, 7 May 2022 08:01:06 +0300 Subject: [PATCH 102/124] Update bg-BG.js (#1617) Translation Fixes --- src/languages/bg-BG.js | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/languages/bg-BG.js b/src/languages/bg-BG.js index 03fd288b..94553bd0 100644 --- a/src/languages/bg-BG.js +++ b/src/languages/bg-BG.js @@ -12,15 +12,15 @@ export default { keywordDescription: "Търси ключова дума в чист html или JSON отговор - чувствителна е към регистъра", pauseDashboardHome: "Пауза", deleteMonitorMsg: "Наистина ли желаете да изтриете този монитор?", - deleteNotificationMsg: "Наистина ли желаете да изтриете това известяване за всички монитори?", + deleteNotificationMsg: "Наистина ли желаете да изтриете това известие за всички монитори?", resolverserverDescription: "Cloudflare е сървърът по подразбиране, но можете да го промените по всяко време.", rrtypeDescription: "Изберете ресурсния запис, който желаете да наблюдавате", pauseMonitorMsg: "Наистина ли желаете да поставите в режим пауза?", - enableDefaultNotificationDescription: "За всеки нов монитор това известяване ще бъде активирано по подразбиране. Можете да го изключите за всеки отделен монитор.", + enableDefaultNotificationDescription: "За всеки нов монитор това известие ще бъде активирано по подразбиране. Можете да го изключите за всеки отделен монитор.", clearEventsMsg: "Наистина ли желаете да изтриете всички събития за този монитор?", clearHeartbeatsMsg: "Наистина ли желаете да изтриете всички записи за честотни проверки на този монитор?", confirmClearStatisticsMsg: "Наистина ли желаете да изтриете всички статистически данни?", - importHandleDescription: "Изберете 'Пропусни съществуващите', ако желаете да пропуснете всеки монитор или известяване със същото име. 'Презапис' ще изтрие всеки съществуващ монитор и известяване.", + importHandleDescription: "Изберете 'Пропусни съществуващите', ако желаете да пропуснете всеки монитор или известие със същото име. 'Презапис' ще изтрие всеки съществуващ монитор и известие.", confirmImportMsg: "Сигурни ли сте, че желаете импортирането на архива? Моля, уверете се, че сте избрали правилната опция за импортиране.", twoFAVerifyLabel: "Моля, въведете вашия токен код, за да проверите дали 2FA работи", tokenValidSettingsMsg: "Токен кодът е валиден! Вече можете да запазите настройките за 2FA.", @@ -76,9 +76,9 @@ export default { "Max. Redirects": "Макс. брой пренасочвания", "Accepted Status Codes": "Допустими статус кодове", Save: "Запази", - Notifications: "Известявания", + Notifications: "Известия", "Not available, please setup.": "Не са налични. Моля, настройте.", - "Setup Notification": "Настрой известяване", + "Setup Notification": "Настрой известие", Light: "Светла", Dark: "Тъмна", Auto: "Автоматично", @@ -109,7 +109,7 @@ export default { Login: "Вход", "No Monitors, please": "Все още няма монитори. Моля, добавете поне ", "add one": "един.", - "Notification Type": "Тип известяване", + "Notification Type": "Тип известие", Email: "Имейл", Test: "Тест", "Certificate Info": "Информация за сертификат", @@ -131,9 +131,9 @@ export default { Events: "Събития", Heartbeats: "Проверки", "Auto Get": "Авт. попълване", - backupDescription: "Можете да архивирате всички монитори и всички известявания в JSON файл.", + backupDescription: "Можете да архивирате всички монитори и всички известия в JSON файл.", backupDescription2: "PS: Имайте предвид, че данните за история и събития няма да бъдат включени.", - backupDescription3: "Чувствителни данни, като токен кодове за известяване, се съдържат в експортирания файл. Моля, бъдете внимателни с неговото съхранение.", + backupDescription3: "Чувствителни данни, като токен кодове за известия, се съдържат в експортирания файл. Моля, бъдете внимателни с неговото съхранение.", alertNoFile: "Моля, изберете файл за импортиране.", alertWrongFileType: "Моля, изберете JSON файл.", "Clear all statistics": "Изтрий цялата статистика", @@ -202,7 +202,7 @@ export default { "Push URL": "Генериран Push URL адрес", needPushEvery: "Необходимо е да извършвате заявка към този URL адрес на всеки {0} секунди", pushOptionalParams: "Допълнителни, но не задължителни параметри: {0}", - defaultNotificationName: "Моето {notification} известяване ({number})", + defaultNotificationName: "Моето {notification} известие ({number})", here: "тук", Required: "Задължително поле", "Bot Token": "Бот токен", @@ -252,7 +252,7 @@ export default { "Notification Sound": "Звуков сигнал", "More info on:": "Повече информация на: {0}", pushoverDesc1: "Приоритет Спешно (2) по подразбиране изчаква 30 секунди между повторните опити и изтича след 1 час.", - pushoverDesc2: "Ако желаете да изпратите известявания до различни устройства, попълнете полето Устройство.", + pushoverDesc2: "Ако желаете да изпратите известия до различни устройства, попълнете полето Устройство.", "SMS Type": "SMS тип", octopushTypePremium: "Премиум (Бърз - препоръчителен в случай на тревога)", octopushTypeLowCost: "Евтин (Бавен - понякога бива блокиран от оператора)", @@ -275,7 +275,7 @@ export default { lineDevConsoleTo: "Line - Конзола за разработчици - {0}", "Basic Settings": "Основни настройки", "User ID": "Потребител ID", - "Messaging API": "API за известяване", + "Messaging API": "API за съобщаване", wayToGetLineChannelToken: "Необходимо е първо да посетите {0}, за да създадете (Messaging API) за доставчик и канал, след което може да вземете токен кода за канал и потребителско ID от споменатите по-горе елементи на менюто.", "Icon URL": "URL адрес за иконка", aboutIconURL: "Може да предоставите линк към картинка в поле \"URL Адрес за иконка\" за да отмените картинката на профила по подразбиране. Няма да се използва, ако вече сте настроили емотикон.", @@ -291,7 +291,7 @@ export default { matrixHomeserverURL: "Сървър URL адрес (започва с http(s):// и порт по желание)", "Internal Room Id": "ID на вътрешна стая", matrixDesc1: "Може да намерите \"ID на вътрешна стая\" в разширените настройки на стаята във вашия Matrix клиент. Примерен изглед: !QMdRCpUIfLwsfjxye6:home.server.", - matrixDesc2: "Силно препоръчваме да създадете НОВ потребител и да НЕ използвате токен кодът на вашия личен Matrix потребирел, т.к. той позволява пълен достъп до вашия акаунт и всички стаи към които сте се присъединили. Вместо това създайте нов потребител и го поканете само в стаята, където желаете да получавате известяванията. Токен код за достъп ще получите изпълнявайки {0}", + matrixDesc2: "Силно препоръчваме да създадете НОВ потребител и да НЕ използвате токен кодът на вашия личен Matrix потребирел, т.к. той позволява пълен достъп до вашия акаунт и всички стаи към които сте се присъединили. Вместо това създайте нов потребител и го поканете само в стаята, където желаете да получавате известията. Токен код за достъп ще получите изпълнявайки {0}", Method: "Метод", Body: "Съобщение", Headers: "Хедъри", @@ -449,7 +449,7 @@ export default { Customize: "Персонализирай", "Custom Footer": "Персонализиран долен колонтитул", "Custom CSS": "Потребителски CSS", - "Domain Name Expiry Notification": "Известяване при изтичащ домейн", + "Domain Name Expiry Notification": "Известие при изтичащ домейн", Proxy: "Прокси", "Date Created": "Дата на създаване", onebotHttpAddress: "OneBot HTTP адрес", From b6803717462963104648fc61da62e7717a95090d Mon Sep 17 00:00:00 2001 From: Clemens Wolff <clemens@justamouse.com> Date: Sat, 7 May 2022 11:00:57 -0400 Subject: [PATCH 103/124] Make apprise notification title configurable in UI --- server/notification-providers/apprise.js | 11 ++--------- src/components/notifications/Apprise.vue | 3 +++ 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/server/notification-providers/apprise.js b/server/notification-providers/apprise.js index acbc0567..887afbf5 100644 --- a/server/notification-providers/apprise.js +++ b/server/notification-providers/apprise.js @@ -1,22 +1,15 @@ const NotificationProvider = require("./notification-provider"); const childProcess = require("child_process"); -/** - * If you use an apprise backend that requires the notification title to - * be set (such as for example messaging a Zulip Stream), you can use this - * environment variable to configure the title. - */ -const { APPRISE_NOTIFICATION_TITLE } = process.env; - class Apprise extends NotificationProvider { name = "apprise"; async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { const args = [ "-vv", "-b", msg, notification.appriseURL ]; - if (APPRISE_NOTIFICATION_TITLE) { + if (notification.title) { args.push("-t"); - args.push(APPRISE_NOTIFICATION_TITLE); + args.push(notification.title); } const s = childProcess.spawnSync("apprise", args); diff --git a/src/components/notifications/Apprise.vue b/src/components/notifications/Apprise.vue index c10e23cf..7432554c 100644 --- a/src/components/notifications/Apprise.vue +++ b/src/components/notifications/Apprise.vue @@ -8,6 +8,9 @@ <a href="https://github.com/caronc/apprise/wiki#notification-services" target="_blank">https://github.com/caronc/apprise/wiki#notification-services</a> </i18n-t> </div> + + <label for="title" class="form-label">{{ $t("Title") }}</label> + <input id="title" v-model="$parent.notification.title" type="text" class="form-control"> </div> <div class="mb-3"> <i18n-t tag="p" keypath="Status:"> From d344914ca0d5cb53ff7fd5884a00a9e7015c3dcf Mon Sep 17 00:00:00 2001 From: Louis Lam <louislam@users.noreply.github.com> Date: Mon, 9 May 2022 13:47:19 +0800 Subject: [PATCH 104/124] Update docker-compose.yml --- docker/docker-compose.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index a6499ef9..f01f0ea5 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -8,7 +8,7 @@ services: image: louislam/uptime-kuma:1 container_name: uptime-kuma volumes: - - ./uptime-kuma:/app/data + - ./uptime-kuma-data:/app/data ports: - - 3001:3001 + - 3001:3001 # <Host Port>:<Container Port> restart: always From 9f493bbec7a00c66295ca58e274c951dd465470d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karel=20Kr=C3=BDda?= <karel.kryda@gmail.com> Date: Mon, 9 May 2022 21:02:29 +0200 Subject: [PATCH 105/124] clone master branch --- CNAME | 2 +- public/icon.svg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CNAME b/CNAME index a5348b07..40f0b545 100644 --- a/CNAME +++ b/CNAME @@ -1 +1 @@ -git.kuma.pet \ No newline at end of file +git.kuma.pet; diff --git a/public/icon.svg b/public/icon.svg index 825c344e..9c0bc6ca 100644 --- a/public/icon.svg +++ b/public/icon.svg @@ -1,3 +1,3 @@ <?xml version="1.0" encoding="UTF-8" standalone="no"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> -<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid meet" viewBox="0 0 640 640" width="640" height="640"><defs><path d="M407.55 916.24C471.25 916.24 522.89 967.88 522.89 1031.57C522.89 1113.88 522.89 1245.44 522.89 1327.74C522.89 1391.44 471.25 1443.08 407.55 1443.08C325.25 1443.08 193.68 1443.08 111.38 1443.08C47.69 1443.08 -3.95 1391.44 -3.95 1327.74C-3.95 1245.44 -3.95 1113.88 -3.95 1031.57C-3.95 967.88 47.69 916.24 111.38 916.24C193.68 916.24 325.25 916.24 407.55 916.24Z" id="a1LdTs1gvU"></path><linearGradient id="gradientcoH7TNh19" gradientUnits="userSpaceOnUse" x1="256.07" y1="1132.14" x2="609.11" y2="1480.42"><stop style="stop-color: #c2efd2;stop-opacity: 1" offset="0%"></stop><stop style="stop-color: #8ff0e5;stop-opacity: 1" offset="100%"></stop></linearGradient><path d="M-467.41 394.63C-467.41 554.76 -597.42 684.76 -757.55 684.76C-917.68 684.76 -1047.69 554.76 -1047.69 394.63C-1047.69 234.5 -917.68 104.49 -757.55 104.49C-597.42 104.49 -467.41 234.5 -467.41 394.63Z" id="a1uaEBd4xM"></path><path d="M-96.99 -586.14C-57.24 -619.85 -5.79 -604.75 19.26 -580.46C31.43 -568.66 56.57 -546.36 40.97 -491.67C32.76 -462.87 10.41 -436.4 -26.05 -412.27C-15.07 -377.85 -5.6 -344.76 2.36 -313C14.29 -265.36 13.55 -189.67 -26.05 -155.4C-67.27 -119.73 -166.91 -104.09 -234.24 -103.09C-301.57 -102.1 -406.19 -113.09 -461.6 -155.4C-517.01 -197.7 -512.24 -257.07 -498.04 -313C-488.58 -350.28 -476.43 -383.38 -461.6 -412.27C-505.54 -441.3 -530.54 -467.76 -536.6 -491.67C-545.68 -527.54 -530.93 -565.61 -501.12 -586.14C-471.31 -606.67 -435.18 -606.9 -400.45 -586.14C-377.3 -572.3 -354.79 -542.13 -332.92 -495.62C-287.85 -505.25 -254.96 -509.57 -234.24 -508.6C-214.74 -507.68 -186.57 -503.36 -149.72 -495.62C-135.81 -537.95 -118.23 -568.12 -96.99 -586.14Z" id="f8p7QlEjN3"></path><linearGradient id="gradienta4Tg99ZOOp" gradientUnits="userSpaceOnUse" x1="-440.25" y1="-388.59" x2="-100.49" y2="-147.33"><stop style="stop-color: #5cdd8b;stop-opacity: 1" offset="0%"></stop><stop style="stop-color: #7ae6a1;stop-opacity: 1" offset="100%"></stop></linearGradient><path d="M-86.03 -10.69C-61.35 -10.69 -41.34 9.32 -41.34 34.01C-41.34 119.07 -41.34 329.58 -41.34 414.65C-41.34 439.33 -61.35 459.34 -86.03 459.34C-136.01 459.34 -241.25 459.34 -291.23 459.34C-315.92 459.34 -335.93 439.33 -335.93 414.65C-335.93 329.58 -335.93 119.07 -335.93 34.01C-335.93 9.32 -315.92 -10.69 -291.23 -10.69C-241.25 -10.69 -136.01 -10.69 -86.03 -10.69Z" id="d32ZZRxd1S"></path><linearGradient id="gradientb1JxIe4xUm" gradientUnits="userSpaceOnUse" x1="-791.65" y1="-33.27" x2="892.1" y2="418.94"><stop style="stop-color: #5cdd8b;stop-opacity: 1" offset="0%"></stop><stop style="stop-color: #5ae98f;stop-opacity: 1" offset="100%"></stop></linearGradient><path d="M-257.95 458.12C-247.92 449.62 -234.93 453.43 -228.61 459.56C-225.54 462.54 -219.19 468.17 -223.13 481.97C-225.2 489.24 -230.84 495.92 -240.05 502.01C-237.27 510.7 -234.88 519.06 -232.88 527.07C-229.86 539.1 -230.05 558.21 -240.05 566.86C-250.45 575.86 -275.6 579.81 -292.6 580.06C-309.6 580.31 -336.01 577.54 -349.99 566.86C-363.98 556.18 -362.77 541.19 -359.19 527.07C-356.8 517.66 -353.73 509.31 -349.99 502.01C-361.08 494.69 -367.39 488.01 -368.92 481.97C-371.22 472.92 -367.49 463.31 -359.97 458.12C-352.44 452.94 -343.32 452.88 -334.56 458.12C-328.71 461.62 -323.03 469.23 -317.51 480.97C-306.13 478.54 -297.83 477.45 -292.6 477.7C-287.68 477.93 -280.56 479.02 -271.26 480.97C-267.75 470.29 -263.32 462.67 -257.95 458.12Z" id="b19LRRbPrG"></path><path d="M490.4 235.64C544.09 358.38 544.09 435.34 490.4 466.5C409.85 513.24 199.96 527.49 139.54 455.64C99.26 407.74 99.26 334.4 139.54 235.64C180.5 168.18 238.71 134.45 314.17 134.45C389.64 134.45 448.38 168.18 490.4 235.64Z" id="bN5StdyPU"></path><linearGradient id="gradientb1HT15TsY0" gradientUnits="userSpaceOnUse" x1="259.78" y1="261.15" x2="463.85" y2="456.49"><stop style="stop-color: #5cdd8b;stop-opacity: 1" offset="0%"></stop><stop style="stop-color: #86e6a9;stop-opacity: 1" offset="100%"></stop></linearGradient><path d="M393.81 -775.89C428.26 -748.09 439.99 -725.54 429 -708.22C412.51 -682.24 353.16 -646.07 324.5 -657.93C305.39 -665.83 294.22 -687.32 290.97 -722.41C292.69 -748.43 304.61 -767.19 326.73 -778.69C348.85 -790.19 371.21 -789.26 393.81 -775.89Z" id="arh6miPP2"></path><linearGradient id="gradientc2g6rBSAiq" gradientUnits="userSpaceOnUse" x1="330.1" y1="-733.26" x2="419.69" y2="-707.1"><stop style="stop-color: #5cdd8b;stop-opacity: 1" offset="0%"></stop><stop style="stop-color: #86e6a9;stop-opacity: 1" offset="100%"></stop></linearGradient><path d="M675.36 -369.24C669.97 -325.31 657.02 -303.43 636.51 -303.61C605.74 -303.87 543.67 -335.15 538.59 -365.74C535.2 -386.14 547.54 -406.99 575.61 -428.29C598.61 -440.58 620.83 -440.37 642.29 -427.67C663.74 -414.97 674.77 -395.49 675.36 -369.24Z" id="a2VENFzCvL"></path><linearGradient id="gradientc18GuJy4sZ" gradientUnits="userSpaceOnUse" x1="605.5" y1="-400.8" x2="630.64" y2="-310.92"><stop style="stop-color: #5cdd8b;stop-opacity: 1" offset="0%"></stop><stop style="stop-color: #86e6a9;stop-opacity: 1" offset="100%"></stop></linearGradient></defs><g><g><g><use xlink:href="#a1LdTs1gvU" opacity="1" fill="url(#gradientcoH7TNh19)"></use></g><g><use xlink:href="#a1uaEBd4xM" opacity="1" fill="#ebf0ed" fill-opacity="1"></use></g><g><use xlink:href="#f8p7QlEjN3" opacity="1" fill="url(#gradienta4Tg99ZOOp)"></use><g><use xlink:href="#f8p7QlEjN3" opacity="1" fill-opacity="0" stroke="#ffffff" stroke-width="98" stroke-opacity="0.57"></use></g></g><g><use xlink:href="#d32ZZRxd1S" opacity="1" fill="url(#gradientb1JxIe4xUm)"></use><g><use xlink:href="#d32ZZRxd1S" opacity="1" fill-opacity="0" stroke="#f2f2f2" stroke-width="60" stroke-opacity="0.51"></use></g></g><g><use xlink:href="#b19LRRbPrG" opacity="1" fill="#d8ad9a" fill-opacity="1"></use><g><use xlink:href="#b19LRRbPrG" opacity="1" fill-opacity="0" stroke="#ffffff" stroke-width="17" stroke-opacity="1"></use></g></g><g><use xlink:href="#bN5StdyPU" opacity="1" fill="url(#gradientb1HT15TsY0)"></use><g><use xlink:href="#bN5StdyPU" opacity="1" fill-opacity="0" stroke="#f2f2f2" stroke-width="200" stroke-opacity="0.51"></use></g></g><g><use xlink:href="#arh6miPP2" opacity="1" fill="url(#gradientc2g6rBSAiq)"></use></g><g><use xlink:href="#a2VENFzCvL" opacity="1" fill="url(#gradientc18GuJy4sZ)"></use></g></g></g></svg> \ No newline at end of file +<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid meet" viewBox="0 0 640 640" width="640" height="640"><defs><path d="M407.55 916.24C471.25 916.24 522.89 967.88 522.89 1031.57C522.89 1113.88 522.89 1245.44 522.89 1327.74C522.89 1391.44 471.25 1443.08 407.55 1443.08C325.25 1443.08 193.68 1443.08 111.38 1443.08C47.69 1443.08 -3.95 1391.44 -3.95 1327.74C-3.95 1245.44 -3.95 1113.88 -3.95 1031.57C-3.95 967.88 47.69 916.24 111.38 916.24C193.68 916.24 325.25 916.24 407.55 916.24Z" id="a1LdTs1gvU"></path><linearGradient id="gradientcoH7TNh19" gradientUnits="userSpaceOnUse" x1="256.07" y1="1132.14" x2="609.11" y2="1480.42"><stop style="stop-color: #c2efd2;stop-opacity: 1" offset="0%"></stop><stop style="stop-color: #8ff0e5;stop-opacity: 1" offset="100%"></stop></linearGradient><path d="M-467.41 394.63C-467.41 554.76 -597.42 684.76 -757.55 684.76C-917.68 684.76 -1047.69 554.76 -1047.69 394.63C-1047.69 234.5 -917.68 104.49 -757.55 104.49C-597.42 104.49 -467.41 234.5 -467.41 394.63Z" id="a1uaEBd4xM"></path><path d="M-96.99 -586.14C-57.24 -619.85 -5.79 -604.75 19.26 -580.46C31.43 -568.66 56.57 -546.36 40.97 -491.67C32.76 -462.87 10.41 -436.4 -26.05 -412.27C-15.07 -377.85 -5.6 -344.76 2.36 -313C14.29 -265.36 13.55 -189.67 -26.05 -155.4C-67.27 -119.73 -166.91 -104.09 -234.24 -103.09C-301.57 -102.1 -406.19 -113.09 -461.6 -155.4C-517.01 -197.7 -512.24 -257.07 -498.04 -313C-488.58 -350.28 -476.43 -383.38 -461.6 -412.27C-505.54 -441.3 -530.54 -467.76 -536.6 -491.67C-545.68 -527.54 -530.93 -565.61 -501.12 -586.14C-471.31 -606.67 -435.18 -606.9 -400.45 -586.14C-377.3 -572.3 -354.79 -542.13 -332.92 -495.62C-287.85 -505.25 -254.96 -509.57 -234.24 -508.6C-214.74 -507.68 -186.57 -503.36 -149.72 -495.62C-135.81 -537.95 -118.23 -568.12 -96.99 -586.14Z" id="f8p7QlEjN3"></path><linearGradient id="gradienta4Tg99ZOOp" gradientUnits="userSpaceOnUse" x1="-440.25" y1="-388.59" x2="-100.49" y2="-147.33"><stop style="stop-color: #5cdd8b;stop-opacity: 1" offset="0%"></stop><stop style="stop-color: #7ae6a1;stop-opacity: 1" offset="100%"></stop></linearGradient><path d="M-86.03 -10.69C-61.35 -10.69 -41.34 9.32 -41.34 34.01C-41.34 119.07 -41.34 329.58 -41.34 414.65C-41.34 439.33 -61.35 459.34 -86.03 459.34C-136.01 459.34 -241.25 459.34 -291.23 459.34C-315.92 459.34 -335.93 439.33 -335.93 414.65C-335.93 329.58 -335.93 119.07 -335.93 34.01C-335.93 9.32 -315.92 -10.69 -291.23 -10.69C-241.25 -10.69 -136.01 -10.69 -86.03 -10.69Z" id="d32ZZRxd1S"></path><linearGradient id="gradientb1JxIe4xUm" gradientUnits="userSpaceOnUse" x1="-791.65" y1="-33.27" x2="892.1" y2="418.94"><stop style="stop-color: #5cdd8b;stop-opacity: 1" offset="0%"></stop><stop style="stop-color: #5ae98f;stop-opacity: 1" offset="100%"></stop></linearGradient><path d="M-257.95 458.12C-247.92 449.62 -234.93 453.43 -228.61 459.56C-225.54 462.54 -219.19 468.17 -223.13 481.97C-225.2 489.24 -230.84 495.92 -240.05 502.01C-237.27 510.7 -234.88 519.06 -232.88 527.07C-229.86 539.1 -230.05 558.21 -240.05 566.86C-250.45 575.86 -275.6 579.81 -292.6 580.06C-309.6 580.31 -336.01 577.54 -349.99 566.86C-363.98 556.18 -362.77 541.19 -359.19 527.07C-356.8 517.66 -353.73 509.31 -349.99 502.01C-361.08 494.69 -367.39 488.01 -368.92 481.97C-371.22 472.92 -367.49 463.31 -359.97 458.12C-352.44 452.94 -343.32 452.88 -334.56 458.12C-328.71 461.62 -323.03 469.23 -317.51 480.97C-306.13 478.54 -297.83 477.45 -292.6 477.7C-287.68 477.93 -280.56 479.02 -271.26 480.97C-267.75 470.29 -263.32 462.67 -257.95 458.12Z" id="b19LRRbPrG"></path><path d="M490.4 235.64C544.09 358.38 544.09 435.34 490.4 466.5C409.85 513.24 199.96 527.49 139.54 455.64C99.26 407.74 99.26 334.4 139.54 235.64C180.5 168.18 238.71 134.45 314.17 134.45C389.64 134.45 448.38 168.18 490.4 235.64Z" id="bN5StdyPU"></path><linearGradient id="gradientb1HT15TsY0" gradientUnits="userSpaceOnUse" x1="259.78" y1="261.15" x2="463.85" y2="456.49"><stop style="stop-color: #5cdd8b;stop-opacity: 1" offset="0%"></stop><stop style="stop-color: #86e6a9;stop-opacity: 1" offset="100%"></stop></linearGradient><path d="M393.81 -775.89C428.26 -748.09 439.99 -725.54 429 -708.22C412.51 -682.24 353.16 -646.07 324.5 -657.93C305.39 -665.83 294.22 -687.32 290.97 -722.41C292.69 -748.43 304.61 -767.19 326.73 -778.69C348.85 -790.19 371.21 -789.26 393.81 -775.89Z" id="arh6miPP2"></path><linearGradient id="gradientc2g6rBSAiq" gradientUnits="userSpaceOnUse" x1="330.1" y1="-733.26" x2="419.69" y2="-707.1"><stop style="stop-color: #5cdd8b;stop-opacity: 1" offset="0%"></stop><stop style="stop-color: #86e6a9;stop-opacity: 1" offset="100%"></stop></linearGradient><path d="M675.36 -369.24C669.97 -325.31 657.02 -303.43 636.51 -303.61C605.74 -303.87 543.67 -335.15 538.59 -365.74C535.2 -386.14 547.54 -406.99 575.61 -428.29C598.61 -440.58 620.83 -440.37 642.29 -427.67C663.74 -414.97 674.77 -395.49 675.36 -369.24Z" id="a2VENFzCvL"></path><linearGradient id="gradientc18GuJy4sZ" gradientUnits="userSpaceOnUse" x1="605.5" y1="-400.8" x2="630.64" y2="-310.92"><stop style="stop-color: #5cdd8b;stop-opacity: 1" offset="0%"></stop><stop style="stop-color: #86e6a9;stop-opacity: 1" offset="100%"></stop></linearGradient></defs><g><g><g><use xlink:href="#a1LdTs1gvU" opacity="1" fill="url(#gradientcoH7TNh19)"></use></g><g><use xlink:href="#a1uaEBd4xM" opacity="1" fill="#ebf0ed" fill-opacity="1"></use></g><g><use xlink:href="#f8p7QlEjN3" opacity="1" fill="url(#gradienta4Tg99ZOOp)"></use><g><use xlink:href="#f8p7QlEjN3" opacity="1" fill-opacity="0" stroke="#ffffff" stroke-width="98" stroke-opacity="0.57"></use></g></g><g><use xlink:href="#d32ZZRxd1S" opacity="1" fill="url(#gradientb1JxIe4xUm)"></use><g><use xlink:href="#d32ZZRxd1S" opacity="1" fill-opacity="0" stroke="#f2f2f2" stroke-width="60" stroke-opacity="0.51"></use></g></g><g><use xlink:href="#b19LRRbPrG" opacity="1" fill="#d8ad9a" fill-opacity="1"></use><g><use xlink:href="#b19LRRbPrG" opacity="1" fill-opacity="0" stroke="#ffffff" stroke-width="17" stroke-opacity="1"></use></g></g><g><use xlink:href="#bN5StdyPU" opacity="1" fill="url(#gradientb1HT15TsY0)"></use><g><use xlink:href="#bN5StdyPU" opacity="1" fill-opacity="0" stroke="#f2f2f2" stroke-width="200" stroke-opacity="0.51"></use></g></g><g><use xlink:href="#arh6miPP2" opacity="1" fill="url(#gradientc2g6rBSAiq)"></use></g><g><use xlink:href="#a2VENFzCvL" opacity="1" fill="url(#gradientc18GuJy4sZ)"></use></g></g></g></svg> From 7acbfd24749e00c5a17ccb3af74408baf0c1e85a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karel=20Kr=C3=BDda?= <karel.kryda@gmail.com> Date: Mon, 9 May 2022 21:05:10 +0200 Subject: [PATCH 106/124] eslint fixes too much --- CNAME | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CNAME b/CNAME index 40f0b545..44250516 100644 --- a/CNAME +++ b/CNAME @@ -1 +1 @@ -git.kuma.pet; +git.kuma.pet From 7da9f139c1bd91c80dd880c8b97c0981576c0749 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karel=20Kr=C3=BDda?= <karel.kryda@gmail.com> Date: Mon, 9 May 2022 21:10:12 +0200 Subject: [PATCH 107/124] Bug fix --- server/model/monitor.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/server/model/monitor.js b/server/model/monitor.js index f2d16524..eaafb775 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -182,7 +182,7 @@ class Monitor extends BeanModel { // undefined if not https let tlsInfo = undefined; - if (!previousBeat) { + if (!previousBeat || this.type === "push") { previousBeat = await R.findOne("heartbeat", " monitor_id = ? ORDER BY time DESC", [ this.id, ]); @@ -377,9 +377,6 @@ class Monitor extends BeanModel { log.debug("monitor", "heartbeatCount" + heartbeatCount + " " + time); if (heartbeatCount <= 0) { - // Fix #922, since previous heartbeat could be inserted by api, it should get from database - previousBeat = await Monitor.getPreviousHeartbeat(this.id); - throw new Error("No heartbeat in the time window"); } else { // No need to insert successful heartbeat for push type, so end here From 0cf395dfc342a350295cea73e3584bd14671b64d Mon Sep 17 00:00:00 2001 From: Louis Lam <louislam@users.noreply.github.com> Date: Sat, 14 May 2022 14:06:35 +0800 Subject: [PATCH 108/124] Fix merge issue --- src/components/HeartbeatBar.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/HeartbeatBar.vue b/src/components/HeartbeatBar.vue index a36c5d1c..deb2bc23 100644 --- a/src/components/HeartbeatBar.vue +++ b/src/components/HeartbeatBar.vue @@ -171,7 +171,7 @@ export default { }, getBeatTitle(beat) { - return `${this.$root.datetime(beat.time)}` + ((beat.msg) ? ` - ${beat.msg}` : ``); + return `${this.$root.datetime(beat.time)}` + ((beat.msg) ? ` - ${beat.msg}` : ""); }, // Toggling the activeSibling class on hover over the current hover item From 92a43e1f305b86e58f61ebc91cf8bc3380581520 Mon Sep 17 00:00:00 2001 From: Louis Lam <louislam@users.noreply.github.com> Date: Sat, 14 May 2022 14:11:43 +0800 Subject: [PATCH 109/124] Make the sibling beats a bit smaller --- src/components/HeartbeatBar.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/HeartbeatBar.vue b/src/components/HeartbeatBar.vue index deb2bc23..10b1f761 100644 --- a/src/components/HeartbeatBar.vue +++ b/src/components/HeartbeatBar.vue @@ -242,7 +242,7 @@ export default { } &.active-sibling { - transform: scale(1.3); + transform: scale(1.15); transition: all ease 0.15s; } } From cec052183434f98edac6da6c81232a680050addc Mon Sep 17 00:00:00 2001 From: Louis Lam <louislam@users.noreply.github.com> Date: Sat, 14 May 2022 14:36:40 +0800 Subject: [PATCH 110/124] [Discord] Fix ping type should no port, update better naming --- server/notification-providers/discord.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/server/notification-providers/discord.js b/server/notification-providers/discord.js index c981d59d..77b04d9d 100644 --- a/server/notification-providers/discord.js +++ b/server/notification-providers/discord.js @@ -22,20 +22,22 @@ class Discord extends NotificationProvider { return okMsg; } - let url; + let address; switch (monitorJSON["type"]) { - case "dns": case "ping": + address = monitorJSON["hostname"]; + break; case "port": + case "dns": case "steam": - url = monitorJSON["hostname"]; + address = monitorJSON["hostname"]; if (monitorJSON["port"]) { - url += ":" + monitorJSON["port"]; + address += ":" + monitorJSON["port"]; } break; default: - url = monitorJSON["url"]; + address = monitorJSON["url"]; break; } @@ -53,8 +55,8 @@ class Discord extends NotificationProvider { value: monitorJSON["name"], }, { - name: "Service URL", - value: url, + name: "Service URL / Address", + value: address, }, { name: "Time (UTC)", @@ -89,7 +91,7 @@ class Discord extends NotificationProvider { }, { name: "Service URL", - value: url.startsWith("http") ? "[Visit Service](" + url + ")" : url, + value: address.startsWith("http") ? "[Visit Service](" + address + ")" : address, }, { name: "Time (UTC)", From 4178b003a2fbd2cfea07cc990e610ba006c747ca Mon Sep 17 00:00:00 2001 From: AnnAngela <naganjue@vip.qq.com> Date: Sat, 14 May 2022 15:27:55 +0800 Subject: [PATCH 111/124] fix `value` --- src/components/notifications/Pushover.vue | 46 +++++++++++------------ 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/src/components/notifications/Pushover.vue b/src/components/notifications/Pushover.vue index 83261deb..4bf6edb3 100644 --- a/src/components/notifications/Pushover.vue +++ b/src/components/notifications/Pushover.vue @@ -18,29 +18,29 @@ </select> <label for="pushover-sound" class="form-label">{{ $t("Notification Sound") }}</label> <select id="pushover-sound" v-model="$parent.notification.pushoversounds" class="form-select"> - <option>{{ $t("pushoversounds pushover") }}</option> - <option>{{ $t("pushoversounds bike") }}</option> - <option>{{ $t("pushoversounds bugle") }}</option> - <option>{{ $t("pushoversounds cashregister") }}</option> - <option>{{ $t("pushoversounds classical") }}</option> - <option>{{ $t("pushoversounds cosmic") }}</option> - <option>{{ $t("pushoversounds falling") }}</option> - <option>{{ $t("pushoversounds gamelan") }}</option> - <option>{{ $t("pushoversounds incoming") }}</option> - <option>{{ $t("pushoversounds intermission") }}</option> - <option>{{ $t("pushoversounds magic") }}</option> - <option>{{ $t("pushoversounds mechanical") }}</option> - <option>{{ $t("pushoversounds pianobar") }}</option> - <option>{{ $t("pushoversounds siren") }}</option> - <option>{{ $t("pushoversounds spacealarm") }}</option> - <option>{{ $t("pushoversounds tugboat") }}</option> - <option>{{ $t("pushoversounds alien") }}</option> - <option>{{ $t("pushoversounds climb") }}</option> - <option>{{ $t("pushoversounds persistent") }}</option> - <option>{{ $t("pushoversounds echo") }}</option> - <option>{{ $t("pushoversounds updown") }}</option> - <option>{{ $t("pushoversounds vibrate") }}</option> - <option>{{ $t("pushoversounds none") }}</option> + <option value="pushover">{{ $t("pushoversounds pushover") }}</option> + <option value="bike">{{ $t("pushoversounds bike") }}</option> + <option value="bugle">{{ $t("pushoversounds bugle") }}</option> + <option value="cashregister">{{ $t("pushoversounds cashregister") }}</option> + <option value="classical">{{ $t("pushoversounds classical") }}</option> + <option value="cosmic">{{ $t("pushoversounds cosmic") }}</option> + <option value="falling">{{ $t("pushoversounds falling") }}</option> + <option value="gamelan">{{ $t("pushoversounds gamelan") }}</option> + <option value="incoming">{{ $t("pushoversounds incoming") }}</option> + <option value="intermission">{{ $t("pushoversounds intermission") }}</option> + <option value="magic">{{ $t("pushoversounds magic") }}</option> + <option value="mechanical">{{ $t("pushoversounds mechanical") }}</option> + <option value="pianobar">{{ $t("pushoversounds pianobar") }}</option> + <option value="siren">{{ $t("pushoversounds siren") }}</option> + <option value="spacealarm">{{ $t("pushoversounds spacealarm") }}</option> + <option value="tugboat">{{ $t("pushoversounds tugboat") }}</option> + <option value="alien">{{ $t("pushoversounds alien") }}</option> + <option value="climb">{{ $t("pushoversounds climb") }}</option> + <option value="persistent">{{ $t("pushoversounds persistent") }}</option> + <option value="echo">{{ $t("pushoversounds echo") }}</option> + <option value="updown">{{ $t("pushoversounds updown") }}</option> + <option value="vibrate">{{ $t("pushoversounds vibrate") }}</option> + <option value="none">{{ $t("pushoversounds none") }}</option> </select> <div class="form-text"> <span style="color: red;"><sup>*</sup></span>{{ $t("Required") }} From 398219f847571619bbb3e7124bf27e7915874a69 Mon Sep 17 00:00:00 2001 From: Louis Lam <louislam@users.noreply.github.com> Date: Tue, 17 May 2022 01:03:51 +0800 Subject: [PATCH 112/124] Update to 1.16.0-beta.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e9b7003b..1ba25baf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "uptime-kuma", - "version": "1.15.1", + "version": "1.16.0-beta.0", "license": "MIT", "repository": { "type": "git", From 175556f9fc0ae5411440451d1ba7ab7d310172f8 Mon Sep 17 00:00:00 2001 From: Alexis Lefebvre <alexislefebvre@users.noreply.github.com> Date: Mon, 16 May 2022 20:30:16 +0200 Subject: [PATCH 113/124] s/cros/CORS --- src/pages/StatusPage.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/StatusPage.vue b/src/pages/StatusPage.vue index 00857db5..22608116 100644 --- a/src/pages/StatusPage.vue +++ b/src/pages/StatusPage.vue @@ -688,7 +688,7 @@ export default { }, statusPageLogoLoaded(eventPayload) { - // Remark: may not work in dev, due to cros + // Remark: may not work in dev, due to CORS favicon.image(eventPayload.target); }, From 9fc5a3329f7db09733c40c27726fb3b05e903c6f Mon Sep 17 00:00:00 2001 From: Louis Lam <louislam@users.noreply.github.com> Date: Wed, 18 May 2022 20:16:50 +0800 Subject: [PATCH 114/124] Revert #1208, due to the break animation --- src/assets/app.scss | 2 +- src/components/HeartbeatBar.vue | 47 +++--------------------------- src/components/PublicGroupList.vue | 8 ++--- 3 files changed, 9 insertions(+), 48 deletions(-) diff --git a/src/assets/app.scss b/src/assets/app.scss index 8eaff499..c3f2fa79 100644 --- a/src/assets/app.scss +++ b/src/assets/app.scss @@ -367,7 +367,7 @@ textarea.form-control { .item { display: block; text-decoration: none; - padding: 15px; + padding: 13px 15px 10px 15px; border-radius: 10px; transition: all ease-in-out 0.15s; diff --git a/src/components/HeartbeatBar.vue b/src/components/HeartbeatBar.vue index 10b1f761..ce888a98 100644 --- a/src/components/HeartbeatBar.vue +++ b/src/components/HeartbeatBar.vue @@ -1,6 +1,6 @@ <template> <div ref="wrap" class="wrap" :style="wrapStyle"> - <div class="hp-bar-big d-flex" :style="barStyle"> + <div class="hp-bar-big" :style="barStyle"> <div v-for="(beat, index) in shortBeatList" :key="index" @@ -8,11 +8,7 @@ :class="{ 'empty' : (beat === 0), 'down' : (beat.status === 0), 'pending' : (beat.status === 2) }" :style="beatStyle" :title="getBeatTitle(beat)" - @mouseenter="toggleActivateSibling" - @mouseleave="toggleActivateSibling" - > - <div class="beat-inner" /> - </div> + /> </div> </div> </template> @@ -174,28 +170,6 @@ export default { return `${this.$root.datetime(beat.time)}` + ((beat.msg) ? ` - ${beat.msg}` : ""); }, - // Toggling the activeSibling class on hover over the current hover item - toggleActivateSibling(e) { - // Variable definition - const element = e.target; - const previous = element.previousSibling; - const next = element.nextSibling; - - // Return if the hovered element has empty class - if (element.classList.contains("empty")) { - return; - } - - // Check if Previous Sibling is heartbar element and doesn't have the empty class - if (previous.children && !previous.classList.contains("empty")) { - previous.classList.toggle("active-sibling"); - } - - // Check if Next Sibling is heartbar element and doesn't have the empty class - if (next.children && !next.classList.contains("empty")) { - next.classList.toggle("active-sibling"); - } - } }, }; </script> @@ -211,10 +185,9 @@ export default { .hp-bar-big { .beat { + display: inline-block; background-color: $primary; border-radius: $border-radius; - display: inline-block; - transition: all ease 0.6s; &.empty { background-color: aliceblue; @@ -228,23 +201,11 @@ export default { background-color: $warning; } - .beat-inner { - border-radius: $border-radius; - display: inline-block; - height: 100%; - width: 5px; - } - &:not(.empty):hover { - transition: all ease 0.15s; + transition: all ease-in-out 0.15s; opacity: 0.8; transform: scale(var(--hover-scale)); } - - &.active-sibling { - transform: scale(1.15); - transition: all ease 0.15s; - } } } diff --git a/src/components/PublicGroupList.vue b/src/components/PublicGroupList.vue index 4d0ada5c..98c0b7ff 100644 --- a/src/components/PublicGroupList.vue +++ b/src/components/PublicGroupList.vue @@ -33,19 +33,19 @@ <template #item="monitor"> <div class="item"> <div class="row"> - <div class="col-9 col-md-8 small-padding d-flex align-items-center flex-wrap"> - <div class="info d-flex align-items-center gap-3 w-100"> + <div class="col-9 col-md-8 small-padding"> + <div class="info"> <font-awesome-icon v-if="editMode" icon="arrows-alt-v" class="action drag me-3" /> <font-awesome-icon v-if="editMode" icon="times" class="action remove me-3" @click="removeMonitor(group.index, monitor.index)" /> <Uptime :monitor="monitor.element" type="24" :pill="true" /> {{ monitor.element.name }} </div> - <div v-if="showTags && monitor.element.tags.length > 0" class="tags"> + <div v-if="showTag" class="tags"> <Tag v-for="tag in monitor.element.tags" :key="tag" :item="tag" :size="'sm'" /> </div> </div> - <div :key="$root.userHeartbeatBar" class="col-3 col-md-4 d-flex align-items-center"> + <div :key="$root.userHeartbeatBar" class="col-3 col-md-4"> <HeartbeatBar size="small" :monitor-id="monitor.element.id" /> </div> </div> From 4c3aa20eb0720088894f828a17fcb17a1a6f38fb Mon Sep 17 00:00:00 2001 From: MrEddX <66828538+MrEddX@users.noreply.github.com> Date: Wed, 18 May 2022 22:18:58 +0300 Subject: [PATCH 115/124] Update bg-BG.js Added new fields --- src/languages/bg-BG.js | 51 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/src/languages/bg-BG.js b/src/languages/bg-BG.js index 94553bd0..6297062a 100644 --- a/src/languages/bg-BG.js +++ b/src/languages/bg-BG.js @@ -464,4 +464,55 @@ export default { "Domain Names": "Домейни", signedInDisp: "Вписан като {0}", signedInDispDisabled: "Удостоверяването е изключено.", + "Certificate Expiry Notification": "Известие за изтичане валидността на сертификата", + "API Username": "API Потребител", + "API Key": "API Ключ", + "Recipient Number": "Номер на получателя", + "From Name/Number": "От Име/Номер", + "Leave blank to use a shared sender number.": "Оставете празно, за да използвате споделен номер на подател.", + "Octopush API Version": "Octopush API версия", + "Legacy Octopush-DM": "Octopush-DM старa версия", + endpoint: "крайна точка", + octopushAPIKey: "\"API ключ\" от HTTP API удостоверяване в контролния панел", + octopushLogin: "\"Вписване\" от HTTP API удостоверяване в контролния панел", + promosmsLogin: "API Потребителско име", + promosmsPassword: "API Парола", + "pushoversounds pushover": "Pushover (по подразбиране)", + "pushoversounds bike": "Велосипед", + "pushoversounds bugle": "Тромпет", + "pushoversounds cashregister": "Касов апарат", + "pushoversounds classical": "Класическа музика", + "pushoversounds cosmic": "Космически", + "pushoversounds falling": "Падащ", + "pushoversounds gamelan": "Игра в мрежа", + "pushoversounds incoming": "Входящ", + "pushoversounds intermission": "Прекъсване", + "pushoversounds magic": "Магия", + "pushoversounds mechanical": "Механичен", + "pushoversounds pianobar": "Пиано бар", + "pushoversounds siren": "Сирена", + "pushoversounds spacealarm": "Космическа аларма", + "pushoversounds tugboat": "Буксир", + "pushoversounds alien": "Извънземна аларма (дълъг)", + "pushoversounds climb": "Изкачване (дълъг)", + "pushoversounds persistent": "Постоянен (дълъг)", + "pushoversounds echo": "Pushover ехо (дълъг)", + "pushoversounds updown": "Горе долу (дълъг)", + "pushoversounds vibrate": "Само вибрация", + "pushoversounds none": "Без (тих)", + pushyAPIKey: "Таен API ключ", + pushyToken: "Токен на устройство", + "Show update if available": "Покажи актуализация, ако е налична", + "Also check beta release": "Проверявай и за бета версии", + "Using a Reverse Proxy?": "Използвате ревърс прокси?", + "Check how to config it for WebSocket": "Проверете как да го конфигурирате за WebSocket", + "Steam Game Server": "Steam Game сървър", + "Most likely causes:": "Най-вероятни причини:", + "The resource is no longer available.": "Ресурсът вече не е наличен.", + "There might be a typing error in the address.": "Възможно е да е допусната грешка при изписването на адреса.", + "What you can try:": "Може да опитате:", + "Retype the address.": "Повторно въвеждане на адреса.", + "Go back to the previous page.": "Да се върнете към предишната страница.", + "Coming Soon": "Очаквайте скоро", + wayToGetClickSendSMSToken: "Може да получите API потребителско име и API ключ от {0} .", }; From 93e5023eadf5752e36cc02097f91867065c49574 Mon Sep 17 00:00:00 2001 From: Yoswaris Lawpaiboon <22832362+kiznick@users.noreply.github.com> Date: Thu, 19 May 2022 19:44:59 +0700 Subject: [PATCH 116/124] Translate to Thai ! --- src/components/settings/Security.vue | 6 + src/i18n.js | 1 + src/languages/th-TH.js | 518 +++++++++++++++++++++++++++ 3 files changed, 525 insertions(+) create mode 100644 src/languages/th-TH.js diff --git a/src/components/settings/Security.vue b/src/components/settings/Security.vue index 87bb745a..17e161a9 100644 --- a/src/components/settings/Security.vue +++ b/src/components/settings/Security.vue @@ -234,6 +234,12 @@ <p>Vui lòng <strong>cẩn thận</strong>.</p> </template> + <template v-else-if="$i18n.locale === 'th-TH' "> + <p>คุณต้องการที่จะ <strong>ปิดใช้งานระบบรับรองความถูกต้องใช่หรือไม่</strong>?</p> + <p>ระบบนี้ถูกออกแบบมาเพื่อการใช้งานกับระบบรับรองความถูกต้องของบุคคลที่สามเช่น Cloudflare Access, Authelia หรือวิธีการอื่น ๆ</p> + <p>โปรดใช้ความระมัดระวังในการเลือกใช้งานระบบนี้ !</p> + </template> + <!-- English (en) --> <template v-else> <p>Are you sure want to <strong>disable authentication</strong>?</p> diff --git a/src/i18n.js b/src/i18n.js index 83a31991..82e95d3d 100644 --- a/src/i18n.js +++ b/src/i18n.js @@ -31,6 +31,7 @@ const languageList = { "vi-VN": "Tiếng Việt", "zh-TW": "繁體中文 (台灣)", "uk-UA": "Український", + "th-TH": "ไทย", }; let messages = { diff --git a/src/languages/th-TH.js b/src/languages/th-TH.js new file mode 100644 index 00000000..b313deb0 --- /dev/null +++ b/src/languages/th-TH.js @@ -0,0 +1,518 @@ +export default { + languageName: "ไทย", + checkEverySecond: "ตรวจสอบทุก {0} วินาที", + retryCheckEverySecond: "ลองใหม่ทุก {0} วินาที", + retriesDescription: "จำนวนครั้งสูงสุดที่จะลองก่อนบริการถูกระบุว่าไม่สามารถใช้งานได้และส่งการแจ้งเตือน", + ignoreTLSError: "ไม่สนใจข้อผิดพลาด TLS/SSL สำหรับเว็บไซต์ HTTPS", + upsideDownModeDescription: "กลับด้านสถานะ เช่น ถ้าบริการสามารถใช้งานได้จะถูกเปลี่ยนเป็นใช้งานไม่ได้", + maxRedirectDescription: "จำนวนครั้งสูงสุดที่จะเปลี่ยนเส้นทาง, ตั่งเป็น 0 เพื่อปิดการเปลี่ยนเส้นทาง", + acceptedStatusCodesDescription: "เลือกรหัสสถานะที่ถือว่าการตอบกลับสำเร็จ", + passwordNotMatchMsg: "รหัสผ่านไม่ตรงกัน", + notificationDescription: "การแจ้งเตือนต้องกำหนดให้มอนิเตอร์เพื่อให้สามารถใช้งานได้", + keywordDescription: "ค้นหาคำสำคัญใน HTML หรือ JSON ของการตอบกลับ, คำสำคัญต้องคำนึงถึงตัวพิมพ์เล็กและตัวพิมพ์ใหญ่", + pauseDashboardHome: "หยุดชั่วคราว", + deleteMonitorMsg: "คุณแน่ใจหรือไม่ที่จะลบมอนิเตอร์?", + deleteNotificationMsg: "คุณแน่ใจหรือไม่ที่จะลบการแจ้งเตือนสำหรับมอนิเตอร์ทั้งหมด?", + resolverserverDescription: "Cloudflare เป็นเซิร์ฟเวอร์ค้นหาเริ่มต้น, คุณสามารถเปลี่ยนเซิร์ฟเวอร์ได้ตลอดเวลา", + rrtypeDescription: "เลือกประเภท DNS Record ที่คุณต้องการจะมอนิเตอร์", + pauseMonitorMsg: "คุณแน่ใจหรือไม่ที่จะหยุดมอนิเตอร์ชั่วคราว?", + enableDefaultNotificationDescription: "การแจ้งเตือนนี้จะถูกเปิดโดนค่าเริ่มต้นสำหรับมอนิเตอร์ใหม่, คุณสามารถปิดการแจ้งเตือนสำหรับแต่ละมอนิเตอร์ได้", + clearEventsMsg: "คุณแน่ใจหรือไม่ที่จะลบเหตุการณ์ทั้งหมดสำหรับมอนิเตอร์นี้?", + clearHeartbeatsMsg: "คุณแน่ใจหรือไม่ที่จะลบประวัติการตรวจสอบทั้งหมดสำหรับมอนิเตอร์นี้?", + confirmClearStatisticsMsg: "คุณแน่ใจหรือไม่ที่จะลบสถิติทั้งหมด?", + importHandleDescription: "เลือก \"ข้ามรายการที่มีอยู่แล้ว\" ถ้าคุณต้องการข้ามทุกมอนิเตอร์หรือการแจ้งเตือนที่มีชื่อซ้ำกัน, \"เขียนทับ\" จะลบทุกมอนิเตอร์หรือการแจ้งเตือนที่มีชื่อซ้ำกัน", + confirmImportMsg: "คุณแน่ใจหรือไม่ที่จะนำเข้าข้อมูลสำรอง, กรุณาตรวจสอบว่าคุณเลือกข้อมูลที่ถูกต้อง", + twoFAVerifyLabel: "โปรดกรอกกุญแจ 2FA ของคุณเพื่อยืนยัน:", + tokenValidSettingsMsg: "กุญแจถูกต้อง, ตอนนี้คุณสามารถบันทึกการตั้งค่า 2FA ของคุณได้แล้ว", + confirmEnableTwoFAMsg: "คุณแน่ใจหรือไม่ที่จะเปิดใช้งาน 2FA?", + confirmDisableTwoFAMsg: "คุณแน่ใจหรือไม่ที่จะปิดใช้งาน 2FA?", + Settings: "การตั่งค่า", + Dashboard: "แผงควบคุม", + "New Update": "อัพเดทใหม่", + Language: "ภาษา", + Appearance: "รูปร่าง", + Theme: "หน้าตา", + General: "ทั่วไป", + "Primary Base URL": "URL หลัก", + Version: "เวอร์ชั่น", + "Check Update On GitHub": "ตรวจสอบการอัปเดตบน GitHub", + List: "รายการ", + Add: "เพิ่ม", + "Add New Monitor": "เพิ่มมอนิเตอร์ใหม่", + "Quick Stats": "สถิติด่วน", + Up: "ใช้งานได้", + Down: "ไม่สามารถใช้งานได้", + Pending: "รอดำเนินการ", + Unknown: "ไม่ทราบ", + Pause: "หยุดชั่วคราว", + Name: "ชื่อ", + Status: "สถานะ", + DateTime: "วันที่และเวลา", + Message: "ข้อความ", + "No important events": "ไม่มีกิจกรรมที่สำคัญ", + Resume: "ดำเนินการต่อ", + Edit: "แก้ไข", + Delete: "ลบ", + Current: "ปัจจุบัน", + Uptime: "เวลาที่ใช้งาน", + "Cert Exp.": "วันหมดอายุใบรับรอง", + days: "วัน", + day: "วัน", + "-day": "-วัน", + hour: "ชั่วโมง", + "-hour": "-ชั่วโมง", + Response: "การตอบสนอง", + Ping: "การตอบสนอง", + "Monitor Type": "ประเภทมอนิเตอร์", + Keyword: "คำสำคัญ", + "Friendly Name": "ชื่อที่เป็นมิตร", + URL: "URL", + Hostname: "ชื่อโฮสต์", + Port: "พอร์ต", + "Heartbeat Interval": "ระยะห่างระหว่างการทดสอบ", + Retries: "จำนวนครั้งที่จะลองใหม่", + "Heartbeat Retry Interval": "ระยะห่างระหว่างการทดสอบใหม่หลังจากไม่สำเร็จ", + Advanced: "ขั้นสูง", + "Upside Down Mode": "โหมดกลับด้าน", + "Max. Redirects": "จำนวนการเปลี่ยนเส้นทางสูงสุด", + "Accepted Status Codes": "รหัสสถานะที่ยอมรับ", + "Push URL": "URL เป้าหมาย", + needPushEvery: "คุณควรเรียก URL นี้ทุก {0} วินาที", + pushOptionalParams: "ตัวแปรเสริม: {0}", + Save: "บันทึก", + Notifications: "การแจ้งเตือน", + "Not available, please setup.": "ไม่พร้อมใช้งาน, กรุณาตั้งค่า", + "Setup Notification": "ตั้งค่าการแจ้งเตือน", + Light: "สว่าง", + Dark: "มืด", + Auto: "อัตโนมัติ", + "Theme - Heartbeat Bar": "หน้าตา - แถบการตอบสนอง", + Normal: "ปกติ", + Bottom: "ด้านล่าง", + None: "ไม่มี", + Timezone: "เขตเวลา", + "Search Engine Visibility": "การมองเห็นของเครื่องมือค้นหา", + "Allow indexing": "อนุญาตให้สร้างดัชนี", + "Discourage search engines from indexing site": "ปฏิเสธเครื่องมือค้นหาไม่ให้สร้างดัชนีของเว็บไซต์", + "Change Password": "เปลี่ยนรหัสผ่าน", + "Current Password": "รหัสผ่านปัจจุบัน", + "New Password": "รหัสผ่านใหม่", + "Repeat New Password": "ยืนยันรหัสผ่านใหม่", + "Update Password": "อัพเดทรหัสผ่าน", + "Disable Auth": "ปิดใช้งานการตรวจสอบสิทธิ์", + "Enable Auth": "เปิดใช้งานการตรวจสอบสิทธิ์", + Logout: "ออกจากระบบ", + Leave: "ออก", + "I understand, please disable": "ฉันเข้าใจแล้ว, กรุณาปิดการใช้งาน", + Confirm: "ยืนยัน", + Yes: "ใช่", + No: "ไม่", + Username: "ชื่อผู้ใช้", + Password: "รหัสผ่าน", + "Remember me": "คงอยู่ในระบบ", + Login: "เข้าสู่ระบบ", + "No Monitors, please": "ไม่มีมอนิเตอร์, กรุณา", + "add one": "สร้าง", + "Notification Type": "ประเภทการแจ้งเตือน", + Email: "อีเมล", + Test: "ทดสอบ", + "Certificate Info": "ข้อมูลใบรับรอง", + "Resolver Server": "เซิร์ฟเวอร์ทีค้นหา", + "Resource Record Type": "ประเภท DNS Record", + "Last Result": "ผลล่าสุด", + "Create your admin account": "สร้างบัญชีผู้ดูแลระบบ", + "Repeat Password": "ยืนยันรหัสผ่าน", + "Import Backup": "นำเข้าข้อมูลสำรอง", + "Export Backup": "ส่งออกข้อมูลสำรอง", + Export: "ส่งออก", + Import: "นำเข้า", + respTime: "ระยะเวลาการตอบสนอง (ms)", + notAvailableShort: "ไม่สามารถใช้งานได้", + "Default enabled": "เปิดใช้งานโดยค่าเริ่มต้น", + "Apply on all existing monitors": "ใช้กับมอนิเตอร์ทั้งหมด", + Create: "สร้าง", + "Clear Data": "ล้างข้อมูล", + Events: "เหตุการณ์", + Heartbeats: "ประวัติการตรวจสอบ", + "Auto Get": "ดึงอัตโนมัติ", + backupDescription: "คุณสามารถสำรองข้อมูลการแจ้งเตือนและมอนิเตอร์ทั้งหมดได้ในไฟล์ JSON", + backupDescription2: "หมายเหตุ : ประวัติและข้อมูลกิจกรรมจะไม่ถูกสำรอง", + backupDescription3: "ข้อมูลที่ละเอียดอ่อนเช่นกุญแจการแจ้งเตือนจะรวมอยู่ในไฟล์ข้อมูลสำรอง, โปรดเก็บข้อมูลสำรองอย่างปลอดภัย", + alertNoFile: "กรุณาเลือกไฟล์ที่จะใช้งาน", + alertWrongFileType: "กรุณาเลือกไฟล์ที่เป็น JSON", + "Clear all statistics": "ล้างข้อมูลสถิติทั้งหมด", + "Skip existing": "ข้ามรายการที่มีอยู่แล้ว", + Overwrite: "เขียนทับ", + Options: "ตัวเลือก", + "Keep both": "เก็บทั้งสอง", + "Verify Token": "ยืนยันกุญแจ", + "Setup 2FA": "ติดตั้ง 2FA", + "Enable 2FA": "เปิดใช้งาน 2FA", + "Disable 2FA": "ปิดใช้งาน 2FA", + "2FA Settings": "ตั่งค่า 2FA", + "Two Factor Authentication": "การตรวจสอบสิทธิ์สองปัจจัย", + Active: "ใช้งาน", + Inactive: "ไม่ใช้งาน", + Token: "กุญแจ", + "Show URI": "แสดง URI", + Tags: "แท็ก", + "Add New below or Select...": "เพิ่มใหม่ด้านล่างหรือเลือก...", + "Tag with this name already exist.": "แท็กที่มีชื่อนี้มีอยู่แล้ว", + "Tag with this value already exist.": "แท็กที่มีข้อมูลนี้มีอยู่แล้ว", + color: "สี", + "value (optional)": "ข้อมูล (ไม่จำเป็น)", + Gray: "เทา", + Red: "แดง", + Orange: "ส้ม", + Green: "เขียว", + Blue: "น้ำเงิน", + Indigo: "ม่วง", + Purple: "ม่วง", + Pink: "ชมพู", + "Search...": "ค้นหา...", + "Avg. Ping": "ค่า Ping เฉลี่ย", + "Avg. Response": "ค่า Response เฉลี่ย", + "Entry Page": "หน้าต้อนรับ", + statusPageNothing: "ไม่มีอะไรตรงนี้ !, กรุณาเพิ่มกลุ่มหรือมอนิเตอร์", + "No Services": "ไม่มีบริการ", + "All Systems Operational": "บริการทั้งหมดทำงานได้ปกติ", + "Partially Degraded Service": "บริการมีปัญหาบางส่วน", + "Degraded Service": "บริการมีปัญหา", + "Add Group": "เพิ่มกลุ่ม", + "Add a monitor": "เพิ่มมอนิเตอร์", + "Edit Status Page": "แก้ไขหน้าสถานะ", + "Go to Dashboard": "ไปที่หน้าควบคุม", + "Status Page": "หน้าสถานะ", + "Status Pages": "หน้าสถานะ", + defaultNotificationName: "การแจ้งเตือน {notification} ของฉัน ({number})", + here: "ที่นี่", + Required: "ต้องการ", + telegram: "Telegram", + "Bot Token": "กุญแจของบอท", + wayToGetTelegramToken: "คุณสามารถรับกุญแจได้จาก {0}.", + "Chat ID": "ไอดีแชท", + supportTelegramChatID: "รองรับ แชทส่วนตัว, แชทกลุ่ม, ไอดีแชท", + wayToGetTelegramChatID: "คุณสามารถรับ ID แชทของคุณได้โดยส่งข้อความไปยังบอทและไปที่ URL นี้เพื่อดู chat_id :", + "YOUR BOT TOKEN HERE": "กุญแจของบอทของคุณที่นี่", + chatIDNotFound: "ไม่พบไอดีแชท, กรุณาส่งข้อความไปที่บอท", + webhook: "Webhook", + "Post URL": "URL โพสต์", + "Content Type": "ประเภทเนื้อหา", + webhookJsonDesc: "{0} ดีสำหรับเซิร์ฟเวอร์ HTTP สมัยใหม่เช่น Express.js", + webhookFormDataDesc: "{multipart} ดีสำหรับ PHP, JSON จะต้องถูกประมวลผลด้วย {decodeFunction}", + smtp: "Email (SMTP)", + secureOptionNone: "None / STARTTLS (25, 587)", + secureOptionTLS: "TLS (465)", + "Ignore TLS Error": "Ignore TLS Error", + "From Email": "From Email", + emailCustomSubject: "Custom Subject", + "To Email": "To Email", + smtpCC: "CC", + smtpBCC: "BCC", + discord: "Discord", + "Discord Webhook URL": "Discord Webhook URL", + wayToGetDiscordURL: "คุณสามารถรับได้โดยการไปที่ Server Settings -> Integrations -> Create Webhook", + "Bot Display Name": "ชื่อบอท", + "Prefix Custom Message": "คำนำหน้าข้อความที่กำหนดเอง", + "Hello @everyone is...": "สวัสดี {'@'}everyone นี่...", + teams: "Microsoft Teams", + "Webhook URL": "Webhook URL", + wayToGetTeamsURL: "คุณสามารถเรียนรู้วิธีการสร้าง Webhook URL {0}", + signal: "Signal", + Number: "หมายเลข", + Recipients: "ผู้รับ", + needSignalAPI: "คุณต้องมี Signal Client ที่มี Rest APIl", + wayToCheckSignalURL: "คุณสามารถตรวจสอบ URL นี้เพื่อดูวิธีตั้งค่า :", + signalImportant: "สำคัญ: คุณไม่สามารถผสมกลุ่มและตัวเลขในผู้รับได้!", + gotify: "Gotify", + "Application Token": "กุญแจของแอพพลิเคชั่น", + "Server URL": "Server URL", + Priority: "ลำดับความสำคัญ", + slack: "Slack", + "Icon Emoji": "Icon Emoji", + "Channel Name": "ชื่อห้อง", + "Uptime Kuma URL": "Uptime Kuma URL", + aboutWebhooks: "ข้อมูลเพิ่มเติมสำหรับ Webhooks : {0}", + aboutChannelName: "ใส่ชื่อห้องบน {0} ในช่องชื่อห้องถ้าต้องการที่จะข้าม Webhook, เช่น: #ช่องอื่นๆ", + aboutKumaURL: "ถ้าคุณไม่ใส่ข้อมูลในช่อง Uptime Kuma URL ค่าเริ่มต้นจะเป็นจะเป็น Uptime Kuma Github", + emojiCheatSheet: "ตาราง Emoji : {0}", + "rocket.chat": "Rocket.Chat", + pushover: "Pushover", + pushy: "Pushy", + PushByTechulus: "Push by Techulus", + octopush: "Octopush", + promosms: "PromoSMS", + clicksendsms: "ClickSend SMS", + lunasea: "LunaSea", + apprise: "Apprise (รองรับการแจ้งเตือนมากกว่า 50 บริการ)", + GoogleChat: "Google Chat (Google Workspace only)", + pushbullet: "Pushbullet", + line: "Line Messenger", + mattermost: "Mattermost", + "User Key": "กุญแจผู้ใช้งาน", + Device: "อุปกรณ์", + "Message Title": "หัวข้อข้อความ", + "Notification Sound": "เสียงแจ้งเตือน", + "More info on:": "ข้อมูลเพิ่มเติม : {0}", + pushoverDesc1: "ลำดับความสำตคญฉุกเฉิน (2) มีการหมดเวลาเริ่มต้น 30 วินาทีระหว่างลองใหม่และจะหมดอายุหลังจาก 1 ชั่วโมง", + pushoverDesc2: "ถ้าคุณต้องการจะส่งการแจ้งเตือนไปยังอุปกรณ์อื่น ๆ สามารถกำหนดได้ที่ช่องอุปกรณ์", + "SMS Type": "ประเภท SMS", + octopushTypePremium: "พรีเมี่ยม (เร็ว - แนะนำสำหรับการแจ้งเตือน)", + octopushTypeLowCost: "ต้นทุนต่ำ (ช้า - บางครั้งจะถูกบล็อกโดยผู้ให้บริการ)", + checkPrice: "ตรวจสอบราคาของ {0} :", + apiCredentials: "ข้อมูลการตรวจสอบสิทธิ์ API", + octopushLegacyHint: "คุณใช้เวอร์ชันดั้งเดิมของ Octopush (2011 - 2020) หรือเวอร์ชันใหม่หรือไม่?", + "Check octopush prices": "ตรวจสอบราคาของ Octopush {0}", + octopushPhoneNumber: "หมายเลขโทรศัพท์ (รูปแบบสากล เช่น +33612345678) ", + octopushSMSSender: "ชื่อผู้ส่ง SMS : ความยาว 3 - 11 ตัวอักษร, ตัวเลข และช่องว่าง (a-zA-Z0-9 )", + "LunaSea Device ID": "ไอดีอุปกรณ์ LunaSea", + "Apprise URL": "Apprise URL", + "Example:": "ตัวอย่าง : {0}", + "Read more:": "อ่านเพิ่มเติม : {0}", + "Status:": "สถานะ : {0}", + "Read more": "อ่านเพิ่มเติม", + appriseInstalled: "Apprise ถูกติดตั่งแล้ว", + appriseNotInstalled: "Apprise ยังไม่ถูกติดตั่ง {0}", + "Access Token": "กุญแจการเข้าถึง", + "Channel access token": "กุญแจการเข้าถึงของช่อง", + "Line Developers Console": "Line Developers Console", + lineDevConsoleTo: "Line Developers Console - {0}", + "Basic Settings": "การตั้งค่าพื้นฐาน", + "User ID": "ไอดีผู้ใช้", + "Messaging API": "Messaging API", + wayToGetLineChannelToken: "ขั้นแรกให้เข้า {0} สร้างผู้ให้บริการและช่องทาง (Messaging API) จากนั้นคุณจะได้รับกุญแจการเข้าถึงช่องและไอดีผู้ใช้จากรายการเมนูที่กล่าวถึงข้างต้น", + "Icon URL": "Icon URL", + aboutIconURL: "คุณสามารถระบุลิงก์ไปยังรูปภาพใน \"URL ไอคอน\" เพื่อแทนที่รูปภาพโปรไฟล์เริ่มต้น จะไม่ถูกใช้หากมีการตั้งค่า Icon Emoji", + aboutMattermostChannelName: "คุณลบล้างช่องเริ่มต้นที่ Webhook โพสต์ได้ด้วยการป้อนชื่อช่องลงในช่อง \"ชื่อช่อง\" ต้องเปิดใช้งานในการตั้งค่า Mattermost Webhook เช่น #ช่องอื่นๆ", + matrix: "Matrix", + promosmsTypeEco: "SMS ECO - ราคาถูก แต่ช้าและมักจะโอเวอร์โหลด จำกัดเฉพาะผู้รับโปแลนด์", + promosmsTypeFlash: "SMS FLASH - ข้อความจะแสดงบนอุปกรณ์ของผู้รับโดยอัตโนมัติ จำกัดเฉพาะผู้รับโปแลนด์", + promosmsTypeFull: "SMS FULL - SMS ระดับพรีเมียม คุณสามารถใช้ชื่อผู้ส่งของคุณได้ (คุณต้องลงทะเบียนชื่อก่อน) เชื่อถือได้สำหรับการแจ้งเตือน", + promosmsTypeSpeed: "SMS SPEED - ลำดับความสำคัญสูงสุดในระบบ รวดเร็วและเชื่อถือได้ แต่มีค่าใช้จ่ายสูง (ประมาณสองเท่าของราคาเต็ม SMS)", + promosmsPhoneNumber: "หมายเลขโทรศัพท์ (สำหรับผู้รับโปแลนด์ คุณสามารถข้ามรหัสพื้นที่ได้)", + promosmsSMSSender: "ชื่อผู้ส่ง SMS : ชื่อที่ลงทะเบียนล่วงหน้าหรือหนึ่งในค่าเริ่มต้น: InfoSMS, ข้อมูล SMS, MaxSMS, INFO, SMS", + "Feishu WebHookUrl": "Feishu WebHookURL", + matrixHomeserverURL: "URL ของโฮมเซิร์ฟเวอร์ (พร้อม http(s):// และพอร์ตเสริม)", + "Internal Room Id": "รหัสห้องภายใน", + matrixDesc1: "คุณค้นหารหัสห้องภายในได้โดยดูในส่วนขั้นสูงของการตั้งค่าห้องในไคลเอ็นต์ Matrix มันควรจะมีลักษณะเช่น !PMdRCpsIfLwsfjIye6:kiznick.server.", + matrixDesc2: "ขอแนะนำเป็นอย่างยิ่งให้คุณสร้างผู้ใช้ใหม่และอย่าใช้โทเค็นการเข้าถึงของผู้ใช้ Matrix ของคุณเอง เนื่องจากจะทำให้สามารถเข้าถึงบัญชีของคุณและห้องทั้งหมดที่คุณเข้าร่วมได้อย่างเต็มที่ ให้สร้างผู้ใช้ใหม่และเชิญเฉพาะห้องที่คุณต้องการรับการแจ้งเตือนแทน คุณสามารถรับโทเค็นเพื่อการเข้าถึงได้โดยเรียกใช้ {0}", + Method: "วิธี", + Body: "เนื้อหา", + Headers: "ส่วนหัว", + PushUrl: "Push URL", + HeadersInvalidFormat: "เนื้อหาคำขอส่วนหัวไม่ใช่ JSON ที่ถูกต้อง :", + BodyInvalidFormat: "เนื้อหาคำขอไม่ใช่ JSON ที่ถูกต้อง : ", + "Monitor History": "ประวัติมอนิเตอร์", + clearDataOlderThan: "เก็บข้อมูลมอนิเตอร์ {0} วัน", + PasswordsDoNotMatch: "รหัสผ่านไม่ตรงกัน", + records: "บันทึก", + "One record": "หนึ่งบันทึก", + steamApiKeyDescription: "สำหรับการมอนิเตอร์ Steam Game Server คุณต้องมี Steam Web-API key, คุณสามารถรสมัครได้จากที่นี่ : ", + "Current User": "ผู้ใช้ปัจจุบัน", + topic: "หัวข้อ", + topicExplanation: "MQTT หัวข้อที่จะมอนิเตอร์", + successMessage: "ข้อความที่จะถือว่าประสบความสำเร็จ", + successMessageExplanation: "MQTT ข้อความที่จะถือว่าประสบความสำเร็จ", + recent: "ล่าสุด", + Done: "สำเร็จ", + Info: "ข้อมูล", + Security: "ความปลอดภัย", + "Steam API Key": "Steam API Key", + "Shrink Database": "ย่อฐานข้อมูล", + "Pick a RR-Type...": "เลือกชนิด DNS Record", + "Pick Accepted Status Codes...": "เลือกสถานะที่ยอมรับ...", + Default: "ค่าเริ่มต้น", + "HTTP Options": "ตัวเลือก HTTP", + "Create Incident": "สร้างเหตุการณ์", + Title: "หัวข้อ", + Content: "เนื้อหา", + Style: "สไตล์", + info: "ข้อมูล", + warning: "แจ้งเตือน", + danger: "อันตราย", + primary: "หลัก", + light: "สว่าง", + dark: "มืด", + Post: "โพสต์", + "Please input title and content": "กรุณาใส่ชื่อและเนื้อหา", + Created: "สร้าง", + "Last Updated": "อัพเดทล่าสุด", + Unpin: "เลิกตรึง", + "Switch to Light Theme": "เปลี่ยนเป็นแบบสว่าง", + "Switch to Dark Theme": "เปลี่ยนเป็นแบบมืด", + "Show Tags": "แสดงแท็ก", + "Hide Tags": "ซ่อนแท็ก", + Description: "รายละเอียด", + "No monitors available.": "ไม่มีมอนิเตอร์ที่สามารถใช้งานได้", + "Add one": "เพิ่ม", + "No Monitors": "ไม่มีมอนิเตอร์", + "Untitled Group": "กลุ่มที่ไม่มีชื่อ", + Services: "บริการ", + Discard: "ทิ้ง", + Cancel: "ยกเลิก", + "Powered by": "ขับเคลื่อนโดย", + shrinkDatabaseDescription: "ทริกเกอร์ฐานข้อมูล VACUUM สำหรับ SQLite หากฐานข้อมูลของคุณถูกสร้างขึ้นหลังจาก 1.10.0 แสดงว่า AUTO_VACUUM เปิดใช้งานอยู่แล้วและไม่จำเป็นต้องดำเนินการนี้", + serwersms: "SerwerSMS.pl", + serwersmsAPIUser: "API Username (incl. webapi_ prefix)", + serwersmsAPIPassword: "API Password", + serwersmsPhoneNumber: "หมายเลขโทรศัพท์", + serwersmsSenderName: "ชื่อผู้ส่ง SMS (ลงทะเบียนผ่านหน้าควบคุม)", + stackfield: "Stackfield", + Customize: "ปรับแต่ง", + "Custom Footer": "ส่วนท้ายที่กำหนดเอง", + "Custom CSS": "CSS ที่กำหนดเอง", + smtpDkimSettings: "ตั่งค่า DKIM", + smtpDkimDesc: "โปรดดู Nodemailer DKIM {0} สำหรับการใช้งาน", + documentation: "เอกสาร", + smtpDkimDomain: "ชื่อโดเมน", + smtpDkimKeySelector: "Key Selector", + smtpDkimPrivateKey: "Private Key", + smtpDkimHashAlgo: "อัลกอริทึมแฮช (ไม่บังคับ)", + smtpDkimheaderFieldNames: "คีย์ส่วนหัวเพื่อลงชื่อ (ไม่บังคับ)", + smtpDkimskipFields: "Header Keys ไม่ต้องเซ็น (ไม่บังคับ)", + gorush: "Gorush", + alerta: "Alerta", + alertaApiEndpoint: "API Endpoint", + alertaEnvironment: "Environment", + alertaApiKey: "กุญแจ API", + alertaAlertState: "แจ้งเตือนสถานะ", + alertaRecoverState: "กู้คืนสถานะ", + deleteStatusPageMsg: "คุณแน่ใจหรือไม่ว่าต้องการลบหน้าสถานะนี้", + Proxies: "พร็อกซี", + default: "ค่าเริ่มต้น", + enabled: "เปิดใช้งาน", + setAsDefault: "ตั่งเป็นค่าเริ่มต้น", + deleteProxyMsg: "คุณแน่ใจหรือไม่ว่าต้องการลบพร็อกซีสำหรับมอนิเตอร์ทั้งหมด?", + proxyDescription: "พร็อกซีจะต้องตั่งค่าให้มอนิเตอร์เพื่อให้ใช้งานได้", + enableProxyDescription: "พร็อกซีนี้จะไม่ส่งผลต่อมอนิเตอร์จนกว่าจะเปิดใช้งาน คุณสามารถควบคุมการปิดใช้งานพร็อกซีชั่วคราวจากมอนิเตอร์ทั้งหมดได้โดยสถานะการเปิดใช้งาน", + setAsDefaultProxyDescription: "พร็อกซีนี้จะถูกเปิดโดนค่าเริ่มต้นสำหรับมอนิเตอร์ใหม่, คุณสามารถปิดการแจ้งเตือนสำหรับแต่ละมอนิเตอร์ได้", + "Certificate Chain": "ห่วงโซ่ใบรับรอง", + Valid: "ถูกต้อง", + Invalid: "ไม่ถูกต้อง", + AccessKeyId: "กุญแจสิทธิ ID", + SecretAccessKey: "กุญแจสิทธิ Secret", + PhoneNumbers: "PhoneNumbers", + TemplateCode: "รหัสเทมเพลต", + SignName: "ป้ายชื่อ", + "Sms template must contain parameters: ": "เทมเพลต SMS ต้องมีพารามิเตอร์ : ", + "Bark Endpoint": "Bark Endpoint", + WebHookUrl: "WebHookUrl", + SecretKey: "SecretKey", + "For safety, must use secret key": "เพื่อความปลอดภัย จำเป็นต้องตั่งค่ากุญแจการเข้าถึง", + "Device Token": "Device Token", + Platform: "แพลตฟอร์ม", + iOS: "iOS", + Android: "Android", + Huawei: "Huawei", + High: "สูง", + Retry: "ลองใหม่", + Topic: "หัวข้อ", + "WeCom Bot Key": "WeCom Bot Key", + "Setup Proxy": "ติดตั้งพร็อกซี่", + "Proxy Protocol": "โปรโตคอลพร็อกซี่", + "Proxy Server": "พร็อกซีเซิร์ฟ", + "Proxy server has authentication": "พร็อกซีเซิร์ฟเวอร์มีการตรวจสอบสิทธิ์", + User: "ผู้ใช้", + Installed: "ติดตั้งแล้ว", + "Not installed": "ไม่ได้ติดตั้ง", + Running: "กำลังทำงาน", + "Not running": "ไม่ได้ทำงาน", + "Remove Token": "ลบกุญแจ", + Start: "เริ่ม", + Stop: "หยุด", + "Uptime Kuma": "Uptime Kuma", + "Add New Status Page": "เพิ่มหน้าสถานะใหม่", + Slug: "ชื่อ", + "Accept characters:": "ตัวอักษรที่ใช้งานได้ :", + startOrEndWithOnly: "เริ่มหรือจบด้วย {0} เท่านั้น", + "No consecutive dashes": "ไม่มีขีดกลางติดต่อกัน", + Next: "ต่อไป", + "The slug is already taken. Please choose another slug.": "ชื่อนี้ถูกใช้งานไปแล้ว กรุณาใช้ชื่ออื่น", + "No Proxy": "ไม่มีพร็อกซี่", + "HTTP Basic Auth": "HTTP Basic Auth", + "New Status Page": "หน้าสถานะใหม่", + "Page Not Found": "ไม่พบหน้านี้", + "Reverse Proxy": "พร็อกซีย้อนกลับ", + Backup: "สำรอง", + About: "เกี่ยวกับ", + wayToGetCloudflaredURL: "(ดาวโหลด cloudflared จาก {0})", + cloudflareWebsite: "เว็บไซต์ Cloudflare", + "Message:": "ข้อความ :", + "Don't know how to get the token? Please read the guide:": "ไม่รู้วิธีการรับกุญแจ?, กรุณาอ่านคู่มือ", + "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "การเชื่อมต่อปัจุบันอาจขาดหายหากคุณกำลังเชื่อมต่อ Cloudflare Tunnel คุณแน่ใจหรือไม่ที่จะหยุด, พิมรหัสผ่านของคุณเพื่อยืนยัน", + "Other Software": "ซอฟต์แวร์อื่น ๆ ", + "For example: nginx, Apache and Traefik.": "เช่น: nginx, Apache และ Traefik", + "Please read": "กรุณาอ่าน", + "Subject:": "เรื่อง :", + "Valid To:": "ถูกต้องถึง :", + "Days Remaining:": "จำนวนวันที่เหลือ :", + "Issuer:": "ผู้ออก :", + "Fingerprint:": "ลายนิ้วมือ :", + "No status pages": "ไม่มีหน้าสถานะ", + "Domain Name Expiry Notification": "แจ้งเตือนการหมดอายุโดเมน", + Proxy: "Proxy", + "Date Created": "วันที่สร้าง", + onebotHttpAddress: "ที่อยู่ HTTP OneBot ", + onebotMessageType: "ชนิดข้อความ OneBot", + onebotGroupMessage: "กลุ่ม", + onebotPrivateMessage: "ส่วนตัว", + onebotUserOrGroupId: "กลุ่ม / ไอดีผู้ใช้", + onebotSafetyTips: "เพื่อความปลอดภัย จำเป็นต้องตั่งค่ากุญแจการเข้าถึง", + "PushDeer Key": "กุญแจ PushDeer", + "Footer Text": "ข้อความส่วนท้าย", + "Show Powered By": "แสดงข้อความ \"ขับเคลื่อนโดย\"", + "Domain Names": "Domain Names", + signedInDisp: "เข้าใช้งานในฐานะ {0}", + signedInDispDisabled: "ปิดการตรวจสอบสิทธิ์", + "Certificate Expiry Notification": "แจ้งเตือนการรับรองหมดอายุ", + "API Username": "API Username", + "API Key": "API Key", + "Recipient Number": "หมายเลขผู้รับ", + "From Name/Number": "จาก ชื่อ / หมายเลข", + "Leave blank to use a shared sender number.": "ไม่ต้องกรอกเพื่อใช้ชื่อผู้ส่งร่วมกัน", + "Octopush API Version": "Octopush API Version", + "Legacy Octopush-DM": "Legacy Octopush-DM", + endpoint: "endpoint", + octopushAPIKey: "\"API key\" จากข้อมูลรับรอง HTTP API ในแผงควบคุม", + octopushLogin: "\"Login\" จากข้อมูลรับรอง HTTP API ในแผงควบคุม", + promosmsLogin: "API Login Name", + promosmsPassword: "API Password", + "pushoversounds pushover": "Pushover (default)", + "pushoversounds bike": "Bike", + "pushoversounds bugle": "Bugle", + "pushoversounds cashregister": "Cash Register", + "pushoversounds classical": "Classical", + "pushoversounds cosmic": "Cosmic", + "pushoversounds falling": "Falling", + "pushoversounds gamelan": "Gamelan", + "pushoversounds incoming": "Incoming", + "pushoversounds intermission": "Intermission", + "pushoversounds magic": "Magic", + "pushoversounds mechanical": "Mechanical", + "pushoversounds pianobar": "Piano Bar", + "pushoversounds siren": "Siren", + "pushoversounds spacealarm": "Space Alarm", + "pushoversounds tugboat": "Tug Boat", + "pushoversounds alien": "Alien Alarm (long)", + "pushoversounds climb": "Climb (long)", + "pushoversounds persistent": "Persistent (long)", + "pushoversounds echo": "Pushover Echo (long)", + "pushoversounds updown": "Up Down (long)", + "pushoversounds vibrate": "Vibrate Only", + "pushoversounds none": "None (silent)", + pushyAPIKey: "Secret API Key", + pushyToken: "Device token", + "Show update if available": "แสดงการอัปเดตถ้ามี", + "Also check beta release": "ตรวจสอบรุ่นเบต้า", + "Using a Reverse Proxy?": "ใช้ Reverse Proxy?", + "Check how to config it for WebSocket": "ตรวจสอบวิธีการตั่งค่าสำหรับ WebSocket", + "Steam Game Server": "Steam Game Server", + "Most likely causes:": "สาเหตุที่เป็นไปได้มากที่สุด :", + "The resource is no longer available.": "ทรัพยากรไม่สามารถใช้งานได้อีกต่อไป", + "There might be a typing error in the address.": "อาจมีข้อผิดพลาดในการพิมพ์ที่อยู่", + "What you can try:": "สิ่งที่คุณสามารถลอง :", + "Retype the address.": "พิมพ์ที่อยู่อีกครั้ง", + "Go back to the previous page.": "กลับไปที่หน้าก่อนหน้า", + "Coming Soon": "เร็ว ๆ นี้", + wayToGetClickSendSMSToken: "คุณสามารถรับ API Username และ API Key ได้จาก {0}", +}; From 751e5ac4779819946c3d39cbaaa78a4dbc8e2c3b Mon Sep 17 00:00:00 2001 From: MrEddX <66828538+MrEddX@users.noreply.github.com> Date: Thu, 19 May 2022 20:32:01 +0300 Subject: [PATCH 117/124] Bulgarian Translation Updated Translation --- src/components/settings/Security.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/settings/Security.vue b/src/components/settings/Security.vue index 87bb745a..8deade63 100644 --- a/src/components/settings/Security.vue +++ b/src/components/settings/Security.vue @@ -206,7 +206,7 @@ <template v-else-if="$i18n.locale === 'bg-BG' "> <p>Сигурни ли сте, че желаете да <strong>изключите удостоверяването</strong>?</p> - <p>Използва се в случаите, когато <strong>има настроен алтернативен метод за удостоверяване</strong> преди Uptime Kuma, например Cloudflare Access.</p> + <p>Използва се в случаите, когато <strong>има настроен алтернативен метод за удостоверяване</strong> преди Uptime Kuma, например Cloudflare Access, Authelia или друг механизъм за удостоверяване.</p> <p>Моля, използвайте с повишено внимание.</p> </template> From 9d87f8d3902db8aa64ae649035df5c0fc1418fc8 Mon Sep 17 00:00:00 2001 From: Yoswaris Lawpaiboon <22832362+kiznick@users.noreply.github.com> Date: Fri, 20 May 2022 19:16:37 +0700 Subject: [PATCH 118/124] Update th-TH.js --- src/languages/th-TH.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/languages/th-TH.js b/src/languages/th-TH.js index b313deb0..70138ff4 100644 --- a/src/languages/th-TH.js +++ b/src/languages/th-TH.js @@ -26,7 +26,7 @@ export default { tokenValidSettingsMsg: "กุญแจถูกต้อง, ตอนนี้คุณสามารถบันทึกการตั้งค่า 2FA ของคุณได้แล้ว", confirmEnableTwoFAMsg: "คุณแน่ใจหรือไม่ที่จะเปิดใช้งาน 2FA?", confirmDisableTwoFAMsg: "คุณแน่ใจหรือไม่ที่จะปิดใช้งาน 2FA?", - Settings: "การตั่งค่า", + Settings: "การตั้งค่า", Dashboard: "แผงควบคุม", "New Update": "อัพเดทใหม่", Language: "ภาษา", @@ -149,7 +149,7 @@ export default { "Setup 2FA": "ติดตั้ง 2FA", "Enable 2FA": "เปิดใช้งาน 2FA", "Disable 2FA": "ปิดใช้งาน 2FA", - "2FA Settings": "ตั่งค่า 2FA", + "2FA Settings": "ตั้งค่า 2FA", "Two Factor Authentication": "การตรวจสอบสิทธิ์สองปัจจัย", Active: "ใช้งาน", Inactive: "ไม่ใช้งาน", @@ -361,7 +361,7 @@ export default { Customize: "ปรับแต่ง", "Custom Footer": "ส่วนท้ายที่กำหนดเอง", "Custom CSS": "CSS ที่กำหนดเอง", - smtpDkimSettings: "ตั่งค่า DKIM", + smtpDkimSettings: "ตั้งค่า DKIM", smtpDkimDesc: "โปรดดู Nodemailer DKIM {0} สำหรับการใช้งาน", documentation: "เอกสาร", smtpDkimDomain: "ชื่อโดเมน", @@ -383,7 +383,7 @@ export default { enabled: "เปิดใช้งาน", setAsDefault: "ตั่งเป็นค่าเริ่มต้น", deleteProxyMsg: "คุณแน่ใจหรือไม่ว่าต้องการลบพร็อกซีสำหรับมอนิเตอร์ทั้งหมด?", - proxyDescription: "พร็อกซีจะต้องตั่งค่าให้มอนิเตอร์เพื่อให้ใช้งานได้", + proxyDescription: "พร็อกซีจะต้องตั้งค่าให้มอนิเตอร์เพื่อให้ใช้งานได้", enableProxyDescription: "พร็อกซีนี้จะไม่ส่งผลต่อมอนิเตอร์จนกว่าจะเปิดใช้งาน คุณสามารถควบคุมการปิดใช้งานพร็อกซีชั่วคราวจากมอนิเตอร์ทั้งหมดได้โดยสถานะการเปิดใช้งาน", setAsDefaultProxyDescription: "พร็อกซีนี้จะถูกเปิดโดนค่าเริ่มต้นสำหรับมอนิเตอร์ใหม่, คุณสามารถปิดการแจ้งเตือนสำหรับแต่ละมอนิเตอร์ได้", "Certificate Chain": "ห่วงโซ่ใบรับรอง", @@ -398,7 +398,7 @@ export default { "Bark Endpoint": "Bark Endpoint", WebHookUrl: "WebHookUrl", SecretKey: "SecretKey", - "For safety, must use secret key": "เพื่อความปลอดภัย จำเป็นต้องตั่งค่ากุญแจการเข้าถึง", + "For safety, must use secret key": "เพื่อความปลอดภัย จำเป็นต้องตั้งค่ากุญแจการเข้าถึง", "Device Token": "Device Token", Platform: "แพลตฟอร์ม", iOS: "iOS", @@ -457,7 +457,7 @@ export default { onebotGroupMessage: "กลุ่ม", onebotPrivateMessage: "ส่วนตัว", onebotUserOrGroupId: "กลุ่ม / ไอดีผู้ใช้", - onebotSafetyTips: "เพื่อความปลอดภัย จำเป็นต้องตั่งค่ากุญแจการเข้าถึง", + onebotSafetyTips: "เพื่อความปลอดภัย จำเป็นต้องตั้งค่ากุญแจการเข้าถึง", "PushDeer Key": "กุญแจ PushDeer", "Footer Text": "ข้อความส่วนท้าย", "Show Powered By": "แสดงข้อความ \"ขับเคลื่อนโดย\"", @@ -505,7 +505,7 @@ export default { "Show update if available": "แสดงการอัปเดตถ้ามี", "Also check beta release": "ตรวจสอบรุ่นเบต้า", "Using a Reverse Proxy?": "ใช้ Reverse Proxy?", - "Check how to config it for WebSocket": "ตรวจสอบวิธีการตั่งค่าสำหรับ WebSocket", + "Check how to config it for WebSocket": "ตรวจสอบวิธีการตั้งค่าสำหรับ WebSocket", "Steam Game Server": "Steam Game Server", "Most likely causes:": "สาเหตุที่เป็นไปได้มากที่สุด :", "The resource is no longer available.": "ทรัพยากรไม่สามารถใช้งานได้อีกต่อไป", From f23baf9c2241284fed55b2b47a88c5efe7970574 Mon Sep 17 00:00:00 2001 From: DasCanard <mail@dascanard.xyz> Date: Tue, 24 May 2022 23:14:27 +0200 Subject: [PATCH 119/124] Added Push Monitor to Discord Notifications --- server/notification-providers/discord.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/server/notification-providers/discord.js b/server/notification-providers/discord.js index 77b04d9d..28ead7b7 100644 --- a/server/notification-providers/discord.js +++ b/server/notification-providers/discord.js @@ -55,8 +55,8 @@ class Discord extends NotificationProvider { value: monitorJSON["name"], }, { - name: "Service URL / Address", - value: address, + name: monitorJSON["type"] === "push" ? "Service Type" : "Service URL", + value: monitorJSON["type"] === "push" ? "Heartbeat" : address, }, { name: "Time (UTC)", @@ -90,8 +90,8 @@ class Discord extends NotificationProvider { value: monitorJSON["name"], }, { - name: "Service URL", - value: address.startsWith("http") ? "[Visit Service](" + address + ")" : address, + name: monitorJSON["type"] === "push" ? "Service Type" : "Service URL", + value: monitorJSON["type"] === "push" ? "Heartbeat" : address.startsWith("http") ? "[Visit Service](" + address + ")" : address, }, { name: "Time (UTC)", @@ -99,7 +99,7 @@ class Discord extends NotificationProvider { }, { name: "Ping", - value: heartbeatJSON["ping"] + "ms", + value: heartbeatJSON["ping"] == null ? "N/A" : heartbeatJSON["ping"] + " ms", }, ], }], From 5830f1e0b5c0bced9904e2db67af875d0d224422 Mon Sep 17 00:00:00 2001 From: Marc Hagen <hello@marchagen.nl> Date: Wed, 16 Feb 2022 23:09:22 +0100 Subject: [PATCH 120/124] [feat] Adding PagerDuty notification --- server/notification-providers/pagerduty.js | 113 +++++++++++++++++++++ server/notification.js | 2 + src/components/notifications/PagerDuty.vue | 40 ++++++++ src/components/notifications/index.js | 2 + src/languages/en.js | 9 ++ 5 files changed, 166 insertions(+) create mode 100644 server/notification-providers/pagerduty.js create mode 100644 src/components/notifications/PagerDuty.vue diff --git a/server/notification-providers/pagerduty.js b/server/notification-providers/pagerduty.js new file mode 100644 index 00000000..86e9a099 --- /dev/null +++ b/server/notification-providers/pagerduty.js @@ -0,0 +1,113 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); +const { UP, DOWN, getMonitorRelativeURL } = require("../../src/util"); +const { setting } = require("../util-server"); +let successMessage = "Sent Successfully."; + +class PagerDuty extends NotificationProvider { + name = "PagerDuty"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + try { + if (heartbeatJSON == null) { + const title = "Uptime Kuma Alert"; + const monitor = { + type: "ping", + url: "Uptime Kuma Test Button", + }; + return this.postNotification(notification, title, msg, monitor); + } + + if (heartbeatJSON.status === UP) { + const title = "Uptime Kuma Monitor ✅ Up"; + const eventAction = notification.pagerdutyAutoResolve || null; + + return this.postNotification(notification, title, heartbeatJSON.msg, monitorJSON, eventAction); + } + + if (heartbeatJSON.status === DOWN) { + const title = "Uptime Kuma Monitor 🔴 Down"; + return this.postNotification(notification, title, heartbeatJSON.msg, monitorJSON, "trigger"); + } + } catch (error) { + this.throwGeneralAxiosError(error); + } + } + + /** + * Check if result is successful, result code should be in range 2xx + * @param {Object} result Axios response object + * @throws {Error} The status code is not in range 2xx + */ + checkResult(result) { + if (result.status == null) { + throw new Error("PagerDuty notification failed with invalid response!"); + } + if (result.status < 200 || result.status >= 300) { + throw new Error("PagerDuty notification failed with status code " + result.status); + } + } + + /** + * Send the message + * @param {BeanModel} notification Message title + * @param {string} title Message title + * @param {string} body Message + * @param {Object} monitorInfo Monitor details (For Up/Down only) + * @param {?string} eventAction Action event for PagerDuty (trigger, acknowledge, resolve) + * @returns {string} + */ + async postNotification(notification, title, body, monitorInfo, eventAction = "trigger") { + + if (eventAction == null) { + return "No action required"; + } + + let monitorUrl; + if (monitorInfo.type === "port") { + monitorUrl = monitorInfo.hostname; + if (monitorInfo.port) { + monitorUrl += ":" + monitorInfo.port; + } + } else if (monitorInfo.hostname != null) { + monitorUrl = monitorInfo.hostname; + } else { + monitorUrl = monitorInfo.url; + } + + const options = { + method: "POST", + url: notification.pagerdutyIntegrationUrl, + headers: { "Content-Type": "application/json" }, + data: { + payload: { + summary: `[${title}] [${monitorInfo.name}] ${body}`, + severity: notification.pagerdutyPriority || "warning", + source: monitorUrl, + }, + routing_key: notification.pagerdutyIntegrationKey, + event_action: eventAction, + dedup_key: "Uptime Kuma/" + monitorInfo.id, + } + }; + + const baseURL = await setting("primaryBaseURL"); + if (baseURL && monitorInfo) { + options.client = "Uptime Kuma"; + options.client_url = baseURL + getMonitorRelativeURL(monitorInfo.id); + } + + let result = await axios.request(options); + this.checkResult(result); + if (result.statusText != null) { + return "PagerDuty notification succeed: " + result.statusText; + } + + return successMessage; + } +} + +module.exports = PagerDuty; diff --git a/server/notification.js b/server/notification.js index 269e9444..d0b6f40d 100644 --- a/server/notification.js +++ b/server/notification.js @@ -29,6 +29,7 @@ const SerwerSMS = require("./notification-providers/serwersms"); const Stackfield = require("./notification-providers/stackfield"); const WeCom = require("./notification-providers/wecom"); const GoogleChat = require("./notification-providers/google-chat"); +const PagerDuty = require("./notification-providers/pagerduty"); const Gorush = require("./notification-providers/gorush"); const Alerta = require("./notification-providers/alerta"); const OneBot = require("./notification-providers/onebot"); @@ -74,6 +75,7 @@ class Notification { new Stackfield(), new WeCom(), new GoogleChat(), + new PagerDuty(), new Gorush(), new Alerta(), new OneBot(), diff --git a/src/components/notifications/PagerDuty.vue b/src/components/notifications/PagerDuty.vue new file mode 100644 index 00000000..73f60443 --- /dev/null +++ b/src/components/notifications/PagerDuty.vue @@ -0,0 +1,40 @@ +<template> + <div class="mb-3"> + <label for="pagerduty-integration-key" class="form-label">{{ $t("Integration Key") }}</label> + <HiddenInput id="pagerduty-integration-key" v-model="$parent.notification.pagerdutyIntegrationKey" :required="true" autocomplete="false"></HiddenInput> + <i18n-t tag="div" keypath="wayToGetPagerDutyKey" class="form-text"> + <a href="https://support.pagerduty.com/docs/services-and-integrations" target="_blank">{{ $t("here") }}</a> + </i18n-t> + </div> + <div class="mb-3"> + <label for="pagerduty-integration-url" class="form-label">{{ $t("Integration URL") }}</label> + <input id="pagerduty-integration-url" v-model="$parent.notification.pagerdutyIntegrationUrl" type="text" class="form-control" autocomplete="false" value="https://events.pagerduty.com/v2/enqueue"> + </div> + <div class="mb-3"> + <label for="pagerduty-priority" class="form-label">{{ $t("Priority") }}</label> + <select id="pagerduty-priority" v-model="$parent.notification.pagerdutyPriority" class="form-select"> + <option value="info">{{ $t("info") }}</option> + <option value="warning" selected="selected">{{ $t("warning") }}</option> + <option value="error">{{ $t("error") }}</option> + <option value="critical">{{ $t("critical") }}</option> + </select> + </div> + <div class="mb-3"> + <label for="pagerduty-resolve" class="form-label">{{ $t("Auto resolve or acknowledged") }}</label> + <select id="pagerduty-resolve" v-model="$parent.notification.pagerdutyAutoResolve" class="form-select"> + <option value="0" selected="selected">{{ $t("do nothing") }}</option> + <option value="acknowledge">{{ $t("auto acknowledged") }}</option> + <option value="resolve">{{ $t("auto resolve") }}</option> + </select> + </div> +</template> + +<script> +import HiddenInput from "../HiddenInput.vue"; + +export default { + components: { + HiddenInput, + }, +}; +</script> diff --git a/src/components/notifications/index.js b/src/components/notifications/index.js index 496d35fa..37beb24d 100644 --- a/src/components/notifications/index.js +++ b/src/components/notifications/index.js @@ -27,6 +27,7 @@ import SerwerSMS from "./SerwerSMS.vue"; import Stackfield from "./Stackfield.vue"; import WeCom from "./WeCom.vue"; import GoogleChat from "./GoogleChat.vue"; +import PagerDuty from "./PagerDuty.vue"; import Gorush from "./Gorush.vue"; import Alerta from "./Alerta.vue"; import OneBot from "./OneBot.vue"; @@ -67,6 +68,7 @@ const NotificationFormList = { "stackfield": Stackfield, "WeCom": WeCom, "GoogleChat": GoogleChat, + "PagerDuty": PagerDuty, "gorush": Gorush, "alerta": Alerta, "OneBot": OneBot, diff --git a/src/languages/en.js b/src/languages/en.js index d634e545..aa6737dd 100644 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -331,6 +331,8 @@ export default { info: "info", warning: "warning", danger: "danger", + error: "error", + critical: "critical", primary: "primary", light: "light", dark: "dark", @@ -371,6 +373,13 @@ export default { smtpDkimHashAlgo: "Hash Algorithm (Optional)", smtpDkimheaderFieldNames: "Header Keys to sign (Optional)", smtpDkimskipFields: "Header Keys not to sign (Optional)", + wayToGetPagerDutyKey: "You can get this by going to Service -> Service Directory -> (Select a service) -> Integrations -> Add integration. Here you can search for \"Events API V2\". More info {0}", + "Integration Key": "Integration Key", + "Integration URL": "Integration URL", + "Auto resolve or acknowledged": "Auto resolve or acknowledged", + "do nothing": "do nothing", + "auto acknowledged": "auto acknowledged", + "auto resolve": "auto resolve", gorush: "Gorush", alerta: "Alerta", alertaApiEndpoint: "API Endpoint", From 5566b038c84792a7f59502e050d5eafa89641fd3 Mon Sep 17 00:00:00 2001 From: burakurer <58211081+burakurer@users.noreply.github.com> Date: Wed, 25 May 2022 17:35:05 +0300 Subject: [PATCH 121/124] Update tr-TR.js --- src/languages/tr-TR.js | 399 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 383 insertions(+), 16 deletions(-) diff --git a/src/languages/tr-TR.js b/src/languages/tr-TR.js index 0edd6020..08cafa2c 100644 --- a/src/languages/tr-TR.js +++ b/src/languages/tr-TR.js @@ -1,6 +1,7 @@ export default { languageName: "Türkçe", checkEverySecond: "{0} Saniyede bir kontrol et.", + retryCheckEverySecond: "{0} Saniyede bir dene.", retriesDescription: "Servisin kapalı olarak işaretlenmeden ve bir bildirim gönderilmeden önce maksimum yeniden deneme sayısı", ignoreTLSError: "HTTPS web siteleri için TLS/SSL hatasını yoksay", upsideDownModeDescription: "Servisin durumunu tersine çevirir. Servis çalışıyorsa kapalı olarak işaretler.", @@ -12,12 +13,20 @@ export default { pauseDashboardHome: "Durdur", deleteMonitorMsg: "Servisi silmek istediğinden emin misin?", deleteNotificationMsg: "Bu bildirimi tüm servisler için silmek istediğinden emin misin?", + dnsPortDescription: "DNS sunucusu bağlantı noktası. Varsayılan değer 53'tür. Bağlantı noktasını istediğiniz zaman değiştirebilirsiniz.", resolverserverDescription: "Cloudflare varsayılan sunucudur, çözümleyici sunucusunu istediğiniz zaman değiştirebilirsiniz.", rrtypeDescription: "İzlemek istediğiniz servisin RR-Tipini seçin", pauseMonitorMsg: "Durdurmak istediğinden emin misin?", + enableDefaultNotificationDescription: "Bu bildirim her yeni serviste aktif olacaktır. Bildirimi servisler için ayrı ayrı deaktive edebilirsiniz. ", clearEventsMsg: "Bu servisin bütün kayıtlarını silmek istediğinden emin misin?", clearHeartbeatsMsg: "Bu servis için tüm sağlık durumunu silmek istediğinden emin misin?", confirmClearStatisticsMsg: "Tüm istatistikleri silmek istediğinden emin misin?", + importHandleDescription: "Aynı isimdeki bütün servisleri ve bildirimleri atlamak için 'Var olanı atla' seçiniz. 'Üzerine yaz' var olan bütün servisleri ve bildirimleri silecektir. ", + confirmImportMsg: "Yedeği içeri aktarmak istediğinize emin misiniz? Lütfen doğru içeri aktarma seçeneğini seçtiğinizden emin olunuz. ", + twoFAVerifyLabel: "Lütfen tokeni yazarak 2FA doğrulamanın çalıştığından emin olunuz.", + tokenValidSettingsMsg: "Token geçerli! Şimdi 2FA ayarlarını kaydedebilirsiniz. ", + confirmEnableTwoFAMsg: "2FA'ı etkinleştirmek istediğinizden emin misiniz?", + confirmDisableTwoFAMsg: "2FA'ı devre dışı bırakmak istediğinize emin misiniz?", Settings: "Ayarlar", Dashboard: "Panel", "New Update": "Yeni Güncelleme", @@ -25,6 +34,7 @@ export default { Appearance: "Görünüm", Theme: "Tema", General: "Genel", + "Primary Base URL": "Birincil Temel URL", Version: "Versiyon", "Check Update On GitHub": "GitHub'da Güncellemeyi Kontrol Edin", List: "Liste", @@ -62,10 +72,14 @@ export default { Port: "Port", "Heartbeat Interval": "Servis Test Aralığı", Retries: "Yeniden deneme", + "Heartbeat Retry Interval": "Sağlık Durumları Tekrar Deneme Sıklığı", Advanced: "Gelişmiş", "Upside Down Mode": "Ters/Düz Modu", "Max. Redirects": "Maksimum Yönlendirme", "Accepted Status Codes": "Kabul Edilen Durum Kodları", + "Push URL": "Push URL", + needPushEvery: "Bu URL'yi her {0} saniyede bir aramalısınız.", + pushOptionalParams: "İsteğe bağlı parametreler: {0}", Save: "Kaydet", Notifications: "Bildirimler", "Not available, please setup.": "Atanmış bildirim yöntemi yok. Ayarlardan belirleyebilirsiniz.", @@ -109,28 +123,19 @@ export default { "Last Result": "En son sonuçlar", "Create your admin account": "Yönetici hesabınızı oluşturun", "Repeat Password": "Şifrenizi tekrar girin", - respTime: "Cevap Süresi (ms)", - notAvailableShort: "N/A", - Create: "Yarat", - "Clear Data": "Verileri Temizle", - Events: "Olaylar", - Heartbeats: "Sağlık Durumları", - "Auto Get": "Otomatik Al", - retryCheckEverySecond: "{0} Saniyede bir dene.", - enableDefaultNotificationDescription: "Bu bildirim her yeni serviste aktif olacaktır. Bildirimi servisler için ayrı ayrı deaktive edebilirsiniz. ", - importHandleDescription: "Aynı isimdeki bütün servisleri ve bildirimleri atlamak için 'Var olanı atla' seçiniz. 'Üzerine yaz' var olan bütün servisleri ve bildirimleri silecektir. ", - confirmImportMsg: "Yedeği içeri aktarmak istediğinize emin misiniz? Lütfen doğru içeri aktarma seçeneğini seçtiğinizden emin olunuz. ", - twoFAVerifyLabel: "Lütfen tokeni yazarak 2FA doğrulamanın çalıştığından emin olunuz.", - tokenValidSettingsMsg: "Token geçerli! Şimdi 2FA ayarlarını kaydedebilirsiniz. ", - confirmEnableTwoFAMsg: "2FA'ı etkinleştirmek istediğinizden emin misiniz?", - confirmDisableTwoFAMsg: "2FA'ı devre dışı bırakmak istediğinize emin misiniz?", - "Heartbeat Retry Interval": "Sağlık Durumları Tekrar Deneme Sıklığı", "Import Backup": "Yedeği içe aktar", "Export Backup": "Yedeği dışa aktar", Export: "Dışa aktar", Import: "İçe aktar", + respTime: "Cevap Süresi (ms)", + notAvailableShort: "N/A", "Default enabled": "Varsayılan etkinleştirilmiş", "Apply on all existing monitors": "Var olan bütün servislere uygula", + Create: "Oluştur", + "Clear Data": "Verileri Temizle", + Events: "Olaylar", + Heartbeats: "Sağlık Durumları", + "Auto Get": "Otomatik Al", backupDescription: "Bütün servisleri ve bildirimleri JSON dosyasına yedekleyebilirsiniz.", backupDescription2: "Not: Geçmiş ve etkinlik verileri içinde değildir.", backupDescription3: "Dışa aktarma dosyasında bildirim tokeni gibi hassas veriler bulunur, dikkatli bir şekilde saklayınız.", @@ -149,4 +154,366 @@ export default { "Two Factor Authentication": "İki Faktörlü Kimlik Doğrulama (2FA)", Active: "Aktif", Inactive: "İnaktif", + Token: "Token", + "Show URI": "URI'yi göster", + Tags: "Etiketler", + "Add New below or Select...": "Aşağıya Yeni Ekle veya Seç...", + "Tag with this name already exist.": "Bu ada sahip etiket zaten var.", + "Tag with this value already exist.": "Bu değere sahip etiket zaten var.", + color: "renk", + "value (optional)": "değer (isteğe bağlı)", + Gray: "Gri", + Red: "Kırmızı", + Orange: "Turuncu", + Green: "Yeşil", + Blue: "Mavi", + Indigo: "Çivit mavisi", + Purple: "Mor", + Pink: "Pembe", + "Search...": "Ara...", + "Avg. Ping": "Ortalama Ping", + "Avg. Response": "Ortalama Cevap Süresi", + "Entry Page": "Giriş Sayfası", + statusPageNothing: "Burada hiçbir şey yok, lütfen bir grup veya servis ekleyin.", + "No Services": "Hizmet Yok", + "All Systems Operational": "Tüm Sistemler Operasyonel", + "Partially Degraded Service": "Kısmen Bozulmuş Hizmet", + "Degraded Service": "Bozulmuş Hizmet", + "Add Group": "Grup Ekle", + "Add a monitor": "Servis Ekle", + "Edit Status Page": "Durum Sayfasını Düzenle", + "Go to Dashboard": "Panele Git", + "Status Page": "Durum Sayfası", + "Status Pages": "Durum Sayfaları", + defaultNotificationName: "My {notification} Alert ({number})", + here: "burada", + Required: "Gerekli", + telegram: "Telegram", + "Bot Token": "Bot Token", + wayToGetTelegramToken: "{0} adresinden bir token alabilirsiniz.", + "Chat ID": "Chat ID", + supportTelegramChatID: "Doğrudan Sohbet / Grup / Kanalın Sohbet Kimliğini Destekleyin", + wayToGetTelegramChatID: "Bot'a bir mesaj göndererek ve chat_id'yi görüntülemek için bu URL'ye giderek sohbet kimliğinizi alabilirsiniz:", + "YOUR BOT TOKEN HERE": "BOT TOKENİNİZ BURADA", + chatIDNotFound: "Chat ID bulunamadı; lütfen önce bu bota bir mesaj gönderin", + webhook: "Webhook", + "Post URL": "Post URL", + "Content Type": "Content Type", + webhookJsonDesc: "{0}, Express.js gibi tüm modern HTTP sunucuları için iyidir", + webhookFormDataDesc: "{multipart} PHP için iyidir. JSON'un {decodeFunction} ile ayrıştırılması gerekecek", + smtp: "E-mail (SMTP)", + secureOptionNone: "Hiçbiri / STARTTLS (25, 587)", + secureOptionTLS: "TLS (465)", + "Ignore TLS Error": "TLS Hatasını Yoksay", + "From Email": "E-postadan", + emailCustomSubject: "Özel Konu", + "To Email": "E-postaya", + smtpCC: "CC", + smtpBCC: "BCC", + discord: "Discord", + "Discord Webhook URL": "Discord Webhook URL", + wayToGetDiscordURL: "Bunu Sunucu Ayarları -> Entegrasyonlar -> Webhook Oluştur'a giderek alabilirsiniz.", + "Bot Display Name": "Botun Görünecek Adı", + "Prefix Custom Message": "Önek Özel Mesaj", + "Hello @everyone is...": "Merhaba {'@'}everyone ...", + teams: "Microsoft Teams", + "Webhook URL": "Webhook URL", + wayToGetTeamsURL: "Bir webhook URL'sinin nasıl oluşturulacağını öğrenebilirsiniz {0}.", + signal: "Signal", + Number: "Numara", + Recipients: "Alıcılar", + needSignalAPI: "REST API ile bir signal istemciniz olması gerekiyor.", + wayToCheckSignalURL: "Nasıl kurulacağını görmek için bu URL'yi kontrol edebilirsiniz:", + signalImportant: "ÖNEMLİ: Alıcılarda grupları ve sayıları karıştıramazsınız!", + gotify: "Gotify", + "Application Token": "Uygulama Tokeni", + "Server URL": "Sunucu URL", + Priority: "Öncelik", + slack: "Slack", + "Icon Emoji": "İkon Emoji", + "Channel Name": "Kanal Adı", + "Uptime Kuma URL": "Uptime Kuma URL", + aboutWebhooks: "Webhook hakkında daha fazla bilgi: {0}", + aboutChannelName: "Webhook kanalını atlamak istiyorsanız, {0} Kanal Adı alanına kanal adını girin. Ör: #diğer-kanal", + aboutKumaURL: "Uptime Kuma URL alanını boş bırakırsanız, varsayılan olarak Project GitHub sayfası olur.", + emojiCheatSheet: "Emoji cheat sheet: {0}", + "rocket.chat": "Rocket.Chat", + pushover: "Pushover", + pushy: "Pushy", + PushByTechulus: "Push by Techulus", + octopush: "Octopush", + promosms: "PromoSMS", + clicksendsms: "ClickSend SMS", + lunasea: "LunaSea", + apprise: "Apprise (50'den fazla Bildirim hizmetini destekler)", + GoogleChat: "Google Chat (sadece Google Workspace)", + pushbullet: "Pushbullet", + line: "Line Messenger", + mattermost: "Mattermost", + "User Key": "Kullancı Anahtarı", + Device: "Cihaz", + "Message Title": "Mesaj Başlığı", + "Notification Sound": "Bilgilendirme sesi", + "More info on:": "Daha fazla bilgi: {0}", + pushoverDesc1: "Acil durum önceliği (2), yeniden denemeler arasında varsayılan olarak 30 saniyelik bir zaman aşımına sahiptir ve 1 saat sonra sona erecektir.", + pushoverDesc2: "Farklı cihazlara bildirim göndermek istiyorsanız Cihaz alanını doldurunuz.", + "SMS Type": "SMS Tipi", + octopushTypePremium: "Premium (Hızlı - uyarı için önerilir)", + octopushTypeLowCost: "Düşük Maliyet (Yavaş - bazen operatör tarafından engellenir)", + checkPrice: "{0} fiyatlarını kontrol edin:", + apiCredentials: "API kimlik bilgileri", + octopushLegacyHint: "Octopush'un (2011-2020) eski sürümünü mü yoksa yeni sürümünü mü kullanıyorsunuz?", + "Check octopush prices": "Octopush fiyatlarını kontrol edin {0}.", + octopushPhoneNumber: "Telefon numarası (uluslararası biçim, örneğin: +33612345678) ", + octopushSMSSender: "SMS Gönderici Adı : 3-11 alfanümerik karakter ve boşluk (a-zA-Z0-9)", + "LunaSea Device ID": "LunaSea Cihaz ID", + "Apprise URL": "Apprise URL", + "Example:": "Örnek: {0}", + "Read more:": "Daha fazla oku: {0}", + "Status:": "Durum: {0}", + "Read more": "Daha fazla oku", + appriseInstalled: "Apprise yüklendi.", + appriseNotInstalled: "Appris yüklü değil. {0}", + "Access Token": "Erişim Tokeni", + "Channel access token": "Kanal erişim tokeni", + "Line Developers Console": "Line Geliştirici Konsolu", + lineDevConsoleTo: "Line Geliştirici Konsolu - {0}", + "Basic Settings": "Temel Ayarlar", + "User ID": "Kullanıcı ID", + "Messaging API": "Messaging API", + wayToGetLineChannelToken: "Önce {0}'e erişin, bir sağlayıcı ve kanal (Messaging API) oluşturun, ardından yukarıda belirtilen menü öğelerinden kanal erişim tokenini ve kullanıcı id alabilirsiniz.", + "Icon URL": "Simge URL", + aboutIconURL: "Varsayılan profil resmini geçersiz kılmak için \"Simge URL\" bölümünde bir resme bağlantı sağlayabilirsiniz. Simge Emojisi ayarlanmışsa kullanılmayacaktır.", + aboutMattermostChannelName: "Kanal adını \"Kanal Adı\" alanına girerek Webhook'un gönderi yaptığı varsayılan kanalı geçersiz kılabilirsiniz. Bunun Mattermost Webhook ayarlarında etkinleştirilmesi gerekir. Ör: #diğer-kanal", + matrix: "Matrix", + promosmsTypeEco: "SMS ECO - ucuz ama yavaş ve genellikle aşırı yüklü. Yalnızca Polonyalı alıcılarla sınırlıdır.", + promosmsTypeFlash: "SMS FLASH - Mesaj, alıcı cihazda otomatik olarak gösterilecektir. Yalnızca Polonyalı alıcılarla sınırlıdır.", + promosmsTypeFull: "SMS FULL - Premium SMS katmanı, Gönderici Adınızı kullanabilirsiniz (Önce adınızı kaydetmeniz gerekir). Uyarılar için güvenilir.", + promosmsTypeSpeed: "SMS HIZI - Sistemde en yüksek öncelik. Çok hızlı ve güvenilir ancak maliyetli (SMS FULL fiyatının yaklaşık iki katı).", + promosmsPhoneNumber: "Telefon numarası (Polonyalı alıcı için Alan kodlarını atlayabilirsiniz)", + promosmsSMSSender: "SMS Gönderici Adı : Ön kayıtlı ad veya varsayılanlardan biri: InfoSMS, SMS Info, MaxSMS, INFO, SMS", + "Feishu WebHookUrl": "Feishu WebHookURL", + matrixHomeserverURL: "Homeserver URL (http(s):// ve isteğe bağlı olarak bağlantı noktası ile)", + "Internal Room Id": "Internal Room ID", + matrixDesc1: "Internal Room ID'sini, Matrix istemcinizdeki oda ayarlarının gelişmiş bölümüne bakarak bulabilirsiniz. !QMdRCpUIfLwsfjxye6:home.server gibi görünmelidir.", + matrixDesc2: "Hesabınıza ve katıldığınız tüm odalara tam erişime izin vereceğinden, yeni bir kullanıcı oluşturmanız ve kendi Matrix kullanıcınızın erişim belirtecini kullanmamanız şiddetle tavsiye edilir. Bunun yerine, yeni bir kullanıcı oluşturun ve onu yalnızca bildirimi almak istediğiniz odaya davet edin. {0} komutunu çalıştırarak erişim tokenini alabilirsiniz.", + Method: "Yöntem", + Body: "Gövde", + Headers: "Başlıklar", + PushUrl: "Push URL", + HeadersInvalidFormat: "İstek başlıkları geçerli JSON değil:", + BodyInvalidFormat: "İstek gövdesi geçerli JSON değil:", + "Monitor History": "Servis Geçmişi", + clearDataOlderThan: "{0} gün boyunca izleme geçmişi verilerini saklayın.", + PasswordsDoNotMatch: "Parolalar uyuşmuyor.", + records: "kayıtlar", + "One record": "Bir Kayıt", + steamApiKeyDescription: "Bir Steam Oyun Sunucusunu izlemek için bir Steam Web-API anahtarına ihtiyacınız vardır. API anahtarınızı buradan kaydedebilirsiniz: ", + "Current User": "Şu anki kullanıcı", + topic: "Başlık", + topicExplanation: "İzlenecek MQTT servisi", + successMessage: "Başarılı Mesaj", + successMessageExplanation: "Başarılı olarak kabul edilecek MQTT mesajı", + recent: "Son", + Done: "Tamamlandı", + Info: "Bilgi", + Security: "Güvenlik", + "Steam API Key": "Steam API Anahtarı", + "Shrink Database": "Veritabanını Küçült", + "Pick a RR-Type...": "Bir RR-Tipi seçin...", + "Pick Accepted Status Codes...": "Kabul Edilen Durum Kodlarını Seçin...", + Default: "Varsayılan", + "HTTP Options": "HTTP Ayarları", + "Create Incident": "Olay Oluştur", + Title: "Başlık", + Content: "İçerik", + Style: "Stil", + info: "info", + warning: "warning", + danger: "danger", + primary: "primary", + light: "light", + dark: "dark", + Post: "Post", + "Please input title and content": "Lütfen başlık ve içerik girin", + Created: "Oluşturuldu", + "Last Updated": "Son Güncelleme", + Unpin: "Unpin", + "Switch to Light Theme": "Açık Temaya Geç", + "Switch to Dark Theme": "Karanlık Temaya Geç", + "Show Tags": "Etiketleri Göster", + "Hide Tags": "Etiketleri Gizle", + Description: "Açıklama", + "No monitors available.": "Kullanılabilir servis yok.", + "Add one": "Bir tane ekle", + "No Monitors": "Servis Yok", + "Untitled Group": "Adsız Grup", + Services: "Hizmetler", + Discard: "İptal Et", + Cancel: "İptal Et", + "Powered by": "Powered by", + shrinkDatabaseDescription: "SQLite için veritabanı VACUUM'unu tetikleyin. Veritabanınız 1.10.0'dan sonra oluşturulduysa, AUTO_VACUUM zaten etkinleştirilmiştir ve bu eyleme gerek yoktur.", + serwersms: "SerwerSMS.pl", + serwersmsAPIUser: "API Kullanıcı Adı (webapi_ öneki dahil)", + serwersmsAPIPassword: "API Şifre", + serwersmsPhoneNumber: "Telefon numarası", + serwersmsSenderName: "SMS Gönderici Adı (müşteri portalı üzerinden kayıtlı)", + stackfield: "Stackfield", + Customize: "Özelleştirme", + "Custom Footer": "Özel Altbilgi", + "Custom CSS": "Özel CSS", + smtpDkimSettings: "DKIM Ayarları", + smtpDkimDesc: "Kullanım için lütfen Nodemailer DKIM'e {0} bakın.", + documentation: "belgeler", + smtpDkimDomain: "Alan adı", + smtpDkimKeySelector: "Anahtar Seçici", + smtpDkimPrivateKey: "Özel anahtar", + smtpDkimHashAlgo: "Hash Algoritması (Opsiyonel)", + smtpDkimheaderFieldNames: "İmzalanacak Başlık Anahtarları (Opsiyonel)", + smtpDkimskipFields: "İmzalamayacak Başlık Anahtarları (Opsiyonel)", + gorush: "Gorush", + alerta: "Alerta", + alertaApiEndpoint: "API Endpoint", + alertaEnvironment: "Environment", + alertaApiKey: "API Key", + alertaAlertState: "Uyarı Durumu", + alertaRecoverState: "Kurtarma Durumu", + deleteStatusPageMsg: "Bu durum sayfasını silmek istediğinizden emin misiniz?", + Proxies: "Proxy'ler", + default: "Varsayılan", + enabled: "Etkinleştirilmiş", + setAsDefault: "Varsayılan Olarak Ayarla", + deleteProxyMsg: "Bu proxy'yi tüm servisler için silmek istediğinizden emin misiniz?", + proxyDescription: "Proxy'lerin çalışması için bir servise atanması gerekir.", + enableProxyDescription: "Bu proxy, etkinleştirilene kadar izleme isteklerini etkilemeyecektir. Aktivasyon durumuna göre proxy'yi tüm servislerden geçici olarak devre dışı bırakabilirsiniz.", + setAsDefaultProxyDescription: "Bu proxy, yeni servisler için varsayılan olarak etkinleştirilecektir. Yine de proxy'yi her servis için ayrı ayrı devre dışı bırakabilirsiniz.", + "Certificate Chain": "Sertifika Zinciri", + Valid: "Geçerli", + Invalid: "Geçersiz", + AccessKeyId: "AccessKey ID", + SecretAccessKey: "AccessKey Secret", + PhoneNumbers: "PhoneNumbers", + TemplateCode: "TemplateCode", + SignName: "SignName", + "Sms template must contain parameters: ": "Sms şablonu parametreleri içermelidir:", + "Bark Endpoint": "Bark Endpoint", + WebHookUrl: "WebHookUrl", + SecretKey: "SecretKey", + "For safety, must use secret key": "Güvenlik için gizli anahtar kullanılmalıdır", + "Device Token": "Cihaz Tokeni", + Platform: "Platform", + iOS: "iOS", + Android: "Android", + Huawei: "Huawei", + High: "High", + Retry: "Retry", + Topic: "Topic", + "WeCom Bot Key": "WeCom Bot Key", + "Setup Proxy": "Proxy kur", + "Proxy Protocol": "Proxy Protokolü", + "Proxy Server": "Proxy Sunucusu", + "Proxy server has authentication": "Proxy sunucusunun kimlik doğrulaması var", + User: "Kullanıcı", + Installed: "Yüklenmiş", + "Not installed": "Yüklü değil", + Running: "Çalışıyor", + "Not running": "Çalışmıyor", + "Remove Token": "Tokeni Kaldır", + Start: "Başlat", + Stop: "Durdur", + "Uptime Kuma": "Uptime Kuma", + "Add New Status Page": "Yeni Durum Sayfası Ekle", + Slug: "Slug", + "Accept characters:": "Kabul edilen karakterler:", + startOrEndWithOnly: "Yalnızca {0} ile başlayın veya bitirin", + "No consecutive dashes": "Ardışık tire yok", + Next: "Sonraki", + "The slug is already taken. Please choose another slug.": "Slug zaten alındı. Lütfen başka bir slug seçin.", + "No Proxy": "Proxy Yok", + "HTTP Basic Auth": "HTTP Temel Yetkilendirme", + "New Status Page": "Yeni Durum Sayfası", + "Page Not Found": "Sayfa bulunamadı", + "Reverse Proxy": "Ters Proxy", + Backup: "Yedek", + About: "Hakkında", + wayToGetCloudflaredURL: "(Cloudflared'i {0} adresinden indirin)", + cloudflareWebsite: "Cloudflare Website", + "Message:": "Mesaj:", + "Don't know how to get the token? Please read the guide:": "Tokeni nasıl alacağınızı bilmiyor musunuz? Lütfen kılavuzu okuyun:", + "The current connection may be lost if you are currently connecting via Cloudflare Tunnel. Are you sure want to stop it? Type your current password to confirm it.": "Halihazırda Cloudflare Tüneli üzerinden bağlanıyorsanız mevcut bağlantı kesilebilir. Durdurmak istediğinden emin misin? Onaylamak için mevcut şifrenizi yazın.", + "Other Software": "Diğer Yazılımlar", + "For example: nginx, Apache and Traefik.": "Örneğin: nginx, Apache ve Traefik.", + "Please read": "Lütfen oku", + "Subject:": "Başlık:", + "Valid To:": "Geçerlilik:", + "Days Remaining:": "Kalan günler:", + "Issuer:": "Veren:", + "Fingerprint:": "Parmak izi:", + "No status pages": "Durum sayfası yok", + "Domain Name Expiry Notification": "Alan Adı Sona Erme Bildirimi", + Proxy: "Proxy", + "Date Created": "Tarih Oluşturuldu", + onebotHttpAddress: "OneBot HTTP Adresi", + onebotMessageType: "OneBot Mesaj Türü", + onebotGroupMessage: "Grup", + onebotPrivateMessage: "Özel", + onebotUserOrGroupId: "Grup/Kullanıcı Kimliği", + onebotSafetyTips: "Güvenlik için erişim tokeni ayarlamalısınız", + "PushDeer Key": "PushDeer Anahtarı", + "Footer Text": "Altbilgi metni", + "Show Powered By": "\"Powered by\" kısmını göster", + "Domain Names": "Alan isimleri", + signedInDisp: "{0} olarak oturum açıldı", + signedInDispDisabled: "Yetkilendirme Devre Dışı.", + "Certificate Expiry Notification": "Sertifika Sona Erme Bildirimi", + "API Username": "API Kullanıc Adı", + "API Key": "API Anahtarı", + "Recipient Number": "Alıcı Numarası", + "From Name/Number": "İsimden/Numaradan", + "Leave blank to use a shared sender number.": "Paylaşılan bir gönderen numarası kullanmak için boş bırakın.", + "Octopush API Version": "Octopush API Sürümü", + "Legacy Octopush-DM": "Eski Octopush-DM", + "endpoint": "endpoint", + octopushAPIKey: "Kontrol panelindeki HTTP API kimlik bilgilerinden \"API Key\"", + octopushLogin: "Kontrol panelindeki HTTP API kimlik bilgilerinden \"Login\"", + promosmsLogin: "API Oturum Açma Adı", + promosmsPassword: "API Şifresi", + "pushoversounds pushover": "Pushover (varsayılan)", + "pushoversounds bike": "Bisiklet", + "pushoversounds bugle": "Boru", + "pushoversounds cashregister": "Yazar kasa", + "pushoversounds classical": "Klasik", + "pushoversounds cosmic": "Kozmik", + "pushoversounds falling": "Düşme", + "pushoversounds gamelan": "Oyun Alanı", + "pushoversounds incoming": "Gelen", + "pushoversounds intermission": "Ara", + "pushoversounds magic": "Büyü", + "pushoversounds mechanical": "Mekanik", + "pushoversounds pianobar": "Piano", + "pushoversounds siren": "Siren", + "pushoversounds spacealarm": "Uzay Alarmı", + "pushoversounds tugboat": "Römorkör", + "pushoversounds alien": "Uzaylı Alarmı (uzun)", + "pushoversounds climb": "Tırmanış (uzun)", + "pushoversounds persistent": "Sürekli (uzun)", + "pushoversounds echo": "Pushover Yankı (uzun)", + "pushoversounds updown": "Yukarı Aşağı (uzun)", + "pushoversounds vibrate": "Sadece titreşim", + "pushoversounds none": "Yok (sessiz)", + pushyAPIKey: "Gizli API Anahtarı", + pushyToken: "Cihaz tokeni", + "Show update if available": "Varsa güncellemeyi göster", + "Also check beta release": "Ayrıca beta sürümünü kontrol edin", + "Using a Reverse Proxy?": "Ters Proxy mi Kullanıyorsunuz?", + "Check how to config it for WebSocket": "WebSocket için nasıl yapılandırılacağını kontrol edin", + "Steam Game Server": "Steam Oyun Sunucusu", + "Most likely causes:": "En olası nedenler:", + "The resource is no longer available.": "Kaynak artık mevcut değil.", + "There might be a typing error in the address.": "Adreste bir yazım hatası olabilir.", + "What you can try:": "Ne deneyebilirsin:", + "Retype the address.": "Adresi tekrar yazın.", + "Go back to the previous page.": "Bir önceki sayfaya geri git.", + "Coming Soon": "Yakında gelecek", + wayToGetClickSendSMSToken: "API Kullanıcı Adı ve API Anahtarını {0} adresinden alabilirsiniz.", }; From 0a8c922abf0b5919d4b6df83b08de133f164eb4d Mon Sep 17 00:00:00 2001 From: Louis Lam <louislam@users.noreply.github.com> Date: Wed, 25 May 2022 23:34:47 +0800 Subject: [PATCH 122/124] Fix default value of pagerduty-integration-url --- src/components/notifications/PagerDuty.vue | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/components/notifications/PagerDuty.vue b/src/components/notifications/PagerDuty.vue index 73f60443..059a9aef 100644 --- a/src/components/notifications/PagerDuty.vue +++ b/src/components/notifications/PagerDuty.vue @@ -8,7 +8,7 @@ </div> <div class="mb-3"> <label for="pagerduty-integration-url" class="form-label">{{ $t("Integration URL") }}</label> - <input id="pagerduty-integration-url" v-model="$parent.notification.pagerdutyIntegrationUrl" type="text" class="form-control" autocomplete="false" value="https://events.pagerduty.com/v2/enqueue"> + <input id="pagerduty-integration-url" v-model="$parent.notification.pagerdutyIntegrationUrl" type="text" class="form-control" autocomplete="false"> </div> <div class="mb-3"> <label for="pagerduty-priority" class="form-label">{{ $t("Priority") }}</label> @@ -36,5 +36,10 @@ export default { components: { HiddenInput, }, + mounted() { + if (typeof this.$parent.notification.pagerdutyIntegrationUrl === "undefined") { + this.$parent.notification.pagerdutyIntegrationUrl = "https://events.pagerduty.com/v2/enqueue"; + } + } }; </script> From 334cb57fed1aed6a3c616e48edc36b68dfe89c1e Mon Sep 17 00:00:00 2001 From: Louis Lam <louislam@users.noreply.github.com> Date: Thu, 26 May 2022 19:32:52 +0800 Subject: [PATCH 123/124] Update to 1.16.0 --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 1ba25baf..806eff07 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "uptime-kuma", - "version": "1.16.0-beta.0", + "version": "1.16.0", "license": "MIT", "repository": { "type": "git", @@ -39,7 +39,7 @@ "build-docker-nightly-alpine": "docker buildx build -f docker/dockerfile-alpine --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:nightly-alpine --target nightly . --push", "build-docker-nightly-amd64": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:nightly-amd64 --target nightly . --push --progress plain", "upload-artifacts": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:upload-artifact --build-arg VERSION --build-arg GITHUB_TOKEN --target upload-artifact . --progress plain", - "setup": "git checkout 1.15.1 && npm ci --production && npm run download-dist", + "setup": "git checkout 1.16.0 && npm ci --production && npm run download-dist", "download-dist": "node extra/download-dist.js", "mark-as-nightly": "node extra/mark-as-nightly.js", "reset-password": "node extra/reset-password.js", From 30858ab038a4d4d18de2a653c1a4af19e3784c02 Mon Sep 17 00:00:00 2001 From: Louis Lam <louislam@users.noreply.github.com> Date: Sat, 28 May 2022 23:08:14 +0800 Subject: [PATCH 124/124] Fix rollback issue of 9fc5a33 and one issue of #1694 --- src/components/PublicGroupList.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/PublicGroupList.vue b/src/components/PublicGroupList.vue index 98c0b7ff..df94eec9 100644 --- a/src/components/PublicGroupList.vue +++ b/src/components/PublicGroupList.vue @@ -41,7 +41,7 @@ <Uptime :monitor="monitor.element" type="24" :pill="true" /> {{ monitor.element.name }} </div> - <div v-if="showTag" class="tags"> + <div v-if="showTags" class="tags"> <Tag v-for="tag in monitor.element.tags" :key="tag" :item="tag" :size="'sm'" /> </div> </div>