@solidxai/core 0.1.4 → 0.1.5-beta.1

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 (251) hide show
  1. package/.claude/settings.local.json +8 -0
  2. package/dist/constants/error-messages.d.ts +1 -0
  3. package/dist/constants/error-messages.d.ts.map +1 -1
  4. package/dist/constants/error-messages.js +1 -0
  5. package/dist/constants/error-messages.js.map +1 -1
  6. package/dist/constants.d.ts +3 -3
  7. package/dist/constants.d.ts.map +1 -1
  8. package/dist/constants.js +12 -12
  9. package/dist/constants.js.map +1 -1
  10. package/dist/controllers/otp-authentication.controller.d.ts +1 -4
  11. package/dist/controllers/otp-authentication.controller.d.ts.map +1 -1
  12. package/dist/controllers/otp-authentication.controller.js +1 -1
  13. package/dist/controllers/role-metadata.controller.d.ts +1 -0
  14. package/dist/controllers/role-metadata.controller.d.ts.map +1 -1
  15. package/dist/controllers/role-metadata.controller.js +15 -0
  16. package/dist/controllers/role-metadata.controller.js.map +1 -1
  17. package/dist/controllers/view-metadata.controller.d.ts +1 -0
  18. package/dist/controllers/view-metadata.controller.d.ts.map +1 -1
  19. package/dist/dtos/create-email-template.dto.d.ts.map +1 -1
  20. package/dist/dtos/create-email-template.dto.js.map +1 -1
  21. package/dist/dtos/create-list-of-values.dto.d.ts.map +1 -1
  22. package/dist/dtos/create-list-of-values.dto.js.map +1 -1
  23. package/dist/dtos/create-menu-item-metadata.dto.d.ts.map +1 -1
  24. package/dist/dtos/create-menu-item-metadata.dto.js.map +1 -1
  25. package/dist/dtos/create-role-metadata.dto.d.ts.map +1 -1
  26. package/dist/dtos/create-role-metadata.dto.js.map +1 -1
  27. package/dist/dtos/create-saved-filters.dto.d.ts +1 -0
  28. package/dist/dtos/create-saved-filters.dto.d.ts.map +1 -1
  29. package/dist/dtos/create-saved-filters.dto.js +8 -1
  30. package/dist/dtos/create-saved-filters.dto.js.map +1 -1
  31. package/dist/dtos/create-scheduled-job.dto.d.ts.map +1 -1
  32. package/dist/dtos/create-scheduled-job.dto.js.map +1 -1
  33. package/dist/dtos/create-security-rule.dto.d.ts.map +1 -1
  34. package/dist/dtos/create-security-rule.dto.js.map +1 -1
  35. package/dist/dtos/create-sms-template.dto.d.ts.map +1 -1
  36. package/dist/dtos/create-sms-template.dto.js.map +1 -1
  37. package/dist/dtos/create-view-metadata.dto.d.ts.map +1 -1
  38. package/dist/dtos/create-view-metadata.dto.js.map +1 -1
  39. package/dist/dtos/otp-sign-in.dto.d.ts +1 -1
  40. package/dist/dtos/otp-sign-in.dto.d.ts.map +1 -1
  41. package/dist/dtos/otp-sign-in.dto.js +2 -2
  42. package/dist/dtos/otp-sign-in.dto.js.map +1 -1
  43. package/dist/dtos/otp-sign-up.dto.d.ts +2 -2
  44. package/dist/dtos/otp-sign-up.dto.d.ts.map +1 -1
  45. package/dist/dtos/otp-sign-up.dto.js +2 -2
  46. package/dist/dtos/otp-sign-up.dto.js.map +1 -1
  47. package/dist/dtos/resolve-s3-url.dto.d.ts +2 -5
  48. package/dist/dtos/resolve-s3-url.dto.d.ts.map +1 -1
  49. package/dist/dtos/resolve-s3-url.dto.js +1 -13
  50. package/dist/dtos/resolve-s3-url.dto.js.map +1 -1
  51. package/dist/dtos/sign-up.dto.d.ts.map +1 -1
  52. package/dist/dtos/sign-up.dto.js.map +1 -1
  53. package/dist/dtos/update-email-template.dto.d.ts.map +1 -1
  54. package/dist/dtos/update-email-template.dto.js.map +1 -1
  55. package/dist/dtos/update-list-of-values.dto.d.ts.map +1 -1
  56. package/dist/dtos/update-list-of-values.dto.js.map +1 -1
  57. package/dist/dtos/update-menu-item-metadata.dto.d.ts.map +1 -1
  58. package/dist/dtos/update-menu-item-metadata.dto.js.map +1 -1
  59. package/dist/dtos/update-saved-filters.dto.d.ts +1 -0
  60. package/dist/dtos/update-saved-filters.dto.d.ts.map +1 -1
  61. package/dist/dtos/update-saved-filters.dto.js +10 -1
  62. package/dist/dtos/update-saved-filters.dto.js.map +1 -1
  63. package/dist/dtos/update-scheduled-job.dto.d.ts.map +1 -1
  64. package/dist/dtos/update-scheduled-job.dto.js.map +1 -1
  65. package/dist/dtos/update-security-rule.dto.d.ts.map +1 -1
  66. package/dist/dtos/update-security-rule.dto.js.map +1 -1
  67. package/dist/dtos/update-sms-template.dto.d.ts.map +1 -1
  68. package/dist/dtos/update-sms-template.dto.js.map +1 -1
  69. package/dist/dtos/update-view-metadata.dto.d.ts.map +1 -1
  70. package/dist/dtos/update-view-metadata.dto.js.map +1 -1
  71. package/dist/entities/chatter-message-details.entity.d.ts.map +1 -1
  72. package/dist/entities/chatter-message-details.entity.js +1 -0
  73. package/dist/entities/chatter-message-details.entity.js.map +1 -1
  74. package/dist/entities/chatter-message.entity.d.ts.map +1 -1
  75. package/dist/entities/chatter-message.entity.js +1 -0
  76. package/dist/entities/chatter-message.entity.js.map +1 -1
  77. package/dist/entities/common.entity.js +4 -4
  78. package/dist/entities/common.entity.js.map +1 -1
  79. package/dist/entities/legacy-common.entity.js +4 -4
  80. package/dist/entities/legacy-common.entity.js.map +1 -1
  81. package/dist/entities/saved-filters.entity.d.ts +1 -0
  82. package/dist/entities/saved-filters.entity.d.ts.map +1 -1
  83. package/dist/entities/saved-filters.entity.js +6 -1
  84. package/dist/entities/saved-filters.entity.js.map +1 -1
  85. package/dist/entities/user.entity.d.ts +1 -0
  86. package/dist/entities/user.entity.d.ts.map +1 -1
  87. package/dist/entities/user.entity.js +6 -1
  88. package/dist/entities/user.entity.js.map +1 -1
  89. package/dist/helpers/field-crud-managers/ManyToManyRelationFieldCrudManager.d.ts +2 -0
  90. package/dist/helpers/field-crud-managers/ManyToManyRelationFieldCrudManager.d.ts.map +1 -1
  91. package/dist/helpers/field-crud-managers/ManyToManyRelationFieldCrudManager.js +33 -23
  92. package/dist/helpers/field-crud-managers/ManyToManyRelationFieldCrudManager.js.map +1 -1
  93. package/dist/helpers/field-crud-managers/OneToManyRelationFieldCrudManager.d.ts +3 -0
  94. package/dist/helpers/field-crud-managers/OneToManyRelationFieldCrudManager.d.ts.map +1 -1
  95. package/dist/helpers/field-crud-managers/OneToManyRelationFieldCrudManager.js +36 -23
  96. package/dist/helpers/field-crud-managers/OneToManyRelationFieldCrudManager.js.map +1 -1
  97. package/dist/helpers/security.helper.js +1 -0
  98. package/dist/helpers/security.helper.js.map +1 -1
  99. package/dist/index.d.ts +0 -4
  100. package/dist/index.d.ts.map +1 -1
  101. package/dist/index.js +0 -4
  102. package/dist/index.js.map +1 -1
  103. package/dist/repository/solid-base.repository.d.ts +10 -1
  104. package/dist/repository/solid-base.repository.d.ts.map +1 -1
  105. package/dist/repository/solid-base.repository.js +109 -0
  106. package/dist/repository/solid-base.repository.js.map +1 -1
  107. package/dist/seeders/module-metadata-seeder.service.d.ts +2 -0
  108. package/dist/seeders/module-metadata-seeder.service.d.ts.map +1 -1
  109. package/dist/seeders/module-metadata-seeder.service.js +142 -72
  110. package/dist/seeders/module-metadata-seeder.service.js.map +1 -1
  111. package/dist/seeders/permission-metadata-seeder.service.d.ts +1 -1
  112. package/dist/seeders/permission-metadata-seeder.service.d.ts.map +1 -1
  113. package/dist/seeders/permission-metadata-seeder.service.js +1 -1
  114. package/dist/seeders/permission-metadata-seeder.service.js.map +1 -1
  115. package/dist/seeders/seed-data/solid-core-metadata.json +23 -25
  116. package/dist/services/authentication.service.d.ts +22 -8
  117. package/dist/services/authentication.service.d.ts.map +1 -1
  118. package/dist/services/authentication.service.js +230 -214
  119. package/dist/services/authentication.service.js.map +1 -1
  120. package/dist/services/crud-helper.service.d.ts +4 -0
  121. package/dist/services/crud-helper.service.d.ts.map +1 -1
  122. package/dist/services/crud-helper.service.js +66 -32
  123. package/dist/services/crud-helper.service.js.map +1 -1
  124. package/dist/services/crud.service.d.ts.map +1 -1
  125. package/dist/services/crud.service.js +7 -4
  126. package/dist/services/crud.service.js.map +1 -1
  127. package/dist/services/field-metadata.service.d.ts.map +1 -1
  128. package/dist/services/field-metadata.service.js.map +1 -1
  129. package/dist/services/file/disk-file.service.d.ts +0 -2
  130. package/dist/services/file/disk-file.service.d.ts.map +1 -1
  131. package/dist/services/file/disk-file.service.js +7 -16
  132. package/dist/services/file/disk-file.service.js.map +1 -1
  133. package/dist/services/file/index.d.ts +1 -0
  134. package/dist/services/file/index.d.ts.map +1 -1
  135. package/dist/services/file/index.js +1 -0
  136. package/dist/services/file/index.js.map +1 -1
  137. package/dist/services/file/storage-path-builder.d.ts +17 -0
  138. package/dist/services/file/storage-path-builder.d.ts.map +1 -0
  139. package/dist/{seeders/sms-template-seeder.service.js → services/file/storage-path-builder.js} +45 -35
  140. package/dist/services/file/storage-path-builder.js.map +1 -0
  141. package/dist/services/media.service.d.ts +1 -1
  142. package/dist/services/media.service.d.ts.map +1 -1
  143. package/dist/services/media.service.js +45 -6
  144. package/dist/services/media.service.js.map +1 -1
  145. package/dist/services/mediaStorageProviders/file-s3-storage-provider.js.map +1 -1
  146. package/dist/services/mediaStorageProviders/file-storage-provider.d.ts.map +1 -1
  147. package/dist/services/mediaStorageProviders/file-storage-provider.js +46 -7
  148. package/dist/services/mediaStorageProviders/file-storage-provider.js.map +1 -1
  149. package/dist/services/module-metadata.service.d.ts +4 -6
  150. package/dist/services/module-metadata.service.d.ts.map +1 -1
  151. package/dist/services/module-metadata.service.js +16 -14
  152. package/dist/services/module-metadata.service.js.map +1 -1
  153. package/dist/services/setting.service.d.ts +3 -2
  154. package/dist/services/setting.service.d.ts.map +1 -1
  155. package/dist/services/setting.service.js +7 -4
  156. package/dist/services/setting.service.js.map +1 -1
  157. package/dist/services/settings/default-settings-provider.service.d.ts +24 -2
  158. package/dist/services/settings/default-settings-provider.service.d.ts.map +1 -1
  159. package/dist/services/settings/default-settings-provider.service.js +8 -6
  160. package/dist/services/settings/default-settings-provider.service.js.map +1 -1
  161. package/dist/services/view-metadata.service.d.ts +1 -0
  162. package/dist/services/view-metadata.service.d.ts.map +1 -1
  163. package/dist/services/view-metadata.service.js +25 -1
  164. package/dist/services/view-metadata.service.js.map +1 -1
  165. package/dist/solid-core.module.d.ts +3 -1
  166. package/dist/solid-core.module.d.ts.map +1 -1
  167. package/dist/solid-core.module.js +45 -9
  168. package/dist/solid-core.module.js.map +1 -1
  169. package/dist/testing/adapters/ui/playwright-adapter.d.ts.map +1 -1
  170. package/dist/testing/adapters/ui/playwright-adapter.js +35 -2
  171. package/dist/testing/adapters/ui/playwright-adapter.js.map +1 -1
  172. package/dist/transformers/typeorm/local-date-time-transformer.d.ts +4 -3
  173. package/dist/transformers/typeorm/local-date-time-transformer.d.ts.map +1 -1
  174. package/dist/transformers/typeorm/local-date-time-transformer.js +20 -2
  175. package/dist/transformers/typeorm/local-date-time-transformer.js.map +1 -1
  176. package/package.json +8 -2
  177. package/src/constants/error-messages.ts +1 -0
  178. package/src/constants.ts +3 -3
  179. package/src/controllers/role-metadata.controller.ts +26 -18
  180. package/src/dtos/create-email-template.dto.ts +7 -0
  181. package/src/dtos/create-list-of-values.dto.ts +7 -0
  182. package/src/dtos/create-menu-item-metadata.dto.ts +12 -1
  183. package/src/dtos/create-role-metadata.dto.ts +9 -0
  184. package/src/dtos/create-saved-filters.dto.ts +7 -0
  185. package/src/dtos/create-scheduled-job.dto.ts +14 -0
  186. package/src/dtos/create-security-rule.dto.ts +6 -0
  187. package/src/dtos/create-sms-template.dto.ts +6 -0
  188. package/src/dtos/create-view-metadata.dto.ts +11 -0
  189. package/src/dtos/otp-sign-in.dto.ts +3 -3
  190. package/src/dtos/otp-sign-up.dto.ts +3 -3
  191. package/src/dtos/resolve-s3-url.dto.ts +2 -12
  192. package/src/dtos/sign-up.dto.ts +0 -2
  193. package/src/dtos/update-email-template.dto.ts +6 -0
  194. package/src/dtos/update-list-of-values.dto.ts +8 -0
  195. package/src/dtos/update-menu-item-metadata.dto.ts +12 -0
  196. package/src/dtos/update-saved-filters.dto.ts +5 -1
  197. package/src/dtos/update-scheduled-job.dto.ts +15 -0
  198. package/src/dtos/update-security-rule.dto.ts +7 -0
  199. package/src/dtos/update-sms-template.dto.ts +32 -32
  200. package/src/dtos/update-view-metadata.dto.ts +12 -0
  201. package/src/entities/chatter-message-details.entity.ts +1 -0
  202. package/src/entities/chatter-message.entity.ts +1 -0
  203. package/src/entities/common.entity.ts +5 -5
  204. package/src/entities/legacy-common.entity.ts +5 -5
  205. package/src/entities/saved-filters.entity.ts +3 -0
  206. package/src/entities/user.entity.ts +4 -1
  207. package/src/helpers/field-crud-managers/ManyToManyRelationFieldCrudManager.ts +43 -32
  208. package/src/helpers/field-crud-managers/OneToManyRelationFieldCrudManager.ts +45 -33
  209. package/src/helpers/security.helper.ts +1 -1
  210. package/src/index.ts +0 -4
  211. package/src/repository/solid-base.repository.ts +172 -1
  212. package/src/seeders/module-metadata-seeder.service.ts +189 -127
  213. package/src/seeders/permission-metadata-seeder.service.ts +1 -4
  214. package/src/seeders/seed-data/solid-core-metadata.json +30 -32
  215. package/src/services/authentication.service.ts +273 -269
  216. package/src/services/crud-helper.service.ts +79 -36
  217. package/src/services/crud.service.ts +9 -4
  218. package/src/services/field-metadata.service.ts +0 -71
  219. package/src/services/file/disk-file.service.ts +8 -18
  220. package/src/services/file/index.ts +1 -0
  221. package/src/services/file/storage-path-builder.ts +56 -0
  222. package/src/services/media.service.ts +13 -7
  223. package/src/services/mediaStorageProviders/file-s3-storage-provider.ts +1 -1
  224. package/src/services/mediaStorageProviders/file-storage-provider.ts +13 -8
  225. package/src/services/module-metadata.service.ts +18 -15
  226. package/src/services/setting.service.ts +5 -3
  227. package/src/services/settings/default-settings-provider.service.ts +5 -3
  228. package/src/services/view-metadata.service.ts +29 -1
  229. package/src/solid-core.module.ts +16 -12
  230. package/src/testing/adapters/ui/playwright-adapter.ts +1 -1
  231. package/src/transformers/typeorm/local-date-time-transformer.ts +21 -3
  232. package/dist/passport-strategies/local.strategy.d.ts +0 -15
  233. package/dist/passport-strategies/local.strategy.d.ts.map +0 -1
  234. package/dist/passport-strategies/local.strategy.js +0 -44
  235. package/dist/passport-strategies/local.strategy.js.map +0 -1
  236. package/dist/seeders/email-template-seeder.service.d.ts +0 -10
  237. package/dist/seeders/email-template-seeder.service.d.ts.map +0 -1
  238. package/dist/seeders/email-template-seeder.service.js +0 -84
  239. package/dist/seeders/email-template-seeder.service.js.map +0 -1
  240. package/dist/seeders/sms-template-seeder.service.d.ts +0 -10
  241. package/dist/seeders/sms-template-seeder.service.d.ts.map +0 -1
  242. package/dist/seeders/sms-template-seeder.service.js.map +0 -1
  243. package/dist/seeders/user-seeder.service.d.ts +0 -10
  244. package/dist/seeders/user-seeder.service.d.ts.map +0 -1
  245. package/dist/seeders/user-seeder.service.js +0 -44
  246. package/dist/seeders/user-seeder.service.js.map +0 -1
  247. package/src/passport-strategies/local.strategy.ts +0 -28
  248. package/src/seeders/email-template-seeder.service.ts +0 -49
  249. package/src/seeders/sms-template-seeder.service.ts +0 -50
  250. package/src/seeders/user-seeder.service.ts +0 -33
  251. package/src/workflow.readme.md +0 -25
@@ -13,9 +13,11 @@ import {
13
13
  FindOptionsWhere,
14
14
  QueryRunner,
15
15
  Repository,
16
- SelectQueryBuilder
16
+ SelectQueryBuilder,
17
+ UpdateResult
17
18
  } from 'typeorm';
18
19
  import { SecurityRuleRepository } from './security-rule.repository';
20
+ import { PickKeysByType } from 'typeorm/common/PickKeysByType';
19
21
 
20
22
  export class SolidBaseRepository<T extends CommonEntity> extends Repository<T> {
21
23
  protected readonly logger: Logger;
@@ -132,4 +134,173 @@ export class SolidBaseRepository<T extends CommonEntity> extends Repository<T> {
132
134
 
133
135
  return qb.getManyAndCount();
134
136
  }
137
+
138
+ /**
139
+ * Security-aware count(): applies security rules before counting.
140
+ */
141
+ override async count(options?: FindManyOptions<T>): Promise<number> {
142
+ const alias = this.modelSingularName();
143
+ const qb = await this.createSecurityRuleAwareQueryBuilder(alias);
144
+
145
+ if (options) {
146
+ qb.setFindOptions(options);
147
+ }
148
+
149
+ return qb.getCount();
150
+ }
151
+
152
+ /**
153
+ * Security-aware countBy(): convenience wrapper routed through count().
154
+ */
155
+ override async countBy(where: FindOptionsWhere<T> | FindOptionsWhere<T>[]): Promise<number> {
156
+ return this.count({ where });
157
+ }
158
+
159
+ /**
160
+ * Security-aware average(): applies security rules before computing the average.
161
+ */
162
+ override async average(columnName: PickKeysByType<T, number>, where?: FindOptionsWhere<T> | FindOptionsWhere<T>[]): Promise<number | null> {
163
+ const alias = this.modelSingularName();
164
+ const qb = await this.createSecurityRuleAwareQueryBuilder(alias);
165
+
166
+ if (where) {
167
+ qb.setFindOptions({ where });
168
+ }
169
+
170
+ const result = await qb
171
+ .select(`AVG(CAST(${alias}.${String(columnName)} AS FLOAT))`, 'avg')
172
+ .getRawOne<{ avg: string | number | null }>();
173
+
174
+ if (result?.avg === null || result?.avg === undefined) {
175
+ return null;
176
+ }
177
+
178
+ return typeof result.avg === 'number'
179
+ ? result.avg
180
+ : parseFloat(result.avg);
181
+ }
182
+
183
+ /**
184
+ * Security-aware sum(): applies security rules before computing the sum.
185
+ */
186
+ override async sum(columnName: PickKeysByType<T, number>, where?: FindOptionsWhere<T> | FindOptionsWhere<T>[]): Promise<number | null> {
187
+ const alias = this.modelSingularName();
188
+ const qb = await this.createSecurityRuleAwareQueryBuilder(alias);
189
+
190
+ if (where) {
191
+ qb.setFindOptions({ where });
192
+ }
193
+
194
+ const result = await qb
195
+ .select(`SUM(CAST(${alias}.${String(columnName)} AS FLOAT))`, 'sum')
196
+ .getRawOne<{ sum: string | number | null }>();
197
+
198
+ if (result?.sum === null || result?.sum === undefined) {
199
+ return null;
200
+ }
201
+
202
+ return typeof result.sum === 'number'
203
+ ? result.sum
204
+ : parseFloat(result.sum);
205
+ }
206
+
207
+ /**
208
+ * Security-aware minimum(): applies security rules before computing the minimum.
209
+ */
210
+ override async minimum(columnName: PickKeysByType<T, number>, where?: FindOptionsWhere<T> | FindOptionsWhere<T>[]): Promise<number | null> {
211
+ const alias = this.modelSingularName();
212
+ const qb = await this.createSecurityRuleAwareQueryBuilder(alias);
213
+
214
+ if (where) {
215
+ qb.setFindOptions({ where });
216
+ }
217
+
218
+ const result = await qb
219
+ .select(`MIN(CAST(${alias}.${String(columnName)} AS FLOAT))`, 'min')
220
+ .getRawOne<{ min: string | number | null }>();
221
+
222
+ if (result?.min === null || result?.min === undefined) {
223
+ return null;
224
+ }
225
+
226
+ return typeof result.min === 'number'
227
+ ? result.min
228
+ : parseFloat(result.min);
229
+ }
230
+
231
+ /**
232
+ * Security-aware maximum(): applies security rules before computing the maximum.
233
+ */
234
+ override async maximum(columnName: PickKeysByType<T, number>, where?: FindOptionsWhere<T> | FindOptionsWhere<T>[]): Promise<number | null> {
235
+ const alias = this.modelSingularName();
236
+ const qb = await this.createSecurityRuleAwareQueryBuilder(alias);
237
+
238
+ if (where) {
239
+ qb.setFindOptions({ where });
240
+ }
241
+
242
+ const result = await qb
243
+ .select(`MAX(CAST(${alias}.${String(columnName)} AS FLOAT))`, 'max')
244
+ .getRawOne<{ max: string | number | null }>();
245
+
246
+ if (result?.max === null || result?.max === undefined) {
247
+ return null;
248
+ }
249
+
250
+ return typeof result.max === 'number'
251
+ ? result.max
252
+ : parseFloat(result.max);
253
+ }
254
+
255
+ /**
256
+ * Security-aware increment(): increments a column by a given value for all matching rows.
257
+ * Security rules are applied to determine which rows the user is allowed to modify.
258
+ */
259
+ override async increment(where: FindOptionsWhere<T>, propertyPath: string, value: string | number,): Promise<UpdateResult> {
260
+ const alias = this.modelSingularName();
261
+ const qb = await this.createSecurityRuleAwareQueryBuilder(alias);
262
+
263
+ qb.setFindOptions({ where });
264
+
265
+ const rows = await qb.select(`${alias}.id`).getMany();
266
+ const ids = rows.map((r) => (r as any).id);
267
+
268
+ if (ids.length === 0) {
269
+ return { raw: [], affected: 0, generatedMaps: [] };
270
+ }
271
+
272
+ return this.manager
273
+ .createQueryBuilder()
274
+ .update(this.metadata.target)
275
+ .set({ [propertyPath]: () => `${propertyPath} + :value` } as any)
276
+ .whereInIds(ids)
277
+ .setParameter('value', value)
278
+ .execute();
279
+ }
280
+
281
+ /**
282
+ * Security-aware decrement(): decrements a column by a given value for all matching rows.
283
+ * Security rules are applied to determine which rows the user is allowed to modify.
284
+ */
285
+ override async decrement(where: FindOptionsWhere<T>, propertyPath: string, value: string | number,): Promise<UpdateResult> {
286
+ const alias = this.modelSingularName();
287
+ const qb = await this.createSecurityRuleAwareQueryBuilder(alias);
288
+
289
+ qb.setFindOptions({ where });
290
+
291
+ const rows = await qb.select(`${alias}.id`).getMany();
292
+ const ids = rows.map((r) => (r as any).id);
293
+
294
+ if (ids.length === 0) {
295
+ return { raw: [], affected: 0, generatedMaps: [] };
296
+ }
297
+
298
+ return this.manager
299
+ .createQueryBuilder()
300
+ .update(this.metadata.target)
301
+ .set({ [propertyPath]: () => `${propertyPath} - :value` } as any)
302
+ .whereInIds(ids)
303
+ .setParameter('value', value)
304
+ .execute();
305
+ }
135
306
  }
@@ -32,7 +32,7 @@ import solidCoreMetadata from './seed-data/solid-core-metadata.json';
32
32
  import { SystemFieldsSeederService } from './system-fields-seeder.service';
33
33
  // import { CreateScheduledJobDto } from 'src/dtos/create-scheduled-job.dto';
34
34
  import { ActionMetadata, DEFAULT_SA_PASSWORD, MENU_ROLE_JOIN_TABLE_NAME, MENU_ROLE_JOIN_TABLE_NAME_MENU_COL, MENU_ROLE_JOIN_TABLE_NAME_ROLE_COL, MenuItemMetadata, ModuleMetadata, RoleMetadata, SignUpDto } from 'src';
35
- import { ADMIN_ROLE_NAME } from 'src/dtos/create-role-metadata.dto';
35
+ import { ADMIN_ROLE_NAME, CreateRoleMetadataDto } from 'src/dtos/create-role-metadata.dto';
36
36
  import { CreateSavedFiltersDto } from 'src/dtos/create-saved-filters.dto';
37
37
  import { CreateScheduledJobDto } from 'src/dtos/create-scheduled-job.dto';
38
38
  import { PermissionMetadataRepository } from 'src/repository/permission-metadata.repository';
@@ -90,134 +90,152 @@ export class ModuleMetadataSeederService {
90
90
  ) { }
91
91
 
92
92
  async seed(conf?: any) {
93
- this.enablePruning = Boolean(conf?.pruneMetadata);
94
- console.log(this.enablePruning ? '▶ Pruning enabled: metadata not present in JSON will be removed.' : '▶ Pruning disabled: existing metadata will be kept.');
95
-
96
- // Global seeding steps i.e across all modules
97
- await this.seedGlobalMetadata();
98
-
99
- // Module specific seeding steps.
100
- // Get all the module metadata files which needs to be seeded.
101
- const seedDataFiles = this.seedDataFiles;
102
- this.logger.debug(`Found seed data for modules: ${seedDataFiles.map(s => s.moduleMetadata?.name)}`);
103
-
104
- /**
105
- * -------------------------------------------------------------
106
- * Selective module seeding via: solid seed --modules-to-seed onboarding,reports
107
- * -------------------------------------------------------------
108
- */
93
+ let currentModule = 'global';
94
+ let currentStep = 'bootstrap';
109
95
  let modulesToSeed: string[] | null = null;
110
96
 
111
- if (conf && Array.isArray(conf.modulesToSeed)) {
112
- modulesToSeed = conf.modulesToSeed;
113
- console.log(`▶ Selective seeding enabled. Modules to seed: ${modulesToSeed.join(', ')}`);
114
- this.logger.log(`Selective seeding enabled. Modules to seed: ${modulesToSeed.join(', ')}`);
115
- } else {
116
- console.log(`▶ No modulesToSeed provided. Seeding ALL modules.`);
117
- this.logger.log(`No modulesToSeed provided. Seeding ALL modules.`);
118
- }
97
+ try {
98
+ this.enablePruning = Boolean(conf?.pruneMetadata);
99
+ console.log(this.enablePruning ? '▶ Pruning enabled: metadata not present in JSON will be removed.' : '▶ Pruning disabled: existing metadata will be kept.');
100
+
101
+ // Global seeding steps i.e across all modules
102
+ currentStep = 'seedGlobalMetadata';
103
+ await this.seedGlobalMetadata();
104
+
105
+ // Module specific seeding steps.
106
+ // Get all the module metadata files which needs to be seeded.
107
+ const seedDataFiles = this.seedDataFiles;
108
+ this.logger.debug(`Found seed data for modules: ${seedDataFiles.map(s => s.moduleMetadata?.name)}`);
109
+
110
+ /**
111
+ * -------------------------------------------------------------
112
+ * Selective module seeding via: solid seed --modules-to-seed onboarding,reports
113
+ * -------------------------------------------------------------
114
+ */
115
+ currentStep = 'resolveModulesToSeed';
116
+ if (conf && Array.isArray(conf.modulesToSeed)) {
117
+ modulesToSeed = conf.modulesToSeed;
118
+ console.log(`▶ Selective seeding enabled. Modules to seed: ${modulesToSeed.join(', ')}`);
119
+ this.logger.log(`Selective seeding enabled. Modules to seed: ${modulesToSeed.join(', ')}`);
120
+ } else {
121
+ console.log(`▶ No modulesToSeed provided. Seeding ALL modules.`);
122
+ this.logger.log(`No modulesToSeed provided. Seeding ALL modules.`);
123
+ }
119
124
 
120
- // Filter modules if needed
121
- const filteredSeedDataFiles = modulesToSeed ? seedDataFiles.filter((file) => modulesToSeed.includes(file.moduleMetadata?.name)) : seedDataFiles;
125
+ // Filter modules if needed
126
+ const filteredSeedDataFiles = modulesToSeed ? seedDataFiles.filter((file) => modulesToSeed.includes(file.moduleMetadata?.name)) : seedDataFiles;
122
127
 
123
- if (filteredSeedDataFiles.length === 0) {
124
- this.logger.warn(`No modules matched the provided modulesToSeed list.`);
125
- return;
126
- }
128
+ if (filteredSeedDataFiles.length === 0) {
129
+ this.logger.warn(`No modules matched the provided modulesToSeed list.`);
130
+ return;
131
+ }
127
132
 
128
- // let usersDetail;
129
- // For each module metadata file, we will process the seeding steps one by one.
130
- for (let i = 0; i < filteredSeedDataFiles.length; i++) {
131
- const overallMetadata = filteredSeedDataFiles[i];
132
- const moduleMetadata: CreateModuleMetadataDto = overallMetadata.moduleMetadata;
133
- console.log(`▶ Seeding Metadata for Module: ${moduleMetadata.name}`);
134
- this.logger.log(`Seeding Metadata for Module: ${moduleMetadata.name}`);
135
-
136
- // Process module metadata first.
137
- this.logger.log(`Seeding Module / Model / Fields`);
138
- const moduleModelFieldCounts = await this.seedModuleModelFields(moduleMetadata);
139
- console.log(`${this.formatSeedResult(moduleMetadata.name, 'Module/Model/Fields', moduleModelFieldCounts)}`);
140
-
141
- // Media Storage provider templates
142
- this.logger.log(`Seeding Media Storage Providers`);
143
- const mediaStorageCounts = await this.seedMediaStorageProviders(overallMetadata.mediaStorageProviders);
144
- console.log(`${this.formatSeedResult(moduleMetadata.name, 'Media Storage Providers', mediaStorageCounts)}`);
145
-
146
- // Custom role handling
147
- this.logger.log(`Seeding Roles`);
148
- const roleCounts = await this.seedRoles(overallMetadata);
149
- console.log(`${this.formatSeedResult(moduleMetadata.name, 'Roles', roleCounts)}`);
150
-
151
- // Custom user handling
152
- this.logger.log(`Seeding Users`);
153
- const userCounts = await this.seedUsers(overallMetadata);
154
- console.log(`${this.formatSeedResult(moduleMetadata.name, 'Users', userCounts)}`);
155
-
156
- // Application Module View handling
157
- this.logger.log(`Seeding Views`);
158
- const viewCounts = await this.seedViews(overallMetadata);
159
- console.log(`${this.formatSeedResult(moduleMetadata.name, 'Views', viewCounts)}`);
160
-
161
- // Application Module Action handling
162
- this.logger.log(`Seeding Actions`);
163
- const actionCounts = await this.seedActions(overallMetadata);
164
- console.log(`${this.formatSeedResult(moduleMetadata.name, 'Actions', actionCounts)}`);
165
-
166
- // Application Module Menu handling
167
- this.logger.log(`Seeding Menus`);
168
- const menuCounts = await this.seedMenus(overallMetadata);
169
- console.log(`${this.formatSeedResult(moduleMetadata.name, 'Menus', menuCounts)}`);
170
-
171
- // Email templates
172
- this.logger.log(`Seeding Email Templates`);
173
- const emailTemplateCounts = await this.seedEmailTemplates(overallMetadata, moduleMetadata.name);
174
- console.log(`${this.formatSeedResult(moduleMetadata.name, 'Email Templates', emailTemplateCounts)}`);
175
-
176
- // Sms templates
177
- this.logger.log(`Seeding Sms Templates`);
178
- const smsTemplateCounts = await this.seedSmsTemplates(overallMetadata, moduleMetadata.name);
179
- console.log(`${this.formatSeedResult(moduleMetadata.name, 'Sms Templates', smsTemplateCounts)}`);
180
-
181
- // Security rules
182
- this.logger.log(`Seeding Security Rules`);
183
- const securityRuleCounts = await this.seedSecurityRules(overallMetadata);
184
- console.log(`${this.formatSeedResult(moduleMetadata.name, 'Security Rules', securityRuleCounts)}`);
185
-
186
- // List Of Values
187
- this.logger.log(`Seeding List Of Values`);
188
- const lovCounts = await this.seedListOfValues(moduleMetadata, overallMetadata);
189
- console.log(`${this.formatSeedResult(moduleMetadata.name, 'List Of Values', lovCounts)}`);
190
-
191
- // Dashboards
192
- this.logger.log(`Seeding Dashboards`);
193
- const dashboardCounts = await this.seedDashboards(moduleMetadata, overallMetadata);
194
- console.log(`${this.formatSeedResult(moduleMetadata.name, 'Dashboards', dashboardCounts)}`);
195
-
196
- // Scheduled Jobs
197
- this.logger.log(`Seeding Scheduled Jobs`);
198
- const scheduledJobCounts = await this.seedScheduledJobs(moduleMetadata, overallMetadata);
199
- console.log(`${this.formatSeedResult(moduleMetadata.name, 'Scheduled Jobs', scheduledJobCounts)}`);
200
-
201
- // Saved Filters
202
- this.logger.log(`Seeding Saved Filters`);
203
- const savedFilterCounts = await this.seedSavedFilters(moduleMetadata, overallMetadata);
204
- console.log(`${this.formatSeedResult(moduleMetadata.name, 'Saved Filters', savedFilterCounts)}`);
205
-
206
- // Model Sequences
207
- this.logger.log(`Seeding Model Sequences`);
208
- const modelSequenceCounts = await this.seedModelSequences(overallMetadata);
209
- console.log(`${this.formatSeedResult(moduleMetadata.name, 'Model Sequences', modelSequenceCounts)}`);
210
- }
211
-
212
- // Setup default roles with permissions.
213
- await this.setupDefaultRolesWithPermissions();
214
-
215
- // Add a console log indicating seeding is finished. This needs to be console.log so that it looks proper when this code is run via CLI.
216
- console.log(`✔ Seeding completed.`);
217
- //this.logger.log(`All Seeders finished`);
218
-
219
- //FIXME: Handle displaying the created users credentials in a better way.
220
- // this.logger.log(`Newly created username is: ${usersDetail?.length > 0 ? usersDetail[0]?.username : ''} and password is ${usersDetail?.length > 0 ? usersDetail[0]?.password : ''}`);
133
+ // let usersDetail;
134
+ // For each module metadata file, we will process the seeding steps one by one.
135
+ for (let i = 0; i < filteredSeedDataFiles.length; i++) {
136
+ const overallMetadata = filteredSeedDataFiles[i];
137
+ const moduleMetadata: CreateModuleMetadataDto = overallMetadata.moduleMetadata;
138
+ currentModule = moduleMetadata?.name ?? 'unknown';
139
+
140
+ console.log(`▶ Seeding Metadata for Module: ${moduleMetadata.name}`);
141
+ this.logger.log(`Seeding Metadata for Module: ${moduleMetadata.name}`);
142
+
143
+ // Process module metadata first.
144
+ currentStep = 'seedModuleModelFields';
145
+ this.logger.log(`Seeding Module / Model / Fields`);
146
+ const moduleModelFieldCounts = await this.seedModuleModelFields(moduleMetadata);
147
+ console.log(`${this.formatSeedResult(moduleMetadata.name, 'Module/Model/Fields', moduleModelFieldCounts)}`);
148
+
149
+ currentStep = 'seedMediaStorageProviders';
150
+ this.logger.log(`Seeding Media Storage Providers`);
151
+ const mediaStorageCounts = await this.seedMediaStorageProviders(overallMetadata.mediaStorageProviders);
152
+ console.log(`${this.formatSeedResult(moduleMetadata.name, 'Media Storage Providers', mediaStorageCounts)}`);
153
+
154
+ currentStep = 'seedRoles';
155
+ this.logger.log(`Seeding Roles`);
156
+ const roleCounts = await this.seedRoles(overallMetadata);
157
+ console.log(`${this.formatSeedResult(moduleMetadata.name, 'Roles', roleCounts)}`);
158
+
159
+ currentStep = 'seedUsers';
160
+ this.logger.log(`Seeding Users`);
161
+ const userCounts = await this.seedUsers(overallMetadata);
162
+ console.log(`${this.formatSeedResult(moduleMetadata.name, 'Users', userCounts)}`);
163
+
164
+ currentStep = 'seedViews';
165
+ this.logger.log(`Seeding Views`);
166
+ const viewCounts = await this.seedViews(overallMetadata);
167
+ console.log(`${this.formatSeedResult(moduleMetadata.name, 'Views', viewCounts)}`);
168
+
169
+ currentStep = 'seedActions';
170
+ this.logger.log(`Seeding Actions`);
171
+ const actionCounts = await this.seedActions(overallMetadata);
172
+ console.log(`${this.formatSeedResult(moduleMetadata.name, 'Actions', actionCounts)}`);
173
+
174
+ currentStep = 'seedMenus';
175
+ this.logger.log(`Seeding Menus`);
176
+ const menuCounts = await this.seedMenus(overallMetadata);
177
+ console.log(`${this.formatSeedResult(moduleMetadata.name, 'Menus', menuCounts)}`);
178
+
179
+ currentStep = 'seedEmailTemplates';
180
+ this.logger.log(`Seeding Email Templates`);
181
+ const emailTemplateCounts = await this.seedEmailTemplates(overallMetadata, moduleMetadata.name);
182
+ console.log(`${this.formatSeedResult(moduleMetadata.name, 'Email Templates', emailTemplateCounts)}`);
183
+
184
+ currentStep = 'seedSmsTemplates';
185
+ this.logger.log(`Seeding Sms Templates`);
186
+ const smsTemplateCounts = await this.seedSmsTemplates(overallMetadata, moduleMetadata.name);
187
+ console.log(`${this.formatSeedResult(moduleMetadata.name, 'Sms Templates', smsTemplateCounts)}`);
188
+
189
+ currentStep = 'seedSecurityRules';
190
+ this.logger.log(`Seeding Security Rules`);
191
+ const securityRuleCounts = await this.seedSecurityRules(overallMetadata);
192
+ console.log(`${this.formatSeedResult(moduleMetadata.name, 'Security Rules', securityRuleCounts)}`);
193
+
194
+ currentStep = 'seedListOfValues';
195
+ this.logger.log(`Seeding List Of Values`);
196
+ const lovCounts = await this.seedListOfValues(moduleMetadata, overallMetadata);
197
+ console.log(`${this.formatSeedResult(moduleMetadata.name, 'List Of Values', lovCounts)}`);
198
+
199
+ currentStep = 'seedDashboards';
200
+ this.logger.log(`Seeding Dashboards`);
201
+ const dashboardCounts = await this.seedDashboards(moduleMetadata, overallMetadata);
202
+ console.log(`${this.formatSeedResult(moduleMetadata.name, 'Dashboards', dashboardCounts)}`);
203
+
204
+ currentStep = 'seedScheduledJobs';
205
+ this.logger.log(`Seeding Scheduled Jobs`);
206
+ const scheduledJobCounts = await this.seedScheduledJobs(moduleMetadata, overallMetadata);
207
+ console.log(`${this.formatSeedResult(moduleMetadata.name, 'Scheduled Jobs', scheduledJobCounts)}`);
208
+
209
+ currentStep = 'seedSavedFilters';
210
+ this.logger.log(`Seeding Saved Filters`);
211
+ const savedFilterCounts = await this.seedSavedFilters(moduleMetadata, overallMetadata);
212
+ console.log(`${this.formatSeedResult(moduleMetadata.name, 'Saved Filters', savedFilterCounts)}`);
213
+
214
+ currentStep = 'seedModelSequences';
215
+ this.logger.log(`Seeding Model Sequences`);
216
+ const modelSequenceCounts = await this.seedModelSequences(overallMetadata);
217
+ console.log(`${this.formatSeedResult(moduleMetadata.name, 'Model Sequences', modelSequenceCounts)}`);
218
+ }
219
+
220
+ currentModule = 'global';
221
+ currentStep = 'setupDefaultRolesWithPermissions';
222
+ await this.setupDefaultRolesWithPermissions();
223
+
224
+ // Add a console log indicating seeding is finished. This needs to be console.log so that it looks proper when this code is run via CLI.
225
+ console.log(`✔ Seeding completed.`);
226
+ //this.logger.log(`All Seeders finished`);
227
+
228
+ //FIXME: Handle displaying the created users credentials in a better way.
229
+ // this.logger.log(`Newly created username is: ${usersDetail?.length > 0 ? usersDetail[0]?.username : ''} and password is ${usersDetail?.length > 0 ? usersDetail[0]?.password : ''}`);
230
+ } catch (error) {
231
+ this.logSeedFailureForCli(error, {
232
+ moduleName: currentModule,
233
+ step: currentStep,
234
+ pruneEnabled: this.enablePruning,
235
+ modulesToSeed,
236
+ });
237
+ throw error;
238
+ }
221
239
  }
222
240
 
223
241
  private async seedScheduledJobs(moduleMetadata: CreateModuleMetadataDto, overallMetadata: any): Promise<{ pruned: number; upserted: number }> {
@@ -403,7 +421,6 @@ export class ModuleMetadataSeederService {
403
421
  await this.systemFieldsSeederService.seed();
404
422
  }
405
423
 
406
-
407
424
  // OK
408
425
  private async seedPermissions() {
409
426
 
@@ -858,7 +875,6 @@ export class ModuleMetadataSeederService {
858
875
  return { pruned, upserted };
859
876
  }
860
877
 
861
-
862
878
  private async handleSeedSecurityRules(rulesDto: CreateSecurityRuleDto[]) {
863
879
  if (!rulesDto || rulesDto.length === 0) {
864
880
  this.logger.debug(`No security rules found to seed`);
@@ -908,7 +924,27 @@ export class ModuleMetadataSeederService {
908
924
  return;
909
925
  }
910
926
  for (const dto of createSavedFilterDto) {
911
- await this.savedFiltersRepo.upsertWithDto({ ...dto, filterQueryJson: JSON.stringify(dto.filterQueryJson) });
927
+ this.validateSavedFilterQueryJsonWrapper(dto);
928
+ await this.savedFiltersRepo.upsertWithDto({ ...dto, filterQueryJson: JSON.stringify(dto.filterQueryJson), isSeeded: true });
929
+ }
930
+ }
931
+
932
+ private validateSavedFilterQueryJsonWrapper(dto: CreateSavedFiltersDto): void {
933
+ const filterName = dto?.name ?? '<unnamed>';
934
+ const filterQueryJson = dto?.filterQueryJson;
935
+ const baseErrorMessage =
936
+ `Invalid saved filter "${filterName}": filterQueryJson must be wrapped with a top-level "$or" or "$and". ` +
937
+ `Example: {"$or":[{"employeeStatus":{"$eq":"Active"}}]}`;
938
+
939
+ if (!filterQueryJson || typeof filterQueryJson !== 'object' || Array.isArray(filterQueryJson)) {
940
+ throw new Error(baseErrorMessage);
941
+ }
942
+
943
+ const topLevelKeys = Object.keys(filterQueryJson);
944
+ const hasLogicalWrapper = topLevelKeys.includes('$or') || topLevelKeys.includes('$and');
945
+ if (!hasLogicalWrapper) {
946
+ const receivedKeys = topLevelKeys.length > 0 ? topLevelKeys.join(', ') : '(none)';
947
+ throw new Error(`${baseErrorMessage}. Received top-level keys: ${receivedKeys}`);
912
948
  }
913
949
  }
914
950
 
@@ -1494,6 +1530,32 @@ export class ModuleMetadataSeederService {
1494
1530
  // return answer.trim().toLowerCase().startsWith('y');
1495
1531
  // }
1496
1532
 
1533
+ private logSeedFailureForCli(
1534
+ error: unknown,
1535
+ context: { moduleName: string; step: string; pruneEnabled: boolean; modulesToSeed: string[] | null }
1536
+ ): void {
1537
+ const err = error instanceof Error ? error : new Error(String(error));
1538
+ const stackLines = (err.stack ?? '')
1539
+ .split('\n')
1540
+ .slice(0, 8)
1541
+ .map((line) => line.trim())
1542
+ .filter(Boolean);
1543
+ const logPayload = {
1544
+ module: context.moduleName,
1545
+ step: context.step,
1546
+ pruneEnabled: context.pruneEnabled,
1547
+ modulesToSeed: context.modulesToSeed?.length ? context.modulesToSeed : 'ALL',
1548
+ error: {
1549
+ name: err.name,
1550
+ message: err.message,
1551
+ stackPreview: stackLines.length > 0 ? stackLines : undefined,
1552
+ },
1553
+ };
1554
+
1555
+ console.log('✖ Seeding failed');
1556
+ console.log(JSON.stringify(logPayload, null, 2));
1557
+ }
1558
+
1497
1559
  private formatSeedResult(moduleName: string, label: string, counts: { pruned: number; upserted: number }): string {
1498
1560
  if (this.enablePruning) {
1499
1561
  return `✔ [${moduleName}] ${label} seeded (pruned ${counts.pruned}, upserted ${counts.upserted})`;
@@ -1,10 +1,7 @@
1
1
  import { forwardRef, Inject, Injectable, Logger } from '@nestjs/common';
2
- import { InjectRepository } from '@nestjs/typeorm';
3
- import { Repository } from 'typeorm';
4
2
  import { SolidRegistry } from 'src/helpers/solid-registry';
5
- import { PermissionMetadata } from '../entities/permission-metadata.entity';
6
- import { RoleMetadataService } from '../services/role-metadata.service';
7
3
  import { PermissionMetadataRepository } from 'src/repository/permission-metadata.repository';
4
+ import { RoleMetadataService } from '../services/role-metadata.service';
8
5
 
9
6
  @Injectable()
10
7
  export class PermissionMetadataSeederService {