@appxdigital/appx-core 0.1.89 → 0.1.91

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,2 +1,2 @@
1
- export declare const adminJsModuleCode = "\nimport {DynamicModule} from '@nestjs/common';\nimport {addBasicFilters, createActions, createPermissionHandler, dynamicImport, getAdminJSResources} from './utils';\nimport {initializeComponents} from './component-loader';\nimport {readFileSync} from 'fs';\nimport {getDMMF} from '@prisma/sdk';\nimport {PrismaService} from '@appxdigital/appx-core';\nimport {PrismaModule} from \"../prisma/prisma.module\";\n\nconst DEFAULT_ADMIN = {\n email: 'joao.duvido@appx.pt',\n password: 'password',\n};\n\nconst authenticate = async (email: string, password: string) => {\n if (email === DEFAULT_ADMIN.email && password === DEFAULT_ADMIN.password) {\n return Promise.resolve(DEFAULT_ADMIN);\n }\n return null;\n};\n\nexport async function createAdminJsModule(): Promise<DynamicModule> {\n const {default: AdminJS} = await dynamicImport('adminjs');\n const {Database, Resource} = await dynamicImport('@adminjs/prisma');\n const {AdminModule} = await dynamicImport('@adminjs/nestjs');\n const {default: importExportFeature} = await dynamicImport('@adminjs/import-export');\n const {default: passwordFeature} = await dynamicImport('@adminjs/passwords');\n const argon2 = await dynamicImport('argon2');\n\n const resources = getAdminJSResources();\n const {componentLoader, Components} = await initializeComponents();\n const schemaPath = './prisma/schema.prisma';\n const schema = readFileSync(schemaPath, 'utf-8');\n const dmmf = await getDMMF({datamodel: schema});\n\n const models = [];\n\n for (const resource of resources) {\n const model = dmmf.datamodel.models.find(\n (model) => model.name === resource.name,\n );\n\n models.push({\n model,\n options: resource.options,\n features: model.name === 'User' ? [\n passwordFeature({\n properties: {\n encryptedPassword: 'password',\n password: 'plainPassword',\n },\n hash: argon2.hash,\n componentLoader,\n }),\n ] : [],\n });\n }\n\n AdminJS.registerAdapter({Database, Resource});\n\n return AdminModule.createAdminAsync({\n imports: [PrismaModule],\n inject: [PrismaService],\n useFactory: async (prisma: PrismaService) => {\n const authenticate = async (email: string, password: string) => {\n const user = await prisma.user.findUnique({\n where: {\n email\n }\n });\n\n if (!user || user.role !== 'ADMIN') {\n return null;\n }\n\n const isPasswordValid = await argon2.verify(user.password, password);\n\n return isPasswordValid ? Promise.resolve({email: user.email, role: user.role, id: user.id}) : null;\n }\n\n return {\n adminJsOptions: {\n rootPath: '/admin',\n dashboard: {\n component: Components.Dashboard,\n handler: async () => {\n return {some: 'output'};\n },\n },\n branding: {\n companyName: 'AppX Core Wizard',\n withMadeWithLove: false,\n logo: 'https://i.ibb.co/XZNRS5m/appxdigitalcom-logo.jpg',\n },\n resources: models.map((m) => {\n return {\n resource: {model: m.model, client: prisma},\n options: {\n ...m.options,\n actions: createActions(),\n },\n features: [...(m.features || []), importExportFeature({\n componentLoader\n })],\n };\n }),\n componentLoader,\n },\n auth: {\n authenticate,\n cookieName: process.env.SESSION_COOKIE_NAME,\n cookiePassword: process.env.SESSION_SECRET,\n },\n sessionOptions: {\n resave: false,\n saveUninitialized: true,\n secret: process.env.SESSION_SECRET,\n cookie: {\n httpOnly: process.env.NODE_ENV === 'production',\n secure: false,\n },\n name: process.env.SESSION_COOKIE_NAME,\n },\n };\n },\n });\n}\n";
1
+ export declare const adminJsModuleCode = "\nimport {DynamicModule} from '@nestjs/common';\nimport {addBasicFilters, createActions, createPermissionHandler, dynamicImport, getAdminJSResources} from './utils';\nimport {initializeComponents} from './component-loader';\nimport {readFileSync} from 'fs';\nimport {getDMMF} from '@prisma/sdk';\nimport {PrismaService} from '@appxdigital/appx-core';\nimport {PrismaModule} from \"../prisma/prisma.module\";\n\nconst DEFAULT_ADMIN = {\n email: 'joao.duvido@appx.pt',\n password: 'password',\n};\n\nconst authenticate = async (email: string, password: string) => {\n if (email === DEFAULT_ADMIN.email && password === DEFAULT_ADMIN.password) {\n return Promise.resolve(DEFAULT_ADMIN);\n }\n return null;\n};\n\nexport async function createAdminJsModule(): Promise<DynamicModule> {\n const {default: AdminJS} = await dynamicImport('adminjs');\n const {Database, Resource} = await dynamicImport('@adminjs/prisma');\n const {AdminModule} = await dynamicImport('@adminjs/nestjs');\n const {default: importExportFeature} = await dynamicImport('@adminjs/import-export');\n const {default: passwordFeature} = await dynamicImport('@adminjs/passwords');\n const argon2 = await dynamicImport('argon2');\n\n const resources = getAdminJSResources();\n const {componentLoader, Components} = await initializeComponents();\n const schemaPath = './prisma/schema.prisma';\n const schema = readFileSync(schemaPath, 'utf-8');\n const dmmf = await getDMMF({datamodel: schema});\n\n const models = [];\n\n for (const resource of resources) {\n const model = dmmf.datamodel.models.find(\n (model) => model.name === resource.name,\n );\n\n models.push({\n model,\n options: resource.options,\n features: model.name === 'User' ? [\n passwordFeature({\n properties: {\n encryptedPassword: 'password',\n password: 'plainPassword',\n },\n hash: argon2.hash,\n componentLoader,\n }),\n ] : [],\n });\n }\n\n AdminJS.registerAdapter({Database, Resource});\n\n return AdminModule.createAdminAsync({\n imports: [PrismaModule],\n inject: [PrismaService],\n useFactory: async (prisma: PrismaService) => {\n const authenticate = async (email: string, password: string) => {\n const user = await prisma.user.findFirst({\n where: {\n email\n }\n });\n\n if (!user || user.role !== 'ADMIN') {\n return null;\n }\n\n const isPasswordValid = await argon2.verify(user.password, password);\n\n return isPasswordValid ? Promise.resolve({email: user.email, role: user.role, id: user.id}) : null;\n }\n\n return {\n adminJsOptions: {\n rootPath: '/admin',\n dashboard: {\n component: Components.Dashboard,\n handler: async () => {\n return {some: 'output'};\n },\n },\n branding: {\n companyName: 'AppX Core Wizard',\n withMadeWithLove: false,\n logo: 'https://i.ibb.co/XZNRS5m/appxdigitalcom-logo.jpg',\n },\n resources: models.map((m) => {\n return {\n resource: {model: m.model, client: prisma},\n options: {\n ...m.options,\n actions: createActions(),\n },\n features: [...(m.features || []), importExportFeature({\n componentLoader\n })],\n };\n }),\n componentLoader,\n },\n auth: {\n authenticate,\n cookieName: process.env.SESSION_COOKIE_NAME,\n cookiePassword: process.env.SESSION_SECRET,\n },\n sessionOptions: {\n resave: false,\n saveUninitialized: true,\n secret: process.env.SESSION_SECRET,\n cookie: {\n httpOnly: process.env.NODE_ENV === 'production',\n secure: false,\n },\n name: process.env.SESSION_COOKIE_NAME,\n },\n };\n },\n });\n}\n";
2
2
  //# sourceMappingURL=admin.template.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"admin.template.d.ts","sourceRoot":"","sources":["../../../../src/config/admin/templates/admin.template.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,iBAAiB,gwJA+H7B,CAAC"}
1
+ {"version":3,"file":"admin.template.d.ts","sourceRoot":"","sources":["../../../../src/config/admin/templates/admin.template.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,iBAAiB,+vJA+H7B,CAAC"}
@@ -66,7 +66,7 @@ export async function createAdminJsModule(): Promise<DynamicModule> {
66
66
  inject: [PrismaService],
67
67
  useFactory: async (prisma: PrismaService) => {
68
68
  const authenticate = async (email: string, password: string) => {
69
- const user = await prisma.user.findUnique({
69
+ const user = await prisma.user.findFirst({
70
70
  where: {
71
71
  email
72
72
  }
@@ -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"}
@@ -37,7 +37,7 @@ function GenericResolverFactory(model, returnType, createInputType, updateInputT
37
37
  async findOne(where, info) {
38
38
  const select = new plugins_1.PrismaSelect(info).value;
39
39
  const modelDelegate = this.prisma.getModelDelegate(model);
40
- return modelDelegate.findUnique({
40
+ return modelDelegate.findFirst({
41
41
  where,
42
42
  ...select,
43
43
  });
@@ -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
  }
@@ -138,7 +138,7 @@ let AuthService = class AuthService {
138
138
  if (!req?.user?.id) {
139
139
  throw new common_1.UnauthorizedException('Please log-in');
140
140
  }
141
- return this.prisma.user.findUnique({
141
+ return this.prisma.user.findFirst({
142
142
  where: {
143
143
  id: req?.user?.id,
144
144
  },
@@ -186,7 +186,7 @@ let AuthService = class AuthService {
186
186
  });
187
187
  }
188
188
  async closeSpecificSession(sessionId) {
189
- const deleted = await this.prisma.session.delete({
189
+ const deleted = await this.prisma.session.deleteMany({
190
190
  where: { id: sessionId },
191
191
  }).catch(() => null);
192
192
  return !!deleted;
@@ -232,7 +232,7 @@ let AuthService = class AuthService {
232
232
  const currentTokenId = userFromRefreshTokenGuard.refreshTokenId;
233
233
  const providedTokenString = userFromRefreshTokenGuard.currentRefreshToken;
234
234
  // Invalidate the used refresh token
235
- await this.prisma.userRefreshToken.update({
235
+ await this.prisma.userRefreshToken.updateMany({
236
236
  where: { id: currentTokenId },
237
237
  data: { revokedAt: new Date() },
238
238
  },
@@ -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"}
@@ -39,7 +39,7 @@ let RefreshTokenStrategy = class RefreshTokenStrategy extends (0, passport_1.Pas
39
39
  if (!user) {
40
40
  throw new common_1.UnauthorizedException('User not found');
41
41
  }
42
- const tokenRecord = await this.prisma.userRefreshToken.findUnique({
42
+ const tokenRecord = await this.prisma.userRefreshToken.findFirst({
43
43
  where: { token: refreshToken },
44
44
  });
45
45
  if (!tokenRecord) {
@@ -26,7 +26,7 @@ let SessionSerializer = class SessionSerializer extends passport_1.PassportSeria
26
26
  return done(new Error('No userId provided'), null);
27
27
  }
28
28
  try {
29
- const user = await this.prisma.user.findUniqueOrThrow({
29
+ const user = await this.prisma.user.findFirstOrThrow({
30
30
  where: { id: Number(userId) },
31
31
  select: {
32
32
  id: true,
@@ -9,12 +9,12 @@ class CorePrismaSessionStore extends express_session_1.Store {
9
9
  super();
10
10
  this.get = async (sid, callback) => {
11
11
  try {
12
- const record = await this.prisma.session.findUnique({ where: { sid } }, { BYPASS_FILTERING: true });
12
+ const record = await this.prisma.session.findFirst({ where: { sid } }, { BYPASS_FILTERING: true });
13
13
  if (!record) {
14
14
  return callback(null, null);
15
15
  }
16
16
  if (record.expiresAt && record.expiresAt <= new Date()) {
17
- await this.prisma.session.delete({ where: { sid } }, { BYPASS_FILTERING: true });
17
+ await this.prisma.session.deleteMany({ where: { sid } }, { BYPASS_FILTERING: true });
18
18
  return callback(null, null);
19
19
  }
20
20
  const session = JSON.parse(record.data);
@@ -60,7 +60,7 @@ class CorePrismaSessionStore extends express_session_1.Store {
60
60
  };
61
61
  this.destroy = async (sid, callback) => {
62
62
  try {
63
- await this.prisma.session.delete({ where: { sid } }, { BYPASS_FILTERING: true });
63
+ await this.prisma.session.deleteMany({ where: { sid } }, { BYPASS_FILTERING: true });
64
64
  callback?.();
65
65
  }
66
66
  catch (err) {
@@ -82,7 +82,7 @@ class CorePrismaSessionStore extends express_session_1.Store {
82
82
  else {
83
83
  expiresAt = new Date(Date.now() + this.options.ttl * 1000);
84
84
  }
85
- await this.prisma.session.update({
85
+ await this.prisma.session.updateMany({
86
86
  where: { sid },
87
87
  data: { expiresAt },
88
88
  }, { BYPASS_FILTERING: true });
@@ -36,7 +36,7 @@ let CoreService = class CoreService {
36
36
  * @returns A promise of the record
37
37
  */
38
38
  async findOne(where, options = {}) {
39
- const record = this.modelDelegate.findUnique({
39
+ const record = this.modelDelegate.findFirst({
40
40
  where,
41
41
  ...options
42
42
  });
@@ -51,7 +51,7 @@ let CoreService = class CoreService {
51
51
  * @returns A promise of the record
52
52
  */
53
53
  async findById(id) {
54
- const record = await this.modelDelegate.findUnique({
54
+ const record = await this.modelDelegate.findFirst({
55
55
  where: this._generateWhereFromIdField(id)
56
56
  });
57
57
  if (!record) {
@@ -80,10 +80,10 @@ let CoreService = class CoreService {
80
80
  */
81
81
  async updateById(id, data) {
82
82
  try {
83
- return await this.modelDelegate.update({
83
+ return (await this.modelDelegate.updateMany({
84
84
  where: this._generateWhereFromIdField(id),
85
85
  data,
86
- });
86
+ }))[0];
87
87
  }
88
88
  catch (error) {
89
89
  (0, error_handler_1.handleError)(error);
@@ -96,9 +96,9 @@ let CoreService = class CoreService {
96
96
  */
97
97
  async deleteById(id) {
98
98
  try {
99
- return await this.modelDelegate.delete({
99
+ return (await this.modelDelegate.deleteMany({
100
100
  where: this._generateWhereFromIdField(id)
101
- });
101
+ }))[0];
102
102
  }
103
103
  catch (error) {
104
104
  (0, error_handler_1.handleError)(error);
@@ -32,7 +32,7 @@ let UserService = class UserService extends core_service_1.CoreService {
32
32
  });
33
33
  }
34
34
  async findByEmail(email) {
35
- return this.prisma.user.findUnique({
35
+ return this.prisma.user.findFirst({
36
36
  where: { email },
37
37
  select: {
38
38
  id: true,
@@ -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;IAsH5B;;;;;;;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,28 @@ 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
+ if (!permissions) {
233
+ throw new common_1.HttpException(`No permissions found for model '${modelName}' and role ${userRole}`, common_1.HttpStatus.FORBIDDEN);
234
+ }
235
+ let actionPermissions = this.selectPermission(permissions, action.toString(), modelName, userRole);
236
+ if (!actionPermissions) {
237
+ console.debug(`No permissions found for action '${modelName}.${String(action)}()' on role ${userRole}`);
238
+ throw new common_1.HttpException('Missing permissions on model ' + modelName, common_1.HttpStatus.FORBIDDEN);
239
+ }
211
240
  if (args.select) {
212
241
  for (const field of Object.keys(args.select)) {
213
- let relation = this.getRelationType(modelName, field);
242
+ // If field is relation
243
+ let relation = this.getRelation(modelName, field);
214
244
  if (!relation) {
215
245
  continue;
216
246
  }
217
247
  if (relation.relation === 'belongsTo') {
218
- const relatedPermissions = permissionsConfig[relation.model.toLowerCase()]?.[userRole]?.[action];
248
+ 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...`);
249
+ const relatedPermissions = this.selectPermission(permissionsConfig[relation.model.toLowerCase()]?.[userRole] || {}, action.toString(), relation.model, userRole);
250
+ if (!relatedPermissions) {
251
+ console.debug(`No permissions found for action '${relation.model}.${String(action)}()' on role ${userRole}`);
252
+ throw new common_1.HttpException('Missing permissions on model ' + relation.model, common_1.HttpStatus.FORBIDDEN);
253
+ }
219
254
  belongsToQueue.push({
220
255
  field,
221
256
  relation,
@@ -223,52 +258,48 @@ let PrismaService = class PrismaService {
223
258
  });
224
259
  continue;
225
260
  }
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({
261
+ this.debug(`Found 1:N / N:N (hasMany) relation to model '${relation.model}' from model '${modelName}' via field '${field}'. Applying filter to the relation...`);
262
+ /* Where conditions are applied outside of the select. Example as per documentation:
263
+ const result = await prisma.user.findFirst({
264
+ select: {
265
+ posts: {
266
+ where: {
267
+ published: false,
268
+ },
229
269
  select: {
230
- posts: {
231
- where: {
232
- published: false,
233
- },
234
- select: {
235
- title: true,
236
- },
237
- },
270
+ title: true,
238
271
  },
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
- }
272
+ },
273
+ },
274
+ })
275
+ */
276
+ args.select[field].where = this.applyWhereConditions(relation.model, userRole, args.select[field], user, action).where;
277
+ delete args.select[field].select.where;
247
278
  }
248
279
  }
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
280
  if (actionPermissions === 'ALL' && belongsToQueue.length === 0 || action.toString().startsWith('create')) {
281
+ this.debug(`No conditions to apply for '${modelName}.${String(action)}()' on role ${userRole}`);
257
282
  return args;
258
283
  }
259
284
  const whereClause = this.buildConditions(actionPermissions.conditions, user);
285
+ this.debug(`Applying where conditions for '${modelName}.${String(action)}()' on role ${userRole}: ${JSON.stringify(whereClause)}`);
260
286
  if (!args.where) {
261
287
  args.where = whereClause;
262
288
  }
263
289
  else {
264
290
  args.where = { AND: [args.where, whereClause] };
265
291
  }
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
292
+ // Failsafe: If there are no conditions at all and there were supposed to be, block access
267
293
  if (Object.keys(args.where).length === 0 && !belongsToQueue?.length) {
294
+ this.debug(`Found a weird edge case. Contact Manuel Olveira @ AppX.`);
268
295
  throw new common_1.ForbiddenException(`You are not authorized to access this record`);
269
296
  }
297
+ if (belongsToQueue?.length > 0) {
298
+ this.debug(`Merging belongsTo relation conditions into main ${modelName}.`, 'warn');
299
+ }
270
300
  for (const entry of belongsToQueue) {
271
301
  const { relatedPermissions, field } = entry;
302
+ this.debug(`Merging conditions for belongsTo relation field '${field}': ${JSON.stringify(relatedPermissions?.conditions)}`, 'info');
272
303
  args.where = {
273
304
  ...args.where,
274
305
  [field]: {
@@ -281,15 +312,6 @@ let PrismaService = class PrismaService {
281
312
  }
282
313
  return args;
283
314
  }
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
315
  /**
294
316
  * Builds dynamic conditions based on the type of clause (OR, AND, or field conditions).
295
317
  * Each condition is processed, and placeholders (like `$USER_ID`) are replaced with actual values.
@@ -349,9 +371,11 @@ let PrismaService = class PrismaService {
349
371
  getFieldsToOmit(modelName, role) {
350
372
  const modelInfo = this.fieldConfigs[modelName.toLowerCase()] || {};
351
373
  const fieldConfig = modelInfo.fieldConfig || {};
352
- return Object.entries(fieldConfig)
374
+ const omitFields = Object.entries(fieldConfig)
353
375
  .filter(([_, roles]) => !roles.includes(role))
354
376
  .map(([field]) => field);
377
+ this.debug(`Fields to omit on '${modelName}', based on schema @Role() configuration: ${omitFields.join(', ')}`);
378
+ return omitFields;
355
379
  }
356
380
  /**
357
381
  * Builds the `select` object for Prisma queries, omitting fields based on the user's role.
@@ -383,7 +407,8 @@ let PrismaService = class PrismaService {
383
407
  if (includedArgs === true) {
384
408
  includedArgs = {};
385
409
  }
386
- const relatedModelName = this.getRelatedModelName(modelName, relationKey);
410
+ const relatedModelName = this.getRelation(modelName, relationKey, true).model;
411
+ this.debug(`Found relation to model '${relatedModelName}' from model '${modelName}' via field '${relationKey}'. Generating select fields...`);
387
412
  const relatedModelOmitFields = this.getFieldsToOmit(relatedModelName, userRole);
388
413
  const relatedSelectFields = this.buildSelectFields(relatedModelName, relatedModelOmitFields, includedArgs.include || null, userRole);
389
414
  if (Object.keys(relatedSelectFields).length > 0) {
@@ -393,24 +418,28 @@ let PrismaService = class PrismaService {
393
418
  }
394
419
  return selectFields;
395
420
  }
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;
421
+ getRelation(parentModel, relatedField, throwOnNotFound = false) {
422
+ parentModel = parentModel.toLowerCase();
423
+ const parent = this.fieldConfigs[parentModel];
424
+ const relation = parent.relationFields[relatedField];
425
+ if (!relation && throwOnNotFound)
426
+ throw new Error(`Relation key ${relatedField} not found in model ${parentModel}`);
427
+ return relation;
428
+ }
429
+ selectPermission(permissions, action, modelName, userRole) {
430
+ let actionPermissions = permissions[action];
431
+ // If action is find* or count and there is no permission, coalesce to one of the find actions
432
+ if (!actionPermissions && (action.startsWith('find') || action === 'count')) {
433
+ if (permissions['findMany']) {
434
+ this.debug(`Using 'findMany' permissions for '${action}' action on model '${modelName}' and role ${userRole}`, 'info');
435
+ actionPermissions = permissions['findMany'];
436
+ }
437
+ else if (permissions['findFirst']) {
438
+ this.debug(`Using 'findFirst' permissions for '${action}' action on model '${modelName}' and role ${userRole}`, 'info');
439
+ actionPermissions = permissions['findFirst'];
440
+ }
412
441
  }
413
- throw new Error(`Relation key ${relationKey} not found in model ${parentModelName}`);
442
+ return actionPermissions;
414
443
  }
415
444
  };
416
445
  exports.PrismaService = PrismaService;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appxdigital/appx-core",
3
- "version": "0.1.89",
3
+ "version": "0.1.91",
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",