Openstatus
www.openstatus.dev
1import { createClient } from "@libsql/client";
2import { drizzle } from "drizzle-orm/libsql";
3
4import { env } from "../env.mjs";
5import {
6 incidentTable,
7 maintenance,
8 maintenancesToMonitors,
9 monitor,
10 monitorsToPages,
11 monitorsToStatusReport,
12 notification,
13 notificationsToMonitors,
14 page,
15 privateLocation,
16 privateLocationToMonitors,
17 statusReport,
18 statusReportUpdate,
19 user,
20 usersToWorkspaces,
21 workspace,
22} from "./schema";
23
24async function main() {
25 const db = drizzle(
26 createClient({ url: env.DATABASE_URL, authToken: env.DATABASE_AUTH_TOKEN }),
27 );
28 console.log("Seeding database ");
29 await db
30 .insert(workspace)
31 .values([
32 {
33 id: 1,
34 slug: "love-openstatus",
35 stripeId: "stripeId1",
36 name: "test",
37 subscriptionId: "subscriptionId",
38 plan: "team",
39 endsAt: null,
40 paidUntil: null,
41 limits:
42 '{"monitors":50,"synthetic-checks":150000,"periodicity":["30s","1m","5m","10m","30m","1h"],"multi-region":true,"max-regions":35,"data-retention":"24 months","status-pages":20,"maintenance":true,"status-subscribers":true,"custom-domain":true,"password-protection":true,"white-label":true,"notifications":true,"sms":true,"pagerduty":true,"notification-channels":50,"members":"Unlimited","audit-log":true,"regions":["ams","arn","atl","bog","bom","bos","cdg","den","dfw","ewr","eze","fra","gdl","gig","gru","hkg","iad","jnb","lax","lhr","mad","mia","nrt","ord","otp","phx","qro","scl","sea","sin","sjc","syd","waw","yul","yyz"]}',
43 },
44 {
45 id: 2,
46 slug: "test2",
47 stripeId: "stripeId2",
48 name: "test2",
49 subscriptionId: "subscriptionId2",
50 plan: "free",
51 endsAt: null,
52 paidUntil: null,
53 },
54 {
55 id: 3,
56 slug: "test3",
57 stripeId: "stripeId3",
58 name: "test3",
59 subscriptionId: "subscriptionId3",
60 plan: "team",
61 endsAt: null,
62 paidUntil: null,
63 },
64 ])
65 .onConflictDoNothing()
66 .run();
67
68 await db
69 .insert(monitor)
70 .values([
71 {
72 id: 1,
73 workspaceId: 1,
74 active: true,
75 url: "https://www.openstatus.dev",
76 name: "OpenStatus",
77 description: "OpenStatus website",
78 method: "POST",
79 periodicity: "1m",
80 regions: "ams",
81 headers: '[{"key":"key", "value":"value"}]',
82 body: '{"hello":"world"}',
83 },
84 {
85 id: 2,
86 active: false,
87 workspaceId: 1,
88 periodicity: "10m",
89 url: "https://www.google.com",
90 method: "GET",
91 regions: "gru",
92 public: true,
93 },
94 {
95 id: 3,
96 workspaceId: 1,
97 active: true,
98 url: "https://www.openstatus.dev",
99 name: "OpenStatus",
100 description: "OpenStatus website",
101 method: "GET",
102 periodicity: "1m",
103 regions: "ams",
104 headers: '[{"key":"key", "value":"value"}]',
105 body: '{"hello":"world"}',
106 },
107 {
108 id: 4,
109 active: true,
110 workspaceId: 1,
111 periodicity: "10m",
112 url: "https://www.google.com",
113 method: "GET",
114 regions: "gru",
115 public: true,
116 otelEndpoint: "https://otel.com:4337",
117 otelHeaders: '[{"key":"Authorization","value":"Basic"}]',
118 },
119 {
120 id: 5,
121 active: true,
122 workspaceId: 3,
123 periodicity: "10m",
124 url: "https://openstat.us",
125 method: "GET",
126 regions: "ams",
127 public: true,
128 },
129 ])
130 .onConflictDoNothing()
131 .run();
132
133 await db
134 .insert(page)
135 .values({
136 id: 1,
137 workspaceId: 1,
138 title: "Acme Inc.",
139 description: "Get informed about our services.",
140 icon: "https://www.openstatus.dev/favicon.ico",
141 slug: "status",
142 customDomain: "",
143 published: true,
144 })
145 .onConflictDoNothing()
146 .run();
147
148 await db
149 .insert(user)
150 .values({
151 id: 1,
152 tenantId: "1",
153 firstName: "Speed",
154 lastName: "Matters",
155 email: "ping@openstatus.dev",
156 photoUrl: "",
157 })
158 .onConflictDoNothing()
159 .run();
160 await db
161 .insert(usersToWorkspaces)
162 .values({ workspaceId: 1, userId: 1 })
163 .onConflictDoNothing()
164 .run();
165
166 await db
167 .insert(monitorsToPages)
168 .values({ monitorId: 1, pageId: 1 })
169 .onConflictDoNothing()
170 .run();
171 await db
172 .insert(notification)
173 .values({
174 id: 1,
175 provider: "email",
176 name: "sample test notification",
177 data: '{"email":"ping@openstatus.dev"}',
178 workspaceId: 1,
179 })
180 .onConflictDoNothing()
181 .run();
182
183 await db
184 .insert(notificationsToMonitors)
185 .values({ monitorId: 1, notificationId: 1 })
186 .onConflictDoNothing()
187 .run();
188
189 // Status Report 1 - Resolved incident from 7 days ago
190 const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
191 const sevenDaysAgoPlus30Min = new Date(
192 sevenDaysAgo.getTime() + 30 * 60 * 1000,
193 );
194 const sevenDaysAgoPlus90Min = new Date(
195 sevenDaysAgo.getTime() + 90 * 60 * 1000,
196 );
197 const sevenDaysAgoPlus120Min = new Date(
198 sevenDaysAgo.getTime() + 120 * 60 * 1000,
199 );
200
201 await db
202 .insert(statusReport)
203 .values({
204 id: 1,
205 workspaceId: 1,
206 pageId: 1,
207 title: "API Gateway Degraded Performance",
208 status: "resolved",
209 updatedAt: sevenDaysAgoPlus120Min,
210 })
211 .onConflictDoNothing()
212 .run();
213
214 await db
215 .insert(statusReportUpdate)
216 .values([
217 {
218 id: 1,
219 statusReportId: 1,
220 status: "investigating",
221 message:
222 "We are investigating reports of slow API response times affecting some customers.",
223 date: sevenDaysAgo,
224 },
225 {
226 id: 2,
227 statusReportId: 1,
228 status: "identified",
229 message:
230 "We have identified the issue as a database connection pool exhaustion and are working on a fix.",
231 date: sevenDaysAgoPlus30Min,
232 },
233 {
234 id: 3,
235 statusReportId: 1,
236 status: "monitoring",
237 message:
238 "A fix has been deployed and we are monitoring the system. Response times are returning to normal.",
239 date: sevenDaysAgoPlus90Min,
240 },
241 {
242 id: 4,
243 statusReportId: 1,
244 status: "resolved",
245 message:
246 "All systems are operating normally. The issue has been fully resolved.",
247 date: sevenDaysAgoPlus120Min,
248 },
249 ])
250 .onConflictDoNothing()
251 .run();
252
253 // Status Report 2 - Ongoing incident from 2 hours ago
254 const twoHoursAgo = new Date(Date.now() - 2 * 60 * 60 * 1000);
255 const oneHourAgo = new Date(Date.now() - 1 * 60 * 60 * 1000);
256 const twentyMinutesAgo = new Date(Date.now() - 20 * 60 * 1000);
257
258 await db
259 .insert(statusReport)
260 .values({
261 id: 2,
262 workspaceId: 1,
263 pageId: 1,
264 title: "Increased Error Rates on Monitoring Checks",
265 status: "resolved",
266 updatedAt: oneHourAgo,
267 })
268 .onConflictDoNothing()
269 .run();
270
271 await db
272 .insert(statusReportUpdate)
273 .values([
274 {
275 id: 5,
276 statusReportId: 2,
277 status: "investigating",
278 message:
279 "We are seeing elevated error rates on some monitoring checks and are investigating the root cause.",
280 date: twoHoursAgo,
281 },
282 {
283 id: 6,
284 statusReportId: 2,
285 status: "monitoring",
286 message:
287 "We have applied a fix and are monitoring the situation. Error rates are decreasing.",
288 date: oneHourAgo,
289 },
290 {
291 id: 7,
292 statusReportId: 2,
293 status: "resolved",
294 message:
295 "Everything is under control, we continue to monitor the situation.",
296 date: twentyMinutesAgo,
297 },
298 ])
299 .onConflictDoNothing()
300 .run();
301
302 // Maintenance windows spread across 30 days
303 const twentyDaysAgo = new Date(Date.now() - 20 * 24 * 60 * 60 * 1000);
304 const twentyDaysAgoPlus2Hours = new Date(
305 twentyDaysAgo.getTime() + 2 * 60 * 60 * 1000,
306 );
307
308 const fiveDaysFromNow = new Date(Date.now() + 5 * 24 * 60 * 60 * 1000);
309 const fiveDaysFromNowPlus4Hours = new Date(
310 fiveDaysFromNow.getTime() + 4 * 60 * 60 * 1000,
311 );
312
313 await db
314 .insert(maintenance)
315 .values([
316 {
317 id: 1,
318 workspaceId: 1,
319 title: "Database Migration and Optimization",
320 message:
321 "We will be performing database maintenance to improve performance. Some queries may be slower during this window.",
322 from: twentyDaysAgo,
323 to: twentyDaysAgoPlus2Hours,
324 pageId: 1,
325 },
326 {
327 id: 2,
328 workspaceId: 1,
329 title: "Infrastructure Upgrade",
330 message:
331 "We will be upgrading our monitoring infrastructure to the latest version. Expect brief interruptions in data collection.",
332 from: fiveDaysFromNow,
333 to: fiveDaysFromNowPlus4Hours,
334 pageId: 1,
335 },
336 ])
337 .onConflictDoNothing()
338 .run();
339
340 await db
341 .insert(maintenancesToMonitors)
342 .values([
343 {
344 maintenanceId: 1,
345 monitorId: 1,
346 },
347 ])
348 .onConflictDoNothing()
349 .run();
350
351 await db
352 .insert(monitorsToStatusReport)
353 .values([
354 {
355 monitorId: 1,
356 statusReportId: 2,
357 },
358 ])
359 .onConflictDoNothing()
360 .run();
361
362 // Incidents - realistic past incidents that were resolved
363 const fifteenDaysAgo = new Date(Date.now() - 15 * 24 * 60 * 60 * 1000);
364 const fifteenDaysAgoPlus2Hours = new Date(
365 fifteenDaysAgo.getTime() + 2 * 60 * 60 * 1000,
366 );
367
368 const threeDaysAgo = new Date(Date.now() - 3 * 24 * 60 * 60 * 1000);
369 const threeDaysAgoPlus20Min = new Date(
370 threeDaysAgo.getTime() + 20 * 60 * 1000,
371 );
372
373 await db
374 .insert(incidentTable)
375 .values([
376 {
377 id: 1,
378 workspaceId: 1,
379 monitorId: 1,
380 createdAt: fifteenDaysAgo,
381 startedAt: fifteenDaysAgo,
382 acknowledgedAt: new Date(fifteenDaysAgo.getTime() + 5 * 60 * 1000),
383 resolvedAt: fifteenDaysAgoPlus2Hours,
384 },
385 {
386 id: 2,
387 workspaceId: 1,
388 monitorId: 1,
389 createdAt: threeDaysAgo,
390 startedAt: threeDaysAgo,
391 acknowledgedAt: new Date(threeDaysAgo.getTime() + 2 * 60 * 1000),
392 resolvedAt: threeDaysAgoPlus20Min,
393 },
394 ])
395 .onConflictDoNothing()
396 .run();
397
398 await db
399 .insert(privateLocation)
400 .values({
401 id: 1,
402 name: "My Home",
403 token: "my-secret-key",
404 workspaceId: 3,
405 createdAt: new Date(),
406 })
407 .onConflictDoNothing()
408 .run();
409 await db
410 .insert(privateLocationToMonitors)
411 .values({
412 privateLocationId: 1,
413 monitorId: 5,
414 createdAt: new Date(),
415 })
416 .onConflictDoNothing()
417 .run();
418 process.exit(0);
419}
420
421main().catch((e) => {
422 console.error("Seed failed");
423 console.error(e);
424 process.exit(1);
425});