EGAC Platform Overview
The EGAC platform is the digital backbone for East Grinstead Athletics Club. It manages the full journey from a parent’s first enquiry through to club membership, handling taster session bookings, the Junior Academy waitlist, automated email communications, and admin reporting — all hosted entirely on Cloudflare’s free tier.
The two athlete paths
Every enquiry is automatically routed into one of two tracks the moment it arrives, based on the athlete’s date of birth and UK Athletics age-group rules.
C —> C1[Send booking invite email\nwith available dates] C1 —> C2[Parent picks a date\nvia /book/:token] C2 —> C3[Booking confirmed\nConfirmation email sent] C3 —> C4[7-day reminder\nsent by cron] C4 —> C5[Admin records attendance] C5 —> C6[Membership invite sent]
D —> D1[Added to academy_waitlist\nWaitlist email sent] D1 —> D2[Season rollover cron\nmoves unplaced entries] D2 —> D3[Admin invites from list] D3 —> D4[Parent responds yes/no\nvia /academy/respond/:token]
Technology stack
| Layer | Technology |
|---|---|
| Runtime | Cloudflare Workers (via Astro SSR) |
| Database | Cloudflare D1 (SQLite) |
| Key-value store | Cloudflare KV |
| Resend API | |
| Frontend | Astro + Tailwind CSS |
| Language | TypeScript (strict) |
| Tests | Vitest |
Repository structure
src/
├── api/ ← Worker request handlers (all HTTP routes)
│ ├── enquiry.ts ← POST /api/enquiry
│ ├── booking.ts ← POST /api/booking
│ ├── academy.ts ← POST /api/academy/respond
│ ├── router.ts ← Single-entry routing switch
│ ├── admin/ ← Admin API handlers (auth-gated)
│ └── cron/ ← Scheduled job handlers
├── lib/
│ ├── business/ ← Pure functions: age, booking, routing
│ ├── repositories/ ← D1 query functions (all DB access)
│ ├── email/ ← Template rendering + Resend client
│ ├── db/ ← Schema types + D1 client
│ ├── auth/ ← Admin token validation
│ └── config.ts ← KV-backed system configuration
├── pages/ ← Astro page components
│ ├── index.astro ← Public landing page
│ ├── book/[token] ← Taster booking UI
│ ├── join.astro ← Membership form
│ ├── admin/ ← Admin dashboard pages
│ └── academy/respond/ ← Academy response UI
├── layouts/ ← Astro layout components
└── middleware/ ← Rate limiting + security headers
db/
└── migrations/ ← SQL migrations (applied via Wrangler)
Key design principles
Age groups are data, not code. All age-group configuration — session days, capacity, booking type — lives in the age_groups D1 table. No slot codes or age thresholds are hardcoded in application logic.
The enquiry is the single source of truth. Every significant action (invite sent, booking created, reminder sent, membership invited) is recorded as a typed event in enquiry.events (a JSON array column). This gives a complete audit trail per athlete.
All cron handlers are also HTTP handlers. Every scheduled job can be triggered via a POST request with a CRON_SECRET bearer token, making them straightforward to test, replay, or trigger manually in an emergency.
Email failures are non-fatal. The platform never lets a Resend API error crash a user-facing request. All email sends are wrapped and their results logged to the structured console, but the parent always gets a success response.