@serve.zone/dcrouter 13.1.3 → 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 +1202 -1133
- 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 -30
- 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} +6 -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} +6 -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} +7 -7
- package/dist_ts_web/elements/ops-dashboard.js +4 -27
- package/dist_ts_web/elements/ops-view-apitokens.js +2 -1
- package/dist_ts_web/elements/ops-view-certificates.js +2 -1
- package/dist_ts_web/elements/ops-view-config.js +3 -3
- package/dist_ts_web/elements/ops-view-remoteingress.js +2 -1
- package/dist_ts_web/elements/ops-view-vpn.js +2 -1
- 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 -53
- package/ts_web/elements/network/ops-view-network.ts +119 -0
- package/ts_web/elements/{ops-view-networktargets.ts → network/ops-view-networktargets.ts} +5 -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} +5 -5
- package/ts_web/elements/{ops-view-targetprofiles.ts → network/ops-view-targetprofiles.ts} +6 -6
- package/ts_web/elements/ops-dashboard.ts +3 -26
- package/ts_web/elements/ops-view-apitokens.ts +1 -0
- package/ts_web/elements/ops-view-certificates.ts +1 -0
- package/ts_web/elements/ops-view-config.ts +2 -2
- package/ts_web/elements/ops-view-remoteingress.ts +1 -0
- package/ts_web/elements/ops-view-vpn.ts +1 -0
- 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 -481
- package/ts_web/elements/ops-view-security.ts +0 -453
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
|
|
|
@@ -357,7 +356,7 @@ export class OpsViewNetwork extends DeesElement {
|
|
|
357
356
|
|
|
358
357
|
private async showRequestDetails(request: INetworkRequest) {
|
|
359
358
|
const { DeesModal } = await import('@design.estate/dees-catalog');
|
|
360
|
-
|
|
359
|
+
|
|
361
360
|
await DeesModal.createAndShow({
|
|
362
361
|
heading: 'Request Details',
|
|
363
362
|
content: html`
|
|
@@ -400,10 +399,10 @@ export class OpsViewNetwork extends DeesElement {
|
|
|
400
399
|
if (!statusCode) {
|
|
401
400
|
return html`<span class="statusBadge warning">N/A</span>`;
|
|
402
401
|
}
|
|
403
|
-
|
|
402
|
+
|
|
404
403
|
const statusClass = statusCode >= 200 && statusCode < 300 ? 'success' :
|
|
405
404
|
statusCode >= 400 ? 'error' : 'warning';
|
|
406
|
-
|
|
405
|
+
|
|
407
406
|
return html`<span class="statusBadge ${statusClass}">${statusCode}</span>`;
|
|
408
407
|
}
|
|
409
408
|
|
|
@@ -426,26 +425,26 @@ export class OpsViewNetwork extends DeesElement {
|
|
|
426
425
|
const units = ['B', 'KB', 'MB', 'GB'];
|
|
427
426
|
let size = bytes;
|
|
428
427
|
let unitIndex = 0;
|
|
429
|
-
|
|
428
|
+
|
|
430
429
|
while (size >= 1024 && unitIndex < units.length - 1) {
|
|
431
430
|
size /= 1024;
|
|
432
431
|
unitIndex++;
|
|
433
432
|
}
|
|
434
|
-
|
|
433
|
+
|
|
435
434
|
return `${size.toFixed(1)} ${units[unitIndex]}`;
|
|
436
435
|
}
|
|
437
|
-
|
|
436
|
+
|
|
438
437
|
private formatBitsPerSecond(bytesPerSecond: number): string {
|
|
439
438
|
const bitsPerSecond = bytesPerSecond * 8; // Convert bytes to bits
|
|
440
439
|
const units = ['bit/s', 'kbit/s', 'Mbit/s', 'Gbit/s'];
|
|
441
440
|
let size = bitsPerSecond;
|
|
442
441
|
let unitIndex = 0;
|
|
443
|
-
|
|
442
|
+
|
|
444
443
|
while (size >= 1000 && unitIndex < units.length - 1) {
|
|
445
444
|
size /= 1000; // Use 1000 for bits (not 1024)
|
|
446
445
|
unitIndex++;
|
|
447
446
|
}
|
|
448
|
-
|
|
447
|
+
|
|
449
448
|
return `${size.toFixed(1)} ${units[unitIndex]}`;
|
|
450
449
|
}
|
|
451
450
|
|
|
@@ -520,18 +519,9 @@ export class OpsViewNetwork extends DeesElement {
|
|
|
520
519
|
];
|
|
521
520
|
|
|
522
521
|
return html`
|
|
523
|
-
<dees-statsgrid
|
|
522
|
+
<dees-statsgrid
|
|
524
523
|
.tiles=${tiles}
|
|
525
524
|
.minTileWidth=${200}
|
|
526
|
-
.gridActions=${[
|
|
527
|
-
{
|
|
528
|
-
name: 'Export Data',
|
|
529
|
-
iconName: 'lucide:FileOutput',
|
|
530
|
-
action: async () => {
|
|
531
|
-
console.log('Export feature coming soon');
|
|
532
|
-
},
|
|
533
|
-
},
|
|
534
|
-
]}
|
|
535
525
|
></dees-statsgrid>
|
|
536
526
|
`;
|
|
537
527
|
}
|
|
@@ -732,12 +722,12 @@ export class OpsViewNetwork extends DeesElement {
|
|
|
732
722
|
// Only update if connections changed significantly
|
|
733
723
|
const newConnectionCount = this.networkState.connections.length;
|
|
734
724
|
const oldConnectionCount = this.networkRequests.length;
|
|
735
|
-
|
|
725
|
+
|
|
736
726
|
// Check if we need to update the network requests array
|
|
737
|
-
const shouldUpdate = newConnectionCount !== oldConnectionCount ||
|
|
727
|
+
const shouldUpdate = newConnectionCount !== oldConnectionCount ||
|
|
738
728
|
newConnectionCount === 0 ||
|
|
739
729
|
(newConnectionCount > 0 && this.networkRequests.length === 0);
|
|
740
|
-
|
|
730
|
+
|
|
741
731
|
if (shouldUpdate) {
|
|
742
732
|
// Convert connection data to network requests format
|
|
743
733
|
if (newConnectionCount > 0) {
|
|
@@ -760,62 +750,62 @@ export class OpsViewNetwork extends DeesElement {
|
|
|
760
750
|
this.networkRequests = [];
|
|
761
751
|
}
|
|
762
752
|
}
|
|
763
|
-
|
|
753
|
+
|
|
764
754
|
// Load server-side throughput history into chart (once)
|
|
765
755
|
if (!this.historyLoaded && this.networkState.throughputHistory && this.networkState.throughputHistory.length > 0) {
|
|
766
756
|
this.loadThroughputHistory();
|
|
767
757
|
}
|
|
768
758
|
}
|
|
769
|
-
|
|
759
|
+
|
|
770
760
|
private startTrafficUpdateTimer() {
|
|
771
761
|
this.stopTrafficUpdateTimer(); // Clear any existing timer
|
|
772
762
|
this.trafficUpdateTimer = setInterval(() => {
|
|
773
763
|
this.addTrafficDataPoint();
|
|
774
|
-
},
|
|
764
|
+
}, OpsViewNetworkActivity.UPDATE_INTERVAL_MS);
|
|
775
765
|
}
|
|
776
|
-
|
|
766
|
+
|
|
777
767
|
private addTrafficDataPoint() {
|
|
778
768
|
const now = Date.now();
|
|
779
|
-
|
|
769
|
+
|
|
780
770
|
// Throttle chart updates to avoid excessive re-renders
|
|
781
771
|
if (now - this.lastChartUpdate < this.chartUpdateThreshold) {
|
|
782
772
|
return;
|
|
783
773
|
}
|
|
784
|
-
|
|
774
|
+
|
|
785
775
|
const throughput = this.calculateThroughput();
|
|
786
|
-
|
|
776
|
+
|
|
787
777
|
// Convert to Mbps (bytes * 8 / 1,000,000)
|
|
788
778
|
const throughputInMbps = (throughput.in * 8) / 1000000;
|
|
789
779
|
const throughputOutMbps = (throughput.out * 8) / 1000000;
|
|
790
|
-
|
|
780
|
+
|
|
791
781
|
// Add new data points
|
|
792
782
|
const timestamp = new Date(now).toISOString();
|
|
793
|
-
|
|
783
|
+
|
|
794
784
|
const newDataPointIn = {
|
|
795
785
|
x: timestamp,
|
|
796
786
|
y: Math.round(throughputInMbps * 10) / 10
|
|
797
787
|
};
|
|
798
|
-
|
|
788
|
+
|
|
799
789
|
const newDataPointOut = {
|
|
800
790
|
x: timestamp,
|
|
801
791
|
y: Math.round(throughputOutMbps * 10) / 10
|
|
802
792
|
};
|
|
803
|
-
|
|
793
|
+
|
|
804
794
|
// In-place mutation then reassign for Lit reactivity (avoids 4 intermediate arrays)
|
|
805
|
-
if (this.trafficDataIn.length >=
|
|
795
|
+
if (this.trafficDataIn.length >= OpsViewNetworkActivity.MAX_DATA_POINTS) {
|
|
806
796
|
this.trafficDataIn.shift();
|
|
807
797
|
this.trafficDataOut.shift();
|
|
808
798
|
}
|
|
809
799
|
this.trafficDataIn = [...this.trafficDataIn, newDataPointIn];
|
|
810
800
|
this.trafficDataOut = [...this.trafficDataOut, newDataPointOut];
|
|
811
|
-
|
|
801
|
+
|
|
812
802
|
this.lastChartUpdate = now;
|
|
813
803
|
}
|
|
814
|
-
|
|
804
|
+
|
|
815
805
|
private stopTrafficUpdateTimer() {
|
|
816
806
|
if (this.trafficUpdateTimer) {
|
|
817
807
|
clearInterval(this.trafficUpdateTimer);
|
|
818
808
|
this.trafficUpdateTimer = null;
|
|
819
809
|
}
|
|
820
810
|
}
|
|
821
|
-
}
|
|
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,13 +63,14 @@ 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
|
|
71
70
|
.heading1=${'Network Targets'}
|
|
72
71
|
.heading2=${'Reusable host:port destinations for routes'}
|
|
73
72
|
.data=${targets}
|
|
73
|
+
.showColumnFilters=${true}
|
|
74
74
|
.displayFunction=${(target: interfaces.data.INetworkTarget) => ({
|
|
75
75
|
Name: target.name,
|
|
76
76
|
Host: Array.isArray(target.host) ? target.host.join(', ') : target.host,
|
|
@@ -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,13 +63,14 @@ 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
|
|
71
70
|
.heading1=${'Source Profiles'}
|
|
72
71
|
.heading2=${'Reusable source configurations for routes'}
|
|
73
72
|
.data=${profiles}
|
|
73
|
+
.showColumnFilters=${true}
|
|
74
74
|
.displayFunction=${(profile: interfaces.data.ISourceProfile) => ({
|
|
75
75
|
Name: profile.name,
|
|
76
76
|
Description: profile.description || '-',
|
|
@@ -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,13 +76,14 @@ 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
|
|
84
83
|
.heading1=${'Target Profiles'}
|
|
85
84
|
.heading2=${'Define what resources VPN clients can access'}
|
|
86
85
|
.data=${profiles}
|
|
86
|
+
.showColumnFilters=${true}
|
|
87
87
|
.displayFunction=${(profile: interfaces.data.ITargetProfile) => ({
|
|
88
88
|
Name: profile.name,
|
|
89
89
|
Description: profile.description || '-',
|