Files
Kochbuch/CLAUDE.md
T
Kyle DammannandClaude Opus 5.5 ef9deaeff7 Add phase 1: Docker setup, auth with invitations and roles
- Django 5.2 + PostgreSQL 17 via docker compose (app + db, volumes,
  healthcheck on /healthz, test profile running pytest against Postgres)
- Custom e-mail user model with user/admin roles
- Invitation-only registration with single-use, hashed, expiring links
- Admin area: invitations, role changes, deactivation, password reset
  via one-time link or temporary password (forced change on login)
- First admin created from ADMIN_EMAIL/ADMIN_PASSWORD on startup
- Login rate limiting per e-mail and IP (DB-backed, worker-safe)
- Proxy middleware honouring X-Forwarded-* only from TRUSTED_PROXIES,
  optional CLIENT_IP_HEADER (e.g. CF-Connecting-IP)
- Security headers (CSP, HSTS, Permissions-Policy), secure __Host- cookies
- Mobile-first German UI, CLAUDE.md, README, init-env script
- 65 tests for auth, permissions, proxy handling and rate limiting

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-30 21:24:59 +01:00

61 lines
3.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Kochbuch – Notes for Claude
Self-hosted recipe web app for a homelab. Runs via docker compose behind the owner's existing
Nginx + Cloudflare (public URL: https://kochbuch.dammann.vip). The app itself serves plain HTTP on one
port; TLS, domain and proxy are out of scope.
## Status
- [x] Phase 1: Docker setup, DB, auth with invitations, roles
- [ ] Phase 2: Recipes CRUD, visibility (private/public), photos, notes, search, favourites
- [ ] Phase 3: Comments, serving calculator, cooking mode (wake lock)
- [ ] Phase 4: AI (OpenAI): clean up manual input + photo/screenshot import
- [ ] Phase 5: Link import (schema.org JSON-LD, Instagram with paste fallback, Android PWA share target)
- [ ] Phase 6: Backup script, full README
## Stack
Django 5.2 LTS (Python 3.12), server-rendered templates, HTMX planned for Phase 2+, PostgreSQL 17,
gunicorn (gthread), WhiteNoise for static files. No Redis, no JS build step.
## Layout
- `config/` – settings (all env-driven, see `.env.example`), urls, wsgi. `settings_test.py` for pytest.
- `core/` – cross-cutting: `ProxyMiddleware` (trusted proxies, client IP), `SecurityHeadersMiddleware`
(CSP, Permissions-Policy, no-store for logged-in pages), DB-backed rate limiter (`core/ratelimit.py`),
`/healthz`, error pages.
- `accounts/` – custom `User` (e-mail login, `role` = user|admin), `Invitation`, `PasswordResetToken`,
login/registration/reset views, admin area (`admin_views.py`, `/verwaltung/…`),
`ensure_admin` management command (first admin from env).
- `templates/`, `static/` – global templates, `app.css` (mobile first, dark mode), `app.js`.
- `docker/entrypoint.sh` – migrate → ensure_admin → purge_ratelimits → gunicorn.
- `scripts/` – `init-env.sh` (creates .env with secrets), `dev_server.py` (local run without Docker).
## Commands
```bash
# Local (Windows dev box: venv lives at C:\Users\kyled\.venvs\kochbuch due to path-length issues)
python -m pytest # tests (SQLite)
python scripts/dev_server.py # http://localhost:8000, [email protected]
SECRET_KEY=dev python manage.py makemigrations
# Docker
./scripts/init-env.sh [email protected] # once
docker compose up -d --build
docker compose --profile test run --rm test # tests against Postgres
docker compose logs -f app
```
## Conventions
- Code, comments, commits: English. UI, README, user-facing messages: German (informal "du").
- **Default deny:** `LoginRequiredMiddleware` is active; public views need `@login_not_required`.
Admin views use `admin_required` (403). Private content that a user may not see → 404, never 403
(don't leak existence).
- **Visibility (Phase 2+):** every recipe query for a user goes through one central queryset method
(`visible_to(user)`), including list, detail, search, tags, images, comments. Uploaded files are never
served from a static path – only through permission-checked views. Tests must cover each access path.
- Tags are stored lowercase.
- One-time tokens (invitations, resets) are stored only as SHA-256 hashes; the link is shown once.
- Rate limits use `core.ratelimit.hit/is_limited`; limit 0 = unlimited. AI limits (Phase 4) must exist
but default to 0 (owner's request), configurable via env.
- Forwarded headers are trusted only from `TRUSTED_PROXIES`; gunicorn does not trust any itself.
- CSP is strict (`script-src 'self'`, `style-src 'self'`): no inline scripts/styles/handlers in templates.
- AI output is always an editable draft, never saved directly. `OPENAI_API_KEY` only via env.
- Keep it lean: small server, no heavy dependencies without reason.