diff --git a/server/src/controllers/notificationController.ts b/server/src/controllers/notificationController.ts index 403375508..c913a65fb 100644 --- a/server/src/controllers/notificationController.ts +++ b/server/src/controllers/notificationController.ts @@ -1,7 +1,7 @@ import { Request, Response, NextFunction } from "express"; import { Notification } from "@/types/index.js"; -import { createNotificationBodyValidation } from "@/validation/joi.js"; +import { createNotificationBodyValidation, bulkEditNotificationBodyValidation } from "@/validation/joi.js"; import { AppError } from "@/utils/AppError.js"; import { IMonitorsRepository } from "@/repositories/index.js"; import { INotificationsService } from "@/service/index.js"; @@ -154,31 +154,18 @@ class NotificationController { } }; + // Handler for bulk editing monitor notification associations bulkEditMonitorNotifications = async (req: Request, res: Response, next: NextFunction) => { try { - const teamId = requireTeamId(req.user?.teamId); + // Joi validates monitorIds, notificationIds, and action automatically + await bulkEditNotificationBodyValidation.validateAsync(req.body, { + abortEarly: false, + }); + const teamId = requireTeamId(req.user?.teamId); const { monitorIds, notificationIds, action } = req.body; - if (!monitorIds || !Array.isArray(monitorIds) || monitorIds.length === 0) { - throw new AppError({ message: "Monitor IDs are required", status: 400 }); - } - - if (!notificationIds || !Array.isArray(notificationIds)) { - throw new AppError({ message: "Notification IDs must be an array", status: 400 }); - } - - if (!action || !["add", "remove", "set"].includes(action)) { - throw new AppError({ message: "Action must be 'add', 'remove', or 'set'", status: 400 }); - } - - // "add" and "remove" need at least one notification ID - // "set" can be empty (to clear all notifications from monitors) - if (notificationIds.length === 0 && action !== "set") { - throw new AppError({ message: "Notification IDs are required for add/remove actions", status: 400 }); - } - - const modifiedCount = await this.monitorsRepository.bulkUpdateNotifications(monitorIds, notificationIds, action, teamId); + const modifiedCount = await this.monitorsRepository.updateNotifications(monitorIds, notificationIds, action, teamId); return res.status(200).json({ success: true, diff --git a/server/src/repositories/monitors/IMonitorsRepository.ts b/server/src/repositories/monitors/IMonitorsRepository.ts index 0732f93dd..88c039881 100644 --- a/server/src/repositories/monitors/IMonitorsRepository.ts +++ b/server/src/repositories/monitors/IMonitorsRepository.ts @@ -41,7 +41,7 @@ export interface IMonitorsRepository { findMonitorsSummaryByTeamId(teamId: string, config?: SummaryConfig): Promise; findGroupsByTeamId(teamId: string): Promise; removeNotificationFromMonitors(notificationId: string): Promise; - bulkUpdateNotifications(monitorIds: string[], notificationIds: string[], action: "add" | "remove" | "set", teamId: string): Promise; + updateNotifications(monitorIds: string[], notificationIds: string[], action: "add" | "remove" | "set", teamId: string): Promise; deleteByTeamIdsNotIn(teamIds: string[]): Promise; findAllMonitorIds(): Promise; } diff --git a/server/src/repositories/monitors/MongoMonitorsRepository.ts b/server/src/repositories/monitors/MongoMonitorsRepository.ts index 529ce2413..63e38721c 100644 --- a/server/src/repositories/monitors/MongoMonitorsRepository.ts +++ b/server/src/repositories/monitors/MongoMonitorsRepository.ts @@ -298,7 +298,7 @@ class MongoMonitorsRepository implements IMonitorsRepository { await MonitorModel.updateMany({ notifications: notificationId }, { $pull: { notifications: notificationId } }); }; - bulkUpdateNotifications = async ( + updateNotifications = async ( monitorIds: string[], notificationIds: string[], action: "add" | "remove" | "set", @@ -323,6 +323,8 @@ class MongoMonitorsRepository implements IMonitorsRepository { case "set": update = { $set: { notifications: notificationIds } }; break; + default: + throw new AppError({ message: `Invalid action: ${action}`, status: 400 }); } const result = await MonitorModel.updateMany(filter, update); diff --git a/server/src/validation/joi.ts b/server/src/validation/joi.ts index 912767587..cfe2502a6 100755 --- a/server/src/validation/joi.ts +++ b/server/src/validation/joi.ts @@ -573,6 +573,20 @@ const triggerNotificationBodyValidation = joi.object({ }), }); +const bulkEditNotificationBodyValidation = joi.object({ + monitorIds: joi.array().items(joi.string()).min(1).required().messages({ + "array.min": "At least one monitor ID is required", + "any.required": "Monitor IDs are required", + }), + notificationIds: joi.array().items(joi.string()).required().messages({ + "any.required": "Notification IDs are required", + }), + action: joi.string().valid("add", "remove", "set").required().messages({ + "any.only": "Action must be 'add', 'remove', or 'set'", + "any.required": "Action is required", + }), +}); + const createNotificationBodyValidation = joi.object({ notificationName: joi.string().required().messages({ "string.empty": "Notification name is required", @@ -793,6 +807,7 @@ export { imageValidation, triggerNotificationBodyValidation, createNotificationBodyValidation, + bulkEditNotificationBodyValidation, webhookConfigValidation, createAnnouncementValidation, sendTestEmailBodyValidation,