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

3.6 KiB
Raw Blame History

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

  • 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

# 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.