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>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
073ab85f30
commit
6bad2a2ad1
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user