From a8b102ad4a0f8a146af04fd12e17adce7ff3662f Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 19 Jul 2021 18:23:06 +0200 Subject: [PATCH 01/31] add retries for pinging function backend: - new field for monitor: maxretries - new pending status while service is retrying: 2 - pending status event is not marked important - pending pings however register as downtime in the calculation frontend: - added pending status while service is retrying - added color for new pending status - added field to configure amount of retries database: - IMPORTANT: THIS REQUIRES MIGRATION!!!! - added field: maxretries with default value 0 --- .gitignore | 1 + db/kuma.db | Bin 57344 -> 61440 bytes server/model/monitor.js | 15 +++++++++++---- server/server.js | 1 + src/assets/vars.scss | 3 ++- src/components/HeartbeatBar.vue | 6 +++++- src/components/Status.vue | 6 +++++- src/components/Uptime.vue | 2 ++ src/mixins/socket.js | 5 +++++ src/pages/DashboardHome.vue | 2 ++ src/pages/EditMonitor.vue | 8 +++++++- 11 files changed, 41 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index 8d435974f..9caa313cb 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ dist-ssr /data !/data/.gitkeep +.vscode \ No newline at end of file diff --git a/db/kuma.db b/db/kuma.db index 07c93cf89abb94bac751a47894d5e0fff94aa17b..6e02ccc01fefc1d40029c18cbec08a6039cf723f 100644 GIT binary patch delta 1162 zcmd^9y>Hq;6t}@OU}Bu8Qcy*tT&yYrDQbX-p;K|;NZlHP%uyv1a>6Nb%U6s`5_KR$ zrHn0N>d=L?Q-!+s&*+%{phK8Cl-?O5l4k1Asgj?q`@PS5zxRCc&3|#_{}5YI7>1FE zh(uT-qruirLSY^~`7ZKPM&wi`(u+J4{o=LoShx=51Czj~fXX9IUHaY7oeG^fHJGA=#?SAyr3k7aBk> zA)r?^ZHwS7vx()?}&j!-7)P5J)GJN$Pam?mD+iN@p@|MV+?2 z@S~=OHZ~nxuhq$$WKF3>SV2m$Om(mE`DxoGSDs&N9;IoX+1Rly+;yvd(5G3;hFVmN zeeeSA$7!R0Qbi?ICw(nK9ntKCk^$9SeP%K5MFJSG01c?;U zpSiHiJRkc5>l_zZV~69-@O!qXNqm$rcw)8c_{^INk1F0JAKg!3n&4G7P+IAq;$Fd@8)myq~$1xGcFo zbGop9VV}$H$@ZTuWV4{aIo8P`Y^zy3c-X`@GqcOHur$Z9vTs)7?q{6*fs@U>Ait<2 zR>9LR#MRw3NJoK}OFI1xUs!gn0V828THM20&%AQ!C5!i&A10!W@H~ zJsg8Hj7&{6H#6}v39~T>Ffi~x-^``(fL~0Kv$Qz12 { console.log(`Monitor ${this.id}: Heartbeat`) @@ -109,12 +110,18 @@ class Monitor extends BeanModel { bean.status = 1; } + retries = 0; + } catch (error) { + if ((this.maxretries > 0) && (retries < this.maxretries)) { + retries++; + bean.status = 2; + } bean.msg = error.message; } - // Mark as important if status changed - if (! previousBeat || previousBeat.status !== bean.status) { + // Mark as important if status changed, ignore pending pings + if ((! previousBeat || previousBeat.status !== bean.status) && bean.status !== 2) { bean.important = true; // Do not send if first beat is UP @@ -233,7 +240,7 @@ class Monitor extends BeanModel { } total += value; - if (row.status === 0) { + if (row.status === 0 || row.status === 2) { downtime += value; } } diff --git a/server/server.js b/server/server.js index bd5894775..567ff490c 100644 --- a/server/server.js +++ b/server/server.js @@ -219,6 +219,7 @@ let needSetup = false; bean.url = monitor.url bean.interval = monitor.interval bean.hostname = monitor.hostname; + bean.maxretries = monitor.maxretries; bean.port = monitor.port; bean.keyword = monitor.keyword; diff --git a/src/assets/vars.scss b/src/assets/vars.scss index 31b0262d8..ebec378a5 100644 --- a/src/assets/vars.scss +++ b/src/assets/vars.scss @@ -1,7 +1,8 @@ $primary: #5CDD8B; $danger: #DC3545; +$warning: #f8a306; $link-color: #111; $border-radius: 50rem; $highlight: #7ce8a4; -$highlight-white: #e7faec; +$highlight-white: #e7faec; \ No newline at end of file diff --git a/src/components/HeartbeatBar.vue b/src/components/HeartbeatBar.vue index 48ffd2926..03cdceca6 100644 --- a/src/components/HeartbeatBar.vue +++ b/src/components/HeartbeatBar.vue @@ -3,7 +3,7 @@
span { - width: 45px; + width: 54px; } diff --git a/src/components/Uptime.vue b/src/components/Uptime.vue index ad8114fcb..322b35f70 100644 --- a/src/components/Uptime.vue +++ b/src/components/Uptime.vue @@ -30,6 +30,8 @@ export default { return "danger" } else if (this.lastHeartBeat.status === 1) { return "primary" + } else if (this.lastHeartBeat.status === 2) { + return "warning" } else { return "secondary" } diff --git a/src/mixins/socket.js b/src/mixins/socket.js index f0a3b1a6a..42de1eb3f 100644 --- a/src/mixins/socket.js +++ b/src/mixins/socket.js @@ -274,6 +274,11 @@ export default { text: "Down", color: "danger" }; + } else if (lastHeartBeat.status === 2) { + result[monitorID] = { + text: "Pending", + color: "warning" + }; } else { result[monitorID] = unknown; } diff --git a/src/pages/DashboardHome.vue b/src/pages/DashboardHome.vue index 820842bac..87380c7c2 100644 --- a/src/pages/DashboardHome.vue +++ b/src/pages/DashboardHome.vue @@ -90,6 +90,8 @@ export default { result.up++; } else if (beat.status === 0) { result.down++; + } else if (beat.status === 2) { + result.up++; } else { result.unknown++; } diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue index 01af50610..e4898c33d 100644 --- a/src/pages/EditMonitor.vue +++ b/src/pages/EditMonitor.vue @@ -48,6 +48,11 @@
+
+ + +
+
@@ -61,7 +66,7 @@

Notifications

Not available, please setup.

-
+
-
+
From 14e1d1f1057854ae7217a3028bca73aaf5da378e Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 20 Jul 2021 17:39:21 +0200 Subject: [PATCH 04/31] add .vscode directory to dockerignore --- .dockerignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.dockerignore b/.dockerignore index d439b2db5..825d58038 100644 --- a/.dockerignore +++ b/.dockerignore @@ -11,3 +11,4 @@ LICENSE README.md .editorconfig +.vscode From ccda6f05f59ac273830c406733f98a0c7daa4557 Mon Sep 17 00:00:00 2001 From: Nelson Chan Date: Wed, 21 Jul 2021 12:09:09 +0800 Subject: [PATCH 05/31] Feat: Add Barebones certificate info display --- server/model/monitor.js | 13 ++++++++++- server/util-server.js | 50 +++++++++++++++++++++++++++++++++++++++++ src/mixins/socket.js | 5 +++++ src/pages/Details.vue | 40 +++++++++++++++++++++++++++++++++ 4 files changed, 107 insertions(+), 1 deletion(-) diff --git a/server/model/monitor.js b/server/model/monitor.js index 04feea6b0..6c5f86f6f 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -5,7 +5,7 @@ var timezone = require('dayjs/plugin/timezone') dayjs.extend(utc) dayjs.extend(timezone) const axios = require("axios"); -const {tcping, ping} = require("../util-server"); +const {tcping, ping, checkCertificate} = require("../util-server"); const {R} = require("redbean-node"); const {BeanModel} = require("redbean-node/dist/bean-model"); const {Notification} = require("../notification") @@ -74,6 +74,9 @@ class Monitor extends BeanModel { }) bean.msg = `${res.status} - ${res.statusText}` bean.ping = dayjs().valueOf() - startTime; + if (this.url.startsWith("https")) { + Monitor.sendCertInfo(checkCertificate(res), io, this.id, this.user_id); + } if (this.type === "http") { bean.status = 1; @@ -189,6 +192,14 @@ class Monitor extends BeanModel { io.to(userID).emit("avgPing", monitorID, avgPing); } + /** + * + * @param checkCertificateResult : Object return result of checkCertificate + */ + static async sendCertInfo(checkCertificateResult, io, monitorID, userID) { + io.to(userID).emit("certInfo", monitorID, checkCertificateResult); + } + /** * Uptime with calculation * Calculation based on: diff --git a/server/util-server.js b/server/util-server.js index b387f4c7c..e0e255345 100644 --- a/server/util-server.js +++ b/server/util-server.js @@ -70,3 +70,53 @@ exports.getSettings = async function (type) { return result; } + + +// ssl-checker by @dyaa +// param: res - response object from axios +// return an object containing the certificate information + +const getDaysBetween = (validFrom, validTo) => + Math.round(Math.abs(+validFrom - +validTo) / 8.64e7); + +const getDaysRemaining = (validFrom, validTo) => { + const daysRemaining = getDaysBetween(validFrom, validTo); + if (new Date(validTo).getTime() < new Date().getTime()) { + return -daysRemaining; + } + return daysRemaining; +}; + +exports.checkCertificate = function (res) { + const { + valid_from, + valid_to, + subjectaltname, + issuer, + fingerprint, + } = res.request.res.socket.getPeerCertificate(false); + + if (!valid_from || !valid_to || !subjectaltname) { + reject(new Error('No certificate')); + return; + } + + const valid = res.request.res.socket.authorized || false; + + const validTo = new Date(valid_to); + + const validFor = subjectaltname + .replace(/DNS:|IP Address:/g, "") + .split(", "); + + const daysRemaining = getDaysRemaining(new Date(), validTo); + + return { + valid, + validFor, + validTo, + daysRemaining, + issuer, + fingerprint, + }; +} \ No newline at end of file diff --git a/src/mixins/socket.js b/src/mixins/socket.js index 6ad627244..14f78c872 100644 --- a/src/mixins/socket.js +++ b/src/mixins/socket.js @@ -25,6 +25,7 @@ export default { importantHeartbeatList: { }, avgPingList: { }, uptimeList: { }, + certInfoList: {}, notificationList: [], windowWidth: window.innerWidth, showListMobile: false, @@ -114,6 +115,10 @@ export default { this.uptimeList[`${monitorID}_${type}`] = data }); + socket.on('certInfo', (monitorID, data) => { + this.certInfoList[monitorID] = data + }); + socket.on('importantHeartbeatList', (monitorID, data) => { if (! (monitorID in this.importantHeartbeatList)) { this.importantHeartbeatList[monitorID] = data; diff --git a/src/pages/Details.vue b/src/pages/Details.vue index f8c4879ad..727f0aab4 100644 --- a/src/pages/Details.vue +++ b/src/pages/Details.vue @@ -54,6 +54,38 @@
+
+
+
+

Certificate Info

+ + + + + + + + + + + + + + + + + + + + + + + +
Valid: {{ certInfo.valid }}
Valid To: {{ certInfo.validTo ? new Date(certInfo.validTo).toLocaleString() : "" }}
Days Remaining: {{ certInfo.daysRemaining }}
Issuer: {{ certInfo.issuer }}
Fingerprint: {{ certInfo.fingerprint }}
+
+
+
+
@@ -180,6 +212,14 @@ export default { } }, + certInfo() { + if (this.$root.certInfoList[this.monitor.id]) { + return this.$root.certInfoList[this.monitor.id] + } else { + return { } + } + }, + displayedRecords() { const startIndex = this.perPage * (this.page - 1); const endIndex = startIndex + this.perPage; From 96c60dd94aa94c09ddd3039e61ea8eed433a082b Mon Sep 17 00:00:00 2001 From: Nelson Chan Date: Thu, 22 Jul 2021 16:04:32 +0800 Subject: [PATCH 06/31] Feat: Add database storage for TLS info --- db/patch2.sql | 9 +++++++++ server/database.js | 2 +- server/model/monitor.js | 44 +++++++++++++++++++++++++++++++++-------- src/mixins/socket.js | 14 +++++++++++-- src/pages/Details.vue | 2 +- 5 files changed, 59 insertions(+), 12 deletions(-) create mode 100644 db/patch2.sql diff --git a/db/patch2.sql b/db/patch2.sql new file mode 100644 index 000000000..012d01502 --- /dev/null +++ b/db/patch2.sql @@ -0,0 +1,9 @@ +BEGIN TRANSACTION; + +CREATE TABLE monitor_tls_info ( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + monitor_id INTEGER NOT NULL, + info_json TEXT +); + +COMMIT; diff --git a/server/database.js b/server/database.js index 49659e613..eef3587e4 100644 --- a/server/database.js +++ b/server/database.js @@ -8,7 +8,7 @@ class Database { static templatePath = "./db/kuma.db" static path = './data/kuma.db'; - static latestVersion = 1; + static latestVersion = 2; static noReject = true; static async patch() { diff --git a/server/model/monitor.js b/server/model/monitor.js index 6c5f86f6f..7afb82b6a 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -74,8 +74,10 @@ class Monitor extends BeanModel { }) bean.msg = `${res.status} - ${res.statusText}` bean.ping = dayjs().valueOf() - startTime; - if (this.url.startsWith("https")) { - Monitor.sendCertInfo(checkCertificate(res), io, this.id, this.user_id); + + // Check certificate if https is used + if (this.getUrl().protocol === "https:") { + await this.updateTlsInfo(checkCertificate(res)); } if (this.type === "http") { @@ -168,10 +170,35 @@ class Monitor extends BeanModel { clearInterval(this.heartbeatInterval) } + // Helper Method: + // returns URL object for further usage + // returns null if url is invalid + getUrl() { + try { + return new URL(this.url); + } catch (_) { + return null; + } + } + + // Store TLS info to database + async updateTlsInfo(checkCertificateResult) { + let tls_info_bean = await R.findOne("monitor_tls_info", "monitor_id = ?", [ + this.id + ]); + if (tls_info_bean == null) { + tls_info_bean = R.dispense("monitor_tls_info"); + tls_info_bean.monitor_id = this.id; + } + tls_info_bean.info_json = JSON.stringify(checkCertificateResult); + R.store(tls_info_bean); + } + static async sendStats(io, monitorID, userID) { Monitor.sendAvgPing(24, io, monitorID, userID); Monitor.sendUptime(24, io, monitorID, userID); Monitor.sendUptime(24 * 30, io, monitorID, userID); + Monitor.sendCertInfo(io, monitorID, userID); } /** @@ -192,12 +219,13 @@ class Monitor extends BeanModel { io.to(userID).emit("avgPing", monitorID, avgPing); } - /** - * - * @param checkCertificateResult : Object return result of checkCertificate - */ - static async sendCertInfo(checkCertificateResult, io, monitorID, userID) { - io.to(userID).emit("certInfo", monitorID, checkCertificateResult); + static async sendCertInfo(io, monitorID, userID) { + let tls_info = await R.findOne("monitor_tls_info", "monitor_id = ?", [ + monitorID + ]); + if (tls_info != null) { + io.to(userID).emit("certInfo", monitorID, tls_info.info_json); + } } /** diff --git a/src/mixins/socket.js b/src/mixins/socket.js index 14f78c872..26a4611ed 100644 --- a/src/mixins/socket.js +++ b/src/mixins/socket.js @@ -59,7 +59,17 @@ export default { this.$router.push("/setup") }); - socket.on('monitorList', (data) => { + socket.on("monitorList", (data) => { + // Add Helper function + Object.entries(data).forEach(([monitorID, monitor]) => { + monitor.getUrl = () => { + try { + return new URL(monitor.url); + } catch (_) { + return null; + } + }; + }); this.monitorList = data; }); @@ -116,7 +126,7 @@ export default { }); socket.on('certInfo', (monitorID, data) => { - this.certInfoList[monitorID] = data + this.certInfoList[monitorID] = JSON.parse(data) }); socket.on('importantHeartbeatList', (monitorID, data) => { diff --git a/src/pages/Details.vue b/src/pages/Details.vue index 727f0aab4..a2952aa0a 100644 --- a/src/pages/Details.vue +++ b/src/pages/Details.vue @@ -54,7 +54,7 @@ -
+

Certificate Info

From f20ab4b0e384152cf40b5fdf4f89d932d3d99b70 Mon Sep 17 00:00:00 2001 From: Nelson Chan Date: Thu, 22 Jul 2021 16:13:58 +0800 Subject: [PATCH 07/31] Fix: use Optional chaining --- server/model/monitor.js | 2 +- src/pages/Details.vue | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/server/model/monitor.js b/server/model/monitor.js index 7afb82b6a..6513782f0 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -76,7 +76,7 @@ class Monitor extends BeanModel { bean.ping = dayjs().valueOf() - startTime; // Check certificate if https is used - if (this.getUrl().protocol === "https:") { + if (this.getUrl()?.protocol === "https:") { await this.updateTlsInfo(checkCertificate(res)); } diff --git a/src/pages/Details.vue b/src/pages/Details.vue index a2952aa0a..6d93df399 100644 --- a/src/pages/Details.vue +++ b/src/pages/Details.vue @@ -54,7 +54,7 @@
-
+

Certificate Info

From 32a5e838bab12dba60b825c3155344abcbef8579 Mon Sep 17 00:00:00 2001 From: LouisLam Date: Thu, 22 Jul 2021 17:44:59 +0800 Subject: [PATCH 08/31] add patch3.sql and fix duplicate id in EditMonitor.vue --- db/patch3.sql | 37 +++++++++++++++++++++++++++++++++++++ src/pages/EditMonitor.vue | 4 ++-- 2 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 db/patch3.sql diff --git a/db/patch3.sql b/db/patch3.sql new file mode 100644 index 000000000..e615632f9 --- /dev/null +++ b/db/patch3.sql @@ -0,0 +1,37 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +-- Add maxretries column to monitor +PRAGMA foreign_keys=off; + +BEGIN TRANSACTION; + +create table monitor_dg_tmp +( + id INTEGER not null + primary key autoincrement, + name VARCHAR(150), + active BOOLEAN default 1 not null, + user_id INTEGER + references user + on update cascade on delete set null, + interval INTEGER default 20 not null, + url TEXT, + type VARCHAR(20), + weight INTEGER default 2000, + hostname VARCHAR(255), + port INTEGER, + created_date DATETIME, + keyword VARCHAR(255), + maxretries INTEGER NOT NULL DEFAULT 0 +); + +insert into monitor_dg_tmp(id, name, active, user_id, interval, url, type, weight, hostname, port, created_date, keyword) select id, name, active, user_id, interval, url, type, weight, hostname, port, created_date, keyword from monitor; + +drop table monitor; + +alter table monitor_dg_tmp rename to monitor; + +create index user_id on monitor (user_id); + +COMMIT; + +PRAGMA foreign_keys=on; diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue index f5d71703a..69cd41625 100644 --- a/src/pages/EditMonitor.vue +++ b/src/pages/EditMonitor.vue @@ -49,8 +49,8 @@
- - + +
From 70b1f197c1a71d78e1c8b05a8269ef42e8264e8a Mon Sep 17 00:00:00 2001 From: LouisLam Date: Thu, 22 Jul 2021 19:02:44 +0800 Subject: [PATCH 09/31] rename "Retry Pings" to "Retries" --- src/pages/EditMonitor.vue | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue index 69cd41625..03fa66e95 100644 --- a/src/pages/EditMonitor.vue +++ b/src/pages/EditMonitor.vue @@ -49,8 +49,9 @@
- + +
Maximum retries before the service is marked as down and send notifications
From 5a219554b30322cce5620e75aa67b8199d04f10c Mon Sep 17 00:00:00 2001 From: LouisLam Date: Thu, 22 Jul 2021 19:49:46 +0800 Subject: [PATCH 10/31] grammar --- 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 03fa66e95..75d7d4b9b 100644 --- a/src/pages/EditMonitor.vue +++ b/src/pages/EditMonitor.vue @@ -51,7 +51,7 @@
-
Maximum retries before the service is marked as down and send notifications
+
Maximum retries before the service is marked as down and a notification is sent
From b7a32d4ab6ffde5cb36152a82715963eeefcc07f Mon Sep 17 00:00:00 2001 From: Niyas Date: Thu, 22 Jul 2021 19:26:54 +0530 Subject: [PATCH 11/31] Pushover enhancements --- server/notification.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/notification.js b/server/notification.js index 3c0e9f2a4..9da8a0dc8 100644 --- a/server/notification.js +++ b/server/notification.js @@ -204,7 +204,7 @@ class Notification { } let data = { - "message": "Uptime Kuma Alert\n\nMessage:" +msg + '\nTime (UTC):' +time, + "message": "Uptime Kuma Alert\n\nMessage:"+msg+ '\nTime (UTC):' +heartbeatJSON["time"], "user":notification.pushoveruserkey, "token": notification.pushoverapptoken, "sound": notification.pushoversounds, From 77fbfc23be9a3bf16a861c755f82ccd545745bc0 Mon Sep 17 00:00:00 2001 From: Niyas Date: Thu, 22 Jul 2021 19:28:25 +0530 Subject: [PATCH 12/31] Pushover enhancements --- src/components/NotificationDialog.vue | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/components/NotificationDialog.vue b/src/components/NotificationDialog.vue index 6df8b9566..afbfc0d4e 100644 --- a/src/components/NotificationDialog.vue +++ b/src/components/NotificationDialog.vue @@ -223,16 +223,22 @@
From 1d4d7fa9c4c9626f4451fe7449171669ad8a808a Mon Sep 17 00:00:00 2001 From: Sam Date: Thu, 22 Jul 2021 20:25:03 +0200 Subject: [PATCH 13/31] fix parenthesis mistake --- 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 2479688ac..1beaa5475 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -120,7 +120,7 @@ class Monitor extends BeanModel { // Mark as important if status changed, ignore pending pings, // Don't notify if disrupted changes to up - if ((! previousBeat || previousBeat.status !== bean.status) && bean.status !== 2 && !(previousBeat.status === 2 && bean.status !== 0)) { + if ((!previousBeat) || ((previousBeat.status !== bean.status) && bean.status !== 2 && !(previousBeat.status === 2 && bean.status !== 0))) { bean.important = true; // Do not send if first beat is UP From 4d262bbb6a07231aa0c9304f7ac56c59919257ac Mon Sep 17 00:00:00 2001 From: Nelson Chan Date: Fri, 23 Jul 2021 11:22:37 +0800 Subject: [PATCH 14/31] Fix: Fix no certificate caused by session reuse --- server/model/monitor.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/server/model/monitor.js b/server/model/monitor.js index 6513782f0..a2a904b10 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -1,4 +1,5 @@ +const https = require('https'); const dayjs = require("dayjs"); const utc = require('dayjs/plugin/utc') var timezone = require('dayjs/plugin/timezone') @@ -10,6 +11,12 @@ const {R} = require("redbean-node"); const {BeanModel} = require("redbean-node/dist/bean-model"); const {Notification} = require("../notification") +// Use Custom agent to disable session reuse +// https://github.com/nodejs/node/issues/3940 +const customAgent = new https.Agent({ + maxCachedSessions: 0 +}); + /** * status: * 0 = DOWN @@ -70,8 +77,9 @@ class Monitor extends BeanModel { if (this.type === "http" || this.type === "keyword") { let startTime = dayjs().valueOf(); let res = await axios.get(this.url, { - headers: { 'User-Agent':'Uptime-Kuma' } - }) + headers: { "User-Agent": "Uptime-Kuma" }, + httpsAgent: customAgent, + }); bean.msg = `${res.status} - ${res.statusText}` bean.ping = dayjs().valueOf() - startTime; From 6b72d5033aeea7501d9af5c2974ce8c4387c746e Mon Sep 17 00:00:00 2001 From: Nelson Chan Date: Fri, 23 Jul 2021 11:23:43 +0800 Subject: [PATCH 15/31] Fix: Fix incorrect error handling --- server/util-server.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/server/util-server.js b/server/util-server.js index e0e255345..f03823d3a 100644 --- a/server/util-server.js +++ b/server/util-server.js @@ -97,8 +97,7 @@ exports.checkCertificate = function (res) { } = res.request.res.socket.getPeerCertificate(false); if (!valid_from || !valid_to || !subjectaltname) { - reject(new Error('No certificate')); - return; + throw { message: 'No TLS certificate in response' }; } const valid = res.request.res.socket.authorized || false; From 2450b3d08234e03a6c9a2f0fedf1be3303d0c3c1 Mon Sep 17 00:00:00 2001 From: rezzorix <72935517+rezzorix@users.noreply.github.com> Date: Fri, 23 Jul 2021 11:48:39 +0800 Subject: [PATCH 16/31] Small grammar updates to Settings.vue Just small grammar corrections. --- src/pages/Settings.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/Settings.vue b/src/pages/Settings.vue index daa521981..f4bf156a8 100644 --- a/src/pages/Settings.vue +++ b/src/pages/Settings.vue @@ -36,7 +36,7 @@
- The repeat password is not match. + The repeat password does not match.
@@ -56,7 +56,7 @@

Notifications

Not available, please setup.

-

Please assign the notification to monitor(s) to get it works.

+

Please assign a notification to monitor(s) to get it to work.

  • From 280ba84acae766948ab77183341ededa643c1a48 Mon Sep 17 00:00:00 2001 From: rezzorix <72935517+rezzorix@users.noreply.github.com> Date: Fri, 23 Jul 2021 12:41:02 +0800 Subject: [PATCH 17/31] Apple touch icon 192px with preserved transparency Resized icon.png to 192px but preserved transparency --- public/apple-touch-icon.png | Bin 4086 -> 2582 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/public/apple-touch-icon.png b/public/apple-touch-icon.png index 3bb82e4c9aa30a7291fa98c89153785d909f97f3..0e9c109f33c926aad9c43ecd3d97db907e24c522 100644 GIT binary patch literal 2582 zcmX|DdpOhkAOCLVerb{WZ6d9l2)RTVwJ|D5w?k>D+(S6IoO9ajVSvi){b^cG2tb37mqBC7;iikW41?`}gn5{{@f7<8rx@agN88sFDx}1e3g3 z3G65Gnk$^w5$qDtUT=g;~RO4Oh5{o;g#8C)2bz+Udi_|nw>{hi!*kJxQ5 z=X-`1Kh?~2Je%v3NY(7uuQ&s*IYa#mV~z8DEu8)d9!s(-S#09;Nen#;BUJ9t^SQ1r z?uV|$5A6$YTR8*m+_(LUW3_X1DtEA+)BASu^T;Bjl{?VLc~dbcMXZ_nd^^ViX!A0Y6M8x}~H*2hbLd^FxC76260ek2UITdXA^W#inB z;AB~{nuu){&u))&0)X63cdW~agz<%vp2#T=E!WC|Z5rKX7$CC`UhJv_+u`9{9~va3 z=$f);kGq3>!0uF`sHQpURx17dS!X}dne(wK24T^n?MU8qSFR!EDk$7r1JvLZ&9h`t z8Si<)vjE%JZeuSRFt||!#VL@&O}a2maHMJvKersfy2gOUS^)os9-#tef9k@l=6N7T zXG|nA15ouaAVwEJ)xqq2F3irJ2S}e~0DL51Vh1=!N{w46r#{k5GPCG@K!$J&RhEV5sU;sZ0zdJl%V*`UOe?f(d!)~@Kd-a9a554L5+w0F4gQ$Lmy z;C$sP)=FN@;*uTCPd&={>F*0x!FLg0r0PG_{xCS0)!^RCh}@%kry4H>`kl`D*Zmh{ zc!Rsi$pD$rb!ERhFTlvA#3OmI#kwlHUxi<0wjMz2UaizIHKzV19h=gSaDwx4K5KQGsif465i8KNX3Omok>h zw_K5mTQAs5XDo60;B5UF>E+eS;}=|$!_J_HREna!hF7zmP*+xT=CN6}JCF#|B1=a2 zJ0s^yRf@Awb+6&lwaR?(aj{1f$syU{1ZcSxI#pGct~!*oH|CbU@B_F#z6|Jdp!e_l(ak(A1jVbT<(e&74`2f00NDxJ&SFK4t<*EU)Hk$kkN}sY?O|17m&+EK$Lor;p3EOfNGqO6p1*LPxI+md4yx6K&tjlTMjb`(Xn|I1ZYl$IuPvyevDb) zPD}B62-7MTg|>hxUzWczGAL~UZ#rz(N(eDvr9>ZwFIll#_eTU)WfNxafe=6|fI+J{hLtC7^z#Ge#; zG5A=d*VNEVpPUQeR4c&d}up*zDi%hQ*qkGKrQl0>{Xi>Q#C`gPi z6#te+EPbwsEj7CxaSCdbo)&AX8$I1Xj6%tFfMYj?&@LoS>;$E+-1Gk=l)^_rjZ)L% z3_BB4lgt$g?aV<}NVLuXzwLr_&K-1)(Oe6cPTpHKMO>euSU4ad-y;__hDsNO zCwvKA!A0+cd{fd$Bm1?9yWSiGZlOh4TfjT|P(B@pirES2P{#aLo#M7aI?!Jp9o|wA zkdwUFER(urMsQs|gnyIw@olj|eq$OSP*;5gZYwAv1?KVV@gi9R(B^^7`}9Fq)dRv- zhauvci%Sl?)Oke#*aZ1ryJonfl!X@VKaFHA6OI2+m;T0&SZx696oO%1?s^3jFvY4Y z`6?FLv)1aBxr+XFo?rMVL`w&jx2T_jsd>vguZ_*T1T;i@NcRSbw0rtJ2+Dzw5}h{ z!Xt-LqWY%?A{Ij}M zAKul;JYl98>Nn{2aQJ}HU25a-yTZvZ+p;jy(F^GOUQ=XV2?pgT_R;M=Zgx9E9HBh( z=wn|8@o$;s4}C#X7)PnSoWGwlDFx)d&S}`0jf63gcoXqHGEYxjrT}5)ioINx#qIg) zhCOh&v0ZCI8tukbn-*jmEp)53qh=OOgbj>?7m)sY)A`d~gJUeh9d4C^9Nv3Y@{s`C Mah}*G2mHzZ0~dTv1ONa4 delta 4089 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 From 48f82b55f86c847d3aed69604a58eb13c4114f1d Mon Sep 17 00:00:00 2001 From: LouisLam Date: Fri, 23 Jul 2021 12:58:05 +0800 Subject: [PATCH 18/31] prevent unexpected error throw from checkCertificate interrupt the beat --- server/model/monitor.js | 15 ++++++++++++--- server/util.js | 6 ++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/server/model/monitor.js b/server/model/monitor.js index a2a904b10..49cc0f0e9 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -6,6 +6,7 @@ var timezone = require('dayjs/plugin/timezone') dayjs.extend(utc) dayjs.extend(timezone) const axios = require("axios"); +const {debug} = require("../util"); const {tcping, ping, checkCertificate} = require("../util-server"); const {R} = require("redbean-node"); const {BeanModel} = require("redbean-node/dist/bean-model"); @@ -84,10 +85,18 @@ class Monitor extends BeanModel { bean.ping = dayjs().valueOf() - startTime; // Check certificate if https is used + + let certInfoStartTime = dayjs().valueOf(); if (this.getUrl()?.protocol === "https:") { - await this.updateTlsInfo(checkCertificate(res)); + try { + await this.updateTlsInfo(checkCertificate(res)); + } catch (e) { + console.error(e.message) + } } + debug("Cert Info Query Time: " + (dayjs().valueOf() - certInfoStartTime) + "ms") + if (this.type === "http") { bean.status = 1; } else { @@ -178,7 +187,7 @@ class Monitor extends BeanModel { clearInterval(this.heartbeatInterval) } - // Helper Method: + // Helper Method: // returns URL object for further usage // returns null if url is invalid getUrl() { @@ -199,7 +208,7 @@ class Monitor extends BeanModel { tls_info_bean.monitor_id = this.id; } tls_info_bean.info_json = JSON.stringify(checkCertificateResult); - R.store(tls_info_bean); + await R.store(tls_info_bean); } static async sendStats(io, monitorID, userID) { diff --git a/server/util.js b/server/util.js index 0a8877b80..0282a0276 100644 --- a/server/util.js +++ b/server/util.js @@ -14,3 +14,9 @@ exports.ucfirst = function (str) { return firstLetter.toUpperCase() + str.substr(1); } +exports.debug = (msg) => { + if (process.env.NODE_ENV === "development") { + console.log(msg) + } +} + From d556509d079aab93467a4ef188f4e54acb9ccfbb Mon Sep 17 00:00:00 2001 From: LouisLam Date: Sat, 24 Jul 2021 11:42:14 +0800 Subject: [PATCH 19/31] =?UTF-8?q?=E6=88=88mprove=20the=20readibility=20of?= =?UTF-8?q?=20important=20condition?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/model/monitor.js | 45 ++++++++++++++++++++++++++++++----------- server/util.js | 4 ++++ 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/server/model/monitor.js b/server/model/monitor.js index 1beaa5475..133088671 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -5,6 +5,7 @@ var timezone = require('dayjs/plugin/timezone') dayjs.extend(utc) dayjs.extend(timezone) const axios = require("axios"); +const {UP, DOWN, PENDING} = require("../util"); const {tcping, ping} = require("../util-server"); const {R} = require("redbean-node"); const {BeanModel} = require("redbean-node/dist/bean-model"); @@ -49,19 +50,22 @@ class Monitor extends BeanModel { let retries = 0; const beat = async () => { + if (! previousBeat) { previousBeat = await R.findOne("heartbeat", " monitor_id = ? ORDER BY time DESC", [ this.id ]) } + const isFirstBeat = !previousBeat; + let bean = R.dispense("heartbeat") bean.monitor_id = this.id; bean.time = R.isoDateTime(dayjs.utc()); - bean.status = 0; + bean.status = DOWN; // Duration - if (previousBeat) { + if (! isFirstBeat) { bean.duration = dayjs(bean.time).diff(dayjs(previousBeat.time), 'second'); } else { bean.duration = 0; @@ -77,7 +81,7 @@ class Monitor extends BeanModel { bean.ping = dayjs().valueOf() - startTime; if (this.type === "http") { - bean.status = 1; + bean.status = UP; } else { let data = res.data; @@ -89,7 +93,7 @@ class Monitor extends BeanModel { if (data.includes(this.keyword)) { bean.msg += ", keyword is found" - bean.status = 1; + bean.status = UP; } else { throw new Error(bean.msg + ", but keyword is not found") } @@ -100,12 +104,12 @@ class Monitor extends BeanModel { } else if (this.type === "port") { bean.ping = await tcping(this.hostname, this.port); bean.msg = "" - bean.status = 1; + bean.status = UP; } else if (this.type === "ping") { bean.ping = await ping(this.hostname); bean.msg = "" - bean.status = 1; + bean.status = UP; } retries = 0; @@ -113,24 +117,39 @@ class Monitor extends BeanModel { } catch (error) { if ((this.maxretries > 0) && (retries < this.maxretries)) { retries++; - bean.status = 2; + bean.status = PENDING; } bean.msg = error.message; } + // * ? -> ANY STATUS = important [isFirstBeat] + // UP -> PENDING = not important + // * UP -> DOWN = important + // UP -> UP = not important + // PENDING -> PENDING = not important + // * PENDING -> DOWN = important + // PENDING -> UP = not important + // DOWN -> PENDING = this case not exists + // DOWN -> DOWN = not important + // * DOWN -> UP = important + let isImportant = isFirstBeat || + (previousBeat.status === UP && bean.status === DOWN) || + (previousBeat.status === DOWN && bean.status === UP) || + (previousBeat.status === PENDING && bean.status === DOWN); + // Mark as important if status changed, ignore pending pings, // Don't notify if disrupted changes to up - if ((!previousBeat) || ((previousBeat.status !== bean.status) && bean.status !== 2 && !(previousBeat.status === 2 && bean.status !== 0))) { + if (isImportant) { bean.important = true; - // Do not send if first beat is UP - if (previousBeat || bean.status !== 1) { + // Send only if the first beat is DOWN + if (!isFirstBeat || bean.status === DOWN) { let notificationList = await R.getAll(`SELECT notification.* FROM notification, monitor_notification WHERE monitor_id = ? AND monitor_notification.notification_id = notification.id `, [ this.id ]) let text; - if (bean.status === 1) { + if (bean.status === UP) { text = "✅ Up" } else { text = "🔴 Down" @@ -151,8 +170,10 @@ class Monitor extends BeanModel { bean.important = false; } - if (bean.status === 1) { + if (bean.status === UP) { console.info(`Monitor #${this.id} '${this.name}': Successful Response: ${bean.ping} ms | Interval: ${this.interval} seconds | Type: ${this.type}`) + } else if (bean.status === PENDING) { + console.warn(`Monitor #${this.id} '${this.name}': Pending: ${bean.msg} | Type: ${this.type}`) } else { console.warn(`Monitor #${this.id} '${this.name}': Failing: ${bean.msg} | Type: ${this.type}`) } diff --git a/server/util.js b/server/util.js index 0a8877b80..33b306b5c 100644 --- a/server/util.js +++ b/server/util.js @@ -1,6 +1,10 @@ // Common JS cannot be used in frontend sadly // sleep, ucfirst is duplicated in ../src/util-frontend.js +exports.DOWN = 0; +exports.UP = 1; +exports.PENDING = 2; + exports.sleep = function (ms) { return new Promise(resolve => setTimeout(resolve, ms)); } From 803f0d6219ae84838cc6884c5a9dec379c6bc1ff Mon Sep 17 00:00:00 2001 From: Nelson Chan Date: Wed, 21 Jul 2021 12:09:09 +0800 Subject: [PATCH 20/31] Feat: Add Barebones certificate info display --- server/model/monitor.js | 13 ++++++++++- server/util-server.js | 50 +++++++++++++++++++++++++++++++++++++++++ src/mixins/socket.js | 5 +++++ src/pages/Details.vue | 40 +++++++++++++++++++++++++++++++++ 4 files changed, 107 insertions(+), 1 deletion(-) diff --git a/server/model/monitor.js b/server/model/monitor.js index 133088671..79376c809 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -6,7 +6,7 @@ dayjs.extend(utc) dayjs.extend(timezone) const axios = require("axios"); const {UP, DOWN, PENDING} = require("../util"); -const {tcping, ping} = require("../util-server"); +const {tcping, ping, checkCertificate} = require("../util-server"); const {R} = require("redbean-node"); const {BeanModel} = require("redbean-node/dist/bean-model"); const {Notification} = require("../notification") @@ -79,6 +79,9 @@ class Monitor extends BeanModel { }) bean.msg = `${res.status} - ${res.statusText}` bean.ping = dayjs().valueOf() - startTime; + if (this.url.startsWith("https")) { + Monitor.sendCertInfo(checkCertificate(res), io, this.id, this.user_id); + } if (this.type === "http") { bean.status = UP; @@ -218,6 +221,14 @@ class Monitor extends BeanModel { io.to(userID).emit("avgPing", monitorID, avgPing); } + /** + * + * @param checkCertificateResult : Object return result of checkCertificate + */ + static async sendCertInfo(checkCertificateResult, io, monitorID, userID) { + io.to(userID).emit("certInfo", monitorID, checkCertificateResult); + } + /** * Uptime with calculation * Calculation based on: diff --git a/server/util-server.js b/server/util-server.js index b387f4c7c..e0e255345 100644 --- a/server/util-server.js +++ b/server/util-server.js @@ -70,3 +70,53 @@ exports.getSettings = async function (type) { return result; } + + +// ssl-checker by @dyaa +// param: res - response object from axios +// return an object containing the certificate information + +const getDaysBetween = (validFrom, validTo) => + Math.round(Math.abs(+validFrom - +validTo) / 8.64e7); + +const getDaysRemaining = (validFrom, validTo) => { + const daysRemaining = getDaysBetween(validFrom, validTo); + if (new Date(validTo).getTime() < new Date().getTime()) { + return -daysRemaining; + } + return daysRemaining; +}; + +exports.checkCertificate = function (res) { + const { + valid_from, + valid_to, + subjectaltname, + issuer, + fingerprint, + } = res.request.res.socket.getPeerCertificate(false); + + if (!valid_from || !valid_to || !subjectaltname) { + reject(new Error('No certificate')); + return; + } + + const valid = res.request.res.socket.authorized || false; + + const validTo = new Date(valid_to); + + const validFor = subjectaltname + .replace(/DNS:|IP Address:/g, "") + .split(", "); + + const daysRemaining = getDaysRemaining(new Date(), validTo); + + return { + valid, + validFor, + validTo, + daysRemaining, + issuer, + fingerprint, + }; +} \ No newline at end of file diff --git a/src/mixins/socket.js b/src/mixins/socket.js index 0c4d68e15..612bbadd6 100644 --- a/src/mixins/socket.js +++ b/src/mixins/socket.js @@ -25,6 +25,7 @@ export default { importantHeartbeatList: { }, avgPingList: { }, uptimeList: { }, + certInfoList: {}, notificationList: [], windowWidth: window.innerWidth, showListMobile: false, @@ -114,6 +115,10 @@ export default { this.uptimeList[`${monitorID}_${type}`] = data }); + socket.on('certInfo', (monitorID, data) => { + this.certInfoList[monitorID] = data + }); + socket.on('importantHeartbeatList', (monitorID, data) => { if (! (monitorID in this.importantHeartbeatList)) { this.importantHeartbeatList[monitorID] = data; diff --git a/src/pages/Details.vue b/src/pages/Details.vue index f8c4879ad..727f0aab4 100644 --- a/src/pages/Details.vue +++ b/src/pages/Details.vue @@ -54,6 +54,38 @@
+
+
+
+

Certificate Info

+
+ + + + + + + + + + + + + + + + + + + + + + +
Valid: {{ certInfo.valid }}
Valid To: {{ certInfo.validTo ? new Date(certInfo.validTo).toLocaleString() : "" }}
Days Remaining: {{ certInfo.daysRemaining }}
Issuer: {{ certInfo.issuer }}
Fingerprint: {{ certInfo.fingerprint }}
+
+
+ +
@@ -180,6 +212,14 @@ export default { } }, + certInfo() { + if (this.$root.certInfoList[this.monitor.id]) { + return this.$root.certInfoList[this.monitor.id] + } else { + return { } + } + }, + displayedRecords() { const startIndex = this.perPage * (this.page - 1); const endIndex = startIndex + this.perPage; From d0c63ebe3e054a200c25c52f551580c000fca4ba Mon Sep 17 00:00:00 2001 From: Nelson Chan Date: Thu, 22 Jul 2021 16:04:32 +0800 Subject: [PATCH 21/31] Feat: Add database storage for TLS info --- db/patch2.sql | 9 +++++++++ server/database.js | 2 +- server/model/monitor.js | 44 +++++++++++++++++++++++++++++++++-------- src/mixins/socket.js | 14 +++++++++++-- src/pages/Details.vue | 2 +- 5 files changed, 59 insertions(+), 12 deletions(-) create mode 100644 db/patch2.sql diff --git a/db/patch2.sql b/db/patch2.sql new file mode 100644 index 000000000..012d01502 --- /dev/null +++ b/db/patch2.sql @@ -0,0 +1,9 @@ +BEGIN TRANSACTION; + +CREATE TABLE monitor_tls_info ( + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + monitor_id INTEGER NOT NULL, + info_json TEXT +); + +COMMIT; diff --git a/server/database.js b/server/database.js index 49659e613..eef3587e4 100644 --- a/server/database.js +++ b/server/database.js @@ -8,7 +8,7 @@ class Database { static templatePath = "./db/kuma.db" static path = './data/kuma.db'; - static latestVersion = 1; + static latestVersion = 2; static noReject = true; static async patch() { diff --git a/server/model/monitor.js b/server/model/monitor.js index 79376c809..ae409616a 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -79,8 +79,10 @@ class Monitor extends BeanModel { }) bean.msg = `${res.status} - ${res.statusText}` bean.ping = dayjs().valueOf() - startTime; - if (this.url.startsWith("https")) { - Monitor.sendCertInfo(checkCertificate(res), io, this.id, this.user_id); + + // Check certificate if https is used + if (this.getUrl().protocol === "https:") { + await this.updateTlsInfo(checkCertificate(res)); } if (this.type === "http") { @@ -197,10 +199,35 @@ class Monitor extends BeanModel { clearInterval(this.heartbeatInterval) } + // Helper Method: + // returns URL object for further usage + // returns null if url is invalid + getUrl() { + try { + return new URL(this.url); + } catch (_) { + return null; + } + } + + // Store TLS info to database + async updateTlsInfo(checkCertificateResult) { + let tls_info_bean = await R.findOne("monitor_tls_info", "monitor_id = ?", [ + this.id + ]); + if (tls_info_bean == null) { + tls_info_bean = R.dispense("monitor_tls_info"); + tls_info_bean.monitor_id = this.id; + } + tls_info_bean.info_json = JSON.stringify(checkCertificateResult); + R.store(tls_info_bean); + } + static async sendStats(io, monitorID, userID) { Monitor.sendAvgPing(24, io, monitorID, userID); Monitor.sendUptime(24, io, monitorID, userID); Monitor.sendUptime(24 * 30, io, monitorID, userID); + Monitor.sendCertInfo(io, monitorID, userID); } /** @@ -221,12 +248,13 @@ class Monitor extends BeanModel { io.to(userID).emit("avgPing", monitorID, avgPing); } - /** - * - * @param checkCertificateResult : Object return result of checkCertificate - */ - static async sendCertInfo(checkCertificateResult, io, monitorID, userID) { - io.to(userID).emit("certInfo", monitorID, checkCertificateResult); + static async sendCertInfo(io, monitorID, userID) { + let tls_info = await R.findOne("monitor_tls_info", "monitor_id = ?", [ + monitorID + ]); + if (tls_info != null) { + io.to(userID).emit("certInfo", monitorID, tls_info.info_json); + } } /** diff --git a/src/mixins/socket.js b/src/mixins/socket.js index 612bbadd6..f36a770e3 100644 --- a/src/mixins/socket.js +++ b/src/mixins/socket.js @@ -59,7 +59,17 @@ export default { this.$router.push("/setup") }); - socket.on('monitorList', (data) => { + socket.on("monitorList", (data) => { + // Add Helper function + Object.entries(data).forEach(([monitorID, monitor]) => { + monitor.getUrl = () => { + try { + return new URL(monitor.url); + } catch (_) { + return null; + } + }; + }); this.monitorList = data; }); @@ -116,7 +126,7 @@ export default { }); socket.on('certInfo', (monitorID, data) => { - this.certInfoList[monitorID] = data + this.certInfoList[monitorID] = JSON.parse(data) }); socket.on('importantHeartbeatList', (monitorID, data) => { diff --git a/src/pages/Details.vue b/src/pages/Details.vue index 727f0aab4..a2952aa0a 100644 --- a/src/pages/Details.vue +++ b/src/pages/Details.vue @@ -54,7 +54,7 @@ -
+

Certificate Info

From 7b8459c73a472145d00118e78bee6dcf06e951be Mon Sep 17 00:00:00 2001 From: Nelson Chan Date: Thu, 22 Jul 2021 16:13:58 +0800 Subject: [PATCH 22/31] Fix: use Optional chaining --- server/model/monitor.js | 2 +- src/pages/Details.vue | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/server/model/monitor.js b/server/model/monitor.js index ae409616a..f523dd4dd 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -81,7 +81,7 @@ class Monitor extends BeanModel { bean.ping = dayjs().valueOf() - startTime; // Check certificate if https is used - if (this.getUrl().protocol === "https:") { + if (this.getUrl()?.protocol === "https:") { await this.updateTlsInfo(checkCertificate(res)); } diff --git a/src/pages/Details.vue b/src/pages/Details.vue index a2952aa0a..6d93df399 100644 --- a/src/pages/Details.vue +++ b/src/pages/Details.vue @@ -54,7 +54,7 @@
-
+

Certificate Info

From db26b7d123d0aebb5b9f41b4a5e46f9bd88e8b59 Mon Sep 17 00:00:00 2001 From: Nelson Chan Date: Fri, 23 Jul 2021 11:22:37 +0800 Subject: [PATCH 23/31] Fix: Fix no certificate caused by session reuse --- server/model/monitor.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/server/model/monitor.js b/server/model/monitor.js index f523dd4dd..0fb747da0 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -1,4 +1,5 @@ +const https = require('https'); const dayjs = require("dayjs"); const utc = require('dayjs/plugin/utc') var timezone = require('dayjs/plugin/timezone') @@ -11,6 +12,12 @@ const {R} = require("redbean-node"); const {BeanModel} = require("redbean-node/dist/bean-model"); const {Notification} = require("../notification") +// Use Custom agent to disable session reuse +// https://github.com/nodejs/node/issues/3940 +const customAgent = new https.Agent({ + maxCachedSessions: 0 +}); + /** * status: * 0 = DOWN @@ -75,8 +82,9 @@ class Monitor extends BeanModel { if (this.type === "http" || this.type === "keyword") { let startTime = dayjs().valueOf(); let res = await axios.get(this.url, { - headers: { 'User-Agent':'Uptime-Kuma' } - }) + headers: { "User-Agent": "Uptime-Kuma" }, + httpsAgent: customAgent, + }); bean.msg = `${res.status} - ${res.statusText}` bean.ping = dayjs().valueOf() - startTime; From 51ac7a58dc5485cd8dc71af1b6e535ae1ceee7e4 Mon Sep 17 00:00:00 2001 From: Nelson Chan Date: Fri, 23 Jul 2021 11:23:43 +0800 Subject: [PATCH 24/31] Fix: Fix incorrect error handling --- server/util-server.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/server/util-server.js b/server/util-server.js index e0e255345..f03823d3a 100644 --- a/server/util-server.js +++ b/server/util-server.js @@ -97,8 +97,7 @@ exports.checkCertificate = function (res) { } = res.request.res.socket.getPeerCertificate(false); if (!valid_from || !valid_to || !subjectaltname) { - reject(new Error('No certificate')); - return; + throw { message: 'No TLS certificate in response' }; } const valid = res.request.res.socket.authorized || false; From caec93318600f0997f14504a75d0d6801fe94648 Mon Sep 17 00:00:00 2001 From: LouisLam Date: Fri, 23 Jul 2021 12:58:05 +0800 Subject: [PATCH 25/31] prevent unexpected error throw from checkCertificate interrupt the beat --- server/model/monitor.js | 16 ++++++++++++---- server/util.js | 6 ++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/server/model/monitor.js b/server/model/monitor.js index 0fb747da0..9043803b3 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -6,7 +6,7 @@ var timezone = require('dayjs/plugin/timezone') dayjs.extend(utc) dayjs.extend(timezone) const axios = require("axios"); -const {UP, DOWN, PENDING} = require("../util"); +const {debug, UP, DOWN, PENDING} = require("../util"); const {tcping, ping, checkCertificate} = require("../util-server"); const {R} = require("redbean-node"); const {BeanModel} = require("redbean-node/dist/bean-model"); @@ -89,10 +89,18 @@ class Monitor extends BeanModel { bean.ping = dayjs().valueOf() - startTime; // Check certificate if https is used + + let certInfoStartTime = dayjs().valueOf(); if (this.getUrl()?.protocol === "https:") { - await this.updateTlsInfo(checkCertificate(res)); + try { + await this.updateTlsInfo(checkCertificate(res)); + } catch (e) { + console.error(e.message) + } } + debug("Cert Info Query Time: " + (dayjs().valueOf() - certInfoStartTime) + "ms") + if (this.type === "http") { bean.status = UP; } else { @@ -207,7 +215,7 @@ class Monitor extends BeanModel { clearInterval(this.heartbeatInterval) } - // Helper Method: + // Helper Method: // returns URL object for further usage // returns null if url is invalid getUrl() { @@ -228,7 +236,7 @@ class Monitor extends BeanModel { tls_info_bean.monitor_id = this.id; } tls_info_bean.info_json = JSON.stringify(checkCertificateResult); - R.store(tls_info_bean); + await R.store(tls_info_bean); } static async sendStats(io, monitorID, userID) { diff --git a/server/util.js b/server/util.js index 33b306b5c..081561bf9 100644 --- a/server/util.js +++ b/server/util.js @@ -18,3 +18,9 @@ exports.ucfirst = function (str) { return firstLetter.toUpperCase() + str.substr(1); } +exports.debug = (msg) => { + if (process.env.NODE_ENV === "development") { + console.log(msg) + } +} + From bf3e9dccd2f07d69a56475bc70fd15328f8e8999 Mon Sep 17 00:00:00 2001 From: LouisLam Date: Mon, 26 Jul 2021 22:53:07 +0800 Subject: [PATCH 26/31] improve the ui of cert info --- src/components/Datetime.vue | 15 +++++++++++++-- src/pages/Details.vue | 27 ++++++++++++++++++++++++--- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/src/components/Datetime.vue b/src/components/Datetime.vue index e84c877bc..3e5516597 100644 --- a/src/components/Datetime.vue +++ b/src/components/Datetime.vue @@ -14,12 +14,23 @@ dayjs.extend(relativeTime) export default { props: { value: String, + dateOnly: { + type: Boolean, + default: false, + }, }, computed: { displayText() { - let format = "YYYY-MM-DD HH:mm:ss"; - return dayjs.utc(this.value).tz(this.$root.timezone).format(format) + if (this.value !== undefined && this.value !== "") { + let format = "YYYY-MM-DD HH:mm:ss"; + if (this.dateOnly) { + format = "YYYY-MM-DD"; + } + return dayjs.utc(this.value).tz(this.$root.timezone).format(format); + } else { + return ""; + } }, } } diff --git a/src/pages/Details.vue b/src/pages/Details.vue index 6d93df399..cc447bf44 100644 --- a/src/pages/Details.vue +++ b/src/pages/Details.vue @@ -51,10 +51,18 @@

(30-day)

+ +
+

CertExp.

+

()

+ + {{certInfo.daysRemaining}} days + +
-
+

Certificate Info

@@ -66,7 +74,7 @@
- + @@ -154,6 +162,7 @@ export default { page: 1, perPage: 25, heartBeatList: [], + toggleCertInfoBox: false, } }, computed: { @@ -216,10 +225,14 @@ export default { if (this.$root.certInfoList[this.monitor.id]) { return this.$root.certInfoList[this.monitor.id] } else { - return { } + return null } }, + showCertInfoBox() { + return this.certInfo != null && this.toggleCertInfoBox; + }, + displayedRecords() { const startIndex = this.perPage * (this.page - 1); const endIndex = startIndex + this.perPage; @@ -308,4 +321,12 @@ table { font-size: 13px; color: #AAA; } + +.stats { + padding: 10px; + + .col { + margin: 20px 0; + } +} From 06c4523ce37b07c1894090df62f2f426b023797b Mon Sep 17 00:00:00 2001 From: LouisLam Date: Mon, 26 Jul 2021 23:05:04 +0800 Subject: [PATCH 27/31] update the latest db version to 3 --- server/database.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/database.js b/server/database.js index eef3587e4..2c9b2f653 100644 --- a/server/database.js +++ b/server/database.js @@ -8,7 +8,7 @@ class Database { static templatePath = "./db/kuma.db" static path = './data/kuma.db'; - static latestVersion = 2; + static latestVersion = 3; static noReject = true; static async patch() { From 281909437789075e9bc54709b8c84ea510c0d921 Mon Sep 17 00:00:00 2001 From: LouisLam Date: Mon, 26 Jul 2021 23:26:47 +0800 Subject: [PATCH 28/31] improve the page load performance --- server/server.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/server/server.js b/server/server.js index 6bf48dc13..df21ff8f8 100644 --- a/server/server.js +++ b/server/server.js @@ -543,12 +543,12 @@ async function afterLogin(socket, user) { let monitorList = await sendMonitorList(socket) for (let monitorID in monitorList) { - await sendHeartbeatList(socket, monitorID); - await sendImportantHeartbeatList(socket, monitorID); - await Monitor.sendStats(io, monitorID, user.id) + sendHeartbeatList(socket, monitorID); + sendImportantHeartbeatList(socket, monitorID); + Monitor.sendStats(io, monitorID, user.id) } - await sendNotificationList(socket) + sendNotificationList(socket) } async function getMonitorJSONList(userID) { From 1982e2f8b8a802631cde11fbcdc7cfdece02b200 Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Tue, 27 Jul 2021 00:53:26 +0800 Subject: [PATCH 29/31] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7777cb2c5..9c99ac361 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ It is a self-hosted monitoring tool like "Uptime Robot". * Monitoring uptime for HTTP(s) / TCP / Ping. * Fancy, Reactive, Fast UI/UX. -* Notifications via Webhook, Telegram, Discord and email (SMTP). +* Notifications via Webhook, Telegram, Discord, Gotify, Slack, Pushover, Email (SMTP) and more by Apprise. * 20 seconds interval. # How to Use From 4d7c2d329b521c0cb0326b7864be095468600238 Mon Sep 17 00:00:00 2001 From: LouisLam Date: Tue, 27 Jul 2021 13:47:15 +0800 Subject: [PATCH 30/31] Update to 1.0.7 --- README.md | 2 +- package.json | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 9c99ac361..ef8211368 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,7 @@ PS: For every new release, it takes some time to build the docker image, please ```bash git fetch --all -git checkout 1.0.6 --force +git checkout 1.0.7 --force npm install npm run build pm2 restart uptime-kuma diff --git a/package.json b/package.json index d4fe68885..d90840b9d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "uptime-kuma", - "version": "1.0.6", + "version": "1.0.7", "license": "MIT", "repository": { "type": "git", @@ -12,10 +12,10 @@ "update": "", "build": "vite build", "vite-preview-dist": "vite preview --host", - "build-docker": "docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma -t louislam/uptime-kuma:1 -t louislam/uptime-kuma:1.0.6 --target release . --push", + "build-docker": "docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma -t louislam/uptime-kuma:1 -t louislam/uptime-kuma:1.0.7 --target release . --push", "build-docker-nightly": "docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:nightly --target nightly . --push", "build-docker-nightly-amd64": "docker buildx build --platform linux/amd64 -t louislam/uptime-kuma:nightly-amd64 --target nightly . --push", - "setup": "git checkout 1.0.6 && npm install && npm run build", + "setup": "git checkout 1.0.7 && npm install && npm run build", "version-global-replace": "node extra/version-global-replace.js", "mark-as-nightly": "node extra/mark-as-nightly.js" }, From cafd2c73882d662fd6f30a3856d131f70ca0567c Mon Sep 17 00:00:00 2001 From: LouisLam Date: Tue, 27 Jul 2021 16:52:44 +0800 Subject: [PATCH 31/31] add vue-fontawesone --- package-lock.json | 36 +++++++++++++++++++++++++++++++++++- package.json | 4 ++++ src/icon.js | 12 ++++++++++++ src/layouts/Layout.vue | 32 +++++++++++++++++++++++++------- src/main.js | 3 +++ src/pages/Dashboard.vue | 2 +- src/pages/Details.vue | 8 ++++---- 7 files changed, 84 insertions(+), 13 deletions(-) create mode 100644 src/icon.js diff --git a/package-lock.json b/package-lock.json index 99dd01b6b..86a6251bd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "uptime-kuma", - "version": "1.0.6", + "version": "1.0.7", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -29,6 +29,40 @@ "to-fast-properties": "^2.0.0" } }, + "@fortawesome/fontawesome-common-types": { + "version": "0.2.35", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-0.2.35.tgz", + "integrity": "sha512-IHUfxSEDS9dDGqYwIW7wTN6tn/O8E0n5PcAHz9cAaBoZw6UpG20IG/YM3NNLaGPwPqgjBAFjIURzqoQs3rrtuw==" + }, + "@fortawesome/fontawesome-svg-core": { + "version": "1.2.35", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-1.2.35.tgz", + "integrity": "sha512-uLEXifXIL7hnh2sNZQrIJWNol7cTVIzwI+4qcBIq9QWaZqUblm0IDrtSqbNg+3SQf8SMGHkiSigD++rHmCHjBg==", + "requires": { + "@fortawesome/fontawesome-common-types": "^0.2.35" + } + }, + "@fortawesome/free-regular-svg-icons": { + "version": "5.15.3", + "resolved": "https://registry.npmjs.org/@fortawesome/free-regular-svg-icons/-/free-regular-svg-icons-5.15.3.tgz", + "integrity": "sha512-q4/p8Xehy9qiVTdDWHL4Z+o5PCLRChePGZRTXkl+/Z7erDVL8VcZUuqzJjs6gUz6czss4VIPBRdCz6wP37/zMQ==", + "requires": { + "@fortawesome/fontawesome-common-types": "^0.2.35" + } + }, + "@fortawesome/free-solid-svg-icons": { + "version": "5.15.3", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-5.15.3.tgz", + "integrity": "sha512-XPeeu1IlGYqz4VWGRAT5ukNMd4VHUEEJ7ysZ7pSSgaEtNvSo+FLurybGJVmiqkQdK50OkSja2bfZXOeyMGRD8Q==", + "requires": { + "@fortawesome/fontawesome-common-types": "^0.2.35" + } + }, + "@fortawesome/vue-fontawesome": { + "version": "3.0.0-4", + "resolved": "https://registry.npmjs.org/@fortawesome/vue-fontawesome/-/vue-fontawesome-3.0.0-4.tgz", + "integrity": "sha512-dQVhhMRcUPCb0aqk5ohm0KGk5OJ7wFZ9aYapLzJB3Z+xs7LhkRWLTb87reelUAG5PFDjutDAXuloT9hi6cz72A==" + }, "@popperjs/core": { "version": "2.9.2", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.9.2.tgz", diff --git a/package.json b/package.json index d90840b9d..b708e4b59 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,10 @@ "mark-as-nightly": "node extra/mark-as-nightly.js" }, "dependencies": { + "@fortawesome/fontawesome-svg-core": "^1.2.35", + "@fortawesome/free-regular-svg-icons": "^5.15.3", + "@fortawesome/free-solid-svg-icons": "^5.15.3", + "@fortawesome/vue-fontawesome": "^3.0.0-4", "@popperjs/core": "^2.9.2", "args-parser": "^1.3.0", "axios": "^0.21.1", diff --git a/src/icon.js b/src/icon.js new file mode 100644 index 000000000..02c5d8180 --- /dev/null +++ b/src/icon.js @@ -0,0 +1,12 @@ +import { library } from '@fortawesome/fontawesome-svg-core' +//import { fa } from '@fortawesome/free-regular-svg-icons' +import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome' +import { faCog, faTachometerAlt, faEdit, faPlus, faPause, faPlay, faTrash, faList } from '@fortawesome/free-solid-svg-icons' + +// Add Free Font Awesome Icons here +// https://fontawesome.com/v5.15/icons?d=gallery&p=2&s=solid&m=free +library.add(faCog, faTachometerAlt, faEdit, faPlus, faPause, faPlay, faTrash, faList) + +export { + FontAwesomeIcon +} diff --git a/src/layouts/Layout.vue b/src/layouts/Layout.vue index 85109e12b..bc99854ac 100644 --- a/src/layouts/Layout.vue +++ b/src/layouts/Layout.vue @@ -14,8 +14,8 @@ @@ -44,10 +44,27 @@
@@ -99,7 +116,7 @@ export default { box-shadow: 0 15px 47px 0 rgba(0, 0, 0, 0.05), 0 5px 14px 0 rgba(0, 0, 0, 0.05); text-align: center; white-space: nowrap; - padding: 0 35px; + padding: 0 10px; a { text-align: center; @@ -144,6 +161,7 @@ main { footer { color: #AAA; font-size: 13px; + margin-top: 10px; margin-bottom: 30px; margin-left: 10px; text-align: center; diff --git a/src/main.js b/src/main.js index a44bef87c..59672f8d6 100644 --- a/src/main.js +++ b/src/main.js @@ -15,6 +15,7 @@ import Toast from "vue-toastification"; import "vue-toastification/dist/index.css"; import "bootstrap" import Setup from "./pages/Setup.vue"; +import {FontAwesomeIcon} from "./icon.js" const routes = [ { @@ -88,5 +89,7 @@ const options = { app.use(Toast, options); +app.component('font-awesome-icon', FontAwesomeIcon) + app.mount('#app') diff --git a/src/pages/Dashboard.vue b/src/pages/Dashboard.vue index 7084c467a..d24d9fc07 100644 --- a/src/pages/Dashboard.vue +++ b/src/pages/Dashboard.vue @@ -4,7 +4,7 @@
- Add New Monitor + Add New Monitor
diff --git a/src/pages/Details.vue b/src/pages/Details.vue index cc447bf44..b1f396132 100644 --- a/src/pages/Details.vue +++ b/src/pages/Details.vue @@ -11,10 +11,10 @@

- - - Edit - + + + Edit +
Valid To: {{ certInfo.validTo ? new Date(certInfo.validTo).toLocaleString() : "" }}
Days Remaining: