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 { selectPageComponentGroupSchema } from "./page_component_groups";
8import { selectPageComponentSchema } from "./page_components";
9import { selectPageSchema } from "./pages";
10import {
11 selectStatusReportSchema,
12 selectStatusReportUpdateSchema,
13} from "./status_reports";
14import { workspacePlanSchema } from "./workspaces";
15
16// TODO: create a 'public-status' schema with all the different types and validations
17
18// Base schema without transform so it can be extended
19const selectPublicMonitorBaseSchema = selectMonitorSchema.omit({
20 body: true,
21 headers: true,
22 method: true,
23 otelEndpoint: true,
24 otelHeaders: true,
25});
26
27export const selectPublicMonitorSchema =
28 selectPublicMonitorBaseSchema.transform((data) => ({
29 ...data,
30 name: data.externalName || data.name,
31 }));
32
33export const selectStatusReportPageSchema = selectStatusReportSchema.extend({
34 statusReportUpdates: z.array(selectStatusReportUpdateSchema).prefault([]),
35 statusReportsToPageComponents: z
36 .array(
37 z.object({
38 pageComponentId: z.number(),
39 statusReportId: z.number(),
40 pageComponent: selectPageComponentSchema,
41 }),
42 )
43 .prefault([]),
44});
45
46export const selectMaintenancePageSchema = selectMaintenanceSchema.extend({
47 maintenancesToPageComponents: z
48 .array(
49 z.object({
50 pageComponentId: z.number(),
51 maintenanceId: z.number(),
52 pageComponent: selectPageComponentSchema,
53 }),
54 )
55 .prefault([]),
56});
57
58export const selectPageSchemaWithRelation = selectPageSchema.extend({
59 monitors: z.array(selectMonitorSchema),
60 statusReports: z.array(selectStatusReportPageSchema),
61});
62
63export const legacy_selectPublicPageSchemaWithRelation = selectPageSchema
64 .extend({
65 monitors: z.array(selectPublicMonitorSchema).prefault([]),
66 statusReports: z.array(selectStatusReportPageSchema).prefault([]),
67 incidents: z.array(selectIncidentSchema).prefault([]),
68 maintenances: z.array(selectMaintenancePageSchema).prefault([]),
69 workspacePlan: workspacePlanSchema
70 .nullable()
71 .prefault("free")
72 .transform((val) => val ?? "free"),
73 })
74 .omit({
75 // workspaceId: true,
76 id: true,
77 });
78
79const selectPublicMonitorWithStatusSchema = selectPublicMonitorBaseSchema
80 .extend({
81 status: z
82 .enum(["success", "degraded", "error", "info"])
83 .prefault("success"),
84 monitorGroupId: z.number().nullable().optional(),
85 order: z.number().default(0).optional(),
86 groupOrder: z.number().default(0).nullish(),
87 })
88 .transform((data) => ({
89 ...data,
90 name: data.externalName || data.name,
91 }));
92
93export const statusPageEventSchema = z.object({
94 id: z.number(),
95 name: z.string(),
96 from: z.date(),
97 to: z.date().nullable(),
98 status: z.enum(["success", "degraded", "error", "info"]).prefault("success"),
99 type: z.enum(["maintenance", "incident", "report"]),
100});
101
102// Page component with status - used for new tracker system
103const selectPublicPageComponentWithStatusSchema =
104 selectPageComponentSchema.extend({
105 status: z
106 .enum(["success", "degraded", "error", "info"])
107 .prefault("success"),
108 events: z.array(statusPageEventSchema).prefault([]),
109 // For monitor-type components - omit status since it's now at component level
110 monitor: selectPublicMonitorBaseSchema
111 .extend({
112 incidents: selectIncidentSchema.array().nullish(),
113 })
114 .nullish(),
115 });
116
117const trackersSchema = z
118 .array(
119 z.discriminatedUnion("type", [
120 z.object({
121 type: z.literal("component"),
122 component: selectPublicPageComponentWithStatusSchema,
123 order: z.number(),
124 }),
125 z.object({
126 type: z.literal("group"),
127 groupId: z.number(),
128 groupName: z.string(),
129 components: z.array(selectPublicPageComponentWithStatusSchema),
130 status: z
131 .enum(["success", "degraded", "error", "info"])
132 .prefault("success"),
133 order: z.number(),
134 }),
135 ]),
136 )
137 .prefault([]);
138
139export const selectPageComponentWithMonitorRelation =
140 selectPageComponentSchema.extend({
141 monitor: selectPublicMonitorBaseSchema
142 .extend({
143 incidents: selectIncidentSchema.array().nullish(),
144 })
145 .transform((data) => ({
146 ...data,
147 name: data.externalName || data.name,
148 }))
149 .nullish(),
150 group: selectPageComponentGroupSchema.nullish(),
151 });
152
153export type PageComponentWithMonitorRelation = z.infer<
154 typeof selectPageComponentWithMonitorRelation
155>;
156
157export const selectPublicPageLightSchemaWithRelation = selectPageSchema
158 .extend({
159 monitors: z.array(selectPublicMonitorSchema).prefault([]),
160 statusReports: z.array(selectStatusReportPageSchema).prefault([]),
161 incidents: z.array(selectIncidentSchema).prefault([]),
162 maintenances: z.array(selectMaintenancePageSchema).prefault([]),
163 workspacePlan: workspacePlanSchema
164 .nullable()
165 .prefault("free")
166 .transform((val) => val ?? "free"),
167 // NEW: Include pageComponents for modern consumers
168 pageComponents: selectPageComponentWithMonitorRelation.array().prefault([]),
169 pageComponentGroups: selectPageComponentGroupSchema.array().prefault([]),
170 })
171 .omit({
172 id: true,
173 });
174
175export const selectPublicPageSchemaWithRelation = selectPageSchema.extend({
176 monitorGroups: selectMonitorGroupSchema.array().prefault([]),
177 // TODO: include status of the monitor
178 monitors: selectPublicMonitorWithStatusSchema.array(),
179 pageComponents: selectPageComponentWithMonitorRelation.array().prefault([]),
180 pageComponentGroups: selectPageComponentGroupSchema.array().prefault([]),
181 trackers: trackersSchema,
182 lastEvents: z.array(statusPageEventSchema),
183 openEvents: z.array(statusPageEventSchema),
184 statusReports: z.array(selectStatusReportPageSchema),
185 incidents: z.array(selectIncidentSchema),
186 maintenances: z.array(selectMaintenancePageSchema),
187 status: z.enum(["success", "degraded", "error", "info"]).prefault("success"),
188 workspacePlan: workspacePlanSchema
189 .nullable()
190 .prefault("free")
191 .transform((val) => val ?? "free"),
192 whiteLabel: z.boolean().prefault(false),
193});
194
195export type StatusReportWithUpdates = z.infer<
196 typeof selectStatusReportPageSchema
197>;
198export type PublicMonitor = z.infer<typeof selectPublicMonitorSchema>;
199export type PublicPage = z.infer<
200 typeof legacy_selectPublicPageSchemaWithRelation
201>;
202export type PublicPageComponentWithStatus = z.infer<
203 typeof selectPublicPageComponentWithStatusSchema
204>;