feat(network): add bandwidth-ranked IP and domain activity metrics to network monitoring

This commit is contained in:
2026-04-13 11:04:15 +00:00
parent 07a3365496
commit 035173702d
10 changed files with 275 additions and 227 deletions

View File

@@ -52,7 +52,9 @@ export interface INetworkState {
throughputRate: { bytesInPerSecond: number; bytesOutPerSecond: number };
totalBytes: { in: number; out: number };
topIPs: Array<{ ip: string; count: number }>;
topIPsByBandwidth: Array<{ ip: string; count: number; bwIn: number; bwOut: number }>;
throughputByIP: Array<{ ip: string; in: number; out: number }>;
domainActivity: interfaces.data.IDomainActivity[];
throughputHistory: Array<{ timestamp: number; in: number; out: number }>;
requestsPerSecond: number;
requestsTotal: number;
@@ -160,7 +162,9 @@ export const networkStatePart = await appState.getStatePart<INetworkState>(
throughputRate: { bytesInPerSecond: 0, bytesOutPerSecond: 0 },
totalBytes: { in: 0, out: 0 },
topIPs: [],
topIPsByBandwidth: [],
throughputByIP: [],
domainActivity: [],
throughputHistory: [],
requestsPerSecond: 0,
requestsTotal: 0,
@@ -552,7 +556,9 @@ export const fetchNetworkStatsAction = networkStatePart.createAction(async (stat
? { in: networkStatsResponse.totalDataTransferred.bytesIn, out: networkStatsResponse.totalDataTransferred.bytesOut }
: { in: 0, out: 0 },
topIPs: networkStatsResponse.topIPs || [],
topIPsByBandwidth: networkStatsResponse.topIPsByBandwidth || [],
throughputByIP: networkStatsResponse.throughputByIP || [],
domainActivity: networkStatsResponse.domainActivity || [],
throughputHistory: networkStatsResponse.throughputHistory || [],
requestsPerSecond: networkStatsResponse.requestsPerSecond || 0,
requestsTotal: networkStatsResponse.requestsTotal || 0,
@@ -2649,67 +2655,52 @@ async function dispatchCombinedRefreshActionInner() {
if (combinedResponse.metrics.network && currentView === 'network') {
const network = combinedResponse.metrics.network;
const connectionsByIP: { [ip: string]: number } = {};
// Convert connection details to IP counts
// Build connectionsByIP from connectionDetails (now populated with real per-IP data)
network.connectionDetails.forEach(conn => {
connectionsByIP[conn.remoteAddress] = (connectionsByIP[conn.remoteAddress] || 0) + 1;
});
// Fetch detailed connections for the network view
try {
const connectionsRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
interfaces.requests.IReq_GetActiveConnections
>('/typedrequest', 'getActiveConnections');
const connectionsResponse = await connectionsRequest.fire({
identity: context.identity,
});
// Build connections from connectionDetails (real per-IP aggregates)
const connections: interfaces.data.IConnectionInfo[] = network.connectionDetails.map((conn, i) => ({
id: `ip-${conn.remoteAddress}`,
remoteAddress: conn.remoteAddress,
localAddress: 'server',
startTime: conn.startTime,
protocol: conn.protocol as any,
state: conn.state as any,
bytesReceived: conn.bytesIn,
bytesSent: conn.bytesOut,
}));
networkStatePart.setState({
...networkStatePart.getState()!,
connections: connectionsResponse.connections,
connectionsByIP,
throughputRate: {
bytesInPerSecond: network.totalBandwidth.in,
bytesOutPerSecond: network.totalBandwidth.out
},
totalBytes: network.totalBytes || { in: 0, out: 0 },
topIPs: network.topEndpoints.map(e => ({ ip: e.endpoint, count: e.requests })),
throughputByIP: network.topEndpoints.map(e => ({ ip: e.endpoint, in: e.bandwidth?.in || 0, out: e.bandwidth?.out || 0 })),
throughputHistory: network.throughputHistory || [],
requestsPerSecond: network.requestsPerSecond || 0,
requestsTotal: network.requestsTotal || 0,
backends: network.backends || [],
frontendProtocols: network.frontendProtocols || null,
backendProtocols: network.backendProtocols || null,
lastUpdated: Date.now(),
isLoading: false,
error: null,
});
} catch (error: unknown) {
console.error('Failed to fetch connections:', error);
networkStatePart.setState({
...networkStatePart.getState()!,
connections: [],
connectionsByIP,
throughputRate: {
bytesInPerSecond: network.totalBandwidth.in,
bytesOutPerSecond: network.totalBandwidth.out
},
totalBytes: network.totalBytes || { in: 0, out: 0 },
topIPs: network.topEndpoints.map(e => ({ ip: e.endpoint, count: e.requests })),
throughputByIP: network.topEndpoints.map(e => ({ ip: e.endpoint, in: e.bandwidth?.in || 0, out: e.bandwidth?.out || 0 })),
throughputHistory: network.throughputHistory || [],
requestsPerSecond: network.requestsPerSecond || 0,
requestsTotal: network.requestsTotal || 0,
backends: network.backends || [],
frontendProtocols: network.frontendProtocols || null,
backendProtocols: network.backendProtocols || null,
lastUpdated: Date.now(),
isLoading: false,
error: null,
});
}
networkStatePart.setState({
...networkStatePart.getState()!,
connections,
connectionsByIP,
throughputRate: {
bytesInPerSecond: network.totalBandwidth.in,
bytesOutPerSecond: network.totalBandwidth.out,
},
totalBytes: network.totalBytes || { in: 0, out: 0 },
topIPs: network.topEndpoints.map(e => ({ ip: e.endpoint, count: e.connections })),
topIPsByBandwidth: (network.topEndpointsByBandwidth || []).map(e => ({
ip: e.endpoint,
count: e.connections,
bwIn: e.bandwidth?.in || 0,
bwOut: e.bandwidth?.out || 0,
})),
throughputByIP: network.topEndpoints.map(e => ({ ip: e.endpoint, in: e.bandwidth?.in || 0, out: e.bandwidth?.out || 0 })),
domainActivity: network.domainActivity || [],
throughputHistory: network.throughputHistory || [],
requestsPerSecond: network.requestsPerSecond || 0,
requestsTotal: network.requestsTotal || 0,
backends: network.backends || [],
frontendProtocols: network.frontendProtocols || null,
backendProtocols: network.backendProtocols || null,
lastUpdated: Date.now(),
isLoading: false,
error: null,
});
}
// Refresh certificate data if on Domains > Certificates subview