From 26f5eaa2f75d788553974cd190c7d89e13b5cbf0 Mon Sep 17 00:00:00 2001 From: Donkie Date: Wed, 22 May 2024 20:15:39 +0200 Subject: [PATCH] Fixed base path issues --- spoolman/client.py | 48 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/spoolman/client.py b/spoolman/client.py index 154aafd..f0f044c 100644 --- a/spoolman/client.py +++ b/spoolman/client.py @@ -4,13 +4,20 @@ import logging import os +from collections.abc import MutableMapping from pathlib import Path -from typing import Union +from typing import Any, Union from fastapi.staticfiles import StaticFiles +from starlette.datastructures import Headers +from starlette.responses import FileResponse, Response +from starlette.staticfiles import NotModifiedResponse logger = logging.getLogger(__name__) +PathLike = Union[str, "os.PathLike[str]"] +Scope = MutableMapping[str, Any] + class SinglePageApplication(StaticFiles): """Serve a single page application.""" @@ -20,6 +27,45 @@ class SinglePageApplication(StaticFiles): super().__init__(directory=directory, packages=None, html=True, check_dir=True) self.base_path = base_path.removeprefix("/") + self.load_and_tweak_index_file() + + def load_and_tweak_index_file(self) -> None: + """Load index.html and tweak it by replacing all asset paths.""" + # Open index.html located in self.directory/index.html + if not self.directory: + return + + with (Path(self.directory) / "index.html").open() as f: + html = f.read() + + # Replace all paths that start with "./" with f"/{self.base_path}" + base_path = "/" if len(self.base_path.strip()) == 0 else f"/{self.base_path}/" + self.html = html.replace('"./', f'"{base_path}') + + def file_response( + self, + full_path: PathLike, + stat_result: os.stat_result, + scope: Scope, + status_code: int = 200, + ) -> Response: + """Overriden default file_response. + + Works the same way, but if the client requests any index.html, we will return our tweaked index.html. + The tweaked index.html has all asset paths updated with the base path. + """ + method = scope["method"] + request_headers = Headers(scope=scope) + + # If full_path points to a index.html, return our tweaked index.html + if Path(full_path).name == "index.html": + return Response(self.html, status_code=status_code, media_type="text/html") + + response = FileResponse(full_path, status_code=status_code, stat_result=stat_result, method=method) + if self.is_not_modified(response.headers, request_headers): + return NotModifiedResponse(response.headers) + return response + def lookup_path(self, path: str) -> tuple[str, Union[os.stat_result, None]]: """Return index.html if the requested file cannot be found.""" path = path.removeprefix(self.base_path).removeprefix("/")