diff --git a/server/model/monitor.js b/server/model/monitor.js index 5999d93e7..d6d968df0 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -4,7 +4,7 @@ const { Prometheus } = require("../prometheus"); const { log, UP, DOWN, PENDING, MAINTENANCE, flipStatus, MAX_INTERVAL_SECOND, MIN_INTERVAL_SECOND, SQL_DATETIME_FORMAT, evaluateJsonQuery } = require("../../src/util"); -const { tcping, ping, checkCertificate, checkStatusCode, getTotalClientInRoom, setting, mssqlQuery, postgresQuery, mysqlQuery, setSetting, httpNtlm, radius, grpcQuery, +const { ping, checkCertificate, checkStatusCode, getTotalClientInRoom, setting, mssqlQuery, postgresQuery, mysqlQuery, setSetting, httpNtlm, radius, grpcQuery, redisPingAsync, kafkaProducerAsync, getOidcTokenClientCredentials, rootCertificatesFingerprints, axiosAbortSignal } = require("../util-server"); const { R } = require("redbean-node"); @@ -611,11 +611,6 @@ class Monitor extends BeanModel { } - } else if (this.type === "port") { - bean.ping = await tcping(this.hostname, this.port); - bean.msg = ""; - bean.status = UP; - } else if (this.type === "ping") { bean.ping = await ping(this.hostname, this.packetSize); bean.msg = ""; diff --git a/server/monitor-types/tcp.js b/server/monitor-types/tcp.js new file mode 100644 index 000000000..b822e05d1 --- /dev/null +++ b/server/monitor-types/tcp.js @@ -0,0 +1,73 @@ +const { MonitorType } = require("./monitor-type"); +const { UP, DOWN } = require("../../src/util"); +const { tcping, checkCertificate } = require("../util-server"); +const tls = require("tls"); + +class TCPMonitorType extends MonitorType { + name = "port"; + + /** + * @inheritdoc + */ + async check(monitor, heartbeat, _server) { + try { + heartbeat.ping = await tcping(monitor.hostname, monitor.port); + heartbeat.msg = ""; + heartbeat.status = UP; + } catch (error) { + heartbeat.status = DOWN; + heartbeat.msg = "Connection failed"; + return; + } + + if (monitor.isEnabledExpiryNotification()) { + let socket = null; + try { + const options = { + host: monitor.hostname, + port: monitor.port, + servername: monitor.hostname, + }; + + const tlsInfoObject = await new Promise((resolve, reject) => { + socket = tls.connect(options); + + socket.on("secureConnect", () => { + try { + const info = checkCertificate(socket); + resolve(info); + } catch (error) { + reject(error); + } + }); + + socket.on("error", (error) => { + reject(error); + }); + + socket.setTimeout(10000, () => { + reject(new Error("Connection timed out")); + }); + }); + + await monitor.handleTlsInfo(tlsInfoObject); + if (!tlsInfoObject.valid) { + heartbeat.status = DOWN; + heartbeat.msg = "Certificate is invalid"; + } + } catch (error) { + heartbeat.status = DOWN; + heartbeat.msg = "Connection failed"; + } finally { + if (socket && !socket.destroyed) { + socket.end(); + } + } + } + } +} + +module.exports = { + TCPMonitorType, +}; + diff --git a/server/uptime-kuma-server.js b/server/uptime-kuma-server.js index 062f098d7..b0fe4268e 100644 --- a/server/uptime-kuma-server.js +++ b/server/uptime-kuma-server.js @@ -116,6 +116,7 @@ class UptimeKumaServer { UptimeKumaServer.monitorTypeList["snmp"] = new SNMPMonitorType(); UptimeKumaServer.monitorTypeList["mongodb"] = new MongodbMonitorType(); UptimeKumaServer.monitorTypeList["rabbitmq"] = new RabbitMqMonitorType(); + UptimeKumaServer.monitorTypeList["port"] = new TCPMonitorType(); // Allow all CORS origins (polling) in development let cors = undefined; @@ -554,4 +555,5 @@ const { MqttMonitorType } = require("./monitor-types/mqtt"); const { SNMPMonitorType } = require("./monitor-types/snmp"); const { MongodbMonitorType } = require("./monitor-types/mongodb"); const { RabbitMqMonitorType } = require("./monitor-types/rabbitmq"); +const { TCPMonitorType } = require("./monitor-types/tcp.js"); const Monitor = require("./model/monitor"); diff --git a/src/pages/EditMonitor.vue b/src/pages/EditMonitor.vue index a83f91cab..a8b481de3 100644 --- a/src/pages/EditMonitor.vue +++ b/src/pages/EditMonitor.vue @@ -612,7 +612,7 @@