@appxdigital/appx-core 0.1.90 → 0.1.92

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.
@@ -1,3 +1,3 @@
1
1
  export declare const PERMISSION_METADATA_KEY = "permission";
2
- export declare const Permission: (action: string) => import("@nestjs/common").CustomDecorator<string>;
2
+ export declare const Permission: (action: string, expose_models?: string[]) => import("@nestjs/common").CustomDecorator<string>;
3
3
  //# sourceMappingURL=permission.decorator.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"permission.decorator.d.ts","sourceRoot":"","sources":["../../../src/common/decorators/permission.decorator.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,uBAAuB,eAAe,CAAC;AAEpD,eAAO,MAAM,UAAU,GAAI,QAAQ,MAAM,qDACS,CAAC"}
1
+ {"version":3,"file":"permission.decorator.d.ts","sourceRoot":"","sources":["../../../src/common/decorators/permission.decorator.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,uBAAuB,eAAe,CAAC;AAEpD,eAAO,MAAM,UAAU,GAAI,QAAQ,MAAM,EAAE,gBAAe,MAAM,EAAO,qDACN,CAAC"}
@@ -3,5 +3,5 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Permission = exports.PERMISSION_METADATA_KEY = void 0;
4
4
  const common_1 = require("@nestjs/common");
5
5
  exports.PERMISSION_METADATA_KEY = 'permission';
6
- const Permission = (action) => (0, common_1.SetMetadata)(exports.PERMISSION_METADATA_KEY, { action });
6
+ const Permission = (action, expose_models = []) => (0, common_1.SetMetadata)(exports.PERMISSION_METADATA_KEY, { action, expose_models });
7
7
  exports.Permission = Permission;
@@ -1 +1 @@
1
- {"version":3,"file":"prisma.interceptor.d.ts","sourceRoot":"","sources":["../../../src/common/interceptors/prisma.interceptor.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,WAAW,EACX,gBAAgB,EAEhB,eAAe,EAClB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAC,aAAa,EAAC,MAAM,6BAA6B,CAAC;AAC1D,OAAO,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AACvC,OAAO,EAAC,aAAa,EAAC,MAAM,gBAAgB,CAAC;AAG7C,OAAO,EAAC,UAAU,EAAa,MAAM,MAAM,CAAC;AAI5C,qBACa,iBAAkB,YAAW,eAAe;IAIjD,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,SAAS;IACjB,OAAO,CAAC,aAAa;IALzB,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAS;gBAG1B,aAAa,EAAE,aAAa,EACrC,SAAS,EAAE,SAAS,EACpB,aAAa,EAAE,aAAa;IAKxC,SAAS,CAAC,OAAO,EAAE,gBAAgB,EAAE,IAAI,EAAE,WAAW,GAAG,UAAU,CAAC,GAAG,CAAC;CAqD3E"}
1
+ {"version":3,"file":"prisma.interceptor.d.ts","sourceRoot":"","sources":["../../../src/common/interceptors/prisma.interceptor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,WAAW,EAAE,gBAAgB,EAAc,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAC3F,OAAO,EAAC,aAAa,EAAC,MAAM,6BAA6B,CAAC;AAC1D,OAAO,EAAC,SAAS,EAAC,MAAM,cAAc,CAAC;AACvC,OAAO,EAAC,aAAa,EAAC,MAAM,gBAAgB,CAAC;AAG7C,OAAO,EAAC,UAAU,EAAa,MAAM,MAAM,CAAC;AAK5C,qBACa,iBAAkB,YAAW,eAAe;IAIjD,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,SAAS;IACjB,OAAO,CAAC,aAAa;IALzB,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAS;gBAG1B,aAAa,EAAE,aAAa,EACrC,SAAS,EAAE,SAAS,EACpB,aAAa,EAAE,aAAa;IAKxC,SAAS,CAAC,OAAO,EAAE,gBAAgB,EAAE,IAAI,EAAE,WAAW,GAAG,UAAU,CAAC,GAAG,CAAC;CAyD3E"}
@@ -18,6 +18,7 @@ const nestjs_request_context_1 = require("nestjs-request-context");
18
18
  const operators_1 = require("rxjs/operators");
19
19
  const rxjs_1 = require("rxjs");
20
20
  const error_handler_1 = require("../utils/error-handler");
21
+ const permission_decorator_1 = require("../decorators/permission.decorator");
21
22
  let PrismaInterceptor = class PrismaInterceptor {
22
23
  constructor(prismaService, reflector, configService) {
23
24
  this.prismaService = prismaService;
@@ -30,6 +31,9 @@ let PrismaInterceptor = class PrismaInterceptor {
30
31
  const req = handlerType === 'graphql'
31
32
  ? context.getArgByIndex(2) // GraphQL context
32
33
  : context.switchToHttp().getRequest(); // HTTP context
34
+ // Attach expose_models metadata if needed
35
+ const permissionMetadata = this.reflector.get(permission_decorator_1.PERMISSION_METADATA_KEY, context.getHandler()) || {};
36
+ nestjs_request_context_1.RequestContext.currentContext.req.prismaExposedModels = permissionMetadata['expose_models'] || [];
33
37
  const useTransaction = this.reflector.get('useTransaction', context.getHandler()) ?? this.defaultUseTransaction === 'true';
34
38
  if (useTransaction) {
35
39
  return new rxjs_1.Observable((observer) => {
@@ -1 +1 @@
1
- {"version":3,"file":"generic.resolver.d.ts","sourceRoot":"","sources":["../../src/graphql/generic.resolver.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,IAAI,EAAC,MAAM,iBAAiB,CAAC;AAErC,OAAO,EAAC,kBAAkB,EAAC,MAAM,SAAS,CAAC;AAE3C,OAAO,EAAC,aAAa,EAAC,MAAM,0BAA0B,CAAC;AAGvD,wBAAgB,sBAAsB,CAAC,SAAS,EAC5C,WAAW,EACX,WAAW,EACX,UAAU,EACV,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,EAClB,eAAe,EAEf,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,EAC3B,eAAe,EAAE,IAAI,CAAC,WAAW,CAAC,EAClC,eAAe,EAAE,IAAI,CAAC,WAAW,CAAC,EAClC,cAAc,EAAE,IAAI,CAAC,UAAU,CAAC,EAChC,oBAAoB,EAAE,IAAI,CAAC,gBAAgB,CAAC,EAC5C,gBAAgB,EAAE,IAAI,CAAC,gBAAgB,CAAC,EACxC,iBAAiB,EAAE,IAAI,CAAC,iBAAiB,CAAC,EAC1C,mBAAmB,EAAE,IAAI,CAAC,mBAAmB,CAAC,EAC9C,kBAAkB,EAAE,IAAI,CAAC,kBAAkB,CAAC,EAC5C,mBAAmB,EAAE,IAAI,CAAC,eAAe,CAAC,EAC1C,cAAc,CAAC,EAAE;IAAC,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;CAAC;iBAIF,aAAa;yBAAb,aAAa;sBAWnC,gBAAgB,QACZ,kBAAkB,GACjC,OAAO,CAAC,SAAS,EAAE,CAAC;uBAcR,gBAAgB,QACb,kBAAkB,GACjC,OAAO,CAAC,SAAS,CAAC;wBAcP,gBAAgB,QACZ,kBAAkB,GACjC,OAAO,CAAC,SAAS,CAAC;wBAcP,iBAAiB,QACb,kBAAkB,GACjC,OAAO,CAAC,mBAAmB,CAAC;;EA0ItC"}
1
+ {"version":3,"file":"generic.resolver.d.ts","sourceRoot":"","sources":["../../src/graphql/generic.resolver.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,IAAI,EAAC,MAAM,iBAAiB,CAAC;AAErC,OAAO,EAAC,kBAAkB,EAAC,MAAM,SAAS,CAAC;AAE3C,OAAO,EAAC,aAAa,EAAC,MAAM,0BAA0B,CAAC;AAEvD,wBAAgB,sBAAsB,CAAC,SAAS,EAC5C,WAAW,EACX,WAAW,EACX,UAAU,EACV,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,EAClB,eAAe,EAEf,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,EAC3B,eAAe,EAAE,IAAI,CAAC,WAAW,CAAC,EAClC,eAAe,EAAE,IAAI,CAAC,WAAW,CAAC,EAClC,cAAc,EAAE,IAAI,CAAC,UAAU,CAAC,EAChC,oBAAoB,EAAE,IAAI,CAAC,gBAAgB,CAAC,EAC5C,gBAAgB,EAAE,IAAI,CAAC,gBAAgB,CAAC,EACxC,iBAAiB,EAAE,IAAI,CAAC,iBAAiB,CAAC,EAC1C,mBAAmB,EAAE,IAAI,CAAC,mBAAmB,CAAC,EAC9C,kBAAkB,EAAE,IAAI,CAAC,kBAAkB,CAAC,EAC5C,mBAAmB,EAAE,IAAI,CAAC,eAAe,CAAC,EAC1C,cAAc,CAAC,EAAE;IAAC,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;CAAC;iBAIF,aAAa;yBAAb,aAAa;sBAWnC,gBAAgB,QACZ,kBAAkB,GACjC,OAAO,CAAC,SAAS,EAAE,CAAC;uBAcR,gBAAgB,QACb,kBAAkB,GACjC,OAAO,CAAC,SAAS,CAAC;wBAcP,gBAAgB,QACZ,kBAAkB,GACjC,OAAO,CAAC,SAAS,CAAC;wBAcP,iBAAiB,QACb,kBAAkB,GACjC,OAAO,CAAC,mBAAmB,CAAC;;EA0ItC"}
@@ -1 +1 @@
1
- {"version":3,"file":"auth.service.d.ts","sourceRoot":"","sources":["../../../src/modules/auth/auth.service.ts"],"names":[],"mappings":"AAOA,OAAO,EAAC,WAAW,EAAC,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAC,WAAW,EAAC,MAAM,oBAAoB,CAAC;AAE/C,OAAO,EAAC,aAAa,EAAC,MAAM,6BAA6B,CAAC;AAC1D,OAAO,EAAC,aAAa,EAAC,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAC,OAAO,EAAE,QAAQ,EAAC,MAAM,SAAS,CAAC;AAC1C,OAAO,EAAC,UAAU,EAAC,MAAM,aAAa,CAAC;AAGvC,OAAO,EAAC,IAAI,EAAC,MAAM,gBAAgB,CAAC;AAEpC,qBACa,WAAW;IAIhB,SAAS,CAAC,QAAQ,CAAC,WAAW,EAAE,WAAW;IAC3C,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,aAAa;IACxC,SAAS,CAAC,QAAQ,CAAC,UAAU,EAAE,UAAU;IACzC,SAAS,CAAC,QAAQ,CAAC,aAAa,EAAE,aAAa;IANnD,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;gBAGpB,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,aAAa,EACrB,UAAU,EAAE,UAAU,EACtB,aAAa,EAAE,aAAa;IAK7C,QAAQ,CAAC,WAAW,EAAE,WAAW;;cAaE,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC;;IAWzD,KAAK,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,GAAG,CAAA;KAAC,CAAC;IA8B1D,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBlD,YAAY,CACd,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,GACtB,OAAO,CAAC,GAAG,CAAC;IAuBT,cAAc,CAAC,GAAG,EAAE,OAAO;IAW3B,iBAAiB,CAAC,GAAG,EAAE,OAAO;IAW9B,oBAAoB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ;IA0BhD,mBAAmB,CAAC,MAAM,EAAE,MAAM;IAMlC,oBAAoB,CAAC,SAAS,EAAE,MAAM;IAOtC,kBAAkB,CAAC,OAAO,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;YAQtC,cAAc;IAkCtB,aAAa,CAAC,yBAAyB,EAAE,GAAG,GAAG,OAAO,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAC,CAAC;IAuB3G,OAAO,CAAC,WAAW;IAUb,QAAQ,CAAC,iBAAiB,EAAE,GAAG,GAAG,OAAO,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,GAAG,CAAA;KAAC,CAAC;IAUnG,0BAA0B,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAUlE"}
1
+ {"version":3,"file":"auth.service.d.ts","sourceRoot":"","sources":["../../../src/modules/auth/auth.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,WAAW,EAAC,MAAM,sBAAsB,CAAC;AACjD,OAAO,EAAC,WAAW,EAAC,MAAM,oBAAoB,CAAC;AAE/C,OAAO,EAAC,aAAa,EAAC,MAAM,6BAA6B,CAAC;AAC1D,OAAO,EAAC,aAAa,EAAC,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAC,OAAO,EAAE,QAAQ,EAAC,MAAM,SAAS,CAAC;AAC1C,OAAO,EAAC,UAAU,EAAC,MAAM,aAAa,CAAC;AAGvC,OAAO,EAAC,IAAI,EAAC,MAAM,gBAAgB,CAAC;AAEpC,qBACa,WAAW;IAIhB,SAAS,CAAC,QAAQ,CAAC,WAAW,EAAE,WAAW;IAC3C,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,aAAa;IACxC,SAAS,CAAC,QAAQ,CAAC,UAAU,EAAE,UAAU;IACzC,SAAS,CAAC,QAAQ,CAAC,aAAa,EAAE,aAAa;IANnD,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;gBAGpB,WAAW,EAAE,WAAW,EACxB,MAAM,EAAE,aAAa,EACrB,UAAU,EAAE,UAAU,EACtB,aAAa,EAAE,aAAa;IAK7C,QAAQ,CAAC,WAAW,EAAE,WAAW;;cAgBE,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC;;IAWzD,KAAK,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,GAAG,CAAA;KAAC,CAAC;IA8B1D,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBlD,YAAY,CACd,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,aAAa,EAAE,MAAM,GACtB,OAAO,CAAC,GAAG,CAAC;IAuBT,cAAc,CAAC,GAAG,EAAE,OAAO;IAW3B,iBAAiB,CAAC,GAAG,EAAE,OAAO;IAW9B,oBAAoB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,QAAQ;IA0BhD,mBAAmB,CAAC,MAAM,EAAE,MAAM;IAMlC,oBAAoB,CAAC,SAAS,EAAE,MAAM;IAOtC,kBAAkB,CAAC,OAAO,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;YAQtC,cAAc;IAkCtB,aAAa,CAAC,yBAAyB,EAAE,GAAG,GAAG,OAAO,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAA;KAAC,CAAC;IAuB3G,OAAO,CAAC,WAAW;IAUb,QAAQ,CAAC,iBAAiB,EAAE,GAAG,GAAG,OAAO,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,GAAG,CAAA;KAAC,CAAC;IAUnG,0BAA0B,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAUlE"}
@@ -59,7 +59,7 @@ let AuthService = class AuthService {
59
59
  this.sessionCookieName = this.configService.get('SESSION_COOKIE_NAME', 'defaultCookieName');
60
60
  }
61
61
  async register(registerDto) {
62
- const existingUser = await this.userService.findByEmail(registerDto.email);
62
+ const existingUser = await this.prisma.user.findFirst({ where: { email: registerDto.email } }, { BYPASS_FILTERING: true });
63
63
  if (existingUser) {
64
64
  throw new common_1.HttpException('Email already in use', common_1.HttpStatus.CONFLICT);
65
65
  }
@@ -1 +1 @@
1
- {"version":3,"file":"refresh-token.strategy.d.ts","sourceRoot":"","sources":["../../../src/modules/auth/refresh-token.strategy.ts"],"names":[],"mappings":"AAEA,OAAO,EAAC,QAAQ,EAAa,MAAM,cAAc,CAAC;AAClD,OAAO,EAAC,aAAa,EAAC,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAC,OAAO,EAAC,MAAM,SAAS,CAAC;AAChC,OAAO,EAAC,aAAa,EAAC,MAAM,6BAA6B,CAAC;AAC1D,OAAO,EAAC,WAAW,EAAC,MAAM,sBAAsB,CAAC;;;;AAEjD,qBACa,oBAAqB,SAAQ,yBAAyC;IAE3E,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,WAAW;gBAFX,aAAa,EAAE,aAAa,EAC5B,MAAM,EAAE,aAAa,EACrB,WAAW,EAAE,WAAW;IAcvC,QAAQ,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG;CA6B5C"}
1
+ {"version":3,"file":"refresh-token.strategy.d.ts","sourceRoot":"","sources":["../../../src/modules/auth/refresh-token.strategy.ts"],"names":[],"mappings":"AAEA,OAAO,EAAa,QAAQ,EAAC,MAAM,cAAc,CAAC;AAClD,OAAO,EAAC,aAAa,EAAC,MAAM,gBAAgB,CAAC;AAC7C,OAAO,EAAC,OAAO,EAAC,MAAM,SAAS,CAAC;AAChC,OAAO,EAAC,aAAa,EAAC,MAAM,6BAA6B,CAAC;AAC1D,OAAO,EAAC,WAAW,EAAC,MAAM,sBAAsB,CAAC;;;;AAEjD,qBACa,oBAAqB,SAAQ,yBAAyC;IAE3E,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,WAAW;gBAFX,aAAa,EAAE,aAAa,EAC5B,MAAM,EAAE,aAAa,EACrB,WAAW,EAAE,WAAW;IAcvC,QAAQ,CAAC,GAAG,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG;CA6B5C"}
@@ -7,11 +7,14 @@ export type CorePrismaOptions = {
7
7
  BYPASS_OMISSION?: boolean;
8
8
  BYPASS_FILTERING?: boolean;
9
9
  };
10
+ export type CorePrismaModel<M extends ModelKey> = Exclude<RuntimeClient[M], 'findUnique' | 'findUniqueOrThrow' | 'delete' | 'update'>;
10
11
  export declare class PrismaService {
11
12
  private readonly permissionsConfig;
12
13
  private fieldConfigs;
13
14
  prismaClient: PrismaClient;
14
15
  constructor(prismaClient: PrismaClient, permissionsConfig: PermissionsConfigType);
16
+ debugQueries(enable: boolean): void;
17
+ private debug;
15
18
  $transaction<T>(fn: (prisma: Prisma.TransactionClient) => Promise<T>): any;
16
19
  proxyModels(): void;
17
20
  /**
@@ -19,10 +22,10 @@ export declare class PrismaService {
19
22
  * Used in the graphql generic resolver
20
23
  * @param model
21
24
  */
22
- getModelDelegate<M extends ModelKey>(model: M): RuntimeClient[M];
25
+ getModelDelegate<M extends ModelKey>(model: M): CorePrismaModel<M>;
23
26
  get model(): RuntimeClient;
24
- get user(): RuntimeClient['user'];
25
- get session(): RuntimeClient['session'];
27
+ get user(): CorePrismaModel<'user'>;
28
+ get session(): CorePrismaModel<'session'>;
26
29
  get userRefreshToken(): any;
27
30
  /**
28
31
  * Parses the Prisma schema to extract model information and field configurations
@@ -53,7 +56,6 @@ export declare class PrismaService {
53
56
  * @throws ForbiddenException if no valid `where` conditions are present after applying permissions.
54
57
  */
55
58
  private applyWhereConditions;
56
- getRelationType(parentModel: string, relatedField: string): any;
57
59
  /**
58
60
  * Builds dynamic conditions based on the type of clause (OR, AND, or field conditions).
59
61
  * Each condition is processed, and placeholders (like `$USER_ID`) are replaced with actual values.
@@ -92,16 +94,13 @@ export declare class PrismaService {
92
94
  * @returns A `select` object for Prisma queries.
93
95
  */
94
96
  private buildSelectFields;
95
- /**
96
- * Retrieves the related model's name for a given relation field.
97
- * This is used to navigate relations between models when constructing the `select` object.
98
- *
99
- * @param parentModelName - The name of the parent model.
100
- * @param relationKey - The key of the relation field.
101
- * @returns The name of the related model.
102
- * @throws Error if the relation field is not found in the parent model.
103
- */
104
- private getRelatedModelName;
97
+ getRelation(parentModel: string, relatedField: string, throwOnNotFound?: true): {
98
+ model: string;
99
+ relation: string;
100
+ foreignKey?: string;
101
+ referencingColumn?: string;
102
+ };
103
+ selectPermission(permissions: any, action: string, modelName: string, userRole: string): any;
105
104
  }
106
105
  export {};
107
106
  //# sourceMappingURL=prisma.service.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"prisma.service.d.ts","sourceRoot":"","sources":["../../src/prisma/prisma.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,MAAM,EAAE,YAAY,EAAC,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAC,qBAAqB,EAAC,MAAM,yCAAyC,CAAC;AAE9E,OAAO,KAAK,EAAC,YAAY,IAAI,aAAa,EAAC,MAAM,gBAAgB,CAAC;AAGlE,KAAK,QAAQ,GAAG,MAAM,aAAa,CAAC;AAEpC,oDAAoD;AACpD,MAAM,MAAM,iBAAiB,GAAG;IAC5B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC9B,CAAC;AAEF,qBACa,aAAa;IAMY,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IALpE,OAAO,CAAC,YAAY,CAA2B;IAC/C,YAAY,EAAE,YAAY,CAAC;gBAGvB,YAAY,EAAE,YAAY,EACqB,iBAAiB,EAAE,qBAAqB;IAO3F,YAAY,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,iBAAiB,KAAK,OAAO,CAAC,CAAC,CAAC;IAIpE,WAAW;IAgDX;;;;OAIG;IACH,gBAAgB,CAAC,CAAC,SAAS,QAAQ,EAAE,KAAK,EAAE,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC;IAYhE,IAAI,KAAK,IAAI,aAAa,CAUzB;IAED,IAAI,IAAI,IAAI,aAAa,CAAC,MAAM,CAAC,CAEhC;IAED,IAAI,OAAO,IAAI,aAAa,CAAC,SAAS,CAAC,CAEtC;IAED,IAAI,gBAAgB,QAEnB;IAED;;;OAGG;IACH,WAAW;IAoDX;;;;;;;;OAQG;IACH,OAAO,CAAC,kBAAkB;IAiC1B,IAAI,2BAA2B,QAM9B;IAED;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,oBAAoB;IAqG5B,eAAe,CAAC,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM;IAYzD;;;;;;;OAOG;IACH,OAAO,CAAC,eAAe;IAgBvB;;;;;;;OAOG;IACH,OAAO,CAAC,mBAAmB;IA2B3B;;;;;;;OAOG;IACH,OAAO,CAAC,eAAe;IAQvB;;;;;;;;;OASG;IACH,OAAO,CAAC,iBAAiB;IAkDzB;;;;;;;;OAQG;IACH,OAAO,CAAC,mBAAmB;CAgB9B"}
1
+ {"version":3,"file":"prisma.service.d.ts","sourceRoot":"","sources":["../../src/prisma/prisma.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,MAAM,EAAE,YAAY,EAAC,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAC,qBAAqB,EAAC,MAAM,yCAAyC,CAAC;AAE9E,OAAO,KAAK,EAAC,YAAY,IAAI,aAAa,EAAC,MAAM,gBAAgB,CAAC;AAGlE,KAAK,QAAQ,GAAG,MAAM,aAAa,CAAC;AAEpC,oDAAoD;AACpD,MAAM,MAAM,iBAAiB,GAAG;IAC5B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC9B,CAAC;AAGF,MAAM,MAAM,eAAe,CAAC,CAAC,SAAS,QAAQ,IAAI,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,YAAY,GAAG,mBAAmB,GAAG,QAAQ,GAAG,QAAQ,CAAC,CAAC;AAEtI,qBACa,aAAa;IAMY,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IALpE,OAAO,CAAC,YAAY,CAA2B;IAC/C,YAAY,EAAE,YAAY,CAAC;gBAGvB,YAAY,EAAE,YAAY,EACqB,iBAAiB,EAAE,qBAAqB;IAO3F,YAAY,CAAC,MAAM,EAAE,OAAO;IAI5B,OAAO,CAAC,KAAK;IAQb,YAAY,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,iBAAiB,KAAK,OAAO,CAAC,CAAC,CAAC;IAIpE,WAAW;IAuDX;;;;OAIG;IACH,gBAAgB,CAAC,CAAC,SAAS,QAAQ,EAAE,KAAK,EAAE,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC;IAYlE,IAAI,KAAK,IAAI,aAAa,CAUzB;IAED,IAAI,IAAI,IAAI,eAAe,CAAC,MAAM,CAAC,CAElC;IAED,IAAI,OAAO,IAAI,eAAe,CAAC,SAAS,CAAC,CAExC;IAED,IAAI,gBAAgB,QAEnB;IAED;;;OAGG;IACH,WAAW;IAoDX;;;;;;;;OAQG;IACH,OAAO,CAAC,kBAAkB;IAmC1B,IAAI,2BAA2B,QAM9B;IAED;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,oBAAoB;IAwI5B;;;;;;;OAOG;IACH,OAAO,CAAC,eAAe;IAgBvB;;;;;;;OAOG;IACH,OAAO,CAAC,mBAAmB;IA2B3B;;;;;;;OAOG;IACH,OAAO,CAAC,eAAe;IAUvB;;;;;;;;;OASG;IACH,OAAO,CAAC,iBAAiB;IAkDzB,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,eAAe,CAAC,EAAE,IAAI,GAAG;QAC5E,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,iBAAiB,CAAC,EAAE,MAAM,CAAC;KAC9B;IAkBD,gBAAgB,CAAC,WAAW,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;CAgBzF"}
@@ -24,6 +24,16 @@ let PrismaService = class PrismaService {
24
24
  this.parseSchema();
25
25
  this.proxyModels();
26
26
  }
27
+ debugQueries(enable) {
28
+ nestjs_request_context_1.RequestContext.currentContext.req.corePrismaDebug = enable;
29
+ }
30
+ debug(msg, type = 'log') {
31
+ if (nestjs_request_context_1.RequestContext.currentContext?.req.corePrismaDebug) {
32
+ // Default for log, yellow for warn, red for error, blue for info
33
+ const color = type === 'log' ? '' : type === 'warn' ? '\x1b[33m' : type === 'error' ? '\x1b[31m' : '\x1b[34m';
34
+ console.debug(color, `[APPX-CORE PRISMA] ${msg}`, '\x1b[0m');
35
+ }
36
+ }
27
37
  $transaction(fn) {
28
38
  return this.prismaClient.$transaction(fn);
29
39
  }
@@ -39,6 +49,7 @@ let PrismaService = class PrismaService {
39
49
  const user = nestjs_request_context_1.RequestContext.currentContext?.req.user || {};
40
50
  const userRole = user?.role || 'GUEST';
41
51
  if (typeof model[methodKey] === 'function' && !methodKey.toString().startsWith('_') && !methodKey.toString().startsWith('$')) {
52
+ this.debug(`Proxying ${String(propKey)}.${String(methodKey)}() for role ${userRole}`);
42
53
  const contextModel = nestjs_request_context_1.RequestContext.currentContext?.req.prisma?.[propKey] || model;
43
54
  return async (params = {}, options) => {
44
55
  // Blacklisted methods, should not be used to ensure permission filtering is applied
@@ -52,14 +63,22 @@ let PrismaService = class PrismaService {
52
63
  throw new Error(`The method ${methodKey.toString()} is not compatible with permission filtering and is not allowed. Please use ${blacklist[methodKey.toString()]}} instead.`);
53
64
  }
54
65
  // delete, deleteMany, update and updateMany methods should not apply field omission because they are not selecting fields
55
- if (!options?.BYPASS_OMISSION && !['delete', 'deleteMany', 'update', 'updateMany', 'create', 'createMany'].includes(methodKey.toString()))
66
+ if (!options?.BYPASS_OMISSION && !['delete', 'deleteMany', 'update', 'updateMany', 'create', 'createMany'].includes(methodKey.toString())) {
56
67
  params = this.applyFieldOmission(String(propKey), userRole, params);
68
+ }
69
+ else {
70
+ this.debug(`Skipping field omission for ${String(propKey)}.${String(methodKey)}()`);
71
+ }
57
72
  if (!options?.BYPASS_FILTERING && !['create', 'createMany'].includes(methodKey.toString())) {
58
73
  params = this.applyWhereConditions(String(propKey), userRole, params, user, methodKey);
59
74
  }
75
+ else {
76
+ this.debug(`Skipping permission filtering for ${String(propKey)}.${String(methodKey)}()`);
77
+ }
60
78
  if (methodKey === 'count' && !!params.select) {
61
79
  delete params.select;
62
80
  }
81
+ this.debug(`Executing ${String(propKey)}.${String(methodKey)}() with params: ${JSON.stringify(params)}`);
63
82
  return contextModel[methodKey](params);
64
83
  };
65
84
  }
@@ -163,6 +182,7 @@ let PrismaService = class PrismaService {
163
182
  * @returns The modified query arguments with omitted fields.
164
183
  */
165
184
  applyFieldOmission(modelName, userRole, args) {
185
+ this.debug(`Applying field omission for model ${modelName} and role ${userRole}`);
166
186
  const omitFields = this.getFieldsToOmit(modelName, userRole);
167
187
  if (!args) {
168
188
  args = {};
@@ -173,6 +193,7 @@ let PrismaService = class PrismaService {
173
193
  });
174
194
  }
175
195
  else if (args.include) {
196
+ this.debug(`Found included model '${modelName}', generating select fields`);
176
197
  args.select = this.buildSelectFields(modelName, omitFields, args.include, userRole);
177
198
  delete args.include;
178
199
  }
@@ -208,14 +229,44 @@ let PrismaService = class PrismaService {
208
229
  const permissionsConfig = this.normalizedPermissionsConfig;
209
230
  const normalizedName = modelName.toLowerCase().trim();
210
231
  const permissions = permissionsConfig[normalizedName]?.[userRole];
232
+ let actionPermissions;
233
+ // If model is exposed, permissions is ALL
234
+ if (nestjs_request_context_1.RequestContext.currentContext.req.prismaExposedModels.map((m) => m.toLowerCase()).includes(modelName.toLowerCase())) {
235
+ actionPermissions = 'ALL';
236
+ }
237
+ else {
238
+ if (!permissions) {
239
+ throw new common_1.HttpException(`No permissions found for model '${modelName}' and role ${userRole}`, common_1.HttpStatus.FORBIDDEN);
240
+ }
241
+ actionPermissions = this.selectPermission(permissions, action.toString(), modelName, userRole);
242
+ if (!actionPermissions) {
243
+ console.debug(`No permissions found for action '${modelName}.${String(action)}()' on role ${userRole}`);
244
+ throw new common_1.HttpException('Missing permissions on model ' + modelName, common_1.HttpStatus.FORBIDDEN);
245
+ }
246
+ }
211
247
  if (args.select) {
212
248
  for (const field of Object.keys(args.select)) {
213
- let relation = this.getRelationType(modelName, field);
249
+ // If field is relation
250
+ let relation = this.getRelation(modelName, field);
214
251
  if (!relation) {
215
252
  continue;
216
253
  }
217
254
  if (relation.relation === 'belongsTo') {
218
- const relatedPermissions = permissionsConfig[relation.model.toLowerCase()]?.[userRole]?.[action];
255
+ this.debug(`Found 1:1 / *:1 (belongsTo) relation to model '${relation.model}' from model '${modelName}' via field '${field}'. Filter will be applied to main conditions...`);
256
+ const relatedPermissions = this.selectPermission(permissionsConfig[relation.model.toLowerCase()]?.[userRole] || {}, action.toString(), relation.model, userRole);
257
+ // If model is exposed, do not apply conditions
258
+ if (nestjs_request_context_1.RequestContext.currentContext.req.prismaExposedModels.map((m) => m.toLowerCase()).includes(relation.model.toLowerCase())) {
259
+ this.debug(`Related model '${relation.model}' is exposed via @Permission() decorator. Skipping conditions for action '${String(action)}' on role ${userRole}.`);
260
+ continue;
261
+ }
262
+ if (!relatedPermissions) {
263
+ console.debug(`No permissions found for action '${relation.model}.${String(action)}()' on role ${userRole}`);
264
+ throw new common_1.HttpException('Missing permissions on model ' + relation.model, common_1.HttpStatus.FORBIDDEN);
265
+ }
266
+ if (relatedPermissions === 'ALL') {
267
+ this.debug(`Related model '${relation.model}' has 'ALL' permissions for action '${String(action)}' on role ${userRole}. No conditions to apply.`);
268
+ continue;
269
+ }
219
270
  belongsToQueue.push({
220
271
  field,
221
272
  relation,
@@ -223,54 +274,33 @@ let PrismaService = class PrismaService {
223
274
  });
224
275
  continue;
225
276
  }
226
- if (permissionsConfig[relation.model.toLowerCase()]) {
227
- /* Where conditions are applied outside of the select. Example as per documentation:
228
- const result = await prisma.user.findFirst({
277
+ this.debug(`Found 1:N / N:N (hasMany) relation to model '${relation.model}' from model '${modelName}' via field '${field}'. Applying filter to the relation...`);
278
+ /* Where conditions are applied outside of the select. Example as per documentation:
279
+ const result = await prisma.user.findFirst({
280
+ select: {
281
+ posts: {
282
+ where: {
283
+ published: false,
284
+ },
229
285
  select: {
230
- posts: {
231
- where: {
232
- published: false,
233
- },
234
- select: {
235
- title: true,
236
- },
237
- },
286
+ title: true,
238
287
  },
239
- })
240
- */
241
- args.select[field].where = this.applyWhereConditions(relation.model, userRole, args.select[field], user, action).where;
242
- delete args.select[field].select.where;
243
- }
244
- else {
245
- throw new common_1.ForbiddenException(`No permissions found for model ${relation.model} and role ${userRole}`);
246
- }
288
+ },
289
+ },
290
+ })
291
+ */
292
+ args.select[field].where = this.applyWhereConditions(relation.model, userRole, args.select[field], user, action).where;
293
+ delete args.select[field].select.where;
247
294
  }
248
295
  }
249
- if (!permissions) {
250
- throw new common_1.HttpException(`No permissions found for model ${modelName} and role ${userRole}`, common_1.HttpStatus.FORBIDDEN);
251
- }
252
- const actionPermissions = permissions[action];
253
- if (!actionPermissions) {
254
- throw new common_1.HttpException(`No permissions found for action ${String(action)} on model ${modelName} and role ${userRole}`, common_1.HttpStatus.FORBIDDEN);
255
- }
256
- if (actionPermissions === 'ALL' && belongsToQueue.length === 0 || action.toString().startsWith('create')) {
257
- return args;
258
- }
259
- const whereClause = this.buildConditions(actionPermissions.conditions, user);
260
- if (!args.where) {
261
- args.where = whereClause;
262
- }
263
- else {
264
- args.where = { AND: [args.where, whereClause] };
265
- }
266
- //TODO Test this in more scenarios, it may need to be more robust, it's a quick fix but I must make sure it wont be abused by adding a model with this type of relation to bypass something it shouldn't
267
- if (Object.keys(args.where).length === 0 && !belongsToQueue?.length) {
268
- throw new common_1.ForbiddenException(`You are not authorized to access this record`);
296
+ if (belongsToQueue?.length > 0) {
297
+ this.debug(`Merging belongsTo relation conditions into main ${modelName}.`, 'warn');
269
298
  }
270
299
  for (const entry of belongsToQueue) {
271
300
  const { relatedPermissions, field } = entry;
301
+ this.debug(`Merging conditions for belongsTo relation field '${field}': ${JSON.stringify(relatedPermissions?.conditions)}`, 'info');
272
302
  args.where = {
273
- ...args.where,
303
+ ...(args.where || {}),
274
304
  [field]: {
275
305
  AND: [
276
306
  args?.where?.[field] ?? {},
@@ -279,17 +309,25 @@ let PrismaService = class PrismaService {
279
309
  }
280
310
  };
281
311
  }
312
+ // If model is exposed, do not apply conditions
313
+ if (nestjs_request_context_1.RequestContext.currentContext.req.prismaExposedModels.map((m) => m.toLowerCase()).includes(modelName.toLowerCase())) {
314
+ this.debug(`Model '${modelName}' is exposed via @Permission() decorator. Skipping conditions for action '${String(action)}' on role ${userRole}.`);
315
+ return args;
316
+ }
317
+ if (actionPermissions === 'ALL') {
318
+ this.debug(`No conditions to apply for '${modelName}.${String(action)}()' on role ${userRole}`);
319
+ return args;
320
+ }
321
+ const whereClause = this.buildConditions(actionPermissions.conditions, user);
322
+ this.debug(`Applying where conditions for '${modelName}.${String(action)}()' on role ${userRole}: ${JSON.stringify(whereClause)}`);
323
+ if (!args.where) {
324
+ args.where = whereClause;
325
+ }
326
+ else {
327
+ args.where = { AND: [args.where, whereClause] };
328
+ }
282
329
  return args;
283
330
  }
284
- getRelationType(parentModel, relatedField) {
285
- parentModel = parentModel.toLowerCase();
286
- relatedField = relatedField.toLowerCase();
287
- const parent = this.fieldConfigs[parentModel];
288
- const relation = parent.relationFields[relatedField];
289
- if (!relation)
290
- return null;
291
- return relation;
292
- }
293
331
  /**
294
332
  * Builds dynamic conditions based on the type of clause (OR, AND, or field conditions).
295
333
  * Each condition is processed, and placeholders (like `$USER_ID`) are replaced with actual values.
@@ -349,9 +387,11 @@ let PrismaService = class PrismaService {
349
387
  getFieldsToOmit(modelName, role) {
350
388
  const modelInfo = this.fieldConfigs[modelName.toLowerCase()] || {};
351
389
  const fieldConfig = modelInfo.fieldConfig || {};
352
- return Object.entries(fieldConfig)
390
+ const omitFields = Object.entries(fieldConfig)
353
391
  .filter(([_, roles]) => !roles.includes(role))
354
392
  .map(([field]) => field);
393
+ this.debug(`Fields to omit on '${modelName}', based on schema @Role() configuration: ${omitFields.join(', ')}`);
394
+ return omitFields;
355
395
  }
356
396
  /**
357
397
  * Builds the `select` object for Prisma queries, omitting fields based on the user's role.
@@ -383,7 +423,8 @@ let PrismaService = class PrismaService {
383
423
  if (includedArgs === true) {
384
424
  includedArgs = {};
385
425
  }
386
- const relatedModelName = this.getRelatedModelName(modelName, relationKey);
426
+ const relatedModelName = this.getRelation(modelName, relationKey, true).model;
427
+ this.debug(`Found relation to model '${relatedModelName}' from model '${modelName}' via field '${relationKey}'. Generating select fields...`);
387
428
  const relatedModelOmitFields = this.getFieldsToOmit(relatedModelName, userRole);
388
429
  const relatedSelectFields = this.buildSelectFields(relatedModelName, relatedModelOmitFields, includedArgs.include || null, userRole);
389
430
  if (Object.keys(relatedSelectFields).length > 0) {
@@ -393,24 +434,28 @@ let PrismaService = class PrismaService {
393
434
  }
394
435
  return selectFields;
395
436
  }
396
- /**
397
- * Retrieves the related model's name for a given relation field.
398
- * This is used to navigate relations between models when constructing the `select` object.
399
- *
400
- * @param parentModelName - The name of the parent model.
401
- * @param relationKey - The key of the relation field.
402
- * @returns The name of the related model.
403
- * @throws Error if the relation field is not found in the parent model.
404
- */
405
- getRelatedModelName(parentModelName, relationKey) {
406
- const modelInfo = this.fieldConfigs[parentModelName.toLowerCase()];
407
- if (!modelInfo) {
408
- throw new Error(`Model information not found for ${parentModelName}`);
409
- }
410
- if (modelInfo.relationFields[relationKey]) {
411
- return modelInfo.relationFields[relationKey].model;
437
+ getRelation(parentModel, relatedField, throwOnNotFound = false) {
438
+ parentModel = parentModel.toLowerCase();
439
+ const parent = this.fieldConfigs[parentModel];
440
+ const relation = parent.relationFields[relatedField];
441
+ if (!relation && throwOnNotFound)
442
+ throw new Error(`Relation key ${relatedField} not found in model ${parentModel}`);
443
+ return relation;
444
+ }
445
+ selectPermission(permissions, action, modelName, userRole) {
446
+ let actionPermissions = permissions[action];
447
+ // If action is find* or count and there is no permission, coalesce to one of the find actions
448
+ if (!actionPermissions && (action.startsWith('find') || action === 'count')) {
449
+ if (permissions['findMany']) {
450
+ this.debug(`Using 'findMany' permissions for '${action}' action on model '${modelName}' and role ${userRole}`, 'info');
451
+ actionPermissions = permissions['findMany'];
452
+ }
453
+ else if (permissions['findFirst']) {
454
+ this.debug(`Using 'findFirst' permissions for '${action}' action on model '${modelName}' and role ${userRole}`, 'info');
455
+ actionPermissions = permissions['findFirst'];
456
+ }
412
457
  }
413
- throw new Error(`Relation key ${relationKey} not found in model ${parentModelName}`);
458
+ return actionPermissions;
414
459
  }
415
460
  };
416
461
  exports.PrismaService = PrismaService;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appxdigital/appx-core",
3
- "version": "0.1.90",
3
+ "version": "0.1.92",
4
4
  "description": "Appx Core is a library that provides a set of tools to help you build your application faster.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",