English · 中文
A starter kit for building a real SaaS on Cloudflare: marketing pages, a dashboard, an API, teams, and Stripe credits — with sensible defaults and CI out of the box.
A SaaS starter that ships as one Cloudflare Worker:
- Marketing + SEO pages: Astro
- Dashboard app: React SPA (mounted under
/dashboard/*) - API: Hono (
/api/*) - Optional chat: Durable Objects + WebSocket
License: MIT (see LICENSE).
- You want a Cloudflare-first SaaS template (Worker + D1 + KV + R2).
- You prefer Astro for marketing/SEO, but still want a React dashboard.
- You want a “works end-to-end” baseline: auth, teams, billing, admin, CI.
- A public website (SEO pages + blog)
- A logged-in dashboard (React app)
- A backend API (
/api/*) - A database schema + migrations (D1)
- Stripe payments (credits) with webhook-based crediting
- Admin screens (blog/SEO/users/billing helpers)
- Auth: email/password, email verification, sessions (optional Google OAuth)
- Teams: workspace + membership + basic roles/permissions
- Billing (credits): Stripe PaymentIntent + webhook is the source of truth (idempotent)
- Admin: manage blog posts, basic user/billing ops, SEO config, email templates
- i18n:
en(default, no prefix) +zh+ja(prefixed on marketing routes) - CI: lint + typecheck + build + Playwright E2E
- Local:
pnpm devthen openhttp://localhost:4321 - Dashboard entry:
/dashboard - Admin entry:
/admin
- Runtime: Cloudflare Workers
- Web: Astro (SSR) + React (SPA for dashboard)
- API: Hono
- DB: Cloudflare D1 + Drizzle
- Storage: R2 (blog + uploads), KV (sessions + tokens)
- Payments: Stripe
- Email: Resend
Requirements:
- Node.js 20+
- pnpm (repo pins
pnpm@10.0.0inpackage.json)
pnpm install
cp .dev.vars.example .dev.vars
pnpm devOpen http://localhost:4321.
If you don’t need chat/WebSocket, use pnpm dev:web (faster).
src/pages/*: Astro routes (marketing + auth pages)src/pages/dashboard/*: React dashboard shell routessrc/pages/settings/*: legacy convenience routes (redirect to/dashboard/settings/*)src/components/dashboard/*: dashboard React components/pagessrc/api/*: Hono API routes + helperspackages/db/*: Drizzle schema + D1 migrationswrangler*.jsonc: Cloudflare config examples
Expected bindings (names matter):
DB(D1): users, teams, billing, email templates, etc.KV: sessions + one-time tokensBLOG_STORAGE(R2): blog content + uploads (/media/uploads/*)- Durable Objects: chat (only if you enable/run it)
Configured via:
- Local:
wrangler.dev.jsonc+.dev.vars - Production:
wrangler.jsonc+ Cloudflare secrets/vars
Common Worker vars/secrets:
- Stripe:
STRIPE_SECRET_KEY(server)STRIPE_PUBLISHABLE_KEY(browser)STRIPE_WEBHOOK_SECRET(webhook signature)
- Email:
RESEND_API_KEY
- Google OAuth (optional):
GOOGLE_CLIENT_IDGOOGLE_CLIENT_SECRET
- Site URL (recommended in production):
SITE_URL
- Turnstile (captcha; enforced in production):
TURNSTILE_SITE_KEYTURNSTILE_SECRET_KEY
Migrations are in packages/db/src/db/migrations/.
- Local:
pnpm db:migrate:local - Remote:
pnpm db:migrate:remote
“Remote migration” is not merging databases — it just upgrades the tables/columns/indexes on your Cloudflare D1 to match the code.
- Webhook:
POST /api/stripe/webhook - Admin reconcile (when a webhook was missed):
POST /api/admin/stripe/reconcile
This template is intentionally “webhook-first”: credits are granted by the webhook, not by the browser redirect page.
Dashboard includes “Delete account” (requires password).
- Data is hard-deleted.
- If a user is the only owner of a team, the team is deleted too.
Workflow: .github/workflows/deploy.yml.
Required GitHub Secrets:
CLOUDFLARE_API_TOKENCLOUDFLARE_ACCOUNT_ID
Deploy triggers:
- Push a version tag (e.g.
v1.2.3) - Or run it manually via
workflow_dispatch
git tag v0.1.0
git push origin v0.1.0Tip: creating a GitHub Release usually creates the tag too.
Deploy does:
- apply remote D1 migrations
- deploy the Worker
/settingsis a redirect stub (it sends you to/dashboard/settings/*).StripeAuthenticationError: Invalid API Keyusually means your.dev.varshas a placeholder/typo forSTRIPE_SECRET_KEY.- If CI says “set
CLOUDFLARE_API_TOKEN”, that’s expected for remote D1 migrations in non-interactive runners.
See CONTRIBUTING.md.
We use GitHub’s built-in security reporting (Security Advisories / “Report a vulnerability”).