adminizer 4.2.0 → 4.3.0-build.101

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 (276) hide show
  1. package/assets/add-1E1UsMVZ.js +1 -0
  2. package/assets/add-form-CGa9QbwE.js +1 -0
  3. package/assets/add-group-7yAK6uFY.js +1 -0
  4. package/assets/add-group-form-BNmpvW8K.js +1 -0
  5. package/assets/add-user-NoYkTfZi.js +1 -0
  6. package/assets/add-user-form-DrclnTOd.js +1 -0
  7. package/assets/{app-CVnUv8s_.css → app-D8oqdt8J.css} +1 -1
  8. package/assets/app-layout-i2zN9Rn-.js +1 -0
  9. package/assets/app-shell-CHEYhVco.js +1 -0
  10. package/assets/app-sidebar-DeF8WNmW.js +1 -0
  11. package/assets/app-sidebar-header-DdM1EOrb.js +1 -0
  12. package/assets/app.js +1699 -1657
  13. package/assets/breadcrumbs-C439DWrC.js +1 -0
  14. package/assets/catalog-B9hdop59.js +6 -0
  15. package/assets/dashboard-Bi5KeZu2.js +3 -0
  16. package/assets/date-picker-DbJ8nHp5.js +1 -0
  17. package/assets/del-modal-_b3CSNHb.js +1 -0
  18. package/assets/dynamic-controls-BOYmRX_z.js +1 -0
  19. package/assets/field-renderer-4obZ_jUo.js +407 -0
  20. package/assets/{field-renderer-CsBa_tMB.css → field-renderer-CRNLShJD.css} +1 -1
  21. package/assets/form-Bte5kzjB.js +1 -0
  22. package/assets/form-oUlsuu7h.js +1 -0
  23. package/assets/geo-json-CadjdhwW.js +4 -0
  24. package/assets/handsontable-PVakPtJV.js +281 -0
  25. package/assets/home-CsUXA-O6.js +2 -0
  26. package/assets/icon-DM49pZW3.js +1 -0
  27. package/assets/init-user-DNrB8Xt5.js +1 -0
  28. package/assets/input-error--IOnWghB.js +1 -0
  29. package/assets/jsoneditor-D95dIAP2.js +3878 -0
  30. package/assets/list-CdfSnSN1.js +1 -0
  31. package/assets/list-table-Byzv346I.js +22 -0
  32. package/assets/login-6Xb8bcPq.js +1 -0
  33. package/assets/manifest.json +331 -221
  34. package/assets/material-icon-BedgD2NE.js +1 -0
  35. package/assets/media-manager-UQT6RhI8.js +14 -0
  36. package/assets/module-B4-7WkY5.js +1 -0
  37. package/assets/monaco-ICtx6xoc.js +11 -0
  38. package/assets/nav-footer-BwkQkwow.js +1 -0
  39. package/assets/nav-main-9Eb_A0rf.js +1 -0
  40. package/assets/nav-user-CRAfyE-x.js +1 -0
  41. package/assets/notification-ma2x7vka.js +27 -0
  42. package/assets/pagination-render-xsEktzA8.js +1 -0
  43. package/assets/register-BIyjCy8M.js +1 -0
  44. package/assets/tabs-BK_5wdPA.js +1 -0
  45. package/assets/theme-switcher-CKiJbMkh.js +1 -0
  46. package/assets/toast-editor-BZM5RHph.js +845 -0
  47. package/assets/user-info-CcYdmjMZ.js +1 -0
  48. package/assets/user-menu-content-DbUk8ZeY.js +1 -0
  49. package/assets/vanilla-picker-l5rcX3cq.js +8 -0
  50. package/assets/vendor-wzZzN_Jv.js +17 -0
  51. package/assets/welcome-cWh1qnl_.js +1 -0
  52. package/controllers/add.js +1 -1
  53. package/controllers/catalog/FrontentCatalogAdapter.d.ts +4 -4
  54. package/controllers/ckeditorUpload.d.ts +1 -1
  55. package/controllers/ckeditorUpload.js +50 -26
  56. package/controllers/edit.js +2 -2
  57. package/controllers/form.js +29 -13
  58. package/controllers/list.js +2 -2
  59. package/controllers/login.js +2 -2
  60. package/controllers/media-manager/mediaManagerAdapter.js +13 -2
  61. package/controllers/media-manager/mediaManagerApi.js +4 -3
  62. package/controllers/navigation/edit.js +1 -1
  63. package/controllers/notifications/NotificationController.d.ts +11 -0
  64. package/controllers/notifications/NotificationController.js +219 -0
  65. package/controllers/remove.js +2 -1
  66. package/controllers/view.js +1 -1
  67. package/files/favicon.png +0 -0
  68. package/helpers/configHelper.d.ts +1 -1
  69. package/helpers/controllerHelper.js +1 -1
  70. package/helpers/diffHelpers.d.ts +2 -0
  71. package/helpers/diffHelpers.js +69 -0
  72. package/helpers/fieldsHelper.d.ts +1 -1
  73. package/helpers/fieldsHelper.js +1 -1
  74. package/helpers/formHelper.js +3 -1
  75. package/helpers/inertiaAddHelper.d.ts +1 -1
  76. package/helpers/inertiaAutHelper.js +19 -0
  77. package/helpers/inertiaFromHelper.js +12 -0
  78. package/helpers/translationHelper.d.ts +1 -1
  79. package/helpers/translationHelper.js +1 -1
  80. package/index.d.ts +16 -14
  81. package/index.js +16 -14
  82. package/interfaces/adminpanelConfig.d.ts +18 -0
  83. package/interfaces/global.d.ts +3 -3
  84. package/interfaces/types.d.ts +44 -2
  85. package/lib/Adminizer.d.ts +16 -8
  86. package/lib/Adminizer.js +95 -12
  87. package/lib/{v4/DataAccessor.d.ts → DataAccessor.d.ts} +5 -5
  88. package/lib/{v4/DataAccessor.js → DataAccessor.js} +3 -3
  89. package/lib/{v4/I18n.js → I18n.js} +2 -2
  90. package/lib/{v4/PolicyManager.d.ts → PolicyManager.d.ts} +1 -1
  91. package/lib/{v4/PolicyManager.js → PolicyManager.js} +2 -2
  92. package/lib/{v4/catalog → catalog}/AbstractCatalog.d.ts +1 -1
  93. package/lib/{v4/catalog → catalog}/Navigation.d.ts +2 -2
  94. package/lib/{v4/catalog → catalog}/Navigation.js +1 -1
  95. package/lib/{v4/controls → controls}/AbstractControls.d.ts +1 -1
  96. package/lib/{v4/controls → controls}/codeEditor/MonacoEditor.d.ts +1 -1
  97. package/lib/{v4/controls → controls}/geojsoneditor/GeoEditor.d.ts +1 -1
  98. package/lib/{v4/controls → controls}/jsoneditor/JsonEditor.d.ts +1 -1
  99. package/lib/{v4/controls → controls}/markdown/ToastUiEditor.d.ts +1 -1
  100. package/lib/{v4/controls → controls}/table/Handsontable.d.ts +1 -1
  101. package/lib/{v4/controls → controls}/wysiwyg/CKeditor.d.ts +1 -1
  102. package/lib/{v4/controls → controls}/wysiwyg/CKeditor.js +1 -1
  103. package/lib/{v4/datatable → datatable}/NodeTable.d.ts +1 -1
  104. package/lib/media-manager/AbstractMediaManager.d.ts +3 -3
  105. package/lib/media-manager/AbstractMediaManager.js +2 -2
  106. package/lib/media-manager/DefaultMediaManager.d.ts +1 -1
  107. package/lib/media-manager/DefaultMediaManager.js +6 -8
  108. package/lib/media-manager/Items.d.ts +2 -1
  109. package/lib/media-manager/Items.js +7 -0
  110. package/lib/media-manager/helpers/MediaManagerHelper.d.ts +10 -0
  111. package/lib/media-manager/helpers/MediaManagerHelper.js +27 -10
  112. package/lib/{v4/model → model}/AbstractModel.d.ts +12 -0
  113. package/lib/model/AbstractModel.js +178 -0
  114. package/lib/{v4/model → model}/ModelHandler.js +1 -1
  115. package/lib/{v4/model → model}/adapter/sequelize.d.ts +3 -4
  116. package/lib/{v4/model → model}/adapter/sequelize.js +41 -14
  117. package/lib/notifications/AbstractNotificationService.d.ts +163 -0
  118. package/lib/notifications/AbstractNotificationService.js +353 -0
  119. package/lib/notifications/GeneralNotificationService.d.ts +8 -0
  120. package/lib/notifications/GeneralNotificationService.js +61 -0
  121. package/lib/notifications/NotificationHandler.d.ts +27 -0
  122. package/lib/notifications/NotificationHandler.js +41 -0
  123. package/lib/notifications/SystemNotificationService.d.ts +19 -0
  124. package/lib/notifications/SystemNotificationService.js +173 -0
  125. package/lib/{v4/widgets → widgets}/abstractCustom.d.ts +1 -1
  126. package/lib/{v4/widgets → widgets}/abstractLink.d.ts +1 -1
  127. package/lib/{v4/widgets → widgets}/abstractWidgetBase.d.ts +1 -1
  128. package/lib/{v4/widgets → widgets}/widgetHandler.d.ts +2 -2
  129. package/migrations/index.d.ts +3 -0
  130. package/migrations/index.js +4 -0
  131. package/migrations/umzug/0001.d.ts +7 -0
  132. package/migrations/umzug/0001.js +121 -0
  133. package/migrations/umzugExports.d.ts +2 -0
  134. package/migrations/umzugExports.js +9 -0
  135. package/models/MediaManagerAP.d.ts +9 -3
  136. package/models/MediaManagerAP.js +11 -5
  137. package/models/MediaManagerAssociationsAP.d.ts +9 -3
  138. package/models/MediaManagerAssociationsAP.js +11 -5
  139. package/models/MediaManagerMetaAP.d.ts +9 -3
  140. package/models/MediaManagerMetaAP.js +11 -5
  141. package/models/NotificationAP.d.ts +44 -0
  142. package/models/NotificationAP.js +30 -0
  143. package/models/UserAP.d.ts +1 -1
  144. package/models/UserNotificationAP.d.ts +26 -0
  145. package/models/UserNotificationAP.js +18 -0
  146. package/package.json +17 -8
  147. package/system/Router.js +21 -7
  148. package/system/bindAssets.js +1 -0
  149. package/system/bindControls.js +6 -6
  150. package/system/bindInertia.js +9 -3
  151. package/system/bindMediaManager.js +2 -1
  152. package/system/bindNavigation.js +3 -4
  153. package/system/bindNotifications.d.ts +2 -0
  154. package/system/bindNotifications.js +14 -0
  155. package/system/bindReqFunctions.js +8 -25
  156. package/system/defaults.js +6 -0
  157. package/translations/ar.json +5 -4
  158. package/translations/cn.json +5 -4
  159. package/translations/de.json +72 -71
  160. package/translations/en.json +5 -4
  161. package/translations/es.json +5 -4
  162. package/translations/fr.json +5 -4
  163. package/translations/hi.json +5 -4
  164. package/translations/jp.json +5 -4
  165. package/translations/ko.json +5 -4
  166. package/translations/pt.json +5 -4
  167. package/translations/ru.json +2 -1
  168. package/translations/ua.json +5 -4
  169. package/translations/uz.json +73 -72
  170. package/translations/vi.json +5 -4
  171. package/ui/components/VanillaJSONEditor.d.ts +2 -0
  172. package/ui/components/VanillaJSONEditor.js +101 -0
  173. package/ui/components/handsontable.d.ts +10 -0
  174. package/ui/components/handsontable.js +51 -0
  175. package/ui/components/monaco-editor.d.ts +11 -0
  176. package/ui/components/monaco-editor.js +57 -0
  177. package/ui/components/ui/table.d.ts +1 -1
  178. package/ui/components/ui/table.js +2 -2
  179. package/ui/hooks/form-state.d.ts +4 -0
  180. package/ui/hooks/form-state.js +45 -0
  181. package/ui/hooks/use-appearance.d.ts +7 -0
  182. package/ui/hooks/use-appearance.js +76 -0
  183. package/ui/hooks/use-window-size.d.ts +5 -0
  184. package/ui/hooks/use-window-size.js +21 -0
  185. package/assets/VanillaJSONEditor-x8tDbio1.js +0 -3833
  186. package/assets/add-DOaZgoTz.js +0 -1
  187. package/assets/add-form-BjackXJe.js +0 -1
  188. package/assets/add-group-Br44s2D6.js +0 -1
  189. package/assets/add-group-form-YI6KS37x.js +0 -1
  190. package/assets/add-user-DWcH0s9-.js +0 -1
  191. package/assets/add-user-form-bv5BNX5A.js +0 -1
  192. package/assets/app-layout-DecN527i.js +0 -1
  193. package/assets/app-shell-DHzMWA1L.js +0 -1
  194. package/assets/app-sidebar-4JFP8UdU.js +0 -1
  195. package/assets/app-sidebar-header-CIKLfLsF.js +0 -1
  196. package/assets/breadcrumbs-Bo2z-LNC.js +0 -1
  197. package/assets/catalog-DVyrGTNR.js +0 -6
  198. package/assets/dashboard-Dy3pk9t7.js +0 -3
  199. package/assets/date-picker-B2z-aeuv.js +0 -1
  200. package/assets/debounce-D6eaSoDg.js +0 -1
  201. package/assets/del-modal-l3LxJWCN.js +0 -1
  202. package/assets/dynamic-controls-CSVVCONh.js +0 -1
  203. package/assets/field-renderer-BHo944Cn.js +0 -407
  204. package/assets/form-BXfnz6h5.js +0 -1
  205. package/assets/form-hP8sjbND.js +0 -1
  206. package/assets/form-state-702tb_BV.js +0 -1
  207. package/assets/geo-json-CVaNUZbb.js +0 -4
  208. package/assets/handsontable-CZQ5Ietj.js +0 -347
  209. package/assets/home-BD4MWmm9.js +0 -2
  210. package/assets/icon-C_X2cQ6W.js +0 -1
  211. package/assets/index-C148XJoK.js +0 -1
  212. package/assets/index-Vcq4gwWv.js +0 -1
  213. package/assets/init-user-DgCD4ZN1.js +0 -1
  214. package/assets/input-error-Bab-HPbn.js +0 -1
  215. package/assets/list-C-27EH39.js +0 -1
  216. package/assets/list-table-_OiQmr2A.js +0 -22
  217. package/assets/login-jzvVXgAV.js +0 -1
  218. package/assets/material-icon-DsiW0iQG.js +0 -1
  219. package/assets/media-manager-CSB_XRcE.js +0 -14
  220. package/assets/module-Ceq6vuwa.js +0 -1
  221. package/assets/monaco-editor-XX8Hz76f.js +0 -11
  222. package/assets/multi-select-BbKSrMw2.js +0 -1
  223. package/assets/nav-footer-B8gMV6Wp.js +0 -1
  224. package/assets/nav-main-BUp9qA8R.js +0 -1
  225. package/assets/nav-user-DeF3SDRj.js +0 -1
  226. package/assets/pagination-render-B9U_hVbB.js +0 -1
  227. package/assets/register-C6acWsLe.js +0 -1
  228. package/assets/theme-switcher-D8T9NgWG.js +0 -1
  229. package/assets/toast-editor-C0550yms.js +0 -845
  230. package/assets/use-appearance-CXP5cHaQ.js +0 -1
  231. package/assets/use-window-size-DLSEoR6e.js +0 -1
  232. package/assets/user-info-CAgkqARx.js +0 -1
  233. package/assets/user-menu-content-BxL0QMC9.js +0 -1
  234. package/assets/vanilla-picker-B6E6ObS_.js +0 -8
  235. package/assets/welcome-Dk7GoS5R.js +0 -1
  236. package/lib/v4/model/AbstractModel.js +0 -67
  237. package/lib/v4/model/adapter/typeorm.d.ts +0 -23
  238. package/lib/v4/model/adapter/typeorm.js +0 -68
  239. package/migrations/20230303171134-adminpanel-init.js +0 -122
  240. package/migrations/20231223215835-add-widgets.js +0 -32
  241. package/migrations/20240804191001-added-navigationap.js +0 -53
  242. package/migrations/20241121184236-v4-init.js +0 -134
  243. /package/lib/{v4/I18n.d.ts → I18n.d.ts} +0 -0
  244. /package/lib/{v4/POWCaptcha.d.ts → POWCaptcha.d.ts} +0 -0
  245. /package/lib/{v4/POWCaptcha.js → POWCaptcha.js} +0 -0
  246. /package/lib/{v4/catalog → catalog}/AbstractCatalog.js +0 -0
  247. /package/lib/{v4/catalog → catalog}/CatalogHandler.d.ts +0 -0
  248. /package/lib/{v4/catalog → catalog}/CatalogHandler.js +0 -0
  249. /package/lib/{v4/controls → controls}/AbstractControls.js +0 -0
  250. /package/lib/{v4/controls → controls}/ControlsHandler.d.ts +0 -0
  251. /package/lib/{v4/controls → controls}/ControlsHandler.js +0 -0
  252. /package/lib/{v4/controls → controls}/codeEditor/MonacoEditor.js +0 -0
  253. /package/lib/{v4/controls → controls}/geojsoneditor/GeoEditor.js +0 -0
  254. /package/lib/{v4/controls → controls}/jsoneditor/JsonEditor.js +0 -0
  255. /package/lib/{v4/controls → controls}/markdown/ToastUiEditor.js +0 -0
  256. /package/lib/{v4/controls → controls}/table/Handsontable.js +0 -0
  257. /package/lib/{v4/datatable → datatable}/NodeTable.js +0 -0
  258. /package/lib/{v4/helper → helper}/jwt.d.ts +0 -0
  259. /package/lib/{v4/helper → helper}/jwt.js +0 -0
  260. /package/lib/{v4/inertia → inertia}/flash.d.ts +0 -0
  261. /package/lib/{v4/inertia → inertia}/flash.js +0 -0
  262. /package/lib/{v4/inertia → inertia}/inertiaAdapter.d.ts +0 -0
  263. /package/lib/{v4/inertia → inertia}/inertiaAdapter.js +0 -0
  264. /package/lib/{v4/model → model}/ModelHandler.d.ts +0 -0
  265. /package/lib/{v4/model → model}/adapter/waterline.d.ts +0 -0
  266. /package/lib/{v4/model → model}/adapter/waterline.js +0 -0
  267. /package/lib/{v4/widgets → widgets}/abstractAction.d.ts +0 -0
  268. /package/lib/{v4/widgets → widgets}/abstractAction.js +0 -0
  269. /package/lib/{v4/widgets → widgets}/abstractCustom.js +0 -0
  270. /package/lib/{v4/widgets → widgets}/abstractInfo.d.ts +0 -0
  271. /package/lib/{v4/widgets → widgets}/abstractInfo.js +0 -0
  272. /package/lib/{v4/widgets → widgets}/abstractLink.js +0 -0
  273. /package/lib/{v4/widgets → widgets}/abstractSwitch.d.ts +0 -0
  274. /package/lib/{v4/widgets → widgets}/abstractSwitch.js +0 -0
  275. /package/lib/{v4/widgets → widgets}/abstractWidgetBase.js +0 -0
  276. /package/lib/{v4/widgets → widgets}/widgetHandler.js +0 -0
@@ -0,0 +1,178 @@
1
+ import { formatChanges, sanitizeForDiff } from "../../helpers/diffHelpers";
2
+ import { diff } from "deep-object-diff";
3
+ export class AbstractModel {
4
+ modelname;
5
+ attributes;
6
+ primaryKey;
7
+ identity;
8
+ constructor(modelname, attributes, primaryKey, identity) {
9
+ this.modelname = modelname;
10
+ this.attributes = attributes;
11
+ this.primaryKey = primaryKey;
12
+ this.identity = identity;
13
+ }
14
+ /**
15
+ * Generate diff between old and new records
16
+ */
17
+ generateDiff(oldRecord, newRecord) {
18
+ const cleanOldRecord = sanitizeForDiff(oldRecord);
19
+ const cleanNewRecord = sanitizeForDiff(newRecord);
20
+ const changesDiff = diff(cleanOldRecord, cleanNewRecord);
21
+ const formattedChanges = formatChanges(changesDiff, cleanOldRecord, cleanNewRecord);
22
+ return {
23
+ changesDiff,
24
+ formattedChanges,
25
+ cleanOldRecord,
26
+ cleanNewRecord
27
+ };
28
+ }
29
+ /**
30
+ * Generate delete diff (record vs empty object)
31
+ */
32
+ generateDeleteDiff(record) {
33
+ const cleanRecord = sanitizeForDiff(record);
34
+ const changesDiff = diff(cleanRecord, {});
35
+ const formattedChanges = formatChanges(changesDiff, cleanRecord, {});
36
+ return {
37
+ changesDiff,
38
+ formattedChanges,
39
+ cleanRecord
40
+ };
41
+ }
42
+ /**
43
+ * Log system event with diff
44
+ */
45
+ async logSystemEvent(dataAccessor, eventType, message, oldRecord, newRecord) {
46
+ let formattedChanges = [];
47
+ let summary = '';
48
+ switch (eventType) {
49
+ case 'Created':
50
+ const cleanNewRecord = sanitizeForDiff(newRecord);
51
+ formattedChanges = formatChanges({}, {}, cleanNewRecord, 'add');
52
+ summary = `Created ${formattedChanges.length} fields`;
53
+ break;
54
+ case 'Updated':
55
+ const { formattedChanges: updateChanges } = this.generateDiff(oldRecord, newRecord);
56
+ formattedChanges = updateChanges;
57
+ summary = `Changes ${formattedChanges.length} fields`;
58
+ break;
59
+ case 'Deleted':
60
+ const { formattedChanges: deleteChanges } = this.generateDeleteDiff(oldRecord);
61
+ formattedChanges = deleteChanges;
62
+ summary = `Deleted ${formattedChanges.length} fields`;
63
+ break;
64
+ }
65
+ // log system event notification
66
+ await dataAccessor.adminizer.logSystemEvent(eventType, message, {
67
+ changes: formattedChanges,
68
+ summary
69
+ });
70
+ }
71
+ async create(data, dataAccessor) {
72
+ let _data = await dataAccessor.setUserRelationAccess(dataAccessor.process(data));
73
+ let record = await this._create(_data);
74
+ // Log creation event
75
+ await this.logSystemEvent(dataAccessor, 'Created',
76
+ // @ts-ignore
77
+ `user ${dataAccessor.user.login} create ${dataAccessor.entity.name} ${record[this.primaryKey]}`, {}, _data);
78
+ return dataAccessor.process(record);
79
+ }
80
+ async findOne(criteria, dataAccessor) {
81
+ criteria = await dataAccessor.sanitizeUserRelationAccess(criteria);
82
+ let record = await this._findOne(criteria);
83
+ return record ? dataAccessor.process(record) : null;
84
+ }
85
+ async find(criteria, dataAccessor) {
86
+ criteria = await dataAccessor.sanitizeUserRelationAccess(criteria);
87
+ let records = await this._find(criteria);
88
+ return records.map(record => dataAccessor.process(record));
89
+ }
90
+ async updateOne(criteria, data, dataAccessor) {
91
+ let _data = dataAccessor.process(data);
92
+ // Get the old record first for diff
93
+ criteria = await dataAccessor.sanitizeUserRelationAccess(criteria);
94
+ const oldRecord = await this._findOne(criteria);
95
+ if (!oldRecord) {
96
+ return null;
97
+ }
98
+ let record = await this._updateOne(criteria, _data);
99
+ if (!record) {
100
+ return null;
101
+ }
102
+ // Log update event
103
+ await this.logSystemEvent(dataAccessor, 'Updated',
104
+ // @ts-ignore
105
+ `user ${dataAccessor.user.login} update ${dataAccessor.entity.name} ${record[this.primaryKey]}`, oldRecord, record);
106
+ return dataAccessor.process(record);
107
+ }
108
+ async update(criteria, data, dataAccessor) {
109
+ let _data = dataAccessor.process(data);
110
+ criteria = await dataAccessor.sanitizeUserRelationAccess(criteria);
111
+ // Get old records automatically
112
+ const oldRecords = await this._find(criteria);
113
+ let records = await this._update(criteria, _data);
114
+ // Log update events for each record
115
+ for (let i = 0; i < records.length; i++) {
116
+ const record = records[i];
117
+ const oldRecord = oldRecords[i];
118
+ if (oldRecord) {
119
+ await this.logSystemEvent(dataAccessor, 'Updated',
120
+ // @ts-ignore
121
+ `user ${dataAccessor.user.login} update ${dataAccessor.entity.name} ${record[this.primaryKey]}`, oldRecord, record);
122
+ }
123
+ }
124
+ return records.map(record => dataAccessor.process(record));
125
+ }
126
+ async destroyOne(criteria, dataAccessor) {
127
+ // Get the record first for diff
128
+ criteria = await dataAccessor.sanitizeUserRelationAccess(criteria);
129
+ const oldRecord = await this._findOne(criteria);
130
+ if (!oldRecord) {
131
+ return null;
132
+ }
133
+ let record = await this._destroyOne(criteria);
134
+ if (!record) {
135
+ return null;
136
+ }
137
+ // Log delete event
138
+ await this.logSystemEvent(dataAccessor, 'Deleted',
139
+ //@ts-ignore
140
+ `user ${dataAccessor.user.login} delete ${dataAccessor.entity.name} ${record[this.primaryKey]}`, oldRecord, {});
141
+ return dataAccessor.process(record);
142
+ }
143
+ async destroy(criteria, dataAccessor) {
144
+ // Get records first for diff
145
+ criteria = await dataAccessor.sanitizeUserRelationAccess(criteria);
146
+ const oldRecords = await this._find(criteria);
147
+ let records = await this._destroy(criteria);
148
+ // Log delete events for each record
149
+ for (let i = 0; i < records.length; i++) {
150
+ const record = records[i];
151
+ const oldRecord = oldRecords[i];
152
+ if (oldRecord) {
153
+ await this.logSystemEvent(dataAccessor, 'Deleted',
154
+ //@ts-ignore
155
+ `user ${dataAccessor.user.login} delete ${dataAccessor.entity.name} ${record[this.primaryKey]}`, oldRecord, {});
156
+ }
157
+ }
158
+ return records.map(record => dataAccessor.process(record));
159
+ }
160
+ async count(criteria, dataAccessor) {
161
+ criteria = await dataAccessor.sanitizeUserRelationAccess(criteria);
162
+ return this._count(criteria);
163
+ }
164
+ }
165
+ export class AbstractAdapter {
166
+ orm;
167
+ ormType;
168
+ constructor(type, orm) {
169
+ this.ormType = type;
170
+ this.orm = orm;
171
+ }
172
+ /**
173
+ * This method must ensure system models registration and throw an error in case of errors. We force all adapters to realize it
174
+ */
175
+ static registerSystemModels(orm) {
176
+ throw new Error("Function 'registerSystemModels' must be implemented by subclass");
177
+ }
178
+ }
@@ -1,4 +1,4 @@
1
- import { Adminizer } from "../../Adminizer";
1
+ import { Adminizer } from "../Adminizer";
2
2
  export class ModelHandler {
3
3
  models = new Map();
4
4
  add(modelName, modelInstance) {
@@ -22,14 +22,13 @@ export declare class SequelizeModel<T> extends AbstractModel<T> {
22
22
  protected _count(criteria?: Partial<T>): Promise<number>;
23
23
  private _buildIncludes;
24
24
  }
25
- /** SequelizeAdapter — адаптер, заменяющий WaterlineAdapter */
26
25
  export declare class SequelizeAdapter extends AbstractAdapter {
27
- private sequelize;
26
+ sequelize: Sequelize;
28
27
  Model: typeof SequelizeModel;
29
28
  constructor(sequelize: Sequelize);
30
29
  get models(): Record<string, any>;
31
30
  getModel(modelName: string): any;
32
31
  getAttributes(modelName: string): any;
33
- /**Registration of system models*/
34
- static registerSystemModels(sequelize: Sequelize): Promise<void>;
32
+ /** Registration of system models */
33
+ static registerSystemModels(sequelize: Sequelize, alter?: boolean): Promise<void>;
35
34
  }
@@ -15,7 +15,11 @@ function generateAssociationsFromSchema(models, schemas) {
15
15
  const targetModel = models[field.collection];
16
16
  const targetSchema = schemas[field.collection];
17
17
  const inverseField = targetSchema?.[field.via];
18
- const throughTableName = [modelName, field.collection].sort().join("");
18
+ // Normalize join table to lowercase for consistency across dialects
19
+ const throughTableName = [modelName, field.collection]
20
+ .sort()
21
+ .join("")
22
+ .toLowerCase();
19
23
  // 💡 M:N связь
20
24
  if (inverseField && inverseField.collection === modelName) {
21
25
  model.belongsToMany(targetModel, {
@@ -170,6 +174,10 @@ export class SequelizeModel extends AbstractModel {
170
174
  if (value === null) {
171
175
  result[targetKey] = { [Op.is]: null };
172
176
  }
177
+ else if (Array.isArray(value)) {
178
+ // ✅ Обработка массивов - используем оператор IN
179
+ result[targetKey] = { [Op.in]: value };
180
+ }
173
181
  else if (typeof value === "object" && !Array.isArray(value)) {
174
182
  const operatorEntries = Object.entries(value)
175
183
  .filter(([_, v]) => v !== undefined && v !== null)
@@ -321,7 +329,7 @@ export class SequelizeModel extends AbstractModel {
321
329
  }
322
330
  await instance.reload({ include: Object.values(this.model.associations) });
323
331
  const pk = this.primaryKey;
324
- const fresh = await this.model.findByPk(instance.get(pk), { include: assocNames.map(a => ({ association: a })) });
332
+ const fresh = (await this.model.findByPk(instance.get(pk), { include: assocNames.map(a => ({ association: a })) })).toJSON();
325
333
  // console.debug(">> Результат после reload:", fresh?.toJSON());
326
334
  return fresh;
327
335
  }
@@ -440,7 +448,7 @@ export class SequelizeModel extends AbstractModel {
440
448
  where,
441
449
  include: Object.values(this.model.associations)
442
450
  });
443
- return reloaded.map(r => r.get({ plain: true }));
451
+ return reloaded.map((r) => r.get({ plain: true }));
444
452
  }
445
453
  // --- DESTROY ONE ---
446
454
  async _destroyOne(criteria) {
@@ -510,7 +518,7 @@ export class SequelizeModel extends AbstractModel {
510
518
  }
511
519
  }
512
520
  }
513
- const raw = records.map(r => r.get({ plain: true }));
521
+ const raw = records.map((r) => r.get({ plain: true }));
514
522
  await this.model.destroy({ where });
515
523
  return raw;
516
524
  }
@@ -525,7 +533,6 @@ export class SequelizeModel extends AbstractModel {
525
533
  return Object.keys(this.model.associations).map(key => ({ association: key }));
526
534
  }
527
535
  }
528
- /** SequelizeAdapter — адаптер, заменяющий WaterlineAdapter */
529
536
  export class SequelizeAdapter extends AbstractAdapter {
530
537
  sequelize;
531
538
  Model = SequelizeModel;
@@ -547,18 +554,33 @@ export class SequelizeAdapter extends AbstractAdapter {
547
554
  const model = this.getModel(modelName);
548
555
  return model?.getAttributes();
549
556
  }
550
- /**Registration of system models*/
551
- static async registerSystemModels(sequelize) {
552
- let modelsDir = path.resolve(import.meta.dirname, "../../../../models");
553
- if (!fs.existsSync(modelsDir)) {
554
- modelsDir = path.resolve(import.meta.dirname, "../../../../src/models");
557
+ /** Registration of system models */
558
+ static async registerSystemModels(sequelize, alter = true) {
559
+ // Try multiple possible model locations
560
+ const possiblePaths = [
561
+ path.resolve(import.meta.dirname, "../../../../models"),
562
+ path.resolve(import.meta.dirname, "../../../../src/models"),
563
+ path.resolve(import.meta.dirname, "../../../models"),
564
+ path.resolve(import.meta.dirname, "../../../src/models"),
565
+ path.resolve(process.cwd(), "node_modules/adminizer/src/models"),
566
+ path.resolve(process.cwd(), "node_modules/adminizer/models")
567
+ ];
568
+ let modelsDir = null;
569
+ for (const possiblePath of possiblePaths) {
570
+ if (fs.existsSync(possiblePath)) {
571
+ modelsDir = possiblePath;
572
+ break;
573
+ }
555
574
  }
556
- let files = fs.readdirSync(modelsDir).filter(f => f.endsWith(".js"));
575
+ if (!modelsDir) {
576
+ return;
577
+ }
578
+ let files = fs.readdirSync(modelsDir).filter((f) => f.endsWith(".js"));
557
579
  if (!files.length) {
558
- files = fs.readdirSync(modelsDir).filter(f => f.endsWith(".ts") && !f.endsWith(".d.ts"));
580
+ files = fs.readdirSync(modelsDir).filter((f) => f.endsWith(".ts") && !f.endsWith(".d.ts"));
559
581
  }
560
582
  if (!files.length) {
561
- throw `Model files not found in dir ${modelsDir}`;
583
+ return;
562
584
  }
563
585
  const schemas = {};
564
586
  for (const file of files) {
@@ -570,7 +592,12 @@ export class SequelizeAdapter extends AbstractAdapter {
570
592
  generateSequelizeModel(sequelize, modelName, definition);
571
593
  }
572
594
  generateAssociationsFromSchema(sequelize.models, schemas);
573
- await sequelize.sync();
595
+ // Check ORM_ALTER environment variable before sync
596
+ if (process.env.ORM_ALTER !== 'false' && alter) {
597
+ await sequelize.sync();
598
+ }
599
+ else {
600
+ }
574
601
  }
575
602
  }
576
603
  /**Generation of the SEQUELIZE model from the definition, analogue of Waterinecollection.extenD*/
@@ -0,0 +1,163 @@
1
+ import { EventEmitter } from 'events';
2
+ import { Adminizer } from '../Adminizer';
3
+ import { INotification, INotificationEvent } from '../../interfaces/types';
4
+ import { NotificationAPModel } from "../../models/NotificationAP";
5
+ import { UserAP } from "../../models/UserAP";
6
+ /**
7
+ * ░█████╗░██████╗░░██████╗████████╗██████╗░░█████╗░░█████╗░████████╗
8
+ * ██╔══██╗██╔══██╗██╔════╝╚══██╔══╝██╔══██╗██╔══██╗██╔══██╗╚══██╔══╝
9
+ * ███████║██████╦╝╚█████╗░░░░██║░░░██████╔╝███████║██║░░╚═╝░░░██║░░░
10
+ * ██╔══██║██╔══██╗░╚═══██╗░░░██║░░░██╔══██╗██╔══██║██║░░██╗░░░██║░░░
11
+ * ██║░░██║██████╦╝██████╔╝░░░██║░░░██║░░██║██║░░██║╚█████╔╝░░░██║░░░
12
+ * ╚═╝░░╚═╝╚═════╝░╚═════╝░░░░╚═╝░░░╚═╝░░╚═╝╚═╝░░╚═╝░╚════╝░░░░╚═╝░░░
13
+ *
14
+ * ███╗░░██╗░█████╗░████████╗██╗███████╗██╗░█████╗░░█████╗░████████╗██╗░█████╗░███╗░░██╗
15
+ * ████╗░██║██╔══██╗╚══██╔══╝██║██╔════╝██║██╔══██╗██╔══██╗╚══██╔══╝██║██╔══██╗████╗░██║
16
+ * ██╔██╗██║██║░░██║░░░██║░░░██║█████╗░░██║██║░░╚═╝███████║░░░██║░░░██║██║░░██║██╔██╗██║
17
+ * ██║╚████║██║░░██║░░░██║░░░██║██╔══╝░░██║██║░░██╗██╔══██║░░░██║░░░██║██║░░██║██║╚████║
18
+ * ██║░╚███║╚█████╔╝░░░██║░░░██║██║░░░░░██║╚█████╔╝██║░░██║░░░██║░░░██║╚█████╔╝██║░╚███║
19
+ * ╚═╝░░╚══╝░╚════╝░░░░╚═╝░░░╚═╝╚═╝░░░░░╚═╝░╚════╝░╚═╝░░╚═╝░░░╚═╝░░░╚═╝░╚════╝░╚═╝░░╚══╝
20
+ *
21
+ * Class: AbstractNotificationService
22
+ *
23
+ * Description:
24
+ * The `AbstractNotificationService` is a base class that provides the fundamental structure and functionality
25
+ * for implementing notification services in the application. It manages client connections, dispatches notifications,
26
+ * tracks user notification status, and handles database interactions for storing and retrieving notification data.
27
+ * This abstract class is intended to be extended by concrete notification service implementations.
28
+ *
29
+ * Key Features:
30
+ * - Manages a registry of clients associated with users using a nested Map structure.
31
+ * - Provides methods to add, remove, and retrieve clients.
32
+ * - Dispatches notifications to connected clients via an abstract method to be implemented by subclasses.
33
+ * - Sends heartbeat (ping) messages to keep client connections alive.
34
+ * - Creates and retrieves user notification records in the database.
35
+ * - Fetches paginated and filtered notification lists for a specific user.
36
+ * - Supports searching notifications by message content.
37
+ * - Marks notifications as read or all notifications as read for a user.
38
+ * - Emits events using Node.js EventEmitter for extensibility and integration.
39
+ *
40
+ * Usage Example:
41
+ * Subclasses should override the `dispatchNotification` method to implement custom notification logic.
42
+ *
43
+ * @abstract
44
+ * @extends EventEmitter
45
+ * @property {Map<number, Map<string, Function>>} clients - A map of user IDs to their client maps.
46
+ * @property {Adminizer} adminizer - Reference to the Adminizer instance for accessing helpers and models.
47
+ * @property {string} notificationClass - Abstract property representing the class name of the notification.
48
+ * @property {string} icon - Abstract property for the notification icon.
49
+ * @property {string} iconColor - Abstract property for the notification icon color.
50
+ */
51
+ export declare abstract class AbstractNotificationService extends EventEmitter {
52
+ protected clients: Map<number, Map<string, (event: INotificationEvent) => void>>;
53
+ protected adminizer: Adminizer;
54
+ abstract readonly notificationClass: string;
55
+ abstract readonly icon: string;
56
+ abstract readonly iconColor: string;
57
+ constructor(adminizer: Adminizer);
58
+ private _bindAccessRight;
59
+ /**
60
+ * Registers a client for a user and associates it with a send function.
61
+ * If the user does not yet have a Map of clients, one is created.
62
+ * The client is then added to the user's Map with the provided send function.
63
+ * @param {string} clientId - The unique identifier of the client to be registered.
64
+ * @param {(event: INotificationEvent) => void} sendFn - The function used to send notifications to the client.
65
+ * @param {UserAP} user - The user associated with the client.
66
+ */
67
+ addClient(clientId: string, sendFn: (event: INotificationEvent) => void, user: UserAP): void;
68
+ /**
69
+ * Removes a client from the internal tracking structure.
70
+ * If the user associated with the client has no more clients after removal, the user's Map is also removed.
71
+ * @param {string} clientId - The unique identifier of the client to be removed.
72
+ */
73
+ removeClient(clientId: string): void;
74
+ /**
75
+ * Retrieves the Map of clients associated with a specific user.
76
+ * If the user has no clients, an empty Map is returned.
77
+ * @param {number} userId - The unique identifier of the user whose clients should be retrieved.
78
+ * @returns {Map<string, (event: INotificationEvent) => void>} A Map where the keys are client IDs and the values are send functions.
79
+ */
80
+ getUserClients(userId: number): Map<string, (event: INotificationEvent) => void>;
81
+ /**
82
+ * Returns the total number of users who have at least one connected client.
83
+ * @returns {number} The count of users with active clients.
84
+ */
85
+ getClientCount(): number;
86
+ /**
87
+ * Aggregates all clients from all users into a single Map for backward compatibility.
88
+ * Each client is uniquely identified by its ID and mapped to its corresponding send function.
89
+ * @returns {Map<string, (event: INotificationEvent) => void>} A combined Map of all client IDs and their send functions.
90
+ */
91
+ getAllClients(): Map<string, (event: INotificationEvent) => void>;
92
+ /**
93
+ * Abstract method that must be implemented by subclasses to dispatch a notification.
94
+ * @param {Omit<INotification, 'id' | 'createdAt' | 'notificationClass' | 'icon'>} notification - The notification data to be dispatched (excluding id, createdAt, notificationClass, and icon).
95
+ * @returns {Promise<boolean>} A promise that resolves to a boolean indicating whether the notification was successfully dispatched.
96
+ */
97
+ abstract dispatchNotification(notification: Omit<INotification, 'id' | 'createdAt' | 'notificationClass' | 'icon'>): Promise<boolean>;
98
+ /**
99
+ * Sends a notification event to all connected clients.
100
+ * Iterates through each user's client Map and invokes the send function for each client.
101
+ * If sending fails, logs an error and removes the client.
102
+ * @param {INotificationEvent} event - The notification event to broadcast.
103
+ */
104
+ protected broadcast(event: INotificationEvent): void;
105
+ /**
106
+ * Sends a heartbeat (ping) message to a specific client.
107
+ * Searches for the client across all user client Maps and sends a heartbeat event if found.
108
+ * @param {string} clientId - The unique identifier of the client to receive the heartbeat.
109
+ */
110
+ sendHeartbeat(clientId: string): void;
111
+ /**
112
+ * Creates a new user notification record in the 'usernotificationap' model.
113
+ * This method is used to associate a notification with a specific user and mark it as unread.
114
+ * @param {string} notificationId - The unique identifier of the notification.
115
+ * @param {number} [userId] - The ID of the user to associate the notification with. Optional.
116
+ * @returns {Promise<void>} A promise that resolves when the record is created or an error is logged.
117
+ */
118
+ protected createUserNotification(notificationId: string, userId?: number): Promise<void>;
119
+ /**
120
+ * Retrieves a user notification record from the 'usernotificationap' model.
121
+ * The record is fetched based on the provided notification ID and user ID.
122
+ * @param {string} notificationId - The unique identifier of the notification.
123
+ * @param {number} userId - The ID of the user associated with the notification.
124
+ * @returns {Promise<any>} A promise that resolves with the found record or null if not found or an error occurs.
125
+ */
126
+ protected getUserNotification(notificationId: string, userId: number): Promise<any>;
127
+ /**
128
+ * Retrieves a paginated list of notifications for a specific user, optionally filtered by read status.
129
+ * @param {number} userId - The ID of the user whose notifications should be fetched.
130
+ * @param {number} [limit=20] - The maximum number of notifications to return.
131
+ * @param {number} [skip=0] - The number of notifications to skip (used for pagination).
132
+ * @param {boolean} [unreadOnly=false] - Whether to return only unread notifications.
133
+ * @returns {Promise<INotification[]>} A promise that resolves with an array of notification objects.
134
+ */
135
+ getNotifications(userId: number, limit?: number, skip?: number, unreadOnly?: boolean): Promise<INotification[]>;
136
+ /**
137
+ * Prepares notification data by enriching it with user-specific read status and icon information.
138
+ * @param {NotificationAPModel[]} notificationsDB - An array of raw notification records from the database.
139
+ * @param {number} userId - The ID of the user for whom the read status is determined.
140
+ * @returns {Promise<INotification[]>} A promise that resolves with an array of enriched notification objects.
141
+ */
142
+ protected prepareNotification(notificationsDB: NotificationAPModel[], userId: number): Promise<INotification[]>;
143
+ /**
144
+ * Searches for notifications for a specific user based on a message keyword.
145
+ * @param {string} s - The search string used to filter notifications by message content.
146
+ * @param {number} userId - The ID of the user whose notifications should be searched.
147
+ * @returns {Promise<INotification[]>} A promise that resolves with an array of matching notification objects.
148
+ */
149
+ search(s: string, userId: number): Promise<INotification[]>;
150
+ /**
151
+ * Marks a specific notification as read for a given user.
152
+ * @param {number} userId - The ID of the user for whom the notification should be marked as read.
153
+ * @param {string} id - The ID of the notification to mark as read.
154
+ * @returns {Promise<void>} A promise that resolves when the operation is complete.
155
+ */
156
+ markAsRead(userId: number, id: string): Promise<void>;
157
+ /**
158
+ * Marks all notifications as read for a given user.
159
+ * @param {number} userId - The ID of the user for whom all notifications should be marked as read.
160
+ * @returns {Promise<void>} A promise that resolves when the operation is complete.
161
+ */
162
+ markAllAsRead(userId: number): Promise<void>;
163
+ }