mirror of
				https://github.com/louislam/uptime-kuma.git
				synced 2025-11-04 13:46:13 +08:00 
			
		
		
		
	Merge pull request #875 from tarun7singh/master
Added MQTT Monitor type
This commit is contained in:
		
							
								
								
									
										16
									
								
								db/patch-added-mqtt-monitor.sql
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								db/patch-added-mqtt-monitor.sql
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					-- You should not modify if this have pushed to Github, unless it does serious wrong with the db.
 | 
				
			||||||
 | 
					BEGIN TRANSACTION;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ALTER TABLE monitor
 | 
				
			||||||
 | 
						ADD mqtt_topic TEXT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ALTER TABLE monitor
 | 
				
			||||||
 | 
						ADD mqtt_success_message VARCHAR(255);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ALTER TABLE monitor
 | 
				
			||||||
 | 
						ADD mqtt_username VARCHAR(255);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ALTER TABLE monitor
 | 
				
			||||||
 | 
						ADD mqtt_password VARCHAR(255);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					COMMIT;
 | 
				
			||||||
							
								
								
									
										50
									
								
								extra/simple-mqtt-server.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								extra/simple-mqtt-server.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,50 @@
 | 
				
			|||||||
 | 
					const { log } = require("../src/util");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const mqttUsername = "louis1";
 | 
				
			||||||
 | 
					const mqttPassword = "!@#$LLam";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SimpleMqttServer {
 | 
				
			||||||
 | 
					    aedes = require("aedes")();
 | 
				
			||||||
 | 
					    server = require("net").createServer(this.aedes.handle);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor(port) {
 | 
				
			||||||
 | 
					        this.port = port;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    start() {
 | 
				
			||||||
 | 
					        this.server.listen(this.port, () => {
 | 
				
			||||||
 | 
					            console.log("server started and listening on port ", this.port);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					let server1 = new SimpleMqttServer(10000);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					server1.aedes.authenticate = function (client, username, password, callback) {
 | 
				
			||||||
 | 
					    if (username && password) {
 | 
				
			||||||
 | 
					        console.log(password.toString("utf-8"));
 | 
				
			||||||
 | 
					        callback(null, username === mqttUsername && password.toString("utf-8") === mqttPassword);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					        callback(null, false);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					server1.aedes.on("subscribe", (subscriptions, client) => {
 | 
				
			||||||
 | 
					    console.log(subscriptions);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (let s of subscriptions) {
 | 
				
			||||||
 | 
					        if (s.topic === "test") {
 | 
				
			||||||
 | 
					            server1.aedes.publish({
 | 
				
			||||||
 | 
					                topic: "test",
 | 
				
			||||||
 | 
					                payload: Buffer.from("ok"),
 | 
				
			||||||
 | 
					            }, (error) => {
 | 
				
			||||||
 | 
					                if (error) {
 | 
				
			||||||
 | 
					                    log.error("mqtt_server", error);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					server1.start();
 | 
				
			||||||
							
								
								
									
										708
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										708
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -49,6 +49,7 @@
 | 
				
			|||||||
        "test-install-script-ubuntu1604": "npm run compile-install-script && docker build --progress plain -f test/test_install_script/ubuntu1604.dockerfile .",
 | 
					        "test-install-script-ubuntu1604": "npm run compile-install-script && docker build --progress plain -f test/test_install_script/ubuntu1604.dockerfile .",
 | 
				
			||||||
        "test-nodejs16": "docker build --progress plain -f test/ubuntu-nodejs16.dockerfile .",
 | 
					        "test-nodejs16": "docker build --progress plain -f test/ubuntu-nodejs16.dockerfile .",
 | 
				
			||||||
        "simple-dns-server": "node extra/simple-dns-server.js",
 | 
					        "simple-dns-server": "node extra/simple-dns-server.js",
 | 
				
			||||||
 | 
					        "simple-mqtt-server": "node extra/simple-mqtt-server.js",
 | 
				
			||||||
        "update-language-files-with-base-lang": "cd extra/update-language-files && node index.js %npm_config_base_lang% && eslint ../../src/languages/**.js --fix",
 | 
					        "update-language-files-with-base-lang": "cd extra/update-language-files && node index.js %npm_config_base_lang% && eslint ../../src/languages/**.js --fix",
 | 
				
			||||||
        "update-language-files": "cd extra/update-language-files && node index.js && eslint ../../src/languages/**.js --fix",
 | 
					        "update-language-files": "cd extra/update-language-files && node index.js && eslint ../../src/languages/**.js --fix",
 | 
				
			||||||
        "ncu-patch": "npm-check-updates -u -t patch",
 | 
					        "ncu-patch": "npm-check-updates -u -t patch",
 | 
				
			||||||
@@ -86,6 +87,7 @@
 | 
				
			|||||||
        "jsonwebtoken": "~8.5.1",
 | 
					        "jsonwebtoken": "~8.5.1",
 | 
				
			||||||
        "jwt-decode": "^3.1.2",
 | 
					        "jwt-decode": "^3.1.2",
 | 
				
			||||||
        "limiter": "^2.1.0",
 | 
					        "limiter": "^2.1.0",
 | 
				
			||||||
 | 
					        "mqtt": "^4.2.8",
 | 
				
			||||||
        "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",
 | 
				
			||||||
@@ -126,6 +128,7 @@
 | 
				
			|||||||
        "@vitejs/plugin-legacy": "~1.6.4",
 | 
					        "@vitejs/plugin-legacy": "~1.6.4",
 | 
				
			||||||
        "@vitejs/plugin-vue": "~1.9.4",
 | 
					        "@vitejs/plugin-vue": "~1.9.4",
 | 
				
			||||||
        "@vue/compiler-sfc": "~3.2.31",
 | 
					        "@vue/compiler-sfc": "~3.2.31",
 | 
				
			||||||
 | 
					        "aedes": "^0.46.3",
 | 
				
			||||||
        "babel-plugin-rewire": "~1.2.0",
 | 
					        "babel-plugin-rewire": "~1.2.0",
 | 
				
			||||||
        "core-js": "~3.18.3",
 | 
					        "core-js": "~3.18.3",
 | 
				
			||||||
        "cross-env": "~7.0.3",
 | 
					        "cross-env": "~7.0.3",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -57,6 +57,7 @@ class Database {
 | 
				
			|||||||
        "patch-proxy.sql": true,
 | 
					        "patch-proxy.sql": true,
 | 
				
			||||||
        "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,
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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, errorLog } = require("../util-server");
 | 
					const { tcping, ping, dnsResolve, checkCertificate, checkStatusCode, getTotalClientInRoom, setting, errorLog, 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");
 | 
				
			||||||
@@ -81,6 +81,10 @@ class Monitor extends BeanModel {
 | 
				
			|||||||
            proxyId: this.proxy_id,
 | 
					            proxyId: this.proxy_id,
 | 
				
			||||||
            notificationIDList,
 | 
					            notificationIDList,
 | 
				
			||||||
            tags: tags,
 | 
					            tags: tags,
 | 
				
			||||||
 | 
					            mqttUsername: this.mqttUsername,
 | 
				
			||||||
 | 
					            mqttPassword: this.mqttPassword,
 | 
				
			||||||
 | 
					            mqttTopic: this.mqttTopic,
 | 
				
			||||||
 | 
					            mqttSuccessMessage: this.mqttSuccessMessage
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (includeSensitiveData) {
 | 
					        if (includeSensitiveData) {
 | 
				
			||||||
@@ -159,7 +163,7 @@ class Monitor extends BeanModel {
 | 
				
			|||||||
            // undefined if not https
 | 
					            // undefined if not https
 | 
				
			||||||
            let tlsInfo = undefined;
 | 
					            let tlsInfo = undefined;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (! previousBeat) {
 | 
					            if (!previousBeat) {
 | 
				
			||||||
                previousBeat = await R.findOne("heartbeat", " monitor_id = ? ORDER BY time DESC", [
 | 
					                previousBeat = await R.findOne("heartbeat", " monitor_id = ? ORDER BY time DESC", [
 | 
				
			||||||
                    this.id,
 | 
					                    this.id,
 | 
				
			||||||
                ]);
 | 
					                ]);
 | 
				
			||||||
@@ -177,7 +181,7 @@ class Monitor extends BeanModel {
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // Duration
 | 
					            // Duration
 | 
				
			||||||
            if (! isFirstBeat) {
 | 
					            if (!isFirstBeat) {
 | 
				
			||||||
                bean.duration = dayjs(bean.time).diff(dayjs(previousBeat.time), "second");
 | 
					                bean.duration = dayjs(bean.time).diff(dayjs(previousBeat.time), "second");
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                bean.duration = 0;
 | 
					                bean.duration = 0;
 | 
				
			||||||
@@ -382,7 +386,7 @@ class Monitor extends BeanModel {
 | 
				
			|||||||
                        },
 | 
					                        },
 | 
				
			||||||
                        httpsAgent: new https.Agent({
 | 
					                        httpsAgent: new https.Agent({
 | 
				
			||||||
                            maxCachedSessions: 0,      // Use Custom agent to disable session reuse (https://github.com/nodejs/node/issues/3940)
 | 
					                            maxCachedSessions: 0,      // Use Custom agent to disable session reuse (https://github.com/nodejs/node/issues/3940)
 | 
				
			||||||
                            rejectUnauthorized: ! this.getIgnoreTls(),
 | 
					                            rejectUnauthorized: !this.getIgnoreTls(),
 | 
				
			||||||
                        }),
 | 
					                        }),
 | 
				
			||||||
                        maxRedirects: this.maxredirects,
 | 
					                        maxRedirects: this.maxredirects,
 | 
				
			||||||
                        validateStatus: (status) => {
 | 
					                        validateStatus: (status) => {
 | 
				
			||||||
@@ -404,7 +408,14 @@ class Monitor extends BeanModel {
 | 
				
			|||||||
                    } else {
 | 
					                    } else {
 | 
				
			||||||
                        throw new Error("Server not found on Steam");
 | 
					                        throw new Error("Server not found on Steam");
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					                } else if (this.type === "mqtt") {
 | 
				
			||||||
 | 
					                    bean.msg = await mqttAsync(this.hostname, this.mqttTopic, this.mqttSuccessMessage, {
 | 
				
			||||||
 | 
					                        port: this.port,
 | 
				
			||||||
 | 
					                        username: this.mqttUsername,
 | 
				
			||||||
 | 
					                        password: this.mqttPassword,
 | 
				
			||||||
 | 
					                        interval: this.interval,
 | 
				
			||||||
 | 
					                    });
 | 
				
			||||||
 | 
					                    bean.status = UP;
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    bean.msg = "Unknown Monitor Type";
 | 
					                    bean.msg = "Unknown Monitor Type";
 | 
				
			||||||
                    bean.status = PENDING;
 | 
					                    bean.status = PENDING;
 | 
				
			||||||
@@ -683,7 +694,7 @@ class Monitor extends BeanModel {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            // Handle new monitor with only one beat, because the beat's duration = 0
 | 
					            // Handle new monitor with only one beat, because the beat's duration = 0
 | 
				
			||||||
            let status = parseInt(await R.getCell("SELECT `status` FROM heartbeat WHERE monitor_id = ?", [ monitorID ]));
 | 
					            let status = parseInt(await R.getCell("SELECT `status` FROM heartbeat WHERE monitor_id = ?", [monitorID]));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (status === UP) {
 | 
					            if (status === UP) {
 | 
				
			||||||
                uptime = 1;
 | 
					                uptime = 1;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -720,6 +720,9 @@ try {
 | 
				
			|||||||
                bean.dns_resolve_server = monitor.dns_resolve_server;
 | 
					                bean.dns_resolve_server = monitor.dns_resolve_server;
 | 
				
			||||||
                bean.pushToken = monitor.pushToken;
 | 
					                bean.pushToken = monitor.pushToken;
 | 
				
			||||||
                bean.proxyId = Number.isInteger(monitor.proxyId) ? monitor.proxyId : null;
 | 
					                bean.proxyId = Number.isInteger(monitor.proxyId) ? monitor.proxyId : null;
 | 
				
			||||||
 | 
					                bean.mqttUsername = monitor.mqttUsername;
 | 
				
			||||||
 | 
					                bean.mqttTopic = monitor.mqttTopic;
 | 
				
			||||||
 | 
					                bean.mqttSuccessMessage = monitor.mqttSuccessMessage;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                await R.store(bean);
 | 
					                await R.store(bean);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -9,6 +9,7 @@ const iconv = require("iconv-lite");
 | 
				
			|||||||
const chardet = require("chardet");
 | 
					const chardet = require("chardet");
 | 
				
			||||||
const fs = require("fs");
 | 
					const fs = require("fs");
 | 
				
			||||||
const nodeJsUtil = require("util");
 | 
					const nodeJsUtil = require("util");
 | 
				
			||||||
 | 
					const mqtt = require("mqtt");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// From ping-lite
 | 
					// From ping-lite
 | 
				
			||||||
exports.WIN = /^win/.test(process.platform);
 | 
					exports.WIN = /^win/.test(process.platform);
 | 
				
			||||||
@@ -26,7 +27,7 @@ exports.initJWTSecret = async () => {
 | 
				
			|||||||
        "jwtSecret",
 | 
					        "jwtSecret",
 | 
				
			||||||
    ]);
 | 
					    ]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (! jwtSecretBean) {
 | 
					    if (!jwtSecretBean) {
 | 
				
			||||||
        jwtSecretBean = R.dispense("setting");
 | 
					        jwtSecretBean = R.dispense("setting");
 | 
				
			||||||
        jwtSecretBean.key = "jwtSecret";
 | 
					        jwtSecretBean.key = "jwtSecret";
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -88,6 +89,63 @@ exports.pingAsync = function (hostname, ipv6 = false) {
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					exports.mqttAsync = function (hostname, topic, okMessage, options = {}) {
 | 
				
			||||||
 | 
					    return new Promise((resolve, reject) => {
 | 
				
			||||||
 | 
					        const { port, username, password, interval = 20 } = options;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Adds MQTT protocol to the hostname if not already present
 | 
				
			||||||
 | 
					        if (!/^(?:http|mqtt)s?:\/\//.test(hostname)) {
 | 
				
			||||||
 | 
					            hostname = "mqtt://" + hostname;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        const timeoutID = setTimeout(() => {
 | 
				
			||||||
 | 
					            log.debug("mqtt", "MQTT timeout triggered");
 | 
				
			||||||
 | 
					            client.end();
 | 
				
			||||||
 | 
					            reject(new Error("Timeout"));
 | 
				
			||||||
 | 
					        }, interval * 1000 * 0.8);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        log.debug("mqtt", "MQTT connecting");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let client = mqtt.connect(hostname, {
 | 
				
			||||||
 | 
					            port,
 | 
				
			||||||
 | 
					            username,
 | 
				
			||||||
 | 
					            password
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        client.on("connect", () => {
 | 
				
			||||||
 | 
					            log.debug("mqtt", "MQTT connected");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                log.debug("mqtt", "MQTT subscribe topic");
 | 
				
			||||||
 | 
					                client.subscribe(topic);
 | 
				
			||||||
 | 
					            } catch (e) {
 | 
				
			||||||
 | 
					                client.end();
 | 
				
			||||||
 | 
					                clearTimeout(timeoutID);
 | 
				
			||||||
 | 
					                reject(new Error("Cannot subscribe topic"));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        client.on("error", (error) => {
 | 
				
			||||||
 | 
					            client.end();
 | 
				
			||||||
 | 
					            clearTimeout(timeoutID);
 | 
				
			||||||
 | 
					            reject(error);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        client.on("message", (messageTopic, message) => {
 | 
				
			||||||
 | 
					            if (messageTopic == topic) {
 | 
				
			||||||
 | 
					                client.end();
 | 
				
			||||||
 | 
					                clearTimeout(timeoutID);
 | 
				
			||||||
 | 
					                if (message.toString() === okMessage) {
 | 
				
			||||||
 | 
					                    resolve(`Topic: ${messageTopic}; Message: ${message.toString()}`);
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    reject(new Error(`Error; Topic: ${messageTopic}; Message: ${message.toString()}`));
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
exports.dnsResolve = function (hostname, resolverServer, rrtype) {
 | 
					exports.dnsResolve = function (hostname, resolverServer, rrtype) {
 | 
				
			||||||
    const resolver = new Resolver();
 | 
					    const resolver = new Resolver();
 | 
				
			||||||
    resolver.setServers([ resolverServer ]);
 | 
					    resolver.setServers([ resolverServer ]);
 | 
				
			||||||
@@ -206,7 +264,7 @@ const parseCertificateInfo = function (info) {
 | 
				
			|||||||
    const existingList = {};
 | 
					    const existingList = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    while (link) {
 | 
					    while (link) {
 | 
				
			||||||
        log.debug("util", `[${i}] ${link.fingerprint}`);
 | 
					        log.debug("cert", `[${i}] ${link.fingerprint}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (!link.valid_from || !link.valid_to) {
 | 
					        if (!link.valid_from || !link.valid_to) {
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
@@ -221,7 +279,7 @@ const parseCertificateInfo = function (info) {
 | 
				
			|||||||
        if (link.issuerCertificate == null) {
 | 
					        if (link.issuerCertificate == null) {
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
        } else if (link.issuerCertificate.fingerprint in existingList) {
 | 
					        } else if (link.issuerCertificate.fingerprint in existingList) {
 | 
				
			||||||
            log.debug("util", `[Last] ${link.issuerCertificate.fingerprint}`);
 | 
					            log.debug("cert", `[Last] ${link.issuerCertificate.fingerprint}`);
 | 
				
			||||||
            link.issuerCertificate = null;
 | 
					            link.issuerCertificate = null;
 | 
				
			||||||
            break;
 | 
					            break;
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
@@ -242,7 +300,7 @@ exports.checkCertificate = function (res) {
 | 
				
			|||||||
    const info = res.request.res.socket.getPeerCertificate(true);
 | 
					    const info = res.request.res.socket.getPeerCertificate(true);
 | 
				
			||||||
    const valid = res.request.res.socket.authorized || false;
 | 
					    const valid = res.request.res.socket.authorized || false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    log.debug("util", "Parsing Certificate Info");
 | 
					    log.debug("cert", "Parsing Certificate Info");
 | 
				
			||||||
    const parsedInfo = parseCertificateInfo(info);
 | 
					    const parsedInfo = parseCertificateInfo(info);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return {
 | 
					    return {
 | 
				
			||||||
@@ -284,13 +342,13 @@ exports.getTotalClientInRoom = (io, roomName) => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    const sockets = io.sockets;
 | 
					    const sockets = io.sockets;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (! sockets) {
 | 
					    if (!sockets) {
 | 
				
			||||||
        return 0;
 | 
					        return 0;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const adapter = sockets.adapter;
 | 
					    const adapter = sockets.adapter;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (! adapter) {
 | 
					    if (!adapter) {
 | 
				
			||||||
        return 0;
 | 
					        return 0;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -315,7 +373,7 @@ exports.allowAllOrigin = (res) => {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
exports.checkLogin = (socket) => {
 | 
					exports.checkLogin = (socket) => {
 | 
				
			||||||
    if (! socket.userID) {
 | 
					    if (!socket.userID) {
 | 
				
			||||||
        throw new Error("You are not logged in.");
 | 
					        throw new Error("You are not logged in.");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -309,6 +309,10 @@ export default {
 | 
				
			|||||||
    "One record": "One record",
 | 
					    "One record": "One record",
 | 
				
			||||||
    steamApiKeyDescription: "For monitoring a Steam Game Server you need a Steam Web-API key. You can register your API key here: ",
 | 
					    steamApiKeyDescription: "For monitoring a Steam Game Server you need a Steam Web-API key. You can register your API key here: ",
 | 
				
			||||||
    "Current User": "Current User",
 | 
					    "Current User": "Current User",
 | 
				
			||||||
 | 
					    topic: "Topic",
 | 
				
			||||||
 | 
					    topicExplanation: "MQTT topic to monitor",
 | 
				
			||||||
 | 
					    successMessage: "Success Message",
 | 
				
			||||||
 | 
					    successMessageExplanation: "MQTT message that will be considered as success",
 | 
				
			||||||
    recent: "Recent",
 | 
					    recent: "Recent",
 | 
				
			||||||
    Done: "Done",
 | 
					    Done: "Done",
 | 
				
			||||||
    Info: "Info",
 | 
					    Info: "Info",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -32,6 +32,9 @@
 | 
				
			|||||||
                                    <option value="steam">
 | 
					                                    <option value="steam">
 | 
				
			||||||
                                        Steam Game Server
 | 
					                                        Steam Game Server
 | 
				
			||||||
                                    </option>
 | 
					                                    </option>
 | 
				
			||||||
 | 
					                                    <option value="mqtt">
 | 
				
			||||||
 | 
					                                        MQTT
 | 
				
			||||||
 | 
					                                    </option>
 | 
				
			||||||
                                </select>
 | 
					                                </select>
 | 
				
			||||||
                            </div>
 | 
					                            </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -67,15 +70,15 @@
 | 
				
			|||||||
                            </div>
 | 
					                            </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                            <!-- Hostname -->
 | 
					                            <!-- Hostname -->
 | 
				
			||||||
                            <!-- TCP Port / Ping / DNS / Steam only -->
 | 
					                            <!-- TCP Port / Ping / DNS / Steam / MQTT only -->
 | 
				
			||||||
                            <div v-if="monitor.type === 'port' || monitor.type === 'ping' || monitor.type === 'dns' || monitor.type === 'steam'" class="my-3">
 | 
					                            <div v-if="monitor.type === 'port' || monitor.type === 'ping' || monitor.type === 'dns' || monitor.type === 'steam' || monitor.type === 'mqtt'" class="my-3">
 | 
				
			||||||
                                <label for="hostname" class="form-label">{{ $t("Hostname") }}</label>
 | 
					                                <label for="hostname" class="form-label">{{ $t("Hostname") }}</label>
 | 
				
			||||||
                                <input id="hostname" v-model="monitor.hostname" type="text" class="form-control" :pattern="`${ipRegexPattern}|${hostnameRegexPattern}`" required>
 | 
					                                <input id="hostname" v-model="monitor.hostname" type="text" class="form-control" :pattern="`${ipRegexPattern}|${hostnameRegexPattern}`" required>
 | 
				
			||||||
                            </div>
 | 
					                            </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                            <!-- Port -->
 | 
					                            <!-- Port -->
 | 
				
			||||||
                            <!-- For TCP Port / Steam Type -->
 | 
					                            <!-- For TCP Port / Steam / MQTT Type -->
 | 
				
			||||||
                            <div v-if="monitor.type === 'port' || monitor.type === 'steam'" class="my-3">
 | 
					                            <div v-if="monitor.type === 'port' || monitor.type === 'steam' || monitor.type === 'mqtt'" class="my-3">
 | 
				
			||||||
                                <label for="port" class="form-label">{{ $t("Port") }}</label>
 | 
					                                <label for="port" class="form-label">{{ $t("Port") }}</label>
 | 
				
			||||||
                                <input id="port" v-model="monitor.port" type="number" class="form-control" required min="0" max="65535" step="1">
 | 
					                                <input id="port" v-model="monitor.port" type="number" class="form-control" required min="0" max="65535" step="1">
 | 
				
			||||||
                            </div>
 | 
					                            </div>
 | 
				
			||||||
@@ -115,6 +118,36 @@
 | 
				
			|||||||
                                </div>
 | 
					                                </div>
 | 
				
			||||||
                            </template>
 | 
					                            </template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            <!-- MQTT -->
 | 
				
			||||||
 | 
					                            <!-- For MQTT Type -->
 | 
				
			||||||
 | 
					                            <template v-if="monitor.type === 'mqtt'">
 | 
				
			||||||
 | 
					                                <div class="my-3">
 | 
				
			||||||
 | 
					                                    <label for="mqttUsername" class="form-label">MQTT {{ $t("Username") }}</label>
 | 
				
			||||||
 | 
					                                    <input id="mqttUsername" v-model="monitor.mqttUsername" type="text" class="form-control">
 | 
				
			||||||
 | 
					                                </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                <div class="my-3">
 | 
				
			||||||
 | 
					                                    <label for="mqttPassword" class="form-label">MQTT {{ $t("Password") }}</label>
 | 
				
			||||||
 | 
					                                    <input id="mqttPassword" v-model="monitor.mqttPassword" type="password" class="form-control">
 | 
				
			||||||
 | 
					                                </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                <div class="my-3">
 | 
				
			||||||
 | 
					                                    <label for="mqttTopic" class="form-label">MQTT {{ $t("Topic") }}</label>
 | 
				
			||||||
 | 
					                                    <input id="mqttTopic" v-model="monitor.mqttTopic" type="text" class="form-control" required>
 | 
				
			||||||
 | 
					                                    <div class="form-text">
 | 
				
			||||||
 | 
					                                        {{ $t("topicExplanation") }}
 | 
				
			||||||
 | 
					                                    </div>
 | 
				
			||||||
 | 
					                                </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                <div class="my-3">
 | 
				
			||||||
 | 
					                                    <label for="mqttSuccessMessage" class="form-label">MQTT {{ $t("successMessage") }}</label>
 | 
				
			||||||
 | 
					                                    <input id="mqttSuccessMessage" v-model="monitor.mqttSuccessMessage" type="text" class="form-control" required>
 | 
				
			||||||
 | 
					                                    <div class="form-text">
 | 
				
			||||||
 | 
					                                        {{ $t("successMessageExplanation") }}
 | 
				
			||||||
 | 
					                                    </div>
 | 
				
			||||||
 | 
					                                </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>
 | 
				
			||||||
@@ -139,7 +172,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                            <h2 v-if="monitor.type !== 'push'" class="mt-5 mb-2">{{ $t("Advanced") }}</h2>
 | 
					                            <h2 v-if="monitor.type !== 'push'" class="mt-5 mb-2">{{ $t("Advanced") }}</h2>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                            <div class="my-3 form-check">
 | 
					                            <div v-if="monitor.type === 'http' || monitor.type === 'keyword' " class="my-3 form-check">
 | 
				
			||||||
                                <input id="expiry-notification" v-model="monitor.expiryNotification" class="form-check-input" type="checkbox">
 | 
					                                <input id="expiry-notification" v-model="monitor.expiryNotification" class="form-check-input" type="checkbox">
 | 
				
			||||||
                                <label class="form-check-label" for="expiry-notification">
 | 
					                                <label class="form-check-label" for="expiry-notification">
 | 
				
			||||||
                                    {{ $t("Domain Name Expiry Notification") }}
 | 
					                                    {{ $t("Domain Name Expiry Notification") }}
 | 
				
			||||||
@@ -492,6 +525,10 @@ export default {
 | 
				
			|||||||
                    dns_resolve_type: "A",
 | 
					                    dns_resolve_type: "A",
 | 
				
			||||||
                    dns_resolve_server: "1.1.1.1",
 | 
					                    dns_resolve_server: "1.1.1.1",
 | 
				
			||||||
                    proxyId: null,
 | 
					                    proxyId: null,
 | 
				
			||||||
 | 
					                    mqttUsername: "",
 | 
				
			||||||
 | 
					                    mqttPassword: "",
 | 
				
			||||||
 | 
					                    mqttTopic: "",
 | 
				
			||||||
 | 
					                    mqttSuccessMessage: "",
 | 
				
			||||||
                };
 | 
					                };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (this.$root.proxyList && !this.monitor.proxyId) {
 | 
					                if (this.$root.proxyList && !this.monitor.proxyId) {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										32
									
								
								src/util.js
									
									
									
									
									
								
							
							
						
						
									
										32
									
								
								src/util.js
									
									
									
									
									
								
							@@ -54,7 +54,39 @@ function debug(msg) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
exports.debug = debug;
 | 
					exports.debug = debug;
 | 
				
			||||||
class Logger {
 | 
					class Logger {
 | 
				
			||||||
 | 
					    constructor() {
 | 
				
			||||||
 | 
					        /**
 | 
				
			||||||
 | 
					         * UPTIME_KUMA_HIDE_LOG=debug_monitor,info_monitor
 | 
				
			||||||
 | 
					         *
 | 
				
			||||||
 | 
					         * Example:
 | 
				
			||||||
 | 
					         *  [
 | 
				
			||||||
 | 
					         *     "debug_monitor",          // Hide all logs that level is debug and the module is monitor
 | 
				
			||||||
 | 
					         *     "info_monitor",
 | 
				
			||||||
 | 
					         *  ]
 | 
				
			||||||
 | 
					         */
 | 
				
			||||||
 | 
					        this.hideLog = {
 | 
				
			||||||
 | 
					            info: [],
 | 
				
			||||||
 | 
					            warn: [],
 | 
				
			||||||
 | 
					            error: [],
 | 
				
			||||||
 | 
					            debug: [],
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        if (typeof process !== "undefined" && process.env.UPTIME_KUMA_HIDE_LOG) {
 | 
				
			||||||
 | 
					            let list = process.env.UPTIME_KUMA_HIDE_LOG.split(",").map(v => v.toLowerCase());
 | 
				
			||||||
 | 
					            for (let pair of list) {
 | 
				
			||||||
 | 
					                // split first "_" only
 | 
				
			||||||
 | 
					                let values = pair.split(/_(.*)/s);
 | 
				
			||||||
 | 
					                if (values.length >= 2) {
 | 
				
			||||||
 | 
					                    this.hideLog[values[0]].push(values[1]);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            this.debug("server", "UPTIME_KUMA_HIDE_LOG is set");
 | 
				
			||||||
 | 
					            this.debug("server", this.hideLog);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    log(module, msg, level) {
 | 
					    log(module, msg, level) {
 | 
				
			||||||
 | 
					        if (this.hideLog[level] && this.hideLog[level].includes(module)) {
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        module = module.toUpperCase();
 | 
					        module = module.toUpperCase();
 | 
				
			||||||
        level = level.toUpperCase();
 | 
					        level = level.toUpperCase();
 | 
				
			||||||
        const now = new Date().toISOString();
 | 
					        const now = new Date().toISOString();
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										39
									
								
								src/util.ts
									
									
									
									
									
								
							
							
						
						
									
										39
									
								
								src/util.ts
									
									
									
									
									
								
							@@ -59,7 +59,46 @@ export function debug(msg: any) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Logger {
 | 
					class Logger {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * UPTIME_KUMA_HIDE_LOG=debug_monitor,info_monitor
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * Example:
 | 
				
			||||||
 | 
					     *  [
 | 
				
			||||||
 | 
					     *     "debug_monitor",          // Hide all logs that level is debug and the module is monitor
 | 
				
			||||||
 | 
					     *     "info_monitor",
 | 
				
			||||||
 | 
					     *  ]
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    hideLog : any = {
 | 
				
			||||||
 | 
					        info: [],
 | 
				
			||||||
 | 
					        warn: [],
 | 
				
			||||||
 | 
					        error: [],
 | 
				
			||||||
 | 
					        debug: [],
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    constructor() {
 | 
				
			||||||
 | 
					        if (typeof process !== "undefined" && process.env.UPTIME_KUMA_HIDE_LOG) {
 | 
				
			||||||
 | 
					            let list = process.env.UPTIME_KUMA_HIDE_LOG.split(",").map(v => v.toLowerCase());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for (let pair of list) {
 | 
				
			||||||
 | 
					                // split first "_" only
 | 
				
			||||||
 | 
					                let values = pair.split(/_(.*)/s);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (values.length >= 2) {
 | 
				
			||||||
 | 
					                    this.hideLog[values[0]].push(values[1]);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            this.debug("server", "UPTIME_KUMA_HIDE_LOG is set");
 | 
				
			||||||
 | 
					            this.debug("server", this.hideLog);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    log(module: string, msg: any, level: string) {
 | 
					    log(module: string, msg: any, level: string) {
 | 
				
			||||||
 | 
					        if (this.hideLog[level] && this.hideLog[level].includes(module)) {
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        module = module.toUpperCase();
 | 
					        module = module.toUpperCase();
 | 
				
			||||||
        level = level.toUpperCase();
 | 
					        level = level.toUpperCase();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user