@vritti/api-sdk 0.0.3 → 0.0.6
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 +113 -0
- package/dist/index.cjs +802 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +579 -7
- package/dist/index.d.ts +579 -7
- package/dist/index.js +788 -8
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -334,16 +334,15 @@ var PrimaryDatabaseService = class _PrimaryDatabaseService {
|
|
|
334
334
|
this.logger.log(`Cleared ${size} cached tenant configs`);
|
|
335
335
|
}
|
|
336
336
|
/**
|
|
337
|
-
* Get
|
|
338
|
-
*
|
|
339
|
-
* This is useful for platform admin operations (creating tenants, billing, etc.)
|
|
337
|
+
* Get the Prisma client for the primary database.
|
|
338
|
+
* This is a synchronous property that returns the initialized Prisma client.
|
|
340
339
|
*
|
|
341
340
|
* @returns Primary database client instance
|
|
342
341
|
* @throws Error if primary database client is not initialized
|
|
343
342
|
*/
|
|
344
|
-
|
|
343
|
+
get prismaClient() {
|
|
345
344
|
if (!this.primaryDbClient) {
|
|
346
|
-
throw new Error("Primary database client not initialized
|
|
345
|
+
throw new Error("Primary database client not initialized");
|
|
347
346
|
}
|
|
348
347
|
return this.primaryDbClient;
|
|
349
348
|
}
|
|
@@ -822,6 +821,7 @@ MessageTenantContextInterceptor = _ts_decorate7([
|
|
|
822
821
|
|
|
823
822
|
// src/database/interceptors/tenant-context.interceptor.ts
|
|
824
823
|
import { Injectable as Injectable6, Logger as Logger4, Scope as Scope5, UnauthorizedException as UnauthorizedException3 } from "@nestjs/common";
|
|
824
|
+
import { Reflector as Reflector2 } from "@nestjs/core";
|
|
825
825
|
function _ts_decorate8(decorators, target, key, desc) {
|
|
826
826
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
827
827
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -837,11 +837,13 @@ var TenantContextInterceptor = class _TenantContextInterceptor {
|
|
|
837
837
|
static {
|
|
838
838
|
__name(this, "TenantContextInterceptor");
|
|
839
839
|
}
|
|
840
|
+
reflector;
|
|
840
841
|
tenantContext;
|
|
841
842
|
primaryDatabase;
|
|
842
843
|
requestService;
|
|
843
844
|
logger = new Logger4(_TenantContextInterceptor.name);
|
|
844
|
-
constructor(tenantContext, primaryDatabase, requestService) {
|
|
845
|
+
constructor(reflector, tenantContext, primaryDatabase, requestService) {
|
|
846
|
+
this.reflector = reflector;
|
|
845
847
|
this.tenantContext = tenantContext;
|
|
846
848
|
this.primaryDatabase = primaryDatabase;
|
|
847
849
|
this.requestService = requestService;
|
|
@@ -849,8 +851,16 @@ var TenantContextInterceptor = class _TenantContextInterceptor {
|
|
|
849
851
|
async intercept(context, next) {
|
|
850
852
|
const request = context.switchToHttp().getRequest();
|
|
851
853
|
this.logger.debug(`Processing request: ${request.method} ${request.url}`);
|
|
854
|
+
const isPublic = this.reflector.getAllAndOverride("isPublic", [
|
|
855
|
+
context.getHandler(),
|
|
856
|
+
context.getClass()
|
|
857
|
+
]);
|
|
852
858
|
try {
|
|
853
859
|
const tenantIdentifier = this.requestService.getTenantIdentifier();
|
|
860
|
+
if (isPublic && !tenantIdentifier) {
|
|
861
|
+
this.logger.debug("Public endpoint without tenant identifier, skipping tenant context setup");
|
|
862
|
+
return next.handle();
|
|
863
|
+
}
|
|
854
864
|
if (!tenantIdentifier) {
|
|
855
865
|
throw new UnauthorizedException3("Tenant identifier not found in request");
|
|
856
866
|
}
|
|
@@ -885,6 +895,7 @@ TenantContextInterceptor = _ts_decorate8([
|
|
|
885
895
|
}),
|
|
886
896
|
_ts_metadata5("design:type", Function),
|
|
887
897
|
_ts_metadata5("design:paramtypes", [
|
|
898
|
+
typeof Reflector2 === "undefined" ? Object : Reflector2,
|
|
888
899
|
typeof TenantContextService === "undefined" ? Object : TenantContextService,
|
|
889
900
|
typeof PrimaryDatabaseService === "undefined" ? Object : PrimaryDatabaseService,
|
|
890
901
|
typeof RequestService === "undefined" ? Object : RequestService
|
|
@@ -929,6 +940,17 @@ var TenantDatabaseService = class _TenantDatabaseService {
|
|
|
929
940
|
this.startConnectionCleaner();
|
|
930
941
|
}
|
|
931
942
|
/**
|
|
943
|
+
* Get the Prisma client for the current tenant's database.
|
|
944
|
+
* This returns the tenant-scoped database client.
|
|
945
|
+
*
|
|
946
|
+
* @returns Tenant-scoped database client instance
|
|
947
|
+
* @throws UnauthorizedException if tenant context not set
|
|
948
|
+
* @throws InternalServerErrorException if connection fails
|
|
949
|
+
*/
|
|
950
|
+
get prismaClient() {
|
|
951
|
+
return this.getDbClient();
|
|
952
|
+
}
|
|
953
|
+
/**
|
|
932
954
|
* Get tenant-scoped database client for the current request/message
|
|
933
955
|
*
|
|
934
956
|
* This method:
|
|
@@ -1122,7 +1144,7 @@ var DatabaseModule = class _DatabaseModule {
|
|
|
1122
1144
|
* })
|
|
1123
1145
|
*/
|
|
1124
1146
|
static forServer(options) {
|
|
1125
|
-
return this.createDynamicModule(options, "
|
|
1147
|
+
return this.createDynamicModule(options, "server");
|
|
1126
1148
|
}
|
|
1127
1149
|
/**
|
|
1128
1150
|
* Configure DatabaseModule for Microservice/Messaging mode (RabbitMQ workers)
|
|
@@ -1165,7 +1187,7 @@ var DatabaseModule = class _DatabaseModule {
|
|
|
1165
1187
|
PrimaryDatabaseService,
|
|
1166
1188
|
TenantDatabaseService
|
|
1167
1189
|
];
|
|
1168
|
-
if (mode === "
|
|
1190
|
+
if (mode === "server") {
|
|
1169
1191
|
providers.push({
|
|
1170
1192
|
provide: APP_INTERCEPTOR,
|
|
1171
1193
|
useClass: TenantContextInterceptor
|
|
@@ -1196,6 +1218,545 @@ DatabaseModule = _ts_decorate10([
|
|
|
1196
1218
|
Module3({})
|
|
1197
1219
|
], DatabaseModule);
|
|
1198
1220
|
|
|
1221
|
+
// src/database/repositories/primary-base.repository.ts
|
|
1222
|
+
import { Logger as Logger6 } from "@nestjs/common";
|
|
1223
|
+
var PrimaryBaseRepository = class {
|
|
1224
|
+
static {
|
|
1225
|
+
__name(this, "PrimaryBaseRepository");
|
|
1226
|
+
}
|
|
1227
|
+
database;
|
|
1228
|
+
logger;
|
|
1229
|
+
modelGetter;
|
|
1230
|
+
/**
|
|
1231
|
+
* Lazy getter for Prisma client.
|
|
1232
|
+
* Accesses the client from the database service only when needed,
|
|
1233
|
+
* avoiding initialization timing issues with NestJS lifecycle.
|
|
1234
|
+
*/
|
|
1235
|
+
get prisma() {
|
|
1236
|
+
return this.database.prismaClient;
|
|
1237
|
+
}
|
|
1238
|
+
/**
|
|
1239
|
+
* Lazy getter for the Prisma model delegate.
|
|
1240
|
+
* Returns the specific model (e.g., prisma.user, prisma.tenant) for this repository.
|
|
1241
|
+
*/
|
|
1242
|
+
get model() {
|
|
1243
|
+
return this.modelGetter(this.prisma);
|
|
1244
|
+
}
|
|
1245
|
+
/**
|
|
1246
|
+
* Create a new repository instance
|
|
1247
|
+
*
|
|
1248
|
+
* @param database - The primary database service
|
|
1249
|
+
* @param getModel - Function that returns the Prisma model delegate from the client
|
|
1250
|
+
*
|
|
1251
|
+
* @example
|
|
1252
|
+
* ```typescript
|
|
1253
|
+
* // Standard usage with full parameter name
|
|
1254
|
+
* constructor(database: PrimaryDatabaseService) {
|
|
1255
|
+
* super(database, (prisma) => prisma.user);
|
|
1256
|
+
* }
|
|
1257
|
+
*
|
|
1258
|
+
* // Short syntax
|
|
1259
|
+
* constructor(database: PrimaryDatabaseService) {
|
|
1260
|
+
* super(database, (p) => p.user);
|
|
1261
|
+
* }
|
|
1262
|
+
*
|
|
1263
|
+
* // Complex model names
|
|
1264
|
+
* constructor(database: PrimaryDatabaseService) {
|
|
1265
|
+
* super(database, (p) => p.emailVerification);
|
|
1266
|
+
* }
|
|
1267
|
+
* ```
|
|
1268
|
+
*/
|
|
1269
|
+
constructor(database, getModel) {
|
|
1270
|
+
this.database = database;
|
|
1271
|
+
this.logger = new Logger6(this.constructor.name);
|
|
1272
|
+
this.modelGetter = getModel;
|
|
1273
|
+
this.logger.debug(`Initialized ${this.constructor.name}`);
|
|
1274
|
+
}
|
|
1275
|
+
/**
|
|
1276
|
+
* Create a new record
|
|
1277
|
+
*
|
|
1278
|
+
* @param data - The data to create the record with
|
|
1279
|
+
* @returns Promise resolving to the created record
|
|
1280
|
+
*
|
|
1281
|
+
* @example
|
|
1282
|
+
* ```typescript
|
|
1283
|
+
* const user = await userRepository.create({
|
|
1284
|
+
* email: 'user@example.com',
|
|
1285
|
+
* name: 'John Doe'
|
|
1286
|
+
* });
|
|
1287
|
+
* ```
|
|
1288
|
+
*/
|
|
1289
|
+
async create(data) {
|
|
1290
|
+
this.logger.log("Creating record");
|
|
1291
|
+
return await this.model.create({
|
|
1292
|
+
data
|
|
1293
|
+
});
|
|
1294
|
+
}
|
|
1295
|
+
/**
|
|
1296
|
+
* Find a single record by ID
|
|
1297
|
+
*
|
|
1298
|
+
* @param id - The record ID
|
|
1299
|
+
* @returns Promise resolving to the record or null if not found
|
|
1300
|
+
*
|
|
1301
|
+
* @example
|
|
1302
|
+
* ```typescript
|
|
1303
|
+
* const user = await userRepository.findById('user-id-123');
|
|
1304
|
+
* ```
|
|
1305
|
+
*/
|
|
1306
|
+
async findById(id) {
|
|
1307
|
+
this.logger.debug(`Finding record by ID: ${id}`);
|
|
1308
|
+
return await this.model.findUnique({
|
|
1309
|
+
where: {
|
|
1310
|
+
id
|
|
1311
|
+
}
|
|
1312
|
+
});
|
|
1313
|
+
}
|
|
1314
|
+
/**
|
|
1315
|
+
* Find a single record with custom where clause
|
|
1316
|
+
*
|
|
1317
|
+
* @param where - The where clause or findUnique args
|
|
1318
|
+
* @returns Promise resolving to the record or null if not found
|
|
1319
|
+
*
|
|
1320
|
+
* @example
|
|
1321
|
+
* ```typescript
|
|
1322
|
+
* // Simple where clause
|
|
1323
|
+
* const user = await userRepository.findOne({ email: 'user@example.com' });
|
|
1324
|
+
*
|
|
1325
|
+
* // With include
|
|
1326
|
+
* const user = await userRepository.findOne({
|
|
1327
|
+
* where: { email: 'user@example.com' },
|
|
1328
|
+
* include: { posts: true }
|
|
1329
|
+
* });
|
|
1330
|
+
* ```
|
|
1331
|
+
*/
|
|
1332
|
+
async findOne(where) {
|
|
1333
|
+
this.logger.debug("Finding record with custom query");
|
|
1334
|
+
return await this.model.findUnique(typeof where === "object" && "where" in where ? where : {
|
|
1335
|
+
where
|
|
1336
|
+
});
|
|
1337
|
+
}
|
|
1338
|
+
/**
|
|
1339
|
+
* Find multiple records
|
|
1340
|
+
*
|
|
1341
|
+
* @param args - Prisma findMany arguments (where, orderBy, take, skip, etc.)
|
|
1342
|
+
* @returns Promise resolving to an array of records
|
|
1343
|
+
*
|
|
1344
|
+
* @example
|
|
1345
|
+
* ```typescript
|
|
1346
|
+
* // Find all users
|
|
1347
|
+
* const users = await userRepository.findMany();
|
|
1348
|
+
*
|
|
1349
|
+
* // Find with filtering and pagination
|
|
1350
|
+
* const users = await userRepository.findMany({
|
|
1351
|
+
* where: { status: 'ACTIVE' },
|
|
1352
|
+
* orderBy: { createdAt: 'desc' },
|
|
1353
|
+
* take: 10,
|
|
1354
|
+
* skip: 0
|
|
1355
|
+
* });
|
|
1356
|
+
* ```
|
|
1357
|
+
*/
|
|
1358
|
+
async findMany(args) {
|
|
1359
|
+
this.logger.debug("Finding multiple records");
|
|
1360
|
+
return await this.model.findMany(args);
|
|
1361
|
+
}
|
|
1362
|
+
/**
|
|
1363
|
+
* Update a record by ID
|
|
1364
|
+
*
|
|
1365
|
+
* @param id - The record ID
|
|
1366
|
+
* @param data - The data to update
|
|
1367
|
+
* @returns Promise resolving to the updated record
|
|
1368
|
+
*
|
|
1369
|
+
* @example
|
|
1370
|
+
* ```typescript
|
|
1371
|
+
* const user = await userRepository.update('user-id-123', {
|
|
1372
|
+
* name: 'Jane Doe'
|
|
1373
|
+
* });
|
|
1374
|
+
* ```
|
|
1375
|
+
*/
|
|
1376
|
+
async update(id, data) {
|
|
1377
|
+
this.logger.log(`Updating record with ID: ${id}`);
|
|
1378
|
+
return await this.model.update({
|
|
1379
|
+
where: {
|
|
1380
|
+
id
|
|
1381
|
+
},
|
|
1382
|
+
data
|
|
1383
|
+
});
|
|
1384
|
+
}
|
|
1385
|
+
/**
|
|
1386
|
+
* Update multiple records
|
|
1387
|
+
*
|
|
1388
|
+
* @param where - The where clause to match records
|
|
1389
|
+
* @param data - The data to update
|
|
1390
|
+
* @returns Promise resolving to the count of updated records
|
|
1391
|
+
*
|
|
1392
|
+
* @example
|
|
1393
|
+
* ```typescript
|
|
1394
|
+
* const result = await userRepository.updateMany(
|
|
1395
|
+
* { status: 'PENDING' },
|
|
1396
|
+
* { status: 'ACTIVE' }
|
|
1397
|
+
* );
|
|
1398
|
+
* console.log(`Updated ${result.count} users`);
|
|
1399
|
+
* ```
|
|
1400
|
+
*/
|
|
1401
|
+
async updateMany(where, data) {
|
|
1402
|
+
this.logger.log("Updating multiple records");
|
|
1403
|
+
return await this.model.updateMany({
|
|
1404
|
+
where,
|
|
1405
|
+
data
|
|
1406
|
+
});
|
|
1407
|
+
}
|
|
1408
|
+
/**
|
|
1409
|
+
* Delete a record by ID
|
|
1410
|
+
*
|
|
1411
|
+
* @param id - The record ID
|
|
1412
|
+
* @returns Promise resolving to the deleted record
|
|
1413
|
+
*
|
|
1414
|
+
* @example
|
|
1415
|
+
* ```typescript
|
|
1416
|
+
* const user = await userRepository.delete('user-id-123');
|
|
1417
|
+
* ```
|
|
1418
|
+
*/
|
|
1419
|
+
async delete(id) {
|
|
1420
|
+
this.logger.log(`Deleting record with ID: ${id}`);
|
|
1421
|
+
return await this.model.delete({
|
|
1422
|
+
where: {
|
|
1423
|
+
id
|
|
1424
|
+
}
|
|
1425
|
+
});
|
|
1426
|
+
}
|
|
1427
|
+
/**
|
|
1428
|
+
* Delete multiple records
|
|
1429
|
+
*
|
|
1430
|
+
* @param where - The where clause to match records
|
|
1431
|
+
* @returns Promise resolving to the count of deleted records
|
|
1432
|
+
*
|
|
1433
|
+
* @example
|
|
1434
|
+
* ```typescript
|
|
1435
|
+
* const result = await userRepository.deleteMany({
|
|
1436
|
+
* status: 'INACTIVE',
|
|
1437
|
+
* createdAt: { lt: new Date('2020-01-01') }
|
|
1438
|
+
* });
|
|
1439
|
+
* console.log(`Deleted ${result.count} users`);
|
|
1440
|
+
* ```
|
|
1441
|
+
*/
|
|
1442
|
+
async deleteMany(where) {
|
|
1443
|
+
this.logger.log("Deleting multiple records");
|
|
1444
|
+
return await this.model.deleteMany({
|
|
1445
|
+
where
|
|
1446
|
+
});
|
|
1447
|
+
}
|
|
1448
|
+
/**
|
|
1449
|
+
* Count records
|
|
1450
|
+
*
|
|
1451
|
+
* @param where - Optional where clause to filter records
|
|
1452
|
+
* @returns Promise resolving to the count of records
|
|
1453
|
+
*
|
|
1454
|
+
* @example
|
|
1455
|
+
* ```typescript
|
|
1456
|
+
* // Count all users
|
|
1457
|
+
* const total = await userRepository.count();
|
|
1458
|
+
*
|
|
1459
|
+
* // Count active users
|
|
1460
|
+
* const activeCount = await userRepository.count({ status: 'ACTIVE' });
|
|
1461
|
+
* ```
|
|
1462
|
+
*/
|
|
1463
|
+
async count(where) {
|
|
1464
|
+
this.logger.debug("Counting records");
|
|
1465
|
+
return await this.model.count({
|
|
1466
|
+
where
|
|
1467
|
+
});
|
|
1468
|
+
}
|
|
1469
|
+
/**
|
|
1470
|
+
* Check if a record exists
|
|
1471
|
+
*
|
|
1472
|
+
* @param where - The where clause to match records
|
|
1473
|
+
* @returns Promise resolving to true if at least one record exists, false otherwise
|
|
1474
|
+
*
|
|
1475
|
+
* @example
|
|
1476
|
+
* ```typescript
|
|
1477
|
+
* const emailExists = await userRepository.exists({
|
|
1478
|
+
* email: 'user@example.com'
|
|
1479
|
+
* });
|
|
1480
|
+
* ```
|
|
1481
|
+
*/
|
|
1482
|
+
async exists(where) {
|
|
1483
|
+
const count = await this.model.count({
|
|
1484
|
+
where
|
|
1485
|
+
});
|
|
1486
|
+
return count > 0;
|
|
1487
|
+
}
|
|
1488
|
+
};
|
|
1489
|
+
|
|
1490
|
+
// src/database/repositories/tenant-base.repository.ts
|
|
1491
|
+
import { Logger as Logger7 } from "@nestjs/common";
|
|
1492
|
+
var TenantBaseRepository = class {
|
|
1493
|
+
static {
|
|
1494
|
+
__name(this, "TenantBaseRepository");
|
|
1495
|
+
}
|
|
1496
|
+
database;
|
|
1497
|
+
logger;
|
|
1498
|
+
modelGetter;
|
|
1499
|
+
/**
|
|
1500
|
+
* Lazy getter for Prisma client.
|
|
1501
|
+
* Accesses the client from the database service only when needed,
|
|
1502
|
+
* avoiding initialization timing issues with NestJS lifecycle.
|
|
1503
|
+
*/
|
|
1504
|
+
get prisma() {
|
|
1505
|
+
return this.database.prismaClient;
|
|
1506
|
+
}
|
|
1507
|
+
/**
|
|
1508
|
+
* Lazy getter for the Prisma model delegate.
|
|
1509
|
+
* Returns the specific model (e.g., prisma.product, prisma.order) for this repository.
|
|
1510
|
+
*/
|
|
1511
|
+
get model() {
|
|
1512
|
+
return this.modelGetter(this.prisma);
|
|
1513
|
+
}
|
|
1514
|
+
/**
|
|
1515
|
+
* Create a new repository instance
|
|
1516
|
+
*
|
|
1517
|
+
* @param database - The tenant database service
|
|
1518
|
+
* @param getModel - Function that returns the Prisma model delegate from the client
|
|
1519
|
+
*
|
|
1520
|
+
* @example
|
|
1521
|
+
* ```typescript
|
|
1522
|
+
* // Standard usage with full parameter name
|
|
1523
|
+
* constructor(database: TenantDatabaseService) {
|
|
1524
|
+
* super(database, (prisma) => prisma.product);
|
|
1525
|
+
* }
|
|
1526
|
+
*
|
|
1527
|
+
* // Short syntax
|
|
1528
|
+
* constructor(database: TenantDatabaseService) {
|
|
1529
|
+
* super(database, (p) => p.product);
|
|
1530
|
+
* }
|
|
1531
|
+
*
|
|
1532
|
+
* // Complex model names
|
|
1533
|
+
* constructor(database: TenantDatabaseService) {
|
|
1534
|
+
* super(database, (p) => p.inventoryItem);
|
|
1535
|
+
* }
|
|
1536
|
+
* ```
|
|
1537
|
+
*/
|
|
1538
|
+
constructor(database, getModel) {
|
|
1539
|
+
this.database = database;
|
|
1540
|
+
this.logger = new Logger7(this.constructor.name);
|
|
1541
|
+
this.modelGetter = getModel;
|
|
1542
|
+
this.logger.debug(`Initialized ${this.constructor.name}`);
|
|
1543
|
+
}
|
|
1544
|
+
/**
|
|
1545
|
+
* Create a new record
|
|
1546
|
+
*
|
|
1547
|
+
* @param data - The data to create the record with
|
|
1548
|
+
* @returns Promise resolving to the created record
|
|
1549
|
+
*
|
|
1550
|
+
* @example
|
|
1551
|
+
* ```typescript
|
|
1552
|
+
* const product = await productRepository.create({
|
|
1553
|
+
* name: 'Widget',
|
|
1554
|
+
* sku: 'WDG-001',
|
|
1555
|
+
* price: 9.99
|
|
1556
|
+
* });
|
|
1557
|
+
* ```
|
|
1558
|
+
*/
|
|
1559
|
+
async create(data) {
|
|
1560
|
+
this.logger.log("Creating record");
|
|
1561
|
+
return await this.model.create({
|
|
1562
|
+
data
|
|
1563
|
+
});
|
|
1564
|
+
}
|
|
1565
|
+
/**
|
|
1566
|
+
* Find a single record by ID
|
|
1567
|
+
*
|
|
1568
|
+
* @param id - The record ID
|
|
1569
|
+
* @returns Promise resolving to the record or null if not found
|
|
1570
|
+
*
|
|
1571
|
+
* @example
|
|
1572
|
+
* ```typescript
|
|
1573
|
+
* const product = await productRepository.findById('product-id-123');
|
|
1574
|
+
* ```
|
|
1575
|
+
*/
|
|
1576
|
+
async findById(id) {
|
|
1577
|
+
this.logger.debug(`Finding record by ID: ${id}`);
|
|
1578
|
+
return await this.model.findUnique({
|
|
1579
|
+
where: {
|
|
1580
|
+
id
|
|
1581
|
+
}
|
|
1582
|
+
});
|
|
1583
|
+
}
|
|
1584
|
+
/**
|
|
1585
|
+
* Find a single record with custom where clause
|
|
1586
|
+
*
|
|
1587
|
+
* @param where - The where clause or findUnique args
|
|
1588
|
+
* @returns Promise resolving to the record or null if not found
|
|
1589
|
+
*
|
|
1590
|
+
* @example
|
|
1591
|
+
* ```typescript
|
|
1592
|
+
* // Simple where clause
|
|
1593
|
+
* const product = await productRepository.findOne({ sku: 'WDG-001' });
|
|
1594
|
+
*
|
|
1595
|
+
* // With include
|
|
1596
|
+
* const product = await productRepository.findOne({
|
|
1597
|
+
* where: { sku: 'WDG-001' },
|
|
1598
|
+
* include: { category: true }
|
|
1599
|
+
* });
|
|
1600
|
+
* ```
|
|
1601
|
+
*/
|
|
1602
|
+
async findOne(where) {
|
|
1603
|
+
this.logger.debug("Finding record with custom query");
|
|
1604
|
+
return await this.model.findUnique(typeof where === "object" && "where" in where ? where : {
|
|
1605
|
+
where
|
|
1606
|
+
});
|
|
1607
|
+
}
|
|
1608
|
+
/**
|
|
1609
|
+
* Find multiple records
|
|
1610
|
+
*
|
|
1611
|
+
* @param args - Prisma findMany arguments (where, orderBy, take, skip, etc.)
|
|
1612
|
+
* @returns Promise resolving to an array of records
|
|
1613
|
+
*
|
|
1614
|
+
* @example
|
|
1615
|
+
* ```typescript
|
|
1616
|
+
* // Find all products
|
|
1617
|
+
* const products = await productRepository.findMany();
|
|
1618
|
+
*
|
|
1619
|
+
* // Find with filtering and pagination
|
|
1620
|
+
* const products = await productRepository.findMany({
|
|
1621
|
+
* where: { status: 'ACTIVE' },
|
|
1622
|
+
* orderBy: { createdAt: 'desc' },
|
|
1623
|
+
* take: 10,
|
|
1624
|
+
* skip: 0
|
|
1625
|
+
* });
|
|
1626
|
+
* ```
|
|
1627
|
+
*/
|
|
1628
|
+
async findMany(args) {
|
|
1629
|
+
this.logger.debug("Finding multiple records");
|
|
1630
|
+
return await this.model.findMany(args);
|
|
1631
|
+
}
|
|
1632
|
+
/**
|
|
1633
|
+
* Update a record by ID
|
|
1634
|
+
*
|
|
1635
|
+
* @param id - The record ID
|
|
1636
|
+
* @param data - The data to update
|
|
1637
|
+
* @returns Promise resolving to the updated record
|
|
1638
|
+
*
|
|
1639
|
+
* @example
|
|
1640
|
+
* ```typescript
|
|
1641
|
+
* const product = await productRepository.update('product-id-123', {
|
|
1642
|
+
* price: 12.99
|
|
1643
|
+
* });
|
|
1644
|
+
* ```
|
|
1645
|
+
*/
|
|
1646
|
+
async update(id, data) {
|
|
1647
|
+
this.logger.log(`Updating record with ID: ${id}`);
|
|
1648
|
+
return await this.model.update({
|
|
1649
|
+
where: {
|
|
1650
|
+
id
|
|
1651
|
+
},
|
|
1652
|
+
data
|
|
1653
|
+
});
|
|
1654
|
+
}
|
|
1655
|
+
/**
|
|
1656
|
+
* Update multiple records
|
|
1657
|
+
*
|
|
1658
|
+
* @param where - The where clause to match records
|
|
1659
|
+
* @param data - The data to update
|
|
1660
|
+
* @returns Promise resolving to the count of updated records
|
|
1661
|
+
*
|
|
1662
|
+
* @example
|
|
1663
|
+
* ```typescript
|
|
1664
|
+
* const result = await productRepository.updateMany(
|
|
1665
|
+
* { status: 'PENDING' },
|
|
1666
|
+
* { status: 'ACTIVE' }
|
|
1667
|
+
* );
|
|
1668
|
+
* console.log(`Updated ${result.count} products`);
|
|
1669
|
+
* ```
|
|
1670
|
+
*/
|
|
1671
|
+
async updateMany(where, data) {
|
|
1672
|
+
this.logger.log("Updating multiple records");
|
|
1673
|
+
return await this.model.updateMany({
|
|
1674
|
+
where,
|
|
1675
|
+
data
|
|
1676
|
+
});
|
|
1677
|
+
}
|
|
1678
|
+
/**
|
|
1679
|
+
* Delete a record by ID
|
|
1680
|
+
*
|
|
1681
|
+
* @param id - The record ID
|
|
1682
|
+
* @returns Promise resolving to the deleted record
|
|
1683
|
+
*
|
|
1684
|
+
* @example
|
|
1685
|
+
* ```typescript
|
|
1686
|
+
* const product = await productRepository.delete('product-id-123');
|
|
1687
|
+
* ```
|
|
1688
|
+
*/
|
|
1689
|
+
async delete(id) {
|
|
1690
|
+
this.logger.log(`Deleting record with ID: ${id}`);
|
|
1691
|
+
return await this.model.delete({
|
|
1692
|
+
where: {
|
|
1693
|
+
id
|
|
1694
|
+
}
|
|
1695
|
+
});
|
|
1696
|
+
}
|
|
1697
|
+
/**
|
|
1698
|
+
* Delete multiple records
|
|
1699
|
+
*
|
|
1700
|
+
* @param where - The where clause to match records
|
|
1701
|
+
* @returns Promise resolving to the count of deleted records
|
|
1702
|
+
*
|
|
1703
|
+
* @example
|
|
1704
|
+
* ```typescript
|
|
1705
|
+
* const result = await productRepository.deleteMany({
|
|
1706
|
+
* status: 'INACTIVE',
|
|
1707
|
+
* createdAt: { lt: new Date('2020-01-01') }
|
|
1708
|
+
* });
|
|
1709
|
+
* console.log(`Deleted ${result.count} products`);
|
|
1710
|
+
* ```
|
|
1711
|
+
*/
|
|
1712
|
+
async deleteMany(where) {
|
|
1713
|
+
this.logger.log("Deleting multiple records");
|
|
1714
|
+
return await this.model.deleteMany({
|
|
1715
|
+
where
|
|
1716
|
+
});
|
|
1717
|
+
}
|
|
1718
|
+
/**
|
|
1719
|
+
* Count records
|
|
1720
|
+
*
|
|
1721
|
+
* @param where - Optional where clause to filter records
|
|
1722
|
+
* @returns Promise resolving to the count of records
|
|
1723
|
+
*
|
|
1724
|
+
* @example
|
|
1725
|
+
* ```typescript
|
|
1726
|
+
* // Count all products
|
|
1727
|
+
* const total = await productRepository.count();
|
|
1728
|
+
*
|
|
1729
|
+
* // Count active products
|
|
1730
|
+
* const activeCount = await productRepository.count({ status: 'ACTIVE' });
|
|
1731
|
+
* ```
|
|
1732
|
+
*/
|
|
1733
|
+
async count(where) {
|
|
1734
|
+
this.logger.debug("Counting records");
|
|
1735
|
+
return await this.model.count({
|
|
1736
|
+
where
|
|
1737
|
+
});
|
|
1738
|
+
}
|
|
1739
|
+
/**
|
|
1740
|
+
* Check if a record exists
|
|
1741
|
+
*
|
|
1742
|
+
* @param where - The where clause to match records
|
|
1743
|
+
* @returns Promise resolving to true if at least one record exists, false otherwise
|
|
1744
|
+
*
|
|
1745
|
+
* @example
|
|
1746
|
+
* ```typescript
|
|
1747
|
+
* const skuExists = await productRepository.exists({
|
|
1748
|
+
* sku: 'WDG-001'
|
|
1749
|
+
* });
|
|
1750
|
+
* ```
|
|
1751
|
+
*/
|
|
1752
|
+
async exists(where) {
|
|
1753
|
+
const count = await this.model.count({
|
|
1754
|
+
where
|
|
1755
|
+
});
|
|
1756
|
+
return count > 0;
|
|
1757
|
+
}
|
|
1758
|
+
};
|
|
1759
|
+
|
|
1199
1760
|
// src/database/decorators/tenant.decorator.ts
|
|
1200
1761
|
import { createParamDecorator } from "@nestjs/common";
|
|
1201
1762
|
var Tenant = createParamDecorator((data, ctx) => {
|
|
@@ -1214,13 +1775,232 @@ var Onboarding = /* @__PURE__ */ __name(() => SetMetadata("isOnboarding", true),
|
|
|
1214
1775
|
// src/auth/decorators/public.decorator.ts
|
|
1215
1776
|
import { SetMetadata as SetMetadata2 } from "@nestjs/common";
|
|
1216
1777
|
var Public = /* @__PURE__ */ __name(() => SetMetadata2("isPublic", true), "Public");
|
|
1778
|
+
|
|
1779
|
+
// src/http/http.module.ts
|
|
1780
|
+
import { Module as Module4 } from "@nestjs/common";
|
|
1781
|
+
|
|
1782
|
+
// src/http/guards/csrf.guard.ts
|
|
1783
|
+
import { ForbiddenException, Injectable as Injectable8, Logger as Logger8 } from "@nestjs/common";
|
|
1784
|
+
import { Reflector as Reflector3 } from "@nestjs/core";
|
|
1785
|
+
function _ts_decorate11(decorators, target, key, desc) {
|
|
1786
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1787
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1788
|
+
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;
|
|
1789
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1790
|
+
}
|
|
1791
|
+
__name(_ts_decorate11, "_ts_decorate");
|
|
1792
|
+
function _ts_metadata7(k, v) {
|
|
1793
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
1794
|
+
}
|
|
1795
|
+
__name(_ts_metadata7, "_ts_metadata");
|
|
1796
|
+
var CsrfGuard = class _CsrfGuard {
|
|
1797
|
+
static {
|
|
1798
|
+
__name(this, "CsrfGuard");
|
|
1799
|
+
}
|
|
1800
|
+
reflector;
|
|
1801
|
+
logger = new Logger8(_CsrfGuard.name);
|
|
1802
|
+
constructor(reflector) {
|
|
1803
|
+
this.reflector = reflector;
|
|
1804
|
+
}
|
|
1805
|
+
async canActivate(context) {
|
|
1806
|
+
const request = context.switchToHttp().getRequest();
|
|
1807
|
+
const reply = context.switchToHttp().getResponse();
|
|
1808
|
+
const safeMethods = [
|
|
1809
|
+
"GET",
|
|
1810
|
+
"HEAD",
|
|
1811
|
+
"OPTIONS"
|
|
1812
|
+
];
|
|
1813
|
+
if (safeMethods.includes(request.method)) {
|
|
1814
|
+
return true;
|
|
1815
|
+
}
|
|
1816
|
+
try {
|
|
1817
|
+
const fastifyInstance = request.server;
|
|
1818
|
+
if (!fastifyInstance.csrfProtection) {
|
|
1819
|
+
this.logger.error("CSRF protection plugin not found. Ensure @fastify/csrf-protection is registered.");
|
|
1820
|
+
throw new ForbiddenException("CSRF protection not configured");
|
|
1821
|
+
}
|
|
1822
|
+
await new Promise((resolve, reject) => {
|
|
1823
|
+
fastifyInstance.csrfProtection(request, reply, (err) => {
|
|
1824
|
+
if (err) {
|
|
1825
|
+
reject(err);
|
|
1826
|
+
} else {
|
|
1827
|
+
resolve();
|
|
1828
|
+
}
|
|
1829
|
+
});
|
|
1830
|
+
});
|
|
1831
|
+
this.logger.debug(`CSRF validation successful for ${request.method} ${request.url}`);
|
|
1832
|
+
return true;
|
|
1833
|
+
} catch (error) {
|
|
1834
|
+
this.logger.warn(`CSRF validation failed for ${request.method} ${request.url}: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
1835
|
+
throw new ForbiddenException({
|
|
1836
|
+
errors: [
|
|
1837
|
+
{
|
|
1838
|
+
field: "csrf",
|
|
1839
|
+
message: "Invalid or missing CSRF token"
|
|
1840
|
+
}
|
|
1841
|
+
],
|
|
1842
|
+
message: "CSRF validation failed"
|
|
1843
|
+
});
|
|
1844
|
+
}
|
|
1845
|
+
}
|
|
1846
|
+
};
|
|
1847
|
+
CsrfGuard = _ts_decorate11([
|
|
1848
|
+
Injectable8(),
|
|
1849
|
+
_ts_metadata7("design:type", Function),
|
|
1850
|
+
_ts_metadata7("design:paramtypes", [
|
|
1851
|
+
typeof Reflector3 === "undefined" ? Object : Reflector3
|
|
1852
|
+
])
|
|
1853
|
+
], CsrfGuard);
|
|
1854
|
+
|
|
1855
|
+
// src/http/http.module.ts
|
|
1856
|
+
function _ts_decorate12(decorators, target, key, desc) {
|
|
1857
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1858
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1859
|
+
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;
|
|
1860
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1861
|
+
}
|
|
1862
|
+
__name(_ts_decorate12, "_ts_decorate");
|
|
1863
|
+
var HttpModule = class {
|
|
1864
|
+
static {
|
|
1865
|
+
__name(this, "HttpModule");
|
|
1866
|
+
}
|
|
1867
|
+
};
|
|
1868
|
+
HttpModule = _ts_decorate12([
|
|
1869
|
+
Module4({
|
|
1870
|
+
providers: [
|
|
1871
|
+
CsrfGuard
|
|
1872
|
+
],
|
|
1873
|
+
exports: [
|
|
1874
|
+
CsrfGuard
|
|
1875
|
+
]
|
|
1876
|
+
})
|
|
1877
|
+
], HttpModule);
|
|
1878
|
+
|
|
1879
|
+
// src/http/filters/http-exception.filter.ts
|
|
1880
|
+
import { Catch, HttpException, HttpStatus, Logger as Logger9 } from "@nestjs/common";
|
|
1881
|
+
function _ts_decorate13(decorators, target, key, desc) {
|
|
1882
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1883
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1884
|
+
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;
|
|
1885
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1886
|
+
}
|
|
1887
|
+
__name(_ts_decorate13, "_ts_decorate");
|
|
1888
|
+
var HttpExceptionFilter = class _HttpExceptionFilter {
|
|
1889
|
+
static {
|
|
1890
|
+
__name(this, "HttpExceptionFilter");
|
|
1891
|
+
}
|
|
1892
|
+
logger = new Logger9(_HttpExceptionFilter.name);
|
|
1893
|
+
catch(exception, host) {
|
|
1894
|
+
const ctx = host.switchToHttp();
|
|
1895
|
+
const reply = ctx.getResponse();
|
|
1896
|
+
const request = ctx.getRequest();
|
|
1897
|
+
let status = HttpStatus.INTERNAL_SERVER_ERROR;
|
|
1898
|
+
let errors = [];
|
|
1899
|
+
let message;
|
|
1900
|
+
if (exception instanceof HttpException) {
|
|
1901
|
+
status = exception.getStatus();
|
|
1902
|
+
const exceptionResponse = exception.getResponse();
|
|
1903
|
+
if (typeof exceptionResponse === "object" && exceptionResponse !== null) {
|
|
1904
|
+
const responseObj = exceptionResponse;
|
|
1905
|
+
if (Array.isArray(responseObj.message)) {
|
|
1906
|
+
errors = this.parseValidationErrors(responseObj.message);
|
|
1907
|
+
message = "Validation failed";
|
|
1908
|
+
} else if (responseObj.message) {
|
|
1909
|
+
errors = [
|
|
1910
|
+
{
|
|
1911
|
+
field: "general",
|
|
1912
|
+
message: responseObj.message
|
|
1913
|
+
}
|
|
1914
|
+
];
|
|
1915
|
+
message = responseObj.message;
|
|
1916
|
+
}
|
|
1917
|
+
} else if (typeof exceptionResponse === "string") {
|
|
1918
|
+
errors = [
|
|
1919
|
+
{
|
|
1920
|
+
field: "general",
|
|
1921
|
+
message: exceptionResponse
|
|
1922
|
+
}
|
|
1923
|
+
];
|
|
1924
|
+
message = exceptionResponse;
|
|
1925
|
+
}
|
|
1926
|
+
} else if (exception instanceof Error) {
|
|
1927
|
+
this.logger.error(`Unhandled error: ${exception.message}`, exception.stack);
|
|
1928
|
+
errors = [
|
|
1929
|
+
{
|
|
1930
|
+
field: "general",
|
|
1931
|
+
message: "Internal server error"
|
|
1932
|
+
}
|
|
1933
|
+
];
|
|
1934
|
+
message = "An unexpected error occurred";
|
|
1935
|
+
} else {
|
|
1936
|
+
this.logger.error("Unknown exception type", exception);
|
|
1937
|
+
errors = [
|
|
1938
|
+
{
|
|
1939
|
+
field: "general",
|
|
1940
|
+
message: "Internal server error"
|
|
1941
|
+
}
|
|
1942
|
+
];
|
|
1943
|
+
message = "An unexpected error occurred";
|
|
1944
|
+
}
|
|
1945
|
+
const errorResponse = {
|
|
1946
|
+
errors,
|
|
1947
|
+
message,
|
|
1948
|
+
statusCode: status,
|
|
1949
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1950
|
+
path: request.url
|
|
1951
|
+
};
|
|
1952
|
+
if (status >= 500) {
|
|
1953
|
+
this.logger.error(`HTTP ${status} Error: ${JSON.stringify(errorResponse)}`, exception instanceof Error ? exception.stack : void 0);
|
|
1954
|
+
} else {
|
|
1955
|
+
this.logger.warn(`HTTP ${status} Error: ${JSON.stringify(errorResponse)}`);
|
|
1956
|
+
}
|
|
1957
|
+
reply.status(status).send(errorResponse);
|
|
1958
|
+
}
|
|
1959
|
+
/**
|
|
1960
|
+
* Parse class-validator error messages into field-specific errors
|
|
1961
|
+
*/
|
|
1962
|
+
parseValidationErrors(messages) {
|
|
1963
|
+
const errors = [];
|
|
1964
|
+
for (const msg of messages) {
|
|
1965
|
+
if (typeof msg === "string") {
|
|
1966
|
+
errors.push({
|
|
1967
|
+
field: "general",
|
|
1968
|
+
message: msg
|
|
1969
|
+
});
|
|
1970
|
+
} else if (typeof msg === "object" && msg.property && msg.constraints) {
|
|
1971
|
+
const field = msg.property;
|
|
1972
|
+
const constraintMessages = Object.values(msg.constraints);
|
|
1973
|
+
for (const constraintMsg of constraintMessages) {
|
|
1974
|
+
errors.push({
|
|
1975
|
+
field,
|
|
1976
|
+
message: constraintMsg
|
|
1977
|
+
});
|
|
1978
|
+
}
|
|
1979
|
+
}
|
|
1980
|
+
}
|
|
1981
|
+
return errors.length > 0 ? errors : [
|
|
1982
|
+
{
|
|
1983
|
+
field: "general",
|
|
1984
|
+
message: "Validation failed"
|
|
1985
|
+
}
|
|
1986
|
+
];
|
|
1987
|
+
}
|
|
1988
|
+
};
|
|
1989
|
+
HttpExceptionFilter = _ts_decorate13([
|
|
1990
|
+
Catch()
|
|
1991
|
+
], HttpExceptionFilter);
|
|
1217
1992
|
export {
|
|
1218
1993
|
AuthConfigModule,
|
|
1994
|
+
CsrfGuard,
|
|
1219
1995
|
DatabaseModule,
|
|
1996
|
+
HttpExceptionFilter,
|
|
1997
|
+
HttpModule,
|
|
1220
1998
|
Onboarding,
|
|
1999
|
+
PrimaryBaseRepository,
|
|
1221
2000
|
PrimaryDatabaseService,
|
|
1222
2001
|
Public,
|
|
1223
2002
|
Tenant,
|
|
2003
|
+
TenantBaseRepository,
|
|
1224
2004
|
TenantContextService,
|
|
1225
2005
|
TenantDatabaseService,
|
|
1226
2006
|
VrittiAuthGuard
|