@vritti/api-sdk 0.0.1 → 0.0.2
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 +824 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +533 -2
- package/dist/index.d.ts +533 -2
- package/dist/index.js +815 -5
- package/dist/index.js.map +1 -1
- package/package.json +17 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,534 @@
|
|
|
1
|
-
|
|
1
|
+
import { DynamicModule, OnModuleInit, OnModuleDestroy, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
|
|
2
|
+
import { Observable } from 'rxjs';
|
|
2
3
|
|
|
3
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Primary database connection configuration
|
|
6
|
+
*/
|
|
7
|
+
interface PrimaryDbConfig {
|
|
8
|
+
/** Database host */
|
|
9
|
+
host: string;
|
|
10
|
+
/** Database port (default: 5432) */
|
|
11
|
+
port?: number;
|
|
12
|
+
/** Database username */
|
|
13
|
+
username: string;
|
|
14
|
+
/** Database password */
|
|
15
|
+
password: string;
|
|
16
|
+
/** Database name */
|
|
17
|
+
database: string;
|
|
18
|
+
/** Default schema (default: 'public') */
|
|
19
|
+
schema?: string;
|
|
20
|
+
/** SSL mode: 'require' | 'prefer' | 'disable' (default: 'require') */
|
|
21
|
+
sslMode?: 'require' | 'prefer' | 'disable';
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Configuration options for DatabaseModule
|
|
25
|
+
*/
|
|
26
|
+
interface DatabaseModuleOptions {
|
|
27
|
+
/**
|
|
28
|
+
* Primary database configuration (for tenant registry queries)
|
|
29
|
+
* Only required in gateway mode
|
|
30
|
+
* @example
|
|
31
|
+
* primaryDb: {
|
|
32
|
+
* host: 'aws-pooler.supabase.com',
|
|
33
|
+
* port: 5432,
|
|
34
|
+
* username: 'postgres.xxx',
|
|
35
|
+
* password: 'xxx',
|
|
36
|
+
* database: 'postgres',
|
|
37
|
+
* schema: 'public',
|
|
38
|
+
* sslMode: 'require',
|
|
39
|
+
* }
|
|
40
|
+
*/
|
|
41
|
+
primaryDb: PrimaryDbConfig;
|
|
42
|
+
/**
|
|
43
|
+
* Primary database client constructor (for querying tenant registry)
|
|
44
|
+
* Only required in gateway mode
|
|
45
|
+
* @example import { PrismaClient } from '@prisma/client'
|
|
46
|
+
*/
|
|
47
|
+
prismaClientConstructor: any;
|
|
48
|
+
/**
|
|
49
|
+
* Connection cache TTL in milliseconds
|
|
50
|
+
* Idle connections will be closed after this period
|
|
51
|
+
* @default 300000 (5 minutes)
|
|
52
|
+
*/
|
|
53
|
+
connectionCacheTTL?: number;
|
|
54
|
+
/**
|
|
55
|
+
* Maximum number of concurrent connections per tenant
|
|
56
|
+
* @default 10
|
|
57
|
+
*/
|
|
58
|
+
maxConnections?: number;
|
|
59
|
+
/**
|
|
60
|
+
* Encryption key for decrypting database credentials
|
|
61
|
+
* Required if tenant config stores encrypted passwords
|
|
62
|
+
*/
|
|
63
|
+
encryptionKey?: string;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Tenant configuration stored in cloud database
|
|
68
|
+
* This is the shape of data returned from the tenant registry
|
|
69
|
+
*/
|
|
70
|
+
interface TenantInfo {
|
|
71
|
+
/** Unique tenant identifier */
|
|
72
|
+
id: string;
|
|
73
|
+
/** Human-readable tenant slug */
|
|
74
|
+
subdomain: string;
|
|
75
|
+
/** Tenant type */
|
|
76
|
+
type: 'SHARED' | 'DEDIACTED';
|
|
77
|
+
/** Tenant status */
|
|
78
|
+
status: string;
|
|
79
|
+
/** For CLOUD tenants: schema name */
|
|
80
|
+
schemaName?: string;
|
|
81
|
+
/** For ENTERPRISE tenants: database configuration */
|
|
82
|
+
databaseName?: string;
|
|
83
|
+
databaseHost?: string;
|
|
84
|
+
databasePort?: number;
|
|
85
|
+
databaseUsername?: string;
|
|
86
|
+
databasePassword?: string;
|
|
87
|
+
databaseSslMode?: string;
|
|
88
|
+
connectionPoolSize?: number;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Dynamic module for multi-tenant database management
|
|
93
|
+
*
|
|
94
|
+
* This module provides:
|
|
95
|
+
* - Tenant context management (request-scoped)
|
|
96
|
+
* - Database connection pooling
|
|
97
|
+
* - Dynamic schema/cluster routing
|
|
98
|
+
* - Support for both gateway and microservice modes
|
|
99
|
+
*
|
|
100
|
+
* ## Gateway Mode (API Gateway)
|
|
101
|
+
* - Automatically extracts tenant from subdomain, falls back to x-tenant-id header
|
|
102
|
+
* - Provide primaryDb configuration and prismaClientConstructor
|
|
103
|
+
* - Automatically queries primary DB for tenant config
|
|
104
|
+
* - Attaches TenantContextInterceptor globally
|
|
105
|
+
*
|
|
106
|
+
* ## Microservice Mode
|
|
107
|
+
* - Only provide prismaClientConstructor
|
|
108
|
+
* - Tenant context comes from RabbitMQ messages
|
|
109
|
+
* - Use MessageTenantContextInterceptor manually
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* // Gateway configuration
|
|
113
|
+
* DatabaseModule.forRootAsync({
|
|
114
|
+
* imports: [ConfigModule],
|
|
115
|
+
* useFactory: (config: ConfigService) => ({
|
|
116
|
+
* primaryDb: {
|
|
117
|
+
* host: config.get('PRIMARY_DB_HOST'),
|
|
118
|
+
* port: config.get('PRIMARY_DB_PORT'),
|
|
119
|
+
* username: config.get('PRIMARY_DB_USERNAME'),
|
|
120
|
+
* password: config.get('PRIMARY_DB_PASSWORD'),
|
|
121
|
+
* database: config.get('PRIMARY_DB_DATABASE'),
|
|
122
|
+
* },
|
|
123
|
+
* prismaClientConstructor: PrismaClient,
|
|
124
|
+
* }),
|
|
125
|
+
* inject: [ConfigService],
|
|
126
|
+
* })
|
|
127
|
+
*
|
|
128
|
+
* @example
|
|
129
|
+
* // Microservice configuration
|
|
130
|
+
* DatabaseModule.forRoot({
|
|
131
|
+
* prismaClientConstructor: PrismaClient,
|
|
132
|
+
* })
|
|
133
|
+
*/
|
|
134
|
+
declare class DatabaseModule {
|
|
135
|
+
/**
|
|
136
|
+
* Synchronous configuration
|
|
137
|
+
*
|
|
138
|
+
* @param options Module configuration options
|
|
139
|
+
* @returns Dynamic module configuration
|
|
140
|
+
*/
|
|
141
|
+
static forRoot(options: DatabaseModuleOptions): DynamicModule;
|
|
142
|
+
/**
|
|
143
|
+
* Asynchronous configuration (recommended)
|
|
144
|
+
*
|
|
145
|
+
* Allows injecting ConfigService or other dependencies
|
|
146
|
+
*
|
|
147
|
+
* @param options Async configuration options
|
|
148
|
+
* @returns Dynamic module configuration
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* DatabaseModule.forRootAsync({
|
|
152
|
+
* imports: [ConfigModule],
|
|
153
|
+
* useFactory: async (config: ConfigService) => ({
|
|
154
|
+
* cloudDatabaseUrl: config.get('CLOUD_DATABASE_URL'),
|
|
155
|
+
* prismaClientConstructor: PrismaClient,
|
|
156
|
+
* tenantResolver: 'subdomain',
|
|
157
|
+
* }),
|
|
158
|
+
* inject: [ConfigService],
|
|
159
|
+
* })
|
|
160
|
+
*/
|
|
161
|
+
static forRootAsync(options: {
|
|
162
|
+
useFactory: (...args: any[]) => Promise<DatabaseModuleOptions> | DatabaseModuleOptions;
|
|
163
|
+
inject?: any[];
|
|
164
|
+
}): DynamicModule;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Service responsible for querying the primary database to resolve tenant configurations
|
|
169
|
+
*
|
|
170
|
+
* This service:
|
|
171
|
+
* - Connects to the primary database (tenant registry)
|
|
172
|
+
* - Queries tenant metadata (database location, credentials, etc.)
|
|
173
|
+
* - Caches tenant configs in memory to reduce database load
|
|
174
|
+
* - Only used in GATEWAY MODE (microservices receive tenant config from messages)
|
|
175
|
+
*
|
|
176
|
+
* @example
|
|
177
|
+
* // In API Gateway
|
|
178
|
+
* const config = await primaryDatabase.getTenantConfig('acme');
|
|
179
|
+
* // Returns: { id, slug, type, databaseHost, databaseName, ... }
|
|
180
|
+
*/
|
|
181
|
+
declare class PrimaryDatabaseService implements OnModuleInit, OnModuleDestroy {
|
|
182
|
+
private readonly options;
|
|
183
|
+
private readonly logger;
|
|
184
|
+
/** Primary database client for querying tenant registry */
|
|
185
|
+
private primaryDbClient;
|
|
186
|
+
/** In-memory cache: Map<tenantIdentifier, TenantConfig> */
|
|
187
|
+
private readonly tenantConfigCache;
|
|
188
|
+
/** Cache TTL in milliseconds */
|
|
189
|
+
private readonly cacheTTL;
|
|
190
|
+
constructor(options: DatabaseModuleOptions);
|
|
191
|
+
onModuleInit(): Promise<void>;
|
|
192
|
+
/**
|
|
193
|
+
* Initialize connection to primary database
|
|
194
|
+
*/
|
|
195
|
+
private initializePrimaryDbClient;
|
|
196
|
+
/**
|
|
197
|
+
* Build connection URL from primary database properties
|
|
198
|
+
*/
|
|
199
|
+
private buildPrimaryDbUrl;
|
|
200
|
+
/**
|
|
201
|
+
* Mask password in connection URL for logging
|
|
202
|
+
*/
|
|
203
|
+
private maskPassword;
|
|
204
|
+
/**
|
|
205
|
+
* Get tenant configuration by identifier (ID or slug)
|
|
206
|
+
*
|
|
207
|
+
* @param tenantIdentifier Tenant ID or slug
|
|
208
|
+
* @returns Tenant configuration or null if not found
|
|
209
|
+
*/
|
|
210
|
+
getTenantInfo(tenantIdentifier: string): Promise<TenantInfo | null>;
|
|
211
|
+
/**
|
|
212
|
+
* Cache tenant information with TTL
|
|
213
|
+
*/
|
|
214
|
+
private cacheInfo;
|
|
215
|
+
/**
|
|
216
|
+
* Clear cached tenant information
|
|
217
|
+
*
|
|
218
|
+
* Useful when tenant settings are updated and cache needs to be invalidated
|
|
219
|
+
*
|
|
220
|
+
* @param tenantIdentifier Tenant ID or slug
|
|
221
|
+
*/
|
|
222
|
+
clearTenantCache(tenantIdentifier: string): void;
|
|
223
|
+
/**
|
|
224
|
+
* Clear all cached tenant configurations
|
|
225
|
+
*/
|
|
226
|
+
clearAllCaches(): void;
|
|
227
|
+
/**
|
|
228
|
+
* Get primary database client for direct database access
|
|
229
|
+
*
|
|
230
|
+
* This is useful for platform admin operations (creating tenants, billing, etc.)
|
|
231
|
+
*
|
|
232
|
+
* @returns Primary database client instance
|
|
233
|
+
* @throws Error if primary database client is not initialized
|
|
234
|
+
*/
|
|
235
|
+
getPrimaryDbClient<T = any>(): T;
|
|
236
|
+
/**
|
|
237
|
+
* Decrypt database credentials
|
|
238
|
+
*
|
|
239
|
+
* Override this method to implement your encryption strategy
|
|
240
|
+
*
|
|
241
|
+
* @param encrypted Encrypted value
|
|
242
|
+
* @returns Decrypted value
|
|
243
|
+
*/
|
|
244
|
+
private decrypt;
|
|
245
|
+
onModuleDestroy(): Promise<void>;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Request-scoped service that holds tenant context for the current request or RabbitMQ message
|
|
250
|
+
*
|
|
251
|
+
* IMPORTANT: This service is REQUEST-SCOPED, meaning NestJS creates a new instance
|
|
252
|
+
* for each HTTP request or RabbitMQ message. This ensures tenant isolation and
|
|
253
|
+
* prevents cross-tenant data leaks in concurrent scenarios.
|
|
254
|
+
*
|
|
255
|
+
* @example
|
|
256
|
+
* // In a controller or service
|
|
257
|
+
* constructor(private readonly tenantContext: TenantContextService) {}
|
|
258
|
+
*
|
|
259
|
+
* async handleRequest() {
|
|
260
|
+
* const tenant = this.tenantContext.getTenant();
|
|
261
|
+
* console.log(`Processing request for tenant: ${tenant.tenantSlug}`);
|
|
262
|
+
* }
|
|
263
|
+
*/
|
|
264
|
+
declare class TenantContextService {
|
|
265
|
+
private tenantInfo;
|
|
266
|
+
/**
|
|
267
|
+
* Set tenant information for this request/message
|
|
268
|
+
*
|
|
269
|
+
* This is typically called by:
|
|
270
|
+
* - TenantContextInterceptor (for HTTP requests in gateway)
|
|
271
|
+
* - MessageTenantContextInterceptor (for RabbitMQ messages in microservices)
|
|
272
|
+
* - Manual context setup in message handlers
|
|
273
|
+
*
|
|
274
|
+
* @param tenantInfo Complete tenant information
|
|
275
|
+
* @throws Error if tenant context is already set (prevents accidental overwrites)
|
|
276
|
+
*/
|
|
277
|
+
setTenant(tenantInfo: TenantInfo): void;
|
|
278
|
+
/**
|
|
279
|
+
* Get tenant information for this request/message
|
|
280
|
+
*
|
|
281
|
+
* @returns Tenant information
|
|
282
|
+
* @throws UnauthorizedException if tenant context hasn't been set
|
|
283
|
+
*/
|
|
284
|
+
getTenant(): TenantInfo;
|
|
285
|
+
/**
|
|
286
|
+
* Check if tenant context has been set
|
|
287
|
+
*
|
|
288
|
+
* @returns true if tenant context is available
|
|
289
|
+
*/
|
|
290
|
+
hasTenant(): boolean;
|
|
291
|
+
/**
|
|
292
|
+
* Clear tenant context
|
|
293
|
+
*
|
|
294
|
+
* This is useful for cleanup in RabbitMQ message handlers
|
|
295
|
+
* after the message has been processed.
|
|
296
|
+
*
|
|
297
|
+
* HTTP requests don't need manual cleanup as the service
|
|
298
|
+
* instance is destroyed when the request ends.
|
|
299
|
+
*/
|
|
300
|
+
clearTenant(): void;
|
|
301
|
+
/**
|
|
302
|
+
* Get tenant ID safely (returns null if not set)
|
|
303
|
+
*
|
|
304
|
+
* @returns Tenant ID or null
|
|
305
|
+
*/
|
|
306
|
+
getTenantIdSafe(): string | null;
|
|
307
|
+
/**
|
|
308
|
+
* Get tenant subdomain safely (returns null if not set)
|
|
309
|
+
*
|
|
310
|
+
* @returns Tenant subdomain or null
|
|
311
|
+
*/
|
|
312
|
+
getTenantSubdomainSafe(): string | null;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Service responsible for managing tenant-scoped database connections
|
|
317
|
+
*
|
|
318
|
+
* This service:
|
|
319
|
+
* - Maintains a connection pool (Map<cacheKey, DbClient>)
|
|
320
|
+
* - Creates new connections dynamically based on tenant context
|
|
321
|
+
* - Reuses existing connections for the same tenant
|
|
322
|
+
* - Supports both cloud schemas and enterprise databases
|
|
323
|
+
* - Automatically cleans up idle connections
|
|
324
|
+
*
|
|
325
|
+
* @example
|
|
326
|
+
* // In a controller or service
|
|
327
|
+
* const dbClient = await this.tenantDatabase.getDbClient<PrismaClient>();
|
|
328
|
+
* const users = await dbClient.user.findMany();
|
|
329
|
+
*/
|
|
330
|
+
declare class TenantDatabaseService implements OnModuleDestroy {
|
|
331
|
+
private readonly options;
|
|
332
|
+
private readonly tenantContext;
|
|
333
|
+
private readonly logger;
|
|
334
|
+
/** Connection pool: Map<cacheKey, DbClient> */
|
|
335
|
+
private readonly clients;
|
|
336
|
+
/** Track last usage time for idle connection cleanup */
|
|
337
|
+
private readonly clientLastUsed;
|
|
338
|
+
/** Cleanup interval timer */
|
|
339
|
+
private cleanupInterval?;
|
|
340
|
+
constructor(options: DatabaseModuleOptions, tenantContext: TenantContextService);
|
|
341
|
+
/**
|
|
342
|
+
* Get tenant-scoped database client for the current request/message
|
|
343
|
+
*
|
|
344
|
+
* This method:
|
|
345
|
+
* 1. Gets tenant info from TenantContextService
|
|
346
|
+
* 2. Builds a connection URL based on tenant type
|
|
347
|
+
* 3. Returns cached client if exists, otherwise creates new one
|
|
348
|
+
*
|
|
349
|
+
* @returns Promise<Database client instance>
|
|
350
|
+
* @throws UnauthorizedException if tenant context not set
|
|
351
|
+
* @throws InternalServerErrorException if connection fails
|
|
352
|
+
*
|
|
353
|
+
* @example
|
|
354
|
+
* const dbClient = await tenantDatabase.getDbClient<PrismaClient>();
|
|
355
|
+
* const users = await dbClient.user.findMany();
|
|
356
|
+
*/
|
|
357
|
+
getDbClient<T = any>(): Promise<T>;
|
|
358
|
+
/**
|
|
359
|
+
* Create a new database client for the given tenant
|
|
360
|
+
*/
|
|
361
|
+
private createDbClient;
|
|
362
|
+
/**
|
|
363
|
+
* Build connection URL for enterprise tenant (dedicated database)
|
|
364
|
+
*/
|
|
365
|
+
private buildTenantDbUrl;
|
|
366
|
+
/**
|
|
367
|
+
* Build cache key for connection pooling
|
|
368
|
+
*/
|
|
369
|
+
private buildCacheKey;
|
|
370
|
+
/**
|
|
371
|
+
* Start periodic cleanup of idle connections
|
|
372
|
+
*/
|
|
373
|
+
private startConnectionCleaner;
|
|
374
|
+
/**
|
|
375
|
+
* Clean up idle connections that haven't been used recently
|
|
376
|
+
*/
|
|
377
|
+
private cleanupIdleConnections;
|
|
378
|
+
/**
|
|
379
|
+
* Get current connection pool statistics
|
|
380
|
+
*/
|
|
381
|
+
getPoolStats(): {
|
|
382
|
+
activeConnections: number;
|
|
383
|
+
tenants: string[];
|
|
384
|
+
};
|
|
385
|
+
/**
|
|
386
|
+
* Mask password in connection URL for logging
|
|
387
|
+
*/
|
|
388
|
+
private maskPassword;
|
|
389
|
+
onModuleDestroy(): Promise<void>;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Interceptor that extracts tenant context from RabbitMQ messages (Microservice Mode)
|
|
394
|
+
*
|
|
395
|
+
* This interceptor:
|
|
396
|
+
* 1. Extracts tenant info from RabbitMQ message payload
|
|
397
|
+
* 2. Sets it in REQUEST-SCOPED TenantContextService
|
|
398
|
+
* 3. Cleans up after message is processed
|
|
399
|
+
*
|
|
400
|
+
* Expected message format:
|
|
401
|
+
* {
|
|
402
|
+
* dto: { ... },
|
|
403
|
+
* tenant: {
|
|
404
|
+
* tenantId: 'abc-123',
|
|
405
|
+
* tenantSlug: 'acme',
|
|
406
|
+
* tenantType: 'ENTERPRISE',
|
|
407
|
+
* databaseHost: 'enterprise-1.aws.com',
|
|
408
|
+
* databaseName: 'acme_db',
|
|
409
|
+
* ...
|
|
410
|
+
* }
|
|
411
|
+
* }
|
|
412
|
+
*
|
|
413
|
+
* @example
|
|
414
|
+
* // In microservice module
|
|
415
|
+
* {
|
|
416
|
+
* provide: APP_INTERCEPTOR,
|
|
417
|
+
* useClass: MessageTenantContextInterceptor,
|
|
418
|
+
* }
|
|
419
|
+
*/
|
|
420
|
+
declare class MessageTenantContextInterceptor implements NestInterceptor {
|
|
421
|
+
private readonly tenantContext;
|
|
422
|
+
private readonly logger;
|
|
423
|
+
constructor(tenantContext: TenantContextService);
|
|
424
|
+
intercept(context: ExecutionContext, next: CallHandler): Observable<any>;
|
|
425
|
+
/**
|
|
426
|
+
* Clean up tenant context after message is processed
|
|
427
|
+
*/
|
|
428
|
+
private cleanupContext;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Interceptor that extracts tenant context from HTTP requests (Gateway Mode)
|
|
433
|
+
*
|
|
434
|
+
* This interceptor runs BEFORE the controller and:
|
|
435
|
+
* 1. Extracts tenant identifier from request (tries subdomain first, then falls back to header)
|
|
436
|
+
* 2. Queries primary database for tenant configuration
|
|
437
|
+
* 3. Stores tenant info in REQUEST-SCOPED TenantContextService
|
|
438
|
+
*
|
|
439
|
+
* Tenant resolution order:
|
|
440
|
+
* - First: Subdomain (e.g., acme.vritti.com → 'acme')
|
|
441
|
+
* - Fallback: x-tenant-id or x-tenant-slug header
|
|
442
|
+
*
|
|
443
|
+
* Only used in API Gateway. Microservices use MessageTenantContextInterceptor instead.
|
|
444
|
+
*
|
|
445
|
+
* @example
|
|
446
|
+
* // Request: https://acme.vritti.com/api/users
|
|
447
|
+
* // Interceptor extracts "acme" from subdomain, queries primary DB, sets context
|
|
448
|
+
*/
|
|
449
|
+
declare class TenantContextInterceptor implements NestInterceptor {
|
|
450
|
+
private readonly tenantContext;
|
|
451
|
+
private readonly primaryDatabase;
|
|
452
|
+
private readonly options;
|
|
453
|
+
private readonly logger;
|
|
454
|
+
constructor(tenantContext: TenantContextService, primaryDatabase: PrimaryDatabaseService, options: DatabaseModuleOptions);
|
|
455
|
+
intercept(context: ExecutionContext, next: CallHandler): Promise<Observable<any>>;
|
|
456
|
+
/**
|
|
457
|
+
* Extract tenant identifier: tries subdomain first, then falls back to header
|
|
458
|
+
*/
|
|
459
|
+
private extractTenantIdentifier;
|
|
460
|
+
/**
|
|
461
|
+
* Extract tenant from subdomain
|
|
462
|
+
* @example acme.vritti.com → 'acme'
|
|
463
|
+
*/
|
|
464
|
+
private extractFromSubdomain;
|
|
465
|
+
/**
|
|
466
|
+
* Extract tenant from HTTP headers
|
|
467
|
+
* Checks x-tenant-id and x-subdomain headers
|
|
468
|
+
*/
|
|
469
|
+
private extractFromHeader;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* Parameter decorator that injects tenant metadata into controller method
|
|
474
|
+
*
|
|
475
|
+
* This decorator retrieves tenant information (ID, slug, type, etc.)
|
|
476
|
+
* from the REQUEST-SCOPED TenantContextService.
|
|
477
|
+
*
|
|
478
|
+
* Useful for:
|
|
479
|
+
* - Logging tenant-specific information
|
|
480
|
+
* - Implementing tenant-specific business logic
|
|
481
|
+
* - Auditing and tracking
|
|
482
|
+
* - Conditional feature flags
|
|
483
|
+
*
|
|
484
|
+
* @returns TenantInfo object with tenant metadata
|
|
485
|
+
*
|
|
486
|
+
* @example
|
|
487
|
+
* // Access tenant metadata
|
|
488
|
+
* @Get('info')
|
|
489
|
+
* async getTenantInfo(@Tenant() tenant: TenantInfo) {
|
|
490
|
+
* return {
|
|
491
|
+
* tenantId: tenant.tenantId,
|
|
492
|
+
* tenantSlug: tenant.tenantSlug,
|
|
493
|
+
* tenantType: tenant.tenantType,
|
|
494
|
+
* };
|
|
495
|
+
* }
|
|
496
|
+
*
|
|
497
|
+
* @example
|
|
498
|
+
* // Use for logging
|
|
499
|
+
* @Post()
|
|
500
|
+
* async createUser(
|
|
501
|
+
* @Body() dto: CreateUserDto,
|
|
502
|
+
* @Tenant() tenant: TenantInfo,
|
|
503
|
+
* ) {
|
|
504
|
+
* this.logger.log(`Creating user for tenant: ${tenant.tenantSlug}`);
|
|
505
|
+
* // ...
|
|
506
|
+
* }
|
|
507
|
+
*
|
|
508
|
+
* @example
|
|
509
|
+
* // Conditional business logic
|
|
510
|
+
* @Get('features')
|
|
511
|
+
* async getFeatures(@Tenant() tenant: TenantInfo) {
|
|
512
|
+
* if (tenant.tenantType === 'ENTERPRISE') {
|
|
513
|
+
* return ['feature-a', 'feature-b', 'feature-c'];
|
|
514
|
+
* }
|
|
515
|
+
* return ['feature-a'];
|
|
516
|
+
* }
|
|
517
|
+
*/
|
|
518
|
+
declare const Tenant: (...dataOrPipes: unknown[]) => ParameterDecorator;
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* Extract subdomain from hostname
|
|
522
|
+
*
|
|
523
|
+
* @param host Full hostname (e.g., 'acme.vritti.com:3000' or 'acme.vritti.com')
|
|
524
|
+
* @returns Subdomain or null if not found
|
|
525
|
+
*
|
|
526
|
+
* @example
|
|
527
|
+
* extractSubdomain('acme.vritti.com') // 'acme'
|
|
528
|
+
* extractSubdomain('staging-acme.vritti.com') // 'staging-acme'
|
|
529
|
+
* extractSubdomain('localhost') // null
|
|
530
|
+
* extractSubdomain('vritti.com') // null
|
|
531
|
+
*/
|
|
532
|
+
declare function extractSubdomain(host: string): string | null;
|
|
533
|
+
|
|
534
|
+
export { DatabaseModule, type DatabaseModuleOptions, MessageTenantContextInterceptor, PrimaryDatabaseService, type PrimaryDbConfig, Tenant, TenantContextInterceptor, TenantContextService, TenantDatabaseService, type TenantInfo, extractSubdomain };
|