Files
Kochbuch/CLAUDE.md
T
Kyle DammannandClaude Opus 5.5 6f7e4f92ca Add phase 2: recipes with visibility, photos, search and favourites
- Recipe model with ingredients (amount/range, unit, group, note), steps,
  lowercase tags, author notes, source URL and private/public visibility
- Central RecipeQuerySet.visible_to() used by every read path; edit only
  by owner, delete by owner or admin (visible recipes only)
- Text-based editing: ingredient/step parser with roundtrip and scaling
- Photo upload (JPEG/PNG/WebP/HEIC), re-encoded to WebP without EXIF,
  thumbnails, cover image, served only through permission-checked view
- Search across title, description, ingredients, tags; ingredient and tag
  filters; "mine" and favourites views
- Admin can delete all recipes of a deactivated user
- Acceptance tests: private recipes unreachable for others and admins
  in list, search, tags, favourites, detail, edit, delete and image URLs

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

67 lines
4.2 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
- [x] 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, PostgreSQL 17,
gunicorn (gthread), WhiteNoise for static files, Pillow + pillow-heif for photos. No Redis, no HTMX,
no JS build step: plain forms with small progressive enhancements in `static/js/app.js`.
## 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).
- `recipes/` – `Recipe` (+ `Ingredient`, `Step`, `Tag`, `RecipeImage`, `Favorite`). `RecipeQuerySet.visible_to/
editable_by/deletable_by/search` hold all access rules. `parsing.py` converts the text form fields
(one ingredient per line, `Gruppe:` headers, steps separated by blank lines) to structured data and
scales amounts. `images.py` validates/re-encodes uploads (WebP 1600 px + 480 px thumb, no EXIF).
Photos are served only via `recipes:photo` (`/bilder/<uuid>/<gross|klein>.webp`) after a visibility check.
- `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.