@vritti/api-sdk 0.0.8 → 0.1.0
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/README.md +267 -0
- package/dist/drizzle-orm.cjs +25 -0
- package/dist/drizzle-orm.cjs.map +1 -0
- package/dist/drizzle-orm.d.cts +2 -0
- package/dist/drizzle-orm.d.ts +2 -0
- package/dist/drizzle-orm.js +3 -0
- package/dist/drizzle-orm.js.map +1 -0
- package/dist/drizzle-pg-core.cjs +25 -0
- package/dist/drizzle-pg-core.cjs.map +1 -0
- package/dist/drizzle-pg-core.d.cts +1 -0
- package/dist/drizzle-pg-core.d.ts +1 -0
- package/dist/drizzle-pg-core.js +3 -0
- package/dist/drizzle-pg-core.js.map +1 -0
- package/dist/index.cjs +1222 -280
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +723 -217
- package/dist/index.d.ts +723 -217
- package/dist/index.js +1209 -279
- package/dist/index.js.map +1 -1
- package/package.json +33 -6
package/dist/index.cjs
CHANGED
|
@@ -36,13 +36,19 @@ __export(index_exports, {
|
|
|
36
36
|
BadRequestException: () => BadRequestException,
|
|
37
37
|
BaseFieldException: () => BaseFieldException,
|
|
38
38
|
ConflictException: () => ConflictException,
|
|
39
|
+
CorrelationIdMiddleware: () => CorrelationIdMiddleware,
|
|
39
40
|
CsrfGuard: () => CsrfGuard,
|
|
41
|
+
DEFAULT_CORRELATION_HEADER: () => DEFAULT_CORRELATION_HEADER,
|
|
40
42
|
DatabaseModule: () => DatabaseModule,
|
|
41
43
|
ForbiddenException: () => ForbiddenException2,
|
|
42
44
|
GoneException: () => GoneException,
|
|
43
45
|
HttpExceptionFilter: () => HttpExceptionFilter,
|
|
46
|
+
HttpLoggerInterceptor: () => HttpLoggerInterceptor,
|
|
44
47
|
HttpModule: () => HttpModule,
|
|
45
48
|
InternalServerErrorException: () => InternalServerErrorException3,
|
|
49
|
+
LOGGER_MODULE_OPTIONS: () => LOGGER_MODULE_OPTIONS,
|
|
50
|
+
LoggerModule: () => LoggerModule,
|
|
51
|
+
LoggerService: () => LoggerService2,
|
|
46
52
|
MethodNotAllowedException: () => MethodNotAllowedException,
|
|
47
53
|
NotAcceptableException: () => NotAcceptableException,
|
|
48
54
|
NotFoundException: () => NotFoundException,
|
|
@@ -64,7 +70,13 @@ __export(index_exports, {
|
|
|
64
70
|
UnsupportedMediaTypeException: () => UnsupportedMediaTypeException,
|
|
65
71
|
ValidationException: () => ValidationException,
|
|
66
72
|
VrittiAuthGuard: () => VrittiAuthGuard,
|
|
67
|
-
|
|
73
|
+
addCorrelationIdToResponse: () => addCorrelationIdToResponse,
|
|
74
|
+
correlationStorage: () => correlationStorage,
|
|
75
|
+
generateCorrelationId: () => generateCorrelationId,
|
|
76
|
+
getCorrelationContext: () => getCorrelationContext,
|
|
77
|
+
getHttpStatusTitle: () => getHttpStatusTitle,
|
|
78
|
+
runWithCorrelationContext: () => runWithCorrelationContext,
|
|
79
|
+
updateCorrelationContext: () => updateCorrelationContext
|
|
68
80
|
});
|
|
69
81
|
module.exports = __toCommonJS(index_exports);
|
|
70
82
|
|
|
@@ -210,6 +222,9 @@ var jwt = __toESM(require("jsonwebtoken"), 1);
|
|
|
210
222
|
|
|
211
223
|
// src/database/services/primary-database.service.ts
|
|
212
224
|
var import_common3 = require("@nestjs/common");
|
|
225
|
+
var import_pg = require("pg");
|
|
226
|
+
var import_node_postgres = require("drizzle-orm/node-postgres");
|
|
227
|
+
var import_drizzle_orm = require("drizzle-orm");
|
|
213
228
|
|
|
214
229
|
// src/database/constants.ts
|
|
215
230
|
var DATABASE_MODULE_OPTIONS = Symbol("DATABASE_MODULE_OPTIONS");
|
|
@@ -238,8 +253,10 @@ var PrimaryDatabaseService = class _PrimaryDatabaseService {
|
|
|
238
253
|
}
|
|
239
254
|
options;
|
|
240
255
|
logger = new import_common3.Logger(_PrimaryDatabaseService.name);
|
|
241
|
-
/**
|
|
242
|
-
|
|
256
|
+
/** PostgreSQL connection pool */
|
|
257
|
+
pool = null;
|
|
258
|
+
/** Drizzle database instance */
|
|
259
|
+
db = null;
|
|
243
260
|
/** In-memory cache: Map<tenantIdentifier, TenantConfig> */
|
|
244
261
|
tenantConfigCache = /* @__PURE__ */ new Map();
|
|
245
262
|
/** Cache TTL in milliseconds */
|
|
@@ -250,28 +267,24 @@ var PrimaryDatabaseService = class _PrimaryDatabaseService {
|
|
|
250
267
|
}
|
|
251
268
|
async onModuleInit() {
|
|
252
269
|
if (this.options.primaryDb) {
|
|
253
|
-
await this.
|
|
270
|
+
await this.initializeDrizzleClient();
|
|
254
271
|
}
|
|
255
272
|
}
|
|
256
273
|
/**
|
|
257
|
-
* Initialize connection to primary database
|
|
274
|
+
* Initialize connection to primary database using Drizzle
|
|
258
275
|
*/
|
|
259
|
-
async
|
|
276
|
+
async initializeDrizzleClient() {
|
|
260
277
|
try {
|
|
261
|
-
const PrimaryDbClient = this.options.prismaClientConstructor;
|
|
262
278
|
const databaseUrl = this.buildPrimaryDbUrl();
|
|
263
|
-
this.
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
url: databaseUrl
|
|
267
|
-
}
|
|
268
|
-
},
|
|
269
|
-
log: [
|
|
270
|
-
"error",
|
|
271
|
-
"warn"
|
|
272
|
-
]
|
|
279
|
+
this.pool = new import_pg.Pool({
|
|
280
|
+
connectionString: databaseUrl,
|
|
281
|
+
max: this.options.maxConnections || 10
|
|
273
282
|
});
|
|
274
|
-
|
|
283
|
+
this.db = (0, import_node_postgres.drizzle)({
|
|
284
|
+
client: this.pool,
|
|
285
|
+
schema: this.options.drizzleSchema
|
|
286
|
+
});
|
|
287
|
+
await this.pool.query("SELECT 1");
|
|
275
288
|
this.logger.log("Connected to primary database (tenant registry)");
|
|
276
289
|
} catch (error) {
|
|
277
290
|
this.logger.error("Failed to connect to primary database", error);
|
|
@@ -286,7 +299,7 @@ var PrimaryDatabaseService = class _PrimaryDatabaseService {
|
|
|
286
299
|
throw new Error("Primary database configuration not provided");
|
|
287
300
|
}
|
|
288
301
|
const { host, port = 5432, username, password, database, schema = "public", sslMode = "require" } = this.options.primaryDb;
|
|
289
|
-
let url = `postgresql://${username}:${password}@${host}:${port}/${database}`;
|
|
302
|
+
let url = `postgresql://${username}:${encodeURIComponent(password)}@${host}:${port}/${database}`;
|
|
290
303
|
const params = new URLSearchParams();
|
|
291
304
|
if (schema) {
|
|
292
305
|
params.set("schema", schema);
|
|
@@ -306,9 +319,9 @@ var PrimaryDatabaseService = class _PrimaryDatabaseService {
|
|
|
306
319
|
return url.replace(/:([^@]+)@/, ":****@");
|
|
307
320
|
}
|
|
308
321
|
/**
|
|
309
|
-
* Get tenant configuration by identifier (ID or
|
|
322
|
+
* Get tenant configuration by identifier (ID or subdomain)
|
|
310
323
|
*
|
|
311
|
-
* @param tenantIdentifier Tenant ID or
|
|
324
|
+
* @param tenantIdentifier Tenant ID or subdomain
|
|
312
325
|
* @returns Tenant configuration or null if not found
|
|
313
326
|
*/
|
|
314
327
|
async getTenantInfo(tenantIdentifier) {
|
|
@@ -318,45 +331,39 @@ var PrimaryDatabaseService = class _PrimaryDatabaseService {
|
|
|
318
331
|
return cached;
|
|
319
332
|
}
|
|
320
333
|
try {
|
|
321
|
-
if (!this.
|
|
334
|
+
if (!this.db) {
|
|
322
335
|
throw new Error("Primary database client not initialized");
|
|
323
336
|
}
|
|
324
337
|
this.logger.debug(`Querying primary database for tenant: ${tenantIdentifier}`);
|
|
325
|
-
const
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
id: tenantIdentifier
|
|
330
|
-
},
|
|
331
|
-
{
|
|
332
|
-
subdomain: tenantIdentifier
|
|
333
|
-
}
|
|
334
|
-
],
|
|
335
|
-
status: "ACTIVE"
|
|
336
|
-
},
|
|
337
|
-
include: {
|
|
338
|
-
databaseConfig: true
|
|
339
|
-
}
|
|
340
|
-
});
|
|
341
|
-
if (!tenant) {
|
|
338
|
+
const schema = this.options.drizzleSchema;
|
|
339
|
+
const { tenants, tenantDatabaseConfigs } = schema;
|
|
340
|
+
const result = await this.db.select().from(tenants).leftJoin(tenantDatabaseConfigs, (0, import_drizzle_orm.eq)(tenants.id, tenantDatabaseConfigs.tenantId)).where((0, import_drizzle_orm.or)((0, import_drizzle_orm.eq)(tenants.id, tenantIdentifier), (0, import_drizzle_orm.eq)(tenants.subdomain, tenantIdentifier))).limit(1);
|
|
341
|
+
if (!result.length) {
|
|
342
342
|
this.logger.warn(`Tenant not found: ${tenantIdentifier}`);
|
|
343
343
|
return null;
|
|
344
344
|
}
|
|
345
|
+
const row = result[0];
|
|
346
|
+
const tenant = row.tenants;
|
|
347
|
+
const config = row.tenant_database_configs;
|
|
348
|
+
if (tenant.status !== "ACTIVE") {
|
|
349
|
+
this.logger.warn(`Tenant not active: ${tenantIdentifier}`);
|
|
350
|
+
return null;
|
|
351
|
+
}
|
|
345
352
|
const info = {
|
|
346
353
|
id: tenant.id,
|
|
347
354
|
subdomain: tenant.subdomain,
|
|
348
355
|
type: tenant.dbType,
|
|
349
356
|
status: tenant.status,
|
|
350
357
|
// For SHARED tenants: schema name
|
|
351
|
-
schemaName:
|
|
358
|
+
schemaName: config?.dbSchema || void 0,
|
|
352
359
|
// For DEDICATED tenants: database configuration from TenantDatabaseConfig table
|
|
353
|
-
databaseName:
|
|
354
|
-
databaseHost:
|
|
355
|
-
databasePort:
|
|
356
|
-
databaseUsername:
|
|
357
|
-
databasePassword:
|
|
358
|
-
databaseSslMode:
|
|
359
|
-
connectionPoolSize:
|
|
360
|
+
databaseName: config?.dbName || void 0,
|
|
361
|
+
databaseHost: config?.dbHost || void 0,
|
|
362
|
+
databasePort: config?.dbPort || void 0,
|
|
363
|
+
databaseUsername: config?.dbUsername ? this.decrypt(config.dbUsername) : void 0,
|
|
364
|
+
databasePassword: config?.dbPassword ? this.decrypt(config.dbPassword) : void 0,
|
|
365
|
+
databaseSslMode: config?.dbSslMode || void 0,
|
|
366
|
+
connectionPoolSize: config?.connectionPoolSize || void 0
|
|
360
367
|
};
|
|
361
368
|
this.cacheInfo(info);
|
|
362
369
|
return info;
|
|
@@ -382,7 +389,7 @@ var PrimaryDatabaseService = class _PrimaryDatabaseService {
|
|
|
382
389
|
*
|
|
383
390
|
* Useful when tenant settings are updated and cache needs to be invalidated
|
|
384
391
|
*
|
|
385
|
-
* @param tenantIdentifier Tenant ID or
|
|
392
|
+
* @param tenantIdentifier Tenant ID or subdomain
|
|
386
393
|
*/
|
|
387
394
|
clearTenantCache(tenantIdentifier) {
|
|
388
395
|
const config = this.tenantConfigCache.get(tenantIdentifier);
|
|
@@ -401,17 +408,23 @@ var PrimaryDatabaseService = class _PrimaryDatabaseService {
|
|
|
401
408
|
this.logger.log(`Cleared ${size} cached tenant configs`);
|
|
402
409
|
}
|
|
403
410
|
/**
|
|
404
|
-
* Get the
|
|
405
|
-
* This is a synchronous property that returns the initialized
|
|
411
|
+
* Get the Drizzle database instance for the primary database.
|
|
412
|
+
* This is a synchronous property that returns the initialized Drizzle client.
|
|
406
413
|
*
|
|
407
|
-
* @returns Primary database
|
|
414
|
+
* @returns Primary database Drizzle instance
|
|
408
415
|
* @throws Error if primary database client is not initialized
|
|
409
416
|
*/
|
|
410
|
-
get
|
|
411
|
-
if (!this.
|
|
417
|
+
get drizzleClient() {
|
|
418
|
+
if (!this.db) {
|
|
412
419
|
throw new Error("Primary database client not initialized");
|
|
413
420
|
}
|
|
414
|
-
return this.
|
|
421
|
+
return this.db;
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* Get the Drizzle schema
|
|
425
|
+
*/
|
|
426
|
+
get schema() {
|
|
427
|
+
return this.options.drizzleSchema;
|
|
415
428
|
}
|
|
416
429
|
/**
|
|
417
430
|
* Decrypt database credentials
|
|
@@ -425,8 +438,8 @@ var PrimaryDatabaseService = class _PrimaryDatabaseService {
|
|
|
425
438
|
return encrypted;
|
|
426
439
|
}
|
|
427
440
|
async onModuleDestroy() {
|
|
428
|
-
if (this.
|
|
429
|
-
await this.
|
|
441
|
+
if (this.pool) {
|
|
442
|
+
await this.pool.end();
|
|
430
443
|
this.logger.log("Disconnected from primary database");
|
|
431
444
|
}
|
|
432
445
|
}
|
|
@@ -971,6 +984,8 @@ TenantContextInterceptor = _ts_decorate8([
|
|
|
971
984
|
|
|
972
985
|
// src/database/services/tenant-database.service.ts
|
|
973
986
|
var import_common9 = require("@nestjs/common");
|
|
987
|
+
var import_pg2 = require("pg");
|
|
988
|
+
var import_node_postgres2 = require("drizzle-orm/node-postgres");
|
|
974
989
|
function _ts_decorate9(decorators, target, key, desc) {
|
|
975
990
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
976
991
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -995,7 +1010,7 @@ var TenantDatabaseService = class _TenantDatabaseService {
|
|
|
995
1010
|
options;
|
|
996
1011
|
tenantContext;
|
|
997
1012
|
logger = new import_common9.Logger(_TenantDatabaseService.name);
|
|
998
|
-
/** Connection pool: Map<cacheKey,
|
|
1013
|
+
/** Connection pool: Map<cacheKey, TenantConnection> */
|
|
999
1014
|
clients = /* @__PURE__ */ new Map();
|
|
1000
1015
|
/** Track last usage time for idle connection cleanup */
|
|
1001
1016
|
clientLastUsed = /* @__PURE__ */ new Map();
|
|
@@ -1007,17 +1022,23 @@ var TenantDatabaseService = class _TenantDatabaseService {
|
|
|
1007
1022
|
this.startConnectionCleaner();
|
|
1008
1023
|
}
|
|
1009
1024
|
/**
|
|
1010
|
-
* Get the
|
|
1025
|
+
* Get the Drizzle client for the current tenant's database.
|
|
1011
1026
|
* This returns the tenant-scoped database client.
|
|
1012
1027
|
*
|
|
1013
|
-
* @returns Tenant-scoped database
|
|
1028
|
+
* @returns Tenant-scoped Drizzle database instance
|
|
1014
1029
|
* @throws UnauthorizedException if tenant context not set
|
|
1015
1030
|
* @throws InternalServerErrorException if connection fails
|
|
1016
1031
|
*/
|
|
1017
|
-
get
|
|
1032
|
+
get drizzleClient() {
|
|
1018
1033
|
return this.getDbClient();
|
|
1019
1034
|
}
|
|
1020
1035
|
/**
|
|
1036
|
+
* Get the Drizzle schema
|
|
1037
|
+
*/
|
|
1038
|
+
get schema() {
|
|
1039
|
+
return this.options.drizzleSchema;
|
|
1040
|
+
}
|
|
1041
|
+
/**
|
|
1021
1042
|
* Get tenant-scoped database client for the current request/message
|
|
1022
1043
|
*
|
|
1023
1044
|
* This method:
|
|
@@ -1025,66 +1046,61 @@ var TenantDatabaseService = class _TenantDatabaseService {
|
|
|
1025
1046
|
* 2. Builds a connection URL based on tenant type
|
|
1026
1047
|
* 3. Returns cached client if exists, otherwise creates new one
|
|
1027
1048
|
*
|
|
1028
|
-
* @returns
|
|
1049
|
+
* @returns Drizzle database instance
|
|
1029
1050
|
* @throws UnauthorizedException if tenant context not set
|
|
1030
1051
|
* @throws InternalServerErrorException if connection fails
|
|
1031
|
-
*
|
|
1032
|
-
* @example
|
|
1033
|
-
* const dbClient = await tenantDatabase.getDbClient<PrismaClient>();
|
|
1034
|
-
* const users = await dbClient.user.findMany();
|
|
1035
1052
|
*/
|
|
1036
|
-
|
|
1053
|
+
getDbClient() {
|
|
1037
1054
|
const tenant = this.tenantContext.getTenant();
|
|
1038
1055
|
const cacheKey = this.buildCacheKey(tenant);
|
|
1039
|
-
|
|
1056
|
+
const existing = this.clients.get(cacheKey);
|
|
1057
|
+
if (existing) {
|
|
1040
1058
|
this.clientLastUsed.set(cacheKey, Date.now());
|
|
1041
1059
|
this.logger.debug(`Reusing cached connection: ${cacheKey}`);
|
|
1042
|
-
return
|
|
1060
|
+
return existing.db;
|
|
1043
1061
|
}
|
|
1044
1062
|
this.logger.log(`Creating new database connection: ${cacheKey}`);
|
|
1045
|
-
const
|
|
1046
|
-
this.clients.set(cacheKey,
|
|
1063
|
+
const connection = this.createDbClientSync(tenant);
|
|
1064
|
+
this.clients.set(cacheKey, connection);
|
|
1047
1065
|
this.clientLastUsed.set(cacheKey, Date.now());
|
|
1048
|
-
return
|
|
1066
|
+
return connection.db;
|
|
1049
1067
|
}
|
|
1050
1068
|
/**
|
|
1051
|
-
* Create a new database client for the given tenant
|
|
1069
|
+
* Create a new database client for the given tenant (synchronous)
|
|
1052
1070
|
*/
|
|
1053
|
-
|
|
1071
|
+
createDbClientSync(tenant) {
|
|
1054
1072
|
try {
|
|
1055
1073
|
const databaseUrl = this.buildTenantDbUrl(tenant);
|
|
1056
|
-
const
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
log: [
|
|
1064
|
-
"error",
|
|
1065
|
-
"warn"
|
|
1066
|
-
]
|
|
1074
|
+
const pool = new import_pg2.Pool({
|
|
1075
|
+
connectionString: databaseUrl,
|
|
1076
|
+
max: tenant.connectionPoolSize || this.options.maxConnections || 10
|
|
1077
|
+
});
|
|
1078
|
+
const db = (0, import_node_postgres2.drizzle)({
|
|
1079
|
+
client: pool,
|
|
1080
|
+
schema: this.options.drizzleSchema
|
|
1067
1081
|
});
|
|
1068
|
-
await client.$connect();
|
|
1069
1082
|
this.logger.log(`Connected to database for tenant: ${tenant.subdomain}`);
|
|
1070
|
-
return
|
|
1083
|
+
return {
|
|
1084
|
+
pool,
|
|
1085
|
+
db
|
|
1086
|
+
};
|
|
1071
1087
|
} catch (error) {
|
|
1072
1088
|
this.logger.error(`Failed to create database connection for tenant: ${tenant.subdomain}`, error);
|
|
1073
1089
|
throw new import_common9.InternalServerErrorException("Failed to connect to tenant database");
|
|
1074
1090
|
}
|
|
1075
1091
|
}
|
|
1076
1092
|
/**
|
|
1077
|
-
* Build connection URL for
|
|
1093
|
+
* Build connection URL for tenant (dedicated database)
|
|
1078
1094
|
*/
|
|
1079
1095
|
buildTenantDbUrl(tenant) {
|
|
1080
1096
|
const { databaseHost, databasePort, databaseName, databaseUsername, databasePassword, databaseSslMode } = tenant;
|
|
1081
1097
|
if (!databaseHost || !databaseName || !databaseUsername) {
|
|
1082
|
-
throw new Error(`
|
|
1098
|
+
throw new Error(`Tenant ${tenant.subdomain} missing database configuration`);
|
|
1083
1099
|
}
|
|
1084
1100
|
const port = databasePort || 5432;
|
|
1085
1101
|
const sslMode = databaseSslMode || "require";
|
|
1086
|
-
const connectionUrl = `postgresql://${databaseUsername}:${databasePassword}@${databaseHost}:${port}/${databaseName}?sslmode=${sslMode}`;
|
|
1087
|
-
this.logger.debug(`
|
|
1102
|
+
const connectionUrl = `postgresql://${databaseUsername}:${encodeURIComponent(databasePassword || "")}@${databaseHost}:${port}/${databaseName}?sslmode=${sslMode}`;
|
|
1103
|
+
this.logger.debug(`Tenant connection URL: ${this.maskPassword(connectionUrl)}`);
|
|
1088
1104
|
return connectionUrl;
|
|
1089
1105
|
}
|
|
1090
1106
|
/**
|
|
@@ -1106,19 +1122,20 @@ var TenantDatabaseService = class _TenantDatabaseService {
|
|
|
1106
1122
|
/**
|
|
1107
1123
|
* Clean up idle connections that haven't been used recently
|
|
1108
1124
|
*/
|
|
1109
|
-
cleanupIdleConnections() {
|
|
1125
|
+
async cleanupIdleConnections() {
|
|
1110
1126
|
const now = Date.now();
|
|
1111
1127
|
const maxIdle = this.options.connectionCacheTTL || 3e5;
|
|
1112
1128
|
let cleaned = 0;
|
|
1113
1129
|
for (const [key, lastUsed] of this.clientLastUsed.entries()) {
|
|
1114
1130
|
if (now - lastUsed > maxIdle) {
|
|
1115
|
-
const
|
|
1116
|
-
if (
|
|
1117
|
-
|
|
1131
|
+
const connection = this.clients.get(key);
|
|
1132
|
+
if (connection) {
|
|
1133
|
+
try {
|
|
1134
|
+
await connection.pool.end();
|
|
1118
1135
|
this.logger.debug(`Cleaned up idle connection: ${key}`);
|
|
1119
|
-
}
|
|
1136
|
+
} catch (error) {
|
|
1120
1137
|
this.logger.error(`Error disconnecting idle client: ${key}`, error);
|
|
1121
|
-
}
|
|
1138
|
+
}
|
|
1122
1139
|
this.clients.delete(key);
|
|
1123
1140
|
this.clientLastUsed.delete(key);
|
|
1124
1141
|
cleaned++;
|
|
@@ -1149,9 +1166,9 @@ var TenantDatabaseService = class _TenantDatabaseService {
|
|
|
1149
1166
|
clearInterval(this.cleanupInterval);
|
|
1150
1167
|
}
|
|
1151
1168
|
this.logger.log(`Disconnecting ${this.clients.size} database connections`);
|
|
1152
|
-
const disconnectPromises = Array.from(this.clients.entries()).map(async ([key,
|
|
1169
|
+
const disconnectPromises = Array.from(this.clients.entries()).map(async ([key, connection]) => {
|
|
1153
1170
|
try {
|
|
1154
|
-
await
|
|
1171
|
+
await connection.pool.end();
|
|
1155
1172
|
this.logger.debug(`Disconnected: ${key}`);
|
|
1156
1173
|
} catch (error) {
|
|
1157
1174
|
this.logger.error(`Error disconnecting client: ${key}`, error);
|
|
@@ -1287,56 +1304,64 @@ DatabaseModule = _ts_decorate10([
|
|
|
1287
1304
|
|
|
1288
1305
|
// src/database/repositories/primary-base.repository.ts
|
|
1289
1306
|
var import_common11 = require("@nestjs/common");
|
|
1307
|
+
var import_drizzle_orm2 = require("drizzle-orm");
|
|
1290
1308
|
var PrimaryBaseRepository = class {
|
|
1291
1309
|
static {
|
|
1292
1310
|
__name(this, "PrimaryBaseRepository");
|
|
1293
1311
|
}
|
|
1294
1312
|
database;
|
|
1313
|
+
table;
|
|
1295
1314
|
logger;
|
|
1296
|
-
modelGetter;
|
|
1297
1315
|
/**
|
|
1298
|
-
*
|
|
1316
|
+
* The table name extracted from the Drizzle table at runtime.
|
|
1317
|
+
* Used to access the query API for this repository's table.
|
|
1318
|
+
*/
|
|
1319
|
+
tableName;
|
|
1320
|
+
/**
|
|
1321
|
+
* Lazy getter for Drizzle client.
|
|
1299
1322
|
* Accesses the client from the database service only when needed,
|
|
1300
1323
|
* avoiding initialization timing issues with NestJS lifecycle.
|
|
1301
1324
|
*/
|
|
1302
|
-
get
|
|
1303
|
-
return this.database.
|
|
1325
|
+
get db() {
|
|
1326
|
+
return this.database.drizzleClient;
|
|
1304
1327
|
}
|
|
1305
1328
|
/**
|
|
1306
|
-
*
|
|
1307
|
-
*
|
|
1329
|
+
* Model query API for THIS repository's table (Prisma-like syntax)
|
|
1330
|
+
* Scoped to only the table this repository manages.
|
|
1331
|
+
* Returns a type-safe wrapper around Drizzle's RelationalQueryBuilder.
|
|
1332
|
+
*
|
|
1333
|
+
* @example
|
|
1334
|
+
* ```typescript
|
|
1335
|
+
* // Use relational queries with type safety
|
|
1336
|
+
* const user = await this.model.findFirst({
|
|
1337
|
+
* where: eq(users.id, id),
|
|
1338
|
+
* with: { posts: true, profile: true }
|
|
1339
|
+
* });
|
|
1340
|
+
* ```
|
|
1308
1341
|
*/
|
|
1309
1342
|
get model() {
|
|
1310
|
-
return this.
|
|
1343
|
+
return this.database.drizzleClient.query[this.tableName];
|
|
1311
1344
|
}
|
|
1312
1345
|
/**
|
|
1313
1346
|
* Create a new repository instance
|
|
1314
1347
|
*
|
|
1315
1348
|
* @param database - The primary database service
|
|
1316
|
-
* @param
|
|
1349
|
+
* @param table - The Drizzle table schema object
|
|
1317
1350
|
*
|
|
1318
1351
|
* @example
|
|
1319
1352
|
* ```typescript
|
|
1320
|
-
*
|
|
1321
|
-
* constructor(database: PrimaryDatabaseService) {
|
|
1322
|
-
* super(database, (prisma) => prisma.user);
|
|
1323
|
-
* }
|
|
1324
|
-
*
|
|
1325
|
-
* // Short syntax
|
|
1326
|
-
* constructor(database: PrimaryDatabaseService) {
|
|
1327
|
-
* super(database, (p) => p.user);
|
|
1328
|
-
* }
|
|
1353
|
+
* import { users } from '@/db/schema';
|
|
1329
1354
|
*
|
|
1330
|
-
* // Complex model names
|
|
1331
1355
|
* constructor(database: PrimaryDatabaseService) {
|
|
1332
|
-
* super(database,
|
|
1356
|
+
* super(database, users);
|
|
1333
1357
|
* }
|
|
1334
1358
|
* ```
|
|
1335
1359
|
*/
|
|
1336
|
-
constructor(database,
|
|
1360
|
+
constructor(database, table) {
|
|
1337
1361
|
this.database = database;
|
|
1362
|
+
this.table = table;
|
|
1363
|
+
this.tableName = (0, import_drizzle_orm2.getTableName)(table);
|
|
1338
1364
|
this.logger = new import_common11.Logger(this.constructor.name);
|
|
1339
|
-
this.modelGetter = getModel;
|
|
1340
1365
|
this.logger.debug(`Initialized ${this.constructor.name}`);
|
|
1341
1366
|
}
|
|
1342
1367
|
/**
|
|
@@ -1349,21 +1374,20 @@ var PrimaryBaseRepository = class {
|
|
|
1349
1374
|
* ```typescript
|
|
1350
1375
|
* const user = await userRepository.create({
|
|
1351
1376
|
* email: 'user@example.com',
|
|
1352
|
-
*
|
|
1377
|
+
* firstName: 'John'
|
|
1353
1378
|
* });
|
|
1354
1379
|
* ```
|
|
1355
1380
|
*/
|
|
1356
1381
|
async create(data) {
|
|
1357
1382
|
this.logger.log("Creating record");
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
});
|
|
1383
|
+
const results = await this.db.insert(this.table).values(data).returning();
|
|
1384
|
+
return results[0];
|
|
1361
1385
|
}
|
|
1362
1386
|
/**
|
|
1363
1387
|
* Find a single record by ID
|
|
1364
1388
|
*
|
|
1365
1389
|
* @param id - The record ID
|
|
1366
|
-
* @returns Promise resolving to the record or
|
|
1390
|
+
* @returns Promise resolving to the record or undefined if not found
|
|
1367
1391
|
*
|
|
1368
1392
|
* @example
|
|
1369
1393
|
* ```typescript
|
|
@@ -1372,59 +1396,54 @@ var PrimaryBaseRepository = class {
|
|
|
1372
1396
|
*/
|
|
1373
1397
|
async findById(id) {
|
|
1374
1398
|
this.logger.debug(`Finding record by ID: ${id}`);
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
}
|
|
1399
|
+
const idColumn = this.table.id;
|
|
1400
|
+
return this.model.findFirst({
|
|
1401
|
+
where: (0, import_drizzle_orm2.eq)(idColumn, id)
|
|
1379
1402
|
});
|
|
1380
1403
|
}
|
|
1381
1404
|
/**
|
|
1382
1405
|
* Find a single record with custom where clause
|
|
1383
1406
|
*
|
|
1384
|
-
* @param where -
|
|
1385
|
-
* @returns Promise resolving to the record or
|
|
1407
|
+
* @param where - SQL condition
|
|
1408
|
+
* @returns Promise resolving to the record or undefined if not found
|
|
1386
1409
|
*
|
|
1387
1410
|
* @example
|
|
1388
1411
|
* ```typescript
|
|
1389
|
-
*
|
|
1390
|
-
* const user = await userRepository.findOne(
|
|
1391
|
-
*
|
|
1392
|
-
* // With include
|
|
1393
|
-
* const user = await userRepository.findOne({
|
|
1394
|
-
* where: { email: 'user@example.com' },
|
|
1395
|
-
* include: { posts: true }
|
|
1396
|
-
* });
|
|
1412
|
+
* import { eq } from 'drizzle-orm';
|
|
1413
|
+
* const user = await userRepository.findOne(eq(users.email, 'user@example.com'));
|
|
1397
1414
|
* ```
|
|
1398
1415
|
*/
|
|
1399
1416
|
async findOne(where) {
|
|
1400
1417
|
this.logger.debug("Finding record with custom query");
|
|
1401
|
-
return
|
|
1418
|
+
return this.model.findFirst({
|
|
1402
1419
|
where
|
|
1403
1420
|
});
|
|
1404
1421
|
}
|
|
1405
1422
|
/**
|
|
1406
1423
|
* Find multiple records
|
|
1407
1424
|
*
|
|
1408
|
-
* @param
|
|
1425
|
+
* @param options - Query options (where, orderBy, limit, offset)
|
|
1409
1426
|
* @returns Promise resolving to an array of records
|
|
1410
1427
|
*
|
|
1411
1428
|
* @example
|
|
1412
1429
|
* ```typescript
|
|
1430
|
+
* import { eq, desc } from 'drizzle-orm';
|
|
1431
|
+
*
|
|
1413
1432
|
* // Find all users
|
|
1414
1433
|
* const users = await userRepository.findMany();
|
|
1415
1434
|
*
|
|
1416
1435
|
* // Find with filtering and pagination
|
|
1417
1436
|
* const users = await userRepository.findMany({
|
|
1418
|
-
* where:
|
|
1419
|
-
* orderBy:
|
|
1420
|
-
*
|
|
1421
|
-
*
|
|
1437
|
+
* where: eq(users.accountStatus, 'ACTIVE'),
|
|
1438
|
+
* orderBy: desc(users.createdAt),
|
|
1439
|
+
* limit: 10,
|
|
1440
|
+
* offset: 0
|
|
1422
1441
|
* });
|
|
1423
1442
|
* ```
|
|
1424
1443
|
*/
|
|
1425
|
-
async findMany(
|
|
1444
|
+
async findMany(options) {
|
|
1426
1445
|
this.logger.debug("Finding multiple records");
|
|
1427
|
-
return
|
|
1446
|
+
return this.model.findMany(options);
|
|
1428
1447
|
}
|
|
1429
1448
|
/**
|
|
1430
1449
|
* Update a record by ID
|
|
@@ -1436,41 +1455,40 @@ var PrimaryBaseRepository = class {
|
|
|
1436
1455
|
* @example
|
|
1437
1456
|
* ```typescript
|
|
1438
1457
|
* const user = await userRepository.update('user-id-123', {
|
|
1439
|
-
*
|
|
1458
|
+
* firstName: 'Jane'
|
|
1440
1459
|
* });
|
|
1441
1460
|
* ```
|
|
1442
1461
|
*/
|
|
1443
1462
|
async update(id, data) {
|
|
1444
1463
|
this.logger.log(`Updating record with ID: ${id}`);
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
},
|
|
1449
|
-
data
|
|
1450
|
-
});
|
|
1464
|
+
const idColumn = this.table.id;
|
|
1465
|
+
const results = await this.db.update(this.table).set(data).where((0, import_drizzle_orm2.eq)(idColumn, id)).returning();
|
|
1466
|
+
return results[0];
|
|
1451
1467
|
}
|
|
1452
1468
|
/**
|
|
1453
1469
|
* Update multiple records
|
|
1454
1470
|
*
|
|
1455
|
-
* @param where -
|
|
1471
|
+
* @param where - SQL condition to match records
|
|
1456
1472
|
* @param data - The data to update
|
|
1457
1473
|
* @returns Promise resolving to the count of updated records
|
|
1458
1474
|
*
|
|
1459
1475
|
* @example
|
|
1460
1476
|
* ```typescript
|
|
1477
|
+
* import { eq } from 'drizzle-orm';
|
|
1478
|
+
*
|
|
1461
1479
|
* const result = await userRepository.updateMany(
|
|
1462
|
-
*
|
|
1463
|
-
* {
|
|
1480
|
+
* eq(users.accountStatus, 'PENDING'),
|
|
1481
|
+
* { accountStatus: 'ACTIVE' }
|
|
1464
1482
|
* );
|
|
1465
1483
|
* console.log(`Updated ${result.count} users`);
|
|
1466
1484
|
* ```
|
|
1467
1485
|
*/
|
|
1468
1486
|
async updateMany(where, data) {
|
|
1469
1487
|
this.logger.log("Updating multiple records");
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
}
|
|
1488
|
+
const result = await this.db.update(this.table).set(data).where(where);
|
|
1489
|
+
return {
|
|
1490
|
+
count: result.rowCount ?? 0
|
|
1491
|
+
};
|
|
1474
1492
|
}
|
|
1475
1493
|
/**
|
|
1476
1494
|
* Delete a record by ID
|
|
@@ -1485,127 +1503,143 @@ var PrimaryBaseRepository = class {
|
|
|
1485
1503
|
*/
|
|
1486
1504
|
async delete(id) {
|
|
1487
1505
|
this.logger.log(`Deleting record with ID: ${id}`);
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
}
|
|
1492
|
-
});
|
|
1506
|
+
const idColumn = this.table.id;
|
|
1507
|
+
const results = await this.db.delete(this.table).where((0, import_drizzle_orm2.eq)(idColumn, id)).returning();
|
|
1508
|
+
return results[0];
|
|
1493
1509
|
}
|
|
1494
1510
|
/**
|
|
1495
1511
|
* Delete multiple records
|
|
1496
1512
|
*
|
|
1497
|
-
* @param where -
|
|
1513
|
+
* @param where - SQL condition to match records
|
|
1498
1514
|
* @returns Promise resolving to the count of deleted records
|
|
1499
1515
|
*
|
|
1500
1516
|
* @example
|
|
1501
1517
|
* ```typescript
|
|
1502
|
-
*
|
|
1503
|
-
*
|
|
1504
|
-
*
|
|
1505
|
-
*
|
|
1518
|
+
* import { lt } from 'drizzle-orm';
|
|
1519
|
+
*
|
|
1520
|
+
* const result = await userRepository.deleteMany(
|
|
1521
|
+
* lt(users.createdAt, new Date('2020-01-01'))
|
|
1522
|
+
* );
|
|
1506
1523
|
* console.log(`Deleted ${result.count} users`);
|
|
1507
1524
|
* ```
|
|
1508
1525
|
*/
|
|
1509
1526
|
async deleteMany(where) {
|
|
1510
1527
|
this.logger.log("Deleting multiple records");
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1528
|
+
const result = await this.db.delete(this.table).where(where);
|
|
1529
|
+
return {
|
|
1530
|
+
count: result.rowCount ?? 0
|
|
1531
|
+
};
|
|
1514
1532
|
}
|
|
1515
1533
|
/**
|
|
1516
1534
|
* Count records
|
|
1517
1535
|
*
|
|
1518
|
-
* @param where - Optional
|
|
1536
|
+
* @param where - Optional SQL condition to filter records
|
|
1519
1537
|
* @returns Promise resolving to the count of records
|
|
1520
1538
|
*
|
|
1521
1539
|
* @example
|
|
1522
1540
|
* ```typescript
|
|
1541
|
+
* import { eq } from 'drizzle-orm';
|
|
1542
|
+
*
|
|
1523
1543
|
* // Count all users
|
|
1524
1544
|
* const total = await userRepository.count();
|
|
1525
1545
|
*
|
|
1526
1546
|
* // Count active users
|
|
1527
|
-
* const activeCount = await userRepository.count(
|
|
1547
|
+
* const activeCount = await userRepository.count(
|
|
1548
|
+
* eq(users.accountStatus, 'ACTIVE')
|
|
1549
|
+
* );
|
|
1528
1550
|
* ```
|
|
1529
1551
|
*/
|
|
1530
1552
|
async count(where) {
|
|
1531
1553
|
this.logger.debug("Counting records");
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
});
|
|
1554
|
+
let query = this.db.select({
|
|
1555
|
+
count: import_drizzle_orm2.sql`count(*)::int`
|
|
1556
|
+
}).from(this.table).$dynamic();
|
|
1557
|
+
if (where) {
|
|
1558
|
+
query = query.where(where);
|
|
1559
|
+
}
|
|
1560
|
+
const results = await query;
|
|
1561
|
+
return results[0].count;
|
|
1535
1562
|
}
|
|
1536
1563
|
/**
|
|
1537
1564
|
* Check if a record exists
|
|
1538
1565
|
*
|
|
1539
|
-
* @param where -
|
|
1566
|
+
* @param where - SQL condition to match records
|
|
1540
1567
|
* @returns Promise resolving to true if at least one record exists, false otherwise
|
|
1541
1568
|
*
|
|
1542
1569
|
* @example
|
|
1543
1570
|
* ```typescript
|
|
1544
|
-
*
|
|
1545
|
-
*
|
|
1546
|
-
*
|
|
1571
|
+
* import { eq } from 'drizzle-orm';
|
|
1572
|
+
*
|
|
1573
|
+
* const emailExists = await userRepository.exists(
|
|
1574
|
+
* eq(users.email, 'user@example.com')
|
|
1575
|
+
* );
|
|
1547
1576
|
* ```
|
|
1548
1577
|
*/
|
|
1549
1578
|
async exists(where) {
|
|
1550
|
-
const count = await this.
|
|
1551
|
-
where
|
|
1552
|
-
});
|
|
1579
|
+
const count = await this.count(where);
|
|
1553
1580
|
return count > 0;
|
|
1554
1581
|
}
|
|
1555
1582
|
};
|
|
1556
1583
|
|
|
1557
1584
|
// src/database/repositories/tenant-base.repository.ts
|
|
1558
1585
|
var import_common12 = require("@nestjs/common");
|
|
1586
|
+
var import_drizzle_orm3 = require("drizzle-orm");
|
|
1559
1587
|
var TenantBaseRepository = class {
|
|
1560
1588
|
static {
|
|
1561
1589
|
__name(this, "TenantBaseRepository");
|
|
1562
1590
|
}
|
|
1563
1591
|
database;
|
|
1592
|
+
table;
|
|
1564
1593
|
logger;
|
|
1565
|
-
modelGetter;
|
|
1566
1594
|
/**
|
|
1567
|
-
*
|
|
1595
|
+
* The table name extracted from the Drizzle table at runtime.
|
|
1596
|
+
* Used to access the query API for this repository's table.
|
|
1597
|
+
*/
|
|
1598
|
+
tableName;
|
|
1599
|
+
/**
|
|
1600
|
+
* Lazy getter for Drizzle client.
|
|
1568
1601
|
* Accesses the client from the database service only when needed,
|
|
1569
1602
|
* avoiding initialization timing issues with NestJS lifecycle.
|
|
1570
1603
|
*/
|
|
1571
|
-
get
|
|
1572
|
-
return this.database.
|
|
1604
|
+
get db() {
|
|
1605
|
+
return this.database.drizzleClient;
|
|
1573
1606
|
}
|
|
1574
1607
|
/**
|
|
1575
|
-
*
|
|
1576
|
-
*
|
|
1608
|
+
* Model query API for THIS repository's table (Prisma-like syntax)
|
|
1609
|
+
* Scoped to only the table this repository manages
|
|
1610
|
+
*
|
|
1611
|
+
* @example
|
|
1612
|
+
* ```typescript
|
|
1613
|
+
* // Use relational queries with type safety
|
|
1614
|
+
* const product = await this.model.findFirst({
|
|
1615
|
+
* where: eq(products.id, id),
|
|
1616
|
+
* with: { category: true, variants: true }
|
|
1617
|
+
* });
|
|
1618
|
+
* ```
|
|
1577
1619
|
*/
|
|
1578
1620
|
get model() {
|
|
1579
|
-
return this.
|
|
1621
|
+
return this.database.drizzleClient.query[this.tableName];
|
|
1580
1622
|
}
|
|
1581
1623
|
/**
|
|
1582
1624
|
* Create a new repository instance
|
|
1583
1625
|
*
|
|
1584
1626
|
* @param database - The tenant database service
|
|
1585
|
-
* @param
|
|
1627
|
+
* @param table - The Drizzle table schema object
|
|
1586
1628
|
*
|
|
1587
1629
|
* @example
|
|
1588
1630
|
* ```typescript
|
|
1589
|
-
*
|
|
1590
|
-
* constructor(database: TenantDatabaseService) {
|
|
1591
|
-
* super(database, (prisma) => prisma.product);
|
|
1592
|
-
* }
|
|
1593
|
-
*
|
|
1594
|
-
* // Short syntax
|
|
1595
|
-
* constructor(database: TenantDatabaseService) {
|
|
1596
|
-
* super(database, (p) => p.product);
|
|
1597
|
-
* }
|
|
1631
|
+
* import { products } from '@/db/schema';
|
|
1598
1632
|
*
|
|
1599
|
-
* // Complex model names
|
|
1600
1633
|
* constructor(database: TenantDatabaseService) {
|
|
1601
|
-
* super(database,
|
|
1634
|
+
* super(database, products);
|
|
1602
1635
|
* }
|
|
1603
1636
|
* ```
|
|
1604
1637
|
*/
|
|
1605
|
-
constructor(database,
|
|
1638
|
+
constructor(database, table) {
|
|
1606
1639
|
this.database = database;
|
|
1640
|
+
this.table = table;
|
|
1641
|
+
this.tableName = (0, import_drizzle_orm3.getTableName)(table);
|
|
1607
1642
|
this.logger = new import_common12.Logger(this.constructor.name);
|
|
1608
|
-
this.modelGetter = getModel;
|
|
1609
1643
|
this.logger.debug(`Initialized ${this.constructor.name}`);
|
|
1610
1644
|
}
|
|
1611
1645
|
/**
|
|
@@ -1625,9 +1659,8 @@ var TenantBaseRepository = class {
|
|
|
1625
1659
|
*/
|
|
1626
1660
|
async create(data) {
|
|
1627
1661
|
this.logger.log("Creating record");
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
});
|
|
1662
|
+
const results = await this.db.insert(this.table).values(data).returning();
|
|
1663
|
+
return results[0];
|
|
1631
1664
|
}
|
|
1632
1665
|
/**
|
|
1633
1666
|
* Find a single record by ID
|
|
@@ -1642,59 +1675,65 @@ var TenantBaseRepository = class {
|
|
|
1642
1675
|
*/
|
|
1643
1676
|
async findById(id) {
|
|
1644
1677
|
this.logger.debug(`Finding record by ID: ${id}`);
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
}
|
|
1649
|
-
});
|
|
1678
|
+
const idColumn = this.table.id;
|
|
1679
|
+
const results = await this.db.select().from(this.table).where((0, import_drizzle_orm3.eq)(idColumn, id)).limit(1);
|
|
1680
|
+
return results[0] ?? null;
|
|
1650
1681
|
}
|
|
1651
1682
|
/**
|
|
1652
1683
|
* Find a single record with custom where clause
|
|
1653
1684
|
*
|
|
1654
|
-
* @param where -
|
|
1685
|
+
* @param where - SQL condition
|
|
1655
1686
|
* @returns Promise resolving to the record or null if not found
|
|
1656
1687
|
*
|
|
1657
1688
|
* @example
|
|
1658
1689
|
* ```typescript
|
|
1659
|
-
*
|
|
1660
|
-
* const product = await productRepository.findOne(
|
|
1661
|
-
*
|
|
1662
|
-
* // With include
|
|
1663
|
-
* const product = await productRepository.findOne({
|
|
1664
|
-
* where: { sku: 'WDG-001' },
|
|
1665
|
-
* include: { category: true }
|
|
1666
|
-
* });
|
|
1690
|
+
* import { eq } from 'drizzle-orm';
|
|
1691
|
+
* const product = await productRepository.findOne(eq(products.sku, 'WDG-001'));
|
|
1667
1692
|
* ```
|
|
1668
1693
|
*/
|
|
1669
1694
|
async findOne(where) {
|
|
1670
1695
|
this.logger.debug("Finding record with custom query");
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
});
|
|
1696
|
+
const results = await this.db.select().from(this.table).where(where).limit(1);
|
|
1697
|
+
return results[0] ?? null;
|
|
1674
1698
|
}
|
|
1675
1699
|
/**
|
|
1676
1700
|
* Find multiple records
|
|
1677
1701
|
*
|
|
1678
|
-
* @param
|
|
1702
|
+
* @param options - Query options (where, orderBy, limit, offset)
|
|
1679
1703
|
* @returns Promise resolving to an array of records
|
|
1680
1704
|
*
|
|
1681
1705
|
* @example
|
|
1682
1706
|
* ```typescript
|
|
1707
|
+
* import { eq, desc } from 'drizzle-orm';
|
|
1708
|
+
*
|
|
1683
1709
|
* // Find all products
|
|
1684
1710
|
* const products = await productRepository.findMany();
|
|
1685
1711
|
*
|
|
1686
1712
|
* // Find with filtering and pagination
|
|
1687
1713
|
* const products = await productRepository.findMany({
|
|
1688
|
-
* where:
|
|
1689
|
-
* orderBy:
|
|
1690
|
-
*
|
|
1691
|
-
*
|
|
1714
|
+
* where: eq(products.status, 'ACTIVE'),
|
|
1715
|
+
* orderBy: desc(products.createdAt),
|
|
1716
|
+
* limit: 10,
|
|
1717
|
+
* offset: 0
|
|
1692
1718
|
* });
|
|
1693
1719
|
* ```
|
|
1694
1720
|
*/
|
|
1695
|
-
async findMany(
|
|
1721
|
+
async findMany(options) {
|
|
1696
1722
|
this.logger.debug("Finding multiple records");
|
|
1697
|
-
|
|
1723
|
+
let query = this.db.select().from(this.table).$dynamic();
|
|
1724
|
+
if (options?.where) {
|
|
1725
|
+
query = query.where(options.where);
|
|
1726
|
+
}
|
|
1727
|
+
if (options?.orderBy) {
|
|
1728
|
+
query = query.orderBy(options.orderBy);
|
|
1729
|
+
}
|
|
1730
|
+
if (options?.limit) {
|
|
1731
|
+
query = query.limit(options.limit);
|
|
1732
|
+
}
|
|
1733
|
+
if (options?.offset) {
|
|
1734
|
+
query = query.offset(options.offset);
|
|
1735
|
+
}
|
|
1736
|
+
return await query;
|
|
1698
1737
|
}
|
|
1699
1738
|
/**
|
|
1700
1739
|
* Update a record by ID
|
|
@@ -1712,24 +1751,23 @@ var TenantBaseRepository = class {
|
|
|
1712
1751
|
*/
|
|
1713
1752
|
async update(id, data) {
|
|
1714
1753
|
this.logger.log(`Updating record with ID: ${id}`);
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
},
|
|
1719
|
-
data
|
|
1720
|
-
});
|
|
1754
|
+
const idColumn = this.table.id;
|
|
1755
|
+
const results = await this.db.update(this.table).set(data).where((0, import_drizzle_orm3.eq)(idColumn, id)).returning();
|
|
1756
|
+
return results[0];
|
|
1721
1757
|
}
|
|
1722
1758
|
/**
|
|
1723
1759
|
* Update multiple records
|
|
1724
1760
|
*
|
|
1725
|
-
* @param where -
|
|
1761
|
+
* @param where - SQL condition to match records
|
|
1726
1762
|
* @param data - The data to update
|
|
1727
1763
|
* @returns Promise resolving to the count of updated records
|
|
1728
1764
|
*
|
|
1729
1765
|
* @example
|
|
1730
1766
|
* ```typescript
|
|
1767
|
+
* import { eq } from 'drizzle-orm';
|
|
1768
|
+
*
|
|
1731
1769
|
* const result = await productRepository.updateMany(
|
|
1732
|
-
*
|
|
1770
|
+
* eq(products.status, 'PENDING'),
|
|
1733
1771
|
* { status: 'ACTIVE' }
|
|
1734
1772
|
* );
|
|
1735
1773
|
* console.log(`Updated ${result.count} products`);
|
|
@@ -1737,10 +1775,10 @@ var TenantBaseRepository = class {
|
|
|
1737
1775
|
*/
|
|
1738
1776
|
async updateMany(where, data) {
|
|
1739
1777
|
this.logger.log("Updating multiple records");
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
}
|
|
1778
|
+
const result = await this.db.update(this.table).set(data).where(where);
|
|
1779
|
+
return {
|
|
1780
|
+
count: result.rowCount ?? 0
|
|
1781
|
+
};
|
|
1744
1782
|
}
|
|
1745
1783
|
/**
|
|
1746
1784
|
* Delete a record by ID
|
|
@@ -1755,71 +1793,80 @@ var TenantBaseRepository = class {
|
|
|
1755
1793
|
*/
|
|
1756
1794
|
async delete(id) {
|
|
1757
1795
|
this.logger.log(`Deleting record with ID: ${id}`);
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
}
|
|
1762
|
-
});
|
|
1796
|
+
const idColumn = this.table.id;
|
|
1797
|
+
const results = await this.db.delete(this.table).where((0, import_drizzle_orm3.eq)(idColumn, id)).returning();
|
|
1798
|
+
return results[0];
|
|
1763
1799
|
}
|
|
1764
1800
|
/**
|
|
1765
1801
|
* Delete multiple records
|
|
1766
1802
|
*
|
|
1767
|
-
* @param where -
|
|
1803
|
+
* @param where - SQL condition to match records
|
|
1768
1804
|
* @returns Promise resolving to the count of deleted records
|
|
1769
1805
|
*
|
|
1770
1806
|
* @example
|
|
1771
1807
|
* ```typescript
|
|
1772
|
-
*
|
|
1773
|
-
*
|
|
1774
|
-
*
|
|
1775
|
-
*
|
|
1808
|
+
* import { lt } from 'drizzle-orm';
|
|
1809
|
+
*
|
|
1810
|
+
* const result = await productRepository.deleteMany(
|
|
1811
|
+
* lt(products.createdAt, new Date('2020-01-01'))
|
|
1812
|
+
* );
|
|
1776
1813
|
* console.log(`Deleted ${result.count} products`);
|
|
1777
1814
|
* ```
|
|
1778
1815
|
*/
|
|
1779
1816
|
async deleteMany(where) {
|
|
1780
1817
|
this.logger.log("Deleting multiple records");
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1818
|
+
const result = await this.db.delete(this.table).where(where);
|
|
1819
|
+
return {
|
|
1820
|
+
count: result.rowCount ?? 0
|
|
1821
|
+
};
|
|
1784
1822
|
}
|
|
1785
1823
|
/**
|
|
1786
1824
|
* Count records
|
|
1787
1825
|
*
|
|
1788
|
-
* @param where - Optional
|
|
1826
|
+
* @param where - Optional SQL condition to filter records
|
|
1789
1827
|
* @returns Promise resolving to the count of records
|
|
1790
1828
|
*
|
|
1791
1829
|
* @example
|
|
1792
1830
|
* ```typescript
|
|
1831
|
+
* import { eq } from 'drizzle-orm';
|
|
1832
|
+
*
|
|
1793
1833
|
* // Count all products
|
|
1794
1834
|
* const total = await productRepository.count();
|
|
1795
1835
|
*
|
|
1796
1836
|
* // Count active products
|
|
1797
|
-
* const activeCount = await productRepository.count(
|
|
1837
|
+
* const activeCount = await productRepository.count(
|
|
1838
|
+
* eq(products.status, 'ACTIVE')
|
|
1839
|
+
* );
|
|
1798
1840
|
* ```
|
|
1799
1841
|
*/
|
|
1800
1842
|
async count(where) {
|
|
1801
1843
|
this.logger.debug("Counting records");
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
});
|
|
1844
|
+
let query = this.db.select({
|
|
1845
|
+
count: import_drizzle_orm3.sql`count(*)::int`
|
|
1846
|
+
}).from(this.table).$dynamic();
|
|
1847
|
+
if (where) {
|
|
1848
|
+
query = query.where(where);
|
|
1849
|
+
}
|
|
1850
|
+
const results = await query;
|
|
1851
|
+
return results[0].count;
|
|
1805
1852
|
}
|
|
1806
1853
|
/**
|
|
1807
1854
|
* Check if a record exists
|
|
1808
1855
|
*
|
|
1809
|
-
* @param where -
|
|
1856
|
+
* @param where - SQL condition to match records
|
|
1810
1857
|
* @returns Promise resolving to true if at least one record exists, false otherwise
|
|
1811
1858
|
*
|
|
1812
1859
|
* @example
|
|
1813
1860
|
* ```typescript
|
|
1814
|
-
*
|
|
1815
|
-
*
|
|
1816
|
-
*
|
|
1861
|
+
* import { eq } from 'drizzle-orm';
|
|
1862
|
+
*
|
|
1863
|
+
* const skuExists = await productRepository.exists(
|
|
1864
|
+
* eq(products.sku, 'WDG-001')
|
|
1865
|
+
* );
|
|
1817
1866
|
* ```
|
|
1818
1867
|
*/
|
|
1819
1868
|
async exists(where) {
|
|
1820
|
-
const count = await this.
|
|
1821
|
-
where
|
|
1822
|
-
});
|
|
1869
|
+
const count = await this.count(where);
|
|
1823
1870
|
return count > 0;
|
|
1824
1871
|
}
|
|
1825
1872
|
};
|
|
@@ -1976,15 +2023,16 @@ var HttpExceptionFilter = class _HttpExceptionFilter {
|
|
|
1976
2023
|
const exceptionResponse = exception.getResponse();
|
|
1977
2024
|
if (typeof exceptionResponse === "object" && exceptionResponse !== null) {
|
|
1978
2025
|
const responseObj = exceptionResponse;
|
|
1979
|
-
if (
|
|
2026
|
+
if ("errors" in responseObj && Array.isArray(responseObj.errors)) {
|
|
1980
2027
|
errors = responseObj.errors;
|
|
1981
|
-
detail = responseObj.detail || exception.message;
|
|
1982
|
-
} else if (
|
|
2028
|
+
detail = ("detail" in responseObj ? responseObj.detail : void 0) || exception.message;
|
|
2029
|
+
} else if ("message" in responseObj && Array.isArray(responseObj.message)) {
|
|
1983
2030
|
errors = responseObj.message.map((msg) => {
|
|
1984
|
-
if (typeof msg === "object" &&
|
|
2031
|
+
if (typeof msg === "object" && "property" in msg && "constraints" in msg) {
|
|
2032
|
+
const constraintValues = Object.values(msg.constraints);
|
|
1985
2033
|
return {
|
|
1986
2034
|
field: msg.property,
|
|
1987
|
-
message:
|
|
2035
|
+
message: constraintValues[0] ?? "Validation failed"
|
|
1988
2036
|
};
|
|
1989
2037
|
}
|
|
1990
2038
|
return {
|
|
@@ -1992,13 +2040,14 @@ var HttpExceptionFilter = class _HttpExceptionFilter {
|
|
|
1992
2040
|
};
|
|
1993
2041
|
});
|
|
1994
2042
|
detail = "Validation failed";
|
|
1995
|
-
} else if (responseObj
|
|
2043
|
+
} else if ("message" in responseObj) {
|
|
2044
|
+
const message = responseObj.message;
|
|
1996
2045
|
errors = [
|
|
1997
2046
|
{
|
|
1998
|
-
message: Array.isArray(
|
|
2047
|
+
message: Array.isArray(message) ? message.join(", ") : message
|
|
1999
2048
|
}
|
|
2000
2049
|
];
|
|
2001
|
-
detail = responseObj.error || exception.message;
|
|
2050
|
+
detail = ("error" in responseObj ? responseObj.error : void 0) || exception.message;
|
|
2002
2051
|
}
|
|
2003
2052
|
} else if (typeof exceptionResponse === "string") {
|
|
2004
2053
|
errors = [
|
|
@@ -2009,7 +2058,9 @@ var HttpExceptionFilter = class _HttpExceptionFilter {
|
|
|
2009
2058
|
detail = exceptionResponse;
|
|
2010
2059
|
}
|
|
2011
2060
|
} else {
|
|
2012
|
-
|
|
2061
|
+
const errorMessage = exception instanceof Error ? exception.message : "Unknown error";
|
|
2062
|
+
const stack = exception instanceof Error ? exception.stack : void 0;
|
|
2063
|
+
this.logger.error(`Unexpected error: ${errorMessage}`, stack);
|
|
2013
2064
|
errors = [
|
|
2014
2065
|
{
|
|
2015
2066
|
message: "An unexpected error occurred"
|
|
@@ -2412,6 +2463,885 @@ var BadGatewayException = class extends BaseFieldException {
|
|
|
2412
2463
|
}
|
|
2413
2464
|
}
|
|
2414
2465
|
};
|
|
2466
|
+
|
|
2467
|
+
// src/logger/logger.module.ts
|
|
2468
|
+
var import_common41 = require("@nestjs/common");
|
|
2469
|
+
|
|
2470
|
+
// src/logger/services/logger.service.ts
|
|
2471
|
+
var import_common38 = require("@nestjs/common");
|
|
2472
|
+
var import_winston = require("winston");
|
|
2473
|
+
var import_winston_daily_rotate_file = __toESM(require("winston-daily-rotate-file"), 1);
|
|
2474
|
+
|
|
2475
|
+
// src/logger/utils/index.ts
|
|
2476
|
+
var import_node_async_hooks = require("async_hooks");
|
|
2477
|
+
var import_node_crypto = require("crypto");
|
|
2478
|
+
var correlationStorage = new import_node_async_hooks.AsyncLocalStorage();
|
|
2479
|
+
function getCorrelationContext() {
|
|
2480
|
+
return correlationStorage.getStore();
|
|
2481
|
+
}
|
|
2482
|
+
__name(getCorrelationContext, "getCorrelationContext");
|
|
2483
|
+
function runWithCorrelationContext(context, callback) {
|
|
2484
|
+
return correlationStorage.run(context, callback);
|
|
2485
|
+
}
|
|
2486
|
+
__name(runWithCorrelationContext, "runWithCorrelationContext");
|
|
2487
|
+
function updateCorrelationContext(updates) {
|
|
2488
|
+
const context = correlationStorage.getStore();
|
|
2489
|
+
if (context) {
|
|
2490
|
+
Object.assign(context, updates);
|
|
2491
|
+
}
|
|
2492
|
+
}
|
|
2493
|
+
__name(updateCorrelationContext, "updateCorrelationContext");
|
|
2494
|
+
var DEFAULT_CORRELATION_HEADER = "x-correlation-id";
|
|
2495
|
+
function generateCorrelationId() {
|
|
2496
|
+
return (0, import_node_crypto.randomUUID)();
|
|
2497
|
+
}
|
|
2498
|
+
__name(generateCorrelationId, "generateCorrelationId");
|
|
2499
|
+
function addCorrelationIdToResponse(reply, correlationId, headerName = DEFAULT_CORRELATION_HEADER) {
|
|
2500
|
+
if (typeof reply.header === "function") {
|
|
2501
|
+
reply.header(headerName, correlationId);
|
|
2502
|
+
} else if (reply.raw && typeof reply.raw.setHeader === "function") {
|
|
2503
|
+
reply.raw.setHeader(headerName, correlationId);
|
|
2504
|
+
}
|
|
2505
|
+
}
|
|
2506
|
+
__name(addCorrelationIdToResponse, "addCorrelationIdToResponse");
|
|
2507
|
+
|
|
2508
|
+
// src/logger/services/logger.service.ts
|
|
2509
|
+
function _ts_decorate14(decorators, target, key, desc) {
|
|
2510
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
2511
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
2512
|
+
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;
|
|
2513
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
2514
|
+
}
|
|
2515
|
+
__name(_ts_decorate14, "_ts_decorate");
|
|
2516
|
+
function _ts_metadata8(k, v) {
|
|
2517
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
2518
|
+
}
|
|
2519
|
+
__name(_ts_metadata8, "_ts_metadata");
|
|
2520
|
+
function _ts_param4(paramIndex, decorator) {
|
|
2521
|
+
return function(target, key) {
|
|
2522
|
+
decorator(target, key, paramIndex);
|
|
2523
|
+
};
|
|
2524
|
+
}
|
|
2525
|
+
__name(_ts_param4, "_ts_param");
|
|
2526
|
+
var LoggerService2 = class _LoggerService {
|
|
2527
|
+
static {
|
|
2528
|
+
__name(this, "LoggerService");
|
|
2529
|
+
}
|
|
2530
|
+
defaultLogger;
|
|
2531
|
+
activeLogger;
|
|
2532
|
+
options;
|
|
2533
|
+
context;
|
|
2534
|
+
constructor(options = {}, defaultLogger) {
|
|
2535
|
+
this.defaultLogger = defaultLogger;
|
|
2536
|
+
this.options = options;
|
|
2537
|
+
const provider = options.provider ?? "winston";
|
|
2538
|
+
if (provider === "default") {
|
|
2539
|
+
if (!this.defaultLogger) {
|
|
2540
|
+
throw new Error("LoggerService: Default Logger not provided");
|
|
2541
|
+
}
|
|
2542
|
+
this.activeLogger = this.defaultLogger;
|
|
2543
|
+
} else {
|
|
2544
|
+
this.activeLogger = this.createWinstonLogger(options);
|
|
2545
|
+
}
|
|
2546
|
+
}
|
|
2547
|
+
/**
|
|
2548
|
+
* Creates a Winston logger instance with inline configuration.
|
|
2549
|
+
* Consolidates winston-config.factory.ts logic.
|
|
2550
|
+
*/
|
|
2551
|
+
createWinstonLogger(opts) {
|
|
2552
|
+
const level = opts.level ?? "debug";
|
|
2553
|
+
const logFormat = opts.format ?? "text";
|
|
2554
|
+
const baseFormatters = [
|
|
2555
|
+
import_winston.format.timestamp({
|
|
2556
|
+
format: "YYYY-MM-DDTHH:mm:ss.SSSZ"
|
|
2557
|
+
}),
|
|
2558
|
+
import_winston.format.errors({
|
|
2559
|
+
stack: true
|
|
2560
|
+
})
|
|
2561
|
+
];
|
|
2562
|
+
const consoleTransport = logFormat === "json" ? new import_winston.transports.Console({
|
|
2563
|
+
level,
|
|
2564
|
+
format: import_winston.format.combine(...baseFormatters, import_winston.format.json())
|
|
2565
|
+
}) : new import_winston.transports.Console({
|
|
2566
|
+
level,
|
|
2567
|
+
format: import_winston.format.combine(...baseFormatters, import_winston.format.printf((info) => {
|
|
2568
|
+
const { timestamp, level: level2, message, context, correlationId, trace } = info;
|
|
2569
|
+
const parts = [
|
|
2570
|
+
timestamp,
|
|
2571
|
+
level2.toUpperCase().padEnd(7),
|
|
2572
|
+
correlationId ? `[${correlationId.toString().slice(-6)}]` : "",
|
|
2573
|
+
context ? `[${context}]` : "",
|
|
2574
|
+
message
|
|
2575
|
+
].filter(Boolean);
|
|
2576
|
+
let output = parts.join(" ");
|
|
2577
|
+
if (trace) {
|
|
2578
|
+
output += "\n" + trace;
|
|
2579
|
+
}
|
|
2580
|
+
return output;
|
|
2581
|
+
}), import_winston.format.colorize({
|
|
2582
|
+
all: true
|
|
2583
|
+
}))
|
|
2584
|
+
});
|
|
2585
|
+
const winstonTransports = [
|
|
2586
|
+
consoleTransport
|
|
2587
|
+
];
|
|
2588
|
+
if (opts.enableFileLogger) {
|
|
2589
|
+
const filePath = opts.filePath ?? "./logs";
|
|
2590
|
+
const maxFiles = opts.maxFiles ?? "14d";
|
|
2591
|
+
winstonTransports.push(new import_winston_daily_rotate_file.default({
|
|
2592
|
+
level,
|
|
2593
|
+
filename: `${filePath}/%DATE%-combined.log`,
|
|
2594
|
+
datePattern: "YYYY-MM-DD",
|
|
2595
|
+
maxSize: "20m",
|
|
2596
|
+
maxFiles,
|
|
2597
|
+
format: import_winston.format.combine(import_winston.format.timestamp(), import_winston.format.json())
|
|
2598
|
+
}), new import_winston_daily_rotate_file.default({
|
|
2599
|
+
level: "error",
|
|
2600
|
+
filename: `${filePath}/%DATE%-error.log`,
|
|
2601
|
+
datePattern: "YYYY-MM-DD",
|
|
2602
|
+
maxSize: "20m",
|
|
2603
|
+
maxFiles,
|
|
2604
|
+
format: import_winston.format.combine(import_winston.format.timestamp(), import_winston.format.json())
|
|
2605
|
+
}));
|
|
2606
|
+
}
|
|
2607
|
+
const config = {
|
|
2608
|
+
level,
|
|
2609
|
+
transports: winstonTransports,
|
|
2610
|
+
exitOnError: false
|
|
2611
|
+
};
|
|
2612
|
+
if (opts.defaultMeta || opts.appName) {
|
|
2613
|
+
config.defaultMeta = {
|
|
2614
|
+
...opts.defaultMeta,
|
|
2615
|
+
appName: opts.appName,
|
|
2616
|
+
environment: opts.environment
|
|
2617
|
+
};
|
|
2618
|
+
}
|
|
2619
|
+
return (0, import_winston.createLogger)(config);
|
|
2620
|
+
}
|
|
2621
|
+
// NestJS LoggerService interface methods
|
|
2622
|
+
log(message, context) {
|
|
2623
|
+
this._log("log", message, context);
|
|
2624
|
+
}
|
|
2625
|
+
error(message, trace, context) {
|
|
2626
|
+
this._log("error", message, context, trace);
|
|
2627
|
+
}
|
|
2628
|
+
warn(message, context) {
|
|
2629
|
+
this._log("warn", message, context);
|
|
2630
|
+
}
|
|
2631
|
+
debug(message, context) {
|
|
2632
|
+
this._log("debug", message, context);
|
|
2633
|
+
}
|
|
2634
|
+
verbose(message, context) {
|
|
2635
|
+
this._log("verbose", message, context);
|
|
2636
|
+
}
|
|
2637
|
+
setContext(context) {
|
|
2638
|
+
this.context = context;
|
|
2639
|
+
}
|
|
2640
|
+
/**
|
|
2641
|
+
* Unified internal logging method that handles both Winston and NestJS Logger.
|
|
2642
|
+
*/
|
|
2643
|
+
_log(level, message, context, trace) {
|
|
2644
|
+
const ctx = context ?? this.context;
|
|
2645
|
+
if ("format" in this.activeLogger && "transports" in this.activeLogger) {
|
|
2646
|
+
const winstonLogger = this.activeLogger;
|
|
2647
|
+
const winstonLevel = level === "log" ? "info" : level;
|
|
2648
|
+
const formattedMessage = this.formatMessage(message);
|
|
2649
|
+
const metadata = this.enrichMetadata({}, ctx, trace);
|
|
2650
|
+
winstonLogger.log({
|
|
2651
|
+
level: winstonLevel,
|
|
2652
|
+
message: formattedMessage,
|
|
2653
|
+
...metadata
|
|
2654
|
+
});
|
|
2655
|
+
} else {
|
|
2656
|
+
const nestLogger = this.activeLogger;
|
|
2657
|
+
if (level === "error" && trace) {
|
|
2658
|
+
ctx ? nestLogger.error(message, trace, ctx) : nestLogger.error(message, trace);
|
|
2659
|
+
} else if (level === "log") {
|
|
2660
|
+
ctx ? nestLogger.log(message, ctx) : nestLogger.log(message);
|
|
2661
|
+
} else if (level === "warn") {
|
|
2662
|
+
ctx ? nestLogger.warn(message, ctx) : nestLogger.warn(message);
|
|
2663
|
+
} else if (level === "debug" && nestLogger.debug) {
|
|
2664
|
+
ctx ? nestLogger.debug(message, ctx) : nestLogger.debug(message);
|
|
2665
|
+
} else if (level === "verbose" && nestLogger.verbose) {
|
|
2666
|
+
ctx ? nestLogger.verbose(message, ctx) : nestLogger.verbose(message);
|
|
2667
|
+
}
|
|
2668
|
+
}
|
|
2669
|
+
}
|
|
2670
|
+
/**
|
|
2671
|
+
* Logs with custom metadata (Winston only).
|
|
2672
|
+
*/
|
|
2673
|
+
logWithMetadata(level, message, metadata, context) {
|
|
2674
|
+
const ctx = context ?? this.context;
|
|
2675
|
+
if ("format" in this.activeLogger && "transports" in this.activeLogger) {
|
|
2676
|
+
const winstonLogger = this.activeLogger;
|
|
2677
|
+
const winstonLevel = level === "log" ? "info" : level;
|
|
2678
|
+
winstonLogger.log({
|
|
2679
|
+
level: winstonLevel,
|
|
2680
|
+
message: this.formatMessage(message),
|
|
2681
|
+
...metadata
|
|
2682
|
+
});
|
|
2683
|
+
} else {
|
|
2684
|
+
const messageWithMeta = metadata ? `${message} ${JSON.stringify(metadata)}` : message;
|
|
2685
|
+
this[level](messageWithMeta, ctx);
|
|
2686
|
+
}
|
|
2687
|
+
}
|
|
2688
|
+
formatMessage(message) {
|
|
2689
|
+
if (message instanceof Error) return message.message;
|
|
2690
|
+
if (typeof message === "object" && message !== null) {
|
|
2691
|
+
try {
|
|
2692
|
+
return JSON.stringify(message);
|
|
2693
|
+
} catch {
|
|
2694
|
+
return String(message);
|
|
2695
|
+
}
|
|
2696
|
+
}
|
|
2697
|
+
return String(message);
|
|
2698
|
+
}
|
|
2699
|
+
/**
|
|
2700
|
+
* Enriches metadata with correlation context from AsyncLocalStorage.
|
|
2701
|
+
* Inline from winston-logger.service.ts
|
|
2702
|
+
*/
|
|
2703
|
+
enrichMetadata(metadata = {}, context, trace) {
|
|
2704
|
+
const enriched = {
|
|
2705
|
+
...metadata
|
|
2706
|
+
};
|
|
2707
|
+
if (context) enriched.context = context;
|
|
2708
|
+
const correlationContext = getCorrelationContext();
|
|
2709
|
+
if (correlationContext) {
|
|
2710
|
+
if (correlationContext.correlationId) enriched.correlationId = correlationContext.correlationId;
|
|
2711
|
+
for (const [key, value] of Object.entries(correlationContext)) {
|
|
2712
|
+
if (key !== "correlationId") {
|
|
2713
|
+
enriched[key] = value;
|
|
2714
|
+
}
|
|
2715
|
+
}
|
|
2716
|
+
}
|
|
2717
|
+
if (trace) enriched.trace = trace;
|
|
2718
|
+
return enriched;
|
|
2719
|
+
}
|
|
2720
|
+
child(context) {
|
|
2721
|
+
const childLogger = new _LoggerService(this.options, this.defaultLogger);
|
|
2722
|
+
childLogger.setContext(context);
|
|
2723
|
+
return childLogger;
|
|
2724
|
+
}
|
|
2725
|
+
};
|
|
2726
|
+
LoggerService2 = _ts_decorate14([
|
|
2727
|
+
(0, import_common38.Injectable)(),
|
|
2728
|
+
_ts_param4(0, (0, import_common38.Optional)()),
|
|
2729
|
+
_ts_param4(1, (0, import_common38.Optional)()),
|
|
2730
|
+
_ts_metadata8("design:type", Function),
|
|
2731
|
+
_ts_metadata8("design:paramtypes", [
|
|
2732
|
+
typeof LoggerModuleOptions === "undefined" ? Object : LoggerModuleOptions,
|
|
2733
|
+
typeof import_common38.Logger === "undefined" ? Object : import_common38.Logger
|
|
2734
|
+
])
|
|
2735
|
+
], LoggerService2);
|
|
2736
|
+
|
|
2737
|
+
// src/logger/middleware/correlation-id.middleware.ts
|
|
2738
|
+
var import_common39 = require("@nestjs/common");
|
|
2739
|
+
function _ts_decorate15(decorators, target, key, desc) {
|
|
2740
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
2741
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
2742
|
+
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;
|
|
2743
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
2744
|
+
}
|
|
2745
|
+
__name(_ts_decorate15, "_ts_decorate");
|
|
2746
|
+
function _ts_metadata9(k, v) {
|
|
2747
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
2748
|
+
}
|
|
2749
|
+
__name(_ts_metadata9, "_ts_metadata");
|
|
2750
|
+
var CorrelationIdMiddleware = class {
|
|
2751
|
+
static {
|
|
2752
|
+
__name(this, "CorrelationIdMiddleware");
|
|
2753
|
+
}
|
|
2754
|
+
includeInResponse;
|
|
2755
|
+
responseHeader;
|
|
2756
|
+
constructor(options = {}) {
|
|
2757
|
+
this.includeInResponse = options.includeInResponse ?? true;
|
|
2758
|
+
this.responseHeader = options.responseHeader ?? DEFAULT_CORRELATION_HEADER;
|
|
2759
|
+
}
|
|
2760
|
+
/**
|
|
2761
|
+
* Middleware handler for processing requests.
|
|
2762
|
+
*/
|
|
2763
|
+
use(req, reply, next) {
|
|
2764
|
+
const correlationId = generateCorrelationId();
|
|
2765
|
+
if (this.includeInResponse) {
|
|
2766
|
+
addCorrelationIdToResponse(reply, correlationId, this.responseHeader);
|
|
2767
|
+
}
|
|
2768
|
+
runWithCorrelationContext({
|
|
2769
|
+
correlationId
|
|
2770
|
+
}, () => {
|
|
2771
|
+
next();
|
|
2772
|
+
});
|
|
2773
|
+
}
|
|
2774
|
+
/**
|
|
2775
|
+
* Fastify hook handler for onRequest.
|
|
2776
|
+
* This is an async function that returns a Promise, ensuring the AsyncLocalStorage
|
|
2777
|
+
* context persists throughout the entire request lifecycle.
|
|
2778
|
+
*/
|
|
2779
|
+
async onRequest(req, reply) {
|
|
2780
|
+
const correlationId = generateCorrelationId();
|
|
2781
|
+
if (this.includeInResponse) {
|
|
2782
|
+
addCorrelationIdToResponse(reply, correlationId, this.responseHeader);
|
|
2783
|
+
}
|
|
2784
|
+
const store = correlationStorage.getStore();
|
|
2785
|
+
if (!store) {
|
|
2786
|
+
correlationStorage.enterWith({
|
|
2787
|
+
correlationId
|
|
2788
|
+
});
|
|
2789
|
+
}
|
|
2790
|
+
}
|
|
2791
|
+
};
|
|
2792
|
+
CorrelationIdMiddleware = _ts_decorate15([
|
|
2793
|
+
(0, import_common39.Injectable)(),
|
|
2794
|
+
_ts_metadata9("design:type", Function),
|
|
2795
|
+
_ts_metadata9("design:paramtypes", [
|
|
2796
|
+
typeof CorrelationIdMiddlewareOptions === "undefined" ? Object : CorrelationIdMiddlewareOptions
|
|
2797
|
+
])
|
|
2798
|
+
], CorrelationIdMiddleware);
|
|
2799
|
+
|
|
2800
|
+
// src/logger/interceptors/http-logger.interceptor.ts
|
|
2801
|
+
var import_common40 = require("@nestjs/common");
|
|
2802
|
+
var import_operators2 = require("rxjs/operators");
|
|
2803
|
+
function _ts_decorate16(decorators, target, key, desc) {
|
|
2804
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
2805
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
2806
|
+
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;
|
|
2807
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
2808
|
+
}
|
|
2809
|
+
__name(_ts_decorate16, "_ts_decorate");
|
|
2810
|
+
function _ts_metadata10(k, v) {
|
|
2811
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
2812
|
+
}
|
|
2813
|
+
__name(_ts_metadata10, "_ts_metadata");
|
|
2814
|
+
function _ts_param5(paramIndex, decorator) {
|
|
2815
|
+
return function(target, key) {
|
|
2816
|
+
decorator(target, key, paramIndex);
|
|
2817
|
+
};
|
|
2818
|
+
}
|
|
2819
|
+
__name(_ts_param5, "_ts_param");
|
|
2820
|
+
var HttpLoggerInterceptor = class {
|
|
2821
|
+
static {
|
|
2822
|
+
__name(this, "HttpLoggerInterceptor");
|
|
2823
|
+
}
|
|
2824
|
+
logger;
|
|
2825
|
+
enableRequestLog;
|
|
2826
|
+
enableResponseLog;
|
|
2827
|
+
slowRequestThreshold;
|
|
2828
|
+
constructor(logger, options) {
|
|
2829
|
+
this.logger = logger;
|
|
2830
|
+
this.enableRequestLog = options?.enableRequestLog ?? true;
|
|
2831
|
+
this.enableResponseLog = options?.enableResponseLog ?? true;
|
|
2832
|
+
this.slowRequestThreshold = options?.slowRequestThreshold ?? 3e3;
|
|
2833
|
+
}
|
|
2834
|
+
intercept(context, next) {
|
|
2835
|
+
if (context.getType() !== "http") {
|
|
2836
|
+
return next.handle();
|
|
2837
|
+
}
|
|
2838
|
+
const httpContext = context.switchToHttp();
|
|
2839
|
+
const request = httpContext.getRequest();
|
|
2840
|
+
const response = httpContext.getResponse();
|
|
2841
|
+
const startTime = Date.now();
|
|
2842
|
+
if (this.enableRequestLog) {
|
|
2843
|
+
this.logRequest(request);
|
|
2844
|
+
}
|
|
2845
|
+
return next.handle().pipe((0, import_operators2.tap)(() => {
|
|
2846
|
+
if (this.enableResponseLog) {
|
|
2847
|
+
const duration = Date.now() - startTime;
|
|
2848
|
+
this.logResponse(request, response, duration);
|
|
2849
|
+
}
|
|
2850
|
+
}), (0, import_operators2.catchError)((error) => {
|
|
2851
|
+
const duration = Date.now() - startTime;
|
|
2852
|
+
this.logError(request, response, duration, error);
|
|
2853
|
+
throw error;
|
|
2854
|
+
}));
|
|
2855
|
+
}
|
|
2856
|
+
logRequest(request) {
|
|
2857
|
+
try {
|
|
2858
|
+
const correlationContext = getCorrelationContext();
|
|
2859
|
+
const metadata = {
|
|
2860
|
+
type: "http_request",
|
|
2861
|
+
method: request.method,
|
|
2862
|
+
url: request.url,
|
|
2863
|
+
correlationId: correlationContext?.correlationId,
|
|
2864
|
+
ip: request.ip,
|
|
2865
|
+
userAgent: request.headers["user-agent"]
|
|
2866
|
+
};
|
|
2867
|
+
this.logger.logWithMetadata("log", `Incoming ${request.method} ${request.url}`, metadata);
|
|
2868
|
+
} catch (error) {
|
|
2869
|
+
this.logger.error("Failed to log HTTP request", error.stack);
|
|
2870
|
+
}
|
|
2871
|
+
}
|
|
2872
|
+
logResponse(request, response, duration) {
|
|
2873
|
+
try {
|
|
2874
|
+
const correlationContext = getCorrelationContext();
|
|
2875
|
+
const statusCode = response.statusCode;
|
|
2876
|
+
const logLevel = statusCode >= 500 ? "error" : statusCode >= 400 ? "warn" : "log";
|
|
2877
|
+
const metadata = {
|
|
2878
|
+
type: "http_response",
|
|
2879
|
+
method: request.method,
|
|
2880
|
+
url: request.url,
|
|
2881
|
+
statusCode,
|
|
2882
|
+
duration,
|
|
2883
|
+
correlationId: correlationContext?.correlationId
|
|
2884
|
+
};
|
|
2885
|
+
if (duration > this.slowRequestThreshold) {
|
|
2886
|
+
metadata.slowRequest = true;
|
|
2887
|
+
}
|
|
2888
|
+
const message = metadata.slowRequest ? `SLOW ${request.method} ${request.url} ${statusCode} - ${duration}ms` : `${request.method} ${request.url} ${statusCode} - ${duration}ms`;
|
|
2889
|
+
this.logger.logWithMetadata(logLevel, message, metadata);
|
|
2890
|
+
} catch (error) {
|
|
2891
|
+
this.logger.error("Failed to log HTTP response", error.stack);
|
|
2892
|
+
}
|
|
2893
|
+
}
|
|
2894
|
+
logError(request, response, duration, error) {
|
|
2895
|
+
try {
|
|
2896
|
+
const correlationContext = getCorrelationContext();
|
|
2897
|
+
const statusCode = response.statusCode || 500;
|
|
2898
|
+
const metadata = {
|
|
2899
|
+
type: "http_error",
|
|
2900
|
+
method: request.method,
|
|
2901
|
+
url: request.url,
|
|
2902
|
+
statusCode,
|
|
2903
|
+
duration,
|
|
2904
|
+
correlationId: correlationContext?.correlationId,
|
|
2905
|
+
errorName: error?.name || "Error",
|
|
2906
|
+
errorMessage: error?.message || "Unknown error"
|
|
2907
|
+
};
|
|
2908
|
+
if (error?.stack) {
|
|
2909
|
+
metadata.trace = error.stack;
|
|
2910
|
+
}
|
|
2911
|
+
if (error?.response) {
|
|
2912
|
+
metadata.errorDetails = error.response;
|
|
2913
|
+
}
|
|
2914
|
+
const message = `ERROR ${request.method} ${request.url} ${statusCode} - ${error?.message || "Unknown error"}`;
|
|
2915
|
+
this.logger.logWithMetadata("error", message, metadata);
|
|
2916
|
+
} catch (loggingError) {
|
|
2917
|
+
this.logger.error("Failed to log HTTP error", loggingError.stack);
|
|
2918
|
+
}
|
|
2919
|
+
}
|
|
2920
|
+
};
|
|
2921
|
+
HttpLoggerInterceptor = _ts_decorate16([
|
|
2922
|
+
(0, import_common40.Injectable)(),
|
|
2923
|
+
_ts_param5(1, (0, import_common40.Optional)()),
|
|
2924
|
+
_ts_metadata10("design:type", Function),
|
|
2925
|
+
_ts_metadata10("design:paramtypes", [
|
|
2926
|
+
typeof LoggerService === "undefined" ? Object : LoggerService,
|
|
2927
|
+
typeof HttpLoggerOptions === "undefined" ? Object : HttpLoggerOptions
|
|
2928
|
+
])
|
|
2929
|
+
], HttpLoggerInterceptor);
|
|
2930
|
+
|
|
2931
|
+
// src/logger/logger.module.ts
|
|
2932
|
+
function _ts_decorate17(decorators, target, key, desc) {
|
|
2933
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
2934
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
2935
|
+
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;
|
|
2936
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
2937
|
+
}
|
|
2938
|
+
__name(_ts_decorate17, "_ts_decorate");
|
|
2939
|
+
var LOGGER_MODULE_OPTIONS = Symbol("LOGGER_MODULE_OPTIONS");
|
|
2940
|
+
var DEFAULT_LOGGER_OPTIONS = {
|
|
2941
|
+
provider: "winston",
|
|
2942
|
+
enableCorrelationId: true,
|
|
2943
|
+
enableHttpLogger: true,
|
|
2944
|
+
filePath: "./logs",
|
|
2945
|
+
maxFiles: "14d"
|
|
2946
|
+
};
|
|
2947
|
+
var ENVIRONMENT_PRESETS = {
|
|
2948
|
+
/**
|
|
2949
|
+
* Development preset - maximum verbosity for local development
|
|
2950
|
+
*/
|
|
2951
|
+
development: {
|
|
2952
|
+
provider: "winston",
|
|
2953
|
+
level: "debug",
|
|
2954
|
+
format: "text",
|
|
2955
|
+
enableFileLogger: false,
|
|
2956
|
+
enableCorrelationId: true,
|
|
2957
|
+
enableHttpLogger: true,
|
|
2958
|
+
httpLogger: {
|
|
2959
|
+
enableRequestLog: true,
|
|
2960
|
+
enableResponseLog: true,
|
|
2961
|
+
slowRequestThreshold: 1e3
|
|
2962
|
+
}
|
|
2963
|
+
},
|
|
2964
|
+
/**
|
|
2965
|
+
* Staging preset - moderate verbosity with file logging
|
|
2966
|
+
*/
|
|
2967
|
+
staging: {
|
|
2968
|
+
provider: "winston",
|
|
2969
|
+
level: "log",
|
|
2970
|
+
format: "json",
|
|
2971
|
+
enableFileLogger: true,
|
|
2972
|
+
enableCorrelationId: true,
|
|
2973
|
+
enableHttpLogger: true,
|
|
2974
|
+
httpLogger: {
|
|
2975
|
+
enableRequestLog: true,
|
|
2976
|
+
enableResponseLog: true,
|
|
2977
|
+
slowRequestThreshold: 3e3
|
|
2978
|
+
}
|
|
2979
|
+
},
|
|
2980
|
+
/**
|
|
2981
|
+
* Production preset - minimal verbosity with all safety features enabled
|
|
2982
|
+
*/
|
|
2983
|
+
production: {
|
|
2984
|
+
provider: "winston",
|
|
2985
|
+
level: "warn",
|
|
2986
|
+
format: "json",
|
|
2987
|
+
enableFileLogger: true,
|
|
2988
|
+
enableCorrelationId: true,
|
|
2989
|
+
enableHttpLogger: true,
|
|
2990
|
+
httpLogger: {
|
|
2991
|
+
enableRequestLog: false,
|
|
2992
|
+
enableResponseLog: true,
|
|
2993
|
+
slowRequestThreshold: 5e3
|
|
2994
|
+
}
|
|
2995
|
+
},
|
|
2996
|
+
/**
|
|
2997
|
+
* Test preset - errors only, minimal features for faster test execution
|
|
2998
|
+
*/
|
|
2999
|
+
test: {
|
|
3000
|
+
provider: "winston",
|
|
3001
|
+
level: "error",
|
|
3002
|
+
format: "json",
|
|
3003
|
+
enableFileLogger: false,
|
|
3004
|
+
enableCorrelationId: false,
|
|
3005
|
+
enableHttpLogger: false
|
|
3006
|
+
}
|
|
3007
|
+
};
|
|
3008
|
+
function mergeWithDefaults(options = {}) {
|
|
3009
|
+
const preset = options.environment ? ENVIRONMENT_PRESETS[options.environment] ?? ENVIRONMENT_PRESETS.development : ENVIRONMENT_PRESETS.development;
|
|
3010
|
+
const filteredOptions = Object.fromEntries(Object.entries(options).filter(([_, value]) => value !== void 0));
|
|
3011
|
+
if (filteredOptions.httpLogger && preset?.httpLogger) {
|
|
3012
|
+
filteredOptions.httpLogger = {
|
|
3013
|
+
...preset.httpLogger,
|
|
3014
|
+
...Object.fromEntries(Object.entries(filteredOptions.httpLogger).filter(([_, value]) => value !== void 0))
|
|
3015
|
+
};
|
|
3016
|
+
}
|
|
3017
|
+
const merged = {
|
|
3018
|
+
...DEFAULT_LOGGER_OPTIONS,
|
|
3019
|
+
...preset,
|
|
3020
|
+
...filteredOptions
|
|
3021
|
+
};
|
|
3022
|
+
return merged;
|
|
3023
|
+
}
|
|
3024
|
+
__name(mergeWithDefaults, "mergeWithDefaults");
|
|
3025
|
+
function createDefaultLoggerProvider(options) {
|
|
3026
|
+
return {
|
|
3027
|
+
provide: import_common41.Logger,
|
|
3028
|
+
useFactory: /* @__PURE__ */ __name(() => {
|
|
3029
|
+
const logger = new import_common41.Logger();
|
|
3030
|
+
if (options.level && typeof logger.setLogLevels === "function") {
|
|
3031
|
+
const levels = getLevelsUpTo(options.level);
|
|
3032
|
+
logger.setLogLevels(levels);
|
|
3033
|
+
}
|
|
3034
|
+
return logger;
|
|
3035
|
+
}, "useFactory")
|
|
3036
|
+
};
|
|
3037
|
+
}
|
|
3038
|
+
__name(createDefaultLoggerProvider, "createDefaultLoggerProvider");
|
|
3039
|
+
function createLoggerProviders(options = {}) {
|
|
3040
|
+
const mergedOptions = mergeWithDefaults(options);
|
|
3041
|
+
const providers = [
|
|
3042
|
+
// Options provider
|
|
3043
|
+
{
|
|
3044
|
+
provide: LOGGER_MODULE_OPTIONS,
|
|
3045
|
+
useValue: mergedOptions
|
|
3046
|
+
}
|
|
3047
|
+
];
|
|
3048
|
+
if (mergedOptions.provider === "default") {
|
|
3049
|
+
providers.push(createDefaultLoggerProvider(mergedOptions));
|
|
3050
|
+
}
|
|
3051
|
+
providers.push({
|
|
3052
|
+
provide: LoggerService2,
|
|
3053
|
+
useFactory: /* @__PURE__ */ __name((opts, defaultLogger) => {
|
|
3054
|
+
return new LoggerService2(opts, defaultLogger);
|
|
3055
|
+
}, "useFactory"),
|
|
3056
|
+
inject: [
|
|
3057
|
+
LOGGER_MODULE_OPTIONS,
|
|
3058
|
+
{
|
|
3059
|
+
token: import_common41.Logger,
|
|
3060
|
+
optional: true
|
|
3061
|
+
}
|
|
3062
|
+
]
|
|
3063
|
+
});
|
|
3064
|
+
providers.push({
|
|
3065
|
+
provide: CorrelationIdMiddleware,
|
|
3066
|
+
useFactory: /* @__PURE__ */ __name(() => {
|
|
3067
|
+
return new CorrelationIdMiddleware({
|
|
3068
|
+
includeInResponse: true,
|
|
3069
|
+
responseHeader: "x-correlation-id"
|
|
3070
|
+
});
|
|
3071
|
+
}, "useFactory")
|
|
3072
|
+
});
|
|
3073
|
+
providers.push({
|
|
3074
|
+
provide: HttpLoggerInterceptor,
|
|
3075
|
+
useFactory: /* @__PURE__ */ __name((logger, opts) => {
|
|
3076
|
+
const httpLoggerOptions = opts.httpLogger ?? {
|
|
3077
|
+
enableRequestLog: opts.enableHttpLogger,
|
|
3078
|
+
enableResponseLog: opts.enableHttpLogger
|
|
3079
|
+
};
|
|
3080
|
+
return new HttpLoggerInterceptor(logger, httpLoggerOptions);
|
|
3081
|
+
}, "useFactory"),
|
|
3082
|
+
inject: [
|
|
3083
|
+
LoggerService2,
|
|
3084
|
+
LOGGER_MODULE_OPTIONS
|
|
3085
|
+
]
|
|
3086
|
+
});
|
|
3087
|
+
return providers;
|
|
3088
|
+
}
|
|
3089
|
+
__name(createLoggerProviders, "createLoggerProviders");
|
|
3090
|
+
function getLevelsUpTo(level) {
|
|
3091
|
+
const allLevels = [
|
|
3092
|
+
"error",
|
|
3093
|
+
"warn",
|
|
3094
|
+
"log",
|
|
3095
|
+
"debug",
|
|
3096
|
+
"verbose"
|
|
3097
|
+
];
|
|
3098
|
+
const isValidLevel = /* @__PURE__ */ __name((l) => allLevels.includes(l), "isValidLevel");
|
|
3099
|
+
if (!isValidLevel(level)) {
|
|
3100
|
+
return [
|
|
3101
|
+
"error",
|
|
3102
|
+
"warn",
|
|
3103
|
+
"log"
|
|
3104
|
+
];
|
|
3105
|
+
}
|
|
3106
|
+
const levelIndex = allLevels.indexOf(level);
|
|
3107
|
+
return allLevels.slice(0, levelIndex + 1);
|
|
3108
|
+
}
|
|
3109
|
+
__name(getLevelsUpTo, "getLevelsUpTo");
|
|
3110
|
+
var LoggerModule = class _LoggerModule {
|
|
3111
|
+
static {
|
|
3112
|
+
__name(this, "LoggerModule");
|
|
3113
|
+
}
|
|
3114
|
+
/**
|
|
3115
|
+
* Configures the logger module with static options.
|
|
3116
|
+
*
|
|
3117
|
+
* Users must explicitly pass `environment` to select a preset.
|
|
3118
|
+
* All preset values can be overridden by passing explicit options.
|
|
3119
|
+
*
|
|
3120
|
+
* @param options - Logger configuration options
|
|
3121
|
+
* @returns Dynamic module configuration
|
|
3122
|
+
*
|
|
3123
|
+
* @example
|
|
3124
|
+
* ```typescript
|
|
3125
|
+
* // Production preset with app name
|
|
3126
|
+
* LoggerModule.forRoot({
|
|
3127
|
+
* environment: 'production',
|
|
3128
|
+
* appName: 'my-service'
|
|
3129
|
+
* })
|
|
3130
|
+
*
|
|
3131
|
+
* // Development preset with custom level
|
|
3132
|
+
* LoggerModule.forRoot({
|
|
3133
|
+
* environment: 'development',
|
|
3134
|
+
* level: 'verbose',
|
|
3135
|
+
* enableFileLogger: true
|
|
3136
|
+
* })
|
|
3137
|
+
*
|
|
3138
|
+
* // Use default NestJS logger
|
|
3139
|
+
* LoggerModule.forRoot({
|
|
3140
|
+
* provider: 'default',
|
|
3141
|
+
* environment: 'development'
|
|
3142
|
+
* })
|
|
3143
|
+
* ```
|
|
3144
|
+
*/
|
|
3145
|
+
static forRoot(options = {}) {
|
|
3146
|
+
const providers = createLoggerProviders(options);
|
|
3147
|
+
return {
|
|
3148
|
+
module: _LoggerModule,
|
|
3149
|
+
providers,
|
|
3150
|
+
exports: [
|
|
3151
|
+
LoggerService2,
|
|
3152
|
+
CorrelationIdMiddleware,
|
|
3153
|
+
HttpLoggerInterceptor,
|
|
3154
|
+
LOGGER_MODULE_OPTIONS
|
|
3155
|
+
]
|
|
3156
|
+
};
|
|
3157
|
+
}
|
|
3158
|
+
/**
|
|
3159
|
+
* Configures the logger module with async options.
|
|
3160
|
+
*
|
|
3161
|
+
* Supports dynamic configuration using:
|
|
3162
|
+
* - `useFactory`: Factory function with dependency injection
|
|
3163
|
+
* - `useClass`: Class implementing `LoggerOptionsFactory`
|
|
3164
|
+
* - `useExisting`: Existing provider implementing `LoggerOptionsFactory`
|
|
3165
|
+
*
|
|
3166
|
+
* Options from the factory/class are merged with environment preset defaults.
|
|
3167
|
+
*
|
|
3168
|
+
* @param options - Async configuration options
|
|
3169
|
+
* @returns Dynamic module configuration
|
|
3170
|
+
*
|
|
3171
|
+
* @example
|
|
3172
|
+
* ```typescript
|
|
3173
|
+
* // Factory with ConfigService
|
|
3174
|
+
* LoggerModule.forRootAsync({
|
|
3175
|
+
* imports: [ConfigModule],
|
|
3176
|
+
* useFactory: (config: ConfigService) => ({
|
|
3177
|
+
* environment: config.get('NODE_ENV', 'development'),
|
|
3178
|
+
* provider: config.get('LOG_PROVIDER', 'winston'),
|
|
3179
|
+
* level: config.get('LOG_LEVEL'),
|
|
3180
|
+
* appName: config.get('APP_NAME'),
|
|
3181
|
+
* }),
|
|
3182
|
+
* inject: [ConfigService]
|
|
3183
|
+
* })
|
|
3184
|
+
*
|
|
3185
|
+
* // Factory class
|
|
3186
|
+
* @Injectable()
|
|
3187
|
+
* class LoggerConfigService implements LoggerOptionsFactory {
|
|
3188
|
+
* createLoggerOptions(): LoggerModuleOptions {
|
|
3189
|
+
* return {
|
|
3190
|
+
* environment: 'production',
|
|
3191
|
+
* appName: 'my-service'
|
|
3192
|
+
* };
|
|
3193
|
+
* }
|
|
3194
|
+
* }
|
|
3195
|
+
*
|
|
3196
|
+
* LoggerModule.forRootAsync({
|
|
3197
|
+
* useClass: LoggerConfigService
|
|
3198
|
+
* })
|
|
3199
|
+
* ```
|
|
3200
|
+
*/
|
|
3201
|
+
static forRootAsync(options) {
|
|
3202
|
+
const asyncProviders = this.createAsyncProviders(options);
|
|
3203
|
+
return {
|
|
3204
|
+
module: _LoggerModule,
|
|
3205
|
+
imports: options.imports || [],
|
|
3206
|
+
providers: [
|
|
3207
|
+
...asyncProviders,
|
|
3208
|
+
// Default logger provider
|
|
3209
|
+
{
|
|
3210
|
+
provide: import_common41.Logger,
|
|
3211
|
+
useFactory: /* @__PURE__ */ __name((opts) => {
|
|
3212
|
+
if (opts.provider === "default") {
|
|
3213
|
+
const logger = new import_common41.Logger();
|
|
3214
|
+
if (opts.level && typeof logger.setLogLevels === "function") {
|
|
3215
|
+
const levels = getLevelsUpTo(opts.level);
|
|
3216
|
+
logger.setLogLevels(levels);
|
|
3217
|
+
}
|
|
3218
|
+
return logger;
|
|
3219
|
+
}
|
|
3220
|
+
return null;
|
|
3221
|
+
}, "useFactory"),
|
|
3222
|
+
inject: [
|
|
3223
|
+
LOGGER_MODULE_OPTIONS
|
|
3224
|
+
]
|
|
3225
|
+
},
|
|
3226
|
+
// Unified logger service
|
|
3227
|
+
{
|
|
3228
|
+
provide: LoggerService2,
|
|
3229
|
+
useFactory: /* @__PURE__ */ __name((opts, defaultLogger) => {
|
|
3230
|
+
return new LoggerService2(opts, defaultLogger);
|
|
3231
|
+
}, "useFactory"),
|
|
3232
|
+
inject: [
|
|
3233
|
+
LOGGER_MODULE_OPTIONS,
|
|
3234
|
+
{
|
|
3235
|
+
token: import_common41.Logger,
|
|
3236
|
+
optional: true
|
|
3237
|
+
}
|
|
3238
|
+
]
|
|
3239
|
+
},
|
|
3240
|
+
// Correlation ID middleware
|
|
3241
|
+
{
|
|
3242
|
+
provide: CorrelationIdMiddleware,
|
|
3243
|
+
useFactory: /* @__PURE__ */ __name(() => {
|
|
3244
|
+
return new CorrelationIdMiddleware({
|
|
3245
|
+
includeInResponse: true,
|
|
3246
|
+
responseHeader: "x-correlation-id"
|
|
3247
|
+
});
|
|
3248
|
+
}, "useFactory")
|
|
3249
|
+
},
|
|
3250
|
+
// HTTP logger interceptor
|
|
3251
|
+
{
|
|
3252
|
+
provide: HttpLoggerInterceptor,
|
|
3253
|
+
useFactory: /* @__PURE__ */ __name((logger, opts) => {
|
|
3254
|
+
const httpLoggerOptions = opts.httpLogger ?? {
|
|
3255
|
+
enableRequestLog: opts.enableHttpLogger,
|
|
3256
|
+
enableResponseLog: opts.enableHttpLogger
|
|
3257
|
+
};
|
|
3258
|
+
return new HttpLoggerInterceptor(logger, httpLoggerOptions);
|
|
3259
|
+
}, "useFactory"),
|
|
3260
|
+
inject: [
|
|
3261
|
+
LoggerService2,
|
|
3262
|
+
LOGGER_MODULE_OPTIONS
|
|
3263
|
+
]
|
|
3264
|
+
}
|
|
3265
|
+
],
|
|
3266
|
+
exports: [
|
|
3267
|
+
LoggerService2,
|
|
3268
|
+
CorrelationIdMiddleware,
|
|
3269
|
+
HttpLoggerInterceptor,
|
|
3270
|
+
LOGGER_MODULE_OPTIONS
|
|
3271
|
+
]
|
|
3272
|
+
};
|
|
3273
|
+
}
|
|
3274
|
+
/**
|
|
3275
|
+
* Configures middleware for the module.
|
|
3276
|
+
* Middleware is registered globally in main.ts using Fastify hooks.
|
|
3277
|
+
*/
|
|
3278
|
+
configure(consumer) {
|
|
3279
|
+
}
|
|
3280
|
+
/**
|
|
3281
|
+
* Creates async providers for dynamic module configuration.
|
|
3282
|
+
*/
|
|
3283
|
+
static createAsyncProviders(options) {
|
|
3284
|
+
if (options.useFactory) {
|
|
3285
|
+
return [
|
|
3286
|
+
this.createAsyncOptionsProvider(options)
|
|
3287
|
+
];
|
|
3288
|
+
}
|
|
3289
|
+
const providers = [
|
|
3290
|
+
this.createAsyncOptionsProvider(options)
|
|
3291
|
+
];
|
|
3292
|
+
if (options.useClass) {
|
|
3293
|
+
providers.push({
|
|
3294
|
+
provide: options.useClass,
|
|
3295
|
+
useClass: options.useClass
|
|
3296
|
+
});
|
|
3297
|
+
}
|
|
3298
|
+
return providers;
|
|
3299
|
+
}
|
|
3300
|
+
/**
|
|
3301
|
+
* Creates the async options provider.
|
|
3302
|
+
*/
|
|
3303
|
+
static createAsyncOptionsProvider(options) {
|
|
3304
|
+
if (options.useFactory) {
|
|
3305
|
+
return {
|
|
3306
|
+
provide: LOGGER_MODULE_OPTIONS,
|
|
3307
|
+
useFactory: /* @__PURE__ */ __name(async (...args) => {
|
|
3308
|
+
const userOptions = await options.useFactory(...args);
|
|
3309
|
+
return mergeWithDefaults(userOptions);
|
|
3310
|
+
}, "useFactory"),
|
|
3311
|
+
inject: options.inject || []
|
|
3312
|
+
};
|
|
3313
|
+
}
|
|
3314
|
+
if (options.useClass) {
|
|
3315
|
+
return {
|
|
3316
|
+
provide: LOGGER_MODULE_OPTIONS,
|
|
3317
|
+
useFactory: /* @__PURE__ */ __name(async (optionsFactory) => {
|
|
3318
|
+
const userOptions = await optionsFactory.createLoggerOptions();
|
|
3319
|
+
return mergeWithDefaults(userOptions);
|
|
3320
|
+
}, "useFactory"),
|
|
3321
|
+
inject: [
|
|
3322
|
+
options.useClass
|
|
3323
|
+
]
|
|
3324
|
+
};
|
|
3325
|
+
}
|
|
3326
|
+
if (options.useExisting) {
|
|
3327
|
+
return {
|
|
3328
|
+
provide: LOGGER_MODULE_OPTIONS,
|
|
3329
|
+
useFactory: /* @__PURE__ */ __name(async (optionsFactory) => {
|
|
3330
|
+
const userOptions = await optionsFactory.createLoggerOptions();
|
|
3331
|
+
return mergeWithDefaults(userOptions);
|
|
3332
|
+
}, "useFactory"),
|
|
3333
|
+
inject: [
|
|
3334
|
+
options.useExisting
|
|
3335
|
+
]
|
|
3336
|
+
};
|
|
3337
|
+
}
|
|
3338
|
+
throw new Error("LoggerModule.forRootAsync() requires one of: useFactory, useClass, or useExisting");
|
|
3339
|
+
}
|
|
3340
|
+
};
|
|
3341
|
+
LoggerModule = _ts_decorate17([
|
|
3342
|
+
(0, import_common41.Global)(),
|
|
3343
|
+
(0, import_common41.Module)({})
|
|
3344
|
+
], LoggerModule);
|
|
2415
3345
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2416
3346
|
0 && (module.exports = {
|
|
2417
3347
|
AuthConfigModule,
|
|
@@ -2419,13 +3349,19 @@ var BadGatewayException = class extends BaseFieldException {
|
|
|
2419
3349
|
BadRequestException,
|
|
2420
3350
|
BaseFieldException,
|
|
2421
3351
|
ConflictException,
|
|
3352
|
+
CorrelationIdMiddleware,
|
|
2422
3353
|
CsrfGuard,
|
|
3354
|
+
DEFAULT_CORRELATION_HEADER,
|
|
2423
3355
|
DatabaseModule,
|
|
2424
3356
|
ForbiddenException,
|
|
2425
3357
|
GoneException,
|
|
2426
3358
|
HttpExceptionFilter,
|
|
3359
|
+
HttpLoggerInterceptor,
|
|
2427
3360
|
HttpModule,
|
|
2428
3361
|
InternalServerErrorException,
|
|
3362
|
+
LOGGER_MODULE_OPTIONS,
|
|
3363
|
+
LoggerModule,
|
|
3364
|
+
LoggerService,
|
|
2429
3365
|
MethodNotAllowedException,
|
|
2430
3366
|
NotAcceptableException,
|
|
2431
3367
|
NotFoundException,
|
|
@@ -2447,6 +3383,12 @@ var BadGatewayException = class extends BaseFieldException {
|
|
|
2447
3383
|
UnsupportedMediaTypeException,
|
|
2448
3384
|
ValidationException,
|
|
2449
3385
|
VrittiAuthGuard,
|
|
2450
|
-
|
|
3386
|
+
addCorrelationIdToResponse,
|
|
3387
|
+
correlationStorage,
|
|
3388
|
+
generateCorrelationId,
|
|
3389
|
+
getCorrelationContext,
|
|
3390
|
+
getHttpStatusTitle,
|
|
3391
|
+
runWithCorrelationContext,
|
|
3392
|
+
updateCorrelationContext
|
|
2451
3393
|
});
|
|
2452
3394
|
//# sourceMappingURL=index.cjs.map
|