Openstatus
www.openstatus.dev
1import { z } from "zod";
2
3import { selectIncidentSchema } from "./incidents/validation";
4import { selectMaintenanceSchema } from "./maintenances";
5import { selectMonitorGroupSchema } from "./monitor_groups";
6import { selectMonitorSchema } from "./monitors";
7import { selectPageSchema } from "./pages";
8import {
9 selectStatusReportSchema,
10 selectStatusReportUpdateSchema,
11} from "./status_reports";
12import { workspacePlanSchema } from "./workspaces";
13
14// TODO: create a 'public-status' schema with all the different types and validations
15
16export const selectPublicMonitorSchema = selectMonitorSchema.omit({
17 body: true,
18 headers: true,
19 method: true,
20 otelEndpoint: true,
21 otelHeaders: true,
22});
23
24export const selectStatusReportPageSchema = selectStatusReportSchema.extend({
25 statusReportUpdates: z.array(selectStatusReportUpdateSchema).default([]),
26 monitorsToStatusReports: z
27 .array(
28 z.object({
29 monitorId: z.number(),
30 statusReportId: z.number(),
31 monitor: selectPublicMonitorSchema,
32 }),
33 )
34 .default([]),
35});
36
37export const selectMaintenancePageSchema = selectMaintenanceSchema.extend({
38 maintenancesToMonitors: z
39 .array(
40 z.object({
41 monitorId: z.number(),
42 maintenanceId: z.number(),
43 monitor: selectPublicMonitorSchema,
44 }),
45 )
46 .default([]),
47});
48// TODO: it would be nice to automatically add the monitor relation here
49// .refine((data) => ({ monitors: data.maintenancesToMonitors.map((m) => m.monitorId) }));
50
51export const selectPageSchemaWithRelation = selectPageSchema.extend({
52 monitors: z.array(selectMonitorSchema),
53 statusReports: z.array(selectStatusReportPageSchema),
54});
55
56export const selectPageSchemaWithMonitorsRelation = selectPageSchema.extend({
57 monitorsToPages: z.array(
58 z.object({
59 monitorId: z.number(),
60 pageId: z.number(),
61 order: z.number().default(0).optional(),
62 monitor: selectMonitorSchema,
63 }),
64 ),
65 maintenances: selectMaintenanceSchema.array().default([]),
66 statusReports: selectStatusReportSchema
67 .extend({ statusReportUpdates: selectStatusReportUpdateSchema.array() })
68 .array()
69 .default([]),
70});
71
72export const legacy_selectPublicPageSchemaWithRelation = selectPageSchema
73 .extend({
74 monitors: z.array(selectPublicMonitorSchema).default([]),
75 statusReports: z.array(selectStatusReportPageSchema).default([]),
76 incidents: z.array(selectIncidentSchema).default([]),
77 maintenances: z.array(selectMaintenancePageSchema).default([]),
78 workspacePlan: workspacePlanSchema
79 .nullable()
80 .default("free")
81 .transform((val) => val ?? "free"),
82 })
83 .omit({
84 // workspaceId: true,
85 id: true,
86 });
87
88const selectPublicMonitorWithStatusSchema = selectPublicMonitorSchema.extend({
89 status: z.enum(["success", "degraded", "error", "info"]).default("success"),
90 monitorGroupId: z.number().nullable().optional(),
91 order: z.number().default(0).optional(),
92 groupOrder: z.number().default(0).optional(),
93});
94
95const trackersSchema = z
96 .array(
97 z.discriminatedUnion("type", [
98 z.object({
99 type: z.literal("monitor"),
100 monitor: selectPublicMonitorWithStatusSchema,
101 order: z.number(),
102 }),
103 z.object({
104 type: z.literal("group"),
105 groupId: z.number(),
106 groupName: z.string(),
107 monitors: z.array(selectPublicMonitorWithStatusSchema),
108 status: z
109 .enum(["success", "degraded", "error", "info"])
110 .default("success"),
111 order: z.number(),
112 }),
113 ]),
114 )
115 .default([]);
116
117export const selectPublicPageSchemaWithRelation = selectPageSchema.extend({
118 monitorGroups: selectMonitorGroupSchema.array().default([]),
119 // TODO: include status of the monitor
120 monitors: selectPublicMonitorWithStatusSchema.array(),
121 trackers: trackersSchema,
122 lastEvents: z.array(
123 z.object({
124 id: z.number(),
125 name: z.string(),
126 from: z.date(),
127 to: z.date().nullable(),
128 status: z
129 .enum(["success", "degraded", "error", "info"])
130 .default("success"),
131 type: z.enum(["maintenance", "incident", "report"]),
132 }),
133 ),
134 openEvents: z.array(
135 z.object({
136 id: z.number(),
137 name: z.string(),
138 from: z.date(),
139 to: z.date().nullable(),
140 status: z
141 .enum(["success", "degraded", "error", "info"])
142 .default("success"),
143 type: z.enum(["maintenance", "incident", "report"]),
144 }),
145 ),
146 statusReports: z.array(selectStatusReportPageSchema),
147 incidents: z.array(selectIncidentSchema),
148 maintenances: z.array(selectMaintenancePageSchema),
149 status: z.enum(["success", "degraded", "error", "info"]).default("success"),
150 workspacePlan: workspacePlanSchema
151 .nullable()
152 .default("free")
153 .transform((val) => val ?? "free"),
154});
155
156export const selectPublicStatusReportSchemaWithRelation =
157 selectStatusReportSchema.extend({
158 monitorsToStatusReports: z
159 .array(
160 z.object({
161 monitorId: z.number(),
162 statusReportId: z.number(),
163 monitor: selectPublicMonitorSchema,
164 }),
165 )
166 .default([]),
167 statusReportUpdates: z.array(selectStatusReportUpdateSchema),
168 });
169
170export type StatusReportWithUpdates = z.infer<
171 typeof selectStatusReportPageSchema
172>;
173export type PublicMonitor = z.infer<typeof selectPublicMonitorSchema>;
174export type PublicPage = z.infer<
175 typeof legacy_selectPublicPageSchemaWithRelation
176>;