@serve.zone/dcrouter 11.15.0 → 11.17.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 +272 -275
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.dcrouter.d.ts +9 -2
- package/dist_ts/classes.dcrouter.js +10 -16
- package/dist_ts/opsserver/handlers/vpn.handler.js +1 -3
- package/dist_ts/vpn/classes.vpn-manager.d.ts +7 -5
- package/dist_ts/vpn/classes.vpn-manager.js +6 -12
- package/dist_ts_interfaces/data/vpn.d.ts +0 -1
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/dist_ts_web/elements/ops-view-vpn.d.ts +2 -1
- package/dist_ts_web/elements/ops-view-vpn.js +130 -42
- package/package.json +2 -2
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.dcrouter.ts +18 -17
- package/ts/opsserver/handlers/vpn.handler.ts +0 -2
- package/ts/vpn/classes.vpn-manager.ts +12 -14
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/elements/ops-view-vpn.ts +128 -41
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
state,
|
|
8
8
|
cssManager,
|
|
9
9
|
} from '@design.estate/dees-element';
|
|
10
|
+
import * as plugins from '../plugins.js';
|
|
10
11
|
import * as appstate from '../appstate.js';
|
|
11
12
|
import * as interfaces from '../../dist_ts_interfaces/index.js';
|
|
12
13
|
import { viewHostCss } from './shared/css.js';
|
|
@@ -181,13 +182,14 @@ export class OpsViewVpn extends DeesElement {
|
|
|
181
182
|
type: 'text',
|
|
182
183
|
value: status?.running ? 'Running' : 'Stopped',
|
|
183
184
|
icon: 'lucide:server',
|
|
184
|
-
description: status?.running ?
|
|
185
|
+
description: status?.running ? 'Active' : 'VPN server not running',
|
|
185
186
|
color: status?.running ? '#10b981' : '#ef4444',
|
|
186
187
|
},
|
|
187
188
|
];
|
|
188
189
|
|
|
189
190
|
return html`
|
|
190
191
|
<ops-sectionheading>VPN</ops-sectionheading>
|
|
192
|
+
<div class="vpnContainer">
|
|
191
193
|
|
|
192
194
|
${this.vpnState.newClientConfig ? html`
|
|
193
195
|
<div class="configDialog">
|
|
@@ -220,7 +222,7 @@ export class OpsViewVpn extends DeesElement {
|
|
|
220
222
|
</div>
|
|
221
223
|
` : ''}
|
|
222
224
|
|
|
223
|
-
<dees-statsgrid .
|
|
225
|
+
<dees-statsgrid .tiles=${statsTiles}></dees-statsgrid>
|
|
224
226
|
|
|
225
227
|
${status ? html`
|
|
226
228
|
<div class="serverInfo">
|
|
@@ -232,10 +234,6 @@ export class OpsViewVpn extends DeesElement {
|
|
|
232
234
|
<span class="infoLabel">WireGuard Port</span>
|
|
233
235
|
<span class="infoValue">${status.wgListenPort}</span>
|
|
234
236
|
</div>
|
|
235
|
-
<div class="infoItem">
|
|
236
|
-
<span class="infoLabel">Forwarding Mode</span>
|
|
237
|
-
<span class="infoValue">${status.forwardingMode}</span>
|
|
238
|
-
</div>
|
|
239
237
|
${status.serverPublicKeys ? html`
|
|
240
238
|
<div class="infoItem">
|
|
241
239
|
<span class="infoLabel">WG Public Key</span>
|
|
@@ -262,31 +260,149 @@ export class OpsViewVpn extends DeesElement {
|
|
|
262
260
|
'Created': new Date(client.createdAt).toLocaleDateString(),
|
|
263
261
|
})}
|
|
264
262
|
.dataActions=${[
|
|
263
|
+
{
|
|
264
|
+
name: 'Create Client',
|
|
265
|
+
iconName: 'lucide:plus',
|
|
266
|
+
type: ['header'],
|
|
267
|
+
actionFunc: async () => {
|
|
268
|
+
const { DeesModal } = await import('@design.estate/dees-catalog');
|
|
269
|
+
await DeesModal.createAndShow({
|
|
270
|
+
heading: 'Create VPN Client',
|
|
271
|
+
content: html`
|
|
272
|
+
<dees-form>
|
|
273
|
+
<dees-input-text .key=${'clientId'} .label=${'Client ID'} .required=${true}></dees-input-text>
|
|
274
|
+
<dees-input-text .key=${'description'} .label=${'Description'}></dees-input-text>
|
|
275
|
+
<dees-input-text .key=${'tags'} .label=${'Server-Defined Tags (comma-separated)'}></dees-input-text>
|
|
276
|
+
</dees-form>
|
|
277
|
+
`,
|
|
278
|
+
menuOptions: [
|
|
279
|
+
{
|
|
280
|
+
name: 'Cancel',
|
|
281
|
+
iconName: 'lucide:x',
|
|
282
|
+
action: async (modalArg: any) => await modalArg.destroy(),
|
|
283
|
+
},
|
|
284
|
+
{
|
|
285
|
+
name: 'Create',
|
|
286
|
+
iconName: 'lucide:plus',
|
|
287
|
+
action: async (modalArg: any) => {
|
|
288
|
+
const form = modalArg.shadowRoot?.querySelector('.content')?.querySelector('dees-form');
|
|
289
|
+
if (!form) return;
|
|
290
|
+
const data = await form.collectFormData();
|
|
291
|
+
if (!data.clientId) return;
|
|
292
|
+
const serverDefinedClientTags = data.tags
|
|
293
|
+
? data.tags.split(',').map((t: string) => t.trim()).filter(Boolean)
|
|
294
|
+
: undefined;
|
|
295
|
+
await appstate.vpnStatePart.dispatchAction(appstate.createVpnClientAction, {
|
|
296
|
+
clientId: data.clientId,
|
|
297
|
+
description: data.description || undefined,
|
|
298
|
+
serverDefinedClientTags,
|
|
299
|
+
});
|
|
300
|
+
await modalArg.destroy();
|
|
301
|
+
},
|
|
302
|
+
},
|
|
303
|
+
],
|
|
304
|
+
});
|
|
305
|
+
},
|
|
306
|
+
},
|
|
265
307
|
{
|
|
266
308
|
name: 'Toggle',
|
|
267
309
|
iconName: 'lucide:power',
|
|
268
|
-
|
|
310
|
+
type: ['contextmenu', 'inRow'],
|
|
311
|
+
actionFunc: async (client: interfaces.data.IVpnClient) => {
|
|
269
312
|
await appstate.vpnStatePart.dispatchAction(appstate.toggleVpnClientAction, {
|
|
270
313
|
clientId: client.clientId,
|
|
271
314
|
enabled: !client.enabled,
|
|
272
315
|
});
|
|
273
316
|
},
|
|
274
317
|
},
|
|
318
|
+
{
|
|
319
|
+
name: 'Export Config',
|
|
320
|
+
iconName: 'lucide:download',
|
|
321
|
+
type: ['contextmenu', 'inRow'],
|
|
322
|
+
actionFunc: async (client: interfaces.data.IVpnClient) => {
|
|
323
|
+
const { DeesToast } = await import('@design.estate/dees-catalog');
|
|
324
|
+
try {
|
|
325
|
+
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
326
|
+
interfaces.requests.IReq_ExportVpnClientConfig
|
|
327
|
+
>('/typedrequest', 'exportVpnClientConfig');
|
|
328
|
+
const response = await request.fire({
|
|
329
|
+
identity: appstate.loginStatePart.getState()!.identity!,
|
|
330
|
+
clientId: client.clientId,
|
|
331
|
+
format: 'wireguard',
|
|
332
|
+
});
|
|
333
|
+
if (response.success && response.config) {
|
|
334
|
+
const blob = new Blob([response.config], { type: 'text/plain' });
|
|
335
|
+
const url = URL.createObjectURL(blob);
|
|
336
|
+
const a = document.createElement('a');
|
|
337
|
+
a.href = url;
|
|
338
|
+
a.download = `${client.clientId}.conf`;
|
|
339
|
+
a.click();
|
|
340
|
+
URL.revokeObjectURL(url);
|
|
341
|
+
DeesToast.createAndShow({ message: 'Config downloaded', type: 'success', duration: 3000 });
|
|
342
|
+
} else {
|
|
343
|
+
DeesToast.createAndShow({ message: response.message || 'Export failed', type: 'error', duration: 5000 });
|
|
344
|
+
}
|
|
345
|
+
} catch (err: any) {
|
|
346
|
+
DeesToast.createAndShow({ message: err.message || 'Export failed', type: 'error', duration: 5000 });
|
|
347
|
+
}
|
|
348
|
+
},
|
|
349
|
+
},
|
|
350
|
+
{
|
|
351
|
+
name: 'Rotate Keys',
|
|
352
|
+
iconName: 'lucide:rotate-cw',
|
|
353
|
+
type: ['contextmenu'],
|
|
354
|
+
actionFunc: async (client: interfaces.data.IVpnClient) => {
|
|
355
|
+
const { DeesModal, DeesToast } = await import('@design.estate/dees-catalog');
|
|
356
|
+
DeesModal.createAndShow({
|
|
357
|
+
heading: 'Rotate Client Keys',
|
|
358
|
+
content: html`<p>Generate new keys for "${client.clientId}"? The old keys will be invalidated and the client will need the new config to reconnect.</p>`,
|
|
359
|
+
menuOptions: [
|
|
360
|
+
{ name: 'Cancel', iconName: 'lucide:x', action: async (modalArg: any) => await modalArg.destroy() },
|
|
361
|
+
{
|
|
362
|
+
name: 'Rotate',
|
|
363
|
+
iconName: 'lucide:rotate-cw',
|
|
364
|
+
action: async (modalArg: any) => {
|
|
365
|
+
try {
|
|
366
|
+
const request = new plugins.domtools.plugins.typedrequest.TypedRequest<
|
|
367
|
+
interfaces.requests.IReq_RotateVpnClientKey
|
|
368
|
+
>('/typedrequest', 'rotateVpnClientKey');
|
|
369
|
+
const response = await request.fire({
|
|
370
|
+
identity: appstate.loginStatePart.getState()!.identity!,
|
|
371
|
+
clientId: client.clientId,
|
|
372
|
+
});
|
|
373
|
+
if (response.success && response.wireguardConfig) {
|
|
374
|
+
appstate.vpnStatePart.setState({
|
|
375
|
+
...appstate.vpnStatePart.getState()!,
|
|
376
|
+
newClientConfig: response.wireguardConfig,
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
await modalArg.destroy();
|
|
380
|
+
} catch (err: any) {
|
|
381
|
+
DeesToast.createAndShow({ message: err.message || 'Rotate failed', type: 'error', duration: 5000 });
|
|
382
|
+
}
|
|
383
|
+
},
|
|
384
|
+
},
|
|
385
|
+
],
|
|
386
|
+
});
|
|
387
|
+
},
|
|
388
|
+
},
|
|
275
389
|
{
|
|
276
390
|
name: 'Delete',
|
|
277
391
|
iconName: 'lucide:trash2',
|
|
278
|
-
|
|
392
|
+
type: ['contextmenu'],
|
|
393
|
+
actionFunc: async (client: interfaces.data.IVpnClient) => {
|
|
279
394
|
const { DeesModal } = await import('@design.estate/dees-catalog');
|
|
280
395
|
DeesModal.createAndShow({
|
|
281
396
|
heading: 'Delete VPN Client',
|
|
282
397
|
content: html`<p>Are you sure you want to delete client "${client.clientId}"?</p>`,
|
|
283
398
|
menuOptions: [
|
|
284
|
-
{ name: 'Cancel', action: async (
|
|
399
|
+
{ name: 'Cancel', iconName: 'lucide:x', action: async (modalArg: any) => await modalArg.destroy() },
|
|
285
400
|
{
|
|
286
401
|
name: 'Delete',
|
|
287
|
-
|
|
402
|
+
iconName: 'lucide:trash2',
|
|
403
|
+
action: async (modalArg: any) => {
|
|
288
404
|
await appstate.vpnStatePart.dispatchAction(appstate.deleteVpnClientAction, client.clientId);
|
|
289
|
-
|
|
405
|
+
await modalArg.destroy();
|
|
290
406
|
},
|
|
291
407
|
},
|
|
292
408
|
],
|
|
@@ -294,37 +410,8 @@ export class OpsViewVpn extends DeesElement {
|
|
|
294
410
|
},
|
|
295
411
|
},
|
|
296
412
|
]}
|
|
297
|
-
.createNewItem=${async () => {
|
|
298
|
-
const { DeesModal, DeesForm, DeesInputText } = await import('@design.estate/dees-catalog');
|
|
299
|
-
DeesModal.createAndShow({
|
|
300
|
-
heading: 'Create VPN Client',
|
|
301
|
-
content: html`
|
|
302
|
-
<dees-form>
|
|
303
|
-
<dees-input-text id="clientId" .label=${'Client ID'} .key=${'clientId'} required></dees-input-text>
|
|
304
|
-
<dees-input-text id="description" .label=${'Description'} .key=${'description'}></dees-input-text>
|
|
305
|
-
<dees-input-text id="tags" .label=${'Tags (comma-separated)'} .key=${'tags'}></dees-input-text>
|
|
306
|
-
</dees-form>
|
|
307
|
-
`,
|
|
308
|
-
menuOptions: [
|
|
309
|
-
{ name: 'Cancel', action: async (modal: any) => modal.destroy() },
|
|
310
|
-
{
|
|
311
|
-
name: 'Create',
|
|
312
|
-
action: async (modal: any) => {
|
|
313
|
-
const form = modal.shadowRoot!.querySelector('dees-form') as any;
|
|
314
|
-
const data = await form.collectFormData();
|
|
315
|
-
const serverDefinedClientTags = data.tags ? data.tags.split(',').map((t: string) => t.trim()).filter(Boolean) : undefined;
|
|
316
|
-
await appstate.vpnStatePart.dispatchAction(appstate.createVpnClientAction, {
|
|
317
|
-
clientId: data.clientId,
|
|
318
|
-
description: data.description || undefined,
|
|
319
|
-
serverDefinedClientTags,
|
|
320
|
-
});
|
|
321
|
-
modal.destroy();
|
|
322
|
-
},
|
|
323
|
-
},
|
|
324
|
-
],
|
|
325
|
-
});
|
|
326
|
-
}}
|
|
327
413
|
></dees-table>
|
|
414
|
+
</div>
|
|
328
415
|
`;
|
|
329
416
|
}
|
|
330
417
|
}
|