Deployment & Infrastructure

The EGAC platform runs entirely on Cloudflare’s free tier. There is no server to maintain — the Worker, D1 database, KV namespace, and cron triggers are all Cloudflare-managed.

Infrastructure overview

flowchart TD subgraph Cloudflare CF[Cloudflare Pages\nAstro SSR + static assets] W[Cloudflare Worker\nAPI routes] D1[(D1 SQLite\nDatabase)] KV[(KV Namespace\nConfig + rate limiting)] CRON[Cron Triggers\n4 schedules] end subgraph External RS[Resend API\nEmail delivery] GH[GitHub repo] end GH -->|git push triggers build| CF CF --> W W --> D1 W --> KV CRON --> W W --> RS

First-time setup

1. Create Cloudflare resources

# Create D1 database
wrangler d1 create egac

# Create KV namespace
wrangler kv:namespace create "KV"

Copy the generated IDs into wrangler.toml:

[[d1_databases]]
binding = "DB"
database_name = "egac"
database_id = "YOUR-D1-ID-HERE"

[[kv_namespaces]]
binding = "KV"
id = "YOUR-KV-ID-HERE"

2. Run database migrations

# Apply all migrations to production
wrangler d1 execute egac --remote --file=db/migrations/0001_initial_schema.sql
wrangler d1 execute egac --remote --file=db/migrations/0002_membership_forms.sql
wrangler d1 execute egac --remote --file=db/migrations/0003_update_academy_waitlist_template.sql
wrangler d1 execute egac --remote --file=db/migrations/0004_coach_app.sql

3. Set secrets

wrangler secret put RESEND_API_KEY   # From resend.com dashboard
wrangler secret put ADMIN_TOKEN      # Strong random string for admin access
wrangler secret put CRON_SECRET      # Strong random string for cron auth

4. Deploy

npm run build
wrangler pages deploy dist --project-name=egac

Or connect to GitHub for automatic deploys on every push.

wrangler.toml reference

name = "egac"
compatibility_date = "2024-09-23"
pages_build_output_dir = "dist"

[vars]
APP_ENV = "production"
EMAIL_FROM = "no-reply@updates.eastgrinsteadac.co.uk"

[[d1_databases]]
binding = "DB"
database_name = "egac"
database_id = "6a053384-20ee-4fee-bf73-aa29c1779fe3"

[[kv_namespaces]]
binding = "KV"
id = "28ecc4df31b44e5e92cc4730c1870829"

Cron trigger configuration

Cron triggers must be configured in the Cloudflare Pages dashboard (not in wrangler.toml for Pages projects):

Workers & Pages → egac → Settings → Functions → Cron Triggers

ExpressionHandlerDescription
0 9 * * */api/cron/expire-invitesDaily 09:00 UTC
0 18 * * 2/api/cron/send-remindersTuesday 18:00 UTC
*/30 * * * */api/cron/retry-invitesEvery 30 minutes
0 9 1 * */api/cron/academy-rollover1st of month 09:00 UTC

Local development

npm install
npm run dev       # Astro dev server on http://localhost:4321

The dev server uses a local D1 database. Run migrations against local D1:

wrangler d1 execute egac --local --file=db/migrations/0001_initial_schema.sql
# ... repeat for other migrations

Seed with test data:

wrangler d1 execute egac --local --file=db/seed.sql

In development, use Authorization: Bearer dev for admin and cron routes.

CI/CD with GitHub

  1. Connect the repository to Cloudflare Pages via the dashboard
  2. Set build command: npm run build
  3. Set output directory: dist
  4. Add the CLOUDFLARE_API_TOKEN environment variable to GitHub Secrets for Wrangler CLI use in CI

Every push to main triggers a production build. Pull requests get automatic preview deployments at unique URLs.

Database migrations

Migrations are plain SQL files in db/migrations/, numbered sequentially. There is no migration runner — apply them manually via Wrangler in order:

# Check what's been applied (no built-in tracker — use naming convention)
wrangler d1 execute egac --remote --command "SELECT name FROM sqlite_master WHERE type='table'"

# Apply a new migration
wrangler d1 execute egac --remote --file=db/migrations/0005_new_feature.sql
warning
D1 is SQLite. Avoid ALTER TABLE ... DROP COLUMN — add new columns with defaults instead, and remove old columns only when confirmed safe. Always test migrations against the local D1 instance before running on production.

Environment variables summary

VariableWhere setDescription
APP_ENVwrangler.toml [vars]production / staging / development
EMAIL_FROMwrangler.toml [vars]Sender email address
RESEND_API_KEYWrangler secretResend API key
ADMIN_TOKENWrangler secretAdmin dashboard access token
CRON_SECRETWrangler secretCron route auth token