Openstatus www.openstatus.dev

feat: add monitor status table (#480)

* feat: add monitor status table

* fix: upsert missing index and update cron

* chore: set updatedAt

* fix: squash index

authored by

Maximilian Kaske and committed by
GitHub
0e08a6f2 b0ae2bdb

+1245 -11
+13 -6
apps/server/src/checker/alerting.ts
··· 1 1 import { db, eq, schema } from "@openstatus/db"; 2 - import type { MonitorStatus } from "@openstatus/db/src/schema"; 2 + import type { MonitorRegion, MonitorStatus } from "@openstatus/db/src/schema"; 3 3 import { 4 4 selectMonitorSchema, 5 5 selectNotificationSchema, 6 6 } from "@openstatus/db/src/schema"; 7 7 import { flyRegionsDict } from "@openstatus/utils"; 8 8 9 + import { env } from "../env"; 9 10 import { providerToFunction } from "./utils"; 10 11 11 12 export const triggerAlerting = async ({ ··· 45 46 } 46 47 }; 47 48 48 - export const updateMonitorStatus = async ({ 49 + export const upsertMonitorStatus = async ({ 49 50 monitorId, 50 51 status, 51 52 }: { 52 53 monitorId: string; 53 54 status: MonitorStatus; 54 55 }) => { 56 + const region = env.FLY_REGION as MonitorRegion; 55 57 await db 56 - .update(schema.monitor) 57 - .set({ status }) 58 - .where(eq(schema.monitor.id, Number(monitorId))) 59 - .run(); 58 + .insert(schema.monitorStatusTable) 59 + .values({ status, region, monitorId: Number(monitorId) }) 60 + .onConflictDoUpdate({ 61 + target: [ 62 + schema.monitorStatusTable.monitorId, 63 + schema.monitorStatusTable.region, 64 + ], 65 + set: { status, updatedAt: new Date() }, 66 + }); 60 67 };
+3 -3
apps/server/src/checker/checker.ts
··· 1 1 import { env } from "../env"; 2 - import { triggerAlerting, updateMonitorStatus } from "./alerting"; 2 + import { triggerAlerting, upsertMonitorStatus } from "./alerting"; 3 3 import type { PublishPingType } from "./ping"; 4 4 import { pingEndpoint, publishPing } from "./ping"; 5 5 import type { Payload } from "./schema"; ··· 76 76 message: undefined, 77 77 }); 78 78 if (data?.status === "error") { 79 - await updateMonitorStatus({ 79 + await upsertMonitorStatus({ 80 80 monitorId: data.monitorId, 81 81 status: "active", 82 82 }); ··· 100 100 }); 101 101 102 102 if (data?.status === "active") { 103 - await updateMonitorStatus({ 103 + await upsertMonitorStatus({ 104 104 monitorId: data.monitorId, 105 105 status: "error", 106 106 });
+3 -1
apps/web/.env.example
··· 71 71 GCP_LOCATION= 72 72 GCP_CLIENT_EMAIL= 73 73 GCP_PRIVATE_KEY= 74 - CRON_SECRET= 74 + CRON_SECRET= 75 + 76 + EXTERNAL_API_URL=
+9 -1
apps/web/src/app/api/checker/cron/_cron.ts
··· 55 55 const allResult = []; 56 56 57 57 for (const row of monitors) { 58 + // TODO: remove - this is not used anymore | remember to update the `type Payload` 58 59 const allPages = await caller.monitor.getAllPagesForMonitor({ 59 60 monitorId: row.id, 60 61 }); 61 62 const selectedRegions = row.regions.length > 1 ? row.regions : ["auto"]; 63 + 64 + const monitorStatus = await caller.monitor.getMonitorStatusByMonitorId({ 65 + monitorId: row.id, 66 + }); 67 + 62 68 for (const region of selectedRegions) { 63 69 const payload: z.infer<typeof payloadSchema> = { 64 70 workspaceId: String(row.workspaceId), ··· 69 75 body: row.body, 70 76 headers: row.headers, 71 77 pageIds: allPages.map((p) => String(p.pageId)), 72 - status: row.status, 78 + status: 79 + monitorStatus.find(({ region }) => region === region)?.status || 80 + "active", 73 81 }; 74 82 75 83 const task: google.cloud.tasks.v2beta3.ITask = {
+28
packages/api/src/router/monitor.ts
··· 4 4 import { and, eq, inArray, sql } from "@openstatus/db"; 5 5 import { 6 6 insertMonitorSchema, 7 + insertMonitorStatusSchema, 7 8 monitor, 8 9 monitorPeriodicitySchema, 10 + monitorStatusTable, 9 11 monitorsToPages, 10 12 notification, 11 13 notificationsToMonitors, 12 14 selectMonitorSchema, 15 + selectMonitorStatusSchema, 13 16 selectNotificationSchema, 14 17 } from "@openstatus/db/src/schema"; 15 18 import { allPlans } from "@openstatus/plans"; ··· 238 241 ) 239 242 .all(); 240 243 return z.array(selectMonitorSchema).parse(result); 244 + }), 245 + 246 + getMonitorStatusByMonitorId: cronProcedure 247 + .input(z.object({ monitorId: z.number() })) 248 + .query(async (opts) => { 249 + const result = await opts.ctx.db 250 + .select() 251 + .from(monitorStatusTable) 252 + .where(eq(monitorStatusTable.monitorId, opts.input.monitorId)) 253 + .all(); 254 + return z.array(selectMonitorStatusSchema).parse(result); 255 + }), 256 + 257 + // FOR TESTING 258 + upsertMonitorStatus: cronProcedure 259 + .input(insertMonitorStatusSchema) 260 + .mutation(async (opts) => { 261 + const { status, region, monitorId } = opts.input; 262 + await opts.ctx.db 263 + .insert(monitorStatusTable) 264 + .values({ status, region, monitorId: Number(monitorId) }) 265 + .onConflictDoUpdate({ 266 + target: [monitorStatusTable.monitorId, monitorStatusTable.region], 267 + set: { status, updatedAt: new Date() }, 268 + }); 241 269 }), 242 270 243 271 getAllPagesForMonitor: cronProcedure
+11
packages/db/drizzle/0010_lame_songbird.sql
··· 1 + CREATE TABLE `monitor_status` ( 2 + `monitor_id` integer NOT NULL, 3 + `region` text DEFAULT '' NOT NULL, 4 + `status` text DEFAULT 'active' NOT NULL, 5 + `created_at` integer DEFAULT (strftime('%s', 'now')), 6 + `updated_at` integer DEFAULT (strftime('%s', 'now')), 7 + PRIMARY KEY(`monitor_id`, `region`), 8 + FOREIGN KEY (`monitor_id`) REFERENCES `monitor`(`id`) ON UPDATE no action ON DELETE cascade 9 + ); 10 + --> statement-breakpoint 11 + CREATE INDEX `monitor_status_idx` ON `monitor_status` (`monitor_id`,`region`);
+1094
packages/db/drizzle/meta/0010_snapshot.json
··· 1 + { 2 + "version": "5", 3 + "dialect": "sqlite", 4 + "id": "452e7037-85cf-4d9a-b4e2-375a695d5fa8", 5 + "prevId": "0f3dddb6-09cf-46a4-b12c-c5bb82475b94", 6 + "tables": { 7 + "incident": { 8 + "name": "incident", 9 + "columns": { 10 + "id": { 11 + "name": "id", 12 + "type": "integer", 13 + "primaryKey": true, 14 + "notNull": true, 15 + "autoincrement": false 16 + }, 17 + "status": { 18 + "name": "status", 19 + "type": "text", 20 + "primaryKey": false, 21 + "notNull": true, 22 + "autoincrement": false 23 + }, 24 + "title": { 25 + "name": "title", 26 + "type": "text(256)", 27 + "primaryKey": false, 28 + "notNull": true, 29 + "autoincrement": false 30 + }, 31 + "workspace_id": { 32 + "name": "workspace_id", 33 + "type": "integer", 34 + "primaryKey": false, 35 + "notNull": false, 36 + "autoincrement": false 37 + }, 38 + "created_at": { 39 + "name": "created_at", 40 + "type": "integer", 41 + "primaryKey": false, 42 + "notNull": false, 43 + "autoincrement": false, 44 + "default": "(strftime('%s', 'now'))" 45 + }, 46 + "updated_at": { 47 + "name": "updated_at", 48 + "type": "integer", 49 + "primaryKey": false, 50 + "notNull": false, 51 + "autoincrement": false, 52 + "default": "(strftime('%s', 'now'))" 53 + } 54 + }, 55 + "indexes": {}, 56 + "foreignKeys": { 57 + "incident_workspace_id_workspace_id_fk": { 58 + "name": "incident_workspace_id_workspace_id_fk", 59 + "tableFrom": "incident", 60 + "tableTo": "workspace", 61 + "columnsFrom": [ 62 + "workspace_id" 63 + ], 64 + "columnsTo": [ 65 + "id" 66 + ], 67 + "onDelete": "no action", 68 + "onUpdate": "no action" 69 + } 70 + }, 71 + "compositePrimaryKeys": {}, 72 + "uniqueConstraints": {} 73 + }, 74 + "incident_update": { 75 + "name": "incident_update", 76 + "columns": { 77 + "id": { 78 + "name": "id", 79 + "type": "integer", 80 + "primaryKey": true, 81 + "notNull": true, 82 + "autoincrement": false 83 + }, 84 + "status": { 85 + "name": "status", 86 + "type": "text(4)", 87 + "primaryKey": false, 88 + "notNull": true, 89 + "autoincrement": false 90 + }, 91 + "date": { 92 + "name": "date", 93 + "type": "integer", 94 + "primaryKey": false, 95 + "notNull": true, 96 + "autoincrement": false 97 + }, 98 + "message": { 99 + "name": "message", 100 + "type": "text", 101 + "primaryKey": false, 102 + "notNull": true, 103 + "autoincrement": false 104 + }, 105 + "incident_id": { 106 + "name": "incident_id", 107 + "type": "integer", 108 + "primaryKey": false, 109 + "notNull": true, 110 + "autoincrement": false 111 + }, 112 + "created_at": { 113 + "name": "created_at", 114 + "type": "integer", 115 + "primaryKey": false, 116 + "notNull": false, 117 + "autoincrement": false, 118 + "default": "(strftime('%s', 'now'))" 119 + }, 120 + "updated_at": { 121 + "name": "updated_at", 122 + "type": "integer", 123 + "primaryKey": false, 124 + "notNull": false, 125 + "autoincrement": false, 126 + "default": "(strftime('%s', 'now'))" 127 + } 128 + }, 129 + "indexes": {}, 130 + "foreignKeys": { 131 + "incident_update_incident_id_incident_id_fk": { 132 + "name": "incident_update_incident_id_incident_id_fk", 133 + "tableFrom": "incident_update", 134 + "tableTo": "incident", 135 + "columnsFrom": [ 136 + "incident_id" 137 + ], 138 + "columnsTo": [ 139 + "id" 140 + ], 141 + "onDelete": "cascade", 142 + "onUpdate": "no action" 143 + } 144 + }, 145 + "compositePrimaryKeys": {}, 146 + "uniqueConstraints": {} 147 + }, 148 + "incidents_to_monitors": { 149 + "name": "incidents_to_monitors", 150 + "columns": { 151 + "monitor_id": { 152 + "name": "monitor_id", 153 + "type": "integer", 154 + "primaryKey": false, 155 + "notNull": true, 156 + "autoincrement": false 157 + }, 158 + "incident_id": { 159 + "name": "incident_id", 160 + "type": "integer", 161 + "primaryKey": false, 162 + "notNull": true, 163 + "autoincrement": false 164 + } 165 + }, 166 + "indexes": {}, 167 + "foreignKeys": { 168 + "incidents_to_monitors_monitor_id_monitor_id_fk": { 169 + "name": "incidents_to_monitors_monitor_id_monitor_id_fk", 170 + "tableFrom": "incidents_to_monitors", 171 + "tableTo": "monitor", 172 + "columnsFrom": [ 173 + "monitor_id" 174 + ], 175 + "columnsTo": [ 176 + "id" 177 + ], 178 + "onDelete": "cascade", 179 + "onUpdate": "no action" 180 + }, 181 + "incidents_to_monitors_incident_id_incident_id_fk": { 182 + "name": "incidents_to_monitors_incident_id_incident_id_fk", 183 + "tableFrom": "incidents_to_monitors", 184 + "tableTo": "incident", 185 + "columnsFrom": [ 186 + "incident_id" 187 + ], 188 + "columnsTo": [ 189 + "id" 190 + ], 191 + "onDelete": "cascade", 192 + "onUpdate": "no action" 193 + } 194 + }, 195 + "compositePrimaryKeys": { 196 + "incidents_to_monitors_monitor_id_incident_id_pk": { 197 + "columns": [ 198 + "incident_id", 199 + "monitor_id" 200 + ] 201 + } 202 + }, 203 + "uniqueConstraints": {} 204 + }, 205 + "incidents_to_pages": { 206 + "name": "incidents_to_pages", 207 + "columns": { 208 + "page_id": { 209 + "name": "page_id", 210 + "type": "integer", 211 + "primaryKey": false, 212 + "notNull": true, 213 + "autoincrement": false 214 + }, 215 + "incident_id": { 216 + "name": "incident_id", 217 + "type": "integer", 218 + "primaryKey": false, 219 + "notNull": true, 220 + "autoincrement": false 221 + } 222 + }, 223 + "indexes": {}, 224 + "foreignKeys": { 225 + "incidents_to_pages_page_id_page_id_fk": { 226 + "name": "incidents_to_pages_page_id_page_id_fk", 227 + "tableFrom": "incidents_to_pages", 228 + "tableTo": "page", 229 + "columnsFrom": [ 230 + "page_id" 231 + ], 232 + "columnsTo": [ 233 + "id" 234 + ], 235 + "onDelete": "cascade", 236 + "onUpdate": "no action" 237 + }, 238 + "incidents_to_pages_incident_id_incident_id_fk": { 239 + "name": "incidents_to_pages_incident_id_incident_id_fk", 240 + "tableFrom": "incidents_to_pages", 241 + "tableTo": "incident", 242 + "columnsFrom": [ 243 + "incident_id" 244 + ], 245 + "columnsTo": [ 246 + "id" 247 + ], 248 + "onDelete": "cascade", 249 + "onUpdate": "no action" 250 + } 251 + }, 252 + "compositePrimaryKeys": { 253 + "incidents_to_pages_page_id_incident_id_pk": { 254 + "columns": [ 255 + "incident_id", 256 + "page_id" 257 + ] 258 + } 259 + }, 260 + "uniqueConstraints": {} 261 + }, 262 + "integration": { 263 + "name": "integration", 264 + "columns": { 265 + "id": { 266 + "name": "id", 267 + "type": "integer", 268 + "primaryKey": true, 269 + "notNull": true, 270 + "autoincrement": false 271 + }, 272 + "name": { 273 + "name": "name", 274 + "type": "text(256)", 275 + "primaryKey": false, 276 + "notNull": true, 277 + "autoincrement": false 278 + }, 279 + "workspace_id": { 280 + "name": "workspace_id", 281 + "type": "integer", 282 + "primaryKey": false, 283 + "notNull": false, 284 + "autoincrement": false 285 + }, 286 + "credential": { 287 + "name": "credential", 288 + "type": "text", 289 + "primaryKey": false, 290 + "notNull": false, 291 + "autoincrement": false 292 + }, 293 + "external_id": { 294 + "name": "external_id", 295 + "type": "text", 296 + "primaryKey": false, 297 + "notNull": true, 298 + "autoincrement": false 299 + }, 300 + "created_at": { 301 + "name": "created_at", 302 + "type": "integer", 303 + "primaryKey": false, 304 + "notNull": false, 305 + "autoincrement": false, 306 + "default": "(strftime('%s', 'now'))" 307 + }, 308 + "updated_at": { 309 + "name": "updated_at", 310 + "type": "integer", 311 + "primaryKey": false, 312 + "notNull": false, 313 + "autoincrement": false, 314 + "default": "(strftime('%s', 'now'))" 315 + }, 316 + "data": { 317 + "name": "data", 318 + "type": "text", 319 + "primaryKey": false, 320 + "notNull": true, 321 + "autoincrement": false 322 + } 323 + }, 324 + "indexes": {}, 325 + "foreignKeys": { 326 + "integration_workspace_id_workspace_id_fk": { 327 + "name": "integration_workspace_id_workspace_id_fk", 328 + "tableFrom": "integration", 329 + "tableTo": "workspace", 330 + "columnsFrom": [ 331 + "workspace_id" 332 + ], 333 + "columnsTo": [ 334 + "id" 335 + ], 336 + "onDelete": "no action", 337 + "onUpdate": "no action" 338 + } 339 + }, 340 + "compositePrimaryKeys": {}, 341 + "uniqueConstraints": {} 342 + }, 343 + "page": { 344 + "name": "page", 345 + "columns": { 346 + "id": { 347 + "name": "id", 348 + "type": "integer", 349 + "primaryKey": true, 350 + "notNull": true, 351 + "autoincrement": false 352 + }, 353 + "workspace_id": { 354 + "name": "workspace_id", 355 + "type": "integer", 356 + "primaryKey": false, 357 + "notNull": true, 358 + "autoincrement": false 359 + }, 360 + "title": { 361 + "name": "title", 362 + "type": "text", 363 + "primaryKey": false, 364 + "notNull": true, 365 + "autoincrement": false 366 + }, 367 + "description": { 368 + "name": "description", 369 + "type": "text", 370 + "primaryKey": false, 371 + "notNull": true, 372 + "autoincrement": false 373 + }, 374 + "icon": { 375 + "name": "icon", 376 + "type": "text(256)", 377 + "primaryKey": false, 378 + "notNull": false, 379 + "autoincrement": false, 380 + "default": "''" 381 + }, 382 + "slug": { 383 + "name": "slug", 384 + "type": "text(256)", 385 + "primaryKey": false, 386 + "notNull": true, 387 + "autoincrement": false 388 + }, 389 + "custom_domain": { 390 + "name": "custom_domain", 391 + "type": "text(256)", 392 + "primaryKey": false, 393 + "notNull": true, 394 + "autoincrement": false 395 + }, 396 + "published": { 397 + "name": "published", 398 + "type": "integer", 399 + "primaryKey": false, 400 + "notNull": false, 401 + "autoincrement": false, 402 + "default": false 403 + }, 404 + "created_at": { 405 + "name": "created_at", 406 + "type": "integer", 407 + "primaryKey": false, 408 + "notNull": false, 409 + "autoincrement": false, 410 + "default": "(strftime('%s', 'now'))" 411 + }, 412 + "updated_at": { 413 + "name": "updated_at", 414 + "type": "integer", 415 + "primaryKey": false, 416 + "notNull": false, 417 + "autoincrement": false, 418 + "default": "(strftime('%s', 'now'))" 419 + } 420 + }, 421 + "indexes": { 422 + "page_slug_unique": { 423 + "name": "page_slug_unique", 424 + "columns": [ 425 + "slug" 426 + ], 427 + "isUnique": true 428 + } 429 + }, 430 + "foreignKeys": { 431 + "page_workspace_id_workspace_id_fk": { 432 + "name": "page_workspace_id_workspace_id_fk", 433 + "tableFrom": "page", 434 + "tableTo": "workspace", 435 + "columnsFrom": [ 436 + "workspace_id" 437 + ], 438 + "columnsTo": [ 439 + "id" 440 + ], 441 + "onDelete": "cascade", 442 + "onUpdate": "no action" 443 + } 444 + }, 445 + "compositePrimaryKeys": {}, 446 + "uniqueConstraints": {} 447 + }, 448 + "monitor": { 449 + "name": "monitor", 450 + "columns": { 451 + "id": { 452 + "name": "id", 453 + "type": "integer", 454 + "primaryKey": true, 455 + "notNull": true, 456 + "autoincrement": false 457 + }, 458 + "job_type": { 459 + "name": "job_type", 460 + "type": "text", 461 + "primaryKey": false, 462 + "notNull": true, 463 + "autoincrement": false, 464 + "default": "'other'" 465 + }, 466 + "periodicity": { 467 + "name": "periodicity", 468 + "type": "text", 469 + "primaryKey": false, 470 + "notNull": true, 471 + "autoincrement": false, 472 + "default": "'other'" 473 + }, 474 + "status": { 475 + "name": "status", 476 + "type": "text", 477 + "primaryKey": false, 478 + "notNull": true, 479 + "autoincrement": false, 480 + "default": "'active'" 481 + }, 482 + "active": { 483 + "name": "active", 484 + "type": "integer", 485 + "primaryKey": false, 486 + "notNull": false, 487 + "autoincrement": false, 488 + "default": false 489 + }, 490 + "regions": { 491 + "name": "regions", 492 + "type": "text", 493 + "primaryKey": false, 494 + "notNull": true, 495 + "autoincrement": false, 496 + "default": "''" 497 + }, 498 + "url": { 499 + "name": "url", 500 + "type": "text(2048)", 501 + "primaryKey": false, 502 + "notNull": true, 503 + "autoincrement": false 504 + }, 505 + "name": { 506 + "name": "name", 507 + "type": "text(256)", 508 + "primaryKey": false, 509 + "notNull": true, 510 + "autoincrement": false, 511 + "default": "''" 512 + }, 513 + "description": { 514 + "name": "description", 515 + "type": "text", 516 + "primaryKey": false, 517 + "notNull": true, 518 + "autoincrement": false, 519 + "default": "''" 520 + }, 521 + "headers": { 522 + "name": "headers", 523 + "type": "text", 524 + "primaryKey": false, 525 + "notNull": false, 526 + "autoincrement": false, 527 + "default": "''" 528 + }, 529 + "body": { 530 + "name": "body", 531 + "type": "text", 532 + "primaryKey": false, 533 + "notNull": false, 534 + "autoincrement": false, 535 + "default": "''" 536 + }, 537 + "method": { 538 + "name": "method", 539 + "type": "text", 540 + "primaryKey": false, 541 + "notNull": false, 542 + "autoincrement": false, 543 + "default": "'GET'" 544 + }, 545 + "workspace_id": { 546 + "name": "workspace_id", 547 + "type": "integer", 548 + "primaryKey": false, 549 + "notNull": false, 550 + "autoincrement": false 551 + }, 552 + "created_at": { 553 + "name": "created_at", 554 + "type": "integer", 555 + "primaryKey": false, 556 + "notNull": false, 557 + "autoincrement": false, 558 + "default": "(strftime('%s', 'now'))" 559 + }, 560 + "updated_at": { 561 + "name": "updated_at", 562 + "type": "integer", 563 + "primaryKey": false, 564 + "notNull": false, 565 + "autoincrement": false, 566 + "default": "(strftime('%s', 'now'))" 567 + } 568 + }, 569 + "indexes": {}, 570 + "foreignKeys": { 571 + "monitor_workspace_id_workspace_id_fk": { 572 + "name": "monitor_workspace_id_workspace_id_fk", 573 + "tableFrom": "monitor", 574 + "tableTo": "workspace", 575 + "columnsFrom": [ 576 + "workspace_id" 577 + ], 578 + "columnsTo": [ 579 + "id" 580 + ], 581 + "onDelete": "no action", 582 + "onUpdate": "no action" 583 + } 584 + }, 585 + "compositePrimaryKeys": {}, 586 + "uniqueConstraints": {} 587 + }, 588 + "monitors_to_pages": { 589 + "name": "monitors_to_pages", 590 + "columns": { 591 + "monitor_id": { 592 + "name": "monitor_id", 593 + "type": "integer", 594 + "primaryKey": false, 595 + "notNull": true, 596 + "autoincrement": false 597 + }, 598 + "page_id": { 599 + "name": "page_id", 600 + "type": "integer", 601 + "primaryKey": false, 602 + "notNull": true, 603 + "autoincrement": false 604 + } 605 + }, 606 + "indexes": {}, 607 + "foreignKeys": { 608 + "monitors_to_pages_monitor_id_monitor_id_fk": { 609 + "name": "monitors_to_pages_monitor_id_monitor_id_fk", 610 + "tableFrom": "monitors_to_pages", 611 + "tableTo": "monitor", 612 + "columnsFrom": [ 613 + "monitor_id" 614 + ], 615 + "columnsTo": [ 616 + "id" 617 + ], 618 + "onDelete": "cascade", 619 + "onUpdate": "no action" 620 + }, 621 + "monitors_to_pages_page_id_page_id_fk": { 622 + "name": "monitors_to_pages_page_id_page_id_fk", 623 + "tableFrom": "monitors_to_pages", 624 + "tableTo": "page", 625 + "columnsFrom": [ 626 + "page_id" 627 + ], 628 + "columnsTo": [ 629 + "id" 630 + ], 631 + "onDelete": "cascade", 632 + "onUpdate": "no action" 633 + } 634 + }, 635 + "compositePrimaryKeys": { 636 + "monitors_to_pages_monitor_id_page_id_pk": { 637 + "columns": [ 638 + "monitor_id", 639 + "page_id" 640 + ] 641 + } 642 + }, 643 + "uniqueConstraints": {} 644 + }, 645 + "user": { 646 + "name": "user", 647 + "columns": { 648 + "id": { 649 + "name": "id", 650 + "type": "integer", 651 + "primaryKey": true, 652 + "notNull": true, 653 + "autoincrement": false 654 + }, 655 + "tenant_id": { 656 + "name": "tenant_id", 657 + "type": "text(256)", 658 + "primaryKey": false, 659 + "notNull": false, 660 + "autoincrement": false 661 + }, 662 + "first_name": { 663 + "name": "first_name", 664 + "type": "text", 665 + "primaryKey": false, 666 + "notNull": false, 667 + "autoincrement": false, 668 + "default": "''" 669 + }, 670 + "last_name": { 671 + "name": "last_name", 672 + "type": "text", 673 + "primaryKey": false, 674 + "notNull": false, 675 + "autoincrement": false, 676 + "default": "''" 677 + }, 678 + "email": { 679 + "name": "email", 680 + "type": "text", 681 + "primaryKey": false, 682 + "notNull": false, 683 + "autoincrement": false, 684 + "default": "''" 685 + }, 686 + "photo_url": { 687 + "name": "photo_url", 688 + "type": "text", 689 + "primaryKey": false, 690 + "notNull": false, 691 + "autoincrement": false, 692 + "default": "''" 693 + }, 694 + "created_at": { 695 + "name": "created_at", 696 + "type": "integer", 697 + "primaryKey": false, 698 + "notNull": false, 699 + "autoincrement": false, 700 + "default": "(strftime('%s', 'now'))" 701 + }, 702 + "updated_at": { 703 + "name": "updated_at", 704 + "type": "integer", 705 + "primaryKey": false, 706 + "notNull": false, 707 + "autoincrement": false, 708 + "default": "(strftime('%s', 'now'))" 709 + } 710 + }, 711 + "indexes": { 712 + "user_tenant_id_unique": { 713 + "name": "user_tenant_id_unique", 714 + "columns": [ 715 + "tenant_id" 716 + ], 717 + "isUnique": true 718 + } 719 + }, 720 + "foreignKeys": {}, 721 + "compositePrimaryKeys": {}, 722 + "uniqueConstraints": {} 723 + }, 724 + "users_to_workspaces": { 725 + "name": "users_to_workspaces", 726 + "columns": { 727 + "user_id": { 728 + "name": "user_id", 729 + "type": "integer", 730 + "primaryKey": false, 731 + "notNull": true, 732 + "autoincrement": false 733 + }, 734 + "workspace_id": { 735 + "name": "workspace_id", 736 + "type": "integer", 737 + "primaryKey": false, 738 + "notNull": true, 739 + "autoincrement": false 740 + } 741 + }, 742 + "indexes": {}, 743 + "foreignKeys": { 744 + "users_to_workspaces_user_id_user_id_fk": { 745 + "name": "users_to_workspaces_user_id_user_id_fk", 746 + "tableFrom": "users_to_workspaces", 747 + "tableTo": "user", 748 + "columnsFrom": [ 749 + "user_id" 750 + ], 751 + "columnsTo": [ 752 + "id" 753 + ], 754 + "onDelete": "no action", 755 + "onUpdate": "no action" 756 + }, 757 + "users_to_workspaces_workspace_id_workspace_id_fk": { 758 + "name": "users_to_workspaces_workspace_id_workspace_id_fk", 759 + "tableFrom": "users_to_workspaces", 760 + "tableTo": "workspace", 761 + "columnsFrom": [ 762 + "workspace_id" 763 + ], 764 + "columnsTo": [ 765 + "id" 766 + ], 767 + "onDelete": "no action", 768 + "onUpdate": "no action" 769 + } 770 + }, 771 + "compositePrimaryKeys": { 772 + "users_to_workspaces_user_id_workspace_id_pk": { 773 + "columns": [ 774 + "user_id", 775 + "workspace_id" 776 + ] 777 + } 778 + }, 779 + "uniqueConstraints": {} 780 + }, 781 + "workspace": { 782 + "name": "workspace", 783 + "columns": { 784 + "id": { 785 + "name": "id", 786 + "type": "integer", 787 + "primaryKey": true, 788 + "notNull": true, 789 + "autoincrement": false 790 + }, 791 + "slug": { 792 + "name": "slug", 793 + "type": "text", 794 + "primaryKey": false, 795 + "notNull": true, 796 + "autoincrement": false 797 + }, 798 + "name": { 799 + "name": "name", 800 + "type": "text", 801 + "primaryKey": false, 802 + "notNull": false, 803 + "autoincrement": false 804 + }, 805 + "stripe_id": { 806 + "name": "stripe_id", 807 + "type": "text(256)", 808 + "primaryKey": false, 809 + "notNull": false, 810 + "autoincrement": false 811 + }, 812 + "subscription_id": { 813 + "name": "subscription_id", 814 + "type": "text", 815 + "primaryKey": false, 816 + "notNull": false, 817 + "autoincrement": false 818 + }, 819 + "plan": { 820 + "name": "plan", 821 + "type": "text", 822 + "primaryKey": false, 823 + "notNull": false, 824 + "autoincrement": false 825 + }, 826 + "ends_at": { 827 + "name": "ends_at", 828 + "type": "integer", 829 + "primaryKey": false, 830 + "notNull": false, 831 + "autoincrement": false 832 + }, 833 + "paid_until": { 834 + "name": "paid_until", 835 + "type": "integer", 836 + "primaryKey": false, 837 + "notNull": false, 838 + "autoincrement": false 839 + }, 840 + "created_at": { 841 + "name": "created_at", 842 + "type": "integer", 843 + "primaryKey": false, 844 + "notNull": false, 845 + "autoincrement": false, 846 + "default": "(strftime('%s', 'now'))" 847 + }, 848 + "updated_at": { 849 + "name": "updated_at", 850 + "type": "integer", 851 + "primaryKey": false, 852 + "notNull": false, 853 + "autoincrement": false, 854 + "default": "(strftime('%s', 'now'))" 855 + } 856 + }, 857 + "indexes": { 858 + "workspace_slug_unique": { 859 + "name": "workspace_slug_unique", 860 + "columns": [ 861 + "slug" 862 + ], 863 + "isUnique": true 864 + }, 865 + "workspace_stripe_id_unique": { 866 + "name": "workspace_stripe_id_unique", 867 + "columns": [ 868 + "stripe_id" 869 + ], 870 + "isUnique": true 871 + } 872 + }, 873 + "foreignKeys": {}, 874 + "compositePrimaryKeys": {}, 875 + "uniqueConstraints": {} 876 + }, 877 + "notification": { 878 + "name": "notification", 879 + "columns": { 880 + "id": { 881 + "name": "id", 882 + "type": "integer", 883 + "primaryKey": true, 884 + "notNull": true, 885 + "autoincrement": false 886 + }, 887 + "name": { 888 + "name": "name", 889 + "type": "text", 890 + "primaryKey": false, 891 + "notNull": true, 892 + "autoincrement": false 893 + }, 894 + "provider": { 895 + "name": "provider", 896 + "type": "text", 897 + "primaryKey": false, 898 + "notNull": true, 899 + "autoincrement": false 900 + }, 901 + "data": { 902 + "name": "data", 903 + "type": "text", 904 + "primaryKey": false, 905 + "notNull": false, 906 + "autoincrement": false, 907 + "default": "'{}'" 908 + }, 909 + "workspace_id": { 910 + "name": "workspace_id", 911 + "type": "integer", 912 + "primaryKey": false, 913 + "notNull": false, 914 + "autoincrement": false 915 + }, 916 + "created_at": { 917 + "name": "created_at", 918 + "type": "integer", 919 + "primaryKey": false, 920 + "notNull": false, 921 + "autoincrement": false, 922 + "default": "(strftime('%s', 'now'))" 923 + }, 924 + "updated_at": { 925 + "name": "updated_at", 926 + "type": "integer", 927 + "primaryKey": false, 928 + "notNull": false, 929 + "autoincrement": false, 930 + "default": "(strftime('%s', 'now'))" 931 + } 932 + }, 933 + "indexes": {}, 934 + "foreignKeys": { 935 + "notification_workspace_id_workspace_id_fk": { 936 + "name": "notification_workspace_id_workspace_id_fk", 937 + "tableFrom": "notification", 938 + "tableTo": "workspace", 939 + "columnsFrom": [ 940 + "workspace_id" 941 + ], 942 + "columnsTo": [ 943 + "id" 944 + ], 945 + "onDelete": "no action", 946 + "onUpdate": "no action" 947 + } 948 + }, 949 + "compositePrimaryKeys": {}, 950 + "uniqueConstraints": {} 951 + }, 952 + "notifications_to_monitors": { 953 + "name": "notifications_to_monitors", 954 + "columns": { 955 + "monitor_id": { 956 + "name": "monitor_id", 957 + "type": "integer", 958 + "primaryKey": false, 959 + "notNull": true, 960 + "autoincrement": false 961 + }, 962 + "notification_id": { 963 + "name": "notification_id", 964 + "type": "integer", 965 + "primaryKey": false, 966 + "notNull": true, 967 + "autoincrement": false 968 + } 969 + }, 970 + "indexes": {}, 971 + "foreignKeys": { 972 + "notifications_to_monitors_monitor_id_monitor_id_fk": { 973 + "name": "notifications_to_monitors_monitor_id_monitor_id_fk", 974 + "tableFrom": "notifications_to_monitors", 975 + "tableTo": "monitor", 976 + "columnsFrom": [ 977 + "monitor_id" 978 + ], 979 + "columnsTo": [ 980 + "id" 981 + ], 982 + "onDelete": "cascade", 983 + "onUpdate": "no action" 984 + }, 985 + "notifications_to_monitors_notification_id_notification_id_fk": { 986 + "name": "notifications_to_monitors_notification_id_notification_id_fk", 987 + "tableFrom": "notifications_to_monitors", 988 + "tableTo": "notification", 989 + "columnsFrom": [ 990 + "notification_id" 991 + ], 992 + "columnsTo": [ 993 + "id" 994 + ], 995 + "onDelete": "cascade", 996 + "onUpdate": "no action" 997 + } 998 + }, 999 + "compositePrimaryKeys": { 1000 + "notifications_to_monitors_monitor_id_notification_id_pk": { 1001 + "columns": [ 1002 + "monitor_id", 1003 + "notification_id" 1004 + ] 1005 + } 1006 + }, 1007 + "uniqueConstraints": {} 1008 + }, 1009 + "monitor_status": { 1010 + "name": "monitor_status", 1011 + "columns": { 1012 + "monitor_id": { 1013 + "name": "monitor_id", 1014 + "type": "integer", 1015 + "primaryKey": false, 1016 + "notNull": true, 1017 + "autoincrement": false 1018 + }, 1019 + "region": { 1020 + "name": "region", 1021 + "type": "text", 1022 + "primaryKey": false, 1023 + "notNull": true, 1024 + "autoincrement": false, 1025 + "default": "''" 1026 + }, 1027 + "status": { 1028 + "name": "status", 1029 + "type": "text", 1030 + "primaryKey": false, 1031 + "notNull": true, 1032 + "autoincrement": false, 1033 + "default": "'active'" 1034 + }, 1035 + "created_at": { 1036 + "name": "created_at", 1037 + "type": "integer", 1038 + "primaryKey": false, 1039 + "notNull": false, 1040 + "autoincrement": false, 1041 + "default": "(strftime('%s', 'now'))" 1042 + }, 1043 + "updated_at": { 1044 + "name": "updated_at", 1045 + "type": "integer", 1046 + "primaryKey": false, 1047 + "notNull": false, 1048 + "autoincrement": false, 1049 + "default": "(strftime('%s', 'now'))" 1050 + } 1051 + }, 1052 + "indexes": { 1053 + "monitor_status_idx": { 1054 + "name": "monitor_status_idx", 1055 + "columns": [ 1056 + "monitor_id", 1057 + "region" 1058 + ], 1059 + "isUnique": false 1060 + } 1061 + }, 1062 + "foreignKeys": { 1063 + "monitor_status_monitor_id_monitor_id_fk": { 1064 + "name": "monitor_status_monitor_id_monitor_id_fk", 1065 + "tableFrom": "monitor_status", 1066 + "tableTo": "monitor", 1067 + "columnsFrom": [ 1068 + "monitor_id" 1069 + ], 1070 + "columnsTo": [ 1071 + "id" 1072 + ], 1073 + "onDelete": "cascade", 1074 + "onUpdate": "no action" 1075 + } 1076 + }, 1077 + "compositePrimaryKeys": { 1078 + "monitor_status_monitor_id_region_pk": { 1079 + "columns": [ 1080 + "monitor_id", 1081 + "region" 1082 + ] 1083 + } 1084 + }, 1085 + "uniqueConstraints": {} 1086 + } 1087 + }, 1088 + "enums": {}, 1089 + "_meta": { 1090 + "schemas": {}, 1091 + "tables": {}, 1092 + "columns": {} 1093 + } 1094 + }
+7
packages/db/drizzle/meta/_journal.json
··· 71 71 "when": 1697285841283, 72 72 "tag": "0009_small_maximus", 73 73 "breakpoints": true 74 + }, 75 + { 76 + "idx": 10, 77 + "version": "5", 78 + "when": 1700586221141, 79 + "tag": "0010_lame_songbird", 80 + "breakpoints": true 74 81 } 75 82 ] 76 83 }
+1
packages/db/src/schema/index.ts
··· 6 6 export * from "./workspaces"; 7 7 export * from "./shared"; 8 8 export * from "./notifications"; 9 + export * from "./monitor_status";
+3
packages/db/src/schema/monitor_status/index.ts
··· 1 + export * from "./monitor_status"; 2 + export * from "./validation"; 3 + export type * from "./validation";
+49
packages/db/src/schema/monitor_status/monitor_status.ts
··· 1 + import { relations, sql } from "drizzle-orm"; 2 + import { 3 + index, 4 + integer, 5 + primaryKey, 6 + sqliteTable, 7 + text, 8 + } from "drizzle-orm/sqlite-core"; 9 + 10 + import { monitor, monitorStatus as monitorStatusEnum } from "../monitors"; 11 + 12 + export const monitorStatusTable = sqliteTable( 13 + "monitor_status", 14 + { 15 + monitorId: integer("monitor_id") 16 + .references(() => monitor.id, { onDelete: "cascade" }) 17 + .notNull(), 18 + region: text("region").default("").notNull(), 19 + status: text("status", { enum: monitorStatusEnum }) 20 + .default("active") 21 + .notNull(), 22 + 23 + createdAt: integer("created_at", { mode: "timestamp" }).default( 24 + sql`(strftime('%s', 'now'))`, 25 + ), 26 + updatedAt: integer("updated_at", { mode: "timestamp" }).default( 27 + sql`(strftime('%s', 'now'))`, 28 + ), 29 + }, 30 + (table) => { 31 + return { 32 + primaryKey: primaryKey(table.monitorId, table.region), 33 + monitorStatusIdx: index("monitor_status_idx").on( 34 + table.monitorId, 35 + table.region, 36 + ), 37 + }; 38 + }, 39 + ); 40 + 41 + export const monitorStatusRelations = relations( 42 + monitorStatusTable, 43 + ({ one }) => ({ 44 + monitor: one(monitor, { 45 + fields: [monitorStatusTable.monitorId], 46 + references: [monitor.id], 47 + }), 48 + }), 49 + );
+24
packages/db/src/schema/monitor_status/validation.ts
··· 1 + import { createInsertSchema, createSelectSchema } from "drizzle-zod"; 2 + import type { z } from "zod"; 3 + 4 + import { monitorRegionSchema, monitorStatusSchema } from "../monitors"; 5 + import { monitorStatusTable } from "./monitor_status"; 6 + 7 + export const selectMonitorStatusSchema = createSelectSchema( 8 + monitorStatusTable, 9 + { 10 + status: monitorStatusSchema.default("active"), 11 + region: monitorRegionSchema.default("auto"), 12 + }, 13 + ); 14 + 15 + export const insertMonitorStatusSchema = createInsertSchema( 16 + monitorStatusTable, 17 + { 18 + status: monitorStatusSchema.default("active"), 19 + region: monitorRegionSchema.default("auto"), 20 + }, 21 + ); 22 + 23 + // export type InsertMonitorStatus = z.infer<typeof insertMonitorStatusSchema>; 24 + // export type MonitorStatus = z.infer<typeof selectMonitorStatusSchema>;