@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.cjs +1390 -1328
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1066 -892
- package/dist/index.d.ts +1066 -892
- package/dist/index.js +1645 -1591
- package/dist/index.js.map +1 -1
- package/package.json +9 -15
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
|
|
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
|
|
68
|
-
* Cookie name
|
|
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
|
|
76
|
-
|
|
77
|
-
|
|
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 (
|
|
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
|
-
|
|
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
|
-
],
|
|
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
|
-
|
|
206
|
+
RequestService2
|
|
130
207
|
],
|
|
131
208
|
exports: [
|
|
132
|
-
|
|
209
|
+
RequestService2
|
|
133
210
|
]
|
|
134
211
|
})
|
|
135
212
|
], RequestModule);
|
|
136
213
|
|
|
137
214
|
// src/auth/guards/vritti-auth.guard.ts
|
|
138
|
-
import { Injectable as
|
|
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/
|
|
151
|
-
|
|
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/
|
|
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
|
-
|
|
247
|
+
_configService;
|
|
399
248
|
jwtService;
|
|
400
249
|
primaryDatabase;
|
|
401
250
|
requestService;
|
|
402
251
|
logger = new Logger2(_VrittiAuthGuard.name);
|
|
403
|
-
constructor(reflector,
|
|
252
|
+
constructor(reflector, _configService, jwtService, primaryDatabase, requestService) {
|
|
404
253
|
this.reflector = reflector;
|
|
405
|
-
this.
|
|
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
|
-
|
|
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
|
|
532
|
-
*
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
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
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
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
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
this.logger.
|
|
555
|
-
|
|
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 =
|
|
588
|
-
|
|
407
|
+
VrittiAuthGuard = _ts_decorate3([
|
|
408
|
+
Injectable2({
|
|
589
409
|
scope: Scope2.REQUEST
|
|
590
410
|
}),
|
|
591
|
-
|
|
592
|
-
|
|
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
|
|
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(
|
|
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 =
|
|
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/
|
|
666
|
-
|
|
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
|
-
|
|
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(
|
|
763
|
-
function
|
|
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(
|
|
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
|
|
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 =
|
|
818
|
-
|
|
819
|
-
scope:
|
|
560
|
+
MessageTenantContextInterceptor = _ts_decorate5([
|
|
561
|
+
Injectable3({
|
|
562
|
+
scope: Scope3.REQUEST
|
|
820
563
|
}),
|
|
821
|
-
|
|
822
|
-
|
|
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
|
|
829
|
-
|
|
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(
|
|
837
|
-
function
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
898
|
-
|
|
899
|
-
scope:
|
|
639
|
+
TenantContextInterceptor = _ts_decorate6([
|
|
640
|
+
Injectable4({
|
|
641
|
+
scope: Scope4.REQUEST
|
|
900
642
|
}),
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
typeof
|
|
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/
|
|
911
|
-
import { Inject as
|
|
912
|
-
import {
|
|
913
|
-
import { drizzle
|
|
914
|
-
|
|
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(
|
|
921
|
-
function
|
|
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(
|
|
925
|
-
function
|
|
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(
|
|
931
|
-
var
|
|
673
|
+
__name(_ts_param2, "_ts_param");
|
|
674
|
+
var PrimaryDatabaseService2 = class _PrimaryDatabaseService {
|
|
932
675
|
static {
|
|
933
|
-
__name(this, "
|
|
676
|
+
__name(this, "PrimaryDatabaseService");
|
|
934
677
|
}
|
|
935
678
|
options;
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
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.
|
|
947
|
-
this.startConnectionCleaner();
|
|
690
|
+
this.cacheTTL = options.connectionCacheTTL || 3e5;
|
|
948
691
|
}
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
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
|
-
*
|
|
698
|
+
* Initialize connection to primary database using Drizzle
|
|
962
699
|
*/
|
|
963
|
-
|
|
964
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
979
|
-
|
|
980
|
-
|
|
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
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
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
|
-
*
|
|
744
|
+
* Mask password in connection URL for logging
|
|
995
745
|
*/
|
|
996
|
-
|
|
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
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
const
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
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
|
|
1014
|
-
throw new
|
|
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
|
-
*
|
|
804
|
+
* Cache tenant information with TTL
|
|
1019
805
|
*/
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
1035
|
-
|
|
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
|
-
*
|
|
831
|
+
* Clear all cached tenant configurations
|
|
1039
832
|
*/
|
|
1040
|
-
|
|
1041
|
-
const
|
|
1042
|
-
this.
|
|
1043
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
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
|
|
852
|
+
* Get the Drizzle schema
|
|
1076
853
|
*/
|
|
1077
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
1087
|
-
return
|
|
865
|
+
decrypt(encrypted) {
|
|
866
|
+
return encrypted;
|
|
1088
867
|
}
|
|
1089
868
|
async onModuleDestroy() {
|
|
1090
|
-
if (this.
|
|
1091
|
-
|
|
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
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
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
|
-
],
|
|
882
|
+
], PrimaryDatabaseService2);
|
|
1115
883
|
|
|
1116
|
-
// src/database/
|
|
1117
|
-
|
|
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(
|
|
1124
|
-
var
|
|
892
|
+
__name(_ts_decorate8, "_ts_decorate");
|
|
893
|
+
var TenantContextService2 = class {
|
|
1125
894
|
static {
|
|
1126
|
-
__name(this, "
|
|
895
|
+
__name(this, "TenantContextService");
|
|
1127
896
|
}
|
|
897
|
+
tenantInfo = null;
|
|
1128
898
|
/**
|
|
1129
|
-
*
|
|
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
|
-
*
|
|
1138
|
-
*
|
|
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
|
-
* @
|
|
1141
|
-
*
|
|
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
|
-
|
|
1156
|
-
|
|
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
|
-
*
|
|
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
|
-
* @
|
|
1170
|
-
*
|
|
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
|
-
|
|
1178
|
-
|
|
921
|
+
getTenant() {
|
|
922
|
+
if (!this.tenantInfo) {
|
|
923
|
+
throw new UnauthorizedException3("Tenant context not set");
|
|
924
|
+
}
|
|
925
|
+
return this.tenantInfo;
|
|
1179
926
|
}
|
|
1180
927
|
/**
|
|
1181
|
-
*
|
|
928
|
+
* Check if tenant context has been set
|
|
1182
929
|
*
|
|
1183
|
-
* @
|
|
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
|
-
|
|
1188
|
-
|
|
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
|
-
*
|
|
1247
|
-
*
|
|
1248
|
-
*
|
|
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
|
-
|
|
1251
|
-
|
|
944
|
+
clearTenant() {
|
|
945
|
+
this.tenantInfo = null;
|
|
1252
946
|
}
|
|
1253
947
|
/**
|
|
1254
|
-
*
|
|
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
|
-
* @
|
|
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
|
-
|
|
1268
|
-
|
|
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
|
-
*
|
|
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
|
-
*
|
|
1288
|
-
* super(database, users);
|
|
1289
|
-
* }
|
|
1290
|
-
* ```
|
|
958
|
+
* @returns Tenant subdomain or null
|
|
1291
959
|
*/
|
|
1292
|
-
|
|
1293
|
-
this.
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
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
|
-
*
|
|
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
|
-
* @
|
|
1307
|
-
*
|
|
1308
|
-
*
|
|
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
|
-
|
|
1315
|
-
this.
|
|
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
|
-
*
|
|
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
|
-
|
|
1331
|
-
this.
|
|
1332
|
-
return this.model.findFirst({
|
|
1333
|
-
where: {
|
|
1334
|
-
id
|
|
1335
|
-
}
|
|
1336
|
-
});
|
|
1023
|
+
get schema() {
|
|
1024
|
+
return this.options.drizzleSchema;
|
|
1337
1025
|
}
|
|
1338
1026
|
/**
|
|
1339
|
-
*
|
|
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
|
-
*
|
|
1350
|
-
*
|
|
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
|
-
*
|
|
1353
|
-
*
|
|
1354
|
-
*
|
|
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
|
-
|
|
1360
|
-
this.
|
|
1361
|
-
|
|
1362
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
1438
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
1456
|
-
this.
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
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
|
-
*
|
|
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
|
|
1478
|
-
|
|
1479
|
-
const
|
|
1480
|
-
|
|
1481
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
}
|
|
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
|
-
*
|
|
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
|
-
|
|
1530
|
-
|
|
1531
|
-
return count > 0;
|
|
1146
|
+
maskPassword(url) {
|
|
1147
|
+
return url.replace(/:([^@]+)@/, ":****@");
|
|
1532
1148
|
}
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
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
|
-
*
|
|
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 (
|
|
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
|
|
1565
|
-
* const
|
|
1566
|
-
* where:
|
|
1567
|
-
* with: {
|
|
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
|
-
|
|
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
|
|
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 {
|
|
1361
|
+
* import { users } from '@/db/schema';
|
|
1583
1362
|
*
|
|
1584
|
-
* constructor(database:
|
|
1585
|
-
* super(database,
|
|
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
|
-
|
|
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
|
|
1605
|
-
*
|
|
1606
|
-
*
|
|
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
|
|
1400
|
+
* @returns Promise resolving to the record or undefined if not found
|
|
1621
1401
|
*
|
|
1622
1402
|
* @example
|
|
1623
1403
|
* ```typescript
|
|
1624
|
-
* const
|
|
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
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
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 -
|
|
1637
|
-
* @returns Promise resolving to the record or
|
|
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
|
-
*
|
|
1642
|
-
* const
|
|
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
|
-
|
|
1648
|
-
|
|
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
|
-
*
|
|
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
|
|
1665
|
-
* where:
|
|
1666
|
-
* orderBy: desc
|
|
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
|
-
|
|
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
|
|
1699
|
-
*
|
|
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(
|
|
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
|
|
1721
|
-
* eq(
|
|
1722
|
-
* {
|
|
1507
|
+
* const result = await userRepository.updateMany(
|
|
1508
|
+
* eq(users.accountStatus, 'PENDING'),
|
|
1509
|
+
* { accountStatus: 'ACTIVE' }
|
|
1723
1510
|
* );
|
|
1724
|
-
* console.log(`Updated ${result.count}
|
|
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
|
|
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(
|
|
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
|
|
1762
|
-
* lt(
|
|
1548
|
+
* const result = await userRepository.deleteMany(
|
|
1549
|
+
* lt(users.createdAt, new Date('2020-01-01'))
|
|
1763
1550
|
* );
|
|
1764
|
-
* console.log(`Deleted ${result.count}
|
|
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
|
|
1785
|
-
* const total = await
|
|
1571
|
+
* // Count all users
|
|
1572
|
+
* const total = await userRepository.count();
|
|
1786
1573
|
*
|
|
1787
|
-
* // Count active
|
|
1788
|
-
* const activeCount = await
|
|
1789
|
-
* eq(
|
|
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:
|
|
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
|
|
1815
|
-
* eq(
|
|
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/
|
|
1826
|
-
import {
|
|
1827
|
-
|
|
1828
|
-
|
|
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, "
|
|
1864
|
-
}
|
|
1865
|
-
reflector;
|
|
1866
|
-
logger = new Logger8(_CsrfGuard.name);
|
|
1867
|
-
constructor(reflector) {
|
|
1868
|
-
this.reflector = reflector;
|
|
1617
|
+
__name(this, "TenantBaseRepository");
|
|
1869
1618
|
}
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
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
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
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
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
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
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
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
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
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
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
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
|
|
2036
|
-
var BaseFieldException = class extends
|
|
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 ||
|
|
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-
|
|
2085
|
-
|
|
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/
|
|
1992
|
+
// src/exceptions/conflict.exception.ts
|
|
2104
1993
|
import { HttpStatus as HttpStatus4 } from "@nestjs/common";
|
|
2105
|
-
var
|
|
1994
|
+
var ConflictException = class extends BaseFieldException {
|
|
2106
1995
|
static {
|
|
2107
|
-
__name(this, "
|
|
1996
|
+
__name(this, "ConflictException");
|
|
2108
1997
|
}
|
|
2109
1998
|
constructor(messageOrField, fieldMessageOrDetail, detail) {
|
|
2110
1999
|
if (Array.isArray(messageOrField)) {
|
|
2111
|
-
super(messageOrField, HttpStatus4.
|
|
2000
|
+
super(messageOrField, HttpStatus4.CONFLICT, fieldMessageOrDetail);
|
|
2112
2001
|
} else if (detail !== void 0) {
|
|
2113
|
-
super(messageOrField, fieldMessageOrDetail, HttpStatus4.
|
|
2002
|
+
super(messageOrField, fieldMessageOrDetail, HttpStatus4.CONFLICT, detail);
|
|
2114
2003
|
} else if (fieldMessageOrDetail) {
|
|
2115
|
-
super(messageOrField, fieldMessageOrDetail, HttpStatus4.
|
|
2004
|
+
super(messageOrField, fieldMessageOrDetail, HttpStatus4.CONFLICT);
|
|
2116
2005
|
} else {
|
|
2117
|
-
super(messageOrField, HttpStatus4.
|
|
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
|
|
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/
|
|
2030
|
+
// src/exceptions/gone.exception.ts
|
|
2142
2031
|
import { HttpStatus as HttpStatus6 } from "@nestjs/common";
|
|
2143
|
-
var
|
|
2032
|
+
var GoneException = class extends BaseFieldException {
|
|
2144
2033
|
static {
|
|
2145
|
-
__name(this, "
|
|
2034
|
+
__name(this, "GoneException");
|
|
2146
2035
|
}
|
|
2147
2036
|
constructor(messageOrField, fieldMessageOrDetail, detail) {
|
|
2148
2037
|
if (Array.isArray(messageOrField)) {
|
|
2149
|
-
super(messageOrField, HttpStatus6.
|
|
2038
|
+
super(messageOrField, HttpStatus6.GONE, fieldMessageOrDetail);
|
|
2150
2039
|
} else if (detail !== void 0) {
|
|
2151
|
-
super(messageOrField, fieldMessageOrDetail, HttpStatus6.
|
|
2040
|
+
super(messageOrField, fieldMessageOrDetail, HttpStatus6.GONE, detail);
|
|
2152
2041
|
} else if (fieldMessageOrDetail) {
|
|
2153
|
-
super(messageOrField, fieldMessageOrDetail, HttpStatus6.
|
|
2042
|
+
super(messageOrField, fieldMessageOrDetail, HttpStatus6.GONE);
|
|
2154
2043
|
} else {
|
|
2155
|
-
super(messageOrField, HttpStatus6.
|
|
2044
|
+
super(messageOrField, HttpStatus6.GONE);
|
|
2156
2045
|
}
|
|
2157
2046
|
}
|
|
2158
2047
|
};
|
|
2159
2048
|
|
|
2160
|
-
// src/exceptions/
|
|
2049
|
+
// src/exceptions/internal-server-error.exception.ts
|
|
2161
2050
|
import { HttpStatus as HttpStatus7 } from "@nestjs/common";
|
|
2162
|
-
var
|
|
2051
|
+
var InternalServerErrorException3 = class extends BaseFieldException {
|
|
2163
2052
|
static {
|
|
2164
|
-
__name(this, "
|
|
2053
|
+
__name(this, "InternalServerErrorException");
|
|
2165
2054
|
}
|
|
2166
2055
|
constructor(messageOrField, fieldMessageOrDetail, detail) {
|
|
2167
2056
|
if (Array.isArray(messageOrField)) {
|
|
2168
|
-
super(messageOrField, HttpStatus7.
|
|
2057
|
+
super(messageOrField, HttpStatus7.INTERNAL_SERVER_ERROR, fieldMessageOrDetail);
|
|
2169
2058
|
} else if (detail !== void 0) {
|
|
2170
|
-
super(messageOrField, fieldMessageOrDetail, HttpStatus7.
|
|
2059
|
+
super(messageOrField, fieldMessageOrDetail, HttpStatus7.INTERNAL_SERVER_ERROR, detail);
|
|
2171
2060
|
} else if (fieldMessageOrDetail) {
|
|
2172
|
-
super(messageOrField, fieldMessageOrDetail, HttpStatus7.
|
|
2061
|
+
super(messageOrField, fieldMessageOrDetail, HttpStatus7.INTERNAL_SERVER_ERROR);
|
|
2173
2062
|
} else {
|
|
2174
|
-
super(messageOrField, HttpStatus7.
|
|
2063
|
+
super(messageOrField, HttpStatus7.INTERNAL_SERVER_ERROR);
|
|
2175
2064
|
}
|
|
2176
2065
|
}
|
|
2177
2066
|
};
|
|
2178
2067
|
|
|
2179
|
-
// src/exceptions/
|
|
2068
|
+
// src/exceptions/method-not-allowed.exception.ts
|
|
2180
2069
|
import { HttpStatus as HttpStatus8 } from "@nestjs/common";
|
|
2181
|
-
var
|
|
2070
|
+
var MethodNotAllowedException = class extends BaseFieldException {
|
|
2182
2071
|
static {
|
|
2183
|
-
__name(this, "
|
|
2072
|
+
__name(this, "MethodNotAllowedException");
|
|
2184
2073
|
}
|
|
2185
2074
|
constructor(messageOrField, fieldMessageOrDetail, detail) {
|
|
2186
2075
|
if (Array.isArray(messageOrField)) {
|
|
2187
|
-
super(messageOrField, HttpStatus8.
|
|
2076
|
+
super(messageOrField, HttpStatus8.METHOD_NOT_ALLOWED, fieldMessageOrDetail);
|
|
2188
2077
|
} else if (detail !== void 0) {
|
|
2189
|
-
super(messageOrField, fieldMessageOrDetail, HttpStatus8.
|
|
2078
|
+
super(messageOrField, fieldMessageOrDetail, HttpStatus8.METHOD_NOT_ALLOWED, detail);
|
|
2190
2079
|
} else if (fieldMessageOrDetail) {
|
|
2191
|
-
super(messageOrField, fieldMessageOrDetail, HttpStatus8.
|
|
2080
|
+
super(messageOrField, fieldMessageOrDetail, HttpStatus8.METHOD_NOT_ALLOWED);
|
|
2192
2081
|
} else {
|
|
2193
|
-
super(messageOrField, HttpStatus8.
|
|
2082
|
+
super(messageOrField, HttpStatus8.METHOD_NOT_ALLOWED);
|
|
2194
2083
|
}
|
|
2195
2084
|
}
|
|
2196
2085
|
};
|
|
2197
2086
|
|
|
2198
|
-
// src/exceptions/
|
|
2087
|
+
// src/exceptions/not-acceptable.exception.ts
|
|
2199
2088
|
import { HttpStatus as HttpStatus9 } from "@nestjs/common";
|
|
2200
|
-
var
|
|
2089
|
+
var NotAcceptableException = class extends BaseFieldException {
|
|
2201
2090
|
static {
|
|
2202
|
-
__name(this, "
|
|
2091
|
+
__name(this, "NotAcceptableException");
|
|
2203
2092
|
}
|
|
2204
|
-
constructor(
|
|
2205
|
-
|
|
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/
|
|
2106
|
+
// src/exceptions/not-found.exception.ts
|
|
2210
2107
|
import { HttpStatus as HttpStatus10 } from "@nestjs/common";
|
|
2211
|
-
var
|
|
2108
|
+
var NotFoundException = class extends BaseFieldException {
|
|
2212
2109
|
static {
|
|
2213
|
-
__name(this, "
|
|
2110
|
+
__name(this, "NotFoundException");
|
|
2214
2111
|
}
|
|
2215
2112
|
constructor(messageOrField, fieldMessageOrDetail, detail) {
|
|
2216
2113
|
if (Array.isArray(messageOrField)) {
|
|
2217
|
-
super(messageOrField, HttpStatus10.
|
|
2114
|
+
super(messageOrField, HttpStatus10.NOT_FOUND, fieldMessageOrDetail);
|
|
2218
2115
|
} else if (detail !== void 0) {
|
|
2219
|
-
super(messageOrField, fieldMessageOrDetail, HttpStatus10.
|
|
2116
|
+
super(messageOrField, fieldMessageOrDetail, HttpStatus10.NOT_FOUND, detail);
|
|
2220
2117
|
} else if (fieldMessageOrDetail) {
|
|
2221
|
-
super(messageOrField, fieldMessageOrDetail, HttpStatus10.
|
|
2118
|
+
super(messageOrField, fieldMessageOrDetail, HttpStatus10.NOT_FOUND);
|
|
2222
2119
|
} else {
|
|
2223
|
-
super(messageOrField, HttpStatus10.
|
|
2120
|
+
super(messageOrField, HttpStatus10.NOT_FOUND);
|
|
2224
2121
|
}
|
|
2225
2122
|
}
|
|
2226
2123
|
};
|
|
2227
2124
|
|
|
2228
|
-
// src/exceptions/
|
|
2125
|
+
// src/exceptions/not-implemented.exception.ts
|
|
2229
2126
|
import { HttpStatus as HttpStatus11 } from "@nestjs/common";
|
|
2230
|
-
var
|
|
2127
|
+
var NotImplementedException = class extends BaseFieldException {
|
|
2231
2128
|
static {
|
|
2232
|
-
__name(this, "
|
|
2129
|
+
__name(this, "NotImplementedException");
|
|
2233
2130
|
}
|
|
2234
2131
|
constructor(messageOrField, fieldMessageOrDetail, detail) {
|
|
2235
2132
|
if (Array.isArray(messageOrField)) {
|
|
2236
|
-
super(messageOrField, HttpStatus11.
|
|
2133
|
+
super(messageOrField, HttpStatus11.NOT_IMPLEMENTED, fieldMessageOrDetail);
|
|
2237
2134
|
} else if (detail !== void 0) {
|
|
2238
|
-
super(messageOrField, fieldMessageOrDetail, HttpStatus11.
|
|
2135
|
+
super(messageOrField, fieldMessageOrDetail, HttpStatus11.NOT_IMPLEMENTED, detail);
|
|
2239
2136
|
} else if (fieldMessageOrDetail) {
|
|
2240
|
-
super(messageOrField, fieldMessageOrDetail, HttpStatus11.
|
|
2137
|
+
super(messageOrField, fieldMessageOrDetail, HttpStatus11.NOT_IMPLEMENTED);
|
|
2241
2138
|
} else {
|
|
2242
|
-
super(messageOrField, HttpStatus11.
|
|
2139
|
+
super(messageOrField, HttpStatus11.NOT_IMPLEMENTED);
|
|
2243
2140
|
}
|
|
2244
2141
|
}
|
|
2245
2142
|
};
|
|
2246
2143
|
|
|
2247
|
-
// src/exceptions/
|
|
2144
|
+
// src/exceptions/payload-too-large.exception.ts
|
|
2248
2145
|
import { HttpStatus as HttpStatus12 } from "@nestjs/common";
|
|
2249
|
-
var
|
|
2146
|
+
var PayloadTooLargeException = class extends BaseFieldException {
|
|
2250
2147
|
static {
|
|
2251
|
-
__name(this, "
|
|
2148
|
+
__name(this, "PayloadTooLargeException");
|
|
2252
2149
|
}
|
|
2253
2150
|
constructor(messageOrField, fieldMessageOrDetail, detail) {
|
|
2254
2151
|
if (Array.isArray(messageOrField)) {
|
|
2255
|
-
super(messageOrField, HttpStatus12.
|
|
2152
|
+
super(messageOrField, HttpStatus12.PAYLOAD_TOO_LARGE, fieldMessageOrDetail);
|
|
2256
2153
|
} else if (detail !== void 0) {
|
|
2257
|
-
super(messageOrField, fieldMessageOrDetail, HttpStatus12.
|
|
2154
|
+
super(messageOrField, fieldMessageOrDetail, HttpStatus12.PAYLOAD_TOO_LARGE, detail);
|
|
2258
2155
|
} else if (fieldMessageOrDetail) {
|
|
2259
|
-
super(messageOrField, fieldMessageOrDetail, HttpStatus12.
|
|
2156
|
+
super(messageOrField, fieldMessageOrDetail, HttpStatus12.PAYLOAD_TOO_LARGE);
|
|
2260
2157
|
} else {
|
|
2261
|
-
super(messageOrField, HttpStatus12.
|
|
2158
|
+
super(messageOrField, HttpStatus12.PAYLOAD_TOO_LARGE);
|
|
2262
2159
|
}
|
|
2263
2160
|
}
|
|
2264
2161
|
};
|
|
2265
2162
|
|
|
2266
|
-
// src/exceptions/
|
|
2163
|
+
// src/exceptions/request-timeout.exception.ts
|
|
2267
2164
|
import { HttpStatus as HttpStatus13 } from "@nestjs/common";
|
|
2268
|
-
var
|
|
2165
|
+
var RequestTimeoutException = class extends BaseFieldException {
|
|
2269
2166
|
static {
|
|
2270
|
-
__name(this, "
|
|
2167
|
+
__name(this, "RequestTimeoutException");
|
|
2271
2168
|
}
|
|
2272
2169
|
constructor(messageOrField, fieldMessageOrDetail, detail) {
|
|
2273
2170
|
if (Array.isArray(messageOrField)) {
|
|
2274
|
-
super(messageOrField, HttpStatus13.
|
|
2171
|
+
super(messageOrField, HttpStatus13.REQUEST_TIMEOUT, fieldMessageOrDetail);
|
|
2275
2172
|
} else if (detail !== void 0) {
|
|
2276
|
-
super(messageOrField, fieldMessageOrDetail, HttpStatus13.
|
|
2173
|
+
super(messageOrField, fieldMessageOrDetail, HttpStatus13.REQUEST_TIMEOUT, detail);
|
|
2277
2174
|
} else if (fieldMessageOrDetail) {
|
|
2278
|
-
super(messageOrField, fieldMessageOrDetail, HttpStatus13.
|
|
2175
|
+
super(messageOrField, fieldMessageOrDetail, HttpStatus13.REQUEST_TIMEOUT);
|
|
2279
2176
|
} else {
|
|
2280
|
-
super(messageOrField, HttpStatus13.
|
|
2177
|
+
super(messageOrField, HttpStatus13.REQUEST_TIMEOUT);
|
|
2281
2178
|
}
|
|
2282
2179
|
}
|
|
2283
2180
|
};
|
|
2284
2181
|
|
|
2285
|
-
// src/exceptions/
|
|
2182
|
+
// src/exceptions/service-unavailable.exception.ts
|
|
2286
2183
|
import { HttpStatus as HttpStatus14 } from "@nestjs/common";
|
|
2287
|
-
var
|
|
2184
|
+
var ServiceUnavailableException = class extends BaseFieldException {
|
|
2288
2185
|
static {
|
|
2289
|
-
__name(this, "
|
|
2186
|
+
__name(this, "ServiceUnavailableException");
|
|
2290
2187
|
}
|
|
2291
2188
|
constructor(messageOrField, fieldMessageOrDetail, detail) {
|
|
2292
2189
|
if (Array.isArray(messageOrField)) {
|
|
2293
|
-
super(messageOrField, HttpStatus14.
|
|
2190
|
+
super(messageOrField, HttpStatus14.SERVICE_UNAVAILABLE, fieldMessageOrDetail);
|
|
2294
2191
|
} else if (detail !== void 0) {
|
|
2295
|
-
super(messageOrField, fieldMessageOrDetail, HttpStatus14.
|
|
2192
|
+
super(messageOrField, fieldMessageOrDetail, HttpStatus14.SERVICE_UNAVAILABLE, detail);
|
|
2296
2193
|
} else if (fieldMessageOrDetail) {
|
|
2297
|
-
super(messageOrField, fieldMessageOrDetail, HttpStatus14.
|
|
2194
|
+
super(messageOrField, fieldMessageOrDetail, HttpStatus14.SERVICE_UNAVAILABLE);
|
|
2298
2195
|
} else {
|
|
2299
|
-
super(messageOrField, HttpStatus14.
|
|
2196
|
+
super(messageOrField, HttpStatus14.SERVICE_UNAVAILABLE);
|
|
2300
2197
|
}
|
|
2301
2198
|
}
|
|
2302
2199
|
};
|
|
2303
2200
|
|
|
2304
|
-
// src/exceptions/
|
|
2201
|
+
// src/exceptions/too-many-requests.exception.ts
|
|
2305
2202
|
import { HttpStatus as HttpStatus15 } from "@nestjs/common";
|
|
2306
|
-
var
|
|
2203
|
+
var TooManyRequestsException = class extends BaseFieldException {
|
|
2307
2204
|
static {
|
|
2308
|
-
__name(this, "
|
|
2205
|
+
__name(this, "TooManyRequestsException");
|
|
2309
2206
|
}
|
|
2310
2207
|
constructor(messageOrField, fieldMessageOrDetail, detail) {
|
|
2311
2208
|
if (Array.isArray(messageOrField)) {
|
|
2312
|
-
super(messageOrField, HttpStatus15.
|
|
2209
|
+
super(messageOrField, HttpStatus15.TOO_MANY_REQUESTS, fieldMessageOrDetail);
|
|
2313
2210
|
} else if (detail !== void 0) {
|
|
2314
|
-
super(messageOrField, fieldMessageOrDetail, HttpStatus15.
|
|
2211
|
+
super(messageOrField, fieldMessageOrDetail, HttpStatus15.TOO_MANY_REQUESTS, detail);
|
|
2315
2212
|
} else if (fieldMessageOrDetail) {
|
|
2316
|
-
super(messageOrField, fieldMessageOrDetail, HttpStatus15.
|
|
2213
|
+
super(messageOrField, fieldMessageOrDetail, HttpStatus15.TOO_MANY_REQUESTS);
|
|
2317
2214
|
} else {
|
|
2318
|
-
super(messageOrField, HttpStatus15.
|
|
2215
|
+
super(messageOrField, HttpStatus15.TOO_MANY_REQUESTS);
|
|
2319
2216
|
}
|
|
2320
2217
|
}
|
|
2321
2218
|
};
|
|
2322
2219
|
|
|
2323
|
-
// src/exceptions/
|
|
2220
|
+
// src/exceptions/unauthorized.exception.ts
|
|
2324
2221
|
import { HttpStatus as HttpStatus16 } from "@nestjs/common";
|
|
2325
|
-
var
|
|
2222
|
+
var UnauthorizedException4 = class extends BaseFieldException {
|
|
2326
2223
|
static {
|
|
2327
|
-
__name(this, "
|
|
2224
|
+
__name(this, "UnauthorizedException");
|
|
2328
2225
|
}
|
|
2329
2226
|
constructor(messageOrField, fieldMessageOrDetail, detail) {
|
|
2330
2227
|
if (Array.isArray(messageOrField)) {
|
|
2331
|
-
super(messageOrField, HttpStatus16.
|
|
2228
|
+
super(messageOrField, HttpStatus16.UNAUTHORIZED, fieldMessageOrDetail);
|
|
2332
2229
|
} else if (detail !== void 0) {
|
|
2333
|
-
super(messageOrField, fieldMessageOrDetail, HttpStatus16.
|
|
2230
|
+
super(messageOrField, fieldMessageOrDetail, HttpStatus16.UNAUTHORIZED, detail);
|
|
2334
2231
|
} else if (fieldMessageOrDetail) {
|
|
2335
|
-
super(messageOrField, fieldMessageOrDetail, HttpStatus16.
|
|
2232
|
+
super(messageOrField, fieldMessageOrDetail, HttpStatus16.UNAUTHORIZED);
|
|
2336
2233
|
} else {
|
|
2337
|
-
super(messageOrField, HttpStatus16.
|
|
2234
|
+
super(messageOrField, HttpStatus16.UNAUTHORIZED);
|
|
2338
2235
|
}
|
|
2339
2236
|
}
|
|
2340
2237
|
};
|
|
2341
2238
|
|
|
2342
|
-
// src/exceptions/
|
|
2239
|
+
// src/exceptions/unprocessable-entity.exception.ts
|
|
2343
2240
|
import { HttpStatus as HttpStatus17 } from "@nestjs/common";
|
|
2344
|
-
var
|
|
2241
|
+
var UnprocessableEntityException = class extends BaseFieldException {
|
|
2345
2242
|
static {
|
|
2346
|
-
__name(this, "
|
|
2243
|
+
__name(this, "UnprocessableEntityException");
|
|
2347
2244
|
}
|
|
2348
2245
|
constructor(messageOrField, fieldMessageOrDetail, detail) {
|
|
2349
2246
|
if (Array.isArray(messageOrField)) {
|
|
2350
|
-
super(messageOrField, HttpStatus17.
|
|
2247
|
+
super(messageOrField, HttpStatus17.UNPROCESSABLE_ENTITY, fieldMessageOrDetail);
|
|
2351
2248
|
} else if (detail !== void 0) {
|
|
2352
|
-
super(messageOrField, fieldMessageOrDetail, HttpStatus17.
|
|
2249
|
+
super(messageOrField, fieldMessageOrDetail, HttpStatus17.UNPROCESSABLE_ENTITY, detail);
|
|
2353
2250
|
} else if (fieldMessageOrDetail) {
|
|
2354
|
-
super(messageOrField, fieldMessageOrDetail, HttpStatus17.
|
|
2251
|
+
super(messageOrField, fieldMessageOrDetail, HttpStatus17.UNPROCESSABLE_ENTITY);
|
|
2355
2252
|
} else {
|
|
2356
|
-
super(messageOrField, HttpStatus17.
|
|
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/
|
|
2277
|
+
// src/exceptions/validation.exception.ts
|
|
2381
2278
|
import { HttpStatus as HttpStatus19 } from "@nestjs/common";
|
|
2382
|
-
var
|
|
2279
|
+
var ValidationException = class extends BaseFieldException {
|
|
2383
2280
|
static {
|
|
2384
|
-
__name(this, "
|
|
2281
|
+
__name(this, "ValidationException");
|
|
2385
2282
|
}
|
|
2386
|
-
constructor(
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
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
|
-
|
|
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/
|
|
2400
|
-
import {
|
|
2401
|
-
|
|
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, "
|
|
2389
|
+
__name(this, "CsrfGuard");
|
|
2404
2390
|
}
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
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
|
-
|
|
2419
|
-
|
|
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
|
|
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(
|
|
2467
|
-
function
|
|
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(
|
|
2471
|
-
function
|
|
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(
|
|
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 +=
|
|
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 =
|
|
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
|
-
|
|
2876
|
-
|
|
2877
|
-
typeof
|
|
2878
|
-
typeof
|
|
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
|
-
],
|
|
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 =
|
|
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(
|
|
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
|
-
|
|
3283
|
+
_LoggerModule.createAsyncOptionsProvider(options)
|
|
3238
3284
|
];
|
|
3239
3285
|
}
|
|
3240
3286
|
const providers = [
|
|
3241
|
-
|
|
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
|
-
|
|
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
|