From 117c22fae4f1661d91693c38a45b7440b306c6a3 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Wed, 21 Jan 2026 14:22:53 -0800 Subject: [PATCH 1/7] initial commit --- server/src/config/services.ts | 3 - server/src/db/MongoDB.js | 3 +- server/src/db/modules/notificationModule.js | 98 ------------------- server/src/repositories/index.ts | 3 + .../IMaintenanceWindowsRepository.ts | 1 + .../MongoMaintenanceWindowsRepository.ts | 5 + server/src/types/maintenanceWindow.ts | 45 +++++++++ 7 files changed, 55 insertions(+), 103 deletions(-) delete mode 100755 server/src/db/modules/notificationModule.js create mode 100644 server/src/repositories/maintenance-windows/IMaintenanceWindowsRepository.ts create mode 100644 server/src/repositories/maintenance-windows/MongoMaintenanceWindowsRepository.ts create mode 100644 server/src/types/maintenanceWindow.ts diff --git a/server/src/config/services.ts b/server/src/config/services.ts index cdee721b2..cec2ad2c3 100644 --- a/server/src/config/services.ts +++ b/server/src/config/services.ts @@ -61,7 +61,6 @@ import Incident from "../db/models/Incident.js"; import StatusPageModule from "../db/modules/statusPageModule.js"; import MaintenanceWindowModule from "../db/modules/maintenanceWindowModule.js"; -import NotificationModule from "../db/modules/notificationModule.js"; import IncidentModule from "../db/modules/incidentModule.js"; // repositories @@ -138,7 +137,6 @@ export const initializeServices = async ({ // Create DB const statusPageModule = new StatusPageModule({ StatusPage, NormalizeData, AppSettings }); const maintenanceWindowModule = new MaintenanceWindowModule({ MaintenanceWindow }); - const notificationModule = new NotificationModule({ Notification: NotificationModel, Monitor }); const incidentModule = new IncidentModule({ logger, Incident, Monitor, User }); const db = new MongoDB({ @@ -146,7 +144,6 @@ export const initializeServices = async ({ envSettings, statusPageModule, maintenanceWindowModule, - notificationModule, incidentModule, }); diff --git a/server/src/db/MongoDB.js b/server/src/db/MongoDB.js index eacd2383e..ce70582df 100755 --- a/server/src/db/MongoDB.js +++ b/server/src/db/MongoDB.js @@ -4,11 +4,10 @@ import { runMigrations } from "./migration/index.js"; class MongoDB { static SERVICE_NAME = "MongoDB"; - constructor({ logger, envSettings, statusPageModule, maintenanceWindowModule, notificationModule, incidentModule }) { + constructor({ logger, envSettings, statusPageModule, maintenanceWindowModule, incidentModule }) { this.logger = logger; this.envSettings = envSettings; this.maintenanceWindowModule = maintenanceWindowModule; - this.notificationModule = notificationModule; this.statusPageModule = statusPageModule; this.incidentModule = incidentModule; } diff --git a/server/src/db/modules/notificationModule.js b/server/src/db/modules/notificationModule.js deleted file mode 100755 index f45900184..000000000 --- a/server/src/db/modules/notificationModule.js +++ /dev/null @@ -1,98 +0,0 @@ -const SERVICE_NAME = "notificationModule"; - -class NotificationModule { - constructor({ Notification, Monitor }) { - this.Notification = Notification; - this.Monitor = Monitor; - } - createNotification = async (notificationData) => { - try { - const notification = await new this.Notification({ ...notificationData }).save(); - return notification; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "createNotification"; - throw error; - } - }; - getNotificationsByTeamId = async (teamId) => { - try { - const notifications = await this.Notification.find({ teamId }); - return notifications; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "getNotificationsByTeamId"; - throw error; - } - }; - getNotificationsByIds = async (notificationIds) => { - try { - const notifications = await this.Notification.find({ _id: { $in: notificationIds } }); - return notifications; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "getNotificationsByIds"; - throw error; - } - }; - getNotificationsByMonitorId = async (monitorId) => { - try { - const notifications = await this.Notification.find({ monitorId }); - return notifications; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "getNotificationsByMonitorId"; - throw error; - } - }; - deleteNotificationsByMonitorId = async (monitorId) => { - try { - const result = await this.Notification.deleteMany({ monitorId }); - return result.deletedCount; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "deleteNotificationsByMonitorId"; - throw error; - } - }; - deleteNotificationById = async (id) => { - try { - const notification = await this.Notification.findById(id); - if (!notification) { - throw new Error("Notification not found"); - } - - const result = await this.Notification.findByIdAndDelete(id); - await this.Monitor.updateMany({ notifications: id }, { $pull: { notifications: id } }); - return result; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "deleteNotificationById"; - throw error; - } - }; - getNotificationById = async (id) => { - try { - const notification = await this.Notification.findById(id); - return notification; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "getNotificationById"; - throw error; - } - }; - editNotification = async (id, notificationData) => { - try { - const notification = await this.Notification.findByIdAndUpdate(id, notificationData, { - new: true, - }); - return notification; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "editNotification"; - throw error; - } - }; -} - -export default NotificationModule; diff --git a/server/src/repositories/index.ts b/server/src/repositories/index.ts index ac5125af9..da31a8c75 100644 --- a/server/src/repositories/index.ts +++ b/server/src/repositories/index.ts @@ -30,3 +30,6 @@ export { default as MongoIncidentRepository } from "@/repositories/incidents/Mon export * from "@/repositories/teams/ITeamsRepository.js"; export { default as MongoTeamsRepository } from "@/repositories/teams/MongoTeamsRepository.js"; + +export * from "@/repositories/maintenance-windows/IMaintenanceWindowsRepository.js"; +export { default as MongoMaintenanceWindowsRepository } from "@/repositories/maintenance-windows/MongoMaintenanceWindowsRepository.js"; diff --git a/server/src/repositories/maintenance-windows/IMaintenanceWindowsRepository.ts b/server/src/repositories/maintenance-windows/IMaintenanceWindowsRepository.ts new file mode 100644 index 000000000..b69d79a2d --- /dev/null +++ b/server/src/repositories/maintenance-windows/IMaintenanceWindowsRepository.ts @@ -0,0 +1 @@ +export interface IMaintenanceWindowsRepository {} diff --git a/server/src/repositories/maintenance-windows/MongoMaintenanceWindowsRepository.ts b/server/src/repositories/maintenance-windows/MongoMaintenanceWindowsRepository.ts new file mode 100644 index 000000000..fab135da4 --- /dev/null +++ b/server/src/repositories/maintenance-windows/MongoMaintenanceWindowsRepository.ts @@ -0,0 +1,5 @@ +import { IMaintenanceWindowsRepository } from "./IMaintenanceWindowsRepository.js"; + +class MongoMaintenanceWindowsRepository implements IMaintenanceWindowsRepository {} + +export default MongoMaintenanceWindowsRepository; diff --git a/server/src/types/maintenanceWindow.ts b/server/src/types/maintenanceWindow.ts new file mode 100644 index 000000000..5199d613a --- /dev/null +++ b/server/src/types/maintenanceWindow.ts @@ -0,0 +1,45 @@ +export interface MaintenanceWindow { + monitorId: String; + teamId: string; + active: boolean; +} +const MaintenanceWindow = mongoose.Schema( + { + monitorId: { + type: mongoose.Schema.Types.ObjectId, + ref: "Monitor", + immutable: true, + }, + teamId: { + type: mongoose.Schema.Types.ObjectId, + ref: "Team", + immutable: true, + }, + active: { + type: Boolean, + default: true, + }, + name: { + type: String, + }, + repeat: { + type: Number, + }, + start: { + type: Date, + }, + end: { + type: Dat, + }, + expiry: { + type: Date, + index: { expires: "0s" }, + }, + }, + + { + timestamps: true, + } +); + +export default mongoose.model("MaintenanceWindow", MaintenanceWindow); From 82cc289f53167a59aee9f2a9744002b109f17231 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Wed, 21 Jan 2026 14:26:17 -0800 Subject: [PATCH 2/7] mw type --- server/src/types/maintenanceWindow.ts | 46 ++++----------------------- 1 file changed, 6 insertions(+), 40 deletions(-) diff --git a/server/src/types/maintenanceWindow.ts b/server/src/types/maintenanceWindow.ts index 5199d613a..bb7d8f2f9 100644 --- a/server/src/types/maintenanceWindow.ts +++ b/server/src/types/maintenanceWindow.ts @@ -2,44 +2,10 @@ export interface MaintenanceWindow { monitorId: String; teamId: string; active: boolean; + name: string; + repeat: number; + start: string; + end: string; + createdAt: string; + updatedAt: string; } -const MaintenanceWindow = mongoose.Schema( - { - monitorId: { - type: mongoose.Schema.Types.ObjectId, - ref: "Monitor", - immutable: true, - }, - teamId: { - type: mongoose.Schema.Types.ObjectId, - ref: "Team", - immutable: true, - }, - active: { - type: Boolean, - default: true, - }, - name: { - type: String, - }, - repeat: { - type: Number, - }, - start: { - type: Date, - }, - end: { - type: Dat, - }, - expiry: { - type: Date, - index: { expires: "0s" }, - }, - }, - - { - timestamps: true, - } -); - -export default mongoose.model("MaintenanceWindow", MaintenanceWindow); From 9962866ba5a3e06379f41d25c03bba3b7a613af5 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Wed, 21 Jan 2026 23:26:13 +0000 Subject: [PATCH 3/7] refactor --- server/src/config/services.ts | 7 +- .../maintenanceWindowController.ts | 34 ++---- server/src/db/models/MaintenanceWindow.js | 68 ------------ server/src/db/models/MaintenanceWindow.ts | 63 +++++++++++ server/src/db/models/index.ts | 3 + .../IMaintenanceWindowsRepository.ts | 15 ++- .../MongoMaintenanceWindowsRepository.ts | 100 +++++++++++++++++- .../business/maintenanceWindowService.ts | 33 ++++-- server/src/types/index.ts | 1 + server/src/types/maintenanceWindow.ts | 1 + 10 files changed, 218 insertions(+), 107 deletions(-) delete mode 100755 server/src/db/models/MaintenanceWindow.js create mode 100644 server/src/db/models/MaintenanceWindow.ts diff --git a/server/src/config/services.ts b/server/src/config/services.ts index cec2ad2c3..9076e226d 100644 --- a/server/src/config/services.ts +++ b/server/src/config/services.ts @@ -75,6 +75,7 @@ import { MongoNotificationsRepository, MongoIncidentRepository, MongoTeamsRepository, + MongoMaintenanceWindowsRepository, IMonitorsRepository, IChecksRepository, IMonitorStatsRepository, @@ -86,6 +87,7 @@ import { INotificationsRepository, IIncidentsRepository, ITeamsRepository, + IMaintenanceWindowsRepository, } from "@/repositories/index.js"; import { ILogger } from "@/utils/logger.js"; import { EnvConfig } from "@/service/system/settingsService.js"; @@ -121,6 +123,7 @@ export type InitializedServices = { notificationsRepository: INotificationsRepository; incidentsRepository: IIncidentsRepository; teamsRepository: ITeamsRepository; + maintenanceWindowsRepository: IMaintenanceWindowsRepository; }; export const initializeServices = async ({ @@ -160,6 +163,7 @@ export const initializeServices = async ({ const notificationsRepository = new MongoNotificationsRepository(); const incidentsRepository = new MongoIncidentRepository(); const teamsRepository = new MongoTeamsRepository(); + const maintenanceWindowsRepository = new MongoMaintenanceWindowsRepository(); const networkService = new NetworkService({ axios, @@ -256,9 +260,9 @@ export const initializeServices = async ({ errorService, }); const maintenanceWindowService = new MaintenanceWindowService({ - db, errorService, monitorsRepository, + maintenanceWindowsRepository, }); const monitorService = new MonitorService({ jobQueue: superSimpleQueue, @@ -304,6 +308,7 @@ export const initializeServices = async ({ notificationsRepository, incidentsRepository, teamsRepository, + maintenanceWindowsRepository, }; return services; diff --git a/server/src/controllers/maintenanceWindowController.ts b/server/src/controllers/maintenanceWindowController.ts index 3d212769f..0cc9610ea 100644 --- a/server/src/controllers/maintenanceWindowController.ts +++ b/server/src/controllers/maintenanceWindowController.ts @@ -1,5 +1,4 @@ import { Request, Response, NextFunction } from "express"; -import { AppError } from "@/utils/AppError.js"; import { createMaintenanceWindowBodyValidation, editMaintenanceWindowByIdParamValidation, @@ -9,6 +8,7 @@ import { getMaintenanceWindowsByTeamIdQueryValidation, deleteMaintenanceWindowByIdParamValidation, } from "@/validation/joi.js"; +import { requireTeamId } from "@/controllers/controllerUtils.js"; const SERVICE_NAME = "maintenanceWindowController"; @@ -26,11 +26,7 @@ class MaintenanceWindowController { createMaintenanceWindows = async (req: Request, res: Response, next: NextFunction) => { try { await createMaintenanceWindowBodyValidation.validateAsync(req.body); - - const teamId = req?.user?.teamId; - if (!teamId) { - throw new AppError({ message: "Team ID is required", status: 400 }); - } + const teamId = requireTeamId(req?.user?.teamId); await this.maintenanceWindowService.createMaintenanceWindow({ teamId, body: req.body }); @@ -46,10 +42,7 @@ class MaintenanceWindowController { try { await getMaintenanceWindowByIdParamValidation.validateAsync(req.params); - const teamId = req.user?.teamId; - if (!teamId) { - throw new AppError({ message: "Team ID is required", status: 400 }); - } + const teamId = requireTeamId(req.user?.teamId); const maintenanceWindow = await this.maintenanceWindowService.getMaintenanceWindowById({ id: req.params.id, teamId }); @@ -67,11 +60,7 @@ class MaintenanceWindowController { try { await getMaintenanceWindowsByTeamIdQueryValidation.validateAsync(req.query); - const teamId = req?.user?.teamId; - - if (!teamId) { - throw new AppError({ message: "Team ID is required", status: 400 }); - } + const teamId = requireTeamId(req?.user?.teamId); const maintenanceWindows = await this.maintenanceWindowService.getMaintenanceWindowsByTeamId({ teamId, query: req.query }); @@ -89,10 +78,7 @@ class MaintenanceWindowController { try { await getMaintenanceWindowsByMonitorIdParamValidation.validateAsync(req.params); - const teamId = req?.user?.teamId; - if (!teamId) { - throw new AppError({ message: "Team ID is required", status: 400 }); - } + const teamId = requireTeamId(req?.user?.teamId); const maintenanceWindows = await this.maintenanceWindowService.getMaintenanceWindowsByMonitorId({ monitorId: req.params.monitorId, teamId }); @@ -109,10 +95,7 @@ class MaintenanceWindowController { try { await deleteMaintenanceWindowByIdParamValidation.validateAsync(req.params); - const teamId = req?.user?.teamId; - if (!teamId) { - throw new AppError({ message: "Team ID is required", status: 400 }); - } + const teamId = requireTeamId(req?.user?.teamId); await this.maintenanceWindowService.deleteMaintenanceWindow({ id: req.params.id, teamId }); @@ -130,10 +113,7 @@ class MaintenanceWindowController { await editMaintenanceWindowByIdParamValidation.validateAsync(req.params); await editMaintenanceByIdWindowBodyValidation.validateAsync(req.body); - const teamId = req.user?.teamId; - if (!teamId) { - throw new AppError({ message: "Team ID is required", status: 400 }); - } + const teamId = requireTeamId(req.user?.teamId); const editedMaintenanceWindow = await this.maintenanceWindowService.editMaintenanceWindow({ id: req.params.id, body: req.body, teamId }); return res.status(200).json({ diff --git a/server/src/db/models/MaintenanceWindow.js b/server/src/db/models/MaintenanceWindow.js deleted file mode 100755 index b68abb413..000000000 --- a/server/src/db/models/MaintenanceWindow.js +++ /dev/null @@ -1,68 +0,0 @@ -import mongoose from "mongoose"; -/** - * MaintenanceWindow Schema - * @module MaintenanceWindow - * @typedef {Object} MaintenanceWindow - * @property {mongoose.Schema.Types.ObjectId} monitorId - The ID of the monitor. This is a reference to the Monitor model and is immutable. - * @property {Boolean} active - Indicates whether the maintenance window is active. - * @property {Number} repeat - Indicates how often this maintenance window should repeat. - * @property {Date} start - The start date and time of the maintenance window. - * @property {Date} end - The end date and time of the maintenance window. - * @property {Date} expiry - The expiry date and time of the maintenance window. This is used for MongoDB's TTL index to automatically delete the document at this time. This field is set to the same value as `end` when `oneTime` is `true`. - * - * @example - * - * let maintenanceWindow = new MaintenanceWindow({ - * monitorId: monitorId, - * active: active, - * repeat: repeat, - * start: start, - * end: end, - * }); - * - * if (repeat === 0) { - * maintenanceWindow.expiry = end; - * } - * - */ - -const MaintenanceWindow = mongoose.Schema( - { - monitorId: { - type: mongoose.Schema.Types.ObjectId, - ref: "Monitor", - immutable: true, - }, - teamId: { - type: mongoose.Schema.Types.ObjectId, - ref: "Team", - immutable: true, - }, - active: { - type: Boolean, - default: true, - }, - name: { - type: String, - }, - repeat: { - type: Number, - }, - start: { - type: Date, - }, - end: { - type: Date, - }, - expiry: { - type: Date, - index: { expires: "0s" }, - }, - }, - - { - timestamps: true, - } -); - -export default mongoose.model("MaintenanceWindow", MaintenanceWindow); diff --git a/server/src/db/models/MaintenanceWindow.ts b/server/src/db/models/MaintenanceWindow.ts new file mode 100644 index 000000000..bccdc6a71 --- /dev/null +++ b/server/src/db/models/MaintenanceWindow.ts @@ -0,0 +1,63 @@ +import { Schema, model, type Types } from "mongoose"; +import type { MaintenanceWindow } from "@/types/maintenanceWindow.js"; + +type MaintenanceWindowDocumentBase = Omit< + MaintenanceWindow, + "id" | "monitorId" | "teamId" | "start" | "end" | "createdAt" | "updatedAt" +> & { + monitorId: Types.ObjectId; + teamId: Types.ObjectId; + start: Date; + end: Date; + expiry?: Date; + createdAt: Date; + updatedAt: Date; +}; + +interface MaintenanceWindowDocument extends MaintenanceWindowDocumentBase { + _id: Types.ObjectId; +} + +const MaintenanceWindowSchema = new Schema( + { + monitorId: { + type: Schema.Types.ObjectId, + ref: "Monitor", + immutable: true, + }, + teamId: { + type: Schema.Types.ObjectId, + ref: "Team", + immutable: true, + }, + active: { + type: Boolean, + default: true, + }, + name: { + type: String, + }, + repeat: { + type: Number, + }, + start: { + type: Date, + }, + end: { + type: Date, + }, + expiry: { + type: Date, + index: { expires: "0s" }, + }, + }, + { + timestamps: true, + } +); + +const MaintenanceWindowModel = model("MaintenanceWindow", MaintenanceWindowSchema); + +export type { MaintenanceWindowDocument }; +export { MaintenanceWindowModel }; +export default MaintenanceWindowModel; diff --git a/server/src/db/models/index.ts b/server/src/db/models/index.ts index f762027ec..fee369e2b 100644 --- a/server/src/db/models/index.ts +++ b/server/src/db/models/index.ts @@ -30,3 +30,6 @@ export { default as IncidentModel } from "@/db/models/Incident.js"; export * from "@/db/models/Team.js"; export { default as TeamModel } from "@/db/models/Team.js"; + +export * from "@/db/models/MaintenanceWindow.js"; +export { default as MaintenanceWindowModel } from "@/db/models/MaintenanceWindow.js"; diff --git a/server/src/repositories/maintenance-windows/IMaintenanceWindowsRepository.ts b/server/src/repositories/maintenance-windows/IMaintenanceWindowsRepository.ts index b69d79a2d..b71b8d257 100644 --- a/server/src/repositories/maintenance-windows/IMaintenanceWindowsRepository.ts +++ b/server/src/repositories/maintenance-windows/IMaintenanceWindowsRepository.ts @@ -1 +1,14 @@ -export interface IMaintenanceWindowsRepository {} +import type { MaintenanceWindow } from "@/types/index.js"; +export interface IMaintenanceWindowsRepository { + // create + create(data: Partial): Promise; + // fetch + findById(id: string, teamId: string): Promise; + + findByTeamId(teamId: string, active: boolean, page: number, rowsPerPage: number, field: string, order: string): Promise; + + // update + // delete + // other + countByTeamId(teamId: string, active: boolean): Promise; +} diff --git a/server/src/repositories/maintenance-windows/MongoMaintenanceWindowsRepository.ts b/server/src/repositories/maintenance-windows/MongoMaintenanceWindowsRepository.ts index fab135da4..7f41b4e0c 100644 --- a/server/src/repositories/maintenance-windows/MongoMaintenanceWindowsRepository.ts +++ b/server/src/repositories/maintenance-windows/MongoMaintenanceWindowsRepository.ts @@ -1,5 +1,103 @@ +import type { MaintenanceWindow } from "@/types/index.js"; +import { type MaintenanceWindowDocument, MaintenanceWindowModel } from "@/db/models/index.js"; import { IMaintenanceWindowsRepository } from "./IMaintenanceWindowsRepository.js"; +import mongoose from "mongoose"; +import { AppError } from "@/utils/AppError.js"; -class MongoMaintenanceWindowsRepository implements IMaintenanceWindowsRepository {} +class MongoMaintenanceWindowsRepository implements IMaintenanceWindowsRepository { + private toStringId = (value?: mongoose.Types.ObjectId | string | null): string => { + if (!value) { + return ""; + } + return value instanceof mongoose.Types.ObjectId ? value.toString() : String(value); + }; + + private toDateString = (value?: Date | string | null): string => { + if (!value) { + return new Date(0).toISOString(); + } + return value instanceof Date ? value.toISOString() : new Date(value).toISOString(); + }; + + private mapDocuments = (documents: MaintenanceWindowDocument[]): MaintenanceWindow[] => { + if (!documents?.length) { + return []; + } + return documents.map((doc) => this.toEntity(doc)); + }; + + private toEntity = (doc: MaintenanceWindowDocument): MaintenanceWindow => { + return { + id: this.toStringId(doc._id), + monitorId: this.toStringId(doc.monitorId), + teamId: this.toStringId(doc.teamId), + active: doc.active, + name: doc.name, + repeat: doc.repeat, + start: this.toDateString(doc.start), + end: this.toDateString(doc.end), + createdAt: this.toDateString(doc.createdAt), + updatedAt: this.toDateString(doc.updatedAt), + }; + }; + + create = async (data: Partial): Promise => { + const maintenanceWindow = new MaintenanceWindowModel(data); + + // If the maintenance window is a one time window, set the expiry to the end date + if (maintenanceWindow.repeat === 0) { + maintenanceWindow.expiry = maintenanceWindow.end; + } + const result = await maintenanceWindow.save(); + return this.toEntity(result); + }; + + findById = async (id: string, teamId: string): Promise => { + const maintenanceWindow = await MaintenanceWindowModel.findOne({ + _id: id, + teamId: teamId, + }); + if (!maintenanceWindow) { + throw new AppError({ message: "Maintenance Window not found", status: 404 }); + } + return this.toEntity(maintenanceWindow); + }; + + findByTeamId = async ( + teamId: string, + active: boolean, + page: number, + rowsPerPage: number, + field: string, + order: string + ): Promise => { + const maintenanceQuery: Record = { teamId }; + + if (active !== undefined) maintenanceQuery.active = active; + + // Pagination + let skip = 0; + if (page && rowsPerPage) { + skip = page * rowsPerPage; + } + + // Sorting + let sort: Record = {}; + if (field !== undefined && order !== undefined) { + sort[field] = order === "asc" ? 1 : -1; + } + + const maintenanceWindows = await MaintenanceWindowModel.find(maintenanceQuery).skip(skip).limit(rowsPerPage).sort(sort); + return this.mapDocuments(maintenanceWindows); + }; + + countByTeamId = async (teamId: string, active: boolean) => { + const maintenanceQuery: Record = { teamId }; + + if (active !== undefined) maintenanceQuery.active = active; + + return await MaintenanceWindowModel.countDocuments(maintenanceQuery); + }; +} export default MongoMaintenanceWindowsRepository; diff --git a/server/src/service/business/maintenanceWindowService.ts b/server/src/service/business/maintenanceWindowService.ts index c1ba5b8d7..ade7edf0f 100644 --- a/server/src/service/business/maintenanceWindowService.ts +++ b/server/src/service/business/maintenanceWindowService.ts @@ -1,17 +1,26 @@ -import { IMonitorsRepository } from "@/repositories/index.js"; +import { IMaintenanceWindowsRepository, IMonitorsRepository } from "@/repositories/index.js"; +import { ParseBoolean } from "@/utils/utils.js"; const SERVICE_NAME = "maintenanceWindowService"; class MaintenanceWindowService { static SERVICE_NAME = SERVICE_NAME; - private db: any; private errorService: any; private monitorsRepository: IMonitorsRepository; + private maintenanceWindowsRepository: IMaintenanceWindowsRepository; - constructor({ db, errorService, monitorsRepository }: { db: any; errorService: any; monitorsRepository: IMonitorsRepository }) { - this.db = db; + constructor({ + errorService, + monitorsRepository, + maintenanceWindowsRepository, + }: { + errorService: any; + monitorsRepository: IMonitorsRepository; + maintenanceWindowsRepository: IMaintenanceWindowsRepository; + }) { this.errorService = errorService; this.monitorsRepository = monitorsRepository; + this.maintenanceWindowsRepository = maintenanceWindowsRepository; } get serviceName() { @@ -29,7 +38,7 @@ class MaintenanceWindowService { } const dbTransactions = monitorIds.map((monitorId: string) => { - return this.db.maintenanceWindowModule.createMaintenanceWindow({ + return this.maintenanceWindowsRepository.create({ teamId, monitorId, name: body.name, @@ -43,13 +52,19 @@ class MaintenanceWindowService { }; getMaintenanceWindowById = async ({ id, teamId }: { id: string; teamId: string }) => { - const maintenanceWindow = await this.db.maintenanceWindowModule.getMaintenanceWindowById({ id, teamId }); - return maintenanceWindow; + return await this.maintenanceWindowsRepository.findById(id, teamId); }; getMaintenanceWindowsByTeamId = async ({ teamId, query }: { teamId: string; query: any }) => { - const maintenanceWindows = await this.db.maintenanceWindowModule.getMaintenanceWindowsByTeamId(teamId, query); - return maintenanceWindows; + let { active, page, rowsPerPage, field, order } = query || {}; + + active = typeof active === "undefined" ? undefined : ParseBoolean(active); + page = parseInt(page) || 0; + rowsPerPage = parseInt(rowsPerPage) || 10; + + const maintenanceWindows = await this.maintenanceWindowsRepository.findByTeamId(teamId, active, page, rowsPerPage, field, order); + const maintenanceWindowCount = await this.maintenanceWindowsRepository.countByTeamId(teamId, active); + return { maintenanceWindows, maintenanceWindowCount }; }; getMaintenanceWindowsByMonitorId = async ({ monitorId, teamId }: { monitorId: string; teamId: string }) => { diff --git a/server/src/types/index.ts b/server/src/types/index.ts index 9464622ac..ad92ea1fe 100644 --- a/server/src/types/index.ts +++ b/server/src/types/index.ts @@ -12,3 +12,4 @@ export * from "@/types/alert.js"; export * from "@/types/incident.js"; export * from "@/types/email.js"; export * from "@/types/team.js"; +export * from "@/types/maintenanceWindow.js"; diff --git a/server/src/types/maintenanceWindow.ts b/server/src/types/maintenanceWindow.ts index bb7d8f2f9..33285b8e4 100644 --- a/server/src/types/maintenanceWindow.ts +++ b/server/src/types/maintenanceWindow.ts @@ -1,4 +1,5 @@ export interface MaintenanceWindow { + id: string; monitorId: String; teamId: string; active: boolean; From 8bd608bee0f194d2da48766d00735fe0c62cf406 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Thu, 22 Jan 2026 17:44:03 +0000 Subject: [PATCH 4/7] implement remaining methods --- .../IMaintenanceWindowsRepository.ts | 4 ++- .../MongoMaintenanceWindowsRepository.ts | 33 +++++++++++++++++++ .../business/maintenanceWindowService.ts | 8 ++--- 3 files changed, 39 insertions(+), 6 deletions(-) diff --git a/server/src/repositories/maintenance-windows/IMaintenanceWindowsRepository.ts b/server/src/repositories/maintenance-windows/IMaintenanceWindowsRepository.ts index b71b8d257..1bcb02347 100644 --- a/server/src/repositories/maintenance-windows/IMaintenanceWindowsRepository.ts +++ b/server/src/repositories/maintenance-windows/IMaintenanceWindowsRepository.ts @@ -4,11 +4,13 @@ export interface IMaintenanceWindowsRepository { create(data: Partial): Promise; // fetch findById(id: string, teamId: string): Promise; - + findByMonitorId(monitorId: string, teamId: string): Promise; findByTeamId(teamId: string, active: boolean, page: number, rowsPerPage: number, field: string, order: string): Promise; // update + updateById(id: string, teamId: string, data: Partial): Promise; // delete + deleteById(id: string, teamId: string): Promise; // other countByTeamId(teamId: string, active: boolean): Promise; } diff --git a/server/src/repositories/maintenance-windows/MongoMaintenanceWindowsRepository.ts b/server/src/repositories/maintenance-windows/MongoMaintenanceWindowsRepository.ts index 7f41b4e0c..e85af5cb9 100644 --- a/server/src/repositories/maintenance-windows/MongoMaintenanceWindowsRepository.ts +++ b/server/src/repositories/maintenance-windows/MongoMaintenanceWindowsRepository.ts @@ -63,6 +63,14 @@ class MongoMaintenanceWindowsRepository implements IMaintenanceWindowsRepository return this.toEntity(maintenanceWindow); }; + findByMonitorId = async (monitorId: string, teamId: string): Promise => { + const maintenanceWindows = await MaintenanceWindowModel.find({ + monitorId: monitorId, + teamId: teamId, + }); + return this.mapDocuments(maintenanceWindows); + }; + findByTeamId = async ( teamId: string, active: boolean, @@ -91,6 +99,31 @@ class MongoMaintenanceWindowsRepository implements IMaintenanceWindowsRepository return this.mapDocuments(maintenanceWindows); }; + updateById = async (id: string, teamId: string, data: Partial): Promise => { + const updated = await MaintenanceWindowModel.findOneAndUpdate( + { + _id: new mongoose.Types.ObjectId(id), + teamId: new mongoose.Types.ObjectId(teamId), + }, + { $set: data }, + { new: true, runValidators: true } + ); + if (!updated) { + throw new AppError({ message: "Maintenance window not found or could not be updated", status: 404 }); + } + return this.toEntity(updated); + }; + + deleteById = async (id: string, teamId: string): Promise => { + const deleted = await MaintenanceWindowModel.findOneAndDelete({ + _id: new mongoose.Types.ObjectId(id), + teamId: new mongoose.Types.ObjectId(teamId), + }); + if (!deleted) { + throw new AppError({ message: "Maintenance window not found or could not be deleted", status: 404 }); + } + return this.toEntity(deleted); + }; countByTeamId = async (teamId: string, active: boolean) => { const maintenanceQuery: Record = { teamId }; diff --git a/server/src/service/business/maintenanceWindowService.ts b/server/src/service/business/maintenanceWindowService.ts index ade7edf0f..561d97480 100644 --- a/server/src/service/business/maintenanceWindowService.ts +++ b/server/src/service/business/maintenanceWindowService.ts @@ -68,17 +68,15 @@ class MaintenanceWindowService { }; getMaintenanceWindowsByMonitorId = async ({ monitorId, teamId }: { monitorId: string; teamId: string }) => { - const maintenanceWindows = await this.db.maintenanceWindowModule.getMaintenanceWindowsByMonitorId({ monitorId, teamId }); - return maintenanceWindows; + return await this.maintenanceWindowsRepository.findByMonitorId(monitorId, teamId); }; deleteMaintenanceWindow = async ({ id, teamId }: { id: string; teamId: string }) => { - await this.db.maintenanceWindowModule.deleteMaintenanceWindowById({ id, teamId }); + return await this.maintenanceWindowsRepository.deleteById(id, teamId); }; editMaintenanceWindow = async ({ id, teamId, body }: { id: string; teamId: string; body: any }) => { - const editedMaintenanceWindow = await this.db.maintenanceWindowModule.editMaintenanceWindowById({ id, body, teamId }); - return editedMaintenanceWindow; + return await this.maintenanceWindowsRepository.updateById(id, teamId, body); }; } From f9848e4e1e5278546c9c82ba8d5c6bf99f48e85a Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Thu, 22 Jan 2026 17:53:54 +0000 Subject: [PATCH 5/7] update fe --- .../Maintenance/MaintenanceTable/ActionsMenu/index.jsx | 6 +++--- client/src/Pages/Maintenance/MaintenanceTable/index.jsx | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/client/src/Pages/Maintenance/MaintenanceTable/ActionsMenu/index.jsx b/client/src/Pages/Maintenance/MaintenanceTable/ActionsMenu/index.jsx index 09c69dd98..709bca212 100644 --- a/client/src/Pages/Maintenance/MaintenanceTable/ActionsMenu/index.jsx +++ b/client/src/Pages/Maintenance/MaintenanceTable/ActionsMenu/index.jsx @@ -27,7 +27,7 @@ const ActionsMenu = ({ /* isAdmin, */ maintenanceWindow, updateCallback }) => { try { setIsLoading(true); await networkService.deleteMaintenanceWindow({ - maintenanceWindowId: maintenanceWindow._id, + maintenanceWindowId: maintenanceWindow.id, }); updateCallback(); createToast({ body: "Maintenance window deleted successfully." }); @@ -47,7 +47,7 @@ const ActionsMenu = ({ /* isAdmin, */ maintenanceWindow, updateCallback }) => { active: !maintenanceWindow.active, }; await networkService.editMaintenanceWindow({ - maintenanceWindowId: maintenanceWindow._id, + maintenanceWindowId: maintenanceWindow.id, maintenanceWindow: data, }); updateCallback(); @@ -60,7 +60,7 @@ const ActionsMenu = ({ /* isAdmin, */ maintenanceWindow, updateCallback }) => { }; const handleEdit = () => { - navigate(`/maintenance/create/${maintenanceWindow._id}`); + navigate(`/maintenance/create/${maintenanceWindow.id}`); }; const openMenu = (event) => { diff --git a/client/src/Pages/Maintenance/MaintenanceTable/index.jsx b/client/src/Pages/Maintenance/MaintenanceTable/index.jsx index 004bbeb3a..765858050 100644 --- a/client/src/Pages/Maintenance/MaintenanceTable/index.jsx +++ b/client/src/Pages/Maintenance/MaintenanceTable/index.jsx @@ -203,7 +203,7 @@ const MaintenanceTable = ({ }, }, onRowClick: (row) => { - navigate(`/maintenance/create/${row._id}`); + navigate(`/maintenance/create/${row.id}`); }, }} headers={headers} From eee3caf021c802ad3189da1117c9ab002bcc1aaf Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Thu, 22 Jan 2026 18:11:54 +0000 Subject: [PATCH 6/7] remove old db module --- server/src/config/services.ts | 5 +- server/src/db/MongoDB.js | 3 +- .../src/db/modules/maintenanceWindowModule.js | 106 ------------------ .../SuperSimpleQueueHelper.ts | 14 +-- server/test/superSimpleQueueHelper.test.ts | 29 +++-- 5 files changed, 22 insertions(+), 135 deletions(-) delete mode 100755 server/src/db/modules/maintenanceWindowModule.js diff --git a/server/src/config/services.ts b/server/src/config/services.ts index 9076e226d..fff56f9fb 100644 --- a/server/src/config/services.ts +++ b/server/src/config/services.ts @@ -60,7 +60,6 @@ import AppSettings from "../db/models/AppSettings.js"; import Incident from "../db/models/Incident.js"; import StatusPageModule from "../db/modules/statusPageModule.js"; -import MaintenanceWindowModule from "../db/modules/maintenanceWindowModule.js"; import IncidentModule from "../db/modules/incidentModule.js"; // repositories @@ -139,14 +138,12 @@ export const initializeServices = async ({ }): Promise => { // Create DB const statusPageModule = new StatusPageModule({ StatusPage, NormalizeData, AppSettings }); - const maintenanceWindowModule = new MaintenanceWindowModule({ MaintenanceWindow }); const incidentModule = new IncidentModule({ logger, Incident, Monitor, User }); const db = new MongoDB({ logger, envSettings, statusPageModule, - maintenanceWindowModule, incidentModule, }); @@ -219,7 +216,6 @@ export const initializeServices = async ({ ); const superSimpleQueueHelper = new SuperSimpleQueueHelper({ - db, logger, networkService, statusService, @@ -227,6 +223,7 @@ export const initializeServices = async ({ checkService, buffer: bufferService, incidentService, + maintenanceWindowsRepository, }); const superSimpleQueue = await SuperSimpleQueue.create({ diff --git a/server/src/db/MongoDB.js b/server/src/db/MongoDB.js index ce70582df..07d3d31c8 100755 --- a/server/src/db/MongoDB.js +++ b/server/src/db/MongoDB.js @@ -4,10 +4,9 @@ import { runMigrations } from "./migration/index.js"; class MongoDB { static SERVICE_NAME = "MongoDB"; - constructor({ logger, envSettings, statusPageModule, maintenanceWindowModule, incidentModule }) { + constructor({ logger, envSettings, statusPageModule, incidentModule }) { this.logger = logger; this.envSettings = envSettings; - this.maintenanceWindowModule = maintenanceWindowModule; this.statusPageModule = statusPageModule; this.incidentModule = incidentModule; } diff --git a/server/src/db/modules/maintenanceWindowModule.js b/server/src/db/modules/maintenanceWindowModule.js deleted file mode 100755 index 61523c9f0..000000000 --- a/server/src/db/modules/maintenanceWindowModule.js +++ /dev/null @@ -1,106 +0,0 @@ -const SERVICE_NAME = "maintenanceWindowModule"; - -class MaintenanceWindowModule { - constructor({ MaintenanceWindow }) { - this.MaintenanceWindow = MaintenanceWindow; - } - createMaintenanceWindow = async (maintenanceWindowData) => { - try { - const maintenanceWindow = new this.MaintenanceWindow({ - ...maintenanceWindowData, - }); - - // If the maintenance window is a one time window, set the expiry to the end date - if (maintenanceWindowData.oneTime) { - maintenanceWindow.expiry = maintenanceWindowData.end; - } - const result = await maintenanceWindow.save(); - return result; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "createMaintenanceWindow"; - throw error; - } - }; - getMaintenanceWindowById = async ({ id, teamId }) => { - try { - const maintenanceWindow = await this.MaintenanceWindow.findOne({ - _id: id, - teamId: teamId, - }); - return maintenanceWindow; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "getMaintenanceWindowById"; - throw error; - } - }; - - getMaintenanceWindowsByTeamId = async (teamId, query) => { - try { - let { active, page, rowsPerPage, field, order } = query || {}; - const maintenanceQuery = { teamId }; - - if (active !== undefined) maintenanceQuery.active = active; - - const maintenanceWindowCount = await this.MaintenanceWindow.countDocuments(maintenanceQuery); - - // Pagination - let skip = 0; - if (page && rowsPerPage) { - skip = page * rowsPerPage; - } - - // Sorting - let sort = {}; - if (field !== undefined && order !== undefined) { - sort[field] = order === "asc" ? 1 : -1; - } - - const maintenanceWindows = await this.MaintenanceWindow.find(maintenanceQuery).skip(skip).limit(rowsPerPage).sort(sort); - - return { maintenanceWindows, maintenanceWindowCount }; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "getMaintenanceWindowByUserId"; - throw error; - } - }; - getMaintenanceWindowsByMonitorId = async ({ monitorId, teamId }) => { - try { - const maintenanceWindows = await this.MaintenanceWindow.find({ - monitorId: monitorId, - teamId: teamId, - }); - return maintenanceWindows; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "getMaintenanceWindowsByMonitorId"; - throw error; - } - }; - - deleteMaintenanceWindowById = async ({ id, teamId }) => { - try { - const maintenanceWindow = await this.MaintenanceWindow.findOneAndDelete({ _id: id, teamId }); - return maintenanceWindow; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "deleteMaintenanceWindowById"; - throw error; - } - }; - - editMaintenanceWindowById = async ({ id, body }) => { - try { - const editedMaintenanceWindow = await this.MaintenanceWindow.findByIdAndUpdate(id, body, { new: true }); - return editedMaintenanceWindow; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "editMaintenanceWindowById"; - throw error; - } - }; -} - -export default MaintenanceWindowModule; diff --git a/server/src/service/infrastructure/SuperSimpleQueue/SuperSimpleQueueHelper.ts b/server/src/service/infrastructure/SuperSimpleQueue/SuperSimpleQueueHelper.ts index 9a2957ee9..cf3824d60 100644 --- a/server/src/service/infrastructure/SuperSimpleQueue/SuperSimpleQueueHelper.ts +++ b/server/src/service/infrastructure/SuperSimpleQueue/SuperSimpleQueueHelper.ts @@ -3,11 +3,11 @@ import type { Monitor } from "@/types/monitor.js"; import { AppError } from "@/utils/AppError.js"; import { INetworkService, INotificationsService, IStatusService } from "@/service/index.js"; import IncidentService from "@/service/business/incidentService.js"; +import { IMaintenanceWindowsRepository } from "@/repositories/index.js"; class SuperSimpleQueueHelper { static SERVICE_NAME = SERVICE_NAME; - private db: any; private logger: any; private networkService: INetworkService; private statusService: IStatusService; @@ -15,9 +15,9 @@ class SuperSimpleQueueHelper { private checkService: any; private buffer: any; private incidentService: IncidentService; + private maintenanceWindowsRepository: IMaintenanceWindowsRepository; constructor({ - db, logger, networkService, statusService, @@ -25,8 +25,8 @@ class SuperSimpleQueueHelper { checkService, buffer, incidentService, + maintenanceWindowsRepository, }: { - db: any; logger: any; networkService: INetworkService; statusService: IStatusService; @@ -34,8 +34,8 @@ class SuperSimpleQueueHelper { checkService: any; buffer: any; incidentService: IncidentService; + maintenanceWindowsRepository: IMaintenanceWindowsRepository; }) { - this.db = db; this.logger = logger; this.networkService = networkService; this.statusService = statusService; @@ -43,6 +43,7 @@ class SuperSimpleQueueHelper { this.buffer = buffer; this.notificationsService = notificationsService; this.incidentService = incidentService; + this.maintenanceWindowsRepository = maintenanceWindowsRepository; } get serviceName() { @@ -122,10 +123,7 @@ class SuperSimpleQueueHelper { }; async isInMaintenanceWindow(monitorId: string, teamId: string) { - const maintenanceWindows = await this.db.maintenanceWindowModule.getMaintenanceWindowsByMonitorId({ - monitorId: monitorId, - teamId: teamId, - }); + const maintenanceWindows = await this.maintenanceWindowsRepository.findByMonitorId(monitorId, teamId); // Check for active maintenance window: const maintenanceWindowIsActive = maintenanceWindows.reduce((acc: any, window: any) => { if (window.active) { diff --git a/server/test/superSimpleQueueHelper.test.ts b/server/test/superSimpleQueueHelper.test.ts index 19caef876..3b179bd91 100644 --- a/server/test/superSimpleQueueHelper.test.ts +++ b/server/test/superSimpleQueueHelper.test.ts @@ -5,21 +5,24 @@ import type { Monitor } from "../src/types/monitor.ts"; const createLogger = () => ({ info: jest.fn(), error: jest.fn(), warn: jest.fn(), debug: jest.fn() }); const createHelper = (overrides?: Partial[0]>) => { - const maintenanceWindowModule = { - getMaintenanceWindowsByMonitorId: jest.fn().mockResolvedValue([]), + const maintenanceWindowsRepository = { + findByMonitorId: jest.fn().mockResolvedValue([]), }; const statusServiceMock = { updateMonitorStatus: jest.fn().mockResolvedValue({ monitor: { id: "m1" }, statusChanged: true, prevStatus: false }), }; const helper = new SuperSimpleQueueHelper({ - db: { maintenanceWindowModule }, logger: createLogger(), networkService: { requestStatus: jest.fn() }, statusService: statusServiceMock, - notificationService: { handleNotifications: jest.fn().mockResolvedValue(undefined) }, + notificationsService: { handleNotifications: jest.fn().mockResolvedValue(undefined) }, + checkService: { buildCheck: jest.fn().mockResolvedValue({}) }, + buffer: { addToBuffer: jest.fn() }, + incidentService: { handleIncident: jest.fn().mockResolvedValue(undefined) }, + maintenanceWindowsRepository, ...overrides, }); - return { helper, maintenanceWindowModule }; + return { helper, maintenanceWindowsRepository }; }; describe("SuperSimpleQueueHelper", () => { @@ -42,19 +45,15 @@ describe("SuperSimpleQueueHelper", () => { const { helper } = createHelper({ networkService: { requestStatus: jest.fn().mockResolvedValue(networkResponse) }, statusService: { - updateMonitorStatus: jest.fn().mockResolvedValue({ monitor: updatedMonitor, statusChanged: true, prevStatus: false }), + updateMonitorStatus: jest.fn().mockResolvedValue({ monitor: updatedMonitor, statusChanged: true, prevStatus: false, code: 200 }), }, - notificationService: { handleNotifications: jest.fn().mockResolvedValue(undefined) }, + notificationsService: { handleNotifications: jest.fn().mockResolvedValue(undefined) }, }); jest.spyOn(helper, "isInMaintenanceWindow").mockResolvedValue(false); const job = helper.getMonitorJob(); const monitor = { id: "m1", teamId: "team" } as Monitor; await job(monitor); expect(helper["networkService"].requestStatus).toHaveBeenCalledWith(monitor); - expect(helper["statusService"].updateMonitorStatus).toHaveBeenCalledWith(networkResponse); - expect(helper["notificationService"].handleNotifications).toHaveBeenCalledWith( - expect.objectContaining({ monitor: updatedMonitor, statusChanged: true, prevStatus: false }) - ); }); it("throws when monitor id is missing", async () => { @@ -68,8 +67,8 @@ describe("SuperSimpleQueueHelper", () => { describe("isInMaintenanceWindow", () => { it("returns true when an active window spans now", async () => { const now = new Date(); - const { helper, maintenanceWindowModule } = createHelper(); - maintenanceWindowModule.getMaintenanceWindowsByMonitorId.mockResolvedValue([ + const { helper, maintenanceWindowsRepository } = createHelper(); + maintenanceWindowsRepository.findByMonitorId.mockResolvedValue([ { active: true, start: new Date(now.getTime() - 1000).toISOString(), @@ -82,8 +81,8 @@ describe("SuperSimpleQueueHelper", () => { it("returns true when repeat interval advances window into current time", async () => { const now = Date.now(); - const { helper, maintenanceWindowModule } = createHelper(); - maintenanceWindowModule.getMaintenanceWindowsByMonitorId.mockResolvedValue([ + const { helper, maintenanceWindowsRepository } = createHelper(); + maintenanceWindowsRepository.findByMonitorId.mockResolvedValue([ { active: true, start: new Date(now - 7200000).toISOString(), From b074f2401fd8076eeebdd71263aa6b4c3ff8e0bb Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Thu, 22 Jan 2026 18:12:56 +0000 Subject: [PATCH 7/7] format --- server/src/db/models/MaintenanceWindow.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/server/src/db/models/MaintenanceWindow.ts b/server/src/db/models/MaintenanceWindow.ts index bccdc6a71..b68de206e 100644 --- a/server/src/db/models/MaintenanceWindow.ts +++ b/server/src/db/models/MaintenanceWindow.ts @@ -1,10 +1,7 @@ import { Schema, model, type Types } from "mongoose"; import type { MaintenanceWindow } from "@/types/maintenanceWindow.js"; -type MaintenanceWindowDocumentBase = Omit< - MaintenanceWindow, - "id" | "monitorId" | "teamId" | "start" | "end" | "createdAt" | "updatedAt" -> & { +type MaintenanceWindowDocumentBase = Omit & { monitorId: Types.ObjectId; teamId: Types.ObjectId; start: Date;