diff --git a/server/src/init/services.ts b/server/src/init/services.ts index 524d9b5da..e607dd446 100644 --- a/server/src/init/services.ts +++ b/server/src/init/services.ts @@ -72,7 +72,11 @@ export const initServices = async () => { ); const jobQueue = await JobQueue.create(jobGenerator, monitorRepository); const entitlementsProvider = EntitlementsFactory.create(); - const authService = new AuthService(jobQueue, entitlementsProvider); + const authService = new AuthService( + jobQueue, + entitlementsProvider, + monitorRepository + ); const meService = new MeService(entitlementsProvider); const monitorService = new MonitorService(jobQueue, monitorRepository); const queueService = new QueueService(jobQueue); diff --git a/server/src/repositories/monitors/IMonitorRepoistory.ts b/server/src/repositories/monitors/IMonitorRepoistory.ts index 9ed3b2f36..c5be5458c 100644 --- a/server/src/repositories/monitors/IMonitorRepoistory.ts +++ b/server/src/repositories/monitors/IMonitorRepoistory.ts @@ -57,6 +57,7 @@ export interface IMonitorRepository { findByOrgId(orgId: string): Promise; // Deletions + deleteAll(): Promise; deleteById(monitorId: string, teamId: string): Promise; deleteByOrgId(orgId: string): Promise; diff --git a/server/src/repositories/monitors/MongoMonitorRepository.ts b/server/src/repositories/monitors/MongoMonitorRepository.ts index 69c5c3be5..3ff2e6dc8 100644 --- a/server/src/repositories/monitors/MongoMonitorRepository.ts +++ b/server/src/repositories/monitors/MongoMonitorRepository.ts @@ -143,6 +143,10 @@ class MongoMonitorRepository implements IMonitorRepository { }; // Deletions + deleteAll = async () => { + const deleted = await Monitor.deleteMany({}); + return deleted.acknowledged; + }; deleteById = async (monitorId: string, teamId: string) => { await Monitor.deleteOne({ _id: monitorId, teamId }); return true; diff --git a/server/src/services/business/AuthService.ts b/server/src/services/business/AuthService.ts index 2e85a57b8..9502c1a00 100644 --- a/server/src/services/business/AuthService.ts +++ b/server/src/services/business/AuthService.ts @@ -11,7 +11,6 @@ import { IRole, IUser, ITokenizedUser, - Monitor, Check, NotificationChannel, IInvite, @@ -24,6 +23,7 @@ import { Plans } from "@/types/entitlements.js"; import { PERMISSIONS } from "@/types/permissions.js"; import type { Entitlements } from "@/types/entitlements.js"; import type { IEntitlementsProvider } from "@/services/system/EntitlementsService.js"; +import type { IMonitorRepository } from "@/repositories/index.js"; const SERVICE_NAME = "AuthService"; @@ -68,13 +68,16 @@ class AuthService implements IAuthService { public SERVICE_NAME: string; private jobQueue: IJobQueue; private entitlementsProvider: IEntitlementsProvider; + private monitorRepository: IMonitorRepository; constructor( jobQueue: IJobQueue, - entitlementsProvider: IEntitlementsProvider + entitlementsProvider: IEntitlementsProvider, + monitorRepository: IMonitorRepository ) { this.SERVICE_NAME = SERVICE_NAME; this.jobQueue = jobQueue; this.entitlementsProvider = entitlementsProvider; + this.monitorRepository = monitorRepository; } async register(signupData: RegisterData) { @@ -487,14 +490,14 @@ class AuthService implements IAuthService { async cleanup() { await User.deleteMany({}); await Role.deleteMany({}); - await Monitor.deleteMany({}); + await this.monitorRepository.deleteAll(); await Check.deleteMany({}); await NotificationChannel.deleteMany({}); await this.jobQueue.flush(); } async cleanMonitors() { - await Monitor.deleteMany({}); + this.monitorRepository.deleteAll(); await Check.deleteMany({}); } diff --git a/server/src/services/business/StatusPageService.ts b/server/src/services/business/StatusPageService.ts index c908c0e28..aaa591d87 100644 --- a/server/src/services/business/StatusPageService.ts +++ b/server/src/services/business/StatusPageService.ts @@ -5,6 +5,8 @@ import { IStatusPage, StatusPage, Monitor, + Check, + IMonitor, } from "@/db/models/index.js"; import ApiError from "@/utils/ApiError.js"; import { count } from "node:console"; @@ -21,8 +23,13 @@ export interface IStatusPageService { page: number, rowsPerPage: number ) => Promise<{ statusPages: IStatusPage[]; count: number }>; - get: (teamId: string, id: string) => Promise; - getPublic: (url: string) => Promise; + get: ( + teamId: string, + id: string + ) => Promise<{ statusPage: IStatusPage; checksMap: Record }>; + getPublic: ( + url: string + ) => Promise<{ statusPage: IStatusPage; checksMap: Record }>; update: ( teamId: string, tokenizedUser: IUserContext, @@ -72,7 +79,32 @@ class StatusPageService implements IStatusPageService { if (!statusPage) { throw new ApiError("Status page not found", 404); } - return statusPage; + + const monitorIds = (statusPage.monitors || []).map((m) => m._id); + + const checks = await Check.aggregate([ + { $match: { "metadata.monitorId": { $in: monitorIds } } }, + { $sort: { createdAt: -1 } }, + { + $group: { + _id: "$metadata.monitorId", + latestChecks: { $push: "$$ROOT" }, + }, + }, + { + $project: { + latestChecks: { $slice: [{ $ifNull: ["$latestChecks", []] }, 25] }, + }, + }, + ]); + + const checksMap = new Map( + checks.map((c: any) => [c._id.toString(), c.latestChecks]) + ); + return { + statusPage, + checksMap: Object.fromEntries(checksMap), + }; }; getPublic = async (url: string) => { @@ -83,7 +115,31 @@ class StatusPageService implements IStatusPageService { if (!statusPage) { throw new ApiError("Public status page not found", 404); } - return statusPage; + const monitorIds = (statusPage.monitors || []).map((m) => m._id); + + const checks = await Check.aggregate([ + { $match: { "metadata.monitorId": { $in: monitorIds } } }, + { $sort: { createdAt: -1 } }, + { + $group: { + _id: "$metadata.monitorId", + latestChecks: { $push: "$$ROOT" }, + }, + }, + { + $project: { + latestChecks: { $slice: [{ $ifNull: ["$latestChecks", []] }, 25] }, + }, + }, + ]); + + const checksMap = new Map( + checks.map((c: any) => [c._id.toString(), c.latestChecks]) + ); + return { + statusPage, + checksMap: Object.fromEntries(checksMap), + }; }; getAll = async (teamId: string, page: number, rowsPerPage: number) => {