@serve.zone/dcrouter 13.28.0 → 13.29.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/.smartconfig.json +32 -10
  2. package/dist_serve/bundle.js +561 -544
  3. package/dist_ts/00_commitinfo_data.js +1 -1
  4. package/dist_ts/classes.dcrouter.d.ts +7 -0
  5. package/dist_ts/classes.dcrouter.js +12 -2
  6. package/dist_ts/config/classes.route-config-manager.js +8 -7
  7. package/dist_ts/opsserver/classes.opsserver.js +4 -1
  8. package/dist_ts/opsserver/handlers/admin.handler.d.ts +21 -6
  9. package/dist_ts/opsserver/handlers/admin.handler.js +188 -29
  10. package/dist_ts/opsserver/handlers/target-profile.handler.js +3 -1
  11. package/dist_ts/opsserver/handlers/users.handler.js +2 -2
  12. package/dist_ts/plugins.d.ts +2 -0
  13. package/dist_ts/plugins.js +4 -1
  14. package/dist_ts/vpn/classes.vpn-manager.d.ts +2 -0
  15. package/dist_ts/vpn/classes.vpn-manager.js +41 -20
  16. package/dist_ts_interfaces/requests/admin.d.ts +38 -0
  17. package/dist_ts_interfaces/requests/users.d.ts +2 -5
  18. package/dist_ts_web/00_commitinfo_data.js +1 -1
  19. package/dist_ts_web/appstate.d.ts +17 -0
  20. package/dist_ts_web/appstate.js +27 -1
  21. package/dist_ts_web/elements/ops-dashboard.d.ts +4 -0
  22. package/dist_ts_web/elements/ops-dashboard.js +100 -3
  23. package/package.json +15 -22
  24. package/ts/00_commitinfo_data.ts +1 -1
  25. package/ts/classes.dcrouter.ts +20 -1
  26. package/ts/config/classes.route-config-manager.ts +8 -6
  27. package/ts/opsserver/classes.opsserver.ts +3 -0
  28. package/ts/opsserver/handlers/admin.handler.ts +244 -32
  29. package/ts/opsserver/handlers/target-profile.handler.ts +2 -0
  30. package/ts/opsserver/handlers/users.handler.ts +1 -1
  31. package/ts/plugins.ts +7 -0
  32. package/ts/vpn/classes.vpn-manager.ts +56 -25
  33. package/ts_web/00_commitinfo_data.ts +1 -1
  34. package/ts_web/appstate.ts +49 -0
  35. package/ts_web/elements/ops-dashboard.ts +100 -0
@@ -66,6 +66,9 @@ export class OpsDashboard extends DeesElement {
66
66
  isLoggedIn: false,
67
67
  };
68
68
 
69
+ private bootstrapStepper?: any;
70
+ private bootstrapCheckPromise?: Promise<void>;
71
+
69
72
  @state() accessor uiState: appstate.IUiState = {
70
73
  activeView: 'overview',
71
74
  activeSubview: null,
@@ -336,6 +339,7 @@ export class OpsDashboard extends DeesElement {
336
339
  await (simpleLogin as any).switchToSlottedContent();
337
340
  await appstate.statsStatePart.dispatchAction(appstate.fetchAllStatsAction, null);
338
341
  await appstate.configStatePart.dispatchAction(appstate.fetchConfigurationAction, null);
342
+ await this.ensureAdminBootstrap();
339
343
  } else {
340
344
  // Server rejected the JWT — clear state, show login
341
345
  await appstate.loginStatePart.dispatchAction(appstate.logoutAction, null);
@@ -370,10 +374,106 @@ export class OpsDashboard extends DeesElement {
370
374
  await simpleLogin!.switchToSlottedContent();
371
375
  await appstate.statsStatePart.dispatchAction(appstate.fetchAllStatsAction, null);
372
376
  await appstate.configStatePart.dispatchAction(appstate.fetchConfigurationAction, null);
377
+ await this.ensureAdminBootstrap();
373
378
  } else {
374
379
  form!.setStatus('error', 'Login failed!');
375
380
  await domtools.convenience.smartdelay.delayFor(2000);
376
381
  form!.reset();
377
382
  }
378
383
  }
384
+
385
+ private async ensureAdminBootstrap(): Promise<void> {
386
+ if (!this.loginState.identity || this.bootstrapStepper?.isConnected) {
387
+ return;
388
+ }
389
+ if (this.bootstrapCheckPromise) {
390
+ return this.bootstrapCheckPromise;
391
+ }
392
+
393
+ this.bootstrapCheckPromise = (async () => {
394
+ try {
395
+ const status = await appstate.getAdminBootstrapStatus();
396
+ if (status.needsBootstrap) {
397
+ await this.showAdminBootstrapStepper(status);
398
+ }
399
+ } catch (error) {
400
+ console.error('Admin bootstrap status check failed:', error);
401
+ } finally {
402
+ this.bootstrapCheckPromise = undefined;
403
+ }
404
+ })();
405
+
406
+ return this.bootstrapCheckPromise;
407
+ }
408
+
409
+ private async showAdminBootstrapStepper(statusArg: appstate.IAdminBootstrapStatus): Promise<void> {
410
+ const { DeesStepper } = await import('@design.estate/dees-catalog');
411
+ this.bootstrapStepper = await DeesStepper.createAndShow({
412
+ cancelable: false,
413
+ steps: [
414
+ {
415
+ title: 'Create Persisted Admin',
416
+ content: html`
417
+ <div style="display: grid; gap: 16px; color: var(--dees-color-text-secondary); font-size: 14px; line-height: 1.5;">
418
+ <p style="margin: 0;">
419
+ This router is currently using the temporary bootstrap admin. Create the first persisted admin account to continue.
420
+ </p>
421
+ <dees-form>
422
+ <dees-input-text .key=${'email'} .label=${'Admin email'} .required=${true}></dees-input-text>
423
+ <dees-input-text .key=${'name'} .label=${'Display name'}></dees-input-text>
424
+ <dees-input-text .key=${'password'} .label=${'Password'} .required=${true} .isPasswordBool=${true}></dees-input-text>
425
+ <dees-input-text .key=${'passwordConfirm'} .label=${'Confirm password'} .required=${true} .isPasswordBool=${true}></dees-input-text>
426
+ <dees-input-checkbox
427
+ .key=${'enableIdpGlobalAuth'}
428
+ .label=${'Allow idp.global login for this email'}
429
+ .description=${statusArg.idpGlobalConfigured
430
+ ? 'The local account remains authoritative; idp.global only verifies identity.'
431
+ : 'Requires DCROUTER_IDP_GLOBAL_URL before idp.global logins can work.'}
432
+ ></dees-input-checkbox>
433
+ </dees-form>
434
+ </div>
435
+ `,
436
+ menuOptions: [
437
+ {
438
+ name: 'Create admin',
439
+ action: async (stepperArg: any) => {
440
+ const form = stepperArg.shadowRoot?.querySelector('.selected dees-form') as any;
441
+ if (!form) return;
442
+ const formData = await form.collectFormData();
443
+ const email = String(formData.email || '').trim();
444
+ const name = String(formData.name || '').trim();
445
+ const password = String(formData.password || '');
446
+ const passwordConfirm = String(formData.passwordConfirm || '');
447
+
448
+ if (!email || !password) {
449
+ form.setStatus?.('error', 'Email and password are required.');
450
+ return;
451
+ }
452
+ if (password !== passwordConfirm) {
453
+ form.setStatus?.('error', 'Passwords do not match.');
454
+ return;
455
+ }
456
+
457
+ try {
458
+ form.setStatus?.('pending', 'Creating persisted admin...');
459
+ await appstate.createInitialAdminUser({
460
+ email,
461
+ name,
462
+ password,
463
+ enableIdpGlobalAuth: Boolean(formData.enableIdpGlobalAuth),
464
+ });
465
+ form.setStatus?.('success', 'Persisted admin created.');
466
+ await stepperArg.destroy();
467
+ this.bootstrapStepper = undefined;
468
+ await appstate.usersStatePart.dispatchAction(appstate.fetchUsersAction, null);
469
+ } catch (error) {
470
+ form.setStatus?.('error', error instanceof Error ? error.message : 'Failed to create admin.');
471
+ }
472
+ },
473
+ },
474
+ ],
475
+ },
476
+ ],
477
+ });
478
+ }
379
479
  }