From b38206438489c6fa3d8d6a382e8326aba39fad29 Mon Sep 17 00:00:00 2001 From: Kyle <3204236+DevKyleS@users.noreply.github.com> Date: Tue, 13 Sep 2022 21:52:32 -0600 Subject: [PATCH 01/17] Replace port env var in healthcheck.js --- extra/healthcheck.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extra/healthcheck.js b/extra/healthcheck.js index 7c3a7eb42..3a7661082 100644 --- a/extra/healthcheck.js +++ b/extra/healthcheck.js @@ -25,7 +25,7 @@ if (!hostname && !FBSD) { hostname = process.env.HOST; } -const port = parseInt(process.env.UPTIME_KUMA_PORT || process.env.PORT || 3001); +const port = parseInt(process.env.UPTIME_KUMA_SERVICE_PORT || process.env.PORT || 3001); let options = { host: hostname || "127.0.0.1", From b4f0f8bca5b62a9fe12d913504b842c3da81cf73 Mon Sep 17 00:00:00 2001 From: Kyle <3204236+DevKyleS@users.noreply.github.com> Date: Tue, 13 Sep 2022 22:18:00 -0600 Subject: [PATCH 02/17] Replace healthcheck hostname env variable name --- extra/healthcheck.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extra/healthcheck.js b/extra/healthcheck.js index 3a7661082..9e5e4e3d8 100644 --- a/extra/healthcheck.js +++ b/extra/healthcheck.js @@ -18,7 +18,7 @@ if (sslKey && sslCert) { // If host is omitted, the server will accept connections on the unspecified IPv6 address (::) when IPv6 is available and the unspecified IPv4 address (0.0.0.0) otherwise. // Dual-stack support for (::) -let hostname = process.env.UPTIME_KUMA_HOST; +let hostname = process.env.UPTIME_KUMA_SERVICE_HOST; // Also read HOST if not *BSD, as HOST is a system environment variable in FreeBSD if (!hostname && !FBSD) { From db4b2cd984e9b7dd82e0af5995e7235eb2f67517 Mon Sep 17 00:00:00 2001 From: Kyle <3204236+DevKyleS@users.noreply.github.com> Date: Wed, 14 Sep 2022 14:25:56 -0600 Subject: [PATCH 03/17] Re-add support for UPTIME_KUMA_HOST and _PORT in healthcheck.js --- extra/healthcheck.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/extra/healthcheck.js b/extra/healthcheck.js index 9e5e4e3d8..0c35fe2f5 100644 --- a/extra/healthcheck.js +++ b/extra/healthcheck.js @@ -18,17 +18,17 @@ if (sslKey && sslCert) { // If host is omitted, the server will accept connections on the unspecified IPv6 address (::) when IPv6 is available and the unspecified IPv4 address (0.0.0.0) otherwise. // Dual-stack support for (::) -let hostname = process.env.UPTIME_KUMA_SERVICE_HOST; +let hostname = process.env.UPTIME_KUMA_SERVICE_HOST || process.env.UPTIME_KUMA_HOST || "127.0.0.1"; // Also read HOST if not *BSD, as HOST is a system environment variable in FreeBSD if (!hostname && !FBSD) { hostname = process.env.HOST; } -const port = parseInt(process.env.UPTIME_KUMA_SERVICE_PORT || process.env.PORT || 3001); +const port = parseInt(process.env.UPTIME_KUMA_SERVICE_PORT || process.env.UPTIME_KUMA_PORT || process.env.PORT || 3001); let options = { - host: hostname || "127.0.0.1", + host: hostname, port: port, timeout: 28 * 1000, }; From 7aa3f6c559f1624442e0f0239eca5c31bdf61ab0 Mon Sep 17 00:00:00 2001 From: Kyle <3204236+DevKyleS@users.noreply.github.com> Date: Wed, 14 Sep 2022 15:04:21 -0600 Subject: [PATCH 04/17] Corrected default hostname port for healthcheck --- extra/healthcheck.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extra/healthcheck.js b/extra/healthcheck.js index 0c35fe2f5..a169f1f43 100644 --- a/extra/healthcheck.js +++ b/extra/healthcheck.js @@ -18,7 +18,7 @@ if (sslKey && sslCert) { // If host is omitted, the server will accept connections on the unspecified IPv6 address (::) when IPv6 is available and the unspecified IPv4 address (0.0.0.0) otherwise. // Dual-stack support for (::) -let hostname = process.env.UPTIME_KUMA_SERVICE_HOST || process.env.UPTIME_KUMA_HOST || "127.0.0.1"; +let hostname = process.env.UPTIME_KUMA_SERVICE_HOST || process.env.UPTIME_KUMA_HOST || "::"; // Also read HOST if not *BSD, as HOST is a system environment variable in FreeBSD if (!hostname && !FBSD) { From 942b55ca031b4a487d299c7e189052634fdb9c17 Mon Sep 17 00:00:00 2001 From: Nelson Chan Date: Tue, 3 Jan 2023 21:45:55 +0800 Subject: [PATCH 05/17] Fix: Add support for maintenance in badges --- server/config.js | 1 + server/routers/api-router.js | 36 +++++++++++++++++++++++++++++++++--- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/server/config.js b/server/config.js index d46f24b75..398ddbb10 100644 --- a/server/config.js +++ b/server/config.js @@ -5,6 +5,7 @@ const badgeConstants = { naColor: "#999", defaultUpColor: "#66c20a", defaultDownColor: "#c2290a", + defaultMaintenanceColor: "#1747f5", defaultPingColor: "blue", // as defined by badge-maker / shields.io defaultStyle: "flat", defaultPingValueSuffix: "ms", diff --git a/server/routers/api-router.js b/server/routers/api-router.js index bbecbced3..45236d206 100644 --- a/server/routers/api-router.js +++ b/server/routers/api-router.js @@ -111,8 +111,10 @@ router.get("/api/badge/:id/status", cache("5 minutes"), async (request, response label, upLabel = "Up", downLabel = "Down", + maintenanceLabel = "Maintenance", upColor = badgeConstants.defaultUpColor, downColor = badgeConstants.defaultDownColor, + maintenanceColor = badgeConstants.defaultMaintenanceColor, style = badgeConstants.defaultStyle, value, // for demo purpose only } = request.query; @@ -139,11 +141,39 @@ router.get("/api/badge/:id/status", cache("5 minutes"), async (request, response badgeValues.color = badgeConstants.naColor; } else { const heartbeat = await Monitor.getPreviousHeartbeat(requestedMonitorId); - const state = overrideValue !== undefined ? overrideValue : heartbeat.status === 1; + const state = overrideValue !== undefined ? overrideValue : heartbeat.status; badgeValues.label = label ? label : ""; - badgeValues.color = state ? upColor : downColor; - badgeValues.message = label ?? state ? upLabel : downLabel; + switch (state) { + case 1: + badgeValues.color = upColor; + break; + case 3: + badgeValues.color = maintenanceColor; + break; + case 0: + badgeValues.color = downColor; + break; + default: + badgeValues.color = badgeConstants.naColor; + } + if (label !== undefined) { + badgeValues.message = label; + } else { + switch (state) { + case 1: + badgeValues.message = upLabel; + break; + case 3: + badgeValues.message = maintenanceLabel; + break; + case 0: + badgeValues.message = downLabel; + break; + default: + badgeValues.message = "N/A"; + } + } } // build the svg based on given values From dd82f36da3fa3b2852bd470e3f62374f27e8d8e2 Mon Sep 17 00:00:00 2001 From: Nelson Chan Date: Mon, 9 Jan 2023 00:16:18 +0800 Subject: [PATCH 06/17] Fix: Improve syntax & fix weird label logic --- server/routers/api-router.js | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/server/routers/api-router.js b/server/routers/api-router.js index 45236d206..41608a049 100644 --- a/server/routers/api-router.js +++ b/server/routers/api-router.js @@ -143,36 +143,23 @@ router.get("/api/badge/:id/status", cache("5 minutes"), async (request, response const heartbeat = await Monitor.getPreviousHeartbeat(requestedMonitorId); const state = overrideValue !== undefined ? overrideValue : heartbeat.status; - badgeValues.label = label ? label : ""; + badgeValues.label = label ?? ""; switch (state) { case 1: badgeValues.color = upColor; + badgeValues.message = upLabel; break; case 3: badgeValues.color = maintenanceColor; + badgeValues.message = maintenanceLabel; break; case 0: badgeValues.color = downColor; + badgeValues.message = downLabel; break; default: badgeValues.color = badgeConstants.naColor; - } - if (label !== undefined) { - badgeValues.message = label; - } else { - switch (state) { - case 1: - badgeValues.message = upLabel; - break; - case 3: - badgeValues.message = maintenanceLabel; - break; - case 0: - badgeValues.message = downLabel; - break; - default: - badgeValues.message = "N/A"; - } + badgeValues.message = "N/A"; } } From 21b418230cb76d970f40a81fd6cf53145f3d7ae9 Mon Sep 17 00:00:00 2001 From: Nelson Chan Date: Tue, 10 Jan 2023 19:25:31 +0800 Subject: [PATCH 07/17] Chore: reorder cases Co-authored-by: Frank Elsinga --- server/routers/api-router.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/server/routers/api-router.js b/server/routers/api-router.js index 41608a049..f2e0874e2 100644 --- a/server/routers/api-router.js +++ b/server/routers/api-router.js @@ -145,6 +145,10 @@ router.get("/api/badge/:id/status", cache("5 minutes"), async (request, response badgeValues.label = label ?? ""; switch (state) { + case 0: + badgeValues.color = downColor; + badgeValues.message = downLabel; + break; case 1: badgeValues.color = upColor; badgeValues.message = upLabel; @@ -153,10 +157,6 @@ router.get("/api/badge/:id/status", cache("5 minutes"), async (request, response badgeValues.color = maintenanceColor; badgeValues.message = maintenanceLabel; break; - case 0: - badgeValues.color = downColor; - badgeValues.message = downLabel; - break; default: badgeValues.color = badgeConstants.naColor; badgeValues.message = "N/A"; From 0ed3dd5e4fd8e33ef2d1d579e1b9d882ce4f09c3 Mon Sep 17 00:00:00 2001 From: Nelson Chan Date: Thu, 12 Jan 2023 04:14:46 +0800 Subject: [PATCH 08/17] Fix: Add support for pending in badges --- server/config.js | 1 + server/routers/api-router.js | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/server/config.js b/server/config.js index 398ddbb10..0523e7078 100644 --- a/server/config.js +++ b/server/config.js @@ -5,6 +5,7 @@ const badgeConstants = { naColor: "#999", defaultUpColor: "#66c20a", defaultDownColor: "#c2290a", + defaultPendingColor: "#f8a306", defaultMaintenanceColor: "#1747f5", defaultPingColor: "blue", // as defined by badge-maker / shields.io defaultStyle: "flat", diff --git a/server/routers/api-router.js b/server/routers/api-router.js index f2e0874e2..6001d58e0 100644 --- a/server/routers/api-router.js +++ b/server/routers/api-router.js @@ -111,9 +111,11 @@ router.get("/api/badge/:id/status", cache("5 minutes"), async (request, response label, upLabel = "Up", downLabel = "Down", + pendingLabel = "Pending", maintenanceLabel = "Maintenance", upColor = badgeConstants.defaultUpColor, downColor = badgeConstants.defaultDownColor, + pendingColor = badgeConstants.defaultPendingColor, maintenanceColor = badgeConstants.defaultMaintenanceColor, style = badgeConstants.defaultStyle, value, // for demo purpose only @@ -153,6 +155,10 @@ router.get("/api/badge/:id/status", cache("5 minutes"), async (request, response badgeValues.color = upColor; badgeValues.message = upLabel; break; + case 2: + badgeValues.color = pendingColor; + badgeValues.message = pendingLabel; + break; case 3: badgeValues.color = maintenanceColor; badgeValues.message = maintenanceLabel; From 98f5bc51a8929c6f4cdc1e3a1d23a858307b2549 Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Sat, 28 Jan 2023 12:38:39 +0800 Subject: [PATCH 09/17] Change golang version --- docker/builder-go.dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/builder-go.dockerfile b/docker/builder-go.dockerfile index 79c1a95ba..1d25843bc 100644 --- a/docker/builder-go.dockerfile +++ b/docker/builder-go.dockerfile @@ -2,7 +2,7 @@ # Build in Golang # Run npm run build-healthcheck-armv7 in the host first, another it will be super slow where it is building the armv7 healthcheck ############################################ -FROM golang:1.19.4-buster +FROM golang:1.19-buster WORKDIR /app ARG TARGETPLATFORM COPY ./extra/ ./extra/ From 1dc2546a3910528ed341537571bfc81615e37d48 Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Sat, 28 Jan 2023 13:39:23 +0800 Subject: [PATCH 10/17] Lint --- src/router.js | 1 - tsconfig.json | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/router.js b/src/router.js index 7bb474eef..35647511f 100644 --- a/src/router.js +++ b/src/router.js @@ -32,7 +32,6 @@ import Proxies from "./components/settings/Proxies.vue"; import Backup from "./components/settings/Backup.vue"; import About from "./components/settings/About.vue"; - const routes = [ { path: "/", diff --git a/tsconfig.json b/tsconfig.json index c54546424..441d846ed 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,7 +6,7 @@ "module": "commonjs", "lib": [ "es2020", - "DOM", + "DOM" ], "removeComments": false, "preserveConstEnums": true, From ddce8f0cb06016d20c4f1910693d5258f95ff275 Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Sat, 28 Jan 2023 19:00:13 +0800 Subject: [PATCH 11/17] Fix plugin installation --- docker/alpine-base.dockerfile | 2 +- docker/debian-base.dockerfile | 2 +- server/plugins-manager.js | 37 ++++++++++++++++++----- server/socket-handlers/plugins-handler.js | 8 +++-- src/components/settings/Plugins.vue | 2 +- 5 files changed, 38 insertions(+), 13 deletions(-) diff --git a/docker/alpine-base.dockerfile b/docker/alpine-base.dockerfile index 82bc7bb05..276d6e450 100644 --- a/docker/alpine-base.dockerfile +++ b/docker/alpine-base.dockerfile @@ -3,6 +3,6 @@ FROM node:16-alpine3.12 WORKDIR /app # Install apprise, iputils for non-root ping, setpriv -RUN apk add --no-cache iputils setpriv dumb-init python3 py3-cryptography py3-pip py3-six py3-yaml py3-click py3-markdown py3-requests py3-requests-oauthlib && \ +RUN apk add --no-cache iputils setpriv dumb-init python3 py3-cryptography py3-pip py3-six py3-yaml py3-click py3-markdown py3-requests py3-requests-oauthlib git && \ pip3 --no-cache-dir install apprise==1.2.1 && \ rm -rf /root/.cache diff --git a/docker/debian-base.dockerfile b/docker/debian-base.dockerfile index d94b4c7fe..026189c47 100644 --- a/docker/debian-base.dockerfile +++ b/docker/debian-base.dockerfile @@ -10,7 +10,7 @@ WORKDIR /app # Stupid python3 and python3-pip actually install a lot of useless things into Debian, specify --no-install-recommends to skip them, make the base even smaller than alpine! RUN apt update && \ apt --yes --no-install-recommends install python3 python3-pip python3-cryptography python3-six python3-yaml python3-click python3-markdown python3-requests python3-requests-oauthlib \ - sqlite3 iputils-ping util-linux dumb-init && \ + sqlite3 iputils-ping util-linux dumb-init git && \ pip3 --no-cache-dir install apprise==1.2.1 && \ rm -rf /var/lib/apt/lists/* && \ apt --yes autoremove diff --git a/server/plugins-manager.js b/server/plugins-manager.js index e48c53c89..674ab9691 100644 --- a/server/plugins-manager.js +++ b/server/plugins-manager.js @@ -72,6 +72,12 @@ class PluginsManager { * @param {string} name Directory name, also known as plugin unique name */ downloadPlugin(repoURL, name) { + if (fs.existsSync(this.pluginsDir + name)) { + log.info("plugin", "Plugin folder already exists? Removing..."); + fs.rmSync(this.pluginsDir + name, { + recursive: true + }); + } log.info("plugin", "Installing plugin: " + name + " " + repoURL); let result = Git.clone(repoURL, this.pluginsDir, name); log.info("plugin", "Install result: " + result); @@ -115,13 +121,19 @@ class PluginsManager { * @returns {Promise<[]>} */ async fetchPluginList() { - const res = await axios.get("https://uptime.kuma.pet/c/plugins.json"); - const list = res.data.pluginList; + let remotePluginList; + try { + const res = await axios.get("https://uptime.kuma.pet/c/plugins.json"); + remotePluginList = res.data.pluginList; + } catch (e) { + log.error("plugin", "Failed to fetch plugin list: " + e.message); + remotePluginList = []; + } for (let plugin of this.pluginList) { let find = false; // Try to merge - for (let remotePlugin of list) { + for (let remotePlugin of remotePluginList) { if (remotePlugin.name === plugin.info.name) { find = true; remotePlugin.installed = true; @@ -136,17 +148,17 @@ class PluginsManager { // Local plugin if (!find) { plugin.info.local = true; - list.push(plugin.info); + remotePluginList.push(plugin.info); } } // Sort Installed first, then sort by name - return list.sort((a, b) => { + return remotePluginList.sort((a, b) => { if (a.installed === b.installed) { - if ( a.fullName < b.fullName ) { + if (a.fullName < b.fullName) { return -1; } - if ( a.fullName > b.fullName ) { + if (a.fullName > b.fullName) { return 1; } return 0; @@ -191,15 +203,24 @@ class PluginWrapper { let indexFile = this.pluginDir + "/index.js"; let packageJSON = this.pluginDir + "/package.json"; + log.info("plugin", "Installing dependencies"); + if (fs.existsSync(indexFile)) { // Install dependencies - childProcess.execSync("npm install", { + let result = childProcess.spawnSync("npm", [ "install" ], { cwd: this.pluginDir, env: { + ...process.env, PLAYWRIGHT_BROWSERS_PATH: "../../browsers", // Special handling for read-browser-monitor } }); + if (result.stdout) { + log.info("plugin", "Install dependencies result: " + result.stdout.toString("utf-8")); + } else { + log.warn("plugin", "Install dependencies result: no output"); + } + this.pluginClass = require(path.join(process.cwd(), indexFile)); let pluginClassType = typeof this.pluginClass; diff --git a/server/socket-handlers/plugins-handler.js b/server/socket-handlers/plugins-handler.js index 4ee712c79..533da309b 100644 --- a/server/socket-handlers/plugins-handler.js +++ b/server/socket-handlers/plugins-handler.js @@ -1,5 +1,6 @@ const { checkLogin } = require("../util-server"); -const { PluginManager } = require("../plugins-manager"); +const { PluginsManager } = require("../plugins-manager"); +const { log } = require("../../src/util.js"); /** * Handlers for plugins @@ -15,7 +16,9 @@ module.exports.pluginsHandler = (socket, server) => { try { checkLogin(socket); - if (PluginManager.disable) { + log.debug("plugin", "PluginManager.disable: " + PluginsManager.disable); + + if (PluginsManager.disable) { throw new Error("Plugin Disabled: In order to enable plugin feature, you need to use the default data directory: ./data/"); } @@ -25,6 +28,7 @@ module.exports.pluginsHandler = (socket, server) => { pluginList, }); } catch (error) { + log.warn("plugin", "Error: " + error.message); callback({ ok: false, msg: error.message, diff --git a/src/components/settings/Plugins.vue b/src/components/settings/Plugins.vue index ca39e7adc..614034fcb 100644 --- a/src/components/settings/Plugins.vue +++ b/src/components/settings/Plugins.vue @@ -48,7 +48,7 @@ export default { this.remotePluginList = res.pluginList; this.remotePluginListMsg = ""; } else { - this.remotePluginListMsg = this.$t("loadingError") + " " + res.message; + this.remotePluginListMsg = this.$t("loadingError") + " " + res.msg; } }); } From 6828d337aee65a65490d66d32c26e37a72e09f71 Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Mon, 30 Jan 2023 00:00:41 +0800 Subject: [PATCH 12/17] Disable HTTP(s) - Browser Engine Reason: Unfortunately, after some test, I found that Playwright requires a lot of libraries to be installed on the Linux host in order to start Chrome or Firefox. It will be hard to install, so I hide this feature for now. --- src/pages/EditMonitor.vue | 4 ++++ src/pages/Settings.vue | 5 ++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue index 0f7fc4a58..0f7e86a3c 100644 --- a/src/pages/EditMonitor.vue +++ b/src/pages/EditMonitor.vue @@ -71,12 +71,16 @@ + + diff --git a/src/pages/Settings.vue b/src/pages/Settings.vue index 2b08d04ea..b034a5411 100644 --- a/src/pages/Settings.vue +++ b/src/pages/Settings.vue @@ -113,9 +113,12 @@ export default { backup: { title: this.$t("Backup"), }, + /* + Hidden for now: Unfortunately, after some test, I found that Playwright requires a lot of libraries to be installed on the Linux host in order to start Chrome or Firefox. + It will be hard to install, so I hide this feature for now. But it still accessible via URL: /settings/plugins. plugins: { title: this.$tc("plugin", 2), - }, + },*/ about: { title: this.$t("About"), }, From 4fb43034cda45230dd58bccbae50c8774d39b40d Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Mon, 30 Jan 2023 21:46:27 +0800 Subject: [PATCH 13/17] Handle k8s in healthcheck.go --- extra/healthcheck.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/extra/healthcheck.go b/extra/healthcheck.go index 302883d84..f79b3e65b 100644 --- a/extra/healthcheck.go +++ b/extra/healthcheck.go @@ -11,12 +11,17 @@ import ( "net/http" "os" "runtime" + "strings" "time" ) func main() { isFreeBSD := runtime.GOOS == "freebsd" + // Is K8S + uptime-kuma as the container name + // See #2083 + isK8s := strings.HasPrefix(os.Getenv("UPTIME_KUMA_PORT"), "tcp://") + // process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{ InsecureSkipVerify: true, @@ -44,7 +49,11 @@ func main() { hostname = "127.0.0.1" } - port := os.Getenv("UPTIME_KUMA_PORT") + port := "" + // UPTIME_KUMA_PORT is override by K8S unexpectedly, + if !isK8s { + port = os.Getenv("UPTIME_KUMA_PORT") + } if len(port) == 0 { port = os.Getenv("PORT") } From 348d0170fa9f561c1ea8dcd714ff7936e44bc8d4 Mon Sep 17 00:00:00 2001 From: Nelson Chan Date: Wed, 1 Feb 2023 05:33:36 +0800 Subject: [PATCH 14/17] Chore: Use constants instead of int --- server/routers/api-router.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/server/routers/api-router.js b/server/routers/api-router.js index 6001d58e0..e95fd045e 100644 --- a/server/routers/api-router.js +++ b/server/routers/api-router.js @@ -4,7 +4,7 @@ const { R } = require("redbean-node"); const apicache = require("../modules/apicache"); const Monitor = require("../model/monitor"); const dayjs = require("dayjs"); -const { UP, MAINTENANCE, DOWN, flipStatus, log } = require("../../src/util"); +const { UP, MAINTENANCE, DOWN, PENDING, flipStatus, log } = require("../../src/util"); const StatusPage = require("../model/status_page"); const { UptimeKumaServer } = require("../uptime-kuma-server"); const { makeBadge } = require("badge-maker"); @@ -147,19 +147,19 @@ router.get("/api/badge/:id/status", cache("5 minutes"), async (request, response badgeValues.label = label ?? ""; switch (state) { - case 0: + case DOWN: badgeValues.color = downColor; badgeValues.message = downLabel; break; - case 1: + case UP: badgeValues.color = upColor; badgeValues.message = upLabel; break; - case 2: + case PENDING: badgeValues.color = pendingColor; badgeValues.message = pendingLabel; break; - case 3: + case MAINTENANCE: badgeValues.color = maintenanceColor; badgeValues.message = maintenanceLabel; break; From 95c934e08bf7ea97a85a7107059d804950907953 Mon Sep 17 00:00:00 2001 From: Nelson Chan Date: Fri, 20 Jan 2023 06:33:45 +0800 Subject: [PATCH 15/17] Fix: Do not allow white space around IP Feat: Trim input on submit Test: Add test for whitespace regex match --- src/pages/EditMonitor.vue | 8 ++++++++ src/util-frontend.js | 2 +- test/cypress/unit/util-frontend.spec.js | 14 +++++++++++--- 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue index 00f45d7eb..e9cbd8245 100644 --- a/src/pages/EditMonitor.vue +++ b/src/pages/EditMonitor.vue @@ -949,6 +949,14 @@ message HealthCheckResponse { this.monitor.headers = JSON.stringify(JSON.parse(this.monitor.headers), null, 4); } + if (this.monitor.hostname) { + this.monitor.hostname = this.monitor.hostname.trim(); + } + + if (this.monitor.url) { + this.monitor.url = this.monitor.url.trim(); + } + if (this.isAdd) { this.$root.add(this.monitor, async (res) => { diff --git a/src/util-frontend.js b/src/util-frontend.js index 55d0f6349..882ee2914 100644 --- a/src/util-frontend.js +++ b/src/util-frontend.js @@ -88,7 +88,7 @@ export function hostNameRegexPattern(mqtt = false) { // mqtt, mqtts, ws and wss schemes accepted by mqtt.js (https://github.com/mqttjs/MQTT.js/#connect) const mqttSchemeRegexPattern = "((mqtt|ws)s?:\\/\\/)?"; // Source: https://digitalfortress.tech/tips/top-15-commonly-used-regex/ - const ipRegexPattern = `((^\\s*${mqtt ? mqttSchemeRegexPattern : ""}((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\\s*$)|(^\\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:)))(%.+)?\\s*$))`; + const ipRegexPattern = `((^${mqtt ? mqttSchemeRegexPattern : ""}((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))$)|(^((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)(\\.(25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)){3}))|:)))(%.+)?$))`; // Source: https://stackoverflow.com/questions/106179/regular-expression-to-match-dns-hostname-or-ip-address const hostNameRegexPattern = `^${mqtt ? mqttSchemeRegexPattern : ""}([a-zA-Z0-9])?(([a-zA-Z0-9_]|[a-zA-Z0-9_][a-zA-Z0-9\\-_]*[a-zA-Z0-9_])\\.)*([A-Za-z0-9_]|[A-Za-z0-9_][A-Za-z0-9\\-_]*[A-Za-z0-9_])$`; diff --git a/test/cypress/unit/util-frontend.spec.js b/test/cypress/unit/util-frontend.spec.js index 6abedf821..9bfd4becc 100644 --- a/test/cypress/unit/util-frontend.spec.js +++ b/test/cypress/unit/util-frontend.spec.js @@ -9,7 +9,11 @@ describe("Test util-frontend.js", () => { expect(regex.test("www.test.com")).to.be.true; expect(regex.test("127.0.0.1")).to.be.true; expect(regex.test("192.168.1.156")).to.be.true; - + expect(regex.test(" 192.168.1.145")).to.be.false; + expect(regex.test("192.168.1.145 ")).to.be.false; + expect(regex.test(" fe80::3282:3ff:ae28:592")).to.be.false; + expect(regex.test("fe80::3282:3ff:ae28:592 ")).to.be.false; + ["mqtt", "mqtts", "ws", "wss"].forEach(schema => { expect(regex.test(`${schema}://www.test.com`)).to.be.false; expect(regex.test(`${schema}://127.0.0.1`)).to.be.false; @@ -23,11 +27,15 @@ describe("Test util-frontend.js", () => { expect(regex.test("www.test.com")).to.be.true; expect(regex.test("127.0.0.1")).to.be.true; expect(regex.test("192.168.1.156")).to.be.true; - + expect(regex.test(" 192.168.1.145")).to.be.false; + expect(regex.test("192.168.1.145 ")).to.be.false; + expect(regex.test(" fe80::3282:3ff:ae28:592")).to.be.false; + expect(regex.test("fe80::3282:3ff:ae28:592 ")).to.be.false; + ["mqtt", "mqtts", "ws", "wss"].forEach(schema => { expect(regex.test(`${schema}://www.test.com`)).to.be.true; expect(regex.test(`${schema}://127.0.0.1`)).to.be.true; }); }); }); -}); \ No newline at end of file +}); From 683f446cf573127e7bd02fccabb31d051655277c Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Wed, 1 Feb 2023 20:07:08 +0800 Subject: [PATCH 16/17] Add support for `.env` --- package-lock.json | 18 ++++++++++++++++-- package.json | 1 + server/server.js | 3 +++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9da629687..c5429446f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "uptime-kuma", - "version": "1.19.5", + "version": "1.19.6", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "uptime-kuma", - "version": "1.19.5", + "version": "1.19.6", "license": "MIT", "dependencies": { "@grpc/grpc-js": "~1.7.3", @@ -27,6 +27,7 @@ "compare-versions": "~3.6.0", "compression": "~1.7.4", "dayjs": "~1.11.5", + "dotenv": "~16.0.3", "express": "~4.17.3", "express-basic-auth": "~1.2.1", "express-static-gzip": "~2.1.7", @@ -7813,6 +7814,14 @@ "url": "https://github.com/fb55/domutils?sponsor=1" } }, + "node_modules/dotenv": { + "version": "16.0.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", + "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==", + "engines": { + "node": ">=12" + } + }, "node_modules/duplexify": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz", @@ -24881,6 +24890,11 @@ "domhandler": "^5.0.1" } }, + "dotenv": { + "version": "16.0.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", + "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==" + }, "duplexify": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz", diff --git a/package.json b/package.json index 9e635d1ba..901408363 100644 --- a/package.json +++ b/package.json @@ -84,6 +84,7 @@ "compare-versions": "~3.6.0", "compression": "~1.7.4", "dayjs": "~1.11.5", + "dotenv": "~16.0.3", "express": "~4.17.3", "express-basic-auth": "~1.2.1", "express-static-gzip": "~2.1.7", diff --git a/server/server.js b/server/server.js index 5fbc8e554..1073f3bef 100644 --- a/server/server.js +++ b/server/server.js @@ -11,6 +11,9 @@ dayjs.extend(require("dayjs/plugin/utc")); dayjs.extend(require("./modules/dayjs/plugin/timezone")); dayjs.extend(require("dayjs/plugin/customParseFormat")); +// Load environment variables from `.env` +require("dotenv").config(); + // Check Node.js Version const nodeVersion = parseInt(process.versions.node.split(".")[0]); const requiredVersion = 14; From 89465e676858b6f456f6d81f65dadadd60cead73 Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Wed, 1 Feb 2023 22:39:09 +0800 Subject: [PATCH 17/17] Update CONTRIBUTING.md --- CONTRIBUTING.md | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f46b17e1a..3f4550928 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -17,8 +17,11 @@ The frontend code build into "dist" directory. The server (express.js) exposes t ## Directories +- config (dev config files) - data (App data) +- db (Base database and migration scripts) - dist (Frontend build) +- docker (Dockerfiles) - extra (Extra useful scripts) - public (Frontend resources for dev only) - server (Server source code) @@ -80,13 +83,13 @@ Before deep into coding, discussion first is preferred. Creating an empty pull r ## Project Styles -I personally do not like it when something requires so much learning and configuration before you can finally start the app. +I personally do not like something that requires so many configurations before you can finally start the app. I hope Uptime Kuma installation could be as easy as like installing a mobile app. -- Easy to install for non-Docker users, no native build dependency is needed (at least for x86_64), no extra config, no extra effort required to get it running +- Easy to install for non-Docker users, no native build dependency is needed (for x86_64/armv7/arm64), no extra config, no extra effort required to get it running - Single container for Docker users, no very complex docker-compose file. Just map the volume and expose the port, then good to go -- Settings should be configurable in the frontend. Environment variable is not encouraged, unless it is related to startup such as `DATA_DIR`. +- Settings should be configurable in the frontend. Environment variable is not encouraged, unless it is related to startup such as `DATA_DIR` - Easy to use -- The web UI styling should be consistent and nice. +- The web UI styling should be consistent and nice ## Coding Styles @@ -95,7 +98,7 @@ I personally do not like it when something requires so much learning and configu - Follow ESLint - Methods and functions should be documented with JSDoc -## Name convention +## Name Conventions - Javascript/Typescript: camelCaseType - SQLite: snake_case (Underscore) @@ -109,7 +112,7 @@ I personally do not like it when something requires so much learning and configu - IDE that supports ESLint and EditorConfig (I am using IntelliJ IDEA) - A SQLite GUI tool (SQLite Expert Personal is suggested) -## Install dependencies +## Install Dependencies for Development ```bash npm ci @@ -127,6 +130,12 @@ Port `3000` and port `3001` will be used. npm run dev ``` +But sometimes, you would like to keep restart the server, but not the frontend, you can run these command in two terminals: +``` +npm run start-frontend-dev +npm run start-server-dev +``` + ## Backend Server It binds to `0.0.0.0:3001` by default. @@ -142,12 +151,15 @@ express.js is used for: ### Structure in /server/ +- jobs/ (Jobs that are running in another process) - model/ (Object model, auto mapping to the database table name) - modules/ (Modified 3rd-party modules) +- monitor_types (Monitor Types) - notification-providers/ (individual notification logic) - routers/ (Express Routers) - socket-handler (Socket.io Handlers) -- server.js (Server entry point and main logic) +- server.js (Server entry point) +- uptime-kuma-server.js (UptimeKumaServer class, main logic should be here, but some still in `server.js`) ## Frontend Dev Server @@ -198,18 +210,12 @@ Both frontend and backend share the same package.json. However, the frontend dep ### Update Dependencies -Install `ncu` -https://github.com/raineorshine/npm-check-updates - -```bash -ncu -u -t patch -npm install -``` - Since previously updating Vite 2.5.10 to 2.6.0 broke the application completely, from now on, it should update patch release version only. Patch release = the third digit ([Semantic Versioning](https://semver.org/)) +If for maybe security reasons, a library must be updated. Then you must need to check if there are any breaking changes. + ## Translations Please read: https://github.com/louislam/uptime-kuma/tree/master/src/languages