@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.cjs
CHANGED
|
@@ -32,11 +32,16 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
32
32
|
var index_exports = {};
|
|
33
33
|
__export(index_exports, {
|
|
34
34
|
AuthConfigModule: () => AuthConfigModule,
|
|
35
|
+
CsrfGuard: () => CsrfGuard,
|
|
35
36
|
DatabaseModule: () => DatabaseModule,
|
|
37
|
+
HttpExceptionFilter: () => HttpExceptionFilter,
|
|
38
|
+
HttpModule: () => HttpModule,
|
|
36
39
|
Onboarding: () => Onboarding,
|
|
40
|
+
PrimaryBaseRepository: () => PrimaryBaseRepository,
|
|
37
41
|
PrimaryDatabaseService: () => PrimaryDatabaseService,
|
|
38
42
|
Public: () => Public,
|
|
39
43
|
Tenant: () => Tenant,
|
|
44
|
+
TenantBaseRepository: () => TenantBaseRepository,
|
|
40
45
|
TenantContextService: () => TenantContextService,
|
|
41
46
|
TenantDatabaseService: () => TenantDatabaseService,
|
|
42
47
|
VrittiAuthGuard: () => VrittiAuthGuard
|
|
@@ -376,16 +381,15 @@ var PrimaryDatabaseService = class _PrimaryDatabaseService {
|
|
|
376
381
|
this.logger.log(`Cleared ${size} cached tenant configs`);
|
|
377
382
|
}
|
|
378
383
|
/**
|
|
379
|
-
* Get
|
|
380
|
-
*
|
|
381
|
-
* This is useful for platform admin operations (creating tenants, billing, etc.)
|
|
384
|
+
* Get the Prisma client for the primary database.
|
|
385
|
+
* This is a synchronous property that returns the initialized Prisma client.
|
|
382
386
|
*
|
|
383
387
|
* @returns Primary database client instance
|
|
384
388
|
* @throws Error if primary database client is not initialized
|
|
385
389
|
*/
|
|
386
|
-
|
|
390
|
+
get prismaClient() {
|
|
387
391
|
if (!this.primaryDbClient) {
|
|
388
|
-
throw new Error("Primary database client not initialized
|
|
392
|
+
throw new Error("Primary database client not initialized");
|
|
389
393
|
}
|
|
390
394
|
return this.primaryDbClient;
|
|
391
395
|
}
|
|
@@ -698,7 +702,7 @@ AuthConfigModule = _ts_decorate5([
|
|
|
698
702
|
|
|
699
703
|
// src/database/database.module.ts
|
|
700
704
|
var import_common10 = require("@nestjs/common");
|
|
701
|
-
var
|
|
705
|
+
var import_core5 = require("@nestjs/core");
|
|
702
706
|
|
|
703
707
|
// src/database/interceptors/message-tenant-context.interceptor.ts
|
|
704
708
|
var import_common7 = require("@nestjs/common");
|
|
@@ -864,6 +868,7 @@ MessageTenantContextInterceptor = _ts_decorate7([
|
|
|
864
868
|
|
|
865
869
|
// src/database/interceptors/tenant-context.interceptor.ts
|
|
866
870
|
var import_common8 = require("@nestjs/common");
|
|
871
|
+
var import_core4 = require("@nestjs/core");
|
|
867
872
|
function _ts_decorate8(decorators, target, key, desc) {
|
|
868
873
|
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
869
874
|
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
@@ -879,11 +884,13 @@ var TenantContextInterceptor = class _TenantContextInterceptor {
|
|
|
879
884
|
static {
|
|
880
885
|
__name(this, "TenantContextInterceptor");
|
|
881
886
|
}
|
|
887
|
+
reflector;
|
|
882
888
|
tenantContext;
|
|
883
889
|
primaryDatabase;
|
|
884
890
|
requestService;
|
|
885
891
|
logger = new import_common8.Logger(_TenantContextInterceptor.name);
|
|
886
|
-
constructor(tenantContext, primaryDatabase, requestService) {
|
|
892
|
+
constructor(reflector, tenantContext, primaryDatabase, requestService) {
|
|
893
|
+
this.reflector = reflector;
|
|
887
894
|
this.tenantContext = tenantContext;
|
|
888
895
|
this.primaryDatabase = primaryDatabase;
|
|
889
896
|
this.requestService = requestService;
|
|
@@ -891,8 +898,16 @@ var TenantContextInterceptor = class _TenantContextInterceptor {
|
|
|
891
898
|
async intercept(context, next) {
|
|
892
899
|
const request = context.switchToHttp().getRequest();
|
|
893
900
|
this.logger.debug(`Processing request: ${request.method} ${request.url}`);
|
|
901
|
+
const isPublic = this.reflector.getAllAndOverride("isPublic", [
|
|
902
|
+
context.getHandler(),
|
|
903
|
+
context.getClass()
|
|
904
|
+
]);
|
|
894
905
|
try {
|
|
895
906
|
const tenantIdentifier = this.requestService.getTenantIdentifier();
|
|
907
|
+
if (isPublic && !tenantIdentifier) {
|
|
908
|
+
this.logger.debug("Public endpoint without tenant identifier, skipping tenant context setup");
|
|
909
|
+
return next.handle();
|
|
910
|
+
}
|
|
896
911
|
if (!tenantIdentifier) {
|
|
897
912
|
throw new import_common8.UnauthorizedException("Tenant identifier not found in request");
|
|
898
913
|
}
|
|
@@ -927,6 +942,7 @@ TenantContextInterceptor = _ts_decorate8([
|
|
|
927
942
|
}),
|
|
928
943
|
_ts_metadata5("design:type", Function),
|
|
929
944
|
_ts_metadata5("design:paramtypes", [
|
|
945
|
+
typeof import_core4.Reflector === "undefined" ? Object : import_core4.Reflector,
|
|
930
946
|
typeof TenantContextService === "undefined" ? Object : TenantContextService,
|
|
931
947
|
typeof PrimaryDatabaseService === "undefined" ? Object : PrimaryDatabaseService,
|
|
932
948
|
typeof RequestService === "undefined" ? Object : RequestService
|
|
@@ -971,6 +987,17 @@ var TenantDatabaseService = class _TenantDatabaseService {
|
|
|
971
987
|
this.startConnectionCleaner();
|
|
972
988
|
}
|
|
973
989
|
/**
|
|
990
|
+
* Get the Prisma client for the current tenant's database.
|
|
991
|
+
* This returns the tenant-scoped database client.
|
|
992
|
+
*
|
|
993
|
+
* @returns Tenant-scoped database client instance
|
|
994
|
+
* @throws UnauthorizedException if tenant context not set
|
|
995
|
+
* @throws InternalServerErrorException if connection fails
|
|
996
|
+
*/
|
|
997
|
+
get prismaClient() {
|
|
998
|
+
return this.getDbClient();
|
|
999
|
+
}
|
|
1000
|
+
/**
|
|
974
1001
|
* Get tenant-scoped database client for the current request/message
|
|
975
1002
|
*
|
|
976
1003
|
* This method:
|
|
@@ -1164,7 +1191,7 @@ var DatabaseModule = class _DatabaseModule {
|
|
|
1164
1191
|
* })
|
|
1165
1192
|
*/
|
|
1166
1193
|
static forServer(options) {
|
|
1167
|
-
return this.createDynamicModule(options, "
|
|
1194
|
+
return this.createDynamicModule(options, "server");
|
|
1168
1195
|
}
|
|
1169
1196
|
/**
|
|
1170
1197
|
* Configure DatabaseModule for Microservice/Messaging mode (RabbitMQ workers)
|
|
@@ -1207,14 +1234,14 @@ var DatabaseModule = class _DatabaseModule {
|
|
|
1207
1234
|
PrimaryDatabaseService,
|
|
1208
1235
|
TenantDatabaseService
|
|
1209
1236
|
];
|
|
1210
|
-
if (mode === "
|
|
1237
|
+
if (mode === "server") {
|
|
1211
1238
|
providers.push({
|
|
1212
|
-
provide:
|
|
1239
|
+
provide: import_core5.APP_INTERCEPTOR,
|
|
1213
1240
|
useClass: TenantContextInterceptor
|
|
1214
1241
|
});
|
|
1215
1242
|
} else {
|
|
1216
1243
|
providers.push({
|
|
1217
|
-
provide:
|
|
1244
|
+
provide: import_core5.APP_INTERCEPTOR,
|
|
1218
1245
|
useClass: MessageTenantContextInterceptor
|
|
1219
1246
|
});
|
|
1220
1247
|
}
|
|
@@ -1238,9 +1265,548 @@ DatabaseModule = _ts_decorate10([
|
|
|
1238
1265
|
(0, import_common10.Module)({})
|
|
1239
1266
|
], DatabaseModule);
|
|
1240
1267
|
|
|
1241
|
-
// src/database/
|
|
1268
|
+
// src/database/repositories/primary-base.repository.ts
|
|
1242
1269
|
var import_common11 = require("@nestjs/common");
|
|
1243
|
-
var
|
|
1270
|
+
var PrimaryBaseRepository = class {
|
|
1271
|
+
static {
|
|
1272
|
+
__name(this, "PrimaryBaseRepository");
|
|
1273
|
+
}
|
|
1274
|
+
database;
|
|
1275
|
+
logger;
|
|
1276
|
+
modelGetter;
|
|
1277
|
+
/**
|
|
1278
|
+
* Lazy getter for Prisma client.
|
|
1279
|
+
* Accesses the client from the database service only when needed,
|
|
1280
|
+
* avoiding initialization timing issues with NestJS lifecycle.
|
|
1281
|
+
*/
|
|
1282
|
+
get prisma() {
|
|
1283
|
+
return this.database.prismaClient;
|
|
1284
|
+
}
|
|
1285
|
+
/**
|
|
1286
|
+
* Lazy getter for the Prisma model delegate.
|
|
1287
|
+
* Returns the specific model (e.g., prisma.user, prisma.tenant) for this repository.
|
|
1288
|
+
*/
|
|
1289
|
+
get model() {
|
|
1290
|
+
return this.modelGetter(this.prisma);
|
|
1291
|
+
}
|
|
1292
|
+
/**
|
|
1293
|
+
* Create a new repository instance
|
|
1294
|
+
*
|
|
1295
|
+
* @param database - The primary database service
|
|
1296
|
+
* @param getModel - Function that returns the Prisma model delegate from the client
|
|
1297
|
+
*
|
|
1298
|
+
* @example
|
|
1299
|
+
* ```typescript
|
|
1300
|
+
* // Standard usage with full parameter name
|
|
1301
|
+
* constructor(database: PrimaryDatabaseService) {
|
|
1302
|
+
* super(database, (prisma) => prisma.user);
|
|
1303
|
+
* }
|
|
1304
|
+
*
|
|
1305
|
+
* // Short syntax
|
|
1306
|
+
* constructor(database: PrimaryDatabaseService) {
|
|
1307
|
+
* super(database, (p) => p.user);
|
|
1308
|
+
* }
|
|
1309
|
+
*
|
|
1310
|
+
* // Complex model names
|
|
1311
|
+
* constructor(database: PrimaryDatabaseService) {
|
|
1312
|
+
* super(database, (p) => p.emailVerification);
|
|
1313
|
+
* }
|
|
1314
|
+
* ```
|
|
1315
|
+
*/
|
|
1316
|
+
constructor(database, getModel) {
|
|
1317
|
+
this.database = database;
|
|
1318
|
+
this.logger = new import_common11.Logger(this.constructor.name);
|
|
1319
|
+
this.modelGetter = getModel;
|
|
1320
|
+
this.logger.debug(`Initialized ${this.constructor.name}`);
|
|
1321
|
+
}
|
|
1322
|
+
/**
|
|
1323
|
+
* Create a new record
|
|
1324
|
+
*
|
|
1325
|
+
* @param data - The data to create the record with
|
|
1326
|
+
* @returns Promise resolving to the created record
|
|
1327
|
+
*
|
|
1328
|
+
* @example
|
|
1329
|
+
* ```typescript
|
|
1330
|
+
* const user = await userRepository.create({
|
|
1331
|
+
* email: 'user@example.com',
|
|
1332
|
+
* name: 'John Doe'
|
|
1333
|
+
* });
|
|
1334
|
+
* ```
|
|
1335
|
+
*/
|
|
1336
|
+
async create(data) {
|
|
1337
|
+
this.logger.log("Creating record");
|
|
1338
|
+
return await this.model.create({
|
|
1339
|
+
data
|
|
1340
|
+
});
|
|
1341
|
+
}
|
|
1342
|
+
/**
|
|
1343
|
+
* Find a single record by ID
|
|
1344
|
+
*
|
|
1345
|
+
* @param id - The record ID
|
|
1346
|
+
* @returns Promise resolving to the record or null if not found
|
|
1347
|
+
*
|
|
1348
|
+
* @example
|
|
1349
|
+
* ```typescript
|
|
1350
|
+
* const user = await userRepository.findById('user-id-123');
|
|
1351
|
+
* ```
|
|
1352
|
+
*/
|
|
1353
|
+
async findById(id) {
|
|
1354
|
+
this.logger.debug(`Finding record by ID: ${id}`);
|
|
1355
|
+
return await this.model.findUnique({
|
|
1356
|
+
where: {
|
|
1357
|
+
id
|
|
1358
|
+
}
|
|
1359
|
+
});
|
|
1360
|
+
}
|
|
1361
|
+
/**
|
|
1362
|
+
* Find a single record with custom where clause
|
|
1363
|
+
*
|
|
1364
|
+
* @param where - The where clause or findUnique args
|
|
1365
|
+
* @returns Promise resolving to the record or null if not found
|
|
1366
|
+
*
|
|
1367
|
+
* @example
|
|
1368
|
+
* ```typescript
|
|
1369
|
+
* // Simple where clause
|
|
1370
|
+
* const user = await userRepository.findOne({ email: 'user@example.com' });
|
|
1371
|
+
*
|
|
1372
|
+
* // With include
|
|
1373
|
+
* const user = await userRepository.findOne({
|
|
1374
|
+
* where: { email: 'user@example.com' },
|
|
1375
|
+
* include: { posts: true }
|
|
1376
|
+
* });
|
|
1377
|
+
* ```
|
|
1378
|
+
*/
|
|
1379
|
+
async findOne(where) {
|
|
1380
|
+
this.logger.debug("Finding record with custom query");
|
|
1381
|
+
return await this.model.findUnique(typeof where === "object" && "where" in where ? where : {
|
|
1382
|
+
where
|
|
1383
|
+
});
|
|
1384
|
+
}
|
|
1385
|
+
/**
|
|
1386
|
+
* Find multiple records
|
|
1387
|
+
*
|
|
1388
|
+
* @param args - Prisma findMany arguments (where, orderBy, take, skip, etc.)
|
|
1389
|
+
* @returns Promise resolving to an array of records
|
|
1390
|
+
*
|
|
1391
|
+
* @example
|
|
1392
|
+
* ```typescript
|
|
1393
|
+
* // Find all users
|
|
1394
|
+
* const users = await userRepository.findMany();
|
|
1395
|
+
*
|
|
1396
|
+
* // Find with filtering and pagination
|
|
1397
|
+
* const users = await userRepository.findMany({
|
|
1398
|
+
* where: { status: 'ACTIVE' },
|
|
1399
|
+
* orderBy: { createdAt: 'desc' },
|
|
1400
|
+
* take: 10,
|
|
1401
|
+
* skip: 0
|
|
1402
|
+
* });
|
|
1403
|
+
* ```
|
|
1404
|
+
*/
|
|
1405
|
+
async findMany(args) {
|
|
1406
|
+
this.logger.debug("Finding multiple records");
|
|
1407
|
+
return await this.model.findMany(args);
|
|
1408
|
+
}
|
|
1409
|
+
/**
|
|
1410
|
+
* Update a record by ID
|
|
1411
|
+
*
|
|
1412
|
+
* @param id - The record ID
|
|
1413
|
+
* @param data - The data to update
|
|
1414
|
+
* @returns Promise resolving to the updated record
|
|
1415
|
+
*
|
|
1416
|
+
* @example
|
|
1417
|
+
* ```typescript
|
|
1418
|
+
* const user = await userRepository.update('user-id-123', {
|
|
1419
|
+
* name: 'Jane Doe'
|
|
1420
|
+
* });
|
|
1421
|
+
* ```
|
|
1422
|
+
*/
|
|
1423
|
+
async update(id, data) {
|
|
1424
|
+
this.logger.log(`Updating record with ID: ${id}`);
|
|
1425
|
+
return await this.model.update({
|
|
1426
|
+
where: {
|
|
1427
|
+
id
|
|
1428
|
+
},
|
|
1429
|
+
data
|
|
1430
|
+
});
|
|
1431
|
+
}
|
|
1432
|
+
/**
|
|
1433
|
+
* Update multiple records
|
|
1434
|
+
*
|
|
1435
|
+
* @param where - The where clause to match records
|
|
1436
|
+
* @param data - The data to update
|
|
1437
|
+
* @returns Promise resolving to the count of updated records
|
|
1438
|
+
*
|
|
1439
|
+
* @example
|
|
1440
|
+
* ```typescript
|
|
1441
|
+
* const result = await userRepository.updateMany(
|
|
1442
|
+
* { status: 'PENDING' },
|
|
1443
|
+
* { status: 'ACTIVE' }
|
|
1444
|
+
* );
|
|
1445
|
+
* console.log(`Updated ${result.count} users`);
|
|
1446
|
+
* ```
|
|
1447
|
+
*/
|
|
1448
|
+
async updateMany(where, data) {
|
|
1449
|
+
this.logger.log("Updating multiple records");
|
|
1450
|
+
return await this.model.updateMany({
|
|
1451
|
+
where,
|
|
1452
|
+
data
|
|
1453
|
+
});
|
|
1454
|
+
}
|
|
1455
|
+
/**
|
|
1456
|
+
* Delete a record by ID
|
|
1457
|
+
*
|
|
1458
|
+
* @param id - The record ID
|
|
1459
|
+
* @returns Promise resolving to the deleted record
|
|
1460
|
+
*
|
|
1461
|
+
* @example
|
|
1462
|
+
* ```typescript
|
|
1463
|
+
* const user = await userRepository.delete('user-id-123');
|
|
1464
|
+
* ```
|
|
1465
|
+
*/
|
|
1466
|
+
async delete(id) {
|
|
1467
|
+
this.logger.log(`Deleting record with ID: ${id}`);
|
|
1468
|
+
return await this.model.delete({
|
|
1469
|
+
where: {
|
|
1470
|
+
id
|
|
1471
|
+
}
|
|
1472
|
+
});
|
|
1473
|
+
}
|
|
1474
|
+
/**
|
|
1475
|
+
* Delete multiple records
|
|
1476
|
+
*
|
|
1477
|
+
* @param where - The where clause to match records
|
|
1478
|
+
* @returns Promise resolving to the count of deleted records
|
|
1479
|
+
*
|
|
1480
|
+
* @example
|
|
1481
|
+
* ```typescript
|
|
1482
|
+
* const result = await userRepository.deleteMany({
|
|
1483
|
+
* status: 'INACTIVE',
|
|
1484
|
+
* createdAt: { lt: new Date('2020-01-01') }
|
|
1485
|
+
* });
|
|
1486
|
+
* console.log(`Deleted ${result.count} users`);
|
|
1487
|
+
* ```
|
|
1488
|
+
*/
|
|
1489
|
+
async deleteMany(where) {
|
|
1490
|
+
this.logger.log("Deleting multiple records");
|
|
1491
|
+
return await this.model.deleteMany({
|
|
1492
|
+
where
|
|
1493
|
+
});
|
|
1494
|
+
}
|
|
1495
|
+
/**
|
|
1496
|
+
* Count records
|
|
1497
|
+
*
|
|
1498
|
+
* @param where - Optional where clause to filter records
|
|
1499
|
+
* @returns Promise resolving to the count of records
|
|
1500
|
+
*
|
|
1501
|
+
* @example
|
|
1502
|
+
* ```typescript
|
|
1503
|
+
* // Count all users
|
|
1504
|
+
* const total = await userRepository.count();
|
|
1505
|
+
*
|
|
1506
|
+
* // Count active users
|
|
1507
|
+
* const activeCount = await userRepository.count({ status: 'ACTIVE' });
|
|
1508
|
+
* ```
|
|
1509
|
+
*/
|
|
1510
|
+
async count(where) {
|
|
1511
|
+
this.logger.debug("Counting records");
|
|
1512
|
+
return await this.model.count({
|
|
1513
|
+
where
|
|
1514
|
+
});
|
|
1515
|
+
}
|
|
1516
|
+
/**
|
|
1517
|
+
* Check if a record exists
|
|
1518
|
+
*
|
|
1519
|
+
* @param where - The where clause to match records
|
|
1520
|
+
* @returns Promise resolving to true if at least one record exists, false otherwise
|
|
1521
|
+
*
|
|
1522
|
+
* @example
|
|
1523
|
+
* ```typescript
|
|
1524
|
+
* const emailExists = await userRepository.exists({
|
|
1525
|
+
* email: 'user@example.com'
|
|
1526
|
+
* });
|
|
1527
|
+
* ```
|
|
1528
|
+
*/
|
|
1529
|
+
async exists(where) {
|
|
1530
|
+
const count = await this.model.count({
|
|
1531
|
+
where
|
|
1532
|
+
});
|
|
1533
|
+
return count > 0;
|
|
1534
|
+
}
|
|
1535
|
+
};
|
|
1536
|
+
|
|
1537
|
+
// src/database/repositories/tenant-base.repository.ts
|
|
1538
|
+
var import_common12 = require("@nestjs/common");
|
|
1539
|
+
var TenantBaseRepository = class {
|
|
1540
|
+
static {
|
|
1541
|
+
__name(this, "TenantBaseRepository");
|
|
1542
|
+
}
|
|
1543
|
+
database;
|
|
1544
|
+
logger;
|
|
1545
|
+
modelGetter;
|
|
1546
|
+
/**
|
|
1547
|
+
* Lazy getter for Prisma client.
|
|
1548
|
+
* Accesses the client from the database service only when needed,
|
|
1549
|
+
* avoiding initialization timing issues with NestJS lifecycle.
|
|
1550
|
+
*/
|
|
1551
|
+
get prisma() {
|
|
1552
|
+
return this.database.prismaClient;
|
|
1553
|
+
}
|
|
1554
|
+
/**
|
|
1555
|
+
* Lazy getter for the Prisma model delegate.
|
|
1556
|
+
* Returns the specific model (e.g., prisma.product, prisma.order) for this repository.
|
|
1557
|
+
*/
|
|
1558
|
+
get model() {
|
|
1559
|
+
return this.modelGetter(this.prisma);
|
|
1560
|
+
}
|
|
1561
|
+
/**
|
|
1562
|
+
* Create a new repository instance
|
|
1563
|
+
*
|
|
1564
|
+
* @param database - The tenant database service
|
|
1565
|
+
* @param getModel - Function that returns the Prisma model delegate from the client
|
|
1566
|
+
*
|
|
1567
|
+
* @example
|
|
1568
|
+
* ```typescript
|
|
1569
|
+
* // Standard usage with full parameter name
|
|
1570
|
+
* constructor(database: TenantDatabaseService) {
|
|
1571
|
+
* super(database, (prisma) => prisma.product);
|
|
1572
|
+
* }
|
|
1573
|
+
*
|
|
1574
|
+
* // Short syntax
|
|
1575
|
+
* constructor(database: TenantDatabaseService) {
|
|
1576
|
+
* super(database, (p) => p.product);
|
|
1577
|
+
* }
|
|
1578
|
+
*
|
|
1579
|
+
* // Complex model names
|
|
1580
|
+
* constructor(database: TenantDatabaseService) {
|
|
1581
|
+
* super(database, (p) => p.inventoryItem);
|
|
1582
|
+
* }
|
|
1583
|
+
* ```
|
|
1584
|
+
*/
|
|
1585
|
+
constructor(database, getModel) {
|
|
1586
|
+
this.database = database;
|
|
1587
|
+
this.logger = new import_common12.Logger(this.constructor.name);
|
|
1588
|
+
this.modelGetter = getModel;
|
|
1589
|
+
this.logger.debug(`Initialized ${this.constructor.name}`);
|
|
1590
|
+
}
|
|
1591
|
+
/**
|
|
1592
|
+
* Create a new record
|
|
1593
|
+
*
|
|
1594
|
+
* @param data - The data to create the record with
|
|
1595
|
+
* @returns Promise resolving to the created record
|
|
1596
|
+
*
|
|
1597
|
+
* @example
|
|
1598
|
+
* ```typescript
|
|
1599
|
+
* const product = await productRepository.create({
|
|
1600
|
+
* name: 'Widget',
|
|
1601
|
+
* sku: 'WDG-001',
|
|
1602
|
+
* price: 9.99
|
|
1603
|
+
* });
|
|
1604
|
+
* ```
|
|
1605
|
+
*/
|
|
1606
|
+
async create(data) {
|
|
1607
|
+
this.logger.log("Creating record");
|
|
1608
|
+
return await this.model.create({
|
|
1609
|
+
data
|
|
1610
|
+
});
|
|
1611
|
+
}
|
|
1612
|
+
/**
|
|
1613
|
+
* Find a single record by ID
|
|
1614
|
+
*
|
|
1615
|
+
* @param id - The record ID
|
|
1616
|
+
* @returns Promise resolving to the record or null if not found
|
|
1617
|
+
*
|
|
1618
|
+
* @example
|
|
1619
|
+
* ```typescript
|
|
1620
|
+
* const product = await productRepository.findById('product-id-123');
|
|
1621
|
+
* ```
|
|
1622
|
+
*/
|
|
1623
|
+
async findById(id) {
|
|
1624
|
+
this.logger.debug(`Finding record by ID: ${id}`);
|
|
1625
|
+
return await this.model.findUnique({
|
|
1626
|
+
where: {
|
|
1627
|
+
id
|
|
1628
|
+
}
|
|
1629
|
+
});
|
|
1630
|
+
}
|
|
1631
|
+
/**
|
|
1632
|
+
* Find a single record with custom where clause
|
|
1633
|
+
*
|
|
1634
|
+
* @param where - The where clause or findUnique args
|
|
1635
|
+
* @returns Promise resolving to the record or null if not found
|
|
1636
|
+
*
|
|
1637
|
+
* @example
|
|
1638
|
+
* ```typescript
|
|
1639
|
+
* // Simple where clause
|
|
1640
|
+
* const product = await productRepository.findOne({ sku: 'WDG-001' });
|
|
1641
|
+
*
|
|
1642
|
+
* // With include
|
|
1643
|
+
* const product = await productRepository.findOne({
|
|
1644
|
+
* where: { sku: 'WDG-001' },
|
|
1645
|
+
* include: { category: true }
|
|
1646
|
+
* });
|
|
1647
|
+
* ```
|
|
1648
|
+
*/
|
|
1649
|
+
async findOne(where) {
|
|
1650
|
+
this.logger.debug("Finding record with custom query");
|
|
1651
|
+
return await this.model.findUnique(typeof where === "object" && "where" in where ? where : {
|
|
1652
|
+
where
|
|
1653
|
+
});
|
|
1654
|
+
}
|
|
1655
|
+
/**
|
|
1656
|
+
* Find multiple records
|
|
1657
|
+
*
|
|
1658
|
+
* @param args - Prisma findMany arguments (where, orderBy, take, skip, etc.)
|
|
1659
|
+
* @returns Promise resolving to an array of records
|
|
1660
|
+
*
|
|
1661
|
+
* @example
|
|
1662
|
+
* ```typescript
|
|
1663
|
+
* // Find all products
|
|
1664
|
+
* const products = await productRepository.findMany();
|
|
1665
|
+
*
|
|
1666
|
+
* // Find with filtering and pagination
|
|
1667
|
+
* const products = await productRepository.findMany({
|
|
1668
|
+
* where: { status: 'ACTIVE' },
|
|
1669
|
+
* orderBy: { createdAt: 'desc' },
|
|
1670
|
+
* take: 10,
|
|
1671
|
+
* skip: 0
|
|
1672
|
+
* });
|
|
1673
|
+
* ```
|
|
1674
|
+
*/
|
|
1675
|
+
async findMany(args) {
|
|
1676
|
+
this.logger.debug("Finding multiple records");
|
|
1677
|
+
return await this.model.findMany(args);
|
|
1678
|
+
}
|
|
1679
|
+
/**
|
|
1680
|
+
* Update a record by ID
|
|
1681
|
+
*
|
|
1682
|
+
* @param id - The record ID
|
|
1683
|
+
* @param data - The data to update
|
|
1684
|
+
* @returns Promise resolving to the updated record
|
|
1685
|
+
*
|
|
1686
|
+
* @example
|
|
1687
|
+
* ```typescript
|
|
1688
|
+
* const product = await productRepository.update('product-id-123', {
|
|
1689
|
+
* price: 12.99
|
|
1690
|
+
* });
|
|
1691
|
+
* ```
|
|
1692
|
+
*/
|
|
1693
|
+
async update(id, data) {
|
|
1694
|
+
this.logger.log(`Updating record with ID: ${id}`);
|
|
1695
|
+
return await this.model.update({
|
|
1696
|
+
where: {
|
|
1697
|
+
id
|
|
1698
|
+
},
|
|
1699
|
+
data
|
|
1700
|
+
});
|
|
1701
|
+
}
|
|
1702
|
+
/**
|
|
1703
|
+
* Update multiple records
|
|
1704
|
+
*
|
|
1705
|
+
* @param where - The where clause to match records
|
|
1706
|
+
* @param data - The data to update
|
|
1707
|
+
* @returns Promise resolving to the count of updated records
|
|
1708
|
+
*
|
|
1709
|
+
* @example
|
|
1710
|
+
* ```typescript
|
|
1711
|
+
* const result = await productRepository.updateMany(
|
|
1712
|
+
* { status: 'PENDING' },
|
|
1713
|
+
* { status: 'ACTIVE' }
|
|
1714
|
+
* );
|
|
1715
|
+
* console.log(`Updated ${result.count} products`);
|
|
1716
|
+
* ```
|
|
1717
|
+
*/
|
|
1718
|
+
async updateMany(where, data) {
|
|
1719
|
+
this.logger.log("Updating multiple records");
|
|
1720
|
+
return await this.model.updateMany({
|
|
1721
|
+
where,
|
|
1722
|
+
data
|
|
1723
|
+
});
|
|
1724
|
+
}
|
|
1725
|
+
/**
|
|
1726
|
+
* Delete a record by ID
|
|
1727
|
+
*
|
|
1728
|
+
* @param id - The record ID
|
|
1729
|
+
* @returns Promise resolving to the deleted record
|
|
1730
|
+
*
|
|
1731
|
+
* @example
|
|
1732
|
+
* ```typescript
|
|
1733
|
+
* const product = await productRepository.delete('product-id-123');
|
|
1734
|
+
* ```
|
|
1735
|
+
*/
|
|
1736
|
+
async delete(id) {
|
|
1737
|
+
this.logger.log(`Deleting record with ID: ${id}`);
|
|
1738
|
+
return await this.model.delete({
|
|
1739
|
+
where: {
|
|
1740
|
+
id
|
|
1741
|
+
}
|
|
1742
|
+
});
|
|
1743
|
+
}
|
|
1744
|
+
/**
|
|
1745
|
+
* Delete multiple records
|
|
1746
|
+
*
|
|
1747
|
+
* @param where - The where clause to match records
|
|
1748
|
+
* @returns Promise resolving to the count of deleted records
|
|
1749
|
+
*
|
|
1750
|
+
* @example
|
|
1751
|
+
* ```typescript
|
|
1752
|
+
* const result = await productRepository.deleteMany({
|
|
1753
|
+
* status: 'INACTIVE',
|
|
1754
|
+
* createdAt: { lt: new Date('2020-01-01') }
|
|
1755
|
+
* });
|
|
1756
|
+
* console.log(`Deleted ${result.count} products`);
|
|
1757
|
+
* ```
|
|
1758
|
+
*/
|
|
1759
|
+
async deleteMany(where) {
|
|
1760
|
+
this.logger.log("Deleting multiple records");
|
|
1761
|
+
return await this.model.deleteMany({
|
|
1762
|
+
where
|
|
1763
|
+
});
|
|
1764
|
+
}
|
|
1765
|
+
/**
|
|
1766
|
+
* Count records
|
|
1767
|
+
*
|
|
1768
|
+
* @param where - Optional where clause to filter records
|
|
1769
|
+
* @returns Promise resolving to the count of records
|
|
1770
|
+
*
|
|
1771
|
+
* @example
|
|
1772
|
+
* ```typescript
|
|
1773
|
+
* // Count all products
|
|
1774
|
+
* const total = await productRepository.count();
|
|
1775
|
+
*
|
|
1776
|
+
* // Count active products
|
|
1777
|
+
* const activeCount = await productRepository.count({ status: 'ACTIVE' });
|
|
1778
|
+
* ```
|
|
1779
|
+
*/
|
|
1780
|
+
async count(where) {
|
|
1781
|
+
this.logger.debug("Counting records");
|
|
1782
|
+
return await this.model.count({
|
|
1783
|
+
where
|
|
1784
|
+
});
|
|
1785
|
+
}
|
|
1786
|
+
/**
|
|
1787
|
+
* Check if a record exists
|
|
1788
|
+
*
|
|
1789
|
+
* @param where - The where clause to match records
|
|
1790
|
+
* @returns Promise resolving to true if at least one record exists, false otherwise
|
|
1791
|
+
*
|
|
1792
|
+
* @example
|
|
1793
|
+
* ```typescript
|
|
1794
|
+
* const skuExists = await productRepository.exists({
|
|
1795
|
+
* sku: 'WDG-001'
|
|
1796
|
+
* });
|
|
1797
|
+
* ```
|
|
1798
|
+
*/
|
|
1799
|
+
async exists(where) {
|
|
1800
|
+
const count = await this.model.count({
|
|
1801
|
+
where
|
|
1802
|
+
});
|
|
1803
|
+
return count > 0;
|
|
1804
|
+
}
|
|
1805
|
+
};
|
|
1806
|
+
|
|
1807
|
+
// src/database/decorators/tenant.decorator.ts
|
|
1808
|
+
var import_common13 = require("@nestjs/common");
|
|
1809
|
+
var Tenant = (0, import_common13.createParamDecorator)((data, ctx) => {
|
|
1244
1810
|
const request = ctx.switchToHttp().getRequest();
|
|
1245
1811
|
const tenantContext = request.app?.get?.(TenantContextService);
|
|
1246
1812
|
if (!tenantContext) {
|
|
@@ -1250,20 +1816,239 @@ var Tenant = (0, import_common11.createParamDecorator)((data, ctx) => {
|
|
|
1250
1816
|
});
|
|
1251
1817
|
|
|
1252
1818
|
// src/auth/decorators/onboarding.decorator.ts
|
|
1253
|
-
var
|
|
1254
|
-
var Onboarding = /* @__PURE__ */ __name(() => (0,
|
|
1819
|
+
var import_common14 = require("@nestjs/common");
|
|
1820
|
+
var Onboarding = /* @__PURE__ */ __name(() => (0, import_common14.SetMetadata)("isOnboarding", true), "Onboarding");
|
|
1255
1821
|
|
|
1256
1822
|
// src/auth/decorators/public.decorator.ts
|
|
1257
|
-
var
|
|
1258
|
-
var Public = /* @__PURE__ */ __name(() => (0,
|
|
1823
|
+
var import_common15 = require("@nestjs/common");
|
|
1824
|
+
var Public = /* @__PURE__ */ __name(() => (0, import_common15.SetMetadata)("isPublic", true), "Public");
|
|
1825
|
+
|
|
1826
|
+
// src/http/http.module.ts
|
|
1827
|
+
var import_common17 = require("@nestjs/common");
|
|
1828
|
+
|
|
1829
|
+
// src/http/guards/csrf.guard.ts
|
|
1830
|
+
var import_common16 = require("@nestjs/common");
|
|
1831
|
+
var import_core6 = require("@nestjs/core");
|
|
1832
|
+
function _ts_decorate11(decorators, target, key, desc) {
|
|
1833
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1834
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1835
|
+
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;
|
|
1836
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1837
|
+
}
|
|
1838
|
+
__name(_ts_decorate11, "_ts_decorate");
|
|
1839
|
+
function _ts_metadata7(k, v) {
|
|
1840
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
1841
|
+
}
|
|
1842
|
+
__name(_ts_metadata7, "_ts_metadata");
|
|
1843
|
+
var CsrfGuard = class _CsrfGuard {
|
|
1844
|
+
static {
|
|
1845
|
+
__name(this, "CsrfGuard");
|
|
1846
|
+
}
|
|
1847
|
+
reflector;
|
|
1848
|
+
logger = new import_common16.Logger(_CsrfGuard.name);
|
|
1849
|
+
constructor(reflector) {
|
|
1850
|
+
this.reflector = reflector;
|
|
1851
|
+
}
|
|
1852
|
+
async canActivate(context) {
|
|
1853
|
+
const request = context.switchToHttp().getRequest();
|
|
1854
|
+
const reply = context.switchToHttp().getResponse();
|
|
1855
|
+
const safeMethods = [
|
|
1856
|
+
"GET",
|
|
1857
|
+
"HEAD",
|
|
1858
|
+
"OPTIONS"
|
|
1859
|
+
];
|
|
1860
|
+
if (safeMethods.includes(request.method)) {
|
|
1861
|
+
return true;
|
|
1862
|
+
}
|
|
1863
|
+
try {
|
|
1864
|
+
const fastifyInstance = request.server;
|
|
1865
|
+
if (!fastifyInstance.csrfProtection) {
|
|
1866
|
+
this.logger.error("CSRF protection plugin not found. Ensure @fastify/csrf-protection is registered.");
|
|
1867
|
+
throw new import_common16.ForbiddenException("CSRF protection not configured");
|
|
1868
|
+
}
|
|
1869
|
+
await new Promise((resolve, reject) => {
|
|
1870
|
+
fastifyInstance.csrfProtection(request, reply, (err) => {
|
|
1871
|
+
if (err) {
|
|
1872
|
+
reject(err);
|
|
1873
|
+
} else {
|
|
1874
|
+
resolve();
|
|
1875
|
+
}
|
|
1876
|
+
});
|
|
1877
|
+
});
|
|
1878
|
+
this.logger.debug(`CSRF validation successful for ${request.method} ${request.url}`);
|
|
1879
|
+
return true;
|
|
1880
|
+
} catch (error) {
|
|
1881
|
+
this.logger.warn(`CSRF validation failed for ${request.method} ${request.url}: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
1882
|
+
throw new import_common16.ForbiddenException({
|
|
1883
|
+
errors: [
|
|
1884
|
+
{
|
|
1885
|
+
field: "csrf",
|
|
1886
|
+
message: "Invalid or missing CSRF token"
|
|
1887
|
+
}
|
|
1888
|
+
],
|
|
1889
|
+
message: "CSRF validation failed"
|
|
1890
|
+
});
|
|
1891
|
+
}
|
|
1892
|
+
}
|
|
1893
|
+
};
|
|
1894
|
+
CsrfGuard = _ts_decorate11([
|
|
1895
|
+
(0, import_common16.Injectable)(),
|
|
1896
|
+
_ts_metadata7("design:type", Function),
|
|
1897
|
+
_ts_metadata7("design:paramtypes", [
|
|
1898
|
+
typeof import_core6.Reflector === "undefined" ? Object : import_core6.Reflector
|
|
1899
|
+
])
|
|
1900
|
+
], CsrfGuard);
|
|
1901
|
+
|
|
1902
|
+
// src/http/http.module.ts
|
|
1903
|
+
function _ts_decorate12(decorators, target, key, desc) {
|
|
1904
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1905
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1906
|
+
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;
|
|
1907
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1908
|
+
}
|
|
1909
|
+
__name(_ts_decorate12, "_ts_decorate");
|
|
1910
|
+
var HttpModule = class {
|
|
1911
|
+
static {
|
|
1912
|
+
__name(this, "HttpModule");
|
|
1913
|
+
}
|
|
1914
|
+
};
|
|
1915
|
+
HttpModule = _ts_decorate12([
|
|
1916
|
+
(0, import_common17.Module)({
|
|
1917
|
+
providers: [
|
|
1918
|
+
CsrfGuard
|
|
1919
|
+
],
|
|
1920
|
+
exports: [
|
|
1921
|
+
CsrfGuard
|
|
1922
|
+
]
|
|
1923
|
+
})
|
|
1924
|
+
], HttpModule);
|
|
1925
|
+
|
|
1926
|
+
// src/http/filters/http-exception.filter.ts
|
|
1927
|
+
var import_common18 = require("@nestjs/common");
|
|
1928
|
+
function _ts_decorate13(decorators, target, key, desc) {
|
|
1929
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
1930
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
1931
|
+
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;
|
|
1932
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
1933
|
+
}
|
|
1934
|
+
__name(_ts_decorate13, "_ts_decorate");
|
|
1935
|
+
var HttpExceptionFilter = class _HttpExceptionFilter {
|
|
1936
|
+
static {
|
|
1937
|
+
__name(this, "HttpExceptionFilter");
|
|
1938
|
+
}
|
|
1939
|
+
logger = new import_common18.Logger(_HttpExceptionFilter.name);
|
|
1940
|
+
catch(exception, host) {
|
|
1941
|
+
const ctx = host.switchToHttp();
|
|
1942
|
+
const reply = ctx.getResponse();
|
|
1943
|
+
const request = ctx.getRequest();
|
|
1944
|
+
let status = import_common18.HttpStatus.INTERNAL_SERVER_ERROR;
|
|
1945
|
+
let errors = [];
|
|
1946
|
+
let message;
|
|
1947
|
+
if (exception instanceof import_common18.HttpException) {
|
|
1948
|
+
status = exception.getStatus();
|
|
1949
|
+
const exceptionResponse = exception.getResponse();
|
|
1950
|
+
if (typeof exceptionResponse === "object" && exceptionResponse !== null) {
|
|
1951
|
+
const responseObj = exceptionResponse;
|
|
1952
|
+
if (Array.isArray(responseObj.message)) {
|
|
1953
|
+
errors = this.parseValidationErrors(responseObj.message);
|
|
1954
|
+
message = "Validation failed";
|
|
1955
|
+
} else if (responseObj.message) {
|
|
1956
|
+
errors = [
|
|
1957
|
+
{
|
|
1958
|
+
field: "general",
|
|
1959
|
+
message: responseObj.message
|
|
1960
|
+
}
|
|
1961
|
+
];
|
|
1962
|
+
message = responseObj.message;
|
|
1963
|
+
}
|
|
1964
|
+
} else if (typeof exceptionResponse === "string") {
|
|
1965
|
+
errors = [
|
|
1966
|
+
{
|
|
1967
|
+
field: "general",
|
|
1968
|
+
message: exceptionResponse
|
|
1969
|
+
}
|
|
1970
|
+
];
|
|
1971
|
+
message = exceptionResponse;
|
|
1972
|
+
}
|
|
1973
|
+
} else if (exception instanceof Error) {
|
|
1974
|
+
this.logger.error(`Unhandled error: ${exception.message}`, exception.stack);
|
|
1975
|
+
errors = [
|
|
1976
|
+
{
|
|
1977
|
+
field: "general",
|
|
1978
|
+
message: "Internal server error"
|
|
1979
|
+
}
|
|
1980
|
+
];
|
|
1981
|
+
message = "An unexpected error occurred";
|
|
1982
|
+
} else {
|
|
1983
|
+
this.logger.error("Unknown exception type", exception);
|
|
1984
|
+
errors = [
|
|
1985
|
+
{
|
|
1986
|
+
field: "general",
|
|
1987
|
+
message: "Internal server error"
|
|
1988
|
+
}
|
|
1989
|
+
];
|
|
1990
|
+
message = "An unexpected error occurred";
|
|
1991
|
+
}
|
|
1992
|
+
const errorResponse = {
|
|
1993
|
+
errors,
|
|
1994
|
+
message,
|
|
1995
|
+
statusCode: status,
|
|
1996
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1997
|
+
path: request.url
|
|
1998
|
+
};
|
|
1999
|
+
if (status >= 500) {
|
|
2000
|
+
this.logger.error(`HTTP ${status} Error: ${JSON.stringify(errorResponse)}`, exception instanceof Error ? exception.stack : void 0);
|
|
2001
|
+
} else {
|
|
2002
|
+
this.logger.warn(`HTTP ${status} Error: ${JSON.stringify(errorResponse)}`);
|
|
2003
|
+
}
|
|
2004
|
+
reply.status(status).send(errorResponse);
|
|
2005
|
+
}
|
|
2006
|
+
/**
|
|
2007
|
+
* Parse class-validator error messages into field-specific errors
|
|
2008
|
+
*/
|
|
2009
|
+
parseValidationErrors(messages) {
|
|
2010
|
+
const errors = [];
|
|
2011
|
+
for (const msg of messages) {
|
|
2012
|
+
if (typeof msg === "string") {
|
|
2013
|
+
errors.push({
|
|
2014
|
+
field: "general",
|
|
2015
|
+
message: msg
|
|
2016
|
+
});
|
|
2017
|
+
} else if (typeof msg === "object" && msg.property && msg.constraints) {
|
|
2018
|
+
const field = msg.property;
|
|
2019
|
+
const constraintMessages = Object.values(msg.constraints);
|
|
2020
|
+
for (const constraintMsg of constraintMessages) {
|
|
2021
|
+
errors.push({
|
|
2022
|
+
field,
|
|
2023
|
+
message: constraintMsg
|
|
2024
|
+
});
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
return errors.length > 0 ? errors : [
|
|
2029
|
+
{
|
|
2030
|
+
field: "general",
|
|
2031
|
+
message: "Validation failed"
|
|
2032
|
+
}
|
|
2033
|
+
];
|
|
2034
|
+
}
|
|
2035
|
+
};
|
|
2036
|
+
HttpExceptionFilter = _ts_decorate13([
|
|
2037
|
+
(0, import_common18.Catch)()
|
|
2038
|
+
], HttpExceptionFilter);
|
|
1259
2039
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1260
2040
|
0 && (module.exports = {
|
|
1261
2041
|
AuthConfigModule,
|
|
2042
|
+
CsrfGuard,
|
|
1262
2043
|
DatabaseModule,
|
|
2044
|
+
HttpExceptionFilter,
|
|
2045
|
+
HttpModule,
|
|
1263
2046
|
Onboarding,
|
|
2047
|
+
PrimaryBaseRepository,
|
|
1264
2048
|
PrimaryDatabaseService,
|
|
1265
2049
|
Public,
|
|
1266
2050
|
Tenant,
|
|
2051
|
+
TenantBaseRepository,
|
|
1267
2052
|
TenantContextService,
|
|
1268
2053
|
TenantDatabaseService,
|
|
1269
2054
|
VrittiAuthGuard
|