fix: remote proxy strips /api prefix causing remote Sencho to return SPA HTML

app.use('/api/', ...) causes Express to strip the '/api/' prefix from
req.url before http-proxy-middleware sees it, so the proxy was forwarding
'/stats' to the remote instead of '/api/stats'. The remote Sencho found
no matching route, fell through to its SPA catch-all, and returned
index.html (200 text/html) — explaining the SyntaxError on res.json().

Added pathRewrite: (path) => '/api' + path to restore the full path.
The connection test passed because testRemoteConnection calls the remote
directly via axios with the full URL, bypassing the proxy entirely.
This commit is contained in:
SaelixCode
2026-03-19 16:59:27 -04:00
parent 497a48c2ed
commit a26c255e7c
2 changed files with 5 additions and 0 deletions
+1
View File
@@ -5,6 +5,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
- **Fixed:** Remote node proxy stripping the `/api` path prefix — `remoteNodeProxy` is mounted at `app.use('/api/', ...)` so Express strips that prefix from `req.url` before `http-proxy-middleware` sees it; added `pathRewrite: (path) => '/api' + path` to restore the full path when forwarding to the remote Sencho instance (e.g. `/stats``/api/stats`). This was the root cause of all remote API calls returning the remote's SPA HTML instead of JSON.
- **Fixed:** Dashboard cards (Active Containers, Host CPU, Host RAM, Docker Network) showing stale local-node data after switching to a remote node — `HomeDashboard` polling effects now depend on `activeNode?.id` and clear state immediately on node change.
- **Fixed:** `refreshStacks` crashing with `SyntaxError` or `TypeError` when the remote proxy returns a non-JSON response (e.g., connection refused to unreachable remote node) — now checks `res.ok` before calling `res.json()` and iterates a typed `fileList` instead of the raw parsed value.
- **Fixed:** Restored Local/Remote type selector and fixed state resets in the Add Node modal — form now resets to defaults every time the dialog opens, and the title reflects the chosen type dynamically.
+4
View File
@@ -331,6 +331,10 @@ const remoteNodeProxy = createProxyMiddleware<Request, Response>({
const node = NodeRegistry.getInstance().getNode(req.nodeId);
return node?.api_url?.replace(/\/$/, '');
},
// When mounted at app.use('/api/', ...), Express strips the '/api/' prefix from
// req.url before the middleware sees it. Re-add it so the remote Sencho instance
// receives the full path (e.g. '/stats' becomes '/api/stats').
pathRewrite: (path) => '/api' + path,
on: {
proxyReq: (proxyReq, req) => {
const node = NodeRegistry.getInstance().getNode(req.nodeId);