fix: NaN-Metriken, Chart-Zeitachse und erweiterte Gerätedetails
Backend: - AgentGrpcService: JSONB-Serialisierung auf camelCase umgestellt (JsonSerializer.SerializeToElement mit CamelCase-Options) → behebt NaN bei CPU, RAM, Disk-Anzeige in der Detailseite - AgentGrpcService: Result-JSONB explizit camelCase (exitCode, stdout, stderr, success) → behebt fehlende Befehlsergebnisse im Frontend - AgentGrpcService: SignalR-Payload enthält nun Disks und NetworkInterfaces - Program.cs: SignalR JsonProtocol auf CamelCase konfiguriert Agent (Go): - Heartbeat sendet nun NetworkInterfaces aus dem Collector → Netzwerkschnittstellen werden im Frontend angezeigt Frontend: - useAgentSignalR: onLiveMetrics-Callback für direktes Live-Update (kein API-Roundtrip mehr, < 50ms Latenz) - AgentDetailPage komplett überarbeitet: - Geräteinformationen-Karte (IP, MAC, OS, Version, Enrolled-At, Last-Seen) - Live-Indikator auf MetricCards (grüner Puls-Punkt bei SignalR-Verbindung) - NaN-Schutz für alle berechneten Werte (safePercent, memPercent) - Chart-Reihenfolge umgekehrt: älteste links, neueste rechts - X-Achse: adaptives Intervall verhindert Label-Überlappung - Netzwerkschnittstellen-Tabelle mit Traffic (RX/TX) - Festplatten mit Fortschrittsbalken + Filesystem-Typ - Strg+Enter für schnelle Befehlsausführung Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -14,6 +14,10 @@ namespace NexusRMM.Api.GrpcServices;
|
||||
|
||||
public class AgentGrpcService : AgentService.AgentServiceBase
|
||||
{
|
||||
private static readonly JsonSerializerOptions _camelCase = new()
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||
};
|
||||
private readonly RmmDbContext _db;
|
||||
private readonly ILogger<AgentGrpcService> _logger;
|
||||
private readonly IHubContext<RmmHub, IRmmHubClient> _hub;
|
||||
@@ -81,7 +85,7 @@ public class AgentGrpcService : AgentService.AgentServiceBase
|
||||
{
|
||||
AgentId = agentId,
|
||||
Timestamp = DateTime.UtcNow,
|
||||
Metrics = JsonSerializer.SerializeToElement(request.Metrics)
|
||||
Metrics = JsonSerializer.SerializeToElement(request.Metrics, _camelCase)
|
||||
});
|
||||
|
||||
var pendingTasks = await _db.Tasks
|
||||
@@ -104,15 +108,30 @@ public class AgentGrpcService : AgentService.AgentServiceBase
|
||||
|
||||
await _db.SaveChangesAsync();
|
||||
|
||||
// SignalR: Metriken an agent-Gruppe pushen
|
||||
// SignalR: Metriken an agent-Gruppe pushen (camelCase durch AddJsonProtocol-Konfiguration)
|
||||
await _hub.Clients.Group($"agent-{agentId}")
|
||||
.AgentMetricsUpdated(request.AgentId, new
|
||||
{
|
||||
CpuUsagePercent = request.Metrics?.CpuUsagePercent ?? 0,
|
||||
MemoryUsagePercent = request.Metrics?.MemoryUsagePercent ?? 0,
|
||||
MemoryTotalBytes = request.Metrics?.MemoryTotalBytes ?? 0,
|
||||
MemoryAvailableBytes = request.Metrics?.MemoryAvailableBytes ?? 0,
|
||||
UptimeSeconds = request.Metrics?.UptimeSeconds ?? 0,
|
||||
cpuUsagePercent = request.Metrics?.CpuUsagePercent ?? 0,
|
||||
memoryUsagePercent = request.Metrics?.MemoryUsagePercent ?? 0,
|
||||
memoryTotalBytes = request.Metrics?.MemoryTotalBytes ?? 0,
|
||||
memoryAvailableBytes = request.Metrics?.MemoryAvailableBytes ?? 0,
|
||||
uptimeSeconds = request.Metrics?.UptimeSeconds ?? 0,
|
||||
disks = request.Metrics?.Disks.Select(d => new
|
||||
{
|
||||
mountPoint = d.MountPoint,
|
||||
totalBytes = d.TotalBytes,
|
||||
freeBytes = d.FreeBytes,
|
||||
filesystem = d.Filesystem,
|
||||
}) ?? [],
|
||||
networkInterfaces = request.Metrics?.NetworkInterfaces.Select(n => new
|
||||
{
|
||||
name = n.Name,
|
||||
ipAddress = n.IpAddress,
|
||||
macAddress = n.MacAddress,
|
||||
bytesSent = n.BytesSent,
|
||||
bytesRecv = n.BytesRecv,
|
||||
}) ?? [],
|
||||
});
|
||||
|
||||
// SignalR: Status-Änderung an alle Clients pushen
|
||||
@@ -135,11 +154,11 @@ public class AgentGrpcService : AgentService.AgentServiceBase
|
||||
taskItem.CompletedAt = DateTime.UtcNow;
|
||||
taskItem.Result = JsonSerializer.SerializeToElement(new
|
||||
{
|
||||
request.ExitCode,
|
||||
request.Stdout,
|
||||
request.Stderr,
|
||||
request.Success
|
||||
});
|
||||
exitCode = request.ExitCode,
|
||||
stdout = request.Stdout,
|
||||
stderr = request.Stderr,
|
||||
success = request.Success,
|
||||
}, _camelCase);
|
||||
|
||||
await _db.SaveChangesAsync();
|
||||
|
||||
|
||||
@@ -19,7 +19,12 @@ builder.Services.AddDbContext<RmmDbContext>(options =>
|
||||
options.UseNpgsql(builder.Configuration.GetConnectionString("DefaultConnection")));
|
||||
|
||||
builder.Services.AddGrpc();
|
||||
builder.Services.AddSignalR();
|
||||
builder.Services.AddSignalR()
|
||||
.AddJsonProtocol(options =>
|
||||
{
|
||||
options.PayloadSerializerOptions.PropertyNamingPolicy = System.Text.Json.JsonNamingPolicy.CamelCase;
|
||||
options.PayloadSerializerOptions.Converters.Add(new System.Text.Json.Serialization.JsonStringEnumConverter());
|
||||
});
|
||||
builder.Services.AddControllers()
|
||||
.AddJsonOptions(options =>
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user