Files
2026-06-09 12:34:57 -07:00

105 lines
4.0 KiB
TypeScript

import { z } from "zod";
import { registry } from "../registry.js";
import { bearer, okJson, okJsonNoData, standardErrors } from "../helpers.js";
import { getIncidentsByTeamQueryValidation, getIncidentSummaryQueryValidation } from "@/api/validation/incidentValidation.js";
const tags = ["incidents"];
const incidentIdParam = z.object({ incidentId: z.string() });
const incidentObject = z
.object({
id: z.string().openapi({ example: "65f1c2a4d8b9e0123456789c" }),
monitorId: z.string().openapi({ example: "65f1c2a4d8b9e0123456789a" }),
teamId: z.string().openapi({ example: "65f1c2a4d8b9e01234567890" }),
startTime: z.string().openapi({ example: "2026-04-15T03:21:00.000Z" }),
endTime: z.string().nullable().openapi({ example: "2026-04-15T03:34:00.000Z" }),
status: z.boolean().openapi({ example: true, description: "true = resolved, false = ongoing" }),
message: z.string().nullable().optional().openapi({ example: "HTTP 503 from origin" }),
statusCode: z.number().nullable().optional().openapi({ example: 503 }),
resolutionType: z.enum(["automatic", "manual"]).nullable().openapi({ example: "automatic" }),
resolvedBy: z.string().nullable().optional(),
resolvedByEmail: z.string().nullable().optional(),
comment: z.string().nullable().optional(),
createdAt: z.string().openapi({ example: "2026-04-15T03:21:00.000Z" }),
updatedAt: z.string().openapi({ example: "2026-04-15T03:34:00.000Z" }),
})
.passthrough()
.openapi("Incident");
const incidentSummaryItem = z
.object({
id: z.string().openapi({ example: "65f1c2a4d8b9e0123456789c" }),
monitorId: z.string().openapi({ example: "65f1c2a4d8b9e0123456789a" }),
monitorName: z.string().nullable().openapi({ example: "Marketing site" }),
status: z.boolean().openapi({ example: true }),
startTime: z.string().openapi({ example: "2026-04-15T03:21:00.000Z" }),
endTime: z.string().nullable().openapi({ example: "2026-04-15T03:34:00.000Z" }),
resolutionType: z.enum(["automatic", "manual"]).nullable().openapi({ example: "automatic" }),
message: z.string().nullable().openapi({ example: "HTTP 503 from origin" }),
statusCode: z.number().nullable().openapi({ example: 503 }),
createdAt: z.string().openapi({ example: "2026-04-15T03:21:00.000Z" }),
})
.openapi("IncidentSummaryItem");
const incidentSummary = z
.object({
total: z.number().openapi({ example: 12 }),
totalActive: z.number().openapi({ example: 1 }),
totalManualResolutions: z.number().openapi({ example: 2 }),
totalAutomaticResolutions: z.number().openapi({ example: 9 }),
avgResolutionTimeHours: z.number().openapi({ example: 0.5 }),
topMonitor: z
.object({
monitorId: z.string().openapi({ example: "65f1c2a4d8b9e0123456789a" }),
monitorName: z.string().nullable().openapi({ example: "Marketing site" }),
incidentCount: z.number().openapi({ example: 4 }),
})
.nullable(),
latestIncidents: z.array(incidentSummaryItem),
})
.openapi("IncidentSummary");
registry.registerPath({
method: "get",
path: "/incidents/team",
tags,
summary: "List incidents for the caller's team",
security: bearer,
request: { query: getIncidentsByTeamQueryValidation },
responses: { "200": okJson(z.array(incidentObject)), ...standardErrors },
});
registry.registerPath({
method: "get",
path: "/incidents/team/summary",
tags,
summary: "Incident summary for the caller's team",
security: bearer,
request: { query: getIncidentSummaryQueryValidation },
responses: { "200": okJson(incidentSummary), ...standardErrors },
});
registry.registerPath({
method: "get",
path: "/incidents/{incidentId}",
tags,
summary: "Get an incident by id",
security: bearer,
request: { params: incidentIdParam },
responses: {
"200": okJson(z.object({ incident: incidentObject, monitor: z.unknown(), user: z.unknown().nullable() })),
...standardErrors,
},
});
registry.registerPath({
method: "put",
path: "/incidents/{incidentId}/resolve",
tags,
summary: "Manually resolve an incident (admin/superadmin)",
security: bearer,
request: { params: incidentIdParam },
responses: { "200": okJsonNoData(), ...standardErrors },
});