6 files changed
+134
-35
No files matched your search
@@ -1,6 +1,8 @@
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
import { getChecksParamValidation, getChecksQueryValidation } from "@/validation/checkValidation.js";
|
||||
import type { IGeoChecksService } from "@/service/business/geoChecksService.js";
|
||||
import { requireTeamId } from "./controllerUtils.js";
|
||||
import { AppError } from "@/utils/AppError.js";
|
||||
|
||||
const SERVICE_NAME = "geoCheckController";
|
||||
|
||||
@@ -18,13 +20,29 @@ class GeoCheckController {
|
||||
|
||||
getGeoChecksByMonitor = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
getChecksParamValidation.parse(req.params);
|
||||
getChecksQueryValidation.parse(req.query);
|
||||
const validatedParams = getChecksParamValidation.parse(req.params);
|
||||
const validatedQuery = getChecksQueryValidation.parse(req.query);
|
||||
|
||||
if (!validatedQuery.continent || validatedQuery.continent.length === 0) {
|
||||
throw new AppError({
|
||||
message: "At least one continent must be specified",
|
||||
service: SERVICE_NAME,
|
||||
method: "getGeoChecksByMonitor",
|
||||
details: { monitorId: validatedParams.monitorId },
|
||||
status: 400,
|
||||
});
|
||||
}
|
||||
|
||||
const teamId = requireTeamId(req.user?.teamId);
|
||||
|
||||
const result = await this.geoChecksService.getGeoChecksByMonitor({
|
||||
monitorId: req?.params?.monitorId as string,
|
||||
query: req?.query,
|
||||
teamId: req?.user?.teamId as string,
|
||||
monitorId: validatedParams.monitorId,
|
||||
teamId: teamId,
|
||||
sortOrder: validatedQuery.sortOrder,
|
||||
dateRange: validatedQuery.dateRange,
|
||||
page: validatedQuery.page,
|
||||
rowsPerPage: validatedQuery.rowsPerPage,
|
||||
continent: validatedQuery.continent,
|
||||
});
|
||||
|
||||
return res.status(200).json({
|
||||
|
||||
@@ -21,7 +21,17 @@ class MaintenanceWindowController {
|
||||
try {
|
||||
const validatedBody = createMaintenanceWindowBodyValidation.parse(req.body);
|
||||
const teamId = requireTeamId(req?.user?.teamId);
|
||||
await this.maintenanceWindowService.createMaintenanceWindow({ teamId, body: validatedBody });
|
||||
|
||||
const monitorIDs = validatedBody.monitors;
|
||||
const name = validatedBody.name;
|
||||
const active = validatedBody.active ? validatedBody.active : true;
|
||||
const duration = validatedBody.duration;
|
||||
const durationUnit = validatedBody.durationUnit;
|
||||
const repeat = validatedBody.repeat;
|
||||
const start = validatedBody.start;
|
||||
const end = validatedBody.end;
|
||||
|
||||
await this.maintenanceWindowService.createMaintenanceWindow({ teamId, monitorIDs, name, active, duration, durationUnit, repeat, start, end });
|
||||
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
|
||||
@@ -24,8 +24,7 @@ export class DiagnosticService implements IDiagnosticService {
|
||||
* Clears performance marks after each measurement to prevent memory leaks.
|
||||
*/
|
||||
const obs = new PerformanceObserver((items) => {
|
||||
// Get the first entry but we don't need to store it
|
||||
items.getEntries()[0];
|
||||
items.getEntries();
|
||||
performance.clearMarks();
|
||||
});
|
||||
obs.observe({ entryTypes: ["measure"] });
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { Monitor, GeoCheck } from "@/types/index.js";
|
||||
import type { GeoCheckResult } from "@/types/geoCheck.js";
|
||||
import type { GeoCheckResult, GeoContinent } from "@/types/geoCheck.js";
|
||||
import { Types } from "mongoose";
|
||||
import type { IGeoChecksRepository } from "@/repositories/index.js";
|
||||
import type { FlatGeoChecksQueryResult, IGeoChecksRepository } from "@/repositories/index.js";
|
||||
import type { IMonitorsRepository } from "@/repositories/index.js";
|
||||
import type { IGlobalPingService } from "@/service/infrastructure/globalPingService.js";
|
||||
import type { ILogger } from "@/utils/logger.js";
|
||||
@@ -13,7 +13,15 @@ export interface IGeoChecksService {
|
||||
readonly serviceName: string;
|
||||
buildGeoCheck(monitor: Monitor): Promise<GeoCheck | null>;
|
||||
createGeoChecks(geoChecks: GeoCheck[]): Promise<GeoCheck[]>;
|
||||
getGeoChecksByMonitor(args: { monitorId: string; query: any; teamId: string }): Promise<any>;
|
||||
getGeoChecksByMonitor(args: {
|
||||
monitorId: string;
|
||||
teamId: string;
|
||||
sortOrder: string;
|
||||
dateRange: string;
|
||||
page?: number;
|
||||
rowsPerPage?: number;
|
||||
continent: GeoContinent | GeoContinent[];
|
||||
}): Promise<FlatGeoChecksQueryResult>;
|
||||
}
|
||||
|
||||
export class GeoChecksService implements IGeoChecksService {
|
||||
@@ -139,7 +147,23 @@ export class GeoChecksService implements IGeoChecksService {
|
||||
return this.geoChecksRepository.createGeoChecks(geoChecks);
|
||||
};
|
||||
|
||||
getGeoChecksByMonitor = async ({ monitorId, query, teamId }: { monitorId: string; query: any; teamId: string }) => {
|
||||
getGeoChecksByMonitor = async ({
|
||||
monitorId,
|
||||
teamId,
|
||||
sortOrder,
|
||||
dateRange,
|
||||
page,
|
||||
rowsPerPage,
|
||||
continent,
|
||||
}: {
|
||||
monitorId: string;
|
||||
teamId: string;
|
||||
sortOrder: string;
|
||||
dateRange: string;
|
||||
page?: number;
|
||||
rowsPerPage?: number;
|
||||
continent: GeoContinent | GeoContinent[];
|
||||
}) => {
|
||||
if (!monitorId) {
|
||||
throw new AppError({
|
||||
message: "No monitor ID in request",
|
||||
@@ -167,10 +191,9 @@ export class GeoChecksService implements IGeoChecksService {
|
||||
});
|
||||
}
|
||||
|
||||
const { sortOrder, dateRange, page, rowsPerPage, continent } = query;
|
||||
const continents = continent ? (Array.isArray(continent) ? continent : [continent]) : undefined;
|
||||
const parsedPage = page ? parseInt(page) : page;
|
||||
const parsedRowsPerPage = rowsPerPage ? parseInt(rowsPerPage) : rowsPerPage;
|
||||
const parsedPage = page || 0;
|
||||
const parsedRowsPerPage = rowsPerPage || 5;
|
||||
const result = await this.geoChecksRepository.findByMonitorId(monitorId, sortOrder, dateRange, parsedPage, parsedRowsPerPage, continents);
|
||||
return result;
|
||||
};
|
||||
|
||||
@@ -2,6 +2,8 @@ import type { Invite, UserRole } from "@/types/index.js";
|
||||
import { canManageRole } from "@/types/user.js";
|
||||
import type { IInvitesRepository } from "@/repositories/index.js";
|
||||
import { AppError } from "@/utils/AppError.js";
|
||||
import { ISettingsService } from "../system/settingsService.js";
|
||||
import { IEmailService } from "../infrastructure/emailService.js";
|
||||
|
||||
const SERVICE_NAME = "inviteService";
|
||||
|
||||
@@ -14,8 +16,8 @@ export interface IInviteService {
|
||||
export class InviteService implements IInviteService {
|
||||
static SERVICE_NAME = SERVICE_NAME;
|
||||
|
||||
private settingsService: any;
|
||||
private emailService: any;
|
||||
private settingsService: ISettingsService;
|
||||
private emailService: IEmailService;
|
||||
private invitesRepository: IInvitesRepository;
|
||||
|
||||
constructor({
|
||||
@@ -24,8 +26,8 @@ export class InviteService implements IInviteService {
|
||||
emailService,
|
||||
}: {
|
||||
invitesRepository: IInvitesRepository;
|
||||
settingsService: any;
|
||||
emailService: any;
|
||||
settingsService: ISettingsService;
|
||||
emailService: IEmailService;
|
||||
}) {
|
||||
this.invitesRepository = invitesRepository;
|
||||
this.settingsService = settingsService;
|
||||
@@ -57,8 +59,16 @@ export class InviteService implements IInviteService {
|
||||
return inviteToken;
|
||||
};
|
||||
|
||||
sendInviteEmail = async ({ invite, firstName, userRoles }: { invite: Partial<Invite>; firstName: any; userRoles: UserRole[] }) => {
|
||||
sendInviteEmail = async ({ invite, firstName, userRoles }: { invite: Partial<Invite>; firstName: string; userRoles: UserRole[] }) => {
|
||||
const inviteRoles = invite.role ?? [];
|
||||
if (!invite.email) {
|
||||
throw new AppError({
|
||||
message: "Invite email is required to send an invite",
|
||||
service: SERVICE_NAME,
|
||||
method: "sendInviteEmail",
|
||||
status: 400,
|
||||
});
|
||||
}
|
||||
|
||||
for (const targetRole of inviteRoles) {
|
||||
const canManage = userRoles.some((actorRole) => canManageRole(actorRole, targetRole));
|
||||
@@ -66,7 +76,7 @@ export class InviteService implements IInviteService {
|
||||
throw new AppError({
|
||||
message: "You do not have permission to create this invite",
|
||||
service: SERVICE_NAME,
|
||||
method: "getInviteToken",
|
||||
method: "sendInviteEmail",
|
||||
status: 403,
|
||||
});
|
||||
}
|
||||
@@ -79,6 +89,16 @@ export class InviteService implements IInviteService {
|
||||
name: firstName,
|
||||
link: `${clientHost}/register/${inviteToken.token}`,
|
||||
});
|
||||
|
||||
if (!html) {
|
||||
throw new AppError({
|
||||
message: "Failed to build invite e-mail... Please verify your settings.",
|
||||
service: SERVICE_NAME,
|
||||
method: "sendInviteEmail",
|
||||
status: 500,
|
||||
});
|
||||
}
|
||||
|
||||
const result = await this.emailService.sendEmail(invite.email, "Welcome to Uptime Monitor", html);
|
||||
if (!result) {
|
||||
throw new AppError({
|
||||
|
||||
@@ -1,11 +1,21 @@
|
||||
import { IMaintenanceWindowsRepository, IMonitorsRepository } from "@/repositories/index.js";
|
||||
import type { MaintenanceWindow } from "@/types/index.js";
|
||||
import type { DurationUnit, MaintenanceWindow } from "@/types/index.js";
|
||||
import { AppError } from "@/utils/AppError.js";
|
||||
|
||||
const SERVICE_NAME = "maintenanceWindowService";
|
||||
|
||||
export interface IMaintenanceWindowService {
|
||||
createMaintenanceWindow(params: { teamId: string; body: Partial<MaintenanceWindow> & { monitors: string[] } }): Promise<void>;
|
||||
createMaintenanceWindow(params: {
|
||||
teamId: string;
|
||||
monitorIDs: string[];
|
||||
name: string;
|
||||
active: boolean;
|
||||
duration: number;
|
||||
durationUnit: DurationUnit;
|
||||
repeat: number;
|
||||
start: string;
|
||||
end: string;
|
||||
}): Promise<void>;
|
||||
getMaintenanceWindowById(params: { id: string; teamId: string }): Promise<MaintenanceWindow>;
|
||||
getMaintenanceWindowsByTeamId(params: {
|
||||
teamId: string;
|
||||
@@ -40,9 +50,28 @@ export class MaintenanceWindowService implements IMaintenanceWindowService {
|
||||
return MaintenanceWindowService.SERVICE_NAME;
|
||||
}
|
||||
|
||||
createMaintenanceWindow = async ({ teamId, body }: { teamId: string; body: any }) => {
|
||||
const monitorIds = body.monitors;
|
||||
const monitors = await this.monitorsRepository.findByIds(monitorIds);
|
||||
createMaintenanceWindow = async ({
|
||||
teamId,
|
||||
monitorIDs,
|
||||
name,
|
||||
active,
|
||||
duration,
|
||||
durationUnit,
|
||||
repeat,
|
||||
start,
|
||||
end,
|
||||
}: {
|
||||
teamId: string;
|
||||
monitorIDs: string[];
|
||||
name: string;
|
||||
active: boolean;
|
||||
duration: number;
|
||||
durationUnit: DurationUnit;
|
||||
repeat: number;
|
||||
start: string;
|
||||
end: string;
|
||||
}) => {
|
||||
const monitors = await this.monitorsRepository.findByIds(monitorIDs);
|
||||
|
||||
const unauthorizedMonitors = monitors.filter((monitor) => monitor.teamId !== teamId);
|
||||
|
||||
@@ -55,17 +84,17 @@ export class MaintenanceWindowService implements IMaintenanceWindowService {
|
||||
});
|
||||
}
|
||||
|
||||
const dbTransactions = monitorIds.map((monitorId: string) => {
|
||||
const dbTransactions = monitorIDs.map((monitorId: string) => {
|
||||
return this.maintenanceWindowsRepository.create({
|
||||
teamId,
|
||||
monitorId,
|
||||
name: body.name,
|
||||
active: body.active ? body.active : true,
|
||||
duration: body.duration,
|
||||
durationUnit: body.durationUnit,
|
||||
repeat: body.repeat,
|
||||
start: body.start,
|
||||
end: body.end,
|
||||
name: name,
|
||||
active: active,
|
||||
duration: duration,
|
||||
durationUnit: durationUnit,
|
||||
repeat: repeat,
|
||||
start: start,
|
||||
end: end,
|
||||
});
|
||||
});
|
||||
await Promise.all(dbTransactions);
|
||||
@@ -106,7 +135,7 @@ export class MaintenanceWindowService implements IMaintenanceWindowService {
|
||||
return await this.maintenanceWindowsRepository.deleteById(id, teamId);
|
||||
};
|
||||
|
||||
editMaintenanceWindow = async ({ id, teamId, body }: { id: string; teamId: string; body: any }) => {
|
||||
editMaintenanceWindow = async ({ id, teamId, body }: { id: string; teamId: string; body: Partial<MaintenanceWindow> }) => {
|
||||
return await this.maintenanceWindowsRepository.updateById(id, teamId, body);
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user