From 03f1e8ea16707d8f6a54036060e33a9dd8480a12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Gremaud?= Date: Sat, 20 Jun 2026 08:58:19 +0200 Subject: [PATCH] fix: correct force script name? --- highscore/app/middleware.py | 29 +++++++++++++++++++++++++++++ highscore/app/served.py | 27 +++++++++++---------------- highscore/app/settings.py | 1 + 3 files changed, 41 insertions(+), 16 deletions(-) create mode 100644 highscore/app/middleware.py diff --git a/highscore/app/middleware.py b/highscore/app/middleware.py new file mode 100644 index 0000000..d4fd5c0 --- /dev/null +++ b/highscore/app/middleware.py @@ -0,0 +1,29 @@ +from collections.abc import Callable + +from django.http import HttpRequest, HttpResponse + + +class ScriptPrefixPathMiddleware: + """Re-add the ``FORCE_SCRIPT_NAME`` prefix to ``request.path`` under ASGI. + + Django's ASGI handler sets ``request.path = scope["path"]`` verbatim (see + ``django/core/handlers/asgi.py``). When the reverse proxy strips the + script-name prefix (e.g. ``/scores``) before forwarding, ``request.path`` — + and therefore ``get_full_path()`` — loses the prefix, even though + ``reverse()`` still prepends it via the script prefix. The result is the + admin login form posting to ``/admin/login/?next=/admin/`` instead of + ``/scores/admin/login/?next=/scores/admin/``. + + URL resolution is unaffected (it uses ``path_info``), so we only need to + realign the path-based URLs with the reverse()-based ones. The check is + idempotent: if the proxy already forwards the prefix, this is a no-op. + """ + + def __init__(self, get_response: Callable[[HttpRequest], HttpResponse]) -> None: + self.get_response = get_response + + def __call__(self, request: HttpRequest) -> HttpResponse: + script_name = request.META.get("SCRIPT_NAME", "").rstrip("/") + if script_name and not request.path.startswith(f"{script_name}/"): + request.path = f"{script_name}{request.path}" + return self.get_response(request) diff --git a/highscore/app/served.py b/highscore/app/served.py index 1816524..f0ba827 100644 --- a/highscore/app/served.py +++ b/highscore/app/served.py @@ -2,23 +2,18 @@ import subprocess import sys from pathlib import Path -from app.config import settings as env - def serve() -> None: """Start the Daphne ASGI server on 0.0.0.0:8080.""" daphne_path = str(Path(sys.path[0]) / "daphne") - args = [ - daphne_path, - "-b", - "0.0.0.0", # noqa: S104 - "-p", - "8080", - ] - # FORCE_SCRIPT_NAME is a WSGI-only setting; under ASGI Django derives the URL - # script prefix from the scope's root_path. Pass the prefix to Daphne so that - # reverse()/redirects (e.g. the admin post-login redirect) include it. - if env.force_script_name: - args += ["--root-path", env.force_script_name.rstrip("/")] - args.append("app.routing:application") - subprocess.run(args, check=True) # noqa: S603 + subprocess.run( # noqa: S603 + [ + daphne_path, + "-b", + "0.0.0.0", # noqa: S104 + "-p", + "8080", + "app.routing:application", + ], + check=True, + ) diff --git a/highscore/app/settings.py b/highscore/app/settings.py index 0ab341f..9996cfe 100644 --- a/highscore/app/settings.py +++ b/highscore/app/settings.py @@ -42,6 +42,7 @@ SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") MIDDLEWARE = [ "django.middleware.security.SecurityMiddleware", "whitenoise.middleware.WhiteNoiseMiddleware", + "app.middleware.ScriptPrefixPathMiddleware", "django.contrib.sessions.middleware.SessionMiddleware", "django.middleware.common.CommonMiddleware", "django.middleware.csrf.CsrfViewMiddleware",