@dereekb/firebase-server 13.6.16 → 13.7.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/index.cjs.js +2615 -951
- package/index.esm.js +2598 -932
- package/mailgun/package.json +9 -9
- package/model/package.json +9 -9
- package/model/src/lib/storagefile/storagefile.action.server.d.ts +4 -13
- package/oidc/index.cjs.js +245 -180
- package/oidc/index.esm.js +242 -178
- package/oidc/package.json +10 -10
- package/oidc/src/lib/middleware/oauth-auth.module.d.ts +18 -25
- package/package.json +11 -10
- package/src/lib/function/error.d.ts +11 -28
- package/src/lib/nest/app.d.ts +4 -45
- package/src/lib/nest/app.module.d.ts +4 -2
- package/src/lib/nest/auth/auth.util.d.ts +71 -5
- package/src/lib/nest/controller/index.d.ts +1 -0
- package/src/lib/nest/controller/model/index.d.ts +4 -0
- package/src/lib/nest/controller/model/model.api.controller.d.ts +93 -0
- package/src/lib/nest/controller/model/model.api.dispatch.d.ts +73 -0
- package/src/lib/nest/controller/model/model.api.get.service.d.ts +73 -0
- package/src/lib/nest/controller/model/model.api.module.d.ts +32 -0
- package/src/lib/nest/model/analytics.handler.d.ts +2 -0
- package/src/lib/nest/model/api.details.d.ts +53 -1
- package/src/lib/nest/model/call.model.function.d.ts +8 -5
- package/src/lib/nest/model/create.model.function.d.ts +1 -1
- package/src/lib/nest/model/crud.assert.function.d.ts +1 -1
- package/src/lib/nest/model/delete.model.function.d.ts +1 -1
- package/src/lib/nest/model/index.d.ts +1 -0
- package/src/lib/nest/model/query.model.function.d.ts +207 -0
- package/src/lib/nest/model/read.model.function.d.ts +1 -1
- package/src/lib/nest/model/update.model.function.d.ts +1 -1
- package/src/lib/nest/nest.provider.d.ts +19 -0
- package/test/index.cjs.js +1358 -398
- package/test/index.esm.js +1355 -400
- package/test/package.json +13 -11
- package/test/src/lib/firebase/firebase.test.d.ts +1 -1
- package/test/src/lib/index.d.ts +1 -0
- package/test/src/lib/oidc/index.d.ts +2 -0
- package/test/src/lib/oidc/oidc.test.fixture.d.ts +126 -0
- package/test/src/lib/oidc/oidc.test.flow.d.ts +43 -0
- package/zoho/package.json +9 -9
package/oidc/package.json
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dereekb/firebase-server/oidc",
|
|
3
|
-
"version": "13.
|
|
3
|
+
"version": "13.7.0",
|
|
4
4
|
"peerDependencies": {
|
|
5
|
-
"@dereekb/analytics": "13.
|
|
6
|
-
"@dereekb/date": "13.
|
|
7
|
-
"@dereekb/firebase": "13.
|
|
8
|
-
"@dereekb/firebase-server": "13.
|
|
9
|
-
"@dereekb/model": "13.
|
|
10
|
-
"@dereekb/nestjs": "13.
|
|
11
|
-
"@dereekb/rxjs": "13.
|
|
12
|
-
"@dereekb/util": "13.
|
|
13
|
-
"@dereekb/zoho": "13.
|
|
5
|
+
"@dereekb/analytics": "13.7.0",
|
|
6
|
+
"@dereekb/date": "13.7.0",
|
|
7
|
+
"@dereekb/firebase": "13.7.0",
|
|
8
|
+
"@dereekb/firebase-server": "13.7.0",
|
|
9
|
+
"@dereekb/model": "13.7.0",
|
|
10
|
+
"@dereekb/nestjs": "13.7.0",
|
|
11
|
+
"@dereekb/rxjs": "13.7.0",
|
|
12
|
+
"@dereekb/util": "13.7.0",
|
|
13
|
+
"@dereekb/zoho": "13.7.0",
|
|
14
14
|
"@nestjs/common": "^11.1.17",
|
|
15
15
|
"@nestjs/config": "^4.0.3",
|
|
16
16
|
"express": "^5.0.0",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type INestApplication } from '@nestjs/common';
|
|
2
2
|
import { type SlashPath } from '@dereekb/util';
|
|
3
3
|
/**
|
|
4
4
|
* Configuration for `OidcAuthBearerTokenMiddleware` route protection.
|
|
@@ -6,12 +6,6 @@ import { type SlashPath } from '@dereekb/util';
|
|
|
6
6
|
* Works in reverse of `FirebaseAppCheckMiddlewareConfig`: instead of protecting
|
|
7
7
|
* all routes and ignoring some, this only protects explicitly specified paths.
|
|
8
8
|
* Routes under the global API prefix (protected by AppCheck) are excluded.
|
|
9
|
-
*
|
|
10
|
-
* @example
|
|
11
|
-
* ```ts
|
|
12
|
-
* // Provide in your module:
|
|
13
|
-
* { provide: OidcAuthMiddlewareConfig, useValue: { protectedPaths: ['/mcp'] } }
|
|
14
|
-
* ```
|
|
15
9
|
*/
|
|
16
10
|
export declare abstract class OidcAuthMiddlewareConfig {
|
|
17
11
|
/**
|
|
@@ -24,27 +18,26 @@ export declare abstract class OidcAuthMiddlewareConfig {
|
|
|
24
18
|
readonly protectedPaths: SlashPath[];
|
|
25
19
|
}
|
|
26
20
|
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
21
|
+
* Applies OAuth bearer token verification as global Express middleware on
|
|
22
|
+
* the given NestJS application.
|
|
29
23
|
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
24
|
+
* Resolves `OidcService` and `OidcAuthMiddlewareConfig` from the app's DI container,
|
|
25
|
+
* then registers an Express middleware that verifies bearer tokens for the configured
|
|
26
|
+
* protected paths and attaches auth data to `req.auth`.
|
|
27
|
+
*
|
|
28
|
+
* This is an alternative to {@link ConfigureOidcAuthMiddlewareModule} for cases where
|
|
29
|
+
* NestJS module scoping makes the module approach impractical.
|
|
30
|
+
*
|
|
31
|
+
* @param nestApp - The NestJS application instance used to resolve dependencies and register the middleware.
|
|
33
32
|
*
|
|
34
33
|
* @example
|
|
35
34
|
* ```ts
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
* }
|
|
42
|
-
* export class AppModule {}
|
|
35
|
+
* export const APP_NEST_SERVER_CONFIG: NestServerInstanceConfig<AppModule> = {
|
|
36
|
+
* moduleClass: AppModule,
|
|
37
|
+
* configureNestServerInstance: (nestApp) => {
|
|
38
|
+
* applyOidcAuthMiddleware(nestApp);
|
|
39
|
+
* }
|
|
40
|
+
* };
|
|
43
41
|
* ```
|
|
44
42
|
*/
|
|
45
|
-
export declare
|
|
46
|
-
private readonly config?;
|
|
47
|
-
private readonly logger;
|
|
48
|
-
constructor(config?: OidcAuthMiddlewareConfig | undefined);
|
|
49
|
-
configure(consumer: MiddlewareConsumer): void;
|
|
50
|
-
}
|
|
43
|
+
export declare function applyOidcAuthMiddleware(nestApp: INestApplication): void;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dereekb/firebase-server",
|
|
3
|
-
"version": "13.
|
|
3
|
+
"version": "13.7.0",
|
|
4
4
|
"exports": {
|
|
5
5
|
"./test": {
|
|
6
6
|
"module": "./test/index.esm.js",
|
|
@@ -43,15 +43,15 @@
|
|
|
43
43
|
"main": "./index.cjs.js",
|
|
44
44
|
"types": "./src/index.d.ts",
|
|
45
45
|
"peerDependencies": {
|
|
46
|
-
"@dereekb/analytics": "13.
|
|
47
|
-
"@dereekb/date": "13.
|
|
48
|
-
"@dereekb/dbx-core": "13.
|
|
49
|
-
"@dereekb/firebase": "13.
|
|
50
|
-
"@dereekb/model": "13.
|
|
51
|
-
"@dereekb/nestjs": "13.
|
|
52
|
-
"@dereekb/rxjs": "13.
|
|
53
|
-
"@dereekb/util": "13.
|
|
54
|
-
"@dereekb/zoho": "13.
|
|
46
|
+
"@dereekb/analytics": "13.7.0",
|
|
47
|
+
"@dereekb/date": "13.7.0",
|
|
48
|
+
"@dereekb/dbx-core": "13.7.0",
|
|
49
|
+
"@dereekb/firebase": "13.7.0",
|
|
50
|
+
"@dereekb/model": "13.7.0",
|
|
51
|
+
"@dereekb/nestjs": "13.7.0",
|
|
52
|
+
"@dereekb/rxjs": "13.7.0",
|
|
53
|
+
"@dereekb/util": "13.7.0",
|
|
54
|
+
"@dereekb/zoho": "13.7.0",
|
|
55
55
|
"@google-cloud/firestore": "^7.11.6",
|
|
56
56
|
"@google-cloud/storage": "^7.19.0",
|
|
57
57
|
"@nestjs/common": "^11.1.17",
|
|
@@ -71,6 +71,7 @@
|
|
|
71
71
|
"nanoid": "^5.1.7",
|
|
72
72
|
"oidc-provider": "^9.7.0",
|
|
73
73
|
"rxjs": "^7.8.0",
|
|
74
|
+
"supertest": "^7.2.2",
|
|
74
75
|
"ts-essentials": "^10.0.0"
|
|
75
76
|
},
|
|
76
77
|
"module": "./index.esm.js"
|
|
@@ -14,100 +14,83 @@ export declare function unauthenticatedContextHasNoAuthData(): HttpsError;
|
|
|
14
14
|
* @returns A new unauthenticated {@link HttpsError} with the no-auth error code.
|
|
15
15
|
*/
|
|
16
16
|
export declare function unauthenticatedContextHasNoUidError(): HttpsError;
|
|
17
|
-
/**
|
|
18
|
-
* Standard error code constants and factory functions for creating typed {@link HttpsError} instances.
|
|
19
|
-
*
|
|
20
|
-
* Each factory wraps the Firebase `HttpsError` with a consistent shape: an HTTP status code,
|
|
21
|
-
* a string error code, and an optional {@link ServerError} detail object.
|
|
22
|
-
*/
|
|
23
|
-
export declare const UNAUTHENTICATED_ERROR_CODE = "UNAUTHENTICATED";
|
|
24
17
|
/**
|
|
25
18
|
* Creates an unauthenticated (401) {@link HttpsError}.
|
|
26
19
|
*
|
|
27
20
|
* @param messageOrError - Optional error message string or partial server error object.
|
|
28
21
|
* @returns A new unauthenticated (401) {@link HttpsError}.
|
|
29
22
|
*/
|
|
30
|
-
export declare function unauthenticatedError(messageOrError?: ErrorMessageOrPartialServerError): HttpsError;
|
|
31
|
-
export declare const FORBIDDEN_ERROR_CODE = "FORBIDDEN";
|
|
23
|
+
export declare function unauthenticatedError(messageOrError?: Maybe<ErrorMessageOrPartialServerError>): HttpsError;
|
|
32
24
|
/**
|
|
33
25
|
* Creates a forbidden (403) {@link HttpsError}.
|
|
34
26
|
*
|
|
35
27
|
* @param messageOrError - Optional error message string or partial server error object.
|
|
36
28
|
* @returns A new forbidden (403) {@link HttpsError}.
|
|
37
29
|
*/
|
|
38
|
-
export declare function forbiddenError(messageOrError?: ErrorMessageOrPartialServerError): HttpsError;
|
|
39
|
-
export declare const PERMISSION_DENIED_ERROR_CODE = "PERMISSION_DENIED";
|
|
30
|
+
export declare function forbiddenError(messageOrError?: Maybe<ErrorMessageOrPartialServerError>): HttpsError;
|
|
40
31
|
/**
|
|
41
32
|
* Creates a permission-denied (403) {@link HttpsError}.
|
|
42
33
|
*
|
|
43
34
|
* @param messageOrError - Optional error message string or partial server error object.
|
|
44
35
|
* @returns A new permission-denied (403) {@link HttpsError}.
|
|
45
36
|
*/
|
|
46
|
-
export declare function permissionDeniedError(messageOrError?: ErrorMessageOrPartialServerError): HttpsError;
|
|
47
|
-
export declare const NOT_FOUND_ERROR_CODE = "NOT_FOUND";
|
|
37
|
+
export declare function permissionDeniedError(messageOrError?: Maybe<ErrorMessageOrPartialServerError>): HttpsError;
|
|
48
38
|
/**
|
|
49
39
|
* Creates a not-found (404) {@link HttpsError}.
|
|
50
40
|
*
|
|
51
41
|
* @param messageOrError - Optional error message string or partial server error object.
|
|
52
42
|
* @returns A new not-found (404) {@link HttpsError}.
|
|
53
43
|
*/
|
|
54
|
-
export declare function notFoundError(messageOrError?: ErrorMessageOrPartialServerError): HttpsError;
|
|
55
|
-
export declare const MODEL_NOT_AVAILABLE_ERROR_CODE = "MODEL_NOT_AVAILABLE";
|
|
44
|
+
export declare function notFoundError(messageOrError?: Maybe<ErrorMessageOrPartialServerError>): HttpsError;
|
|
56
45
|
/**
|
|
57
46
|
* Creates a model-not-available (404) {@link HttpsError}, used when a Firestore document does not exist.
|
|
58
47
|
*
|
|
59
48
|
* @param messageOrError - Optional error message string or partial server error object.
|
|
60
49
|
* @returns A new model-not-available (404) {@link HttpsError}.
|
|
61
50
|
*/
|
|
62
|
-
export declare function modelNotAvailableError(messageOrError?: ErrorMessageOrPartialServerError): HttpsError;
|
|
63
|
-
export declare const BAD_REQUEST_ERROR_CODE = "BAD_REQUEST";
|
|
51
|
+
export declare function modelNotAvailableError(messageOrError?: Maybe<ErrorMessageOrPartialServerError>): HttpsError;
|
|
64
52
|
/**
|
|
65
53
|
* Creates a bad-request (400) {@link HttpsError}.
|
|
66
54
|
*
|
|
67
55
|
* @param messageOrError - Optional error message string or partial server error object.
|
|
68
56
|
* @returns A new bad-request (400) {@link HttpsError}.
|
|
69
57
|
*/
|
|
70
|
-
export declare function badRequestError(messageOrError?: ErrorMessageOrPartialServerError): HttpsError;
|
|
71
|
-
export declare const CONFLICT_ERROR_CODE = "CONFLICT";
|
|
58
|
+
export declare function badRequestError(messageOrError?: Maybe<ErrorMessageOrPartialServerError>): HttpsError;
|
|
72
59
|
/**
|
|
73
60
|
* Creates a precondition-conflict (409) {@link HttpsError}.
|
|
74
61
|
*
|
|
75
62
|
* @param messageOrError - Optional error message string or partial server error object.
|
|
76
63
|
* @returns A new precondition-conflict (409) {@link HttpsError}.
|
|
77
64
|
*/
|
|
78
|
-
export declare function preconditionConflictError(messageOrError?: ErrorMessageOrPartialServerError): HttpsError;
|
|
79
|
-
export declare const ALREADY_EXISTS_ERROR_CODE = "ALREADY_EXISTS";
|
|
65
|
+
export declare function preconditionConflictError(messageOrError?: Maybe<ErrorMessageOrPartialServerError>): HttpsError;
|
|
80
66
|
/**
|
|
81
67
|
* Creates an already-exists (409) {@link HttpsError}.
|
|
82
68
|
*
|
|
83
69
|
* @param messageOrError - Optional error message string or partial server error object.
|
|
84
70
|
* @returns A new already-exists (409) {@link HttpsError}.
|
|
85
71
|
*/
|
|
86
|
-
export declare function alreadyExistsError(messageOrError?: ErrorMessageOrPartialServerError): HttpsError;
|
|
87
|
-
export declare const UNAVAILABLE_ERROR_CODE = "UNAVAILABLE";
|
|
72
|
+
export declare function alreadyExistsError(messageOrError?: Maybe<ErrorMessageOrPartialServerError>): HttpsError;
|
|
88
73
|
/**
|
|
89
74
|
* Creates an unavailable (503) {@link HttpsError}.
|
|
90
75
|
*
|
|
91
76
|
* @param messageOrError - Optional error message string or partial server error object.
|
|
92
77
|
* @returns A new unavailable (503) {@link HttpsError}.
|
|
93
78
|
*/
|
|
94
|
-
export declare function unavailableError(messageOrError?: ErrorMessageOrPartialServerError): HttpsError;
|
|
95
|
-
export declare const UNAVAILABLE_OR_DEACTIVATED_FUNCTION_ERROR_CODE = "UNAVAILABLE_OR_DEACTIVATED_FUNCTION";
|
|
79
|
+
export declare function unavailableError(messageOrError?: Maybe<ErrorMessageOrPartialServerError>): HttpsError;
|
|
96
80
|
/**
|
|
97
81
|
* Creates an unimplemented (501) {@link HttpsError} for deactivated or unavailable functions.
|
|
98
82
|
*
|
|
99
83
|
* @param messageOrError - Optional error message string or partial server error object.
|
|
100
84
|
* @returns A new unimplemented (501) {@link HttpsError}.
|
|
101
85
|
*/
|
|
102
|
-
export declare function unavailableOrDeactivatedFunctionError(messageOrError?: ErrorMessageOrPartialServerError): HttpsError;
|
|
103
|
-
export declare const INTERNAL_SERVER_ERROR_CODE = "INTERNAL_ERROR";
|
|
86
|
+
export declare function unavailableOrDeactivatedFunctionError(messageOrError?: Maybe<ErrorMessageOrPartialServerError>): HttpsError;
|
|
104
87
|
/**
|
|
105
88
|
* Creates an internal-error (500) {@link HttpsError}.
|
|
106
89
|
*
|
|
107
90
|
* @param messageOrError - Optional error message string or partial server error object.
|
|
108
91
|
* @returns A new internal-error (500) {@link HttpsError}.
|
|
109
92
|
*/
|
|
110
|
-
export declare function internalServerError(messageOrError?: ErrorMessageOrPartialServerError): HttpsError;
|
|
93
|
+
export declare function internalServerError(messageOrError?: Maybe<ErrorMessageOrPartialServerError>): HttpsError;
|
|
111
94
|
/**
|
|
112
95
|
* Discriminator for the type of Firebase server error encountered.
|
|
113
96
|
*/
|
package/src/lib/nest/app.d.ts
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
|
-
import { type ClassType, type Getter
|
|
2
|
-
import { type INestApplication, type INestApplicationContext, type NestApplicationOptions
|
|
1
|
+
import { type ClassType, type Getter } from '@dereekb/util';
|
|
2
|
+
import { type INestApplication, type INestApplicationContext, type NestApplicationOptions } from '@nestjs/common';
|
|
3
3
|
import express from 'express';
|
|
4
4
|
import type * as admin from 'firebase-admin';
|
|
5
|
-
import { type StorageBucketId } from '@dereekb/firebase';
|
|
6
5
|
import { type FirebaseServerEnvironmentConfig } from '../env/env.config';
|
|
7
|
-
import { type
|
|
8
|
-
import { type NestServerAssetConfig } from './app.module';
|
|
6
|
+
import { type NestServerRootModuleConfig } from './app.module';
|
|
9
7
|
/**
|
|
10
8
|
* A running NestJS server instance backed by Express, paired with a lazy promise getter for the NestJS application context.
|
|
11
9
|
*/
|
|
@@ -58,58 +56,19 @@ export type ConfigureNestServerInstanceFunction = (nestApp: INestApplication) =>
|
|
|
58
56
|
* });
|
|
59
57
|
* ```
|
|
60
58
|
*/
|
|
61
|
-
export interface NestServerInstanceConfig<T> {
|
|
59
|
+
export interface NestServerInstanceConfig<T> extends Omit<NestServerRootModuleConfig, 'firebaseAppGetter'> {
|
|
62
60
|
/**
|
|
63
61
|
* Module to instantiate.
|
|
64
62
|
*/
|
|
65
63
|
readonly moduleClass: ClassType<T>;
|
|
66
|
-
/**
|
|
67
|
-
* Additional providers to provide globally.
|
|
68
|
-
*/
|
|
69
|
-
readonly providers?: Provider<unknown>[];
|
|
70
|
-
/**
|
|
71
|
-
* Whether or not to configure FirebaseServerEnvService to be provided globally.
|
|
72
|
-
*/
|
|
73
|
-
readonly configureEnvService?: boolean;
|
|
74
|
-
/**
|
|
75
|
-
* Whether or not to configure webhook usage.
|
|
76
|
-
*
|
|
77
|
-
* This will configure the webhook routes.
|
|
78
|
-
*/
|
|
79
|
-
readonly configureWebhooks?: boolean;
|
|
80
|
-
/**
|
|
81
|
-
* Default storage bucket to use. If provided, overrides what the app uses in the default FirebaseServerStorageContextModule and default FirebaseStorageContext.
|
|
82
|
-
*/
|
|
83
|
-
readonly defaultStorageBucket?: StorageBucketId;
|
|
84
|
-
/**
|
|
85
|
-
* Whether or not to force using the default storage bucket.
|
|
86
|
-
*/
|
|
87
|
-
readonly forceStorageBucket?: boolean;
|
|
88
|
-
/**
|
|
89
|
-
* Whether or not to verify API calls with app check. Is true by default.
|
|
90
|
-
*/
|
|
91
|
-
readonly appCheckEnabled?: boolean;
|
|
92
64
|
/**
|
|
93
65
|
* Additional nest application options.
|
|
94
66
|
*/
|
|
95
67
|
readonly applicationOptions?: NestApplicationOptions;
|
|
96
|
-
/**
|
|
97
|
-
* Global routing prefix or options.
|
|
98
|
-
*
|
|
99
|
-
* Example: '/api'
|
|
100
|
-
*/
|
|
101
|
-
readonly globalApiRoutePrefix?: WebsitePath | GlobalRoutePrefixConfig;
|
|
102
68
|
/**
|
|
103
69
|
* Optional configuration function
|
|
104
70
|
*/
|
|
105
71
|
readonly configureNestServerInstance?: ConfigureNestServerInstanceFunction;
|
|
106
|
-
/**
|
|
107
|
-
* Optional asset loader configuration.
|
|
108
|
-
*
|
|
109
|
-
* When provided, configures the {@link AssetLoader} with the given settings.
|
|
110
|
-
* The AssetLoader is always provided globally regardless of this config.
|
|
111
|
-
*/
|
|
112
|
-
readonly assets?: Maybe<NestServerAssetConfig>;
|
|
113
72
|
}
|
|
114
73
|
export interface NestFirebaseServerEnvironmentConfig {
|
|
115
74
|
readonly environment: FirebaseServerEnvironmentConfig;
|
|
@@ -41,7 +41,7 @@ export interface NestServerRootModuleConfig {
|
|
|
41
41
|
/**
|
|
42
42
|
* Module(s) to import into the root module.
|
|
43
43
|
*/
|
|
44
|
-
readonly modules
|
|
44
|
+
readonly modules?: Maybe<ArrayOrValue<ClassType | DynamicModule>>;
|
|
45
45
|
/**
|
|
46
46
|
* Getter for the Firebase Admin app instance.
|
|
47
47
|
* When provided, the `FIREBASE_APP_TOKEN` is made available globally.
|
|
@@ -50,7 +50,7 @@ export interface NestServerRootModuleConfig {
|
|
|
50
50
|
/**
|
|
51
51
|
* Additional providers to include globally.
|
|
52
52
|
*/
|
|
53
|
-
readonly
|
|
53
|
+
readonly providers?: Provider[];
|
|
54
54
|
/**
|
|
55
55
|
* Environment configuration. When provided, injects env tokens via `firebaseServerEnvTokenProviders`.
|
|
56
56
|
*/
|
|
@@ -71,6 +71,8 @@ export interface NestServerRootModuleConfig {
|
|
|
71
71
|
/**
|
|
72
72
|
* Global route prefix configuration.
|
|
73
73
|
* The `GlobalRoutePrefixConfig` token is always provided (empty object when no config given).
|
|
74
|
+
*
|
|
75
|
+
* Example: '/api'
|
|
74
76
|
*/
|
|
75
77
|
readonly globalApiRoutePrefix?: WebsitePath | GlobalRoutePrefixConfig;
|
|
76
78
|
/**
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { type UserRelated } from '@dereekb/firebase';
|
|
2
|
-
import { type ArrayOrValue, type AuthRole } from '@dereekb/util';
|
|
2
|
+
import { type ArrayOrValue, type AuthRole, type ErrorMessageOrPartialServerError, type Maybe } from '@dereekb/util';
|
|
3
3
|
import { type NestContextCallableRequestWithOptionalAuth } from '../function/nest';
|
|
4
4
|
import { type AbstractFirebaseNestContext } from '../nest.provider';
|
|
5
5
|
/**
|
|
6
6
|
* Asserts that the caller has admin privileges in the request.
|
|
7
7
|
*
|
|
8
8
|
* @param request - The callable request to check for admin privileges.
|
|
9
|
+
* @param messageOrError - Optional custom error message or partial server error for the forbidden response.
|
|
9
10
|
* @throws {HttpsError} Throws forbidden (403) if the caller is not an admin.
|
|
10
11
|
*/
|
|
11
|
-
export declare function assertIsAdminInRequest<N extends AbstractFirebaseNestContext<any, any> = AbstractFirebaseNestContext<any, any>, I = unknown>(request: NestContextCallableRequestWithOptionalAuth<N, I>): void;
|
|
12
|
+
export declare function assertIsAdminInRequest<N extends AbstractFirebaseNestContext<any, any> = AbstractFirebaseNestContext<any, any>, I = unknown>(request: NestContextCallableRequestWithOptionalAuth<N, I>, messageOrError?: Maybe<ErrorMessageOrPartialServerError>): void;
|
|
12
13
|
/**
|
|
13
14
|
* Checks whether the caller has admin privileges in the request.
|
|
14
15
|
*
|
|
@@ -23,10 +24,11 @@ export declare function isAdminInRequest<N extends AbstractFirebaseNestContext<a
|
|
|
23
24
|
*
|
|
24
25
|
* @param request - The callable request containing the target UID.
|
|
25
26
|
* @param requireUid - If true, a UID must be present in the request data.
|
|
27
|
+
* @param messageOrError - Optional custom error message or partial server error for the forbidden response.
|
|
26
28
|
* @returns The resolved target UID (from request data or auth).
|
|
27
29
|
* @throws {HttpsError} Throws forbidden (403) if the caller is not authorized.
|
|
28
30
|
*/
|
|
29
|
-
export declare function assertIsAdminOrTargetUserInRequestData<N extends AbstractFirebaseNestContext<any, any> = AbstractFirebaseNestContext<any, any>, I extends Partial<UserRelated> = Partial<UserRelated>>(request: NestContextCallableRequestWithOptionalAuth<N, I>, requireUid?: boolean): string | undefined;
|
|
31
|
+
export declare function assertIsAdminOrTargetUserInRequestData<N extends AbstractFirebaseNestContext<any, any> = AbstractFirebaseNestContext<any, any>, I extends Partial<UserRelated> = Partial<UserRelated>>(request: NestContextCallableRequestWithOptionalAuth<N, I>, requireUid?: boolean, messageOrError?: Maybe<ErrorMessageOrPartialServerError>): string | undefined;
|
|
30
32
|
/**
|
|
31
33
|
* Checks whether the caller is an admin or is targeting their own user record in the request data.
|
|
32
34
|
*
|
|
@@ -39,9 +41,10 @@ export declare function isAdminOrTargetUserInRequestData<N extends AbstractFireb
|
|
|
39
41
|
* Asserts that the caller has signed the Terms of Service.
|
|
40
42
|
*
|
|
41
43
|
* @param request - The callable request to check for ToS status.
|
|
44
|
+
* @param messageOrError - Optional custom error message or partial server error for the forbidden response.
|
|
42
45
|
* @throws {HttpsError} Throws forbidden (403) if ToS has not been signed.
|
|
43
46
|
*/
|
|
44
|
-
export declare function assertHasSignedTosInRequest<N extends AbstractFirebaseNestContext<any, any> = AbstractFirebaseNestContext<any, any>, I = unknown>(request: NestContextCallableRequestWithOptionalAuth<N, I>): void;
|
|
47
|
+
export declare function assertHasSignedTosInRequest<N extends AbstractFirebaseNestContext<any, any> = AbstractFirebaseNestContext<any, any>, I = unknown>(request: NestContextCallableRequestWithOptionalAuth<N, I>, messageOrError?: Maybe<ErrorMessageOrPartialServerError>): void;
|
|
45
48
|
/**
|
|
46
49
|
* Checks whether the caller has signed the Terms of Service.
|
|
47
50
|
*
|
|
@@ -54,9 +57,10 @@ export declare function hasSignedTosInRequest<N extends AbstractFirebaseNestCont
|
|
|
54
57
|
*
|
|
55
58
|
* @param request - The callable request to check for auth roles.
|
|
56
59
|
* @param authRoles - One or more roles that must all be present.
|
|
60
|
+
* @param messageOrError - Optional custom error message or partial server error for the forbidden response.
|
|
57
61
|
* @throws {HttpsError} Throws forbidden (403) if any required role is missing.
|
|
58
62
|
*/
|
|
59
|
-
export declare function assertHasRolesInRequest<N extends AbstractFirebaseNestContext<any, any> = AbstractFirebaseNestContext<any, any>, I = unknown>(request: NestContextCallableRequestWithOptionalAuth<N, I>, authRoles: ArrayOrValue<AuthRole>): void;
|
|
63
|
+
export declare function assertHasRolesInRequest<N extends AbstractFirebaseNestContext<any, any> = AbstractFirebaseNestContext<any, any>, I = unknown>(request: NestContextCallableRequestWithOptionalAuth<N, I>, authRoles: ArrayOrValue<AuthRole>, messageOrError?: Maybe<ErrorMessageOrPartialServerError>): void;
|
|
60
64
|
/**
|
|
61
65
|
* Checks whether the caller has all of the specified auth roles.
|
|
62
66
|
*
|
|
@@ -65,6 +69,68 @@ export declare function assertHasRolesInRequest<N extends AbstractFirebaseNestCo
|
|
|
65
69
|
* @returns True if the caller has all of the specified auth roles.
|
|
66
70
|
*/
|
|
67
71
|
export declare function hasAuthRolesInRequest<N extends AbstractFirebaseNestContext<any, any> = AbstractFirebaseNestContext<any, any>, I = unknown>(request: NestContextCallableRequestWithOptionalAuth<N, I>, authRoles: ArrayOrValue<AuthRole>): boolean;
|
|
72
|
+
/**
|
|
73
|
+
* Configuration for {@link resolveAdminOnlyValue}.
|
|
74
|
+
*
|
|
75
|
+
* @typeParam T - The value type being resolved.
|
|
76
|
+
*/
|
|
77
|
+
export interface ResolveAdminOnlyValueConfig<N extends AbstractFirebaseNestContext<any, any>, I, T> {
|
|
78
|
+
/**
|
|
79
|
+
* The callable request to check for admin privileges.
|
|
80
|
+
*/
|
|
81
|
+
readonly request: NestContextCallableRequestWithOptionalAuth<N, I>;
|
|
82
|
+
/**
|
|
83
|
+
* The raw input value from the request data. May be undefined if the caller didn't provide it.
|
|
84
|
+
*/
|
|
85
|
+
readonly value?: Maybe<T>;
|
|
86
|
+
/**
|
|
87
|
+
* The default value to use when {@link value} is undefined and the caller is not an admin.
|
|
88
|
+
*
|
|
89
|
+
* Admins receive no default (undefined) so the query has no restriction.
|
|
90
|
+
*/
|
|
91
|
+
readonly defaultValue?: Maybe<T>;
|
|
92
|
+
/**
|
|
93
|
+
* Predicate that returns true if the given resolved value is restricted to admins only.
|
|
94
|
+
*
|
|
95
|
+
* When this returns true and the caller is not an admin, a forbidden error is thrown.
|
|
96
|
+
*
|
|
97
|
+
* @param value - The resolved value to check.
|
|
98
|
+
* @returns True if the value requires admin privileges.
|
|
99
|
+
*/
|
|
100
|
+
readonly isAdminOnlyValue: (value: Maybe<T>) => boolean;
|
|
101
|
+
/**
|
|
102
|
+
* Optional error message or partial server error to include in the forbidden error.
|
|
103
|
+
*/
|
|
104
|
+
readonly messageOrError?: ErrorMessageOrPartialServerError;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Resolves a request parameter value with admin-aware defaulting and access control.
|
|
108
|
+
*
|
|
109
|
+
* For non-admins:
|
|
110
|
+
* - If the caller didn't provide a value, the {@link ResolveAdminOnlyValueConfig.defaultValue} is used.
|
|
111
|
+
* - If the resolved value is admin-only (per the predicate), a forbidden error is thrown.
|
|
112
|
+
*
|
|
113
|
+
* For admins:
|
|
114
|
+
* - If the caller didn't provide a value, undefined is returned (no restriction).
|
|
115
|
+
* - Any value is allowed.
|
|
116
|
+
*
|
|
117
|
+
* @param config - Configuration specifying the request, value, defaults, and admin-only predicate.
|
|
118
|
+
* @returns The resolved value, or undefined for admins who didn't provide a value.
|
|
119
|
+
* @throws {HttpsError} Throws forbidden (403) if a non-admin attempts to use an admin-only value.
|
|
120
|
+
*
|
|
121
|
+
* @example
|
|
122
|
+
* ```typescript
|
|
123
|
+
* // Non-admins can only query published=true; admins can query anything
|
|
124
|
+
* const published = resolveAdminOnlyValue({
|
|
125
|
+
* request,
|
|
126
|
+
* value: data.published,
|
|
127
|
+
* defaultValue: true,
|
|
128
|
+
* isAdminOnlyValue: (v) => v !== true,
|
|
129
|
+
* messageOrError: { message: 'Users can only search published entries.' }
|
|
130
|
+
* });
|
|
131
|
+
* ```
|
|
132
|
+
*/
|
|
133
|
+
export declare function resolveAdminOnlyValue<N extends AbstractFirebaseNestContext<any, any>, I, T>(config: ResolveAdminOnlyValueConfig<N, I, T>): Maybe<T>;
|
|
68
134
|
/**
|
|
69
135
|
* Returns true if the claims have a FIREBASE_SERVER_AUTH_CLAIMS_SETUP_PASSWORD_KEY claims value, indicating they are a newly invited user.
|
|
70
136
|
*
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { type Request } from 'express';
|
|
2
|
+
import { type OnCallTypedModelParams, type FirestoreModelKey } from '@dereekb/firebase';
|
|
3
|
+
import { type Maybe } from '@dereekb/util';
|
|
4
|
+
import { ModelApiCallModelDispatchService } from './model.api.dispatch';
|
|
5
|
+
import { ModelApiGetService } from './model.api.get.service';
|
|
6
|
+
/**
|
|
7
|
+
* Body for multi-read POST requests on the `get` route.
|
|
8
|
+
*/
|
|
9
|
+
interface ModelAccessMultiReadBody {
|
|
10
|
+
readonly keys: string[];
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* REST API controller that exposes the callModel dispatch chain and direct document access via HTTP.
|
|
14
|
+
*
|
|
15
|
+
* Mounted at `model` — under the `/api` global prefix, routes become `/api/model/*`.
|
|
16
|
+
*
|
|
17
|
+
* Provides three access patterns:
|
|
18
|
+
* 1. **Direct dispatch**: `POST /api/model/call` with an {@link OnCallTypedModelParams} body.
|
|
19
|
+
* 2. **Document access**: `GET /api/model/:modelType/get?key=...` (single) or
|
|
20
|
+
* `POST /api/model/:modelType/get` with `{ keys: [...] }` (multi) via `useModel()`.
|
|
21
|
+
* 3. **Path-based dispatch**: `POST|PUT /api/model/:modelType/:call/:specifier?` dispatches
|
|
22
|
+
* to the callModel chain. `DELETE` is only allowed when the call segment is `'delete'`.
|
|
23
|
+
* The call type comes from the path, not the HTTP method.
|
|
24
|
+
*
|
|
25
|
+
* Auth is provided by the OIDC bearer token middleware on the `req.auth` field.
|
|
26
|
+
*/
|
|
27
|
+
export declare class ModelApiController {
|
|
28
|
+
private readonly dispatchService;
|
|
29
|
+
private readonly accessService;
|
|
30
|
+
constructor(dispatchService: ModelApiCallModelDispatchService, accessService: ModelApiGetService);
|
|
31
|
+
/**
|
|
32
|
+
* Direct dispatch with full OnCallTypedModelParams body.
|
|
33
|
+
*
|
|
34
|
+
* This route MUST be declared before the catch-all to prevent NestJS
|
|
35
|
+
* from matching "call" as a modelType.
|
|
36
|
+
*
|
|
37
|
+
* @param body - The full {@link OnCallTypedModelParams} describing the model call to dispatch.
|
|
38
|
+
* @param req - The Express request containing auth credentials on `req.auth`.
|
|
39
|
+
* @returns The result of the dispatched model call.
|
|
40
|
+
*/
|
|
41
|
+
directDispatch(body: OnCallTypedModelParams, req: Request): Promise<unknown>;
|
|
42
|
+
/**
|
|
43
|
+
* Reads a single document by model type and key via `useModel()` with `'read'` roles.
|
|
44
|
+
*
|
|
45
|
+
* The key is a full Firestore model key (e.g., `pr/abc123`), not just an ID.
|
|
46
|
+
*
|
|
47
|
+
* Declared before the catch-all `{*path}` route so NestJS matches this first.
|
|
48
|
+
*
|
|
49
|
+
* @param modelType - The model type identifier (e.g., 'pr', 'user').
|
|
50
|
+
* @param key - The full Firestore model key to read (e.g., `pr/abc123`).
|
|
51
|
+
* @param req - The Express request containing auth credentials on `req.auth`.
|
|
52
|
+
* @returns The document data for the requested model key.
|
|
53
|
+
*/
|
|
54
|
+
getOne(modelType: string, key: Maybe<FirestoreModelKey>, req: Request): Promise<import("./model.api.get.service").ModelAccessReadResult>;
|
|
55
|
+
/**
|
|
56
|
+
* Reads multiple documents of the same model type via `useMultipleModels()` with `'read'` roles.
|
|
57
|
+
*
|
|
58
|
+
* Keys are full Firestore model keys. Maximum {@link MAX_MODEL_ACCESS_MULTI_READ_KEYS} keys per request.
|
|
59
|
+
*
|
|
60
|
+
* Declared before the catch-all `{*path}` route so NestJS matches this first.
|
|
61
|
+
*
|
|
62
|
+
* @param modelType - The model type identifier (e.g., 'pr', 'user').
|
|
63
|
+
* @param body - Request body containing the array of Firestore model keys to read.
|
|
64
|
+
* @param req - The Express request containing auth credentials on `req.auth`.
|
|
65
|
+
* @returns An object with `results` (document data array) and `errors` (per-key failures).
|
|
66
|
+
*/
|
|
67
|
+
getMany(modelType: string, body: ModelAccessMultiReadBody, req: Request): Promise<import("./model.api.get.service").ModelAccessMultiReadResult>;
|
|
68
|
+
/**
|
|
69
|
+
* Catch-all handler for callModel dispatch via path.
|
|
70
|
+
*
|
|
71
|
+
* Path: `/api/model/:modelType/:call/:specifier?`
|
|
72
|
+
*
|
|
73
|
+
* The call type (create, read, update, delete, query, etc.) is determined by the
|
|
74
|
+
* path segment, not the HTTP method. POST and PUT are allowed for any call type.
|
|
75
|
+
* DELETE is only allowed when the call segment is `'delete'`.
|
|
76
|
+
*
|
|
77
|
+
* @param req - The Express request whose path segments encode modelType, call, and optional specifier.
|
|
78
|
+
* @returns The result of the dispatched model call.
|
|
79
|
+
*/
|
|
80
|
+
handleDispatchRequest(req: Request): Promise<unknown>;
|
|
81
|
+
/**
|
|
82
|
+
* Parses modelType, call, and specifier from the wildcard path segments.
|
|
83
|
+
*
|
|
84
|
+
* Expected path format: `:modelType/:call/:specifier?`
|
|
85
|
+
*
|
|
86
|
+
* @param req - The Express request containing wildcard path params.
|
|
87
|
+
* @returns Parsed path components with modelType, call, and specifier (defaults to '_').
|
|
88
|
+
*/
|
|
89
|
+
private _parsePath;
|
|
90
|
+
private _dispatch;
|
|
91
|
+
private _toHttpException;
|
|
92
|
+
}
|
|
93
|
+
export {};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { type Maybe } from '@dereekb/util';
|
|
2
|
+
import { type OnCallTypedModelParams } from '@dereekb/firebase';
|
|
3
|
+
import { type INestApplicationContext } from '@nestjs/common';
|
|
4
|
+
import { type Request } from 'express';
|
|
5
|
+
import { type OnCallWithNestContext } from '../../function/call';
|
|
6
|
+
import { type OnCallApiDetailsRef, type ModelApiDetailsResult } from '../../model/api.details';
|
|
7
|
+
import { type MakeNestContext } from '../../nest.provider';
|
|
8
|
+
import { type FirebaseServerAuthData } from '../auth.context.server';
|
|
9
|
+
/**
|
|
10
|
+
* The combined type of the function returned by onCallModel() with _apiDetails attached.
|
|
11
|
+
*/
|
|
12
|
+
export type OnCallModelFnWithApiDetails = OnCallWithNestContext<unknown, OnCallTypedModelParams> & OnCallApiDetailsRef;
|
|
13
|
+
/**
|
|
14
|
+
* Abstract injectable config token for the Model API dispatch layer.
|
|
15
|
+
*
|
|
16
|
+
* Downstream apps provide this by creating a concrete provider that references
|
|
17
|
+
* their onCallModel() return value and MakeNestContext factory.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* {
|
|
22
|
+
* provide: ModelApiDispatchConfig,
|
|
23
|
+
* useValue: {
|
|
24
|
+
* callModelFn: demoCallModelFn,
|
|
25
|
+
* makeNestContext: mapDemoApiNestContext
|
|
26
|
+
* }
|
|
27
|
+
* }
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export declare abstract class ModelApiDispatchConfig {
|
|
31
|
+
/**
|
|
32
|
+
* The onCallModel() return value with _apiDetails attached.
|
|
33
|
+
*/
|
|
34
|
+
readonly callModelFn: OnCallModelFnWithApiDetails;
|
|
35
|
+
/**
|
|
36
|
+
* Factory to create typed nest context from INestApplicationContext.
|
|
37
|
+
*/
|
|
38
|
+
readonly makeNestContext: MakeNestContext<unknown>;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Injection token for providing the NestJS application context to the dispatch service.
|
|
42
|
+
*
|
|
43
|
+
* The app module metadata factory creates a provider for this token
|
|
44
|
+
* using the module's own INestApplicationContext.
|
|
45
|
+
*/
|
|
46
|
+
export declare const MODEL_API_NEST_APPLICATION_CONTEXT = "MODEL_API_NEST_APPLICATION_CONTEXT";
|
|
47
|
+
/**
|
|
48
|
+
* Service that bridges HTTP/MCP requests to the callModel dispatch chain.
|
|
49
|
+
*
|
|
50
|
+
* Builds a synthetic {@link CallableRequest} from the HTTP request auth and body,
|
|
51
|
+
* injects the NestJS application context and typed nest context, then invokes
|
|
52
|
+
* the callModel function.
|
|
53
|
+
*/
|
|
54
|
+
export declare class ModelApiCallModelDispatchService {
|
|
55
|
+
private readonly config;
|
|
56
|
+
private readonly nestApplication;
|
|
57
|
+
constructor(config: ModelApiDispatchConfig, nestApplication: INestApplicationContext);
|
|
58
|
+
/**
|
|
59
|
+
* Dispatch to the callModel chain.
|
|
60
|
+
*
|
|
61
|
+
* @param params - The typed model params (call, modelType, specifier, data).
|
|
62
|
+
* @param auth - The authenticated user's auth data from the OIDC middleware.
|
|
63
|
+
* @param rawRequest - The raw Express request.
|
|
64
|
+
* @returns The handler's return value.
|
|
65
|
+
*/
|
|
66
|
+
dispatch(params: OnCallTypedModelParams, auth: Maybe<FirebaseServerAuthData>, rawRequest: Request): Promise<unknown>;
|
|
67
|
+
/**
|
|
68
|
+
* Returns the model-first API details view, or undefined if no handlers have _apiDetails.
|
|
69
|
+
*
|
|
70
|
+
* @returns The aggregated API details describing all registered model call handlers, or undefined if unavailable.
|
|
71
|
+
*/
|
|
72
|
+
getApiDetails(): Maybe<ModelApiDetailsResult>;
|
|
73
|
+
}
|