Files
beanflows/web/CLAUDE.md
Deeman 2748c606e9 Add BeanFlows MVP: coffee analytics dashboard, API, and web app
- Fix pipeline granularity: add market_year to cleaned/serving SQL models
- Add DuckDB data access layer with async query functions (analytics.py)
- Build Chart.js dashboard: supply/demand, STU ratio, top producers, YoY table
- Add country comparison page with multi-select picker
- Replace items CRUD with read-only commodity API (list, metrics, countries, CSV)
- Configure BeanFlows plan tiers (Free/Starter/Pro) with feature gating
- Rewrite public pages for coffee market intelligence positioning
- Remove boilerplate items schema, update health check for DuckDB
- Add test suite: 139 tests passing (dashboard, API, billing)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 16:11:50 +01:00

4.5 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

What This Is

A Copier template for generating SaaS applications. The template lives in {{project_slug}}/ with Jinja2-templated files (.jinja extension). Files without .jinja are copied as-is. The copier.yml defines template variables: project_slug, project_name, description, author_name, author_email, base_url, payment_provider (stripe/paddle/lemonsqueezy).

This is NOT a runnable application itself — it generates one via copier copy.

Generated Project Commands

After generation, the project uses uv as package manager:

uv sync                                          # Install dependencies
uv run python -m <slug>.migrations.migrate        # Initialize/migrate DB
uv run python -m <slug>.app                       # Run dev server (port 5000)
uv run python -m <slug>.worker                    # Run background worker
uv run python -m <slug>.worker scheduler           # Run periodic scheduler
uv run pytest                                     # Run tests
uv run ruff check .                               # Lint
docker compose up -d                              # Production deploy

Stack

  • Quart (async Flask) with Jinja2 templates and Pico CSS (no build step)
  • SQLite with WAL mode + aiosqlite (no ORM — plain SQL everywhere)
  • Stripe, Paddle, or LemonSqueezy for billing (chosen via payment_provider template variable)
  • Resend for transactional email
  • Litestream for SQLite replication/backups
  • Docker + Caddy for deployment
  • Hypercorn as ASGI server in production

Architecture

Domain-based flat structure

Each domain is a directory with routes.py + templates/. Routes, SQL queries, and decorators all live together in routes.py — no separate models/services/repositories layers.

src/<slug>/
  app.py          → Application factory, blueprint registration, middleware
  core.py         → Config class, DB helpers (fetch_one/fetch_all/execute), email, CSRF, rate limiting
  worker.py       → SQLite-based background task queue (no Redis)
  auth/           → Magic link auth, login_required/subscription_required decorators
  billing/        → Checkout/webhooks/portal (Stripe, Paddle, or LemonSqueezy), plan feature/limit checks
  dashboard/      → User settings, API key management
  public/         → Marketing pages (landing, terms, privacy)
  api/            → REST API with Bearer token auth (api_key_required decorator)
  admin/          → Password-protected admin panel (ADMIN_PASSWORD env var)
  migrations/     → schema.sql + migrate.py (runs full schema idempotently with CREATE IF NOT EXISTS)

Key patterns

  • Database access: Use fetch_one(), fetch_all(), execute() from core.py. No ORM. Queries live directly in route files next to the routes that use them.
  • Auth decorators: @login_required for user pages, @subscription_required(plans=["pro"]) for plan-gated features, @api_key_required(scopes=["read"]) for API endpoints.
  • CSRF: @csrf_protect decorator on POST routes. Templates include <input type="hidden" name="csrf_token" value="{{ csrf_token() }}">.
  • Background tasks: from ..worker import enqueue then await enqueue("task_name", {"key": "value"}). Register handlers with @task("task_name") decorator in worker.py.
  • Blueprints: Each domain registers as a Quart Blueprint with its own template_folder. Templates in domain dirs override shared ones.
  • Soft deletes: deleted_at column pattern. Use soft_delete(), restore(), hard_delete() from core.py.
  • Dates: All stored as ISO 8601 TEXT in SQLite, using datetime.utcnow().isoformat().
  • Rate limiting: SQLite-based, no Redis. @rate_limit() decorator or check_rate_limit() function.
  • Migrations: Single schema.sql file with all CREATE TABLE IF NOT EXISTS. Run via python -m <slug>.migrations.migrate.

Conditional template blocks

billing/routes.py.jinja conditionally generates the full billing implementation based on the payment_provider variable (stripe/paddle/lemonsqueezy). The .env.example.jinja, core.py.jinja, and schema.sql.jinja similarly adapt per provider.

Design Philosophy

Data-oriented, minimal abstraction. Plain SQL over ORM. Write code first, extract patterns only when repeated 3+ times. SQLite as the default database. Server-rendered HTML with Pico CSS.

Ruff Config

Line length 100, target Python 3.11+, rules: E, F, I, UP (ignoring E501).