Skip to content

fix(integrations): make a fresh Cloudflare connection explain itself - #688

Merged
Makisuo merged 1 commit into
mainfrom
fix/cloudflare-post-connect-empty-state
Aug 30, 2026
Merged

fix(integrations): make a fresh Cloudflare connection explain itself#688
Makisuo merged 1 commit into
mainfrom
fix/cloudflare-post-connect-empty-state

Conversation

@Makisuo

@Makisuo Makisuo commented Aug 30, 2026

Copy link
Copy Markdown
Collaborator

The problem

Connecting Cloudflare did no work. The OAuth callback stored the grant, called resetOrgState, and returned — discovery and the first poll were both left to the alerting worker's */5 cron. Combined with the 10-minute safety lag (analytics buckets younger than that are incomplete, so the poller never asks for them), a freshly connected org saw an account name and nothing else — no zones, no Workers, no data — for 10–20 minutes.

Nothing said so. /infra/cloudflare rendered a generic empty, the zone drill-in claimed the zone had no traffic, the integrations card said "within a few minutes" with no zones under it, and none of them refreshed, so even once data landed the page stayed blank until a manual reload.

What changed

Prime at connect. The callback now runs pollOrg right after resetOrgState (which has already cleared discoveredAt, so this rediscovers). Bounded by a 12s timeout and catchCause'd — discovery, the part that makes the UI stop looking empty, commits in the first seconds, and whatever polling the timeout cuts short resumes on the next tick. It can never fail the callback page. Same shape as the PlanetScale callback's inline finalizeOrgSelection.

Two facts added to the wire, both optionalKey for the deploy window:

  • connectedAt on the status, from the grant row's createdAt — a token refresh never touches it, so it dates the connection rather than the token.
  • backfillAt per zone and per Workers row, which was already in Postgres and just wasn't exposed.

One phase derivation. cloudflareIngestPhase() turns status + warehouse usage into discoveringcollectingpartial / backfillinglive, plus stalled past 30 minutes of silence, where "give it a few minutes" stops being the answer and the copy points at traffic and permissions instead. Copy lives in one function, so the surfaces can't contradict each other. The infra index, the zone drill-in, and the integrations card all render from it, and status + usage poll every 30s until the phase is live so the page fills in on its own.

Worth a reviewer's attention

  • Backfill progress reads the zone with the highest frontier. The frontier walks down, so that's the zone furthest from done, and a day-wide chart is only as complete as it is. Reading the lowest (the first thing I wrote) lets a single finished zone, whose frontier rests at the floor, report the whole account as caught up. Covered by a test.
  • A quiet zone stops being called partial past the stall window. A zone with no traffic is indistinguishable from one still catching up, so the "n of m reporting" line only holds while catching-up is the likelier explanation — otherwise any account with an idle domain grows a permanent banner.
  • A failed usage read is not treated as evidence of no data — the phase holds at the poller's own view rather than reporting an absence the warehouse never confirmed.
  • The 12s prime timeout is a guess at the right tradeoff. Long enough for discovery plus a first window on an ordinary account; a many-zoned one still gets its success page promptly and lets the cron finish. Happy to move it.

Testing

12 unit tests on the phase derivation (ingest-phase.test.ts). apps/api Cloudflare service + OAuth suites and the 26 integrations/setup-audit route files pass; apps/web typechecks.

Not verified in a browser — the new states only render for an org with a live Cloudflare grant, which needs the full local stack. Worth a look at /infra/cloudflare right after a reconnect.

Out of scope

Service-map Cloudflare nodes and the Cloudflare dashboard template go silently empty the same way. They need their own empty states rather than this banner, so they're untouched here.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

Connecting Cloudflare did no work: the callback stored the grant and returned,
leaving discovery and the first poll to the alerting worker's */5 cron. Between
that cadence and the 10-minute safety lag on incomplete analytics buckets, a
newly connected org saw an account name and nothing else — no zones, no
Workers, no data — for 10-20 minutes, on every Cloudflare surface, with no copy
anywhere saying that was expected or when it would end.

Prime the org inline instead: the callback now runs pollOrg after resetOrgState
(which has already cleared discoveredAt), bounded by a 12s timeout and
best-effort, so discovery commits before the popup reports success and whatever
polling the timeout cuts short resumes on the next tick.

Then say what is happening while the rest arrives. Status gains connectedAt
(from the grant row's createdAt, which a token refresh never touches) and a
per-zone backfillAt, which was already in Postgres; cloudflareIngestPhase()
turns those plus warehouse usage into one of discovering / collecting /
partial / backfilling / live / stalled, and every Cloudflare surface — the
infra index, the zone drill-in, the integrations card — renders its banner and
empty state from that single derivation, so they cannot disagree about whether
an empty page is normal. Status and usage poll every 30s until the phase is
live, so the page fills in without a reload.

Backfill progress reads the zone with the HIGHEST frontier: the frontier walks
down, so that is the one furthest from done, and a day-wide chart is only as
complete as it. Reading the lowest would let one finished zone claim the whole
account was caught up.

Silence past 30 minutes stops being described as a cadence and starts pointing
at traffic and permissions instead.
@Makisuo
Makisuo merged commit 5bdfa84 into main Aug 30, 2026
34 checks passed
@Makisuo
Makisuo deleted the fix/cloudflare-post-connect-empty-state branch August 30, 2026 11:02
@github-actions

Copy link
Copy Markdown

🍁 Maple PR preview

Warning

Preview cleanup could not be confirmed. The Alchemy teardown outcome was skipped.

Final commit d4a2e51 · View workflow run

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant