feat: auth by default, theme toggle, CoderPad startup, review fixes

- Require a session on every route; public routes opt out with @allow_anonymous
- Split password hashing and pepper loading into passwords.py
- Add a system/light/dark theme toggle backed by light-dark() colors
- Ignore stale 401s from an earlier session, PATCH only changed book fields,
  and block overlapping journal-entry saves
- Add bin/start and CoderPad Vite server settings for the pad's start/restart
- Rewrite README as a mise onboarding guide; expand .gitignore
- Include review-round fixes and tests
This commit is contained in:
2026-10-02 16:49:14 -05:00
parent 9d7b0e805c
commit 7f5d034a1f
46 changed files with 1797 additions and 497 deletions
+54 -41
View File
@@ -1,58 +1,71 @@
# My Library: Personal Book Collection Tracker
# My Library
Flask + React + PostgreSQL. Users catalog their books, track reading progress, keep a reading journal per
book, and search/filter their library. Each user sees only their own data.
A personal book tracker. You sign up, add the books you own, track how far you are in each one, and keep a
reading journal for every book. You can search your library by title or author and filter it by genre. Each
user sees only their own books.
## Run (CoderPad)
It is a Flask (Python) API with a React (TypeScript) frontend, backed by PostgreSQL.
The pad starts Flask (`backend/app.py`, port 5000) and Vite automatically. The schema is created on startup.
Optionally set `PASSWORD_PEPPER` (≥ 32 chars). Without it, a development pepper is generated into
`backend/.pepper` and a warning is logged.
## What you need
## Run (local)
- [mise](https://mise.jdx.dev) – installs the right versions of Python, Node and the other tools for you
- [Docker](https://www.docker.com) – runs the PostgreSQL database
## First-time setup
```bash
mise trust && mise install # Python 3.14, Node 24
docker compose up -d db # postgres:16 on 127.0.0.1:5432
until docker compose exec -T db pg_isready -U postgres; do sleep 1; done
mise exec -- python -m venv .venv && .venv/bin/pip install -r backend/requirements.txt
mise exec -- npm install
.venv/bin/python backend/app.py # API on :5000
mise exec -- npm run dev # UI; /api is proxied to Flask
mise trust # allow mise to use this project's mise.toml
mise install # install Python, Node, uv, ruff and biome
mise run setup # create the Python virtual environment and install all dependencies
```
## Test
## Run the app
```bash
.venv/bin/python -W error -m unittest discover -s backend/tests -t backend -v # real Postgres (books_test); run from repo root
mise exec -- npm test && mise exec -- npm run typecheck
mise run dev
```
## Design
This starts the database, the API (port 5000) and the web app. Open http://localhost:5173 and create an
account. Press `Ctrl+C` to stop it.
- **Feature slices.** One Flask Blueprint per feature: `auth.py`, `books.py` and `notes.py` each hold their
routes, validation and SQL. `db.py` holds the pool and transactions; `validation.py` holds the shared input rules.
- **Isolation in SQL.** Every query filters on the session's user (`AND user_id = %s`, or joins to `books`
for notes). Another user's book and a missing book return the same 404.
- **Passwords.** Peppered PBKDF2-HMAC-SHA256 (FIPS-approved, stdlib only). See [ADR-0001](docs/adr/0001-password-hashing-pbkdf2.md).
- **Sessions.** Opaque tokens hashed at rest; 30-minute idle and 12-hour absolute expiry; real logout; CSRF
handled by SameSite plus a JSON-only rule. See [ADR-0002](docs/adr/0002-server-side-sessions.md).
- **Data rules in two places.** The API returns specific 400s, and Postgres CHECK and FK constraints
backstop them.
- **Frontend.** React Router (search and filter state lives in the URL), a small auth context, Tailwind.
No state library.
You can also start the parts on their own: `mise run db`, `mise run dev:api` and `mise run dev:web`.
Full spec: [docs/superpowers/specs/2026-10-02-book-tracker-design.md](docs/superpowers/specs/2026-10-02-book-tracker-design.md)
To stop the database when you are done, run `docker compose down`.
## Known trade-offs
## Run the tests
| Not included | Add when |
|---|---|
| Pagination | libraries exceed a few hundred books |
| Trigram/full-text index | ILIKE search slows (thousands of books) |
| Login rate limiting | before production |
| Migrations tool | schema changes after first deploy |
| Pepper rotation | before production key management |
```bash
mise run test # everything
mise run test:py # backend only
mise run test:ts # frontend only, plus the type check
```
A request that fails with 4xx rolls back its transaction, including the session idle-window slide, so failed
requests don't extend the 30-minute idle timeout.
The backend tests use a real PostgreSQL database called `books_test`. It is created for you, so the database
from `mise run db` just needs to be running (the test task starts it).
## Lint and format
```bash
mise run format # fix formatting and import order (writes changes)
mise run lint # check linting, formatting and types without changing files
```
Run `mise run lint` and `mise run test` before you push.
## Change backend dependencies
Edit `backend/requirements.in`, then run:
```bash
mise run lock # rebuild the pinned backend/requirements.txt
mise run setup # install the new versions
```
## Settings
- `DATABASE_URL` – the PostgreSQL connection string. It defaults to the local Docker database.
- `PASSWORD_PEPPER` – a secret of at least 32 characters used when storing passwords. If you do not set it,
a development value is created in `backend/.pepper` and a warning is logged. Always set it outside local
development.
Run `mise tasks` to see every available task.