@vritti/api-sdk 0.0.1 → 0.0.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.js CHANGED
@@ -1,11 +1,1228 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
3
 
4
- // src/index.ts
5
- var getHello = /* @__PURE__ */ __name(() => {
6
- return "Hello, World!";
7
- }, "getHello");
4
+ // src/auth/auth-config.module.ts
5
+ import { Global as Global2, Module as Module2 } from "@nestjs/common";
6
+ import { ConfigModule, ConfigService as ConfigService2 } from "@nestjs/config";
7
+ import { APP_GUARD } from "@nestjs/core";
8
+ import { JwtModule } from "@nestjs/jwt";
9
+
10
+ // src/request/request.module.ts
11
+ import { Global, Module } from "@nestjs/common";
12
+
13
+ // src/request/services/request.service.ts
14
+ import { Inject, Injectable, Scope } from "@nestjs/common";
15
+ import { REQUEST } from "@nestjs/core";
16
+ function _ts_decorate(decorators, target, key, desc) {
17
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
18
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
19
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
20
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
21
+ }
22
+ __name(_ts_decorate, "_ts_decorate");
23
+ function _ts_metadata(k, v) {
24
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
25
+ }
26
+ __name(_ts_metadata, "_ts_metadata");
27
+ function _ts_param(paramIndex, decorator) {
28
+ return function(target, key) {
29
+ decorator(target, key, paramIndex);
30
+ };
31
+ }
32
+ __name(_ts_param, "_ts_param");
33
+ var RequestService = class {
34
+ static {
35
+ __name(this, "RequestService");
36
+ }
37
+ request;
38
+ constructor(request) {
39
+ this.request = request;
40
+ }
41
+ /**
42
+ * Extract tenant identifier from request headers
43
+ * Priority: x-tenant-id > x-subdomain
44
+ * @returns Tenant identifier or null if not found
45
+ */
46
+ getTenantIdentifier() {
47
+ const getHeader = /* @__PURE__ */ __name((key) => {
48
+ const value = this.request.headers?.[key];
49
+ return Array.isArray(value) ? value[0] : value;
50
+ }, "getHeader");
51
+ return getHeader("x-tenant-id") || getHeader("x-subdomain") || null;
52
+ }
53
+ /**
54
+ * Extract access token from Authorization header
55
+ * Format: "Bearer <token>"
56
+ * @returns Access token or null if not found
57
+ */
58
+ getAccessToken() {
59
+ const authHeader = this.request.headers?.authorization;
60
+ if (!authHeader) {
61
+ return null;
62
+ }
63
+ const [type, token] = authHeader.split(" ") ?? [];
64
+ return type === "Bearer" && token ? token : null;
65
+ }
66
+ /**
67
+ * Extract refresh token from session-id cookie
68
+ * Cookie name: session-id
69
+ * @returns Refresh token or null if not found
70
+ */
71
+ getRefreshToken() {
72
+ try {
73
+ const cookies = this.request.cookies;
74
+ if (cookies && typeof cookies === "object") {
75
+ const sessionId = cookies["session-id"];
76
+ if (sessionId) {
77
+ return sessionId;
78
+ }
79
+ }
80
+ return null;
81
+ } catch (error) {
82
+ return null;
83
+ }
84
+ }
85
+ /**
86
+ * Get a specific header value
87
+ * @param key Header key
88
+ * @returns Header value (string, array, or undefined)
89
+ */
90
+ getHeader(key) {
91
+ return this.request.headers?.[key];
92
+ }
93
+ /**
94
+ * Get all headers
95
+ * @returns Record of all headers
96
+ */
97
+ getAllHeaders() {
98
+ return this.request.headers || {};
99
+ }
100
+ };
101
+ RequestService = _ts_decorate([
102
+ Injectable({
103
+ scope: Scope.REQUEST
104
+ }),
105
+ _ts_param(0, Inject(REQUEST)),
106
+ _ts_metadata("design:type", Function),
107
+ _ts_metadata("design:paramtypes", [
108
+ typeof FastifyRequest === "undefined" ? Object : FastifyRequest
109
+ ])
110
+ ], RequestService);
111
+
112
+ // src/request/request.module.ts
113
+ function _ts_decorate2(decorators, target, key, desc) {
114
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
115
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
116
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
117
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
118
+ }
119
+ __name(_ts_decorate2, "_ts_decorate");
120
+ var RequestModule = class {
121
+ static {
122
+ __name(this, "RequestModule");
123
+ }
124
+ };
125
+ RequestModule = _ts_decorate2([
126
+ Global(),
127
+ Module({
128
+ providers: [
129
+ RequestService
130
+ ],
131
+ exports: [
132
+ RequestService
133
+ ]
134
+ })
135
+ ], RequestModule);
136
+
137
+ // src/auth/guards/vritti-auth.guard.ts
138
+ import { Injectable as Injectable3, Logger as Logger2, Scope as Scope2, UnauthorizedException } from "@nestjs/common";
139
+ import { ConfigService } from "@nestjs/config";
140
+ import { Reflector } from "@nestjs/core";
141
+ import { JwtService } from "@nestjs/jwt";
142
+ import * as jwt from "jsonwebtoken";
143
+
144
+ // src/database/services/primary-database.service.ts
145
+ import { Inject as Inject2, Injectable as Injectable2, InternalServerErrorException, Logger } from "@nestjs/common";
146
+
147
+ // src/database/constants.ts
148
+ var DATABASE_MODULE_OPTIONS = Symbol("DATABASE_MODULE_OPTIONS");
149
+
150
+ // src/database/services/primary-database.service.ts
151
+ function _ts_decorate3(decorators, target, key, desc) {
152
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
153
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
154
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
155
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
156
+ }
157
+ __name(_ts_decorate3, "_ts_decorate");
158
+ function _ts_metadata2(k, v) {
159
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
160
+ }
161
+ __name(_ts_metadata2, "_ts_metadata");
162
+ function _ts_param2(paramIndex, decorator) {
163
+ return function(target, key) {
164
+ decorator(target, key, paramIndex);
165
+ };
166
+ }
167
+ __name(_ts_param2, "_ts_param");
168
+ var PrimaryDatabaseService = class _PrimaryDatabaseService {
169
+ static {
170
+ __name(this, "PrimaryDatabaseService");
171
+ }
172
+ options;
173
+ logger = new Logger(_PrimaryDatabaseService.name);
174
+ /** Primary database client for querying tenant registry */
175
+ primaryDbClient;
176
+ /** In-memory cache: Map<tenantIdentifier, TenantConfig> */
177
+ tenantConfigCache = /* @__PURE__ */ new Map();
178
+ /** Cache TTL in milliseconds */
179
+ cacheTTL;
180
+ constructor(options) {
181
+ this.options = options;
182
+ this.cacheTTL = options.connectionCacheTTL || 3e5;
183
+ }
184
+ async onModuleInit() {
185
+ if (this.options.primaryDb) {
186
+ await this.initializePrimaryDbClient();
187
+ }
188
+ }
189
+ /**
190
+ * Initialize connection to primary database
191
+ */
192
+ async initializePrimaryDbClient() {
193
+ try {
194
+ const PrimaryDbClient = this.options.prismaClientConstructor;
195
+ const databaseUrl = this.buildPrimaryDbUrl();
196
+ this.primaryDbClient = new PrimaryDbClient({
197
+ datasources: {
198
+ db: {
199
+ url: databaseUrl
200
+ }
201
+ },
202
+ log: [
203
+ "error",
204
+ "warn"
205
+ ]
206
+ });
207
+ await this.primaryDbClient.$connect();
208
+ this.logger.log("Connected to primary database (tenant registry)");
209
+ } catch (error) {
210
+ this.logger.error("Failed to connect to primary database", error);
211
+ throw new InternalServerErrorException("Failed to initialize tenant registry");
212
+ }
213
+ }
214
+ /**
215
+ * Build connection URL from primary database properties
216
+ */
217
+ buildPrimaryDbUrl() {
218
+ if (!this.options.primaryDb) {
219
+ throw new Error("Primary database configuration not provided");
220
+ }
221
+ const { host, port = 5432, username, password, database, schema = "public", sslMode = "require" } = this.options.primaryDb;
222
+ let url = `postgresql://${username}:${password}@${host}:${port}/${database}`;
223
+ const params = new URLSearchParams();
224
+ if (schema) {
225
+ params.set("schema", schema);
226
+ }
227
+ params.set("sslmode", sslMode);
228
+ const queryString = params.toString();
229
+ if (queryString) {
230
+ url += `?${queryString}`;
231
+ }
232
+ this.logger.debug(`Primary DB connection URL: ${this.maskPassword(url)}`);
233
+ return url;
234
+ }
235
+ /**
236
+ * Mask password in connection URL for logging
237
+ */
238
+ maskPassword(url) {
239
+ return url.replace(/:([^@]+)@/, ":****@");
240
+ }
241
+ /**
242
+ * Get tenant configuration by identifier (ID or slug)
243
+ *
244
+ * @param tenantIdentifier Tenant ID or slug
245
+ * @returns Tenant configuration or null if not found
246
+ */
247
+ async getTenantInfo(tenantIdentifier) {
248
+ const cached = this.tenantConfigCache.get(tenantIdentifier);
249
+ if (cached) {
250
+ this.logger.debug(`Cache hit for tenant: ${tenantIdentifier}`);
251
+ return cached;
252
+ }
253
+ try {
254
+ if (!this.primaryDbClient) {
255
+ throw new Error("Primary database client not initialized");
256
+ }
257
+ this.logger.debug(`Querying primary database for tenant: ${tenantIdentifier}`);
258
+ const tenant = await this.primaryDbClient.tenant.findFirst({
259
+ where: {
260
+ OR: [
261
+ {
262
+ id: tenantIdentifier
263
+ },
264
+ {
265
+ subdomain: tenantIdentifier
266
+ }
267
+ ],
268
+ status: "ACTIVE"
269
+ },
270
+ include: {
271
+ databaseConfig: true
272
+ }
273
+ });
274
+ if (!tenant) {
275
+ this.logger.warn(`Tenant not found: ${tenantIdentifier}`);
276
+ return null;
277
+ }
278
+ const info = {
279
+ id: tenant.id,
280
+ subdomain: tenant.subdomain,
281
+ type: tenant.dbType,
282
+ status: tenant.status,
283
+ // For SHARED tenants: schema name
284
+ schemaName: tenant.databaseConfig?.dbSchema || void 0,
285
+ // For DEDICATED tenants: database configuration from TenantDatabaseConfig table
286
+ databaseName: tenant.databaseConfig?.dbName || void 0,
287
+ databaseHost: tenant.databaseConfig?.dbHost || void 0,
288
+ databasePort: tenant.databaseConfig?.dbPort || void 0,
289
+ databaseUsername: tenant.databaseConfig?.dbUsername ? this.decrypt(tenant.databaseConfig.dbUsername) : void 0,
290
+ databasePassword: tenant.databaseConfig?.dbPassword ? this.decrypt(tenant.databaseConfig.dbPassword) : void 0,
291
+ databaseSslMode: tenant.databaseConfig?.dbSslMode || void 0,
292
+ connectionPoolSize: tenant.databaseConfig?.connectionPoolSize || void 0
293
+ };
294
+ this.cacheInfo(info);
295
+ return info;
296
+ } catch (error) {
297
+ this.logger.error(`Failed to fetch tenant info: ${tenantIdentifier}`, error);
298
+ throw new InternalServerErrorException("Failed to resolve tenant");
299
+ }
300
+ }
301
+ /**
302
+ * Cache tenant information with TTL
303
+ */
304
+ cacheInfo(info) {
305
+ this.tenantConfigCache.set(info.id, info);
306
+ this.tenantConfigCache.set(info.subdomain, info);
307
+ setTimeout(() => {
308
+ this.tenantConfigCache.delete(info.id);
309
+ this.tenantConfigCache.delete(info.subdomain);
310
+ this.logger.debug(`Cache expired for tenant: ${info.subdomain}`);
311
+ }, this.cacheTTL);
312
+ }
313
+ /**
314
+ * Clear cached tenant information
315
+ *
316
+ * Useful when tenant settings are updated and cache needs to be invalidated
317
+ *
318
+ * @param tenantIdentifier Tenant ID or slug
319
+ */
320
+ clearTenantCache(tenantIdentifier) {
321
+ const config = this.tenantConfigCache.get(tenantIdentifier);
322
+ if (config) {
323
+ this.tenantConfigCache.delete(config.id);
324
+ this.tenantConfigCache.delete(config.subdomain);
325
+ this.logger.log(`Cleared cache for tenant: ${tenantIdentifier}`);
326
+ }
327
+ }
328
+ /**
329
+ * Clear all cached tenant configurations
330
+ */
331
+ clearAllCaches() {
332
+ const size = this.tenantConfigCache.size;
333
+ this.tenantConfigCache.clear();
334
+ this.logger.log(`Cleared ${size} cached tenant configs`);
335
+ }
336
+ /**
337
+ * Get primary database client for direct database access
338
+ *
339
+ * This is useful for platform admin operations (creating tenants, billing, etc.)
340
+ *
341
+ * @returns Primary database client instance
342
+ * @throws Error if primary database client is not initialized
343
+ */
344
+ getPrimaryDbClient() {
345
+ if (!this.primaryDbClient) {
346
+ throw new Error("Primary database client not initialized. Are you in gateway mode?");
347
+ }
348
+ return this.primaryDbClient;
349
+ }
350
+ /**
351
+ * Decrypt database credentials
352
+ *
353
+ * Override this method to implement your encryption strategy
354
+ *
355
+ * @param encrypted Encrypted value
356
+ * @returns Decrypted value
357
+ */
358
+ decrypt(encrypted) {
359
+ return encrypted;
360
+ }
361
+ async onModuleDestroy() {
362
+ if (this.primaryDbClient) {
363
+ await this.primaryDbClient.$disconnect();
364
+ this.logger.log("Disconnected from primary database");
365
+ }
366
+ }
367
+ };
368
+ PrimaryDatabaseService = _ts_decorate3([
369
+ Injectable2(),
370
+ _ts_param2(0, Inject2(DATABASE_MODULE_OPTIONS)),
371
+ _ts_metadata2("design:type", Function),
372
+ _ts_metadata2("design:paramtypes", [
373
+ typeof DatabaseModuleOptions === "undefined" ? Object : DatabaseModuleOptions
374
+ ])
375
+ ], PrimaryDatabaseService);
376
+
377
+ // src/auth/guards/vritti-auth.guard.ts
378
+ function _ts_decorate4(decorators, target, key, desc) {
379
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
380
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
381
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
382
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
383
+ }
384
+ __name(_ts_decorate4, "_ts_decorate");
385
+ function _ts_metadata3(k, v) {
386
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
387
+ }
388
+ __name(_ts_metadata3, "_ts_metadata");
389
+ var VrittiAuthGuard = class _VrittiAuthGuard {
390
+ static {
391
+ __name(this, "VrittiAuthGuard");
392
+ }
393
+ reflector;
394
+ configService;
395
+ jwtService;
396
+ primaryDatabase;
397
+ requestService;
398
+ logger = new Logger2(_VrittiAuthGuard.name);
399
+ constructor(reflector, configService, jwtService, primaryDatabase, requestService) {
400
+ this.reflector = reflector;
401
+ this.configService = configService;
402
+ this.jwtService = jwtService;
403
+ this.primaryDatabase = primaryDatabase;
404
+ this.requestService = requestService;
405
+ }
406
+ async canActivate(context) {
407
+ const request = context.switchToHttp().getRequest();
408
+ const isPublic = this.reflector.getAllAndOverride("isPublic", [
409
+ context.getHandler(),
410
+ context.getClass()
411
+ ]);
412
+ if (isPublic) {
413
+ this.logger.debug("Public endpoint detected, skipping authentication");
414
+ return true;
415
+ }
416
+ const isOnboarding = this.reflector.getAllAndOverride("isOnboarding", [
417
+ context.getHandler(),
418
+ context.getClass()
419
+ ]);
420
+ try {
421
+ const accessToken = this.requestService.getAccessToken();
422
+ if (!accessToken) {
423
+ this.logger.warn("Access token not found in Authorization header");
424
+ throw new UnauthorizedException("Access token not found");
425
+ }
426
+ const decodedToken = this.jwtService.decode(accessToken);
427
+ if (!decodedToken) {
428
+ this.logger.warn("Failed to decode access token");
429
+ throw new UnauthorizedException("Invalid token format");
430
+ }
431
+ if (isOnboarding) {
432
+ if (decodedToken.type !== "onboarding") {
433
+ this.logger.warn("Onboarding endpoint requires onboarding token");
434
+ throw new UnauthorizedException("This endpoint requires an onboarding token");
435
+ }
436
+ const validatedToken2 = this.validateAccessToken(accessToken);
437
+ this.logger.debug("Onboarding token validated successfully");
438
+ const userId2 = validatedToken2.userId;
439
+ request.user = {
440
+ id: userId2
441
+ };
442
+ return true;
443
+ }
444
+ if (decodedToken.type === "onboarding") {
445
+ this.logger.warn("Regular endpoint accessed with onboarding token");
446
+ throw new UnauthorizedException("Onboarding tokens cannot access this endpoint");
447
+ }
448
+ const validatedToken = this.validateAccessToken(accessToken);
449
+ this.logger.debug("Access token validated successfully");
450
+ const refreshToken = this.requestService.getRefreshToken();
451
+ if (!refreshToken) {
452
+ this.logger.warn("Refresh token (session-id) not found in cookies");
453
+ throw new UnauthorizedException("Refresh token not found");
454
+ }
455
+ this.validateRefreshToken(refreshToken);
456
+ this.logger.debug("Refresh token validated successfully");
457
+ const userId = validatedToken.userId;
458
+ request.user = {
459
+ id: userId
460
+ };
461
+ const tenantIdentifier = this.requestService.getTenantIdentifier();
462
+ if (!tenantIdentifier) {
463
+ this.logger.warn("Tenant identifier not found in request");
464
+ throw new UnauthorizedException("Tenant identifier not found");
465
+ }
466
+ this.logger.debug(`Tenant identifier extracted: ${tenantIdentifier}`);
467
+ if (tenantIdentifier === "cloud") {
468
+ this.logger.debug("Platform admin access detected, skipping tenant database validation");
469
+ return true;
470
+ }
471
+ const tenantInfo = await this.primaryDatabase.getTenantInfo(tenantIdentifier);
472
+ if (!tenantInfo) {
473
+ this.logger.warn(`Invalid tenant: ${tenantIdentifier}`);
474
+ throw new UnauthorizedException("Invalid tenant");
475
+ }
476
+ if (tenantInfo.status !== "ACTIVE") {
477
+ this.logger.warn(`Tenant ${tenantIdentifier} has status: ${tenantInfo.status}`);
478
+ throw new UnauthorizedException(`Tenant is ${tenantInfo.status}`);
479
+ }
480
+ this.logger.debug(`Tenant validated: ${tenantInfo.subdomain} (${tenantInfo.type})`);
481
+ return true;
482
+ } catch (error) {
483
+ if (error instanceof UnauthorizedException) {
484
+ throw error;
485
+ }
486
+ this.logger.error("Unexpected error in auth guard", error);
487
+ throw new UnauthorizedException("Authentication failed");
488
+ }
489
+ }
490
+ /**
491
+ * Validate access token with proper expiry checks
492
+ * Throws UnauthorizedException if token is invalid or expired
493
+ */
494
+ validateAccessToken(token) {
495
+ try {
496
+ const decoded = this.jwtService.verify(token);
497
+ this.logger.debug(`Access token decoded for user: ${decoded.userId}`);
498
+ if (decoded.exp) {
499
+ const expiryTime = decoded.exp * 1e3;
500
+ const currentTime = Date.now();
501
+ const timeRemaining = expiryTime - currentTime;
502
+ this.logger.debug(`Access token valid for ${Math.floor(timeRemaining / 1e3)} more seconds`);
503
+ }
504
+ return decoded;
505
+ } catch (error) {
506
+ if (error instanceof UnauthorizedException) {
507
+ throw error;
508
+ }
509
+ const jwtError = error;
510
+ if (jwtError?.name === "TokenExpiredError") {
511
+ this.logger.warn(`Access token expired at: ${jwtError?.expiredAt}`);
512
+ throw new UnauthorizedException("Access token has expired");
513
+ }
514
+ if (jwtError?.name === "JsonWebTokenError") {
515
+ this.logger.warn(`Access token verification failed: ${jwtError?.message}`);
516
+ throw new UnauthorizedException("Invalid access token");
517
+ }
518
+ if (jwtError?.name === "NotBeforeError") {
519
+ this.logger.warn("Access token used before valid (nbf claim)");
520
+ throw new UnauthorizedException("Access token not yet valid");
521
+ }
522
+ this.logger.error("Unexpected error validating access token", error);
523
+ throw new UnauthorizedException("Access token validation failed");
524
+ }
525
+ }
526
+ /**
527
+ * Validate refresh token with proper expiry checks
528
+ * Throws UnauthorizedException if token is invalid or expired
529
+ */
530
+ validateRefreshToken(token) {
531
+ const jwtSecret = this.configService.get("JWT_REFRESH_SECRET") || this.configService.get("JWT_SECRET");
532
+ this.validateRefreshTokenWithSecret(token, jwtSecret);
533
+ }
534
+ /**
535
+ * Helper to validate refresh token with specific secret
536
+ */
537
+ validateRefreshTokenWithSecret(token, secret) {
538
+ if (!secret) {
539
+ this.logger.error("JWT secret not configured for refresh token validation");
540
+ throw new UnauthorizedException("Server configuration error");
541
+ }
542
+ try {
543
+ const decoded = jwt.verify(token, secret, {
544
+ algorithms: [
545
+ "HS256",
546
+ "HS512",
547
+ "RS256"
548
+ ]
549
+ });
550
+ this.logger.debug(`Refresh token decoded for user: ${decoded.userId}`);
551
+ if (decoded.exp) {
552
+ const expiryTime = decoded.exp * 1e3;
553
+ const currentTime = Date.now();
554
+ if (currentTime > expiryTime) {
555
+ this.logger.warn("Refresh token has expired");
556
+ throw new UnauthorizedException("Refresh token has expired. Please login again");
557
+ }
558
+ const timeRemaining = expiryTime - currentTime;
559
+ this.logger.debug(`Refresh token valid for ${Math.floor(timeRemaining / 1e3)} more seconds`);
560
+ }
561
+ } catch (error) {
562
+ if (error instanceof UnauthorizedException) {
563
+ throw error;
564
+ }
565
+ const jwtError = error;
566
+ if (jwtError?.name === "TokenExpiredError") {
567
+ this.logger.warn(`Refresh token expired at: ${jwtError?.expiredAt}`);
568
+ throw new UnauthorizedException("Refresh token has expired. Please login again");
569
+ }
570
+ if (jwtError?.name === "JsonWebTokenError") {
571
+ this.logger.warn(`Refresh token verification failed: ${jwtError?.message}`);
572
+ throw new UnauthorizedException("Invalid refresh token");
573
+ }
574
+ if (jwtError?.name === "NotBeforeError") {
575
+ this.logger.warn("Refresh token used before valid (nbf claim)");
576
+ throw new UnauthorizedException("Refresh token not yet valid");
577
+ }
578
+ this.logger.error("Unexpected error validating refresh token", error);
579
+ throw new UnauthorizedException("Refresh token validation failed");
580
+ }
581
+ }
582
+ };
583
+ VrittiAuthGuard = _ts_decorate4([
584
+ Injectable3({
585
+ scope: Scope2.REQUEST
586
+ }),
587
+ _ts_metadata3("design:type", Function),
588
+ _ts_metadata3("design:paramtypes", [
589
+ typeof Reflector === "undefined" ? Object : Reflector,
590
+ typeof ConfigService === "undefined" ? Object : ConfigService,
591
+ typeof JwtService === "undefined" ? Object : JwtService,
592
+ typeof PrimaryDatabaseService === "undefined" ? Object : PrimaryDatabaseService,
593
+ typeof RequestService === "undefined" ? Object : RequestService
594
+ ])
595
+ ], VrittiAuthGuard);
596
+
597
+ // src/auth/auth-config.module.ts
598
+ function _ts_decorate5(decorators, target, key, desc) {
599
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
600
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
601
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
602
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
603
+ }
604
+ __name(_ts_decorate5, "_ts_decorate");
605
+ var AuthConfigModule = class _AuthConfigModule {
606
+ static {
607
+ __name(this, "AuthConfigModule");
608
+ }
609
+ /**
610
+ * Register the auth module with async configuration
611
+ *
612
+ * This method:
613
+ * 1. Configures JwtModule with JWT_SECRET from ConfigService
614
+ * 2. Provides VrittiAuthGuard globally (applies to all routes)
615
+ * 3. Exports JwtModule for use in other modules (e.g., for signing tokens)
616
+ *
617
+ * @returns Dynamic module configuration
618
+ */
619
+ static forRootAsync() {
620
+ return {
621
+ module: _AuthConfigModule,
622
+ imports: [
623
+ ConfigModule,
624
+ RequestModule,
625
+ JwtModule.registerAsync({
626
+ imports: [
627
+ ConfigModule
628
+ ],
629
+ inject: [
630
+ ConfigService2
631
+ ],
632
+ useFactory: /* @__PURE__ */ __name((config) => ({
633
+ secret: config.get("JWT_SECRET"),
634
+ signOptions: {
635
+ algorithm: "HS256"
636
+ }
637
+ }), "useFactory")
638
+ })
639
+ ],
640
+ providers: [
641
+ {
642
+ provide: APP_GUARD,
643
+ useClass: VrittiAuthGuard
644
+ }
645
+ ],
646
+ exports: [
647
+ JwtModule
648
+ ]
649
+ };
650
+ }
651
+ };
652
+ AuthConfigModule = _ts_decorate5([
653
+ Global2(),
654
+ Module2({})
655
+ ], AuthConfigModule);
656
+
657
+ // src/database/database.module.ts
658
+ import { Global as Global3, Module as Module3 } from "@nestjs/common";
659
+ import { APP_INTERCEPTOR } from "@nestjs/core";
660
+
661
+ // src/database/interceptors/message-tenant-context.interceptor.ts
662
+ import { Injectable as Injectable5, Logger as Logger3, Scope as Scope4 } from "@nestjs/common";
663
+ import { tap } from "rxjs/operators";
664
+
665
+ // src/database/services/tenant-context.service.ts
666
+ import { Injectable as Injectable4, Scope as Scope3, UnauthorizedException as UnauthorizedException2 } from "@nestjs/common";
667
+ function _ts_decorate6(decorators, target, key, desc) {
668
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
669
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
670
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
671
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
672
+ }
673
+ __name(_ts_decorate6, "_ts_decorate");
674
+ var TenantContextService = class {
675
+ static {
676
+ __name(this, "TenantContextService");
677
+ }
678
+ tenantInfo = null;
679
+ /**
680
+ * Set tenant information for this request/message
681
+ *
682
+ * This is typically called by:
683
+ * - TenantContextInterceptor (for HTTP requests in gateway)
684
+ * - MessageTenantContextInterceptor (for RabbitMQ messages in microservices)
685
+ * - Manual context setup in message handlers
686
+ *
687
+ * @param tenantInfo Complete tenant information
688
+ * @throws Error if tenant context is already set (prevents accidental overwrites)
689
+ */
690
+ setTenant(tenantInfo) {
691
+ if (this.tenantInfo) {
692
+ throw new Error("Tenant context already set for this request");
693
+ }
694
+ this.tenantInfo = tenantInfo;
695
+ }
696
+ /**
697
+ * Get tenant information for this request/message
698
+ *
699
+ * @returns Tenant information
700
+ * @throws UnauthorizedException if tenant context hasn't been set
701
+ */
702
+ getTenant() {
703
+ if (!this.tenantInfo) {
704
+ throw new UnauthorizedException2("Tenant context not set");
705
+ }
706
+ return this.tenantInfo;
707
+ }
708
+ /**
709
+ * Check if tenant context has been set
710
+ *
711
+ * @returns true if tenant context is available
712
+ */
713
+ hasTenant() {
714
+ return this.tenantInfo !== null;
715
+ }
716
+ /**
717
+ * Clear tenant context
718
+ *
719
+ * This is useful for cleanup in RabbitMQ message handlers
720
+ * after the message has been processed.
721
+ *
722
+ * HTTP requests don't need manual cleanup as the service
723
+ * instance is destroyed when the request ends.
724
+ */
725
+ clearTenant() {
726
+ this.tenantInfo = null;
727
+ }
728
+ /**
729
+ * Get tenant ID safely (returns null if not set)
730
+ *
731
+ * @returns Tenant ID or null
732
+ */
733
+ getTenantIdSafe() {
734
+ return this.tenantInfo?.id ?? null;
735
+ }
736
+ /**
737
+ * Get tenant subdomain safely (returns null if not set)
738
+ *
739
+ * @returns Tenant subdomain or null
740
+ */
741
+ getTenantSubdomainSafe() {
742
+ return this.tenantInfo?.subdomain ?? null;
743
+ }
744
+ };
745
+ TenantContextService = _ts_decorate6([
746
+ Injectable4({
747
+ scope: Scope3.REQUEST
748
+ })
749
+ ], TenantContextService);
750
+
751
+ // src/database/interceptors/message-tenant-context.interceptor.ts
752
+ function _ts_decorate7(decorators, target, key, desc) {
753
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
754
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
755
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
756
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
757
+ }
758
+ __name(_ts_decorate7, "_ts_decorate");
759
+ function _ts_metadata4(k, v) {
760
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
761
+ }
762
+ __name(_ts_metadata4, "_ts_metadata");
763
+ var MessageTenantContextInterceptor = class _MessageTenantContextInterceptor {
764
+ static {
765
+ __name(this, "MessageTenantContextInterceptor");
766
+ }
767
+ tenantContext;
768
+ logger = new Logger3(_MessageTenantContextInterceptor.name);
769
+ constructor(tenantContext) {
770
+ this.tenantContext = tenantContext;
771
+ }
772
+ intercept(context, next) {
773
+ const contextType = context.getType();
774
+ if (contextType === "rpc") {
775
+ const rpcContext = context.switchToRpc();
776
+ const payload = rpcContext.getData();
777
+ if (payload && payload.tenant) {
778
+ const tenant = payload.tenant;
779
+ this.logger.debug(`Setting tenant context from message: ${tenant.subdomain}`);
780
+ try {
781
+ this.tenantContext.setTenant(tenant);
782
+ this.logger.log(`Tenant context set: ${tenant.subdomain} (${tenant.type})`);
783
+ } catch (error) {
784
+ this.logger.error("Failed to set tenant context from message", error);
785
+ }
786
+ } else {
787
+ this.logger.warn("Message payload missing tenant information");
788
+ }
789
+ }
790
+ return next.handle().pipe(tap({
791
+ next: /* @__PURE__ */ __name(() => {
792
+ this.cleanupContext();
793
+ }, "next"),
794
+ error: /* @__PURE__ */ __name(() => {
795
+ this.cleanupContext();
796
+ }, "error"),
797
+ complete: /* @__PURE__ */ __name(() => {
798
+ this.cleanupContext();
799
+ }, "complete")
800
+ }));
801
+ }
802
+ /**
803
+ * Clean up tenant context after message is processed
804
+ */
805
+ cleanupContext() {
806
+ if (this.tenantContext.hasTenant()) {
807
+ const tenant = this.tenantContext.getTenantIdSafe();
808
+ this.tenantContext.clearTenant();
809
+ this.logger.debug(`Cleaned up tenant context: ${tenant}`);
810
+ }
811
+ }
812
+ };
813
+ MessageTenantContextInterceptor = _ts_decorate7([
814
+ Injectable5({
815
+ scope: Scope4.REQUEST
816
+ }),
817
+ _ts_metadata4("design:type", Function),
818
+ _ts_metadata4("design:paramtypes", [
819
+ typeof TenantContextService === "undefined" ? Object : TenantContextService
820
+ ])
821
+ ], MessageTenantContextInterceptor);
822
+
823
+ // src/database/interceptors/tenant-context.interceptor.ts
824
+ import { Injectable as Injectable6, Logger as Logger4, Scope as Scope5, UnauthorizedException as UnauthorizedException3 } from "@nestjs/common";
825
+ function _ts_decorate8(decorators, target, key, desc) {
826
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
827
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
828
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
829
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
830
+ }
831
+ __name(_ts_decorate8, "_ts_decorate");
832
+ function _ts_metadata5(k, v) {
833
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
834
+ }
835
+ __name(_ts_metadata5, "_ts_metadata");
836
+ var TenantContextInterceptor = class _TenantContextInterceptor {
837
+ static {
838
+ __name(this, "TenantContextInterceptor");
839
+ }
840
+ tenantContext;
841
+ primaryDatabase;
842
+ requestService;
843
+ logger = new Logger4(_TenantContextInterceptor.name);
844
+ constructor(tenantContext, primaryDatabase, requestService) {
845
+ this.tenantContext = tenantContext;
846
+ this.primaryDatabase = primaryDatabase;
847
+ this.requestService = requestService;
848
+ }
849
+ async intercept(context, next) {
850
+ const request = context.switchToHttp().getRequest();
851
+ this.logger.debug(`Processing request: ${request.method} ${request.url}`);
852
+ try {
853
+ const tenantIdentifier = this.requestService.getTenantIdentifier();
854
+ if (!tenantIdentifier) {
855
+ throw new UnauthorizedException3("Tenant identifier not found in request");
856
+ }
857
+ this.logger.debug(`Tenant identifier extracted: ${tenantIdentifier}`);
858
+ if (tenantIdentifier === "cloud") {
859
+ this.logger.log("Cloud platform access detected, skipping tenant context setup");
860
+ return next.handle();
861
+ }
862
+ const tenantInfo = await this.primaryDatabase.getTenantInfo(tenantIdentifier);
863
+ if (!tenantInfo) {
864
+ this.logger.warn(`Invalid tenant: ${tenantIdentifier}`);
865
+ throw new UnauthorizedException3("Invalid tenant");
866
+ }
867
+ if (tenantInfo.status !== "ACTIVE") {
868
+ this.logger.warn(`Tenant ${tenantIdentifier} has status: ${tenantInfo.status}`);
869
+ throw new UnauthorizedException3(`Tenant is ${tenantInfo.status}`);
870
+ }
871
+ this.logger.debug(`Tenant config loaded: ${tenantInfo.subdomain} (${tenantInfo.type})`);
872
+ this.tenantContext.setTenant(tenantInfo);
873
+ request.tenant = tenantInfo;
874
+ this.logger.log(`Tenant context set: ${tenantInfo.subdomain}`);
875
+ } catch (error) {
876
+ this.logger.error("Failed to set tenant context", error);
877
+ throw error;
878
+ }
879
+ return next.handle();
880
+ }
881
+ };
882
+ TenantContextInterceptor = _ts_decorate8([
883
+ Injectable6({
884
+ scope: Scope5.REQUEST
885
+ }),
886
+ _ts_metadata5("design:type", Function),
887
+ _ts_metadata5("design:paramtypes", [
888
+ typeof TenantContextService === "undefined" ? Object : TenantContextService,
889
+ typeof PrimaryDatabaseService === "undefined" ? Object : PrimaryDatabaseService,
890
+ typeof RequestService === "undefined" ? Object : RequestService
891
+ ])
892
+ ], TenantContextInterceptor);
893
+
894
+ // src/database/services/tenant-database.service.ts
895
+ import { Inject as Inject3, Injectable as Injectable7, InternalServerErrorException as InternalServerErrorException2, Logger as Logger5 } from "@nestjs/common";
896
+ function _ts_decorate9(decorators, target, key, desc) {
897
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
898
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
899
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
900
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
901
+ }
902
+ __name(_ts_decorate9, "_ts_decorate");
903
+ function _ts_metadata6(k, v) {
904
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
905
+ }
906
+ __name(_ts_metadata6, "_ts_metadata");
907
+ function _ts_param3(paramIndex, decorator) {
908
+ return function(target, key) {
909
+ decorator(target, key, paramIndex);
910
+ };
911
+ }
912
+ __name(_ts_param3, "_ts_param");
913
+ var TenantDatabaseService = class _TenantDatabaseService {
914
+ static {
915
+ __name(this, "TenantDatabaseService");
916
+ }
917
+ options;
918
+ tenantContext;
919
+ logger = new Logger5(_TenantDatabaseService.name);
920
+ /** Connection pool: Map<cacheKey, DbClient> */
921
+ clients = /* @__PURE__ */ new Map();
922
+ /** Track last usage time for idle connection cleanup */
923
+ clientLastUsed = /* @__PURE__ */ new Map();
924
+ /** Cleanup interval timer */
925
+ cleanupInterval;
926
+ constructor(options, tenantContext) {
927
+ this.options = options;
928
+ this.tenantContext = tenantContext;
929
+ this.startConnectionCleaner();
930
+ }
931
+ /**
932
+ * Get tenant-scoped database client for the current request/message
933
+ *
934
+ * This method:
935
+ * 1. Gets tenant info from TenantContextService
936
+ * 2. Builds a connection URL based on tenant type
937
+ * 3. Returns cached client if exists, otherwise creates new one
938
+ *
939
+ * @returns Promise<Database client instance>
940
+ * @throws UnauthorizedException if tenant context not set
941
+ * @throws InternalServerErrorException if connection fails
942
+ *
943
+ * @example
944
+ * const dbClient = await tenantDatabase.getDbClient<PrismaClient>();
945
+ * const users = await dbClient.user.findMany();
946
+ */
947
+ async getDbClient() {
948
+ const tenant = this.tenantContext.getTenant();
949
+ const cacheKey = this.buildCacheKey(tenant);
950
+ if (this.clients.has(cacheKey)) {
951
+ this.clientLastUsed.set(cacheKey, Date.now());
952
+ this.logger.debug(`Reusing cached connection: ${cacheKey}`);
953
+ return this.clients.get(cacheKey);
954
+ }
955
+ this.logger.log(`Creating new database connection: ${cacheKey}`);
956
+ const client = await this.createDbClient(tenant);
957
+ this.clients.set(cacheKey, client);
958
+ this.clientLastUsed.set(cacheKey, Date.now());
959
+ return client;
960
+ }
961
+ /**
962
+ * Create a new database client for the given tenant
963
+ */
964
+ async createDbClient(tenant) {
965
+ try {
966
+ const databaseUrl = this.buildTenantDbUrl(tenant);
967
+ const PrismaClient = await this.options.prismaClientConstructor;
968
+ const client = new PrismaClient({
969
+ datasources: {
970
+ db: {
971
+ url: databaseUrl
972
+ }
973
+ },
974
+ log: [
975
+ "error",
976
+ "warn"
977
+ ]
978
+ });
979
+ await client.$connect();
980
+ this.logger.log(`Connected to database for tenant: ${tenant.subdomain}`);
981
+ return client;
982
+ } catch (error) {
983
+ this.logger.error(`Failed to create database connection for tenant: ${tenant.subdomain}`, error);
984
+ throw new InternalServerErrorException2("Failed to connect to tenant database");
985
+ }
986
+ }
987
+ /**
988
+ * Build connection URL for enterprise tenant (dedicated database)
989
+ */
990
+ buildTenantDbUrl(tenant) {
991
+ const { databaseHost, databasePort, databaseName, databaseUsername, databasePassword, databaseSslMode } = tenant;
992
+ if (!databaseHost || !databaseName || !databaseUsername) {
993
+ throw new Error(`Enterprise tenant ${tenant.subdomain} missing database configuration`);
994
+ }
995
+ const port = databasePort || 5432;
996
+ const sslMode = databaseSslMode || "require";
997
+ const connectionUrl = `postgresql://${databaseUsername}:${databasePassword}@${databaseHost}:${port}/${databaseName}?sslmode=${sslMode}`;
998
+ this.logger.debug(`Enterprise connection URL: ${this.maskPassword(connectionUrl)}`);
999
+ return connectionUrl;
1000
+ }
1001
+ /**
1002
+ * Build cache key for connection pooling
1003
+ */
1004
+ buildCacheKey(tenant) {
1005
+ return `${tenant.type}:${tenant.databaseName}@${tenant.databaseHost}`;
1006
+ }
1007
+ /**
1008
+ * Start periodic cleanup of idle connections
1009
+ */
1010
+ startConnectionCleaner() {
1011
+ const interval = this.options.connectionCacheTTL || 3e5;
1012
+ this.cleanupInterval = setInterval(() => {
1013
+ this.cleanupIdleConnections();
1014
+ }, interval);
1015
+ this.logger.log(`Connection cleanup scheduled every ${interval / 1e3} seconds`);
1016
+ }
1017
+ /**
1018
+ * Clean up idle connections that haven't been used recently
1019
+ */
1020
+ cleanupIdleConnections() {
1021
+ const now = Date.now();
1022
+ const maxIdle = this.options.connectionCacheTTL || 3e5;
1023
+ let cleaned = 0;
1024
+ for (const [key, lastUsed] of this.clientLastUsed.entries()) {
1025
+ if (now - lastUsed > maxIdle) {
1026
+ const client = this.clients.get(key);
1027
+ if (client) {
1028
+ client.$disconnect().then(() => {
1029
+ this.logger.debug(`Cleaned up idle connection: ${key}`);
1030
+ }).catch((error) => {
1031
+ this.logger.error(`Error disconnecting idle client: ${key}`, error);
1032
+ });
1033
+ this.clients.delete(key);
1034
+ this.clientLastUsed.delete(key);
1035
+ cleaned++;
1036
+ }
1037
+ }
1038
+ }
1039
+ if (cleaned > 0) {
1040
+ this.logger.log(`Cleaned up ${cleaned} idle connections`);
1041
+ }
1042
+ }
1043
+ /**
1044
+ * Get current connection pool statistics
1045
+ */
1046
+ getPoolStats() {
1047
+ return {
1048
+ activeConnections: this.clients.size,
1049
+ tenants: Array.from(this.clients.keys())
1050
+ };
1051
+ }
1052
+ /**
1053
+ * Mask password in connection URL for logging
1054
+ */
1055
+ maskPassword(url) {
1056
+ return url.replace(/:([^@]+)@/, ":****@");
1057
+ }
1058
+ async onModuleDestroy() {
1059
+ if (this.cleanupInterval) {
1060
+ clearInterval(this.cleanupInterval);
1061
+ }
1062
+ this.logger.log(`Disconnecting ${this.clients.size} database connections`);
1063
+ const disconnectPromises = Array.from(this.clients.entries()).map(async ([key, client]) => {
1064
+ try {
1065
+ await client.$disconnect();
1066
+ this.logger.debug(`Disconnected: ${key}`);
1067
+ } catch (error) {
1068
+ this.logger.error(`Error disconnecting client: ${key}`, error);
1069
+ }
1070
+ });
1071
+ await Promise.all(disconnectPromises);
1072
+ this.logger.log("All database connections closed");
1073
+ }
1074
+ };
1075
+ TenantDatabaseService = _ts_decorate9([
1076
+ Injectable7(),
1077
+ _ts_param3(0, Inject3(DATABASE_MODULE_OPTIONS)),
1078
+ _ts_metadata6("design:type", Function),
1079
+ _ts_metadata6("design:paramtypes", [
1080
+ typeof DatabaseModuleOptions === "undefined" ? Object : DatabaseModuleOptions,
1081
+ typeof TenantContextService === "undefined" ? Object : TenantContextService
1082
+ ])
1083
+ ], TenantDatabaseService);
1084
+
1085
+ // src/database/database.module.ts
1086
+ function _ts_decorate10(decorators, target, key, desc) {
1087
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1088
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1089
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1090
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
1091
+ }
1092
+ __name(_ts_decorate10, "_ts_decorate");
1093
+ var DatabaseModule = class _DatabaseModule {
1094
+ static {
1095
+ __name(this, "DatabaseModule");
1096
+ }
1097
+ /**
1098
+ * Configure DatabaseModule for Gateway/HTTP mode (multi-tenant web servers)
1099
+ *
1100
+ * This mode is for API Gateways that handle HTTP requests:
1101
+ * - Automatically registers TenantContextInterceptor
1102
+ * - Extracts tenant from subdomain or x-tenant-id header
1103
+ * - Queries primary database for tenant configuration
1104
+ * - Provides PrimaryDatabaseService for tenant lookup
1105
+ *
1106
+ * @param options Async configuration options
1107
+ * @returns Dynamic module configuration with HTTP interceptor
1108
+ *
1109
+ * @example
1110
+ * DatabaseModule.forServer({
1111
+ * inject: [ConfigService],
1112
+ * useFactory: (config: ConfigService) => ({
1113
+ * primaryDb: {
1114
+ * host: config.get('PRIMARY_DB_HOST'),
1115
+ * port: config.get('PRIMARY_DB_PORT'),
1116
+ * username: config.get('PRIMARY_DB_USERNAME'),
1117
+ * password: config.get('PRIMARY_DB_PASSWORD'),
1118
+ * database: config.get('PRIMARY_DB_DATABASE'),
1119
+ * },
1120
+ * prismaClientConstructor: PrismaClient,
1121
+ * }),
1122
+ * })
1123
+ */
1124
+ static forServer(options) {
1125
+ return this.createDynamicModule(options, "gateway");
1126
+ }
1127
+ /**
1128
+ * Configure DatabaseModule for Microservice/Messaging mode (RabbitMQ workers)
1129
+ *
1130
+ * This mode is for microservices that process messages from queues:
1131
+ * - Automatically registers MessageTenantContextInterceptor
1132
+ * - Extracts tenant from RabbitMQ message patterns
1133
+ * - No primary database needed (tenant comes from message context)
1134
+ *
1135
+ * @param options Async configuration options
1136
+ * @returns Dynamic module configuration with message interceptor
1137
+ *
1138
+ * @example
1139
+ * DatabaseModule.forMicroservice({
1140
+ * inject: [ConfigService],
1141
+ * useFactory: (config: ConfigService) => ({
1142
+ * prismaClientConstructor: PrismaClient,
1143
+ * }),
1144
+ * })
1145
+ */
1146
+ static forMicroservice(options) {
1147
+ return this.createDynamicModule(options, "microservice");
1148
+ }
1149
+ /**
1150
+ * Internal helper to create dynamic module with conditional interceptor registration
1151
+ *
1152
+ * @param options Configuration options
1153
+ * @param mode Mode of operation (gateway or microservice)
1154
+ * @returns Dynamic module configuration
1155
+ */
1156
+ static createDynamicModule(options, mode) {
1157
+ const asyncProvider = {
1158
+ provide: DATABASE_MODULE_OPTIONS,
1159
+ useFactory: options.useFactory,
1160
+ inject: options.inject || []
1161
+ };
1162
+ const providers = [
1163
+ asyncProvider,
1164
+ TenantContextService,
1165
+ PrimaryDatabaseService,
1166
+ TenantDatabaseService
1167
+ ];
1168
+ if (mode === "gateway") {
1169
+ providers.push({
1170
+ provide: APP_INTERCEPTOR,
1171
+ useClass: TenantContextInterceptor
1172
+ });
1173
+ } else {
1174
+ providers.push({
1175
+ provide: APP_INTERCEPTOR,
1176
+ useClass: MessageTenantContextInterceptor
1177
+ });
1178
+ }
1179
+ return {
1180
+ module: _DatabaseModule,
1181
+ imports: [
1182
+ RequestModule
1183
+ ],
1184
+ providers,
1185
+ exports: [
1186
+ TenantDatabaseService,
1187
+ TenantContextService,
1188
+ PrimaryDatabaseService,
1189
+ asyncProvider
1190
+ ]
1191
+ };
1192
+ }
1193
+ };
1194
+ DatabaseModule = _ts_decorate10([
1195
+ Global3(),
1196
+ Module3({})
1197
+ ], DatabaseModule);
1198
+
1199
+ // src/database/decorators/tenant.decorator.ts
1200
+ import { createParamDecorator } from "@nestjs/common";
1201
+ var Tenant = createParamDecorator((data, ctx) => {
1202
+ const request = ctx.switchToHttp().getRequest();
1203
+ const tenantContext = request.app?.get?.(TenantContextService);
1204
+ if (!tenantContext) {
1205
+ throw new Error("TenantContextService not found.");
1206
+ }
1207
+ return tenantContext.getTenant();
1208
+ });
1209
+
1210
+ // src/auth/decorators/onboarding.decorator.ts
1211
+ import { SetMetadata } from "@nestjs/common";
1212
+ var Onboarding = /* @__PURE__ */ __name(() => SetMetadata("isOnboarding", true), "Onboarding");
1213
+
1214
+ // src/auth/decorators/public.decorator.ts
1215
+ import { SetMetadata as SetMetadata2 } from "@nestjs/common";
1216
+ var Public = /* @__PURE__ */ __name(() => SetMetadata2("isPublic", true), "Public");
8
1217
  export {
9
- getHello
1218
+ AuthConfigModule,
1219
+ DatabaseModule,
1220
+ Onboarding,
1221
+ PrimaryDatabaseService,
1222
+ Public,
1223
+ Tenant,
1224
+ TenantContextService,
1225
+ TenantDatabaseService,
1226
+ VrittiAuthGuard
10
1227
  };
11
1228
  //# sourceMappingURL=index.js.map