diff --git a/server/notification-providers/flashduty.js b/server/notification-providers/flashduty.js new file mode 100644 index 000000000..0d6f69e59 --- /dev/null +++ b/server/notification-providers/flashduty.js @@ -0,0 +1,98 @@ +const NotificationProvider = require("./notification-provider"); +const axios = require("axios"); +const { UP, DOWN, getMonitorRelativeURL } = require("../../src/util"); +const { setting } = require("../util-server"); +const successMessage = "Sent Successfully."; + +class FlashDuty extends NotificationProvider { + name = "FlashDuty"; + + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + try { + if (heartbeatJSON == null) { + const title = "Uptime Kuma Alert"; + const monitor = { + type: "ping", + url: msg, + name: "https://flashcat.cloud" + }; + return this.postNotification(notification, title, msg, monitor); + } + + if (heartbeatJSON.status === UP) { + const title = "Uptime Kuma Monitor ✅ Up"; + + return this.postNotification(notification, title, heartbeatJSON.msg, monitorJSON, "Ok"); + } + + if (heartbeatJSON.status === DOWN) { + const title = "Uptime Kuma Monitor 🔴 Down"; + return this.postNotification(notification, title, heartbeatJSON.msg, monitorJSON, notification.flashdutySeverity); + } + } catch (error) { + this.throwGeneralAxiosError(error); + } + } + /** + * Generate a monitor url from the monitors infomation + * @param {Object} monitorInfo Monitor details + * @returns {string|undefined} + */ + + genMonitorUrl(monitorInfo) { + if (monitorInfo.type === "port" && monitorInfo.port) { + return monitorInfo.hostname + ":" + monitorInfo.port; + } + if (monitorInfo.hostname != null) { + return monitorInfo.hostname; + } + return monitorInfo.url; + } + + /** + * Send the message + * @param {BeanModel} notification Message title + * @param {string} title Message + * @param {string} body Message + * @param {Object} monitorInfo Monitor details + * @param {string} eventStatus Monitor status (Info, Warning, Critical, Ok) + * @returns {string} + */ + async postNotification(notification, title, body, monitorInfo, eventStatus) { + const options = { + method: "POST", + url: "https://api.flashcat.cloud/event/push/alert/standard?integration_key=" + notification.flashdutyIntegrationKey, + headers: { "Content-Type": "application/json" }, + data: { + description: `[${title}] [${monitorInfo.name}] ${body}`, + title, + event_status: eventStatus || "Info", + alert_key: String(monitorInfo.id) || Math.random().toString(36).substring(7), + labels: monitorInfo?.tags?.reduce((acc, item) => ({ ...acc, + [item.name]: item.value + }), { resource: this.genMonitorUrl(monitorInfo) }), + } + }; + + const baseURL = await setting("primaryBaseURL"); + if (baseURL && monitorInfo) { + options.client = "Uptime Kuma"; + options.client_url = baseURL + getMonitorRelativeURL(monitorInfo.id); + } + + let result = await axios.request(options); + if (result.status == null) { + throw new Error("FlashDuty notification failed with invalid response!"); + } + if (result.status < 200 || result.status >= 300) { + throw new Error("FlashDuty notification failed with status code " + result.status); + } + if (result.statusText != null) { + return "FlashDuty notification succeed: " + result.statusText; + } + + return successMessage; + } +} + +module.exports = FlashDuty; diff --git a/server/notification.js b/server/notification.js index a55b092f1..570c440df 100644 --- a/server/notification.js +++ b/server/notification.js @@ -27,6 +27,7 @@ const Octopush = require("./notification-providers/octopush"); const OneBot = require("./notification-providers/onebot"); const Opsgenie = require("./notification-providers/opsgenie"); const PagerDuty = require("./notification-providers/pagerduty"); +const FlashDuty = require("./notification-providers/flashduty"); const PagerTree = require("./notification-providers/pagertree"); const PromoSMS = require("./notification-providers/promosms"); const Pushbullet = require("./notification-providers/pushbullet"); @@ -91,6 +92,7 @@ class Notification { new OneBot(), new Opsgenie(), new PagerDuty(), + new FlashDuty(), new PagerTree(), new PromoSMS(), new Pushbullet(), @@ -117,7 +119,6 @@ class Notification { new GoAlert(), new ZohoCliq() ]; - for (let item of list) { if (! item.name) { throw new Error("Notification provider without name"); diff --git a/src/components/NotificationDialog.vue b/src/components/NotificationDialog.vue index 540d2dbc8..f7fce0d31 100644 --- a/src/components/NotificationDialog.vue +++ b/src/components/NotificationDialog.vue @@ -158,6 +158,7 @@ export default { "AliyunSMS": "AliyunSMS (阿里云短信服务)", "DingDing": "DingDing (钉钉自定义机器人)", "Feishu": "Feishu (飞书)", + "FlashDuty": "FlashDuty (快猫星云)", "FreeMobile": "FreeMobile (mobile.free.fr)", "PushDeer": "PushDeer", "promosms": "PromoSMS", diff --git a/src/components/notifications/FlashDuty.vue b/src/components/notifications/FlashDuty.vue new file mode 100644 index 000000000..a66ada013 --- /dev/null +++ b/src/components/notifications/FlashDuty.vue @@ -0,0 +1,29 @@ + + + diff --git a/src/components/notifications/index.js b/src/components/notifications/index.js index 0d4f28ba5..05b82a103 100644 --- a/src/components/notifications/index.js +++ b/src/components/notifications/index.js @@ -25,6 +25,7 @@ import Octopush from "./Octopush.vue"; import OneBot from "./OneBot.vue"; import Opsgenie from "./Opsgenie.vue"; import PagerDuty from "./PagerDuty.vue"; +import FlashDuty from "./FlashDuty.vue"; import PagerTree from "./PagerTree.vue"; import PromoSMS from "./PromoSMS.vue"; import Pushbullet from "./Pushbullet.vue"; @@ -84,6 +85,7 @@ const NotificationFormList = { "OneBot": OneBot, "Opsgenie": Opsgenie, "PagerDuty": PagerDuty, + "FlashDuty": FlashDuty, "PagerTree": PagerTree, "promosms": PromoSMS, "pushbullet": Pushbullet, diff --git a/src/lang/en.json b/src/lang/en.json index 910cd3c0f..f6a6e924b 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -791,6 +791,8 @@ "noGroupMonitorMsg": "Not Available. Create a Group Monitor First.", "Close": "Close", "Request Body": "Request Body", + "wayToGetFlashDutyKey":"You can go to Channel -> (Select a Channel) -> Integrations -> Add a new integration' page, add a 'Custom Event' to get a push address, copy the Integration Key in the address. For more information, please visit", + "FlashDuty Severity":"Severity", "nostrRelays": "Nostr relays", "nostrRelaysHelp": "One relay URL per line", "nostrSender": "Sender Private Key (nsec)", diff --git a/src/lang/yue.json b/src/lang/yue.json index f2a5c4760..991c9b7b7 100644 --- a/src/lang/yue.json +++ b/src/lang/yue.json @@ -98,5 +98,7 @@ "Heartbeat Interval": "檢查間距", "Add New Monitor": "新增監測器", "Quick Stats": "綜合數據", - "markdownSupported": "可以用 Markdown" + "markdownSupported": "可以用 Markdown", + "wayToGetFlashDutyKey": "您可以进入 协作空间 -> (选择一个 协作空间) -> 集成数据 -> 新增一个集成 页面,添加“自定义事件”获得一个推送地址,复制地址中的 Integration Key,更多信息前往{0}", + "FlashDuty Severity":"严重程度" } diff --git a/src/lang/zh-CN.json b/src/lang/zh-CN.json index c395187b9..3b8a135e3 100644 --- a/src/lang/zh-CN.json +++ b/src/lang/zh-CN.json @@ -413,6 +413,7 @@ "smtpDkimheaderFieldNames": "包含在哈希计算对象内的 Header 列表(可选)", "smtpDkimskipFields": "不包含在哈希计算对象内的 Header 列表(可选)", "wayToGetPagerDutyKey": "您可以在 Service -> Service Directory -> (选择一个 Service) -> Integrations -> Add integration 页面中搜索“Events API V2”以获取此 Integration Key,更多信息请看{0}", + "wayToGetFlashDutyKey": "您可以进入 协作空间 -> (选择一个 协作空间) -> 集成数据 -> 新增一个集成 页面,添加“自定义事件”获得一个推送地址,复制地址中的 Integration Key,更多信息前往{0}", "Integration Key": "集成密钥", "Integration URL": "集成网址", "Auto resolve or acknowledged": "自动标记为已解决或已读", @@ -784,5 +785,6 @@ "Edit Maintenance": "编辑维护计划", "Home": "首页", "noGroupMonitorMsg": "暂无可用,请先创建一个监控项组。", - "Close": "关闭" + "Close": "关闭", + "FlashDuty Severity":"严重程度" }