diff --git a/db/patch-monitor-tls.sql b/db/patch-monitor-tls.sql new file mode 100644 index 000000000..ac4edb798 --- /dev/null +++ b/db/patch-monitor-tls.sql @@ -0,0 +1,13 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + +ALTER TABLE monitor + ADD tls_ca TEXT default null; + +ALTER TABLE monitor + ADD tls_cert TEXT default null; + +ALTER TABLE monitor + ADD tls_key TEXT default null; + +COMMIT; diff --git a/extra/deploy-demo-server.js b/extra/deploy-demo-server.js index d5a517f90..2cc196b39 100644 --- a/extra/deploy-demo-server.js +++ b/extra/deploy-demo-server.js @@ -43,10 +43,11 @@ const prompt = (query) => new Promise((resolve) => rl.question(query, resolve)); }); console.log(result.stdout + result.stderr); + /* result = await ssh.execCommand("pm2 restart 1", { cwd, }); - console.log(result.stdout + result.stderr); + console.log(result.stdout + result.stderr);*/ } catch (e) { console.log(e); diff --git a/extra/sort-contributors.js b/extra/sort-contributors.js index 418bc233d..b60d191f6 100644 --- a/extra/sort-contributors.js +++ b/extra/sort-contributors.js @@ -13,7 +13,7 @@ lines = lines.filter((line) => line !== ""); lines = [ ...new Set(lines) ]; // Remove @weblate and @UptimeKumaBot -lines = lines.filter((line) => line !== "@weblate" && line !== "@UptimeKumaBot"); +lines = lines.filter((line) => line !== "@weblate" && line !== "@UptimeKumaBot" && line !== "@louislam"); // Sort the lines lines = lines.sort(); diff --git a/package-lock.json b/package-lock.json index d568f5d3d..060a42fb9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,16 @@ { "name": "uptime-kuma", - "version": "1.21.0-beta.0", + "version": "1.21.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "uptime-kuma", - "version": "1.21.0-beta.0", + "version": "1.21.0", "license": "MIT", "dependencies": { "@grpc/grpc-js": "~1.7.3", - "@louislam/ping": "~0.4.2-mod.2", + "@louislam/ping": "~0.4.4-mod.0", "@louislam/sqlite3": "15.1.2", "args-parser": "~1.3.0", "axios": "~0.27.0", @@ -4213,13 +4213,11 @@ "integrity": "sha512-retLUN4TwCJ0QJDi9OCJwYVaXAz93NeOkEtEQL98M2bykBOxmURlP0YlfsuE46kItOOVZIWRYC3KsSLhQ1R2Qw==" }, "node_modules/@louislam/ping": { - "version": "0.4.2-mod.2", - "resolved": "https://registry.npmjs.org/@louislam/ping/-/ping-0.4.2-mod.2.tgz", - "integrity": "sha512-4krrRGohYdhQOD+Mt0Q8e1Z05DEKntZ7TgiY1jYaqWrMz0H2XJyRh+mLPOUVPL5zSymiHsZiK2ZACXtp/d9Wxg==", + "version": "0.4.4-mod.0", + "resolved": "https://registry.npmjs.org/@louislam/ping/-/ping-0.4.4-mod.0.tgz", + "integrity": "sha512-U2ZXcgFRPmZYd/ooA8KILG4aCMBsDrGP9NDWseHriZSsKlu5Y1lf/LbenN6tnqQ9JjAsbJjqwSi3xtAcWqU+1w==", "dependencies": { - "command-exists": "~1.2.9", - "q": "1.x", - "underscore": "^1.12.0" + "command-exists": "~1.2.9" }, "engines": { "node": ">=4.0.0" @@ -15570,15 +15568,6 @@ "node": ">=6" } }, - "node_modules/q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", - "engines": { - "node": ">=0.6.0", - "teleport": ">=0.2.0" - } - }, "node_modules/qlobber": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/qlobber/-/qlobber-5.0.3.tgz", @@ -17977,11 +17966,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/underscore": { - "version": "1.13.6", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", - "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==" - }, "node_modules/unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", @@ -22231,13 +22215,11 @@ "integrity": "sha512-retLUN4TwCJ0QJDi9OCJwYVaXAz93NeOkEtEQL98M2bykBOxmURlP0YlfsuE46kItOOVZIWRYC3KsSLhQ1R2Qw==" }, "@louislam/ping": { - "version": "0.4.2-mod.2", - "resolved": "https://registry.npmjs.org/@louislam/ping/-/ping-0.4.2-mod.2.tgz", - "integrity": "sha512-4krrRGohYdhQOD+Mt0Q8e1Z05DEKntZ7TgiY1jYaqWrMz0H2XJyRh+mLPOUVPL5zSymiHsZiK2ZACXtp/d9Wxg==", + "version": "0.4.4-mod.0", + "resolved": "https://registry.npmjs.org/@louislam/ping/-/ping-0.4.4-mod.0.tgz", + "integrity": "sha512-U2ZXcgFRPmZYd/ooA8KILG4aCMBsDrGP9NDWseHriZSsKlu5Y1lf/LbenN6tnqQ9JjAsbJjqwSi3xtAcWqU+1w==", "requires": { - "command-exists": "~1.2.9", - "q": "1.x", - "underscore": "^1.12.0" + "command-exists": "~1.2.9" } }, "@louislam/sqlite3": { @@ -30817,11 +30799,6 @@ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==" }, - "q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==" - }, "qlobber": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/qlobber/-/qlobber-5.0.3.tgz", @@ -32679,11 +32656,6 @@ "which-boxed-primitive": "^1.0.2" } }, - "underscore": { - "version": "1.13.6", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", - "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==" - }, "unicode-canonical-property-names-ecmascript": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz", diff --git a/package.json b/package.json index 5b2bda4a4..3ef2f0c00 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "uptime-kuma", - "version": "1.21.0-beta.0", + "version": "1.21.0", "license": "MIT", "repository": { "type": "git", @@ -39,7 +39,7 @@ "build-docker-nightly-amd64": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:nightly-amd64 --target nightly . --push --progress plain", "build-docker-pr-test": "docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64 -t louislam/uptime-kuma:pr-test --target pr-test . --push", "upload-artifacts": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:upload-artifact --build-arg VERSION --build-arg GITHUB_TOKEN --target upload-artifact . --progress plain", - "setup": "git checkout 1.20.2 && npm ci --production && npm run download-dist", + "setup": "git checkout 1.21.0 && npm ci --production && npm run download-dist", "download-dist": "node extra/download-dist.js", "mark-as-nightly": "node extra/mark-as-nightly.js", "reset-password": "node extra/reset-password.js", @@ -69,7 +69,7 @@ }, "dependencies": { "@grpc/grpc-js": "~1.7.3", - "@louislam/ping": "~0.4.2-mod.2", + "@louislam/ping": "~0.4.4-mod.0", "@louislam/sqlite3": "15.1.2", "args-parser": "~1.3.0", "axios": "~0.27.0", diff --git a/server/database.js b/server/database.js index 5a83e1fbf..e52ae8bfc 100644 --- a/server/database.js +++ b/server/database.js @@ -73,6 +73,7 @@ class Database { "patch-http-body-encoding.sql": true, "patch-add-description-monitor.sql": true, "patch-api-key-table.sql": true, + "patch-monitor-tls.sql": true, }; /** diff --git a/server/model/maintenance_timeslot.js b/server/model/maintenance_timeslot.js index 3e7076d8a..dad719c74 100644 --- a/server/model/maintenance_timeslot.js +++ b/server/model/maintenance_timeslot.js @@ -58,7 +58,14 @@ class MaintenanceTimeslot extends BeanModel { bean.start_date = maintenance.start_date; bean.end_date = maintenance.end_date; bean.generated_next = true; - return await R.store(bean); + + if (!await this.isDuplicateTimeslot(bean)) { + await R.store(bean); + return bean; + } else { + log.debug("maintenance", "Duplicate timeslot, skip"); + return null; + } } else if (maintenance.strategy === "recurring-interval") { // Prevent dead loop, in case interval_day is not set @@ -144,6 +151,15 @@ class MaintenanceTimeslot extends BeanModel { } } + static async isDuplicateTimeslot(timeslot) { + let bean = await R.findOne("maintenance_timeslot", "maintenance_id = ? AND start_date = ? AND end_date = ?", [ + timeslot.maintenance_id, + timeslot.start_date, + timeslot.end_date + ]); + return bean !== null; + } + /** * Generate a next timeslot for all recurring types * @param maintenance @@ -161,7 +177,7 @@ class MaintenanceTimeslot extends BeanModel { // Keep generating from the first possible date, until it is ok while (true) { - log.debug("timeslot", "startDateTime: " + startDateTime.format()); + //log.debug("timeslot", "startDateTime: " + startDateTime.format()); // Handling out of effective date range if (startDateTime.diff(dayjs.utc(maintenance.end_date)) > 0) { @@ -193,7 +209,14 @@ class MaintenanceTimeslot extends BeanModel { bean.start_date = localToUTC(startDateTime); bean.end_date = localToUTC(endDateTime); bean.generated_next = false; - return await R.store(bean); + + if (!await this.isDuplicateTimeslot(bean)) { + await R.store(bean); + return bean; + } else { + log.debug("maintenance", "Duplicate timeslot, skip"); + return null; + } } } diff --git a/server/model/monitor.js b/server/model/monitor.js index 312ac732b..1e011c5a0 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -133,6 +133,9 @@ class Monitor extends BeanModel { mqttPassword: this.mqttPassword, authWorkstation: this.authWorkstation, authDomain: this.authDomain, + tlsCa: this.tlsCa, + tlsCert: this.tlsCert, + tlsKey: this.tlsKey, }; } @@ -331,6 +334,18 @@ class Monitor extends BeanModel { options.httpsAgent = new https.Agent(httpsAgentOptions); } + if (this.auth_method === "mtls") { + if (this.tlsCert !== null && this.tlsCert !== "") { + options.httpsAgent.options.cert = Buffer.from(this.tlsCert); + } + if (this.tlsCa !== null && this.tlsCa !== "") { + options.httpsAgent.options.ca = Buffer.from(this.tlsCa); + } + if (this.tlsKey !== null && this.tlsKey !== "") { + options.httpsAgent.options.key = Buffer.from(this.tlsKey); + } + } + log.debug("monitor", `[${this.name}] Axios Options: ${JSON.stringify(options)}`); log.debug("monitor", `[${this.name}] Axios Request`); @@ -836,7 +851,6 @@ class Monitor extends BeanModel { domain: this.authDomain, workstation: this.authWorkstation ? this.authWorkstation : undefined }); - } else { res = await axios.request(options); } @@ -1251,7 +1265,7 @@ class Monitor extends BeanModel { for (let notification of notificationList) { try { log.debug("monitor", "Sending to " + notification.name); - await Notification.send(JSON.parse(notification.config), `[${this.name}][${this.url}] Certificate will be expired in ${daysRemaining} days`); + await Notification.send(JSON.parse(notification.config), `[${this.name}][${this.url}] Certificate will expire in ${daysRemaining} days`); sent = true; } catch (e) { log.error("monitor", "Cannot send cert notification to " + notification.name); diff --git a/server/server.js b/server/server.js index d7fd1398b..ac2851ab4 100644 --- a/server/server.js +++ b/server/server.js @@ -688,6 +688,9 @@ let needSetup = false; bean.headers = monitor.headers; bean.basic_auth_user = monitor.basic_auth_user; bean.basic_auth_pass = monitor.basic_auth_pass; + bean.tlsCa = monitor.tlsCa; + bean.tlsCert = monitor.tlsCert; + bean.tlsKey = monitor.tlsKey; bean.interval = monitor.interval; bean.retryInterval = monitor.retryInterval; bean.resendInterval = monitor.resendInterval; diff --git a/server/uptime-kuma-server.js b/server/uptime-kuma-server.js index a9c2289e7..3dd7ba936 100644 --- a/server/uptime-kuma-server.js +++ b/server/uptime-kuma-server.js @@ -271,6 +271,7 @@ class UptimeKumaServer { /** Load the timeslots for maintenance */ async generateMaintenanceTimeslots() { + log.debug("maintenance", "Routine: Generating Maintenance Timeslots"); // Prevent #2776 // Remove duplicate maintenance_timeslot with same start_date, end_date and maintenance_id diff --git a/server/util-server.js b/server/util-server.js index 2cf81f6aa..01e66ee26 100644 --- a/server/util-server.js +++ b/server/util-server.js @@ -408,6 +408,9 @@ exports.redisPingAsync = function (dsn) { }); client.connect().then(() => { client.ping().then((res, err) => { + if (client.isOpen) { + client.disconnect(); + } if (err) { reject(err); } else { diff --git a/src/components/notifications/LunaSea.vue b/src/components/notifications/LunaSea.vue index 1ee4a8a51..4c55b3c21 100644 --- a/src/components/notifications/LunaSea.vue +++ b/src/components/notifications/LunaSea.vue @@ -1,20 +1,20 @@