From b999dd048cdfed08eea518fbe8eb9e30eb2392e9 Mon Sep 17 00:00:00 2001 From: Maximilian Kaske <56969857+mxkaske@users.noreply.github.com> Date: Fri, 24 Nov 2023 15:35:36 +0100 Subject: [PATCH] chore: include incident to public status (#484) --- apps/docs/getting-started/status-widget.mdx | 10 +++++++ apps/server/src/public/status.ts | 33 +++++++++++++++++++-- packages/react/README.md | 3 +- packages/react/src/utils.ts | 11 ++++++- packages/react/src/widget.tsx | 10 +++---- 5 files changed, 58 insertions(+), 9 deletions(-) diff --git a/apps/docs/getting-started/status-widget.mdx b/apps/docs/getting-started/status-widget.mdx index 70887999c4..d796ddacf7 100644 --- a/apps/docs/getting-started/status-widget.mdx +++ b/apps/docs/getting-started/status-widget.mdx @@ -32,6 +32,7 @@ enum Status { MajorOutage = "major_outage", UnderMaintenance = "under_maintenance", // currently not in use Unknown = "unknown", + Incident = "incident", } ``` @@ -54,6 +55,10 @@ function getStatus(ratio: number) { We are caching the result for `30 seconds` to reduce the load on our database. +The `Status.Incident` will always be returned when then status of any incident +on your page is **not** _"monitoring"_ or _"resolved"_. You can attach an +incident to a monitor (implicit) or a page (explicit). + > If you have a doubt about the above calculation, feel free to contact us via > [ping@openstatus.dev](mailto:ping@openstatus.dev) or > [discord](https://openstatus.dev/discord). @@ -82,6 +87,7 @@ const statusEnum = z.enum([ "major_outage", "under_maintenance", "unknown", + "incident", ]); const statusSchema = z.object({ status: statusEnum }); @@ -107,6 +113,10 @@ const dictionary = { label: "Unknown", color: "bg-gray-500", }, + incident: { + label: "Incident", + color: "bg-yellow-500", + }, under_maintenance: { label: "Under Maintenance", color: "bg-gray-500", diff --git a/apps/server/src/public/status.ts b/apps/server/src/public/status.ts index e54e3f919d..d6b0b530c1 100644 --- a/apps/server/src/public/status.ts +++ b/apps/server/src/public/status.ts @@ -2,7 +2,14 @@ import { Hono } from "hono"; import { endTime, setMetric, startTime } from "hono/timing"; import { db, eq } from "@openstatus/db"; -import { monitor, monitorsToPages, page } from "@openstatus/db/src/schema"; +import { + incident, + monitor, + monitorsToIncidents, + monitorsToPages, + page, + pagesToIncidents, +} from "@openstatus/db/src/schema"; import { getMonitorList, Tinybird } from "@openstatus/tinybird"; import { Redis } from "@openstatus/upstash"; @@ -20,6 +27,7 @@ enum Status { MajorOutage = "major_outage", UnderMaintenance = "under_maintenance", Unknown = "unknown", + Incident = "incident", } export const status = new Hono(); @@ -33,17 +41,37 @@ status.get("/:slug", async (c) => { return c.json({ status: cache }); } + startTime(c, "database"); // { monitors, pages, monitors_to_pages } const monitorData = await db .select() .from(monitorsToPages) .leftJoin(monitor, eq(monitorsToPages.monitorId, monitor.id)) + .leftJoin( + monitorsToIncidents, + eq(monitor.id, monitorsToIncidents.monitorId), + ) + .leftJoin(incident, eq(monitorsToIncidents.incidentId, incident.id)) .leftJoin(page, eq(monitorsToPages.pageId, page.id)) .where(eq(page.slug, slug)) .all(); + + const pageIncidentData = await db + .select() + .from(pagesToIncidents) + .leftJoin(incident, eq(pagesToIncidents.incidentId, incident.id)) + .leftJoin(page, eq(pagesToIncidents.pageId, page.id)) + .where(eq(page.slug, slug)) + .all(); + endTime(c, "database"); + const isIncident = [...pageIncidentData, ...monitorData].some((data) => { + if (!data.incident) return false; + return !["monitoring", "resolved"].includes(data.incident.status); + }); + startTime(c, "clickhouse"); // { data: [{ ok, count }] } const lastMonitorPings = await Promise.allSettled( @@ -77,7 +105,8 @@ status.get("/:slug", async (c) => { const ratio = data.ok / data.count; - const status = getStatus(ratio); + const status: Status = isIncident ? Status.Incident : getStatus(ratio); + await redis.set(slug, status, { ex: 30 }); return c.json({ status }); diff --git a/packages/react/README.md b/packages/react/README.md index 06d13526d9..82d9404a33 100644 --- a/packages/react/README.md +++ b/packages/react/README.md @@ -85,7 +85,8 @@ export type Status = | "partial_outage" | "major_outage" | "under_maintenance" - | "unknown"; + | "unknown" + | "incident"; ``` Learn more in the [docs](https://docs.openstatus.dev/packages/react). diff --git a/packages/react/src/utils.ts b/packages/react/src/utils.ts index a25ce3ef02..612e345223 100644 --- a/packages/react/src/utils.ts +++ b/packages/react/src/utils.ts @@ -1,4 +1,9 @@ -export const statusDictionary = { +import type { Status } from "./widget"; + +export const statusDictionary: Record< + Status, + { label: string; color: string } +> = { operational: { label: "Operational", color: "bg-green-500", @@ -19,6 +24,10 @@ export const statusDictionary = { label: "Unknown", color: "bg-gray-500", }, + incident: { + label: "Incident", + color: "bg-yellow-500", + }, under_maintenance: { label: "Under Maintenance", color: "bg-gray-500", diff --git a/packages/react/src/widget.tsx b/packages/react/src/widget.tsx index 08d20a7a0f..e4560f7cbe 100644 --- a/packages/react/src/widget.tsx +++ b/packages/react/src/widget.tsx @@ -6,7 +6,8 @@ export type Status = | "partial_outage" | "major_outage" | "under_maintenance" - | "unknown"; + | "unknown" + | "incident"; export type StatusResponse = { status: Status }; @@ -29,10 +30,9 @@ export type StatusWidgetProps = { }; export async function StatusWidget({ slug, href }: StatusWidgetProps) { - const data = await getStatus(slug); + const { status } = await getStatus(slug); - const key = data.status; - const { label, color } = statusDictionary[key]; + const { label, color } = statusDictionary[status]; return ( {label} - {data.status === "operational" ? ( + {status === "operational" ? (