fix(ui): sub-path routing and API proxy for /magpie/ base URL
- spa_server.py: strip /magpie prefix before API check and file lookup; all HTTP methods call _normalise_path() first so /magpie/api/v1/* proxies correctly and /magpie/assets/* resolve against the dist root - manage.sh: pass --base /magpie to spa_server.py so the handler knows the deployment prefix - main.ts: pass import.meta.env.BASE_URL to createWebHistory so Vue Router strips the /magpie prefix before matching routes Without these fixes, assets returned index.html (MIME type error), API calls returned HTML instead of JSON (stats.posts undefined), and router routes never matched (matched: []). Closes: #12
This commit is contained in:
parent
35c6e5f7bc
commit
8ea4baa915
3 changed files with 26 additions and 3 deletions
|
|
@ -12,7 +12,7 @@ import OpportunitiesView from './components/OpportunitiesView.vue'
|
||||||
import SignalsView from './components/SignalsView.vue'
|
import SignalsView from './components/SignalsView.vue'
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHistory(),
|
history: createWebHistory(import.meta.env.BASE_URL),
|
||||||
routes: [
|
routes: [
|
||||||
{ path: '/', redirect: '/signals' },
|
{ path: '/', redirect: '/signals' },
|
||||||
{ path: '/signals', component: SignalsView },
|
{ path: '/signals', component: SignalsView },
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@ _start_web() {
|
||||||
if [[ -f "frontend/dist/index.html" ]]; then
|
if [[ -f "frontend/dist/index.html" ]]; then
|
||||||
info "Starting web on :${WEB_PORT} (static dist — production mode)..."
|
info "Starting web on :${WEB_PORT} (static dist — production mode)..."
|
||||||
conda run --no-capture-output -n "$CONDA_ENV" \
|
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
|
else
|
||||||
info "Starting web on :${WEB_PORT} (Vite dev server — no dist found)..."
|
info "Starting web on :${WEB_PORT} (Vite dev server — no dist found)..."
|
||||||
cd frontend
|
cd frontend
|
||||||
|
|
@ -266,7 +266,7 @@ case "$cmd" in
|
||||||
# In production, Caddy proxies menagerie.circuitforge.tech/magpie* → this port.
|
# In production, Caddy proxies menagerie.circuitforge.tech/magpie* → this port.
|
||||||
info "Serving pre-built frontend on :${WEB_PORT} (SPA fallback enabled)..."
|
info "Serving pre-built frontend on :${WEB_PORT} (SPA fallback enabled)..."
|
||||||
conda run --no-capture-output -n "$CONDA_ENV" \
|
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"
|
echo $! > "$PID_WEB"
|
||||||
ok "Static server up → http://localhost:${WEB_PORT}"
|
ok "Static server up → http://localhost:${WEB_PORT}"
|
||||||
;;
|
;;
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,18 @@ from http.server import HTTPServer, SimpleHTTPRequestHandler
|
||||||
|
|
||||||
class SPAHandler(SimpleHTTPRequestHandler):
|
class SPAHandler(SimpleHTTPRequestHandler):
|
||||||
api_port: int = 8532
|
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
|
# API proxy
|
||||||
|
|
@ -51,11 +63,16 @@ class SPAHandler(SimpleHTTPRequestHandler):
|
||||||
# Request dispatch
|
# 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:
|
def _is_api(self) -> bool:
|
||||||
path = self.path.split("?", 1)[0]
|
path = self.path.split("?", 1)[0]
|
||||||
return path.startswith("/api/")
|
return path.startswith("/api/")
|
||||||
|
|
||||||
def do_GET(self) -> None:
|
def do_GET(self) -> None:
|
||||||
|
self._normalise_path()
|
||||||
if self._is_api():
|
if self._is_api():
|
||||||
self._proxy_api()
|
self._proxy_api()
|
||||||
return
|
return
|
||||||
|
|
@ -65,24 +82,28 @@ class SPAHandler(SimpleHTTPRequestHandler):
|
||||||
super().do_GET()
|
super().do_GET()
|
||||||
|
|
||||||
def do_POST(self) -> None:
|
def do_POST(self) -> None:
|
||||||
|
self._normalise_path()
|
||||||
if self._is_api():
|
if self._is_api():
|
||||||
self._proxy_api()
|
self._proxy_api()
|
||||||
return
|
return
|
||||||
self.send_error(405, "Method Not Allowed")
|
self.send_error(405, "Method Not Allowed")
|
||||||
|
|
||||||
def do_PUT(self) -> None:
|
def do_PUT(self) -> None:
|
||||||
|
self._normalise_path()
|
||||||
if self._is_api():
|
if self._is_api():
|
||||||
self._proxy_api()
|
self._proxy_api()
|
||||||
return
|
return
|
||||||
self.send_error(405, "Method Not Allowed")
|
self.send_error(405, "Method Not Allowed")
|
||||||
|
|
||||||
def do_PATCH(self) -> None:
|
def do_PATCH(self) -> None:
|
||||||
|
self._normalise_path()
|
||||||
if self._is_api():
|
if self._is_api():
|
||||||
self._proxy_api()
|
self._proxy_api()
|
||||||
return
|
return
|
||||||
self.send_error(405, "Method Not Allowed")
|
self.send_error(405, "Method Not Allowed")
|
||||||
|
|
||||||
def do_DELETE(self) -> None:
|
def do_DELETE(self) -> None:
|
||||||
|
self._normalise_path()
|
||||||
if self._is_api():
|
if self._is_api():
|
||||||
self._proxy_api()
|
self._proxy_api()
|
||||||
return
|
return
|
||||||
|
|
@ -98,9 +119,11 @@ def main() -> None:
|
||||||
parser.add_argument("--port", type=int, default=8531)
|
parser.add_argument("--port", type=int, default=8531)
|
||||||
parser.add_argument("--directory", default="frontend/dist")
|
parser.add_argument("--directory", default="frontend/dist")
|
||||||
parser.add_argument("--api-port", type=int, default=8532)
|
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()
|
args = parser.parse_args()
|
||||||
|
|
||||||
SPAHandler.api_port = args.api_port
|
SPAHandler.api_port = args.api_port
|
||||||
|
SPAHandler.base_prefix = args.base.rstrip("/")
|
||||||
os.chdir(args.directory)
|
os.chdir(args.directory)
|
||||||
server = HTTPServer(("", args.port), SPAHandler)
|
server = HTTPServer(("", args.port), SPAHandler)
|
||||||
print(
|
print(
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue