@dudousxd/nestjs-durable-dashboard 0.51.1 → 0.52.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.
@@ -2469,6 +2469,186 @@ DurableAuthController = _ts_decorate9([
2469
2469
  ])
2470
2470
  ], DurableAuthController);
2471
2471
 
2472
+ // src/server/durable-dashboard-gate.guard.ts
2473
+ import { timingSafeEqual as timingSafeEqual2 } from "node:crypto";
2474
+ import { ForbiddenException, Inject as Inject5, Injectable as Injectable4, Logger as Logger3, UnauthorizedException as UnauthorizedException2 } from "@nestjs/common";
2475
+ function _ts_decorate10(decorators, target, key, desc) {
2476
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2477
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
2478
+ r = Reflect.decorate(decorators, target, key, desc);
2479
+ } else {
2480
+ for (var i = decorators.length - 1; i >= 0; i--) {
2481
+ if (d = decorators[i]) {
2482
+ r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
2483
+ }
2484
+ }
2485
+ }
2486
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
2487
+ }
2488
+ __name(_ts_decorate10, "_ts_decorate");
2489
+ function _ts_metadata8(metadataKey, metadataValue) {
2490
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") {
2491
+ return Reflect.metadata(metadataKey, metadataValue);
2492
+ }
2493
+ }
2494
+ __name(_ts_metadata8, "_ts_metadata");
2495
+ function _ts_param6(paramIndex, decorator) {
2496
+ return function(target, key) {
2497
+ decorator(target, key, paramIndex);
2498
+ };
2499
+ }
2500
+ __name(_ts_param6, "_ts_param");
2501
+ var DURABLE_DASHBOARD_TOKEN_ENV = "DURABLE_DASHBOARD_TOKEN";
2502
+ var DASHBOARD_GATE = Symbol("DASHBOARD_GATE");
2503
+ var LOG_CONTEXT = "DurableDashboard";
2504
+ var HOW_TO_CONFIGURE = [
2505
+ "Configure one of: `guards: [YourAuthGuard]`, `dashboardAuth: { secret, session | login }`",
2506
+ "(or `useDashboardAuth` in forRootAsync), a bearer token via the `token` option or the",
2507
+ `${DURABLE_DASHBOARD_TOKEN_ENV} env var, or \`allowUnauthenticated: true\` when the console is`,
2508
+ "already protected outside this module (a global guard, a reverse proxy, a private network)."
2509
+ ].join(" ");
2510
+ function isProduction() {
2511
+ return (process.env.NODE_ENV ?? "").trim().toLowerCase() === "production";
2512
+ }
2513
+ __name(isProduction, "isProduction");
2514
+ function resolveDashboardGate(options, auth) {
2515
+ if (options.hasGuards || auth !== null) return {
2516
+ mode: "delegated"
2517
+ };
2518
+ const token = options.token ?? process.env[DURABLE_DASHBOARD_TOKEN_ENV];
2519
+ if (typeof token === "string" && token !== "") return {
2520
+ mode: "token",
2521
+ token
2522
+ };
2523
+ if (options.allowUnauthenticated === true) return {
2524
+ mode: "open"
2525
+ };
2526
+ return {
2527
+ mode: isProduction() ? "deny" : "unconfigured"
2528
+ };
2529
+ }
2530
+ __name(resolveDashboardGate, "resolveDashboardGate");
2531
+ function reportDashboardGate(gate, options, logger = new Logger3(LOG_CONTEXT)) {
2532
+ switch (gate.mode) {
2533
+ case "open":
2534
+ logger.warn("allowUnauthenticated: true. The durable console (including retry/cancel and other mutating endpoints) is served with NO authentication from this module. Only do this when a global guard or reverse proxy already protects it.");
2535
+ return;
2536
+ case "token":
2537
+ if (options.allowUnauthenticated === true) {
2538
+ logger.warn("allowUnauthenticated: true is ignored because a dashboard token is configured; requests must carry the token.");
2539
+ }
2540
+ return;
2541
+ case "unconfigured":
2542
+ logger.warn([
2543
+ "The durable console is UNAUTHENTICATED: no guards, dashboardAuth, token or",
2544
+ "allowUnauthenticated were configured. It is served open because NODE_ENV is not",
2545
+ '"production", but it WILL deny every request when NODE_ENV=production.',
2546
+ HOW_TO_CONFIGURE
2547
+ ].join(" "));
2548
+ return;
2549
+ case "deny":
2550
+ logger.error([
2551
+ "The durable console has no authentication configured and NODE_ENV=production, so it",
2552
+ "is failing closed: every console request (UI and API) is denied.",
2553
+ HOW_TO_CONFIGURE
2554
+ ].join(" "));
2555
+ return;
2556
+ case "delegated":
2557
+ return;
2558
+ }
2559
+ }
2560
+ __name(reportDashboardGate, "reportDashboardGate");
2561
+ function dashboardGateProvider(options) {
2562
+ return {
2563
+ provide: DASHBOARD_GATE,
2564
+ inject: [
2565
+ DASHBOARD_AUTH
2566
+ ],
2567
+ useFactory: /* @__PURE__ */ __name((auth) => {
2568
+ const gate = resolveDashboardGate(options, auth);
2569
+ reportDashboardGate(gate, options);
2570
+ return gate;
2571
+ }, "useFactory")
2572
+ };
2573
+ }
2574
+ __name(dashboardGateProvider, "dashboardGateProvider");
2575
+ function isRecord3(value) {
2576
+ return typeof value === "object" && value !== null;
2577
+ }
2578
+ __name(isRecord3, "isRecord");
2579
+ function headerValue(request, name) {
2580
+ const headers = request.headers;
2581
+ if (!isRecord3(headers)) return void 0;
2582
+ const value = headers[name];
2583
+ if (typeof value === "string") return value;
2584
+ if (Array.isArray(value) && typeof value[0] === "string") return value[0];
2585
+ return void 0;
2586
+ }
2587
+ __name(headerValue, "headerValue");
2588
+ function readDashboardToken(request) {
2589
+ if (!isRecord3(request)) return void 0;
2590
+ const authorization = headerValue(request, "authorization");
2591
+ if (authorization) {
2592
+ const match = /^Bearer\s+(.+)$/i.exec(authorization);
2593
+ if (match?.[1]) return match[1].trim();
2594
+ }
2595
+ const header = headerValue(request, "x-durable-token");
2596
+ if (header) return header.trim();
2597
+ const method = typeof request.method === "string" ? request.method.toUpperCase() : "";
2598
+ if (method === "GET" || method === "HEAD") {
2599
+ const query = request.query;
2600
+ if (isRecord3(query)) {
2601
+ const value = query.token;
2602
+ if (typeof value === "string" && value !== "") return value;
2603
+ }
2604
+ }
2605
+ return void 0;
2606
+ }
2607
+ __name(readDashboardToken, "readDashboardToken");
2608
+ function secretsMatch(provided, expected) {
2609
+ const a = Buffer.from(provided);
2610
+ const b = Buffer.from(expected);
2611
+ if (a.length !== b.length) return false;
2612
+ return timingSafeEqual2(a, b);
2613
+ }
2614
+ __name(secretsMatch, "secretsMatch");
2615
+ var DurableDashboardGateGuard = class {
2616
+ static {
2617
+ __name(this, "DurableDashboardGateGuard");
2618
+ }
2619
+ gate;
2620
+ constructor(gate) {
2621
+ this.gate = gate;
2622
+ }
2623
+ canActivate(context) {
2624
+ switch (this.gate.mode) {
2625
+ case "delegated":
2626
+ case "open":
2627
+ case "unconfigured":
2628
+ return true;
2629
+ case "deny":
2630
+ throw new ForbiddenException([
2631
+ "Durable dashboard authentication is not configured, and NODE_ENV=production denies",
2632
+ "an unauthenticated console.",
2633
+ HOW_TO_CONFIGURE
2634
+ ].join(" "));
2635
+ case "token": {
2636
+ const provided = readDashboardToken(context.switchToHttp().getRequest());
2637
+ if (provided !== void 0 && secretsMatch(provided, this.gate.token ?? "")) return true;
2638
+ throw new UnauthorizedException2("A valid durable dashboard token is required (Authorization: Bearer <token>, x-durable-token, or ?token= on GET).");
2639
+ }
2640
+ }
2641
+ }
2642
+ };
2643
+ DurableDashboardGateGuard = _ts_decorate10([
2644
+ Injectable4(),
2645
+ _ts_param6(0, Inject5(DASHBOARD_GATE)),
2646
+ _ts_metadata8("design:type", Function),
2647
+ _ts_metadata8("design:paramtypes", [
2648
+ typeof DashboardGate === "undefined" ? Object : DashboardGate
2649
+ ])
2650
+ ], DurableDashboardGateGuard);
2651
+
2472
2652
  // src/server/durable-dashboard.module.ts
2473
2653
  import "reflect-metadata";
2474
2654
  import { ApplyFilterInterceptor as ApplyFilterInterceptor2, FilterRunner as FilterRunner2 } from "@dudousxd/nestjs-filter";
@@ -2476,8 +2656,8 @@ import { Module } from "@nestjs/common";
2476
2656
  import { ModuleRef, RouterModule } from "@nestjs/core";
2477
2657
 
2478
2658
  // src/server/durable-session.guard.ts
2479
- import { Inject as Inject5, Injectable as Injectable4, UnauthorizedException as UnauthorizedException2 } from "@nestjs/common";
2480
- function _ts_decorate10(decorators, target, key, desc) {
2659
+ import { Inject as Inject6, Injectable as Injectable5, UnauthorizedException as UnauthorizedException3 } from "@nestjs/common";
2660
+ function _ts_decorate11(decorators, target, key, desc) {
2481
2661
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2482
2662
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
2483
2663
  r = Reflect.decorate(decorators, target, key, desc);
@@ -2490,19 +2670,19 @@ function _ts_decorate10(decorators, target, key, desc) {
2490
2670
  }
2491
2671
  return c > 3 && r && Object.defineProperty(target, key, r), r;
2492
2672
  }
2493
- __name(_ts_decorate10, "_ts_decorate");
2494
- function _ts_metadata8(metadataKey, metadataValue) {
2673
+ __name(_ts_decorate11, "_ts_decorate");
2674
+ function _ts_metadata9(metadataKey, metadataValue) {
2495
2675
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") {
2496
2676
  return Reflect.metadata(metadataKey, metadataValue);
2497
2677
  }
2498
2678
  }
2499
- __name(_ts_metadata8, "_ts_metadata");
2500
- function _ts_param6(paramIndex, decorator) {
2679
+ __name(_ts_metadata9, "_ts_metadata");
2680
+ function _ts_param7(paramIndex, decorator) {
2501
2681
  return function(target, key) {
2502
2682
  decorator(target, key, paramIndex);
2503
2683
  };
2504
2684
  }
2505
- __name(_ts_param6, "_ts_param");
2685
+ __name(_ts_param7, "_ts_param");
2506
2686
  var DurableUiSessionGuard = class {
2507
2687
  static {
2508
2688
  __name(this, "DurableUiSessionGuard");
@@ -2543,12 +2723,12 @@ var DurableUiSessionGuard = class {
2543
2723
  throw new DashboardLoginRedirectException(`${this.basePath}/login?returnTo=${returnTo}`);
2544
2724
  }
2545
2725
  };
2546
- DurableUiSessionGuard = _ts_decorate10([
2547
- Injectable4(),
2548
- _ts_param6(0, Inject5(DASHBOARD_AUTH)),
2549
- _ts_param6(1, Inject5(DASHBOARD_BASE_PATH)),
2550
- _ts_metadata8("design:type", Function),
2551
- _ts_metadata8("design:paramtypes", [
2726
+ DurableUiSessionGuard = _ts_decorate11([
2727
+ Injectable5(),
2728
+ _ts_param7(0, Inject6(DASHBOARD_AUTH)),
2729
+ _ts_param7(1, Inject6(DASHBOARD_BASE_PATH)),
2730
+ _ts_metadata9("design:type", Function),
2731
+ _ts_metadata9("design:paramtypes", [
2552
2732
  Object,
2553
2733
  String
2554
2734
  ])
@@ -2580,24 +2760,24 @@ var DurableApiSessionGuard = class {
2580
2760
  * 401; `DurableUiSessionGuard`'s redirect/session-required exceptions above don't reach it.
2581
2761
  */
2582
2762
  unauthorized(auth) {
2583
- return new UnauthorizedException2({
2763
+ return new UnauthorizedException3({
2584
2764
  auth: {
2585
2765
  modes: auth.modes
2586
2766
  }
2587
2767
  });
2588
2768
  }
2589
2769
  };
2590
- DurableApiSessionGuard = _ts_decorate10([
2591
- Injectable4(),
2592
- _ts_param6(0, Inject5(DASHBOARD_AUTH)),
2593
- _ts_metadata8("design:type", Function),
2594
- _ts_metadata8("design:paramtypes", [
2770
+ DurableApiSessionGuard = _ts_decorate11([
2771
+ Injectable5(),
2772
+ _ts_param7(0, Inject6(DASHBOARD_AUTH)),
2773
+ _ts_metadata9("design:type", Function),
2774
+ _ts_metadata9("design:paramtypes", [
2595
2775
  Object
2596
2776
  ])
2597
2777
  ], DurableApiSessionGuard);
2598
2778
 
2599
2779
  // src/server/durable-dashboard.module.ts
2600
- function _ts_decorate11(decorators, target, key, desc) {
2780
+ function _ts_decorate12(decorators, target, key, desc) {
2601
2781
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2602
2782
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
2603
2783
  r = Reflect.decorate(decorators, target, key, desc);
@@ -2610,7 +2790,7 @@ function _ts_decorate11(decorators, target, key, desc) {
2610
2790
  }
2611
2791
  return c > 3 && r && Object.defineProperty(target, key, r), r;
2612
2792
  }
2613
- __name(_ts_decorate11, "_ts_decorate");
2793
+ __name(_ts_decorate12, "_ts_decorate");
2614
2794
  var GUARDS_METADATA = "__guards__";
2615
2795
  function normalize(path) {
2616
2796
  return `/${path.replace(/^\/+|\/+$/g, "")}`;
@@ -2618,9 +2798,7 @@ function normalize(path) {
2618
2798
  __name(normalize, "normalize");
2619
2799
  function stampGuards(hostGuards, builtIn, controller) {
2620
2800
  const merged = [
2621
- ...builtIn ? [
2622
- builtIn
2623
- ] : [],
2801
+ ...builtIn.filter((guard) => guard !== void 0),
2624
2802
  ...hostGuards ?? []
2625
2803
  ];
2626
2804
  Reflect.defineMetadata(GUARDS_METADATA, merged, controller);
@@ -2631,7 +2809,10 @@ var DurableApiModule = class _DurableApiModule {
2631
2809
  __name(this, "DurableApiModule");
2632
2810
  }
2633
2811
  static register(options) {
2634
- stampGuards(options.guards, options.authMayBeConfigured ? DurableApiSessionGuard : void 0, DurableApiController);
2812
+ stampGuards(options.guards, [
2813
+ options.authMayBeConfigured ? DurableApiSessionGuard : void 0,
2814
+ options.stampGate ? DurableDashboardGateGuard : void 0
2815
+ ], DurableApiController);
2635
2816
  return {
2636
2817
  module: _DurableApiModule,
2637
2818
  imports: [
@@ -2675,15 +2856,20 @@ var DurableApiModule = class _DurableApiModule {
2675
2856
  RunFilter,
2676
2857
  options.authProvider,
2677
2858
  DurableApiSessionGuard,
2859
+ // Resolved ONCE here (this module is imported by the UI module, which reuses it via the
2860
+ // export below), so the boot report is logged once per mount.
2861
+ dashboardGateProvider(options.gate),
2862
+ DurableDashboardGateGuard,
2678
2863
  ...options.guards ?? []
2679
2864
  ],
2680
2865
  exports: [
2681
- DashboardService
2866
+ DashboardService,
2867
+ DASHBOARD_GATE
2682
2868
  ]
2683
2869
  };
2684
2870
  }
2685
2871
  };
2686
- DurableApiModule = _ts_decorate11([
2872
+ DurableApiModule = _ts_decorate12([
2687
2873
  Module({})
2688
2874
  ], DurableApiModule);
2689
2875
  var DurableDashboardModule = class _DurableDashboardModule {
@@ -2695,7 +2881,8 @@ var DurableDashboardModule = class _DurableDashboardModule {
2695
2881
  provide: DASHBOARD_AUTH,
2696
2882
  useValue: resolveDashboardAuth(options.dashboardAuth)
2697
2883
  };
2698
- return _DurableDashboardModule.build(options, authProvider, options.dashboardAuth !== void 0);
2884
+ const authConfigured = options.dashboardAuth !== void 0;
2885
+ return _DurableDashboardModule.build(options, authProvider, authConfigured, authConfigured);
2699
2886
  }
2700
2887
  static forRootAsync(options) {
2701
2888
  const authProvider = {
@@ -2703,13 +2890,27 @@ var DurableDashboardModule = class _DurableDashboardModule {
2703
2890
  inject: options.inject ?? [],
2704
2891
  useFactory: /* @__PURE__ */ __name(async (...deps) => resolveDashboardAuth(await options.useDashboardAuth(...deps)), "useFactory")
2705
2892
  };
2706
- return _DurableDashboardModule.build(options, authProvider, true);
2893
+ return _DurableDashboardModule.build(options, authProvider, true, false);
2707
2894
  }
2708
2895
  /** Shared wiring: static routing + the API module, with `dashboardAuth` supplied by `authProvider`. */
2709
- static build(options, authProvider, authMayBeConfigured) {
2896
+ static build(options, authProvider, authMayBeConfigured, authStaticallyConfigured) {
2710
2897
  const basePath = normalize(options.basePath ?? "/durable");
2711
2898
  const apiBasePath = normalize(options.apiBasePath ?? `${basePath}/api`);
2712
- stampGuards(options.guards, authMayBeConfigured ? DurableUiSessionGuard : void 0, DurableUiController);
2899
+ const hasGuards = (options.guards?.length ?? 0) > 0;
2900
+ const gate = {
2901
+ hasGuards,
2902
+ ...options.allowUnauthenticated !== void 0 ? {
2903
+ allowUnauthenticated: options.allowUnauthenticated
2904
+ } : {},
2905
+ ...options.token !== void 0 ? {
2906
+ token: options.token
2907
+ } : {}
2908
+ };
2909
+ const stampGate = !hasGuards && !authStaticallyConfigured;
2910
+ stampGuards(options.guards, [
2911
+ authMayBeConfigured ? DurableUiSessionGuard : void 0,
2912
+ stampGate ? DurableDashboardGateGuard : void 0
2913
+ ], DurableUiController);
2713
2914
  return {
2714
2915
  module: _DurableDashboardModule,
2715
2916
  imports: [
@@ -2725,7 +2926,9 @@ var DurableDashboardModule = class _DurableDashboardModule {
2725
2926
  guards: options.guards
2726
2927
  } : {},
2727
2928
  authProvider,
2728
- authMayBeConfigured
2929
+ authMayBeConfigured,
2930
+ gate,
2931
+ stampGate
2729
2932
  }),
2730
2933
  RouterModule.register([
2731
2934
  {
@@ -2758,6 +2961,8 @@ var DurableDashboardModule = class _DurableDashboardModule {
2758
2961
  },
2759
2962
  authProvider,
2760
2963
  DurableUiSessionGuard,
2964
+ // `DASHBOARD_GATE` itself comes from the imported DurableApiModule (resolved once there).
2965
+ DurableDashboardGateGuard,
2761
2966
  // DurableUiController (and DurableAuthController, when present) are hosted HERE, so their
2762
2967
  // guards DI-instantiate from this module.
2763
2968
  ...options.guards ?? []
@@ -2769,19 +2974,26 @@ var DurableDashboardModule = class _DurableDashboardModule {
2769
2974
  };
2770
2975
  }
2771
2976
  };
2772
- DurableDashboardModule = _ts_decorate11([
2977
+ DurableDashboardModule = _ts_decorate12([
2773
2978
  Module({})
2774
2979
  ], DurableDashboardModule);
2775
2980
  export {
2776
2981
  DASHBOARD_API_PATH,
2777
2982
  DASHBOARD_BASE_PATH,
2983
+ DASHBOARD_GATE,
2984
+ DURABLE_DASHBOARD_TOKEN_ENV,
2778
2985
  DashboardService,
2779
2986
  DurableApiController,
2780
2987
  DurableApiModule,
2781
2988
  DurableApiSessionGuard,
2782
2989
  DurableAuthController,
2990
+ DurableDashboardGateGuard,
2783
2991
  DurableDashboardModule,
2784
2992
  DurableUiController,
2785
- DurableUiSessionGuard
2993
+ DurableUiSessionGuard,
2994
+ dashboardGateProvider,
2995
+ readDashboardToken,
2996
+ reportDashboardGate,
2997
+ resolveDashboardGate
2786
2998
  };
2787
2999
  //# sourceMappingURL=index.js.map