@appxdigital/appx-core 0.1.101 → 0.1.103

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.
Files changed (36) hide show
  1. package/dist/backoffice/appx-core-admin.module.d.ts +7 -0
  2. package/dist/backoffice/appx-core-admin.module.d.ts.map +1 -0
  3. package/dist/backoffice/appx-core-admin.module.js +238 -0
  4. package/dist/backoffice/utils.d.ts +21 -0
  5. package/dist/backoffice/utils.d.ts.map +1 -0
  6. package/dist/backoffice/utils.js +82 -0
  7. package/dist/common/config/adminConfigType.d.ts +26 -0
  8. package/dist/common/config/adminConfigType.d.ts.map +1 -0
  9. package/dist/common/config/adminConfigType.js +2 -0
  10. package/dist/common/interceptors/prisma.interceptor.d.ts.map +1 -1
  11. package/dist/common/interceptors/prisma.interceptor.js +49 -39
  12. package/dist/index.d.ts +2 -0
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +2 -0
  15. package/dist/prisma/prisma.service.d.ts +6 -2
  16. package/dist/prisma/prisma.service.d.ts.map +1 -1
  17. package/dist/prisma/prisma.service.js +21 -14
  18. package/package.json +15 -3
  19. package/dist/config/admin/create.d.ts +0 -2
  20. package/dist/config/admin/create.d.ts.map +0 -1
  21. package/dist/config/admin/create.js +0 -25
  22. package/dist/config/admin/generate-admin.d.ts +0 -2
  23. package/dist/config/admin/generate-admin.d.ts.map +0 -1
  24. package/dist/config/admin/generate-admin.js +0 -75
  25. package/dist/config/admin/templates/admin.template.d.ts +0 -2
  26. package/dist/config/admin/templates/admin.template.d.ts.map +0 -1
  27. package/dist/config/admin/templates/admin.template.js +0 -131
  28. package/dist/config/admin/templates/component-loader.template.d.ts +0 -2
  29. package/dist/config/admin/templates/component-loader.template.d.ts.map +0 -1
  30. package/dist/config/admin/templates/component-loader.template.js +0 -17
  31. package/dist/config/admin/templates/dashboard.template.d.ts +0 -2
  32. package/dist/config/admin/templates/dashboard.template.d.ts.map +0 -1
  33. package/dist/config/admin/templates/dashboard.template.js +0 -40
  34. package/dist/config/admin/templates/utils.template.d.ts +0 -2
  35. package/dist/config/admin/templates/utils.template.d.ts.map +0 -1
  36. package/dist/config/admin/templates/utils.template.js +0 -135
@@ -0,0 +1,7 @@
1
+ import { DynamicModule } from '@nestjs/common';
2
+ import { AdminConfigType } from "../common/config/adminConfigType";
3
+ import { PermissionsConfigType } from "../common/config/permissionsConfigTypes";
4
+ export declare class AppxCoreAdminModule {
5
+ static forRoot(config: AdminConfigType, permissionConfig: PermissionsConfigType): Promise<DynamicModule>;
6
+ }
7
+ //# sourceMappingURL=appx-core-admin.module.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"appx-core-admin.module.d.ts","sourceRoot":"","sources":["../../src/backoffice/appx-core-admin.module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,aAAa,EAAiB,MAAM,gBAAgB,CAAC;AAQ7D,OAAO,EAAC,eAAe,EAAC,MAAM,kCAAkC,CAAC;AAIjE,OAAO,EAAC,qBAAqB,EAAC,MAAM,yCAAyC,CAAC;AAE9E,qBAEa,mBAAmB;IAC5B,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,eAAe,EAAE,gBAAgB,EAAE,qBAAqB,GAAG,OAAO,CAAC,aAAa,CAAC;CAG3G"}
@@ -0,0 +1,238 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
19
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
20
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
21
+ 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;
22
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
23
+ };
24
+ var __importStar = (this && this.__importStar) || (function () {
25
+ var ownKeys = function(o) {
26
+ ownKeys = Object.getOwnPropertyNames || function (o) {
27
+ var ar = [];
28
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
29
+ return ar;
30
+ };
31
+ return ownKeys(o);
32
+ };
33
+ return function (mod) {
34
+ if (mod && mod.__esModule) return mod;
35
+ var result = {};
36
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
37
+ __setModuleDefault(result, mod);
38
+ return result;
39
+ };
40
+ })();
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ exports.AppxCoreAdminModule = void 0;
43
+ const common_1 = require("@nestjs/common");
44
+ const utils_1 = require("./utils");
45
+ const fs_1 = require("fs");
46
+ const sdk_1 = require("@prisma/sdk");
47
+ const nestjs_request_context_1 = require("nestjs-request-context");
48
+ const adminjs_1 = require("adminjs");
49
+ const argon2 = __importStar(require("argon2"));
50
+ const prisma_service_1 = require("../prisma/prisma.service");
51
+ let AppxCoreAdminModule = class AppxCoreAdminModule {
52
+ static forRoot(config, permissionConfig) {
53
+ return createAdminJsModule(config, permissionConfig);
54
+ }
55
+ };
56
+ exports.AppxCoreAdminModule = AppxCoreAdminModule;
57
+ exports.AppxCoreAdminModule = AppxCoreAdminModule = __decorate([
58
+ (0, common_1.Global)(),
59
+ (0, common_1.Module)({})
60
+ ], AppxCoreAdminModule);
61
+ async function createAdminJsModule(adminConfig, permissionConfig) {
62
+ // Due to AdminJS only allowing ESM now, we need to use dynamic imports to load the modules, this function can be found within src/backoffice/utils.ts
63
+ const { default: AdminJS } = await (0, utils_1.dynamicImport)('adminjs');
64
+ const { Database, Resource, convertParam } = await (0, utils_1.dynamicImport)('@adminjs/prisma');
65
+ const { AdminModule } = await (0, utils_1.dynamicImport)('@adminjs/nestjs');
66
+ const { default: importExportFeature } = await (0, utils_1.dynamicImport)('@adminjs/import-export');
67
+ const { default: passwordFeature } = await (0, utils_1.dynamicImport)('@adminjs/passwords');
68
+ // This gets the models from the Prisma schema, and then creates the AdminJS resources
69
+ const schemaPath = './prisma/schema.prisma';
70
+ const schema = (0, fs_1.readFileSync)(schemaPath, 'utf-8');
71
+ const dmmf = await (0, sdk_1.getDMMF)({ datamodel: schema });
72
+ const models = [];
73
+ for (const resource of adminConfig.resources) {
74
+ const model = dmmf.datamodel.models.find((model) => model.name === resource.name);
75
+ if (!model)
76
+ continue;
77
+ models.push({
78
+ model: model,
79
+ options: {
80
+ navigation: {
81
+ name: null,
82
+ ...(resource.options?.navigation || {}),
83
+ },
84
+ ...resource.options,
85
+ },
86
+ features: model.name === 'User'
87
+ ? [
88
+ passwordFeature({
89
+ properties: {
90
+ encryptedPassword: 'password',
91
+ password: 'plainPassword',
92
+ },
93
+ hash: argon2.hash,
94
+ componentLoader: adminConfig.componentLoader,
95
+ }),
96
+ ]
97
+ : [],
98
+ });
99
+ }
100
+ AdminJS.registerAdapter({
101
+ Database,
102
+ Resource: class ExtendedResource extends Resource {
103
+ async findOne(id) {
104
+ const idProperty = this.properties().find((property) => property.isId());
105
+ if (!idProperty)
106
+ return null;
107
+ const result = await this.manager.findFirst({
108
+ where: {
109
+ [idProperty.path()]: convertParam(idProperty, this.model.fields, id),
110
+ },
111
+ });
112
+ if (!result)
113
+ return null;
114
+ return new adminjs_1.BaseRecord(this.prepareReturnValues(result), this);
115
+ }
116
+ async update(pk, params = {}) {
117
+ const idProperty = this.properties().find((property) => property.isId());
118
+ if (!idProperty)
119
+ return {};
120
+ const preparedParams = this.prepareParams(params);
121
+ await this.manager.updateMany({
122
+ where: {
123
+ [idProperty.path()]: convertParam(idProperty, this.model.fields, pk),
124
+ },
125
+ data: preparedParams,
126
+ });
127
+ const result = (await this.manager.findFirst({
128
+ where: {
129
+ [idProperty.path()]: convertParam(idProperty, this.model.fields, pk),
130
+ },
131
+ }));
132
+ return this.prepareReturnValues(result);
133
+ }
134
+ },
135
+ });
136
+ const Module = await AdminModule.createAdminAsync({
137
+ inject: [prisma_service_1.PrismaService],
138
+ useFactory: async (prisma) => {
139
+ const authenticate = async (email, password) => {
140
+ return new Promise((resolve) => {
141
+ prisma.withExposedModels(['User'], async () => {
142
+ const user = await prisma.user.findFirst({
143
+ where: {
144
+ email,
145
+ },
146
+ },
147
+ //@ts-ignore
148
+ {
149
+ BYPASS_OMISSION: true,
150
+ });
151
+ if (!user) {
152
+ return resolve(null);
153
+ }
154
+ const isPasswordValid = await argon2.verify(user.password, password);
155
+ if (!isPasswordValid) {
156
+ return resolve(null);
157
+ }
158
+ return resolve({ email: user.email, role: user.role, id: user.id });
159
+ });
160
+ });
161
+ };
162
+ return {
163
+ adminJsOptions: {
164
+ rootPath: adminConfig.rootPath,
165
+ dashboard: adminConfig.dashboard,
166
+ branding: {
167
+ withMadeWithLove: false,
168
+ ...adminConfig.branding,
169
+ },
170
+ resources: models.map((m) => {
171
+ return {
172
+ // Proxy client with global getter that reads client.model.X
173
+ resource: {
174
+ model: m.model,
175
+ client: new Proxy(prisma, {
176
+ get(target, prop) {
177
+ return (target[prop] ||
178
+ target.model[m.model.name.toLowerCase()]);
179
+ },
180
+ }),
181
+ },
182
+ options: {
183
+ ...m.options,
184
+ actions: (0, utils_1.createActions)(permissionConfig),
185
+ },
186
+ features: [
187
+ ...(m.features || []),
188
+ importExportFeature({ componentLoader: adminConfig.componentLoader }),
189
+ ],
190
+ };
191
+ }),
192
+ componentLoader: adminConfig.componentLoader,
193
+ },
194
+ auth: {
195
+ authenticate,
196
+ cookieName: process.env.SESSION_COOKIE_NAME,
197
+ cookiePassword: process.env.SESSION_SECRET,
198
+ },
199
+ sessionOptions: {
200
+ resave: false,
201
+ saveUninitialized: true,
202
+ secret: process.env.SESSION_SECRET,
203
+ cookie: {
204
+ httpOnly: process.env.NODE_ENV === 'production',
205
+ //secure: process.env.NODE_ENV === 'production',
206
+ secure: false,
207
+ },
208
+ name: process.env.SESSION_COOKIE_NAME,
209
+ },
210
+ };
211
+ },
212
+ });
213
+ return {
214
+ ...Module,
215
+ module: class AdminJsModule extends Module.module {
216
+ async onModuleInit() {
217
+ if (super.onModuleInit) {
218
+ await super.onModuleInit();
219
+ }
220
+ const options = this.adminModuleOptions.adminJsOptions;
221
+ const httpAdapter = this.httpAdapterHost.httpAdapter.getInstance();
222
+ // Add to stack, right before admin router
223
+ const adminIdx = httpAdapter.router.stack.findIndex((l) => l.name === 'admin');
224
+ const middleware = new nestjs_request_context_1.RequestContextMiddleware();
225
+ //httpAdapter.use(options.rootPath, );
226
+ httpAdapter.use(options.rootPath, (req, res, next) => {
227
+ middleware.use(req, res, () => {
228
+ // @ts-ignore
229
+ req.user = req.session?.adminUser;
230
+ next();
231
+ });
232
+ });
233
+ const requestContextLayer = httpAdapter.router.stack.splice(httpAdapter.router.stack.length - 1, 1)[0];
234
+ httpAdapter.router.stack.splice(adminIdx, 0, requestContextLayer);
235
+ }
236
+ },
237
+ };
238
+ }
@@ -0,0 +1,21 @@
1
+ import { PermissionsConfigType } from "../common/config/permissionsConfigTypes";
2
+ export declare const dynamicImport: (packageName: string) => Promise<any>;
3
+ export declare function createPermissionHandler(role: string, resource: string, action: string, permissionConfig: PermissionsConfigType): (requestContext: any) => any;
4
+ export declare const createActions: (permissionConfig: PermissionsConfigType) => {
5
+ list: {
6
+ isAccessible: (context: any) => any;
7
+ };
8
+ show: {
9
+ isAccessible: (context: any) => any;
10
+ };
11
+ edit: {
12
+ isAccessible: (context: any) => any;
13
+ };
14
+ delete: {
15
+ isAccessible: (context: any) => any;
16
+ };
17
+ new: {
18
+ isAccessible: (context: any) => any;
19
+ };
20
+ };
21
+ //# sourceMappingURL=utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/backoffice/utils.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,qBAAqB,EAAC,MAAM,yCAAyC,CAAC;AAE9E,eAAO,MAAM,aAAa,GAAU,aAAa,MAAM,iBACF,CAAC;AAEtD,wBAAgB,uBAAuB,CACnC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,gBAAgB,EAAE,qBAAqB,oBAkCP,GAAG,SAwBtC;AAED,eAAO,MAAM,aAAa,GAAI,kBAAkB,qBAAqB;;gCAqBhD,GAAG;;;gCAAH,GAAG;;;gCAAH,GAAG;;;gCAAH,GAAG;;;gCAAH,GAAG;;CAHvB,CAAC"}
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createActions = exports.dynamicImport = void 0;
4
+ exports.createPermissionHandler = createPermissionHandler;
5
+ const prisma_service_1 = require("../prisma/prisma.service");
6
+ const dynamicImport = async (packageName) => new Function(`return import('${packageName}')`)();
7
+ exports.dynamicImport = dynamicImport;
8
+ function createPermissionHandler(role, resource, action, permissionConfig) {
9
+ const rolePermissions = permissionConfig[resource]?.[role];
10
+ if (!rolePermissions) {
11
+ return () => false;
12
+ }
13
+ const actionMapping = {
14
+ list: ['findMany', 'findFirst'],
15
+ show: ['findFirst', 'findMany'],
16
+ edit: 'updateMany',
17
+ delete: 'delete',
18
+ new: 'create',
19
+ };
20
+ const mappedAction = Array.isArray(actionMapping[action])
21
+ ? actionMapping[action]
22
+ : [actionMapping[action]];
23
+ for (const act of mappedAction) {
24
+ if (!rolePermissions[act]) {
25
+ continue;
26
+ }
27
+ if (rolePermissions[act] === 'ALL') {
28
+ return () => true;
29
+ }
30
+ if (typeof rolePermissions[act] === 'object') {
31
+ return () => true;
32
+ // TODO granular permissions with db query (cached)
33
+ return (requestContext) => {
34
+ // @ts-ignore
35
+ let conditions = rolePermissions[act]?.conditions;
36
+ if (!conditions)
37
+ return () => false;
38
+ conditions = prisma_service_1.PrismaService._buildConditions(conditions, requestContext.currentAdmin);
39
+ const record = requestContext.record;
40
+ // Overlay conditions with record values if available
41
+ // Todo validation is shallow
42
+ if (record) {
43
+ for (const key in conditions) {
44
+ if (record.params[key] !== conditions[key]) {
45
+ return false;
46
+ }
47
+ }
48
+ }
49
+ return conditions;
50
+ };
51
+ }
52
+ }
53
+ return () => false;
54
+ }
55
+ const createActions = (permissionConfig) => {
56
+ return {
57
+ list: {
58
+ isAccessible: createIsAccessible('list', permissionConfig),
59
+ },
60
+ show: {
61
+ isAccessible: createIsAccessible('show', permissionConfig),
62
+ },
63
+ edit: {
64
+ isAccessible: createIsAccessible('edit', permissionConfig),
65
+ },
66
+ delete: {
67
+ isAccessible: createIsAccessible('delete', permissionConfig),
68
+ },
69
+ new: {
70
+ isAccessible: createIsAccessible('new', permissionConfig),
71
+ },
72
+ };
73
+ };
74
+ exports.createActions = createActions;
75
+ const createIsAccessible = (action, permissionConfig) => {
76
+ return (context) => {
77
+ if (!context.currentAdmin)
78
+ return false;
79
+ const { role } = context.currentAdmin;
80
+ return createPermissionHandler(role, context.resource.model.name, action, permissionConfig)(context);
81
+ };
82
+ };
@@ -0,0 +1,26 @@
1
+ export interface AdminConfigType {
2
+ resources: {
3
+ name: string;
4
+ options?: {
5
+ navigation?: {
6
+ name?: string | null;
7
+ icon?: string;
8
+ };
9
+ [key: string]: any;
10
+ };
11
+ }[];
12
+ rootPath: string;
13
+ branding: {
14
+ companyName?: string;
15
+ withMadeWithLove?: boolean;
16
+ logo?: string;
17
+ };
18
+ dashboard?: {
19
+ component: string;
20
+ handler?: (request: any, response: any) => Promise<{
21
+ [key: string]: any;
22
+ }>;
23
+ };
24
+ componentLoader: any;
25
+ }
26
+ //# sourceMappingURL=adminConfigType.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adminConfigType.d.ts","sourceRoot":"","sources":["../../../src/common/config/adminConfigType.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,eAAe;IAC5B,SAAS,EAAE;QACP,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,CAAC,EAAE;YACN,UAAU,CAAC,EAAE;gBACT,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;gBACrB,IAAI,CAAC,EAAE,MAAM,CAAC;aACjB,CAAC;YACF,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;SACtB,CAAC;KACL,EAAE,CAAC;IACJ,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE;QACN,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,gBAAgB,CAAC,EAAE,OAAO,CAAC;QAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,SAAS,CAAC,EAAE;QACR,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,KAAK,OAAO,CAAC;YAAE,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAA;SAAE,CAAC,CAAC;KAC9E,CAAC;IACF,eAAe,EAAE,GAAG,CAAC;CACxB"}
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -1 +1 @@
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;CA2D3E"}
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,EAAoB,aAAa,EAAC,MAAM,6BAA6B,CAAC;AAC7E,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;CAoE3E"}
@@ -33,48 +33,58 @@ let PrismaInterceptor = class PrismaInterceptor {
33
33
  : context.switchToHttp().getRequest(); // HTTP context
34
34
  // Attach expose_models metadata if needed
35
35
  const permissionMetadata = this.reflector.get(permission_decorator_1.PERMISSION_METADATA_KEY, context.getHandler()) || {};
36
- if (nestjs_request_context_1.RequestContext.currentContext)
37
- nestjs_request_context_1.RequestContext.currentContext.req.prismaExposedModels = permissionMetadata['expose_models'] || [];
38
- const useTransaction = this.reflector.get('useTransaction', context.getHandler()) ?? this.defaultUseTransaction === 'true';
39
- if (useTransaction) {
40
- return new rxjs_1.Observable((observer) => {
41
- this.prismaService
42
- .$transaction(async (transactionClient) => {
43
- nestjs_request_context_1.RequestContext.currentContext.req.prisma = transactionClient;
44
- await new Promise((resolve, reject) => {
45
- next
46
- .handle()
47
- .pipe((0, operators_1.tap)(() => { }), (0, operators_1.catchError)((err) => {
48
- const handledError = (0, error_handler_1.handleError)(err);
49
- reject(handledError);
50
- return (0, rxjs_1.throwError)(() => handledError);
51
- }))
52
- .subscribe({
53
- next: (result) => observer.next(result),
54
- complete: () => {
55
- delete nestjs_request_context_1.RequestContext.currentContext.req.prisma;
56
- observer.complete();
57
- resolve(null);
58
- },
59
- error: (err) => {
60
- delete nestjs_request_context_1.RequestContext.currentContext.req.prisma;
61
- observer.error(err);
62
- reject(err);
63
- },
36
+ return new rxjs_1.Observable((observer) => {
37
+ prisma_service_1.CorePrismaContext.run({
38
+ exposedModels: permissionMetadata['expose_models'] || [],
39
+ }, () => {
40
+ const useTransaction = this.reflector.get('useTransaction', context.getHandler()) ?? this.defaultUseTransaction === 'true';
41
+ if (useTransaction) {
42
+ this.prismaService
43
+ .$transaction(async (transactionClient) => {
44
+ nestjs_request_context_1.RequestContext.currentContext.req.prisma = transactionClient;
45
+ await new Promise((resolve, reject) => {
46
+ next
47
+ .handle()
48
+ .pipe((0, operators_1.tap)(() => { }), (0, operators_1.catchError)((err) => {
49
+ const handledError = (0, error_handler_1.handleError)(err);
50
+ reject(handledError);
51
+ return (0, rxjs_1.throwError)(() => handledError);
52
+ }))
53
+ .subscribe({
54
+ next: (result) => observer.next(result),
55
+ complete: () => {
56
+ delete nestjs_request_context_1.RequestContext.currentContext.req.prisma;
57
+ observer.complete();
58
+ resolve(null);
59
+ },
60
+ error: (err) => {
61
+ delete nestjs_request_context_1.RequestContext.currentContext.req.prisma;
62
+ observer.error(err);
63
+ reject(err);
64
+ },
65
+ });
64
66
  });
67
+ })
68
+ .catch((err) => {
69
+ delete nestjs_request_context_1.RequestContext.currentContext.req.prisma;
70
+ observer.error(err);
65
71
  });
66
- })
67
- .catch((err) => {
68
- delete nestjs_request_context_1.RequestContext.currentContext.req.prisma;
69
- observer.error(err);
70
- });
72
+ }
73
+ else {
74
+ next.handle().pipe((0, operators_1.tap)(() => { }), (0, operators_1.catchError)((error) => {
75
+ return (0, rxjs_1.throwError)(() => (0, error_handler_1.handleError)(error));
76
+ })).subscribe({
77
+ next: (result) => observer.next(result),
78
+ complete: () => {
79
+ observer.complete();
80
+ },
81
+ error: (err) => {
82
+ observer.error(err);
83
+ }
84
+ });
85
+ }
71
86
  });
72
- }
73
- else {
74
- return next.handle().pipe((0, operators_1.tap)(() => { }), (0, operators_1.catchError)((error) => {
75
- return (0, rxjs_1.throwError)(() => (0, error_handler_1.handleError)(error));
76
- }));
77
- }
87
+ });
78
88
  }
79
89
  };
80
90
  exports.PrismaInterceptor = PrismaInterceptor;
package/dist/index.d.ts CHANGED
@@ -12,6 +12,7 @@ export * from './common/guards/user-population.guard';
12
12
  export * from './modules/common.module';
13
13
  export * from './common/interceptors/prisma.interceptor';
14
14
  export * from './common/config/permissionsConfigTypes';
15
+ export * from './common/config/adminConfigType';
15
16
  export * from './common/config/permissions.config.provider';
16
17
  export * from './appx-core.module';
17
18
  export * from './modules/file/file-upload.module';
@@ -24,4 +25,5 @@ export * from './common/decorators/permission.decorator';
24
25
  export * from './common/interceptors/file.interceptor';
25
26
  export * from './modules/auth/authenticated.guard';
26
27
  export * from './modules/auth/jwt-auth.guard';
28
+ export * from './backoffice/appx-core-admin.module';
27
29
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,4BAA4B,CAAC;AAC3C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,yBAAyB,CAAC;AACxC,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,iCAAiC,CAAC;AAChD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,sCAAsC,CAAC;AACrD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,uCAAuC,CAAC;AACtD,cAAc,yBAAyB,CAAC;AACxC,cAAc,0CAA0C,CAAC;AACzD,cAAc,wCAAwC,CAAC;AACvD,cAAc,6CAA6C,CAAC;AAC5D,cAAc,oBAAoB,CAAC;AACnC,cAAc,mCAAmC,CAAC;AAClD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,wCAAwC,CAAC;AACvD,cAAc,+CAA+C,CAAC;AAC9D,cAAc,uCAAuC,CAAC;AACtD,cAAc,oCAAoC,CAAC;AACnD,cAAc,0CAA0C,CAAC;AACzD,cAAc,wCAAwC,CAAC;AACvD,cAAc,oCAAoC,CAAC;AACnD,cAAc,+BAA+B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,4BAA4B,CAAC;AAC3C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,yBAAyB,CAAC;AACxC,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,iCAAiC,CAAC;AAChD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,sCAAsC,CAAC;AACrD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,uCAAuC,CAAC;AACtD,cAAc,yBAAyB,CAAC;AACxC,cAAc,0CAA0C,CAAC;AACzD,cAAc,wCAAwC,CAAC;AACvD,cAAc,iCAAiC,CAAC;AAChD,cAAc,6CAA6C,CAAC;AAC5D,cAAc,oBAAoB,CAAC;AACnC,cAAc,mCAAmC,CAAC;AAClD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,wCAAwC,CAAC;AACvD,cAAc,+CAA+C,CAAC;AAC9D,cAAc,uCAAuC,CAAC;AACtD,cAAc,oCAAoC,CAAC;AACnD,cAAc,0CAA0C,CAAC;AACzD,cAAc,wCAAwC,CAAC;AACvD,cAAc,oCAAoC,CAAC;AACnD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,qCAAqC,CAAC"}
package/dist/index.js CHANGED
@@ -28,6 +28,7 @@ __exportStar(require("./common/guards/user-population.guard"), exports);
28
28
  __exportStar(require("./modules/common.module"), exports);
29
29
  __exportStar(require("./common/interceptors/prisma.interceptor"), exports);
30
30
  __exportStar(require("./common/config/permissionsConfigTypes"), exports);
31
+ __exportStar(require("./common/config/adminConfigType"), exports);
31
32
  __exportStar(require("./common/config/permissions.config.provider"), exports);
32
33
  __exportStar(require("./appx-core.module"), exports);
33
34
  __exportStar(require("./modules/file/file-upload.module"), exports);
@@ -40,3 +41,4 @@ __exportStar(require("./common/decorators/permission.decorator"), exports);
40
41
  __exportStar(require("./common/interceptors/file.interceptor"), exports);
41
42
  __exportStar(require("./modules/auth/authenticated.guard"), exports);
42
43
  __exportStar(require("./modules/auth/jwt-auth.guard"), exports);
44
+ __exportStar(require("./backoffice/appx-core-admin.module"), exports);
@@ -1,6 +1,10 @@
1
1
  import { Prisma, PrismaClient } from '@prisma/client';
2
2
  import { PermissionsConfigType } from '../common/config/permissionsConfigTypes';
3
3
  import type { PrismaClient as RuntimeClient } from '.prisma/client';
4
+ import { AsyncLocalStorage } from 'node:async_hooks';
5
+ export declare const CorePrismaContext: AsyncLocalStorage<{
6
+ exposedModels: string[];
7
+ }>;
4
8
  type ModelKey = keyof RuntimeClient;
5
9
  /** Extra options available on every model method */
6
10
  export type CorePrismaOptions = {
@@ -65,7 +69,7 @@ export declare class PrismaService {
65
69
  * @param user - The user object used to replace placeholders.
66
70
  * @returns The constructed `where` clause object.
67
71
  */
68
- private buildConditions;
72
+ static _buildConditions(conditions: any[], user: any): any;
69
73
  /**
70
74
  * Replaces placeholders (like `$USER_ID`) in the condition with actual values from the user object.
71
75
  * Handles conditions in the form of strings, arrays, or objects.
@@ -74,7 +78,7 @@ export declare class PrismaService {
74
78
  * @param user - The user object containing values like `id`.
75
79
  * @returns The condition with placeholders replaced by actual values.
76
80
  */
77
- private replacePlaceholders;
81
+ private static replacePlaceholders;
78
82
  /**
79
83
  * Retrieves the list of fields to omit based on the user's role.
80
84
  * It checks the field configurations stored in `fieldConfigs` and compares them against the role.
@@ -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;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;IAO5B,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC;IAQjE,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;IAqDX;;;;;;;;OAQG;IACH,OAAO,CAAC,kBAAkB;IAqC1B,IAAI,2BAA2B,QAM9B;IAED;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,oBAAoB;IA6K5B;;;;;;;OAOG;IACH,OAAO,CAAC,eAAe;IAgBvB;;;;;;;OAOG;IACH,OAAO,CAAC,mBAAmB;IA2B3B;;;;;;;OAOG;IACH,OAAO,CAAC,eAAe;IAUvB;;;;;;;;;;OAUG;IACH,OAAO,CAAC,iBAAiB;IA8EzB,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;QAC3B,UAAU,EAAE,OAAO,CAAA;KACtB;IAmBD,gBAAgB,CAAC,WAAW,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;CAgBzF"}
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;AAElE,OAAO,EAAC,iBAAiB,EAAC,MAAM,kBAAkB,CAAC;AAEnD,eAAO,MAAM,iBAAiB;mBACX,MAAM,EAAE;EACvB,CAAC;AAEL,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;IAO5B,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC;IAWjE,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;IAqDX;;;;;;;;OAQG;IACH,OAAO,CAAC,kBAAkB;IAqC1B,IAAI,2BAA2B,QAM9B;IAED;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,oBAAoB;IA8K5B;;;;;;;OAOG;WACW,gBAAgB,CAAC,UAAU,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,GAAG,GAAG,GAAG;IAgBjE;;;;;;;OAOG;IACH,OAAO,CAAC,MAAM,CAAC,mBAAmB;IA2BlC;;;;;;;OAOG;IACH,OAAO,CAAC,eAAe;IAUvB;;;;;;;;;;OAUG;IACH,OAAO,CAAC,iBAAiB;IA8EzB,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;QAC3B,UAAU,EAAE,OAAO,CAAA;KACtB;IAmBD,gBAAgB,CAAC,WAAW,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;CAgBzF"}
@@ -11,12 +11,15 @@ var __metadata = (this && this.__metadata) || function (k, v) {
11
11
  var __param = (this && this.__param) || function (paramIndex, decorator) {
12
12
  return function (target, key) { decorator(target, key, paramIndex); }
13
13
  };
14
+ var PrismaService_1;
14
15
  Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.PrismaService = void 0;
16
+ exports.PrismaService = exports.CorePrismaContext = void 0;
16
17
  const common_1 = require("@nestjs/common");
17
18
  const client_1 = require("@prisma/client");
18
19
  const nestjs_request_context_1 = require("nestjs-request-context");
19
- let PrismaService = class PrismaService {
20
+ const node_async_hooks_1 = require("node:async_hooks");
21
+ exports.CorePrismaContext = new node_async_hooks_1.AsyncLocalStorage();
22
+ let PrismaService = PrismaService_1 = class PrismaService {
20
23
  constructor(prismaClient, permissionsConfig) {
21
24
  this.permissionsConfig = permissionsConfig;
22
25
  this.fieldConfigs = {};
@@ -31,10 +34,13 @@ let PrismaService = class PrismaService {
31
34
  * Exposes the specified models for the duration of the callback execution.
32
35
  */
33
36
  withExposedModels(models, callback) {
34
- const previous = nestjs_request_context_1.RequestContext.currentContext?.req.prismaExposedModels || [];
35
- nestjs_request_context_1.RequestContext.currentContext.req.prismaExposedModels = [...new Set([...previous, ...models.map(m => m.toLowerCase())])];
36
- return callback().finally(() => {
37
- nestjs_request_context_1.RequestContext.currentContext.req.prismaExposedModels = previous;
37
+ const previous = exports.CorePrismaContext.getStore()?.exposedModels || [];
38
+ return new Promise(async (resolve, reject) => {
39
+ exports.CorePrismaContext.run({
40
+ exposedModels: [...new Set([...previous, ...models.map(m => m.toLowerCase())])],
41
+ }, () => {
42
+ callback().then(resolve).catch(reject);
43
+ });
38
44
  });
39
45
  }
40
46
  debug(msg, type = 'log') {
@@ -242,7 +248,8 @@ let PrismaService = class PrismaService {
242
248
  const permissions = permissionsConfig[normalizedName]?.[userRole];
243
249
  let actionPermissions;
244
250
  // If model is exposed, permissions is ALL
245
- if (nestjs_request_context_1.RequestContext.currentContext?.req.prismaExposedModels?.map((m) => m.toLowerCase()).includes(modelName.toLowerCase())) {
251
+ const exposedModels = (exports.CorePrismaContext.getStore()?.exposedModels || []).map((m) => m.toLowerCase());
252
+ if (exposedModels.includes(modelName.toLowerCase())) {
246
253
  actionPermissions = 'ALL';
247
254
  }
248
255
  else {
@@ -271,7 +278,7 @@ let PrismaService = class PrismaService {
271
278
  // findFirst permissions for related model
272
279
  const relatedPermissions = this.selectPermission(permissionsConfig[relation.model.toLowerCase()]?.[userRole] || {}, 'findFirst', relation.model, userRole);
273
280
  // If model is exposed, do not apply conditions
274
- if (nestjs_request_context_1.RequestContext.currentContext?.req.prismaExposedModels?.map((m) => m.toLowerCase()).includes(relation.model.toLowerCase())) {
281
+ if (exposedModels.includes(relation.model.toLowerCase())) {
275
282
  this.debug(`Related model '${relation.model}' is exposed via @Permission() decorator. Skipping conditions for action '${String(action)}' on role ${userRole}.`);
276
283
  continue;
277
284
  }
@@ -319,7 +326,7 @@ let PrismaService = class PrismaService {
319
326
  for (const entry of belongsToQueue) {
320
327
  const { relatedPermissions, field, relation } = entry;
321
328
  this.debug(`Merging conditions for belongsTo relation field '${field}': ${JSON.stringify(relatedPermissions?.conditions)}`, 'info');
322
- let belongsToConditions = this.buildConditions(relatedPermissions?.conditions, user);
329
+ let belongsToConditions = PrismaService_1._buildConditions(relatedPermissions?.conditions, user);
323
330
  if (!args.where)
324
331
  args.where = {};
325
332
  // If relation is optional allow for non-matching records
@@ -355,7 +362,7 @@ let PrismaService = class PrismaService {
355
362
  }
356
363
  }
357
364
  // If model is exposed, do not apply conditions
358
- if (nestjs_request_context_1.RequestContext.currentContext?.req.prismaExposedModels?.map((m) => m.toLowerCase()).includes(modelName.toLowerCase())) {
365
+ if (exposedModels.includes(modelName.toLowerCase())) {
359
366
  this.debug(`Model '${modelName}' is exposed via @Permission() decorator. Skipping conditions for action '${String(action)}' on role ${userRole}.`);
360
367
  return args;
361
368
  }
@@ -363,7 +370,7 @@ let PrismaService = class PrismaService {
363
370
  this.debug(`No conditions to apply for '${modelName}.${String(action)}()' on role ${userRole}`);
364
371
  return args;
365
372
  }
366
- const whereClause = this.buildConditions(actionPermissions.conditions, user);
373
+ const whereClause = PrismaService_1._buildConditions(actionPermissions.conditions, user);
367
374
  this.debug(`Applying where conditions for '${modelName}.${String(action)}()' on role ${userRole}: ${JSON.stringify(whereClause)}`);
368
375
  if (!args.where) {
369
376
  args.where = whereClause;
@@ -381,7 +388,7 @@ let PrismaService = class PrismaService {
381
388
  * @param user - The user object used to replace placeholders.
382
389
  * @returns The constructed `where` clause object.
383
390
  */
384
- buildConditions(conditions, user) {
391
+ static _buildConditions(conditions, user) {
385
392
  const whereClause = {};
386
393
  if (!conditions) {
387
394
  return whereClause;
@@ -399,7 +406,7 @@ let PrismaService = class PrismaService {
399
406
  * @param user - The user object containing values like `id`.
400
407
  * @returns The condition with placeholders replaced by actual values.
401
408
  */
402
- replacePlaceholders(condition, user) {
409
+ static replacePlaceholders(condition, user) {
403
410
  if (condition === null) {
404
411
  return null;
405
412
  }
@@ -529,7 +536,7 @@ let PrismaService = class PrismaService {
529
536
  }
530
537
  };
531
538
  exports.PrismaService = PrismaService;
532
- exports.PrismaService = PrismaService = __decorate([
539
+ exports.PrismaService = PrismaService = PrismaService_1 = __decorate([
533
540
  (0, common_1.Injectable)(),
534
541
  __param(1, (0, common_1.Inject)('PERMISSIONS_CONFIG')),
535
542
  __metadata("design:paramtypes", [Object, Object])
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appxdigital/appx-core",
3
- "version": "0.1.101",
3
+ "version": "0.1.103",
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",
@@ -44,7 +44,13 @@
44
44
  "@types/multer": "^1.4.12",
45
45
  "@types/passport-jwt": "^4.0.1",
46
46
  "@types/passport-local": "^1.0.38",
47
- "typescript": "^5.1.3"
47
+ "typescript": "^5.1.3",
48
+ "@adminjs/express": "^6.1.1",
49
+ "@adminjs/import-export": "^3.0.0",
50
+ "@adminjs/nestjs": "^7.0.0",
51
+ "@adminjs/passwords": "^4.0.0",
52
+ "@adminjs/prisma": "^5.0.4",
53
+ "@prisma/sdk": "^3.15.2"
48
54
  },
49
55
  "peerDependencies": {
50
56
  "@nestjs/apollo": "^13.0.0",
@@ -58,7 +64,13 @@
58
64
  "@prisma/client": "^6.5.0",
59
65
  "nestjs-request-context": "^4.0.0",
60
66
  "reflect-metadata": "^0.2.0",
61
- "rxjs": "^7.8.1"
67
+ "rxjs": "^7.8.1",
68
+ "@adminjs/express": "^6.1.1",
69
+ "@adminjs/import-export": "^3.0.0",
70
+ "@adminjs/nestjs": "^7.0.0",
71
+ "@adminjs/passwords": "^4.0.0",
72
+ "@adminjs/prisma": "^5.0.4",
73
+ "@prisma/sdk": "^3.15.2"
62
74
  },
63
75
  "overrides": {
64
76
  "rimraf": "^4.0.0",
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=create.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../../../src/config/admin/create.ts"],"names":[],"mappings":""}
@@ -1,25 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- const child_process_1 = require("child_process");
7
- const path_1 = __importDefault(require("path"));
8
- const currentProjectPath = process.cwd();
9
- try {
10
- const command = 'npm install adminjs @adminjs/express @adminjs/nestjs @adminjs/prisma @prisma/sdk react express-formidable @adminjs/passwords @adminjs/import-export';
11
- (0, child_process_1.execSync)(command, {
12
- stdio: 'inherit',
13
- cwd: currentProjectPath
14
- });
15
- console.log('Dependencies installed successfully!');
16
- const scriptsDir = path_1.default.join(__dirname);
17
- console.log('Generating Admin...');
18
- (0, child_process_1.execSync)(`node ${path_1.default.join(scriptsDir, 'generate-admin.js')}`, {
19
- stdio: 'inherit',
20
- });
21
- console.log('Admin generated successfully!');
22
- }
23
- catch (error) {
24
- console.error('Error installing dependencies:', error.message);
25
- }
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=generate-admin.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"generate-admin.d.ts","sourceRoot":"","sources":["../../../src/config/admin/generate-admin.ts"],"names":[],"mappings":""}
@@ -1,75 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- const fs_1 = __importDefault(require("fs"));
7
- const path_1 = __importDefault(require("path"));
8
- const admin_template_1 = require("./templates/admin.template");
9
- const utils_template_1 = require("./templates/utils.template");
10
- const dashboard_template_1 = require("./templates/dashboard.template");
11
- const component_loader_template_1 = require("./templates/component-loader.template");
12
- const scriptsDir = path_1.default.join(__dirname, "/templates");
13
- function ensureDirSync(dirPath) {
14
- if (!fs_1.default.existsSync(dirPath)) {
15
- fs_1.default.mkdirSync(dirPath, { recursive: true });
16
- }
17
- }
18
- function getTemplateContent(templateFileName) {
19
- const templatePath = path_1.default.join(scriptsDir, templateFileName);
20
- return fs_1.default.readFileSync(templatePath, 'utf8');
21
- }
22
- function setupAdminJS() {
23
- const projectPath = process.cwd();
24
- const backofficePath = path_1.default.join(projectPath, 'src/backoffice');
25
- ensureDirSync(backofficePath);
26
- const backofficeComponentsPath = path_1.default.join(backofficePath, 'components');
27
- ensureDirSync(backofficeComponentsPath);
28
- fs_1.default.writeFileSync(path_1.default.join(backofficePath, 'component-loader.ts'), component_loader_template_1.componentLoaderTemplate);
29
- fs_1.default.writeFileSync(path_1.default.join(backofficeComponentsPath, 'dashboard.tsx'), dashboard_template_1.dashboardTemplate);
30
- fs_1.default.writeFileSync(path_1.default.join(backofficePath, 'utils.ts'), utils_template_1.permissionsUtilsContent);
31
- fs_1.default.writeFileSync(path_1.default.join(backofficePath, 'admin.ts'), admin_template_1.adminJsModuleCode);
32
- const tsConfigPath = path_1.default.join(projectPath, 'tsconfig.json');
33
- if (fs_1.default.existsSync(tsConfigPath)) {
34
- const tsConfigData = fs_1.default.readFileSync(tsConfigPath, 'utf8');
35
- const tsConfig = JSON.parse(tsConfigData);
36
- tsConfig.compilerOptions = tsConfig.compilerOptions || {};
37
- tsConfig.compilerOptions.jsx = 'react';
38
- fs_1.default.writeFileSync(tsConfigPath, JSON.stringify(tsConfig, null, 2));
39
- console.log('Updated tsconfig.json with JSX support.');
40
- }
41
- else {
42
- console.error('tsconfig.json not found. Please ensure your project has TypeScript configured.');
43
- }
44
- insertInAppModuleImports();
45
- console.log('Backoffice setup complete!');
46
- }
47
- function insertInAppModuleImports() {
48
- const projectRoot = process.cwd();
49
- const appModulePath = path_1.default.join(projectRoot, 'src', 'app.module.ts');
50
- if (!fs_1.default.existsSync(appModulePath)) {
51
- console.error(`Could not find app.module.ts at ${appModulePath}`);
52
- return;
53
- }
54
- let appModuleContent = fs_1.default.readFileSync(appModulePath, 'utf8');
55
- const importStatement = 'import {createAdminJsModule} from "./backoffice/admin";';
56
- if (!appModuleContent.includes(importStatement)) {
57
- appModuleContent = `${importStatement}\n${appModuleContent}`;
58
- }
59
- const importsRegex = /imports\s*:\s*\[\s*([\s\S]*?)\s*\]/;
60
- if (importsRegex.test(appModuleContent)) {
61
- appModuleContent = appModuleContent.replace(importsRegex, (match, importsContent) => {
62
- const trimmedImportsContent = importsContent.trim();
63
- if (!trimmedImportsContent.includes('createAdminJsModule')) {
64
- return `imports: [${trimmedImportsContent}${trimmedImportsContent.endsWith(',') ? '' : ','} \ncreateAdminJsModule().then((AdminJsModule: any) => AdminJsModule)]`;
65
- }
66
- return match;
67
- });
68
- fs_1.default.writeFileSync(appModulePath, appModuleContent, 'utf8');
69
- console.log('createAdminJsModule has been added to the imports array.');
70
- }
71
- else {
72
- console.error('Could not find the imports array in app.module.ts');
73
- }
74
- }
75
- setupAdminJS();
@@ -1,2 +0,0 @@
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
- //# sourceMappingURL=admin.template.d.ts.map
@@ -1 +0,0 @@
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"}
@@ -1,131 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.adminJsModuleCode = void 0;
4
- exports.adminJsModuleCode = `
5
- import {DynamicModule} from '@nestjs/common';
6
- import {addBasicFilters, createActions, createPermissionHandler, dynamicImport, getAdminJSResources} from './utils';
7
- import {initializeComponents} from './component-loader';
8
- import {readFileSync} from 'fs';
9
- import {getDMMF} from '@prisma/sdk';
10
- import {PrismaService} from '@appxdigital/appx-core';
11
- import {PrismaModule} from "../prisma/prisma.module";
12
-
13
- const DEFAULT_ADMIN = {
14
- email: 'joao.duvido@appx.pt',
15
- password: 'password',
16
- };
17
-
18
- const authenticate = async (email: string, password: string) => {
19
- if (email === DEFAULT_ADMIN.email && password === DEFAULT_ADMIN.password) {
20
- return Promise.resolve(DEFAULT_ADMIN);
21
- }
22
- return null;
23
- };
24
-
25
- export async function createAdminJsModule(): Promise<DynamicModule> {
26
- const {default: AdminJS} = await dynamicImport('adminjs');
27
- const {Database, Resource} = await dynamicImport('@adminjs/prisma');
28
- const {AdminModule} = await dynamicImport('@adminjs/nestjs');
29
- const {default: importExportFeature} = await dynamicImport('@adminjs/import-export');
30
- const {default: passwordFeature} = await dynamicImport('@adminjs/passwords');
31
- const argon2 = await dynamicImport('argon2');
32
-
33
- const resources = getAdminJSResources();
34
- const {componentLoader, Components} = await initializeComponents();
35
- const schemaPath = './prisma/schema.prisma';
36
- const schema = readFileSync(schemaPath, 'utf-8');
37
- const dmmf = await getDMMF({datamodel: schema});
38
-
39
- const models = [];
40
-
41
- for (const resource of resources) {
42
- const model = dmmf.datamodel.models.find(
43
- (model) => model.name === resource.name,
44
- );
45
-
46
- models.push({
47
- model,
48
- options: resource.options,
49
- features: model.name === 'User' ? [
50
- passwordFeature({
51
- properties: {
52
- encryptedPassword: 'password',
53
- password: 'plainPassword',
54
- },
55
- hash: argon2.hash,
56
- componentLoader,
57
- }),
58
- ] : [],
59
- });
60
- }
61
-
62
- AdminJS.registerAdapter({Database, Resource});
63
-
64
- return AdminModule.createAdminAsync({
65
- imports: [PrismaModule],
66
- inject: [PrismaService],
67
- useFactory: async (prisma: PrismaService) => {
68
- const authenticate = async (email: string, password: string) => {
69
- const user = await prisma.user.findFirst({
70
- where: {
71
- email
72
- }
73
- });
74
-
75
- if (!user || user.role !== 'ADMIN') {
76
- return null;
77
- }
78
-
79
- const isPasswordValid = await argon2.verify(user.password, password);
80
-
81
- return isPasswordValid ? Promise.resolve({email: user.email, role: user.role, id: user.id}) : null;
82
- }
83
-
84
- return {
85
- adminJsOptions: {
86
- rootPath: '/admin',
87
- dashboard: {
88
- component: Components.Dashboard,
89
- handler: async () => {
90
- return {some: 'output'};
91
- },
92
- },
93
- branding: {
94
- companyName: 'AppX Core Wizard',
95
- withMadeWithLove: false,
96
- logo: 'https://i.ibb.co/XZNRS5m/appxdigitalcom-logo.jpg',
97
- },
98
- resources: models.map((m) => {
99
- return {
100
- resource: {model: m.model, client: prisma},
101
- options: {
102
- ...m.options,
103
- actions: createActions(),
104
- },
105
- features: [...(m.features || []), importExportFeature({
106
- componentLoader
107
- })],
108
- };
109
- }),
110
- componentLoader,
111
- },
112
- auth: {
113
- authenticate,
114
- cookieName: process.env.SESSION_COOKIE_NAME,
115
- cookiePassword: process.env.SESSION_SECRET,
116
- },
117
- sessionOptions: {
118
- resave: false,
119
- saveUninitialized: true,
120
- secret: process.env.SESSION_SECRET,
121
- cookie: {
122
- httpOnly: process.env.NODE_ENV === 'production',
123
- secure: false,
124
- },
125
- name: process.env.SESSION_COOKIE_NAME,
126
- },
127
- };
128
- },
129
- });
130
- }
131
- `;
@@ -1,2 +0,0 @@
1
- export declare const componentLoaderTemplate = "import { dynamicImport } from \"./utils\";\n\nasync function loadComponents() {\n const { ComponentLoader } = await dynamicImport('adminjs');\n const componentLoader = new ComponentLoader();\n\n const Components = {\n Dashboard: componentLoader.add('dashboard', './components/dashboard'),\n };\n\n return { componentLoader, Components };\n}\nexport const initializeComponents = loadComponents;\n";
2
- //# sourceMappingURL=component-loader.template.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"component-loader.template.d.ts","sourceRoot":"","sources":["../../../../src/config/admin/templates/component-loader.template.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,uBAAuB,saAanC,CAAA"}
@@ -1,17 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.componentLoaderTemplate = void 0;
4
- exports.componentLoaderTemplate = `import { dynamicImport } from "./utils";
5
-
6
- async function loadComponents() {
7
- const { ComponentLoader } = await dynamicImport('adminjs');
8
- const componentLoader = new ComponentLoader();
9
-
10
- const Components = {
11
- Dashboard: componentLoader.add('dashboard', './components/dashboard'),
12
- };
13
-
14
- return { componentLoader, Components };
15
- }
16
- export const initializeComponents = loadComponents;
17
- `;
@@ -1,2 +0,0 @@
1
- export declare const dashboardTemplate = "import React from 'react';\n\nexport const Dashboard = () => {\n\n return (\n <div style={{\n backgroundColor: 'white',\n borderRadius: '15px',\n height: '100%',\n padding: '1rem',\n margin: '1rem',\n }}>\n <h1 style={{fontSize: \"1.5rem\", textAlign: \"center\"}}>Dashboard</h1>\n <div style={{\n display: 'flex',\n gap: '1rem',\n flexDirection: 'column',\n textAlign: 'center',\n marginTop: '3rem',\n borderRadius: '15px',\n border: '2px solid gainsboro',\n maxWidth: '300px',\n margin: '3rem auto',\n padding: '1rem',\n }}>\n <h2>Customize it!</h2>\n <p>You can customize your dashboard however you want.</p>\n <p>Display any data you might find useful.</p>\n <p>See the number of users or check statistics on graphics</p>\n <p>Whatever you want to do! Edit it on components/dashboard.</p>\n </div>\n </div>\n\n );\n};\n\nexport default Dashboard;";
2
- //# sourceMappingURL=dashboard.template.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"dashboard.template.d.ts","sourceRoot":"","sources":["../../../../src/config/admin/templates/dashboard.template.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,iBAAiB,srCAoCJ,CAAA"}
@@ -1,40 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.dashboardTemplate = void 0;
4
- exports.dashboardTemplate = `import React from 'react';
5
-
6
- export const Dashboard = () => {
7
-
8
- return (
9
- <div style={{
10
- backgroundColor: 'white',
11
- borderRadius: '15px',
12
- height: '100%',
13
- padding: '1rem',
14
- margin: '1rem',
15
- }}>
16
- <h1 style={{fontSize: "1.5rem", textAlign: "center"}}>Dashboard</h1>
17
- <div style={{
18
- display: 'flex',
19
- gap: '1rem',
20
- flexDirection: 'column',
21
- textAlign: 'center',
22
- marginTop: '3rem',
23
- borderRadius: '15px',
24
- border: '2px solid gainsboro',
25
- maxWidth: '300px',
26
- margin: '3rem auto',
27
- padding: '1rem',
28
- }}>
29
- <h2>Customize it!</h2>
30
- <p>You can customize your dashboard however you want.</p>
31
- <p>Display any data you might find useful.</p>
32
- <p>See the number of users or check statistics on graphics</p>
33
- <p>Whatever you want to do! Edit it on components/dashboard.</p>
34
- </div>
35
- </div>
36
-
37
- );
38
- };
39
-
40
- export default Dashboard;`;
@@ -1,2 +0,0 @@
1
- export declare const permissionsUtilsContent = "\nimport {PermissionsConfig} from \"../config/permissions.config\";\n\nexport const dynamicImport = async (packageName: string) =>\n new Function(`return import('${packageName}')`)();\n\nexport const getAdminJSResources = (specific = null) => {\n const resources = [\n {\n name: 'User',\n options: {},\n },\n ];\n\n return specific ? resources.filter((r) => r.name === specific) : resources;\n};\n\nexport function createPermissionHandler(role: string, resource: string, action: string) {\n const rolePermissions = PermissionsConfig[resource]?.[role];\n\n if (!rolePermissions) {\n return () => false;\n }\n\n const actionMapping = {\n list : 'findMany',\n show : 'findUnique',\n edit : 'update',\n delete : 'delete',\n new : 'create',\n };\n\n const mappedAction = actionMapping[action];\n\n if (!mappedAction || !rolePermissions[mappedAction]) {\n return () => false;\n }\n\n if (rolePermissions[mappedAction] === 'ALL') {\n return () => true;\n }\n\n if (typeof rolePermissions[mappedAction] === 'object') {\n\n return (requestContext) => {\n // @ts-ignore\n const clauses = rolePermissions[mappedAction]?.clauses;\n if (!clauses) return () => false;\n return parseClauses(clauses)(requestContext);\n };\n }\n\n return () => false;\n}\n\nconst clauseMapping = {\n '$USER_ID': \"id\",\n '$USER_EMAIL': \"email\",\n};\n\nconst parseClauses = (clauses) => {\n return (requestContext) => {\n if (!clauses || !Array.isArray(clauses)) return true;\n\n for (const clause of clauses) {\n if (clause.type === 'field') {\n for (const [field, value] of Object.entries(clause.conditions)) {\n let actualValue = value;\n\n if (typeof value === 'string' && value.startsWith('$')) {\n actualValue = requestContext?.currentAdmin[clauseMapping[value]] ?? null;\n }\n\n if (requestContext.record?.param(field) !== actualValue) {\n return false;\n }\n }\n }\n }\n return true;\n };\n};\n\nexport const addBasicFilters = (filters) => {\n return async (request, context) => {\n for (const [field, value] of Object.entries(filters)) {\n request.query[`filters.${field}`] = value;\n }\n return request;\n };\n};\n\nexport const createActions = () => {\n return {\n list: {\n isAccessible: createIsAccessible('list'),\n },\n show: {\n isAccessible: createIsAccessible('show'),\n },\n edit: {\n isAccessible: createIsAccessible('edit'),\n },\n delete: {\n isAccessible: createIsAccessible('delete'),\n },\n new: {\n isAccessible: createIsAccessible('new'),\n },\n };\n};\n\nconst createIsAccessible = (action) => {\n return (context) => {\n if (!context.currentAdmin) return false;\n const { role } = context.currentAdmin;\n return createPermissionHandler(role, context.resource.model.name, action)(context);\n };\n};\n\nconst createBeforeHook = (filters) => {\n return (request, context) => {\n if (context.currentAdmin) {\n request.query.filters = {\n ...request.query.filters,\n ...filters,\n };\n }\n return request;\n };\n};\n";
2
- //# sourceMappingURL=utils.template.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"utils.template.d.ts","sourceRoot":"","sources":["../../../../src/config/admin/templates/utils.template.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,uBAAuB,uiHAmInC,CAAC"}
@@ -1,135 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.permissionsUtilsContent = void 0;
4
- exports.permissionsUtilsContent = `
5
- import {PermissionsConfig} from "../config/permissions.config";
6
-
7
- export const dynamicImport = async (packageName: string) =>
8
- new Function(\`return import('\${packageName}')\`)();
9
-
10
- export const getAdminJSResources = (specific = null) => {
11
- const resources = [
12
- {
13
- name: 'User',
14
- options: {},
15
- },
16
- ];
17
-
18
- return specific ? resources.filter((r) => r.name === specific) : resources;
19
- };
20
-
21
- export function createPermissionHandler(role: string, resource: string, action: string) {
22
- const rolePermissions = PermissionsConfig[resource]?.[role];
23
-
24
- if (!rolePermissions) {
25
- return () => false;
26
- }
27
-
28
- const actionMapping = {
29
- list : 'findMany',
30
- show : 'findUnique',
31
- edit : 'update',
32
- delete : 'delete',
33
- new : 'create',
34
- };
35
-
36
- const mappedAction = actionMapping[action];
37
-
38
- if (!mappedAction || !rolePermissions[mappedAction]) {
39
- return () => false;
40
- }
41
-
42
- if (rolePermissions[mappedAction] === 'ALL') {
43
- return () => true;
44
- }
45
-
46
- if (typeof rolePermissions[mappedAction] === 'object') {
47
-
48
- return (requestContext) => {
49
- // @ts-ignore
50
- const clauses = rolePermissions[mappedAction]?.clauses;
51
- if (!clauses) return () => false;
52
- return parseClauses(clauses)(requestContext);
53
- };
54
- }
55
-
56
- return () => false;
57
- }
58
-
59
- const clauseMapping = {
60
- '$USER_ID': "id",
61
- '$USER_EMAIL': "email",
62
- };
63
-
64
- const parseClauses = (clauses) => {
65
- return (requestContext) => {
66
- if (!clauses || !Array.isArray(clauses)) return true;
67
-
68
- for (const clause of clauses) {
69
- if (clause.type === 'field') {
70
- for (const [field, value] of Object.entries(clause.conditions)) {
71
- let actualValue = value;
72
-
73
- if (typeof value === 'string' && value.startsWith('$')) {
74
- actualValue = requestContext?.currentAdmin[clauseMapping[value]] ?? null;
75
- }
76
-
77
- if (requestContext.record?.param(field) !== actualValue) {
78
- return false;
79
- }
80
- }
81
- }
82
- }
83
- return true;
84
- };
85
- };
86
-
87
- export const addBasicFilters = (filters) => {
88
- return async (request, context) => {
89
- for (const [field, value] of Object.entries(filters)) {
90
- request.query[\`filters.\${field}\`] = value;
91
- }
92
- return request;
93
- };
94
- };
95
-
96
- export const createActions = () => {
97
- return {
98
- list: {
99
- isAccessible: createIsAccessible('list'),
100
- },
101
- show: {
102
- isAccessible: createIsAccessible('show'),
103
- },
104
- edit: {
105
- isAccessible: createIsAccessible('edit'),
106
- },
107
- delete: {
108
- isAccessible: createIsAccessible('delete'),
109
- },
110
- new: {
111
- isAccessible: createIsAccessible('new'),
112
- },
113
- };
114
- };
115
-
116
- const createIsAccessible = (action) => {
117
- return (context) => {
118
- if (!context.currentAdmin) return false;
119
- const { role } = context.currentAdmin;
120
- return createPermissionHandler(role, context.resource.model.name, action)(context);
121
- };
122
- };
123
-
124
- const createBeforeHook = (filters) => {
125
- return (request, context) => {
126
- if (context.currentAdmin) {
127
- request.query.filters = {
128
- ...request.query.filters,
129
- ...filters,
130
- };
131
- }
132
- return request;
133
- };
134
- };
135
- `;