update
This commit is contained in:
@@ -89,7 +89,7 @@ export const configStatePart = await appState.getStatePart<IConfigState>(
|
||||
export const uiStatePart = await appState.getStatePart<IUiState>(
|
||||
'ui',
|
||||
{
|
||||
activeView: 'dashboard',
|
||||
activeView: 'overview',
|
||||
sidebarCollapsed: false,
|
||||
autoRefresh: true,
|
||||
refreshInterval: 1000, // 1 second
|
||||
@@ -184,56 +184,35 @@ export const logoutAction = loginStatePart.createAction(async (statePartArg) =>
|
||||
};
|
||||
});
|
||||
|
||||
// Fetch All Stats Action
|
||||
// Fetch All Stats Action - Using combined endpoint for efficiency
|
||||
export const fetchAllStatsAction = statsStatePart.createAction(async (statePartArg) => {
|
||||
const context = getActionContext();
|
||||
|
||||
const currentState = statePartArg.getState();
|
||||
|
||||
try {
|
||||
// Fetch server stats
|
||||
const serverStatsRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
||||
interfaces.requests.IReq_GetServerStatistics
|
||||
>('/typedrequest', 'getServerStatistics');
|
||||
// Use combined metrics endpoint - single request instead of 4
|
||||
const combinedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
||||
interfaces.requests.IReq_GetCombinedMetrics
|
||||
>('/typedrequest', 'getCombinedMetrics');
|
||||
|
||||
const serverStatsResponse = await serverStatsRequest.fire({
|
||||
const combinedResponse = await combinedRequest.fire({
|
||||
identity: context.identity,
|
||||
includeHistory: false,
|
||||
sections: {
|
||||
server: true,
|
||||
email: true,
|
||||
dns: true,
|
||||
security: true,
|
||||
network: false, // Network is fetched separately for the network view
|
||||
},
|
||||
});
|
||||
|
||||
// Fetch email stats
|
||||
const emailStatsRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
||||
interfaces.requests.IReq_GetEmailStatistics
|
||||
>('/typedrequest', 'getEmailStatistics');
|
||||
|
||||
const emailStatsResponse = await emailStatsRequest.fire({
|
||||
identity: context.identity,
|
||||
});
|
||||
|
||||
// Fetch DNS stats
|
||||
const dnsStatsRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
||||
interfaces.requests.IReq_GetDnsStatistics
|
||||
>('/typedrequest', 'getDnsStatistics');
|
||||
|
||||
const dnsStatsResponse = await dnsStatsRequest.fire({
|
||||
identity: context.identity,
|
||||
});
|
||||
|
||||
// Fetch security metrics
|
||||
const securityRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
||||
interfaces.requests.IReq_GetSecurityMetrics
|
||||
>('/typedrequest', 'getSecurityMetrics');
|
||||
|
||||
const securityResponse = await securityRequest.fire({
|
||||
identity: context.identity,
|
||||
});
|
||||
|
||||
// Update state with all stats
|
||||
// Update state with all stats from combined response
|
||||
return {
|
||||
serverStats: serverStatsResponse.stats,
|
||||
emailStats: emailStatsResponse.stats,
|
||||
dnsStats: dnsStatsResponse.stats,
|
||||
securityMetrics: securityResponse.metrics,
|
||||
serverStats: combinedResponse.metrics.server || currentState.serverStats,
|
||||
emailStats: combinedResponse.metrics.email || currentState.emailStats,
|
||||
dnsStats: combinedResponse.metrics.dns || currentState.dnsStats,
|
||||
securityMetrics: combinedResponse.metrics.security || currentState.securityMetrics,
|
||||
lastUpdated: Date.now(),
|
||||
isLoading: false,
|
||||
error: null,
|
||||
@@ -342,6 +321,14 @@ export const toggleAutoRefreshAction = uiStatePart.createAction(async (statePart
|
||||
// Set Active View Action
|
||||
export const setActiveViewAction = uiStatePart.createAction<string>(async (statePartArg, viewName) => {
|
||||
const currentState = statePartArg.getState();
|
||||
|
||||
// If switching to network view, ensure we fetch network data
|
||||
if (viewName === 'network' && currentState.activeView !== 'network') {
|
||||
setTimeout(() => {
|
||||
networkStatePart.dispatchAction(fetchNetworkStatsAction, null);
|
||||
}, 100);
|
||||
}
|
||||
|
||||
return {
|
||||
...currentState,
|
||||
activeView: viewName,
|
||||
@@ -410,18 +397,118 @@ export const fetchNetworkStatsAction = networkStatePart.createAction(async (stat
|
||||
}
|
||||
});
|
||||
|
||||
// Combined refresh action for efficient polling
|
||||
async function dispatchCombinedRefreshAction() {
|
||||
const context = getActionContext();
|
||||
const currentView = uiStatePart.getState().activeView;
|
||||
|
||||
try {
|
||||
// Always fetch basic stats for dashboard widgets
|
||||
const combinedRequest = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
||||
interfaces.requests.IReq_GetCombinedMetrics
|
||||
>('/typedrequest', 'getCombinedMetrics');
|
||||
|
||||
const combinedResponse = await combinedRequest.fire({
|
||||
identity: context.identity,
|
||||
sections: {
|
||||
server: true,
|
||||
email: true,
|
||||
dns: true,
|
||||
security: true,
|
||||
network: currentView === 'network' || currentView === 'Network', // Only fetch network if on network view
|
||||
},
|
||||
});
|
||||
|
||||
// Update all stats from combined response
|
||||
statsStatePart.setState({
|
||||
...statsStatePart.getState(),
|
||||
serverStats: combinedResponse.metrics.server || statsStatePart.getState().serverStats,
|
||||
emailStats: combinedResponse.metrics.email || statsStatePart.getState().emailStats,
|
||||
dnsStats: combinedResponse.metrics.dns || statsStatePart.getState().dnsStats,
|
||||
securityMetrics: combinedResponse.metrics.security || statsStatePart.getState().securityMetrics,
|
||||
lastUpdated: Date.now(),
|
||||
isLoading: false,
|
||||
error: null,
|
||||
});
|
||||
|
||||
// Update network stats if included
|
||||
if (combinedResponse.metrics.network && (currentView === 'network' || currentView === 'Network')) {
|
||||
const network = combinedResponse.metrics.network;
|
||||
const connectionsByIP: { [ip: string]: number } = {};
|
||||
|
||||
// Convert connection details to IP counts
|
||||
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,
|
||||
});
|
||||
|
||||
networkStatePart.setState({
|
||||
...networkStatePart.getState(),
|
||||
connections: connectionsResponse.connections,
|
||||
connectionsByIP,
|
||||
throughputRate: {
|
||||
bytesInPerSecond: network.totalBandwidth.in,
|
||||
bytesOutPerSecond: network.totalBandwidth.out
|
||||
},
|
||||
topIPs: network.topEndpoints.map(e => ({ ip: e.endpoint, count: e.requests })),
|
||||
lastUpdated: Date.now(),
|
||||
isLoading: false,
|
||||
error: null,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch connections:', error);
|
||||
networkStatePart.setState({
|
||||
...networkStatePart.getState(),
|
||||
connections: [],
|
||||
connectionsByIP,
|
||||
throughputRate: {
|
||||
bytesInPerSecond: network.totalBandwidth.in,
|
||||
bytesOutPerSecond: network.totalBandwidth.out
|
||||
},
|
||||
topIPs: network.topEndpoints.map(e => ({ ip: e.endpoint, count: e.requests })),
|
||||
lastUpdated: Date.now(),
|
||||
isLoading: false,
|
||||
error: null,
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Combined refresh failed:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize auto-refresh
|
||||
let refreshInterval: NodeJS.Timeout | null = null;
|
||||
let currentRefreshRate = 1000; // Track current refresh rate to avoid unnecessary restarts
|
||||
|
||||
// Initialize auto-refresh when UI state is ready
|
||||
(() => {
|
||||
const startAutoRefresh = () => {
|
||||
const uiState = uiStatePart.getState();
|
||||
if (uiState.autoRefresh && loginStatePart.getState().isLoggedIn) {
|
||||
refreshInterval = setInterval(() => {
|
||||
statsStatePart.dispatchAction(fetchAllStatsAction, null);
|
||||
networkStatePart.dispatchAction(fetchNetworkStatsAction, null);
|
||||
}, uiState.refreshInterval);
|
||||
const loginState = loginStatePart.getState();
|
||||
|
||||
// Only start if conditions are met and not already running at the same rate
|
||||
if (uiState.autoRefresh && loginState.isLoggedIn) {
|
||||
// Check if we need to restart the interval (rate changed or not running)
|
||||
if (!refreshInterval || currentRefreshRate !== uiState.refreshInterval) {
|
||||
stopAutoRefresh();
|
||||
currentRefreshRate = uiState.refreshInterval;
|
||||
refreshInterval = setInterval(() => {
|
||||
// Use combined refresh action for efficiency
|
||||
dispatchCombinedRefreshAction();
|
||||
}, uiState.refreshInterval);
|
||||
}
|
||||
} else {
|
||||
stopAutoRefresh();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -429,18 +516,31 @@ let refreshInterval: NodeJS.Timeout | null = null;
|
||||
if (refreshInterval) {
|
||||
clearInterval(refreshInterval);
|
||||
refreshInterval = null;
|
||||
currentRefreshRate = 0;
|
||||
}
|
||||
};
|
||||
|
||||
// Watch for changes
|
||||
uiStatePart.state.subscribe(() => {
|
||||
stopAutoRefresh();
|
||||
startAutoRefresh();
|
||||
// Watch for relevant changes only
|
||||
let previousAutoRefresh = uiStatePart.getState().autoRefresh;
|
||||
let previousRefreshInterval = uiStatePart.getState().refreshInterval;
|
||||
let previousIsLoggedIn = loginStatePart.getState().isLoggedIn;
|
||||
|
||||
uiStatePart.state.subscribe((state) => {
|
||||
// Only restart if relevant values changed
|
||||
if (state.autoRefresh !== previousAutoRefresh ||
|
||||
state.refreshInterval !== previousRefreshInterval) {
|
||||
previousAutoRefresh = state.autoRefresh;
|
||||
previousRefreshInterval = state.refreshInterval;
|
||||
startAutoRefresh();
|
||||
}
|
||||
});
|
||||
|
||||
loginStatePart.state.subscribe(() => {
|
||||
stopAutoRefresh();
|
||||
startAutoRefresh();
|
||||
loginStatePart.state.subscribe((state) => {
|
||||
// Only restart if login state changed
|
||||
if (state.isLoggedIn !== previousIsLoggedIn) {
|
||||
previousIsLoggedIn = state.isLoggedIn;
|
||||
startAutoRefresh();
|
||||
}
|
||||
});
|
||||
|
||||
// Initial start
|
||||
|
@@ -127,6 +127,16 @@ export class OpsDashboard extends DeesElement {
|
||||
this.login(e.detail.data.username, e.detail.data.password);
|
||||
});
|
||||
|
||||
// Handle view changes
|
||||
const appDash = this.shadowRoot.querySelector('dees-simple-appdash');
|
||||
if (appDash) {
|
||||
appDash.addEventListener('viewSwitch', (e: CustomEvent) => {
|
||||
const viewName = e.detail.tabName;
|
||||
console.log('View switched to:', viewName);
|
||||
appstate.uiStatePart.dispatchAction(appstate.setActiveViewAction, viewName.toLowerCase());
|
||||
});
|
||||
}
|
||||
|
||||
// Handle initial state
|
||||
const loginState = appstate.loginStatePart.getState();
|
||||
console.log('Initial login state:', loginState);
|
||||
|
@@ -43,6 +43,10 @@ export class OpsViewNetwork extends DeesElement {
|
||||
@state()
|
||||
private trafficDataOut: Array<{ x: string | number; y: number }> = [];
|
||||
|
||||
// Track if we need to update the chart to avoid unnecessary re-renders
|
||||
private lastChartUpdate = 0;
|
||||
private chartUpdateThreshold = 1000; // Minimum ms between chart updates
|
||||
|
||||
private lastTrafficUpdateTime = 0;
|
||||
private trafficUpdateInterval = 1000; // Update every 1 second
|
||||
private requestCountHistory = new Map<number, number>(); // Track requests per time bucket
|
||||
@@ -59,21 +63,35 @@ export class OpsViewNetwork extends DeesElement {
|
||||
this.startTrafficUpdateTimer();
|
||||
}
|
||||
|
||||
async connectedCallback() {
|
||||
await super.connectedCallback();
|
||||
|
||||
// When network view becomes visible, ensure we fetch network data
|
||||
console.log('Network view connected - fetching initial data');
|
||||
await appstate.networkStatePart.dispatchAction(appstate.fetchNetworkStatsAction, null);
|
||||
|
||||
// Also update the active view state
|
||||
appstate.uiStatePart.dispatchAction(appstate.setActiveViewAction, 'network');
|
||||
}
|
||||
|
||||
async disconnectedCallback() {
|
||||
await super.disconnectedCallback();
|
||||
this.stopTrafficUpdateTimer();
|
||||
}
|
||||
|
||||
private subscribeToStateParts() {
|
||||
appstate.statsStatePart.state.subscribe((state) => {
|
||||
// Subscribe and track unsubscribe functions
|
||||
const statsUnsubscribe = appstate.statsStatePart.state.subscribe((state) => {
|
||||
this.statsState = state;
|
||||
this.updateNetworkData();
|
||||
});
|
||||
this.rxSubscriptions.push(statsUnsubscribe);
|
||||
|
||||
appstate.networkStatePart.state.subscribe((state) => {
|
||||
const networkUnsubscribe = appstate.networkStatePart.state.subscribe((state) => {
|
||||
this.networkState = state;
|
||||
this.updateNetworkData();
|
||||
});
|
||||
this.rxSubscriptions.push(networkUnsubscribe);
|
||||
}
|
||||
|
||||
private initializeTrafficData() {
|
||||
@@ -169,6 +187,13 @@ export class OpsViewNetwork extends DeesElement {
|
||||
];
|
||||
|
||||
public render() {
|
||||
console.log('Network view render - chart data points:', {
|
||||
inPoints: this.trafficDataIn.length,
|
||||
outPoints: this.trafficDataOut.length,
|
||||
lastInValue: this.trafficDataIn[this.trafficDataIn.length - 1]?.y,
|
||||
lastOutValue: this.trafficDataOut[this.trafficDataOut.length - 1]?.y
|
||||
});
|
||||
|
||||
return html`
|
||||
<ops-sectionheading>Network Activity</ops-sectionheading>
|
||||
|
||||
@@ -278,7 +303,6 @@ export class OpsViewNetwork extends DeesElement {
|
||||
iconName: 'copy',
|
||||
action: async () => {
|
||||
await navigator.clipboard.writeText(request.id);
|
||||
console.log('Request ID copied to clipboard');
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -366,6 +390,8 @@ export class OpsViewNetwork extends DeesElement {
|
||||
const reqPerSec = this.calculateRequestsPerSecond();
|
||||
const throughput = this.calculateThroughput();
|
||||
const activeConnections = this.statsState.serverStats?.activeConnections || 0;
|
||||
|
||||
// Throughput data is now available in the stats tiles
|
||||
|
||||
// Use request count history for the requests/sec trend
|
||||
const trendData = [...this.requestsPerSecHistory];
|
||||
@@ -466,25 +492,36 @@ export class OpsViewNetwork extends DeesElement {
|
||||
}
|
||||
|
||||
private async updateNetworkData() {
|
||||
// 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 = [];
|
||||
// Only update if connections changed significantly
|
||||
const newConnectionCount = this.networkState.connections.length;
|
||||
const oldConnectionCount = this.networkRequests.length;
|
||||
|
||||
// Check if we need to update the network requests array
|
||||
const shouldUpdate = newConnectionCount !== oldConnectionCount ||
|
||||
newConnectionCount === 0 ||
|
||||
(newConnectionCount > 0 && this.networkRequests.length === 0);
|
||||
|
||||
if (shouldUpdate) {
|
||||
// Convert connection data to network requests format
|
||||
if (newConnectionCount > 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
|
||||
@@ -492,87 +529,58 @@ export class OpsViewNetwork extends DeesElement {
|
||||
}
|
||||
|
||||
private updateTrafficData() {
|
||||
// This method is called when network data updates
|
||||
// The actual chart updates are handled by the timer calling addTrafficDataPoint()
|
||||
console.log('UpdateTrafficData called - network data updated');
|
||||
}
|
||||
|
||||
private startTrafficUpdateTimer() {
|
||||
this.stopTrafficUpdateTimer(); // Clear any existing timer
|
||||
this.trafficUpdateTimer = setInterval(() => {
|
||||
// Add a new data point every second
|
||||
this.addTrafficDataPoint();
|
||||
}, 1000); // Update every second
|
||||
}
|
||||
|
||||
private addTrafficDataPoint() {
|
||||
const now = Date.now();
|
||||
// Fixed 5 minute time range
|
||||
const range = 5 * 60 * 1000; // 5 minutes
|
||||
const bucketSize = range / 60; // 60 data points // 60 data points
|
||||
|
||||
// Check if enough time has passed to add a new data point
|
||||
const timeSinceLastUpdate = now - this.lastTrafficUpdateTime;
|
||||
const shouldAddNewPoint = timeSinceLastUpdate >= this.trafficUpdateInterval;
|
||||
|
||||
console.log('UpdateTrafficData called:', {
|
||||
networkRequestsCount: this.networkRequests.length,
|
||||
timeSinceLastUpdate,
|
||||
shouldAddNewPoint,
|
||||
currentDataPoints: this.trafficDataIn.length
|
||||
});
|
||||
|
||||
if (!shouldAddNewPoint && this.trafficDataIn.length > 0) {
|
||||
// Not enough time has passed, don't update
|
||||
// Throttle chart updates to avoid excessive re-renders
|
||||
if (now - this.lastChartUpdate < this.chartUpdateThreshold) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Use real-time throughput data from SmartProxy (same as throughput tiles)
|
||||
const throughput = this.calculateThroughput();
|
||||
|
||||
// Convert to Mbps (bytes * 8 / 1,000,000)
|
||||
const throughputInMbps = (throughput.in * 8) / 1000000;
|
||||
const throughputOutMbps = (throughput.out * 8) / 1000000;
|
||||
|
||||
console.log('Throughput calculation:', {
|
||||
bytesInPerSecond: throughput.in,
|
||||
bytesOutPerSecond: throughput.out,
|
||||
throughputInMbps,
|
||||
throughputOutMbps,
|
||||
throughputTileValue: `${this.formatBitsPerSecond(throughput.in)} IN, ${this.formatBitsPerSecond(throughput.out)} OUT`
|
||||
});
|
||||
// Add new data points
|
||||
const timestamp = new Date(now).toISOString();
|
||||
|
||||
if (this.trafficDataIn.length === 0) {
|
||||
// Initialize if empty
|
||||
this.initializeTrafficData();
|
||||
const newDataPointIn = {
|
||||
x: timestamp,
|
||||
y: Math.round(throughputInMbps * 10) / 10
|
||||
};
|
||||
|
||||
const newDataPointOut = {
|
||||
x: timestamp,
|
||||
y: Math.round(throughputOutMbps * 10) / 10
|
||||
};
|
||||
|
||||
// Efficient array updates - modify in place when possible
|
||||
if (this.trafficDataIn.length >= 60) {
|
||||
// Remove oldest and add newest
|
||||
this.trafficDataIn = [...this.trafficDataIn.slice(1), newDataPointIn];
|
||||
this.trafficDataOut = [...this.trafficDataOut.slice(1), newDataPointOut];
|
||||
} else {
|
||||
// Add new data points for both in and out
|
||||
const timestamp = new Date(now).toISOString();
|
||||
|
||||
const newDataPointIn = {
|
||||
x: timestamp,
|
||||
y: Math.round(throughputInMbps * 10) / 10 // Round to 1 decimal place
|
||||
};
|
||||
|
||||
const newDataPointOut = {
|
||||
x: timestamp,
|
||||
y: Math.round(throughputOutMbps * 10) / 10 // Round to 1 decimal place
|
||||
};
|
||||
|
||||
// Create new arrays with existing data plus new points
|
||||
const newTrafficDataIn = [...this.trafficDataIn, newDataPointIn];
|
||||
const newTrafficDataOut = [...this.trafficDataOut, newDataPointOut];
|
||||
|
||||
// Keep only the last 60 points
|
||||
if (newTrafficDataIn.length > 60) {
|
||||
newTrafficDataIn.shift(); // Remove oldest point
|
||||
newTrafficDataOut.shift();
|
||||
}
|
||||
|
||||
this.trafficDataIn = newTrafficDataIn;
|
||||
this.trafficDataOut = newTrafficDataOut;
|
||||
this.lastTrafficUpdateTime = now;
|
||||
|
||||
console.log('Added new traffic data points:', {
|
||||
timestamp: timestamp,
|
||||
throughputInMbps: newDataPointIn.y,
|
||||
throughputOutMbps: newDataPointOut.y,
|
||||
totalPoints: this.trafficDataIn.length
|
||||
});
|
||||
// Still filling up the initial data
|
||||
this.trafficDataIn = [...this.trafficDataIn, newDataPointIn];
|
||||
this.trafficDataOut = [...this.trafficDataOut, newDataPointOut];
|
||||
}
|
||||
}
|
||||
|
||||
private startTrafficUpdateTimer() {
|
||||
this.stopTrafficUpdateTimer(); // Clear any existing timer
|
||||
this.trafficUpdateTimer = setInterval(() => {
|
||||
this.updateTrafficData();
|
||||
}, 1000); // Check every second, but only update when interval has passed
|
||||
|
||||
this.lastChartUpdate = now;
|
||||
}
|
||||
|
||||
private stopTrafficUpdateTimer() {
|
||||
@@ -581,5 +589,4 @@ export class OpsViewNetwork extends DeesElement {
|
||||
this.trafficUpdateTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user