Skip to content
547 changes: 547 additions & 0 deletions ALARMS_IMPLEMENTATION.md

Large diffs are not rendered by default.

432 changes: 432 additions & 0 deletions UPTIME_ALARM_INTEGRATION.md

Large diffs are not rendered by default.

567 changes: 567 additions & 0 deletions apps/api/src/routes/alarms.ts

Large diffs are not rendered by default.

48 changes: 48 additions & 0 deletions apps/uptime/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ import { Elysia } from "elysia";
import { z } from "zod";
import { type CheckOptions, checkUptime, lookupSchedule } from "./actions";
import type { JsonParsingConfig } from "./json-parser";
import {
evaluateUptimeAlarms,
autoResolveAlarms,
consecutiveFailures,
} from "./lib/alarm-trigger";
import { sendUptimeEvent } from "./lib/producer";
import {
captureError,
Expand All @@ -11,6 +16,7 @@ import {
shutdownTracing,
startRequestSpan,
} from "./lib/tracing";
import { MonitorStatus } from "./types";

initTracing();

Expand Down Expand Up @@ -47,6 +53,9 @@ const receiver = new Receiver({
nextSigningKey: NEXT_SIGNING_KEY,
});

// Track previous status for alarm evaluation
const previousStatusMap = new Map<string, MonitorStatus>();

const app = new Elysia()
.state("tracing", {
span: null as ReturnType<typeof startRequestSpan> | null,
Expand Down Expand Up @@ -192,6 +201,45 @@ const app = new Elysia()
);
}

// ========== ALARM INTEGRATION ==========
// Evaluate alarms after uptime check (non-blocking)
try {
const currentStatus = result.data.status;
const previousStatus = previousStatusMap.get(scheduleId);

// Track consecutive failures
let consecutiveFailureCount = 0;
if (currentStatus === MonitorStatus.DOWN) {
consecutiveFailureCount = consecutiveFailures.increment(scheduleId);
} else {
consecutiveFailures.reset(scheduleId);
}

// Evaluate alarms
await evaluateUptimeAlarms({
uptimeScheduleId: scheduleId,
uptimeData: result.data,
previousStatus,
consecutiveFailureCount,
});

// Auto-resolve alarms if website is back up
if (currentStatus === MonitorStatus.UP && previousStatus === MonitorStatus.DOWN) {
await autoResolveAlarms(scheduleId, result.data);
}

// Update previous status
previousStatusMap.set(scheduleId, currentStatus);
} catch (error) {
// Log but don't fail the uptime check
console.error("[uptime] Alarm evaluation error:", error);
captureError(error, {
type: "alarm_evaluation_error",
scheduleId,
});
}
// ========== END ALARM INTEGRATION ==========

return new Response("Uptime check complete", { status: 200 });
} catch (error) {
captureError(error, { type: "unexpected_error" });
Expand Down
Loading