From b67b4d5afd2e7c3eb866fec0dad030fcc6719832 Mon Sep 17 00:00:00 2001 From: Denis Freund Date: Mon, 27 Sep 2021 11:17:57 +0200 Subject: [PATCH 001/163] 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 000000000..1a30bdf30 --- /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 2f6c1c5fe..c0b6bc1db 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 9a80225e0..e484ccec0 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 f5a8b16e3..fb8db1f7c 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 75d8f30c3..6b5dadf37 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 84231b1ae..0a68a34a9 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/163] 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 0a68a34a9..e5c793260 100644 --- a/src/pages/EditMonitor.vue +++ b/src/pages/EditMonitor.vue @@ -50,12 +50,18 @@
- -
+ +
+ +
+ + +
+
From 13cf6891acf99e55fdb10bb769f651df5c19eb44 Mon Sep 17 00:00:00 2001 From: Andreas Brett Date: Sun, 10 Oct 2021 21:58:23 +0200 Subject: [PATCH 003/163] 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 6e911998d..22279a7d1 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 004/163] 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 22279a7d1..6fdfc3bef 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 e127e168b6b6295b38dc32e722466a61f94fa4cb Mon Sep 17 00:00:00 2001 From: Andreas Brett Date: Sun, 10 Oct 2021 22:15:42 +0200 Subject: [PATCH 005/163] typed parameters --- src/util.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util.ts b/src/util.ts index 6fdfc3bef..8ba537380 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 006/163] typo --- src/util.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util.ts b/src/util.ts index 8ba537380..6f058eedd 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 007/163] const --- src/util.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util.ts b/src/util.ts index 6f058eedd..205589fef 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 008/163] 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 7fb50c5b0..265ed51db 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 dc1de50a02d7f4ea3205f179b60495be23403692 Mon Sep 17 00:00:00 2001 From: Andreas Brett Date: Mon, 11 Oct 2021 01:18:33 +0200 Subject: [PATCH 009/163] 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 265ed51db..8135f2a08 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 205589fef..a1f6f2594 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 dc805cff975d16145fb56c7783f1665c1f8c1d11 Mon Sep 17 00:00:00 2001 From: Phuong Nguyen Minh <73861816+mrphuongbn@users.noreply.github.com> Date: Wed, 13 Oct 2021 13:36:09 +0700 Subject: [PATCH 010/163] Add files via upload --- src/languages/vi.js | 285 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 285 insertions(+) create mode 100644 src/languages/vi.js diff --git a/src/languages/vi.js b/src/languages/vi.js new file mode 100644 index 000000000..dab84c790 --- /dev/null +++ b/src/languages/vi.js @@ -0,0 +1,285 @@ +export default { + languageName: "Vietnamese", + checkEverySecond: "Kiểm tra mỗi {0} giây.", + retryCheckEverySecond: "Thử lại mỗi {0} giây.", + retriesDescription: "Số lần thử lại tối đa trước khi dịch vụ được đánh dấu là down và gửi thông báo.", + ignoreTLSError: "Bỏ qua lỗi TLS/SSL với các web HTTPS.", + upsideDownModeDescription: "Trạng thái đảo ngược, nếu dịch vụ có thể truy cập được nghĩa là DOWN.", + maxRedirectDescription: "Số lần chuyển hướng (redirect) tối đa. Đặt thành 0 để tắt chuyển hướng", + acceptedStatusCodesDescription: "Chọn mã code trạng thái được coi là phản hồi thành công.", + passwordNotMatchMsg: "Mật khẩu nhập lại không khớp.", + notificationDescription: "Vui lòng chỉ định một kênh thông báo.", + keywordDescription: "Từ khoá tìm kiếm phản hồi ở dạng html hoặc JSON, có phân biệt chữ HOA - thường", + pauseDashboardHome: "Tạm dừng", + deleteMonitorMsg: "Bạn chắc chắn muốn xóa monitor này chứ?", + deleteNotificationMsg: "Bạn có chắc chắn muốn xóa kênh thông báo này cho tất cả monitor?", + resoverserverDescription: "Cloudflare là máy chủ mặc định, bạn có thể thay đổi bất cứ lúc nào.", + rrtypeDescription: "Hãy chọn RR-Type mà bạn muốn giám sát", + pauseMonitorMsg: "Bạn chắc chắn muốn tạm dừng chứ?", + enableDefaultNotificationDescription: "Bật làm mặc định cho mọi monitor mới về sau. Bạn vẫn có thể tắt thông báo riêng cho từng monitor.", + clearEventsMsg: "Bạn chắc chắn muốn xoá TẤT CẢ sự kiện cho monitor này chứ?", + clearHeartbeatsMsg: "Bạn chắc chắn muốn xoá TẤT CẢ heartbeats cho monitor này chứ?", + confirmClearStatisticsMsg: "Bạn chắc chắn muốn xoá TẤT CẢ số liệu thống kê?", + importHandleDescription: "Chọn 'Skip existing' nếu bạn muốn bỏ qua mọi monitor và kênh thông báo trùng tên. 'Overwrite' sẽ ghi đè lên tất cả các monitor và kênh thông báo.", + confirmImportMsg: "Bạn có chắc chắn muốn khôi phục bản bản sao lưu này không?.", + twoFAVerifyLabel: "Vui lòng nhập mã token của bạn để xác minh rằng 2FA đang hoạt động", + tokenValidSettingsMsg: "Mã token hợp lệ! Bạn có thể lưu cài đặt 2FA bây giờ.", + confirmEnableTwoFAMsg: "Bạn chắc chắn muốn bật 2FA chứ?", + confirmDisableTwoFAMsg: "Bạn chắc chắn muốn tắt 2FA chứ?", + Settings: "Cài đặt", + Dashboard: "Dashboard", + "New Update": "Bản cập nhật mới", + Language: "Ngôn ngữ", + Appearance: "Giao diện", + Theme: "Theme", + General: "Chung", + Version: "Phiên bản", + "Check Update On GitHub": "Kiểm tra bản cập nhật mới trên GitHub", + List: "List", + Add: "Thêm", + "Add New Monitor": "Thêm mới Monitor", + "Quick Stats": "Thống kê nhanh", + Up: "Lên", + Down: "Xuống", + Pending: "Chờ xử lý", + Unknown: "Không xác định", + Pause: "Tạm dừng", + Name: "Tên", + Status: "Trạng thái", + DateTime: "Ngày tháng", + Message: "Tin nhắn", + "No important events": "Không có sự kiện quan trọng nào", + Resume: "Khôi phục", + Edit: "Sửa", + Delete: "Xoá", + Current: "Hiện tại", + Uptime: "Uptime", + "Cert Exp.": "Cert hết hạn", + days: "ngày", + day: "ngày", + "-day": "-ngày", + hour: "giờ", + "-hour": "-giờ", + Response: "Phản hồi", + Ping: "Ping", + "Monitor Type": "Kiểu monitor", + Keyword: "Từ khoá", + "Friendly Name": "Tên dễ hiểu", + URL: "URL", + Hostname: "Hostname", + Port: "Port", + "Heartbeat Interval": "Tần suất heartbeat", + Retries: "Thử lại", + "Heartbeat Retry Interval": "Tần suất thử lại của Heartbeat", + Advanced: "Nâng cao", + "Upside Down Mode": "Trạng thái đảo ngược", + "Max. Redirects": "Chuyển hướng tối đa", + "Accepted Status Codes": "Codes trạng thái chấp nhận", + Save: "Lưu", + Notifications: "Thông báo", + "Not available, please setup.": "Chưa sẵn sàng, hãy cài đặt.", + "Setup Notification": "Cài đặt thông báo", + Light: "Sáng", + Dark: "Tối", + Auto: "Tự động", + "Theme - Heartbeat Bar": "Theme - Heartbeat Bar", + Normal: "Bình thường", + Bottom: "Dưới", + None: "Không có", + Timezone: "Múi giờ", + "Search Engine Visibility": "Hiển thị với các công cụ tìm kiếm", + "Allow indexing": "Cho phép indexing", + "Discourage search engines from indexing site": "Ngăn chặn các công cụ tìm kiếm indexing trang", + "Change Password": "Thay đổi mật khẩu", + "Current Password": "Mật khẩu hiện tại", + "New Password": "Mật khẩu mới", + "Repeat New Password": "Lặp lại mật khẩu mới", + "Update Password": "Cập nhật mật khẩu", + "Disable Auth": "Tắt xác minh", + "Enable Auth": "Bật xác minh", + Logout: "Đăng xuất", + Leave: "Rời", + "I understand, please disable": "Tôi hiểu, làm ơn hãy tắt!", + Confirm: "Xác nhận", + Yes: "Có", + No: "Không", + Username: "Tài khoản", + Password: "Mật khẩu", + "Remember me": "Lưu phiên đăng nhập", + Login: "Đăng nhập", + "No Monitors, please": "Không có monitor nào", + "add one": "Thêm mới", + "Notification Type": "Kiểu thông báo", + Email: "Email", + Test: "Thử", + "Certificate Info": "Thông tin Certificate", + "Resolver Server": "Máy chủ Resolver", + "Resource Record Type": "Loại bản ghi", + "Last Result": "Kết quả cuối cùng", + "Create your admin account": "Tạo tài khoản quản trị", + "Repeat Password": "Lặp lại mật khẩu", + "Import Backup": "Khôi phục bản sao lưu", + "Export Backup": "Xuất bản sao lưu", + Export: "Xuất", + Import: "Khôi phục", + respTime: "Thời gian phản hồi (ms)", + notAvailableShort: "N/A", + "Default enabled": "Mặc định bật", + "Apply on all existing monitors": "Áp dụng cho tất cả monitor đang có", + Create: "Tạo", + "Clear Data": "Xoá dữ liệu", + Events: "Sự kiện", + Heartbeats: "Heartbeats", + "Auto Get": "Tự động lấy", + backupDescription: "Bạn có thể sao lưu tất cả các màn hình và tất cả các thông báo vào một file JSON.", + backupDescription2: "PS: Không bao gồm dữ liệu lịch sử các sự kiện.", + backupDescription3: "Hãy lưu giữ file này cẩn thận vì trong file đó chứa cả các mã token thông báo.", + alertNoFile: "Hãy chọn file để khôi phục.", + alertWrongFileType: "Hãy chọn file JSON.", + "Clear all statistics": "Xoá tất cả thống kê", + "Skip existing": "Skip existing", + Overwrite: "Ghi đè", + Options: "Tuỳ chọn", + "Keep both": "Giữ lại cả hai", + "Verify Token": "Xác minh Token", + "Setup 2FA": "Cài đặt 2FA", + "Enable 2FA": "Bật 2FA", + "Disable 2FA": "Tắt 2FA", + "2FA Settings": "Cài đặt 2FA", + "Two Factor Authentication": "Xác thực hai yếu tố", + Active: "Hoạt động", + Inactive: "Ngừng hoạt động", + Token: "Token", + "Show URI": "Hiển thị URI", + Tags: "Tags", + "Add New below or Select...": "Thêm mới ở dưới hoặc Chọn...", + "Tag with this name already exist.": "Tag với tên đã tồn tại.", + "Tag with this value already exist.": "Tag với value đã tồn tại.", + color: "Màu sắc", + "value (optional)": "Value (tuỳ chọn)", + Gray: "Xám", + Red: "Đỏ", + Orange: "Cam", + Green: "Xanh lá", + Blue: "Xanh da trời", + Indigo: "Chàm", + Purple: "Tím", + Pink: "Hồng", + "Search...": "Tìm kiếm...", + "Avg. Ping": "Ping Trung bình", + "Avg. Response": "Phản hồi trung bình", + "Entry Page": "Entry Page", + statusPageNothing: "Không có gì, hãy thêm nhóm monitor hoặc monitor.", + "No Services": "Không có dịch vụ", + "All Systems Operational": "Tất cả các hệ thống hoạt động", + "Partially Degraded Service": "Dịch vụ xuống cấp một phần", + "Degraded Service": "Degraded Service", + "Add Group": "Thêm nhóm", + "Add a monitor": "Thêm monitor", + "Edit Status Page": "Sửa trang trạng thái", + "Go to Dashboard": "Đi tới Dashboard", + "Status Page": "Trang trạng thái", + // Start notification form + defaultNotificationName: "My {notification} Alerts ({number})", + here: "tại đây", + "Required": "Bắt buộc", + "telegram": "Telegram", + "Bot Token": "Bot Token", + "You can get a token from": "Bạn có thể lấy mã token từ", + "Chat ID": "Chat ID", + supportTelegramChatID: "Hỗ trợ chat trực tiếp / Nhóm / Kênh Chat ID", + wayToGetTelegramChatID: "Bạn có thể lấy chat id của mình bằng cách gửi tin nhắn tới bot và truy cập url này để xem chat_id:", + "YOUR BOT TOKEN HERE": "MÃ BOT TOKEN CỦA BẠN", + chatIDNotFound: "Không tìm thấy Chat ID, vui lòng gửi tin nhắn cho bot này trước", + "webhook": "Webhook", + "Post URL": "URL đăng", + "Content Type": "Loại nội dung", + webhookJsonDesc: "{0} phù hợp với bất kỳ máy chủ http hiện đại nào như express.js", + webhookFormDataDesc: "{multipart} phù hợp với PHP, bạn chỉ cần phân tích cú pháp json bằng {decodeFunction}", + "smtp": "Email (SMTP)", + secureOptionNone: "None / STARTTLS (25, 587)", + secureOptionTLS: "TLS (465)", + "Ignore TLS Error": "Bỏ qua lỗi TLS", + "From Email": "Từ Email", + "To Email": "Tới Email", + smtpCC: "CC", + smtpBCC: "BCC", + "discord": "Discord", + "Discord Webhook URL": "Discord Webhook URL", + wayToGetDiscordURL: "Để lấy Discord, hãy vào: Server Settings -> Integrations -> Create Webhook", + "Bot Display Name": "Tên hiển thị của Bot", + "Prefix Custom Message": "Tiền tố tin nhắn tuỳ chọn", + "Hello @everyone is...": "Xin chào {'@'} mọi người đang...", + "teams": "Microsoft Teams", + "Webhook URL": "Webhook URL", + wayToGetTeamsURL: "Bạn có thể học cách tạo webhook url {0}.", + "signal": "Signal", + "Number": "Số", + "Recipients": "Người nhận", + needSignalAPI: "Bạn cần một tín hiệu client với REST API.", + wayToCheckSignalURL: "Bạn có thể kiểm tra url này để xem cách thiết lập:", + signalImportant: "QUAN TRỌNG: Bạn không thể kết hợp các nhóm và số trong người nhận!", + "gotify": "Gotify", + "Application Token": "Mã Token ứng dụng", + "Server URL": "URL máy chủ", + "Priority": "Mức ưu tiên", + "slack": "Slack", + "Icon Emoji": "Icon Emoji", + "Channel Name": "Tên Channel", + "Uptime Kuma URL": "Uptime Kuma URL", + aboutWebhooks: "Thông tin thêm về webhook trên: {0}", + aboutChannelName: "Nhập tên kênh trên {0} trường Channel Name nếu bạn muốn bỏ qua kênh webhook. vd: #other-channel", + aboutKumaURL: "Nếu bạn để trống trường Uptime Kuma URL, mặc định sẽ là trang Project Github.", + emojiCheatSheet: "Bảng tra cứu Emoji: {0}", + "rocket.chat": "Rocket.chat", + pushover: "Pushover", + pushy: "Pushy", + octopush: "Octopush", + promosms: "PromoSMS", + lunasea: "LunaSea", + apprise: "Thông báo (Hỗ trợ 50+ dịch vụ thông báo)", + pushbullet: "Pushbullet", + line: "Line Messenger", + mattermost: "Mattermost", + "User Key": "User Key", + "Device": "Thiết bị", + "Message Title": "Tiêu đề tin nhắn", + "Notification Sound": "Âm thanh thông báo", + "More info on:": "Thông tin chi tiết tại: {0}", + pushoverDesc1: "Mức ưu tiên khẩn cấp (2) có thời gian chờ mặc định là 30 giây giữa các lần thử lại và sẽ hết hạn sau 1 giờ.", + pushoverDesc2: "Nếu bạn muốn gửi thông báo đến các thiết bị khác nhau, hãy điền vào trường Thiết bị.", + "SMS Type": "SMS Type", + octopushTypePremium: "Premium (Nhanh - Khuyến nghị nên dùng cho cảnh báo)", + octopushTypeLowCost: "Giá rẻ (Chậm, thỉnh thoảng bị chặn)", + "Check octopush prices": "Kiểm tra giá octopush {0}.", + octopushPhoneNumber: "Số điện thoại (Định dạng intl, vd : +33612345678) ", + octopushSMSSender: "SMS người gửi : 3-11 ký tự chữ, số và dấu cách (a-zA-Z0-9)", + "LunaSea Device ID": "LunaSea ID thiết bị", + "Apprise URL": "URL thông báo", + "Example:": "Ví dụ: {0}", + "Read more:": "Đọc thêm: {0}", + "Status:": "Trạng thái: {0}", + "Read more": "Đọc thêm", + appriseInstalled: "Đã cài đặt Thông báo.", + appriseNotInstalled: "Chưa cài đặt Thông báo. {0}", + "Access Token": "Token truy cập", + "Channel access token": "Token kênh truy cập", + "Line Developers Console": "Line Developers Console", + lineDevConsoleTo: "Line Developers Console - {0}", + "Basic Settings": "Cài đặt cơ bản", + "User ID": "User ID", + "Messaging API": "Messaging API", + wayToGetLineChannelToken: "Trước tiên, hãy truy cập {0},tạo nhà cung cấp và kênh (Messaging API), sau đó bạn có thể nhận mã token truy cập kênh và id người dùng từ các mục menu được đề cập ở trên.", + "Icon URL": "Icon URL", + aboutIconURL: "Bạn có thể cung cấp liên kết đến ảnh trong \"Icon URL\" để ghi đè ảnh hồ sơ mặc định. Sẽ không được sử dụng nếu Biểu tượng cảm xúc được thiết lập.", + aboutMattermostChannelName: "Bạn có thể ghi đè kênh mặc định mà webhook đăng lên bằng cách nhập tên kênh vào trường \"Channel Name\". Điều này cần được bật trong cài đặt Mattermost webhook. Ví dụ: #other-channel", + "matrix": "Matrix", + promosmsTypeEco: "SMS ECO - rẻ nhưng chậm và thường xuyên quá tải. Chỉ dành cho người Ba Lan.", + promosmsTypeFlash: "SMS FLASH - Tin nhắn sẽ tự động hiển thị trên thiết bị của người nhận. Chỉ dành cho người Ba Lan.", + promosmsTypeFull: "SMS FULL - SMS cao cấp, Bạn có thể sử dụng Tên Người gửi (Bạn cần đăng ký tên trước). Đáng tin cậy cho các cảnh báo.", + promosmsTypeSpeed: "SMS SPEED - Ưu tiên cao nhất trong hệ thống. Rất nhanh chóng và đáng tin cậy nhưng tốn kém, (giá gấp đôi SMS FULL).", + promosmsPhoneNumber: "Số điện thoại (Bỏ qua mã vùng với người Ba Lan)", + promosmsSMSSender: "SMS Tên người gửi: Tên đã đăng ký trước hoặc tên mặc định: InfoSMS, SMS Info, MaxSMS, INFO, SMS", + "Feishu WebHookUrl": "Feishu WebHookUrl", + // End notification form +}; \ No newline at end of file From 89c0f8b7349d173ba262709ef8138cf3be057009 Mon Sep 17 00:00:00 2001 From: Phuong Nguyen Minh <73861816+mrphuongbn@users.noreply.github.com> Date: Wed, 13 Oct 2021 13:44:31 +0700 Subject: [PATCH 011/163] Update i18n.js --- src/i18n.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/i18n.js b/src/i18n.js index b95e32d00..7f55a0610 100644 --- a/src/i18n.js +++ b/src/i18n.js @@ -21,9 +21,11 @@ import sr from "./languages/sr"; import srLatn from "./languages/sr-latn"; import svSE from "./languages/sv-SE"; import trTR from "./languages/tr-TR"; +import vi from "./languages/vi"; import zhCN from "./languages/zh-CN"; import zhHK from "./languages/zh-HK"; + const languageList = { en, "zh-HK": zhHK, @@ -49,6 +51,7 @@ const languageList = { "zh-CN": zhCN, "pl": pl, "et-EE": etEE, + "vi": vi, }; const rtlLangs = ["fa"]; From 86dcc9bc8fc39faa44949c52ad74d9831dfedfb4 Mon Sep 17 00:00:00 2001 From: Andreas Brett Date: Wed, 13 Oct 2021 17:34:56 +0200 Subject: [PATCH 012/163] import webcrypto lib --- src/util.js | 1 + src/util.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/src/util.js b/src/util.js index 8135f2a08..b8ee76d98 100644 --- a/src/util.js +++ b/src/util.js @@ -10,6 +10,7 @@ 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.isDev = process.env.NODE_ENV === "development"; exports.appName = "Uptime Kuma"; exports.DOWN = 0; diff --git a/src/util.ts b/src/util.ts index a1f6f2594..259ff6e57 100644 --- a/src/util.ts +++ b/src/util.ts @@ -8,6 +8,7 @@ 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"; From bda481c61eb603a8a7fe5bfd980ea129804978a5 Mon Sep 17 00:00:00 2001 From: Phuong Nguyen Minh <73861816+mrphuongbn@users.noreply.github.com> Date: Fri, 15 Oct 2021 08:26:33 +0700 Subject: [PATCH 013/163] Update vi.js --- src/languages/vi.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/languages/vi.js b/src/languages/vi.js index dab84c790..c47ebe024 100644 --- a/src/languages/vi.js +++ b/src/languages/vi.js @@ -255,13 +255,13 @@ export default { octopushPhoneNumber: "Số điện thoại (Định dạng intl, vd : +33612345678) ", octopushSMSSender: "SMS người gửi : 3-11 ký tự chữ, số và dấu cách (a-zA-Z0-9)", "LunaSea Device ID": "LunaSea ID thiết bị", - "Apprise URL": "URL thông báo", + "Apprise URL": "Apprise URL", "Example:": "Ví dụ: {0}", "Read more:": "Đọc thêm: {0}", "Status:": "Trạng thái: {0}", "Read more": "Đọc thêm", - appriseInstalled: "Đã cài đặt Thông báo.", - appriseNotInstalled: "Chưa cài đặt Thông báo. {0}", + appriseInstalled: "Đã cài đặt Apprise.", + appriseNotInstalled: "Chưa cài đặt Apprise. {0}", "Access Token": "Token truy cập", "Channel access token": "Token kênh truy cập", "Line Developers Console": "Line Developers Console", @@ -282,4 +282,4 @@ export default { promosmsSMSSender: "SMS Tên người gửi: Tên đã đăng ký trước hoặc tên mặc định: InfoSMS, SMS Info, MaxSMS, INFO, SMS", "Feishu WebHookUrl": "Feishu WebHookUrl", // End notification form -}; \ No newline at end of file +}; From 8cb26d2b31c0984a34a35cb35dd01b94b02c5674 Mon Sep 17 00:00:00 2001 From: NeuralMiner Date: Fri, 15 Oct 2021 19:12:09 -0600 Subject: [PATCH 014/163] Text update --- server/database.js | 20 ++++++++++---------- server/server.js | 13 ++++++++----- src/pages/EditMonitor.vue | 2 +- src/pages/Setup.vue | 2 +- 4 files changed, 20 insertions(+), 17 deletions(-) diff --git a/server/database.js b/server/database.js index 297df655a..4c7e53648 100644 --- a/server/database.js +++ b/server/database.js @@ -131,7 +131,7 @@ class Database { console.info("Latest database version: " + this.latestVersion); if (version === this.latestVersion) { - console.info("Database no need to patch"); + console.info("Database patch not needed"); } else if (version > this.latestVersion) { console.info("Warning: Database version is newer than expected"); } else { @@ -152,8 +152,8 @@ class Database { await Database.close(); console.error(ex); - console.error("Start Uptime-Kuma failed due to patch db failed"); - console.error("Please submit the bug report if you still encounter the problem after restart: https://github.com/louislam/uptime-kuma/issues"); + console.error("Start Uptime-Kuma failed due to issue patching the database"); + console.error("Please submit a bug report if you still encounter the problem after restart: https://github.com/louislam/uptime-kuma/issues"); this.restore(); process.exit(1); @@ -191,7 +191,7 @@ class Database { await Database.close(); console.error(ex); - console.error("Start Uptime-Kuma failed due to patch db failed"); + console.error("Start Uptime-Kuma failed due to issue patching the database"); console.error("Please submit the bug report if you still encounter the problem after restart: https://github.com/louislam/uptime-kuma/issues"); this.restore(); @@ -232,7 +232,7 @@ class Database { this.patched = true; await this.importSQLFile("./db/" + sqlFilename); databasePatchedFiles[sqlFilename] = true; - console.log(sqlFilename + " is patched successfully"); + console.log(sqlFilename + " was patched successfully"); } else { debug(sqlFilename + " is already patched, skip"); @@ -287,7 +287,7 @@ class Database { }; process.addListener("unhandledRejection", listener); - console.log("Closing DB"); + console.log("Closing the database"); while (true) { Database.noReject = true; @@ -297,7 +297,7 @@ class Database { if (Database.noReject) { break; } else { - console.log("Waiting to close the db"); + console.log("Waiting to close the database"); } } console.log("SQLite closed"); @@ -312,7 +312,7 @@ class Database { */ static backup(version) { if (! this.backupPath) { - console.info("Backup the db"); + console.info("Backup the database"); this.backupPath = this.dataDir + "kuma.db.bak" + version; fs.copyFileSync(Database.path, this.backupPath); @@ -335,7 +335,7 @@ class Database { */ static restore() { if (this.backupPath) { - console.error("Patch db failed!!! Restoring the backup"); + console.error("Patching the database failed!!! Restoring the backup"); const shmPath = Database.path + "-shm"; const walPath = Database.path + "-wal"; @@ -354,7 +354,7 @@ class Database { fs.unlinkSync(walPath); } } catch (e) { - console.log("Restore failed, you may need to restore the backup manually"); + console.log("Restore failed; you may need to restore the backup manually"); process.exit(1); } diff --git a/server/server.js b/server/server.js index 8d994d3b6..329484f56 100644 --- a/server/server.js +++ b/server/server.js @@ -1,7 +1,6 @@ console.log("Welcome to Uptime Kuma"); const args = require("args-parser")(process.argv); const { sleep, debug, getRandomInt, genSecret } = require("../src/util"); -const config = require("./config"); debug(args); @@ -9,6 +8,10 @@ if (! process.env.NODE_ENV) { process.env.NODE_ENV = "production"; } +// Demo Mode? +const demoMode = args["demo"] || false; +exports.demoMode = demoMode; + console.log("Node Env: " + process.env.NODE_ENV); console.log("Importing Node libraries"); @@ -81,7 +84,7 @@ const sslCert = process.env.UPTIME_KUMA_SSL_CERT || process.env.SSL_CERT || args */ const testMode = !!args["test"] || false; -if (config.demoMode) { +if (demoMode) { console.log("==== Demo Mode ===="); } @@ -440,7 +443,7 @@ exports.entryPage = "dashboard"; socket.on("setup", async (username, password, callback) => { try { if ((await R.count("user")) !== 0) { - throw new Error("Uptime Kuma has been setup. If you want to setup again, please delete the database."); + throw new Error("Uptime Kuma has been set up. If you want to setup again, please delete the database."); } let user = R.dispense("user"); @@ -1328,7 +1331,7 @@ async function initDatabase() { fs.copyFileSync(Database.templatePath, Database.path); } - console.log("Connecting to Database"); + console.log("Connecting to the Database"); await Database.connect(); console.log("Connected"); @@ -1428,7 +1431,7 @@ async function shutdownFunction(signal) { } function finalFunction() { - console.log("Graceful shutdown successfully!"); + console.log("Graceful shutdown successful!"); } gracefulShutdown(server, { diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue index b23490218..062582566 100644 --- a/src/pages/EditMonitor.vue +++ b/src/pages/EditMonitor.vue @@ -467,7 +467,7 @@ export default { return; } - // Beautiful the JSON format + // Beautify the JSON format if (this.monitor.body) { this.monitor.body = JSON.stringify(JSON.parse(this.monitor.body), null, 4); } diff --git a/src/pages/Setup.vue b/src/pages/Setup.vue index 5a4f836f5..ed8ea3efb 100644 --- a/src/pages/Setup.vue +++ b/src/pages/Setup.vue @@ -75,7 +75,7 @@ export default { this.processing = true; if (this.password !== this.repeatPassword) { - toast.error("Repeat password do not match."); + toast.error("Passwords do not match."); this.processing = false; return; } 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 015/163] 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 000000000..2d0b82cb7 --- /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 016/163] 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 2d0b82cb7..6dbcbb23a 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 017/163] 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 6dbcbb23a..5992b93ed 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 018/163] 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 b8ee76d98..df54cf2ec 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 259ff6e57..633d933ea 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 019/163] 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 1a30bdf30..000000000 --- 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 148191019..1030ffdd9 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 589c198e9..6d6992586 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 53bda1bd7..4542d72b2 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 dfeef93f6..2a53be7c0 100644 --- a/src/pages/EditMonitor.vue +++ b/src/pages/EditMonitor.vue @@ -67,18 +67,20 @@
- -
+ +
- -
+ + +
+