@vritti/api-sdk 0.1.2 → 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 +1271 -1289
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1025 -1028
- package/dist/index.d.ts +1025 -1028
- package/dist/index.js +1238 -1256
- package/dist/index.js.map +1 -1
- package/package.json +9 -15
package/dist/index.js
CHANGED
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
var __defProp = Object.defineProperty;
|
|
2
2
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
3
|
|
|
4
|
+
// src/auth/auth-config.module.ts
|
|
5
|
+
import { Global as Global2, Module as Module2 } from "@nestjs/common";
|
|
6
|
+
import { ConfigModule, ConfigService as ConfigService2 } from "@nestjs/config";
|
|
7
|
+
import { APP_GUARD } from "@nestjs/core";
|
|
8
|
+
import { JwtModule } from "@nestjs/jwt";
|
|
9
|
+
|
|
10
|
+
// src/request/request.module.ts
|
|
11
|
+
import { Global, Module } from "@nestjs/common";
|
|
12
|
+
|
|
13
|
+
// src/request/services/request.service.ts
|
|
14
|
+
import { Inject, Injectable, Scope } from "@nestjs/common";
|
|
15
|
+
import { REQUEST } from "@nestjs/core";
|
|
16
|
+
|
|
4
17
|
// src/config/index.ts
|
|
5
18
|
var defaultConfig = {
|
|
6
19
|
cookie: {
|
|
@@ -75,31 +88,7 @@ function getJwtExpiry() {
|
|
|
75
88
|
}
|
|
76
89
|
__name(getJwtExpiry, "getJwtExpiry");
|
|
77
90
|
|
|
78
|
-
// src/auth/utils/token-hash.util.ts
|
|
79
|
-
import * as crypto from "crypto";
|
|
80
|
-
function hashToken(token) {
|
|
81
|
-
return crypto.createHash("sha256").update(token).digest("hex");
|
|
82
|
-
}
|
|
83
|
-
__name(hashToken, "hashToken");
|
|
84
|
-
function verifyTokenHash(token, expectedHash) {
|
|
85
|
-
const computedHash = hashToken(token);
|
|
86
|
-
if (computedHash.length !== expectedHash.length) return false;
|
|
87
|
-
return crypto.timingSafeEqual(Buffer.from(computedHash, "hex"), Buffer.from(expectedHash, "hex"));
|
|
88
|
-
}
|
|
89
|
-
__name(verifyTokenHash, "verifyTokenHash");
|
|
90
|
-
|
|
91
|
-
// src/auth/auth-config.module.ts
|
|
92
|
-
import { Global as Global2, Module as Module2 } from "@nestjs/common";
|
|
93
|
-
import { ConfigModule, ConfigService as ConfigService2 } from "@nestjs/config";
|
|
94
|
-
import { APP_GUARD } from "@nestjs/core";
|
|
95
|
-
import { JwtModule } from "@nestjs/jwt";
|
|
96
|
-
|
|
97
|
-
// src/request/request.module.ts
|
|
98
|
-
import { Global, Module } from "@nestjs/common";
|
|
99
|
-
|
|
100
91
|
// src/request/services/request.service.ts
|
|
101
|
-
import { Inject, Injectable, Scope } from "@nestjs/common";
|
|
102
|
-
import { REQUEST } from "@nestjs/core";
|
|
103
92
|
function _ts_decorate(decorators, target, key, desc) {
|
|
104
93
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
105
94
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -117,7 +106,7 @@ function _ts_param(paramIndex, decorator) {
|
|
|
117
106
|
};
|
|
118
107
|
}
|
|
119
108
|
__name(_ts_param, "_ts_param");
|
|
120
|
-
var
|
|
109
|
+
var RequestService2 = class {
|
|
121
110
|
static {
|
|
122
111
|
__name(this, "RequestService");
|
|
123
112
|
}
|
|
@@ -166,7 +155,7 @@ var RequestService = class {
|
|
|
166
155
|
}
|
|
167
156
|
}
|
|
168
157
|
return null;
|
|
169
|
-
} catch (
|
|
158
|
+
} catch (_error) {
|
|
170
159
|
return null;
|
|
171
160
|
}
|
|
172
161
|
}
|
|
@@ -186,7 +175,7 @@ var RequestService = class {
|
|
|
186
175
|
return this.request.headers || {};
|
|
187
176
|
}
|
|
188
177
|
};
|
|
189
|
-
|
|
178
|
+
RequestService2 = _ts_decorate([
|
|
190
179
|
Injectable({
|
|
191
180
|
scope: Scope.REQUEST
|
|
192
181
|
}),
|
|
@@ -195,7 +184,7 @@ RequestService = _ts_decorate([
|
|
|
195
184
|
_ts_metadata("design:paramtypes", [
|
|
196
185
|
typeof FastifyRequest === "undefined" ? Object : FastifyRequest
|
|
197
186
|
])
|
|
198
|
-
],
|
|
187
|
+
], RequestService2);
|
|
199
188
|
|
|
200
189
|
// src/request/request.module.ts
|
|
201
190
|
function _ts_decorate2(decorators, target, key, desc) {
|
|
@@ -214,30 +203,31 @@ RequestModule = _ts_decorate2([
|
|
|
214
203
|
Global(),
|
|
215
204
|
Module({
|
|
216
205
|
providers: [
|
|
217
|
-
|
|
206
|
+
RequestService2
|
|
218
207
|
],
|
|
219
208
|
exports: [
|
|
220
|
-
|
|
209
|
+
RequestService2
|
|
221
210
|
]
|
|
222
211
|
})
|
|
223
212
|
], RequestModule);
|
|
224
213
|
|
|
225
214
|
// src/auth/guards/vritti-auth.guard.ts
|
|
226
|
-
import { Injectable as
|
|
227
|
-
import { ConfigService } from "@nestjs/config";
|
|
228
|
-
import { Reflector } from "@nestjs/core";
|
|
229
|
-
import { JwtService } from "@nestjs/jwt";
|
|
230
|
-
|
|
231
|
-
// src/database/services/primary-database.service.ts
|
|
232
|
-
import { Inject as Inject2, Injectable as Injectable2, InternalServerErrorException, Logger } from "@nestjs/common";
|
|
233
|
-
import { Pool } from "pg";
|
|
234
|
-
import { drizzle } from "drizzle-orm/node-postgres";
|
|
235
|
-
import { eq, or } from "drizzle-orm";
|
|
215
|
+
import { Injectable as Injectable2, Logger as Logger2, Scope as Scope2, UnauthorizedException } from "@nestjs/common";
|
|
236
216
|
|
|
237
|
-
// src/
|
|
238
|
-
|
|
217
|
+
// src/auth/utils/token-hash.util.ts
|
|
218
|
+
import * as crypto from "crypto";
|
|
219
|
+
function hashToken(token) {
|
|
220
|
+
return crypto.createHash("sha256").update(token).digest("hex");
|
|
221
|
+
}
|
|
222
|
+
__name(hashToken, "hashToken");
|
|
223
|
+
function verifyTokenHash(token, expectedHash) {
|
|
224
|
+
const computedHash = hashToken(token);
|
|
225
|
+
if (computedHash.length !== expectedHash.length) return false;
|
|
226
|
+
return crypto.timingSafeEqual(Buffer.from(computedHash, "hex"), Buffer.from(expectedHash, "hex"));
|
|
227
|
+
}
|
|
228
|
+
__name(verifyTokenHash, "verifyTokenHash");
|
|
239
229
|
|
|
240
|
-
// src/
|
|
230
|
+
// src/auth/guards/vritti-auth.guard.ts
|
|
241
231
|
function _ts_decorate3(decorators, target, key, desc) {
|
|
242
232
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
243
233
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -249,491 +239,658 @@ function _ts_metadata2(k, v) {
|
|
|
249
239
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
250
240
|
}
|
|
251
241
|
__name(_ts_metadata2, "_ts_metadata");
|
|
252
|
-
|
|
253
|
-
return function(target, key) {
|
|
254
|
-
decorator(target, key, paramIndex);
|
|
255
|
-
};
|
|
256
|
-
}
|
|
257
|
-
__name(_ts_param2, "_ts_param");
|
|
258
|
-
var PrimaryDatabaseService = class _PrimaryDatabaseService {
|
|
242
|
+
var VrittiAuthGuard = class _VrittiAuthGuard {
|
|
259
243
|
static {
|
|
260
|
-
__name(this, "
|
|
244
|
+
__name(this, "VrittiAuthGuard");
|
|
261
245
|
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
this.
|
|
274
|
-
this.cacheTTL = options.connectionCacheTTL || 3e5;
|
|
246
|
+
reflector;
|
|
247
|
+
_configService;
|
|
248
|
+
jwtService;
|
|
249
|
+
primaryDatabase;
|
|
250
|
+
requestService;
|
|
251
|
+
logger = new Logger2(_VrittiAuthGuard.name);
|
|
252
|
+
constructor(reflector, _configService, jwtService, primaryDatabase, requestService) {
|
|
253
|
+
this.reflector = reflector;
|
|
254
|
+
this._configService = _configService;
|
|
255
|
+
this.jwtService = jwtService;
|
|
256
|
+
this.primaryDatabase = primaryDatabase;
|
|
257
|
+
this.requestService = requestService;
|
|
275
258
|
}
|
|
276
|
-
async
|
|
277
|
-
|
|
278
|
-
|
|
259
|
+
async canActivate(context) {
|
|
260
|
+
const request = context.switchToHttp().getRequest();
|
|
261
|
+
const isPublic = this.reflector.getAllAndOverride("isPublic", [
|
|
262
|
+
context.getHandler(),
|
|
263
|
+
context.getClass()
|
|
264
|
+
]);
|
|
265
|
+
if (isPublic) {
|
|
266
|
+
this.logger.debug("Public endpoint detected, skipping authentication");
|
|
267
|
+
return true;
|
|
268
|
+
}
|
|
269
|
+
const isOnboarding = this.reflector.getAllAndOverride("isOnboarding", [
|
|
270
|
+
context.getHandler(),
|
|
271
|
+
context.getClass()
|
|
272
|
+
]);
|
|
273
|
+
try {
|
|
274
|
+
const accessToken = this.requestService.getAccessToken();
|
|
275
|
+
if (!accessToken) {
|
|
276
|
+
this.logger.warn("Access token not found in Authorization header");
|
|
277
|
+
throw new UnauthorizedException("Access token not found");
|
|
278
|
+
}
|
|
279
|
+
const decodedToken = this.jwtService.decode(accessToken);
|
|
280
|
+
if (!decodedToken) {
|
|
281
|
+
this.logger.warn("Failed to decode access token");
|
|
282
|
+
throw new UnauthorizedException("Invalid token format");
|
|
283
|
+
}
|
|
284
|
+
if (isOnboarding) {
|
|
285
|
+
if (decodedToken.type !== "onboarding") {
|
|
286
|
+
this.logger.warn("Onboarding endpoint requires onboarding token");
|
|
287
|
+
throw new UnauthorizedException("This endpoint requires an onboarding token");
|
|
288
|
+
}
|
|
289
|
+
const validatedToken2 = this.validateAccessToken(accessToken);
|
|
290
|
+
this.logger.debug("Onboarding token validated successfully");
|
|
291
|
+
this.validateRefreshTokenBinding(context, validatedToken2);
|
|
292
|
+
const userId2 = validatedToken2.userId;
|
|
293
|
+
request.user = {
|
|
294
|
+
id: userId2
|
|
295
|
+
};
|
|
296
|
+
return true;
|
|
297
|
+
}
|
|
298
|
+
if (decodedToken.type === "onboarding") {
|
|
299
|
+
this.logger.warn("Regular endpoint accessed with onboarding token");
|
|
300
|
+
throw new UnauthorizedException("Onboarding tokens cannot access this endpoint");
|
|
301
|
+
}
|
|
302
|
+
const validatedToken = this.validateAccessToken(accessToken);
|
|
303
|
+
this.logger.debug("Access token validated successfully");
|
|
304
|
+
this.validateRefreshTokenBinding(context, validatedToken);
|
|
305
|
+
const userId = validatedToken.userId;
|
|
306
|
+
request.user = {
|
|
307
|
+
id: userId
|
|
308
|
+
};
|
|
309
|
+
const tenantIdentifier = this.requestService.getTenantIdentifier();
|
|
310
|
+
if (!tenantIdentifier) {
|
|
311
|
+
this.logger.warn("Tenant identifier not found in request");
|
|
312
|
+
throw new UnauthorizedException("Tenant identifier not found");
|
|
313
|
+
}
|
|
314
|
+
this.logger.debug(`Tenant identifier extracted: ${tenantIdentifier}`);
|
|
315
|
+
if (tenantIdentifier === "cloud") {
|
|
316
|
+
this.logger.debug("Platform admin access detected, skipping tenant database validation");
|
|
317
|
+
return true;
|
|
318
|
+
}
|
|
319
|
+
const tenantInfo = await this.primaryDatabase.getTenantInfo(tenantIdentifier);
|
|
320
|
+
if (!tenantInfo) {
|
|
321
|
+
this.logger.warn(`Invalid tenant: ${tenantIdentifier}`);
|
|
322
|
+
throw new UnauthorizedException("Invalid tenant");
|
|
323
|
+
}
|
|
324
|
+
if (tenantInfo.status !== "ACTIVE") {
|
|
325
|
+
this.logger.warn(`Tenant ${tenantIdentifier} has status: ${tenantInfo.status}`);
|
|
326
|
+
throw new UnauthorizedException(`Tenant is ${tenantInfo.status}`);
|
|
327
|
+
}
|
|
328
|
+
this.logger.debug(`Tenant validated: ${tenantInfo.subdomain} (${tenantInfo.type})`);
|
|
329
|
+
return true;
|
|
330
|
+
} catch (error) {
|
|
331
|
+
if (error instanceof UnauthorizedException) {
|
|
332
|
+
throw error;
|
|
333
|
+
}
|
|
334
|
+
this.logger.error("Unexpected error in auth guard", error);
|
|
335
|
+
throw new UnauthorizedException("Authentication failed");
|
|
279
336
|
}
|
|
280
337
|
}
|
|
281
338
|
/**
|
|
282
|
-
*
|
|
339
|
+
* Validate access token with proper expiry checks
|
|
340
|
+
* Throws UnauthorizedException if token is invalid or expired
|
|
283
341
|
*/
|
|
284
|
-
|
|
342
|
+
validateAccessToken(token) {
|
|
285
343
|
try {
|
|
286
|
-
const
|
|
287
|
-
this.
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
schema: this.options.drizzleSchema,
|
|
296
|
-
relations: this.options.drizzleRelations
|
|
297
|
-
});
|
|
298
|
-
this.logger.debug(`Drizzle query keys after init: [${Object.keys(this.db.query || {}).join(", ")}]`);
|
|
299
|
-
await this.pool.query("SELECT 1");
|
|
300
|
-
this.logger.log("Connected to primary database (tenant registry)");
|
|
344
|
+
const decoded = this.jwtService.verify(token);
|
|
345
|
+
this.logger.debug(`Access token decoded for user: ${decoded.userId}`);
|
|
346
|
+
if (decoded.exp) {
|
|
347
|
+
const expiryTime = decoded.exp * 1e3;
|
|
348
|
+
const currentTime = Date.now();
|
|
349
|
+
const timeRemaining = expiryTime - currentTime;
|
|
350
|
+
this.logger.debug(`Access token valid for ${Math.floor(timeRemaining / 1e3)} more seconds`);
|
|
351
|
+
}
|
|
352
|
+
return decoded;
|
|
301
353
|
} catch (error) {
|
|
302
|
-
|
|
303
|
-
|
|
354
|
+
if (error instanceof UnauthorizedException) {
|
|
355
|
+
throw error;
|
|
356
|
+
}
|
|
357
|
+
const jwtError = error;
|
|
358
|
+
if (jwtError?.name === "TokenExpiredError") {
|
|
359
|
+
this.logger.warn(`Access token expired at: ${jwtError?.expiredAt}`);
|
|
360
|
+
throw new UnauthorizedException("Access token has expired");
|
|
361
|
+
}
|
|
362
|
+
if (jwtError?.name === "JsonWebTokenError") {
|
|
363
|
+
this.logger.warn(`Access token verification failed: ${jwtError?.message}`);
|
|
364
|
+
throw new UnauthorizedException("Invalid access token");
|
|
365
|
+
}
|
|
366
|
+
if (jwtError?.name === "NotBeforeError") {
|
|
367
|
+
this.logger.warn("Access token used before valid (nbf claim)");
|
|
368
|
+
throw new UnauthorizedException("Access token not yet valid");
|
|
369
|
+
}
|
|
370
|
+
this.logger.error("Unexpected error validating access token", error);
|
|
371
|
+
throw new UnauthorizedException("Access token validation failed");
|
|
304
372
|
}
|
|
305
373
|
}
|
|
306
374
|
/**
|
|
307
|
-
*
|
|
375
|
+
* Validate that the access token is bound to the refresh token in the cookie.
|
|
376
|
+
* This prevents token theft - a stolen access token is useless without the
|
|
377
|
+
* corresponding refresh token cookie.
|
|
378
|
+
*
|
|
379
|
+
* @param context - The execution context containing the request
|
|
380
|
+
* @param validatedToken - The decoded and validated JWT token
|
|
381
|
+
* @throws UnauthorizedException if token binding validation fails
|
|
308
382
|
*/
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
383
|
+
validateRefreshTokenBinding(context, validatedToken) {
|
|
384
|
+
const config = getConfig();
|
|
385
|
+
if (!config.jwt.validateTokenBinding) {
|
|
386
|
+
this.logger.debug("Token binding validation is disabled");
|
|
387
|
+
return;
|
|
312
388
|
}
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
if (schema) {
|
|
317
|
-
params.set("schema", schema);
|
|
389
|
+
if (!validatedToken.refreshTokenHash) {
|
|
390
|
+
this.logger.debug("Token does not contain refreshTokenHash, skipping binding validation");
|
|
391
|
+
return;
|
|
318
392
|
}
|
|
319
|
-
|
|
320
|
-
const
|
|
321
|
-
|
|
322
|
-
|
|
393
|
+
const request = context.switchToHttp().getRequest();
|
|
394
|
+
const cookies = request.cookies || {};
|
|
395
|
+
const refreshToken = cookies[config.cookie.refreshCookieName];
|
|
396
|
+
if (!refreshToken) {
|
|
397
|
+
this.logger.warn("Session validation failed - refresh token cookie not found");
|
|
398
|
+
throw new UnauthorizedException("Session validation failed");
|
|
323
399
|
}
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
/**
|
|
328
|
-
* Mask password in connection URL for logging
|
|
329
|
-
*/
|
|
330
|
-
maskPassword(url) {
|
|
331
|
-
return url.replace(/:([^@]+)@/, ":****@");
|
|
332
|
-
}
|
|
333
|
-
/**
|
|
334
|
-
* Get tenant configuration by identifier (ID or subdomain)
|
|
335
|
-
*
|
|
336
|
-
* @param tenantIdentifier Tenant ID or subdomain
|
|
337
|
-
* @returns Tenant configuration or null if not found
|
|
338
|
-
*/
|
|
339
|
-
async getTenantInfo(tenantIdentifier) {
|
|
340
|
-
const cached = this.tenantConfigCache.get(tenantIdentifier);
|
|
341
|
-
if (cached) {
|
|
342
|
-
this.logger.debug(`Cache hit for tenant: ${tenantIdentifier}`);
|
|
343
|
-
return cached;
|
|
344
|
-
}
|
|
345
|
-
try {
|
|
346
|
-
if (!this.db) {
|
|
347
|
-
throw new Error("Primary database client not initialized");
|
|
348
|
-
}
|
|
349
|
-
this.logger.debug(`Querying primary database for tenant: ${tenantIdentifier}`);
|
|
350
|
-
const schema = this.options.drizzleSchema;
|
|
351
|
-
const { tenants, tenantDatabaseConfigs } = schema;
|
|
352
|
-
const result = await this.db.select().from(tenants).leftJoin(tenantDatabaseConfigs, eq(tenants.id, tenantDatabaseConfigs.tenantId)).where(or(eq(tenants.id, tenantIdentifier), eq(tenants.subdomain, tenantIdentifier))).limit(1);
|
|
353
|
-
if (!result.length) {
|
|
354
|
-
this.logger.warn(`Tenant not found: ${tenantIdentifier}`);
|
|
355
|
-
return null;
|
|
356
|
-
}
|
|
357
|
-
const row = result[0];
|
|
358
|
-
const tenant = row.tenants;
|
|
359
|
-
const config = row.tenant_database_configs;
|
|
360
|
-
if (tenant.status !== "ACTIVE") {
|
|
361
|
-
this.logger.warn(`Tenant not active: ${tenantIdentifier}`);
|
|
362
|
-
return null;
|
|
363
|
-
}
|
|
364
|
-
const info = {
|
|
365
|
-
id: tenant.id,
|
|
366
|
-
subdomain: tenant.subdomain,
|
|
367
|
-
type: tenant.dbType,
|
|
368
|
-
status: tenant.status,
|
|
369
|
-
// For SHARED tenants: schema name
|
|
370
|
-
schemaName: config?.dbSchema || void 0,
|
|
371
|
-
// For DEDICATED tenants: database configuration from TenantDatabaseConfig table
|
|
372
|
-
databaseName: config?.dbName || void 0,
|
|
373
|
-
databaseHost: config?.dbHost || void 0,
|
|
374
|
-
databasePort: config?.dbPort || void 0,
|
|
375
|
-
databaseUsername: config?.dbUsername ? this.decrypt(config.dbUsername) : void 0,
|
|
376
|
-
databasePassword: config?.dbPassword ? this.decrypt(config.dbPassword) : void 0,
|
|
377
|
-
databaseSslMode: config?.dbSslMode || void 0,
|
|
378
|
-
connectionPoolSize: config?.connectionPoolSize || void 0
|
|
379
|
-
};
|
|
380
|
-
this.cacheInfo(info);
|
|
381
|
-
return info;
|
|
382
|
-
} catch (error) {
|
|
383
|
-
this.logger.error(`Failed to fetch tenant info: ${tenantIdentifier}`, error);
|
|
384
|
-
throw new InternalServerErrorException("Failed to resolve tenant");
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
/**
|
|
388
|
-
* Cache tenant information with TTL
|
|
389
|
-
*/
|
|
390
|
-
cacheInfo(info) {
|
|
391
|
-
this.tenantConfigCache.set(info.id, info);
|
|
392
|
-
this.tenantConfigCache.set(info.subdomain, info);
|
|
393
|
-
setTimeout(() => {
|
|
394
|
-
this.tenantConfigCache.delete(info.id);
|
|
395
|
-
this.tenantConfigCache.delete(info.subdomain);
|
|
396
|
-
this.logger.debug(`Cache expired for tenant: ${info.subdomain}`);
|
|
397
|
-
}, this.cacheTTL);
|
|
398
|
-
}
|
|
399
|
-
/**
|
|
400
|
-
* Clear cached tenant information
|
|
401
|
-
*
|
|
402
|
-
* Useful when tenant settings are updated and cache needs to be invalidated
|
|
403
|
-
*
|
|
404
|
-
* @param tenantIdentifier Tenant ID or subdomain
|
|
405
|
-
*/
|
|
406
|
-
clearTenantCache(tenantIdentifier) {
|
|
407
|
-
const config = this.tenantConfigCache.get(tenantIdentifier);
|
|
408
|
-
if (config) {
|
|
409
|
-
this.tenantConfigCache.delete(config.id);
|
|
410
|
-
this.tenantConfigCache.delete(config.subdomain);
|
|
411
|
-
this.logger.log(`Cleared cache for tenant: ${tenantIdentifier}`);
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
/**
|
|
415
|
-
* Clear all cached tenant configurations
|
|
416
|
-
*/
|
|
417
|
-
clearAllCaches() {
|
|
418
|
-
const size = this.tenantConfigCache.size;
|
|
419
|
-
this.tenantConfigCache.clear();
|
|
420
|
-
this.logger.log(`Cleared ${size} cached tenant configs`);
|
|
421
|
-
}
|
|
422
|
-
/**
|
|
423
|
-
* Get the Drizzle database instance for the primary database.
|
|
424
|
-
* This is a synchronous property that returns the initialized Drizzle client.
|
|
425
|
-
*
|
|
426
|
-
* @returns Primary database Drizzle instance
|
|
427
|
-
* @throws Error if primary database client is not initialized
|
|
428
|
-
*/
|
|
429
|
-
get drizzleClient() {
|
|
430
|
-
if (!this.db) {
|
|
431
|
-
throw new Error("Primary database client not initialized");
|
|
400
|
+
if (!verifyTokenHash(refreshToken, validatedToken.refreshTokenHash)) {
|
|
401
|
+
this.logger.warn("Session validation failed - token binding mismatch");
|
|
402
|
+
throw new UnauthorizedException("Session validation failed");
|
|
432
403
|
}
|
|
433
|
-
|
|
404
|
+
this.logger.debug("Token binding validated successfully");
|
|
434
405
|
}
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
406
|
+
};
|
|
407
|
+
VrittiAuthGuard = _ts_decorate3([
|
|
408
|
+
Injectable2({
|
|
409
|
+
scope: Scope2.REQUEST
|
|
410
|
+
}),
|
|
411
|
+
_ts_metadata2("design:type", Function),
|
|
412
|
+
_ts_metadata2("design:paramtypes", [
|
|
413
|
+
typeof Reflector === "undefined" ? Object : Reflector,
|
|
414
|
+
typeof ConfigService === "undefined" ? Object : ConfigService,
|
|
415
|
+
typeof JwtService === "undefined" ? Object : JwtService,
|
|
416
|
+
typeof PrimaryDatabaseService === "undefined" ? Object : PrimaryDatabaseService,
|
|
417
|
+
typeof RequestService === "undefined" ? Object : RequestService
|
|
418
|
+
])
|
|
419
|
+
], VrittiAuthGuard);
|
|
420
|
+
|
|
421
|
+
// src/auth/auth-config.module.ts
|
|
422
|
+
function _ts_decorate4(decorators, target, key, desc) {
|
|
423
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
424
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
425
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
426
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
427
|
+
}
|
|
428
|
+
__name(_ts_decorate4, "_ts_decorate");
|
|
429
|
+
var AuthConfigModule = class _AuthConfigModule {
|
|
430
|
+
static {
|
|
431
|
+
__name(this, "AuthConfigModule");
|
|
440
432
|
}
|
|
441
433
|
/**
|
|
442
|
-
*
|
|
434
|
+
* Register the auth module with async configuration
|
|
443
435
|
*
|
|
444
|
-
*
|
|
436
|
+
* This method:
|
|
437
|
+
* 1. Configures JwtModule with JWT_SECRET from ConfigService
|
|
438
|
+
* 2. Provides VrittiAuthGuard globally (applies to all routes)
|
|
439
|
+
* 3. Exports JwtModule for use in other modules (e.g., for signing tokens)
|
|
445
440
|
*
|
|
446
|
-
* @
|
|
447
|
-
* @returns Decrypted value
|
|
441
|
+
* @returns Dynamic module configuration
|
|
448
442
|
*/
|
|
449
|
-
|
|
450
|
-
return
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
443
|
+
static forRootAsync() {
|
|
444
|
+
return {
|
|
445
|
+
module: _AuthConfigModule,
|
|
446
|
+
imports: [
|
|
447
|
+
ConfigModule,
|
|
448
|
+
RequestModule,
|
|
449
|
+
JwtModule.registerAsync({
|
|
450
|
+
imports: [
|
|
451
|
+
ConfigModule
|
|
452
|
+
],
|
|
453
|
+
inject: [
|
|
454
|
+
ConfigService2
|
|
455
|
+
],
|
|
456
|
+
useFactory: /* @__PURE__ */ __name((config) => ({
|
|
457
|
+
secret: config.get("JWT_SECRET"),
|
|
458
|
+
signOptions: {
|
|
459
|
+
algorithm: "HS256"
|
|
460
|
+
}
|
|
461
|
+
}), "useFactory")
|
|
462
|
+
})
|
|
463
|
+
],
|
|
464
|
+
providers: [
|
|
465
|
+
{
|
|
466
|
+
provide: APP_GUARD,
|
|
467
|
+
useClass: VrittiAuthGuard
|
|
468
|
+
}
|
|
469
|
+
],
|
|
470
|
+
exports: [
|
|
471
|
+
JwtModule
|
|
472
|
+
]
|
|
473
|
+
};
|
|
457
474
|
}
|
|
458
475
|
};
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
_ts_metadata2("design:paramtypes", [
|
|
464
|
-
typeof DatabaseModuleOptions === "undefined" ? Object : DatabaseModuleOptions
|
|
465
|
-
])
|
|
466
|
-
], PrimaryDatabaseService);
|
|
476
|
+
AuthConfigModule = _ts_decorate4([
|
|
477
|
+
Global2(),
|
|
478
|
+
Module2({})
|
|
479
|
+
], AuthConfigModule);
|
|
467
480
|
|
|
468
|
-
// src/auth/
|
|
469
|
-
|
|
481
|
+
// src/auth/decorators/onboarding.decorator.ts
|
|
482
|
+
import { SetMetadata } from "@nestjs/common";
|
|
483
|
+
var Onboarding = /* @__PURE__ */ __name(() => SetMetadata("isOnboarding", true), "Onboarding");
|
|
484
|
+
|
|
485
|
+
// src/auth/decorators/public.decorator.ts
|
|
486
|
+
import { SetMetadata as SetMetadata2 } from "@nestjs/common";
|
|
487
|
+
var Public = /* @__PURE__ */ __name(() => SetMetadata2("isPublic", true), "Public");
|
|
488
|
+
|
|
489
|
+
// src/database/database.module.ts
|
|
490
|
+
import { Global as Global3, Module as Module3 } from "@nestjs/common";
|
|
491
|
+
import { APP_INTERCEPTOR } from "@nestjs/core";
|
|
492
|
+
|
|
493
|
+
// src/database/constants.ts
|
|
494
|
+
var DATABASE_MODULE_OPTIONS = Symbol("DATABASE_MODULE_OPTIONS");
|
|
495
|
+
|
|
496
|
+
// src/database/interceptors/message-tenant-context.interceptor.ts
|
|
497
|
+
import { Injectable as Injectable3, Logger as Logger3, Scope as Scope3 } from "@nestjs/common";
|
|
498
|
+
import { tap } from "rxjs/operators";
|
|
499
|
+
function _ts_decorate5(decorators, target, key, desc) {
|
|
470
500
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
471
501
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
472
502
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
473
503
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
474
504
|
}
|
|
475
|
-
__name(
|
|
505
|
+
__name(_ts_decorate5, "_ts_decorate");
|
|
476
506
|
function _ts_metadata3(k, v) {
|
|
477
507
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
478
508
|
}
|
|
479
509
|
__name(_ts_metadata3, "_ts_metadata");
|
|
480
|
-
var
|
|
510
|
+
var MessageTenantContextInterceptor = class _MessageTenantContextInterceptor {
|
|
481
511
|
static {
|
|
482
|
-
__name(this, "
|
|
512
|
+
__name(this, "MessageTenantContextInterceptor");
|
|
483
513
|
}
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
requestService;
|
|
489
|
-
logger = new Logger2(_VrittiAuthGuard.name);
|
|
490
|
-
constructor(reflector, configService, jwtService, primaryDatabase, requestService) {
|
|
491
|
-
this.reflector = reflector;
|
|
492
|
-
this.configService = configService;
|
|
493
|
-
this.jwtService = jwtService;
|
|
494
|
-
this.primaryDatabase = primaryDatabase;
|
|
495
|
-
this.requestService = requestService;
|
|
514
|
+
tenantContext;
|
|
515
|
+
logger = new Logger3(_MessageTenantContextInterceptor.name);
|
|
516
|
+
constructor(tenantContext) {
|
|
517
|
+
this.tenantContext = tenantContext;
|
|
496
518
|
}
|
|
497
|
-
|
|
498
|
-
const
|
|
499
|
-
|
|
500
|
-
context.
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
]);
|
|
511
|
-
try {
|
|
512
|
-
const accessToken = this.requestService.getAccessToken();
|
|
513
|
-
if (!accessToken) {
|
|
514
|
-
this.logger.warn("Access token not found in Authorization header");
|
|
515
|
-
throw new UnauthorizedException("Access token not found");
|
|
516
|
-
}
|
|
517
|
-
const decodedToken = this.jwtService.decode(accessToken);
|
|
518
|
-
if (!decodedToken) {
|
|
519
|
-
this.logger.warn("Failed to decode access token");
|
|
520
|
-
throw new UnauthorizedException("Invalid token format");
|
|
521
|
-
}
|
|
522
|
-
if (isOnboarding) {
|
|
523
|
-
if (decodedToken.type !== "onboarding") {
|
|
524
|
-
this.logger.warn("Onboarding endpoint requires onboarding token");
|
|
525
|
-
throw new UnauthorizedException("This endpoint requires an onboarding token");
|
|
519
|
+
intercept(context, next) {
|
|
520
|
+
const contextType = context.getType();
|
|
521
|
+
if (contextType === "rpc") {
|
|
522
|
+
const rpcContext = context.switchToRpc();
|
|
523
|
+
const payload = rpcContext.getData();
|
|
524
|
+
if (payload?.tenant) {
|
|
525
|
+
const tenant = payload.tenant;
|
|
526
|
+
this.logger.debug(`Setting tenant context from message: ${tenant.subdomain}`);
|
|
527
|
+
try {
|
|
528
|
+
this.tenantContext.setTenant(tenant);
|
|
529
|
+
this.logger.log(`Tenant context set: ${tenant.subdomain} (${tenant.type})`);
|
|
530
|
+
} catch (error) {
|
|
531
|
+
this.logger.error("Failed to set tenant context from message", error);
|
|
526
532
|
}
|
|
527
|
-
|
|
528
|
-
this.logger.
|
|
529
|
-
this.validateRefreshTokenBinding(context, validatedToken2);
|
|
530
|
-
const userId2 = validatedToken2.userId;
|
|
531
|
-
request.user = {
|
|
532
|
-
id: userId2
|
|
533
|
-
};
|
|
534
|
-
return true;
|
|
535
|
-
}
|
|
536
|
-
if (decodedToken.type === "onboarding") {
|
|
537
|
-
this.logger.warn("Regular endpoint accessed with onboarding token");
|
|
538
|
-
throw new UnauthorizedException("Onboarding tokens cannot access this endpoint");
|
|
533
|
+
} else {
|
|
534
|
+
this.logger.warn("Message payload missing tenant information");
|
|
539
535
|
}
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
536
|
+
}
|
|
537
|
+
return next.handle().pipe(tap({
|
|
538
|
+
next: /* @__PURE__ */ __name(() => {
|
|
539
|
+
this.cleanupContext();
|
|
540
|
+
}, "next"),
|
|
541
|
+
error: /* @__PURE__ */ __name(() => {
|
|
542
|
+
this.cleanupContext();
|
|
543
|
+
}, "error"),
|
|
544
|
+
complete: /* @__PURE__ */ __name(() => {
|
|
545
|
+
this.cleanupContext();
|
|
546
|
+
}, "complete")
|
|
547
|
+
}));
|
|
548
|
+
}
|
|
549
|
+
/**
|
|
550
|
+
* Clean up tenant context after message is processed
|
|
551
|
+
*/
|
|
552
|
+
cleanupContext() {
|
|
553
|
+
if (this.tenantContext.hasTenant()) {
|
|
554
|
+
const tenant = this.tenantContext.getTenantIdSafe();
|
|
555
|
+
this.tenantContext.clearTenant();
|
|
556
|
+
this.logger.debug(`Cleaned up tenant context: ${tenant}`);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
};
|
|
560
|
+
MessageTenantContextInterceptor = _ts_decorate5([
|
|
561
|
+
Injectable3({
|
|
562
|
+
scope: Scope3.REQUEST
|
|
563
|
+
}),
|
|
564
|
+
_ts_metadata3("design:type", Function),
|
|
565
|
+
_ts_metadata3("design:paramtypes", [
|
|
566
|
+
typeof TenantContextService === "undefined" ? Object : TenantContextService
|
|
567
|
+
])
|
|
568
|
+
], MessageTenantContextInterceptor);
|
|
569
|
+
|
|
570
|
+
// src/database/interceptors/tenant-context.interceptor.ts
|
|
571
|
+
import { Injectable as Injectable4, Logger as Logger4, Scope as Scope4, UnauthorizedException as UnauthorizedException2 } from "@nestjs/common";
|
|
572
|
+
function _ts_decorate6(decorators, target, key, desc) {
|
|
573
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
574
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
575
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
576
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
577
|
+
}
|
|
578
|
+
__name(_ts_decorate6, "_ts_decorate");
|
|
579
|
+
function _ts_metadata4(k, v) {
|
|
580
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
581
|
+
}
|
|
582
|
+
__name(_ts_metadata4, "_ts_metadata");
|
|
583
|
+
var TenantContextInterceptor = class _TenantContextInterceptor {
|
|
584
|
+
static {
|
|
585
|
+
__name(this, "TenantContextInterceptor");
|
|
586
|
+
}
|
|
587
|
+
reflector;
|
|
588
|
+
tenantContext;
|
|
589
|
+
primaryDatabase;
|
|
590
|
+
requestService;
|
|
591
|
+
logger = new Logger4(_TenantContextInterceptor.name);
|
|
592
|
+
constructor(reflector, tenantContext, primaryDatabase, requestService) {
|
|
593
|
+
this.reflector = reflector;
|
|
594
|
+
this.tenantContext = tenantContext;
|
|
595
|
+
this.primaryDatabase = primaryDatabase;
|
|
596
|
+
this.requestService = requestService;
|
|
597
|
+
}
|
|
598
|
+
async intercept(context, next) {
|
|
599
|
+
const request = context.switchToHttp().getRequest();
|
|
600
|
+
this.logger.debug(`Processing request: ${request.method} ${request.url}`);
|
|
601
|
+
const isPublic = this.reflector.getAllAndOverride("isPublic", [
|
|
602
|
+
context.getHandler(),
|
|
603
|
+
context.getClass()
|
|
604
|
+
]);
|
|
605
|
+
try {
|
|
547
606
|
const tenantIdentifier = this.requestService.getTenantIdentifier();
|
|
607
|
+
if (isPublic && !tenantIdentifier) {
|
|
608
|
+
this.logger.debug("Public endpoint without tenant identifier, skipping tenant context setup");
|
|
609
|
+
return next.handle();
|
|
610
|
+
}
|
|
548
611
|
if (!tenantIdentifier) {
|
|
549
|
-
|
|
550
|
-
throw new UnauthorizedException("Tenant identifier not found");
|
|
612
|
+
throw new UnauthorizedException2("Tenant identifier not found in request");
|
|
551
613
|
}
|
|
552
614
|
this.logger.debug(`Tenant identifier extracted: ${tenantIdentifier}`);
|
|
553
615
|
if (tenantIdentifier === "cloud") {
|
|
554
|
-
this.logger.
|
|
555
|
-
return
|
|
616
|
+
this.logger.log("Cloud platform access detected, skipping tenant context setup");
|
|
617
|
+
return next.handle();
|
|
556
618
|
}
|
|
557
619
|
const tenantInfo = await this.primaryDatabase.getTenantInfo(tenantIdentifier);
|
|
558
620
|
if (!tenantInfo) {
|
|
559
621
|
this.logger.warn(`Invalid tenant: ${tenantIdentifier}`);
|
|
560
|
-
throw new
|
|
622
|
+
throw new UnauthorizedException2("Invalid tenant");
|
|
561
623
|
}
|
|
562
624
|
if (tenantInfo.status !== "ACTIVE") {
|
|
563
625
|
this.logger.warn(`Tenant ${tenantIdentifier} has status: ${tenantInfo.status}`);
|
|
564
|
-
throw new
|
|
565
|
-
}
|
|
566
|
-
this.logger.debug(`Tenant validated: ${tenantInfo.subdomain} (${tenantInfo.type})`);
|
|
567
|
-
return true;
|
|
568
|
-
} catch (error) {
|
|
569
|
-
if (error instanceof UnauthorizedException) {
|
|
570
|
-
throw error;
|
|
571
|
-
}
|
|
572
|
-
this.logger.error("Unexpected error in auth guard", error);
|
|
573
|
-
throw new UnauthorizedException("Authentication failed");
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
/**
|
|
577
|
-
* Validate access token with proper expiry checks
|
|
578
|
-
* Throws UnauthorizedException if token is invalid or expired
|
|
579
|
-
*/
|
|
580
|
-
validateAccessToken(token) {
|
|
581
|
-
try {
|
|
582
|
-
const decoded = this.jwtService.verify(token);
|
|
583
|
-
this.logger.debug(`Access token decoded for user: ${decoded.userId}`);
|
|
584
|
-
if (decoded.exp) {
|
|
585
|
-
const expiryTime = decoded.exp * 1e3;
|
|
586
|
-
const currentTime = Date.now();
|
|
587
|
-
const timeRemaining = expiryTime - currentTime;
|
|
588
|
-
this.logger.debug(`Access token valid for ${Math.floor(timeRemaining / 1e3)} more seconds`);
|
|
626
|
+
throw new UnauthorizedException2(`Tenant is ${tenantInfo.status}`);
|
|
589
627
|
}
|
|
590
|
-
|
|
628
|
+
this.logger.debug(`Tenant config loaded: ${tenantInfo.subdomain} (${tenantInfo.type})`);
|
|
629
|
+
this.tenantContext.setTenant(tenantInfo);
|
|
630
|
+
request.tenant = tenantInfo;
|
|
631
|
+
this.logger.log(`Tenant context set: ${tenantInfo.subdomain}`);
|
|
591
632
|
} catch (error) {
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
}
|
|
595
|
-
const jwtError = error;
|
|
596
|
-
if (jwtError?.name === "TokenExpiredError") {
|
|
597
|
-
this.logger.warn(`Access token expired at: ${jwtError?.expiredAt}`);
|
|
598
|
-
throw new UnauthorizedException("Access token has expired");
|
|
599
|
-
}
|
|
600
|
-
if (jwtError?.name === "JsonWebTokenError") {
|
|
601
|
-
this.logger.warn(`Access token verification failed: ${jwtError?.message}`);
|
|
602
|
-
throw new UnauthorizedException("Invalid access token");
|
|
603
|
-
}
|
|
604
|
-
if (jwtError?.name === "NotBeforeError") {
|
|
605
|
-
this.logger.warn("Access token used before valid (nbf claim)");
|
|
606
|
-
throw new UnauthorizedException("Access token not yet valid");
|
|
607
|
-
}
|
|
608
|
-
this.logger.error("Unexpected error validating access token", error);
|
|
609
|
-
throw new UnauthorizedException("Access token validation failed");
|
|
610
|
-
}
|
|
611
|
-
}
|
|
612
|
-
/**
|
|
613
|
-
* Validate that the access token is bound to the refresh token in the cookie.
|
|
614
|
-
* This prevents token theft - a stolen access token is useless without the
|
|
615
|
-
* corresponding refresh token cookie.
|
|
616
|
-
*
|
|
617
|
-
* @param context - The execution context containing the request
|
|
618
|
-
* @param validatedToken - The decoded and validated JWT token
|
|
619
|
-
* @throws UnauthorizedException if token binding validation fails
|
|
620
|
-
*/
|
|
621
|
-
validateRefreshTokenBinding(context, validatedToken) {
|
|
622
|
-
const config = getConfig();
|
|
623
|
-
if (!config.jwt.validateTokenBinding) {
|
|
624
|
-
this.logger.debug("Token binding validation is disabled");
|
|
625
|
-
return;
|
|
626
|
-
}
|
|
627
|
-
if (!validatedToken.refreshTokenHash) {
|
|
628
|
-
this.logger.debug("Token does not contain refreshTokenHash, skipping binding validation");
|
|
629
|
-
return;
|
|
630
|
-
}
|
|
631
|
-
const request = context.switchToHttp().getRequest();
|
|
632
|
-
const cookies = request.cookies || {};
|
|
633
|
-
const refreshToken = cookies[config.cookie.refreshCookieName];
|
|
634
|
-
if (!refreshToken) {
|
|
635
|
-
this.logger.warn("Session validation failed - refresh token cookie not found");
|
|
636
|
-
throw new UnauthorizedException("Session validation failed");
|
|
637
|
-
}
|
|
638
|
-
if (!verifyTokenHash(refreshToken, validatedToken.refreshTokenHash)) {
|
|
639
|
-
this.logger.warn("Session validation failed - token binding mismatch");
|
|
640
|
-
throw new UnauthorizedException("Session validation failed");
|
|
633
|
+
this.logger.error("Failed to set tenant context", error);
|
|
634
|
+
throw error;
|
|
641
635
|
}
|
|
642
|
-
|
|
636
|
+
return next.handle();
|
|
643
637
|
}
|
|
644
638
|
};
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
scope:
|
|
639
|
+
TenantContextInterceptor = _ts_decorate6([
|
|
640
|
+
Injectable4({
|
|
641
|
+
scope: Scope4.REQUEST
|
|
648
642
|
}),
|
|
649
|
-
|
|
650
|
-
|
|
643
|
+
_ts_metadata4("design:type", Function),
|
|
644
|
+
_ts_metadata4("design:paramtypes", [
|
|
651
645
|
typeof Reflector === "undefined" ? Object : Reflector,
|
|
652
|
-
typeof
|
|
653
|
-
typeof JwtService === "undefined" ? Object : JwtService,
|
|
646
|
+
typeof TenantContextService === "undefined" ? Object : TenantContextService,
|
|
654
647
|
typeof PrimaryDatabaseService === "undefined" ? Object : PrimaryDatabaseService,
|
|
655
648
|
typeof RequestService === "undefined" ? Object : RequestService
|
|
656
649
|
])
|
|
657
|
-
],
|
|
650
|
+
], TenantContextInterceptor);
|
|
658
651
|
|
|
659
|
-
// src/
|
|
660
|
-
|
|
652
|
+
// src/database/services/primary-database.service.ts
|
|
653
|
+
import { Inject as Inject2, Injectable as Injectable5, InternalServerErrorException, Logger as Logger5 } from "@nestjs/common";
|
|
654
|
+
import { eq, or } from "drizzle-orm";
|
|
655
|
+
import { drizzle } from "drizzle-orm/node-postgres";
|
|
656
|
+
import { Pool } from "pg";
|
|
657
|
+
function _ts_decorate7(decorators, target, key, desc) {
|
|
661
658
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
662
659
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
663
660
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
664
661
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
665
662
|
}
|
|
666
|
-
__name(
|
|
667
|
-
|
|
663
|
+
__name(_ts_decorate7, "_ts_decorate");
|
|
664
|
+
function _ts_metadata5(k, v) {
|
|
665
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
666
|
+
}
|
|
667
|
+
__name(_ts_metadata5, "_ts_metadata");
|
|
668
|
+
function _ts_param2(paramIndex, decorator) {
|
|
669
|
+
return function(target, key) {
|
|
670
|
+
decorator(target, key, paramIndex);
|
|
671
|
+
};
|
|
672
|
+
}
|
|
673
|
+
__name(_ts_param2, "_ts_param");
|
|
674
|
+
var PrimaryDatabaseService2 = class _PrimaryDatabaseService {
|
|
668
675
|
static {
|
|
669
|
-
__name(this, "
|
|
676
|
+
__name(this, "PrimaryDatabaseService");
|
|
677
|
+
}
|
|
678
|
+
options;
|
|
679
|
+
logger = new Logger5(_PrimaryDatabaseService.name);
|
|
680
|
+
/** PostgreSQL connection pool */
|
|
681
|
+
pool = null;
|
|
682
|
+
/** Drizzle database instance */
|
|
683
|
+
db = null;
|
|
684
|
+
/** In-memory cache: Map<tenantIdentifier, TenantConfig> */
|
|
685
|
+
tenantConfigCache = /* @__PURE__ */ new Map();
|
|
686
|
+
/** Cache TTL in milliseconds */
|
|
687
|
+
cacheTTL;
|
|
688
|
+
constructor(options) {
|
|
689
|
+
this.options = options;
|
|
690
|
+
this.cacheTTL = options.connectionCacheTTL || 3e5;
|
|
691
|
+
}
|
|
692
|
+
async onModuleInit() {
|
|
693
|
+
if (this.options.primaryDb) {
|
|
694
|
+
await this.initializeDrizzleClient();
|
|
695
|
+
}
|
|
670
696
|
}
|
|
671
697
|
/**
|
|
672
|
-
*
|
|
698
|
+
* Initialize connection to primary database using Drizzle
|
|
699
|
+
*/
|
|
700
|
+
async initializeDrizzleClient() {
|
|
701
|
+
try {
|
|
702
|
+
const databaseUrl = this.buildPrimaryDbUrl();
|
|
703
|
+
this.pool = new Pool({
|
|
704
|
+
connectionString: databaseUrl,
|
|
705
|
+
max: this.options.maxConnections || 10
|
|
706
|
+
});
|
|
707
|
+
this.logger.debug(`Schema keys passed to drizzle: [${Object.keys(this.options.drizzleSchema || {}).join(", ")}]`);
|
|
708
|
+
this.logger.debug(`Relations keys passed to drizzle: [${Object.keys(this.options.drizzleRelations || {}).join(", ")}]`);
|
|
709
|
+
this.db = drizzle({
|
|
710
|
+
client: this.pool,
|
|
711
|
+
schema: this.options.drizzleSchema,
|
|
712
|
+
relations: this.options.drizzleRelations
|
|
713
|
+
});
|
|
714
|
+
this.logger.debug(`Drizzle query keys after init: [${Object.keys(this.db.query || {}).join(", ")}]`);
|
|
715
|
+
await this.pool.query("SELECT 1");
|
|
716
|
+
this.logger.log("Connected to primary database (tenant registry)");
|
|
717
|
+
} catch (error) {
|
|
718
|
+
this.logger.error("Failed to connect to primary database", error);
|
|
719
|
+
throw new InternalServerErrorException("Failed to initialize tenant registry");
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
/**
|
|
723
|
+
* Build connection URL from primary database properties
|
|
724
|
+
*/
|
|
725
|
+
buildPrimaryDbUrl() {
|
|
726
|
+
if (!this.options.primaryDb) {
|
|
727
|
+
throw new Error("Primary database configuration not provided");
|
|
728
|
+
}
|
|
729
|
+
const { host, port = 5432, username, password, database, schema = "public", sslMode = "require" } = this.options.primaryDb;
|
|
730
|
+
let url = `postgresql://${username}:${encodeURIComponent(password)}@${host}:${port}/${database}`;
|
|
731
|
+
const params = new URLSearchParams();
|
|
732
|
+
if (schema) {
|
|
733
|
+
params.set("schema", schema);
|
|
734
|
+
}
|
|
735
|
+
params.set("sslmode", sslMode);
|
|
736
|
+
const queryString = params.toString();
|
|
737
|
+
if (queryString) {
|
|
738
|
+
url += `?${queryString}`;
|
|
739
|
+
}
|
|
740
|
+
this.logger.debug(`Primary DB connection URL: ${this.maskPassword(url)}`);
|
|
741
|
+
return url;
|
|
742
|
+
}
|
|
743
|
+
/**
|
|
744
|
+
* Mask password in connection URL for logging
|
|
745
|
+
*/
|
|
746
|
+
maskPassword(url) {
|
|
747
|
+
return url.replace(/:([^@]+)@/, ":****@");
|
|
748
|
+
}
|
|
749
|
+
/**
|
|
750
|
+
* Get tenant configuration by identifier (ID or subdomain)
|
|
673
751
|
*
|
|
674
|
-
*
|
|
675
|
-
*
|
|
676
|
-
|
|
677
|
-
|
|
752
|
+
* @param tenantIdentifier Tenant ID or subdomain
|
|
753
|
+
* @returns Tenant configuration or null if not found
|
|
754
|
+
*/
|
|
755
|
+
async getTenantInfo(tenantIdentifier) {
|
|
756
|
+
const cached = this.tenantConfigCache.get(tenantIdentifier);
|
|
757
|
+
if (cached) {
|
|
758
|
+
this.logger.debug(`Cache hit for tenant: ${tenantIdentifier}`);
|
|
759
|
+
return cached;
|
|
760
|
+
}
|
|
761
|
+
try {
|
|
762
|
+
if (!this.db) {
|
|
763
|
+
throw new Error("Primary database client not initialized");
|
|
764
|
+
}
|
|
765
|
+
this.logger.debug(`Querying primary database for tenant: ${tenantIdentifier}`);
|
|
766
|
+
const schema = this.options.drizzleSchema;
|
|
767
|
+
const { tenants, tenantDatabaseConfigs } = schema;
|
|
768
|
+
const result = await this.db.select().from(tenants).leftJoin(tenantDatabaseConfigs, eq(tenants.id, tenantDatabaseConfigs.tenantId)).where(or(eq(tenants.id, tenantIdentifier), eq(tenants.subdomain, tenantIdentifier))).limit(1);
|
|
769
|
+
if (!result.length) {
|
|
770
|
+
this.logger.warn(`Tenant not found: ${tenantIdentifier}`);
|
|
771
|
+
return null;
|
|
772
|
+
}
|
|
773
|
+
const row = result[0];
|
|
774
|
+
const tenant = row.tenants;
|
|
775
|
+
const config = row.tenant_database_configs;
|
|
776
|
+
if (tenant.status !== "ACTIVE") {
|
|
777
|
+
this.logger.warn(`Tenant not active: ${tenantIdentifier}`);
|
|
778
|
+
return null;
|
|
779
|
+
}
|
|
780
|
+
const info = {
|
|
781
|
+
id: tenant.id,
|
|
782
|
+
subdomain: tenant.subdomain,
|
|
783
|
+
type: tenant.dbType,
|
|
784
|
+
status: tenant.status,
|
|
785
|
+
// For SHARED tenants: schema name
|
|
786
|
+
schemaName: config?.dbSchema || void 0,
|
|
787
|
+
// For DEDICATED tenants: database configuration from TenantDatabaseConfig table
|
|
788
|
+
databaseName: config?.dbName || void 0,
|
|
789
|
+
databaseHost: config?.dbHost || void 0,
|
|
790
|
+
databasePort: config?.dbPort || void 0,
|
|
791
|
+
databaseUsername: config?.dbUsername ? this.decrypt(config.dbUsername) : void 0,
|
|
792
|
+
databasePassword: config?.dbPassword ? this.decrypt(config.dbPassword) : void 0,
|
|
793
|
+
databaseSslMode: config?.dbSslMode || void 0,
|
|
794
|
+
connectionPoolSize: config?.connectionPoolSize || void 0
|
|
795
|
+
};
|
|
796
|
+
this.cacheInfo(info);
|
|
797
|
+
return info;
|
|
798
|
+
} catch (error) {
|
|
799
|
+
this.logger.error(`Failed to fetch tenant info: ${tenantIdentifier}`, error);
|
|
800
|
+
throw new InternalServerErrorException("Failed to resolve tenant");
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
/**
|
|
804
|
+
* Cache tenant information with TTL
|
|
805
|
+
*/
|
|
806
|
+
cacheInfo(info) {
|
|
807
|
+
this.tenantConfigCache.set(info.id, info);
|
|
808
|
+
this.tenantConfigCache.set(info.subdomain, info);
|
|
809
|
+
setTimeout(() => {
|
|
810
|
+
this.tenantConfigCache.delete(info.id);
|
|
811
|
+
this.tenantConfigCache.delete(info.subdomain);
|
|
812
|
+
this.logger.debug(`Cache expired for tenant: ${info.subdomain}`);
|
|
813
|
+
}, this.cacheTTL);
|
|
814
|
+
}
|
|
815
|
+
/**
|
|
816
|
+
* Clear cached tenant information
|
|
678
817
|
*
|
|
679
|
-
*
|
|
818
|
+
* Useful when tenant settings are updated and cache needs to be invalidated
|
|
819
|
+
*
|
|
820
|
+
* @param tenantIdentifier Tenant ID or subdomain
|
|
680
821
|
*/
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
822
|
+
clearTenantCache(tenantIdentifier) {
|
|
823
|
+
const config = this.tenantConfigCache.get(tenantIdentifier);
|
|
824
|
+
if (config) {
|
|
825
|
+
this.tenantConfigCache.delete(config.id);
|
|
826
|
+
this.tenantConfigCache.delete(config.subdomain);
|
|
827
|
+
this.logger.log(`Cleared cache for tenant: ${tenantIdentifier}`);
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
/**
|
|
831
|
+
* Clear all cached tenant configurations
|
|
832
|
+
*/
|
|
833
|
+
clearAllCaches() {
|
|
834
|
+
const size = this.tenantConfigCache.size;
|
|
835
|
+
this.tenantConfigCache.clear();
|
|
836
|
+
this.logger.log(`Cleared ${size} cached tenant configs`);
|
|
837
|
+
}
|
|
838
|
+
/**
|
|
839
|
+
* Get the Drizzle database instance for the primary database.
|
|
840
|
+
* This is a synchronous property that returns the initialized Drizzle client.
|
|
841
|
+
*
|
|
842
|
+
* @returns Primary database Drizzle instance
|
|
843
|
+
* @throws Error if primary database client is not initialized
|
|
844
|
+
*/
|
|
845
|
+
get drizzleClient() {
|
|
846
|
+
if (!this.db) {
|
|
847
|
+
throw new Error("Primary database client not initialized");
|
|
848
|
+
}
|
|
849
|
+
return this.db;
|
|
850
|
+
}
|
|
851
|
+
/**
|
|
852
|
+
* Get the Drizzle schema
|
|
853
|
+
*/
|
|
854
|
+
get schema() {
|
|
855
|
+
return this.options.drizzleSchema;
|
|
856
|
+
}
|
|
857
|
+
/**
|
|
858
|
+
* Decrypt database credentials
|
|
859
|
+
*
|
|
860
|
+
* Override this method to implement your encryption strategy
|
|
861
|
+
*
|
|
862
|
+
* @param encrypted Encrypted value
|
|
863
|
+
* @returns Decrypted value
|
|
864
|
+
*/
|
|
865
|
+
decrypt(encrypted) {
|
|
866
|
+
return encrypted;
|
|
867
|
+
}
|
|
868
|
+
async onModuleDestroy() {
|
|
869
|
+
if (this.pool) {
|
|
870
|
+
await this.pool.end();
|
|
871
|
+
this.logger.log("Disconnected from primary database");
|
|
872
|
+
}
|
|
712
873
|
}
|
|
713
874
|
};
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
// src/database/interceptors/message-tenant-context.interceptor.ts
|
|
724
|
-
import { Injectable as Injectable5, Logger as Logger3, Scope as Scope4 } from "@nestjs/common";
|
|
725
|
-
import { tap } from "rxjs/operators";
|
|
875
|
+
PrimaryDatabaseService2 = _ts_decorate7([
|
|
876
|
+
Injectable5(),
|
|
877
|
+
_ts_param2(0, Inject2(DATABASE_MODULE_OPTIONS)),
|
|
878
|
+
_ts_metadata5("design:type", Function),
|
|
879
|
+
_ts_metadata5("design:paramtypes", [
|
|
880
|
+
typeof DatabaseModuleOptions === "undefined" ? Object : DatabaseModuleOptions
|
|
881
|
+
])
|
|
882
|
+
], PrimaryDatabaseService2);
|
|
726
883
|
|
|
727
884
|
// src/database/services/tenant-context.service.ts
|
|
728
|
-
import { Injectable as
|
|
729
|
-
function
|
|
885
|
+
import { Injectable as Injectable6, Scope as Scope5, UnauthorizedException as UnauthorizedException3 } from "@nestjs/common";
|
|
886
|
+
function _ts_decorate8(decorators, target, key, desc) {
|
|
730
887
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
731
888
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
732
889
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
733
890
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
734
891
|
}
|
|
735
|
-
__name(
|
|
736
|
-
var
|
|
892
|
+
__name(_ts_decorate8, "_ts_decorate");
|
|
893
|
+
var TenantContextService2 = class {
|
|
737
894
|
static {
|
|
738
895
|
__name(this, "TenantContextService");
|
|
739
896
|
}
|
|
@@ -763,7 +920,7 @@ var TenantContextService = class {
|
|
|
763
920
|
*/
|
|
764
921
|
getTenant() {
|
|
765
922
|
if (!this.tenantInfo) {
|
|
766
|
-
throw new
|
|
923
|
+
throw new UnauthorizedException3("Tenant context not set");
|
|
767
924
|
}
|
|
768
925
|
return this.tenantInfo;
|
|
769
926
|
}
|
|
@@ -804,195 +961,40 @@ var TenantContextService = class {
|
|
|
804
961
|
return this.tenantInfo?.subdomain ?? null;
|
|
805
962
|
}
|
|
806
963
|
};
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
scope:
|
|
964
|
+
TenantContextService2 = _ts_decorate8([
|
|
965
|
+
Injectable6({
|
|
966
|
+
scope: Scope5.REQUEST
|
|
810
967
|
})
|
|
811
|
-
],
|
|
968
|
+
], TenantContextService2);
|
|
812
969
|
|
|
813
|
-
// src/database/
|
|
814
|
-
|
|
970
|
+
// src/database/services/tenant-database.service.ts
|
|
971
|
+
import { Inject as Inject3, Injectable as Injectable7, InternalServerErrorException as InternalServerErrorException2, Logger as Logger6 } from "@nestjs/common";
|
|
972
|
+
import { drizzle as drizzle2 } from "drizzle-orm/node-postgres";
|
|
973
|
+
import { Pool as Pool2 } from "pg";
|
|
974
|
+
function _ts_decorate9(decorators, target, key, desc) {
|
|
815
975
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
816
976
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
817
977
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
818
978
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
819
979
|
}
|
|
820
|
-
__name(
|
|
821
|
-
function
|
|
980
|
+
__name(_ts_decorate9, "_ts_decorate");
|
|
981
|
+
function _ts_metadata6(k, v) {
|
|
822
982
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
823
983
|
}
|
|
824
|
-
__name(
|
|
825
|
-
|
|
984
|
+
__name(_ts_metadata6, "_ts_metadata");
|
|
985
|
+
function _ts_param3(paramIndex, decorator) {
|
|
986
|
+
return function(target, key) {
|
|
987
|
+
decorator(target, key, paramIndex);
|
|
988
|
+
};
|
|
989
|
+
}
|
|
990
|
+
__name(_ts_param3, "_ts_param");
|
|
991
|
+
var TenantDatabaseService = class _TenantDatabaseService {
|
|
826
992
|
static {
|
|
827
|
-
__name(this, "
|
|
993
|
+
__name(this, "TenantDatabaseService");
|
|
828
994
|
}
|
|
995
|
+
options;
|
|
829
996
|
tenantContext;
|
|
830
|
-
logger = new
|
|
831
|
-
constructor(tenantContext) {
|
|
832
|
-
this.tenantContext = tenantContext;
|
|
833
|
-
}
|
|
834
|
-
intercept(context, next) {
|
|
835
|
-
const contextType = context.getType();
|
|
836
|
-
if (contextType === "rpc") {
|
|
837
|
-
const rpcContext = context.switchToRpc();
|
|
838
|
-
const payload = rpcContext.getData();
|
|
839
|
-
if (payload && payload.tenant) {
|
|
840
|
-
const tenant = payload.tenant;
|
|
841
|
-
this.logger.debug(`Setting tenant context from message: ${tenant.subdomain}`);
|
|
842
|
-
try {
|
|
843
|
-
this.tenantContext.setTenant(tenant);
|
|
844
|
-
this.logger.log(`Tenant context set: ${tenant.subdomain} (${tenant.type})`);
|
|
845
|
-
} catch (error) {
|
|
846
|
-
this.logger.error("Failed to set tenant context from message", error);
|
|
847
|
-
}
|
|
848
|
-
} else {
|
|
849
|
-
this.logger.warn("Message payload missing tenant information");
|
|
850
|
-
}
|
|
851
|
-
}
|
|
852
|
-
return next.handle().pipe(tap({
|
|
853
|
-
next: /* @__PURE__ */ __name(() => {
|
|
854
|
-
this.cleanupContext();
|
|
855
|
-
}, "next"),
|
|
856
|
-
error: /* @__PURE__ */ __name(() => {
|
|
857
|
-
this.cleanupContext();
|
|
858
|
-
}, "error"),
|
|
859
|
-
complete: /* @__PURE__ */ __name(() => {
|
|
860
|
-
this.cleanupContext();
|
|
861
|
-
}, "complete")
|
|
862
|
-
}));
|
|
863
|
-
}
|
|
864
|
-
/**
|
|
865
|
-
* Clean up tenant context after message is processed
|
|
866
|
-
*/
|
|
867
|
-
cleanupContext() {
|
|
868
|
-
if (this.tenantContext.hasTenant()) {
|
|
869
|
-
const tenant = this.tenantContext.getTenantIdSafe();
|
|
870
|
-
this.tenantContext.clearTenant();
|
|
871
|
-
this.logger.debug(`Cleaned up tenant context: ${tenant}`);
|
|
872
|
-
}
|
|
873
|
-
}
|
|
874
|
-
};
|
|
875
|
-
MessageTenantContextInterceptor = _ts_decorate7([
|
|
876
|
-
Injectable5({
|
|
877
|
-
scope: Scope4.REQUEST
|
|
878
|
-
}),
|
|
879
|
-
_ts_metadata4("design:type", Function),
|
|
880
|
-
_ts_metadata4("design:paramtypes", [
|
|
881
|
-
typeof TenantContextService === "undefined" ? Object : TenantContextService
|
|
882
|
-
])
|
|
883
|
-
], MessageTenantContextInterceptor);
|
|
884
|
-
|
|
885
|
-
// src/database/interceptors/tenant-context.interceptor.ts
|
|
886
|
-
import { Injectable as Injectable6, Logger as Logger4, Scope as Scope5, UnauthorizedException as UnauthorizedException3 } from "@nestjs/common";
|
|
887
|
-
import { Reflector as Reflector2 } from "@nestjs/core";
|
|
888
|
-
function _ts_decorate8(decorators, target, key, desc) {
|
|
889
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
890
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
891
|
-
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;
|
|
892
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
893
|
-
}
|
|
894
|
-
__name(_ts_decorate8, "_ts_decorate");
|
|
895
|
-
function _ts_metadata5(k, v) {
|
|
896
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
897
|
-
}
|
|
898
|
-
__name(_ts_metadata5, "_ts_metadata");
|
|
899
|
-
var TenantContextInterceptor = class _TenantContextInterceptor {
|
|
900
|
-
static {
|
|
901
|
-
__name(this, "TenantContextInterceptor");
|
|
902
|
-
}
|
|
903
|
-
reflector;
|
|
904
|
-
tenantContext;
|
|
905
|
-
primaryDatabase;
|
|
906
|
-
requestService;
|
|
907
|
-
logger = new Logger4(_TenantContextInterceptor.name);
|
|
908
|
-
constructor(reflector, tenantContext, primaryDatabase, requestService) {
|
|
909
|
-
this.reflector = reflector;
|
|
910
|
-
this.tenantContext = tenantContext;
|
|
911
|
-
this.primaryDatabase = primaryDatabase;
|
|
912
|
-
this.requestService = requestService;
|
|
913
|
-
}
|
|
914
|
-
async intercept(context, next) {
|
|
915
|
-
const request = context.switchToHttp().getRequest();
|
|
916
|
-
this.logger.debug(`Processing request: ${request.method} ${request.url}`);
|
|
917
|
-
const isPublic = this.reflector.getAllAndOverride("isPublic", [
|
|
918
|
-
context.getHandler(),
|
|
919
|
-
context.getClass()
|
|
920
|
-
]);
|
|
921
|
-
try {
|
|
922
|
-
const tenantIdentifier = this.requestService.getTenantIdentifier();
|
|
923
|
-
if (isPublic && !tenantIdentifier) {
|
|
924
|
-
this.logger.debug("Public endpoint without tenant identifier, skipping tenant context setup");
|
|
925
|
-
return next.handle();
|
|
926
|
-
}
|
|
927
|
-
if (!tenantIdentifier) {
|
|
928
|
-
throw new UnauthorizedException3("Tenant identifier not found in request");
|
|
929
|
-
}
|
|
930
|
-
this.logger.debug(`Tenant identifier extracted: ${tenantIdentifier}`);
|
|
931
|
-
if (tenantIdentifier === "cloud") {
|
|
932
|
-
this.logger.log("Cloud platform access detected, skipping tenant context setup");
|
|
933
|
-
return next.handle();
|
|
934
|
-
}
|
|
935
|
-
const tenantInfo = await this.primaryDatabase.getTenantInfo(tenantIdentifier);
|
|
936
|
-
if (!tenantInfo) {
|
|
937
|
-
this.logger.warn(`Invalid tenant: ${tenantIdentifier}`);
|
|
938
|
-
throw new UnauthorizedException3("Invalid tenant");
|
|
939
|
-
}
|
|
940
|
-
if (tenantInfo.status !== "ACTIVE") {
|
|
941
|
-
this.logger.warn(`Tenant ${tenantIdentifier} has status: ${tenantInfo.status}`);
|
|
942
|
-
throw new UnauthorizedException3(`Tenant is ${tenantInfo.status}`);
|
|
943
|
-
}
|
|
944
|
-
this.logger.debug(`Tenant config loaded: ${tenantInfo.subdomain} (${tenantInfo.type})`);
|
|
945
|
-
this.tenantContext.setTenant(tenantInfo);
|
|
946
|
-
request.tenant = tenantInfo;
|
|
947
|
-
this.logger.log(`Tenant context set: ${tenantInfo.subdomain}`);
|
|
948
|
-
} catch (error) {
|
|
949
|
-
this.logger.error("Failed to set tenant context", error);
|
|
950
|
-
throw error;
|
|
951
|
-
}
|
|
952
|
-
return next.handle();
|
|
953
|
-
}
|
|
954
|
-
};
|
|
955
|
-
TenantContextInterceptor = _ts_decorate8([
|
|
956
|
-
Injectable6({
|
|
957
|
-
scope: Scope5.REQUEST
|
|
958
|
-
}),
|
|
959
|
-
_ts_metadata5("design:type", Function),
|
|
960
|
-
_ts_metadata5("design:paramtypes", [
|
|
961
|
-
typeof Reflector2 === "undefined" ? Object : Reflector2,
|
|
962
|
-
typeof TenantContextService === "undefined" ? Object : TenantContextService,
|
|
963
|
-
typeof PrimaryDatabaseService === "undefined" ? Object : PrimaryDatabaseService,
|
|
964
|
-
typeof RequestService === "undefined" ? Object : RequestService
|
|
965
|
-
])
|
|
966
|
-
], TenantContextInterceptor);
|
|
967
|
-
|
|
968
|
-
// src/database/services/tenant-database.service.ts
|
|
969
|
-
import { Inject as Inject3, Injectable as Injectable7, InternalServerErrorException as InternalServerErrorException2, Logger as Logger5 } from "@nestjs/common";
|
|
970
|
-
import { Pool as Pool2 } from "pg";
|
|
971
|
-
import { drizzle as drizzle2 } from "drizzle-orm/node-postgres";
|
|
972
|
-
function _ts_decorate9(decorators, target, key, desc) {
|
|
973
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
974
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
975
|
-
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;
|
|
976
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
977
|
-
}
|
|
978
|
-
__name(_ts_decorate9, "_ts_decorate");
|
|
979
|
-
function _ts_metadata6(k, v) {
|
|
980
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
981
|
-
}
|
|
982
|
-
__name(_ts_metadata6, "_ts_metadata");
|
|
983
|
-
function _ts_param3(paramIndex, decorator) {
|
|
984
|
-
return function(target, key) {
|
|
985
|
-
decorator(target, key, paramIndex);
|
|
986
|
-
};
|
|
987
|
-
}
|
|
988
|
-
__name(_ts_param3, "_ts_param");
|
|
989
|
-
var TenantDatabaseService = class _TenantDatabaseService {
|
|
990
|
-
static {
|
|
991
|
-
__name(this, "TenantDatabaseService");
|
|
992
|
-
}
|
|
993
|
-
options;
|
|
994
|
-
tenantContext;
|
|
995
|
-
logger = new Logger5(_TenantDatabaseService.name);
|
|
997
|
+
logger = new Logger6(_TenantDatabaseService.name);
|
|
996
998
|
/** Connection pool: Map<cacheKey, TenantConnection> */
|
|
997
999
|
clients = /* @__PURE__ */ new Map();
|
|
998
1000
|
/** Track last usage time for idle connection cleanup */
|
|
@@ -1211,7 +1213,7 @@ var DatabaseModule = class _DatabaseModule {
|
|
|
1211
1213
|
* })
|
|
1212
1214
|
*/
|
|
1213
1215
|
static forServer(options) {
|
|
1214
|
-
return
|
|
1216
|
+
return _DatabaseModule.createDynamicModule(options, "server");
|
|
1215
1217
|
}
|
|
1216
1218
|
/**
|
|
1217
1219
|
* Configure DatabaseModule for Microservice/Messaging mode (RabbitMQ workers)
|
|
@@ -1233,7 +1235,7 @@ var DatabaseModule = class _DatabaseModule {
|
|
|
1233
1235
|
* })
|
|
1234
1236
|
*/
|
|
1235
1237
|
static forMicroservice(options) {
|
|
1236
|
-
return
|
|
1238
|
+
return _DatabaseModule.createDynamicModule(options, "microservice");
|
|
1237
1239
|
}
|
|
1238
1240
|
/**
|
|
1239
1241
|
* Internal helper to create dynamic module with conditional interceptor registration
|
|
@@ -1250,8 +1252,8 @@ var DatabaseModule = class _DatabaseModule {
|
|
|
1250
1252
|
};
|
|
1251
1253
|
const providers = [
|
|
1252
1254
|
asyncProvider,
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
+
TenantContextService2,
|
|
1256
|
+
PrimaryDatabaseService2,
|
|
1255
1257
|
TenantDatabaseService
|
|
1256
1258
|
];
|
|
1257
1259
|
if (mode === "server") {
|
|
@@ -1273,8 +1275,8 @@ var DatabaseModule = class _DatabaseModule {
|
|
|
1273
1275
|
providers,
|
|
1274
1276
|
exports: [
|
|
1275
1277
|
TenantDatabaseService,
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
+
TenantContextService2,
|
|
1279
|
+
PrimaryDatabaseService2,
|
|
1278
1280
|
asyncProvider
|
|
1279
1281
|
]
|
|
1280
1282
|
};
|
|
@@ -1285,9 +1287,20 @@ DatabaseModule = _ts_decorate10([
|
|
|
1285
1287
|
Module3({})
|
|
1286
1288
|
], DatabaseModule);
|
|
1287
1289
|
|
|
1290
|
+
// src/database/decorators/tenant.decorator.ts
|
|
1291
|
+
import { createParamDecorator } from "@nestjs/common";
|
|
1292
|
+
var Tenant = createParamDecorator((_data, ctx) => {
|
|
1293
|
+
const request = ctx.switchToHttp().getRequest();
|
|
1294
|
+
const tenantContext = request.app?.get?.(TenantContextService2);
|
|
1295
|
+
if (!tenantContext) {
|
|
1296
|
+
throw new Error("TenantContextService not found.");
|
|
1297
|
+
}
|
|
1298
|
+
return tenantContext.getTenant();
|
|
1299
|
+
});
|
|
1300
|
+
|
|
1288
1301
|
// src/database/repositories/primary-base.repository.ts
|
|
1289
|
-
import { Logger as
|
|
1290
|
-
import { eq as eq2,
|
|
1302
|
+
import { Logger as Logger7 } from "@nestjs/common";
|
|
1303
|
+
import { eq as eq2, getTableName, sql } from "drizzle-orm";
|
|
1291
1304
|
function snakeToCamel(str) {
|
|
1292
1305
|
return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
|
|
1293
1306
|
}
|
|
@@ -1357,7 +1370,7 @@ var PrimaryBaseRepository = class {
|
|
|
1357
1370
|
this.table = table;
|
|
1358
1371
|
const dbTableName = getTableName(table);
|
|
1359
1372
|
this.tableName = snakeToCamel(dbTableName);
|
|
1360
|
-
this.logger = new
|
|
1373
|
+
this.logger = new Logger7(this.constructor.name);
|
|
1361
1374
|
this.logger.debug(`Initialized ${this.constructor.name}`);
|
|
1362
1375
|
this.logger.debug(`Table name: '${dbTableName}' -> query key: '${this.tableName}'`);
|
|
1363
1376
|
}
|
|
@@ -1597,8 +1610,8 @@ var PrimaryBaseRepository = class {
|
|
|
1597
1610
|
};
|
|
1598
1611
|
|
|
1599
1612
|
// src/database/repositories/tenant-base.repository.ts
|
|
1600
|
-
import { Logger as
|
|
1601
|
-
import { eq as eq3,
|
|
1613
|
+
import { Logger as Logger8 } from "@nestjs/common";
|
|
1614
|
+
import { eq as eq3, getTableName as getTableName2, sql as sql2 } from "drizzle-orm";
|
|
1602
1615
|
var TenantBaseRepository = class {
|
|
1603
1616
|
static {
|
|
1604
1617
|
__name(this, "TenantBaseRepository");
|
|
@@ -1654,7 +1667,7 @@ var TenantBaseRepository = class {
|
|
|
1654
1667
|
this.database = database;
|
|
1655
1668
|
this.table = table;
|
|
1656
1669
|
this.tableName = getTableName2(table);
|
|
1657
|
-
this.logger = new
|
|
1670
|
+
this.logger = new Logger8(this.constructor.name);
|
|
1658
1671
|
this.logger.debug(`Initialized ${this.constructor.name}`);
|
|
1659
1672
|
}
|
|
1660
1673
|
/**
|
|
@@ -1886,262 +1899,74 @@ var TenantBaseRepository = class {
|
|
|
1886
1899
|
}
|
|
1887
1900
|
};
|
|
1888
1901
|
|
|
1889
|
-
// src/
|
|
1890
|
-
import {
|
|
1891
|
-
var Tenant = createParamDecorator((data, ctx) => {
|
|
1892
|
-
const request = ctx.switchToHttp().getRequest();
|
|
1893
|
-
const tenantContext = request.app?.get?.(TenantContextService);
|
|
1894
|
-
if (!tenantContext) {
|
|
1895
|
-
throw new Error("TenantContextService not found.");
|
|
1896
|
-
}
|
|
1897
|
-
return tenantContext.getTenant();
|
|
1898
|
-
});
|
|
1899
|
-
|
|
1900
|
-
// src/auth/decorators/onboarding.decorator.ts
|
|
1901
|
-
import { SetMetadata } from "@nestjs/common";
|
|
1902
|
-
var Onboarding = /* @__PURE__ */ __name(() => SetMetadata("isOnboarding", true), "Onboarding");
|
|
1903
|
-
|
|
1904
|
-
// src/auth/decorators/public.decorator.ts
|
|
1905
|
-
import { SetMetadata as SetMetadata2 } from "@nestjs/common";
|
|
1906
|
-
var Public = /* @__PURE__ */ __name(() => SetMetadata2("isPublic", true), "Public");
|
|
1902
|
+
// src/exceptions/bad-gateway.exception.ts
|
|
1903
|
+
import { HttpStatus as HttpStatus2 } from "@nestjs/common";
|
|
1907
1904
|
|
|
1908
|
-
// src/
|
|
1909
|
-
import {
|
|
1905
|
+
// src/exceptions/base-field.exception.ts
|
|
1906
|
+
import { HttpException, HttpStatus } from "@nestjs/common";
|
|
1907
|
+
var BaseFieldException = class extends HttpException {
|
|
1908
|
+
static {
|
|
1909
|
+
__name(this, "BaseFieldException");
|
|
1910
|
+
}
|
|
1911
|
+
constructor(statusOrMessageOrErrors, messageOrStatus, statusOrDetail, detail) {
|
|
1912
|
+
let errors;
|
|
1913
|
+
let httpStatus;
|
|
1914
|
+
let finalDetail;
|
|
1915
|
+
if (Array.isArray(statusOrMessageOrErrors)) {
|
|
1916
|
+
errors = statusOrMessageOrErrors;
|
|
1917
|
+
httpStatus = messageOrStatus || HttpStatus.BAD_REQUEST;
|
|
1918
|
+
finalDetail = typeof statusOrDetail === "string" ? statusOrDetail : void 0;
|
|
1919
|
+
} else if (typeof statusOrMessageOrErrors === "string" && typeof messageOrStatus === "string") {
|
|
1920
|
+
errors = [
|
|
1921
|
+
{
|
|
1922
|
+
field: statusOrMessageOrErrors,
|
|
1923
|
+
message: messageOrStatus
|
|
1924
|
+
}
|
|
1925
|
+
];
|
|
1926
|
+
httpStatus = statusOrDetail;
|
|
1927
|
+
finalDetail = detail;
|
|
1928
|
+
} else if (typeof statusOrMessageOrErrors === "string") {
|
|
1929
|
+
errors = [
|
|
1930
|
+
{
|
|
1931
|
+
message: statusOrMessageOrErrors
|
|
1932
|
+
}
|
|
1933
|
+
];
|
|
1934
|
+
httpStatus = messageOrStatus;
|
|
1935
|
+
finalDetail = typeof statusOrDetail === "string" ? statusOrDetail : void 0;
|
|
1936
|
+
} else {
|
|
1937
|
+
errors = [
|
|
1938
|
+
{
|
|
1939
|
+
message: "An error occurred"
|
|
1940
|
+
}
|
|
1941
|
+
];
|
|
1942
|
+
httpStatus = statusOrMessageOrErrors;
|
|
1943
|
+
finalDetail = void 0;
|
|
1944
|
+
}
|
|
1945
|
+
const response = finalDetail !== void 0 ? {
|
|
1946
|
+
errors,
|
|
1947
|
+
detail: finalDetail
|
|
1948
|
+
} : {
|
|
1949
|
+
errors
|
|
1950
|
+
};
|
|
1951
|
+
super(response, httpStatus);
|
|
1952
|
+
}
|
|
1953
|
+
};
|
|
1910
1954
|
|
|
1911
|
-
// src/
|
|
1912
|
-
|
|
1913
|
-
import { Reflector as Reflector3 } from "@nestjs/core";
|
|
1914
|
-
function _ts_decorate11(decorators, target, key, desc) {
|
|
1915
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1916
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1917
|
-
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;
|
|
1918
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1919
|
-
}
|
|
1920
|
-
__name(_ts_decorate11, "_ts_decorate");
|
|
1921
|
-
function _ts_metadata7(k, v) {
|
|
1922
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
1923
|
-
}
|
|
1924
|
-
__name(_ts_metadata7, "_ts_metadata");
|
|
1925
|
-
var CsrfGuard = class _CsrfGuard {
|
|
1955
|
+
// src/exceptions/bad-gateway.exception.ts
|
|
1956
|
+
var BadGatewayException = class extends BaseFieldException {
|
|
1926
1957
|
static {
|
|
1927
|
-
__name(this, "
|
|
1958
|
+
__name(this, "BadGatewayException");
|
|
1928
1959
|
}
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
"HEAD",
|
|
1940
|
-
"OPTIONS"
|
|
1941
|
-
];
|
|
1942
|
-
if (safeMethods.includes(request.method)) {
|
|
1943
|
-
return true;
|
|
1944
|
-
}
|
|
1945
|
-
try {
|
|
1946
|
-
const fastifyInstance = request.server;
|
|
1947
|
-
if (!fastifyInstance.csrfProtection) {
|
|
1948
|
-
this.logger.error("CSRF protection plugin not found. Ensure @fastify/csrf-protection is registered.");
|
|
1949
|
-
throw new ForbiddenException("CSRF protection not configured");
|
|
1950
|
-
}
|
|
1951
|
-
await new Promise((resolve, reject) => {
|
|
1952
|
-
fastifyInstance.csrfProtection(request, reply, (err) => {
|
|
1953
|
-
if (err) {
|
|
1954
|
-
reject(err);
|
|
1955
|
-
} else {
|
|
1956
|
-
resolve();
|
|
1957
|
-
}
|
|
1958
|
-
});
|
|
1959
|
-
});
|
|
1960
|
-
this.logger.debug(`CSRF validation successful for ${request.method} ${request.url}`);
|
|
1961
|
-
return true;
|
|
1962
|
-
} catch (error) {
|
|
1963
|
-
this.logger.warn(`CSRF validation failed for ${request.method} ${request.url}: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
1964
|
-
throw new ForbiddenException({
|
|
1965
|
-
errors: [
|
|
1966
|
-
{
|
|
1967
|
-
field: "csrf",
|
|
1968
|
-
message: "Invalid or missing CSRF token"
|
|
1969
|
-
}
|
|
1970
|
-
],
|
|
1971
|
-
message: "CSRF validation failed"
|
|
1972
|
-
});
|
|
1973
|
-
}
|
|
1974
|
-
}
|
|
1975
|
-
};
|
|
1976
|
-
CsrfGuard = _ts_decorate11([
|
|
1977
|
-
Injectable8(),
|
|
1978
|
-
_ts_metadata7("design:type", Function),
|
|
1979
|
-
_ts_metadata7("design:paramtypes", [
|
|
1980
|
-
typeof Reflector3 === "undefined" ? Object : Reflector3
|
|
1981
|
-
])
|
|
1982
|
-
], CsrfGuard);
|
|
1983
|
-
|
|
1984
|
-
// src/http/http.module.ts
|
|
1985
|
-
function _ts_decorate12(decorators, target, key, desc) {
|
|
1986
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1987
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1988
|
-
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;
|
|
1989
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1990
|
-
}
|
|
1991
|
-
__name(_ts_decorate12, "_ts_decorate");
|
|
1992
|
-
var HttpModule = class {
|
|
1993
|
-
static {
|
|
1994
|
-
__name(this, "HttpModule");
|
|
1995
|
-
}
|
|
1996
|
-
};
|
|
1997
|
-
HttpModule = _ts_decorate12([
|
|
1998
|
-
Module4({
|
|
1999
|
-
providers: [
|
|
2000
|
-
CsrfGuard
|
|
2001
|
-
],
|
|
2002
|
-
exports: [
|
|
2003
|
-
CsrfGuard
|
|
2004
|
-
]
|
|
2005
|
-
})
|
|
2006
|
-
], HttpModule);
|
|
2007
|
-
|
|
2008
|
-
// src/filters/http-exception.filter.ts
|
|
2009
|
-
import { Catch, HttpException, HttpStatus, Logger as Logger9 } from "@nestjs/common";
|
|
2010
|
-
function _ts_decorate13(decorators, target, key, desc) {
|
|
2011
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
2012
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
2013
|
-
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;
|
|
2014
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
2015
|
-
}
|
|
2016
|
-
__name(_ts_decorate13, "_ts_decorate");
|
|
2017
|
-
function getHttpStatusTitle(status) {
|
|
2018
|
-
const enumKey = Object.entries(HttpStatus).find(([key, value]) => value === status && isNaN(Number(key)))?.[0];
|
|
2019
|
-
if (!enumKey) {
|
|
2020
|
-
return "Error";
|
|
2021
|
-
}
|
|
2022
|
-
return enumKey.split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
|
|
2023
|
-
}
|
|
2024
|
-
__name(getHttpStatusTitle, "getHttpStatusTitle");
|
|
2025
|
-
var HttpExceptionFilter = class _HttpExceptionFilter {
|
|
2026
|
-
static {
|
|
2027
|
-
__name(this, "HttpExceptionFilter");
|
|
2028
|
-
}
|
|
2029
|
-
logger = new Logger9(_HttpExceptionFilter.name);
|
|
2030
|
-
catch(exception, host) {
|
|
2031
|
-
const ctx = host.switchToHttp();
|
|
2032
|
-
const response = ctx.getResponse();
|
|
2033
|
-
let status = HttpStatus.INTERNAL_SERVER_ERROR;
|
|
2034
|
-
let errors = [];
|
|
2035
|
-
let detail = "Internal server error";
|
|
2036
|
-
if (exception instanceof HttpException) {
|
|
2037
|
-
status = exception.getStatus();
|
|
2038
|
-
const exceptionResponse = exception.getResponse();
|
|
2039
|
-
if (typeof exceptionResponse === "object" && exceptionResponse !== null) {
|
|
2040
|
-
const responseObj = exceptionResponse;
|
|
2041
|
-
if ("errors" in responseObj && Array.isArray(responseObj.errors)) {
|
|
2042
|
-
errors = responseObj.errors;
|
|
2043
|
-
detail = ("detail" in responseObj ? responseObj.detail : void 0) || exception.message;
|
|
2044
|
-
} else if ("message" in responseObj && Array.isArray(responseObj.message)) {
|
|
2045
|
-
errors = responseObj.message.map((msg) => {
|
|
2046
|
-
if (typeof msg === "object" && "property" in msg && "constraints" in msg) {
|
|
2047
|
-
const constraintValues = Object.values(msg.constraints);
|
|
2048
|
-
return {
|
|
2049
|
-
field: msg.property,
|
|
2050
|
-
message: constraintValues[0] ?? "Validation failed"
|
|
2051
|
-
};
|
|
2052
|
-
}
|
|
2053
|
-
return {
|
|
2054
|
-
message: typeof msg === "string" ? msg : JSON.stringify(msg)
|
|
2055
|
-
};
|
|
2056
|
-
});
|
|
2057
|
-
detail = "Validation failed";
|
|
2058
|
-
} else if ("message" in responseObj) {
|
|
2059
|
-
const message = responseObj.message;
|
|
2060
|
-
errors = [
|
|
2061
|
-
{
|
|
2062
|
-
message: Array.isArray(message) ? message.join(", ") : message
|
|
2063
|
-
}
|
|
2064
|
-
];
|
|
2065
|
-
detail = ("error" in responseObj ? responseObj.error : void 0) || exception.message;
|
|
2066
|
-
}
|
|
2067
|
-
} else if (typeof exceptionResponse === "string") {
|
|
2068
|
-
errors = [
|
|
2069
|
-
{
|
|
2070
|
-
message: exceptionResponse
|
|
2071
|
-
}
|
|
2072
|
-
];
|
|
2073
|
-
detail = exceptionResponse;
|
|
2074
|
-
}
|
|
2075
|
-
} else {
|
|
2076
|
-
const errorMessage = exception instanceof Error ? exception.message : "Unknown error";
|
|
2077
|
-
const stack = exception instanceof Error ? exception.stack : void 0;
|
|
2078
|
-
this.logger.error(`Unexpected error: ${errorMessage}`, stack);
|
|
2079
|
-
errors = [
|
|
2080
|
-
{
|
|
2081
|
-
message: "An unexpected error occurred"
|
|
2082
|
-
}
|
|
2083
|
-
];
|
|
2084
|
-
}
|
|
2085
|
-
const problemDetails = {
|
|
2086
|
-
title: getHttpStatusTitle(status),
|
|
2087
|
-
status,
|
|
2088
|
-
detail,
|
|
2089
|
-
errors
|
|
2090
|
-
};
|
|
2091
|
-
response.status(status).send(problemDetails);
|
|
2092
|
-
}
|
|
2093
|
-
};
|
|
2094
|
-
HttpExceptionFilter = _ts_decorate13([
|
|
2095
|
-
Catch()
|
|
2096
|
-
], HttpExceptionFilter);
|
|
2097
|
-
|
|
2098
|
-
// src/exceptions/base-field.exception.ts
|
|
2099
|
-
import { HttpException as HttpException2, HttpStatus as HttpStatus2 } from "@nestjs/common";
|
|
2100
|
-
var BaseFieldException = class extends HttpException2 {
|
|
2101
|
-
static {
|
|
2102
|
-
__name(this, "BaseFieldException");
|
|
2103
|
-
}
|
|
2104
|
-
constructor(statusOrMessageOrErrors, messageOrStatus, statusOrDetail, detail) {
|
|
2105
|
-
let errors;
|
|
2106
|
-
let httpStatus;
|
|
2107
|
-
let finalDetail;
|
|
2108
|
-
if (Array.isArray(statusOrMessageOrErrors)) {
|
|
2109
|
-
errors = statusOrMessageOrErrors;
|
|
2110
|
-
httpStatus = messageOrStatus || HttpStatus2.BAD_REQUEST;
|
|
2111
|
-
finalDetail = typeof statusOrDetail === "string" ? statusOrDetail : void 0;
|
|
2112
|
-
} else if (typeof statusOrMessageOrErrors === "string" && typeof messageOrStatus === "string") {
|
|
2113
|
-
errors = [
|
|
2114
|
-
{
|
|
2115
|
-
field: statusOrMessageOrErrors,
|
|
2116
|
-
message: messageOrStatus
|
|
2117
|
-
}
|
|
2118
|
-
];
|
|
2119
|
-
httpStatus = statusOrDetail;
|
|
2120
|
-
finalDetail = detail;
|
|
2121
|
-
} else if (typeof statusOrMessageOrErrors === "string") {
|
|
2122
|
-
errors = [
|
|
2123
|
-
{
|
|
2124
|
-
message: statusOrMessageOrErrors
|
|
2125
|
-
}
|
|
2126
|
-
];
|
|
2127
|
-
httpStatus = messageOrStatus;
|
|
2128
|
-
finalDetail = typeof statusOrDetail === "string" ? statusOrDetail : void 0;
|
|
2129
|
-
} else {
|
|
2130
|
-
errors = [
|
|
2131
|
-
{
|
|
2132
|
-
message: "An error occurred"
|
|
2133
|
-
}
|
|
2134
|
-
];
|
|
2135
|
-
httpStatus = statusOrMessageOrErrors;
|
|
2136
|
-
finalDetail = void 0;
|
|
2137
|
-
}
|
|
2138
|
-
const response = finalDetail !== void 0 ? {
|
|
2139
|
-
errors,
|
|
2140
|
-
detail: finalDetail
|
|
2141
|
-
} : {
|
|
2142
|
-
errors
|
|
2143
|
-
};
|
|
2144
|
-
super(response, httpStatus);
|
|
1960
|
+
constructor(messageOrField, fieldMessageOrDetail, detail) {
|
|
1961
|
+
if (Array.isArray(messageOrField)) {
|
|
1962
|
+
super(messageOrField, HttpStatus2.BAD_GATEWAY, fieldMessageOrDetail);
|
|
1963
|
+
} else if (detail !== void 0) {
|
|
1964
|
+
super(messageOrField, fieldMessageOrDetail, HttpStatus2.BAD_GATEWAY, detail);
|
|
1965
|
+
} else if (fieldMessageOrDetail) {
|
|
1966
|
+
super(messageOrField, fieldMessageOrDetail, HttpStatus2.BAD_GATEWAY);
|
|
1967
|
+
} else {
|
|
1968
|
+
super(messageOrField, HttpStatus2.BAD_GATEWAY);
|
|
1969
|
+
}
|
|
2145
1970
|
}
|
|
2146
1971
|
};
|
|
2147
1972
|
|
|
@@ -2164,28 +1989,28 @@ var BadRequestException = class extends BaseFieldException {
|
|
|
2164
1989
|
}
|
|
2165
1990
|
};
|
|
2166
1991
|
|
|
2167
|
-
// src/exceptions/
|
|
1992
|
+
// src/exceptions/conflict.exception.ts
|
|
2168
1993
|
import { HttpStatus as HttpStatus4 } from "@nestjs/common";
|
|
2169
|
-
var
|
|
1994
|
+
var ConflictException = class extends BaseFieldException {
|
|
2170
1995
|
static {
|
|
2171
|
-
__name(this, "
|
|
1996
|
+
__name(this, "ConflictException");
|
|
2172
1997
|
}
|
|
2173
1998
|
constructor(messageOrField, fieldMessageOrDetail, detail) {
|
|
2174
1999
|
if (Array.isArray(messageOrField)) {
|
|
2175
|
-
super(messageOrField, HttpStatus4.
|
|
2000
|
+
super(messageOrField, HttpStatus4.CONFLICT, fieldMessageOrDetail);
|
|
2176
2001
|
} else if (detail !== void 0) {
|
|
2177
|
-
super(messageOrField, fieldMessageOrDetail, HttpStatus4.
|
|
2002
|
+
super(messageOrField, fieldMessageOrDetail, HttpStatus4.CONFLICT, detail);
|
|
2178
2003
|
} else if (fieldMessageOrDetail) {
|
|
2179
|
-
super(messageOrField, fieldMessageOrDetail, HttpStatus4.
|
|
2004
|
+
super(messageOrField, fieldMessageOrDetail, HttpStatus4.CONFLICT);
|
|
2180
2005
|
} else {
|
|
2181
|
-
super(messageOrField, HttpStatus4.
|
|
2006
|
+
super(messageOrField, HttpStatus4.CONFLICT);
|
|
2182
2007
|
}
|
|
2183
2008
|
}
|
|
2184
2009
|
};
|
|
2185
2010
|
|
|
2186
2011
|
// src/exceptions/forbidden.exception.ts
|
|
2187
2012
|
import { HttpStatus as HttpStatus5 } from "@nestjs/common";
|
|
2188
|
-
var
|
|
2013
|
+
var ForbiddenException = class extends BaseFieldException {
|
|
2189
2014
|
static {
|
|
2190
2015
|
__name(this, "ForbiddenException");
|
|
2191
2016
|
}
|
|
@@ -2202,222 +2027,230 @@ var ForbiddenException2 = class extends BaseFieldException {
|
|
|
2202
2027
|
}
|
|
2203
2028
|
};
|
|
2204
2029
|
|
|
2205
|
-
// src/exceptions/
|
|
2030
|
+
// src/exceptions/gone.exception.ts
|
|
2206
2031
|
import { HttpStatus as HttpStatus6 } from "@nestjs/common";
|
|
2207
|
-
var
|
|
2032
|
+
var GoneException = class extends BaseFieldException {
|
|
2208
2033
|
static {
|
|
2209
|
-
__name(this, "
|
|
2034
|
+
__name(this, "GoneException");
|
|
2210
2035
|
}
|
|
2211
2036
|
constructor(messageOrField, fieldMessageOrDetail, detail) {
|
|
2212
2037
|
if (Array.isArray(messageOrField)) {
|
|
2213
|
-
super(messageOrField, HttpStatus6.
|
|
2038
|
+
super(messageOrField, HttpStatus6.GONE, fieldMessageOrDetail);
|
|
2214
2039
|
} else if (detail !== void 0) {
|
|
2215
|
-
super(messageOrField, fieldMessageOrDetail, HttpStatus6.
|
|
2040
|
+
super(messageOrField, fieldMessageOrDetail, HttpStatus6.GONE, detail);
|
|
2216
2041
|
} else if (fieldMessageOrDetail) {
|
|
2217
|
-
super(messageOrField, fieldMessageOrDetail, HttpStatus6.
|
|
2042
|
+
super(messageOrField, fieldMessageOrDetail, HttpStatus6.GONE);
|
|
2218
2043
|
} else {
|
|
2219
|
-
super(messageOrField, HttpStatus6.
|
|
2044
|
+
super(messageOrField, HttpStatus6.GONE);
|
|
2220
2045
|
}
|
|
2221
2046
|
}
|
|
2222
2047
|
};
|
|
2223
2048
|
|
|
2224
|
-
// src/exceptions/
|
|
2049
|
+
// src/exceptions/internal-server-error.exception.ts
|
|
2225
2050
|
import { HttpStatus as HttpStatus7 } from "@nestjs/common";
|
|
2226
|
-
var
|
|
2051
|
+
var InternalServerErrorException3 = class extends BaseFieldException {
|
|
2227
2052
|
static {
|
|
2228
|
-
__name(this, "
|
|
2053
|
+
__name(this, "InternalServerErrorException");
|
|
2229
2054
|
}
|
|
2230
2055
|
constructor(messageOrField, fieldMessageOrDetail, detail) {
|
|
2231
2056
|
if (Array.isArray(messageOrField)) {
|
|
2232
|
-
super(messageOrField, HttpStatus7.
|
|
2057
|
+
super(messageOrField, HttpStatus7.INTERNAL_SERVER_ERROR, fieldMessageOrDetail);
|
|
2233
2058
|
} else if (detail !== void 0) {
|
|
2234
|
-
super(messageOrField, fieldMessageOrDetail, HttpStatus7.
|
|
2059
|
+
super(messageOrField, fieldMessageOrDetail, HttpStatus7.INTERNAL_SERVER_ERROR, detail);
|
|
2235
2060
|
} else if (fieldMessageOrDetail) {
|
|
2236
|
-
super(messageOrField, fieldMessageOrDetail, HttpStatus7.
|
|
2061
|
+
super(messageOrField, fieldMessageOrDetail, HttpStatus7.INTERNAL_SERVER_ERROR);
|
|
2237
2062
|
} else {
|
|
2238
|
-
super(messageOrField, HttpStatus7.
|
|
2063
|
+
super(messageOrField, HttpStatus7.INTERNAL_SERVER_ERROR);
|
|
2239
2064
|
}
|
|
2240
2065
|
}
|
|
2241
2066
|
};
|
|
2242
2067
|
|
|
2243
|
-
// src/exceptions/
|
|
2068
|
+
// src/exceptions/method-not-allowed.exception.ts
|
|
2244
2069
|
import { HttpStatus as HttpStatus8 } from "@nestjs/common";
|
|
2245
|
-
var
|
|
2070
|
+
var MethodNotAllowedException = class extends BaseFieldException {
|
|
2246
2071
|
static {
|
|
2247
|
-
__name(this, "
|
|
2072
|
+
__name(this, "MethodNotAllowedException");
|
|
2248
2073
|
}
|
|
2249
2074
|
constructor(messageOrField, fieldMessageOrDetail, detail) {
|
|
2250
2075
|
if (Array.isArray(messageOrField)) {
|
|
2251
|
-
super(messageOrField, HttpStatus8.
|
|
2076
|
+
super(messageOrField, HttpStatus8.METHOD_NOT_ALLOWED, fieldMessageOrDetail);
|
|
2252
2077
|
} else if (detail !== void 0) {
|
|
2253
|
-
super(messageOrField, fieldMessageOrDetail, HttpStatus8.
|
|
2078
|
+
super(messageOrField, fieldMessageOrDetail, HttpStatus8.METHOD_NOT_ALLOWED, detail);
|
|
2254
2079
|
} else if (fieldMessageOrDetail) {
|
|
2255
|
-
super(messageOrField, fieldMessageOrDetail, HttpStatus8.
|
|
2080
|
+
super(messageOrField, fieldMessageOrDetail, HttpStatus8.METHOD_NOT_ALLOWED);
|
|
2256
2081
|
} else {
|
|
2257
|
-
super(messageOrField, HttpStatus8.
|
|
2082
|
+
super(messageOrField, HttpStatus8.METHOD_NOT_ALLOWED);
|
|
2258
2083
|
}
|
|
2259
2084
|
}
|
|
2260
2085
|
};
|
|
2261
2086
|
|
|
2262
|
-
// src/exceptions/
|
|
2087
|
+
// src/exceptions/not-acceptable.exception.ts
|
|
2263
2088
|
import { HttpStatus as HttpStatus9 } from "@nestjs/common";
|
|
2264
|
-
var
|
|
2089
|
+
var NotAcceptableException = class extends BaseFieldException {
|
|
2265
2090
|
static {
|
|
2266
|
-
__name(this, "
|
|
2091
|
+
__name(this, "NotAcceptableException");
|
|
2267
2092
|
}
|
|
2268
|
-
constructor(
|
|
2269
|
-
|
|
2093
|
+
constructor(messageOrField, fieldMessageOrDetail, detail) {
|
|
2094
|
+
if (Array.isArray(messageOrField)) {
|
|
2095
|
+
super(messageOrField, HttpStatus9.NOT_ACCEPTABLE, fieldMessageOrDetail);
|
|
2096
|
+
} else if (detail !== void 0) {
|
|
2097
|
+
super(messageOrField, fieldMessageOrDetail, HttpStatus9.NOT_ACCEPTABLE, detail);
|
|
2098
|
+
} else if (fieldMessageOrDetail) {
|
|
2099
|
+
super(messageOrField, fieldMessageOrDetail, HttpStatus9.NOT_ACCEPTABLE);
|
|
2100
|
+
} else {
|
|
2101
|
+
super(messageOrField, HttpStatus9.NOT_ACCEPTABLE);
|
|
2102
|
+
}
|
|
2270
2103
|
}
|
|
2271
2104
|
};
|
|
2272
2105
|
|
|
2273
|
-
// src/exceptions/
|
|
2106
|
+
// src/exceptions/not-found.exception.ts
|
|
2274
2107
|
import { HttpStatus as HttpStatus10 } from "@nestjs/common";
|
|
2275
|
-
var
|
|
2108
|
+
var NotFoundException = class extends BaseFieldException {
|
|
2276
2109
|
static {
|
|
2277
|
-
__name(this, "
|
|
2110
|
+
__name(this, "NotFoundException");
|
|
2278
2111
|
}
|
|
2279
2112
|
constructor(messageOrField, fieldMessageOrDetail, detail) {
|
|
2280
2113
|
if (Array.isArray(messageOrField)) {
|
|
2281
|
-
super(messageOrField, HttpStatus10.
|
|
2114
|
+
super(messageOrField, HttpStatus10.NOT_FOUND, fieldMessageOrDetail);
|
|
2282
2115
|
} else if (detail !== void 0) {
|
|
2283
|
-
super(messageOrField, fieldMessageOrDetail, HttpStatus10.
|
|
2116
|
+
super(messageOrField, fieldMessageOrDetail, HttpStatus10.NOT_FOUND, detail);
|
|
2284
2117
|
} else if (fieldMessageOrDetail) {
|
|
2285
|
-
super(messageOrField, fieldMessageOrDetail, HttpStatus10.
|
|
2118
|
+
super(messageOrField, fieldMessageOrDetail, HttpStatus10.NOT_FOUND);
|
|
2286
2119
|
} else {
|
|
2287
|
-
super(messageOrField, HttpStatus10.
|
|
2120
|
+
super(messageOrField, HttpStatus10.NOT_FOUND);
|
|
2288
2121
|
}
|
|
2289
2122
|
}
|
|
2290
2123
|
};
|
|
2291
2124
|
|
|
2292
|
-
// src/exceptions/
|
|
2125
|
+
// src/exceptions/not-implemented.exception.ts
|
|
2293
2126
|
import { HttpStatus as HttpStatus11 } from "@nestjs/common";
|
|
2294
|
-
var
|
|
2127
|
+
var NotImplementedException = class extends BaseFieldException {
|
|
2295
2128
|
static {
|
|
2296
|
-
__name(this, "
|
|
2129
|
+
__name(this, "NotImplementedException");
|
|
2297
2130
|
}
|
|
2298
2131
|
constructor(messageOrField, fieldMessageOrDetail, detail) {
|
|
2299
2132
|
if (Array.isArray(messageOrField)) {
|
|
2300
|
-
super(messageOrField, HttpStatus11.
|
|
2133
|
+
super(messageOrField, HttpStatus11.NOT_IMPLEMENTED, fieldMessageOrDetail);
|
|
2301
2134
|
} else if (detail !== void 0) {
|
|
2302
|
-
super(messageOrField, fieldMessageOrDetail, HttpStatus11.
|
|
2135
|
+
super(messageOrField, fieldMessageOrDetail, HttpStatus11.NOT_IMPLEMENTED, detail);
|
|
2303
2136
|
} else if (fieldMessageOrDetail) {
|
|
2304
|
-
super(messageOrField, fieldMessageOrDetail, HttpStatus11.
|
|
2137
|
+
super(messageOrField, fieldMessageOrDetail, HttpStatus11.NOT_IMPLEMENTED);
|
|
2305
2138
|
} else {
|
|
2306
|
-
super(messageOrField, HttpStatus11.
|
|
2139
|
+
super(messageOrField, HttpStatus11.NOT_IMPLEMENTED);
|
|
2307
2140
|
}
|
|
2308
2141
|
}
|
|
2309
2142
|
};
|
|
2310
2143
|
|
|
2311
|
-
// src/exceptions/
|
|
2144
|
+
// src/exceptions/payload-too-large.exception.ts
|
|
2312
2145
|
import { HttpStatus as HttpStatus12 } from "@nestjs/common";
|
|
2313
|
-
var
|
|
2146
|
+
var PayloadTooLargeException = class extends BaseFieldException {
|
|
2314
2147
|
static {
|
|
2315
|
-
__name(this, "
|
|
2148
|
+
__name(this, "PayloadTooLargeException");
|
|
2316
2149
|
}
|
|
2317
2150
|
constructor(messageOrField, fieldMessageOrDetail, detail) {
|
|
2318
2151
|
if (Array.isArray(messageOrField)) {
|
|
2319
|
-
super(messageOrField, HttpStatus12.
|
|
2152
|
+
super(messageOrField, HttpStatus12.PAYLOAD_TOO_LARGE, fieldMessageOrDetail);
|
|
2320
2153
|
} else if (detail !== void 0) {
|
|
2321
|
-
super(messageOrField, fieldMessageOrDetail, HttpStatus12.
|
|
2154
|
+
super(messageOrField, fieldMessageOrDetail, HttpStatus12.PAYLOAD_TOO_LARGE, detail);
|
|
2322
2155
|
} else if (fieldMessageOrDetail) {
|
|
2323
|
-
super(messageOrField, fieldMessageOrDetail, HttpStatus12.
|
|
2156
|
+
super(messageOrField, fieldMessageOrDetail, HttpStatus12.PAYLOAD_TOO_LARGE);
|
|
2324
2157
|
} else {
|
|
2325
|
-
super(messageOrField, HttpStatus12.
|
|
2158
|
+
super(messageOrField, HttpStatus12.PAYLOAD_TOO_LARGE);
|
|
2326
2159
|
}
|
|
2327
2160
|
}
|
|
2328
2161
|
};
|
|
2329
2162
|
|
|
2330
|
-
// src/exceptions/
|
|
2163
|
+
// src/exceptions/request-timeout.exception.ts
|
|
2331
2164
|
import { HttpStatus as HttpStatus13 } from "@nestjs/common";
|
|
2332
|
-
var
|
|
2165
|
+
var RequestTimeoutException = class extends BaseFieldException {
|
|
2333
2166
|
static {
|
|
2334
|
-
__name(this, "
|
|
2167
|
+
__name(this, "RequestTimeoutException");
|
|
2335
2168
|
}
|
|
2336
2169
|
constructor(messageOrField, fieldMessageOrDetail, detail) {
|
|
2337
2170
|
if (Array.isArray(messageOrField)) {
|
|
2338
|
-
super(messageOrField, HttpStatus13.
|
|
2171
|
+
super(messageOrField, HttpStatus13.REQUEST_TIMEOUT, fieldMessageOrDetail);
|
|
2339
2172
|
} else if (detail !== void 0) {
|
|
2340
|
-
super(messageOrField, fieldMessageOrDetail, HttpStatus13.
|
|
2173
|
+
super(messageOrField, fieldMessageOrDetail, HttpStatus13.REQUEST_TIMEOUT, detail);
|
|
2341
2174
|
} else if (fieldMessageOrDetail) {
|
|
2342
|
-
super(messageOrField, fieldMessageOrDetail, HttpStatus13.
|
|
2175
|
+
super(messageOrField, fieldMessageOrDetail, HttpStatus13.REQUEST_TIMEOUT);
|
|
2343
2176
|
} else {
|
|
2344
|
-
super(messageOrField, HttpStatus13.
|
|
2177
|
+
super(messageOrField, HttpStatus13.REQUEST_TIMEOUT);
|
|
2345
2178
|
}
|
|
2346
2179
|
}
|
|
2347
2180
|
};
|
|
2348
2181
|
|
|
2349
|
-
// src/exceptions/
|
|
2182
|
+
// src/exceptions/service-unavailable.exception.ts
|
|
2350
2183
|
import { HttpStatus as HttpStatus14 } from "@nestjs/common";
|
|
2351
|
-
var
|
|
2184
|
+
var ServiceUnavailableException = class extends BaseFieldException {
|
|
2352
2185
|
static {
|
|
2353
|
-
__name(this, "
|
|
2186
|
+
__name(this, "ServiceUnavailableException");
|
|
2354
2187
|
}
|
|
2355
2188
|
constructor(messageOrField, fieldMessageOrDetail, detail) {
|
|
2356
2189
|
if (Array.isArray(messageOrField)) {
|
|
2357
|
-
super(messageOrField, HttpStatus14.
|
|
2190
|
+
super(messageOrField, HttpStatus14.SERVICE_UNAVAILABLE, fieldMessageOrDetail);
|
|
2358
2191
|
} else if (detail !== void 0) {
|
|
2359
|
-
super(messageOrField, fieldMessageOrDetail, HttpStatus14.
|
|
2192
|
+
super(messageOrField, fieldMessageOrDetail, HttpStatus14.SERVICE_UNAVAILABLE, detail);
|
|
2360
2193
|
} else if (fieldMessageOrDetail) {
|
|
2361
|
-
super(messageOrField, fieldMessageOrDetail, HttpStatus14.
|
|
2194
|
+
super(messageOrField, fieldMessageOrDetail, HttpStatus14.SERVICE_UNAVAILABLE);
|
|
2362
2195
|
} else {
|
|
2363
|
-
super(messageOrField, HttpStatus14.
|
|
2196
|
+
super(messageOrField, HttpStatus14.SERVICE_UNAVAILABLE);
|
|
2364
2197
|
}
|
|
2365
2198
|
}
|
|
2366
2199
|
};
|
|
2367
2200
|
|
|
2368
|
-
// src/exceptions/
|
|
2201
|
+
// src/exceptions/too-many-requests.exception.ts
|
|
2369
2202
|
import { HttpStatus as HttpStatus15 } from "@nestjs/common";
|
|
2370
|
-
var
|
|
2203
|
+
var TooManyRequestsException = class extends BaseFieldException {
|
|
2371
2204
|
static {
|
|
2372
|
-
__name(this, "
|
|
2205
|
+
__name(this, "TooManyRequestsException");
|
|
2373
2206
|
}
|
|
2374
2207
|
constructor(messageOrField, fieldMessageOrDetail, detail) {
|
|
2375
2208
|
if (Array.isArray(messageOrField)) {
|
|
2376
|
-
super(messageOrField, HttpStatus15.
|
|
2209
|
+
super(messageOrField, HttpStatus15.TOO_MANY_REQUESTS, fieldMessageOrDetail);
|
|
2377
2210
|
} else if (detail !== void 0) {
|
|
2378
|
-
super(messageOrField, fieldMessageOrDetail, HttpStatus15.
|
|
2211
|
+
super(messageOrField, fieldMessageOrDetail, HttpStatus15.TOO_MANY_REQUESTS, detail);
|
|
2379
2212
|
} else if (fieldMessageOrDetail) {
|
|
2380
|
-
super(messageOrField, fieldMessageOrDetail, HttpStatus15.
|
|
2213
|
+
super(messageOrField, fieldMessageOrDetail, HttpStatus15.TOO_MANY_REQUESTS);
|
|
2381
2214
|
} else {
|
|
2382
|
-
super(messageOrField, HttpStatus15.
|
|
2215
|
+
super(messageOrField, HttpStatus15.TOO_MANY_REQUESTS);
|
|
2383
2216
|
}
|
|
2384
2217
|
}
|
|
2385
2218
|
};
|
|
2386
2219
|
|
|
2387
|
-
// src/exceptions/
|
|
2220
|
+
// src/exceptions/unauthorized.exception.ts
|
|
2388
2221
|
import { HttpStatus as HttpStatus16 } from "@nestjs/common";
|
|
2389
|
-
var
|
|
2222
|
+
var UnauthorizedException4 = class extends BaseFieldException {
|
|
2390
2223
|
static {
|
|
2391
|
-
__name(this, "
|
|
2224
|
+
__name(this, "UnauthorizedException");
|
|
2392
2225
|
}
|
|
2393
2226
|
constructor(messageOrField, fieldMessageOrDetail, detail) {
|
|
2394
2227
|
if (Array.isArray(messageOrField)) {
|
|
2395
|
-
super(messageOrField, HttpStatus16.
|
|
2228
|
+
super(messageOrField, HttpStatus16.UNAUTHORIZED, fieldMessageOrDetail);
|
|
2396
2229
|
} else if (detail !== void 0) {
|
|
2397
|
-
super(messageOrField, fieldMessageOrDetail, HttpStatus16.
|
|
2230
|
+
super(messageOrField, fieldMessageOrDetail, HttpStatus16.UNAUTHORIZED, detail);
|
|
2398
2231
|
} else if (fieldMessageOrDetail) {
|
|
2399
|
-
super(messageOrField, fieldMessageOrDetail, HttpStatus16.
|
|
2232
|
+
super(messageOrField, fieldMessageOrDetail, HttpStatus16.UNAUTHORIZED);
|
|
2400
2233
|
} else {
|
|
2401
|
-
super(messageOrField, HttpStatus16.
|
|
2234
|
+
super(messageOrField, HttpStatus16.UNAUTHORIZED);
|
|
2402
2235
|
}
|
|
2403
2236
|
}
|
|
2404
2237
|
};
|
|
2405
2238
|
|
|
2406
|
-
// src/exceptions/
|
|
2239
|
+
// src/exceptions/unprocessable-entity.exception.ts
|
|
2407
2240
|
import { HttpStatus as HttpStatus17 } from "@nestjs/common";
|
|
2408
|
-
var
|
|
2241
|
+
var UnprocessableEntityException = class extends BaseFieldException {
|
|
2409
2242
|
static {
|
|
2410
|
-
__name(this, "
|
|
2243
|
+
__name(this, "UnprocessableEntityException");
|
|
2411
2244
|
}
|
|
2412
2245
|
constructor(messageOrField, fieldMessageOrDetail, detail) {
|
|
2413
2246
|
if (Array.isArray(messageOrField)) {
|
|
2414
|
-
super(messageOrField, HttpStatus17.
|
|
2247
|
+
super(messageOrField, HttpStatus17.UNPROCESSABLE_ENTITY, fieldMessageOrDetail);
|
|
2415
2248
|
} else if (detail !== void 0) {
|
|
2416
|
-
super(messageOrField, fieldMessageOrDetail, HttpStatus17.
|
|
2249
|
+
super(messageOrField, fieldMessageOrDetail, HttpStatus17.UNPROCESSABLE_ENTITY, detail);
|
|
2417
2250
|
} else if (fieldMessageOrDetail) {
|
|
2418
|
-
super(messageOrField, fieldMessageOrDetail, HttpStatus17.
|
|
2251
|
+
super(messageOrField, fieldMessageOrDetail, HttpStatus17.UNPROCESSABLE_ENTITY);
|
|
2419
2252
|
} else {
|
|
2420
|
-
super(messageOrField, HttpStatus17.
|
|
2253
|
+
super(messageOrField, HttpStatus17.UNPROCESSABLE_ENTITY);
|
|
2421
2254
|
}
|
|
2422
2255
|
}
|
|
2423
2256
|
};
|
|
@@ -2441,103 +2274,445 @@ var UnsupportedMediaTypeException = class extends BaseFieldException {
|
|
|
2441
2274
|
}
|
|
2442
2275
|
};
|
|
2443
2276
|
|
|
2444
|
-
// src/exceptions/
|
|
2277
|
+
// src/exceptions/validation.exception.ts
|
|
2445
2278
|
import { HttpStatus as HttpStatus19 } from "@nestjs/common";
|
|
2446
|
-
var
|
|
2279
|
+
var ValidationException = class extends BaseFieldException {
|
|
2447
2280
|
static {
|
|
2448
|
-
__name(this, "
|
|
2281
|
+
__name(this, "ValidationException");
|
|
2449
2282
|
}
|
|
2450
|
-
constructor(
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2283
|
+
constructor(errors, detail) {
|
|
2284
|
+
super(errors, HttpStatus19.BAD_REQUEST, detail);
|
|
2285
|
+
}
|
|
2286
|
+
};
|
|
2287
|
+
|
|
2288
|
+
// src/filters/http-exception.filter.ts
|
|
2289
|
+
import { Catch, HttpException as HttpException2, HttpStatus as HttpStatus20, Logger as Logger9 } from "@nestjs/common";
|
|
2290
|
+
function _ts_decorate11(decorators, target, key, desc) {
|
|
2291
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
2292
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
2293
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
2294
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
2295
|
+
}
|
|
2296
|
+
__name(_ts_decorate11, "_ts_decorate");
|
|
2297
|
+
function getHttpStatusTitle(status) {
|
|
2298
|
+
const enumKey = Object.entries(HttpStatus20).find(([key, value]) => value === status && Number.isNaN(Number(key)))?.[0];
|
|
2299
|
+
if (!enumKey) {
|
|
2300
|
+
return "Error";
|
|
2301
|
+
}
|
|
2302
|
+
return enumKey.split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
|
|
2303
|
+
}
|
|
2304
|
+
__name(getHttpStatusTitle, "getHttpStatusTitle");
|
|
2305
|
+
var HttpExceptionFilter = class _HttpExceptionFilter {
|
|
2306
|
+
static {
|
|
2307
|
+
__name(this, "HttpExceptionFilter");
|
|
2308
|
+
}
|
|
2309
|
+
logger = new Logger9(_HttpExceptionFilter.name);
|
|
2310
|
+
catch(exception, host) {
|
|
2311
|
+
const ctx = host.switchToHttp();
|
|
2312
|
+
const response = ctx.getResponse();
|
|
2313
|
+
let status = HttpStatus20.INTERNAL_SERVER_ERROR;
|
|
2314
|
+
let errors = [];
|
|
2315
|
+
let detail = "Internal server error";
|
|
2316
|
+
if (exception instanceof HttpException2) {
|
|
2317
|
+
status = exception.getStatus();
|
|
2318
|
+
const exceptionResponse = exception.getResponse();
|
|
2319
|
+
if (typeof exceptionResponse === "object" && exceptionResponse !== null) {
|
|
2320
|
+
const responseObj = exceptionResponse;
|
|
2321
|
+
if ("errors" in responseObj && Array.isArray(responseObj.errors)) {
|
|
2322
|
+
errors = responseObj.errors;
|
|
2323
|
+
detail = ("detail" in responseObj ? responseObj.detail : void 0) || exception.message;
|
|
2324
|
+
} else if ("message" in responseObj && Array.isArray(responseObj.message)) {
|
|
2325
|
+
errors = responseObj.message.map((msg) => {
|
|
2326
|
+
if (typeof msg === "object" && "property" in msg && "constraints" in msg) {
|
|
2327
|
+
const constraintValues = Object.values(msg.constraints);
|
|
2328
|
+
return {
|
|
2329
|
+
field: msg.property,
|
|
2330
|
+
message: constraintValues[0] ?? "Validation failed"
|
|
2331
|
+
};
|
|
2332
|
+
}
|
|
2333
|
+
return {
|
|
2334
|
+
message: typeof msg === "string" ? msg : JSON.stringify(msg)
|
|
2335
|
+
};
|
|
2336
|
+
});
|
|
2337
|
+
detail = "Validation failed";
|
|
2338
|
+
} else if ("message" in responseObj) {
|
|
2339
|
+
const message = responseObj.message;
|
|
2340
|
+
errors = [
|
|
2341
|
+
{
|
|
2342
|
+
message: Array.isArray(message) ? message.join(", ") : message
|
|
2343
|
+
}
|
|
2344
|
+
];
|
|
2345
|
+
detail = ("error" in responseObj ? responseObj.error : void 0) || exception.message;
|
|
2346
|
+
}
|
|
2347
|
+
} else if (typeof exceptionResponse === "string") {
|
|
2348
|
+
errors = [
|
|
2349
|
+
{
|
|
2350
|
+
message: exceptionResponse
|
|
2351
|
+
}
|
|
2352
|
+
];
|
|
2353
|
+
detail = exceptionResponse;
|
|
2354
|
+
}
|
|
2457
2355
|
} else {
|
|
2458
|
-
|
|
2356
|
+
const errorMessage = exception instanceof Error ? exception.message : "Unknown error";
|
|
2357
|
+
const stack = exception instanceof Error ? exception.stack : void 0;
|
|
2358
|
+
this.logger.error(`Unexpected error: ${errorMessage}`, stack);
|
|
2359
|
+
errors = [
|
|
2360
|
+
{
|
|
2361
|
+
message: "An unexpected error occurred"
|
|
2362
|
+
}
|
|
2363
|
+
];
|
|
2459
2364
|
}
|
|
2365
|
+
const problemDetails = {
|
|
2366
|
+
title: getHttpStatusTitle(status),
|
|
2367
|
+
status,
|
|
2368
|
+
detail,
|
|
2369
|
+
errors
|
|
2370
|
+
};
|
|
2371
|
+
response.status(status).send(problemDetails);
|
|
2460
2372
|
}
|
|
2461
2373
|
};
|
|
2374
|
+
HttpExceptionFilter = _ts_decorate11([
|
|
2375
|
+
Catch()
|
|
2376
|
+
], HttpExceptionFilter);
|
|
2462
2377
|
|
|
2463
|
-
// src/
|
|
2464
|
-
import {
|
|
2465
|
-
|
|
2378
|
+
// src/http/guards/csrf.guard.ts
|
|
2379
|
+
import { ForbiddenException as ForbiddenException2, Injectable as Injectable8, Logger as Logger10 } from "@nestjs/common";
|
|
2380
|
+
function _ts_decorate12(decorators, target, key, desc) {
|
|
2381
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
2382
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
2383
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
2384
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
2385
|
+
}
|
|
2386
|
+
__name(_ts_decorate12, "_ts_decorate");
|
|
2387
|
+
var CsrfGuard = class _CsrfGuard {
|
|
2466
2388
|
static {
|
|
2467
|
-
__name(this, "
|
|
2389
|
+
__name(this, "CsrfGuard");
|
|
2468
2390
|
}
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2391
|
+
logger = new Logger10(_CsrfGuard.name);
|
|
2392
|
+
async canActivate(context) {
|
|
2393
|
+
const request = context.switchToHttp().getRequest();
|
|
2394
|
+
const reply = context.switchToHttp().getResponse();
|
|
2395
|
+
const safeMethods = [
|
|
2396
|
+
"GET",
|
|
2397
|
+
"HEAD",
|
|
2398
|
+
"OPTIONS"
|
|
2399
|
+
];
|
|
2400
|
+
if (safeMethods.includes(request.method)) {
|
|
2401
|
+
return true;
|
|
2402
|
+
}
|
|
2403
|
+
try {
|
|
2404
|
+
const fastifyInstance = request.server;
|
|
2405
|
+
if (!fastifyInstance.csrfProtection) {
|
|
2406
|
+
this.logger.error("CSRF protection plugin not found. Ensure @fastify/csrf-protection is registered.");
|
|
2407
|
+
throw new ForbiddenException2("CSRF protection not configured");
|
|
2408
|
+
}
|
|
2409
|
+
await new Promise((resolve, reject) => {
|
|
2410
|
+
fastifyInstance.csrfProtection(request, reply, (err) => {
|
|
2411
|
+
if (err) {
|
|
2412
|
+
reject(err);
|
|
2413
|
+
} else {
|
|
2414
|
+
resolve();
|
|
2415
|
+
}
|
|
2416
|
+
});
|
|
2417
|
+
});
|
|
2418
|
+
this.logger.debug(`CSRF validation successful for ${request.method} ${request.url}`);
|
|
2419
|
+
return true;
|
|
2420
|
+
} catch (error) {
|
|
2421
|
+
this.logger.warn(`CSRF validation failed for ${request.method} ${request.url}: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
2422
|
+
throw new ForbiddenException2({
|
|
2423
|
+
errors: [
|
|
2424
|
+
{
|
|
2425
|
+
field: "csrf",
|
|
2426
|
+
message: "Invalid or missing CSRF token"
|
|
2427
|
+
}
|
|
2428
|
+
],
|
|
2429
|
+
message: "CSRF validation failed"
|
|
2430
|
+
});
|
|
2431
|
+
}
|
|
2432
|
+
}
|
|
2433
|
+
};
|
|
2434
|
+
CsrfGuard = _ts_decorate12([
|
|
2435
|
+
Injectable8()
|
|
2436
|
+
], CsrfGuard);
|
|
2437
|
+
|
|
2438
|
+
// src/http/http.module.ts
|
|
2439
|
+
import { Module as Module4 } from "@nestjs/common";
|
|
2440
|
+
function _ts_decorate13(decorators, target, key, desc) {
|
|
2441
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
2442
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
2443
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
2444
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
2445
|
+
}
|
|
2446
|
+
__name(_ts_decorate13, "_ts_decorate");
|
|
2447
|
+
var HttpModule = class {
|
|
2448
|
+
static {
|
|
2449
|
+
__name(this, "HttpModule");
|
|
2450
|
+
}
|
|
2451
|
+
};
|
|
2452
|
+
HttpModule = _ts_decorate13([
|
|
2453
|
+
Module4({
|
|
2454
|
+
providers: [
|
|
2455
|
+
CsrfGuard
|
|
2456
|
+
],
|
|
2457
|
+
exports: [
|
|
2458
|
+
CsrfGuard
|
|
2459
|
+
]
|
|
2460
|
+
})
|
|
2461
|
+
], HttpModule);
|
|
2462
|
+
|
|
2463
|
+
// src/logger/interceptors/http-logger.interceptor.ts
|
|
2464
|
+
import { Injectable as Injectable9, Optional } from "@nestjs/common";
|
|
2465
|
+
import { catchError, tap as tap2 } from "rxjs/operators";
|
|
2466
|
+
|
|
2467
|
+
// src/logger/utils/index.ts
|
|
2468
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
2469
|
+
import { randomUUID } from "crypto";
|
|
2470
|
+
var correlationStorage = new AsyncLocalStorage();
|
|
2471
|
+
function getCorrelationContext() {
|
|
2472
|
+
return correlationStorage.getStore();
|
|
2473
|
+
}
|
|
2474
|
+
__name(getCorrelationContext, "getCorrelationContext");
|
|
2475
|
+
function runWithCorrelationContext(context, callback) {
|
|
2476
|
+
return correlationStorage.run(context, callback);
|
|
2477
|
+
}
|
|
2478
|
+
__name(runWithCorrelationContext, "runWithCorrelationContext");
|
|
2479
|
+
function updateCorrelationContext(updates) {
|
|
2480
|
+
const context = correlationStorage.getStore();
|
|
2481
|
+
if (context) {
|
|
2482
|
+
Object.assign(context, updates);
|
|
2483
|
+
}
|
|
2484
|
+
}
|
|
2485
|
+
__name(updateCorrelationContext, "updateCorrelationContext");
|
|
2486
|
+
var DEFAULT_CORRELATION_HEADER = "x-correlation-id";
|
|
2487
|
+
function generateCorrelationId() {
|
|
2488
|
+
return randomUUID();
|
|
2489
|
+
}
|
|
2490
|
+
__name(generateCorrelationId, "generateCorrelationId");
|
|
2491
|
+
function addCorrelationIdToResponse(reply, correlationId, headerName = DEFAULT_CORRELATION_HEADER) {
|
|
2492
|
+
if (typeof reply.header === "function") {
|
|
2493
|
+
reply.header(headerName, correlationId);
|
|
2494
|
+
} else if (reply.raw && typeof reply.raw.setHeader === "function") {
|
|
2495
|
+
reply.raw.setHeader(headerName, correlationId);
|
|
2496
|
+
}
|
|
2497
|
+
}
|
|
2498
|
+
__name(addCorrelationIdToResponse, "addCorrelationIdToResponse");
|
|
2499
|
+
|
|
2500
|
+
// src/logger/interceptors/http-logger.interceptor.ts
|
|
2501
|
+
function _ts_decorate14(decorators, target, key, desc) {
|
|
2502
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
2503
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
2504
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
2505
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
2506
|
+
}
|
|
2507
|
+
__name(_ts_decorate14, "_ts_decorate");
|
|
2508
|
+
function _ts_metadata7(k, v) {
|
|
2509
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
2510
|
+
}
|
|
2511
|
+
__name(_ts_metadata7, "_ts_metadata");
|
|
2512
|
+
function _ts_param4(paramIndex, decorator) {
|
|
2513
|
+
return function(target, key) {
|
|
2514
|
+
decorator(target, key, paramIndex);
|
|
2515
|
+
};
|
|
2516
|
+
}
|
|
2517
|
+
__name(_ts_param4, "_ts_param");
|
|
2518
|
+
var HttpLoggerInterceptor = class {
|
|
2519
|
+
static {
|
|
2520
|
+
__name(this, "HttpLoggerInterceptor");
|
|
2521
|
+
}
|
|
2522
|
+
logger;
|
|
2523
|
+
enableRequestLog;
|
|
2524
|
+
enableResponseLog;
|
|
2525
|
+
slowRequestThreshold;
|
|
2526
|
+
constructor(logger, options) {
|
|
2527
|
+
this.logger = logger;
|
|
2528
|
+
this.enableRequestLog = options?.enableRequestLog ?? true;
|
|
2529
|
+
this.enableResponseLog = options?.enableResponseLog ?? true;
|
|
2530
|
+
this.slowRequestThreshold = options?.slowRequestThreshold ?? 3e3;
|
|
2531
|
+
}
|
|
2532
|
+
intercept(context, next) {
|
|
2533
|
+
if (context.getType() !== "http") {
|
|
2534
|
+
return next.handle();
|
|
2535
|
+
}
|
|
2536
|
+
const httpContext = context.switchToHttp();
|
|
2537
|
+
const request = httpContext.getRequest();
|
|
2538
|
+
const response = httpContext.getResponse();
|
|
2539
|
+
const startTime = Date.now();
|
|
2540
|
+
if (this.enableRequestLog) {
|
|
2541
|
+
this.logRequest(request);
|
|
2542
|
+
}
|
|
2543
|
+
return next.handle().pipe(tap2(() => {
|
|
2544
|
+
if (this.enableResponseLog) {
|
|
2545
|
+
const duration = Date.now() - startTime;
|
|
2546
|
+
this.logResponse(request, response, duration);
|
|
2547
|
+
}
|
|
2548
|
+
}), catchError((error) => {
|
|
2549
|
+
const duration = Date.now() - startTime;
|
|
2550
|
+
this.logError(request, response, duration, error);
|
|
2551
|
+
throw error;
|
|
2552
|
+
}));
|
|
2553
|
+
}
|
|
2554
|
+
logRequest(request) {
|
|
2555
|
+
try {
|
|
2556
|
+
const correlationContext = getCorrelationContext();
|
|
2557
|
+
const metadata = {
|
|
2558
|
+
type: "http_request",
|
|
2559
|
+
method: request.method,
|
|
2560
|
+
url: request.url,
|
|
2561
|
+
correlationId: correlationContext?.correlationId,
|
|
2562
|
+
ip: request.ip,
|
|
2563
|
+
userAgent: request.headers["user-agent"]
|
|
2564
|
+
};
|
|
2565
|
+
this.logger.logWithMetadata("log", `Incoming ${request.method} ${request.url}`, metadata);
|
|
2566
|
+
} catch (error) {
|
|
2567
|
+
this.logger.error("Failed to log HTTP request", error.stack);
|
|
2568
|
+
}
|
|
2569
|
+
}
|
|
2570
|
+
logResponse(request, response, duration) {
|
|
2571
|
+
try {
|
|
2572
|
+
const correlationContext = getCorrelationContext();
|
|
2573
|
+
const statusCode = response.statusCode;
|
|
2574
|
+
const logLevel = statusCode >= 500 ? "error" : statusCode >= 400 ? "warn" : "log";
|
|
2575
|
+
const metadata = {
|
|
2576
|
+
type: "http_response",
|
|
2577
|
+
method: request.method,
|
|
2578
|
+
url: request.url,
|
|
2579
|
+
statusCode,
|
|
2580
|
+
duration,
|
|
2581
|
+
correlationId: correlationContext?.correlationId
|
|
2582
|
+
};
|
|
2583
|
+
if (duration > this.slowRequestThreshold) {
|
|
2584
|
+
metadata.slowRequest = true;
|
|
2585
|
+
}
|
|
2586
|
+
const message = metadata.slowRequest ? `SLOW ${request.method} ${request.url} ${statusCode} - ${duration}ms` : `${request.method} ${request.url} ${statusCode} - ${duration}ms`;
|
|
2587
|
+
this.logger.logWithMetadata(logLevel, message, metadata);
|
|
2588
|
+
} catch (error) {
|
|
2589
|
+
this.logger.error("Failed to log HTTP response", error.stack);
|
|
2590
|
+
}
|
|
2591
|
+
}
|
|
2592
|
+
logError(request, response, duration, error) {
|
|
2593
|
+
try {
|
|
2594
|
+
const correlationContext = getCorrelationContext();
|
|
2595
|
+
const statusCode = response.statusCode || 500;
|
|
2596
|
+
const metadata = {
|
|
2597
|
+
type: "http_error",
|
|
2598
|
+
method: request.method,
|
|
2599
|
+
url: request.url,
|
|
2600
|
+
statusCode,
|
|
2601
|
+
duration,
|
|
2602
|
+
correlationId: correlationContext?.correlationId,
|
|
2603
|
+
errorName: error?.name || "Error",
|
|
2604
|
+
errorMessage: error?.message || "Unknown error"
|
|
2605
|
+
};
|
|
2606
|
+
if (error?.stack) {
|
|
2607
|
+
metadata.trace = error.stack;
|
|
2608
|
+
}
|
|
2609
|
+
if (error?.response) {
|
|
2610
|
+
metadata.errorDetails = error.response;
|
|
2611
|
+
}
|
|
2612
|
+
const message = `ERROR ${request.method} ${request.url} ${statusCode} - ${error?.message || "Unknown error"}`;
|
|
2613
|
+
this.logger.logWithMetadata("error", message, metadata);
|
|
2614
|
+
} catch (loggingError) {
|
|
2615
|
+
this.logger.error("Failed to log HTTP error", loggingError.stack);
|
|
2616
|
+
}
|
|
2617
|
+
}
|
|
2618
|
+
};
|
|
2619
|
+
HttpLoggerInterceptor = _ts_decorate14([
|
|
2620
|
+
Injectable9(),
|
|
2621
|
+
_ts_param4(1, Optional()),
|
|
2622
|
+
_ts_metadata7("design:type", Function),
|
|
2623
|
+
_ts_metadata7("design:paramtypes", [
|
|
2624
|
+
typeof LoggerService === "undefined" ? Object : LoggerService,
|
|
2625
|
+
typeof HttpLoggerOptions === "undefined" ? Object : HttpLoggerOptions
|
|
2626
|
+
])
|
|
2627
|
+
], HttpLoggerInterceptor);
|
|
2628
|
+
|
|
2629
|
+
// src/logger/logger.module.ts
|
|
2630
|
+
import { Global as Global4, Logger as Logger11, Module as Module5 } from "@nestjs/common";
|
|
2631
|
+
|
|
2632
|
+
// src/logger/middleware/correlation-id.middleware.ts
|
|
2633
|
+
import { Injectable as Injectable10 } from "@nestjs/common";
|
|
2634
|
+
function _ts_decorate15(decorators, target, key, desc) {
|
|
2635
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
2636
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
2637
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
2638
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
2639
|
+
}
|
|
2640
|
+
__name(_ts_decorate15, "_ts_decorate");
|
|
2641
|
+
function _ts_metadata8(k, v) {
|
|
2642
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
2643
|
+
}
|
|
2644
|
+
__name(_ts_metadata8, "_ts_metadata");
|
|
2645
|
+
var CorrelationIdMiddleware = class {
|
|
2646
|
+
static {
|
|
2647
|
+
__name(this, "CorrelationIdMiddleware");
|
|
2648
|
+
}
|
|
2649
|
+
includeInResponse;
|
|
2650
|
+
responseHeader;
|
|
2651
|
+
constructor(options = {}) {
|
|
2652
|
+
this.includeInResponse = options.includeInResponse ?? true;
|
|
2653
|
+
this.responseHeader = options.responseHeader ?? DEFAULT_CORRELATION_HEADER;
|
|
2654
|
+
}
|
|
2655
|
+
/**
|
|
2656
|
+
* Middleware handler for processing requests.
|
|
2657
|
+
*/
|
|
2658
|
+
use(_req, reply, next) {
|
|
2659
|
+
const correlationId = generateCorrelationId();
|
|
2660
|
+
if (this.includeInResponse) {
|
|
2661
|
+
addCorrelationIdToResponse(reply, correlationId, this.responseHeader);
|
|
2662
|
+
}
|
|
2663
|
+
runWithCorrelationContext({
|
|
2664
|
+
correlationId
|
|
2665
|
+
}, () => {
|
|
2666
|
+
next();
|
|
2667
|
+
});
|
|
2668
|
+
}
|
|
2669
|
+
/**
|
|
2670
|
+
* Fastify hook handler for onRequest.
|
|
2671
|
+
* This is an async function that returns a Promise, ensuring the AsyncLocalStorage
|
|
2672
|
+
* context persists throughout the entire request lifecycle.
|
|
2673
|
+
*/
|
|
2674
|
+
async onRequest(_req, reply) {
|
|
2675
|
+
const correlationId = generateCorrelationId();
|
|
2676
|
+
if (this.includeInResponse) {
|
|
2677
|
+
addCorrelationIdToResponse(reply, correlationId, this.responseHeader);
|
|
2678
|
+
}
|
|
2679
|
+
const store = correlationStorage.getStore();
|
|
2680
|
+
if (!store) {
|
|
2681
|
+
correlationStorage.enterWith({
|
|
2682
|
+
correlationId
|
|
2683
|
+
});
|
|
2478
2684
|
}
|
|
2479
2685
|
}
|
|
2480
2686
|
};
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2687
|
+
CorrelationIdMiddleware = _ts_decorate15([
|
|
2688
|
+
Injectable10(),
|
|
2689
|
+
_ts_metadata8("design:type", Function),
|
|
2690
|
+
_ts_metadata8("design:paramtypes", [
|
|
2691
|
+
typeof CorrelationIdMiddlewareOptions === "undefined" ? Object : CorrelationIdMiddlewareOptions
|
|
2692
|
+
])
|
|
2693
|
+
], CorrelationIdMiddleware);
|
|
2484
2694
|
|
|
2485
2695
|
// src/logger/services/logger.service.ts
|
|
2486
|
-
import { Injectable as
|
|
2696
|
+
import { Injectable as Injectable11, Optional as Optional2 } from "@nestjs/common";
|
|
2487
2697
|
import { createLogger, format, transports } from "winston";
|
|
2488
2698
|
import DailyRotateFile from "winston-daily-rotate-file";
|
|
2489
|
-
|
|
2490
|
-
// src/logger/utils/index.ts
|
|
2491
|
-
import { AsyncLocalStorage } from "async_hooks";
|
|
2492
|
-
import { randomUUID } from "crypto";
|
|
2493
|
-
var correlationStorage = new AsyncLocalStorage();
|
|
2494
|
-
function getCorrelationContext() {
|
|
2495
|
-
return correlationStorage.getStore();
|
|
2496
|
-
}
|
|
2497
|
-
__name(getCorrelationContext, "getCorrelationContext");
|
|
2498
|
-
function runWithCorrelationContext(context, callback) {
|
|
2499
|
-
return correlationStorage.run(context, callback);
|
|
2500
|
-
}
|
|
2501
|
-
__name(runWithCorrelationContext, "runWithCorrelationContext");
|
|
2502
|
-
function updateCorrelationContext(updates) {
|
|
2503
|
-
const context = correlationStorage.getStore();
|
|
2504
|
-
if (context) {
|
|
2505
|
-
Object.assign(context, updates);
|
|
2506
|
-
}
|
|
2507
|
-
}
|
|
2508
|
-
__name(updateCorrelationContext, "updateCorrelationContext");
|
|
2509
|
-
var DEFAULT_CORRELATION_HEADER = "x-correlation-id";
|
|
2510
|
-
function generateCorrelationId() {
|
|
2511
|
-
return randomUUID();
|
|
2512
|
-
}
|
|
2513
|
-
__name(generateCorrelationId, "generateCorrelationId");
|
|
2514
|
-
function addCorrelationIdToResponse(reply, correlationId, headerName = DEFAULT_CORRELATION_HEADER) {
|
|
2515
|
-
if (typeof reply.header === "function") {
|
|
2516
|
-
reply.header(headerName, correlationId);
|
|
2517
|
-
} else if (reply.raw && typeof reply.raw.setHeader === "function") {
|
|
2518
|
-
reply.raw.setHeader(headerName, correlationId);
|
|
2519
|
-
}
|
|
2520
|
-
}
|
|
2521
|
-
__name(addCorrelationIdToResponse, "addCorrelationIdToResponse");
|
|
2522
|
-
|
|
2523
|
-
// src/logger/services/logger.service.ts
|
|
2524
|
-
function _ts_decorate14(decorators, target, key, desc) {
|
|
2699
|
+
function _ts_decorate16(decorators, target, key, desc) {
|
|
2525
2700
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
2526
2701
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
2527
2702
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
2528
2703
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
2529
2704
|
}
|
|
2530
|
-
__name(
|
|
2531
|
-
function
|
|
2705
|
+
__name(_ts_decorate16, "_ts_decorate");
|
|
2706
|
+
function _ts_metadata9(k, v) {
|
|
2532
2707
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
2533
2708
|
}
|
|
2534
|
-
__name(
|
|
2535
|
-
function
|
|
2709
|
+
__name(_ts_metadata9, "_ts_metadata");
|
|
2710
|
+
function _ts_param5(paramIndex, decorator) {
|
|
2536
2711
|
return function(target, key) {
|
|
2537
2712
|
decorator(target, key, paramIndex);
|
|
2538
2713
|
};
|
|
2539
2714
|
}
|
|
2540
|
-
__name(
|
|
2715
|
+
__name(_ts_param5, "_ts_param");
|
|
2541
2716
|
var LoggerService2 = class _LoggerService {
|
|
2542
2717
|
static {
|
|
2543
2718
|
__name(this, "LoggerService");
|
|
@@ -2590,7 +2765,8 @@ var LoggerService2 = class _LoggerService {
|
|
|
2590
2765
|
].filter(Boolean);
|
|
2591
2766
|
let output = parts.join(" ");
|
|
2592
2767
|
if (trace) {
|
|
2593
|
-
output +=
|
|
2768
|
+
output += `
|
|
2769
|
+
${trace}`;
|
|
2594
2770
|
}
|
|
2595
2771
|
return output;
|
|
2596
2772
|
}), format.colorize({
|
|
@@ -2738,210 +2914,16 @@ var LoggerService2 = class _LoggerService {
|
|
|
2738
2914
|
return childLogger;
|
|
2739
2915
|
}
|
|
2740
2916
|
};
|
|
2741
|
-
LoggerService2 =
|
|
2742
|
-
Injectable9(),
|
|
2743
|
-
_ts_param4(0, Optional()),
|
|
2744
|
-
_ts_param4(1, Optional()),
|
|
2745
|
-
_ts_metadata8("design:type", Function),
|
|
2746
|
-
_ts_metadata8("design:paramtypes", [
|
|
2747
|
-
typeof LoggerModuleOptions === "undefined" ? Object : LoggerModuleOptions,
|
|
2748
|
-
typeof Logger10 === "undefined" ? Object : Logger10
|
|
2749
|
-
])
|
|
2750
|
-
], LoggerService2);
|
|
2751
|
-
|
|
2752
|
-
// src/logger/middleware/correlation-id.middleware.ts
|
|
2753
|
-
import { Injectable as Injectable10 } from "@nestjs/common";
|
|
2754
|
-
function _ts_decorate15(decorators, target, key, desc) {
|
|
2755
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
2756
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
2757
|
-
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
2758
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
2759
|
-
}
|
|
2760
|
-
__name(_ts_decorate15, "_ts_decorate");
|
|
2761
|
-
function _ts_metadata9(k, v) {
|
|
2762
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
2763
|
-
}
|
|
2764
|
-
__name(_ts_metadata9, "_ts_metadata");
|
|
2765
|
-
var CorrelationIdMiddleware = class {
|
|
2766
|
-
static {
|
|
2767
|
-
__name(this, "CorrelationIdMiddleware");
|
|
2768
|
-
}
|
|
2769
|
-
includeInResponse;
|
|
2770
|
-
responseHeader;
|
|
2771
|
-
constructor(options = {}) {
|
|
2772
|
-
this.includeInResponse = options.includeInResponse ?? true;
|
|
2773
|
-
this.responseHeader = options.responseHeader ?? DEFAULT_CORRELATION_HEADER;
|
|
2774
|
-
}
|
|
2775
|
-
/**
|
|
2776
|
-
* Middleware handler for processing requests.
|
|
2777
|
-
*/
|
|
2778
|
-
use(req, reply, next) {
|
|
2779
|
-
const correlationId = generateCorrelationId();
|
|
2780
|
-
if (this.includeInResponse) {
|
|
2781
|
-
addCorrelationIdToResponse(reply, correlationId, this.responseHeader);
|
|
2782
|
-
}
|
|
2783
|
-
runWithCorrelationContext({
|
|
2784
|
-
correlationId
|
|
2785
|
-
}, () => {
|
|
2786
|
-
next();
|
|
2787
|
-
});
|
|
2788
|
-
}
|
|
2789
|
-
/**
|
|
2790
|
-
* Fastify hook handler for onRequest.
|
|
2791
|
-
* This is an async function that returns a Promise, ensuring the AsyncLocalStorage
|
|
2792
|
-
* context persists throughout the entire request lifecycle.
|
|
2793
|
-
*/
|
|
2794
|
-
async onRequest(req, reply) {
|
|
2795
|
-
const correlationId = generateCorrelationId();
|
|
2796
|
-
if (this.includeInResponse) {
|
|
2797
|
-
addCorrelationIdToResponse(reply, correlationId, this.responseHeader);
|
|
2798
|
-
}
|
|
2799
|
-
const store = correlationStorage.getStore();
|
|
2800
|
-
if (!store) {
|
|
2801
|
-
correlationStorage.enterWith({
|
|
2802
|
-
correlationId
|
|
2803
|
-
});
|
|
2804
|
-
}
|
|
2805
|
-
}
|
|
2806
|
-
};
|
|
2807
|
-
CorrelationIdMiddleware = _ts_decorate15([
|
|
2808
|
-
Injectable10(),
|
|
2809
|
-
_ts_metadata9("design:type", Function),
|
|
2810
|
-
_ts_metadata9("design:paramtypes", [
|
|
2811
|
-
typeof CorrelationIdMiddlewareOptions === "undefined" ? Object : CorrelationIdMiddlewareOptions
|
|
2812
|
-
])
|
|
2813
|
-
], CorrelationIdMiddleware);
|
|
2814
|
-
|
|
2815
|
-
// src/logger/interceptors/http-logger.interceptor.ts
|
|
2816
|
-
import { Injectable as Injectable11, Optional as Optional2 } from "@nestjs/common";
|
|
2817
|
-
import { tap as tap2, catchError } from "rxjs/operators";
|
|
2818
|
-
function _ts_decorate16(decorators, target, key, desc) {
|
|
2819
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
2820
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
2821
|
-
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;
|
|
2822
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
2823
|
-
}
|
|
2824
|
-
__name(_ts_decorate16, "_ts_decorate");
|
|
2825
|
-
function _ts_metadata10(k, v) {
|
|
2826
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
2827
|
-
}
|
|
2828
|
-
__name(_ts_metadata10, "_ts_metadata");
|
|
2829
|
-
function _ts_param5(paramIndex, decorator) {
|
|
2830
|
-
return function(target, key) {
|
|
2831
|
-
decorator(target, key, paramIndex);
|
|
2832
|
-
};
|
|
2833
|
-
}
|
|
2834
|
-
__name(_ts_param5, "_ts_param");
|
|
2835
|
-
var HttpLoggerInterceptor = class {
|
|
2836
|
-
static {
|
|
2837
|
-
__name(this, "HttpLoggerInterceptor");
|
|
2838
|
-
}
|
|
2839
|
-
logger;
|
|
2840
|
-
enableRequestLog;
|
|
2841
|
-
enableResponseLog;
|
|
2842
|
-
slowRequestThreshold;
|
|
2843
|
-
constructor(logger, options) {
|
|
2844
|
-
this.logger = logger;
|
|
2845
|
-
this.enableRequestLog = options?.enableRequestLog ?? true;
|
|
2846
|
-
this.enableResponseLog = options?.enableResponseLog ?? true;
|
|
2847
|
-
this.slowRequestThreshold = options?.slowRequestThreshold ?? 3e3;
|
|
2848
|
-
}
|
|
2849
|
-
intercept(context, next) {
|
|
2850
|
-
if (context.getType() !== "http") {
|
|
2851
|
-
return next.handle();
|
|
2852
|
-
}
|
|
2853
|
-
const httpContext = context.switchToHttp();
|
|
2854
|
-
const request = httpContext.getRequest();
|
|
2855
|
-
const response = httpContext.getResponse();
|
|
2856
|
-
const startTime = Date.now();
|
|
2857
|
-
if (this.enableRequestLog) {
|
|
2858
|
-
this.logRequest(request);
|
|
2859
|
-
}
|
|
2860
|
-
return next.handle().pipe(tap2(() => {
|
|
2861
|
-
if (this.enableResponseLog) {
|
|
2862
|
-
const duration = Date.now() - startTime;
|
|
2863
|
-
this.logResponse(request, response, duration);
|
|
2864
|
-
}
|
|
2865
|
-
}), catchError((error) => {
|
|
2866
|
-
const duration = Date.now() - startTime;
|
|
2867
|
-
this.logError(request, response, duration, error);
|
|
2868
|
-
throw error;
|
|
2869
|
-
}));
|
|
2870
|
-
}
|
|
2871
|
-
logRequest(request) {
|
|
2872
|
-
try {
|
|
2873
|
-
const correlationContext = getCorrelationContext();
|
|
2874
|
-
const metadata = {
|
|
2875
|
-
type: "http_request",
|
|
2876
|
-
method: request.method,
|
|
2877
|
-
url: request.url,
|
|
2878
|
-
correlationId: correlationContext?.correlationId,
|
|
2879
|
-
ip: request.ip,
|
|
2880
|
-
userAgent: request.headers["user-agent"]
|
|
2881
|
-
};
|
|
2882
|
-
this.logger.logWithMetadata("log", `Incoming ${request.method} ${request.url}`, metadata);
|
|
2883
|
-
} catch (error) {
|
|
2884
|
-
this.logger.error("Failed to log HTTP request", error.stack);
|
|
2885
|
-
}
|
|
2886
|
-
}
|
|
2887
|
-
logResponse(request, response, duration) {
|
|
2888
|
-
try {
|
|
2889
|
-
const correlationContext = getCorrelationContext();
|
|
2890
|
-
const statusCode = response.statusCode;
|
|
2891
|
-
const logLevel = statusCode >= 500 ? "error" : statusCode >= 400 ? "warn" : "log";
|
|
2892
|
-
const metadata = {
|
|
2893
|
-
type: "http_response",
|
|
2894
|
-
method: request.method,
|
|
2895
|
-
url: request.url,
|
|
2896
|
-
statusCode,
|
|
2897
|
-
duration,
|
|
2898
|
-
correlationId: correlationContext?.correlationId
|
|
2899
|
-
};
|
|
2900
|
-
if (duration > this.slowRequestThreshold) {
|
|
2901
|
-
metadata.slowRequest = true;
|
|
2902
|
-
}
|
|
2903
|
-
const message = metadata.slowRequest ? `SLOW ${request.method} ${request.url} ${statusCode} - ${duration}ms` : `${request.method} ${request.url} ${statusCode} - ${duration}ms`;
|
|
2904
|
-
this.logger.logWithMetadata(logLevel, message, metadata);
|
|
2905
|
-
} catch (error) {
|
|
2906
|
-
this.logger.error("Failed to log HTTP response", error.stack);
|
|
2907
|
-
}
|
|
2908
|
-
}
|
|
2909
|
-
logError(request, response, duration, error) {
|
|
2910
|
-
try {
|
|
2911
|
-
const correlationContext = getCorrelationContext();
|
|
2912
|
-
const statusCode = response.statusCode || 500;
|
|
2913
|
-
const metadata = {
|
|
2914
|
-
type: "http_error",
|
|
2915
|
-
method: request.method,
|
|
2916
|
-
url: request.url,
|
|
2917
|
-
statusCode,
|
|
2918
|
-
duration,
|
|
2919
|
-
correlationId: correlationContext?.correlationId,
|
|
2920
|
-
errorName: error?.name || "Error",
|
|
2921
|
-
errorMessage: error?.message || "Unknown error"
|
|
2922
|
-
};
|
|
2923
|
-
if (error?.stack) {
|
|
2924
|
-
metadata.trace = error.stack;
|
|
2925
|
-
}
|
|
2926
|
-
if (error?.response) {
|
|
2927
|
-
metadata.errorDetails = error.response;
|
|
2928
|
-
}
|
|
2929
|
-
const message = `ERROR ${request.method} ${request.url} ${statusCode} - ${error?.message || "Unknown error"}`;
|
|
2930
|
-
this.logger.logWithMetadata("error", message, metadata);
|
|
2931
|
-
} catch (loggingError) {
|
|
2932
|
-
this.logger.error("Failed to log HTTP error", loggingError.stack);
|
|
2933
|
-
}
|
|
2934
|
-
}
|
|
2935
|
-
};
|
|
2936
|
-
HttpLoggerInterceptor = _ts_decorate16([
|
|
2917
|
+
LoggerService2 = _ts_decorate16([
|
|
2937
2918
|
Injectable11(),
|
|
2919
|
+
_ts_param5(0, Optional2()),
|
|
2938
2920
|
_ts_param5(1, Optional2()),
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
typeof
|
|
2942
|
-
typeof
|
|
2921
|
+
_ts_metadata9("design:type", Function),
|
|
2922
|
+
_ts_metadata9("design:paramtypes", [
|
|
2923
|
+
typeof LoggerModuleOptions === "undefined" ? Object : LoggerModuleOptions,
|
|
2924
|
+
typeof Logger === "undefined" ? Object : Logger
|
|
2943
2925
|
])
|
|
2944
|
-
],
|
|
2926
|
+
], LoggerService2);
|
|
2945
2927
|
|
|
2946
2928
|
// src/logger/logger.module.ts
|
|
2947
2929
|
function _ts_decorate17(decorators, target, key, desc) {
|
|
@@ -3214,7 +3196,7 @@ var LoggerModule = class _LoggerModule {
|
|
|
3214
3196
|
* ```
|
|
3215
3197
|
*/
|
|
3216
3198
|
static forRootAsync(options) {
|
|
3217
|
-
const asyncProviders =
|
|
3199
|
+
const asyncProviders = _LoggerModule.createAsyncProviders(options);
|
|
3218
3200
|
return {
|
|
3219
3201
|
module: _LoggerModule,
|
|
3220
3202
|
imports: options.imports || [],
|
|
@@ -3290,7 +3272,7 @@ var LoggerModule = class _LoggerModule {
|
|
|
3290
3272
|
* Configures middleware for the module.
|
|
3291
3273
|
* Middleware is registered globally in main.ts using Fastify hooks.
|
|
3292
3274
|
*/
|
|
3293
|
-
configure(
|
|
3275
|
+
configure(_consumer) {
|
|
3294
3276
|
}
|
|
3295
3277
|
/**
|
|
3296
3278
|
* Creates async providers for dynamic module configuration.
|
|
@@ -3298,11 +3280,11 @@ var LoggerModule = class _LoggerModule {
|
|
|
3298
3280
|
static createAsyncProviders(options) {
|
|
3299
3281
|
if (options.useFactory) {
|
|
3300
3282
|
return [
|
|
3301
|
-
|
|
3283
|
+
_LoggerModule.createAsyncOptionsProvider(options)
|
|
3302
3284
|
];
|
|
3303
3285
|
}
|
|
3304
3286
|
const providers = [
|
|
3305
|
-
|
|
3287
|
+
_LoggerModule.createAsyncOptionsProvider(options)
|
|
3306
3288
|
];
|
|
3307
3289
|
if (options.useClass) {
|
|
3308
3290
|
providers.push({
|
|
@@ -3320,7 +3302,7 @@ var LoggerModule = class _LoggerModule {
|
|
|
3320
3302
|
return {
|
|
3321
3303
|
provide: LOGGER_MODULE_OPTIONS,
|
|
3322
3304
|
useFactory: /* @__PURE__ */ __name(async (...args) => {
|
|
3323
|
-
const userOptions = await options.useFactory(...args);
|
|
3305
|
+
const userOptions = await options.useFactory?.(...args);
|
|
3324
3306
|
return mergeWithDefaults(userOptions);
|
|
3325
3307
|
}, "useFactory"),
|
|
3326
3308
|
inject: options.inject || []
|
|
@@ -3367,7 +3349,7 @@ export {
|
|
|
3367
3349
|
CsrfGuard,
|
|
3368
3350
|
DEFAULT_CORRELATION_HEADER,
|
|
3369
3351
|
DatabaseModule,
|
|
3370
|
-
|
|
3352
|
+
ForbiddenException,
|
|
3371
3353
|
GoneException,
|
|
3372
3354
|
HttpExceptionFilter,
|
|
3373
3355
|
HttpLoggerInterceptor,
|
|
@@ -3383,13 +3365,13 @@ export {
|
|
|
3383
3365
|
Onboarding,
|
|
3384
3366
|
PayloadTooLargeException,
|
|
3385
3367
|
PrimaryBaseRepository,
|
|
3386
|
-
PrimaryDatabaseService,
|
|
3368
|
+
PrimaryDatabaseService2 as PrimaryDatabaseService,
|
|
3387
3369
|
Public,
|
|
3388
3370
|
RequestTimeoutException,
|
|
3389
3371
|
ServiceUnavailableException,
|
|
3390
3372
|
Tenant,
|
|
3391
3373
|
TenantBaseRepository,
|
|
3392
|
-
TenantContextService,
|
|
3374
|
+
TenantContextService2 as TenantContextService,
|
|
3393
3375
|
TenantDatabaseService,
|
|
3394
3376
|
TooManyRequestsException,
|
|
3395
3377
|
UnauthorizedException4 as UnauthorizedException,
|