From a3ac95414065da256880bf50c1a0e5b01c760052 Mon Sep 17 00:00:00 2001 From: Sebastian Lang Date: Sun, 19 May 2024 21:59:57 +0200 Subject: [PATCH] Add support for custom mongodb commands (#4445) Co-authored-by: Sebastian Lang Co-authored-by: Frank Elsinga --- server/model/monitor.js | 11 +----- server/monitor-types/mongodb.js | 65 +++++++++++++++++++++++++++++++++ server/uptime-kuma-server.js | 2 + server/util-server.js | 19 ---------- src/lang/en.json | 2 + src/pages/EditMonitor.vue | 26 +++++++++++++ 6 files changed, 96 insertions(+), 29 deletions(-) create mode 100644 server/monitor-types/mongodb.js diff --git a/server/model/monitor.js b/server/model/monitor.js index 65bfb420c..69b685c77 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, kafkaProducerAsync, getOidcTokenClientCredentials, rootCertificatesFingerprints, axiosAbortSignal } = require("../util-server"); const { R } = require("redbean-node"); const { BeanModel } = require("redbean-node/dist/bean-model"); @@ -822,15 +822,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(); - - await mongodbPing(this.databaseConnectionString); - - 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 000000000..fd3a1dc98 --- /dev/null +++ b/server/monitor-types/mongodb.js @@ -0,0 +1,65 @@ +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"); + } else { + heartbeat.msg = "Command executed successfully"; + } + + if (monitor.jsonPath) { + let expression = jsonata(monitor.jsonPath); + result = await expression.evaluate(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 = "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) + "]"); + } + } + + heartbeat.status = UP; + } + + /** + * 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 + * 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 bcf497b58..6ab5f6c26 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 24d428694..d728b6c20 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,24 +436,6 @@ exports.mysqlQuery = function (connectionString, query, password = undefined) { }); }; -/** - * Connect to and ping a MongoDB database - * @param {string} connectionString The database connection string - * @returns {Promise<(string[] | object[] | object)>} Response from - * server - */ -exports.mongodbPing = async function (connectionString) { - let client = await MongoClient.connect(connectionString); - let dbPing = await client.db().command({ ping: 1 }); - await client.close(); - - if (dbPing["ok"] === 1) { - return "UP"; - } else { - throw Error("failed"); - } -}; - /** * Query radius server * @param {string} hostname Hostname of radius server diff --git a/src/lang/en.json b/src/lang/en.json index 2a3e6628c..aa597a5e3 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -902,6 +902,8 @@ "deleteRemoteBrowserMessage": "Are you sure want to delete this Remote Browser for all monitors?", "GrafanaOncallUrl": "Grafana Oncall URL", "Browser Screenshot": "Browser Screenshot", + "Command": "Command", + "mongodbCommandDescription": "Run a MongoDB command against the database. For information about the available commands check out the {documentation}", "wayToGetSevenIOApiKey": "Visit the dashboard under app.seven.io > developer > api key > the green add button", "senderSevenIO": "Sending number or name", "receiverSevenIO": "Receiving number", diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue index e9f3ac832..38017778a 100644 --- a/src/pages/EditMonitor.vue +++ b/src/pages/EditMonitor.vue @@ -434,6 +434,32 @@ + + +