mirror of
https://github.com/louislam/uptime-kuma.git
synced 2025-06-15 17:06:48 +02:00
Added the option not to send notifications if the master monitor is pending, down or degraded.
This commit is contained in:
parent
64c8a90e8a
commit
7e2503c0dc
7 changed files with 60 additions and 17 deletions
|
@ -1,6 +1,9 @@
|
||||||
-- You should not modify if this have pushed to Github, unless it does serious wrong with the db.
|
-- You should not modify if this have pushed to Github, unless it does serious wrong with the db.
|
||||||
BEGIN TRANSACTION;
|
BEGIN TRANSACTION;
|
||||||
|
|
||||||
|
ALTER TABLE monitor
|
||||||
|
ADD no_notification_if_master_down BOOLEAN default 0 NOT NULL;
|
||||||
|
|
||||||
CREATE TABLE dependent_monitors
|
CREATE TABLE dependent_monitors
|
||||||
(
|
(
|
||||||
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
|
|
@ -72,6 +72,7 @@ class Monitor extends BeanModel {
|
||||||
keyword: this.keyword,
|
keyword: this.keyword,
|
||||||
ignoreTls: this.getIgnoreTls(),
|
ignoreTls: this.getIgnoreTls(),
|
||||||
upsideDown: this.isUpsideDown(),
|
upsideDown: this.isUpsideDown(),
|
||||||
|
noNotificationIfMasterDown: this.getNoNotificationIfMasterDown(),
|
||||||
maxredirects: this.maxredirects,
|
maxredirects: this.maxredirects,
|
||||||
accepted_statuscodes: this.getAcceptedStatuscodes(),
|
accepted_statuscodes: this.getAcceptedStatuscodes(),
|
||||||
dns_resolve_type: this.dns_resolve_type,
|
dns_resolve_type: this.dns_resolve_type,
|
||||||
|
@ -108,6 +109,14 @@ class Monitor extends BeanModel {
|
||||||
return Boolean(this.upsideDown);
|
return Boolean(this.upsideDown);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse to boolean
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
getNoNotificationIfMasterDown() {
|
||||||
|
return Boolean(this.noNotificationIfMasterDown);
|
||||||
|
}
|
||||||
|
|
||||||
getAcceptedStatuscodes() {
|
getAcceptedStatuscodes() {
|
||||||
return JSON.parse(this.accepted_statuscodes_json);
|
return JSON.parse(this.accepted_statuscodes_json);
|
||||||
}
|
}
|
||||||
|
@ -148,7 +157,10 @@ class Monitor extends BeanModel {
|
||||||
bean.duration = 0;
|
bean.duration = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let isDegraded = false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
isDegraded = await Monitor.isDegraded(this.id);
|
||||||
if (this.type === "http" || this.type === "keyword") {
|
if (this.type === "http" || this.type === "keyword") {
|
||||||
// Do not do any queries/high loading things before the "bean.ping"
|
// Do not do any queries/high loading things before the "bean.ping"
|
||||||
let startTime = dayjs().valueOf();
|
let startTime = dayjs().valueOf();
|
||||||
|
@ -363,8 +375,8 @@ class Monitor extends BeanModel {
|
||||||
|
|
||||||
retries = 0;
|
retries = 0;
|
||||||
|
|
||||||
if (bean.status === UP && await Monitor.isDegraded(this.id)) {
|
if (bean.status === UP && isDegraded) {
|
||||||
bean.msg = "Monitor is degraded, because at least one dependent monitor is DOWN";
|
bean.msg = "Monitor is degraded, because at least one master monitor is pending, down or degraded";
|
||||||
bean.status = DEGRADED;
|
bean.status = DEGRADED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -381,6 +393,12 @@ class Monitor extends BeanModel {
|
||||||
retries++;
|
retries++;
|
||||||
bean.status = PENDING;
|
bean.status = PENDING;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Do not change this text!
|
||||||
|
// Condition below and in api-router depends on it
|
||||||
|
if (isDegraded && bean.status === DOWN) {
|
||||||
|
bean.msg = "Monitor is down and degraded";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let beatInterval = this.interval;
|
let beatInterval = this.interval;
|
||||||
|
@ -393,7 +411,10 @@ class Monitor extends BeanModel {
|
||||||
if (isImportant) {
|
if (isImportant) {
|
||||||
bean.important = true;
|
bean.important = true;
|
||||||
|
|
||||||
if (Monitor.isImportantForNotification(isFirstBeat, previousBeat?.status, bean.status)) {
|
if (this.noNotificationIfMasterDown && isDegraded || previousBeat.msg === "Monitor is down and degraded") {
|
||||||
|
debug(`[${this.name}] will not sendNotification because it is/was degraded`);
|
||||||
|
}
|
||||||
|
else if (Monitor.isImportantForNotification(isFirstBeat, previousBeat?.status, bean.status)) {
|
||||||
debug(`[${this.name}] sendNotification`);
|
debug(`[${this.name}] sendNotification`);
|
||||||
await Monitor.sendNotification(isFirstBeat, this, bean);
|
await Monitor.sendNotification(isFirstBeat, this, bean);
|
||||||
}
|
}
|
||||||
|
@ -801,7 +822,7 @@ class Monitor extends BeanModel {
|
||||||
|
|
||||||
static async getPreviousHeartbeat(monitorID) {
|
static async getPreviousHeartbeat(monitorID) {
|
||||||
return await R.getRow(`
|
return await R.getRow(`
|
||||||
SELECT status, time FROM heartbeat
|
SELECT msg, status, time FROM heartbeat
|
||||||
WHERE id = (select MAX(id) from heartbeat where monitor_id = ?)
|
WHERE id = (select MAX(id) from heartbeat where monitor_id = ?)
|
||||||
`, [
|
`, [
|
||||||
monitorID
|
monitorID
|
||||||
|
@ -811,7 +832,7 @@ class Monitor extends BeanModel {
|
||||||
static async isDegraded(monitorID) {
|
static async isDegraded(monitorID) {
|
||||||
const monitors = await R.getAll(`
|
const monitors = await R.getAll(`
|
||||||
SELECT hb.id FROM heartbeat hb JOIN dependent_monitors dm on hb.monitor_id = dm.depends_on JOIN (SELECT MAX(id) AS id FROM heartbeat GROUP BY monitor_id) USING (id)
|
SELECT hb.id FROM heartbeat hb JOIN dependent_monitors dm on hb.monitor_id = dm.depends_on JOIN (SELECT MAX(id) AS id FROM heartbeat GROUP BY monitor_id) USING (id)
|
||||||
WHERE dm.monitor_id = ? AND hb.status = 0
|
WHERE dm.monitor_id = ? AND (hb.status = 0 OR hb.status = 2 OR hb.status = 4)
|
||||||
`, [
|
`, [
|
||||||
monitorID
|
monitorID
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -51,7 +51,9 @@ router.get("/api/push/:pushToken", async (request, response) => {
|
||||||
duration = dayjs(bean.time).diff(dayjs(previousHeartbeat.time), "second");
|
duration = dayjs(bean.time).diff(dayjs(previousHeartbeat.time), "second");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status === UP && await Monitor.isDegraded(monitor.id)) {
|
const isDegraded = await Monitor.isDegraded(monitor.id)
|
||||||
|
|
||||||
|
if (status === UP && isDegraded) {
|
||||||
msg = "Monitor is degraded, because at least one dependent monitor is DOWN";
|
msg = "Monitor is degraded, because at least one dependent monitor is DOWN";
|
||||||
status = DEGRADED;
|
status = DEGRADED;
|
||||||
}
|
}
|
||||||
|
@ -75,7 +77,10 @@ router.get("/api/push/:pushToken", async (request, response) => {
|
||||||
ok: true,
|
ok: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (Monitor.isImportantForNotification(isFirstBeat, previousStatus, status)) {
|
if (monitor.noNotificationIfMasterDown && isDegraded || previousHeartbeat.msg === "Monitor is down and degraded") {
|
||||||
|
debug(`[${monitor.name}] will not sendNotification because it is/was degraded`);
|
||||||
|
}
|
||||||
|
else if (Monitor.isImportantForNotification(isFirstBeat, previousStatus, status)) {
|
||||||
await Monitor.sendNotification(isFirstBeat, monitor, bean);
|
await Monitor.sendNotification(isFirstBeat, monitor, bean);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -594,6 +594,7 @@ exports.entryPage = "dashboard";
|
||||||
bean.keyword = monitor.keyword;
|
bean.keyword = monitor.keyword;
|
||||||
bean.ignoreTls = monitor.ignoreTls;
|
bean.ignoreTls = monitor.ignoreTls;
|
||||||
bean.upsideDown = monitor.upsideDown;
|
bean.upsideDown = monitor.upsideDown;
|
||||||
|
bean.noNotificationIfMasterDown = monitor.noNotificationIfMasterDown;
|
||||||
bean.maxredirects = monitor.maxredirects;
|
bean.maxredirects = monitor.maxredirects;
|
||||||
bean.accepted_statuscodes_json = JSON.stringify(monitor.accepted_statuscodes);
|
bean.accepted_statuscodes_json = JSON.stringify(monitor.accepted_statuscodes);
|
||||||
bean.dns_resolve_type = monitor.dns_resolve_type;
|
bean.dns_resolve_type = monitor.dns_resolve_type;
|
||||||
|
|
|
@ -6,8 +6,11 @@ export default {
|
||||||
ignoreTLSError: "Ignore TLS/SSL error for HTTPS websites",
|
ignoreTLSError: "Ignore TLS/SSL error for HTTPS websites",
|
||||||
upsideDownModeDescription: "Flip the status upside down. If the service is reachable, it is DOWN.",
|
upsideDownModeDescription: "Flip the status upside down. If the service is reachable, it is DOWN.",
|
||||||
maxRedirectDescription: "Maximum number of redirects to follow. Set to 0 to disable redirects.",
|
maxRedirectDescription: "Maximum number of redirects to follow. Set to 0 to disable redirects.",
|
||||||
|
sendNotificationTitle: "Do not send notification in case of master monitor failure",
|
||||||
|
sendNotificationDescription: "Do not send any notifications if this monitor is DOWN and at least one master monitor is also DOWN.",
|
||||||
Degraded: "Degraded",
|
Degraded: "Degraded",
|
||||||
"Dependent Monitors": "Dependent Monitors",
|
"Dependent Monitors": "Dependent Monitors",
|
||||||
|
monitorDependsOn: "The status of this monitor depends on",
|
||||||
"Pick Dependent Monitors...": "Pick Dependent Monitors...",
|
"Pick Dependent Monitors...": "Pick Dependent Monitors...",
|
||||||
dependentMonitorsDescription: "Select the monitor(s) on which this monitor depends. If the dependent monitor(s) fails, this monitor will be affected too.",
|
dependentMonitorsDescription: "Select the monitor(s) on which this monitor depends. If the dependent monitor(s) fails, this monitor will be affected too.",
|
||||||
acceptedStatusCodesDescription: "Select status codes which are considered as a successful response.",
|
acceptedStatusCodesDescription: "Select status codes which are considered as a successful response.",
|
||||||
|
|
|
@ -103,6 +103,14 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="shadow-box table-shadow-box">
|
||||||
|
<label for="dependent-monitors" class="form-label" style="margin-top: 20px; font-weight: bold">{{ $t("monitorDependsOn") }}:</label>
|
||||||
|
<br>
|
||||||
|
<button v-for="monitor in this.dependentMonitors" class="btn btn-monitor" style="margin: 5px; cursor: auto; color: white; font-weight: 500">
|
||||||
|
{{ monitor }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="shadow-box table-shadow-box">
|
<div class="shadow-box table-shadow-box">
|
||||||
<div class="dropdown dropdown-clear-data">
|
<div class="dropdown dropdown-clear-data">
|
||||||
<button class="btn btn-sm btn-outline-danger dropdown-toggle" type="button" data-bs-toggle="dropdown">
|
<button class="btn btn-sm btn-outline-danger dropdown-toggle" type="button" data-bs-toggle="dropdown">
|
||||||
|
@ -154,14 +162,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="shadow-box table-shadow-box">
|
|
||||||
<label for="dependent-monitors" class="form-label" style="margin-top: 20px">{{ $t("Dependent Monitors") }}</label>
|
|
||||||
<br>
|
|
||||||
<button v-for="monitor in this.dependentMonitors" class="btn btn-monitor" style="margin: 5px; cursor: auto; color: white; font-weight: bold">
|
|
||||||
{{ monitor }}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Confirm ref="confirmPause" :yes-text="$t('Yes')" :no-text="$t('No')" @yes="pauseMonitor">
|
<Confirm ref="confirmPause" :yes-text="$t('Yes')" :no-text="$t('No')" @yes="pauseMonitor">
|
||||||
{{ $t("pauseMonitorMsg") }}
|
{{ $t("pauseMonitorMsg") }}
|
||||||
</Confirm>
|
</Confirm>
|
||||||
|
|
|
@ -156,6 +156,16 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div v-if="dependentMonitors.length !== 0" class="my-3 form-check">
|
||||||
|
<input id="send-notification" v-model="monitor.noNotificationIfMasterDown" class="form-check-input" type="checkbox">
|
||||||
|
<label class="form-check-label" for="send-notification">
|
||||||
|
{{ $t("sendNotificationTitle") }}
|
||||||
|
</label>
|
||||||
|
<div class="form-text">
|
||||||
|
{{ $t("sendNotificationDescription") }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- HTTP / Keyword only -->
|
<!-- HTTP / Keyword only -->
|
||||||
<template v-if="monitor.type === 'http' || monitor.type === 'keyword' ">
|
<template v-if="monitor.type === 'http' || monitor.type === 'keyword' ">
|
||||||
<div class="my-3">
|
<div class="my-3">
|
||||||
|
@ -200,14 +210,13 @@
|
||||||
track-by="id"
|
track-by="id"
|
||||||
label="name"
|
label="name"
|
||||||
:multiple="true"
|
:multiple="true"
|
||||||
:allow-empty="false"
|
|
||||||
:close-on-select="false"
|
:close-on-select="false"
|
||||||
:clear-on-select="false"
|
:clear-on-select="false"
|
||||||
:preserve-search="true"
|
:preserve-search="true"
|
||||||
:placeholder="$t('Pick Dependent Monitors...')"
|
:placeholder="$t('Pick Dependent Monitors...')"
|
||||||
:preselect-first="false"
|
:preselect-first="false"
|
||||||
:max-height="600"
|
:max-height="600"
|
||||||
:taggable="false"
|
:taggable="true"
|
||||||
></VueMultiselect>
|
></VueMultiselect>
|
||||||
|
|
||||||
<div class="form-text">
|
<div class="form-text">
|
||||||
|
@ -476,6 +485,7 @@ export default {
|
||||||
notificationIDList: {},
|
notificationIDList: {},
|
||||||
ignoreTls: false,
|
ignoreTls: false,
|
||||||
upsideDown: false,
|
upsideDown: false,
|
||||||
|
noNotificationIfMasterDown: false,
|
||||||
maxredirects: 10,
|
maxredirects: 10,
|
||||||
accepted_statuscodes: ["200-299"],
|
accepted_statuscodes: ["200-299"],
|
||||||
dns_resolve_type: "A",
|
dns_resolve_type: "A",
|
||||||
|
|
Loading…
Add table
Reference in a new issue