fix(integrations): make a fresh Cloudflare connection explain itself - #688
Merged
Conversation
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.
🍁 Maple PR previewWarning Preview cleanup could not be confirmed. The Alchemy teardown outcome was Final commit |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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*/5cron. 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/cloudflarerendered 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
pollOrgright afterresetOrgState(which has already cleareddiscoveredAt, so this rediscovers). Bounded by a 12s timeout andcatchCause'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 inlinefinalizeOrgSelection.Two facts added to the wire, both
optionalKeyfor the deploy window:connectedAton the status, from the grant row'screatedAt— a token refresh never touches it, so it dates the connection rather than the token.backfillAtper zone and per Workers row, which was already in Postgres and just wasn't exposed.One phase derivation.
cloudflareIngestPhase()turns status + warehouse usage intodiscovering→collecting→partial/backfilling→live, plusstalledpast 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 isliveso the page fills in on its own.Worth a reviewer's attention
partialpast 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.Testing
12 unit tests on the phase derivation (
ingest-phase.test.ts).apps/apiCloudflare service + OAuth suites and the 26 integrations/setup-audit route files pass;apps/webtypechecks.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/cloudflareright 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.
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.