Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Discord webhook notification support #1612

Merged
merged 42 commits into from
Feb 18, 2025
Merged
Show file tree
Hide file tree
Changes from 41 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
28f5d35
Add Discord webhook notification support
Skorpios604 Jan 22, 2025
172597e
Add Slack webhook integration.
Skorpios604 Jan 23, 2025
d3810db
Added telegram notifications
Skorpios604 Jan 26, 2025
d83773a
Merge remote-tracking branch 'upstream/develop' into feat/be/webhook-…
Skorpios604 Jan 26, 2025
84fe58b
Refactor webhook integrations functions.
Skorpios604 Jan 26, 2025
92d9573
Merge remote-tracking branch 'upstream/develop' into feat/be/webhook-…
Skorpios604 Feb 2, 2025
cc48f1d
Use the NetworkService for making network requests.
Skorpios604 Feb 2, 2025
8667f74
Merge remote-tracking branch 'upstream/develop' into feat/be/webhook-…
Skorpios604 Feb 3, 2025
0bb7eae
Got rid of imports.
Skorpios604 Feb 3, 2025
d3fb41a
Refactor BASE URL.
Skorpios604 Feb 3, 2025
8f94062
Store bot token and chat id in their own respective fields.
Skorpios604 Feb 3, 2025
1d9f94c
Merge remote-tracking branch 'upstream/develop' into feat/be/webhook-…
Skorpios604 Feb 4, 2025
e44cbf3
Stop execution in the event of an unwanted platform.
Skorpios604 Feb 4, 2025
aa977ce
Refactor out to a template for easier message maintenance.
Skorpios604 Feb 4, 2025
964a923
Returned comments.
Skorpios604 Feb 4, 2025
e9bcf66
Resolved merge conflict in Server/index.js
Skorpios604 Feb 9, 2025
64c5aa9
Added notifcation controller and route.
Skorpios604 Feb 9, 2025
01e73fe
Merge remote-tracking branch 'upstream/develop' into feat/be/webhook-…
Skorpios604 Feb 9, 2025
d8379f7
Configured a config object in the notification schema.
Skorpios604 Feb 9, 2025
f46b076
Refactored notification schema config object.
Skorpios604 Feb 9, 2025
ccd6c58
Merge remote-tracking branch 'upstream/develop' into feat/be/webhook-…
Skorpios604 Feb 11, 2025
ef41712
Got rid of falsey value.
Skorpios604 Feb 11, 2025
fa50706
Simplified network response.
Skorpios604 Feb 11, 2025
67da574
Secured notifications route.
Skorpios604 Feb 11, 2025
023d943
Merge remote-tracking branch 'upstream/develop' into feat/be/webhook-…
Skorpios604 Feb 13, 2025
012483d
Automated validation via joi.
Skorpios604 Feb 13, 2025
8007daa
Used response handling middleware for the response format.
Skorpios604 Feb 13, 2025
70ffe1b
Use middleware for handling errors.
Skorpios604 Feb 13, 2025
12d9f15
Got rid context bind in the route.
Skorpios604 Feb 13, 2025
07882e0
Merge remote-tracking branch 'upstream/develop' into feat/be/webhook-…
Skorpios604 Feb 17, 2025
8409571
Defined new schema for the config object.
Skorpios604 Feb 17, 2025
55c1a1c
Moved validation to the controller.
Skorpios604 Feb 17, 2025
ff2b3b4
Removed timing request for the webhook.
Skorpios604 Feb 17, 2025
6152679
Update Docs.
Skorpios604 Feb 17, 2025
e4e3f9c
Merge remote-tracking branch 'upstream/develop' into feat/be/webhook-…
Skorpios604 Feb 17, 2025
f22b67a
Use the localization service for user facing strings.
Skorpios604 Feb 17, 2025
d710ec0
Merge remote-tracking branch 'upstream/develop' into feat/be/webhook-…
Skorpios604 Feb 17, 2025
c4168a5
Removed null and replace with undefined.
Skorpios604 Feb 17, 2025
afc9d46
Refactored notification messsages into an array of acceptable types.
Skorpios604 Feb 17, 2025
03a938d
Check if platform type is accepted before formatting the message.
Skorpios604 Feb 17, 2025
5be17af
Used 1 validation schema for all platforms.
Skorpios604 Feb 18, 2025
4ef2b29
Used string service instead of hardcoded value.
Skorpios604 Feb 18, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions Server/controllers/notificationController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import {
triggerNotificationBodyValidation,
} from '../validation/joi.js';
import { handleError, handleValidationError } from './controllerUtils.js';
Comment on lines +1 to +4
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Yo, where's the platform validation squad at? 🎤

We need to import those platform-specific validation schemas to keep our webhooks tight and secure!

 import {
     triggerNotificationBodyValidation,
+    discordWebhookConfigValidation,
+    telegramWebhookConfigValidation,
+    slackWebhookConfigValidation
 } from '../validation/joi.js';
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import {
triggerNotificationBodyValidation,
} from '../validation/joi.js';
import { handleError, handleValidationError } from './controllerUtils.js';
import {
triggerNotificationBodyValidation,
discordWebhookConfigValidation,
telegramWebhookConfigValidation,
slackWebhookConfigValidation
} from '../validation/joi.js';
import { handleError, handleValidationError } from './controllerUtils.js';


const SERVICE_NAME = "NotificationController";

class NotificationController {
constructor(notificationService, stringService) {
this.notificationService = notificationService;
this.stringService = stringService;
this.triggerNotification = this.triggerNotification.bind(this);
}

async triggerNotification(req, res, next) {
try {
await triggerNotificationBodyValidation.validateAsync(req.body, {
abortEarly: false,
stripUnknown: true
});
} catch (error) {
next(handleValidationError(error, SERVICE_NAME));
return;
}

try {
const { monitorId, type, platform, config } = req.body;

const networkResponse = {
monitor: { _id: monitorId, name: "Test Monitor", url: "http://www.google.com" },
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see the name and url in networkResponse object are hardcoded. Was this just for testing?

status: false,
statusChanged: true,
prevStatus: true,
};

if (type === "webhook") {
const notification = {
type,
platform,
config
};

await this.notificationService.sendWebhookNotification(
networkResponse,
notification
);
}
Comment on lines +36 to +47
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Drop the beat with platform-specific validation! 🎤

You've imported those validation schemas like they're backup dancers, but they're not getting any stage time! Let's make them perform!

            if (type === "webhook") {
+               let validationSchema;
+               switch (platform) {
+                   case 'discord':
+                       validationSchema = discordWebhookConfigValidation;
+                       break;
+                   case 'telegram':
+                       validationSchema = telegramWebhookConfigValidation;
+                       break;
+                   case 'slack':
+                       validationSchema = slackWebhookConfigValidation;
+                       break;
+                   default:
+                       throw new Error(`Unsupported platform: ${platform}`);
+               }
+               
+               await validationSchema.validateAsync(config, {
+                   abortEarly: false,
+                   stripUnknown: true
+               });

                const notification = {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (type === "webhook") {
const notification = {
type,
platform,
config
};
await this.notificationService.sendWebhookNotification(
networkResponse,
notification
);
}
if (type === "webhook") {
let validationSchema;
switch (platform) {
case 'discord':
validationSchema = discordWebhookConfigValidation;
break;
case 'telegram':
validationSchema = telegramWebhookConfigValidation;
break;
case 'slack':
validationSchema = slackWebhookConfigValidation;
break;
default:
throw new Error(`Unsupported platform: ${platform}`);
}
await validationSchema.validateAsync(config, {
abortEarly: false,
stripUnknown: true
});
const notification = {
type,
platform,
config
};
await this.notificationService.sendWebhookNotification(
networkResponse,
notification
);
}


return res.success({
msg: "Notification sent successfully"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to use the string service here

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

});

} catch (error) {
next(handleError(error, SERVICE_NAME, "triggerNotification"));
}
}
}

export default NotificationController;
14 changes: 13 additions & 1 deletion Server/db/models/Notification.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import mongoose from "mongoose";

const configSchema = mongoose.Schema({
webhookUrl: { type: String },
botToken: { type: String },
chatId: { type: String }
}, { _id: false });

const NotificationSchema = mongoose.Schema(
{
monitorId: {
Expand All @@ -8,8 +15,12 @@ const NotificationSchema = mongoose.Schema(
},
type: {
type: String,
enum: ["email", "sms"],
enum: ["email", "sms", "webhook"],
},
config: {
type: configSchema,
default: () => ({})
},
address: {
type: String,
},
Expand Down Expand Up @@ -76,4 +87,5 @@ NotificationSchema.pre("findOneAndUpdate", function (next) {
}
next();
});

export default mongoose.model("Notification", NotificationSchema);
15 changes: 14 additions & 1 deletion Server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@
import DistributedUptimeRoutes from "./routes/distributedUptimeRoute.js";
import DistributedUptimeController from "./controllers/distributedUptimeController.js";

import NotificationRoutes from "./routes/notificationRoute.js";

import NotificationController from "./controllers/notificationController.js";

//JobQueue service and dependencies
import JobQueue from "./service/jobQueue.js";
import { Queue, Worker } from "bullmq";
Expand Down Expand Up @@ -166,7 +170,7 @@
logger
);
const statusService = new StatusService(db, logger);
const notificationService = new NotificationService(emailService, db, logger);
const notificationService = new NotificationService(emailService, db, logger, networkService, stringService);

const jobQueue = new JobQueue(
db,
Expand Down Expand Up @@ -251,6 +255,11 @@
ServiceRegistry.get(StringService.SERVICE_NAME)
);

const notificationController = new NotificationController(
ServiceRegistry.get(NotificationService.SERVICE_NAME),
ServiceRegistry.get(StringService.SERVICE_NAME)
);

const distributedUptimeController = new DistributedUptimeController(
ServiceRegistry.get(MongoDB.SERVICE_NAME),
http,
Expand All @@ -271,6 +280,9 @@
const distributedUptimeRoutes = new DistributedUptimeRoutes(
distributedUptimeController
);

const notificationRoutes = new NotificationRoutes(notificationController);

// Init job queue
await jobQueue.initJobQueue();
// Middleware
Expand All @@ -293,6 +305,7 @@
app.use("/api/v1/queue", verifyJWT, queueRoutes.getRouter());
app.use("/api/v1/distributed-uptime", distributedUptimeRoutes.getRouter());
app.use("/api/v1/status-page", statusPageRoutes.getRouter());
app.use("/api/v1/notifications", verifyJWT, notificationRoutes.getRouter());

Check failure

Code scanning / CodeQL

Missing rate limiting High

This route handler performs
authorization
, but is not rate-limited.

Copilot Autofix AI 4 days ago

To fix the problem, we need to introduce rate limiting to the Express application. The best way to do this is by using the express-rate-limit package, which allows us to set a maximum number of requests that can be made to the server within a specified time window. We will apply this rate limiter to the /api/v1/notifications route to prevent abuse.

  1. Install the express-rate-limit package.
  2. Import the express-rate-limit package in the Server/index.js file.
  3. Set up a rate limiter with appropriate configuration (e.g., maximum of 100 requests per 15 minutes).
  4. Apply the rate limiter to the /api/v1/notifications route.
Suggested changeset 2
Server/index.js

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/Server/index.js b/Server/index.js
--- a/Server/index.js
+++ b/Server/index.js
@@ -3,3 +3,3 @@
 import swaggerUi from "swagger-ui-express";
-
+import rateLimit from "express-rate-limit";
 import express from "express";
@@ -44,2 +44,8 @@
 
+// set up rate limiter: maximum of 100 requests per 15 minutes
+const limiter = rateLimit({
+    windowMs: 15 * 60 * 1000, // 15 minutes
+    max: 100, // limit each IP to 100 requests per windowMs
+});
+
 //JobQueue service and dependencies
@@ -307,3 +313,3 @@
 	app.use("/api/v1/status-page", statusPageRoutes.getRouter());
-	app.use("/api/v1/notifications", verifyJWT, notificationRoutes.getRouter());
+	app.use("/api/v1/notifications", limiter, verifyJWT, notificationRoutes.getRouter());
 	app.use(handleErrors);
EOF
@@ -3,3 +3,3 @@
import swaggerUi from "swagger-ui-express";

import rateLimit from "express-rate-limit";
import express from "express";
@@ -44,2 +44,8 @@

// set up rate limiter: maximum of 100 requests per 15 minutes
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // limit each IP to 100 requests per windowMs
});

//JobQueue service and dependencies
@@ -307,3 +313,3 @@
app.use("/api/v1/status-page", statusPageRoutes.getRouter());
app.use("/api/v1/notifications", verifyJWT, notificationRoutes.getRouter());
app.use("/api/v1/notifications", limiter, verifyJWT, notificationRoutes.getRouter());
app.use(handleErrors);
Server/package.json
Outside changed files

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/Server/package.json b/Server/package.json
--- a/Server/package.json
+++ b/Server/package.json
@@ -40,3 +40,4 @@
 		"swagger-ui-express": "5.0.1",
-		"winston": "^3.13.0"
+		"winston": "^3.13.0",
+		"express-rate-limit": "^7.5.0"
 	},
EOF
@@ -40,3 +40,4 @@
"swagger-ui-express": "5.0.1",
"winston": "^3.13.0"
"winston": "^3.13.0",
"express-rate-limit": "^7.5.0"
},
This fix introduces these dependencies
Package Version Security advisories
express-rate-limit (npm) 7.5.0 None
Copilot is powered by AI and may make mistakes. Always verify output.
Positive Feedback
Negative Feedback

Provide additional feedback

Please help us improve GitHub Copilot by sharing more details about this comment.

Please select one or more of the options
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add rate limiting to protect against abuse.

The notification route is properly secured with JWT verification, but it lacks rate limiting which could make it vulnerable to abuse.

Apply this diff to add rate limiting:

-app.use("/api/v1/notifications", verifyJWT, notificationRoutes.getRouter());
+app.use("/api/v1/notifications", verifyJWT, rateLimiter, notificationRoutes.getRouter());

And add the rate limiter middleware at the top of the file:

import rateLimit from 'express-rate-limit';

const rateLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100 // limit each IP to 100 requests per windowMs
});
🧰 Tools
🪛 GitHub Check: CodeQL

[failure] 308-308: Missing rate limiting
This route handler performs authorization, but is not rate-limited.

app.use(handleErrors);
};

Expand Down
24 changes: 24 additions & 0 deletions Server/routes/notificationRoute.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import express from 'express';
import { verifyJWT } from '../middleware/verifyJWT.js';

class NotificationRoutes {
constructor(notificationController) {
this.notificationController = notificationController;
this.router = express.Router();
this.initializeRoutes();
}

initializeRoutes() {
this.router.post(
'/trigger',
verifyJWT,

Check failure

Code scanning / CodeQL

Missing rate limiting High

This route handler performs
authorization
, but is not rate-limited.
Comment on lines +1 to +14
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me.

this.notificationController.triggerNotification
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Lose yourself in the proper binding! 🎵

The controller method needs to be bound to maintain its context, just like you need to bind yourself to the beat!

-           this.notificationController.triggerNotification
+           this.notificationController.triggerNotification.bind(this.notificationController)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
this.notificationController.triggerNotification
this.notificationController.triggerNotification.bind(this.notificationController)

);
}

getRouter() {
return this.router;
}
}

export default NotificationRoutes;
40 changes: 40 additions & 0 deletions Server/service/networkService.js
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,46 @@ class NetworkService {
throw err;
}

async requestWebhook(platform, url, message) {
try {
const response = await this.axios.post(url, message, {
headers: {
'Content-Type': 'application/json'
}
});

return {
type: 'webhook',
status: true,
code: response.status,
message: `Successfully sent ${platform} notification`,
payload: response.data
};

} catch (error) {
this.logger.warn({
message: error.message,
service: this.SERVICE_NAME,
method: 'requestWebhook',
url,
platform,
error: error.message,
statusCode: error.response?.status,
responseData: error.response?.data,
requestPayload: message
});

return {
type: 'webhook',
status: false,
code: error.response?.status || this.NETWORK_ERROR,
message: `Failed to send ${platform} notification`,
payload: error.response?.data
};
}
}


Comment on lines +436 to +475
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

error handling and logging looks good.

/**
* Gets the status of a job based on its type and returns the appropriate response.
*
Expand Down
121 changes: 113 additions & 8 deletions Server/service/notificationService.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
const SERVICE_NAME = "NotificationService";
const TELEGRAM_API_BASE_URL = "https://api.telegram.org/bot";
const PLATFORM_TYPES = ['telegram', 'slack', 'discord'];

const MESSAGE_FORMATTERS = {
telegram: (messageText, chatId) => ({ chat_id: chatId, text: messageText }),
slack: (messageText) => ({ text: messageText }),
discord: (messageText) => ({ content: messageText })
};

class NotificationService {
static SERVICE_NAME = SERVICE_NAME;
Expand All @@ -8,14 +16,111 @@ class NotificationService {
* @param {Object} emailService - The email service used for sending notifications.
* @param {Object} db - The database instance for storing notification data.
* @param {Object} logger - The logger instance for logging activities.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Be sure to update the JSdoc since this now takes a networkService parameter

* @param {Object} networkService - The network service for sending webhook notifications.
*/
constructor(emailService, db, logger) {
constructor(emailService, db, logger, networkService, stringService) {
this.SERVICE_NAME = SERVICE_NAME;
this.emailService = emailService;
this.db = db;
this.logger = logger;
this.networkService = networkService;
this.stringService = stringService;
}

/**
* Formats a notification message based on the monitor status and platform.
*
* @param {Object} monitor - The monitor object.
* @param {string} monitor.name - The name of the monitor.
* @param {string} monitor.url - The URL of the monitor.
* @param {boolean} status - The current status of the monitor (true for up, false for down).
* @param {string} platform - The notification platform (e.g., "telegram", "slack", "discord").
* @param {string} [chatId] - The chat ID for platforms that require it (e.g., Telegram).
* @returns {Object|null} The formatted message object for the specified platform, or null if the platform is unsupported.
*/

formatNotificationMessage(monitor, status, platform, chatId) {
const messageText = this.stringService.getMonitorStatus(
monitor.name,
status,
monitor.url
);

if (!PLATFORM_TYPES.includes(platform)) {
return undefined;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this if/else ladder meant to fall through at the end?

Should we continue execution if the platform is not one of slack, discord, or telegram? Or are we meant to return early?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, sorry, you are right. It should not continue execution. I wrote an else to return early.


return MESSAGE_FORMATTERS[platform](messageText, chatId);
}

/**
* Sends a webhook notification to a specified platform.
*
* @param {Object} networkResponse - The response object from the network.
* @param {Object} networkResponse.monitor - The monitor object.
* @param {boolean} networkResponse.status - The monitor's status (true for up, false for down).
* @param {Object} notification - The notification settings.
* @param {string} notification.platform - The target platform ("telegram", "slack", "discord").
* @param {Object} notification.config - The configuration object for the webhook.
* @param {string} notification.config.webhookUrl - The webhook URL for the platform.
* @param {string} [notification.config.botToken] - The bot token for Telegram notifications.
* @param {string} [notification.config.chatId] - The chat ID for Telegram notifications.
* @returns {Promise<boolean>} A promise that resolves to true if the notification was sent successfully, otherwise false.
*/

async sendWebhookNotification(networkResponse, notification) {
const { monitor, status } = networkResponse;
const { platform } = notification;
const { webhookUrl, botToken, chatId } = notification.config;

// Early return if platform is not supported
if (!PLATFORM_TYPES.includes(platform)) {
this.logger.warn({
message: this.stringService.getWebhookUnsupportedPlatform(platform),
service: this.SERVICE_NAME,
method: 'sendWebhookNotification',
platform
});
return false;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this error meant to be swallowed and operations meant to continue in event of error?

If not it should have method specifics added to it and rethrown to be caught by middleware

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes!

}

// Early return for telegram if required fields are missing
if (platform === 'telegram' && (!botToken || !chatId)) {
this.logger.warn({
message: 'Missing required fields for Telegram notification',
service: this.SERVICE_NAME,
method: 'sendWebhookNotification',
platform
});
return false;
}

let url = webhookUrl;
if (platform === 'telegram') {
url = `${TELEGRAM_API_BASE_URL}${botToken}/sendMessage`;
}

// Now that we know the platform is valid, format the message
const message = this.formatNotificationMessage(monitor, status, platform, chatId);

try {
const response = await this.networkService.requestWebhook(platform, url, message);
return response.status;
} catch (error) {
this.logger.error({
message: this.stringService.getWebhookSendError(platform),
service: this.SERVICE_NAME,
method: 'sendWebhookNotification',
error: error.message,
stack: error.stack,
url,
platform,
requestPayload: message
});
return false;
}
}

/**
* Sends an email notification for hardware infrastructure alerts
*
Expand Down Expand Up @@ -57,18 +162,18 @@ class NotificationService {

async handleStatusNotifications(networkResponse) {
try {
//If status hasn't changed, we're done
// If status hasn't changed, we're done
if (networkResponse.statusChanged === false) return false;

// if prevStatus is undefined, monitor is resuming, we're done
if (networkResponse.prevStatus === undefined) return false;
const notifications = await this.db.getNotificationsByMonitorId(
networkResponse.monitorId
);


const notifications = await this.db.getNotificationsByMonitorId(networkResponse.monitorId);

for (const notification of notifications) {
if (notification.type === "email") {
this.sendEmail(networkResponse, notification.address);
await this.sendEmail(networkResponse, notification.address);
} else if (notification.type === "webhook") {
await this.sendWebhookNotification(networkResponse, notification);
}
// Handle other types of notifications here
}
Expand Down
Loading