diff --git a/apps/dashboard/src/app/(dashboard)/monitors/[id]/incidents/layout.tsx b/apps/dashboard/src/app/(dashboard)/monitors/[id]/incidents/layout.tsx index f25ec0a8..d4425b07 100644 --- a/apps/dashboard/src/app/(dashboard)/monitors/[id]/incidents/layout.tsx +++ b/apps/dashboard/src/app/(dashboard)/monitors/[id]/incidents/layout.tsx @@ -21,7 +21,7 @@ export default async function Layout({ const monitorId = Number.parseInt(id); if (Number.isNaN(monitorId)) notFound(); await queryClient.prefetchQuery( - trpc.incident.list.queryOptions({ monitorId }), + trpc.monitorIncident.list.queryOptions({ monitorId }), ); const defaultOpen = await getSidebarDefaultOpen(RIGHT_SIDEBAR_COOKIE, false); diff --git a/apps/dashboard/src/app/(dashboard)/monitors/[id]/incidents/page.tsx b/apps/dashboard/src/app/(dashboard)/monitors/[id]/incidents/page.tsx index d5a4a3b9..d623f156 100644 --- a/apps/dashboard/src/app/(dashboard)/monitors/[id]/incidents/page.tsx +++ b/apps/dashboard/src/app/(dashboard)/monitors/[id]/incidents/page.tsx @@ -27,7 +27,7 @@ export default function Page() { const { id } = useParams<{ id: string }>(); const trpc = useTRPC(); const { data: incidents } = useQuery( - trpc.incident.list.queryOptions({ + trpc.monitorIncident.list.queryOptions({ monitorId: Number.parseInt(id), }), ); diff --git a/apps/dashboard/src/app/(dashboard)/overview/layout.tsx b/apps/dashboard/src/app/(dashboard)/overview/layout.tsx index b7c410f4..afea1916 100644 --- a/apps/dashboard/src/app/(dashboard)/overview/layout.tsx +++ b/apps/dashboard/src/app/(dashboard)/overview/layout.tsx @@ -17,7 +17,7 @@ export default async function Layout({ const queryClient = getQueryClient(); await Promise.all([ - queryClient.prefetchQuery(trpc.incident.list.queryOptions()), + queryClient.prefetchQuery(trpc.monitorIncident.list.queryOptions()), queryClient.prefetchQuery(trpc.statusReport.list.queryOptions({})), queryClient.prefetchQuery(trpc.maintenance.list.queryOptions()), ]); diff --git a/apps/dashboard/src/app/(dashboard)/overview/page.tsx b/apps/dashboard/src/app/(dashboard)/overview/page.tsx index 943a2e7e..af533dc1 100644 --- a/apps/dashboard/src/app/(dashboard)/overview/page.tsx +++ b/apps/dashboard/src/app/(dashboard)/overview/page.tsx @@ -39,7 +39,9 @@ export default function Page() { const { data: monitors } = useQuery(trpc.monitor.list.queryOptions()); const { data: pages } = useQuery(trpc.page.list.queryOptions()); // no period — an incident open for weeks must still surface here - const { data: incidents } = useQuery(trpc.incident.list.queryOptions()); + const { data: incidents } = useQuery( + trpc.monitorIncident.list.queryOptions(), + ); const { data: statusReports } = useQuery( trpc.statusReport.list.queryOptions({}), ); diff --git a/apps/dashboard/src/components/data-table/incidents/columns.tsx b/apps/dashboard/src/components/data-table/incidents/columns.tsx index c9795877..0edc7f04 100644 --- a/apps/dashboard/src/components/data-table/incidents/columns.tsx +++ b/apps/dashboard/src/components/data-table/incidents/columns.tsx @@ -11,7 +11,7 @@ import { DataTableColumnHeader } from "@/components/ui/data-table/data-table-col import { DataTableRowActions } from "./data-table-row-actions"; -type Incident = RouterOutputs["incident"]["list"][number]; +type Incident = RouterOutputs["monitorIncident"]["list"][number]; export const columns: ColumnDef[] = [ { diff --git a/apps/dashboard/src/components/data-table/incidents/data-table-row-actions.tsx b/apps/dashboard/src/components/data-table/incidents/data-table-row-actions.tsx index 65cbf6ae..026ca572 100644 --- a/apps/dashboard/src/components/data-table/incidents/data-table-row-actions.tsx +++ b/apps/dashboard/src/components/data-table/incidents/data-table-row-actions.tsx @@ -14,7 +14,7 @@ import { type IncidentConfirmType, } from "./dialog-confirm"; -type Incident = RouterOutputs["incident"]["list"][number]; +type Incident = RouterOutputs["monitorIncident"]["list"][number]; export function DataTableRowActions({ row }: { row: Row }) { return ; @@ -24,11 +24,11 @@ export function IncidentRowActions({ incident }: { incident: Incident }) { const trpc = useTRPC(); const queryClient = useQueryClient(); const deleteIncidentMutation = useMutation( - trpc.incident.delete.mutationOptions({ + trpc.monitorIncident.delete.mutationOptions({ onSuccess: () => // no-input prefix key — matches every incident.list query (overview, monitor detail) queryClient.invalidateQueries({ - queryKey: trpc.incident.list.queryKey(), + queryKey: trpc.monitorIncident.list.queryKey(), }), }), ); diff --git a/apps/dashboard/src/components/data-table/incidents/dialog-confirm.tsx b/apps/dashboard/src/components/data-table/incidents/dialog-confirm.tsx index 85b4cd9a..2abe6853 100644 --- a/apps/dashboard/src/components/data-table/incidents/dialog-confirm.tsx +++ b/apps/dashboard/src/components/data-table/incidents/dialog-confirm.tsx @@ -18,7 +18,7 @@ import { toast } from "sonner"; import { useTRPC } from "@/lib/trpc/client"; -type Incident = RouterOutputs["incident"]["list"][number]; +type Incident = RouterOutputs["monitorIncident"]["list"][number]; export type IncidentConfirmType = "acknowledge" | "resolve"; @@ -40,13 +40,15 @@ export function DialogConfirmIncident({ // no-input prefix key — matches every incident.list query (overview, monitor detail) const invalidateList = () => queryClient.invalidateQueries({ - queryKey: trpc.incident.list.queryKey(), + queryKey: trpc.monitorIncident.list.queryKey(), }); const acknowledgeIncidentMutation = useMutation( - trpc.incident.acknowledge.mutationOptions({ onSuccess: invalidateList }), + trpc.monitorIncident.acknowledge.mutationOptions({ + onSuccess: invalidateList, + }), ); const resolveIncidentMutation = useMutation( - trpc.incident.resolve.mutationOptions({ onSuccess: invalidateList }), + trpc.monitorIncident.resolve.mutationOptions({ onSuccess: invalidateList }), ); const handleConfirm = () => { diff --git a/apps/dashboard/src/components/data-table/overview-events/incident-action-cell.tsx b/apps/dashboard/src/components/data-table/overview-events/incident-action-cell.tsx index a6cdd87d..c9cec08c 100644 --- a/apps/dashboard/src/components/data-table/overview-events/incident-action-cell.tsx +++ b/apps/dashboard/src/components/data-table/overview-events/incident-action-cell.tsx @@ -9,7 +9,7 @@ import { type IncidentConfirmType, } from "@/components/data-table/incidents/dialog-confirm"; -type Incident = RouterOutputs["incident"]["list"][number]; +type Incident = RouterOutputs["monitorIncident"]["list"][number]; export function IncidentActionCell({ incident }: { incident: Incident }) { // capture the action at click time so a background refetch can't flip it mid-dialog diff --git a/apps/dashboard/src/data/overview-events.client.ts b/apps/dashboard/src/data/overview-events.client.ts index 1b5d213f..479b3028 100644 --- a/apps/dashboard/src/data/overview-events.client.ts +++ b/apps/dashboard/src/data/overview-events.client.ts @@ -8,7 +8,7 @@ import { Maintenance as MaintenanceIcon, } from "@openstatus/icons"; -type Incident = RouterOutputs["incident"]["list"][number]; +type Incident = RouterOutputs["monitorIncident"]["list"][number]; type StatusReport = RouterOutputs["statusReport"]["list"][number]; type Maintenance = RouterOutputs["maintenance"]["list"][number]; diff --git a/apps/private-location/internal/server/db_testdata b/apps/private-location/internal/server/db_testdata index b174cc57..e4951133 100644 --- a/apps/private-location/internal/server/db_testdata +++ b/apps/private-location/internal/server/db_testdata @@ -157,8 +157,8 @@ CREATE TABLE `invitation` ( `accepted_at` integer ); -DROP TABLE IF EXISTS "incident"; -CREATE TABLE "incident" ( +DROP TABLE IF EXISTS "monitor_incident"; +CREATE TABLE "monitor_incident" ( `id` integer PRIMARY KEY NOT NULL, `title` text DEFAULT '' NOT NULL, `summary` text DEFAULT '' NOT NULL, @@ -446,7 +446,7 @@ INSERT INTO "notification" ("id", "name", "provider", "data", "workspace_id", "c INSERT INTO "notifications_to_monitors" ("monitor_id", "notification_id", "created_at") VALUES ('1', '1', '1760358329'); -INSERT INTO "incident" ("id", "title", "summary", "status", "monitor_id", "workspace_id", "started_at", "acknowledged_at", "acknowledged_by", "resolved_at", "resolved_by", "created_at", "updated_at", "auto_resolved", "incident_screenshot_url", "recovery_screenshot_url") VALUES +INSERT INTO "monitor_incident" ("id", "title", "summary", "status", "monitor_id", "workspace_id", "started_at", "acknowledged_at", "acknowledged_by", "resolved_at", "resolved_by", "created_at", "updated_at", "auto_resolved", "incident_screenshot_url", "recovery_screenshot_url") VALUES ('1', '', '', 'triage', '1', '1', '1760358329', NULL, NULL, NULL, NULL, '1760358329', '1760358329', '0', NULL, NULL), ('2', '', '', 'triage', '1', '1', '1760358330', NULL, NULL, NULL, NULL, '1760358329', '1760358329', '0', NULL, NULL); diff --git a/apps/screenshot-service/src/index.ts b/apps/screenshot-service/src/index.ts index e0818636..389e9908 100644 --- a/apps/screenshot-service/src/index.ts +++ b/apps/screenshot-service/src/index.ts @@ -1,7 +1,7 @@ import { PutObjectCommand, S3Client } from "@aws-sdk/client-s3"; import { zValidator } from "@hono/zod-validator"; import { db, eq } from "@openstatus/db"; -import { incidentTable } from "@openstatus/db/src/schema/incidents/incident"; +import { monitorIncidentTable } from "@openstatus/db/src/schema/monitor_incidents/monitor_incident"; import { Receiver } from "@upstash/qstash"; import { Hono } from "hono"; import playwright from "playwright"; @@ -78,28 +78,28 @@ app.post( if (data.kind === "incident") { await db - .update(incidentTable) + .update(monitorIncidentTable) .set({ incidentScreenshotUrl: url }) - .where(eq(incidentTable.id, data.incidentId)) + .where(eq(monitorIncidentTable.id, data.incidentId)) .run(); } if (data.kind === "recovery") { await db - .update(incidentTable) + .update(monitorIncidentTable) .set({ recoveryScreenshotUrl: url }) - .where(eq(incidentTable.id, data.incidentId)) + .where(eq(monitorIncidentTable.id, data.incidentId)) .run(); } } catch (e) { console.log("could not take screenshot timeout"); if (data.kind === "incident") { await db - .update(incidentTable) + .update(monitorIncidentTable) .set({ incidentScreenshotUrl: "https://screenshot.openstat.us/err-connection-timed-out.jpg", }) - .where(eq(incidentTable.id, data.incidentId)) + .where(eq(monitorIncidentTable.id, data.incidentId)) .run(); } // diff --git a/apps/server/src/routes/public/status.test.ts b/apps/server/src/routes/public/status.test.ts index 87956190..0945504b 100644 --- a/apps/server/src/routes/public/status.test.ts +++ b/apps/server/src/routes/public/status.test.ts @@ -13,7 +13,7 @@ import { app } from "../../index"; const testRedisStore = (globalThis as Record) .__testRedisStore as Map | undefined; import { - incidentTable, + monitorIncidentTable, maintenance, monitor, page, @@ -125,8 +125,8 @@ afterAll(async () => { // Clean up test data if (testIncidentId) { await db - .delete(incidentTable) - .where(eq(incidentTable.id, testIncidentId)) + .delete(monitorIncidentTable) + .where(eq(monitorIncidentTable.id, testIncidentId)) .catch(() => {}); } if (testStatusReportId) { @@ -210,7 +210,7 @@ describe("Status Route: Active monitor filtering", () => { test("only considers active monitors for status calculation", async () => { // Create an incident for the inactive monitor const inactiveIncident = await db - .insert(incidentTable) + .insert(monitorIncidentTable) .values({ monitorId: testMonitor2Id, title: "Inactive Monitor Incident", @@ -229,8 +229,8 @@ describe("Status Route: Active monitor filtering", () => { // Clean up await db - .delete(incidentTable) - .where(eq(incidentTable.id, inactiveIncident.id)); + .delete(monitorIncidentTable) + .where(eq(monitorIncidentTable.id, inactiveIncident.id)); }); }); @@ -238,7 +238,7 @@ describe("Status Route: Incident detection", () => { test("returns incident status with ongoing incident", async () => { // Create an ongoing incident for the active monitor const incident = await db - .insert(incidentTable) + .insert(monitorIncidentTable) .values({ monitorId: testMonitorId, title: "Test Incident", @@ -257,7 +257,9 @@ describe("Status Route: Incident detection", () => { expect(data.status).toBe("incident"); // Clean up - await db.delete(incidentTable).where(eq(incidentTable.id, testIncidentId)); + await db + .delete(monitorIncidentTable) + .where(eq(monitorIncidentTable.id, testIncidentId)); testIncidentId = 0; }); @@ -265,15 +267,15 @@ describe("Status Route: Incident detection", () => { // First clean up the ongoing incident from previous test if it still exists if (testIncidentId) { await db - .delete(incidentTable) - .where(eq(incidentTable.id, testIncidentId)) + .delete(monitorIncidentTable) + .where(eq(monitorIncidentTable.id, testIncidentId)) .catch(() => {}); testIncidentId = 0; } // Create a resolved incident const resolvedIncident = await db - .insert(incidentTable) + .insert(monitorIncidentTable) .values({ monitorId: testMonitorId, title: "Resolved Incident", @@ -293,8 +295,8 @@ describe("Status Route: Incident detection", () => { // Clean up await db - .delete(incidentTable) - .where(eq(incidentTable.id, resolvedIncident.id)); + .delete(monitorIncidentTable) + .where(eq(monitorIncidentTable.id, resolvedIncident.id)); }); }); diff --git a/apps/server/src/routes/public/status.ts b/apps/server/src/routes/public/status.ts index 7822b246..566aca86 100644 --- a/apps/server/src/routes/public/status.ts +++ b/apps/server/src/routes/public/status.ts @@ -37,7 +37,7 @@ status.get("/:slug", async (c) => { with: { monitor: { with: { - incidents: true, + monitorIncidents: true, }, }, }, @@ -68,7 +68,8 @@ status.get("/:slug", async (c) => { // Extract all ongoing incidents from active monitors const ongoingIncidents = monitorComponents.flatMap( - (c) => c.monitor?.incidents?.filter((inc) => !inc.resolvedAt) ?? [], + (c) => + c.monitor?.monitorIncidents?.filter((inc) => !inc.resolvedAt) ?? [], ); // Filter for unresolved status reports diff --git a/apps/server/src/routes/v1/incidents/get.ts b/apps/server/src/routes/v1/incidents/get.ts index 7d34bf9d..7e8362fe 100644 --- a/apps/server/src/routes/v1/incidents/get.ts +++ b/apps/server/src/routes/v1/incidents/get.ts @@ -1,6 +1,6 @@ import { createRoute } from "@hono/zod-openapi"; import { and, db, eq } from "@openstatus/db"; -import { incidentTable } from "@openstatus/db/src/schema/incidents"; +import { monitorIncidentTable } from "@openstatus/db/src/schema/monitor_incidents"; import { OpenStatusApiError, openApiErrorResponses } from "@/libs/errors"; @@ -35,11 +35,11 @@ export function registerGetIncident(app: typeof incidentsApi) { const _incident = await db .select() - .from(incidentTable) + .from(monitorIncidentTable) .where( and( - eq(incidentTable.workspaceId, workspaceId), - eq(incidentTable.id, Number(id)), + eq(monitorIncidentTable.workspaceId, workspaceId), + eq(monitorIncidentTable.id, Number(id)), ), ) .get(); diff --git a/apps/server/src/routes/v1/incidents/get_all.test.ts b/apps/server/src/routes/v1/incidents/get_all.test.ts index 19cb2dc1..c0c5cf08 100644 --- a/apps/server/src/routes/v1/incidents/get_all.test.ts +++ b/apps/server/src/routes/v1/incidents/get_all.test.ts @@ -1,5 +1,5 @@ import { db, eq } from "@openstatus/db"; -import { incidentTable, monitor } from "@openstatus/db/src/schema"; +import { monitorIncidentTable, monitor } from "@openstatus/db/src/schema"; import { expect } from "@std/expect"; import { afterAll, beforeAll, test } from "@std/testing/bdd"; @@ -13,8 +13,8 @@ let testIncidentId: number; beforeAll(async () => { await db - .delete(incidentTable) - .where(eq(incidentTable.title, `${TEST_PREFIX}-incident`)); + .delete(monitorIncidentTable) + .where(eq(monitorIncidentTable.title, `${TEST_PREFIX}-incident`)); await db.delete(monitor).where(eq(monitor.name, `${TEST_PREFIX}-monitor`)); const mon = await db @@ -35,7 +35,7 @@ beforeAll(async () => { testMonitorId = mon.id; const incident = await db - .insert(incidentTable) + .insert(monitorIncidentTable) .values({ workspaceId: 1, monitorId: testMonitorId, @@ -50,8 +50,8 @@ beforeAll(async () => { afterAll(async () => { await db - .delete(incidentTable) - .where(eq(incidentTable.title, `${TEST_PREFIX}-incident`)); + .delete(monitorIncidentTable) + .where(eq(monitorIncidentTable.title, `${TEST_PREFIX}-incident`)); await db.delete(monitor).where(eq(monitor.name, `${TEST_PREFIX}-monitor`)); }); diff --git a/apps/server/src/routes/v1/incidents/get_all.ts b/apps/server/src/routes/v1/incidents/get_all.ts index 97c6fe4e..05f65090 100644 --- a/apps/server/src/routes/v1/incidents/get_all.ts +++ b/apps/server/src/routes/v1/incidents/get_all.ts @@ -1,6 +1,6 @@ import { createRoute } from "@hono/zod-openapi"; import { db, eq } from "@openstatus/db"; -import { incidentTable } from "@openstatus/db/src/schema/incidents"; +import { monitorIncidentTable } from "@openstatus/db/src/schema/monitor_incidents"; import { openApiErrorResponses } from "@/libs/errors"; @@ -32,8 +32,8 @@ export function registerGetAllIncidents(app: typeof incidentsApi) { const _incidents = await db .select() - .from(incidentTable) - .where(eq(incidentTable.workspaceId, workspaceId)) + .from(monitorIncidentTable) + .where(eq(monitorIncidentTable.workspaceId, workspaceId)) .all(); const data = IncidentSchema.array().parse(_incidents); diff --git a/apps/server/src/routes/v1/incidents/put.ts b/apps/server/src/routes/v1/incidents/put.ts index feda8de5..62252a20 100644 --- a/apps/server/src/routes/v1/incidents/put.ts +++ b/apps/server/src/routes/v1/incidents/put.ts @@ -1,7 +1,7 @@ import { createRoute } from "@hono/zod-openapi"; import { Events } from "@openstatus/analytics"; import { and, db, eq } from "@openstatus/db"; -import { incidentTable } from "@openstatus/db/src/schema/incidents"; +import { monitorIncidentTable } from "@openstatus/db/src/schema/monitor_incidents"; import { OpenStatusApiError, openApiErrorResponses } from "@/libs/errors"; import { trackMiddleware } from "@/libs/middlewares"; @@ -58,11 +58,11 @@ export function registerPutIncident(app: typeof incidentsApi) { const _incident = await db .select() - .from(incidentTable) + .from(monitorIncidentTable) .where( and( - eq(incidentTable.id, Number(id)), - eq(incidentTable.workspaceId, workspaceId), + eq(monitorIncidentTable.id, Number(id)), + eq(monitorIncidentTable.workspaceId, workspaceId), ), ) .get(); @@ -75,10 +75,10 @@ export function registerPutIncident(app: typeof incidentsApi) { } const _newIncident = await db - .update(incidentTable) + .update(monitorIncidentTable) // TODO: we should set the acknowledgedBy and resolvedBy fields .set({ ...input, updatedAt: new Date() }) - .where(eq(incidentTable.id, Number(id))) + .where(eq(monitorIncidentTable.id, Number(id))) .returning() .get(); diff --git a/apps/status-page/src/components/status-page/status-calendar.tsx b/apps/status-page/src/components/status-page/status-calendar.tsx index 19992ddd..c2c76eae 100644 --- a/apps/status-page/src/components/status-page/status-calendar.tsx +++ b/apps/status-page/src/components/status-page/status-calendar.tsx @@ -108,7 +108,7 @@ export function StatusCalendar({ const seenIncidents = new Set(); for (const c of pageComponents) { - const incidents = c.monitor?.incidents ?? []; + const incidents = c.monitor?.monitorIncidents ?? []; for (const inc of incidents) { if (seenIncidents.has(inc.id)) continue; seenIncidents.add(inc.id); diff --git a/apps/workflows/src/checker/alerting.ts b/apps/workflows/src/checker/alerting.ts index 2db10c63..4204070e 100644 --- a/apps/workflows/src/checker/alerting.ts +++ b/apps/workflows/src/checker/alerting.ts @@ -1,6 +1,6 @@ import { getLogger } from "@logtape/logtape"; import { db, eq, schema } from "@openstatus/db"; -import type { Incident } from "@openstatus/db/src/schema"; +import type { MonitorIncident } from "@openstatus/db/src/schema"; import { selectMonitorSchema, selectNotificationSchema, @@ -40,11 +40,11 @@ export const triggerNotifications = async ({ const triggered: { notificationId: number; provider: string }[] = []; const smsQuota = new Map(); - let incident: Incident | undefined; + let incident: MonitorIncident | undefined; if (incidentId) { try { - incident = await db.query.incidentTable.findFirst({ - where: eq(schema.incidentTable.id, incidentId), + incident = await db.query.monitorIncidentTable.findFirst({ + where: eq(schema.monitorIncidentTable.id, incidentId), }); } catch (err) { logger.warn("Failed to fetch incident data", { diff --git a/apps/workflows/src/checker/incident-utils.ts b/apps/workflows/src/checker/incident-utils.ts index 17ec152e..e812009a 100644 --- a/apps/workflows/src/checker/incident-utils.ts +++ b/apps/workflows/src/checker/incident-utils.ts @@ -11,11 +11,11 @@ const logger = getLogger(["workflow"]); export async function findOpenIncident(monitorId: number) { return db .select() - .from(schema.incidentTable) + .from(schema.monitorIncidentTable) .where( and( - eq(schema.incidentTable.monitorId, monitorId), - isNull(schema.incidentTable.resolvedAt), + eq(schema.monitorIncidentTable.monitorId, monitorId), + isNull(schema.monitorIncidentTable.resolvedAt), ), ) .get(); @@ -27,11 +27,11 @@ export async function findOpenIncident(monitorId: number) { export async function findAllOpenIncidents(monitorId: number) { return db .select() - .from(schema.incidentTable) + .from(schema.monitorIncidentTable) .where( and( - eq(schema.incidentTable.monitorId, monitorId), - isNull(schema.incidentTable.resolvedAt), + eq(schema.monitorIncidentTable.monitorId, monitorId), + isNull(schema.monitorIncidentTable.resolvedAt), ), ) .all(); @@ -45,7 +45,7 @@ export async function findAllOpenIncidents(monitorId: number) { export async function resolveIncident(params: { monitorId: string; cronTimestamp: number; -}): Promise<(typeof schema.incidentTable.$inferSelect)[]> { +}): Promise<(typeof schema.monitorIncidentTable.$inferSelect)[]> { const { monitorId, cronTimestamp } = params; // Find ALL open incidents for this monitor @@ -61,15 +61,15 @@ export async function resolveIncident(params: { // ATOMIC BULK UPDATE: Resolve all incidents in a single query // This prevents partial state if operation fails midway const resolvedIncidents = await db - .update(schema.incidentTable) + .update(schema.monitorIncidentTable) .set({ resolvedAt: new Date(cronTimestamp), autoResolved: true, }) .where( and( - inArray(schema.incidentTable.id, incidentIds), - isNull(schema.incidentTable.resolvedAt), // Still prevents race conditions + inArray(schema.monitorIncidentTable.id, incidentIds), + isNull(schema.monitorIncidentTable.resolvedAt), // Still prevents race conditions ), ) .returning(); diff --git a/apps/workflows/src/checker/outbox.ts b/apps/workflows/src/checker/outbox.ts index 0cd0085c..d16f337a 100644 --- a/apps/workflows/src/checker/outbox.ts +++ b/apps/workflows/src/checker/outbox.ts @@ -1,13 +1,13 @@ import { getLogger } from "@logtape/logtape"; import { and, db, eq, inArray, lt, notInArray, sql } from "@openstatus/db"; import type { - Incident, + MonitorIncident, Monitor, Notification, } from "@openstatus/db/src/schema"; import { notificationOutbox, - incidentTable, + monitorIncidentTable, monitor, notification, notificationDeadLetter, @@ -145,7 +145,7 @@ async function claimRows( type DeliveryDeps = { monitors: Map; notifications: Map; - incidents: Map; + incidents: Map; smsBlocked: Map; }; @@ -170,11 +170,11 @@ async function loadDeps(rows: OutboxRow[]): Promise { .where(inArray(notification.id, notificationIds)), db .select() - .from(incidentTable) + .from(monitorIncidentTable) .where( incidentIds.length === 0 ? sql`1 = 0` - : inArray(incidentTable.id, incidentIds), + : inArray(monitorIncidentTable.id, incidentIds), ), ]), ); @@ -191,7 +191,7 @@ async function loadDeps(rows: OutboxRow[]): Promise { if (parsed.success) notifications.set(row.id, parsed.data); } - const incidents = new Map(); + const incidents = new Map(); for (const row of incidentRows) { incidents.set(row.id, row); } diff --git a/apps/workflows/src/checker/private-location.ts b/apps/workflows/src/checker/private-location.ts index b72a4cc2..8bad40b3 100644 --- a/apps/workflows/src/checker/private-location.ts +++ b/apps/workflows/src/checker/private-location.ts @@ -257,7 +257,7 @@ export async function updateStatusPrivate(c: Context) { const existingIncident = await findOpenIncident(monitorIdNumber); if (!existingIncident) { const [newIncident] = await db - .insert(schema.incidentTable) + .insert(schema.monitorIncidentTable) .values({ monitorId: monitorIdNumber, workspaceId: monitor.workspaceId, diff --git a/apps/workflows/src/checker/transition.test.ts b/apps/workflows/src/checker/transition.test.ts index fe67e82c..ec616a4f 100644 --- a/apps/workflows/src/checker/transition.test.ts +++ b/apps/workflows/src/checker/transition.test.ts @@ -2,7 +2,7 @@ import { and, count, db, eq, isNull } from "@openstatus/db"; import { monitorTransition, notificationOutbox, - incidentTable, + monitorIncidentTable, monitor, monitorStatusTable, } from "@openstatus/db/src/schema"; @@ -169,11 +169,11 @@ describe("replay", () => { const incidents = await db .select({ total: count() }) - .from(incidentTable) + .from(monitorIncidentTable) .where( and( - eq(incidentTable.monitorId, monitorId), - isNull(incidentTable.resolvedAt), + eq(monitorIncidentTable.monitorId, monitorId), + isNull(monitorIncidentTable.resolvedAt), ), ) .all(); @@ -234,11 +234,11 @@ describe("recovery", () => { const open = await db .select({ total: count() }) - .from(incidentTable) + .from(monitorIncidentTable) .where( and( - eq(incidentTable.monitorId, monitorId), - isNull(incidentTable.resolvedAt), + eq(monitorIncidentTable.monitorId, monitorId), + isNull(monitorIncidentTable.resolvedAt), ), ) .all(); @@ -271,8 +271,8 @@ describe("concurrency", () => { const incidents = await db .select({ total: count() }) - .from(incidentTable) - .where(eq(incidentTable.monitorId, monitorId)) + .from(monitorIncidentTable) + .where(eq(monitorIncidentTable.monitorId, monitorId)) .all(); expect(incidents[0]?.total).toBe(1); @@ -338,11 +338,11 @@ describe("degraded", () => { const open = await db .select({ total: count() }) - .from(incidentTable) + .from(monitorIncidentTable) .where( and( - eq(incidentTable.monitorId, monitorId), - isNull(incidentTable.resolvedAt), + eq(monitorIncidentTable.monitorId, monitorId), + isNull(monitorIncidentTable.resolvedAt), ), ) .all(); diff --git a/apps/workflows/src/checker/transition.ts b/apps/workflows/src/checker/transition.ts index 86ce13f0..c84296d8 100644 --- a/apps/workflows/src/checker/transition.ts +++ b/apps/workflows/src/checker/transition.ts @@ -9,7 +9,7 @@ import { monitorTransition, notificationOutbox, notificationOutboxEventType, - incidentTable, + monitorIncidentTable, monitor, monitorStatusTable, notification, @@ -123,16 +123,16 @@ function journalStatement( function createIncidentStatement(input: TransitionInput, guard: SQL) { return db.all<{ id: number }>(sql` - INSERT INTO ${incidentTable} (monitor_id, workspace_id, started_at) + INSERT INTO ${monitorIncidentTable} (monitor_id, workspace_id, started_at) SELECT ${monitor.id}, ${monitor.workspaceId}, ${Math.floor(input.cronTimestamp / 1000)} FROM ${monitor} WHERE ${monitor.id} = ${input.monitorId} AND ${monitor.status} <> ${input.status} AND ${guard} AND NOT EXISTS ( - SELECT 1 FROM ${incidentTable} - WHERE ${incidentTable.monitorId} = ${monitor.id} - AND ${incidentTable.resolvedAt} IS NULL) + SELECT 1 FROM ${monitorIncidentTable} + WHERE ${monitorIncidentTable.monitorId} = ${monitor.id} + AND ${monitorIncidentTable.resolvedAt} IS NULL) ON CONFLICT DO NOTHING RETURNING id `); @@ -140,10 +140,10 @@ function createIncidentStatement(input: TransitionInput, guard: SQL) { function resolveIncidentStatement(input: TransitionInput, guard: SQL) { return db.all<{ id: number }>(sql` - UPDATE ${incidentTable} + UPDATE ${monitorIncidentTable} SET resolved_at = ${Math.floor(input.cronTimestamp / 1000)}, auto_resolved = 1 - WHERE ${incidentTable.monitorId} = ${input.monitorId} - AND ${incidentTable.resolvedAt} IS NULL + WHERE ${monitorIncidentTable.monitorId} = ${input.monitorId} + AND ${monitorIncidentTable.resolvedAt} IS NULL AND EXISTS ( SELECT 1 FROM ${monitor} WHERE ${monitor.id} = ${input.monitorId} @@ -174,9 +174,9 @@ function outboxStatement( ${monitor.id}, ${monitor.workspaceId}, ${notification.id}, ${notification.provider}, ${EVENT_TYPE[input.status]}, ${monitor.status}, ${input.status}, ${input.cronTimestamp}, - (SELECT id FROM ${incidentTable} - WHERE ${incidentTable.monitorId} = ${monitor.id} - AND ${incidentTable.resolvedAt} IS NULL + (SELECT id FROM ${monitorIncidentTable} + WHERE ${monitorIncidentTable.monitorId} = ${monitor.id} + AND ${monitorIncidentTable.resolvedAt} IS NULL ORDER BY id DESC LIMIT 1), ${JSON.stringify(payload)}, CASE WHEN ${owned} THEN 'pending' ELSE 'settled' END, diff --git a/apps/workflows/src/checker/update-status.test.ts b/apps/workflows/src/checker/update-status.test.ts index b0a975b2..9ab676cc 100644 --- a/apps/workflows/src/checker/update-status.test.ts +++ b/apps/workflows/src/checker/update-status.test.ts @@ -1,6 +1,6 @@ import { db, eq } from "@openstatus/db"; import { - incidentTable, + monitorIncidentTable, monitor, notificationOutbox, notificationTrigger, @@ -72,8 +72,8 @@ afterEach(async () => { .where(eq(notificationOutbox.monitorId, monitorId)) .run(); await db - .delete(incidentTable) - .where(eq(incidentTable.monitorId, monitorId)) + .delete(monitorIncidentTable) + .where(eq(monitorIncidentTable.monitorId, monitorId)) .run(); await db .update(monitor) diff --git a/apps/workflows/src/cron/status-drift.test.ts b/apps/workflows/src/cron/status-drift.test.ts index 7a032fc4..4b72744c 100644 --- a/apps/workflows/src/cron/status-drift.test.ts +++ b/apps/workflows/src/cron/status-drift.test.ts @@ -1,7 +1,7 @@ import { and, count, db, eq, isNull } from "@openstatus/db"; import { notificationOutbox, - incidentTable, + monitorIncidentTable, monitor, monitorStatusTable, } from "@openstatus/db/src/schema"; @@ -109,11 +109,11 @@ describe("handleStatusDriftCron", () => { const incidents = await db .select({ total: count() }) - .from(incidentTable) + .from(monitorIncidentTable) .where( and( - eq(incidentTable.monitorId, monitorRow.id), - isNull(incidentTable.resolvedAt), + eq(monitorIncidentTable.monitorId, monitorRow.id), + isNull(monitorIncidentTable.resolvedAt), ), ) .all(); diff --git a/apps/workflows/src/incident/index.ts b/apps/workflows/src/incident/index.ts index 770d9fb0..6cda00c6 100644 --- a/apps/workflows/src/incident/index.ts +++ b/apps/workflows/src/incident/index.ts @@ -17,9 +17,9 @@ incidentRoute.use("*", async (c, next) => { incidentRoute.get("/cleanup", async (c) => { // Find monitors that have unresolved incidents but are active const unresolvedIncidentMonitorIds = db - .select({ monitorId: schema.incidentTable.monitorId }) - .from(schema.incidentTable) - .where(isNull(schema.incidentTable.resolvedAt)); + .select({ monitorId: schema.monitorIncidentTable.monitorId }) + .from(schema.monitorIncidentTable) + .where(isNull(schema.monitorIncidentTable.resolvedAt)); const activeMonitorsWithUnresolvedIncidents = await db .select({ id: schema.monitor.id }) @@ -41,18 +41,18 @@ incidentRoute.get("/cleanup", async (c) => { // Update incidents for these monitors: set resolvedAt to now and autoResolved to true const result = await db - .update(schema.incidentTable) + .update(schema.monitorIncidentTable) .set({ resolvedAt: new Date(), autoResolved: true, }) .where( and( - inArray(schema.incidentTable.monitorId, monitorIds), - isNull(schema.incidentTable.resolvedAt), + inArray(schema.monitorIncidentTable.monitorId, monitorIds), + isNull(schema.monitorIncidentTable.resolvedAt), ), ) - .returning({ id: schema.incidentTable.id }); + .returning({ id: schema.monitorIncidentTable.id }); return c.json({ status: "ok", updated: result.length }); }); diff --git a/apps/workflows/src/scripts/outbox-preflight.ts b/apps/workflows/src/scripts/outbox-preflight.ts index 8656537a..bcfea218 100644 --- a/apps/workflows/src/scripts/outbox-preflight.ts +++ b/apps/workflows/src/scripts/outbox-preflight.ts @@ -10,7 +10,7 @@ import { sql, } from "@openstatus/db"; import { - incidentTable, + monitorIncidentTable, monitor, notification, notificationTrigger, @@ -34,17 +34,20 @@ const THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1000; async function duplicateOpenIncidents() { const rows = await db .select({ - monitorId: incidentTable.monitorId, - openCount: count(incidentTable.id), + monitorId: monitorIncidentTable.monitorId, + openCount: count(monitorIncidentTable.id), }) - .from(incidentTable) + .from(monitorIncidentTable) // NULLs are distinct in a SQLite unique index, so incidents with no monitor - // can never collide with incident_open_idx however many are open. + // can never collide with monitor_incident_open_idx however many are open. .where( - and(isNull(incidentTable.resolvedAt), isNotNull(incidentTable.monitorId)), + and( + isNull(monitorIncidentTable.resolvedAt), + isNotNull(monitorIncidentTable.monitorId), + ), ) - .groupBy(incidentTable.monitorId) - .having(sql`count(${incidentTable.id}) > 1`) + .groupBy(monitorIncidentTable.monitorId) + .having(sql`count(${monitorIncidentTable.id}) > 1`) .all(); console.log( diff --git a/packages/api/src/edge.ts b/packages/api/src/edge.ts index 00ecd8a0..07ff1bca 100644 --- a/packages/api/src/edge.ts +++ b/packages/api/src/edge.ts @@ -5,11 +5,11 @@ import { domainRouter } from "./router/domain"; import { externalServiceRouter } from "./router/externalService"; import { feedbackRouter } from "./router/feedback"; import { importRouter } from "./router/import"; -import { incidentRouter } from "./router/incident"; import { invitationRouter } from "./router/invitation"; import { maintenanceRouter } from "./router/maintenance"; import { memberRouter } from "./router/member"; import { monitorRouter } from "./router/monitor"; +import { monitorIncidentRouter } from "./router/monitorIncident"; import { monitorTagRouter } from "./router/monitorTag"; import { notificationRouter } from "./router/notification"; import { oauthRouter } from "./router/oauth"; @@ -36,7 +36,7 @@ export const edgeRouter = createTRPCRouter({ user: userRouter, notification: notificationRouter, invitation: invitationRouter, - incident: incidentRouter, + monitorIncident: monitorIncidentRouter, pageSubscriber: pageSubscriberRouter, tinybird: tinybirdRouter, monitorTag: monitorTagRouter, diff --git a/packages/api/src/router/incident.ts b/packages/api/src/router/monitorIncident.ts similarity index 90% rename from packages/api/src/router/incident.ts rename to packages/api/src/router/monitorIncident.ts index 2566c03f..26cc23ff 100644 --- a/packages/api/src/router/incident.ts +++ b/packages/api/src/router/monitorIncident.ts @@ -1,24 +1,24 @@ import { Events } from "@openstatus/analytics"; import { NotFoundError } from "@openstatus/services"; import { - acknowledgeIncident, - deleteIncident, - listIncidents, - resolveIncident, -} from "@openstatus/services/incident"; + acknowledgeMonitorIncident, + deleteMonitorIncident, + listMonitorIncidents, + resolveMonitorIncident, +} from "@openstatus/services/monitor-incident"; import { z } from "zod"; import { toServiceCtx, toTRPCError } from "../service-adapter"; import { createTRPCRouter, protectedProcedure } from "../trpc"; import { periods } from "./utils"; -export const incidentRouter = createTRPCRouter({ +export const monitorIncidentRouter = createTRPCRouter({ delete: protectedProcedure .meta({ track: Events.DeleteIncident }) .input(z.object({ id: z.number() })) .mutation(async ({ ctx, input }) => { try { - await deleteIncident({ + await deleteMonitorIncident({ ctx: toServiceCtx(ctx), input: { id: input.id }, }); @@ -42,7 +42,7 @@ export const incidentRouter = createTRPCRouter({ ) .query(async ({ ctx, input }) => { try { - const { items } = await listIncidents({ + const { items } = await listMonitorIncidents({ ctx: toServiceCtx(ctx), input: { monitorId: input?.monitorId ?? undefined, @@ -70,7 +70,7 @@ export const incidentRouter = createTRPCRouter({ .input(z.object({ id: z.number() })) .mutation(async ({ ctx, input }) => { try { - await acknowledgeIncident({ + await acknowledgeMonitorIncident({ ctx: toServiceCtx(ctx), input: { id: input.id }, }); @@ -86,7 +86,7 @@ export const incidentRouter = createTRPCRouter({ .input(z.object({ id: z.number() })) .mutation(async ({ ctx, input }) => { try { - await resolveIncident({ + await resolveMonitorIncident({ ctx: toServiceCtx(ctx), input: { id: input.id }, }); diff --git a/packages/api/src/router/statusPage.e2e.test.ts b/packages/api/src/router/statusPage.e2e.test.ts index 1a632a19..dfbb340c 100644 --- a/packages/api/src/router/statusPage.e2e.test.ts +++ b/packages/api/src/router/statusPage.e2e.test.ts @@ -1,6 +1,6 @@ import { and, db, eq, isNotNull, isNull } from "@openstatus/db"; import { - incidentTable, + monitorIncidentTable, monitor, page, pageComponent, @@ -1263,7 +1263,7 @@ describe("statusPage.get gates incidents by barType (calendar manual mode)", () const component = result?.pageComponents.find( (c) => c.monitorId === barTypeMonitorId, ); - return component?.monitor?.incidents ?? []; + return component?.monitor?.monitorIncidents ?? []; } beforeAll(async () => { @@ -1304,7 +1304,7 @@ describe("statusPage.get gates incidents by barType (calendar manual mode)", () barTypeMonitorId = testMonitor.id; const testIncident = await db - .insert(incidentTable) + .insert(monitorIncidentTable) .values({ monitorId: barTypeMonitorId, workspaceId: 1, @@ -1335,8 +1335,8 @@ describe("statusPage.get gates incidents by barType (calendar manual mode)", () .delete(pageComponent) .where(eq(pageComponent.id, barTypeComponentId)); await db - .delete(incidentTable) - .where(eq(incidentTable.id, barTypeIncidentId)); + .delete(monitorIncidentTable) + .where(eq(monitorIncidentTable.id, barTypeIncidentId)); await db.delete(monitor).where(eq(monitor.id, barTypeMonitorId)); await db.delete(page).where(eq(page.id, barTypePageId)); }); diff --git a/packages/api/src/router/statusPage.ts b/packages/api/src/router/statusPage.ts index 1f3b3b61..e02809e8 100644 --- a/packages/api/src/router/statusPage.ts +++ b/packages/api/src/router/statusPage.ts @@ -120,7 +120,7 @@ export const statusPageRouter = createTRPCRouter({ with: { monitor: { with: { - incidents: true, + monitorIncidents: true, }, }, group: true, @@ -164,7 +164,7 @@ export const statusPageRouter = createTRPCRouter({ const components = pageComponents.map((c) => { const events = getEvents({ maintenances: _page.maintenances, - incidents: c.monitor?.incidents ?? [], + incidents: c.monitor?.monitorIncidents ?? [], reports: _page.statusReports, pageComponentId: c.id, monitorId: c.monitorId ?? undefined, @@ -220,7 +220,7 @@ export const statusPageRouter = createTRPCRouter({ const monitors = monitorComponents.map((c) => { const events = getEvents({ maintenances: _page.maintenances, - incidents: c.monitor.incidents ?? [], + incidents: c.monitor.monitorIncidents ?? [], reports: _page.statusReports, monitorId: c.monitor.id, }); @@ -337,7 +337,9 @@ export const statusPageRouter = createTRPCRouter({ // Get page-wide events (not tied to specific monitors) const pageEvents = getEvents({ maintenances: _page.maintenances, - incidents: monitorComponents.flatMap((c) => c.monitor.incidents ?? []), + incidents: monitorComponents.flatMap( + (c) => c.monitor.monitorIncidents ?? [], + ), reports: _page.statusReports, // No monitorId provided, so we get all events for the page }); @@ -497,7 +499,7 @@ export const statusPageRouter = createTRPCRouter({ barType === "manual" ? pageComponents.map((c) => c.monitor - ? { ...c, monitor: { ...c.monitor, incidents: [] } } + ? { ...c, monitor: { ...c.monitor, monitorIncidents: [] } } : c, ) : pageComponents; @@ -509,7 +511,8 @@ export const statusPageRouter = createTRPCRouter({ monitorGroups, trackers, incidents: - monitorsWithPrivateLocationCount.flatMap((m) => m.incidents) ?? [], + monitorsWithPrivateLocationCount.flatMap((m) => m.monitorIncidents) ?? + [], statusReports, maintenances, workspacePlan: _page.workspace.plan, @@ -554,7 +557,7 @@ export const statusPageRouter = createTRPCRouter({ }, pageComponents: { with: { - monitor: { with: { incidents: true } }, + monitor: { with: { monitorIncidents: true } }, group: true, }, orderBy: (pageComponents, { asc }) => asc(pageComponents.order), @@ -602,7 +605,7 @@ export const statusPageRouter = createTRPCRouter({ // Extract all incidents from monitor components const incidents = monitorComponents.flatMap( - (c) => c.monitor?.incidents ?? [], + (c) => c.monitor?.monitorIncidents ?? [], ); const ws = selectWorkspaceSchema.safeParse(_page.workspace); @@ -744,7 +747,7 @@ export const statusPageRouter = createTRPCRouter({ with: { monitor: { with: { - incidents: true, + monitorIncidents: true, }, }, }, @@ -840,7 +843,7 @@ export const statusPageRouter = createTRPCRouter({ return pageComponents.map((c) => { const events = getEvents({ maintenances: _page.maintenances, - incidents: c.monitor?.incidents ?? [], + incidents: c.monitor?.monitorIncidents ?? [], reports: _page.statusReports, pageComponentId: c.id, monitorId: c.monitorId ?? undefined, diff --git a/packages/api/src/router/statusPage.utils.test.ts b/packages/api/src/router/statusPage.utils.test.ts index 8b582653..f3cf8b33 100644 --- a/packages/api/src/router/statusPage.utils.test.ts +++ b/packages/api/src/router/statusPage.utils.test.ts @@ -1,5 +1,5 @@ import type { - Incident, + MonitorIncident, Maintenance, PageComponent, StatusReport, @@ -1129,7 +1129,7 @@ describe("getEvents - pageComponent filtering", () => { } // Helper to create a mock incident - function createMockIncident(id: number, monitorId: number): Incident { + function createMockIncident(id: number, monitorId: number): MonitorIncident { const now = new Date(); const startedAt = new Date(now.getTime() - 1000 * 60 * 60); // 1 hour ago diff --git a/packages/db/drizzle/0088_rename_incident_to_monitor_incident.sql b/packages/db/drizzle/0088_rename_incident_to_monitor_incident.sql new file mode 100644 index 00000000..89cb6146 --- /dev/null +++ b/packages/db/drizzle/0088_rename_incident_to_monitor_incident.sql @@ -0,0 +1,13 @@ +-- Handwritten: drizzle-kit would drop and recreate the table. SQLite rewrites the +-- notification_outbox FK and the index definitions on rename. +ALTER TABLE `incident` RENAME TO `monitor_incident`;--> statement-breakpoint +DROP INDEX `incident_monitor_id_started_at_unique`;--> statement-breakpoint +CREATE UNIQUE INDEX `monitor_incident_monitor_id_started_at_unique` ON `monitor_incident` (`monitor_id`,`started_at`);--> statement-breakpoint +DROP INDEX `incident_workspace_id_started_at_idx`;--> statement-breakpoint +CREATE INDEX `monitor_incident_workspace_id_started_at_idx` ON `monitor_incident` (`workspace_id`,`started_at`);--> statement-breakpoint +DROP INDEX `incident_open_idx`;--> statement-breakpoint +CREATE UNIQUE INDEX `monitor_incident_open_idx` ON `monitor_incident` (`monitor_id`) WHERE "monitor_incident"."resolved_at" IS NULL;--> statement-breakpoint +-- Frees entity_type "incident" for the managed-incident entity; old ids would collide with it. +UPDATE `audit_log` +SET `action` = 'monitor_' || `action`, `entity_type` = 'monitor_incident' +WHERE `entity_type` = 'incident' AND `action` IN ('incident.update', 'incident.delete'); diff --git a/packages/db/drizzle/meta/0088_snapshot.json b/packages/db/drizzle/meta/0088_snapshot.json new file mode 100644 index 00000000..661a6045 --- /dev/null +++ b/packages/db/drizzle/meta/0088_snapshot.json @@ -0,0 +1,6060 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "832ae388-bf78-46a8-8e51-a8fedd3e602e", + "prevId": "e1e8bcc5-f7cd-4f9f-90eb-00ace43c4365", + "tables": { + "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 + }, + "trial_ends_at": { + "name": "trial_ends_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "limits": { + "name": "limits", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'{}'" + }, + "workos_organization_id": { + "name": "workos_organization_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "sso_enabled": { + "name": "sso_enabled", + "type": "integer", + "primaryKey": false, + "notNull": true, + "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'))" + }, + "dsn": { + "name": "dsn", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "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 + }, + "workspace_workos_organization_id_unique": { + "name": "workspace_workos_organization_id_unique", + "columns": [ + "workos_organization_id" + ], + "isUnique": true + }, + "workspace_id_dsn_unique": { + "name": "workspace_id_dsn_unique", + "columns": [ + "id", + "dsn" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "workspace_sso_domain": { + "name": "workspace_sso_domain", + "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 + }, + "domain": { + "name": "domain", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "verified_at": { + "name": "verified_at", + "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'))" + } + }, + "indexes": { + "workspace_sso_domain_domain_unique": { + "name": "workspace_sso_domain_domain_unique", + "columns": [ + "domain" + ], + "isUnique": true + }, + "workspace_sso_domain_workspace_id_idx": { + "name": "workspace_sso_domain_workspace_id_idx", + "columns": [ + "workspace_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "workspace_sso_domain_workspace_id_workspace_id_fk": { + "name": "workspace_sso_domain_workspace_id_workspace_id_fk", + "tableFrom": "workspace_sso_domain", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "account": { + "name": "account", + "columns": { + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider_account_id": { + "name": "provider_account_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "token_type": { + "name": "token_type", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "session_state": { + "name": "session_state", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "account_user_id_user_id_fk": { + "name": "account_user_id_user_id_fk", + "tableFrom": "account", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "account_provider_provider_account_id_pk": { + "columns": [ + "provider", + "provider_account_id" + ], + "name": "account_provider_provider_account_id_pk" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "session": { + "name": "session", + "columns": { + "session_token": { + "name": "session_token", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires": { + "name": "expires", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "session_user_id_idx": { + "name": "session_user_id_idx", + "columns": [ + "user_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "session_user_id_user_id_fk": { + "name": "session_user_id_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "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": "''" + }, + "photo_url": { + "name": "photo_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "''" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "''" + }, + "emailVerified": { + "name": "emailVerified", + "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'))" + }, + "deleted_at": { + "name": "deleted_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "user_tenant_id_unique": { + "name": "user_tenant_id_unique", + "columns": [ + "tenant_id" + ], + "isUnique": true + }, + "user_email_idx": { + "name": "user_email_idx", + "columns": [ + "email" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "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 + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'member'" + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "(strftime('%s', 'now'))" + } + }, + "indexes": { + "users_to_workspaces_workspace_id_idx": { + "name": "users_to_workspaces_workspace_id_idx", + "columns": [ + "workspace_id" + ], + "isUnique": false + } + }, + "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" + ], + "name": "users_to_workspaces_user_id_workspace_id_pk" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "verification_token": { + "name": "verification_token", + "columns": { + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires": { + "name": "expires", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "verification_token_identifier_token_pk": { + "columns": [ + "identifier", + "token" + ], + "name": "verification_token_identifier_token_pk" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "status_report": { + "name": "status_report", + "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 + }, + "page_id": { + "name": "page_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": { + "status_report_workspace_created_idx": { + "name": "status_report_workspace_created_idx", + "columns": [ + "workspace_id", + "created_at" + ], + "isUnique": false + }, + "status_report_page_id_idx": { + "name": "status_report_page_id_idx", + "columns": [ + "page_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "status_report_workspace_id_workspace_id_fk": { + "name": "status_report_workspace_id_workspace_id_fk", + "tableFrom": "status_report", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "status_report_page_id_page_id_fk": { + "name": "status_report_page_id_page_id_fk", + "tableFrom": "status_report", + "tableTo": "page", + "columnsFrom": [ + "page_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "status_report_update": { + "name": "status_report_update", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "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 + }, + "status_report_id": { + "name": "status_report_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": { + "status_report_update_status_report_id_idx": { + "name": "status_report_update_status_report_id_idx", + "columns": [ + "status_report_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "status_report_update_status_report_id_status_report_id_fk": { + "name": "status_report_update_status_report_id_status_report_id_fk", + "tableFrom": "status_report_update", + "tableTo": "status_report", + "columnsFrom": [ + "status_report_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "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": { + "integration_workspace_id_idx": { + "name": "integration_workspace_id_idx", + "columns": [ + "workspace_id" + ], + "isUnique": false + } + }, + "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": {}, + "checkConstraints": {} + }, + "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 + }, + "force_theme": { + "name": "force_theme", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'system'" + }, + "custom_theme": { + "name": "custom_theme", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "password": { + "name": "password", + "type": "text(256)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "password_protected": { + "name": "password_protected", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": false + }, + "access_type": { + "name": "access_type", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'public'" + }, + "auth_email_domains": { + "name": "auth_email_domains", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "allowed_ip_ranges": { + "name": "allowed_ip_ranges", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "homepage_url": { + "name": "homepage_url", + "type": "text(256)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "contact_url": { + "name": "contact_url", + "type": "text(256)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "default_locale": { + "name": "default_locale", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'en'" + }, + "locales": { + "name": "locales", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "legacy_page": { + "name": "legacy_page", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "configuration": { + "name": "configuration", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "allow_index": { + "name": "allow_index", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "show_monitor_values": { + "name": "show_monitor_values", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": true + }, + "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 + }, + "page_lower_slug_idx": { + "name": "page_lower_slug_idx", + "columns": [ + "LOWER(\"slug\")" + ], + "isUnique": false + }, + "page_lower_custom_domain_idx": { + "name": "page_lower_custom_domain_idx", + "columns": [ + "LOWER(\"custom_domain\")" + ], + "isUnique": false + }, + "page_workspace_id_idx": { + "name": "page_workspace_id_idx", + "columns": [ + "workspace_id" + ], + "isUnique": false + } + }, + "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": {}, + "checkConstraints": {} + }, + "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": "'http'" + }, + "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": "''" + }, + "external_name": { + "name": "external_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "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 + }, + "timeout": { + "name": "timeout", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 45000 + }, + "degraded_after": { + "name": "degraded_after", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "assertions": { + "name": "assertions", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "otel_endpoint": { + "name": "otel_endpoint", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "otel_headers": { + "name": "otel_headers", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "public": { + "name": "public", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": false + }, + "retry": { + "name": "retry", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 3 + }, + "follow_redirects": { + "name": "follow_redirects", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": true + }, + "grpc_service": { + "name": "grpc_service", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "grpc_tls": { + "name": "grpc_tls", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'tls'" + }, + "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'))" + }, + "deleted_at": { + "name": "deleted_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "monitor_workspace_id_active_idx": { + "name": "monitor_workspace_id_active_idx", + "columns": [ + "workspace_id" + ], + "isUnique": false, + "where": "\"monitor\".\"deleted_at\" IS NULL" + } + }, + "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": {}, + "checkConstraints": {} + }, + "page_subscriber": { + "name": "page_subscriber", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "page_id": { + "name": "page_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "channel_type": { + "name": "channel_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'email'" + }, + "webhook_url": { + "name": "webhook_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "channel_config": { + "name": "channel_config", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "slack_channel_id": { + "name": "slack_channel_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'self_signup'" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "accepted_at": { + "name": "accepted_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "unsubscribed_at": { + "name": "unsubscribed_at", + "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": { + "page_subscriber_page_id_idx": { + "name": "page_subscriber_page_id_idx", + "columns": [ + "page_id" + ], + "isUnique": false + }, + "idx_page_subscriber_email_page_active": { + "name": "idx_page_subscriber_email_page_active", + "columns": [ + "LOWER(\"email\")", + "page_id" + ], + "isUnique": true, + "where": "\"page_subscriber\".\"unsubscribed_at\" IS NULL AND \"page_subscriber\".\"channel_type\" = 'email'" + }, + "idx_page_subscriber_webhook_page_active": { + "name": "idx_page_subscriber_webhook_page_active", + "columns": [ + "LOWER(\"webhook_url\")", + "page_id" + ], + "isUnique": true, + "where": "\"page_subscriber\".\"unsubscribed_at\" IS NULL AND \"page_subscriber\".\"channel_type\" = 'webhook'" + }, + "idx_page_subscriber_slack_channel_page_active": { + "name": "idx_page_subscriber_slack_channel_page_active", + "columns": [ + "slack_channel_id", + "page_id" + ], + "isUnique": true, + "where": "\"page_subscriber\".\"unsubscribed_at\" IS NULL AND \"page_subscriber\".\"channel_type\" = 'slack'" + } + }, + "foreignKeys": { + "page_subscriber_page_id_page_id_fk": { + "name": "page_subscriber_page_id_page_id_fk", + "tableFrom": "page_subscriber", + "tableTo": "page", + "columnsFrom": [ + "page_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": { + "page_subscriber_channel_check": { + "name": "page_subscriber_channel_check", + "value": "(\"page_subscriber\".\"channel_type\" = 'email' AND \"page_subscriber\".\"email\" IS NOT NULL AND \"page_subscriber\".\"webhook_url\" IS NULL) OR (\"page_subscriber\".\"channel_type\" = 'webhook' AND \"page_subscriber\".\"webhook_url\" IS NOT NULL AND \"page_subscriber\".\"email\" IS NULL) OR (\"page_subscriber\".\"channel_type\" = 'slack' AND \"page_subscriber\".\"slack_channel_id\" IS NOT NULL AND \"page_subscriber\".\"email\" IS NULL AND \"page_subscriber\".\"webhook_url\" IS NULL)" + } + } + }, + "page_subscriber_to_page_component": { + "name": "page_subscriber_to_page_component", + "columns": { + "page_subscriber_id": { + "name": "page_subscriber_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "page_component_id": { + "name": "page_component_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'))" + } + }, + "indexes": {}, + "foreignKeys": { + "page_subscriber_to_page_component_page_subscriber_id_page_subscriber_id_fk": { + "name": "page_subscriber_to_page_component_page_subscriber_id_page_subscriber_id_fk", + "tableFrom": "page_subscriber_to_page_component", + "tableTo": "page_subscriber", + "columnsFrom": [ + "page_subscriber_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "page_subscriber_to_page_component_page_component_id_page_component_id_fk": { + "name": "page_subscriber_to_page_component_page_component_id_page_component_id_fk", + "tableFrom": "page_subscriber_to_page_component", + "tableTo": "page_component", + "columnsFrom": [ + "page_component_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "page_subscriber_to_page_component_page_subscriber_id_page_component_id_pk": { + "columns": [ + "page_subscriber_id", + "page_component_id" + ], + "name": "page_subscriber_to_page_component_page_subscriber_id_page_component_id_pk" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "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": { + "notification_workspace_id_idx": { + "name": "notification_workspace_id_idx", + "columns": [ + "workspace_id" + ], + "isUnique": false + } + }, + "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": {}, + "checkConstraints": {} + }, + "notification_trigger": { + "name": "notification_trigger", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "monitor_id": { + "name": "monitor_id", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "notification_id": { + "name": "notification_id", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "cron_timestamp": { + "name": "cron_timestamp", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "notification_id_monitor_id_crontimestampe": { + "name": "notification_id_monitor_id_crontimestampe", + "columns": [ + "notification_id", + "monitor_id", + "cron_timestamp" + ], + "isUnique": true + } + }, + "foreignKeys": { + "notification_trigger_monitor_id_monitor_id_fk": { + "name": "notification_trigger_monitor_id_monitor_id_fk", + "tableFrom": "notification_trigger", + "tableTo": "monitor", + "columnsFrom": [ + "monitor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_trigger_notification_id_notification_id_fk": { + "name": "notification_trigger_notification_id_notification_id_fk", + "tableFrom": "notification_trigger", + "tableTo": "notification", + "columnsFrom": [ + "notification_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "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 + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "(strftime('%s', 'now'))" + } + }, + "indexes": { + "notifications_to_monitors_notification_id_idx": { + "name": "notifications_to_monitors_notification_id_idx", + "columns": [ + "notification_id" + ], + "isUnique": false + } + }, + "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" + ], + "name": "notifications_to_monitors_monitor_id_notification_id_pk" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "notification_dead_letter": { + "name": "notification_dead_letter", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "outbox_id": { + "name": "outbox_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "dedup_key": { + "name": "dedup_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "monitor_id": { + "name": "monitor_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "notification_id": { + "name": "notification_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "from_status": { + "name": "from_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "to_status": { + "name": "to_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "cron_timestamp": { + "name": "cron_timestamp", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "incident_id": { + "name": "incident_id", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "payload": { + "name": "payload", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "attempts": { + "name": "attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "final_error": { + "name": "final_error", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "died_at": { + "name": "died_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "notification_dead_letter_dedup_key_idx": { + "name": "notification_dead_letter_dedup_key_idx", + "columns": [ + "dedup_key" + ], + "isUnique": true + }, + "notification_dead_letter_workspace_id_died_at_idx": { + "name": "notification_dead_letter_workspace_id_died_at_idx", + "columns": [ + "workspace_id", + "died_at" + ], + "isUnique": false + } + }, + "foreignKeys": { + "notification_dead_letter_monitor_id_monitor_id_fk": { + "name": "notification_dead_letter_monitor_id_monitor_id_fk", + "tableFrom": "notification_dead_letter", + "tableTo": "monitor", + "columnsFrom": [ + "monitor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_dead_letter_workspace_id_workspace_id_fk": { + "name": "notification_dead_letter_workspace_id_workspace_id_fk", + "tableFrom": "notification_dead_letter", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "notification_outbox": { + "name": "notification_outbox", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "dedup_key": { + "name": "dedup_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "monitor_id": { + "name": "monitor_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "notification_id": { + "name": "notification_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "from_status": { + "name": "from_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "to_status": { + "name": "to_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "cron_timestamp": { + "name": "cron_timestamp", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "incident_id": { + "name": "incident_id", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "payload": { + "name": "payload", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "delivery_status": { + "name": "delivery_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'pending'" + }, + "outcome": { + "name": "outcome", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "attempts": { + "name": "attempts", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "next_attempt_at": { + "name": "next_attempt_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "deadline_at": { + "name": "deadline_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "locked_by": { + "name": "locked_by", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "locked_until": { + "name": "locked_until", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "delivered_at": { + "name": "delivered_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "notification_outbox_dedup_key_idx": { + "name": "notification_outbox_dedup_key_idx", + "columns": [ + "dedup_key" + ], + "isUnique": true + }, + "notification_outbox_claim_idx": { + "name": "notification_outbox_claim_idx", + "columns": [ + "next_attempt_at" + ], + "isUnique": false, + "where": "\"notification_outbox\".\"delivery_status\" = 'pending'" + }, + "notification_outbox_notification_id_cron_timestamp_idx": { + "name": "notification_outbox_notification_id_cron_timestamp_idx", + "columns": [ + "notification_id", + "cron_timestamp" + ], + "isUnique": false + }, + "notification_outbox_channel_idx": { + "name": "notification_outbox_channel_idx", + "columns": [ + "monitor_id", + "notification_id" + ], + "isUnique": false, + "where": "\"notification_outbox\".\"delivery_status\" = 'pending'" + } + }, + "foreignKeys": { + "notification_outbox_monitor_id_monitor_id_fk": { + "name": "notification_outbox_monitor_id_monitor_id_fk", + "tableFrom": "notification_outbox", + "tableTo": "monitor", + "columnsFrom": [ + "monitor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_outbox_workspace_id_workspace_id_fk": { + "name": "notification_outbox_workspace_id_workspace_id_fk", + "tableFrom": "notification_outbox", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "notification_outbox_notification_id_notification_id_fk": { + "name": "notification_outbox_notification_id_notification_id_fk", + "tableFrom": "notification_outbox", + "tableTo": "notification", + "columnsFrom": [ + "notification_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notification_outbox_incident_id_monitor_incident_id_fk": { + "name": "notification_outbox_incident_id_monitor_incident_id_fk", + "tableFrom": "notification_outbox", + "tableTo": "monitor_incident", + "columnsFrom": [ + "incident_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "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'" + }, + "cron_timestamp": { + "name": "cron_timestamp", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "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" + ], + "name": "monitor_status_monitor_id_region_pk" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "invitation": { + "name": "invitation", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'member'" + }, + "workspace_id": { + "name": "workspace_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "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'))" + }, + "accepted_at": { + "name": "accepted_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "invitation_workspace_id_idx": { + "name": "invitation_workspace_id_idx", + "columns": [ + "workspace_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "monitor_incident": { + "name": "monitor_incident", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "summary": { + "name": "summary", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'triage'" + }, + "monitor_id": { + "name": "monitor_id", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "started_at": { + "name": "started_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%s', 'now'))" + }, + "acknowledged_at": { + "name": "acknowledged_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "acknowledged_by": { + "name": "acknowledged_by", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "resolved_at": { + "name": "resolved_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "resolved_by": { + "name": "resolved_by", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "incident_screenshot_url": { + "name": "incident_screenshot_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "recovery_screenshot_url": { + "name": "recovery_screenshot_url", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "auto_resolved": { + "name": "auto_resolved", + "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": { + "monitor_incident_workspace_id_started_at_idx": { + "name": "monitor_incident_workspace_id_started_at_idx", + "columns": [ + "workspace_id", + "started_at" + ], + "isUnique": false + }, + "monitor_incident_open_idx": { + "name": "monitor_incident_open_idx", + "columns": [ + "monitor_id" + ], + "isUnique": true, + "where": "\"monitor_incident\".\"resolved_at\" IS NULL" + }, + "monitor_incident_monitor_id_started_at_unique": { + "name": "monitor_incident_monitor_id_started_at_unique", + "columns": [ + "monitor_id", + "started_at" + ], + "isUnique": true + } + }, + "foreignKeys": { + "monitor_incident_monitor_id_monitor_id_fk": { + "name": "monitor_incident_monitor_id_monitor_id_fk", + "tableFrom": "monitor_incident", + "tableTo": "monitor", + "columnsFrom": [ + "monitor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set default", + "onUpdate": "no action" + }, + "monitor_incident_workspace_id_workspace_id_fk": { + "name": "monitor_incident_workspace_id_workspace_id_fk", + "tableFrom": "monitor_incident", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "monitor_incident_acknowledged_by_user_id_fk": { + "name": "monitor_incident_acknowledged_by_user_id_fk", + "tableFrom": "monitor_incident", + "tableTo": "user", + "columnsFrom": [ + "acknowledged_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "monitor_incident_resolved_by_user_id_fk": { + "name": "monitor_incident_resolved_by_user_id_fk", + "tableFrom": "monitor_incident", + "tableTo": "user", + "columnsFrom": [ + "resolved_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "monitor_tag": { + "name": "monitor_tag", + "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 + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "color": { + "name": "color", + "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'))" + } + }, + "indexes": { + "monitor_tag_workspace_id_idx": { + "name": "monitor_tag_workspace_id_idx", + "columns": [ + "workspace_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "monitor_tag_workspace_id_workspace_id_fk": { + "name": "monitor_tag_workspace_id_workspace_id_fk", + "tableFrom": "monitor_tag", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "monitor_tag_to_monitor": { + "name": "monitor_tag_to_monitor", + "columns": { + "monitor_id": { + "name": "monitor_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "monitor_tag_id": { + "name": "monitor_tag_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'))" + } + }, + "indexes": { + "monitor_tag_to_monitor_monitor_tag_id_idx": { + "name": "monitor_tag_to_monitor_monitor_tag_id_idx", + "columns": [ + "monitor_tag_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "monitor_tag_to_monitor_monitor_id_monitor_id_fk": { + "name": "monitor_tag_to_monitor_monitor_id_monitor_id_fk", + "tableFrom": "monitor_tag_to_monitor", + "tableTo": "monitor", + "columnsFrom": [ + "monitor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "monitor_tag_to_monitor_monitor_tag_id_monitor_tag_id_fk": { + "name": "monitor_tag_to_monitor_monitor_tag_id_monitor_tag_id_fk", + "tableFrom": "monitor_tag_to_monitor", + "tableTo": "monitor_tag", + "columnsFrom": [ + "monitor_tag_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "monitor_tag_to_monitor_monitor_id_monitor_tag_id_pk": { + "columns": [ + "monitor_id", + "monitor_tag_id" + ], + "name": "monitor_tag_to_monitor_monitor_id_monitor_tag_id_pk" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "application": { + "name": "application", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "dsn": { + "name": "dsn", + "type": "text", + "primaryKey": false, + "notNull": false, + "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": { + "application_dsn_unique": { + "name": "application_dsn_unique", + "columns": [ + "dsn" + ], + "isUnique": true + }, + "application_workspace_id_idx": { + "name": "application_workspace_id_idx", + "columns": [ + "workspace_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "application_workspace_id_workspace_id_fk": { + "name": "application_workspace_id_workspace_id_fk", + "tableFrom": "application", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "maintenance": { + "name": "maintenance", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text(256)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "from": { + "name": "from", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "to": { + "name": "to", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "page_id": { + "name": "page_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": { + "maintenance_page_id_idx": { + "name": "maintenance_page_id_idx", + "columns": [ + "page_id" + ], + "isUnique": false + }, + "maintenance_workspace_id_idx": { + "name": "maintenance_workspace_id_idx", + "columns": [ + "workspace_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "maintenance_workspace_id_workspace_id_fk": { + "name": "maintenance_workspace_id_workspace_id_fk", + "tableFrom": "maintenance", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "maintenance_page_id_page_id_fk": { + "name": "maintenance_page_id_page_id_fk", + "tableFrom": "maintenance", + "tableTo": "page", + "columnsFrom": [ + "page_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "check": { + "name": "check", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "regions": { + "name": "regions", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "url": { + "name": "url", + "type": "text(4096)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "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'" + }, + "count_requests": { + "name": "count_requests", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 1 + }, + "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'))" + } + }, + "indexes": { + "check_workspace_id_idx": { + "name": "check_workspace_id_idx", + "columns": [ + "workspace_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "check_workspace_id_workspace_id_fk": { + "name": "check_workspace_id_workspace_id_fk", + "tableFrom": "check", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "monitor_run": { + "name": "monitor_run", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "monitor_id": { + "name": "monitor_id", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "runned_at": { + "name": "runned_at", + "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'))" + } + }, + "indexes": { + "monitor_run_workspace_id_created_at_idx": { + "name": "monitor_run_workspace_id_created_at_idx", + "columns": [ + "workspace_id", + "created_at" + ], + "isUnique": false + }, + "monitor_run_monitor_id_idx": { + "name": "monitor_run_monitor_id_idx", + "columns": [ + "monitor_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "monitor_run_workspace_id_workspace_id_fk": { + "name": "monitor_run_workspace_id_workspace_id_fk", + "tableFrom": "monitor_run", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "monitor_run_monitor_id_monitor_id_fk": { + "name": "monitor_run_monitor_id_monitor_id_fk", + "tableFrom": "monitor_run", + "tableTo": "monitor", + "columnsFrom": [ + "monitor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "private_location_monitor_status": { + "name": "private_location_monitor_status", + "columns": { + "monitor_id": { + "name": "monitor_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "private_location_id": { + "name": "private_location_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'active'" + }, + "cron_timestamp": { + "name": "cron_timestamp", + "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": { + "private_location_monitor_status_pl_id_idx": { + "name": "private_location_monitor_status_pl_id_idx", + "columns": [ + "private_location_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "private_location_monitor_status_monitor_id_monitor_id_fk": { + "name": "private_location_monitor_status_monitor_id_monitor_id_fk", + "tableFrom": "private_location_monitor_status", + "tableTo": "monitor", + "columnsFrom": [ + "monitor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "private_location_monitor_status_private_location_id_private_location_id_fk": { + "name": "private_location_monitor_status_private_location_id_private_location_id_fk", + "tableFrom": "private_location_monitor_status", + "tableTo": "private_location", + "columnsFrom": [ + "private_location_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "private_location_monitor_status_monitor_id_private_location_id_pk": { + "columns": [ + "monitor_id", + "private_location_id" + ], + "name": "private_location_monitor_status_monitor_id_private_location_id_pk" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "private_location": { + "name": "private_location", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'error'" + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_seen_at": { + "name": "last_seen_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "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": { + "private_location_workspace_id_idx": { + "name": "private_location_workspace_id_idx", + "columns": [ + "workspace_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "private_location_workspace_id_workspace_id_fk": { + "name": "private_location_workspace_id_workspace_id_fk", + "tableFrom": "private_location", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "private_location_to_monitor": { + "name": "private_location_to_monitor", + "columns": { + "private_location_id": { + "name": "private_location_id", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "monitor_id": { + "name": "monitor_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'))" + }, + "deleted_at": { + "name": "deleted_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "private_location_to_monitor_private_location_id_idx": { + "name": "private_location_to_monitor_private_location_id_idx", + "columns": [ + "private_location_id" + ], + "isUnique": false + }, + "private_location_to_monitor_monitor_id_idx": { + "name": "private_location_to_monitor_monitor_id_idx", + "columns": [ + "monitor_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "private_location_to_monitor_private_location_id_private_location_id_fk": { + "name": "private_location_to_monitor_private_location_id_private_location_id_fk", + "tableFrom": "private_location_to_monitor", + "tableTo": "private_location", + "columnsFrom": [ + "private_location_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "private_location_to_monitor_monitor_id_monitor_id_fk": { + "name": "private_location_to_monitor_monitor_id_monitor_id_fk", + "tableFrom": "private_location_to_monitor", + "tableTo": "monitor", + "columnsFrom": [ + "monitor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "monitor_group": { + "name": "monitor_group", + "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 + }, + "page_id": { + "name": "page_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "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'))" + } + }, + "indexes": { + "monitor_group_workspace_id_idx": { + "name": "monitor_group_workspace_id_idx", + "columns": [ + "workspace_id" + ], + "isUnique": false + }, + "monitor_group_page_id_idx": { + "name": "monitor_group_page_id_idx", + "columns": [ + "page_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "monitor_group_workspace_id_workspace_id_fk": { + "name": "monitor_group_workspace_id_workspace_id_fk", + "tableFrom": "monitor_group", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "monitor_group_page_id_page_id_fk": { + "name": "monitor_group_page_id_page_id_fk", + "tableFrom": "monitor_group", + "tableTo": "page", + "columnsFrom": [ + "page_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "viewer": { + "name": "viewer", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "emailVerified": { + "name": "emailVerified", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "image": { + "name": "image", + "type": "text", + "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": { + "viewer_email_unique": { + "name": "viewer_email_unique", + "columns": [ + "email" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "viewer_accounts": { + "name": "viewer_accounts", + "columns": { + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "providerAccountId": { + "name": "providerAccountId", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "token_type": { + "name": "token_type", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "session_state": { + "name": "session_state", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "viewer_accounts_user_id_viewer_id_fk": { + "name": "viewer_accounts_user_id_viewer_id_fk", + "tableFrom": "viewer_accounts", + "tableTo": "viewer", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "viewer_accounts_provider_providerAccountId_pk": { + "columns": [ + "provider", + "providerAccountId" + ], + "name": "viewer_accounts_provider_providerAccountId_pk" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "viewer_session": { + "name": "viewer_session", + "columns": { + "session_token": { + "name": "session_token", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires": { + "name": "expires", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "viewer_session_user_id_viewer_id_fk": { + "name": "viewer_session_user_id_viewer_id_fk", + "tableFrom": "viewer_session", + "tableTo": "viewer", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "api_key": { + "name": "api_key", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "prefix": { + "name": "prefix", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "hashed_token": { + "name": "hashed_token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_by_id": { + "name": "created_by_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "scopes": { + "name": "scopes", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[\"write\"]'" + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "(strftime('%s', 'now'))" + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_used_at": { + "name": "last_used_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "api_key_prefix_unique": { + "name": "api_key_prefix_unique", + "columns": [ + "prefix" + ], + "isUnique": true + }, + "api_key_hashed_token_unique": { + "name": "api_key_hashed_token_unique", + "columns": [ + "hashed_token" + ], + "isUnique": true + }, + "api_key_prefix_idx": { + "name": "api_key_prefix_idx", + "columns": [ + "prefix" + ], + "isUnique": false + }, + "api_key_workspace_id_idx": { + "name": "api_key_workspace_id_idx", + "columns": [ + "workspace_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "api_key_workspace_id_workspace_id_fk": { + "name": "api_key_workspace_id_workspace_id_fk", + "tableFrom": "api_key", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "api_key_created_by_id_user_id_fk": { + "name": "api_key_created_by_id_user_id_fk", + "tableFrom": "api_key", + "tableTo": "user", + "columnsFrom": [ + "created_by_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "maintenance_to_page_component": { + "name": "maintenance_to_page_component", + "columns": { + "maintenance_id": { + "name": "maintenance_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "page_component_id": { + "name": "page_component_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'))" + } + }, + "indexes": { + "maintenance_to_page_component_page_component_id_idx": { + "name": "maintenance_to_page_component_page_component_id_idx", + "columns": [ + "page_component_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "maintenance_to_page_component_maintenance_id_maintenance_id_fk": { + "name": "maintenance_to_page_component_maintenance_id_maintenance_id_fk", + "tableFrom": "maintenance_to_page_component", + "tableTo": "maintenance", + "columnsFrom": [ + "maintenance_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "maintenance_to_page_component_page_component_id_page_component_id_fk": { + "name": "maintenance_to_page_component_page_component_id_page_component_id_fk", + "tableFrom": "maintenance_to_page_component", + "tableTo": "page_component", + "columnsFrom": [ + "page_component_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "maintenance_to_page_component_maintenance_id_page_component_id_pk": { + "columns": [ + "maintenance_id", + "page_component_id" + ], + "name": "maintenance_to_page_component_maintenance_id_page_component_id_pk" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "page_component": { + "name": "page_component", + "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 + }, + "page_id": { + "name": "page_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'monitor'" + }, + "monitor_id": { + "name": "monitor_id", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "order": { + "name": "order", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "group_id": { + "name": "group_id", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "group_order": { + "name": "group_order", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "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_component_workspace_id_idx": { + "name": "page_component_workspace_id_idx", + "columns": [ + "workspace_id" + ], + "isUnique": false + }, + "page_component_page_id_monitor_id_unique": { + "name": "page_component_page_id_monitor_id_unique", + "columns": [ + "page_id", + "monitor_id" + ], + "isUnique": true + } + }, + "foreignKeys": { + "page_component_workspace_id_workspace_id_fk": { + "name": "page_component_workspace_id_workspace_id_fk", + "tableFrom": "page_component", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "page_component_page_id_page_id_fk": { + "name": "page_component_page_id_page_id_fk", + "tableFrom": "page_component", + "tableTo": "page", + "columnsFrom": [ + "page_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "page_component_monitor_id_monitor_id_fk": { + "name": "page_component_monitor_id_monitor_id_fk", + "tableFrom": "page_component", + "tableTo": "monitor", + "columnsFrom": [ + "monitor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "page_component_group_id_page_component_groups_id_fk": { + "name": "page_component_group_id_page_component_groups_id_fk", + "tableFrom": "page_component", + "tableTo": "page_component_groups", + "columnsFrom": [ + "group_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": { + "page_component_type_check": { + "name": "page_component_type_check", + "value": "\"page_component\".\"type\" = 'monitor' AND \"page_component\".\"monitor_id\" IS NOT NULL OR \"page_component\".\"type\" = 'static' AND \"page_component\".\"monitor_id\" IS NULL" + } + } + }, + "status_report_update_to_page_component": { + "name": "status_report_update_to_page_component", + "columns": { + "status_report_update_id": { + "name": "status_report_update_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "page_component_id": { + "name": "page_component_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "impact": { + "name": "impact", + "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'))" + } + }, + "indexes": { + "status_report_update_to_page_component_page_component_id_idx": { + "name": "status_report_update_to_page_component_page_component_id_idx", + "columns": [ + "page_component_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "status_report_update_to_page_component_status_report_update_id_status_report_update_id_fk": { + "name": "status_report_update_to_page_component_status_report_update_id_status_report_update_id_fk", + "tableFrom": "status_report_update_to_page_component", + "tableTo": "status_report_update", + "columnsFrom": [ + "status_report_update_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "status_report_update_to_page_component_page_component_id_page_component_id_fk": { + "name": "status_report_update_to_page_component_page_component_id_page_component_id_fk", + "tableFrom": "status_report_update_to_page_component", + "tableTo": "page_component", + "columnsFrom": [ + "page_component_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "status_report_update_to_page_component_status_report_update_id_page_component_id_pk": { + "columns": [ + "status_report_update_id", + "page_component_id" + ], + "name": "status_report_update_to_page_component_status_report_update_id_page_component_id_pk" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "status_report_to_page_component": { + "name": "status_report_to_page_component", + "columns": { + "status_report_id": { + "name": "status_report_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "page_component_id": { + "name": "page_component_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'))" + } + }, + "indexes": { + "status_report_to_page_component_page_component_id_idx": { + "name": "status_report_to_page_component_page_component_id_idx", + "columns": [ + "page_component_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "status_report_to_page_component_status_report_id_status_report_id_fk": { + "name": "status_report_to_page_component_status_report_id_status_report_id_fk", + "tableFrom": "status_report_to_page_component", + "tableTo": "status_report", + "columnsFrom": [ + "status_report_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "status_report_to_page_component_page_component_id_page_component_id_fk": { + "name": "status_report_to_page_component_page_component_id_page_component_id_fk", + "tableFrom": "status_report_to_page_component", + "tableTo": "page_component", + "columnsFrom": [ + "page_component_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "status_report_to_page_component_status_report_id_page_component_id_pk": { + "columns": [ + "status_report_id", + "page_component_id" + ], + "name": "status_report_to_page_component_status_report_id_page_component_id_pk" + } + }, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "page_component_groups": { + "name": "page_component_groups", + "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 + }, + "page_id": { + "name": "page_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "default_open": { + "name": "default_open", + "type": "integer", + "primaryKey": false, + "notNull": true, + "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_component_groups_page_id_idx": { + "name": "page_component_groups_page_id_idx", + "columns": [ + "page_id" + ], + "isUnique": false + }, + "page_component_groups_workspace_id_idx": { + "name": "page_component_groups_workspace_id_idx", + "columns": [ + "workspace_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "page_component_groups_workspace_id_workspace_id_fk": { + "name": "page_component_groups_workspace_id_workspace_id_fk", + "tableFrom": "page_component_groups", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "page_component_groups_page_id_page_id_fk": { + "name": "page_component_groups_page_id_page_id_fk", + "tableFrom": "page_component_groups", + "tableTo": "page", + "columnsFrom": [ + "page_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "feedback": { + "name": "feedback", + "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 + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "blocker": { + "name": "blocker", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%s', 'now'))" + } + }, + "indexes": { + "feedback_workspace_id_idx": { + "name": "feedback_workspace_id_idx", + "columns": [ + "workspace_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "feedback_workspace_id_workspace_id_fk": { + "name": "feedback_workspace_id_workspace_id_fk", + "tableFrom": "feedback", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "feedback_user_id_user_id_fk": { + "name": "feedback_user_id_user_id_fk", + "tableFrom": "feedback", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "audit_log": { + "name": "audit_log", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "actor_type": { + "name": "actor_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "actor_id": { + "name": "actor_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "actor_user_id": { + "name": "actor_user_id", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "entity_type": { + "name": "entity_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "before": { + "name": "before", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "after": { + "name": "after", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "changed_fields": { + "name": "changed_fields", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "audit_log_workspace_created_idx": { + "name": "audit_log_workspace_created_idx", + "columns": [ + "workspace_id", + "created_at" + ], + "isUnique": false + }, + "audit_log_entity_idx": { + "name": "audit_log_entity_idx", + "columns": [ + "workspace_id", + "entity_type", + "entity_id", + "created_at" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "external_service": { + "name": "external_service", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "slug": { + "name": "slug", + "type": "text(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "aliases": { + "name": "aliases", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(json_array())" + }, + "name": { + "name": "name", + "type": "text(256)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "url": { + "name": "url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status_page_url": { + "name": "status_page_url", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "industry": { + "name": "industry", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "api_config": { + "name": "api_config", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "deleted_at": { + "name": "deleted_at", + "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": { + "external_service_slug_unique": { + "name": "external_service_slug_unique", + "columns": [ + "slug" + ], + "isUnique": true + }, + "external_service_deleted_at_idx": { + "name": "external_service_deleted_at_idx", + "columns": [ + "deleted_at" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "external_service_component": { + "name": "external_service_component", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "external_service_id": { + "name": "external_service_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "upstream_component_id": { + "name": "upstream_component_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "aliases": { + "name": "aliases", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(json_array())" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "group_name": { + "name": "group_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "position": { + "name": "position", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "indicator": { + "name": "indicator", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "first_seen_at": { + "name": "first_seen_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%s', 'now'))" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%s', 'now'))" + } + }, + "indexes": { + "external_service_component_unique_idx": { + "name": "external_service_component_unique_idx", + "columns": [ + "external_service_id", + "upstream_component_id" + ], + "isUnique": true + }, + "external_service_component_slug_unique_idx": { + "name": "external_service_component_slug_unique_idx", + "columns": [ + "external_service_id", + "slug" + ], + "isUnique": true + } + }, + "foreignKeys": { + "external_service_component_external_service_id_external_service_id_fk": { + "name": "external_service_component_external_service_id_external_service_id_fk", + "tableFrom": "external_service_component", + "tableTo": "external_service", + "columnsFrom": [ + "external_service_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "external_service_incident": { + "name": "external_service_incident", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "external_service_id": { + "name": "external_service_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider_incident_id": { + "name": "provider_incident_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "impact": { + "name": "impact", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "shortlink": { + "name": "shortlink", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "started_at": { + "name": "started_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "resolved_at": { + "name": "resolved_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "affected_component_ids": { + "name": "affected_component_ids", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[]'" + }, + "raw_payload": { + "name": "raw_payload", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "raw_payload_purged_at": { + "name": "raw_payload_purged_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "first_seen_at": { + "name": "first_seen_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%s', 'now'))" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%s', 'now'))" + } + }, + "indexes": { + "external_service_incident_unique_idx": { + "name": "external_service_incident_unique_idx", + "columns": [ + "external_service_id", + "provider_incident_id" + ], + "isUnique": true + }, + "external_service_incident_started_at_idx": { + "name": "external_service_incident_started_at_idx", + "columns": [ + "external_service_id", + "started_at" + ], + "isUnique": false + }, + "external_service_incident_resolved_at_idx": { + "name": "external_service_incident_resolved_at_idx", + "columns": [ + "resolved_at" + ], + "isUnique": false + } + }, + "foreignKeys": { + "external_service_incident_external_service_id_external_service_id_fk": { + "name": "external_service_incident_external_service_id_external_service_id_fk", + "tableFrom": "external_service_incident", + "tableTo": "external_service", + "columnsFrom": [ + "external_service_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "external_service_report": { + "name": "external_service_report", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "external_service_id": { + "name": "external_service_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "external_service_component_id": { + "name": "external_service_component_id", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "reporter_hash": { + "name": "reporter_hash", + "type": "text(64)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "country": { + "name": "country", + "type": "text(2)", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(strftime('%s', 'now'))" + } + }, + "indexes": { + "external_service_report_service_idx": { + "name": "external_service_report_service_idx", + "columns": [ + "external_service_id", + "created_at" + ], + "isUnique": false + }, + "external_service_report_component_idx": { + "name": "external_service_report_component_idx", + "columns": [ + "external_service_component_id", + "created_at" + ], + "isUnique": false + } + }, + "foreignKeys": { + "external_service_report_external_service_id_external_service_id_fk": { + "name": "external_service_report_external_service_id_external_service_id_fk", + "tableFrom": "external_service_report", + "tableTo": "external_service", + "columnsFrom": [ + "external_service_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "external_service_report_external_service_component_id_external_service_component_id_fk": { + "name": "external_service_report_external_service_component_id_external_service_component_id_fk", + "tableFrom": "external_service_report", + "tableTo": "external_service_component", + "columnsFrom": [ + "external_service_component_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "chat_session": { + "name": "chat_session", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "messages": { + "name": "messages", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "chat_session_workspace_user_updated_idx": { + "name": "chat_session_workspace_user_updated_idx", + "columns": [ + "workspace_id", + "user_id", + "updated_at" + ], + "isUnique": false + } + }, + "foreignKeys": { + "chat_session_workspace_id_workspace_id_fk": { + "name": "chat_session_workspace_id_workspace_id_fk", + "tableFrom": "chat_session", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "chat_session_user_id_user_id_fk": { + "name": "chat_session_user_id_user_id_fk", + "tableFrom": "chat_session", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "frozen_monitor_uptime": { + "name": "frozen_monitor_uptime", + "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 + }, + "monitor_id": { + "name": "monitor_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "month": { + "name": "month", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "days": { + "name": "days", + "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'))" + } + }, + "indexes": { + "frozen_monitor_uptime_workspace_id_idx": { + "name": "frozen_monitor_uptime_workspace_id_idx", + "columns": [ + "workspace_id" + ], + "isUnique": false + }, + "frozen_monitor_uptime_monitor_id_month_unique": { + "name": "frozen_monitor_uptime_monitor_id_month_unique", + "columns": [ + "monitor_id", + "month" + ], + "isUnique": true + } + }, + "foreignKeys": { + "frozen_monitor_uptime_workspace_id_workspace_id_fk": { + "name": "frozen_monitor_uptime_workspace_id_workspace_id_fk", + "tableFrom": "frozen_monitor_uptime", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "frozen_monitor_uptime_monitor_id_monitor_id_fk": { + "name": "frozen_monitor_uptime_monitor_id_monitor_id_fk", + "tableFrom": "frozen_monitor_uptime", + "tableTo": "monitor", + "columnsFrom": [ + "monitor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "monitor_transition": { + "name": "monitor_transition", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "monitor_id": { + "name": "monitor_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "region": { + "name": "region", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "cron_timestamp": { + "name": "cron_timestamp", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "from_status": { + "name": "from_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "to_status": { + "name": "to_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "quorum_count": { + "name": "quorum_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "region_count": { + "name": "region_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "transitioned": { + "name": "transitioned", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "outbox_rows": { + "name": "outbox_rows", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "monitor_transition_monitor_id_cron_timestamp_idx": { + "name": "monitor_transition_monitor_id_cron_timestamp_idx", + "columns": [ + "monitor_id", + "cron_timestamp" + ], + "isUnique": false + }, + "monitor_transition_created_at_idx": { + "name": "monitor_transition_created_at_idx", + "columns": [ + "created_at" + ], + "isUnique": false + } + }, + "foreignKeys": { + "monitor_transition_monitor_id_monitor_id_fk": { + "name": "monitor_transition_monitor_id_monitor_id_fk", + "tableFrom": "monitor_transition", + "tableTo": "monitor", + "columnsFrom": [ + "monitor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "oauth_client": { + "name": "oauth_client", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'MCP Client'" + }, + "redirect_uris": { + "name": "redirect_uris", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "revoked_at": { + "name": "revoked_at", + "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'))" + } + }, + "indexes": { + "oauth_client_client_id_unique": { + "name": "oauth_client_client_id_unique", + "columns": [ + "client_id" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "oauth_session": { + "name": "oauth_session", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "redirect_uri": { + "name": "redirect_uri", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "state": { + "name": "state", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "resource": { + "name": "resource", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "code_challenge": { + "name": "code_challenge", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "code_challenge_method": { + "name": "code_challenge_method", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'S256'" + }, + "decided_at": { + "name": "decided_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "oauth_session_client_id_idx": { + "name": "oauth_session_client_id_idx", + "columns": [ + "client_id" + ], + "isUnique": false + }, + "oauth_session_expires_at_idx": { + "name": "oauth_session_expires_at_idx", + "columns": [ + "expires_at" + ], + "isUnique": false + } + }, + "foreignKeys": { + "oauth_session_client_id_oauth_client_client_id_fk": { + "name": "oauth_session_client_id_oauth_client_client_id_fk", + "tableFrom": "oauth_session", + "tableTo": "oauth_client", + "columnsFrom": [ + "client_id" + ], + "columnsTo": [ + "client_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "oauth_authorization_code": { + "name": "oauth_authorization_code", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "hash": { + "name": "hash", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "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 + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "redirect_uri": { + "name": "redirect_uri", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "code_challenge": { + "name": "code_challenge", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "consumed_at": { + "name": "consumed_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "grant_id": { + "name": "grant_id", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "oauth_authorization_code_hash_unique": { + "name": "oauth_authorization_code_hash_unique", + "columns": [ + "hash" + ], + "isUnique": true + }, + "oauth_authorization_code_expires_at_idx": { + "name": "oauth_authorization_code_expires_at_idx", + "columns": [ + "expires_at" + ], + "isUnique": false + }, + "oauth_authorization_code_client_id_idx": { + "name": "oauth_authorization_code_client_id_idx", + "columns": [ + "client_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "oauth_authorization_code_client_id_oauth_client_client_id_fk": { + "name": "oauth_authorization_code_client_id_oauth_client_client_id_fk", + "tableFrom": "oauth_authorization_code", + "tableTo": "oauth_client", + "columnsFrom": [ + "client_id" + ], + "columnsTo": [ + "client_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oauth_authorization_code_user_id_user_id_fk": { + "name": "oauth_authorization_code_user_id_user_id_fk", + "tableFrom": "oauth_authorization_code", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oauth_authorization_code_workspace_id_workspace_id_fk": { + "name": "oauth_authorization_code_workspace_id_workspace_id_fk", + "tableFrom": "oauth_authorization_code", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oauth_authorization_code_grant_id_oauth_grant_id_fk": { + "name": "oauth_authorization_code_grant_id_oauth_grant_id_fk", + "tableFrom": "oauth_authorization_code", + "tableTo": "oauth_grant", + "columnsFrom": [ + "grant_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "oauth_grant": { + "name": "oauth_grant", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "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 + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "access_token_hash": { + "name": "access_token_hash", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "refresh_token_hash": { + "name": "refresh_token_hash", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "previous_refresh_token_hash": { + "name": "previous_refresh_token_hash", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "rotated_at": { + "name": "rotated_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_used_at": { + "name": "last_used_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "revoked_at": { + "name": "revoked_at", + "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'))" + } + }, + "indexes": { + "oauth_grant_access_token_hash_unique": { + "name": "oauth_grant_access_token_hash_unique", + "columns": [ + "access_token_hash" + ], + "isUnique": true + }, + "oauth_grant_refresh_token_hash_unique": { + "name": "oauth_grant_refresh_token_hash_unique", + "columns": [ + "refresh_token_hash" + ], + "isUnique": true + }, + "oauth_grant_workspace_id_idx": { + "name": "oauth_grant_workspace_id_idx", + "columns": [ + "workspace_id" + ], + "isUnique": false + }, + "oauth_grant_client_id_user_id_idx": { + "name": "oauth_grant_client_id_user_id_idx", + "columns": [ + "client_id", + "user_id" + ], + "isUnique": false + }, + "oauth_grant_user_id_idx": { + "name": "oauth_grant_user_id_idx", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "oauth_grant_previous_refresh_token_hash_idx": { + "name": "oauth_grant_previous_refresh_token_hash_idx", + "columns": [ + "previous_refresh_token_hash" + ], + "isUnique": false + }, + "oauth_grant_refresh_token_expires_at_idx": { + "name": "oauth_grant_refresh_token_expires_at_idx", + "columns": [ + "refresh_token_expires_at" + ], + "isUnique": false + } + }, + "foreignKeys": { + "oauth_grant_client_id_oauth_client_client_id_fk": { + "name": "oauth_grant_client_id_oauth_client_client_id_fk", + "tableFrom": "oauth_grant", + "tableTo": "oauth_client", + "columnsFrom": [ + "client_id" + ], + "columnsTo": [ + "client_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oauth_grant_user_id_user_id_fk": { + "name": "oauth_grant_user_id_user_id_fk", + "tableFrom": "oauth_grant", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oauth_grant_workspace_id_workspace_id_fk": { + "name": "oauth_grant_workspace_id_workspace_id_fk", + "tableFrom": "oauth_grant", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + } + }, + "views": {}, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": { + "page_lower_slug_idx": { + "columns": { + "LOWER(\"slug\")": { + "isExpression": true + } + } + }, + "page_lower_custom_domain_idx": { + "columns": { + "LOWER(\"custom_domain\")": { + "isExpression": true + } + } + }, + "idx_page_subscriber_email_page_active": { + "columns": { + "LOWER(\"email\")": { + "isExpression": true + } + } + }, + "idx_page_subscriber_webhook_page_active": { + "columns": { + "LOWER(\"webhook_url\")": { + "isExpression": true + } + } + } + } + } +} \ No newline at end of file diff --git a/packages/db/drizzle/meta/_journal.json b/packages/db/drizzle/meta/_journal.json index 9d3fcf2c..888c4c64 100644 --- a/packages/db/drizzle/meta/_journal.json +++ b/packages/db/drizzle/meta/_journal.json @@ -617,6 +617,13 @@ "when": 1790175988767, "tag": "0087_narrow_black_tarantula", "breakpoints": true + }, + { + "idx": 88, + "version": "6", + "when": 1790589150938, + "tag": "0088_rename_incident_to_monitor_incident", + "breakpoints": true } ] } \ No newline at end of file diff --git a/packages/db/package.json b/packages/db/package.json index 8e7d6d38..964ad8ca 100644 --- a/packages/db/package.json +++ b/packages/db/package.json @@ -11,7 +11,7 @@ "./src/test/factories": "./src/test/factories.ts", "./src/schema/api-keys": "./src/schema/api-keys/index.ts", "./src/schema/check": "./src/schema/check/index.ts", - "./src/schema/incidents": "./src/schema/incidents/index.ts", + "./src/schema/monitor_incidents": "./src/schema/monitor_incidents/index.ts", "./src/schema/maintenances": "./src/schema/maintenances/index.ts", "./src/schema/page_components": "./src/schema/page_components/index.ts", "./src/schema/workspaces": "./src/schema/workspaces/index.ts", diff --git a/packages/db/src/schema/audit_logs/validation.ts b/packages/db/src/schema/audit_logs/validation.ts index 6349bb7c..cbdc29fc 100644 --- a/packages/db/src/schema/audit_logs/validation.ts +++ b/packages/db/src/schema/audit_logs/validation.ts @@ -121,9 +121,9 @@ const maintenanceActions = [ action("maintenance.delete", "maintenance", intId), ] as const; -const incidentActions = [ - action("incident.update", "incident", intId), - action("incident.delete", "incident", intId), +const monitorIncidentActions = [ + action("monitor_incident.update", "monitor_incident", intId), + action("monitor_incident.delete", "monitor_incident", intId), ] as const; const statusReportActions = [ @@ -158,7 +158,7 @@ const invitationActions = [ // (the acceptance stamp), so `member.create` covers only the paths that // mint a membership with no invitation behind it — today, SSO JIT // provisioning. -// - `incident`: rows originate from the checker pipeline, not user +// - `monitor_incident`: rows originate from the checker pipeline, not user // mutations. // When those write paths migrate to the service layer, add the missing // verbs alongside. @@ -222,7 +222,7 @@ export const auditActionSchema = z.discriminatedUnion("action", [ ...userActions, ...workspaceActions, ...maintenanceActions, - ...incidentActions, + ...monitorIncidentActions, ...statusReportActions, ...statusReportUpdateActions, ...invitationActions, diff --git a/packages/db/src/schema/incidents/index.ts b/packages/db/src/schema/incidents/index.ts deleted file mode 100644 index eff8f2af..00000000 --- a/packages/db/src/schema/incidents/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from "./incident"; -export * from "./validation"; diff --git a/packages/db/src/schema/incidents/validation.ts b/packages/db/src/schema/incidents/validation.ts deleted file mode 100644 index 72655c06..00000000 --- a/packages/db/src/schema/incidents/validation.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { createSelectSchema } from "drizzle-zod"; -import { z } from "zod"; - -import { incidentTable } from "./incident"; - -export const selectIncidentSchema = createSelectSchema(incidentTable).extend({ - monitorName: z.string().optional(), -}); - -export type Incident = z.infer; diff --git a/packages/db/src/schema/index.ts b/packages/db/src/schema/index.ts index d2760b21..2f996517 100644 --- a/packages/db/src/schema/index.ts +++ b/packages/db/src/schema/index.ts @@ -9,7 +9,7 @@ export * from "./shared"; export * from "./notifications"; export * from "./monitor_status"; export * from "./invitations"; -export * from "./incidents"; +export * from "./monitor_incidents"; export * from "./monitor_tags"; export * from "./applications"; export * from "./maintenances"; diff --git a/packages/db/src/schema/monitor_incidents/index.ts b/packages/db/src/schema/monitor_incidents/index.ts new file mode 100644 index 00000000..31cb5516 --- /dev/null +++ b/packages/db/src/schema/monitor_incidents/index.ts @@ -0,0 +1,2 @@ +export * from "./monitor_incident"; +export * from "./validation"; diff --git a/packages/db/src/schema/incidents/incident.ts b/packages/db/src/schema/monitor_incidents/monitor_incident.ts similarity index 71% rename from packages/db/src/schema/incidents/incident.ts rename to packages/db/src/schema/monitor_incidents/monitor_incident.ts index d1520425..d75bb1d3 100644 --- a/packages/db/src/schema/incidents/incident.ts +++ b/packages/db/src/schema/monitor_incidents/monitor_incident.ts @@ -12,7 +12,7 @@ import { monitor } from "../monitors"; import { user } from "../users/user"; import { workspace } from "../workspaces"; -export const statusIncident = [ +export const monitorIncidentStatus = [ "triage", "investigating", "identified", @@ -21,13 +21,13 @@ export const statusIncident = [ "duplicated", ] as const; -export const incidentTable = sqliteTable( - "incident", +export const monitorIncidentTable = sqliteTable( + "monitor_incident", { id: integer("id").primaryKey(), title: text("title").default("").notNull(), summary: text("summary").default("").notNull(), - status: text("status", { enum: statusIncident }) + status: text("status", { enum: monitorIncidentStatus }) .default("triage") .notNull(), @@ -64,33 +64,36 @@ export const incidentTable = sqliteTable( }, (table) => [ unique().on(table.monitorId, table.startedAt), - index("incident_workspace_id_started_at_idx").on( + index("monitor_incident_workspace_id_started_at_idx").on( table.workspaceId, table.startedAt, ), // Partial: open incidents are looked up on every check result, every region, // every minute. Unique so a monitor cannot hold two open incidents at once. - uniqueIndex("incident_open_idx") + uniqueIndex("monitor_incident_open_idx") .on(table.monitorId) .where(sql`${table.resolvedAt} IS NULL`), ], ); -export const incidentRelations = relations(incidentTable, ({ one }) => ({ - monitor: one(monitor, { - fields: [incidentTable.monitorId], - references: [monitor.id], - }), - workspace: one(workspace, { - fields: [incidentTable.workspaceId], - references: [workspace.id], - }), - acknowledgedByUser: one(user, { - fields: [incidentTable.acknowledgedBy], - references: [user.id], - }), - resolvedByUser: one(user, { - fields: [incidentTable.resolvedBy], - references: [user.id], +export const monitorIncidentRelations = relations( + monitorIncidentTable, + ({ one }) => ({ + monitor: one(monitor, { + fields: [monitorIncidentTable.monitorId], + references: [monitor.id], + }), + workspace: one(workspace, { + fields: [monitorIncidentTable.workspaceId], + references: [workspace.id], + }), + acknowledgedByUser: one(user, { + fields: [monitorIncidentTable.acknowledgedBy], + references: [user.id], + }), + resolvedByUser: one(user, { + fields: [monitorIncidentTable.resolvedBy], + references: [user.id], + }), }), -})); +); diff --git a/packages/db/src/schema/monitor_incidents/validation.ts b/packages/db/src/schema/monitor_incidents/validation.ts new file mode 100644 index 00000000..fc4112af --- /dev/null +++ b/packages/db/src/schema/monitor_incidents/validation.ts @@ -0,0 +1,12 @@ +import { createSelectSchema } from "drizzle-zod"; +import { z } from "zod"; + +import { monitorIncidentTable } from "./monitor_incident"; + +export const selectMonitorIncidentSchema = createSelectSchema( + monitorIncidentTable, +).extend({ + monitorName: z.string().optional(), +}); + +export type MonitorIncident = z.infer; diff --git a/packages/db/src/schema/monitors/monitor.ts b/packages/db/src/schema/monitors/monitor.ts index eae65c16..11e63287 100644 --- a/packages/db/src/schema/monitors/monitor.ts +++ b/packages/db/src/schema/monitors/monitor.ts @@ -2,7 +2,7 @@ import { relations, sql } from "drizzle-orm"; import { index, integer, sqliteTable, text } from "drizzle-orm/sqlite-core"; import { monitorPeriodicity } from "../constants"; -import { incidentTable } from "../incidents/incident"; +import { monitorIncidentTable } from "../monitor_incidents/monitor_incident"; import { monitorStatusTable } from "../monitor_status/monitor_status"; import { monitorTagsToMonitors } from "../monitor_tags"; import { notificationsToMonitors } from "../notifications"; @@ -88,7 +88,7 @@ export const monitorRelation = relations(monitor, ({ one, many }) => ({ references: [workspace.id], }), monitorsToNotifications: many(notificationsToMonitors), - incidents: many(incidentTable), + monitorIncidents: many(monitorIncidentTable), monitorStatus: many(monitorStatusTable), privateLocationToMonitors: many(privateLocationToMonitors), })); diff --git a/packages/db/src/schema/notifications/outbox.ts b/packages/db/src/schema/notifications/outbox.ts index 36e24243..e9340b04 100644 --- a/packages/db/src/schema/notifications/outbox.ts +++ b/packages/db/src/schema/notifications/outbox.ts @@ -7,7 +7,7 @@ import { uniqueIndex, } from "drizzle-orm/sqlite-core"; -import { incidentTable } from "../incidents/incident"; +import { monitorIncidentTable } from "../monitor_incidents/monitor_incident"; import { monitorStatus } from "../monitors/constants"; import { monitor } from "../monitors/monitor"; import { workspace } from "../workspaces/workspace"; @@ -52,9 +52,12 @@ export const notificationOutbox = sqliteTable( fromStatus: text("from_status", { enum: monitorStatus }).notNull(), toStatus: text("to_status", { enum: monitorStatus }).notNull(), cronTimestamp: integer("cron_timestamp").notNull(), - incidentId: integer("incident_id").references(() => incidentTable.id, { - onDelete: "set null", - }), + incidentId: integer("incident_id").references( + () => monitorIncidentTable.id, + { + onDelete: "set null", + }, + ), payload: text("payload", { mode: "json" }) .$type() .notNull(), @@ -134,9 +137,9 @@ export const notificationOutboxRelations = relations( fields: [notificationOutbox.notificationId], references: [notification.id], }), - incident: one(incidentTable, { + incident: one(monitorIncidentTable, { fields: [notificationOutbox.incidentId], - references: [incidentTable.id], + references: [monitorIncidentTable.id], }), }), ); diff --git a/packages/db/src/schema/shared.ts b/packages/db/src/schema/shared.ts index 8237a0cf..3e2a1c7d 100644 --- a/packages/db/src/schema/shared.ts +++ b/packages/db/src/schema/shared.ts @@ -1,8 +1,8 @@ import { z } from "zod"; -import { selectIncidentSchema } from "./incidents/validation"; import { selectMaintenanceSchema } from "./maintenances"; import { selectMonitorGroupSchema } from "./monitor_groups"; +import { selectMonitorIncidentSchema } from "./monitor_incidents/validation"; import { selectMonitorSchema } from "./monitors"; import { selectPageComponentGroupSchema } from "./page_component_groups"; import { @@ -75,7 +75,7 @@ export const legacy_selectPublicPageSchemaWithRelation = selectPageSchema .extend({ monitors: z.array(selectPublicMonitorSchema).prefault([]), statusReports: z.array(selectStatusReportPageSchema).prefault([]), - incidents: z.array(selectIncidentSchema).prefault([]), + incidents: z.array(selectMonitorIncidentSchema).prefault([]), maintenances: z.array(selectMaintenancePageSchema).prefault([]), workspacePlan: workspacePlanSchema .nullable() @@ -121,7 +121,7 @@ const selectPublicPageComponentWithStatusSchema = // For monitor-type components - omit status since it's now at component level monitor: selectPublicMonitorBaseSchema .extend({ - incidents: selectIncidentSchema.array().nullish(), + monitorIncidents: selectMonitorIncidentSchema.array().nullish(), }) .nullish(), }); @@ -153,7 +153,7 @@ export const selectPageComponentWithMonitorRelation = selectPageComponentSchema .extend({ monitor: selectPublicMonitorBaseSchema .extend({ - incidents: selectIncidentSchema.array().nullish(), + monitorIncidents: selectMonitorIncidentSchema.array().nullish(), }) .nullish(), group: selectPageComponentGroupSchema.nullish(), @@ -182,7 +182,7 @@ export const selectPublicPageLightSchemaWithRelation = selectPageSchema .extend({ monitors: z.array(selectPublicMonitorSchema).prefault([]), statusReports: z.array(selectStatusReportPageSchema).prefault([]), - incidents: z.array(selectIncidentSchema).prefault([]), + incidents: z.array(selectMonitorIncidentSchema).prefault([]), maintenances: z.array(selectMaintenancePageSchema).prefault([]), workspacePlan: workspacePlanSchema .nullable() @@ -211,7 +211,7 @@ export const selectPublicPageSchemaWithRelation = selectPageSchema lastEvents: z.array(statusPageEventSchema), openEvents: z.array(statusPageEventSchema), statusReports: z.array(selectStatusReportPageSchema), - incidents: z.array(selectIncidentSchema), + incidents: z.array(selectMonitorIncidentSchema), maintenances: z.array(selectMaintenancePageSchema), status: z .enum(["success", "degraded", "error", "info"]) diff --git a/packages/db/src/seed.mts b/packages/db/src/seed.mts index 0bc17373..170ef7d8 100644 --- a/packages/db/src/seed.mts +++ b/packages/db/src/seed.mts @@ -4,7 +4,7 @@ import { drizzle } from "drizzle-orm/libsql"; import { env } from "../env.mjs"; import { externalService, - incidentTable, + monitorIncidentTable, maintenance, maintenancesToPageComponents, monitor, @@ -443,7 +443,7 @@ async function main() { ); await db - .insert(incidentTable) + .insert(monitorIncidentTable) .values([ { id: 1, diff --git a/packages/notifications/base/src/types.ts b/packages/notifications/base/src/types.ts index 00e6a098..23e5c744 100644 --- a/packages/notifications/base/src/types.ts +++ b/packages/notifications/base/src/types.ts @@ -1,5 +1,5 @@ import type { - Incident, + MonitorIncident, Monitor, Notification, } from "@openstatus/db/src/schema"; @@ -15,7 +15,7 @@ export interface NotificationContext { cronTimestamp: number; regions?: string[]; latency?: number; - incident?: Incident; + incident?: MonitorIncident; } /** diff --git a/packages/notifications/base/src/utils/incident.test.ts b/packages/notifications/base/src/utils/incident.test.ts index 78b59a7c..a3d3f9c6 100644 --- a/packages/notifications/base/src/utils/incident.test.ts +++ b/packages/notifications/base/src/utils/incident.test.ts @@ -1,11 +1,11 @@ -import type { Incident } from "@openstatus/db/src/schema"; +import type { MonitorIncident } from "@openstatus/db/src/schema"; import { expect } from "@std/expect"; import { describe, it } from "@std/testing/bdd"; import { getIncidentDuration } from "./incident"; // Helper to create a partial incident object for testing -function createIncident(overrides: Partial): Incident { +function createIncident(overrides: Partial): MonitorIncident { return { id: 1, monitorId: 1, @@ -16,7 +16,7 @@ function createIncident(overrides: Partial): Incident { acknowledgedAt: null, acknowledgedBy: null, ...overrides, - } as Incident; + } as MonitorIncident; } describe("getIncidentDuration", () => { diff --git a/packages/notifications/base/src/utils/incident.ts b/packages/notifications/base/src/utils/incident.ts index aa6709f1..0f2ee2df 100644 --- a/packages/notifications/base/src/utils/incident.ts +++ b/packages/notifications/base/src/utils/incident.ts @@ -1,4 +1,4 @@ -import type { Incident } from "@openstatus/db/src/schema"; +import type { MonitorIncident } from "@openstatus/db/src/schema"; import { formatDuration } from "./duration"; @@ -24,7 +24,7 @@ import { formatDuration } from "./duration"; * @param incident - The incident object * @returns Formatted duration string or null if incident is not resolved */ -export function getIncidentDuration(incident: Incident): string | null { +export function getIncidentDuration(incident: MonitorIncident): string | null { if (!incident.startedAt) { return null; } diff --git a/packages/notifications/base/src/utils/message.ts b/packages/notifications/base/src/utils/message.ts index 65f5a8ca..3c32f571 100644 --- a/packages/notifications/base/src/utils/message.ts +++ b/packages/notifications/base/src/utils/message.ts @@ -1,4 +1,4 @@ -import type { Incident } from "@openstatus/db/src/schema"; +import type { MonitorIncident } from "@openstatus/db/src/schema"; import { getRegionInfo } from "@openstatus/regions"; import type { FormattedMessageData, NotificationContext } from "../types"; @@ -78,7 +78,7 @@ export function formatStatusCode(statusCode?: number): string { export function buildCommonMessageData( context: NotificationContext, options?: { - incident?: Incident; + incident?: MonitorIncident; }, ): FormattedMessageData { const { monitor, statusCode, message, cronTimestamp, regions, latency } = diff --git a/packages/notifications/ms-teams/src/index.test.ts b/packages/notifications/ms-teams/src/index.test.ts index 62e31ac3..18b41f49 100644 --- a/packages/notifications/ms-teams/src/index.test.ts +++ b/packages/notifications/ms-teams/src/index.test.ts @@ -1,4 +1,4 @@ -import type { Incident, Monitor } from "@openstatus/db/src/schema"; +import type { MonitorIncident, Monitor } from "@openstatus/db/src/schema"; import { selectNotificationSchema } from "@openstatus/db/src/schema"; import { expect } from "@std/expect"; import { afterEach, beforeEach, describe, test } from "@std/testing/bdd"; @@ -96,7 +96,7 @@ describe("Microsoft Teams Notifications", () => { }), }); - const createMockResolvedIncident = (): Incident => ({ + const createMockResolvedIncident = (): MonitorIncident => ({ id: 1, title: "", summary: "", diff --git a/packages/services/package.json b/packages/services/package.json index 10e407a7..f1848723 100644 --- a/packages/services/package.json +++ b/packages/services/package.json @@ -21,9 +21,9 @@ "import": "./src/maintenance/index.ts", "types": "./src/maintenance/index.ts" }, - "./incident": { - "import": "./src/incident/index.ts", - "types": "./src/incident/index.ts" + "./monitor-incident": { + "import": "./src/monitor-incident/index.ts", + "types": "./src/monitor-incident/index.ts" }, "./monitor": { "import": "./src/monitor/index.ts", diff --git a/packages/services/src/frozen-uptime/__tests__/get-history.test.ts b/packages/services/src/frozen-uptime/__tests__/get-history.test.ts index 6eba4ace..2a86168c 100644 --- a/packages/services/src/frozen-uptime/__tests__/get-history.test.ts +++ b/packages/services/src/frozen-uptime/__tests__/get-history.test.ts @@ -1,6 +1,6 @@ import { frozenMonitorUptime, - incidentTable, + monitorIncidentTable, monitor, page, pageComponent, @@ -595,7 +595,7 @@ describe("getUptimeHistory", () => { const base = monthStart(key(1)).getTime() + 5 * MS_PER_DAY; const twoHours = 2 * 3_600_000; // incident and major report describe the same 2h outage → merged once - await tx.insert(incidentTable).values({ + await tx.insert(monitorIncidentTable).values({ workspaceId: teamWorkspaceId, monitorId: testMonitor.id, startedAt: new Date(base), @@ -718,7 +718,7 @@ describe("getUptimeHistory", () => { }, ]); const twoHours = 2 * 3_600_000; - await tx.insert(incidentTable).values({ + await tx.insert(monitorIncidentTable).values({ workspaceId: teamWorkspaceId, monitorId: testMonitor.id, startedAt: new Date(start + twoHours), diff --git a/packages/services/src/frozen-uptime/get-history.ts b/packages/services/src/frozen-uptime/get-history.ts index 5d38b027..7584a0c1 100644 --- a/packages/services/src/frozen-uptime/get-history.ts +++ b/packages/services/src/frozen-uptime/get-history.ts @@ -181,7 +181,7 @@ export async function getUptimeHistory(args: { }, }, pageComponents: { - with: { monitor: { with: { incidents: true } } }, + with: { monitor: { with: { monitorIncidents: true } } }, orderBy: (components, { asc }) => asc(components.order), }, }, @@ -295,7 +295,7 @@ export async function getUptimeHistory(args: { const rows: UptimeHistoryRow[] = components.map((c) => { const events = getEvents({ maintenances: _page.maintenances, - incidents: c.monitor?.incidents ?? [], + incidents: c.monitor?.monitorIncidents ?? [], reports: _page.statusReports, pageComponentId: c.id, monitorId: c.monitorId ?? undefined, diff --git a/packages/services/src/incident/delete.ts b/packages/services/src/incident/delete.ts deleted file mode 100644 index 113b4a16..00000000 --- a/packages/services/src/incident/delete.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { eq } from "@openstatus/db"; -import { incidentTable } from "@openstatus/db/src/schema"; - -import { emitAudit } from "../audit"; -import { requireScope } from "../auth"; -import { type ServiceContext, withTransaction } from "../context"; -import { getIncidentInWorkspace } from "./internal"; -import { DeleteIncidentInput } from "./schemas"; - -export async function deleteIncident(args: { - ctx: ServiceContext; - input: DeleteIncidentInput; -}): Promise { - const { ctx } = args; - requireScope(ctx, "write"); - const input = DeleteIncidentInput.parse(args.input); - - await withTransaction(ctx, async (tx) => { - const existing = await getIncidentInWorkspace({ - tx, - id: input.id, - workspaceId: ctx.workspace.id, - }); - - await tx.delete(incidentTable).where(eq(incidentTable.id, existing.id)); - - await emitAudit(tx, ctx, { - action: "incident.delete", - entityType: "incident", - entityId: existing.id, - before: existing, - }); - }); -} diff --git a/packages/services/src/incident/index.ts b/packages/services/src/incident/index.ts deleted file mode 100644 index 8e80a5a6..00000000 --- a/packages/services/src/incident/index.ts +++ /dev/null @@ -1,20 +0,0 @@ -export { acknowledgeIncident } from "./acknowledge"; -export { deleteIncident } from "./delete"; -export { - getIncident, - type IncidentWithRelations, - listIncidents, - type ListIncidentsResult, -} from "./list"; -export { resolveIncident } from "./resolve"; - -export { - AcknowledgeIncidentInput, - DeleteIncidentInput, - GetIncidentInput, - type IncidentListPeriod, - incidentListPeriodSchema, - incidentListPeriods, - ListIncidentsInput, - ResolveIncidentInput, -} from "./schemas"; diff --git a/packages/services/src/incident/schemas.ts b/packages/services/src/incident/schemas.ts deleted file mode 100644 index b90054fa..00000000 --- a/packages/services/src/incident/schemas.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { z } from "zod"; - -/** Matches the `periods` constant in the existing tRPC router. */ -export const incidentListPeriods = ["1d", "7d", "14d"] as const; -export type IncidentListPeriod = (typeof incidentListPeriods)[number]; -export const incidentListPeriodSchema = z.enum(incidentListPeriods); - -export const AcknowledgeIncidentInput = z.object({ id: z.number().int() }); -export type AcknowledgeIncidentInput = z.infer; - -export const ResolveIncidentInput = z.object({ id: z.number().int() }); -export type ResolveIncidentInput = z.infer; - -export const DeleteIncidentInput = z.object({ id: z.number().int() }); -export type DeleteIncidentInput = z.infer; - -export const GetIncidentInput = z.object({ id: z.number().int() }); -export type GetIncidentInput = z.infer; - -// `limit` unbounded at the schema level — same convention as status-report / -// maintenance. tRPC passes the 10_000 sentinel; external surfaces cap in the -// adapter. -export const ListIncidentsInput = z.object({ - limit: z.number().int().min(1).default(50), - offset: z.number().int().min(0).default(0), - period: incidentListPeriodSchema.optional(), - monitorId: z.number().int().optional(), - order: z.enum(["asc", "desc"]).default("desc"), -}); -export type ListIncidentsInput = z.infer; diff --git a/packages/services/src/incident/__tests__/incident.test.ts b/packages/services/src/monitor-incident/__tests__/monitor-incident.test.ts similarity index 83% rename from packages/services/src/incident/__tests__/incident.test.ts rename to packages/services/src/monitor-incident/__tests__/monitor-incident.test.ts index 439b26fa..cfcb02e0 100644 --- a/packages/services/src/incident/__tests__/incident.test.ts +++ b/packages/services/src/monitor-incident/__tests__/monitor-incident.test.ts @@ -1,5 +1,5 @@ import { db, eq } from "@openstatus/db"; -import { incidentTable, monitor } from "@openstatus/db/src/schema"; +import { monitorIncidentTable, monitor } from "@openstatus/db/src/schema"; import { expect } from "@std/expect"; import { afterAll, beforeAll, describe, test } from "@std/testing/bdd"; @@ -12,10 +12,10 @@ import { } from "../../../test/helpers"; import type { DrizzleTx, ServiceContext } from "../../context"; import { ConflictError, ForbiddenError, NotFoundError } from "../../errors"; -import { acknowledgeIncident } from "../acknowledge"; -import { deleteIncident } from "../delete"; -import { getIncident, listIncidents } from "../list"; -import { resolveIncident } from "../resolve"; +import { acknowledgeMonitorIncident } from "../acknowledge"; +import { deleteMonitorIncident } from "../delete"; +import { getMonitorIncident, listMonitorIncidents } from "../list"; +import { resolveMonitorIncident } from "../resolve"; const TEST_PREFIX = "svc-incident-test"; @@ -66,7 +66,7 @@ async function insertIncident( nextStartedAtOffset += 1; const startedAt = new Date(Date.now() - nextStartedAtOffset * 60 * 1000); const row = await tx - .insert(incidentTable) + .insert(monitorIncidentTable) .values({ workspaceId: opts.workspaceId, monitorId: opts.monitorId, @@ -79,7 +79,7 @@ async function insertIncident( return row; } -describe("acknowledgeIncident", () => { +describe("acknowledgeMonitorIncident", () => { test("stamps acknowledgedAt + acknowledgedBy and emits audit", async () => { await withTestTransaction(async (tx) => { const ctx = { ...teamCtx, db: tx }; @@ -88,7 +88,7 @@ describe("acknowledgeIncident", () => { monitorId: testMonitorId, }); - const updated = await acknowledgeIncident({ + const updated = await acknowledgeMonitorIncident({ ctx, input: { id: incident.id }, }); @@ -98,8 +98,8 @@ describe("acknowledgeIncident", () => { await expectAuditRow({ workspaceId: teamCtx.workspace.id, - action: "incident.update", - entityType: "incident", + action: "monitor_incident.update", + entityType: "monitor_incident", entityId: incident.id, db: tx, }); @@ -116,7 +116,7 @@ describe("acknowledgeIncident", () => { }); await expect( - acknowledgeIncident({ ctx, input: { id: incident.id } }), + acknowledgeMonitorIncident({ ctx, input: { id: incident.id } }), ).rejects.toBeInstanceOf(ConflictError); }); }); @@ -129,7 +129,7 @@ describe("acknowledgeIncident", () => { }); await expect( - acknowledgeIncident({ + acknowledgeMonitorIncident({ ctx: { ...freeCtx, db: tx }, input: { id: incident.id }, }), @@ -152,13 +152,13 @@ describe("acknowledgeIncident", () => { db: tx, }; await expect( - acknowledgeIncident({ ctx, input: { id: incident.id } }), + acknowledgeMonitorIncident({ ctx, input: { id: incident.id } }), ).rejects.toBeInstanceOf(ForbiddenError); }); }); }); -describe("resolveIncident", () => { +describe("resolveMonitorIncident", () => { test("stamps resolvedAt + resolvedBy", async () => { await withTestTransaction(async (tx) => { const ctx = { ...teamCtx, db: tx }; @@ -167,7 +167,7 @@ describe("resolveIncident", () => { monitorId: testMonitorId, }); - const updated = await resolveIncident({ + const updated = await resolveMonitorIncident({ ctx, input: { id: incident.id }, }); @@ -186,7 +186,7 @@ describe("resolveIncident", () => { }); await expect( - resolveIncident({ ctx, input: { id: incident.id } }), + resolveMonitorIncident({ ctx, input: { id: incident.id } }), ).rejects.toBeInstanceOf(ConflictError); }); }); @@ -199,7 +199,7 @@ describe("resolveIncident", () => { }); await expect( - resolveIncident({ + resolveMonitorIncident({ ctx: { ...freeCtx, db: tx }, input: { id: incident.id }, }), @@ -208,7 +208,7 @@ describe("resolveIncident", () => { }); }); -describe("deleteIncident", () => { +describe("deleteMonitorIncident", () => { test("removes the row and emits audit", async () => { await withTestTransaction(async (tx) => { const ctx = { ...teamCtx, db: tx }; @@ -217,19 +217,19 @@ describe("deleteIncident", () => { monitorId: testMonitorId, }); - await deleteIncident({ ctx, input: { id: incident.id } }); + await deleteMonitorIncident({ ctx, input: { id: incident.id } }); const remaining = await tx .select() - .from(incidentTable) - .where(eq(incidentTable.id, incident.id)) + .from(monitorIncidentTable) + .where(eq(monitorIncidentTable.id, incident.id)) .all(); expect(remaining).toHaveLength(0); await expectAuditRow({ workspaceId: teamCtx.workspace.id, - action: "incident.delete", - entityType: "incident", + action: "monitor_incident.delete", + entityType: "monitor_incident", entityId: incident.id, db: tx, }); @@ -244,7 +244,7 @@ describe("deleteIncident", () => { }); await expect( - deleteIncident({ + deleteMonitorIncident({ ctx: { ...freeCtx, db: tx }, input: { id: incident.id }, }), @@ -263,17 +263,17 @@ describe("list / get", () => { monitorId: testMonitorId, }); - const full = await getIncident({ + const full = await getMonitorIncident({ ctx: teamCtxTx, input: { id: incident.id }, }); expect(full.monitor?.id).toBe(testMonitorId); await expect( - getIncident({ ctx: freeCtxTx, input: { id: incident.id } }), + getMonitorIncident({ ctx: freeCtxTx, input: { id: incident.id } }), ).rejects.toBeInstanceOf(NotFoundError); - const { items } = await listIncidents({ + const { items } = await listMonitorIncidents({ ctx: freeCtxTx, input: { limit: 100, @@ -293,14 +293,14 @@ describe("list / get", () => { monitorId: testMonitorId, }); // Only one incident per monitor may be open at a time - // (partial unique index `incident_open_idx`), so the second is resolved. + // (partial unique index `monitor_incident_open_idx`), so the second is resolved. const b = await insertIncident(tx, { workspaceId: teamCtx.workspace.id, monitorId: testMonitorId, resolvedAt: new Date(), }); - const { items } = await listIncidents({ + const { items } = await listMonitorIncidents({ ctx, input: { limit: 100, diff --git a/packages/services/src/incident/acknowledge.ts b/packages/services/src/monitor-incident/acknowledge.ts similarity index 64% rename from packages/services/src/incident/acknowledge.ts rename to packages/services/src/monitor-incident/acknowledge.ts index 373911ec..bee94a19 100644 --- a/packages/services/src/incident/acknowledge.ts +++ b/packages/services/src/monitor-incident/acknowledge.ts @@ -1,5 +1,5 @@ import { and, eq, isNull } from "@openstatus/db"; -import { incidentTable } from "@openstatus/db/src/schema"; +import { monitorIncidentTable } from "@openstatus/db/src/schema"; import { emitAudit } from "../audit"; import { requireScope } from "../auth"; @@ -9,20 +9,20 @@ import { withTransaction, } from "../context"; import { ConflictError } from "../errors"; -import type { Incident } from "../types"; -import { getIncidentInWorkspace } from "./internal"; -import { AcknowledgeIncidentInput } from "./schemas"; +import type { MonitorIncident } from "../types"; +import { getMonitorIncidentInWorkspace } from "./internal"; +import { AcknowledgeMonitorIncidentInput } from "./schemas"; -export async function acknowledgeIncident(args: { +export async function acknowledgeMonitorIncident(args: { ctx: ServiceContext; - input: AcknowledgeIncidentInput; -}): Promise { + input: AcknowledgeMonitorIncidentInput; +}): Promise { const { ctx } = args; requireScope(ctx, "write"); - const input = AcknowledgeIncidentInput.parse(args.input); + const input = AcknowledgeMonitorIncidentInput.parse(args.input); return withTransaction(ctx, async (tx) => { - const existing = await getIncidentInWorkspace({ + const existing = await getMonitorIncidentInWorkspace({ tx, id: input.id, workspaceId: ctx.workspace.id, @@ -37,7 +37,7 @@ export async function acknowledgeIncident(args: { // row, at which point we throw the same `ConflictError` the pre-read // would have raised. const updated = await tx - .update(incidentTable) + .update(monitorIncidentTable) .set({ acknowledgedAt: now, acknowledgedBy: tryGetActorUserId(ctx.actor), @@ -45,8 +45,8 @@ export async function acknowledgeIncident(args: { }) .where( and( - eq(incidentTable.id, existing.id), - isNull(incidentTable.acknowledgedAt), + eq(monitorIncidentTable.id, existing.id), + isNull(monitorIncidentTable.acknowledgedAt), ), ) .returning() @@ -56,8 +56,8 @@ export async function acknowledgeIncident(args: { } await emitAudit(tx, ctx, { - action: "incident.update", - entityType: "incident", + action: "monitor_incident.update", + entityType: "monitor_incident", entityId: updated.id, before: existing, after: updated, diff --git a/packages/services/src/monitor-incident/delete.ts b/packages/services/src/monitor-incident/delete.ts new file mode 100644 index 00000000..3fdfc8db --- /dev/null +++ b/packages/services/src/monitor-incident/delete.ts @@ -0,0 +1,36 @@ +import { eq } from "@openstatus/db"; +import { monitorIncidentTable } from "@openstatus/db/src/schema"; + +import { emitAudit } from "../audit"; +import { requireScope } from "../auth"; +import { type ServiceContext, withTransaction } from "../context"; +import { getMonitorIncidentInWorkspace } from "./internal"; +import { DeleteMonitorIncidentInput } from "./schemas"; + +export async function deleteMonitorIncident(args: { + ctx: ServiceContext; + input: DeleteMonitorIncidentInput; +}): Promise { + const { ctx } = args; + requireScope(ctx, "write"); + const input = DeleteMonitorIncidentInput.parse(args.input); + + await withTransaction(ctx, async (tx) => { + const existing = await getMonitorIncidentInWorkspace({ + tx, + id: input.id, + workspaceId: ctx.workspace.id, + }); + + await tx + .delete(monitorIncidentTable) + .where(eq(monitorIncidentTable.id, existing.id)); + + await emitAudit(tx, ctx, { + action: "monitor_incident.delete", + entityType: "monitor_incident", + entityId: existing.id, + before: existing, + }); + }); +} diff --git a/packages/services/src/monitor-incident/index.ts b/packages/services/src/monitor-incident/index.ts new file mode 100644 index 00000000..0bd95aba --- /dev/null +++ b/packages/services/src/monitor-incident/index.ts @@ -0,0 +1,20 @@ +export { acknowledgeMonitorIncident } from "./acknowledge"; +export { deleteMonitorIncident } from "./delete"; +export { + getMonitorIncident, + type MonitorIncidentWithRelations, + listMonitorIncidents, + type ListMonitorIncidentsResult, +} from "./list"; +export { resolveMonitorIncident } from "./resolve"; + +export { + AcknowledgeMonitorIncidentInput, + DeleteMonitorIncidentInput, + GetMonitorIncidentInput, + type MonitorIncidentListPeriod, + monitorIncidentListPeriodSchema, + monitorIncidentListPeriods, + ListMonitorIncidentsInput, + ResolveMonitorIncidentInput, +} from "./schemas"; diff --git a/packages/services/src/incident/internal.ts b/packages/services/src/monitor-incident/internal.ts similarity index 60% rename from packages/services/src/incident/internal.ts rename to packages/services/src/monitor-incident/internal.ts index 98c5319f..f2065a8d 100644 --- a/packages/services/src/incident/internal.ts +++ b/packages/services/src/monitor-incident/internal.ts @@ -1,11 +1,11 @@ import { and, eq } from "@openstatus/db"; -import { incidentTable } from "@openstatus/db/src/schema"; +import { monitorIncidentTable } from "@openstatus/db/src/schema"; import type { DB } from "../context"; import { NotFoundError } from "../errors"; /** Load an incident by id, scoped to the workspace. Throws on miss. */ -export async function getIncidentInWorkspace(args: { +export async function getMonitorIncidentInWorkspace(args: { tx: DB; id: number; workspaceId: number; @@ -13,9 +13,12 @@ export async function getIncidentInWorkspace(args: { const { tx, id, workspaceId } = args; const row = await tx .select() - .from(incidentTable) + .from(monitorIncidentTable) .where( - and(eq(incidentTable.id, id), eq(incidentTable.workspaceId, workspaceId)), + and( + eq(monitorIncidentTable.id, id), + eq(monitorIncidentTable.workspaceId, workspaceId), + ), ) .get(); if (!row) throw new NotFoundError("incident", id); diff --git a/packages/services/src/incident/list.ts b/packages/services/src/monitor-incident/list.ts similarity index 67% rename from packages/services/src/incident/list.ts rename to packages/services/src/monitor-incident/list.ts index 6fe1731f..37bf2484 100644 --- a/packages/services/src/incident/list.ts +++ b/packages/services/src/monitor-incident/list.ts @@ -10,21 +10,21 @@ import { sql, } from "@openstatus/db"; import { - incidentTable, + monitorIncidentTable, monitor, selectMonitorSchema, } from "@openstatus/db/src/schema"; import type { DB, ServiceContext } from "../context"; -import type { Incident, Monitor } from "../types"; -import { getIncidentInWorkspace } from "./internal"; +import type { MonitorIncident, Monitor } from "../types"; +import { getMonitorIncidentInWorkspace } from "./internal"; import { - GetIncidentInput, - type IncidentListPeriod, - ListIncidentsInput, + GetMonitorIncidentInput, + type MonitorIncidentListPeriod, + ListMonitorIncidentsInput, } from "./schemas"; -function periodToSince(period: IncidentListPeriod): Date { +function periodToSince(period: MonitorIncidentListPeriod): Date { const day = 24 * 60 * 60 * 1000; const now = Date.now(); switch (period) { @@ -37,12 +37,12 @@ function periodToSince(period: IncidentListPeriod): Date { } } -export type IncidentWithRelations = Incident & { +export type MonitorIncidentWithRelations = MonitorIncident & { monitor: Monitor | null; }; -export type ListIncidentsResult = { - items: IncidentWithRelations[]; +export type ListMonitorIncidentsResult = { + items: MonitorIncidentWithRelations[]; totalSize: number; }; @@ -56,9 +56,9 @@ export type ListIncidentsResult = { */ async function enrichIncidentsBatch( db: DB, - rows: Incident[], + rows: MonitorIncident[], workspaceId: number, -): Promise { +): Promise { if (rows.length === 0) return []; const monitorIdsSet = new Set(); @@ -89,37 +89,41 @@ async function enrichIncidentsBatch( })); } -export async function listIncidents(args: { +export async function listMonitorIncidents(args: { ctx: ServiceContext; - input: ListIncidentsInput; -}): Promise { + input: ListMonitorIncidentsInput; +}): Promise { const { ctx } = args; - const input = ListIncidentsInput.parse(args.input); + const input = ListMonitorIncidentsInput.parse(args.input); const db = ctx.db ?? defaultDb; - const conditions: SQL[] = [eq(incidentTable.workspaceId, ctx.workspace.id)]; + const conditions: SQL[] = [ + eq(monitorIncidentTable.workspaceId, ctx.workspace.id), + ]; if (input.monitorId !== undefined) { - conditions.push(eq(incidentTable.monitorId, input.monitorId)); + conditions.push(eq(monitorIncidentTable.monitorId, input.monitorId)); } if (input.period !== undefined) { - conditions.push(gte(incidentTable.startedAt, periodToSince(input.period))); + conditions.push( + gte(monitorIncidentTable.startedAt, periodToSince(input.period)), + ); } const whereClause = and(...conditions); const [countRow, rows] = await Promise.all([ db .select({ count: sql`count(*)` }) - .from(incidentTable) + .from(monitorIncidentTable) .where(whereClause) .get(), db .select() - .from(incidentTable) + .from(monitorIncidentTable) .where(whereClause) .orderBy( input.order === "asc" - ? asc(incidentTable.startedAt) - : desc(incidentTable.startedAt), + ? asc(monitorIncidentTable.startedAt) + : desc(monitorIncidentTable.startedAt), ) .limit(input.limit) .offset(input.offset) @@ -131,14 +135,14 @@ export async function listIncidents(args: { return { items, totalSize }; } -export async function getIncident(args: { +export async function getMonitorIncident(args: { ctx: ServiceContext; - input: GetIncidentInput; -}): Promise { + input: GetMonitorIncidentInput; +}): Promise { const { ctx } = args; - const input = GetIncidentInput.parse(args.input); + const input = GetMonitorIncidentInput.parse(args.input); const db = ctx.db ?? defaultDb; - const record = await getIncidentInWorkspace({ + const record = await getMonitorIncidentInWorkspace({ tx: db, id: input.id, workspaceId: ctx.workspace.id, diff --git a/packages/services/src/incident/resolve.ts b/packages/services/src/monitor-incident/resolve.ts similarity index 64% rename from packages/services/src/incident/resolve.ts rename to packages/services/src/monitor-incident/resolve.ts index 2d4a7023..229ff605 100644 --- a/packages/services/src/incident/resolve.ts +++ b/packages/services/src/monitor-incident/resolve.ts @@ -1,5 +1,5 @@ import { and, eq, isNull } from "@openstatus/db"; -import { incidentTable } from "@openstatus/db/src/schema"; +import { monitorIncidentTable } from "@openstatus/db/src/schema"; import { emitAudit } from "../audit"; import { requireScope } from "../auth"; @@ -9,20 +9,20 @@ import { withTransaction, } from "../context"; import { ConflictError } from "../errors"; -import type { Incident } from "../types"; -import { getIncidentInWorkspace } from "./internal"; -import { ResolveIncidentInput } from "./schemas"; +import type { MonitorIncident } from "../types"; +import { getMonitorIncidentInWorkspace } from "./internal"; +import { ResolveMonitorIncidentInput } from "./schemas"; -export async function resolveIncident(args: { +export async function resolveMonitorIncident(args: { ctx: ServiceContext; - input: ResolveIncidentInput; -}): Promise { + input: ResolveMonitorIncidentInput; +}): Promise { const { ctx } = args; requireScope(ctx, "write"); - const input = ResolveIncidentInput.parse(args.input); + const input = ResolveMonitorIncidentInput.parse(args.input); return withTransaction(ctx, async (tx) => { - const existing = await getIncidentInWorkspace({ + const existing = await getMonitorIncidentInWorkspace({ tx, id: input.id, workspaceId: ctx.workspace.id, @@ -36,7 +36,7 @@ export async function resolveIncident(args: { // still NULL. Concurrent resolvers lose the race and get a no-row // return, which we translate into the same `ConflictError`. const updated = await tx - .update(incidentTable) + .update(monitorIncidentTable) .set({ resolvedAt: now, resolvedBy: tryGetActorUserId(ctx.actor), @@ -44,8 +44,8 @@ export async function resolveIncident(args: { }) .where( and( - eq(incidentTable.id, existing.id), - isNull(incidentTable.resolvedAt), + eq(monitorIncidentTable.id, existing.id), + isNull(monitorIncidentTable.resolvedAt), ), ) .returning() @@ -55,8 +55,8 @@ export async function resolveIncident(args: { } await emitAudit(tx, ctx, { - action: "incident.update", - entityType: "incident", + action: "monitor_incident.update", + entityType: "monitor_incident", entityId: updated.id, before: existing, after: updated, diff --git a/packages/services/src/monitor-incident/schemas.ts b/packages/services/src/monitor-incident/schemas.ts new file mode 100644 index 00000000..cfa5044c --- /dev/null +++ b/packages/services/src/monitor-incident/schemas.ts @@ -0,0 +1,43 @@ +import { z } from "zod"; + +/** Matches the `periods` constant in the existing tRPC router. */ +export const monitorIncidentListPeriods = ["1d", "7d", "14d"] as const; +export type MonitorIncidentListPeriod = + (typeof monitorIncidentListPeriods)[number]; +export const monitorIncidentListPeriodSchema = z.enum( + monitorIncidentListPeriods, +); + +export const AcknowledgeMonitorIncidentInput = z.object({ + id: z.number().int(), +}); +export type AcknowledgeMonitorIncidentInput = z.infer< + typeof AcknowledgeMonitorIncidentInput +>; + +export const ResolveMonitorIncidentInput = z.object({ id: z.number().int() }); +export type ResolveMonitorIncidentInput = z.infer< + typeof ResolveMonitorIncidentInput +>; + +export const DeleteMonitorIncidentInput = z.object({ id: z.number().int() }); +export type DeleteMonitorIncidentInput = z.infer< + typeof DeleteMonitorIncidentInput +>; + +export const GetMonitorIncidentInput = z.object({ id: z.number().int() }); +export type GetMonitorIncidentInput = z.infer; + +// `limit` unbounded at the schema level — same convention as status-report / +// maintenance. tRPC passes the 10_000 sentinel; external surfaces cap in the +// adapter. +export const ListMonitorIncidentsInput = z.object({ + limit: z.number().int().min(1).default(50), + offset: z.number().int().min(0).default(0), + period: monitorIncidentListPeriodSchema.optional(), + monitorId: z.number().int().optional(), + order: z.enum(["asc", "desc"]).default("desc"), +}); +export type ListMonitorIncidentsInput = z.infer< + typeof ListMonitorIncidentsInput +>; diff --git a/packages/services/src/monitor/list.ts b/packages/services/src/monitor/list.ts index 77d14e5e..7f1c3b92 100644 --- a/packages/services/src/monitor/list.ts +++ b/packages/services/src/monitor/list.ts @@ -10,7 +10,7 @@ import { sql, } from "@openstatus/db"; import { - incidentTable, + monitorIncidentTable, monitor, monitorTag, monitorTagsToMonitors, @@ -18,7 +18,7 @@ import { notificationsToMonitors, privateLocation, privateLocationToMonitors, - selectIncidentSchema, + selectMonitorIncidentSchema, selectMonitorSchema, selectMonitorTagSchema, selectNotificationSchema, @@ -27,7 +27,7 @@ import { import type { DB, ServiceContext } from "../context"; import type { - Incident, + MonitorIncident, Monitor, MonitorTag, Notification, @@ -38,12 +38,12 @@ import { GetMonitorInput, ListMonitorsInput } from "./schemas"; export type MonitorListItem = Monitor & { tags: MonitorTag[]; - incidents: Incident[]; + incidents: MonitorIncident[]; }; export type MonitorWithRelations = Monitor & { tags: MonitorTag[]; - incidents: Incident[]; + incidents: MonitorIncident[]; notifications: Notification[]; privateLocations: PrivateLocation[]; }; @@ -92,14 +92,14 @@ async function enrichMonitorsBatch( .all(), db .select() - .from(incidentTable) + .from(monitorIncidentTable) .where( and( - inArray(incidentTable.monitorId, ids), + inArray(monitorIncidentTable.monitorId, ids), // Scope to caller's workspace — defence-in-depth in case an // incident.monitorId somehow points cross-workspace. The // `incident.monitorId` FK doesn't enforce workspace ownership. - eq(incidentTable.workspaceId, workspaceId), + eq(monitorIncidentTable.workspaceId, workspaceId), ), ) .all(), @@ -151,10 +151,10 @@ async function enrichMonitorsBatch( else tagsByMonitor.set(row.monitorId, [tag]); } - const incidentsByMonitor = new Map(); + const incidentsByMonitor = new Map(); for (const row of incidentRows) { if (row.monitorId == null) continue; - const incident = selectIncidentSchema.parse(row); + const incident = selectMonitorIncidentSchema.parse(row); const arr = incidentsByMonitor.get(row.monitorId); if (arr) arr.push(incident); else incidentsByMonitor.set(row.monitorId, [incident]); diff --git a/packages/services/src/page-component/get-daily-summary.ts b/packages/services/src/page-component/get-daily-summary.ts index 0b5b4b31..0d4ec1b6 100644 --- a/packages/services/src/page-component/get-daily-summary.ts +++ b/packages/services/src/page-component/get-daily-summary.ts @@ -1,5 +1,5 @@ import { and, asc, eq, inArray } from "@openstatus/db"; -import { incidentTable, pageComponent } from "@openstatus/db/src/schema"; +import { monitorIncidentTable, pageComponent } from "@openstatus/db/src/schema"; import type { PageComponentImpact, PageComponentType, @@ -112,8 +112,8 @@ export async function getPageComponentDailySummary(args: { monitorIds.length > 0 ? db .select() - .from(incidentTable) - .where(inArray(incidentTable.monitorId, monitorIds)) + .from(monitorIncidentTable) + .where(inArray(monitorIncidentTable.monitorId, monitorIds)) .all() : Promise.resolve([]), ]); diff --git a/packages/services/src/status-timeline/events.ts b/packages/services/src/status-timeline/events.ts index 98e6a000..2519d879 100644 --- a/packages/services/src/status-timeline/events.ts +++ b/packages/services/src/status-timeline/events.ts @@ -1,5 +1,5 @@ import type { - Incident, + MonitorIncident, Maintenance, PageComponent, PageComponentImpact, @@ -260,7 +260,7 @@ export function getEvents({ pageComponent: PageComponent | null; }[]; })[]; - incidents: Incident[]; + incidents: MonitorIncident[]; reports: (StatusReport & { statusReportsToPageComponents: { pageComponent: PageComponent | null; diff --git a/packages/services/src/types.ts b/packages/services/src/types.ts index 9937d1dd..6579d93a 100644 --- a/packages/services/src/types.ts +++ b/packages/services/src/types.ts @@ -24,7 +24,7 @@ export type { Page, PageComponent } from "@openstatus/db/src/schema"; export type { Maintenance } from "@openstatus/db/src/schema"; -export type { Incident, Monitor } from "@openstatus/db/src/schema"; +export type { MonitorIncident, Monitor } from "@openstatus/db/src/schema"; export type { MonitorTag, diff --git a/packages/tracker/src/tracker.test.ts b/packages/tracker/src/tracker.test.ts index 34fb595b..4046f845 100644 --- a/packages/tracker/src/tracker.test.ts +++ b/packages/tracker/src/tracker.test.ts @@ -1,5 +1,5 @@ import type { - Incident, + MonitorIncident, Maintenance, StatusReport, StatusReportUpdate, @@ -24,7 +24,9 @@ type ReportWithUpdates = StatusReport & { statusReportUpdates?: StatusReportUpdate[]; }; -function createIncident(overrides: Partial = {}): Incident { +function createIncident( + overrides: Partial = {}, +): MonitorIncident { return { id: 1, title: "", diff --git a/packages/tracker/src/tracker.ts b/packages/tracker/src/tracker.ts index 36fbd230..e89ff8f3 100644 --- a/packages/tracker/src/tracker.ts +++ b/packages/tracker/src/tracker.ts @@ -1,5 +1,5 @@ import type { - Incident, + MonitorIncident, Maintenance, StatusReport, StatusReportUpdate, @@ -19,7 +19,7 @@ export type Monitor = { type StatusReports = (StatusReport & { statusReportUpdates?: StatusReportUpdate[]; })[]; -type Incidents = Incident[]; +type Incidents = MonitorIncident[]; type Maintenances = Maintenance[]; /** diff --git a/packages/tracker/src/types.ts b/packages/tracker/src/types.ts index a8980efc..26d7be80 100644 --- a/packages/tracker/src/types.ts +++ b/packages/tracker/src/types.ts @@ -1,4 +1,4 @@ -import type { Incident, StatusReport } from "@openstatus/db/src/schema"; +import type { MonitorIncident, StatusReport } from "@openstatus/db/src/schema"; // DO NOT CHANGE! export enum Status { @@ -33,7 +33,7 @@ export type TrackerData = { ok: number; count: number; date: Date; - incidents: Incident[]; + incidents: MonitorIncident[]; statusReports: StatusReport[]; status: Status; variant: StatusVariant;