@serve.zone/dcrouter 13.2.2 → 13.4.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.
Files changed (81) hide show
  1. package/dist_serve/bundle.js +1499 -1413
  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.d.ts +1 -0
  5. package/dist_ts_web/appstate.js +14 -38
  6. package/dist_ts_web/elements/access/index.d.ts +1 -0
  7. package/dist_ts_web/elements/access/index.js +2 -0
  8. package/dist_ts_web/elements/{ops-view-apitokens.d.ts → access/ops-view-apitokens.d.ts} +1 -1
  9. package/dist_ts_web/elements/{ops-view-apitokens.js → access/ops-view-apitokens.js} +4 -4
  10. package/dist_ts_web/elements/email/index.d.ts +2 -0
  11. package/dist_ts_web/elements/email/index.js +3 -0
  12. package/dist_ts_web/elements/email/ops-view-email-security.d.ts +14 -0
  13. package/dist_ts_web/elements/email/ops-view-email-security.js +197 -0
  14. package/dist_ts_web/elements/{ops-view-emails.d.ts → email/ops-view-emails.d.ts} +2 -2
  15. package/dist_ts_web/elements/{ops-view-emails.js → email/ops-view-emails.js} +5 -5
  16. package/dist_ts_web/elements/index.d.ts +5 -12
  17. package/dist_ts_web/elements/index.js +6 -13
  18. package/dist_ts_web/elements/network/index.d.ts +7 -0
  19. package/dist_ts_web/elements/network/index.js +8 -0
  20. package/dist_ts_web/elements/{ops-view-network.d.ts → network/ops-view-network-activity.d.ts} +3 -3
  21. package/dist_ts_web/elements/{ops-view-network.js → network/ops-view-network-activity.js} +20 -32
  22. package/dist_ts_web/elements/{ops-view-networktargets.d.ts → network/ops-view-networktargets.d.ts} +1 -1
  23. package/dist_ts_web/elements/{ops-view-networktargets.js → network/ops-view-networktargets.js} +5 -5
  24. package/dist_ts_web/elements/{ops-view-remoteingress.d.ts → network/ops-view-remoteingress.d.ts} +1 -1
  25. package/dist_ts_web/elements/{ops-view-remoteingress.js → network/ops-view-remoteingress.js} +5 -5
  26. package/dist_ts_web/elements/{ops-view-routes.d.ts → network/ops-view-routes.d.ts} +1 -1
  27. package/dist_ts_web/elements/{ops-view-routes.js → network/ops-view-routes.js} +5 -5
  28. package/dist_ts_web/elements/{ops-view-sourceprofiles.d.ts → network/ops-view-sourceprofiles.d.ts} +1 -1
  29. package/dist_ts_web/elements/{ops-view-sourceprofiles.js → network/ops-view-sourceprofiles.js} +5 -5
  30. package/dist_ts_web/elements/{ops-view-targetprofiles.d.ts → network/ops-view-targetprofiles.d.ts} +2 -2
  31. package/dist_ts_web/elements/{ops-view-targetprofiles.js → network/ops-view-targetprofiles.js} +6 -6
  32. package/dist_ts_web/elements/{ops-view-vpn.d.ts → network/ops-view-vpn.d.ts} +2 -2
  33. package/dist_ts_web/elements/{ops-view-vpn.js → network/ops-view-vpn.js} +6 -6
  34. package/dist_ts_web/elements/ops-dashboard.d.ts +8 -2
  35. package/dist_ts_web/elements/ops-dashboard.js +101 -83
  36. package/dist_ts_web/elements/overview/index.d.ts +2 -0
  37. package/dist_ts_web/elements/overview/index.js +3 -0
  38. package/dist_ts_web/elements/{ops-view-config.d.ts → overview/ops-view-config.d.ts} +2 -2
  39. package/dist_ts_web/elements/{ops-view-config.js → overview/ops-view-config.js} +9 -9
  40. package/dist_ts_web/elements/{ops-view-overview.d.ts → overview/ops-view-overview.d.ts} +2 -2
  41. package/dist_ts_web/elements/{ops-view-overview.js → overview/ops-view-overview.js} +4 -4
  42. package/dist_ts_web/elements/security/index.d.ts +3 -0
  43. package/dist_ts_web/elements/security/index.js +4 -0
  44. package/dist_ts_web/elements/security/ops-view-security-authentication.d.ts +13 -0
  45. package/dist_ts_web/elements/security/ops-view-security-authentication.js +157 -0
  46. package/dist_ts_web/elements/security/ops-view-security-blocked.d.ts +15 -0
  47. package/dist_ts_web/elements/security/ops-view-security-blocked.js +153 -0
  48. package/dist_ts_web/elements/security/ops-view-security-overview.d.ts +16 -0
  49. package/dist_ts_web/elements/security/ops-view-security-overview.js +205 -0
  50. package/dist_ts_web/router.d.ts +5 -3
  51. package/dist_ts_web/router.js +75 -17
  52. package/package.json +2 -2
  53. package/ts/00_commitinfo_data.ts +1 -1
  54. package/ts_web/00_commitinfo_data.ts +1 -1
  55. package/ts_web/appstate.ts +15 -42
  56. package/ts_web/elements/access/index.ts +1 -0
  57. package/ts_web/elements/{ops-view-apitokens.ts → access/ops-view-apitokens.ts} +3 -3
  58. package/ts_web/elements/email/index.ts +2 -0
  59. package/ts_web/elements/email/ops-view-email-security.ts +160 -0
  60. package/ts_web/elements/{ops-view-emails.ts → email/ops-view-emails.ts} +4 -4
  61. package/ts_web/elements/index.ts +6 -13
  62. package/ts_web/elements/network/index.ts +7 -0
  63. package/ts_web/elements/{ops-view-network.ts → network/ops-view-network-activity.ts} +43 -55
  64. package/ts_web/elements/{ops-view-networktargets.ts → network/ops-view-networktargets.ts} +4 -4
  65. package/ts_web/elements/{ops-view-remoteingress.ts → network/ops-view-remoteingress.ts} +4 -4
  66. package/ts_web/elements/{ops-view-routes.ts → network/ops-view-routes.ts} +4 -4
  67. package/ts_web/elements/{ops-view-sourceprofiles.ts → network/ops-view-sourceprofiles.ts} +4 -4
  68. package/ts_web/elements/{ops-view-targetprofiles.ts → network/ops-view-targetprofiles.ts} +5 -5
  69. package/ts_web/elements/{ops-view-vpn.ts → network/ops-view-vpn.ts} +5 -5
  70. package/ts_web/elements/ops-dashboard.ts +125 -90
  71. package/ts_web/elements/overview/index.ts +2 -0
  72. package/ts_web/elements/{ops-view-config.ts → overview/ops-view-config.ts} +8 -8
  73. package/ts_web/elements/{ops-view-overview.ts → overview/ops-view-overview.ts} +3 -3
  74. package/ts_web/elements/security/index.ts +3 -0
  75. package/ts_web/elements/security/ops-view-security-authentication.ts +121 -0
  76. package/ts_web/elements/security/ops-view-security-blocked.ts +118 -0
  77. package/ts_web/elements/security/ops-view-security-overview.ts +172 -0
  78. package/ts_web/router.ts +81 -17
  79. package/dist_ts_web/elements/ops-view-security.d.ts +0 -24
  80. package/dist_ts_web/elements/ops-view-security.js +0 -484
  81. package/ts_web/elements/ops-view-security.ts +0 -456
@@ -1,456 +0,0 @@
1
- import * as plugins from '../plugins.js';
2
- import * as shared from './shared/index.js';
3
- import * as appstate from '../appstate.js';
4
-
5
- import {
6
- DeesElement,
7
- customElement,
8
- html,
9
- state,
10
- css,
11
- cssManager,
12
- } from '@design.estate/dees-element';
13
- import { type IStatsTile } from '@design.estate/dees-catalog';
14
-
15
- @customElement('ops-view-security')
16
- export class OpsViewSecurity extends DeesElement {
17
- @state()
18
- accessor statsState: appstate.IStatsState = {
19
- serverStats: null,
20
- emailStats: null,
21
- dnsStats: null,
22
- securityMetrics: null,
23
- radiusStats: null,
24
- vpnStats: null,
25
- lastUpdated: 0,
26
- isLoading: false,
27
- error: null,
28
- };
29
-
30
- @state()
31
- accessor selectedTab: 'overview' | 'blocked' | 'authentication' | 'email-security' = 'overview';
32
-
33
- private tabLabelMap: Record<string, string> = {
34
- 'overview': 'Overview',
35
- 'blocked': 'Blocked IPs',
36
- 'authentication': 'Authentication',
37
- 'email-security': 'Email Security',
38
- };
39
-
40
- private labelToTab: Record<string, 'overview' | 'blocked' | 'authentication' | 'email-security'> = {
41
- 'Overview': 'overview',
42
- 'Blocked IPs': 'blocked',
43
- 'Authentication': 'authentication',
44
- 'Email Security': 'email-security',
45
- };
46
-
47
- constructor() {
48
- super();
49
- const subscription = appstate.statsStatePart
50
- .select((stateArg) => stateArg)
51
- .subscribe((statsState) => {
52
- this.statsState = statsState;
53
- });
54
- this.rxSubscriptions.push(subscription);
55
- }
56
-
57
- async firstUpdated() {
58
- const toggle = this.shadowRoot!.querySelector('dees-input-multitoggle') as any;
59
- if (toggle) {
60
- const sub = toggle.changeSubject.subscribe(() => {
61
- const tab = this.labelToTab[toggle.selectedOption];
62
- if (tab) this.selectedTab = tab;
63
- });
64
- this.rxSubscriptions.push(sub);
65
- }
66
- }
67
-
68
- public static styles = [
69
- cssManager.defaultStyles,
70
- shared.viewHostCss,
71
- css`
72
- dees-input-multitoggle {
73
- margin-bottom: 24px;
74
- }
75
-
76
- h2 {
77
- margin: 32px 0 16px 0;
78
- font-size: 24px;
79
- font-weight: 600;
80
- color: ${cssManager.bdTheme('#333', '#ccc')};
81
- }
82
-
83
- dees-statsgrid {
84
- margin-bottom: 32px;
85
- }
86
-
87
- .securityCard {
88
- background: ${cssManager.bdTheme('#fff', '#222')};
89
- border: 1px solid ${cssManager.bdTheme('#e9ecef', '#333')};
90
- border-radius: 8px;
91
- padding: 24px;
92
- position: relative;
93
- overflow: hidden;
94
- }
95
-
96
- .actionButton {
97
- margin-top: 16px;
98
- }
99
-
100
- `,
101
- ];
102
-
103
- public render() {
104
- return html`
105
- <dees-heading level="2">Security</dees-heading>
106
-
107
- <dees-input-multitoggle
108
- .type=${'single'}
109
- .options=${['Overview', 'Blocked IPs', 'Authentication', 'Email Security']}
110
- .selectedOption=${this.tabLabelMap[this.selectedTab]}
111
- ></dees-input-multitoggle>
112
-
113
- ${this.renderTabContent()}
114
- `;
115
- }
116
-
117
- private renderTabContent() {
118
- const metrics = this.statsState.securityMetrics;
119
-
120
- if (!metrics) {
121
- return html`
122
- <div class="loadingMessage">
123
- <p>Loading security metrics...</p>
124
- </div>
125
- `;
126
- }
127
-
128
- switch(this.selectedTab) {
129
- case 'overview':
130
- return this.renderOverview(metrics);
131
- case 'blocked':
132
- return this.renderBlockedIPs(metrics);
133
- case 'authentication':
134
- return this.renderAuthentication(metrics);
135
- case 'email-security':
136
- return this.renderEmailSecurity(metrics);
137
- }
138
- }
139
-
140
- private renderOverview(metrics: any) {
141
- const threatLevel = this.calculateThreatLevel(metrics);
142
- const threatScore = this.getThreatScore(metrics);
143
-
144
- // Derive active sessions from recent successful auth events (last hour)
145
- const allEvents: any[] = metrics.recentEvents || [];
146
- const oneHourAgo = Date.now() - 3600000;
147
- const recentAuthSuccesses = allEvents.filter(
148
- (evt: any) => evt.type === 'authentication' && evt.success === true && evt.timestamp >= oneHourAgo
149
- ).length;
150
-
151
- const tiles: IStatsTile[] = [
152
- {
153
- id: 'threatLevel',
154
- title: 'Threat Level',
155
- value: threatScore,
156
- type: 'gauge',
157
- icon: 'lucide:Shield',
158
- gaugeOptions: {
159
- min: 0,
160
- max: 100,
161
- thresholds: [
162
- { value: 0, color: '#ef4444' },
163
- { value: 30, color: '#f59e0b' },
164
- { value: 70, color: '#22c55e' },
165
- ],
166
- },
167
- description: `Status: ${threatLevel.toUpperCase()}`,
168
- },
169
- {
170
- id: 'blockedThreats',
171
- title: 'Blocked Threats',
172
- value: (metrics.blockedIPs?.length || 0) + metrics.spamDetected,
173
- type: 'number',
174
- icon: 'lucide:ShieldCheck',
175
- color: '#ef4444',
176
- description: 'Total threats blocked today',
177
- },
178
- {
179
- id: 'activeSessions',
180
- title: 'Active Sessions',
181
- value: recentAuthSuccesses,
182
- type: 'number',
183
- icon: 'lucide:Users',
184
- color: '#22c55e',
185
- description: 'Authenticated in last hour',
186
- },
187
- {
188
- id: 'authFailures',
189
- title: 'Auth Failures',
190
- value: metrics.authenticationFailures,
191
- type: 'number',
192
- icon: 'lucide:LockOpen',
193
- color: metrics.authenticationFailures > 10 ? '#ef4444' : '#f59e0b',
194
- description: 'Failed login attempts today',
195
- },
196
- ];
197
-
198
- return html`
199
- <dees-statsgrid
200
- .tiles=${tiles}
201
- .minTileWidth=${200}
202
- ></dees-statsgrid>
203
-
204
- <h2>Recent Security Events</h2>
205
- <dees-table
206
- .heading1=${'Security Events'}
207
- .heading2=${'Last 24 hours'}
208
- .data=${this.getSecurityEvents(metrics)}
209
- .showColumnFilters=${true}
210
- .displayFunction=${(item) => ({
211
- 'Time': new Date(item.timestamp).toLocaleTimeString(),
212
- 'Event': item.event,
213
- 'Severity': item.severity,
214
- 'Details': item.details,
215
- })}
216
- ></dees-table>
217
- `;
218
- }
219
-
220
- private renderBlockedIPs(metrics: any) {
221
- const blockedIPs: string[] = metrics.blockedIPs || [];
222
-
223
- const tiles: IStatsTile[] = [
224
- {
225
- id: 'totalBlocked',
226
- title: 'Blocked IPs',
227
- value: blockedIPs.length,
228
- type: 'number',
229
- icon: 'lucide:ShieldBan',
230
- color: blockedIPs.length > 0 ? '#ef4444' : '#22c55e',
231
- description: 'Currently blocked addresses',
232
- },
233
- ];
234
-
235
- return html`
236
- <dees-statsgrid
237
- .tiles=${tiles}
238
- .minTileWidth=${200}
239
- ></dees-statsgrid>
240
-
241
- <dees-table
242
- .heading1=${'Blocked IP Addresses'}
243
- .heading2=${'IPs blocked due to suspicious activity'}
244
- .data=${blockedIPs.map((ip) => ({ ip }))}
245
- .showColumnFilters=${true}
246
- .displayFunction=${(item) => ({
247
- 'IP Address': item.ip,
248
- 'Reason': 'Suspicious activity',
249
- })}
250
- .dataActions=${[
251
- {
252
- name: 'Unblock',
253
- iconName: 'lucide:shield-off',
254
- type: ['contextmenu' as const],
255
- actionFunc: async (item) => {
256
- await this.unblockIP(item.ip);
257
- },
258
- },
259
- {
260
- name: 'Clear All',
261
- iconName: 'lucide:trash-2',
262
- type: ['header' as const],
263
- actionFunc: async () => {
264
- await this.clearBlockedIPs();
265
- },
266
- },
267
- ]}
268
- ></dees-table>
269
- `;
270
- }
271
-
272
- private renderAuthentication(metrics: any) {
273
- // Derive auth events from recentEvents
274
- const allEvents: any[] = metrics.recentEvents || [];
275
- const authEvents = allEvents.filter((evt: any) => evt.type === 'authentication');
276
- const successfulLogins = authEvents.filter((evt: any) => evt.success === true).length;
277
-
278
- const tiles: IStatsTile[] = [
279
- {
280
- id: 'authFailures',
281
- title: 'Authentication Failures',
282
- value: metrics.authenticationFailures,
283
- type: 'number',
284
- icon: 'lucide:LockOpen',
285
- color: metrics.authenticationFailures > 10 ? '#ef4444' : '#f59e0b',
286
- description: 'Failed authentication attempts today',
287
- },
288
- {
289
- id: 'successfulLogins',
290
- title: 'Successful Logins',
291
- value: successfulLogins,
292
- type: 'number',
293
- icon: 'lucide:Lock',
294
- color: '#22c55e',
295
- description: 'Successful logins today',
296
- },
297
- ];
298
-
299
- // Map auth events to login history table data
300
- const loginHistory = authEvents.map((evt: any) => ({
301
- timestamp: evt.timestamp,
302
- username: evt.details?.username || 'unknown',
303
- ipAddress: evt.ipAddress || 'unknown',
304
- success: evt.success ?? false,
305
- reason: evt.success ? '' : evt.message || 'Authentication failed',
306
- }));
307
-
308
- return html`
309
- <dees-statsgrid
310
- .tiles=${tiles}
311
- .minTileWidth=${200}
312
- ></dees-statsgrid>
313
-
314
- <h2>Recent Login Attempts</h2>
315
- <dees-table
316
- .heading1=${'Login History'}
317
- .heading2=${'Recent authentication attempts'}
318
- .data=${loginHistory}
319
- .showColumnFilters=${true}
320
- .displayFunction=${(item) => ({
321
- 'Time': new Date(item.timestamp).toLocaleString(),
322
- 'Username': item.username,
323
- 'IP Address': item.ipAddress,
324
- 'Status': item.success ? 'Success' : 'Failed',
325
- 'Reason': item.reason || '-',
326
- })}
327
- ></dees-table>
328
- `;
329
- }
330
-
331
- private renderEmailSecurity(metrics: any) {
332
- const tiles: IStatsTile[] = [
333
- {
334
- id: 'malware',
335
- title: 'Malware Detection',
336
- value: metrics.malwareDetected,
337
- type: 'number',
338
- icon: 'lucide:BugOff',
339
- color: metrics.malwareDetected > 0 ? '#ef4444' : '#22c55e',
340
- description: 'Malware detected',
341
- },
342
- {
343
- id: 'phishing',
344
- title: 'Phishing Detection',
345
- value: metrics.phishingDetected,
346
- type: 'number',
347
- icon: 'lucide:Fish',
348
- color: metrics.phishingDetected > 0 ? '#ef4444' : '#22c55e',
349
- description: 'Phishing attempts detected',
350
- },
351
- {
352
- id: 'suspicious',
353
- title: 'Suspicious Activities',
354
- value: metrics.suspiciousActivities,
355
- type: 'number',
356
- icon: 'lucide:TriangleAlert',
357
- color: metrics.suspiciousActivities > 5 ? '#ef4444' : '#f59e0b',
358
- description: 'Suspicious activities detected',
359
- },
360
- {
361
- id: 'spam',
362
- title: 'Spam Detection',
363
- value: metrics.spamDetected,
364
- type: 'number',
365
- icon: 'lucide:Ban',
366
- color: '#f59e0b',
367
- description: 'Spam emails blocked',
368
- },
369
- ];
370
-
371
- return html`
372
- <dees-statsgrid
373
- .tiles=${tiles}
374
- .minTileWidth=${200}
375
- ></dees-statsgrid>
376
-
377
- <h2>Email Security Configuration</h2>
378
- <div class="securityCard">
379
- <dees-form>
380
- <dees-input-checkbox
381
- .key=${'enableSPF'}
382
- .label=${'Enable SPF checking'}
383
- .value=${true}
384
- ></dees-input-checkbox>
385
- <dees-input-checkbox
386
- .key=${'enableDKIM'}
387
- .label=${'Enable DKIM validation'}
388
- .value=${true}
389
- ></dees-input-checkbox>
390
- <dees-input-checkbox
391
- .key=${'enableDMARC'}
392
- .label=${'Enable DMARC policy enforcement'}
393
- .value=${true}
394
- ></dees-input-checkbox>
395
- <dees-input-checkbox
396
- .key=${'enableSpamFilter'}
397
- .label=${'Enable spam filtering'}
398
- .value=${true}
399
- ></dees-input-checkbox>
400
- </dees-form>
401
- <dees-button
402
- class="actionButton"
403
- type="highlighted"
404
- @click=${() => this.saveEmailSecuritySettings()}
405
- >
406
- Save Settings
407
- </dees-button>
408
- </div>
409
- `;
410
- }
411
-
412
- private calculateThreatLevel(metrics: any): string {
413
- const score = this.getThreatScore(metrics);
414
- if (score < 30) return 'alert';
415
- if (score < 70) return 'warning';
416
- return 'success';
417
- }
418
-
419
- private getThreatScore(metrics: any): number {
420
- // Simple scoring algorithm
421
- let score = 100;
422
- const blockedCount = Array.isArray(metrics.blockedIPs) ? metrics.blockedIPs.length : (metrics.blockedIPs || 0);
423
- score -= blockedCount * 2;
424
- score -= (metrics.authenticationFailures || 0) * 1;
425
- score -= (metrics.spamDetected || 0) * 0.5;
426
- score -= (metrics.malwareDetected || 0) * 3;
427
- score -= (metrics.phishingDetected || 0) * 3;
428
- score -= (metrics.suspiciousActivities || 0) * 2;
429
- return Math.max(0, Math.min(100, Math.round(score)));
430
- }
431
-
432
- private getSecurityEvents(metrics: any): any[] {
433
- const events: any[] = metrics.recentEvents || [];
434
- return events.map((evt: any) => ({
435
- timestamp: evt.timestamp,
436
- event: evt.message,
437
- severity: evt.level === 'critical' ? 'critical' : evt.level === 'error' ? 'high' : evt.level === 'warn' ? 'warning' : 'info',
438
- details: evt.ipAddress ? `IP: ${evt.ipAddress}` : evt.domain ? `Domain: ${evt.domain}` : evt.type,
439
- }));
440
- }
441
-
442
- private async clearBlockedIPs() {
443
- // SmartProxy manages IP blocking — not yet exposed via API
444
- alert('Clearing blocked IPs is not yet supported from the UI.');
445
- }
446
-
447
- private async unblockIP(ip: string) {
448
- // SmartProxy manages IP blocking — not yet exposed via API
449
- alert(`Unblocking IP ${ip} is not yet supported from the UI.`);
450
- }
451
-
452
- private async saveEmailSecuritySettings() {
453
- // Config is read-only from the UI for now
454
- alert('Email security settings are read-only. Update the dcrouter configuration file to change these settings.');
455
- }
456
- }