From b67b4d5afd2e7c3eb866fec0dad030fcc6719832 Mon Sep 17 00:00:00 2001 From: Denis Freund Date: Mon, 27 Sep 2021 11:17:57 +0200 Subject: [PATCH 001/121] add steam gameserver for monitoring --- db/patch-add-apikey-monitor.sql | 7 ++++++ server/database.js | 1 + server/model/monitor.js | 41 +++++++++++++++++++++++++++++++++ server/server.js | 1 + src/languages/en.js | 1 + src/pages/EditMonitor.vue | 24 ++++++++++++++++--- 6 files changed, 72 insertions(+), 3 deletions(-) create mode 100644 db/patch-add-apikey-monitor.sql diff --git a/db/patch-add-apikey-monitor.sql b/db/patch-add-apikey-monitor.sql new file mode 100644 index 00000000..1a30bdf3 --- /dev/null +++ b/db/patch-add-apikey-monitor.sql @@ -0,0 +1,7 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + +ALTER TABLE monitor + ADD apikey VARCHAR(64) default '' not null; + +COMMIT; diff --git a/server/database.js b/server/database.js index 2f6c1c5f..c0b6bc1d 100644 --- a/server/database.js +++ b/server/database.js @@ -46,6 +46,7 @@ class Database { "patch-improve-performance.sql": true, "patch-2fa.sql": true, "patch-add-retry-interval-monitor.sql": true, + "patch-add-apikey-monitor.sql": true, "patch-incident-table.sql": true, "patch-group-table.sql": true, } diff --git a/server/model/monitor.js b/server/model/monitor.js index 9a80225e..e484ccec 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -59,6 +59,7 @@ class Monitor extends BeanModel { weight: this.weight, active: this.active, type: this.type, + apikey: this.apikey, interval: this.interval, retryInterval: this.retryInterval, keyword: this.keyword, @@ -236,6 +237,46 @@ class Monitor extends BeanModel { bean.msg = dnsMessage; bean.status = UP; + } else if (this.type === "steam") { + const steamApiUrl = "https://api.steampowered.com/IGameServersService/GetServerList/v1/"; + const filter = `addr\\${this.hostname}:${this.port}`; + + let res = await axios.get(steamApiUrl, { + timeout: this.interval * 1000 * 0.8, + headers: { + "Accept": "*/*", + "User-Agent": "Uptime-Kuma/" + version, + }, + 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()); + }, + params: { + filter: filter, + key: this.apikey, + } + }); + + bean.msg = `${res.status} - ${res.statusText}`; + bean.ping = await ping(this.hostname); + + let data = res.data; + + // Convert to string for object/array + if (typeof data !== "string") { + data = JSON.stringify(data); + } + + if (data.includes(`${this.hostname}:${this.port}`)) { + bean.msg += ", server is found"; + bean.status = UP; + } else { + throw new Error(bean.msg + ", but server is not found"); + } } if (this.isUpsideDown()) { diff --git a/server/server.js b/server/server.js index f5a8b16e..fb8db1f7 100644 --- a/server/server.js +++ b/server/server.js @@ -504,6 +504,7 @@ exports.entryPage = "dashboard"; bean.hostname = monitor.hostname; bean.maxretries = monitor.maxretries; bean.port = monitor.port; + bean.apikey = monitor.apikey; bean.keyword = monitor.keyword; bean.ignoreTls = monitor.ignoreTls; bean.upsideDown = monitor.upsideDown; diff --git a/src/languages/en.js b/src/languages/en.js index 75d8f30c..6b5dadf3 100644 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -178,4 +178,5 @@ export default { "Add a monitor": "Add a monitor", "Edit Status Page": "Edit Status Page", "Go to Dashboard": "Go to Dashboard", + steamApiKeyDescription: "For monitoring a Steam Gameserver you need a steam Web-API key. You can register your api key here: https://steamcommunity.com/dev", }; diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue index 84231b1a..0a68a34a 100644 --- a/src/pages/EditMonitor.vue +++ b/src/pages/EditMonitor.vue @@ -26,6 +26,9 @@ + @@ -48,17 +51,23 @@ -
+
-
+
+ +
+ + +
+ + +
+ + +
+ {{ $t("steamApiKeyDescription") }} +
+
+
@@ -328,7 +346,7 @@ export default { } } } else if (this.isEdit) { - this.$root.getSocket().emit("getMonitor", this.$route.params.id, (res) => { + this.$root.getSocket().emit("getMonitor", this.$route.params.id, (res) => { if (res.ok) { this.monitor = res.monitor; From efbadd0737d3664c4023ff35708cac54ea4786e8 Mon Sep 17 00:00:00 2001 From: Denis Freund Date: Tue, 28 Sep 2021 13:38:46 +0200 Subject: [PATCH 002/121] only allow ip address for hostname when monitor type is steam --- src/pages/EditMonitor.vue | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue index 0a68a34a..e5c79326 100644 --- a/src/pages/EditMonitor.vue +++ b/src/pages/EditMonitor.vue @@ -50,12 +50,18 @@
- -
+ +
+ +
+ + +
+
From 3f0b85e5a88d65e88b7df138ae237e7cd90dbd41 Mon Sep 17 00:00:00 2001 From: Bert Verhelst Date: Sat, 2 Oct 2021 16:48:27 +0200 Subject: [PATCH 003/121] feat(http-requests): add support for methods, body and headers for http --- ...h-http-monitor-method-body-and-headers.sql | 13 +++ server/database.js | 1 + server/model/monitor.js | 37 +++++++- server/server.js | 6 ++ src/assets/app.scss | 6 +- src/assets/multiselect.scss | 2 +- src/components/HeartbeatBar.vue | 2 +- src/pages/EditMonitor.vue | 84 ++++++++++++++++++- 8 files changed, 144 insertions(+), 7 deletions(-) create mode 100644 db/patch-http-monitor-method-body-and-headers.sql diff --git a/db/patch-http-monitor-method-body-and-headers.sql b/db/patch-http-monitor-method-body-and-headers.sql new file mode 100644 index 00000000..dc2526b4 --- /dev/null +++ b/db/patch-http-monitor-method-body-and-headers.sql @@ -0,0 +1,13 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + +ALTER TABLE monitor + ADD method TEXT default 'GET' not null; + +ALTER TABLE monitor + ADD body TEXT default null; + +ALTER TABLE monitor + ADD headers TEXT default null; + +COMMIT; diff --git a/server/database.js b/server/database.js index 47eca283..297df655 100644 --- a/server/database.js +++ b/server/database.js @@ -49,6 +49,7 @@ class Database { "patch-incident-table.sql": true, "patch-group-table.sql": true, "patch-monitor-push_token.sql": true, + "patch-http-monitor-method-body-and-headers.sql": true, } /** diff --git a/server/model/monitor.js b/server/model/monitor.js index c551fa7d..dc95753d 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -53,6 +53,9 @@ class Monitor extends BeanModel { id: this.id, name: this.name, url: this.url, + method: this.method, + body: this.body, + headers: this.headers, hostname: this.hostname, port: this.port, maxretries: this.maxretries, @@ -95,6 +98,31 @@ class Monitor extends BeanModel { return JSON.parse(this.accepted_statuscodes_json); } + /** + * Convert header string into an object: + * eg: + * + * Authorization: Basic + * Content-Type: application/json + * + * into + * + * { + * "Authorization": "Basic ", + * "Content-Type": "application/json" + * } + **/ + getParsedHeaders() { + if (!this.headers || !this.headers.includes(":")) { + return {}; + } + return Object.fromEntries(this.headers.split("\n").map(header => { + const trimmedHeader = header.trim(); + const firstColonIndex = trimmedHeader.indexOf(":"); + return [trimmedHeader.slice(0, firstColonIndex), trimmedHeader.slice(firstColonIndex + 1) || ""]; + }).filter(arr => !!arr[0] && !!arr[1])); + } + start(io) { let previousBeat = null; let retries = 0; @@ -136,11 +164,15 @@ class Monitor extends BeanModel { // Do not do any queries/high loading things before the "bean.ping" let startTime = dayjs().valueOf(); - let res = await axios.get(this.url, { + const options = { + url: this.url, + method: (this.method || "get").toLowerCase(), + ...(this.body ? { data: JSON.parse(this.body) } : {}), timeout: this.interval * 1000 * 0.8, headers: { "Accept": "*/*", "User-Agent": "Uptime-Kuma/" + version, + ...this.getParsedHeaders(), }, httpsAgent: new https.Agent({ maxCachedSessions: 0, // Use Custom agent to disable session reuse (https://github.com/nodejs/node/issues/3940) @@ -150,7 +182,8 @@ class Monitor extends BeanModel { validateStatus: (status) => { return checkStatusCode(status, this.getAcceptedStatuscodes()); }, - }); + }; + let res = await axios.request(options); bean.msg = `${res.status} - ${res.statusText}`; bean.ping = dayjs().valueOf() - startTime; diff --git a/server/server.js b/server/server.js index 0fbe8325..6d1a939f 100644 --- a/server/server.js +++ b/server/server.js @@ -505,6 +505,9 @@ exports.entryPage = "dashboard"; bean.name = monitor.name; bean.type = monitor.type; bean.url = monitor.url; + bean.method = monitor.method; + bean.body = monitor.body; + bean.headers = monitor.headers; bean.interval = monitor.interval; bean.retryInterval = monitor.retryInterval; bean.hostname = monitor.hostname; @@ -1028,6 +1031,9 @@ exports.entryPage = "dashboard"; name: monitorListData[i].name, type: monitorListData[i].type, url: monitorListData[i].url, + method: monitorListData[i].method || "GET", + body: monitorListData[i].body, + headers: monitorListData[i].headers, interval: monitorListData[i].interval, retryInterval: retryInterval, hostname: monitorListData[i].hostname, diff --git a/src/assets/app.scss b/src/assets/app.scss index 34a4560c..fdb57b43 100644 --- a/src/assets/app.scss +++ b/src/assets/app.scss @@ -14,6 +14,10 @@ h2 { font-size: 26px; } +textarea.form-control { + border-radius: 19px; +} + ::-webkit-scrollbar { width: 10px; } @@ -413,4 +417,4 @@ h2 { // Localization -@import "localization.scss"; \ No newline at end of file +@import "localization.scss"; diff --git a/src/assets/multiselect.scss b/src/assets/multiselect.scss index 30023076..53b47c16 100644 --- a/src/assets/multiselect.scss +++ b/src/assets/multiselect.scss @@ -21,7 +21,7 @@ } .multiselect__tag { - border-radius: 50rem; + border-radius: $border-radius; margin-bottom: 0; padding: 6px 26px 6px 10px; background: $primary !important; diff --git a/src/components/HeartbeatBar.vue b/src/components/HeartbeatBar.vue index 4dc2c712..e62b95df 100644 --- a/src/components/HeartbeatBar.vue +++ b/src/components/HeartbeatBar.vue @@ -186,7 +186,7 @@ export default { .beat { display: inline-block; background-color: $primary; - border-radius: 50rem; + border-radius: $border-radius; &.empty { background-color: aliceblue; diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue index 96f221e8..e63f4986 100644 --- a/src/pages/EditMonitor.vue +++ b/src/pages/EditMonitor.vue @@ -44,6 +44,46 @@
+ +
+ + +
+ + +
+ + +
+ + +
+ + +
+
@@ -285,6 +325,18 @@ export default { pushURL() { return this.$root.baseURL + "/api/push/" + this.monitor.pushToken + "?msg=OK&ping="; + }, + + bodyPlaceholder() { + return `{ + "id": 124357, + "username": "admin", + "password": "myAdminPassword" +}`; + }, + + headersPlaceholder() { + return "Authorization: Bearer abc123\nContent-Type: application/json"; } }, @@ -295,7 +347,7 @@ export default { }, "monitor.interval"(value, oldValue) { - // Link interval and retryInerval if they are the same value. + // Link interval and retryInterval if they are the same value. if (this.monitor.retryInterval === oldValue) { this.monitor.retryInterval = value; } @@ -349,6 +401,7 @@ export default { type: "http", name: "", url: "https://", + method: "GET", interval: 60, retryInterval: this.interval, maxretries: 0, @@ -383,9 +436,32 @@ export default { }, + isInputValid() { + if (this.monitor.body) { + try { + JSON.parse(this.monitor.body); + } catch (err) { + toast.error(this.$t("The request body is not valid json: ") + err.message); + return false; + } + } + if (this.monitor.headers) { + if (!/^([^:]+:.*)([\s]+[^:]+:.*)+$/g.test(this.monitor.headers)) { + toast.error(this.$t("Headers do not have a valid format: \"key: valuekey: value...\"")); + return false; + } + } + return true; + }, + async submit() { this.processing = true; + if (!this.isInputValid()) { + this.processing = false; + return; + } + if (this.isAdd) { this.$root.add(this.monitor, async (res) => { @@ -422,8 +498,12 @@ export default { }; - From 7ee89fab5c19dc9acc11a041b3abd03fe934d03b Mon Sep 17 00:00:00 2001 From: Bert Verhelst Date: Mon, 4 Oct 2021 11:29:43 +0200 Subject: [PATCH 004/121] fix(edit-monitor): Make json capitalised Co-authored-by: Adam Stachowicz --- 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 e63f4986..30cf1da0 100644 --- a/src/pages/EditMonitor.vue +++ b/src/pages/EditMonitor.vue @@ -441,7 +441,7 @@ export default { try { JSON.parse(this.monitor.body); } catch (err) { - toast.error(this.$t("The request body is not valid json: ") + err.message); + toast.error(this.$t("The request body is not valid JSON: ") + err.message); return false; } } From afeb424dc05b45586967edb6ea9f2ec429b6a2ad Mon Sep 17 00:00:00 2001 From: Bert Verhelst Date: Tue, 5 Oct 2021 09:20:24 +0200 Subject: [PATCH 005/121] fix(edit-monitor): add translations to en.js --- src/languages/en.js | 8 ++++++++ src/pages/EditMonitor.vue | 14 +++++--------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/languages/en.js b/src/languages/en.js index 46298b01..db3928da 100644 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -195,4 +195,12 @@ export default { "pushbullet": "Pushbullet", "line": "Line Messenger", "mattermost": "Mattermost", + Method: "Method", + Body: "Body", + Headers: "Headers", + PushUrl: "Push URL", + HeadersInvalidFormat: "Headers do not have a valid format: \"key: value key: value ...\"", + BodyInvalidFormat: "The request body is not valid JSON: ", + BodyPlaceholder: "{\n\t\"id\": 124357,\n\t\"username\": \"admin\",\n\t\"password\": \"myAdminPassword\"\n}", + HeadersPlaceholder: "Authorization: Bearer abc123\nContent-Type: application/json", }; diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue index 30cf1da0..df3b211f 100644 --- a/src/pages/EditMonitor.vue +++ b/src/pages/EditMonitor.vue @@ -86,7 +86,7 @@
- +
You should call this url every {{ monitor.interval }} seconds.
@@ -328,15 +328,11 @@ export default { }, bodyPlaceholder() { - return `{ - "id": 124357, - "username": "admin", - "password": "myAdminPassword" -}`; + return this.$t("BodyPlaceholder"); }, headersPlaceholder() { - return "Authorization: Bearer abc123\nContent-Type: application/json"; + return this.$t("HeadersPlaceholder"); } }, @@ -441,13 +437,13 @@ export default { try { JSON.parse(this.monitor.body); } catch (err) { - toast.error(this.$t("The request body is not valid JSON: ") + err.message); + toast.error(this.$t("BodyInvalidFormat") + err.message); return false; } } if (this.monitor.headers) { if (!/^([^:]+:.*)([\s]+[^:]+:.*)+$/g.test(this.monitor.headers)) { - toast.error(this.$t("Headers do not have a valid format: \"key: valuekey: value...\"")); + toast.error(this.$t("HeadersInvalidFormat")); return false; } } From a0ffa42b42ea2ecd4408c0fa94aaddc50994f31b Mon Sep 17 00:00:00 2001 From: Bert Verhelst Date: Tue, 5 Oct 2021 18:21:31 +0200 Subject: [PATCH 006/121] fix(translations): add translations for method body and headers to dutch --- src/languages/nl-NL.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/languages/nl-NL.js b/src/languages/nl-NL.js index 5fa9d4e1..af08bfc7 100644 --- a/src/languages/nl-NL.js +++ b/src/languages/nl-NL.js @@ -179,4 +179,12 @@ export default { "Add a monitor": "Add a monitor", "Edit Status Page": "Edit Status Page", "Go to Dashboard": "Go to Dashboard", + Method: "Methode", + Body: "Body", + Headers: "Headers", + PushUrl: "Push URL", + HeadersInvalidFormat: "Headers hebben een incorrect formaat: \"key: waarde key: waarde ...\"", + BodyInvalidFormat: "De request body is geen geldige JSON: ", + BodyPlaceholder: "{\n\t\"id\": 124357,\n\t\"gebruikersnaam\": \"admin\",\n\t\"wachtwoord\": \"mijnAdminWachtwoord\"\n}", + HeadersPlaceholder: "Authorization: Bearer abc123\nContent-Type: application/json", }; From c3c273f9df86a7f1c17dfa827afb455c9d61381b Mon Sep 17 00:00:00 2001 From: Bert Verhelst Date: Sat, 9 Oct 2021 11:20:33 +0200 Subject: [PATCH 007/121] fix(edit-monitor): fix regex to allow a single header --- 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 96448c6d..852a266f 100644 --- a/src/pages/EditMonitor.vue +++ b/src/pages/EditMonitor.vue @@ -442,7 +442,7 @@ export default { } } if (this.monitor.headers) { - if (!/^([^:]+:.*)([\s]+[^:]+:.*)+$/g.test(this.monitor.headers)) { + if (!/^([^:]+:.*)([\s]+[^:]+:.*)*$/g.test(this.monitor.headers)) { toast.error(this.$t("HeadersInvalidFormat")); return false; } From b8093e909b764ebc48573a2e57ed39eb6e3127f3 Mon Sep 17 00:00:00 2001 From: Bert Verhelst Date: Sat, 9 Oct 2021 11:38:12 +0200 Subject: [PATCH 008/121] fix(edit-monitor): fix minification of translations containing { } --- src/languages/en.js | 5 ++--- src/languages/nl-NL.js | 2 +- src/pages/EditMonitor.vue | 10 ++++++++-- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/languages/en.js b/src/languages/en.js index 8b1c28d3..7871f0fe 100644 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -277,17 +277,16 @@ export default { promosmsTypeEco: "SMS ECO - cheap but slow and often overloaded. Limited only to Polish recipients.", promosmsTypeFlash: "SMS FLASH - Message will automatically show on recipient device. Limited only to Polish recipients.", promosmsTypeFull: "SMS FULL - Premium tier of SMS, You can use Your Sender Name (You need to register name first). Reliable for alerts.", - promosmsTypeFull: "SMS SPEED - Highest priority in system. Very quick and reliable but costly (about twice of SMS FULL price).", + promosmsTypeSpeed: "SMS SPEED - Highest priority in system. Very quick and reliable but costly (about twice of SMS FULL price).", promosmsPhoneNumber: "Phone number (for Polish recipient You can skip area codes)", promosmsSMSSender: "SMS Sender Name : Pre-registred name or one of defaults: InfoSMS, SMS Info, MaxSMS, INFO, SMS", // End notification form - "Status Page": "Status Page", Method: "Method", Body: "Body", Headers: "Headers", PushUrl: "Push URL", HeadersInvalidFormat: "Headers do not have a valid format: \"key: value key: value ...\"", BodyInvalidFormat: "The request body is not valid JSON: ", - BodyPlaceholder: "{\n\t\"id\": 124357,\n\t\"username\": \"admin\",\n\t\"password\": \"myAdminPassword\"\n}", + BodyPlaceholder: "{\n\t\"id\": 124357,\n\t\"username\": \"admin\",\n\t\"password\": \"myAdminPassword\"\n}", HeadersPlaceholder: "Authorization: Bearer abc123\nContent-Type: application/json", }; diff --git a/src/languages/nl-NL.js b/src/languages/nl-NL.js index a9e8f6a3..33d84cd5 100644 --- a/src/languages/nl-NL.js +++ b/src/languages/nl-NL.js @@ -204,6 +204,6 @@ export default { PushUrl: "Push URL", HeadersInvalidFormat: "Headers hebben een incorrect formaat: \"key: waarde key: waarde ...\"", BodyInvalidFormat: "De request body is geen geldige JSON: ", - BodyPlaceholder: "{\n\t\"id\": 124357,\n\t\"gebruikersnaam\": \"admin\",\n\t\"wachtwoord\": \"mijnAdminWachtwoord\"\n}", + BodyPlaceholder: "{\n\t\"id\": 124357,\n\t\"gebruikersnaam\": \"admin\",\n\t\"wachtwoord\": \"mijnAdminWachtwoord\"\n}", HeadersPlaceholder: "Authorization: Bearer abc123\nContent-Type: application/json", }; diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue index 852a266f..5074dab9 100644 --- a/src/pages/EditMonitor.vue +++ b/src/pages/EditMonitor.vue @@ -328,11 +328,11 @@ export default { }, bodyPlaceholder() { - return this.$t("BodyPlaceholder"); + return this.decodeHtml(this.$t("BodyPlaceholder")); }, headersPlaceholder() { - return this.$t("HeadersPlaceholder"); + return this.decodeHtml(this.$t("HeadersPlaceholder")); } }, @@ -490,6 +490,12 @@ export default { addedNotification(id) { this.monitor.notificationIDList[id] = true; }, + + decodeHtml(html) { + const txt = document.createElement("textarea"); + txt.innerHTML = html; + return txt.value; + } }, }; From d71d27220be2bc9832f8a5860172bce32ca9191c Mon Sep 17 00:00:00 2001 From: Bert Verhelst Date: Sat, 9 Oct 2021 12:42:32 +0200 Subject: [PATCH 009/121] fix(edit-monitor): store headers as JSON --- server/model/monitor.js | 27 +-------------------------- src/languages/en.js | 4 ++-- src/languages/nl-NL.js | 4 ++-- src/pages/EditMonitor.vue | 6 ++++-- 4 files changed, 9 insertions(+), 32 deletions(-) diff --git a/server/model/monitor.js b/server/model/monitor.js index 33ad82f0..2738898f 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -99,31 +99,6 @@ class Monitor extends BeanModel { return JSON.parse(this.accepted_statuscodes_json); } - /** - * Convert header string into an object: - * eg: - * - * Authorization: Basic - * Content-Type: application/json - * - * into - * - * { - * "Authorization": "Basic ", - * "Content-Type": "application/json" - * } - **/ - getParsedHeaders() { - if (!this.headers || !this.headers.includes(":")) { - return {}; - } - return Object.fromEntries(this.headers.split("\n").map(header => { - const trimmedHeader = header.trim(); - const firstColonIndex = trimmedHeader.indexOf(":"); - return [trimmedHeader.slice(0, firstColonIndex), trimmedHeader.slice(firstColonIndex + 1) || ""]; - }).filter(arr => !!arr[0] && !!arr[1])); - } - start(io) { let previousBeat = null; let retries = 0; @@ -173,7 +148,7 @@ class Monitor extends BeanModel { headers: { "Accept": "*/*", "User-Agent": "Uptime-Kuma/" + version, - ...this.getParsedHeaders(), + ...JSON.parse(this.headers), }, httpsAgent: new https.Agent({ maxCachedSessions: 0, // Use Custom agent to disable session reuse (https://github.com/nodejs/node/issues/3940) diff --git a/src/languages/en.js b/src/languages/en.js index 7871f0fe..a5ad24e0 100644 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -285,8 +285,8 @@ export default { Body: "Body", Headers: "Headers", PushUrl: "Push URL", - HeadersInvalidFormat: "Headers do not have a valid format: \"key: value key: value ...\"", + HeadersInvalidFormat: "The request headers are not valid JSON: ", BodyInvalidFormat: "The request body is not valid JSON: ", BodyPlaceholder: "{\n\t\"id\": 124357,\n\t\"username\": \"admin\",\n\t\"password\": \"myAdminPassword\"\n}", - HeadersPlaceholder: "Authorization: Bearer abc123\nContent-Type: application/json", + HeadersPlaceholder: "{\n\t\"Authorization\": \"Bearer abc123\",\n\t\"Content-Type\": \"application/json\"\n}", }; diff --git a/src/languages/nl-NL.js b/src/languages/nl-NL.js index 33d84cd5..170b4a3c 100644 --- a/src/languages/nl-NL.js +++ b/src/languages/nl-NL.js @@ -202,8 +202,8 @@ export default { Body: "Body", Headers: "Headers", PushUrl: "Push URL", - HeadersInvalidFormat: "Headers hebben een incorrect formaat: \"key: waarde key: waarde ...\"", + HeadersInvalidFormat: "The request headers is geen geldige JSON: ", BodyInvalidFormat: "De request body is geen geldige JSON: ", BodyPlaceholder: "{\n\t\"id\": 124357,\n\t\"gebruikersnaam\": \"admin\",\n\t\"wachtwoord\": \"mijnAdminWachtwoord\"\n}", - HeadersPlaceholder: "Authorization: Bearer abc123\nContent-Type: application/json", + HeadersPlaceholder: "{\n\t\"Authorization\": \"Bearer abc123\",\n\t\"Content-Type\": \"application/json\"\n}", }; diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue index 5074dab9..ed8b359e 100644 --- a/src/pages/EditMonitor.vue +++ b/src/pages/EditMonitor.vue @@ -442,8 +442,10 @@ export default { } } if (this.monitor.headers) { - if (!/^([^:]+:.*)([\s]+[^:]+:.*)*$/g.test(this.monitor.headers)) { - toast.error(this.$t("HeadersInvalidFormat")); + try { + JSON.parse(this.monitor.headers); + } catch (err) { + toast.error(this.$t("HeadersInvalidFormat") + err.message); return false; } } From 656a4d6270812f324cbe6248f42f1902973f5f50 Mon Sep 17 00:00:00 2001 From: Nelson Chan Date: Mon, 27 Sep 2021 23:40:38 +0800 Subject: [PATCH 010/121] WIP: Enable background jobs WIP: Remove better-sqlite3 --- package-lock.json | 518 +++++++++++++++++++++++++++++++++- package.json | 1 + server/jobs.js | 28 ++ server/jobs/clear-old-data.js | 24 ++ server/server.js | 5 + 5 files changed, 570 insertions(+), 6 deletions(-) create mode 100644 server/jobs.js create mode 100644 server/jobs/clear-old-data.js diff --git a/package-lock.json b/package-lock.json index 3820dbf7..59b3c71d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "axios": "~0.21.4", "bcryptjs": "~2.4.3", "bootstrap": "~5.1.1", + "bree": "~6.3.1", "chart.js": "~3.5.1", "chartjs-adapter-dayjs": "~1.0.0", "command-exists": "~1.2.9", @@ -1712,7 +1713,6 @@ "version": "7.15.4", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.15.4.tgz", "integrity": "sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw==", - "dev": true, "dependencies": { "regenerator-runtime": "^0.13.4" }, @@ -1815,6 +1815,14 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "node_modules/@breejs/later": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@breejs/later/-/later-4.0.2.tgz", + "integrity": "sha512-EN0SlbyYouBdtZis1htdsgGlwFePzkXPwdIeqaBaavxkJT1G2/bitc2LSixjv45z2njXslxlJI1mW2O/Gmrb+A==", + "engines": { + "node": ">= 10" + } + }, "node_modules/@eslint/eslintrc": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", @@ -2660,6 +2668,11 @@ "@types/koa": "*" } }, + "node_modules/@types/lodash": { + "version": "4.14.175", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.175.tgz", + "integrity": "sha512-XmdEOrKQ8a1Y/yxQFOMbC47G/V2VDO1GvMRnl4O75M4GW/abC5tnfzadQYkqEveqRM1dEJGFFegfPNA2vvx2iw==" + }, "node_modules/@types/mdast": { "version": "3.0.10", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.10.tgz", @@ -3548,6 +3561,11 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, + "node_modules/boolean": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.1.4.tgz", + "integrity": "sha512-3hx0kwU3uzG6ReQ3pnaFQPSktpBw6RHN3/ivDKEuU8g1XSfafowyvDnadjv1xp8IZqhtSukxlwv9bF6FhX8m0w==" + }, "node_modules/bootstrap": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.1.1.tgz", @@ -3581,6 +3599,29 @@ "node": ">=8" } }, + "node_modules/bree": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/bree/-/bree-6.3.1.tgz", + "integrity": "sha512-FADpEV5c+3ZuFIBothyyRUxZClJD2PetIo0lmqAFJ3ZMI9WsSmQmmstZ86Dy0G4Gyw3nPNdfYTjV7+9pPtlB8g==", + "dependencies": { + "@babel/runtime": "^7.12.5", + "@breejs/later": "^4.0.2", + "boolean": "^3.0.2", + "bthreads": "^0.5.1", + "combine-errors": "^3.0.3", + "cron-validate": "^1.4.1", + "debug": "^4.3.1", + "human-interval": "^2.0.0", + "is-string-and-not-blank": "^0.0.2", + "is-valid-path": "^0.1.1", + "ms": "^2.1.2", + "p-wait-for": "3.1.0", + "safe-timers": "^1.1.0" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/browser-process-hrtime": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", @@ -3619,6 +3660,17 @@ "node-int64": "^0.4.0" } }, + "node_modules/bthreads": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/bthreads/-/bthreads-0.5.1.tgz", + "integrity": "sha512-nK7Jo9ll+r1FRMNPWEFRTZMQrX6HhX8JjPAofxmbTNILHqWVIJPmWzCi9JlX/K0DL5AKZTFZg2Qser5C6gVs9A==", + "dependencies": { + "bufio": "~1.0.5" + }, + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -3680,6 +3732,14 @@ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" }, + "node_modules/bufio": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/bufio/-/bufio-1.0.7.tgz", + "integrity": "sha512-bd1dDQhiC+bEbEfg56IdBv7faWa6OipMs/AFFFvtFnB3wAYjlwQpQRZ0pm6ZkgtfL0pILRXhKxOiQj6UzoMR7A==", + "engines": { + "node": ">=8.0.0" + } + }, "node_modules/bytes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", @@ -3967,6 +4027,15 @@ "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.1.tgz", "integrity": "sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==" }, + "node_modules/combine-errors": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/combine-errors/-/combine-errors-3.0.3.tgz", + "integrity": "sha1-9N9nQAg+VwOjGBEQwrEFUfAD2oY=", + "dependencies": { + "custom-error-instance": "2.1.1", + "lodash.uniqby": "4.5.0" + } + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -4120,6 +4189,14 @@ "node": ">=10" } }, + "node_modules/cron-validate": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cron-validate/-/cron-validate-1.4.3.tgz", + "integrity": "sha512-N+qKw019oQBEPIP5Qwi8Z5XelQ00ThN6Maahwv+9UGu2u/b/MPb35zngMQI0T8pBoNiBrIXGlhvsmspNSYae/w==", + "dependencies": { + "yup": "0.32.9" + } + }, "node_modules/cross-env": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", @@ -4193,6 +4270,11 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.18.tgz", "integrity": "sha512-RSU6Hyeg14am3Ah4VZEmeX8H7kLwEEirXe6aU2IPfKNvhXwTflK5HQRDNI0ypQXoqmm+QPyG2IaPuQE5zMwSIQ==" }, + "node_modules/custom-error-instance": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/custom-error-instance/-/custom-error-instance-2.1.1.tgz", + "integrity": "sha1-PPY5FIemYppiR+sMoM4ACBt+Nho=" + }, "node_modules/cwd": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/cwd/-/cwd-0.10.0.tgz", @@ -6285,6 +6367,14 @@ "node": ">= 6" } }, + "node_modules/human-interval": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/human-interval/-/human-interval-2.0.1.tgz", + "integrity": "sha512-r4Aotzf+OtKIGQCB3odUowy4GfUDTy3aTWTfLd7ZF2gBCy3XW3v/dJLRefZnOFFnjqs5B1TypvS8WarpBkYUNQ==", + "dependencies": { + "numbered": "^1.1.0" + } + }, "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -6569,6 +6659,36 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/is-invalid-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-invalid-path/-/is-invalid-path-0.1.0.tgz", + "integrity": "sha1-MHqFWzzxqTi0TqcNLGEQYFNxTzQ=", + "dependencies": { + "is-glob": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-invalid-path/node_modules/is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-invalid-path/node_modules/is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "dependencies": { + "is-extglob": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -6626,6 +6746,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-string-and-not-blank": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/is-string-and-not-blank/-/is-string-and-not-blank-0.0.2.tgz", + "integrity": "sha512-FyPGAbNVyZpTeDCTXnzuwbu9/WpNXbCfbHXLpCRpN4GANhS00eEIP5Ef+k5HYSNIzIhdN9zRDoBj6unscECvtQ==", + "dependencies": { + "is-string-blank": "^1.0.1" + }, + "engines": { + "node": ">=6.4.0" + } + }, + "node_modules/is-string-blank": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-string-blank/-/is-string-blank-1.0.1.tgz", + "integrity": "sha512-9H+ZBCVs3L9OYqv8nuUAzpcT9OTgMD1yAWrG7ihlnibdkbtB850heAmYWxHuXc4CHy4lKeK69tN+ny1K7gBIrw==" + }, "node_modules/is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -6644,6 +6780,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-valid-path": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-valid-path/-/is-valid-path-0.1.1.tgz", + "integrity": "sha1-EQ+f90w39mPh7HkV60UfLbk6yd8=", + "dependencies": { + "is-invalid-path": "^0.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-windows": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-0.2.0.tgz", @@ -7801,6 +7948,51 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + }, + "node_modules/lodash._baseiteratee": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash._baseiteratee/-/lodash._baseiteratee-4.7.0.tgz", + "integrity": "sha1-NKm1VDVycnw9sueO2uPA6eZr0QI=", + "dependencies": { + "lodash._stringtopath": "~4.8.0" + } + }, + "node_modules/lodash._basetostring": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/lodash._basetostring/-/lodash._basetostring-4.12.0.tgz", + "integrity": "sha1-kyfJ3FFYhmt/pLnUL0Y45XZt2d8=" + }, + "node_modules/lodash._baseuniq": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz", + "integrity": "sha1-DrtE5FaBSveQXGIS+iybLVG4Qeg=", + "dependencies": { + "lodash._createset": "~4.0.0", + "lodash._root": "~3.0.0" + } + }, + "node_modules/lodash._createset": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/lodash._createset/-/lodash._createset-4.0.3.tgz", + "integrity": "sha1-D0ZZ+7CddRlPqeK4imZE02PJ/iY=" + }, + "node_modules/lodash._root": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._root/-/lodash._root-3.0.1.tgz", + "integrity": "sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI=" + }, + "node_modules/lodash._stringtopath": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/lodash._stringtopath/-/lodash._stringtopath-4.8.0.tgz", + "integrity": "sha1-lBvPDmQmbl/B1m/tCmlZVExXaCQ=", + "dependencies": { + "lodash._basetostring": "~4.12.0" + } + }, "node_modules/lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", @@ -7865,6 +8057,15 @@ "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", "dev": true }, + "node_modules/lodash.uniqby": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.5.0.tgz", + "integrity": "sha1-o6F7v2LutiQPSRhG6XwcTipeHiE=", + "dependencies": { + "lodash._baseiteratee": "~4.7.0", + "lodash._baseuniq": "~4.6.0" + } + }, "node_modules/log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -8275,6 +8476,11 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "node_modules/nanoclone": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/nanoclone/-/nanoclone-0.2.1.tgz", + "integrity": "sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA==" + }, "node_modules/nanoid": { "version": "3.1.28", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.28.tgz", @@ -8516,6 +8722,11 @@ "node": ">=0.10.0" } }, + "node_modules/numbered": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/numbered/-/numbered-1.1.0.tgz", + "integrity": "sha512-pv/ue2Odr7IfYOO0byC1KgBI10wo5YDauLhxY6/saNzAdAs0r1SotGCPzzCLNPL0xtrAwWRialLu23AAu9xO1g==" + }, "node_modules/nwsapi": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", @@ -8626,6 +8837,14 @@ "node": ">=0.10.0" } }, + "node_modules/p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "engines": { + "node": ">=4" + } + }, "node_modules/p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", @@ -8652,6 +8871,17 @@ "node": ">=8" } }, + "node_modules/p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "dependencies": { + "p-finally": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", @@ -8660,6 +8890,17 @@ "node": ">=6" } }, + "node_modules/p-wait-for": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-wait-for/-/p-wait-for-3.1.0.tgz", + "integrity": "sha512-0Uy19uhxbssHelu9ynDMcON6BmMk6pH8551CvxROhiz3Vx+yC4RqxjyIDk2V4ll0g9177RKT++PK4zcV58uJ7A==", + "dependencies": { + "p-timeout": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -9152,6 +9393,11 @@ "node": ">= 6" } }, + "node_modules/property-expr": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.4.tgz", + "integrity": "sha512-sFPkHQjVKheDNnPvotjQmm3KD3uk1fWKUN7CrpdbwmUx3CrG3QiM8QpTSimvig5vTXmTvjz7+TDvXOI9+4rkcg==" + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -9696,8 +9942,7 @@ "node_modules/regenerator-runtime": { "version": "0.13.9", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", - "dev": true + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" }, "node_modules/regenerator-transform": { "version": "0.14.5", @@ -10116,6 +10361,11 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, + "node_modules/safe-timers": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-timers/-/safe-timers-1.1.0.tgz", + "integrity": "sha1-xYroMl2407BnMi8KTvOgytZ6rYM=" + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -11179,6 +11429,11 @@ "node": ">=0.6" } }, + "node_modules/toposort": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", + "integrity": "sha1-riF2gXXRVZ1IvvNUILL0li8JwzA=" + }, "node_modules/tough-cookie": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", @@ -12224,6 +12479,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/yup": { + "version": "0.32.9", + "resolved": "https://registry.npmjs.org/yup/-/yup-0.32.9.tgz", + "integrity": "sha512-Ci1qN+i2H0XpY7syDQ0k5zKQ/DoxO0LzPg8PAR/X4Mpj6DqaeCoIYEEjDJwhArh3Fa7GWbQQVDZKeXYlSH4JMg==", + "dependencies": { + "@babel/runtime": "^7.10.5", + "@types/lodash": "^4.14.165", + "lodash": "^4.17.20", + "lodash-es": "^4.17.15", + "nanoclone": "^0.2.1", + "property-expr": "^2.0.4", + "toposort": "^2.0.2" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/zwitch": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-1.0.5.tgz", @@ -13378,7 +13650,6 @@ "version": "7.15.4", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.15.4.tgz", "integrity": "sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw==", - "dev": true, "requires": { "regenerator-runtime": "^0.13.4" } @@ -13461,6 +13732,11 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, + "@breejs/later": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@breejs/later/-/later-4.0.2.tgz", + "integrity": "sha512-EN0SlbyYouBdtZis1htdsgGlwFePzkXPwdIeqaBaavxkJT1G2/bitc2LSixjv45z2njXslxlJI1mW2O/Gmrb+A==" + }, "@eslint/eslintrc": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", @@ -14162,6 +14438,11 @@ "@types/koa": "*" } }, + "@types/lodash": { + "version": "4.14.175", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.175.tgz", + "integrity": "sha512-XmdEOrKQ8a1Y/yxQFOMbC47G/V2VDO1GvMRnl4O75M4GW/abC5tnfzadQYkqEveqRM1dEJGFFegfPNA2vvx2iw==" + }, "@types/mdast": { "version": "3.0.10", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.10.tgz", @@ -14898,6 +15179,11 @@ } } }, + "boolean": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.1.4.tgz", + "integrity": "sha512-3hx0kwU3uzG6ReQ3pnaFQPSktpBw6RHN3/ivDKEuU8g1XSfafowyvDnadjv1xp8IZqhtSukxlwv9bF6FhX8m0w==" + }, "bootstrap": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.1.1.tgz", @@ -14922,6 +15208,26 @@ "fill-range": "^7.0.1" } }, + "bree": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/bree/-/bree-6.3.1.tgz", + "integrity": "sha512-FADpEV5c+3ZuFIBothyyRUxZClJD2PetIo0lmqAFJ3ZMI9WsSmQmmstZ86Dy0G4Gyw3nPNdfYTjV7+9pPtlB8g==", + "requires": { + "@babel/runtime": "^7.12.5", + "@breejs/later": "^4.0.2", + "boolean": "^3.0.2", + "bthreads": "^0.5.1", + "combine-errors": "^3.0.3", + "cron-validate": "^1.4.1", + "debug": "^4.3.1", + "human-interval": "^2.0.0", + "is-string-and-not-blank": "^0.0.2", + "is-valid-path": "^0.1.1", + "ms": "^2.1.2", + "p-wait-for": "3.1.0", + "safe-timers": "^1.1.0" + } + }, "browser-process-hrtime": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", @@ -14950,6 +15256,14 @@ "node-int64": "^0.4.0" } }, + "bthreads": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/bthreads/-/bthreads-0.5.1.tgz", + "integrity": "sha512-nK7Jo9ll+r1FRMNPWEFRTZMQrX6HhX8JjPAofxmbTNILHqWVIJPmWzCi9JlX/K0DL5AKZTFZg2Qser5C6gVs9A==", + "requires": { + "bufio": "~1.0.5" + } + }, "buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -14994,6 +15308,11 @@ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" }, + "bufio": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/bufio/-/bufio-1.0.7.tgz", + "integrity": "sha512-bd1dDQhiC+bEbEfg56IdBv7faWa6OipMs/AFFFvtFnB3wAYjlwQpQRZ0pm6ZkgtfL0pILRXhKxOiQj6UzoMR7A==" + }, "bytes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", @@ -15207,6 +15526,15 @@ "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.2.1.tgz", "integrity": "sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw==" }, + "combine-errors": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/combine-errors/-/combine-errors-3.0.3.tgz", + "integrity": "sha1-9N9nQAg+VwOjGBEQwrEFUfAD2oY=", + "requires": { + "custom-error-instance": "2.1.1", + "lodash.uniqby": "4.5.0" + } + }, "combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -15329,6 +15657,14 @@ "yaml": "^1.10.0" } }, + "cron-validate": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cron-validate/-/cron-validate-1.4.3.tgz", + "integrity": "sha512-N+qKw019oQBEPIP5Qwi8Z5XelQ00ThN6Maahwv+9UGu2u/b/MPb35zngMQI0T8pBoNiBrIXGlhvsmspNSYae/w==", + "requires": { + "yup": "0.32.9" + } + }, "cross-env": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", @@ -15383,6 +15719,11 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.18.tgz", "integrity": "sha512-RSU6Hyeg14am3Ah4VZEmeX8H7kLwEEirXe6aU2IPfKNvhXwTflK5HQRDNI0ypQXoqmm+QPyG2IaPuQE5zMwSIQ==" }, + "custom-error-instance": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/custom-error-instance/-/custom-error-instance-2.1.1.tgz", + "integrity": "sha1-PPY5FIemYppiR+sMoM4ACBt+Nho=" + }, "cwd": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/cwd/-/cwd-0.10.0.tgz", @@ -16946,6 +17287,14 @@ "debug": "4" } }, + "human-interval": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/human-interval/-/human-interval-2.0.1.tgz", + "integrity": "sha512-r4Aotzf+OtKIGQCB3odUowy4GfUDTy3aTWTfLd7ZF2gBCy3XW3v/dJLRefZnOFFnjqs5B1TypvS8WarpBkYUNQ==", + "requires": { + "numbered": "^1.1.0" + } + }, "human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -17140,6 +17489,29 @@ "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==", "dev": true }, + "is-invalid-path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-invalid-path/-/is-invalid-path-0.1.0.tgz", + "integrity": "sha1-MHqFWzzxqTi0TqcNLGEQYFNxTzQ=", + "requires": { + "is-glob": "^2.0.0" + }, + "dependencies": { + "is-extglob": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", + "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=" + }, + "is-glob": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", + "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", + "requires": { + "is-extglob": "^1.0.0" + } + } + } + }, "is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -17179,6 +17551,19 @@ "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true }, + "is-string-and-not-blank": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/is-string-and-not-blank/-/is-string-and-not-blank-0.0.2.tgz", + "integrity": "sha512-FyPGAbNVyZpTeDCTXnzuwbu9/WpNXbCfbHXLpCRpN4GANhS00eEIP5Ef+k5HYSNIzIhdN9zRDoBj6unscECvtQ==", + "requires": { + "is-string-blank": "^1.0.1" + } + }, + "is-string-blank": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-string-blank/-/is-string-blank-1.0.1.tgz", + "integrity": "sha512-9H+ZBCVs3L9OYqv8nuUAzpcT9OTgMD1yAWrG7ihlnibdkbtB850heAmYWxHuXc4CHy4lKeK69tN+ny1K7gBIrw==" + }, "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -17191,6 +17576,14 @@ "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true }, + "is-valid-path": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-valid-path/-/is-valid-path-0.1.1.tgz", + "integrity": "sha1-EQ+f90w39mPh7HkV60UfLbk6yd8=", + "requires": { + "is-invalid-path": "^0.1.0" + } + }, "is-windows": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-0.2.0.tgz", @@ -18099,6 +18492,51 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + }, + "lodash._baseiteratee": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash._baseiteratee/-/lodash._baseiteratee-4.7.0.tgz", + "integrity": "sha1-NKm1VDVycnw9sueO2uPA6eZr0QI=", + "requires": { + "lodash._stringtopath": "~4.8.0" + } + }, + "lodash._basetostring": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/lodash._basetostring/-/lodash._basetostring-4.12.0.tgz", + "integrity": "sha1-kyfJ3FFYhmt/pLnUL0Y45XZt2d8=" + }, + "lodash._baseuniq": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz", + "integrity": "sha1-DrtE5FaBSveQXGIS+iybLVG4Qeg=", + "requires": { + "lodash._createset": "~4.0.0", + "lodash._root": "~3.0.0" + } + }, + "lodash._createset": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/lodash._createset/-/lodash._createset-4.0.3.tgz", + "integrity": "sha1-D0ZZ+7CddRlPqeK4imZE02PJ/iY=" + }, + "lodash._root": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/lodash._root/-/lodash._root-3.0.1.tgz", + "integrity": "sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI=" + }, + "lodash._stringtopath": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/lodash._stringtopath/-/lodash._stringtopath-4.8.0.tgz", + "integrity": "sha1-lBvPDmQmbl/B1m/tCmlZVExXaCQ=", + "requires": { + "lodash._basetostring": "~4.12.0" + } + }, "lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", @@ -18163,6 +18601,15 @@ "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", "dev": true }, + "lodash.uniqby": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.5.0.tgz", + "integrity": "sha1-o6F7v2LutiQPSRhG6XwcTipeHiE=", + "requires": { + "lodash._baseiteratee": "~4.7.0", + "lodash._baseuniq": "~4.6.0" + } + }, "log-symbols": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", @@ -18456,6 +18903,11 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "nanoclone": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/nanoclone/-/nanoclone-0.2.1.tgz", + "integrity": "sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA==" + }, "nanoid": { "version": "3.1.28", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.28.tgz", @@ -18643,6 +19095,11 @@ "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" }, + "numbered": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/numbered/-/numbered-1.1.0.tgz", + "integrity": "sha512-pv/ue2Odr7IfYOO0byC1KgBI10wo5YDauLhxY6/saNzAdAs0r1SotGCPzzCLNPL0xtrAwWRialLu23AAu9xO1g==" + }, "nwsapi": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", @@ -18723,6 +19180,11 @@ "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" + }, "p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", @@ -18740,11 +19202,27 @@ "p-limit": "^2.2.0" } }, + "p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "requires": { + "p-finally": "^1.0.0" + } + }, "p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==" }, + "p-wait-for": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-wait-for/-/p-wait-for-3.1.0.tgz", + "integrity": "sha512-0Uy19uhxbssHelu9ynDMcON6BmMk6pH8551CvxROhiz3Vx+yC4RqxjyIDk2V4ll0g9177RKT++PK4zcV58uJ7A==", + "requires": { + "p-timeout": "^3.0.0" + } + }, "parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -19110,6 +19588,11 @@ "sisteransi": "^1.0.5" } }, + "property-expr": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/property-expr/-/property-expr-2.0.4.tgz", + "integrity": "sha512-sFPkHQjVKheDNnPvotjQmm3KD3uk1fWKUN7CrpdbwmUx3CrG3QiM8QpTSimvig5vTXmTvjz7+TDvXOI9+4rkcg==" + }, "proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -19536,8 +20019,7 @@ "regenerator-runtime": { "version": "0.13.9", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", - "dev": true + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" }, "regenerator-transform": { "version": "0.14.5", @@ -19842,6 +20324,11 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, + "safe-timers": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-timers/-/safe-timers-1.1.0.tgz", + "integrity": "sha1-xYroMl2407BnMi8KTvOgytZ6rYM=" + }, "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -20703,6 +21190,11 @@ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" }, + "toposort": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz", + "integrity": "sha1-riF2gXXRVZ1IvvNUILL0li8JwzA=" + }, "tough-cookie": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.0.0.tgz", @@ -21432,6 +21924,20 @@ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" }, + "yup": { + "version": "0.32.9", + "resolved": "https://registry.npmjs.org/yup/-/yup-0.32.9.tgz", + "integrity": "sha512-Ci1qN+i2H0XpY7syDQ0k5zKQ/DoxO0LzPg8PAR/X4Mpj6DqaeCoIYEEjDJwhArh3Fa7GWbQQVDZKeXYlSH4JMg==", + "requires": { + "@babel/runtime": "^7.10.5", + "@types/lodash": "^4.14.165", + "lodash": "^4.17.20", + "lodash-es": "^4.17.15", + "nanoclone": "^0.2.1", + "property-expr": "^2.0.4", + "toposort": "^2.0.2" + } + }, "zwitch": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-1.0.5.tgz", diff --git a/package.json b/package.json index 024816ba..944dbbd5 100644 --- a/package.json +++ b/package.json @@ -60,6 +60,7 @@ "axios": "~0.21.4", "bcryptjs": "~2.4.3", "bootstrap": "~5.1.1", + "bree": "~6.3.1", "chart.js": "~3.5.1", "chartjs-adapter-dayjs": "~1.0.0", "command-exists": "~1.2.9", diff --git a/server/jobs.js b/server/jobs.js new file mode 100644 index 00000000..18460a1a --- /dev/null +++ b/server/jobs.js @@ -0,0 +1,28 @@ +const path = require("path"); +const Bree = require("bree"); +const { SHARE_ENV } = require("worker_threads"); + +const jobs = [ + { + name: "clear-old-data", + interval: "every 1 minute", + } +]; + +const initBackgroundJobs = function (args) { + const bree = new Bree({ + root: path.resolve("server", "jobs"), + jobs, + worker: { + env: SHARE_ENV, + workerData: args, + }, + }); + + bree.start(); + return bree; +}; + +module.exports = { + initBackgroundJobs +}; diff --git a/server/jobs/clear-old-data.js b/server/jobs/clear-old-data.js new file mode 100644 index 00000000..5b109da0 --- /dev/null +++ b/server/jobs/clear-old-data.js @@ -0,0 +1,24 @@ +const path = require("path"); +const { R } = require("redbean-node"); +const Database = require("../database"); + +const dbPath = path.join( + process.env.DATA_DIR || + require("worker_threads").workerData["data-dir"] || + "./data/" +); + +Database.init({ + "data-dir": dbPath, +}); + +(async () => { + await Database.connect(); + + console.log(await R.getAll("PRAGMA journal_mode")); + console.log( + await R.getAll("SELECT * from setting WHERE key = 'database_version'") + ); + + process.exit(0); +})(); diff --git a/server/server.js b/server/server.js index 390c9409..d78ab93b 100644 --- a/server/server.js +++ b/server/server.js @@ -46,6 +46,9 @@ Notification.init(); debug("Importing Database"); const Database = require("./database"); +debug("Importing Background Jobs"); +const { initBackgroundJobs } = require("./jobs"); + const { basicAuth } = require("./auth"); const { login } = require("./auth"); const passwordHash = require("./password-hash"); @@ -1231,6 +1234,8 @@ exports.entryPage = "dashboard"; } }); + initBackgroundJobs(args); + })(); async function updateMonitorNotification(monitorID, notificationIDList) { From dca5a59dbc17715ddc98ffb0f7430d773f19bd05 Mon Sep 17 00:00:00 2001 From: Nelson Chan Date: Sat, 9 Oct 2021 23:33:47 +0800 Subject: [PATCH 011/121] Feat: Implement data clearing logic & frontend --- server/jobs.js | 5 +++- server/jobs/clear-old-data.js | 50 +++++++++++++++++++++++------------ server/jobs/util-worker.js | 39 +++++++++++++++++++++++++++ src/languages/en.js | 2 ++ src/pages/Settings.vue | 12 +++++++++ 5 files changed, 90 insertions(+), 18 deletions(-) create mode 100644 server/jobs/util-worker.js diff --git a/server/jobs.js b/server/jobs.js index 18460a1a..d8435ba3 100644 --- a/server/jobs.js +++ b/server/jobs.js @@ -5,7 +5,7 @@ const { SHARE_ENV } = require("worker_threads"); const jobs = [ { name: "clear-old-data", - interval: "every 1 minute", + interval: "every 1 day", } ]; @@ -17,6 +17,9 @@ const initBackgroundJobs = function (args) { env: SHARE_ENV, workerData: args, }, + workerMessageHandler: (message) => { + console.log("[Background Job]:", message); + } }); bree.start(); diff --git a/server/jobs/clear-old-data.js b/server/jobs/clear-old-data.js index 5b109da0..2b1ed47c 100644 --- a/server/jobs/clear-old-data.js +++ b/server/jobs/clear-old-data.js @@ -1,24 +1,40 @@ -const path = require("path"); +const { log, exit, connectDb } = require("./util-worker"); const { R } = require("redbean-node"); -const Database = require("../database"); +const { setSetting, setting } = require("../util-server"); -const dbPath = path.join( - process.env.DATA_DIR || - require("worker_threads").workerData["data-dir"] || - "./data/" -); - -Database.init({ - "data-dir": dbPath, -}); +const DEFAULT_KEEP_PERIOD = 30; (async () => { - await Database.connect(); + await connectDb(); - console.log(await R.getAll("PRAGMA journal_mode")); - console.log( - await R.getAll("SELECT * from setting WHERE key = 'database_version'") - ); + let period = await setting("keepDataPeriodDays"); - process.exit(0); + // Set Default Period + if (period == null) { + await setSetting("keepDataPeriodDays", DEFAULT_KEEP_PERIOD); + period = DEFAULT_KEEP_PERIOD; + } + + // Try parse setting + let parsedPeriod; + try { + parsedPeriod = parseInt(period); + } catch (_) { + log("Failed to parse setting, resetting to default.."); + await setSetting("keepDataPeriodDays", DEFAULT_KEEP_PERIOD); + parsedPeriod = DEFAULT_KEEP_PERIOD; + } + + log(`Clearing Data older than ${parsedPeriod} days...`); + + try { + await R.exec( + "DELETE FROM heartbeat WHERE time < DATETIME('now', '-' || ? || ' days') ", + [parsedPeriod] + ); + } catch (e) { + log(`Failed to clear old data: ${e.message}`); + } + + exit(); })(); diff --git a/server/jobs/util-worker.js b/server/jobs/util-worker.js new file mode 100644 index 00000000..9426840d --- /dev/null +++ b/server/jobs/util-worker.js @@ -0,0 +1,39 @@ +const { parentPort, workerData } = require("worker_threads"); +const Database = require("../database"); +const path = require("path"); + +const log = function (any) { + if (parentPort) { + parentPort.postMessage(any); + } +}; + +const exit = function (error) { + if (error && error != 0) { + process.exit(error); + } else { + if (parentPort) { + parentPort.postMessage("done"); + } else { + process.exit(0); + } + } +}; + +const connectDb = async function () { + const dbPath = path.join( + process.env.DATA_DIR || workerData["data-dir"] || "./data/" + ); + + Database.init({ + "data-dir": dbPath, + }); + + await Database.connect(); +}; + +module.exports = { + log, + exit, + connectDb, +}; diff --git a/src/languages/en.js b/src/languages/en.js index 2ce8f46b..31eaee0e 100644 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -281,4 +281,6 @@ export default { promosmsPhoneNumber: "Phone number (for Polish recipient You can skip area codes)", promosmsSMSSender: "SMS Sender Name : Pre-registred name or one of defaults: InfoSMS, SMS Info, MaxSMS, INFO, SMS", // End notification form + "Monitor History": "Monitor History", + clearDataOlderThan: "Remove monitor data older than {0} days", }; diff --git a/src/pages/Settings.vue b/src/pages/Settings.vue index 259f334b..b5f0f7a7 100644 --- a/src/pages/Settings.vue +++ b/src/pages/Settings.vue @@ -212,6 +212,14 @@ {{ importAlert }}
+
+

{{ $t("Monitor History") }}

+
+ + +
+
+

{{ $t("Advanced") }}

@@ -465,6 +473,10 @@ export default { this.settings.entryPage = "dashboard"; } + if (this.settings.keepDataPeriodDays === undefined) { + this.settings.keepDataPeriodDays = 30; + } + this.loaded = true; }); }, From 6cf2eb036d60e01ef4ab12163d93e9b8cdf86ca1 Mon Sep 17 00:00:00 2001 From: Nelson Chan Date: Sat, 9 Oct 2021 23:51:05 +0800 Subject: [PATCH 012/121] Fix: Improve settings layout and wording --- src/languages/en.js | 4 ++-- src/pages/Settings.vue | 17 +++++++++-------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/languages/en.js b/src/languages/en.js index 31eaee0e..0c2a0263 100644 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -281,6 +281,6 @@ export default { promosmsPhoneNumber: "Phone number (for Polish recipient You can skip area codes)", promosmsSMSSender: "SMS Sender Name : Pre-registred name or one of defaults: InfoSMS, SMS Info, MaxSMS, INFO, SMS", // End notification form - "Monitor History": "Monitor History", - clearDataOlderThan: "Remove monitor data older than {0} days", + "Monitor History": "Monitor History:", + clearDataOlderThan: "Keep monitor history data for {0} days.", }; diff --git a/src/pages/Settings.vue b/src/pages/Settings.vue index b5f0f7a7..b5c57c20 100644 --- a/src/pages/Settings.vue +++ b/src/pages/Settings.vue @@ -119,6 +119,15 @@
+ +
+

{{ $t("Monitor History") }}

+
+ + +
+
+
-
-

{{ $t("Monitor History") }}

-
- - -
-
-

{{ $t("Advanced") }}

From 8caf47988cd08297def5b106a864521dcc8c02c7 Mon Sep 17 00:00:00 2001 From: Nelson Chan Date: Sun, 10 Oct 2021 00:16:13 +0800 Subject: [PATCH 013/121] Fix: Allow setting settings type --- server/jobs/clear-old-data.js | 4 ++-- server/util-server.js | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/server/jobs/clear-old-data.js b/server/jobs/clear-old-data.js index 2b1ed47c..6577f897 100644 --- a/server/jobs/clear-old-data.js +++ b/server/jobs/clear-old-data.js @@ -11,7 +11,7 @@ const DEFAULT_KEEP_PERIOD = 30; // Set Default Period if (period == null) { - await setSetting("keepDataPeriodDays", DEFAULT_KEEP_PERIOD); + await setSetting("keepDataPeriodDays", DEFAULT_KEEP_PERIOD, "general"); period = DEFAULT_KEEP_PERIOD; } @@ -21,7 +21,7 @@ const DEFAULT_KEEP_PERIOD = 30; parsedPeriod = parseInt(period); } catch (_) { log("Failed to parse setting, resetting to default.."); - await setSetting("keepDataPeriodDays", DEFAULT_KEEP_PERIOD); + await setSetting("keepDataPeriodDays", DEFAULT_KEEP_PERIOD, "general"); parsedPeriod = DEFAULT_KEEP_PERIOD; } diff --git a/server/util-server.js b/server/util-server.js index 5620d674..5a486d3a 100644 --- a/server/util-server.js +++ b/server/util-server.js @@ -116,7 +116,7 @@ exports.setting = async function (key) { } }; -exports.setSetting = async function (key, value) { +exports.setSetting = async function (key, value, type = null) { let bean = await R.findOne("setting", " `key` = ? ", [ key, ]); @@ -124,6 +124,7 @@ exports.setSetting = async function (key, value) { bean = R.dispense("setting"); bean.key = key; } + bean.type = type; bean.value = JSON.stringify(value); await R.store(bean); }; From ac80631bcd2b9e0d79a94ba9541db081ffda5feb Mon Sep 17 00:00:00 2001 From: Nelson Chan Date: Sun, 10 Oct 2021 00:16:29 +0800 Subject: [PATCH 014/121] Fix: Run clear data at specific time --- server/jobs.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/jobs.js b/server/jobs.js index d8435ba3..8a768b91 100644 --- a/server/jobs.js +++ b/server/jobs.js @@ -5,7 +5,7 @@ const { SHARE_ENV } = require("worker_threads"); const jobs = [ { name: "clear-old-data", - interval: "every 1 day", + interval: "at 03:14", } ]; From 5e3ea3293c5d1f37ba65ab94182075b788608b45 Mon Sep 17 00:00:00 2001 From: Lukas <35193662+NixNotCastey@users.noreply.github.com> Date: Sat, 9 Oct 2021 20:32:45 +0200 Subject: [PATCH 015/121] Very basic email subject customization --- server/notification-providers/smtp.js | 10 +++++++++- src/components/notifications/SMTP.vue | 5 +++++ src/languages/en.js | 1 + 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/server/notification-providers/smtp.js b/server/notification-providers/smtp.js index ecb583eb..e5fd53e4 100644 --- a/server/notification-providers/smtp.js +++ b/server/notification-providers/smtp.js @@ -20,6 +20,14 @@ class SMTP extends NotificationProvider { pass: notification.smtpPassword, }; } + // Lets start with default subject + let subject = msg; + // Our subject cannot end with whitespace it's often raise spam score + let customsubject = notification.customsubject.trim() + // If custom subject is not empty, change subject for notification + if (customsubject !== "") { + subject = customsubject + } let transporter = nodemailer.createTransport(config); @@ -34,7 +42,7 @@ class SMTP extends NotificationProvider { cc: notification.smtpCC, bcc: notification.smtpBCC, to: notification.smtpTo, - subject: msg, + subject: subject, text: bodyTextContent, tls: { rejectUnauthorized: notification.smtpIgnoreTLSError || false, diff --git a/src/components/notifications/SMTP.vue b/src/components/notifications/SMTP.vue index 72934cda..165d39c6 100644 --- a/src/components/notifications/SMTP.vue +++ b/src/components/notifications/SMTP.vue @@ -43,6 +43,11 @@
+
+ + +
+
diff --git a/src/languages/en.js b/src/languages/en.js index 2ce8f46b..b59cdb73 100644 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -201,6 +201,7 @@ export default { secureOptionTLS: "TLS (465)", "Ignore TLS Error": "Ignore TLS Error", "From Email": "From Email", + "Custom Email subject": "Custom Email Subject (leave blank for default one)", "To Email": "To Email", smtpCC: "CC", smtpBCC: "BCC", From 792f3c7c5c17a2db80e7f9638e272b4607f3d4c6 Mon Sep 17 00:00:00 2001 From: Lukas <35193662+NixNotCastey@users.noreply.github.com> Date: Sat, 9 Oct 2021 21:48:28 +0200 Subject: [PATCH 016/121] Add support for values of Name, Hostname and Status --- server/notification-providers/smtp.js | 30 +++++++++++++++++++++++++++ src/components/notifications/SMTP.vue | 4 ++-- src/languages/en.js | 2 +- 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/server/notification-providers/smtp.js b/server/notification-providers/smtp.js index e5fd53e4..2bbec584 100644 --- a/server/notification-providers/smtp.js +++ b/server/notification-providers/smtp.js @@ -22,10 +22,40 @@ class SMTP extends NotificationProvider { } // Lets start with default subject let subject = msg; + // Our subject cannot end with whitespace it's often raise spam score let customsubject = notification.customsubject.trim() + // If custom subject is not empty, change subject for notification if (customsubject !== "") { + + // Replace "MACROS" with coresponding variable + let replaceName = new RegExp("{NAME}", "g"); + let replaceHostname = new RegExp("{HOSTNAME}", "g"); + let replaceStatus = new RegExp("{STATUS}", "g"); + + let serviceStatus; + + if (monitorJSON !== null) { + customsubject = customsubject.replace(replaceName,monitorJSON["name"]); + customsubject = customsubject.replace(replaceHostname,monitorJSON["hostname"]); + } else { + // Insert dummy values during test + customsubject = customsubject.replace(replaceName,"Test"); + customsubject = customsubject.replace(replaceHostname,"example.com"); + } + if (heartbeatJSON !== null) { + if (heartbeatJSON["status"] === 0) { + serviceStatus = "🔴 Down" + } else { + serviceStatus = "✅ Up" + } + customsubject = customsubject.replace(replaceStatus,serviceStatus); + } else { + // Insert dummy values during test + customsubject = customsubject.replace(replaceStatus,"TEST"); + } + subject = customsubject } diff --git a/src/components/notifications/SMTP.vue b/src/components/notifications/SMTP.vue index 165d39c6..01bdf860 100644 --- a/src/components/notifications/SMTP.vue +++ b/src/components/notifications/SMTP.vue @@ -44,8 +44,8 @@
- - + +
diff --git a/src/languages/en.js b/src/languages/en.js index b59cdb73..0e8e9230 100644 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -201,7 +201,7 @@ export default { secureOptionTLS: "TLS (465)", "Ignore TLS Error": "Ignore TLS Error", "From Email": "From Email", - "Custom Email subject": "Custom Email Subject (leave blank for default one)", + "Email Subject": "Subject (leave blank for default one)", "To Email": "To Email", smtpCC: "CC", smtpBCC: "BCC", From 5137c80c07d8a0781e585d35c86b58252663907e Mon Sep 17 00:00:00 2001 From: Bert Verhelst Date: Sat, 9 Oct 2021 21:51:24 +0200 Subject: [PATCH 017/121] fix(monitor): handle empty headers --- server/model/monitor.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/model/monitor.js b/server/model/monitor.js index 2738898f..e2029c5e 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -148,10 +148,10 @@ class Monitor extends BeanModel { headers: { "Accept": "*/*", "User-Agent": "Uptime-Kuma/" + version, - ...JSON.parse(this.headers), + ...(this.headers ? JSON.parse(this.headers) : {}), }, httpsAgent: new https.Agent({ - maxCachedSessions: 0, // Use Custom agent to disable session reuse (https://github.com/nodejs/node/issues/3940) + maxCachedSessions: 0, // Use "{}"om agent to disable session reuse (https://github.com/nodejs/node/issues/3940) rejectUnauthorized: ! this.getIgnoreTls(), }), maxRedirects: this.maxredirects, From 5445c2a2ffec9cb4c87529e7afdf87737ccfba13 Mon Sep 17 00:00:00 2001 From: Bert Verhelst Date: Sun, 10 Oct 2021 18:41:29 +0200 Subject: [PATCH 018/121] fix(monitor): revert unintentional change to comment --- server/model/monitor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/model/monitor.js b/server/model/monitor.js index 094b1c69..21d36000 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -152,7 +152,7 @@ class Monitor extends BeanModel { ...(this.headers ? JSON.parse(this.headers) : {}), }, httpsAgent: new https.Agent({ - maxCachedSessions: 0, // Use "{}"om agent to disable session reuse (https://github.com/nodejs/node/issues/3940) + maxCachedSessions: 0, // Use Custom agent to disable session reuse (https://github.com/nodejs/node/issues/3940) rejectUnauthorized: ! this.getIgnoreTls(), }), maxRedirects: this.maxredirects, From 13cf6891acf99e55fdb10bb769f651df5c19eb44 Mon Sep 17 00:00:00 2001 From: Andreas Brett Date: Sun, 10 Oct 2021 21:58:23 +0200 Subject: [PATCH 019/121] cryptographically strong secret generation generate TOTP secret using WebCrypto API (see https://github.com/louislam/uptime-kuma/issues/640) --- src/util.ts | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/util.ts b/src/util.ts index 6e911998..22279a7d 100644 --- a/src/util.ts +++ b/src/util.ts @@ -114,12 +114,21 @@ export function getRandomInt(min: number, max: number) { return Math.floor(Math.random() * (max - min + 1)) + min; } +export function getCryptoRandomInt(min, max) { + const randomBuffer = new Uint32Array(1); + crypto.getRandomValues(randomBuffer); + let randomNumber = randomBuffer[0] / (0xffffffff + 1); + min = Math.ceil(min); + max = Math.floor(max); + return Math.floor(randomNumber * (max - min + 1)) + min; +} + export function genSecret(length = 64) { let secret = ""; - let chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; - let charsLength = chars.length; - for ( let i = 0; i < length; i++ ) { - secret += chars.charAt(Math.floor(Math.random() * charsLength)); + const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + const charsLength = chars.length; + for ( let i = 0; i < 64; i++ ) { + secret += chars.charAt(getCryptoRandomInt(0, charsLength)); } return secret; } From 075535ba460a9f20439748b72d6f48d88a79a8e8 Mon Sep 17 00:00:00 2001 From: Andreas Brett Date: Sun, 10 Oct 2021 21:59:23 +0200 Subject: [PATCH 020/121] Update util.ts --- src/util.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util.ts b/src/util.ts index 22279a7d..6fdfc3be 100644 --- a/src/util.ts +++ b/src/util.ts @@ -127,7 +127,7 @@ export function genSecret(length = 64) { let secret = ""; const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; const charsLength = chars.length; - for ( let i = 0; i < 64; i++ ) { + for ( let i = 0; i < length; i++ ) { secret += chars.charAt(getCryptoRandomInt(0, charsLength)); } return secret; From b5b391c73b1757b3cc6580b679196921328835ea Mon Sep 17 00:00:00 2001 From: Andreas Brett Date: Sun, 10 Oct 2021 22:13:18 +0200 Subject: [PATCH 021/121] avoid default values for token verification override default values: window=1, window size=30 (see https://github.com/louislam/uptime-kuma/issues/640) --- server/server.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/server.js b/server/server.js index 67095ff5..e5f3335f 100644 --- a/server/server.js +++ b/server/server.js @@ -265,7 +265,7 @@ exports.entryPage = "dashboard"; } if (data.token) { - let verify = notp.totp.verify(data.token, user.twofa_secret); + let verify = notp.totp.verify(data.token, user.twofa_secret, {"window": 1, "time": 30}); if (verify && verify.delta == 0) { callback({ @@ -383,7 +383,7 @@ exports.entryPage = "dashboard"; socket.userID, ]); - let verify = notp.totp.verify(token, user.twofa_secret); + let verify = notp.totp.verify(token, user.twofa_secret, {"window": 1, "time": 30}); if (verify && verify.delta == 0) { callback({ From e127e168b6b6295b38dc32e722466a61f94fa4cb Mon Sep 17 00:00:00 2001 From: Andreas Brett Date: Sun, 10 Oct 2021 22:15:42 +0200 Subject: [PATCH 022/121] typed parameters --- src/util.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util.ts b/src/util.ts index 6fdfc3be..8ba53738 100644 --- a/src/util.ts +++ b/src/util.ts @@ -114,7 +114,7 @@ export function getRandomInt(min: number, max: number) { return Math.floor(Math.random() * (max - min + 1)) + min; } -export function getCryptoRandomInt(min, max) { +export function getCryptoRandomInt(min; number, max: number) { const randomBuffer = new Uint32Array(1); crypto.getRandomValues(randomBuffer); let randomNumber = randomBuffer[0] / (0xffffffff + 1); From 06310423f4483164b0e43df30eb8f92ec2ca61a9 Mon Sep 17 00:00:00 2001 From: Andreas Brett Date: Sun, 10 Oct 2021 22:19:10 +0200 Subject: [PATCH 023/121] typo --- src/util.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util.ts b/src/util.ts index 8ba53738..6f058eed 100644 --- a/src/util.ts +++ b/src/util.ts @@ -114,7 +114,7 @@ export function getRandomInt(min: number, max: number) { return Math.floor(Math.random() * (max - min + 1)) + min; } -export function getCryptoRandomInt(min; number, max: number) { +export function getCryptoRandomInt(min: number, max: number) { const randomBuffer = new Uint32Array(1); crypto.getRandomValues(randomBuffer); let randomNumber = randomBuffer[0] / (0xffffffff + 1); From 11bcd1e2ed06651b8cf07e8df76ef20e5a6bca6a Mon Sep 17 00:00:00 2001 From: Andreas Brett Date: Sun, 10 Oct 2021 22:55:32 +0200 Subject: [PATCH 024/121] const --- src/util.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util.ts b/src/util.ts index 6f058eed..205589fe 100644 --- a/src/util.ts +++ b/src/util.ts @@ -117,7 +117,7 @@ export function getRandomInt(min: number, max: number) { export function getCryptoRandomInt(min: number, max: number) { const randomBuffer = new Uint32Array(1); crypto.getRandomValues(randomBuffer); - let randomNumber = randomBuffer[0] / (0xffffffff + 1); + const randomNumber = randomBuffer[0] / (0xffffffff + 1); min = Math.ceil(min); max = Math.floor(max); return Math.floor(randomNumber * (max - min + 1)) + min; From 0e6d7694cebcc0ec752ebab701a895890a9363ef Mon Sep 17 00:00:00 2001 From: Andreas Brett Date: Sun, 10 Oct 2021 23:54:45 +0200 Subject: [PATCH 025/121] Update util.js --- src/util.js | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/util.js b/src/util.js index 7fb50c5b..265ed51d 100644 --- a/src/util.js +++ b/src/util.js @@ -102,12 +102,21 @@ function getRandomInt(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; } exports.getRandomInt = getRandomInt; +function getCryptoRandomInt(min, max) { + const randomBuffer = new Uint32Array(1); + crypto.getRandomValues(randomBuffer); + const randomNumber = randomBuffer[0] / (0xffffffff + 1); + min = Math.ceil(min); + max = Math.floor(max); + return Math.floor(randomNumber * (max - min + 1)) + min; +} +exports.getCryptoRandomInt = getCryptoRandomInt; function genSecret(length = 64) { let secret = ""; - let chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; - let charsLength = chars.length; - for (let i = 0; i < length; i++) { - secret += chars.charAt(Math.floor(Math.random() * charsLength)); + const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + const charsLength = chars.length; + for ( let i = 0; i < length; i++ ) { + secret += chars.charAt(getCryptoRandomInt(0, charsLength)); } return secret; } From e223e826a376b6601dd8ec8e93b71b3a8752f50f Mon Sep 17 00:00:00 2001 From: Andreas Brett Date: Mon, 11 Oct 2021 01:02:54 +0200 Subject: [PATCH 026/121] linting --- server/server.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/server/server.js b/server/server.js index e5f3335f..10ec8700 100644 --- a/server/server.js +++ b/server/server.js @@ -265,7 +265,8 @@ exports.entryPage = "dashboard"; } if (data.token) { - let verify = notp.totp.verify(data.token, user.twofa_secret, {"window": 1, "time": 30}); + let verify = notp.totp.verify(data.token, user.twofa_secret, { "window": 1, + "time": 30 }); if (verify && verify.delta == 0) { callback({ @@ -383,7 +384,8 @@ exports.entryPage = "dashboard"; socket.userID, ]); - let verify = notp.totp.verify(token, user.twofa_secret, {"window": 1, "time": 30}); + let verify = notp.totp.verify(token, user.twofa_secret, { "window": 1, + "time": 30 }); if (verify && verify.delta == 0) { callback({ From dc1de50a02d7f4ea3205f179b60495be23403692 Mon Sep 17 00:00:00 2001 From: Andreas Brett Date: Mon, 11 Oct 2021 01:18:33 +0200 Subject: [PATCH 027/121] fix for max-inclusive --- src/util.js | 254 ++++++++++++++++++++++++++-------------------------- src/util.ts | 2 +- 2 files changed, 128 insertions(+), 128 deletions(-) diff --git a/src/util.js b/src/util.js index 265ed51d..8135f2a0 100644 --- a/src/util.js +++ b/src/util.js @@ -1,127 +1,127 @@ -"use strict"; -// Common Util for frontend and backend -// -// DOT NOT MODIFY util.js! -// Need to run "tsc" to compile if there are any changes. -// -// Backend uses the compiled file util.js -// Frontend uses util.ts -Object.defineProperty(exports, "__esModule", { value: true }); -exports.getMonitorRelativeURL = exports.genSecret = exports.getRandomInt = exports.getRandomArbitrary = exports.TimeLogger = exports.polyfill = exports.debug = exports.ucfirst = exports.sleep = exports.flipStatus = exports.STATUS_PAGE_PARTIAL_DOWN = exports.STATUS_PAGE_ALL_UP = exports.STATUS_PAGE_ALL_DOWN = exports.PENDING = exports.UP = exports.DOWN = exports.appName = exports.isDev = void 0; -const _dayjs = require("dayjs"); -const dayjs = _dayjs; -exports.isDev = process.env.NODE_ENV === "development"; -exports.appName = "Uptime Kuma"; -exports.DOWN = 0; -exports.UP = 1; -exports.PENDING = 2; -exports.STATUS_PAGE_ALL_DOWN = 0; -exports.STATUS_PAGE_ALL_UP = 1; -exports.STATUS_PAGE_PARTIAL_DOWN = 2; -function flipStatus(s) { - if (s === exports.UP) { - return exports.DOWN; - } - if (s === exports.DOWN) { - return exports.UP; - } - return s; -} -exports.flipStatus = flipStatus; -function sleep(ms) { - return new Promise(resolve => setTimeout(resolve, ms)); -} -exports.sleep = sleep; -/** - * PHP's ucfirst - * @param str - */ -function ucfirst(str) { - if (!str) { - return str; - } - const firstLetter = str.substr(0, 1); - return firstLetter.toUpperCase() + str.substr(1); -} -exports.ucfirst = ucfirst; -function debug(msg) { - if (exports.isDev) { - console.log(msg); - } -} -exports.debug = debug; -function polyfill() { - /** - * String.prototype.replaceAll() polyfill - * https://gomakethings.com/how-to-replace-a-section-of-a-string-with-another-one-with-vanilla-js/ - * @author Chris Ferdinandi - * @license MIT - */ - if (!String.prototype.replaceAll) { - String.prototype.replaceAll = function (str, newStr) { - // If a regex pattern - if (Object.prototype.toString.call(str).toLowerCase() === "[object regexp]") { - return this.replace(str, newStr); - } - // If a string - return this.replace(new RegExp(str, "g"), newStr); - }; - } -} -exports.polyfill = polyfill; -class TimeLogger { - constructor() { - this.startTime = dayjs().valueOf(); - } - print(name) { - if (exports.isDev && process.env.TIMELOGGER === "1") { - console.log(name + ": " + (dayjs().valueOf() - this.startTime) + "ms"); - } - } -} -exports.TimeLogger = TimeLogger; -/** - * Returns a random number between min (inclusive) and max (exclusive) - */ -function getRandomArbitrary(min, max) { - return Math.random() * (max - min) + min; -} -exports.getRandomArbitrary = getRandomArbitrary; -/** - * From: https://stackoverflow.com/questions/1527803/generating-random-whole-numbers-in-javascript-in-a-specific-range - * - * Returns a random integer between min (inclusive) and max (inclusive). - * The value is no lower than min (or the next integer greater than min - * if min isn't an integer) and no greater than max (or the next integer - * lower than max if max isn't an integer). - * Using Math.round() will give you a non-uniform distribution! - */ -function getRandomInt(min, max) { - min = Math.ceil(min); - max = Math.floor(max); - return Math.floor(Math.random() * (max - min + 1)) + min; -} -exports.getRandomInt = getRandomInt; -function getCryptoRandomInt(min, max) { - const randomBuffer = new Uint32Array(1); - crypto.getRandomValues(randomBuffer); - const randomNumber = randomBuffer[0] / (0xffffffff + 1); - min = Math.ceil(min); - max = Math.floor(max); - return Math.floor(randomNumber * (max - min + 1)) + min; -} -exports.getCryptoRandomInt = getCryptoRandomInt; -function genSecret(length = 64) { - let secret = ""; - const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; - const charsLength = chars.length; - for ( let i = 0; i < length; i++ ) { - secret += chars.charAt(getCryptoRandomInt(0, charsLength)); - } - return secret; -} -exports.genSecret = genSecret; -function getMonitorRelativeURL(id) { - return "/dashboard/" + id; -} -exports.getMonitorRelativeURL = getMonitorRelativeURL; +"use strict"; +// Common Util for frontend and backend +// +// DOT NOT MODIFY util.js! +// Need to run "tsc" to compile if there are any changes. +// +// Backend uses the compiled file util.js +// Frontend uses util.ts +Object.defineProperty(exports, "__esModule", { value: true }); +exports.getMonitorRelativeURL = exports.genSecret = exports.getRandomInt = exports.getRandomArbitrary = exports.TimeLogger = exports.polyfill = exports.debug = exports.ucfirst = exports.sleep = exports.flipStatus = exports.STATUS_PAGE_PARTIAL_DOWN = exports.STATUS_PAGE_ALL_UP = exports.STATUS_PAGE_ALL_DOWN = exports.PENDING = exports.UP = exports.DOWN = exports.appName = exports.isDev = void 0; +const _dayjs = require("dayjs"); +const dayjs = _dayjs; +exports.isDev = process.env.NODE_ENV === "development"; +exports.appName = "Uptime Kuma"; +exports.DOWN = 0; +exports.UP = 1; +exports.PENDING = 2; +exports.STATUS_PAGE_ALL_DOWN = 0; +exports.STATUS_PAGE_ALL_UP = 1; +exports.STATUS_PAGE_PARTIAL_DOWN = 2; +function flipStatus(s) { + if (s === exports.UP) { + return exports.DOWN; + } + if (s === exports.DOWN) { + return exports.UP; + } + return s; +} +exports.flipStatus = flipStatus; +function sleep(ms) { + return new Promise(resolve => setTimeout(resolve, ms)); +} +exports.sleep = sleep; +/** + * PHP's ucfirst + * @param str + */ +function ucfirst(str) { + if (!str) { + return str; + } + const firstLetter = str.substr(0, 1); + return firstLetter.toUpperCase() + str.substr(1); +} +exports.ucfirst = ucfirst; +function debug(msg) { + if (exports.isDev) { + console.log(msg); + } +} +exports.debug = debug; +function polyfill() { + /** + * String.prototype.replaceAll() polyfill + * https://gomakethings.com/how-to-replace-a-section-of-a-string-with-another-one-with-vanilla-js/ + * @author Chris Ferdinandi + * @license MIT + */ + if (!String.prototype.replaceAll) { + String.prototype.replaceAll = function (str, newStr) { + // If a regex pattern + if (Object.prototype.toString.call(str).toLowerCase() === "[object regexp]") { + return this.replace(str, newStr); + } + // If a string + return this.replace(new RegExp(str, "g"), newStr); + }; + } +} +exports.polyfill = polyfill; +class TimeLogger { + constructor() { + this.startTime = dayjs().valueOf(); + } + print(name) { + if (exports.isDev && process.env.TIMELOGGER === "1") { + console.log(name + ": " + (dayjs().valueOf() - this.startTime) + "ms"); + } + } +} +exports.TimeLogger = TimeLogger; +/** + * Returns a random number between min (inclusive) and max (exclusive) + */ +function getRandomArbitrary(min, max) { + return Math.random() * (max - min) + min; +} +exports.getRandomArbitrary = getRandomArbitrary; +/** + * From: https://stackoverflow.com/questions/1527803/generating-random-whole-numbers-in-javascript-in-a-specific-range + * + * Returns a random integer between min (inclusive) and max (inclusive). + * The value is no lower than min (or the next integer greater than min + * if min isn't an integer) and no greater than max (or the next integer + * lower than max if max isn't an integer). + * Using Math.round() will give you a non-uniform distribution! + */ +function getRandomInt(min, max) { + min = Math.ceil(min); + max = Math.floor(max); + return Math.floor(Math.random() * (max - min + 1)) + min; +} +exports.getRandomInt = getRandomInt; +function getCryptoRandomInt(min, max) { + const randomBuffer = new Uint32Array(1); + crypto.getRandomValues(randomBuffer); + const randomNumber = randomBuffer[0] / (0xffffffff + 1); + min = Math.ceil(min); + max = Math.floor(max); + return Math.floor(randomNumber * (max - min + 1)) + min; +} +exports.getCryptoRandomInt = getCryptoRandomInt; +function genSecret(length = 64) { + let secret = ""; + const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + const charsLength = chars.length; + for ( let i = 0; i < length; i++ ) { + secret += chars.charAt(getCryptoRandomInt(0, charsLength - 1)); + } + return secret; +} +exports.genSecret = genSecret; +function getMonitorRelativeURL(id) { + return "/dashboard/" + id; +} +exports.getMonitorRelativeURL = getMonitorRelativeURL; diff --git a/src/util.ts b/src/util.ts index 205589fe..a1f6f259 100644 --- a/src/util.ts +++ b/src/util.ts @@ -128,7 +128,7 @@ export function genSecret(length = 64) { const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; const charsLength = chars.length; for ( let i = 0; i < length; i++ ) { - secret += chars.charAt(getCryptoRandomInt(0, charsLength)); + secret += chars.charAt(getCryptoRandomInt(0, charsLength - 1)); } return secret; } From 2538bd04ce41361481b0252bb00b94aeea1a50f3 Mon Sep 17 00:00:00 2001 From: Andreas Brett Date: Mon, 11 Oct 2021 20:18:40 +0200 Subject: [PATCH 028/121] notp verification defaults --- server/server.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/server/server.js b/server/server.js index 10ec8700..416cfe1f 100644 --- a/server/server.js +++ b/server/server.js @@ -68,6 +68,12 @@ const port = parseInt(process.env.PORT || args.port || 3001); const sslKey = process.env.SSL_KEY || args["ssl-key"] || undefined; const sslCert = process.env.SSL_CERT || args["ssl-cert"] || undefined; +// 2FA / notp verification defaults +const twofa_verification_opts = { + "window": 1, + "time": 30 +} + /** * Run unit test after the server is ready * @type {boolean} @@ -265,8 +271,7 @@ exports.entryPage = "dashboard"; } if (data.token) { - let verify = notp.totp.verify(data.token, user.twofa_secret, { "window": 1, - "time": 30 }); + let verify = notp.totp.verify(data.token, user.twofa_secret, twofa_verification_opts); if (verify && verify.delta == 0) { callback({ @@ -384,8 +389,7 @@ exports.entryPage = "dashboard"; socket.userID, ]); - let verify = notp.totp.verify(token, user.twofa_secret, { "window": 1, - "time": 30 }); + let verify = notp.totp.verify(token, user.twofa_secret, twofa_verification_opts); if (verify && verify.delta == 0) { callback({ From a2cc7d1db93237d69f8b3d7a4f72472976f460c1 Mon Sep 17 00:00:00 2001 From: zsxeee Date: Tue, 12 Oct 2021 15:10:32 +0800 Subject: [PATCH 029/121] Avoid directory not found error --- extra/update-language-files/index.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/extra/update-language-files/index.js b/extra/update-language-files/index.js index a90f9f36..7ba30cc0 100644 --- a/extra/update-language-files/index.js +++ b/extra/update-language-files/index.js @@ -26,10 +26,12 @@ const copyRecursiveSync = function (src, dest) { } }; -console.log("Arguments:", process.argv) +console.log("Arguments:", process.argv); const baseLangCode = process.argv[2] || "en"; console.log("Base Lang: " + baseLangCode); -fs.rmdirSync("./languages", { recursive: true }); +if (fs.existsSync("./languages")) { + fs.rmdirSync("./languages", { recursive: true }); +} copyRecursiveSync("../../src/languages", "./languages"); const en = (await import("./languages/en.js")).default; @@ -39,7 +41,7 @@ console.log("Files:", files); for (const file of files) { if (!file.endsWith(".js")) { - console.log("Skipping " + file) + console.log("Skipping " + file); continue; } From 3fe91c52cb994d5e8e0617d7a05854c1e8c6c193 Mon Sep 17 00:00:00 2001 From: zsxeee Date: Tue, 12 Oct 2021 16:29:18 +0800 Subject: [PATCH 030/121] Fix i18n Missing webhook json description Ajust Telegram context-based sentence, (also changed translated language files) Missing primary base url label Wrong PromoSMS i18n Missing Octopush legacy hint Missing Matrix i18n Missing push url i18n --- src/components/notifications/Matrix.vue | 18 +++++++++--------- src/components/notifications/Octopush.vue | 2 +- src/components/notifications/PromoSMS.vue | 6 +++--- src/components/notifications/Telegram.vue | 6 +++--- src/components/notifications/Webhook.vue | 2 +- src/languages/en.js | 12 +++++++++++- src/languages/fr-FR.js | 2 +- src/languages/ko-KR.js | 2 +- src/languages/nb-NO.js | 2 +- src/languages/pl.js | 2 +- src/pages/EditMonitor.vue | 4 ++-- src/pages/Settings.vue | 2 +- 12 files changed, 35 insertions(+), 25 deletions(-) diff --git a/src/components/notifications/Matrix.vue b/src/components/notifications/Matrix.vue index d1e973cd..d4790646 100644 --- a/src/components/notifications/Matrix.vue +++ b/src/components/notifications/Matrix.vue @@ -1,25 +1,25 @@ @@ -30,5 +30,5 @@ export default { components: { HiddenInput, }, -} +}; diff --git a/src/components/notifications/Octopush.vue b/src/components/notifications/Octopush.vue index c75e87d3..37629d39 100644 --- a/src/components/notifications/Octopush.vue +++ b/src/components/notifications/Octopush.vue @@ -6,7 +6,7 @@
- Do you use the legacy version of Octopush (2011-2020) or the new version? + {{ $t("octopushLegacyHint") }}
diff --git a/src/components/notifications/PromoSMS.vue b/src/components/notifications/PromoSMS.vue index 640360d0..06dea0b2 100644 --- a/src/components/notifications/PromoSMS.vue +++ b/src/components/notifications/PromoSMS.vue @@ -13,9 +13,10 @@ - +
+ {{ $t("checkPrice", [$t("promosms")]) }} https://promosms.com/cennik/ - +
@@ -25,7 +26,6 @@
- From 1d63dd9ddd540b78e6bf6e3f701b31010dc0fd4b Mon Sep 17 00:00:00 2001 From: Bert Verhelst Date: Sat, 16 Oct 2021 11:28:03 +0200 Subject: [PATCH 077/121] fix(monitor): safely get status of previous beat if first beat --- server/model/monitor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/model/monitor.js b/server/model/monitor.js index 4049a993..3640c732 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -304,7 +304,7 @@ class Monitor extends BeanModel { let beatInterval = this.interval; - let isImportant = Monitor.isImportantBeat(isFirstBeat, previousBeat.status, bean.status); + let isImportant = Monitor.isImportantBeat(isFirstBeat, previousBeat?.status, bean.status); // Mark as important if status changed, ignore pending pings, // Don't notify if disrupted changes to up From 53a008ae2b7226a3d5151a64a50bd39cf4617fbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Patr=C3=B3n=20G=C3=B3mez?= Date: Sat, 16 Oct 2021 11:32:15 +0200 Subject: [PATCH 078/121] Add Status Translation --- src/pages/Details.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/Details.vue b/src/pages/Details.vue index 1363aa20..d38d8f84 100644 --- a/src/pages/Details.vue +++ b/src/pages/Details.vue @@ -41,7 +41,7 @@ {{ $t("checkEverySecond", [ monitor.interval ]) }}
- {{ status.text }} + {{ $t(status.text) }}
From 15f36f96c3a5a0c40663a560f442a95cd41fb3f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Patr=C3=B3n=20G=C3=B3mez?= Date: Sat, 16 Oct 2021 12:15:39 +0200 Subject: [PATCH 079/121] Add spanish translation for monitor history --- src/languages/es-ES.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/languages/es-ES.js b/src/languages/es-ES.js index 41254b51..ecb94b1b 100644 --- a/src/languages/es-ES.js +++ b/src/languages/es-ES.js @@ -198,4 +198,6 @@ export default { pushbullet: "Pushbullet", line: "Line Messenger", mattermost: "Mattermost", + "Monitor History": "Historial de monitor:", + clearDataOlderThan: "Mantener los datos del historial del monitor durante {0} días.", }; From 0a73b84ae686dff56b39df80ea3626beabb883cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Patr=C3=B3n=20G=C3=B3mez?= Date: Sat, 16 Oct 2021 12:45:41 +0200 Subject: [PATCH 080/121] Add records translations and fix pause translation --- src/languages/en.js | 3 +++ src/languages/es-ES.js | 7 +++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/languages/en.js b/src/languages/en.js index f150046f..c0045ea1 100644 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -291,4 +291,7 @@ export default { BodyInvalidFormat: "The request body is not valid JSON: ", "Monitor History": "Monitor History:", clearDataOlderThan: "Keep monitor history data for {0} days.", + records: "records", + "One record": "One record", + "Showing {from} to {to} of {count} records": "Showing {from} to {to} of {count} records", }; diff --git a/src/languages/es-ES.js b/src/languages/es-ES.js index ecb94b1b..b438dbae 100644 --- a/src/languages/es-ES.js +++ b/src/languages/es-ES.js @@ -9,7 +9,7 @@ export default { passwordNotMatchMsg: "La contraseña repetida no coincide.", notificationDescription: "Por favor asigne una notificación a el/los monitor(es) para hacerlos funcional(es).", keywordDescription: "Palabra clave en HTML plano o respuesta JSON y es sensible a mayúsculas", - pauseDashboardHome: "Pausar", + pauseDashboardHome: "Pausado", deleteMonitorMsg: "¿Seguro que quieres eliminar este monitor?", deleteNotificationMsg: "¿Seguro que quieres eliminar esta notificación para todos los monitores?", resoverserverDescription: "Cloudflare es el servidor por defecto, puedes cambiar el servidor de resolución en cualquier momento.", @@ -32,7 +32,7 @@ export default { Down: "Caído", Pending: "Pendiente", Unknown: "Desconocido", - Pause: "Pausa", + Pause: "Pausar", Name: "Nombre", Status: "Estado", DateTime: "Fecha y Hora", @@ -200,4 +200,7 @@ export default { mattermost: "Mattermost", "Monitor History": "Historial de monitor:", clearDataOlderThan: "Mantener los datos del historial del monitor durante {0} días.", + records: "registros", + "One record": "Un registro", + "Showing {from} to {to} of {count} records": "Mostrando desde {from} a {to} de {count} registros", }; From 1276102c18627f3a3c354f6f5622fe2b4b625e7f Mon Sep 17 00:00:00 2001 From: "J. Eckert" Date: Sat, 16 Oct 2021 23:07:25 -0700 Subject: [PATCH 081/121] Update README.md minor grammatical edits for slight improvements. (Honestly though, your grammar is not that bad at all for not being a native speaker!) --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 1dc492bf..6b842080 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Try it! https://demo.uptime.kuma.pet -It is a 10 minutes live demo, all data will be deleted after that. The server is located at Tokyo, if you live far away from here, it may affact your experience. I suggest that you should install to try it. +It is a temporary live demo, all data will be deleted after 10 minutes. The server is located at Tokyo, so if you live far from there it may affact your experience. I suggest that you should install and try it out for the best demo experience. VPS is sponsored by Uptime Kuma sponsors on [Open Collective](https://opencollective.com/uptime-kuma)! Thank you so much! @@ -25,7 +25,7 @@ VPS is sponsored by Uptime Kuma sponsors on [Open Collective](https://opencollec * Monitoring uptime for HTTP(s) / TCP / Ping / DNS Record / Push. * Fancy, Reactive, Fast UI/UX. * Notifications via Telegram, Discord, Gotify, Slack, Pushover, Email (SMTP), and [70+ notification services, click here for the full list](https://github.com/louislam/uptime-kuma/tree/master/src/components/notifications). -* 20 seconds interval. +* 20 second intervals. * [Multi Languages](https://github.com/louislam/uptime-kuma/tree/master/src/languages) * Simple Status Page * Ping Chart @@ -40,7 +40,7 @@ docker volume create uptime-kuma docker run -d --restart=always -p 3001:3001 -v uptime-kuma:/app/data --name uptime-kuma louislam/uptime-kuma:1 ``` -Browse to http://localhost:3001 after started. +Browse to http://localhost:3001 after starting. ### 💪🏻 Without Docker @@ -58,11 +58,11 @@ npm run setup node server/server.js # (Recommended) Option 2. Run in background using PM2 -# Install PM2 if you don't have: npm install pm2 -g +# Install PM2 if you don't have it: npm install pm2 -g pm2 start server/server.js --name uptime-kuma ``` -Browse to http://localhost:3001 after started. +Browse to http://localhost:3001 after starting. ### Advanced Installation @@ -124,7 +124,7 @@ You can discuss or ask for help in [Issues](https://github.com/louislam/uptime-k ### Subreddit My Reddit account: louislamlam -You can mention me if you ask question on Reddit. +You can mention me if you ask a question on Reddit. https://www.reddit.com/r/UptimeKuma/ ## Contribute From 4cd66b20b166be346b6fbc3eda50cebfc4237b0d Mon Sep 17 00:00:00 2001 From: "J. Eckert" Date: Sun, 17 Oct 2021 08:08:44 -0700 Subject: [PATCH 082/121] Update README.md additional spelling correction --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6b842080..6caa1a85 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ Try it! https://demo.uptime.kuma.pet -It is a temporary live demo, all data will be deleted after 10 minutes. The server is located at Tokyo, so if you live far from there it may affact your experience. I suggest that you should install and try it out for the best demo experience. +It is a temporary live demo, all data will be deleted after 10 minutes. The server is located at Tokyo, so if you live far from there it may affect your experience. I suggest that you should install and try it out for the best demo experience. VPS is sponsored by Uptime Kuma sponsors on [Open Collective](https://opencollective.com/uptime-kuma)! Thank you so much! From c622f7958fe5ee42240de415466916beb0375f93 Mon Sep 17 00:00:00 2001 From: Juan Calderon-Perez <835733+gaby@users.noreply.github.com> Date: Sun, 17 Oct 2021 17:16:07 -0400 Subject: [PATCH 083/121] Add support for closing stale Issues/PR --- .github/workflows/stale-bot | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .github/workflows/stale-bot diff --git a/.github/workflows/stale-bot b/.github/workflows/stale-bot new file mode 100644 index 00000000..2d0b82cb --- /dev/null +++ b/.github/workflows/stale-bot @@ -0,0 +1,17 @@ +name: 'Automatically close stale issues and PRs' +on: + schedule: + - cron: '0 0 * * *' +#Run once a day at midnight + +jobs: + stale: + runs-on: ubuntu-latest + steps: + - uses: actions/stale@v3 + with: + stale-issue-message: 'We are clearing up our old issues and your ticket has been open for 6 months with no activity. Remove stale label or comment or this will be closed in 15 days.' + days-before-stale: 180 + days-before-close: 15 + exempt-issue-labels: 'News,Medium,High,discussion,bug,doc,' + operations-per-run: 500 From 7cb25255bfddce6bed333fcbdafda2e21a38dfb3 Mon Sep 17 00:00:00 2001 From: Juan Calderon-Perez <835733+gaby@users.noreply.github.com> Date: Sun, 17 Oct 2021 17:20:32 -0400 Subject: [PATCH 084/121] Update stale-bot --- .github/workflows/stale-bot | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/stale-bot b/.github/workflows/stale-bot index 2d0b82cb..6dbcbb23 100644 --- a/.github/workflows/stale-bot +++ b/.github/workflows/stale-bot @@ -10,8 +10,14 @@ jobs: steps: - uses: actions/stale@v3 with: - stale-issue-message: 'We are clearing up our old issues and your ticket has been open for 6 months with no activity. Remove stale label or comment or this will be closed in 15 days.' + stale-issue-message: 'We are clearing up our old issues and your ticket has been open for 6 months with no activity. Remove stale label or comment or this will be closed in 7 days.' + stale-pr-message: ''We are clearing up our old Pull Requests and yours has been open for 6 months with no activity. Remove stale label or comment or this will be closed in 7 days.' + close-issue-message: 'This issue was closed because it has been stalled for 7 days with no activity.' + close-pr-message: 'This PR was closed because it has been stalled for 7 days with no activity.' days-before-stale: 180 - days-before-close: 15 + days-before-close: 7 exempt-issue-labels: 'News,Medium,High,discussion,bug,doc,' + exempt-pr-labels: 'awaiting-approval,work-in-progress,enhancement,' operations-per-run: 500 + exempt-issue-assignees: 'louislam' + exempt-pr-assignees: 'louislam' From 2a3ce15328fb1cccef04d9a7d7d10a4fb50dd04f Mon Sep 17 00:00:00 2001 From: Juan Calderon-Perez <835733+gaby@users.noreply.github.com> Date: Sun, 17 Oct 2021 17:23:44 -0400 Subject: [PATCH 085/121] Use default number of operations per day. Defaults to 30 --- .github/workflows/stale-bot | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/stale-bot b/.github/workflows/stale-bot index 6dbcbb23..5992b93e 100644 --- a/.github/workflows/stale-bot +++ b/.github/workflows/stale-bot @@ -18,6 +18,5 @@ jobs: days-before-close: 7 exempt-issue-labels: 'News,Medium,High,discussion,bug,doc,' exempt-pr-labels: 'awaiting-approval,work-in-progress,enhancement,' - operations-per-run: 500 exempt-issue-assignees: 'louislam' exempt-pr-assignees: 'louislam' From 11a1f35cc5cc1743de60b2bc4fba33a0109686eb Mon Sep 17 00:00:00 2001 From: Andreas Brett Date: Mon, 18 Oct 2021 01:06:20 +0200 Subject: [PATCH 086/121] independent csprng solution --- src/util.js | 87 +++++++++++++++++++++++++++++++++++++++-------------- src/util.ts | 66 +++++++++++++++++++++++++++++++++++----- 2 files changed, 122 insertions(+), 31 deletions(-) diff --git a/src/util.js b/src/util.js index b8ee76d9..df54cf2e 100644 --- a/src/util.js +++ b/src/util.js @@ -6,11 +6,10 @@ // // Backend uses the compiled file util.js // Frontend uses util.ts -Object.defineProperty(exports, "__esModule", { value: true }); -exports.getMonitorRelativeURL = exports.genSecret = exports.getRandomInt = exports.getRandomArbitrary = exports.TimeLogger = exports.polyfill = exports.debug = exports.ucfirst = exports.sleep = exports.flipStatus = exports.STATUS_PAGE_PARTIAL_DOWN = exports.STATUS_PAGE_ALL_UP = exports.STATUS_PAGE_ALL_DOWN = exports.PENDING = exports.UP = exports.DOWN = exports.appName = exports.isDev = void 0; -const _dayjs = require("dayjs"); -const dayjs = _dayjs; -const crypto = require("crypto").webcrypto; +exports.__esModule = true; +exports.getMonitorRelativeURL = exports.genSecret = exports.getCryptoRandomInt = exports.getRandomInt = exports.getRandomArbitrary = exports.TimeLogger = exports.polyfill = exports.debug = exports.ucfirst = exports.sleep = exports.flipStatus = exports.STATUS_PAGE_PARTIAL_DOWN = exports.STATUS_PAGE_ALL_UP = exports.STATUS_PAGE_ALL_DOWN = exports.PENDING = exports.UP = exports.DOWN = exports.appName = exports.isDev = void 0; +var _dayjs = require("dayjs"); +var dayjs = _dayjs; exports.isDev = process.env.NODE_ENV === "development"; exports.appName = "Uptime Kuma"; exports.DOWN = 0; @@ -30,7 +29,7 @@ function flipStatus(s) { } exports.flipStatus = flipStatus; function sleep(ms) { - return new Promise(resolve => setTimeout(resolve, ms)); + return new Promise(function (resolve) { return setTimeout(resolve, ms); }); } exports.sleep = sleep; /** @@ -41,7 +40,7 @@ function ucfirst(str) { if (!str) { return str; } - const firstLetter = str.substr(0, 1); + var firstLetter = str.substr(0, 1); return firstLetter.toUpperCase() + str.substr(1); } exports.ucfirst = ucfirst; @@ -70,16 +69,17 @@ function polyfill() { } } exports.polyfill = polyfill; -class TimeLogger { - constructor() { +var TimeLogger = /** @class */ (function () { + function TimeLogger() { this.startTime = dayjs().valueOf(); } - print(name) { + TimeLogger.prototype.print = function (name) { if (exports.isDev && process.env.TIMELOGGER === "1") { console.log(name + ": " + (dayjs().valueOf() - this.startTime) + "ms"); } - } -} + }; + return TimeLogger; +}()); exports.TimeLogger = TimeLogger; /** * Returns a random number between min (inclusive) and max (exclusive) @@ -103,20 +103,61 @@ function getRandomInt(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; } exports.getRandomInt = getRandomInt; +/** + * Returns either the NodeJS crypto.randomBytes() function or its + * browser equivalent implemented via window.crypto.getRandomValues() + */ +var getRandomBytes = ((typeof window !== 'undefined' && window.crypto) + // Browsers + ? function () { + return function (numBytes) { + var randomBytes = new Uint8Array(numBytes); + for (var i = 0; i < numBytes; i += 65536) { + window.crypto.getRandomValues(randomBytes.subarray(i, i + Math.min(numBytes - i, 65536))); + } + return randomBytes; + }; + } + // Node + : function () { + return require("crypto").randomBytes; + })(); function getCryptoRandomInt(min, max) { - const randomBuffer = new Uint32Array(1); - crypto.getRandomValues(randomBuffer); - const randomNumber = randomBuffer[0] / (0xffffffff + 1); - min = Math.ceil(min); - max = Math.floor(max); - return Math.floor(randomNumber * (max - min + 1)) + min; + // synchronous version of: https://github.com/joepie91/node-random-number-csprng + var range = max - min; + if (range >= Math.pow(2, 32)) + console.log("Warning! Range is too large."); + var tmpRange = range; + var bitsNeeded = 0; + var bytesNeeded = 0; + var mask = 1; + while (tmpRange > 0) { + if (bitsNeeded % 8 === 0) + bytesNeeded += 1; + bitsNeeded += 1; + mask = mask << 1 | 1; + tmpRange = tmpRange >>> 1; + } + var randomBytes = getRandomBytes(bytesNeeded); + var randomValue = 0; + for (var i = 0; i < bytesNeeded; i++) { + randomValue |= randomBytes[i] << 8 * i; + } + randomValue = randomValue & mask; + if (randomValue <= range) { + return min + randomValue; + } + else { + return getCryptoRandomInt(min, max); + } } exports.getCryptoRandomInt = getCryptoRandomInt; -function genSecret(length = 64) { - let secret = ""; - const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; - const charsLength = chars.length; - for ( let i = 0; i < length; i++ ) { +function genSecret(length) { + if (length === void 0) { length = 64; } + var secret = ""; + var chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + var charsLength = chars.length; + for (var i = 0; i < length; i++) { secret += chars.charAt(getCryptoRandomInt(0, charsLength - 1)); } return secret; diff --git a/src/util.ts b/src/util.ts index 259ff6e5..633d933e 100644 --- a/src/util.ts +++ b/src/util.ts @@ -8,7 +8,6 @@ import * as _dayjs from "dayjs"; const dayjs = _dayjs; -const crypto = require("crypto").webcrypto; export const isDev = process.env.NODE_ENV === "development"; export const appName = "Uptime Kuma"; @@ -115,13 +114,64 @@ export function getRandomInt(min: number, max: number) { return Math.floor(Math.random() * (max - min + 1)) + min; } -export function getCryptoRandomInt(min: number, max: number) { - const randomBuffer = new Uint32Array(1); - crypto.getRandomValues(randomBuffer); - const randomNumber = randomBuffer[0] / (0xffffffff + 1); - min = Math.ceil(min); - max = Math.floor(max); - return Math.floor(randomNumber * (max - min + 1)) + min; +/** + * Returns either the NodeJS crypto.randomBytes() function or its + * browser equivalent implemented via window.crypto.getRandomValues() + */ +let getRandomBytes = ( + (typeof window !== 'undefined' && window.crypto) + + // Browsers + ? function () { + return (numBytes: number) => { + let randomBytes = new Uint8Array(numBytes); + for (let i = 0; i < numBytes; i += 65536) { + window.crypto.getRandomValues(randomBytes.subarray(i, i + Math.min(numBytes - i, 65536))); + } + return randomBytes; + }; + } + + // Node + : function() { + return require("crypto").randomBytes; + } +)(); + +export function getCryptoRandomInt(min: number, max: number):number { + + // synchronous version of: https://github.com/joepie91/node-random-number-csprng + + const range = max - min + if (range >= Math.pow(2, 32)) + console.log("Warning! Range is too large.") + + let tmpRange = range + let bitsNeeded = 0 + let bytesNeeded = 0 + let mask = 1 + + while (tmpRange > 0) { + if (bitsNeeded % 8 === 0) bytesNeeded += 1 + bitsNeeded += 1 + mask = mask << 1 | 1 + tmpRange = tmpRange >>> 1 + } + + const randomBytes = getRandomBytes(bytesNeeded) + let randomValue = 0 + + for (let i = 0; i < bytesNeeded; i++) { + randomValue |= randomBytes[i] << 8 * i + } + + randomValue = randomValue & mask; + + if (randomValue <= range) { + return min + randomValue + } else { + return getCryptoRandomInt(min, max) + } } export function genSecret(length = 64) { From 20d59e5a132eac076818dd19a6a0f06c2bc324b5 Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Mon, 18 Oct 2021 17:02:05 +0800 Subject: [PATCH 087/121] fix and move the steam api key to settings page --- db/patch-add-apikey-monitor.sql | 7 ------- server/database.js | 3 +-- server/model/monitor.js | 26 +++++++++++--------------- src/languages/en.js | 2 +- src/pages/EditMonitor.vue | 10 ++++++---- src/pages/Settings.vue | 10 ++++++++++ 6 files changed, 29 insertions(+), 29 deletions(-) delete mode 100644 db/patch-add-apikey-monitor.sql diff --git a/db/patch-add-apikey-monitor.sql b/db/patch-add-apikey-monitor.sql deleted file mode 100644 index 1a30bdf3..00000000 --- a/db/patch-add-apikey-monitor.sql +++ /dev/null @@ -1,7 +0,0 @@ --- You should not modify if this have pushed to Github, unless it does serious wrong with the db. -BEGIN TRANSACTION; - -ALTER TABLE monitor - ADD apikey VARCHAR(64) default '' not null; - -COMMIT; diff --git a/server/database.js b/server/database.js index 14819101..1030ffdd 100644 --- a/server/database.js +++ b/server/database.js @@ -46,7 +46,6 @@ class Database { "patch-improve-performance.sql": true, "patch-2fa.sql": true, "patch-add-retry-interval-monitor.sql": true, - "patch-add-apikey-monitor.sql": true, "patch-incident-table.sql": true, "patch-group-table.sql": true, "patch-monitor-push_token.sql": true, @@ -54,7 +53,7 @@ class Database { } /** - * The finally version should be 10 after merged tag feature + * The final version should be 10 after merged tag feature * @deprecated Use patchList for any new feature */ static latestVersion = 10; diff --git a/server/model/monitor.js b/server/model/monitor.js index 589c198e..6d699258 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 { debug, UP, DOWN, PENDING, flipStatus, TimeLogger } = require("../../src/util"); -const { tcping, ping, dnsResolve, checkCertificate, checkStatusCode, getTotalClientInRoom } = require("../util-server"); +const { tcping, ping, dnsResolve, checkCertificate, checkStatusCode, getTotalClientInRoom, setting } = require("../util-server"); const { R } = require("redbean-node"); const { BeanModel } = require("redbean-node/dist/bean-model"); const { Notification } = require("../notification"); @@ -292,26 +292,22 @@ class Monitor extends BeanModel { }, params: { filter: filter, - key: this.apikey, + key: await setting("steamAPIKey"), } }); - bean.msg = `${res.status} - ${res.statusText}`; - bean.ping = await ping(this.hostname); - - let data = res.data; - - // Convert to string for object/array - if (typeof data !== "string") { - data = JSON.stringify(data); - } - - if (data.includes(`${this.hostname}:${this.port}`)) { - bean.msg += ", server is found"; + if (res.data.response && res.data.response.servers && res.data.response.servers.length > 0) { bean.status = UP; + bean.msg = res.data.response.servers[0].name; + + try { + bean.ping = await ping(this.hostname); + } catch (_) { } + } else { - throw new Error(bean.msg + ", but server is not found"); + throw new Error("Server not found on Steam"); } + } else { bean.msg = "Unknown Monitor Type"; bean.status = PENDING; diff --git a/src/languages/en.js b/src/languages/en.js index 53bda1bd..4542d72b 100644 --- a/src/languages/en.js +++ b/src/languages/en.js @@ -304,5 +304,5 @@ export default { records: "records", "One record": "One record", "Showing {from} to {to} of {count} records": "Showing {from} to {to} of {count} records", - steamApiKeyDescription: "For monitoring a Steam Gameserver you need a steam Web-API key. You can register your api key here: https://steamcommunity.com/dev", + steamApiKeyDescription: "For monitoring a Steam Gameserver you need a steam Web-API key. You can register your api key here: ", }; diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue index dfeef93f..2a53be7c 100644 --- a/src/pages/EditMonitor.vue +++ b/src/pages/EditMonitor.vue @@ -67,18 +67,20 @@ - -
+ +
- -
+ + +
+