export team incidents

This commit is contained in:
Alex Holliday committed 2025-12-08 23:18:46 +00:00
1 parent 36208aacea
commit e12e198aed
4 files changed
+88 -9

No files matched your search

@@ -12,6 +12,7 @@ export interface IIncidentsController {
getIncidents: (req: Request, res: Response, next: NextFunction) => void;
getIncidentById: (req: Request, res: Response, next: NextFunction) => void;
resolveIncident: (req: Request, res: Response, next: NextFunction) => void;
exportIncidents: (req: Request, res: Response, next: NextFunction) => void;
}
class IncidentsController implements IIncidentsController {
@@ -136,6 +137,26 @@ class IncidentsController implements IIncidentsController {
next(error);
}
};
exportIncidents = async (req: Request, res: Response, next: NextFunction) => {
try {
const userContext = req.user;
if (!userContext) {
return res.status(401).json({ message: "Unauthorized" });
}
const teamId = userContext.currentTeamId;
if (!teamId) {
throw new ApiError("No team ID", 400);
}
const incidents = await this.incidentService.export(teamId);
return res.status(200).json({ message: "OK", data: incidents });
} catch (error) {
next(error);
}
};
}
export default IncidentsController;
+12 -1
View File
@@ -4,7 +4,10 @@ import { IncidentsController } from "@/controllers/index.js";
import { verifyToken } from "@/middleware/VerifyToken.js";
import { addUserContext } from "@/middleware/AddUserContext.js";
import { validateQuery, validateBody } from "@/middleware/validation.js";
import { verifyOrgPermission } from "@/middleware/VerifyPermission.js";
import {
verifyOrgPermission,
verifyTeamPermission,
} from "@/middleware/VerifyPermission.js";
import { PERMISSIONS } from "@/types/permissions.js";
import {
getIncidentsQuerySchema,
@@ -30,6 +33,14 @@ class IncidentsRoutes {
validateQuery(getIncidentsQuerySchema),
this.controller.getIncidents
);
this.router.get(
"/export",
verifyToken,
addUserContext,
verifyTeamPermission([PERMISSIONS.incidents.read]),
this.controller.exportIncidents
);
this.router.get(
"/:id",
verifyToken,
@@ -4,6 +4,7 @@ import {
IMonitor,
Monitor,
ICheck,
IUser,
} from "@/db/models/index.js";
import type { ResolutionType } from "@/db/models/index.js";
import mongoose from "mongoose";
@@ -11,6 +12,11 @@ import { getChildLogger } from "@/logger/Logger.js";
import ApiError from "@/utils/ApiError.js";
import { ThresholdEvaluationResult } from "@/services/infrastructure/StatusService.js";
type IncidentPopulated = Omit<IIncident, "monitorId" | "resolvedBy"> & {
monitorId: IMonitor;
resolvedBy?: IUser | null;
};
export interface IIncidentService {
handleStatusChange: (
updatedMonitor: IMonitor,
@@ -48,6 +54,7 @@ export interface IIncidentService {
teamId: mongoose.Types.ObjectId,
incidentId: mongoose.Types.ObjectId
) => Promise<boolean>;
export: (teamId: string) => Promise<Array<Record<string, unknown>>>;
cleanupOrphanedIncidents: () => Promise<boolean>;
}
@@ -267,6 +274,25 @@ class IncidentService implements IIncidentService {
return result.deletedCount === 1;
};
export = async (teamId: string) => {
const incidents = await Incident.find({
teamId,
})
.populate("monitorId")
.populate("resolvedBy")
.lean<IncidentPopulated[]>();
return incidents.map((incident) => ({
monitor: incident.monitorId?.name ?? "",
monitorId: incident.monitorId?._id?.toString?.() ?? "",
resolved: incident.resolved,
resolutionType: incident.resolutionType ?? "Unresolved",
startedAt: incident.startedAt,
endedAt: incident.endedAt ?? "Ongoing",
resolvedBy: incident.resolvedBy ? incident.resolvedBy.email ?? "" : "",
}));
};
async cleanupOrphanedIncidents() {
try {
const monitorIds = await Monitor.find().distinct("_id");