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.
3.0 kB ยท 103 lines
TypeScript
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104import { getLogger } from "@logtape/logtape";import { and, db, eq, inArray, isNull, schema } from "@openstatus/db";
import { checkerAudit } from "../utils/audit-log";
const logger = getLogger(["workflow"]);
/** * Finds an open incident (not resolved) for the given monitor. */export async function findOpenIncident(monitorId: number) { return db .select() .from(schema.monitorIncidentTable) .where( and( eq(schema.monitorIncidentTable.monitorId, monitorId), isNull(schema.monitorIncidentTable.resolvedAt), ), ) .get();}
/** * Finds all open incidents (not resolved) for the given monitor. */export async function findAllOpenIncidents(monitorId: number) { return db .select() .from(schema.monitorIncidentTable) .where( and( eq(schema.monitorIncidentTable.monitorId, monitorId), isNull(schema.monitorIncidentTable.resolvedAt), ), ) .all();}
/** * Resolves all open incidents by setting resolvedAt and autoResolved flag. * Uses a single atomic bulk update to prevent partial state on failures. * Returns array of successfully resolved incidents. */export async function resolveIncident(params: { monitorId: string; cronTimestamp: number;}): Promise<(typeof schema.monitorIncidentTable.$inferSelect)[]> { const { monitorId, cronTimestamp } = params;
// Find ALL open incidents for this monitor const incidents = await findAllOpenIncidents(Number(monitorId));
if (incidents.length === 0) { return []; // No open incidents }
// Extract all incident IDs for bulk update const incidentIds = incidents.map((i) => i.id);
// ATOMIC BULK UPDATE: Resolve all incidents in a single query // This prevents partial state if operation fails midway const resolvedIncidents = await db .update(schema.monitorIncidentTable) .set({ resolvedAt: new Date(cronTimestamp), autoResolved: true, }) .where( and( inArray(schema.monitorIncidentTable.id, incidentIds), isNull(schema.monitorIncidentTable.resolvedAt), // Still prevents race conditions ), ) .returning();
// Emit audit logs for each resolved incident // These are best-effort; failure here doesn't affect data integrity for (const incident of resolvedIncidents) { logger.info("Recovered incident", { incident_id: incident.id, monitor_id: monitorId, });
try { await checkerAudit.publishAuditLog({ id: `monitor:${monitorId}`, action: "incident.resolved", targets: [{ id: monitorId, type: "monitor" }], metadata: { cronTimestamp, incidentId: incident.id }, }); } catch (error) { logger.error("Failed to publish audit log for incident resolution", { incident_id: incident.id, monitor_id: monitorId, error, }); // Don't throw - incident is already resolved } }
return resolvedIncidents;}