Files
RosterChief/rosterchief/urls.py
Bernard Siebens 1a2bf257da Brand the auth screens per tenant
A club member signing in at ajax-united.rosterchief.app now sees their club's
logo, name and colours; the base domain keeps the RosterChief skin for the
control panel and Django admin.

The mechanism is `{% extends base_template %}` -- Django lets the parent be a
context variable, so the `branding` context processor picks the skin from
request.club and *every* auth screen allauth ships (login, password reset, MFA,
passkeys, and whatever it adds next) follows the tenant without a single one of
them knowing that clubs exist.

Templates split three ways: _base.html is the skeleton with no branding, and
_platform_base.html / _club_base.html dress it. The control panel extends the
platform base *explicitly* rather than through the variable, so a bug in
branding resolution can never dress the panel up as a club.

Club gains an optional logo and primary_color. Notes on both:

- No logo falls back to the club's initials, never the RosterChief mark, which
  would pass our branding off as theirs.
- Club colours land in an inline :root. daisyUI declares its theme variables
  inside `@layer base`, and unlayered styles beat every layered rule regardless
  of specificity, so this needs no !important. --color-primary-content is derived
  from WCAG relative luminance, so a club that picks pale yellow gets black text
  instead of invisible white.
- primary_color is a text input, not <input type="color">: a colour picker cannot
  express "no colour", so every club that never touched it would submit #000000
  and silently get a black theme.

"/" now resolves per tenant (club home, or hand off to the control panel), which
is why LOGIN_REDIRECT_URL can stay "/" and allauth needs no redirect adapter.

Also folds in the theme toggle gaining a third "auto" state and the logo
switching from `content:` to background-image (content-replacement on a real
element is not supported in Firefox), both of which lived in the base template
this commit replaces.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-13 22:30:38 +02:00

32 lines
1.3 KiB
Python

"""URL configuration for rosterchief.
``/admin/login/`` is deliberately intercepted *before* ``admin.site.urls`` and
redirected to the allauth login, so Django staff go through the same MFA
challenge as everyone else — Django's own admin login form knows nothing about
second factors. ``RequireMFAMiddleware`` then blocks any staff user who has not
enrolled.
"""
from django.conf import settings
from django.conf.urls.static import static
from django.contrib import admin
from django.urls import include, path
from django.views.generic import RedirectView
from club.views import root
urlpatterns = [
path("admin/login/", RedirectView.as_view(pattern_name="account_login", query_string=True), name="admin_login_redirect"),
path("admin/", admin.site.urls),
path("accounts/", include("allauth.urls")),
path("controlpanel/", include("controlpanel.urls")),
# "/" resolves per tenant: a club subdomain lands on the club, the base domain
# hands off to the control panel. This is why LOGIN_REDIRECT_URL can stay "/".
path("", root, name="root"),
]
if settings.DEBUG:
urlpatterns += [path("__reload__/", include("django_browser_reload.urls"))]
# Club logos are uploads: runserver has to serve MEDIA_ROOT itself.
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)