diff --git a/server/model/monitor.js b/server/model/monitor.js index 5999d93e7..a9aedea84 100644 --- a/server/model/monitor.js +++ b/server/model/monitor.js @@ -7,6 +7,7 @@ const { log, UP, DOWN, PENDING, MAINTENANCE, flipStatus, MAX_INTERVAL_SECOND, MI const { tcping, ping, checkCertificate, checkStatusCode, getTotalClientInRoom, setting, mssqlQuery, postgresQuery, mysqlQuery, setSetting, httpNtlm, radius, grpcQuery, redisPingAsync, kafkaProducerAsync, getOidcTokenClientCredentials, rootCertificatesFingerprints, axiosAbortSignal } = require("../util-server"); +const { Settings } = require("../settings"); const { R } = require("redbean-node"); const { BeanModel } = require("redbean-node/dist/bean-model"); const { Notification } = require("../notification"); @@ -345,6 +346,16 @@ class Monitor extends BeanModel { } } + /** + * If the monitor designated to represent healthy connectivity is down, + * then we can just stop here. + */ + const systemIsHealthy = await this.systemIsHealthy(); + if (systemIsHealthy === false) { + log.warn("monitor", "Health check monitor is down, monitoring paused!"); + return; + } + // Expose here for prometheus update // undefined if not https let tlsInfo = undefined; @@ -1737,6 +1748,42 @@ class Monitor extends BeanModel { await this.checkCertExpiryNotifications(tlsInfo); } } + + /** + * Checks if the monitor selected for the health check is down. + * @returns {Promise} If true, the system is healthy. + */ + async systemIsHealthy() { + let healthCheckMonitorId = await Settings.get("healthCheckMonitorId"); + + // User hasn't made a selection yet, save in the database as null + if (healthCheckMonitorId === undefined) { + await setSetting("healthCheckMonitorId", null); + healthCheckMonitorId = null; + } + + // No health check monitor is specified, nothing to do! + if (healthCheckMonitorId === null) { + return true; + } + + // We still need to check the health check monitor + if (healthCheckMonitorId === this.id) { + return true; + } + + const healthCheckMonitor = await R.findOne("heartbeat", " monitor_id = ? ORDER BY time DESC", [ + healthCheckMonitorId, + ]); + + if (healthCheckMonitor) { + return healthCheckMonitor.id === UP; + } + + // Default to indicative of being healthy, this shouldn't happen + // Better to be safe if we can't find the selector monitor, it may have been deleted + return true; + } } module.exports = Monitor; diff --git a/src/components/settings/Notifications.vue b/src/components/settings/Notifications.vue index 2a65d796e..1f0c5670a 100644 --- a/src/components/settings/Notifications.vue +++ b/src/components/settings/Notifications.vue @@ -53,6 +53,29 @@ +
+
{{ $t("Heath Check") }}
+

{{ $t("HealthCheckDescription") }}

+ +
+ + +
+
+
{{ $t("settingsCertificateExpiry") }}

{{ $t("certificationExpiryDescription") }}

@@ -184,7 +207,7 @@ export default { this.toastErrorTimeoutSecs = parsedTimeout > 0 ? parsedTimeout / 1000 : parsedTimeout; } } - }, + } }, }; diff --git a/src/lang/en.json b/src/lang/en.json index cb704b0fe..25e6469f8 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -1067,5 +1067,7 @@ "YZJ Robot Token": "YZJ Robot token", "Plain Text": "Plain Text", "Message Template": "Message Template", - "Template Format": "Template Format" + "Template Format": "Template Format", + "Heath Check": "Heath Check", + "HealthCheckDescription": "If the selected monitor is offline, all notifications will be paused and downtime ignored. Ideal for monitoring connectivity to the internet." } diff --git a/src/pages/Settings.vue b/src/pages/Settings.vue index 96bb1fee1..6b492cf02 100644 --- a/src/pages/Settings.vue +++ b/src/pages/Settings.vue @@ -183,6 +183,10 @@ export default { this.settings.trustProxy = false; } + if (this.settings.healthCheckMonitorId === undefined) { + this.settings.healthCheckMonitorId = null; + } + this.settingsLoaded = true; }); },