mirror of
https://github.com/koichixD/uptime-kuma.git
synced 2026-09-04 09:13:21 +00:00
feat: Add 'local service' monitor type
This adds a new monitor type to check local services by executing a shell command. It also includes fixes for Prometheus errors when adding new tags and for the UI not updating when tags are changed.
This commit is contained in:
@@ -148,6 +148,7 @@ class Monitor extends BeanModel {
|
||||
httpBodyEncoding: this.httpBodyEncoding,
|
||||
jsonPath: this.jsonPath,
|
||||
expectedValue: this.expectedValue,
|
||||
local_service_check_type: this.local_service_check_type,
|
||||
kafkaProducerTopic: this.kafkaProducerTopic,
|
||||
kafkaProducerBrokers: JSON.parse(this.kafkaProducerBrokers),
|
||||
kafkaProducerSsl: this.getKafkaProducerSsl(),
|
||||
@@ -201,6 +202,8 @@ class Monitor extends BeanModel {
|
||||
kafkaProducerSaslOptions: JSON.parse(this.kafkaProducerSaslOptions),
|
||||
rabbitmqUsername: this.rabbitmqUsername,
|
||||
rabbitmqPassword: this.rabbitmqPassword,
|
||||
local_service_command: this.local_service_command,
|
||||
local_service_expected_output: this.local_service_expected_output,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
const { MonitorType } = require("./monitor-type");
|
||||
const { exec } = require("child_process");
|
||||
const { DOWN, UP, log, evaluateJsonQuery } = require("../../src/util");
|
||||
|
||||
class LocalServiceMonitorType extends MonitorType {
|
||||
name = "local-service";
|
||||
description = "Checks if a local service is running by executing a command.";
|
||||
|
||||
async check(monitor, heartbeat, server) {
|
||||
return new Promise((resolve, reject) => {
|
||||
exec(monitor.local_service_command, async (error, stdout, stderr) => {
|
||||
if (error) {
|
||||
heartbeat.status = DOWN;
|
||||
heartbeat.msg = `Error executing command: ${error.message}`;
|
||||
reject(new Error(heartbeat.msg));
|
||||
return;
|
||||
}
|
||||
|
||||
const output = stdout.trim();
|
||||
|
||||
if (monitor.local_service_check_type === "keyword") {
|
||||
if (monitor.local_service_expected_output) {
|
||||
if (output.includes(monitor.local_service_expected_output)) {
|
||||
heartbeat.status = UP;
|
||||
heartbeat.msg = `OK - Output contains "${monitor.local_service_expected_output}"`;
|
||||
resolve();
|
||||
} else {
|
||||
heartbeat.status = DOWN;
|
||||
heartbeat.msg = `Output did not contain "${monitor.local_service_expected_output}"`;
|
||||
reject(new Error(heartbeat.msg));
|
||||
}
|
||||
} else {
|
||||
heartbeat.status = UP;
|
||||
heartbeat.msg = "OK - Command executed successfully";
|
||||
resolve();
|
||||
}
|
||||
} else if (monitor.local_service_check_type === "json-query") {
|
||||
try {
|
||||
const data = JSON.parse(output);
|
||||
const { status, response } = await evaluateJsonQuery(data, monitor.jsonPath, monitor.jsonPathOperator, monitor.expectedValue);
|
||||
|
||||
if (status) {
|
||||
heartbeat.status = UP;
|
||||
heartbeat.msg = `JSON query passes (comparing ${response} ${monitor.jsonPathOperator} ${monitor.expectedValue})`;
|
||||
resolve();
|
||||
} else {
|
||||
throw new Error(`JSON query does not pass (comparing ${response} ${monitor.jsonPathOperator} ${monitor.expectedValue})`);
|
||||
}
|
||||
} catch (e) {
|
||||
heartbeat.status = DOWN;
|
||||
heartbeat.msg = e.message;
|
||||
reject(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
LocalServiceMonitorType,
|
||||
};
|
||||
@@ -900,6 +900,9 @@ let needSetup = false;
|
||||
bean.rabbitmqPassword = monitor.rabbitmqPassword;
|
||||
bean.conditions = JSON.stringify(monitor.conditions);
|
||||
bean.manual_status = monitor.manual_status;
|
||||
bean.local_service_command = monitor.local_service_command;
|
||||
bean.local_service_expected_output = monitor.local_service_expected_output;
|
||||
bean.local_service_check_type = monitor.local_service_check_type;
|
||||
|
||||
// ping advanced options
|
||||
bean.ping_numeric = monitor.ping_numeric;
|
||||
@@ -1173,6 +1176,8 @@ let needSetup = false;
|
||||
bean.color = tag.color;
|
||||
await R.store(bean);
|
||||
|
||||
await Prometheus.init();
|
||||
|
||||
callback({
|
||||
ok: true,
|
||||
tag: await bean.toJSON(),
|
||||
@@ -1248,6 +1253,8 @@ let needSetup = false;
|
||||
value,
|
||||
]);
|
||||
|
||||
await server.sendUpdateMonitorIntoList(socket, monitorID);
|
||||
|
||||
callback({
|
||||
ok: true,
|
||||
msg: "successAdded",
|
||||
@@ -1272,6 +1279,8 @@ let needSetup = false;
|
||||
monitorID,
|
||||
]);
|
||||
|
||||
await server.sendUpdateMonitorIntoList(socket, monitorID);
|
||||
|
||||
callback({
|
||||
ok: true,
|
||||
msg: "successEdited",
|
||||
@@ -1296,6 +1305,8 @@ let needSetup = false;
|
||||
value,
|
||||
]);
|
||||
|
||||
await server.sendUpdateMonitorIntoList(socket, monitorID);
|
||||
|
||||
callback({
|
||||
ok: true,
|
||||
msg: "successDeleted",
|
||||
|
||||
@@ -124,6 +124,7 @@ class UptimeKumaServer {
|
||||
UptimeKumaServer.monitorTypeList["port"] = new TCPMonitorType();
|
||||
UptimeKumaServer.monitorTypeList["manual"] = new ManualMonitorType();
|
||||
UptimeKumaServer.monitorTypeList["redis"] = new RedisMonitorType();
|
||||
UptimeKumaServer.monitorTypeList["local-service"] = new LocalServiceMonitorType();
|
||||
|
||||
// Allow all CORS origins (polling) in development
|
||||
let cors = undefined;
|
||||
@@ -570,5 +571,6 @@ const { RabbitMqMonitorType } = require("./monitor-types/rabbitmq");
|
||||
const { TCPMonitorType } = require("./monitor-types/tcp.js");
|
||||
const { ManualMonitorType } = require("./monitor-types/manual");
|
||||
const { RedisMonitorType } = require("./monitor-types/redis");
|
||||
const { LocalServiceMonitorType } = require("./monitor-types/local-service");
|
||||
const Monitor = require("./model/monitor");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user