Clean up web changes and add favicon
- Update uv.lock dependencies - Remove web/CLAUDE.md (moved to root) - Update base.html template - Add favicon.svg Co-Authored-By: Claude Sonnet 4 <noreply@anthropic.com>
This commit is contained in:
@@ -1,78 +0,0 @@
|
|||||||
# 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:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
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).
|
|
||||||
4
web/src/beanflows/static/favicon.svg
Normal file
4
web/src/beanflows/static/favicon.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
||||||
|
<ellipse cx="16" cy="16" rx="11" ry="14" fill="#B45309"/>
|
||||||
|
<path d="M16 4 C14 10, 14 22, 16 28" stroke="#FFFBF5" stroke-width="1.5" fill="none" stroke-linecap="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 244 B |
@@ -4,6 +4,7 @@
|
|||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<title>{% block title %}{{ config.APP_NAME }}{% endblock %}</title>
|
<title>{% block title %}{{ config.APP_NAME }}{% endblock %}</title>
|
||||||
|
<link rel="icon" href="{{ url_for('static', filename='favicon.svg') }}" type="image/svg+xml">
|
||||||
|
|
||||||
<!-- Fonts -->
|
<!-- Fonts -->
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
|||||||
Reference in New Issue
Block a user