feat: Implement network metrics integration and UI updates for real-time data display

This commit is contained in:
Juergen Kunz
2025-06-20 10:56:53 +00:00
parent b81bda6ce8
commit 92fde9d0d7
5 changed files with 377 additions and 48 deletions

View File

@ -30,6 +30,9 @@ export class OpsViewNetwork extends DeesElement {
@state()
private statsState = appstate.statsStatePart.getState();
@state()
private networkState = appstate.networkStatePart.getState();
@state()
private selectedTimeRange: '1m' | '5m' | '15m' | '1h' | '24h' = '5m';
@ -48,7 +51,7 @@ export class OpsViewNetwork extends DeesElement {
constructor() {
super();
this.subscribeToStateParts();
this.generateMockData(); // TODO: Replace with real data from metrics
this.updateNetworkData();
}
private subscribeToStateParts() {
@ -56,6 +59,11 @@ export class OpsViewNetwork extends DeesElement {
this.statsState = state;
this.updateNetworkData();
});
appstate.networkStatePart.state.subscribe((state) => {
this.networkState = state;
this.updateNetworkData();
});
}
public static styles = [
@ -221,6 +229,9 @@ export class OpsViewNetwork extends DeesElement {
]}
></dees-chart-area>
<!-- Top IPs Section -->
${this.renderTopIPs()}
<!-- Requests Table -->
<dees-table
.data=${this.getFilteredRequests()}
@ -290,7 +301,6 @@ export class OpsViewNetwork extends DeesElement {
iconName: 'copy',
action: async () => {
await navigator.clipboard.writeText(request.id);
// TODO: Implement toast notification when DeesToast.show is available
console.log('Request ID copied to clipboard');
}
}
@ -365,18 +375,17 @@ export class OpsViewNetwork extends DeesElement {
}
private calculateRequestsPerSecond(): number {
// TODO: Calculate from real data based on connection metrics
// For now, return a calculated value based on active connections
return Math.floor((this.statsState.serverStats?.activeConnections || 0) * 0.8);
// Calculate from actual request data in the last minute
const oneMinuteAgo = Date.now() - 60000;
const recentRequests = this.networkRequests.filter(req => req.timestamp >= oneMinuteAgo);
return Math.round(recentRequests.length / 60);
}
private calculateThroughput(): { in: number; out: number } {
// TODO: Calculate from real connection data
// For now, return estimated values
const activeConnections = this.statsState.serverStats?.activeConnections || 0;
// Use real throughput data from network state
return {
in: activeConnections * 1024 * 10, // 10KB per connection estimate
out: activeConnections * 1024 * 50, // 50KB per connection estimate
in: this.networkState.throughputRate.bytesInPerSecond,
out: this.networkState.throughputRate.bytesOutPerSecond,
};
}
@ -404,7 +413,6 @@ export class OpsViewNetwork extends DeesElement {
name: 'View Details',
iconName: 'magnifyingGlass',
action: async () => {
// TODO: Show connection details
},
},
],
@ -448,8 +456,6 @@ export class OpsViewNetwork extends DeesElement {
name: 'Export Data',
iconName: 'fileExport',
action: async () => {
// TODO: Export network data
// TODO: Implement toast notification when DeesToast.show is available
console.log('Export feature coming soon');
},
},
@ -461,43 +467,90 @@ export class OpsViewNetwork extends DeesElement {
private async refreshData() {
this.isLoading = true;
await appstate.statsStatePart.dispatchAction(appstate.fetchAllStatsAction, null);
await appstate.networkStatePart.dispatchAction(appstate.fetchNetworkStatsAction, null);
await this.updateNetworkData();
this.isLoading = false;
}
private renderTopIPs(): TemplateResult {
if (this.networkState.topIPs.length === 0) {
return html``;
}
return html`
<dees-table
.data=${this.networkState.topIPs}
.displayFunction=${(ipData: { ip: string; count: number }) => ({
'IP Address': ipData.ip,
'Connections': ipData.count,
'Percentage': ((ipData.count / this.networkState.connections.length) * 100).toFixed(1) + '%',
})}
heading1="Top Connected IPs"
heading2="IPs with most active connections"
.pagination=${false}
dataName="ip"
></dees-table>
`;
}
private async updateNetworkData() {
// TODO: Fetch real network data from the server
// For now, using mock data
this.generateMockData();
// Convert connection data to network requests format
if (this.networkState.connections.length > 0) {
this.networkRequests = this.networkState.connections.map((conn, index) => ({
id: conn.id,
timestamp: conn.startTime,
method: 'GET', // Default method for proxy connections
url: '/',
hostname: conn.remoteAddress,
port: conn.protocol === 'https' ? 443 : 80,
protocol: conn.protocol === 'https' || conn.protocol === 'http' ? conn.protocol : 'tcp',
statusCode: conn.state === 'connected' ? 200 : undefined,
duration: Date.now() - conn.startTime,
bytesIn: conn.bytesReceived,
bytesOut: conn.bytesSent,
remoteIp: conn.remoteAddress,
route: 'proxy',
}));
} else {
this.networkRequests = [];
}
// Generate traffic data based on request history
this.updateTrafficData();
}
private generateMockData() {
// Generate mock network requests
private updateTrafficData() {
const now = Date.now();
const protocols: Array<'http' | 'https' | 'tcp' | 'udp'> = ['http', 'https', 'tcp', 'udp'];
const methods = ['GET', 'POST', 'PUT', 'DELETE', 'HEAD', 'OPTIONS'];
const hosts = ['api.example.com', 'app.local', 'mail.server.com', 'dns.resolver.net'];
const timeRanges = {
'1m': 60 * 1000,
'5m': 5 * 60 * 1000,
'15m': 15 * 60 * 1000,
'1h': 60 * 60 * 1000,
'24h': 24 * 60 * 60 * 1000,
};
this.networkRequests = Array.from({ length: 100 }, (_, i) => ({
id: `req-${i}`,
timestamp: now - (i * 5000), // 5 seconds apart
method: methods[Math.floor(Math.random() * methods.length)],
url: `/api/v1/resource/${Math.floor(Math.random() * 100)}`,
hostname: hosts[Math.floor(Math.random() * hosts.length)],
port: Math.random() > 0.5 ? 443 : 80,
protocol: protocols[Math.floor(Math.random() * protocols.length)],
statusCode: Math.random() > 0.8 ? 404 : 200,
duration: Math.floor(Math.random() * 500),
bytesIn: Math.floor(Math.random() * 10000),
bytesOut: Math.floor(Math.random() * 50000),
remoteIp: `192.168.${Math.floor(Math.random() * 255)}.${Math.floor(Math.random() * 255)}`,
route: 'main-route',
}));
// Generate traffic data for chart
this.trafficData = Array.from({ length: 60 }, (_, i) => ({
x: now - (i * 60000), // 1 minute intervals
y: Math.floor(Math.random() * 100) + 50,
})).reverse();
const range = timeRanges[this.selectedTimeRange];
const bucketSize = range / 60; // 60 data points
// Create buckets for traffic data
const buckets = new Map<number, number>();
// Count requests per bucket
this.networkRequests.forEach(req => {
if (req.timestamp >= now - range) {
const bucketIndex = Math.floor((now - req.timestamp) / bucketSize);
const bucketTime = now - (bucketIndex * bucketSize);
buckets.set(bucketTime, (buckets.get(bucketTime) || 0) + 1);
}
});
// Convert to chart data
this.trafficData = Array.from({ length: 60 }, (_, i) => {
const time = now - (i * bucketSize);
return {
x: time,
y: buckets.get(time) || 0,
};
}).reverse();
}
}