From dfb95dfdcb37192be4243c2c66b0b66a464a1c17 Mon Sep 17 00:00:00 2001 From: Dave Baker Date: Thu, 16 Feb 2023 08:55:19 +0000 Subject: [PATCH 1/6] Hostnames may be specified with a trailing dot to prevent DNS search lookups --- src/util-frontend.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util-frontend.js b/src/util-frontend.js index 882ee291..3a59dac5 100644 --- a/src/util-frontend.js +++ b/src/util-frontend.js @@ -90,7 +90,7 @@ export function hostNameRegexPattern(mqtt = false) { // Source: https://digitalfortress.tech/tips/top-15-commonly-used-regex/ 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_])$`; + 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_])(\\.)?$`; return `${ipRegexPattern}|${hostNameRegexPattern}`; } From 186ca3050805f89aab6c88528fafffa3cd2653e1 Mon Sep 17 00:00:00 2001 From: Michael Telgkamp Date: Thu, 23 Feb 2023 17:40:39 +0100 Subject: [PATCH 2/6] Improve mattermost notifications --- server/notification-providers/mattermost.js | 162 +++++++++----------- 1 file changed, 72 insertions(+), 90 deletions(-) diff --git a/server/notification-providers/mattermost.js b/server/notification-providers/mattermost.js index 2076ad21..bade6c19 100644 --- a/server/notification-providers/mattermost.js +++ b/server/notification-providers/mattermost.js @@ -10,7 +10,7 @@ class Mattermost extends NotificationProvider { let okMsg = "Sent Successfully."; try { const mattermostUserName = notification.mattermostusername || "Uptime Kuma"; - // If heartbeatJSON is null, assume we're testing. + // If heartbeatJSON is null, assume non monitoring notification (Certificate warning) or testing. if (heartbeatJSON == null) { let mattermostTestData = { username: mattermostUserName, @@ -27,97 +27,79 @@ class Mattermost extends NotificationProvider { } const mattermostIconEmoji = notification.mattermosticonemo; - const mattermostIconUrl = notification.mattermosticonurl; + let mattermostIconEmojiOnline = ""; + let mattermostIconEmojiOffline = ""; - if (heartbeatJSON["status"] === DOWN) { - let mattermostdowndata = { - username: mattermostUserName, - text: "Uptime Kuma Alert", - channel: mattermostChannel, - icon_emoji: mattermostIconEmoji, - icon_url: mattermostIconUrl, - attachments: [ - { - fallback: - "Your " + - monitorJSON["name"] + - " service went down.", - color: "#FF0000", - title: - "❌ " + - monitorJSON["name"] + - " service went down. ❌", - title_link: monitorJSON["url"], - fields: [ - { - short: true, - title: "Service Name", - value: monitorJSON["name"], - }, - { - short: true, - title: "Time (UTC)", - value: heartbeatJSON["time"], - }, - { - short: false, - title: "Error", - value: heartbeatJSON["msg"], - }, - ], - }, - ], - }; - await axios.post( - notification.mattermostWebhookUrl, - mattermostdowndata - ); - return okMsg; - } else if (heartbeatJSON["status"] === UP) { - let mattermostupdata = { - username: mattermostUserName, - text: "Uptime Kuma Alert", - channel: mattermostChannel, - icon_emoji: mattermostIconEmoji, - icon_url: mattermostIconUrl, - attachments: [ - { - fallback: - "Your " + - monitorJSON["name"] + - " service went up!", - color: "#32CD32", - title: - "✅ " + - monitorJSON["name"] + - " service went up! ✅", - title_link: monitorJSON["url"], - fields: [ - { - short: true, - title: "Service Name", - value: monitorJSON["name"], - }, - { - short: true, - title: "Time (UTC)", - value: heartbeatJSON["time"], - }, - { - short: false, - title: "Ping", - value: heartbeatJSON["ping"] + "ms", - }, - ], - }, - ], - }; - await axios.post( - notification.mattermostWebhookUrl, - mattermostupdata - ); - return okMsg; + if (mattermostIconEmoji && typeof mattermostIconEmoji === "string") { + const emojiArray = mattermostIconEmoji.split(" "); + if (emojiArray.length >= 2) { + mattermostIconEmojiOnline = emojiArray[0]; + mattermostIconEmojiOffline = emojiArray[1]; + } } + const mattermostIconUrl = notification.mattermosticonurl; + let iconEmoji = mattermostIconEmoji; + let statusField = { + short: false, + title: "Error", + value: heartbeatJSON.msg, + }; + let statusText = "unknown"; + let color = "#000000"; + if (heartbeatJSON.status === DOWN) { + iconEmoji = mattermostIconEmojiOffline || mattermostIconEmoji; + statusField = { + short: false, + title: "Error", + value: heartbeatJSON.msg, + }; + statusText = "down."; + color = "#FF0000"; + } else if (heartbeatJSON.status === UP) { + iconEmoji = mattermostIconEmojiOnline || mattermostIconEmoji; + statusField = { + short: false, + title: "Ping", + value: heartbeatJSON.ping + "ms", + }; + statusText = "up!"; + color = "#32CD32"; + } + + let mattermostdata = { + username: monitorJSON.name + " " + mattermostUserName, + channel: mattermostChannel, + icon_emoji: iconEmoji, + icon_url: mattermostIconUrl, + attachments: [ + { + fallback: + "Your " + + monitorJSON.name + + " service went " + + statusText, + color: color, + title: + monitorJSON.name + + " service went " + + statusText, + title_link: monitorJSON.url, + fields: [ + statusField, + { + short: true, + title: "Time (UTC)", + value: heartbeatJSON.time, + }, + ], + }, + ], + }; + await axios.post( + notification.mattermostWebhookUrl, + mattermostdata + ); + return okMsg; } catch (error) { this.throwGeneralAxiosError(error); } From 7b4f90ce9274f08f6c1c1eb56b20a7ebc773d584 Mon Sep 17 00:00:00 2001 From: Michael Telgkamp Date: Wed, 1 Mar 2023 08:37:06 +0100 Subject: [PATCH 3/6] Improve ntfy notifications - use tags `red_circle` for down and `green_circle` for up - increase priority for down alert by 1 if not already max - add monitor name and status to title - use heartbeat msg as Message - add monitor url as action --- server/notification-providers/ntfy.js | 41 +++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/server/notification-providers/ntfy.js b/server/notification-providers/ntfy.js index 521137cd..2f2c7dc5 100644 --- a/server/notification-providers/ntfy.js +++ b/server/notification-providers/ntfy.js @@ -1,5 +1,6 @@ const NotificationProvider = require("./notification-provider"); const axios = require("axios"); +const { DOWN, UP } = require("../../src/util"); class Ntfy extends NotificationProvider { @@ -14,11 +15,45 @@ class Ntfy extends NotificationProvider { "Authorization": "Basic " + Buffer.from(notification.ntfyusername + ":" + notification.ntfypassword).toString("base64"), }; } + // If heartbeatJSON is null, assume non monitoring notification (Certificate warning) or testing. + if (heartbeatJSON == null) { + let ntfyTestData = { + "topic": notification.ntfytopic, + "title": (monitorJSON?.name || notification.ntfytopic) + " [Uptime-Kuma]", + "message": msg, + "priority": notification.ntfyPriority, + "tags": [ "test_tube" ] + }; + await axios.post(`${notification.ntfyserverurl}`, ntfyTestData, { headers: headers }); + return okMsg; + } + let tags = []; + let status = "unknown"; + let priority = notification.ntfyPriority || 4; + if ("status" in heartbeatJSON) { + if (heartbeatJSON.status === DOWN) { + tags = [ "red_circle" ]; + status = "Down"; + // if priority is not 5, increase priority for down alerts + priority = priority === 5 ? priority : priority + 1; + } else if (heartbeatJSON["status"] === UP) { + tags = [ "green_circle" ]; + status = "Up"; + } + } let data = { "topic": notification.ntfytopic, - "message": msg, - "priority": notification.ntfyPriority || 4, - "title": "Uptime-Kuma", + "message": heartbeatJSON.msg, + "priority": priority, + "title": monitorJSON.name + " " + status + " [Uptime-Kuma]", + "tags": tags, + "actions": [ + { + "action": "view", + "label": "Open " + monitorJSON.name, + "url": monitorJSON.url + } + ] }; if (notification.ntfyIcon) { From cbbd3e20ad10e01fd06a543909805d2405d48211 Mon Sep 17 00:00:00 2001 From: Michael Telgkamp Date: Wed, 1 Mar 2023 23:05:23 +0100 Subject: [PATCH 4/6] Codestyle: Add trailing comma --- server/notification-providers/ntfy.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/notification-providers/ntfy.js b/server/notification-providers/ntfy.js index 2f2c7dc5..b21e9800 100644 --- a/server/notification-providers/ntfy.js +++ b/server/notification-providers/ntfy.js @@ -22,7 +22,7 @@ class Ntfy extends NotificationProvider { "title": (monitorJSON?.name || notification.ntfytopic) + " [Uptime-Kuma]", "message": msg, "priority": notification.ntfyPriority, - "tags": [ "test_tube" ] + "tags": [ "test_tube" ], }; await axios.post(`${notification.ntfyserverurl}`, ntfyTestData, { headers: headers }); return okMsg; @@ -51,7 +51,7 @@ class Ntfy extends NotificationProvider { { "action": "view", "label": "Open " + monitorJSON.name, - "url": monitorJSON.url + "url": monitorJSON.url, } ] }; From 49741bbef27f23e68906fd4e24e92b8bcf8e8148 Mon Sep 17 00:00:00 2001 From: Zaid-maker Date: Fri, 7 Apr 2023 02:09:13 +0500 Subject: [PATCH 5/6] =?UTF-8?q?=F0=9F=90=9B=20fix(package.json):=20correct?= =?UTF-8?q?=20typo=20in=20deploy-demo-server=20script=20name?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5af6b18b..96485086 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,7 @@ "cy:run:unit": "npx cypress run --browser chrome --headless --config-file ./config/cypress.frontend.config.js", "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", - "depoly-demo-server": "node extra/deploy-demo-server.js", + "deploy-demo-server": "node extra/deploy-demo-server.js", "sort-contributors": "node extra/sort-contributors.js" }, "dependencies": { From 9e320dc5fb8b620cc2e2f0e52a00203365a4275a Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Sun, 9 Apr 2023 16:01:27 +0800 Subject: [PATCH 6/6] Expose timezone and local datetime to notification providers --- server/model/monitor.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/server/model/monitor.js b/server/model/monitor.js index b4a0ba2a..ffa4c454 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -2,7 +2,9 @@ const https = require("https"); 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 } = require("../../src/util"); +const { log, UP, DOWN, PENDING, MAINTENANCE, flipStatus, TimeLogger, MAX_INTERVAL_SECOND, MIN_INTERVAL_SECOND, + SQL_DATETIME_FORMAT +} = require("../../src/util"); const { tcping, ping, dnsResolve, checkCertificate, checkStatusCode, getTotalClientInRoom, setting, mssqlQuery, postgresQuery, mysqlQuery, mqttAsync, setSetting, httpNtlm, radius, grpcQuery, redisPingAsync, mongodbPing, } = require("../util-server"); @@ -1176,12 +1178,18 @@ class Monitor extends BeanModel { for (let notification of notificationList) { try { - // Prevent if the msg is undefined, notifications such as Discord cannot send out. const heartbeatJSON = bean.toJSON(); + + // Prevent if the msg is undefined, notifications such as Discord cannot send out. if (!heartbeatJSON["msg"]) { heartbeatJSON["msg"] = "N/A"; } + // Also provide the time in server timezone + heartbeatJSON["timezone"] = await UptimeKumaServer.getInstance().getTimezone(); + heartbeatJSON["timezoneOffset"] = UptimeKumaServer.getInstance().getTimezoneOffset(); + heartbeatJSON["localDateTime"] = dayjs.utc(heartbeatJSON["time"]).tz(heartbeatJSON["timezone"]).format(SQL_DATETIME_FORMAT); + await Notification.send(JSON.parse(notification.config), msg, await monitor.toJSON(false), heartbeatJSON); } catch (e) { log.error("monitor", "Cannot send notification to " + notification.name);