From 93ac212aef23f8bdb8a64968ecb6a6ca5f704407 Mon Sep 17 00:00:00 2001 From: Humberto Evans Date: Mon, 12 Feb 2024 15:58:54 -0800 Subject: [PATCH 01/17] Add Heii On-Call --- server/notification-providers/heii-oncall.js | 68 +++++++++++ server/notification.js | 40 ++++--- src/components/NotificationDialog.vue | 118 +++++++++++++++---- src/components/notifications/HeiiOnCall.vue | 35 ++++++ src/components/notifications/index.js | 108 ++++++++--------- src/lang/en.json | 3 +- 6 files changed, 283 insertions(+), 89 deletions(-) create mode 100644 server/notification-providers/heii-oncall.js create mode 100644 src/components/notifications/HeiiOnCall.vue diff --git a/server/notification-providers/heii-oncall.js b/server/notification-providers/heii-oncall.js new file mode 100644 index 000000000..7babd907e --- /dev/null +++ b/server/notification-providers/heii-oncall.js @@ -0,0 +1,68 @@ +const { UP, DOWN, getMonitorRelativeURL } = require("../../src/util"); +const { setting } = require("../util-server"); + +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); + +const heiiOnCallBaseUrl = "http://192.168.2.1:3005"; + +class HeiiOnCall extends NotificationProvider { + name = "HeiiOnCall"; + + /** + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + // Payload to Heii On-Call is the entire heartbat JSON + const payload = heartbeatJSON ? heartbeatJSON : {}; + + if (!heartbeatJSON) { + // Test button was clicked on Notification Setup, trigger the alert as a test + payload["message"] = "Testing UptimeKuma Trigger"; + return this.postNotification(notification, "alert", payload); + } + + // If we can add url back to mintor to payload + const baseURL = await setting("primaryBaseURL"); + if (baseURL && monitorJSON) { + payload["url"] = baseURL + getMonitorRelativeURL(monitorJSON.id); + } + + if (heartbeatJSON.status === DOWN) { + // Monitor is DOWN, alert on Heii On-Call + return this.postNotification(notification, "alert", payload); + } + + if (heartbeatJSON.status === UP) { + // Monitor is UP, resolve on Heii On-Call + return this.postNotification(notification, "resolve", payload); + } + } + + /** + * Post to Heii On-Call + * @param {BeanModel} notification Message title + * @param {string} action Trigger Action (alert, resovle) + * @param {object} payload Data for Heii On-Call + * @returns {Promise} Success message + */ + async postNotification(notification, action, payload) { + const config = { + headers: { + Accept: "application/json", + "Content-Type": "application/json", + Authorization: "Bearer " + notification.heiiOnCallApiKey, + }, + }; + + // Post to Heii On-Call Trigger https://heiioncall.com/docs#manual-triggers + await axios.post( + `${heiiOnCallBaseUrl}/triggers/${notification.heiiOnCallTriggerId}/${action}`, + payload, + config + ); + return "Sent Successfully."; + } +} + +module.exports = HeiiOnCall; diff --git a/server/notification.js b/server/notification.js index 5e76d6eb1..722cf90c2 100644 --- a/server/notification.js +++ b/server/notification.js @@ -16,6 +16,7 @@ const Gorush = require("./notification-providers/gorush"); const Gotify = require("./notification-providers/gotify"); const GrafanaOncall = require("./notification-providers/grafana-oncall"); const HomeAssistant = require("./notification-providers/home-assistant"); +const HeiiOnCall = require("./notification-providers/heii-oncall"); const Kook = require("./notification-providers/kook"); const Line = require("./notification-providers/line"); const LineNotify = require("./notification-providers/linenotify"); @@ -56,7 +57,6 @@ const ServerChan = require("./notification-providers/serverchan"); const ZohoCliq = require("./notification-providers/zoho-cliq"); class Notification { - providerList = {}; /** @@ -87,6 +87,7 @@ class Notification { new Gotify(), new GrafanaOncall(), new HomeAssistant(), + new HeiiOnCall(), new Kook(), new Line(), new LineNotify(), @@ -124,10 +125,10 @@ class Notification { new Webhook(), new WeCom(), new GoAlert(), - new ZohoCliq() + new ZohoCliq(), ]; for (let item of list) { - if (! item.name) { + if (!item.name) { throw new Error("Notification provider without name"); } @@ -147,9 +148,19 @@ class Notification { * @returns {Promise} Successful msg * @throws Error with fail msg */ - static async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + static async send( + notification, + msg, + monitorJSON = null, + heartbeatJSON = null + ) { if (this.providerList[notification.type]) { - return this.providerList[notification.type].send(notification, msg, monitorJSON, heartbeatJSON); + return this.providerList[notification.type].send( + notification, + msg, + monitorJSON, + heartbeatJSON + ); } else { throw new Error("Notification type is not supported"); } @@ -171,10 +182,9 @@ class Notification { userID, ]); - if (! bean) { + if (!bean) { throw new Error("notification not found"); } - } else { bean = R.dispense("notification"); } @@ -204,7 +214,7 @@ class Notification { userID, ]); - if (! bean) { + if (!bean) { throw new Error("notification not found"); } @@ -220,7 +230,6 @@ class Notification { let exists = commandExistsSync("apprise"); return exists; } - } /** @@ -231,16 +240,17 @@ class Notification { */ async function applyNotificationEveryMonitor(notificationID, userID) { let monitors = await R.getAll("SELECT id FROM monitor WHERE user_id = ?", [ - userID + userID, ]); for (let i = 0; i < monitors.length; i++) { - let checkNotification = await R.findOne("monitor_notification", " monitor_id = ? AND notification_id = ? ", [ - monitors[i].id, - notificationID, - ]); + let checkNotification = await R.findOne( + "monitor_notification", + " monitor_id = ? AND notification_id = ? ", + [monitors[i].id, notificationID] + ); - if (! checkNotification) { + if (!checkNotification) { let relation = R.dispense("monitor_notification"); relation.monitor_id = monitors[i].id; relation.notification_id = notificationID; diff --git a/src/components/NotificationDialog.vue b/src/components/NotificationDialog.vue index 57a2fdf2d..4a16fdf3e 100644 --- a/src/components/NotificationDialog.vue +++ b/src/components/NotificationDialog.vue @@ -1,62 +1,132 @@ @@ -121,6 +197,7 @@ export default { "gotify": "Gotify", "GrafanaOncall": "Grafana Oncall", "HomeAssistant": "Home Assistant", + "HeiiOnCall": "Heii On-Call", "Kook": "Kook", "line": "LINE Messenger", "LineNotify": "LINE Notify", @@ -330,7 +407,8 @@ export default { @import "../assets/vars.scss"; .dark { - .modal-dialog .form-text, .modal-dialog p { + .modal-dialog .form-text, + .modal-dialog p { color: $dark-font-color; } } diff --git a/src/components/notifications/HeiiOnCall.vue b/src/components/notifications/HeiiOnCall.vue new file mode 100644 index 000000000..f773dbaef --- /dev/null +++ b/src/components/notifications/HeiiOnCall.vue @@ -0,0 +1,35 @@ + + + diff --git a/src/components/notifications/index.js b/src/components/notifications/index.js index 0606d41af..c71736a79 100644 --- a/src/components/notifications/index.js +++ b/src/components/notifications/index.js @@ -14,6 +14,7 @@ import Gorush from "./Gorush.vue"; import Gotify from "./Gotify.vue"; import GrafanaOncall from "./GrafanaOncall.vue"; import HomeAssistant from "./HomeAssistant.vue"; +import HeiiOnCall from "./HeiiOnCall.vue"; import Kook from "./Kook.vue"; import Line from "./Line.vue"; import LineNotify from "./LineNotify.vue"; @@ -58,60 +59,61 @@ import Splunk from "./Splunk.vue"; * @type { Record } */ const NotificationFormList = { - "alerta": Alerta, - "AlertNow": AlertNow, - "AliyunSMS": AliyunSMS, - "apprise": Apprise, - "Bark": Bark, - "clicksendsms": ClickSendSMS, - "smsc": SMSC, - "DingDing": DingDing, - "discord": Discord, - "Feishu": Feishu, - "FreeMobile": FreeMobile, - "GoogleChat": GoogleChat, - "gorush": Gorush, - "gotify": Gotify, - "GrafanaOncall": GrafanaOncall, - "HomeAssistant": HomeAssistant, - "Kook": Kook, - "line": Line, - "LineNotify": LineNotify, - "lunasea": LunaSea, - "matrix": Matrix, - "mattermost": Mattermost, - "nostr": Nostr, - "ntfy": Ntfy, - "octopush": Octopush, - "OneBot": OneBot, - "Opsgenie": Opsgenie, - "PagerDuty": PagerDuty, - "FlashDuty": FlashDuty, - "PagerTree": PagerTree, - "promosms": PromoSMS, - "pushbullet": Pushbullet, - "PushByTechulus": TechulusPush, - "PushDeer": PushDeer, - "pushover": Pushover, - "pushy": Pushy, + alerta: Alerta, + AlertNow: AlertNow, + AliyunSMS: AliyunSMS, + apprise: Apprise, + Bark: Bark, + clicksendsms: ClickSendSMS, + smsc: SMSC, + DingDing: DingDing, + discord: Discord, + Feishu: Feishu, + FreeMobile: FreeMobile, + GoogleChat: GoogleChat, + gorush: Gorush, + gotify: Gotify, + GrafanaOncall: GrafanaOncall, + HomeAssistant: HomeAssistant, + HeiiOnCall: HeiiOnCall, + Kook: Kook, + line: Line, + LineNotify: LineNotify, + lunasea: LunaSea, + matrix: Matrix, + mattermost: Mattermost, + nostr: Nostr, + ntfy: Ntfy, + octopush: Octopush, + OneBot: OneBot, + Opsgenie: Opsgenie, + PagerDuty: PagerDuty, + FlashDuty: FlashDuty, + PagerTree: PagerTree, + promosms: PromoSMS, + pushbullet: Pushbullet, + PushByTechulus: TechulusPush, + PushDeer: PushDeer, + pushover: Pushover, + pushy: Pushy, "rocket.chat": RocketChat, - "serwersms": SerwerSMS, - "signal": Signal, - "SMSManager": SMSManager, - "slack": Slack, - "squadcast": Squadcast, - "SMSEagle": SMSEagle, - "smtp": STMP, - "stackfield": Stackfield, - "teams": Teams, - "telegram": Telegram, - "twilio": Twilio, - "Splunk": Splunk, - "webhook": Webhook, - "WeCom": WeCom, - "GoAlert": GoAlert, - "ServerChan": ServerChan, - "ZohoCliq": ZohoCliq + serwersms: SerwerSMS, + signal: Signal, + SMSManager: SMSManager, + slack: Slack, + squadcast: Squadcast, + SMSEagle: SMSEagle, + smtp: STMP, + stackfield: Stackfield, + teams: Teams, + telegram: Telegram, + twilio: Twilio, + Splunk: Splunk, + webhook: Webhook, + WeCom: WeCom, + GoAlert: GoAlert, + ServerChan: ServerChan, + ZohoCliq: ZohoCliq, }; export default NotificationFormList; diff --git a/src/lang/en.json b/src/lang/en.json index 0f59e62ae..c81403d06 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -883,5 +883,6 @@ "deleteRemoteBrowserMessage": "Are you sure want to delete this Remote Browser for all monitors?", "GrafanaOncallUrl": "Grafana Oncall URL", "Browser Screenshot": "Browser Screenshot", - "What is a Remote Browser?": "What is a Remote Browser?" + "What is a Remote Browser?": "What is a Remote Browser?", + "Trigger ID": "Trigger ID" } From e8fada938675d5b7655291492d0d22382365737b Mon Sep 17 00:00:00 2001 From: Humberto Evans Date: Mon, 12 Feb 2024 16:14:41 -0800 Subject: [PATCH 02/17] set url to heii on call prod --- server/notification-providers/heii-oncall.js | 23 ++++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/server/notification-providers/heii-oncall.js b/server/notification-providers/heii-oncall.js index 7babd907e..6332fee85 100644 --- a/server/notification-providers/heii-oncall.js +++ b/server/notification-providers/heii-oncall.js @@ -4,7 +4,7 @@ const { setting } = require("../util-server"); const NotificationProvider = require("./notification-provider"); const axios = require("axios"); -const heiiOnCallBaseUrl = "http://192.168.2.1:3005"; +const heiiOnCallBaseUrl = "https://heiioncall.com"; class HeiiOnCall extends NotificationProvider { name = "HeiiOnCall"; @@ -22,7 +22,7 @@ class HeiiOnCall extends NotificationProvider { return this.postNotification(notification, "alert", payload); } - // If we can add url back to mintor to payload + // If we can, add url back to mintor to payload const baseURL = await setting("primaryBaseURL"); if (baseURL && monitorJSON) { payload["url"] = baseURL + getMonitorRelativeURL(monitorJSON.id); @@ -42,7 +42,7 @@ class HeiiOnCall extends NotificationProvider { /** * Post to Heii On-Call * @param {BeanModel} notification Message title - * @param {string} action Trigger Action (alert, resovle) + * @param {string} action Trigger action (alert, resovle) * @param {object} payload Data for Heii On-Call * @returns {Promise} Success message */ @@ -56,12 +56,17 @@ class HeiiOnCall extends NotificationProvider { }; // Post to Heii On-Call Trigger https://heiioncall.com/docs#manual-triggers - await axios.post( - `${heiiOnCallBaseUrl}/triggers/${notification.heiiOnCallTriggerId}/${action}`, - payload, - config - ); - return "Sent Successfully."; + try { + await axios.post( + `${heiiOnCallBaseUrl}/triggers/${notification.heiiOnCallTriggerId}/${action}`, + payload, + config + ); + } catch (error) { + this.throwGeneralAxiosError(error); + } + + return "Heii On-Call post sent successfully."; } } From 80bdc455da6cd255ee95d5714ae803dab3bec8a2 Mon Sep 17 00:00:00 2001 From: Humberto Evans Date: Mon, 12 Feb 2024 16:31:58 -0800 Subject: [PATCH 03/17] lint --- server/notification.js | 2 +- src/components/notifications/HeiiOnCall.vue | 12 ++++-------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/server/notification.js b/server/notification.js index 722cf90c2..71310b000 100644 --- a/server/notification.js +++ b/server/notification.js @@ -247,7 +247,7 @@ async function applyNotificationEveryMonitor(notificationID, userID) { let checkNotification = await R.findOne( "monitor_notification", " monitor_id = ? AND notification_id = ? ", - [monitors[i].id, notificationID] + [ monitors[i].id, notificationID ] ); if (!checkNotification) { diff --git a/src/components/notifications/HeiiOnCall.vue b/src/components/notifications/HeiiOnCall.vue index f773dbaef..64c0a9e09 100644 --- a/src/components/notifications/HeiiOnCall.vue +++ b/src/components/notifications/HeiiOnCall.vue @@ -1,9 +1,7 @@