mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-31 12:48:10 +00:00
fix(backend): restore remote proxy mount order before local routers (#747)
The index.ts refactor inverted the proxy mount order. The pre-refactor
monolith mounted `app.use('/api/', remoteNodeProxy)` before any inline
route, so remote-nodeId requests short-circuited into the proxy. After
the refactor the proxy was registered after every per-group router, so
Express matched local routers first and remote-nodeId requests were
silently handled with the control instance's local state (e.g.
GET /api/stacks with x-node-id=<remote> returned local stacks rather
than the remote's).
Fix moves createRemoteProxyMiddleware() between enforceApiTokenScope
and the first per-group router, matching middleware-order.md step 13
and restoring pre-refactor behavior. PROXY_EXEMPT_PREFIXES continues to
cover gateway-level paths (auth, nodes, license, fleet, webhooks, meta)
that must stay local even when x-node-id targets a remote.
Add four regression guards that would have caught this:
- json-parser-bypass.test.ts: asserts conditionalJsonParser leaves the
request stream intact on proxy-eligible paths so http-proxy can pipe
the raw body to the upstream; spins up a local echo server and
verifies the bytes arrive.
- proxy-mount-order.test.ts: asserts a remote-nodeId GET short-circuits
into the proxy (502 from unreachable upstream) instead of matching a
local router (200 from local state).
- upgrade-order.test.ts: pins WebSocket dispatch order by observing
handler-specific side effects for notifications, remote forwarder,
logs, and pilot tunnel.
- remote-console-session.test.ts: asserts the HTTP console-token route
mints a JWT with the same claim shape as the shared mintConsoleSession
helper, so gateway and WS forwarder tokens remain interchangeable.
Full suite: 73 files, 1,358 tests, all passing.
This commit is contained in:
@@ -73,6 +73,14 @@ app.use('/api', auditLog);
|
||||
|
||||
app.use('/api', enforceApiTokenScope);
|
||||
|
||||
// Remote Node HTTP Proxy (see proxy/remoteNodeProxy.ts). Mounted BEFORE the
|
||||
// per-group routers so a request targeting a remote node short-circuits into
|
||||
// the proxy instead of hitting a local handler that would read local state.
|
||||
// Gateway-level paths (auth, nodes, license, fleet, webhooks, meta) are listed
|
||||
// in helpers/proxyExemptPaths.ts and bypass the proxy back to the local
|
||||
// handlers below.
|
||||
app.use('/api/', createRemoteProxyMiddleware());
|
||||
|
||||
app.use('/api/license', licenseRouter);
|
||||
app.use('/api/system', systemUpdateRouter);
|
||||
app.use('/api/permissions', permissionsRouter);
|
||||
@@ -107,11 +115,6 @@ app.use('/api/ports', portsRouter);
|
||||
app.use('/api/nodes', nodesRouter);
|
||||
app.use('/api/stacks', stacksRouter);
|
||||
|
||||
// Remote Node HTTP Proxy (see proxy/remoteNodeProxy.ts). Mounted here after
|
||||
// authGate + auditLog + apiTokenScope so local Sencho enforces auth first;
|
||||
// the proxy then takes over for remote-targeted requests.
|
||||
app.use('/api/', createRemoteProxyMiddleware());
|
||||
|
||||
const { server, wss, pilotTunnelWss } = createServer(app);
|
||||
attachUpgrade(server, { wss, pilotTunnelWss });
|
||||
|
||||
|
||||
Reference in New Issue
Block a user