@serve.zone/dcrouter 13.2.2 → 13.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist_serve/bundle.js +1198 -1142
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/dist_ts_web/appstate.d.ts +1 -0
- package/dist_ts_web/appstate.js +9 -22
- package/dist_ts_web/elements/index.d.ts +2 -6
- package/dist_ts_web/elements/index.js +3 -7
- package/dist_ts_web/elements/network/index.d.ts +6 -0
- package/dist_ts_web/elements/network/index.js +7 -0
- package/dist_ts_web/elements/{ops-view-network.d.ts → network/ops-view-network-activity.d.ts} +3 -3
- package/dist_ts_web/elements/{ops-view-network.js → network/ops-view-network-activity.js} +20 -33
- package/dist_ts_web/elements/network/ops-view-network.d.ts +24 -0
- package/dist_ts_web/elements/network/ops-view-network.js +151 -0
- package/dist_ts_web/elements/{ops-view-networktargets.d.ts → network/ops-view-networktargets.d.ts} +1 -1
- package/dist_ts_web/elements/{ops-view-networktargets.js → network/ops-view-networktargets.js} +5 -6
- package/dist_ts_web/elements/{ops-view-routes.d.ts → network/ops-view-routes.d.ts} +1 -1
- package/dist_ts_web/elements/{ops-view-routes.js → network/ops-view-routes.js} +5 -6
- package/dist_ts_web/elements/{ops-view-sourceprofiles.d.ts → network/ops-view-sourceprofiles.d.ts} +1 -1
- package/dist_ts_web/elements/{ops-view-sourceprofiles.js → network/ops-view-sourceprofiles.js} +5 -6
- package/dist_ts_web/elements/{ops-view-targetprofiles.d.ts → network/ops-view-targetprofiles.d.ts} +2 -2
- package/dist_ts_web/elements/{ops-view-targetprofiles.js → network/ops-view-targetprofiles.js} +6 -7
- package/dist_ts_web/elements/ops-dashboard.js +4 -27
- package/dist_ts_web/elements/ops-view-config.js +3 -3
- package/dist_ts_web/elements/security/index.d.ts +5 -0
- package/dist_ts_web/elements/security/index.js +6 -0
- package/dist_ts_web/elements/security/ops-view-security-authentication.d.ts +13 -0
- package/dist_ts_web/elements/security/ops-view-security-authentication.js +156 -0
- package/dist_ts_web/elements/security/ops-view-security-blocked.d.ts +15 -0
- package/dist_ts_web/elements/security/ops-view-security-blocked.js +152 -0
- package/dist_ts_web/elements/security/ops-view-security-emailsecurity.d.ts +14 -0
- package/dist_ts_web/elements/security/ops-view-security-emailsecurity.js +196 -0
- package/dist_ts_web/elements/security/ops-view-security-overview.d.ts +16 -0
- package/dist_ts_web/elements/security/ops-view-security-overview.js +204 -0
- package/dist_ts_web/elements/security/ops-view-security.d.ts +23 -0
- package/dist_ts_web/elements/security/ops-view-security.js +146 -0
- package/dist_ts_web/router.d.ts +5 -3
- package/dist_ts_web/router.js +69 -17
- package/package.json +1 -1
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/appstate.ts +10 -24
- package/ts_web/elements/index.ts +2 -6
- package/ts_web/elements/network/index.ts +6 -0
- package/ts_web/elements/{ops-view-network.ts → network/ops-view-network-activity.ts} +43 -56
- package/ts_web/elements/network/ops-view-network.ts +119 -0
- package/ts_web/elements/{ops-view-networktargets.ts → network/ops-view-networktargets.ts} +4 -5
- package/ts_web/elements/{ops-view-routes.ts → network/ops-view-routes.ts} +4 -5
- package/ts_web/elements/{ops-view-sourceprofiles.ts → network/ops-view-sourceprofiles.ts} +4 -5
- package/ts_web/elements/{ops-view-targetprofiles.ts → network/ops-view-targetprofiles.ts} +5 -6
- package/ts_web/elements/ops-dashboard.ts +3 -26
- package/ts_web/elements/ops-view-config.ts +2 -2
- package/ts_web/elements/security/index.ts +5 -0
- package/ts_web/elements/security/ops-view-security-authentication.ts +120 -0
- package/ts_web/elements/security/ops-view-security-blocked.ts +117 -0
- package/ts_web/elements/security/ops-view-security-emailsecurity.ts +159 -0
- package/ts_web/elements/security/ops-view-security-overview.ts +171 -0
- package/ts_web/elements/security/ops-view-security.ts +114 -0
- package/ts_web/router.ts +75 -17
- package/dist_ts_web/elements/ops-view-security.d.ts +0 -24
- package/dist_ts_web/elements/ops-view-security.js +0 -484
- package/ts_web/elements/ops-view-security.ts +0 -456
package/ts_web/appstate.ts
CHANGED
|
@@ -30,6 +30,7 @@ export interface IConfigState {
|
|
|
30
30
|
|
|
31
31
|
export interface IUiState {
|
|
32
32
|
activeView: string;
|
|
33
|
+
activeSubview: string | null;
|
|
33
34
|
sidebarCollapsed: boolean;
|
|
34
35
|
autoRefresh: boolean;
|
|
35
36
|
refreshInterval: number; // milliseconds
|
|
@@ -116,16 +117,24 @@ export const configStatePart = await appState.getStatePart<IConfigState>(
|
|
|
116
117
|
// Determine initial view from URL path
|
|
117
118
|
const getInitialView = (): string => {
|
|
118
119
|
const path = typeof window !== 'undefined' ? window.location.pathname : '/';
|
|
119
|
-
const validViews = ['overview', 'network', 'emails', 'logs', '
|
|
120
|
+
const validViews = ['overview', 'network', 'emails', 'logs', 'apitokens', 'configuration', 'security', 'certificates', 'remoteingress', 'vpn'];
|
|
120
121
|
const segments = path.split('/').filter(Boolean);
|
|
121
122
|
const view = segments[0];
|
|
122
123
|
return validViews.includes(view) ? view : 'overview';
|
|
123
124
|
};
|
|
124
125
|
|
|
126
|
+
// Determine initial subview (second URL segment) from the path
|
|
127
|
+
const getInitialSubview = (): string | null => {
|
|
128
|
+
const path = typeof window !== 'undefined' ? window.location.pathname : '/';
|
|
129
|
+
const segments = path.split('/').filter(Boolean);
|
|
130
|
+
return segments[1] ?? null;
|
|
131
|
+
};
|
|
132
|
+
|
|
125
133
|
export const uiStatePart = await appState.getStatePart<IUiState>(
|
|
126
134
|
'ui',
|
|
127
135
|
{
|
|
128
136
|
activeView: getInitialView(),
|
|
137
|
+
activeSubview: getInitialSubview(),
|
|
129
138
|
sidebarCollapsed: false,
|
|
130
139
|
autoRefresh: true,
|
|
131
140
|
refreshInterval: 1000, // 1 second
|
|
@@ -435,15 +444,6 @@ export const setActiveViewAction = uiStatePart.createAction<string>(async (state
|
|
|
435
444
|
}, 100);
|
|
436
445
|
}
|
|
437
446
|
|
|
438
|
-
// If switching to routes view, ensure we fetch route data
|
|
439
|
-
if (viewName === 'routes' && currentState.activeView !== 'routes') {
|
|
440
|
-
setTimeout(() => {
|
|
441
|
-
routeManagementStatePart.dispatchAction(fetchMergedRoutesAction, null);
|
|
442
|
-
// Also fetch profiles/targets for the Create Route dropdowns
|
|
443
|
-
profilesTargetsStatePart.dispatchAction(fetchProfilesAndTargetsAction, null);
|
|
444
|
-
}, 100);
|
|
445
|
-
}
|
|
446
|
-
|
|
447
447
|
// If switching to apitokens view, ensure we fetch token data
|
|
448
448
|
if (viewName === 'apitokens' && currentState.activeView !== 'apitokens') {
|
|
449
449
|
setTimeout(() => {
|
|
@@ -458,20 +458,6 @@ export const setActiveViewAction = uiStatePart.createAction<string>(async (state
|
|
|
458
458
|
}, 100);
|
|
459
459
|
}
|
|
460
460
|
|
|
461
|
-
// If switching to security profiles or network targets views, fetch profiles/targets data
|
|
462
|
-
if ((viewName === 'sourceprofiles' || viewName === 'networktargets') && currentState.activeView !== viewName) {
|
|
463
|
-
setTimeout(() => {
|
|
464
|
-
profilesTargetsStatePart.dispatchAction(fetchProfilesAndTargetsAction, null);
|
|
465
|
-
}, 100);
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
// If switching to target profiles view, fetch target profiles data
|
|
469
|
-
if (viewName === 'targetprofiles' && currentState.activeView !== viewName) {
|
|
470
|
-
setTimeout(() => {
|
|
471
|
-
targetProfilesStatePart.dispatchAction(fetchTargetProfilesAction, null);
|
|
472
|
-
}, 100);
|
|
473
|
-
}
|
|
474
|
-
|
|
475
461
|
return {
|
|
476
462
|
...currentState,
|
|
477
463
|
activeView: viewName,
|
package/ts_web/elements/index.ts
CHANGED
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
export * from './ops-dashboard.js';
|
|
2
2
|
export * from './ops-view-overview.js';
|
|
3
|
-
export * from './
|
|
3
|
+
export * from './network/index.js';
|
|
4
4
|
export * from './ops-view-emails.js';
|
|
5
5
|
export * from './ops-view-logs.js';
|
|
6
6
|
export * from './ops-view-config.js';
|
|
7
|
-
export * from './ops-view-routes.js';
|
|
8
7
|
export * from './ops-view-apitokens.js';
|
|
9
|
-
export * from './
|
|
8
|
+
export * from './security/index.js';
|
|
10
9
|
export * from './ops-view-certificates.js';
|
|
11
10
|
export * from './ops-view-remoteingress.js';
|
|
12
11
|
export * from './ops-view-vpn.js';
|
|
13
|
-
export * from './ops-view-sourceprofiles.js';
|
|
14
|
-
export * from './ops-view-networktargets.js';
|
|
15
|
-
export * from './ops-view-targetprofiles.js';
|
|
16
12
|
export * from './shared/index.js';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export * from './ops-view-network.js';
|
|
2
|
+
export * from './ops-view-network-activity.js';
|
|
3
|
+
export * from './ops-view-routes.js';
|
|
4
|
+
export * from './ops-view-sourceprofiles.js';
|
|
5
|
+
export * from './ops-view-networktargets.js';
|
|
6
|
+
export * from './ops-view-targetprofiles.js';
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { DeesElement, property, html, customElement, type TemplateResult, css, state, cssManager } from '@design.estate/dees-element';
|
|
2
|
-
import * as appstate from '
|
|
3
|
-
import * as interfaces from '
|
|
4
|
-
import { viewHostCss } from './shared/css.js';
|
|
2
|
+
import * as appstate from '../../appstate.js';
|
|
3
|
+
import * as interfaces from '../../../dist_ts_interfaces/index.js';
|
|
5
4
|
import { type IStatsTile } from '@design.estate/dees-catalog';
|
|
6
5
|
|
|
7
6
|
declare global {
|
|
8
7
|
interface HTMLElementTagNameMap {
|
|
9
|
-
'ops-view-network':
|
|
8
|
+
'ops-view-network-activity': OpsViewNetworkActivity;
|
|
10
9
|
}
|
|
11
10
|
}
|
|
12
11
|
|
|
@@ -26,14 +25,14 @@ interface INetworkRequest {
|
|
|
26
25
|
route?: string;
|
|
27
26
|
}
|
|
28
27
|
|
|
29
|
-
@customElement('ops-view-network')
|
|
30
|
-
export class
|
|
28
|
+
@customElement('ops-view-network-activity')
|
|
29
|
+
export class OpsViewNetworkActivity extends DeesElement {
|
|
31
30
|
/** How far back the traffic chart shows */
|
|
32
31
|
private static readonly CHART_WINDOW_MS = 5 * 60 * 1000; // 5 minutes
|
|
33
32
|
/** How often a new data point is added */
|
|
34
33
|
private static readonly UPDATE_INTERVAL_MS = 1000; // 1 second
|
|
35
34
|
/** Derived: max data points the buffer holds */
|
|
36
|
-
private static readonly MAX_DATA_POINTS =
|
|
35
|
+
private static readonly MAX_DATA_POINTS = OpsViewNetworkActivity.CHART_WINDOW_MS / OpsViewNetworkActivity.UPDATE_INTERVAL_MS;
|
|
37
36
|
|
|
38
37
|
@state()
|
|
39
38
|
accessor statsState = appstate.statsStatePart.getState()!;
|
|
@@ -50,10 +49,10 @@ export class OpsViewNetwork extends DeesElement {
|
|
|
50
49
|
|
|
51
50
|
@state()
|
|
52
51
|
accessor trafficDataOut: Array<{ x: string | number; y: number }> = [];
|
|
53
|
-
|
|
52
|
+
|
|
54
53
|
// Track if we need to update the chart to avoid unnecessary re-renders
|
|
55
54
|
private lastChartUpdate = 0;
|
|
56
|
-
private chartUpdateThreshold =
|
|
55
|
+
private chartUpdateThreshold = OpsViewNetworkActivity.UPDATE_INTERVAL_MS; // Minimum ms between chart updates
|
|
57
56
|
|
|
58
57
|
private trafficUpdateTimer: any = null;
|
|
59
58
|
private requestsPerSecHistory: number[] = []; // Track requests/sec over time for trend
|
|
@@ -101,17 +100,17 @@ export class OpsViewNetwork extends DeesElement {
|
|
|
101
100
|
this.updateNetworkData();
|
|
102
101
|
});
|
|
103
102
|
this.rxSubscriptions.push(statsUnsubscribe);
|
|
104
|
-
|
|
103
|
+
|
|
105
104
|
const networkUnsubscribe = appstate.networkStatePart.select().subscribe((state) => {
|
|
106
105
|
this.networkState = state;
|
|
107
106
|
this.updateNetworkData();
|
|
108
107
|
});
|
|
109
108
|
this.rxSubscriptions.push(networkUnsubscribe);
|
|
110
109
|
}
|
|
111
|
-
|
|
110
|
+
|
|
112
111
|
private initializeTrafficData() {
|
|
113
112
|
const now = Date.now();
|
|
114
|
-
const { MAX_DATA_POINTS, UPDATE_INTERVAL_MS } =
|
|
113
|
+
const { MAX_DATA_POINTS, UPDATE_INTERVAL_MS } = OpsViewNetworkActivity;
|
|
115
114
|
|
|
116
115
|
// Initialize with empty data points for both in and out
|
|
117
116
|
const emptyData = Array.from({ length: MAX_DATA_POINTS }, (_, i) => {
|
|
@@ -148,7 +147,7 @@ export class OpsViewNetwork extends DeesElement {
|
|
|
148
147
|
y: Math.round((p.out * 8) / 1000000 * 10) / 10,
|
|
149
148
|
}));
|
|
150
149
|
|
|
151
|
-
const { MAX_DATA_POINTS, UPDATE_INTERVAL_MS } =
|
|
150
|
+
const { MAX_DATA_POINTS, UPDATE_INTERVAL_MS } = OpsViewNetworkActivity;
|
|
152
151
|
|
|
153
152
|
// Use history as the chart data, keeping the most recent points within the window
|
|
154
153
|
const sliceStart = Math.max(0, historyIn.length - MAX_DATA_POINTS);
|
|
@@ -176,8 +175,8 @@ export class OpsViewNetwork extends DeesElement {
|
|
|
176
175
|
|
|
177
176
|
public static styles = [
|
|
178
177
|
cssManager.defaultStyles,
|
|
179
|
-
viewHostCss,
|
|
180
178
|
css`
|
|
179
|
+
:host { display: block; }
|
|
181
180
|
.networkContainer {
|
|
182
181
|
display: flex;
|
|
183
182
|
flex-direction: column;
|
|
@@ -285,8 +284,8 @@ export class OpsViewNetwork extends DeesElement {
|
|
|
285
284
|
|
|
286
285
|
public render() {
|
|
287
286
|
return html`
|
|
288
|
-
<dees-heading level="
|
|
289
|
-
|
|
287
|
+
<dees-heading level="hr">Network Activity</dees-heading>
|
|
288
|
+
|
|
290
289
|
<div class="networkContainer">
|
|
291
290
|
<!-- Stats Grid -->
|
|
292
291
|
${this.renderNetworkStats()}
|
|
@@ -307,7 +306,7 @@ export class OpsViewNetwork extends DeesElement {
|
|
|
307
306
|
}
|
|
308
307
|
]}
|
|
309
308
|
.realtimeMode=${true}
|
|
310
|
-
.rollingWindow=${
|
|
309
|
+
.rollingWindow=${OpsViewNetworkActivity.CHART_WINDOW_MS}
|
|
311
310
|
.yAxisFormatter=${(val: number) => `${val} Mbit/s`}
|
|
312
311
|
></dees-chart-area>
|
|
313
312
|
|
|
@@ -323,7 +322,6 @@ export class OpsViewNetwork extends DeesElement {
|
|
|
323
322
|
<!-- Requests Table -->
|
|
324
323
|
<dees-table
|
|
325
324
|
.data=${this.networkRequests}
|
|
326
|
-
.showColumnFilters=${true}
|
|
327
325
|
.displayFunction=${(req: INetworkRequest) => ({
|
|
328
326
|
Time: new Date(req.timestamp).toLocaleTimeString(),
|
|
329
327
|
Protocol: html`<span class="protocolBadge ${req.protocol}">${req.protocol.toUpperCase()}</span>`,
|
|
@@ -358,7 +356,7 @@ export class OpsViewNetwork extends DeesElement {
|
|
|
358
356
|
|
|
359
357
|
private async showRequestDetails(request: INetworkRequest) {
|
|
360
358
|
const { DeesModal } = await import('@design.estate/dees-catalog');
|
|
361
|
-
|
|
359
|
+
|
|
362
360
|
await DeesModal.createAndShow({
|
|
363
361
|
heading: 'Request Details',
|
|
364
362
|
content: html`
|
|
@@ -401,10 +399,10 @@ export class OpsViewNetwork extends DeesElement {
|
|
|
401
399
|
if (!statusCode) {
|
|
402
400
|
return html`<span class="statusBadge warning">N/A</span>`;
|
|
403
401
|
}
|
|
404
|
-
|
|
402
|
+
|
|
405
403
|
const statusClass = statusCode >= 200 && statusCode < 300 ? 'success' :
|
|
406
404
|
statusCode >= 400 ? 'error' : 'warning';
|
|
407
|
-
|
|
405
|
+
|
|
408
406
|
return html`<span class="statusBadge ${statusClass}">${statusCode}</span>`;
|
|
409
407
|
}
|
|
410
408
|
|
|
@@ -427,26 +425,26 @@ export class OpsViewNetwork extends DeesElement {
|
|
|
427
425
|
const units = ['B', 'KB', 'MB', 'GB'];
|
|
428
426
|
let size = bytes;
|
|
429
427
|
let unitIndex = 0;
|
|
430
|
-
|
|
428
|
+
|
|
431
429
|
while (size >= 1024 && unitIndex < units.length - 1) {
|
|
432
430
|
size /= 1024;
|
|
433
431
|
unitIndex++;
|
|
434
432
|
}
|
|
435
|
-
|
|
433
|
+
|
|
436
434
|
return `${size.toFixed(1)} ${units[unitIndex]}`;
|
|
437
435
|
}
|
|
438
|
-
|
|
436
|
+
|
|
439
437
|
private formatBitsPerSecond(bytesPerSecond: number): string {
|
|
440
438
|
const bitsPerSecond = bytesPerSecond * 8; // Convert bytes to bits
|
|
441
439
|
const units = ['bit/s', 'kbit/s', 'Mbit/s', 'Gbit/s'];
|
|
442
440
|
let size = bitsPerSecond;
|
|
443
441
|
let unitIndex = 0;
|
|
444
|
-
|
|
442
|
+
|
|
445
443
|
while (size >= 1000 && unitIndex < units.length - 1) {
|
|
446
444
|
size /= 1000; // Use 1000 for bits (not 1024)
|
|
447
445
|
unitIndex++;
|
|
448
446
|
}
|
|
449
|
-
|
|
447
|
+
|
|
450
448
|
return `${size.toFixed(1)} ${units[unitIndex]}`;
|
|
451
449
|
}
|
|
452
450
|
|
|
@@ -521,18 +519,9 @@ export class OpsViewNetwork extends DeesElement {
|
|
|
521
519
|
];
|
|
522
520
|
|
|
523
521
|
return html`
|
|
524
|
-
<dees-statsgrid
|
|
522
|
+
<dees-statsgrid
|
|
525
523
|
.tiles=${tiles}
|
|
526
524
|
.minTileWidth=${200}
|
|
527
|
-
.gridActions=${[
|
|
528
|
-
{
|
|
529
|
-
name: 'Export Data',
|
|
530
|
-
iconName: 'lucide:FileOutput',
|
|
531
|
-
action: async () => {
|
|
532
|
-
console.log('Export feature coming soon');
|
|
533
|
-
},
|
|
534
|
-
},
|
|
535
|
-
]}
|
|
536
525
|
></dees-statsgrid>
|
|
537
526
|
`;
|
|
538
527
|
}
|
|
@@ -604,7 +593,6 @@ export class OpsViewNetwork extends DeesElement {
|
|
|
604
593
|
return html`
|
|
605
594
|
<dees-table
|
|
606
595
|
.data=${this.networkState.topIPs}
|
|
607
|
-
.showColumnFilters=${true}
|
|
608
596
|
.displayFunction=${(ipData: { ip: string; count: number }) => {
|
|
609
597
|
const bw = bandwidthByIP.get(ipData.ip);
|
|
610
598
|
return {
|
|
@@ -632,7 +620,6 @@ export class OpsViewNetwork extends DeesElement {
|
|
|
632
620
|
return html`
|
|
633
621
|
<dees-table
|
|
634
622
|
.data=${backends}
|
|
635
|
-
.showColumnFilters=${true}
|
|
636
623
|
.displayFunction=${(item: interfaces.data.IBackendInfo) => {
|
|
637
624
|
const totalErrors = item.connectErrors + item.handshakeErrors + item.requestErrors;
|
|
638
625
|
const protocolClass = item.protocol.toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
@@ -735,12 +722,12 @@ export class OpsViewNetwork extends DeesElement {
|
|
|
735
722
|
// Only update if connections changed significantly
|
|
736
723
|
const newConnectionCount = this.networkState.connections.length;
|
|
737
724
|
const oldConnectionCount = this.networkRequests.length;
|
|
738
|
-
|
|
725
|
+
|
|
739
726
|
// Check if we need to update the network requests array
|
|
740
|
-
const shouldUpdate = newConnectionCount !== oldConnectionCount ||
|
|
727
|
+
const shouldUpdate = newConnectionCount !== oldConnectionCount ||
|
|
741
728
|
newConnectionCount === 0 ||
|
|
742
729
|
(newConnectionCount > 0 && this.networkRequests.length === 0);
|
|
743
|
-
|
|
730
|
+
|
|
744
731
|
if (shouldUpdate) {
|
|
745
732
|
// Convert connection data to network requests format
|
|
746
733
|
if (newConnectionCount > 0) {
|
|
@@ -763,62 +750,62 @@ export class OpsViewNetwork extends DeesElement {
|
|
|
763
750
|
this.networkRequests = [];
|
|
764
751
|
}
|
|
765
752
|
}
|
|
766
|
-
|
|
753
|
+
|
|
767
754
|
// Load server-side throughput history into chart (once)
|
|
768
755
|
if (!this.historyLoaded && this.networkState.throughputHistory && this.networkState.throughputHistory.length > 0) {
|
|
769
756
|
this.loadThroughputHistory();
|
|
770
757
|
}
|
|
771
758
|
}
|
|
772
|
-
|
|
759
|
+
|
|
773
760
|
private startTrafficUpdateTimer() {
|
|
774
761
|
this.stopTrafficUpdateTimer(); // Clear any existing timer
|
|
775
762
|
this.trafficUpdateTimer = setInterval(() => {
|
|
776
763
|
this.addTrafficDataPoint();
|
|
777
|
-
},
|
|
764
|
+
}, OpsViewNetworkActivity.UPDATE_INTERVAL_MS);
|
|
778
765
|
}
|
|
779
|
-
|
|
766
|
+
|
|
780
767
|
private addTrafficDataPoint() {
|
|
781
768
|
const now = Date.now();
|
|
782
|
-
|
|
769
|
+
|
|
783
770
|
// Throttle chart updates to avoid excessive re-renders
|
|
784
771
|
if (now - this.lastChartUpdate < this.chartUpdateThreshold) {
|
|
785
772
|
return;
|
|
786
773
|
}
|
|
787
|
-
|
|
774
|
+
|
|
788
775
|
const throughput = this.calculateThroughput();
|
|
789
|
-
|
|
776
|
+
|
|
790
777
|
// Convert to Mbps (bytes * 8 / 1,000,000)
|
|
791
778
|
const throughputInMbps = (throughput.in * 8) / 1000000;
|
|
792
779
|
const throughputOutMbps = (throughput.out * 8) / 1000000;
|
|
793
|
-
|
|
780
|
+
|
|
794
781
|
// Add new data points
|
|
795
782
|
const timestamp = new Date(now).toISOString();
|
|
796
|
-
|
|
783
|
+
|
|
797
784
|
const newDataPointIn = {
|
|
798
785
|
x: timestamp,
|
|
799
786
|
y: Math.round(throughputInMbps * 10) / 10
|
|
800
787
|
};
|
|
801
|
-
|
|
788
|
+
|
|
802
789
|
const newDataPointOut = {
|
|
803
790
|
x: timestamp,
|
|
804
791
|
y: Math.round(throughputOutMbps * 10) / 10
|
|
805
792
|
};
|
|
806
|
-
|
|
793
|
+
|
|
807
794
|
// In-place mutation then reassign for Lit reactivity (avoids 4 intermediate arrays)
|
|
808
|
-
if (this.trafficDataIn.length >=
|
|
795
|
+
if (this.trafficDataIn.length >= OpsViewNetworkActivity.MAX_DATA_POINTS) {
|
|
809
796
|
this.trafficDataIn.shift();
|
|
810
797
|
this.trafficDataOut.shift();
|
|
811
798
|
}
|
|
812
799
|
this.trafficDataIn = [...this.trafficDataIn, newDataPointIn];
|
|
813
800
|
this.trafficDataOut = [...this.trafficDataOut, newDataPointOut];
|
|
814
|
-
|
|
801
|
+
|
|
815
802
|
this.lastChartUpdate = now;
|
|
816
803
|
}
|
|
817
|
-
|
|
804
|
+
|
|
818
805
|
private stopTrafficUpdateTimer() {
|
|
819
806
|
if (this.trafficUpdateTimer) {
|
|
820
807
|
clearInterval(this.trafficUpdateTimer);
|
|
821
808
|
this.trafficUpdateTimer = null;
|
|
822
809
|
}
|
|
823
810
|
}
|
|
824
|
-
}
|
|
811
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import * as appstate from '../../appstate.js';
|
|
2
|
+
import { appRouter } from '../../router.js';
|
|
3
|
+
import { viewHostCss } from '../shared/css.js';
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
DeesElement,
|
|
7
|
+
customElement,
|
|
8
|
+
html,
|
|
9
|
+
state,
|
|
10
|
+
css,
|
|
11
|
+
cssManager,
|
|
12
|
+
type TemplateResult,
|
|
13
|
+
} from '@design.estate/dees-element';
|
|
14
|
+
|
|
15
|
+
// Side-effect imports register the subview custom elements
|
|
16
|
+
import './ops-view-network-activity.js';
|
|
17
|
+
import './ops-view-routes.js';
|
|
18
|
+
import './ops-view-sourceprofiles.js';
|
|
19
|
+
import './ops-view-networktargets.js';
|
|
20
|
+
import './ops-view-targetprofiles.js';
|
|
21
|
+
|
|
22
|
+
declare global {
|
|
23
|
+
interface HTMLElementTagNameMap {
|
|
24
|
+
'ops-view-network': OpsViewNetwork;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
type TNetworkTab = 'activity' | 'routes' | 'sourceprofiles' | 'networktargets' | 'targetprofiles';
|
|
29
|
+
|
|
30
|
+
@customElement('ops-view-network')
|
|
31
|
+
export class OpsViewNetwork extends DeesElement {
|
|
32
|
+
@state()
|
|
33
|
+
accessor selectedTab: TNetworkTab = 'activity';
|
|
34
|
+
|
|
35
|
+
private tabLabelMap: Record<TNetworkTab, string> = {
|
|
36
|
+
'activity': 'Network Activity',
|
|
37
|
+
'routes': 'Routes',
|
|
38
|
+
'sourceprofiles': 'Source Profiles',
|
|
39
|
+
'networktargets': 'Network Targets',
|
|
40
|
+
'targetprofiles': 'Target Profiles',
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
private labelToTab: Record<string, TNetworkTab> = {
|
|
44
|
+
'Network Activity': 'activity',
|
|
45
|
+
'Routes': 'routes',
|
|
46
|
+
'Source Profiles': 'sourceprofiles',
|
|
47
|
+
'Network Targets': 'networktargets',
|
|
48
|
+
'Target Profiles': 'targetprofiles',
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
private static isNetworkTab(s: string | null): s is TNetworkTab {
|
|
52
|
+
return s === 'activity' || s === 'routes' || s === 'sourceprofiles'
|
|
53
|
+
|| s === 'networktargets' || s === 'targetprofiles';
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
constructor() {
|
|
57
|
+
super();
|
|
58
|
+
// Read initial subview from state (URL-driven)
|
|
59
|
+
const initialState = appstate.uiStatePart.getState()!;
|
|
60
|
+
if (OpsViewNetwork.isNetworkTab(initialState.activeSubview)) {
|
|
61
|
+
this.selectedTab = initialState.activeSubview;
|
|
62
|
+
}
|
|
63
|
+
// Subscribe to future changes (back/forward navigation, direct URL entry)
|
|
64
|
+
const sub = appstate.uiStatePart.select((s) => s.activeSubview).subscribe((sub) => {
|
|
65
|
+
if (OpsViewNetwork.isNetworkTab(sub) && sub !== this.selectedTab) {
|
|
66
|
+
this.selectedTab = sub;
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
this.rxSubscriptions.push(sub);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async firstUpdated() {
|
|
73
|
+
const toggle = this.shadowRoot!.querySelector('dees-input-multitoggle') as any;
|
|
74
|
+
if (toggle) {
|
|
75
|
+
const sub = toggle.changeSubject.subscribe(() => {
|
|
76
|
+
const tab = this.labelToTab[toggle.selectedOption];
|
|
77
|
+
if (tab && tab !== this.selectedTab) {
|
|
78
|
+
// Push URL → router updates state → subscription updates selectedTab
|
|
79
|
+
appRouter.navigateToView('network', tab);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
this.rxSubscriptions.push(sub);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
public static styles = [
|
|
87
|
+
cssManager.defaultStyles,
|
|
88
|
+
viewHostCss,
|
|
89
|
+
css`
|
|
90
|
+
dees-input-multitoggle {
|
|
91
|
+
margin-bottom: 24px;
|
|
92
|
+
}
|
|
93
|
+
`,
|
|
94
|
+
];
|
|
95
|
+
|
|
96
|
+
public render(): TemplateResult {
|
|
97
|
+
return html`
|
|
98
|
+
<dees-heading level="2">Network</dees-heading>
|
|
99
|
+
|
|
100
|
+
<dees-input-multitoggle
|
|
101
|
+
.type=${'single'}
|
|
102
|
+
.options=${['Network Activity', 'Routes', 'Source Profiles', 'Network Targets', 'Target Profiles']}
|
|
103
|
+
.selectedOption=${this.tabLabelMap[this.selectedTab]}
|
|
104
|
+
></dees-input-multitoggle>
|
|
105
|
+
|
|
106
|
+
${this.renderTabContent()}
|
|
107
|
+
`;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
private renderTabContent(): TemplateResult {
|
|
111
|
+
switch (this.selectedTab) {
|
|
112
|
+
case 'activity': return html`<ops-view-network-activity></ops-view-network-activity>`;
|
|
113
|
+
case 'routes': return html`<ops-view-routes></ops-view-routes>`;
|
|
114
|
+
case 'sourceprofiles': return html`<ops-view-sourceprofiles></ops-view-sourceprofiles>`;
|
|
115
|
+
case 'networktargets': return html`<ops-view-networktargets></ops-view-networktargets>`;
|
|
116
|
+
case 'targetprofiles': return html`<ops-view-targetprofiles></ops-view-targetprofiles>`;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -7,9 +7,8 @@ import {
|
|
|
7
7
|
state,
|
|
8
8
|
cssManager,
|
|
9
9
|
} from '@design.estate/dees-element';
|
|
10
|
-
import * as appstate from '
|
|
11
|
-
import * as interfaces from '
|
|
12
|
-
import { viewHostCss } from './shared/css.js';
|
|
10
|
+
import * as appstate from '../../appstate.js';
|
|
11
|
+
import * as interfaces from '../../../dist_ts_interfaces/index.js';
|
|
13
12
|
import { type IStatsTile } from '@design.estate/dees-catalog';
|
|
14
13
|
|
|
15
14
|
declare global {
|
|
@@ -38,8 +37,8 @@ export class OpsViewNetworkTargets extends DeesElement {
|
|
|
38
37
|
|
|
39
38
|
public static styles = [
|
|
40
39
|
cssManager.defaultStyles,
|
|
41
|
-
viewHostCss,
|
|
42
40
|
css`
|
|
41
|
+
:host { display: block; }
|
|
43
42
|
.targetsContainer {
|
|
44
43
|
display: flex;
|
|
45
44
|
flex-direction: column;
|
|
@@ -64,7 +63,7 @@ export class OpsViewNetworkTargets extends DeesElement {
|
|
|
64
63
|
];
|
|
65
64
|
|
|
66
65
|
return html`
|
|
67
|
-
<dees-heading level="
|
|
66
|
+
<dees-heading level="hr">Network Targets</dees-heading>
|
|
68
67
|
<div class="targetsContainer">
|
|
69
68
|
<dees-statsgrid .tiles=${statsTiles}></dees-statsgrid>
|
|
70
69
|
<dees-table
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import * as appstate from '
|
|
2
|
-
import * as interfaces from '
|
|
3
|
-
import { viewHostCss } from './shared/css.js';
|
|
1
|
+
import * as appstate from '../../appstate.js';
|
|
2
|
+
import * as interfaces from '../../../dist_ts_interfaces/index.js';
|
|
4
3
|
import { type IStatsTile } from '@design.estate/dees-catalog';
|
|
5
4
|
|
|
6
5
|
import {
|
|
@@ -97,8 +96,8 @@ export class OpsViewRoutes extends DeesElement {
|
|
|
97
96
|
|
|
98
97
|
public static styles = [
|
|
99
98
|
cssManager.defaultStyles,
|
|
100
|
-
viewHostCss,
|
|
101
99
|
css`
|
|
100
|
+
:host { display: block; }
|
|
102
101
|
.routesContainer {
|
|
103
102
|
display: flex;
|
|
104
103
|
flex-direction: column;
|
|
@@ -200,7 +199,7 @@ export class OpsViewRoutes extends DeesElement {
|
|
|
200
199
|
});
|
|
201
200
|
|
|
202
201
|
return html`
|
|
203
|
-
<dees-heading level="
|
|
202
|
+
<dees-heading level="hr">Route Management</dees-heading>
|
|
204
203
|
|
|
205
204
|
<div class="routesContainer">
|
|
206
205
|
<dees-statsgrid
|
|
@@ -7,9 +7,8 @@ import {
|
|
|
7
7
|
state,
|
|
8
8
|
cssManager,
|
|
9
9
|
} from '@design.estate/dees-element';
|
|
10
|
-
import * as appstate from '
|
|
11
|
-
import * as interfaces from '
|
|
12
|
-
import { viewHostCss } from './shared/css.js';
|
|
10
|
+
import * as appstate from '../../appstate.js';
|
|
11
|
+
import * as interfaces from '../../../dist_ts_interfaces/index.js';
|
|
13
12
|
import { type IStatsTile } from '@design.estate/dees-catalog';
|
|
14
13
|
|
|
15
14
|
declare global {
|
|
@@ -38,8 +37,8 @@ export class OpsViewSourceProfiles extends DeesElement {
|
|
|
38
37
|
|
|
39
38
|
public static styles = [
|
|
40
39
|
cssManager.defaultStyles,
|
|
41
|
-
viewHostCss,
|
|
42
40
|
css`
|
|
41
|
+
:host { display: block; }
|
|
43
42
|
.profilesContainer {
|
|
44
43
|
display: flex;
|
|
45
44
|
flex-direction: column;
|
|
@@ -64,7 +63,7 @@ export class OpsViewSourceProfiles extends DeesElement {
|
|
|
64
63
|
];
|
|
65
64
|
|
|
66
65
|
return html`
|
|
67
|
-
<dees-heading level="
|
|
66
|
+
<dees-heading level="hr">Source Profiles</dees-heading>
|
|
68
67
|
<div class="profilesContainer">
|
|
69
68
|
<dees-statsgrid .tiles=${statsTiles}></dees-statsgrid>
|
|
70
69
|
<dees-table
|
|
@@ -7,10 +7,9 @@ import {
|
|
|
7
7
|
state,
|
|
8
8
|
cssManager,
|
|
9
9
|
} from '@design.estate/dees-element';
|
|
10
|
-
import * as plugins from '
|
|
11
|
-
import * as appstate from '
|
|
12
|
-
import * as interfaces from '
|
|
13
|
-
import { viewHostCss } from './shared/css.js';
|
|
10
|
+
import * as plugins from '../../plugins.js';
|
|
11
|
+
import * as appstate from '../../appstate.js';
|
|
12
|
+
import * as interfaces from '../../../dist_ts_interfaces/index.js';
|
|
14
13
|
import { type IStatsTile } from '@design.estate/dees-catalog';
|
|
15
14
|
|
|
16
15
|
declare global {
|
|
@@ -39,8 +38,8 @@ export class OpsViewTargetProfiles extends DeesElement {
|
|
|
39
38
|
|
|
40
39
|
public static styles = [
|
|
41
40
|
cssManager.defaultStyles,
|
|
42
|
-
viewHostCss,
|
|
43
41
|
css`
|
|
42
|
+
:host { display: block; }
|
|
44
43
|
.profilesContainer {
|
|
45
44
|
display: flex;
|
|
46
45
|
flex-direction: column;
|
|
@@ -77,7 +76,7 @@ export class OpsViewTargetProfiles extends DeesElement {
|
|
|
77
76
|
];
|
|
78
77
|
|
|
79
78
|
return html`
|
|
80
|
-
<dees-heading level="
|
|
79
|
+
<dees-heading level="hr">Target Profiles</dees-heading>
|
|
81
80
|
<div class="profilesContainer">
|
|
82
81
|
<dees-statsgrid .tiles=${statsTiles}></dees-statsgrid>
|
|
83
82
|
<dees-table
|