@dudousxd/nestjs-durable-dashboard 0.51.0 → 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.
- package/dist/server/index.cjs +279 -41
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.d.cts +112 -11
- package/dist/server/index.d.ts +112 -11
- package/dist/server/index.js +270 -39
- package/dist/server/index.js.map +1 -1
- package/package.json +2 -2
package/dist/server/index.d.cts
CHANGED
|
@@ -5,7 +5,7 @@ import * as rxjs from 'rxjs';
|
|
|
5
5
|
import { Observable } from 'rxjs';
|
|
6
6
|
import * as _dudousxd_nestjs_filter from '@dudousxd/nestjs-filter';
|
|
7
7
|
import { FilterRunner } from '@dudousxd/nestjs-filter';
|
|
8
|
-
import {
|
|
8
|
+
import { CanActivate, ExecutionContext, Provider, Logger, DynamicModule, Type, InjectionToken, OptionalFactoryDependency, StreamableFile } from '@nestjs/common';
|
|
9
9
|
|
|
10
10
|
/** The session user the host `login` hook returns to mint a cookie. */
|
|
11
11
|
interface DashboardSessionUser {
|
|
@@ -199,8 +199,12 @@ declare class DashboardService {
|
|
|
199
199
|
}) | null>;
|
|
200
200
|
/**
|
|
201
201
|
* Deliver a `ctx.webhook()` callback: turn an inbound POST (token + body) into the signal the
|
|
202
|
-
*
|
|
203
|
-
*
|
|
202
|
+
* run is (or will be) parked on. A callback that arrives BEFORE the run reaches `wait()` (the url
|
|
203
|
+
* was handed out and the run is still busy or sleeping) is buffered, and `wait()` consumes it, so
|
|
204
|
+
* the result is the run's current status. Returns `null` — mapped to 404 by the controller — for a
|
|
205
|
+
* token that is not a webhook token, whose run doesn't exist or is terminal, or whose wait already
|
|
206
|
+
* resolved (a duplicate): nothing is buffered for those, so a public endpoint can't leave junk
|
|
207
|
+
* rows behind or signal non-webhook tokens. Control-plane-only.
|
|
204
208
|
*/
|
|
205
209
|
deliverWebhook(token: string, body: unknown): Promise<RunResult | null>;
|
|
206
210
|
/** Side-effect-free read of a value a run published via `ctx.setEvent` (a live query). Control-plane-only. */
|
|
@@ -412,6 +416,68 @@ declare class DurableAuthController {
|
|
|
412
416
|
private runHook;
|
|
413
417
|
}
|
|
414
418
|
|
|
419
|
+
/** The env var the token gate reads when no `token` option is given. */
|
|
420
|
+
declare const DURABLE_DASHBOARD_TOKEN_ENV = "DURABLE_DASHBOARD_TOKEN";
|
|
421
|
+
/** DI token carrying the resolved {@link DashboardGate} (one per dashboard mount). */
|
|
422
|
+
declare const DASHBOARD_GATE: unique symbol;
|
|
423
|
+
/**
|
|
424
|
+
* What the module knows about how the console is protected, captured at `forRoot`/`forRootAsync`
|
|
425
|
+
* time. Everything that can change between module definition and boot (env vars, the async
|
|
426
|
+
* `dashboardAuth` factory) is read later, in {@link resolveDashboardGate}.
|
|
427
|
+
*/
|
|
428
|
+
interface DashboardGateOptions {
|
|
429
|
+
/** Host `guards` were passed (non-empty). */
|
|
430
|
+
hasGuards: boolean;
|
|
431
|
+
/** `allowUnauthenticated` as passed. */
|
|
432
|
+
allowUnauthenticated?: boolean;
|
|
433
|
+
/** `token` as passed (else `DURABLE_DASHBOARD_TOKEN` is read at boot). */
|
|
434
|
+
token?: string;
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* How the gate treats every console request:
|
|
438
|
+
*
|
|
439
|
+
* - `delegated`: `guards` or `dashboardAuth` protect the console; the gate passes everything.
|
|
440
|
+
* - `open`: `allowUnauthenticated: true`; the gate passes everything (warned at boot).
|
|
441
|
+
* - `token`: a request must carry the token (`Authorization: Bearer`, `x-durable-token`, or
|
|
442
|
+
* `?token=` on GET/HEAD).
|
|
443
|
+
* - `unconfigured`: nothing was configured outside production; the gate passes everything
|
|
444
|
+
* (warned at boot, because production will deny).
|
|
445
|
+
* - `deny`: nothing was configured and `NODE_ENV=production`; every request is refused.
|
|
446
|
+
*/
|
|
447
|
+
type DashboardGateMode = 'delegated' | 'open' | 'token' | 'unconfigured' | 'deny';
|
|
448
|
+
interface DashboardGate {
|
|
449
|
+
mode: DashboardGateMode;
|
|
450
|
+
/** Set only when `mode === 'token'`. */
|
|
451
|
+
token?: string;
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* Decide the gate mode. Precedence: `guards`/`dashboardAuth` (they are the gate) > a token > the
|
|
455
|
+
* `allowUnauthenticated` opt-out > the environment default. A token beats `allowUnauthenticated`
|
|
456
|
+
* because a configured secret is never silently ignored.
|
|
457
|
+
*/
|
|
458
|
+
declare function resolveDashboardGate(options: DashboardGateOptions, auth: ResolvedDashboardAuth | null): DashboardGate;
|
|
459
|
+
/** Log the one boot line the resolved gate calls for (nothing for a properly configured console). */
|
|
460
|
+
declare function reportDashboardGate(gate: DashboardGate, options: DashboardGateOptions, logger?: Pick<Logger, 'warn' | 'error'>): void;
|
|
461
|
+
/** The `DASHBOARD_GATE` provider: resolves once, after `DASHBOARD_AUTH`, and logs once. */
|
|
462
|
+
declare function dashboardGateProvider(options: DashboardGateOptions): Provider;
|
|
463
|
+
/**
|
|
464
|
+
* Read the token from `Authorization: Bearer <token>`, `x-durable-token`, or (GET/HEAD only) a
|
|
465
|
+
* `token` query param. The query channel is read-only on purpose: a `?token=` URL ends up in access
|
|
466
|
+
* logs, `Referer` headers and browser history, and must never authorize a mutation.
|
|
467
|
+
*/
|
|
468
|
+
declare function readDashboardToken(request: unknown): string | undefined;
|
|
469
|
+
/**
|
|
470
|
+
* The fail-closed default gate, stamped on `DurableUiController` and `DurableApiController` (after
|
|
471
|
+
* the built-in session guard, before host `guards`) whenever the module can't prove at definition
|
|
472
|
+
* time that something else protects the console. It never gates `DurableAuthController`, so the
|
|
473
|
+
* login/session/logout routes stay reachable. See {@link DashboardGateMode} for what each mode does.
|
|
474
|
+
*/
|
|
475
|
+
declare class DurableDashboardGateGuard implements CanActivate {
|
|
476
|
+
private readonly gate;
|
|
477
|
+
constructor(gate: DashboardGate);
|
|
478
|
+
canActivate(context: ExecutionContext): boolean;
|
|
479
|
+
}
|
|
480
|
+
|
|
415
481
|
interface DurableDashboardOptions {
|
|
416
482
|
/**
|
|
417
483
|
* Where the SPA (UI) is served. Default `/durable`. This is a page route — keep it out of an
|
|
@@ -428,8 +494,10 @@ interface DurableDashboardOptions {
|
|
|
428
494
|
* Guard classes fronting BOTH dashboard controllers (the SPA at `basePath` and its JSON API at
|
|
429
495
|
* `apiBasePath`). Stamped onto each controller via `@nestjs/common`'s own `@UseGuards` metadata key
|
|
430
496
|
* — REPLACE semantics, so a second `forRoot(...)` call overwrites (not appends to) whatever a prior
|
|
431
|
-
* call stamped, same as re-applying `@UseGuards` by hand.
|
|
432
|
-
*
|
|
497
|
+
* call stamped, same as re-applying `@UseGuards` by hand. A non-empty `guards` counts as the
|
|
498
|
+
* console's authentication, so the fail-closed default gate steps aside (see
|
|
499
|
+
* {@link allowUnauthenticated}). If the host fronts the routes another way (a global guard,
|
|
500
|
+
* reverse-proxy auth), say so with `allowUnauthenticated: true` instead.
|
|
433
501
|
*
|
|
434
502
|
* A guard's own DEPENDENCIES resolve from this module's `imports` (see {@link imports}) — the
|
|
435
503
|
* dashboard module has no application context of its own to pull them from otherwise.
|
|
@@ -490,15 +558,36 @@ interface DurableDashboardOptions {
|
|
|
490
558
|
*
|
|
491
559
|
* Composes with {@link guards}: when both are set, a request must pass the built-in session check
|
|
492
560
|
* AND every guard in `guards` — the SAME logical-AND semantics `@UseGuards` already gives multiple
|
|
493
|
-
* guards, since the built-in guard is simply prepended to whatever `guards` you pass.
|
|
494
|
-
* the console
|
|
495
|
-
*
|
|
561
|
+
* guards, since the built-in guard is simply prepended to whatever `guards` you pass. Setting it
|
|
562
|
+
* counts as the console's authentication (the fail-closed default gate steps aside). Omit it and
|
|
563
|
+
* configure `guards`, a {@link token}, or {@link allowUnauthenticated} instead.
|
|
496
564
|
*
|
|
497
565
|
* See {@link DashboardAuthOptions} for the `secret`/`ttl`/`session`/`login`/`revalidate` shape,
|
|
498
566
|
* and `forRootAsync` / {@link DurableDashboardAsyncOptions.useDashboardAuth} when a hook needs DI
|
|
499
567
|
* (e.g. an `EntityManager` to look up an admin user).
|
|
500
568
|
*/
|
|
501
569
|
dashboardAuth?: DashboardAuthOptions;
|
|
570
|
+
/**
|
|
571
|
+
* Explicitly serve the console with NO authentication from this module. Use it only when
|
|
572
|
+
* something outside the module already protects it: a global guard, a reverse proxy / SSO in
|
|
573
|
+
* front of the mount, a private network. Logs a boot warning.
|
|
574
|
+
*
|
|
575
|
+
* Without it, an unconfigured console (no `guards`, no `dashboardAuth`, no token) is DENIED when
|
|
576
|
+
* `NODE_ENV=production` (every UI and API request answers `403`, and one boot error explains how
|
|
577
|
+
* to configure it). Outside production it is still served open, with a boot warning. Ignored when
|
|
578
|
+
* `guards`, `dashboardAuth` or a token is configured.
|
|
579
|
+
*/
|
|
580
|
+
allowUnauthenticated?: boolean;
|
|
581
|
+
/**
|
|
582
|
+
* Bearer-token gate for the console. Defaults to `process.env.DURABLE_DASHBOARD_TOKEN` (read at
|
|
583
|
+
* boot). Only applies when neither `guards` nor `dashboardAuth` is configured: every UI and API
|
|
584
|
+
* request must then carry it as `Authorization: Bearer <token>`, an `x-durable-token` header, or
|
|
585
|
+
* (GET/HEAD only) a `?token=` query param, compared in constant time; anything else gets `401`.
|
|
586
|
+
*
|
|
587
|
+
* Meant for scripts and reverse proxies that inject the header: the bundled SPA does not attach
|
|
588
|
+
* the token to its own asset and API requests. Browser operators want `dashboardAuth` or `guards`.
|
|
589
|
+
*/
|
|
590
|
+
token?: string;
|
|
502
591
|
}
|
|
503
592
|
/**
|
|
504
593
|
* Async variant of {@link DurableDashboardOptions}. The mount paths and `guards` stay static (the
|
|
@@ -506,7 +595,9 @@ interface DurableDashboardOptions {
|
|
|
506
595
|
* already has), but `dashboardAuth` is built by an injected factory, so its `session`/`login` hooks
|
|
507
596
|
* can reach your DB/services — e.g. Mode A's `session` hook calling out to the same auth service
|
|
508
597
|
* your host app already uses to validate the raw request. Returning `undefined` from
|
|
509
|
-
* `useDashboardAuth`
|
|
598
|
+
* `useDashboardAuth` is exactly like omitting `dashboardAuth` from `forRoot`: the console then
|
|
599
|
+
* falls back to `guards` / the token / `allowUnauthenticated`, and is DENIED in production when
|
|
600
|
+
* none of those is configured.
|
|
510
601
|
*/
|
|
511
602
|
interface DurableDashboardAsyncOptions {
|
|
512
603
|
basePath?: string;
|
|
@@ -515,8 +606,12 @@ interface DurableDashboardAsyncOptions {
|
|
|
515
606
|
imports?: DynamicModule['imports'];
|
|
516
607
|
/** Providers injected into `useDashboardAuth`, in order. Shared with a `guards` class's own deps. */
|
|
517
608
|
inject?: Array<InjectionToken | OptionalFactoryDependency>;
|
|
518
|
-
/** Build the `dashboardAuth` config from injected deps (or `undefined`
|
|
609
|
+
/** Build the `dashboardAuth` config from injected deps (or `undefined` for no session auth). */
|
|
519
610
|
useDashboardAuth: (...deps: any[]) => DashboardAuthOptions | undefined | Promise<DashboardAuthOptions | undefined>;
|
|
611
|
+
/** See {@link DurableDashboardOptions.allowUnauthenticated}. */
|
|
612
|
+
allowUnauthenticated?: boolean;
|
|
613
|
+
/** See {@link DurableDashboardOptions.token}. */
|
|
614
|
+
token?: string;
|
|
520
615
|
}
|
|
521
616
|
/**
|
|
522
617
|
* Holds the JSON API controller + its read service, mounted on its own path by `forRoot`. Dynamic:
|
|
@@ -536,6 +631,10 @@ declare class DurableApiModule {
|
|
|
536
631
|
* (true whenever `dashboardAuth` was set, or unconditionally for `forRootAsync`, where it's
|
|
537
632
|
* resolved at runtime and is a no-op if it ends up unconfigured). */
|
|
538
633
|
authMayBeConfigured: boolean;
|
|
634
|
+
/** What the fail-closed gate needs; resolved into `DASHBOARD_GATE` at boot (logged once). */
|
|
635
|
+
gate: DashboardGateOptions;
|
|
636
|
+
/** Whether `DurableDashboardGateGuard` is stamped (false when guards/dashboardAuth are static). */
|
|
637
|
+
stampGate: boolean;
|
|
539
638
|
}): DynamicModule;
|
|
540
639
|
}
|
|
541
640
|
/**
|
|
@@ -545,6 +644,8 @@ declare class DurableApiModule {
|
|
|
545
644
|
* so it resolves the engine and store. Front the routes with the first-class `guards` option
|
|
546
645
|
* (plus `imports` for the guards' own dependencies) — see {@link DurableDashboardOptions.guards} —
|
|
547
646
|
* or with the built-in `dashboardAuth` cookie login, or both.
|
|
647
|
+
* With none of those (and no `token` / `allowUnauthenticated`), the console fails closed when
|
|
648
|
+
* `NODE_ENV=production` — see {@link DurableDashboardOptions.allowUnauthenticated}.
|
|
548
649
|
*/
|
|
549
650
|
declare class DurableDashboardModule {
|
|
550
651
|
static forRoot(options?: DurableDashboardOptions): DynamicModule;
|
|
@@ -631,4 +732,4 @@ declare class DurableUiController {
|
|
|
631
732
|
asset(file: string): StreamableFile;
|
|
632
733
|
}
|
|
633
734
|
|
|
634
|
-
export { DASHBOARD_API_PATH, DASHBOARD_BASE_PATH, type DashboardAuthOptions, DashboardService, type DashboardSessionUser, DurableApiController, DurableApiModule, DurableApiSessionGuard, DurableAuthController, type DurableDashboardAsyncOptions, DurableDashboardModule, type DurableDashboardOptions, DurableUiController, DurableUiSessionGuard, type UnauthenticatedPageContext, type UnauthenticatedPageHook };
|
|
735
|
+
export { DASHBOARD_API_PATH, DASHBOARD_BASE_PATH, DASHBOARD_GATE, DURABLE_DASHBOARD_TOKEN_ENV, type DashboardAuthOptions, type DashboardGate, type DashboardGateMode, type DashboardGateOptions, DashboardService, type DashboardSessionUser, DurableApiController, DurableApiModule, DurableApiSessionGuard, DurableAuthController, type DurableDashboardAsyncOptions, DurableDashboardGateGuard, DurableDashboardModule, type DurableDashboardOptions, DurableUiController, DurableUiSessionGuard, type UnauthenticatedPageContext, type UnauthenticatedPageHook, dashboardGateProvider, readDashboardToken, reportDashboardGate, resolveDashboardGate };
|
package/dist/server/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ import * as rxjs from 'rxjs';
|
|
|
5
5
|
import { Observable } from 'rxjs';
|
|
6
6
|
import * as _dudousxd_nestjs_filter from '@dudousxd/nestjs-filter';
|
|
7
7
|
import { FilterRunner } from '@dudousxd/nestjs-filter';
|
|
8
|
-
import {
|
|
8
|
+
import { CanActivate, ExecutionContext, Provider, Logger, DynamicModule, Type, InjectionToken, OptionalFactoryDependency, StreamableFile } from '@nestjs/common';
|
|
9
9
|
|
|
10
10
|
/** The session user the host `login` hook returns to mint a cookie. */
|
|
11
11
|
interface DashboardSessionUser {
|
|
@@ -199,8 +199,12 @@ declare class DashboardService {
|
|
|
199
199
|
}) | null>;
|
|
200
200
|
/**
|
|
201
201
|
* Deliver a `ctx.webhook()` callback: turn an inbound POST (token + body) into the signal the
|
|
202
|
-
*
|
|
203
|
-
*
|
|
202
|
+
* run is (or will be) parked on. A callback that arrives BEFORE the run reaches `wait()` (the url
|
|
203
|
+
* was handed out and the run is still busy or sleeping) is buffered, and `wait()` consumes it, so
|
|
204
|
+
* the result is the run's current status. Returns `null` — mapped to 404 by the controller — for a
|
|
205
|
+
* token that is not a webhook token, whose run doesn't exist or is terminal, or whose wait already
|
|
206
|
+
* resolved (a duplicate): nothing is buffered for those, so a public endpoint can't leave junk
|
|
207
|
+
* rows behind or signal non-webhook tokens. Control-plane-only.
|
|
204
208
|
*/
|
|
205
209
|
deliverWebhook(token: string, body: unknown): Promise<RunResult | null>;
|
|
206
210
|
/** Side-effect-free read of a value a run published via `ctx.setEvent` (a live query). Control-plane-only. */
|
|
@@ -412,6 +416,68 @@ declare class DurableAuthController {
|
|
|
412
416
|
private runHook;
|
|
413
417
|
}
|
|
414
418
|
|
|
419
|
+
/** The env var the token gate reads when no `token` option is given. */
|
|
420
|
+
declare const DURABLE_DASHBOARD_TOKEN_ENV = "DURABLE_DASHBOARD_TOKEN";
|
|
421
|
+
/** DI token carrying the resolved {@link DashboardGate} (one per dashboard mount). */
|
|
422
|
+
declare const DASHBOARD_GATE: unique symbol;
|
|
423
|
+
/**
|
|
424
|
+
* What the module knows about how the console is protected, captured at `forRoot`/`forRootAsync`
|
|
425
|
+
* time. Everything that can change between module definition and boot (env vars, the async
|
|
426
|
+
* `dashboardAuth` factory) is read later, in {@link resolveDashboardGate}.
|
|
427
|
+
*/
|
|
428
|
+
interface DashboardGateOptions {
|
|
429
|
+
/** Host `guards` were passed (non-empty). */
|
|
430
|
+
hasGuards: boolean;
|
|
431
|
+
/** `allowUnauthenticated` as passed. */
|
|
432
|
+
allowUnauthenticated?: boolean;
|
|
433
|
+
/** `token` as passed (else `DURABLE_DASHBOARD_TOKEN` is read at boot). */
|
|
434
|
+
token?: string;
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* How the gate treats every console request:
|
|
438
|
+
*
|
|
439
|
+
* - `delegated`: `guards` or `dashboardAuth` protect the console; the gate passes everything.
|
|
440
|
+
* - `open`: `allowUnauthenticated: true`; the gate passes everything (warned at boot).
|
|
441
|
+
* - `token`: a request must carry the token (`Authorization: Bearer`, `x-durable-token`, or
|
|
442
|
+
* `?token=` on GET/HEAD).
|
|
443
|
+
* - `unconfigured`: nothing was configured outside production; the gate passes everything
|
|
444
|
+
* (warned at boot, because production will deny).
|
|
445
|
+
* - `deny`: nothing was configured and `NODE_ENV=production`; every request is refused.
|
|
446
|
+
*/
|
|
447
|
+
type DashboardGateMode = 'delegated' | 'open' | 'token' | 'unconfigured' | 'deny';
|
|
448
|
+
interface DashboardGate {
|
|
449
|
+
mode: DashboardGateMode;
|
|
450
|
+
/** Set only when `mode === 'token'`. */
|
|
451
|
+
token?: string;
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* Decide the gate mode. Precedence: `guards`/`dashboardAuth` (they are the gate) > a token > the
|
|
455
|
+
* `allowUnauthenticated` opt-out > the environment default. A token beats `allowUnauthenticated`
|
|
456
|
+
* because a configured secret is never silently ignored.
|
|
457
|
+
*/
|
|
458
|
+
declare function resolveDashboardGate(options: DashboardGateOptions, auth: ResolvedDashboardAuth | null): DashboardGate;
|
|
459
|
+
/** Log the one boot line the resolved gate calls for (nothing for a properly configured console). */
|
|
460
|
+
declare function reportDashboardGate(gate: DashboardGate, options: DashboardGateOptions, logger?: Pick<Logger, 'warn' | 'error'>): void;
|
|
461
|
+
/** The `DASHBOARD_GATE` provider: resolves once, after `DASHBOARD_AUTH`, and logs once. */
|
|
462
|
+
declare function dashboardGateProvider(options: DashboardGateOptions): Provider;
|
|
463
|
+
/**
|
|
464
|
+
* Read the token from `Authorization: Bearer <token>`, `x-durable-token`, or (GET/HEAD only) a
|
|
465
|
+
* `token` query param. The query channel is read-only on purpose: a `?token=` URL ends up in access
|
|
466
|
+
* logs, `Referer` headers and browser history, and must never authorize a mutation.
|
|
467
|
+
*/
|
|
468
|
+
declare function readDashboardToken(request: unknown): string | undefined;
|
|
469
|
+
/**
|
|
470
|
+
* The fail-closed default gate, stamped on `DurableUiController` and `DurableApiController` (after
|
|
471
|
+
* the built-in session guard, before host `guards`) whenever the module can't prove at definition
|
|
472
|
+
* time that something else protects the console. It never gates `DurableAuthController`, so the
|
|
473
|
+
* login/session/logout routes stay reachable. See {@link DashboardGateMode} for what each mode does.
|
|
474
|
+
*/
|
|
475
|
+
declare class DurableDashboardGateGuard implements CanActivate {
|
|
476
|
+
private readonly gate;
|
|
477
|
+
constructor(gate: DashboardGate);
|
|
478
|
+
canActivate(context: ExecutionContext): boolean;
|
|
479
|
+
}
|
|
480
|
+
|
|
415
481
|
interface DurableDashboardOptions {
|
|
416
482
|
/**
|
|
417
483
|
* Where the SPA (UI) is served. Default `/durable`. This is a page route — keep it out of an
|
|
@@ -428,8 +494,10 @@ interface DurableDashboardOptions {
|
|
|
428
494
|
* Guard classes fronting BOTH dashboard controllers (the SPA at `basePath` and its JSON API at
|
|
429
495
|
* `apiBasePath`). Stamped onto each controller via `@nestjs/common`'s own `@UseGuards` metadata key
|
|
430
496
|
* — REPLACE semantics, so a second `forRoot(...)` call overwrites (not appends to) whatever a prior
|
|
431
|
-
* call stamped, same as re-applying `@UseGuards` by hand.
|
|
432
|
-
*
|
|
497
|
+
* call stamped, same as re-applying `@UseGuards` by hand. A non-empty `guards` counts as the
|
|
498
|
+
* console's authentication, so the fail-closed default gate steps aside (see
|
|
499
|
+
* {@link allowUnauthenticated}). If the host fronts the routes another way (a global guard,
|
|
500
|
+
* reverse-proxy auth), say so with `allowUnauthenticated: true` instead.
|
|
433
501
|
*
|
|
434
502
|
* A guard's own DEPENDENCIES resolve from this module's `imports` (see {@link imports}) — the
|
|
435
503
|
* dashboard module has no application context of its own to pull them from otherwise.
|
|
@@ -490,15 +558,36 @@ interface DurableDashboardOptions {
|
|
|
490
558
|
*
|
|
491
559
|
* Composes with {@link guards}: when both are set, a request must pass the built-in session check
|
|
492
560
|
* AND every guard in `guards` — the SAME logical-AND semantics `@UseGuards` already gives multiple
|
|
493
|
-
* guards, since the built-in guard is simply prepended to whatever `guards` you pass.
|
|
494
|
-
* the console
|
|
495
|
-
*
|
|
561
|
+
* guards, since the built-in guard is simply prepended to whatever `guards` you pass. Setting it
|
|
562
|
+
* counts as the console's authentication (the fail-closed default gate steps aside). Omit it and
|
|
563
|
+
* configure `guards`, a {@link token}, or {@link allowUnauthenticated} instead.
|
|
496
564
|
*
|
|
497
565
|
* See {@link DashboardAuthOptions} for the `secret`/`ttl`/`session`/`login`/`revalidate` shape,
|
|
498
566
|
* and `forRootAsync` / {@link DurableDashboardAsyncOptions.useDashboardAuth} when a hook needs DI
|
|
499
567
|
* (e.g. an `EntityManager` to look up an admin user).
|
|
500
568
|
*/
|
|
501
569
|
dashboardAuth?: DashboardAuthOptions;
|
|
570
|
+
/**
|
|
571
|
+
* Explicitly serve the console with NO authentication from this module. Use it only when
|
|
572
|
+
* something outside the module already protects it: a global guard, a reverse proxy / SSO in
|
|
573
|
+
* front of the mount, a private network. Logs a boot warning.
|
|
574
|
+
*
|
|
575
|
+
* Without it, an unconfigured console (no `guards`, no `dashboardAuth`, no token) is DENIED when
|
|
576
|
+
* `NODE_ENV=production` (every UI and API request answers `403`, and one boot error explains how
|
|
577
|
+
* to configure it). Outside production it is still served open, with a boot warning. Ignored when
|
|
578
|
+
* `guards`, `dashboardAuth` or a token is configured.
|
|
579
|
+
*/
|
|
580
|
+
allowUnauthenticated?: boolean;
|
|
581
|
+
/**
|
|
582
|
+
* Bearer-token gate for the console. Defaults to `process.env.DURABLE_DASHBOARD_TOKEN` (read at
|
|
583
|
+
* boot). Only applies when neither `guards` nor `dashboardAuth` is configured: every UI and API
|
|
584
|
+
* request must then carry it as `Authorization: Bearer <token>`, an `x-durable-token` header, or
|
|
585
|
+
* (GET/HEAD only) a `?token=` query param, compared in constant time; anything else gets `401`.
|
|
586
|
+
*
|
|
587
|
+
* Meant for scripts and reverse proxies that inject the header: the bundled SPA does not attach
|
|
588
|
+
* the token to its own asset and API requests. Browser operators want `dashboardAuth` or `guards`.
|
|
589
|
+
*/
|
|
590
|
+
token?: string;
|
|
502
591
|
}
|
|
503
592
|
/**
|
|
504
593
|
* Async variant of {@link DurableDashboardOptions}. The mount paths and `guards` stay static (the
|
|
@@ -506,7 +595,9 @@ interface DurableDashboardOptions {
|
|
|
506
595
|
* already has), but `dashboardAuth` is built by an injected factory, so its `session`/`login` hooks
|
|
507
596
|
* can reach your DB/services — e.g. Mode A's `session` hook calling out to the same auth service
|
|
508
597
|
* your host app already uses to validate the raw request. Returning `undefined` from
|
|
509
|
-
* `useDashboardAuth`
|
|
598
|
+
* `useDashboardAuth` is exactly like omitting `dashboardAuth` from `forRoot`: the console then
|
|
599
|
+
* falls back to `guards` / the token / `allowUnauthenticated`, and is DENIED in production when
|
|
600
|
+
* none of those is configured.
|
|
510
601
|
*/
|
|
511
602
|
interface DurableDashboardAsyncOptions {
|
|
512
603
|
basePath?: string;
|
|
@@ -515,8 +606,12 @@ interface DurableDashboardAsyncOptions {
|
|
|
515
606
|
imports?: DynamicModule['imports'];
|
|
516
607
|
/** Providers injected into `useDashboardAuth`, in order. Shared with a `guards` class's own deps. */
|
|
517
608
|
inject?: Array<InjectionToken | OptionalFactoryDependency>;
|
|
518
|
-
/** Build the `dashboardAuth` config from injected deps (or `undefined`
|
|
609
|
+
/** Build the `dashboardAuth` config from injected deps (or `undefined` for no session auth). */
|
|
519
610
|
useDashboardAuth: (...deps: any[]) => DashboardAuthOptions | undefined | Promise<DashboardAuthOptions | undefined>;
|
|
611
|
+
/** See {@link DurableDashboardOptions.allowUnauthenticated}. */
|
|
612
|
+
allowUnauthenticated?: boolean;
|
|
613
|
+
/** See {@link DurableDashboardOptions.token}. */
|
|
614
|
+
token?: string;
|
|
520
615
|
}
|
|
521
616
|
/**
|
|
522
617
|
* Holds the JSON API controller + its read service, mounted on its own path by `forRoot`. Dynamic:
|
|
@@ -536,6 +631,10 @@ declare class DurableApiModule {
|
|
|
536
631
|
* (true whenever `dashboardAuth` was set, or unconditionally for `forRootAsync`, where it's
|
|
537
632
|
* resolved at runtime and is a no-op if it ends up unconfigured). */
|
|
538
633
|
authMayBeConfigured: boolean;
|
|
634
|
+
/** What the fail-closed gate needs; resolved into `DASHBOARD_GATE` at boot (logged once). */
|
|
635
|
+
gate: DashboardGateOptions;
|
|
636
|
+
/** Whether `DurableDashboardGateGuard` is stamped (false when guards/dashboardAuth are static). */
|
|
637
|
+
stampGate: boolean;
|
|
539
638
|
}): DynamicModule;
|
|
540
639
|
}
|
|
541
640
|
/**
|
|
@@ -545,6 +644,8 @@ declare class DurableApiModule {
|
|
|
545
644
|
* so it resolves the engine and store. Front the routes with the first-class `guards` option
|
|
546
645
|
* (plus `imports` for the guards' own dependencies) — see {@link DurableDashboardOptions.guards} —
|
|
547
646
|
* or with the built-in `dashboardAuth` cookie login, or both.
|
|
647
|
+
* With none of those (and no `token` / `allowUnauthenticated`), the console fails closed when
|
|
648
|
+
* `NODE_ENV=production` — see {@link DurableDashboardOptions.allowUnauthenticated}.
|
|
548
649
|
*/
|
|
549
650
|
declare class DurableDashboardModule {
|
|
550
651
|
static forRoot(options?: DurableDashboardOptions): DynamicModule;
|
|
@@ -631,4 +732,4 @@ declare class DurableUiController {
|
|
|
631
732
|
asset(file: string): StreamableFile;
|
|
632
733
|
}
|
|
633
734
|
|
|
634
|
-
export { DASHBOARD_API_PATH, DASHBOARD_BASE_PATH, type DashboardAuthOptions, DashboardService, type DashboardSessionUser, DurableApiController, DurableApiModule, DurableApiSessionGuard, DurableAuthController, type DurableDashboardAsyncOptions, DurableDashboardModule, type DurableDashboardOptions, DurableUiController, DurableUiSessionGuard, type UnauthenticatedPageContext, type UnauthenticatedPageHook };
|
|
735
|
+
export { DASHBOARD_API_PATH, DASHBOARD_BASE_PATH, DASHBOARD_GATE, DURABLE_DASHBOARD_TOKEN_ENV, type DashboardAuthOptions, type DashboardGate, type DashboardGateMode, type DashboardGateOptions, DashboardService, type DashboardSessionUser, DurableApiController, DurableApiModule, DurableApiSessionGuard, DurableAuthController, type DurableDashboardAsyncOptions, DurableDashboardGateGuard, DurableDashboardModule, type DurableDashboardOptions, DurableUiController, DurableUiSessionGuard, type UnauthenticatedPageContext, type UnauthenticatedPageHook, dashboardGateProvider, readDashboardToken, reportDashboardGate, resolveDashboardGate };
|