import { Elysia } from "elysia"; import { cron } from "@elysiajs/cron"; const CLOUDFLARE_STATUS_URL = "https://www.cloudflarestatus.com"; const CLOUDFLARE_API_URL = `${CLOUDFLARE_STATUS_URL}/api/v2`; const REFRESH_INTERVAL = "*/5 * * * *"; type PageIndicator = "none" | "minor" | "major" | "critical" | "unknown"; type ComponentStatus = | "operational" | "degraded_performance" | "partial_outage" | "major_outage" | "unknown"; interface CloudflareComponent { id: string; name: string; status: ComponentStatus; position?: number; description?: string | null; group?: boolean; } interface CloudflareIncident { id: string; name: string; status: string; impact?: string; shortlink?: string; started_at?: string; created_at?: string; updated_at?: string; resolved_at?: string | null; } interface CloudflareMaintenance { id: string; name: string; status: string; impact?: string; shortlink?: string; started_at?: string; scheduled_for?: string; scheduled_until?: string; } interface CloudflareSummaryResponse { page?: { name?: string; updated_at?: string }; status?: { indicator?: string; description?: string }; components?: CloudflareComponent[]; incidents?: CloudflareIncident[]; scheduled_maintenances?: CloudflareMaintenance[]; } interface CloudflareIncidentsResponse { incidents?: CloudflareIncident[]; } interface StatusSnapshot { ok: boolean; fetchedAt: string | null; pageUpdatedAt: string | null; status: { indicator: PageIndicator; description: string }; streak: { days: number | null; since: string | null; activeIncident: boolean }; lastIncident: { id: string; name: string; startedAt: string | null; resolvedAt: string | null; impact: string; shortlink: string | null; } | null; incidents: Array<{ id: string; name: string; status: string; impact: string; startedAt: string | null; resolvedAt: string | null; shortlink: string | null; }>; components: Array<{ id: string; name: string; status: ComponentStatus; description: string | null; }>; componentCounts: { total: number; operational: number; affected: number }; maintenances: Array<{ id: string; name: string; status: string; impact: string; startsAt: string | null; endsAt: string | null; shortlink: string | null; }>; error: string | null; } const initialSnapshot: StatusSnapshot = { ok: false, fetchedAt: null, pageUpdatedAt: null, status: { indicator: "unknown", description: "Cloudflare 상태를 확인하는 중입니다" }, streak: { days: null, since: null, activeIncident: false }, lastIncident: null, incidents: [], components: [], componentCounts: { total: 0, operational: 0, affected: 0 }, maintenances: [], error: null, }; let snapshot = initialSnapshot; function asArray(value: unknown): T[] { return Array.isArray(value) ? (value as T[]) : []; } function isIncidentActive(incident: CloudflareIncident): boolean { return !["resolved", "postmortem"].includes(incident.status); } function toTime(value?: string | null): number { if (!value) return 0; const time = Date.parse(value); return Number.isNaN(time) ? 0 : time; } function daysSince(value: string | null, now = Date.now()): number | null { if (!value) return null; const since = toTime(value); if (!since) return null; return Math.max(0, Math.floor((now - since) / (1000 * 60 * 60 * 24))); } function indicator(value?: string): PageIndicator { if (value === "none" || value === "minor" || value === "major" || value === "critical") return value; return "unknown"; } function componentStatus(value?: string): ComponentStatus { if ( value === "operational" || value === "degraded_performance" || value === "partial_outage" || value === "major_outage" ) return value; return "unknown"; } function normalizeIncident(incident: CloudflareIncident) { return { id: incident.id, name: incident.name, status: incident.status, impact: incident.impact ?? "none", startedAt: incident.started_at ?? incident.created_at ?? null, resolvedAt: incident.resolved_at ?? null, shortlink: incident.shortlink ?? null, }; } function normalizeMaintenance(maintenance: CloudflareMaintenance) { return { id: maintenance.id, name: maintenance.name, status: maintenance.status, impact: maintenance.impact ?? "none", startsAt: maintenance.scheduled_for ?? maintenance.started_at ?? null, endsAt: maintenance.scheduled_until ?? null, shortlink: maintenance.shortlink ?? null, }; } async function fetchJson(url: string): Promise { const response = await fetch(url, { headers: { accept: "application/json", "user-agent": "cloudflare-status-dday/1.0" }, signal: AbortSignal.timeout(10_000), }); if (!response.ok) throw new Error(`Cloudflare API returned ${response.status}`); return (await response.json()) as T; } async function refreshStatus(source: "startup" | "cron") { try { const [summary, incidentsResponse] = await Promise.all([ fetchJson(`${CLOUDFLARE_API_URL}/summary.json`), fetchJson(`${CLOUDFLARE_API_URL}/incidents.json`), ]); const incidentsById = new Map(); for (const incident of [ ...asArray(summary.incidents), ...asArray(incidentsResponse.incidents), ]) { if (incident.id) incidentsById.set(incident.id, incident); } const incidents = [...incidentsById.values()].sort( (a, b) => toTime(b.started_at ?? b.created_at) - toTime(a.started_at ?? a.created_at), ); const activeIncidents = incidents.filter(isIncidentActive); const latestIncident = incidents[0]; const components = asArray(summary.components) .filter((component) => !component.group) .sort((a, b) => { const aAffected = componentStatus(a.status) === "operational" ? 1 : 0; const bAffected = componentStatus(b.status) === "operational" ? 1 : 0; return aAffected - bAffected || (a.position ?? 0) - (b.position ?? 0); }) .map((component) => ({ id: component.id, name: component.name, status: componentStatus(component.status), description: component.description ?? null, })); const maintenances = asArray(summary.scheduled_maintenances) .sort((a, b) => toTime(a.scheduled_for ?? a.started_at) - toTime(b.scheduled_for ?? b.started_at)) .map(normalizeMaintenance); const now = new Date().toISOString(); snapshot = { ok: true, fetchedAt: now, pageUpdatedAt: summary.page?.updated_at ?? null, status: { indicator: indicator(summary.status?.indicator), description: summary.status?.description ?? "상태 정보가 없습니다", }, streak: { days: daysSince(latestIncident?.started_at ?? latestIncident?.created_at ?? null), since: latestIncident?.started_at ?? latestIncident?.created_at ?? null, activeIncident: activeIncidents.length > 0, }, lastIncident: latestIncident ? { id: latestIncident.id, name: latestIncident.name, startedAt: latestIncident.started_at ?? latestIncident.created_at ?? null, resolvedAt: latestIncident.resolved_at ?? null, impact: latestIncident.impact ?? "none", shortlink: latestIncident.shortlink ?? null, } : null, incidents: incidents.slice(0, 6).map(normalizeIncident), components, componentCounts: { total: components.length, operational: components.filter((component) => component.status === "operational").length, affected: components.filter((component) => component.status !== "operational").length, }, maintenances: maintenances.slice(0, 5), error: null, }; console.log(`[${source}] Cloudflare status updated at ${now}`); } catch (error) { const message = error instanceof Error ? error.message : "Unknown error"; snapshot = { ...snapshot, ok: false, error: message }; console.error(`[${source}] Failed to update Cloudflare status: ${message}`); } } const INDEX_HTML = String.raw` Cloudflare · Days Without Incident
a quiet day on the edge

Cloudflare 장애 없이
계산 중 지났습니다

공식 Cloudflare Status API를 주기적으로 확인해
마지막 인시던트 이후의 시간을 기록합니다.

days without incident
상태 확인 중

Cloudflare status 데이터를 불러오고 있습니다.

현재 전체 상태

Cloudflare 공식 페이지의 롤업 상태

LIVE
상태 확인 중마지막 동기화: —
전체 서비스
정상 운영
확인 필요

서비스 구성요소

영향이 있는 항목을 먼저 표시합니다

불러오는 중…

최근 인시던트

Cloudflare Status에 기록된 최근 장애

불러오는 중…

예정된 유지보수

예정 또는 진행 중인 작업

예정된 유지보수가 없습니다.
`; const port = Number.parseInt(Bun.env.PORT ?? "3000", 10); const hostname = Bun.env.HOST ?? "0.0.0.0"; const app = new Elysia() .use(cron({ name: "cloudflare-status-refresh", pattern: REFRESH_INTERVAL, catch: true, run: () => refreshStatus("cron") })) .get("/", () => new Response(INDEX_HTML, { headers: { "content-type": "text/html; charset=utf-8" } })) .get("/api/status", () => new Response(JSON.stringify(snapshot), { headers: { "cache-control": "no-store", "content-type": "application/json; charset=utf-8" } })) .get("/healthz", () => ({ ok: true, fetchedAt: snapshot.fetchedAt, cloudflareOk: snapshot.ok })) .listen({ port, hostname }); await refreshStatus("startup"); console.log(`🦊 Cloudflare D-Day is running at ${app.server?.hostname}:${app.server?.port}`);