@appxdigital/appx-core-cli 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,159 @@
1
+ <p align="center">
2
+ <a href="https://appx-digital.com/" target="_blank">
3
+ <img src="./assets/logo_appx.svg" width="300" alt="Appx Logo" />
4
+ </a>
5
+ </p>
6
+
7
+ ## Description
8
+
9
+ <p style="text-align: left;">
10
+ This project is a foundational Node.js boilerplate built with <a href="https://nestjs.com/" target="_blank">NestJS</a>, <a href="https://www.prisma.io/" target="_blank">Prisma ORM</a>, and <a href="https://graphql.org/" target="_blank">GraphQL</a>. It simplifies the development process by using Prisma ORM as the single source of truth, automatically generating and managing all essential components of your application.
11
+ </p>
12
+
13
+ ## Installation
14
+
15
+ 1. Open your terminal and run the following command:
16
+ ```bash
17
+ npm install appx-core-cli -g
18
+ appx-core create
19
+ ```
20
+ 2. Follow the prompts provided by the setup wizard. This will scaffold a new NestJS project with `appx-core` pre-integrated.
21
+ 3. Open created project in your favorite IDE.
22
+
23
+ ## Configure Database Schema
24
+
25
+ 1. Open the `prisma/schema.prisma` file.
26
+ 2. Modify this file to define your application's specific data models. You can add new models, fields, and relations as needed.
27
+
28
+ Note: If you already have a database created and want to import the structure, use the following command:
29
+
30
+ ```
31
+ npx prisma db pull
32
+ ```
33
+
34
+ **This command will override the schema.prisma file, please ensure that the original configurations and models are kept (you can adapt User and Session model, but it's advised to keep all default attributes)**
35
+
36
+ ## Generate Modules and Prisma Client
37
+
38
+ 1. After editing your schema, generate the modules for each model by running:
39
+ ```bash
40
+ appx-core generate
41
+ ```
42
+ 2. This command also updates the `@prisma/client` library within your `node_modules` to match your defined schema, providing type-safe database access. This command should be run everytime you make a change to your prisma schema.
43
+
44
+ ## Apply Database Migrations
45
+
46
+ 1. To create the necessary database tables and apply your schema changes, run:
47
+ ```bash
48
+ npx prisma migrate dev
49
+ ```
50
+ 2. This will:
51
+ * Create a new SQL migration file reflecting the changes between your current schema and the last migration.
52
+ * Apply the migration to your database.
53
+
54
+ ## Run the Application
55
+
56
+ 1. Start your application in development mode using:
57
+ ```bash
58
+ npm run start:dev
59
+ ```
60
+ 2. The application will start on `http://localhost:3000` or the port specified during your setup, you can change this in the `.env` file.
61
+
62
+ ## Register Your First User
63
+
64
+ 1. To create a user account, send an HTTP `POST` request to the `/auth/register` endpoint.
65
+ 2. The request body must be JSON and include the `email` and `password`:
66
+ ```json
67
+ {
68
+ "email": "email@example.com",
69
+ "password": "password"
70
+ }
71
+ ```
72
+ 3. On success, you'll receive a JSON response confirming registration and containing the new user's details.
73
+
74
+ ## Log In
75
+
76
+ 1. To log in with the registered user, send an HTTP `POST` request to the `/auth/login` endpoint.
77
+ 2. The request body must be JSON and include the same fields used for registration:
78
+ ```json
79
+ {
80
+ "email": "email@example.com",
81
+ "password": "password"
82
+ }
83
+ ```
84
+ 3. On success, you'll receive a JSON response confirming login and containing the user's details.
85
+
86
+ ## Permissions
87
+
88
+ This project uses RBAC to control user access. Permissions are defined in a configuration file and checked automatically before controller actions and during database queries.
89
+
90
+ ### Configuration
91
+
92
+ * Permissions are defined in the `src/config/permissions.config.ts` file.
93
+ * The configuration structure is hierarchical: `ModelName` -> `RoleName` -> `ActionName` -> `Rule`.
94
+
95
+ ```typescript
96
+ export const PermissionsConfig: PermissionsConfigType = {
97
+ ModelName: { // e.g., 'User'; links to controller's entityName
98
+ RoleName: { // e.g., 'ADMIN', 'CLIENT'; matches user roles
99
+ ActionName: Rule, // e.g., 'findUnique', 'update'
100
+ // ... other actions for this role/model
101
+ },
102
+ // ... other roles for this model
103
+ },
104
+ // ... other models
105
+ };
106
+ ```
107
+
108
+ ### Permission Rules & Effects
109
+
110
+ The `Rule` assigned to an action determines if a user with that role can perform the action:
111
+
112
+ * **`'ALL'`:** Grants unrestricted access for the specific action.
113
+ * **Object `{ conditions: { ... } }`:** Grants access *only if* the specified conditions match the data being accessed.
114
+ * These `conditions` (e.g., `{ id: '$USER_ID' }`) are automatically added as `WHERE` clauses to the underlying database query by the `PrismaService`.
115
+ * Placeholders like `$USER_ID` are replaced with the current user's actual data (e.g., `req.user.id`).
116
+ * **Missing Rule:** If no rule (`'ALL'` or an object) is defined for an action under the user's role, access is **denied**.
117
+
118
+ ### Applying Permissions
119
+
120
+ * Protect controller methods by decorating them with `@Permission('actionName')`. This links the method to an `ActionName` in your `permissions.config.ts`.
121
+ * Methods *without* the `@Permission` decorator are not subject to specific action checks by the `RbacGuard` and are allowed access.
122
+
123
+ ```typescript
124
+ @Controller('users')
125
+ export class UserController extends CoreController<User> {
126
+
127
+ @Get(':id')
128
+ @Permission('findUnique') // Checks config for User -> Role -> findUnique
129
+ async findOne(@Param('id') id: string) {
130
+ /* ... */
131
+ }
132
+
133
+ @Get('/public/info')
134
+ // No @Permission decorator - RbacGuard allows by default access for this route
135
+ async getPublicInfo() {
136
+ /* ... */
137
+ }
138
+
139
+ static get entityName(): string {
140
+ return 'User';
141
+ }
142
+ }
143
+ ```
144
+
145
+ ### Adding Permissions for a New Action
146
+
147
+ 1. **Decorate Method & Choose Name:** Add the `@Permission('yourActionName')` decorator to the relevant controller method, choosing a unique string for `'yourActionName'` (e.g., `'publishItem'`, `'resetPassword'`).
148
+
149
+ 2. **Update Config:** Edit `permissions.config.ts`. Add the `actionName` under the appropriate `ModelName` -> `RoleName`(s). Set the rule:
150
+ * Use `'ALL'` for full access.
151
+ * Use an object like `{ conditions: { field: '$USER_ID' } }` for conditional access (enforced by the database query).
152
+
153
+ ```typescript
154
+ // Example: Adding 'publishItem' rule to Article model config
155
+ Article: {
156
+ ADMIN: { /*...,*/ publishItem: 'ALL' },
157
+ EDITOR: { /*...,*/ publishItem: { conditions: { authorId: '$USER_ID' } } },
158
+ },
159
+ ```
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "type": "module",
3
+ "name": "@appxdigital/appx-core-cli",
4
+ "version": "1.0.0",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "npm:publish": "npm publish --access public",
8
+ "test": "echo \"Error: no test specified\" && exit 1"
9
+ },
10
+ "keywords": [],
11
+ "author": "",
12
+ "license": "ISC",
13
+ "bin": {
14
+ "appx-core": "./wizard.js"
15
+ },
16
+ "dependencies": {
17
+ "@inquirer/prompts": "^7.0.0",
18
+ "@nestjs/cli": "^11.0.6",
19
+ "cli-progress": "^3.12.0",
20
+ "commander": "^12.1.0",
21
+ "fs-extra": "^11.2.0",
22
+ "handlebars": "^4.7.8",
23
+ "inquirer": "^12.0.0"
24
+ },
25
+ "description": ""
26
+ }
@@ -0,0 +1,143 @@
1
+ import { DynamicModule } from '@nestjs/common';
2
+ import { addBasicFilters, createActions, createPermissionHandler, dynamicImport, getAdminJSResources } from './utils';
3
+ import { initializeComponents } from './component-loader';
4
+ import { readFileSync } from 'fs';
5
+ import { getDMMF } from '@prisma/sdk';
6
+ import { PrismaService } from '@appxdigital/appx-core';
7
+ import { PrismaModule } from "../prisma/prisma.module";
8
+
9
+ const DEFAULT_ADMIN = {
10
+ email: 'joao.duvido@appx.pt',
11
+ password: 'password',
12
+ };
13
+
14
+ const authenticate = async (email: string, password: string) => {
15
+ if (email === DEFAULT_ADMIN.email && password === DEFAULT_ADMIN.password) {
16
+ return Promise.resolve(DEFAULT_ADMIN);
17
+ }
18
+ return null;
19
+ };
20
+
21
+ export async function createAdminJsModule(): Promise<DynamicModule> {
22
+ // 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
23
+ const { default: AdminJS } = await dynamicImport('adminjs');
24
+ const { Database, Resource } = await dynamicImport('@adminjs/prisma');
25
+ const { AdminModule } = await dynamicImport('@adminjs/nestjs');
26
+ const { default: importExportFeature } = await dynamicImport('@adminjs/import-export');
27
+ const { default: passwordFeature } = await dynamicImport('@adminjs/passwords');
28
+ const argon2 = await dynamicImport('argon2');
29
+
30
+ // Below, this function in src/backoffice/utils.ts is used to get the resources you want to expose to AdminJS
31
+ const resources = getAdminJSResources();
32
+
33
+ // Once you create customized components, you can load them on the componentLoader just like the Dashboard component example
34
+
35
+ const { componentLoader, Components } = await initializeComponents();
36
+
37
+ // This gets the models from the Prisma schema, and then creates the AdminJS resources
38
+ const schemaPath = './prisma/schema.prisma';
39
+ const schema = readFileSync(schemaPath, 'utf-8');
40
+ const dmmf = await getDMMF({ datamodel: schema });
41
+
42
+ const models = [];
43
+
44
+ for (const resource of resources) {
45
+ const model = dmmf.datamodel.models.find(
46
+ (model) => model.name === resource.name,
47
+ );
48
+
49
+ models.push({
50
+ model,
51
+ options: resource.options,
52
+ features: model.name === 'User' ? [
53
+ passwordFeature({
54
+ properties: {
55
+ encryptedPassword: 'password',
56
+ password: 'plainPassword',
57
+ },
58
+ hash: argon2.hash,
59
+ componentLoader,
60
+ }),
61
+ ] : [],
62
+ });
63
+ }
64
+
65
+ AdminJS.registerAdapter({ Database, Resource });
66
+
67
+
68
+ return AdminModule.createAdminAsync({
69
+ imports: [PrismaModule],
70
+ inject: [PrismaService],
71
+ useFactory: async (prisma: PrismaService) => {
72
+
73
+ // If you comment out this function below, you will be able to access the AdminJS with the DEFAULT_ADMIN credentials at the top of this file
74
+ // With that, you can create a new user, and then undo the comment on this function to disable the default admin, using the new user you created
75
+ const authenticate = async (email: string, password: string) => {
76
+ const user = await prisma.user.findUnique({
77
+ where: {
78
+ email
79
+ }
80
+ });
81
+
82
+ if (!user || user.role !== 'ADMIN') {
83
+ return null;
84
+ }
85
+
86
+ const isPasswordValid = await argon2.verify(user.password, password);
87
+
88
+ return isPasswordValid ? Promise.resolve({ email: user.email, role: user.role, id: user.id }) : null;
89
+ }
90
+
91
+ return {
92
+ adminJsOptions: {
93
+ rootPath: '/admin',
94
+ // As you can see below, you can customize the dashboard component, which is the first page you see when you access the AdminJS
95
+ dashboard: {
96
+ component: Components.Dashboard,
97
+ handler: async () => {
98
+ return { some: 'output' };
99
+ },
100
+ },
101
+ branding: {
102
+ // Here you can change the company name, which appears on the browser's tab
103
+ companyName: 'AppX Core Wizard',
104
+ // This removes the "Made with love (...)" from the footer
105
+ withMadeWithLove: false,
106
+ // Here you can change the logo, currently using AppX's as an example
107
+ logo: 'https://i.ibb.co/XZNRS5m/appxdigitalcom-logo.jpg',
108
+ },
109
+ resources: models.map((m) => {
110
+ return {
111
+ resource: { model: m.model, client: prisma },
112
+ options: {
113
+ ...m.options,
114
+ actions: createActions(),
115
+ },
116
+ features: [...(m.features || []), importExportFeature({
117
+ componentLoader
118
+ })],
119
+ };
120
+ }),
121
+
122
+ componentLoader,
123
+ },
124
+ auth: {
125
+ authenticate,
126
+ cookieName: process.env.SESSION_COOKIE_NAME,
127
+ cookiePassword: process.env.SESSION_SECRET,
128
+ },
129
+ sessionOptions: {
130
+ resave: false,
131
+ saveUninitialized: true,
132
+ secret: process.env.SESSION_SECRET,
133
+ cookie: {
134
+ httpOnly: process.env.NODE_ENV === 'production',
135
+ //secure: process.env.NODE_ENV === 'production',
136
+ secure: false,
137
+ },
138
+ name: process.env.SESSION_COOKIE_NAME,
139
+ },
140
+ };
141
+ },
142
+ });
143
+ }
@@ -0,0 +1,48 @@
1
+ import {
2
+ MiddlewareConsumer,
3
+ Module,
4
+ NestModule,
5
+ RequestMethod,
6
+ } from '@nestjs/common';
7
+ import { ConfigModule } from '@nestjs/config';
8
+ import { APP_INTERCEPTOR } from '@nestjs/core';
9
+ import { AppController } from './app.controller';
10
+ import { AppService } from './app.service';
11
+ import { AppxCoreModule, AuthModule, PrismaInterceptor } from '@appxdigital/appx-core';
12
+ import {
13
+ RequestContextModule,
14
+ RequestContextMiddleware,
15
+ } from 'nestjs-request-context';
16
+ import { UserModule } from './modules/user/user.module';
17
+ import { PermissionsConfig } from "./config/permissions.config";
18
+ import { createAdminJsModule } from "./backoffice/admin";
19
+
20
+ @Module({
21
+ imports: [
22
+ RequestContextModule,
23
+ ConfigModule.forRoot({
24
+ isGlobal: true,
25
+ expandVariables: true,
26
+ envFilePath: `.env.${process.env.NODE_ENV || 'development'}`,
27
+ }),
28
+ AppxCoreModule.forRoot(PermissionsConfig),
29
+ AuthModule,
30
+ UserModule,
31
+ createAdminJsModule().then((AdminJsModule) => AdminJsModule),
32
+ ],
33
+ controllers: [AppController],
34
+ providers: [
35
+ AppService,
36
+ {
37
+ provide: APP_INTERCEPTOR,
38
+ useClass: PrismaInterceptor,
39
+ },
40
+ ],
41
+ })
42
+ export class AppModule implements NestModule {
43
+ configure(consumer: MiddlewareConsumer) {
44
+ consumer
45
+ .apply(RequestContextMiddleware)
46
+ .forRoutes({ path: '*', method: RequestMethod.ALL });
47
+ }
48
+ }
@@ -0,0 +1,14 @@
1
+ import { dynamicImport } from "./utils";
2
+
3
+ async function loadComponents() {
4
+ const { ComponentLoader } = await dynamicImport('adminjs');
5
+ const componentLoader = new ComponentLoader();
6
+
7
+ const Components = {
8
+ Dashboard: componentLoader.add('dashboard', './components/dashboard'),
9
+ };
10
+
11
+ return { componentLoader, Components };
12
+ }
13
+
14
+ export const initializeComponents = loadComponents;
@@ -0,0 +1,37 @@
1
+ import React from 'react';
2
+
3
+ export const Dashboard = () => {
4
+
5
+ return (
6
+ <div style={{
7
+ backgroundColor: 'white',
8
+ borderRadius: '15px',
9
+ height: '100%',
10
+ padding: '1rem',
11
+ margin: '1rem',
12
+ }}>
13
+ <h1 style={{ fontSize: "1.5rem", textAlign: "center" }}>Dashboard</h1>
14
+ <div style={{
15
+ display: 'flex',
16
+ gap: '1rem',
17
+ flexDirection: 'column',
18
+ textAlign: 'center',
19
+ marginTop: '3rem',
20
+ borderRadius: '15px',
21
+ border: '2px solid gainsboro',
22
+ maxWidth: '300px',
23
+ margin: '3rem auto',
24
+ padding: '1rem',
25
+ }}>
26
+ <h2>Customize it!</h2>
27
+ <p>You can customize your dashboard however you want.</p>
28
+ <p>Display any data you might find useful.</p>
29
+ <p>See the number of users or check statistics on graphics</p>
30
+ <p>Whatever you want to do! Edit it on components/dashboard.</p>
31
+ </div>
32
+ </div>
33
+
34
+ );
35
+ };
36
+
37
+ export default Dashboard;
@@ -0,0 +1,131 @@
1
+ import { PermissionsConfig } from "../config/permissions.config";
2
+
3
+ export const dynamicImport = async (packageName: string) =>
4
+ new Function(`return import('${packageName}')`)();
5
+
6
+ export const getAdminJSResources = (specific = null) => {
7
+ const resources = [
8
+ {
9
+ name: 'User',
10
+ options: {},
11
+ },
12
+ ];
13
+
14
+ return specific ? resources.filter((r) => r.name === specific) : resources;
15
+ };
16
+
17
+ export function createPermissionHandler(role: string, resource: string, action: string) {
18
+ const rolePermissions = PermissionsConfig[resource]?.[role];
19
+
20
+ if (!rolePermissions) {
21
+ return () => false;
22
+ }
23
+
24
+ const actionMapping = {
25
+ list: 'findMany',
26
+ show: 'findUnique',
27
+ edit: 'update',
28
+ delete: 'delete',
29
+ new: 'create',
30
+ };
31
+
32
+ const mappedAction = actionMapping[action];
33
+
34
+ if (!mappedAction || !rolePermissions[mappedAction]) {
35
+ return () => false;
36
+ }
37
+
38
+ if (rolePermissions[mappedAction] === 'ALL') {
39
+ return () => true;
40
+ }
41
+
42
+ if (typeof rolePermissions[mappedAction] === 'object') {
43
+
44
+ return (requestContext) => {
45
+ // @ts-ignore
46
+ const clauses = rolePermissions[mappedAction]?.clauses;
47
+ if (!clauses) return () => false;
48
+ return parseClauses(clauses)(requestContext);
49
+ };
50
+ }
51
+
52
+ return () => false;
53
+ }
54
+
55
+ const clauseMapping = {
56
+ '$USER_ID': "id",
57
+ '$USER_EMAIL': "email",
58
+ };
59
+
60
+ const parseClauses = (clauses) => {
61
+ return (requestContext) => {
62
+ if (!clauses || !Array.isArray(clauses)) return true;
63
+
64
+ for (const clause of clauses) {
65
+ if (clause.type === 'field') {
66
+ for (const [field, value] of Object.entries(clause.conditions)) {
67
+ let actualValue = value;
68
+
69
+ if (typeof value === 'string' && value.startsWith('$')) {
70
+ actualValue = requestContext?.currentAdmin[clauseMapping[value]] ?? null;
71
+ }
72
+
73
+ if (requestContext.record?.param(field) !== actualValue) {
74
+ return false;
75
+ }
76
+ }
77
+ }
78
+ }
79
+ return true;
80
+ };
81
+ };
82
+
83
+
84
+ export const addBasicFilters = (filters) => {
85
+ return async (request, context) => {
86
+ for (const [field, value] of Object.entries(filters)) {
87
+ request.query[`filters.${field}`] = value;
88
+ }
89
+ return request;
90
+ };
91
+ };
92
+
93
+ export const createActions = () => {
94
+ return {
95
+ list: {
96
+ isAccessible: createIsAccessible('list'),
97
+ },
98
+ show: {
99
+ isAccessible: createIsAccessible('show'),
100
+ },
101
+ edit: {
102
+ isAccessible: createIsAccessible('edit'),
103
+ },
104
+ delete: {
105
+ isAccessible: createIsAccessible('delete'),
106
+ },
107
+ new: {
108
+ isAccessible: createIsAccessible('new'),
109
+ },
110
+ };
111
+ };
112
+
113
+ const createIsAccessible = (action) => {
114
+ return (context) => {
115
+ if (!context.currentAdmin) return false;
116
+ const { role } = context.currentAdmin;
117
+ return createPermissionHandler(role, context.resource.model.name, action)(context);
118
+ };
119
+ };
120
+
121
+ const createBeforeHook = (filters) => {
122
+ return (request, context) => {
123
+ if (context.currentAdmin) {
124
+ request.query.filters = {
125
+ ...request.query.filters,
126
+ ...filters,
127
+ };
128
+ }
129
+ return request;
130
+ };
131
+ };
@@ -0,0 +1,27 @@
1
+ import {
2
+ MiddlewareConsumer, Module, NestModule, RequestMethod,
3
+ } from '@nestjs/common';
4
+ import { AppController } from './app.controller';
5
+ import { AppService } from './app.service';
6
+ import { ConfigModule } from '@nestjs/config';
7
+ import { AuthModule, PrismaInterceptor, AppxCoreModule } from '@appxdigital/appx-core';
8
+ import { APP_INTERCEPTOR } from '@nestjs/core';
9
+ import {
10
+ RequestContextModule, RequestContextMiddleware,
11
+ } from 'nestjs-request-context'
12
+ import { PermissionsConfig } from './config/permissions.config';
13
+
14
+ @Module({
15
+ imports: [RequestContextModule, ConfigModule.forRoot({
16
+ isGlobal: true, expandVariables: true, envFilePath: `.env.${process.env.NODE_ENV || 'development'}`,
17
+ }), AppxCoreModule.forRoot(PermissionsConfig), RequestContextModule, AuthModule,], controllers: [AppController], providers: [AppService, {
18
+ provide: APP_INTERCEPTOR, useClass: PrismaInterceptor,
19
+ },],
20
+ })
21
+ export class AppModule implements NestModule {
22
+ configure(consumer: MiddlewareConsumer) {
23
+ consumer
24
+ .apply(RequestContextMiddleware)
25
+ .forRoutes({ path: '*', method: RequestMethod.ALL });
26
+ }
27
+ }
@@ -0,0 +1,45 @@
1
+ import { NestFactory } from '@nestjs/core';
2
+ import { AppModule } from './app.module';
3
+ import { ConfigService } from '@nestjs/config';
4
+ import * as session from 'express-session';
5
+ import * as passport from 'passport';
6
+ import { PrismaService } from '@appxdigital/appx-core';
7
+ import { ValidationPipe } from '@nestjs/common';
8
+ import { CorePrismaSessionStore } from '@appxdigital/appx-core';
9
+
10
+ async function bootstrap() {
11
+ const app = await NestFactory.create(AppModule);
12
+ const configService = app.get(ConfigService);
13
+ const prismaService = await app.get(PrismaService);
14
+ const secret = configService.get<string>('SESSION_SECRET');
15
+ const port = configService.get<number>('APP_PORT') ?? 3000;
16
+ const sessionTTL = configService.get<number>('SESSION_TTL') || 86400;
17
+ const cookiename = configService.get<string>('SESSION_COOKIE_NAME');
18
+ const isProduction = process.env.NODE_ENV === 'production';
19
+
20
+ app.use(
21
+ session({
22
+ cookie: {
23
+ maxAge: sessionTTL * 1000,
24
+ secure: isProduction,
25
+ httpOnly: isProduction,
26
+ },
27
+ secret: secret ?? 'APPXCORESECRET',
28
+ resave: false,
29
+ name: cookiename,
30
+ saveUninitialized: false,
31
+ store: new CorePrismaSessionStore(prismaService),
32
+ }),
33
+ );
34
+ app.use(passport.initialize());
35
+ app.use(passport.session());
36
+ app.enableCors({
37
+ origin: 'http://localhost:3000',
38
+ credentials: true,
39
+ });
40
+ app.useGlobalPipes(new ValidationPipe({ transform: true }));
41
+ await app.listen(port);
42
+ console.log(`Your application is successfully running on: http://www.localhost:${port} 🚀`);
43
+ }
44
+
45
+ bootstrap();
@@ -0,0 +1,18 @@
1
+ import { PermissionsConfigType } from '@appxdigital/appx-core';
2
+
3
+ export const PermissionsConfig: PermissionsConfigType = {
4
+ User: {
5
+ ADMIN: {
6
+ findUnique: 'ALL',
7
+ findMany: 'ALL',
8
+ create: 'ALL',
9
+ update: 'ALL',
10
+ delete: 'ALL',
11
+ },
12
+ CLIENT: {
13
+ findUnique: {
14
+ conditions: { id: '$USER_ID' }
15
+ },
16
+ }
17
+ },
18
+ };