mirror of
https://github.com/louislam/uptime-kuma.git
synced 2024-11-27 16:54:04 +00:00
Add support for custom mongodb commands (#4445)
Co-authored-by: Sebastian Lang <sebastian.lang@damovo.com> Co-authored-by: Frank Elsinga <frank@elsinga.de>
This commit is contained in:
parent
e856cb6007
commit
a3ac954140
6 changed files with 96 additions and 29 deletions
|
@ -5,7 +5,7 @@ const { log, UP, DOWN, PENDING, MAINTENANCE, flipStatus, MAX_INTERVAL_SECOND, MI
|
||||||
SQL_DATETIME_FORMAT
|
SQL_DATETIME_FORMAT
|
||||||
} = require("../../src/util");
|
} = require("../../src/util");
|
||||||
const { tcping, ping, checkCertificate, checkStatusCode, getTotalClientInRoom, setting, mssqlQuery, postgresQuery, mysqlQuery, setSetting, httpNtlm, radius, grpcQuery,
|
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");
|
} = require("../util-server");
|
||||||
const { R } = require("redbean-node");
|
const { R } = require("redbean-node");
|
||||||
const { BeanModel } = require("redbean-node/dist/bean-model");
|
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.msg = await mysqlQuery(this.databaseConnectionString, this.databaseQuery || "SELECT 1", mysqlPassword);
|
||||||
bean.status = UP;
|
bean.status = UP;
|
||||||
bean.ping = dayjs().valueOf() - startTime;
|
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") {
|
} else if (this.type === "radius") {
|
||||||
let startTime = dayjs().valueOf();
|
let startTime = dayjs().valueOf();
|
||||||
|
|
||||||
|
|
65
server/monitor-types/mongodb.js
Normal file
65
server/monitor-types/mongodb.js
Normal file
|
@ -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,
|
||||||
|
};
|
|
@ -113,6 +113,7 @@ class UptimeKumaServer {
|
||||||
UptimeKumaServer.monitorTypeList["tailscale-ping"] = new TailscalePing();
|
UptimeKumaServer.monitorTypeList["tailscale-ping"] = new TailscalePing();
|
||||||
UptimeKumaServer.monitorTypeList["dns"] = new DnsMonitorType();
|
UptimeKumaServer.monitorTypeList["dns"] = new DnsMonitorType();
|
||||||
UptimeKumaServer.monitorTypeList["mqtt"] = new MqttMonitorType();
|
UptimeKumaServer.monitorTypeList["mqtt"] = new MqttMonitorType();
|
||||||
|
UptimeKumaServer.monitorTypeList["mongodb"] = new MongodbMonitorType();
|
||||||
|
|
||||||
// Allow all CORS origins (polling) in development
|
// Allow all CORS origins (polling) in development
|
||||||
let cors = undefined;
|
let cors = undefined;
|
||||||
|
@ -516,3 +517,4 @@ const { RealBrowserMonitorType } = require("./monitor-types/real-browser-monitor
|
||||||
const { TailscalePing } = require("./monitor-types/tailscale-ping");
|
const { TailscalePing } = require("./monitor-types/tailscale-ping");
|
||||||
const { DnsMonitorType } = require("./monitor-types/dns");
|
const { DnsMonitorType } = require("./monitor-types/dns");
|
||||||
const { MqttMonitorType } = require("./monitor-types/mqtt");
|
const { MqttMonitorType } = require("./monitor-types/mqtt");
|
||||||
|
const { MongodbMonitorType } = require("./monitor-types/mongodb");
|
||||||
|
|
|
@ -11,7 +11,6 @@ const mssql = require("mssql");
|
||||||
const { Client } = require("pg");
|
const { Client } = require("pg");
|
||||||
const postgresConParse = require("pg-connection-string").parse;
|
const postgresConParse = require("pg-connection-string").parse;
|
||||||
const mysql = require("mysql2");
|
const mysql = require("mysql2");
|
||||||
const { MongoClient } = require("mongodb");
|
|
||||||
const { NtlmClient } = require("axios-ntlm");
|
const { NtlmClient } = require("axios-ntlm");
|
||||||
const { Settings } = require("./settings");
|
const { Settings } = require("./settings");
|
||||||
const grpc = require("@grpc/grpc-js");
|
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
|
* Query radius server
|
||||||
* @param {string} hostname Hostname of radius server
|
* @param {string} hostname Hostname of radius server
|
||||||
|
|
|
@ -902,6 +902,8 @@
|
||||||
"deleteRemoteBrowserMessage": "Are you sure want to delete this Remote Browser for all monitors?",
|
"deleteRemoteBrowserMessage": "Are you sure want to delete this Remote Browser for all monitors?",
|
||||||
"GrafanaOncallUrl": "Grafana Oncall URL",
|
"GrafanaOncallUrl": "Grafana Oncall URL",
|
||||||
"Browser Screenshot": "Browser Screenshot",
|
"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",
|
"wayToGetSevenIOApiKey": "Visit the dashboard under app.seven.io > developer > api key > the green add button",
|
||||||
"senderSevenIO": "Sending number or name",
|
"senderSevenIO": "Sending number or name",
|
||||||
"receiverSevenIO": "Receiving number",
|
"receiverSevenIO": "Receiving number",
|
||||||
|
|
|
@ -434,6 +434,32 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<!-- MongoDB -->
|
||||||
|
<template v-if="monitor.type === 'mongodb'">
|
||||||
|
<div class="my-3">
|
||||||
|
<label for="mongodbCommand" class="form-label">{{ $t("Command") }}</label>
|
||||||
|
<textarea id="mongodbCommand" v-model="monitor.databaseQuery" class="form-control" :placeholder="$t('Example:', [ '{ "ping": 1 }' ])"></textarea>
|
||||||
|
<i18n-t tag="div" class="form-text" keypath="mongodbCommandDescription">
|
||||||
|
<template #documentation>
|
||||||
|
<a href="https://www.mongodb.com/docs/manual/reference/command/">{{ $t('documentationOf', ['MongoDB']) }}</a>
|
||||||
|
</template>
|
||||||
|
</i18n-t>
|
||||||
|
</div>
|
||||||
|
<div class="my-3">
|
||||||
|
<label for="jsonPath" class="form-label">{{ $t("Json Query") }}</label>
|
||||||
|
<input id="jsonPath" v-model="monitor.jsonPath" type="text" class="form-control">
|
||||||
|
|
||||||
|
<i18n-t tag="div" class="form-text" keypath="jsonQueryDescription">
|
||||||
|
<a href="https://jsonata.org/">jsonata.org</a>
|
||||||
|
<a href="https://try.jsonata.org/">{{ $t('here') }}</a>
|
||||||
|
</i18n-t>
|
||||||
|
</div>
|
||||||
|
<div class="my-3">
|
||||||
|
<label for="expectedValue" class="form-label">{{ $t("Expected Value") }}</label>
|
||||||
|
<input id="expectedValue" v-model="monitor.expectedValue" type="text" class="form-control">
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
<!-- Interval -->
|
<!-- Interval -->
|
||||||
<div class="my-3">
|
<div class="my-3">
|
||||||
<label for="interval" class="form-label">{{ $t("Heartbeat Interval") }} ({{ $t("checkEverySecond", [ monitor.interval ]) }})</label>
|
<label for="interval" class="form-label">{{ $t("Heartbeat Interval") }} ({{ $t("checkEverySecond", [ monitor.interval ]) }})</label>
|
||||||
|
|
Loading…
Reference in a new issue