Merge pull request #1636 from christopherpickering/master

Add SQLServer Monitor
pull/1598/head
Louis Lam 2 years ago committed by GitHub
commit e1681ce370
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,10 @@
BEGIN TRANSACTION;
ALTER TABLE monitor
ADD database_connection_string VARCHAR(2000);
ALTER TABLE monitor
ADD database_query TEXT;
COMMIT

1744
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -96,6 +96,7 @@
"jwt-decode": "^3.1.2", "jwt-decode": "^3.1.2",
"limiter": "^2.1.0", "limiter": "^2.1.0",
"mqtt": "^4.2.8", "mqtt": "^4.2.8",
"mssql": "^8.1.0",
"node-cloudflared-tunnel": "~1.0.9", "node-cloudflared-tunnel": "~1.0.9",
"nodemailer": "~6.6.5", "nodemailer": "~6.6.5",
"notp": "~2.0.3", "notp": "~2.0.3",

@ -58,6 +58,7 @@ class Database {
"patch-monitor-expiry-notification.sql": true, "patch-monitor-expiry-notification.sql": true,
"patch-status-page-footer-css.sql": true, "patch-status-page-footer-css.sql": true,
"patch-added-mqtt-monitor.sql": true, "patch-added-mqtt-monitor.sql": true,
"patch-add-sqlserver-monitor.sql": true,
}; };
/** /**

@ -7,7 +7,7 @@ dayjs.extend(timezone);
const axios = require("axios"); const axios = require("axios");
const { Prometheus } = require("../prometheus"); const { Prometheus } = require("../prometheus");
const { log, UP, DOWN, PENDING, flipStatus, TimeLogger } = require("../../src/util"); const { log, UP, DOWN, PENDING, flipStatus, TimeLogger } = require("../../src/util");
const { tcping, ping, dnsResolve, checkCertificate, checkStatusCode, getTotalClientInRoom, setting, mqttAsync } = require("../util-server"); const { tcping, ping, dnsResolve, checkCertificate, checkStatusCode, getTotalClientInRoom, setting, mssqlQuery, mqttAsync } = 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");
const { Notification } = require("../notification"); const { Notification } = require("../notification");
@ -87,7 +87,9 @@ class Monitor extends BeanModel {
mqttUsername: this.mqttUsername, mqttUsername: this.mqttUsername,
mqttPassword: this.mqttPassword, mqttPassword: this.mqttPassword,
mqttTopic: this.mqttTopic, mqttTopic: this.mqttTopic,
mqttSuccessMessage: this.mqttSuccessMessage mqttSuccessMessage: this.mqttSuccessMessage,
databaseConnectionString: this.databaseConnectionString,
databaseQuery: this.databaseQuery,
}; };
if (includeSensitiveData) { if (includeSensitiveData) {
@ -447,6 +449,14 @@ class Monitor extends BeanModel {
interval: this.interval, interval: this.interval,
}); });
bean.status = UP; bean.status = UP;
} else if (this.type === "sqlserver") {
let startTime = dayjs().valueOf();
await mssqlQuery(this.databaseConnectionString, this.databaseQuery);
bean.msg = "";
bean.status = UP;
bean.ping = dayjs().valueOf() - startTime;
} else { } else {
bean.msg = "Unknown Monitor Type"; bean.msg = "Unknown Monitor Type";
bean.status = PENDING; bean.status = PENDING;

@ -669,6 +669,8 @@ let needSetup = false;
bean.mqttPassword = monitor.mqttPassword; bean.mqttPassword = monitor.mqttPassword;
bean.mqttTopic = monitor.mqttTopic; bean.mqttTopic = monitor.mqttTopic;
bean.mqttSuccessMessage = monitor.mqttSuccessMessage; bean.mqttSuccessMessage = monitor.mqttSuccessMessage;
bean.databaseConnectionString = monitor.databaseConnectionString;
bean.databaseQuery = monitor.databaseQuery;
await R.store(bean); await R.store(bean);

@ -10,6 +10,7 @@ const chardet = require("chardet");
const mqtt = require("mqtt"); const mqtt = require("mqtt");
const chroma = require("chroma-js"); const chroma = require("chroma-js");
const { badgeConstants } = require("./config"); const { badgeConstants } = require("./config");
const mssql = require("mssql");
// From ping-lite // From ping-lite
exports.WIN = /^win/.test(process.platform); exports.WIN = /^win/.test(process.platform);
@ -207,6 +208,31 @@ exports.dnsResolve = function (hostname, resolverServer, resolverPort, rrtype) {
}); });
}; };
/**
* Run a query on SQL Server
* @param {string} connectionString The database connection string
* @param {string} query The query to validate the database with
* @returns {Promise<(string[]|Object[]|Object)>}
*/
exports.mssqlQuery = function (connectionString, query) {
return new Promise((resolve, reject) => {
mssql.on("error", err => {
reject(err);
});
mssql.connect(connectionString).then(pool => {
return pool.request()
.query(query);
}).then(result => {
resolve(result);
}).catch(err => {
reject(err);
}).finally(() => {
mssql.close();
});
});
};
/** /**
* Retrieve value of setting based on key * Retrieve value of setting based on key
* @param {string} key Key of setting to retrieve * @param {string} key Key of setting to retrieve

@ -525,4 +525,6 @@ export default {
"Go back to the previous page.": "Go back to the previous page.", "Go back to the previous page.": "Go back to the previous page.",
"Coming Soon": "Coming Soon", "Coming Soon": "Coming Soon",
wayToGetClickSendSMSToken: "You can get API Username and API Key from {0} .", wayToGetClickSendSMSToken: "You can get API Username and API Key from {0} .",
"Connection String": "Connection String",
"Query": "Query",
}; };

@ -35,6 +35,9 @@
<option value="mqtt"> <option value="mqtt">
MQTT MQTT
</option> </option>
<option value="sqlserver">
SQL Server
</option>
</select> </select>
</div> </div>
@ -157,6 +160,18 @@
</div> </div>
</template> </template>
<!-- SQL Server -->
<template v-if="monitor.type === 'sqlserver'">
<div class="my-3">
<label for="sqlserverConnectionString" class="form-label">SQL Server {{ $t("Connection String") }}</label>
<input id="sqlserverConnectionString" v-model="monitor.databaseConnectionString" type="text" class="form-control">
</div>
<div class="my-3">
<label for="sqlserverQuery" class="form-label">SQL Server {{ $t("Query") }}</label>
<textarea id="sqlserverQuery" v-model="monitor.databaseQuery" class="form-control" placeholder="Example: select getdate()"></textarea>
</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…
Cancel
Save