MCPcopy Index your code
hub / github.com/Jaironlanda/isupmap

github.com/Jaironlanda/isupmap @main

Chat with this repo
repository ↗ · DeepWiki ↗ · + Follow
5,129 symbols 11,621 edges 24 files 199 documented · 4%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

isUpMap — Service Status Heatmap

isUpMap status Deploy to Cloudflare

Live: isUpMap (Official) Live: map.warmindex.com (Backup)

A live up / down heatmap for 80+ popular internet services, rendered as a stock-map style treemap with brand logos. Built on a single Cloudflare Worker: a Cron Trigger polls each service's official status and persists snapshots + an incident log to D1, and a static frontend renders the treemap with per-service uptime, an incident history, a command palette, light/dark themes, and a mobile-friendly detail sheet.

Heatmap example

Features

  • Treemap heatmap — services grouped into category "sectors", tiles sized by prominence and colored by status, each with a brand logo.
  • Per-service detail — tap a tile (mobile) or click it (desktop) for status, 24h/7d uptime, component rollup, active incident, status-page host, and a "Visit status page" link.
  • Incident history — server-recorded incidents (open/close/duration), persisted in D1 and shown in a side panel and per-service (last 2 shown).
  • 90-day uptime history — Statuspage-style daily bars (colored by each day's worst incident status, with per-day uptime tooltips) plus the window average, derived from the incident log. Shown in the detail modal and on the SSR /status/<id> pages.
  • Community "report it's down" — users can vote a service is down with a reason (Can't connect, Errors, Can't log in, Slow, Other). Votes are deduplicated per IP-hash per 24 h, buffered through a Cloudflare Queue, and persisted in a separate D1. The per-service detail page shows aggregate counts, a 24-hour report-volume chart (Downdetector-style line + faint area over a shaded "typical range" band), per-country and per-reason breakdowns, and a Protomaps world map of recent report origins.
  • Report-surge detection — a Downdetector-style anomaly signal layered on the community reports. Each cron run scores a service's recent report volume against its own rolling baseline (EWMA mean + variance) and raises a surge when the count is a statistical outlier (≥3σ), clears an absolute floor (≥5 reports), and holds for 2 consecutive passes. A surging service is shown as a new "Reported" status color (orange) and a 24h sparkline on big tiles — supplementary only, it never opens an incident or alters the authoritative probe status. See detectSurges.
  • Command palette (⌘K) — fuzzy-search services and run commands.
  • Local customization — show/hide services or categories and a "problems only" filter, persisted in localStorage.
  • Notifications — in-page toasts on status changes, plus optional opt-in desktop notifications.
  • Atom incident feeds — subscribe to outages in any feed reader (or an RSS-to-alert tool): /feed.xml covers every service, /status/<id>/feed.xml covers one. Entries update in place when an incident resolves ("Resolved: GitHub was down for 25m").
  • Light / dark theme, deep links (?service=, ?filter=problems), and a favicon/title that reflect overall state.

How it works

Cron (every 5m) ─▶ scheduled() ─▶ resolveStatus() × N      ┌─▶ Statuspage JSON   ({base}/api/v2/summary.json)
                       │           (1 retry on a blip)      ├─▶ RSS / Atom feed   (fast-xml-parser)
                       │   (src/sources.ts) ────────────────┘
                       │                                    └─▶ HTTP reachability ping
                       │       each fetch attempt ──────────────▶ Analytics Engine (FETCH_ANALYTICS)
                       │                                          (service id, url, latency, status code, attempt)
                       ├─▶ persist to D1 (src/db.ts): flap-dampen → upsert `current`,
                       │   open/close `incidents`; daily retention sweep
                       ├─▶ decorateReports() — surge detection + 24h sparkline from REPORTS_DB
                       │   (supplementary; failure never blocks the status snapshot)
                       └─▶ publish finished snapshot to KV (SNAPSHOT_KV)
Browser (public/) ─poll /api/status every 45s─▶ Worker reads KV (Cache-API fronted) ─▶ treemap + uptime
                  └─open detail / panel ───────▶ GET /api/incidents (D1, cached)     ─▶ incident history

Community reports:
User taps "Report it's down" ─▶ POST /api/report/:id (VOTE_RATE_LIMITER: 10/60s per IP)
                                        │  hash IP → VOTE_QUEUE (Cloudflare Queue)
                                        └─▶ queue consumer → batch-insert into REPORTS_DB (D1)
GET /api/report/:id ─▶ KV hit (10-min TTL) → or D1 aggregate query → report JSON
                                                                       (total, countries, reasons,
                                                                        recent 10, 7-day timeline,
                                                                        24h hourly series, surge flag)
/status/:id page ─▶ server-rendered with Protomaps world map (report locations) + analytics panel
  • A Cron Trigger (*/5 * * * *) invokes scheduled(), which resolves every service concurrently (Promise.allSettled, each upstream guarded by a 15s timeout + edge caching, with one retry on a transient blip so a flaky connection doesn't read as unknown). It persists the result to D1, then publishes the finished snapshot to KV for the read path.
  • Flap-dampening (src/db.ts) — a non-up status must hold for 2 consecutive polls before it is committed to current or opens an incident; recovery to up is immediate, and an unknown (timed-out) probe holds the last status rather than resolving a real incident. A single glitchy upstream read therefore never paints a false outage. Resolved incidents older than 90 days are pruned once a day (~03:00 UTC).
  • GET /api/status is a fast KV read of the snapshot the cron publishes (no D1 query on the hot path), with per-service uptime (24h / 7d). It's fronted by the Cache API; before the first cron run it serves a "warming" snapshot (never a live fan-out). The response carries a stale flag (older than 3 cron cycles) so the UI warns instead of showing frozen data. Each service also carries a supplementary surge flag and a 24h report-volume spark series (added by the cron) that the UI uses for the "Reported" tile color and per-tile sparkline.
  • GET /api/incidents returns the recent incident log from D1 (Cache-API fronted). An optional ?service=<id> filter scopes it to a single service.
  • GET /api/uptime/:id returns a 90-day daily uptime series for one service ({ date, uptime, worst } per UTC day, oldest first, plus a uptime90 window average), computed from incident intervals — degraded counts against uptime, matching the 24h/7d numbers. Same Cache-API + rate-limit fronting as /api/incidents (5-min TTL). Approximations: days before a service was added read 100%, and an incident whose severity changed carries its final status for the whole interval.
  • GET /feed.xml (and per-service GET /status/<id>/feed.xml) serves the same incident log as an Atom feed for feed readers and RSS-to-alert tools. Entry ids are permanent and <updated> moves on resolution, so readers show recovery as an update to the same item rather than a duplicate. The dashboard and SSR pages advertise the feeds via <link rel="alternate"> autodiscovery. Cache-API fronted with a 5-min edge TTL (the cron cadence), so aggressive polling never reaches D1.
  • POST /api/report/:id accepts a community down-report ({ reason }) for a known service. The IP is hashed with VOTE_SALT (SHA-256; never stored raw), deduplicated per IP-hash per 24 h, and enqueued to VOTE_QUEUE for async batch-insert into REPORTS_DB. Tighter rate limit: 10 req / 60s per IP (VOTE_RATE_LIMITER). Returns 503 if VOTE_SALT is not set.
  • GET /api/report/:id returns aggregated community-report data for a service: 7-day total, per-country and per-reason breakdowns, up to 10 most-recent individual reports, a 7-day daily volume timeline, a 24h hourly series (for the report-volume chart), and the current surge flag. Cached in SNAPSHOT_KV (reportcount:<id>) with a 10-min TTL.
  • GET /api/summary returns a single overall-status rollup (worst status wins) plus a headline ("All systems operational" / "2 down, 1 degraded"), per-status counts, and the same stale flag — handy for compact embeds, badges, or a status banner. Add ?format=shields for a shields.io endpoint badge payload (status-colored), which powers the live badge at the top of this README. Same KV-read + Cache-API fronting as /api/status.
  • All API routes are rate-limited per IP (60 req / 60s) via a Workers rate-limit binding.
  • Crawlable pages — the dashboard is a client-rendered SPA, so for SEO the Worker also server-renders (no-JS) GET /status/<id> (per-service status + uptime + community report summary + Protomaps world map) and GET /status (a directory), plus a generated /sitemap.xml and a static robots.txt. These give crawlers real content for queries like "is GitHub down?". See src/pages.ts. The map and report widget assets are only shipped when the service actually has community report data (showMap flag).
  • The frontend (public/app.js) lays services out with a squarified treemap; tiles are sized by a per-service weight (layout only).

Status model

Every service is normalized to one of: up · degraded · down · unknown.

Source type How status is derived
Statuspage Reads {base}/api/v2/summary.json (Atlassian). status.indicator maps: noneup, minor/maintenancedegraded. A major/critical indicator is tempered by breadth — since the indicator reflects peak component severity, not how much is affected — so it only reads down when the major outage is broad (≥50% of components, or too few components to judge); a localized one (e.g. a single region) → degraded. The summary also yields component rollups and active incidents for the detail view.
RSS / Atom Parses the latest feed entry (via fast-xml-parser). Entries older than 48h are treated as resolved (up). A fresh entry mentioning resolved/restored/operationalup; outage/down/major/criticaldown; anything else fresh → degraded. Heuristic.
HTTP ping A plain GET. 2xx/3xxup; other response → degraded; network error or timeout → down.
(any) A failed fetch or timeout → unknown (treated as "no data" — never opens an incident or counts as downtime).

Statuspage JSON is authoritative where available. RSS-based status is a best-effort heuristic, since incident feeds describe history rather than a current-state field.

There is also a display-only fifth state, reported (orange): shown when the probe says up/unknown but community reports are surging (see Report-surge detection). It's derived at render time from the surge flag and never replaces a service's real status — a confirmed down/degraded always wins, and incidents and uptime stay 100% probe-driven.

Data sources

All data comes from each service's own public status page / feed. IsUpMap is a read-only aggregator and is not affiliated with any of these services. The list lives in src/services.ts; for Statuspage entries the /api/v2/summary.json path is appended to the base shown below.

Rows marked ⊘ disabled have an upstream that no longer publishes a usable machine-readable feed, so their status can't be resolved. They are shown as permanently unknown (grey), hidden from the heatmap, and appear locked in the Customize panel with the reason on hover — see Disabled services below.

Service Category Source Endpoint / base
GitHub Developer & Cloud Statuspage https://www.githubstatus.com
Cloudflare Developer & Cloud Statuspage https://www.cloudflarestatus.com
npm Developer & Cloud Statuspage https://status.npmjs.org
DigitalOcean Developer & Cloud Statuspage https://status.digitalocean.com
Vercel Developer & Cloud Statuspage https://www.vercel-status.com
Netlify Developer & Cloud Statuspage https://www.netlifystatus.com
MongoDB Developer & Cloud Statuspage https://status.mongodb.com
Sentry Developer & Cloud Statuspage https://status.sentry.io
CircleCI Developer & Cloud Statuspage https://status.circleci.com
Linode Developer & Cloud Statuspage https://status.linode.com
Render Developer & Cloud Statuspage https://status.render.com
AWS Developer & Cloud RSS https://status.aws.amazon.com/rss/all.rss
Google Cloud Developer & Cloud RSS https://status.cloud.google.com/en/feed.atom
Microsoft Azure Developer & Cloud RSS · ⊘ disabled https://azure.status.microsoft/en-us/status/feed/
Supabase Developer & Cloud Statuspage https://status.supabase.com
Fly.io Developer & Cloud Statuspage https://status.flyio.net
Railway Developer & Cloud RSS · **⊘

Extension points exported contracts — how you extend this code

Response (Interface)
* The **`Response`** interface of the Fetch API represents the response to a request. * * [MDN Reference](https://deve [8 …
worker-configuration.d.ts
FetchCtx (Interface)
Carries the Analytics Engine dataset + service context into fetch helpers.
src/sources.ts
ProvidedEnv (Interface)
(no doc)
test/env.d.ts
Snapshot (Interface)
(no doc)
src/index.ts
FetchEvent (Interface)
(no doc)
src/analytics.ts
ProbeStateRow (Interface)
(no doc)
src/db.ts
StatusCopy (Interface)
(no doc)
src/pages.ts
CountryCount (Interface)
(no doc)
src/reports.ts

Core symbols most depended-on inside this repo

push
called by 581
public/lib/maplibre-gl.js
get
called by 419
worker-configuration.d.ts
evaluate
called by 213
public/lib/maplibre-gl.js
map
called by 206
public/lib/maplibre-gl.js
fire
called by 199
public/lib/maplibre-gl.js
set
called by 176
worker-configuration.d.ts
emplaceBack
called by 99
public/lib/maplibre-gl.js
add
called by 99
public/lib/maplibre-gl.js

Shape

Method 3,092
Function 823
Class 741
Interface 473

Languages

TypeScript100%

Modules by API surface

public/lib/maplibre-gl.js4,106 symbols
worker-configuration.d.ts782 symbols
public/app.js67 symbols
src/reports.ts23 symbols
public/report.js22 symbols
src/sources.ts20 symbols
src/index.ts19 symbols
test/sources.test.ts15 symbols
src/pages.ts15 symbols
src/db.ts15 symbols
public/lib/protomaps-basemaps.esm.js11 symbols
scripts/probe-status.mjs6 symbols

For agents

$ claude mcp add isupmap \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact