fix(dashboard): add aggregated resource usage stats to the dashboard
Some checks failed
Publish to npm / npm-publish (push) Failing after 4s
CI / Type Check & Lint (push) Failing after 29s
CI / Build Test (Current Platform) (push) Successful in 1m1s
Release / build-and-release (push) Failing after 1m43s
CI / Build All Platforms (push) Successful in 2m8s
Some checks failed
Publish to npm / npm-publish (push) Failing after 4s
CI / Type Check & Lint (push) Failing after 29s
CI / Build Test (Current Platform) (push) Successful in 1m1s
Release / build-and-release (push) Failing after 1m43s
CI / Build All Platforms (push) Successful in 2m8s
- Aggregate CPU, memory, and network stats across all running containers - Extend ISystemStatus.docker with resource usage fields - Fix getContainerStats Swarm service ID fallback - Wire dashboard resource usage card to real backend data
This commit is contained in:
@@ -1,5 +1,13 @@
|
||||
# Changelog
|
||||
|
||||
## 2026-03-16 - 1.19.1 - fix(dashboard)
|
||||
add aggregated resource usage stats to the dashboard
|
||||
|
||||
- Aggregate CPU, memory, and network stats across all running user and platform service containers in getSystemStatus
|
||||
- Extend ISystemStatus.docker interface with cpuUsage, memoryUsage, memoryTotal, networkIn, networkOut fields
|
||||
- Fix getContainerStats to properly handle Swarm service IDs by catching exceptions and falling back to label-based container lookup
|
||||
- Wire dashboard resource usage card to display real aggregated data from the backend
|
||||
|
||||
## 2026-03-16 - 1.19.0 - feat(opsserver,web)
|
||||
add real-time platform service log streaming to the dashboard
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@serve.zone/onebox",
|
||||
"version": "1.19.0",
|
||||
"version": "1.19.1",
|
||||
"exports": "./mod.ts",
|
||||
"tasks": {
|
||||
"test": "deno test --allow-all test/",
|
||||
|
||||
@@ -596,18 +596,26 @@ export class OneboxDockerManager {
|
||||
async getContainerStats(containerID: string): Promise<IContainerStats | null> {
|
||||
try {
|
||||
// Try to get container directly first
|
||||
let container = await this.dockerClient!.getContainerById(containerID);
|
||||
let container: any = null;
|
||||
try {
|
||||
container = await this.dockerClient!.getContainerById(containerID);
|
||||
} catch {
|
||||
// Container not found by ID — might be a Swarm service ID
|
||||
}
|
||||
|
||||
// If not found, it might be a service ID - try to get the actual container ID
|
||||
if (!container) {
|
||||
const serviceContainerId = await this.getContainerIdForService(containerID);
|
||||
if (serviceContainerId) {
|
||||
container = await this.dockerClient!.getContainerById(serviceContainerId);
|
||||
try {
|
||||
container = await this.dockerClient!.getContainerById(serviceContainerId);
|
||||
} catch {
|
||||
// Service container also not found
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!container) {
|
||||
// Container/service not found
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -291,10 +291,65 @@ export class Onebox {
|
||||
// Sort expiring domains by days remaining (ascending)
|
||||
expiringDomains.sort((a, b) => a.daysRemaining - b.daysRemaining);
|
||||
|
||||
// Aggregate resource usage across all running service containers
|
||||
let totalCpu = 0;
|
||||
let totalMemoryUsed = 0;
|
||||
let totalMemoryLimit = 0;
|
||||
let totalNetworkIn = 0;
|
||||
let totalNetworkOut = 0;
|
||||
|
||||
if (dockerRunning) {
|
||||
const allServices = this.services.listServices();
|
||||
const runningUserServices = allServices.filter((s) => s.status === 'running' && s.containerID);
|
||||
logger.debug(`Resource stats: ${runningUserServices.length} running user services`);
|
||||
|
||||
const statsPromises = runningUserServices
|
||||
.map((s) => {
|
||||
logger.debug(`Fetching stats for user service: ${s.name} (${s.containerID})`);
|
||||
return this.docker.getContainerStats(s.containerID!).catch((err) => {
|
||||
logger.debug(`Stats failed for ${s.name}: ${(err as Error).message}`);
|
||||
return null;
|
||||
});
|
||||
});
|
||||
|
||||
// Also get stats for platform service containers
|
||||
const allPlatformServices = this.platformServices.getAllPlatformServices();
|
||||
const runningPlatformServices = allPlatformServices.filter((s) => s.status === 'running' && s.containerId);
|
||||
logger.debug(`Resource stats: ${runningPlatformServices.length} running platform services`);
|
||||
|
||||
const platformStatsPromises = runningPlatformServices
|
||||
.map((s) => {
|
||||
logger.debug(`Fetching stats for platform service: ${s.type} (${s.containerId})`);
|
||||
return this.docker.getContainerStats(s.containerId!).catch((err) => {
|
||||
logger.debug(`Stats failed for ${s.type}: ${(err as Error).message}`);
|
||||
return null;
|
||||
});
|
||||
});
|
||||
|
||||
const allStats = await Promise.all([...statsPromises, ...platformStatsPromises]);
|
||||
let successCount = 0;
|
||||
for (const stats of allStats) {
|
||||
if (stats) {
|
||||
successCount++;
|
||||
totalCpu += stats.cpuPercent;
|
||||
totalMemoryUsed += stats.memoryUsed;
|
||||
totalMemoryLimit = Math.max(totalMemoryLimit, stats.memoryLimit);
|
||||
totalNetworkIn += stats.networkRx;
|
||||
totalNetworkOut += stats.networkTx;
|
||||
}
|
||||
}
|
||||
logger.debug(`Resource stats: ${successCount}/${allStats.length} containers returned stats. CPU: ${totalCpu}, Mem: ${totalMemoryUsed}`);
|
||||
}
|
||||
|
||||
return {
|
||||
docker: {
|
||||
running: dockerRunning,
|
||||
version: dockerRunning ? await this.docker.getDockerVersion() : null,
|
||||
cpuUsage: Math.round(totalCpu * 10) / 10,
|
||||
memoryUsage: totalMemoryUsed,
|
||||
memoryTotal: totalMemoryLimit,
|
||||
networkIn: totalNetworkIn,
|
||||
networkOut: totalNetworkOut,
|
||||
},
|
||||
reverseProxy: proxyStatus,
|
||||
dns: {
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -8,6 +8,11 @@ export interface ISystemStatus {
|
||||
docker: {
|
||||
running: boolean;
|
||||
version: unknown;
|
||||
cpuUsage: number;
|
||||
memoryUsage: number;
|
||||
memoryTotal: number;
|
||||
networkIn: number;
|
||||
networkOut: number;
|
||||
};
|
||||
reverseProxy: {
|
||||
http: { running: boolean; port: number };
|
||||
|
||||
@@ -110,8 +110,8 @@ export class ObViewDashboard extends DeesElement {
|
||||
cpu: status?.docker?.cpuUsage || 0,
|
||||
memoryUsed: status?.docker?.memoryUsage || 0,
|
||||
memoryTotal: status?.docker?.memoryTotal || 0,
|
||||
networkIn: 0,
|
||||
networkOut: 0,
|
||||
networkIn: status?.docker?.networkIn || 0,
|
||||
networkOut: status?.docker?.networkOut || 0,
|
||||
topConsumers: [],
|
||||
},
|
||||
platformServices: platformServices.map((ps) => ({
|
||||
|
||||
Reference in New Issue
Block a user