Skip to content

feat(api,web): real-time newsletter progress via Durable Objects#6

Merged
Irere123 merged 1 commit into
stagingfrom
feat/newsletter-realtime-durable-objects
Jun 28, 2026
Merged

feat(api,web): real-time newsletter progress via Durable Objects#6
Irere123 merged 1 commit into
stagingfrom
feat/newsletter-realtime-durable-objects

Conversation

@Irere123

Copy link
Copy Markdown
Owner

Summary

Adds a real-time layer for newsletter sends and subscriber stats, built on two SQLite-backed Cloudflare Durable Objects surfaced over WebSockets.

  • CampaignProgress (per campaign): coordination point for a send. The email worker seeds totals via start(), each batch reports completion (idempotent by batchIndex), and analytics/unsubscribe feed engagement. When the final batch lands it marks the campaign SENT and records totalSent in D1 — the queue fan-out previously had no "all batches done" signal, so campaigns were stuck on SENDING and totalSent was always 0 (click rate always 0%).
  • WriterStats (per writer): subscriber lifecycle mutations re-read authoritative counts from D1 and broadcast them.

Cookie-authenticated WS routes (/realtime/campaigns/:id, /realtime/subscribers) forward to the DOs using the hibernation API. All notifiers are best-effort, so realtime can never break a send or mutation.

Frontend adds a reconnecting WS channel hook plus useCampaignRealtime / useSubscriberStatsRealtime: a live send progress bar + engagement on the campaign detail page, and live subscriber stat cards that refresh the list as people subscribe, confirm, and unsubscribe.

Validation

  • API tsc --noEmit: clean.
  • wrangler --dry-run bundles the worker with both DOs registered; wrangler types accepted the bindings + v1 migration.
  • Web typecheck shows only pre-existing cross-project warnings (verified against a stashed baseline); no new real errors.

Notes

  • On wrangler deploy --env staging, the v1 DO migration applies on first deploy.
  • Open-tracking remains a placeholder (no open_events table), so the live Opens counter reflects pixel hits for the current send session only.

🤖 Generated with Claude Code

Add a realtime layer for newsletter sends and subscriber stats built on
two SQLite-backed Cloudflare Durable Objects, surfaced over WebSockets.

- CampaignProgress (per campaign): coordination point for a send. The
  email worker seeds totals via start(), each batch reports completion
  (idempotent by batchIndex), and analytics/unsubscribe feed engagement.
  When the final batch lands it marks the campaign SENT and records
  totalSent in D1 — the queue fan-out previously had no "all batches
  done" signal, so campaigns were stuck on SENDING and totalSent was 0.
- WriterStats (per writer): subscriber lifecycle mutations re-read
  authoritative counts from D1 and broadcast them.

Cookie-authenticated WS routes (/realtime/campaigns/:id, /realtime/
subscribers) forward to the DOs using the hibernation API. All notifiers
are best-effort so realtime can never break a send or mutation.

Frontend adds a reconnecting WS channel hook plus useCampaignRealtime /
useSubscriberStatsRealtime: a live send progress bar + engagement on the
campaign detail page, and live subscriber stat cards that refresh the
list as people subscribe, confirm, and unsubscribe.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Irere123 Irere123 merged commit af4a68c into staging Jun 28, 2026
2 checks passed
@Irere123 Irere123 deleted the feat/newsletter-realtime-durable-objects branch June 28, 2026 10:29
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