Initial Claude implementation

This commit is contained in:
Ian Roddis committed 2026-06-22 14:26:44 -03:00
commit e2d3ab9c2a
63 files changed
+3930

No files matched your search

+144
View File
@@ -0,0 +1,144 @@
# FastAPI Skeleton
A production-shaped starting point for a web app that serves both **HTML (HTMX)** and a **JSON API**.
## Stack
| Concern | Choice |
|----------------|--------|
| Web framework | FastAPI |
| HTML / interactivity | Jinja2 + HTMX |
| ORM | SQLAlchemy 2.0 (async) |
| Migrations | Alembic |
| Database | SQLite (default) or Postgres — one `DATABASE_URL` |
| Auth | JWT access + refresh tokens, rotation, DB-backed revocation |
| Authorization | RBAC: users → roles → permissions |
| Admin UI | Custom HTMX dashboard (user & role CRUD) |
| Config | env vars **or** `.env` (pydantic-settings) |
| Tooling | uv, pytest, ruff |
| Packaging | Dockerfile + docker-compose |
## Layout
```
app/
core/ config, database engine/session, security (hashing + JWT)
models/ SQLAlchemy models: user, role, permission, refresh_token
schemas/ Pydantic request/response models
crud/ data-access helpers
services/ auth token issuance / rotation
deps.py DI: get_db, current user, require_permission(...)
api/ JSON API: auth, users, roles
web/ HTML/HTMX routes: pages + admin dashboard
templates/ Jinja2 templates (+ admin partials)
static/ CSS
alembic/ migration environment + initial migration
scripts/seed.py bootstrap permissions, roles, first superuser
tests/ pytest (auth + RBAC)
```
## Quick start (local, SQLite)
```bash
# 1. Install dependencies (https://docs.astral.sh/uv/)
uv sync --extra dev
# 2. Configure (optional — sensible defaults exist)
cp .env.example .env # then edit SECRET_KEY etc.
# 3. Seed default roles/permissions + the first superuser
uv run python -m scripts.seed
# 4. Run
uv run uvicorn app.main:app --reload
```
Open:
- http://localhost:8000/ — HTMX dashboard (log in with the seeded superuser)
- http://localhost:8000/docs — interactive API docs
- http://localhost:8000/admin/users — admin dashboard
Default superuser (override via env/`.env`): `admin@example.com` / `changethis`.
## Run with Docker (Postgres)
```bash
docker compose up --build
```
This starts Postgres, seeds the database, and serves the app on
http://localhost:8000. The app connects with
`DATABASE_URL=postgresql+asyncpg://app:app@db:5432/app`.
## Configuration
Every setting is read from an environment variable first, then `.env`
(see `.env.example`). Key ones:
| Variable | Default | Notes |
|----------|---------|-------|
| `SECRET_KEY` | dev placeholder | **set a strong value in production** |
| `DATABASE_URL` | `sqlite+aiosqlite:///./app.db` | use `postgresql+asyncpg://...` for Postgres |
| `ACCESS_TOKEN_EXPIRE_MINUTES` | `15` | |
| `REFRESH_TOKEN_EXPIRE_DAYS` | `7` | |
| `AUTO_CREATE_TABLES` | `true` | create tables on startup (dev); use Alembic in prod |
| `COOKIE_SECURE` | `false` | set `true` behind HTTPS |
| `FIRST_SUPERUSER_EMAIL` / `_PASSWORD` | admin@example.com / changethis | used by seed |
## Auth model
- `POST /api/auth/register` — self-service signup (gets the default `user` role)
- `POST /api/auth/login` — OAuth2 password form (`username` = email) → access + refresh
- `POST /api/auth/refresh` — rotates: the presented refresh token is revoked, a new pair is issued
- `POST /api/auth/logout` — revoke one refresh token (body) or all for the user
The JSON API authenticates via `Authorization: Bearer <access>`. The HTMX UI
stores the access token in an httpOnly cookie so pages work without client JS.
Refresh tokens are stored server-side as a SHA-256 hash + `jti` with a `revoked`
flag, so logout and rotation genuinely invalidate them.
## RBAC
`users → roles → permissions`. Permissions follow `resource:action`
(`users:read`, `users:write`, `users:delete`, `roles:read`, `roles:write`).
Protect an endpoint with the dependency factory:
```python
@router.get("", dependencies=[Depends(require_permission("users:read"))])
async def list_users(...): ...
```
Superusers (`is_superuser=True`) bypass all permission checks.
## Migrations (Alembic)
`AUTO_CREATE_TABLES` is convenient for dev/SQLite. For real deployments use
migrations instead:
```bash
# apply the bundled initial migration
uv run alembic upgrade head
# after changing models, autogenerate the next revision
uv run alembic revision --autogenerate -m "describe change"
uv run alembic upgrade head
```
The Alembic env reads `DATABASE_URL` from app settings, so it targets the same
database as the app.
## Tests
```bash
uv run pytest
```
Tests run against an in-memory SQLite database and cover the auth lifecycle
(register/login/me/refresh/logout) and RBAC enforcement.
## Where to go next
- Add your own models in `app/models/`, schemas in `app/schemas/`, routes in `app/api/` or `app/web/`.
- Add permissions to `scripts/seed.py` and guard routes with `require_permission(...)`.
- Swap `HS256` for asymmetric `RS256` keys if you need to verify tokens in other services.