Files
gaehsnitz/suff.md
T
flo 47d46e8e6f Add suff drink booking tool with PIN auth
Self-service drink tab at /suff/ for festival attendees. Users log in
with username + 3-digit PIN stored in a separate User.pin field, so
staff/admin accounts can keep their strong password for /admin/ and
also use the drink tool with the same username. PINs for staff users
must be set from the admin panel via a dedicated "PIN setzen" view to
prevent account takeover by name collision.

Time-gated to the festival window (Thu–Sun in Berlin tz) with phases
before/booking/readonly/closed; in non-production mode the tool is
always in booking phase for local testing.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-14 12:05:25 +02:00

2.7 KiB
Raw Blame History

Suff drink booking tool

Self-service drink tab for festival attendees. Lives at /suff/. Plain Django, no JS, no CSS yet.

Auth

  • User.pin field (hashed CharField) stores 3-digit PIN, separate from password. Lets staff keep a strong password for /admin/ and use the same username on /suff/ with just a PIN.
  • PinBackend (gaehsnitz/auth_backends.py) authenticates by username + pin via user.check_pin(). Default ModelBackend stays first in AUTHENTICATION_BACKENDS so /admin/ keeps requiring the strong password.
  • Staff PINs cannot be self-set on /suff/. If a name matches an existing user with no PIN, the user lands on suff/no_pin.html explaining that an admin must set the PIN via the admin panel — otherwise anyone could claim a staff name and lock out the real owner.
  • Admin convenience: User change page shows PIN status ("gesetzt"/"nicht gesetzt") + "PIN setzen" link → custom admin view <id>/pin/ with a 3-digit form, calls user.set_pin().

Name flow

  • Username = slugify(input) (e.g. "Flo Hä!" → "flo-ha"). Slug shown back so user can memorize it.
  • POST name → check existence:
    • not found → set new PIN → create user → login
    • found, has PIN → enter PIN → login
    • found, no PIN → no_pin.html (ask admin)

Booking

  • /suff/me/ shows: greeting (slug), running paid total, full consumption history with timestamps, drink buttons.
  • Each drink = +1 POST form. Server creates Consumption(amount=1, day=current_weekday, for_free=False, created_at=auto).
  • No undo, no delete, no edit. No special bartender role.
  • History sorted newest-first, created_at shown as Do 18:42 etc.

Time gating (Berlin tz)

  • Phases: before / booking / readonly / closed.
  • Booking allowed Thu 2026-06-11 00:00 → Sun 2026-06-14 23:59.
  • Read-only until Sun 2026-06-21 23:59.
  • After: every /suff/ URL returns 404.
  • Local dev: settings.PRODUCTION=False forces booking phase always.

Files

  • gaehsnitz/auth_backends.pyPinBackend
  • gaehsnitz/suff.py — views + phase logic
  • gaehsnitz/suff_urls.py — routes
  • gaehsnitz/admin.pySetPinForm + set_pin_view
  • gaehsnitz/templates/suff/{base,name,pin,no_pin,me}.html
  • gaehsnitz/templates/admin/gaehsnitz/user/set_pin.html
  • gaehsnitz/migrations/0003_consumption_created_at_user_pin.py
  • Edits: gaehsnitzproject/settings.py, gaehsnitzproject/urls.py, gaehsnitz/models.py

Next step

Mobile-first styling. Currently zero CSS. Big tap targets for drink buttons, sticky/large running total, dark-mode friendly for outdoor evening use. Visual feedback after booking, day-grouped history, friendlier empty state, bigger PIN input. Decide whether /suff/ integrates into the GOA site header or stays a separate microsite.