Skip to content

feat(site): checkmydata.ai is a static site on Cloudflare; hosting costs nothing - #443

Merged
sshlg merged 1 commit into
mainfrom
feat/static-site-cloudflare
Oct 11, 2026
Merged

sshlg merged 1 commit into
mainfrom
feat/static-site-cloudflare

Conversation

@sshlg

@sshlg sshlg commented Oct 11, 2026

Copy link
Copy Markdown
Collaborator

What

  • npm run build:site (SITE_EXPORT=1) → static export, served by Cloudflare Worker checkmydata-site (free plan). Worker runs only for /api/*: the waitlist (Resend → contact@checkmydata.ai), rate-limited by a Cloudflare binding.
  • Security headers / retired-route redirects → public/_headers, public/_redirects.
  • deploy.yml: manual, Cloudflare, narrow tokens; verifies the live domain answers as only the Worker can.
  • CI also builds the export.
  • Fixes a feat(site): the hosted product is off; checkmydata.ai is a waitlist #442 regression: the waitlist redirects applied to self-hosted builds too. Plain npm run build is the standalone product again (headers, no redirects); /dashboard/<id> keeps its URL via a rewrite.

Already live

Deployed from this branch and cut over: route checkmydata.ai/* → checkmydata-site (apex is proxied — no DNS change, no downtime). Both Heroku apps at 0 dynos. A test sign-up sent from production reached contact@checkmydata.ai.

Checks run

  • npx tsc --noEmit, npx eslint . --max-warnings=0 — clean
  • npx vitest run — 888/888
  • npm run build:site — 17 pages exported (4.1 MB)
  • backend: test_deploy_pipeline_can_fail.py, test_ci_covers_every_pull_request.py, test_ux_scenarios.py, test_the_ux_base_checks_all_three_documents.py — 37/37; ruff clean
  • Live: / 200, /login → 307 /#waitlist, /api/deploy-probe → Worker JSON 404, empty waitlist POST → 422, CSP/HSTS/X-Frame-Options present

🤖 Generated with Claude Code

…sts nothing

The site's own Heroku dyno was the last running cost. `npm run build:site`
(SITE_EXPORT=1) builds a static export served by the Cloudflare Worker
`checkmydata-site` (free plan); the Worker runs only for /api/* and serves the
waitlist, rate-limited by a Cloudflare binding. Security headers and the
retired-route redirects moved to public/_headers and public/_redirects, which a
static export needs. Both Heroku apps now run 0 dynos.

Deployed and cut over already: route checkmydata.ai/* -> checkmydata-site
(the apex record is proxied, so no DNS change and no downtime); a test sign-up
sent from production reached contact@checkmydata.ai.

Fixes a regression from #442: the waitlist redirects lived in next.config.ts for
every build, so a self-hosted /login redirected to a waitlist. A plain
`npm run build` is the standalone product again, with its headers and no
redirects; /dashboard/<id> keeps its URL through a rewrite to the one page the
export prerenders.

- deploy.yml: manual, Cloudflare; narrow tokens (account Worker scripts; one
  zone's Worker routes); verifies the live domain answers as only the Worker can.
- CI builds the export too, so a page that cannot be exported fails a PR.
- Tests: worker handler (18), static-config consistency, deploy pipeline guards
  rewritten for Cloudflare; scenario citations repointed to DashboardView.tsx.

Brand-Case: site hosting moved to zero cost with no downtime

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@sshlg
sshlg merged commit 01205e5 into main Oct 11, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant