@serve.zone/dcrouter 12.9.4 → 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.
- package/dist_serve/bundle.js +1030 -923
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.dcrouter.d.ts +5 -4
- package/dist_ts/classes.dcrouter.js +25 -49
- package/dist_ts/config/classes.reference-resolver.d.ts +7 -7
- package/dist_ts/config/classes.reference-resolver.js +27 -27
- package/dist_ts/config/classes.route-config-manager.d.ts +2 -2
- package/dist_ts/config/classes.route-config-manager.js +24 -16
- package/dist_ts/config/classes.target-profile-manager.d.ts +63 -0
- package/dist_ts/config/classes.target-profile-manager.js +295 -0
- package/dist_ts/config/index.d.ts +1 -0
- package/dist_ts/config/index.js +2 -1
- package/dist_ts/db/documents/{classes.security-profile.doc.d.ts → classes.source-profile.doc.d.ts} +4 -4
- package/dist_ts/db/documents/{classes.security-profile.doc.js → classes.source-profile.doc.js} +9 -9
- package/dist_ts/db/documents/classes.target-profile.doc.d.ts +17 -0
- package/dist_ts/db/documents/classes.target-profile.doc.js +124 -0
- package/dist_ts/db/documents/classes.vpn-client.doc.d.ts +1 -1
- package/dist_ts/db/documents/classes.vpn-client.doc.js +8 -8
- package/dist_ts/db/documents/index.d.ts +2 -1
- package/dist_ts/db/documents/index.js +3 -2
- package/dist_ts/opsserver/classes.opsserver.d.ts +2 -1
- package/dist_ts/opsserver/classes.opsserver.js +5 -3
- package/dist_ts/opsserver/handlers/index.d.ts +2 -1
- package/dist_ts/opsserver/handlers/index.js +3 -2
- package/dist_ts/opsserver/handlers/{security-profile.handler.d.ts → source-profile.handler.d.ts} +1 -1
- package/dist_ts/opsserver/handlers/{security-profile.handler.js → source-profile.handler.js} +20 -20
- package/dist_ts/opsserver/handlers/target-profile.handler.d.ts +10 -0
- package/dist_ts/opsserver/handlers/target-profile.handler.js +115 -0
- package/dist_ts/opsserver/handlers/vpn.handler.js +5 -5
- package/dist_ts/vpn/classes.vpn-manager.d.ts +6 -10
- package/dist_ts/vpn/classes.vpn-manager.js +11 -34
- package/dist_ts_interfaces/data/index.d.ts +1 -0
- package/dist_ts_interfaces/data/index.js +2 -1
- package/dist_ts_interfaces/data/remoteingress.d.ts +4 -15
- package/dist_ts_interfaces/data/route-management.d.ts +9 -6
- package/dist_ts_interfaces/data/target-profile.d.ts +28 -0
- package/dist_ts_interfaces/data/target-profile.js +2 -0
- package/dist_ts_interfaces/data/vpn.d.ts +2 -1
- package/dist_ts_interfaces/requests/index.d.ts +2 -1
- package/dist_ts_interfaces/requests/index.js +3 -2
- package/dist_ts_interfaces/requests/{security-profiles.d.ts → source-profiles.d.ts} +21 -21
- package/dist_ts_interfaces/requests/source-profiles.js +2 -0
- package/dist_ts_interfaces/requests/target-profiles.d.ts +103 -0
- package/dist_ts_interfaces/requests/target-profiles.js +2 -0
- package/dist_ts_interfaces/requests/vpn.d.ts +2 -2
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/dist_ts_web/appstate.d.ts +36 -3
- package/dist_ts_web/appstate.js +127 -10
- package/dist_ts_web/elements/index.d.ts +2 -1
- package/dist_ts_web/elements/index.js +3 -2
- package/dist_ts_web/elements/ops-dashboard.js +10 -4
- package/dist_ts_web/elements/ops-view-routes.js +120 -10
- package/dist_ts_web/elements/{ops-view-securityprofiles.d.ts → ops-view-sourceprofiles.d.ts} +2 -2
- package/dist_ts_web/elements/{ops-view-securityprofiles.js → ops-view-sourceprofiles.js} +12 -12
- package/dist_ts_web/elements/ops-view-targetprofiles.d.ts +19 -0
- package/dist_ts_web/elements/ops-view-targetprofiles.js +412 -0
- package/dist_ts_web/elements/ops-view-vpn.js +13 -13
- package/dist_ts_web/router.d.ts +1 -1
- package/dist_ts_web/router.js +2 -2
- package/package.json +3 -3
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.dcrouter.ts +33 -50
- package/ts/config/classes.reference-resolver.ts +34 -34
- package/ts/config/classes.route-config-manager.ts +21 -13
- package/ts/config/classes.target-profile-manager.ts +348 -0
- package/ts/config/index.ts +2 -1
- package/ts/db/documents/{classes.security-profile.doc.ts → classes.source-profile.doc.ts} +7 -7
- package/ts/db/documents/classes.target-profile.doc.ts +52 -0
- package/ts/db/documents/classes.vpn-client.doc.ts +1 -1
- package/ts/db/documents/index.ts +2 -1
- package/ts/opsserver/classes.opsserver.ts +4 -2
- package/ts/opsserver/handlers/index.ts +2 -1
- package/ts/opsserver/handlers/{security-profile.handler.ts → source-profile.handler.ts} +25 -25
- package/ts/opsserver/handlers/target-profile.handler.ts +155 -0
- package/ts/opsserver/handlers/vpn.handler.ts +4 -4
- package/ts/vpn/classes.vpn-manager.ts +14 -38
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/appstate.ts +180 -17
- package/ts_web/elements/index.ts +2 -1
- package/ts_web/elements/ops-dashboard.ts +9 -3
- package/ts_web/elements/ops-view-routes.ts +118 -9
- package/ts_web/elements/{ops-view-securityprofiles.ts → ops-view-sourceprofiles.ts} +13 -13
- package/ts_web/elements/ops-view-targetprofiles.ts +379 -0
- package/ts_web/elements/ops-view-vpn.ts +12 -12
- package/ts_web/router.ts +1 -1
- 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
|
-
'
|
|
331
|
-
? html`${client.
|
|
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=${'
|
|
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
|
|
387
|
-
? data.
|
|
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
|
-
|
|
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">
|
|
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
|
|
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=${'
|
|
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
|
|
694
|
-
? data.
|
|
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
|
-
|
|
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', '
|
|
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=
|