From c75a418f7b7e550abad057557812ab1f68fcaefa Mon Sep 17 00:00:00 2001 From: Daan Meijer Date: Fri, 13 Oct 2023 13:00:57 +0200 Subject: [PATCH 01/27] fix for #3351: restructure alert actions, add 'visit site' button --- server/notification-providers/slack.js | 53 ++++++++++++++++---------- 1 file changed, 33 insertions(+), 20 deletions(-) diff --git a/server/notification-providers/slack.js b/server/notification-providers/slack.js index d512a7cd..f48fe8a3 100644 --- a/server/notification-providers/slack.js +++ b/server/notification-providers/slack.js @@ -49,6 +49,35 @@ class Slack extends NotificationProvider { return okMsg; } + const actions = []; + + const baseURL = await setting("primaryBaseURL"); + + if(baseURL){ + actions.push({ + "type": "button", + "text": { + "type": "plain_text", + "text": "Visit Uptime Kuma", + }, + "value": "Uptime-Kuma", + "url": baseURL + getMonitorRelativeURL(monitorJSON.id), + }); + + } + + if(monitorJSON.url) { + actions.push({ + "type": "button", + "text": { + "type": "plain_text", + "text": "Visit site", + }, + "value": "Site", + "url": monitorJSON.url, + }) + } + const textMsg = "Uptime Kuma Alert"; let data = { "text": `${textMsg}\n${msg}`, @@ -76,6 +105,10 @@ class Slack extends NotificationProvider { "type": "mrkdwn", "text": `*Time (${heartbeatJSON["timezone"]})*\n${heartbeatJSON["localDateTime"]}`, }], + }, + { + "type": "actions", + "elements": actions, } ], } @@ -86,26 +119,6 @@ class Slack extends NotificationProvider { await Slack.deprecateURL(notification.slackbutton); } - const baseURL = await setting("primaryBaseURL"); - - // Button - if (baseURL) { - data.attachments.forEach(element => { - element.blocks.push({ - "type": "actions", - "elements": [{ - "type": "button", - "text": { - "type": "plain_text", - "text": "Visit Uptime Kuma", - }, - "value": "Uptime-Kuma", - "url": baseURL + getMonitorRelativeURL(monitorJSON.id), - }], - }); - }); - } - await axios.post(notification.slackwebhookURL, data); return okMsg; } catch (error) { From 69e9fced88e8e5abba767d60891caa1b1f969977 Mon Sep 17 00:00:00 2001 From: Daan Meijer Date: Fri, 13 Oct 2023 13:53:31 +0200 Subject: [PATCH 02/27] added comments to code, restructured code to take into account not having any buttons --- server/notification-providers/slack.js | 64 ++++++++++++++++---------- 1 file changed, 40 insertions(+), 24 deletions(-) diff --git a/server/notification-providers/slack.js b/server/notification-providers/slack.js index f48fe8a3..63f67ce6 100644 --- a/server/notification-providers/slack.js +++ b/server/notification-providers/slack.js @@ -79,6 +79,45 @@ class Slack extends NotificationProvider { } const textMsg = "Uptime Kuma Alert"; + + //create an array to dynamically add blocks + const blocks = []; + + // the header block + blocks.push({ + "type": "header", + "text": { + "type": "plain_text", + "text": textMsg, + }, + }); + + // the body block, containing the details + blocks.push({ + "type": "section", + "fields": [ + { + "type": "mrkdwn", + "text": "*Message*\n" + msg, + }, + { + "type": "mrkdwn", + "text": `*Time (${heartbeatJSON["timezone"]})*\n${heartbeatJSON["localDateTime"]}`, + } + ], + }); + + //only add this block if we have actions + if(actions.length > 0){ + + //the actions block, containing buttons + blocks.push({ + "type": "actions", + "elements": actions, + }); + } + + //finally, build the entire slack request object let data = { "text": `${textMsg}\n${msg}`, "channel": notification.slackchannel, @@ -87,30 +126,7 @@ class Slack extends NotificationProvider { "attachments": [ { "color": (heartbeatJSON["status"] === UP) ? "#2eb886" : "#e01e5a", - "blocks": [ - { - "type": "header", - "text": { - "type": "plain_text", - "text": textMsg, - }, - }, - { - "type": "section", - "fields": [{ - "type": "mrkdwn", - "text": "*Message*\n" + msg, - }, - { - "type": "mrkdwn", - "text": `*Time (${heartbeatJSON["timezone"]})*\n${heartbeatJSON["localDateTime"]}`, - }], - }, - { - "type": "actions", - "elements": actions, - } - ], + "blocks": blocks, } ] }; From ed85b95057e56a3796e8d288ae8bccb77f87b639 Mon Sep 17 00:00:00 2001 From: Daan Meijer Date: Fri, 13 Oct 2023 14:02:48 +0200 Subject: [PATCH 03/27] fixd linting errors --- server/notification-providers/slack.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/server/notification-providers/slack.js b/server/notification-providers/slack.js index 63f67ce6..280b0214 100644 --- a/server/notification-providers/slack.js +++ b/server/notification-providers/slack.js @@ -53,7 +53,7 @@ class Slack extends NotificationProvider { const baseURL = await setting("primaryBaseURL"); - if(baseURL){ + if (baseURL) { actions.push({ "type": "button", "text": { @@ -66,7 +66,7 @@ class Slack extends NotificationProvider { } - if(monitorJSON.url) { + if (monitorJSON.url) { actions.push({ "type": "button", "text": { @@ -75,7 +75,7 @@ class Slack extends NotificationProvider { }, "value": "Site", "url": monitorJSON.url, - }) + }); } const textMsg = "Uptime Kuma Alert"; @@ -108,7 +108,7 @@ class Slack extends NotificationProvider { }); //only add this block if we have actions - if(actions.length > 0){ + if (actions.length > 0) { //the actions block, containing buttons blocks.push({ From c7421acab830928fea96a849ed9265fe1dd8eeb5 Mon Sep 17 00:00:00 2001 From: Daan Meijer Date: Sat, 14 Oct 2023 01:03:15 +0200 Subject: [PATCH 04/27] stylistic changes for review --- server/notification-providers/slack.js | 143 ++++++++++++++----------- 1 file changed, 78 insertions(+), 65 deletions(-) diff --git a/server/notification-providers/slack.js b/server/notification-providers/slack.js index 280b0214..ee597882 100644 --- a/server/notification-providers/slack.js +++ b/server/notification-providers/slack.js @@ -27,6 +27,83 @@ class Slack extends NotificationProvider { } } + static async buildActions(monitorJSON){ + const actions = []; + + const baseURL = await setting("primaryBaseURL"); + + if (baseURL) { + actions.push({ + "type": "button", + "text": { + "type": "plain_text", + "text": "Visit Uptime Kuma", + }, + "value": "Uptime-Kuma", + "url": baseURL + getMonitorRelativeURL(monitorJSON.id), + }); + + } + + if (monitorJSON.url) { + actions.push({ + "type": "button", + "text": { + "type": "plain_text", + "text": "Visit site", + }, + "value": "Site", + "url": monitorJSON.url, + }); + } + + return actions; + } + static async buildBLocks(monitorJSON, heartbeatJSON, textMsg, msg){ + + //create an array to dynamically add blocks + const blocks = []; + + // the header block + blocks.push({ + "type": "header", + "text": { + "type": "plain_text", + "text": textMsg, + }, + }); + + // the body block, containing the details + blocks.push({ + "type": "section", + "fields": [ + { + "type": "mrkdwn", + "text": "*Message*\n" + msg, + }, + { + "type": "mrkdwn", + "text": `*Time (${heartbeatJSON["timezone"]})*\n${heartbeatJSON["localDateTime"]}`, + } + ], + }); + + + const actions = await this.buildActions(monitorJSON); + + //only add this block if we have actions + if (actions.length > 0) { + + //the actions block, containing buttons + blocks.push({ + "type": "actions", + "elements": actions, + }); + } + + return blocks; + } + /** * @inheritdoc */ @@ -49,73 +126,9 @@ class Slack extends NotificationProvider { return okMsg; } - const actions = []; - - const baseURL = await setting("primaryBaseURL"); - - if (baseURL) { - actions.push({ - "type": "button", - "text": { - "type": "plain_text", - "text": "Visit Uptime Kuma", - }, - "value": "Uptime-Kuma", - "url": baseURL + getMonitorRelativeURL(monitorJSON.id), - }); - - } - - if (monitorJSON.url) { - actions.push({ - "type": "button", - "text": { - "type": "plain_text", - "text": "Visit site", - }, - "value": "Site", - "url": monitorJSON.url, - }); - } - const textMsg = "Uptime Kuma Alert"; - //create an array to dynamically add blocks - const blocks = []; - - // the header block - blocks.push({ - "type": "header", - "text": { - "type": "plain_text", - "text": textMsg, - }, - }); - - // the body block, containing the details - blocks.push({ - "type": "section", - "fields": [ - { - "type": "mrkdwn", - "text": "*Message*\n" + msg, - }, - { - "type": "mrkdwn", - "text": `*Time (${heartbeatJSON["timezone"]})*\n${heartbeatJSON["localDateTime"]}`, - } - ], - }); - - //only add this block if we have actions - if (actions.length > 0) { - - //the actions block, containing buttons - blocks.push({ - "type": "actions", - "elements": actions, - }); - } + const blocks = await Slack.buildBLocks(monitorJSON, heartbeatJSON, textMsg, msg); //finally, build the entire slack request object let data = { From ec65ca4536064f2e2eaba98898d0a72881d9bcdb Mon Sep 17 00:00:00 2001 From: Daan Meijer Date: Sat, 14 Oct 2023 10:24:07 +0200 Subject: [PATCH 05/27] Update server/notification-providers/slack.js Fixed casing Co-authored-by: Frank Elsinga --- server/notification-providers/slack.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/notification-providers/slack.js b/server/notification-providers/slack.js index ee597882..2d574ca8 100644 --- a/server/notification-providers/slack.js +++ b/server/notification-providers/slack.js @@ -59,7 +59,7 @@ class Slack extends NotificationProvider { return actions; } - static async buildBLocks(monitorJSON, heartbeatJSON, textMsg, msg){ + static async buildBlocks(monitorJSON, heartbeatJSON, textMsg, msg){ //create an array to dynamically add blocks const blocks = []; From 42e0aed74ddc43c971b18241674e7a787b6298f6 Mon Sep 17 00:00:00 2001 From: Daan Meijer Date: Sat, 14 Oct 2023 10:24:17 +0200 Subject: [PATCH 06/27] Update server/notification-providers/slack.js Co-authored-by: Frank Elsinga --- server/notification-providers/slack.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/server/notification-providers/slack.js b/server/notification-providers/slack.js index 2d574ca8..6321aa70 100644 --- a/server/notification-providers/slack.js +++ b/server/notification-providers/slack.js @@ -90,8 +90,6 @@ class Slack extends NotificationProvider { const actions = await this.buildActions(monitorJSON); - - //only add this block if we have actions if (actions.length > 0) { //the actions block, containing buttons From b5ae9d9e63e7ae81c4cd1222d69c7410341bf987 Mon Sep 17 00:00:00 2001 From: Daan Meijer Date: Sat, 14 Oct 2023 10:25:07 +0200 Subject: [PATCH 07/27] Update server/notification-providers/slack.js Co-authored-by: Frank Elsinga --- server/notification-providers/slack.js | 1 - 1 file changed, 1 deletion(-) diff --git a/server/notification-providers/slack.js b/server/notification-providers/slack.js index 6321aa70..fa037062 100644 --- a/server/notification-providers/slack.js +++ b/server/notification-providers/slack.js @@ -31,7 +31,6 @@ class Slack extends NotificationProvider { const actions = []; const baseURL = await setting("primaryBaseURL"); - if (baseURL) { actions.push({ "type": "button", From 46a088a19b7b5137a99d1d2806072aefcf0306cd Mon Sep 17 00:00:00 2001 From: Daan Meijer Date: Sat, 14 Oct 2023 10:25:49 +0200 Subject: [PATCH 08/27] Update server/notification-providers/slack.js Co-authored-by: Frank Elsinga --- server/notification-providers/slack.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/server/notification-providers/slack.js b/server/notification-providers/slack.js index fa037062..5efad48d 100644 --- a/server/notification-providers/slack.js +++ b/server/notification-providers/slack.js @@ -124,10 +124,6 @@ class Slack extends NotificationProvider { } const textMsg = "Uptime Kuma Alert"; - - const blocks = await Slack.buildBLocks(monitorJSON, heartbeatJSON, textMsg, msg); - - //finally, build the entire slack request object let data = { "text": `${textMsg}\n${msg}`, "channel": notification.slackchannel, From 7be2de863a163e6be3b773fc2f880955063d3543 Mon Sep 17 00:00:00 2001 From: Daan Meijer Date: Sat, 14 Oct 2023 10:25:58 +0200 Subject: [PATCH 09/27] Update server/notification-providers/slack.js Co-authored-by: Frank Elsinga --- server/notification-providers/slack.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/notification-providers/slack.js b/server/notification-providers/slack.js index 5efad48d..771c89d3 100644 --- a/server/notification-providers/slack.js +++ b/server/notification-providers/slack.js @@ -132,7 +132,7 @@ class Slack extends NotificationProvider { "attachments": [ { "color": (heartbeatJSON["status"] === UP) ? "#2eb886" : "#e01e5a", - "blocks": blocks, + "blocks": await Slack.buildBlocks(monitorJSON, heartbeatJSON, textMsg, msg), } ] }; From d0e6a11b20c12badbb1b81c4ce28fc218d1cff18 Mon Sep 17 00:00:00 2001 From: Daan Meijer Date: Sat, 14 Oct 2023 10:27:33 +0200 Subject: [PATCH 10/27] Apply suggestions from code review Co-authored-by: Frank Elsinga --- server/notification-providers/slack.js | 1 - 1 file changed, 1 deletion(-) diff --git a/server/notification-providers/slack.js b/server/notification-providers/slack.js index 771c89d3..f924350e 100644 --- a/server/notification-providers/slack.js +++ b/server/notification-providers/slack.js @@ -90,7 +90,6 @@ class Slack extends NotificationProvider { const actions = await this.buildActions(monitorJSON); if (actions.length > 0) { - //the actions block, containing buttons blocks.push({ "type": "actions", From 535ce0b3e41b291eae1fef73eca55ebcf4ea2153 Mon Sep 17 00:00:00 2001 From: Daan Meijer Date: Sat, 14 Oct 2023 10:47:18 +0200 Subject: [PATCH 11/27] fixed linter warnings/errors --- server/notification-providers/slack.js | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/server/notification-providers/slack.js b/server/notification-providers/slack.js index f924350e..70503282 100644 --- a/server/notification-providers/slack.js +++ b/server/notification-providers/slack.js @@ -27,7 +27,11 @@ class Slack extends NotificationProvider { } } - static async buildActions(monitorJSON){ + /** + * Builds the actions available in the slack message + * @param monitorJSON + */ + static async buildActions (monitorJSON) { const actions = []; const baseURL = await setting("primaryBaseURL"); @@ -58,7 +62,16 @@ class Slack extends NotificationProvider { return actions; } - static async buildBlocks(monitorJSON, heartbeatJSON, textMsg, msg){ + + /** + * Builds the different blocks the Slack message consists of. + * @param monitorJSON + * @param heartbeatJSON + * @param textMsg + * @param msg + * @returns {Promise<*[]>} + */ + static async buildBlocks (monitorJSON, heartbeatJSON, textMsg, msg) { //create an array to dynamically add blocks const blocks = []; From 875b047d53ac05afede19cbb99ea5b542c0a67ba Mon Sep 17 00:00:00 2001 From: Daan Meijer Date: Sat, 14 Oct 2023 11:18:20 +0200 Subject: [PATCH 12/27] fixed linting errors --- server/notification-providers/slack.js | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/server/notification-providers/slack.js b/server/notification-providers/slack.js index 70503282..18ec362d 100644 --- a/server/notification-providers/slack.js +++ b/server/notification-providers/slack.js @@ -29,9 +29,10 @@ class Slack extends NotificationProvider { /** * Builds the actions available in the slack message - * @param monitorJSON + * @param {object} monitorJSON The monitor config + * @returns {Array} The relevant action objects */ - static async buildActions (monitorJSON) { + static async buildActions(monitorJSON) { const actions = []; const baseURL = await setting("primaryBaseURL"); @@ -65,13 +66,13 @@ class Slack extends NotificationProvider { /** * Builds the different blocks the Slack message consists of. - * @param monitorJSON - * @param heartbeatJSON - * @param textMsg - * @param msg - * @returns {Promise<*[]>} + * @param {object} monitorJSON The monitor object + * @param {object} heartbeatJSON The heartbeat object + * @param {string} title The message title + * @param {string} msg The message body + * @returns {Promise<*[object]>} The rich content blocks for the Slack message */ - static async buildBlocks (monitorJSON, heartbeatJSON, textMsg, msg) { + static async buildBlocks(monitorJSON, heartbeatJSON, title, msg) { //create an array to dynamically add blocks const blocks = []; @@ -81,7 +82,7 @@ class Slack extends NotificationProvider { "type": "header", "text": { "type": "plain_text", - "text": textMsg, + "text": title, }, }); @@ -100,7 +101,6 @@ class Slack extends NotificationProvider { ], }); - const actions = await this.buildActions(monitorJSON); if (actions.length > 0) { //the actions block, containing buttons @@ -135,16 +135,16 @@ class Slack extends NotificationProvider { return okMsg; } - const textMsg = "Uptime Kuma Alert"; + const title = "Uptime Kuma Alert"; let data = { - "text": `${textMsg}\n${msg}`, + "text": `${title}\n${msg}`, "channel": notification.slackchannel, "username": notification.slackusername, "icon_emoji": notification.slackiconemo, "attachments": [ { "color": (heartbeatJSON["status"] === UP) ? "#2eb886" : "#e01e5a", - "blocks": await Slack.buildBlocks(monitorJSON, heartbeatJSON, textMsg, msg), + "blocks": await Slack.buildBlocks(monitorJSON, heartbeatJSON, title, msg), } ] }; From e44f5b2edd68fdd47e4a57ceaeb151863ff02190 Mon Sep 17 00:00:00 2001 From: Daan Meijer Date: Sat, 14 Oct 2023 17:21:28 +0200 Subject: [PATCH 13/27] refactored block and action building to not be async --- server/notification-providers/slack.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/server/notification-providers/slack.js b/server/notification-providers/slack.js index 18ec362d..addee749 100644 --- a/server/notification-providers/slack.js +++ b/server/notification-providers/slack.js @@ -29,13 +29,13 @@ class Slack extends NotificationProvider { /** * Builds the actions available in the slack message + * @param {string} baseURL Uptime Kuma base URL * @param {object} monitorJSON The monitor config * @returns {Array} The relevant action objects */ - static async buildActions(monitorJSON) { + static buildActions(baseURL, monitorJSON) { const actions = []; - const baseURL = await setting("primaryBaseURL"); if (baseURL) { actions.push({ "type": "button", @@ -66,13 +66,14 @@ class Slack extends NotificationProvider { /** * Builds the different blocks the Slack message consists of. + * @param {string} baseURL Uptime Kuma base URL * @param {object} monitorJSON The monitor object * @param {object} heartbeatJSON The heartbeat object * @param {string} title The message title * @param {string} msg The message body * @returns {Promise<*[object]>} The rich content blocks for the Slack message */ - static async buildBlocks(monitorJSON, heartbeatJSON, title, msg) { + static buildBlocks(baseURL, monitorJSON, heartbeatJSON, title, msg) { //create an array to dynamically add blocks const blocks = []; @@ -101,7 +102,7 @@ class Slack extends NotificationProvider { ], }); - const actions = await this.buildActions(monitorJSON); + const actions = this.buildActions(baseURL, monitorJSON); if (actions.length > 0) { //the actions block, containing buttons blocks.push({ @@ -135,6 +136,8 @@ class Slack extends NotificationProvider { return okMsg; } + const baseURL = await setting("primaryBaseURL"); + const title = "Uptime Kuma Alert"; let data = { "text": `${title}\n${msg}`, @@ -144,7 +147,7 @@ class Slack extends NotificationProvider { "attachments": [ { "color": (heartbeatJSON["status"] === UP) ? "#2eb886" : "#e01e5a", - "blocks": await Slack.buildBlocks(monitorJSON, heartbeatJSON, title, msg), + "blocks": await Slack.buildBlocks(baseURL, monitorJSON, heartbeatJSON, title, msg), } ] }; From f94faa2f4c942063accfb5bdd52193373619037e Mon Sep 17 00:00:00 2001 From: Daan Meijer Date: Sat, 14 Oct 2023 19:24:40 +0200 Subject: [PATCH 15/27] Apply suggestions from code review Co-authored-by: Nelson Chan <3271800+chakflying@users.noreply.github.com> --- server/notification-providers/slack.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/notification-providers/slack.js b/server/notification-providers/slack.js index addee749..194445f0 100644 --- a/server/notification-providers/slack.js +++ b/server/notification-providers/slack.js @@ -71,7 +71,7 @@ class Slack extends NotificationProvider { * @param {object} heartbeatJSON The heartbeat object * @param {string} title The message title * @param {string} msg The message body - * @returns {Promise<*[object]>} The rich content blocks for the Slack message + * @returns {Array} The rich content blocks for the Slack message */ static buildBlocks(baseURL, monitorJSON, heartbeatJSON, title, msg) { @@ -147,7 +147,7 @@ class Slack extends NotificationProvider { "attachments": [ { "color": (heartbeatJSON["status"] === UP) ? "#2eb886" : "#e01e5a", - "blocks": await Slack.buildBlocks(baseURL, monitorJSON, heartbeatJSON, title, msg), + "blocks": Slack.buildBlocks(baseURL, monitorJSON, heartbeatJSON, title, msg), } ] }; From 4d63ec33394d2035def6a05ebb9cabb5e8e5d954 Mon Sep 17 00:00:00 2001 From: Daan Meijer Date: Sat, 14 Oct 2023 00:51:59 +0200 Subject: [PATCH 16/27] work in progress --- src/components/notifications/SlackAlarm.vue | 36 +++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 src/components/notifications/SlackAlarm.vue diff --git a/src/components/notifications/SlackAlarm.vue b/src/components/notifications/SlackAlarm.vue new file mode 100644 index 00000000..dead709c --- /dev/null +++ b/src/components/notifications/SlackAlarm.vue @@ -0,0 +1,36 @@ + From c4d71ad838858c871fcc63546a628a8245b837e4 Mon Sep 17 00:00:00 2001 From: Daan Meijer Date: Sat, 14 Oct 2023 17:12:15 +0200 Subject: [PATCH 17/27] work in progress --- server/notification-providers/slack.js | 51 ++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/server/notification-providers/slack.js b/server/notification-providers/slack.js index 194445f0..16c0bb4f 100644 --- a/server/notification-providers/slack.js +++ b/server/notification-providers/slack.js @@ -1,7 +1,16 @@ const NotificationProvider = require("./notification-provider"); const axios = require("axios"); const { setSettings, setting } = require("../util-server"); -const { getMonitorRelativeURL, UP } = require("../../src/util"); +const { getMonitorRelativeURL, UP, flipStatus, DOWN} = require("../../src/util"); +const {R} = require("redbean-node"); +const dayjs = require("dayjs"); + +const duration = require('dayjs/plugin/duration') +const relativeTime = require('dayjs/plugin/relativeTime') + +dayjs.extend(duration) +dayjs.extend(relativeTime) + class Slack extends NotificationProvider { @@ -27,6 +36,9 @@ class Slack extends NotificationProvider { } } + // Keeps track of open alerts in order to update them + static openAlerts = {}; + /** * Builds the actions available in the slack message * @param {string} baseURL Uptime Kuma base URL @@ -98,6 +110,10 @@ class Slack extends NotificationProvider { { "type": "mrkdwn", "text": `*Time (${heartbeatJSON["timezone"]})*\n${heartbeatJSON["localDateTime"]}`, + }, + { + "type": "mrkdwn", + "text": `*After*\n${duration.humanize()}`, } ], }); @@ -125,6 +141,8 @@ class Slack extends NotificationProvider { } try { + + // check if the notification provider is being tested if (heartbeatJSON == null) { let data = { "text": msg, @@ -136,6 +154,8 @@ class Slack extends NotificationProvider { return okMsg; } + const duration = await Slack.calculateDuration(heartbeatJSON); + const baseURL = await setting("primaryBaseURL"); const title = "Uptime Kuma Alert"; @@ -147,7 +167,7 @@ class Slack extends NotificationProvider { "attachments": [ { "color": (heartbeatJSON["status"] === UP) ? "#2eb886" : "#e01e5a", - "blocks": Slack.buildBlocks(baseURL, monitorJSON, heartbeatJSON, title, msg), + "blocks": Slack.buildBlocks(baseURL, monitorJSON, heartbeatJSON, title, msg, duration), } ] }; @@ -156,13 +176,38 @@ class Slack extends NotificationProvider { await Slack.deprecateURL(notification.slackbutton); } - await axios.post(notification.slackwebhookURL, data); + const response = await axios.post(notification.slackwebhookURL, data); + + console.log({response: response.data}); + return okMsg; } catch (error) { this.throwGeneralAxiosError(error); } } + + /** + * + * @param {object} heartbeatJSON + * @returns {Promise} + */ + static async calculateDuration(heartbeatJSON) { + + console.log(heartbeatJSON); + const previousDifferentBeat = await R.findOne("heartbeat", " monitor_id = ? AND status = ? ORDER BY time DESC", [ + monitorJSON.id, + flipStatus(heartbeatJSON.status) + ]); + + let durationInMs = null; + + if(previousDifferentBeat){ + durationInMs = new Date(heartbeatJSON.time) - new Date(previousDifferentBeat._time); + } + + return durationInMs; + } } module.exports = Slack; From 5b7968f1a0bae43a911b6848277098370b756403 Mon Sep 17 00:00:00 2001 From: Daan Meijer Date: Sun, 15 Oct 2023 12:11:38 +0200 Subject: [PATCH 18/27] added settings for app api --- server/notification-providers/slack.js | 241 ++++++++++++++------ src/components/notifications/Slack.vue | 30 ++- src/components/notifications/SlackAlarm.vue | 36 --- 3 files changed, 197 insertions(+), 110 deletions(-) delete mode 100644 src/components/notifications/SlackAlarm.vue diff --git a/server/notification-providers/slack.js b/server/notification-providers/slack.js index 16c0bb4f..b9fb7054 100644 --- a/server/notification-providers/slack.js +++ b/server/notification-providers/slack.js @@ -36,19 +36,111 @@ class Slack extends NotificationProvider { } } - // Keeps track of open alerts in order to update them - static openAlerts = {}; /** - * Builds the actions available in the slack message - * @param {string} baseURL Uptime Kuma base URL + * @inheritdoc + */ + async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { + let okMsg = "Sent Successfully."; + + if (notification.slackchannelnotify) { + msg += " "; + } + + try { + + const title = "Uptime Kuma Alert"; + + const message = await Slack.buildMessage(heartbeatJSON, monitorJSON, notification, msg, title); + + //not sure what this does, I think it can be safely removed + if (notification.slackbutton) { + await Slack.deprecateURL(notification.slackbutton); + } + + const response = await Slack.deliverMessage(notification, heartbeatJSON, message); + + console.log({response: response.data}); + + return okMsg; + } catch (error) { + this.throwGeneralAxiosError(error); + } + + } + + /** + * + * @param {object} heartbeatJSON + * @returns {Promise} + */ + static async calculateDuration(heartbeatJSON) { + + console.log(heartbeatJSON); + const previousDifferentBeat = await R.findOne("heartbeat", " monitor_id = ? AND status = ? ORDER BY time DESC", [ + heartbeatJSON.monitorID, + flipStatus(heartbeatJSON.status) + ]); + + let durationInMs = null; + + if (previousDifferentBeat) { + durationInMs = new Date(heartbeatJSON.time) - new Date(previousDifferentBeat._time); + } + + return durationInMs; + } + + + static async buildMessage(heartbeatJSON, monitorJSON, notification, msg, title){ + + // check if the notification provider is being tested + if (heartbeatJSON == null) { + return { + "text": msg, + "channel": notification.slackchannel, + "username": notification.slackusername, + "icon_emoji": notification.slackiconemo, + }; + + } + + console.log({heartbeatJSON}); + + const duration = await Slack.calculateDuration(heartbeatJSON); + + const baseURL = await setting("primaryBaseURL"); + const monitorUrl = baseURL + getMonitorRelativeURL(heartbeatJSON.monitorID); + + const actions = this.buildActions(monitorUrl, monitorJSON); + + return { + "text": `${title}\n${msg}`, + "channel": notification.slackchannel, + "username": notification.slackusername, + "icon_emoji": notification.slackiconemo, + "attachments": [ + { + "color": (heartbeatJSON["status"] === UP) ? "#2eb886" : "#e01e5a", + "blocks": Slack.buildBlocks(actions, heartbeatJSON, title, msg, duration), + } + ] + }; + + } + + + + /** + * Builds the actions available in the Slack message + * @param {string} monitorUrl Uptime Kuma base URL * @param {object} monitorJSON The monitor config * @returns {Array} The relevant action objects */ - static buildActions(baseURL, monitorJSON) { + static buildActions(monitorUrl, monitorJSON) { const actions = []; - if (baseURL) { + if (monitorUrl) { actions.push({ "type": "button", "text": { @@ -56,7 +148,7 @@ class Slack extends NotificationProvider { "text": "Visit Uptime Kuma", }, "value": "Uptime-Kuma", - "url": baseURL + getMonitorRelativeURL(monitorJSON.id), + "url": monitorUrl, }); } @@ -76,16 +168,17 @@ class Slack extends NotificationProvider { return actions; } + /** * Builds the different blocks the Slack message consists of. - * @param {string} baseURL Uptime Kuma base URL - * @param {object} monitorJSON The monitor object + * @param {Array} actions The action objects for the message * @param {object} heartbeatJSON The heartbeat object * @param {string} title The message title * @param {string} msg The message body + * @param {null|number} duration Number of milliseconds since previous state * @returns {Array} The rich content blocks for the Slack message */ - static buildBlocks(baseURL, monitorJSON, heartbeatJSON, title, msg) { + static buildBlocks(actions, heartbeatJSON, title, msg, duration) { //create an array to dynamically add blocks const blocks = []; @@ -113,12 +206,11 @@ class Slack extends NotificationProvider { }, { "type": "mrkdwn", - "text": `*After*\n${duration.humanize()}`, + "text": `*After*\n${dayjs.duration(duration/1000).humanize()}`, } ], }); - const actions = this.buildActions(baseURL, monitorJSON); if (actions.length > 0) { //the actions block, containing buttons blocks.push({ @@ -130,83 +222,96 @@ class Slack extends NotificationProvider { return blocks; } - /** - * @inheritdoc - */ - async send(notification, msg, monitorJSON = null, heartbeatJSON = null) { - let okMsg = "Sent Successfully."; - if (notification.slackchannelnotify) { - msg += " "; - } + static ENDPOINTS = { + postMessage: 'https://slack.com/api/chat.postMessage', + getPermalink: 'https://slack.com/api/chat.getPermalink', + update: 'https://slack.com/api/chat.update', + } - try { - // check if the notification provider is being tested - if (heartbeatJSON == null) { - let data = { - "text": msg, - "channel": notification.slackchannel, - "username": notification.slackusername, - "icon_emoji": notification.slackiconemo, + // Keeps track of open alerts in order to update them + static openAlerts = {}; + + static async deliverMessage(options, heartbeatJSON, message) { + + let response = null; + switch(options.mode){ + case 'app': + const token = options.botToken; + + const monitorId = heartbeatJSON.monitorId; + + const axiosConfig = { + headers: { + 'Authorization': 'Bearer ' +token, + } }; - await axios.post(notification.slackwebhookURL, data); - return okMsg; - } - const duration = await Slack.calculateDuration(heartbeatJSON); + const existingAlerts = Slack.getAlerts(monitorId); + if(existingAlerts.length > 0 && heartbeatJSON.status === UP){ + console.log(`Updating ${existingAlerts.length} messages`); + const responses = await Promise.all(existingAlerts.map(({channel, ts}) => { + message.channel = channel; + message.ts = ts; + return axios.post(Slack.ENDPOINTS.update, message, axiosConfig); + })) + + //get the last response + response = responses.pop(); + }else{ + response = await axios.post(Slack.ENDPOINTS.postMessage, message, axiosConfig); + } - const baseURL = await setting("primaryBaseURL"); + if(response.data.ok){ - const title = "Uptime Kuma Alert"; - let data = { - "text": `${title}\n${msg}`, - "channel": notification.slackchannel, - "username": notification.slackusername, - "icon_emoji": notification.slackiconemo, - "attachments": [ - { - "color": (heartbeatJSON["status"] === UP) ? "#2eb886" : "#e01e5a", - "blocks": Slack.buildBlocks(baseURL, monitorJSON, heartbeatJSON, title, msg, duration), + if(heartbeatJSON.status === DOWN){ + await Slack.trackAlert(monitorId, axiosConfig, response.data.channel, response.data.ts); + }else if(heartbeatJSON.status === UP){ + Slack.clearAlerts(monitorId); } - ] - }; - if (notification.slackbutton) { - await Slack.deprecateURL(notification.slackbutton); - } - const response = await axios.post(notification.slackwebhookURL, data); + } + + break; + + case 'webhook': + default: + response = axios.post(options.slackwebhookURL, message); - console.log({response: response.data}); - return okMsg; - } catch (error) { - this.throwGeneralAxiosError(error); } + return response; } - /** - * - * @param {object} heartbeatJSON - * @returns {Promise} - */ - static async calculateDuration(heartbeatJSON) { - console.log(heartbeatJSON); - const previousDifferentBeat = await R.findOne("heartbeat", " monitor_id = ? AND status = ? ORDER BY time DESC", [ - monitorJSON.id, - flipStatus(heartbeatJSON.status) - ]); + static async trackAlert(monitorId, axiosConfig, channel, ts) { + Slack.openAlerts[monitorId] = Slack.openAlerts[monitorId] || []; - let durationInMs = null; + Slack.openAlerts[monitorId].push({ + channel, + ts + }); - if(previousDifferentBeat){ - durationInMs = new Date(heartbeatJSON.time) - new Date(previousDifferentBeat._time); + /* + const getPermalinkUrl = `${Slack.ENDPOINTS.getPermalink}?channel=${encodeURIComponent(channel)}&message_ts=${encodeURIComponent(ts)}`; + + const permalinkResponse = await axios.get(getPermalinkUrl, axiosConfig) + if(permalinkResponse.data.ok){ + Slack.openAlerts[monitorId].push(permalinkResponse.data.permalink); } - return durationInMs; + */ + } + + static clearAlerts(monitorId) { + Slack.openAlerts[monitorId] = []; + } + + static getAlerts(monitorId) { + return Slack.openAlerts[monitorId] || []; } } diff --git a/src/components/notifications/Slack.vue b/src/components/notifications/Slack.vue index dead709c..a6e30cdb 100644 --- a/src/components/notifications/Slack.vue +++ b/src/components/notifications/Slack.vue @@ -1,11 +1,27 @@ + \ No newline at end of file diff --git a/src/components/notifications/SlackAlarm.vue b/src/components/notifications/SlackAlarm.vue deleted file mode 100644 index dead709c..00000000 --- a/src/components/notifications/SlackAlarm.vue +++ /dev/null @@ -1,36 +0,0 @@ - From 9a3e1ce7d051d4b858983c0df667e5855b21a3ca Mon Sep 17 00:00:00 2001 From: Daan Meijer Date: Sun, 15 Oct 2023 14:35:28 +0200 Subject: [PATCH 19/27] Added jsdoc comments, fixed parameter order --- server/notification-providers/slack.js | 105 +++++++++++++++---------- 1 file changed, 64 insertions(+), 41 deletions(-) diff --git a/server/notification-providers/slack.js b/server/notification-providers/slack.js index b9fb7054..e02c8d59 100644 --- a/server/notification-providers/slack.js +++ b/server/notification-providers/slack.js @@ -1,7 +1,7 @@ const NotificationProvider = require("./notification-provider"); const axios = require("axios"); const { setSettings, setting } = require("../util-server"); -const { getMonitorRelativeURL, UP, flipStatus, DOWN} = require("../../src/util"); +const { getMonitorRelativeURL, UP, flipStatus, DOWN, log} = require("../../src/util"); const {R} = require("redbean-node"); const dayjs = require("dayjs"); @@ -51,16 +51,14 @@ class Slack extends NotificationProvider { const title = "Uptime Kuma Alert"; - const message = await Slack.buildMessage(heartbeatJSON, monitorJSON, notification, msg, title); + const message = await Slack.buildMessage(heartbeatJSON, monitorJSON, notification, title, msg); //not sure what this does, I think it can be safely removed if (notification.slackbutton) { await Slack.deprecateURL(notification.slackbutton); } - const response = await Slack.deliverMessage(notification, heartbeatJSON, message); - - console.log({response: response.data}); + await Slack.deliverMessage(notification, heartbeatJSON, message); return okMsg; } catch (error) { @@ -70,13 +68,12 @@ class Slack extends NotificationProvider { } /** - * + * Function to calculate the duration of the downtime * @param {object} heartbeatJSON * @returns {Promise} */ static async calculateDuration(heartbeatJSON) { - console.log(heartbeatJSON); const previousDifferentBeat = await R.findOne("heartbeat", " monitor_id = ? AND status = ? ORDER BY time DESC", [ heartbeatJSON.monitorID, flipStatus(heartbeatJSON.status) @@ -92,7 +89,16 @@ class Slack extends NotificationProvider { } - static async buildMessage(heartbeatJSON, monitorJSON, notification, msg, title){ + /** + * + * @param {object} heartbeatJSON The heartbeat bean + * @param {object} monitorJSON The monitor bean + * @param {object} notification The notification config + * @param {string} title The message title + * @param {string} msg The textual message + * @returns {Promise} + */ + static async buildMessage(heartbeatJSON, monitorJSON, notification, title, msg){ // check if the notification provider is being tested if (heartbeatJSON == null) { @@ -105,8 +111,6 @@ class Slack extends NotificationProvider { } - console.log({heartbeatJSON}); - const duration = await Slack.calculateDuration(heartbeatJSON); const baseURL = await setting("primaryBaseURL"); @@ -192,23 +196,28 @@ class Slack extends NotificationProvider { }, }); + const body = [ + { + "type": "mrkdwn", + "text": "*Message*\n" + msg, + }, + { + "type": "mrkdwn", + "text": `*Time (${heartbeatJSON["timezone"]})*\n${heartbeatJSON["localDateTime"]}`, + }, + ]; + + if(duration){ + body.push({ + "type": "mrkdwn", + "text": `*After*\n${dayjs.duration(duration/1000).humanize()}`, + }); + } + // the body block, containing the details blocks.push({ "type": "section", - "fields": [ - { - "type": "mrkdwn", - "text": "*Message*\n" + msg, - }, - { - "type": "mrkdwn", - "text": `*Time (${heartbeatJSON["timezone"]})*\n${heartbeatJSON["localDateTime"]}`, - }, - { - "type": "mrkdwn", - "text": `*After*\n${dayjs.duration(duration/1000).humanize()}`, - } - ], + "fields": body, }); if (actions.length > 0) { @@ -230,9 +239,16 @@ class Slack extends NotificationProvider { } - // Keeps track of open alerts in order to update them + // Keeps track of open alerts in order to update/close them static openAlerts = {}; + /** + * + * @param {object} options The slack configuration + * @param {object} heartbeatJSON The heartbeat bean + * @param {object} message The message object to send to Slack + * @returns {Promise>} + */ static async deliverMessage(options, heartbeatJSON, message) { let response = null; @@ -250,7 +266,10 @@ class Slack extends NotificationProvider { const existingAlerts = Slack.getAlerts(monitorId); if(existingAlerts.length > 0 && heartbeatJSON.status === UP){ - console.log(`Updating ${existingAlerts.length} messages`); + + log.info("slack", `Updating ${existingAlerts.length} message(s)`); + + //Update the messages in parallel const responses = await Promise.all(existingAlerts.map(({channel, ts}) => { message.channel = channel; message.ts = ts; @@ -259,6 +278,7 @@ class Slack extends NotificationProvider { //get the last response response = responses.pop(); + }else{ response = await axios.post(Slack.ENDPOINTS.postMessage, message, axiosConfig); } @@ -266,12 +286,11 @@ class Slack extends NotificationProvider { if(response.data.ok){ if(heartbeatJSON.status === DOWN){ - await Slack.trackAlert(monitorId, axiosConfig, response.data.channel, response.data.ts); + Slack.trackAlert(monitorId, response.data); }else if(heartbeatJSON.status === UP){ Slack.clearAlerts(monitorId); } - } break; @@ -287,29 +306,33 @@ class Slack extends NotificationProvider { } - static async trackAlert(monitorId, axiosConfig, channel, ts) { + /** + * Track an open alert for a specific monitor + * @param {string} monitorId The monitor id + * @param {object} data The object representing the message + */ + static trackAlert(monitorId, data) { Slack.openAlerts[monitorId] = Slack.openAlerts[monitorId] || []; - Slack.openAlerts[monitorId].push({ - channel, - ts - }); - - /* - const getPermalinkUrl = `${Slack.ENDPOINTS.getPermalink}?channel=${encodeURIComponent(channel)}&message_ts=${encodeURIComponent(ts)}`; + Slack.openAlerts[monitorId].push(data); - const permalinkResponse = await axios.get(getPermalinkUrl, axiosConfig) - if(permalinkResponse.data.ok){ - Slack.openAlerts[monitorId].push(permalinkResponse.data.permalink); - } + log.debug("notification.slack", `Monitor ${monitorId} now has ${Slack.openAlerts[monitorId].length} open alerts`); - */ } + /** + * Clears the open alerts for a specific monitor + * @param {string} monitorId The monitor id + */ static clearAlerts(monitorId) { Slack.openAlerts[monitorId] = []; } + /** + * Returns the alert(s) for the ongoing incident for a specific monitor + * @param {string} monitorId The monitor id + * @returns {Array} + */ static getAlerts(monitorId) { return Slack.openAlerts[monitorId] || []; } From 0bb81945cb673bb3d365c7f0277a5b07d07a0c1d Mon Sep 17 00:00:00 2001 From: Daan Meijer Date: Sun, 15 Oct 2023 14:55:58 +0200 Subject: [PATCH 20/27] fixed some linting issues --- server/notification-providers/slack.js | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/server/notification-providers/slack.js b/server/notification-providers/slack.js index e02c8d59..8779b75e 100644 --- a/server/notification-providers/slack.js +++ b/server/notification-providers/slack.js @@ -1,16 +1,15 @@ const NotificationProvider = require("./notification-provider"); const axios = require("axios"); const { setSettings, setting } = require("../util-server"); -const { getMonitorRelativeURL, UP, flipStatus, DOWN, log} = require("../../src/util"); -const {R} = require("redbean-node"); +const { getMonitorRelativeURL, UP, flipStatus, DOWN, log } = require("../../src/util"); +const { R } = require("redbean-node"); const dayjs = require("dayjs"); -const duration = require('dayjs/plugin/duration') -const relativeTime = require('dayjs/plugin/relativeTime') - -dayjs.extend(duration) -dayjs.extend(relativeTime) +const duration = require("dayjs/plugin/duration"); +const relativeTime = require("dayjs/plugin/relativeTime"); +dayjs.extend(duration); +dayjs.extend(relativeTime); class Slack extends NotificationProvider { @@ -36,7 +35,6 @@ class Slack extends NotificationProvider { } } - /** * @inheritdoc */ From 431c76abb3cd79a8c410cade7ff7fc0efeac3dff Mon Sep 17 00:00:00 2001 From: Daan Meijer Date: Sun, 15 Oct 2023 16:20:10 +0200 Subject: [PATCH 21/27] removed tab characters --- src/components/notifications/Slack.vue | 34 +++++++++++++------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/components/notifications/Slack.vue b/src/components/notifications/Slack.vue index a6e30cdb..1dc6e1e3 100644 --- a/src/components/notifications/Slack.vue +++ b/src/components/notifications/Slack.vue @@ -1,26 +1,26 @@