diff --git a/frontend/src/main.ts b/frontend/src/main.ts index 747dd47..4f220bc 100644 --- a/frontend/src/main.ts +++ b/frontend/src/main.ts @@ -12,7 +12,7 @@ import OpportunitiesView from './components/OpportunitiesView.vue' import SignalsView from './components/SignalsView.vue' const router = createRouter({ - history: createWebHistory(), + history: createWebHistory(import.meta.env.BASE_URL), routes: [ { path: '/', redirect: '/signals' }, { path: '/signals', component: SignalsView }, diff --git a/manage.sh b/manage.sh index dc703f6..1243cc0 100755 --- a/manage.sh +++ b/manage.sh @@ -85,7 +85,7 @@ _start_web() { if [[ -f "frontend/dist/index.html" ]]; then info "Starting web on :${WEB_PORT} (static dist — production mode)..." conda run --no-capture-output -n "$CONDA_ENV" \ - python scripts/spa_server.py --port "$WEB_PORT" --directory frontend/dist >> "$LOG_WEB" 2>&1 & + python scripts/spa_server.py --port "$WEB_PORT" --directory frontend/dist --base /magpie >> "$LOG_WEB" 2>&1 & else info "Starting web on :${WEB_PORT} (Vite dev server — no dist found)..." cd frontend @@ -266,7 +266,7 @@ case "$cmd" in # In production, Caddy proxies menagerie.circuitforge.tech/magpie* → this port. info "Serving pre-built frontend on :${WEB_PORT} (SPA fallback enabled)..." conda run --no-capture-output -n "$CONDA_ENV" \ - python scripts/spa_server.py --port "$WEB_PORT" --directory frontend/dist >> "$LOG_WEB" 2>&1 & + python scripts/spa_server.py --port "$WEB_PORT" --directory frontend/dist --base /magpie >> "$LOG_WEB" 2>&1 & echo $! > "$PID_WEB" ok "Static server up → http://localhost:${WEB_PORT}" ;; diff --git a/scripts/spa_server.py b/scripts/spa_server.py index 7757f7f..8ef49a4 100644 --- a/scripts/spa_server.py +++ b/scripts/spa_server.py @@ -20,6 +20,18 @@ from http.server import HTTPServer, SimpleHTTPRequestHandler class SPAHandler(SimpleHTTPRequestHandler): api_port: int = 8532 + base_prefix: str = "" # e.g. "/magpie" — stripped before file lookup + + # ------------------------------------------------------------------ # + # Path helpers + # ------------------------------------------------------------------ # + + def _strip_base(self, path: str) -> str: + """Remove the base prefix so file lookup works against the dist root.""" + p = path.split("?", 1)[0].split("#", 1)[0] + if self.base_prefix and p.startswith(self.base_prefix): + path = path[len(self.base_prefix):] + return path or "/" # ------------------------------------------------------------------ # # API proxy @@ -51,11 +63,16 @@ class SPAHandler(SimpleHTTPRequestHandler): # Request dispatch # ------------------------------------------------------------------ # + def _normalise_path(self) -> None: + """Strip base prefix so all subsequent checks work on bare /api/ or /asset paths.""" + self.path = self._strip_base(self.path) + def _is_api(self) -> bool: path = self.path.split("?", 1)[0] return path.startswith("/api/") def do_GET(self) -> None: + self._normalise_path() if self._is_api(): self._proxy_api() return @@ -65,24 +82,28 @@ class SPAHandler(SimpleHTTPRequestHandler): super().do_GET() def do_POST(self) -> None: + self._normalise_path() if self._is_api(): self._proxy_api() return self.send_error(405, "Method Not Allowed") def do_PUT(self) -> None: + self._normalise_path() if self._is_api(): self._proxy_api() return self.send_error(405, "Method Not Allowed") def do_PATCH(self) -> None: + self._normalise_path() if self._is_api(): self._proxy_api() return self.send_error(405, "Method Not Allowed") def do_DELETE(self) -> None: + self._normalise_path() if self._is_api(): self._proxy_api() return @@ -98,9 +119,11 @@ def main() -> None: parser.add_argument("--port", type=int, default=8531) parser.add_argument("--directory", default="frontend/dist") parser.add_argument("--api-port", type=int, default=8532) + parser.add_argument("--base", default="", help="URL base prefix to strip (e.g. /magpie)") args = parser.parse_args() SPAHandler.api_port = args.api_port + SPAHandler.base_prefix = args.base.rstrip("/") os.chdir(args.directory) server = HTTPServer(("", args.port), SPAHandler) print(