From 0e08a6f20843e3ac7369d94d5a3a09f5d71fe5c1 Mon Sep 17 00:00:00 2001 From: Maximilian Kaske <56969857+mxkaske@users.noreply.github.com> Date: Tue, 21 Nov 2023 18:24:52 +0100 Subject: [PATCH] feat: add monitor status table (#480) * feat: add monitor status table * fix: upsert missing index and update cron * chore: set updatedAt * fix: squash index --- apps/server/src/checker/alerting.ts | 19 +- apps/server/src/checker/checker.ts | 6 +- apps/web/.env.example | 4 +- apps/web/src/app/api/checker/cron/_cron.ts | 10 +- packages/api/src/router/monitor.ts | 28 + packages/db/drizzle/0010_lame_songbird.sql | 11 + packages/db/drizzle/meta/0010_snapshot.json | 1094 +++++++++++++++++ packages/db/drizzle/meta/_journal.json | 7 + packages/db/src/schema/index.ts | 1 + .../db/src/schema/monitor_status/index.ts | 3 + .../schema/monitor_status/monitor_status.ts | 49 + .../src/schema/monitor_status/validation.ts | 24 + 12 files changed, 1245 insertions(+), 11 deletions(-) create mode 100644 packages/db/drizzle/0010_lame_songbird.sql create mode 100644 packages/db/drizzle/meta/0010_snapshot.json create mode 100644 packages/db/src/schema/monitor_status/index.ts create mode 100644 packages/db/src/schema/monitor_status/monitor_status.ts create mode 100644 packages/db/src/schema/monitor_status/validation.ts diff --git a/apps/server/src/checker/alerting.ts b/apps/server/src/checker/alerting.ts index 92fc40db..e1657baf 100644 --- a/apps/server/src/checker/alerting.ts +++ b/apps/server/src/checker/alerting.ts @@ -1,11 +1,12 @@ import { db, eq, schema } from "@openstatus/db"; -import type { MonitorStatus } from "@openstatus/db/src/schema"; +import type { MonitorRegion, MonitorStatus } from "@openstatus/db/src/schema"; import { selectMonitorSchema, selectNotificationSchema, } from "@openstatus/db/src/schema"; import { flyRegionsDict } from "@openstatus/utils"; +import { env } from "../env"; import { providerToFunction } from "./utils"; export const triggerAlerting = async ({ @@ -45,16 +46,22 @@ export const triggerAlerting = async ({ } }; -export const updateMonitorStatus = async ({ +export const upsertMonitorStatus = async ({ monitorId, status, }: { monitorId: string; status: MonitorStatus; }) => { + const region = env.FLY_REGION as MonitorRegion; await db - .update(schema.monitor) - .set({ status }) - .where(eq(schema.monitor.id, Number(monitorId))) - .run(); + .insert(schema.monitorStatusTable) + .values({ status, region, monitorId: Number(monitorId) }) + .onConflictDoUpdate({ + target: [ + schema.monitorStatusTable.monitorId, + schema.monitorStatusTable.region, + ], + set: { status, updatedAt: new Date() }, + }); }; diff --git a/apps/server/src/checker/checker.ts b/apps/server/src/checker/checker.ts index 4889ac3c..37934188 100644 --- a/apps/server/src/checker/checker.ts +++ b/apps/server/src/checker/checker.ts @@ -1,5 +1,5 @@ import { env } from "../env"; -import { triggerAlerting, updateMonitorStatus } from "./alerting"; +import { triggerAlerting, upsertMonitorStatus } from "./alerting"; import type { PublishPingType } from "./ping"; import { pingEndpoint, publishPing } from "./ping"; import type { Payload } from "./schema"; @@ -76,7 +76,7 @@ const run = async (data: Payload, retry: number) => { message: undefined, }); if (data?.status === "error") { - await updateMonitorStatus({ + await upsertMonitorStatus({ monitorId: data.monitorId, status: "active", }); @@ -100,7 +100,7 @@ const run = async (data: Payload, retry: number) => { }); if (data?.status === "active") { - await updateMonitorStatus({ + await upsertMonitorStatus({ monitorId: data.monitorId, status: "error", }); diff --git a/apps/web/.env.example b/apps/web/.env.example index 5b9f12ea..22d0e78d 100644 --- a/apps/web/.env.example +++ b/apps/web/.env.example @@ -71,4 +71,6 @@ GCP_PROJECT_ID= GCP_LOCATION= GCP_CLIENT_EMAIL= GCP_PRIVATE_KEY= -CRON_SECRET= \ No newline at end of file +CRON_SECRET= + +EXTERNAL_API_URL= \ No newline at end of file diff --git a/apps/web/src/app/api/checker/cron/_cron.ts b/apps/web/src/app/api/checker/cron/_cron.ts index 7e330ee7..801b4e4f 100644 --- a/apps/web/src/app/api/checker/cron/_cron.ts +++ b/apps/web/src/app/api/checker/cron/_cron.ts @@ -55,10 +55,16 @@ export const cron = async ({ const allResult = []; for (const row of monitors) { + // TODO: remove - this is not used anymore | remember to update the `type Payload` const allPages = await caller.monitor.getAllPagesForMonitor({ monitorId: row.id, }); const selectedRegions = row.regions.length > 1 ? row.regions : ["auto"]; + + const monitorStatus = await caller.monitor.getMonitorStatusByMonitorId({ + monitorId: row.id, + }); + for (const region of selectedRegions) { const payload: z.infer = { workspaceId: String(row.workspaceId), @@ -69,7 +75,9 @@ export const cron = async ({ body: row.body, headers: row.headers, pageIds: allPages.map((p) => String(p.pageId)), - status: row.status, + status: + monitorStatus.find(({ region }) => region === region)?.status || + "active", }; const task: google.cloud.tasks.v2beta3.ITask = { diff --git a/packages/api/src/router/monitor.ts b/packages/api/src/router/monitor.ts index b5559e2a..43860070 100644 --- a/packages/api/src/router/monitor.ts +++ b/packages/api/src/router/monitor.ts @@ -4,12 +4,15 @@ import { z } from "zod"; import { and, eq, inArray, sql } from "@openstatus/db"; import { insertMonitorSchema, + insertMonitorStatusSchema, monitor, monitorPeriodicitySchema, + monitorStatusTable, monitorsToPages, notification, notificationsToMonitors, selectMonitorSchema, + selectMonitorStatusSchema, selectNotificationSchema, } from "@openstatus/db/src/schema"; import { allPlans } from "@openstatus/plans"; @@ -240,6 +243,31 @@ export const monitorRouter = createTRPCRouter({ return z.array(selectMonitorSchema).parse(result); }), + getMonitorStatusByMonitorId: cronProcedure + .input(z.object({ monitorId: z.number() })) + .query(async (opts) => { + const result = await opts.ctx.db + .select() + .from(monitorStatusTable) + .where(eq(monitorStatusTable.monitorId, opts.input.monitorId)) + .all(); + return z.array(selectMonitorStatusSchema).parse(result); + }), + + // FOR TESTING + upsertMonitorStatus: cronProcedure + .input(insertMonitorStatusSchema) + .mutation(async (opts) => { + const { status, region, monitorId } = opts.input; + await opts.ctx.db + .insert(monitorStatusTable) + .values({ status, region, monitorId: Number(monitorId) }) + .onConflictDoUpdate({ + target: [monitorStatusTable.monitorId, monitorStatusTable.region], + set: { status, updatedAt: new Date() }, + }); + }), + getAllPagesForMonitor: cronProcedure .input(z.object({ monitorId: z.number() })) .query(async (opts) => { diff --git a/packages/db/drizzle/0010_lame_songbird.sql b/packages/db/drizzle/0010_lame_songbird.sql new file mode 100644 index 00000000..2520341f --- /dev/null +++ b/packages/db/drizzle/0010_lame_songbird.sql @@ -0,0 +1,11 @@ +CREATE TABLE `monitor_status` ( + `monitor_id` integer NOT NULL, + `region` text DEFAULT '' NOT NULL, + `status` text DEFAULT 'active' NOT NULL, + `created_at` integer DEFAULT (strftime('%s', 'now')), + `updated_at` integer DEFAULT (strftime('%s', 'now')), + PRIMARY KEY(`monitor_id`, `region`), + FOREIGN KEY (`monitor_id`) REFERENCES `monitor`(`id`) ON UPDATE no action ON DELETE cascade +); +--> statement-breakpoint +CREATE INDEX `monitor_status_idx` ON `monitor_status` (`monitor_id`,`region`); \ No newline at end of file diff --git a/packages/db/drizzle/meta/0010_snapshot.json b/packages/db/drizzle/meta/0010_snapshot.json new file mode 100644 index 00000000..55c4859f --- /dev/null +++ b/packages/db/drizzle/meta/0010_snapshot.json @@ -0,0 +1,1094 @@ +{ + "version": "5", + "dialect": "sqlite", + "id": "452e7037-85cf-4d9a-b4e2-375a695d5fa8", + "prevId": "0f3dddb6-09cf-46a4-b12c-c5bb82475b94", + "tables": { + "incident": { + "name": "incident", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text(256)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "(strftime('%s', 'now'))" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "(strftime('%s', 'now'))" + } + }, + "indexes": {}, + "foreignKeys": { + "incident_workspace_id_workspace_id_fk": { + "name": "incident_workspace_id_workspace_id_fk", + "tableFrom": "incident", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "incident_update": { + "name": "incident_update", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text(4)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "date": { + "name": "date", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "incident_id": { + "name": "incident_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "(strftime('%s', 'now'))" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "(strftime('%s', 'now'))" + } + }, + "indexes": {}, + "foreignKeys": { + "incident_update_incident_id_incident_id_fk": { + "name": "incident_update_incident_id_incident_id_fk", + "tableFrom": "incident_update", + "tableTo": "incident", + "columnsFrom": [ + "incident_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "incidents_to_monitors": { + "name": "incidents_to_monitors", + "columns": { + "monitor_id": { + "name": "monitor_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "incident_id": { + "name": "incident_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "incidents_to_monitors_monitor_id_monitor_id_fk": { + "name": "incidents_to_monitors_monitor_id_monitor_id_fk", + "tableFrom": "incidents_to_monitors", + "tableTo": "monitor", + "columnsFrom": [ + "monitor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "incidents_to_monitors_incident_id_incident_id_fk": { + "name": "incidents_to_monitors_incident_id_incident_id_fk", + "tableFrom": "incidents_to_monitors", + "tableTo": "incident", + "columnsFrom": [ + "incident_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "incidents_to_monitors_monitor_id_incident_id_pk": { + "columns": [ + "incident_id", + "monitor_id" + ] + } + }, + "uniqueConstraints": {} + }, + "incidents_to_pages": { + "name": "incidents_to_pages", + "columns": { + "page_id": { + "name": "page_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "incident_id": { + "name": "incident_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "incidents_to_pages_page_id_page_id_fk": { + "name": "incidents_to_pages_page_id_page_id_fk", + "tableFrom": "incidents_to_pages", + "tableTo": "page", + "columnsFrom": [ + "page_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "incidents_to_pages_incident_id_incident_id_fk": { + "name": "incidents_to_pages_incident_id_incident_id_fk", + "tableFrom": "incidents_to_pages", + "tableTo": "incident", + "columnsFrom": [ + "incident_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "incidents_to_pages_page_id_incident_id_pk": { + "columns": [ + "incident_id", + "page_id" + ] + } + }, + "uniqueConstraints": {} + }, + "integration": { + "name": "integration", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text(256)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "credential": { + "name": "credential", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "(strftime('%s', 'now'))" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "(strftime('%s', 'now'))" + }, + "data": { + "name": "data", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "integration_workspace_id_workspace_id_fk": { + "name": "integration_workspace_id_workspace_id_fk", + "tableFrom": "integration", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "page": { + "name": "page", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "icon": { + "name": "icon", + "type": "text(256)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "''" + }, + "slug": { + "name": "slug", + "type": "text(256)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "custom_domain": { + "name": "custom_domain", + "type": "text(256)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "published": { + "name": "published", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "(strftime('%s', 'now'))" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "(strftime('%s', 'now'))" + } + }, + "indexes": { + "page_slug_unique": { + "name": "page_slug_unique", + "columns": [ + "slug" + ], + "isUnique": true + } + }, + "foreignKeys": { + "page_workspace_id_workspace_id_fk": { + "name": "page_workspace_id_workspace_id_fk", + "tableFrom": "page", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "monitor": { + "name": "monitor", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "job_type": { + "name": "job_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'other'" + }, + "periodicity": { + "name": "periodicity", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'other'" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'active'" + }, + "active": { + "name": "active", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": false + }, + "regions": { + "name": "regions", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "url": { + "name": "url", + "type": "text(2048)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text(256)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "headers": { + "name": "headers", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "''" + }, + "body": { + "name": "body", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "''" + }, + "method": { + "name": "method", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'GET'" + }, + "workspace_id": { + "name": "workspace_id", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "(strftime('%s', 'now'))" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "(strftime('%s', 'now'))" + } + }, + "indexes": {}, + "foreignKeys": { + "monitor_workspace_id_workspace_id_fk": { + "name": "monitor_workspace_id_workspace_id_fk", + "tableFrom": "monitor", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "monitors_to_pages": { + "name": "monitors_to_pages", + "columns": { + "monitor_id": { + "name": "monitor_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "page_id": { + "name": "page_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "monitors_to_pages_monitor_id_monitor_id_fk": { + "name": "monitors_to_pages_monitor_id_monitor_id_fk", + "tableFrom": "monitors_to_pages", + "tableTo": "monitor", + "columnsFrom": [ + "monitor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "monitors_to_pages_page_id_page_id_fk": { + "name": "monitors_to_pages_page_id_page_id_fk", + "tableFrom": "monitors_to_pages", + "tableTo": "page", + "columnsFrom": [ + "page_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "monitors_to_pages_monitor_id_page_id_pk": { + "columns": [ + "monitor_id", + "page_id" + ] + } + }, + "uniqueConstraints": {} + }, + "user": { + "name": "user", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "tenant_id": { + "name": "tenant_id", + "type": "text(256)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "first_name": { + "name": "first_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "''" + }, + "last_name": { + "name": "last_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "''" + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "''" + }, + "photo_url": { + "name": "photo_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "''" + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "(strftime('%s', 'now'))" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "(strftime('%s', 'now'))" + } + }, + "indexes": { + "user_tenant_id_unique": { + "name": "user_tenant_id_unique", + "columns": [ + "tenant_id" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "users_to_workspaces": { + "name": "users_to_workspaces", + "columns": { + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "users_to_workspaces_user_id_user_id_fk": { + "name": "users_to_workspaces_user_id_user_id_fk", + "tableFrom": "users_to_workspaces", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "users_to_workspaces_workspace_id_workspace_id_fk": { + "name": "users_to_workspaces_workspace_id_workspace_id_fk", + "tableFrom": "users_to_workspaces", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "users_to_workspaces_user_id_workspace_id_pk": { + "columns": [ + "user_id", + "workspace_id" + ] + } + }, + "uniqueConstraints": {} + }, + "workspace": { + "name": "workspace", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "stripe_id": { + "name": "stripe_id", + "type": "text(256)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "subscription_id": { + "name": "subscription_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "plan": { + "name": "plan", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "ends_at": { + "name": "ends_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "paid_until": { + "name": "paid_until", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "(strftime('%s', 'now'))" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "(strftime('%s', 'now'))" + } + }, + "indexes": { + "workspace_slug_unique": { + "name": "workspace_slug_unique", + "columns": [ + "slug" + ], + "isUnique": true + }, + "workspace_stripe_id_unique": { + "name": "workspace_stripe_id_unique", + "columns": [ + "stripe_id" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "notification": { + "name": "notification", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "data": { + "name": "data", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'{}'" + }, + "workspace_id": { + "name": "workspace_id", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "(strftime('%s', 'now'))" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "(strftime('%s', 'now'))" + } + }, + "indexes": {}, + "foreignKeys": { + "notification_workspace_id_workspace_id_fk": { + "name": "notification_workspace_id_workspace_id_fk", + "tableFrom": "notification", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "notifications_to_monitors": { + "name": "notifications_to_monitors", + "columns": { + "monitor_id": { + "name": "monitor_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "notification_id": { + "name": "notification_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "notifications_to_monitors_monitor_id_monitor_id_fk": { + "name": "notifications_to_monitors_monitor_id_monitor_id_fk", + "tableFrom": "notifications_to_monitors", + "tableTo": "monitor", + "columnsFrom": [ + "monitor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notifications_to_monitors_notification_id_notification_id_fk": { + "name": "notifications_to_monitors_notification_id_notification_id_fk", + "tableFrom": "notifications_to_monitors", + "tableTo": "notification", + "columnsFrom": [ + "notification_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "notifications_to_monitors_monitor_id_notification_id_pk": { + "columns": [ + "monitor_id", + "notification_id" + ] + } + }, + "uniqueConstraints": {} + }, + "monitor_status": { + "name": "monitor_status", + "columns": { + "monitor_id": { + "name": "monitor_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "region": { + "name": "region", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'active'" + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "(strftime('%s', 'now'))" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "(strftime('%s', 'now'))" + } + }, + "indexes": { + "monitor_status_idx": { + "name": "monitor_status_idx", + "columns": [ + "monitor_id", + "region" + ], + "isUnique": false + } + }, + "foreignKeys": { + "monitor_status_monitor_id_monitor_id_fk": { + "name": "monitor_status_monitor_id_monitor_id_fk", + "tableFrom": "monitor_status", + "tableTo": "monitor", + "columnsFrom": [ + "monitor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "monitor_status_monitor_id_region_pk": { + "columns": [ + "monitor_id", + "region" + ] + } + }, + "uniqueConstraints": {} + } + }, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + } +} \ No newline at end of file diff --git a/packages/db/drizzle/meta/_journal.json b/packages/db/drizzle/meta/_journal.json index 771d2a72..5d4f5c78 100644 --- a/packages/db/drizzle/meta/_journal.json +++ b/packages/db/drizzle/meta/_journal.json @@ -71,6 +71,13 @@ "when": 1697285841283, "tag": "0009_small_maximus", "breakpoints": true + }, + { + "idx": 10, + "version": "5", + "when": 1700586221141, + "tag": "0010_lame_songbird", + "breakpoints": true } ] } \ No newline at end of file diff --git a/packages/db/src/schema/index.ts b/packages/db/src/schema/index.ts index f693f352..9cba6eee 100644 --- a/packages/db/src/schema/index.ts +++ b/packages/db/src/schema/index.ts @@ -6,3 +6,4 @@ export * from "./users"; export * from "./workspaces"; export * from "./shared"; export * from "./notifications"; +export * from "./monitor_status"; diff --git a/packages/db/src/schema/monitor_status/index.ts b/packages/db/src/schema/monitor_status/index.ts new file mode 100644 index 00000000..23785aa1 --- /dev/null +++ b/packages/db/src/schema/monitor_status/index.ts @@ -0,0 +1,3 @@ +export * from "./monitor_status"; +export * from "./validation"; +export type * from "./validation"; diff --git a/packages/db/src/schema/monitor_status/monitor_status.ts b/packages/db/src/schema/monitor_status/monitor_status.ts new file mode 100644 index 00000000..2bb1f68b --- /dev/null +++ b/packages/db/src/schema/monitor_status/monitor_status.ts @@ -0,0 +1,49 @@ +import { relations, sql } from "drizzle-orm"; +import { + index, + integer, + primaryKey, + sqliteTable, + text, +} from "drizzle-orm/sqlite-core"; + +import { monitor, monitorStatus as monitorStatusEnum } from "../monitors"; + +export const monitorStatusTable = sqliteTable( + "monitor_status", + { + monitorId: integer("monitor_id") + .references(() => monitor.id, { onDelete: "cascade" }) + .notNull(), + region: text("region").default("").notNull(), + status: text("status", { enum: monitorStatusEnum }) + .default("active") + .notNull(), + + createdAt: integer("created_at", { mode: "timestamp" }).default( + sql`(strftime('%s', 'now'))`, + ), + updatedAt: integer("updated_at", { mode: "timestamp" }).default( + sql`(strftime('%s', 'now'))`, + ), + }, + (table) => { + return { + primaryKey: primaryKey(table.monitorId, table.region), + monitorStatusIdx: index("monitor_status_idx").on( + table.monitorId, + table.region, + ), + }; + }, +); + +export const monitorStatusRelations = relations( + monitorStatusTable, + ({ one }) => ({ + monitor: one(monitor, { + fields: [monitorStatusTable.monitorId], + references: [monitor.id], + }), + }), +); diff --git a/packages/db/src/schema/monitor_status/validation.ts b/packages/db/src/schema/monitor_status/validation.ts new file mode 100644 index 00000000..636b24a3 --- /dev/null +++ b/packages/db/src/schema/monitor_status/validation.ts @@ -0,0 +1,24 @@ +import { createInsertSchema, createSelectSchema } from "drizzle-zod"; +import type { z } from "zod"; + +import { monitorRegionSchema, monitorStatusSchema } from "../monitors"; +import { monitorStatusTable } from "./monitor_status"; + +export const selectMonitorStatusSchema = createSelectSchema( + monitorStatusTable, + { + status: monitorStatusSchema.default("active"), + region: monitorRegionSchema.default("auto"), + }, +); + +export const insertMonitorStatusSchema = createInsertSchema( + monitorStatusTable, + { + status: monitorStatusSchema.default("active"), + region: monitorRegionSchema.default("auto"), + }, +); + +// export type InsertMonitorStatus = z.infer; +// export type MonitorStatus = z.infer; -- 2.51.2