diff options
| author | Tom Flux <tom@tomflux.xyz> | 2026-06-23 20:37:48 +0100 |
|---|---|---|
| committer | Tom Flux <tom@tomflux.xyz> | 2026-06-23 20:37:48 +0100 |
| commit | d92deeab514a52e7c3416baef78f8969ade951cc (patch) | |
| tree | f5c071147ea6f62501dee904a1f381805a1e5941 /kitchen/views_htmx.py | |
| parent | 682c0f76b85950ad0889e11750dc84e0f00d2807 (diff) | |
Phase 2: session auth for the web UI
Require login for /app/, with a long sliding session so you log in
once per device and effectively stay in.
- AppLoginRequiredMiddleware gates only /app/; /api/ keeps DRF token
auth and /admin/ keeps its own login (a blanket LoginRequired would
break token requests, whose user isn't resolved until the view runs).
- Login page (styled to the dark palette) via django.contrib.auth.urls;
logout control in the nav.
- Session: ~1 year cookie, sliding (saved every request), survives
browser close.
- Dropped every @csrf_exempt now that a real session + CSRF token are
in place (HTMX already sends X-CSRFToken).
- SECRET_KEY and DEBUG now read from the environment (prod-safe
defaults); systemd loads an optional /var/lib/food/.env.
- Tests authenticate, plus new coverage: /app/ redirects when logged
out, login grants access, /api/ is not caught by the app gate.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Diffstat (limited to 'kitchen/views_htmx.py')
| -rw-r--r-- | kitchen/views_htmx.py | 9 |
1 files changed, 0 insertions, 9 deletions
diff --git a/kitchen/views_htmx.py b/kitchen/views_htmx.py index 21eec8a..9e1838e 100644 --- a/kitchen/views_htmx.py +++ b/kitchen/views_htmx.py @@ -7,7 +7,6 @@ from decimal import Decimal from django.http import HttpResponse from django.shortcuts import render, get_object_or_404 -from django.views.decorators.csrf import csrf_exempt from django.views.decorators.http import require_POST, require_http_methods from .models import ( @@ -221,7 +220,6 @@ def log_page(request): # --- HTMX Actions --- -@csrf_exempt @require_POST def pantry_add(request): """Add an item (or restock an existing one to 'in'). Quantity is optional.""" @@ -264,7 +262,6 @@ def pantry_add(request): return render(request, "kitchen/partials/pantry_table.html", _pantry_context()) -@csrf_exempt @require_POST def pantry_set_state(request, item_id): """Set an item's In/Low/Out state — the primary pantry interaction.""" @@ -289,7 +286,6 @@ def pantry_search(request): ) -@csrf_exempt @require_http_methods(["DELETE"]) def pantry_delete(request, item_id): item = get_object_or_404(PantryItem, id=item_id) @@ -298,7 +294,6 @@ def pantry_delete(request, item_id): return render(request, "kitchen/partials/pantry_table.html", ctx) -@csrf_exempt @require_POST def pantry_move(request, item_id): """Move an item between fridge / freezer / cupboard.""" @@ -322,7 +317,6 @@ def pantry_move(request, item_id): return render(request, "kitchen/partials/pantry_table.html", _pantry_context()) -@csrf_exempt @require_POST def pantry_save_expiry(request, item_id): """Set or clear an item's expiry date (inline editor in the item menu).""" @@ -332,7 +326,6 @@ def pantry_save_expiry(request, item_id): return render(request, "kitchen/partials/pantry_table.html", _pantry_context()) -@csrf_exempt @require_POST def shopping_generate(request): """Generate smart shopping list and return updated HTML.""" @@ -420,7 +413,6 @@ def shopping_generate(request): }) -@csrf_exempt @require_POST def shopping_toggle(request, item_id): item = get_object_or_404(ShoppingListItem, id=item_id) @@ -430,7 +422,6 @@ def shopping_toggle(request, item_id): return render(request, "kitchen/partials/shopping_list.html", {"items": _shopping_list_items()}) -@csrf_exempt @require_POST def shopping_clear(request): ShoppingListItem.objects.filter(checked=True).delete() |
