@serve.zone/dcrouter 13.3.0 → 13.4.2

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.
Files changed (75) hide show
  1. package/dist_serve/bundle.js +1491 -1461
  2. package/dist_ts/00_commitinfo_data.js +1 -1
  3. package/dist_ts_web/00_commitinfo_data.js +1 -1
  4. package/dist_ts_web/appstate.js +7 -18
  5. package/dist_ts_web/elements/access/index.d.ts +1 -0
  6. package/dist_ts_web/elements/access/index.js +2 -0
  7. package/dist_ts_web/elements/{ops-view-apitokens.d.ts → access/ops-view-apitokens.d.ts} +1 -1
  8. package/dist_ts_web/elements/{ops-view-apitokens.js → access/ops-view-apitokens.js} +4 -4
  9. package/dist_ts_web/elements/email/index.d.ts +2 -0
  10. package/dist_ts_web/elements/email/index.js +3 -0
  11. package/dist_ts_web/elements/{security/ops-view-security-emailsecurity.d.ts → email/ops-view-email-security.d.ts} +2 -2
  12. package/dist_ts_web/elements/{security/ops-view-security-emailsecurity.js → email/ops-view-email-security.js} +9 -8
  13. package/dist_ts_web/elements/{ops-view-emails.d.ts → email/ops-view-emails.d.ts} +2 -2
  14. package/dist_ts_web/elements/{ops-view-emails.js → email/ops-view-emails.js} +5 -5
  15. package/dist_ts_web/elements/index.d.ts +3 -6
  16. package/dist_ts_web/elements/index.js +4 -7
  17. package/dist_ts_web/elements/network/index.d.ts +2 -1
  18. package/dist_ts_web/elements/network/index.js +3 -2
  19. package/dist_ts_web/elements/network/ops-view-network-activity.js +3 -2
  20. package/dist_ts_web/elements/network/ops-view-networktargets.js +3 -2
  21. package/dist_ts_web/elements/{ops-view-remoteingress.d.ts → network/ops-view-remoteingress.d.ts} +1 -1
  22. package/dist_ts_web/elements/{ops-view-remoteingress.js → network/ops-view-remoteingress.js} +5 -5
  23. package/dist_ts_web/elements/network/ops-view-routes.js +3 -2
  24. package/dist_ts_web/elements/network/ops-view-sourceprofiles.js +3 -2
  25. package/dist_ts_web/elements/network/ops-view-targetprofiles.js +3 -2
  26. package/dist_ts_web/elements/{ops-view-vpn.d.ts → network/ops-view-vpn.d.ts} +2 -2
  27. package/dist_ts_web/elements/{ops-view-vpn.js → network/ops-view-vpn.js} +6 -6
  28. package/dist_ts_web/elements/ops-dashboard.d.ts +8 -2
  29. package/dist_ts_web/elements/ops-dashboard.js +100 -59
  30. package/dist_ts_web/elements/overview/index.d.ts +2 -0
  31. package/dist_ts_web/elements/overview/index.js +3 -0
  32. package/dist_ts_web/elements/{ops-view-config.d.ts → overview/ops-view-config.d.ts} +2 -2
  33. package/dist_ts_web/elements/{ops-view-config.js → overview/ops-view-config.js} +7 -7
  34. package/dist_ts_web/elements/{ops-view-overview.d.ts → overview/ops-view-overview.d.ts} +2 -2
  35. package/dist_ts_web/elements/{ops-view-overview.js → overview/ops-view-overview.js} +4 -4
  36. package/dist_ts_web/elements/security/index.d.ts +0 -2
  37. package/dist_ts_web/elements/security/index.js +1 -3
  38. package/dist_ts_web/elements/security/ops-view-security-authentication.js +3 -2
  39. package/dist_ts_web/elements/security/ops-view-security-blocked.js +3 -2
  40. package/dist_ts_web/elements/security/ops-view-security-overview.js +3 -2
  41. package/dist_ts_web/router.d.ts +1 -1
  42. package/dist_ts_web/router.js +10 -4
  43. package/package.json +2 -2
  44. package/ts/00_commitinfo_data.ts +1 -1
  45. package/ts_web/00_commitinfo_data.ts +1 -1
  46. package/ts_web/appstate.ts +6 -19
  47. package/ts_web/elements/access/index.ts +1 -0
  48. package/ts_web/elements/{ops-view-apitokens.ts → access/ops-view-apitokens.ts} +3 -3
  49. package/ts_web/elements/email/index.ts +2 -0
  50. package/ts_web/elements/{security/ops-view-security-emailsecurity.ts → email/ops-view-email-security.ts} +5 -4
  51. package/ts_web/elements/{ops-view-emails.ts → email/ops-view-emails.ts} +4 -4
  52. package/ts_web/elements/index.ts +4 -7
  53. package/ts_web/elements/network/index.ts +2 -1
  54. package/ts_web/elements/network/ops-view-network-activity.ts +2 -1
  55. package/ts_web/elements/network/ops-view-networktargets.ts +2 -1
  56. package/ts_web/elements/{ops-view-remoteingress.ts → network/ops-view-remoteingress.ts} +4 -4
  57. package/ts_web/elements/network/ops-view-routes.ts +2 -1
  58. package/ts_web/elements/network/ops-view-sourceprofiles.ts +2 -1
  59. package/ts_web/elements/network/ops-view-targetprofiles.ts +2 -1
  60. package/ts_web/elements/{ops-view-vpn.ts → network/ops-view-vpn.ts} +5 -5
  61. package/ts_web/elements/ops-dashboard.ts +124 -66
  62. package/ts_web/elements/overview/index.ts +2 -0
  63. package/ts_web/elements/{ops-view-config.ts → overview/ops-view-config.ts} +6 -6
  64. package/ts_web/elements/{ops-view-overview.ts → overview/ops-view-overview.ts} +3 -3
  65. package/ts_web/elements/security/index.ts +0 -2
  66. package/ts_web/elements/security/ops-view-security-authentication.ts +2 -1
  67. package/ts_web/elements/security/ops-view-security-blocked.ts +2 -1
  68. package/ts_web/elements/security/ops-view-security-overview.ts +2 -1
  69. package/ts_web/router.ts +9 -3
  70. package/dist_ts_web/elements/network/ops-view-network.d.ts +0 -24
  71. package/dist_ts_web/elements/network/ops-view-network.js +0 -151
  72. package/dist_ts_web/elements/security/ops-view-security.d.ts +0 -23
  73. package/dist_ts_web/elements/security/ops-view-security.js +0 -146
  74. package/ts_web/elements/network/ops-view-network.ts +0 -119
  75. package/ts_web/elements/security/ops-view-security.ts +0 -114
@@ -11,18 +11,45 @@ import {
11
11
  state,
12
12
  type TemplateResult
13
13
  } from '@design.estate/dees-element';
14
+ import type { IView } from '@design.estate/dees-catalog';
14
15
 
15
- // Import view components
16
- import { OpsViewOverview } from './ops-view-overview.js';
17
- import { OpsViewNetwork } from './network/ops-view-network.js';
18
- import { OpsViewEmails } from './ops-view-emails.js';
16
+ // Top-level / flat views
19
17
  import { OpsViewLogs } from './ops-view-logs.js';
20
- import { OpsViewConfig } from './ops-view-config.js';
21
- import { OpsViewApiTokens } from './ops-view-apitokens.js';
22
- import { OpsViewSecurity } from './security/ops-view-security.js';
23
18
  import { OpsViewCertificates } from './ops-view-certificates.js';
24
- import { OpsViewRemoteIngress } from './ops-view-remoteingress.js';
25
- import { OpsViewVpn } from './ops-view-vpn.js';
19
+
20
+ // Overview group
21
+ import { OpsViewOverview } from './overview/ops-view-overview.js';
22
+ import { OpsViewConfig } from './overview/ops-view-config.js';
23
+
24
+ // Network group
25
+ import { OpsViewNetworkActivity } from './network/ops-view-network-activity.js';
26
+ import { OpsViewRoutes } from './network/ops-view-routes.js';
27
+ import { OpsViewSourceProfiles } from './network/ops-view-sourceprofiles.js';
28
+ import { OpsViewNetworkTargets } from './network/ops-view-networktargets.js';
29
+ import { OpsViewTargetProfiles } from './network/ops-view-targetprofiles.js';
30
+ import { OpsViewRemoteIngress } from './network/ops-view-remoteingress.js';
31
+ import { OpsViewVpn } from './network/ops-view-vpn.js';
32
+
33
+ // Email group
34
+ import { OpsViewEmails } from './email/ops-view-emails.js';
35
+ import { OpsViewEmailSecurity } from './email/ops-view-email-security.js';
36
+
37
+ // Access group
38
+ import { OpsViewApiTokens } from './access/ops-view-apitokens.js';
39
+
40
+ // Security group
41
+ import { OpsViewSecurityOverview } from './security/ops-view-security-overview.js';
42
+ import { OpsViewSecurityBlocked } from './security/ops-view-security-blocked.js';
43
+ import { OpsViewSecurityAuthentication } from './security/ops-view-security-authentication.js';
44
+
45
+ /**
46
+ * Extended IView with explicit URL slug. Without an explicit `slug`, the URL
47
+ * slug is derived from `name.toLowerCase().replace(/\s+/g, '')`.
48
+ */
49
+ interface ITabbedView extends IView {
50
+ slug?: string;
51
+ subViews?: ITabbedView[];
52
+ }
26
53
 
27
54
  @customElement('ops-dashboard')
28
55
  export class OpsDashboard extends DeesElement {
@@ -46,27 +73,36 @@ export class OpsDashboard extends DeesElement {
46
73
  error: null,
47
74
  };
48
75
 
49
- // Store viewTabs as a property to maintain object references
50
- private viewTabs = [
76
+ // Store viewTabs as a property to maintain object references (used for === selectedView identity)
77
+ private viewTabs: ITabbedView[] = [
51
78
  {
52
79
  name: 'Overview',
53
80
  iconName: 'lucide:layoutDashboard',
54
- element: OpsViewOverview,
55
- },
56
- {
57
- name: 'Configuration',
58
- iconName: 'lucide:settings',
59
- element: OpsViewConfig,
81
+ subViews: [
82
+ { slug: 'stats', name: 'Stats', iconName: 'lucide:activity', element: OpsViewOverview },
83
+ { slug: 'configuration', name: 'Configuration', iconName: 'lucide:settings', element: OpsViewConfig },
84
+ ],
60
85
  },
61
86
  {
62
87
  name: 'Network',
63
88
  iconName: 'lucide:network',
64
- element: OpsViewNetwork,
89
+ subViews: [
90
+ { slug: 'activity', name: 'Network Activity', iconName: 'lucide:activity', element: OpsViewNetworkActivity },
91
+ { slug: 'routes', name: 'Routes', iconName: 'lucide:route', element: OpsViewRoutes },
92
+ { slug: 'sourceprofiles', name: 'Source Profiles', iconName: 'lucide:shieldCheck', element: OpsViewSourceProfiles },
93
+ { slug: 'networktargets', name: 'Network Targets', iconName: 'lucide:server', element: OpsViewNetworkTargets },
94
+ { slug: 'targetprofiles', name: 'Target Profiles', iconName: 'lucide:target', element: OpsViewTargetProfiles },
95
+ { slug: 'remoteingress', name: 'Remote Ingress', iconName: 'lucide:globe', element: OpsViewRemoteIngress },
96
+ { slug: 'vpn', name: 'VPN', iconName: 'lucide:shield', element: OpsViewVpn },
97
+ ],
65
98
  },
66
99
  {
67
- name: 'Emails',
100
+ name: 'Email',
68
101
  iconName: 'lucide:mail',
69
- element: OpsViewEmails,
102
+ subViews: [
103
+ { slug: 'log', name: 'Email Log', iconName: 'lucide:scrollText', element: OpsViewEmails },
104
+ { slug: 'security', name: 'Email Security', iconName: 'lucide:shieldCheck', element: OpsViewEmailSecurity },
105
+ ],
70
106
  },
71
107
  {
72
108
  name: 'Logs',
@@ -74,32 +110,48 @@ export class OpsDashboard extends DeesElement {
74
110
  element: OpsViewLogs,
75
111
  },
76
112
  {
77
- name: 'ApiTokens',
78
- iconName: 'lucide:key',
79
- element: OpsViewApiTokens,
113
+ name: 'Access',
114
+ iconName: 'lucide:keyRound',
115
+ subViews: [
116
+ { slug: 'apitokens', name: 'API Tokens', iconName: 'lucide:key', element: OpsViewApiTokens },
117
+ ],
80
118
  },
81
119
  {
82
120
  name: 'Security',
83
121
  iconName: 'lucide:shield',
84
- element: OpsViewSecurity,
122
+ subViews: [
123
+ { slug: 'overview', name: 'Overview', iconName: 'lucide:eye', element: OpsViewSecurityOverview },
124
+ { slug: 'blocked', name: 'Blocked IPs', iconName: 'lucide:shieldBan', element: OpsViewSecurityBlocked },
125
+ { slug: 'authentication', name: 'Authentication', iconName: 'lucide:lock', element: OpsViewSecurityAuthentication },
126
+ ],
85
127
  },
86
128
  {
87
129
  name: 'Certificates',
88
130
  iconName: 'lucide:badgeCheck',
89
131
  element: OpsViewCertificates,
90
132
  },
91
- {
92
- name: 'RemoteIngress',
93
- iconName: 'lucide:globe',
94
- element: OpsViewRemoteIngress,
95
- },
96
- {
97
- name: 'VPN',
98
- iconName: 'lucide:shield',
99
- element: OpsViewVpn,
100
- },
101
133
  ];
102
134
 
135
+ /** URL slug for a view (explicit `slug` field, or lowercased name with spaces stripped). */
136
+ private slugFor(view: ITabbedView): string {
137
+ return view.slug ?? view.name.toLowerCase().replace(/\s+/g, '');
138
+ }
139
+
140
+ /** Find the parent group of a subview, or undefined for top-level views. */
141
+ private findParent(view: ITabbedView): ITabbedView | undefined {
142
+ return this.viewTabs.find((v) => v.subViews?.includes(view));
143
+ }
144
+
145
+ /** Look up a view (or subview) by its URL slug pair. */
146
+ private findViewBySlug(viewSlug: string, subSlug: string | null): ITabbedView | undefined {
147
+ const top = this.viewTabs.find((v) => this.slugFor(v) === viewSlug);
148
+ if (!top) return undefined;
149
+ if (subSlug && top.subViews) {
150
+ return top.subViews.find((sv) => this.slugFor(sv) === subSlug) ?? top;
151
+ }
152
+ return top;
153
+ }
154
+
103
155
  private get globalMessages() {
104
156
  const messages: Array<{ id: string; type: string; message: string; dismissible?: boolean }> = [];
105
157
  const config = this.configState.config;
@@ -115,17 +167,19 @@ export class OpsDashboard extends DeesElement {
115
167
  }
116
168
 
117
169
  /**
118
- * Get the current view tab based on the UI state's activeView.
170
+ * Get the current view tab based on the UI state's activeView/activeSubview.
119
171
  * Used to pass the correct selectedView to dees-simple-appdash on initial render.
120
172
  */
121
- private get currentViewTab() {
122
- return this.viewTabs.find(t => t.name.toLowerCase() === this.uiState.activeView) || this.viewTabs[0];
173
+ private get currentViewTab(): ITabbedView {
174
+ return (
175
+ this.findViewBySlug(this.uiState.activeView, this.uiState.activeSubview) ?? this.viewTabs[0]
176
+ );
123
177
  }
124
178
 
125
179
  constructor() {
126
180
  super();
127
181
  document.title = 'DCRouter OpsServer';
128
-
182
+
129
183
  // Subscribe to login state
130
184
  const loginSubscription = appstate.loginStatePart
131
185
  .select((stateArg) => stateArg)
@@ -138,7 +192,7 @@ export class OpsDashboard extends DeesElement {
138
192
  }
139
193
  });
140
194
  this.rxSubscriptions.push(loginSubscription);
141
-
195
+
142
196
  // Subscribe to config state (for global warnings)
143
197
  const configSubscription = appstate.configStatePart
144
198
  .select((stateArg) => stateArg)
@@ -153,38 +207,27 @@ export class OpsDashboard extends DeesElement {
153
207
  .subscribe((uiState) => {
154
208
  this.uiState = uiState;
155
209
  // Sync appdash view when state changes (e.g., from URL navigation)
156
- this.syncAppdashView(uiState.activeView);
210
+ this.syncAppdashView(uiState.activeView, uiState.activeSubview);
157
211
  });
158
212
  this.rxSubscriptions.push(uiSubscription);
159
213
  }
160
214
 
161
215
  /**
162
216
  * Sync the dees-simple-appdash view selection with the current state.
163
- * This is needed when the URL changes and we need to update the UI.
217
+ * This is needed when the URL changes externally (back/forward, deep link).
164
218
  */
165
- private syncAppdashView(viewName: string): void {
219
+ private syncAppdashView(viewSlug: string, subviewSlug: string | null): void {
166
220
  const appDash = this.shadowRoot?.querySelector('dees-simple-appdash') as any;
167
221
  if (!appDash) return;
168
222
 
169
- const targetTab = this.viewTabs.find(t => t.name.toLowerCase() === viewName);
170
- if (!targetTab) return;
223
+ const targetView = this.findViewBySlug(viewSlug, subviewSlug);
224
+ if (!targetView) return;
171
225
 
172
- // Check if we need to switch (avoid unnecessary updates)
173
- if (appDash.selectedView === targetTab) return;
226
+ if (appDash.selectedView === targetView) return;
174
227
 
175
- // Update the selected view programmatically
176
- appDash.selectedView = targetTab;
177
-
178
- // Update the displayed content
179
- const content = appDash.shadowRoot?.querySelector('.appcontent');
180
- if (content) {
181
- if (appDash.currentView) {
182
- appDash.currentView.remove();
183
- }
184
- const view = new targetTab.element();
185
- content.appendChild(view);
186
- appDash.currentView = view;
187
- }
228
+ // Use loadView to update both selectedView and the mounted element.
229
+ // It will dispatch view-select; our handler skips when state already matches.
230
+ appDash.loadView(targetView);
188
231
  }
189
232
 
190
233
  public static styles = [
@@ -226,7 +269,7 @@ export class OpsDashboard extends DeesElement {
226
269
  public async firstUpdated() {
227
270
  const simpleLogin = this.shadowRoot!.querySelector('dees-simple-login') as any;
228
271
  simpleLogin.addEventListener('login', (e: Event) => {
229
- // Handle logout event
272
+ // Handle login event
230
273
  const detail = (e as CustomEvent).detail;
231
274
  this.login(detail.data.username, detail.data.password);
232
275
  });
@@ -235,9 +278,24 @@ export class OpsDashboard extends DeesElement {
235
278
  const appDash = this.shadowRoot!.querySelector('dees-simple-appdash');
236
279
  if (appDash) {
237
280
  appDash.addEventListener('view-select', (e: Event) => {
238
- const viewName = (e as CustomEvent).detail.view.name.toLowerCase();
239
- // Use router for navigation instead of direct state update
240
- appRouter.navigateToView(viewName);
281
+ const view = (e as CustomEvent).detail.view as ITabbedView;
282
+ const parent = this.findParent(view);
283
+ const currentState = appstate.uiStatePart.getState();
284
+ if (parent) {
285
+ const parentSlug = this.slugFor(parent);
286
+ const subSlug = this.slugFor(view);
287
+ // Skip if already on this exact subview — preserves URL on initial mount
288
+ if (currentState?.activeView === parentSlug && currentState?.activeSubview === subSlug) {
289
+ return;
290
+ }
291
+ appRouter.navigateToView(parentSlug, subSlug);
292
+ } else {
293
+ const slug = this.slugFor(view);
294
+ if (currentState?.activeView === slug && !currentState?.activeSubview) {
295
+ return;
296
+ }
297
+ appRouter.navigateToView(slug);
298
+ }
241
299
  });
242
300
 
243
301
  // Handle logout event
@@ -283,12 +341,12 @@ export class OpsDashboard extends DeesElement {
283
341
  const simpleLogin = this.shadowRoot!.querySelector('dees-simple-login') as any;
284
342
  const form = simpleLogin.shadowRoot!.querySelector('dees-form') as any;
285
343
  form.setStatus('pending', 'Logging in...');
286
-
344
+
287
345
  const state = await appstate.loginStatePart.dispatchAction(appstate.loginAction, {
288
346
  username,
289
347
  password,
290
348
  });
291
-
349
+
292
350
  if (state.identity) {
293
351
  console.log('Login successful');
294
352
  this.loginState = state;
@@ -302,4 +360,4 @@ export class OpsDashboard extends DeesElement {
302
360
  form!.reset();
303
361
  }
304
362
  }
305
- }
363
+ }
@@ -0,0 +1,2 @@
1
+ export * from './ops-view-overview.js';
2
+ export * from './ops-view-config.js';
@@ -1,7 +1,7 @@
1
- import * as plugins from '../plugins.js';
2
- import * as shared from './shared/index.js';
3
- import * as appstate from '../appstate.js';
4
- import { appRouter } from '../router.js';
1
+ import * as plugins from '../../plugins.js';
2
+ import * as shared from '../shared/index.js';
3
+ import * as appstate from '../../appstate.js';
4
+ import { appRouter } from '../../router.js';
5
5
 
6
6
  import {
7
7
  DeesElement,
@@ -181,7 +181,7 @@ export class OpsViewConfig extends DeesElement {
181
181
  }
182
182
 
183
183
  const actions: IConfigSectionAction[] = [
184
- { label: 'View Emails', icon: 'lucide:arrow-right', event: 'navigate', detail: { view: 'emails' } },
184
+ { label: 'View Emails', icon: 'lucide:arrow-right', event: 'navigate', detail: { view: 'email', subview: 'log' } },
185
185
  ];
186
186
 
187
187
  return html`
@@ -305,7 +305,7 @@ export class OpsViewConfig extends DeesElement {
305
305
  ];
306
306
 
307
307
  const actions: IConfigSectionAction[] = [
308
- { label: 'View Remote Ingress', icon: 'lucide:arrow-right', event: 'navigate', detail: { view: 'remoteingress' } },
308
+ { label: 'View Remote Ingress', icon: 'lucide:arrow-right', event: 'navigate', detail: { view: 'network', subview: 'remoteingress' } },
309
309
  ];
310
310
 
311
311
  return html`
@@ -1,6 +1,6 @@
1
- import * as plugins from '../plugins.js';
2
- import * as shared from './shared/index.js';
3
- import * as appstate from '../appstate.js';
1
+ import * as plugins from '../../plugins.js';
2
+ import * as shared from '../shared/index.js';
3
+ import * as appstate from '../../appstate.js';
4
4
 
5
5
  import {
6
6
  DeesElement,
@@ -1,5 +1,3 @@
1
- export * from './ops-view-security.js';
2
1
  export * from './ops-view-security-overview.js';
3
2
  export * from './ops-view-security-blocked.js';
4
3
  export * from './ops-view-security-authentication.js';
5
- export * from './ops-view-security-emailsecurity.js';
@@ -1,4 +1,5 @@
1
1
  import * as appstate from '../../appstate.js';
2
+ import { viewHostCss } from '../shared/css.js';
2
3
 
3
4
  import {
4
5
  DeesElement,
@@ -34,8 +35,8 @@ export class OpsViewSecurityAuthentication extends DeesElement {
34
35
 
35
36
  public static styles = [
36
37
  cssManager.defaultStyles,
38
+ viewHostCss,
37
39
  css`
38
- :host { display: block; }
39
40
  h2 {
40
41
  margin: 32px 0 16px 0;
41
42
  font-size: 24px;
@@ -1,4 +1,5 @@
1
1
  import * as appstate from '../../appstate.js';
2
+ import { viewHostCss } from '../shared/css.js';
2
3
 
3
4
  import {
4
5
  DeesElement,
@@ -34,8 +35,8 @@ export class OpsViewSecurityBlocked extends DeesElement {
34
35
 
35
36
  public static styles = [
36
37
  cssManager.defaultStyles,
38
+ viewHostCss,
37
39
  css`
38
- :host { display: block; }
39
40
  dees-statsgrid {
40
41
  margin-bottom: 32px;
41
42
  }
@@ -1,4 +1,5 @@
1
1
  import * as appstate from '../../appstate.js';
2
+ import { viewHostCss } from '../shared/css.js';
2
3
 
3
4
  import {
4
5
  DeesElement,
@@ -34,8 +35,8 @@ export class OpsViewSecurityOverview extends DeesElement {
34
35
 
35
36
  public static styles = [
36
37
  cssManager.defaultStyles,
38
+ viewHostCss,
37
39
  css`
38
- :host { display: block; }
39
40
  h2 {
40
41
  margin: 32px 0 16px 0;
41
42
  font-size: 24px;
package/ts_web/router.ts CHANGED
@@ -4,17 +4,23 @@ import * as appstate from './appstate.js';
4
4
  const SmartRouter = plugins.domtools.plugins.smartrouter.SmartRouter;
5
5
 
6
6
  // Flat top-level views (no subviews)
7
- const flatViews = ['overview', 'configuration', 'emails', 'logs', 'apitokens', 'certificates', 'remoteingress', 'vpn'] as const;
7
+ const flatViews = ['logs', 'certificates'] as const;
8
8
 
9
9
  // Tabbed views and their valid subviews
10
10
  const subviewMap: Record<string, readonly string[]> = {
11
- network: ['activity', 'routes', 'sourceprofiles', 'networktargets', 'targetprofiles'] as const,
12
- security: ['overview', 'blocked', 'authentication', 'emailsecurity'] as const,
11
+ overview: ['stats', 'configuration'] as const,
12
+ network: ['activity', 'routes', 'sourceprofiles', 'networktargets', 'targetprofiles', 'remoteingress', 'vpn'] as const,
13
+ email: ['log', 'security'] as const,
14
+ access: ['apitokens'] as const,
15
+ security: ['overview', 'blocked', 'authentication'] as const,
13
16
  };
14
17
 
15
18
  // Default subview when user visits the bare parent URL
16
19
  const defaultSubview: Record<string, string> = {
20
+ overview: 'stats',
17
21
  network: 'activity',
22
+ email: 'log',
23
+ access: 'apitokens',
18
24
  security: 'overview',
19
25
  };
20
26
 
@@ -1,24 +0,0 @@
1
- import { DeesElement, type TemplateResult } from '@design.estate/dees-element';
2
- import './ops-view-network-activity.js';
3
- import './ops-view-routes.js';
4
- import './ops-view-sourceprofiles.js';
5
- import './ops-view-networktargets.js';
6
- import './ops-view-targetprofiles.js';
7
- declare global {
8
- interface HTMLElementTagNameMap {
9
- 'ops-view-network': OpsViewNetwork;
10
- }
11
- }
12
- type TNetworkTab = 'activity' | 'routes' | 'sourceprofiles' | 'networktargets' | 'targetprofiles';
13
- export declare class OpsViewNetwork extends DeesElement {
14
- accessor selectedTab: TNetworkTab;
15
- private tabLabelMap;
16
- private labelToTab;
17
- private static isNetworkTab;
18
- constructor();
19
- firstUpdated(): Promise<void>;
20
- static styles: import("@design.estate/dees-element").CSSResult[];
21
- render(): TemplateResult;
22
- private renderTabContent;
23
- }
24
- export {};
@@ -1,151 +0,0 @@
1
- var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
2
- function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
3
- var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
4
- var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
5
- var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
6
- var _, done = false;
7
- for (var i = decorators.length - 1; i >= 0; i--) {
8
- var context = {};
9
- for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
10
- for (var p in contextIn.access) context.access[p] = contextIn.access[p];
11
- context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
12
- var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
13
- if (kind === "accessor") {
14
- if (result === void 0) continue;
15
- if (result === null || typeof result !== "object") throw new TypeError("Object expected");
16
- if (_ = accept(result.get)) descriptor.get = _;
17
- if (_ = accept(result.set)) descriptor.set = _;
18
- if (_ = accept(result.init)) initializers.unshift(_);
19
- }
20
- else if (_ = accept(result)) {
21
- if (kind === "field") initializers.unshift(_);
22
- else descriptor[key] = _;
23
- }
24
- }
25
- if (target) Object.defineProperty(target, contextIn.name, descriptor);
26
- done = true;
27
- };
28
- var __runInitializers = (this && this.__runInitializers) || function (thisArg, initializers, value) {
29
- var useValue = arguments.length > 2;
30
- for (var i = 0; i < initializers.length; i++) {
31
- value = useValue ? initializers[i].call(thisArg, value) : initializers[i].call(thisArg);
32
- }
33
- return useValue ? value : void 0;
34
- };
35
- import * as appstate from '../../appstate.js';
36
- import { appRouter } from '../../router.js';
37
- import { viewHostCss } from '../shared/css.js';
38
- import { DeesElement, customElement, html, state, css, cssManager, } from '@design.estate/dees-element';
39
- // Side-effect imports register the subview custom elements
40
- import './ops-view-network-activity.js';
41
- import './ops-view-routes.js';
42
- import './ops-view-sourceprofiles.js';
43
- import './ops-view-networktargets.js';
44
- import './ops-view-targetprofiles.js';
45
- let OpsViewNetwork = (() => {
46
- let _classDecorators = [customElement('ops-view-network')];
47
- let _classDescriptor;
48
- let _classExtraInitializers = [];
49
- let _classThis;
50
- let _classSuper = DeesElement;
51
- let _selectedTab_decorators;
52
- let _selectedTab_initializers = [];
53
- let _selectedTab_extraInitializers = [];
54
- var OpsViewNetwork = class extends _classSuper {
55
- static { _classThis = this; }
56
- static {
57
- const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(_classSuper[Symbol.metadata] ?? null) : void 0;
58
- _selectedTab_decorators = [state()];
59
- __esDecorate(this, null, _selectedTab_decorators, { kind: "accessor", name: "selectedTab", static: false, private: false, access: { has: obj => "selectedTab" in obj, get: obj => obj.selectedTab, set: (obj, value) => { obj.selectedTab = value; } }, metadata: _metadata }, _selectedTab_initializers, _selectedTab_extraInitializers);
60
- __esDecorate(null, _classDescriptor = { value: _classThis }, _classDecorators, { kind: "class", name: _classThis.name, metadata: _metadata }, null, _classExtraInitializers);
61
- OpsViewNetwork = _classThis = _classDescriptor.value;
62
- if (_metadata) Object.defineProperty(_classThis, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
63
- }
64
- #selectedTab_accessor_storage = __runInitializers(this, _selectedTab_initializers, 'activity');
65
- get selectedTab() { return this.#selectedTab_accessor_storage; }
66
- set selectedTab(value) { this.#selectedTab_accessor_storage = value; }
67
- tabLabelMap = (__runInitializers(this, _selectedTab_extraInitializers), {
68
- 'activity': 'Network Activity',
69
- 'routes': 'Routes',
70
- 'sourceprofiles': 'Source Profiles',
71
- 'networktargets': 'Network Targets',
72
- 'targetprofiles': 'Target Profiles',
73
- });
74
- labelToTab = {
75
- 'Network Activity': 'activity',
76
- 'Routes': 'routes',
77
- 'Source Profiles': 'sourceprofiles',
78
- 'Network Targets': 'networktargets',
79
- 'Target Profiles': 'targetprofiles',
80
- };
81
- static isNetworkTab(s) {
82
- return s === 'activity' || s === 'routes' || s === 'sourceprofiles'
83
- || s === 'networktargets' || s === 'targetprofiles';
84
- }
85
- constructor() {
86
- super();
87
- // Read initial subview from state (URL-driven)
88
- const initialState = appstate.uiStatePart.getState();
89
- if (OpsViewNetwork.isNetworkTab(initialState.activeSubview)) {
90
- this.selectedTab = initialState.activeSubview;
91
- }
92
- // Subscribe to future changes (back/forward navigation, direct URL entry)
93
- const sub = appstate.uiStatePart.select((s) => s.activeSubview).subscribe((sub) => {
94
- if (OpsViewNetwork.isNetworkTab(sub) && sub !== this.selectedTab) {
95
- this.selectedTab = sub;
96
- }
97
- });
98
- this.rxSubscriptions.push(sub);
99
- }
100
- async firstUpdated() {
101
- const toggle = this.shadowRoot.querySelector('dees-input-multitoggle');
102
- if (toggle) {
103
- const sub = toggle.changeSubject.subscribe(() => {
104
- const tab = this.labelToTab[toggle.selectedOption];
105
- if (tab && tab !== this.selectedTab) {
106
- // Push URL → router updates state → subscription updates selectedTab
107
- appRouter.navigateToView('network', tab);
108
- }
109
- });
110
- this.rxSubscriptions.push(sub);
111
- }
112
- }
113
- static styles = [
114
- cssManager.defaultStyles,
115
- viewHostCss,
116
- css `
117
- dees-input-multitoggle {
118
- margin-bottom: 24px;
119
- }
120
- `,
121
- ];
122
- render() {
123
- return html `
124
- <dees-heading level="2">Network</dees-heading>
125
-
126
- <dees-input-multitoggle
127
- .type=${'single'}
128
- .options=${['Network Activity', 'Routes', 'Source Profiles', 'Network Targets', 'Target Profiles']}
129
- .selectedOption=${this.tabLabelMap[this.selectedTab]}
130
- ></dees-input-multitoggle>
131
-
132
- ${this.renderTabContent()}
133
- `;
134
- }
135
- renderTabContent() {
136
- switch (this.selectedTab) {
137
- case 'activity': return html `<ops-view-network-activity></ops-view-network-activity>`;
138
- case 'routes': return html `<ops-view-routes></ops-view-routes>`;
139
- case 'sourceprofiles': return html `<ops-view-sourceprofiles></ops-view-sourceprofiles>`;
140
- case 'networktargets': return html `<ops-view-networktargets></ops-view-networktargets>`;
141
- case 'targetprofiles': return html `<ops-view-targetprofiles></ops-view-targetprofiles>`;
142
- }
143
- }
144
- static {
145
- __runInitializers(_classThis, _classExtraInitializers);
146
- }
147
- };
148
- return OpsViewNetwork = _classThis;
149
- })();
150
- export { OpsViewNetwork };
151
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3BzLXZpZXctbmV0d29yay5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3RzX3dlYi9lbGVtZW50cy9uZXR3b3JrL29wcy12aWV3LW5ldHdvcmsudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUFBLE9BQU8sS0FBSyxRQUFRLE1BQU0sbUJBQW1CLENBQUM7QUFDOUMsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQzVDLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQUUvQyxPQUFPLEVBQ0wsV0FBVyxFQUNYLGFBQWEsRUFDYixJQUFJLEVBQ0osS0FBSyxFQUNMLEdBQUcsRUFDSCxVQUFVLEdBRVgsTUFBTSw2QkFBNkIsQ0FBQztBQUVyQywyREFBMkQ7QUFDM0QsT0FBTyxnQ0FBZ0MsQ0FBQztBQUN4QyxPQUFPLHNCQUFzQixDQUFDO0FBQzlCLE9BQU8sOEJBQThCLENBQUM7QUFDdEMsT0FBTyw4QkFBOEIsQ0FBQztBQUN0QyxPQUFPLDhCQUE4QixDQUFDO0lBV3pCLGNBQWM7NEJBRDFCLGFBQWEsQ0FBQyxrQkFBa0IsQ0FBQzs7OztzQkFDRSxXQUFXOzs7OzhCQUFuQixTQUFRLFdBQVc7Ozs7dUNBQzVDLEtBQUssRUFBRTtZQUNSLHNMQUFTLFdBQVcsNkJBQVgsV0FBVyxpR0FBMkI7WUFGakQsNktBd0ZDOzs7O1FBdEZDLG1GQUFvQyxVQUFVLEVBQUM7UUFBL0MsSUFBUyxXQUFXLGlEQUEyQjtRQUEvQyxJQUFTLFdBQVcsdURBQTJCO1FBRXZDLFdBQVcsNkRBQWdDO1lBQ2pELFVBQVUsRUFBUSxrQkFBa0I7WUFDcEMsUUFBUSxFQUFVLFFBQVE7WUFDMUIsZ0JBQWdCLEVBQUUsaUJBQWlCO1lBQ25DLGdCQUFnQixFQUFFLGlCQUFpQjtZQUNuQyxnQkFBZ0IsRUFBRSxpQkFBaUI7U0FDcEMsRUFBQztRQUVNLFVBQVUs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
@@ -1,23 +0,0 @@
1
- import { DeesElement, type TemplateResult } from '@design.estate/dees-element';
2
- import './ops-view-security-overview.js';
3
- import './ops-view-security-blocked.js';
4
- import './ops-view-security-authentication.js';
5
- import './ops-view-security-emailsecurity.js';
6
- declare global {
7
- interface HTMLElementTagNameMap {
8
- 'ops-view-security': OpsViewSecurity;
9
- }
10
- }
11
- type TSecurityTab = 'overview' | 'blocked' | 'authentication' | 'emailsecurity';
12
- export declare class OpsViewSecurity extends DeesElement {
13
- accessor selectedTab: TSecurityTab;
14
- private tabLabelMap;
15
- private labelToTab;
16
- private static isSecurityTab;
17
- constructor();
18
- firstUpdated(): Promise<void>;
19
- static styles: import("@design.estate/dees-element").CSSResult[];
20
- render(): TemplateResult;
21
- private renderTabContent;
22
- }
23
- export {};