From 301b2007a0666258dd68355ff1b550a9a42df56c Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Tue, 19 Jul 2022 20:53:19 +0800 Subject: [PATCH 001/592] Drop Alpine support --- docker/alpine-base.dockerfile | 8 -------- docker/dockerfile-alpine | 25 ------------------------- package.json | 6 +----- 3 files changed, 1 insertion(+), 38 deletions(-) delete mode 100644 docker/alpine-base.dockerfile delete mode 100644 docker/dockerfile-alpine diff --git a/docker/alpine-base.dockerfile b/docker/alpine-base.dockerfile deleted file mode 100644 index cde65bb64..000000000 --- a/docker/alpine-base.dockerfile +++ /dev/null @@ -1,8 +0,0 @@ -# DON'T UPDATE TO alpine3.13, 1.14, see #41. -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 && \ - pip3 --no-cache-dir install apprise==0.9.9 && \ - rm -rf /root/.cache diff --git a/docker/dockerfile-alpine b/docker/dockerfile-alpine deleted file mode 100644 index ab9255f95..000000000 --- a/docker/dockerfile-alpine +++ /dev/null @@ -1,25 +0,0 @@ -FROM louislam/uptime-kuma:base-alpine AS build -WORKDIR /app - -ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=1 - -COPY . . -RUN npm ci --production && \ - chmod +x /app/extra/entrypoint.sh - - -FROM louislam/uptime-kuma:base-alpine AS release -WORKDIR /app - -# Copy app files from build layer -COPY --from=build /app /app - -EXPOSE 3001 -VOLUME ["/app/data"] -HEALTHCHECK --interval=60s --timeout=30s --start-period=180s --retries=5 CMD node extra/healthcheck.js -ENTRYPOINT ["/usr/bin/dumb-init", "--", "extra/entrypoint.sh"] -CMD ["node", "server/server.js"] - - -FROM release AS nightly -RUN npm run mark-as-nightly diff --git a/package.json b/package.json index 7a18dbdfd..676a05581 100644 --- a/package.json +++ b/package.json @@ -30,13 +30,10 @@ "jest-backend": "cross-env TEST_BACKEND=1 jest --config=./config/jest-backend.config.js", "tsc": "tsc", "vite-preview-dist": "vite preview --host --config ./config/vite.config.js", - "build-docker": "npm run build && npm run build-docker-debian && npm run build-docker-alpine", - "build-docker-alpine-base": "docker buildx build -f docker/alpine-base.dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:base-alpine . --push", + "build-docker": "npm run build && npm run build-docker-debian", "build-docker-debian-base": "docker buildx build -f docker/debian-base.dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:base-debian . --push", - "build-docker-alpine": "node ./extra/env2arg.js docker buildx build -f docker/dockerfile-alpine --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:alpine -t louislam/uptime-kuma:1-alpine -t louislam/uptime-kuma:$VERSION-alpine --target release . --push", "build-docker-debian": "node ./extra/env2arg.js docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma -t louislam/uptime-kuma:1 -t louislam/uptime-kuma:$VERSION -t louislam/uptime-kuma:debian -t louislam/uptime-kuma:1-debian -t louislam/uptime-kuma:$VERSION-debian --target release . --push", "build-docker-nightly": "npm run build && docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:nightly --target nightly . --push", - "build-docker-nightly-alpine": "docker buildx build -f docker/dockerfile-alpine --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:nightly-alpine --target nightly . --push", "build-docker-nightly-amd64": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:nightly-amd64 --target nightly . --push --progress plain", "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.17.1 && npm ci --production && npm run download-dist", @@ -46,7 +43,6 @@ "remove-2fa": "node extra/remove-2fa.js", "compile-install-script": "@powershell -NoProfile -ExecutionPolicy Unrestricted -Command ./extra/compile-install-script.ps1", "test-install-script-centos7": "npm run compile-install-script && docker build --progress plain -f test/test_install_script/centos7.dockerfile .", - "test-install-script-alpine3": "npm run compile-install-script && docker build --progress plain -f test/test_install_script/alpine3.dockerfile .", "test-install-script-ubuntu": "npm run compile-install-script && docker build --progress plain -f test/test_install_script/ubuntu.dockerfile .", "test-install-script-ubuntu1604": "npm run compile-install-script && docker build --progress plain -f test/test_install_script/ubuntu1604.dockerfile .", "test-nodejs16": "docker build --progress plain -f test/ubuntu-nodejs16.dockerfile .", From b0d39b44ce8eedc976fd13111c5b83f0ac73bb79 Mon Sep 17 00:00:00 2001 From: Louis Lam Date: Fri, 22 Jul 2022 23:15:55 +0800 Subject: [PATCH 002/592] Testing --- docker/debian-base.dockerfile | 2 +- docker/dockerfile | 9 +++++++-- package-lock.json | 2 +- package.json | 3 ++- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/docker/debian-base.dockerfile b/docker/debian-base.dockerfile index f90968a8b..19d996db5 100644 --- a/docker/debian-base.dockerfile +++ b/docker/debian-base.dockerfile @@ -7,7 +7,7 @@ WORKDIR /app # Install Curl # Install Apprise, add sqlite3 cli for debugging in the future, iputils-ping for ping, util-linux for setpriv -# 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! +# Stupid python3 and python3-pip actually install a lot of useless things into Debian, specify --no-install-recommends to skip them. 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 && \ diff --git a/docker/dockerfile b/docker/dockerfile index a9984351b..174775a50 100644 --- a/docker/dockerfile +++ b/docker/dockerfile @@ -20,11 +20,16 @@ HEALTHCHECK --interval=60s --timeout=30s --start-period=180s --retries=5 CMD nod ENTRYPOINT ["/usr/bin/dumb-init", "--", "extra/entrypoint.sh"] CMD ["node", "server/server.js"] +FROM release AS mariadb +# Install MariaDB +RUN apt update && \ + apt --yes --no-install-recommends install mariadb-server && \ + rm -rf /var/lib/apt/lists/* && \ + apt --yes autoremove -FROM release AS nightly +FROM mariadb AS nightly RUN npm run mark-as-nightly - # Upload the artifact to Github FROM louislam/uptime-kuma:base-debian AS upload-artifact WORKDIR / diff --git a/package-lock.json b/package-lock.json index e64f9fa71..d76f5a948 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,7 +16,7 @@ "badge-maker": "^3.3.1", "bcryptjs": "~2.4.3", "bree": "~7.1.5", - "cacheable-lookup": "^6.0.4", + "cacheable-lookup": "~6.0.4", "chardet": "^1.3.0", "check-password-strength": "^2.0.5", "cheerio": "^1.0.0-rc.10", diff --git a/package.json b/package.json index 676a05581..1fc45546d 100644 --- a/package.json +++ b/package.json @@ -30,9 +30,10 @@ "jest-backend": "cross-env TEST_BACKEND=1 jest --config=./config/jest-backend.config.js", "tsc": "tsc", "vite-preview-dist": "vite preview --host --config ./config/vite.config.js", - "build-docker": "npm run build && npm run build-docker-debian", + "build-docker": "npm run build && npm run build-docker-debian && npm run build-docker-debian-mariadb", "build-docker-debian-base": "docker buildx build -f docker/debian-base.dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:base-debian . --push", "build-docker-debian": "node ./extra/env2arg.js docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma -t louislam/uptime-kuma:1 -t louislam/uptime-kuma:$VERSION -t louislam/uptime-kuma:debian -t louislam/uptime-kuma:1-debian -t louislam/uptime-kuma:$VERSION-debian --target release . --push", + "build-docker-debian-mariadb": "node ./extra/env2arg.js docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:mariadb -t louislam/uptime-kuma:1-mariadb -t louislam/uptime-kuma:$VERSION-mariadb --target mariadb . --push", "build-docker-nightly": "npm run build && docker buildx build -f docker/dockerfile --platform linux/amd64,linux/arm64,linux/arm/v7 -t louislam/uptime-kuma:nightly --target nightly . --push", "build-docker-nightly-amd64": "docker buildx build -f docker/dockerfile --platform linux/amd64 -t louislam/uptime-kuma:nightly-amd64 --target nightly . --push --progress plain", "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 f89ed0a3a44b7d8dad2b98c9e7a0163bbd93d7ca Mon Sep 17 00:00:00 2001 From: Stefan Ottosson Date: Fri, 12 Aug 2022 18:43:24 +0200 Subject: [PATCH 003/592] feat: added HTTP method option for push monitor --- server/routers/api-router.js | 7 ++- src/pages/EditMonitor.vue | 84 ++++++++++++++++++------------------ 2 files changed, 48 insertions(+), 43 deletions(-) diff --git a/server/routers/api-router.js b/server/routers/api-router.js index 201efc41f..bba789ac5 100644 --- a/server/routers/api-router.js +++ b/server/routers/api-router.js @@ -31,9 +31,8 @@ router.get("/api/entry-page", async (request, response) => { response.json(result); }); -router.get("/api/push/:pushToken", async (request, response) => { +router.all("/api/push/:pushToken", async (request, response) => { try { - let pushToken = request.params.pushToken; let msg = request.query.msg || "OK"; let ping = request.query.ping || null; @@ -48,6 +47,10 @@ router.get("/api/push/:pushToken", async (request, response) => { throw new Error("Monitor not found or not active."); } + if (monitor.method !== request.method) { + throw new Error("Monitor HTTP method (" + monitor.method + ") does not match request (" + request.method + ")."); + } + const previousHeartbeat = await Monitor.getPreviousHeartbeat(monitor.id); if (monitor.isUpsideDown()) { diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue index 82859a4ff..d82aa2800 100644 --- a/src/pages/EditMonitor.vue +++ b/src/pages/EditMonitor.vue @@ -398,7 +398,7 @@ - - -
+ +
- -
+ +
- - - - - - -
- - -
- - - -
-
- - -
-
- - - - - - - - - - - - - - - - -
- - -
- -
- - -
- {{ $t("retriesDescription") }} -
-
- -
- - -
- - -
- - -
- -
- - -
- -

{{ $t("Advanced") }}

- -
- - -
-
-
- -
- - -
- -
- - -
- {{ $t("upsideDownModeDescription") }} -
-
- -
- - -
- {{ $t("gamedigGuessPortDescription") }} -
-
- - -
- - -
- - - - - -
- - -
- - -
- - -
- -
-
-
-
+
+ + +
- -

{{ $t("Notifications") }}

-

+ + + + + + +

+ + +
+ + + +
+
+ + +
+
+ + + + + + + + + + + + + + + + +
+ + +
+ +
+ + +
+ {{ $t("retriesDescription") }} +
+
+ +
+ + +
+ + +
+ + +
+ +
+ + +
+ +

{{ $t("Advanced") }}

+ +
+ + +
+
+
+ +
+ + +
+ +
+ + +
+ {{ $t("upsideDownModeDescription") }} +
+
+ +
+ + +
+ {{ $t("gamedigGuessPortDescription") }} +
+
+ + +
+ + +
+ + + + + +
+ + +
+ + +
+ + +
+ +
+ +
+
+ +
+
+ + +

{{ $t("Notifications") }}

+

+ {{ $t("Not available, please setup.") }} +

+ +
+ + + + + {{ $t("Default") }} +
+ + + + +
+

{{ $t("Proxy") }}

+

{{ $t("Not available, please setup.") }}

-
- +
+ + +
-
- -
-

{{ $t("Proxy") }}

-

- {{ $t("Not available, please setup.") }} -

- -
- - + + + - + + @@ -926,7 +902,7 @@ const monitorDefaults = { packetSize: 56, expiryNotification: false, maxredirects: 10, - accepted_statuscodes: [ "200-299" ], + accepted_statuscodes: ["200-299"], dns_resolve_type: "A", dns_resolve_server: "1.1.1.1", docker_container: "", @@ -947,7 +923,10 @@ const monitorDefaults = { kafkaProducerSsl: false, kafkaProducerAllowAutoTopicCreation: false, gamedigGivenPortOnly: true, - remote_browser: null + remote_browser: null, + port: 161, + communityString: 'public', + oid: '1.3.6.1.2.1.1.1.0', }; export default { @@ -996,7 +975,7 @@ export default { ipRegex() { // Allow to test with simple dns server with port (127.0.0.1:5300) - if (! isDev) { + if (!isDev) { return this.ipRegexPattern; } return null; @@ -1053,15 +1032,15 @@ export default { }, protoServicePlaceholder() { - return this.$t("Example:", [ "Health" ]); + return this.$t("Example:", ["Health"]); }, protoMethodPlaceholder() { - return this.$t("Example:", [ "check" ]); + return this.$t("Example:", ["check"]); }, protoBufDataPlaceholder() { - return this.$t("Example:", [ ` + return this.$t("Example:", [` syntax = "proto3"; package grpc.health.v1; @@ -1088,7 +1067,7 @@ message HealthCheckResponse { }, bodyPlaceholder() { if (this.monitor && this.monitor.httpBodyEncoding && this.monitor.httpBodyEncoding === "xml") { - return this.$t("Example:", [ ` + return this.$t("Example:", [` @@ -1097,16 +1076,16 @@ message HealthCheckResponse { ` ]); } if (this.monitor && this.monitor.httpBodyEncoding === "form") { - return this.$t("Example:", [ "key1=value1&key2=value2" ]); + return this.$t("Example:", ["key1=value1&key2=value2"]); } - return this.$t("Example:", [ ` + return this.$t("Example:", [` { "key": "value" }` ]); }, headersPlaceholder() { - return this.$t("Example:", [ ` + return this.$t("Example:", [` { "HeaderName": "HeaderValue" }` ]); @@ -1131,8 +1110,8 @@ message HealthCheckResponse { // Only groups, not itself, not a decendant result = result.filter( monitor => monitor.type === "group" && - monitor.id !== this.monitor.id && - !this.monitor.childrenIDs?.includes(monitor.id) + monitor.id !== this.monitor.id && + !this.monitor.childrenIDs?.includes(monitor.id) ); // Filter result by active state, weight and alphabetical @@ -1251,7 +1230,7 @@ message HealthCheckResponse { "monitor.type"() { if (this.monitor.type === "push") { - if (! this.monitor.pushToken) { + if (!this.monitor.pushToken) { // ideally this would require checking if the generated token is already used // it's very unlikely to get a collision though (62^32 ~ 2.27265788 * 10^57 unique tokens) this.monitor.pushToken = genSecret(pushTokenLength); @@ -1259,7 +1238,7 @@ message HealthCheckResponse { } // Set default port for DNS if not already defined - if (! this.monitor.port || this.monitor.port === "53" || this.monitor.port === "1812") { + if (!this.monitor.port || this.monitor.port === "53" || this.monitor.port === "1812") { if (this.monitor.type === "dns") { this.monitor.port = "53"; } else if (this.monitor.type === "radius") { @@ -1399,7 +1378,7 @@ message HealthCheckResponse { this.monitor.pathName = undefined; this.monitor.screenshot = undefined; - this.monitor.name = this.$t("cloneOf", [ this.monitor.name ]); + this.monitor.name = this.$t("cloneOf", [this.monitor.name]); this.$refs.tagsManager.newTags = this.monitor.tags.map((monitorTag) => { return { id: monitorTag.tag_id, @@ -1486,7 +1465,7 @@ message HealthCheckResponse { this.monitor.body = JSON.stringify(JSON.parse(this.monitor.body), null, 4); } - const monitorTypesWithEncodingAllowed = [ "http", "keyword", "json-query" ]; + const monitorTypesWithEncodingAllowed = ["http", "keyword", "json-query"]; if (this.monitor.type && !monitorTypesWithEncodingAllowed.includes(this.monitor.type)) { this.monitor.httpBodyEncoding = null; } @@ -1564,7 +1543,7 @@ message HealthCheckResponse { async startParentGroupMonitor() { await sleep(2000); - await this.$root.getSocket().emit("resumeMonitor", this.monitor.parent, () => {}); + await this.$root.getSocket().emit("resumeMonitor", this.monitor.parent, () => { }); }, /** @@ -1610,7 +1589,7 @@ message HealthCheckResponse { // Clamp timeout clampTimeout(timeout) { // limit to 80% of interval, narrowly avoiding epsilon bug - const maxTimeout = ~~(this.monitor.interval * 8 ) / 10; + const maxTimeout = ~~(this.monitor.interval * 8) / 10; const clamped = Math.max(0, Math.min(timeout, maxTimeout)); // 0 will be treated as 80% of interval @@ -1630,9 +1609,9 @@ message HealthCheckResponse { +textarea { + min-height: 200px; +} + \ No newline at end of file From 126d6cd912cbae2602d643916773e1fd064c4567 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=87=AF=E8=A7=82=E7=94=9F=E6=B4=BB?= Date: Sat, 27 Apr 2024 23:16:39 +0800 Subject: [PATCH 381/592] Add the ability to notify `@everyone` in DingTalk notifications (#4718) Co-authored-by: Frank Elsinga --- server/notification-providers/dingding.js | 3 +++ src/components/notifications/DingDing.vue | 25 +++++++++++++++++++++-- src/lang/en.json | 3 +++ 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/server/notification-providers/dingding.js b/server/notification-providers/dingding.js index 3ad36d547..5d8cb8948 100644 --- a/server/notification-providers/dingding.js +++ b/server/notification-providers/dingding.js @@ -19,6 +19,9 @@ class DingDing extends NotificationProvider { markdown: { title: `[${this.statusToString(heartbeatJSON["status"])}] ${monitorJSON["name"]}`, text: `## [${this.statusToString(heartbeatJSON["status"])}] ${monitorJSON["name"]} \n> ${heartbeatJSON["msg"]}\n> Time (${heartbeatJSON["timezone"]}): ${heartbeatJSON["localDateTime"]}`, + }, + "at": { + "isAtAll": notification.mentioning === "everyone" } }; if (await this.sendToDingDing(notification, params)) { diff --git a/src/components/notifications/DingDing.vue b/src/components/notifications/DingDing.vue index 79d8d837d..710677fd7 100644 --- a/src/components/notifications/DingDing.vue +++ b/src/components/notifications/DingDing.vue @@ -2,9 +2,10 @@
- +
+
- +

{{ $t("For safety, must use secret key") }}

@@ -13,4 +14,24 @@
+
+ + +
+ + diff --git a/src/lang/en.json b/src/lang/en.json index 726186f55..696b0ca67 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -667,6 +667,9 @@ "WebHookUrl": "WebHookUrl", "SecretKey": "SecretKey", "For safety, must use secret key": "For safety, must use secret key", + "Mentioning": "Mentioning", + "Don't mention people": "Don't mention people", + "Mention group": "Mention {group}", "Device Token": "Device Token", "Platform": "Platform", "Huawei": "Huawei", From 19e8c75c3b7fa8e1e9d6675372785dc37a8ef04e Mon Sep 17 00:00:00 2001 From: Joschua Becker Date: Sat, 27 Apr 2024 23:40:59 +0200 Subject: [PATCH 382/592] SevenIO Notification Provider (#4219) Co-authored-by: Frank Elsinga --- server/notification-providers/sevenio.js | 78 ++++++++++++++++++++++++ server/notification.js | 2 + src/components/NotificationDialog.vue | 1 + src/components/notifications/SevenIO.vue | 31 ++++++++++ src/components/notifications/index.js | 2 + src/lang/en.json | 5 ++ 6 files changed, 119 insertions(+) create mode 100644 server/notification-providers/sevenio.js create mode 100644 src/components/notifications/SevenIO.vue diff --git a/server/notification-providers/sevenio.js b/server/notification-providers/sevenio.js new file mode 100644 index 000000000..d363e95d0 --- /dev/null +++ b/server/notification-providers/sevenio.js @@ -0,0 +1,78 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); +const { DOWN, UP } = require("../../src/util"); + +class SevenIO extends NotificationProvider { + name = "SevenIO"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + const data = { + to: notification.sevenioTo, + from: notification.sevenioSender || "Uptime Kuma", + text: msg, + }; + + const config = { + baseURL: "https://gateway.seven.io/api/", + headers: { + "Content-Type": "application/json", + "X-API-Key": notification.sevenioApiKey, + }, + }; + + try { + // testing or certificate expiry notification + if (heartbeatJSON == null) { + await axios.post("sms", data, config); + return okMsg; + } + + let address = ""; + + switch (monitorJSON["type"]) { + case "ping": + address = monitorJSON["hostname"]; + break; + case "port": + case "dns": + case "gamedig": + case "steam": + address = monitorJSON["hostname"]; + if (monitorJSON["port"]) { + address += ":" + monitorJSON["port"]; + } + break; + default: + if (![ "https://", "http://", "" ].includes(monitorJSON["url"])) { + address = monitorJSON["url"]; + } + break; + } + + if (address !== "") { + address = `(${address}) `; + } + + // If heartbeatJSON is not null, we go into the normal alerting loop. + if (heartbeatJSON["status"] === DOWN) { + data.text = `Your service ${monitorJSON["name"]} ${address}went down at ${heartbeatJSON["localDateTime"]} ` + + `(${heartbeatJSON["timezone"]}). Error: ${heartbeatJSON["msg"]}`; + } else if (heartbeatJSON["status"] === UP) { + data.text = `Your service ${monitorJSON["name"]} ${address}went back up at ${heartbeatJSON["localDateTime"]} ` + + `(${heartbeatJSON["timezone"]}).`; + } + await axios.post("sms", data, config); + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } + +} + +module.exports = SevenIO; diff --git a/server/notification.js b/server/notification.js index f5a952061..b64bb1b56 100644 --- a/server/notification.js +++ b/server/notification.js @@ -56,6 +56,7 @@ const GoAlert = require("./notification-providers/goalert"); const SMSManager = require("./notification-providers/smsmanager"); const ServerChan = require("./notification-providers/serverchan"); const ZohoCliq = require("./notification-providers/zoho-cliq"); +const SevenIO = require("./notification-providers/sevenio"); const Whapi = require("./notification-providers/whapi"); const GtxMessaging = require("./notification-providers/gtx-messaging"); const Cellsynt = require("./notification-providers/cellsynt"); @@ -132,6 +133,7 @@ class Notification { new WeCom(), new GoAlert(), new ZohoCliq(), + new SevenIO(), new Whapi(), new GtxMessaging(), new Cellsynt(), diff --git a/src/components/NotificationDialog.vue b/src/components/NotificationDialog.vue index f57e0628a..347fa2308 100644 --- a/src/components/NotificationDialog.vue +++ b/src/components/NotificationDialog.vue @@ -154,6 +154,7 @@ export default { "webhook": "Webhook", "GoAlert": "GoAlert", "ZohoCliq": "ZohoCliq", + "SevenIO": "SevenIO", "whapi": "WhatsApp (Whapi)", "gtxmessaging": "GtxMessaging", "Cellsynt": "Cellsynt", diff --git a/src/components/notifications/SevenIO.vue b/src/components/notifications/SevenIO.vue new file mode 100644 index 000000000..fcf746ec8 --- /dev/null +++ b/src/components/notifications/SevenIO.vue @@ -0,0 +1,31 @@ + + + diff --git a/src/components/notifications/index.js b/src/components/notifications/index.js index 458322384..51107ac9f 100644 --- a/src/components/notifications/index.js +++ b/src/components/notifications/index.js @@ -55,6 +55,7 @@ import WeCom from "./WeCom.vue"; import GoAlert from "./GoAlert.vue"; import ZohoCliq from "./ZohoCliq.vue"; import Splunk from "./Splunk.vue"; +import SevenIO from "./SevenIO.vue"; import Whapi from "./Whapi.vue"; import Cellsynt from "./Cellsynt.vue"; @@ -119,6 +120,7 @@ const NotificationFormList = { "GoAlert": GoAlert, "ServerChan": ServerChan, "ZohoCliq": ZohoCliq, + "SevenIO": SevenIO, "whapi": Whapi, "gtxmessaging": GtxMessaging, "Cellsynt": Cellsynt, diff --git a/src/lang/en.json b/src/lang/en.json index 696b0ca67..45e800d9e 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -890,6 +890,11 @@ "deleteRemoteBrowserMessage": "Are you sure want to delete this Remote Browser for all monitors?", "GrafanaOncallUrl": "Grafana Oncall URL", "Browser Screenshot": "Browser Screenshot", + "wayToGetSevenIOApiKey": "Visit the dashboard under app.seven.io > developer > api key > the green add button", + "senderSevenIO": "Sending number or name", + "receiverSevenIO": "Receiving number", + "receiverInfoSevenIO": "If the receiving number is not located in Germany, you have to add the country code in front of the number (e.g. for the country code 1 from the US use 117612121212 instead of 017612121212)", + "apiKeySevenIO": "SevenIO API Key", "wayToWriteWhapiRecipient": "The phone number with the international prefix, but without the plus sign at the start ({0}), the Contact ID ({1}) or the Group ID ({2}).", "wayToGetWhapiUrlAndToken": "You can get the API URL and the token by going into your desired channel from {0}", "whapiRecipient": "Phone Number / Contact ID / Group ID", From a3cdd69995a138a27fac31866e3993b3a44d3c67 Mon Sep 17 00:00:00 2001 From: Matt Visnovsky Date: Mon, 29 Apr 2024 13:56:32 -0600 Subject: [PATCH 383/592] Use net-snmp instead of snmp-native net-snmp over snmp-native is: -more robust -more popular -better documented -supports v3 --- package-lock.json | 16 ++++++++++------ package.json | 2 +- server/monitor-types/snmp.js | 2 +- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7728a0cfe..0e09c595c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -54,6 +54,7 @@ "mssql": "~8.1.4", "mysql2": "~3.6.2", "nanoid": "~3.3.4", + "net-snmp": "^3.11.2", "node-cloudflared-tunnel": "~1.0.9", "node-radius-client": "~1.0.0", "nodemailer": "~6.9.13", @@ -72,7 +73,6 @@ "redbean-node": "~0.3.0", "redis": "~4.5.1", "semver": "~7.5.4", - "snmp-native": "^1.2.0", "socket.io": "~4.6.1", "socket.io-client": "~4.6.1", "socks-proxy-agent": "6.1.1", @@ -10337,6 +10337,15 @@ "node": ">= 0.6" } }, + "node_modules/net-snmp": { + "version": "3.11.2", + "resolved": "https://registry.npmjs.org/net-snmp/-/net-snmp-3.11.2.tgz", + "integrity": "sha512-QKy2JQHIBsSK344dUxYRZv7tU0ANk8f8fzKD/Mmq/cCxm/cPbtiT7009QEgxdViW/gGjqGIOiLHxkCc+JhZltg==", + "dependencies": { + "asn1-ber": "^1.2.1", + "smart-buffer": "^4.1.0" + } + }, "node_modules/node-abort-controller": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", @@ -12610,11 +12619,6 @@ "npm": ">= 3.0.0" } }, - "node_modules/snmp-native": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/snmp-native/-/snmp-native-1.2.0.tgz", - "integrity": "sha512-JIyuLX3bQmuAI4gHztHSQd3M/M2hqgLhiHBZYEk8YnYRJ2ooxqwON4gUQfgp/WCZVDca4tIX3vFJgv6lz5iY+g==" - }, "node_modules/socket.io": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.6.2.tgz", diff --git a/package.json b/package.json index 7ee8afb41..49cc56ffc 100644 --- a/package.json +++ b/package.json @@ -119,6 +119,7 @@ "mssql": "~8.1.4", "mysql2": "~3.6.2", "nanoid": "~3.3.4", + "net-snmp": "^3.11.2", "node-cloudflared-tunnel": "~1.0.9", "node-radius-client": "~1.0.0", "nodemailer": "~6.9.13", @@ -137,7 +138,6 @@ "redbean-node": "~0.3.0", "redis": "~4.5.1", "semver": "~7.5.4", - "snmp-native": "^1.2.0", "socket.io": "~4.6.1", "socket.io-client": "~4.6.1", "socks-proxy-agent": "6.1.1", diff --git a/server/monitor-types/snmp.js b/server/monitor-types/snmp.js index 4ddc88230..433fc527f 100644 --- a/server/monitor-types/snmp.js +++ b/server/monitor-types/snmp.js @@ -1,6 +1,6 @@ const { MonitorType } = require("./monitor-type"); const { UP, DOWN } = require("../../src/util"); -const snmp = require("snmp-native"); +const snmp = require("net-snmp"); class SNMPMonitorType extends MonitorType { name = "snmp"; From ff5890a11f0ef3e69959d04e5a43c7b808663787 Mon Sep 17 00:00:00 2001 From: Matt Visnovsky Date: Mon, 29 Apr 2024 15:59:21 -0600 Subject: [PATCH 384/592] Updated a comment --- db/knex_migrations/2024-04-26-0000-snmp-monitor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/knex_migrations/2024-04-26-0000-snmp-monitor.js b/db/knex_migrations/2024-04-26-0000-snmp-monitor.js index 8b93ecd49..489a44241 100644 --- a/db/knex_migrations/2024-04-26-0000-snmp-monitor.js +++ b/db/knex_migrations/2024-04-26-0000-snmp-monitor.js @@ -1,7 +1,7 @@ exports.up = function (knex) { return knex.schema .alterTable("monitor", function (table) { - table.string("snmp_community_string", 255).defaultTo("public"); // Add community_string column + table.string("snmp_community_string", 255).defaultTo("public"); // Add snmp_community_string column table.string("snmp_oid").notNullable(); // Add oid column table.enum("snmp_version", ["1", "2c", "3"]).defaultTo("2c"); // Add snmp_version column with enum values table.float("snmp_control_value").notNullable(); // Add control_value column as float From 4a882be6bac6884a84acf930645ae2f93c18ec7f Mon Sep 17 00:00:00 2001 From: Matt Visnovsky Date: Mon, 29 Apr 2024 15:59:59 -0600 Subject: [PATCH 385/592] Further SNMP monitor development Further testing of SNMP feat, however I'm running into the issue `Error in SNMP check: RequestTimedOutError: Request timed out` when the check function is called. I am unsure as to why since my local SNMP script works great with very similar code. --- server/monitor-types/snmp.js | 65 +++++++++++++++++++++++++----------- src/pages/EditMonitor.vue | 10 ++++-- 2 files changed, 52 insertions(+), 23 deletions(-) diff --git a/server/monitor-types/snmp.js b/server/monitor-types/snmp.js index 433fc527f..92c0f4875 100644 --- a/server/monitor-types/snmp.js +++ b/server/monitor-types/snmp.js @@ -12,51 +12,76 @@ class SNMPMonitorType extends MonitorType { * @param {object} _server Unused server object. */ async check(monitor, heartbeat, _server) { + + console.log("IP Address:", monitor._hostname); + console.log("SNMP Community String:", monitor._snmpCommunityString); + console.log("SNMP OID:", monitor._snmpOid); + console.log("SNMP Version:", monitor._snmpVersion); + console.log("SNMP Condition:", monitor._snmpCondition); + console.log("SNMP Control Value:", monitor._snmpControlValue); + + const options = { + port: monitor._port || 161, + retries: 1, + timeout: 1000, + version: getKey(snmp.Version, monitor._snmpVersion) || snmp.Version2c, + }; + + function getKey(obj, value) { + return Object.keys(obj).find(key => obj[key] === value) || null; + } + try { - const session = new snmp.Session({ host: monitor.ipAddress, community: monitor.snmpCommunityString, version: monitor.snmpVersion }); + const session = snmp.createSession(monitor._ipAddress, monitor._snmpCommunityString, options); - session.get({ oid: monitor.snmpOid }, (err, varbinds) => { - if (err) { - heartbeat.status = DOWN; - heartbeat.msg = `Error: ${err.message}`; - return; - } + const varbinds = await new Promise((resolve, reject) => { + session.get([monitor._snmpOid], (error, varbinds) => { + if (error) { + reject(error); + } else { + resolve(varbinds); + } + }); + }); - // Assuming only one varbind is returned + console.log("Received varbinds:", varbinds); // Log the received varbinds for debugging + + if (varbinds && varbinds.length > 0) { const value = varbinds[0].value; - - // Convert value to appropriate type based on SNMP type (assuming it's integer or string for simplicity) const numericValue = parseInt(value); const stringValue = value.toString(); - // Check against condition and control value switch (monitor.snmpCondition) { case '>': - heartbeat.status = numericValue > monitor.snmpControlValue ? UP : DOWN; + heartbeat.status = numericValue > monitor._snmpControlValue ? UP : DOWN; break; case '>=': - heartbeat.status = numericValue >= monitor.snmpControlValue ? UP : DOWN; + heartbeat.status = numericValue >= monitor._snmpControlValue ? UP : DOWN; break; case '<': - heartbeat.status = numericValue < monitor.snmpControlValue ? UP : DOWN; + heartbeat.status = numericValue < monitor._snmpControlValue ? UP : DOWN; break; case '<=': - heartbeat.status = numericValue <= monitor.snmpControlValue ? UP : DOWN; + heartbeat.status = numericValue <= monitor._snmpControlValue ? UP : DOWN; break; case '==': - heartbeat.status = value === monitor.snmpControlValue ? UP : DOWN; + heartbeat.status = value === monitor._snmpControlValue ? UP : DOWN; break; case 'contains': - heartbeat.status = stringValue.includes(monitor.snmpControlValue) ? UP : DOWN; + heartbeat.status = stringValue.includes(monitor._snmpControlValue) ? UP : DOWN; break; default: heartbeat.status = DOWN; - heartbeat.msg = `Invalid condition: ${monitor.snmpCondition}`; + heartbeat.msg = `Invalid condition: ${monitor._snmpCondition}`; } + } else { + heartbeat.status = DOWN; + heartbeat.msg = 'No varbinds returned from SNMP session'; + } - session.close(); - }); + session.close(); // Close the session after use } catch (err) { + console.error("Error in SNMP check:", err); // Log any errors heartbeat.status = DOWN; heartbeat.msg = `Error: ${err.message}`; } diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue index d082d3e2f..c21c4a14b 100644 --- a/src/pages/EditMonitor.vue +++ b/src/pages/EditMonitor.vue @@ -924,9 +924,6 @@ const monitorDefaults = { kafkaProducerAllowAutoTopicCreation: false, gamedigGivenPortOnly: true, remote_browser: null, - port: 161, - communityString: 'public', - oid: '1.3.6.1.2.1.1.1.0', }; export default { @@ -1243,11 +1240,18 @@ message HealthCheckResponse { this.monitor.port = "53"; } else if (this.monitor.type === "radius") { this.monitor.port = "1812"; + } else if (this.monitor.type === "snmp") { + this.monitor.port = "161"; } else { this.monitor.port = undefined; } } + // Set default SNMP version + if (!this.monitor.snmpVersion) { + this.monitor.snmpVersion = "2c"; + } + // Get the game list from server if (this.monitor.type === "gamedig") { this.$root.getSocket().emit("getGameList", (res) => { From 99dc4cfb46c2dcf805829b7b34cb33be0376ded5 Mon Sep 17 00:00:00 2001 From: Matt Visnovsky Date: Mon, 29 Apr 2024 22:24:51 -0600 Subject: [PATCH 386/592] Wrong variable used Thanks for pointing it out @CommanderStorm! --- server/monitor-types/snmp.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/monitor-types/snmp.js b/server/monitor-types/snmp.js index 92c0f4875..1752ab717 100644 --- a/server/monitor-types/snmp.js +++ b/server/monitor-types/snmp.js @@ -32,7 +32,7 @@ class SNMPMonitorType extends MonitorType { } try { - const session = snmp.createSession(monitor._ipAddress, monitor._snmpCommunityString, options); + const session = snmp.createSession(monitor.hostname, monitor.snmpCommunityString, options); const varbinds = await new Promise((resolve, reject) => { session.get([monitor._snmpOid], (error, varbinds) => { From 988ba79679f7cfacb42453470982f4c465797eeb Mon Sep 17 00:00:00 2001 From: Ezhil Shanmugham Date: Tue, 30 Apr 2024 19:47:34 +0530 Subject: [PATCH 387/592] feat: keephq notification provider (#4722) --- server/notification-providers/keep.js | 42 +++++++++++++++++++++++++++ server/notification.js | 2 ++ src/components/NotificationDialog.vue | 1 + src/components/notifications/Keep.vue | 42 +++++++++++++++++++++++++++ src/components/notifications/index.js | 2 ++ src/lang/en.json | 1 + 6 files changed, 90 insertions(+) create mode 100644 server/notification-providers/keep.js create mode 100644 src/components/notifications/Keep.vue diff --git a/server/notification-providers/keep.js b/server/notification-providers/keep.js new file mode 100644 index 000000000..aa65a867b --- /dev/null +++ b/server/notification-providers/keep.js @@ -0,0 +1,42 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +class Keep extends NotificationProvider { + name = "Keep"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + const okMsg = "Sent Successfully."; + + try { + let data = { + heartbeat: heartbeatJSON, + monitor: monitorJSON, + msg, + }; + let config = { + headers: { + "x-api-key": notification.webhookAPIKey, + "content-type": "application/json", + }, + }; + + let url = notification.webhookURL; + + if (url.endsWith("/")) { + url = url.slice(0, -1); + } + + let webhookURL = url + "/alerts/event/uptimekuma"; + + await axios.post(webhookURL, data, config); + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + } +} + +module.exports = Keep; diff --git a/server/notification.js b/server/notification.js index b64bb1b56..03bd368ed 100644 --- a/server/notification.js +++ b/server/notification.js @@ -18,6 +18,7 @@ const Gotify = require("./notification-providers/gotify"); const GrafanaOncall = require("./notification-providers/grafana-oncall"); const HomeAssistant = require("./notification-providers/home-assistant"); const HeiiOnCall = require("./notification-providers/heii-oncall"); +const Keep = require("./notification-providers/keep"); const Kook = require("./notification-providers/kook"); const Line = require("./notification-providers/line"); const LineNotify = require("./notification-providers/linenotify"); @@ -95,6 +96,7 @@ class Notification { new GrafanaOncall(), new HomeAssistant(), new HeiiOnCall(), + new Keep(), new Kook(), new Line(), new LineNotify(), diff --git a/src/components/NotificationDialog.vue b/src/components/NotificationDialog.vue index 347fa2308..09646d599 100644 --- a/src/components/NotificationDialog.vue +++ b/src/components/NotificationDialog.vue @@ -123,6 +123,7 @@ export default { "GrafanaOncall": "Grafana Oncall", "HeiiOnCall": "Heii On-Call", "HomeAssistant": "Home Assistant", + "Keep": "Keep", "Kook": "Kook", "line": "LINE Messenger", "LineNotify": "LINE Notify", diff --git a/src/components/notifications/Keep.vue b/src/components/notifications/Keep.vue new file mode 100644 index 000000000..f0c856ee8 --- /dev/null +++ b/src/components/notifications/Keep.vue @@ -0,0 +1,42 @@ + + + diff --git a/src/components/notifications/index.js b/src/components/notifications/index.js index 51107ac9f..52bebf83d 100644 --- a/src/components/notifications/index.js +++ b/src/components/notifications/index.js @@ -17,6 +17,7 @@ import GrafanaOncall from "./GrafanaOncall.vue"; import GtxMessaging from "./GtxMessaging.vue"; import HomeAssistant from "./HomeAssistant.vue"; import HeiiOnCall from "./HeiiOnCall.vue"; +import Keep from "./Keep.vue"; import Kook from "./Kook.vue"; import Line from "./Line.vue"; import LineNotify from "./LineNotify.vue"; @@ -82,6 +83,7 @@ const NotificationFormList = { "GrafanaOncall": GrafanaOncall, "HomeAssistant": HomeAssistant, "HeiiOnCall": HeiiOnCall, + "Keep": Keep, "Kook": Kook, "line": Line, "LineNotify": LineNotify, diff --git a/src/lang/en.json b/src/lang/en.json index 45e800d9e..d4a0997c2 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -63,6 +63,7 @@ "Friendly Name": "Friendly Name", "URL": "URL", "Hostname": "Hostname", + "Host URL": "Host URL", "locally configured mail transfer agent": "locally configured mail transfer agent", "Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "Either enter the hostname of the server you want to connect to or {localhost} if you intend to use a {local_mta}", "Port": "Port", From 59f10d542b277302c87723b1ef415b73342c48e6 Mon Sep 17 00:00:00 2001 From: Nelson Chan <3271800+chakflying@users.noreply.github.com> Date: Wed, 1 May 2024 04:11:09 +0800 Subject: [PATCH 388/592] Fix: Show API Keys disabled msg. when disabled Auth (#4723) Co-authored-by: Frank Elsinga --- src/components/settings/APIKeys.vue | 277 +++++++++++++++------------- src/lang/en.json | 3 +- 2 files changed, 148 insertions(+), 132 deletions(-) diff --git a/src/components/settings/APIKeys.vue b/src/components/settings/APIKeys.vue index 757789937..744357f6d 100644 --- a/src/components/settings/APIKeys.vue +++ b/src/components/settings/APIKeys.vue @@ -1,53 +1,63 @@ @@ -439,13 +480,13 @@
- +
@@ -460,21 +501,21 @@
- +
@@ -482,7 +523,7 @@

{{ $t("Advanced") }}

-
+
-
+
-