Marketing + local-SEO site for expressrepairs.com.au (Xpress Phone Repairs, Riverwood Plaza). Built with Astro 5, deployed to Cloudflare Pages.
A statically-generated Astro 5 site with a few React islands for the interactive bits:
src/pages/**— routes. The homepage (index.astro) plus a programmatic local-SEO family:repairs/,repairs/[service]/,repairs/[service]/[suburb]/, andblog/[slug]/.src/components/*.astro— static page chrome (nav, footer, CTA, price table).src/components/*.jsx— React islands hydrated only where needed (booking widget, plans toggle, FAQ accordion, contact form).src/data/*— the single source of truth (Zod-validated): services, prices, suburbs, posts, hours, business NAP, and schema.org definitions.functions/api/lead.js— a Cloudflare Pages Function (POST /api/lead) that emails contact/booking submissions to the shop via Resend.astro.config.mjs—@astrojs/react+@astrojs/sitemap,siteset to the production URL,build.format: 'directory'(trailing-slash URLs).
Build output goes to dist/ (git-ignored).
npm install
npm run dev # http://localhost:4321
npm test # vitest (data integrity, SEO helpers, hours, lead API, build output)
npm run build # static build → dist/
npm run preview # serve the built dist/Requires Node ≥ 20 (CI uses Node 22).
The contact form and booking widget POST to /api/lead, which emails the lead
to the shop via Resend. Configure these in the Cloudflare Pages project
(Settings → Environment variables / Secrets):
| Variable | Required | Notes |
|---|---|---|
RESEND_API_KEY |
yes | Resend API key (secret). |
LEAD_TO_EMAIL |
no | Recipient. Defaults to sales@funcovers.com.au. |
LEAD_FROM_EMAIL |
no | Sender on a Resend-verified domain. |
Until RESEND_API_KEY is set the endpoint returns 503 and the form shows a
"please call us" fallback — it never silently swallows a lead.
Production is the Cloudflare Pages project expressrepairs; the custom
domain expressrepairs.com.au is attached there.
- Automatic (normal path): every push to
mainruns.github/workflows/deploy.yml, which builds and runswrangler pages deploy dist --project-name expressrepairs --branch main. Requires repo secretsCLOUDFLARE_API_TOKENandCLOUDFLARE_ACCOUNT_ID. - Manual:
npm run deploy(same command; needswrangler loginor the Cloudflare env vars).
⚠️ Do not runwrangler deploy(the Workers command). It publishes a separate Worker that does not serve the domain and drops thefunctions/Pages Function (the/api/leadendpoint). Always usewrangler pages deploy.
See ../VERIFY.md for the live-site health checks.