From ccda6f05f59ac273830c406733f98a0c7daa4557 Mon Sep 17 00:00:00 2001 From: Nelson Chan Date: Wed, 21 Jul 2021 12:09:09 +0800 Subject: [PATCH 01/37] 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 9a1bf6006ad07637ebece18703b5da10ef94f9ea Mon Sep 17 00:00:00 2001 From: Matthew Macdonald-Wallace Date: Thu, 22 Jul 2021 08:22:15 +0100 Subject: [PATCH 02/37] Add initial package import and config --- package.json | 3 +++ server/server.js | 12 ++++++++++++ 2 files changed, 15 insertions(+) diff --git a/package.json b/package.json index d4fe68885..4d3c56b92 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "args-parser": "^1.3.0", "axios": "^0.21.1", "bcrypt": "^5.0.1", + "bcryptjs": "^2.4.3", "bootstrap": "^5.0.2", "command-exists": "^1.2.9", "dayjs": "^1.10.6", @@ -31,8 +32,10 @@ "form-data": "^4.0.0", "http-graceful-shutdown": "^3.1.2", "jsonwebtoken": "^8.5.1", + "node-gyp": "^3.8.0", "nodemailer": "^6.6.3", "password-hash": "^1.2.2", + "prom-client": "^13.1.0", "redbean-node": "0.0.20", "socket.io": "^4.1.3", "socket.io-client": "^4.1.3", diff --git a/server/server.js b/server/server.js index 3c4d00295..085e90701 100644 --- a/server/server.js +++ b/server/server.js @@ -15,6 +15,7 @@ const gracefulShutdown = require('http-graceful-shutdown'); const Database = require("./database"); const {sleep} = require("./util"); const args = require('args-parser')(process.argv); +const prom_client = require('prom-client') const version = require('../package.json').version; const hostname = args.host || "0.0.0.0" @@ -32,6 +33,11 @@ app.use(express.json()) * Total WebSocket client connected to server currently, no actual use * @type {number} */ +console.log("Setting up the Prometheus Client") + +const collectDefaultMetrics = prom_client.collectDefaultMetrics; +collectDefaultMetrics({ prefix: 'uptimekuma' }); + let totalClient = 0; /** @@ -58,6 +64,12 @@ let needSetup = false; console.log("Adding route") app.use('/', express.static("dist")); + console.log("Adding /metrics") + app.get('/metrics', function (req, res) { + res.set('Content-Type', prom_client.register.contentType); + res.end(prom_client.register.metrics()); + }); + app.get('*', function(request, response, next) { response.sendFile(process.cwd() + '/dist/index.html'); }); From e3d4a896b158c3b13749c858feafb0333bbf70e8 Mon Sep 17 00:00:00 2001 From: Matthew Macdonald-Wallace Date: Thu, 22 Jul 2021 08:33:21 +0100 Subject: [PATCH 03/37] Fix up some formatting --- server/server.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/server.js b/server/server.js index 085e90701..c71dfaaae 100644 --- a/server/server.js +++ b/server/server.js @@ -66,9 +66,9 @@ let needSetup = false; console.log("Adding /metrics") app.get('/metrics', function (req, res) { - res.set('Content-Type', prom_client.register.contentType); - res.end(prom_client.register.metrics()); - }); + res.set('Content-Type', prom_client.register.contentType); + res.end(prom_client.register.metrics()); + }); app.get('*', function(request, response, next) { response.sendFile(process.cwd() + '/dist/index.html'); From 582fb2fe29dba6a3cbcc64139caeb843238cfcd8 Mon Sep 17 00:00:00 2001 From: Matthew Macdonald-Wallace Date: Thu, 22 Jul 2021 08:43:04 +0100 Subject: [PATCH 04/37] Export general metrics via the /metrics endpoint --- package-lock.json | 273 ++++++++++++++++++++++++++++++++++------------ package.json | 1 + server/server.js | 14 +-- 3 files changed, 205 insertions(+), 83 deletions(-) diff --git a/package-lock.json b/package-lock.json index 99dd01b6b..6604f6a86 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,16 +34,57 @@ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.9.2.tgz", "integrity": "sha512-VZMYa7+fXHdwIq1TDhSXoVmSPEGM/aa+6Aiq3nVVJ9bXr24zScr+NlKFKC3iPljA7ho/GAZr+d2jOf5GIRC30Q==" }, + "@types/accepts": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/accepts/-/accepts-1.3.5.tgz", + "integrity": "sha512-jOdnI/3qTpHABjM5cx1Hc0sKsPoYCp+DP/GJRGtDlPd7fiV9oXGGIcjW/ZOxLIvjGz8MA+uMZI9metHlgqbgwQ==", + "requires": { + "@types/node": "*" + } + }, + "@types/body-parser": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.1.tgz", + "integrity": "sha512-a6bTJ21vFOGIkwM0kzh9Yr89ziVxq4vYH2fQ6N8AeipEzai/cFK6aGMArIkUeIdRIgpwQa+2bXiLuUJCpSf2Cg==", + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, "@types/component-emitter": { "version": "1.2.10", "resolved": "https://registry.npmjs.org/@types/component-emitter/-/component-emitter-1.2.10.tgz", "integrity": "sha512-bsjleuRKWmGqajMerkzox19aGbscQX5rmmvvXl3wlIp5gMG1HgkiwPxsN5p070fBDKTNSPgojVbuY1+HWMbFhg==" }, + "@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "requires": { + "@types/node": "*" + } + }, + "@types/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@types/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-0mPF08jn9zYI0n0Q/Pnz7C4kThdSt+6LD4amsrYDDpgBfrVWa3TcCOxKX1zkGgYniGagRv8heN2cbh+CAn+uuQ==" + }, "@types/cookie": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==" }, + "@types/cookies": { + "version": "0.7.7", + "resolved": "https://registry.npmjs.org/@types/cookies/-/cookies-0.7.7.tgz", + "integrity": "sha512-h7BcvPUogWbKCzBR2lY4oqaZbO3jXZksexYJVFvkrFeLgbZjQkU4x8pRq6eg2MHXQhY0McQdqmmsxRWlVAHooA==", + "requires": { + "@types/connect": "*", + "@types/express": "*", + "@types/keygrip": "*", + "@types/node": "*" + } + }, "@types/cors": { "version": "2.8.12", "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz", @@ -55,11 +96,94 @@ "integrity": "sha512-LfZwXoGUDo0C3me81HXgkBg5CTQYb6xzEl+fNmbO4JdRiSKQ8A0GD1OBBvKAIsbCUgoyAty7m99GqqMQe784ew==", "dev": true }, + "@types/express": { + "version": "4.17.13", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", + "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==", + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "@types/express-serve-static-core": { + "version": "4.17.24", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.24.tgz", + "integrity": "sha512-3UJuW+Qxhzwjq3xhwXm2onQcFHn76frIYVbTu+kn24LFxI+dEhdfISDFovPB8VpEgW8oQCTpRuCe+0zJxB7NEA==", + "requires": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "@types/http-assert": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@types/http-assert/-/http-assert-1.5.1.tgz", + "integrity": "sha512-PGAK759pxyfXE78NbKxyfRcWYA/KwW17X290cNev/qAsn9eQIxkH4shoNBafH37wewhDG/0p1cHPbK6+SzZjWQ==" + }, + "@types/http-errors": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-1.8.1.tgz", + "integrity": "sha512-e+2rjEwK6KDaNOm5Aa9wNGgyS9oSZU/4pfSMMPYNOfjvFI0WVXm29+ITRFr6aKDvvKo7uU1jV68MW4ScsfDi7Q==" + }, + "@types/keygrip": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/keygrip/-/keygrip-1.0.2.tgz", + "integrity": "sha512-GJhpTepz2udxGexqos8wgaBx4I/zWIDPh/KOGEwAqtuGDkOUJu5eFvwmdBX4AmB8Odsr+9pHCQqiAqDL/yKMKw==" + }, + "@types/koa": { + "version": "2.13.4", + "resolved": "https://registry.npmjs.org/@types/koa/-/koa-2.13.4.tgz", + "integrity": "sha512-dfHYMfU+z/vKtQB7NUrthdAEiSvnLebvBjwHtfFmpZmB7em2N3WVQdHgnFq+xvyVgxW5jKDmjWfLD3lw4g4uTw==", + "requires": { + "@types/accepts": "*", + "@types/content-disposition": "*", + "@types/cookies": "*", + "@types/http-assert": "*", + "@types/http-errors": "*", + "@types/keygrip": "*", + "@types/koa-compose": "*", + "@types/node": "*" + } + }, + "@types/koa-compose": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/@types/koa-compose/-/koa-compose-3.2.5.tgz", + "integrity": "sha512-B8nG/OoE1ORZqCkBVsup/AKcvjdgoHnfi4pZMn5UwAPCbhk/96xyv284eBYW8JlQbQ7zDmnpFr68I/40mFoIBQ==", + "requires": { + "@types/koa": "*" + } + }, + "@types/mime": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.2.tgz", + "integrity": "sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==" + }, "@types/node": { "version": "16.3.3", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.3.3.tgz", "integrity": "sha512-8h7k1YgQKxKXWckzFCMfsIwn0Y61UK6tlD6y2lOb3hTOIMlK3t9/QwHOhc81TwU+RMf0As5fj7NPjroERCnejQ==" }, + "@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==" + }, + "@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==" + }, + "@types/serve-static": { + "version": "1.13.10", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.10.tgz", + "integrity": "sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==", + "requires": { + "@types/mime": "^1", + "@types/node": "*" + } + }, "@vitejs/plugin-legacy": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/@vitejs/plugin-legacy/-/plugin-legacy-1.4.4.tgz", @@ -260,7 +384,6 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "optional": true, "requires": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -341,7 +464,6 @@ "version": "0.2.4", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "optional": true, "requires": { "safer-buffer": "~2.1.0" } @@ -349,8 +471,7 @@ "assert-plus": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "optional": true + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" }, "assign-symbols": { "version": "1.0.0", @@ -375,14 +496,12 @@ "aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "optional": true + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" }, "aws4": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", - "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", - "optional": true + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" }, "axios": { "version": "0.21.1", @@ -535,7 +654,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "optional": true, "requires": { "tweetnacl": "^0.14.3" } @@ -557,11 +675,15 @@ "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true }, + "bintrees": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.1.tgz", + "integrity": "sha1-DmVcm5wkNeqraL9AJyJtK1WjRSQ=" + }, "block-stream": { "version": "0.0.9", "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", - "optional": true, "requires": { "inherits": "~2.0.0" } @@ -656,8 +778,7 @@ "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "optional": true + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" }, "chokidar": { "version": "3.5.2", @@ -826,7 +947,6 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "optional": true, "requires": { "assert-plus": "^1.0.0" } @@ -925,7 +1045,6 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "optional": true, "requires": { "jsbn": "~0.1.0", "safer-buffer": "^2.1.0" @@ -1217,20 +1336,17 @@ "extsprintf": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "optional": true + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "optional": true + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "optional": true + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, "fill-range": { "version": "7.0.1", @@ -1319,8 +1435,7 @@ "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "optional": true + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" }, "form-data": { "version": "4.0.0", @@ -1374,7 +1489,6 @@ "version": "1.0.12", "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", - "optional": true, "requires": { "graceful-fs": "^4.1.2", "inherits": "~2.0.0", @@ -1386,7 +1500,6 @@ "version": "0.5.5", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "optional": true, "requires": { "minimist": "^1.2.5" } @@ -1436,7 +1549,6 @@ "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "optional": true, "requires": { "assert-plus": "^1.0.0" } @@ -1488,20 +1600,17 @@ "graceful-fs": { "version": "4.2.6", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", - "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", - "optional": true + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==" }, "har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "optional": true + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" }, "har-validator": { "version": "5.1.5", "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "optional": true, "requires": { "ajv": "^6.12.3", "har-schema": "^2.0.0" @@ -1610,7 +1719,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "optional": true, "requires": { "assert-plus": "^1.0.0", "jsprim": "^1.2.2", @@ -1818,8 +1926,7 @@ "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "optional": true + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" }, "is-unc-path": { "version": "1.0.0", @@ -1852,32 +1959,27 @@ "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "optional": true + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" }, "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "optional": true + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" }, "json-schema": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "optional": true + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "optional": true + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "optional": true + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" }, "json5": { "version": "1.0.1", @@ -1909,7 +2011,6 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "optional": true, "requires": { "assert-plus": "1.0.0", "extsprintf": "1.3.0", @@ -2004,6 +2105,11 @@ "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=", "dev": true }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" + }, "lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", @@ -2384,7 +2490,6 @@ "version": "3.8.0", "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz", "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==", - "optional": true, "requires": { "fstream": "^1.0.0", "glob": "^7.0.3", @@ -2404,7 +2509,6 @@ "version": "0.5.5", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "optional": true, "requires": { "minimist": "^1.2.5" } @@ -2412,14 +2516,12 @@ "semver": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", - "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", - "optional": true + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=" }, "tar": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz", "integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==", - "optional": true, "requires": { "block-stream": "*", "fstream": "^1.0.12", @@ -2512,7 +2614,6 @@ "version": "3.0.6", "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", - "optional": true, "requires": { "abbrev": "1" } @@ -2565,8 +2666,7 @@ "oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "optional": true + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" }, "object-assign": { "version": "4.1.1", @@ -2743,8 +2843,7 @@ "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "optional": true + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" }, "pg-connection-string": { "version": "2.4.0", @@ -2757,6 +2856,11 @@ "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", "dev": true }, + "pkginfo": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.4.1.tgz", + "integrity": "sha1-tUGO8EOd5UJfxJlQQtztFPsqhP8=" + }, "posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", @@ -2845,6 +2949,37 @@ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" }, + "prom-client": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/prom-client/-/prom-client-13.1.0.tgz", + "integrity": "sha512-jT9VccZCWrJWXdyEtQddCDszYsiuWj5T0ekrPszi/WEegj3IZy6Mm09iOOVM86A4IKMWq8hZkT2dD9MaSe+sng==", + "requires": { + "tdigest": "^0.1.1" + } + }, + "prometheus-api-metrics": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/prometheus-api-metrics/-/prometheus-api-metrics-3.2.0.tgz", + "integrity": "sha512-JekPhtIBLGX8HxD2EndvBsLU6ZQ1JVVqyHWVfm5CposUOqgBHXnUVFW6x5Ux2gykpdej/5LLM3dU9V8Ma7GfkA==", + "requires": { + "@types/express": "^4.17.8", + "@types/express-serve-static-core": "^4.17.12", + "@types/koa": "^2.11.4", + "debug": "^3.2.6", + "lodash.get": "^4.4.2", + "pkginfo": "^0.4.1" + }, + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "requires": { + "ms": "^2.1.1" + } + } + } + }, "proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -2857,14 +2992,12 @@ "psl": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", - "optional": true + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "optional": true + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" }, "qs": { "version": "6.7.0", @@ -2979,7 +3112,6 @@ "version": "2.88.2", "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "optional": true, "requires": { "aws-sign2": "~0.7.0", "aws4": "^1.8.0", @@ -3007,7 +3139,6 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "optional": true, "requires": { "asynckit": "^0.4.0", "combined-stream": "^1.0.6", @@ -3017,8 +3148,7 @@ "qs": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "optional": true + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" } } }, @@ -3414,7 +3544,6 @@ "version": "1.16.1", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "optional": true, "requires": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", @@ -3529,6 +3658,14 @@ "resolved": "https://registry.npmjs.org/tcp-ping/-/tcp-ping-0.1.1.tgz", "integrity": "sha1-At1/QrW/fXy3jVt6rO+hVf2PfAw=" }, + "tdigest": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/tdigest/-/tdigest-0.1.1.tgz", + "integrity": "sha1-Ljyyw56kSeVdHmzZEReszKRYgCE=", + "requires": { + "bintrees": "1.0.1" + } + }, "tildify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/tildify/-/tildify-2.0.0.tgz", @@ -3586,7 +3723,6 @@ "version": "2.5.0", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "optional": true, "requires": { "psl": "^1.1.28", "punycode": "^2.1.1" @@ -3596,7 +3732,6 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "optional": true, "requires": { "safe-buffer": "^5.0.1" } @@ -3604,8 +3739,7 @@ "tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "optional": true + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" }, "type-is": { "version": "1.6.18", @@ -3677,7 +3811,6 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "optional": true, "requires": { "punycode": "^2.1.0" } @@ -3705,8 +3838,7 @@ "uuid": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "optional": true + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" }, "v-pagination-3": { "version": "0.1.6", @@ -3735,7 +3867,6 @@ "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "optional": true, "requires": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", diff --git a/package.json b/package.json index 4d3c56b92..6ade69682 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,7 @@ "nodemailer": "^6.6.3", "password-hash": "^1.2.2", "prom-client": "^13.1.0", + "prometheus-api-metrics": "^3.2.0", "redbean-node": "0.0.20", "socket.io": "^4.1.3", "socket.io-client": "^4.1.3", diff --git a/server/server.js b/server/server.js index c71dfaaae..816d55f69 100644 --- a/server/server.js +++ b/server/server.js @@ -15,8 +15,7 @@ const gracefulShutdown = require('http-graceful-shutdown'); const Database = require("./database"); const {sleep} = require("./util"); const args = require('args-parser')(process.argv); -const prom_client = require('prom-client') - +const apiMetrics = require('prometheus-api-metrics'); const version = require('../package.json').version; const hostname = args.host || "0.0.0.0" const port = args.port || 3001 @@ -33,11 +32,6 @@ app.use(express.json()) * Total WebSocket client connected to server currently, no actual use * @type {number} */ -console.log("Setting up the Prometheus Client") - -const collectDefaultMetrics = prom_client.collectDefaultMetrics; -collectDefaultMetrics({ prefix: 'uptimekuma' }); - let totalClient = 0; /** @@ -63,12 +57,8 @@ let needSetup = false; console.log("Adding route") app.use('/', express.static("dist")); + app.use(apiMetrics()) - console.log("Adding /metrics") - app.get('/metrics', function (req, res) { - res.set('Content-Type', prom_client.register.contentType); - res.end(prom_client.register.metrics()); - }); app.get('*', function(request, response, next) { response.sendFile(process.cwd() + '/dist/index.html'); From 7acb2655591c741af996eac1091119cca94fd6c5 Mon Sep 17 00:00:00 2001 From: Matthew Macdonald-Wallace Date: Thu, 22 Jul 2021 09:01:51 +0100 Subject: [PATCH 05/37] Remove bcryptjs and node-gyp, they should not be here... --- package-lock.json | 95 +++++++++++++++++++++++++++++++++++------------ package.json | 2 - 2 files changed, 71 insertions(+), 26 deletions(-) diff --git a/package-lock.json b/package-lock.json index 6604f6a86..bddd68e67 100644 --- a/package-lock.json +++ b/package-lock.json @@ -384,6 +384,7 @@ "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "optional": true, "requires": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -464,6 +465,7 @@ "version": "0.2.4", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "optional": true, "requires": { "safer-buffer": "~2.1.0" } @@ -471,7 +473,8 @@ "assert-plus": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "optional": true }, "assign-symbols": { "version": "1.0.0", @@ -496,12 +499,14 @@ "aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "optional": true }, "aws4": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", - "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==" + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", + "optional": true }, "axios": { "version": "0.21.1", @@ -654,6 +659,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "optional": true, "requires": { "tweetnacl": "^0.14.3" } @@ -684,6 +690,7 @@ "version": "0.0.9", "resolved": "https://registry.npmjs.org/block-stream/-/block-stream-0.0.9.tgz", "integrity": "sha1-E+v+d4oDIFz+A3UUgeu0szAMEmo=", + "optional": true, "requires": { "inherits": "~2.0.0" } @@ -778,7 +785,8 @@ "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "optional": true }, "chokidar": { "version": "3.5.2", @@ -947,6 +955,7 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "optional": true, "requires": { "assert-plus": "^1.0.0" } @@ -1045,6 +1054,7 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "optional": true, "requires": { "jsbn": "~0.1.0", "safer-buffer": "^2.1.0" @@ -1336,17 +1346,20 @@ "extsprintf": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "optional": true }, "fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "optional": true }, "fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "optional": true }, "fill-range": { "version": "7.0.1", @@ -1435,7 +1448,8 @@ "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "optional": true }, "form-data": { "version": "4.0.0", @@ -1489,6 +1503,7 @@ "version": "1.0.12", "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "optional": true, "requires": { "graceful-fs": "^4.1.2", "inherits": "~2.0.0", @@ -1500,6 +1515,7 @@ "version": "0.5.5", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "optional": true, "requires": { "minimist": "^1.2.5" } @@ -1549,6 +1565,7 @@ "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "optional": true, "requires": { "assert-plus": "^1.0.0" } @@ -1600,17 +1617,20 @@ "graceful-fs": { "version": "4.2.6", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", - "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==" + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==", + "optional": true }, "har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "optional": true }, "har-validator": { "version": "5.1.5", "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "optional": true, "requires": { "ajv": "^6.12.3", "har-schema": "^2.0.0" @@ -1719,6 +1739,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "optional": true, "requires": { "assert-plus": "^1.0.0", "jsprim": "^1.2.2", @@ -1926,7 +1947,8 @@ "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "optional": true }, "is-unc-path": { "version": "1.0.0", @@ -1959,27 +1981,32 @@ "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "optional": true }, "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "optional": true }, "json-schema": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "optional": true }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "optional": true }, "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "optional": true }, "json5": { "version": "1.0.1", @@ -2011,6 +2038,7 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "optional": true, "requires": { "assert-plus": "1.0.0", "extsprintf": "1.3.0", @@ -2490,6 +2518,7 @@ "version": "3.8.0", "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-3.8.0.tgz", "integrity": "sha512-3g8lYefrRRzvGeSowdJKAKyks8oUpLEd/DyPV4eMhVlhJ0aNaZqIrNUIPuEWWTAoPqyFkfGrM67MC69baqn6vA==", + "optional": true, "requires": { "fstream": "^1.0.0", "glob": "^7.0.3", @@ -2509,6 +2538,7 @@ "version": "0.5.5", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "optional": true, "requires": { "minimist": "^1.2.5" } @@ -2516,12 +2546,14 @@ "semver": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", - "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=" + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", + "optional": true }, "tar": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz", "integrity": "sha512-FCEhQ/4rE1zYv9rYXJw/msRqsnmlje5jHP6huWeBZ704jUTy02c5AZyWujpMR1ax6mVw9NyJMfuK2CMDWVIfgA==", + "optional": true, "requires": { "block-stream": "*", "fstream": "^1.0.12", @@ -2614,6 +2646,7 @@ "version": "3.0.6", "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", "integrity": "sha1-xkZdvwirzU2zWTF/eaxopkayj/k=", + "optional": true, "requires": { "abbrev": "1" } @@ -2666,7 +2699,8 @@ "oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "optional": true }, "object-assign": { "version": "4.1.1", @@ -2843,7 +2877,8 @@ "performance-now": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "optional": true }, "pg-connection-string": { "version": "2.4.0", @@ -2992,12 +3027,14 @@ "psl": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", + "optional": true }, "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "optional": true }, "qs": { "version": "6.7.0", @@ -3112,6 +3149,7 @@ "version": "2.88.2", "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "optional": true, "requires": { "aws-sign2": "~0.7.0", "aws4": "^1.8.0", @@ -3139,6 +3177,7 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "optional": true, "requires": { "asynckit": "^0.4.0", "combined-stream": "^1.0.6", @@ -3148,7 +3187,8 @@ "qs": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "optional": true } } }, @@ -3544,6 +3584,7 @@ "version": "1.16.1", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "optional": true, "requires": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", @@ -3723,6 +3764,7 @@ "version": "2.5.0", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "optional": true, "requires": { "psl": "^1.1.28", "punycode": "^2.1.1" @@ -3732,6 +3774,7 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "optional": true, "requires": { "safe-buffer": "^5.0.1" } @@ -3739,7 +3782,8 @@ "tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "optional": true }, "type-is": { "version": "1.6.18", @@ -3811,6 +3855,7 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "optional": true, "requires": { "punycode": "^2.1.0" } @@ -3838,7 +3883,8 @@ "uuid": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "optional": true }, "v-pagination-3": { "version": "0.1.6", @@ -3867,6 +3913,7 @@ "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "optional": true, "requires": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", diff --git a/package.json b/package.json index 6ade69682..94c73d2f4 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,6 @@ "args-parser": "^1.3.0", "axios": "^0.21.1", "bcrypt": "^5.0.1", - "bcryptjs": "^2.4.3", "bootstrap": "^5.0.2", "command-exists": "^1.2.9", "dayjs": "^1.10.6", @@ -32,7 +31,6 @@ "form-data": "^4.0.0", "http-graceful-shutdown": "^3.1.2", "jsonwebtoken": "^8.5.1", - "node-gyp": "^3.8.0", "nodemailer": "^6.6.3", "password-hash": "^1.2.2", "prom-client": "^13.1.0", From 96c60dd94aa94c09ddd3039e61ea8eed433a082b Mon Sep 17 00:00:00 2001 From: Nelson Chan Date: Thu, 22 Jul 2021 16:04:32 +0800 Subject: [PATCH 06/37] 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/37] 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 96242dce0dc8a816b326b0c1daa2fda39c9f92e1 Mon Sep 17 00:00:00 2001 From: Matthew Macdonald-Wallace Date: Thu, 22 Jul 2021 09:38:27 +0100 Subject: [PATCH 08/37] Expose check status and response time to Prometheus --- server/model/monitor.js | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/server/model/monitor.js b/server/model/monitor.js index 04feea6b0..ca9d6c9f1 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -1,4 +1,4 @@ - +const Prometheus = require('prom-client'); const dayjs = require("dayjs"); const utc = require('dayjs/plugin/utc') var timezone = require('dayjs/plugin/timezone') @@ -10,6 +10,16 @@ const {R} = require("redbean-node"); const {BeanModel} = require("redbean-node/dist/bean-model"); const {Notification} = require("../notification") +const monitor_response_time = new Prometheus.Gauge({ + name: 'monitor_response_time', + help: 'Monitor Response Time (ms)', + labelNames: ['monitor_name'] +}); +const monitor_status = new Prometheus.Gauge({ + name: 'montor_status', + help: 'Monitor Status (1 = UP, 0= DOWN)', + labelNames: ['monitor_name'] +}); /** * status: * 0 = DOWN @@ -143,12 +153,21 @@ class Monitor extends BeanModel { bean.important = false; } + + monitor_status.set({ + monitor_name: this.name + }, bean.status) + if (bean.status === 1) { console.info(`Monitor #${this.id} '${this.name}': Successful Response: ${bean.ping} ms | Interval: ${this.interval} seconds | Type: ${this.type}`) } else { console.warn(`Monitor #${this.id} '${this.name}': Failing: ${bean.msg} | Type: ${this.type}`) } + monitor_response_time.set({ + monitor_name: this.name + }, bean.ping) + io.to(this.user_id).emit("heartbeat", bean.toJSON()); await R.store(bean) From 3dcbae0889c02e46c25fdcc91bffa2d20d895c7c Mon Sep 17 00:00:00 2001 From: Matthew Macdonald-Wallace Date: Thu, 22 Jul 2021 10:21:20 +0100 Subject: [PATCH 09/37] Add labels to metrics for querying --- server/model/monitor.js | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/server/model/monitor.js b/server/model/monitor.js index ca9d6c9f1..5ced52f21 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -13,12 +13,24 @@ const {Notification} = require("../notification") const monitor_response_time = new Prometheus.Gauge({ name: 'monitor_response_time', help: 'Monitor Response Time (ms)', - labelNames: ['monitor_name'] + labelNames: [ + 'monitor_name', + 'monitor_type', + 'monitor_url', + 'monitor_hostname', + 'monitor_port' + ] }); const monitor_status = new Prometheus.Gauge({ name: 'montor_status', help: 'Monitor Status (1 = UP, 0= DOWN)', - labelNames: ['monitor_name'] + labelNames: [ + 'monitor_name', + 'monitor_type', + 'monitor_url', + 'monitor_hostname', + 'monitor_port' + ] }); /** * status: @@ -155,7 +167,11 @@ class Monitor extends BeanModel { monitor_status.set({ - monitor_name: this.name + monitor_name: this.name, + monitor_type: this.type, + monitor_url: this.url, + monitor_hostname: this.hostname, + monitor_port: this.port }, bean.status) if (bean.status === 1) { @@ -165,7 +181,11 @@ class Monitor extends BeanModel { } monitor_response_time.set({ - monitor_name: this.name + monitor_name: this.name, + monitor_type: this.type, + monitor_url: this.url, + monitor_hostname: this.hostname, + monitor_port: this.port }, bean.ping) io.to(this.user_id).emit("heartbeat", bean.toJSON()); From 720051a3511be2277708f25b6a83c9a1e8d7e7cc Mon Sep 17 00:00:00 2001 From: Matthew Macdonald-Wallace Date: Thu, 22 Jul 2021 11:18:20 +0100 Subject: [PATCH 10/37] Typo in monitor status name --- 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 5ced52f21..c2551d189 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -22,7 +22,7 @@ const monitor_response_time = new Prometheus.Gauge({ ] }); const monitor_status = new Prometheus.Gauge({ - name: 'montor_status', + name: 'monitor_status', help: 'Monitor Status (1 = UP, 0= DOWN)', labelNames: [ 'monitor_name', From 3b450065679e27617f7c012392c1c97e68605760 Mon Sep 17 00:00:00 2001 From: Matthew Macdonald-Wallace Date: Thu, 22 Jul 2021 14:58:22 +0100 Subject: [PATCH 11/37] Move common labels into dedicated const --- server/model/monitor.js | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/server/model/monitor.js b/server/model/monitor.js index c2551d189..ca994d7e2 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -10,27 +10,23 @@ const {R} = require("redbean-node"); const {BeanModel} = require("redbean-node/dist/bean-model"); const {Notification} = require("../notification") +const commonLabels = [ + 'monitor_name', + 'monitor_type', + 'monitor_url', + 'monitor_hostname', + 'monitor_port', +] + const monitor_response_time = new Prometheus.Gauge({ name: 'monitor_response_time', help: 'Monitor Response Time (ms)', - labelNames: [ - 'monitor_name', - 'monitor_type', - 'monitor_url', - 'monitor_hostname', - 'monitor_port' - ] + labelNames: commonLabels }); const monitor_status = new Prometheus.Gauge({ name: 'monitor_status', help: 'Monitor Status (1 = UP, 0= DOWN)', - labelNames: [ - 'monitor_name', - 'monitor_type', - 'monitor_url', - 'monitor_hostname', - 'monitor_port' - ] + labelNames: commonLabels }); /** * status: From a93fd274fdb576f35550acbde2d8b10f9d83d76b Mon Sep 17 00:00:00 2001 From: Matthew Macdonald-Wallace Date: Thu, 22 Jul 2021 15:02:33 +0100 Subject: [PATCH 12/37] Update README to include examples for Prometheus --- README.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/README.md b/README.md index 7777cb2c5..e49ef0a8d 100644 --- a/README.md +++ b/README.md @@ -86,6 +86,35 @@ npm run build pm2 restart uptime-kuma ``` +# Passing metrics to other platforms + +If you already use [Prometheus.io](https://prometheus.io) or a platform that supports Prometheus exporter format, you can get the metrics about each monitoring target from `http://:/metrics`. + +Labels to filter by include: + +| Label Name | Description | ++------------+-------------+ +|monitor_name| The "Friendly Name" of the monitor | +|monitor_type| The type (http, keyword, tcp) of monitoring check | +|monitor_url | The URL to be monitored (http, keyword) +|monitor_hostname | The Hostname to be monitored (tcp) | +|monitor_port | The port to be monitored (tcp) | + +## Example PromQL queries + +Assuming we have http monitors in place for bbc.co.uk and google.com: + +``` +# Show all response rates gouped by site +sum(monitor_response_time) by (monitor_name) + +# Show only the response time for BBC.co.uk +sum(monitor_reponse_time{monitor_url="https://www.bbc.co.uk/"}) + +# Show the current status of Google.com +monitor_status{monitor_name="Google"} +``` + # What's Next? I will mark requests/issues to the next milestone. From 3a8fbff514434c1f00a6a08233c93d2837db4a2b Mon Sep 17 00:00:00 2001 From: Matthew Macdonald-Wallace Date: Thu, 22 Jul 2021 16:00:11 +0100 Subject: [PATCH 13/37] Change casing in README, apply DRY to label values --- README.md | 10 +++++----- server/model/monitor.js | 26 ++++++++++++-------------- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index e49ef0a8d..76373fd2b 100644 --- a/README.md +++ b/README.md @@ -95,14 +95,14 @@ Labels to filter by include: | Label Name | Description | +------------+-------------+ |monitor_name| The "Friendly Name" of the monitor | -|monitor_type| The type (http, keyword, tcp) of monitoring check | -|monitor_url | The URL to be monitored (http, keyword) -|monitor_hostname | The Hostname to be monitored (tcp) | -|monitor_port | The port to be monitored (tcp) | +|monitor_type| The type (HTTP, keyword, TCP) of monitoring check | +|monitor_url | The URL to be monitored (HTTP, keyword) +|monitor_hostname | The Hostname to be monitored (TCP) | +|monitor_port | The port to be monitored (TCP) | ## Example PromQL queries -Assuming we have http monitors in place for bbc.co.uk and google.com: +Assuming we have HTTP monitors in place for bbc.co.uk and google.com: ``` # Show all response rates gouped by site diff --git a/server/model/monitor.js b/server/model/monitor.js index ca994d7e2..c366869b7 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -18,6 +18,7 @@ const commonLabels = [ 'monitor_port', ] + const monitor_response_time = new Prometheus.Gauge({ name: 'monitor_response_time', help: 'Monitor Response Time (ms)', @@ -65,6 +66,15 @@ class Monitor extends BeanModel { start(io) { let previousBeat = null; + const monitorLabelValues = { + monitor_name: this.name, + monitor_type: this.type, + monitor_url: this.url, + monitor_hostname: this.hostname, + monitor_port: this.port + } + + const beat = async () => { if (! previousBeat) { previousBeat = await R.findOne("heartbeat", " monitor_id = ? ORDER BY time DESC", [ @@ -162,13 +172,7 @@ class Monitor extends BeanModel { } - monitor_status.set({ - monitor_name: this.name, - monitor_type: this.type, - monitor_url: this.url, - monitor_hostname: this.hostname, - monitor_port: this.port - }, bean.status) + monitor_status.set(monitorLabelValues, bean.status) if (bean.status === 1) { console.info(`Monitor #${this.id} '${this.name}': Successful Response: ${bean.ping} ms | Interval: ${this.interval} seconds | Type: ${this.type}`) @@ -176,13 +180,7 @@ class Monitor extends BeanModel { console.warn(`Monitor #${this.id} '${this.name}': Failing: ${bean.msg} | Type: ${this.type}`) } - monitor_response_time.set({ - monitor_name: this.name, - monitor_type: this.type, - monitor_url: this.url, - monitor_hostname: this.hostname, - monitor_port: this.port - }, bean.ping) + monitor_response_time.set(monitorLabelValues, bean.ping) io.to(this.user_id).emit("heartbeat", bean.toJSON()); From 47d830db1f76a29d9966fc49cbbcafe1f47aee10 Mon Sep 17 00:00:00 2001 From: Matthew Macdonald-Wallace Date: Thu, 22 Jul 2021 16:15:19 +0100 Subject: [PATCH 14/37] Remove examples so they can go on the wiki instead --- README.md | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/README.md b/README.md index 76373fd2b..301fb35bc 100644 --- a/README.md +++ b/README.md @@ -100,21 +100,6 @@ Labels to filter by include: |monitor_hostname | The Hostname to be monitored (TCP) | |monitor_port | The port to be monitored (TCP) | -## Example PromQL queries - -Assuming we have HTTP monitors in place for bbc.co.uk and google.com: - -``` -# Show all response rates gouped by site -sum(monitor_response_time) by (monitor_name) - -# Show only the response time for BBC.co.uk -sum(monitor_reponse_time{monitor_url="https://www.bbc.co.uk/"}) - -# Show the current status of Google.com -monitor_status{monitor_name="Google"} -``` - # What's Next? I will mark requests/issues to the next milestone. From 4d262bbb6a07231aa0c9304f7ac56c59919257ac Mon Sep 17 00:00:00 2001 From: Nelson Chan Date: Fri, 23 Jul 2021 11:22:37 +0800 Subject: [PATCH 15/37] 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 16/37] 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 48f82b55f86c847d3aed69604a58eb13c4114f1d Mon Sep 17 00:00:00 2001 From: LouisLam Date: Fri, 23 Jul 2021 12:58:05 +0800 Subject: [PATCH 17/37] 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 803f0d6219ae84838cc6884c5a9dec379c6bc1ff Mon Sep 17 00:00:00 2001 From: Nelson Chan Date: Wed, 21 Jul 2021 12:09:09 +0800 Subject: [PATCH 18/37] 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 19/37] 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 20/37] 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 21/37] 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 22/37] 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 23/37] 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 24/37] 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 25/37] 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 26/37] 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 27/37] 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 28/37] 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 29/37] 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 +
From 46337ec34870afb0f1d93ff8507369c6062645f9 Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Tue, 27 Jul 2021 18:04:55 +0800 Subject: [PATCH 30/37] Update issue templates --- .github/ISSUE_TEMPLATE/help.md | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/help.md diff --git a/.github/ISSUE_TEMPLATE/help.md b/.github/ISSUE_TEMPLATE/help.md new file mode 100644 index 000000000..4b257072f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/help.md @@ -0,0 +1,10 @@ +--- +name: Help +about: Help +title: '' +labels: help +assignees: '' + +--- + + From fade240c7fba13c39eed48c0ac707115719b6d3f Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Tue, 27 Jul 2021 18:05:11 +0800 Subject: [PATCH 31/37] Delete --please-go-to--discussion--tab-if-you-want-to-ask-or-share-something.md --- ...ssion--tab-if-you-want-to-ask-or-share-something.md | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/--please-go-to--discussion--tab-if-you-want-to-ask-or-share-something.md diff --git a/.github/ISSUE_TEMPLATE/--please-go-to--discussion--tab-if-you-want-to-ask-or-share-something.md b/.github/ISSUE_TEMPLATE/--please-go-to--discussion--tab-if-you-want-to-ask-or-share-something.md deleted file mode 100644 index eb8623709..000000000 --- a/.github/ISSUE_TEMPLATE/--please-go-to--discussion--tab-if-you-want-to-ask-or-share-something.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -name: ⚠ Please go to "Discussions" Tab if you want to ask or share something -about: BUG REPORT ONLY HERE -title: '' -labels: '' -assignees: '' - ---- - -BUG REPORT ONLY HERE From 7edd79a74d100e6f82eaa85203d0ef919549f21f Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Tue, 27 Jul 2021 18:06:26 +0800 Subject: [PATCH 32/37] Update issue templates --- .github/ISSUE_TEMPLATE/ask-for-help.md | 10 ++++++++++ .github/ISSUE_TEMPLATE/feature_request.md | 20 ++++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/ask-for-help.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/ask-for-help.md b/.github/ISSUE_TEMPLATE/ask-for-help.md new file mode 100644 index 000000000..fd25e4133 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/ask-for-help.md @@ -0,0 +1,10 @@ +--- +name: Ask for Help +about: You can ask any question related to Uptime Kuma. +title: '' +labels: help +assignees: '' + +--- + + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..11fc491ef --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: enhancement +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. From ef45aedda52aeeb5ef252df94827418440e02950 Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Tue, 27 Jul 2021 18:18:27 +0800 Subject: [PATCH 33/37] Delete help.md --- .github/ISSUE_TEMPLATE/help.md | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/help.md diff --git a/.github/ISSUE_TEMPLATE/help.md b/.github/ISSUE_TEMPLATE/help.md deleted file mode 100644 index 4b257072f..000000000 --- a/.github/ISSUE_TEMPLATE/help.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -name: Help -about: Help -title: '' -labels: help -assignees: '' - ---- - - From e6ca1056008f4355bd90a6ed68ee9a97b1206b4c Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Tue, 27 Jul 2021 18:18:38 +0800 Subject: [PATCH 34/37] Update ask-for-help.md --- .github/ISSUE_TEMPLATE/ask-for-help.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/ask-for-help.md b/.github/ISSUE_TEMPLATE/ask-for-help.md index fd25e4133..c3657267a 100644 --- a/.github/ISSUE_TEMPLATE/ask-for-help.md +++ b/.github/ISSUE_TEMPLATE/ask-for-help.md @@ -1,5 +1,5 @@ --- -name: Ask for Help +name: Ask for help about: You can ask any question related to Uptime Kuma. title: '' labels: help From 0f0a6299c0ad1fca8b1ee3f4c775e1135b7cca34 Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Tue, 27 Jul 2021 20:25:59 +0800 Subject: [PATCH 35/37] Create codeql-analysis.yml --- .github/workflows/codeql-analysis.yml | 71 +++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 .github/workflows/codeql-analysis.yml diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 000000000..3949d2495 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,71 @@ +# For most projects, this workflow file will not need changing; you simply need +# to commit it to your repository. +# +# You may wish to alter this file to override the set of languages analyzed, +# or to provide custom queries or build logic. +# +# ******** NOTE ******** +# We have attempted to detect the languages in your repository. Please check +# the `language` matrix defined below to confirm you have the correct set of +# supported CodeQL languages. +# +name: "CodeQL" + +on: + push: + branches: [ master ] + pull_request: + # The branches below must be a subset of the branches above + branches: [ master ] + schedule: + - cron: '35 5 * * 2' + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'javascript' ] + # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] + # Learn more: + # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed + + steps: + - name: Checkout repository + uses: actions/checkout@v2 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@v1 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + # queries: ./path/to/local/query, your-org/your-repo/queries@main + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@v1 + + # ℹī¸ Command-line programs to run using the OS shell. + # 📚 https://git.io/JvXDl + + # ✏ī¸ If the Autobuild fails above, remove it and uncomment the following three lines + # and modify them (or add more) to build your code if your project + # uses a compiled language + + #- run: | + # make bootstrap + # make release + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v1 From 36436ed4ef2caf729253a78d1b1c85a2e6c53c31 Mon Sep 17 00:00:00 2001 From: LouisLam Date: Tue, 27 Jul 2021 23:14:13 +0800 Subject: [PATCH 36/37] Move all Prometheus guides to wiki --- README.md | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/README.md b/README.md index 811be0502..ef8211368 100644 --- a/README.md +++ b/README.md @@ -86,20 +86,6 @@ npm run build pm2 restart uptime-kuma ``` -# Passing metrics to other platforms - -If you already use [Prometheus.io](https://prometheus.io) or a platform that supports Prometheus exporter format, you can get the metrics about each monitoring target from `http://:/metrics`. - -Labels to filter by include: - -| Label Name | Description | -+------------+-------------+ -|monitor_name| The "Friendly Name" of the monitor | -|monitor_type| The type (HTTP, keyword, TCP) of monitoring check | -|monitor_url | The URL to be monitored (HTTP, keyword) -|monitor_hostname | The Hostname to be monitored (TCP) | -|monitor_port | The port to be monitored (TCP) | - # What's Next? I will mark requests/issues to the next milestone. From 209fa83cff4b35a955ba202c5079fdb9bad1cf57 Mon Sep 17 00:00:00 2001 From: LouisLam Date: Wed, 28 Jul 2021 00:52:31 +0800 Subject: [PATCH 37/37] Add Basic Auth for /metrics --- package-lock.json | 16 +++++++++++ package.json | 1 + server/auth.js | 40 ++++++++++++++++++++++++++++ server/model/monitor.js | 33 +++-------------------- server/prometheus.js | 59 +++++++++++++++++++++++++++++++++++++++++ server/server.js | 39 +++++++++++++++------------ 6 files changed, 141 insertions(+), 47 deletions(-) create mode 100644 server/auth.js create mode 100644 server/prometheus.js diff --git a/package-lock.json b/package-lock.json index 0a2a3a469..2fcc0feec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -625,6 +625,14 @@ "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==" }, + "basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "requires": { + "safe-buffer": "5.1.2" + } + }, "bcrypt": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.0.1.tgz", @@ -1294,6 +1302,14 @@ } } }, + "express-basic-auth": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/express-basic-auth/-/express-basic-auth-1.2.0.tgz", + "integrity": "sha512-iJ0h1Gk6fZRrFmO7tP9nIbxwNgCUJASfNj5fb0Hy15lGtbqqsxpt7609+wq+0XlByZjXmC/rslWQtnuSTVRIcg==", + "requires": { + "basic-auth": "^2.0.1" + } + }, "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", diff --git a/package.json b/package.json index f0af413e0..b54c73ff9 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "command-exists": "^1.2.9", "dayjs": "^1.10.6", "express": "^4.17.1", + "express-basic-auth": "^1.2.0", "form-data": "^4.0.0", "http-graceful-shutdown": "^3.1.2", "jsonwebtoken": "^8.5.1", diff --git a/server/auth.js b/server/auth.js new file mode 100644 index 000000000..b4d13d68a --- /dev/null +++ b/server/auth.js @@ -0,0 +1,40 @@ +const basicAuth = require('express-basic-auth') +const passwordHash = require('./password-hash'); +const {R} = require("redbean-node"); + +/** + * + * @param username : string + * @param password : string + * @returns {Promise} + */ +exports.login = async function (username, password) { + let user = await R.findOne("user", " username = ? AND active = 1 ", [ + username + ]) + + if (user && passwordHash.verify(password, user.password)) { + // Upgrade the hash to bcrypt + if (passwordHash.needRehash(user.password)) { + await R.exec("UPDATE `user` SET password = ? WHERE id = ? ", [ + passwordHash.generate(password), + user.id + ]); + } + return user; + } else { + return null; + } +} + +function myAuthorizer(username, password, callback) { + exports.login(username, password).then((user) => { + callback(null, user != null) + }) +} + +exports.basicAuth = basicAuth({ + authorizer: myAuthorizer, + authorizeAsync: true, + challenge: true +}); diff --git a/server/model/monitor.js b/server/model/monitor.js index 552149e57..27f3f9bab 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -1,4 +1,3 @@ -const Prometheus = require('prom-client'); const https = require('https'); const dayjs = require("dayjs"); const utc = require('dayjs/plugin/utc') @@ -6,6 +5,7 @@ var timezone = require('dayjs/plugin/timezone') dayjs.extend(utc) dayjs.extend(timezone) const axios = require("axios"); +const {Prometheus} = require("../prometheus"); const {debug, UP, DOWN, PENDING} = require("../util"); const {tcping, ping, checkCertificate} = require("../util-server"); const {R} = require("redbean-node"); @@ -18,26 +18,6 @@ const customAgent = new https.Agent({ maxCachedSessions: 0 }); -const commonLabels = [ - 'monitor_name', - 'monitor_type', - 'monitor_url', - 'monitor_hostname', - 'monitor_port', -] - -const monitor_response_time = new Prometheus.Gauge({ - name: 'monitor_response_time', - help: 'Monitor Response Time (ms)', - labelNames: commonLabels -}); - -const monitor_status = new Prometheus.Gauge({ - name: 'monitor_status', - help: 'Monitor Status (1 = UP, 0= DOWN)', - labelNames: commonLabels -}); - /** * status: * 0 = DOWN @@ -76,13 +56,7 @@ class Monitor extends BeanModel { let previousBeat = null; let retries = 0; - const monitorLabelValues = { - monitor_name: this.name, - monitor_type: this.type, - monitor_url: this.url, - monitor_hostname: this.hostname, - monitor_port: this.port - } + let prometheus = new Prometheus(this); const beat = async () => { @@ -219,7 +193,6 @@ class Monitor extends BeanModel { bean.important = false; } - monitor_status.set(monitorLabelValues, bean.status) if (bean.status === UP) { console.info(`Monitor #${this.id} '${this.name}': Successful Response: ${bean.ping} ms | Interval: ${this.interval} seconds | Type: ${this.type}`) @@ -229,7 +202,7 @@ class Monitor extends BeanModel { console.warn(`Monitor #${this.id} '${this.name}': Failing: ${bean.msg} | Type: ${this.type}`) } - monitor_response_time.set(monitorLabelValues, bean.ping) + prometheus.update(bean) io.to(this.user_id).emit("heartbeat", bean.toJSON()); diff --git a/server/prometheus.js b/server/prometheus.js new file mode 100644 index 000000000..f60ec45a6 --- /dev/null +++ b/server/prometheus.js @@ -0,0 +1,59 @@ +const PrometheusClient = require('prom-client'); + +const commonLabels = [ + 'monitor_name', + 'monitor_type', + 'monitor_url', + 'monitor_hostname', + 'monitor_port', +] + +const monitor_response_time = new PrometheusClient.Gauge({ + name: 'monitor_response_time', + help: 'Monitor Response Time (ms)', + labelNames: commonLabels +}); + +const monitor_status = new PrometheusClient.Gauge({ + name: 'monitor_status', + help: 'Monitor Status (1 = UP, 0= DOWN)', + labelNames: commonLabels +}); + +class Prometheus { + monitorLabelValues = {} + + constructor(monitor) { + this.monitorLabelValues = { + monitor_name: monitor.name, + monitor_type: monitor.type, + monitor_url: monitor.url, + monitor_hostname: monitor.hostname, + monitor_port: monitor.port + } + } + + update(heartbeat) { + try { + monitor_status.set(this.monitorLabelValues, heartbeat.status) + } catch (e) { + console.error(e) + } + + try { + if (typeof heartbeat.ping === 'number') { + monitor_response_time.set(this.monitorLabelValues, heartbeat.ping) + } else { + // Is it good? + monitor_response_time.set(this.monitorLabelValues, -1) + } + } catch (e) { + console.error(e) + } + } + +} + +module.exports = { + Prometheus +} diff --git a/server/server.js b/server/server.js index 624e69be2..2014d927f 100644 --- a/server/server.js +++ b/server/server.js @@ -5,7 +5,6 @@ const http = require('http'); const { Server } = require("socket.io"); const dayjs = require("dayjs"); const {R} = require("redbean-node"); -const passwordHash = require('./password-hash'); const jwt = require('jsonwebtoken'); const Monitor = require("./model/monitor"); const fs = require("fs"); @@ -15,7 +14,9 @@ const gracefulShutdown = require('http-graceful-shutdown'); const Database = require("./database"); const {sleep} = require("./util"); const args = require('args-parser')(process.argv); -const apiMetrics = require('prometheus-api-metrics'); +const prometheusAPIMetrics = require('prometheus-api-metrics'); +const { basicAuth } = require("./auth"); +const {login} = require("./auth"); const version = require('../package.json').version; const hostname = args.host || "0.0.0.0" const port = args.port || 3001 @@ -27,6 +28,9 @@ const app = express(); const server = http.createServer(app); const io = new Server(server); app.use(express.json()) +const basicAuthRouter = express.Router(); +basicAuthRouter.use(basicAuth) +app.use(basicAuthRouter) /** * Total WebSocket client connected to server currently, no actual use @@ -56,15 +60,27 @@ let needSetup = false; await initDatabase(); console.log("Adding route") + + // Normal Router here + app.use('/', express.static("dist")); - app.use(apiMetrics()) + // Basic Auth Router here + + // For testing + basicAuthRouter.get('/test-auth', (req, res) => { + res.end("OK") + }); + + // Prometheus API metrics /metrics + // With Basic Auth using the first user's username/password + basicAuthRouter.use(prometheusAPIMetrics()) + // Universal Route Handler, must be at the end app.get('*', function(request, response, next) { response.sendFile(process.cwd() + '/dist/index.html'); }); - console.log("Adding socket handler") io.on('connection', async (socket) => { @@ -120,20 +136,9 @@ let needSetup = false; socket.on("login", async (data, callback) => { console.log("Login") - let user = await R.findOne("user", " username = ? AND active = 1 ", [ - data.username - ]) - - if (user && passwordHash.verify(data.password, user.password)) { - - // Upgrade the hash to bcrypt - if (passwordHash.needRehash(user.password)) { - await R.exec("UPDATE `user` SET password = ? WHERE id = ? ", [ - passwordHash.generate(data.password), - user.id - ]); - } + let user = await login(data.username, data.password) + if (user) { await afterLogin(socket, user) callback({
Valid To: {{ certInfo.validTo ? new Date(certInfo.validTo).toLocaleString() : "" }}
Days Remaining: