mirror of
https://github.com/louislam/uptime-kuma.git
synced 2025-07-26 02:14:04 +02:00
Add Websocket path to mqtt monitor for WebSocket connection (#6009)
Some checks failed
Auto Test / armv7-simple-test (18, ARMv7) (push) Has been cancelled
Auto Test / armv7-simple-test (20, ARMv7) (push) Has been cancelled
Auto Test / check-linters (push) Has been cancelled
Auto Test / e2e-test (push) Has been cancelled
CodeQL / Analyze (push) Has been cancelled
Merge Conflict Labeler / Labeling (push) Has been cancelled
validate / json-yaml-validate (push) Has been cancelled
validate / validate (push) Has been cancelled
Auto Test / auto-test (18, ARM64) (push) Has been cancelled
Auto Test / auto-test (18, macos-latest) (push) Has been cancelled
Auto Test / auto-test (18, ubuntu-latest) (push) Has been cancelled
Auto Test / auto-test (18, windows-latest) (push) Has been cancelled
Auto Test / auto-test (20, ARM64) (push) Has been cancelled
Auto Test / auto-test (20, macos-latest) (push) Has been cancelled
Auto Test / auto-test (20, ubuntu-latest) (push) Has been cancelled
Auto Test / auto-test (20, windows-latest) (push) Has been cancelled
Some checks failed
Auto Test / armv7-simple-test (18, ARMv7) (push) Has been cancelled
Auto Test / armv7-simple-test (20, ARMv7) (push) Has been cancelled
Auto Test / check-linters (push) Has been cancelled
Auto Test / e2e-test (push) Has been cancelled
CodeQL / Analyze (push) Has been cancelled
Merge Conflict Labeler / Labeling (push) Has been cancelled
validate / json-yaml-validate (push) Has been cancelled
validate / validate (push) Has been cancelled
Auto Test / auto-test (18, ARM64) (push) Has been cancelled
Auto Test / auto-test (18, macos-latest) (push) Has been cancelled
Auto Test / auto-test (18, ubuntu-latest) (push) Has been cancelled
Auto Test / auto-test (18, windows-latest) (push) Has been cancelled
Auto Test / auto-test (20, ARM64) (push) Has been cancelled
Auto Test / auto-test (20, macos-latest) (push) Has been cancelled
Auto Test / auto-test (20, ubuntu-latest) (push) Has been cancelled
Auto Test / auto-test (20, windows-latest) (push) Has been cancelled
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: lupaulus <20111917+lupaulus@users.noreply.github.com>
This commit is contained in:
parent
2fd4e1cc72
commit
2a6d9b4acd
7 changed files with 68 additions and 3 deletions
15
db/knex_migrations/2025-07-17-0000-mqtt-websocket-path.js
Normal file
15
db/knex_migrations/2025-07-17-0000-mqtt-websocket-path.js
Normal file
|
@ -0,0 +1,15 @@
|
|||
exports.up = function (knex) {
|
||||
// Add new column monitor.mqtt_websocket_path
|
||||
return knex.schema
|
||||
.alterTable("monitor", function (table) {
|
||||
table.string("mqtt_websocket_path", 255).nullable();
|
||||
});
|
||||
};
|
||||
|
||||
exports.down = function (knex) {
|
||||
// Drop column monitor.mqtt_websocket_path
|
||||
return knex.schema
|
||||
.alterTable("monitor", function (table) {
|
||||
table.dropColumn("mqtt_websocket_path");
|
||||
});
|
||||
};
|
|
@ -190,6 +190,7 @@ class Monitor extends BeanModel {
|
|||
radiusSecret: this.radiusSecret,
|
||||
mqttUsername: this.mqttUsername,
|
||||
mqttPassword: this.mqttPassword,
|
||||
mqttWebsocketPath: this.mqttWebsocketPath,
|
||||
authWorkstation: this.authWorkstation,
|
||||
authDomain: this.authDomain,
|
||||
tlsCa: this.tlsCa,
|
||||
|
|
|
@ -15,6 +15,7 @@ class MqttMonitorType extends MonitorType {
|
|||
username: monitor.mqttUsername,
|
||||
password: monitor.mqttPassword,
|
||||
interval: monitor.interval,
|
||||
websocketPath: monitor.mqttWebsocketPath,
|
||||
});
|
||||
|
||||
if (monitor.mqttCheckType == null || monitor.mqttCheckType === "") {
|
||||
|
@ -52,12 +53,12 @@ class MqttMonitorType extends MonitorType {
|
|||
* @param {string} hostname Hostname / address of machine to test
|
||||
* @param {string} topic MQTT topic
|
||||
* @param {object} options MQTT options. Contains port, username,
|
||||
* password and interval (interval defaults to 20)
|
||||
* password, websocketPath and interval (interval defaults to 20)
|
||||
* @returns {Promise<string>} Received MQTT message
|
||||
*/
|
||||
mqttAsync(hostname, topic, options = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const { port, username, password, interval = 20 } = options;
|
||||
const { port, username, password, websocketPath, interval = 20 } = options;
|
||||
|
||||
// Adds MQTT protocol to the hostname if not already present
|
||||
if (!/^(?:http|mqtt|ws)s?:\/\//.test(hostname)) {
|
||||
|
@ -70,7 +71,15 @@ class MqttMonitorType extends MonitorType {
|
|||
reject(new Error("Timeout, Message not received"));
|
||||
}, interval * 1000 * 0.8);
|
||||
|
||||
const mqttUrl = `${hostname}:${port}`;
|
||||
// Construct the URL based on protocol
|
||||
let mqttUrl = `${hostname}:${port}`;
|
||||
if (hostname.startsWith("ws://") || hostname.startsWith("wss://")) {
|
||||
if (websocketPath && !websocketPath.startsWith("/")) {
|
||||
mqttUrl = `${hostname}:${port}/${websocketPath || ""}`;
|
||||
} else {
|
||||
mqttUrl = `${hostname}:${port}${websocketPath || ""}`;
|
||||
}
|
||||
}
|
||||
|
||||
log.debug("mqtt", `MQTT connecting to ${mqttUrl}`);
|
||||
|
||||
|
|
|
@ -837,6 +837,7 @@ let needSetup = false;
|
|||
bean.mqttTopic = monitor.mqttTopic;
|
||||
bean.mqttSuccessMessage = monitor.mqttSuccessMessage;
|
||||
bean.mqttCheckType = monitor.mqttCheckType;
|
||||
bean.mqttWebsocketPath = monitor.mqttWebsocketPath;
|
||||
bean.databaseConnectionString = monitor.databaseConnectionString;
|
||||
bean.databaseQuery = monitor.databaseQuery;
|
||||
bean.authMethod = monitor.authMethod;
|
||||
|
|
|
@ -71,6 +71,7 @@
|
|||
"locally configured mail transfer agent": "locally configured mail transfer agent",
|
||||
"Either enter the hostname of the server you want to connect to or localhost if you intend to use a locally configured mail transfer agent": "Either enter the hostname of the server you want to connect to or {localhost} if you intend to use a {local_mta}",
|
||||
"Port": "Port",
|
||||
"Path": "Path",
|
||||
"Heartbeat Interval": "Heartbeat Interval",
|
||||
"Request Timeout": "Request Timeout",
|
||||
"timeoutAfter": "Timeout after {0} seconds",
|
||||
|
@ -266,6 +267,10 @@
|
|||
"Current User": "Current User",
|
||||
"topic": "Topic",
|
||||
"topicExplanation": "MQTT topic to monitor",
|
||||
"mqttWebSocketPath": "MQTT WebSocket Path",
|
||||
"mqttWebsocketPathExplanation": "WebSocket path for MQTT over WebSocket connections (e.g., /mqtt)",
|
||||
"mqttWebsocketPathInvalid": "Please use a valid WebSocket Path format",
|
||||
"mqttHostnameTip": "Please use this format {hostnameFormat}",
|
||||
"successKeyword": "Success Keyword",
|
||||
"successKeywordExplanation": "MQTT Keyword that will be considered as success",
|
||||
"recent": "Recent",
|
||||
|
|
|
@ -311,6 +311,13 @@
|
|||
required
|
||||
data-testid="hostname-input"
|
||||
>
|
||||
<div v-if="monitor.type === 'mqtt'" class="form-text">
|
||||
<i18n-t tag="p" keypath="mqttHostnameTip">
|
||||
<template #hostnameFormat>
|
||||
<code>[mqtt,ws,wss]://hostname</code>
|
||||
</template>
|
||||
</i18n-t>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Port -->
|
||||
|
@ -483,6 +490,21 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="my-3">
|
||||
<label for="mqttWebsocketPath" class="form-label">{{ $t("mqttWebSocketPath") }}</label>
|
||||
<input
|
||||
v-if="/wss?:\/\/.+/.test(monitor.hostname)"
|
||||
id="mqttWebsocketPath"
|
||||
v-model="monitor.mqttWebsocketPath"
|
||||
type="text"
|
||||
class="form-control"
|
||||
>
|
||||
<input v-else type="text" class="form-control" disabled>
|
||||
<div class="form-text">
|
||||
{{ $t("mqttWebsocketPathExplanation") }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="my-3">
|
||||
<label for="mqttCheckType" class="form-label">MQTT {{ $t("Check Type") }}</label>
|
||||
<select id="mqttCheckType" v-model="monitor.mqttCheckType" class="form-select" required>
|
||||
|
@ -1181,6 +1203,7 @@ const monitorDefaults = {
|
|||
mqttUsername: "",
|
||||
mqttPassword: "",
|
||||
mqttTopic: "",
|
||||
mqttWebsocketPath: "",
|
||||
mqttSuccessMessage: "",
|
||||
mqttCheckType: "keyword",
|
||||
authMethod: null,
|
||||
|
@ -1845,6 +1868,16 @@ message HealthCheckResponse {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Validate MQTT WebSocket Path pattern if present
|
||||
if (this.monitor.type === "mqtt" && this.monitor.mqttWebsocketPath) {
|
||||
const pattern = /^\/[A-Za-z0-9-_&()*+]*$/;
|
||||
if (!pattern.test(this.monitor.mqttWebsocketPath)) {
|
||||
toast.error(this.$t("mqttWebsocketPathInvalid"));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ async function testMqtt(mqttSuccessMessage, mqttCheckType, receivedMessage) {
|
|||
port: connectionString.split(":")[2],
|
||||
mqttUsername: null,
|
||||
mqttPassword: null,
|
||||
mqttWebsocketPath: null, // for WebSocket connections
|
||||
interval: 20, // controls the timeout
|
||||
mqttSuccessMessage: mqttSuccessMessage, // for keywords
|
||||
expectedValue: mqttSuccessMessage, // for json-query
|
||||
|
|
Loading…
Add table
Reference in a new issue