Openstatus www.openstatus.dev
at main 165 lines 4.6 kB view raw
1import { z } from "zod"; 2 3import { type SQL, and, asc, desc, eq, gte, schema } from "@openstatus/db"; 4import { 5 incidentTable, 6 selectIncidentSchema, 7 selectMonitorSchema, 8} from "@openstatus/db/src/schema"; 9 10import { Events } from "@openstatus/analytics"; 11import { TRPCError } from "@trpc/server"; 12import { createTRPCRouter, protectedProcedure } from "../trpc"; 13import { getPeriodDate, periods } from "./utils"; 14 15export const incidentRouter = createTRPCRouter({ 16 delete: protectedProcedure 17 .meta({ track: Events.DeleteIncident }) 18 .input(z.object({ id: z.number() })) 19 .mutation(async (opts) => { 20 const incidentToDelete = await opts.ctx.db 21 .select() 22 .from(schema.incidentTable) 23 .where( 24 and( 25 eq(schema.incidentTable.id, opts.input.id), 26 eq(schema.incidentTable.workspaceId, opts.ctx.workspace.id), 27 ), 28 ) 29 .get(); 30 if (!incidentToDelete) return; 31 32 await opts.ctx.db 33 .delete(schema.incidentTable) 34 .where(eq(schema.incidentTable.id, incidentToDelete.id)) 35 .run(); 36 }), 37 38 list: protectedProcedure 39 .input( 40 z 41 .object({ 42 period: z.enum(periods).optional(), 43 monitorId: z.number().nullish(), 44 order: z.enum(["asc", "desc"]).optional(), 45 }) 46 .optional(), 47 ) 48 .query(async (opts) => { 49 const whereConditions: SQL[] = [ 50 eq(incidentTable.workspaceId, opts.ctx.workspace.id), 51 ]; 52 53 if (opts.input?.period) { 54 whereConditions.push( 55 gte(incidentTable.startedAt, getPeriodDate(opts.input.period)), 56 ); 57 } 58 59 if (opts.input?.monitorId) { 60 whereConditions.push(eq(incidentTable.monitorId, opts.input.monitorId)); 61 } 62 63 const result = await opts.ctx.db.query.incidentTable.findMany({ 64 where: and(...whereConditions), 65 with: { 66 monitor: true, 67 }, 68 orderBy: 69 opts.input?.order === "asc" 70 ? asc(incidentTable.startedAt) 71 : desc(incidentTable.startedAt), 72 }); 73 74 return selectIncidentSchema 75 .extend({ 76 monitor: selectMonitorSchema, 77 }) 78 .array() 79 .parse(result); 80 }), 81 82 acknowledge: protectedProcedure 83 .meta({ track: Events.AcknowledgeIncident }) 84 .input(z.object({ id: z.number() })) 85 .mutation(async (opts) => { 86 const currentIncident = await opts.ctx.db 87 .select() 88 .from(schema.incidentTable) 89 .where( 90 and( 91 eq(schema.incidentTable.id, opts.input.id), 92 eq(schema.incidentTable.workspaceId, opts.ctx.workspace.id), 93 ), 94 ) 95 .get(); 96 if (!currentIncident) { 97 throw new TRPCError({ 98 code: "NOT_FOUND", 99 message: "Incident not found", 100 }); 101 } 102 if (currentIncident.acknowledgedAt) { 103 throw new TRPCError({ 104 code: "BAD_REQUEST", 105 message: "Incident already acknowledged", 106 }); 107 } 108 await opts.ctx.db 109 .update(schema.incidentTable) 110 .set({ 111 acknowledgedAt: new Date(), 112 acknowledgedBy: opts.ctx.user.id, 113 updatedAt: new Date(), 114 }) 115 .where( 116 and( 117 eq(schema.incidentTable.id, opts.input.id), 118 eq(schema.incidentTable.workspaceId, opts.ctx.workspace.id), 119 ), 120 ); 121 return true; 122 }), 123 124 resolve: protectedProcedure 125 .meta({ track: Events.ResolveIncident }) 126 .input(z.object({ id: z.number() })) 127 .mutation(async (opts) => { 128 const currentIncident = await opts.ctx.db 129 .select() 130 .from(schema.incidentTable) 131 .where( 132 and( 133 eq(schema.incidentTable.id, opts.input.id), 134 eq(schema.incidentTable.workspaceId, opts.ctx.workspace.id), 135 ), 136 ) 137 .get(); 138 if (!currentIncident) { 139 throw new TRPCError({ 140 code: "NOT_FOUND", 141 message: "Incident not found", 142 }); 143 } 144 if (currentIncident.resolvedAt) { 145 throw new TRPCError({ 146 code: "BAD_REQUEST", 147 message: "Incident already resolved", 148 }); 149 } 150 await opts.ctx.db 151 .update(schema.incidentTable) 152 .set({ 153 resolvedAt: new Date(), 154 resolvedBy: opts.ctx.user.id, 155 updatedAt: new Date(), 156 }) 157 .where( 158 and( 159 eq(schema.incidentTable.id, opts.input.id), 160 eq(schema.incidentTable.workspaceId, opts.ctx.workspace.id), 161 ), 162 ); 163 return true; 164 }), 165});