Beeminder autodialer. A single Cloudflare Worker serves the React SPA (as static assets), the user API, and the dialing cron, backed by Workers KV.
- Copy
.env.exampleto.envand set variables npm start— runs the frontend at http://localhost:3000/cd functions && npm run dev— runs the Worker at http://localhost:8787/
- Go to your account settings
- At the bottom of the page, click "Register a new app"
- Name it something like
bm_autodial_dev - Use
http://localhost:3000as the redirect and post-deauthorize callback urls - Copy the client ID into your
.envfile
The Worker serves the built SPA from ../build as static assets, exposes
POST /update and POST /remove (called by the frontend), and runs a
scheduled() cron handler that dials every stored user's goals. In production
the SPA and API share an origin, so the frontend calls /update relatively
(no CORS); local dev is cross-origin (:3000 → :8787), which the Worker's
CORS headers cover.
Run tests:
cd functions/
npm run testRun locally (set DRY_RUN=true in .dev.vars to avoid writing to Beeminder):
npm run devnpm run build # build the SPA into ./build first
cd functions/
npx wrangler kv namespace create USERS # paste the id into wrangler.toml
npx wrangler deploy # deploys the Worker + SPA assets + cronThe cron only dials users present in KV, so before cutover copy the existing
Firestore users collection across (each user's token goes in KV metadata, the
shape getUsers reads):
functions/scripts/migrate-firestore-to-kv.shNeeds gcloud read access to the old Firestore and an authed wrangler. Run it at cutover — it's a point-in-time snapshot; anyone who authorizes on the old system afterward is missed until you re-run it.
Pushes to master deploy via GitHub Actions (.github/workflows/deploy.yaml):
one job builds the SPA and runs wrangler deploy, which uploads the Worker,
the static assets, and the cron trigger together. Requires CLOUDFLARE_API_TOKEN
and CLOUDFLARE_ACCOUNT_ID repo secrets, plus the REACT_APP_APP_URL and
REACT_APP_BM_CLIENT_ID build secrets (REACT_APP_API_URL is left empty in
prod — same origin). The cron cadence is set in functions/wrangler.toml.
Both halves are optional and no-op until their DSN is set:
- Frontend —
REACT_APP_SENTRY_DSNrepo secret (baked into the build). - Worker — a
SENTRY_DSNWorker secret, set once withcd functions && wrangler secret put SENTRY_DSN(not a build var; the deploy doesn't provision it).
Beeminder tokens are scrubbed from URLs before events are sent (redactToken).