diff --git a/.env.example b/.env.example index 9bec51ec64..b724838845 100644 --- a/.env.example +++ b/.env.example @@ -12,6 +12,14 @@ # Get your API key from: https://console.anthropic.com/ ANTHROPIC_API_KEY=your_anthropic_api_key_here +# Cerebras (High-performance inference) +# Get your API key from: https://cloud.cerebras.ai/settings +CEREBRAS_API_KEY=your_cerebras_api_key_here + +# Fireworks AI (Fast inference with FireAttention engine) +# Get your API key from: https://fireworks.ai/api-keys +FIREWORKS_API_KEY=your_fireworks_api_key_here + # OpenAI GPT models # Get your API key from: https://platform.openai.com/api-keys OPENAI_API_KEY=your_openai_api_key_here @@ -59,6 +67,10 @@ XAI_API_KEY=your_xai_api_key_here # Get your API key from: https://platform.moonshot.ai/console/api-keys MOONSHOT_API_KEY=your_moonshot_api_key_here +# Z.AI (GLM models with JWT authentication) +# Get your API key from: https://open.bigmodel.cn/usercenter/apikeys +ZAI_API_KEY=your_zai_api_key_here + # Hugging Face # Get your API key from: https://huggingface.co/settings/tokens HuggingFace_API_KEY=your_huggingface_api_key_here diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000000..09b2067227 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,19 @@ +## Summary + + +## Definition of Done +- [ ] **Shipped**: Merged + deployed +- [ ] **Tested**: Jest unit + integration tests pass; Cypress E2E tests pass +- [ ] **Logged**: Funnel events + audit log entries where applicable +- [ ] **Secured**: Zod validation, RBAC, rate limits, sanitization +- [ ] **Documented**: SETUP.md updated if keys/config changed + +## Test Plan + + +## Checklist +- [ ] No secrets/PII in code or logs +- [ ] Idempotent webhook/job handlers +- [ ] Request size limits enforced +- [ ] Error handling with typed AppError +- [ ] Structured logs with request_id diff --git a/.github/workflows/project-sites.yaml b/.github/workflows/project-sites.yaml new file mode 100644 index 0000000000..ae20dbf20b --- /dev/null +++ b/.github/workflows/project-sites.yaml @@ -0,0 +1,234 @@ +name: Project Sites CI/CD + +on: + push: + branches: [main, staging] + paths: + - 'apps/project-sites/**' + - 'packages/shared/**' + - 'supabase/migrations/**' + pull_request: + branches: [main] + paths: + - 'apps/project-sites/**' + - 'packages/shared/**' + - 'supabase/migrations/**' + +env: + NODE_VERSION: '20.18.0' + +jobs: + # ─── Stage 1: Auto-fix + Lint + Typecheck ────────────────── + check: + name: Typecheck + Lint + Format + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + + - name: Install shared deps + working-directory: packages/shared + run: npm ci --legacy-peer-deps + + - name: Install worker deps + working-directory: apps/project-sites + run: npm ci --legacy-peer-deps + + - name: Typecheck shared + working-directory: packages/shared + run: npm run typecheck + + - name: Typecheck worker + working-directory: apps/project-sites + run: npm run typecheck + + - name: Lint shared + working-directory: packages/shared + run: npm run lint + + - name: Lint worker + working-directory: apps/project-sites + run: npm run lint + + - name: Format check shared + working-directory: packages/shared + run: npm run format:check + + - name: Format check worker + working-directory: apps/project-sites + run: npm run format:check + + # ─── Stage 2: Unit / Functional Tests (Jest) ─────────────── + test-unit: + name: Unit Tests + needs: [check] + runs-on: ubuntu-latest + env: + ENVIRONMENT: test + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + + - name: Install shared deps + working-directory: packages/shared + run: npm ci --legacy-peer-deps + + - name: Install worker deps + working-directory: apps/project-sites + run: npm ci --legacy-peer-deps + + - name: Test shared package + working-directory: packages/shared + run: npm run test:unit -- --coverage + + - name: Test worker package + working-directory: apps/project-sites + run: npm run test:unit -- --coverage + + - name: Upload coverage + uses: actions/upload-artifact@v4 + if: always() + with: + name: coverage-reports + path: | + packages/shared/coverage + apps/project-sites/coverage + + # ─── Stage 3: Deploy to Staging ──────────────────────────── + deploy-staging: + name: Deploy to Staging + needs: [test-unit] + if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/staging' + runs-on: ubuntu-latest + environment: staging + outputs: + deployment-version: ${{ steps.deploy.outputs.version }} + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + + - name: Install worker deps + working-directory: apps/project-sites + run: npm ci --legacy-peer-deps + + - name: Deploy Worker to staging + id: deploy + working-directory: apps/project-sites + run: | + npx wrangler deploy --env staging 2>&1 | tee deploy.log + echo "version=$(date +%s)" >> "$GITHUB_OUTPUT" + env: + CLOUDFLARE_API_TOKEN: ${{ secrets.CF_API_TOKEN }} + + # ─── Stage 4: Playwright E2E on Staging ─────────────────── + e2e-staging: + name: E2E Tests (Staging) + needs: [deploy-staging] + runs-on: ubuntu-latest + env: + BASE_URL: https://sites-staging.megabyte.space + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + + - name: Install worker deps + working-directory: apps/project-sites + run: npm ci --legacy-peer-deps + + - name: Install Playwright browsers + working-directory: apps/project-sites + run: npx playwright install --with-deps chromium + + - name: Run Playwright E2E on staging + working-directory: apps/project-sites + run: npx playwright test + env: + BASE_URL: ${{ env.BASE_URL }} + + - name: Upload test results on failure + uses: actions/upload-artifact@v4 + if: failure() + with: + name: playwright-results-staging + path: apps/project-sites/test-results + + # ─── Stage 5: Deploy to Production ───────────────────────── + deploy-production: + name: Deploy to Production + needs: [e2e-staging] + if: github.ref == 'refs/heads/main' + runs-on: ubuntu-latest + environment: production + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + + - name: Install worker deps + working-directory: apps/project-sites + run: npm ci --legacy-peer-deps + + - name: Deploy Worker to production + working-directory: apps/project-sites + run: npx wrangler deploy --env production + env: + CLOUDFLARE_API_TOKEN: ${{ secrets.CF_API_TOKEN }} + + # ─── Stage 6: Post-deploy E2E on Production ──────────────── + e2e-production: + name: Post-Deploy E2E (Production) + needs: [deploy-production] + runs-on: ubuntu-latest + env: + BASE_URL: https://sites.megabyte.space + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} + + - name: Install worker deps + working-directory: apps/project-sites + run: npm ci --legacy-peer-deps + + - name: Install Playwright browsers + working-directory: apps/project-sites + run: npx playwright install --with-deps chromium + + - name: Run production smoke tests + working-directory: apps/project-sites + run: npx playwright test + env: + BASE_URL: ${{ env.BASE_URL }} + + - name: Upload test results on failure + uses: actions/upload-artifact@v4 + if: failure() + with: + name: playwright-results-production + path: apps/project-sites/test-results + + # ─── Rollback if E2E fails ────────────────────────────── + - name: Rollback on failure + if: failure() + working-directory: apps/project-sites + run: | + echo "::error::Production E2E failed - triggering rollback" + npx wrangler rollback --env production --message "Automated rollback: E2E failed" + env: + CLOUDFLARE_API_TOKEN: ${{ secrets.CF_API_TOKEN }} diff --git a/.gitignore b/.gitignore index 4bc03e175d..67ef3de1f5 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,9 @@ functions/build/ *.vars .wrangler _worker.bundle +package-lock.json +test-results/ +playwright-report/ Modelfile modelfiles diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000000..8e83c789f4 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,281 @@ +# bolt.diy Monorepo — AI Context Guide + +> **Purpose**: This file is the primary AI onboarding document for the bolt.diy monorepo. +> A new Claude Code session reading only this file should understand the project structure, +> development patterns, and how to get productive immediately. + +## Quick Orientation + +This is a monorepo containing: + +1. **Root app** (`/app`): A Remix + Vite web app for bolt.diy (AI code editor), deployed to Cloudflare Pages at `bolt.megabyte.space` +2. **Project Sites Worker** (`/apps/project-sites`): A Cloudflare Worker (Hono) that powers the SaaS website delivery engine at `sites.megabyte.space` +3. **Shared Package** (`/packages/shared`): Zod schemas, constants, RBAC middleware, utilities shared between packages +4. **Database Schema** (`/supabase/migrations/`): Reference Postgres schema (D1 SQLite equivalent used in production) + +The **primary development focus** in recent sessions has been on `apps/project-sites/` and `packages/shared/`. + +## Repository Structure + +``` +bolt.diy/ +├── app/ # Remix frontend (bolt.diy AI code editor) +├── apps/ +│ └── project-sites/ # Cloudflare Worker → sites.megabyte.space +│ ├── src/ +│ │ ├── index.ts # Hono app entry point +│ │ ├── types/env.ts # Env bindings + Variables +│ │ ├── middleware/ # auth, error_handler, payload_limit, request_id, security_headers +│ │ ├── routes/ # api.ts, health.ts, search.ts, webhooks.ts +│ │ ├── services/ # ai_workflows, analytics, audit, auth, billing, db, domains, sentry, site_serving, webhook +│ │ ├── prompts/ # TS infra: parser, renderer, registry, schemas, observability, types +│ │ ├── workflows/ # site-generation.ts (Cloudflare Workflow) +│ │ ├── lib/ # posthog.ts, sentry.ts +│ │ └── __tests__/ # 25 test suites +│ ├── prompts/ # .prompt.md files (YAML frontmatter + # System/# User) +│ ├── public/ # index.html (marketing SPA), static assets +│ ├── e2e/ # Playwright E2E specs +│ ├── wrangler.toml # Worker config (dev/staging/production) +│ ├── jest.config.cjs +│ └── playwright.config.ts +├── packages/ +│ └── shared/ # @project-sites/shared +│ └── src/ +│ ├── schemas/ # Zod: org, site, billing, auth, audit, webhook, workflow, config, analytics, hostname, api +│ ├── middleware/ # RBAC + entitlements +│ ├── utils/ # errors, crypto, sanitize, redact +│ └── constants/ # DOMAINS, AUTH, PRICING, CAPS, ENTITLEMENTS, ROLES +├── supabase/migrations/ # Reference Postgres schema (D1 SQLite used in prod) +├── docs/ # MkDocs documentation site +├── electron/ # Electron desktop wrapper +└── .github/workflows/ # CI/CD pipelines +``` + +## MANDATORY: Test-Driven Development (TDD) + +> **ALL development in this repository MUST follow strict Test-Driven Development.** +> This is NON-NEGOTIABLE. No feature, bug fix, or refactor may be merged without tests. + +### TDD Workflow (Red → Green → Refactor) + +1. **Write failing tests FIRST** — Before writing any implementation code, write unit tests that describe the expected behavior. Run them and confirm they fail. +2. **Write the minimum code to pass** — Implement just enough to make the tests pass. +3. **Refactor** — Clean up the implementation while keeping all tests green. +4. **Write E2E tests** — After the unit tests pass, write Playwright E2E tests that cover the full user flow (see below). +5. **All tests must pass** — Run `npm test` in both `packages/shared/` and `apps/project-sites/`, plus `npx playwright test` for E2E, before considering any work complete. + +### Unit Test Requirements +- Every new function, route handler, or service method MUST have corresponding unit tests +- Test the happy path AND error/edge cases (invalid input, unauthorized, rate limiting, etc.) +- Mock external dependencies (D1, fetch, KV, R2) — never call real APIs in unit tests +- Minimum coverage expectation: all branches of new code must be tested + +### E2E Test Requirements (Playwright) +- Every user-facing feature MUST have E2E tests that cover the **complete user flow** +- E2E flows should cover realistic scenarios end-to-end: + - Loading the marketing homepage + - Searching for a business + - Selecting a result from the dropdown + - Navigating to the details screen + - Signing in (Google OAuth or Email Magic Link) + - Triggering a website build + - Verifying the waiting/progress screen + - Verifying the built site is accessible +- E2E test files live in `apps/project-sites/e2e/` +- Use the custom fixture from `e2e/fixtures.ts` (blocks external CDN requests) +- Run with: `npx playwright test` in `apps/project-sites/` + +### Test Commands +```bash +# Unit tests +cd apps/project-sites && npm test +cd packages/shared && npm test + +# E2E tests +cd apps/project-sites && npx playwright test + +# All checks (unit + typecheck + lint + format) +cd apps/project-sites && npm run check +cd packages/shared && npm run check +``` + +## MANDATORY: Auto-Deploy After Each Session + +> **After completing all changes**, you MUST deploy to staging (and production if on main). +> If `CLOUDFLARE_API_KEY` and `CLOUDFLARE_EMAIL` are not set as environment variables, +> **ask the user to provide them** before deploying. + +### Deploy Checklist +1. Run all unit tests (`npm test` in both packages) — all must pass +2. Run E2E tests (`npx playwright test`) — all must pass +3. Run typecheck (`npm run typecheck`) — no errors +4. Run lint (`npm run lint`) — no errors +5. Deploy to staging: `cd apps/project-sites && npx wrangler deploy --env staging` +6. Upload marketing homepage: `npx wrangler r2 object put project-sites-staging/marketing/index.html --file public/index.html --content-type text/html --remote` +7. If on main branch, also deploy to production after verifying staging + +### Deployment Credentials +- **CLOUDFLARE_API_KEY** and **CLOUDFLARE_EMAIL** must be set as env vars for `wrangler deploy` +- If not available, **ASK THE USER** for these credentials before deploying +- **NEVER** modify secrets that are already set in the Cloudflare dashboard (Stripe keys, SendGrid, Google OAuth, etc.) +- Only use `wrangler secret put` when explicitly asked to set a NEW secret + +## Critical Development Patterns + +### Git / File Operations +- **`.gitignore` blocks `*.md`** — ALWAYS use `git add -f` for markdown files +- Development branch: `claude/setup-cloudflare-workers-gDWiV` +- Never push to main/master without explicit permission + +### Package Management +- pnpm workspace defined but `pnpm install` fails (electron-builder SSH dep) +- **Use `npm install --legacy-peer-deps`** in sub-packages (`apps/project-sites/`, `packages/shared/`) +- Worker dep: `@project-sites/shared` linked via `"file:../../packages/shared"` + +### TypeScript / Build +- All packages use `"type": "module"` in package.json +- `moduleResolution: "Bundler"` in tsconfig +- `.js` extensions in imports (TypeScript resolves them) +- Typecheck: `npx tsc --noEmit` in each package + +### Testing +- **Jest config MUST be `.cjs`** (not `.js` or `.ts`) because `"type": "module"` +- Jest needs `moduleNameMapper: { '^(\\.{1,2}/.*)\\.js$': '$1' }` for TS imports +- Test counts: worker tests (25 suites) + shared tests (6 suites) + E2E (6 files) +- Run: `npm test` in `apps/project-sites/` or `packages/shared/` +- E2E: Playwright, run with `npx playwright test` in `apps/project-sites/` + +### Linting +- Local `eslint.config.mjs` in each sub-package (root `@blitz/eslint-plugin` not available) +- **`console.log` is blocked by eslint** — use `console.warn` for structured logs +- Run: `npx eslint --config eslint.config.mjs src` in each package + +### Scripts (in each sub-package) +```bash +npm test # Run unit tests +npm run test:watch # Watch mode +npm run test:coverage # With coverage +npm run typecheck # tsc --noEmit +npm run lint # ESLint +npm run format # Prettier write +npm run format:check # Prettier check +npm run check # All of the above +``` + +## Architecture at a Glance + +### Project Sites Worker Stack +| Layer | Technology | Purpose | +|-------|-----------|---------| +| Ingress/API | Cloudflare Workers + Hono | API gateway, site serving | +| Database | Cloudflare D1 (SQLite) | System of record | +| Cache | Cloudflare KV | Host resolution (60s TTL), prompt hot-patching | +| Storage | Cloudflare R2 | Static sites, marketing assets | +| Background | Cloudflare Workflows | AI site generation pipeline | +| AI | Cloudflare Workers AI | LLM inference (Llama 3.1) | +| Payments | Stripe | Checkout, subscriptions, webhooks | +| Email | Resend / SendGrid | Magic links, transactional | +| Analytics | PostHog (server-side) | Funnel events | +| Errors | Sentry (HTTP API) | Exception tracking | + +### Key Design Decisions +- **No Supabase JS client** — D1 via parameterized SQL for Workers compat +- **Dash-based subdomains**: `{slug}-sites.megabyte.space` (not nested wildcards) +- **R2 paths**: `sites/{slug}/{version}/{file}`, marketing at `marketing/index.html` +- **Queues NOT yet enabled** — `QUEUE` binding is optional in Env type +- **CSP must include `'unsafe-inline'`** — homepage uses inline ` + + + + + + + + + + + + + + + + + + + +
+ + +We build, launch, and maintain your business website so you don't have to.
+Search for your business to generate a free preview — no account or credit card required.
+ +Free preview includes a full AI-generated site. Pay only when you're ready to go live.
+ + + + +Three steps. Five minutes. Zero technical skills required.
+Type your business name above. We pull in your info from Google and start building a preview instantly — no account needed.
+Our AI researches your business, writes original content, and designs a mobile-first site with local SEO built in. Review and request changes.
+Approve your site and it goes live. We maintain it, keep it updated, and handle any change requests. You focus on your business.
+We set it up, keep it updated, and you can request changes anytime.
+ +As a paid subscriber ($50/mo), you can customize your website anytime using our AI Editor →. Update hours, swap photos, add services — changes go live in real time, no coding required.
+SSL certificates, DDoS protection, and 99.9% uptime — all included. We host everything on Cloudflare, so your site is served from edge locations closest to your visitors for blazing-fast load times worldwide.
+Our templates are built on 15+ years of senior engineering experience. Every site starts with a deep business entity research crawl — then gets schema markup, meta tags, sitemaps, and performance tuning baked in from day one.
+Other platforms give you tools. We give you a finished website — and keep it running.
+ +Everything you need to know before getting started.
+ +Start free. Pay only when you're ready to go live.
+ +See your AI-generated site before committing.
+Everything you need from a business website.
+Have a question, feedback, or need help? We'd love to hear from you.
+ +Create your account to get started.
+ + +Give us a few minutes. We'll notify you when it's ready.
+ + +