|
@@ -1,10 +1,12 @@
|
|
|
import { Injectable, OnModuleInit, OnModuleDestroy, Logger } from '@nestjs/common';
|
|
import { Injectable, OnModuleInit, OnModuleDestroy, Logger } from '@nestjs/common';
|
|
|
import * as si from 'systeminformation';
|
|
import * as si from 'systeminformation';
|
|
|
|
|
|
|
|
|
|
+export type ServiceStatusType = 'ACTIVE' | 'IDLE' | 'OFFLINE';
|
|
|
|
|
+
|
|
|
export interface ServiceStatus {
|
|
export interface ServiceStatus {
|
|
|
service: string; // 'nestjs' | 'n8n' | 'ollama'
|
|
service: string; // 'nestjs' | 'n8n' | 'ollama'
|
|
|
pid: number | null;
|
|
pid: number | null;
|
|
|
- online: boolean;
|
|
|
|
|
|
|
+ status: ServiceStatusType;
|
|
|
cpu: number; // % usage
|
|
cpu: number; // % usage
|
|
|
memory: number; // bytes
|
|
memory: number; // bytes
|
|
|
}
|
|
}
|
|
@@ -21,7 +23,12 @@ export interface SystemMetrics {
|
|
|
// Keep legacy alias so the Gateway compiles without changes
|
|
// Keep legacy alias so the Gateway compiles without changes
|
|
|
export type MonitorPayload = SystemMetrics;
|
|
export type MonitorPayload = SystemMetrics;
|
|
|
|
|
|
|
|
-const TRACKED_NAMES = ['node', 'n8n', 'ollama'];
|
|
|
|
|
|
|
+// Command-based signatures — checked against p.command to avoid node.exe collisions
|
|
|
|
|
+const SERVICE_SIGNATURES: Record<string, string> = {
|
|
|
|
|
+ nestjs: 'main.js',
|
|
|
|
|
+ n8n: 'n8n',
|
|
|
|
|
+ ollama: 'ollama',
|
|
|
|
|
+};
|
|
|
|
|
|
|
|
@Injectable()
|
|
@Injectable()
|
|
|
export class SurveillanceService implements OnModuleInit, OnModuleDestroy {
|
|
export class SurveillanceService implements OnModuleInit, OnModuleDestroy {
|
|
@@ -94,27 +101,50 @@ export class SurveillanceService implements OnModuleInit, OnModuleDestroy {
|
|
|
private buildServiceStatuses(
|
|
private buildServiceStatuses(
|
|
|
processList: si.Systeminformation.ProcessesProcessData[],
|
|
processList: si.Systeminformation.ProcessesProcessData[],
|
|
|
): ServiceStatus[] {
|
|
): ServiceStatus[] {
|
|
|
- return TRACKED_NAMES.map((name) => {
|
|
|
|
|
|
|
+ const ACTIVE_CPU_THRESHOLD = 1.0;
|
|
|
|
|
+
|
|
|
|
|
+ const statuses = Object.entries(SERVICE_SIGNATURES).map(([serviceName, signature]) => {
|
|
|
if (processList.length === 0) {
|
|
if (processList.length === 0) {
|
|
|
- return { service: name, pid: null, online: false, cpu: 0, memory: 0 };
|
|
|
|
|
|
|
+ return { service: serviceName, pid: null, status: 'OFFLINE' as ServiceStatusType, cpu: 0, memory: 0 };
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- const match = processList.find((p) =>
|
|
|
|
|
- (p.name ?? '').toLowerCase().includes(name) ||
|
|
|
|
|
- (p.command ?? '').toLowerCase().includes(name),
|
|
|
|
|
|
|
+ // Single-pass filter by command — avoids node.exe collisions from name-only matching
|
|
|
|
|
+ const matches = processList.filter((p) =>
|
|
|
|
|
+ (p.command ?? '').toLowerCase().includes(signature.toLowerCase()),
|
|
|
);
|
|
);
|
|
|
|
|
|
|
|
- if (!match) {
|
|
|
|
|
- return { service: name, pid: null, online: false, cpu: 0, memory: 0 };
|
|
|
|
|
|
|
+ if (matches.length === 0) {
|
|
|
|
|
+ return { service: serviceName, pid: null, status: 'OFFLINE' as ServiceStatusType, cpu: 0, memory: 0 };
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // Aggregate CPU and RSS memory across all matching processes
|
|
|
|
|
+ const { totalCpu, totalMem, firstPid } = matches.reduce(
|
|
|
|
|
+ (acc, p) => ({
|
|
|
|
|
+ totalCpu: acc.totalCpu + (p.cpu ?? 0),
|
|
|
|
|
+ totalMem: acc.totalMem + (p.memRss ?? 0),
|
|
|
|
|
+ firstPid: acc.firstPid ?? p.pid,
|
|
|
|
|
+ }),
|
|
|
|
|
+ { totalCpu: 0, totalMem: 0, firstPid: null as number | null },
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ const status: ServiceStatusType = totalCpu > ACTIVE_CPU_THRESHOLD ? 'ACTIVE' : 'IDLE';
|
|
|
|
|
+
|
|
|
return {
|
|
return {
|
|
|
- service: name,
|
|
|
|
|
- pid: match.pid,
|
|
|
|
|
- online: true,
|
|
|
|
|
- cpu: parseFloat((match.cpu ?? 0).toFixed(2)),
|
|
|
|
|
- memory: match.memRss ?? 0,
|
|
|
|
|
|
|
+ service: serviceName,
|
|
|
|
|
+ pid: firstPid,
|
|
|
|
|
+ status,
|
|
|
|
|
+ cpu: parseFloat(totalCpu.toFixed(2)),
|
|
|
|
|
+ memory: totalMem,
|
|
|
};
|
|
};
|
|
|
});
|
|
});
|
|
|
|
|
+
|
|
|
|
|
+ // Verification debug log — one line per service per tick
|
|
|
|
|
+ for (const s of statuses) {
|
|
|
|
|
+ this.logger.debug(
|
|
|
|
|
+ `[aggregator] ${s.service.padEnd(8)} | status=${s.status.padEnd(7)} | cpu=${s.cpu.toFixed(2).padStart(6)}% | mem=${s.memory} bytes | pid=${s.pid ?? 'none'}`,
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return statuses;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|