A real API call returned 429 "You have no credits remaining" without the
word "quota", so the UI wrongly said the AI was busy. Match quota,
credit and billing wording; test uses the real message.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- Optional OPENAI_BASE_URL for OpenAI-compatible endpoints or proxies
- Tests run the real openai SDK against a local stub server: request
shape (model, auth, strict json_schema), text cleanup, photo import
and an authentication error mapped to the UI message
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Runs backup.sh/restore.sh against a logging docker stub and checks
archive content, retention, data flow, .env recovery and the order of
stopping/starting the app.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- Lint rules (pycodestyle, pyflakes, bugbear, isort, pyupgrade, bandit)
in pyproject.toml; ruff pinned in requirements-dev
- Full suite verified against PostgreSQL (embedded pgserver), plus a
production-like check: collectstatic with manifest storage, fresh
migrations, ensure_admin and all main pages with DEBUG off
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- backup.sh: pg_dump (custom format), media volume and .env in one
archive with retention (KEEP), cron example
- restore.sh: confirmation, restores .env if missing, pg_restore
--clean in one transaction, replaces media, restarts the app
- Fix: MEDIA_ROOT=/data/media in the image so photos land on the volume
- README: installation, Nginx/Cloudflare client-IP setup, configuration
table, AI and Instagram notes, update, backup, troubleshooting, security
- Test settings independent of the local .env
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- SSRF-safe fetcher: public IPs only, connects to the checked address,
redirects re-validated, size/time limits, honest user agent
- schema.org/Recipe JSON-LD extraction (graphs, HowToSections, ISO
durations, yields, keywords, images) without extra dependencies;
AI fallback on plain page text when no structured recipe exists
- Instagram: one login-free request to the public embed page; when
Instagram withholds the caption, the import page explains how to paste
the text or import a screenshot instead
- Suggested source photo can be downloaded and attached on save
- Text import works without AI (text becomes the description)
- Installable PWA with manifest, icons and Android share target
- Offline tests for SSRF rules, parsers, Instagram and import views
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- recipes/ai.py: Chat Completions with strict JSON schema (Structured
Outputs), Pydantic re-validation, plausibility clamping, prompt treats
input as data only
- "Mit KI aufbereiten" on create/edit form returns an editable draft
- Import page: photo/screenshot (vision, downscaled JPEG, not stored) and
pasted text; drafts pre-fill the recipe form with a preview
- Clear UI messages for timeout, rate limit, quota, auth, model and
connection errors; without OPENAI_API_KEY AI features show a notice
- Optional per-user and global daily AI limits (default unlimited)
- Tests for response parsing, schema strictness, error mapping, views
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- Comments on visible recipes; author and admin may delete; per-user
hourly rate limit (COMMENT_RATE_LIMIT_PER_HOUR)
- Cooking mode with large text, tick-off checklists, serving scaling and
Screen Wake Lock to keep the display on
- Serving calculator auto-submits on change
- Privacy tests cover comment and cooking-mode URLs
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- 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]>
- 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]>