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

+145
View File
@@ -0,0 +1,145 @@
❯ Please create a skeleton project for a typical web application that serves HTML and JSON data. The skeleton
should build out a basic scaffold using python FastAPI, HTMX, and an ORM that can use sqlite or postgres. The
skeleton should also support an admin dashboard, RBAC, and JWT token management interfaces for users. Since this
is a skeleton configuration should be accepted either by file or by environment variables. There should be a
Docker file as well. Please ask any clarifying questions and propose a plan for this project.
● I'll ask a few key questions to shape the skeleton, since several decisions here meaningfully change the
structure.
● User answered Claude's questions:
⎿  · Which ORM / data layer approach do you want? → SQLAlchemy 2.0 async (Recommended)
· How should the admin dashboard be built? → Custom HTMX dashboard (Recommended)
· What auth/JWT depth do you want in the skeleton? → Access + refresh + revocation (Recommended)
· Python tooling / dependency manager? → uv (Recommended)
● Good — all defaults confirmed. Let me present a concrete plan for approval before building.
● Here's my proposed plan for the FastAPI skeleton.
Stack (confirmed)
- FastAPI + Jinja2 + HTMX (server-rendered HTML, JSON API alongside)
- SQLAlchemy 2.0 async + Alembic migrations; SQLite (aiosqlite) or Postgres (asyncpg) via one DATABASE_URL
- JWT: access + refresh tokens with rotation and DB-backed revocation (logout / blocklist)
- RBAC: users ↔ roles ↔ permissions, enforced via FastAPI dependencies
- Custom HTMX admin dashboard (user/role CRUD)
- uv for deps; Docker + docker-compose (app + Postgres)
- Config via pydantic-settings — reads env vars and .env file
Project layout
fastapi_skeleton/
├── app/
│ ├── main.py # app factory, router + middleware wiring, lifespan
│ ├── core/
│ │ ├── config.py # pydantic-settings (env + .env)
│ │ ├── security.py # password hashing, JWT encode/decode
│ │ └── database.py # async engine, session, Base
│ ├── models/ # user, role, permission, refresh_token (revocation)
│ ├── schemas/ # pydantic request/response models
│ ├── crud/ # data-access helpers
│ ├── deps.py # get_db, get_current_user, require_permission(...)
│ ├── api/ # JSON API
│ │ ├── auth.py # register, login, refresh, logout
│ │ ├── users.py # me, user management
│ │ └── router.py
│ ├── web/ # HTML/HTMX routes
│ │ ├── pages.py # login page, dashboard home
│ │ └── admin.py # admin: users, roles, permissions CRUD (HTMX)
│ ├── templates/ # Jinja2: base, partials, admin/*
│ └── static/ # htmx.min.js, minimal css
├── alembic/ + alembic.ini # migrations
├── scripts/seed.py # bootstrap admin user + default roles/perms
├── tests/ # pytest: auth, rbac, smoke
├── pyproject.toml + uv.lock
├── Dockerfile # multi-stage, uv-based
├── docker-compose.yml # app + postgres
├── .env.example
├── .dockerignore / .gitignore
└── README.md
Key design points
- RBAC model: require_permission("users:write") dependency factory; roles aggregate permissions; a superuser flag
bypasses checks. Seed script creates admin/user roles + an initial admin.
- Auth flow: /api/auth/login → access (short) + refresh (long) tokens; /api/auth/refresh rotates and revokes the
old refresh token; /api/auth/logout revokes. Refresh tokens stored hashed in DB with a revoked flag.
- Web vs API auth: API uses Authorization: Bearer; the HTMX UI uses an httpOnly cookie holding the access token so
the dashboard works without JS token handling.
- DB portability: single DATABASE_URL; defaults to SQLite file so it runs with zero setup, Postgres via compose.
- Config: all settings (secret key, token TTLs, DB URL, CORS, admin bootstrap creds) overridable by env var or
.env.
- Tests: pytest + httpx async client against an in-memory SQLite, covering register/login/refresh/logout and an
RBAC permission check.
Build order
1. Tooling + config + Docker scaffolding
2. Database, models, Alembic
3. Security + auth API + revocation
4. RBAC deps + user/role API
5. HTMX templates + admin dashboard
6. Seed script, tests, README
This is a sizable scaffold (~35–40 files). Want me to build it all now, or would you prefer I build it phase by
phase so you can review as I go? I'll start with phase 1 either way unless you'd like to adjust anything above.
● Everything works. Let me verify the hand-written Alembic migration applies cleanly against a fresh database:
● Bash(cd /home/iroddis/dev/fastapi_skeleton
export DATABASE_URL="sqlite+aiosqlite:///./alembic_test.db" SECRET_KEY=test…)
⎿  == upgrade head ==
INFO [alembic.runtime.migration] Context impl SQLiteImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.
… +7 lines (ctrl+o to expand)
● Migrations work both directions. Let me stop the server and clean up:
● Bash(rm -f app.db alembic_test.db /tmp/skel_cookies.txt /tmp/skeleton_server.log; echo "cleaned")
⎿  cleaned
● Stop Task
⎿  SECRET_KEY=test-secret AUTO_CREATE_TABLES=true uv run uvicorn app.main:app --port 8099 · stopped
● Verified and working. Here's what I confirmed:
Results
Static checks
- ruff check . → All checks passed (B008 ignored — that rule fires on FastAPI's Depends()-in-defaults idiom, which
is intentional)
- uv sync --extra dev resolved and installed cleanly on Python 3.14
Test suite — uv run pytest → 7 passed
- Auth lifecycle: register → login → /me → refresh (with rotation) → confirms old refresh token is revoked →
logout invalidates the rotated token
- Wrong password rejected; unauthenticated /me returns 401
- RBAC: reader role can GET but not POST users (403); superuser bypasses checks; user with no roles is forbidden;
registration grants the default user role
Live smoke test (seeded SQLite, real uvicorn server)
- Login page renders; unauthenticated / → 302 redirect to /login
- API bearer-token login works; /api/users/me returns the admin with all permissions; /api/roles → 200
- Web cookie flow: form login → 303 + httpOnly access_token cookie → dashboard and /admin/users render → HTMX
create-user returns the table partial with the new row
- RBAC denial confirmed live: a normal registered user gets 403 on POST /api/users but 200 on GET /api/users
Alembic — the hand-written 0001_initial migration applies (upgrade head creates all 7 tables) and reverses cleanly
(downgrade base).
One fix made during verification
app/services/auth.py was passing datetime.fromtimestamp(payload["exp"]), but PyJWT leaves exp as a timezone-aware
datetime in the payload dict (it only converts to epoch inside the encoded token). Changed it to use
payload["exp"] directly — this was the cause of the initial 5 failures, now all green.
The skeleton is complete and functional. To run it yourself:
uv sync --extra dev
uv run python -m scripts.seed # admin@example.com / changethis
uv run uvicorn app.main:app --reload # http://localhost:8000
Test artifacts (app.db, cookie jar, logs) were cleaned up, and the background server was stopped.