From 2547515a376ddb3325f24e74a421dc593a6b4497 Mon Sep 17 00:00:00 2001 From: William Harrison Date: Thu, 16 Nov 2023 20:31:20 +0800 Subject: [PATCH 01/29] feat: grammar fixes (#4042) * feat: grammar fixes * Update PULL_REQUEST_TEMPLATE.md --- .github/PULL_REQUEST_TEMPLATE.md | 7 +++---- CONTRIBUTING.md | 35 ++++++++++++++++---------------- README.md | 20 +++++++++--------- SECURITY.md | 8 ++++---- 4 files changed, 34 insertions(+), 36 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 6a217143d..0dfb5faed 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -15,7 +15,7 @@ Please delete any options that are not relevant. - Bug fix (non-breaking change which fixes an issue) - User interface (UI) - New feature (non-breaking change which adds functionality) -- Breaking change (fix or feature that would cause existing functionality to not work as expected) +- Breaking change (a fix or feature that would cause existing functionality to not work as expected) - Other - This change requires a documentation update @@ -24,9 +24,8 @@ Please delete any options that are not relevant. - [ ] My code follows the style guidelines of this project - [ ] I ran ESLint and other linters for modified files - [ ] I have performed a self-review of my own code and tested it -- [ ] I have commented my code, particularly in hard-to-understand areas - (including JSDoc for methods) -- [ ] My changes generate no new warnings +- [ ] I have commented my code, particularly in hard-to-understand areas (including JSDoc for methods) +- [ ] My changes generates no new warnings - [ ] My code needed automated testing. I have added them (this is optional task) ## Screenshots (if any) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f253cff04..33b7336d1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,14 +1,14 @@ # Project Info -First of all, I want to thank everyone who made pull requests for Uptime Kuma. I never thought the GitHub Community would be so nice! Because of this, I also never thought that other people would actually read and edit my code. It is not very well structured or commented, sorry about that. +First of all, I want to thank everyone who have made pull requests for Uptime Kuma. I never thought the GitHub community would be so nice! Because of this, I also never thought that other people would actually read and edit my code. It is not very well structured or commented, sorry about that. -The project was created with vite.js (vue3). Then I created a subdirectory called "server" for the server part. Both frontend and backend share the same package.json. +The project was created with vite.js (vue3). Then I created a subdirectory called "server" for the server part. Both frontend and backend share the same `package.json`. The frontend code builds into "dist" directory. The server (express.js) exposes the "dist" directory as the root of the endpoint. This is how production is working. ## Key Technical Skills -- Node.js (You should know about promise, async/await and arrow function etc.) +- Node.js (You should know about promises, async/await, arrow functions, etc.) - Socket.io - SCSS - Vue.js @@ -62,7 +62,7 @@ Here are some references: The above cases may not cover all possible situations. -I (@louislam) have the final say. If your pull request does not meet my expectations, I will reject it, no matter how much time you spend on it. Therefore, it is essential to have a discussion beforehand. +I ([@louislam](https://github.com/louislam)) have the final say. If your pull request does not meet my expectations, I will reject it, no matter how much time you spent on it. Therefore, it is essential to have a discussion beforehand. I will assign your pull request to a [milestone](https://github.com/louislam/uptime-kuma/milestones), if I plan to review and merge it. @@ -73,15 +73,14 @@ Also, please don't rush or ask for an ETA, because I have to understand the pull Before deep into coding, discussion first is preferred. Creating an empty pull request for discussion would be recommended. 1. Fork the project -1. Clone your fork repo to local -1. Create a new branch -1. Create an empty commit - `git commit -m "[empty commit] pull request for " --allow-empty` -1. Push to your fork repo -1. Create a pull request: https://github.com/louislam/uptime-kuma/compare -1. Write a proper description -1. Click "Change to draft" -1. Discussion +2. Clone your fork repo to local +3. Create a new branch +4. Create an empty commit: `git commit -m "[empty commit] pull request for " --allow-empty` +5. Push to your fork repo +6. Create a pull request: https://github.com/louislam/uptime-kuma/compare +7. Write a proper description +8. Click "Change to draft" +9. Discussion ## Project Styles @@ -114,9 +113,9 @@ I personally do not like something that requires so many configurations before y - IDE that supports [`ESLint`](https://eslint.org/) and EditorConfig (I am using [`IntelliJ IDEA`](https://www.jetbrains.com/idea/)) - A SQLite GUI tool (f.ex. [`SQLite Expert Personal`](https://www.sqliteexpert.com/download.html) or [`DBeaver Community`](https://dbeaver.io/download/)) -### GitHub Codespace +### GitHub Codespaces -If you don't want to setup an local environment, you can now develop on GitHub Codespace, read more: +If you don't want to setup an local environment, you can now develop on GitHub Codespaces, read more: https://github.com/louislam/uptime-kuma/tree/master/.devcontainer @@ -231,9 +230,9 @@ If for security / bug / other reasons, a library must be updated, breaking chang ## Translations -Please add **all** the strings which are translatable to `src/lang/en.json` (If translation keys are omitted, they can not be translated). +Please add **all** the strings which are translatable to `src/lang/en.json` (if translation keys are omitted, they can not be translated.) -**Don't include any other languages in your initial Pull-Request** (even if this is your mother tongue), to avoid merge-conflicts between weblate and `master`. +**Don't include any other languages in your initial pull request** (even if this is your mother tongue), to avoid merge-conflicts between weblate and `master`. The translations can then (after merging a PR into `master`) be translated by awesome people donating their language skills. If you want to help by translating Uptime Kuma into your language, please visit the [instructions on how to translate using weblate](https://github.com/louislam/uptime-kuma/blob/master/src/lang/README.md). @@ -245,7 +244,7 @@ My mother language is not English and my grammar is not that great. ## Wiki -Since there is no way to make a pull request to wiki's repo, I have set up another repo to do that. +Since there is no way to make a pull request to the wiki, I have set up another repo to do that. https://github.com/louislam/uptime-kuma-wiki diff --git a/README.md b/README.md index 8afa9e695..1502f392a 100644 --- a/README.md +++ b/README.md @@ -136,26 +136,26 @@ Telegram Notification Sample: ## Motivation -- I was looking for a self-hosted monitoring tool like "Uptime Robot", but it is hard to find a suitable one. One of the close ones is statping. Unfortunately, it is not stable and no longer maintained. -- Want to build a fancy UI. +- I was looking for a self-hosted monitoring tool like "Uptime Robot", but it is hard to find a suitable one. One of the closest ones is statping. Unfortunately, it is not stable and no longer maintained. +- Wanted to build a fancy UI. - Learn Vue 3 and vite.js. - Show the power of Bootstrap 5. -- Try to use WebSocket with SPA instead of REST API. +- Try to use WebSocket with SPA instead of a REST API. - Deploy my first Docker image to Docker Hub. -If you love this project, please consider giving me a ⭐. +If you love this project, please consider giving it a ⭐. ## 🗣️ Discussion / Ask for Help -⚠️ For any general or technical questions, please don't send me an email, as I am unable to provide support in that manner. I will not respond if you ask such questions. +⚠️ For any general or technical questions, please don't send me an email, as I am unable to provide support in that manner. I will not respond if you ask questions there. -I recommend using Google, GitHub Issues, or Uptime Kuma's Subreddit for finding answers to your question. If you cannot find the information you need, feel free to ask: +I recommend using Google, GitHub Issues, or Uptime Kuma's subreddit for finding answers to your question. If you cannot find the information you need, feel free to ask: - [GitHub Issues](https://github.com/louislam/uptime-kuma/issues) -- [Subreddit r/Uptime kuma](https://www.reddit.com/r/UptimeKuma/) +- [Subreddit (r/UptimeKuma)](https://www.reddit.com/r/UptimeKuma/) -My Reddit account: [u/louislamlam](https://reddit.com/u/louislamlam). -You can mention me if you ask a question on Reddit. +My Reddit account: [u/louislamlam](https://reddit.com/u/louislamlam) +You can mention me if you ask a question on the subreddit. ## Contribute @@ -181,7 +181,7 @@ If you want to translate Uptime Kuma into your language, please visit [Weblate R ### Spelling & Grammar Feel free to correct the grammar in the documentation or code. -My mother language is not english and my grammar is not that great. +My mother language is not English and my grammar is not that great. ### Create Pull Requests diff --git a/SECURITY.md b/SECURITY.md index db4bc138f..72b4fc0f1 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -3,7 +3,7 @@ ## Reporting a Vulnerability 1. Please report security issues to https://github.com/louislam/uptime-kuma/security/advisories/new. -1. Please also create an empty security issue to alert me, as GitHub Advisories do not send a notification, I probably will miss it without this. https://github.com/louislam/uptime-kuma/issues/new?assignees=&labels=help&template=security.md +2. Please also create an empty security issue to alert me, as GitHub Advisories do not send a notification, I probably will miss it without this. https://github.com/louislam/uptime-kuma/issues/new?assignees=&labels=help&template=security.md Do not use the public issue tracker or discuss it in public as it will cause more damage. @@ -19,12 +19,12 @@ You should use or upgrade to the latest version of Uptime Kuma. All `1.X.X` vers ### Upgradable Docker Tags -| Tag | Supported | -| ------- | ------------------ | +| Tag | Supported | +|-|-| | 1 | :white_check_mark: | | 1-debian | :white_check_mark: | | latest | :white_check_mark: | | debian | :white_check_mark: | | 1-alpine | ⚠️ Deprecated | | alpine | ⚠️ Deprecated | -| All other tags | ❌ | +| All other tags | ❌ | From 9964b6c4d88fb76948513366bc119583a42845cb Mon Sep 17 00:00:00 2001 From: Nelson Chan <3271800+chakflying@users.noreply.github.com> Date: Thu, 16 Nov 2023 20:41:35 +0800 Subject: [PATCH 02/29] Fix: Update monitor object on pause (#4032) --- server/server.js | 1 + 1 file changed, 1 insertion(+) diff --git a/server/server.js b/server/server.js index 605dde325..9d509b45e 100644 --- a/server/server.js +++ b/server/server.js @@ -1832,6 +1832,7 @@ async function pauseMonitor(userID, monitorID) { if (monitorID in server.monitorList) { server.monitorList[monitorID].stop(); + server.monitorList[monitorID].active = 0; } } From b383392e8f962009ed68a5a086889c2ffab660c1 Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Fri, 17 Nov 2023 15:21:08 +0800 Subject: [PATCH 03/29] Remains Node.js 16' SSL behavior for 1.23.X (#4044) --- server/model/monitor.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/server/model/monitor.js b/server/model/monitor.js index e4d112bd4..54a1972c4 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -443,6 +443,7 @@ class Monitor extends BeanModel { const httpsAgentOptions = { maxCachedSessions: 0, // Use Custom agent to disable session reuse (https://github.com/nodejs/node/issues/3940) rejectUnauthorized: !this.getIgnoreTls(), + secureOptions: crypto.constants.SSL_OP_LEGACY_SERVER_CONNECT, }; log.debug("monitor", `[${this.name}] Prepare Options for axios`); @@ -697,6 +698,7 @@ class Monitor extends BeanModel { httpsAgent: CacheableDnsHttpAgent.getHttpsAgent({ maxCachedSessions: 0, // Use Custom agent to disable session reuse (https://github.com/nodejs/node/issues/3940) rejectUnauthorized: !this.getIgnoreTls(), + secureOptions: crypto.constants.SSL_OP_LEGACY_SERVER_CONNECT, }), httpAgent: CacheableDnsHttpAgent.getHttpAgent({ maxCachedSessions: 0, @@ -749,6 +751,7 @@ class Monitor extends BeanModel { httpsAgent: CacheableDnsHttpAgent.getHttpsAgent({ maxCachedSessions: 0, // Use Custom agent to disable session reuse (https://github.com/nodejs/node/issues/3940) rejectUnauthorized: !this.getIgnoreTls(), + secureOptions: crypto.constants.SSL_OP_LEGACY_SERVER_CONNECT, }), httpAgent: CacheableDnsHttpAgent.getHttpAgent({ maxCachedSessions: 0, From 40d6a2145301e2515b198ecba2968c8dfa1c278e Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Fri, 17 Nov 2023 15:21:29 +0800 Subject: [PATCH 04/29] Fix kafka migration script again (#4043) --- db/patch-fix-kafka-producer-booleans.sql | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/db/patch-fix-kafka-producer-booleans.sql b/db/patch-fix-kafka-producer-booleans.sql index 505f65a95..be2e992f5 100644 --- a/db/patch-fix-kafka-producer-booleans.sql +++ b/db/patch-fix-kafka-producer-booleans.sql @@ -15,12 +15,14 @@ ALTER TABLE monitor ALTER TABLE monitor ADD COLUMN kafka_producer_allow_auto_topic_creation BOOLEAN default 0 NOT NULL; --- Set bring old values from `_old` COLUMNs to correct ones -UPDATE monitor SET kafka_producer_allow_auto_topic_creation = monitor.kafka_producer_allow_auto_topic_creation_old - WHERE monitor.kafka_producer_allow_auto_topic_creation_old IS NOT NULL; +-- These SQL is still not fully safe. See https://github.com/louislam/uptime-kuma/issues/4039. -UPDATE monitor SET kafka_producer_ssl = monitor.kafka_producer_ssl_old - WHERE monitor.kafka_producer_ssl_old IS NOT NULL; +-- Set bring old values from `_old` COLUMNs to correct ones +-- UPDATE monitor SET kafka_producer_allow_auto_topic_creation = monitor.kafka_producer_allow_auto_topic_creation_old +-- WHERE monitor.kafka_producer_allow_auto_topic_creation_old IS NOT NULL; + +-- UPDATE monitor SET kafka_producer_ssl = monitor.kafka_producer_ssl_old +-- WHERE monitor.kafka_producer_ssl_old IS NOT NULL; -- Remove old COLUMNs ALTER TABLE monitor From f0975cd929ead07dee2cb5cbed5faa0eebe75b0f Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Sat, 18 Nov 2023 01:17:54 +0800 Subject: [PATCH 05/29] Should be a final ulitmate fix for request timeout issue (#4045) * Try to fix timeout again * Ops --- db/patch-timeout.sql | 7 +++++++ server/database.js | 1 + server/model/monitor.js | 2 +- 3 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 db/patch-timeout.sql diff --git a/db/patch-timeout.sql b/db/patch-timeout.sql new file mode 100644 index 000000000..f25711201 --- /dev/null +++ b/db/patch-timeout.sql @@ -0,0 +1,7 @@ +-- You should not modify if this have pushed to Github, unless it does serious wrong with the db. +BEGIN TRANSACTION; + +UPDATE monitor SET timeout = (interval * 0.8) +WHERE timeout IS NULL OR timeout <= 0; + +COMMIT; diff --git a/server/database.js b/server/database.js index 29ab3b941..07a43a812 100644 --- a/server/database.js +++ b/server/database.js @@ -83,6 +83,7 @@ class Database { "patch-add-gamedig-given-port.sql": true, "patch-notification-config.sql": true, "patch-fix-kafka-producer-booleans.sql": true, + "patch-timeout.sql": true, }; /** diff --git a/server/model/monitor.js b/server/model/monitor.js index 54a1972c4..194980a12 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -369,7 +369,7 @@ class Monitor extends BeanModel { // Runtime patch timeout if it is 0 // See https://github.com/louislam/uptime-kuma/pull/3961#issuecomment-1804149144 - if (this.timeout <= 0) { + if (!this.timeout || this.timeout <= 0) { this.timeout = this.interval * 1000 * 0.8; } From 6d4a45f18c9a7372408a7d132e570f7ab4640123 Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Sat, 18 Nov 2023 01:23:53 +0800 Subject: [PATCH 06/29] Update to 1.23.5 --- package-lock.json | 4 ++-- package.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index dbc44b488..c25c4de75 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "uptime-kuma", - "version": "1.23.4", + "version": "1.23.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "uptime-kuma", - "version": "1.23.4", + "version": "1.23.5", "license": "MIT", "dependencies": { "@grpc/grpc-js": "~1.7.3", diff --git a/package.json b/package.json index 4ecc87e86..f55d12a88 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "uptime-kuma", - "version": "1.23.4", + "version": "1.23.5", "license": "MIT", "repository": { "type": "git", @@ -40,7 +40,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.23.4 && npm ci --production && npm run download-dist", + "setup": "git checkout 1.23.5 && 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", From 954e05b72f3c565957e88237270de176c4a219ca Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Sat, 18 Nov 2023 11:33:34 +0800 Subject: [PATCH 07/29] Fix #4051 --- package.json | 3 ++- server/model/monitor.js | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index f55d12a88..59797ad87 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,8 @@ "cypress-open": "concurrently -k -r \"node test/prepare-test-server.js && node server/server.js --port=3002 --data-dir=./data/test/\" \"cypress open --config-file ./config/cypress.config.js\"", "build-healthcheck-armv7": "cross-env GOOS=linux GOARCH=arm GOARM=7 go build -x -o ./extra/healthcheck-armv7 ./extra/healthcheck.go", "deploy-demo-server": "node extra/deploy-demo-server.js", - "sort-contributors": "node extra/sort-contributors.js" + "sort-contributors": "node extra/sort-contributors.js", + "start-server-node14-win": "private\\node14\\node.exe server/server.js" }, "dependencies": { "@grpc/grpc-js": "~1.7.3", diff --git a/server/model/monitor.js b/server/model/monitor.js index 194980a12..f7cbff303 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -22,6 +22,7 @@ const { UptimeCacheList } = require("../uptime-cache-list"); const Gamedig = require("gamedig"); const jsonata = require("jsonata"); const jwt = require("jsonwebtoken"); +const crypto = require("crypto"); const rootCertificates = rootCertificatesFingerprints(); From c1aaad0d8554253fcc3ba15a1c41c7469dc7424f Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Sat, 18 Nov 2023 11:37:14 +0800 Subject: [PATCH 08/29] Update to 1.23.6 --- package-lock.json | 4 ++-- package.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index c25c4de75..81bfcf76f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "uptime-kuma", - "version": "1.23.5", + "version": "1.23.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "uptime-kuma", - "version": "1.23.5", + "version": "1.23.6", "license": "MIT", "dependencies": { "@grpc/grpc-js": "~1.7.3", diff --git a/package.json b/package.json index 59797ad87..b6c7c22d3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "uptime-kuma", - "version": "1.23.5", + "version": "1.23.6", "license": "MIT", "repository": { "type": "git", @@ -40,7 +40,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.23.5 && npm ci --production && npm run download-dist", + "setup": "git checkout 1.23.6 && 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", From e2782810cf75e4c4a92d202f873906a96df43b1e Mon Sep 17 00:00:00 2001 From: Rakibul Yeasin Date: Mon, 20 Nov 2023 12:45:34 +0600 Subject: [PATCH 09/29] fix: Clickable link monitors aren't underlined when editing status page (#3820) --- src/components/PublicGroupList.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/PublicGroupList.vue b/src/components/PublicGroupList.vue index ec758682f..d1c1f4c52 100644 --- a/src/components/PublicGroupList.vue +++ b/src/components/PublicGroupList.vue @@ -163,7 +163,7 @@ export default { if (this.$parent.editMode && ignoreSendUrl && Object.keys(this.$root.monitorList).length) { return this.$root.monitorList[monitor.element.id].type === "http" || this.$root.monitorList[monitor.element.id].type === "keyword" || this.$root.monitorList[monitor.element.id].type === "json-query"; } - return monitor.element.sendUrl && monitor.element.url && monitor.element.url !== "https://" && !this.editMode; + return monitor.element.sendUrl && monitor.element.url && monitor.element.url !== "https://"; }, /** From 9973d73dd77e290677875a7e14c842b48119be55 Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Mon, 20 Nov 2023 14:47:58 +0800 Subject: [PATCH 10/29] Fix a merge issue --- db/{ => old_migrations}/patch-timeout.sql | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename db/{ => old_migrations}/patch-timeout.sql (100%) diff --git a/db/patch-timeout.sql b/db/old_migrations/patch-timeout.sql similarity index 100% rename from db/patch-timeout.sql rename to db/old_migrations/patch-timeout.sql From 33ce0ef02c8ad1e660d253020cb8d01d9e829188 Mon Sep 17 00:00:00 2001 From: Nelson Chan <3271800+chakflying@users.noreply.github.com> Date: Tue, 21 Nov 2023 23:56:17 +0800 Subject: [PATCH 11/29] Fix: Improve error message on timeout (#4054) * Fix: Improve error message on timeout * Chore: Format --- server/model/monitor.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/server/model/monitor.js b/server/model/monitor.js index f7cbff303..8958e3d80 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -938,7 +938,11 @@ class Monitor extends BeanModel { } catch (error) { - bean.msg = error.message; + if (error?.name === "CanceledError") { + bean.msg = `timeout by AbortSignal (${this.timeout}s)`; + } else { + bean.msg = error.message; + } // If UP come in here, it must be upside down mode // Just reset the retries From bf58838b891a9bec92f611e50ab6a14f04de68b6 Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Wed, 22 Nov 2023 16:03:03 +0800 Subject: [PATCH 12/29] +10 seconds for Abort signal (#4053) * Debug only * Remove debug --- server/model/monitor.js | 2 +- server/util-server.js | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/server/model/monitor.js b/server/model/monitor.js index 8958e3d80..ec40115c1 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -483,7 +483,7 @@ class Monitor extends BeanModel { validateStatus: (status) => { return checkStatusCode(status, this.getAcceptedStatuscodes()); }, - signal: axiosAbortSignal(this.timeout * 1000), + signal: axiosAbortSignal((this.timeout + 10) * 1000), }; if (bodyValue) { diff --git a/server/util-server.js b/server/util-server.js index 329810be0..b9832af31 100644 --- a/server/util-server.js +++ b/server/util-server.js @@ -1141,7 +1141,6 @@ module.exports.axiosAbortSignal = (timeoutMs) => { // v16-: AbortSignal.timeout is not supported try { const abortController = new AbortController(); - setTimeout(() => abortController.abort(), timeoutMs); return abortController.signal; From d810a74d707325eaba714c4f52e2ff46733aeb65 Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Wed, 22 Nov 2023 16:12:15 +0800 Subject: [PATCH 13/29] Move rootless images to an another set (#4052) --- docker/dockerfile | 9 ++++++++- package.json | 3 +++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/docker/dockerfile b/docker/dockerfile index 1993c1045..572c732e0 100644 --- a/docker/dockerfile +++ b/docker/dockerfile @@ -42,13 +42,20 @@ HEALTHCHECK --interval=60s --timeout=30s --start-period=180s --retries=5 CMD ext ENTRYPOINT ["/usr/bin/dumb-init", "--"] CMD ["node", "server/server.js"] +############################################ +# Rootless Image +############################################ +FROM release AS rootless + ############################################ # Mark as Nightly ############################################ FROM release AS nightly -USER node RUN npm run mark-as-nightly +FROM nightly AS nightly-rootless +USER node + ############################################ # Build an image for testing pr ############################################ diff --git a/package.json b/package.json index be1fc7a5a..2839bce27 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,9 @@ "build-docker-slim": "node ./extra/env2arg.js docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:2-slim -t louislam/uptime-kuma:$VERSION-slim --target release --build-arg BASE_IMAGE=louislam/uptime-kuma:base2-slim . --push", "build-docker-full": "node ./extra/env2arg.js docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:2 -t louislam/uptime-kuma:$VERSION --target release . --push", "build-docker-nightly": "node ./extra/test-docker.js && npm run build && docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:nightly2 --target nightly . --push", + "build-docker-slim-rootless": "node ./extra/env2arg.js docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:2-slim-rootless -t louislam/uptime-kuma:$VERSION-slim-rootless --target rootless --build-arg BASE_IMAGE=louislam/uptime-kuma:base2-slim . --push", + "build-docker-full-rootless": "node ./extra/env2arg.js docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:2-rootless -t louislam/uptime-kuma:$VERSION-rootless --target rootless . --push", + "build-docker-nightly-rootless": "node ./extra/test-docker.js && npm run build && docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:nightly2-rootless --target nightly-rootless . --push", "build-docker-nightly-local": "npm run build && docker build -f docker/dockerfile -t louislam/uptime-kuma:nightly2 --target nightly .", "build-docker-pr-test": "docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64 -t louislam/uptime-kuma:pr-test2 --target pr-test2 . --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", From dc4242019331e65a79ac16deef97510144e01b12 Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Wed, 22 Nov 2023 19:35:38 +0800 Subject: [PATCH 14/29] Change version to 2.0.0-dev --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 488058391..885bab3ab 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "uptime-kuma", - "version": "1.23.6", + "version": "2.0.0-dev", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "uptime-kuma", - "version": "1.23.6", + "version": "2.0.0-dev", "license": "MIT", "dependencies": { "@grpc/grpc-js": "~1.7.3", diff --git a/package.json b/package.json index 2839bce27..dd312fba9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "uptime-kuma", - "version": "1.23.6", + "version": "2.0.0-dev", "license": "MIT", "repository": { "type": "git", From 8e611587587cb74483cd5a9ca768ca682533b41d Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Wed, 22 Nov 2023 19:50:03 +0800 Subject: [PATCH 15/29] Close the client postgresql connection after rejection. (#4084) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Manuel Vázquez Acosta --- server/util-server.js | 1 + 1 file changed, 1 insertion(+) diff --git a/server/util-server.js b/server/util-server.js index b9832af31..ec0cb7684 100644 --- a/server/util-server.js +++ b/server/util-server.js @@ -458,6 +458,7 @@ exports.postgresQuery = function (connectionString, query) { }); } catch (e) { reject(e); + client.end(); } } }); From 121d1a11af7ff73bba12fbb5c73dc419a4fff7d8 Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Fri, 24 Nov 2023 02:23:38 +0800 Subject: [PATCH 16/29] Revert "Restart running monitors if no heartbeat (#3952)" (#4088) This reverts commit c43223a16de9f1c65e0aa14fd6a71837655385f8. --- server/model/monitor.js | 18 +------- server/uptime-kuma-server.js | 89 ------------------------------------ 2 files changed, 1 insertion(+), 106 deletions(-) diff --git a/server/model/monitor.js b/server/model/monitor.js index ec40115c1..e407e5a2d 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -3,7 +3,7 @@ const dayjs = require("dayjs"); const axios = require("axios"); const { Prometheus } = require("../prometheus"); const { log, UP, DOWN, PENDING, MAINTENANCE, flipStatus, TimeLogger, MAX_INTERVAL_SECOND, MIN_INTERVAL_SECOND, - SQL_DATETIME_FORMAT, isDev, sleep, getRandomInt + SQL_DATETIME_FORMAT } = require("../../src/util"); const { tcping, ping, dnsResolve, checkCertificate, checkStatusCode, getTotalClientInRoom, setting, mssqlQuery, postgresQuery, mysqlQuery, mqttAsync, setSetting, httpNtlm, radius, grpcQuery, redisPingAsync, mongodbPing, kafkaProducerAsync, getOidcTokenClientCredentials, rootCertificatesFingerprints, axiosAbortSignal @@ -329,16 +329,6 @@ class Monitor extends BeanModel { } } - // Evil - if (isDev) { - if (process.env.EVIL_RANDOM_MONITOR_SLEEP === "SURE") { - if (getRandomInt(0, 100) === 0) { - log.debug("evil", `[${this.name}] Evil mode: Random sleep: ` + beatInterval * 10000); - await sleep(beatInterval * 10000); - } - } - } - // Expose here for prometheus update // undefined if not https let tlsInfo = undefined; @@ -1024,7 +1014,6 @@ class Monitor extends BeanModel { if (! this.isStop) { log.debug("monitor", `[${this.name}] SetTimeout for next check.`); this.heartbeatInterval = setTimeout(safeBeat, beatInterval * 1000); - this.lastScheduleBeatTime = dayjs(); } else { log.info("monitor", `[${this.name}] isStop = true, no next check.`); } @@ -1034,9 +1023,7 @@ class Monitor extends BeanModel { /** Get a heartbeat and handle errors */ const safeBeat = async () => { try { - this.lastStartBeatTime = dayjs(); await beat(); - this.lastEndBeatTime = dayjs(); } catch (e) { console.trace(e); UptimeKumaServer.errorLog(e, false); @@ -1045,9 +1032,6 @@ class Monitor extends BeanModel { if (! this.isStop) { log.info("monitor", "Try to restart the monitor"); this.heartbeatInterval = setTimeout(safeBeat, this.interval * 1000); - this.lastScheduleBeatTime = dayjs(); - } else { - log.info("monitor", "isStop = true, no next check."); } } }; diff --git a/server/uptime-kuma-server.js b/server/uptime-kuma-server.js index 6b1d3d010..6acc8d4df 100644 --- a/server/uptime-kuma-server.js +++ b/server/uptime-kuma-server.js @@ -12,7 +12,6 @@ const { Settings } = require("./settings"); const dayjs = require("dayjs"); const childProcess = require("child_process"); const path = require("path"); -const axios = require("axios"); // DO NOT IMPORT HERE IF THE MODULES USED `UptimeKumaServer.getInstance()`, put at the bottom of this file instead. /** @@ -63,8 +62,6 @@ class UptimeKumaServer { */ jwtSecret = null; - checkMonitorsInterval = null; - static getInstance(args) { if (UptimeKumaServer.instance == null) { UptimeKumaServer.instance = new UptimeKumaServer(args); @@ -78,9 +75,6 @@ class UptimeKumaServer { const sslCert = args["ssl-cert"] || process.env.UPTIME_KUMA_SSL_CERT || process.env.SSL_CERT || undefined; const sslKeyPassphrase = args["ssl-key-passphrase"] || process.env.UPTIME_KUMA_SSL_KEY_PASSPHRASE || process.env.SSL_KEY_PASSPHRASE || undefined; - // Set default axios timeout to 5 minutes instead of infinity - axios.defaults.timeout = 300 * 1000; - log.info("server", "Creating express and socket.io instance"); this.app = express(); if (sslKey && sslCert) { @@ -352,10 +346,6 @@ class UptimeKumaServer { if (enable || enable === null) { this.startNSCDServices(); } - - this.checkMonitorsInterval = setInterval(() => { - this.checkMonitors(); - }, 60 * 1000); } /** @@ -368,8 +358,6 @@ class UptimeKumaServer { if (enable || enable === null) { this.stopNSCDServices(); } - - clearInterval(this.checkMonitorsInterval); } /** @@ -400,83 +388,6 @@ class UptimeKumaServer { } } } - - /** - * Start the specified monitor - * @param {number} monitorID ID of monitor to start - * @returns {Promise} - */ - async startMonitor(monitorID) { - log.info("manage", `Resume Monitor: ${monitorID} by server`); - - await R.exec("UPDATE monitor SET active = 1 WHERE id = ?", [ - monitorID, - ]); - - let monitor = await R.findOne("monitor", " id = ? ", [ - monitorID, - ]); - - if (monitor.id in this.monitorList) { - this.monitorList[monitor.id].stop(); - } - - this.monitorList[monitor.id] = monitor; - monitor.start(this.io); - } - - /** - * Restart a given monitor - * @param {number} monitorID ID of monitor to start - * @returns {Promise} - */ - async restartMonitor(monitorID) { - return await this.startMonitor(monitorID); - } - - /** - * Check if monitors are running properly - */ - async checkMonitors() { - log.debug("monitor_checker", "Checking monitors"); - - for (let monitorID in this.monitorList) { - let monitor = this.monitorList[monitorID]; - - // Not for push monitor - if (monitor.type === "push") { - continue; - } - - if (!monitor.active) { - continue; - } - - // Check the lastStartBeatTime, if it is too long, then restart - if (monitor.lastScheduleBeatTime ) { - let diff = dayjs().diff(monitor.lastStartBeatTime, "second"); - - if (diff > monitor.interval * 1.5) { - log.error("monitor_checker", `Monitor Interval: ${monitor.interval} Monitor ` + monitorID + " lastStartBeatTime diff: " + diff); - log.error("monitor_checker", "Unexpected error: Monitor " + monitorID + " is struck for unknown reason"); - log.error("monitor_checker", "Last start beat time: " + R.isoDateTime(monitor.lastStartBeatTime)); - log.error("monitor_checker", "Last end beat time: " + R.isoDateTime(monitor.lastEndBeatTime)); - log.error("monitor_checker", "Last ScheduleBeatTime: " + R.isoDateTime(monitor.lastScheduleBeatTime)); - - // Restart - log.error("monitor_checker", `Restarting monitor ${monitorID} automatically now`); - this.restartMonitor(monitorID); - } else { - //log.debug("monitor_checker", "Monitor " + monitorID + " is running normally"); - } - } else { - //log.debug("monitor_checker", "Monitor " + monitorID + " is not started yet, skipp"); - } - - } - - log.debug("monitor_checker", "Checking monitors end"); - } } module.exports = { From ac452bbcb944a69b946c132df688fd32409e839b Mon Sep 17 00:00:00 2001 From: Adam Hancock Date: Thu, 23 Nov 2023 18:58:33 +0000 Subject: [PATCH 17/29] Zoom in on real browser screenshot (#3925) * Screenshot in modal * Update src/components/ScreenshotDialog.vue Co-authored-by: Frank Elsinga * Update src/pages/Details.vue Co-authored-by: Frank Elsinga * Added title * Update ScreenshotDialog.vue Co-authored-by: Frank Elsinga * Add translations --------- Co-authored-by: Frank Elsinga --- src/assets/app.scss | 4 +++ src/components/ScreenshotDialog.vue | 52 +++++++++++++++++++++++++++++ src/lang/en.json | 3 +- src/pages/Details.vue | 15 +++++++-- 4 files changed, 71 insertions(+), 3 deletions(-) create mode 100644 src/components/ScreenshotDialog.vue diff --git a/src/assets/app.scss b/src/assets/app.scss index eb3c9f8e4..c7e56ba74 100644 --- a/src/assets/app.scss +++ b/src/assets/app.scss @@ -635,6 +635,10 @@ $shadow-box-padding: 20px; } } +.zoom-cursor { + cursor: zoom-in; +} + // Localization @import "localization.scss"; diff --git a/src/components/ScreenshotDialog.vue b/src/components/ScreenshotDialog.vue new file mode 100644 index 000000000..bc829095d --- /dev/null +++ b/src/components/ScreenshotDialog.vue @@ -0,0 +1,52 @@ + + + + + diff --git a/src/lang/en.json b/src/lang/en.json index a04f5d8a9..806d6fbd6 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -859,5 +859,6 @@ "successEnabled": "Enabled Successfully.", "tagNotFound": "Tag not found.", "foundChromiumVersion": "Found Chromium/Chrome. Version: {0}", - "GrafanaOncallUrl": "Grafana Oncall URL" + "GrafanaOncallUrl": "Grafana Oncall URL", + "Browser Screenshot": "Browser Screenshot" } diff --git a/src/pages/Details.vue b/src/pages/Details.vue index 20c8aa13a..f1692027c 100644 --- a/src/pages/Details.vue +++ b/src/pages/Details.vue @@ -184,9 +184,10 @@
-
- +
+ screenshot of the website
+
@@ -283,6 +284,7 @@ import "prismjs/components/prism-javascript"; import "prismjs/components/prism-css"; import { PrismEditor } from "vue-prism-editor"; import "vue-prism-editor/dist/prismeditor.min.css"; +import ScreenshotDialog from "../components/ScreenshotDialog.vue"; export default { components: { @@ -297,6 +299,7 @@ export default { Tag, CertificateInfo, PrismEditor, + ScreenshotDialog }, data() { return { @@ -476,6 +479,14 @@ export default { this.$refs.confirmDelete.show(); }, + /** + * Show Screenshot Dialog + * @returns {void} + */ + showScreenshotDialog() { + this.$refs.screenshotDialog.show(); + }, + /** * Show dialog to confirm clearing events * @returns {void} From afaa7bb2f0bc955816e99bfcafd3e1699793d607 Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Fri, 24 Nov 2023 16:03:35 +0800 Subject: [PATCH 18/29] Do not process debug log for production --- src/util.js | 3 +++ src/util.ts | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/src/util.js b/src/util.js index 6b8f8f374..8936ffbf9 100644 --- a/src/util.js +++ b/src/util.js @@ -101,6 +101,9 @@ class Logger { * @param level Log level. One of INFO, WARN, ERROR, DEBUG or can be customized. */ log(module, msg, level) { + if (level === "DEBUG" && !exports.isDev) { + return; + } if (this.hideLog[level] && this.hideLog[level].includes(module.toLowerCase())) { return; } diff --git a/src/util.ts b/src/util.ts index e8a2706e3..4f62fe763 100644 --- a/src/util.ts +++ b/src/util.ts @@ -115,6 +115,10 @@ class Logger { * @param level Log level. One of INFO, WARN, ERROR, DEBUG or can be customized. */ log(module: string, msg: any, level: string) { + if (level === "DEBUG" && !isDev) { + return; + } + if (this.hideLog[level] && this.hideLog[level].includes(module.toLowerCase())) { return; } From b689733d59139f158de95b0017865c867d0c7159 Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Fri, 24 Nov 2023 16:37:52 +0800 Subject: [PATCH 19/29] Fix getGameList, testChrome without checkLogin --- .../socket-handlers/general-socket-handler.js | 40 +++++++++++++------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/server/socket-handlers/general-socket-handler.js b/server/socket-handlers/general-socket-handler.js index 2f0c63b41..c7f77b65b 100644 --- a/server/socket-handlers/general-socket-handler.js +++ b/server/socket-handlers/general-socket-handler.js @@ -42,24 +42,40 @@ module.exports.generalSocketHandler = (socket, server) => { }); socket.on("getGameList", async (callback) => { - callback({ - ok: true, - gameList: getGameList(), - }); - }); - - socket.on("testChrome", (executable, callback) => { - // Just noticed that await call could block the whole socket.io server!!! Use pure promise instead. - testChrome(executable).then((version) => { + try { + checkLogin(socket); callback({ ok: true, - msg: "Found Chromium/Chrome. Version: " + version, + gameList: getGameList(), }); - }).catch((e) => { + } catch (e) { callback({ ok: false, msg: e.message, + }) + } + }); + + socket.on("testChrome", (executable, callback) => { + try { + checkLogin(socket); + // Just noticed that await call could block the whole socket.io server!!! Use pure promise instead. + testChrome(executable).then((version) => { + callback({ + ok: true, + msg: "Found Chromium/Chrome. Version: " + version, + }); + }).catch((e) => { + callback({ + ok: false, + msg: e.message, + }); }); - }); + } catch (e) { + callback({ + ok: false, + msg: e.message, + }) + } }); }; From f28dccf4e11f041564293e4f407e69ab9ee2277f Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Fri, 24 Nov 2023 17:18:01 +0800 Subject: [PATCH 20/29] Merge pull request from GHSA-v4v2-8h88-65qj --- package-lock.json | 14 ++++++++++---- package.json | 1 + server/google-analytics.js | 14 +++++++++----- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/package-lock.json b/package-lock.json index 81bfcf76f..8cf653a17 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,6 +33,7 @@ "express-static-gzip": "~2.1.7", "form-data": "~4.0.0", "gamedig": "~4.1.0", + "html-escaper": "^3.0.3", "http-graceful-shutdown": "~3.1.7", "http-proxy-agent": "~5.0.0", "https-proxy-agent": "~5.0.1", @@ -10747,10 +10748,9 @@ "dev": true }, "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz", + "integrity": "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==" }, "node_modules/html-tags": { "version": "3.3.1", @@ -11558,6 +11558,12 @@ "node": ">=8" } }, + "node_modules/istanbul-reports/node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, "node_modules/jackspeak": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", diff --git a/package.json b/package.json index b6c7c22d3..cb108bd3e 100644 --- a/package.json +++ b/package.json @@ -100,6 +100,7 @@ "express-static-gzip": "~2.1.7", "form-data": "~4.0.0", "gamedig": "~4.1.0", + "html-escaper": "^3.0.3", "http-graceful-shutdown": "~3.1.7", "http-proxy-agent": "~5.0.0", "https-proxy-agent": "~5.0.1", diff --git a/server/google-analytics.js b/server/google-analytics.js index fc9fbec84..3e8e645f4 100644 --- a/server/google-analytics.js +++ b/server/google-analytics.js @@ -1,4 +1,5 @@ const jsesc = require("jsesc"); +const { escape } = require("html-escaper"); /** * Returns a string that represents the javascript that is required to insert the Google Analytics scripts @@ -7,15 +8,18 @@ const jsesc = require("jsesc"); * @returns {string} */ function getGoogleAnalyticsScript(tagId) { - let escapedTagId = jsesc(tagId, { isScriptContext: true }); + let escapedTagIdJS = jsesc(tagId, { isScriptContext: true }); - if (escapedTagId) { - escapedTagId = escapedTagId.trim(); + if (escapedTagIdJS) { + escapedTagIdJS = escapedTagIdJS.trim(); } + // Escape the tag ID for use in an HTML attribute. + let escapedTagIdHTMLAttribute = escape(tagId); + return ` - - + + `; } From 4255496b113abf4ee989333510c62f5e3f2ddc37 Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Fri, 24 Nov 2023 17:29:42 +0800 Subject: [PATCH 21/29] Rewrite Tailscale ping using spawnSync --- server/monitor-types/tailscale-ping.js | 41 +++++++++++--------------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/server/monitor-types/tailscale-ping.js b/server/monitor-types/tailscale-ping.js index eeec9e3f3..3b2ac487f 100644 --- a/server/monitor-types/tailscale-ping.js +++ b/server/monitor-types/tailscale-ping.js @@ -1,6 +1,6 @@ const { MonitorType } = require("./monitor-type"); -const { UP, log } = require("../../src/util"); -const exec = require("child_process").exec; +const { UP } = require("../../src/util"); +const childProcess = require("child_process"); /** * A TailscalePing class extends the MonitorType. @@ -23,7 +23,6 @@ class TailscalePing extends MonitorType { let tailscaleOutput = await this.runTailscalePing(monitor.hostname, monitor.interval); this.parseTailscaleOutput(tailscaleOutput, heartbeat); } catch (err) { - log.debug("Tailscale", err); // trigger log function somewhere to display a notification or alert to the user (but how?) throw new Error(`Error checking Tailscale ping: ${err}`); } @@ -33,30 +32,26 @@ class TailscalePing extends MonitorType { * Runs the Tailscale ping command to the given URL. * * @param {string} hostname - The hostname to ping. + * @param {number} interval * @returns {Promise} - A Promise that resolves to the output of the Tailscale ping command * @throws Will throw an error if the command execution encounters any error. */ async runTailscalePing(hostname, interval) { - let cmd = `tailscale ping ${hostname}`; - - log.debug("Tailscale", cmd); - - return new Promise((resolve, reject) => { - let timeout = interval * 1000 * 0.8; - exec(cmd, { timeout: timeout }, (error, stdout, stderr) => { - // we may need to handle more cases if tailscale reports an error that isn't necessarily an error (such as not-logged in or DERP health-related issues) - if (error) { - reject(`Execution error: ${error.message}`); - return; - } - if (stderr) { - reject(`Error in output: ${stderr}`); - return; - } - - resolve(stdout); - }); + let timeout = interval * 1000 * 0.8; + let res = childProcess.spawnSync("tailscale", [ "ping", hostname ], { + timeout: timeout }); + if (res.error) { + throw new Error(`Execution error: ${res.error.message}`); + } + if (res.stderr && res.stderr.toString()) { + throw new Error(`Error in output: ${res.stderr.toString()}`); + } + if (res.stdout && res.stdout.toString()) { + return res.stdout.toString(); + } else { + throw new Error("No output from Tailscale ping"); + } } /** @@ -74,7 +69,7 @@ class TailscalePing extends MonitorType { heartbeat.status = UP; let time = line.split(" in ")[1].split(" ")[0]; heartbeat.ping = parseInt(time); - heartbeat.msg = line; + heartbeat.msg = "OK"; break; } else if (line.includes("timed out")) { throw new Error(`Ping timed out: "${line}"`); From 9536c6aa6a5fc12749fa54192d0aeb86465a6877 Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Fri, 24 Nov 2023 17:33:13 +0800 Subject: [PATCH 22/29] Minor --- server/socket-handlers/general-socket-handler.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/socket-handlers/general-socket-handler.js b/server/socket-handlers/general-socket-handler.js index c7f77b65b..3fc6f1d57 100644 --- a/server/socket-handlers/general-socket-handler.js +++ b/server/socket-handlers/general-socket-handler.js @@ -52,7 +52,7 @@ module.exports.generalSocketHandler = (socket, server) => { callback({ ok: false, msg: e.message, - }) + }); } }); @@ -75,7 +75,7 @@ module.exports.generalSocketHandler = (socket, server) => { callback({ ok: false, msg: e.message, - }) + }); } }); }; From 67250d6302f42bd9ccb8ac83350b1bbcc71b4086 Mon Sep 17 00:00:00 2001 From: Nelson Chan <3271800+chakflying@users.noreply.github.com> Date: Fri, 24 Nov 2023 18:11:36 +0800 Subject: [PATCH 23/29] Feat: Retries persistence (#3814) * Feat: Retries persistence * Fix: Set duration for first beat of push monitor * Feat: Update UptimeCalculator in push route * Fix: Handle resend in push route * Chore: Remove debug log --- .../2023-09-29-0000-heartbeat-retires.js | 15 +++ server/model/heartbeat.js | 15 +-- server/model/monitor.js | 15 ++- server/routers/api-router.js | 114 ++++++++++++++---- 4 files changed, 125 insertions(+), 34 deletions(-) create mode 100644 db/knex_migrations/2023-09-29-0000-heartbeat-retires.js diff --git a/db/knex_migrations/2023-09-29-0000-heartbeat-retires.js b/db/knex_migrations/2023-09-29-0000-heartbeat-retires.js new file mode 100644 index 000000000..a6b9c7bb9 --- /dev/null +++ b/db/knex_migrations/2023-09-29-0000-heartbeat-retires.js @@ -0,0 +1,15 @@ +exports.up = function (knex) { + // Add new column heartbeat.retries + return knex.schema + .alterTable("heartbeat", function (table) { + table.integer("retries").notNullable().defaultTo(0); + }); + +}; + +exports.down = function (knex) { + return knex.schema + .alterTable("heartbeat", function (table) { + table.dropColumn("retries"); + }); +}; diff --git a/server/model/heartbeat.js b/server/model/heartbeat.js index cc86ef634..9e972a376 100644 --- a/server/model/heartbeat.js +++ b/server/model/heartbeat.js @@ -29,13 +29,14 @@ class Heartbeat extends BeanModel { */ toJSON() { return { - monitorID: this.monitor_id, - status: this.status, - time: this.time, - msg: this.msg, - ping: this.ping, - important: this.important, - duration: this.duration, + monitorID: this._monitorId, + status: this._status, + time: this._time, + msg: this._msg, + ping: this._ping, + important: this._important, + duration: this._duration, + retries: this._retries, }; } diff --git a/server/model/monitor.js b/server/model/monitor.js index f2e14269c..809580d61 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -361,6 +361,9 @@ class Monitor extends BeanModel { previousBeat = await R.findOne("heartbeat", " monitor_id = ? ORDER BY time DESC", [ this.id, ]); + if (previousBeat) { + retries = previousBeat.retries; + } } const isFirstBeat = !previousBeat; @@ -632,6 +635,7 @@ class Monitor extends BeanModel { // If the previous beat was down or pending we use the regular // beatInterval/retryInterval in the setTimeout further below if (previousBeat.status !== (this.isUpsideDown() ? DOWN : UP) || msSinceLastBeat > beatInterval * 1000 + bufferTime) { + bean.duration = Math.round(msSinceLastBeat / 1000); throw new Error("No heartbeat in the time window"); } else { let timeout = beatInterval * 1000 - msSinceLastBeat; @@ -647,6 +651,7 @@ class Monitor extends BeanModel { return; } } else { + bean.duration = beatInterval; throw new Error("No heartbeat in the time window"); } @@ -915,9 +920,14 @@ class Monitor extends BeanModel { } else if ((this.maxretries > 0) && (retries < this.maxretries)) { retries++; bean.status = PENDING; + } else { + // Continue counting retries during DOWN + retries++; } } + bean.retries = retries; + log.debug("monitor", `[${this.name}] Check isImportant`); let isImportant = Monitor.isImportantBeat(isFirstBeat, previousBeat?.status, bean.status); @@ -1437,10 +1447,7 @@ class Monitor extends BeanModel { * @returns {Promise>} Previous heartbeat */ static async getPreviousHeartbeat(monitorID) { - return await R.getRow(` - SELECT ping, status, time FROM heartbeat - WHERE id = (select MAX(id) from heartbeat where monitor_id = ?) - `, [ + return await R.findOne("heartbeat", " id = (select MAX(id) from heartbeat where monitor_id = ?)", [ monitorID ]); } diff --git a/server/routers/api-router.js b/server/routers/api-router.js index 0549518f3..7b14a6dac 100644 --- a/server/routers/api-router.js +++ b/server/routers/api-router.js @@ -64,38 +64,57 @@ router.get("/api/push/:pushToken", async (request, response) => { const previousHeartbeat = await Monitor.getPreviousHeartbeat(monitor.id); - if (monitor.isUpsideDown()) { - status = flipStatus(status); - } - let isFirstBeat = true; - let previousStatus = status; - let duration = 0; let bean = R.dispense("heartbeat"); bean.time = R.isoDateTimeMillis(dayjs.utc()); + bean.monitor_id = monitor.id; + bean.ping = ping; + bean.msg = msg; + bean.downCount = previousHeartbeat?.downCount || 0; if (previousHeartbeat) { isFirstBeat = false; - previousStatus = previousHeartbeat.status; - duration = dayjs(bean.time).diff(dayjs(previousHeartbeat.time), "second"); + bean.duration = dayjs(bean.time).diff(dayjs(previousHeartbeat.time), "second"); } if (await Monitor.isUnderMaintenance(monitor.id)) { msg = "Monitor under maintenance"; - status = MAINTENANCE; + bean.status = MAINTENANCE; + } else { + determineStatus(status, previousHeartbeat, monitor.maxretries, monitor.isUpsideDown(), bean); } - log.debug("router", `/api/push/ called at ${dayjs().format("YYYY-MM-DD HH:mm:ss.SSS")}`); - log.debug("router", "PreviousStatus: " + previousStatus); - log.debug("router", "Current Status: " + status); + // Calculate uptime + let uptimeCalculator = await UptimeCalculator.getUptimeCalculator(monitor.id); + let endTimeDayjs = await uptimeCalculator.update(bean.status, parseFloat(bean.ping)); + bean.end_time = R.isoDateTimeMillis(endTimeDayjs); - bean.important = Monitor.isImportantBeat(isFirstBeat, previousStatus, status); - bean.monitor_id = monitor.id; - bean.status = status; - bean.msg = msg; - bean.ping = ping; - bean.duration = duration; + log.debug("router", `/api/push/ called at ${dayjs().format("YYYY-MM-DD HH:mm:ss.SSS")}`); + log.debug("router", "PreviousStatus: " + previousHeartbeat?.status); + log.debug("router", "Current Status: " + bean.status); + + bean.important = Monitor.isImportantBeat(isFirstBeat, previousHeartbeat?.status, status); + + if (Monitor.isImportantForNotification(isFirstBeat, previousHeartbeat?.status, status)) { + // Reset down count + bean.downCount = 0; + + log.debug("monitor", `[${this.name}] sendNotification`); + await Monitor.sendNotification(isFirstBeat, monitor, bean); + } else { + if (bean.status === DOWN && this.resendInterval > 0) { + ++bean.downCount; + if (bean.downCount >= this.resendInterval) { + // Send notification again, because we are still DOWN + log.debug("monitor", `[${this.name}] sendNotification again: Down Count: ${bean.downCount} | Resend Interval: ${this.resendInterval}`); + await Monitor.sendNotification(isFirstBeat, this, bean); + + // Reset down count + bean.downCount = 0; + } + } + } await R.store(bean); @@ -107,11 +126,6 @@ router.get("/api/push/:pushToken", async (request, response) => { response.json({ ok: true, }); - - if (Monitor.isImportantForNotification(isFirstBeat, previousStatus, status)) { - await Monitor.sendNotification(isFirstBeat, monitor, bean); - } - } catch (e) { response.status(404).json({ ok: false, @@ -562,4 +576,58 @@ router.get("/api/badge/:id/response", cache("5 minutes"), async (request, respon } }); +/** + * Determines the status of the next beat in the push route handling. + * @param {string} status - The reported new status. + * @param {object} previousHeartbeat - The previous heartbeat object. + * @param {number} maxretries - The maximum number of retries allowed. + * @param {boolean} isUpsideDown - Indicates if the monitor is upside down. + * @param {object} bean - The new heartbeat object. + * @returns {void} + */ +function determineStatus(status, previousHeartbeat, maxretries, isUpsideDown, bean) { + if (isUpsideDown) { + status = flipStatus(status); + } + + if (previousHeartbeat) { + if (previousHeartbeat.status === UP && status === DOWN) { + // Going Down + if ((maxretries > 0) && (previousHeartbeat.retries < maxretries)) { + // Retries available + bean.retries = previousHeartbeat.retries + 1; + bean.status = PENDING; + } else { + // No more retries + bean.retries = 0; + bean.status = DOWN; + } + } else if (previousHeartbeat.status === PENDING && status === DOWN && previousHeartbeat.retries < maxretries) { + // Retries available + bean.retries = previousHeartbeat.retries + 1; + bean.status = PENDING; + } else { + // No more retries or not pending + if (status === DOWN) { + bean.retries = previousHeartbeat.retries + 1; + bean.status = status; + } else { + bean.retries = 0; + bean.status = status; + } + } + } else { + // First beat? + if (status === DOWN && maxretries > 0) { + // Retries available + bean.retries = 1; + bean.status = PENDING; + } else { + // Retires not enabled + bean.retries = 0; + bean.status = status; + } + } +} + module.exports = router; From 4ceeb304f12be913288a15d4e7bdd5784c68e297 Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Fri, 24 Nov 2023 18:44:54 +0800 Subject: [PATCH 24/29] Add a script to prepare a changelog --- extra/reformat-changelog.js | 44 +++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 extra/reformat-changelog.js diff --git a/extra/reformat-changelog.js b/extra/reformat-changelog.js new file mode 100644 index 000000000..80a1b725a --- /dev/null +++ b/extra/reformat-changelog.js @@ -0,0 +1,44 @@ +// Generate on GitHub +const input = ` +* Add Korean translation by @Alanimdeo in https://github.com/louislam/dockge/pull/86 +`; + +const template = ` +### 🆕 New Features + +### 💇‍♀️ Improvements + +### 🐞 Bug Fixes + +### ⬆️ Security Fixes + +### 🦎 Translation Contributions + +### Others +- Other small changes, code refactoring and comment/doc updates in this repo: +`; + +const lines = input.split("\n").filter((line) => line.trim() !== ""); + +for (const line of lines) { + // Split the last " by " + const usernamePullRequesURL = line.split(" by ").pop(); + + if (!usernamePullRequesURL) { + console.log("Unable to parse", line); + continue; + } + + const [ username, pullRequestURL ] = usernamePullRequesURL.split(" in "); + const pullRequestID = "#" + pullRequestURL.split("/").pop(); + let message = line.split(" by ").shift(); + + if (!message) { + console.log("Unable to parse", line); + continue; + } + + message = message.split("* ").pop(); + console.log("-", pullRequestID, message, `(Thanks ${username})`); +} +console.log(template); From 73239d441d830db3e6ab8c9558434c1295bafae0 Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Fri, 24 Nov 2023 18:49:27 +0800 Subject: [PATCH 25/29] Update to 1.23.7 --- package-lock.json | 4 ++-- package.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8cf653a17..248e64a5e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "uptime-kuma", - "version": "1.23.6", + "version": "1.23.7", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "uptime-kuma", - "version": "1.23.6", + "version": "1.23.7", "license": "MIT", "dependencies": { "@grpc/grpc-js": "~1.7.3", diff --git a/package.json b/package.json index cb108bd3e..b24f3ec21 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "uptime-kuma", - "version": "1.23.6", + "version": "1.23.7", "license": "MIT", "repository": { "type": "git", @@ -40,7 +40,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.23.6 && npm ci --production && npm run download-dist", + "setup": "git checkout 1.23.7 && 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", From 676587b7fba222d277f6a0374af48a59b8ee189f Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Sat, 25 Nov 2023 03:25:50 +0800 Subject: [PATCH 26/29] Merge package-lock.json --- package-lock.json | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 885bab3ab..d5f1699f6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,6 +33,7 @@ "express-static-gzip": "~2.1.7", "form-data": "~4.0.0", "gamedig": "~4.1.0", + "html-escaper": "^3.0.3", "http-graceful-shutdown": "~3.1.7", "http-proxy-agent": "~5.0.0", "https-proxy-agent": "~5.0.1", @@ -9037,10 +9038,9 @@ "dev": true }, "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz", + "integrity": "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==" }, "node_modules/html-tags": { "version": "3.3.1", @@ -9854,6 +9854,12 @@ "node": ">=8" } }, + "node_modules/istanbul-reports/node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, "node_modules/jackspeak": { "version": "2.3.6", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", From 60be875eddb44e7cbf9c8d469e7362189c39f9bc Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Sat, 25 Nov 2023 03:28:45 +0800 Subject: [PATCH 27/29] Fix a merge issue --- server/uptime-kuma-server.js | 1 + 1 file changed, 1 insertion(+) diff --git a/server/uptime-kuma-server.js b/server/uptime-kuma-server.js index 3944a7e69..67321abca 100644 --- a/server/uptime-kuma-server.js +++ b/server/uptime-kuma-server.js @@ -12,6 +12,7 @@ const { Settings } = require("./settings"); const dayjs = require("dayjs"); const childProcess = require("child_process"); const path = require("path"); +const axios = require("axios"); // DO NOT IMPORT HERE IF THE MODULES USED `UptimeKumaServer.getInstance()`, put at the bottom of this file instead. /** From 2ad8af9d14be5dafe384f1347e223830174da837 Mon Sep 17 00:00:00 2001 From: Ikko Eltociear Ashimine Date: Sat, 25 Nov 2023 23:08:21 +0900 Subject: [PATCH 28/29] Minor (#4104) infomation -> information --- extra/fs-rmSync.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extra/fs-rmSync.js b/extra/fs-rmSync.js index 0fdbab936..a42e30a68 100644 --- a/extra/fs-rmSync.js +++ b/extra/fs-rmSync.js @@ -5,7 +5,7 @@ const fs = require("fs"); * or the `recursive` property removing completely in the future Node.js version. * See the link below. * @todo Once we drop the support for Node.js v14 (or at least versions before v14.14.0), we can safely replace this function with `fs.rmSync`, since `fs.rmSync` was add in Node.js v14.14.0 and currently we supports all the Node.js v14 versions that include the versions before the v14.14.0, and this function have almost the same signature with `fs.rmSync`. - * @link https://nodejs.org/docs/latest-v16.x/api/deprecations.html#dep0147-fsrmdirpath--recursive-true- the deprecation infomation of `fs.rmdirSync` + * @link https://nodejs.org/docs/latest-v16.x/api/deprecations.html#dep0147-fsrmdirpath--recursive-true- the deprecation information of `fs.rmdirSync` * @link https://nodejs.org/docs/latest-v16.x/api/fs.html#fsrmsyncpath-options the document of `fs.rmSync` * @param {fs.PathLike} path Valid types for path values in "fs". * @param {fs.RmDirOptions} options options for `fs.rmdirSync`, if `fs.rmSync` is available and property `recursive` is true, it will automatically have property `force` with value `true`. From b8bd17ddbdb68a018d85bd4ccb67cbb2d37b0bf7 Mon Sep 17 00:00:00 2001 From: Nelson Chan <3271800+chakflying@users.noreply.github.com> Date: Sun, 26 Nov 2023 18:47:56 +0800 Subject: [PATCH 29/29] Fix: Add timeout to testDockerHost (#4097) --- server/docker.js | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/server/docker.js b/server/docker.js index a96324a9f..bec0e0b12 100644 --- a/server/docker.js +++ b/server/docker.js @@ -1,10 +1,10 @@ const axios = require("axios"); const { R } = require("redbean-node"); -const version = require("../package.json").version; const https = require("https"); const fs = require("fs"); const path = require("path"); const Database = require("./database"); +const { axiosAbortSignal } = require("./util-server"); class DockerHost { @@ -70,9 +70,11 @@ class DockerHost { static async testDockerHost(dockerHost) { const options = { url: "/containers/json?all=true", + timeout: 5000, headers: { "Accept": "*/*", }, + signal: axiosAbortSignal(6000), }; if (dockerHost.dockerType === "socket") { @@ -82,26 +84,33 @@ class DockerHost { options.httpsAgent = new https.Agent(DockerHost.getHttpsAgentOptions(dockerHost.dockerType, options.baseURL)); } - let res = await axios.request(options); + try { + let res = await axios.request(options); - if (Array.isArray(res.data)) { + if (Array.isArray(res.data)) { - if (res.data.length > 1) { + if (res.data.length > 1) { + + if ("ImageID" in res.data[0]) { + return res.data.length; + } else { + throw new Error("Invalid Docker response, is it Docker really a daemon?"); + } - if ("ImageID" in res.data[0]) { - return res.data.length; } else { - throw new Error("Invalid Docker response, is it Docker really a daemon?"); + return res.data.length; } } else { - return res.data.length; + throw new Error("Invalid Docker response, is it Docker really a daemon?"); + } + } catch (e) { + if (e.code === "ECONNABORTED" || e.name === "CanceledError") { + throw new Error("Connection to Docker daemon timed out."); + } else { + throw e; } - - } else { - throw new Error("Invalid Docker response, is it Docker really a daemon?"); } - } /**