@serve.zone/dcrouter 12.10.0 → 13.0.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 (86) hide show
  1. package/dist_serve/bundle.js +957 -866
  2. package/dist_ts/00_commitinfo_data.js +2 -2
  3. package/dist_ts/classes.dcrouter.d.ts +5 -4
  4. package/dist_ts/classes.dcrouter.js +25 -49
  5. package/dist_ts/config/classes.reference-resolver.d.ts +7 -7
  6. package/dist_ts/config/classes.reference-resolver.js +27 -27
  7. package/dist_ts/config/classes.route-config-manager.d.ts +2 -2
  8. package/dist_ts/config/classes.route-config-manager.js +12 -15
  9. package/dist_ts/config/classes.target-profile-manager.d.ts +63 -0
  10. package/dist_ts/config/classes.target-profile-manager.js +295 -0
  11. package/dist_ts/config/index.d.ts +1 -0
  12. package/dist_ts/config/index.js +2 -1
  13. package/dist_ts/db/documents/{classes.security-profile.doc.d.ts → classes.source-profile.doc.d.ts} +4 -4
  14. package/dist_ts/db/documents/{classes.security-profile.doc.js → classes.source-profile.doc.js} +9 -9
  15. package/dist_ts/db/documents/classes.target-profile.doc.d.ts +17 -0
  16. package/dist_ts/db/documents/classes.target-profile.doc.js +124 -0
  17. package/dist_ts/db/documents/classes.vpn-client.doc.d.ts +1 -1
  18. package/dist_ts/db/documents/classes.vpn-client.doc.js +8 -8
  19. package/dist_ts/db/documents/index.d.ts +2 -1
  20. package/dist_ts/db/documents/index.js +3 -2
  21. package/dist_ts/opsserver/classes.opsserver.d.ts +2 -1
  22. package/dist_ts/opsserver/classes.opsserver.js +5 -3
  23. package/dist_ts/opsserver/handlers/index.d.ts +2 -1
  24. package/dist_ts/opsserver/handlers/index.js +3 -2
  25. package/dist_ts/opsserver/handlers/{security-profile.handler.d.ts → source-profile.handler.d.ts} +1 -1
  26. package/dist_ts/opsserver/handlers/{security-profile.handler.js → source-profile.handler.js} +20 -20
  27. package/dist_ts/opsserver/handlers/target-profile.handler.d.ts +10 -0
  28. package/dist_ts/opsserver/handlers/target-profile.handler.js +115 -0
  29. package/dist_ts/opsserver/handlers/vpn.handler.js +5 -5
  30. package/dist_ts/vpn/classes.vpn-manager.d.ts +6 -10
  31. package/dist_ts/vpn/classes.vpn-manager.js +11 -34
  32. package/dist_ts_interfaces/data/index.d.ts +1 -0
  33. package/dist_ts_interfaces/data/index.js +2 -1
  34. package/dist_ts_interfaces/data/remoteingress.d.ts +4 -15
  35. package/dist_ts_interfaces/data/route-management.d.ts +9 -6
  36. package/dist_ts_interfaces/data/target-profile.d.ts +28 -0
  37. package/dist_ts_interfaces/data/target-profile.js +2 -0
  38. package/dist_ts_interfaces/data/vpn.d.ts +2 -1
  39. package/dist_ts_interfaces/requests/index.d.ts +2 -1
  40. package/dist_ts_interfaces/requests/index.js +3 -2
  41. package/dist_ts_interfaces/requests/{security-profiles.d.ts → source-profiles.d.ts} +21 -21
  42. package/dist_ts_interfaces/requests/source-profiles.js +2 -0
  43. package/dist_ts_interfaces/requests/target-profiles.d.ts +103 -0
  44. package/dist_ts_interfaces/requests/target-profiles.js +2 -0
  45. package/dist_ts_interfaces/requests/vpn.d.ts +2 -2
  46. package/dist_ts_web/00_commitinfo_data.js +2 -2
  47. package/dist_ts_web/appstate.d.ts +36 -3
  48. package/dist_ts_web/appstate.js +127 -10
  49. package/dist_ts_web/elements/index.d.ts +2 -1
  50. package/dist_ts_web/elements/index.js +3 -2
  51. package/dist_ts_web/elements/ops-dashboard.js +10 -4
  52. package/dist_ts_web/elements/ops-view-routes.js +8 -8
  53. package/dist_ts_web/elements/{ops-view-securityprofiles.d.ts → ops-view-sourceprofiles.d.ts} +2 -2
  54. package/dist_ts_web/elements/{ops-view-securityprofiles.js → ops-view-sourceprofiles.js} +12 -12
  55. package/dist_ts_web/elements/ops-view-targetprofiles.d.ts +19 -0
  56. package/dist_ts_web/elements/ops-view-targetprofiles.js +412 -0
  57. package/dist_ts_web/elements/ops-view-vpn.js +13 -13
  58. package/dist_ts_web/router.d.ts +1 -1
  59. package/dist_ts_web/router.js +2 -2
  60. package/package.json +1 -1
  61. package/ts/00_commitinfo_data.ts +1 -1
  62. package/ts/classes.dcrouter.ts +33 -50
  63. package/ts/config/classes.reference-resolver.ts +34 -34
  64. package/ts/config/classes.route-config-manager.ts +9 -12
  65. package/ts/config/classes.target-profile-manager.ts +348 -0
  66. package/ts/config/index.ts +2 -1
  67. package/ts/db/documents/{classes.security-profile.doc.ts → classes.source-profile.doc.ts} +7 -7
  68. package/ts/db/documents/classes.target-profile.doc.ts +52 -0
  69. package/ts/db/documents/classes.vpn-client.doc.ts +1 -1
  70. package/ts/db/documents/index.ts +2 -1
  71. package/ts/opsserver/classes.opsserver.ts +4 -2
  72. package/ts/opsserver/handlers/index.ts +2 -1
  73. package/ts/opsserver/handlers/{security-profile.handler.ts → source-profile.handler.ts} +25 -25
  74. package/ts/opsserver/handlers/target-profile.handler.ts +155 -0
  75. package/ts/opsserver/handlers/vpn.handler.ts +4 -4
  76. package/ts/vpn/classes.vpn-manager.ts +14 -38
  77. package/ts_web/00_commitinfo_data.ts +1 -1
  78. package/ts_web/appstate.ts +180 -17
  79. package/ts_web/elements/index.ts +2 -1
  80. package/ts_web/elements/ops-dashboard.ts +9 -3
  81. package/ts_web/elements/ops-view-routes.ts +7 -7
  82. package/ts_web/elements/{ops-view-securityprofiles.ts → ops-view-sourceprofiles.ts} +13 -13
  83. package/ts_web/elements/ops-view-targetprofiles.ts +379 -0
  84. package/ts_web/elements/ops-view-vpn.ts +12 -12
  85. package/ts_web/router.ts +1 -1
  86. package/dist_ts_interfaces/requests/security-profiles.js +0 -2
@@ -0,0 +1,379 @@
1
+ import {
2
+ DeesElement,
3
+ html,
4
+ customElement,
5
+ type TemplateResult,
6
+ css,
7
+ state,
8
+ cssManager,
9
+ } from '@design.estate/dees-element';
10
+ import * as plugins from '../plugins.js';
11
+ import * as appstate from '../appstate.js';
12
+ import * as interfaces from '../../dist_ts_interfaces/index.js';
13
+ import { viewHostCss } from './shared/css.js';
14
+ import { type IStatsTile } from '@design.estate/dees-catalog';
15
+
16
+ declare global {
17
+ interface HTMLElementTagNameMap {
18
+ 'ops-view-targetprofiles': OpsViewTargetProfiles;
19
+ }
20
+ }
21
+
22
+ @customElement('ops-view-targetprofiles')
23
+ export class OpsViewTargetProfiles extends DeesElement {
24
+ @state()
25
+ accessor targetProfilesState: appstate.ITargetProfilesState = appstate.targetProfilesStatePart.getState()!;
26
+
27
+ constructor() {
28
+ super();
29
+ const sub = appstate.targetProfilesStatePart.select().subscribe((newState) => {
30
+ this.targetProfilesState = newState;
31
+ });
32
+ this.rxSubscriptions.push(sub);
33
+ }
34
+
35
+ async connectedCallback() {
36
+ await super.connectedCallback();
37
+ await appstate.targetProfilesStatePart.dispatchAction(appstate.fetchTargetProfilesAction, null);
38
+ }
39
+
40
+ public static styles = [
41
+ cssManager.defaultStyles,
42
+ viewHostCss,
43
+ css`
44
+ .profilesContainer {
45
+ display: flex;
46
+ flex-direction: column;
47
+ gap: 24px;
48
+ }
49
+
50
+ .tagBadge {
51
+ display: inline-flex;
52
+ padding: 2px 8px;
53
+ border-radius: 4px;
54
+ font-size: 12px;
55
+ font-weight: 500;
56
+ background: ${cssManager.bdTheme('#eff6ff', '#172554')};
57
+ color: ${cssManager.bdTheme('#1e40af', '#60a5fa')};
58
+ margin-right: 4px;
59
+ margin-bottom: 2px;
60
+ }
61
+ `,
62
+ ];
63
+
64
+ public render(): TemplateResult {
65
+ const profiles = this.targetProfilesState.profiles;
66
+
67
+ const statsTiles: IStatsTile[] = [
68
+ {
69
+ id: 'totalProfiles',
70
+ title: 'Total Profiles',
71
+ type: 'number',
72
+ value: profiles.length,
73
+ icon: 'lucide:target',
74
+ description: 'Reusable target profiles',
75
+ color: '#8b5cf6',
76
+ },
77
+ ];
78
+
79
+ return html`
80
+ <ops-sectionheading>Target Profiles</ops-sectionheading>
81
+ <div class="profilesContainer">
82
+ <dees-statsgrid .tiles=${statsTiles}></dees-statsgrid>
83
+ <dees-table
84
+ .heading1=${'Target Profiles'}
85
+ .heading2=${'Define what resources VPN clients can access'}
86
+ .data=${profiles}
87
+ .displayFunction=${(profile: interfaces.data.ITargetProfile) => ({
88
+ Name: profile.name,
89
+ Description: profile.description || '-',
90
+ Domains: profile.domains?.length
91
+ ? html`${profile.domains.map(d => html`<span class="tagBadge">${d}</span>`)}`
92
+ : '-',
93
+ Targets: profile.targets?.length
94
+ ? html`${profile.targets.map(t => html`<span class="tagBadge">${t.host}:${t.port}</span>`)}`
95
+ : '-',
96
+ 'Route Refs': profile.routeRefs?.length
97
+ ? html`${profile.routeRefs.map(r => html`<span class="tagBadge">${r}</span>`)}`
98
+ : '-',
99
+ Created: new Date(profile.createdAt).toLocaleDateString(),
100
+ })}
101
+ .dataActions=${[
102
+ {
103
+ name: 'Create Profile',
104
+ iconName: 'lucide:plus',
105
+ type: ['header' as const],
106
+ actionFunc: async () => {
107
+ await this.showCreateProfileDialog();
108
+ },
109
+ },
110
+ {
111
+ name: 'Refresh',
112
+ iconName: 'lucide:rotateCw',
113
+ type: ['header' as const],
114
+ actionFunc: async () => {
115
+ await appstate.targetProfilesStatePart.dispatchAction(appstate.fetchTargetProfilesAction, null);
116
+ },
117
+ },
118
+ {
119
+ name: 'Detail',
120
+ iconName: 'lucide:info',
121
+ type: ['doubleClick'] as any,
122
+ actionFunc: async (actionData: any) => {
123
+ const profile = actionData.item as interfaces.data.ITargetProfile;
124
+ await this.showDetailDialog(profile);
125
+ },
126
+ },
127
+ {
128
+ name: 'Edit',
129
+ iconName: 'lucide:pencil',
130
+ type: ['inRow', 'contextmenu'] as any,
131
+ actionFunc: async (actionData: any) => {
132
+ const profile = actionData.item as interfaces.data.ITargetProfile;
133
+ await this.showEditProfileDialog(profile);
134
+ },
135
+ },
136
+ {
137
+ name: 'Delete',
138
+ iconName: 'lucide:trash2',
139
+ type: ['inRow', 'contextmenu'] as any,
140
+ actionFunc: async (actionData: any) => {
141
+ const profile = actionData.item as interfaces.data.ITargetProfile;
142
+ await this.deleteProfile(profile);
143
+ },
144
+ },
145
+ ]}
146
+ ></dees-table>
147
+ </div>
148
+ `;
149
+ }
150
+
151
+ private async showCreateProfileDialog() {
152
+ const { DeesModal } = await import('@design.estate/dees-catalog');
153
+ DeesModal.createAndShow({
154
+ heading: 'Create Target Profile',
155
+ content: html`
156
+ <dees-form>
157
+ <dees-input-text .key=${'name'} .label=${'Name'} .required=${true}></dees-input-text>
158
+ <dees-input-text .key=${'description'} .label=${'Description'}></dees-input-text>
159
+ <dees-input-text .key=${'domains'} .label=${'Domains (comma-separated, e.g. *.example.com)'} ></dees-input-text>
160
+ <dees-input-text .key=${'targets'} .label=${'Targets (comma-separated host:port, e.g. 10.0.0.1:443)'}></dees-input-text>
161
+ <dees-input-text .key=${'routeRefs'} .label=${'Route Refs (comma-separated route names/IDs)'}></dees-input-text>
162
+ </dees-form>
163
+ `,
164
+ menuOptions: [
165
+ { name: 'Cancel', iconName: 'lucide:x', action: async (modalArg: any) => modalArg.destroy() },
166
+ {
167
+ name: 'Create',
168
+ iconName: 'lucide:plus',
169
+ action: async (modalArg: any) => {
170
+ const form = modalArg.shadowRoot?.querySelector('.content')?.querySelector('dees-form');
171
+ if (!form) return;
172
+ const data = await form.collectFormData();
173
+ if (!data.name) return;
174
+
175
+ const domains = data.domains
176
+ ? String(data.domains).split(',').map((s: string) => s.trim()).filter(Boolean)
177
+ : undefined;
178
+ const targets = data.targets
179
+ ? String(data.targets).split(',').map((s: string) => {
180
+ const trimmed = s.trim();
181
+ const lastColon = trimmed.lastIndexOf(':');
182
+ if (lastColon === -1) return null;
183
+ return {
184
+ host: trimmed.substring(0, lastColon),
185
+ port: parseInt(trimmed.substring(lastColon + 1), 10),
186
+ };
187
+ }).filter((t): t is { host: string; port: number } => t !== null && !isNaN(t.port))
188
+ : undefined;
189
+ const routeRefs = data.routeRefs
190
+ ? String(data.routeRefs).split(',').map((s: string) => s.trim()).filter(Boolean)
191
+ : undefined;
192
+
193
+ await appstate.targetProfilesStatePart.dispatchAction(appstate.createTargetProfileAction, {
194
+ name: String(data.name),
195
+ description: data.description ? String(data.description) : undefined,
196
+ domains,
197
+ targets,
198
+ routeRefs,
199
+ });
200
+ modalArg.destroy();
201
+ },
202
+ },
203
+ ],
204
+ });
205
+ }
206
+
207
+ private async showEditProfileDialog(profile: interfaces.data.ITargetProfile) {
208
+ const currentDomains = profile.domains?.join(', ') ?? '';
209
+ const currentTargets = profile.targets?.map(t => `${t.host}:${t.port}`).join(', ') ?? '';
210
+ const currentRouteRefs = profile.routeRefs?.join(', ') ?? '';
211
+
212
+ const { DeesModal } = await import('@design.estate/dees-catalog');
213
+ DeesModal.createAndShow({
214
+ heading: `Edit Profile: ${profile.name}`,
215
+ content: html`
216
+ <dees-form>
217
+ <dees-input-text .key=${'name'} .label=${'Name'} .value=${profile.name}></dees-input-text>
218
+ <dees-input-text .key=${'description'} .label=${'Description'} .value=${profile.description || ''}></dees-input-text>
219
+ <dees-input-text .key=${'domains'} .label=${'Domains (comma-separated, e.g. *.example.com)'} .value=${currentDomains}></dees-input-text>
220
+ <dees-input-text .key=${'targets'} .label=${'Targets (comma-separated host:port, e.g. 10.0.0.1:443)'} .value=${currentTargets}></dees-input-text>
221
+ <dees-input-text .key=${'routeRefs'} .label=${'Route Refs (comma-separated route names/IDs)'} .value=${currentRouteRefs}></dees-input-text>
222
+ </dees-form>
223
+ `,
224
+ menuOptions: [
225
+ { name: 'Cancel', iconName: 'lucide:x', action: async (modalArg: any) => modalArg.destroy() },
226
+ {
227
+ name: 'Save',
228
+ iconName: 'lucide:check',
229
+ action: async (modalArg: any) => {
230
+ const form = modalArg.shadowRoot?.querySelector('.content')?.querySelector('dees-form');
231
+ if (!form) return;
232
+ const data = await form.collectFormData();
233
+
234
+ const domains = data.domains
235
+ ? String(data.domains).split(',').map((s: string) => s.trim()).filter(Boolean)
236
+ : [];
237
+ const targets = data.targets
238
+ ? String(data.targets).split(',').map((s: string) => {
239
+ const trimmed = s.trim();
240
+ if (!trimmed) return null;
241
+ const lastColon = trimmed.lastIndexOf(':');
242
+ if (lastColon === -1) return null;
243
+ return {
244
+ host: trimmed.substring(0, lastColon),
245
+ port: parseInt(trimmed.substring(lastColon + 1), 10),
246
+ };
247
+ }).filter((t): t is { host: string; port: number } => t !== null && !isNaN(t.port))
248
+ : [];
249
+ const routeRefs = data.routeRefs
250
+ ? String(data.routeRefs).split(',').map((s: string) => s.trim()).filter(Boolean)
251
+ : [];
252
+
253
+ await appstate.targetProfilesStatePart.dispatchAction(appstate.updateTargetProfileAction, {
254
+ id: profile.id,
255
+ name: String(data.name),
256
+ description: data.description ? String(data.description) : undefined,
257
+ domains,
258
+ targets,
259
+ routeRefs,
260
+ });
261
+ modalArg.destroy();
262
+ },
263
+ },
264
+ ],
265
+ });
266
+ }
267
+
268
+ private async showDetailDialog(profile: interfaces.data.ITargetProfile) {
269
+ const { DeesModal } = await import('@design.estate/dees-catalog');
270
+
271
+ // Fetch usage (which VPN clients reference this profile)
272
+ let usageHtml = html`<p style="color: #9ca3af;">Loading usage...</p>`;
273
+ try {
274
+ const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
275
+ interfaces.requests.IReq_GetTargetProfileUsage
276
+ >('/typedrequest', 'getTargetProfileUsage');
277
+ const response = await request.fire({
278
+ identity: appstate.loginStatePart.getState()!.identity!,
279
+ id: profile.id,
280
+ });
281
+ if (response.clients.length > 0) {
282
+ usageHtml = html`
283
+ <div style="margin-top: 8px;">
284
+ ${response.clients.map(c => html`
285
+ <div style="padding: 4px 0; font-size: 13px;">
286
+ <strong>${c.clientId}</strong>${c.description ? html` - ${c.description}` : ''}
287
+ </div>
288
+ `)}
289
+ </div>
290
+ `;
291
+ } else {
292
+ usageHtml = html`<p style="color: #9ca3af; font-size: 13px;">No VPN clients reference this profile.</p>`;
293
+ }
294
+ } catch {
295
+ usageHtml = html`<p style="color: #9ca3af;">Usage data unavailable.</p>`;
296
+ }
297
+
298
+ DeesModal.createAndShow({
299
+ heading: `Target Profile: ${profile.name}`,
300
+ content: html`
301
+ <div style="display: flex; flex-direction: column; gap: 12px;">
302
+ <div>
303
+ <div style="font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; color: ${cssManager.bdTheme('#6b7280', '#9ca3af')};">Description</div>
304
+ <div style="font-size: 14px; margin-top: 4px;">${profile.description || '-'}</div>
305
+ </div>
306
+ <div>
307
+ <div style="font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; color: ${cssManager.bdTheme('#6b7280', '#9ca3af')};">Domains</div>
308
+ <div style="font-size: 14px; margin-top: 4px;">
309
+ ${profile.domains?.length
310
+ ? profile.domains.map(d => html`<span class="tagBadge">${d}</span>`)
311
+ : '-'}
312
+ </div>
313
+ </div>
314
+ <div>
315
+ <div style="font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; color: ${cssManager.bdTheme('#6b7280', '#9ca3af')};">Targets</div>
316
+ <div style="font-size: 14px; margin-top: 4px;">
317
+ ${profile.targets?.length
318
+ ? profile.targets.map(t => html`<span class="tagBadge">${t.host}:${t.port}</span>`)
319
+ : '-'}
320
+ </div>
321
+ </div>
322
+ <div>
323
+ <div style="font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; color: ${cssManager.bdTheme('#6b7280', '#9ca3af')};">Route Refs</div>
324
+ <div style="font-size: 14px; margin-top: 4px;">
325
+ ${profile.routeRefs?.length
326
+ ? profile.routeRefs.map(r => html`<span class="tagBadge">${r}</span>`)
327
+ : '-'}
328
+ </div>
329
+ </div>
330
+ <div>
331
+ <div style="font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; color: ${cssManager.bdTheme('#6b7280', '#9ca3af')};">Created</div>
332
+ <div style="font-size: 14px; margin-top: 4px;">${new Date(profile.createdAt).toLocaleString()} by ${profile.createdBy}</div>
333
+ </div>
334
+ <div>
335
+ <div style="font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; color: ${cssManager.bdTheme('#6b7280', '#9ca3af')};">Updated</div>
336
+ <div style="font-size: 14px; margin-top: 4px;">${new Date(profile.updatedAt).toLocaleString()}</div>
337
+ </div>
338
+ <div>
339
+ <div style="font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em; color: ${cssManager.bdTheme('#6b7280', '#9ca3af')};">VPN Clients Using This Profile</div>
340
+ ${usageHtml}
341
+ </div>
342
+ </div>
343
+ `,
344
+ menuOptions: [
345
+ { name: 'Close', iconName: 'lucide:x', action: async (m: any) => await m.destroy() },
346
+ ],
347
+ });
348
+ }
349
+
350
+ private async deleteProfile(profile: interfaces.data.ITargetProfile) {
351
+ await appstate.targetProfilesStatePart.dispatchAction(appstate.deleteTargetProfileAction, {
352
+ id: profile.id,
353
+ force: false,
354
+ });
355
+
356
+ const currentState = appstate.targetProfilesStatePart.getState()!;
357
+ if (currentState.error?.includes('in use')) {
358
+ const { DeesModal } = await import('@design.estate/dees-catalog');
359
+ DeesModal.createAndShow({
360
+ heading: 'Profile In Use',
361
+ content: html`<p>${currentState.error} Force delete?</p>`,
362
+ menuOptions: [
363
+ {
364
+ name: 'Force Delete',
365
+ iconName: 'lucide:trash2',
366
+ action: async (modalArg: any) => {
367
+ await appstate.targetProfilesStatePart.dispatchAction(appstate.deleteTargetProfileAction, {
368
+ id: profile.id,
369
+ force: true,
370
+ });
371
+ modalArg.destroy();
372
+ },
373
+ },
374
+ { name: 'Cancel', iconName: 'lucide:x', action: async (modalArg: any) => modalArg.destroy() },
375
+ ],
376
+ });
377
+ }
378
+ }
379
+ }
@@ -327,8 +327,8 @@ export class OpsViewVpn extends DeesElement {
327
327
  'Status': statusHtml,
328
328
  'Routing': routingHtml,
329
329
  'VPN IP': client.assignedIp || '-',
330
- 'Tags': client.serverDefinedClientTags?.length
331
- ? html`${client.serverDefinedClientTags.map(t => html`<span class="tagBadge">${t}</span>`)}`
330
+ 'Target Profiles': client.targetProfileIds?.length
331
+ ? html`${client.targetProfileIds.map(t => html`<span class="tagBadge">${t}</span>`)}`
332
332
  : '-',
333
333
  'Description': client.description || '-',
334
334
  'Created': new Date(client.createdAt).toLocaleDateString(),
@@ -347,7 +347,7 @@ export class OpsViewVpn extends DeesElement {
347
347
  <dees-form>
348
348
  <dees-input-text .key=${'clientId'} .label=${'Client ID'} .required=${true}></dees-input-text>
349
349
  <dees-input-text .key=${'description'} .label=${'Description'}></dees-input-text>
350
- <dees-input-text .key=${'tags'} .label=${'Server-Defined Tags (comma-separated)'}></dees-input-text>
350
+ <dees-input-text .key=${'targetProfileIds'} .label=${'Target Profile IDs (comma-separated)'}></dees-input-text>
351
351
  <dees-input-checkbox .key=${'forceDestinationSmartproxy'} .label=${'Force traffic through SmartProxy'} .value=${true}></dees-input-checkbox>
352
352
  <div class="hostIpGroup" style="display: none; flex-direction: column; gap: 16px;">
353
353
  <dees-input-checkbox .key=${'useHostIp'} .label=${'Get Host IP'} .value=${false}></dees-input-checkbox>
@@ -383,8 +383,8 @@ export class OpsViewVpn extends DeesElement {
383
383
  if (!form) return;
384
384
  const data = await form.collectFormData();
385
385
  if (!data.clientId) return;
386
- const serverDefinedClientTags = data.tags
387
- ? data.tags.split(',').map((t: string) => t.trim()).filter(Boolean)
386
+ const targetProfileIds = data.targetProfileIds
387
+ ? data.targetProfileIds.split(',').map((t: string) => t.trim()).filter(Boolean)
388
388
  : undefined;
389
389
 
390
390
  // Apply conditional logic based on checkbox states
@@ -406,7 +406,7 @@ export class OpsViewVpn extends DeesElement {
406
406
  await appstate.vpnStatePart.dispatchAction(appstate.createVpnClientAction, {
407
407
  clientId: data.clientId,
408
408
  description: data.description || undefined,
409
- serverDefinedClientTags,
409
+ targetProfileIds,
410
410
  forceDestinationSmartproxy: forceSmartproxy,
411
411
  useHostIp: useHostIp || undefined,
412
412
  useDhcp: useDhcp || undefined,
@@ -479,7 +479,7 @@ export class OpsViewVpn extends DeesElement {
479
479
  <div class="infoItem"><span class="infoLabel">Transport</span><span class="infoValue">${conn.transport}</span></div>
480
480
  ` : ''}
481
481
  <div class="infoItem"><span class="infoLabel">Description</span><span class="infoValue">${client.description || '-'}</span></div>
482
- <div class="infoItem"><span class="infoLabel">Tags</span><span class="infoValue">${client.serverDefinedClientTags?.join(', ') || '-'}</span></div>
482
+ <div class="infoItem"><span class="infoLabel">Target Profiles</span><span class="infoValue">${client.targetProfileIds?.join(', ') || '-'}</span></div>
483
483
  <div class="infoItem"><span class="infoLabel">Routing</span><span class="infoValue">${client.forceDestinationSmartproxy !== false ? 'SmartProxy' : client.useHostIp ? 'Host IP' : 'Direct'}</span></div>
484
484
  ${client.useHostIp ? html`
485
485
  <div class="infoItem"><span class="infoLabel">Host IP</span><span class="infoValue">${client.useDhcp ? 'DHCP' : client.staticIp ? `Static: ${client.staticIp}` : 'Not configured'}</span></div>
@@ -643,7 +643,7 @@ export class OpsViewVpn extends DeesElement {
643
643
  const client = actionData.item as interfaces.data.IVpnClient;
644
644
  const { DeesModal } = await import('@design.estate/dees-catalog');
645
645
  const currentDescription = client.description ?? '';
646
- const currentTags = client.serverDefinedClientTags?.join(', ') ?? '';
646
+ const currentTargetProfileIds = client.targetProfileIds?.join(', ') ?? '';
647
647
  const currentForceSmartproxy = client.forceDestinationSmartproxy ?? true;
648
648
  const currentUseHostIp = client.useHostIp ?? false;
649
649
  const currentUseDhcp = client.useDhcp ?? false;
@@ -659,7 +659,7 @@ export class OpsViewVpn extends DeesElement {
659
659
  content: html`
660
660
  <dees-form>
661
661
  <dees-input-text .key=${'description'} .label=${'Description'} .value=${currentDescription}></dees-input-text>
662
- <dees-input-text .key=${'tags'} .label=${'Server-Defined Tags (comma-separated)'} .value=${currentTags}></dees-input-text>
662
+ <dees-input-text .key=${'targetProfileIds'} .label=${'Target Profile IDs (comma-separated)'} .value=${currentTargetProfileIds}></dees-input-text>
663
663
  <dees-input-checkbox .key=${'forceDestinationSmartproxy'} .label=${'Force traffic through SmartProxy'} .value=${currentForceSmartproxy}></dees-input-checkbox>
664
664
  <div class="hostIpGroup" style="display: ${currentForceSmartproxy ? 'none' : 'flex'}; flex-direction: column; gap: 16px;">
665
665
  <dees-input-checkbox .key=${'useHostIp'} .label=${'Get Host IP'} .value=${currentUseHostIp}></dees-input-checkbox>
@@ -690,8 +690,8 @@ export class OpsViewVpn extends DeesElement {
690
690
  const form = modalArg.shadowRoot?.querySelector('.content')?.querySelector('dees-form');
691
691
  if (!form) return;
692
692
  const data = await form.collectFormData();
693
- const serverDefinedClientTags = data.tags
694
- ? data.tags.split(',').map((t: string) => t.trim()).filter(Boolean)
693
+ const targetProfileIds = data.targetProfileIds
694
+ ? data.targetProfileIds.split(',').map((t: string) => t.trim()).filter(Boolean)
695
695
  : [];
696
696
 
697
697
  // Apply conditional logic based on checkbox states
@@ -713,7 +713,7 @@ export class OpsViewVpn extends DeesElement {
713
713
  await appstate.vpnStatePart.dispatchAction(appstate.updateVpnClientAction, {
714
714
  clientId: client.clientId,
715
715
  description: data.description || undefined,
716
- serverDefinedClientTags,
716
+ targetProfileIds,
717
717
  forceDestinationSmartproxy: forceSmartproxy,
718
718
  useHostIp: useHostIp || undefined,
719
719
  useDhcp: useDhcp || undefined,
package/ts_web/router.ts CHANGED
@@ -3,7 +3,7 @@ import * as appstate from './appstate.js';
3
3
 
4
4
  const SmartRouter = plugins.domtools.plugins.smartrouter.SmartRouter;
5
5
 
6
- export const validViews = ['overview', 'network', 'emails', 'logs', 'routes', 'apitokens', 'configuration', 'security', 'certificates', 'remoteingress', 'vpn', 'securityprofiles', 'networktargets'] as const;
6
+ export const validViews = ['overview', 'network', 'emails', 'logs', 'routes', 'apitokens', 'configuration', 'security', 'certificates', 'remoteingress', 'vpn', 'sourceprofiles', 'networktargets', 'targetprofiles'] as const;
7
7
 
8
8
  export type TValidView = typeof validViews[number];
9
9
 
@@ -1,2 +0,0 @@
1
- import * as plugins from '../plugins.js';
2
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VjdXJpdHktcHJvZmlsZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90c19pbnRlcmZhY2VzL3JlcXVlc3RzL3NlY3VyaXR5LXByb2ZpbGVzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sZUFBZSxDQUFDIn0=