@serve.zone/dcrouter 13.30.0 → 13.32.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 +1042 -1014
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.dcrouter.d.ts +1 -1
- package/dist_ts/opsserver/classes.opsserver.d.ts +4 -2
- package/dist_ts/opsserver/classes.opsserver.js +2 -11
- package/dist_ts/opsserver/handlers/acme-config.handler.js +7 -24
- package/dist_ts/opsserver/handlers/admin.handler.d.ts +12 -0
- package/dist_ts/opsserver/handlers/admin.handler.js +129 -95
- package/dist_ts/opsserver/handlers/api-token.handler.js +28 -2
- package/dist_ts/opsserver/handlers/certificate.handler.js +7 -24
- package/dist_ts/opsserver/handlers/config.handler.js +3 -1
- package/dist_ts/opsserver/handlers/dns-provider.handler.js +7 -24
- package/dist_ts/opsserver/handlers/dns-record.handler.js +7 -24
- package/dist_ts/opsserver/handlers/domain.handler.js +7 -24
- package/dist_ts/opsserver/handlers/email-domain.handler.js +7 -24
- package/dist_ts/opsserver/handlers/email-ops.handler.js +8 -1
- package/dist_ts/opsserver/handlers/logs.handler.js +4 -1
- package/dist_ts/opsserver/handlers/network-target.handler.js +7 -24
- package/dist_ts/opsserver/handlers/radius.handler.js +32 -1
- package/dist_ts/opsserver/handlers/remoteingress.handler.js +24 -1
- package/dist_ts/opsserver/handlers/route-management.handler.js +7 -26
- package/dist_ts/opsserver/handlers/security.handler.js +32 -7
- package/dist_ts/opsserver/handlers/source-profile.handler.js +7 -24
- package/dist_ts/opsserver/handlers/stats.handler.js +8 -1
- package/dist_ts/opsserver/handlers/target-profile.handler.js +7 -24
- package/dist_ts/opsserver/handlers/users.handler.d.ts +1 -1
- package/dist_ts/opsserver/handlers/users.handler.js +35 -4
- package/dist_ts/opsserver/handlers/vpn.handler.js +34 -1
- package/dist_ts/opsserver/handlers/workhoster.handler.js +16 -35
- package/dist_ts/opsserver/helpers/auth.d.ts +21 -0
- package/dist_ts/opsserver/helpers/auth.js +63 -0
- package/dist_ts_interfaces/data/route-management.d.ts +2 -1
- package/dist_ts_interfaces/data/route-management.js +48 -2
- package/dist_ts_interfaces/requests/api-tokens.d.ts +10 -5
- package/dist_ts_interfaces/requests/combined.stats.d.ts +2 -1
- package/dist_ts_interfaces/requests/config.d.ts +2 -1
- package/dist_ts_interfaces/requests/email-ops.d.ts +6 -3
- package/dist_ts_interfaces/requests/logs.d.ts +4 -2
- package/dist_ts_interfaces/requests/radius.d.ts +24 -12
- package/dist_ts_interfaces/requests/remoteingress.d.ts +14 -7
- package/dist_ts_interfaces/requests/security-policy.d.ts +16 -8
- package/dist_ts_interfaces/requests/stats.d.ts +18 -9
- package/dist_ts_interfaces/requests/users.d.ts +39 -2
- package/dist_ts_interfaces/requests/vpn.d.ts +22 -11
- package/dist_ts_interfaces/requests/workhoster.d.ts +10 -5
- package/dist_ts_migrations/index.js +3 -1
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/dist_ts_web/appstate.d.ts +8 -0
- package/dist_ts_web/appstate.js +52 -2
- package/dist_ts_web/elements/access/ops-view-apitokens.js +2 -21
- package/dist_ts_web/elements/access/ops-view-users.d.ts +2 -0
- package/dist_ts_web/elements/access/ops-view-users.js +133 -2
- package/dist_ts_web/elements/network/ops-view-routes.js +11 -1
- package/dist_ts_web/elements/ops-dashboard.js +2 -4
- package/package.json +3 -3
- package/readme.md +1 -1
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.dcrouter.ts +1 -1
- package/ts/opsserver/classes.opsserver.ts +3 -14
- package/ts/opsserver/handlers/acme-config.handler.ts +6 -23
- package/ts/opsserver/handlers/admin.handler.ts +155 -111
- package/ts/opsserver/handlers/api-token.handler.ts +27 -1
- package/ts/opsserver/handlers/certificate.handler.ts +6 -23
- package/ts/opsserver/handlers/config.handler.ts +2 -0
- package/ts/opsserver/handlers/dns-provider.handler.ts +6 -23
- package/ts/opsserver/handlers/dns-record.handler.ts +6 -23
- package/ts/opsserver/handlers/domain.handler.ts +6 -23
- package/ts/opsserver/handlers/email-domain.handler.ts +6 -23
- package/ts/opsserver/handlers/email-ops.handler.ts +7 -0
- package/ts/opsserver/handlers/logs.handler.ts +3 -0
- package/ts/opsserver/handlers/network-target.handler.ts +6 -23
- package/ts/opsserver/handlers/radius.handler.ts +31 -0
- package/ts/opsserver/handlers/remoteingress.handler.ts +23 -0
- package/ts/opsserver/handlers/route-management.handler.ts +6 -25
- package/ts/opsserver/handlers/security.handler.ts +31 -6
- package/ts/opsserver/handlers/source-profile.handler.ts +6 -23
- package/ts/opsserver/handlers/stats.handler.ts +7 -0
- package/ts/opsserver/handlers/target-profile.handler.ts +6 -23
- package/ts/opsserver/handlers/users.handler.ts +46 -3
- package/ts/opsserver/handlers/vpn.handler.ts +33 -0
- package/ts/opsserver/handlers/workhoster.handler.ts +18 -33
- package/ts/opsserver/helpers/auth.ts +91 -0
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/appstate.ts +69 -1
- package/ts_web/elements/access/ops-view-apitokens.ts +1 -20
- package/ts_web/elements/access/ops-view-users.ts +139 -1
- package/ts_web/elements/network/ops-view-routes.ts +9 -0
- package/ts_web/elements/ops-dashboard.ts +1 -3
package/ts_web/appstate.ts
CHANGED
|
@@ -2637,7 +2637,7 @@ export async function createGatewayClientToken(
|
|
|
2637
2637
|
});
|
|
2638
2638
|
}
|
|
2639
2639
|
|
|
2640
|
-
// Users
|
|
2640
|
+
// Users
|
|
2641
2641
|
export const fetchUsersAction = usersStatePart.createAction(async (statePartArg): Promise<IUsersState> => {
|
|
2642
2642
|
const context = getActionContext();
|
|
2643
2643
|
const currentState = statePartArg.getState()!;
|
|
@@ -2666,6 +2666,74 @@ export const fetchUsersAction = usersStatePart.createAction(async (statePartArg)
|
|
|
2666
2666
|
}
|
|
2667
2667
|
});
|
|
2668
2668
|
|
|
2669
|
+
export const createUserAction = usersStatePart.createAction<{
|
|
2670
|
+
email: string;
|
|
2671
|
+
name?: string;
|
|
2672
|
+
role: interfaces.requests.TUserManagementRole;
|
|
2673
|
+
password: string;
|
|
2674
|
+
enableIdpGlobalAuth?: boolean;
|
|
2675
|
+
}>(async (statePartArg, dataArg, actionContext): Promise<IUsersState> => {
|
|
2676
|
+
const context = getActionContext();
|
|
2677
|
+
const currentState = statePartArg.getState()!;
|
|
2678
|
+
if (!context.identity) return currentState;
|
|
2679
|
+
|
|
2680
|
+
try {
|
|
2681
|
+
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
2682
|
+
interfaces.requests.IReq_CreateUser
|
|
2683
|
+
>('/typedrequest', 'createUser');
|
|
2684
|
+
|
|
2685
|
+
const response = await request.fire({
|
|
2686
|
+
identity: context.identity,
|
|
2687
|
+
email: dataArg.email,
|
|
2688
|
+
name: dataArg.name,
|
|
2689
|
+
role: dataArg.role,
|
|
2690
|
+
password: dataArg.password,
|
|
2691
|
+
enableIdpGlobalAuth: dataArg.enableIdpGlobalAuth,
|
|
2692
|
+
});
|
|
2693
|
+
|
|
2694
|
+
if (!response.success) {
|
|
2695
|
+
throw new Error(response.message || 'Failed to create user');
|
|
2696
|
+
}
|
|
2697
|
+
|
|
2698
|
+
return await actionContext!.dispatch(fetchUsersAction, null);
|
|
2699
|
+
} catch (error) {
|
|
2700
|
+
return {
|
|
2701
|
+
...currentState,
|
|
2702
|
+
error: error instanceof Error ? error.message : 'Failed to create user',
|
|
2703
|
+
};
|
|
2704
|
+
}
|
|
2705
|
+
});
|
|
2706
|
+
|
|
2707
|
+
export const deleteUserAction = usersStatePart.createAction<string>(
|
|
2708
|
+
async (statePartArg, userIdArg, actionContext): Promise<IUsersState> => {
|
|
2709
|
+
const context = getActionContext();
|
|
2710
|
+
const currentState = statePartArg.getState()!;
|
|
2711
|
+
if (!context.identity) return currentState;
|
|
2712
|
+
|
|
2713
|
+
try {
|
|
2714
|
+
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
2715
|
+
interfaces.requests.IReq_DeleteUser
|
|
2716
|
+
>('/typedrequest', 'deleteUser');
|
|
2717
|
+
|
|
2718
|
+
const response = await request.fire({
|
|
2719
|
+
identity: context.identity,
|
|
2720
|
+
id: userIdArg,
|
|
2721
|
+
});
|
|
2722
|
+
|
|
2723
|
+
if (!response.success) {
|
|
2724
|
+
throw new Error(response.message || 'Failed to delete user');
|
|
2725
|
+
}
|
|
2726
|
+
|
|
2727
|
+
return await actionContext!.dispatch(fetchUsersAction, null);
|
|
2728
|
+
} catch (error) {
|
|
2729
|
+
return {
|
|
2730
|
+
...currentState,
|
|
2731
|
+
error: error instanceof Error ? error.message : 'Failed to delete user',
|
|
2732
|
+
};
|
|
2733
|
+
}
|
|
2734
|
+
},
|
|
2735
|
+
);
|
|
2736
|
+
|
|
2669
2737
|
export async function createApiToken(
|
|
2670
2738
|
name: string,
|
|
2671
2739
|
scopes: interfaces.data.TApiTokenScope[],
|
|
@@ -200,26 +200,7 @@ export class OpsViewApiTokens extends DeesElement {
|
|
|
200
200
|
private async showCreateTokenDialog() {
|
|
201
201
|
const { DeesModal } = await import('@design.estate/dees-catalog');
|
|
202
202
|
|
|
203
|
-
const allScopes = [
|
|
204
|
-
'*',
|
|
205
|
-
'routes:read',
|
|
206
|
-
'routes:write',
|
|
207
|
-
'config:read',
|
|
208
|
-
'certificates:read',
|
|
209
|
-
'certificates:write',
|
|
210
|
-
'tokens:read',
|
|
211
|
-
'tokens:manage',
|
|
212
|
-
'domains:read',
|
|
213
|
-
'domains:write',
|
|
214
|
-
'dns-records:read',
|
|
215
|
-
'dns-records:write',
|
|
216
|
-
'email-domains:read',
|
|
217
|
-
'email-domains:write',
|
|
218
|
-
'gateway-clients:read',
|
|
219
|
-
'gateway-clients:write',
|
|
220
|
-
'workhosters:read',
|
|
221
|
-
'workhosters:write',
|
|
222
|
-
];
|
|
203
|
+
const allScopes = [...interfaces.data.apiTokenScopes];
|
|
223
204
|
|
|
224
205
|
await DeesModal.createAndShow({
|
|
225
206
|
heading: 'Create API Token',
|
|
@@ -116,12 +116,31 @@ export class OpsViewUsers extends DeesElement {
|
|
|
116
116
|
.showColumnFilters=${true}
|
|
117
117
|
.displayFunction=${(user: appstate.IUser) => ({
|
|
118
118
|
ID: html`<span class="userIdCell">${user.id}</span>`,
|
|
119
|
-
|
|
119
|
+
Email: user.email || user.username,
|
|
120
|
+
Name: user.name || '',
|
|
120
121
|
Role: this.renderRoleBadge(user.role),
|
|
122
|
+
Status: user.status || 'active',
|
|
123
|
+
Auth: (user.authSources || []).join(', ') || 'bootstrap',
|
|
121
124
|
Session: user.id === currentUserId
|
|
122
125
|
? html`<span class="sessionBadge">current</span>`
|
|
123
126
|
: '',
|
|
124
127
|
})}
|
|
128
|
+
.dataActions=${[
|
|
129
|
+
{
|
|
130
|
+
name: 'Create User',
|
|
131
|
+
iconName: 'lucide:userPlus',
|
|
132
|
+
type: ['header'],
|
|
133
|
+
actionFunc: async () => await this.showCreateUserDialog(),
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
name: 'Delete',
|
|
137
|
+
iconName: 'lucide:trash2',
|
|
138
|
+
type: ['inRow', 'contextmenu'] as any,
|
|
139
|
+
actionFunc: async (actionData: any) => {
|
|
140
|
+
await this.showDeleteUserDialog(actionData.item as appstate.IUser);
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
]}
|
|
125
144
|
></dees-table>
|
|
126
145
|
</div>
|
|
127
146
|
`;
|
|
@@ -132,6 +151,125 @@ export class OpsViewUsers extends DeesElement {
|
|
|
132
151
|
return html`<span class="roleBadge ${cls}">${role}</span>`;
|
|
133
152
|
}
|
|
134
153
|
|
|
154
|
+
private async showCreateUserDialog(): Promise<void> {
|
|
155
|
+
const { DeesModal, DeesToast } = await import('@design.estate/dees-catalog');
|
|
156
|
+
|
|
157
|
+
await DeesModal.createAndShow({
|
|
158
|
+
heading: 'Create User',
|
|
159
|
+
content: html`
|
|
160
|
+
<dees-form>
|
|
161
|
+
<dees-input-text .key=${'email'} .label=${'Email'} .required=${true}></dees-input-text>
|
|
162
|
+
<dees-input-text .key=${'name'} .label=${'Display name'}></dees-input-text>
|
|
163
|
+
<dees-input-dropdown
|
|
164
|
+
.key=${'role'}
|
|
165
|
+
.label=${'Role'}
|
|
166
|
+
.options=${[
|
|
167
|
+
{ option: 'User', key: 'user' },
|
|
168
|
+
{ option: 'Admin', key: 'admin' },
|
|
169
|
+
]}
|
|
170
|
+
.selectedOption=${{ option: 'User', key: 'user' }}
|
|
171
|
+
.required=${true}
|
|
172
|
+
></dees-input-dropdown>
|
|
173
|
+
<dees-input-text .key=${'password'} .label=${'Password'} .required=${true} .isPasswordBool=${true}></dees-input-text>
|
|
174
|
+
<dees-input-text .key=${'passwordConfirm'} .label=${'Confirm password'} .required=${true} .isPasswordBool=${true}></dees-input-text>
|
|
175
|
+
<dees-input-checkbox
|
|
176
|
+
.key=${'enableIdpGlobalAuth'}
|
|
177
|
+
.label=${'Allow idp.global login for this email'}
|
|
178
|
+
.description=${'Uses https://idp.global by default; the local dcrouter account and role remain authoritative.'}
|
|
179
|
+
></dees-input-checkbox>
|
|
180
|
+
</dees-form>
|
|
181
|
+
`,
|
|
182
|
+
menuOptions: [
|
|
183
|
+
{
|
|
184
|
+
name: 'Cancel',
|
|
185
|
+
iconName: 'lucide:x',
|
|
186
|
+
action: async (modalArg: any) => await modalArg.destroy(),
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
name: 'Create',
|
|
190
|
+
iconName: 'lucide:userPlus',
|
|
191
|
+
action: async (modalArg: any) => {
|
|
192
|
+
const form = modalArg.shadowRoot?.querySelector('.content')?.querySelector('dees-form') as any;
|
|
193
|
+
if (!form) return;
|
|
194
|
+
const data = await form.collectFormData();
|
|
195
|
+
const email = String(data.email || '').trim();
|
|
196
|
+
const name = String(data.name || '').trim();
|
|
197
|
+
const password = String(data.password || '');
|
|
198
|
+
const passwordConfirm = String(data.passwordConfirm || '');
|
|
199
|
+
const roleValue = String(data.role?.key ?? data.role ?? 'user');
|
|
200
|
+
|
|
201
|
+
if (!email || !password) {
|
|
202
|
+
form.setStatus?.('error', 'Email and password are required.');
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
if (password !== passwordConfirm) {
|
|
206
|
+
form.setStatus?.('error', 'Passwords do not match.');
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
form.setStatus?.('pending', 'Creating user...');
|
|
211
|
+
await appstate.usersStatePart.dispatchAction(appstate.createUserAction, {
|
|
212
|
+
email,
|
|
213
|
+
name,
|
|
214
|
+
role: roleValue === 'admin' ? 'admin' : 'user',
|
|
215
|
+
password,
|
|
216
|
+
enableIdpGlobalAuth: Boolean(data.enableIdpGlobalAuth),
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
const state = appstate.usersStatePart.getState();
|
|
220
|
+
if (state?.error) {
|
|
221
|
+
form.setStatus?.('error', state.error);
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
DeesToast.show({ message: `User created for ${email}`, type: 'success', duration: 3000 });
|
|
226
|
+
await modalArg.destroy();
|
|
227
|
+
},
|
|
228
|
+
},
|
|
229
|
+
],
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
private async showDeleteUserDialog(userArg: appstate.IUser): Promise<void> {
|
|
234
|
+
const { DeesModal, DeesToast } = await import('@design.estate/dees-catalog');
|
|
235
|
+
const currentUserId = this.loginState.identity?.userId;
|
|
236
|
+
if (userArg.id === currentUserId) {
|
|
237
|
+
DeesToast.show({ message: 'You cannot delete the current user.', type: 'error', duration: 4000 });
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
await DeesModal.createAndShow({
|
|
242
|
+
heading: 'Delete User',
|
|
243
|
+
content: html`
|
|
244
|
+
<div style="padding: 8px 0; font-size: 14px; line-height: 1.5;">
|
|
245
|
+
<p>Delete <strong>${userArg.email || userArg.username}</strong>?</p>
|
|
246
|
+
<p style="color: #f59e0b; margin-top: 12px;">This removes the local dcrouter account and cannot be undone.</p>
|
|
247
|
+
</div>
|
|
248
|
+
`,
|
|
249
|
+
menuOptions: [
|
|
250
|
+
{
|
|
251
|
+
name: 'Cancel',
|
|
252
|
+
iconName: 'lucide:x',
|
|
253
|
+
action: async (modalArg: any) => await modalArg.destroy(),
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
name: 'Delete',
|
|
257
|
+
iconName: 'lucide:trash2',
|
|
258
|
+
action: async (modalArg: any) => {
|
|
259
|
+
await appstate.usersStatePart.dispatchAction(appstate.deleteUserAction, userArg.id);
|
|
260
|
+
const state = appstate.usersStatePart.getState();
|
|
261
|
+
if (state?.error) {
|
|
262
|
+
DeesToast.show({ message: state.error, type: 'error', duration: 4000 });
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
DeesToast.show({ message: 'User deleted.', type: 'success', duration: 3000 });
|
|
266
|
+
await modalArg.destroy();
|
|
267
|
+
},
|
|
268
|
+
},
|
|
269
|
+
],
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
|
|
135
273
|
async firstUpdated() {
|
|
136
274
|
if (this.loginState.isLoggedIn) {
|
|
137
275
|
await appstate.usersStatePart.dispatchAction(appstate.fetchUsersAction, null);
|
|
@@ -271,6 +271,7 @@ export class OpsViewRoutes extends DeesElement {
|
|
|
271
271
|
const tags = [...(mr.route.tags || [])];
|
|
272
272
|
tags.push(mr.origin);
|
|
273
273
|
if (!mr.enabled) tags.push('disabled');
|
|
274
|
+
if (mr.route.vpnOnly) tags.push('vpn-only');
|
|
274
275
|
|
|
275
276
|
return {
|
|
276
277
|
...mr.route,
|
|
@@ -360,6 +361,7 @@ export class OpsViewRoutes extends DeesElement {
|
|
|
360
361
|
<div style="color: #ccc; padding: 8px 0;">
|
|
361
362
|
<p>Origin: <strong style="color: #0af;">${merged.origin}</strong></p>
|
|
362
363
|
<p>Status: <strong>${merged.enabled ? 'Enabled' : 'Disabled'}</strong></p>
|
|
364
|
+
${merged.route.vpnOnly ? html`<p>Access: <strong style="color: #22c55e;">VPN only</strong></p>` : ''}
|
|
363
365
|
<p>ID: <code style="color: #888;">${merged.id}</code></p>
|
|
364
366
|
${isSystemManaged ? html`<p>This route is system-managed. Change its source config to modify it directly.</p>` : ''}
|
|
365
367
|
${meta?.sourceProfileName ? html`<p>Source Profile: <strong style="color: #a78bfa;">${meta.sourceProfileName}</strong></p>` : ''}
|
|
@@ -491,6 +493,7 @@ export class OpsViewRoutes extends DeesElement {
|
|
|
491
493
|
? (Array.isArray(firstTarget.host) ? firstTarget.host[0] : firstTarget.host)
|
|
492
494
|
: '';
|
|
493
495
|
const currentTargetPort = typeof firstTarget?.port === 'number' ? String(firstTarget.port) : '';
|
|
496
|
+
const currentVpnOnly = route.vpnOnly === true;
|
|
494
497
|
const currentRemoteIngressEnabled = route.remoteIngress?.enabled === true;
|
|
495
498
|
const currentEdgeFilter = route.remoteIngress?.edgeFilter || [];
|
|
496
499
|
|
|
@@ -518,6 +521,7 @@ export class OpsViewRoutes extends DeesElement {
|
|
|
518
521
|
<dees-input-text .key=${'targetHost'} .label=${'Target Host'} .description=${'Used when no network target is selected'} .value=${currentTargetHost}></dees-input-text>
|
|
519
522
|
<dees-input-text .key=${'targetPort'} .label=${'Target Port'} .description=${'Used when no network target is selected'} .value=${currentTargetPort}></dees-input-text>
|
|
520
523
|
<dees-input-checkbox .key=${'preserveMatchPort'} .label=${'Preserve incoming port'} .value=${currentPreserveMatchPort}></dees-input-checkbox>
|
|
524
|
+
<dees-input-checkbox .key=${'vpnOnly'} .label=${'VPN only'} .description=${'Only VPN clients with matching target profiles can access this route'} .value=${currentVpnOnly}></dees-input-checkbox>
|
|
521
525
|
<dees-input-checkbox .key=${'remoteIngressEnabled'} .label=${'Enable Remote Ingress'} .value=${currentRemoteIngressEnabled}></dees-input-checkbox>
|
|
522
526
|
<div class="remoteIngressGroup" style="display: ${currentRemoteIngressEnabled ? 'flex' : 'none'}; flex-direction: column; gap: 16px;">
|
|
523
527
|
<dees-input-list .key=${'remoteIngressEdgeFilter'} .label=${'Edge Filter'} .description=${'Optional edge IDs or tags. Leave empty to allow all edges.'} .placeholder=${'Add edge ID or tag...'} .value=${currentEdgeFilter}></dees-input-list>
|
|
@@ -570,6 +574,7 @@ export class OpsViewRoutes extends DeesElement {
|
|
|
570
574
|
const remoteIngressEdgeFilter: string[] = Array.isArray(formData.remoteIngressEdgeFilter)
|
|
571
575
|
? formData.remoteIngressEdgeFilter.filter(Boolean)
|
|
572
576
|
: [];
|
|
577
|
+
const vpnOnly = Boolean(formData.vpnOnly);
|
|
573
578
|
|
|
574
579
|
const updatedRoute: any = {
|
|
575
580
|
name: formData.name,
|
|
@@ -586,6 +591,7 @@ export class OpsViewRoutes extends DeesElement {
|
|
|
586
591
|
},
|
|
587
592
|
],
|
|
588
593
|
},
|
|
594
|
+
vpnOnly: vpnOnly ? true : null,
|
|
589
595
|
remoteIngress: remoteIngressEnabled
|
|
590
596
|
? {
|
|
591
597
|
enabled: true,
|
|
@@ -684,6 +690,7 @@ export class OpsViewRoutes extends DeesElement {
|
|
|
684
690
|
<dees-input-text .key=${'targetHost'} .label=${'Target Host'} .description=${'Used when no network target is selected'} .value=${'localhost'}></dees-input-text>
|
|
685
691
|
<dees-input-text .key=${'targetPort'} .label=${'Target Port'} .description=${'Used when no network target is selected'}></dees-input-text>
|
|
686
692
|
<dees-input-checkbox .key=${'preserveMatchPort'} .label=${'Preserve incoming port'} .value=${false}></dees-input-checkbox>
|
|
693
|
+
<dees-input-checkbox .key=${'vpnOnly'} .label=${'VPN only'} .description=${'Only VPN clients with matching target profiles can access this route'} .value=${false}></dees-input-checkbox>
|
|
687
694
|
<dees-input-checkbox .key=${'remoteIngressEnabled'} .label=${'Enable Remote Ingress'} .value=${false}></dees-input-checkbox>
|
|
688
695
|
<div class="remoteIngressGroup" style="display: none; flex-direction: column; gap: 16px;">
|
|
689
696
|
<dees-input-list .key=${'remoteIngressEdgeFilter'} .label=${'Edge Filter'} .description=${'Optional edge IDs or tags. Leave empty to allow all edges.'} .placeholder=${'Add edge ID or tag...'}></dees-input-list>
|
|
@@ -736,6 +743,7 @@ export class OpsViewRoutes extends DeesElement {
|
|
|
736
743
|
const remoteIngressEdgeFilter: string[] = Array.isArray(formData.remoteIngressEdgeFilter)
|
|
737
744
|
? formData.remoteIngressEdgeFilter.filter(Boolean)
|
|
738
745
|
: [];
|
|
746
|
+
const vpnOnly = Boolean(formData.vpnOnly);
|
|
739
747
|
|
|
740
748
|
const route: any = {
|
|
741
749
|
name: formData.name,
|
|
@@ -752,6 +760,7 @@ export class OpsViewRoutes extends DeesElement {
|
|
|
752
760
|
},
|
|
753
761
|
],
|
|
754
762
|
},
|
|
763
|
+
...(vpnOnly ? { vpnOnly: true } : {}),
|
|
755
764
|
...(remoteIngressEnabled
|
|
756
765
|
? {
|
|
757
766
|
remoteIngress: {
|
|
@@ -426,9 +426,7 @@ export class OpsDashboard extends DeesElement {
|
|
|
426
426
|
<dees-input-checkbox
|
|
427
427
|
.key=${'enableIdpGlobalAuth'}
|
|
428
428
|
.label=${'Allow idp.global login for this email'}
|
|
429
|
-
.description=${
|
|
430
|
-
? 'The local account remains authoritative; idp.global only verifies identity.'
|
|
431
|
-
: 'Requires DCROUTER_IDP_GLOBAL_URL before idp.global logins can work.'}
|
|
429
|
+
.description=${'Uses https://idp.global by default; the local dcrouter account and role remain authoritative.'}
|
|
432
430
|
></dees-input-checkbox>
|
|
433
431
|
</dees-form>
|
|
434
432
|
</div>
|