@appxdigital/appx-core 0.1.68

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 (196) hide show
  1. package/dist/appx-core.module.d.ts +7 -0
  2. package/dist/appx-core.module.d.ts.map +1 -0
  3. package/dist/appx-core.module.js +46 -0
  4. package/dist/common/config/default-permissions.config.d.ts +3 -0
  5. package/dist/common/config/default-permissions.config.d.ts.map +1 -0
  6. package/dist/common/config/default-permissions.config.js +6 -0
  7. package/dist/common/config/permissions.config.provider.d.ts +8 -0
  8. package/dist/common/config/permissions.config.provider.d.ts.map +1 -0
  9. package/dist/common/config/permissions.config.provider.js +12 -0
  10. package/dist/common/config/permissions.service.d.ts +7 -0
  11. package/dist/common/config/permissions.service.d.ts.map +1 -0
  12. package/dist/common/config/permissions.service.js +31 -0
  13. package/dist/common/config/permissionsConfigTypes.d.ts +18 -0
  14. package/dist/common/config/permissionsConfigTypes.d.ts.map +1 -0
  15. package/dist/common/config/permissionsConfigTypes.js +2 -0
  16. package/dist/common/contants.d.ts +3 -0
  17. package/dist/common/contants.d.ts.map +1 -0
  18. package/dist/common/contants.js +5 -0
  19. package/dist/common/decorators/guard.decorator.d.ts +5 -0
  20. package/dist/common/decorators/guard.decorator.d.ts.map +1 -0
  21. package/dist/common/decorators/guard.decorator.js +22 -0
  22. package/dist/common/decorators/permission.decorator.d.ts +3 -0
  23. package/dist/common/decorators/permission.decorator.d.ts.map +1 -0
  24. package/dist/common/decorators/permission.decorator.js +7 -0
  25. package/dist/common/decorators/transaction.decorator.d.ts +8 -0
  26. package/dist/common/decorators/transaction.decorator.d.ts.map +1 -0
  27. package/dist/common/decorators/transaction.decorator.js +12 -0
  28. package/dist/common/enums/role.enum.d.ts +6 -0
  29. package/dist/common/enums/role.enum.d.ts.map +1 -0
  30. package/dist/common/enums/role.enum.js +9 -0
  31. package/dist/common/guards/rbac.guard.d.ts +10 -0
  32. package/dist/common/guards/rbac.guard.d.ts.map +1 -0
  33. package/dist/common/guards/rbac.guard.js +56 -0
  34. package/dist/common/guards/role.guard.d.ts +12 -0
  35. package/dist/common/guards/role.guard.d.ts.map +1 -0
  36. package/dist/common/guards/role.guard.js +44 -0
  37. package/dist/common/interceptors/file.interceptor.d.ts +9 -0
  38. package/dist/common/interceptors/file.interceptor.d.ts.map +1 -0
  39. package/dist/common/interceptors/file.interceptor.js +74 -0
  40. package/dist/common/interceptors/prisma.interceptor.d.ts +14 -0
  41. package/dist/common/interceptors/prisma.interceptor.d.ts.map +1 -0
  42. package/dist/common/interceptors/prisma.interceptor.js +82 -0
  43. package/dist/common/interfaces/file-upload.interface.d.ts +13 -0
  44. package/dist/common/interfaces/file-upload.interface.d.ts.map +1 -0
  45. package/dist/common/interfaces/file-upload.interface.js +2 -0
  46. package/dist/common/interfaces/storage-service.interface.d.ts +4 -0
  47. package/dist/common/interfaces/storage-service.interface.d.ts.map +1 -0
  48. package/dist/common/interfaces/storage-service.interface.js +2 -0
  49. package/dist/common/interfaces/user.interface.d.ts +12 -0
  50. package/dist/common/interfaces/user.interface.d.ts.map +1 -0
  51. package/dist/common/interfaces/user.interface.js +2 -0
  52. package/dist/common/providers/aws-storage.service.d.ts +10 -0
  53. package/dist/common/providers/aws-storage.service.d.ts.map +1 -0
  54. package/dist/common/providers/aws-storage.service.js +60 -0
  55. package/dist/common/providers/gcp-storage.service.d.ts +10 -0
  56. package/dist/common/providers/gcp-storage.service.d.ts.map +1 -0
  57. package/dist/common/providers/gcp-storage.service.js +87 -0
  58. package/dist/common/providers/local-storage.service.d.ts +7 -0
  59. package/dist/common/providers/local-storage.service.d.ts.map +1 -0
  60. package/dist/common/providers/local-storage.service.js +80 -0
  61. package/dist/common/types.d.ts +21 -0
  62. package/dist/common/types.d.ts.map +1 -0
  63. package/dist/common/types.js +3 -0
  64. package/dist/common/utils/context-transformer.util.d.ts +6 -0
  65. package/dist/common/utils/context-transformer.util.d.ts.map +1 -0
  66. package/dist/common/utils/context-transformer.util.js +28 -0
  67. package/dist/common/utils/error-handler.d.ts +2 -0
  68. package/dist/common/utils/error-handler.d.ts.map +1 -0
  69. package/dist/common/utils/error-handler.js +38 -0
  70. package/dist/config/admin/create.d.ts +2 -0
  71. package/dist/config/admin/create.d.ts.map +1 -0
  72. package/dist/config/admin/create.js +25 -0
  73. package/dist/config/admin/generate-admin.d.ts +2 -0
  74. package/dist/config/admin/generate-admin.d.ts.map +1 -0
  75. package/dist/config/admin/generate-admin.js +75 -0
  76. package/dist/config/admin/templates/admin.template.d.ts +2 -0
  77. package/dist/config/admin/templates/admin.template.d.ts.map +1 -0
  78. package/dist/config/admin/templates/admin.template.js +131 -0
  79. package/dist/config/admin/templates/component-loader.template.d.ts +2 -0
  80. package/dist/config/admin/templates/component-loader.template.d.ts.map +1 -0
  81. package/dist/config/admin/templates/component-loader.template.js +17 -0
  82. package/dist/config/admin/templates/dashboard.template.d.ts +2 -0
  83. package/dist/config/admin/templates/dashboard.template.d.ts.map +1 -0
  84. package/dist/config/admin/templates/dashboard.template.js +40 -0
  85. package/dist/config/admin/templates/utils.template.d.ts +2 -0
  86. package/dist/config/admin/templates/utils.template.d.ts.map +1 -0
  87. package/dist/config/admin/templates/utils.template.js +135 -0
  88. package/dist/config/generate-all.d.ts +3 -0
  89. package/dist/config/generate-all.d.ts.map +1 -0
  90. package/dist/config/generate-all.js +64 -0
  91. package/dist/config/generate-controllers.d.ts +2 -0
  92. package/dist/config/generate-controllers.d.ts.map +1 -0
  93. package/dist/config/generate-controllers.js +83 -0
  94. package/dist/config/generate-modules.d.ts +2 -0
  95. package/dist/config/generate-modules.d.ts.map +1 -0
  96. package/dist/config/generate-modules.js +158 -0
  97. package/dist/config/generate-resolvers.d.ts +2 -0
  98. package/dist/config/generate-resolvers.d.ts.map +1 -0
  99. package/dist/config/generate-resolvers.js +97 -0
  100. package/dist/config/generate-services.d.ts +2 -0
  101. package/dist/config/generate-services.d.ts.map +1 -0
  102. package/dist/config/generate-services.js +72 -0
  103. package/dist/config/generate-session-schema.d.ts +2 -0
  104. package/dist/config/generate-session-schema.d.ts.map +1 -0
  105. package/dist/config/generate-session-schema.js +78 -0
  106. package/dist/config/setup-fileupload.d.ts +3 -0
  107. package/dist/config/setup-fileupload.d.ts.map +1 -0
  108. package/dist/config/setup-fileupload.js +265 -0
  109. package/dist/config/utils.d.ts +3 -0
  110. package/dist/config/utils.d.ts.map +1 -0
  111. package/dist/config/utils.js +55 -0
  112. package/dist/graphql/batch.payload.d.ts +4 -0
  113. package/dist/graphql/batch.payload.d.ts.map +1 -0
  114. package/dist/graphql/batch.payload.js +23 -0
  115. package/dist/graphql/generic.resolver.d.ts +15 -0
  116. package/dist/graphql/generic.resolver.d.ts.map +1 -0
  117. package/dist/graphql/generic.resolver.js +107 -0
  118. package/dist/graphql/graphql.module.d.ts +3 -0
  119. package/dist/graphql/graphql.module.d.ts.map +1 -0
  120. package/dist/graphql/graphql.module.js +28 -0
  121. package/dist/index.d.ts +24 -0
  122. package/dist/index.d.ts.map +1 -0
  123. package/dist/index.js +39 -0
  124. package/dist/modules/auth/auth-field.decorator.d.ts +10 -0
  125. package/dist/modules/auth/auth-field.decorator.d.ts.map +1 -0
  126. package/dist/modules/auth/auth-field.decorator.js +18 -0
  127. package/dist/modules/auth/auth.controller.d.ts +25 -0
  128. package/dist/modules/auth/auth.controller.d.ts.map +1 -0
  129. package/dist/modules/auth/auth.controller.js +158 -0
  130. package/dist/modules/auth/auth.module.d.ts +3 -0
  131. package/dist/modules/auth/auth.module.d.ts.map +1 -0
  132. package/dist/modules/auth/auth.module.js +28 -0
  133. package/dist/modules/auth/auth.resolver.d.ts +6 -0
  134. package/dist/modules/auth/auth.resolver.d.ts.map +1 -0
  135. package/dist/modules/auth/auth.resolver.js +24 -0
  136. package/dist/modules/auth/auth.service.d.ts +28 -0
  137. package/dist/modules/auth/auth.service.d.ts.map +1 -0
  138. package/dist/modules/auth/auth.service.js +197 -0
  139. package/dist/modules/auth/authenticated.guard.d.ts +5 -0
  140. package/dist/modules/auth/authenticated.guard.d.ts.map +1 -0
  141. package/dist/modules/auth/authenticated.guard.js +20 -0
  142. package/dist/modules/auth/dto/register.dto.d.ts +6 -0
  143. package/dist/modules/auth/dto/register.dto.d.ts.map +1 -0
  144. package/dist/modules/auth/dto/register.dto.js +21 -0
  145. package/dist/modules/auth/dto/user.dto.d.ts +5 -0
  146. package/dist/modules/auth/dto/user.dto.d.ts.map +1 -0
  147. package/dist/modules/auth/dto/user.dto.js +36 -0
  148. package/dist/modules/auth/local-auth.guard.d.ts +7 -0
  149. package/dist/modules/auth/local-auth.guard.d.ts.map +1 -0
  150. package/dist/modules/auth/local-auth.guard.js +31 -0
  151. package/dist/modules/auth/local.strategy.d.ts +15 -0
  152. package/dist/modules/auth/local.strategy.d.ts.map +1 -0
  153. package/dist/modules/auth/local.strategy.js +48 -0
  154. package/dist/modules/auth/session/session-auth.guard.d.ts +5 -0
  155. package/dist/modules/auth/session/session-auth.guard.d.ts.map +1 -0
  156. package/dist/modules/auth/session/session-auth.guard.js +30 -0
  157. package/dist/modules/auth/session/session-serializer.d.ts +9 -0
  158. package/dist/modules/auth/session/session-serializer.d.ts.map +1 -0
  159. package/dist/modules/auth/session/session-serializer.js +44 -0
  160. package/dist/modules/auth/session/session-store.d.ts +11 -0
  161. package/dist/modules/auth/session/session-store.d.ts.map +1 -0
  162. package/dist/modules/auth/session/session-store.js +97 -0
  163. package/dist/modules/common.module.d.ts +3 -0
  164. package/dist/modules/common.module.d.ts.map +1 -0
  165. package/dist/modules/common.module.js +20 -0
  166. package/dist/modules/core/core.controller.d.ts +14 -0
  167. package/dist/modules/core/core.controller.d.ts.map +1 -0
  168. package/dist/modules/core/core.controller.js +114 -0
  169. package/dist/modules/core/core.module.d.ts +3 -0
  170. package/dist/modules/core/core.module.d.ts.map +1 -0
  171. package/dist/modules/core/core.module.js +22 -0
  172. package/dist/modules/core/core.service.d.ts +44 -0
  173. package/dist/modules/core/core.service.d.ts.map +1 -0
  174. package/dist/modules/core/core.service.js +121 -0
  175. package/dist/modules/file/file-upload.controller.d.ts +11 -0
  176. package/dist/modules/file/file-upload.controller.d.ts.map +1 -0
  177. package/dist/modules/file/file-upload.controller.js +52 -0
  178. package/dist/modules/file/file-upload.module.d.ts +6 -0
  179. package/dist/modules/file/file-upload.module.d.ts.map +1 -0
  180. package/dist/modules/file/file-upload.module.js +62 -0
  181. package/dist/modules/file/file-upload.service.d.ts +27 -0
  182. package/dist/modules/file/file-upload.service.d.ts.map +1 -0
  183. package/dist/modules/file/file-upload.service.js +69 -0
  184. package/dist/modules/user/user.module.d.ts +3 -0
  185. package/dist/modules/user/user.module.d.ts.map +1 -0
  186. package/dist/modules/user/user.module.js +20 -0
  187. package/dist/modules/user/user.service.d.ts +10 -0
  188. package/dist/modules/user/user.service.d.ts.map +1 -0
  189. package/dist/modules/user/user.service.js +49 -0
  190. package/dist/prisma/prisma.service.d.ts +128 -0
  191. package/dist/prisma/prisma.service.d.ts.map +1 -0
  192. package/dist/prisma/prisma.service.js +512 -0
  193. package/dist/tasks/session-cleanup.service.d.ts +7 -0
  194. package/dist/tasks/session-cleanup.service.d.ts.map +1 -0
  195. package/dist/tasks/session-cleanup.service.js +38 -0
  196. package/package.json +66 -0
@@ -0,0 +1,512 @@
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
+ var __metadata = (this && this.__metadata) || function (k, v) {
42
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
43
+ };
44
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
45
+ return function (target, key) { decorator(target, key, paramIndex); }
46
+ };
47
+ Object.defineProperty(exports, "__esModule", { value: true });
48
+ exports.PrismaService = void 0;
49
+ const common_1 = require("@nestjs/common");
50
+ const client_1 = require("@prisma/client");
51
+ const path = __importStar(require("path"));
52
+ const fs = __importStar(require("fs"));
53
+ const nestjs_request_context_1 = require("nestjs-request-context");
54
+ let PrismaService = class PrismaService {
55
+ constructor(prismaClient, permissionsConfig) {
56
+ this.permissionsConfig = permissionsConfig;
57
+ this.fieldConfigs = {};
58
+ this.schemaPath = '';
59
+ this.prismaClient = prismaClient;
60
+ this.parseSchema();
61
+ this.proxyModels();
62
+ }
63
+ $transaction(fn) {
64
+ return this.prismaClient.$transaction(fn);
65
+ }
66
+ proxyModels() {
67
+ // Proxy client to intercept model calls
68
+ this.prismaClient = new Proxy(this.prismaClient, {
69
+ get: (target, propKey) => {
70
+ const modelDelegate = target[propKey];
71
+ if (typeof modelDelegate === 'object' && modelDelegate !== null && !['$connect', '$disconnect', '$use', '$on'].includes(propKey.toString()) && !propKey.toString().startsWith?.('_') && !propKey.toString().startsWith?.('$')) {
72
+ // Proxy model methods to apply field omission and where conditions
73
+ return new Proxy(modelDelegate, {
74
+ get: (model, methodKey) => {
75
+ const user = nestjs_request_context_1.RequestContext.currentContext?.req.user || {};
76
+ const userRole = user?.role || 'GUEST';
77
+ if (typeof model[methodKey] === 'function' && !methodKey.toString().startsWith('_') && !methodKey.toString().startsWith('$')) {
78
+ const contextModel = nestjs_request_context_1.RequestContext.currentContext?.req.prisma?.[propKey] || model;
79
+ return async (params, options) => {
80
+ // delete, deleteMany, update and updateMany methods should not apply field omission because they are not selecting fields
81
+ if (!options?.BYPASS_OMISSION && !['delete', 'deleteMany', 'update', 'updateMany'].includes(methodKey.toString()))
82
+ params = this.applyFieldOmission(String(propKey), userRole, params);
83
+ if (!options?.BYPASS_FILTERING) {
84
+ params = this.applyWhereConditions(String(propKey), userRole, params, user, methodKey);
85
+ // findUnique should be findFirst for where conditions to work properly
86
+ if (methodKey === 'findUnique')
87
+ methodKey = 'findFirst';
88
+ // delete should become deleteMany for where conditions to work properly
89
+ if (methodKey === 'delete')
90
+ methodKey = 'deleteMany';
91
+ // update should become updateMany for where conditions to work properly
92
+ if (methodKey === 'update')
93
+ methodKey = 'updateMany';
94
+ }
95
+ if (methodKey === 'count' && !!params.select) {
96
+ delete params.select;
97
+ }
98
+ return contextModel[methodKey](params);
99
+ };
100
+ }
101
+ return model[methodKey];
102
+ },
103
+ });
104
+ }
105
+ return modelDelegate;
106
+ },
107
+ });
108
+ }
109
+ /**
110
+ * Retrieves the model delegate from the Prisma client.
111
+ * Used in the graphql generic resolver
112
+ * @param model
113
+ */
114
+ getModelDelegate(model) {
115
+ const modelName = model.toLowerCase();
116
+ const client = this.prismaClient;
117
+ if (modelName in client) {
118
+ return client[modelName];
119
+ }
120
+ throw new Error(`Model ${model.toString()} not found in PrismaClient.`);
121
+ }
122
+ get model() {
123
+ return new Proxy(nestjs_request_context_1.RequestContext.currentContext?.req.prisma || this.prismaClient, {
124
+ get: (target, prop) => {
125
+ if (prop in target) {
126
+ return target[prop];
127
+ }
128
+ else {
129
+ throw new Error(`Model ${String(prop)} does not exist on PrismaClient`);
130
+ }
131
+ },
132
+ });
133
+ }
134
+ get user() {
135
+ return this.prismaClient.user;
136
+ }
137
+ get session() {
138
+ return this.prismaClient.session;
139
+ }
140
+ /**
141
+ * Parses the Prisma schema to extract model information and field configurations
142
+ * including custom annotations (e.g., @Role(CLIENT)) and enums.
143
+ */
144
+ parseSchema() {
145
+ this.schemaPath = path.join(process.cwd(), 'prisma/schema.prisma');
146
+ const schema = fs.readFileSync(this.schemaPath, 'utf-8');
147
+ const enums = this.extractEnumsFromSchema(schema);
148
+ this.fieldConfigs = this.extractModelsFromSchema(schema, enums);
149
+ }
150
+ /**
151
+ * Extracts enums from the Prisma schema.
152
+ * @param schema - The raw schema content as a string.
153
+ * @returns An array of enum names.
154
+ */
155
+ extractEnumsFromSchema(schema) {
156
+ const enumRegex = /enum\s+(\w+)\s+\{/g;
157
+ const enums = [];
158
+ let match;
159
+ while ((match = enumRegex.exec(schema)) !== null) {
160
+ enums.push(match[1]);
161
+ }
162
+ return enums;
163
+ }
164
+ /**
165
+ * Extracts models and custom annotations from the Prisma schema.
166
+ * @param schema - The raw schema content as a string.
167
+ * @param enums - An array of enum names for determining scalar fields.
168
+ * @returns A parsed object with models and their custom annotations.
169
+ */
170
+ extractModelsFromSchema(schema, enums) {
171
+ const models = {};
172
+ const modelRegex = /model\s+(\w+)\s+\{([\s\S]+?)\}/g;
173
+ let match;
174
+ while ((match = modelRegex.exec(schema)) !== null) {
175
+ const modelName = match[1];
176
+ const modelBody = match[2];
177
+ const { allFields, fieldConfig, fieldTypes, scalarFields, relationFields, } = this.extractFieldsFromModel(modelBody);
178
+ models[modelName.toLowerCase()] = {
179
+ fieldConfig,
180
+ allFields,
181
+ fieldTypes,
182
+ scalarFields,
183
+ relationFields,
184
+ };
185
+ }
186
+ return models;
187
+ }
188
+ /**
189
+ * Extracts fields from the model and looks for custom annotations like @Role.
190
+ * @param modelBody - The body of the model in the schema.
191
+ * @returns A parsed object with fields and their custom annotations.
192
+ */
193
+ extractFieldsFromModel(modelBody) {
194
+ const allFields = [];
195
+ const fieldConfig = {};
196
+ const fieldTypes = {};
197
+ const scalarFields = [];
198
+ const relationFields = [];
199
+ const lines = modelBody.split('\n');
200
+ for (const line of lines) {
201
+ const trimmedLine = line.trim();
202
+ if (!trimmedLine || trimmedLine.startsWith('//')) {
203
+ continue;
204
+ }
205
+ const [codePart, commentPart] = trimmedLine.split('///');
206
+ const codeTokens = codePart.trim().split(/\s+/);
207
+ if (codeTokens.length < 2) {
208
+ continue;
209
+ }
210
+ const fieldName = codeTokens[0];
211
+ const fieldType = codeTokens[1];
212
+ const attributes = codeTokens.slice(2);
213
+ if (commentPart && commentPart.includes('@Role(')) {
214
+ const roleMatch = commentPart.match(/@Role\((.*?)\)/);
215
+ if (roleMatch) {
216
+ const roles = roleMatch[1].split(',').map(role => role.trim());
217
+ fieldConfig[fieldName] = roles;
218
+ }
219
+ }
220
+ allFields.push(fieldName);
221
+ fieldTypes[fieldName] = fieldType;
222
+ let baseType = fieldType.replace('?', '').replace('[]', '');
223
+ if (this.isScalarType(baseType)) {
224
+ scalarFields.push(fieldName);
225
+ }
226
+ else {
227
+ relationFields.push(fieldName);
228
+ }
229
+ }
230
+ return {
231
+ allFields,
232
+ fieldConfig,
233
+ fieldTypes,
234
+ scalarFields,
235
+ relationFields,
236
+ };
237
+ }
238
+ // Helper method to identify scalar types
239
+ isScalarType(fieldType) {
240
+ const scalarTypes = ['Int', 'String', 'Boolean', 'DateTime', 'Float', 'BigInt', 'Decimal', 'Json', 'Bytes', 'Unsupported'];
241
+ return scalarTypes.includes(fieldType);
242
+ }
243
+ /**
244
+ * Applies field omission logic based on the user's role.
245
+ * Fields that the user does not have permission to access are removed from the query.
246
+ *
247
+ * @param modelName - The name of the Prisma model being queried.
248
+ * @param userRole - The current user's role (e.g., 'ADMIN', 'CLIENT').
249
+ * @param args - The query arguments, including `select` or `include`.
250
+ * @returns The modified query arguments with omitted fields.
251
+ */
252
+ applyFieldOmission(modelName, userRole, args) {
253
+ const omitFields = this.getFieldsToOmit(modelName, userRole);
254
+ if (!args) {
255
+ args = {};
256
+ }
257
+ if (args.select) {
258
+ omitFields.forEach((field) => {
259
+ delete args.select[field];
260
+ });
261
+ }
262
+ else if (args.include) {
263
+ args.select = this.buildSelectFields(modelName, omitFields, args.include, userRole);
264
+ delete args.include;
265
+ }
266
+ else {
267
+ args.select = this.buildSelectFields(modelName, omitFields, null, userRole);
268
+ }
269
+ return args;
270
+ }
271
+ get normalizedPermissionsConfig() {
272
+ const normalizedConfig = {};
273
+ for (const model in this.permissionsConfig) {
274
+ normalizedConfig[model.toLowerCase()] = this.permissionsConfig[model];
275
+ }
276
+ return normalizedConfig;
277
+ }
278
+ /**
279
+ * Applies `where` conditions to the query based on the user's role and the action being performed.
280
+ * The conditions are pulled from the `PermissionsConfig`.
281
+ *
282
+ * @param modelName - The name of the model being queried.
283
+ * @param userRole - The role of the user making the request.
284
+ * @param args - The query arguments (including `where`).
285
+ * @param user - The user object containing information like `id`.
286
+ * @param action - The action being performed (e.g., 'find', 'update').
287
+ * @returns The modified query arguments with the appropriate `where` conditions applied.
288
+ * @throws ForbiddenException if no valid `where` conditions are present after applying permissions.
289
+ */
290
+ applyWhereConditions(modelName, userRole, args, user, action) {
291
+ if (!args) {
292
+ args = {};
293
+ }
294
+ const belongsToQueue = [];
295
+ const permissionsConfig = this.normalizedPermissionsConfig;
296
+ const normalizedName = modelName.toLowerCase().trim();
297
+ const permissions = permissionsConfig[normalizedName]?.[userRole];
298
+ for (const model of Object.keys(args.select)) {
299
+ if (!this.fieldConfigs[model.toLowerCase()]) {
300
+ continue;
301
+ }
302
+ const relation = this.getRelationType(modelName, model);
303
+ if (relation.relation === 'belongsTo') {
304
+ const relatedPermissions = permissionsConfig[model.toLowerCase()]?.[userRole]?.[action];
305
+ belongsToQueue.push({
306
+ modelName: model,
307
+ relation,
308
+ relatedPermissions,
309
+ });
310
+ continue;
311
+ }
312
+ if (permissionsConfig[model.toLowerCase()]) {
313
+ this.applyWhereConditions(model, userRole, args.select[model], user, action);
314
+ }
315
+ else {
316
+ throw new common_1.ForbiddenException(`No permissions found for model ${model} and role ${userRole}`);
317
+ }
318
+ }
319
+ if (!permissions) {
320
+ throw new common_1.HttpException(`No permissions found for model ${modelName} and role ${userRole}`, common_1.HttpStatus.FORBIDDEN);
321
+ }
322
+ const actionPermissions = permissions[action];
323
+ if (!actionPermissions || (actionPermissions === 'ALL' && !belongsToQueue?.length) || action === 'create') {
324
+ return args;
325
+ }
326
+ const whereClause = this.buildConditions(actionPermissions.conditions, user);
327
+ if (!args.where) {
328
+ args.where = {};
329
+ }
330
+ args.where = { AND: [args.where, whereClause] };
331
+ //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
332
+ if (Object.keys(args.where).length === 0 && !belongsToQueue?.length) {
333
+ throw new common_1.ForbiddenException(`You are not authorized to access this record`);
334
+ }
335
+ for (const entry of belongsToQueue) {
336
+ const { modelName: relatedModel, relatedPermissions } = entry;
337
+ args.where = {
338
+ ...args.where,
339
+ [relatedModel]: {
340
+ AND: [
341
+ args?.where?.[relatedModel] ?? {},
342
+ this.buildConditions(relatedPermissions?.conditions, user)
343
+ ]
344
+ }
345
+ };
346
+ //TODO This may no longer be of use, since we are defaulting to not returning the parent if it includes a model the user has no access to.
347
+ // if (relatedPermissions?.conditions) {
348
+ // const relatedWhere = this.buildConditions(relatedPermissions.conditions, user);
349
+ // const foreignKey = relation.identifier;
350
+ // const foreignKeyValue = args.where?.[foreignKey];
351
+ // const requiredValue = relatedWhere[foreignKey] || relatedWhere.id;
352
+ //
353
+ //
354
+ // if (requiredValue !== undefined && foreignKeyValue !== requiredValue) {
355
+ // throw new common_1.ForbiddenException(
356
+ // `Access denied: You are requesting ${modelName.toUpperCase()} with an associated ${relatedModel.toUpperCase()}, ` +
357
+ // `but your permissions only allow access to ${relatedModel.toUpperCase()} where ${foreignKey} is ${requiredValue}. ` +
358
+ // `The requested record has ${foreignKey} = ${foreignKeyValue}.`
359
+ // );
360
+ // }
361
+ // }
362
+ }
363
+ return args;
364
+ }
365
+ getRelationType(parentModel, relatedField) {
366
+ //TODO Remove what is now unnecessary since this is no longer used to determine the foreign key and it was very strict due to relying on the assumption that said fk would be [model name]_id
367
+ parentModel = parentModel.toLowerCase();
368
+ relatedField = relatedField.toLowerCase();
369
+ const parentKey = Object.keys(this.fieldConfigs).find(key => key.toLowerCase() === parentModel);
370
+ if (!parentKey)
371
+ return { relation: null, identifier: null };
372
+ const parent = this.fieldConfigs[parentKey];
373
+ const relationKey = parent.relationFields.find((key) => key.toLowerCase() === relatedField);
374
+ if (!relationKey)
375
+ return { relation: null, identifier: null };
376
+ const fieldType = parent.fieldTypes[relationKey];
377
+ if (fieldType.endsWith('[]')) {
378
+ return { relation: 'hasMany', identifier: null };
379
+ }
380
+ const foreignKeyName = `${relatedField}_id`;
381
+ const foreignKey = parent.scalarFields.find((key) => key.toLowerCase() === foreignKeyName);
382
+ if (foreignKey) {
383
+ return { relation: 'belongsTo', identifier: foreignKey };
384
+ }
385
+ return { relation: null, identifier: null };
386
+ }
387
+ /**
388
+ * Builds dynamic conditions based on the type of clause (OR, AND, or field conditions).
389
+ * Each condition is processed, and placeholders (like `$USER_ID`) are replaced with actual values.
390
+ *
391
+ * @param conditions - An array of clauses that define the conditions for the query.
392
+ * @param user - The user object used to replace placeholders.
393
+ * @returns The constructed `where` clause object.
394
+ */
395
+ buildConditions(conditions, user) {
396
+ const whereClause = {};
397
+ if (!conditions) {
398
+ return whereClause;
399
+ }
400
+ for (let field in conditions) {
401
+ whereClause[field] = this.replacePlaceholders(conditions[field], user);
402
+ }
403
+ return whereClause;
404
+ }
405
+ /**
406
+ * Replaces placeholders (like `$USER_ID`) in the condition with actual values from the user object.
407
+ * Handles conditions in the form of strings, arrays, or objects.
408
+ *
409
+ * @param condition - The condition that might contain placeholders.
410
+ * @param user - The user object containing values like `id`.
411
+ * @returns The condition with placeholders replaced by actual values.
412
+ */
413
+ replacePlaceholders(condition, user) {
414
+ if (typeof condition === 'string') {
415
+ if (condition === '$USER_ID') {
416
+ return user.id;
417
+ }
418
+ return condition;
419
+ }
420
+ if (Array.isArray(condition)) {
421
+ return condition.map((item) => this.replacePlaceholders(item, user));
422
+ }
423
+ if (typeof condition === 'object') {
424
+ const replaced = {};
425
+ for (const key in condition) {
426
+ replaced[key] = this.replacePlaceholders(condition[key], user);
427
+ }
428
+ return replaced;
429
+ }
430
+ return condition;
431
+ }
432
+ /**
433
+ * Retrieves the list of fields to omit based on the user's role.
434
+ * It checks the field configurations stored in `fieldConfigs` and compares them against the role.
435
+ *
436
+ * @param modelName - The name of the model being queried.
437
+ * @param role - The role of the user making the request.
438
+ * @returns An array of field names to be omitted from the query.
439
+ */
440
+ getFieldsToOmit(modelName, role) {
441
+ const modelInfo = this.fieldConfigs[modelName.toLowerCase()] || {};
442
+ const fieldConfig = modelInfo.fieldConfig || {};
443
+ return Object.entries(fieldConfig)
444
+ .filter(([_, roles]) => !roles.includes(role))
445
+ .map(([field]) => field);
446
+ }
447
+ /**
448
+ * Builds the `select` object for Prisma queries, omitting fields based on the user's role.
449
+ * It handles both scalar fields and relation fields.
450
+ *
451
+ * @param modelName - The name of the model being queried.
452
+ * @param omitFields - A list of fields to omit based on the user's role.
453
+ * @param includeRelations - The relations to include in the query.
454
+ * @param userRole - The role of the user making the request.
455
+ * @returns A `select` object for Prisma queries.
456
+ */
457
+ buildSelectFields(modelName, omitFields, includeRelations, userRole) {
458
+ const modelInfo = this.fieldConfigs[modelName.toLowerCase()];
459
+ if (!modelInfo) {
460
+ return {};
461
+ }
462
+ const { scalarFields, relationFields } = modelInfo;
463
+ const selectFields = {};
464
+ for (const field of scalarFields) {
465
+ if (!omitFields.includes(field)) {
466
+ selectFields[field] = true;
467
+ }
468
+ }
469
+ if (includeRelations) {
470
+ for (const relationKey in includeRelations) {
471
+ if (!relationFields.includes(relationKey))
472
+ continue;
473
+ let includedArgs = includeRelations[relationKey];
474
+ if (includedArgs === true) {
475
+ includedArgs = {};
476
+ }
477
+ const relatedModelName = this.getRelatedModelName(modelName, relationKey);
478
+ const relatedModelOmitFields = this.getFieldsToOmit(relatedModelName, userRole);
479
+ const relatedSelectFields = this.buildSelectFields(relatedModelName, relatedModelOmitFields, includedArgs.include || null, userRole);
480
+ if (Object.keys(relatedSelectFields).length > 0) {
481
+ selectFields[relationKey] = { select: relatedSelectFields };
482
+ }
483
+ }
484
+ }
485
+ return selectFields;
486
+ }
487
+ /**
488
+ * Retrieves the related model's name for a given relation field.
489
+ * This is used to navigate relations between models when constructing the `select` object.
490
+ *
491
+ * @param parentModelName - The name of the parent model.
492
+ * @param relationKey - The key of the relation field.
493
+ * @returns The name of the related model.
494
+ * @throws Error if the relation field is not found in the parent model.
495
+ */
496
+ getRelatedModelName(parentModelName, relationKey) {
497
+ const modelInfo = this.fieldConfigs[parentModelName.toLowerCase()];
498
+ if (!modelInfo) {
499
+ throw new Error(`Model information not found for ${parentModelName}`);
500
+ }
501
+ if (modelInfo.relationFields.includes(relationKey)) {
502
+ return modelInfo.fieldTypes[relationKey].toLowerCase();
503
+ }
504
+ throw new Error(`Relation key ${relationKey} not found in model ${parentModelName}`);
505
+ }
506
+ };
507
+ exports.PrismaService = PrismaService;
508
+ exports.PrismaService = PrismaService = __decorate([
509
+ (0, common_1.Injectable)(),
510
+ __param(1, (0, common_1.Inject)('PERMISSIONS_CONFIG')),
511
+ __metadata("design:paramtypes", [Object, Object])
512
+ ], PrismaService);
@@ -0,0 +1,7 @@
1
+ import { PrismaService } from '../prisma/prisma.service';
2
+ export declare class SessionCleanupService {
3
+ private prisma;
4
+ constructor(prisma: PrismaService);
5
+ cleanUpExpiredSessions(): Promise<void>;
6
+ }
7
+ //# sourceMappingURL=session-cleanup.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-cleanup.service.d.ts","sourceRoot":"","sources":["../../src/tasks/session-cleanup.service.ts"],"names":[],"mappings":"AAEA,OAAO,EAAC,aAAa,EAAC,MAAM,0BAA0B,CAAC;AAEvD,qBACa,qBAAqB;IAClB,OAAO,CAAC,MAAM;gBAAN,MAAM,EAAE,aAAa;IAGnC,sBAAsB;CAO/B"}
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ 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;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.SessionCleanupService = void 0;
13
+ const common_1 = require("@nestjs/common");
14
+ const schedule_1 = require("@nestjs/schedule");
15
+ const prisma_service_1 = require("../prisma/prisma.service");
16
+ let SessionCleanupService = class SessionCleanupService {
17
+ constructor(prisma) {
18
+ this.prisma = prisma;
19
+ }
20
+ async cleanUpExpiredSessions() {
21
+ await this.prisma.session.deleteMany({
22
+ where: {
23
+ expiresAt: { lte: new Date() },
24
+ },
25
+ });
26
+ }
27
+ };
28
+ exports.SessionCleanupService = SessionCleanupService;
29
+ __decorate([
30
+ (0, schedule_1.Cron)('0 * * * *'),
31
+ __metadata("design:type", Function),
32
+ __metadata("design:paramtypes", []),
33
+ __metadata("design:returntype", Promise)
34
+ ], SessionCleanupService.prototype, "cleanUpExpiredSessions", null);
35
+ exports.SessionCleanupService = SessionCleanupService = __decorate([
36
+ (0, common_1.Injectable)(),
37
+ __metadata("design:paramtypes", [prisma_service_1.PrismaService])
38
+ ], SessionCleanupService);
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "@appxdigital/appx-core",
3
+ "version": "0.1.68",
4
+ "description": "Appx Core is a library that provides a set of tools to help you build your application faster.",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "scripts": {
8
+ "build": "tsc",
9
+ "npm:publish": "rimraf dist && npm run build && npm publish --access=public",
10
+ "db-pull": "npx prisma db pull && prisma-case-format --table-case pascal --field-case camel --enum-case pascal --file prisma/schema.prisma"
11
+ },
12
+ "keywords": [],
13
+ "author": "",
14
+ "license": "ISC",
15
+ "dependencies": {
16
+ "@aws-sdk/client-s3": "^3.688.0",
17
+ "@aws-sdk/types": "^3.686.0",
18
+ "@google-cloud/storage": "^7.14.0",
19
+ "@paljs/plugins": "^8.1.0",
20
+ "@paralleldrive/cuid2": "^2.2.2",
21
+ "@types/bcrypt": "^5.0.2",
22
+ "@types/express": "^5.0.0",
23
+ "@types/express-session": "^1.18.0",
24
+ "argon2": "^0.41.1",
25
+ "class-transformer": "^0.5.1",
26
+ "class-validator": "^0.14.1",
27
+ "express": "^4.21.1",
28
+ "express-session": "^1.18.1",
29
+ "graphql-type-json": "^0.3.2",
30
+ "inquirer": "^12.0.1",
31
+ "multer": "^1.4.5-lts.1",
32
+ "passport": "^0.7.0",
33
+ "passport-local": "^1.0.0",
34
+ "prisma-case-format": "^2.2.1",
35
+ "prisma-nestjs-graphql": "21.1.1"
36
+ },
37
+ "devDependencies": {
38
+ "@prisma/client": "^6.5.0",
39
+ "@types/express-formidable": "^1.2.3",
40
+ "@types/inquirer": "^9.0.7",
41
+ "@types/multer": "^1.4.12",
42
+ "@types/passport-local": "^1.0.38",
43
+ "typescript": "^5.1.3"
44
+ },
45
+ "peerDependencies": {
46
+ "@nestjs/apollo": "^13.0.0",
47
+ "@nestjs/common": "^11.0.0",
48
+ "@nestjs/config": "^4.0.0",
49
+ "@nestjs/core": "^11.0.0",
50
+ "@nestjs/graphql": "^13.1.0",
51
+ "@nestjs/passport": "^11.0.0",
52
+ "@nestjs/platform-express": "^11.0.0",
53
+ "@nestjs/schedule": "^5.0.0",
54
+ "@prisma/client": "^6.5.0",
55
+ "nestjs-request-context": "^4.0.0",
56
+ "reflect-metadata": "^0.2.0",
57
+ "rxjs": "^7.8.1"
58
+ },
59
+ "overrides": {
60
+ "rimraf": "^4.0.0",
61
+ "glob": "^9.0.0"
62
+ },
63
+ "files": [
64
+ "dist"
65
+ ]
66
+ }