fix(backend): remove broken remote branch in /api/system/stats

The remoteNodeProxy middleware (line 373) is already positioned before
all API route definitions, so remote requests are correctly proxied to
the target Sencho instance before any route handler executes.

However, /api/system/stats had a dead remote branch that called
NodeRegistry.getDocker() for remote nodes. This method throws by design
("remote nodes are not directly accessible") since no direct Docker TCP
socket is used for remote nodes. The thrown error propagated through the
catch block, returning a 500 to the frontend and causing system stats to
show as loading/unavailable for remote nodes.

Removed the dead branch. Remote requests for /api/system/stats are now
correctly proxied, returning real CPU/RAM/disk data from the remote host.
This commit is contained in:
SaelixCode
2026-03-19 22:02:29 -04:00
parent d64d23fc50
commit 3f473c5c97
2 changed files with 3 additions and 26 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 system stats, container stats, logs, and exec returning errors — `remoteNodeProxy` middleware was already positioned before API route definitions, but `/api/system/stats` contained a dead remote branch that called `NodeRegistry.getDocker()` for remote nodes, which throws since remote nodes have no direct Docker socket access. Removed the broken branch; remote requests are correctly intercepted by the proxy middleware before reaching any route handler.
- **Added:** Background image update checker — `ImageUpdateService` polls OCI-compliant registries (Docker Hub, GHCR, LSCR, etc.) every 6 hours using manifest digest comparison against local `RepoDigests`. Results cached in a new `stack_update_status` SQLite table. A pulsing blue dot badge appears in the stack list sidebar for stacks with available updates. Manual refresh available via `POST /api/image-updates/refresh` (rate-limited to once per 10 minutes).
- **Fixed:** `AppStoreView` and `GlobalObservabilityView` using raw `fetch()` instead of `apiFetch()` — all calls now inject the `x-node-id` header so templates, deploys, stacks, and logs are correctly proxied to the active remote node.
- **Fixed:** `HostConsole` WebSocket URL missing `?nodeId=` query parameter — the upgrade handler now receives the active node ID and routes the PTY session to the correct node.
+2 -26
View File
@@ -1139,35 +1139,11 @@ app.get('/api/logs/global/stream', async (req: Request, res: Response) => {
// Get host system stats
app.get('/api/system/stats', async (req: Request, res: Response) => {
try {
const nodeId = req.nodeId ?? NodeRegistry.getInstance().getDefaultNodeId();
const node = NodeRegistry.getInstance().getNode(nodeId);
const rxSec = Math.max(0, globalDockerNetwork.rxSec);
const txSec = Math.max(0, globalDockerNetwork.txSec);
if (node && node.type === 'remote') {
// Remote node: use Docker daemon info for CPU/RAM - disk is not available via Docker API
const docker = NodeRegistry.getInstance().getDocker(nodeId);
const info = await docker.info();
res.json({
cpu: {
usage: '0',
cores: info.NCPU ?? 0,
},
memory: {
total: info.MemTotal ?? 0,
used: 0,
free: info.MemTotal ?? 0,
usagePercent: '0',
},
disk: null,
network: { rxBytes: 0, txBytes: 0, rxSec, txSec },
});
return;
}
// Local node: use systeminformation for accurate host metrics
// Remote node requests are intercepted and proxied by remoteNodeProxy before reaching here.
// This handler only runs for local nodes.
const [currentLoad, mem, fsSize] = await Promise.all([
si.currentLoad(),
si.mem(),