Files
web_django_template/site/public/middleware.py
T
westfarn f3e34f5099 Add shopper accounts, reviews, tracking, and seed_demo (#10)
## Summary
- Closes #9. Port shop-gated buyer accounts (register/login/profile, password reset, order history), purchase-gated reviews, and Stripe customer ids. Cards stay on Stripe.
- Slim public contact form to email + interest + message. Shipping tracking status, scan events, paste-in numbers, and EasyPost webhook when FEATURE_SHIPPING is on.
- Add template-neutral `seed_demo` (generic SKUs, demo shoppers, guest orders, tracking stubs). Prod refused. `--reset` deletes tagged demo rows after invoices.

## Test plan
- [ ] `cd site && uv run python manage.py test` (173 tests passed locally)
- [ ] Shop on: `/account/register`, login, profile, password reset, claim guest orders by email
- [ ] Shop off: `/account/` URLs absent; Sign in / Account links hidden
- [ ] Checkout as logged-in buyer stores `Order.user` and Stripe customer id; product review only after paid purchase
- [ ] Shipping on: portal paste-in tracking + EasyPost webhook updates status; shipping off: orders still work without tracking UI
- [ ] `seed_demo` refused in prod; `--reset` rebuilds tagged catalog/shoppers without deleting staff invoices

Reviewed-on: #10
2026-09-21 13:29:36 -07:00

53 lines
1.5 KiB
Python

from django.conf import settings
from django.shortcuts import redirect
class UnderConstructionMiddleware:
"""
When SITE_UNDER_CONSTRUCTION is true, redirect public traffic to the holding page.
Exempt: health, static/media, admin, auth login/logout, SMS webhook, and the
under-construction page itself (so notify-me POSTs work).
"""
EXEMPT_PREFIXES = (
"/healthz",
"/static/",
"/media/",
"/admin/",
"/accounts/login",
"/accounts/logout",
"/account/login",
"/account/logout",
"/account/register",
"/account/password-reset",
"/under-construction",
"/portal/messaging/webhooks/",
"/unsubscribe/",
"/api/address-suggest",
"/robots.txt",
"/sitemap.xml",
)
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
if settings.SITE_UNDER_CONSTRUCTION and not self._is_exempt(request):
return redirect("public:under_construction")
return self.get_response(request)
def _is_exempt(self, request) -> bool:
path = request.path
if any(path.startswith(prefix) for prefix in self.EXEMPT_PREFIXES):
return True
user = getattr(request, "user", None)
if (
user is not None
and getattr(user, "is_authenticated", False)
and getattr(user, "is_staff", False)
and path.startswith("/portal/")
):
return True
return False