@vritti/api-sdk 0.1.2 → 0.1.3
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/index.cjs +1271 -1289
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1025 -1028
- package/dist/index.d.ts +1025 -1028
- package/dist/index.js +1238 -1256
- package/dist/index.js.map +1 -1
- package/package.json +9 -15
package/dist/index.d.cts
CHANGED
|
@@ -1,192 +1,15 @@
|
|
|
1
1
|
import * as _nestjs_common from '@nestjs/common';
|
|
2
|
-
import { DynamicModule,
|
|
3
|
-
import { NodePgDatabase } from 'drizzle-orm/node-postgres';
|
|
4
|
-
import { InferInsertModel, InferSelectModel, SQL } from 'drizzle-orm';
|
|
5
|
-
import { PgTable } from 'drizzle-orm/pg-core';
|
|
2
|
+
import { DynamicModule, OnModuleInit, OnModuleDestroy, CanActivate, ExecutionContext, Logger, HttpException, HttpStatus, ExceptionFilter, ArgumentsHost, ModuleMetadata, Type, LoggerService as LoggerService$1, NestInterceptor, CallHandler, NestModule, MiddlewareConsumer, NestMiddleware } from '@nestjs/common';
|
|
6
3
|
import { ConfigService } from '@nestjs/config';
|
|
7
4
|
import { Reflector } from '@nestjs/core';
|
|
8
5
|
import { JwtService } from '@nestjs/jwt';
|
|
6
|
+
import { NodePgDatabase } from 'drizzle-orm/node-postgres';
|
|
9
7
|
import { FastifyRequest, FastifyReply } from 'fastify';
|
|
8
|
+
import { InferInsertModel, InferSelectModel, SQL } from 'drizzle-orm';
|
|
9
|
+
import { PgTable } from 'drizzle-orm/pg-core';
|
|
10
10
|
import { Observable } from 'rxjs';
|
|
11
11
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
12
12
|
|
|
13
|
-
/**
|
|
14
|
-
* api-sdk Configuration System
|
|
15
|
-
*
|
|
16
|
-
* Similar to quantum-ui's config pattern - provides a type-safe configuration system
|
|
17
|
-
*
|
|
18
|
-
* @example
|
|
19
|
-
* ```typescript
|
|
20
|
-
* // In vritti-api-nexus/src/main.ts
|
|
21
|
-
* import { configureApiSdk } from '@vritti/api-sdk';
|
|
22
|
-
*
|
|
23
|
-
* configureApiSdk({
|
|
24
|
-
* cookie: {
|
|
25
|
-
* refreshCookieName: 'vritti_refresh',
|
|
26
|
-
* refreshCookieMaxAge: 30 * 24 * 60 * 60 * 1000, // 30 days
|
|
27
|
-
* },
|
|
28
|
-
* jwt: {
|
|
29
|
-
* accessTokenExpiry: '15m',
|
|
30
|
-
* refreshTokenExpiry: '30d',
|
|
31
|
-
* validateTokenBinding: true,
|
|
32
|
-
* },
|
|
33
|
-
* guard: {
|
|
34
|
-
* tenantHeaderName: 'x-tenant-id',
|
|
35
|
-
* },
|
|
36
|
-
* });
|
|
37
|
-
* ```
|
|
38
|
-
*/
|
|
39
|
-
/**
|
|
40
|
-
* Cookie configuration options
|
|
41
|
-
*/
|
|
42
|
-
interface CookieConfig {
|
|
43
|
-
/**
|
|
44
|
-
* The name of the httpOnly cookie containing the refresh token
|
|
45
|
-
* @default 'vritti_refresh'
|
|
46
|
-
*/
|
|
47
|
-
refreshCookieName: string;
|
|
48
|
-
/**
|
|
49
|
-
* Max age of the refresh cookie in milliseconds
|
|
50
|
-
* @default 2592000000 (30 days)
|
|
51
|
-
*/
|
|
52
|
-
refreshCookieMaxAge: number;
|
|
53
|
-
/**
|
|
54
|
-
* Cookie path
|
|
55
|
-
* @default '/'
|
|
56
|
-
*/
|
|
57
|
-
refreshCookiePath: string;
|
|
58
|
-
/**
|
|
59
|
-
* Whether the cookie is secure (HTTPS only)
|
|
60
|
-
* @default true in production
|
|
61
|
-
*/
|
|
62
|
-
refreshCookieSecure: boolean;
|
|
63
|
-
/**
|
|
64
|
-
* SameSite attribute for the cookie
|
|
65
|
-
* @default 'strict'
|
|
66
|
-
*/
|
|
67
|
-
refreshCookieSameSite: 'strict' | 'lax' | 'none';
|
|
68
|
-
}
|
|
69
|
-
/**
|
|
70
|
-
* JWT token configuration options
|
|
71
|
-
*/
|
|
72
|
-
interface JwtConfig {
|
|
73
|
-
/**
|
|
74
|
-
* Access token expiry time
|
|
75
|
-
* @default '15m'
|
|
76
|
-
*/
|
|
77
|
-
accessTokenExpiry: string;
|
|
78
|
-
/**
|
|
79
|
-
* Refresh token expiry time
|
|
80
|
-
* @default '30d'
|
|
81
|
-
*/
|
|
82
|
-
refreshTokenExpiry: string;
|
|
83
|
-
/**
|
|
84
|
-
* Onboarding token expiry time
|
|
85
|
-
* @default '24h'
|
|
86
|
-
*/
|
|
87
|
-
onboardingTokenExpiry: string;
|
|
88
|
-
/**
|
|
89
|
-
* Whether to validate refresh token binding (hash in access token)
|
|
90
|
-
* @default true
|
|
91
|
-
*/
|
|
92
|
-
validateTokenBinding: boolean;
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* Auth guard configuration options
|
|
96
|
-
*/
|
|
97
|
-
interface GuardConfig {
|
|
98
|
-
/**
|
|
99
|
-
* Header name for tenant ID
|
|
100
|
-
* @default 'x-tenant-id'
|
|
101
|
-
*/
|
|
102
|
-
tenantHeaderName: string;
|
|
103
|
-
/**
|
|
104
|
-
* Header name for authorization
|
|
105
|
-
* @default 'authorization'
|
|
106
|
-
*/
|
|
107
|
-
authHeaderName: string;
|
|
108
|
-
/**
|
|
109
|
-
* Token prefix (e.g., 'Bearer')
|
|
110
|
-
* @default 'Bearer'
|
|
111
|
-
*/
|
|
112
|
-
tokenPrefix: string;
|
|
113
|
-
}
|
|
114
|
-
/**
|
|
115
|
-
* Complete api-sdk configuration interface
|
|
116
|
-
*/
|
|
117
|
-
interface ApiSdkConfig {
|
|
118
|
-
/**
|
|
119
|
-
* Cookie configuration
|
|
120
|
-
*/
|
|
121
|
-
cookie?: Partial<CookieConfig>;
|
|
122
|
-
/**
|
|
123
|
-
* JWT token configuration
|
|
124
|
-
*/
|
|
125
|
-
jwt?: Partial<JwtConfig>;
|
|
126
|
-
/**
|
|
127
|
-
* Auth guard configuration
|
|
128
|
-
*/
|
|
129
|
-
guard?: Partial<GuardConfig>;
|
|
130
|
-
}
|
|
131
|
-
/**
|
|
132
|
-
* Full configuration type with all properties required
|
|
133
|
-
*/
|
|
134
|
-
interface FullConfig {
|
|
135
|
-
cookie: CookieConfig;
|
|
136
|
-
jwt: JwtConfig;
|
|
137
|
-
guard: GuardConfig;
|
|
138
|
-
}
|
|
139
|
-
/**
|
|
140
|
-
* Helper function to define configuration with type safety
|
|
141
|
-
* Similar to Tailwind's defineConfig()
|
|
142
|
-
*/
|
|
143
|
-
declare function defineConfig(config: ApiSdkConfig): ApiSdkConfig;
|
|
144
|
-
/**
|
|
145
|
-
* Configure api-sdk with user settings
|
|
146
|
-
* This should be called once in the application's bootstrap (main.ts)
|
|
147
|
-
*/
|
|
148
|
-
declare function configureApiSdk(userConfig: ApiSdkConfig): void;
|
|
149
|
-
/**
|
|
150
|
-
* Get the current configuration
|
|
151
|
-
*/
|
|
152
|
-
declare function getConfig(): FullConfig;
|
|
153
|
-
/**
|
|
154
|
-
* Reset configuration to defaults (for testing)
|
|
155
|
-
*/
|
|
156
|
-
declare function resetConfig(): void;
|
|
157
|
-
/**
|
|
158
|
-
* Get refresh cookie options (convenience method)
|
|
159
|
-
*/
|
|
160
|
-
declare function getRefreshCookieOptions(): {
|
|
161
|
-
httpOnly: boolean;
|
|
162
|
-
secure: boolean;
|
|
163
|
-
sameSite: "strict" | "lax" | "none";
|
|
164
|
-
path: string;
|
|
165
|
-
maxAge: number;
|
|
166
|
-
};
|
|
167
|
-
/**
|
|
168
|
-
* Get JWT expiry settings (convenience method)
|
|
169
|
-
*/
|
|
170
|
-
declare function getJwtExpiry(): {
|
|
171
|
-
access: string;
|
|
172
|
-
refresh: string;
|
|
173
|
-
onboarding: string;
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Hash a token using SHA-256
|
|
178
|
-
* @param token The token to hash
|
|
179
|
-
* @returns The hex-encoded SHA-256 hash
|
|
180
|
-
*/
|
|
181
|
-
declare function hashToken(token: string): string;
|
|
182
|
-
/**
|
|
183
|
-
* Verify a token against its expected hash using constant-time comparison
|
|
184
|
-
* @param token The token to verify
|
|
185
|
-
* @param expectedHash The expected SHA-256 hash
|
|
186
|
-
* @returns true if the token matches the hash
|
|
187
|
-
*/
|
|
188
|
-
declare function verifyTokenHash(token: string, expectedHash: string): boolean;
|
|
189
|
-
|
|
190
13
|
/**
|
|
191
14
|
* Global authentication configuration module
|
|
192
15
|
*
|
|
@@ -261,6 +84,78 @@ declare class AuthConfigModule {
|
|
|
261
84
|
static forRootAsync(): DynamicModule;
|
|
262
85
|
}
|
|
263
86
|
|
|
87
|
+
/**
|
|
88
|
+
* Onboarding Decorator - Marks endpoints that require onboarding token
|
|
89
|
+
*
|
|
90
|
+
* Use this decorator on controllers or route handlers that should only be
|
|
91
|
+
* accessible during the onboarding flow with JWT tokens containing type='onboarding'.
|
|
92
|
+
*
|
|
93
|
+
* These endpoints:
|
|
94
|
+
* - Accept ONLY tokens with type='onboarding'
|
|
95
|
+
* - Reject regular access tokens (type='access')
|
|
96
|
+
* - Skip tenant validation and refresh token checks
|
|
97
|
+
* - Only validate JWT signature and expiry
|
|
98
|
+
*
|
|
99
|
+
* Useful for:
|
|
100
|
+
* - Email/phone verification during onboarding
|
|
101
|
+
* - Onboarding status checks
|
|
102
|
+
* - Resending OTPs during registration
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* // On a controller method
|
|
106
|
+
* @Post('verify-email')
|
|
107
|
+
* @Onboarding()
|
|
108
|
+
* async verifyEmail(@Request() req, @Body() dto: VerifyEmailDto) {
|
|
109
|
+
* const userId = req.user.id; // Available from VrittiAuthGuard
|
|
110
|
+
* return this.service.verifyEmail(userId, dto.otp);
|
|
111
|
+
* }
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* // Multiple onboarding endpoints
|
|
115
|
+
* @Controller('onboarding')
|
|
116
|
+
* export class OnboardingController {
|
|
117
|
+
* @Post('verify-email')
|
|
118
|
+
* @Onboarding()
|
|
119
|
+
* async verifyEmail() { ... }
|
|
120
|
+
*
|
|
121
|
+
* @Post('resend-otp')
|
|
122
|
+
* @Onboarding()
|
|
123
|
+
* async resendOtp() { ... }
|
|
124
|
+
* }
|
|
125
|
+
*/
|
|
126
|
+
declare const Onboarding: () => _nestjs_common.CustomDecorator<string>;
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Public Decorator - Marks endpoints that don't require authentication
|
|
130
|
+
*
|
|
131
|
+
* Use this decorator on controllers or route handlers to bypass VrittiAuthGuard
|
|
132
|
+
* tenant validation. Useful for:
|
|
133
|
+
* - Login/signup endpoints
|
|
134
|
+
* - Health checks
|
|
135
|
+
* - Public documentation endpoints
|
|
136
|
+
* - Webhook endpoints that don't require tenant context
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* // On a controller method
|
|
140
|
+
* @Public()
|
|
141
|
+
* @Post('auth/login')
|
|
142
|
+
* async login(@Body() dto: LoginDto) {
|
|
143
|
+
* return this.authService.login(dto);
|
|
144
|
+
* }
|
|
145
|
+
*
|
|
146
|
+
* @example
|
|
147
|
+
* // On an entire controller
|
|
148
|
+
* @Public()
|
|
149
|
+
* @Controller('health')
|
|
150
|
+
* export class HealthController {
|
|
151
|
+
* @Get()
|
|
152
|
+
* check() {
|
|
153
|
+
* return { status: 'ok' };
|
|
154
|
+
* }
|
|
155
|
+
* }
|
|
156
|
+
*/
|
|
157
|
+
declare const Public: () => _nestjs_common.CustomDecorator<string>;
|
|
158
|
+
|
|
264
159
|
/**
|
|
265
160
|
* Schema Registry Interface
|
|
266
161
|
*
|
|
@@ -275,8 +170,7 @@ declare class AuthConfigModule {
|
|
|
275
170
|
* }
|
|
276
171
|
* }
|
|
277
172
|
*/
|
|
278
|
-
|
|
279
|
-
}
|
|
173
|
+
type SchemaRegistry = {};
|
|
280
174
|
/**
|
|
281
175
|
* Extracts the registered schema type.
|
|
282
176
|
* Falls back to Record<string, unknown> if no schema is registered.
|
|
@@ -386,361 +280,557 @@ interface TenantInfo {
|
|
|
386
280
|
}
|
|
387
281
|
|
|
388
282
|
/**
|
|
389
|
-
*
|
|
390
|
-
*
|
|
391
|
-
* This module provides:
|
|
392
|
-
* - Tenant context management (request-scoped)
|
|
393
|
-
* - Database connection pooling
|
|
394
|
-
* - Dynamic schema/cluster routing
|
|
395
|
-
* - Support for both gateway and microservice modes
|
|
396
|
-
*
|
|
397
|
-
* ## Gateway Mode (API Gateway)
|
|
398
|
-
* - Use DatabaseModule.forServer() method
|
|
399
|
-
* - Automatically extracts tenant from subdomain, falls back to x-tenant-id header
|
|
400
|
-
* - Provide primaryDb configuration and prismaClientConstructor
|
|
401
|
-
* - Automatically queries primary DB for tenant config
|
|
402
|
-
* - Automatically registers TenantContextInterceptor globally
|
|
403
|
-
* - No manual interceptor registration needed
|
|
404
|
-
*
|
|
405
|
-
* ## Microservice Mode (RabbitMQ Workers)
|
|
406
|
-
* - Use DatabaseModule.forMicroservice() method
|
|
407
|
-
* - Only provide prismaClientConstructor
|
|
408
|
-
* - Tenant context comes from RabbitMQ messages
|
|
409
|
-
* - Automatically registers MessageTenantContextInterceptor globally
|
|
410
|
-
* - No manual interceptor registration needed
|
|
283
|
+
* Service responsible for querying the primary database to resolve tenant configurations
|
|
411
284
|
*
|
|
412
|
-
*
|
|
413
|
-
*
|
|
414
|
-
*
|
|
415
|
-
*
|
|
416
|
-
*
|
|
417
|
-
* primaryDb: {
|
|
418
|
-
* host: config.get('PRIMARY_DB_HOST'),
|
|
419
|
-
* port: config.get('PRIMARY_DB_PORT'),
|
|
420
|
-
* username: config.get('PRIMARY_DB_USERNAME'),
|
|
421
|
-
* password: config.get('PRIMARY_DB_PASSWORD'),
|
|
422
|
-
* database: config.get('PRIMARY_DB_DATABASE'),
|
|
423
|
-
* },
|
|
424
|
-
* prismaClientConstructor: PrismaClient,
|
|
425
|
-
* }),
|
|
426
|
-
* })
|
|
285
|
+
* This service:
|
|
286
|
+
* - Connects to the primary database (tenant registry)
|
|
287
|
+
* - Queries tenant metadata (database location, credentials, etc.)
|
|
288
|
+
* - Caches tenant configs in memory to reduce database load
|
|
289
|
+
* - Only used in GATEWAY MODE (microservices receive tenant config from messages)
|
|
427
290
|
*
|
|
428
291
|
* @example
|
|
429
|
-
* //
|
|
430
|
-
*
|
|
431
|
-
*
|
|
432
|
-
* useFactory: (config: ConfigService) => ({
|
|
433
|
-
* prismaClientConstructor: PrismaClient,
|
|
434
|
-
* }),
|
|
435
|
-
* })
|
|
292
|
+
* // In API Gateway
|
|
293
|
+
* const config = await primaryDatabase.getTenantConfig('acme');
|
|
294
|
+
* // Returns: { id, slug, type, databaseHost, databaseName, ... }
|
|
436
295
|
*/
|
|
437
|
-
declare class
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
* @example
|
|
451
|
-
* DatabaseModule.forServer({
|
|
452
|
-
* inject: [ConfigService],
|
|
453
|
-
* useFactory: (config: ConfigService) => ({
|
|
454
|
-
* primaryDb: {
|
|
455
|
-
* host: config.get('PRIMARY_DB_HOST'),
|
|
456
|
-
* port: config.get('PRIMARY_DB_PORT'),
|
|
457
|
-
* username: config.get('PRIMARY_DB_USERNAME'),
|
|
458
|
-
* password: config.get('PRIMARY_DB_PASSWORD'),
|
|
459
|
-
* database: config.get('PRIMARY_DB_DATABASE'),
|
|
460
|
-
* },
|
|
461
|
-
* prismaClientConstructor: PrismaClient,
|
|
462
|
-
* }),
|
|
463
|
-
* })
|
|
464
|
-
*/
|
|
465
|
-
static forServer(options: {
|
|
466
|
-
useFactory: (...args: any[]) => Promise<DatabaseModuleOptions> | DatabaseModuleOptions;
|
|
467
|
-
inject?: any[];
|
|
468
|
-
}): DynamicModule;
|
|
296
|
+
declare class PrimaryDatabaseService implements OnModuleInit, OnModuleDestroy {
|
|
297
|
+
private readonly options;
|
|
298
|
+
private readonly logger;
|
|
299
|
+
/** PostgreSQL connection pool */
|
|
300
|
+
private pool;
|
|
301
|
+
/** Drizzle database instance */
|
|
302
|
+
private db;
|
|
303
|
+
/** In-memory cache: Map<tenantIdentifier, TenantConfig> */
|
|
304
|
+
private readonly tenantConfigCache;
|
|
305
|
+
/** Cache TTL in milliseconds */
|
|
306
|
+
private readonly cacheTTL;
|
|
307
|
+
constructor(options: DatabaseModuleOptions);
|
|
308
|
+
onModuleInit(): Promise<void>;
|
|
469
309
|
/**
|
|
470
|
-
*
|
|
471
|
-
*
|
|
472
|
-
* This mode is for microservices that process messages from queues:
|
|
473
|
-
* - Automatically registers MessageTenantContextInterceptor
|
|
474
|
-
* - Extracts tenant from RabbitMQ message patterns
|
|
475
|
-
* - No primary database needed (tenant comes from message context)
|
|
476
|
-
*
|
|
477
|
-
* @param options Async configuration options
|
|
478
|
-
* @returns Dynamic module configuration with message interceptor
|
|
479
|
-
*
|
|
480
|
-
* @example
|
|
481
|
-
* DatabaseModule.forMicroservice({
|
|
482
|
-
* inject: [ConfigService],
|
|
483
|
-
* useFactory: (config: ConfigService) => ({
|
|
484
|
-
* prismaClientConstructor: PrismaClient,
|
|
485
|
-
* }),
|
|
486
|
-
* })
|
|
310
|
+
* Initialize connection to primary database using Drizzle
|
|
487
311
|
*/
|
|
488
|
-
|
|
489
|
-
useFactory: (...args: any[]) => Promise<DatabaseModuleOptions> | DatabaseModuleOptions;
|
|
490
|
-
inject?: any[];
|
|
491
|
-
}): DynamicModule;
|
|
312
|
+
private initializeDrizzleClient;
|
|
492
313
|
/**
|
|
493
|
-
*
|
|
494
|
-
*
|
|
495
|
-
* @param options Configuration options
|
|
496
|
-
* @param mode Mode of operation (gateway or microservice)
|
|
497
|
-
* @returns Dynamic module configuration
|
|
314
|
+
* Build connection URL from primary database properties
|
|
498
315
|
*/
|
|
499
|
-
private
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
/**
|
|
503
|
-
* Request-scoped service that holds tenant context for the current request or RabbitMQ message
|
|
504
|
-
*
|
|
505
|
-
* IMPORTANT: This service is REQUEST-SCOPED, meaning NestJS creates a new instance
|
|
506
|
-
* for each HTTP request or RabbitMQ message. This ensures tenant isolation and
|
|
507
|
-
* prevents cross-tenant data leaks in concurrent scenarios.
|
|
508
|
-
*
|
|
509
|
-
* @example
|
|
510
|
-
* // In a controller or service
|
|
511
|
-
* constructor(private readonly tenantContext: TenantContextService) {}
|
|
512
|
-
*
|
|
513
|
-
* async handleRequest() {
|
|
514
|
-
* const tenant = this.tenantContext.getTenant();
|
|
515
|
-
* console.log(`Processing request for tenant: ${tenant.tenantSlug}`);
|
|
516
|
-
* }
|
|
517
|
-
*/
|
|
518
|
-
declare class TenantContextService {
|
|
519
|
-
private tenantInfo;
|
|
316
|
+
private buildPrimaryDbUrl;
|
|
520
317
|
/**
|
|
521
|
-
*
|
|
522
|
-
*
|
|
523
|
-
* This is typically called by:
|
|
524
|
-
* - TenantContextInterceptor (for HTTP requests in gateway)
|
|
525
|
-
* - MessageTenantContextInterceptor (for RabbitMQ messages in microservices)
|
|
526
|
-
* - Manual context setup in message handlers
|
|
527
|
-
*
|
|
528
|
-
* @param tenantInfo Complete tenant information
|
|
529
|
-
* @throws Error if tenant context is already set (prevents accidental overwrites)
|
|
318
|
+
* Mask password in connection URL for logging
|
|
530
319
|
*/
|
|
531
|
-
|
|
320
|
+
private maskPassword;
|
|
532
321
|
/**
|
|
533
|
-
* Get tenant
|
|
322
|
+
* Get tenant configuration by identifier (ID or subdomain)
|
|
534
323
|
*
|
|
535
|
-
* @
|
|
536
|
-
* @
|
|
324
|
+
* @param tenantIdentifier Tenant ID or subdomain
|
|
325
|
+
* @returns Tenant configuration or null if not found
|
|
537
326
|
*/
|
|
538
|
-
|
|
327
|
+
getTenantInfo(tenantIdentifier: string): Promise<TenantInfo | null>;
|
|
539
328
|
/**
|
|
540
|
-
*
|
|
541
|
-
*
|
|
542
|
-
* @returns true if tenant context is available
|
|
329
|
+
* Cache tenant information with TTL
|
|
543
330
|
*/
|
|
544
|
-
|
|
331
|
+
private cacheInfo;
|
|
545
332
|
/**
|
|
546
|
-
* Clear tenant
|
|
547
|
-
*
|
|
548
|
-
* This is useful for cleanup in RabbitMQ message handlers
|
|
549
|
-
* after the message has been processed.
|
|
333
|
+
* Clear cached tenant information
|
|
550
334
|
*
|
|
551
|
-
*
|
|
552
|
-
* instance is destroyed when the request ends.
|
|
553
|
-
*/
|
|
554
|
-
clearTenant(): void;
|
|
555
|
-
/**
|
|
556
|
-
* Get tenant ID safely (returns null if not set)
|
|
335
|
+
* Useful when tenant settings are updated and cache needs to be invalidated
|
|
557
336
|
*
|
|
558
|
-
* @
|
|
337
|
+
* @param tenantIdentifier Tenant ID or subdomain
|
|
559
338
|
*/
|
|
560
|
-
|
|
339
|
+
clearTenantCache(tenantIdentifier: string): void;
|
|
561
340
|
/**
|
|
562
|
-
*
|
|
563
|
-
*
|
|
564
|
-
* @returns Tenant subdomain or null
|
|
341
|
+
* Clear all cached tenant configurations
|
|
565
342
|
*/
|
|
566
|
-
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
/**
|
|
570
|
-
* Service responsible for managing tenant-scoped database connections
|
|
571
|
-
*
|
|
572
|
-
* This service:
|
|
573
|
-
* - Maintains a connection pool (Map<cacheKey, TenantConnection>)
|
|
574
|
-
* - Creates new connections dynamically based on tenant context
|
|
575
|
-
* - Reuses existing connections for the same tenant
|
|
576
|
-
* - Supports both cloud schemas and enterprise databases
|
|
577
|
-
* - Automatically cleans up idle connections
|
|
578
|
-
*
|
|
579
|
-
* @example
|
|
580
|
-
* // In a controller or service
|
|
581
|
-
* const db = this.tenantDatabase.drizzleClient;
|
|
582
|
-
* const users = await db.select().from(usersTable);
|
|
583
|
-
*/
|
|
584
|
-
declare class TenantDatabaseService implements OnModuleDestroy {
|
|
585
|
-
private readonly options;
|
|
586
|
-
private readonly tenantContext;
|
|
587
|
-
private readonly logger;
|
|
588
|
-
/** Connection pool: Map<cacheKey, TenantConnection> */
|
|
589
|
-
private readonly clients;
|
|
590
|
-
/** Track last usage time for idle connection cleanup */
|
|
591
|
-
private readonly clientLastUsed;
|
|
592
|
-
/** Cleanup interval timer */
|
|
593
|
-
private cleanupInterval?;
|
|
594
|
-
constructor(options: DatabaseModuleOptions, tenantContext: TenantContextService);
|
|
343
|
+
clearAllCaches(): void;
|
|
595
344
|
/**
|
|
596
|
-
* Get the Drizzle
|
|
597
|
-
* This returns the
|
|
345
|
+
* Get the Drizzle database instance for the primary database.
|
|
346
|
+
* This is a synchronous property that returns the initialized Drizzle client.
|
|
598
347
|
*
|
|
599
|
-
* @returns
|
|
600
|
-
* @throws
|
|
601
|
-
* @throws InternalServerErrorException if connection fails
|
|
348
|
+
* @returns Primary database Drizzle instance
|
|
349
|
+
* @throws Error if primary database client is not initialized
|
|
602
350
|
*/
|
|
603
351
|
get drizzleClient(): TypedDrizzleClient;
|
|
604
352
|
/**
|
|
605
353
|
* Get the Drizzle schema
|
|
606
354
|
*/
|
|
607
|
-
get schema():
|
|
355
|
+
get schema(): typeof this$1.options.drizzleSchema;
|
|
608
356
|
/**
|
|
609
|
-
*
|
|
357
|
+
* Decrypt database credentials
|
|
610
358
|
*
|
|
611
|
-
*
|
|
612
|
-
* 1. Gets tenant info from TenantContextService
|
|
613
|
-
* 2. Builds a connection URL based on tenant type
|
|
614
|
-
* 3. Returns cached client if exists, otherwise creates new one
|
|
359
|
+
* Override this method to implement your encryption strategy
|
|
615
360
|
*
|
|
616
|
-
* @
|
|
617
|
-
* @
|
|
618
|
-
* @throws InternalServerErrorException if connection fails
|
|
361
|
+
* @param encrypted Encrypted value
|
|
362
|
+
* @returns Decrypted value
|
|
619
363
|
*/
|
|
620
|
-
private
|
|
364
|
+
private decrypt;
|
|
365
|
+
onModuleDestroy(): Promise<void>;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
declare class RequestService {
|
|
369
|
+
private readonly request;
|
|
370
|
+
constructor(request: FastifyRequest);
|
|
621
371
|
/**
|
|
622
|
-
*
|
|
372
|
+
* Extract tenant identifier from request headers
|
|
373
|
+
* Priority: x-tenant-id > x-subdomain
|
|
374
|
+
* @returns Tenant identifier or null if not found
|
|
623
375
|
*/
|
|
624
|
-
|
|
376
|
+
getTenantIdentifier(): string | null;
|
|
625
377
|
/**
|
|
626
|
-
*
|
|
378
|
+
* Extract access token from Authorization header
|
|
379
|
+
* Format: "Bearer <token>"
|
|
380
|
+
* @returns Access token or null if not found
|
|
627
381
|
*/
|
|
628
|
-
|
|
382
|
+
getAccessToken(): string | null;
|
|
629
383
|
/**
|
|
630
|
-
*
|
|
384
|
+
* Extract refresh token from httpOnly cookie
|
|
385
|
+
* Cookie name is configurable via api-sdk config
|
|
386
|
+
* @returns Refresh token or null if not found
|
|
631
387
|
*/
|
|
632
|
-
|
|
388
|
+
getRefreshToken(): string | null;
|
|
633
389
|
/**
|
|
634
|
-
*
|
|
390
|
+
* Get a specific header value
|
|
391
|
+
* @param key Header key
|
|
392
|
+
* @returns Header value (string, array, or undefined)
|
|
635
393
|
*/
|
|
636
|
-
|
|
394
|
+
getHeader(key: string): string | string[] | undefined;
|
|
637
395
|
/**
|
|
638
|
-
*
|
|
396
|
+
* Get all headers
|
|
397
|
+
* @returns Record of all headers
|
|
639
398
|
*/
|
|
640
|
-
|
|
399
|
+
getAllHeaders(): FastifyRequest['headers'];
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Vritti Authentication Guard - Validates JWT access tokens and tenant context
|
|
404
|
+
*
|
|
405
|
+
* This guard performs access token validation and attaches user data to request.
|
|
406
|
+
* NOTE: Refresh tokens are NOT validated here - they are only validated in
|
|
407
|
+
* /auth/token and /auth/refresh endpoints (session.service.ts).
|
|
408
|
+
*
|
|
409
|
+
* Validation Flow:
|
|
410
|
+
* 1. Checks if endpoint is marked with @Public() decorator → skip all validation
|
|
411
|
+
* 2. Checks if endpoint is marked with @Onboarding() decorator:
|
|
412
|
+
* - Requires token type='onboarding'
|
|
413
|
+
* - Validates JWT signature and expiry only
|
|
414
|
+
* - Skips tenant validation
|
|
415
|
+
* - Attaches user data to request.user
|
|
416
|
+
* 3. For regular endpoints (no decorator):
|
|
417
|
+
* - Rejects tokens with type='onboarding'
|
|
418
|
+
* - Validates access token (JWT signature, expiry, nbf)
|
|
419
|
+
* - Validates tenant exists and is ACTIVE
|
|
420
|
+
* - Attaches user data to request.user
|
|
421
|
+
*
|
|
422
|
+
* Token Format:
|
|
423
|
+
* - Access Token: "Authorization: Bearer <jwt_token>"
|
|
424
|
+
*
|
|
425
|
+
* Token Types:
|
|
426
|
+
* - type='onboarding': Limited access during registration flow (@Onboarding endpoints only)
|
|
427
|
+
* - type='access': Full access to authenticated endpoints
|
|
428
|
+
*
|
|
429
|
+
* Environment Variables Required:
|
|
430
|
+
* - JWT_SECRET: Secret key to verify access tokens (required)
|
|
431
|
+
*
|
|
432
|
+
* Error Responses:
|
|
433
|
+
* - 401: Invalid/expired access token
|
|
434
|
+
* - 401: Tenant not found or inactive
|
|
435
|
+
* - 401: Tenant identifier not found
|
|
436
|
+
* - 401: Token type mismatch (onboarding token on regular endpoint or vice versa)
|
|
437
|
+
*
|
|
438
|
+
* @example
|
|
439
|
+
* // Automatically registered by AuthConfigModule.forRootAsync()
|
|
440
|
+
* // No manual registration needed
|
|
441
|
+
* //
|
|
442
|
+
* // Internal registration uses useExisting pattern:
|
|
443
|
+
* // providers: [
|
|
444
|
+
* // VrittiAuthGuard,
|
|
445
|
+
* // {
|
|
446
|
+
* // provide: APP_GUARD,
|
|
447
|
+
* // useExisting: VrittiAuthGuard,
|
|
448
|
+
* // },
|
|
449
|
+
* // ]
|
|
450
|
+
*
|
|
451
|
+
* @example
|
|
452
|
+
* // Bypass guard with @Public() decorator
|
|
453
|
+
* @Public()
|
|
454
|
+
* @Post('auth/login')
|
|
455
|
+
* async login(@Body() dto: LoginDto) { ... }
|
|
456
|
+
*
|
|
457
|
+
* @example
|
|
458
|
+
* // Restrict to onboarding tokens with @Onboarding() decorator
|
|
459
|
+
* @Onboarding()
|
|
460
|
+
* @Post('onboarding/verify-email')
|
|
461
|
+
* async verifyEmail(@Request() req) {
|
|
462
|
+
* const userId = req.user.id; // Available from guard
|
|
463
|
+
* ...
|
|
464
|
+
* }
|
|
465
|
+
*/
|
|
466
|
+
declare class VrittiAuthGuard implements CanActivate {
|
|
467
|
+
private readonly reflector;
|
|
468
|
+
readonly _configService: ConfigService;
|
|
469
|
+
private readonly jwtService;
|
|
470
|
+
private readonly primaryDatabase;
|
|
471
|
+
private readonly requestService;
|
|
472
|
+
private readonly logger;
|
|
473
|
+
constructor(reflector: Reflector, _configService: ConfigService, jwtService: JwtService, primaryDatabase: PrimaryDatabaseService, requestService: RequestService);
|
|
474
|
+
canActivate(context: ExecutionContext): Promise<boolean>;
|
|
641
475
|
/**
|
|
642
|
-
*
|
|
476
|
+
* Validate access token with proper expiry checks
|
|
477
|
+
* Throws UnauthorizedException if token is invalid or expired
|
|
643
478
|
*/
|
|
644
|
-
|
|
645
|
-
activeConnections: number;
|
|
646
|
-
tenants: string[];
|
|
647
|
-
};
|
|
479
|
+
private validateAccessToken;
|
|
648
480
|
/**
|
|
649
|
-
*
|
|
481
|
+
* Validate that the access token is bound to the refresh token in the cookie.
|
|
482
|
+
* This prevents token theft - a stolen access token is useless without the
|
|
483
|
+
* corresponding refresh token cookie.
|
|
484
|
+
*
|
|
485
|
+
* @param context - The execution context containing the request
|
|
486
|
+
* @param validatedToken - The decoded and validated JWT token
|
|
487
|
+
* @throws UnauthorizedException if token binding validation fails
|
|
650
488
|
*/
|
|
651
|
-
private
|
|
652
|
-
onModuleDestroy(): Promise<void>;
|
|
489
|
+
private validateRefreshTokenBinding;
|
|
653
490
|
}
|
|
654
491
|
|
|
655
492
|
/**
|
|
656
|
-
*
|
|
493
|
+
* Hash a token using SHA-256
|
|
494
|
+
* @param token The token to hash
|
|
495
|
+
* @returns The hex-encoded SHA-256 hash
|
|
496
|
+
*/
|
|
497
|
+
declare function hashToken(token: string): string;
|
|
498
|
+
/**
|
|
499
|
+
* Verify a token against its expected hash using constant-time comparison
|
|
500
|
+
* @param token The token to verify
|
|
501
|
+
* @param expectedHash The expected SHA-256 hash
|
|
502
|
+
* @returns true if the token matches the hash
|
|
503
|
+
*/
|
|
504
|
+
declare function verifyTokenHash(token: string, expectedHash: string): boolean;
|
|
505
|
+
|
|
506
|
+
/**
|
|
507
|
+
* api-sdk Configuration System
|
|
657
508
|
*
|
|
658
|
-
*
|
|
659
|
-
* - Connects to the primary database (tenant registry)
|
|
660
|
-
* - Queries tenant metadata (database location, credentials, etc.)
|
|
661
|
-
* - Caches tenant configs in memory to reduce database load
|
|
662
|
-
* - Only used in GATEWAY MODE (microservices receive tenant config from messages)
|
|
509
|
+
* Similar to quantum-ui's config pattern - provides a type-safe configuration system
|
|
663
510
|
*
|
|
664
511
|
* @example
|
|
665
|
-
*
|
|
666
|
-
*
|
|
667
|
-
*
|
|
512
|
+
* ```typescript
|
|
513
|
+
* // In vritti-api-nexus/src/main.ts
|
|
514
|
+
* import { configureApiSdk } from '@vritti/api-sdk';
|
|
515
|
+
*
|
|
516
|
+
* configureApiSdk({
|
|
517
|
+
* cookie: {
|
|
518
|
+
* refreshCookieName: 'vritti_refresh',
|
|
519
|
+
* refreshCookieMaxAge: 30 * 24 * 60 * 60 * 1000, // 30 days
|
|
520
|
+
* },
|
|
521
|
+
* jwt: {
|
|
522
|
+
* accessTokenExpiry: '15m',
|
|
523
|
+
* refreshTokenExpiry: '30d',
|
|
524
|
+
* validateTokenBinding: true,
|
|
525
|
+
* },
|
|
526
|
+
* guard: {
|
|
527
|
+
* tenantHeaderName: 'x-tenant-id',
|
|
528
|
+
* },
|
|
529
|
+
* });
|
|
530
|
+
* ```
|
|
668
531
|
*/
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
private pool;
|
|
674
|
-
/** Drizzle database instance */
|
|
675
|
-
private db;
|
|
676
|
-
/** In-memory cache: Map<tenantIdentifier, TenantConfig> */
|
|
677
|
-
private readonly tenantConfigCache;
|
|
678
|
-
/** Cache TTL in milliseconds */
|
|
679
|
-
private readonly cacheTTL;
|
|
680
|
-
constructor(options: DatabaseModuleOptions);
|
|
681
|
-
onModuleInit(): Promise<void>;
|
|
682
|
-
/**
|
|
683
|
-
* Initialize connection to primary database using Drizzle
|
|
684
|
-
*/
|
|
685
|
-
private initializeDrizzleClient;
|
|
532
|
+
/**
|
|
533
|
+
* Cookie configuration options
|
|
534
|
+
*/
|
|
535
|
+
interface CookieConfig {
|
|
686
536
|
/**
|
|
687
|
-
*
|
|
537
|
+
* The name of the httpOnly cookie containing the refresh token
|
|
538
|
+
* @default 'vritti_refresh'
|
|
688
539
|
*/
|
|
689
|
-
|
|
540
|
+
refreshCookieName: string;
|
|
690
541
|
/**
|
|
691
|
-
*
|
|
542
|
+
* Max age of the refresh cookie in milliseconds
|
|
543
|
+
* @default 2592000000 (30 days)
|
|
692
544
|
*/
|
|
693
|
-
|
|
545
|
+
refreshCookieMaxAge: number;
|
|
694
546
|
/**
|
|
695
|
-
*
|
|
696
|
-
*
|
|
697
|
-
* @param tenantIdentifier Tenant ID or subdomain
|
|
698
|
-
* @returns Tenant configuration or null if not found
|
|
547
|
+
* Cookie path
|
|
548
|
+
* @default '/'
|
|
699
549
|
*/
|
|
700
|
-
|
|
550
|
+
refreshCookiePath: string;
|
|
701
551
|
/**
|
|
702
|
-
*
|
|
552
|
+
* Whether the cookie is secure (HTTPS only)
|
|
553
|
+
* @default true in production
|
|
703
554
|
*/
|
|
704
|
-
|
|
555
|
+
refreshCookieSecure: boolean;
|
|
705
556
|
/**
|
|
706
|
-
*
|
|
707
|
-
*
|
|
708
|
-
* Useful when tenant settings are updated and cache needs to be invalidated
|
|
709
|
-
*
|
|
710
|
-
* @param tenantIdentifier Tenant ID or subdomain
|
|
557
|
+
* SameSite attribute for the cookie
|
|
558
|
+
* @default 'strict'
|
|
711
559
|
*/
|
|
712
|
-
|
|
560
|
+
refreshCookieSameSite: 'strict' | 'lax' | 'none';
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* JWT token configuration options
|
|
564
|
+
*/
|
|
565
|
+
interface JwtConfig {
|
|
713
566
|
/**
|
|
714
|
-
*
|
|
567
|
+
* Access token expiry time
|
|
568
|
+
* @default '15m'
|
|
715
569
|
*/
|
|
716
|
-
|
|
570
|
+
accessTokenExpiry: string;
|
|
717
571
|
/**
|
|
718
|
-
*
|
|
719
|
-
*
|
|
720
|
-
*
|
|
721
|
-
* @returns Primary database Drizzle instance
|
|
722
|
-
* @throws Error if primary database client is not initialized
|
|
572
|
+
* Refresh token expiry time
|
|
573
|
+
* @default '30d'
|
|
723
574
|
*/
|
|
724
|
-
|
|
575
|
+
refreshTokenExpiry: string;
|
|
725
576
|
/**
|
|
726
|
-
*
|
|
577
|
+
* Onboarding token expiry time
|
|
578
|
+
* @default '24h'
|
|
727
579
|
*/
|
|
728
|
-
|
|
580
|
+
onboardingTokenExpiry: string;
|
|
729
581
|
/**
|
|
730
|
-
*
|
|
731
|
-
*
|
|
732
|
-
* Override this method to implement your encryption strategy
|
|
733
|
-
*
|
|
734
|
-
* @param encrypted Encrypted value
|
|
735
|
-
* @returns Decrypted value
|
|
582
|
+
* Whether to validate refresh token binding (hash in access token)
|
|
583
|
+
* @default true
|
|
736
584
|
*/
|
|
737
|
-
|
|
738
|
-
onModuleDestroy(): Promise<void>;
|
|
585
|
+
validateTokenBinding: boolean;
|
|
739
586
|
}
|
|
740
|
-
|
|
741
587
|
/**
|
|
742
|
-
*
|
|
743
|
-
|
|
588
|
+
* Auth guard configuration options
|
|
589
|
+
*/
|
|
590
|
+
interface GuardConfig {
|
|
591
|
+
/**
|
|
592
|
+
* Header name for tenant ID
|
|
593
|
+
* @default 'x-tenant-id'
|
|
594
|
+
*/
|
|
595
|
+
tenantHeaderName: string;
|
|
596
|
+
/**
|
|
597
|
+
* Header name for authorization
|
|
598
|
+
* @default 'authorization'
|
|
599
|
+
*/
|
|
600
|
+
authHeaderName: string;
|
|
601
|
+
/**
|
|
602
|
+
* Token prefix (e.g., 'Bearer')
|
|
603
|
+
* @default 'Bearer'
|
|
604
|
+
*/
|
|
605
|
+
tokenPrefix: string;
|
|
606
|
+
}
|
|
607
|
+
/**
|
|
608
|
+
* Complete api-sdk configuration interface
|
|
609
|
+
*/
|
|
610
|
+
interface ApiSdkConfig {
|
|
611
|
+
/**
|
|
612
|
+
* Cookie configuration
|
|
613
|
+
*/
|
|
614
|
+
cookie?: Partial<CookieConfig>;
|
|
615
|
+
/**
|
|
616
|
+
* JWT token configuration
|
|
617
|
+
*/
|
|
618
|
+
jwt?: Partial<JwtConfig>;
|
|
619
|
+
/**
|
|
620
|
+
* Auth guard configuration
|
|
621
|
+
*/
|
|
622
|
+
guard?: Partial<GuardConfig>;
|
|
623
|
+
}
|
|
624
|
+
/**
|
|
625
|
+
* Full configuration type with all properties required
|
|
626
|
+
*/
|
|
627
|
+
interface FullConfig {
|
|
628
|
+
cookie: CookieConfig;
|
|
629
|
+
jwt: JwtConfig;
|
|
630
|
+
guard: GuardConfig;
|
|
631
|
+
}
|
|
632
|
+
/**
|
|
633
|
+
* Helper function to define configuration with type safety
|
|
634
|
+
* Similar to Tailwind's defineConfig()
|
|
635
|
+
*/
|
|
636
|
+
declare function defineConfig(config: ApiSdkConfig): ApiSdkConfig;
|
|
637
|
+
/**
|
|
638
|
+
* Configure api-sdk with user settings
|
|
639
|
+
* This should be called once in the application's bootstrap (main.ts)
|
|
640
|
+
*/
|
|
641
|
+
declare function configureApiSdk(userConfig: ApiSdkConfig): void;
|
|
642
|
+
/**
|
|
643
|
+
* Get the current configuration
|
|
644
|
+
*/
|
|
645
|
+
declare function getConfig(): FullConfig;
|
|
646
|
+
/**
|
|
647
|
+
* Reset configuration to defaults (for testing)
|
|
648
|
+
*/
|
|
649
|
+
declare function resetConfig(): void;
|
|
650
|
+
/**
|
|
651
|
+
* Get refresh cookie options (convenience method)
|
|
652
|
+
*/
|
|
653
|
+
declare function getRefreshCookieOptions(): {
|
|
654
|
+
httpOnly: boolean;
|
|
655
|
+
secure: boolean;
|
|
656
|
+
sameSite: "strict" | "lax" | "none";
|
|
657
|
+
path: string;
|
|
658
|
+
maxAge: number;
|
|
659
|
+
};
|
|
660
|
+
/**
|
|
661
|
+
* Get JWT expiry settings (convenience method)
|
|
662
|
+
*/
|
|
663
|
+
declare function getJwtExpiry(): {
|
|
664
|
+
access: string;
|
|
665
|
+
refresh: string;
|
|
666
|
+
onboarding: string;
|
|
667
|
+
};
|
|
668
|
+
|
|
669
|
+
/**
|
|
670
|
+
* Dynamic module for multi-tenant database management
|
|
671
|
+
*
|
|
672
|
+
* This module provides:
|
|
673
|
+
* - Tenant context management (request-scoped)
|
|
674
|
+
* - Database connection pooling
|
|
675
|
+
* - Dynamic schema/cluster routing
|
|
676
|
+
* - Support for both gateway and microservice modes
|
|
677
|
+
*
|
|
678
|
+
* ## Gateway Mode (API Gateway)
|
|
679
|
+
* - Use DatabaseModule.forServer() method
|
|
680
|
+
* - Automatically extracts tenant from subdomain, falls back to x-tenant-id header
|
|
681
|
+
* - Provide primaryDb configuration and prismaClientConstructor
|
|
682
|
+
* - Automatically queries primary DB for tenant config
|
|
683
|
+
* - Automatically registers TenantContextInterceptor globally
|
|
684
|
+
* - No manual interceptor registration needed
|
|
685
|
+
*
|
|
686
|
+
* ## Microservice Mode (RabbitMQ Workers)
|
|
687
|
+
* - Use DatabaseModule.forMicroservice() method
|
|
688
|
+
* - Only provide prismaClientConstructor
|
|
689
|
+
* - Tenant context comes from RabbitMQ messages
|
|
690
|
+
* - Automatically registers MessageTenantContextInterceptor globally
|
|
691
|
+
* - No manual interceptor registration needed
|
|
692
|
+
*
|
|
693
|
+
* @example
|
|
694
|
+
* // Gateway configuration
|
|
695
|
+
* DatabaseModule.forServer({
|
|
696
|
+
* inject: [ConfigService],
|
|
697
|
+
* useFactory: (config: ConfigService) => ({
|
|
698
|
+
* primaryDb: {
|
|
699
|
+
* host: config.get('PRIMARY_DB_HOST'),
|
|
700
|
+
* port: config.get('PRIMARY_DB_PORT'),
|
|
701
|
+
* username: config.get('PRIMARY_DB_USERNAME'),
|
|
702
|
+
* password: config.get('PRIMARY_DB_PASSWORD'),
|
|
703
|
+
* database: config.get('PRIMARY_DB_DATABASE'),
|
|
704
|
+
* },
|
|
705
|
+
* prismaClientConstructor: PrismaClient,
|
|
706
|
+
* }),
|
|
707
|
+
* })
|
|
708
|
+
*
|
|
709
|
+
* @example
|
|
710
|
+
* // Microservice configuration
|
|
711
|
+
* DatabaseModule.forMicroservice({
|
|
712
|
+
* inject: [ConfigService],
|
|
713
|
+
* useFactory: (config: ConfigService) => ({
|
|
714
|
+
* prismaClientConstructor: PrismaClient,
|
|
715
|
+
* }),
|
|
716
|
+
* })
|
|
717
|
+
*/
|
|
718
|
+
declare class DatabaseModule {
|
|
719
|
+
/**
|
|
720
|
+
* Configure DatabaseModule for Gateway/HTTP mode (multi-tenant web servers)
|
|
721
|
+
*
|
|
722
|
+
* This mode is for API Gateways that handle HTTP requests:
|
|
723
|
+
* - Automatically registers TenantContextInterceptor
|
|
724
|
+
* - Extracts tenant from subdomain or x-tenant-id header
|
|
725
|
+
* - Queries primary database for tenant configuration
|
|
726
|
+
* - Provides PrimaryDatabaseService for tenant lookup
|
|
727
|
+
*
|
|
728
|
+
* @param options Async configuration options
|
|
729
|
+
* @returns Dynamic module configuration with HTTP interceptor
|
|
730
|
+
*
|
|
731
|
+
* @example
|
|
732
|
+
* DatabaseModule.forServer({
|
|
733
|
+
* inject: [ConfigService],
|
|
734
|
+
* useFactory: (config: ConfigService) => ({
|
|
735
|
+
* primaryDb: {
|
|
736
|
+
* host: config.get('PRIMARY_DB_HOST'),
|
|
737
|
+
* port: config.get('PRIMARY_DB_PORT'),
|
|
738
|
+
* username: config.get('PRIMARY_DB_USERNAME'),
|
|
739
|
+
* password: config.get('PRIMARY_DB_PASSWORD'),
|
|
740
|
+
* database: config.get('PRIMARY_DB_DATABASE'),
|
|
741
|
+
* },
|
|
742
|
+
* prismaClientConstructor: PrismaClient,
|
|
743
|
+
* }),
|
|
744
|
+
* })
|
|
745
|
+
*/
|
|
746
|
+
static forServer(options: {
|
|
747
|
+
useFactory: (...args: any[]) => Promise<DatabaseModuleOptions> | DatabaseModuleOptions;
|
|
748
|
+
inject?: any[];
|
|
749
|
+
}): DynamicModule;
|
|
750
|
+
/**
|
|
751
|
+
* Configure DatabaseModule for Microservice/Messaging mode (RabbitMQ workers)
|
|
752
|
+
*
|
|
753
|
+
* This mode is for microservices that process messages from queues:
|
|
754
|
+
* - Automatically registers MessageTenantContextInterceptor
|
|
755
|
+
* - Extracts tenant from RabbitMQ message patterns
|
|
756
|
+
* - No primary database needed (tenant comes from message context)
|
|
757
|
+
*
|
|
758
|
+
* @param options Async configuration options
|
|
759
|
+
* @returns Dynamic module configuration with message interceptor
|
|
760
|
+
*
|
|
761
|
+
* @example
|
|
762
|
+
* DatabaseModule.forMicroservice({
|
|
763
|
+
* inject: [ConfigService],
|
|
764
|
+
* useFactory: (config: ConfigService) => ({
|
|
765
|
+
* prismaClientConstructor: PrismaClient,
|
|
766
|
+
* }),
|
|
767
|
+
* })
|
|
768
|
+
*/
|
|
769
|
+
static forMicroservice(options: {
|
|
770
|
+
useFactory: (...args: any[]) => Promise<DatabaseModuleOptions> | DatabaseModuleOptions;
|
|
771
|
+
inject?: any[];
|
|
772
|
+
}): DynamicModule;
|
|
773
|
+
/**
|
|
774
|
+
* Internal helper to create dynamic module with conditional interceptor registration
|
|
775
|
+
*
|
|
776
|
+
* @param options Configuration options
|
|
777
|
+
* @param mode Mode of operation (gateway or microservice)
|
|
778
|
+
* @returns Dynamic module configuration
|
|
779
|
+
*/
|
|
780
|
+
private static createDynamicModule;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
/**
|
|
784
|
+
* Parameter decorator that injects tenant metadata into controller method
|
|
785
|
+
*
|
|
786
|
+
* This decorator retrieves tenant information (ID, slug, type, etc.)
|
|
787
|
+
* from the REQUEST-SCOPED TenantContextService.
|
|
788
|
+
*
|
|
789
|
+
* Useful for:
|
|
790
|
+
* - Logging tenant-specific information
|
|
791
|
+
* - Implementing tenant-specific business logic
|
|
792
|
+
* - Auditing and tracking
|
|
793
|
+
* - Conditional feature flags
|
|
794
|
+
*
|
|
795
|
+
* @returns TenantInfo object with tenant metadata
|
|
796
|
+
*
|
|
797
|
+
* @example
|
|
798
|
+
* // Access tenant metadata
|
|
799
|
+
* @Get('info')
|
|
800
|
+
* async getTenantInfo(@Tenant() tenant: TenantInfo) {
|
|
801
|
+
* return {
|
|
802
|
+
* id: tenant.id,
|
|
803
|
+
* subdomain: tenant.subdomain,
|
|
804
|
+
* type: tenant.type,
|
|
805
|
+
* };
|
|
806
|
+
* }
|
|
807
|
+
*
|
|
808
|
+
* @example
|
|
809
|
+
* // Use for logging
|
|
810
|
+
* @Post()
|
|
811
|
+
* async createUser(
|
|
812
|
+
* @Body() dto: CreateUserDto,
|
|
813
|
+
* @Tenant() tenant: TenantInfo,
|
|
814
|
+
* ) {
|
|
815
|
+
* this.logger.log(`Creating user for tenant: ${tenant.subdomain}`);
|
|
816
|
+
* // ...
|
|
817
|
+
* }
|
|
818
|
+
*
|
|
819
|
+
* @example
|
|
820
|
+
* // Conditional business logic
|
|
821
|
+
* @Get('features')
|
|
822
|
+
* async getFeatures(@Tenant() tenant: TenantInfo) {
|
|
823
|
+
* if (tenant.type === 'ENTERPRISE') {
|
|
824
|
+
* return ['feature-a', 'feature-b', 'feature-c'];
|
|
825
|
+
* }
|
|
826
|
+
* return ['feature-a'];
|
|
827
|
+
* }
|
|
828
|
+
*/
|
|
829
|
+
declare const Tenant: (...dataOrPipes: unknown[]) => ParameterDecorator;
|
|
830
|
+
|
|
831
|
+
/**
|
|
832
|
+
* Drizzle ORM v2 object-based where filter type.
|
|
833
|
+
* Supports simple equality, operators, AND/OR/NOT, and RAW SQL.
|
|
744
834
|
*
|
|
745
835
|
* @example
|
|
746
836
|
* ```typescript
|
|
@@ -1072,16 +1162,169 @@ declare abstract class PrimaryBaseRepository<TTable extends PgTable, TInsert = I
|
|
|
1072
1162
|
}
|
|
1073
1163
|
|
|
1074
1164
|
/**
|
|
1075
|
-
*
|
|
1076
|
-
* TTable['_']['name'] gives us the string literal type (e.g., 'products')
|
|
1077
|
-
*/
|
|
1078
|
-
type ExtractTableName<TTable extends PgTable> = TTable['_']['name'];
|
|
1079
|
-
/**
|
|
1080
|
-
* Abstract base repository for tenant-scoped database operations using Drizzle ORM.
|
|
1081
|
-
* All operations are automatically scoped to the current tenant.
|
|
1165
|
+
* Request-scoped service that holds tenant context for the current request or RabbitMQ message
|
|
1082
1166
|
*
|
|
1083
|
-
*
|
|
1084
|
-
*
|
|
1167
|
+
* IMPORTANT: This service is REQUEST-SCOPED, meaning NestJS creates a new instance
|
|
1168
|
+
* for each HTTP request or RabbitMQ message. This ensures tenant isolation and
|
|
1169
|
+
* prevents cross-tenant data leaks in concurrent scenarios.
|
|
1170
|
+
*
|
|
1171
|
+
* @example
|
|
1172
|
+
* // In a controller or service
|
|
1173
|
+
* constructor(private readonly tenantContext: TenantContextService) {}
|
|
1174
|
+
*
|
|
1175
|
+
* async handleRequest() {
|
|
1176
|
+
* const tenant = this.tenantContext.getTenant();
|
|
1177
|
+
* console.log(`Processing request for tenant: ${tenant.tenantSlug}`);
|
|
1178
|
+
* }
|
|
1179
|
+
*/
|
|
1180
|
+
declare class TenantContextService {
|
|
1181
|
+
private tenantInfo;
|
|
1182
|
+
/**
|
|
1183
|
+
* Set tenant information for this request/message
|
|
1184
|
+
*
|
|
1185
|
+
* This is typically called by:
|
|
1186
|
+
* - TenantContextInterceptor (for HTTP requests in gateway)
|
|
1187
|
+
* - MessageTenantContextInterceptor (for RabbitMQ messages in microservices)
|
|
1188
|
+
* - Manual context setup in message handlers
|
|
1189
|
+
*
|
|
1190
|
+
* @param tenantInfo Complete tenant information
|
|
1191
|
+
* @throws Error if tenant context is already set (prevents accidental overwrites)
|
|
1192
|
+
*/
|
|
1193
|
+
setTenant(tenantInfo: TenantInfo): void;
|
|
1194
|
+
/**
|
|
1195
|
+
* Get tenant information for this request/message
|
|
1196
|
+
*
|
|
1197
|
+
* @returns Tenant information
|
|
1198
|
+
* @throws UnauthorizedException if tenant context hasn't been set
|
|
1199
|
+
*/
|
|
1200
|
+
getTenant(): TenantInfo;
|
|
1201
|
+
/**
|
|
1202
|
+
* Check if tenant context has been set
|
|
1203
|
+
*
|
|
1204
|
+
* @returns true if tenant context is available
|
|
1205
|
+
*/
|
|
1206
|
+
hasTenant(): boolean;
|
|
1207
|
+
/**
|
|
1208
|
+
* Clear tenant context
|
|
1209
|
+
*
|
|
1210
|
+
* This is useful for cleanup in RabbitMQ message handlers
|
|
1211
|
+
* after the message has been processed.
|
|
1212
|
+
*
|
|
1213
|
+
* HTTP requests don't need manual cleanup as the service
|
|
1214
|
+
* instance is destroyed when the request ends.
|
|
1215
|
+
*/
|
|
1216
|
+
clearTenant(): void;
|
|
1217
|
+
/**
|
|
1218
|
+
* Get tenant ID safely (returns null if not set)
|
|
1219
|
+
*
|
|
1220
|
+
* @returns Tenant ID or null
|
|
1221
|
+
*/
|
|
1222
|
+
getTenantIdSafe(): string | null;
|
|
1223
|
+
/**
|
|
1224
|
+
* Get tenant subdomain safely (returns null if not set)
|
|
1225
|
+
*
|
|
1226
|
+
* @returns Tenant subdomain or null
|
|
1227
|
+
*/
|
|
1228
|
+
getTenantSubdomainSafe(): string | null;
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
/**
|
|
1232
|
+
* Service responsible for managing tenant-scoped database connections
|
|
1233
|
+
*
|
|
1234
|
+
* This service:
|
|
1235
|
+
* - Maintains a connection pool (Map<cacheKey, TenantConnection>)
|
|
1236
|
+
* - Creates new connections dynamically based on tenant context
|
|
1237
|
+
* - Reuses existing connections for the same tenant
|
|
1238
|
+
* - Supports both cloud schemas and enterprise databases
|
|
1239
|
+
* - Automatically cleans up idle connections
|
|
1240
|
+
*
|
|
1241
|
+
* @example
|
|
1242
|
+
* // In a controller or service
|
|
1243
|
+
* const db = this.tenantDatabase.drizzleClient;
|
|
1244
|
+
* const users = await db.select().from(usersTable);
|
|
1245
|
+
*/
|
|
1246
|
+
declare class TenantDatabaseService implements OnModuleDestroy {
|
|
1247
|
+
private readonly options;
|
|
1248
|
+
private readonly tenantContext;
|
|
1249
|
+
private readonly logger;
|
|
1250
|
+
/** Connection pool: Map<cacheKey, TenantConnection> */
|
|
1251
|
+
private readonly clients;
|
|
1252
|
+
/** Track last usage time for idle connection cleanup */
|
|
1253
|
+
private readonly clientLastUsed;
|
|
1254
|
+
/** Cleanup interval timer */
|
|
1255
|
+
private cleanupInterval?;
|
|
1256
|
+
constructor(options: DatabaseModuleOptions, tenantContext: TenantContextService);
|
|
1257
|
+
/**
|
|
1258
|
+
* Get the Drizzle client for the current tenant's database.
|
|
1259
|
+
* This returns the tenant-scoped database client.
|
|
1260
|
+
*
|
|
1261
|
+
* @returns Tenant-scoped Drizzle database instance
|
|
1262
|
+
* @throws UnauthorizedException if tenant context not set
|
|
1263
|
+
* @throws InternalServerErrorException if connection fails
|
|
1264
|
+
*/
|
|
1265
|
+
get drizzleClient(): TypedDrizzleClient;
|
|
1266
|
+
/**
|
|
1267
|
+
* Get the Drizzle schema
|
|
1268
|
+
*/
|
|
1269
|
+
get schema(): Record<string, unknown>;
|
|
1270
|
+
/**
|
|
1271
|
+
* Get tenant-scoped database client for the current request/message
|
|
1272
|
+
*
|
|
1273
|
+
* This method:
|
|
1274
|
+
* 1. Gets tenant info from TenantContextService
|
|
1275
|
+
* 2. Builds a connection URL based on tenant type
|
|
1276
|
+
* 3. Returns cached client if exists, otherwise creates new one
|
|
1277
|
+
*
|
|
1278
|
+
* @returns Drizzle database instance
|
|
1279
|
+
* @throws UnauthorizedException if tenant context not set
|
|
1280
|
+
* @throws InternalServerErrorException if connection fails
|
|
1281
|
+
*/
|
|
1282
|
+
private getDbClient;
|
|
1283
|
+
/**
|
|
1284
|
+
* Create a new database client for the given tenant (synchronous)
|
|
1285
|
+
*/
|
|
1286
|
+
private createDbClientSync;
|
|
1287
|
+
/**
|
|
1288
|
+
* Build connection URL for tenant (dedicated database)
|
|
1289
|
+
*/
|
|
1290
|
+
private buildTenantDbUrl;
|
|
1291
|
+
/**
|
|
1292
|
+
* Build cache key for connection pooling
|
|
1293
|
+
*/
|
|
1294
|
+
private buildCacheKey;
|
|
1295
|
+
/**
|
|
1296
|
+
* Start periodic cleanup of idle connections
|
|
1297
|
+
*/
|
|
1298
|
+
private startConnectionCleaner;
|
|
1299
|
+
/**
|
|
1300
|
+
* Clean up idle connections that haven't been used recently
|
|
1301
|
+
*/
|
|
1302
|
+
private cleanupIdleConnections;
|
|
1303
|
+
/**
|
|
1304
|
+
* Get current connection pool statistics
|
|
1305
|
+
*/
|
|
1306
|
+
getPoolStats(): {
|
|
1307
|
+
activeConnections: number;
|
|
1308
|
+
tenants: string[];
|
|
1309
|
+
};
|
|
1310
|
+
/**
|
|
1311
|
+
* Mask password in connection URL for logging
|
|
1312
|
+
*/
|
|
1313
|
+
private maskPassword;
|
|
1314
|
+
onModuleDestroy(): Promise<void>;
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
/**
|
|
1318
|
+
* Type helper to extract table name from Drizzle table.
|
|
1319
|
+
* TTable['_']['name'] gives us the string literal type (e.g., 'products')
|
|
1320
|
+
*/
|
|
1321
|
+
type ExtractTableName<TTable extends PgTable> = TTable['_']['name'];
|
|
1322
|
+
/**
|
|
1323
|
+
* Abstract base repository for tenant-scoped database operations using Drizzle ORM.
|
|
1324
|
+
* All operations are automatically scoped to the current tenant.
|
|
1325
|
+
*
|
|
1326
|
+
* @template TTable - The Drizzle table type (must be registered in SchemaRegistry)
|
|
1327
|
+
* @template TInsert - Type for insert operations (inferred from table.$inferInsert)
|
|
1085
1328
|
* @template TSelect - Type for select operations (inferred from table.$inferSelect)
|
|
1086
1329
|
*
|
|
1087
1330
|
* @remarks
|
|
@@ -1321,350 +1564,30 @@ declare abstract class TenantBaseRepository<TTable extends PgTable, TInsert = In
|
|
|
1321
1564
|
*
|
|
1322
1565
|
* // Count all products
|
|
1323
1566
|
* const total = await productRepository.count();
|
|
1324
|
-
*
|
|
1325
|
-
* // Count active products
|
|
1326
|
-
* const activeCount = await productRepository.count(
|
|
1327
|
-
* eq(products.status, 'ACTIVE')
|
|
1328
|
-
* );
|
|
1329
|
-
* ```
|
|
1330
|
-
*/
|
|
1331
|
-
count(where?: SQL): Promise<number>;
|
|
1332
|
-
/**
|
|
1333
|
-
* Check if a record exists
|
|
1334
|
-
*
|
|
1335
|
-
* @param where - SQL condition to match records
|
|
1336
|
-
* @returns Promise resolving to true if at least one record exists, false otherwise
|
|
1337
|
-
*
|
|
1338
|
-
* @example
|
|
1339
|
-
* ```typescript
|
|
1340
|
-
* import { eq } from 'drizzle-orm';
|
|
1341
|
-
*
|
|
1342
|
-
* const skuExists = await productRepository.exists(
|
|
1343
|
-
* eq(products.sku, 'WDG-001')
|
|
1344
|
-
* );
|
|
1345
|
-
* ```
|
|
1346
|
-
*/
|
|
1347
|
-
exists(where: SQL): Promise<boolean>;
|
|
1348
|
-
}
|
|
1349
|
-
|
|
1350
|
-
declare class RequestService {
|
|
1351
|
-
private readonly request;
|
|
1352
|
-
constructor(request: FastifyRequest);
|
|
1353
|
-
/**
|
|
1354
|
-
* Extract tenant identifier from request headers
|
|
1355
|
-
* Priority: x-tenant-id > x-subdomain
|
|
1356
|
-
* @returns Tenant identifier or null if not found
|
|
1357
|
-
*/
|
|
1358
|
-
getTenantIdentifier(): string | null;
|
|
1359
|
-
/**
|
|
1360
|
-
* Extract access token from Authorization header
|
|
1361
|
-
* Format: "Bearer <token>"
|
|
1362
|
-
* @returns Access token or null if not found
|
|
1363
|
-
*/
|
|
1364
|
-
getAccessToken(): string | null;
|
|
1365
|
-
/**
|
|
1366
|
-
* Extract refresh token from httpOnly cookie
|
|
1367
|
-
* Cookie name is configurable via api-sdk config
|
|
1368
|
-
* @returns Refresh token or null if not found
|
|
1369
|
-
*/
|
|
1370
|
-
getRefreshToken(): string | null;
|
|
1371
|
-
/**
|
|
1372
|
-
* Get a specific header value
|
|
1373
|
-
* @param key Header key
|
|
1374
|
-
* @returns Header value (string, array, or undefined)
|
|
1375
|
-
*/
|
|
1376
|
-
getHeader(key: string): string | string[] | undefined;
|
|
1377
|
-
/**
|
|
1378
|
-
* Get all headers
|
|
1379
|
-
* @returns Record of all headers
|
|
1380
|
-
*/
|
|
1381
|
-
getAllHeaders(): FastifyRequest['headers'];
|
|
1382
|
-
}
|
|
1383
|
-
|
|
1384
|
-
/**
|
|
1385
|
-
* Vritti Authentication Guard - Validates JWT access tokens and tenant context
|
|
1386
|
-
*
|
|
1387
|
-
* This guard performs access token validation and attaches user data to request.
|
|
1388
|
-
* NOTE: Refresh tokens are NOT validated here - they are only validated in
|
|
1389
|
-
* /auth/token and /auth/refresh endpoints (session.service.ts).
|
|
1390
|
-
*
|
|
1391
|
-
* Validation Flow:
|
|
1392
|
-
* 1. Checks if endpoint is marked with @Public() decorator → skip all validation
|
|
1393
|
-
* 2. Checks if endpoint is marked with @Onboarding() decorator:
|
|
1394
|
-
* - Requires token type='onboarding'
|
|
1395
|
-
* - Validates JWT signature and expiry only
|
|
1396
|
-
* - Skips tenant validation
|
|
1397
|
-
* - Attaches user data to request.user
|
|
1398
|
-
* 3. For regular endpoints (no decorator):
|
|
1399
|
-
* - Rejects tokens with type='onboarding'
|
|
1400
|
-
* - Validates access token (JWT signature, expiry, nbf)
|
|
1401
|
-
* - Validates tenant exists and is ACTIVE
|
|
1402
|
-
* - Attaches user data to request.user
|
|
1403
|
-
*
|
|
1404
|
-
* Token Format:
|
|
1405
|
-
* - Access Token: "Authorization: Bearer <jwt_token>"
|
|
1406
|
-
*
|
|
1407
|
-
* Token Types:
|
|
1408
|
-
* - type='onboarding': Limited access during registration flow (@Onboarding endpoints only)
|
|
1409
|
-
* - type='access': Full access to authenticated endpoints
|
|
1410
|
-
*
|
|
1411
|
-
* Environment Variables Required:
|
|
1412
|
-
* - JWT_SECRET: Secret key to verify access tokens (required)
|
|
1413
|
-
*
|
|
1414
|
-
* Error Responses:
|
|
1415
|
-
* - 401: Invalid/expired access token
|
|
1416
|
-
* - 401: Tenant not found or inactive
|
|
1417
|
-
* - 401: Tenant identifier not found
|
|
1418
|
-
* - 401: Token type mismatch (onboarding token on regular endpoint or vice versa)
|
|
1419
|
-
*
|
|
1420
|
-
* @example
|
|
1421
|
-
* // Automatically registered by AuthConfigModule.forRootAsync()
|
|
1422
|
-
* // No manual registration needed
|
|
1423
|
-
* //
|
|
1424
|
-
* // Internal registration uses useExisting pattern:
|
|
1425
|
-
* // providers: [
|
|
1426
|
-
* // VrittiAuthGuard,
|
|
1427
|
-
* // {
|
|
1428
|
-
* // provide: APP_GUARD,
|
|
1429
|
-
* // useExisting: VrittiAuthGuard,
|
|
1430
|
-
* // },
|
|
1431
|
-
* // ]
|
|
1432
|
-
*
|
|
1433
|
-
* @example
|
|
1434
|
-
* // Bypass guard with @Public() decorator
|
|
1435
|
-
* @Public()
|
|
1436
|
-
* @Post('auth/login')
|
|
1437
|
-
* async login(@Body() dto: LoginDto) { ... }
|
|
1438
|
-
*
|
|
1439
|
-
* @example
|
|
1440
|
-
* // Restrict to onboarding tokens with @Onboarding() decorator
|
|
1441
|
-
* @Onboarding()
|
|
1442
|
-
* @Post('onboarding/verify-email')
|
|
1443
|
-
* async verifyEmail(@Request() req) {
|
|
1444
|
-
* const userId = req.user.id; // Available from guard
|
|
1445
|
-
* ...
|
|
1446
|
-
* }
|
|
1447
|
-
*/
|
|
1448
|
-
declare class VrittiAuthGuard implements CanActivate {
|
|
1449
|
-
private readonly reflector;
|
|
1450
|
-
private readonly configService;
|
|
1451
|
-
private readonly jwtService;
|
|
1452
|
-
private readonly primaryDatabase;
|
|
1453
|
-
private readonly requestService;
|
|
1454
|
-
private readonly logger;
|
|
1455
|
-
constructor(reflector: Reflector, configService: ConfigService, jwtService: JwtService, primaryDatabase: PrimaryDatabaseService, requestService: RequestService);
|
|
1456
|
-
canActivate(context: ExecutionContext): Promise<boolean>;
|
|
1457
|
-
/**
|
|
1458
|
-
* Validate access token with proper expiry checks
|
|
1459
|
-
* Throws UnauthorizedException if token is invalid or expired
|
|
1460
|
-
*/
|
|
1461
|
-
private validateAccessToken;
|
|
1462
|
-
/**
|
|
1463
|
-
* Validate that the access token is bound to the refresh token in the cookie.
|
|
1464
|
-
* This prevents token theft - a stolen access token is useless without the
|
|
1465
|
-
* corresponding refresh token cookie.
|
|
1466
|
-
*
|
|
1467
|
-
* @param context - The execution context containing the request
|
|
1468
|
-
* @param validatedToken - The decoded and validated JWT token
|
|
1469
|
-
* @throws UnauthorizedException if token binding validation fails
|
|
1470
|
-
*/
|
|
1471
|
-
private validateRefreshTokenBinding;
|
|
1472
|
-
}
|
|
1473
|
-
|
|
1474
|
-
/**
|
|
1475
|
-
* Parameter decorator that injects tenant metadata into controller method
|
|
1476
|
-
*
|
|
1477
|
-
* This decorator retrieves tenant information (ID, slug, type, etc.)
|
|
1478
|
-
* from the REQUEST-SCOPED TenantContextService.
|
|
1479
|
-
*
|
|
1480
|
-
* Useful for:
|
|
1481
|
-
* - Logging tenant-specific information
|
|
1482
|
-
* - Implementing tenant-specific business logic
|
|
1483
|
-
* - Auditing and tracking
|
|
1484
|
-
* - Conditional feature flags
|
|
1485
|
-
*
|
|
1486
|
-
* @returns TenantInfo object with tenant metadata
|
|
1487
|
-
*
|
|
1488
|
-
* @example
|
|
1489
|
-
* // Access tenant metadata
|
|
1490
|
-
* @Get('info')
|
|
1491
|
-
* async getTenantInfo(@Tenant() tenant: TenantInfo) {
|
|
1492
|
-
* return {
|
|
1493
|
-
* id: tenant.id,
|
|
1494
|
-
* subdomain: tenant.subdomain,
|
|
1495
|
-
* type: tenant.type,
|
|
1496
|
-
* };
|
|
1497
|
-
* }
|
|
1498
|
-
*
|
|
1499
|
-
* @example
|
|
1500
|
-
* // Use for logging
|
|
1501
|
-
* @Post()
|
|
1502
|
-
* async createUser(
|
|
1503
|
-
* @Body() dto: CreateUserDto,
|
|
1504
|
-
* @Tenant() tenant: TenantInfo,
|
|
1505
|
-
* ) {
|
|
1506
|
-
* this.logger.log(`Creating user for tenant: ${tenant.subdomain}`);
|
|
1507
|
-
* // ...
|
|
1508
|
-
* }
|
|
1509
|
-
*
|
|
1510
|
-
* @example
|
|
1511
|
-
* // Conditional business logic
|
|
1512
|
-
* @Get('features')
|
|
1513
|
-
* async getFeatures(@Tenant() tenant: TenantInfo) {
|
|
1514
|
-
* if (tenant.type === 'ENTERPRISE') {
|
|
1515
|
-
* return ['feature-a', 'feature-b', 'feature-c'];
|
|
1516
|
-
* }
|
|
1517
|
-
* return ['feature-a'];
|
|
1518
|
-
* }
|
|
1519
|
-
*/
|
|
1520
|
-
declare const Tenant: (...dataOrPipes: unknown[]) => ParameterDecorator;
|
|
1521
|
-
|
|
1522
|
-
/**
|
|
1523
|
-
* Onboarding Decorator - Marks endpoints that require onboarding token
|
|
1524
|
-
*
|
|
1525
|
-
* Use this decorator on controllers or route handlers that should only be
|
|
1526
|
-
* accessible during the onboarding flow with JWT tokens containing type='onboarding'.
|
|
1527
|
-
*
|
|
1528
|
-
* These endpoints:
|
|
1529
|
-
* - Accept ONLY tokens with type='onboarding'
|
|
1530
|
-
* - Reject regular access tokens (type='access')
|
|
1531
|
-
* - Skip tenant validation and refresh token checks
|
|
1532
|
-
* - Only validate JWT signature and expiry
|
|
1533
|
-
*
|
|
1534
|
-
* Useful for:
|
|
1535
|
-
* - Email/phone verification during onboarding
|
|
1536
|
-
* - Onboarding status checks
|
|
1537
|
-
* - Resending OTPs during registration
|
|
1538
|
-
*
|
|
1539
|
-
* @example
|
|
1540
|
-
* // On a controller method
|
|
1541
|
-
* @Post('verify-email')
|
|
1542
|
-
* @Onboarding()
|
|
1543
|
-
* async verifyEmail(@Request() req, @Body() dto: VerifyEmailDto) {
|
|
1544
|
-
* const userId = req.user.id; // Available from VrittiAuthGuard
|
|
1545
|
-
* return this.service.verifyEmail(userId, dto.otp);
|
|
1546
|
-
* }
|
|
1547
|
-
*
|
|
1548
|
-
* @example
|
|
1549
|
-
* // Multiple onboarding endpoints
|
|
1550
|
-
* @Controller('onboarding')
|
|
1551
|
-
* export class OnboardingController {
|
|
1552
|
-
* @Post('verify-email')
|
|
1553
|
-
* @Onboarding()
|
|
1554
|
-
* async verifyEmail() { ... }
|
|
1555
|
-
*
|
|
1556
|
-
* @Post('resend-otp')
|
|
1557
|
-
* @Onboarding()
|
|
1558
|
-
* async resendOtp() { ... }
|
|
1559
|
-
* }
|
|
1560
|
-
*/
|
|
1561
|
-
declare const Onboarding: () => _nestjs_common.CustomDecorator<string>;
|
|
1562
|
-
|
|
1563
|
-
/**
|
|
1564
|
-
* Public Decorator - Marks endpoints that don't require authentication
|
|
1565
|
-
*
|
|
1566
|
-
* Use this decorator on controllers or route handlers to bypass VrittiAuthGuard
|
|
1567
|
-
* tenant validation. Useful for:
|
|
1568
|
-
* - Login/signup endpoints
|
|
1569
|
-
* - Health checks
|
|
1570
|
-
* - Public documentation endpoints
|
|
1571
|
-
* - Webhook endpoints that don't require tenant context
|
|
1572
|
-
*
|
|
1573
|
-
* @example
|
|
1574
|
-
* // On a controller method
|
|
1575
|
-
* @Public()
|
|
1576
|
-
* @Post('auth/login')
|
|
1577
|
-
* async login(@Body() dto: LoginDto) {
|
|
1578
|
-
* return this.authService.login(dto);
|
|
1579
|
-
* }
|
|
1580
|
-
*
|
|
1581
|
-
* @example
|
|
1582
|
-
* // On an entire controller
|
|
1583
|
-
* @Public()
|
|
1584
|
-
* @Controller('health')
|
|
1585
|
-
* export class HealthController {
|
|
1586
|
-
* @Get()
|
|
1587
|
-
* check() {
|
|
1588
|
-
* return { status: 'ok' };
|
|
1589
|
-
* }
|
|
1590
|
-
* }
|
|
1591
|
-
*/
|
|
1592
|
-
declare const Public: () => _nestjs_common.CustomDecorator<string>;
|
|
1593
|
-
|
|
1594
|
-
/**
|
|
1595
|
-
* HTTP Module
|
|
1596
|
-
*
|
|
1597
|
-
* Provides HTTP utilities including:
|
|
1598
|
-
* - CSRF Guard for request protection
|
|
1599
|
-
* - HTTP Exception Filter for standardized error responses
|
|
1600
|
-
*
|
|
1601
|
-
* Usage:
|
|
1602
|
-
* Import this module to access HTTP guards and filters.
|
|
1603
|
-
* Guards and filters are registered globally in the main application.
|
|
1604
|
-
*/
|
|
1605
|
-
declare class HttpModule {
|
|
1606
|
-
}
|
|
1607
|
-
|
|
1608
|
-
/**
|
|
1609
|
-
* CSRF Guard
|
|
1610
|
-
*
|
|
1611
|
-
* Global guard that automatically protects all state-changing requests (POST, PUT, PATCH, DELETE)
|
|
1612
|
-
* from CSRF attacks using Fastify's csrf-protection plugin.
|
|
1613
|
-
*
|
|
1614
|
-
* Flow:
|
|
1615
|
-
* 1. Skip safe methods (GET, HEAD, OPTIONS)
|
|
1616
|
-
* 2. Skip endpoints marked with @Public()
|
|
1617
|
-
* 3. Validate CSRF token for all other requests
|
|
1618
|
-
*
|
|
1619
|
-
* Token Sources (in priority order by @fastify/csrf-protection):
|
|
1620
|
-
* 1. req.headers['csrf-token']
|
|
1621
|
-
* 2. req.headers['xsrf-token']
|
|
1622
|
-
* 3. req.headers['x-csrf-token']
|
|
1623
|
-
* 4. req.headers['x-xsrf-token']
|
|
1624
|
-
* 5. req.body._csrf
|
|
1625
|
-
*
|
|
1626
|
-
* This guard should be registered globally in main.ts after CSRF plugin registration.
|
|
1627
|
-
*/
|
|
1628
|
-
declare class CsrfGuard implements CanActivate {
|
|
1629
|
-
private reflector;
|
|
1630
|
-
private readonly logger;
|
|
1631
|
-
constructor(reflector: Reflector);
|
|
1632
|
-
canActivate(context: ExecutionContext): Promise<boolean>;
|
|
1633
|
-
}
|
|
1634
|
-
|
|
1635
|
-
/**
|
|
1636
|
-
* Converts an HTTP status code to its corresponding title string.
|
|
1637
|
-
* Uses the HttpStatus enum to map status codes to human-readable titles.
|
|
1638
|
-
*
|
|
1639
|
-
* @param status - The HTTP status code
|
|
1640
|
-
* @returns The human-readable title for the status code
|
|
1641
|
-
*
|
|
1642
|
-
* @example
|
|
1643
|
-
* getHttpStatusTitle(400) // Returns: "Bad Request"
|
|
1644
|
-
* getHttpStatusTitle(404) // Returns: "Not Found"
|
|
1645
|
-
* getHttpStatusTitle(500) // Returns: "Internal Server Error"
|
|
1646
|
-
*/
|
|
1647
|
-
declare function getHttpStatusTitle(status: number): string;
|
|
1648
|
-
/**
|
|
1649
|
-
* Global HTTP Exception Filter implementing RFC 7807 Problem Details
|
|
1650
|
-
*
|
|
1651
|
-
* Transforms all exceptions into a standardized RFC 7807 format:
|
|
1652
|
-
* {
|
|
1653
|
-
* title: string, // Human-readable status title
|
|
1654
|
-
* status: number, // HTTP status code
|
|
1655
|
-
* detail: string, // Detailed error description
|
|
1656
|
-
* errors: FieldError[] // Field-specific error messages
|
|
1657
|
-
* }
|
|
1658
|
-
*
|
|
1659
|
-
* Handles:
|
|
1660
|
-
* - Custom field exceptions from @vritti/api-sdk (BaseFieldException)
|
|
1661
|
-
* - Class-validator DTO validation errors
|
|
1662
|
-
* - Standard NestJS HTTP exceptions
|
|
1663
|
-
* - Unknown errors
|
|
1664
|
-
*/
|
|
1665
|
-
declare class HttpExceptionFilter implements ExceptionFilter {
|
|
1666
|
-
private readonly logger;
|
|
1667
|
-
catch(exception: unknown, host: ArgumentsHost): void;
|
|
1567
|
+
*
|
|
1568
|
+
* // Count active products
|
|
1569
|
+
* const activeCount = await productRepository.count(
|
|
1570
|
+
* eq(products.status, 'ACTIVE')
|
|
1571
|
+
* );
|
|
1572
|
+
* ```
|
|
1573
|
+
*/
|
|
1574
|
+
count(where?: SQL): Promise<number>;
|
|
1575
|
+
/**
|
|
1576
|
+
* Check if a record exists
|
|
1577
|
+
*
|
|
1578
|
+
* @param where - SQL condition to match records
|
|
1579
|
+
* @returns Promise resolving to true if at least one record exists, false otherwise
|
|
1580
|
+
*
|
|
1581
|
+
* @example
|
|
1582
|
+
* ```typescript
|
|
1583
|
+
* import { eq } from 'drizzle-orm';
|
|
1584
|
+
*
|
|
1585
|
+
* const skuExists = await productRepository.exists(
|
|
1586
|
+
* eq(products.sku, 'WDG-001')
|
|
1587
|
+
* );
|
|
1588
|
+
* ```
|
|
1589
|
+
*/
|
|
1590
|
+
exists(where: SQL): Promise<boolean>;
|
|
1668
1591
|
}
|
|
1669
1592
|
|
|
1670
1593
|
interface FieldError {
|
|
@@ -1684,6 +1607,29 @@ declare abstract class BaseFieldException extends HttpException {
|
|
|
1684
1607
|
constructor(statusOrMessageOrErrors: HttpStatus | string | FieldError[], messageOrStatus?: string | HttpStatus, statusOrDetail?: HttpStatus | string, detail?: string);
|
|
1685
1608
|
}
|
|
1686
1609
|
|
|
1610
|
+
/**
|
|
1611
|
+
* Exception thrown when a gateway or proxy receives an invalid response (HTTP 502).
|
|
1612
|
+
* Used when a server acting as a gateway gets an error from an upstream server.
|
|
1613
|
+
*
|
|
1614
|
+
* @example
|
|
1615
|
+
* // Simple message
|
|
1616
|
+
* throw new BadGatewayException('Bad gateway');
|
|
1617
|
+
*
|
|
1618
|
+
* // Field-specific error
|
|
1619
|
+
* throw new BadGatewayException('upstream', 'Upstream service returned invalid response');
|
|
1620
|
+
*
|
|
1621
|
+
* // With detail
|
|
1622
|
+
* throw new BadGatewayException('proxy', 'Gateway error', 'Payment service is not responding correctly');
|
|
1623
|
+
*
|
|
1624
|
+
* // Multiple field errors
|
|
1625
|
+
* throw new BadGatewayException([
|
|
1626
|
+
* { field: 'gateway', message: 'Invalid response from upstream server' }
|
|
1627
|
+
* ]);
|
|
1628
|
+
*/
|
|
1629
|
+
declare class BadGatewayException extends BaseFieldException {
|
|
1630
|
+
constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
|
|
1631
|
+
}
|
|
1632
|
+
|
|
1687
1633
|
/**
|
|
1688
1634
|
* Exception thrown when a request is malformed or contains invalid data (HTTP 400).
|
|
1689
1635
|
*
|
|
@@ -1708,24 +1654,25 @@ declare class BadRequestException extends BaseFieldException {
|
|
|
1708
1654
|
}
|
|
1709
1655
|
|
|
1710
1656
|
/**
|
|
1711
|
-
* Exception thrown when
|
|
1657
|
+
* Exception thrown when a request conflicts with the current state (HTTP 409).
|
|
1658
|
+
* Commonly used for duplicate resources or concurrent modification issues.
|
|
1712
1659
|
*
|
|
1713
1660
|
* @example
|
|
1714
1661
|
* // Simple message
|
|
1715
|
-
* throw new
|
|
1662
|
+
* throw new ConflictException('Resource already exists');
|
|
1716
1663
|
*
|
|
1717
1664
|
* // Field-specific error
|
|
1718
|
-
* throw new
|
|
1665
|
+
* throw new ConflictException('email', 'Email already registered');
|
|
1719
1666
|
*
|
|
1720
1667
|
* // With detail
|
|
1721
|
-
* throw new
|
|
1668
|
+
* throw new ConflictException('email', 'Email already exists', 'Try logging in instead');
|
|
1722
1669
|
*
|
|
1723
1670
|
* // Multiple field errors
|
|
1724
|
-
* throw new
|
|
1725
|
-
* { field: '
|
|
1671
|
+
* throw new ConflictException([
|
|
1672
|
+
* { field: 'email', message: 'Email already in use' }
|
|
1726
1673
|
* ]);
|
|
1727
1674
|
*/
|
|
1728
|
-
declare class
|
|
1675
|
+
declare class ConflictException extends BaseFieldException {
|
|
1729
1676
|
constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
|
|
1730
1677
|
}
|
|
1731
1678
|
|
|
@@ -1752,275 +1699,276 @@ declare class ForbiddenException extends BaseFieldException {
|
|
|
1752
1699
|
}
|
|
1753
1700
|
|
|
1754
1701
|
/**
|
|
1755
|
-
* Exception thrown when a
|
|
1702
|
+
* Exception thrown when a resource has been permanently removed (HTTP 410).
|
|
1703
|
+
* Unlike 404, this indicates the resource existed but is intentionally gone.
|
|
1756
1704
|
*
|
|
1757
1705
|
* @example
|
|
1758
1706
|
* // Simple message
|
|
1759
|
-
* throw new
|
|
1707
|
+
* throw new GoneException('Resource permanently deleted');
|
|
1760
1708
|
*
|
|
1761
1709
|
* // Field-specific error
|
|
1762
|
-
* throw new
|
|
1710
|
+
* throw new GoneException('account', 'Account has been permanently deleted');
|
|
1763
1711
|
*
|
|
1764
1712
|
* // With detail
|
|
1765
|
-
* throw new
|
|
1713
|
+
* throw new GoneException('account', 'Deleted', 'This account was removed on user request');
|
|
1766
1714
|
*
|
|
1767
1715
|
* // Multiple field errors
|
|
1768
|
-
* throw new
|
|
1769
|
-
* { field: '
|
|
1716
|
+
* throw new GoneException([
|
|
1717
|
+
* { field: 'resource', message: 'This content has been permanently removed' }
|
|
1770
1718
|
* ]);
|
|
1771
1719
|
*/
|
|
1772
|
-
declare class
|
|
1720
|
+
declare class GoneException extends BaseFieldException {
|
|
1773
1721
|
constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
|
|
1774
1722
|
}
|
|
1775
1723
|
|
|
1776
1724
|
/**
|
|
1777
|
-
* Exception thrown when
|
|
1778
|
-
* Commonly used for duplicate resources or concurrent modification issues.
|
|
1725
|
+
* Exception thrown when an unexpected server error occurs (HTTP 500).
|
|
1779
1726
|
*
|
|
1780
1727
|
* @example
|
|
1781
1728
|
* // Simple message
|
|
1782
|
-
* throw new
|
|
1729
|
+
* throw new InternalServerErrorException('An unexpected error occurred');
|
|
1783
1730
|
*
|
|
1784
1731
|
* // Field-specific error
|
|
1785
|
-
* throw new
|
|
1732
|
+
* throw new InternalServerErrorException('database', 'Database connection failed');
|
|
1786
1733
|
*
|
|
1787
1734
|
* // With detail
|
|
1788
|
-
* throw new
|
|
1735
|
+
* throw new InternalServerErrorException('database', 'Connection failed', 'Please try again later');
|
|
1789
1736
|
*
|
|
1790
1737
|
* // Multiple field errors
|
|
1791
|
-
* throw new
|
|
1792
|
-
* { field: '
|
|
1738
|
+
* throw new InternalServerErrorException([
|
|
1739
|
+
* { field: 'system', message: 'Internal error' }
|
|
1793
1740
|
* ]);
|
|
1794
1741
|
*/
|
|
1795
|
-
declare class
|
|
1742
|
+
declare class InternalServerErrorException extends BaseFieldException {
|
|
1796
1743
|
constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
|
|
1797
1744
|
}
|
|
1798
1745
|
|
|
1799
1746
|
/**
|
|
1800
|
-
* Exception thrown when an
|
|
1747
|
+
* Exception thrown when an HTTP method is not supported for the endpoint (HTTP 405).
|
|
1748
|
+
* For example, when a POST is sent to a GET-only endpoint.
|
|
1801
1749
|
*
|
|
1802
1750
|
* @example
|
|
1803
1751
|
* // Simple message
|
|
1804
|
-
* throw new
|
|
1752
|
+
* throw new MethodNotAllowedException('Method not allowed');
|
|
1805
1753
|
*
|
|
1806
1754
|
* // Field-specific error
|
|
1807
|
-
* throw new
|
|
1755
|
+
* throw new MethodNotAllowedException('method', 'POST method not allowed on this endpoint');
|
|
1808
1756
|
*
|
|
1809
1757
|
* // With detail
|
|
1810
|
-
* throw new
|
|
1758
|
+
* throw new MethodNotAllowedException('method', 'Not allowed', 'Only GET and PUT are supported');
|
|
1811
1759
|
*
|
|
1812
1760
|
* // Multiple field errors
|
|
1813
|
-
* throw new
|
|
1814
|
-
* { field: '
|
|
1761
|
+
* throw new MethodNotAllowedException([
|
|
1762
|
+
* { field: 'method', message: 'DELETE is not allowed on this resource' }
|
|
1815
1763
|
* ]);
|
|
1816
1764
|
*/
|
|
1817
|
-
declare class
|
|
1765
|
+
declare class MethodNotAllowedException extends BaseFieldException {
|
|
1818
1766
|
constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
|
|
1819
1767
|
}
|
|
1820
1768
|
|
|
1821
1769
|
/**
|
|
1822
|
-
* Exception thrown when
|
|
1823
|
-
*
|
|
1770
|
+
* Exception thrown when content negotiation fails (HTTP 406).
|
|
1771
|
+
* Used when the server cannot produce a response matching the Accept headers.
|
|
1824
1772
|
*
|
|
1825
1773
|
* @example
|
|
1826
|
-
* //
|
|
1827
|
-
* throw new
|
|
1828
|
-
*
|
|
1829
|
-
*
|
|
1830
|
-
*
|
|
1774
|
+
* // Simple message
|
|
1775
|
+
* throw new NotAcceptableException('Requested format not available');
|
|
1776
|
+
*
|
|
1777
|
+
* // Field-specific error
|
|
1778
|
+
* throw new NotAcceptableException('accept', 'Cannot produce response in requested format');
|
|
1831
1779
|
*
|
|
1832
1780
|
* // With detail
|
|
1833
|
-
* throw new
|
|
1834
|
-
*
|
|
1835
|
-
*
|
|
1836
|
-
*
|
|
1781
|
+
* throw new NotAcceptableException('accept', 'Format not supported', 'Only JSON is available');
|
|
1782
|
+
*
|
|
1783
|
+
* // Multiple field errors
|
|
1784
|
+
* throw new NotAcceptableException([
|
|
1785
|
+
* { field: 'contentType', message: 'XML format is not supported' }
|
|
1786
|
+
* ]);
|
|
1837
1787
|
*/
|
|
1838
|
-
declare class
|
|
1839
|
-
constructor(
|
|
1788
|
+
declare class NotAcceptableException extends BaseFieldException {
|
|
1789
|
+
constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
|
|
1840
1790
|
}
|
|
1841
1791
|
|
|
1842
1792
|
/**
|
|
1843
|
-
* Exception thrown when
|
|
1844
|
-
* Used for business logic validation failures that prevent processing.
|
|
1793
|
+
* Exception thrown when a requested resource cannot be found (HTTP 404).
|
|
1845
1794
|
*
|
|
1846
1795
|
* @example
|
|
1847
1796
|
* // Simple message
|
|
1848
|
-
* throw new
|
|
1797
|
+
* throw new NotFoundException('Resource not found');
|
|
1849
1798
|
*
|
|
1850
1799
|
* // Field-specific error
|
|
1851
|
-
* throw new
|
|
1800
|
+
* throw new NotFoundException('userId', 'User not found');
|
|
1852
1801
|
*
|
|
1853
1802
|
* // With detail
|
|
1854
|
-
* throw new
|
|
1803
|
+
* throw new NotFoundException('userId', 'User not found', 'No user exists with the provided ID');
|
|
1855
1804
|
*
|
|
1856
1805
|
* // Multiple field errors
|
|
1857
|
-
* throw new
|
|
1858
|
-
* { field: '
|
|
1859
|
-
* { field: 'endDate', message: 'End date cannot be in the past' }
|
|
1806
|
+
* throw new NotFoundException([
|
|
1807
|
+
* { field: 'userId', message: 'User does not exist' }
|
|
1860
1808
|
* ]);
|
|
1861
1809
|
*/
|
|
1862
|
-
declare class
|
|
1810
|
+
declare class NotFoundException extends BaseFieldException {
|
|
1863
1811
|
constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
|
|
1864
1812
|
}
|
|
1865
1813
|
|
|
1866
1814
|
/**
|
|
1867
|
-
* Exception thrown when
|
|
1868
|
-
* Used
|
|
1815
|
+
* Exception thrown when a feature or endpoint is not yet implemented (HTTP 501).
|
|
1816
|
+
* Used for planned but unavailable functionality.
|
|
1869
1817
|
*
|
|
1870
1818
|
* @example
|
|
1871
1819
|
* // Simple message
|
|
1872
|
-
* throw new
|
|
1820
|
+
* throw new NotImplementedException('Feature not yet implemented');
|
|
1873
1821
|
*
|
|
1874
1822
|
* // Field-specific error
|
|
1875
|
-
* throw new
|
|
1823
|
+
* throw new NotImplementedException('feature', 'This feature is coming soon');
|
|
1876
1824
|
*
|
|
1877
1825
|
* // With detail
|
|
1878
|
-
* throw new
|
|
1826
|
+
* throw new NotImplementedException('export', 'Not implemented', 'PDF export will be available in v2.0');
|
|
1879
1827
|
*
|
|
1880
1828
|
* // Multiple field errors
|
|
1881
|
-
* throw new
|
|
1882
|
-
* { field: '
|
|
1829
|
+
* throw new NotImplementedException([
|
|
1830
|
+
* { field: 'functionality', message: 'This functionality is not available yet' }
|
|
1883
1831
|
* ]);
|
|
1884
1832
|
*/
|
|
1885
|
-
declare class
|
|
1833
|
+
declare class NotImplementedException extends BaseFieldException {
|
|
1886
1834
|
constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
|
|
1887
1835
|
}
|
|
1888
1836
|
|
|
1889
1837
|
/**
|
|
1890
|
-
* Exception thrown when
|
|
1891
|
-
*
|
|
1838
|
+
* Exception thrown when request payload exceeds size limits (HTTP 413).
|
|
1839
|
+
* Commonly used for file upload size restrictions or large request bodies.
|
|
1892
1840
|
*
|
|
1893
1841
|
* @example
|
|
1894
1842
|
* // Simple message
|
|
1895
|
-
* throw new
|
|
1843
|
+
* throw new PayloadTooLargeException('Request payload too large');
|
|
1896
1844
|
*
|
|
1897
1845
|
* // Field-specific error
|
|
1898
|
-
* throw new
|
|
1846
|
+
* throw new PayloadTooLargeException('file', 'File size exceeds maximum allowed');
|
|
1899
1847
|
*
|
|
1900
1848
|
* // With detail
|
|
1901
|
-
* throw new
|
|
1849
|
+
* throw new PayloadTooLargeException('file', 'File too large', 'Maximum size is 10MB');
|
|
1902
1850
|
*
|
|
1903
1851
|
* // Multiple field errors
|
|
1904
|
-
* throw new
|
|
1905
|
-
* { field: '
|
|
1852
|
+
* throw new PayloadTooLargeException([
|
|
1853
|
+
* { field: 'upload', message: 'File exceeds 10MB limit' }
|
|
1906
1854
|
* ]);
|
|
1907
1855
|
*/
|
|
1908
|
-
declare class
|
|
1856
|
+
declare class PayloadTooLargeException extends BaseFieldException {
|
|
1909
1857
|
constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
|
|
1910
1858
|
}
|
|
1911
1859
|
|
|
1912
1860
|
/**
|
|
1913
|
-
* Exception thrown when
|
|
1914
|
-
*
|
|
1861
|
+
* Exception thrown when a request takes too long to process (HTTP 408).
|
|
1862
|
+
* Used when the client or server times out while waiting for completion.
|
|
1915
1863
|
*
|
|
1916
1864
|
* @example
|
|
1917
1865
|
* // Simple message
|
|
1918
|
-
* throw new
|
|
1866
|
+
* throw new RequestTimeoutException('Request timeout');
|
|
1919
1867
|
*
|
|
1920
1868
|
* // Field-specific error
|
|
1921
|
-
* throw new
|
|
1869
|
+
* throw new RequestTimeoutException('operation', 'Operation timed out');
|
|
1922
1870
|
*
|
|
1923
1871
|
* // With detail
|
|
1924
|
-
* throw new
|
|
1872
|
+
* throw new RequestTimeoutException('query', 'Database query timeout', 'Try with fewer filters');
|
|
1925
1873
|
*
|
|
1926
1874
|
* // Multiple field errors
|
|
1927
|
-
* throw new
|
|
1928
|
-
* { field: '
|
|
1875
|
+
* throw new RequestTimeoutException([
|
|
1876
|
+
* { field: 'processing', message: 'Request took too long to complete' }
|
|
1929
1877
|
* ]);
|
|
1930
1878
|
*/
|
|
1931
|
-
declare class
|
|
1879
|
+
declare class RequestTimeoutException extends BaseFieldException {
|
|
1932
1880
|
constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
|
|
1933
1881
|
}
|
|
1934
1882
|
|
|
1935
1883
|
/**
|
|
1936
|
-
* Exception thrown when
|
|
1937
|
-
*
|
|
1884
|
+
* Exception thrown when the service is temporarily unavailable (HTTP 503).
|
|
1885
|
+
* Used during maintenance, overload, or temporary outages.
|
|
1938
1886
|
*
|
|
1939
1887
|
* @example
|
|
1940
1888
|
* // Simple message
|
|
1941
|
-
* throw new
|
|
1889
|
+
* throw new ServiceUnavailableException('Service temporarily unavailable');
|
|
1942
1890
|
*
|
|
1943
1891
|
* // Field-specific error
|
|
1944
|
-
* throw new
|
|
1892
|
+
* throw new ServiceUnavailableException('service', 'Scheduled maintenance in progress');
|
|
1945
1893
|
*
|
|
1946
1894
|
* // With detail
|
|
1947
|
-
* throw new
|
|
1895
|
+
* throw new ServiceUnavailableException('service', 'Maintenance', 'Service will be back at 2 PM EST');
|
|
1948
1896
|
*
|
|
1949
1897
|
* // Multiple field errors
|
|
1950
|
-
* throw new
|
|
1951
|
-
* { field: '
|
|
1898
|
+
* throw new ServiceUnavailableException([
|
|
1899
|
+
* { field: 'database', message: 'Database is temporarily unavailable' }
|
|
1952
1900
|
* ]);
|
|
1953
1901
|
*/
|
|
1954
|
-
declare class
|
|
1902
|
+
declare class ServiceUnavailableException extends BaseFieldException {
|
|
1955
1903
|
constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
|
|
1956
1904
|
}
|
|
1957
1905
|
|
|
1958
1906
|
/**
|
|
1959
|
-
* Exception thrown when
|
|
1960
|
-
* Used
|
|
1907
|
+
* Exception thrown when rate limiting is triggered (HTTP 429).
|
|
1908
|
+
* Used to prevent abuse and ensure fair resource usage.
|
|
1961
1909
|
*
|
|
1962
1910
|
* @example
|
|
1963
1911
|
* // Simple message
|
|
1964
|
-
* throw new
|
|
1912
|
+
* throw new TooManyRequestsException('Too many requests');
|
|
1965
1913
|
*
|
|
1966
1914
|
* // Field-specific error
|
|
1967
|
-
* throw new
|
|
1915
|
+
* throw new TooManyRequestsException('api', 'Rate limit exceeded');
|
|
1968
1916
|
*
|
|
1969
1917
|
* // With detail
|
|
1970
|
-
* throw new
|
|
1918
|
+
* throw new TooManyRequestsException('api', 'Rate limit exceeded', 'Try again in 60 seconds');
|
|
1971
1919
|
*
|
|
1972
1920
|
* // Multiple field errors
|
|
1973
|
-
* throw new
|
|
1974
|
-
* { field: '
|
|
1921
|
+
* throw new TooManyRequestsException([
|
|
1922
|
+
* { field: 'requests', message: 'Rate limit exceeded for this endpoint' }
|
|
1975
1923
|
* ]);
|
|
1976
1924
|
*/
|
|
1977
|
-
declare class
|
|
1925
|
+
declare class TooManyRequestsException extends BaseFieldException {
|
|
1978
1926
|
constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
|
|
1979
1927
|
}
|
|
1980
1928
|
|
|
1981
1929
|
/**
|
|
1982
|
-
* Exception thrown when
|
|
1983
|
-
* Used when the client or server times out while waiting for completion.
|
|
1930
|
+
* Exception thrown when authentication is required or has failed (HTTP 401).
|
|
1984
1931
|
*
|
|
1985
1932
|
* @example
|
|
1986
1933
|
* // Simple message
|
|
1987
|
-
* throw new
|
|
1934
|
+
* throw new UnauthorizedException('Authentication required');
|
|
1988
1935
|
*
|
|
1989
1936
|
* // Field-specific error
|
|
1990
|
-
* throw new
|
|
1937
|
+
* throw new UnauthorizedException('token', 'Invalid or expired token');
|
|
1991
1938
|
*
|
|
1992
1939
|
* // With detail
|
|
1993
|
-
* throw new
|
|
1940
|
+
* throw new UnauthorizedException('token', 'Invalid token', 'Please login again');
|
|
1994
1941
|
*
|
|
1995
1942
|
* // Multiple field errors
|
|
1996
|
-
* throw new
|
|
1997
|
-
* { field: '
|
|
1943
|
+
* throw new UnauthorizedException([
|
|
1944
|
+
* { field: 'token', message: 'Token expired' }
|
|
1998
1945
|
* ]);
|
|
1999
1946
|
*/
|
|
2000
|
-
declare class
|
|
1947
|
+
declare class UnauthorizedException extends BaseFieldException {
|
|
2001
1948
|
constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
|
|
2002
1949
|
}
|
|
2003
1950
|
|
|
2004
1951
|
/**
|
|
2005
|
-
* Exception thrown when request
|
|
2006
|
-
*
|
|
1952
|
+
* Exception thrown when the request is well-formed but contains semantic errors (HTTP 422).
|
|
1953
|
+
* Used for business logic validation failures that prevent processing.
|
|
2007
1954
|
*
|
|
2008
1955
|
* @example
|
|
2009
1956
|
* // Simple message
|
|
2010
|
-
* throw new
|
|
1957
|
+
* throw new UnprocessableEntityException('Cannot process the request');
|
|
2011
1958
|
*
|
|
2012
1959
|
* // Field-specific error
|
|
2013
|
-
* throw new
|
|
1960
|
+
* throw new UnprocessableEntityException('age', 'Age must be 18 or older');
|
|
2014
1961
|
*
|
|
2015
1962
|
* // With detail
|
|
2016
|
-
* throw new
|
|
1963
|
+
* throw new UnprocessableEntityException('quantity', 'Insufficient stock', 'Only 5 items available');
|
|
2017
1964
|
*
|
|
2018
1965
|
* // Multiple field errors
|
|
2019
|
-
* throw new
|
|
2020
|
-
* { field: '
|
|
1966
|
+
* throw new UnprocessableEntityException([
|
|
1967
|
+
* { field: 'startDate', message: 'Start date must be before end date' },
|
|
1968
|
+
* { field: 'endDate', message: 'End date cannot be in the past' }
|
|
2021
1969
|
* ]);
|
|
2022
1970
|
*/
|
|
2023
|
-
declare class
|
|
1971
|
+
declare class UnprocessableEntityException extends BaseFieldException {
|
|
2024
1972
|
constructor(messageOrField: string | FieldError[], fieldMessageOrDetail?: string, detail?: string);
|
|
2025
1973
|
}
|
|
2026
1974
|
|
|
@@ -2048,49 +1996,98 @@ declare class UnsupportedMediaTypeException extends BaseFieldException {
|
|
|
2048
1996
|
}
|
|
2049
1997
|
|
|
2050
1998
|
/**
|
|
2051
|
-
* Exception thrown when
|
|
2052
|
-
*
|
|
1999
|
+
* Exception thrown when request validation fails (HTTP 400).
|
|
2000
|
+
* Typically used for form validation or DTO validation errors.
|
|
2053
2001
|
*
|
|
2054
2002
|
* @example
|
|
2055
|
-
* //
|
|
2056
|
-
* throw new
|
|
2057
|
-
*
|
|
2058
|
-
*
|
|
2059
|
-
*
|
|
2003
|
+
* // Multiple validation errors
|
|
2004
|
+
* throw new ValidationException([
|
|
2005
|
+
* { field: 'email', message: 'Invalid email format' },
|
|
2006
|
+
* { field: 'password', message: 'Password must be at least 8 characters' }
|
|
2007
|
+
* ]);
|
|
2060
2008
|
*
|
|
2061
2009
|
* // With detail
|
|
2062
|
-
* throw new
|
|
2063
|
-
*
|
|
2064
|
-
*
|
|
2065
|
-
*
|
|
2066
|
-
* { field: 'functionality', message: 'This functionality is not available yet' }
|
|
2067
|
-
* ]);
|
|
2010
|
+
* throw new ValidationException(
|
|
2011
|
+
* [{ field: 'email', message: 'Invalid format' }],
|
|
2012
|
+
* 'Please correct the errors and try again'
|
|
2013
|
+
* );
|
|
2068
2014
|
*/
|
|
2069
|
-
declare class
|
|
2070
|
-
constructor(
|
|
2015
|
+
declare class ValidationException extends BaseFieldException {
|
|
2016
|
+
constructor(errors: FieldError[], detail?: string);
|
|
2071
2017
|
}
|
|
2072
2018
|
|
|
2073
2019
|
/**
|
|
2074
|
-
*
|
|
2075
|
-
*
|
|
2020
|
+
* Converts an HTTP status code to its corresponding title string.
|
|
2021
|
+
* Uses the HttpStatus enum to map status codes to human-readable titles.
|
|
2022
|
+
*
|
|
2023
|
+
* @param status - The HTTP status code
|
|
2024
|
+
* @returns The human-readable title for the status code
|
|
2076
2025
|
*
|
|
2077
2026
|
* @example
|
|
2078
|
-
* //
|
|
2079
|
-
*
|
|
2027
|
+
* getHttpStatusTitle(400) // Returns: "Bad Request"
|
|
2028
|
+
* getHttpStatusTitle(404) // Returns: "Not Found"
|
|
2029
|
+
* getHttpStatusTitle(500) // Returns: "Internal Server Error"
|
|
2030
|
+
*/
|
|
2031
|
+
declare function getHttpStatusTitle(status: number): string;
|
|
2032
|
+
/**
|
|
2033
|
+
* Global HTTP Exception Filter implementing RFC 7807 Problem Details
|
|
2080
2034
|
*
|
|
2081
|
-
*
|
|
2082
|
-
*
|
|
2035
|
+
* Transforms all exceptions into a standardized RFC 7807 format:
|
|
2036
|
+
* {
|
|
2037
|
+
* title: string, // Human-readable status title
|
|
2038
|
+
* status: number, // HTTP status code
|
|
2039
|
+
* detail: string, // Detailed error description
|
|
2040
|
+
* errors: FieldError[] // Field-specific error messages
|
|
2041
|
+
* }
|
|
2083
2042
|
*
|
|
2084
|
-
*
|
|
2085
|
-
*
|
|
2043
|
+
* Handles:
|
|
2044
|
+
* - Custom field exceptions from @vritti/api-sdk (BaseFieldException)
|
|
2045
|
+
* - Class-validator DTO validation errors
|
|
2046
|
+
* - Standard NestJS HTTP exceptions
|
|
2047
|
+
* - Unknown errors
|
|
2048
|
+
*/
|
|
2049
|
+
declare class HttpExceptionFilter implements ExceptionFilter {
|
|
2050
|
+
private readonly logger;
|
|
2051
|
+
catch(exception: unknown, host: ArgumentsHost): void;
|
|
2052
|
+
}
|
|
2053
|
+
|
|
2054
|
+
/**
|
|
2055
|
+
* CSRF Guard
|
|
2086
2056
|
*
|
|
2087
|
-
*
|
|
2088
|
-
*
|
|
2089
|
-
*
|
|
2090
|
-
*
|
|
2057
|
+
* Global guard that automatically protects all state-changing requests (POST, PUT, PATCH, DELETE)
|
|
2058
|
+
* from CSRF attacks using Fastify's csrf-protection plugin.
|
|
2059
|
+
*
|
|
2060
|
+
* Flow:
|
|
2061
|
+
* 1. Skip safe methods (GET, HEAD, OPTIONS)
|
|
2062
|
+
* 2. Skip endpoints marked with @Public()
|
|
2063
|
+
* 3. Validate CSRF token for all other requests
|
|
2064
|
+
*
|
|
2065
|
+
* Token Sources (in priority order by @fastify/csrf-protection):
|
|
2066
|
+
* 1. req.headers['csrf-token']
|
|
2067
|
+
* 2. req.headers['xsrf-token']
|
|
2068
|
+
* 3. req.headers['x-csrf-token']
|
|
2069
|
+
* 4. req.headers['x-xsrf-token']
|
|
2070
|
+
* 5. req.body._csrf
|
|
2071
|
+
*
|
|
2072
|
+
* This guard should be registered globally in main.ts after CSRF plugin registration.
|
|
2091
2073
|
*/
|
|
2092
|
-
declare class
|
|
2093
|
-
|
|
2074
|
+
declare class CsrfGuard implements CanActivate {
|
|
2075
|
+
private readonly logger;
|
|
2076
|
+
canActivate(context: ExecutionContext): Promise<boolean>;
|
|
2077
|
+
}
|
|
2078
|
+
|
|
2079
|
+
/**
|
|
2080
|
+
* HTTP Module
|
|
2081
|
+
*
|
|
2082
|
+
* Provides HTTP utilities including:
|
|
2083
|
+
* - CSRF Guard for request protection
|
|
2084
|
+
* - HTTP Exception Filter for standardized error responses
|
|
2085
|
+
*
|
|
2086
|
+
* Usage:
|
|
2087
|
+
* Import this module to access HTTP guards and filters.
|
|
2088
|
+
* Guards and filters are registered globally in the main application.
|
|
2089
|
+
*/
|
|
2090
|
+
declare class HttpModule {
|
|
2094
2091
|
}
|
|
2095
2092
|
|
|
2096
2093
|
/**
|
|
@@ -2167,6 +2164,77 @@ interface HttpLoggerOptions {
|
|
|
2167
2164
|
maxBodySize?: number;
|
|
2168
2165
|
}
|
|
2169
2166
|
|
|
2167
|
+
/**
|
|
2168
|
+
* Unified Logger Service
|
|
2169
|
+
*
|
|
2170
|
+
* Single service that provides both default NestJS Logger and Winston logger implementations.
|
|
2171
|
+
* Automatically delegates to the configured provider (default or winston).
|
|
2172
|
+
* @module logger/logger.service
|
|
2173
|
+
*/
|
|
2174
|
+
|
|
2175
|
+
/**
|
|
2176
|
+
* Unified logger service implementing NestJS LoggerService interface.
|
|
2177
|
+
* Supports both default NestJS Logger and Winston implementations via facade pattern.
|
|
2178
|
+
*/
|
|
2179
|
+
declare class LoggerService implements LoggerService$1 {
|
|
2180
|
+
private readonly defaultLogger?;
|
|
2181
|
+
private readonly activeLogger;
|
|
2182
|
+
private readonly options;
|
|
2183
|
+
private context?;
|
|
2184
|
+
constructor(options?: LoggerModuleOptions, defaultLogger?: Logger | undefined);
|
|
2185
|
+
/**
|
|
2186
|
+
* Creates a Winston logger instance with inline configuration.
|
|
2187
|
+
* Consolidates winston-config.factory.ts logic.
|
|
2188
|
+
*/
|
|
2189
|
+
private createWinstonLogger;
|
|
2190
|
+
log(message: any, context?: string): void;
|
|
2191
|
+
error(message: any, trace?: string, context?: string): void;
|
|
2192
|
+
warn(message: any, context?: string): void;
|
|
2193
|
+
debug(message: any, context?: string): void;
|
|
2194
|
+
verbose(message: any, context?: string): void;
|
|
2195
|
+
setContext(context: string): void;
|
|
2196
|
+
/**
|
|
2197
|
+
* Unified internal logging method that handles both Winston and NestJS Logger.
|
|
2198
|
+
*/
|
|
2199
|
+
private _log;
|
|
2200
|
+
/**
|
|
2201
|
+
* Logs with custom metadata (Winston only).
|
|
2202
|
+
*/
|
|
2203
|
+
logWithMetadata(level: LogLevel, message: any, metadata?: LogMetadata, context?: string): void;
|
|
2204
|
+
private formatMessage;
|
|
2205
|
+
/**
|
|
2206
|
+
* Enriches metadata with correlation context from AsyncLocalStorage.
|
|
2207
|
+
* Inline from winston-logger.service.ts
|
|
2208
|
+
*/
|
|
2209
|
+
private enrichMetadata;
|
|
2210
|
+
child(context: string): LoggerService;
|
|
2211
|
+
}
|
|
2212
|
+
|
|
2213
|
+
/**
|
|
2214
|
+
* HTTP Logger Interceptor
|
|
2215
|
+
*
|
|
2216
|
+
* Automatically logs HTTP requests and responses with correlation tracking.
|
|
2217
|
+
* @module logger/http-logger.interceptor
|
|
2218
|
+
*/
|
|
2219
|
+
|
|
2220
|
+
/**
|
|
2221
|
+
* HTTP Logger Interceptor for NestJS applications.
|
|
2222
|
+
*
|
|
2223
|
+
* Logs all HTTP requests and responses with metadata including
|
|
2224
|
+
* correlation IDs, performance metrics, and error details.
|
|
2225
|
+
*/
|
|
2226
|
+
declare class HttpLoggerInterceptor implements NestInterceptor {
|
|
2227
|
+
private readonly logger;
|
|
2228
|
+
private readonly enableRequestLog;
|
|
2229
|
+
private readonly enableResponseLog;
|
|
2230
|
+
private readonly slowRequestThreshold;
|
|
2231
|
+
constructor(logger: LoggerService, options?: HttpLoggerOptions);
|
|
2232
|
+
intercept(context: ExecutionContext, next: CallHandler): Observable<any>;
|
|
2233
|
+
private logRequest;
|
|
2234
|
+
private logResponse;
|
|
2235
|
+
private logError;
|
|
2236
|
+
}
|
|
2237
|
+
|
|
2170
2238
|
/**
|
|
2171
2239
|
* Logger Module
|
|
2172
2240
|
*
|
|
@@ -2329,7 +2397,7 @@ declare class LoggerModule implements NestModule {
|
|
|
2329
2397
|
* Configures middleware for the module.
|
|
2330
2398
|
* Middleware is registered globally in main.ts using Fastify hooks.
|
|
2331
2399
|
*/
|
|
2332
|
-
configure(
|
|
2400
|
+
configure(_consumer: MiddlewareConsumer): void;
|
|
2333
2401
|
/**
|
|
2334
2402
|
* Creates async providers for dynamic module configuration.
|
|
2335
2403
|
*/
|
|
@@ -2340,52 +2408,6 @@ declare class LoggerModule implements NestModule {
|
|
|
2340
2408
|
private static createAsyncOptionsProvider;
|
|
2341
2409
|
}
|
|
2342
2410
|
|
|
2343
|
-
/**
|
|
2344
|
-
* Unified Logger Service
|
|
2345
|
-
*
|
|
2346
|
-
* Single service that provides both default NestJS Logger and Winston logger implementations.
|
|
2347
|
-
* Automatically delegates to the configured provider (default or winston).
|
|
2348
|
-
* @module logger/logger.service
|
|
2349
|
-
*/
|
|
2350
|
-
|
|
2351
|
-
/**
|
|
2352
|
-
* Unified logger service implementing NestJS LoggerService interface.
|
|
2353
|
-
* Supports both default NestJS Logger and Winston implementations via facade pattern.
|
|
2354
|
-
*/
|
|
2355
|
-
declare class LoggerService implements LoggerService$1 {
|
|
2356
|
-
private readonly defaultLogger?;
|
|
2357
|
-
private readonly activeLogger;
|
|
2358
|
-
private readonly options;
|
|
2359
|
-
private context?;
|
|
2360
|
-
constructor(options?: LoggerModuleOptions, defaultLogger?: Logger | undefined);
|
|
2361
|
-
/**
|
|
2362
|
-
* Creates a Winston logger instance with inline configuration.
|
|
2363
|
-
* Consolidates winston-config.factory.ts logic.
|
|
2364
|
-
*/
|
|
2365
|
-
private createWinstonLogger;
|
|
2366
|
-
log(message: any, context?: string): void;
|
|
2367
|
-
error(message: any, trace?: string, context?: string): void;
|
|
2368
|
-
warn(message: any, context?: string): void;
|
|
2369
|
-
debug(message: any, context?: string): void;
|
|
2370
|
-
verbose(message: any, context?: string): void;
|
|
2371
|
-
setContext(context: string): void;
|
|
2372
|
-
/**
|
|
2373
|
-
* Unified internal logging method that handles both Winston and NestJS Logger.
|
|
2374
|
-
*/
|
|
2375
|
-
private _log;
|
|
2376
|
-
/**
|
|
2377
|
-
* Logs with custom metadata (Winston only).
|
|
2378
|
-
*/
|
|
2379
|
-
logWithMetadata(level: LogLevel, message: any, metadata?: LogMetadata, context?: string): void;
|
|
2380
|
-
private formatMessage;
|
|
2381
|
-
/**
|
|
2382
|
-
* Enriches metadata with correlation context from AsyncLocalStorage.
|
|
2383
|
-
* Inline from winston-logger.service.ts
|
|
2384
|
-
*/
|
|
2385
|
-
private enrichMetadata;
|
|
2386
|
-
child(context: string): LoggerService;
|
|
2387
|
-
}
|
|
2388
|
-
|
|
2389
2411
|
/**
|
|
2390
2412
|
* Correlation ID Middleware
|
|
2391
2413
|
*
|
|
@@ -2423,38 +2445,13 @@ declare class CorrelationIdMiddleware implements NestMiddleware {
|
|
|
2423
2445
|
/**
|
|
2424
2446
|
* Middleware handler for processing requests.
|
|
2425
2447
|
*/
|
|
2426
|
-
use(
|
|
2448
|
+
use(_req: FastifyRequest, reply: FastifyReply, next: () => void): void;
|
|
2427
2449
|
/**
|
|
2428
2450
|
* Fastify hook handler for onRequest.
|
|
2429
2451
|
* This is an async function that returns a Promise, ensuring the AsyncLocalStorage
|
|
2430
2452
|
* context persists throughout the entire request lifecycle.
|
|
2431
2453
|
*/
|
|
2432
|
-
onRequest(
|
|
2433
|
-
}
|
|
2434
|
-
|
|
2435
|
-
/**
|
|
2436
|
-
* HTTP Logger Interceptor
|
|
2437
|
-
*
|
|
2438
|
-
* Automatically logs HTTP requests and responses with correlation tracking.
|
|
2439
|
-
* @module logger/http-logger.interceptor
|
|
2440
|
-
*/
|
|
2441
|
-
|
|
2442
|
-
/**
|
|
2443
|
-
* HTTP Logger Interceptor for NestJS applications.
|
|
2444
|
-
*
|
|
2445
|
-
* Logs all HTTP requests and responses with metadata including
|
|
2446
|
-
* correlation IDs, performance metrics, and error details.
|
|
2447
|
-
*/
|
|
2448
|
-
declare class HttpLoggerInterceptor implements NestInterceptor {
|
|
2449
|
-
private readonly logger;
|
|
2450
|
-
private readonly enableRequestLog;
|
|
2451
|
-
private readonly enableResponseLog;
|
|
2452
|
-
private readonly slowRequestThreshold;
|
|
2453
|
-
constructor(logger: LoggerService, options?: HttpLoggerOptions);
|
|
2454
|
-
intercept(context: ExecutionContext, next: CallHandler): Observable<any>;
|
|
2455
|
-
private logRequest;
|
|
2456
|
-
private logResponse;
|
|
2457
|
-
private logError;
|
|
2454
|
+
onRequest(_req: FastifyRequest, reply: FastifyReply): Promise<void>;
|
|
2458
2455
|
}
|
|
2459
2456
|
|
|
2460
2457
|
/**
|