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