mirror of
https://github.com/louislam/uptime-kuma.git
synced 2025-04-11 10:52:20 +00:00
Merge 7a94fc86cd
into 30f82b9cb4
This commit is contained in:
commit
be6d80b5bd
6 changed files with 161 additions and 2 deletions
server/model
src
components
lang
layouts
pages
|
@ -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<boolean>} 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.status === UP;
|
||||
}
|
||||
|
||||
// Default to indicative of being healthy, this shouldn't happen
|
||||
// Better to be safe if we can't find the selected monitor, it may have been deleted
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Monitor;
|
||||
|
|
80
src/components/HealthCheckAlert.vue
Normal file
80
src/components/HealthCheckAlert.vue
Normal file
|
@ -0,0 +1,80 @@
|
|||
<template>
|
||||
<div v-if="! healthCheckStatus" id="health-check" class="d-flex flex-wrap py-3 mx-4">
|
||||
<div class="alert alert-danger w-100 d-inline-flex align-items-center justify-content-between">
|
||||
<div class="px-3">Monitoring is paused, the health check monitor is down!</div>
|
||||
<div>
|
||||
<router-link :to="monitorURL(healthCheckMonitor.id)" class="btn btn-danger text-nowrap">
|
||||
View {{ healthCheckMonitor.name }}
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { UP } from "../util.ts";
|
||||
import { getMonitorRelativeURL } from "../util.ts";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
settings: {},
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
/**
|
||||
* Find the designated health check monitor from the monitor list
|
||||
* @returns {*|null} A monitor object if the health check monitor id is set
|
||||
*/
|
||||
healthCheckMonitor() {
|
||||
const healthCheckMonitorId = this.settings?.healthCheckMonitorId;
|
||||
|
||||
if (this.$root.monitorList[healthCheckMonitorId]) {
|
||||
return this.$root.monitorList[healthCheckMonitorId];
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Determines if the designated health check monitor is down
|
||||
* @returns {boolean} The health check monitor status
|
||||
*/
|
||||
healthCheckStatus() {
|
||||
const healthCheckMonitorId = this.healthCheckMonitor?.id;
|
||||
|
||||
if (healthCheckMonitorId in this.$root.lastHeartbeatList && this.$root.lastHeartbeatList[healthCheckMonitorId]) {
|
||||
return this.$root.lastHeartbeatList[healthCheckMonitorId].status === UP;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.loadSettings();
|
||||
},
|
||||
|
||||
methods: {
|
||||
/**
|
||||
* Load settings from server
|
||||
* @returns {void}
|
||||
*/
|
||||
loadSettings() {
|
||||
this.$root.getSocket().emit("getSettings", (res) => {
|
||||
this.settings = res.data;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Get URL of monitor
|
||||
* @param {number} id ID of monitor
|
||||
* @returns {string} Relative URL of monitor
|
||||
*/
|
||||
monitorURL(id) {
|
||||
return getMonitorRelativeURL(id);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
|
@ -53,6 +53,29 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="settingsLoaded" class="my-4 pt-4">
|
||||
<h5 class="my-4 settings-subheading">{{ $t("Heath Check") }}</h5>
|
||||
<p>{{ $t("HealthCheckDescription") }}</p>
|
||||
|
||||
<div class="my-4">
|
||||
<label for="timezone" class="form-label">
|
||||
{{ $t("Monitor") }}
|
||||
</label>
|
||||
<select id="timezone" v-model="settings.healthCheckMonitorId" class="form-select">
|
||||
<option :value="null">
|
||||
{{ $t("Select") }}
|
||||
</option>
|
||||
<option
|
||||
v-for="(monitor, index) in $root.monitorList"
|
||||
:key="index"
|
||||
:value="monitor.id"
|
||||
>
|
||||
{{ monitor.name }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="my-4 pt-4">
|
||||
<h5 class="my-4 settings-subheading">{{ $t("settingsCertificateExpiry") }}</h5>
|
||||
<p>{{ $t("certificationExpiryDescription") }}</p>
|
||||
|
@ -184,7 +207,7 @@ export default {
|
|||
this.toastErrorTimeoutSecs = parsedTimeout > 0 ? parsedTimeout / 1000 : parsedTimeout;
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -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."
|
||||
}
|
||||
|
|
|
@ -90,6 +90,7 @@
|
|||
</header>
|
||||
|
||||
<main>
|
||||
<health-check-alert v-if="$root.loggedIn" />
|
||||
<router-view v-if="$root.loggedIn" />
|
||||
<Login v-if="! $root.loggedIn && $root.allowLoginDialog" />
|
||||
</main>
|
||||
|
@ -133,11 +134,13 @@
|
|||
import Login from "../components/Login.vue";
|
||||
import compareVersions from "compare-versions";
|
||||
import { useToast } from "vue-toastification";
|
||||
import HealthCheckAlert from "../components/HealthCheckAlert.vue";
|
||||
const toast = useToast();
|
||||
|
||||
export default {
|
||||
|
||||
components: {
|
||||
HealthCheckAlert,
|
||||
Login,
|
||||
},
|
||||
|
||||
|
|
|
@ -183,6 +183,10 @@ export default {
|
|||
this.settings.trustProxy = false;
|
||||
}
|
||||
|
||||
if (this.settings.healthCheckMonitorId === undefined) {
|
||||
this.settings.healthCheckMonitorId = null;
|
||||
}
|
||||
|
||||
this.settingsLoaded = true;
|
||||
});
|
||||
},
|
||||
|
|
Loading…
Add table
Reference in a new issue