How the app is built
High-level architecture notes for developers and curious admins.
Stack
- Astro 6 SSR on Cloudflare Pages for the web UI at
volunteers.trinitaschurch.com. - Cloudflare D1 (SQLite) for the database.
- Cloudflare Workers for the email worker, the weekly-reminder cron worker, and the Tail worker (error forwarding).
- Cloudflare Email Service for transactional email from
deacons@trinitaschurch.com. - Mailchimp for the weekly reminder campaign (pluggable — see below).
- Tailwind with the same design tokens as the sibling
trinitaschurch/websiterepo.
Three Workers, linked by Service Bindings
volunteers-web ──RPC──▶ volunteers-email
│ ▲
│ (D1 binding) │ RPC
▼ │
D1 ◀──(D1 binding)─── volunteers-scheduler (cron: Sun 18:00 UTC)
volunteers-web— everything users see. Auth, admin, CRUD, UI.volunteers-email— the only Worker that imports email provider SDKs and the only writer of theemail_campaigns/email_messagestables.volunteers-scheduler— a tiny cron Worker that fires once a week, reads next Sunday’s assignments, and calls the email Worker to queue the reminder campaign.volunteers-tail— Tail Worker bound as an error consumer on the other three; posts to a deacons’ Slack channel.
Email provider plugin layers
Three swappable layers, each selected via env flag:
- Send (transactional, per-message):
cf_emaildefault,resend/mandrill/mailchimp_transactionalavailable. - Campaign (scheduled fan-out):
direct(per-recipient via Layer 1) ormailchimp(Marketing API with IF/ELSEIF). Selectable per-kind. - Audience sync (list membership):
mailchimpornoop.
To remove Mailchimp entirely: flip the campaign and audience providers, delete the mailchimp.ts files, drop the API key. No other code changes.
Observability
- Workers Logs with structured JSON lines.
- Logpush to R2 bucket
trinitas-church-logsfor 30-day retention. - Workers Analytics Engine custom dataset with dimensional metrics on every send and every schedule generation.
- Web Analytics (privacy-friendly page views).
- D1 Insights (built-in query stats).
- Health Check on a
/_healthzendpoint that flips to 503 if the weekly cron hasn’t fired in 8 days. - Tail Worker forwards unhandled errors to Slack within minutes.
Database schema
Nineteen tables (users, roles, availability, services, assignments, coverage_requests, swap_proposals, groups, group_members, applications, application_steps, …). The full schema is in migrations/0001_init.sql in the repo.
Repo
Source at github.com/trinitaschurch/volunteers. Private. Infra in github.com/trinitaschurch/infra.