@dudousxd/nestjs-durable-dashboard 0.55.0 → 0.56.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/client/durable-client.d.ts +11 -0
- package/dist/client/durable-client.d.ts.map +1 -1
- package/dist/client/durable-client.js +32 -16
- package/dist/client/durable-client.js.map +1 -1
- package/dist/server/index.cjs +684 -350
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.d.cts +136 -40
- package/dist/server/index.d.ts +136 -40
- package/dist/server/index.js +597 -283
- package/dist/server/index.js.map +1 -1
- package/dist/spa/assets/{bench-DCclNvcZ.js → bench-BXhQP2W7.js} +1 -1
- package/dist/spa/assets/index-C6Ih-K3x.js +1 -0
- package/dist/spa/assets/index-D_rIGnea.css +1 -0
- package/dist/spa/assets/{index-B-wtX4GV.js → index-DwGUovEa.js} +15 -15
- package/dist/spa/assets/{preview-CdsjvOrK.js → preview-ekPMzj7t.js} +1 -1
- package/dist/spa/bench.html +3 -3
- package/dist/spa/index.html +3 -3
- package/dist/spa/preview.html +3 -3
- package/package.json +8 -2
- package/dist/spa/assets/index-CJdk28vt.css +0 -1
- package/dist/spa/assets/index-ChjKRQZx.js +0 -1
package/dist/server/index.d.cts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { HttpException, ExecutionContext, Type, ExceptionFilter, ArgumentsHost, CanActivate, Logger, DynamicModule, StreamableFile } from '@nestjs/common';
|
|
2
|
+
import { HttpAdapterHost, ModuleRef } from '@nestjs/core';
|
|
1
3
|
import * as _dudousxd_nestjs_durable_core from '@dudousxd/nestjs-durable-core';
|
|
2
4
|
import { RunGateway, StateStore, WorkflowEngine, ScheduleListQuery, ScheduleDescription, RunResult, RunQuery, RunListItem, RunFacetQuery, RunFacetRow, RunValueAxis, RunValueFacetOptions, RunValueFacetRow, DurableTopology, GroupHealth, RunDetailOptions, RunDetail, FanPageQuery, FanPage, RepublishLifecycleResult, WorkflowLifecycleLinks, UpdateResult, EngineEvent, AttributeFilter } from '@dudousxd/nestjs-durable-core';
|
|
3
5
|
export { RunDetail } from '@dudousxd/nestjs-durable-core';
|
|
@@ -5,7 +7,102 @@ import * as rxjs from 'rxjs';
|
|
|
5
7
|
import { Observable } from 'rxjs';
|
|
6
8
|
import * as _dudousxd_nestjs_filter from '@dudousxd/nestjs-filter';
|
|
7
9
|
import { FilterRunner } from '@dudousxd/nestjs-filter';
|
|
8
|
-
|
|
10
|
+
|
|
11
|
+
type ConsoleAuthorizeResult = boolean | {
|
|
12
|
+
allowed: true;
|
|
13
|
+
} | {
|
|
14
|
+
allowed: false;
|
|
15
|
+
reason?: 'unauthenticated' | 'forbidden';
|
|
16
|
+
} | {
|
|
17
|
+
redirectTo: string;
|
|
18
|
+
};
|
|
19
|
+
/** Class form: an `@Injectable()` provider (constructor injection works). */
|
|
20
|
+
interface ConsoleAuthorizer {
|
|
21
|
+
authorize(context: ExecutionContext): ConsoleAuthorizeResult | Promise<ConsoleAuthorizeResult>;
|
|
22
|
+
}
|
|
23
|
+
type ConsoleAuthorizerClass = Type<ConsoleAuthorizer>;
|
|
24
|
+
/** An `@nestjs/authorization` policy check: `policy[ability](user)`. */
|
|
25
|
+
interface ConsolePolicyCheck {
|
|
26
|
+
policy: Type<unknown>;
|
|
27
|
+
ability: string;
|
|
28
|
+
}
|
|
29
|
+
type ConsoleAuthorize = ((context: ExecutionContext) => ConsoleAuthorizeResult | Promise<ConsoleAuthorizeResult>) | ConsoleAuthorizerClass | (() => Promise<{
|
|
30
|
+
default: ConsoleAuthorizerClass;
|
|
31
|
+
}>) | ConsolePolicyCheck | string;
|
|
32
|
+
type ConsoleDecision = {
|
|
33
|
+
allowed: true;
|
|
34
|
+
} | {
|
|
35
|
+
allowed: false;
|
|
36
|
+
reason: 'unauthenticated' | 'forbidden';
|
|
37
|
+
redirectTo?: string;
|
|
38
|
+
};
|
|
39
|
+
/** What a resolved `authorize` is: called once per request, never throws. */
|
|
40
|
+
type ConsoleDecide = (context: ExecutionContext) => Promise<ConsoleDecision>;
|
|
41
|
+
/**
|
|
42
|
+
* The provider `@dudousxd/nestjs-authz` registers to resolve gate names (`authorize: 'console.x'`).
|
|
43
|
+
* A registry symbol, so it is found without depending on that package.
|
|
44
|
+
*/
|
|
45
|
+
declare const CONSOLE_GATE: unique symbol;
|
|
46
|
+
interface ConsoleGate {
|
|
47
|
+
allows(name: string, context: ExecutionContext): Promise<ConsoleAuthorizeResult>;
|
|
48
|
+
}
|
|
49
|
+
/** The one boot warning for a console with no `authorize`. Same wording in every Aviary console. */
|
|
50
|
+
declare const CONSOLE_AUTHORIZE_MISSING_WARNING = "[@dudousxd/nestjs-durable-dashboard] console has no `authorize` configured: it is open outside production and denies every request in production. Set `authorize` in DurableDashboardModule.forRoot().";
|
|
51
|
+
/** Turns any {@link ConsoleAuthorizeResult} into a {@link ConsoleDecision}. */
|
|
52
|
+
declare function normalizeConsoleResult(result: unknown): ConsoleDecision;
|
|
53
|
+
/**
|
|
54
|
+
* Resolves `authorize` into one function the guard calls per request. Resolution errors that are
|
|
55
|
+
* configuration mistakes (a gate name without nestjs-authz, a policy without @nestjs/authorization)
|
|
56
|
+
* throw here, so call it at boot. The returned function never throws: an error is a `forbidden`.
|
|
57
|
+
*
|
|
58
|
+
* `authorize` omitted: open outside production, `forbidden` for every request in production (read
|
|
59
|
+
* when this runs), and one warning logged (unless `warnIfMissing: false`).
|
|
60
|
+
*/
|
|
61
|
+
declare function resolveConsoleAuthorize(authorize: ConsoleAuthorize | undefined, moduleRef: ModuleRef, options?: {
|
|
62
|
+
logger?: Logger;
|
|
63
|
+
warnIfMissing?: boolean;
|
|
64
|
+
}): Promise<ConsoleDecide>;
|
|
65
|
+
/**
|
|
66
|
+
* Allows a request carrying `Authorization: Bearer <token>` (compared timing-safe); anything else
|
|
67
|
+
* is `unauthenticated`. Header only: no query string, no custom header. Throws when `token` is
|
|
68
|
+
* empty, so a missing env var fails at config time instead of opening the console.
|
|
69
|
+
*/
|
|
70
|
+
declare function authorizeByToken(options: {
|
|
71
|
+
token: string | undefined;
|
|
72
|
+
}): ConsoleAuthorize;
|
|
73
|
+
/** DI token of the resolved {@link ConsoleDecide} the guard runs. */
|
|
74
|
+
declare const CONSOLE_AUTHORIZE_DECIDE: unique symbol;
|
|
75
|
+
/** Marks a controller as serving pages: a denied `GET` that accepts HTML gets a page, not JSON. */
|
|
76
|
+
declare const CONSOLE_PAGE_METADATA = "@dudousxd/nestjs-durable-dashboard:console-page";
|
|
77
|
+
declare const ConsolePage: () => ClassDecorator;
|
|
78
|
+
/**
|
|
79
|
+
* The denial the guard throws. An `HttpException` with the JSON body, so it still answers correctly
|
|
80
|
+
* where {@link ConsoleAuthorizeFilter} is not applied; the filter adds the page answers.
|
|
81
|
+
*/
|
|
82
|
+
declare class ConsoleAccessDenied extends HttpException {
|
|
83
|
+
readonly decision: Extract<ConsoleDecision, {
|
|
84
|
+
allowed: false;
|
|
85
|
+
}>;
|
|
86
|
+
readonly page: boolean;
|
|
87
|
+
constructor(decision: Extract<ConsoleDecision, {
|
|
88
|
+
allowed: false;
|
|
89
|
+
}>, page: boolean);
|
|
90
|
+
}
|
|
91
|
+
declare class ConsoleAuthorizeGuard implements CanActivate {
|
|
92
|
+
private readonly decide;
|
|
93
|
+
constructor(decide: ConsoleDecide);
|
|
94
|
+
canActivate(context: ExecutionContext): Promise<boolean>;
|
|
95
|
+
}
|
|
96
|
+
declare class ConsoleAuthorizeFilter implements ExceptionFilter {
|
|
97
|
+
private readonly adapterHost;
|
|
98
|
+
constructor(adapterHost: HttpAdapterHost);
|
|
99
|
+
catch(exception: ConsoleAccessDenied, host: ArgumentsHost): void;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* The guard and its filter as one class decorator, for a console you mount yourself
|
|
103
|
+
* (`routes: false`). List it ABOVE your authentication guards, so it runs after them.
|
|
104
|
+
*/
|
|
105
|
+
declare const UseConsoleAuthorize: () => ClassDecorator & MethodDecorator;
|
|
9
106
|
|
|
10
107
|
/**
|
|
11
108
|
* Read-model and actions backing the control-plane UI. Run-facing ops (list/detail/retry/cancel/
|
|
@@ -345,6 +442,23 @@ declare class DurableApiController extends DurableApiControllerBase {
|
|
|
345
442
|
}
|
|
346
443
|
|
|
347
444
|
interface DurableDashboardOptions {
|
|
445
|
+
/**
|
|
446
|
+
* Who may use the console (the SPA, its JSON API, the run stream and the webhook route). One of:
|
|
447
|
+
*
|
|
448
|
+
* - a function `(context) => boolean | { allowed } | { redirectTo }` (may be async);
|
|
449
|
+
* - an `@Injectable()` class implementing {@link ConsoleAuthorizer} (constructor injection works;
|
|
450
|
+
* its dependencies resolve from {@link imports} or global modules);
|
|
451
|
+
* - a lazy import `() => import('./console-access.js')` whose default export is such a class;
|
|
452
|
+
* - `{ policy, ability }`, checked with `@nestjs/authorization`'s `AuthorizationService`;
|
|
453
|
+
* - a gate name, resolved by `@dudousxd/nestjs-authz`'s `AuthzModule`.
|
|
454
|
+
*
|
|
455
|
+
* `authorizeByToken({ token })` builds one for a bearer token. It runs after the guards in
|
|
456
|
+
* {@link decorators}, so `request.user` is already set.
|
|
457
|
+
*
|
|
458
|
+
* Omitted: the console is open outside production and answers `403` to every request when
|
|
459
|
+
* `NODE_ENV=production`, and one warning is logged at boot.
|
|
460
|
+
*/
|
|
461
|
+
authorize?: ConsoleAuthorize;
|
|
348
462
|
/**
|
|
349
463
|
* Where the SPA (UI) is served. Default `/durable`. This is a page route — keep it out of an
|
|
350
464
|
* `/api` prefix so it reads as a UI, not an endpoint.
|
|
@@ -372,26 +486,26 @@ interface DurableDashboardOptions {
|
|
|
372
486
|
* another registration or onto the exported classes. A guard named in `UseGuards(...)` resolves
|
|
373
487
|
* its dependencies from {@link imports} (or a global module).
|
|
374
488
|
*
|
|
375
|
-
*
|
|
376
|
-
*
|
|
377
|
-
* {@link DurableDashboardModule}. The module cannot tell a `Can(...)` from a decorator that
|
|
378
|
-
* protects nothing, so passing one is your statement that the console is protected.
|
|
489
|
+
* `decorators` is not access control on its own: {@link authorize} decides who gets in, and runs
|
|
490
|
+
* after these. Use `decorators` for what must run first, typically authentication.
|
|
379
491
|
*
|
|
380
|
-
*
|
|
381
|
-
*
|
|
382
|
-
*
|
|
492
|
+
* The controllers are marked `Can.Anyone()` for `@nestjs/authorization`, whose global guard
|
|
493
|
+
* otherwise refuses every route that declares no `@Can()`. A `@Can()` passed here is still
|
|
494
|
+
* enforced.
|
|
383
495
|
*/
|
|
384
496
|
decorators?: ClassDecorator[];
|
|
385
497
|
/**
|
|
386
498
|
* `false` mounts NO controllers: the SPA and its JSON API are not registered,
|
|
387
499
|
* while every provider still is (the run read/action service, the run filter, the SPA's base
|
|
388
|
-
* paths). Serve the console from controllers of your own instead,
|
|
389
|
-
*
|
|
500
|
+
* paths). Serve the console from controllers of your own instead, by extending
|
|
501
|
+
* {@link DurableUiControllerBase} and {@link DurableApiControllerBase}. Apply `authorize` to them
|
|
502
|
+
* with `@UseConsoleAuthorize()` (listed above your authentication guards, so it runs after them),
|
|
503
|
+
* or protect them with ordinary Nest decorators:
|
|
390
504
|
*
|
|
391
505
|
* ```ts
|
|
392
|
-
* @Controller('ops/durable') @
|
|
506
|
+
* @Controller('ops/durable') @UseConsoleAuthorize() @UseGuards(AuthenticationGuard)
|
|
393
507
|
* class ConsoleUi extends DurableUiControllerBase {}
|
|
394
|
-
* @Controller('ops/durable/api') @
|
|
508
|
+
* @Controller('ops/durable/api') @UseConsoleAuthorize() @UseGuards(AuthenticationGuard)
|
|
395
509
|
* class ConsoleApi extends DurableApiControllerBase {}
|
|
396
510
|
* // in the module that declares them:
|
|
397
511
|
* imports: [DurableDashboardModule.forRoot({ routes: false, basePath: '/ops/durable', apiBasePath: '/ops/durable/api' })]
|
|
@@ -400,8 +514,9 @@ interface DurableDashboardOptions {
|
|
|
400
514
|
* `basePath`/`apiBasePath` still matter here: they are where you mounted the two controllers, and
|
|
401
515
|
* the SPA is told to load its assets from `basePath` and fetch from `apiBasePath`.
|
|
402
516
|
*
|
|
403
|
-
*
|
|
404
|
-
*
|
|
517
|
+
* `authorize` omitted works as for mounted routes: `@UseConsoleAuthorize()` lets every request
|
|
518
|
+
* through outside production and answers `403` in production, and the boot warning is logged.
|
|
519
|
+
* The `*Base` classes carry no
|
|
405
520
|
* guards and no `Can.Anyone()` mark, so under `@nestjs/authorization`'s global guard a subclass
|
|
406
521
|
* without a `@Can()` is denied. `decorators` throws when combined with `routes: false` rather than
|
|
407
522
|
* being silently ignored.
|
|
@@ -411,10 +526,6 @@ interface DurableDashboardOptions {
|
|
|
411
526
|
*/
|
|
412
527
|
routes?: boolean;
|
|
413
528
|
}
|
|
414
|
-
/** DI token of the boot-time check that refuses an unprotected console in production. */
|
|
415
|
-
declare const DASHBOARD_PROTECTION_CHECK: unique symbol;
|
|
416
|
-
/** The boot error for a mounted console with no `decorators` under `NODE_ENV=production`. */
|
|
417
|
-
declare const UNPROTECTED_CONSOLE_IN_PRODUCTION: string;
|
|
418
529
|
/**
|
|
419
530
|
* Holds the JSON API controller + its read service, mounted on its own path by `forRoot`. Its own
|
|
420
531
|
* dynamic module because enhancers (a guard from `decorators`) DI-instantiate from their
|
|
@@ -427,34 +538,19 @@ declare class DurableApiModule {
|
|
|
427
538
|
routes?: boolean;
|
|
428
539
|
/** See {@link DurableDashboardOptions.decorators}. */
|
|
429
540
|
decorators?: ClassDecorator[];
|
|
541
|
+
/** See {@link DurableDashboardOptions.authorize}. */
|
|
542
|
+
authorize?: ConsoleAuthorize;
|
|
430
543
|
}): DynamicModule;
|
|
431
544
|
}
|
|
432
|
-
/**
|
|
433
|
-
* Mounts the control plane: the bundled React SPA at `basePath` and its JSON API at `apiBasePath`
|
|
434
|
-
* (default `<basePath>/api`). Import via `DurableDashboardModule.forRoot(...)` (or `forRootAsync`
|
|
435
|
-
* when `dashboardAuth`'s `login` hook needs injected services) alongside `DurableModule` (global),
|
|
436
|
-
* so it resolves the engine and store. Front the routes with the first-class `guards` option
|
|
437
|
-
* (plus `imports` for the guards' own dependencies) — see {@link DurableDashboardOptions.guards} —
|
|
438
|
-
* or with the built-in `dashboardAuth` cookie login, or both.
|
|
439
|
-
* With none of those (and no `decorators` / `token` / `allowUnauthenticated`), the console fails
|
|
440
|
-
* closed when `NODE_ENV=production` — see {@link DurableDashboardOptions.allowUnauthenticated}.
|
|
441
|
-
* `routes: false` mounts nothing and leaves serving the console to the host's own controllers — see
|
|
442
|
-
* {@link DurableDashboardOptions.routes}.
|
|
443
|
-
*/
|
|
444
545
|
/**
|
|
445
546
|
* Mounts the control plane: the bundled React SPA at `basePath` and its JSON API at `apiBasePath`
|
|
446
547
|
* (default `<basePath>/api`). Import via `DurableDashboardModule.forRoot(...)` alongside
|
|
447
548
|
* `DurableModule` (global), so it resolves the engine and store.
|
|
448
549
|
*
|
|
449
|
-
*
|
|
450
|
-
*
|
|
451
|
-
*
|
|
452
|
-
*
|
|
453
|
-
* 2. `routes: false`, and mount it yourself by extending `DurableUiControllerBase` /
|
|
454
|
-
* `DurableApiControllerBase` (see {@link DurableDashboardOptions.routes}).
|
|
455
|
-
*
|
|
456
|
-
* With `NODE_ENV=production`, a mounted console with no `decorators` fails the boot with
|
|
457
|
-
* {@link UNPROTECTED_CONSOLE_IN_PRODUCTION}. Outside production it is served open, with a warning.
|
|
550
|
+
* Access is decided by {@link DurableDashboardOptions.authorize}. Without it the console is open
|
|
551
|
+
* outside production and denies every request in production, with one warning at boot.
|
|
552
|
+
* `routes: false` mounts nothing and leaves serving the console to the host's own controllers (see
|
|
553
|
+
* {@link DurableDashboardOptions.routes}).
|
|
458
554
|
*/
|
|
459
555
|
declare class DurableDashboardModule {
|
|
460
556
|
static forRoot(options?: DurableDashboardOptions): DynamicModule;
|
|
@@ -492,4 +588,4 @@ declare class DurableUiControllerBase {
|
|
|
492
588
|
declare class DurableUiController extends DurableUiControllerBase {
|
|
493
589
|
}
|
|
494
590
|
|
|
495
|
-
export { DASHBOARD_API_PATH, DASHBOARD_BASE_PATH,
|
|
591
|
+
export { CONSOLE_AUTHORIZE_DECIDE, CONSOLE_AUTHORIZE_MISSING_WARNING, CONSOLE_GATE, CONSOLE_PAGE_METADATA, ConsoleAccessDenied, type ConsoleAuthorize, ConsoleAuthorizeFilter, ConsoleAuthorizeGuard, type ConsoleAuthorizeResult, type ConsoleAuthorizer, type ConsoleAuthorizerClass, type ConsoleDecide, type ConsoleDecision, type ConsoleGate, ConsolePage, type ConsolePolicyCheck, DASHBOARD_API_PATH, DASHBOARD_BASE_PATH, DashboardService, DurableApiController, DurableApiControllerBase, DurableApiModule, DurableDashboardModule, type DurableDashboardOptions, DurableUiController, DurableUiControllerBase, UseConsoleAuthorize, type WorkflowLifecyclePage, authorizeByToken, normalizeConsoleResult, resolveConsoleAuthorize };
|
package/dist/server/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { HttpException, ExecutionContext, Type, ExceptionFilter, ArgumentsHost, CanActivate, Logger, DynamicModule, StreamableFile } from '@nestjs/common';
|
|
2
|
+
import { HttpAdapterHost, ModuleRef } from '@nestjs/core';
|
|
1
3
|
import * as _dudousxd_nestjs_durable_core from '@dudousxd/nestjs-durable-core';
|
|
2
4
|
import { RunGateway, StateStore, WorkflowEngine, ScheduleListQuery, ScheduleDescription, RunResult, RunQuery, RunListItem, RunFacetQuery, RunFacetRow, RunValueAxis, RunValueFacetOptions, RunValueFacetRow, DurableTopology, GroupHealth, RunDetailOptions, RunDetail, FanPageQuery, FanPage, RepublishLifecycleResult, WorkflowLifecycleLinks, UpdateResult, EngineEvent, AttributeFilter } from '@dudousxd/nestjs-durable-core';
|
|
3
5
|
export { RunDetail } from '@dudousxd/nestjs-durable-core';
|
|
@@ -5,7 +7,102 @@ import * as rxjs from 'rxjs';
|
|
|
5
7
|
import { Observable } from 'rxjs';
|
|
6
8
|
import * as _dudousxd_nestjs_filter from '@dudousxd/nestjs-filter';
|
|
7
9
|
import { FilterRunner } from '@dudousxd/nestjs-filter';
|
|
8
|
-
|
|
10
|
+
|
|
11
|
+
type ConsoleAuthorizeResult = boolean | {
|
|
12
|
+
allowed: true;
|
|
13
|
+
} | {
|
|
14
|
+
allowed: false;
|
|
15
|
+
reason?: 'unauthenticated' | 'forbidden';
|
|
16
|
+
} | {
|
|
17
|
+
redirectTo: string;
|
|
18
|
+
};
|
|
19
|
+
/** Class form: an `@Injectable()` provider (constructor injection works). */
|
|
20
|
+
interface ConsoleAuthorizer {
|
|
21
|
+
authorize(context: ExecutionContext): ConsoleAuthorizeResult | Promise<ConsoleAuthorizeResult>;
|
|
22
|
+
}
|
|
23
|
+
type ConsoleAuthorizerClass = Type<ConsoleAuthorizer>;
|
|
24
|
+
/** An `@nestjs/authorization` policy check: `policy[ability](user)`. */
|
|
25
|
+
interface ConsolePolicyCheck {
|
|
26
|
+
policy: Type<unknown>;
|
|
27
|
+
ability: string;
|
|
28
|
+
}
|
|
29
|
+
type ConsoleAuthorize = ((context: ExecutionContext) => ConsoleAuthorizeResult | Promise<ConsoleAuthorizeResult>) | ConsoleAuthorizerClass | (() => Promise<{
|
|
30
|
+
default: ConsoleAuthorizerClass;
|
|
31
|
+
}>) | ConsolePolicyCheck | string;
|
|
32
|
+
type ConsoleDecision = {
|
|
33
|
+
allowed: true;
|
|
34
|
+
} | {
|
|
35
|
+
allowed: false;
|
|
36
|
+
reason: 'unauthenticated' | 'forbidden';
|
|
37
|
+
redirectTo?: string;
|
|
38
|
+
};
|
|
39
|
+
/** What a resolved `authorize` is: called once per request, never throws. */
|
|
40
|
+
type ConsoleDecide = (context: ExecutionContext) => Promise<ConsoleDecision>;
|
|
41
|
+
/**
|
|
42
|
+
* The provider `@dudousxd/nestjs-authz` registers to resolve gate names (`authorize: 'console.x'`).
|
|
43
|
+
* A registry symbol, so it is found without depending on that package.
|
|
44
|
+
*/
|
|
45
|
+
declare const CONSOLE_GATE: unique symbol;
|
|
46
|
+
interface ConsoleGate {
|
|
47
|
+
allows(name: string, context: ExecutionContext): Promise<ConsoleAuthorizeResult>;
|
|
48
|
+
}
|
|
49
|
+
/** The one boot warning for a console with no `authorize`. Same wording in every Aviary console. */
|
|
50
|
+
declare const CONSOLE_AUTHORIZE_MISSING_WARNING = "[@dudousxd/nestjs-durable-dashboard] console has no `authorize` configured: it is open outside production and denies every request in production. Set `authorize` in DurableDashboardModule.forRoot().";
|
|
51
|
+
/** Turns any {@link ConsoleAuthorizeResult} into a {@link ConsoleDecision}. */
|
|
52
|
+
declare function normalizeConsoleResult(result: unknown): ConsoleDecision;
|
|
53
|
+
/**
|
|
54
|
+
* Resolves `authorize` into one function the guard calls per request. Resolution errors that are
|
|
55
|
+
* configuration mistakes (a gate name without nestjs-authz, a policy without @nestjs/authorization)
|
|
56
|
+
* throw here, so call it at boot. The returned function never throws: an error is a `forbidden`.
|
|
57
|
+
*
|
|
58
|
+
* `authorize` omitted: open outside production, `forbidden` for every request in production (read
|
|
59
|
+
* when this runs), and one warning logged (unless `warnIfMissing: false`).
|
|
60
|
+
*/
|
|
61
|
+
declare function resolveConsoleAuthorize(authorize: ConsoleAuthorize | undefined, moduleRef: ModuleRef, options?: {
|
|
62
|
+
logger?: Logger;
|
|
63
|
+
warnIfMissing?: boolean;
|
|
64
|
+
}): Promise<ConsoleDecide>;
|
|
65
|
+
/**
|
|
66
|
+
* Allows a request carrying `Authorization: Bearer <token>` (compared timing-safe); anything else
|
|
67
|
+
* is `unauthenticated`. Header only: no query string, no custom header. Throws when `token` is
|
|
68
|
+
* empty, so a missing env var fails at config time instead of opening the console.
|
|
69
|
+
*/
|
|
70
|
+
declare function authorizeByToken(options: {
|
|
71
|
+
token: string | undefined;
|
|
72
|
+
}): ConsoleAuthorize;
|
|
73
|
+
/** DI token of the resolved {@link ConsoleDecide} the guard runs. */
|
|
74
|
+
declare const CONSOLE_AUTHORIZE_DECIDE: unique symbol;
|
|
75
|
+
/** Marks a controller as serving pages: a denied `GET` that accepts HTML gets a page, not JSON. */
|
|
76
|
+
declare const CONSOLE_PAGE_METADATA = "@dudousxd/nestjs-durable-dashboard:console-page";
|
|
77
|
+
declare const ConsolePage: () => ClassDecorator;
|
|
78
|
+
/**
|
|
79
|
+
* The denial the guard throws. An `HttpException` with the JSON body, so it still answers correctly
|
|
80
|
+
* where {@link ConsoleAuthorizeFilter} is not applied; the filter adds the page answers.
|
|
81
|
+
*/
|
|
82
|
+
declare class ConsoleAccessDenied extends HttpException {
|
|
83
|
+
readonly decision: Extract<ConsoleDecision, {
|
|
84
|
+
allowed: false;
|
|
85
|
+
}>;
|
|
86
|
+
readonly page: boolean;
|
|
87
|
+
constructor(decision: Extract<ConsoleDecision, {
|
|
88
|
+
allowed: false;
|
|
89
|
+
}>, page: boolean);
|
|
90
|
+
}
|
|
91
|
+
declare class ConsoleAuthorizeGuard implements CanActivate {
|
|
92
|
+
private readonly decide;
|
|
93
|
+
constructor(decide: ConsoleDecide);
|
|
94
|
+
canActivate(context: ExecutionContext): Promise<boolean>;
|
|
95
|
+
}
|
|
96
|
+
declare class ConsoleAuthorizeFilter implements ExceptionFilter {
|
|
97
|
+
private readonly adapterHost;
|
|
98
|
+
constructor(adapterHost: HttpAdapterHost);
|
|
99
|
+
catch(exception: ConsoleAccessDenied, host: ArgumentsHost): void;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* The guard and its filter as one class decorator, for a console you mount yourself
|
|
103
|
+
* (`routes: false`). List it ABOVE your authentication guards, so it runs after them.
|
|
104
|
+
*/
|
|
105
|
+
declare const UseConsoleAuthorize: () => ClassDecorator & MethodDecorator;
|
|
9
106
|
|
|
10
107
|
/**
|
|
11
108
|
* Read-model and actions backing the control-plane UI. Run-facing ops (list/detail/retry/cancel/
|
|
@@ -345,6 +442,23 @@ declare class DurableApiController extends DurableApiControllerBase {
|
|
|
345
442
|
}
|
|
346
443
|
|
|
347
444
|
interface DurableDashboardOptions {
|
|
445
|
+
/**
|
|
446
|
+
* Who may use the console (the SPA, its JSON API, the run stream and the webhook route). One of:
|
|
447
|
+
*
|
|
448
|
+
* - a function `(context) => boolean | { allowed } | { redirectTo }` (may be async);
|
|
449
|
+
* - an `@Injectable()` class implementing {@link ConsoleAuthorizer} (constructor injection works;
|
|
450
|
+
* its dependencies resolve from {@link imports} or global modules);
|
|
451
|
+
* - a lazy import `() => import('./console-access.js')` whose default export is such a class;
|
|
452
|
+
* - `{ policy, ability }`, checked with `@nestjs/authorization`'s `AuthorizationService`;
|
|
453
|
+
* - a gate name, resolved by `@dudousxd/nestjs-authz`'s `AuthzModule`.
|
|
454
|
+
*
|
|
455
|
+
* `authorizeByToken({ token })` builds one for a bearer token. It runs after the guards in
|
|
456
|
+
* {@link decorators}, so `request.user` is already set.
|
|
457
|
+
*
|
|
458
|
+
* Omitted: the console is open outside production and answers `403` to every request when
|
|
459
|
+
* `NODE_ENV=production`, and one warning is logged at boot.
|
|
460
|
+
*/
|
|
461
|
+
authorize?: ConsoleAuthorize;
|
|
348
462
|
/**
|
|
349
463
|
* Where the SPA (UI) is served. Default `/durable`. This is a page route — keep it out of an
|
|
350
464
|
* `/api` prefix so it reads as a UI, not an endpoint.
|
|
@@ -372,26 +486,26 @@ interface DurableDashboardOptions {
|
|
|
372
486
|
* another registration or onto the exported classes. A guard named in `UseGuards(...)` resolves
|
|
373
487
|
* its dependencies from {@link imports} (or a global module).
|
|
374
488
|
*
|
|
375
|
-
*
|
|
376
|
-
*
|
|
377
|
-
* {@link DurableDashboardModule}. The module cannot tell a `Can(...)` from a decorator that
|
|
378
|
-
* protects nothing, so passing one is your statement that the console is protected.
|
|
489
|
+
* `decorators` is not access control on its own: {@link authorize} decides who gets in, and runs
|
|
490
|
+
* after these. Use `decorators` for what must run first, typically authentication.
|
|
379
491
|
*
|
|
380
|
-
*
|
|
381
|
-
*
|
|
382
|
-
*
|
|
492
|
+
* The controllers are marked `Can.Anyone()` for `@nestjs/authorization`, whose global guard
|
|
493
|
+
* otherwise refuses every route that declares no `@Can()`. A `@Can()` passed here is still
|
|
494
|
+
* enforced.
|
|
383
495
|
*/
|
|
384
496
|
decorators?: ClassDecorator[];
|
|
385
497
|
/**
|
|
386
498
|
* `false` mounts NO controllers: the SPA and its JSON API are not registered,
|
|
387
499
|
* while every provider still is (the run read/action service, the run filter, the SPA's base
|
|
388
|
-
* paths). Serve the console from controllers of your own instead,
|
|
389
|
-
*
|
|
500
|
+
* paths). Serve the console from controllers of your own instead, by extending
|
|
501
|
+
* {@link DurableUiControllerBase} and {@link DurableApiControllerBase}. Apply `authorize` to them
|
|
502
|
+
* with `@UseConsoleAuthorize()` (listed above your authentication guards, so it runs after them),
|
|
503
|
+
* or protect them with ordinary Nest decorators:
|
|
390
504
|
*
|
|
391
505
|
* ```ts
|
|
392
|
-
* @Controller('ops/durable') @
|
|
506
|
+
* @Controller('ops/durable') @UseConsoleAuthorize() @UseGuards(AuthenticationGuard)
|
|
393
507
|
* class ConsoleUi extends DurableUiControllerBase {}
|
|
394
|
-
* @Controller('ops/durable/api') @
|
|
508
|
+
* @Controller('ops/durable/api') @UseConsoleAuthorize() @UseGuards(AuthenticationGuard)
|
|
395
509
|
* class ConsoleApi extends DurableApiControllerBase {}
|
|
396
510
|
* // in the module that declares them:
|
|
397
511
|
* imports: [DurableDashboardModule.forRoot({ routes: false, basePath: '/ops/durable', apiBasePath: '/ops/durable/api' })]
|
|
@@ -400,8 +514,9 @@ interface DurableDashboardOptions {
|
|
|
400
514
|
* `basePath`/`apiBasePath` still matter here: they are where you mounted the two controllers, and
|
|
401
515
|
* the SPA is told to load its assets from `basePath` and fetch from `apiBasePath`.
|
|
402
516
|
*
|
|
403
|
-
*
|
|
404
|
-
*
|
|
517
|
+
* `authorize` omitted works as for mounted routes: `@UseConsoleAuthorize()` lets every request
|
|
518
|
+
* through outside production and answers `403` in production, and the boot warning is logged.
|
|
519
|
+
* The `*Base` classes carry no
|
|
405
520
|
* guards and no `Can.Anyone()` mark, so under `@nestjs/authorization`'s global guard a subclass
|
|
406
521
|
* without a `@Can()` is denied. `decorators` throws when combined with `routes: false` rather than
|
|
407
522
|
* being silently ignored.
|
|
@@ -411,10 +526,6 @@ interface DurableDashboardOptions {
|
|
|
411
526
|
*/
|
|
412
527
|
routes?: boolean;
|
|
413
528
|
}
|
|
414
|
-
/** DI token of the boot-time check that refuses an unprotected console in production. */
|
|
415
|
-
declare const DASHBOARD_PROTECTION_CHECK: unique symbol;
|
|
416
|
-
/** The boot error for a mounted console with no `decorators` under `NODE_ENV=production`. */
|
|
417
|
-
declare const UNPROTECTED_CONSOLE_IN_PRODUCTION: string;
|
|
418
529
|
/**
|
|
419
530
|
* Holds the JSON API controller + its read service, mounted on its own path by `forRoot`. Its own
|
|
420
531
|
* dynamic module because enhancers (a guard from `decorators`) DI-instantiate from their
|
|
@@ -427,34 +538,19 @@ declare class DurableApiModule {
|
|
|
427
538
|
routes?: boolean;
|
|
428
539
|
/** See {@link DurableDashboardOptions.decorators}. */
|
|
429
540
|
decorators?: ClassDecorator[];
|
|
541
|
+
/** See {@link DurableDashboardOptions.authorize}. */
|
|
542
|
+
authorize?: ConsoleAuthorize;
|
|
430
543
|
}): DynamicModule;
|
|
431
544
|
}
|
|
432
|
-
/**
|
|
433
|
-
* Mounts the control plane: the bundled React SPA at `basePath` and its JSON API at `apiBasePath`
|
|
434
|
-
* (default `<basePath>/api`). Import via `DurableDashboardModule.forRoot(...)` (or `forRootAsync`
|
|
435
|
-
* when `dashboardAuth`'s `login` hook needs injected services) alongside `DurableModule` (global),
|
|
436
|
-
* so it resolves the engine and store. Front the routes with the first-class `guards` option
|
|
437
|
-
* (plus `imports` for the guards' own dependencies) — see {@link DurableDashboardOptions.guards} —
|
|
438
|
-
* or with the built-in `dashboardAuth` cookie login, or both.
|
|
439
|
-
* With none of those (and no `decorators` / `token` / `allowUnauthenticated`), the console fails
|
|
440
|
-
* closed when `NODE_ENV=production` — see {@link DurableDashboardOptions.allowUnauthenticated}.
|
|
441
|
-
* `routes: false` mounts nothing and leaves serving the console to the host's own controllers — see
|
|
442
|
-
* {@link DurableDashboardOptions.routes}.
|
|
443
|
-
*/
|
|
444
545
|
/**
|
|
445
546
|
* Mounts the control plane: the bundled React SPA at `basePath` and its JSON API at `apiBasePath`
|
|
446
547
|
* (default `<basePath>/api`). Import via `DurableDashboardModule.forRoot(...)` alongside
|
|
447
548
|
* `DurableModule` (global), so it resolves the engine and store.
|
|
448
549
|
*
|
|
449
|
-
*
|
|
450
|
-
*
|
|
451
|
-
*
|
|
452
|
-
*
|
|
453
|
-
* 2. `routes: false`, and mount it yourself by extending `DurableUiControllerBase` /
|
|
454
|
-
* `DurableApiControllerBase` (see {@link DurableDashboardOptions.routes}).
|
|
455
|
-
*
|
|
456
|
-
* With `NODE_ENV=production`, a mounted console with no `decorators` fails the boot with
|
|
457
|
-
* {@link UNPROTECTED_CONSOLE_IN_PRODUCTION}. Outside production it is served open, with a warning.
|
|
550
|
+
* Access is decided by {@link DurableDashboardOptions.authorize}. Without it the console is open
|
|
551
|
+
* outside production and denies every request in production, with one warning at boot.
|
|
552
|
+
* `routes: false` mounts nothing and leaves serving the console to the host's own controllers (see
|
|
553
|
+
* {@link DurableDashboardOptions.routes}).
|
|
458
554
|
*/
|
|
459
555
|
declare class DurableDashboardModule {
|
|
460
556
|
static forRoot(options?: DurableDashboardOptions): DynamicModule;
|
|
@@ -492,4 +588,4 @@ declare class DurableUiControllerBase {
|
|
|
492
588
|
declare class DurableUiController extends DurableUiControllerBase {
|
|
493
589
|
}
|
|
494
590
|
|
|
495
|
-
export { DASHBOARD_API_PATH, DASHBOARD_BASE_PATH,
|
|
591
|
+
export { CONSOLE_AUTHORIZE_DECIDE, CONSOLE_AUTHORIZE_MISSING_WARNING, CONSOLE_GATE, CONSOLE_PAGE_METADATA, ConsoleAccessDenied, type ConsoleAuthorize, ConsoleAuthorizeFilter, ConsoleAuthorizeGuard, type ConsoleAuthorizeResult, type ConsoleAuthorizer, type ConsoleAuthorizerClass, type ConsoleDecide, type ConsoleDecision, type ConsoleGate, ConsolePage, type ConsolePolicyCheck, DASHBOARD_API_PATH, DASHBOARD_BASE_PATH, DashboardService, DurableApiController, DurableApiControllerBase, DurableApiModule, DurableDashboardModule, type DurableDashboardOptions, DurableUiController, DurableUiControllerBase, UseConsoleAuthorize, type WorkflowLifecyclePage, authorizeByToken, normalizeConsoleResult, resolveConsoleAuthorize };
|