Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/openstatusHQ/openstatus. ๐ซ Status page with uptime monitoring & API monitoring as code ๐ซ openstatus.dev
bun drizzle-orm monitoring monitoring-as-code nextjs observability on-call open-source shadcn-ui status-page statuspage synthetic-monitoring tinybird turso uptime uptime-checker uptime-monitor
Something went wrong. Try again.
7.2 kB ยท 232 lines
TypeScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233import { and, db, eq, isNotNull, isNull } from "@openstatus/db";import { maintenance, page, pageSubscriber, statusReportUpdate,} from "@openstatus/db/src/schema";import { currentImpactsFromUpdates } from "@openstatus/db/src/schema/page_components/constants";
import { getChannel } from "./channels";import type { PageUpdate, Subscription } from "./types";
/** * Dispatch notifications for a status report update */export async function dispatchStatusReportUpdate(statusReportUpdateId: number) { const update = await db.query.statusReportUpdate.findFirst({ where: eq(statusReportUpdate.id, statusReportUpdateId), with: { statusReport: { with: { // Membership: the full set of components on the report (id + name). statusReportsToPageComponents: { with: { pageComponent: true }, }, // All updates' impact rows โ current state is reconstructed from the // delta history, not from any single update's rows. statusReportUpdates: { with: { statusReportUpdateToPageComponents: true }, }, }, }, }, });
if (!update?.statusReport) { console.error(`Status report update ${statusReportUpdateId} not found`); return; }
if (!update.statusReport.pageId) { console.error(`Status report ${update.statusReport.id} has no page ID`); return; }
const pageComponents = update.statusReport.statusReportsToPageComponents.map( (i) => i.pageComponent, );
// impacts as of this update: a late dispatch must not show later updates const currentImpacts = currentImpactsFromUpdates( update.statusReport.statusReportUpdates .filter((u) => u.date.getTime() <= update.date.getTime()) .map((u) => ({ id: u.id, date: u.date, componentImpacts: u.statusReportUpdateToPageComponents, })), ); // legacy report (no impact rows): channels fall back to bare names const componentsWithImpact = currentImpacts.size > 0 ? pageComponents.map((c) => ({ id: c.id, name: c.name, impact: currentImpacts.get(c.id) ?? ("operational" as const), })) : undefined;
await dispatchPageUpdate({ id: update.statusReport.id, pageId: update.statusReport.pageId, title: update.statusReport.title, status: update.status as PageUpdate["status"], message: update.message, pageComponentIds: pageComponents.map((c) => c.id), pageComponents: pageComponents.map((c) => c.name), date: update.date.toISOString(), updateId: update.id, pageComponentsWithId: pageComponents.map((c) => ({ id: c.id, name: c.name, })), componentsWithImpact, });}
/** * Dispatch notifications for a maintenance update */export async function dispatchMaintenanceUpdate(maintenanceId: number) { const maintenanceWithComponents = await db.query.maintenance.findFirst({ where: eq(maintenance.id, maintenanceId), with: { maintenancesToPageComponents: { with: { pageComponent: true }, }, }, });
if (!maintenanceWithComponents) { console.error(`Maintenance ${maintenanceId} not found`); return; }
if (!maintenanceWithComponents.pageId) { console.error(`Maintenance ${maintenanceId} has no page ID`); return; }
const pageComponents = maintenanceWithComponents.maintenancesToPageComponents.map( (i) => i.pageComponent, );
await dispatchPageUpdate({ id: maintenanceWithComponents.id, pageId: maintenanceWithComponents.pageId, title: maintenanceWithComponents.title, status: "maintenance", message: maintenanceWithComponents.message, pageComponentIds: pageComponents.map((c) => c.id), pageComponents: pageComponents.map((c) => c.name), date: `${maintenanceWithComponents.from.toISOString()} - ${maintenanceWithComponents.to.toISOString()}`, startsAt: maintenanceWithComponents.from.toISOString(), endsAt: maintenanceWithComponents.to.toISOString(), pageComponentsWithId: pageComponents.map((c) => ({ id: c.id, name: c.name, })), });}
/** * Dispatch notifications for a page update to all matching subscriptions * * - Entire page subscriptions (empty componentIds): always notified * - Component subscriptions: only notified if any affected component matches */export async function dispatchPageUpdate(pageUpdate: PageUpdate) { const affectedComponentIds = pageUpdate.pageComponentIds;
const pageData = await db .select({ id: page.id, name: page.title, slug: page.slug, customDomain: page.customDomain, }) .from(page) .where(eq(page.id, pageUpdate.pageId)) .get();
if (!pageData) { console.error(`Page ${pageUpdate.pageId} not found`); return; }
const subscribersWithComponents = await db.query.pageSubscriber.findMany({ where: and( eq(pageSubscriber.pageId, pageUpdate.pageId), isNotNull(pageSubscriber.acceptedAt), isNull(pageSubscriber.unsubscribedAt), ), // Deterministic order so the email idempotency fingerprint is stable // across dispatches; id breaks same-second createdAt ties. orderBy: (subs, { asc }) => [asc(subs.createdAt), asc(subs.id)], with: { components: true, }, });
const matchingSubscriptions: Subscription[] = subscribersWithComponents .map((sub) => ({ id: sub.id, pageId: sub.pageId, pageName: pageData.name, pageSlug: pageData.slug, customDomain: pageData.customDomain, channelType: sub.channelType as "email" | "webhook" | "slack", email: sub.email ?? undefined, webhookUrl: sub.webhookUrl ?? undefined, slackChannelId: sub.slackChannelId ?? undefined, channelConfig: sub.channelConfig ?? undefined, token: sub.token ?? undefined, acceptedAt: sub.acceptedAt ?? undefined, componentIds: sub.components.map((c) => c.pageComponentId), })) .filter((sub) => { // Entire page subscription matches all updates if (sub.componentIds.length === 0) return true;
// Component subscription: check for overlap return affectedComponentIds.some((id) => sub.componentIds.includes(id)); });
if (matchingSubscriptions.length === 0) { console.log(`No matching subscriptions for page update ${pageUpdate.id}`); return; }
const byChannel = matchingSubscriptions.reduce( (acc, sub) => { if (!acc[sub.channelType]) { acc[sub.channelType] = []; } acc[sub.channelType].push(sub); return acc; }, {} as Record<string, Subscription[]>, );
await Promise.allSettled( Object.entries(byChannel).map(async ([channelType, subs]) => { const channel = getChannel(channelType); if (!channel) { console.error(`Unknown channel type: ${channelType}`); return; }
try { await channel.sendNotifications(subs, pageUpdate); console.log(`Sent ${subs.length} notifications via ${channelType}`); } catch (error) { console.error( `Failed to send notifications via ${channelType}:`, error, ); } }), );}