@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.cjs
CHANGED
|
@@ -40,7 +40,7 @@ __export(index_exports, {
|
|
|
40
40
|
CsrfGuard: () => CsrfGuard,
|
|
41
41
|
DEFAULT_CORRELATION_HEADER: () => DEFAULT_CORRELATION_HEADER,
|
|
42
42
|
DatabaseModule: () => DatabaseModule,
|
|
43
|
-
ForbiddenException: () =>
|
|
43
|
+
ForbiddenException: () => ForbiddenException,
|
|
44
44
|
GoneException: () => GoneException,
|
|
45
45
|
HttpExceptionFilter: () => HttpExceptionFilter,
|
|
46
46
|
HttpLoggerInterceptor: () => HttpLoggerInterceptor,
|
|
@@ -56,13 +56,13 @@ __export(index_exports, {
|
|
|
56
56
|
Onboarding: () => Onboarding,
|
|
57
57
|
PayloadTooLargeException: () => PayloadTooLargeException,
|
|
58
58
|
PrimaryBaseRepository: () => PrimaryBaseRepository,
|
|
59
|
-
PrimaryDatabaseService: () =>
|
|
59
|
+
PrimaryDatabaseService: () => PrimaryDatabaseService2,
|
|
60
60
|
Public: () => Public,
|
|
61
61
|
RequestTimeoutException: () => RequestTimeoutException,
|
|
62
62
|
ServiceUnavailableException: () => ServiceUnavailableException,
|
|
63
63
|
Tenant: () => Tenant,
|
|
64
64
|
TenantBaseRepository: () => TenantBaseRepository,
|
|
65
|
-
TenantContextService: () =>
|
|
65
|
+
TenantContextService: () => TenantContextService2,
|
|
66
66
|
TenantDatabaseService: () => TenantDatabaseService,
|
|
67
67
|
TooManyRequestsException: () => TooManyRequestsException,
|
|
68
68
|
UnauthorizedException: () => UnauthorizedException4,
|
|
@@ -71,20 +71,28 @@ __export(index_exports, {
|
|
|
71
71
|
ValidationException: () => ValidationException,
|
|
72
72
|
VrittiAuthGuard: () => VrittiAuthGuard,
|
|
73
73
|
addCorrelationIdToResponse: () => addCorrelationIdToResponse,
|
|
74
|
+
configureApiSdk: () => configureApiSdk,
|
|
74
75
|
correlationStorage: () => correlationStorage,
|
|
76
|
+
defineConfig: () => defineConfig,
|
|
75
77
|
generateCorrelationId: () => generateCorrelationId,
|
|
78
|
+
getConfig: () => getConfig,
|
|
76
79
|
getCorrelationContext: () => getCorrelationContext,
|
|
77
80
|
getHttpStatusTitle: () => getHttpStatusTitle,
|
|
81
|
+
getJwtExpiry: () => getJwtExpiry,
|
|
82
|
+
getRefreshCookieOptions: () => getRefreshCookieOptions,
|
|
83
|
+
hashToken: () => hashToken,
|
|
84
|
+
resetConfig: () => resetConfig,
|
|
78
85
|
runWithCorrelationContext: () => runWithCorrelationContext,
|
|
79
|
-
updateCorrelationContext: () => updateCorrelationContext
|
|
86
|
+
updateCorrelationContext: () => updateCorrelationContext,
|
|
87
|
+
verifyTokenHash: () => verifyTokenHash
|
|
80
88
|
});
|
|
81
89
|
module.exports = __toCommonJS(index_exports);
|
|
82
90
|
|
|
83
91
|
// src/auth/auth-config.module.ts
|
|
84
|
-
var
|
|
85
|
-
var
|
|
86
|
-
var
|
|
87
|
-
var
|
|
92
|
+
var import_common4 = require("@nestjs/common");
|
|
93
|
+
var import_config3 = require("@nestjs/config");
|
|
94
|
+
var import_core2 = require("@nestjs/core");
|
|
95
|
+
var import_jwt = require("@nestjs/jwt");
|
|
88
96
|
|
|
89
97
|
// src/request/request.module.ts
|
|
90
98
|
var import_common2 = require("@nestjs/common");
|
|
@@ -92,6 +100,82 @@ var import_common2 = require("@nestjs/common");
|
|
|
92
100
|
// src/request/services/request.service.ts
|
|
93
101
|
var import_common = require("@nestjs/common");
|
|
94
102
|
var import_core = require("@nestjs/core");
|
|
103
|
+
|
|
104
|
+
// src/config/index.ts
|
|
105
|
+
var defaultConfig = {
|
|
106
|
+
cookie: {
|
|
107
|
+
refreshCookieName: "vritti_refresh",
|
|
108
|
+
refreshCookieMaxAge: 30 * 24 * 60 * 60 * 1e3,
|
|
109
|
+
refreshCookiePath: "/",
|
|
110
|
+
refreshCookieSecure: process.env.NODE_ENV === "production",
|
|
111
|
+
refreshCookieSameSite: "strict"
|
|
112
|
+
},
|
|
113
|
+
jwt: {
|
|
114
|
+
accessTokenExpiry: "15m",
|
|
115
|
+
refreshTokenExpiry: "30d",
|
|
116
|
+
onboardingTokenExpiry: "24h",
|
|
117
|
+
validateTokenBinding: true
|
|
118
|
+
},
|
|
119
|
+
guard: {
|
|
120
|
+
tenantHeaderName: "x-tenant-id",
|
|
121
|
+
authHeaderName: "authorization",
|
|
122
|
+
tokenPrefix: "Bearer"
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
var currentConfig = {
|
|
126
|
+
...defaultConfig
|
|
127
|
+
};
|
|
128
|
+
function defineConfig(config) {
|
|
129
|
+
return config;
|
|
130
|
+
}
|
|
131
|
+
__name(defineConfig, "defineConfig");
|
|
132
|
+
function configureApiSdk(userConfig) {
|
|
133
|
+
currentConfig = {
|
|
134
|
+
cookie: {
|
|
135
|
+
...defaultConfig.cookie,
|
|
136
|
+
...userConfig.cookie || {}
|
|
137
|
+
},
|
|
138
|
+
jwt: {
|
|
139
|
+
...defaultConfig.jwt,
|
|
140
|
+
...userConfig.jwt || {}
|
|
141
|
+
},
|
|
142
|
+
guard: {
|
|
143
|
+
...defaultConfig.guard,
|
|
144
|
+
...userConfig.guard || {}
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
__name(configureApiSdk, "configureApiSdk");
|
|
149
|
+
function getConfig() {
|
|
150
|
+
return currentConfig;
|
|
151
|
+
}
|
|
152
|
+
__name(getConfig, "getConfig");
|
|
153
|
+
function resetConfig() {
|
|
154
|
+
currentConfig = {
|
|
155
|
+
...defaultConfig
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
__name(resetConfig, "resetConfig");
|
|
159
|
+
function getRefreshCookieOptions() {
|
|
160
|
+
return {
|
|
161
|
+
httpOnly: true,
|
|
162
|
+
secure: currentConfig.cookie.refreshCookieSecure,
|
|
163
|
+
sameSite: currentConfig.cookie.refreshCookieSameSite,
|
|
164
|
+
path: currentConfig.cookie.refreshCookiePath,
|
|
165
|
+
maxAge: currentConfig.cookie.refreshCookieMaxAge
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
__name(getRefreshCookieOptions, "getRefreshCookieOptions");
|
|
169
|
+
function getJwtExpiry() {
|
|
170
|
+
return {
|
|
171
|
+
access: currentConfig.jwt.accessTokenExpiry,
|
|
172
|
+
refresh: currentConfig.jwt.refreshTokenExpiry,
|
|
173
|
+
onboarding: currentConfig.jwt.onboardingTokenExpiry
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
__name(getJwtExpiry, "getJwtExpiry");
|
|
177
|
+
|
|
178
|
+
// src/request/services/request.service.ts
|
|
95
179
|
function _ts_decorate(decorators, target, key, desc) {
|
|
96
180
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
97
181
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -109,7 +193,7 @@ function _ts_param(paramIndex, decorator) {
|
|
|
109
193
|
};
|
|
110
194
|
}
|
|
111
195
|
__name(_ts_param, "_ts_param");
|
|
112
|
-
var
|
|
196
|
+
var RequestService2 = class {
|
|
113
197
|
static {
|
|
114
198
|
__name(this, "RequestService");
|
|
115
199
|
}
|
|
@@ -143,21 +227,22 @@ var RequestService = class {
|
|
|
143
227
|
return type === "Bearer" && token ? token : null;
|
|
144
228
|
}
|
|
145
229
|
/**
|
|
146
|
-
* Extract refresh token from
|
|
147
|
-
* Cookie name
|
|
230
|
+
* Extract refresh token from httpOnly cookie
|
|
231
|
+
* Cookie name is configurable via api-sdk config
|
|
148
232
|
* @returns Refresh token or null if not found
|
|
149
233
|
*/
|
|
150
234
|
getRefreshToken() {
|
|
151
235
|
try {
|
|
152
236
|
const cookies = this.request.cookies;
|
|
153
237
|
if (cookies && typeof cookies === "object") {
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
238
|
+
const config = getConfig();
|
|
239
|
+
const refreshToken = cookies[config.cookie.refreshCookieName];
|
|
240
|
+
if (refreshToken) {
|
|
241
|
+
return refreshToken;
|
|
157
242
|
}
|
|
158
243
|
}
|
|
159
244
|
return null;
|
|
160
|
-
} catch (
|
|
245
|
+
} catch (_error) {
|
|
161
246
|
return null;
|
|
162
247
|
}
|
|
163
248
|
}
|
|
@@ -177,7 +262,7 @@ var RequestService = class {
|
|
|
177
262
|
return this.request.headers || {};
|
|
178
263
|
}
|
|
179
264
|
};
|
|
180
|
-
|
|
265
|
+
RequestService2 = _ts_decorate([
|
|
181
266
|
(0, import_common.Injectable)({
|
|
182
267
|
scope: import_common.Scope.REQUEST
|
|
183
268
|
}),
|
|
@@ -186,7 +271,7 @@ RequestService = _ts_decorate([
|
|
|
186
271
|
_ts_metadata("design:paramtypes", [
|
|
187
272
|
typeof FastifyRequest === "undefined" ? Object : FastifyRequest
|
|
188
273
|
])
|
|
189
|
-
],
|
|
274
|
+
], RequestService2);
|
|
190
275
|
|
|
191
276
|
// src/request/request.module.ts
|
|
192
277
|
function _ts_decorate2(decorators, target, key, desc) {
|
|
@@ -205,31 +290,31 @@ RequestModule = _ts_decorate2([
|
|
|
205
290
|
(0, import_common2.Global)(),
|
|
206
291
|
(0, import_common2.Module)({
|
|
207
292
|
providers: [
|
|
208
|
-
|
|
293
|
+
RequestService2
|
|
209
294
|
],
|
|
210
295
|
exports: [
|
|
211
|
-
|
|
296
|
+
RequestService2
|
|
212
297
|
]
|
|
213
298
|
})
|
|
214
299
|
], RequestModule);
|
|
215
300
|
|
|
216
301
|
// src/auth/guards/vritti-auth.guard.ts
|
|
217
|
-
var import_common4 = require("@nestjs/common");
|
|
218
|
-
var import_config = require("@nestjs/config");
|
|
219
|
-
var import_core2 = require("@nestjs/core");
|
|
220
|
-
var import_jwt = require("@nestjs/jwt");
|
|
221
|
-
var jwt = __toESM(require("jsonwebtoken"), 1);
|
|
222
|
-
|
|
223
|
-
// src/database/services/primary-database.service.ts
|
|
224
302
|
var import_common3 = require("@nestjs/common");
|
|
225
|
-
var import_pg = require("pg");
|
|
226
|
-
var import_node_postgres = require("drizzle-orm/node-postgres");
|
|
227
|
-
var import_drizzle_orm = require("drizzle-orm");
|
|
228
303
|
|
|
229
|
-
// src/
|
|
230
|
-
var
|
|
304
|
+
// src/auth/utils/token-hash.util.ts
|
|
305
|
+
var crypto = __toESM(require("crypto"), 1);
|
|
306
|
+
function hashToken(token) {
|
|
307
|
+
return crypto.createHash("sha256").update(token).digest("hex");
|
|
308
|
+
}
|
|
309
|
+
__name(hashToken, "hashToken");
|
|
310
|
+
function verifyTokenHash(token, expectedHash) {
|
|
311
|
+
const computedHash = hashToken(token);
|
|
312
|
+
if (computedHash.length !== expectedHash.length) return false;
|
|
313
|
+
return crypto.timingSafeEqual(Buffer.from(computedHash, "hex"), Buffer.from(expectedHash, "hex"));
|
|
314
|
+
}
|
|
315
|
+
__name(verifyTokenHash, "verifyTokenHash");
|
|
231
316
|
|
|
232
|
-
// src/
|
|
317
|
+
// src/auth/guards/vritti-auth.guard.ts
|
|
233
318
|
function _ts_decorate3(decorators, target, key, desc) {
|
|
234
319
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
235
320
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -241,560 +326,698 @@ function _ts_metadata2(k, v) {
|
|
|
241
326
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
242
327
|
}
|
|
243
328
|
__name(_ts_metadata2, "_ts_metadata");
|
|
244
|
-
|
|
245
|
-
return function(target, key) {
|
|
246
|
-
decorator(target, key, paramIndex);
|
|
247
|
-
};
|
|
248
|
-
}
|
|
249
|
-
__name(_ts_param2, "_ts_param");
|
|
250
|
-
var PrimaryDatabaseService = class _PrimaryDatabaseService {
|
|
329
|
+
var VrittiAuthGuard = class _VrittiAuthGuard {
|
|
251
330
|
static {
|
|
252
|
-
__name(this, "
|
|
331
|
+
__name(this, "VrittiAuthGuard");
|
|
253
332
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
this.
|
|
266
|
-
this.cacheTTL = options.connectionCacheTTL || 3e5;
|
|
333
|
+
reflector;
|
|
334
|
+
_configService;
|
|
335
|
+
jwtService;
|
|
336
|
+
primaryDatabase;
|
|
337
|
+
requestService;
|
|
338
|
+
logger = new import_common3.Logger(_VrittiAuthGuard.name);
|
|
339
|
+
constructor(reflector, _configService, jwtService, primaryDatabase, requestService) {
|
|
340
|
+
this.reflector = reflector;
|
|
341
|
+
this._configService = _configService;
|
|
342
|
+
this.jwtService = jwtService;
|
|
343
|
+
this.primaryDatabase = primaryDatabase;
|
|
344
|
+
this.requestService = requestService;
|
|
267
345
|
}
|
|
268
|
-
async
|
|
269
|
-
|
|
270
|
-
|
|
346
|
+
async canActivate(context) {
|
|
347
|
+
const request = context.switchToHttp().getRequest();
|
|
348
|
+
const isPublic = this.reflector.getAllAndOverride("isPublic", [
|
|
349
|
+
context.getHandler(),
|
|
350
|
+
context.getClass()
|
|
351
|
+
]);
|
|
352
|
+
if (isPublic) {
|
|
353
|
+
this.logger.debug("Public endpoint detected, skipping authentication");
|
|
354
|
+
return true;
|
|
355
|
+
}
|
|
356
|
+
const isOnboarding = this.reflector.getAllAndOverride("isOnboarding", [
|
|
357
|
+
context.getHandler(),
|
|
358
|
+
context.getClass()
|
|
359
|
+
]);
|
|
360
|
+
try {
|
|
361
|
+
const accessToken = this.requestService.getAccessToken();
|
|
362
|
+
if (!accessToken) {
|
|
363
|
+
this.logger.warn("Access token not found in Authorization header");
|
|
364
|
+
throw new import_common3.UnauthorizedException("Access token not found");
|
|
365
|
+
}
|
|
366
|
+
const decodedToken = this.jwtService.decode(accessToken);
|
|
367
|
+
if (!decodedToken) {
|
|
368
|
+
this.logger.warn("Failed to decode access token");
|
|
369
|
+
throw new import_common3.UnauthorizedException("Invalid token format");
|
|
370
|
+
}
|
|
371
|
+
if (isOnboarding) {
|
|
372
|
+
if (decodedToken.type !== "onboarding") {
|
|
373
|
+
this.logger.warn("Onboarding endpoint requires onboarding token");
|
|
374
|
+
throw new import_common3.UnauthorizedException("This endpoint requires an onboarding token");
|
|
375
|
+
}
|
|
376
|
+
const validatedToken2 = this.validateAccessToken(accessToken);
|
|
377
|
+
this.logger.debug("Onboarding token validated successfully");
|
|
378
|
+
this.validateRefreshTokenBinding(context, validatedToken2);
|
|
379
|
+
const userId2 = validatedToken2.userId;
|
|
380
|
+
request.user = {
|
|
381
|
+
id: userId2
|
|
382
|
+
};
|
|
383
|
+
return true;
|
|
384
|
+
}
|
|
385
|
+
if (decodedToken.type === "onboarding") {
|
|
386
|
+
this.logger.warn("Regular endpoint accessed with onboarding token");
|
|
387
|
+
throw new import_common3.UnauthorizedException("Onboarding tokens cannot access this endpoint");
|
|
388
|
+
}
|
|
389
|
+
const validatedToken = this.validateAccessToken(accessToken);
|
|
390
|
+
this.logger.debug("Access token validated successfully");
|
|
391
|
+
this.validateRefreshTokenBinding(context, validatedToken);
|
|
392
|
+
const userId = validatedToken.userId;
|
|
393
|
+
request.user = {
|
|
394
|
+
id: userId
|
|
395
|
+
};
|
|
396
|
+
const tenantIdentifier = this.requestService.getTenantIdentifier();
|
|
397
|
+
if (!tenantIdentifier) {
|
|
398
|
+
this.logger.warn("Tenant identifier not found in request");
|
|
399
|
+
throw new import_common3.UnauthorizedException("Tenant identifier not found");
|
|
400
|
+
}
|
|
401
|
+
this.logger.debug(`Tenant identifier extracted: ${tenantIdentifier}`);
|
|
402
|
+
if (tenantIdentifier === "cloud") {
|
|
403
|
+
this.logger.debug("Platform admin access detected, skipping tenant database validation");
|
|
404
|
+
return true;
|
|
405
|
+
}
|
|
406
|
+
const tenantInfo = await this.primaryDatabase.getTenantInfo(tenantIdentifier);
|
|
407
|
+
if (!tenantInfo) {
|
|
408
|
+
this.logger.warn(`Invalid tenant: ${tenantIdentifier}`);
|
|
409
|
+
throw new import_common3.UnauthorizedException("Invalid tenant");
|
|
410
|
+
}
|
|
411
|
+
if (tenantInfo.status !== "ACTIVE") {
|
|
412
|
+
this.logger.warn(`Tenant ${tenantIdentifier} has status: ${tenantInfo.status}`);
|
|
413
|
+
throw new import_common3.UnauthorizedException(`Tenant is ${tenantInfo.status}`);
|
|
414
|
+
}
|
|
415
|
+
this.logger.debug(`Tenant validated: ${tenantInfo.subdomain} (${tenantInfo.type})`);
|
|
416
|
+
return true;
|
|
417
|
+
} catch (error) {
|
|
418
|
+
if (error instanceof import_common3.UnauthorizedException) {
|
|
419
|
+
throw error;
|
|
420
|
+
}
|
|
421
|
+
this.logger.error("Unexpected error in auth guard", error);
|
|
422
|
+
throw new import_common3.UnauthorizedException("Authentication failed");
|
|
271
423
|
}
|
|
272
424
|
}
|
|
273
425
|
/**
|
|
274
|
-
*
|
|
426
|
+
* Validate access token with proper expiry checks
|
|
427
|
+
* Throws UnauthorizedException if token is invalid or expired
|
|
275
428
|
*/
|
|
276
|
-
|
|
429
|
+
validateAccessToken(token) {
|
|
277
430
|
try {
|
|
278
|
-
const
|
|
279
|
-
this.
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
schema: this.options.drizzleSchema,
|
|
288
|
-
relations: this.options.drizzleRelations
|
|
289
|
-
});
|
|
290
|
-
this.logger.debug(`Drizzle query keys after init: [${Object.keys(this.db.query || {}).join(", ")}]`);
|
|
291
|
-
await this.pool.query("SELECT 1");
|
|
292
|
-
this.logger.log("Connected to primary database (tenant registry)");
|
|
431
|
+
const decoded = this.jwtService.verify(token);
|
|
432
|
+
this.logger.debug(`Access token decoded for user: ${decoded.userId}`);
|
|
433
|
+
if (decoded.exp) {
|
|
434
|
+
const expiryTime = decoded.exp * 1e3;
|
|
435
|
+
const currentTime = Date.now();
|
|
436
|
+
const timeRemaining = expiryTime - currentTime;
|
|
437
|
+
this.logger.debug(`Access token valid for ${Math.floor(timeRemaining / 1e3)} more seconds`);
|
|
438
|
+
}
|
|
439
|
+
return decoded;
|
|
293
440
|
} catch (error) {
|
|
294
|
-
|
|
295
|
-
|
|
441
|
+
if (error instanceof import_common3.UnauthorizedException) {
|
|
442
|
+
throw error;
|
|
443
|
+
}
|
|
444
|
+
const jwtError = error;
|
|
445
|
+
if (jwtError?.name === "TokenExpiredError") {
|
|
446
|
+
this.logger.warn(`Access token expired at: ${jwtError?.expiredAt}`);
|
|
447
|
+
throw new import_common3.UnauthorizedException("Access token has expired");
|
|
448
|
+
}
|
|
449
|
+
if (jwtError?.name === "JsonWebTokenError") {
|
|
450
|
+
this.logger.warn(`Access token verification failed: ${jwtError?.message}`);
|
|
451
|
+
throw new import_common3.UnauthorizedException("Invalid access token");
|
|
452
|
+
}
|
|
453
|
+
if (jwtError?.name === "NotBeforeError") {
|
|
454
|
+
this.logger.warn("Access token used before valid (nbf claim)");
|
|
455
|
+
throw new import_common3.UnauthorizedException("Access token not yet valid");
|
|
456
|
+
}
|
|
457
|
+
this.logger.error("Unexpected error validating access token", error);
|
|
458
|
+
throw new import_common3.UnauthorizedException("Access token validation failed");
|
|
296
459
|
}
|
|
297
460
|
}
|
|
298
461
|
/**
|
|
299
|
-
*
|
|
462
|
+
* Validate that the access token is bound to the refresh token in the cookie.
|
|
463
|
+
* This prevents token theft - a stolen access token is useless without the
|
|
464
|
+
* corresponding refresh token cookie.
|
|
465
|
+
*
|
|
466
|
+
* @param context - The execution context containing the request
|
|
467
|
+
* @param validatedToken - The decoded and validated JWT token
|
|
468
|
+
* @throws UnauthorizedException if token binding validation fails
|
|
300
469
|
*/
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
470
|
+
validateRefreshTokenBinding(context, validatedToken) {
|
|
471
|
+
const config = getConfig();
|
|
472
|
+
if (!config.jwt.validateTokenBinding) {
|
|
473
|
+
this.logger.debug("Token binding validation is disabled");
|
|
474
|
+
return;
|
|
304
475
|
}
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
if (schema) {
|
|
309
|
-
params.set("schema", schema);
|
|
476
|
+
if (!validatedToken.refreshTokenHash) {
|
|
477
|
+
this.logger.debug("Token does not contain refreshTokenHash, skipping binding validation");
|
|
478
|
+
return;
|
|
310
479
|
}
|
|
311
|
-
|
|
312
|
-
const
|
|
313
|
-
|
|
314
|
-
|
|
480
|
+
const request = context.switchToHttp().getRequest();
|
|
481
|
+
const cookies = request.cookies || {};
|
|
482
|
+
const refreshToken = cookies[config.cookie.refreshCookieName];
|
|
483
|
+
if (!refreshToken) {
|
|
484
|
+
this.logger.warn("Session validation failed - refresh token cookie not found");
|
|
485
|
+
throw new import_common3.UnauthorizedException("Session validation failed");
|
|
315
486
|
}
|
|
316
|
-
|
|
317
|
-
|
|
487
|
+
if (!verifyTokenHash(refreshToken, validatedToken.refreshTokenHash)) {
|
|
488
|
+
this.logger.warn("Session validation failed - token binding mismatch");
|
|
489
|
+
throw new import_common3.UnauthorizedException("Session validation failed");
|
|
490
|
+
}
|
|
491
|
+
this.logger.debug("Token binding validated successfully");
|
|
318
492
|
}
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
493
|
+
};
|
|
494
|
+
VrittiAuthGuard = _ts_decorate3([
|
|
495
|
+
(0, import_common3.Injectable)({
|
|
496
|
+
scope: import_common3.Scope.REQUEST
|
|
497
|
+
}),
|
|
498
|
+
_ts_metadata2("design:type", Function),
|
|
499
|
+
_ts_metadata2("design:paramtypes", [
|
|
500
|
+
typeof Reflector === "undefined" ? Object : Reflector,
|
|
501
|
+
typeof ConfigService === "undefined" ? Object : ConfigService,
|
|
502
|
+
typeof JwtService === "undefined" ? Object : JwtService,
|
|
503
|
+
typeof PrimaryDatabaseService === "undefined" ? Object : PrimaryDatabaseService,
|
|
504
|
+
typeof RequestService === "undefined" ? Object : RequestService
|
|
505
|
+
])
|
|
506
|
+
], VrittiAuthGuard);
|
|
507
|
+
|
|
508
|
+
// src/auth/auth-config.module.ts
|
|
509
|
+
function _ts_decorate4(decorators, target, key, desc) {
|
|
510
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
511
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
512
|
+
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;
|
|
513
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
514
|
+
}
|
|
515
|
+
__name(_ts_decorate4, "_ts_decorate");
|
|
516
|
+
var AuthConfigModule = class _AuthConfigModule {
|
|
517
|
+
static {
|
|
518
|
+
__name(this, "AuthConfigModule");
|
|
324
519
|
}
|
|
325
520
|
/**
|
|
326
|
-
*
|
|
521
|
+
* Register the auth module with async configuration
|
|
327
522
|
*
|
|
328
|
-
*
|
|
329
|
-
*
|
|
523
|
+
* This method:
|
|
524
|
+
* 1. Configures JwtModule with JWT_SECRET from ConfigService
|
|
525
|
+
* 2. Provides VrittiAuthGuard globally (applies to all routes)
|
|
526
|
+
* 3. Exports JwtModule for use in other modules (e.g., for signing tokens)
|
|
527
|
+
*
|
|
528
|
+
* @returns Dynamic module configuration
|
|
330
529
|
*/
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
schemaName: config?.dbSchema || void 0,
|
|
363
|
-
// For DEDICATED tenants: database configuration from TenantDatabaseConfig table
|
|
364
|
-
databaseName: config?.dbName || void 0,
|
|
365
|
-
databaseHost: config?.dbHost || void 0,
|
|
366
|
-
databasePort: config?.dbPort || void 0,
|
|
367
|
-
databaseUsername: config?.dbUsername ? this.decrypt(config.dbUsername) : void 0,
|
|
368
|
-
databasePassword: config?.dbPassword ? this.decrypt(config.dbPassword) : void 0,
|
|
369
|
-
databaseSslMode: config?.dbSslMode || void 0,
|
|
370
|
-
connectionPoolSize: config?.connectionPoolSize || void 0
|
|
371
|
-
};
|
|
372
|
-
this.cacheInfo(info);
|
|
373
|
-
return info;
|
|
374
|
-
} catch (error) {
|
|
375
|
-
this.logger.error(`Failed to fetch tenant info: ${tenantIdentifier}`, error);
|
|
376
|
-
throw new import_common3.InternalServerErrorException("Failed to resolve tenant");
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
/**
|
|
380
|
-
* Cache tenant information with TTL
|
|
381
|
-
*/
|
|
382
|
-
cacheInfo(info) {
|
|
383
|
-
this.tenantConfigCache.set(info.id, info);
|
|
384
|
-
this.tenantConfigCache.set(info.subdomain, info);
|
|
385
|
-
setTimeout(() => {
|
|
386
|
-
this.tenantConfigCache.delete(info.id);
|
|
387
|
-
this.tenantConfigCache.delete(info.subdomain);
|
|
388
|
-
this.logger.debug(`Cache expired for tenant: ${info.subdomain}`);
|
|
389
|
-
}, this.cacheTTL);
|
|
530
|
+
static forRootAsync() {
|
|
531
|
+
return {
|
|
532
|
+
module: _AuthConfigModule,
|
|
533
|
+
imports: [
|
|
534
|
+
import_config3.ConfigModule,
|
|
535
|
+
RequestModule,
|
|
536
|
+
import_jwt.JwtModule.registerAsync({
|
|
537
|
+
imports: [
|
|
538
|
+
import_config3.ConfigModule
|
|
539
|
+
],
|
|
540
|
+
inject: [
|
|
541
|
+
import_config3.ConfigService
|
|
542
|
+
],
|
|
543
|
+
useFactory: /* @__PURE__ */ __name((config) => ({
|
|
544
|
+
secret: config.get("JWT_SECRET"),
|
|
545
|
+
signOptions: {
|
|
546
|
+
algorithm: "HS256"
|
|
547
|
+
}
|
|
548
|
+
}), "useFactory")
|
|
549
|
+
})
|
|
550
|
+
],
|
|
551
|
+
providers: [
|
|
552
|
+
{
|
|
553
|
+
provide: import_core2.APP_GUARD,
|
|
554
|
+
useClass: VrittiAuthGuard
|
|
555
|
+
}
|
|
556
|
+
],
|
|
557
|
+
exports: [
|
|
558
|
+
import_jwt.JwtModule
|
|
559
|
+
]
|
|
560
|
+
};
|
|
390
561
|
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
562
|
+
};
|
|
563
|
+
AuthConfigModule = _ts_decorate4([
|
|
564
|
+
(0, import_common4.Global)(),
|
|
565
|
+
(0, import_common4.Module)({})
|
|
566
|
+
], AuthConfigModule);
|
|
567
|
+
|
|
568
|
+
// src/auth/decorators/onboarding.decorator.ts
|
|
569
|
+
var import_common5 = require("@nestjs/common");
|
|
570
|
+
var Onboarding = /* @__PURE__ */ __name(() => (0, import_common5.SetMetadata)("isOnboarding", true), "Onboarding");
|
|
571
|
+
|
|
572
|
+
// src/auth/decorators/public.decorator.ts
|
|
573
|
+
var import_common6 = require("@nestjs/common");
|
|
574
|
+
var Public = /* @__PURE__ */ __name(() => (0, import_common6.SetMetadata)("isPublic", true), "Public");
|
|
575
|
+
|
|
576
|
+
// src/database/database.module.ts
|
|
577
|
+
var import_common12 = require("@nestjs/common");
|
|
578
|
+
var import_core3 = require("@nestjs/core");
|
|
579
|
+
|
|
580
|
+
// src/database/constants.ts
|
|
581
|
+
var DATABASE_MODULE_OPTIONS = Symbol("DATABASE_MODULE_OPTIONS");
|
|
582
|
+
|
|
583
|
+
// src/database/interceptors/message-tenant-context.interceptor.ts
|
|
584
|
+
var import_common7 = require("@nestjs/common");
|
|
585
|
+
var import_operators = require("rxjs/operators");
|
|
586
|
+
function _ts_decorate5(decorators, target, key, desc) {
|
|
587
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
588
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
589
|
+
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;
|
|
590
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
591
|
+
}
|
|
592
|
+
__name(_ts_decorate5, "_ts_decorate");
|
|
593
|
+
function _ts_metadata3(k, v) {
|
|
594
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
595
|
+
}
|
|
596
|
+
__name(_ts_metadata3, "_ts_metadata");
|
|
597
|
+
var MessageTenantContextInterceptor = class _MessageTenantContextInterceptor {
|
|
598
|
+
static {
|
|
599
|
+
__name(this, "MessageTenantContextInterceptor");
|
|
405
600
|
}
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
const size = this.tenantConfigCache.size;
|
|
411
|
-
this.tenantConfigCache.clear();
|
|
412
|
-
this.logger.log(`Cleared ${size} cached tenant configs`);
|
|
601
|
+
tenantContext;
|
|
602
|
+
logger = new import_common7.Logger(_MessageTenantContextInterceptor.name);
|
|
603
|
+
constructor(tenantContext) {
|
|
604
|
+
this.tenantContext = tenantContext;
|
|
413
605
|
}
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
606
|
+
intercept(context, next) {
|
|
607
|
+
const contextType = context.getType();
|
|
608
|
+
if (contextType === "rpc") {
|
|
609
|
+
const rpcContext = context.switchToRpc();
|
|
610
|
+
const payload = rpcContext.getData();
|
|
611
|
+
if (payload?.tenant) {
|
|
612
|
+
const tenant = payload.tenant;
|
|
613
|
+
this.logger.debug(`Setting tenant context from message: ${tenant.subdomain}`);
|
|
614
|
+
try {
|
|
615
|
+
this.tenantContext.setTenant(tenant);
|
|
616
|
+
this.logger.log(`Tenant context set: ${tenant.subdomain} (${tenant.type})`);
|
|
617
|
+
} catch (error) {
|
|
618
|
+
this.logger.error("Failed to set tenant context from message", error);
|
|
619
|
+
}
|
|
620
|
+
} else {
|
|
621
|
+
this.logger.warn("Message payload missing tenant information");
|
|
622
|
+
}
|
|
424
623
|
}
|
|
425
|
-
return
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
624
|
+
return next.handle().pipe((0, import_operators.tap)({
|
|
625
|
+
next: /* @__PURE__ */ __name(() => {
|
|
626
|
+
this.cleanupContext();
|
|
627
|
+
}, "next"),
|
|
628
|
+
error: /* @__PURE__ */ __name(() => {
|
|
629
|
+
this.cleanupContext();
|
|
630
|
+
}, "error"),
|
|
631
|
+
complete: /* @__PURE__ */ __name(() => {
|
|
632
|
+
this.cleanupContext();
|
|
633
|
+
}, "complete")
|
|
634
|
+
}));
|
|
432
635
|
}
|
|
433
636
|
/**
|
|
434
|
-
*
|
|
435
|
-
*
|
|
436
|
-
* Override this method to implement your encryption strategy
|
|
437
|
-
*
|
|
438
|
-
* @param encrypted Encrypted value
|
|
439
|
-
* @returns Decrypted value
|
|
637
|
+
* Clean up tenant context after message is processed
|
|
440
638
|
*/
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
await this.pool.end();
|
|
447
|
-
this.logger.log("Disconnected from primary database");
|
|
639
|
+
cleanupContext() {
|
|
640
|
+
if (this.tenantContext.hasTenant()) {
|
|
641
|
+
const tenant = this.tenantContext.getTenantIdSafe();
|
|
642
|
+
this.tenantContext.clearTenant();
|
|
643
|
+
this.logger.debug(`Cleaned up tenant context: ${tenant}`);
|
|
448
644
|
}
|
|
449
645
|
}
|
|
450
646
|
};
|
|
451
|
-
|
|
452
|
-
(0,
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
647
|
+
MessageTenantContextInterceptor = _ts_decorate5([
|
|
648
|
+
(0, import_common7.Injectable)({
|
|
649
|
+
scope: import_common7.Scope.REQUEST
|
|
650
|
+
}),
|
|
651
|
+
_ts_metadata3("design:type", Function),
|
|
652
|
+
_ts_metadata3("design:paramtypes", [
|
|
653
|
+
typeof TenantContextService === "undefined" ? Object : TenantContextService
|
|
457
654
|
])
|
|
458
|
-
],
|
|
655
|
+
], MessageTenantContextInterceptor);
|
|
459
656
|
|
|
460
|
-
// src/
|
|
461
|
-
|
|
657
|
+
// src/database/interceptors/tenant-context.interceptor.ts
|
|
658
|
+
var import_common8 = require("@nestjs/common");
|
|
659
|
+
function _ts_decorate6(decorators, target, key, desc) {
|
|
462
660
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
463
661
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
464
662
|
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;
|
|
465
663
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
466
664
|
}
|
|
467
|
-
__name(
|
|
468
|
-
function
|
|
665
|
+
__name(_ts_decorate6, "_ts_decorate");
|
|
666
|
+
function _ts_metadata4(k, v) {
|
|
469
667
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
470
668
|
}
|
|
471
|
-
__name(
|
|
472
|
-
var
|
|
669
|
+
__name(_ts_metadata4, "_ts_metadata");
|
|
670
|
+
var TenantContextInterceptor = class _TenantContextInterceptor {
|
|
473
671
|
static {
|
|
474
|
-
__name(this, "
|
|
672
|
+
__name(this, "TenantContextInterceptor");
|
|
475
673
|
}
|
|
476
674
|
reflector;
|
|
477
|
-
|
|
478
|
-
jwtService;
|
|
675
|
+
tenantContext;
|
|
479
676
|
primaryDatabase;
|
|
480
677
|
requestService;
|
|
481
|
-
logger = new
|
|
482
|
-
constructor(reflector,
|
|
678
|
+
logger = new import_common8.Logger(_TenantContextInterceptor.name);
|
|
679
|
+
constructor(reflector, tenantContext, primaryDatabase, requestService) {
|
|
483
680
|
this.reflector = reflector;
|
|
484
|
-
this.
|
|
485
|
-
this.jwtService = jwtService;
|
|
681
|
+
this.tenantContext = tenantContext;
|
|
486
682
|
this.primaryDatabase = primaryDatabase;
|
|
487
683
|
this.requestService = requestService;
|
|
488
684
|
}
|
|
489
|
-
async
|
|
685
|
+
async intercept(context, next) {
|
|
490
686
|
const request = context.switchToHttp().getRequest();
|
|
687
|
+
this.logger.debug(`Processing request: ${request.method} ${request.url}`);
|
|
491
688
|
const isPublic = this.reflector.getAllAndOverride("isPublic", [
|
|
492
689
|
context.getHandler(),
|
|
493
690
|
context.getClass()
|
|
494
691
|
]);
|
|
495
|
-
if (isPublic) {
|
|
496
|
-
this.logger.debug("Public endpoint detected, skipping authentication");
|
|
497
|
-
return true;
|
|
498
|
-
}
|
|
499
|
-
const isOnboarding = this.reflector.getAllAndOverride("isOnboarding", [
|
|
500
|
-
context.getHandler(),
|
|
501
|
-
context.getClass()
|
|
502
|
-
]);
|
|
503
692
|
try {
|
|
504
|
-
const
|
|
505
|
-
if (!
|
|
506
|
-
this.logger.
|
|
507
|
-
|
|
508
|
-
}
|
|
509
|
-
const decodedToken = this.jwtService.decode(accessToken);
|
|
510
|
-
if (!decodedToken) {
|
|
511
|
-
this.logger.warn("Failed to decode access token");
|
|
512
|
-
throw new import_common4.UnauthorizedException("Invalid token format");
|
|
693
|
+
const tenantIdentifier = this.requestService.getTenantIdentifier();
|
|
694
|
+
if (isPublic && !tenantIdentifier) {
|
|
695
|
+
this.logger.debug("Public endpoint without tenant identifier, skipping tenant context setup");
|
|
696
|
+
return next.handle();
|
|
513
697
|
}
|
|
514
|
-
if (
|
|
515
|
-
|
|
516
|
-
this.logger.warn("Onboarding endpoint requires onboarding token");
|
|
517
|
-
throw new import_common4.UnauthorizedException("This endpoint requires an onboarding token");
|
|
518
|
-
}
|
|
519
|
-
const validatedToken2 = this.validateAccessToken(accessToken);
|
|
520
|
-
this.logger.debug("Onboarding token validated successfully");
|
|
521
|
-
const userId2 = validatedToken2.userId;
|
|
522
|
-
request.user = {
|
|
523
|
-
id: userId2
|
|
524
|
-
};
|
|
525
|
-
return true;
|
|
698
|
+
if (!tenantIdentifier) {
|
|
699
|
+
throw new import_common8.UnauthorizedException("Tenant identifier not found in request");
|
|
526
700
|
}
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
const validatedToken = this.validateAccessToken(accessToken);
|
|
532
|
-
this.logger.debug("Access token validated successfully");
|
|
533
|
-
const refreshToken = this.requestService.getRefreshToken();
|
|
534
|
-
if (!refreshToken) {
|
|
535
|
-
this.logger.warn("Refresh token (session-id) not found in cookies");
|
|
536
|
-
throw new import_common4.UnauthorizedException("Refresh token not found");
|
|
537
|
-
}
|
|
538
|
-
this.validateRefreshToken(refreshToken);
|
|
539
|
-
this.logger.debug("Refresh token validated successfully");
|
|
540
|
-
const userId = validatedToken.userId;
|
|
541
|
-
request.user = {
|
|
542
|
-
id: userId
|
|
543
|
-
};
|
|
544
|
-
const tenantIdentifier = this.requestService.getTenantIdentifier();
|
|
545
|
-
if (!tenantIdentifier) {
|
|
546
|
-
this.logger.warn("Tenant identifier not found in request");
|
|
547
|
-
throw new import_common4.UnauthorizedException("Tenant identifier not found");
|
|
548
|
-
}
|
|
549
|
-
this.logger.debug(`Tenant identifier extracted: ${tenantIdentifier}`);
|
|
550
|
-
if (tenantIdentifier === "cloud") {
|
|
551
|
-
this.logger.debug("Platform admin access detected, skipping tenant database validation");
|
|
552
|
-
return true;
|
|
701
|
+
this.logger.debug(`Tenant identifier extracted: ${tenantIdentifier}`);
|
|
702
|
+
if (tenantIdentifier === "cloud") {
|
|
703
|
+
this.logger.log("Cloud platform access detected, skipping tenant context setup");
|
|
704
|
+
return next.handle();
|
|
553
705
|
}
|
|
554
706
|
const tenantInfo = await this.primaryDatabase.getTenantInfo(tenantIdentifier);
|
|
555
707
|
if (!tenantInfo) {
|
|
556
708
|
this.logger.warn(`Invalid tenant: ${tenantIdentifier}`);
|
|
557
|
-
throw new
|
|
709
|
+
throw new import_common8.UnauthorizedException("Invalid tenant");
|
|
558
710
|
}
|
|
559
711
|
if (tenantInfo.status !== "ACTIVE") {
|
|
560
712
|
this.logger.warn(`Tenant ${tenantIdentifier} has status: ${tenantInfo.status}`);
|
|
561
|
-
throw new
|
|
562
|
-
}
|
|
563
|
-
this.logger.debug(`Tenant validated: ${tenantInfo.subdomain} (${tenantInfo.type})`);
|
|
564
|
-
return true;
|
|
565
|
-
} catch (error) {
|
|
566
|
-
if (error instanceof import_common4.UnauthorizedException) {
|
|
567
|
-
throw error;
|
|
568
|
-
}
|
|
569
|
-
this.logger.error("Unexpected error in auth guard", error);
|
|
570
|
-
throw new import_common4.UnauthorizedException("Authentication failed");
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
/**
|
|
574
|
-
* Validate access token with proper expiry checks
|
|
575
|
-
* Throws UnauthorizedException if token is invalid or expired
|
|
576
|
-
*/
|
|
577
|
-
validateAccessToken(token) {
|
|
578
|
-
try {
|
|
579
|
-
const decoded = this.jwtService.verify(token);
|
|
580
|
-
this.logger.debug(`Access token decoded for user: ${decoded.userId}`);
|
|
581
|
-
if (decoded.exp) {
|
|
582
|
-
const expiryTime = decoded.exp * 1e3;
|
|
583
|
-
const currentTime = Date.now();
|
|
584
|
-
const timeRemaining = expiryTime - currentTime;
|
|
585
|
-
this.logger.debug(`Access token valid for ${Math.floor(timeRemaining / 1e3)} more seconds`);
|
|
586
|
-
}
|
|
587
|
-
return decoded;
|
|
588
|
-
} catch (error) {
|
|
589
|
-
if (error instanceof import_common4.UnauthorizedException) {
|
|
590
|
-
throw error;
|
|
591
|
-
}
|
|
592
|
-
const jwtError = error;
|
|
593
|
-
if (jwtError?.name === "TokenExpiredError") {
|
|
594
|
-
this.logger.warn(`Access token expired at: ${jwtError?.expiredAt}`);
|
|
595
|
-
throw new import_common4.UnauthorizedException("Access token has expired");
|
|
596
|
-
}
|
|
597
|
-
if (jwtError?.name === "JsonWebTokenError") {
|
|
598
|
-
this.logger.warn(`Access token verification failed: ${jwtError?.message}`);
|
|
599
|
-
throw new import_common4.UnauthorizedException("Invalid access token");
|
|
600
|
-
}
|
|
601
|
-
if (jwtError?.name === "NotBeforeError") {
|
|
602
|
-
this.logger.warn("Access token used before valid (nbf claim)");
|
|
603
|
-
throw new import_common4.UnauthorizedException("Access token not yet valid");
|
|
604
|
-
}
|
|
605
|
-
this.logger.error("Unexpected error validating access token", error);
|
|
606
|
-
throw new import_common4.UnauthorizedException("Access token validation failed");
|
|
607
|
-
}
|
|
608
|
-
}
|
|
609
|
-
/**
|
|
610
|
-
* Validate refresh token with proper expiry checks
|
|
611
|
-
* Throws UnauthorizedException if token is invalid or expired
|
|
612
|
-
*/
|
|
613
|
-
validateRefreshToken(token) {
|
|
614
|
-
const jwtSecret = this.configService.get("JWT_REFRESH_SECRET") || this.configService.get("JWT_SECRET");
|
|
615
|
-
this.validateRefreshTokenWithSecret(token, jwtSecret);
|
|
616
|
-
}
|
|
617
|
-
/**
|
|
618
|
-
* Helper to validate refresh token with specific secret
|
|
619
|
-
*/
|
|
620
|
-
validateRefreshTokenWithSecret(token, secret) {
|
|
621
|
-
if (!secret) {
|
|
622
|
-
this.logger.error("JWT secret not configured for refresh token validation");
|
|
623
|
-
throw new import_common4.UnauthorizedException("Server configuration error");
|
|
624
|
-
}
|
|
625
|
-
try {
|
|
626
|
-
const decoded = jwt.verify(token, secret, {
|
|
627
|
-
algorithms: [
|
|
628
|
-
"HS256",
|
|
629
|
-
"HS512",
|
|
630
|
-
"RS256"
|
|
631
|
-
]
|
|
632
|
-
});
|
|
633
|
-
this.logger.debug(`Refresh token decoded for user: ${decoded.userId}`);
|
|
634
|
-
if (decoded.exp) {
|
|
635
|
-
const expiryTime = decoded.exp * 1e3;
|
|
636
|
-
const currentTime = Date.now();
|
|
637
|
-
if (currentTime > expiryTime) {
|
|
638
|
-
this.logger.warn("Refresh token has expired");
|
|
639
|
-
throw new import_common4.UnauthorizedException("Refresh token has expired. Please login again");
|
|
640
|
-
}
|
|
641
|
-
const timeRemaining = expiryTime - currentTime;
|
|
642
|
-
this.logger.debug(`Refresh token valid for ${Math.floor(timeRemaining / 1e3)} more seconds`);
|
|
713
|
+
throw new import_common8.UnauthorizedException(`Tenant is ${tenantInfo.status}`);
|
|
643
714
|
}
|
|
715
|
+
this.logger.debug(`Tenant config loaded: ${tenantInfo.subdomain} (${tenantInfo.type})`);
|
|
716
|
+
this.tenantContext.setTenant(tenantInfo);
|
|
717
|
+
request.tenant = tenantInfo;
|
|
718
|
+
this.logger.log(`Tenant context set: ${tenantInfo.subdomain}`);
|
|
644
719
|
} catch (error) {
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
}
|
|
648
|
-
const jwtError = error;
|
|
649
|
-
if (jwtError?.name === "TokenExpiredError") {
|
|
650
|
-
this.logger.warn(`Refresh token expired at: ${jwtError?.expiredAt}`);
|
|
651
|
-
throw new import_common4.UnauthorizedException("Refresh token has expired. Please login again");
|
|
652
|
-
}
|
|
653
|
-
if (jwtError?.name === "JsonWebTokenError") {
|
|
654
|
-
this.logger.warn(`Refresh token verification failed: ${jwtError?.message}`);
|
|
655
|
-
throw new import_common4.UnauthorizedException("Invalid refresh token");
|
|
656
|
-
}
|
|
657
|
-
if (jwtError?.name === "NotBeforeError") {
|
|
658
|
-
this.logger.warn("Refresh token used before valid (nbf claim)");
|
|
659
|
-
throw new import_common4.UnauthorizedException("Refresh token not yet valid");
|
|
660
|
-
}
|
|
661
|
-
this.logger.error("Unexpected error validating refresh token", error);
|
|
662
|
-
throw new import_common4.UnauthorizedException("Refresh token validation failed");
|
|
720
|
+
this.logger.error("Failed to set tenant context", error);
|
|
721
|
+
throw error;
|
|
663
722
|
}
|
|
723
|
+
return next.handle();
|
|
664
724
|
}
|
|
665
725
|
};
|
|
666
|
-
|
|
667
|
-
(0,
|
|
668
|
-
scope:
|
|
726
|
+
TenantContextInterceptor = _ts_decorate6([
|
|
727
|
+
(0, import_common8.Injectable)({
|
|
728
|
+
scope: import_common8.Scope.REQUEST
|
|
669
729
|
}),
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
typeof
|
|
673
|
-
typeof
|
|
674
|
-
typeof import_jwt.JwtService === "undefined" ? Object : import_jwt.JwtService,
|
|
730
|
+
_ts_metadata4("design:type", Function),
|
|
731
|
+
_ts_metadata4("design:paramtypes", [
|
|
732
|
+
typeof Reflector === "undefined" ? Object : Reflector,
|
|
733
|
+
typeof TenantContextService === "undefined" ? Object : TenantContextService,
|
|
675
734
|
typeof PrimaryDatabaseService === "undefined" ? Object : PrimaryDatabaseService,
|
|
676
735
|
typeof RequestService === "undefined" ? Object : RequestService
|
|
677
736
|
])
|
|
678
|
-
],
|
|
737
|
+
], TenantContextInterceptor);
|
|
679
738
|
|
|
680
|
-
// src/
|
|
681
|
-
|
|
739
|
+
// src/database/services/primary-database.service.ts
|
|
740
|
+
var import_common9 = require("@nestjs/common");
|
|
741
|
+
var import_drizzle_orm = require("drizzle-orm");
|
|
742
|
+
var import_node_postgres = require("drizzle-orm/node-postgres");
|
|
743
|
+
var import_pg = require("pg");
|
|
744
|
+
function _ts_decorate7(decorators, target, key, desc) {
|
|
682
745
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
683
746
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
684
747
|
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;
|
|
685
748
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
686
749
|
}
|
|
687
|
-
__name(
|
|
688
|
-
|
|
750
|
+
__name(_ts_decorate7, "_ts_decorate");
|
|
751
|
+
function _ts_metadata5(k, v) {
|
|
752
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
753
|
+
}
|
|
754
|
+
__name(_ts_metadata5, "_ts_metadata");
|
|
755
|
+
function _ts_param2(paramIndex, decorator) {
|
|
756
|
+
return function(target, key) {
|
|
757
|
+
decorator(target, key, paramIndex);
|
|
758
|
+
};
|
|
759
|
+
}
|
|
760
|
+
__name(_ts_param2, "_ts_param");
|
|
761
|
+
var PrimaryDatabaseService2 = class _PrimaryDatabaseService {
|
|
689
762
|
static {
|
|
690
|
-
__name(this, "
|
|
763
|
+
__name(this, "PrimaryDatabaseService");
|
|
691
764
|
}
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
imports: [
|
|
706
|
-
import_config2.ConfigModule,
|
|
707
|
-
RequestModule,
|
|
708
|
-
import_jwt2.JwtModule.registerAsync({
|
|
709
|
-
imports: [
|
|
710
|
-
import_config2.ConfigModule
|
|
711
|
-
],
|
|
712
|
-
inject: [
|
|
713
|
-
import_config2.ConfigService
|
|
714
|
-
],
|
|
715
|
-
useFactory: /* @__PURE__ */ __name((config) => ({
|
|
716
|
-
secret: config.get("JWT_SECRET"),
|
|
717
|
-
signOptions: {
|
|
718
|
-
algorithm: "HS256"
|
|
719
|
-
}
|
|
720
|
-
}), "useFactory")
|
|
721
|
-
})
|
|
722
|
-
],
|
|
723
|
-
providers: [
|
|
724
|
-
{
|
|
725
|
-
provide: import_core3.APP_GUARD,
|
|
726
|
-
useClass: VrittiAuthGuard
|
|
727
|
-
}
|
|
728
|
-
],
|
|
729
|
-
exports: [
|
|
730
|
-
import_jwt2.JwtModule
|
|
731
|
-
]
|
|
732
|
-
};
|
|
765
|
+
options;
|
|
766
|
+
logger = new import_common9.Logger(_PrimaryDatabaseService.name);
|
|
767
|
+
/** PostgreSQL connection pool */
|
|
768
|
+
pool = null;
|
|
769
|
+
/** Drizzle database instance */
|
|
770
|
+
db = null;
|
|
771
|
+
/** In-memory cache: Map<tenantIdentifier, TenantConfig> */
|
|
772
|
+
tenantConfigCache = /* @__PURE__ */ new Map();
|
|
773
|
+
/** Cache TTL in milliseconds */
|
|
774
|
+
cacheTTL;
|
|
775
|
+
constructor(options) {
|
|
776
|
+
this.options = options;
|
|
777
|
+
this.cacheTTL = options.connectionCacheTTL || 3e5;
|
|
733
778
|
}
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
], AuthConfigModule);
|
|
739
|
-
|
|
740
|
-
// src/database/database.module.ts
|
|
741
|
-
var import_common10 = require("@nestjs/common");
|
|
742
|
-
var import_core5 = require("@nestjs/core");
|
|
743
|
-
|
|
744
|
-
// src/database/interceptors/message-tenant-context.interceptor.ts
|
|
745
|
-
var import_common7 = require("@nestjs/common");
|
|
746
|
-
var import_operators = require("rxjs/operators");
|
|
747
|
-
|
|
748
|
-
// src/database/services/tenant-context.service.ts
|
|
749
|
-
var import_common6 = require("@nestjs/common");
|
|
750
|
-
function _ts_decorate6(decorators, target, key, desc) {
|
|
751
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
752
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
753
|
-
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;
|
|
754
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
755
|
-
}
|
|
756
|
-
__name(_ts_decorate6, "_ts_decorate");
|
|
757
|
-
var TenantContextService = class {
|
|
758
|
-
static {
|
|
759
|
-
__name(this, "TenantContextService");
|
|
779
|
+
async onModuleInit() {
|
|
780
|
+
if (this.options.primaryDb) {
|
|
781
|
+
await this.initializeDrizzleClient();
|
|
782
|
+
}
|
|
760
783
|
}
|
|
761
|
-
tenantInfo = null;
|
|
762
784
|
/**
|
|
763
|
-
*
|
|
764
|
-
*
|
|
765
|
-
* This is typically called by:
|
|
766
|
-
* - TenantContextInterceptor (for HTTP requests in gateway)
|
|
767
|
-
* - MessageTenantContextInterceptor (for RabbitMQ messages in microservices)
|
|
768
|
-
* - Manual context setup in message handlers
|
|
769
|
-
*
|
|
770
|
-
* @param tenantInfo Complete tenant information
|
|
771
|
-
* @throws Error if tenant context is already set (prevents accidental overwrites)
|
|
785
|
+
* Initialize connection to primary database using Drizzle
|
|
772
786
|
*/
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
787
|
+
async initializeDrizzleClient() {
|
|
788
|
+
try {
|
|
789
|
+
const databaseUrl = this.buildPrimaryDbUrl();
|
|
790
|
+
this.pool = new import_pg.Pool({
|
|
791
|
+
connectionString: databaseUrl,
|
|
792
|
+
max: this.options.maxConnections || 10
|
|
793
|
+
});
|
|
794
|
+
this.logger.debug(`Schema keys passed to drizzle: [${Object.keys(this.options.drizzleSchema || {}).join(", ")}]`);
|
|
795
|
+
this.logger.debug(`Relations keys passed to drizzle: [${Object.keys(this.options.drizzleRelations || {}).join(", ")}]`);
|
|
796
|
+
this.db = (0, import_node_postgres.drizzle)({
|
|
797
|
+
client: this.pool,
|
|
798
|
+
schema: this.options.drizzleSchema,
|
|
799
|
+
relations: this.options.drizzleRelations
|
|
800
|
+
});
|
|
801
|
+
this.logger.debug(`Drizzle query keys after init: [${Object.keys(this.db.query || {}).join(", ")}]`);
|
|
802
|
+
await this.pool.query("SELECT 1");
|
|
803
|
+
this.logger.log("Connected to primary database (tenant registry)");
|
|
804
|
+
} catch (error) {
|
|
805
|
+
this.logger.error("Failed to connect to primary database", error);
|
|
806
|
+
throw new import_common9.InternalServerErrorException("Failed to initialize tenant registry");
|
|
776
807
|
}
|
|
777
|
-
this.tenantInfo = tenantInfo;
|
|
778
808
|
}
|
|
779
809
|
/**
|
|
780
|
-
*
|
|
781
|
-
*
|
|
782
|
-
* @returns Tenant information
|
|
783
|
-
* @throws UnauthorizedException if tenant context hasn't been set
|
|
810
|
+
* Build connection URL from primary database properties
|
|
784
811
|
*/
|
|
785
|
-
|
|
786
|
-
if (!this.
|
|
787
|
-
throw new
|
|
812
|
+
buildPrimaryDbUrl() {
|
|
813
|
+
if (!this.options.primaryDb) {
|
|
814
|
+
throw new Error("Primary database configuration not provided");
|
|
788
815
|
}
|
|
789
|
-
|
|
816
|
+
const { host, port = 5432, username, password, database, schema = "public", sslMode = "require" } = this.options.primaryDb;
|
|
817
|
+
let url = `postgresql://${username}:${encodeURIComponent(password)}@${host}:${port}/${database}`;
|
|
818
|
+
const params = new URLSearchParams();
|
|
819
|
+
if (schema) {
|
|
820
|
+
params.set("schema", schema);
|
|
821
|
+
}
|
|
822
|
+
params.set("sslmode", sslMode);
|
|
823
|
+
const queryString = params.toString();
|
|
824
|
+
if (queryString) {
|
|
825
|
+
url += `?${queryString}`;
|
|
826
|
+
}
|
|
827
|
+
this.logger.debug(`Primary DB connection URL: ${this.maskPassword(url)}`);
|
|
828
|
+
return url;
|
|
790
829
|
}
|
|
791
830
|
/**
|
|
792
|
-
*
|
|
793
|
-
*
|
|
794
|
-
* @returns true if tenant context is available
|
|
831
|
+
* Mask password in connection URL for logging
|
|
795
832
|
*/
|
|
796
|
-
|
|
797
|
-
return
|
|
833
|
+
maskPassword(url) {
|
|
834
|
+
return url.replace(/:([^@]+)@/, ":****@");
|
|
835
|
+
}
|
|
836
|
+
/**
|
|
837
|
+
* Get tenant configuration by identifier (ID or subdomain)
|
|
838
|
+
*
|
|
839
|
+
* @param tenantIdentifier Tenant ID or subdomain
|
|
840
|
+
* @returns Tenant configuration or null if not found
|
|
841
|
+
*/
|
|
842
|
+
async getTenantInfo(tenantIdentifier) {
|
|
843
|
+
const cached = this.tenantConfigCache.get(tenantIdentifier);
|
|
844
|
+
if (cached) {
|
|
845
|
+
this.logger.debug(`Cache hit for tenant: ${tenantIdentifier}`);
|
|
846
|
+
return cached;
|
|
847
|
+
}
|
|
848
|
+
try {
|
|
849
|
+
if (!this.db) {
|
|
850
|
+
throw new Error("Primary database client not initialized");
|
|
851
|
+
}
|
|
852
|
+
this.logger.debug(`Querying primary database for tenant: ${tenantIdentifier}`);
|
|
853
|
+
const schema = this.options.drizzleSchema;
|
|
854
|
+
const { tenants, tenantDatabaseConfigs } = schema;
|
|
855
|
+
const result = await this.db.select().from(tenants).leftJoin(tenantDatabaseConfigs, (0, import_drizzle_orm.eq)(tenants.id, tenantDatabaseConfigs.tenantId)).where((0, import_drizzle_orm.or)((0, import_drizzle_orm.eq)(tenants.id, tenantIdentifier), (0, import_drizzle_orm.eq)(tenants.subdomain, tenantIdentifier))).limit(1);
|
|
856
|
+
if (!result.length) {
|
|
857
|
+
this.logger.warn(`Tenant not found: ${tenantIdentifier}`);
|
|
858
|
+
return null;
|
|
859
|
+
}
|
|
860
|
+
const row = result[0];
|
|
861
|
+
const tenant = row.tenants;
|
|
862
|
+
const config = row.tenant_database_configs;
|
|
863
|
+
if (tenant.status !== "ACTIVE") {
|
|
864
|
+
this.logger.warn(`Tenant not active: ${tenantIdentifier}`);
|
|
865
|
+
return null;
|
|
866
|
+
}
|
|
867
|
+
const info = {
|
|
868
|
+
id: tenant.id,
|
|
869
|
+
subdomain: tenant.subdomain,
|
|
870
|
+
type: tenant.dbType,
|
|
871
|
+
status: tenant.status,
|
|
872
|
+
// For SHARED tenants: schema name
|
|
873
|
+
schemaName: config?.dbSchema || void 0,
|
|
874
|
+
// For DEDICATED tenants: database configuration from TenantDatabaseConfig table
|
|
875
|
+
databaseName: config?.dbName || void 0,
|
|
876
|
+
databaseHost: config?.dbHost || void 0,
|
|
877
|
+
databasePort: config?.dbPort || void 0,
|
|
878
|
+
databaseUsername: config?.dbUsername ? this.decrypt(config.dbUsername) : void 0,
|
|
879
|
+
databasePassword: config?.dbPassword ? this.decrypt(config.dbPassword) : void 0,
|
|
880
|
+
databaseSslMode: config?.dbSslMode || void 0,
|
|
881
|
+
connectionPoolSize: config?.connectionPoolSize || void 0
|
|
882
|
+
};
|
|
883
|
+
this.cacheInfo(info);
|
|
884
|
+
return info;
|
|
885
|
+
} catch (error) {
|
|
886
|
+
this.logger.error(`Failed to fetch tenant info: ${tenantIdentifier}`, error);
|
|
887
|
+
throw new import_common9.InternalServerErrorException("Failed to resolve tenant");
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
/**
|
|
891
|
+
* Cache tenant information with TTL
|
|
892
|
+
*/
|
|
893
|
+
cacheInfo(info) {
|
|
894
|
+
this.tenantConfigCache.set(info.id, info);
|
|
895
|
+
this.tenantConfigCache.set(info.subdomain, info);
|
|
896
|
+
setTimeout(() => {
|
|
897
|
+
this.tenantConfigCache.delete(info.id);
|
|
898
|
+
this.tenantConfigCache.delete(info.subdomain);
|
|
899
|
+
this.logger.debug(`Cache expired for tenant: ${info.subdomain}`);
|
|
900
|
+
}, this.cacheTTL);
|
|
901
|
+
}
|
|
902
|
+
/**
|
|
903
|
+
* Clear cached tenant information
|
|
904
|
+
*
|
|
905
|
+
* Useful when tenant settings are updated and cache needs to be invalidated
|
|
906
|
+
*
|
|
907
|
+
* @param tenantIdentifier Tenant ID or subdomain
|
|
908
|
+
*/
|
|
909
|
+
clearTenantCache(tenantIdentifier) {
|
|
910
|
+
const config = this.tenantConfigCache.get(tenantIdentifier);
|
|
911
|
+
if (config) {
|
|
912
|
+
this.tenantConfigCache.delete(config.id);
|
|
913
|
+
this.tenantConfigCache.delete(config.subdomain);
|
|
914
|
+
this.logger.log(`Cleared cache for tenant: ${tenantIdentifier}`);
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
/**
|
|
918
|
+
* Clear all cached tenant configurations
|
|
919
|
+
*/
|
|
920
|
+
clearAllCaches() {
|
|
921
|
+
const size = this.tenantConfigCache.size;
|
|
922
|
+
this.tenantConfigCache.clear();
|
|
923
|
+
this.logger.log(`Cleared ${size} cached tenant configs`);
|
|
924
|
+
}
|
|
925
|
+
/**
|
|
926
|
+
* Get the Drizzle database instance for the primary database.
|
|
927
|
+
* This is a synchronous property that returns the initialized Drizzle client.
|
|
928
|
+
*
|
|
929
|
+
* @returns Primary database Drizzle instance
|
|
930
|
+
* @throws Error if primary database client is not initialized
|
|
931
|
+
*/
|
|
932
|
+
get drizzleClient() {
|
|
933
|
+
if (!this.db) {
|
|
934
|
+
throw new Error("Primary database client not initialized");
|
|
935
|
+
}
|
|
936
|
+
return this.db;
|
|
937
|
+
}
|
|
938
|
+
/**
|
|
939
|
+
* Get the Drizzle schema
|
|
940
|
+
*/
|
|
941
|
+
get schema() {
|
|
942
|
+
return this.options.drizzleSchema;
|
|
943
|
+
}
|
|
944
|
+
/**
|
|
945
|
+
* Decrypt database credentials
|
|
946
|
+
*
|
|
947
|
+
* Override this method to implement your encryption strategy
|
|
948
|
+
*
|
|
949
|
+
* @param encrypted Encrypted value
|
|
950
|
+
* @returns Decrypted value
|
|
951
|
+
*/
|
|
952
|
+
decrypt(encrypted) {
|
|
953
|
+
return encrypted;
|
|
954
|
+
}
|
|
955
|
+
async onModuleDestroy() {
|
|
956
|
+
if (this.pool) {
|
|
957
|
+
await this.pool.end();
|
|
958
|
+
this.logger.log("Disconnected from primary database");
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
};
|
|
962
|
+
PrimaryDatabaseService2 = _ts_decorate7([
|
|
963
|
+
(0, import_common9.Injectable)(),
|
|
964
|
+
_ts_param2(0, (0, import_common9.Inject)(DATABASE_MODULE_OPTIONS)),
|
|
965
|
+
_ts_metadata5("design:type", Function),
|
|
966
|
+
_ts_metadata5("design:paramtypes", [
|
|
967
|
+
typeof DatabaseModuleOptions === "undefined" ? Object : DatabaseModuleOptions
|
|
968
|
+
])
|
|
969
|
+
], PrimaryDatabaseService2);
|
|
970
|
+
|
|
971
|
+
// src/database/services/tenant-context.service.ts
|
|
972
|
+
var import_common10 = require("@nestjs/common");
|
|
973
|
+
function _ts_decorate8(decorators, target, key, desc) {
|
|
974
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
975
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
976
|
+
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;
|
|
977
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
978
|
+
}
|
|
979
|
+
__name(_ts_decorate8, "_ts_decorate");
|
|
980
|
+
var TenantContextService2 = class {
|
|
981
|
+
static {
|
|
982
|
+
__name(this, "TenantContextService");
|
|
983
|
+
}
|
|
984
|
+
tenantInfo = null;
|
|
985
|
+
/**
|
|
986
|
+
* Set tenant information for this request/message
|
|
987
|
+
*
|
|
988
|
+
* This is typically called by:
|
|
989
|
+
* - TenantContextInterceptor (for HTTP requests in gateway)
|
|
990
|
+
* - MessageTenantContextInterceptor (for RabbitMQ messages in microservices)
|
|
991
|
+
* - Manual context setup in message handlers
|
|
992
|
+
*
|
|
993
|
+
* @param tenantInfo Complete tenant information
|
|
994
|
+
* @throws Error if tenant context is already set (prevents accidental overwrites)
|
|
995
|
+
*/
|
|
996
|
+
setTenant(tenantInfo) {
|
|
997
|
+
if (this.tenantInfo) {
|
|
998
|
+
throw new Error("Tenant context already set for this request");
|
|
999
|
+
}
|
|
1000
|
+
this.tenantInfo = tenantInfo;
|
|
1001
|
+
}
|
|
1002
|
+
/**
|
|
1003
|
+
* Get tenant information for this request/message
|
|
1004
|
+
*
|
|
1005
|
+
* @returns Tenant information
|
|
1006
|
+
* @throws UnauthorizedException if tenant context hasn't been set
|
|
1007
|
+
*/
|
|
1008
|
+
getTenant() {
|
|
1009
|
+
if (!this.tenantInfo) {
|
|
1010
|
+
throw new import_common10.UnauthorizedException("Tenant context not set");
|
|
1011
|
+
}
|
|
1012
|
+
return this.tenantInfo;
|
|
1013
|
+
}
|
|
1014
|
+
/**
|
|
1015
|
+
* Check if tenant context has been set
|
|
1016
|
+
*
|
|
1017
|
+
* @returns true if tenant context is available
|
|
1018
|
+
*/
|
|
1019
|
+
hasTenant() {
|
|
1020
|
+
return this.tenantInfo !== null;
|
|
798
1021
|
}
|
|
799
1022
|
/**
|
|
800
1023
|
* Clear tenant context
|
|
@@ -825,213 +1048,58 @@ var TenantContextService = class {
|
|
|
825
1048
|
return this.tenantInfo?.subdomain ?? null;
|
|
826
1049
|
}
|
|
827
1050
|
};
|
|
828
|
-
|
|
829
|
-
(0,
|
|
830
|
-
scope:
|
|
1051
|
+
TenantContextService2 = _ts_decorate8([
|
|
1052
|
+
(0, import_common10.Injectable)({
|
|
1053
|
+
scope: import_common10.Scope.REQUEST
|
|
831
1054
|
})
|
|
832
|
-
],
|
|
1055
|
+
], TenantContextService2);
|
|
833
1056
|
|
|
834
|
-
// src/database/
|
|
835
|
-
|
|
1057
|
+
// src/database/services/tenant-database.service.ts
|
|
1058
|
+
var import_common11 = require("@nestjs/common");
|
|
1059
|
+
var import_node_postgres2 = require("drizzle-orm/node-postgres");
|
|
1060
|
+
var import_pg2 = require("pg");
|
|
1061
|
+
function _ts_decorate9(decorators, target, key, desc) {
|
|
836
1062
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
837
1063
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
838
1064
|
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;
|
|
839
1065
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
840
1066
|
}
|
|
841
|
-
__name(
|
|
842
|
-
function
|
|
1067
|
+
__name(_ts_decorate9, "_ts_decorate");
|
|
1068
|
+
function _ts_metadata6(k, v) {
|
|
843
1069
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
844
1070
|
}
|
|
845
|
-
__name(
|
|
846
|
-
|
|
1071
|
+
__name(_ts_metadata6, "_ts_metadata");
|
|
1072
|
+
function _ts_param3(paramIndex, decorator) {
|
|
1073
|
+
return function(target, key) {
|
|
1074
|
+
decorator(target, key, paramIndex);
|
|
1075
|
+
};
|
|
1076
|
+
}
|
|
1077
|
+
__name(_ts_param3, "_ts_param");
|
|
1078
|
+
var TenantDatabaseService = class _TenantDatabaseService {
|
|
847
1079
|
static {
|
|
848
|
-
__name(this, "
|
|
1080
|
+
__name(this, "TenantDatabaseService");
|
|
849
1081
|
}
|
|
1082
|
+
options;
|
|
850
1083
|
tenantContext;
|
|
851
|
-
logger = new
|
|
852
|
-
|
|
1084
|
+
logger = new import_common11.Logger(_TenantDatabaseService.name);
|
|
1085
|
+
/** Connection pool: Map<cacheKey, TenantConnection> */
|
|
1086
|
+
clients = /* @__PURE__ */ new Map();
|
|
1087
|
+
/** Track last usage time for idle connection cleanup */
|
|
1088
|
+
clientLastUsed = /* @__PURE__ */ new Map();
|
|
1089
|
+
/** Cleanup interval timer */
|
|
1090
|
+
cleanupInterval;
|
|
1091
|
+
constructor(options, tenantContext) {
|
|
1092
|
+
this.options = options;
|
|
853
1093
|
this.tenantContext = tenantContext;
|
|
854
|
-
|
|
855
|
-
intercept(context, next) {
|
|
856
|
-
const contextType = context.getType();
|
|
857
|
-
if (contextType === "rpc") {
|
|
858
|
-
const rpcContext = context.switchToRpc();
|
|
859
|
-
const payload = rpcContext.getData();
|
|
860
|
-
if (payload && payload.tenant) {
|
|
861
|
-
const tenant = payload.tenant;
|
|
862
|
-
this.logger.debug(`Setting tenant context from message: ${tenant.subdomain}`);
|
|
863
|
-
try {
|
|
864
|
-
this.tenantContext.setTenant(tenant);
|
|
865
|
-
this.logger.log(`Tenant context set: ${tenant.subdomain} (${tenant.type})`);
|
|
866
|
-
} catch (error) {
|
|
867
|
-
this.logger.error("Failed to set tenant context from message", error);
|
|
868
|
-
}
|
|
869
|
-
} else {
|
|
870
|
-
this.logger.warn("Message payload missing tenant information");
|
|
871
|
-
}
|
|
872
|
-
}
|
|
873
|
-
return next.handle().pipe((0, import_operators.tap)({
|
|
874
|
-
next: /* @__PURE__ */ __name(() => {
|
|
875
|
-
this.cleanupContext();
|
|
876
|
-
}, "next"),
|
|
877
|
-
error: /* @__PURE__ */ __name(() => {
|
|
878
|
-
this.cleanupContext();
|
|
879
|
-
}, "error"),
|
|
880
|
-
complete: /* @__PURE__ */ __name(() => {
|
|
881
|
-
this.cleanupContext();
|
|
882
|
-
}, "complete")
|
|
883
|
-
}));
|
|
1094
|
+
this.startConnectionCleaner();
|
|
884
1095
|
}
|
|
885
1096
|
/**
|
|
886
|
-
*
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
this.logger.debug(`Cleaned up tenant context: ${tenant}`);
|
|
893
|
-
}
|
|
894
|
-
}
|
|
895
|
-
};
|
|
896
|
-
MessageTenantContextInterceptor = _ts_decorate7([
|
|
897
|
-
(0, import_common7.Injectable)({
|
|
898
|
-
scope: import_common7.Scope.REQUEST
|
|
899
|
-
}),
|
|
900
|
-
_ts_metadata4("design:type", Function),
|
|
901
|
-
_ts_metadata4("design:paramtypes", [
|
|
902
|
-
typeof TenantContextService === "undefined" ? Object : TenantContextService
|
|
903
|
-
])
|
|
904
|
-
], MessageTenantContextInterceptor);
|
|
905
|
-
|
|
906
|
-
// src/database/interceptors/tenant-context.interceptor.ts
|
|
907
|
-
var import_common8 = require("@nestjs/common");
|
|
908
|
-
var import_core4 = require("@nestjs/core");
|
|
909
|
-
function _ts_decorate8(decorators, target, key, desc) {
|
|
910
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
911
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
912
|
-
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;
|
|
913
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
914
|
-
}
|
|
915
|
-
__name(_ts_decorate8, "_ts_decorate");
|
|
916
|
-
function _ts_metadata5(k, v) {
|
|
917
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
918
|
-
}
|
|
919
|
-
__name(_ts_metadata5, "_ts_metadata");
|
|
920
|
-
var TenantContextInterceptor = class _TenantContextInterceptor {
|
|
921
|
-
static {
|
|
922
|
-
__name(this, "TenantContextInterceptor");
|
|
923
|
-
}
|
|
924
|
-
reflector;
|
|
925
|
-
tenantContext;
|
|
926
|
-
primaryDatabase;
|
|
927
|
-
requestService;
|
|
928
|
-
logger = new import_common8.Logger(_TenantContextInterceptor.name);
|
|
929
|
-
constructor(reflector, tenantContext, primaryDatabase, requestService) {
|
|
930
|
-
this.reflector = reflector;
|
|
931
|
-
this.tenantContext = tenantContext;
|
|
932
|
-
this.primaryDatabase = primaryDatabase;
|
|
933
|
-
this.requestService = requestService;
|
|
934
|
-
}
|
|
935
|
-
async intercept(context, next) {
|
|
936
|
-
const request = context.switchToHttp().getRequest();
|
|
937
|
-
this.logger.debug(`Processing request: ${request.method} ${request.url}`);
|
|
938
|
-
const isPublic = this.reflector.getAllAndOverride("isPublic", [
|
|
939
|
-
context.getHandler(),
|
|
940
|
-
context.getClass()
|
|
941
|
-
]);
|
|
942
|
-
try {
|
|
943
|
-
const tenantIdentifier = this.requestService.getTenantIdentifier();
|
|
944
|
-
if (isPublic && !tenantIdentifier) {
|
|
945
|
-
this.logger.debug("Public endpoint without tenant identifier, skipping tenant context setup");
|
|
946
|
-
return next.handle();
|
|
947
|
-
}
|
|
948
|
-
if (!tenantIdentifier) {
|
|
949
|
-
throw new import_common8.UnauthorizedException("Tenant identifier not found in request");
|
|
950
|
-
}
|
|
951
|
-
this.logger.debug(`Tenant identifier extracted: ${tenantIdentifier}`);
|
|
952
|
-
if (tenantIdentifier === "cloud") {
|
|
953
|
-
this.logger.log("Cloud platform access detected, skipping tenant context setup");
|
|
954
|
-
return next.handle();
|
|
955
|
-
}
|
|
956
|
-
const tenantInfo = await this.primaryDatabase.getTenantInfo(tenantIdentifier);
|
|
957
|
-
if (!tenantInfo) {
|
|
958
|
-
this.logger.warn(`Invalid tenant: ${tenantIdentifier}`);
|
|
959
|
-
throw new import_common8.UnauthorizedException("Invalid tenant");
|
|
960
|
-
}
|
|
961
|
-
if (tenantInfo.status !== "ACTIVE") {
|
|
962
|
-
this.logger.warn(`Tenant ${tenantIdentifier} has status: ${tenantInfo.status}`);
|
|
963
|
-
throw new import_common8.UnauthorizedException(`Tenant is ${tenantInfo.status}`);
|
|
964
|
-
}
|
|
965
|
-
this.logger.debug(`Tenant config loaded: ${tenantInfo.subdomain} (${tenantInfo.type})`);
|
|
966
|
-
this.tenantContext.setTenant(tenantInfo);
|
|
967
|
-
request.tenant = tenantInfo;
|
|
968
|
-
this.logger.log(`Tenant context set: ${tenantInfo.subdomain}`);
|
|
969
|
-
} catch (error) {
|
|
970
|
-
this.logger.error("Failed to set tenant context", error);
|
|
971
|
-
throw error;
|
|
972
|
-
}
|
|
973
|
-
return next.handle();
|
|
974
|
-
}
|
|
975
|
-
};
|
|
976
|
-
TenantContextInterceptor = _ts_decorate8([
|
|
977
|
-
(0, import_common8.Injectable)({
|
|
978
|
-
scope: import_common8.Scope.REQUEST
|
|
979
|
-
}),
|
|
980
|
-
_ts_metadata5("design:type", Function),
|
|
981
|
-
_ts_metadata5("design:paramtypes", [
|
|
982
|
-
typeof import_core4.Reflector === "undefined" ? Object : import_core4.Reflector,
|
|
983
|
-
typeof TenantContextService === "undefined" ? Object : TenantContextService,
|
|
984
|
-
typeof PrimaryDatabaseService === "undefined" ? Object : PrimaryDatabaseService,
|
|
985
|
-
typeof RequestService === "undefined" ? Object : RequestService
|
|
986
|
-
])
|
|
987
|
-
], TenantContextInterceptor);
|
|
988
|
-
|
|
989
|
-
// src/database/services/tenant-database.service.ts
|
|
990
|
-
var import_common9 = require("@nestjs/common");
|
|
991
|
-
var import_pg2 = require("pg");
|
|
992
|
-
var import_node_postgres2 = require("drizzle-orm/node-postgres");
|
|
993
|
-
function _ts_decorate9(decorators, target, key, desc) {
|
|
994
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
995
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
996
|
-
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;
|
|
997
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
998
|
-
}
|
|
999
|
-
__name(_ts_decorate9, "_ts_decorate");
|
|
1000
|
-
function _ts_metadata6(k, v) {
|
|
1001
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
1002
|
-
}
|
|
1003
|
-
__name(_ts_metadata6, "_ts_metadata");
|
|
1004
|
-
function _ts_param3(paramIndex, decorator) {
|
|
1005
|
-
return function(target, key) {
|
|
1006
|
-
decorator(target, key, paramIndex);
|
|
1007
|
-
};
|
|
1008
|
-
}
|
|
1009
|
-
__name(_ts_param3, "_ts_param");
|
|
1010
|
-
var TenantDatabaseService = class _TenantDatabaseService {
|
|
1011
|
-
static {
|
|
1012
|
-
__name(this, "TenantDatabaseService");
|
|
1013
|
-
}
|
|
1014
|
-
options;
|
|
1015
|
-
tenantContext;
|
|
1016
|
-
logger = new import_common9.Logger(_TenantDatabaseService.name);
|
|
1017
|
-
/** Connection pool: Map<cacheKey, TenantConnection> */
|
|
1018
|
-
clients = /* @__PURE__ */ new Map();
|
|
1019
|
-
/** Track last usage time for idle connection cleanup */
|
|
1020
|
-
clientLastUsed = /* @__PURE__ */ new Map();
|
|
1021
|
-
/** Cleanup interval timer */
|
|
1022
|
-
cleanupInterval;
|
|
1023
|
-
constructor(options, tenantContext) {
|
|
1024
|
-
this.options = options;
|
|
1025
|
-
this.tenantContext = tenantContext;
|
|
1026
|
-
this.startConnectionCleaner();
|
|
1027
|
-
}
|
|
1028
|
-
/**
|
|
1029
|
-
* Get the Drizzle client for the current tenant's database.
|
|
1030
|
-
* This returns the tenant-scoped database client.
|
|
1031
|
-
*
|
|
1032
|
-
* @returns Tenant-scoped Drizzle database instance
|
|
1033
|
-
* @throws UnauthorizedException if tenant context not set
|
|
1034
|
-
* @throws InternalServerErrorException if connection fails
|
|
1097
|
+
* Get the Drizzle client for the current tenant's database.
|
|
1098
|
+
* This returns the tenant-scoped database client.
|
|
1099
|
+
*
|
|
1100
|
+
* @returns Tenant-scoped Drizzle database instance
|
|
1101
|
+
* @throws UnauthorizedException if tenant context not set
|
|
1102
|
+
* @throws InternalServerErrorException if connection fails
|
|
1035
1103
|
*/
|
|
1036
1104
|
get drizzleClient() {
|
|
1037
1105
|
return this.getDbClient();
|
|
@@ -1090,7 +1158,7 @@ var TenantDatabaseService = class _TenantDatabaseService {
|
|
|
1090
1158
|
};
|
|
1091
1159
|
} catch (error) {
|
|
1092
1160
|
this.logger.error(`Failed to create database connection for tenant: ${tenant.subdomain}`, error);
|
|
1093
|
-
throw new
|
|
1161
|
+
throw new import_common11.InternalServerErrorException("Failed to connect to tenant database");
|
|
1094
1162
|
}
|
|
1095
1163
|
}
|
|
1096
1164
|
/**
|
|
@@ -1183,8 +1251,8 @@ var TenantDatabaseService = class _TenantDatabaseService {
|
|
|
1183
1251
|
}
|
|
1184
1252
|
};
|
|
1185
1253
|
TenantDatabaseService = _ts_decorate9([
|
|
1186
|
-
(0,
|
|
1187
|
-
_ts_param3(0, (0,
|
|
1254
|
+
(0, import_common11.Injectable)(),
|
|
1255
|
+
_ts_param3(0, (0, import_common11.Inject)(DATABASE_MODULE_OPTIONS)),
|
|
1188
1256
|
_ts_metadata6("design:type", Function),
|
|
1189
1257
|
_ts_metadata6("design:paramtypes", [
|
|
1190
1258
|
typeof DatabaseModuleOptions === "undefined" ? Object : DatabaseModuleOptions,
|
|
@@ -1232,7 +1300,7 @@ var DatabaseModule = class _DatabaseModule {
|
|
|
1232
1300
|
* })
|
|
1233
1301
|
*/
|
|
1234
1302
|
static forServer(options) {
|
|
1235
|
-
return
|
|
1303
|
+
return _DatabaseModule.createDynamicModule(options, "server");
|
|
1236
1304
|
}
|
|
1237
1305
|
/**
|
|
1238
1306
|
* Configure DatabaseModule for Microservice/Messaging mode (RabbitMQ workers)
|
|
@@ -1254,7 +1322,7 @@ var DatabaseModule = class _DatabaseModule {
|
|
|
1254
1322
|
* })
|
|
1255
1323
|
*/
|
|
1256
1324
|
static forMicroservice(options) {
|
|
1257
|
-
return
|
|
1325
|
+
return _DatabaseModule.createDynamicModule(options, "microservice");
|
|
1258
1326
|
}
|
|
1259
1327
|
/**
|
|
1260
1328
|
* Internal helper to create dynamic module with conditional interceptor registration
|
|
@@ -1271,18 +1339,18 @@ var DatabaseModule = class _DatabaseModule {
|
|
|
1271
1339
|
};
|
|
1272
1340
|
const providers = [
|
|
1273
1341
|
asyncProvider,
|
|
1274
|
-
|
|
1275
|
-
|
|
1342
|
+
TenantContextService2,
|
|
1343
|
+
PrimaryDatabaseService2,
|
|
1276
1344
|
TenantDatabaseService
|
|
1277
1345
|
];
|
|
1278
1346
|
if (mode === "server") {
|
|
1279
1347
|
providers.push({
|
|
1280
|
-
provide:
|
|
1348
|
+
provide: import_core3.APP_INTERCEPTOR,
|
|
1281
1349
|
useClass: TenantContextInterceptor
|
|
1282
1350
|
});
|
|
1283
1351
|
} else {
|
|
1284
1352
|
providers.push({
|
|
1285
|
-
provide:
|
|
1353
|
+
provide: import_core3.APP_INTERCEPTOR,
|
|
1286
1354
|
useClass: MessageTenantContextInterceptor
|
|
1287
1355
|
});
|
|
1288
1356
|
}
|
|
@@ -1294,21 +1362,36 @@ var DatabaseModule = class _DatabaseModule {
|
|
|
1294
1362
|
providers,
|
|
1295
1363
|
exports: [
|
|
1296
1364
|
TenantDatabaseService,
|
|
1297
|
-
|
|
1298
|
-
|
|
1365
|
+
TenantContextService2,
|
|
1366
|
+
PrimaryDatabaseService2,
|
|
1299
1367
|
asyncProvider
|
|
1300
1368
|
]
|
|
1301
1369
|
};
|
|
1302
1370
|
}
|
|
1303
1371
|
};
|
|
1304
1372
|
DatabaseModule = _ts_decorate10([
|
|
1305
|
-
(0,
|
|
1306
|
-
(0,
|
|
1373
|
+
(0, import_common12.Global)(),
|
|
1374
|
+
(0, import_common12.Module)({})
|
|
1307
1375
|
], DatabaseModule);
|
|
1308
1376
|
|
|
1377
|
+
// src/database/decorators/tenant.decorator.ts
|
|
1378
|
+
var import_common13 = require("@nestjs/common");
|
|
1379
|
+
var Tenant = (0, import_common13.createParamDecorator)((_data, ctx) => {
|
|
1380
|
+
const request = ctx.switchToHttp().getRequest();
|
|
1381
|
+
const tenantContext = request.app?.get?.(TenantContextService2);
|
|
1382
|
+
if (!tenantContext) {
|
|
1383
|
+
throw new Error("TenantContextService not found.");
|
|
1384
|
+
}
|
|
1385
|
+
return tenantContext.getTenant();
|
|
1386
|
+
});
|
|
1387
|
+
|
|
1309
1388
|
// src/database/repositories/primary-base.repository.ts
|
|
1310
|
-
var
|
|
1389
|
+
var import_common14 = require("@nestjs/common");
|
|
1311
1390
|
var import_drizzle_orm2 = require("drizzle-orm");
|
|
1391
|
+
function snakeToCamel(str) {
|
|
1392
|
+
return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
1393
|
+
}
|
|
1394
|
+
__name(snakeToCamel, "snakeToCamel");
|
|
1312
1395
|
var PrimaryBaseRepository = class {
|
|
1313
1396
|
static {
|
|
1314
1397
|
__name(this, "PrimaryBaseRepository");
|
|
@@ -1318,7 +1401,8 @@ var PrimaryBaseRepository = class {
|
|
|
1318
1401
|
logger;
|
|
1319
1402
|
/**
|
|
1320
1403
|
* The table name extracted from the Drizzle table at runtime.
|
|
1321
|
-
*
|
|
1404
|
+
* Stored in camelCase to match Drizzle's query object keys.
|
|
1405
|
+
* Example: 'email_verifications' -> 'emailVerifications'
|
|
1322
1406
|
*/
|
|
1323
1407
|
tableName;
|
|
1324
1408
|
/**
|
|
@@ -1371,10 +1455,11 @@ var PrimaryBaseRepository = class {
|
|
|
1371
1455
|
constructor(database, table) {
|
|
1372
1456
|
this.database = database;
|
|
1373
1457
|
this.table = table;
|
|
1374
|
-
|
|
1375
|
-
this.
|
|
1458
|
+
const dbTableName = (0, import_drizzle_orm2.getTableName)(table);
|
|
1459
|
+
this.tableName = snakeToCamel(dbTableName);
|
|
1460
|
+
this.logger = new import_common14.Logger(this.constructor.name);
|
|
1376
1461
|
this.logger.debug(`Initialized ${this.constructor.name}`);
|
|
1377
|
-
this.logger.debug(`Table name
|
|
1462
|
+
this.logger.debug(`Table name: '${dbTableName}' -> query key: '${this.tableName}'`);
|
|
1378
1463
|
}
|
|
1379
1464
|
/**
|
|
1380
1465
|
* Create a new record
|
|
@@ -1612,7 +1697,7 @@ var PrimaryBaseRepository = class {
|
|
|
1612
1697
|
};
|
|
1613
1698
|
|
|
1614
1699
|
// src/database/repositories/tenant-base.repository.ts
|
|
1615
|
-
var
|
|
1700
|
+
var import_common15 = require("@nestjs/common");
|
|
1616
1701
|
var import_drizzle_orm3 = require("drizzle-orm");
|
|
1617
1702
|
var TenantBaseRepository = class {
|
|
1618
1703
|
static {
|
|
@@ -1669,7 +1754,7 @@ var TenantBaseRepository = class {
|
|
|
1669
1754
|
this.database = database;
|
|
1670
1755
|
this.table = table;
|
|
1671
1756
|
this.tableName = (0, import_drizzle_orm3.getTableName)(table);
|
|
1672
|
-
this.logger = new
|
|
1757
|
+
this.logger = new import_common15.Logger(this.constructor.name);
|
|
1673
1758
|
this.logger.debug(`Initialized ${this.constructor.name}`);
|
|
1674
1759
|
}
|
|
1675
1760
|
/**
|
|
@@ -1901,424 +1986,282 @@ var TenantBaseRepository = class {
|
|
|
1901
1986
|
}
|
|
1902
1987
|
};
|
|
1903
1988
|
|
|
1904
|
-
// src/
|
|
1905
|
-
var import_common13 = require("@nestjs/common");
|
|
1906
|
-
var Tenant = (0, import_common13.createParamDecorator)((data, ctx) => {
|
|
1907
|
-
const request = ctx.switchToHttp().getRequest();
|
|
1908
|
-
const tenantContext = request.app?.get?.(TenantContextService);
|
|
1909
|
-
if (!tenantContext) {
|
|
1910
|
-
throw new Error("TenantContextService not found.");
|
|
1911
|
-
}
|
|
1912
|
-
return tenantContext.getTenant();
|
|
1913
|
-
});
|
|
1914
|
-
|
|
1915
|
-
// src/auth/decorators/onboarding.decorator.ts
|
|
1916
|
-
var import_common14 = require("@nestjs/common");
|
|
1917
|
-
var Onboarding = /* @__PURE__ */ __name(() => (0, import_common14.SetMetadata)("isOnboarding", true), "Onboarding");
|
|
1918
|
-
|
|
1919
|
-
// src/auth/decorators/public.decorator.ts
|
|
1920
|
-
var import_common15 = require("@nestjs/common");
|
|
1921
|
-
var Public = /* @__PURE__ */ __name(() => (0, import_common15.SetMetadata)("isPublic", true), "Public");
|
|
1922
|
-
|
|
1923
|
-
// src/http/http.module.ts
|
|
1989
|
+
// src/exceptions/bad-gateway.exception.ts
|
|
1924
1990
|
var import_common17 = require("@nestjs/common");
|
|
1925
1991
|
|
|
1926
|
-
// src/
|
|
1992
|
+
// src/exceptions/base-field.exception.ts
|
|
1927
1993
|
var import_common16 = require("@nestjs/common");
|
|
1928
|
-
var
|
|
1929
|
-
function _ts_decorate11(decorators, target, key, desc) {
|
|
1930
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1931
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1932
|
-
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;
|
|
1933
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1934
|
-
}
|
|
1935
|
-
__name(_ts_decorate11, "_ts_decorate");
|
|
1936
|
-
function _ts_metadata7(k, v) {
|
|
1937
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
1938
|
-
}
|
|
1939
|
-
__name(_ts_metadata7, "_ts_metadata");
|
|
1940
|
-
var CsrfGuard = class _CsrfGuard {
|
|
1994
|
+
var BaseFieldException = class extends import_common16.HttpException {
|
|
1941
1995
|
static {
|
|
1942
|
-
__name(this, "
|
|
1996
|
+
__name(this, "BaseFieldException");
|
|
1943
1997
|
}
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1998
|
+
constructor(statusOrMessageOrErrors, messageOrStatus, statusOrDetail, detail) {
|
|
1999
|
+
let errors;
|
|
2000
|
+
let httpStatus;
|
|
2001
|
+
let finalDetail;
|
|
2002
|
+
if (Array.isArray(statusOrMessageOrErrors)) {
|
|
2003
|
+
errors = statusOrMessageOrErrors;
|
|
2004
|
+
httpStatus = messageOrStatus || import_common16.HttpStatus.BAD_REQUEST;
|
|
2005
|
+
finalDetail = typeof statusOrDetail === "string" ? statusOrDetail : void 0;
|
|
2006
|
+
} else if (typeof statusOrMessageOrErrors === "string" && typeof messageOrStatus === "string") {
|
|
2007
|
+
errors = [
|
|
2008
|
+
{
|
|
2009
|
+
field: statusOrMessageOrErrors,
|
|
2010
|
+
message: messageOrStatus
|
|
2011
|
+
}
|
|
2012
|
+
];
|
|
2013
|
+
httpStatus = statusOrDetail;
|
|
2014
|
+
finalDetail = detail;
|
|
2015
|
+
} else if (typeof statusOrMessageOrErrors === "string") {
|
|
2016
|
+
errors = [
|
|
2017
|
+
{
|
|
2018
|
+
message: statusOrMessageOrErrors
|
|
2019
|
+
}
|
|
2020
|
+
];
|
|
2021
|
+
httpStatus = messageOrStatus;
|
|
2022
|
+
finalDetail = typeof statusOrDetail === "string" ? statusOrDetail : void 0;
|
|
2023
|
+
} else {
|
|
2024
|
+
errors = [
|
|
2025
|
+
{
|
|
2026
|
+
message: "An error occurred"
|
|
2027
|
+
}
|
|
2028
|
+
];
|
|
2029
|
+
httpStatus = statusOrMessageOrErrors;
|
|
2030
|
+
finalDetail = void 0;
|
|
2031
|
+
}
|
|
2032
|
+
const response = finalDetail !== void 0 ? {
|
|
2033
|
+
errors,
|
|
2034
|
+
detail: finalDetail
|
|
2035
|
+
} : {
|
|
2036
|
+
errors
|
|
2037
|
+
};
|
|
2038
|
+
super(response, httpStatus);
|
|
1948
2039
|
}
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
if (
|
|
1958
|
-
|
|
2040
|
+
};
|
|
2041
|
+
|
|
2042
|
+
// src/exceptions/bad-gateway.exception.ts
|
|
2043
|
+
var BadGatewayException = class extends BaseFieldException {
|
|
2044
|
+
static {
|
|
2045
|
+
__name(this, "BadGatewayException");
|
|
2046
|
+
}
|
|
2047
|
+
constructor(messageOrField, fieldMessageOrDetail, detail) {
|
|
2048
|
+
if (Array.isArray(messageOrField)) {
|
|
2049
|
+
super(messageOrField, import_common17.HttpStatus.BAD_GATEWAY, fieldMessageOrDetail);
|
|
2050
|
+
} else if (detail !== void 0) {
|
|
2051
|
+
super(messageOrField, fieldMessageOrDetail, import_common17.HttpStatus.BAD_GATEWAY, detail);
|
|
2052
|
+
} else if (fieldMessageOrDetail) {
|
|
2053
|
+
super(messageOrField, fieldMessageOrDetail, import_common17.HttpStatus.BAD_GATEWAY);
|
|
2054
|
+
} else {
|
|
2055
|
+
super(messageOrField, import_common17.HttpStatus.BAD_GATEWAY);
|
|
1959
2056
|
}
|
|
1960
|
-
try {
|
|
1961
|
-
const fastifyInstance = request.server;
|
|
1962
|
-
if (!fastifyInstance.csrfProtection) {
|
|
1963
|
-
this.logger.error("CSRF protection plugin not found. Ensure @fastify/csrf-protection is registered.");
|
|
1964
|
-
throw new import_common16.ForbiddenException("CSRF protection not configured");
|
|
1965
|
-
}
|
|
1966
|
-
await new Promise((resolve, reject) => {
|
|
1967
|
-
fastifyInstance.csrfProtection(request, reply, (err) => {
|
|
1968
|
-
if (err) {
|
|
1969
|
-
reject(err);
|
|
1970
|
-
} else {
|
|
1971
|
-
resolve();
|
|
1972
|
-
}
|
|
1973
|
-
});
|
|
1974
|
-
});
|
|
1975
|
-
this.logger.debug(`CSRF validation successful for ${request.method} ${request.url}`);
|
|
1976
|
-
return true;
|
|
1977
|
-
} catch (error) {
|
|
1978
|
-
this.logger.warn(`CSRF validation failed for ${request.method} ${request.url}: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
1979
|
-
throw new import_common16.ForbiddenException({
|
|
1980
|
-
errors: [
|
|
1981
|
-
{
|
|
1982
|
-
field: "csrf",
|
|
1983
|
-
message: "Invalid or missing CSRF token"
|
|
1984
|
-
}
|
|
1985
|
-
],
|
|
1986
|
-
message: "CSRF validation failed"
|
|
1987
|
-
});
|
|
1988
|
-
}
|
|
1989
|
-
}
|
|
1990
|
-
};
|
|
1991
|
-
CsrfGuard = _ts_decorate11([
|
|
1992
|
-
(0, import_common16.Injectable)(),
|
|
1993
|
-
_ts_metadata7("design:type", Function),
|
|
1994
|
-
_ts_metadata7("design:paramtypes", [
|
|
1995
|
-
typeof import_core6.Reflector === "undefined" ? Object : import_core6.Reflector
|
|
1996
|
-
])
|
|
1997
|
-
], CsrfGuard);
|
|
1998
|
-
|
|
1999
|
-
// src/http/http.module.ts
|
|
2000
|
-
function _ts_decorate12(decorators, target, key, desc) {
|
|
2001
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
2002
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
2003
|
-
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;
|
|
2004
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
2005
|
-
}
|
|
2006
|
-
__name(_ts_decorate12, "_ts_decorate");
|
|
2007
|
-
var HttpModule = class {
|
|
2008
|
-
static {
|
|
2009
|
-
__name(this, "HttpModule");
|
|
2010
2057
|
}
|
|
2011
2058
|
};
|
|
2012
|
-
HttpModule = _ts_decorate12([
|
|
2013
|
-
(0, import_common17.Module)({
|
|
2014
|
-
providers: [
|
|
2015
|
-
CsrfGuard
|
|
2016
|
-
],
|
|
2017
|
-
exports: [
|
|
2018
|
-
CsrfGuard
|
|
2019
|
-
]
|
|
2020
|
-
})
|
|
2021
|
-
], HttpModule);
|
|
2022
2059
|
|
|
2023
|
-
// src/
|
|
2060
|
+
// src/exceptions/bad-request.exception.ts
|
|
2024
2061
|
var import_common18 = require("@nestjs/common");
|
|
2025
|
-
|
|
2026
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
2027
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
2028
|
-
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;
|
|
2029
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
2030
|
-
}
|
|
2031
|
-
__name(_ts_decorate13, "_ts_decorate");
|
|
2032
|
-
function getHttpStatusTitle(status) {
|
|
2033
|
-
const enumKey = Object.entries(import_common18.HttpStatus).find(([key, value]) => value === status && isNaN(Number(key)))?.[0];
|
|
2034
|
-
if (!enumKey) {
|
|
2035
|
-
return "Error";
|
|
2036
|
-
}
|
|
2037
|
-
return enumKey.split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
|
|
2038
|
-
}
|
|
2039
|
-
__name(getHttpStatusTitle, "getHttpStatusTitle");
|
|
2040
|
-
var HttpExceptionFilter = class _HttpExceptionFilter {
|
|
2062
|
+
var BadRequestException = class extends BaseFieldException {
|
|
2041
2063
|
static {
|
|
2042
|
-
__name(this, "
|
|
2064
|
+
__name(this, "BadRequestException");
|
|
2043
2065
|
}
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
if (exception instanceof import_common18.HttpException) {
|
|
2052
|
-
status = exception.getStatus();
|
|
2053
|
-
const exceptionResponse = exception.getResponse();
|
|
2054
|
-
if (typeof exceptionResponse === "object" && exceptionResponse !== null) {
|
|
2055
|
-
const responseObj = exceptionResponse;
|
|
2056
|
-
if ("errors" in responseObj && Array.isArray(responseObj.errors)) {
|
|
2057
|
-
errors = responseObj.errors;
|
|
2058
|
-
detail = ("detail" in responseObj ? responseObj.detail : void 0) || exception.message;
|
|
2059
|
-
} else if ("message" in responseObj && Array.isArray(responseObj.message)) {
|
|
2060
|
-
errors = responseObj.message.map((msg) => {
|
|
2061
|
-
if (typeof msg === "object" && "property" in msg && "constraints" in msg) {
|
|
2062
|
-
const constraintValues = Object.values(msg.constraints);
|
|
2063
|
-
return {
|
|
2064
|
-
field: msg.property,
|
|
2065
|
-
message: constraintValues[0] ?? "Validation failed"
|
|
2066
|
-
};
|
|
2067
|
-
}
|
|
2068
|
-
return {
|
|
2069
|
-
message: typeof msg === "string" ? msg : JSON.stringify(msg)
|
|
2070
|
-
};
|
|
2071
|
-
});
|
|
2072
|
-
detail = "Validation failed";
|
|
2073
|
-
} else if ("message" in responseObj) {
|
|
2074
|
-
const message = responseObj.message;
|
|
2075
|
-
errors = [
|
|
2076
|
-
{
|
|
2077
|
-
message: Array.isArray(message) ? message.join(", ") : message
|
|
2078
|
-
}
|
|
2079
|
-
];
|
|
2080
|
-
detail = ("error" in responseObj ? responseObj.error : void 0) || exception.message;
|
|
2081
|
-
}
|
|
2082
|
-
} else if (typeof exceptionResponse === "string") {
|
|
2083
|
-
errors = [
|
|
2084
|
-
{
|
|
2085
|
-
message: exceptionResponse
|
|
2086
|
-
}
|
|
2087
|
-
];
|
|
2088
|
-
detail = exceptionResponse;
|
|
2089
|
-
}
|
|
2066
|
+
constructor(messageOrField, fieldMessageOrDetail, detail) {
|
|
2067
|
+
if (Array.isArray(messageOrField)) {
|
|
2068
|
+
super(messageOrField, import_common18.HttpStatus.BAD_REQUEST, fieldMessageOrDetail);
|
|
2069
|
+
} else if (detail !== void 0) {
|
|
2070
|
+
super(messageOrField, fieldMessageOrDetail, import_common18.HttpStatus.BAD_REQUEST, detail);
|
|
2071
|
+
} else if (fieldMessageOrDetail) {
|
|
2072
|
+
super(messageOrField, fieldMessageOrDetail, import_common18.HttpStatus.BAD_REQUEST);
|
|
2090
2073
|
} else {
|
|
2091
|
-
|
|
2092
|
-
const stack = exception instanceof Error ? exception.stack : void 0;
|
|
2093
|
-
this.logger.error(`Unexpected error: ${errorMessage}`, stack);
|
|
2094
|
-
errors = [
|
|
2095
|
-
{
|
|
2096
|
-
message: "An unexpected error occurred"
|
|
2097
|
-
}
|
|
2098
|
-
];
|
|
2074
|
+
super(messageOrField, import_common18.HttpStatus.BAD_REQUEST);
|
|
2099
2075
|
}
|
|
2100
|
-
const problemDetails = {
|
|
2101
|
-
title: getHttpStatusTitle(status),
|
|
2102
|
-
status,
|
|
2103
|
-
detail,
|
|
2104
|
-
errors
|
|
2105
|
-
};
|
|
2106
|
-
response.status(status).send(problemDetails);
|
|
2107
2076
|
}
|
|
2108
2077
|
};
|
|
2109
|
-
HttpExceptionFilter = _ts_decorate13([
|
|
2110
|
-
(0, import_common18.Catch)()
|
|
2111
|
-
], HttpExceptionFilter);
|
|
2112
2078
|
|
|
2113
|
-
// src/exceptions/
|
|
2079
|
+
// src/exceptions/conflict.exception.ts
|
|
2114
2080
|
var import_common19 = require("@nestjs/common");
|
|
2115
|
-
var
|
|
2081
|
+
var ConflictException = class extends BaseFieldException {
|
|
2116
2082
|
static {
|
|
2117
|
-
__name(this, "
|
|
2083
|
+
__name(this, "ConflictException");
|
|
2118
2084
|
}
|
|
2119
|
-
constructor(
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
finalDetail = typeof statusOrDetail === "string" ? statusOrDetail : void 0;
|
|
2127
|
-
} else if (typeof statusOrMessageOrErrors === "string" && typeof messageOrStatus === "string") {
|
|
2128
|
-
errors = [
|
|
2129
|
-
{
|
|
2130
|
-
field: statusOrMessageOrErrors,
|
|
2131
|
-
message: messageOrStatus
|
|
2132
|
-
}
|
|
2133
|
-
];
|
|
2134
|
-
httpStatus = statusOrDetail;
|
|
2135
|
-
finalDetail = detail;
|
|
2136
|
-
} else if (typeof statusOrMessageOrErrors === "string") {
|
|
2137
|
-
errors = [
|
|
2138
|
-
{
|
|
2139
|
-
message: statusOrMessageOrErrors
|
|
2140
|
-
}
|
|
2141
|
-
];
|
|
2142
|
-
httpStatus = messageOrStatus;
|
|
2143
|
-
finalDetail = typeof statusOrDetail === "string" ? statusOrDetail : void 0;
|
|
2085
|
+
constructor(messageOrField, fieldMessageOrDetail, detail) {
|
|
2086
|
+
if (Array.isArray(messageOrField)) {
|
|
2087
|
+
super(messageOrField, import_common19.HttpStatus.CONFLICT, fieldMessageOrDetail);
|
|
2088
|
+
} else if (detail !== void 0) {
|
|
2089
|
+
super(messageOrField, fieldMessageOrDetail, import_common19.HttpStatus.CONFLICT, detail);
|
|
2090
|
+
} else if (fieldMessageOrDetail) {
|
|
2091
|
+
super(messageOrField, fieldMessageOrDetail, import_common19.HttpStatus.CONFLICT);
|
|
2144
2092
|
} else {
|
|
2145
|
-
|
|
2146
|
-
{
|
|
2147
|
-
message: "An error occurred"
|
|
2148
|
-
}
|
|
2149
|
-
];
|
|
2150
|
-
httpStatus = statusOrMessageOrErrors;
|
|
2151
|
-
finalDetail = void 0;
|
|
2093
|
+
super(messageOrField, import_common19.HttpStatus.CONFLICT);
|
|
2152
2094
|
}
|
|
2153
|
-
const response = finalDetail !== void 0 ? {
|
|
2154
|
-
errors,
|
|
2155
|
-
detail: finalDetail
|
|
2156
|
-
} : {
|
|
2157
|
-
errors
|
|
2158
|
-
};
|
|
2159
|
-
super(response, httpStatus);
|
|
2160
2095
|
}
|
|
2161
2096
|
};
|
|
2162
2097
|
|
|
2163
|
-
// src/exceptions/
|
|
2098
|
+
// src/exceptions/forbidden.exception.ts
|
|
2164
2099
|
var import_common20 = require("@nestjs/common");
|
|
2165
|
-
var
|
|
2100
|
+
var ForbiddenException = class extends BaseFieldException {
|
|
2166
2101
|
static {
|
|
2167
|
-
__name(this, "
|
|
2102
|
+
__name(this, "ForbiddenException");
|
|
2168
2103
|
}
|
|
2169
2104
|
constructor(messageOrField, fieldMessageOrDetail, detail) {
|
|
2170
2105
|
if (Array.isArray(messageOrField)) {
|
|
2171
|
-
super(messageOrField, import_common20.HttpStatus.
|
|
2106
|
+
super(messageOrField, import_common20.HttpStatus.FORBIDDEN, fieldMessageOrDetail);
|
|
2172
2107
|
} else if (detail !== void 0) {
|
|
2173
|
-
super(messageOrField, fieldMessageOrDetail, import_common20.HttpStatus.
|
|
2108
|
+
super(messageOrField, fieldMessageOrDetail, import_common20.HttpStatus.FORBIDDEN, detail);
|
|
2174
2109
|
} else if (fieldMessageOrDetail) {
|
|
2175
|
-
super(messageOrField, fieldMessageOrDetail, import_common20.HttpStatus.
|
|
2110
|
+
super(messageOrField, fieldMessageOrDetail, import_common20.HttpStatus.FORBIDDEN);
|
|
2176
2111
|
} else {
|
|
2177
|
-
super(messageOrField, import_common20.HttpStatus.
|
|
2112
|
+
super(messageOrField, import_common20.HttpStatus.FORBIDDEN);
|
|
2178
2113
|
}
|
|
2179
2114
|
}
|
|
2180
2115
|
};
|
|
2181
2116
|
|
|
2182
|
-
// src/exceptions/
|
|
2117
|
+
// src/exceptions/gone.exception.ts
|
|
2183
2118
|
var import_common21 = require("@nestjs/common");
|
|
2184
|
-
var
|
|
2119
|
+
var GoneException = class extends BaseFieldException {
|
|
2185
2120
|
static {
|
|
2186
|
-
__name(this, "
|
|
2121
|
+
__name(this, "GoneException");
|
|
2187
2122
|
}
|
|
2188
2123
|
constructor(messageOrField, fieldMessageOrDetail, detail) {
|
|
2189
2124
|
if (Array.isArray(messageOrField)) {
|
|
2190
|
-
super(messageOrField, import_common21.HttpStatus.
|
|
2125
|
+
super(messageOrField, import_common21.HttpStatus.GONE, fieldMessageOrDetail);
|
|
2191
2126
|
} else if (detail !== void 0) {
|
|
2192
|
-
super(messageOrField, fieldMessageOrDetail, import_common21.HttpStatus.
|
|
2127
|
+
super(messageOrField, fieldMessageOrDetail, import_common21.HttpStatus.GONE, detail);
|
|
2193
2128
|
} else if (fieldMessageOrDetail) {
|
|
2194
|
-
super(messageOrField, fieldMessageOrDetail, import_common21.HttpStatus.
|
|
2129
|
+
super(messageOrField, fieldMessageOrDetail, import_common21.HttpStatus.GONE);
|
|
2195
2130
|
} else {
|
|
2196
|
-
super(messageOrField, import_common21.HttpStatus.
|
|
2131
|
+
super(messageOrField, import_common21.HttpStatus.GONE);
|
|
2197
2132
|
}
|
|
2198
2133
|
}
|
|
2199
2134
|
};
|
|
2200
2135
|
|
|
2201
|
-
// src/exceptions/
|
|
2136
|
+
// src/exceptions/internal-server-error.exception.ts
|
|
2202
2137
|
var import_common22 = require("@nestjs/common");
|
|
2203
|
-
var
|
|
2138
|
+
var InternalServerErrorException3 = class extends BaseFieldException {
|
|
2204
2139
|
static {
|
|
2205
|
-
__name(this, "
|
|
2140
|
+
__name(this, "InternalServerErrorException");
|
|
2206
2141
|
}
|
|
2207
2142
|
constructor(messageOrField, fieldMessageOrDetail, detail) {
|
|
2208
2143
|
if (Array.isArray(messageOrField)) {
|
|
2209
|
-
super(messageOrField, import_common22.HttpStatus.
|
|
2144
|
+
super(messageOrField, import_common22.HttpStatus.INTERNAL_SERVER_ERROR, fieldMessageOrDetail);
|
|
2210
2145
|
} else if (detail !== void 0) {
|
|
2211
|
-
super(messageOrField, fieldMessageOrDetail, import_common22.HttpStatus.
|
|
2146
|
+
super(messageOrField, fieldMessageOrDetail, import_common22.HttpStatus.INTERNAL_SERVER_ERROR, detail);
|
|
2212
2147
|
} else if (fieldMessageOrDetail) {
|
|
2213
|
-
super(messageOrField, fieldMessageOrDetail, import_common22.HttpStatus.
|
|
2148
|
+
super(messageOrField, fieldMessageOrDetail, import_common22.HttpStatus.INTERNAL_SERVER_ERROR);
|
|
2214
2149
|
} else {
|
|
2215
|
-
super(messageOrField, import_common22.HttpStatus.
|
|
2150
|
+
super(messageOrField, import_common22.HttpStatus.INTERNAL_SERVER_ERROR);
|
|
2216
2151
|
}
|
|
2217
2152
|
}
|
|
2218
2153
|
};
|
|
2219
2154
|
|
|
2220
|
-
// src/exceptions/not-
|
|
2155
|
+
// src/exceptions/method-not-allowed.exception.ts
|
|
2221
2156
|
var import_common23 = require("@nestjs/common");
|
|
2222
|
-
var
|
|
2157
|
+
var MethodNotAllowedException = class extends BaseFieldException {
|
|
2223
2158
|
static {
|
|
2224
|
-
__name(this, "
|
|
2159
|
+
__name(this, "MethodNotAllowedException");
|
|
2225
2160
|
}
|
|
2226
2161
|
constructor(messageOrField, fieldMessageOrDetail, detail) {
|
|
2227
2162
|
if (Array.isArray(messageOrField)) {
|
|
2228
|
-
super(messageOrField, import_common23.HttpStatus.
|
|
2163
|
+
super(messageOrField, import_common23.HttpStatus.METHOD_NOT_ALLOWED, fieldMessageOrDetail);
|
|
2229
2164
|
} else if (detail !== void 0) {
|
|
2230
|
-
super(messageOrField, fieldMessageOrDetail, import_common23.HttpStatus.
|
|
2165
|
+
super(messageOrField, fieldMessageOrDetail, import_common23.HttpStatus.METHOD_NOT_ALLOWED, detail);
|
|
2231
2166
|
} else if (fieldMessageOrDetail) {
|
|
2232
|
-
super(messageOrField, fieldMessageOrDetail, import_common23.HttpStatus.
|
|
2167
|
+
super(messageOrField, fieldMessageOrDetail, import_common23.HttpStatus.METHOD_NOT_ALLOWED);
|
|
2233
2168
|
} else {
|
|
2234
|
-
super(messageOrField, import_common23.HttpStatus.
|
|
2169
|
+
super(messageOrField, import_common23.HttpStatus.METHOD_NOT_ALLOWED);
|
|
2235
2170
|
}
|
|
2236
2171
|
}
|
|
2237
2172
|
};
|
|
2238
2173
|
|
|
2239
|
-
// src/exceptions/
|
|
2174
|
+
// src/exceptions/not-acceptable.exception.ts
|
|
2240
2175
|
var import_common24 = require("@nestjs/common");
|
|
2241
|
-
var
|
|
2176
|
+
var NotAcceptableException = class extends BaseFieldException {
|
|
2242
2177
|
static {
|
|
2243
|
-
__name(this, "
|
|
2178
|
+
__name(this, "NotAcceptableException");
|
|
2244
2179
|
}
|
|
2245
2180
|
constructor(messageOrField, fieldMessageOrDetail, detail) {
|
|
2246
2181
|
if (Array.isArray(messageOrField)) {
|
|
2247
|
-
super(messageOrField, import_common24.HttpStatus.
|
|
2182
|
+
super(messageOrField, import_common24.HttpStatus.NOT_ACCEPTABLE, fieldMessageOrDetail);
|
|
2248
2183
|
} else if (detail !== void 0) {
|
|
2249
|
-
super(messageOrField, fieldMessageOrDetail, import_common24.HttpStatus.
|
|
2184
|
+
super(messageOrField, fieldMessageOrDetail, import_common24.HttpStatus.NOT_ACCEPTABLE, detail);
|
|
2250
2185
|
} else if (fieldMessageOrDetail) {
|
|
2251
|
-
super(messageOrField, fieldMessageOrDetail, import_common24.HttpStatus.
|
|
2186
|
+
super(messageOrField, fieldMessageOrDetail, import_common24.HttpStatus.NOT_ACCEPTABLE);
|
|
2252
2187
|
} else {
|
|
2253
|
-
super(messageOrField, import_common24.HttpStatus.
|
|
2188
|
+
super(messageOrField, import_common24.HttpStatus.NOT_ACCEPTABLE);
|
|
2254
2189
|
}
|
|
2255
2190
|
}
|
|
2256
2191
|
};
|
|
2257
2192
|
|
|
2258
|
-
// src/exceptions/
|
|
2193
|
+
// src/exceptions/not-found.exception.ts
|
|
2259
2194
|
var import_common25 = require("@nestjs/common");
|
|
2260
|
-
var
|
|
2195
|
+
var NotFoundException = class extends BaseFieldException {
|
|
2261
2196
|
static {
|
|
2262
|
-
__name(this, "
|
|
2197
|
+
__name(this, "NotFoundException");
|
|
2263
2198
|
}
|
|
2264
2199
|
constructor(messageOrField, fieldMessageOrDetail, detail) {
|
|
2265
2200
|
if (Array.isArray(messageOrField)) {
|
|
2266
|
-
super(messageOrField, import_common25.HttpStatus.
|
|
2201
|
+
super(messageOrField, import_common25.HttpStatus.NOT_FOUND, fieldMessageOrDetail);
|
|
2267
2202
|
} else if (detail !== void 0) {
|
|
2268
|
-
super(messageOrField, fieldMessageOrDetail, import_common25.HttpStatus.
|
|
2203
|
+
super(messageOrField, fieldMessageOrDetail, import_common25.HttpStatus.NOT_FOUND, detail);
|
|
2269
2204
|
} else if (fieldMessageOrDetail) {
|
|
2270
|
-
super(messageOrField, fieldMessageOrDetail, import_common25.HttpStatus.
|
|
2205
|
+
super(messageOrField, fieldMessageOrDetail, import_common25.HttpStatus.NOT_FOUND);
|
|
2271
2206
|
} else {
|
|
2272
|
-
super(messageOrField, import_common25.HttpStatus.
|
|
2207
|
+
super(messageOrField, import_common25.HttpStatus.NOT_FOUND);
|
|
2273
2208
|
}
|
|
2274
2209
|
}
|
|
2275
2210
|
};
|
|
2276
2211
|
|
|
2277
|
-
// src/exceptions/
|
|
2212
|
+
// src/exceptions/not-implemented.exception.ts
|
|
2278
2213
|
var import_common26 = require("@nestjs/common");
|
|
2279
|
-
var
|
|
2214
|
+
var NotImplementedException = class extends BaseFieldException {
|
|
2280
2215
|
static {
|
|
2281
|
-
__name(this, "
|
|
2216
|
+
__name(this, "NotImplementedException");
|
|
2282
2217
|
}
|
|
2283
|
-
constructor(
|
|
2284
|
-
|
|
2218
|
+
constructor(messageOrField, fieldMessageOrDetail, detail) {
|
|
2219
|
+
if (Array.isArray(messageOrField)) {
|
|
2220
|
+
super(messageOrField, import_common26.HttpStatus.NOT_IMPLEMENTED, fieldMessageOrDetail);
|
|
2221
|
+
} else if (detail !== void 0) {
|
|
2222
|
+
super(messageOrField, fieldMessageOrDetail, import_common26.HttpStatus.NOT_IMPLEMENTED, detail);
|
|
2223
|
+
} else if (fieldMessageOrDetail) {
|
|
2224
|
+
super(messageOrField, fieldMessageOrDetail, import_common26.HttpStatus.NOT_IMPLEMENTED);
|
|
2225
|
+
} else {
|
|
2226
|
+
super(messageOrField, import_common26.HttpStatus.NOT_IMPLEMENTED);
|
|
2227
|
+
}
|
|
2285
2228
|
}
|
|
2286
2229
|
};
|
|
2287
2230
|
|
|
2288
|
-
// src/exceptions/
|
|
2231
|
+
// src/exceptions/payload-too-large.exception.ts
|
|
2289
2232
|
var import_common27 = require("@nestjs/common");
|
|
2290
|
-
var
|
|
2233
|
+
var PayloadTooLargeException = class extends BaseFieldException {
|
|
2291
2234
|
static {
|
|
2292
|
-
__name(this, "
|
|
2235
|
+
__name(this, "PayloadTooLargeException");
|
|
2293
2236
|
}
|
|
2294
2237
|
constructor(messageOrField, fieldMessageOrDetail, detail) {
|
|
2295
2238
|
if (Array.isArray(messageOrField)) {
|
|
2296
|
-
super(messageOrField, import_common27.HttpStatus.
|
|
2239
|
+
super(messageOrField, import_common27.HttpStatus.PAYLOAD_TOO_LARGE, fieldMessageOrDetail);
|
|
2297
2240
|
} else if (detail !== void 0) {
|
|
2298
|
-
super(messageOrField, fieldMessageOrDetail, import_common27.HttpStatus.
|
|
2241
|
+
super(messageOrField, fieldMessageOrDetail, import_common27.HttpStatus.PAYLOAD_TOO_LARGE, detail);
|
|
2299
2242
|
} else if (fieldMessageOrDetail) {
|
|
2300
|
-
super(messageOrField, fieldMessageOrDetail, import_common27.HttpStatus.
|
|
2243
|
+
super(messageOrField, fieldMessageOrDetail, import_common27.HttpStatus.PAYLOAD_TOO_LARGE);
|
|
2301
2244
|
} else {
|
|
2302
|
-
super(messageOrField, import_common27.HttpStatus.
|
|
2245
|
+
super(messageOrField, import_common27.HttpStatus.PAYLOAD_TOO_LARGE);
|
|
2303
2246
|
}
|
|
2304
2247
|
}
|
|
2305
2248
|
};
|
|
2306
2249
|
|
|
2307
|
-
// src/exceptions/
|
|
2250
|
+
// src/exceptions/request-timeout.exception.ts
|
|
2308
2251
|
var import_common28 = require("@nestjs/common");
|
|
2309
|
-
var
|
|
2252
|
+
var RequestTimeoutException = class extends BaseFieldException {
|
|
2310
2253
|
static {
|
|
2311
|
-
__name(this, "
|
|
2254
|
+
__name(this, "RequestTimeoutException");
|
|
2312
2255
|
}
|
|
2313
2256
|
constructor(messageOrField, fieldMessageOrDetail, detail) {
|
|
2314
2257
|
if (Array.isArray(messageOrField)) {
|
|
2315
|
-
super(messageOrField, import_common28.HttpStatus.
|
|
2258
|
+
super(messageOrField, import_common28.HttpStatus.REQUEST_TIMEOUT, fieldMessageOrDetail);
|
|
2316
2259
|
} else if (detail !== void 0) {
|
|
2317
|
-
super(messageOrField, fieldMessageOrDetail, import_common28.HttpStatus.
|
|
2260
|
+
super(messageOrField, fieldMessageOrDetail, import_common28.HttpStatus.REQUEST_TIMEOUT, detail);
|
|
2318
2261
|
} else if (fieldMessageOrDetail) {
|
|
2319
|
-
super(messageOrField, fieldMessageOrDetail, import_common28.HttpStatus.
|
|
2262
|
+
super(messageOrField, fieldMessageOrDetail, import_common28.HttpStatus.REQUEST_TIMEOUT);
|
|
2320
2263
|
} else {
|
|
2321
|
-
super(messageOrField, import_common28.HttpStatus.
|
|
2264
|
+
super(messageOrField, import_common28.HttpStatus.REQUEST_TIMEOUT);
|
|
2322
2265
|
}
|
|
2323
2266
|
}
|
|
2324
2267
|
};
|
|
@@ -2342,217 +2285,521 @@ var ServiceUnavailableException = class extends BaseFieldException {
|
|
|
2342
2285
|
}
|
|
2343
2286
|
};
|
|
2344
2287
|
|
|
2345
|
-
// src/exceptions/
|
|
2288
|
+
// src/exceptions/too-many-requests.exception.ts
|
|
2346
2289
|
var import_common30 = require("@nestjs/common");
|
|
2347
|
-
var
|
|
2290
|
+
var TooManyRequestsException = class extends BaseFieldException {
|
|
2348
2291
|
static {
|
|
2349
|
-
__name(this, "
|
|
2292
|
+
__name(this, "TooManyRequestsException");
|
|
2350
2293
|
}
|
|
2351
2294
|
constructor(messageOrField, fieldMessageOrDetail, detail) {
|
|
2352
2295
|
if (Array.isArray(messageOrField)) {
|
|
2353
|
-
super(messageOrField, import_common30.HttpStatus.
|
|
2296
|
+
super(messageOrField, import_common30.HttpStatus.TOO_MANY_REQUESTS, fieldMessageOrDetail);
|
|
2354
2297
|
} else if (detail !== void 0) {
|
|
2355
|
-
super(messageOrField, fieldMessageOrDetail, import_common30.HttpStatus.
|
|
2298
|
+
super(messageOrField, fieldMessageOrDetail, import_common30.HttpStatus.TOO_MANY_REQUESTS, detail);
|
|
2356
2299
|
} else if (fieldMessageOrDetail) {
|
|
2357
|
-
super(messageOrField, fieldMessageOrDetail, import_common30.HttpStatus.
|
|
2300
|
+
super(messageOrField, fieldMessageOrDetail, import_common30.HttpStatus.TOO_MANY_REQUESTS);
|
|
2358
2301
|
} else {
|
|
2359
|
-
super(messageOrField, import_common30.HttpStatus.
|
|
2302
|
+
super(messageOrField, import_common30.HttpStatus.TOO_MANY_REQUESTS);
|
|
2360
2303
|
}
|
|
2361
2304
|
}
|
|
2362
2305
|
};
|
|
2363
2306
|
|
|
2364
|
-
// src/exceptions/
|
|
2307
|
+
// src/exceptions/unauthorized.exception.ts
|
|
2365
2308
|
var import_common31 = require("@nestjs/common");
|
|
2366
|
-
var
|
|
2309
|
+
var UnauthorizedException4 = class extends BaseFieldException {
|
|
2367
2310
|
static {
|
|
2368
|
-
__name(this, "
|
|
2311
|
+
__name(this, "UnauthorizedException");
|
|
2369
2312
|
}
|
|
2370
2313
|
constructor(messageOrField, fieldMessageOrDetail, detail) {
|
|
2371
2314
|
if (Array.isArray(messageOrField)) {
|
|
2372
|
-
super(messageOrField, import_common31.HttpStatus.
|
|
2315
|
+
super(messageOrField, import_common31.HttpStatus.UNAUTHORIZED, fieldMessageOrDetail);
|
|
2373
2316
|
} else if (detail !== void 0) {
|
|
2374
|
-
super(messageOrField, fieldMessageOrDetail, import_common31.HttpStatus.
|
|
2317
|
+
super(messageOrField, fieldMessageOrDetail, import_common31.HttpStatus.UNAUTHORIZED, detail);
|
|
2375
2318
|
} else if (fieldMessageOrDetail) {
|
|
2376
|
-
super(messageOrField, fieldMessageOrDetail, import_common31.HttpStatus.
|
|
2319
|
+
super(messageOrField, fieldMessageOrDetail, import_common31.HttpStatus.UNAUTHORIZED);
|
|
2377
2320
|
} else {
|
|
2378
|
-
super(messageOrField, import_common31.HttpStatus.
|
|
2321
|
+
super(messageOrField, import_common31.HttpStatus.UNAUTHORIZED);
|
|
2379
2322
|
}
|
|
2380
2323
|
}
|
|
2381
2324
|
};
|
|
2382
2325
|
|
|
2383
|
-
// src/exceptions/
|
|
2326
|
+
// src/exceptions/unprocessable-entity.exception.ts
|
|
2384
2327
|
var import_common32 = require("@nestjs/common");
|
|
2385
|
-
var
|
|
2328
|
+
var UnprocessableEntityException = class extends BaseFieldException {
|
|
2386
2329
|
static {
|
|
2387
|
-
__name(this, "
|
|
2330
|
+
__name(this, "UnprocessableEntityException");
|
|
2388
2331
|
}
|
|
2389
2332
|
constructor(messageOrField, fieldMessageOrDetail, detail) {
|
|
2390
2333
|
if (Array.isArray(messageOrField)) {
|
|
2391
|
-
super(messageOrField, import_common32.HttpStatus.
|
|
2334
|
+
super(messageOrField, import_common32.HttpStatus.UNPROCESSABLE_ENTITY, fieldMessageOrDetail);
|
|
2392
2335
|
} else if (detail !== void 0) {
|
|
2393
|
-
super(messageOrField, fieldMessageOrDetail, import_common32.HttpStatus.
|
|
2336
|
+
super(messageOrField, fieldMessageOrDetail, import_common32.HttpStatus.UNPROCESSABLE_ENTITY, detail);
|
|
2394
2337
|
} else if (fieldMessageOrDetail) {
|
|
2395
|
-
super(messageOrField, fieldMessageOrDetail, import_common32.HttpStatus.
|
|
2338
|
+
super(messageOrField, fieldMessageOrDetail, import_common32.HttpStatus.UNPROCESSABLE_ENTITY);
|
|
2396
2339
|
} else {
|
|
2397
|
-
super(messageOrField, import_common32.HttpStatus.
|
|
2340
|
+
super(messageOrField, import_common32.HttpStatus.UNPROCESSABLE_ENTITY);
|
|
2398
2341
|
}
|
|
2399
2342
|
}
|
|
2400
2343
|
};
|
|
2401
2344
|
|
|
2402
|
-
// src/exceptions/
|
|
2345
|
+
// src/exceptions/unsupported-media-type.exception.ts
|
|
2403
2346
|
var import_common33 = require("@nestjs/common");
|
|
2404
|
-
var
|
|
2347
|
+
var UnsupportedMediaTypeException = class extends BaseFieldException {
|
|
2405
2348
|
static {
|
|
2406
|
-
__name(this, "
|
|
2349
|
+
__name(this, "UnsupportedMediaTypeException");
|
|
2407
2350
|
}
|
|
2408
2351
|
constructor(messageOrField, fieldMessageOrDetail, detail) {
|
|
2409
2352
|
if (Array.isArray(messageOrField)) {
|
|
2410
|
-
super(messageOrField, import_common33.HttpStatus.
|
|
2353
|
+
super(messageOrField, import_common33.HttpStatus.UNSUPPORTED_MEDIA_TYPE, fieldMessageOrDetail);
|
|
2411
2354
|
} else if (detail !== void 0) {
|
|
2412
|
-
super(messageOrField, fieldMessageOrDetail, import_common33.HttpStatus.
|
|
2355
|
+
super(messageOrField, fieldMessageOrDetail, import_common33.HttpStatus.UNSUPPORTED_MEDIA_TYPE, detail);
|
|
2413
2356
|
} else if (fieldMessageOrDetail) {
|
|
2414
|
-
super(messageOrField, fieldMessageOrDetail, import_common33.HttpStatus.
|
|
2357
|
+
super(messageOrField, fieldMessageOrDetail, import_common33.HttpStatus.UNSUPPORTED_MEDIA_TYPE);
|
|
2415
2358
|
} else {
|
|
2416
|
-
super(messageOrField, import_common33.HttpStatus.
|
|
2359
|
+
super(messageOrField, import_common33.HttpStatus.UNSUPPORTED_MEDIA_TYPE);
|
|
2417
2360
|
}
|
|
2418
2361
|
}
|
|
2419
2362
|
};
|
|
2420
2363
|
|
|
2421
|
-
// src/exceptions/
|
|
2364
|
+
// src/exceptions/validation.exception.ts
|
|
2422
2365
|
var import_common34 = require("@nestjs/common");
|
|
2423
|
-
var
|
|
2366
|
+
var ValidationException = class extends BaseFieldException {
|
|
2424
2367
|
static {
|
|
2425
|
-
__name(this, "
|
|
2368
|
+
__name(this, "ValidationException");
|
|
2426
2369
|
}
|
|
2427
|
-
constructor(
|
|
2428
|
-
|
|
2429
|
-
super(messageOrField, import_common34.HttpStatus.PAYLOAD_TOO_LARGE, fieldMessageOrDetail);
|
|
2430
|
-
} else if (detail !== void 0) {
|
|
2431
|
-
super(messageOrField, fieldMessageOrDetail, import_common34.HttpStatus.PAYLOAD_TOO_LARGE, detail);
|
|
2432
|
-
} else if (fieldMessageOrDetail) {
|
|
2433
|
-
super(messageOrField, fieldMessageOrDetail, import_common34.HttpStatus.PAYLOAD_TOO_LARGE);
|
|
2434
|
-
} else {
|
|
2435
|
-
super(messageOrField, import_common34.HttpStatus.PAYLOAD_TOO_LARGE);
|
|
2436
|
-
}
|
|
2370
|
+
constructor(errors, detail) {
|
|
2371
|
+
super(errors, import_common34.HttpStatus.BAD_REQUEST, detail);
|
|
2437
2372
|
}
|
|
2438
2373
|
};
|
|
2439
2374
|
|
|
2440
|
-
// src/
|
|
2375
|
+
// src/filters/http-exception.filter.ts
|
|
2441
2376
|
var import_common35 = require("@nestjs/common");
|
|
2442
|
-
|
|
2377
|
+
function _ts_decorate11(decorators, target, key, desc) {
|
|
2378
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
2379
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
2380
|
+
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;
|
|
2381
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
2382
|
+
}
|
|
2383
|
+
__name(_ts_decorate11, "_ts_decorate");
|
|
2384
|
+
function getHttpStatusTitle(status) {
|
|
2385
|
+
const enumKey = Object.entries(import_common35.HttpStatus).find(([key, value]) => value === status && Number.isNaN(Number(key)))?.[0];
|
|
2386
|
+
if (!enumKey) {
|
|
2387
|
+
return "Error";
|
|
2388
|
+
}
|
|
2389
|
+
return enumKey.split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
|
|
2390
|
+
}
|
|
2391
|
+
__name(getHttpStatusTitle, "getHttpStatusTitle");
|
|
2392
|
+
var HttpExceptionFilter = class _HttpExceptionFilter {
|
|
2443
2393
|
static {
|
|
2444
|
-
__name(this, "
|
|
2394
|
+
__name(this, "HttpExceptionFilter");
|
|
2445
2395
|
}
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2396
|
+
logger = new import_common35.Logger(_HttpExceptionFilter.name);
|
|
2397
|
+
catch(exception, host) {
|
|
2398
|
+
const ctx = host.switchToHttp();
|
|
2399
|
+
const response = ctx.getResponse();
|
|
2400
|
+
let status = import_common35.HttpStatus.INTERNAL_SERVER_ERROR;
|
|
2401
|
+
let errors = [];
|
|
2402
|
+
let detail = "Internal server error";
|
|
2403
|
+
if (exception instanceof import_common35.HttpException) {
|
|
2404
|
+
status = exception.getStatus();
|
|
2405
|
+
const exceptionResponse = exception.getResponse();
|
|
2406
|
+
if (typeof exceptionResponse === "object" && exceptionResponse !== null) {
|
|
2407
|
+
const responseObj = exceptionResponse;
|
|
2408
|
+
if ("errors" in responseObj && Array.isArray(responseObj.errors)) {
|
|
2409
|
+
errors = responseObj.errors;
|
|
2410
|
+
detail = ("detail" in responseObj ? responseObj.detail : void 0) || exception.message;
|
|
2411
|
+
} else if ("message" in responseObj && Array.isArray(responseObj.message)) {
|
|
2412
|
+
errors = responseObj.message.map((msg) => {
|
|
2413
|
+
if (typeof msg === "object" && "property" in msg && "constraints" in msg) {
|
|
2414
|
+
const constraintValues = Object.values(msg.constraints);
|
|
2415
|
+
return {
|
|
2416
|
+
field: msg.property,
|
|
2417
|
+
message: constraintValues[0] ?? "Validation failed"
|
|
2418
|
+
};
|
|
2419
|
+
}
|
|
2420
|
+
return {
|
|
2421
|
+
message: typeof msg === "string" ? msg : JSON.stringify(msg)
|
|
2422
|
+
};
|
|
2423
|
+
});
|
|
2424
|
+
detail = "Validation failed";
|
|
2425
|
+
} else if ("message" in responseObj) {
|
|
2426
|
+
const message = responseObj.message;
|
|
2427
|
+
errors = [
|
|
2428
|
+
{
|
|
2429
|
+
message: Array.isArray(message) ? message.join(", ") : message
|
|
2430
|
+
}
|
|
2431
|
+
];
|
|
2432
|
+
detail = ("error" in responseObj ? responseObj.error : void 0) || exception.message;
|
|
2433
|
+
}
|
|
2434
|
+
} else if (typeof exceptionResponse === "string") {
|
|
2435
|
+
errors = [
|
|
2436
|
+
{
|
|
2437
|
+
message: exceptionResponse
|
|
2438
|
+
}
|
|
2439
|
+
];
|
|
2440
|
+
detail = exceptionResponse;
|
|
2441
|
+
}
|
|
2453
2442
|
} else {
|
|
2454
|
-
|
|
2443
|
+
const errorMessage = exception instanceof Error ? exception.message : "Unknown error";
|
|
2444
|
+
const stack = exception instanceof Error ? exception.stack : void 0;
|
|
2445
|
+
this.logger.error(`Unexpected error: ${errorMessage}`, stack);
|
|
2446
|
+
errors = [
|
|
2447
|
+
{
|
|
2448
|
+
message: "An unexpected error occurred"
|
|
2449
|
+
}
|
|
2450
|
+
];
|
|
2455
2451
|
}
|
|
2452
|
+
const problemDetails = {
|
|
2453
|
+
title: getHttpStatusTitle(status),
|
|
2454
|
+
status,
|
|
2455
|
+
detail,
|
|
2456
|
+
errors
|
|
2457
|
+
};
|
|
2458
|
+
response.status(status).send(problemDetails);
|
|
2456
2459
|
}
|
|
2457
2460
|
};
|
|
2461
|
+
HttpExceptionFilter = _ts_decorate11([
|
|
2462
|
+
(0, import_common35.Catch)()
|
|
2463
|
+
], HttpExceptionFilter);
|
|
2458
2464
|
|
|
2459
|
-
// src/
|
|
2465
|
+
// src/http/guards/csrf.guard.ts
|
|
2460
2466
|
var import_common36 = require("@nestjs/common");
|
|
2461
|
-
|
|
2467
|
+
function _ts_decorate12(decorators, target, key, desc) {
|
|
2468
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
2469
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
2470
|
+
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;
|
|
2471
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
2472
|
+
}
|
|
2473
|
+
__name(_ts_decorate12, "_ts_decorate");
|
|
2474
|
+
var CsrfGuard = class _CsrfGuard {
|
|
2462
2475
|
static {
|
|
2463
|
-
__name(this, "
|
|
2476
|
+
__name(this, "CsrfGuard");
|
|
2464
2477
|
}
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2478
|
+
logger = new import_common36.Logger(_CsrfGuard.name);
|
|
2479
|
+
async canActivate(context) {
|
|
2480
|
+
const request = context.switchToHttp().getRequest();
|
|
2481
|
+
const reply = context.switchToHttp().getResponse();
|
|
2482
|
+
const safeMethods = [
|
|
2483
|
+
"GET",
|
|
2484
|
+
"HEAD",
|
|
2485
|
+
"OPTIONS"
|
|
2486
|
+
];
|
|
2487
|
+
if (safeMethods.includes(request.method)) {
|
|
2488
|
+
return true;
|
|
2489
|
+
}
|
|
2490
|
+
try {
|
|
2491
|
+
const fastifyInstance = request.server;
|
|
2492
|
+
if (!fastifyInstance.csrfProtection) {
|
|
2493
|
+
this.logger.error("CSRF protection plugin not found. Ensure @fastify/csrf-protection is registered.");
|
|
2494
|
+
throw new import_common36.ForbiddenException("CSRF protection not configured");
|
|
2495
|
+
}
|
|
2496
|
+
await new Promise((resolve, reject) => {
|
|
2497
|
+
fastifyInstance.csrfProtection(request, reply, (err) => {
|
|
2498
|
+
if (err) {
|
|
2499
|
+
reject(err);
|
|
2500
|
+
} else {
|
|
2501
|
+
resolve();
|
|
2502
|
+
}
|
|
2503
|
+
});
|
|
2504
|
+
});
|
|
2505
|
+
this.logger.debug(`CSRF validation successful for ${request.method} ${request.url}`);
|
|
2506
|
+
return true;
|
|
2507
|
+
} catch (error) {
|
|
2508
|
+
this.logger.warn(`CSRF validation failed for ${request.method} ${request.url}: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
2509
|
+
throw new import_common36.ForbiddenException({
|
|
2510
|
+
errors: [
|
|
2511
|
+
{
|
|
2512
|
+
field: "csrf",
|
|
2513
|
+
message: "Invalid or missing CSRF token"
|
|
2514
|
+
}
|
|
2515
|
+
],
|
|
2516
|
+
message: "CSRF validation failed"
|
|
2517
|
+
});
|
|
2474
2518
|
}
|
|
2475
2519
|
}
|
|
2476
2520
|
};
|
|
2521
|
+
CsrfGuard = _ts_decorate12([
|
|
2522
|
+
(0, import_common36.Injectable)()
|
|
2523
|
+
], CsrfGuard);
|
|
2477
2524
|
|
|
2478
|
-
// src/
|
|
2525
|
+
// src/http/http.module.ts
|
|
2479
2526
|
var import_common37 = require("@nestjs/common");
|
|
2480
|
-
|
|
2527
|
+
function _ts_decorate13(decorators, target, key, desc) {
|
|
2528
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
2529
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
2530
|
+
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;
|
|
2531
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
2532
|
+
}
|
|
2533
|
+
__name(_ts_decorate13, "_ts_decorate");
|
|
2534
|
+
var HttpModule = class {
|
|
2481
2535
|
static {
|
|
2482
|
-
__name(this, "
|
|
2536
|
+
__name(this, "HttpModule");
|
|
2483
2537
|
}
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2538
|
+
};
|
|
2539
|
+
HttpModule = _ts_decorate13([
|
|
2540
|
+
(0, import_common37.Module)({
|
|
2541
|
+
providers: [
|
|
2542
|
+
CsrfGuard
|
|
2543
|
+
],
|
|
2544
|
+
exports: [
|
|
2545
|
+
CsrfGuard
|
|
2546
|
+
]
|
|
2547
|
+
})
|
|
2548
|
+
], HttpModule);
|
|
2549
|
+
|
|
2550
|
+
// src/logger/interceptors/http-logger.interceptor.ts
|
|
2551
|
+
var import_common38 = require("@nestjs/common");
|
|
2552
|
+
var import_operators2 = require("rxjs/operators");
|
|
2553
|
+
|
|
2554
|
+
// src/logger/utils/index.ts
|
|
2555
|
+
var import_node_async_hooks = require("async_hooks");
|
|
2556
|
+
var import_node_crypto = require("crypto");
|
|
2557
|
+
var correlationStorage = new import_node_async_hooks.AsyncLocalStorage();
|
|
2558
|
+
function getCorrelationContext() {
|
|
2559
|
+
return correlationStorage.getStore();
|
|
2560
|
+
}
|
|
2561
|
+
__name(getCorrelationContext, "getCorrelationContext");
|
|
2562
|
+
function runWithCorrelationContext(context, callback) {
|
|
2563
|
+
return correlationStorage.run(context, callback);
|
|
2564
|
+
}
|
|
2565
|
+
__name(runWithCorrelationContext, "runWithCorrelationContext");
|
|
2566
|
+
function updateCorrelationContext(updates) {
|
|
2567
|
+
const context = correlationStorage.getStore();
|
|
2568
|
+
if (context) {
|
|
2569
|
+
Object.assign(context, updates);
|
|
2570
|
+
}
|
|
2571
|
+
}
|
|
2572
|
+
__name(updateCorrelationContext, "updateCorrelationContext");
|
|
2573
|
+
var DEFAULT_CORRELATION_HEADER = "x-correlation-id";
|
|
2574
|
+
function generateCorrelationId() {
|
|
2575
|
+
return (0, import_node_crypto.randomUUID)();
|
|
2576
|
+
}
|
|
2577
|
+
__name(generateCorrelationId, "generateCorrelationId");
|
|
2578
|
+
function addCorrelationIdToResponse(reply, correlationId, headerName = DEFAULT_CORRELATION_HEADER) {
|
|
2579
|
+
if (typeof reply.header === "function") {
|
|
2580
|
+
reply.header(headerName, correlationId);
|
|
2581
|
+
} else if (reply.raw && typeof reply.raw.setHeader === "function") {
|
|
2582
|
+
reply.raw.setHeader(headerName, correlationId);
|
|
2583
|
+
}
|
|
2584
|
+
}
|
|
2585
|
+
__name(addCorrelationIdToResponse, "addCorrelationIdToResponse");
|
|
2586
|
+
|
|
2587
|
+
// src/logger/interceptors/http-logger.interceptor.ts
|
|
2588
|
+
function _ts_decorate14(decorators, target, key, desc) {
|
|
2589
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
2590
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
2591
|
+
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;
|
|
2592
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
2593
|
+
}
|
|
2594
|
+
__name(_ts_decorate14, "_ts_decorate");
|
|
2595
|
+
function _ts_metadata7(k, v) {
|
|
2596
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
2597
|
+
}
|
|
2598
|
+
__name(_ts_metadata7, "_ts_metadata");
|
|
2599
|
+
function _ts_param4(paramIndex, decorator) {
|
|
2600
|
+
return function(target, key) {
|
|
2601
|
+
decorator(target, key, paramIndex);
|
|
2602
|
+
};
|
|
2603
|
+
}
|
|
2604
|
+
__name(_ts_param4, "_ts_param");
|
|
2605
|
+
var HttpLoggerInterceptor = class {
|
|
2606
|
+
static {
|
|
2607
|
+
__name(this, "HttpLoggerInterceptor");
|
|
2608
|
+
}
|
|
2609
|
+
logger;
|
|
2610
|
+
enableRequestLog;
|
|
2611
|
+
enableResponseLog;
|
|
2612
|
+
slowRequestThreshold;
|
|
2613
|
+
constructor(logger, options) {
|
|
2614
|
+
this.logger = logger;
|
|
2615
|
+
this.enableRequestLog = options?.enableRequestLog ?? true;
|
|
2616
|
+
this.enableResponseLog = options?.enableResponseLog ?? true;
|
|
2617
|
+
this.slowRequestThreshold = options?.slowRequestThreshold ?? 3e3;
|
|
2618
|
+
}
|
|
2619
|
+
intercept(context, next) {
|
|
2620
|
+
if (context.getType() !== "http") {
|
|
2621
|
+
return next.handle();
|
|
2622
|
+
}
|
|
2623
|
+
const httpContext = context.switchToHttp();
|
|
2624
|
+
const request = httpContext.getRequest();
|
|
2625
|
+
const response = httpContext.getResponse();
|
|
2626
|
+
const startTime = Date.now();
|
|
2627
|
+
if (this.enableRequestLog) {
|
|
2628
|
+
this.logRequest(request);
|
|
2629
|
+
}
|
|
2630
|
+
return next.handle().pipe((0, import_operators2.tap)(() => {
|
|
2631
|
+
if (this.enableResponseLog) {
|
|
2632
|
+
const duration = Date.now() - startTime;
|
|
2633
|
+
this.logResponse(request, response, duration);
|
|
2634
|
+
}
|
|
2635
|
+
}), (0, import_operators2.catchError)((error) => {
|
|
2636
|
+
const duration = Date.now() - startTime;
|
|
2637
|
+
this.logError(request, response, duration, error);
|
|
2638
|
+
throw error;
|
|
2639
|
+
}));
|
|
2640
|
+
}
|
|
2641
|
+
logRequest(request) {
|
|
2642
|
+
try {
|
|
2643
|
+
const correlationContext = getCorrelationContext();
|
|
2644
|
+
const metadata = {
|
|
2645
|
+
type: "http_request",
|
|
2646
|
+
method: request.method,
|
|
2647
|
+
url: request.url,
|
|
2648
|
+
correlationId: correlationContext?.correlationId,
|
|
2649
|
+
ip: request.ip,
|
|
2650
|
+
userAgent: request.headers["user-agent"]
|
|
2651
|
+
};
|
|
2652
|
+
this.logger.logWithMetadata("log", `Incoming ${request.method} ${request.url}`, metadata);
|
|
2653
|
+
} catch (error) {
|
|
2654
|
+
this.logger.error("Failed to log HTTP request", error.stack);
|
|
2655
|
+
}
|
|
2656
|
+
}
|
|
2657
|
+
logResponse(request, response, duration) {
|
|
2658
|
+
try {
|
|
2659
|
+
const correlationContext = getCorrelationContext();
|
|
2660
|
+
const statusCode = response.statusCode;
|
|
2661
|
+
const logLevel = statusCode >= 500 ? "error" : statusCode >= 400 ? "warn" : "log";
|
|
2662
|
+
const metadata = {
|
|
2663
|
+
type: "http_response",
|
|
2664
|
+
method: request.method,
|
|
2665
|
+
url: request.url,
|
|
2666
|
+
statusCode,
|
|
2667
|
+
duration,
|
|
2668
|
+
correlationId: correlationContext?.correlationId
|
|
2669
|
+
};
|
|
2670
|
+
if (duration > this.slowRequestThreshold) {
|
|
2671
|
+
metadata.slowRequest = true;
|
|
2672
|
+
}
|
|
2673
|
+
const message = metadata.slowRequest ? `SLOW ${request.method} ${request.url} ${statusCode} - ${duration}ms` : `${request.method} ${request.url} ${statusCode} - ${duration}ms`;
|
|
2674
|
+
this.logger.logWithMetadata(logLevel, message, metadata);
|
|
2675
|
+
} catch (error) {
|
|
2676
|
+
this.logger.error("Failed to log HTTP response", error.stack);
|
|
2677
|
+
}
|
|
2678
|
+
}
|
|
2679
|
+
logError(request, response, duration, error) {
|
|
2680
|
+
try {
|
|
2681
|
+
const correlationContext = getCorrelationContext();
|
|
2682
|
+
const statusCode = response.statusCode || 500;
|
|
2683
|
+
const metadata = {
|
|
2684
|
+
type: "http_error",
|
|
2685
|
+
method: request.method,
|
|
2686
|
+
url: request.url,
|
|
2687
|
+
statusCode,
|
|
2688
|
+
duration,
|
|
2689
|
+
correlationId: correlationContext?.correlationId,
|
|
2690
|
+
errorName: error?.name || "Error",
|
|
2691
|
+
errorMessage: error?.message || "Unknown error"
|
|
2692
|
+
};
|
|
2693
|
+
if (error?.stack) {
|
|
2694
|
+
metadata.trace = error.stack;
|
|
2695
|
+
}
|
|
2696
|
+
if (error?.response) {
|
|
2697
|
+
metadata.errorDetails = error.response;
|
|
2698
|
+
}
|
|
2699
|
+
const message = `ERROR ${request.method} ${request.url} ${statusCode} - ${error?.message || "Unknown error"}`;
|
|
2700
|
+
this.logger.logWithMetadata("error", message, metadata);
|
|
2701
|
+
} catch (loggingError) {
|
|
2702
|
+
this.logger.error("Failed to log HTTP error", loggingError.stack);
|
|
2703
|
+
}
|
|
2704
|
+
}
|
|
2705
|
+
};
|
|
2706
|
+
HttpLoggerInterceptor = _ts_decorate14([
|
|
2707
|
+
(0, import_common38.Injectable)(),
|
|
2708
|
+
_ts_param4(1, (0, import_common38.Optional)()),
|
|
2709
|
+
_ts_metadata7("design:type", Function),
|
|
2710
|
+
_ts_metadata7("design:paramtypes", [
|
|
2711
|
+
typeof LoggerService === "undefined" ? Object : LoggerService,
|
|
2712
|
+
typeof HttpLoggerOptions === "undefined" ? Object : HttpLoggerOptions
|
|
2713
|
+
])
|
|
2714
|
+
], HttpLoggerInterceptor);
|
|
2715
|
+
|
|
2716
|
+
// src/logger/logger.module.ts
|
|
2717
|
+
var import_common41 = require("@nestjs/common");
|
|
2718
|
+
|
|
2719
|
+
// src/logger/middleware/correlation-id.middleware.ts
|
|
2720
|
+
var import_common39 = require("@nestjs/common");
|
|
2721
|
+
function _ts_decorate15(decorators, target, key, desc) {
|
|
2722
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
2723
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
2724
|
+
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;
|
|
2725
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
2726
|
+
}
|
|
2727
|
+
__name(_ts_decorate15, "_ts_decorate");
|
|
2728
|
+
function _ts_metadata8(k, v) {
|
|
2729
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
2730
|
+
}
|
|
2731
|
+
__name(_ts_metadata8, "_ts_metadata");
|
|
2732
|
+
var CorrelationIdMiddleware = class {
|
|
2733
|
+
static {
|
|
2734
|
+
__name(this, "CorrelationIdMiddleware");
|
|
2735
|
+
}
|
|
2736
|
+
includeInResponse;
|
|
2737
|
+
responseHeader;
|
|
2738
|
+
constructor(options = {}) {
|
|
2739
|
+
this.includeInResponse = options.includeInResponse ?? true;
|
|
2740
|
+
this.responseHeader = options.responseHeader ?? DEFAULT_CORRELATION_HEADER;
|
|
2741
|
+
}
|
|
2742
|
+
/**
|
|
2743
|
+
* Middleware handler for processing requests.
|
|
2744
|
+
*/
|
|
2745
|
+
use(_req, reply, next) {
|
|
2746
|
+
const correlationId = generateCorrelationId();
|
|
2747
|
+
if (this.includeInResponse) {
|
|
2748
|
+
addCorrelationIdToResponse(reply, correlationId, this.responseHeader);
|
|
2749
|
+
}
|
|
2750
|
+
runWithCorrelationContext({
|
|
2751
|
+
correlationId
|
|
2752
|
+
}, () => {
|
|
2753
|
+
next();
|
|
2754
|
+
});
|
|
2755
|
+
}
|
|
2756
|
+
/**
|
|
2757
|
+
* Fastify hook handler for onRequest.
|
|
2758
|
+
* This is an async function that returns a Promise, ensuring the AsyncLocalStorage
|
|
2759
|
+
* context persists throughout the entire request lifecycle.
|
|
2760
|
+
*/
|
|
2761
|
+
async onRequest(_req, reply) {
|
|
2762
|
+
const correlationId = generateCorrelationId();
|
|
2763
|
+
if (this.includeInResponse) {
|
|
2764
|
+
addCorrelationIdToResponse(reply, correlationId, this.responseHeader);
|
|
2765
|
+
}
|
|
2766
|
+
const store = correlationStorage.getStore();
|
|
2767
|
+
if (!store) {
|
|
2768
|
+
correlationStorage.enterWith({
|
|
2769
|
+
correlationId
|
|
2770
|
+
});
|
|
2493
2771
|
}
|
|
2494
2772
|
}
|
|
2495
2773
|
};
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2774
|
+
CorrelationIdMiddleware = _ts_decorate15([
|
|
2775
|
+
(0, import_common39.Injectable)(),
|
|
2776
|
+
_ts_metadata8("design:type", Function),
|
|
2777
|
+
_ts_metadata8("design:paramtypes", [
|
|
2778
|
+
typeof CorrelationIdMiddlewareOptions === "undefined" ? Object : CorrelationIdMiddlewareOptions
|
|
2779
|
+
])
|
|
2780
|
+
], CorrelationIdMiddleware);
|
|
2499
2781
|
|
|
2500
2782
|
// src/logger/services/logger.service.ts
|
|
2501
|
-
var
|
|
2783
|
+
var import_common40 = require("@nestjs/common");
|
|
2502
2784
|
var import_winston = require("winston");
|
|
2503
2785
|
var import_winston_daily_rotate_file = __toESM(require("winston-daily-rotate-file"), 1);
|
|
2504
|
-
|
|
2505
|
-
// src/logger/utils/index.ts
|
|
2506
|
-
var import_node_async_hooks = require("async_hooks");
|
|
2507
|
-
var import_node_crypto = require("crypto");
|
|
2508
|
-
var correlationStorage = new import_node_async_hooks.AsyncLocalStorage();
|
|
2509
|
-
function getCorrelationContext() {
|
|
2510
|
-
return correlationStorage.getStore();
|
|
2511
|
-
}
|
|
2512
|
-
__name(getCorrelationContext, "getCorrelationContext");
|
|
2513
|
-
function runWithCorrelationContext(context, callback) {
|
|
2514
|
-
return correlationStorage.run(context, callback);
|
|
2515
|
-
}
|
|
2516
|
-
__name(runWithCorrelationContext, "runWithCorrelationContext");
|
|
2517
|
-
function updateCorrelationContext(updates) {
|
|
2518
|
-
const context = correlationStorage.getStore();
|
|
2519
|
-
if (context) {
|
|
2520
|
-
Object.assign(context, updates);
|
|
2521
|
-
}
|
|
2522
|
-
}
|
|
2523
|
-
__name(updateCorrelationContext, "updateCorrelationContext");
|
|
2524
|
-
var DEFAULT_CORRELATION_HEADER = "x-correlation-id";
|
|
2525
|
-
function generateCorrelationId() {
|
|
2526
|
-
return (0, import_node_crypto.randomUUID)();
|
|
2527
|
-
}
|
|
2528
|
-
__name(generateCorrelationId, "generateCorrelationId");
|
|
2529
|
-
function addCorrelationIdToResponse(reply, correlationId, headerName = DEFAULT_CORRELATION_HEADER) {
|
|
2530
|
-
if (typeof reply.header === "function") {
|
|
2531
|
-
reply.header(headerName, correlationId);
|
|
2532
|
-
} else if (reply.raw && typeof reply.raw.setHeader === "function") {
|
|
2533
|
-
reply.raw.setHeader(headerName, correlationId);
|
|
2534
|
-
}
|
|
2535
|
-
}
|
|
2536
|
-
__name(addCorrelationIdToResponse, "addCorrelationIdToResponse");
|
|
2537
|
-
|
|
2538
|
-
// src/logger/services/logger.service.ts
|
|
2539
|
-
function _ts_decorate14(decorators, target, key, desc) {
|
|
2786
|
+
function _ts_decorate16(decorators, target, key, desc) {
|
|
2540
2787
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
2541
2788
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
2542
2789
|
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;
|
|
2543
2790
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
2544
2791
|
}
|
|
2545
|
-
__name(
|
|
2546
|
-
function
|
|
2792
|
+
__name(_ts_decorate16, "_ts_decorate");
|
|
2793
|
+
function _ts_metadata9(k, v) {
|
|
2547
2794
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
2548
2795
|
}
|
|
2549
|
-
__name(
|
|
2550
|
-
function
|
|
2796
|
+
__name(_ts_metadata9, "_ts_metadata");
|
|
2797
|
+
function _ts_param5(paramIndex, decorator) {
|
|
2551
2798
|
return function(target, key) {
|
|
2552
2799
|
decorator(target, key, paramIndex);
|
|
2553
2800
|
};
|
|
2554
2801
|
}
|
|
2555
|
-
__name(
|
|
2802
|
+
__name(_ts_param5, "_ts_param");
|
|
2556
2803
|
var LoggerService2 = class _LoggerService {
|
|
2557
2804
|
static {
|
|
2558
2805
|
__name(this, "LoggerService");
|
|
@@ -2605,7 +2852,8 @@ var LoggerService2 = class _LoggerService {
|
|
|
2605
2852
|
].filter(Boolean);
|
|
2606
2853
|
let output = parts.join(" ");
|
|
2607
2854
|
if (trace) {
|
|
2608
|
-
output +=
|
|
2855
|
+
output += `
|
|
2856
|
+
${trace}`;
|
|
2609
2857
|
}
|
|
2610
2858
|
return output;
|
|
2611
2859
|
}), import_winston.format.colorize({
|
|
@@ -2753,210 +3001,16 @@ var LoggerService2 = class _LoggerService {
|
|
|
2753
3001
|
return childLogger;
|
|
2754
3002
|
}
|
|
2755
3003
|
};
|
|
2756
|
-
LoggerService2 =
|
|
2757
|
-
(0, import_common38.Injectable)(),
|
|
2758
|
-
_ts_param4(0, (0, import_common38.Optional)()),
|
|
2759
|
-
_ts_param4(1, (0, import_common38.Optional)()),
|
|
2760
|
-
_ts_metadata8("design:type", Function),
|
|
2761
|
-
_ts_metadata8("design:paramtypes", [
|
|
2762
|
-
typeof LoggerModuleOptions === "undefined" ? Object : LoggerModuleOptions,
|
|
2763
|
-
typeof import_common38.Logger === "undefined" ? Object : import_common38.Logger
|
|
2764
|
-
])
|
|
2765
|
-
], LoggerService2);
|
|
2766
|
-
|
|
2767
|
-
// src/logger/middleware/correlation-id.middleware.ts
|
|
2768
|
-
var import_common39 = require("@nestjs/common");
|
|
2769
|
-
function _ts_decorate15(decorators, target, key, desc) {
|
|
2770
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
2771
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
2772
|
-
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;
|
|
2773
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
2774
|
-
}
|
|
2775
|
-
__name(_ts_decorate15, "_ts_decorate");
|
|
2776
|
-
function _ts_metadata9(k, v) {
|
|
2777
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
2778
|
-
}
|
|
2779
|
-
__name(_ts_metadata9, "_ts_metadata");
|
|
2780
|
-
var CorrelationIdMiddleware = class {
|
|
2781
|
-
static {
|
|
2782
|
-
__name(this, "CorrelationIdMiddleware");
|
|
2783
|
-
}
|
|
2784
|
-
includeInResponse;
|
|
2785
|
-
responseHeader;
|
|
2786
|
-
constructor(options = {}) {
|
|
2787
|
-
this.includeInResponse = options.includeInResponse ?? true;
|
|
2788
|
-
this.responseHeader = options.responseHeader ?? DEFAULT_CORRELATION_HEADER;
|
|
2789
|
-
}
|
|
2790
|
-
/**
|
|
2791
|
-
* Middleware handler for processing requests.
|
|
2792
|
-
*/
|
|
2793
|
-
use(req, reply, next) {
|
|
2794
|
-
const correlationId = generateCorrelationId();
|
|
2795
|
-
if (this.includeInResponse) {
|
|
2796
|
-
addCorrelationIdToResponse(reply, correlationId, this.responseHeader);
|
|
2797
|
-
}
|
|
2798
|
-
runWithCorrelationContext({
|
|
2799
|
-
correlationId
|
|
2800
|
-
}, () => {
|
|
2801
|
-
next();
|
|
2802
|
-
});
|
|
2803
|
-
}
|
|
2804
|
-
/**
|
|
2805
|
-
* Fastify hook handler for onRequest.
|
|
2806
|
-
* This is an async function that returns a Promise, ensuring the AsyncLocalStorage
|
|
2807
|
-
* context persists throughout the entire request lifecycle.
|
|
2808
|
-
*/
|
|
2809
|
-
async onRequest(req, reply) {
|
|
2810
|
-
const correlationId = generateCorrelationId();
|
|
2811
|
-
if (this.includeInResponse) {
|
|
2812
|
-
addCorrelationIdToResponse(reply, correlationId, this.responseHeader);
|
|
2813
|
-
}
|
|
2814
|
-
const store = correlationStorage.getStore();
|
|
2815
|
-
if (!store) {
|
|
2816
|
-
correlationStorage.enterWith({
|
|
2817
|
-
correlationId
|
|
2818
|
-
});
|
|
2819
|
-
}
|
|
2820
|
-
}
|
|
2821
|
-
};
|
|
2822
|
-
CorrelationIdMiddleware = _ts_decorate15([
|
|
2823
|
-
(0, import_common39.Injectable)(),
|
|
2824
|
-
_ts_metadata9("design:type", Function),
|
|
2825
|
-
_ts_metadata9("design:paramtypes", [
|
|
2826
|
-
typeof CorrelationIdMiddlewareOptions === "undefined" ? Object : CorrelationIdMiddlewareOptions
|
|
2827
|
-
])
|
|
2828
|
-
], CorrelationIdMiddleware);
|
|
2829
|
-
|
|
2830
|
-
// src/logger/interceptors/http-logger.interceptor.ts
|
|
2831
|
-
var import_common40 = require("@nestjs/common");
|
|
2832
|
-
var import_operators2 = require("rxjs/operators");
|
|
2833
|
-
function _ts_decorate16(decorators, target, key, desc) {
|
|
2834
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
2835
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
2836
|
-
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;
|
|
2837
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
2838
|
-
}
|
|
2839
|
-
__name(_ts_decorate16, "_ts_decorate");
|
|
2840
|
-
function _ts_metadata10(k, v) {
|
|
2841
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
2842
|
-
}
|
|
2843
|
-
__name(_ts_metadata10, "_ts_metadata");
|
|
2844
|
-
function _ts_param5(paramIndex, decorator) {
|
|
2845
|
-
return function(target, key) {
|
|
2846
|
-
decorator(target, key, paramIndex);
|
|
2847
|
-
};
|
|
2848
|
-
}
|
|
2849
|
-
__name(_ts_param5, "_ts_param");
|
|
2850
|
-
var HttpLoggerInterceptor = class {
|
|
2851
|
-
static {
|
|
2852
|
-
__name(this, "HttpLoggerInterceptor");
|
|
2853
|
-
}
|
|
2854
|
-
logger;
|
|
2855
|
-
enableRequestLog;
|
|
2856
|
-
enableResponseLog;
|
|
2857
|
-
slowRequestThreshold;
|
|
2858
|
-
constructor(logger, options) {
|
|
2859
|
-
this.logger = logger;
|
|
2860
|
-
this.enableRequestLog = options?.enableRequestLog ?? true;
|
|
2861
|
-
this.enableResponseLog = options?.enableResponseLog ?? true;
|
|
2862
|
-
this.slowRequestThreshold = options?.slowRequestThreshold ?? 3e3;
|
|
2863
|
-
}
|
|
2864
|
-
intercept(context, next) {
|
|
2865
|
-
if (context.getType() !== "http") {
|
|
2866
|
-
return next.handle();
|
|
2867
|
-
}
|
|
2868
|
-
const httpContext = context.switchToHttp();
|
|
2869
|
-
const request = httpContext.getRequest();
|
|
2870
|
-
const response = httpContext.getResponse();
|
|
2871
|
-
const startTime = Date.now();
|
|
2872
|
-
if (this.enableRequestLog) {
|
|
2873
|
-
this.logRequest(request);
|
|
2874
|
-
}
|
|
2875
|
-
return next.handle().pipe((0, import_operators2.tap)(() => {
|
|
2876
|
-
if (this.enableResponseLog) {
|
|
2877
|
-
const duration = Date.now() - startTime;
|
|
2878
|
-
this.logResponse(request, response, duration);
|
|
2879
|
-
}
|
|
2880
|
-
}), (0, import_operators2.catchError)((error) => {
|
|
2881
|
-
const duration = Date.now() - startTime;
|
|
2882
|
-
this.logError(request, response, duration, error);
|
|
2883
|
-
throw error;
|
|
2884
|
-
}));
|
|
2885
|
-
}
|
|
2886
|
-
logRequest(request) {
|
|
2887
|
-
try {
|
|
2888
|
-
const correlationContext = getCorrelationContext();
|
|
2889
|
-
const metadata = {
|
|
2890
|
-
type: "http_request",
|
|
2891
|
-
method: request.method,
|
|
2892
|
-
url: request.url,
|
|
2893
|
-
correlationId: correlationContext?.correlationId,
|
|
2894
|
-
ip: request.ip,
|
|
2895
|
-
userAgent: request.headers["user-agent"]
|
|
2896
|
-
};
|
|
2897
|
-
this.logger.logWithMetadata("log", `Incoming ${request.method} ${request.url}`, metadata);
|
|
2898
|
-
} catch (error) {
|
|
2899
|
-
this.logger.error("Failed to log HTTP request", error.stack);
|
|
2900
|
-
}
|
|
2901
|
-
}
|
|
2902
|
-
logResponse(request, response, duration) {
|
|
2903
|
-
try {
|
|
2904
|
-
const correlationContext = getCorrelationContext();
|
|
2905
|
-
const statusCode = response.statusCode;
|
|
2906
|
-
const logLevel = statusCode >= 500 ? "error" : statusCode >= 400 ? "warn" : "log";
|
|
2907
|
-
const metadata = {
|
|
2908
|
-
type: "http_response",
|
|
2909
|
-
method: request.method,
|
|
2910
|
-
url: request.url,
|
|
2911
|
-
statusCode,
|
|
2912
|
-
duration,
|
|
2913
|
-
correlationId: correlationContext?.correlationId
|
|
2914
|
-
};
|
|
2915
|
-
if (duration > this.slowRequestThreshold) {
|
|
2916
|
-
metadata.slowRequest = true;
|
|
2917
|
-
}
|
|
2918
|
-
const message = metadata.slowRequest ? `SLOW ${request.method} ${request.url} ${statusCode} - ${duration}ms` : `${request.method} ${request.url} ${statusCode} - ${duration}ms`;
|
|
2919
|
-
this.logger.logWithMetadata(logLevel, message, metadata);
|
|
2920
|
-
} catch (error) {
|
|
2921
|
-
this.logger.error("Failed to log HTTP response", error.stack);
|
|
2922
|
-
}
|
|
2923
|
-
}
|
|
2924
|
-
logError(request, response, duration, error) {
|
|
2925
|
-
try {
|
|
2926
|
-
const correlationContext = getCorrelationContext();
|
|
2927
|
-
const statusCode = response.statusCode || 500;
|
|
2928
|
-
const metadata = {
|
|
2929
|
-
type: "http_error",
|
|
2930
|
-
method: request.method,
|
|
2931
|
-
url: request.url,
|
|
2932
|
-
statusCode,
|
|
2933
|
-
duration,
|
|
2934
|
-
correlationId: correlationContext?.correlationId,
|
|
2935
|
-
errorName: error?.name || "Error",
|
|
2936
|
-
errorMessage: error?.message || "Unknown error"
|
|
2937
|
-
};
|
|
2938
|
-
if (error?.stack) {
|
|
2939
|
-
metadata.trace = error.stack;
|
|
2940
|
-
}
|
|
2941
|
-
if (error?.response) {
|
|
2942
|
-
metadata.errorDetails = error.response;
|
|
2943
|
-
}
|
|
2944
|
-
const message = `ERROR ${request.method} ${request.url} ${statusCode} - ${error?.message || "Unknown error"}`;
|
|
2945
|
-
this.logger.logWithMetadata("error", message, metadata);
|
|
2946
|
-
} catch (loggingError) {
|
|
2947
|
-
this.logger.error("Failed to log HTTP error", loggingError.stack);
|
|
2948
|
-
}
|
|
2949
|
-
}
|
|
2950
|
-
};
|
|
2951
|
-
HttpLoggerInterceptor = _ts_decorate16([
|
|
3004
|
+
LoggerService2 = _ts_decorate16([
|
|
2952
3005
|
(0, import_common40.Injectable)(),
|
|
3006
|
+
_ts_param5(0, (0, import_common40.Optional)()),
|
|
2953
3007
|
_ts_param5(1, (0, import_common40.Optional)()),
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
typeof
|
|
2957
|
-
typeof
|
|
3008
|
+
_ts_metadata9("design:type", Function),
|
|
3009
|
+
_ts_metadata9("design:paramtypes", [
|
|
3010
|
+
typeof LoggerModuleOptions === "undefined" ? Object : LoggerModuleOptions,
|
|
3011
|
+
typeof Logger === "undefined" ? Object : Logger
|
|
2958
3012
|
])
|
|
2959
|
-
],
|
|
3013
|
+
], LoggerService2);
|
|
2960
3014
|
|
|
2961
3015
|
// src/logger/logger.module.ts
|
|
2962
3016
|
function _ts_decorate17(decorators, target, key, desc) {
|
|
@@ -3229,7 +3283,7 @@ var LoggerModule = class _LoggerModule {
|
|
|
3229
3283
|
* ```
|
|
3230
3284
|
*/
|
|
3231
3285
|
static forRootAsync(options) {
|
|
3232
|
-
const asyncProviders =
|
|
3286
|
+
const asyncProviders = _LoggerModule.createAsyncProviders(options);
|
|
3233
3287
|
return {
|
|
3234
3288
|
module: _LoggerModule,
|
|
3235
3289
|
imports: options.imports || [],
|
|
@@ -3305,7 +3359,7 @@ var LoggerModule = class _LoggerModule {
|
|
|
3305
3359
|
* Configures middleware for the module.
|
|
3306
3360
|
* Middleware is registered globally in main.ts using Fastify hooks.
|
|
3307
3361
|
*/
|
|
3308
|
-
configure(
|
|
3362
|
+
configure(_consumer) {
|
|
3309
3363
|
}
|
|
3310
3364
|
/**
|
|
3311
3365
|
* Creates async providers for dynamic module configuration.
|
|
@@ -3313,11 +3367,11 @@ var LoggerModule = class _LoggerModule {
|
|
|
3313
3367
|
static createAsyncProviders(options) {
|
|
3314
3368
|
if (options.useFactory) {
|
|
3315
3369
|
return [
|
|
3316
|
-
|
|
3370
|
+
_LoggerModule.createAsyncOptionsProvider(options)
|
|
3317
3371
|
];
|
|
3318
3372
|
}
|
|
3319
3373
|
const providers = [
|
|
3320
|
-
|
|
3374
|
+
_LoggerModule.createAsyncOptionsProvider(options)
|
|
3321
3375
|
];
|
|
3322
3376
|
if (options.useClass) {
|
|
3323
3377
|
providers.push({
|
|
@@ -3335,7 +3389,7 @@ var LoggerModule = class _LoggerModule {
|
|
|
3335
3389
|
return {
|
|
3336
3390
|
provide: LOGGER_MODULE_OPTIONS,
|
|
3337
3391
|
useFactory: /* @__PURE__ */ __name(async (...args) => {
|
|
3338
|
-
const userOptions = await options.useFactory(...args);
|
|
3392
|
+
const userOptions = await options.useFactory?.(...args);
|
|
3339
3393
|
return mergeWithDefaults(userOptions);
|
|
3340
3394
|
}, "useFactory"),
|
|
3341
3395
|
inject: options.inject || []
|
|
@@ -3414,11 +3468,19 @@ LoggerModule = _ts_decorate17([
|
|
|
3414
3468
|
ValidationException,
|
|
3415
3469
|
VrittiAuthGuard,
|
|
3416
3470
|
addCorrelationIdToResponse,
|
|
3471
|
+
configureApiSdk,
|
|
3417
3472
|
correlationStorage,
|
|
3473
|
+
defineConfig,
|
|
3418
3474
|
generateCorrelationId,
|
|
3475
|
+
getConfig,
|
|
3419
3476
|
getCorrelationContext,
|
|
3420
3477
|
getHttpStatusTitle,
|
|
3478
|
+
getJwtExpiry,
|
|
3479
|
+
getRefreshCookieOptions,
|
|
3480
|
+
hashToken,
|
|
3481
|
+
resetConfig,
|
|
3421
3482
|
runWithCorrelationContext,
|
|
3422
|
-
updateCorrelationContext
|
|
3483
|
+
updateCorrelationContext,
|
|
3484
|
+
verifyTokenHash
|
|
3423
3485
|
});
|
|
3424
3486
|
//# sourceMappingURL=index.cjs.map
|