Skip to content

Send contact form email via Cloudflare send_email binding - #5

Merged
blindtechmage merged 1 commit into
mainfrom
feature/contact-form-cloudflare-email
Aug 9, 2026
Merged

Send contact form email via Cloudflare send_email binding#5
blindtechmage merged 1 commit into
mainfrom
feature/contact-form-cloudflare-email

Conversation

@blindtechmage

Copy link
Copy Markdown
Owner

Summary

  • Replaces the Gmail API/OAuth2 contact-form email path with Cloudflare's native Email Routing send_email Worker binding (legacy EmailMessage/mimetext API)
  • This Cloudflare account doesn't have the Workers Paid plan the newer "Email Sending" product requires (confirmed via a live 2036 Unauthorized API response), so the classic binding is used instead — free on the current plan
  • Binding is restricted via destination_address to a single, already-verified destination address (blindtechmage+jwauthier@gmail.com), so no sending-domain SPF/DKIM onboarding is needed and no third-party (Google OAuth) credentials are involved in delivery
  • Verified end to end with a real send, via a temporary local wrangler dev --remote session (torn down afterward, not part of this PR)

Test plan

  • npm run typecheck passes
  • npm run lint:js passes
  • npm run test:unit passes (26 tests) — includes new tests/unit/contact/email.test.ts (MIME message construction) and tests/unit/contact/route.test.ts (full POST /api/contact pipeline against mocked EMAIL/RATE_LIMIT bindings and mocked fetch for reCAPTCHA)
  • npm run build succeeds; confirmed send_email binding and nodejs_compat flag appear in the generated dist/server/wrangler.json
  • Live send confirmed manually (real email delivered to the verified destination address)

Not yet functional in production

Still requires manual provisioning outside this repo: the RATE_LIMIT KV namespace and a reCAPTCHA site/secret key pair. See docs/SECURITY.md Open Items.

🤖 Generated with Claude Code

Replaces the stalled Gmail API/OAuth2 approach with Cloudflare's native
Email Routing send_email binding (legacy EmailMessage/mimetext API).
This account doesn't have the Workers Paid plan the newer Email Sending
product requires, so the classic binding is used instead, restricted to
an already-verified destination address — free, and no third-party
credentials needed.

Verified end to end with a live send via a temporary local wrangler dev
session in remote mode. Adds route-level unit tests for the full
POST /api/contact pipeline (validation, reCAPTCHA, rate limit, send)
against mocked bindings, plus cloudflare:email/cloudflare:workers test
shims since those are Workers-runtime-only virtual modules.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@blindtechmage
blindtechmage merged commit 529a4ba into main Aug 9, 2026
4 checks passed
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.

2 participants