From fd5c37c1e850fd88a343dd6c829071d352c0c154 Mon Sep 17 00:00:00 2001 From: Sebastian Lang Date: Thu, 1 Feb 2024 17:06:10 +0100 Subject: [PATCH 1/5] Add support for custom mongodb commands --- server/model/monitor.js | 36 ++++++++++++++++++++++++++++++++---- server/util-server.js | 12 ++++-------- src/pages/EditMonitor.vue | 21 +++++++++++++++++++++ 3 files changed, 57 insertions(+), 12 deletions(-) diff --git a/server/model/monitor.js b/server/model/monitor.js index b2fed86f57..d8b69188dc 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -5,7 +5,7 @@ const { log, UP, DOWN, PENDING, MAINTENANCE, flipStatus, MAX_INTERVAL_SECOND, MI SQL_DATETIME_FORMAT } = require("../../src/util"); const { tcping, ping, checkCertificate, checkStatusCode, getTotalClientInRoom, setting, mssqlQuery, postgresQuery, mysqlQuery, setSetting, httpNtlm, radius, grpcQuery, - redisPingAsync, mongodbPing, kafkaProducerAsync, getOidcTokenClientCredentials, rootCertificatesFingerprints, axiosAbortSignal + redisPingAsync, mongodbCommand, kafkaProducerAsync, getOidcTokenClientCredentials, rootCertificatesFingerprints, axiosAbortSignal } = require("../util-server"); const { R } = require("redbean-node"); const { BeanModel } = require("redbean-node/dist/bean-model"); @@ -817,10 +817,38 @@ class Monitor extends BeanModel { } else if (this.type === "mongodb") { let startTime = dayjs().valueOf(); - await mongodbPing(this.databaseConnectionString); + let command = { "ping": 1 }; + + if (this.databaseQuery) { + command = JSON.parse(this.databaseQuery); + } + + let result = await mongodbCommand(this.databaseConnectionString, command); + + if (result["ok"] !== 1) { + throw new Error("MongoDB command failed"); + } + + if (this.jsonPath) { + let expression = jsonata(this.jsonPath); + result = await expression.evaluate(result); + if (!result) { + throw new Error("Queried value not found."); + } + } + + if (this.expectedValue) { + if (result.toString() === this.expectedValue) { + bean.msg = "Expected value found"; + bean.status = UP; + } else { + throw new Error("Query executed, but value is not equal to expected value, value was: [" + JSON.stringify(result) + "]"); + } + } else { + bean.msg = ""; + bean.status = UP; + } - bean.msg = ""; - bean.status = UP; bean.ping = dayjs().valueOf() - startTime; } else if (this.type === "radius") { diff --git a/server/util-server.js b/server/util-server.js index 8add5bc564..b38c5b5852 100644 --- a/server/util-server.js +++ b/server/util-server.js @@ -440,19 +440,15 @@ exports.mysqlQuery = function (connectionString, query, password = undefined) { /** * Connect to and ping a MongoDB database * @param {string} connectionString The database connection string + * @param {object} command MongoDB command to run on the database * @returns {Promise<(string[] | object[] | object)>} Response from * server */ -exports.mongodbPing = async function (connectionString) { +exports.mongodbCommand = async function (connectionString, command) { let client = await MongoClient.connect(connectionString); - let dbPing = await client.db().command({ ping: 1 }); + let result = await client.db().command(command); await client.close(); - - if (dbPing["ok"] === 1) { - return "UP"; - } else { - throw Error("failed"); - } + return result; }; /** diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue index 6661629915..7d9e2746da 100644 --- a/src/pages/EditMonitor.vue +++ b/src/pages/EditMonitor.vue @@ -434,6 +434,27 @@ + + +
From 6ee18efcd073bc1a7281740a7eeb25ac67aeb338 Mon Sep 17 00:00:00 2001 From: Sebastian Lang Date: Fri, 2 Feb 2024 10:30:19 +0100 Subject: [PATCH 2/5] Migrate to new monitor-type --- server/model/monitor.js | 39 +------------------- server/monitor-types/mongodb.js | 64 +++++++++++++++++++++++++++++++++ server/uptime-kuma-server.js | 2 ++ server/util-server.js | 15 -------- 4 files changed, 67 insertions(+), 53 deletions(-) create mode 100644 server/monitor-types/mongodb.js diff --git a/server/model/monitor.js b/server/model/monitor.js index d8b69188dc..cdd68ab2dc 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -5,7 +5,7 @@ const { log, UP, DOWN, PENDING, MAINTENANCE, flipStatus, MAX_INTERVAL_SECOND, MI SQL_DATETIME_FORMAT } = require("../../src/util"); const { tcping, ping, checkCertificate, checkStatusCode, getTotalClientInRoom, setting, mssqlQuery, postgresQuery, mysqlQuery, setSetting, httpNtlm, radius, grpcQuery, - redisPingAsync, mongodbCommand, kafkaProducerAsync, getOidcTokenClientCredentials, rootCertificatesFingerprints, axiosAbortSignal + redisPingAsync, kafkaProducerAsync, getOidcTokenClientCredentials, rootCertificatesFingerprints, axiosAbortSignal } = require("../util-server"); const { R } = require("redbean-node"); const { BeanModel } = require("redbean-node/dist/bean-model"); @@ -814,43 +814,6 @@ class Monitor extends BeanModel { bean.msg = await mysqlQuery(this.databaseConnectionString, this.databaseQuery || "SELECT 1", mysqlPassword); bean.status = UP; bean.ping = dayjs().valueOf() - startTime; - } else if (this.type === "mongodb") { - let startTime = dayjs().valueOf(); - - let command = { "ping": 1 }; - - if (this.databaseQuery) { - command = JSON.parse(this.databaseQuery); - } - - let result = await mongodbCommand(this.databaseConnectionString, command); - - if (result["ok"] !== 1) { - throw new Error("MongoDB command failed"); - } - - if (this.jsonPath) { - let expression = jsonata(this.jsonPath); - result = await expression.evaluate(result); - if (!result) { - throw new Error("Queried value not found."); - } - } - - if (this.expectedValue) { - if (result.toString() === this.expectedValue) { - bean.msg = "Expected value found"; - bean.status = UP; - } else { - throw new Error("Query executed, but value is not equal to expected value, value was: [" + JSON.stringify(result) + "]"); - } - } else { - bean.msg = ""; - bean.status = UP; - } - - bean.ping = dayjs().valueOf() - startTime; - } else if (this.type === "radius") { let startTime = dayjs().valueOf(); diff --git a/server/monitor-types/mongodb.js b/server/monitor-types/mongodb.js new file mode 100644 index 0000000000..9eb02a61e5 --- /dev/null +++ b/server/monitor-types/mongodb.js @@ -0,0 +1,64 @@ +const { MonitorType } = require("./monitor-type"); +const { UP } = require("../../src/util"); +const { MongoClient } = require("mongodb"); +const jsonata = require("jsonata"); + +class MongodbMonitorType extends MonitorType { + + name = "mongodb"; + + /** + * @inheritdoc + */ + async check(monitor, heartbeat, _server) { + let command = { "ping": 1 }; + + if (monitor.databaseQuery) { + command = JSON.parse(monitor.databaseQuery); + } + + let result = await this.runMongodbCommand(monitor.databaseConnectionString, command); + + if (result["ok"] !== 1) { + throw new Error("MongoDB command failed"); + } + + if (monitor.jsonPath) { + let expression = jsonata(monitor.jsonPath); + result = await expression.evaluate(result); + if (!result) { + throw new Error("Queried value not found."); + } + } + + if (monitor.expectedValue) { + if (result.toString() === monitor.expectedValue) { + heartbeat.msg = "Expected value found"; + heartbeat.status = UP; + } else { + throw new Error("Query executed, but value is not equal to expected value, value was: [" + JSON.stringify(result) + "]"); + } + } else { + heartbeat.msg = ""; + heartbeat.status = UP; + } + } + + /** + * Connect to and ping a MongoDB database + * @param {string} connectionString The database connection string + * @param {object} command MongoDB command to run on the database + * @returns {Promise<(string[] | object[] | object)>} Response from + * server + */ + async runMongodbCommand(connectionString, command) { + let client = await MongoClient.connect(connectionString); + let result = await client.db().command(command); + await client.close(); + return result; + } +} + +module.exports = { + MongodbMonitorType, +}; diff --git a/server/uptime-kuma-server.js b/server/uptime-kuma-server.js index 2eec9c7c13..4d3b2b4fcb 100644 --- a/server/uptime-kuma-server.js +++ b/server/uptime-kuma-server.js @@ -113,6 +113,7 @@ class UptimeKumaServer { UptimeKumaServer.monitorTypeList["tailscale-ping"] = new TailscalePing(); UptimeKumaServer.monitorTypeList["dns"] = new DnsMonitorType(); UptimeKumaServer.monitorTypeList["mqtt"] = new MqttMonitorType(); + UptimeKumaServer.monitorTypeList["mongodb"] = new MongodbMonitorType(); // Allow all CORS origins (polling) in development let cors = undefined; @@ -516,3 +517,4 @@ const { RealBrowserMonitorType } = require("./monitor-types/real-browser-monitor const { TailscalePing } = require("./monitor-types/tailscale-ping"); const { DnsMonitorType } = require("./monitor-types/dns"); const { MqttMonitorType } = require("./monitor-types/mqtt"); +const { MongodbMonitorType } = require("./monitor-types/mongodb"); diff --git a/server/util-server.js b/server/util-server.js index b38c5b5852..a643b10001 100644 --- a/server/util-server.js +++ b/server/util-server.js @@ -11,7 +11,6 @@ const mssql = require("mssql"); const { Client } = require("pg"); const postgresConParse = require("pg-connection-string").parse; const mysql = require("mysql2"); -const { MongoClient } = require("mongodb"); const { NtlmClient } = require("axios-ntlm"); const { Settings } = require("./settings"); const grpc = require("@grpc/grpc-js"); @@ -437,20 +436,6 @@ exports.mysqlQuery = function (connectionString, query, password = undefined) { }); }; -/** - * Connect to and ping a MongoDB database - * @param {string} connectionString The database connection string - * @param {object} command MongoDB command to run on the database - * @returns {Promise<(string[] | object[] | object)>} Response from - * server - */ -exports.mongodbCommand = async function (connectionString, command) { - let client = await MongoClient.connect(connectionString); - let result = await client.db().command(command); - await client.close(); - return result; -}; - /** * Query radius server * @param {string} hostname Hostname of radius server From 936349cafbd7bbdbe0000bc53462c4503642b788 Mon Sep 17 00:00:00 2001 From: Sebastian Lang Date: Fri, 2 Feb 2024 11:30:41 +0100 Subject: [PATCH 3/5] Fix runMongodbCommand docs --- server/monitor-types/mongodb.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/monitor-types/mongodb.js b/server/monitor-types/mongodb.js index 9eb02a61e5..e9e19de4d2 100644 --- a/server/monitor-types/mongodb.js +++ b/server/monitor-types/mongodb.js @@ -45,7 +45,7 @@ class MongodbMonitorType extends MonitorType { } /** - * Connect to and ping a MongoDB database + * Connect to and run MongoDB command on a MongoDB database * @param {string} connectionString The database connection string * @param {object} command MongoDB command to run on the database * @returns {Promise<(string[] | object[] | object)>} Response from From ad583cce723bc6bfde07ab868316d7031f39362d Mon Sep 17 00:00:00 2001 From: Sebastian Lang Date: Mon, 5 Feb 2024 12:07:18 +0100 Subject: [PATCH 4/5] Add query helptext, translation, improve success messages --- server/monitor-types/mongodb.js | 15 ++++++++------- src/lang/en.json | 4 +++- src/pages/EditMonitor.vue | 7 +++++-- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/server/monitor-types/mongodb.js b/server/monitor-types/mongodb.js index e9e19de4d2..fd3a1dc983 100644 --- a/server/monitor-types/mongodb.js +++ b/server/monitor-types/mongodb.js @@ -12,7 +12,6 @@ class MongodbMonitorType extends MonitorType { */ async check(monitor, heartbeat, _server) { let command = { "ping": 1 }; - if (monitor.databaseQuery) { command = JSON.parse(monitor.databaseQuery); } @@ -21,27 +20,29 @@ class MongodbMonitorType extends MonitorType { if (result["ok"] !== 1) { throw new Error("MongoDB command failed"); + } else { + heartbeat.msg = "Command executed successfully"; } if (monitor.jsonPath) { let expression = jsonata(monitor.jsonPath); result = await expression.evaluate(result); - if (!result) { + if (result) { + heartbeat.msg = "Command executed successfully and the jsonata expression produces a result."; + } else { throw new Error("Queried value not found."); } } if (monitor.expectedValue) { if (result.toString() === monitor.expectedValue) { - heartbeat.msg = "Expected value found"; - heartbeat.status = UP; + heartbeat.msg = "Command executed successfully and expected value was found"; } else { throw new Error("Query executed, but value is not equal to expected value, value was: [" + JSON.stringify(result) + "]"); } - } else { - heartbeat.msg = ""; - heartbeat.status = UP; } + + heartbeat.status = UP; } /** diff --git a/src/lang/en.json b/src/lang/en.json index 0f59e62aef..ed8133bf65 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -883,5 +883,7 @@ "deleteRemoteBrowserMessage": "Are you sure want to delete this Remote Browser for all monitors?", "GrafanaOncallUrl": "Grafana Oncall URL", "Browser Screenshot": "Browser Screenshot", - "What is a Remote Browser?": "What is a Remote Browser?" + "What is a Remote Browser?": "What is a Remote Browser?", + "Command": "Command", + "mongodbCommandDescription": "Run a MongoDB command against the database. For information about the available commands check out the MongoDB documentation {0}." } diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue index 7d9e2746da..6f4e0b0ea6 100644 --- a/src/pages/EditMonitor.vue +++ b/src/pages/EditMonitor.vue @@ -439,6 +439,9 @@
+ + {{ $t('here') }} +
@@ -448,8 +451,8 @@ jsonata.org {{ $t('here') }} -
- +
+
From ac3cbfd3fd542d76ea183a4c1deb7f0cac918457 Mon Sep 17 00:00:00 2001 From: Frank Elsinga Date: Thu, 15 Feb 2024 02:06:11 +0100 Subject: [PATCH 5/5] made the helptext a bit easier to translate and a bit more accessible --- src/lang/en.json | 3 ++- src/pages/EditMonitor.vue | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/lang/en.json b/src/lang/en.json index ed8133bf65..c038734012 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -885,5 +885,6 @@ "Browser Screenshot": "Browser Screenshot", "What is a Remote Browser?": "What is a Remote Browser?", "Command": "Command", - "mongodbCommandDescription": "Run a MongoDB command against the database. For information about the available commands check out the MongoDB documentation {0}." + "mongodbCommandDescription": "Run a MongoDB command against the database. For information about the available commands check out the {documentation}", + "documentationOf": "{0} Documentation" } diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue index 6f4e0b0ea6..ca5a435836 100644 --- a/src/pages/EditMonitor.vue +++ b/src/pages/EditMonitor.vue @@ -440,7 +440,9 @@ - {{ $t('here') }} +