from __future__ import annotations import urllib.parse import httpx from django.conf import settings from django.http import ( HttpRequest, HttpResponse, HttpResponseBase, HttpResponseRedirect, ) from django.template import engines from django.template.response import TemplateResponse from django.views.generic import TemplateView def catchall_dev(request: HttpRequest) -> HttpResponseBase: """Proxy the react dev-server.""" # Validate the request.path to ensure it's safe # For example, ensure it does not contain "..", which could lead to directory # traversal Adjust the regex pattern according to your application's routing # scheme uri = urllib.parse.urlsplit( request.build_absolute_uri(request.get_full_path_info()), ) if uri.hostname != "localhost": port = "" if uri.port is None else f":{uri.port}" return HttpResponseRedirect(uri._replace(netloc=f"localhost{port}").geturl()) query_string = request.META["QUERY_STRING"] upstream = "http://localhost:5173" try: response = httpx.get( upstream + request.path + (f"?{query_string}" if query_string else ""), ) except httpx.RequestError as e: raise RuntimeError( f"you need to run `npm start` in the {settings.FRONTEND_DIR} directory", ) from e content_type = response.headers.get("Content-Type") if content_type is not None and content_type.lower() == "text/html; charset=utf-8": tr = TemplateResponse( template=engines["django"].from_string(response.text), # type: ignore[arg-type] request=request, content_type=content_type, status=response.status_code, ) tr.reason_phrase = response.reason_phrase return tr return HttpResponse( content=response.content, content_type=content_type, status=response.status_code, reason=response.reason_phrase, ) catchall_prod = TemplateView.as_view( template_name="index.html", ) def catchall(request: HttpRequest) -> HttpResponseBase: """Catchall view for the frontend.""" return catchall_dev(request) if settings.DEVELOPMENT else catchall_prod(request)