@serve.zone/dcrouter 12.0.0 → 12.2.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 +1179 -1004
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.dcrouter.d.ts +17 -1
- package/dist_ts/classes.dcrouter.js +16 -3
- package/dist_ts/config/classes.db-seeder.d.ts +25 -0
- package/dist_ts/config/classes.db-seeder.js +69 -0
- package/dist_ts/config/classes.reference-resolver.d.ts +80 -0
- package/dist_ts/config/classes.reference-resolver.js +482 -0
- package/dist_ts/config/classes.route-config-manager.d.ts +13 -3
- package/dist_ts/config/classes.route-config-manager.js +53 -3
- package/dist_ts/config/index.d.ts +2 -0
- package/dist_ts/config/index.js +3 -1
- package/dist_ts/db/documents/classes.network-target.doc.d.ts +15 -0
- package/dist_ts/db/documents/classes.network-target.doc.js +118 -0
- package/dist_ts/db/documents/classes.security-profile.doc.d.ts +16 -0
- package/dist_ts/db/documents/classes.security-profile.doc.js +118 -0
- package/dist_ts/db/documents/classes.stored-route.doc.d.ts +2 -0
- package/dist_ts/db/documents/classes.stored-route.doc.js +8 -2
- package/dist_ts/db/documents/classes.vpn-client.doc.d.ts +8 -0
- package/dist_ts/db/documents/classes.vpn-client.doc.js +50 -2
- package/dist_ts/db/documents/index.d.ts +2 -0
- package/dist_ts/db/documents/index.js +3 -1
- package/dist_ts/opsserver/classes.opsserver.d.ts +2 -0
- package/dist_ts/opsserver/classes.opsserver.js +5 -1
- package/dist_ts/opsserver/handlers/index.d.ts +2 -0
- package/dist_ts/opsserver/handlers/index.js +3 -1
- package/dist_ts/opsserver/handlers/network-target.handler.d.ts +10 -0
- package/dist_ts/opsserver/handlers/network-target.handler.js +117 -0
- package/dist_ts/opsserver/handlers/route-management.handler.js +3 -2
- package/dist_ts/opsserver/handlers/security-profile.handler.d.ts +10 -0
- package/dist_ts/opsserver/handlers/security-profile.handler.js +119 -0
- package/dist_ts/opsserver/handlers/vpn.handler.js +35 -1
- package/dist_ts/vpn/classes.vpn-manager.d.ts +33 -0
- package/dist_ts/vpn/classes.vpn-manager.js +122 -7
- package/dist_ts_interfaces/data/route-management.d.ts +48 -1
- package/dist_ts_interfaces/data/vpn.d.ts +8 -0
- package/dist_ts_interfaces/requests/index.d.ts +2 -0
- package/dist_ts_interfaces/requests/index.js +3 -1
- package/dist_ts_interfaces/requests/network-targets.d.ts +102 -0
- package/dist_ts_interfaces/requests/network-targets.js +2 -0
- package/dist_ts_interfaces/requests/route-management.d.ts +3 -1
- package/dist_ts_interfaces/requests/security-profiles.d.ts +102 -0
- package/dist_ts_interfaces/requests/security-profiles.js +2 -0
- package/dist_ts_interfaces/requests/vpn.d.ts +16 -0
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/dist_ts_web/appstate.d.ts +59 -0
- package/dist_ts_web/appstate.js +192 -2
- package/dist_ts_web/elements/index.d.ts +2 -0
- package/dist_ts_web/elements/index.js +3 -1
- package/dist_ts_web/elements/ops-dashboard.js +13 -1
- package/dist_ts_web/elements/ops-view-networktargets.d.ts +17 -0
- package/dist_ts_web/elements/ops-view-networktargets.js +246 -0
- package/dist_ts_web/elements/ops-view-securityprofiles.d.ts +17 -0
- package/dist_ts_web/elements/ops-view-securityprofiles.js +275 -0
- package/dist_ts_web/elements/ops-view-vpn.js +155 -3
- 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 +35 -1
- package/ts/config/classes.db-seeder.ts +95 -0
- package/ts/config/classes.reference-resolver.ts +576 -0
- package/ts/config/classes.route-config-manager.ts +64 -1
- package/ts/config/index.ts +3 -1
- package/ts/db/documents/classes.network-target.doc.ts +48 -0
- package/ts/db/documents/classes.security-profile.doc.ts +49 -0
- package/ts/db/documents/classes.stored-route.doc.ts +4 -0
- package/ts/db/documents/classes.vpn-client.doc.ts +24 -0
- package/ts/db/documents/index.ts +2 -0
- package/ts/opsserver/classes.opsserver.ts +4 -0
- package/ts/opsserver/handlers/index.ts +3 -1
- package/ts/opsserver/handlers/network-target.handler.ts +167 -0
- package/ts/opsserver/handlers/route-management.handler.ts +2 -1
- package/ts/opsserver/handlers/security-profile.handler.ts +169 -0
- package/ts/opsserver/handlers/vpn.handler.ts +37 -0
- package/ts/vpn/classes.vpn-manager.ts +143 -6
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/appstate.ts +275 -1
- package/ts_web/elements/index.ts +2 -0
- package/ts_web/elements/ops-dashboard.ts +12 -0
- package/ts_web/elements/ops-view-networktargets.ts +214 -0
- package/ts_web/elements/ops-view-securityprofiles.ts +242 -0
- package/ts_web/elements/ops-view-vpn.ts +153 -2
- package/ts_web/router.ts +1 -1
|
@@ -0,0 +1,242 @@
|
|
|
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 appstate from '../appstate.js';
|
|
11
|
+
import * as interfaces from '../../dist_ts_interfaces/index.js';
|
|
12
|
+
import { viewHostCss } from './shared/css.js';
|
|
13
|
+
import { type IStatsTile } from '@design.estate/dees-catalog';
|
|
14
|
+
|
|
15
|
+
declare global {
|
|
16
|
+
interface HTMLElementTagNameMap {
|
|
17
|
+
'ops-view-securityprofiles': OpsViewSecurityProfiles;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@customElement('ops-view-securityprofiles')
|
|
22
|
+
export class OpsViewSecurityProfiles extends DeesElement {
|
|
23
|
+
@state()
|
|
24
|
+
accessor profilesState: appstate.IProfilesTargetsState = appstate.profilesTargetsStatePart.getState()!;
|
|
25
|
+
|
|
26
|
+
constructor() {
|
|
27
|
+
super();
|
|
28
|
+
const sub = appstate.profilesTargetsStatePart.select().subscribe((newState) => {
|
|
29
|
+
this.profilesState = newState;
|
|
30
|
+
});
|
|
31
|
+
this.rxSubscriptions.push(sub);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async connectedCallback() {
|
|
35
|
+
await super.connectedCallback();
|
|
36
|
+
await appstate.profilesTargetsStatePart.dispatchAction(appstate.fetchProfilesAndTargetsAction, null);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
public static styles = [
|
|
40
|
+
cssManager.defaultStyles,
|
|
41
|
+
viewHostCss,
|
|
42
|
+
css`
|
|
43
|
+
.profilesContainer {
|
|
44
|
+
display: flex;
|
|
45
|
+
flex-direction: column;
|
|
46
|
+
gap: 24px;
|
|
47
|
+
}
|
|
48
|
+
`,
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
public render(): TemplateResult {
|
|
52
|
+
const profiles = this.profilesState.profiles;
|
|
53
|
+
|
|
54
|
+
const statsTiles: IStatsTile[] = [
|
|
55
|
+
{
|
|
56
|
+
id: 'totalProfiles',
|
|
57
|
+
title: 'Total Profiles',
|
|
58
|
+
type: 'number',
|
|
59
|
+
value: profiles.length,
|
|
60
|
+
icon: 'lucide:shieldCheck',
|
|
61
|
+
description: 'Reusable security profiles',
|
|
62
|
+
color: '#3b82f6',
|
|
63
|
+
},
|
|
64
|
+
];
|
|
65
|
+
|
|
66
|
+
return html`
|
|
67
|
+
<div class="profilesContainer">
|
|
68
|
+
<dees-statsgrid .tiles=${statsTiles}></dees-statsgrid>
|
|
69
|
+
<dees-table
|
|
70
|
+
.heading=${'Security Profiles'}
|
|
71
|
+
.data=${profiles}
|
|
72
|
+
.displayFunction=${(profile: interfaces.data.ISecurityProfile) => ({
|
|
73
|
+
Name: profile.name,
|
|
74
|
+
Description: profile.description || '-',
|
|
75
|
+
'IP Allow List': (profile.security?.ipAllowList || []).join(', ') || '-',
|
|
76
|
+
'IP Block List': (profile.security?.ipBlockList || []).join(', ') || '-',
|
|
77
|
+
'Max Connections': profile.security?.maxConnections ?? '-',
|
|
78
|
+
'Rate Limit': profile.security?.rateLimit?.enabled ? `${profile.security.rateLimit.maxRequests}/${profile.security.rateLimit.window}s` : '-',
|
|
79
|
+
Extends: (profile.extendsProfiles || []).length > 0
|
|
80
|
+
? profile.extendsProfiles!.map(id => {
|
|
81
|
+
const p = profiles.find(pp => pp.id === id);
|
|
82
|
+
return p ? p.name : id.slice(0, 8);
|
|
83
|
+
}).join(', ')
|
|
84
|
+
: '-',
|
|
85
|
+
})}
|
|
86
|
+
.dataActions=${[
|
|
87
|
+
{
|
|
88
|
+
name: 'Create Profile',
|
|
89
|
+
iconName: 'lucide:plus',
|
|
90
|
+
type: ['header' as const],
|
|
91
|
+
action: async (_: any, table: any) => {
|
|
92
|
+
await this.showCreateProfileDialog(table);
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
name: 'Refresh',
|
|
97
|
+
iconName: 'lucide:rotateCw',
|
|
98
|
+
type: ['header' as const],
|
|
99
|
+
action: async () => {
|
|
100
|
+
await appstate.profilesTargetsStatePart.dispatchAction(appstate.fetchProfilesAndTargetsAction, null);
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
name: 'Edit',
|
|
105
|
+
iconName: 'lucide:pencil',
|
|
106
|
+
type: ['contextmenu' as const],
|
|
107
|
+
action: async (profile: interfaces.data.ISecurityProfile, table: any) => {
|
|
108
|
+
await this.showEditProfileDialog(profile, table);
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
name: 'Delete',
|
|
113
|
+
iconName: 'lucide:trash2',
|
|
114
|
+
type: ['contextmenu' as const],
|
|
115
|
+
action: async (profile: interfaces.data.ISecurityProfile) => {
|
|
116
|
+
await this.deleteProfile(profile);
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
]}
|
|
120
|
+
></dees-table>
|
|
121
|
+
</div>
|
|
122
|
+
`;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
private async showCreateProfileDialog(table: any) {
|
|
126
|
+
const { DeesModal } = await import('@design.estate/dees-catalog');
|
|
127
|
+
DeesModal.createAndShow({
|
|
128
|
+
heading: 'Create Security Profile',
|
|
129
|
+
content: html`
|
|
130
|
+
<dees-form>
|
|
131
|
+
<dees-input-text .key=${'name'} .label=${'Name'} .required=${true}></dees-input-text>
|
|
132
|
+
<dees-input-text .key=${'description'} .label=${'Description'}></dees-input-text>
|
|
133
|
+
<dees-input-text .key=${'ipAllowList'} .label=${'IP Allow List (comma-separated)'}></dees-input-text>
|
|
134
|
+
<dees-input-text .key=${'ipBlockList'} .label=${'IP Block List (comma-separated)'}></dees-input-text>
|
|
135
|
+
<dees-input-text .key=${'maxConnections'} .label=${'Max Connections'}></dees-input-text>
|
|
136
|
+
</dees-form>
|
|
137
|
+
`,
|
|
138
|
+
menuOptions: [
|
|
139
|
+
{
|
|
140
|
+
name: 'Create',
|
|
141
|
+
action: async (modalArg: any) => {
|
|
142
|
+
const form = modalArg.shadowRoot!.querySelector('dees-form');
|
|
143
|
+
const data = await form.collectFormData();
|
|
144
|
+
const ipAllowList = data.ipAllowList
|
|
145
|
+
? String(data.ipAllowList).split(',').map((s: string) => s.trim()).filter(Boolean)
|
|
146
|
+
: undefined;
|
|
147
|
+
const ipBlockList = data.ipBlockList
|
|
148
|
+
? String(data.ipBlockList).split(',').map((s: string) => s.trim()).filter(Boolean)
|
|
149
|
+
: undefined;
|
|
150
|
+
const maxConnections = data.maxConnections ? parseInt(String(data.maxConnections)) : undefined;
|
|
151
|
+
|
|
152
|
+
await appstate.profilesTargetsStatePart.dispatchAction(appstate.createProfileAction, {
|
|
153
|
+
name: String(data.name),
|
|
154
|
+
description: data.description ? String(data.description) : undefined,
|
|
155
|
+
security: {
|
|
156
|
+
...(ipAllowList ? { ipAllowList } : {}),
|
|
157
|
+
...(ipBlockList ? { ipBlockList } : {}),
|
|
158
|
+
...(maxConnections ? { maxConnections } : {}),
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
modalArg.destroy();
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
{ name: 'Cancel', action: async (modalArg: any) => modalArg.destroy() },
|
|
165
|
+
],
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
private async showEditProfileDialog(profile: interfaces.data.ISecurityProfile, table: any) {
|
|
170
|
+
const { DeesModal } = await import('@design.estate/dees-catalog');
|
|
171
|
+
DeesModal.createAndShow({
|
|
172
|
+
heading: `Edit Profile: ${profile.name}`,
|
|
173
|
+
content: html`
|
|
174
|
+
<dees-form>
|
|
175
|
+
<dees-input-text .key=${'name'} .label=${'Name'} .value=${profile.name}></dees-input-text>
|
|
176
|
+
<dees-input-text .key=${'description'} .label=${'Description'} .value=${profile.description || ''}></dees-input-text>
|
|
177
|
+
<dees-input-text .key=${'ipAllowList'} .label=${'IP Allow List (comma-separated)'} .value=${(profile.security?.ipAllowList || []).join(', ')}></dees-input-text>
|
|
178
|
+
<dees-input-text .key=${'ipBlockList'} .label=${'IP Block List (comma-separated)'} .value=${(profile.security?.ipBlockList || []).join(', ')}></dees-input-text>
|
|
179
|
+
<dees-input-text .key=${'maxConnections'} .label=${'Max Connections'} .value=${String(profile.security?.maxConnections || '')}></dees-input-text>
|
|
180
|
+
</dees-form>
|
|
181
|
+
`,
|
|
182
|
+
menuOptions: [
|
|
183
|
+
{
|
|
184
|
+
name: 'Save',
|
|
185
|
+
action: async (modalArg: any) => {
|
|
186
|
+
const form = modalArg.shadowRoot!.querySelector('dees-form');
|
|
187
|
+
const data = await form.collectFormData();
|
|
188
|
+
const ipAllowList = data.ipAllowList
|
|
189
|
+
? String(data.ipAllowList).split(',').map((s: string) => s.trim()).filter(Boolean)
|
|
190
|
+
: [];
|
|
191
|
+
const ipBlockList = data.ipBlockList
|
|
192
|
+
? String(data.ipBlockList).split(',').map((s: string) => s.trim()).filter(Boolean)
|
|
193
|
+
: [];
|
|
194
|
+
const maxConnections = data.maxConnections ? parseInt(String(data.maxConnections)) : undefined;
|
|
195
|
+
|
|
196
|
+
await appstate.profilesTargetsStatePart.dispatchAction(appstate.updateProfileAction, {
|
|
197
|
+
id: profile.id,
|
|
198
|
+
name: String(data.name),
|
|
199
|
+
description: data.description ? String(data.description) : undefined,
|
|
200
|
+
security: {
|
|
201
|
+
ipAllowList,
|
|
202
|
+
ipBlockList,
|
|
203
|
+
...(maxConnections ? { maxConnections } : {}),
|
|
204
|
+
},
|
|
205
|
+
});
|
|
206
|
+
modalArg.destroy();
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
{ name: 'Cancel', action: async (modalArg: any) => modalArg.destroy() },
|
|
210
|
+
],
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
private async deleteProfile(profile: interfaces.data.ISecurityProfile) {
|
|
215
|
+
await appstate.profilesTargetsStatePart.dispatchAction(appstate.deleteProfileAction, {
|
|
216
|
+
id: profile.id,
|
|
217
|
+
force: false,
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
const currentState = appstate.profilesTargetsStatePart.getState()!;
|
|
221
|
+
if (currentState.error?.includes('in use')) {
|
|
222
|
+
const { DeesModal } = await import('@design.estate/dees-catalog');
|
|
223
|
+
DeesModal.createAndShow({
|
|
224
|
+
heading: 'Profile In Use',
|
|
225
|
+
content: html`<p>${currentState.error} Force delete?</p>`,
|
|
226
|
+
menuOptions: [
|
|
227
|
+
{
|
|
228
|
+
name: 'Force Delete',
|
|
229
|
+
action: async (modalArg: any) => {
|
|
230
|
+
await appstate.profilesTargetsStatePart.dispatchAction(appstate.deleteProfileAction, {
|
|
231
|
+
id: profile.id,
|
|
232
|
+
force: true,
|
|
233
|
+
});
|
|
234
|
+
modalArg.destroy();
|
|
235
|
+
},
|
|
236
|
+
},
|
|
237
|
+
{ name: 'Cancel', action: async (modalArg: any) => modalArg.destroy() },
|
|
238
|
+
],
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
@@ -13,6 +13,31 @@ import * as interfaces from '../../dist_ts_interfaces/index.js';
|
|
|
13
13
|
import { viewHostCss } from './shared/css.js';
|
|
14
14
|
import { type IStatsTile } from '@design.estate/dees-catalog';
|
|
15
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Toggle form field visibility based on checkbox states.
|
|
18
|
+
* Used in Create and Edit VPN client dialogs.
|
|
19
|
+
*/
|
|
20
|
+
function setupFormVisibility(formEl: any) {
|
|
21
|
+
const show = 'flex'; // match dees-form's flex layout
|
|
22
|
+
const updateVisibility = async () => {
|
|
23
|
+
const data = await formEl.collectFormData();
|
|
24
|
+
const contentEl = formEl.closest('.content') || formEl.parentElement;
|
|
25
|
+
if (!contentEl) return;
|
|
26
|
+
const hostIpGroup = contentEl.querySelector('.hostIpGroup') as HTMLElement;
|
|
27
|
+
const hostIpDetails = contentEl.querySelector('.hostIpDetails') as HTMLElement;
|
|
28
|
+
const staticIpGroup = contentEl.querySelector('.staticIpGroup') as HTMLElement;
|
|
29
|
+
const vlanIdGroup = contentEl.querySelector('.vlanIdGroup') as HTMLElement;
|
|
30
|
+
const aclGroup = contentEl.querySelector('.aclGroup') as HTMLElement;
|
|
31
|
+
if (hostIpGroup) hostIpGroup.style.display = data.forceDestinationSmartproxy ? 'none' : show;
|
|
32
|
+
if (hostIpDetails) hostIpDetails.style.display = data.useHostIp ? show : 'none';
|
|
33
|
+
if (staticIpGroup) staticIpGroup.style.display = data.useDhcp ? 'none' : show;
|
|
34
|
+
if (vlanIdGroup) vlanIdGroup.style.display = data.forceVlan ? show : 'none';
|
|
35
|
+
if (aclGroup) aclGroup.style.display = data.allowAdditionalAcls ? show : 'none';
|
|
36
|
+
};
|
|
37
|
+
formEl.changeSubject.subscribe(() => updateVisibility());
|
|
38
|
+
updateVisibility();
|
|
39
|
+
}
|
|
40
|
+
|
|
16
41
|
declare global {
|
|
17
42
|
interface HTMLElementTagNameMap {
|
|
18
43
|
'ops-view-vpn': OpsViewVpn;
|
|
@@ -289,9 +314,18 @@ export class OpsViewVpn extends DeesElement {
|
|
|
289
314
|
} else {
|
|
290
315
|
statusHtml = html`<span class="statusBadge enabled" style="background: ${cssManager.bdTheme('#eff6ff', '#172554')}; color: ${cssManager.bdTheme('#1e40af', '#60a5fa')};">offline</span>`;
|
|
291
316
|
}
|
|
317
|
+
let routingHtml;
|
|
318
|
+
if (client.forceDestinationSmartproxy !== false) {
|
|
319
|
+
routingHtml = html`<span class="statusBadge enabled">SmartProxy</span>`;
|
|
320
|
+
} else if (client.useHostIp) {
|
|
321
|
+
routingHtml = html`<span class="statusBadge" style="background: ${cssManager.bdTheme('#f3e8ff', '#3b0764')}; color: ${cssManager.bdTheme('#7c3aed', '#c084fc')};">Host IP</span>`;
|
|
322
|
+
} else {
|
|
323
|
+
routingHtml = html`<span class="statusBadge" style="background: ${cssManager.bdTheme('#eff6ff', '#172554')}; color: ${cssManager.bdTheme('#1e40af', '#60a5fa')};">Direct</span>`;
|
|
324
|
+
}
|
|
292
325
|
return {
|
|
293
326
|
'Client ID': client.clientId,
|
|
294
327
|
'Status': statusHtml,
|
|
328
|
+
'Routing': routingHtml,
|
|
295
329
|
'VPN IP': client.assignedIp || '-',
|
|
296
330
|
'Tags': client.serverDefinedClientTags?.length
|
|
297
331
|
? html`${client.serverDefinedClientTags.map(t => html`<span class="tagBadge">${t}</span>`)}`
|
|
@@ -307,13 +341,32 @@ export class OpsViewVpn extends DeesElement {
|
|
|
307
341
|
type: ['header'],
|
|
308
342
|
actionFunc: async () => {
|
|
309
343
|
const { DeesModal } = await import('@design.estate/dees-catalog');
|
|
310
|
-
await DeesModal.createAndShow({
|
|
344
|
+
const createModal = await DeesModal.createAndShow({
|
|
311
345
|
heading: 'Create VPN Client',
|
|
312
346
|
content: html`
|
|
313
347
|
<dees-form>
|
|
314
348
|
<dees-input-text .key=${'clientId'} .label=${'Client ID'} .required=${true}></dees-input-text>
|
|
315
349
|
<dees-input-text .key=${'description'} .label=${'Description'}></dees-input-text>
|
|
316
350
|
<dees-input-text .key=${'tags'} .label=${'Server-Defined Tags (comma-separated)'}></dees-input-text>
|
|
351
|
+
<dees-input-checkbox .key=${'forceDestinationSmartproxy'} .label=${'Force traffic through SmartProxy'} .value=${true}></dees-input-checkbox>
|
|
352
|
+
<div class="hostIpGroup" style="display: none; flex-direction: column; gap: 16px;">
|
|
353
|
+
<dees-input-checkbox .key=${'useHostIp'} .label=${'Get Host IP'} .value=${false}></dees-input-checkbox>
|
|
354
|
+
<div class="hostIpDetails" style="display: none; flex-direction: column; gap: 16px;">
|
|
355
|
+
<dees-input-checkbox .key=${'useDhcp'} .label=${'Get IP through DHCP'} .value=${false}></dees-input-checkbox>
|
|
356
|
+
<div class="staticIpGroup" style="display: flex; flex-direction: column; gap: 16px;">
|
|
357
|
+
<dees-input-text .key=${'staticIp'} .label=${'Static IP'}></dees-input-text>
|
|
358
|
+
</div>
|
|
359
|
+
<dees-input-checkbox .key=${'forceVlan'} .label=${'Force VLAN'} .value=${false}></dees-input-checkbox>
|
|
360
|
+
<div class="vlanIdGroup" style="display: none; flex-direction: column; gap: 16px;">
|
|
361
|
+
<dees-input-text .key=${'vlanId'} .label=${'VLAN ID'}></dees-input-text>
|
|
362
|
+
</div>
|
|
363
|
+
</div>
|
|
364
|
+
</div>
|
|
365
|
+
<dees-input-checkbox .key=${'allowAdditionalAcls'} .label=${'Allow additional ACLs'} .value=${false}></dees-input-checkbox>
|
|
366
|
+
<div class="aclGroup" style="display: none; flex-direction: column; gap: 16px;">
|
|
367
|
+
<dees-input-text .key=${'destinationAllowList'} .label=${'Destination Allow List (comma-separated IPs/CIDRs)'}></dees-input-text>
|
|
368
|
+
<dees-input-text .key=${'destinationBlockList'} .label=${'Destination Block List (comma-separated IPs/CIDRs)'}></dees-input-text>
|
|
369
|
+
</div>
|
|
317
370
|
</dees-form>
|
|
318
371
|
`,
|
|
319
372
|
menuOptions: [
|
|
@@ -333,16 +386,47 @@ export class OpsViewVpn extends DeesElement {
|
|
|
333
386
|
const serverDefinedClientTags = data.tags
|
|
334
387
|
? data.tags.split(',').map((t: string) => t.trim()).filter(Boolean)
|
|
335
388
|
: undefined;
|
|
389
|
+
|
|
390
|
+
// Apply conditional logic based on checkbox states
|
|
391
|
+
const forceSmartproxy = data.forceDestinationSmartproxy ?? true;
|
|
392
|
+
const useHostIp = !forceSmartproxy && (data.useHostIp ?? false);
|
|
393
|
+
const useDhcp = useHostIp && (data.useDhcp ?? false);
|
|
394
|
+
const staticIp = useHostIp && !useDhcp && data.staticIp ? data.staticIp : undefined;
|
|
395
|
+
const forceVlan = useHostIp && (data.forceVlan ?? false);
|
|
396
|
+
const vlanId = forceVlan && data.vlanId ? parseInt(data.vlanId, 10) : undefined;
|
|
397
|
+
|
|
398
|
+
const allowAcls = data.allowAdditionalAcls ?? false;
|
|
399
|
+
const destinationAllowList = allowAcls && data.destinationAllowList
|
|
400
|
+
? data.destinationAllowList.split(',').map((s: string) => s.trim()).filter(Boolean)
|
|
401
|
+
: undefined;
|
|
402
|
+
const destinationBlockList = allowAcls && data.destinationBlockList
|
|
403
|
+
? data.destinationBlockList.split(',').map((s: string) => s.trim()).filter(Boolean)
|
|
404
|
+
: undefined;
|
|
405
|
+
|
|
336
406
|
await appstate.vpnStatePart.dispatchAction(appstate.createVpnClientAction, {
|
|
337
407
|
clientId: data.clientId,
|
|
338
408
|
description: data.description || undefined,
|
|
339
409
|
serverDefinedClientTags,
|
|
410
|
+
forceDestinationSmartproxy: forceSmartproxy,
|
|
411
|
+
useHostIp: useHostIp || undefined,
|
|
412
|
+
useDhcp: useDhcp || undefined,
|
|
413
|
+
staticIp,
|
|
414
|
+
forceVlan: forceVlan || undefined,
|
|
415
|
+
vlanId,
|
|
416
|
+
destinationAllowList,
|
|
417
|
+
destinationBlockList,
|
|
340
418
|
});
|
|
341
419
|
await modalArg.destroy();
|
|
342
420
|
},
|
|
343
421
|
},
|
|
344
422
|
],
|
|
345
423
|
});
|
|
424
|
+
// Setup conditional form visibility after modal renders
|
|
425
|
+
const createForm = createModal?.shadowRoot?.querySelector('.content')?.querySelector('dees-form') as any;
|
|
426
|
+
if (createForm) {
|
|
427
|
+
await createForm.updateComplete;
|
|
428
|
+
setupFormVisibility(createForm);
|
|
429
|
+
}
|
|
346
430
|
},
|
|
347
431
|
},
|
|
348
432
|
{
|
|
@@ -396,6 +480,13 @@ export class OpsViewVpn extends DeesElement {
|
|
|
396
480
|
` : ''}
|
|
397
481
|
<div class="infoItem"><span class="infoLabel">Description</span><span class="infoValue">${client.description || '-'}</span></div>
|
|
398
482
|
<div class="infoItem"><span class="infoLabel">Tags</span><span class="infoValue">${client.serverDefinedClientTags?.join(', ') || '-'}</span></div>
|
|
483
|
+
<div class="infoItem"><span class="infoLabel">Routing</span><span class="infoValue">${client.forceDestinationSmartproxy !== false ? 'SmartProxy' : client.useHostIp ? 'Host IP' : 'Direct'}</span></div>
|
|
484
|
+
${client.useHostIp ? html`
|
|
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>
|
|
486
|
+
<div class="infoItem"><span class="infoLabel">VLAN</span><span class="infoValue">${client.forceVlan && client.vlanId != null ? `VLAN ${client.vlanId}` : 'No VLAN'}</span></div>
|
|
487
|
+
` : ''}
|
|
488
|
+
<div class="infoItem"><span class="infoLabel">Allow List</span><span class="infoValue">${client.destinationAllowList?.length ? client.destinationAllowList.join(', ') : 'None'}</span></div>
|
|
489
|
+
<div class="infoItem"><span class="infoLabel">Block List</span><span class="infoValue">${client.destinationBlockList?.length ? client.destinationBlockList.join(', ') : 'None'}</span></div>
|
|
399
490
|
<div class="infoItem"><span class="infoLabel">Created</span><span class="infoValue">${new Date(client.createdAt).toLocaleString()}</span></div>
|
|
400
491
|
<div class="infoItem"><span class="infoLabel">Updated</span><span class="infoValue">${new Date(client.updatedAt).toLocaleString()}</span></div>
|
|
401
492
|
</div>
|
|
@@ -553,12 +644,41 @@ export class OpsViewVpn extends DeesElement {
|
|
|
553
644
|
const { DeesModal } = await import('@design.estate/dees-catalog');
|
|
554
645
|
const currentDescription = client.description ?? '';
|
|
555
646
|
const currentTags = client.serverDefinedClientTags?.join(', ') ?? '';
|
|
556
|
-
|
|
647
|
+
const currentForceSmartproxy = client.forceDestinationSmartproxy ?? true;
|
|
648
|
+
const currentUseHostIp = client.useHostIp ?? false;
|
|
649
|
+
const currentUseDhcp = client.useDhcp ?? false;
|
|
650
|
+
const currentStaticIp = client.staticIp ?? '';
|
|
651
|
+
const currentForceVlan = client.forceVlan ?? false;
|
|
652
|
+
const currentVlanId = client.vlanId != null ? String(client.vlanId) : '';
|
|
653
|
+
const currentAllowList = client.destinationAllowList?.join(', ') ?? '';
|
|
654
|
+
const currentBlockList = client.destinationBlockList?.join(', ') ?? '';
|
|
655
|
+
const currentAllowAcls = (client.destinationAllowList?.length ?? 0) > 0
|
|
656
|
+
|| (client.destinationBlockList?.length ?? 0) > 0;
|
|
657
|
+
const editModal = await DeesModal.createAndShow({
|
|
557
658
|
heading: `Edit: ${client.clientId}`,
|
|
558
659
|
content: html`
|
|
559
660
|
<dees-form>
|
|
560
661
|
<dees-input-text .key=${'description'} .label=${'Description'} .value=${currentDescription}></dees-input-text>
|
|
561
662
|
<dees-input-text .key=${'tags'} .label=${'Server-Defined Tags (comma-separated)'} .value=${currentTags}></dees-input-text>
|
|
663
|
+
<dees-input-checkbox .key=${'forceDestinationSmartproxy'} .label=${'Force traffic through SmartProxy'} .value=${currentForceSmartproxy}></dees-input-checkbox>
|
|
664
|
+
<div class="hostIpGroup" style="display: ${currentForceSmartproxy ? 'none' : 'flex'}; flex-direction: column; gap: 16px;">
|
|
665
|
+
<dees-input-checkbox .key=${'useHostIp'} .label=${'Get Host IP'} .value=${currentUseHostIp}></dees-input-checkbox>
|
|
666
|
+
<div class="hostIpDetails" style="display: ${currentUseHostIp ? 'flex' : 'none'}; flex-direction: column; gap: 16px;">
|
|
667
|
+
<dees-input-checkbox .key=${'useDhcp'} .label=${'Get IP through DHCP'} .value=${currentUseDhcp}></dees-input-checkbox>
|
|
668
|
+
<div class="staticIpGroup" style="display: ${currentUseDhcp ? 'none' : 'flex'}; flex-direction: column; gap: 16px;">
|
|
669
|
+
<dees-input-text .key=${'staticIp'} .label=${'Static IP'} .value=${currentStaticIp}></dees-input-text>
|
|
670
|
+
</div>
|
|
671
|
+
<dees-input-checkbox .key=${'forceVlan'} .label=${'Force VLAN'} .value=${currentForceVlan}></dees-input-checkbox>
|
|
672
|
+
<div class="vlanIdGroup" style="display: ${currentForceVlan ? 'flex' : 'none'}; flex-direction: column; gap: 16px;">
|
|
673
|
+
<dees-input-text .key=${'vlanId'} .label=${'VLAN ID'} .value=${currentVlanId}></dees-input-text>
|
|
674
|
+
</div>
|
|
675
|
+
</div>
|
|
676
|
+
</div>
|
|
677
|
+
<dees-input-checkbox .key=${'allowAdditionalAcls'} .label=${'Allow additional ACLs'} .value=${currentAllowAcls}></dees-input-checkbox>
|
|
678
|
+
<div class="aclGroup" style="display: ${currentAllowAcls ? 'flex' : 'none'}; flex-direction: column; gap: 16px;">
|
|
679
|
+
<dees-input-text .key=${'destinationAllowList'} .label=${'Destination Allow List (comma-separated IPs/CIDRs)'} .value=${currentAllowList}></dees-input-text>
|
|
680
|
+
<dees-input-text .key=${'destinationBlockList'} .label=${'Destination Block List (comma-separated IPs/CIDRs)'} .value=${currentBlockList}></dees-input-text>
|
|
681
|
+
</div>
|
|
562
682
|
</dees-form>
|
|
563
683
|
`,
|
|
564
684
|
menuOptions: [
|
|
@@ -573,16 +693,47 @@ export class OpsViewVpn extends DeesElement {
|
|
|
573
693
|
const serverDefinedClientTags = data.tags
|
|
574
694
|
? data.tags.split(',').map((t: string) => t.trim()).filter(Boolean)
|
|
575
695
|
: [];
|
|
696
|
+
|
|
697
|
+
// Apply conditional logic based on checkbox states
|
|
698
|
+
const forceSmartproxy = data.forceDestinationSmartproxy ?? true;
|
|
699
|
+
const useHostIp = !forceSmartproxy && (data.useHostIp ?? false);
|
|
700
|
+
const useDhcp = useHostIp && (data.useDhcp ?? false);
|
|
701
|
+
const staticIp = useHostIp && !useDhcp && data.staticIp ? data.staticIp : undefined;
|
|
702
|
+
const forceVlan = useHostIp && (data.forceVlan ?? false);
|
|
703
|
+
const vlanId = forceVlan && data.vlanId ? parseInt(data.vlanId, 10) : undefined;
|
|
704
|
+
|
|
705
|
+
const allowAcls = data.allowAdditionalAcls ?? false;
|
|
706
|
+
const destinationAllowList = allowAcls && data.destinationAllowList
|
|
707
|
+
? data.destinationAllowList.split(',').map((s: string) => s.trim()).filter(Boolean)
|
|
708
|
+
: [];
|
|
709
|
+
const destinationBlockList = allowAcls && data.destinationBlockList
|
|
710
|
+
? data.destinationBlockList.split(',').map((s: string) => s.trim()).filter(Boolean)
|
|
711
|
+
: [];
|
|
712
|
+
|
|
576
713
|
await appstate.vpnStatePart.dispatchAction(appstate.updateVpnClientAction, {
|
|
577
714
|
clientId: client.clientId,
|
|
578
715
|
description: data.description || undefined,
|
|
579
716
|
serverDefinedClientTags,
|
|
717
|
+
forceDestinationSmartproxy: forceSmartproxy,
|
|
718
|
+
useHostIp: useHostIp || undefined,
|
|
719
|
+
useDhcp: useDhcp || undefined,
|
|
720
|
+
staticIp,
|
|
721
|
+
forceVlan: forceVlan || undefined,
|
|
722
|
+
vlanId,
|
|
723
|
+
destinationAllowList,
|
|
724
|
+
destinationBlockList,
|
|
580
725
|
});
|
|
581
726
|
await modalArg.destroy();
|
|
582
727
|
},
|
|
583
728
|
},
|
|
584
729
|
],
|
|
585
730
|
});
|
|
731
|
+
// Setup conditional form visibility for edit dialog
|
|
732
|
+
const editForm = editModal?.shadowRoot?.querySelector('.content')?.querySelector('dees-form') as any;
|
|
733
|
+
if (editForm) {
|
|
734
|
+
await editForm.updateComplete;
|
|
735
|
+
setupFormVisibility(editForm);
|
|
736
|
+
}
|
|
586
737
|
},
|
|
587
738
|
},
|
|
588
739
|
{
|
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'] as const;
|
|
6
|
+
export const validViews = ['overview', 'network', 'emails', 'logs', 'routes', 'apitokens', 'configuration', 'security', 'certificates', 'remoteingress', 'vpn', 'securityprofiles', 'networktargets'] as const;
|
|
7
7
|
|
|
8
8
|
export type TValidView = typeof validViews[number];
|
|
9
9
|
|