From 5a50e9b30119a4db6fd015c58f91fcd1a8daffff Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Mon, 9 Jun 2025 10:30:21 +0800 Subject: [PATCH] Add controller, db, and validation for notifications --- server/controllers/notificationController.js | 49 ++++++++++++++++++- server/db/mongo/modules/notificationModule.js | 38 ++++++++++++++ server/validation/joi.js | 42 ++++++++++++++++ 3 files changed, 128 insertions(+), 1 deletion(-) diff --git a/server/controllers/notificationController.js b/server/controllers/notificationController.js index 3164a111b..108233847 100755 --- a/server/controllers/notificationController.js +++ b/server/controllers/notificationController.js @@ -1,4 +1,7 @@ -import { triggerNotificationBodyValidation } from "../validation/joi.js"; +import { + triggerNotificationBodyValidation, + createNotificationBodyValidation, +} from "../validation/joi.js"; import { handleError, handleValidationError } from "./controllerUtils.js"; const SERVICE_NAME = "NotificationController"; @@ -169,6 +172,50 @@ class NotificationController { next(handleError(error, SERVICE_NAME, "testWebhook")); } } + + createNotification = async (req, res, next) => { + try { + await createNotificationBodyValidation.validateAsync(req.body, { + abortEarly: false, + }); + } catch (error) { + next(handleValidationError(error, SERVICE_NAME)); + return; + } + + try { + const notification = await this.db.createNotification(req.body); + return res.success({ + msg: "Notification created successfully", + data: notification, + }); + } catch (error) { + next(handleError(error, SERVICE_NAME, "createNotification")); + } + }; + + getNotificationsByTeamId = async (req, res, next) => { + try { + const notifications = await this.db.getNotificationsByTeamId(req.params.teamId); + return res.success({ + msg: "Notifications fetched successfully", + data: notifications, + }); + } catch (error) { + next(handleError(error, SERVICE_NAME, "getNotificationsByTeamId")); + } + }; + + deleteNotification = async (req, res, next) => { + try { + await this.db.deleteNotificationById(req.params.id); + return res.success({ + msg: "Notification deleted successfully", + }); + } catch (error) { + next(handleError(error, SERVICE_NAME, "deleteNotification")); + } + }; } export default NotificationController; diff --git a/server/db/mongo/modules/notificationModule.js b/server/db/mongo/modules/notificationModule.js index aab2d03bd..d172ee8cf 100755 --- a/server/db/mongo/modules/notificationModule.js +++ b/server/db/mongo/modules/notificationModule.js @@ -1,4 +1,5 @@ import Notification from "../../models/Notification.js"; +import Monitor from "../../models/Monitor.js"; const SERVICE_NAME = "notificationModule"; /** * Creates a new notification. @@ -21,6 +22,28 @@ const createNotification = async (notificationData) => { } }; +const getNotificationsByTeamId = async (teamId) => { + try { + const notifications = await Notification.find({ teamId }); + return notifications; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "getNotificationsByTeamId"; + throw error; + } +}; + +const getNotificationsByIds = async (notificationIds) => { + try { + const notifications = await Notification.find({ _id: { $in: notificationIds } }); + return notifications; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "getNotificationsByIds"; + throw error; + } +}; + /** * Retrieves notifications by monitor ID. * @param {mongoose.Types.ObjectId} monitorId - The ID of the monitor. @@ -49,8 +72,23 @@ const deleteNotificationsByMonitorId = async (monitorId) => { } }; +const deleteNotificationById = async (id) => { + try { + const result = await Notification.findByIdAndDelete(id); + await Monitor.updateMany({ notifications: id }, { $pull: { notifications: id } }); + return result; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "deleteNotificationById"; + throw error; + } +}; + export { createNotification, + getNotificationsByTeamId, + getNotificationsByIds, getNotificationsByMonitorId, deleteNotificationsByMonitorId, + deleteNotificationById, }; diff --git a/server/validation/joi.js b/server/validation/joi.js index 3efd202b5..1e3325fcb 100755 --- a/server/validation/joi.js +++ b/server/validation/joi.js @@ -566,6 +566,47 @@ const triggerNotificationBodyValidation = joi.object({ }), }); +const createNotificationBodyValidation = joi.object({ + userId: joi.number().required().messages({ + "number.empty": "User ID is required", + "any.required": "User ID is required", + }), + teamId: joi.number().required().messages({ + "string.empty": "Team ID is required", + "any.required": "Team ID is required", + }), + notificationName: joi.string().required().messages({ + "string.empty": "Notification name is required", + "any.required": "Notification name is required", + }), + address: joi.when("type", { + is: "email", + then: joi.string().email().required().messages({ + "string.empty": "E-mail address cannot be empty", + "any.required": "E-mail address is required", + "string.email": "Please enter a valid e-mail address", + }), + }), + type: joi.string().valid("email", "webhook", "pager_duty").required().messages({ + "string.empty": "Notification type is required", + "any.required": "Notification type is required", + "any.only": "Notification type must be email, webhook, or pager_duty", + }), + config: joi.when("type", { + is: "webhook", + then: joi.object({ + webhookUrl: joi.string().uri().required().messages({ + "string.uri": "Webhook URL must be a valid URI", + "any.required": "Webhook URL is required", + }), + platform: joi.string().required().messages({ + "string.empty": "Platform is required", + "any.required": "Platform is required", + }), + }), + }), +}); + //**************************************** // Announcetment Page Validation //**************************************** @@ -653,6 +694,7 @@ export { getStatusPageQueryValidation, imageValidation, triggerNotificationBodyValidation, + createNotificationBodyValidation, webhookConfigValidation, createAnnouncementValidation, sendTestEmailBodyValidation,