From 56aa7b3e1af54c6cbe6c06b149e2d3ef46ab7ab3 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 2 Jan 2026 22:16:00 +0000 Subject: [PATCH] refactor service --- client/src/pages/docker/DockerMonitors.tsx | 2 +- .../src/pages/docker/DockerMonitorsTable.tsx | 12 +- .../pages/infrastructure/InfraMonitors.tsx | 2 +- .../infrastructure/InfraMonitorsTable.tsx | 18 +-- .../pages/pagespeed/PageSpeedMonitorTable.tsx | 12 +- .../src/pages/pagespeed/PageSpeedMonitors.tsx | 2 +- client/src/pages/uptime/UptimeDetails.tsx | 2 +- client/src/pages/uptime/UptimeMonitors.tsx | 2 +- .../src/pages/uptime/UptimeMonitorsTable.tsx | 14 +- client/src/types/monitor.ts | 3 +- client/src/utils/MonitorUtils.ts | 2 + server/src/init/services.ts | 4 +- .../repositories/checks/ICheckRepository.ts | 0 .../checks/MongoChecksRepository.ts | 58 -------- server/src/repositories/index.ts | 5 + .../monitors/IMonitorRepoistory.ts | 41 ++++++ .../monitors/MongoMonitorRepository.ts | 126 ++++++++++++++++++ .../src/services/business/MonitorService.ts | 102 +++++--------- server/src/types/domain/index.ts | 1 + server/src/types/domain/monitor.ts | 49 +++++++ .../src/types/monitor-response-with-checks.ts | 6 +- 21 files changed, 301 insertions(+), 162 deletions(-) create mode 100644 server/src/repositories/checks/ICheckRepository.ts delete mode 100644 server/src/repositories/checks/MongoChecksRepository.ts create mode 100644 server/src/repositories/index.ts create mode 100644 server/src/repositories/monitors/IMonitorRepoistory.ts create mode 100644 server/src/repositories/monitors/MongoMonitorRepository.ts create mode 100644 server/src/types/domain/index.ts create mode 100644 server/src/types/domain/monitor.ts diff --git a/client/src/pages/docker/DockerMonitors.tsx b/client/src/pages/docker/DockerMonitors.tsx index 56cb963d0..b71a42897 100644 --- a/client/src/pages/docker/DockerMonitors.tsx +++ b/client/src/pages/docker/DockerMonitors.tsx @@ -70,7 +70,7 @@ const DockerMonitorsPage = () => { const monitorLimitReached = useLimitReached("monitorsMax", count); const handleConfirm = async () => { - await deleteFn(`/monitors/${selectedMonitor?._id}`); + await deleteFn(`/monitors/${selectedMonitor?.id}`); setSelectedMonitor(null); refetch(); }; diff --git a/client/src/pages/docker/DockerMonitorsTable.tsx b/client/src/pages/docker/DockerMonitorsTable.tsx index 066d909dd..e7f77e770 100644 --- a/client/src/pages/docker/DockerMonitorsTable.tsx +++ b/client/src/pages/docker/DockerMonitorsTable.tsx @@ -58,17 +58,17 @@ export const DockerMonitorsTable = ({ { id: 2, label: t("monitors.common.actions.details"), - action: () => navigate(`/docker/${monitor._id}`), + action: () => navigate(`/docker/${monitor.id}`), }, { id: 3, label: t("monitors.common.actions.incidents"), - action: () => navigate(`/incidents?monitorId=${monitor._id}`), + action: () => navigate(`/incidents?monitorId=${monitor.id}`), }, { id: 4, label: t("monitors.common.actions.configure"), - action: () => navigate(`/docker/${monitor._id}/configure`), + action: () => navigate(`/docker/${monitor.id}/configure`), }, { id: 6, @@ -77,7 +77,7 @@ export const DockerMonitorsTable = ({ ? t("common.buttons.resume") : t("common.buttons.pause"), action: async () => { - await patch(`/monitors/${monitor._id}/active`); + await patch(`/monitors/${monitor.id}/active`); refetch(); }, closeMenu: true, @@ -124,7 +124,7 @@ export const DockerMonitorsTable = ({ ), render: (row) => { const containerCount = ( - checksMap[row._id]?.[0]?.dockerContainers || [] + checksMap[row.id]?.[0]?.dockerContainers || [] ).filter((c: any) => c?.running).length; return containerCount; }, @@ -145,7 +145,7 @@ export const DockerMonitorsTable = ({ navigate(`/docker/${row._id}`)} + onRowClick={(row) => navigate(`/docker/${row.id}`)} /> { const monitorLimitReached = useLimitReached("monitorsMax", count); const handleConfirm = async () => { - await deleteFn(`/monitors/${selectedMonitor?._id}`); + await deleteFn(`/monitors/${selectedMonitor?.id}`); setSelectedMonitor(null); refetch(); }; diff --git a/client/src/pages/infrastructure/InfraMonitorsTable.tsx b/client/src/pages/infrastructure/InfraMonitorsTable.tsx index 8035c2db0..8c3e17a99 100644 --- a/client/src/pages/infrastructure/InfraMonitorsTable.tsx +++ b/client/src/pages/infrastructure/InfraMonitorsTable.tsx @@ -97,21 +97,21 @@ export const InfraMonitorsTable = ({ id: 2, label: t("monitors.common.actions.details"), action: () => { - navigate(`${monitor._id}`); + navigate(`${monitor.id}`); }, }, { id: 3, label: t("monitors.common.actions.incidents"), action: () => { - navigate(`/incidents?monitorId=${monitor._id}`); + navigate(`/incidents?monitorId=${monitor.id}`); }, }, { id: 4, label: t("monitors.common.actions.configure"), action: () => { - navigate(`/infrastructure/${monitor._id}/configure`); + navigate(`/infrastructure/${monitor.id}/configure`); }, }, // { @@ -128,7 +128,7 @@ export const InfraMonitorsTable = ({ ? t("common.buttons.resume") : t("common.buttons.pause"), action: async () => { - await patch(`/monitors/${monitor._id}/active`); + await patch(`/monitors/${monitor.id}/active`); refetch(); }, closeMenu: true, @@ -208,7 +208,7 @@ export const InfraMonitorsTable = ({ content: t("monitors.infrastructure.table.headers.cpu"), render: (row) => { const cpuUsage = - (checksMap[row._id]?.[0]?.system?.cpu?.usage_percent || 0) * 100; + (checksMap[row.id]?.[0]?.system?.cpu?.usage_percent || 0) * 100; return ; }, }, @@ -217,7 +217,7 @@ export const InfraMonitorsTable = ({ content: t("monitors.infrastructure.table.headers.memory"), render: (row) => { const memoryUsage = - (checksMap[row._id]?.[0]?.system?.memory?.usage_percent || 0) * 100; + (checksMap[row.id]?.[0]?.system?.memory?.usage_percent || 0) * 100; return ; }, }, @@ -225,11 +225,11 @@ export const InfraMonitorsTable = ({ id: "disk", content: t("monitors.infrastructure.table.headers.disk"), render: (row) => { - const totalDiskUsage = checksMap[row._id]?.[0]?.system?.disk?.reduce( + const totalDiskUsage = checksMap[row.id]?.[0]?.system?.disk?.reduce( (acc, disk) => acc + disk.usage_percent, 0 ); - const diskCount = checksMap[row._id]?.[0]?.system?.disk?.length || 1; + const diskCount = checksMap[row.id]?.[0]?.system?.disk?.length || 1; const diskUsage = ((totalDiskUsage || 0) / diskCount) * 100; return ; }, @@ -257,7 +257,7 @@ export const InfraMonitorsTable = ({ headers={headers} data={monitors} onRowClick={(row) => { - navigate(`/infrastructure/${row._id}`); + navigate(`/infrastructure/${row.id}`); }} /> { - navigate(`${monitor._id}`); + navigate(`${monitor.id}`); }, }, { id: 3, label: t("monitors.common.actions.incidents"), action: () => { - navigate(`/incidents?monitorId=${monitor._id}`); + navigate(`/incidents?monitorId=${monitor.id}`); }, }, { id: 4, label: t("monitors.common.actions.configure"), action: () => { - navigate(`/pagespeed/${monitor._id}/configure`); + navigate(`/pagespeed/${monitor.id}/configure`); }, }, // { @@ -129,7 +129,7 @@ export const PageSpeedMonitorTable = ({ ? t("common.buttons.resume") : t("common.buttons.pause"), action: async () => { - await patch(`/monitors/${monitor._id}/active`); + await patch(`/monitors/${monitor.id}/active`); refetch(); }, closeMenu: true, @@ -207,7 +207,7 @@ export const PageSpeedMonitorTable = ({ return ( @@ -237,7 +237,7 @@ export const PageSpeedMonitorTable = ({ headers={headers} data={monitors} onRowClick={(row) => { - navigate(`/pagespeed/${row._id}`); + navigate(`/pagespeed/${row.id}`); }} /> { const monitorLimitReached = useLimitReached("monitorsMax", count); const handleConfirm = async () => { - await deleteFn(`/monitors/${selectedMonitor?._id}`); + await deleteFn(`/monitors/${selectedMonitor?.id}`); setSelectedMonitor(null); refetch(); }; diff --git a/client/src/pages/uptime/UptimeDetails.tsx b/client/src/pages/uptime/UptimeDetails.tsx index b22292b1c..cf75ebc50 100644 --- a/client/src/pages/uptime/UptimeDetails.tsx +++ b/client/src/pages/uptime/UptimeDetails.tsx @@ -155,7 +155,7 @@ const UptimeDetailsPage = () => { - + ); }; diff --git a/client/src/pages/uptime/UptimeMonitors.tsx b/client/src/pages/uptime/UptimeMonitors.tsx index 429b3c323..49d270626 100644 --- a/client/src/pages/uptime/UptimeMonitors.tsx +++ b/client/src/pages/uptime/UptimeMonitors.tsx @@ -97,7 +97,7 @@ const UptimeMonitors = () => { const monitorLimitReached = useLimitReached("monitorsMax", count); const handleConfirm = async () => { - await deleteFn(`/monitors/${selectedMonitor?._id}`); + await deleteFn(`/monitors/${selectedMonitor?.id}`); setSelectedMonitor(null); refetch(); }; diff --git a/client/src/pages/uptime/UptimeMonitorsTable.tsx b/client/src/pages/uptime/UptimeMonitorsTable.tsx index dbabecb5c..b4a53afb0 100644 --- a/client/src/pages/uptime/UptimeMonitorsTable.tsx +++ b/client/src/pages/uptime/UptimeMonitorsTable.tsx @@ -102,21 +102,21 @@ export const MonitorTable = ({ id: 2, label: t("monitors.common.actions.details"), action: () => { - navigate(`${monitor._id}`); + navigate(`${monitor.id}`); }, }, { id: 3, label: t("monitors.common.actions.incidents"), action: () => { - navigate(`/incidents?monitorId=${monitor._id}`); + navigate(`/incidents?monitorId=${monitor.id}`); }, }, { id: 4, label: t("monitors.common.actions.configure"), action: () => { - navigate(`/uptime/${monitor._id}/configure`); + navigate(`/uptime/${monitor.id}/configure`); }, }, // { @@ -133,7 +133,7 @@ export const MonitorTable = ({ ? t("common.buttons.resume") : t("common.buttons.pause"), action: async () => { - await patch(`/monitors/${monitor._id}/active`); + await patch(`/monitors/${monitor.id}/active`); refetch(); }, closeMenu: true, @@ -209,9 +209,9 @@ export const MonitorTable = ({ content: t("monitors.uptime.table.headers.responseTime"), render: (row) => { if (chartType === "histogram") { - return ; + return ; } else { - return ; + return ; } }, }, @@ -253,7 +253,7 @@ export const MonitorTable = ({ headers={headers} data={monitors} onRowClick={(row) => { - navigate(`/uptime/${row._id}`); + navigate(`/uptime/${row.id}`); }} /> { down: "error", paused: "warning", initializing: "warning", + resuming: "warning", }; return paletteMap[status]; }; @@ -40,6 +41,7 @@ export const getStatusColor = (status: MonitorStatus, theme: any): string => { down: theme.palette.error.light, paused: theme.palette.warning.light, initializing: theme.palette.warning.light, + resuming: theme.palette.warning.light, }; return statusColors[status]; }; diff --git a/server/src/init/services.ts b/server/src/init/services.ts index 866f08696..f58773a48 100644 --- a/server/src/init/services.ts +++ b/server/src/init/services.ts @@ -33,6 +33,7 @@ import { } from "@/services/index.js"; import { EntitlementsFactory } from "@/services/system/EntitlementsService.js"; import { MeService } from "@/services/index.js"; +import { MongoMonitorRepository } from "@/repositories/index.js"; export const initServices = async () => { const checkService = new CheckService(); @@ -71,7 +72,8 @@ export const initServices = async () => { const entitlementsProvider = EntitlementsFactory.create(); const authService = new AuthService(jobQueue, entitlementsProvider); const meService = new MeService(entitlementsProvider); - const monitorService = new MonitorService(jobQueue); + const monitorRepository = new MongoMonitorRepository(); + const monitorService = new MonitorService(jobQueue, monitorRepository); const queueService = new QueueService(jobQueue); const teamService = new TeamService(jobQueue); const roleService = new RoleService(); diff --git a/server/src/repositories/checks/ICheckRepository.ts b/server/src/repositories/checks/ICheckRepository.ts new file mode 100644 index 000000000..e69de29bb diff --git a/server/src/repositories/checks/MongoChecksRepository.ts b/server/src/repositories/checks/MongoChecksRepository.ts deleted file mode 100644 index 9265c0b1e..000000000 --- a/server/src/repositories/checks/MongoChecksRepository.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { Check } from "@/db/models/index.js"; -import type { ICheck } from "@/db/models/index.js"; -import type { IChecksRepository } from "@/repositories/checks/IChecksRepository.js"; -import { CheckEntity } from "@/types/domain/check.js"; -import mongoose from "mongoose"; - -export class MongoChecksRepository implements IChecksRepository { - private toEntity = (check: ICheck): CheckEntity => { - return { - id: check._id.toString(), - metadata: { - monitorId: check.metadata.monitorId.toString(), - teamId: check.metadata.teamId.toString(), - type: check.metadata.type, - }, - status: check.status, - httpStatusCode: check.httpStatusCode, - message: check.message, - timings: check.timings, - errorMessage: check.errorMessage, - ackAt: check.ackAt, - ackBy: check.ackBy?.toString(), - system: check.system, - capture: check.capture, - lighthouse: check.lighthouse, - dockerContainers: check.dockerContainers, - createdAt: check.createdAt, - updatedAt: check.updatedAt, - expiry: check.expiry, - }; - }; - - findLatestByMonitorIds = async (monitorIds: string[]) => { - const checks = await Check.aggregate([ - { - $match: { - "metadata.monitorId": { - $in: monitorIds.map((id) => new mongoose.Types.ObjectId(id)), - }, - }, - }, - { $sort: { createdAt: -1 } }, - { - $group: { - _id: "$metadata.monitorId", - latestChecks: { $push: "$$ROOT" }, - }, - }, - { - $project: { - latestChecks: { $slice: [{ $ifNull: ["$latestChecks", []] }, 25] }, - }, - }, - ]); - console.log(checks); - return checks; - }; -} diff --git a/server/src/repositories/index.ts b/server/src/repositories/index.ts new file mode 100644 index 000000000..0d32ec8a1 --- /dev/null +++ b/server/src/repositories/index.ts @@ -0,0 +1,5 @@ +export type { + IMonitorRepository, + TeamQueryConfig, +} from "@/repositories/monitors/IMonitorRepoistory.js"; +export { default as MongoMonitorRepository } from "@/repositories/monitors/MongoMonitorRepository.js"; diff --git a/server/src/repositories/monitors/IMonitorRepoistory.ts b/server/src/repositories/monitors/IMonitorRepoistory.ts new file mode 100644 index 000000000..702fd29ad --- /dev/null +++ b/server/src/repositories/monitors/IMonitorRepoistory.ts @@ -0,0 +1,41 @@ +import type { + Monitor, + MonitorType, + MonitorStatus, +} from "@/types/domain/monitor.js"; + +export interface TeamQueryConfig { + search: string; + sortField: string; + sortOrder: string; + page: number; + rowsPerPage: number; + type: MonitorType[]; + status: MonitorStatus[]; +} + +export interface IMonitorRepository { + // Single fetch + findById(monitorId: string, teamId: string): Promise; + + // Collection fetches + findByTeamId(teamId: string): Promise; + findByTeamIdWithConfig( + teamId: string, + config: TeamQueryConfig + ): Promise; + + findMonitorCountsByTeamId(teamId: string): Promise<{ + total: number; + upCount: number; + downCount: number; + pausedCount: number; + }>; + findByOrgId(orgId: string): Promise; + + // Deletions + deleteById(id: string): Promise; + deleteByOrgId(orgId: string): Promise; +} + +export default IMonitorRepository; diff --git a/server/src/repositories/monitors/MongoMonitorRepository.ts b/server/src/repositories/monitors/MongoMonitorRepository.ts new file mode 100644 index 000000000..a889af178 --- /dev/null +++ b/server/src/repositories/monitors/MongoMonitorRepository.ts @@ -0,0 +1,126 @@ +import { Monitor, IMonitor } from "@/db/models/index.js"; +import { IMonitorRepository, TeamQueryConfig } from "@/repositories/index.js"; +import type { Monitor as MonitorEntity } from "@/types/domain/monitor.js"; +import mongoose from "mongoose"; +class MongoMonitorRepository implements IMonitorRepository { + private toEntity = (monitor: IMonitor): MonitorEntity => { + return { + id: monitor._id.toString(), + orgId: monitor.orgId.toString(), + teamId: monitor.teamId.toString(), + name: monitor.name, + url: monitor.url, + port: monitor.port, + secret: monitor.secret, + type: monitor.type, + interval: monitor.interval, + rejectUnauthorized: monitor.rejectUnauthorized, + status: monitor.status, + n: monitor.n, + thresholds: monitor.thresholds, + lastCheckedAt: monitor.lastCheckedAt, + latestStatuses: monitor.latestStatuses, + notificationChannels: monitor.notificationChannels?.map((id) => + id.toString() + ), + createdBy: monitor.createdBy.toString(), + updatedBy: monitor.updatedBy.toString(), + createdAt: monitor.createdAt, + updatedAt: monitor.updatedAt, + }; + }; + + // Single fetch + findById = async (monitorId: string, teamId: string) => { + const monitor = await Monitor.findOne({ _id: monitorId, teamId }); + if (!monitor) return null; + return this.toEntity(monitor); + }; + + // Collection fetches + findByTeamId = async (teamId: string) => { + const monitors = await Monitor.find({ teamId }); + return monitors.map(this.toEntity); + }; + + findByTeamIdWithConfig = async (teamId: string, config: TeamQueryConfig) => { + if (!config) { + const monitors = await Monitor.find({ teamId }); + return monitors.map(this.toEntity); + } + + const { search, sortField, sortOrder, page, rowsPerPage, type, status } = + config; + + const match: Record = { + teamId: new mongoose.Types.ObjectId(teamId), + }; + + if (search && search.trim() !== "") { + const term = search.trim(); + (match as any).$or = [ + { name: { $regex: term, $options: "i" } }, + { url: { $regex: term, $options: "i" } }, + ]; + } + + if (Array.isArray(type) && type.length > 0) { + match.type = { $in: type }; + } + + if (Array.isArray(status) && status.length > 0) { + match.status = { $in: status }; + } + + const skip = page * rowsPerPage; + + const monitors = await Monitor.find(match) + .sort({ [sortField]: sortOrder === "asc" ? 1 : -1 }) + .skip(skip) + .limit(rowsPerPage); + + return monitors.map(this.toEntity); + }; + findByOrgId = async (orgId: string) => { + return []; + }; + + findMonitorCountsByTeamId = async (teamId: string) => { + const countResult = await Monitor.aggregate([ + { + $match: { teamId: new mongoose.Types.ObjectId(teamId) }, + }, + { + $group: { + _id: null, + total: { $sum: 1 }, + upCount: { $sum: { $cond: [{ $eq: ["$status", "up"] }, 1, 0] } }, + downCount: { $sum: { $cond: [{ $eq: ["$status", "down"] }, 1, 0] } }, + pausedCount: { + $sum: { + $cond: [{ $in: ["$status", ["paused", "resuming"]] }, 1, 0], + }, + }, + }, + }, + ]); + + const counts = countResult[0] || { + total: 0, + upCount: 0, + downCount: 0, + pausedCount: 0, + }; + return counts; + }; + + // Deletions + deleteById = async (id: string) => { + return false; + }; + deleteByOrgId = async (orgId: string) => { + return 0; + }; +} + +export default MongoMonitorRepository; diff --git a/server/src/services/business/MonitorService.ts b/server/src/services/business/MonitorService.ts index 3b7aee38c..abf8f864f 100644 --- a/server/src/services/business/MonitorService.ts +++ b/server/src/services/business/MonitorService.ts @@ -8,6 +8,8 @@ import { MonitorWithChecksResponse } from "@/types/index.js"; import { MonitorStatus, MonitorType } from "@/db/models/monitors/Monitor.js"; import { Entitlements } from "@/types/entitlements.js"; import { getStartDate } from "@/utils/TimeUtils.js"; +import { IMonitorRepository } from "@/repositories/index.js"; +import type { Monitor as MonitorEntity } from "@/types/domain/index.js"; export interface IImportedMonitor { name: string; @@ -44,7 +46,7 @@ export interface IMonitorService { currentTeamId: string, monitorData: IMonitor ) => Promise; - getAll: (teamId: string) => Promise; + getAll: (teamId: string) => Promise; getAllEmbedChecks: ( teamId: string, search: string, @@ -61,7 +63,7 @@ export interface IMonitorService { pausedCount: number; monitors: any[]; }>; - get: (teamId: string, monitorId: string) => Promise; + get: (teamId: string, monitorId: string) => Promise; getEmbedChecks: ( teamId: string, monitorId: string, @@ -93,9 +95,11 @@ export interface IMonitorService { class MonitorService implements IMonitorService { public SERVICE_NAME: string; private jobQueue: IJobQueue; - constructor(jobQueue: IJobQueue) { + private monitorRepository: IMonitorRepository; + constructor(jobQueue: IJobQueue, monitorRepository: IMonitorRepository) { this.SERVICE_NAME = SERVICE_NAME; this.jobQueue = jobQueue; + this.monitorRepository = monitorRepository; } create = async ( @@ -122,7 +126,7 @@ class MonitorService implements IMonitorService { }; getAll = async (teamId: string) => { - return Monitor.find({ teamId }); + return await this.monitorRepository.findByTeamId(teamId); }; getAllEmbedChecks = async ( @@ -135,61 +139,24 @@ class MonitorService implements IMonitorService { type: MonitorType[] = [], status: MonitorStatus[] = [] ) => { - const teamObjectId = new mongoose.Types.ObjectId(teamId); + const counts = await this.monitorRepository.findMonitorCountsByTeamId( + teamId + ); - const matchConditions: Record = { - teamId: teamObjectId, - }; - - if (search && search.trim() !== "") { - const term = search.trim(); - (matchConditions as any).$or = [ - { name: { $regex: term, $options: "i" } }, - { url: { $regex: term, $options: "i" } }, - ]; - } - - if (Array.isArray(type) && type.length > 0) { - matchConditions.type = { $in: type }; - } - - if (Array.isArray(status) && status.length > 0) { - matchConditions.status = { $in: status }; - } - - const countResult = await Monitor.aggregate([ + const monitors = await this.monitorRepository.findByTeamIdWithConfig( + teamId, { - $match: { teamId: teamObjectId }, - }, - { - $group: { - _id: null, - total: { $sum: 1 }, - upCount: { $sum: { $cond: [{ $eq: ["$status", "up"] }, 1, 0] } }, - downCount: { $sum: { $cond: [{ $eq: ["$status", "down"] }, 1, 0] } }, - pausedCount: { - $sum: { $cond: [{ $eq: ["$status", "paused"] }, 1, 0] }, - }, - }, - }, - ]); + search, + sortField, + sortOrder, + page, + rowsPerPage, + type, + status, + } + ); - const counts = countResult[0] || { - total: 0, - upCount: 0, - downCount: 0, - pausedCount: 0, - }; - - const skip = page * rowsPerPage; - - const monitors = await Monitor.find(matchConditions) - .lean() - .sort({ [sortField]: sortOrder === "asc" ? 1 : -1 }) - .skip(skip) - .limit(rowsPerPage); - - const monitorIds = monitors.map((m) => m._id); + const monitorIds = monitors.map((m) => new mongoose.Types.ObjectId(m.id)); const checks = await Check.aggregate([ { @@ -226,7 +193,7 @@ class MonitorService implements IMonitorService { }; get = async (teamId: string, monitorId: string) => { - const monitor = await Monitor.findOne({ _id: monitorId, teamId }); + const monitor = await this.monitorRepository.findById(monitorId, teamId); if (!monitor) { throw new ApiError("Monitor not found", 404); } @@ -555,13 +522,16 @@ class MonitorService implements IMonitorService { return {}; }; - private getEmbedChecksRecent = async (monitor: IMonitor, startDate: Date) => { + private getEmbedChecksRecent = async ( + monitor: MonitorEntity, + startDate: Date + ) => { const endDate = new Date(); const matchStage: { "metadata.monitorId": mongoose.Types.ObjectId; createdAt: { $gte: Date; $lt: Date }; } = { - "metadata.monitorId": monitor._id, + "metadata.monitorId": new mongoose.Types.ObjectId(monitor.id), createdAt: { $gte: startDate, $lt: endDate }, }; @@ -652,7 +622,7 @@ class MonitorService implements IMonitorService { // Get monitor stats const monitorStats = await MonitorStats.findOne({ - monitorId: monitor._id, + monitorId: monitor.id, }); if (!monitorStats) { @@ -660,19 +630,19 @@ class MonitorService implements IMonitorService { } return { - monitor: monitor.toObject(), + monitor: monitor, checks, stats: monitorStats, }; }; private getEmbedChecksOtherRanges = async ( - monitor: IMonitor, + monitor: MonitorEntity, range: string, startDate: Date ) => { const monitorStats = await MonitorStats.findOne({ - monitorId: monitor._id, + monitorId: monitor.id, }); if (!monitorStats) { throw new ApiError("Monitor stats not found", 404); @@ -683,7 +653,7 @@ class MonitorService implements IMonitorService { const endDate = new Date(); const docs = await Model.find({ - monitorId: monitor._id, + monitorId: monitor.id, windowStart: { $gte: startDate, $lt: endDate }, }) .sort({ windowStart: -1 }) @@ -739,7 +709,7 @@ class MonitorService implements IMonitorService { }); return { - monitor: monitor.toObject(), + monitor: monitor, checks, stats: monitorStats, }; @@ -750,7 +720,7 @@ class MonitorService implements IMonitorService { monitorId: string, range: string ): Promise => { - const monitor = await Monitor.findOne({ _id: monitorId, teamId }); + const monitor = await this.monitorRepository.findById(monitorId, teamId); if (!monitor) { throw new ApiError("Monitor not found", 404); } diff --git a/server/src/types/domain/index.ts b/server/src/types/domain/index.ts new file mode 100644 index 000000000..e7790153d --- /dev/null +++ b/server/src/types/domain/index.ts @@ -0,0 +1 @@ +export type { Monitor } from "@/types/domain/monitor.js"; diff --git a/server/src/types/domain/monitor.ts b/server/src/types/domain/monitor.ts new file mode 100644 index 000000000..825ab989c --- /dev/null +++ b/server/src/types/domain/monitor.ts @@ -0,0 +1,49 @@ +export const MonitorTypes = [ + "http", + "https", + "port", + "ping", + "infrastructure", + "pagespeed", + "docker", +] as const; +export type MonitorType = (typeof MonitorTypes)[number]; + +export const MonitorStatuses = [ + "up", + "down", + "paused", + "initializing", + "resuming", +] as const; +export type MonitorStatus = (typeof MonitorStatuses)[number]; + +export interface MonitorThresholds { + cpu?: number; + memory?: number; + disk?: number; + temperature?: number; +} + +export interface Monitor { + id: string; + orgId: string; + teamId: string; + name: string; + url: string; + port?: number; + secret?: string; + type: MonitorType; + interval: number; // in ms + rejectUnauthorized: boolean; + status: MonitorStatus; + n: number; // Number of consecutive successes required to change status + thresholds?: MonitorThresholds; + lastCheckedAt?: Date; + latestStatuses?: MonitorStatus[]; + notificationChannels?: string[]; + createdBy: string; + updatedBy: string; + createdAt: Date; + updatedAt: Date; +} diff --git a/server/src/types/monitor-response-with-checks.ts b/server/src/types/monitor-response-with-checks.ts index b547459e5..f9e064e44 100644 --- a/server/src/types/monitor-response-with-checks.ts +++ b/server/src/types/monitor-response-with-checks.ts @@ -1,7 +1,7 @@ -import { IMonitor, IMonitorStats } from "@/db/models/index.js"; - +import { IMonitorStats } from "@/db/models/index.js"; +import type { Monitor } from "@/types/domain/index.js"; export interface MonitorWithChecksResponse { - monitor: IMonitor; + monitor: Monitor; checks: Array<{ _id: string; count: number;