diff --git a/server/src/controllers/maintenanceWindowController.ts b/server/src/controllers/maintenanceWindowController.ts index d4c99a549..ed16911bf 100644 --- a/server/src/controllers/maintenanceWindowController.ts +++ b/server/src/controllers/maintenanceWindowController.ts @@ -25,7 +25,7 @@ class MaintenanceWindowController { createMaintenanceWindows = async (req: Request, res: Response, next: NextFunction) => { try { - await createMaintenanceWindowBodyValidation.validateAsync(req.body); + createMaintenanceWindowBodyValidation.parse(req.body); const teamId = requireTeamId(req?.user?.teamId); await this.maintenanceWindowService.createMaintenanceWindow({ teamId, body: req.body }); @@ -40,7 +40,7 @@ class MaintenanceWindowController { }; getMaintenanceWindowById = async (req: Request, res: Response, next: NextFunction) => { try { - await getMaintenanceWindowByIdParamValidation.validateAsync(req.params); + getMaintenanceWindowByIdParamValidation.parse(req.params); const teamId = requireTeamId(req.user?.teamId); @@ -58,7 +58,7 @@ class MaintenanceWindowController { getMaintenanceWindowsByTeamId = async (req: Request, res: Response, next: NextFunction) => { try { - await getMaintenanceWindowsByTeamIdQueryValidation.validateAsync(req.query); + getMaintenanceWindowsByTeamIdQueryValidation.parse(req.query); const teamId = requireTeamId(req?.user?.teamId); @@ -76,7 +76,7 @@ class MaintenanceWindowController { getMaintenanceWindowsByMonitorId = async (req: Request, res: Response, next: NextFunction) => { try { - await getMaintenanceWindowsByMonitorIdParamValidation.validateAsync(req.params); + getMaintenanceWindowsByMonitorIdParamValidation.parse(req.params); const teamId = requireTeamId(req?.user?.teamId); @@ -96,7 +96,7 @@ class MaintenanceWindowController { }; deleteMaintenanceWindow = async (req: Request, res: Response, next: NextFunction) => { try { - await deleteMaintenanceWindowByIdParamValidation.validateAsync(req.params); + deleteMaintenanceWindowByIdParamValidation.parse(req.params); const teamId = requireTeamId(req?.user?.teamId); @@ -113,8 +113,8 @@ class MaintenanceWindowController { editMaintenanceWindow = async (req: Request, res: Response, next: NextFunction) => { try { - await editMaintenanceWindowByIdParamValidation.validateAsync(req.params); - await editMaintenanceByIdWindowBodyValidation.validateAsync(req.body); + editMaintenanceWindowByIdParamValidation.parse(req.params); + editMaintenanceByIdWindowBodyValidation.parse(req.body); const teamId = requireTeamId(req.user?.teamId); diff --git a/server/src/controllers/monitorController.ts b/server/src/controllers/monitorController.ts index 4dba18abc..1d222e6e2 100644 --- a/server/src/controllers/monitorController.ts +++ b/server/src/controllers/monitorController.ts @@ -45,7 +45,7 @@ class MonitorController { getMonitorCertificate = async (req: Request, res: Response, next: NextFunction) => { try { - await getCertificateParamValidation.validateAsync(req.params); + getCertificateParamValidation.parse(req.params); const teamId = requireTeamId(req?.user?.teamId); const monitorId = requireString(req.params?.monitorId, "Monitor ID"); const monitor = await this.monitorService.getMonitorById({ teamId, monitorId }); @@ -88,8 +88,8 @@ class MonitorController { getHardwareDetailsById = async (req: Request, res: Response, next: NextFunction) => { try { - await getHardwareDetailsByIdParamValidation.validateAsync(req.params); - await getHardwareDetailsByIdQueryValidation.validateAsync(req.query); + getHardwareDetailsByIdParamValidation.parse(req.params); + getHardwareDetailsByIdQueryValidation.parse(req.query); const monitorId = requireString(req?.params?.monitorId, "Monitor ID"); const dateRange = optionalString(req?.query?.dateRange, "dateRange") || "recent"; @@ -112,8 +112,8 @@ class MonitorController { }; getPageSpeedDetailsById = async (req: Request, res: Response, next: NextFunction) => { try { - await getHardwareDetailsByIdParamValidation.validateAsync(req.params); - await getHardwareDetailsByIdQueryValidation.validateAsync(req.query); + getHardwareDetailsByIdParamValidation.parse(req.params); + getHardwareDetailsByIdQueryValidation.parse(req.query); const monitorId = requireString(req?.params?.monitorId, "Monitor ID"); const dateRange = requireString(req?.query?.dateRange, "dateRange"); @@ -137,8 +137,8 @@ class MonitorController { getGeoChecksByMonitorId = async (req: Request, res: Response, next: NextFunction) => { try { - await getMonitorByIdParamValidation.validateAsync(req.params); - await getMonitorByIdQueryValidation.validateAsync(req.query); + getMonitorByIdParamValidation.parse(req.params); + getMonitorByIdQueryValidation.parse(req.query); const monitorId = requireString(req?.params?.monitorId, "Monitor ID"); const dateRange = requireString(req?.query?.dateRange, "dateRange"); @@ -169,8 +169,8 @@ class MonitorController { getMonitorById = async (req: Request, res: Response, next: NextFunction) => { try { - await getMonitorByIdParamValidation.validateAsync(req.params); - await getMonitorByIdQueryValidation.validateAsync(req.query); + getMonitorByIdParamValidation.parse(req.params); + getMonitorByIdQueryValidation.parse(req.query); const teamId = requireTeamId(req?.user?.teamId); const monitorId = requireString(req?.params?.monitorId, "Monitor ID"); @@ -189,7 +189,7 @@ class MonitorController { createMonitor = async (req: Request, res: Response, next: NextFunction) => { try { - await createMonitorBodyValidation.validateAsync(req.body); + createMonitorBodyValidation.parse(req.body); const userId = requireString(req?.user?.id, "User ID"); const teamId = requireTeamId(req?.user?.teamId); @@ -231,7 +231,7 @@ class MonitorController { deleteMonitor = async (req: Request, res: Response, next: NextFunction) => { try { - await getMonitorByIdParamValidation.validateAsync(req.params); + getMonitorByIdParamValidation.parse(req.params); const monitorId = requireString(req?.params?.monitorId, "Monitor ID"); const teamId = requireTeamId(req?.user?.teamId); @@ -264,8 +264,8 @@ class MonitorController { editMonitor = async (req: Request, res: Response, next: NextFunction) => { try { - await getMonitorByIdParamValidation.validateAsync(req.params); - await editMonitorBodyValidation.validateAsync(req.body); + getMonitorByIdParamValidation.parse(req.params); + editMonitorBodyValidation.parse(req.body); const monitorId = requireString(req?.params?.monitorId, "Monitor ID"); const teamId = requireTeamId(req?.user?.teamId); @@ -283,7 +283,7 @@ class MonitorController { pauseMonitor = async (req: Request, res: Response, next: NextFunction) => { try { - await pauseMonitorParamValidation.validateAsync(req.params); + pauseMonitorParamValidation.parse(req.params); const monitorId = requireString(req?.params?.monitorId, "Monitor ID"); const teamId = requireTeamId(req?.user?.teamId); @@ -336,8 +336,8 @@ class MonitorController { getMonitorsByTeamId = async (req: Request, res: Response, next: NextFunction) => { try { - await getMonitorsByTeamIdParamValidation.validateAsync(req.params); - await getMonitorsByTeamIdQueryValidation.validateAsync(req.query); + getMonitorsByTeamIdParamValidation.parse(req.params); + getMonitorsByTeamIdQueryValidation.parse(req.query); const teamId = requireTeamId(req?.user?.teamId); const type = parseMonitorTypeFilter(req.query?.type); @@ -357,8 +357,8 @@ class MonitorController { getMonitorsWithChecksByTeamId = async (req: Request, res: Response, next: NextFunction) => { try { - await getMonitorsByTeamIdParamValidation.validateAsync(req.params); - await getMonitorsWithChecksQueryValidation.validateAsync(req.query); + getMonitorsByTeamIdParamValidation.parse(req.params); + getMonitorsWithChecksQueryValidation.parse(req.query); const explain = optionalBoolean(req?.query?.explain, "explain"); const limit = optionalNumber(req?.query?.limit, "limit"); const page = optionalNumber(req?.query?.page, "page"); diff --git a/server/src/validation/checkValidation.ts b/server/src/validation/checkValidation.ts index 011507692..6c098bec7 100644 --- a/server/src/validation/checkValidation.ts +++ b/server/src/validation/checkValidation.ts @@ -5,6 +5,19 @@ import { GeoContinents } from "@/types/geoCheck.js"; // Check Validations //**************************************** +export const ackCheckBodyValidation = z.object({ + ack: z.coerce.boolean().optional(), +}); + +export const ackAllChecksParamValidation = z.object({ + monitorId: z.string().optional(), + path: z.enum(["monitor", "team"]), +}); + +export const ackAllChecksBodyValidation = z.object({ + ack: z.coerce.boolean().optional(), +}); + export const getChecksParamValidation = z.object({ monitorId: z.string().min(1, "Monitor ID is required"), }); @@ -12,24 +25,24 @@ export const getChecksParamValidation = z.object({ export const getChecksQueryValidation = z.object({ type: z.enum(["http", "ping", "pagespeed", "hardware", "docker", "port", "game", "grpc"]).optional(), sortOrder: z.enum(["asc", "desc"]).optional(), - limit: z.number().optional(), + limit: z.coerce.number().optional(), dateRange: z.enum(["recent", "hour", "day", "week", "month", "all"]).optional(), filter: z.enum(["all", "up", "down", "resolve"]).optional(), - ack: z.boolean().optional(), - page: z.number().optional(), - rowsPerPage: z.number().optional(), - status: z.boolean().optional(), + ack: z.coerce.boolean().optional(), + page: z.coerce.number().optional(), + rowsPerPage: z.coerce.number().optional(), + status: z.coerce.boolean().optional(), continent: z.union([z.enum(GeoContinents), z.array(z.enum(GeoContinents))]).optional(), }); export const getTeamChecksQueryValidation = z.object({ sortOrder: z.enum(["asc", "desc"]).optional(), - limit: z.number().optional(), + limit: z.coerce.number().optional(), dateRange: z.enum(["recent", "hour", "day", "week", "month", "all"]).optional(), filter: z.enum(["all", "up", "down", "resolve"]).optional(), - ack: z.boolean().optional(), - page: z.number().optional(), - rowsPerPage: z.number().optional(), + ack: z.coerce.boolean().optional(), + page: z.coerce.number().optional(), + rowsPerPage: z.coerce.number().optional(), }); export const deleteChecksParamValidation = z.object({ diff --git a/server/src/validation/maintenanceWindowValidation.ts b/server/src/validation/maintenanceWindowValidation.ts index ac3e32966..c1e65c733 100644 --- a/server/src/validation/maintenanceWindowValidation.ts +++ b/server/src/validation/maintenanceWindowValidation.ts @@ -1,53 +1,53 @@ -import joi from "joi"; +import { z } from "zod"; //**************************************** // Maintenance Window Validations //**************************************** -export const createMaintenanceWindowBodyValidation = joi.object({ - monitors: joi.array().items(joi.string()).required(), - name: joi.string().required(), - active: joi.boolean(), - duration: joi.number().required(), - durationUnit: joi.string().valid("seconds", "minutes", "hours", "days").required(), - start: joi.date().required(), - end: joi.date().required(), - repeat: joi.number().required(), - expiry: joi.date(), +export const createMaintenanceWindowBodyValidation = z.object({ + monitors: z.array(z.string()).min(1, "At least one monitor is required"), + name: z.string().min(1, "Name is required"), + active: z.boolean().optional(), + duration: z.number().min(1, "Duration is required"), + durationUnit: z.enum(["seconds", "minutes", "hours", "days"]), + start: z.coerce.date(), + end: z.coerce.date(), + repeat: z.number().min(0, "Repeat must be a non-negative number"), + expiry: z.coerce.date().optional(), }); -export const getMaintenanceWindowByIdParamValidation = joi.object({ - id: joi.string().required(), +export const getMaintenanceWindowByIdParamValidation = z.object({ + id: z.string().min(1, "ID is required"), }); -export const getMaintenanceWindowsByTeamIdQueryValidation = joi.object({ - active: joi.boolean(), - page: joi.number(), - rowsPerPage: joi.number(), - field: joi.string(), - order: joi.string().valid("asc", "desc"), +export const getMaintenanceWindowsByTeamIdQueryValidation = z.object({ + active: z.coerce.boolean().optional(), + page: z.coerce.number().optional(), + rowsPerPage: z.coerce.number().optional(), + field: z.string().optional(), + order: z.enum(["asc", "desc"]).optional(), }); -export const getMaintenanceWindowsByMonitorIdParamValidation = joi.object({ - monitorId: joi.string().required(), +export const getMaintenanceWindowsByMonitorIdParamValidation = z.object({ + monitorId: z.string().min(1, "Monitor ID is required"), }); -export const deleteMaintenanceWindowByIdParamValidation = joi.object({ - id: joi.string().required(), +export const deleteMaintenanceWindowByIdParamValidation = z.object({ + id: z.string().min(1, "ID is required"), }); -export const editMaintenanceWindowByIdParamValidation = joi.object({ - id: joi.string().required(), +export const editMaintenanceWindowByIdParamValidation = z.object({ + id: z.string().min(1, "ID is required"), }); -export const editMaintenanceByIdWindowBodyValidation = joi.object({ - active: joi.boolean(), - name: joi.string(), - repeat: joi.number(), - start: joi.date(), - end: joi.date(), - expiry: joi.date(), - monitors: joi.array(), - duration: joi.number(), - durationUnit: joi.string().valid("seconds", "minutes", "hours", "days"), +export const editMaintenanceByIdWindowBodyValidation = z.object({ + active: z.boolean().optional(), + name: z.string().optional(), + repeat: z.number().optional(), + start: z.coerce.date().optional(), + end: z.coerce.date().optional(), + expiry: z.coerce.date().optional(), + monitors: z.array(z.unknown()).optional(), + duration: z.number().optional(), + durationUnit: z.enum(["seconds", "minutes", "hours", "days"]).optional(), }); diff --git a/server/src/validation/monitorValidation.ts b/server/src/validation/monitorValidation.ts index 37c669ca3..0effa94a9 100644 --- a/server/src/validation/monitorValidation.ts +++ b/server/src/validation/monitorValidation.ts @@ -1,127 +1,120 @@ -import joi from "joi"; +import { z } from "zod"; import { GeoContinents } from "@/types/geoCheck.js"; -export const getMonitorByIdParamValidation = joi.object({ - monitorId: joi.string().required(), +export const getMonitorByIdParamValidation = z.object({ + monitorId: z.string().min(1, "Monitor ID is required"), }); -export const getMonitorByIdQueryValidation = joi.object({ - status: joi.boolean(), - sortOrder: joi.string().valid("asc", "desc"), - limit: joi.number(), - dateRange: joi.string().valid("recent", "hour", "day", "week", "month", "all"), - numToDisplay: joi.number(), - normalize: joi.boolean(), - continent: joi.string().valid(...GeoContinents), +export const getMonitorByIdQueryValidation = z.object({ + status: z.coerce.boolean().optional(), + sortOrder: z.enum(["asc", "desc"]).optional(), + limit: z.coerce.number().optional(), + dateRange: z.enum(["recent", "hour", "day", "week", "month", "all"]).optional(), + numToDisplay: z.coerce.number().optional(), + normalize: z.coerce.boolean().optional(), + continent: z.enum(GeoContinents).optional(), }); -export const getMonitorsByTeamIdParamValidation = joi.object({}); +export const getMonitorsByTeamIdParamValidation = z.object({}); -export const getMonitorsByTeamIdQueryValidation = joi.object({ - type: joi - .alternatives() - .try( - joi.string().valid("http", "ping", "pagespeed", "docker", "hardware", "port", "game", "grpc"), - joi.array().items(joi.string().valid("http", "ping", "pagespeed", "docker", "hardware", "port", "game", "grpc")) - ), - filter: joi.string().allow("", null), -}); - -export const getMonitorsWithChecksQueryValidation = joi.object({ - limit: joi.number().integer().min(1).max(100).optional(), - page: joi.number().integer().min(0).optional(), - rowsPerPage: joi.number().integer().min(1).max(100).optional(), - filter: joi.string().allow("", null).optional(), - field: joi.string().optional(), - order: joi.string().valid("asc", "desc").optional(), - type: joi - .alternatives() - .try( - joi.string().valid("http", "ping", "pagespeed", "docker", "hardware", "port", "game", "grpc"), - joi.array().items(joi.string().valid("http", "ping", "pagespeed", "docker", "hardware", "port", "game", "grpc")) - ) +export const getMonitorsByTeamIdQueryValidation = z.object({ + type: z + .union([ + z.enum(["http", "ping", "pagespeed", "docker", "hardware", "port", "game", "grpc"]), + z.array(z.enum(["http", "ping", "pagespeed", "docker", "hardware", "port", "game", "grpc"])), + ]) .optional(), - explain: joi.boolean().optional(), + filter: z.union([z.string(), z.literal(""), z.null()]).optional(), }); -export const getCertificateParamValidation = joi.object({ - monitorId: joi.string().required(), -}); - -export const createMonitorBodyValidation = joi.object({ - _id: joi.string(), - name: joi.string().required(), - description: joi.string().allow(null, ""), - type: joi.string().required(), - statusWindowSize: joi.number().min(1).max(20).default(5), - statusWindowThreshold: joi.number().min(1).max(100).default(60), - url: joi.string().required(), - ignoreTlsErrors: joi.boolean().default(false), - useAdvancedMatching: joi.boolean().default(false), - port: joi.number(), - isActive: joi.boolean(), - interval: joi.number(), - cpuAlertThreshold: joi.number(), - memoryAlertThreshold: joi.number(), - diskAlertThreshold: joi.number(), - tempAlertThreshold: joi.number(), - notifications: joi.array().items(joi.string()), - secret: joi.string(), - jsonPath: joi.string().allow(""), - expectedValue: joi.string().allow(""), - matchMethod: joi.string().allow(null, ""), - gameId: joi.string().allow(""), - grpcServiceName: joi.string().allow("").default(""), - selectedDisks: joi.array().items(joi.string()).optional(), - group: joi.string().max(50).trim().allow(null, "").optional(), - geoCheckEnabled: joi.boolean().optional(), - geoCheckLocations: joi - .array() - .items(joi.string().valid(...GeoContinents)) +export const getMonitorsWithChecksQueryValidation = z.object({ + limit: z.coerce.number().int().min(1).max(100).optional(), + page: z.coerce.number().int().min(0).optional(), + rowsPerPage: z.coerce.number().int().min(1).max(100).optional(), + filter: z.union([z.string(), z.literal(""), z.null()]).optional(), + field: z.string().optional(), + order: z.enum(["asc", "desc"]).optional(), + type: z + .union([ + z.enum(["http", "ping", "pagespeed", "docker", "hardware", "port", "game", "grpc"]), + z.array(z.enum(["http", "ping", "pagespeed", "docker", "hardware", "port", "game", "grpc"])), + ]) .optional(), - geoCheckInterval: joi.number().min(300000).optional(), + explain: z.coerce.boolean().optional(), }); -export const editMonitorBodyValidation = joi +export const getCertificateParamValidation = z.object({ + monitorId: z.string().min(1, "Monitor ID is required"), +}); + +export const createMonitorBodyValidation = z.object({ + _id: z.string().optional(), + name: z.string().min(1, "Name is required"), + description: z.union([z.string(), z.null(), z.literal("")]).optional(), + type: z.string().min(1, "Type is required"), + statusWindowSize: z.number().min(1).max(20).default(5), + statusWindowThreshold: z.number().min(1).max(100).default(60), + url: z.string().min(1, "URL is required"), + ignoreTlsErrors: z.boolean().default(false), + useAdvancedMatching: z.boolean().default(false), + port: z.number().optional(), + isActive: z.boolean().optional(), + interval: z.number().optional(), + cpuAlertThreshold: z.number().optional(), + memoryAlertThreshold: z.number().optional(), + diskAlertThreshold: z.number().optional(), + tempAlertThreshold: z.number().optional(), + notifications: z.array(z.string()).optional(), + secret: z.string().optional(), + jsonPath: z.union([z.string(), z.literal("")]).optional(), + expectedValue: z.union([z.string(), z.literal("")]).optional(), + matchMethod: z.union([z.string(), z.null(), z.literal("")]).optional(), + gameId: z.union([z.string(), z.literal("")]).optional(), + grpcServiceName: z.union([z.string(), z.literal("")]).default(""), + selectedDisks: z.array(z.string()).optional(), + group: z.union([z.string().max(50).trim(), z.null(), z.literal("")]).optional(), + geoCheckEnabled: z.boolean().optional(), + geoCheckLocations: z.array(z.enum(GeoContinents)).optional(), + geoCheckInterval: z.number().min(300000).optional(), +}); + +export const editMonitorBodyValidation = z .object({ - name: joi.string(), - statusWindowSize: joi.number().min(1).max(20).default(5), - statusWindowThreshold: joi.number().min(1).max(100).default(60), - description: joi.string().allow(null, ""), - interval: joi.number(), - notifications: joi.array().items(joi.string()), - secret: joi.string(), - ignoreTlsErrors: joi.boolean(), - useAdvancedMatching: joi.boolean(), - jsonPath: joi.string().allow(""), - expectedValue: joi.string().allow(""), - matchMethod: joi.string().allow(null, ""), - port: joi.number().min(1).max(65535), - cpuAlertThreshold: joi.number(), - memoryAlertThreshold: joi.number(), - diskAlertThreshold: joi.number(), - tempAlertThreshold: joi.number(), - gameId: joi.string().allow(""), - grpcServiceName: joi.string().allow(""), - selectedDisks: joi.array().items(joi.string()).optional(), - group: joi.string().max(50).trim().allow(null, "").optional(), - geoCheckEnabled: joi.boolean().optional(), - geoCheckLocations: joi - .array() - .items(joi.string().valid(...GeoContinents)) - .optional(), - geoCheckInterval: joi.number().min(300000).optional(), + name: z.string().optional(), + statusWindowSize: z.number().min(1).max(20).default(5), + statusWindowThreshold: z.number().min(1).max(100).default(60), + description: z.union([z.string(), z.null(), z.literal("")]).optional(), + interval: z.number().optional(), + notifications: z.array(z.string()).optional(), + secret: z.string().optional(), + ignoreTlsErrors: z.boolean().optional(), + useAdvancedMatching: z.boolean().optional(), + jsonPath: z.union([z.string(), z.literal("")]).optional(), + expectedValue: z.union([z.string(), z.literal("")]).optional(), + matchMethod: z.union([z.string(), z.null(), z.literal("")]).optional(), + port: z.number().min(1).max(65535).optional(), + cpuAlertThreshold: z.number().optional(), + memoryAlertThreshold: z.number().optional(), + diskAlertThreshold: z.number().optional(), + tempAlertThreshold: z.number().optional(), + gameId: z.union([z.string(), z.literal("")]).optional(), + grpcServiceName: z.union([z.string(), z.literal("")]).optional(), + selectedDisks: z.array(z.string()).optional(), + group: z.union([z.string().max(50).trim(), z.null(), z.literal("")]).optional(), + geoCheckEnabled: z.boolean().optional(), + geoCheckLocations: z.array(z.enum(GeoContinents)).optional(), + geoCheckInterval: z.number().min(300000).optional(), }) - .options({ stripUnknown: true }); + .strict(); -export const pauseMonitorParamValidation = joi.object({ - monitorId: joi.string().required(), +export const pauseMonitorParamValidation = z.object({ + monitorId: z.string().min(1, "Monitor ID is required"), }); -export const getHardwareDetailsByIdParamValidation = joi.object({ - monitorId: joi.string().required(), +export const getHardwareDetailsByIdParamValidation = z.object({ + monitorId: z.string().min(1, "Monitor ID is required"), }); -export const getHardwareDetailsByIdQueryValidation = joi.object({ - dateRange: joi.string().valid("recent", "hour", "day", "week", "month", "all"), +export const getHardwareDetailsByIdQueryValidation = z.object({ + dateRange: z.enum(["recent", "hour", "day", "week", "month", "all"]).optional(), });