@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.
@@ -24,14 +24,21 @@ var index_exports = {};
24
24
  __export(index_exports, {
25
25
  DASHBOARD_API_PATH: () => DASHBOARD_API_PATH,
26
26
  DASHBOARD_BASE_PATH: () => DASHBOARD_BASE_PATH,
27
+ DASHBOARD_GATE: () => DASHBOARD_GATE,
28
+ DURABLE_DASHBOARD_TOKEN_ENV: () => DURABLE_DASHBOARD_TOKEN_ENV,
27
29
  DashboardService: () => DashboardService,
28
30
  DurableApiController: () => DurableApiController,
29
31
  DurableApiModule: () => DurableApiModule,
30
32
  DurableApiSessionGuard: () => DurableApiSessionGuard,
31
33
  DurableAuthController: () => DurableAuthController,
34
+ DurableDashboardGateGuard: () => DurableDashboardGateGuard,
32
35
  DurableDashboardModule: () => DurableDashboardModule,
33
36
  DurableUiController: () => DurableUiController,
34
- DurableUiSessionGuard: () => DurableUiSessionGuard
37
+ DurableUiSessionGuard: () => DurableUiSessionGuard,
38
+ dashboardGateProvider: () => dashboardGateProvider,
39
+ readDashboardToken: () => readDashboardToken,
40
+ reportDashboardGate: () => reportDashboardGate,
41
+ resolveDashboardGate: () => resolveDashboardGate
35
42
  });
36
43
  module.exports = __toCommonJS(index_exports);
37
44
 
@@ -2503,13 +2510,8 @@ DurableAuthController = _ts_decorate9([
2503
2510
  ])
2504
2511
  ], DurableAuthController);
2505
2512
 
2506
- // src/server/durable-dashboard.module.ts
2507
- var import_reflect_metadata = require("reflect-metadata");
2508
- var import_nestjs_filter3 = require("@dudousxd/nestjs-filter");
2509
- var import_common11 = require("@nestjs/common");
2510
- var import_core = require("@nestjs/core");
2511
-
2512
- // src/server/durable-session.guard.ts
2513
+ // src/server/durable-dashboard-gate.guard.ts
2514
+ var import_node_crypto2 = require("crypto");
2513
2515
  var import_common10 = require("@nestjs/common");
2514
2516
  function _ts_decorate10(decorators, target, key, desc) {
2515
2517
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
@@ -2537,6 +2539,191 @@ function _ts_param6(paramIndex, decorator) {
2537
2539
  };
2538
2540
  }
2539
2541
  __name(_ts_param6, "_ts_param");
2542
+ var DURABLE_DASHBOARD_TOKEN_ENV = "DURABLE_DASHBOARD_TOKEN";
2543
+ var DASHBOARD_GATE = Symbol("DASHBOARD_GATE");
2544
+ var LOG_CONTEXT = "DurableDashboard";
2545
+ var HOW_TO_CONFIGURE = [
2546
+ "Configure one of: `guards: [YourAuthGuard]`, `dashboardAuth: { secret, session | login }`",
2547
+ "(or `useDashboardAuth` in forRootAsync), a bearer token via the `token` option or the",
2548
+ `${DURABLE_DASHBOARD_TOKEN_ENV} env var, or \`allowUnauthenticated: true\` when the console is`,
2549
+ "already protected outside this module (a global guard, a reverse proxy, a private network)."
2550
+ ].join(" ");
2551
+ function isProduction() {
2552
+ return (process.env.NODE_ENV ?? "").trim().toLowerCase() === "production";
2553
+ }
2554
+ __name(isProduction, "isProduction");
2555
+ function resolveDashboardGate(options, auth) {
2556
+ if (options.hasGuards || auth !== null) return {
2557
+ mode: "delegated"
2558
+ };
2559
+ const token = options.token ?? process.env[DURABLE_DASHBOARD_TOKEN_ENV];
2560
+ if (typeof token === "string" && token !== "") return {
2561
+ mode: "token",
2562
+ token
2563
+ };
2564
+ if (options.allowUnauthenticated === true) return {
2565
+ mode: "open"
2566
+ };
2567
+ return {
2568
+ mode: isProduction() ? "deny" : "unconfigured"
2569
+ };
2570
+ }
2571
+ __name(resolveDashboardGate, "resolveDashboardGate");
2572
+ function reportDashboardGate(gate, options, logger = new import_common10.Logger(LOG_CONTEXT)) {
2573
+ switch (gate.mode) {
2574
+ case "open":
2575
+ 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.");
2576
+ return;
2577
+ case "token":
2578
+ if (options.allowUnauthenticated === true) {
2579
+ logger.warn("allowUnauthenticated: true is ignored because a dashboard token is configured; requests must carry the token.");
2580
+ }
2581
+ return;
2582
+ case "unconfigured":
2583
+ logger.warn([
2584
+ "The durable console is UNAUTHENTICATED: no guards, dashboardAuth, token or",
2585
+ "allowUnauthenticated were configured. It is served open because NODE_ENV is not",
2586
+ '"production", but it WILL deny every request when NODE_ENV=production.',
2587
+ HOW_TO_CONFIGURE
2588
+ ].join(" "));
2589
+ return;
2590
+ case "deny":
2591
+ logger.error([
2592
+ "The durable console has no authentication configured and NODE_ENV=production, so it",
2593
+ "is failing closed: every console request (UI and API) is denied.",
2594
+ HOW_TO_CONFIGURE
2595
+ ].join(" "));
2596
+ return;
2597
+ case "delegated":
2598
+ return;
2599
+ }
2600
+ }
2601
+ __name(reportDashboardGate, "reportDashboardGate");
2602
+ function dashboardGateProvider(options) {
2603
+ return {
2604
+ provide: DASHBOARD_GATE,
2605
+ inject: [
2606
+ DASHBOARD_AUTH
2607
+ ],
2608
+ useFactory: /* @__PURE__ */ __name((auth) => {
2609
+ const gate = resolveDashboardGate(options, auth);
2610
+ reportDashboardGate(gate, options);
2611
+ return gate;
2612
+ }, "useFactory")
2613
+ };
2614
+ }
2615
+ __name(dashboardGateProvider, "dashboardGateProvider");
2616
+ function isRecord3(value) {
2617
+ return typeof value === "object" && value !== null;
2618
+ }
2619
+ __name(isRecord3, "isRecord");
2620
+ function headerValue(request, name) {
2621
+ const headers = request.headers;
2622
+ if (!isRecord3(headers)) return void 0;
2623
+ const value = headers[name];
2624
+ if (typeof value === "string") return value;
2625
+ if (Array.isArray(value) && typeof value[0] === "string") return value[0];
2626
+ return void 0;
2627
+ }
2628
+ __name(headerValue, "headerValue");
2629
+ function readDashboardToken(request) {
2630
+ if (!isRecord3(request)) return void 0;
2631
+ const authorization = headerValue(request, "authorization");
2632
+ if (authorization) {
2633
+ const match = /^Bearer\s+(.+)$/i.exec(authorization);
2634
+ if (match?.[1]) return match[1].trim();
2635
+ }
2636
+ const header = headerValue(request, "x-durable-token");
2637
+ if (header) return header.trim();
2638
+ const method = typeof request.method === "string" ? request.method.toUpperCase() : "";
2639
+ if (method === "GET" || method === "HEAD") {
2640
+ const query = request.query;
2641
+ if (isRecord3(query)) {
2642
+ const value = query.token;
2643
+ if (typeof value === "string" && value !== "") return value;
2644
+ }
2645
+ }
2646
+ return void 0;
2647
+ }
2648
+ __name(readDashboardToken, "readDashboardToken");
2649
+ function secretsMatch(provided, expected) {
2650
+ const a = Buffer.from(provided);
2651
+ const b = Buffer.from(expected);
2652
+ if (a.length !== b.length) return false;
2653
+ return (0, import_node_crypto2.timingSafeEqual)(a, b);
2654
+ }
2655
+ __name(secretsMatch, "secretsMatch");
2656
+ var DurableDashboardGateGuard = class {
2657
+ static {
2658
+ __name(this, "DurableDashboardGateGuard");
2659
+ }
2660
+ gate;
2661
+ constructor(gate) {
2662
+ this.gate = gate;
2663
+ }
2664
+ canActivate(context) {
2665
+ switch (this.gate.mode) {
2666
+ case "delegated":
2667
+ case "open":
2668
+ case "unconfigured":
2669
+ return true;
2670
+ case "deny":
2671
+ throw new import_common10.ForbiddenException([
2672
+ "Durable dashboard authentication is not configured, and NODE_ENV=production denies",
2673
+ "an unauthenticated console.",
2674
+ HOW_TO_CONFIGURE
2675
+ ].join(" "));
2676
+ case "token": {
2677
+ const provided = readDashboardToken(context.switchToHttp().getRequest());
2678
+ if (provided !== void 0 && secretsMatch(provided, this.gate.token ?? "")) return true;
2679
+ throw new import_common10.UnauthorizedException("A valid durable dashboard token is required (Authorization: Bearer <token>, x-durable-token, or ?token= on GET).");
2680
+ }
2681
+ }
2682
+ }
2683
+ };
2684
+ DurableDashboardGateGuard = _ts_decorate10([
2685
+ (0, import_common10.Injectable)(),
2686
+ _ts_param6(0, (0, import_common10.Inject)(DASHBOARD_GATE)),
2687
+ _ts_metadata8("design:type", Function),
2688
+ _ts_metadata8("design:paramtypes", [
2689
+ typeof DashboardGate === "undefined" ? Object : DashboardGate
2690
+ ])
2691
+ ], DurableDashboardGateGuard);
2692
+
2693
+ // src/server/durable-dashboard.module.ts
2694
+ var import_reflect_metadata = require("reflect-metadata");
2695
+ var import_nestjs_filter3 = require("@dudousxd/nestjs-filter");
2696
+ var import_common12 = require("@nestjs/common");
2697
+ var import_core = require("@nestjs/core");
2698
+
2699
+ // src/server/durable-session.guard.ts
2700
+ var import_common11 = require("@nestjs/common");
2701
+ function _ts_decorate11(decorators, target, key, desc) {
2702
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2703
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
2704
+ r = Reflect.decorate(decorators, target, key, desc);
2705
+ } else {
2706
+ for (var i = decorators.length - 1; i >= 0; i--) {
2707
+ if (d = decorators[i]) {
2708
+ r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
2709
+ }
2710
+ }
2711
+ }
2712
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
2713
+ }
2714
+ __name(_ts_decorate11, "_ts_decorate");
2715
+ function _ts_metadata9(metadataKey, metadataValue) {
2716
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") {
2717
+ return Reflect.metadata(metadataKey, metadataValue);
2718
+ }
2719
+ }
2720
+ __name(_ts_metadata9, "_ts_metadata");
2721
+ function _ts_param7(paramIndex, decorator) {
2722
+ return function(target, key) {
2723
+ decorator(target, key, paramIndex);
2724
+ };
2725
+ }
2726
+ __name(_ts_param7, "_ts_param");
2540
2727
  var DurableUiSessionGuard = class {
2541
2728
  static {
2542
2729
  __name(this, "DurableUiSessionGuard");
@@ -2577,12 +2764,12 @@ var DurableUiSessionGuard = class {
2577
2764
  throw new DashboardLoginRedirectException(`${this.basePath}/login?returnTo=${returnTo}`);
2578
2765
  }
2579
2766
  };
2580
- DurableUiSessionGuard = _ts_decorate10([
2581
- (0, import_common10.Injectable)(),
2582
- _ts_param6(0, (0, import_common10.Inject)(DASHBOARD_AUTH)),
2583
- _ts_param6(1, (0, import_common10.Inject)(DASHBOARD_BASE_PATH)),
2584
- _ts_metadata8("design:type", Function),
2585
- _ts_metadata8("design:paramtypes", [
2767
+ DurableUiSessionGuard = _ts_decorate11([
2768
+ (0, import_common11.Injectable)(),
2769
+ _ts_param7(0, (0, import_common11.Inject)(DASHBOARD_AUTH)),
2770
+ _ts_param7(1, (0, import_common11.Inject)(DASHBOARD_BASE_PATH)),
2771
+ _ts_metadata9("design:type", Function),
2772
+ _ts_metadata9("design:paramtypes", [
2586
2773
  Object,
2587
2774
  String
2588
2775
  ])
@@ -2614,24 +2801,24 @@ var DurableApiSessionGuard = class {
2614
2801
  * 401; `DurableUiSessionGuard`'s redirect/session-required exceptions above don't reach it.
2615
2802
  */
2616
2803
  unauthorized(auth) {
2617
- return new import_common10.UnauthorizedException({
2804
+ return new import_common11.UnauthorizedException({
2618
2805
  auth: {
2619
2806
  modes: auth.modes
2620
2807
  }
2621
2808
  });
2622
2809
  }
2623
2810
  };
2624
- DurableApiSessionGuard = _ts_decorate10([
2625
- (0, import_common10.Injectable)(),
2626
- _ts_param6(0, (0, import_common10.Inject)(DASHBOARD_AUTH)),
2627
- _ts_metadata8("design:type", Function),
2628
- _ts_metadata8("design:paramtypes", [
2811
+ DurableApiSessionGuard = _ts_decorate11([
2812
+ (0, import_common11.Injectable)(),
2813
+ _ts_param7(0, (0, import_common11.Inject)(DASHBOARD_AUTH)),
2814
+ _ts_metadata9("design:type", Function),
2815
+ _ts_metadata9("design:paramtypes", [
2629
2816
  Object
2630
2817
  ])
2631
2818
  ], DurableApiSessionGuard);
2632
2819
 
2633
2820
  // src/server/durable-dashboard.module.ts
2634
- function _ts_decorate11(decorators, target, key, desc) {
2821
+ function _ts_decorate12(decorators, target, key, desc) {
2635
2822
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2636
2823
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") {
2637
2824
  r = Reflect.decorate(decorators, target, key, desc);
@@ -2644,7 +2831,7 @@ function _ts_decorate11(decorators, target, key, desc) {
2644
2831
  }
2645
2832
  return c > 3 && r && Object.defineProperty(target, key, r), r;
2646
2833
  }
2647
- __name(_ts_decorate11, "_ts_decorate");
2834
+ __name(_ts_decorate12, "_ts_decorate");
2648
2835
  var GUARDS_METADATA = "__guards__";
2649
2836
  function normalize(path) {
2650
2837
  return `/${path.replace(/^\/+|\/+$/g, "")}`;
@@ -2652,9 +2839,7 @@ function normalize(path) {
2652
2839
  __name(normalize, "normalize");
2653
2840
  function stampGuards(hostGuards, builtIn, controller) {
2654
2841
  const merged = [
2655
- ...builtIn ? [
2656
- builtIn
2657
- ] : [],
2842
+ ...builtIn.filter((guard) => guard !== void 0),
2658
2843
  ...hostGuards ?? []
2659
2844
  ];
2660
2845
  Reflect.defineMetadata(GUARDS_METADATA, merged, controller);
@@ -2665,7 +2850,10 @@ var DurableApiModule = class _DurableApiModule {
2665
2850
  __name(this, "DurableApiModule");
2666
2851
  }
2667
2852
  static register(options) {
2668
- stampGuards(options.guards, options.authMayBeConfigured ? DurableApiSessionGuard : void 0, DurableApiController);
2853
+ stampGuards(options.guards, [
2854
+ options.authMayBeConfigured ? DurableApiSessionGuard : void 0,
2855
+ options.stampGate ? DurableDashboardGateGuard : void 0
2856
+ ], DurableApiController);
2669
2857
  return {
2670
2858
  module: _DurableApiModule,
2671
2859
  imports: [
@@ -2709,16 +2897,21 @@ var DurableApiModule = class _DurableApiModule {
2709
2897
  RunFilter,
2710
2898
  options.authProvider,
2711
2899
  DurableApiSessionGuard,
2900
+ // Resolved ONCE here (this module is imported by the UI module, which reuses it via the
2901
+ // export below), so the boot report is logged once per mount.
2902
+ dashboardGateProvider(options.gate),
2903
+ DurableDashboardGateGuard,
2712
2904
  ...options.guards ?? []
2713
2905
  ],
2714
2906
  exports: [
2715
- DashboardService
2907
+ DashboardService,
2908
+ DASHBOARD_GATE
2716
2909
  ]
2717
2910
  };
2718
2911
  }
2719
2912
  };
2720
- DurableApiModule = _ts_decorate11([
2721
- (0, import_common11.Module)({})
2913
+ DurableApiModule = _ts_decorate12([
2914
+ (0, import_common12.Module)({})
2722
2915
  ], DurableApiModule);
2723
2916
  var DurableDashboardModule = class _DurableDashboardModule {
2724
2917
  static {
@@ -2729,7 +2922,8 @@ var DurableDashboardModule = class _DurableDashboardModule {
2729
2922
  provide: DASHBOARD_AUTH,
2730
2923
  useValue: resolveDashboardAuth(options.dashboardAuth)
2731
2924
  };
2732
- return _DurableDashboardModule.build(options, authProvider, options.dashboardAuth !== void 0);
2925
+ const authConfigured = options.dashboardAuth !== void 0;
2926
+ return _DurableDashboardModule.build(options, authProvider, authConfigured, authConfigured);
2733
2927
  }
2734
2928
  static forRootAsync(options) {
2735
2929
  const authProvider = {
@@ -2737,13 +2931,27 @@ var DurableDashboardModule = class _DurableDashboardModule {
2737
2931
  inject: options.inject ?? [],
2738
2932
  useFactory: /* @__PURE__ */ __name(async (...deps) => resolveDashboardAuth(await options.useDashboardAuth(...deps)), "useFactory")
2739
2933
  };
2740
- return _DurableDashboardModule.build(options, authProvider, true);
2934
+ return _DurableDashboardModule.build(options, authProvider, true, false);
2741
2935
  }
2742
2936
  /** Shared wiring: static routing + the API module, with `dashboardAuth` supplied by `authProvider`. */
2743
- static build(options, authProvider, authMayBeConfigured) {
2937
+ static build(options, authProvider, authMayBeConfigured, authStaticallyConfigured) {
2744
2938
  const basePath = normalize(options.basePath ?? "/durable");
2745
2939
  const apiBasePath = normalize(options.apiBasePath ?? `${basePath}/api`);
2746
- stampGuards(options.guards, authMayBeConfigured ? DurableUiSessionGuard : void 0, DurableUiController);
2940
+ const hasGuards = (options.guards?.length ?? 0) > 0;
2941
+ const gate = {
2942
+ hasGuards,
2943
+ ...options.allowUnauthenticated !== void 0 ? {
2944
+ allowUnauthenticated: options.allowUnauthenticated
2945
+ } : {},
2946
+ ...options.token !== void 0 ? {
2947
+ token: options.token
2948
+ } : {}
2949
+ };
2950
+ const stampGate = !hasGuards && !authStaticallyConfigured;
2951
+ stampGuards(options.guards, [
2952
+ authMayBeConfigured ? DurableUiSessionGuard : void 0,
2953
+ stampGate ? DurableDashboardGateGuard : void 0
2954
+ ], DurableUiController);
2747
2955
  return {
2748
2956
  module: _DurableDashboardModule,
2749
2957
  imports: [
@@ -2759,7 +2967,9 @@ var DurableDashboardModule = class _DurableDashboardModule {
2759
2967
  guards: options.guards
2760
2968
  } : {},
2761
2969
  authProvider,
2762
- authMayBeConfigured
2970
+ authMayBeConfigured,
2971
+ gate,
2972
+ stampGate
2763
2973
  }),
2764
2974
  import_core.RouterModule.register([
2765
2975
  {
@@ -2792,6 +3002,8 @@ var DurableDashboardModule = class _DurableDashboardModule {
2792
3002
  },
2793
3003
  authProvider,
2794
3004
  DurableUiSessionGuard,
3005
+ // `DASHBOARD_GATE` itself comes from the imported DurableApiModule (resolved once there).
3006
+ DurableDashboardGateGuard,
2795
3007
  // DurableUiController (and DurableAuthController, when present) are hosted HERE, so their
2796
3008
  // guards DI-instantiate from this module.
2797
3009
  ...options.guards ?? []
@@ -2803,20 +3015,27 @@ var DurableDashboardModule = class _DurableDashboardModule {
2803
3015
  };
2804
3016
  }
2805
3017
  };
2806
- DurableDashboardModule = _ts_decorate11([
2807
- (0, import_common11.Module)({})
3018
+ DurableDashboardModule = _ts_decorate12([
3019
+ (0, import_common12.Module)({})
2808
3020
  ], DurableDashboardModule);
2809
3021
  // Annotate the CommonJS export names for ESM import in node:
2810
3022
  0 && (module.exports = {
2811
3023
  DASHBOARD_API_PATH,
2812
3024
  DASHBOARD_BASE_PATH,
3025
+ DASHBOARD_GATE,
3026
+ DURABLE_DASHBOARD_TOKEN_ENV,
2813
3027
  DashboardService,
2814
3028
  DurableApiController,
2815
3029
  DurableApiModule,
2816
3030
  DurableApiSessionGuard,
2817
3031
  DurableAuthController,
3032
+ DurableDashboardGateGuard,
2818
3033
  DurableDashboardModule,
2819
3034
  DurableUiController,
2820
- DurableUiSessionGuard
3035
+ DurableUiSessionGuard,
3036
+ dashboardGateProvider,
3037
+ readDashboardToken,
3038
+ reportDashboardGate,
3039
+ resolveDashboardGate
2821
3040
  });
2822
3041
  //# sourceMappingURL=index.cjs.map