From f6ef390c76b282474a9f96984777576a60102e0d Mon Sep 17 00:00:00 2001 From: Nelson Chan Date: Fri, 7 Jan 2022 11:57:24 +0800 Subject: [PATCH 01/84] Fix: Remove Prom. metrics on delete monitor --- server/model/monitor.js | 5 +++++ server/prometheus.js | 10 ++++++++++ server/server.js | 1 + 3 files changed, 16 insertions(+) diff --git a/server/model/monitor.js b/server/model/monitor.js index c4441d63e..5a04b3833 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -468,6 +468,11 @@ class Monitor extends BeanModel { this.isStop = true; } + onDelete() { + let prometheus = new Prometheus(this); + prometheus.remove(); + } + /** * Helper Method: * returns URL object for further usage diff --git a/server/prometheus.js b/server/prometheus.js index 870581d2e..ebcc8fa47 100644 --- a/server/prometheus.js +++ b/server/prometheus.js @@ -84,6 +84,16 @@ class Prometheus { } } + remove() { + try { + monitor_cert_days_remaining.remove(this.monitorLabelValues); + monitor_cert_is_valid.remove(this.monitorLabelValues); + monitor_response_time.remove(this.monitorLabelValues); + monitor_status.remove(this.monitorLabelValues); + } catch (e) { + console.error(e); + } + } } module.exports = { diff --git a/server/server.js b/server/server.js index 868bbd5ef..db985c439 100644 --- a/server/server.js +++ b/server/server.js @@ -733,6 +733,7 @@ exports.entryPage = "dashboard"; if (monitorID in monitorList) { monitorList[monitorID].stop(); + monitorList[monitorID].onDelete(); delete monitorList[monitorID]; } From edd2534a1ba2fbaca5b1c00db533807646d25c7a Mon Sep 17 00:00:00 2001 From: Nelson Chan Date: Fri, 7 Jan 2022 12:26:26 +0800 Subject: [PATCH 02/84] Fix: Clear metrics also on stop and edit --- server/model/monitor.js | 9 +++++++-- server/server.js | 5 +++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/server/model/monitor.js b/server/model/monitor.js index 5a04b3833..48bd5a4cc 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -466,11 +466,16 @@ class Monitor extends BeanModel { stop() { clearTimeout(this.heartbeatInterval); this.isStop = true; + + this.prometheus().remove(); } onDelete() { - let prometheus = new Prometheus(this); - prometheus.remove(); + this.prometheus().remove(); + } + + prometheus() { + return new Prometheus(this); } /** diff --git a/server/server.js b/server/server.js index db985c439..fbf0a06e0 100644 --- a/server/server.js +++ b/server/server.js @@ -567,6 +567,11 @@ exports.entryPage = "dashboard"; throw new Error("Permission denied."); } + // Reset Prometheus labels + if (monitorList[monitor.id] && monitorList[monitor.id].prometheus) { + monitorList[monitor.id].prometheus().remove(); + } + bean.name = monitor.name; bean.type = monitor.type; bean.url = monitor.url; From 1e92487f30639343b9c0bf0cf138100d5adc4b9e Mon Sep 17 00:00:00 2001 From: Nelson Chan Date: Fri, 7 Jan 2022 12:28:08 +0800 Subject: [PATCH 03/84] Chore: Remove onDelete as unused --- server/model/monitor.js | 4 ---- server/server.js | 1 - 2 files changed, 5 deletions(-) diff --git a/server/model/monitor.js b/server/model/monitor.js index 48bd5a4cc..f99939170 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -470,10 +470,6 @@ class Monitor extends BeanModel { this.prometheus().remove(); } - onDelete() { - this.prometheus().remove(); - } - prometheus() { return new Prometheus(this); } diff --git a/server/server.js b/server/server.js index fbf0a06e0..3e888c92a 100644 --- a/server/server.js +++ b/server/server.js @@ -738,7 +738,6 @@ exports.entryPage = "dashboard"; if (monitorID in monitorList) { monitorList[monitorID].stop(); - monitorList[monitorID].onDelete(); delete monitorList[monitorID]; } From 2e0e35a1ee982ae21452035648c7f707f404498c Mon Sep 17 00:00:00 2001 From: Nelson Chan Date: Fri, 7 Jan 2022 12:34:01 +0800 Subject: [PATCH 04/84] Fix: Fix typo --- server/server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/server.js b/server/server.js index 3e888c92a..7db702a9f 100644 --- a/server/server.js +++ b/server/server.js @@ -568,7 +568,7 @@ exports.entryPage = "dashboard"; } // Reset Prometheus labels - if (monitorList[monitor.id] && monitorList[monitor.id].prometheus) { + if (monitorList[monitor.id] && monitorList[monitor.id].prometheus()) { monitorList[monitor.id].prometheus().remove(); } From 1bbd744d0285bcb5bda457165221a3da97bc79d3 Mon Sep 17 00:00:00 2001 From: Nelson Chan Date: Fri, 7 Jan 2022 14:29:42 +0800 Subject: [PATCH 05/84] Chore: Improve syntax --- server/server.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/server/server.js b/server/server.js index 7db702a9f..821fccc7c 100644 --- a/server/server.js +++ b/server/server.js @@ -568,9 +568,7 @@ exports.entryPage = "dashboard"; } // Reset Prometheus labels - if (monitorList[monitor.id] && monitorList[monitor.id].prometheus()) { - monitorList[monitor.id].prometheus().remove(); - } + monitorList[monitor.id]?.prometheus()?.remove(); bean.name = monitor.name; bean.type = monitor.type; From 2c8d5d28e972693af13de11bb9be5a2023eb2a8a Mon Sep 17 00:00:00 2001 From: Marc Harnos Date: Fri, 7 Jan 2022 23:13:38 +0100 Subject: [PATCH 06/84] simplify host fallback logic move decision logic for freeBSD HOST environment var into temp var --- server/server.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/server/server.js b/server/server.js index 868bbd5ef..48a4c084c 100644 --- a/server/server.js +++ b/server/server.js @@ -63,12 +63,9 @@ console.info("Version: " + checkVersion.version); // If host is omitted, the server will accept connections on the unspecified IPv6 address (::) when IPv6 is available and the unspecified IPv4 address (0.0.0.0) otherwise. // Dual-stack support for (::) -let hostname = process.env.UPTIME_KUMA_HOST || args.host; - // Also read HOST if not FreeBSD, as HOST is a system environment variable in FreeBSD -if (!hostname && !FBSD) { - hostname = process.env.HOST; -} +let hostEnv = FBSD ? null : process.env.HOST; +let hostname = process.env.UPTIME_KUMA_HOST || args.host || hostEnv; if (hostname) { console.log("Custom hostname: " + hostname); From 0053a29d103d803c22a278a861e417933241fb97 Mon Sep 17 00:00:00 2001 From: Marc Harnos Date: Fri, 7 Jan 2022 23:16:26 +0100 Subject: [PATCH 07/84] add validation to port value parsing only port configurations that are valid (not isNaN) after parseInt are considered to be used in port variable --- server/server.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/server/server.js b/server/server.js index 48a4c084c..6b822a3de 100644 --- a/server/server.js +++ b/server/server.js @@ -71,7 +71,9 @@ if (hostname) { console.log("Custom hostname: " + hostname); } -const port = parseInt(process.env.UPTIME_KUMA_PORT || process.env.PORT || args.port || 3001); +const port = [process.env.UPTIME_KUMA_PORT, process.env.PORT, args.port, 3001] + .map(portValue => parseInt(portValue)) + .find(portValue => !isNaN(portValue)); // SSL const sslKey = process.env.UPTIME_KUMA_SSL_KEY || process.env.SSL_KEY || args["ssl-key"] || undefined; From 0bbe157099996db930294893207bad3f71ea3fae Mon Sep 17 00:00:00 2001 From: Marc Harnos Date: Sat, 8 Jan 2022 14:36:33 +0100 Subject: [PATCH 08/84] change parsing priority for all passed arguments update all passed args in server.js to prioritize command line, then use env.UPTIME_KUMA_ environment variables, then use the generic environment variable versions env.HOST, env.PORT, env.SSL_KEY, env.SSL_CERT and fall back to default values where applicable --- server/server.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/server/server.js b/server/server.js index 6b822a3de..d3f2b20a5 100644 --- a/server/server.js +++ b/server/server.js @@ -65,20 +65,20 @@ console.info("Version: " + checkVersion.version); // Dual-stack support for (::) // Also read HOST if not FreeBSD, as HOST is a system environment variable in FreeBSD let hostEnv = FBSD ? null : process.env.HOST; -let hostname = process.env.UPTIME_KUMA_HOST || args.host || hostEnv; +let hostname = args.host || process.env.UPTIME_KUMA_HOST || hostEnv; if (hostname) { console.log("Custom hostname: " + hostname); } -const port = [process.env.UPTIME_KUMA_PORT, process.env.PORT, args.port, 3001] +const port = [args.port, process.env.UPTIME_KUMA_PORT, process.env.PORT, 3001] .map(portValue => parseInt(portValue)) .find(portValue => !isNaN(portValue)); // SSL -const sslKey = process.env.UPTIME_KUMA_SSL_KEY || process.env.SSL_KEY || args["ssl-key"] || undefined; -const sslCert = process.env.UPTIME_KUMA_SSL_CERT || process.env.SSL_CERT || args["ssl-cert"] || undefined; -const disableFrameSameOrigin = !!process.env.UPTIME_KUMA_DISABLE_FRAME_SAMEORIGIN || args["disable-frame-sameorigin"] || false; +const sslKey = args["ssl-key"] || process.env.UPTIME_KUMA_SSL_KEY || process.env.SSL_KEY || undefined; +const sslCert = args["ssl-cert"] || process.env.UPTIME_KUMA_SSL_CERT || process.env.SSL_CERT || undefined; +const disableFrameSameOrigin = args["disable-frame-sameorigin"] || !!process.env.UPTIME_KUMA_DISABLE_FRAME_SAMEORIGIN || false; // 2FA / notp verification defaults const twofa_verification_opts = { From 281fe365c056e90107fcaf3d335565e61090f098 Mon Sep 17 00:00:00 2001 From: AnnAngela-work Date: Wed, 2 Mar 2022 15:23:08 +0800 Subject: [PATCH 09/84] Mark the version as 1.11.4 in package-lock.json --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index eefd9d5ac..a3bfe0610 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "uptime-kuma", - "version": "1.11.4", + "version": "1.12.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "uptime-kuma", - "version": "1.11.4", + "version": "1.12.1", "license": "MIT", "dependencies": { "@fortawesome/fontawesome-svg-core": "~1.2.36", From 783173fd1fa432d1c018e792e928cbf504c61f85 Mon Sep 17 00:00:00 2001 From: AnnAngela-work Date: Wed, 2 Mar 2022 15:48:08 +0800 Subject: [PATCH 10/84] Add a helper function --- extra/download-dist.js | 5 +++-- extra/fs-rmSync.js | 20 ++++++++++++++++++++ extra/update-language-files/index.js | 9 +++++---- extra/update-version.js | 9 +++++---- test/prepare-jest.js | 3 ++- test/prepare-test-server.js | 3 ++- 6 files changed, 37 insertions(+), 12 deletions(-) create mode 100644 extra/fs-rmSync.js diff --git a/extra/download-dist.js b/extra/download-dist.js index dc64166c4..c184c2846 100644 --- a/extra/download-dist.js +++ b/extra/download-dist.js @@ -4,6 +4,7 @@ const tar = require("tar"); const packageJSON = require("../package.json"); const fs = require("fs"); +const rmSync = require("./fs-rmSync.js"); const version = packageJSON.version; const filename = "dist.tar.gz"; @@ -21,7 +22,7 @@ function download(url) { if (fs.existsSync("./dist")) { if (fs.existsSync("./dist-backup")) { - fs.rmdirSync("./dist-backup", { + rmSync("./dist-backup", { recursive: true }); } @@ -35,7 +36,7 @@ function download(url) { tarStream.on("close", () => { if (fs.existsSync("./dist-backup")) { - fs.rmdirSync("./dist-backup", { + rmSync("./dist-backup", { recursive: true }); } diff --git a/extra/fs-rmSync.js b/extra/fs-rmSync.js new file mode 100644 index 000000000..8def68d0f --- /dev/null +++ b/extra/fs-rmSync.js @@ -0,0 +1,20 @@ +const fs = require("fs"); +/** + * Detect if `fs.rmSync` is available + * to avoid the runtime warning triggered for using `fs.rmdirSync` with `{ recursive: true }` in Node.js v16, + * or the `recursive` property removing completely in the future Node.js version. + * See the link below. + * @link https://nodejs.org/docs/latest-v16.x/api/deprecations.html#dep0147-fsrmdirpath--recursive-true- + * @param {fs.PathLike} path Valid types for path values in "fs". + * @param {fs.RmDirOptions} [options] options for `fs.rmdirSync`, if `fs.rmSync` is available and property `recursive` is true, it will automatically have property `force` with value `true`. + */ +const rmSync = (path, options) => { + if (typeof fs.rmSync === "function") { + if (options.recursive) { + options.force = true; + } + return fs.rmSync(path, options); + } + return fs.rmdirSync(path, options); +}; +module.exports = rmSync; diff --git a/extra/update-language-files/index.js b/extra/update-language-files/index.js index 7ba30cc05..1f58d01f3 100644 --- a/extra/update-language-files/index.js +++ b/extra/update-language-files/index.js @@ -3,6 +3,7 @@ import fs from "fs"; import path from "path"; import util from "util"; +import rmSync from "../fs-rmSync.js"; // https://stackoverflow.com/questions/13786160/copy-folder-recursively-in-node-js /** @@ -30,7 +31,7 @@ console.log("Arguments:", process.argv); const baseLangCode = process.argv[2] || "en"; console.log("Base Lang: " + baseLangCode); if (fs.existsSync("./languages")) { - fs.rmdirSync("./languages", { recursive: true }); + rmSync("./languages", { recursive: true }); } copyRecursiveSync("../../src/languages", "./languages"); @@ -61,7 +62,7 @@ for (const file of files) { // En first for (const key in en) { - if (! obj[key]) { + if (!obj[key]) { obj[key] = en[key]; } } @@ -69,7 +70,7 @@ for (const file of files) { if (baseLang !== en) { // Base second for (const key in baseLang) { - if (! obj[key]) { + if (!obj[key]) { obj[key] = key; } } @@ -82,5 +83,5 @@ for (const file of files) { fs.writeFileSync(`../../src/languages/${file}`, code); } -fs.rmdirSync("./languages", { recursive: true }); +rmSync("./languages", { recursive: true }); console.log("Done. Fixing formatting by ESLint..."); diff --git a/extra/update-version.js b/extra/update-version.js index 2e3b42da8..499426b8a 100644 --- a/extra/update-version.js +++ b/extra/update-version.js @@ -1,5 +1,6 @@ const pkg = require("../package.json"); const fs = require("fs"); +const rmSync = require("./fs-rmSync.js"); const child_process = require("child_process"); const util = require("../src/util"); @@ -11,14 +12,14 @@ const newVersion = process.argv[2]; console.log("Old Version: " + oldVersion); console.log("New Version: " + newVersion); -if (! newVersion) { +if (!newVersion) { console.error("invalid version"); process.exit(1); } const exists = tagExists(newVersion); -if (! exists) { +if (!exists) { // Process package.json pkg.version = newVersion; @@ -55,7 +56,7 @@ function tag(version) { } function tagExists(version) { - if (! version) { + if (!version) { throw new Error("invalid version"); } @@ -93,7 +94,7 @@ function updateWiki(oldVersion, newVersion) { function safeDelete(dir) { if (fs.existsSync(dir)) { - fs.rmdirSync(dir, { + rmSync(dir, { recursive: true, }); } diff --git a/test/prepare-jest.js b/test/prepare-jest.js index 9dfaba7d9..3fd89d10b 100644 --- a/test/prepare-jest.js +++ b/test/prepare-jest.js @@ -1,9 +1,10 @@ const fs = require("fs"); +const rmSync = require("../extra/fs-rmSync.js"); const path = "./data/test-chrome-profile"; if (fs.existsSync(path)) { - fs.rmdirSync(path, { + rmSync(path, { recursive: true, }); } diff --git a/test/prepare-test-server.js b/test/prepare-test-server.js index 0e49c7fb9..1a9b04df1 100644 --- a/test/prepare-test-server.js +++ b/test/prepare-test-server.js @@ -1,9 +1,10 @@ const fs = require("fs"); +const rmSync = require("../extra/fs-rmSync.js"); const path = "./data/test"; if (fs.existsSync(path)) { - fs.rmdirSync(path, { + rmSync(path, { recursive: true, }); } From 88a798704b793408cab3bd80077367bf863f7c54 Mon Sep 17 00:00:00 2001 From: AnnAngela Date: Wed, 2 Mar 2022 16:10:14 +0800 Subject: [PATCH 11/84] Update fs-rmSync.js --- extra/fs-rmSync.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extra/fs-rmSync.js b/extra/fs-rmSync.js index 8def68d0f..4c12f22e0 100644 --- a/extra/fs-rmSync.js +++ b/extra/fs-rmSync.js @@ -1,7 +1,7 @@ const fs = require("fs"); /** * Detect if `fs.rmSync` is available - * to avoid the runtime warning triggered for using `fs.rmdirSync` with `{ recursive: true }` in Node.js v16, + * to avoid the runtime deprecation warning triggered for using `fs.rmdirSync` with `{ recursive: true }` in Node.js v16, * or the `recursive` property removing completely in the future Node.js version. * See the link below. * @link https://nodejs.org/docs/latest-v16.x/api/deprecations.html#dep0147-fsrmdirpath--recursive-true- From 2cc7a990ff44fc0ecd7561622e975b4312783c00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?U=C4=9Fur=20Erkan?= Date: Sat, 30 Oct 2021 17:02:29 +0300 Subject: [PATCH 12/84] Add http and https agents --- package-lock.json | 122 ++++++++++++++++++++++++++++++++++++++-------- package.json | 2 + 2 files changed, 103 insertions(+), 21 deletions(-) diff --git a/package-lock.json b/package-lock.json index eefd9d5ac..19b3c153d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "uptime-kuma", - "version": "1.11.4", + "version": "1.12.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "uptime-kuma", - "version": "1.11.4", + "version": "1.12.1", "license": "MIT", "dependencies": { "@fortawesome/fontawesome-svg-core": "~1.2.36", @@ -31,6 +31,8 @@ "express-basic-auth": "~1.2.0", "form-data": "~4.0.0", "http-graceful-shutdown": "~3.1.5", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", "iconv-lite": "^0.6.3", "jsonwebtoken": "~8.5.1", "jwt-decode": "^3.1.2", @@ -2929,12 +2931,11 @@ "integrity": "sha512-2pTGuibAXJswAPJjaKisthqS/NOK5ypG4LYT6tEAV0S/mxW0zOIvYvGK0V8w8+SHxAm6vRMSjqSalFXeBAqs+Q==" }, "node_modules/@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", - "devOptional": true, + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", "engines": { - "node": ">= 6" + "node": ">= 10" } }, "node_modules/@types/accepts": { @@ -6821,12 +6822,11 @@ } }, "node_modules/http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", - "devOptional": true, + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", "dependencies": { - "@tootallnate/once": "1", + "@tootallnate/once": "2", "agent-base": "6", "debug": "4" }, @@ -9297,6 +9297,15 @@ } } }, + "node_modules/jsdom/node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/jsdom/node_modules/acorn": { "version": "8.6.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.6.0.tgz", @@ -9323,6 +9332,20 @@ "node": ">= 6" } }, + "node_modules/jsdom/node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dev": true, + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", @@ -9748,6 +9771,29 @@ "node": ">= 10" } }, + "node_modules/make-fetch-happen/node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "optional": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/make-fetch-happen/node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "optional": true, + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/makeerror": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", @@ -15711,10 +15757,9 @@ "integrity": "sha512-2pTGuibAXJswAPJjaKisthqS/NOK5ypG4LYT6tEAV0S/mxW0zOIvYvGK0V8w8+SHxAm6vRMSjqSalFXeBAqs+Q==" }, "@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", - "devOptional": true + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==" }, "@types/accepts": { "version": "1.3.5", @@ -18752,12 +18797,11 @@ } }, "http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", - "devOptional": true, + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", "requires": { - "@tootallnate/once": "1", + "@tootallnate/once": "2", "agent-base": "6", "debug": "4" } @@ -20609,6 +20653,12 @@ "xml-name-validator": "^3.0.0" }, "dependencies": { + "@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "dev": true + }, "acorn": { "version": "8.6.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.6.0.tgz", @@ -20625,6 +20675,17 @@ "combined-stream": "^1.0.8", "mime-types": "^2.1.12" } + }, + "http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dev": true, + "requires": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + } } } }, @@ -20974,6 +21035,25 @@ "promise-retry": "^2.0.1", "socks-proxy-agent": "^6.0.0", "ssri": "^8.0.0" + }, + "dependencies": { + "@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "optional": true + }, + "http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "optional": true, + "requires": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + } + } } }, "makeerror": { diff --git a/package.json b/package.json index 9da737982..cc6f6593c 100644 --- a/package.json +++ b/package.json @@ -76,6 +76,8 @@ "express-basic-auth": "~1.2.0", "form-data": "~4.0.0", "http-graceful-shutdown": "~3.1.5", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", "iconv-lite": "^0.6.3", "jsonwebtoken": "~8.5.1", "jwt-decode": "^3.1.2", From 78d76512ba7928404463ee45835ade62cc9ef804 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?U=C4=9Fur=20Erkan?= Date: Sat, 30 Oct 2021 20:37:15 +0300 Subject: [PATCH 13/84] Add http and https proxy feature Added new proxy feature based on http and https proxy agents. Proxy feature works like notifications, there is many proxy could be related one proxy entry. Supported features - Proxies can activate and disable in bulk - Proxies auto enabled by default for new monitors - Proxies could be applied in bulk to current monitors - Both authenticated and anonymous proxies supported - Export and import support for proxies --- db/patch-proxy.sql | 23 ++++ server/client.js | 21 ++- server/database.js | 1 + server/model/monitor.js | 50 ++++++- server/model/proxy.js | 21 +++ server/proxy.js | 99 ++++++++++++++ server/server.js | 87 +++++++++++- src/components/ProxyDialog.vue | 203 ++++++++++++++++++++++++++++ src/components/settings/Proxies.vue | 47 +++++++ src/languages/en.js | 8 ++ src/mixins/socket.js | 11 ++ src/pages/EditMonitor.vue | 55 ++++++++ src/pages/Settings.vue | 3 + src/router.js | 5 + 14 files changed, 627 insertions(+), 7 deletions(-) create mode 100644 db/patch-proxy.sql create mode 100644 server/model/proxy.js create mode 100644 server/proxy.js create mode 100644 src/components/ProxyDialog.vue create mode 100644 src/components/settings/Proxies.vue diff --git a/db/patch-proxy.sql b/db/patch-proxy.sql new file mode 100644 index 000000000..41897b1e2 --- /dev/null +++ b/db/patch-proxy.sql @@ -0,0 +1,23 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + +CREATE TABLE proxy ( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + user_id INT NOT NULL, + protocol VARCHAR(10) NOT NULL, + host VARCHAR(255) NOT NULL, + port SMALLINT NOT NULL, + auth BOOLEAN NOT NULL, + username VARCHAR(255) NULL, + password VARCHAR(255) NULL, + active BOOLEAN NOT NULL DEFAULT 1, + 'default' BOOLEAN NOT NULL DEFAULT 0, + created_date DATETIME DEFAULT (DATETIME('now')) NOT NULL +); + +ALTER TABLE monitor ADD COLUMN proxy_id INTEGER REFERENCES proxy(id); + +CREATE INDEX proxy_id ON monitor (proxy_id); +CREATE INDEX proxy_user_id ON proxy (user_id); + +COMMIT; diff --git a/server/client.js b/server/client.js index c7b3bc162..2c07448b1 100644 --- a/server/client.js +++ b/server/client.js @@ -83,6 +83,23 @@ async function sendImportantHeartbeatList(socket, monitorID, toUser = false, ove } +/** + * Delivers proxy list + * + * @param socket + * @return {Promise} + */ +async function sendProxyList(socket) { + const timeLogger = new TimeLogger(); + + const list = await R.find("proxy", " user_id = ? ", [socket.userID]); + io.to(socket.userID).emit("proxyList", list.map(bean => bean.export())); + + timeLogger.print("Send Proxy List"); + + return list; +} + async function sendInfo(socket) { socket.emit("info", { version: checkVersion.version, @@ -95,6 +112,6 @@ module.exports = { sendNotificationList, sendImportantHeartbeatList, sendHeartbeatList, - sendInfo + sendProxyList, + sendInfo, }; - diff --git a/server/database.js b/server/database.js index afcace705..a7f7ae7d9 100644 --- a/server/database.js +++ b/server/database.js @@ -53,6 +53,7 @@ class Database { "patch-2fa-invalidate-used-token.sql": true, "patch-notification_sent_history.sql": true, "patch-monitor-basic-auth.sql": true, + "patch-proxy.sql": true, } /** diff --git a/server/model/monitor.js b/server/model/monitor.js index b4a805980..f938ca80b 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -1,4 +1,6 @@ const https = require("https"); +const HttpProxyAgent = require("http-proxy-agent"); +const HttpsProxyAgent = require("https-proxy-agent"); const dayjs = require("dayjs"); const utc = require("dayjs/plugin/utc"); let timezone = require("dayjs/plugin/timezone"); @@ -77,6 +79,7 @@ class Monitor extends BeanModel { dns_resolve_server: this.dns_resolve_server, dns_last_result: this.dns_last_result, pushToken: this.pushToken, + proxyId: this.proxy_id, notificationIDList, tags: tags, }; @@ -173,6 +176,11 @@ class Monitor extends BeanModel { }; } + const httpsAgentOptions = { + maxCachedSessions: 0, // Use Custom agent to disable session reuse (https://github.com/nodejs/node/issues/3940) + rejectUnauthorized: !this.getIgnoreTls(), + }; + debug(`[${this.name}] Prepare Options for axios`); const options = { @@ -186,17 +194,51 @@ class Monitor extends BeanModel { ...(this.headers ? JSON.parse(this.headers) : {}), ...(basicAuthHeader), }, - httpsAgent: new https.Agent({ - maxCachedSessions: 0, // Use Custom agent to disable session reuse (https://github.com/nodejs/node/issues/3940) - rejectUnauthorized: ! this.getIgnoreTls(), - }), maxRedirects: this.maxredirects, validateStatus: (status) => { return checkStatusCode(status, this.getAcceptedStatuscodes()); }, }; + if (this.proxy_id) { + const proxy = await R.load("proxy", this.proxy_id); + + if (proxy && proxy.active) { + const httpProxyAgentOptions = { + protocol: proxy.protocol, + host: proxy.host, + port: proxy.port, + }; + const httpsProxyAgentOptions = { + ...httpsAgentOptions, + protocol: proxy.protocol, + hostname: proxy.host, + port: proxy.port, + }; + + if (proxy.auth) { + httpProxyAgentOptions.auth = `${proxy.username}:${proxy.password}`; + httpsProxyAgentOptions.auth = `${proxy.username}:${proxy.password}`; + } + + debug(`[${this.name}] HTTP options: ${JSON.stringify({ + "http": httpProxyAgentOptions, + "https": httpsProxyAgentOptions, + })}`); + + options.proxy = false; + options.httpAgent = new HttpProxyAgent(httpProxyAgentOptions); + options.httpsAgent = new HttpsProxyAgent(httpsProxyAgentOptions); + } + } + + if (!options.httpsAgent) { + options.httpsAgent = new https.Agent(httpsAgentOptions); + } + + debug(`[${this.name}] Axios Options: ${JSON.stringify(options)}`); debug(`[${this.name}] Axios Request`); + let res = await axios.request(options); bean.msg = `${res.status} - ${res.statusText}`; bean.ping = dayjs().valueOf() - startTime; diff --git a/server/model/proxy.js b/server/model/proxy.js new file mode 100644 index 000000000..7ddec4349 --- /dev/null +++ b/server/model/proxy.js @@ -0,0 +1,21 @@ +const { BeanModel } = require("redbean-node/dist/bean-model"); + +class Proxy extends BeanModel { + toJSON() { + return { + id: this._id, + userId: this._user_id, + protocol: this._protocol, + host: this._host, + port: this._port, + auth: !!this._auth, + username: this._username, + password: this._password, + active: !!this._active, + default: !!this._default, + createdDate: this._created_date, + }; + } +} + +module.exports = Proxy; diff --git a/server/proxy.js b/server/proxy.js new file mode 100644 index 000000000..df3831538 --- /dev/null +++ b/server/proxy.js @@ -0,0 +1,99 @@ +const { R } = require("redbean-node"); + +class Proxy { + + /** + * Saves and updates given proxy entity + * + * @param proxy + * @param proxyID + * @param userID + * @return {Promise} + */ + static async save(proxy, proxyID, userID) { + let bean; + + if (proxyID) { + bean = await R.findOne("proxy", " id = ? AND user_id = ? ", [proxyID, userID]); + + if (!bean) { + throw new Error("proxy not found"); + } + + } else { + bean = R.dispense("proxy"); + } + + // Make sure given proxy protocol is supported + if (!["http", "https"].includes(proxy.protocol)) { + throw new Error(`Unsupported proxy protocol "${proxy.protocol}. Supported protocols are http and https."`); + } + + // When proxy is default update deactivate old default proxy + if (proxy.default) { + await R.exec("UPDATE proxy SET `default` = 0 WHERE `default` = 1"); + } + + bean.user_id = userID; + bean.protocol = proxy.protocol; + bean.host = proxy.host; + bean.port = proxy.port; + bean.auth = proxy.auth; + bean.username = proxy.username; + bean.password = proxy.password; + bean.active = proxy.active || true; + bean.default = proxy.default || false; + + await R.store(bean); + + if (proxy.applyExisting) { + await applyProxyEveryMonitor(bean.id, userID); + } + + return bean; + } + + /** + * Deletes proxy with given id and removes it from monitors + * + * @param proxyID + * @param userID + * @return {Promise} + */ + static async delete(proxyID, userID) { + const bean = await R.findOne("proxy", " id = ? AND user_id = ? ", [proxyID, userID]); + + if (!bean) { + throw new Error("proxy not found"); + } + + // Delete removed proxy from monitors if exists + await R.exec("UPDATE monitor SET proxy_id = null WHERE proxy_id = ?", [proxyID]); + + // Delete proxy from list + await R.trash(bean); + } +} + +/** + * Applies given proxy id to monitors + * + * @param proxyID + * @param userID + * @return {Promise} + */ +async function applyProxyEveryMonitor(proxyID, userID) { + // Find all monitors with id and proxy id + const monitors = await R.getAll("SELECT id, proxy_id FROM monitor WHERE user_id = ?", [userID]); + + // Update proxy id not match with given proxy id + for (const monitor of monitors) { + if (monitor.proxy_id !== proxyID) { + await R.exec("UPDATE monitor SET proxy_id = ? WHERE id = ?", [proxyID, monitor.id]); + } + } +} + +module.exports = { + Proxy, +}; diff --git a/server/server.js b/server/server.js index 153cac4fd..b713e4f7f 100644 --- a/server/server.js +++ b/server/server.js @@ -58,6 +58,9 @@ debug("Importing Notification"); const { Notification } = require("./notification"); Notification.init(); +debug("Importing Proxy"); +const { Proxy } = require("./proxy"); + debug("Importing Database"); const Database = require("./database"); @@ -128,7 +131,7 @@ const io = new Server(server); module.exports.io = io; // Must be after io instantiation -const { sendNotificationList, sendHeartbeatList, sendImportantHeartbeatList, sendInfo } = require("./client"); +const { sendNotificationList, sendHeartbeatList, sendImportantHeartbeatList, sendInfo, sendProxyList } = require("./client"); const { statusPageSocketHandler } = require("./socket-handlers/status-page-socket-handler"); const databaseSocketHandler = require("./socket-handlers/database-socket-handler"); const TwoFA = require("./2fa"); @@ -599,6 +602,7 @@ exports.entryPage = "dashboard"; bean.dns_resolve_type = monitor.dns_resolve_type; bean.dns_resolve_server = monitor.dns_resolve_server; bean.pushToken = monitor.pushToken; + bean.proxyId = Number.isInteger(monitor.proxyId) ? monitor.proxyId : null; await R.store(bean); @@ -1061,6 +1065,52 @@ exports.entryPage = "dashboard"; } }); + socket.on("addProxy", async (proxy, proxyID, callback) => { + try { + checkLogin(socket); + + const proxyBean = await Proxy.save(proxy, proxyID, socket.userID); + await sendProxyList(socket); + + if (proxy.applyExisting) { + await restartMonitors(socket.userID); + } + + callback({ + ok: true, + msg: "Saved", + id: proxyBean.id, + }); + + } catch (e) { + callback({ + ok: false, + msg: e.message, + }); + } + }); + + socket.on("deleteProxy", async (proxyID, callback) => { + try { + checkLogin(socket); + + await Proxy.delete(proxyID, socket.userID); + await sendProxyList(socket); + await restartMonitors(socket.userID); + + callback({ + ok: true, + msg: "Deleted", + }); + + } catch (e) { + callback({ + ok: false, + msg: e.message, + }); + } + }); + socket.on("checkApprise", async (callback) => { try { checkLogin(socket); @@ -1079,6 +1129,7 @@ exports.entryPage = "dashboard"; console.log(`Importing Backup, User ID: ${socket.userID}, Version: ${backupData.version}`); let notificationListData = backupData.notificationList; + let proxyListData = backupData.proxyList; let monitorListData = backupData.monitorList; let version17x = compareVersions.compare(backupData.version, "1.7.0", ">="); @@ -1097,6 +1148,7 @@ exports.entryPage = "dashboard"; await R.exec("DELETE FROM monitor_tag"); await R.exec("DELETE FROM tag"); await R.exec("DELETE FROM monitor"); + await R.exec("DELETE FROM proxy"); } // Only starts importing if the backup file contains at least one notification @@ -1116,6 +1168,24 @@ exports.entryPage = "dashboard"; } } + // Only starts importing if the backup file contains at least one proxy + if (proxyListData.length >= 1) { + const proxies = await R.findAll("proxy"); + + // Loop over proxy list and save proxies + for (const proxy of proxyListData) { + const exists = proxies.find(item => item.id === proxy.id); + + // Do not process when proxy already exists in import handle is skip and keep + if (["skip", "keep"].includes(importHandle) && !exists) { + return; + } + + // Save proxy as new entry if exists update exists one + await Proxy.save(proxy, exists ? proxy.id : undefined, proxy.userId); + } + } + // Only starts importing if the backup file contains at least one monitor if (monitorListData.length >= 1) { // Get every existing monitor name and puts them in one simple string @@ -1165,6 +1235,7 @@ exports.entryPage = "dashboard"; dns_resolve_type: monitorListData[i].dns_resolve_type, dns_resolve_server: monitorListData[i].dns_resolve_server, notificationIDList: {}, + proxy_id: monitorListData[i].proxy_id || null, }; if (monitorListData[i].pushToken) { @@ -1400,6 +1471,7 @@ async function afterLogin(socket, user) { let monitorList = await sendMonitorList(socket); sendNotificationList(socket); + sendProxyList(socket); await sleep(500); @@ -1490,6 +1562,19 @@ async function restartMonitor(userID, monitorID) { return await startMonitor(userID, monitorID); } +async function restartMonitors(userID) { + // Fetch all active monitors for user + const monitors = await R.getAll("SELECT id FROM monitor WHERE active = 1 AND user_id = ?", [userID]); + + for (const monitor of monitors) { + // Start updated monitor + await startMonitor(userID, monitor.id); + + // Give some delays, so all monitors won't make request at the same moment when just start the server. + await sleep(getRandomInt(300, 1000)); + } +} + async function pauseMonitor(userID, monitorID) { await checkOwner(userID, monitorID); diff --git a/src/components/ProxyDialog.vue b/src/components/ProxyDialog.vue new file mode 100644 index 000000000..372cc64b4 --- /dev/null +++ b/src/components/ProxyDialog.vue @@ -0,0 +1,203 @@ + + + + + diff --git a/src/components/settings/Proxies.vue b/src/components/settings/Proxies.vue new file mode 100644 index 000000000..344cbb6e0 --- /dev/null +++ b/src/components/settings/Proxies.vue @@ -0,0 +1,47 @@ + + + + + diff --git a/src/languages/en.js b/src/languages/en.js index 40c9c89f3..cd62de178 100644 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -369,4 +369,12 @@ export default { alertaApiKey: 'API Key', alertaAlertState: 'Alert State', alertaRecoverState: 'Recover State', + Proxies: "Proxies", + default: "Default", + enabled: "Enabled", + setAsDefault: "Set As Default", + deleteProxyMsg: "Are you sure want to delete this proxy for all monitors?", + proxyDescription: "Proxies must be assigned to a monitor to function.", + enableProxyDescription: "This proxy will not effect on monitor requests until it is activated. You can control temporarily disable the proxy from all monitors by activation status.", + setAsDefaultProxyDescription: "This proxy will be enabled by default for new monitors. You can still disable the proxy separately for each monitor.", }; diff --git a/src/mixins/socket.js b/src/mixins/socket.js index affac4f82..2f127bcf4 100644 --- a/src/mixins/socket.js +++ b/src/mixins/socket.js @@ -33,6 +33,7 @@ export default { uptimeList: { }, tlsInfoList: {}, notificationList: [], + proxyList: [], connectionErrorMsg: "Cannot connect to the socket server. Reconnecting...", }; }, @@ -103,6 +104,16 @@ export default { this.notificationList = data; }); + socket.on("proxyList", (data) => { + this.proxyList = data.map(item => { + item.auth = !!item.auth; + item.active = !!item.active; + item.default = !!item.default; + + return item; + }); + }); + socket.on("heartbeat", (data) => { if (! (data.monitorID in this.heartbeatList)) { this.heartbeatList[data.monitorID] = []; diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue index f89e63bf6..47e685285 100644 --- a/src/pages/EditMonitor.vue +++ b/src/pages/EditMonitor.vue @@ -222,6 +222,32 @@ {{ $t("Setup Notification") }} + +

{{ $t("Proxies") }}

+

+ {{ $t("Not available, please setup.") }} +

+ +
+ + +
+ +
+ + + + + {{ $t("default") }} +
+ + + diff --git a/src/pages/Settings.vue b/src/pages/Settings.vue index 1717dd524..cb54ae495 100644 --- a/src/pages/Settings.vue +++ b/src/pages/Settings.vue @@ -81,6 +81,9 @@ export default { security: { title: this.$t("Security"), }, + proxies: { + title: this.$t("Proxies"), + }, backup: { title: this.$t("Backup"), }, diff --git a/src/router.js b/src/router.js index c881dc979..1b79dfcb1 100644 --- a/src/router.js +++ b/src/router.js @@ -16,6 +16,7 @@ import General from "./components/settings/General.vue"; import Notifications from "./components/settings/Notifications.vue"; import MonitorHistory from "./components/settings/MonitorHistory.vue"; import Security from "./components/settings/Security.vue"; +import Proxies from "./components/settings/Proxies.vue"; import Backup from "./components/settings/Backup.vue"; import About from "./components/settings/About.vue"; @@ -88,6 +89,10 @@ const routes = [ path: "security", component: Security, }, + { + path: "proxies", + component: Proxies, + }, { path: "backup", component: Backup, From 9e27acb511a2449131e5abf4b8733b1a13826527 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?U=C4=9Fur=20Erkan?= Date: Thu, 4 Nov 2021 13:53:54 +0300 Subject: [PATCH 14/84] Add socks proxy agent --- package-lock.json | 15 ++++----------- package.json | 1 + 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/package-lock.json b/package-lock.json index 19b3c153d..02971c980 100644 --- a/package-lock.json +++ b/package-lock.json @@ -48,6 +48,7 @@ "redbean-node": "0.1.3", "socket.io": "~4.4.1", "socket.io-client": "~4.4.1", + "socks-proxy-agent": "^6.1.0", "tar": "^6.1.11", "tcp-ping": "~0.1.1", "thirty-two": "~1.0.2", @@ -7008,8 +7009,7 @@ "node_modules/ip": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", - "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", - "optional": true + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" }, "node_modules/ipaddr.js": { "version": "1.9.1", @@ -11958,7 +11958,6 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "optional": true, "engines": { "node": ">= 6.0.0", "npm": ">= 3.0.0" @@ -12030,7 +12029,6 @@ "version": "2.6.1", "resolved": "https://registry.npmjs.org/socks/-/socks-2.6.1.tgz", "integrity": "sha512-kLQ9N5ucj8uIcxrDwjm0Jsqk06xdpBjGNQtpXy4Q8/QY2k+fY7nZH8CARy+hkbG+SGAovmzzuauCpBlb8FrnBA==", - "optional": true, "dependencies": { "ip": "^1.1.5", "smart-buffer": "^4.1.0" @@ -12044,7 +12042,6 @@ "version": "6.1.1", "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.1.1.tgz", "integrity": "sha512-t8J0kG3csjA4g6FTbsMOWws+7R7vuRC8aQ/wy3/1OWmsgwA68zs/+cExQ0koSitUDXqhufF/YJr9wtNMZHw5Ew==", - "optional": true, "dependencies": { "agent-base": "^6.0.2", "debug": "^4.3.1", @@ -18930,8 +18927,7 @@ "ip": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.5.tgz", - "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=", - "optional": true + "integrity": "sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo=" }, "ipaddr.js": { "version": "1.9.1", @@ -22673,8 +22669,7 @@ "smart-buffer": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "optional": true + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==" }, "socket.io": { "version": "4.4.1", @@ -22732,7 +22727,6 @@ "version": "2.6.1", "resolved": "https://registry.npmjs.org/socks/-/socks-2.6.1.tgz", "integrity": "sha512-kLQ9N5ucj8uIcxrDwjm0Jsqk06xdpBjGNQtpXy4Q8/QY2k+fY7nZH8CARy+hkbG+SGAovmzzuauCpBlb8FrnBA==", - "optional": true, "requires": { "ip": "^1.1.5", "smart-buffer": "^4.1.0" @@ -22742,7 +22736,6 @@ "version": "6.1.1", "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.1.1.tgz", "integrity": "sha512-t8J0kG3csjA4g6FTbsMOWws+7R7vuRC8aQ/wy3/1OWmsgwA68zs/+cExQ0koSitUDXqhufF/YJr9wtNMZHw5Ew==", - "optional": true, "requires": { "agent-base": "^6.0.2", "debug": "^4.3.1", diff --git a/package.json b/package.json index cc6f6593c..af194e2ec 100644 --- a/package.json +++ b/package.json @@ -93,6 +93,7 @@ "redbean-node": "0.1.3", "socket.io": "~4.4.1", "socket.io-client": "~4.4.1", + "socks-proxy-agent": "^6.1.0", "tar": "^6.1.11", "tcp-ping": "~0.1.1", "thirty-two": "~1.0.2", From 8078d0618d076d913feb03f78673860cfec00f44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?U=C4=9Fur=20Erkan?= Date: Thu, 4 Nov 2021 14:32:16 +0300 Subject: [PATCH 15/84] Add socks proxy support to proxy feature - Socks proxy support implemented. - Monitor proxy agent create flow refactored and moved under proxy class. Thanks for suggestion @thomasleveil --- server/model/monitor.js | 31 +++--------- server/proxy.js | 75 ++++++++++++++++++++++++++++- src/components/ProxyDialog.vue | 3 ++ src/components/settings/Proxies.vue | 3 +- src/pages/EditMonitor.vue | 2 +- 5 files changed, 85 insertions(+), 29 deletions(-) diff --git a/server/model/monitor.js b/server/model/monitor.js index f938ca80b..f9c025d11 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -1,6 +1,4 @@ const https = require("https"); -const HttpProxyAgent = require("http-proxy-agent"); -const HttpsProxyAgent = require("https-proxy-agent"); const dayjs = require("dayjs"); const utc = require("dayjs/plugin/utc"); let timezone = require("dayjs/plugin/timezone"); @@ -13,6 +11,7 @@ const { tcping, ping, dnsResolve, checkCertificate, checkStatusCode, getTotalCli const { R } = require("redbean-node"); const { BeanModel } = require("redbean-node/dist/bean-model"); const { Notification } = require("../notification"); +const { Proxy } = require("../proxy"); const { demoMode } = require("../config"); const version = require("../../package.json").version; const apicache = require("../modules/apicache"); @@ -204,31 +203,13 @@ class Monitor extends BeanModel { const proxy = await R.load("proxy", this.proxy_id); if (proxy && proxy.active) { - const httpProxyAgentOptions = { - protocol: proxy.protocol, - host: proxy.host, - port: proxy.port, - }; - const httpsProxyAgentOptions = { - ...httpsAgentOptions, - protocol: proxy.protocol, - hostname: proxy.host, - port: proxy.port, - }; - - if (proxy.auth) { - httpProxyAgentOptions.auth = `${proxy.username}:${proxy.password}`; - httpsProxyAgentOptions.auth = `${proxy.username}:${proxy.password}`; - } - - debug(`[${this.name}] HTTP options: ${JSON.stringify({ - "http": httpProxyAgentOptions, - "https": httpsProxyAgentOptions, - })}`); + const { httpAgent, httpsAgent } = Proxy.createAgents(proxy, { + httpsAgentOptions: httpsAgentOptions, + }); options.proxy = false; - options.httpAgent = new HttpProxyAgent(httpProxyAgentOptions); - options.httpsAgent = new HttpsProxyAgent(httpsProxyAgentOptions); + options.httpAgent = httpAgent; + options.httpsAgent = httpsAgent; } } diff --git a/server/proxy.js b/server/proxy.js index df3831538..392a0af7f 100644 --- a/server/proxy.js +++ b/server/proxy.js @@ -1,7 +1,13 @@ const { R } = require("redbean-node"); +const HttpProxyAgent = require("http-proxy-agent"); +const HttpsProxyAgent = require("https-proxy-agent"); +const SocksProxyAgent = require("socks-proxy-agent"); +const { debug } = require("../src/util"); class Proxy { + static SUPPORTED_PROXY_PROTOCOLS = ["http", "https", "socks", "socks5", "socks4"] + /** * Saves and updates given proxy entity * @@ -25,8 +31,11 @@ class Proxy { } // Make sure given proxy protocol is supported - if (!["http", "https"].includes(proxy.protocol)) { - throw new Error(`Unsupported proxy protocol "${proxy.protocol}. Supported protocols are http and https."`); + if (!this.SUPPORTED_PROXY_PROTOCOLS.includes(proxy.protocol)) { + throw new Error(` + Unsupported proxy protocol "${proxy.protocol}. + Supported protocols are ${this.SUPPORTED_PROXY_PROTOCOLS.join(", ")}."` + ); } // When proxy is default update deactivate old default proxy @@ -73,6 +82,68 @@ class Proxy { // Delete proxy from list await R.trash(bean); } + + /** + * Create HTTP and HTTPS agents related with given proxy bean object + * + * @param proxy proxy bean object + * @param options http and https agent options + * @return {{httpAgent: Agent, httpsAgent: Agent}} + */ + static createAgents(proxy, options) { + const { httpAgentOptions, httpsAgentOptions } = options || {}; + let agent; + let httpAgent; + let httpsAgent; + + const proxyOptions = { + protocol: proxy.protocol, + host: proxy.host, + port: proxy.port, + }; + + if (proxy.auth) { + proxyOptions.auth = `${proxy.username}:${proxy.password}`; + } + + debug(`Proxy Options: ${JSON.stringify(proxyOptions)}`); + debug(`HTTP Agent Options: ${JSON.stringify(httpAgentOptions)}`); + debug(`HTTPS Agent Options: ${JSON.stringify(httpsAgentOptions)}`); + + switch (proxy.protocol) { + case "http": + case "https": + httpAgent = new HttpProxyAgent({ + ...httpAgentOptions || {}, + ...proxyOptions + }); + + httpsAgent = new HttpsProxyAgent({ + ...httpsAgentOptions || {}, + ...proxyOptions, + }); + break; + case "socks": + case "socks5": + case "socks4": + agent = new SocksProxyAgent({ + ...httpAgentOptions, + ...httpsAgentOptions, + ...proxyOptions, + }); + + httpAgent = agent; + httpsAgent = agent; + break; + + default: throw new Error(`Unsupported proxy protocol provided. ${proxy.protocol}`); + } + + return { + httpAgent, + httpsAgent + }; + } } /** diff --git a/src/components/ProxyDialog.vue b/src/components/ProxyDialog.vue index 372cc64b4..32fa04cb5 100644 --- a/src/components/ProxyDialog.vue +++ b/src/components/ProxyDialog.vue @@ -15,6 +15,9 @@ diff --git a/src/components/settings/Proxies.vue b/src/components/settings/Proxies.vue index 344cbb6e0..4608f3aa4 100644 --- a/src/components/settings/Proxies.vue +++ b/src/components/settings/Proxies.vue @@ -11,7 +11,8 @@
  • - {{ proxy.host }}{{ $t("Default") }}
    + {{ proxy.host }}:{{ proxy.port }} ({{ proxy.protocol }}) + {{ $t("Default") }}
    {{ $t("Edit") }}
diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue index 47e685285..42454e522 100644 --- a/src/pages/EditMonitor.vue +++ b/src/pages/EditMonitor.vue @@ -237,7 +237,7 @@ From 623d03dc6f3c5a1162f78ae51b536015bf2dff35 Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Fri, 25 Mar 2022 00:03:25 +0800 Subject: [PATCH 16/84] Fix release process --- extra/press-any-key.js | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/extra/press-any-key.js b/extra/press-any-key.js index 038cafcab..42fc363cd 100644 --- a/extra/press-any-key.js +++ b/extra/press-any-key.js @@ -1,4 +1,4 @@ -console.log("Publish the release note on github, then press any key to continue"); +console.log("Git Push and Publish the release note on github, then press any key to continue"); process.stdin.setRawMode(true); process.stdin.resume(); diff --git a/package.json b/package.json index 326ebf143..134271c06 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ "update-language-files-with-base-lang": "cd extra/update-language-files && node index.js %npm_config_base_lang% && eslint ../../src/languages/**.js --fix", "update-language-files": "cd extra/update-language-files && node index.js && eslint ../../src/languages/**.js --fix", "ncu-patch": "npm-check-updates -u -t patch", - "release-final": "node extra/update-version.js && npm run build-docker && git push --tags && node ./extra/press-any-key.js && npm run upload-artifacts && node ./extra/update-wiki-version.js", + "release-final": "node extra/update-version.js && npm run build-docker && node ./extra/press-any-key.js && npm run upload-artifacts && node ./extra/update-wiki-version.js", "release-beta": "node extra/beta/update-version.js && npm run build && node ./extra/env2arg.js docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:$VERSION -t louislam/uptime-kuma:beta . --target release --push && node ./extra/press-any-key.js && npm run upload-artifacts", "git-remove-tag": "git tag -d" }, From 59227719090cf4c37cf0128836e24defebec810a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ivan=20Bratovi=C4=87?= Date: Fri, 25 Mar 2022 11:05:51 +0100 Subject: [PATCH 17/84] Update croatian (hr-HR) translation file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Ivan Bratović --- src/languages/hr-HR.js | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/languages/hr-HR.js b/src/languages/hr-HR.js index ac851b675..a033edb5c 100644 --- a/src/languages/hr-HR.js +++ b/src/languages/hr-HR.js @@ -183,7 +183,7 @@ export default { "Edit Status Page": "Uredi Statusnu stranicu", "Go to Dashboard": "Na Kontrolnu ploču", "Status Page": "Statusna stranica", - "Status Pages": "Statusna stranica", + "Status Pages": "Statusne stranice", defaultNotificationName: "Moja {number}. {notification} obavijest", here: "ovdje", Required: "Potrebno", @@ -347,4 +347,30 @@ export default { Cancel: "Otkaži", "Powered by": "Pokreće", Saved: "Spremljeno", + PushByTechulus: "Push by Techulus", + GoogleChat: "Google Chat (preko platforme Google Workspace)", + shrinkDatabaseDescription: "Pokreni VACUUM operaciju za SQLite. Ako je baza podataka kreirana nakon inačice 1.10.0, AUTO_VACUUM opcija već je uključena te ova akcija nije nužna.", + serwersms: "SerwerSMS.pl", + serwersmsAPIUser: "API korisničko ime (uključujući webapi_ prefiks)", + serwersmsAPIPassword: "API lozinka", + serwersmsPhoneNumber: "Broj telefona", + serwersmsSenderName: "Ime SMS pošiljatelja (registrirano preko korisničkog portala)", + stackfield: "Stackfield", + smtpDkimSettings: "DKIM postavke", + smtpDkimDesc: "Za više informacija, postoji Nodemailer DKIM {0}.", + documentation: "dokumentacija", + smtpDkimDomain: "Domena", + smtpDkimKeySelector: "Odabir ključa", + smtpDkimPrivateKey: "Privatni ključ", + smtpDkimHashAlgo: "Hash algoritam (neobavezno)", + smtpDkimheaderFieldNames: "Ključevi zaglavlja za potpis (neobavezno)", + smtpDkimskipFields: "Ključevi zaglavlja koji se neće potpisati (neobavezno)", + gorush: "Gorush", + alerta: "Alerta", + alertaApiEndpoint: "Krajnja točka API-ja (Endpoint)", + alertaEnvironment: "Okruženje (Environment)", + alertaApiKey: "API ključ", + alertaAlertState: "Stanje upozorenja", + alertaRecoverState: "Stanje oporavka", + deleteStatusPageMsg: "Sigurno želite obrisati ovu statusnu stranicu?", }; From 1ecd2e45d058ada5c4bffedfcaa34216b09e157e Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Fri, 25 Mar 2022 18:59:06 +0800 Subject: [PATCH 18/84] [Status Page] Plan to support domain names for status pages --- src/pages/Entry.vue | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/pages/Entry.vue b/src/pages/Entry.vue index 6148ec56d..90f039162 100644 --- a/src/pages/Entry.vue +++ b/src/pages/Entry.vue @@ -1,5 +1,9 @@ + + diff --git a/src/router.js b/src/router.js index 76dfa5cbe..f59192d3e 100644 --- a/src/router.js +++ b/src/router.js @@ -20,6 +20,7 @@ import Backup from "./components/settings/Backup.vue"; import About from "./components/settings/About.vue"; import ManageStatusPage from "./pages/ManageStatusPage.vue"; import AddStatusPage from "./pages/AddStatusPage.vue"; +import NotFound from "./pages/NotFound.vue"; const routes = [ { @@ -128,6 +129,10 @@ const routes = [ path: "/status/:slug", component: StatusPage, }, + { + path: "/:pathMatch(.*)*", + component: NotFound, + }, ]; export const router = createRouter({ From e11ea7b0618d59dbd5bf13b020b00a4b192b6a70 Mon Sep 17 00:00:00 2001 From: sovushik <30425777+sovushik@users.noreply.github.com> Date: Sat, 26 Mar 2022 10:46:07 +0500 Subject: [PATCH 20/84] Update ru-RU.js Add new string for 1.13.1 --- src/languages/ru-RU.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/languages/ru-RU.js b/src/languages/ru-RU.js index d5bec924e..cfc263e0b 100644 --- a/src/languages/ru-RU.js +++ b/src/languages/ru-RU.js @@ -181,7 +181,7 @@ export default { "Edit Status Page": "Редактировать", "Go to Dashboard": "Панель управления", "Status Page": "Мониторинг", - "Status Pages": "Página de Status", + "Status Pages": "Панель мониторингов", Discard: "Отмена", "Create Incident": "Создать инцидент", "Switch to Dark Theme": "Тёмная тема", @@ -343,4 +343,14 @@ export default { primary: "ОСНОВНОЙ", light: "СВЕТЛЫЙ", dark: "ТЕМНЫЙ", + "New Status Page": "Новый мониторинг", + "Show update if available": "Показывать доступные обновления", + "Also check beta release": "Проверять обновления для бета версий", + "Add New Status Page": "Добавить страницу мониторинга", + "Next": "Далее", + "Accept characters: a-z 0-9 -": "Разрешены символы: a-z 0-9 -", + "Start or end with a-z 0-9 only": "Начало и окончание имени только на символы: a-z 0-9", + "No consecutive dashes --": "Запрещено использовать тире --", + "HTTP Options": "HTTP Опции", + "Basic Auth": "HTTP Авторизация", }; From 381605aca1fe80b95684fe5fbaaa8a89343f030d Mon Sep 17 00:00:00 2001 From: AnnAngela Date: Sun, 27 Mar 2022 20:55:28 +0800 Subject: [PATCH 21/84] Update update-version.js --- extra/update-version.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/extra/update-version.js b/extra/update-version.js index 302863002..21fdf3227 100644 --- a/extra/update-version.js +++ b/extra/update-version.js @@ -10,14 +10,14 @@ const newVersion = process.env.VERSION; console.log("New Version: " + newVersion); -if (!newVersion) { +if (! newVersion) { console.error("invalid version"); process.exit(1); } const exists = tagExists(newVersion); -if (!exists) { +if (! exists) { // Process package.json pkg.version = newVersion; @@ -51,7 +51,7 @@ function tag(version) { } function tagExists(version) { - if (!version) { + if (! version) { throw new Error("invalid version"); } @@ -93,4 +93,4 @@ function safeDelete(dir) { recursive: true, }); } -} \ No newline at end of file +} From 96289fe014d42d2a4147d826c2d5e262b64e9d1b Mon Sep 17 00:00:00 2001 From: AnnAngela Date: Sun, 27 Mar 2022 20:56:42 +0800 Subject: [PATCH 22/84] Update index.js --- extra/update-language-files/index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/extra/update-language-files/index.js b/extra/update-language-files/index.js index 1f58d01f3..e449fe347 100644 --- a/extra/update-language-files/index.js +++ b/extra/update-language-files/index.js @@ -41,7 +41,7 @@ const files = fs.readdirSync("./languages"); console.log("Files:", files); for (const file of files) { - if (!file.endsWith(".js")) { + if (! file.endsWith(".js")) { console.log("Skipping " + file); continue; } @@ -62,7 +62,7 @@ for (const file of files) { // En first for (const key in en) { - if (!obj[key]) { + if (! obj[key]) { obj[key] = en[key]; } } @@ -70,7 +70,7 @@ for (const file of files) { if (baseLang !== en) { // Base second for (const key in baseLang) { - if (!obj[key]) { + if (! obj[key]) { obj[key] = key; } } From 7fd5b61babcbbe6e568d7c2d8cbbe0324f52ff50 Mon Sep 17 00:00:00 2001 From: AnnAngela Date: Sun, 27 Mar 2022 21:12:51 +0800 Subject: [PATCH 23/84] Inproperly conflict resolving --- extra/update-version.js | 35 ----------------------------------- 1 file changed, 35 deletions(-) diff --git a/extra/update-version.js b/extra/update-version.js index 21fdf3227..505bb2cb0 100644 --- a/extra/update-version.js +++ b/extra/update-version.js @@ -59,38 +59,3 @@ function tagExists(version) { return res.stdout.toString().trim() === version; } - -function updateWiki(oldVersion, newVersion) { - const wikiDir = "./tmp/wiki"; - const howToUpdateFilename = "./tmp/wiki/🆙-How-to-Update.md"; - - safeDelete(wikiDir); - - child_process.spawnSync("git", ["clone", "https://github.com/louislam/uptime-kuma.wiki.git", wikiDir]); - let content = fs.readFileSync(howToUpdateFilename).toString(); - content = content.replaceAll(`git checkout ${oldVersion}`, `git checkout ${newVersion}`); - fs.writeFileSync(howToUpdateFilename, content); - - child_process.spawnSync("git", ["add", "-A"], { - cwd: wikiDir, - }); - - child_process.spawnSync("git", ["commit", "-m", `Update to ${newVersion} from ${oldVersion}`], { - cwd: wikiDir, - }); - - console.log("Pushing to Github"); - child_process.spawnSync("git", ["push"], { - cwd: wikiDir, - }); - - safeDelete(wikiDir); -} - -function safeDelete(dir) { - if (fs.existsSync(dir)) { - rmSync(dir, { - recursive: true, - }); - } -} From 61d0a0abce8d699a44b9df3d84c70e05586a8a57 Mon Sep 17 00:00:00 2001 From: DX37 Date: Mon, 28 Mar 2022 21:16:13 +0700 Subject: [PATCH 24/84] update russian translation --- src/languages/ru-RU.js | 64 +++++++++++++++++++++++++++++++++--------- 1 file changed, 50 insertions(+), 14 deletions(-) diff --git a/src/languages/ru-RU.js b/src/languages/ru-RU.js index cfc263e0b..c8212442b 100644 --- a/src/languages/ru-RU.js +++ b/src/languages/ru-RU.js @@ -180,8 +180,8 @@ export default { "Add a monitor": "Добавить монитор", "Edit Status Page": "Редактировать", "Go to Dashboard": "Панель управления", - "Status Page": "Мониторинг", - "Status Pages": "Панель мониторингов", + "Status Page": "Страница статуса", + "Status Pages": "Страницы статуса", Discard: "Отмена", "Create Incident": "Создать инцидент", "Switch to Dark Theme": "Тёмная тема", @@ -311,28 +311,28 @@ export default { "One record": "Одна запись", steamApiKeyDescription: "Для мониторинга игрового сервера Steam вам необходим Web-API ключ Steam. Зарегистрировать его можно здесь: ", "Certificate Chain": "Цепочка сертификатов", - "Valid": "Действительный", + Valid: "Действительный", "Hide Tags": "Скрыть тэги", Title: "Название инцидента:", Content: "Содержание инцидента:", Post: "Опубликовать", - "Cancel": "Отмена", - "Created": "Создано", - "Unpin": "Открепить", + Cancel: "Отмена", + Created: "Создано", + Unpin: "Открепить", "Show Tags": "Показать тэги", - "recent": "Сейчас", + recent: "Сейчас", "3h": "3 часа", "6h": "6 часов", "24h": "24 часа", "1w": "1 неделя", "No monitors available.": "Нет доступных мониторов", "Add one": "Добавить новый", - "Backup": "Резервная копия", - "Security": "Безопасность", + Backup: "Резервная копия", + Security: "Безопасность", "Shrink Database": "Сжать Базу Данных", "Current User": "Текущий пользователь", - "About": "О программе", - "Description": "Описание", + About: "О программе", + Description: "Описание", "Powered by": "Работает на основе скрипта от", shrinkDatabaseDescription: "Включает VACUUM для базы данных SQLite. Если ваша база данных была создана на версии 1.10.0 и более, AUTO_VACUUM уже включен и это действие не требуется.", deleteStatusPageMsg: "Вы действительно хотите удалить эту страницу статуса сервисов?", @@ -343,14 +343,50 @@ export default { primary: "ОСНОВНОЙ", light: "СВЕТЛЫЙ", dark: "ТЕМНЫЙ", - "New Status Page": "Новый мониторинг", + "New Status Page": "Новая страница статуса", "Show update if available": "Показывать доступные обновления", "Also check beta release": "Проверять обновления для бета версий", - "Add New Status Page": "Добавить страницу мониторинга", - "Next": "Далее", + "Add New Status Page": "Добавить страницу статуса", + Next: "Далее", "Accept characters: a-z 0-9 -": "Разрешены символы: a-z 0-9 -", "Start or end with a-z 0-9 only": "Начало и окончание имени только на символы: a-z 0-9", "No consecutive dashes --": "Запрещено использовать тире --", "HTTP Options": "HTTP Опции", "Basic Auth": "HTTP Авторизация", + PushByTechulus: "Push by Techulus", + clicksendsms: "ClickSend SMS", + GoogleChat: "Google Chat (только Google Workspace)", + apiCredentials: "API реквизиты", + Done: "Готово", + Info: "Инфо", + "Steam API Key": "Steam API-Ключ", + "Pick a RR-Type...": "Выберите RR-Тип...", + "Pick Accepted Status Codes...": "Выберите принятые коды состояния...", + Default: "По умолчанию", + "Please input title and content": "Пожалуйста, введите название и содержание", + "Last Updated": "Последнее Обновление", + "Untitled Group": "Группа без названия", + Services: "Сервисы", + serwersms: "SerwerSMS.pl", + serwersmsAPIUser: "API Пользователь (включая префикс webapi_)", + serwersmsAPIPassword: "API Пароль", + serwersmsPhoneNumber: "Номер телефона", + serwersmsSenderName: "SMS Имя Отправителя (регистрированный через пользовательский портал)", + stackfield: "Stackfield", + smtpDkimSettings: "DKIM Настройки", + smtpDkimDesc: "Please refer to the Nodemailer DKIM {0} for usage.", + documentation: "документация", + smtpDkimDomain: "Имя Домена", + smtpDkimKeySelector: "Ключ", + smtpDkimPrivateKey: "Приватный ключ", + smtpDkimHashAlgo: "Алгоритм хэша (опционально)", + smtpDkimheaderFieldNames: "Заголовок ключей для подписи (опционально)", + smtpDkimskipFields: "Заколовок ключей не для подписи (опционально)", + gorush: "Gorush", + alerta: "Alerta", + alertaApiEndpoint: "Конечная точка API", + alertaEnvironment: "Среда", + alertaApiKey: "Ключ API", + alertaAlertState: "Состояние алерта", + alertaRecoverState: "Состояние восстановления", }; From 7d3cbff79475c7e32818e7c77a208c94c165a0dd Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Tue, 29 Mar 2022 02:24:10 +0800 Subject: [PATCH 25/84] [Cloudflared] Install into base docker --- docker/debian-base.dockerfile | 14 +++++++++++ extra/download-cloudflared.js | 44 +++++++++++++++++++++++++++++++++++ package.json | 1 + 3 files changed, 59 insertions(+) create mode 100644 extra/download-cloudflared.js diff --git a/docker/debian-base.dockerfile b/docker/debian-base.dockerfile index 9a8c759bb..62889dc94 100644 --- a/docker/debian-base.dockerfile +++ b/docker/debian-base.dockerfile @@ -1,8 +1,11 @@ # DON'T UPDATE TO node:14-bullseye-slim, see #372. # If the image changed, the second stage image should be changed too FROM node:16-buster-slim +ARG TARGETPLATFORM + WORKDIR /app +# Install Curl # Install Apprise, add sqlite3 cli for debugging in the future, iputils-ping for ping, util-linux for setpriv # Stupid python3 and python3-pip actually install a lot of useless things into Debian, specify --no-install-recommends to skip them, make the base even smaller than alpine! RUN apt update && \ @@ -10,3 +13,14 @@ RUN apt update && \ sqlite3 iputils-ping util-linux dumb-init && \ pip3 --no-cache-dir install apprise==0.9.7 && \ rm -rf /var/lib/apt/lists/* + +# Install cloudflared +# dpkg --add-architecture arm: cloudflared do not provide armhf, this is workaround. Read more: https://github.com/cloudflare/cloudflared/issues/583 +COPY extra/download-cloudflared.js ./extra/download-cloudflared.js +RUN node ./extra/download-cloudflared.js $TARGETPLATFORM && \ + dpkg --add-architecture arm && \ + apt update && \ + apt --yes --no-install-recommends install ./cloudflared.deb && \ + rm -rf /var/lib/apt/lists/* && \ + rm -f cloudflared.deb + diff --git a/extra/download-cloudflared.js b/extra/download-cloudflared.js new file mode 100644 index 000000000..41519b7ca --- /dev/null +++ b/extra/download-cloudflared.js @@ -0,0 +1,44 @@ +// + +const http = require("https"); // or 'https' for https:// URLs +const fs = require("fs"); + +const platform = process.argv[2]; + +if (!platform) { + console.error("No platform??"); + process.exit(1); +} + +let arch = null; + +if (platform === "linux/amd64") { + arch = "amd64"; +} else if (platform === "linux/arm64") { + arch = "arm64"; +} else if (platform === "linux/arm/v7") { + arch = "arm"; +} else { + console.error("Invalid platform?? " + platform); +} + +const file = fs.createWriteStream("cloudflared.deb"); +get("https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-" + arch + ".deb"); + +function get(url) { + http.get(url, function (res) { + if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) { + console.log("Redirect to " + res.headers.location); + get(res.headers.location); + } else if (res.statusCode >= 200 && res.statusCode < 300) { + res.pipe(file); + + res.on("end", function () { + console.log("Downloaded"); + }); + } else { + console.error(res.statusCode); + process.exit(1); + } + }); +} diff --git a/package.json b/package.json index 134271c06..24558dc5d 100644 --- a/package.json +++ b/package.json @@ -83,6 +83,7 @@ "jsonwebtoken": "~8.5.1", "jwt-decode": "^3.1.2", "limiter": "^2.1.0", + "node-cloudflared-tunnel": "~1.0.0", "nodemailer": "~6.6.5", "notp": "~2.0.3", "password-hash": "~1.2.2", From 44fb2a88f290acec2ba750d678a6a45cee81d394 Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Tue, 29 Mar 2022 14:48:02 +0800 Subject: [PATCH 26/84] Add cloudflared socket handler --- server/server.js | 2 ++ .../cloudflared-socket-handler.js | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 server/socket-handlers/cloudflared-socket-handler.js diff --git a/server/server.js b/server/server.js index 9a5e1028b..602b5a866 100644 --- a/server/server.js +++ b/server/server.js @@ -133,6 +133,7 @@ const { statusPageSocketHandler } = require("./socket-handlers/status-page-socke const databaseSocketHandler = require("./socket-handlers/database-socket-handler"); const TwoFA = require("./2fa"); const StatusPage = require("./model/status_page"); +const { cloudflaredSocketHandler } = require("./socket-handlers/cloudflared-socket-handler"); app.use(express.json()); @@ -1319,6 +1320,7 @@ exports.entryPage = "dashboard"; // Status Page Socket Handler for admin only statusPageSocketHandler(socket); + cloudflaredSocketHandler(socket); databaseSocketHandler(socket); debug("added all socket handlers"); diff --git a/server/socket-handlers/cloudflared-socket-handler.js b/server/socket-handlers/cloudflared-socket-handler.js new file mode 100644 index 000000000..95dd4d80b --- /dev/null +++ b/server/socket-handlers/cloudflared-socket-handler.js @@ -0,0 +1,19 @@ +const { checkLogin } = require("../util-server"); + +const prefix = "cloudflared_"; + +module.exports.cloudflaredSocketHandler = (socket) => { + + socket.on(prefix + "start", async (callback) => { + try { + checkLogin(socket); + + } catch (error) { + callback({ + ok: false, + msg: error.message, + }); + } + }); + +}; From 0da6e6b1fb5ee7bc687d491eb3a183061d53b418 Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Tue, 29 Mar 2022 17:38:48 +0800 Subject: [PATCH 27/84] Some improvements --- server/auth.js | 4 + server/rate-limiter.js | 10 ++- server/server.js | 120 ++++++++++++++++++--------- server/util-server.js | 27 +++++- src/components/MonitorList.vue | 4 +- src/components/TwoFADialog.vue | 58 ++++++++----- src/components/settings/Security.vue | 24 +++++- src/pages/Settings.vue | 12 ++- 8 files changed, 188 insertions(+), 71 deletions(-) diff --git a/server/auth.js b/server/auth.js index 1196f94d7..c59d65492 100644 --- a/server/auth.js +++ b/server/auth.js @@ -12,6 +12,10 @@ const { loginRateLimiter } = require("./rate-limiter"); * @returns {Promise} */ exports.login = async function (username, password) { + if (typeof username !== "string" || typeof password !== "string") { + return null; + } + let user = await R.findOne("user", " username = ? AND active = 1 ", [ username, ]); diff --git a/server/rate-limiter.js b/server/rate-limiter.js index 0bacc14c7..6422af8d2 100644 --- a/server/rate-limiter.js +++ b/server/rate-limiter.js @@ -34,6 +34,14 @@ const loginRateLimiter = new KumaRateLimiter({ errorMessage: "Too frequently, try again later." }); +const twoFaRateLimiter = new KumaRateLimiter({ + tokensPerInterval: 30, + interval: "minute", + fireImmediately: true, + errorMessage: "Too frequently, try again later." +}); + module.exports = { - loginRateLimiter + loginRateLimiter, + twoFaRateLimiter, }; diff --git a/server/server.js b/server/server.js index 9a5e1028b..cac2bdb63 100644 --- a/server/server.js +++ b/server/server.js @@ -52,7 +52,7 @@ console.log("Importing this project modules"); debug("Importing Monitor"); const Monitor = require("./model/monitor"); debug("Importing Settings"); -const { getSettings, setSettings, setting, initJWTSecret, checkLogin, startUnitTest, FBSD, errorLog } = require("./util-server"); +const { getSettings, setSettings, setting, initJWTSecret, checkLogin, startUnitTest, FBSD, errorLog, doubleCheckPassword } = require("./util-server"); debug("Importing Notification"); const { Notification } = require("./notification"); @@ -63,7 +63,7 @@ const Database = require("./database"); debug("Importing Background Jobs"); const { initBackgroundJobs } = require("./jobs"); -const { loginRateLimiter } = require("./rate-limiter"); +const { loginRateLimiter, twoFaRateLimiter } = require("./rate-limiter"); const { basicAuth } = require("./auth"); const { login } = require("./auth"); @@ -305,6 +305,15 @@ exports.entryPage = "dashboard"; socket.on("login", async (data, callback) => { console.log("Login"); + // Checking + if (typeof callback !== "function") { + return; + } + + if (!data) { + return; + } + // Login Rate Limit if (! await loginRateLimiter.pass(callback)) { return; @@ -363,14 +372,27 @@ exports.entryPage = "dashboard"; }); socket.on("logout", async (callback) => { + // Rate Limit + if (! await loginRateLimiter.pass(callback)) { + return; + } + socket.leave(socket.userID); socket.userID = null; - callback(); + + if (typeof callback === "function") { + callback(); + } }); - socket.on("prepare2FA", async (callback) => { + socket.on("prepare2FA", async (currentPassword, callback) => { try { + if (! await twoFaRateLimiter.pass(callback)) { + return; + } + checkLogin(socket); + await doubleCheckPassword(socket, currentPassword); let user = await R.findOne("user", " id = ? AND active = 1 ", [ socket.userID, @@ -405,14 +427,19 @@ exports.entryPage = "dashboard"; } catch (error) { callback({ ok: false, - msg: "Error while trying to prepare 2FA.", + msg: error.message, }); } }); - socket.on("save2FA", async (callback) => { + socket.on("save2FA", async (currentPassword, callback) => { try { + if (! await twoFaRateLimiter.pass(callback)) { + return; + } + checkLogin(socket); + await doubleCheckPassword(socket, currentPassword); await R.exec("UPDATE `user` SET twofa_status = 1 WHERE id = ? ", [ socket.userID, @@ -425,14 +452,19 @@ exports.entryPage = "dashboard"; } catch (error) { callback({ ok: false, - msg: "Error while trying to change 2FA.", + msg: error.message, }); } }); - socket.on("disable2FA", async (callback) => { + socket.on("disable2FA", async (currentPassword, callback) => { try { + if (! await twoFaRateLimiter.pass(callback)) { + return; + } + checkLogin(socket); + await doubleCheckPassword(socket, currentPassword); await TwoFA.disable2FA(socket.userID); callback({ @@ -442,36 +474,47 @@ exports.entryPage = "dashboard"; } catch (error) { callback({ ok: false, - msg: "Error while trying to change 2FA.", + msg: error.message, }); } }); - socket.on("verifyToken", async (token, callback) => { - let user = await R.findOne("user", " id = ? AND active = 1 ", [ - socket.userID, - ]); + socket.on("verifyToken", async (token, currentPassword, callback) => { + try { + checkLogin(socket); + await doubleCheckPassword(socket, currentPassword); - let verify = notp.totp.verify(token, user.twofa_secret, twofa_verification_opts); + let user = await R.findOne("user", " id = ? AND active = 1 ", [ + socket.userID, + ]); - if (user.twofa_last_token !== token && verify) { - callback({ - ok: true, - valid: true, - }); - } else { + let verify = notp.totp.verify(token, user.twofa_secret, twofa_verification_opts); + + if (user.twofa_last_token !== token && verify) { + callback({ + ok: true, + valid: true, + }); + } else { + callback({ + ok: false, + msg: "Invalid Token.", + valid: false, + }); + } + + } catch (error) { callback({ ok: false, - msg: "Invalid Token.", - valid: false, + msg: error.message, }); } }); socket.on("twoFAStatus", async (callback) => { - checkLogin(socket); - try { + checkLogin(socket); + let user = await R.findOne("user", " id = ? AND active = 1 ", [ socket.userID, ]); @@ -488,9 +531,10 @@ exports.entryPage = "dashboard"; }); } } catch (error) { + console.log(error); callback({ ok: false, - msg: "Error while trying to get 2FA status.", + msg: error.message, }); } }); @@ -936,21 +980,13 @@ exports.entryPage = "dashboard"; throw new Error("Password is too weak. It should contain alphabetic and numeric characters. It must be at least 6 characters in length."); } - let user = await R.findOne("user", " id = ? AND active = 1 ", [ - socket.userID, - ]); + let user = await doubleCheckPassword(socket, password.currentPassword); + await user.resetPassword(password.newPassword); - if (user && passwordHash.verify(password.currentPassword, user.password)) { - - user.resetPassword(password.newPassword); - - callback({ - ok: true, - msg: "Password has been updated successfully.", - }); - } else { - throw new Error("Incorrect current password"); - } + callback({ + ok: true, + msg: "Password has been updated successfully.", + }); } catch (e) { callback({ @@ -977,10 +1013,14 @@ exports.entryPage = "dashboard"; } }); - socket.on("setSettings", async (data, callback) => { + socket.on("setSettings", async (data, currentPassword, callback) => { try { checkLogin(socket); + if (data.disableAuth) { + await doubleCheckPassword(socket, currentPassword); + } + await setSettings("general", data); exports.entryPage = data.entryPage; diff --git a/server/util-server.js b/server/util-server.js index 2264ebea9..b2c70d92f 100644 --- a/server/util-server.js +++ b/server/util-server.js @@ -1,9 +1,8 @@ const tcpp = require("tcp-ping"); const Ping = require("./ping-lite"); const { R } = require("redbean-node"); -const { debug } = require("../src/util"); +const { debug, genSecret } = require("../src/util"); const passwordHash = require("./password-hash"); -const dayjs = require("dayjs"); const { Resolver } = require("dns"); const child_process = require("child_process"); const iconv = require("iconv-lite"); @@ -32,7 +31,7 @@ exports.initJWTSecret = async () => { jwtSecretBean.key = "jwtSecret"; } - jwtSecretBean.value = passwordHash.generate(dayjs() + ""); + jwtSecretBean.value = passwordHash.generate(genSecret()); await R.store(jwtSecretBean); return jwtSecretBean; }; @@ -321,6 +320,28 @@ exports.checkLogin = (socket) => { } }; +/** + * For logged-in users, double-check the password + * @param socket + * @param currentPassword + * @returns {Promise} + */ +exports.doubleCheckPassword = async (socket, currentPassword) => { + if (typeof currentPassword !== "string") { + throw new Error("Wrong data type?"); + } + + let user = await R.findOne("user", " id = ? AND active = 1 ", [ + socket.userID, + ]); + + if (!user || !passwordHash.verify(currentPassword, user.password)) { + throw new Error("Incorrect current password"); + } + + return user; +}; + exports.startUnitTest = async () => { console.log("Starting unit test..."); const npm = /^win/.test(process.platform) ? "npm.cmd" : "npm"; diff --git a/src/components/MonitorList.vue b/src/components/MonitorList.vue index e38d1810b..6171c0b3a 100644 --- a/src/components/MonitorList.vue +++ b/src/components/MonitorList.vue @@ -9,7 +9,9 @@ - +
+ +
diff --git a/src/components/TwoFADialog.vue b/src/components/TwoFADialog.vue index b7b9668d8..8a773d6b2 100644 --- a/src/components/TwoFADialog.vue +++ b/src/components/TwoFADialog.vue @@ -19,6 +19,19 @@

{{ uri }}

+
+ + +
+ @@ -59,11 +72,11 @@ diff --git a/src/mixins/socket.js b/src/mixins/socket.js index 7d1bbea51..f2c4ff6d8 100644 --- a/src/mixins/socket.js +++ b/src/mixins/socket.js @@ -42,6 +42,13 @@ export default { statusPageList: [], connectionErrorMsg: "Cannot connect to the socket server. Reconnecting...", showReverseProxyGuide: true, + cloudflared: { + cloudflareTunnelToken: "", + installed: null, + running: false, + message: "", + errorMessage: "", + } }; }, @@ -231,6 +238,12 @@ export default { this.socket.firstConnect = false; }); + // cloudflared + socket.on("cloudflared_installed", (res) => this.cloudflared.installed = res); + socket.on("cloudflared_running", (res) => this.cloudflared.running = res); + socket.on("cloudflared_message", (res) => this.cloudflared.message = res); + socket.on("cloudflared_errorMessage", (res) => this.cloudflared.errorMessage = res); + socket.on("cloudflared_token", (res) => this.cloudflared.cloudflareTunnelToken = res); }, storage() { diff --git a/src/pages/Settings.vue b/src/pages/Settings.vue index 5b54e4247..bd8ade5aa 100644 --- a/src/pages/Settings.vue +++ b/src/pages/Settings.vue @@ -75,6 +75,9 @@ export default { notifications: { title: this.$t("Notifications"), }, + "reverse-proxy": { + title: this.$t("Reverse Proxy"), + }, "monitor-history": { title: this.$t("Monitor History"), }, diff --git a/src/router.js b/src/router.js index f59192d3e..7ab759397 100644 --- a/src/router.js +++ b/src/router.js @@ -14,6 +14,7 @@ import Entry from "./pages/Entry.vue"; import Appearance from "./components/settings/Appearance.vue"; import General from "./components/settings/General.vue"; import Notifications from "./components/settings/Notifications.vue"; +import ReverseProxy from "./components/settings/ReverseProxy.vue"; import MonitorHistory from "./components/settings/MonitorHistory.vue"; import Security from "./components/settings/Security.vue"; import Backup from "./components/settings/Backup.vue"; @@ -83,6 +84,10 @@ const routes = [ path: "notifications", component: Notifications, }, + { + path: "reverse-proxy", + component: ReverseProxy, + }, { path: "monitor-history", component: MonitorHistory, From 82ea896bbc340eec09adc80872762607bc38d276 Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Wed, 30 Mar 2022 11:59:49 +0800 Subject: [PATCH 29/84] Improve the workflow of cloudflared --- package.json | 2 +- server/server.js | 5 +- .../cloudflared-socket-handler.js | 33 ++++++++++--- src/components/settings/ReverseProxy.vue | 48 +++++++++++++++---- src/mixins/socket.js | 1 + 5 files changed, 70 insertions(+), 19 deletions(-) diff --git a/package.json b/package.json index bb7b1e570..641bcace9 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,7 @@ "jsonwebtoken": "~8.5.1", "jwt-decode": "^3.1.2", "limiter": "^2.1.0", - "node-cloudflared-tunnel": "~1.0.6", + "node-cloudflared-tunnel": "~1.0.7", "nodemailer": "~6.6.5", "notp": "~2.0.3", "password-hash": "~1.2.2", diff --git a/server/server.js b/server/server.js index f3203545b..0734f5277 100644 --- a/server/server.js +++ b/server/server.js @@ -133,7 +133,7 @@ const { statusPageSocketHandler } = require("./socket-handlers/status-page-socke const databaseSocketHandler = require("./socket-handlers/database-socket-handler"); const TwoFA = require("./2fa"); const StatusPage = require("./model/status_page"); -const { cloudflaredSocketHandler } = require("./socket-handlers/cloudflared-socket-handler"); +const { cloudflaredSocketHandler, autoStart: cloudflaredAutoStart } = require("./socket-handlers/cloudflared-socket-handler"); app.use(express.json()); @@ -1406,6 +1406,9 @@ exports.entryPage = "dashboard"; initBackgroundJobs(args); + // Start cloudflared at the end if configured + await cloudflaredAutoStart(); + })(); async function updateMonitorNotification(monitorID, notificationIDList) { diff --git a/server/socket-handlers/cloudflared-socket-handler.js b/server/socket-handlers/cloudflared-socket-handler.js index f7c69ed2a..128c47884 100644 --- a/server/socket-handlers/cloudflared-socket-handler.js +++ b/server/socket-handlers/cloudflared-socket-handler.js @@ -1,17 +1,13 @@ -const { checkLogin, setSetting, setting } = require("../util-server"); +const { checkLogin, setSetting, setting, doubleCheckPassword } = require("../util-server"); const { CloudflaredTunnel } = require("node-cloudflared-tunnel"); const { io } = require("../server"); const prefix = "cloudflared_"; const cloudflared = new CloudflaredTunnel(); -let isRunning; - cloudflared.change = (running, message) => { io.to("cloudflared").emit(prefix + "running", running); io.to("cloudflared").emit(prefix + "message", message); - isRunning = running; - }; cloudflared.error = (errorMessage) => { @@ -25,7 +21,7 @@ module.exports.cloudflaredSocketHandler = (socket) => { checkLogin(socket); socket.join("cloudflared"); io.to(socket.userID).emit(prefix + "installed", cloudflared.checkInstalled()); - io.to(socket.userID).emit(prefix + "running", isRunning); + io.to(socket.userID).emit(prefix + "running", cloudflared.running); io.to(socket.userID).emit(prefix + "token", await setting("cloudflaredTunnelToken")); } catch (error) { } }); @@ -62,11 +58,34 @@ module.exports.cloudflaredSocketHandler = (socket) => { } catch (error) { } }); - socket.on(prefix + "stop", async () => { + socket.on(prefix + "stop", async (currentPassword, callback) => { try { checkLogin(socket); + await doubleCheckPassword(socket, currentPassword); cloudflared.stop(); + } catch (error) { + callback({ + ok: false, + msg: error.message, + }); + } + }); + + socket.on(prefix + "removeToken", async () => { + try { + checkLogin(socket); + await setSetting("cloudflaredTunnelToken", ""); } catch (error) { } }); }; + +module.exports.autoStart = async () => { + let token = await setting("cloudflaredTunnelToken"); + + if (token) { + console.log("Start cloudflared"); + cloudflared.token = token; + cloudflared.start(); + } +}; diff --git a/src/components/settings/ReverseProxy.vue b/src/components/settings/ReverseProxy.vue index fe41644b6..2b5f65d82 100644 --- a/src/components/settings/ReverseProxy.vue +++ b/src/components/settings/ReverseProxy.vue @@ -19,7 +19,7 @@ {{ message }} -
+
Message:
@@ -37,8 +37,13 @@ id="cloudflareTunnelToken" v-model="cloudflareTunnelToken" autocomplete="one-time-code" + :readonly="running" />
+
+ {{ $t("Remove Token") }} +
+ Don't know how to get the token? Please read the guide:
https://github.com/louislam/uptime-kuma/wiki/Reverse-Proxy-with-Cloudflare-Tunnel @@ -46,15 +51,31 @@
-
- + + + The current connection may be lost if you are connecting Cloudflare Tunnel. Are you sure want to stop it? Type your password to confirm it. + +
+ + +
+
@@ -68,14 +89,17 @@ diff --git a/src/mixins/socket.js b/src/mixins/socket.js index f2c4ff6d8..d8b1ad224 100644 --- a/src/mixins/socket.js +++ b/src/mixins/socket.js @@ -48,6 +48,7 @@ export default { running: false, message: "", errorMessage: "", + currentPassword: "", } }; }, From 73b338bba6e4d5861f4a38baa31c72398c17f47a Mon Sep 17 00:00:00 2001 From: Janik Schumacher Date: Wed, 30 Mar 2022 12:09:38 +0200 Subject: [PATCH 30/84] feat: Favicon is updated based on satus page logo --- src/pages/StatusPage.vue | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/pages/StatusPage.vue b/src/pages/StatusPage.vue index 2695b10d2..1fe170693 100644 --- a/src/pages/StatusPage.vue +++ b/src/pages/StatusPage.vue @@ -67,7 +67,7 @@

- + @@ -592,6 +592,10 @@ export default { } }, + statusPageLogoLoaded(eventPayload) { + favicon.image(eventPayload.target); + }, + createIncident() { this.enableEditIncidentMode = true; From 71be030733bcbc582a1305ec8700cdc31571fb64 Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Wed, 30 Mar 2022 18:52:10 +0800 Subject: [PATCH 31/84] Add package-lock.json and minor words --- package-lock.json | 21 +++++++++++++++++++-- src/components/settings/ReverseProxy.vue | 2 +- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 1d30ce076..da15c8c37 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "uptime-kuma", - "version": "1.12.1", + "version": "1.13.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "uptime-kuma", - "version": "1.12.1", + "version": "1.13.1", "license": "MIT", "dependencies": { "@fortawesome/fontawesome-svg-core": "~1.2.36", @@ -36,6 +36,7 @@ "jsonwebtoken": "~8.5.1", "jwt-decode": "^3.1.2", "limiter": "^2.1.0", + "node-cloudflared-tunnel": "~1.0.7", "nodemailer": "~6.6.5", "notp": "~2.0.3", "password-hash": "~1.2.2", @@ -11160,6 +11161,14 @@ "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==" }, + "node_modules/node-cloudflared-tunnel": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/node-cloudflared-tunnel/-/node-cloudflared-tunnel-1.0.7.tgz", + "integrity": "sha512-2xKygxFNZZPktF73dvJTNPjFkK4ThOPMpsZf885Iqq5Eie/vxk5mFH8a8dLlDWZYYpGDc699qToJTOlrTqd3Eg==", + "dependencies": { + "command-exists": "^1.2.9" + } + }, "node_modules/node-fetch": { "version": "2.6.7", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", @@ -24071,6 +24080,14 @@ "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==" }, + "node-cloudflared-tunnel": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/node-cloudflared-tunnel/-/node-cloudflared-tunnel-1.0.7.tgz", + "integrity": "sha512-2xKygxFNZZPktF73dvJTNPjFkK4ThOPMpsZf885Iqq5Eie/vxk5mFH8a8dLlDWZYYpGDc699qToJTOlrTqd3Eg==", + "requires": { + "command-exists": "^1.2.9" + } + }, "node-fetch": { "version": "2.6.7", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", diff --git a/src/components/settings/ReverseProxy.vue b/src/components/settings/ReverseProxy.vue index 2b5f65d82..d35d53535 100644 --- a/src/components/settings/ReverseProxy.vue +++ b/src/components/settings/ReverseProxy.vue @@ -61,7 +61,7 @@ - The current connection may be lost if you are connecting Cloudflare Tunnel. Are you sure want to stop it? Type your password to confirm it. + 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.