From abfc2f7eb773ecda2f4e8438bd16df5fde66bda1 Mon Sep 17 00:00:00 2001 From: Thibault Le Ouay Date: Tue, 22 Oct 2024 08:54:32 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=A5=20On-demand=20test=20(#1046)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🚧 on demand test * 🚧 on demand test * ci: apply automated fixes * 💽 migrations * ci: apply automated fixes * add result endpoint * ci: apply automated fixes * 🙏 fix build * 😁 check limit * ci: apply automated fixes * chore: update pricing table * 🤣 limit per month * ci: apply automated fixes * 🔥 updaet tb * drop migration * apply migration * ci: apply automated fixes --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Maximilian Kaske --- apps/checker/handlers/checker.go | 10 +- apps/checker/request/request.go | 1 + apps/server/src/v1/monitors/results/get.ts | 120 + apps/server/src/v1/monitors/trigger/post.ts | 192 ++ apps/web/src/app/api/checker/cron/_cron.ts | 2 +- apps/web/src/app/api/checker/schema.ts | 31 - .../src/app/api/checker/test/http/route.ts | 2 +- apps/web/src/app/api/checker/test/route.ts | 2 +- .../marketing/pricing/pricing-table.tsx | 18 +- apps/web/src/config/pricing-table.ts | 12 +- packages/db/drizzle/0038_foamy_stardust.sql | 9 + packages/db/drizzle/meta/0038_snapshot.json | 2277 +++++++++++++++++ packages/db/drizzle/meta/_journal.json | 7 + packages/db/src/schema/index.ts | 1 + packages/db/src/schema/monitor_run/index.ts | 1 + .../db/src/schema/monitor_run/monitor_run.ts | 17 + packages/db/src/schema/plan/config.ts | 12 +- packages/db/src/schema/plan/schema.ts | 2 +- .../get_result_for_on_demand_check_http.pipe | 23 + packages/tinybird/src/os-client.ts | 47 + packages/utils/index.ts | 31 + packages/utils/package.json | 4 +- pnpm-lock.yaml | 26 +- 23 files changed, 2780 insertions(+), 67 deletions(-) create mode 100644 apps/server/src/v1/monitors/results/get.ts create mode 100644 apps/server/src/v1/monitors/trigger/post.ts delete mode 100644 apps/web/src/app/api/checker/schema.ts create mode 100644 packages/db/drizzle/0038_foamy_stardust.sql create mode 100644 packages/db/drizzle/meta/0038_snapshot.json create mode 100644 packages/db/src/schema/monitor_run/index.ts create mode 100644 packages/db/src/schema/monitor_run/monitor_run.ts create mode 100644 packages/tinybird/pipes/get_result_for_on_demand_check_http.pipe diff --git a/apps/checker/handlers/checker.go b/apps/checker/handlers/checker.go index ec4afd46..40807e5a 100644 --- a/apps/checker/handlers/checker.go +++ b/apps/checker/handlers/checker.go @@ -31,6 +31,7 @@ type PingData struct { Headers string `json:"headers,omitempty"` Assertions string `json:"assertions"` Body string `json:"body,omitempty"` + Trigger string `json:"trigger,omitempty"` Latency int64 `json:"latency"` CronTimestamp int64 `json:"cronTimestamp"` Timestamp int64 `json:"timestamp"` @@ -81,7 +82,13 @@ func (h Handler) HTTPCheckerHandler(c *gin.Context) { assertionAsString = "" } + var trigger = "cron" + if req.Trigger != "" { + trigger = req.Trigger + } + var called int + op := func() error { called++ res, err := checker.Http(ctx, requestClient, req) @@ -113,6 +120,7 @@ func (h Handler) HTTPCheckerHandler(c *gin.Context) { Timing: string(timingAsString), Headers: string(headersAsString), Body: string(res.Body), + Trigger: trigger, } statusCode := statusCode(res.Status) @@ -277,6 +285,7 @@ func (h Handler) HTTPCheckerHandler(c *gin.Context) { Error: 1, Assertions: assertionAsString, Body: "", + Trigger: trigger, }, dataSourceName); err != nil { log.Ctx(ctx).Error().Err(err).Msg("failed to send event to tinybird") } @@ -294,5 +303,4 @@ func (h Handler) HTTPCheckerHandler(c *gin.Context) { } c.JSON(http.StatusOK, gin.H{"message": "ok"}) - } diff --git a/apps/checker/request/request.go b/apps/checker/request/request.go index 1bb338f5..66d7f7d5 100644 --- a/apps/checker/request/request.go +++ b/apps/checker/request/request.go @@ -56,6 +56,7 @@ type HttpCheckerRequest struct { Method string `json:"method"` Status string `json:"status"` Body string `json:"body"` + Trigger string `json:"trigger,omitempty"` RawAssertions []json.RawMessage `json:"assertions,omitempty"` CronTimestamp int64 `json:"cronTimestamp"` Timeout int64 `json:"timeout"` diff --git a/apps/server/src/v1/monitors/results/get.ts b/apps/server/src/v1/monitors/results/get.ts new file mode 100644 index 00000000..1fa37c75 --- /dev/null +++ b/apps/server/src/v1/monitors/results/get.ts @@ -0,0 +1,120 @@ +import { createRoute, z } from "@hono/zod-openapi"; + +import { and, db, eq, isNull } from "@openstatus/db"; +import { monitor, monitorRun } from "@openstatus/db/src/schema"; +import { OSTinybird } from "@openstatus/tinybird"; + +import { flyRegions } from "@openstatus/db/src/schema/constants"; +import { HTTPException } from "hono/http-exception"; +import { env } from "../../../env"; +import { openApiErrorResponses } from "../../../libs/errors/openapi-error-responses"; +import type { monitorsApi } from "../index"; +import { ParamsSchema } from "../schema"; + +const tb = new OSTinybird({ token: env.TINY_BIRD_API_KEY }); + +const timingSchema = z.object({ + dnsStart: z.number(), + dnsDone: z.number(), + connectStart: z.number(), + connectDone: z.number(), + tlsHandshakeStart: z.number(), + tlsHandshakeDone: z.number(), + firstByteStart: z.number(), + firstByteDone: z.number(), + transferStart: z.number(), + transferDone: z.number(), +}); + +const getMonitorStats = createRoute({ + method: "get", + tags: ["monitor"], + description: "Get a monitor result", + path: "/:id/result/:resultId", + request: { + params: ParamsSchema.extend({ + resultId: z.number().int().openapi({ + description: "The id of the result", + }), + }), + }, + responses: { + 200: { + content: { + "application/json": { + schema: z.object({ + data: z.object({ + latency: z.number().int(), // in ms + statusCode: z.number().int().nullable().default(null), + monitorId: z.string().default(""), + url: z.string().url().optional(), + error: z + .number() + .default(0) + .transform((val) => val !== 0), + region: z.enum(flyRegions), + timestamp: z.number().int().optional(), + message: z.string().nullable().optional(), + timing: z + .string() + .nullable() + .optional() + .transform((val) => { + if (!val) return null; + const value = timingSchema.safeParse(JSON.parse(val)); + if (value.success) return value.data; + return null; + }), + }), + }), + }, + }, + description: "All the metrics for the monitor", + }, + ...openApiErrorResponses, + }, +}); + +export function registerGetMonitorSummary(api: typeof monitorsApi) { + return api.openapi(getMonitorStats, async (c) => { + const workspaceId = c.get("workspaceId"); + const { id, resultId } = c.req.valid("param"); + + const _monitorRun = await db + .select() + .from(monitorRun) + .where( + and( + eq(monitorRun.id, Number(resultId)), + eq(monitorRun.monitorId, Number(id)), + eq(monitorRun.workspaceId, Number(workspaceId)), + ), + ) + .get(); + + if (!_monitorRun || !_monitorRun?.runnedAt) { + throw new HTTPException(404, { message: "Not Found" }); + } + + const _monitor = await db + .select() + .from(monitor) + .where(eq(monitor.id, Number(id))) + .get(); + + if (!_monitor) { + throw new HTTPException(404, { message: "Not Found" }); + } + // Fetch result from tb pipe + const data = await tb.getResultForOnDemandCheckHttp()({ + monitorId: _monitor.id, + timestamp: _monitorRun.runnedAt?.getTime(), + url: _monitor.url, + }); + // return array of results + if (!data) { + throw new HTTPException(404, { message: "Not Found" }); + } + return c.json({ data }, 200); + }); +} diff --git a/apps/server/src/v1/monitors/trigger/post.ts b/apps/server/src/v1/monitors/trigger/post.ts new file mode 100644 index 00000000..7b251a33 --- /dev/null +++ b/apps/server/src/v1/monitors/trigger/post.ts @@ -0,0 +1,192 @@ +import { createRoute, z } from "@hono/zod-openapi"; +import { and, eq, gte, isNull, sql } from "@openstatus/db"; +import { db } from "@openstatus/db/src/db"; +import { monitorRun } from "@openstatus/db/src/schema"; +import { monitorStatusTable } from "@openstatus/db/src/schema/monitor_status/monitor_status"; +import { selectMonitorStatusSchema } from "@openstatus/db/src/schema/monitor_status/validation"; +import { monitor } from "@openstatus/db/src/schema/monitors/monitor"; +import { selectMonitorSchema } from "@openstatus/db/src/schema/monitors/validation"; +import { getLimit } from "@openstatus/db/src/schema/plan/utils"; +import type { httpPayloadSchema, tpcPayloadSchema } from "@openstatus/utils"; +import { HTTPException } from "hono/http-exception"; +import type { monitorsApi } from ".."; +import { env } from "../../../env"; +import { openApiErrorResponses } from "../../../libs/errors/openapi-error-responses"; +import { ParamsSchema } from "../schema"; +const triggerMonitor = createRoute({ + method: "get", + tags: ["monitor"], + description: "Trigger a monitor check", + path: "/:id/trigger", + request: { + params: ParamsSchema, + }, + responses: { + 200: { + content: { + "application/json": { + schema: z.object({ + resultId: z + .number() + .openapi({ description: "the id of your check result" }), + }), + }, + }, + description: "All the historical metrics", + }, + ...openApiErrorResponses, + }, +}); + +export function registerTriggerMonitor(api: typeof monitorsApi) { + return api.openapi(triggerMonitor, async (c) => { + const workspaceId = c.get("workspaceId"); + const { id } = c.req.valid("param"); + const limits = c.get("limits"); + + const lastMonth = new Date().setMonth(new Date().getMonth() - 1); + + const count = ( + await db + .select({ count: sql`count(*)` }) + .from(monitorRun) + .where( + and( + eq(monitorRun.workspaceId, Number(workspaceId)), + gte(monitorRun.createdAt, new Date(lastMonth)), + ), + ) + .all() + )[0].count; + + if (count >= getLimit(limits, "synthetic-checks")) { + throw new HTTPException(403, { + message: "Upgrade for more checks", + }); + } + + const monitorData = await db + .select() + .from(monitor) + .where( + and( + eq(monitor.id, Number(id)), + eq(monitor.workspaceId, Number(workspaceId)), + isNull(monitor.deletedAt), + ), + ) + .get(); + + if (!monitorData) { + throw new HTTPException(404, { message: "Not Found" }); + } + + const parseMonitor = selectMonitorSchema.safeParse(monitorData); + + if (!parseMonitor.success) { + throw new HTTPException(400, { message: "Something went wrong" }); + } + + const row = parseMonitor.data; + + // Maybe later overwrite the region + + const monitorStatusData = await db + .select() + .from(monitorStatusTable) + .where(eq(monitorStatusTable.monitorId, monitorData.id)) + .all(); + + const monitorStatus = z + .array(selectMonitorStatusSchema) + .safeParse(monitorStatusData); + if (!monitorStatus.success) { + throw new HTTPException(400, { message: "Something went wrong" }); + } + + const timestamp = Date.now(); + + const newRun = await db + .insert(monitorRun) + .values({ + monitorId: row.id, + workspaceId: row.workspaceId, + runnedAt: new Date(timestamp), + }) + .returning(); + + if (!newRun[0]) { + throw new HTTPException(400, { message: "Something went wrong" }); + } + + const allResult = []; + for (const region of monitorData.regions) { + const status = + monitorStatus.data.find((m) => region === m.region)?.status || "active"; + // Trigger the monitor + + let payload: + | z.infer + | z.infer + | null = null; + // + if (row.jobType === "http") { + payload = { + workspaceId: String(row.workspaceId), + monitorId: String(row.id), + url: row.url, + method: row.method || "GET", + cronTimestamp: timestamp, + body: row.body, + headers: row.headers, + status: status, + assertions: row.assertions ? JSON.parse(row.assertions) : null, + degradedAfter: row.degradedAfter, + timeout: row.timeout, + }; + } + if (row.jobType === "tcp") { + payload = { + workspaceId: String(row.workspaceId), + monitorId: String(row.id), + url: row.url, + status: status, + assertions: row.assertions ? JSON.parse(row.assertions) : null, + cronTimestamp: timestamp, + degradedAfter: row.degradedAfter, + timeout: row.timeout, + }; + } + + if (!payload) { + throw new Error("Invalid jobType"); + } + const url = generateUrl({ row }); + const result = fetch(url, { + headers: { + "Content-Type": "application/json", + "fly-prefer-region": region, // Specify the region you want the request to be sent to + Authorization: `Basic ${env.CRON_SECRET}`, + }, + method: "POST", + body: JSON.stringify(payload), + }); + allResult.push(result); + } + + await Promise.all(allResult); + + return c.json({ resultId: newRun[0].id }, 200); + }); +} + +function generateUrl({ row }: { row: z.infer }) { + switch (row.jobType) { + case "http": + return `https://openstatus-checker.fly.dev/checker/http?monitor_id=${row.id}&trigger=api`; + case "tcp": + return `https://openstatus-checker.fly.dev/checker/tcp?monitor_id=${row.id}&trigger=api`; + default: + throw new Error("Invalid jobType"); + } +} diff --git a/apps/web/src/app/api/checker/cron/_cron.ts b/apps/web/src/app/api/checker/cron/_cron.ts index d97e4bfc..6ca1daf8 100644 --- a/apps/web/src/app/api/checker/cron/_cron.ts +++ b/apps/web/src/app/api/checker/cron/_cron.ts @@ -15,7 +15,7 @@ import { } from "@openstatus/db/src/schema"; import { env } from "@/env"; -import type { httpPayloadSchema, tpcPayloadSchema } from "../schema"; +import type { httpPayloadSchema, tpcPayloadSchema } from "@openstatus/utils"; const periodicityAvailable = selectMonitorSchema.pick({ periodicity: true }); diff --git a/apps/web/src/app/api/checker/schema.ts b/apps/web/src/app/api/checker/schema.ts deleted file mode 100644 index 17a255e9..00000000 --- a/apps/web/src/app/api/checker/schema.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { z } from "zod"; - -import { base } from "@openstatus/assertions"; -import { monitorMethods, monitorStatus } from "@openstatus/db/src/schema"; - -export const httpPayloadSchema = z.object({ - workspaceId: z.string(), - monitorId: z.string(), - method: z.enum(monitorMethods), - body: z.string().optional(), - headers: z.array(z.object({ key: z.string(), value: z.string() })).optional(), - url: z.string(), - cronTimestamp: z.number(), - status: z.enum(monitorStatus), - assertions: z.array(base).nullable(), - timeout: z.number().default(45000), - degradedAfter: z.number().nullable(), -}); - -export type HttpPayload = z.infer; - -export const tpcPayloadSchema = z.object({ - status: z.enum(monitorStatus), - workspaceId: z.string(), - url: z.string(), - monitorId: z.string(), - assertions: z.array(base).nullable(), - cronTimestamp: z.number(), - timeout: z.number().default(45000), - degradedAfter: z.number().nullable(), -}); diff --git a/apps/web/src/app/api/checker/test/http/route.ts b/apps/web/src/app/api/checker/test/http/route.ts index c65e4184..028ba043 100644 --- a/apps/web/src/app/api/checker/test/http/route.ts +++ b/apps/web/src/app/api/checker/test/http/route.ts @@ -4,7 +4,7 @@ import { z } from "zod"; import { monitorFlyRegionSchema } from "@openstatus/db/src/schema/constants"; import { checkRegion } from "@/components/ping-response-analysis/utils"; -import { httpPayloadSchema } from "../../schema"; +import { httpPayloadSchema } from "@openstatus/utils"; import { isAnInvalidTestUrl } from "../../utils"; export const runtime = "edge"; diff --git a/apps/web/src/app/api/checker/test/route.ts b/apps/web/src/app/api/checker/test/route.ts index 180a43d6..d5fc8d40 100644 --- a/apps/web/src/app/api/checker/test/route.ts +++ b/apps/web/src/app/api/checker/test/route.ts @@ -4,7 +4,7 @@ import { z } from "zod"; import { monitorFlyRegionSchema } from "@openstatus/db/src/schema/constants"; import { checkRegion } from "@/components/ping-response-analysis/utils"; -import { httpPayloadSchema } from "../schema"; +import { httpPayloadSchema } from "@openstatus/utils"; import { isAnInvalidTestUrl } from "../utils"; export const runtime = "edge"; diff --git a/apps/web/src/components/marketing/pricing/pricing-table.tsx b/apps/web/src/components/marketing/pricing/pricing-table.tsx index 4cc08369..985c6f01 100644 --- a/apps/web/src/components/marketing/pricing/pricing-table.tsx +++ b/apps/web/src/components/marketing/pricing/pricing-table.tsx @@ -112,7 +112,7 @@ export function PricingTable({ {label} - {features.map(({ label, value, badge }, _i) => { + {features.map(({ label, value, badge, monthly }, _i) => { return ( @@ -122,7 +122,8 @@ export function PricingTable({ ) : null} {selectedPlans.map((plan, _i) => { - const limitValue = plan.limits[value]; + const limitValue = + plan.limits[value as keyof typeof plan.limits]; function renderContent() { if (typeof limitValue === "boolean") { if (limitValue) { @@ -137,13 +138,9 @@ export function PricingTable({ ); } if (typeof limitValue === "number") { - return ( - - {new Intl.NumberFormat("us") - .format(limitValue) - .toString()} - - ); + return new Intl.NumberFormat("us") + .format(limitValue) + .toString(); } if ( Array.isArray(limitValue) && @@ -159,11 +156,12 @@ export function PricingTable({ // biome-ignore lint/suspicious/noArrayIndexKey: key={key + value + _i} className={cn( - "p-3", + "p-3 font-mono", plan.key === "team" && "bg-muted/30", )} > {renderContent()} + {monthly ? "/mo" : ""} ); })} diff --git a/apps/web/src/config/pricing-table.ts b/apps/web/src/config/pricing-table.ts index 4713a688..86bd1184 100644 --- a/apps/web/src/config/pricing-table.ts +++ b/apps/web/src/config/pricing-table.ts @@ -1,10 +1,15 @@ -import type { LimitsV2 } from "@openstatus/db/src/schema/plan/schema"; +import type { LimitsV1, LimitsV2 } from "@openstatus/db/src/schema/plan/schema"; export const pricingTableConfig: Record< string, { label: string; - features: { value: keyof LimitsV2; label: string; badge?: string }[]; + features: { + value: keyof LimitsV1 | keyof LimitsV2; + label: string; + badge?: string; + monthly?: boolean; + }[]; } > = { monitors: { @@ -32,7 +37,8 @@ export const pricingTableConfig: Record< features: [ { value: "synthetic-checks", - label: "Number of synthetic API checks", + label: "Number of on-demand checks", + monthly: true, }, { value: "private-locations", label: "Private Locations" }, ], diff --git a/packages/db/drizzle/0038_foamy_stardust.sql b/packages/db/drizzle/0038_foamy_stardust.sql new file mode 100644 index 00000000..a3e86674 --- /dev/null +++ b/packages/db/drizzle/0038_foamy_stardust.sql @@ -0,0 +1,9 @@ +CREATE TABLE `monitor_run` ( + `id` integer PRIMARY KEY NOT NULL, + `workspace_id` integer, + `monitor_id` integer, + `runned_at` integer, + `created_at` integer DEFAULT (strftime('%s', 'now')), + FOREIGN KEY (`workspace_id`) REFERENCES `workspace`(`id`) ON UPDATE no action ON DELETE no action, + FOREIGN KEY (`monitor_id`) REFERENCES `monitor`(`id`) ON UPDATE no action ON DELETE no action +); diff --git a/packages/db/drizzle/meta/0038_snapshot.json b/packages/db/drizzle/meta/0038_snapshot.json new file mode 100644 index 00000000..ef8c986f --- /dev/null +++ b/packages/db/drizzle/meta/0038_snapshot.json @@ -0,0 +1,2277 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "e284ec98-e715-4599-9f79-ecbf022ca7a2", + "prevId": "09d93b56-dcc3-4148-934a-a490ab04bab9", + "tables": { + "status_report_to_monitors": { + "name": "status_report_to_monitors", + "columns": { + "monitor_id": { + "name": "monitor_id", + "type": "integer", + "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'))" + } + }, + "indexes": {}, + "foreignKeys": { + "status_report_to_monitors_monitor_id_monitor_id_fk": { + "name": "status_report_to_monitors_monitor_id_monitor_id_fk", + "tableFrom": "status_report_to_monitors", + "tableTo": "monitor", + "columnsFrom": [ + "monitor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "status_report_to_monitors_status_report_id_status_report_id_fk": { + "name": "status_report_to_monitors_status_report_id_status_report_id_fk", + "tableFrom": "status_report_to_monitors", + "tableTo": "status_report", + "columnsFrom": [ + "status_report_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "status_report_to_monitors_monitor_id_status_report_id_pk": { + "columns": [ + "monitor_id", + "status_report_id" + ], + "name": "status_report_to_monitors_monitor_id_status_report_id_pk" + } + }, + "uniqueConstraints": {} + }, + "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": {}, + "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": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "status_report_update": { + "name": "status_report_update", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text(4)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "date": { + "name": "date", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "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": {}, + "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": {} + }, + "integration": { + "name": "integration", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text(256)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "credential": { + "name": "credential", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "(strftime('%s', 'now'))" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "(strftime('%s', 'now'))" + }, + "data": { + "name": "data", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "integration_workspace_id_workspace_id_fk": { + "name": "integration_workspace_id_workspace_id_fk", + "tableFrom": "integration", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "page": { + "name": "page", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "icon": { + "name": "icon", + "type": "text(256)", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "''" + }, + "slug": { + "name": "slug", + "type": "text(256)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "custom_domain": { + "name": "custom_domain", + "type": "text(256)", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "published": { + "name": "published", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": false + }, + "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 + }, + "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 + } + }, + "foreignKeys": { + "page_workspace_id_workspace_id_fk": { + "name": "page_workspace_id_workspace_id_fk", + "tableFrom": "page", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "monitor": { + "name": "monitor", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "job_type": { + "name": "job_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'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": "''" + }, + "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 + }, + "public": { + "name": "public", + "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'))" + }, + "deleted_at": { + "name": "deleted_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": { + "monitor_workspace_id_workspace_id_fk": { + "name": "monitor_workspace_id_workspace_id_fk", + "tableFrom": "monitor", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "monitors_to_pages": { + "name": "monitors_to_pages", + "columns": { + "monitor_id": { + "name": "monitor_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "page_id": { + "name": "page_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "(strftime('%s', 'now'))" + }, + "order": { + "name": "order", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + } + }, + "indexes": {}, + "foreignKeys": { + "monitors_to_pages_monitor_id_monitor_id_fk": { + "name": "monitors_to_pages_monitor_id_monitor_id_fk", + "tableFrom": "monitors_to_pages", + "tableTo": "monitor", + "columnsFrom": [ + "monitor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "monitors_to_pages_page_id_page_id_fk": { + "name": "monitors_to_pages_page_id_page_id_fk", + "tableFrom": "monitors_to_pages", + "tableTo": "page", + "columnsFrom": [ + "page_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "monitors_to_pages_monitor_id_page_id_pk": { + "columns": [ + "monitor_id", + "page_id" + ], + "name": "monitors_to_pages_monitor_id_page_id_pk" + } + }, + "uniqueConstraints": {} + }, + "workspace": { + "name": "workspace", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "stripe_id": { + "name": "stripe_id", + "type": "text(256)", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "subscription_id": { + "name": "subscription_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "plan": { + "name": "plan", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "ends_at": { + "name": "ends_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "paid_until": { + "name": "paid_until", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "limits": { + "name": "limits", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'{}'" + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "(strftime('%s', 'now'))" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "(strftime('%s', 'now'))" + }, + "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_id_dsn_unique": { + "name": "workspace_id_dsn_unique", + "columns": [ + "id", + "dsn" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "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": {} + }, + "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": {}, + "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": {} + }, + "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'))" + } + }, + "indexes": { + "user_tenant_id_unique": { + "name": "user_tenant_id_unique", + "columns": [ + "tenant_id" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "users_to_workspaces": { + "name": "users_to_workspaces", + "columns": { + "user_id": { + "name": "user_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "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": {}, + "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": {} + }, + "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": {} + }, + "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": true, + "autoincrement": false + }, + "page_id": { + "name": "page_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "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 + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "(strftime('%s', 'now'))" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "(strftime('%s', 'now'))" + } + }, + "indexes": {}, + "foreignKeys": { + "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": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "notification": { + "name": "notification", + "columns": { + "id": { + "name": "id", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "data": { + "name": "data", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "'{}'" + }, + "workspace_id": { + "name": "workspace_id", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "(strftime('%s', 'now'))" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "(strftime('%s', 'now'))" + } + }, + "indexes": {}, + "foreignKeys": { + "notification_workspace_id_workspace_id_fk": { + "name": "notification_workspace_id_workspace_id_fk", + "tableFrom": "notification", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "notifications_to_monitors": { + "name": "notifications_to_monitors", + "columns": { + "monitor_id": { + "name": "monitor_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "notification_id": { + "name": "notification_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "(strftime('%s', 'now'))" + } + }, + "indexes": {}, + "foreignKeys": { + "notifications_to_monitors_monitor_id_monitor_id_fk": { + "name": "notifications_to_monitors_monitor_id_monitor_id_fk", + "tableFrom": "notifications_to_monitors", + "tableTo": "monitor", + "columnsFrom": [ + "monitor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "notifications_to_monitors_notification_id_notification_id_fk": { + "name": "notifications_to_monitors_notification_id_notification_id_fk", + "tableFrom": "notifications_to_monitors", + "tableTo": "notification", + "columnsFrom": [ + "notification_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "notifications_to_monitors_monitor_id_notification_id_pk": { + "columns": [ + "monitor_id", + "notification_id" + ], + "name": "notifications_to_monitors_monitor_id_notification_id_pk" + } + }, + "uniqueConstraints": {} + }, + "monitor_status": { + "name": "monitor_status", + "columns": { + "monitor_id": { + "name": "monitor_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "region": { + "name": "region", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'active'" + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "(strftime('%s', 'now'))" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "(strftime('%s', 'now'))" + } + }, + "indexes": { + "monitor_status_idx": { + "name": "monitor_status_idx", + "columns": [ + "monitor_id", + "region" + ], + "isUnique": false + } + }, + "foreignKeys": { + "monitor_status_monitor_id_monitor_id_fk": { + "name": "monitor_status_monitor_id_monitor_id_fk", + "tableFrom": "monitor_status", + "tableTo": "monitor", + "columnsFrom": [ + "monitor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "monitor_status_monitor_id_region_pk": { + "columns": [ + "monitor_id", + "region" + ], + "name": "monitor_status_monitor_id_region_pk" + } + }, + "uniqueConstraints": {} + }, + "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": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "incident": { + "name": "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": { + "incident_monitor_id_started_at_unique": { + "name": "incident_monitor_id_started_at_unique", + "columns": [ + "monitor_id", + "started_at" + ], + "isUnique": true + } + }, + "foreignKeys": { + "incident_monitor_id_monitor_id_fk": { + "name": "incident_monitor_id_monitor_id_fk", + "tableFrom": "incident", + "tableTo": "monitor", + "columnsFrom": [ + "monitor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set default", + "onUpdate": "no action" + }, + "incident_workspace_id_workspace_id_fk": { + "name": "incident_workspace_id_workspace_id_fk", + "tableFrom": "incident", + "tableTo": "workspace", + "columnsFrom": [ + "workspace_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "incident_acknowledged_by_user_id_fk": { + "name": "incident_acknowledged_by_user_id_fk", + "tableFrom": "incident", + "tableTo": "user", + "columnsFrom": [ + "acknowledged_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + }, + "incident_resolved_by_user_id_fk": { + "name": "incident_resolved_by_user_id_fk", + "tableFrom": "incident", + "tableTo": "user", + "columnsFrom": [ + "resolved_by" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "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": {}, + "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": {} + }, + "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": {}, + "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": {} + }, + "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 + } + }, + "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": {} + }, + "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": {}, + "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": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {} + }, + "maintenance_to_monitor": { + "name": "maintenance_to_monitor", + "columns": { + "monitor_id": { + "name": "monitor_id", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "maintenance_id": { + "name": "maintenance_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": { + "maintenance_to_monitor_monitor_id_monitor_id_fk": { + "name": "maintenance_to_monitor_monitor_id_monitor_id_fk", + "tableFrom": "maintenance_to_monitor", + "tableTo": "monitor", + "columnsFrom": [ + "monitor_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "maintenance_to_monitor_maintenance_id_maintenance_id_fk": { + "name": "maintenance_to_monitor_maintenance_id_maintenance_id_fk", + "tableFrom": "maintenance_to_monitor", + "tableTo": "maintenance", + "columnsFrom": [ + "maintenance_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "maintenance_to_monitor_monitor_id_maintenance_id_pk": { + "columns": [ + "maintenance_id", + "monitor_id" + ], + "name": "maintenance_to_monitor_monitor_id_maintenance_id_pk" + } + }, + "uniqueConstraints": {} + }, + "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": {}, + "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": {} + }, + "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": {}, + "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": {} + } + }, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": {} + } +} \ No newline at end of file diff --git a/packages/db/drizzle/meta/_journal.json b/packages/db/drizzle/meta/_journal.json index e2be5b21..bd257299 100644 --- a/packages/db/drizzle/meta/_journal.json +++ b/packages/db/drizzle/meta/_journal.json @@ -267,6 +267,13 @@ "when": 1729533101998, "tag": "0037_equal_beyonder", "breakpoints": true + }, + { + "idx": 38, + "version": "6", + "when": 1729579461221, + "tag": "0038_foamy_stardust", + "breakpoints": true } ] } \ No newline at end of file diff --git a/packages/db/src/schema/index.ts b/packages/db/src/schema/index.ts index 759a140d..1c6fea21 100644 --- a/packages/db/src/schema/index.ts +++ b/packages/db/src/schema/index.ts @@ -14,3 +14,4 @@ export * from "./monitor_tags"; export * from "./applications"; export * from "./maintenances"; export * from "./check"; +export * from "./monitor_run"; diff --git a/packages/db/src/schema/monitor_run/index.ts b/packages/db/src/schema/monitor_run/index.ts new file mode 100644 index 00000000..c8ccc3ff --- /dev/null +++ b/packages/db/src/schema/monitor_run/index.ts @@ -0,0 +1 @@ +export * from "./monitor_run"; diff --git a/packages/db/src/schema/monitor_run/monitor_run.ts b/packages/db/src/schema/monitor_run/monitor_run.ts new file mode 100644 index 00000000..c382251d --- /dev/null +++ b/packages/db/src/schema/monitor_run/monitor_run.ts @@ -0,0 +1,17 @@ +import { sql } from "drizzle-orm"; +import { integer, sqliteTable } from "drizzle-orm/sqlite-core"; +import { monitor } from "../monitors"; +import { workspace } from "../workspaces/workspace"; + +export const monitorRun = sqliteTable("monitor_run", { + id: integer("id").primaryKey(), + + workspaceId: integer("workspace_id").references(() => workspace.id), + monitorId: integer("monitor_id").references(() => monitor.id), + + runnedAt: integer("runned_at", { mode: "timestamp" }), + + createdAt: integer("created_at", { mode: "timestamp" }).default( + sql`(strftime('%s', 'now'))`, + ), +}); diff --git a/packages/db/src/schema/plan/config.ts b/packages/db/src/schema/plan/config.ts index 25d4d3d5..365c5c4d 100644 --- a/packages/db/src/schema/plan/config.ts +++ b/packages/db/src/schema/plan/config.ts @@ -1,5 +1,5 @@ import type { WorkspacePlan } from "../workspaces/validation"; -import type { Limits, LimitsV1, LimitsV2 } from "./schema"; +import type { Limits } from "./schema"; // TODO: rename to `planConfig` export const allPlans: Record< @@ -8,7 +8,7 @@ export const allPlans: Record< title: "Hobby" | "Starter" | "Growth" | "Pro"; description: string; price: number; - limits: Limits & { "private-locations": boolean }; + limits: Limits; } > = { free: { @@ -17,7 +17,7 @@ export const allPlans: Record< price: 0, limits: { monitors: 1, - "synthetic-checks": 1000, + "synthetic-checks": 30, periodicity: ["10m", "30m", "1h"], "multi-region": true, "max-regions": 6, @@ -44,7 +44,7 @@ export const allPlans: Record< price: 30, limits: { monitors: 5, - "synthetic-checks": 10000, + "synthetic-checks": 100, periodicity: ["1m", "5m", "10m", "30m", "1h"], "multi-region": true, "max-regions": 35, @@ -107,7 +107,7 @@ export const allPlans: Record< price: 100, limits: { monitors: 15, - "synthetic-checks": 50000, + "synthetic-checks": 300, periodicity: ["30s", "1m", "5m", "10m", "30m", "1h"], "multi-region": true, "max-regions": 35, @@ -170,7 +170,7 @@ export const allPlans: Record< price: 300, limits: { monitors: 50, - "synthetic-checks": 150000, + "synthetic-checks": 500, periodicity: ["30s", "1m", "5m", "10m", "30m", "1h"], "multi-region": true, "max-regions": 35, diff --git a/packages/db/src/schema/plan/schema.ts b/packages/db/src/schema/plan/schema.ts index fdd353a5..f14407d9 100644 --- a/packages/db/src/schema/plan/schema.ts +++ b/packages/db/src/schema/plan/schema.ts @@ -37,7 +37,7 @@ export const limitsV2 = limitsV1.extend({ export type LimitsV2 = z.infer; -const unknownLimit = z.discriminatedUnion("version", [limitsV1]); +const unknownLimit = z.discriminatedUnion("version", [limitsV1, limitsV2]); export function migrateFromV1ToV2({ data }: { data: LimitsV1 }) { return { diff --git a/packages/tinybird/pipes/get_result_for_on_demand_check_http.pipe b/packages/tinybird/pipes/get_result_for_on_demand_check_http.pipe new file mode 100644 index 00000000..1fa29f76 --- /dev/null +++ b/packages/tinybird/pipes/get_result_for_on_demand_check_http.pipe @@ -0,0 +1,23 @@ +VERSION 0 + +NODE get_result_for_on_demand_check_http_0 +SQL > + + % + SELECT + latency, + monitorId, + error, + region, + statusCode, + timestamp, + url, + workspaceId, + timing + FROM __ttl_45d_all_mv__v1 + where + cronTimestamp = {{ String(cronTimestamp, '1') }} + and monitorId = {{ String(monitorId, '1') }} + and url = {{ String(url , 'https://www.openstatus.dev') }} + + diff --git a/packages/tinybird/src/os-client.ts b/packages/tinybird/src/os-client.ts index e866af32..ae3f7bc7 100644 --- a/packages/tinybird/src/os-client.ts +++ b/packages/tinybird/src/os-client.ts @@ -460,6 +460,53 @@ export class OSTinybird { } }; } + + getResultForOnDemandCheckHttp() { + const parameters = z.object({ + monitorId: z.number().int(), + timestamp: z.number(), + url: z.string(), + }); + return async (props: z.infer) => { + try { + const res = await this.tb.buildPipe({ + pipe: "get_result_for_on_demand_check_http", + parameters, + data: z.object({ + latency: z.number().int(), // in ms + statusCode: z.number().int().nullable().default(null), + monitorId: z.string().default(""), + url: z.string().url().optional(), + error: z + .number() + .default(0) + .transform((val) => val !== 0), + region: z.enum(flyRegions), + timestamp: z.number().int().optional(), + message: z.string().nullable().optional(), + timing: z + .string() + .nullable() + .optional() + .transform((val) => { + if (!val) return null; + const value = timingSchema.safeParse(JSON.parse(val)); + if (value.success) return value.data; + return null; + }), + }), + opts: { + next: { + revalidate: MIN_CACHE, + }, + }, + })(props); + return res.data[0]; + } catch (e) { + console.error(e); + } + }; + } } /** diff --git a/packages/utils/index.ts b/packages/utils/index.ts index 845b4176..875f9973 100644 --- a/packages/utils/index.ts +++ b/packages/utils/index.ts @@ -5,6 +5,10 @@ import type { MonitorFlyRegion } from "@openstatus/db/src/schema/constants"; +import { base } from "@openstatus/assertions"; +import { monitorMethods, monitorStatus } from "@openstatus/db/src/schema"; + +import { z } from "zod"; // export const vercelRegionsDict = { // /** // * A random location will be chosen @@ -428,3 +432,30 @@ export const vercelRegions = [ // export const availableRegions = [...vercelRegions, ...flyRegions] as const; // export const regionsDict = { ...vercelRegionsDict, ...flyRegionsDict } as const; + +export const httpPayloadSchema = z.object({ + workspaceId: z.string(), + monitorId: z.string(), + method: z.enum(monitorMethods), + body: z.string().optional(), + headers: z.array(z.object({ key: z.string(), value: z.string() })).optional(), + url: z.string(), + cronTimestamp: z.number(), + status: z.enum(monitorStatus), + assertions: z.array(base).nullable(), + timeout: z.number().default(45000), + degradedAfter: z.number().nullable(), +}); + +export type HttpPayload = z.infer; + +export const tpcPayloadSchema = z.object({ + status: z.enum(monitorStatus), + workspaceId: z.string(), + url: z.string(), + monitorId: z.string(), + assertions: z.array(base).nullable(), + cronTimestamp: z.number(), + timeout: z.number().default(45000), + degradedAfter: z.number().nullable(), +}); diff --git a/packages/utils/package.json b/packages/utils/package.json index 83c9cc4e..5f965fb3 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -6,9 +6,11 @@ "scripts": {}, "dependencies": {}, "devDependencies": { + "@openstatus/assertions": "workspace:*", "@openstatus/db": "workspace:*", "@openstatus/tsconfig": "workspace:*", - "typescript": "5.5.2" + "typescript": "5.5.2", + "zod": "3.23.8" }, "keywords": [], "author": "", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 36e915f4..ced1fa6f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -124,7 +124,7 @@ importers: version: link:../../packages/utils '@scalar/hono-api-reference': specifier: 0.5.131 - version: 0.5.131(postcss@8.4.38)(storybook@8.2.1(@babel/preset-env@7.24.8(@babel/core@7.24.8))(bufferutil@4.0.8)(utf-8-validate@6.0.4))(tailwindcss@3.4.3(ts-node@10.9.2(@types/node@20.8.0)(typescript@5.5.2)))(typescript@5.5.2) + version: 0.5.131(postcss@8.4.38)(storybook@8.2.1(@babel/preset-env@7.24.8(@babel/core@7.24.8))(bufferutil@4.0.8)(utf-8-validate@6.0.4))(tailwindcss@3.4.3(ts-node@10.9.2(@types/node@20.14.8)(typescript@5.5.2)))(typescript@5.5.2) '@t3-oss/env-core': specifier: 0.7.1 version: 0.7.1(typescript@5.5.2)(zod@3.23.8) @@ -1084,6 +1084,9 @@ importers: packages/utils: devDependencies: + '@openstatus/assertions': + specifier: workspace:* + version: link:../assertions '@openstatus/db': specifier: workspace:* version: link:../db @@ -1093,6 +1096,9 @@ importers: typescript: specifier: 5.5.2 version: 5.5.2 + zod: + specifier: 3.23.8 + version: 3.23.8 packages: @@ -2247,9 +2253,11 @@ packages: '@esbuild-kit/core-utils@3.3.2': resolution: {integrity: sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==} + deprecated: 'Merged into tsx: https://tsx.is' '@esbuild-kit/esm-loader@2.6.5': resolution: {integrity: sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==} + deprecated: 'Merged into tsx: https://tsx.is' '@esbuild-plugins/node-resolve@0.1.4': resolution: {integrity: sha512-haFQ0qhxEpqtWWY0kx1Y5oE3sMyO1PcoSiWEPrAw6tm/ZOOLXjSs6Q+v1v9eyuVF0nNt50YEvrcrvENmyoMv5g==} @@ -12105,10 +12113,6 @@ snapshots: dependencies: tailwindcss: 3.4.3(ts-node@10.9.2(@types/node@20.14.8)(typescript@5.5.2)) - '@headlessui/tailwindcss@0.2.0(tailwindcss@3.4.3(ts-node@10.9.2(@types/node@20.8.0)(typescript@5.5.2)))': - dependencies: - tailwindcss: 3.4.3(ts-node@10.9.2(@types/node@20.8.0)(typescript@5.5.2)) - '@headlessui/tailwindcss@0.2.1(tailwindcss@3.4.3(ts-node@10.9.2(@types/node@20.14.8)(typescript@5.5.2)))': dependencies: tailwindcss: 3.4.3(ts-node@10.9.2(@types/node@20.14.8)(typescript@5.5.2)) @@ -13654,9 +13658,9 @@ snapshots: optionalDependencies: rollup: 2.78.0 - '@scalar/api-client@2.0.45(storybook@8.2.1(@babel/preset-env@7.24.8(@babel/core@7.24.8))(bufferutil@4.0.8)(utf-8-validate@6.0.4))(tailwindcss@3.4.3(ts-node@10.9.2(@types/node@20.8.0)(typescript@5.5.2)))(typescript@5.5.2)': + '@scalar/api-client@2.0.45(storybook@8.2.1(@babel/preset-env@7.24.8(@babel/core@7.24.8))(bufferutil@4.0.8)(utf-8-validate@6.0.4))(tailwindcss@3.4.3(ts-node@10.9.2(@types/node@20.14.8)(typescript@5.5.2)))(typescript@5.5.2)': dependencies: - '@headlessui/tailwindcss': 0.2.0(tailwindcss@3.4.3(ts-node@10.9.2(@types/node@20.8.0)(typescript@5.5.2))) + '@headlessui/tailwindcss': 0.2.0(tailwindcss@3.4.3(ts-node@10.9.2(@types/node@20.14.8)(typescript@5.5.2))) '@headlessui/vue': 1.7.22(vue@3.4.31(typescript@5.5.2)) '@scalar/components': 0.12.28(storybook@8.2.1(@babel/preset-env@7.24.8(@babel/core@7.24.8))(bufferutil@4.0.8)(utf-8-validate@6.0.4))(typescript@5.5.2) '@scalar/draggable': 0.1.4(typescript@5.5.2) @@ -13692,11 +13696,11 @@ snapshots: - typescript - vitest - '@scalar/api-reference@1.24.70(postcss@8.4.38)(storybook@8.2.1(@babel/preset-env@7.24.8(@babel/core@7.24.8))(bufferutil@4.0.8)(utf-8-validate@6.0.4))(tailwindcss@3.4.3(ts-node@10.9.2(@types/node@20.8.0)(typescript@5.5.2)))(typescript@5.5.2)': + '@scalar/api-reference@1.24.70(postcss@8.4.38)(storybook@8.2.1(@babel/preset-env@7.24.8(@babel/core@7.24.8))(bufferutil@4.0.8)(utf-8-validate@6.0.4))(tailwindcss@3.4.3(ts-node@10.9.2(@types/node@20.14.8)(typescript@5.5.2)))(typescript@5.5.2)': dependencies: '@floating-ui/vue': 1.1.1(vue@3.4.31(typescript@5.5.2)) '@headlessui/vue': 1.7.22(vue@3.4.31(typescript@5.5.2)) - '@scalar/api-client': 2.0.45(storybook@8.2.1(@babel/preset-env@7.24.8(@babel/core@7.24.8))(bufferutil@4.0.8)(utf-8-validate@6.0.4))(tailwindcss@3.4.3(ts-node@10.9.2(@types/node@20.8.0)(typescript@5.5.2)))(typescript@5.5.2) + '@scalar/api-client': 2.0.45(storybook@8.2.1(@babel/preset-env@7.24.8(@babel/core@7.24.8))(bufferutil@4.0.8)(utf-8-validate@6.0.4))(tailwindcss@3.4.3(ts-node@10.9.2(@types/node@20.14.8)(typescript@5.5.2)))(typescript@5.5.2) '@scalar/components': 0.12.28(storybook@8.2.1(@babel/preset-env@7.24.8(@babel/core@7.24.8))(bufferutil@4.0.8)(utf-8-validate@6.0.4))(typescript@5.5.2) '@scalar/oas-utils': 0.2.26(typescript@5.5.2) '@scalar/openapi-parser': 0.7.2 @@ -13781,9 +13785,9 @@ snapshots: transitivePeerDependencies: - typescript - '@scalar/hono-api-reference@0.5.131(postcss@8.4.38)(storybook@8.2.1(@babel/preset-env@7.24.8(@babel/core@7.24.8))(bufferutil@4.0.8)(utf-8-validate@6.0.4))(tailwindcss@3.4.3(ts-node@10.9.2(@types/node@20.8.0)(typescript@5.5.2)))(typescript@5.5.2)': + '@scalar/hono-api-reference@0.5.131(postcss@8.4.38)(storybook@8.2.1(@babel/preset-env@7.24.8(@babel/core@7.24.8))(bufferutil@4.0.8)(utf-8-validate@6.0.4))(tailwindcss@3.4.3(ts-node@10.9.2(@types/node@20.14.8)(typescript@5.5.2)))(typescript@5.5.2)': dependencies: - '@scalar/api-reference': 1.24.70(postcss@8.4.38)(storybook@8.2.1(@babel/preset-env@7.24.8(@babel/core@7.24.8))(bufferutil@4.0.8)(utf-8-validate@6.0.4))(tailwindcss@3.4.3(ts-node@10.9.2(@types/node@20.8.0)(typescript@5.5.2)))(typescript@5.5.2) + '@scalar/api-reference': 1.24.70(postcss@8.4.38)(storybook@8.2.1(@babel/preset-env@7.24.8(@babel/core@7.24.8))(bufferutil@4.0.8)(utf-8-validate@6.0.4))(tailwindcss@3.4.3(ts-node@10.9.2(@types/node@20.14.8)(typescript@5.5.2)))(typescript@5.5.2) hono: 4.5.3 transitivePeerDependencies: - '@jest/globals' -- 2.51.2