@vritti/api-sdk 0.1.1 → 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.js CHANGED
@@ -13,6 +13,82 @@ import { Global, Module } from "@nestjs/common";
13
13
  // src/request/services/request.service.ts
14
14
  import { Inject, Injectable, Scope } from "@nestjs/common";
15
15
  import { REQUEST } from "@nestjs/core";
16
+
17
+ // src/config/index.ts
18
+ var defaultConfig = {
19
+ cookie: {
20
+ refreshCookieName: "vritti_refresh",
21
+ refreshCookieMaxAge: 30 * 24 * 60 * 60 * 1e3,
22
+ refreshCookiePath: "/",
23
+ refreshCookieSecure: process.env.NODE_ENV === "production",
24
+ refreshCookieSameSite: "strict"
25
+ },
26
+ jwt: {
27
+ accessTokenExpiry: "15m",
28
+ refreshTokenExpiry: "30d",
29
+ onboardingTokenExpiry: "24h",
30
+ validateTokenBinding: true
31
+ },
32
+ guard: {
33
+ tenantHeaderName: "x-tenant-id",
34
+ authHeaderName: "authorization",
35
+ tokenPrefix: "Bearer"
36
+ }
37
+ };
38
+ var currentConfig = {
39
+ ...defaultConfig
40
+ };
41
+ function defineConfig(config) {
42
+ return config;
43
+ }
44
+ __name(defineConfig, "defineConfig");
45
+ function configureApiSdk(userConfig) {
46
+ currentConfig = {
47
+ cookie: {
48
+ ...defaultConfig.cookie,
49
+ ...userConfig.cookie || {}
50
+ },
51
+ jwt: {
52
+ ...defaultConfig.jwt,
53
+ ...userConfig.jwt || {}
54
+ },
55
+ guard: {
56
+ ...defaultConfig.guard,
57
+ ...userConfig.guard || {}
58
+ }
59
+ };
60
+ }
61
+ __name(configureApiSdk, "configureApiSdk");
62
+ function getConfig() {
63
+ return currentConfig;
64
+ }
65
+ __name(getConfig, "getConfig");
66
+ function resetConfig() {
67
+ currentConfig = {
68
+ ...defaultConfig
69
+ };
70
+ }
71
+ __name(resetConfig, "resetConfig");
72
+ function getRefreshCookieOptions() {
73
+ return {
74
+ httpOnly: true,
75
+ secure: currentConfig.cookie.refreshCookieSecure,
76
+ sameSite: currentConfig.cookie.refreshCookieSameSite,
77
+ path: currentConfig.cookie.refreshCookiePath,
78
+ maxAge: currentConfig.cookie.refreshCookieMaxAge
79
+ };
80
+ }
81
+ __name(getRefreshCookieOptions, "getRefreshCookieOptions");
82
+ function getJwtExpiry() {
83
+ return {
84
+ access: currentConfig.jwt.accessTokenExpiry,
85
+ refresh: currentConfig.jwt.refreshTokenExpiry,
86
+ onboarding: currentConfig.jwt.onboardingTokenExpiry
87
+ };
88
+ }
89
+ __name(getJwtExpiry, "getJwtExpiry");
90
+
91
+ // src/request/services/request.service.ts
16
92
  function _ts_decorate(decorators, target, key, desc) {
17
93
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
18
94
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -30,7 +106,7 @@ function _ts_param(paramIndex, decorator) {
30
106
  };
31
107
  }
32
108
  __name(_ts_param, "_ts_param");
33
- var RequestService = class {
109
+ var RequestService2 = class {
34
110
  static {
35
111
  __name(this, "RequestService");
36
112
  }
@@ -64,21 +140,22 @@ var RequestService = class {
64
140
  return type === "Bearer" && token ? token : null;
65
141
  }
66
142
  /**
67
- * Extract refresh token from session-id cookie
68
- * Cookie name: session-id
143
+ * Extract refresh token from httpOnly cookie
144
+ * Cookie name is configurable via api-sdk config
69
145
  * @returns Refresh token or null if not found
70
146
  */
71
147
  getRefreshToken() {
72
148
  try {
73
149
  const cookies = this.request.cookies;
74
150
  if (cookies && typeof cookies === "object") {
75
- const sessionId = cookies["session-id"];
76
- if (sessionId) {
77
- return sessionId;
151
+ const config = getConfig();
152
+ const refreshToken = cookies[config.cookie.refreshCookieName];
153
+ if (refreshToken) {
154
+ return refreshToken;
78
155
  }
79
156
  }
80
157
  return null;
81
- } catch (error) {
158
+ } catch (_error) {
82
159
  return null;
83
160
  }
84
161
  }
@@ -98,7 +175,7 @@ var RequestService = class {
98
175
  return this.request.headers || {};
99
176
  }
100
177
  };
101
- RequestService = _ts_decorate([
178
+ RequestService2 = _ts_decorate([
102
179
  Injectable({
103
180
  scope: Scope.REQUEST
104
181
  }),
@@ -107,7 +184,7 @@ RequestService = _ts_decorate([
107
184
  _ts_metadata("design:paramtypes", [
108
185
  typeof FastifyRequest === "undefined" ? Object : FastifyRequest
109
186
  ])
110
- ], RequestService);
187
+ ], RequestService2);
111
188
 
112
189
  // src/request/request.module.ts
113
190
  function _ts_decorate2(decorators, target, key, desc) {
@@ -126,31 +203,31 @@ RequestModule = _ts_decorate2([
126
203
  Global(),
127
204
  Module({
128
205
  providers: [
129
- RequestService
206
+ RequestService2
130
207
  ],
131
208
  exports: [
132
- RequestService
209
+ RequestService2
133
210
  ]
134
211
  })
135
212
  ], RequestModule);
136
213
 
137
214
  // 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
- import { Pool } from "pg";
147
- import { drizzle } from "drizzle-orm/node-postgres";
148
- import { eq, or } from "drizzle-orm";
215
+ import { Injectable as Injectable2, Logger as Logger2, Scope as Scope2, UnauthorizedException } from "@nestjs/common";
149
216
 
150
- // src/database/constants.ts
151
- var DATABASE_MODULE_OPTIONS = Symbol("DATABASE_MODULE_OPTIONS");
217
+ // src/auth/utils/token-hash.util.ts
218
+ import * as crypto from "crypto";
219
+ function hashToken(token) {
220
+ return crypto.createHash("sha256").update(token).digest("hex");
221
+ }
222
+ __name(hashToken, "hashToken");
223
+ function verifyTokenHash(token, expectedHash) {
224
+ const computedHash = hashToken(token);
225
+ if (computedHash.length !== expectedHash.length) return false;
226
+ return crypto.timingSafeEqual(Buffer.from(computedHash, "hex"), Buffer.from(expectedHash, "hex"));
227
+ }
228
+ __name(verifyTokenHash, "verifyTokenHash");
152
229
 
153
- // src/database/services/primary-database.service.ts
230
+ // src/auth/guards/vritti-auth.guard.ts
154
231
  function _ts_decorate3(decorators, target, key, desc) {
155
232
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
156
233
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
@@ -162,247 +239,19 @@ function _ts_metadata2(k, v) {
162
239
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
163
240
  }
164
241
  __name(_ts_metadata2, "_ts_metadata");
165
- function _ts_param2(paramIndex, decorator) {
166
- return function(target, key) {
167
- decorator(target, key, paramIndex);
168
- };
169
- }
170
- __name(_ts_param2, "_ts_param");
171
- var PrimaryDatabaseService = class _PrimaryDatabaseService {
172
- static {
173
- __name(this, "PrimaryDatabaseService");
174
- }
175
- options;
176
- logger = new Logger(_PrimaryDatabaseService.name);
177
- /** PostgreSQL connection pool */
178
- pool = null;
179
- /** Drizzle database instance */
180
- db = null;
181
- /** In-memory cache: Map<tenantIdentifier, TenantConfig> */
182
- tenantConfigCache = /* @__PURE__ */ new Map();
183
- /** Cache TTL in milliseconds */
184
- cacheTTL;
185
- constructor(options) {
186
- this.options = options;
187
- this.cacheTTL = options.connectionCacheTTL || 3e5;
188
- }
189
- async onModuleInit() {
190
- if (this.options.primaryDb) {
191
- await this.initializeDrizzleClient();
192
- }
193
- }
194
- /**
195
- * Initialize connection to primary database using Drizzle
196
- */
197
- async initializeDrizzleClient() {
198
- try {
199
- const databaseUrl = this.buildPrimaryDbUrl();
200
- this.pool = new Pool({
201
- connectionString: databaseUrl,
202
- max: this.options.maxConnections || 10
203
- });
204
- this.logger.debug(`Schema keys passed to drizzle: [${Object.keys(this.options.drizzleSchema || {}).join(", ")}]`);
205
- this.logger.debug(`Relations keys passed to drizzle: [${Object.keys(this.options.drizzleRelations || {}).join(", ")}]`);
206
- this.db = drizzle({
207
- client: this.pool,
208
- schema: this.options.drizzleSchema,
209
- relations: this.options.drizzleRelations
210
- });
211
- this.logger.debug(`Drizzle query keys after init: [${Object.keys(this.db.query || {}).join(", ")}]`);
212
- await this.pool.query("SELECT 1");
213
- this.logger.log("Connected to primary database (tenant registry)");
214
- } catch (error) {
215
- this.logger.error("Failed to connect to primary database", error);
216
- throw new InternalServerErrorException("Failed to initialize tenant registry");
217
- }
218
- }
219
- /**
220
- * Build connection URL from primary database properties
221
- */
222
- buildPrimaryDbUrl() {
223
- if (!this.options.primaryDb) {
224
- throw new Error("Primary database configuration not provided");
225
- }
226
- const { host, port = 5432, username, password, database, schema = "public", sslMode = "require" } = this.options.primaryDb;
227
- let url = `postgresql://${username}:${encodeURIComponent(password)}@${host}:${port}/${database}`;
228
- const params = new URLSearchParams();
229
- if (schema) {
230
- params.set("schema", schema);
231
- }
232
- params.set("sslmode", sslMode);
233
- const queryString = params.toString();
234
- if (queryString) {
235
- url += `?${queryString}`;
236
- }
237
- this.logger.debug(`Primary DB connection URL: ${this.maskPassword(url)}`);
238
- return url;
239
- }
240
- /**
241
- * Mask password in connection URL for logging
242
- */
243
- maskPassword(url) {
244
- return url.replace(/:([^@]+)@/, ":****@");
245
- }
246
- /**
247
- * Get tenant configuration by identifier (ID or subdomain)
248
- *
249
- * @param tenantIdentifier Tenant ID or subdomain
250
- * @returns Tenant configuration or null if not found
251
- */
252
- async getTenantInfo(tenantIdentifier) {
253
- const cached = this.tenantConfigCache.get(tenantIdentifier);
254
- if (cached) {
255
- this.logger.debug(`Cache hit for tenant: ${tenantIdentifier}`);
256
- return cached;
257
- }
258
- try {
259
- if (!this.db) {
260
- throw new Error("Primary database client not initialized");
261
- }
262
- this.logger.debug(`Querying primary database for tenant: ${tenantIdentifier}`);
263
- const schema = this.options.drizzleSchema;
264
- const { tenants, tenantDatabaseConfigs } = schema;
265
- const result = await this.db.select().from(tenants).leftJoin(tenantDatabaseConfigs, eq(tenants.id, tenantDatabaseConfigs.tenantId)).where(or(eq(tenants.id, tenantIdentifier), eq(tenants.subdomain, tenantIdentifier))).limit(1);
266
- if (!result.length) {
267
- this.logger.warn(`Tenant not found: ${tenantIdentifier}`);
268
- return null;
269
- }
270
- const row = result[0];
271
- const tenant = row.tenants;
272
- const config = row.tenant_database_configs;
273
- if (tenant.status !== "ACTIVE") {
274
- this.logger.warn(`Tenant not active: ${tenantIdentifier}`);
275
- return null;
276
- }
277
- const info = {
278
- id: tenant.id,
279
- subdomain: tenant.subdomain,
280
- type: tenant.dbType,
281
- status: tenant.status,
282
- // For SHARED tenants: schema name
283
- schemaName: config?.dbSchema || void 0,
284
- // For DEDICATED tenants: database configuration from TenantDatabaseConfig table
285
- databaseName: config?.dbName || void 0,
286
- databaseHost: config?.dbHost || void 0,
287
- databasePort: config?.dbPort || void 0,
288
- databaseUsername: config?.dbUsername ? this.decrypt(config.dbUsername) : void 0,
289
- databasePassword: config?.dbPassword ? this.decrypt(config.dbPassword) : void 0,
290
- databaseSslMode: config?.dbSslMode || void 0,
291
- connectionPoolSize: config?.connectionPoolSize || void 0
292
- };
293
- this.cacheInfo(info);
294
- return info;
295
- } catch (error) {
296
- this.logger.error(`Failed to fetch tenant info: ${tenantIdentifier}`, error);
297
- throw new InternalServerErrorException("Failed to resolve tenant");
298
- }
299
- }
300
- /**
301
- * Cache tenant information with TTL
302
- */
303
- cacheInfo(info) {
304
- this.tenantConfigCache.set(info.id, info);
305
- this.tenantConfigCache.set(info.subdomain, info);
306
- setTimeout(() => {
307
- this.tenantConfigCache.delete(info.id);
308
- this.tenantConfigCache.delete(info.subdomain);
309
- this.logger.debug(`Cache expired for tenant: ${info.subdomain}`);
310
- }, this.cacheTTL);
311
- }
312
- /**
313
- * Clear cached tenant information
314
- *
315
- * Useful when tenant settings are updated and cache needs to be invalidated
316
- *
317
- * @param tenantIdentifier Tenant ID or subdomain
318
- */
319
- clearTenantCache(tenantIdentifier) {
320
- const config = this.tenantConfigCache.get(tenantIdentifier);
321
- if (config) {
322
- this.tenantConfigCache.delete(config.id);
323
- this.tenantConfigCache.delete(config.subdomain);
324
- this.logger.log(`Cleared cache for tenant: ${tenantIdentifier}`);
325
- }
326
- }
327
- /**
328
- * Clear all cached tenant configurations
329
- */
330
- clearAllCaches() {
331
- const size = this.tenantConfigCache.size;
332
- this.tenantConfigCache.clear();
333
- this.logger.log(`Cleared ${size} cached tenant configs`);
334
- }
335
- /**
336
- * Get the Drizzle database instance for the primary database.
337
- * This is a synchronous property that returns the initialized Drizzle client.
338
- *
339
- * @returns Primary database Drizzle instance
340
- * @throws Error if primary database client is not initialized
341
- */
342
- get drizzleClient() {
343
- if (!this.db) {
344
- throw new Error("Primary database client not initialized");
345
- }
346
- return this.db;
347
- }
348
- /**
349
- * Get the Drizzle schema
350
- */
351
- get schema() {
352
- return this.options.drizzleSchema;
353
- }
354
- /**
355
- * Decrypt database credentials
356
- *
357
- * Override this method to implement your encryption strategy
358
- *
359
- * @param encrypted Encrypted value
360
- * @returns Decrypted value
361
- */
362
- decrypt(encrypted) {
363
- return encrypted;
364
- }
365
- async onModuleDestroy() {
366
- if (this.pool) {
367
- await this.pool.end();
368
- this.logger.log("Disconnected from primary database");
369
- }
370
- }
371
- };
372
- PrimaryDatabaseService = _ts_decorate3([
373
- Injectable2(),
374
- _ts_param2(0, Inject2(DATABASE_MODULE_OPTIONS)),
375
- _ts_metadata2("design:type", Function),
376
- _ts_metadata2("design:paramtypes", [
377
- typeof DatabaseModuleOptions === "undefined" ? Object : DatabaseModuleOptions
378
- ])
379
- ], PrimaryDatabaseService);
380
-
381
- // src/auth/guards/vritti-auth.guard.ts
382
- function _ts_decorate4(decorators, target, key, desc) {
383
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
384
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
385
- 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;
386
- return c > 3 && r && Object.defineProperty(target, key, r), r;
387
- }
388
- __name(_ts_decorate4, "_ts_decorate");
389
- function _ts_metadata3(k, v) {
390
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
391
- }
392
- __name(_ts_metadata3, "_ts_metadata");
393
242
  var VrittiAuthGuard = class _VrittiAuthGuard {
394
243
  static {
395
244
  __name(this, "VrittiAuthGuard");
396
245
  }
397
246
  reflector;
398
- configService;
247
+ _configService;
399
248
  jwtService;
400
249
  primaryDatabase;
401
250
  requestService;
402
251
  logger = new Logger2(_VrittiAuthGuard.name);
403
- constructor(reflector, configService, jwtService, primaryDatabase, requestService) {
252
+ constructor(reflector, _configService, jwtService, primaryDatabase, requestService) {
404
253
  this.reflector = reflector;
405
- this.configService = configService;
254
+ this._configService = _configService;
406
255
  this.jwtService = jwtService;
407
256
  this.primaryDatabase = primaryDatabase;
408
257
  this.requestService = requestService;
@@ -439,6 +288,7 @@ var VrittiAuthGuard = class _VrittiAuthGuard {
439
288
  }
440
289
  const validatedToken2 = this.validateAccessToken(accessToken);
441
290
  this.logger.debug("Onboarding token validated successfully");
291
+ this.validateRefreshTokenBinding(context, validatedToken2);
442
292
  const userId2 = validatedToken2.userId;
443
293
  request.user = {
444
294
  id: userId2
@@ -451,13 +301,7 @@ var VrittiAuthGuard = class _VrittiAuthGuard {
451
301
  }
452
302
  const validatedToken = this.validateAccessToken(accessToken);
453
303
  this.logger.debug("Access token validated successfully");
454
- const refreshToken = this.requestService.getRefreshToken();
455
- if (!refreshToken) {
456
- this.logger.warn("Refresh token (session-id) not found in cookies");
457
- throw new UnauthorizedException("Refresh token not found");
458
- }
459
- this.validateRefreshToken(refreshToken);
460
- this.logger.debug("Refresh token validated successfully");
304
+ this.validateRefreshTokenBinding(context, validatedToken);
461
305
  const userId = validatedToken.userId;
462
306
  request.user = {
463
307
  id: userId
@@ -528,68 +372,44 @@ var VrittiAuthGuard = class _VrittiAuthGuard {
528
372
  }
529
373
  }
530
374
  /**
531
- * Validate refresh token with proper expiry checks
532
- * Throws UnauthorizedException if token is invalid or expired
533
- */
534
- validateRefreshToken(token) {
535
- const jwtSecret = this.configService.get("JWT_REFRESH_SECRET") || this.configService.get("JWT_SECRET");
536
- this.validateRefreshTokenWithSecret(token, jwtSecret);
537
- }
538
- /**
539
- * Helper to validate refresh token with specific secret
375
+ * Validate that the access token is bound to the refresh token in the cookie.
376
+ * This prevents token theft - a stolen access token is useless without the
377
+ * corresponding refresh token cookie.
378
+ *
379
+ * @param context - The execution context containing the request
380
+ * @param validatedToken - The decoded and validated JWT token
381
+ * @throws UnauthorizedException if token binding validation fails
540
382
  */
541
- validateRefreshTokenWithSecret(token, secret) {
542
- if (!secret) {
543
- this.logger.error("JWT secret not configured for refresh token validation");
544
- throw new UnauthorizedException("Server configuration error");
383
+ validateRefreshTokenBinding(context, validatedToken) {
384
+ const config = getConfig();
385
+ if (!config.jwt.validateTokenBinding) {
386
+ this.logger.debug("Token binding validation is disabled");
387
+ return;
545
388
  }
546
- try {
547
- const decoded = jwt.verify(token, secret, {
548
- algorithms: [
549
- "HS256",
550
- "HS512",
551
- "RS256"
552
- ]
553
- });
554
- this.logger.debug(`Refresh token decoded for user: ${decoded.userId}`);
555
- if (decoded.exp) {
556
- const expiryTime = decoded.exp * 1e3;
557
- const currentTime = Date.now();
558
- if (currentTime > expiryTime) {
559
- this.logger.warn("Refresh token has expired");
560
- throw new UnauthorizedException("Refresh token has expired. Please login again");
561
- }
562
- const timeRemaining = expiryTime - currentTime;
563
- this.logger.debug(`Refresh token valid for ${Math.floor(timeRemaining / 1e3)} more seconds`);
564
- }
565
- } catch (error) {
566
- if (error instanceof UnauthorizedException) {
567
- throw error;
568
- }
569
- const jwtError = error;
570
- if (jwtError?.name === "TokenExpiredError") {
571
- this.logger.warn(`Refresh token expired at: ${jwtError?.expiredAt}`);
572
- throw new UnauthorizedException("Refresh token has expired. Please login again");
573
- }
574
- if (jwtError?.name === "JsonWebTokenError") {
575
- this.logger.warn(`Refresh token verification failed: ${jwtError?.message}`);
576
- throw new UnauthorizedException("Invalid refresh token");
577
- }
578
- if (jwtError?.name === "NotBeforeError") {
579
- this.logger.warn("Refresh token used before valid (nbf claim)");
580
- throw new UnauthorizedException("Refresh token not yet valid");
581
- }
582
- this.logger.error("Unexpected error validating refresh token", error);
583
- throw new UnauthorizedException("Refresh token validation failed");
389
+ if (!validatedToken.refreshTokenHash) {
390
+ this.logger.debug("Token does not contain refreshTokenHash, skipping binding validation");
391
+ return;
392
+ }
393
+ const request = context.switchToHttp().getRequest();
394
+ const cookies = request.cookies || {};
395
+ const refreshToken = cookies[config.cookie.refreshCookieName];
396
+ if (!refreshToken) {
397
+ this.logger.warn("Session validation failed - refresh token cookie not found");
398
+ throw new UnauthorizedException("Session validation failed");
584
399
  }
400
+ if (!verifyTokenHash(refreshToken, validatedToken.refreshTokenHash)) {
401
+ this.logger.warn("Session validation failed - token binding mismatch");
402
+ throw new UnauthorizedException("Session validation failed");
403
+ }
404
+ this.logger.debug("Token binding validated successfully");
585
405
  }
586
406
  };
587
- VrittiAuthGuard = _ts_decorate4([
588
- Injectable3({
407
+ VrittiAuthGuard = _ts_decorate3([
408
+ Injectable2({
589
409
  scope: Scope2.REQUEST
590
410
  }),
591
- _ts_metadata3("design:type", Function),
592
- _ts_metadata3("design:paramtypes", [
411
+ _ts_metadata2("design:type", Function),
412
+ _ts_metadata2("design:paramtypes", [
593
413
  typeof Reflector === "undefined" ? Object : Reflector,
594
414
  typeof ConfigService === "undefined" ? Object : ConfigService,
595
415
  typeof JwtService === "undefined" ? Object : JwtService,
@@ -599,13 +419,13 @@ VrittiAuthGuard = _ts_decorate4([
599
419
  ], VrittiAuthGuard);
600
420
 
601
421
  // src/auth/auth-config.module.ts
602
- function _ts_decorate5(decorators, target, key, desc) {
422
+ function _ts_decorate4(decorators, target, key, desc) {
603
423
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
604
424
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
605
425
  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;
606
426
  return c > 3 && r && Object.defineProperty(target, key, r), r;
607
427
  }
608
- __name(_ts_decorate5, "_ts_decorate");
428
+ __name(_ts_decorate4, "_ts_decorate");
609
429
  var AuthConfigModule = class _AuthConfigModule {
610
430
  static {
611
431
  __name(this, "AuthConfigModule");
@@ -653,117 +473,40 @@ var AuthConfigModule = class _AuthConfigModule {
653
473
  };
654
474
  }
655
475
  };
656
- AuthConfigModule = _ts_decorate5([
476
+ AuthConfigModule = _ts_decorate4([
657
477
  Global2(),
658
478
  Module2({})
659
479
  ], AuthConfigModule);
660
480
 
481
+ // src/auth/decorators/onboarding.decorator.ts
482
+ import { SetMetadata } from "@nestjs/common";
483
+ var Onboarding = /* @__PURE__ */ __name(() => SetMetadata("isOnboarding", true), "Onboarding");
484
+
485
+ // src/auth/decorators/public.decorator.ts
486
+ import { SetMetadata as SetMetadata2 } from "@nestjs/common";
487
+ var Public = /* @__PURE__ */ __name(() => SetMetadata2("isPublic", true), "Public");
488
+
661
489
  // src/database/database.module.ts
662
490
  import { Global as Global3, Module as Module3 } from "@nestjs/common";
663
491
  import { APP_INTERCEPTOR } from "@nestjs/core";
664
492
 
665
- // src/database/interceptors/message-tenant-context.interceptor.ts
666
- import { Injectable as Injectable5, Logger as Logger3, Scope as Scope4 } from "@nestjs/common";
667
- import { tap } from "rxjs/operators";
668
-
669
- // src/database/services/tenant-context.service.ts
670
- import { Injectable as Injectable4, Scope as Scope3, UnauthorizedException as UnauthorizedException2 } from "@nestjs/common";
671
- function _ts_decorate6(decorators, target, key, desc) {
672
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
673
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
674
- 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;
675
- return c > 3 && r && Object.defineProperty(target, key, r), r;
676
- }
677
- __name(_ts_decorate6, "_ts_decorate");
678
- var TenantContextService = class {
679
- static {
680
- __name(this, "TenantContextService");
681
- }
682
- tenantInfo = null;
683
- /**
684
- * Set tenant information for this request/message
685
- *
686
- * This is typically called by:
687
- * - TenantContextInterceptor (for HTTP requests in gateway)
688
- * - MessageTenantContextInterceptor (for RabbitMQ messages in microservices)
689
- * - Manual context setup in message handlers
690
- *
691
- * @param tenantInfo Complete tenant information
692
- * @throws Error if tenant context is already set (prevents accidental overwrites)
693
- */
694
- setTenant(tenantInfo) {
695
- if (this.tenantInfo) {
696
- throw new Error("Tenant context already set for this request");
697
- }
698
- this.tenantInfo = tenantInfo;
699
- }
700
- /**
701
- * Get tenant information for this request/message
702
- *
703
- * @returns Tenant information
704
- * @throws UnauthorizedException if tenant context hasn't been set
705
- */
706
- getTenant() {
707
- if (!this.tenantInfo) {
708
- throw new UnauthorizedException2("Tenant context not set");
709
- }
710
- return this.tenantInfo;
711
- }
712
- /**
713
- * Check if tenant context has been set
714
- *
715
- * @returns true if tenant context is available
716
- */
717
- hasTenant() {
718
- return this.tenantInfo !== null;
719
- }
720
- /**
721
- * Clear tenant context
722
- *
723
- * This is useful for cleanup in RabbitMQ message handlers
724
- * after the message has been processed.
725
- *
726
- * HTTP requests don't need manual cleanup as the service
727
- * instance is destroyed when the request ends.
728
- */
729
- clearTenant() {
730
- this.tenantInfo = null;
731
- }
732
- /**
733
- * Get tenant ID safely (returns null if not set)
734
- *
735
- * @returns Tenant ID or null
736
- */
737
- getTenantIdSafe() {
738
- return this.tenantInfo?.id ?? null;
739
- }
740
- /**
741
- * Get tenant subdomain safely (returns null if not set)
742
- *
743
- * @returns Tenant subdomain or null
744
- */
745
- getTenantSubdomainSafe() {
746
- return this.tenantInfo?.subdomain ?? null;
747
- }
748
- };
749
- TenantContextService = _ts_decorate6([
750
- Injectable4({
751
- scope: Scope3.REQUEST
752
- })
753
- ], TenantContextService);
493
+ // src/database/constants.ts
494
+ var DATABASE_MODULE_OPTIONS = Symbol("DATABASE_MODULE_OPTIONS");
754
495
 
755
496
  // src/database/interceptors/message-tenant-context.interceptor.ts
756
- function _ts_decorate7(decorators, target, key, desc) {
497
+ import { Injectable as Injectable3, Logger as Logger3, Scope as Scope3 } from "@nestjs/common";
498
+ import { tap } from "rxjs/operators";
499
+ function _ts_decorate5(decorators, target, key, desc) {
757
500
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
758
501
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
759
502
  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;
760
503
  return c > 3 && r && Object.defineProperty(target, key, r), r;
761
504
  }
762
- __name(_ts_decorate7, "_ts_decorate");
763
- function _ts_metadata4(k, v) {
505
+ __name(_ts_decorate5, "_ts_decorate");
506
+ function _ts_metadata3(k, v) {
764
507
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
765
508
  }
766
- __name(_ts_metadata4, "_ts_metadata");
509
+ __name(_ts_metadata3, "_ts_metadata");
767
510
  var MessageTenantContextInterceptor = class _MessageTenantContextInterceptor {
768
511
  static {
769
512
  __name(this, "MessageTenantContextInterceptor");
@@ -778,7 +521,7 @@ var MessageTenantContextInterceptor = class _MessageTenantContextInterceptor {
778
521
  if (contextType === "rpc") {
779
522
  const rpcContext = context.switchToRpc();
780
523
  const payload = rpcContext.getData();
781
- if (payload && payload.tenant) {
524
+ if (payload?.tenant) {
782
525
  const tenant = payload.tenant;
783
526
  this.logger.debug(`Setting tenant context from message: ${tenant.subdomain}`);
784
527
  try {
@@ -814,30 +557,29 @@ var MessageTenantContextInterceptor = class _MessageTenantContextInterceptor {
814
557
  }
815
558
  }
816
559
  };
817
- MessageTenantContextInterceptor = _ts_decorate7([
818
- Injectable5({
819
- scope: Scope4.REQUEST
560
+ MessageTenantContextInterceptor = _ts_decorate5([
561
+ Injectable3({
562
+ scope: Scope3.REQUEST
820
563
  }),
821
- _ts_metadata4("design:type", Function),
822
- _ts_metadata4("design:paramtypes", [
564
+ _ts_metadata3("design:type", Function),
565
+ _ts_metadata3("design:paramtypes", [
823
566
  typeof TenantContextService === "undefined" ? Object : TenantContextService
824
567
  ])
825
568
  ], MessageTenantContextInterceptor);
826
569
 
827
570
  // src/database/interceptors/tenant-context.interceptor.ts
828
- import { Injectable as Injectable6, Logger as Logger4, Scope as Scope5, UnauthorizedException as UnauthorizedException3 } from "@nestjs/common";
829
- import { Reflector as Reflector2 } from "@nestjs/core";
830
- function _ts_decorate8(decorators, target, key, desc) {
571
+ import { Injectable as Injectable4, Logger as Logger4, Scope as Scope4, UnauthorizedException as UnauthorizedException2 } from "@nestjs/common";
572
+ function _ts_decorate6(decorators, target, key, desc) {
831
573
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
832
574
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
833
575
  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;
834
576
  return c > 3 && r && Object.defineProperty(target, key, r), r;
835
577
  }
836
- __name(_ts_decorate8, "_ts_decorate");
837
- function _ts_metadata5(k, v) {
578
+ __name(_ts_decorate6, "_ts_decorate");
579
+ function _ts_metadata4(k, v) {
838
580
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
839
581
  }
840
- __name(_ts_metadata5, "_ts_metadata");
582
+ __name(_ts_metadata4, "_ts_metadata");
841
583
  var TenantContextInterceptor = class _TenantContextInterceptor {
842
584
  static {
843
585
  __name(this, "TenantContextInterceptor");
@@ -867,7 +609,7 @@ var TenantContextInterceptor = class _TenantContextInterceptor {
867
609
  return next.handle();
868
610
  }
869
611
  if (!tenantIdentifier) {
870
- throw new UnauthorizedException3("Tenant identifier not found in request");
612
+ throw new UnauthorizedException2("Tenant identifier not found in request");
871
613
  }
872
614
  this.logger.debug(`Tenant identifier extracted: ${tenantIdentifier}`);
873
615
  if (tenantIdentifier === "cloud") {
@@ -877,11 +619,11 @@ var TenantContextInterceptor = class _TenantContextInterceptor {
877
619
  const tenantInfo = await this.primaryDatabase.getTenantInfo(tenantIdentifier);
878
620
  if (!tenantInfo) {
879
621
  this.logger.warn(`Invalid tenant: ${tenantIdentifier}`);
880
- throw new UnauthorizedException3("Invalid tenant");
622
+ throw new UnauthorizedException2("Invalid tenant");
881
623
  }
882
624
  if (tenantInfo.status !== "ACTIVE") {
883
625
  this.logger.warn(`Tenant ${tenantIdentifier} has status: ${tenantInfo.status}`);
884
- throw new UnauthorizedException3(`Tenant is ${tenantInfo.status}`);
626
+ throw new UnauthorizedException2(`Tenant is ${tenantInfo.status}`);
885
627
  }
886
628
  this.logger.debug(`Tenant config loaded: ${tenantInfo.subdomain} (${tenantInfo.type})`);
887
629
  this.tenantContext.setTenant(tenantInfo);
@@ -894,657 +636,686 @@ var TenantContextInterceptor = class _TenantContextInterceptor {
894
636
  return next.handle();
895
637
  }
896
638
  };
897
- TenantContextInterceptor = _ts_decorate8([
898
- Injectable6({
899
- scope: Scope5.REQUEST
639
+ TenantContextInterceptor = _ts_decorate6([
640
+ Injectable4({
641
+ scope: Scope4.REQUEST
900
642
  }),
901
- _ts_metadata5("design:type", Function),
902
- _ts_metadata5("design:paramtypes", [
903
- typeof Reflector2 === "undefined" ? Object : Reflector2,
643
+ _ts_metadata4("design:type", Function),
644
+ _ts_metadata4("design:paramtypes", [
645
+ typeof Reflector === "undefined" ? Object : Reflector,
904
646
  typeof TenantContextService === "undefined" ? Object : TenantContextService,
905
647
  typeof PrimaryDatabaseService === "undefined" ? Object : PrimaryDatabaseService,
906
648
  typeof RequestService === "undefined" ? Object : RequestService
907
649
  ])
908
650
  ], TenantContextInterceptor);
909
651
 
910
- // src/database/services/tenant-database.service.ts
911
- import { Inject as Inject3, Injectable as Injectable7, InternalServerErrorException as InternalServerErrorException2, Logger as Logger5 } from "@nestjs/common";
912
- import { Pool as Pool2 } from "pg";
913
- import { drizzle as drizzle2 } from "drizzle-orm/node-postgres";
914
- function _ts_decorate9(decorators, target, key, desc) {
652
+ // src/database/services/primary-database.service.ts
653
+ import { Inject as Inject2, Injectable as Injectable5, InternalServerErrorException, Logger as Logger5 } from "@nestjs/common";
654
+ import { eq, or } from "drizzle-orm";
655
+ import { drizzle } from "drizzle-orm/node-postgres";
656
+ import { Pool } from "pg";
657
+ function _ts_decorate7(decorators, target, key, desc) {
915
658
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
916
659
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
917
660
  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;
918
661
  return c > 3 && r && Object.defineProperty(target, key, r), r;
919
662
  }
920
- __name(_ts_decorate9, "_ts_decorate");
921
- function _ts_metadata6(k, v) {
663
+ __name(_ts_decorate7, "_ts_decorate");
664
+ function _ts_metadata5(k, v) {
922
665
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
923
666
  }
924
- __name(_ts_metadata6, "_ts_metadata");
925
- function _ts_param3(paramIndex, decorator) {
667
+ __name(_ts_metadata5, "_ts_metadata");
668
+ function _ts_param2(paramIndex, decorator) {
926
669
  return function(target, key) {
927
670
  decorator(target, key, paramIndex);
928
671
  };
929
672
  }
930
- __name(_ts_param3, "_ts_param");
931
- var TenantDatabaseService = class _TenantDatabaseService {
673
+ __name(_ts_param2, "_ts_param");
674
+ var PrimaryDatabaseService2 = class _PrimaryDatabaseService {
932
675
  static {
933
- __name(this, "TenantDatabaseService");
676
+ __name(this, "PrimaryDatabaseService");
934
677
  }
935
678
  options;
936
- tenantContext;
937
- logger = new Logger5(_TenantDatabaseService.name);
938
- /** Connection pool: Map<cacheKey, TenantConnection> */
939
- clients = /* @__PURE__ */ new Map();
940
- /** Track last usage time for idle connection cleanup */
941
- clientLastUsed = /* @__PURE__ */ new Map();
942
- /** Cleanup interval timer */
943
- cleanupInterval;
944
- constructor(options, tenantContext) {
679
+ logger = new Logger5(_PrimaryDatabaseService.name);
680
+ /** PostgreSQL connection pool */
681
+ pool = null;
682
+ /** Drizzle database instance */
683
+ db = null;
684
+ /** In-memory cache: Map<tenantIdentifier, TenantConfig> */
685
+ tenantConfigCache = /* @__PURE__ */ new Map();
686
+ /** Cache TTL in milliseconds */
687
+ cacheTTL;
688
+ constructor(options) {
945
689
  this.options = options;
946
- this.tenantContext = tenantContext;
947
- this.startConnectionCleaner();
690
+ this.cacheTTL = options.connectionCacheTTL || 3e5;
948
691
  }
949
- /**
950
- * Get the Drizzle client for the current tenant's database.
951
- * This returns the tenant-scoped database client.
952
- *
953
- * @returns Tenant-scoped Drizzle database instance
954
- * @throws UnauthorizedException if tenant context not set
955
- * @throws InternalServerErrorException if connection fails
956
- */
957
- get drizzleClient() {
958
- return this.getDbClient();
692
+ async onModuleInit() {
693
+ if (this.options.primaryDb) {
694
+ await this.initializeDrizzleClient();
695
+ }
959
696
  }
960
697
  /**
961
- * Get the Drizzle schema
698
+ * Initialize connection to primary database using Drizzle
962
699
  */
963
- get schema() {
964
- return this.options.drizzleSchema;
700
+ async initializeDrizzleClient() {
701
+ try {
702
+ const databaseUrl = this.buildPrimaryDbUrl();
703
+ this.pool = new Pool({
704
+ connectionString: databaseUrl,
705
+ max: this.options.maxConnections || 10
706
+ });
707
+ this.logger.debug(`Schema keys passed to drizzle: [${Object.keys(this.options.drizzleSchema || {}).join(", ")}]`);
708
+ this.logger.debug(`Relations keys passed to drizzle: [${Object.keys(this.options.drizzleRelations || {}).join(", ")}]`);
709
+ this.db = drizzle({
710
+ client: this.pool,
711
+ schema: this.options.drizzleSchema,
712
+ relations: this.options.drizzleRelations
713
+ });
714
+ this.logger.debug(`Drizzle query keys after init: [${Object.keys(this.db.query || {}).join(", ")}]`);
715
+ await this.pool.query("SELECT 1");
716
+ this.logger.log("Connected to primary database (tenant registry)");
717
+ } catch (error) {
718
+ this.logger.error("Failed to connect to primary database", error);
719
+ throw new InternalServerErrorException("Failed to initialize tenant registry");
720
+ }
965
721
  }
966
722
  /**
967
- * Get tenant-scoped database client for the current request/message
968
- *
969
- * This method:
970
- * 1. Gets tenant info from TenantContextService
971
- * 2. Builds a connection URL based on tenant type
972
- * 3. Returns cached client if exists, otherwise creates new one
973
- *
974
- * @returns Drizzle database instance
975
- * @throws UnauthorizedException if tenant context not set
976
- * @throws InternalServerErrorException if connection fails
723
+ * Build connection URL from primary database properties
977
724
  */
978
- getDbClient() {
979
- const tenant = this.tenantContext.getTenant();
980
- const cacheKey = this.buildCacheKey(tenant);
981
- const existing = this.clients.get(cacheKey);
982
- if (existing) {
983
- this.clientLastUsed.set(cacheKey, Date.now());
984
- this.logger.debug(`Reusing cached connection: ${cacheKey}`);
985
- return existing.db;
725
+ buildPrimaryDbUrl() {
726
+ if (!this.options.primaryDb) {
727
+ throw new Error("Primary database configuration not provided");
986
728
  }
987
- this.logger.log(`Creating new database connection: ${cacheKey}`);
988
- const connection = this.createDbClientSync(tenant);
989
- this.clients.set(cacheKey, connection);
990
- this.clientLastUsed.set(cacheKey, Date.now());
991
- return connection.db;
729
+ const { host, port = 5432, username, password, database, schema = "public", sslMode = "require" } = this.options.primaryDb;
730
+ let url = `postgresql://${username}:${encodeURIComponent(password)}@${host}:${port}/${database}`;
731
+ const params = new URLSearchParams();
732
+ if (schema) {
733
+ params.set("schema", schema);
734
+ }
735
+ params.set("sslmode", sslMode);
736
+ const queryString = params.toString();
737
+ if (queryString) {
738
+ url += `?${queryString}`;
739
+ }
740
+ this.logger.debug(`Primary DB connection URL: ${this.maskPassword(url)}`);
741
+ return url;
992
742
  }
993
743
  /**
994
- * Create a new database client for the given tenant (synchronous)
744
+ * Mask password in connection URL for logging
995
745
  */
996
- createDbClientSync(tenant) {
746
+ maskPassword(url) {
747
+ return url.replace(/:([^@]+)@/, ":****@");
748
+ }
749
+ /**
750
+ * Get tenant configuration by identifier (ID or subdomain)
751
+ *
752
+ * @param tenantIdentifier Tenant ID or subdomain
753
+ * @returns Tenant configuration or null if not found
754
+ */
755
+ async getTenantInfo(tenantIdentifier) {
756
+ const cached = this.tenantConfigCache.get(tenantIdentifier);
757
+ if (cached) {
758
+ this.logger.debug(`Cache hit for tenant: ${tenantIdentifier}`);
759
+ return cached;
760
+ }
997
761
  try {
998
- const databaseUrl = this.buildTenantDbUrl(tenant);
999
- const pool = new Pool2({
1000
- connectionString: databaseUrl,
1001
- max: tenant.connectionPoolSize || this.options.maxConnections || 10
1002
- });
1003
- const db = drizzle2({
1004
- client: pool,
1005
- schema: this.options.drizzleSchema
1006
- });
1007
- this.logger.log(`Connected to database for tenant: ${tenant.subdomain}`);
1008
- return {
1009
- pool,
1010
- db
762
+ if (!this.db) {
763
+ throw new Error("Primary database client not initialized");
764
+ }
765
+ this.logger.debug(`Querying primary database for tenant: ${tenantIdentifier}`);
766
+ const schema = this.options.drizzleSchema;
767
+ const { tenants, tenantDatabaseConfigs } = schema;
768
+ const result = await this.db.select().from(tenants).leftJoin(tenantDatabaseConfigs, eq(tenants.id, tenantDatabaseConfigs.tenantId)).where(or(eq(tenants.id, tenantIdentifier), eq(tenants.subdomain, tenantIdentifier))).limit(1);
769
+ if (!result.length) {
770
+ this.logger.warn(`Tenant not found: ${tenantIdentifier}`);
771
+ return null;
772
+ }
773
+ const row = result[0];
774
+ const tenant = row.tenants;
775
+ const config = row.tenant_database_configs;
776
+ if (tenant.status !== "ACTIVE") {
777
+ this.logger.warn(`Tenant not active: ${tenantIdentifier}`);
778
+ return null;
779
+ }
780
+ const info = {
781
+ id: tenant.id,
782
+ subdomain: tenant.subdomain,
783
+ type: tenant.dbType,
784
+ status: tenant.status,
785
+ // For SHARED tenants: schema name
786
+ schemaName: config?.dbSchema || void 0,
787
+ // For DEDICATED tenants: database configuration from TenantDatabaseConfig table
788
+ databaseName: config?.dbName || void 0,
789
+ databaseHost: config?.dbHost || void 0,
790
+ databasePort: config?.dbPort || void 0,
791
+ databaseUsername: config?.dbUsername ? this.decrypt(config.dbUsername) : void 0,
792
+ databasePassword: config?.dbPassword ? this.decrypt(config.dbPassword) : void 0,
793
+ databaseSslMode: config?.dbSslMode || void 0,
794
+ connectionPoolSize: config?.connectionPoolSize || void 0
1011
795
  };
796
+ this.cacheInfo(info);
797
+ return info;
1012
798
  } catch (error) {
1013
- this.logger.error(`Failed to create database connection for tenant: ${tenant.subdomain}`, error);
1014
- throw new InternalServerErrorException2("Failed to connect to tenant database");
799
+ this.logger.error(`Failed to fetch tenant info: ${tenantIdentifier}`, error);
800
+ throw new InternalServerErrorException("Failed to resolve tenant");
1015
801
  }
1016
802
  }
1017
803
  /**
1018
- * Build connection URL for tenant (dedicated database)
804
+ * Cache tenant information with TTL
1019
805
  */
1020
- buildTenantDbUrl(tenant) {
1021
- const { databaseHost, databasePort, databaseName, databaseUsername, databasePassword, databaseSslMode } = tenant;
1022
- if (!databaseHost || !databaseName || !databaseUsername) {
1023
- throw new Error(`Tenant ${tenant.subdomain} missing database configuration`);
1024
- }
1025
- const port = databasePort || 5432;
1026
- const sslMode = databaseSslMode || "require";
1027
- const connectionUrl = `postgresql://${databaseUsername}:${encodeURIComponent(databasePassword || "")}@${databaseHost}:${port}/${databaseName}?sslmode=${sslMode}`;
1028
- this.logger.debug(`Tenant connection URL: ${this.maskPassword(connectionUrl)}`);
1029
- return connectionUrl;
806
+ cacheInfo(info) {
807
+ this.tenantConfigCache.set(info.id, info);
808
+ this.tenantConfigCache.set(info.subdomain, info);
809
+ setTimeout(() => {
810
+ this.tenantConfigCache.delete(info.id);
811
+ this.tenantConfigCache.delete(info.subdomain);
812
+ this.logger.debug(`Cache expired for tenant: ${info.subdomain}`);
813
+ }, this.cacheTTL);
1030
814
  }
1031
815
  /**
1032
- * Build cache key for connection pooling
816
+ * Clear cached tenant information
817
+ *
818
+ * Useful when tenant settings are updated and cache needs to be invalidated
819
+ *
820
+ * @param tenantIdentifier Tenant ID or subdomain
1033
821
  */
1034
- buildCacheKey(tenant) {
1035
- return `${tenant.type}:${tenant.databaseName}@${tenant.databaseHost}`;
822
+ clearTenantCache(tenantIdentifier) {
823
+ const config = this.tenantConfigCache.get(tenantIdentifier);
824
+ if (config) {
825
+ this.tenantConfigCache.delete(config.id);
826
+ this.tenantConfigCache.delete(config.subdomain);
827
+ this.logger.log(`Cleared cache for tenant: ${tenantIdentifier}`);
828
+ }
1036
829
  }
1037
830
  /**
1038
- * Start periodic cleanup of idle connections
831
+ * Clear all cached tenant configurations
1039
832
  */
1040
- startConnectionCleaner() {
1041
- const interval = this.options.connectionCacheTTL || 3e5;
1042
- this.cleanupInterval = setInterval(() => {
1043
- this.cleanupIdleConnections();
1044
- }, interval);
1045
- this.logger.log(`Connection cleanup scheduled every ${interval / 1e3} seconds`);
833
+ clearAllCaches() {
834
+ const size = this.tenantConfigCache.size;
835
+ this.tenantConfigCache.clear();
836
+ this.logger.log(`Cleared ${size} cached tenant configs`);
1046
837
  }
1047
838
  /**
1048
- * Clean up idle connections that haven't been used recently
839
+ * Get the Drizzle database instance for the primary database.
840
+ * This is a synchronous property that returns the initialized Drizzle client.
841
+ *
842
+ * @returns Primary database Drizzle instance
843
+ * @throws Error if primary database client is not initialized
1049
844
  */
1050
- async cleanupIdleConnections() {
1051
- const now = Date.now();
1052
- const maxIdle = this.options.connectionCacheTTL || 3e5;
1053
- let cleaned = 0;
1054
- for (const [key, lastUsed] of this.clientLastUsed.entries()) {
1055
- if (now - lastUsed > maxIdle) {
1056
- const connection = this.clients.get(key);
1057
- if (connection) {
1058
- try {
1059
- await connection.pool.end();
1060
- this.logger.debug(`Cleaned up idle connection: ${key}`);
1061
- } catch (error) {
1062
- this.logger.error(`Error disconnecting idle client: ${key}`, error);
1063
- }
1064
- this.clients.delete(key);
1065
- this.clientLastUsed.delete(key);
1066
- cleaned++;
1067
- }
1068
- }
1069
- }
1070
- if (cleaned > 0) {
1071
- this.logger.log(`Cleaned up ${cleaned} idle connections`);
845
+ get drizzleClient() {
846
+ if (!this.db) {
847
+ throw new Error("Primary database client not initialized");
1072
848
  }
849
+ return this.db;
1073
850
  }
1074
851
  /**
1075
- * Get current connection pool statistics
852
+ * Get the Drizzle schema
1076
853
  */
1077
- getPoolStats() {
1078
- return {
1079
- activeConnections: this.clients.size,
1080
- tenants: Array.from(this.clients.keys())
1081
- };
854
+ get schema() {
855
+ return this.options.drizzleSchema;
1082
856
  }
1083
857
  /**
1084
- * Mask password in connection URL for logging
858
+ * Decrypt database credentials
859
+ *
860
+ * Override this method to implement your encryption strategy
861
+ *
862
+ * @param encrypted Encrypted value
863
+ * @returns Decrypted value
1085
864
  */
1086
- maskPassword(url) {
1087
- return url.replace(/:([^@]+)@/, ":****@");
865
+ decrypt(encrypted) {
866
+ return encrypted;
1088
867
  }
1089
868
  async onModuleDestroy() {
1090
- if (this.cleanupInterval) {
1091
- clearInterval(this.cleanupInterval);
869
+ if (this.pool) {
870
+ await this.pool.end();
871
+ this.logger.log("Disconnected from primary database");
1092
872
  }
1093
- this.logger.log(`Disconnecting ${this.clients.size} database connections`);
1094
- const disconnectPromises = Array.from(this.clients.entries()).map(async ([key, connection]) => {
1095
- try {
1096
- await connection.pool.end();
1097
- this.logger.debug(`Disconnected: ${key}`);
1098
- } catch (error) {
1099
- this.logger.error(`Error disconnecting client: ${key}`, error);
1100
- }
1101
- });
1102
- await Promise.all(disconnectPromises);
1103
- this.logger.log("All database connections closed");
1104
873
  }
1105
874
  };
1106
- TenantDatabaseService = _ts_decorate9([
1107
- Injectable7(),
1108
- _ts_param3(0, Inject3(DATABASE_MODULE_OPTIONS)),
1109
- _ts_metadata6("design:type", Function),
1110
- _ts_metadata6("design:paramtypes", [
1111
- typeof DatabaseModuleOptions === "undefined" ? Object : DatabaseModuleOptions,
1112
- typeof TenantContextService === "undefined" ? Object : TenantContextService
875
+ PrimaryDatabaseService2 = _ts_decorate7([
876
+ Injectable5(),
877
+ _ts_param2(0, Inject2(DATABASE_MODULE_OPTIONS)),
878
+ _ts_metadata5("design:type", Function),
879
+ _ts_metadata5("design:paramtypes", [
880
+ typeof DatabaseModuleOptions === "undefined" ? Object : DatabaseModuleOptions
1113
881
  ])
1114
- ], TenantDatabaseService);
882
+ ], PrimaryDatabaseService2);
1115
883
 
1116
- // src/database/database.module.ts
1117
- function _ts_decorate10(decorators, target, key, desc) {
884
+ // src/database/services/tenant-context.service.ts
885
+ import { Injectable as Injectable6, Scope as Scope5, UnauthorizedException as UnauthorizedException3 } from "@nestjs/common";
886
+ function _ts_decorate8(decorators, target, key, desc) {
1118
887
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1119
888
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1120
889
  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;
1121
890
  return c > 3 && r && Object.defineProperty(target, key, r), r;
1122
891
  }
1123
- __name(_ts_decorate10, "_ts_decorate");
1124
- var DatabaseModule = class _DatabaseModule {
892
+ __name(_ts_decorate8, "_ts_decorate");
893
+ var TenantContextService2 = class {
1125
894
  static {
1126
- __name(this, "DatabaseModule");
895
+ __name(this, "TenantContextService");
1127
896
  }
897
+ tenantInfo = null;
1128
898
  /**
1129
- * Configure DatabaseModule for Gateway/HTTP mode (multi-tenant web servers)
1130
- *
1131
- * This mode is for API Gateways that handle HTTP requests:
1132
- * - Automatically registers TenantContextInterceptor
1133
- * - Extracts tenant from subdomain or x-tenant-id header
1134
- * - Queries primary database for tenant configuration
1135
- * - Provides PrimaryDatabaseService for tenant lookup
899
+ * Set tenant information for this request/message
1136
900
  *
1137
- * @param options Async configuration options
1138
- * @returns Dynamic module configuration with HTTP interceptor
901
+ * This is typically called by:
902
+ * - TenantContextInterceptor (for HTTP requests in gateway)
903
+ * - MessageTenantContextInterceptor (for RabbitMQ messages in microservices)
904
+ * - Manual context setup in message handlers
1139
905
  *
1140
- * @example
1141
- * DatabaseModule.forServer({
1142
- * inject: [ConfigService],
1143
- * useFactory: (config: ConfigService) => ({
1144
- * primaryDb: {
1145
- * host: config.get('PRIMARY_DB_HOST'),
1146
- * port: config.get('PRIMARY_DB_PORT'),
1147
- * username: config.get('PRIMARY_DB_USERNAME'),
1148
- * password: config.get('PRIMARY_DB_PASSWORD'),
1149
- * database: config.get('PRIMARY_DB_DATABASE'),
1150
- * },
1151
- * prismaClientConstructor: PrismaClient,
1152
- * }),
1153
- * })
906
+ * @param tenantInfo Complete tenant information
907
+ * @throws Error if tenant context is already set (prevents accidental overwrites)
1154
908
  */
1155
- static forServer(options) {
1156
- return this.createDynamicModule(options, "server");
909
+ setTenant(tenantInfo) {
910
+ if (this.tenantInfo) {
911
+ throw new Error("Tenant context already set for this request");
912
+ }
913
+ this.tenantInfo = tenantInfo;
1157
914
  }
1158
915
  /**
1159
- * Configure DatabaseModule for Microservice/Messaging mode (RabbitMQ workers)
1160
- *
1161
- * This mode is for microservices that process messages from queues:
1162
- * - Automatically registers MessageTenantContextInterceptor
1163
- * - Extracts tenant from RabbitMQ message patterns
1164
- * - No primary database needed (tenant comes from message context)
1165
- *
1166
- * @param options Async configuration options
1167
- * @returns Dynamic module configuration with message interceptor
916
+ * Get tenant information for this request/message
1168
917
  *
1169
- * @example
1170
- * DatabaseModule.forMicroservice({
1171
- * inject: [ConfigService],
1172
- * useFactory: (config: ConfigService) => ({
1173
- * prismaClientConstructor: PrismaClient,
1174
- * }),
1175
- * })
918
+ * @returns Tenant information
919
+ * @throws UnauthorizedException if tenant context hasn't been set
1176
920
  */
1177
- static forMicroservice(options) {
1178
- return this.createDynamicModule(options, "microservice");
921
+ getTenant() {
922
+ if (!this.tenantInfo) {
923
+ throw new UnauthorizedException3("Tenant context not set");
924
+ }
925
+ return this.tenantInfo;
1179
926
  }
1180
927
  /**
1181
- * Internal helper to create dynamic module with conditional interceptor registration
928
+ * Check if tenant context has been set
1182
929
  *
1183
- * @param options Configuration options
1184
- * @param mode Mode of operation (gateway or microservice)
1185
- * @returns Dynamic module configuration
930
+ * @returns true if tenant context is available
1186
931
  */
1187
- static createDynamicModule(options, mode) {
1188
- const asyncProvider = {
1189
- provide: DATABASE_MODULE_OPTIONS,
1190
- useFactory: options.useFactory,
1191
- inject: options.inject || []
1192
- };
1193
- const providers = [
1194
- asyncProvider,
1195
- TenantContextService,
1196
- PrimaryDatabaseService,
1197
- TenantDatabaseService
1198
- ];
1199
- if (mode === "server") {
1200
- providers.push({
1201
- provide: APP_INTERCEPTOR,
1202
- useClass: TenantContextInterceptor
1203
- });
1204
- } else {
1205
- providers.push({
1206
- provide: APP_INTERCEPTOR,
1207
- useClass: MessageTenantContextInterceptor
1208
- });
1209
- }
1210
- return {
1211
- module: _DatabaseModule,
1212
- imports: [
1213
- RequestModule
1214
- ],
1215
- providers,
1216
- exports: [
1217
- TenantDatabaseService,
1218
- TenantContextService,
1219
- PrimaryDatabaseService,
1220
- asyncProvider
1221
- ]
1222
- };
1223
- }
1224
- };
1225
- DatabaseModule = _ts_decorate10([
1226
- Global3(),
1227
- Module3({})
1228
- ], DatabaseModule);
1229
-
1230
- // src/database/repositories/primary-base.repository.ts
1231
- import { Logger as Logger6 } from "@nestjs/common";
1232
- import { eq as eq2, sql, getTableName } from "drizzle-orm";
1233
- var PrimaryBaseRepository = class {
1234
- static {
1235
- __name(this, "PrimaryBaseRepository");
932
+ hasTenant() {
933
+ return this.tenantInfo !== null;
1236
934
  }
1237
- database;
1238
- table;
1239
- logger;
1240
- /**
1241
- * The table name extracted from the Drizzle table at runtime.
1242
- * Used to access the query API for this repository's table.
1243
- */
1244
- tableName;
1245
935
  /**
1246
- * Lazy getter for Drizzle client.
1247
- * Accesses the client from the database service only when needed,
1248
- * avoiding initialization timing issues with NestJS lifecycle.
936
+ * Clear tenant context
937
+ *
938
+ * This is useful for cleanup in RabbitMQ message handlers
939
+ * after the message has been processed.
940
+ *
941
+ * HTTP requests don't need manual cleanup as the service
942
+ * instance is destroyed when the request ends.
1249
943
  */
1250
- get db() {
1251
- return this.database.drizzleClient;
944
+ clearTenant() {
945
+ this.tenantInfo = null;
1252
946
  }
1253
947
  /**
1254
- * Model query API for THIS repository's table (Drizzle v2 relational queries)
1255
- * Scoped to only the table this repository manages.
1256
- * Returns a type-safe wrapper around Drizzle's RelationalQueryBuilder.
948
+ * Get tenant ID safely (returns null if not set)
1257
949
  *
1258
- * @example
1259
- * ```typescript
1260
- * // Use relational queries with v2 object-based where syntax
1261
- * const user = await this.model.findFirst({
1262
- * where: { id },
1263
- * with: { posts: true, profile: true }
1264
- * });
1265
- * ```
950
+ * @returns Tenant ID or null
1266
951
  */
1267
- get model() {
1268
- const query = this.database.drizzleClient.query;
1269
- const queryKeys = Object.keys(query || {});
1270
- this.logger.debug(`Looking for '${this.tableName}' in query keys: [${queryKeys.join(", ")}]`);
1271
- const model = query[this.tableName];
1272
- if (!model) {
1273
- this.logger.error(`Table '${this.tableName}' not found in query object. Available: [${queryKeys.join(", ")}]`);
1274
- }
1275
- return model;
952
+ getTenantIdSafe() {
953
+ return this.tenantInfo?.id ?? null;
1276
954
  }
1277
955
  /**
1278
- * Create a new repository instance
1279
- *
1280
- * @param database - The primary database service
1281
- * @param table - The Drizzle table schema object
1282
- *
1283
- * @example
1284
- * ```typescript
1285
- * import { users } from '@/db/schema';
956
+ * Get tenant subdomain safely (returns null if not set)
1286
957
  *
1287
- * constructor(database: PrimaryDatabaseService) {
1288
- * super(database, users);
1289
- * }
1290
- * ```
958
+ * @returns Tenant subdomain or null
1291
959
  */
1292
- constructor(database, table) {
1293
- this.database = database;
1294
- this.table = table;
1295
- this.tableName = getTableName(table);
1296
- this.logger = new Logger6(this.constructor.name);
1297
- this.logger.debug(`Initialized ${this.constructor.name}`);
1298
- this.logger.debug(`Table name from getTableName: '${this.tableName}'`);
960
+ getTenantSubdomainSafe() {
961
+ return this.tenantInfo?.subdomain ?? null;
962
+ }
963
+ };
964
+ TenantContextService2 = _ts_decorate8([
965
+ Injectable6({
966
+ scope: Scope5.REQUEST
967
+ })
968
+ ], TenantContextService2);
969
+
970
+ // src/database/services/tenant-database.service.ts
971
+ import { Inject as Inject3, Injectable as Injectable7, InternalServerErrorException as InternalServerErrorException2, Logger as Logger6 } from "@nestjs/common";
972
+ import { drizzle as drizzle2 } from "drizzle-orm/node-postgres";
973
+ import { Pool as Pool2 } from "pg";
974
+ function _ts_decorate9(decorators, target, key, desc) {
975
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
976
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
977
+ 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;
978
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
979
+ }
980
+ __name(_ts_decorate9, "_ts_decorate");
981
+ function _ts_metadata6(k, v) {
982
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
983
+ }
984
+ __name(_ts_metadata6, "_ts_metadata");
985
+ function _ts_param3(paramIndex, decorator) {
986
+ return function(target, key) {
987
+ decorator(target, key, paramIndex);
988
+ };
989
+ }
990
+ __name(_ts_param3, "_ts_param");
991
+ var TenantDatabaseService = class _TenantDatabaseService {
992
+ static {
993
+ __name(this, "TenantDatabaseService");
994
+ }
995
+ options;
996
+ tenantContext;
997
+ logger = new Logger6(_TenantDatabaseService.name);
998
+ /** Connection pool: Map<cacheKey, TenantConnection> */
999
+ clients = /* @__PURE__ */ new Map();
1000
+ /** Track last usage time for idle connection cleanup */
1001
+ clientLastUsed = /* @__PURE__ */ new Map();
1002
+ /** Cleanup interval timer */
1003
+ cleanupInterval;
1004
+ constructor(options, tenantContext) {
1005
+ this.options = options;
1006
+ this.tenantContext = tenantContext;
1007
+ this.startConnectionCleaner();
1299
1008
  }
1300
1009
  /**
1301
- * Create a new record
1302
- *
1303
- * @param data - The data to create the record with
1304
- * @returns Promise resolving to the created record
1010
+ * Get the Drizzle client for the current tenant's database.
1011
+ * This returns the tenant-scoped database client.
1305
1012
  *
1306
- * @example
1307
- * ```typescript
1308
- * const user = await userRepository.create({
1309
- * email: 'user@example.com',
1310
- * firstName: 'John'
1311
- * });
1312
- * ```
1013
+ * @returns Tenant-scoped Drizzle database instance
1014
+ * @throws UnauthorizedException if tenant context not set
1015
+ * @throws InternalServerErrorException if connection fails
1313
1016
  */
1314
- async create(data) {
1315
- this.logger.log("Creating record");
1316
- const results = await this.db.insert(this.table).values(data).returning();
1317
- return results[0];
1017
+ get drizzleClient() {
1018
+ return this.getDbClient();
1318
1019
  }
1319
1020
  /**
1320
- * Find a single record by ID
1321
- *
1322
- * @param id - The record ID
1323
- * @returns Promise resolving to the record or undefined if not found
1324
- *
1325
- * @example
1326
- * ```typescript
1327
- * const user = await userRepository.findById('user-id-123');
1328
- * ```
1021
+ * Get the Drizzle schema
1329
1022
  */
1330
- async findById(id) {
1331
- this.logger.debug(`Finding record by ID: ${id}`);
1332
- return this.model.findFirst({
1333
- where: {
1334
- id
1335
- }
1336
- });
1023
+ get schema() {
1024
+ return this.options.drizzleSchema;
1337
1025
  }
1338
1026
  /**
1339
- * Find a single record with custom where clause (Drizzle v2 object-based syntax)
1340
- *
1341
- * @param where - Object-based filter condition
1342
- * @returns Promise resolving to the record or undefined if not found
1343
- *
1344
- * @example
1345
- * ```typescript
1346
- * // Simple equality
1347
- * const user = await userRepository.findOne({ email: 'user@example.com' });
1027
+ * Get tenant-scoped database client for the current request/message
1348
1028
  *
1349
- * // With operators
1350
- * const user = await userRepository.findOne({ age: { gte: 18 } });
1029
+ * This method:
1030
+ * 1. Gets tenant info from TenantContextService
1031
+ * 2. Builds a connection URL based on tenant type
1032
+ * 3. Returns cached client if exists, otherwise creates new one
1351
1033
  *
1352
- * // Multiple conditions (AND)
1353
- * const user = await userRepository.findOne({
1354
- * email: 'user@example.com',
1355
- * status: 'ACTIVE'
1356
- * });
1357
- * ```
1034
+ * @returns Drizzle database instance
1035
+ * @throws UnauthorizedException if tenant context not set
1036
+ * @throws InternalServerErrorException if connection fails
1358
1037
  */
1359
- async findOne(where) {
1360
- this.logger.debug("Finding record with custom query");
1361
- return this.model.findFirst({
1362
- where
1363
- });
1038
+ getDbClient() {
1039
+ const tenant = this.tenantContext.getTenant();
1040
+ const cacheKey = this.buildCacheKey(tenant);
1041
+ const existing = this.clients.get(cacheKey);
1042
+ if (existing) {
1043
+ this.clientLastUsed.set(cacheKey, Date.now());
1044
+ this.logger.debug(`Reusing cached connection: ${cacheKey}`);
1045
+ return existing.db;
1046
+ }
1047
+ this.logger.log(`Creating new database connection: ${cacheKey}`);
1048
+ const connection = this.createDbClientSync(tenant);
1049
+ this.clients.set(cacheKey, connection);
1050
+ this.clientLastUsed.set(cacheKey, Date.now());
1051
+ return connection.db;
1364
1052
  }
1365
1053
  /**
1366
- * Find multiple records (Drizzle v2 object-based syntax)
1367
- *
1368
- * @param options - Query options (where, orderBy, limit, offset)
1369
- * @returns Promise resolving to an array of records
1370
- *
1371
- * @example
1372
- * ```typescript
1373
- * // Find all users
1374
- * const users = await userRepository.findMany();
1375
- *
1376
- * // Find with filtering and pagination (v2 object syntax)
1377
- * const users = await userRepository.findMany({
1378
- * where: { accountStatus: 'ACTIVE' },
1379
- * orderBy: { createdAt: 'desc' },
1380
- * limit: 10,
1381
- * offset: 0
1382
- * });
1383
- *
1384
- * // Multiple conditions
1385
- * const users = await userRepository.findMany({
1386
- * where: {
1387
- * AND: [
1388
- * { status: 'ACTIVE' },
1389
- * { age: { gte: 18 } }
1390
- * ]
1391
- * }
1392
- * });
1393
- * ```
1054
+ * Create a new database client for the given tenant (synchronous)
1394
1055
  */
1395
- async findMany(options) {
1396
- this.logger.debug("Finding multiple records");
1397
- return this.model.findMany(options);
1056
+ createDbClientSync(tenant) {
1057
+ try {
1058
+ const databaseUrl = this.buildTenantDbUrl(tenant);
1059
+ const pool = new Pool2({
1060
+ connectionString: databaseUrl,
1061
+ max: tenant.connectionPoolSize || this.options.maxConnections || 10
1062
+ });
1063
+ const db = drizzle2({
1064
+ client: pool,
1065
+ schema: this.options.drizzleSchema
1066
+ });
1067
+ this.logger.log(`Connected to database for tenant: ${tenant.subdomain}`);
1068
+ return {
1069
+ pool,
1070
+ db
1071
+ };
1072
+ } catch (error) {
1073
+ this.logger.error(`Failed to create database connection for tenant: ${tenant.subdomain}`, error);
1074
+ throw new InternalServerErrorException2("Failed to connect to tenant database");
1075
+ }
1398
1076
  }
1399
1077
  /**
1400
- * Update a record by ID
1401
- *
1402
- * @param id - The record ID
1403
- * @param data - The data to update
1404
- * @returns Promise resolving to the updated record
1405
- *
1406
- * @example
1407
- * ```typescript
1408
- * const user = await userRepository.update('user-id-123', {
1409
- * firstName: 'Jane'
1410
- * });
1411
- * ```
1078
+ * Build connection URL for tenant (dedicated database)
1412
1079
  */
1413
- async update(id, data) {
1414
- this.logger.log(`Updating record with ID: ${id}`);
1415
- const idColumn = this.table.id;
1416
- const results = await this.db.update(this.table).set(data).where(eq2(idColumn, id)).returning();
1417
- return results[0];
1080
+ buildTenantDbUrl(tenant) {
1081
+ const { databaseHost, databasePort, databaseName, databaseUsername, databasePassword, databaseSslMode } = tenant;
1082
+ if (!databaseHost || !databaseName || !databaseUsername) {
1083
+ throw new Error(`Tenant ${tenant.subdomain} missing database configuration`);
1084
+ }
1085
+ const port = databasePort || 5432;
1086
+ const sslMode = databaseSslMode || "require";
1087
+ const connectionUrl = `postgresql://${databaseUsername}:${encodeURIComponent(databasePassword || "")}@${databaseHost}:${port}/${databaseName}?sslmode=${sslMode}`;
1088
+ this.logger.debug(`Tenant connection URL: ${this.maskPassword(connectionUrl)}`);
1089
+ return connectionUrl;
1418
1090
  }
1419
1091
  /**
1420
- * Update multiple records
1421
- *
1422
- * @param where - SQL condition to match records
1423
- * @param data - The data to update
1424
- * @returns Promise resolving to the count of updated records
1425
- *
1426
- * @example
1427
- * ```typescript
1428
- * import { eq } from 'drizzle-orm';
1429
- *
1430
- * const result = await userRepository.updateMany(
1431
- * eq(users.accountStatus, 'PENDING'),
1432
- * { accountStatus: 'ACTIVE' }
1433
- * );
1434
- * console.log(`Updated ${result.count} users`);
1435
- * ```
1092
+ * Build cache key for connection pooling
1436
1093
  */
1437
- async updateMany(where, data) {
1438
- this.logger.log("Updating multiple records");
1439
- const result = await this.db.update(this.table).set(data).where(where);
1440
- return {
1441
- count: result.rowCount ?? 0
1442
- };
1094
+ buildCacheKey(tenant) {
1095
+ return `${tenant.type}:${tenant.databaseName}@${tenant.databaseHost}`;
1443
1096
  }
1444
1097
  /**
1445
- * Delete a record by ID
1446
- *
1447
- * @param id - The record ID
1448
- * @returns Promise resolving to the deleted record
1449
- *
1450
- * @example
1451
- * ```typescript
1452
- * const user = await userRepository.delete('user-id-123');
1453
- * ```
1098
+ * Start periodic cleanup of idle connections
1454
1099
  */
1455
- async delete(id) {
1456
- this.logger.log(`Deleting record with ID: ${id}`);
1457
- const idColumn = this.table.id;
1458
- const results = await this.db.delete(this.table).where(eq2(idColumn, id)).returning();
1459
- return results[0];
1100
+ startConnectionCleaner() {
1101
+ const interval = this.options.connectionCacheTTL || 3e5;
1102
+ this.cleanupInterval = setInterval(() => {
1103
+ this.cleanupIdleConnections();
1104
+ }, interval);
1105
+ this.logger.log(`Connection cleanup scheduled every ${interval / 1e3} seconds`);
1460
1106
  }
1461
1107
  /**
1462
- * Delete multiple records
1463
- *
1464
- * @param where - SQL condition to match records
1465
- * @returns Promise resolving to the count of deleted records
1466
- *
1467
- * @example
1468
- * ```typescript
1469
- * import { lt } from 'drizzle-orm';
1470
- *
1471
- * const result = await userRepository.deleteMany(
1472
- * lt(users.createdAt, new Date('2020-01-01'))
1473
- * );
1474
- * console.log(`Deleted ${result.count} users`);
1475
- * ```
1108
+ * Clean up idle connections that haven't been used recently
1476
1109
  */
1477
- async deleteMany(where) {
1478
- this.logger.log("Deleting multiple records");
1479
- const result = await this.db.delete(this.table).where(where);
1480
- return {
1481
- count: result.rowCount ?? 0
1482
- };
1110
+ async cleanupIdleConnections() {
1111
+ const now = Date.now();
1112
+ const maxIdle = this.options.connectionCacheTTL || 3e5;
1113
+ let cleaned = 0;
1114
+ for (const [key, lastUsed] of this.clientLastUsed.entries()) {
1115
+ if (now - lastUsed > maxIdle) {
1116
+ const connection = this.clients.get(key);
1117
+ if (connection) {
1118
+ try {
1119
+ await connection.pool.end();
1120
+ this.logger.debug(`Cleaned up idle connection: ${key}`);
1121
+ } catch (error) {
1122
+ this.logger.error(`Error disconnecting idle client: ${key}`, error);
1123
+ }
1124
+ this.clients.delete(key);
1125
+ this.clientLastUsed.delete(key);
1126
+ cleaned++;
1127
+ }
1128
+ }
1129
+ }
1130
+ if (cleaned > 0) {
1131
+ this.logger.log(`Cleaned up ${cleaned} idle connections`);
1132
+ }
1483
1133
  }
1484
1134
  /**
1485
- * Count records
1486
- *
1487
- * @param where - Optional SQL condition to filter records
1488
- * @returns Promise resolving to the count of records
1489
- *
1490
- * @example
1491
- * ```typescript
1492
- * import { eq } from 'drizzle-orm';
1493
- *
1494
- * // Count all users
1495
- * const total = await userRepository.count();
1496
- *
1497
- * // Count active users
1498
- * const activeCount = await userRepository.count(
1499
- * eq(users.accountStatus, 'ACTIVE')
1500
- * );
1501
- * ```
1135
+ * Get current connection pool statistics
1502
1136
  */
1503
- async count(where) {
1504
- this.logger.debug("Counting records");
1505
- let query = this.db.select({
1506
- count: sql`count(*)::int`
1507
- }).from(this.table).$dynamic();
1508
- if (where) {
1509
- query = query.where(where);
1510
- }
1511
- const results = await query;
1512
- return results[0].count;
1137
+ getPoolStats() {
1138
+ return {
1139
+ activeConnections: this.clients.size,
1140
+ tenants: Array.from(this.clients.keys())
1141
+ };
1513
1142
  }
1514
1143
  /**
1515
- * Check if a record exists
1516
- *
1517
- * @param where - SQL condition to match records
1518
- * @returns Promise resolving to true if at least one record exists, false otherwise
1519
- *
1520
- * @example
1521
- * ```typescript
1522
- * import { eq } from 'drizzle-orm';
1523
- *
1524
- * const emailExists = await userRepository.exists(
1525
- * eq(users.email, 'user@example.com')
1526
- * );
1527
- * ```
1144
+ * Mask password in connection URL for logging
1528
1145
  */
1529
- async exists(where) {
1530
- const count = await this.count(where);
1531
- return count > 0;
1146
+ maskPassword(url) {
1147
+ return url.replace(/:([^@]+)@/, ":****@");
1532
1148
  }
1533
- };
1534
-
1535
- // src/database/repositories/tenant-base.repository.ts
1536
- import { Logger as Logger7 } from "@nestjs/common";
1537
- import { eq as eq3, sql as sql2, getTableName as getTableName2 } from "drizzle-orm";
1538
- var TenantBaseRepository = class {
1539
- static {
1540
- __name(this, "TenantBaseRepository");
1149
+ async onModuleDestroy() {
1150
+ if (this.cleanupInterval) {
1151
+ clearInterval(this.cleanupInterval);
1152
+ }
1153
+ this.logger.log(`Disconnecting ${this.clients.size} database connections`);
1154
+ const disconnectPromises = Array.from(this.clients.entries()).map(async ([key, connection]) => {
1155
+ try {
1156
+ await connection.pool.end();
1157
+ this.logger.debug(`Disconnected: ${key}`);
1158
+ } catch (error) {
1159
+ this.logger.error(`Error disconnecting client: ${key}`, error);
1160
+ }
1161
+ });
1162
+ await Promise.all(disconnectPromises);
1163
+ this.logger.log("All database connections closed");
1164
+ }
1165
+ };
1166
+ TenantDatabaseService = _ts_decorate9([
1167
+ Injectable7(),
1168
+ _ts_param3(0, Inject3(DATABASE_MODULE_OPTIONS)),
1169
+ _ts_metadata6("design:type", Function),
1170
+ _ts_metadata6("design:paramtypes", [
1171
+ typeof DatabaseModuleOptions === "undefined" ? Object : DatabaseModuleOptions,
1172
+ typeof TenantContextService === "undefined" ? Object : TenantContextService
1173
+ ])
1174
+ ], TenantDatabaseService);
1175
+
1176
+ // src/database/database.module.ts
1177
+ function _ts_decorate10(decorators, target, key, desc) {
1178
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1179
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1180
+ 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;
1181
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
1182
+ }
1183
+ __name(_ts_decorate10, "_ts_decorate");
1184
+ var DatabaseModule = class _DatabaseModule {
1185
+ static {
1186
+ __name(this, "DatabaseModule");
1187
+ }
1188
+ /**
1189
+ * Configure DatabaseModule for Gateway/HTTP mode (multi-tenant web servers)
1190
+ *
1191
+ * This mode is for API Gateways that handle HTTP requests:
1192
+ * - Automatically registers TenantContextInterceptor
1193
+ * - Extracts tenant from subdomain or x-tenant-id header
1194
+ * - Queries primary database for tenant configuration
1195
+ * - Provides PrimaryDatabaseService for tenant lookup
1196
+ *
1197
+ * @param options Async configuration options
1198
+ * @returns Dynamic module configuration with HTTP interceptor
1199
+ *
1200
+ * @example
1201
+ * DatabaseModule.forServer({
1202
+ * inject: [ConfigService],
1203
+ * useFactory: (config: ConfigService) => ({
1204
+ * primaryDb: {
1205
+ * host: config.get('PRIMARY_DB_HOST'),
1206
+ * port: config.get('PRIMARY_DB_PORT'),
1207
+ * username: config.get('PRIMARY_DB_USERNAME'),
1208
+ * password: config.get('PRIMARY_DB_PASSWORD'),
1209
+ * database: config.get('PRIMARY_DB_DATABASE'),
1210
+ * },
1211
+ * prismaClientConstructor: PrismaClient,
1212
+ * }),
1213
+ * })
1214
+ */
1215
+ static forServer(options) {
1216
+ return _DatabaseModule.createDynamicModule(options, "server");
1217
+ }
1218
+ /**
1219
+ * Configure DatabaseModule for Microservice/Messaging mode (RabbitMQ workers)
1220
+ *
1221
+ * This mode is for microservices that process messages from queues:
1222
+ * - Automatically registers MessageTenantContextInterceptor
1223
+ * - Extracts tenant from RabbitMQ message patterns
1224
+ * - No primary database needed (tenant comes from message context)
1225
+ *
1226
+ * @param options Async configuration options
1227
+ * @returns Dynamic module configuration with message interceptor
1228
+ *
1229
+ * @example
1230
+ * DatabaseModule.forMicroservice({
1231
+ * inject: [ConfigService],
1232
+ * useFactory: (config: ConfigService) => ({
1233
+ * prismaClientConstructor: PrismaClient,
1234
+ * }),
1235
+ * })
1236
+ */
1237
+ static forMicroservice(options) {
1238
+ return _DatabaseModule.createDynamicModule(options, "microservice");
1239
+ }
1240
+ /**
1241
+ * Internal helper to create dynamic module with conditional interceptor registration
1242
+ *
1243
+ * @param options Configuration options
1244
+ * @param mode Mode of operation (gateway or microservice)
1245
+ * @returns Dynamic module configuration
1246
+ */
1247
+ static createDynamicModule(options, mode) {
1248
+ const asyncProvider = {
1249
+ provide: DATABASE_MODULE_OPTIONS,
1250
+ useFactory: options.useFactory,
1251
+ inject: options.inject || []
1252
+ };
1253
+ const providers = [
1254
+ asyncProvider,
1255
+ TenantContextService2,
1256
+ PrimaryDatabaseService2,
1257
+ TenantDatabaseService
1258
+ ];
1259
+ if (mode === "server") {
1260
+ providers.push({
1261
+ provide: APP_INTERCEPTOR,
1262
+ useClass: TenantContextInterceptor
1263
+ });
1264
+ } else {
1265
+ providers.push({
1266
+ provide: APP_INTERCEPTOR,
1267
+ useClass: MessageTenantContextInterceptor
1268
+ });
1269
+ }
1270
+ return {
1271
+ module: _DatabaseModule,
1272
+ imports: [
1273
+ RequestModule
1274
+ ],
1275
+ providers,
1276
+ exports: [
1277
+ TenantDatabaseService,
1278
+ TenantContextService2,
1279
+ PrimaryDatabaseService2,
1280
+ asyncProvider
1281
+ ]
1282
+ };
1283
+ }
1284
+ };
1285
+ DatabaseModule = _ts_decorate10([
1286
+ Global3(),
1287
+ Module3({})
1288
+ ], DatabaseModule);
1289
+
1290
+ // src/database/decorators/tenant.decorator.ts
1291
+ import { createParamDecorator } from "@nestjs/common";
1292
+ var Tenant = createParamDecorator((_data, ctx) => {
1293
+ const request = ctx.switchToHttp().getRequest();
1294
+ const tenantContext = request.app?.get?.(TenantContextService2);
1295
+ if (!tenantContext) {
1296
+ throw new Error("TenantContextService not found.");
1297
+ }
1298
+ return tenantContext.getTenant();
1299
+ });
1300
+
1301
+ // src/database/repositories/primary-base.repository.ts
1302
+ import { Logger as Logger7 } from "@nestjs/common";
1303
+ import { eq as eq2, getTableName, sql } from "drizzle-orm";
1304
+ function snakeToCamel(str) {
1305
+ return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
1306
+ }
1307
+ __name(snakeToCamel, "snakeToCamel");
1308
+ var PrimaryBaseRepository = class {
1309
+ static {
1310
+ __name(this, "PrimaryBaseRepository");
1541
1311
  }
1542
1312
  database;
1543
1313
  table;
1544
1314
  logger;
1545
1315
  /**
1546
1316
  * The table name extracted from the Drizzle table at runtime.
1547
- * Used to access the query API for this repository's table.
1317
+ * Stored in camelCase to match Drizzle's query object keys.
1318
+ * Example: 'email_verifications' -> 'emailVerifications'
1548
1319
  */
1549
1320
  tableName;
1550
1321
  /**
@@ -1556,42 +1327,52 @@ var TenantBaseRepository = class {
1556
1327
  return this.database.drizzleClient;
1557
1328
  }
1558
1329
  /**
1559
- * Model query API for THIS repository's table (Prisma-like syntax)
1560
- * Scoped to only the table this repository manages
1330
+ * Model query API for THIS repository's table (Drizzle v2 relational queries)
1331
+ * Scoped to only the table this repository manages.
1332
+ * Returns a type-safe wrapper around Drizzle's RelationalQueryBuilder.
1561
1333
  *
1562
1334
  * @example
1563
1335
  * ```typescript
1564
- * // Use relational queries with type safety
1565
- * const product = await this.model.findFirst({
1566
- * where: eq(products.id, id),
1567
- * with: { category: true, variants: true }
1336
+ * // Use relational queries with v2 object-based where syntax
1337
+ * const user = await this.model.findFirst({
1338
+ * where: { id },
1339
+ * with: { posts: true, profile: true }
1568
1340
  * });
1569
1341
  * ```
1570
1342
  */
1571
1343
  get model() {
1572
- return this.database.drizzleClient.query[this.tableName];
1344
+ const query = this.database.drizzleClient.query;
1345
+ const queryKeys = Object.keys(query || {});
1346
+ this.logger.debug(`Looking for '${this.tableName}' in query keys: [${queryKeys.join(", ")}]`);
1347
+ const model = query[this.tableName];
1348
+ if (!model) {
1349
+ this.logger.error(`Table '${this.tableName}' not found in query object. Available: [${queryKeys.join(", ")}]`);
1350
+ }
1351
+ return model;
1573
1352
  }
1574
1353
  /**
1575
1354
  * Create a new repository instance
1576
1355
  *
1577
- * @param database - The tenant database service
1356
+ * @param database - The primary database service
1578
1357
  * @param table - The Drizzle table schema object
1579
1358
  *
1580
1359
  * @example
1581
1360
  * ```typescript
1582
- * import { products } from '@/db/schema';
1361
+ * import { users } from '@/db/schema';
1583
1362
  *
1584
- * constructor(database: TenantDatabaseService) {
1585
- * super(database, products);
1363
+ * constructor(database: PrimaryDatabaseService) {
1364
+ * super(database, users);
1586
1365
  * }
1587
1366
  * ```
1588
1367
  */
1589
1368
  constructor(database, table) {
1590
1369
  this.database = database;
1591
1370
  this.table = table;
1592
- this.tableName = getTableName2(table);
1371
+ const dbTableName = getTableName(table);
1372
+ this.tableName = snakeToCamel(dbTableName);
1593
1373
  this.logger = new Logger7(this.constructor.name);
1594
1374
  this.logger.debug(`Initialized ${this.constructor.name}`);
1375
+ this.logger.debug(`Table name: '${dbTableName}' -> query key: '${this.tableName}'`);
1595
1376
  }
1596
1377
  /**
1597
1378
  * Create a new record
@@ -1601,10 +1382,9 @@ var TenantBaseRepository = class {
1601
1382
  *
1602
1383
  * @example
1603
1384
  * ```typescript
1604
- * const product = await productRepository.create({
1605
- * name: 'Widget',
1606
- * sku: 'WDG-001',
1607
- * price: 9.99
1385
+ * const user = await userRepository.create({
1386
+ * email: 'user@example.com',
1387
+ * firstName: 'John'
1608
1388
  * });
1609
1389
  * ```
1610
1390
  */
@@ -1617,74 +1397,81 @@ var TenantBaseRepository = class {
1617
1397
  * Find a single record by ID
1618
1398
  *
1619
1399
  * @param id - The record ID
1620
- * @returns Promise resolving to the record or null if not found
1400
+ * @returns Promise resolving to the record or undefined if not found
1621
1401
  *
1622
1402
  * @example
1623
1403
  * ```typescript
1624
- * const product = await productRepository.findById('product-id-123');
1404
+ * const user = await userRepository.findById('user-id-123');
1625
1405
  * ```
1626
1406
  */
1627
1407
  async findById(id) {
1628
1408
  this.logger.debug(`Finding record by ID: ${id}`);
1629
- const idColumn = this.table.id;
1630
- const results = await this.db.select().from(this.table).where(eq3(idColumn, id)).limit(1);
1631
- return results[0] ?? null;
1409
+ return this.model.findFirst({
1410
+ where: {
1411
+ id
1412
+ }
1413
+ });
1632
1414
  }
1633
1415
  /**
1634
- * Find a single record with custom where clause
1416
+ * Find a single record with custom where clause (Drizzle v2 object-based syntax)
1635
1417
  *
1636
- * @param where - SQL condition
1637
- * @returns Promise resolving to the record or null if not found
1418
+ * @param where - Object-based filter condition
1419
+ * @returns Promise resolving to the record or undefined if not found
1638
1420
  *
1639
1421
  * @example
1640
1422
  * ```typescript
1641
- * import { eq } from 'drizzle-orm';
1642
- * const product = await productRepository.findOne(eq(products.sku, 'WDG-001'));
1423
+ * // Simple equality
1424
+ * const user = await userRepository.findOne({ email: 'user@example.com' });
1425
+ *
1426
+ * // With operators
1427
+ * const user = await userRepository.findOne({ age: { gte: 18 } });
1428
+ *
1429
+ * // Multiple conditions (AND)
1430
+ * const user = await userRepository.findOne({
1431
+ * email: 'user@example.com',
1432
+ * status: 'ACTIVE'
1433
+ * });
1643
1434
  * ```
1644
1435
  */
1645
1436
  async findOne(where) {
1646
1437
  this.logger.debug("Finding record with custom query");
1647
- const results = await this.db.select().from(this.table).where(where).limit(1);
1648
- return results[0] ?? null;
1438
+ return this.model.findFirst({
1439
+ where
1440
+ });
1649
1441
  }
1650
1442
  /**
1651
- * Find multiple records
1443
+ * Find multiple records (Drizzle v2 object-based syntax)
1652
1444
  *
1653
1445
  * @param options - Query options (where, orderBy, limit, offset)
1654
1446
  * @returns Promise resolving to an array of records
1655
1447
  *
1656
1448
  * @example
1657
1449
  * ```typescript
1658
- * import { eq, desc } from 'drizzle-orm';
1659
- *
1660
- * // Find all products
1661
- * const products = await productRepository.findMany();
1450
+ * // Find all users
1451
+ * const users = await userRepository.findMany();
1662
1452
  *
1663
- * // Find with filtering and pagination
1664
- * const products = await productRepository.findMany({
1665
- * where: eq(products.status, 'ACTIVE'),
1666
- * orderBy: desc(products.createdAt),
1453
+ * // Find with filtering and pagination (v2 object syntax)
1454
+ * const users = await userRepository.findMany({
1455
+ * where: { accountStatus: 'ACTIVE' },
1456
+ * orderBy: { createdAt: 'desc' },
1667
1457
  * limit: 10,
1668
1458
  * offset: 0
1669
1459
  * });
1460
+ *
1461
+ * // Multiple conditions
1462
+ * const users = await userRepository.findMany({
1463
+ * where: {
1464
+ * AND: [
1465
+ * { status: 'ACTIVE' },
1466
+ * { age: { gte: 18 } }
1467
+ * ]
1468
+ * }
1469
+ * });
1670
1470
  * ```
1671
1471
  */
1672
1472
  async findMany(options) {
1673
1473
  this.logger.debug("Finding multiple records");
1674
- let query = this.db.select().from(this.table).$dynamic();
1675
- if (options?.where) {
1676
- query = query.where(options.where);
1677
- }
1678
- if (options?.orderBy) {
1679
- query = query.orderBy(options.orderBy);
1680
- }
1681
- if (options?.limit) {
1682
- query = query.limit(options.limit);
1683
- }
1684
- if (options?.offset) {
1685
- query = query.offset(options.offset);
1686
- }
1687
- return await query;
1474
+ return this.model.findMany(options);
1688
1475
  }
1689
1476
  /**
1690
1477
  * Update a record by ID
@@ -1695,15 +1482,15 @@ var TenantBaseRepository = class {
1695
1482
  *
1696
1483
  * @example
1697
1484
  * ```typescript
1698
- * const product = await productRepository.update('product-id-123', {
1699
- * price: 12.99
1485
+ * const user = await userRepository.update('user-id-123', {
1486
+ * firstName: 'Jane'
1700
1487
  * });
1701
1488
  * ```
1702
1489
  */
1703
1490
  async update(id, data) {
1704
1491
  this.logger.log(`Updating record with ID: ${id}`);
1705
1492
  const idColumn = this.table.id;
1706
- const results = await this.db.update(this.table).set(data).where(eq3(idColumn, id)).returning();
1493
+ const results = await this.db.update(this.table).set(data).where(eq2(idColumn, id)).returning();
1707
1494
  return results[0];
1708
1495
  }
1709
1496
  /**
@@ -1717,11 +1504,11 @@ var TenantBaseRepository = class {
1717
1504
  * ```typescript
1718
1505
  * import { eq } from 'drizzle-orm';
1719
1506
  *
1720
- * const result = await productRepository.updateMany(
1721
- * eq(products.status, 'PENDING'),
1722
- * { status: 'ACTIVE' }
1507
+ * const result = await userRepository.updateMany(
1508
+ * eq(users.accountStatus, 'PENDING'),
1509
+ * { accountStatus: 'ACTIVE' }
1723
1510
  * );
1724
- * console.log(`Updated ${result.count} products`);
1511
+ * console.log(`Updated ${result.count} users`);
1725
1512
  * ```
1726
1513
  */
1727
1514
  async updateMany(where, data) {
@@ -1739,13 +1526,13 @@ var TenantBaseRepository = class {
1739
1526
  *
1740
1527
  * @example
1741
1528
  * ```typescript
1742
- * const product = await productRepository.delete('product-id-123');
1529
+ * const user = await userRepository.delete('user-id-123');
1743
1530
  * ```
1744
1531
  */
1745
1532
  async delete(id) {
1746
1533
  this.logger.log(`Deleting record with ID: ${id}`);
1747
1534
  const idColumn = this.table.id;
1748
- const results = await this.db.delete(this.table).where(eq3(idColumn, id)).returning();
1535
+ const results = await this.db.delete(this.table).where(eq2(idColumn, id)).returning();
1749
1536
  return results[0];
1750
1537
  }
1751
1538
  /**
@@ -1758,10 +1545,10 @@ var TenantBaseRepository = class {
1758
1545
  * ```typescript
1759
1546
  * import { lt } from 'drizzle-orm';
1760
1547
  *
1761
- * const result = await productRepository.deleteMany(
1762
- * lt(products.createdAt, new Date('2020-01-01'))
1548
+ * const result = await userRepository.deleteMany(
1549
+ * lt(users.createdAt, new Date('2020-01-01'))
1763
1550
  * );
1764
- * console.log(`Deleted ${result.count} products`);
1551
+ * console.log(`Deleted ${result.count} users`);
1765
1552
  * ```
1766
1553
  */
1767
1554
  async deleteMany(where) {
@@ -1781,19 +1568,19 @@ var TenantBaseRepository = class {
1781
1568
  * ```typescript
1782
1569
  * import { eq } from 'drizzle-orm';
1783
1570
  *
1784
- * // Count all products
1785
- * const total = await productRepository.count();
1571
+ * // Count all users
1572
+ * const total = await userRepository.count();
1786
1573
  *
1787
- * // Count active products
1788
- * const activeCount = await productRepository.count(
1789
- * eq(products.status, 'ACTIVE')
1574
+ * // Count active users
1575
+ * const activeCount = await userRepository.count(
1576
+ * eq(users.accountStatus, 'ACTIVE')
1790
1577
  * );
1791
1578
  * ```
1792
1579
  */
1793
1580
  async count(where) {
1794
1581
  this.logger.debug("Counting records");
1795
1582
  let query = this.db.select({
1796
- count: sql2`count(*)::int`
1583
+ count: sql`count(*)::int`
1797
1584
  }).from(this.table).$dynamic();
1798
1585
  if (where) {
1799
1586
  query = query.where(where);
@@ -1811,8 +1598,8 @@ var TenantBaseRepository = class {
1811
1598
  * ```typescript
1812
1599
  * import { eq } from 'drizzle-orm';
1813
1600
  *
1814
- * const skuExists = await productRepository.exists(
1815
- * eq(products.sku, 'WDG-001')
1601
+ * const emailExists = await userRepository.exists(
1602
+ * eq(users.email, 'user@example.com')
1816
1603
  * );
1817
1604
  * ```
1818
1605
  */
@@ -1822,218 +1609,302 @@ var TenantBaseRepository = class {
1822
1609
  }
1823
1610
  };
1824
1611
 
1825
- // src/database/decorators/tenant.decorator.ts
1826
- import { createParamDecorator } from "@nestjs/common";
1827
- var Tenant = createParamDecorator((data, ctx) => {
1828
- const request = ctx.switchToHttp().getRequest();
1829
- const tenantContext = request.app?.get?.(TenantContextService);
1830
- if (!tenantContext) {
1831
- throw new Error("TenantContextService not found.");
1832
- }
1833
- return tenantContext.getTenant();
1834
- });
1835
-
1836
- // src/auth/decorators/onboarding.decorator.ts
1837
- import { SetMetadata } from "@nestjs/common";
1838
- var Onboarding = /* @__PURE__ */ __name(() => SetMetadata("isOnboarding", true), "Onboarding");
1839
-
1840
- // src/auth/decorators/public.decorator.ts
1841
- import { SetMetadata as SetMetadata2 } from "@nestjs/common";
1842
- var Public = /* @__PURE__ */ __name(() => SetMetadata2("isPublic", true), "Public");
1843
-
1844
- // src/http/http.module.ts
1845
- import { Module as Module4 } from "@nestjs/common";
1846
-
1847
- // src/http/guards/csrf.guard.ts
1848
- import { ForbiddenException, Injectable as Injectable8, Logger as Logger8 } from "@nestjs/common";
1849
- import { Reflector as Reflector3 } from "@nestjs/core";
1850
- function _ts_decorate11(decorators, target, key, desc) {
1851
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1852
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1853
- 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;
1854
- return c > 3 && r && Object.defineProperty(target, key, r), r;
1855
- }
1856
- __name(_ts_decorate11, "_ts_decorate");
1857
- function _ts_metadata7(k, v) {
1858
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
1859
- }
1860
- __name(_ts_metadata7, "_ts_metadata");
1861
- var CsrfGuard = class _CsrfGuard {
1612
+ // src/database/repositories/tenant-base.repository.ts
1613
+ import { Logger as Logger8 } from "@nestjs/common";
1614
+ import { eq as eq3, getTableName as getTableName2, sql as sql2 } from "drizzle-orm";
1615
+ var TenantBaseRepository = class {
1862
1616
  static {
1863
- __name(this, "CsrfGuard");
1864
- }
1865
- reflector;
1866
- logger = new Logger8(_CsrfGuard.name);
1867
- constructor(reflector) {
1868
- this.reflector = reflector;
1617
+ __name(this, "TenantBaseRepository");
1869
1618
  }
1870
- async canActivate(context) {
1871
- const request = context.switchToHttp().getRequest();
1872
- const reply = context.switchToHttp().getResponse();
1873
- const safeMethods = [
1874
- "GET",
1875
- "HEAD",
1876
- "OPTIONS"
1877
- ];
1878
- if (safeMethods.includes(request.method)) {
1879
- return true;
1880
- }
1881
- try {
1882
- const fastifyInstance = request.server;
1883
- if (!fastifyInstance.csrfProtection) {
1884
- this.logger.error("CSRF protection plugin not found. Ensure @fastify/csrf-protection is registered.");
1885
- throw new ForbiddenException("CSRF protection not configured");
1886
- }
1887
- await new Promise((resolve, reject) => {
1888
- fastifyInstance.csrfProtection(request, reply, (err) => {
1889
- if (err) {
1890
- reject(err);
1891
- } else {
1892
- resolve();
1893
- }
1894
- });
1895
- });
1896
- this.logger.debug(`CSRF validation successful for ${request.method} ${request.url}`);
1897
- return true;
1898
- } catch (error) {
1899
- this.logger.warn(`CSRF validation failed for ${request.method} ${request.url}: ${error instanceof Error ? error.message : "Unknown error"}`);
1900
- throw new ForbiddenException({
1901
- errors: [
1902
- {
1903
- field: "csrf",
1904
- message: "Invalid or missing CSRF token"
1905
- }
1906
- ],
1907
- message: "CSRF validation failed"
1908
- });
1909
- }
1619
+ database;
1620
+ table;
1621
+ logger;
1622
+ /**
1623
+ * The table name extracted from the Drizzle table at runtime.
1624
+ * Used to access the query API for this repository's table.
1625
+ */
1626
+ tableName;
1627
+ /**
1628
+ * Lazy getter for Drizzle client.
1629
+ * Accesses the client from the database service only when needed,
1630
+ * avoiding initialization timing issues with NestJS lifecycle.
1631
+ */
1632
+ get db() {
1633
+ return this.database.drizzleClient;
1910
1634
  }
1911
- };
1912
- CsrfGuard = _ts_decorate11([
1913
- Injectable8(),
1914
- _ts_metadata7("design:type", Function),
1915
- _ts_metadata7("design:paramtypes", [
1916
- typeof Reflector3 === "undefined" ? Object : Reflector3
1917
- ])
1918
- ], CsrfGuard);
1919
-
1920
- // src/http/http.module.ts
1921
- function _ts_decorate12(decorators, target, key, desc) {
1922
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1923
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1924
- 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;
1925
- return c > 3 && r && Object.defineProperty(target, key, r), r;
1926
- }
1927
- __name(_ts_decorate12, "_ts_decorate");
1928
- var HttpModule = class {
1929
- static {
1930
- __name(this, "HttpModule");
1635
+ /**
1636
+ * Model query API for THIS repository's table (Prisma-like syntax)
1637
+ * Scoped to only the table this repository manages
1638
+ *
1639
+ * @example
1640
+ * ```typescript
1641
+ * // Use relational queries with type safety
1642
+ * const product = await this.model.findFirst({
1643
+ * where: eq(products.id, id),
1644
+ * with: { category: true, variants: true }
1645
+ * });
1646
+ * ```
1647
+ */
1648
+ get model() {
1649
+ return this.database.drizzleClient.query[this.tableName];
1931
1650
  }
1932
- };
1933
- HttpModule = _ts_decorate12([
1934
- Module4({
1935
- providers: [
1936
- CsrfGuard
1937
- ],
1938
- exports: [
1939
- CsrfGuard
1940
- ]
1941
- })
1942
- ], HttpModule);
1943
-
1944
- // src/filters/http-exception.filter.ts
1945
- import { Catch, HttpException, HttpStatus, Logger as Logger9 } from "@nestjs/common";
1946
- function _ts_decorate13(decorators, target, key, desc) {
1947
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1948
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1949
- 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;
1950
- return c > 3 && r && Object.defineProperty(target, key, r), r;
1951
- }
1952
- __name(_ts_decorate13, "_ts_decorate");
1953
- function getHttpStatusTitle(status) {
1954
- const enumKey = Object.entries(HttpStatus).find(([key, value]) => value === status && isNaN(Number(key)))?.[0];
1955
- if (!enumKey) {
1956
- return "Error";
1651
+ /**
1652
+ * Create a new repository instance
1653
+ *
1654
+ * @param database - The tenant database service
1655
+ * @param table - The Drizzle table schema object
1656
+ *
1657
+ * @example
1658
+ * ```typescript
1659
+ * import { products } from '@/db/schema';
1660
+ *
1661
+ * constructor(database: TenantDatabaseService) {
1662
+ * super(database, products);
1663
+ * }
1664
+ * ```
1665
+ */
1666
+ constructor(database, table) {
1667
+ this.database = database;
1668
+ this.table = table;
1669
+ this.tableName = getTableName2(table);
1670
+ this.logger = new Logger8(this.constructor.name);
1671
+ this.logger.debug(`Initialized ${this.constructor.name}`);
1957
1672
  }
1958
- return enumKey.split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
1959
- }
1960
- __name(getHttpStatusTitle, "getHttpStatusTitle");
1961
- var HttpExceptionFilter = class _HttpExceptionFilter {
1962
- static {
1963
- __name(this, "HttpExceptionFilter");
1673
+ /**
1674
+ * Create a new record
1675
+ *
1676
+ * @param data - The data to create the record with
1677
+ * @returns Promise resolving to the created record
1678
+ *
1679
+ * @example
1680
+ * ```typescript
1681
+ * const product = await productRepository.create({
1682
+ * name: 'Widget',
1683
+ * sku: 'WDG-001',
1684
+ * price: 9.99
1685
+ * });
1686
+ * ```
1687
+ */
1688
+ async create(data) {
1689
+ this.logger.log("Creating record");
1690
+ const results = await this.db.insert(this.table).values(data).returning();
1691
+ return results[0];
1964
1692
  }
1965
- logger = new Logger9(_HttpExceptionFilter.name);
1966
- catch(exception, host) {
1967
- const ctx = host.switchToHttp();
1968
- const response = ctx.getResponse();
1969
- let status = HttpStatus.INTERNAL_SERVER_ERROR;
1970
- let errors = [];
1971
- let detail = "Internal server error";
1972
- if (exception instanceof HttpException) {
1973
- status = exception.getStatus();
1974
- const exceptionResponse = exception.getResponse();
1975
- if (typeof exceptionResponse === "object" && exceptionResponse !== null) {
1976
- const responseObj = exceptionResponse;
1977
- if ("errors" in responseObj && Array.isArray(responseObj.errors)) {
1978
- errors = responseObj.errors;
1979
- detail = ("detail" in responseObj ? responseObj.detail : void 0) || exception.message;
1980
- } else if ("message" in responseObj && Array.isArray(responseObj.message)) {
1981
- errors = responseObj.message.map((msg) => {
1982
- if (typeof msg === "object" && "property" in msg && "constraints" in msg) {
1983
- const constraintValues = Object.values(msg.constraints);
1984
- return {
1985
- field: msg.property,
1986
- message: constraintValues[0] ?? "Validation failed"
1987
- };
1988
- }
1989
- return {
1990
- message: typeof msg === "string" ? msg : JSON.stringify(msg)
1991
- };
1992
- });
1993
- detail = "Validation failed";
1994
- } else if ("message" in responseObj) {
1995
- const message = responseObj.message;
1996
- errors = [
1997
- {
1998
- message: Array.isArray(message) ? message.join(", ") : message
1999
- }
2000
- ];
2001
- detail = ("error" in responseObj ? responseObj.error : void 0) || exception.message;
2002
- }
2003
- } else if (typeof exceptionResponse === "string") {
2004
- errors = [
2005
- {
2006
- message: exceptionResponse
2007
- }
2008
- ];
2009
- detail = exceptionResponse;
2010
- }
2011
- } else {
2012
- const errorMessage = exception instanceof Error ? exception.message : "Unknown error";
2013
- const stack = exception instanceof Error ? exception.stack : void 0;
2014
- this.logger.error(`Unexpected error: ${errorMessage}`, stack);
2015
- errors = [
2016
- {
2017
- message: "An unexpected error occurred"
2018
- }
2019
- ];
1693
+ /**
1694
+ * Find a single record by ID
1695
+ *
1696
+ * @param id - The record ID
1697
+ * @returns Promise resolving to the record or null if not found
1698
+ *
1699
+ * @example
1700
+ * ```typescript
1701
+ * const product = await productRepository.findById('product-id-123');
1702
+ * ```
1703
+ */
1704
+ async findById(id) {
1705
+ this.logger.debug(`Finding record by ID: ${id}`);
1706
+ const idColumn = this.table.id;
1707
+ const results = await this.db.select().from(this.table).where(eq3(idColumn, id)).limit(1);
1708
+ return results[0] ?? null;
1709
+ }
1710
+ /**
1711
+ * Find a single record with custom where clause
1712
+ *
1713
+ * @param where - SQL condition
1714
+ * @returns Promise resolving to the record or null if not found
1715
+ *
1716
+ * @example
1717
+ * ```typescript
1718
+ * import { eq } from 'drizzle-orm';
1719
+ * const product = await productRepository.findOne(eq(products.sku, 'WDG-001'));
1720
+ * ```
1721
+ */
1722
+ async findOne(where) {
1723
+ this.logger.debug("Finding record with custom query");
1724
+ const results = await this.db.select().from(this.table).where(where).limit(1);
1725
+ return results[0] ?? null;
1726
+ }
1727
+ /**
1728
+ * Find multiple records
1729
+ *
1730
+ * @param options - Query options (where, orderBy, limit, offset)
1731
+ * @returns Promise resolving to an array of records
1732
+ *
1733
+ * @example
1734
+ * ```typescript
1735
+ * import { eq, desc } from 'drizzle-orm';
1736
+ *
1737
+ * // Find all products
1738
+ * const products = await productRepository.findMany();
1739
+ *
1740
+ * // Find with filtering and pagination
1741
+ * const products = await productRepository.findMany({
1742
+ * where: eq(products.status, 'ACTIVE'),
1743
+ * orderBy: desc(products.createdAt),
1744
+ * limit: 10,
1745
+ * offset: 0
1746
+ * });
1747
+ * ```
1748
+ */
1749
+ async findMany(options) {
1750
+ this.logger.debug("Finding multiple records");
1751
+ let query = this.db.select().from(this.table).$dynamic();
1752
+ if (options?.where) {
1753
+ query = query.where(options.where);
2020
1754
  }
2021
- const problemDetails = {
2022
- title: getHttpStatusTitle(status),
2023
- status,
2024
- detail,
2025
- errors
1755
+ if (options?.orderBy) {
1756
+ query = query.orderBy(options.orderBy);
1757
+ }
1758
+ if (options?.limit) {
1759
+ query = query.limit(options.limit);
1760
+ }
1761
+ if (options?.offset) {
1762
+ query = query.offset(options.offset);
1763
+ }
1764
+ return await query;
1765
+ }
1766
+ /**
1767
+ * Update a record by ID
1768
+ *
1769
+ * @param id - The record ID
1770
+ * @param data - The data to update
1771
+ * @returns Promise resolving to the updated record
1772
+ *
1773
+ * @example
1774
+ * ```typescript
1775
+ * const product = await productRepository.update('product-id-123', {
1776
+ * price: 12.99
1777
+ * });
1778
+ * ```
1779
+ */
1780
+ async update(id, data) {
1781
+ this.logger.log(`Updating record with ID: ${id}`);
1782
+ const idColumn = this.table.id;
1783
+ const results = await this.db.update(this.table).set(data).where(eq3(idColumn, id)).returning();
1784
+ return results[0];
1785
+ }
1786
+ /**
1787
+ * Update multiple records
1788
+ *
1789
+ * @param where - SQL condition to match records
1790
+ * @param data - The data to update
1791
+ * @returns Promise resolving to the count of updated records
1792
+ *
1793
+ * @example
1794
+ * ```typescript
1795
+ * import { eq } from 'drizzle-orm';
1796
+ *
1797
+ * const result = await productRepository.updateMany(
1798
+ * eq(products.status, 'PENDING'),
1799
+ * { status: 'ACTIVE' }
1800
+ * );
1801
+ * console.log(`Updated ${result.count} products`);
1802
+ * ```
1803
+ */
1804
+ async updateMany(where, data) {
1805
+ this.logger.log("Updating multiple records");
1806
+ const result = await this.db.update(this.table).set(data).where(where);
1807
+ return {
1808
+ count: result.rowCount ?? 0
2026
1809
  };
2027
- response.status(status).send(problemDetails);
1810
+ }
1811
+ /**
1812
+ * Delete a record by ID
1813
+ *
1814
+ * @param id - The record ID
1815
+ * @returns Promise resolving to the deleted record
1816
+ *
1817
+ * @example
1818
+ * ```typescript
1819
+ * const product = await productRepository.delete('product-id-123');
1820
+ * ```
1821
+ */
1822
+ async delete(id) {
1823
+ this.logger.log(`Deleting record with ID: ${id}`);
1824
+ const idColumn = this.table.id;
1825
+ const results = await this.db.delete(this.table).where(eq3(idColumn, id)).returning();
1826
+ return results[0];
1827
+ }
1828
+ /**
1829
+ * Delete multiple records
1830
+ *
1831
+ * @param where - SQL condition to match records
1832
+ * @returns Promise resolving to the count of deleted records
1833
+ *
1834
+ * @example
1835
+ * ```typescript
1836
+ * import { lt } from 'drizzle-orm';
1837
+ *
1838
+ * const result = await productRepository.deleteMany(
1839
+ * lt(products.createdAt, new Date('2020-01-01'))
1840
+ * );
1841
+ * console.log(`Deleted ${result.count} products`);
1842
+ * ```
1843
+ */
1844
+ async deleteMany(where) {
1845
+ this.logger.log("Deleting multiple records");
1846
+ const result = await this.db.delete(this.table).where(where);
1847
+ return {
1848
+ count: result.rowCount ?? 0
1849
+ };
1850
+ }
1851
+ /**
1852
+ * Count records
1853
+ *
1854
+ * @param where - Optional SQL condition to filter records
1855
+ * @returns Promise resolving to the count of records
1856
+ *
1857
+ * @example
1858
+ * ```typescript
1859
+ * import { eq } from 'drizzle-orm';
1860
+ *
1861
+ * // Count all products
1862
+ * const total = await productRepository.count();
1863
+ *
1864
+ * // Count active products
1865
+ * const activeCount = await productRepository.count(
1866
+ * eq(products.status, 'ACTIVE')
1867
+ * );
1868
+ * ```
1869
+ */
1870
+ async count(where) {
1871
+ this.logger.debug("Counting records");
1872
+ let query = this.db.select({
1873
+ count: sql2`count(*)::int`
1874
+ }).from(this.table).$dynamic();
1875
+ if (where) {
1876
+ query = query.where(where);
1877
+ }
1878
+ const results = await query;
1879
+ return results[0].count;
1880
+ }
1881
+ /**
1882
+ * Check if a record exists
1883
+ *
1884
+ * @param where - SQL condition to match records
1885
+ * @returns Promise resolving to true if at least one record exists, false otherwise
1886
+ *
1887
+ * @example
1888
+ * ```typescript
1889
+ * import { eq } from 'drizzle-orm';
1890
+ *
1891
+ * const skuExists = await productRepository.exists(
1892
+ * eq(products.sku, 'WDG-001')
1893
+ * );
1894
+ * ```
1895
+ */
1896
+ async exists(where) {
1897
+ const count = await this.count(where);
1898
+ return count > 0;
2028
1899
  }
2029
1900
  };
2030
- HttpExceptionFilter = _ts_decorate13([
2031
- Catch()
2032
- ], HttpExceptionFilter);
1901
+
1902
+ // src/exceptions/bad-gateway.exception.ts
1903
+ import { HttpStatus as HttpStatus2 } from "@nestjs/common";
2033
1904
 
2034
1905
  // src/exceptions/base-field.exception.ts
2035
- import { HttpException as HttpException2, HttpStatus as HttpStatus2 } from "@nestjs/common";
2036
- var BaseFieldException = class extends HttpException2 {
1906
+ import { HttpException, HttpStatus } from "@nestjs/common";
1907
+ var BaseFieldException = class extends HttpException {
2037
1908
  static {
2038
1909
  __name(this, "BaseFieldException");
2039
1910
  }
@@ -2043,7 +1914,7 @@ var BaseFieldException = class extends HttpException2 {
2043
1914
  let finalDetail;
2044
1915
  if (Array.isArray(statusOrMessageOrErrors)) {
2045
1916
  errors = statusOrMessageOrErrors;
2046
- httpStatus = messageOrStatus || HttpStatus2.BAD_REQUEST;
1917
+ httpStatus = messageOrStatus || HttpStatus.BAD_REQUEST;
2047
1918
  finalDetail = typeof statusOrDetail === "string" ? statusOrDetail : void 0;
2048
1919
  } else if (typeof statusOrMessageOrErrors === "string" && typeof messageOrStatus === "string") {
2049
1920
  errors = [
@@ -2081,8 +1952,26 @@ var BaseFieldException = class extends HttpException2 {
2081
1952
  }
2082
1953
  };
2083
1954
 
2084
- // src/exceptions/bad-request.exception.ts
2085
- import { HttpStatus as HttpStatus3 } from "@nestjs/common";
1955
+ // src/exceptions/bad-gateway.exception.ts
1956
+ var BadGatewayException = class extends BaseFieldException {
1957
+ static {
1958
+ __name(this, "BadGatewayException");
1959
+ }
1960
+ constructor(messageOrField, fieldMessageOrDetail, detail) {
1961
+ if (Array.isArray(messageOrField)) {
1962
+ super(messageOrField, HttpStatus2.BAD_GATEWAY, fieldMessageOrDetail);
1963
+ } else if (detail !== void 0) {
1964
+ super(messageOrField, fieldMessageOrDetail, HttpStatus2.BAD_GATEWAY, detail);
1965
+ } else if (fieldMessageOrDetail) {
1966
+ super(messageOrField, fieldMessageOrDetail, HttpStatus2.BAD_GATEWAY);
1967
+ } else {
1968
+ super(messageOrField, HttpStatus2.BAD_GATEWAY);
1969
+ }
1970
+ }
1971
+ };
1972
+
1973
+ // src/exceptions/bad-request.exception.ts
1974
+ import { HttpStatus as HttpStatus3 } from "@nestjs/common";
2086
1975
  var BadRequestException = class extends BaseFieldException {
2087
1976
  static {
2088
1977
  __name(this, "BadRequestException");
@@ -2100,28 +1989,28 @@ var BadRequestException = class extends BaseFieldException {
2100
1989
  }
2101
1990
  };
2102
1991
 
2103
- // src/exceptions/unauthorized.exception.ts
1992
+ // src/exceptions/conflict.exception.ts
2104
1993
  import { HttpStatus as HttpStatus4 } from "@nestjs/common";
2105
- var UnauthorizedException4 = class extends BaseFieldException {
1994
+ var ConflictException = class extends BaseFieldException {
2106
1995
  static {
2107
- __name(this, "UnauthorizedException");
1996
+ __name(this, "ConflictException");
2108
1997
  }
2109
1998
  constructor(messageOrField, fieldMessageOrDetail, detail) {
2110
1999
  if (Array.isArray(messageOrField)) {
2111
- super(messageOrField, HttpStatus4.UNAUTHORIZED, fieldMessageOrDetail);
2000
+ super(messageOrField, HttpStatus4.CONFLICT, fieldMessageOrDetail);
2112
2001
  } else if (detail !== void 0) {
2113
- super(messageOrField, fieldMessageOrDetail, HttpStatus4.UNAUTHORIZED, detail);
2002
+ super(messageOrField, fieldMessageOrDetail, HttpStatus4.CONFLICT, detail);
2114
2003
  } else if (fieldMessageOrDetail) {
2115
- super(messageOrField, fieldMessageOrDetail, HttpStatus4.UNAUTHORIZED);
2004
+ super(messageOrField, fieldMessageOrDetail, HttpStatus4.CONFLICT);
2116
2005
  } else {
2117
- super(messageOrField, HttpStatus4.UNAUTHORIZED);
2006
+ super(messageOrField, HttpStatus4.CONFLICT);
2118
2007
  }
2119
2008
  }
2120
2009
  };
2121
2010
 
2122
2011
  // src/exceptions/forbidden.exception.ts
2123
2012
  import { HttpStatus as HttpStatus5 } from "@nestjs/common";
2124
- var ForbiddenException2 = class extends BaseFieldException {
2013
+ var ForbiddenException = class extends BaseFieldException {
2125
2014
  static {
2126
2015
  __name(this, "ForbiddenException");
2127
2016
  }
@@ -2138,222 +2027,230 @@ var ForbiddenException2 = class extends BaseFieldException {
2138
2027
  }
2139
2028
  };
2140
2029
 
2141
- // src/exceptions/not-found.exception.ts
2030
+ // src/exceptions/gone.exception.ts
2142
2031
  import { HttpStatus as HttpStatus6 } from "@nestjs/common";
2143
- var NotFoundException = class extends BaseFieldException {
2032
+ var GoneException = class extends BaseFieldException {
2144
2033
  static {
2145
- __name(this, "NotFoundException");
2034
+ __name(this, "GoneException");
2146
2035
  }
2147
2036
  constructor(messageOrField, fieldMessageOrDetail, detail) {
2148
2037
  if (Array.isArray(messageOrField)) {
2149
- super(messageOrField, HttpStatus6.NOT_FOUND, fieldMessageOrDetail);
2038
+ super(messageOrField, HttpStatus6.GONE, fieldMessageOrDetail);
2150
2039
  } else if (detail !== void 0) {
2151
- super(messageOrField, fieldMessageOrDetail, HttpStatus6.NOT_FOUND, detail);
2040
+ super(messageOrField, fieldMessageOrDetail, HttpStatus6.GONE, detail);
2152
2041
  } else if (fieldMessageOrDetail) {
2153
- super(messageOrField, fieldMessageOrDetail, HttpStatus6.NOT_FOUND);
2042
+ super(messageOrField, fieldMessageOrDetail, HttpStatus6.GONE);
2154
2043
  } else {
2155
- super(messageOrField, HttpStatus6.NOT_FOUND);
2044
+ super(messageOrField, HttpStatus6.GONE);
2156
2045
  }
2157
2046
  }
2158
2047
  };
2159
2048
 
2160
- // src/exceptions/conflict.exception.ts
2049
+ // src/exceptions/internal-server-error.exception.ts
2161
2050
  import { HttpStatus as HttpStatus7 } from "@nestjs/common";
2162
- var ConflictException = class extends BaseFieldException {
2051
+ var InternalServerErrorException3 = class extends BaseFieldException {
2163
2052
  static {
2164
- __name(this, "ConflictException");
2053
+ __name(this, "InternalServerErrorException");
2165
2054
  }
2166
2055
  constructor(messageOrField, fieldMessageOrDetail, detail) {
2167
2056
  if (Array.isArray(messageOrField)) {
2168
- super(messageOrField, HttpStatus7.CONFLICT, fieldMessageOrDetail);
2057
+ super(messageOrField, HttpStatus7.INTERNAL_SERVER_ERROR, fieldMessageOrDetail);
2169
2058
  } else if (detail !== void 0) {
2170
- super(messageOrField, fieldMessageOrDetail, HttpStatus7.CONFLICT, detail);
2059
+ super(messageOrField, fieldMessageOrDetail, HttpStatus7.INTERNAL_SERVER_ERROR, detail);
2171
2060
  } else if (fieldMessageOrDetail) {
2172
- super(messageOrField, fieldMessageOrDetail, HttpStatus7.CONFLICT);
2061
+ super(messageOrField, fieldMessageOrDetail, HttpStatus7.INTERNAL_SERVER_ERROR);
2173
2062
  } else {
2174
- super(messageOrField, HttpStatus7.CONFLICT);
2063
+ super(messageOrField, HttpStatus7.INTERNAL_SERVER_ERROR);
2175
2064
  }
2176
2065
  }
2177
2066
  };
2178
2067
 
2179
- // src/exceptions/internal-server-error.exception.ts
2068
+ // src/exceptions/method-not-allowed.exception.ts
2180
2069
  import { HttpStatus as HttpStatus8 } from "@nestjs/common";
2181
- var InternalServerErrorException3 = class extends BaseFieldException {
2070
+ var MethodNotAllowedException = class extends BaseFieldException {
2182
2071
  static {
2183
- __name(this, "InternalServerErrorException");
2072
+ __name(this, "MethodNotAllowedException");
2184
2073
  }
2185
2074
  constructor(messageOrField, fieldMessageOrDetail, detail) {
2186
2075
  if (Array.isArray(messageOrField)) {
2187
- super(messageOrField, HttpStatus8.INTERNAL_SERVER_ERROR, fieldMessageOrDetail);
2076
+ super(messageOrField, HttpStatus8.METHOD_NOT_ALLOWED, fieldMessageOrDetail);
2188
2077
  } else if (detail !== void 0) {
2189
- super(messageOrField, fieldMessageOrDetail, HttpStatus8.INTERNAL_SERVER_ERROR, detail);
2078
+ super(messageOrField, fieldMessageOrDetail, HttpStatus8.METHOD_NOT_ALLOWED, detail);
2190
2079
  } else if (fieldMessageOrDetail) {
2191
- super(messageOrField, fieldMessageOrDetail, HttpStatus8.INTERNAL_SERVER_ERROR);
2080
+ super(messageOrField, fieldMessageOrDetail, HttpStatus8.METHOD_NOT_ALLOWED);
2192
2081
  } else {
2193
- super(messageOrField, HttpStatus8.INTERNAL_SERVER_ERROR);
2082
+ super(messageOrField, HttpStatus8.METHOD_NOT_ALLOWED);
2194
2083
  }
2195
2084
  }
2196
2085
  };
2197
2086
 
2198
- // src/exceptions/validation.exception.ts
2087
+ // src/exceptions/not-acceptable.exception.ts
2199
2088
  import { HttpStatus as HttpStatus9 } from "@nestjs/common";
2200
- var ValidationException = class extends BaseFieldException {
2089
+ var NotAcceptableException = class extends BaseFieldException {
2201
2090
  static {
2202
- __name(this, "ValidationException");
2091
+ __name(this, "NotAcceptableException");
2203
2092
  }
2204
- constructor(errors, detail) {
2205
- super(errors, HttpStatus9.BAD_REQUEST, detail);
2093
+ constructor(messageOrField, fieldMessageOrDetail, detail) {
2094
+ if (Array.isArray(messageOrField)) {
2095
+ super(messageOrField, HttpStatus9.NOT_ACCEPTABLE, fieldMessageOrDetail);
2096
+ } else if (detail !== void 0) {
2097
+ super(messageOrField, fieldMessageOrDetail, HttpStatus9.NOT_ACCEPTABLE, detail);
2098
+ } else if (fieldMessageOrDetail) {
2099
+ super(messageOrField, fieldMessageOrDetail, HttpStatus9.NOT_ACCEPTABLE);
2100
+ } else {
2101
+ super(messageOrField, HttpStatus9.NOT_ACCEPTABLE);
2102
+ }
2206
2103
  }
2207
2104
  };
2208
2105
 
2209
- // src/exceptions/unprocessable-entity.exception.ts
2106
+ // src/exceptions/not-found.exception.ts
2210
2107
  import { HttpStatus as HttpStatus10 } from "@nestjs/common";
2211
- var UnprocessableEntityException = class extends BaseFieldException {
2108
+ var NotFoundException = class extends BaseFieldException {
2212
2109
  static {
2213
- __name(this, "UnprocessableEntityException");
2110
+ __name(this, "NotFoundException");
2214
2111
  }
2215
2112
  constructor(messageOrField, fieldMessageOrDetail, detail) {
2216
2113
  if (Array.isArray(messageOrField)) {
2217
- super(messageOrField, HttpStatus10.UNPROCESSABLE_ENTITY, fieldMessageOrDetail);
2114
+ super(messageOrField, HttpStatus10.NOT_FOUND, fieldMessageOrDetail);
2218
2115
  } else if (detail !== void 0) {
2219
- super(messageOrField, fieldMessageOrDetail, HttpStatus10.UNPROCESSABLE_ENTITY, detail);
2116
+ super(messageOrField, fieldMessageOrDetail, HttpStatus10.NOT_FOUND, detail);
2220
2117
  } else if (fieldMessageOrDetail) {
2221
- super(messageOrField, fieldMessageOrDetail, HttpStatus10.UNPROCESSABLE_ENTITY);
2118
+ super(messageOrField, fieldMessageOrDetail, HttpStatus10.NOT_FOUND);
2222
2119
  } else {
2223
- super(messageOrField, HttpStatus10.UNPROCESSABLE_ENTITY);
2120
+ super(messageOrField, HttpStatus10.NOT_FOUND);
2224
2121
  }
2225
2122
  }
2226
2123
  };
2227
2124
 
2228
- // src/exceptions/too-many-requests.exception.ts
2125
+ // src/exceptions/not-implemented.exception.ts
2229
2126
  import { HttpStatus as HttpStatus11 } from "@nestjs/common";
2230
- var TooManyRequestsException = class extends BaseFieldException {
2127
+ var NotImplementedException = class extends BaseFieldException {
2231
2128
  static {
2232
- __name(this, "TooManyRequestsException");
2129
+ __name(this, "NotImplementedException");
2233
2130
  }
2234
2131
  constructor(messageOrField, fieldMessageOrDetail, detail) {
2235
2132
  if (Array.isArray(messageOrField)) {
2236
- super(messageOrField, HttpStatus11.TOO_MANY_REQUESTS, fieldMessageOrDetail);
2133
+ super(messageOrField, HttpStatus11.NOT_IMPLEMENTED, fieldMessageOrDetail);
2237
2134
  } else if (detail !== void 0) {
2238
- super(messageOrField, fieldMessageOrDetail, HttpStatus11.TOO_MANY_REQUESTS, detail);
2135
+ super(messageOrField, fieldMessageOrDetail, HttpStatus11.NOT_IMPLEMENTED, detail);
2239
2136
  } else if (fieldMessageOrDetail) {
2240
- super(messageOrField, fieldMessageOrDetail, HttpStatus11.TOO_MANY_REQUESTS);
2137
+ super(messageOrField, fieldMessageOrDetail, HttpStatus11.NOT_IMPLEMENTED);
2241
2138
  } else {
2242
- super(messageOrField, HttpStatus11.TOO_MANY_REQUESTS);
2139
+ super(messageOrField, HttpStatus11.NOT_IMPLEMENTED);
2243
2140
  }
2244
2141
  }
2245
2142
  };
2246
2143
 
2247
- // src/exceptions/service-unavailable.exception.ts
2144
+ // src/exceptions/payload-too-large.exception.ts
2248
2145
  import { HttpStatus as HttpStatus12 } from "@nestjs/common";
2249
- var ServiceUnavailableException = class extends BaseFieldException {
2146
+ var PayloadTooLargeException = class extends BaseFieldException {
2250
2147
  static {
2251
- __name(this, "ServiceUnavailableException");
2148
+ __name(this, "PayloadTooLargeException");
2252
2149
  }
2253
2150
  constructor(messageOrField, fieldMessageOrDetail, detail) {
2254
2151
  if (Array.isArray(messageOrField)) {
2255
- super(messageOrField, HttpStatus12.SERVICE_UNAVAILABLE, fieldMessageOrDetail);
2152
+ super(messageOrField, HttpStatus12.PAYLOAD_TOO_LARGE, fieldMessageOrDetail);
2256
2153
  } else if (detail !== void 0) {
2257
- super(messageOrField, fieldMessageOrDetail, HttpStatus12.SERVICE_UNAVAILABLE, detail);
2154
+ super(messageOrField, fieldMessageOrDetail, HttpStatus12.PAYLOAD_TOO_LARGE, detail);
2258
2155
  } else if (fieldMessageOrDetail) {
2259
- super(messageOrField, fieldMessageOrDetail, HttpStatus12.SERVICE_UNAVAILABLE);
2156
+ super(messageOrField, fieldMessageOrDetail, HttpStatus12.PAYLOAD_TOO_LARGE);
2260
2157
  } else {
2261
- super(messageOrField, HttpStatus12.SERVICE_UNAVAILABLE);
2158
+ super(messageOrField, HttpStatus12.PAYLOAD_TOO_LARGE);
2262
2159
  }
2263
2160
  }
2264
2161
  };
2265
2162
 
2266
- // src/exceptions/method-not-allowed.exception.ts
2163
+ // src/exceptions/request-timeout.exception.ts
2267
2164
  import { HttpStatus as HttpStatus13 } from "@nestjs/common";
2268
- var MethodNotAllowedException = class extends BaseFieldException {
2165
+ var RequestTimeoutException = class extends BaseFieldException {
2269
2166
  static {
2270
- __name(this, "MethodNotAllowedException");
2167
+ __name(this, "RequestTimeoutException");
2271
2168
  }
2272
2169
  constructor(messageOrField, fieldMessageOrDetail, detail) {
2273
2170
  if (Array.isArray(messageOrField)) {
2274
- super(messageOrField, HttpStatus13.METHOD_NOT_ALLOWED, fieldMessageOrDetail);
2171
+ super(messageOrField, HttpStatus13.REQUEST_TIMEOUT, fieldMessageOrDetail);
2275
2172
  } else if (detail !== void 0) {
2276
- super(messageOrField, fieldMessageOrDetail, HttpStatus13.METHOD_NOT_ALLOWED, detail);
2173
+ super(messageOrField, fieldMessageOrDetail, HttpStatus13.REQUEST_TIMEOUT, detail);
2277
2174
  } else if (fieldMessageOrDetail) {
2278
- super(messageOrField, fieldMessageOrDetail, HttpStatus13.METHOD_NOT_ALLOWED);
2175
+ super(messageOrField, fieldMessageOrDetail, HttpStatus13.REQUEST_TIMEOUT);
2279
2176
  } else {
2280
- super(messageOrField, HttpStatus13.METHOD_NOT_ALLOWED);
2177
+ super(messageOrField, HttpStatus13.REQUEST_TIMEOUT);
2281
2178
  }
2282
2179
  }
2283
2180
  };
2284
2181
 
2285
- // src/exceptions/gone.exception.ts
2182
+ // src/exceptions/service-unavailable.exception.ts
2286
2183
  import { HttpStatus as HttpStatus14 } from "@nestjs/common";
2287
- var GoneException = class extends BaseFieldException {
2184
+ var ServiceUnavailableException = class extends BaseFieldException {
2288
2185
  static {
2289
- __name(this, "GoneException");
2186
+ __name(this, "ServiceUnavailableException");
2290
2187
  }
2291
2188
  constructor(messageOrField, fieldMessageOrDetail, detail) {
2292
2189
  if (Array.isArray(messageOrField)) {
2293
- super(messageOrField, HttpStatus14.GONE, fieldMessageOrDetail);
2190
+ super(messageOrField, HttpStatus14.SERVICE_UNAVAILABLE, fieldMessageOrDetail);
2294
2191
  } else if (detail !== void 0) {
2295
- super(messageOrField, fieldMessageOrDetail, HttpStatus14.GONE, detail);
2192
+ super(messageOrField, fieldMessageOrDetail, HttpStatus14.SERVICE_UNAVAILABLE, detail);
2296
2193
  } else if (fieldMessageOrDetail) {
2297
- super(messageOrField, fieldMessageOrDetail, HttpStatus14.GONE);
2194
+ super(messageOrField, fieldMessageOrDetail, HttpStatus14.SERVICE_UNAVAILABLE);
2298
2195
  } else {
2299
- super(messageOrField, HttpStatus14.GONE);
2196
+ super(messageOrField, HttpStatus14.SERVICE_UNAVAILABLE);
2300
2197
  }
2301
2198
  }
2302
2199
  };
2303
2200
 
2304
- // src/exceptions/not-acceptable.exception.ts
2201
+ // src/exceptions/too-many-requests.exception.ts
2305
2202
  import { HttpStatus as HttpStatus15 } from "@nestjs/common";
2306
- var NotAcceptableException = class extends BaseFieldException {
2203
+ var TooManyRequestsException = class extends BaseFieldException {
2307
2204
  static {
2308
- __name(this, "NotAcceptableException");
2205
+ __name(this, "TooManyRequestsException");
2309
2206
  }
2310
2207
  constructor(messageOrField, fieldMessageOrDetail, detail) {
2311
2208
  if (Array.isArray(messageOrField)) {
2312
- super(messageOrField, HttpStatus15.NOT_ACCEPTABLE, fieldMessageOrDetail);
2209
+ super(messageOrField, HttpStatus15.TOO_MANY_REQUESTS, fieldMessageOrDetail);
2313
2210
  } else if (detail !== void 0) {
2314
- super(messageOrField, fieldMessageOrDetail, HttpStatus15.NOT_ACCEPTABLE, detail);
2211
+ super(messageOrField, fieldMessageOrDetail, HttpStatus15.TOO_MANY_REQUESTS, detail);
2315
2212
  } else if (fieldMessageOrDetail) {
2316
- super(messageOrField, fieldMessageOrDetail, HttpStatus15.NOT_ACCEPTABLE);
2213
+ super(messageOrField, fieldMessageOrDetail, HttpStatus15.TOO_MANY_REQUESTS);
2317
2214
  } else {
2318
- super(messageOrField, HttpStatus15.NOT_ACCEPTABLE);
2215
+ super(messageOrField, HttpStatus15.TOO_MANY_REQUESTS);
2319
2216
  }
2320
2217
  }
2321
2218
  };
2322
2219
 
2323
- // src/exceptions/request-timeout.exception.ts
2220
+ // src/exceptions/unauthorized.exception.ts
2324
2221
  import { HttpStatus as HttpStatus16 } from "@nestjs/common";
2325
- var RequestTimeoutException = class extends BaseFieldException {
2222
+ var UnauthorizedException4 = class extends BaseFieldException {
2326
2223
  static {
2327
- __name(this, "RequestTimeoutException");
2224
+ __name(this, "UnauthorizedException");
2328
2225
  }
2329
2226
  constructor(messageOrField, fieldMessageOrDetail, detail) {
2330
2227
  if (Array.isArray(messageOrField)) {
2331
- super(messageOrField, HttpStatus16.REQUEST_TIMEOUT, fieldMessageOrDetail);
2228
+ super(messageOrField, HttpStatus16.UNAUTHORIZED, fieldMessageOrDetail);
2332
2229
  } else if (detail !== void 0) {
2333
- super(messageOrField, fieldMessageOrDetail, HttpStatus16.REQUEST_TIMEOUT, detail);
2230
+ super(messageOrField, fieldMessageOrDetail, HttpStatus16.UNAUTHORIZED, detail);
2334
2231
  } else if (fieldMessageOrDetail) {
2335
- super(messageOrField, fieldMessageOrDetail, HttpStatus16.REQUEST_TIMEOUT);
2232
+ super(messageOrField, fieldMessageOrDetail, HttpStatus16.UNAUTHORIZED);
2336
2233
  } else {
2337
- super(messageOrField, HttpStatus16.REQUEST_TIMEOUT);
2234
+ super(messageOrField, HttpStatus16.UNAUTHORIZED);
2338
2235
  }
2339
2236
  }
2340
2237
  };
2341
2238
 
2342
- // src/exceptions/payload-too-large.exception.ts
2239
+ // src/exceptions/unprocessable-entity.exception.ts
2343
2240
  import { HttpStatus as HttpStatus17 } from "@nestjs/common";
2344
- var PayloadTooLargeException = class extends BaseFieldException {
2241
+ var UnprocessableEntityException = class extends BaseFieldException {
2345
2242
  static {
2346
- __name(this, "PayloadTooLargeException");
2243
+ __name(this, "UnprocessableEntityException");
2347
2244
  }
2348
2245
  constructor(messageOrField, fieldMessageOrDetail, detail) {
2349
2246
  if (Array.isArray(messageOrField)) {
2350
- super(messageOrField, HttpStatus17.PAYLOAD_TOO_LARGE, fieldMessageOrDetail);
2247
+ super(messageOrField, HttpStatus17.UNPROCESSABLE_ENTITY, fieldMessageOrDetail);
2351
2248
  } else if (detail !== void 0) {
2352
- super(messageOrField, fieldMessageOrDetail, HttpStatus17.PAYLOAD_TOO_LARGE, detail);
2249
+ super(messageOrField, fieldMessageOrDetail, HttpStatus17.UNPROCESSABLE_ENTITY, detail);
2353
2250
  } else if (fieldMessageOrDetail) {
2354
- super(messageOrField, fieldMessageOrDetail, HttpStatus17.PAYLOAD_TOO_LARGE);
2251
+ super(messageOrField, fieldMessageOrDetail, HttpStatus17.UNPROCESSABLE_ENTITY);
2355
2252
  } else {
2356
- super(messageOrField, HttpStatus17.PAYLOAD_TOO_LARGE);
2253
+ super(messageOrField, HttpStatus17.UNPROCESSABLE_ENTITY);
2357
2254
  }
2358
2255
  }
2359
2256
  };
@@ -2377,103 +2274,445 @@ var UnsupportedMediaTypeException = class extends BaseFieldException {
2377
2274
  }
2378
2275
  };
2379
2276
 
2380
- // src/exceptions/not-implemented.exception.ts
2277
+ // src/exceptions/validation.exception.ts
2381
2278
  import { HttpStatus as HttpStatus19 } from "@nestjs/common";
2382
- var NotImplementedException = class extends BaseFieldException {
2279
+ var ValidationException = class extends BaseFieldException {
2383
2280
  static {
2384
- __name(this, "NotImplementedException");
2281
+ __name(this, "ValidationException");
2385
2282
  }
2386
- constructor(messageOrField, fieldMessageOrDetail, detail) {
2387
- if (Array.isArray(messageOrField)) {
2388
- super(messageOrField, HttpStatus19.NOT_IMPLEMENTED, fieldMessageOrDetail);
2389
- } else if (detail !== void 0) {
2390
- super(messageOrField, fieldMessageOrDetail, HttpStatus19.NOT_IMPLEMENTED, detail);
2391
- } else if (fieldMessageOrDetail) {
2392
- super(messageOrField, fieldMessageOrDetail, HttpStatus19.NOT_IMPLEMENTED);
2283
+ constructor(errors, detail) {
2284
+ super(errors, HttpStatus19.BAD_REQUEST, detail);
2285
+ }
2286
+ };
2287
+
2288
+ // src/filters/http-exception.filter.ts
2289
+ import { Catch, HttpException as HttpException2, HttpStatus as HttpStatus20, Logger as Logger9 } from "@nestjs/common";
2290
+ function _ts_decorate11(decorators, target, key, desc) {
2291
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2292
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
2293
+ 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;
2294
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
2295
+ }
2296
+ __name(_ts_decorate11, "_ts_decorate");
2297
+ function getHttpStatusTitle(status) {
2298
+ const enumKey = Object.entries(HttpStatus20).find(([key, value]) => value === status && Number.isNaN(Number(key)))?.[0];
2299
+ if (!enumKey) {
2300
+ return "Error";
2301
+ }
2302
+ return enumKey.split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
2303
+ }
2304
+ __name(getHttpStatusTitle, "getHttpStatusTitle");
2305
+ var HttpExceptionFilter = class _HttpExceptionFilter {
2306
+ static {
2307
+ __name(this, "HttpExceptionFilter");
2308
+ }
2309
+ logger = new Logger9(_HttpExceptionFilter.name);
2310
+ catch(exception, host) {
2311
+ const ctx = host.switchToHttp();
2312
+ const response = ctx.getResponse();
2313
+ let status = HttpStatus20.INTERNAL_SERVER_ERROR;
2314
+ let errors = [];
2315
+ let detail = "Internal server error";
2316
+ if (exception instanceof HttpException2) {
2317
+ status = exception.getStatus();
2318
+ const exceptionResponse = exception.getResponse();
2319
+ if (typeof exceptionResponse === "object" && exceptionResponse !== null) {
2320
+ const responseObj = exceptionResponse;
2321
+ if ("errors" in responseObj && Array.isArray(responseObj.errors)) {
2322
+ errors = responseObj.errors;
2323
+ detail = ("detail" in responseObj ? responseObj.detail : void 0) || exception.message;
2324
+ } else if ("message" in responseObj && Array.isArray(responseObj.message)) {
2325
+ errors = responseObj.message.map((msg) => {
2326
+ if (typeof msg === "object" && "property" in msg && "constraints" in msg) {
2327
+ const constraintValues = Object.values(msg.constraints);
2328
+ return {
2329
+ field: msg.property,
2330
+ message: constraintValues[0] ?? "Validation failed"
2331
+ };
2332
+ }
2333
+ return {
2334
+ message: typeof msg === "string" ? msg : JSON.stringify(msg)
2335
+ };
2336
+ });
2337
+ detail = "Validation failed";
2338
+ } else if ("message" in responseObj) {
2339
+ const message = responseObj.message;
2340
+ errors = [
2341
+ {
2342
+ message: Array.isArray(message) ? message.join(", ") : message
2343
+ }
2344
+ ];
2345
+ detail = ("error" in responseObj ? responseObj.error : void 0) || exception.message;
2346
+ }
2347
+ } else if (typeof exceptionResponse === "string") {
2348
+ errors = [
2349
+ {
2350
+ message: exceptionResponse
2351
+ }
2352
+ ];
2353
+ detail = exceptionResponse;
2354
+ }
2393
2355
  } else {
2394
- super(messageOrField, HttpStatus19.NOT_IMPLEMENTED);
2356
+ const errorMessage = exception instanceof Error ? exception.message : "Unknown error";
2357
+ const stack = exception instanceof Error ? exception.stack : void 0;
2358
+ this.logger.error(`Unexpected error: ${errorMessage}`, stack);
2359
+ errors = [
2360
+ {
2361
+ message: "An unexpected error occurred"
2362
+ }
2363
+ ];
2395
2364
  }
2365
+ const problemDetails = {
2366
+ title: getHttpStatusTitle(status),
2367
+ status,
2368
+ detail,
2369
+ errors
2370
+ };
2371
+ response.status(status).send(problemDetails);
2396
2372
  }
2397
2373
  };
2374
+ HttpExceptionFilter = _ts_decorate11([
2375
+ Catch()
2376
+ ], HttpExceptionFilter);
2398
2377
 
2399
- // src/exceptions/bad-gateway.exception.ts
2400
- import { HttpStatus as HttpStatus20 } from "@nestjs/common";
2401
- var BadGatewayException = class extends BaseFieldException {
2378
+ // src/http/guards/csrf.guard.ts
2379
+ import { ForbiddenException as ForbiddenException2, Injectable as Injectable8, Logger as Logger10 } from "@nestjs/common";
2380
+ function _ts_decorate12(decorators, target, key, desc) {
2381
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2382
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
2383
+ 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;
2384
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
2385
+ }
2386
+ __name(_ts_decorate12, "_ts_decorate");
2387
+ var CsrfGuard = class _CsrfGuard {
2402
2388
  static {
2403
- __name(this, "BadGatewayException");
2389
+ __name(this, "CsrfGuard");
2404
2390
  }
2405
- constructor(messageOrField, fieldMessageOrDetail, detail) {
2406
- if (Array.isArray(messageOrField)) {
2407
- super(messageOrField, HttpStatus20.BAD_GATEWAY, fieldMessageOrDetail);
2408
- } else if (detail !== void 0) {
2409
- super(messageOrField, fieldMessageOrDetail, HttpStatus20.BAD_GATEWAY, detail);
2410
- } else if (fieldMessageOrDetail) {
2411
- super(messageOrField, fieldMessageOrDetail, HttpStatus20.BAD_GATEWAY);
2412
- } else {
2413
- super(messageOrField, HttpStatus20.BAD_GATEWAY);
2391
+ logger = new Logger10(_CsrfGuard.name);
2392
+ async canActivate(context) {
2393
+ const request = context.switchToHttp().getRequest();
2394
+ const reply = context.switchToHttp().getResponse();
2395
+ const safeMethods = [
2396
+ "GET",
2397
+ "HEAD",
2398
+ "OPTIONS"
2399
+ ];
2400
+ if (safeMethods.includes(request.method)) {
2401
+ return true;
2402
+ }
2403
+ try {
2404
+ const fastifyInstance = request.server;
2405
+ if (!fastifyInstance.csrfProtection) {
2406
+ this.logger.error("CSRF protection plugin not found. Ensure @fastify/csrf-protection is registered.");
2407
+ throw new ForbiddenException2("CSRF protection not configured");
2408
+ }
2409
+ await new Promise((resolve, reject) => {
2410
+ fastifyInstance.csrfProtection(request, reply, (err) => {
2411
+ if (err) {
2412
+ reject(err);
2413
+ } else {
2414
+ resolve();
2415
+ }
2416
+ });
2417
+ });
2418
+ this.logger.debug(`CSRF validation successful for ${request.method} ${request.url}`);
2419
+ return true;
2420
+ } catch (error) {
2421
+ this.logger.warn(`CSRF validation failed for ${request.method} ${request.url}: ${error instanceof Error ? error.message : "Unknown error"}`);
2422
+ throw new ForbiddenException2({
2423
+ errors: [
2424
+ {
2425
+ field: "csrf",
2426
+ message: "Invalid or missing CSRF token"
2427
+ }
2428
+ ],
2429
+ message: "CSRF validation failed"
2430
+ });
2431
+ }
2432
+ }
2433
+ };
2434
+ CsrfGuard = _ts_decorate12([
2435
+ Injectable8()
2436
+ ], CsrfGuard);
2437
+
2438
+ // src/http/http.module.ts
2439
+ import { Module as Module4 } from "@nestjs/common";
2440
+ function _ts_decorate13(decorators, target, key, desc) {
2441
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2442
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
2443
+ 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;
2444
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
2445
+ }
2446
+ __name(_ts_decorate13, "_ts_decorate");
2447
+ var HttpModule = class {
2448
+ static {
2449
+ __name(this, "HttpModule");
2450
+ }
2451
+ };
2452
+ HttpModule = _ts_decorate13([
2453
+ Module4({
2454
+ providers: [
2455
+ CsrfGuard
2456
+ ],
2457
+ exports: [
2458
+ CsrfGuard
2459
+ ]
2460
+ })
2461
+ ], HttpModule);
2462
+
2463
+ // src/logger/interceptors/http-logger.interceptor.ts
2464
+ import { Injectable as Injectable9, Optional } from "@nestjs/common";
2465
+ import { catchError, tap as tap2 } from "rxjs/operators";
2466
+
2467
+ // src/logger/utils/index.ts
2468
+ import { AsyncLocalStorage } from "async_hooks";
2469
+ import { randomUUID } from "crypto";
2470
+ var correlationStorage = new AsyncLocalStorage();
2471
+ function getCorrelationContext() {
2472
+ return correlationStorage.getStore();
2473
+ }
2474
+ __name(getCorrelationContext, "getCorrelationContext");
2475
+ function runWithCorrelationContext(context, callback) {
2476
+ return correlationStorage.run(context, callback);
2477
+ }
2478
+ __name(runWithCorrelationContext, "runWithCorrelationContext");
2479
+ function updateCorrelationContext(updates) {
2480
+ const context = correlationStorage.getStore();
2481
+ if (context) {
2482
+ Object.assign(context, updates);
2483
+ }
2484
+ }
2485
+ __name(updateCorrelationContext, "updateCorrelationContext");
2486
+ var DEFAULT_CORRELATION_HEADER = "x-correlation-id";
2487
+ function generateCorrelationId() {
2488
+ return randomUUID();
2489
+ }
2490
+ __name(generateCorrelationId, "generateCorrelationId");
2491
+ function addCorrelationIdToResponse(reply, correlationId, headerName = DEFAULT_CORRELATION_HEADER) {
2492
+ if (typeof reply.header === "function") {
2493
+ reply.header(headerName, correlationId);
2494
+ } else if (reply.raw && typeof reply.raw.setHeader === "function") {
2495
+ reply.raw.setHeader(headerName, correlationId);
2496
+ }
2497
+ }
2498
+ __name(addCorrelationIdToResponse, "addCorrelationIdToResponse");
2499
+
2500
+ // src/logger/interceptors/http-logger.interceptor.ts
2501
+ function _ts_decorate14(decorators, target, key, desc) {
2502
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2503
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
2504
+ 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;
2505
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
2506
+ }
2507
+ __name(_ts_decorate14, "_ts_decorate");
2508
+ function _ts_metadata7(k, v) {
2509
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
2510
+ }
2511
+ __name(_ts_metadata7, "_ts_metadata");
2512
+ function _ts_param4(paramIndex, decorator) {
2513
+ return function(target, key) {
2514
+ decorator(target, key, paramIndex);
2515
+ };
2516
+ }
2517
+ __name(_ts_param4, "_ts_param");
2518
+ var HttpLoggerInterceptor = class {
2519
+ static {
2520
+ __name(this, "HttpLoggerInterceptor");
2521
+ }
2522
+ logger;
2523
+ enableRequestLog;
2524
+ enableResponseLog;
2525
+ slowRequestThreshold;
2526
+ constructor(logger, options) {
2527
+ this.logger = logger;
2528
+ this.enableRequestLog = options?.enableRequestLog ?? true;
2529
+ this.enableResponseLog = options?.enableResponseLog ?? true;
2530
+ this.slowRequestThreshold = options?.slowRequestThreshold ?? 3e3;
2531
+ }
2532
+ intercept(context, next) {
2533
+ if (context.getType() !== "http") {
2534
+ return next.handle();
2535
+ }
2536
+ const httpContext = context.switchToHttp();
2537
+ const request = httpContext.getRequest();
2538
+ const response = httpContext.getResponse();
2539
+ const startTime = Date.now();
2540
+ if (this.enableRequestLog) {
2541
+ this.logRequest(request);
2542
+ }
2543
+ return next.handle().pipe(tap2(() => {
2544
+ if (this.enableResponseLog) {
2545
+ const duration = Date.now() - startTime;
2546
+ this.logResponse(request, response, duration);
2547
+ }
2548
+ }), catchError((error) => {
2549
+ const duration = Date.now() - startTime;
2550
+ this.logError(request, response, duration, error);
2551
+ throw error;
2552
+ }));
2553
+ }
2554
+ logRequest(request) {
2555
+ try {
2556
+ const correlationContext = getCorrelationContext();
2557
+ const metadata = {
2558
+ type: "http_request",
2559
+ method: request.method,
2560
+ url: request.url,
2561
+ correlationId: correlationContext?.correlationId,
2562
+ ip: request.ip,
2563
+ userAgent: request.headers["user-agent"]
2564
+ };
2565
+ this.logger.logWithMetadata("log", `Incoming ${request.method} ${request.url}`, metadata);
2566
+ } catch (error) {
2567
+ this.logger.error("Failed to log HTTP request", error.stack);
2568
+ }
2569
+ }
2570
+ logResponse(request, response, duration) {
2571
+ try {
2572
+ const correlationContext = getCorrelationContext();
2573
+ const statusCode = response.statusCode;
2574
+ const logLevel = statusCode >= 500 ? "error" : statusCode >= 400 ? "warn" : "log";
2575
+ const metadata = {
2576
+ type: "http_response",
2577
+ method: request.method,
2578
+ url: request.url,
2579
+ statusCode,
2580
+ duration,
2581
+ correlationId: correlationContext?.correlationId
2582
+ };
2583
+ if (duration > this.slowRequestThreshold) {
2584
+ metadata.slowRequest = true;
2585
+ }
2586
+ const message = metadata.slowRequest ? `SLOW ${request.method} ${request.url} ${statusCode} - ${duration}ms` : `${request.method} ${request.url} ${statusCode} - ${duration}ms`;
2587
+ this.logger.logWithMetadata(logLevel, message, metadata);
2588
+ } catch (error) {
2589
+ this.logger.error("Failed to log HTTP response", error.stack);
2590
+ }
2591
+ }
2592
+ logError(request, response, duration, error) {
2593
+ try {
2594
+ const correlationContext = getCorrelationContext();
2595
+ const statusCode = response.statusCode || 500;
2596
+ const metadata = {
2597
+ type: "http_error",
2598
+ method: request.method,
2599
+ url: request.url,
2600
+ statusCode,
2601
+ duration,
2602
+ correlationId: correlationContext?.correlationId,
2603
+ errorName: error?.name || "Error",
2604
+ errorMessage: error?.message || "Unknown error"
2605
+ };
2606
+ if (error?.stack) {
2607
+ metadata.trace = error.stack;
2608
+ }
2609
+ if (error?.response) {
2610
+ metadata.errorDetails = error.response;
2611
+ }
2612
+ const message = `ERROR ${request.method} ${request.url} ${statusCode} - ${error?.message || "Unknown error"}`;
2613
+ this.logger.logWithMetadata("error", message, metadata);
2614
+ } catch (loggingError) {
2615
+ this.logger.error("Failed to log HTTP error", loggingError.stack);
2616
+ }
2617
+ }
2618
+ };
2619
+ HttpLoggerInterceptor = _ts_decorate14([
2620
+ Injectable9(),
2621
+ _ts_param4(1, Optional()),
2622
+ _ts_metadata7("design:type", Function),
2623
+ _ts_metadata7("design:paramtypes", [
2624
+ typeof LoggerService === "undefined" ? Object : LoggerService,
2625
+ typeof HttpLoggerOptions === "undefined" ? Object : HttpLoggerOptions
2626
+ ])
2627
+ ], HttpLoggerInterceptor);
2628
+
2629
+ // src/logger/logger.module.ts
2630
+ import { Global as Global4, Logger as Logger11, Module as Module5 } from "@nestjs/common";
2631
+
2632
+ // src/logger/middleware/correlation-id.middleware.ts
2633
+ import { Injectable as Injectable10 } from "@nestjs/common";
2634
+ function _ts_decorate15(decorators, target, key, desc) {
2635
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2636
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
2637
+ 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;
2638
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
2639
+ }
2640
+ __name(_ts_decorate15, "_ts_decorate");
2641
+ function _ts_metadata8(k, v) {
2642
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
2643
+ }
2644
+ __name(_ts_metadata8, "_ts_metadata");
2645
+ var CorrelationIdMiddleware = class {
2646
+ static {
2647
+ __name(this, "CorrelationIdMiddleware");
2648
+ }
2649
+ includeInResponse;
2650
+ responseHeader;
2651
+ constructor(options = {}) {
2652
+ this.includeInResponse = options.includeInResponse ?? true;
2653
+ this.responseHeader = options.responseHeader ?? DEFAULT_CORRELATION_HEADER;
2654
+ }
2655
+ /**
2656
+ * Middleware handler for processing requests.
2657
+ */
2658
+ use(_req, reply, next) {
2659
+ const correlationId = generateCorrelationId();
2660
+ if (this.includeInResponse) {
2661
+ addCorrelationIdToResponse(reply, correlationId, this.responseHeader);
2662
+ }
2663
+ runWithCorrelationContext({
2664
+ correlationId
2665
+ }, () => {
2666
+ next();
2667
+ });
2668
+ }
2669
+ /**
2670
+ * Fastify hook handler for onRequest.
2671
+ * This is an async function that returns a Promise, ensuring the AsyncLocalStorage
2672
+ * context persists throughout the entire request lifecycle.
2673
+ */
2674
+ async onRequest(_req, reply) {
2675
+ const correlationId = generateCorrelationId();
2676
+ if (this.includeInResponse) {
2677
+ addCorrelationIdToResponse(reply, correlationId, this.responseHeader);
2678
+ }
2679
+ const store = correlationStorage.getStore();
2680
+ if (!store) {
2681
+ correlationStorage.enterWith({
2682
+ correlationId
2683
+ });
2414
2684
  }
2415
2685
  }
2416
2686
  };
2417
-
2418
- // src/logger/logger.module.ts
2419
- import { Global as Global4, Module as Module5, Logger as Logger11 } from "@nestjs/common";
2687
+ CorrelationIdMiddleware = _ts_decorate15([
2688
+ Injectable10(),
2689
+ _ts_metadata8("design:type", Function),
2690
+ _ts_metadata8("design:paramtypes", [
2691
+ typeof CorrelationIdMiddlewareOptions === "undefined" ? Object : CorrelationIdMiddlewareOptions
2692
+ ])
2693
+ ], CorrelationIdMiddleware);
2420
2694
 
2421
2695
  // src/logger/services/logger.service.ts
2422
- import { Injectable as Injectable9, Logger as Logger10, Optional } from "@nestjs/common";
2696
+ import { Injectable as Injectable11, Optional as Optional2 } from "@nestjs/common";
2423
2697
  import { createLogger, format, transports } from "winston";
2424
2698
  import DailyRotateFile from "winston-daily-rotate-file";
2425
-
2426
- // src/logger/utils/index.ts
2427
- import { AsyncLocalStorage } from "async_hooks";
2428
- import { randomUUID } from "crypto";
2429
- var correlationStorage = new AsyncLocalStorage();
2430
- function getCorrelationContext() {
2431
- return correlationStorage.getStore();
2432
- }
2433
- __name(getCorrelationContext, "getCorrelationContext");
2434
- function runWithCorrelationContext(context, callback) {
2435
- return correlationStorage.run(context, callback);
2436
- }
2437
- __name(runWithCorrelationContext, "runWithCorrelationContext");
2438
- function updateCorrelationContext(updates) {
2439
- const context = correlationStorage.getStore();
2440
- if (context) {
2441
- Object.assign(context, updates);
2442
- }
2443
- }
2444
- __name(updateCorrelationContext, "updateCorrelationContext");
2445
- var DEFAULT_CORRELATION_HEADER = "x-correlation-id";
2446
- function generateCorrelationId() {
2447
- return randomUUID();
2448
- }
2449
- __name(generateCorrelationId, "generateCorrelationId");
2450
- function addCorrelationIdToResponse(reply, correlationId, headerName = DEFAULT_CORRELATION_HEADER) {
2451
- if (typeof reply.header === "function") {
2452
- reply.header(headerName, correlationId);
2453
- } else if (reply.raw && typeof reply.raw.setHeader === "function") {
2454
- reply.raw.setHeader(headerName, correlationId);
2455
- }
2456
- }
2457
- __name(addCorrelationIdToResponse, "addCorrelationIdToResponse");
2458
-
2459
- // src/logger/services/logger.service.ts
2460
- function _ts_decorate14(decorators, target, key, desc) {
2699
+ function _ts_decorate16(decorators, target, key, desc) {
2461
2700
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2462
2701
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
2463
2702
  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;
2464
2703
  return c > 3 && r && Object.defineProperty(target, key, r), r;
2465
2704
  }
2466
- __name(_ts_decorate14, "_ts_decorate");
2467
- function _ts_metadata8(k, v) {
2705
+ __name(_ts_decorate16, "_ts_decorate");
2706
+ function _ts_metadata9(k, v) {
2468
2707
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
2469
2708
  }
2470
- __name(_ts_metadata8, "_ts_metadata");
2471
- function _ts_param4(paramIndex, decorator) {
2709
+ __name(_ts_metadata9, "_ts_metadata");
2710
+ function _ts_param5(paramIndex, decorator) {
2472
2711
  return function(target, key) {
2473
2712
  decorator(target, key, paramIndex);
2474
2713
  };
2475
2714
  }
2476
- __name(_ts_param4, "_ts_param");
2715
+ __name(_ts_param5, "_ts_param");
2477
2716
  var LoggerService2 = class _LoggerService {
2478
2717
  static {
2479
2718
  __name(this, "LoggerService");
@@ -2526,7 +2765,8 @@ var LoggerService2 = class _LoggerService {
2526
2765
  ].filter(Boolean);
2527
2766
  let output = parts.join(" ");
2528
2767
  if (trace) {
2529
- output += "\n" + trace;
2768
+ output += `
2769
+ ${trace}`;
2530
2770
  }
2531
2771
  return output;
2532
2772
  }), format.colorize({
@@ -2674,210 +2914,16 @@ var LoggerService2 = class _LoggerService {
2674
2914
  return childLogger;
2675
2915
  }
2676
2916
  };
2677
- LoggerService2 = _ts_decorate14([
2678
- Injectable9(),
2679
- _ts_param4(0, Optional()),
2680
- _ts_param4(1, Optional()),
2681
- _ts_metadata8("design:type", Function),
2682
- _ts_metadata8("design:paramtypes", [
2683
- typeof LoggerModuleOptions === "undefined" ? Object : LoggerModuleOptions,
2684
- typeof Logger10 === "undefined" ? Object : Logger10
2685
- ])
2686
- ], LoggerService2);
2687
-
2688
- // src/logger/middleware/correlation-id.middleware.ts
2689
- import { Injectable as Injectable10 } from "@nestjs/common";
2690
- function _ts_decorate15(decorators, target, key, desc) {
2691
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2692
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
2693
- 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;
2694
- return c > 3 && r && Object.defineProperty(target, key, r), r;
2695
- }
2696
- __name(_ts_decorate15, "_ts_decorate");
2697
- function _ts_metadata9(k, v) {
2698
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
2699
- }
2700
- __name(_ts_metadata9, "_ts_metadata");
2701
- var CorrelationIdMiddleware = class {
2702
- static {
2703
- __name(this, "CorrelationIdMiddleware");
2704
- }
2705
- includeInResponse;
2706
- responseHeader;
2707
- constructor(options = {}) {
2708
- this.includeInResponse = options.includeInResponse ?? true;
2709
- this.responseHeader = options.responseHeader ?? DEFAULT_CORRELATION_HEADER;
2710
- }
2711
- /**
2712
- * Middleware handler for processing requests.
2713
- */
2714
- use(req, reply, next) {
2715
- const correlationId = generateCorrelationId();
2716
- if (this.includeInResponse) {
2717
- addCorrelationIdToResponse(reply, correlationId, this.responseHeader);
2718
- }
2719
- runWithCorrelationContext({
2720
- correlationId
2721
- }, () => {
2722
- next();
2723
- });
2724
- }
2725
- /**
2726
- * Fastify hook handler for onRequest.
2727
- * This is an async function that returns a Promise, ensuring the AsyncLocalStorage
2728
- * context persists throughout the entire request lifecycle.
2729
- */
2730
- async onRequest(req, reply) {
2731
- const correlationId = generateCorrelationId();
2732
- if (this.includeInResponse) {
2733
- addCorrelationIdToResponse(reply, correlationId, this.responseHeader);
2734
- }
2735
- const store = correlationStorage.getStore();
2736
- if (!store) {
2737
- correlationStorage.enterWith({
2738
- correlationId
2739
- });
2740
- }
2741
- }
2742
- };
2743
- CorrelationIdMiddleware = _ts_decorate15([
2744
- Injectable10(),
2745
- _ts_metadata9("design:type", Function),
2746
- _ts_metadata9("design:paramtypes", [
2747
- typeof CorrelationIdMiddlewareOptions === "undefined" ? Object : CorrelationIdMiddlewareOptions
2748
- ])
2749
- ], CorrelationIdMiddleware);
2750
-
2751
- // src/logger/interceptors/http-logger.interceptor.ts
2752
- import { Injectable as Injectable11, Optional as Optional2 } from "@nestjs/common";
2753
- import { tap as tap2, catchError } from "rxjs/operators";
2754
- function _ts_decorate16(decorators, target, key, desc) {
2755
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
2756
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
2757
- 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;
2758
- return c > 3 && r && Object.defineProperty(target, key, r), r;
2759
- }
2760
- __name(_ts_decorate16, "_ts_decorate");
2761
- function _ts_metadata10(k, v) {
2762
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
2763
- }
2764
- __name(_ts_metadata10, "_ts_metadata");
2765
- function _ts_param5(paramIndex, decorator) {
2766
- return function(target, key) {
2767
- decorator(target, key, paramIndex);
2768
- };
2769
- }
2770
- __name(_ts_param5, "_ts_param");
2771
- var HttpLoggerInterceptor = class {
2772
- static {
2773
- __name(this, "HttpLoggerInterceptor");
2774
- }
2775
- logger;
2776
- enableRequestLog;
2777
- enableResponseLog;
2778
- slowRequestThreshold;
2779
- constructor(logger, options) {
2780
- this.logger = logger;
2781
- this.enableRequestLog = options?.enableRequestLog ?? true;
2782
- this.enableResponseLog = options?.enableResponseLog ?? true;
2783
- this.slowRequestThreshold = options?.slowRequestThreshold ?? 3e3;
2784
- }
2785
- intercept(context, next) {
2786
- if (context.getType() !== "http") {
2787
- return next.handle();
2788
- }
2789
- const httpContext = context.switchToHttp();
2790
- const request = httpContext.getRequest();
2791
- const response = httpContext.getResponse();
2792
- const startTime = Date.now();
2793
- if (this.enableRequestLog) {
2794
- this.logRequest(request);
2795
- }
2796
- return next.handle().pipe(tap2(() => {
2797
- if (this.enableResponseLog) {
2798
- const duration = Date.now() - startTime;
2799
- this.logResponse(request, response, duration);
2800
- }
2801
- }), catchError((error) => {
2802
- const duration = Date.now() - startTime;
2803
- this.logError(request, response, duration, error);
2804
- throw error;
2805
- }));
2806
- }
2807
- logRequest(request) {
2808
- try {
2809
- const correlationContext = getCorrelationContext();
2810
- const metadata = {
2811
- type: "http_request",
2812
- method: request.method,
2813
- url: request.url,
2814
- correlationId: correlationContext?.correlationId,
2815
- ip: request.ip,
2816
- userAgent: request.headers["user-agent"]
2817
- };
2818
- this.logger.logWithMetadata("log", `Incoming ${request.method} ${request.url}`, metadata);
2819
- } catch (error) {
2820
- this.logger.error("Failed to log HTTP request", error.stack);
2821
- }
2822
- }
2823
- logResponse(request, response, duration) {
2824
- try {
2825
- const correlationContext = getCorrelationContext();
2826
- const statusCode = response.statusCode;
2827
- const logLevel = statusCode >= 500 ? "error" : statusCode >= 400 ? "warn" : "log";
2828
- const metadata = {
2829
- type: "http_response",
2830
- method: request.method,
2831
- url: request.url,
2832
- statusCode,
2833
- duration,
2834
- correlationId: correlationContext?.correlationId
2835
- };
2836
- if (duration > this.slowRequestThreshold) {
2837
- metadata.slowRequest = true;
2838
- }
2839
- const message = metadata.slowRequest ? `SLOW ${request.method} ${request.url} ${statusCode} - ${duration}ms` : `${request.method} ${request.url} ${statusCode} - ${duration}ms`;
2840
- this.logger.logWithMetadata(logLevel, message, metadata);
2841
- } catch (error) {
2842
- this.logger.error("Failed to log HTTP response", error.stack);
2843
- }
2844
- }
2845
- logError(request, response, duration, error) {
2846
- try {
2847
- const correlationContext = getCorrelationContext();
2848
- const statusCode = response.statusCode || 500;
2849
- const metadata = {
2850
- type: "http_error",
2851
- method: request.method,
2852
- url: request.url,
2853
- statusCode,
2854
- duration,
2855
- correlationId: correlationContext?.correlationId,
2856
- errorName: error?.name || "Error",
2857
- errorMessage: error?.message || "Unknown error"
2858
- };
2859
- if (error?.stack) {
2860
- metadata.trace = error.stack;
2861
- }
2862
- if (error?.response) {
2863
- metadata.errorDetails = error.response;
2864
- }
2865
- const message = `ERROR ${request.method} ${request.url} ${statusCode} - ${error?.message || "Unknown error"}`;
2866
- this.logger.logWithMetadata("error", message, metadata);
2867
- } catch (loggingError) {
2868
- this.logger.error("Failed to log HTTP error", loggingError.stack);
2869
- }
2870
- }
2871
- };
2872
- HttpLoggerInterceptor = _ts_decorate16([
2917
+ LoggerService2 = _ts_decorate16([
2873
2918
  Injectable11(),
2919
+ _ts_param5(0, Optional2()),
2874
2920
  _ts_param5(1, Optional2()),
2875
- _ts_metadata10("design:type", Function),
2876
- _ts_metadata10("design:paramtypes", [
2877
- typeof LoggerService === "undefined" ? Object : LoggerService,
2878
- typeof HttpLoggerOptions === "undefined" ? Object : HttpLoggerOptions
2921
+ _ts_metadata9("design:type", Function),
2922
+ _ts_metadata9("design:paramtypes", [
2923
+ typeof LoggerModuleOptions === "undefined" ? Object : LoggerModuleOptions,
2924
+ typeof Logger === "undefined" ? Object : Logger
2879
2925
  ])
2880
- ], HttpLoggerInterceptor);
2926
+ ], LoggerService2);
2881
2927
 
2882
2928
  // src/logger/logger.module.ts
2883
2929
  function _ts_decorate17(decorators, target, key, desc) {
@@ -3150,7 +3196,7 @@ var LoggerModule = class _LoggerModule {
3150
3196
  * ```
3151
3197
  */
3152
3198
  static forRootAsync(options) {
3153
- const asyncProviders = this.createAsyncProviders(options);
3199
+ const asyncProviders = _LoggerModule.createAsyncProviders(options);
3154
3200
  return {
3155
3201
  module: _LoggerModule,
3156
3202
  imports: options.imports || [],
@@ -3226,7 +3272,7 @@ var LoggerModule = class _LoggerModule {
3226
3272
  * Configures middleware for the module.
3227
3273
  * Middleware is registered globally in main.ts using Fastify hooks.
3228
3274
  */
3229
- configure(consumer) {
3275
+ configure(_consumer) {
3230
3276
  }
3231
3277
  /**
3232
3278
  * Creates async providers for dynamic module configuration.
@@ -3234,11 +3280,11 @@ var LoggerModule = class _LoggerModule {
3234
3280
  static createAsyncProviders(options) {
3235
3281
  if (options.useFactory) {
3236
3282
  return [
3237
- this.createAsyncOptionsProvider(options)
3283
+ _LoggerModule.createAsyncOptionsProvider(options)
3238
3284
  ];
3239
3285
  }
3240
3286
  const providers = [
3241
- this.createAsyncOptionsProvider(options)
3287
+ _LoggerModule.createAsyncOptionsProvider(options)
3242
3288
  ];
3243
3289
  if (options.useClass) {
3244
3290
  providers.push({
@@ -3256,7 +3302,7 @@ var LoggerModule = class _LoggerModule {
3256
3302
  return {
3257
3303
  provide: LOGGER_MODULE_OPTIONS,
3258
3304
  useFactory: /* @__PURE__ */ __name(async (...args) => {
3259
- const userOptions = await options.useFactory(...args);
3305
+ const userOptions = await options.useFactory?.(...args);
3260
3306
  return mergeWithDefaults(userOptions);
3261
3307
  }, "useFactory"),
3262
3308
  inject: options.inject || []
@@ -3303,7 +3349,7 @@ export {
3303
3349
  CsrfGuard,
3304
3350
  DEFAULT_CORRELATION_HEADER,
3305
3351
  DatabaseModule,
3306
- ForbiddenException2 as ForbiddenException,
3352
+ ForbiddenException,
3307
3353
  GoneException,
3308
3354
  HttpExceptionFilter,
3309
3355
  HttpLoggerInterceptor,
@@ -3319,13 +3365,13 @@ export {
3319
3365
  Onboarding,
3320
3366
  PayloadTooLargeException,
3321
3367
  PrimaryBaseRepository,
3322
- PrimaryDatabaseService,
3368
+ PrimaryDatabaseService2 as PrimaryDatabaseService,
3323
3369
  Public,
3324
3370
  RequestTimeoutException,
3325
3371
  ServiceUnavailableException,
3326
3372
  Tenant,
3327
3373
  TenantBaseRepository,
3328
- TenantContextService,
3374
+ TenantContextService2 as TenantContextService,
3329
3375
  TenantDatabaseService,
3330
3376
  TooManyRequestsException,
3331
3377
  UnauthorizedException4 as UnauthorizedException,
@@ -3334,11 +3380,19 @@ export {
3334
3380
  ValidationException,
3335
3381
  VrittiAuthGuard,
3336
3382
  addCorrelationIdToResponse,
3383
+ configureApiSdk,
3337
3384
  correlationStorage,
3385
+ defineConfig,
3338
3386
  generateCorrelationId,
3387
+ getConfig,
3339
3388
  getCorrelationContext,
3340
3389
  getHttpStatusTitle,
3390
+ getJwtExpiry,
3391
+ getRefreshCookieOptions,
3392
+ hashToken,
3393
+ resetConfig,
3341
3394
  runWithCorrelationContext,
3342
- updateCorrelationContext
3395
+ updateCorrelationContext,
3396
+ verifyTokenHash
3343
3397
  };
3344
3398
  //# sourceMappingURL=index.js.map