mirror of
https://github.com/louislam/uptime-kuma.git
synced 2025-05-20 14:02:34 +02:00
feat: filter which event (UP/DOWN/CERT-EXPIRY) triggers alerts
This commit is contained in:
parent
cd6dc144a7
commit
3e54c3ef8c
6 changed files with 119 additions and 3 deletions
|
@ -0,0 +1,25 @@
|
||||||
|
exports.up = async function (knex) {
|
||||||
|
await knex.schema.alterTable("notification", function (table) {
|
||||||
|
table.text("trigger").notNullable().defaultTo("up,down,certificate");
|
||||||
|
});
|
||||||
|
|
||||||
|
await knex("notification").whereNull("trigger").update({
|
||||||
|
trigger: "up,down,certificate",
|
||||||
|
});
|
||||||
|
|
||||||
|
const notifications = await knex("notification").select("*");
|
||||||
|
for (let n of notifications) {
|
||||||
|
await knex("notification").where("id", n.id).update({
|
||||||
|
config: JSON.stringify({
|
||||||
|
...JSON.parse(n.config),
|
||||||
|
trigger: "up,down,certificate",
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.down = function (knex) {
|
||||||
|
return knex.schema.alterTable("notification", function (table) {
|
||||||
|
table.dropColumn("trigger");
|
||||||
|
});
|
||||||
|
};
|
|
@ -1292,6 +1292,7 @@ class Monitor extends BeanModel {
|
||||||
|
|
||||||
for (let notification of notificationList) {
|
for (let notification of notificationList) {
|
||||||
try {
|
try {
|
||||||
|
const triggers = notification.trigger.split(",");
|
||||||
const heartbeatJSON = bean.toJSON();
|
const heartbeatJSON = bean.toJSON();
|
||||||
const monitorData = [{ id: monitor.id,
|
const monitorData = [{ id: monitor.id,
|
||||||
active: monitor.active,
|
active: monitor.active,
|
||||||
|
@ -1308,7 +1309,9 @@ class Monitor extends BeanModel {
|
||||||
heartbeatJSON["timezoneOffset"] = UptimeKumaServer.getInstance().getTimezoneOffset();
|
heartbeatJSON["timezoneOffset"] = UptimeKumaServer.getInstance().getTimezoneOffset();
|
||||||
heartbeatJSON["localDateTime"] = dayjs.utc(heartbeatJSON["time"]).tz(heartbeatJSON["timezone"]).format(SQL_DATETIME_FORMAT);
|
heartbeatJSON["localDateTime"] = dayjs.utc(heartbeatJSON["time"]).tz(heartbeatJSON["timezone"]).format(SQL_DATETIME_FORMAT);
|
||||||
|
|
||||||
|
if ((bean.status === UP && triggers.includes("up")) || (bean.status === DOWN && triggers.includes("down"))) {
|
||||||
await Notification.send(JSON.parse(notification.config), msg, monitor.toJSON(preloadData, false), heartbeatJSON);
|
await Notification.send(JSON.parse(notification.config), msg, monitor.toJSON(preloadData, false), heartbeatJSON);
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log.error("monitor", "Cannot send notification to " + notification.name);
|
log.error("monitor", "Cannot send notification to " + notification.name);
|
||||||
log.error("monitor", e);
|
log.error("monitor", e);
|
||||||
|
@ -1400,6 +1403,11 @@ class Monitor extends BeanModel {
|
||||||
log.debug("monitor", "Send certificate notification");
|
log.debug("monitor", "Send certificate notification");
|
||||||
|
|
||||||
for (let notification of notificationList) {
|
for (let notification of notificationList) {
|
||||||
|
const triggers = notification.trigger.split(",");
|
||||||
|
if (!triggers.includes("certificate")) {
|
||||||
|
log.debug("monitor", "Notification does not trigger on certificate");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
log.debug("monitor", "Sending to " + notification.name);
|
log.debug("monitor", "Sending to " + notification.name);
|
||||||
await Notification.send(JSON.parse(notification.config), `[${this.name}][${this.url}] ${certType} certificate ${certCN} will expire in ${daysRemaining} days`);
|
await Notification.send(JSON.parse(notification.config), `[${this.name}][${this.url}] ${certType} certificate ${certCN} will expire in ${daysRemaining} days`);
|
||||||
|
|
|
@ -229,6 +229,7 @@ class Notification {
|
||||||
bean.user_id = userID;
|
bean.user_id = userID;
|
||||||
bean.config = JSON.stringify(notification);
|
bean.config = JSON.stringify(notification);
|
||||||
bean.is_default = notification.isDefault || false;
|
bean.is_default = notification.isDefault || false;
|
||||||
|
bean.trigger = notification.trigger;
|
||||||
await R.store(bean);
|
await R.store(bean);
|
||||||
|
|
||||||
if (notification.applyExisting) {
|
if (notification.applyExisting) {
|
||||||
|
|
|
@ -39,6 +39,56 @@
|
||||||
{{ $t("enableDefaultNotificationDescription") }}
|
{{ $t("enableDefaultNotificationDescription") }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
<MonitorListFilterDropdown :filter-active="false" style="margin-left: -5px;">
|
||||||
|
<template #status>
|
||||||
|
<span>{{ notification.trigger?.split(',')
|
||||||
|
?.map(tt => $t(String(tt).charAt(0).toUpperCase() + String(tt).slice(1)))
|
||||||
|
?.join(", ") || $t("Default") }}</span>
|
||||||
|
</template>
|
||||||
|
<template #dropdown>
|
||||||
|
<li>
|
||||||
|
<div class="dropdown-item" tabindex="0" @click.stop="updateTriggers('up')">
|
||||||
|
<div class="d-flex align-items-center justify-content-between">
|
||||||
|
<span>{{ $t("UpTrigger") }}</span>
|
||||||
|
<span class="ps-3">
|
||||||
|
<span v-if="notification.trigger?.split(',').includes('up')" class="px-1 filter-active">
|
||||||
|
<font-awesome-icon icon="check" />
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<div class="dropdown-item" tabindex="0" @click.stop="updateTriggers('down')">
|
||||||
|
<div class="d-flex align-items-center justify-content-between">
|
||||||
|
<span>{{ $t("DownTrigger") }}</span>
|
||||||
|
<span class="ps-3">
|
||||||
|
<span v-if="notification.trigger?.split(',').includes('down')" class="px-1 filter-active">
|
||||||
|
<font-awesome-icon icon="check" />
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<div class="dropdown-item" tabindex="0" @click.stop="updateTriggers('certificate')">
|
||||||
|
<div class="d-flex align-items-center justify-content-between">
|
||||||
|
<span>{{ $t("CertificateTrigger") }}</span>
|
||||||
|
<span class="ps-3">
|
||||||
|
<span v-if="notification.trigger?.split(',').includes('certificate')" class="px-1 filter-active">
|
||||||
|
<font-awesome-icon icon="check" />
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</template>
|
||||||
|
</MonitorListFilterDropdown>
|
||||||
|
|
||||||
|
<div class="form-text">
|
||||||
|
{{ $t("NotificationTriggerDescription") }}
|
||||||
|
</div>
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
<div class="form-check form-switch">
|
<div class="form-check form-switch">
|
||||||
|
@ -75,10 +125,12 @@ import { Modal } from "bootstrap";
|
||||||
|
|
||||||
import Confirm from "./Confirm.vue";
|
import Confirm from "./Confirm.vue";
|
||||||
import NotificationFormList from "./notifications";
|
import NotificationFormList from "./notifications";
|
||||||
|
import MonitorListFilterDropdown from "./MonitorListFilterDropdown.vue";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
Confirm,
|
Confirm,
|
||||||
|
MonitorListFilterDropdown,
|
||||||
},
|
},
|
||||||
props: {},
|
props: {},
|
||||||
emits: [ "added" ],
|
emits: [ "added" ],
|
||||||
|
@ -275,6 +327,7 @@ export default {
|
||||||
name: "",
|
name: "",
|
||||||
type: "telegram",
|
type: "telegram",
|
||||||
isDefault: false,
|
isDefault: false,
|
||||||
|
trigger: "up,down,certificate",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -287,6 +340,9 @@ export default {
|
||||||
*/
|
*/
|
||||||
submit() {
|
submit() {
|
||||||
this.processing = true;
|
this.processing = true;
|
||||||
|
if (!this.notification.trigger) {
|
||||||
|
this.notification.trigger = "up,down,certificate";
|
||||||
|
}
|
||||||
this.$root.getSocket().emit("addNotification", this.notification, this.id, (res) => {
|
this.$root.getSocket().emit("addNotification", this.notification, this.id, (res) => {
|
||||||
this.$root.toastRes(res);
|
this.$root.toastRes(res);
|
||||||
this.processing = false;
|
this.processing = false;
|
||||||
|
@ -361,6 +417,21 @@ export default {
|
||||||
console.warn("Modal hide failed:", e);
|
console.warn("Modal hide failed:", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
updateTriggers(trigger) {
|
||||||
|
let triggers;
|
||||||
|
if (!this.notification.trigger) {
|
||||||
|
triggers = [];
|
||||||
|
} else {
|
||||||
|
triggers = this.notification.trigger.split(",");
|
||||||
|
}
|
||||||
|
if (triggers.includes(trigger)) {
|
||||||
|
triggers = triggers.filter(t => t !== trigger);
|
||||||
|
} else {
|
||||||
|
triggers.push(trigger);
|
||||||
|
}
|
||||||
|
this.notification.trigger = triggers.join(",");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -1097,5 +1097,11 @@
|
||||||
"Phone numbers": "Phone numbers",
|
"Phone numbers": "Phone numbers",
|
||||||
"Sender name": "Sender name",
|
"Sender name": "Sender name",
|
||||||
"smsplanetNeedToApproveName": "Needs to be approved in the client panel",
|
"smsplanetNeedToApproveName": "Needs to be approved in the client panel",
|
||||||
"Disable URL in Notification": "Disable URL in Notification"
|
"Disable URL in Notification": "Disable URL in Notification",
|
||||||
|
"UpTrigger": "Notify on up status",
|
||||||
|
"DownTrigger": "Notify on down status",
|
||||||
|
"Certificate": "Certificate",
|
||||||
|
"CertificateTrigger": "Notify on certificate expiration",
|
||||||
|
"NotificationTriggerDescription": "Customise which alerts trigger this notification provider",
|
||||||
|
"MonitorNotification": "Notification will be sent on {0} status changes"
|
||||||
}
|
}
|
||||||
|
|
|
@ -733,7 +733,12 @@
|
||||||
{{ $t("Not available, please setup.") }}
|
{{ $t("Not available, please setup.") }}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div v-for="notification in $root.notificationList" :key="notification.id" class="form-check form-switch my-3">
|
<div
|
||||||
|
v-for="notification in $root.notificationList" :key="notification.id" class="form-check form-switch my-3"
|
||||||
|
:title="`${$t('MonitorNotification', [notification.trigger.split(',')
|
||||||
|
.map(tt => $t(String(tt).charAt(0).toUpperCase() + String(tt).slice(1)))
|
||||||
|
.join(', ')])}`"
|
||||||
|
>
|
||||||
<input :id=" 'notification' + notification.id" v-model="monitor.notificationIDList[notification.id]" class="form-check-input" type="checkbox">
|
<input :id=" 'notification' + notification.id" v-model="monitor.notificationIDList[notification.id]" class="form-check-input" type="checkbox">
|
||||||
|
|
||||||
<label class="form-check-label" :for=" 'notification' + notification.id">
|
<label class="form-check-label" :for=" 'notification' + notification.id">
|
||||||
|
|
Loading…
Add table
Reference in a new issue