adminizer 4.3.0-build.80 → 4.3.0-build.81

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 (167) hide show
  1. package/assets/add-ChaPnvFv.js +1 -0
  2. package/assets/{add-form-C04ZI5LT.js → add-form-DNqlORQQ.js} +1 -1
  3. package/assets/add-group-D6YsQVrC.js +1 -0
  4. package/assets/{add-group-form-CyRwxTs3.js → add-group-form-CDi1y4af.js} +1 -1
  5. package/assets/{add-user-form-yp020h4r.js → add-user-form-BzXHahSY.js} +1 -1
  6. package/assets/add-user-psdNyGx8.js +1 -0
  7. package/assets/{app-CpGtjhxF.css → app-PLVuwM-H.css} +1 -1
  8. package/assets/{app-layout-BmT5KQwc.js → app-layout-AWzEe1aH.js} +1 -1
  9. package/assets/{app-sidebar-4lm9f_CG.js → app-sidebar-Bd4nWVXq.js} +1 -1
  10. package/assets/app-sidebar-header-jBFp1qZb.js +1 -0
  11. package/assets/app.js +3 -3
  12. package/assets/{catalog-CG_EB2kz.js → catalog-XiWBZptC.js} +1 -1
  13. package/assets/{dashboard-CsmJfdLb.js → dashboard-DcRJErQB.js} +1 -1
  14. package/assets/date-picker-A6CYw4SC.js +1 -0
  15. package/assets/del-modal-0klf0PNC.js +1 -0
  16. package/assets/{field-renderer-Bp1lfm0v.js → field-renderer-CYB5W2He.js} +3 -3
  17. package/assets/form-B-he6_tT.js +1 -0
  18. package/assets/{form-CSTeMDf5.js → form-BGRpsR-M.js} +1 -1
  19. package/assets/{home-D00XjLjD.js → home-B7aZTHTv.js} +2 -2
  20. package/assets/icon-k6999vVE.js +1 -0
  21. package/assets/{init-user-L-oxFTOv.js → init-user-Btm16ElL.js} +1 -1
  22. package/assets/input-error-Dbp_lp61.js +1 -0
  23. package/assets/list-_tZumymo.js +1 -0
  24. package/assets/{list-table-vIvydJV2.js → list-table-OzVUH7i4.js} +4 -4
  25. package/assets/{login-DLMJ6l_r.js → login-DeqxLVx3.js} +1 -1
  26. package/assets/manifest.json +74 -74
  27. package/assets/material-icon-B9BY_7p9.js +1 -0
  28. package/assets/{media-manager-ASGJ4PcS.js → media-manager-DQ5CA_na.js} +1 -1
  29. package/assets/{module-mxngd2cA.js → module-Drcr0QjC.js} +1 -1
  30. package/assets/{nav-footer-DbGCtmmN.js → nav-footer-DWHxIgb7.js} +1 -1
  31. package/assets/{nav-main-BcWhl71g.js → nav-main-CRT9ezos.js} +1 -1
  32. package/assets/notification-C7K-zK4k.js +27 -0
  33. package/assets/{register-C_W_hOmq.js → register-CMhPasDj.js} +1 -1
  34. package/assets/tabs-GDFuClHU.js +1 -0
  35. package/assets/{theme-switcher-D1G6OSMc.js → theme-switcher-C35zBCil.js} +1 -1
  36. package/assets/{welcome-FHRCdke0.js → welcome-CbhDDQlg.js} +1 -1
  37. package/controllers/add.js +1 -1
  38. package/controllers/catalog/FrontentCatalogAdapter.d.ts +4 -4
  39. package/controllers/edit.js +1 -15
  40. package/controllers/list.js +2 -2
  41. package/controllers/login.js +2 -2
  42. package/controllers/navigation/edit.js +1 -1
  43. package/controllers/notifications/NotificationController.d.ts +5 -5
  44. package/controllers/notifications/NotificationController.js +77 -78
  45. package/controllers/remove.js +1 -11
  46. package/controllers/view.js +1 -1
  47. package/helpers/configHelper.d.ts +1 -1
  48. package/helpers/fieldsHelper.d.ts +1 -1
  49. package/helpers/fieldsHelper.js +1 -1
  50. package/helpers/inertiaAddHelper.d.ts +1 -1
  51. package/helpers/translationHelper.d.ts +1 -1
  52. package/helpers/translationHelper.js +1 -1
  53. package/index.d.ts +14 -13
  54. package/index.js +14 -13
  55. package/interfaces/adminpanelConfig.d.ts +1 -1
  56. package/interfaces/global.d.ts +3 -3
  57. package/interfaces/types.d.ts +10 -1
  58. package/lib/Adminizer.d.ts +7 -7
  59. package/lib/Adminizer.js +7 -7
  60. package/lib/{v4/DataAccessor.d.ts → DataAccessor.d.ts} +4 -4
  61. package/lib/{v4/DataAccessor.js → DataAccessor.js} +3 -3
  62. package/lib/{v4/I18n.js → I18n.js} +2 -2
  63. package/lib/{v4/PolicyManager.d.ts → PolicyManager.d.ts} +1 -1
  64. package/lib/{v4/PolicyManager.js → PolicyManager.js} +2 -2
  65. package/lib/{v4/catalog → catalog}/AbstractCatalog.d.ts +1 -1
  66. package/lib/{v4/catalog → catalog}/Navigation.d.ts +2 -2
  67. package/lib/{v4/catalog → catalog}/Navigation.js +1 -1
  68. package/lib/{v4/controls → controls}/AbstractControls.d.ts +1 -1
  69. package/lib/{v4/controls → controls}/codeEditor/MonacoEditor.d.ts +1 -1
  70. package/lib/{v4/controls → controls}/geojsoneditor/GeoEditor.d.ts +1 -1
  71. package/lib/{v4/controls → controls}/jsoneditor/JsonEditor.d.ts +1 -1
  72. package/lib/{v4/controls → controls}/markdown/ToastUiEditor.d.ts +1 -1
  73. package/lib/{v4/controls → controls}/table/Handsontable.d.ts +1 -1
  74. package/lib/{v4/controls → controls}/wysiwyg/CKeditor.d.ts +1 -1
  75. package/lib/{v4/datatable → datatable}/NodeTable.d.ts +1 -1
  76. package/lib/{v4/model → model}/AbstractModel.d.ts +12 -0
  77. package/lib/model/AbstractModel.js +178 -0
  78. package/lib/{v4/model → model}/ModelHandler.js +1 -1
  79. package/lib/{v4/model → model}/adapter/sequelize.d.ts +0 -1
  80. package/lib/{v4/model → model}/adapter/sequelize.js +6 -3
  81. package/lib/{v4/notifications → notifications}/AbstractNotificationService.d.ts +14 -7
  82. package/lib/notifications/AbstractNotificationService.js +233 -0
  83. package/lib/{v4/notifications → notifications}/GeneralNotificationService.d.ts +1 -1
  84. package/lib/{v4/notifications → notifications}/GeneralNotificationService.js +4 -2
  85. package/lib/notifications/NotificationHandler.d.ts +27 -0
  86. package/lib/notifications/NotificationHandler.js +41 -0
  87. package/lib/{v4/notifications → notifications}/SystemNotificationService.d.ts +5 -4
  88. package/lib/notifications/SystemNotificationService.js +173 -0
  89. package/lib/{v4/widgets → widgets}/abstractCustom.d.ts +1 -1
  90. package/lib/{v4/widgets → widgets}/abstractLink.d.ts +1 -1
  91. package/lib/{v4/widgets → widgets}/abstractWidgetBase.d.ts +1 -1
  92. package/lib/{v4/widgets → widgets}/widgetHandler.d.ts +2 -2
  93. package/migrations/index.d.ts +3 -0
  94. package/migrations/index.js +4 -0
  95. package/migrations/index.ts +5 -0
  96. package/migrations/umzug/0001.d.ts +7 -0
  97. package/migrations/umzug/0001.js +119 -0
  98. package/migrations/umzug/0001.ts +132 -0
  99. package/migrations/umzug.d.ts +2 -0
  100. package/migrations/umzug.js +8 -0
  101. package/migrations/umzug.ts +9 -0
  102. package/models/NotificationAP.d.ts +8 -0
  103. package/models/NotificationAP.js +8 -0
  104. package/models/UserAP.d.ts +1 -1
  105. package/package.json +5 -4
  106. package/system/Router.js +4 -8
  107. package/system/bindControls.js +6 -6
  108. package/system/bindInertia.js +4 -3
  109. package/system/bindNavigation.js +1 -1
  110. package/system/bindNotifications.js +3 -3
  111. package/system/bindReqFunctions.js +8 -25
  112. package/system/defaults.js +1 -1
  113. package/ui/components/ui/table.d.ts +1 -1
  114. package/ui/components/ui/table.js +2 -2
  115. package/assets/add-DSapOiv0.js +0 -1
  116. package/assets/add-group-DWR7SBTO.js +0 -1
  117. package/assets/add-user-acATGxUL.js +0 -1
  118. package/assets/app-sidebar-header-CxXmzszq.js +0 -1
  119. package/assets/date-picker-By3kj-Rx.js +0 -1
  120. package/assets/del-modal-DlcbcdR5.js +0 -1
  121. package/assets/form-CxHRGp7p.js +0 -1
  122. package/assets/icon-ButfCBrt.js +0 -1
  123. package/assets/input-error-CHsDLcK0.js +0 -1
  124. package/assets/list-4TOTsjqE.js +0 -1
  125. package/assets/material-icon-sXk4BK-N.js +0 -1
  126. package/assets/notification-BEfA5sT6.js +0 -1
  127. package/assets/tabs-CgZW_LLn.js +0 -1
  128. package/lib/v4/model/AbstractModel.js +0 -79
  129. package/lib/v4/notifications/AbstractNotificationService.js +0 -150
  130. package/lib/v4/notifications/NotificationHandler.d.ts +0 -69
  131. package/lib/v4/notifications/NotificationHandler.js +0 -127
  132. package/lib/v4/notifications/SystemNotificationService.js +0 -124
  133. /package/lib/{v4/I18n.d.ts → I18n.d.ts} +0 -0
  134. /package/lib/{v4/POWCaptcha.d.ts → POWCaptcha.d.ts} +0 -0
  135. /package/lib/{v4/POWCaptcha.js → POWCaptcha.js} +0 -0
  136. /package/lib/{v4/catalog → catalog}/AbstractCatalog.js +0 -0
  137. /package/lib/{v4/catalog → catalog}/CatalogHandler.d.ts +0 -0
  138. /package/lib/{v4/catalog → catalog}/CatalogHandler.js +0 -0
  139. /package/lib/{v4/controls → controls}/AbstractControls.js +0 -0
  140. /package/lib/{v4/controls → controls}/ControlsHandler.d.ts +0 -0
  141. /package/lib/{v4/controls → controls}/ControlsHandler.js +0 -0
  142. /package/lib/{v4/controls → controls}/codeEditor/MonacoEditor.js +0 -0
  143. /package/lib/{v4/controls → controls}/geojsoneditor/GeoEditor.js +0 -0
  144. /package/lib/{v4/controls → controls}/jsoneditor/JsonEditor.js +0 -0
  145. /package/lib/{v4/controls → controls}/markdown/ToastUiEditor.js +0 -0
  146. /package/lib/{v4/controls → controls}/table/Handsontable.js +0 -0
  147. /package/lib/{v4/controls → controls}/wysiwyg/CKeditor.js +0 -0
  148. /package/lib/{v4/datatable → datatable}/NodeTable.js +0 -0
  149. /package/lib/{v4/helper → helper}/jwt.d.ts +0 -0
  150. /package/lib/{v4/helper → helper}/jwt.js +0 -0
  151. /package/lib/{v4/inertia → inertia}/flash.d.ts +0 -0
  152. /package/lib/{v4/inertia → inertia}/flash.js +0 -0
  153. /package/lib/{v4/inertia → inertia}/inertiaAdapter.d.ts +0 -0
  154. /package/lib/{v4/inertia → inertia}/inertiaAdapter.js +0 -0
  155. /package/lib/{v4/model → model}/ModelHandler.d.ts +0 -0
  156. /package/lib/{v4/model → model}/adapter/waterline.d.ts +0 -0
  157. /package/lib/{v4/model → model}/adapter/waterline.js +0 -0
  158. /package/lib/{v4/widgets → widgets}/abstractAction.d.ts +0 -0
  159. /package/lib/{v4/widgets → widgets}/abstractAction.js +0 -0
  160. /package/lib/{v4/widgets → widgets}/abstractCustom.js +0 -0
  161. /package/lib/{v4/widgets → widgets}/abstractInfo.d.ts +0 -0
  162. /package/lib/{v4/widgets → widgets}/abstractInfo.js +0 -0
  163. /package/lib/{v4/widgets → widgets}/abstractLink.js +0 -0
  164. /package/lib/{v4/widgets → widgets}/abstractSwitch.d.ts +0 -0
  165. /package/lib/{v4/widgets → widgets}/abstractSwitch.js +0 -0
  166. /package/lib/{v4/widgets → widgets}/abstractWidgetBase.js +0 -0
  167. /package/lib/{v4/widgets → widgets}/widgetHandler.js +0 -0
@@ -1,4 +1,4 @@
1
- import { Adminizer } from "../../Adminizer";
1
+ import { Adminizer } from "../Adminizer";
2
2
  export type ControlType = 'wysiwyg' | 'jsonEditor' | 'geoJson' | 'markdown' | 'table' | 'codeEditor';
3
3
  export interface Path {
4
4
  jsPath: {
@@ -1,5 +1,5 @@
1
1
  import { AbstractControls, ControlType, Config, Path } from "../AbstractControls";
2
- import { Adminizer } from "../../../Adminizer";
2
+ import { Adminizer } from "../../Adminizer";
3
3
  export declare class MonacoEditor extends AbstractControls {
4
4
  readonly config: Config;
5
5
  readonly name: string;
@@ -1,5 +1,5 @@
1
1
  import { AbstractControls, ControlType, Config, Path } from "../AbstractControls";
2
- import { Adminizer } from "../../../Adminizer";
2
+ import { Adminizer } from "../../Adminizer";
3
3
  export declare class GeoEditor extends AbstractControls {
4
4
  readonly config: Config;
5
5
  readonly name: string;
@@ -1,6 +1,6 @@
1
1
  import { AbstractControls, ControlType, Path, Config } from "../AbstractControls";
2
2
  import { EditorOptions } from "@toast-ui/editor/types/editor";
3
- import { Adminizer } from "../../../Adminizer";
3
+ import { Adminizer } from "../../Adminizer";
4
4
  export declare class JsonEditor extends AbstractControls {
5
5
  readonly config: Partial<EditorOptions>;
6
6
  readonly name: string;
@@ -1,6 +1,6 @@
1
1
  import { AbstractControls, ControlType, Path, Config } from "../AbstractControls";
2
2
  import { EditorOptions } from "@toast-ui/editor/types/editor";
3
- import { Adminizer } from "../../../Adminizer";
3
+ import { Adminizer } from "../../Adminizer";
4
4
  export declare class ToastUiEditor extends AbstractControls {
5
5
  readonly config: Partial<EditorOptions>;
6
6
  readonly name: string;
@@ -1,5 +1,5 @@
1
1
  import { AbstractControls, ControlType, Config, Path } from "../AbstractControls";
2
- import { Adminizer } from "../../../Adminizer";
2
+ import { Adminizer } from "../../Adminizer";
3
3
  export declare class Handsontable extends AbstractControls {
4
4
  readonly config: Config;
5
5
  readonly name: string;
@@ -1,5 +1,5 @@
1
1
  import { AbstractControls, ControlType, Config, Path } from "../AbstractControls";
2
- import { Adminizer } from "../../../Adminizer";
2
+ import { Adminizer } from "../../Adminizer";
3
3
  export declare class CKeditor extends AbstractControls {
4
4
  readonly name: string;
5
5
  readonly type: ControlType;
@@ -1,4 +1,4 @@
1
- import { Fields } from '../../../helpers/fieldsHelper';
1
+ import { Fields } from '../../helpers/fieldsHelper';
2
2
  import { AbstractModel } from '../model/AbstractModel';
3
3
  import { DataAccessor } from "../DataAccessor";
4
4
  interface Request {
@@ -46,6 +46,18 @@ export declare abstract class AbstractModel<T> {
46
46
  protected abstract _destroyOne(criteria: Partial<T>): Promise<T | null>;
47
47
  protected abstract _destroy(criteria: Partial<T>): Promise<T[]>;
48
48
  protected abstract _count(criteria: Partial<T> | undefined): Promise<number>;
49
+ /**
50
+ * Generate diff between old and new records
51
+ */
52
+ private generateDiff;
53
+ /**
54
+ * Generate delete diff (record vs empty object)
55
+ */
56
+ private generateDeleteDiff;
57
+ /**
58
+ * Log system event with diff
59
+ */
60
+ private logSystemEvent;
49
61
  create(data: T, dataAccessor: DataAccessor): Promise<Partial<T>>;
50
62
  findOne(criteria: Partial<T>, dataAccessor: DataAccessor): Promise<Partial<T> | null>;
51
63
  find(criteria: Partial<T>, dataAccessor: DataAccessor): Promise<Partial<T>[]>;
@@ -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) {
@@ -26,7 +26,6 @@ export declare class SequelizeModel<T> extends AbstractModel<T> {
26
26
  export declare class SequelizeAdapter extends AbstractAdapter {
27
27
  private sequelize;
28
28
  Model: typeof SequelizeModel;
29
- alter: boolean;
30
29
  constructor(sequelize: Sequelize);
31
30
  get models(): Record<string, any>;
32
31
  getModel(modelName: string): any;
@@ -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, {
@@ -533,7 +537,6 @@ export class SequelizeModel extends AbstractModel {
533
537
  export class SequelizeAdapter extends AbstractAdapter {
534
538
  sequelize;
535
539
  Model = SequelizeModel;
536
- alter = false;
537
540
  constructor(sequelize) {
538
541
  super("sequelize", sequelize);
539
542
  this.sequelize = sequelize;
@@ -575,7 +578,7 @@ export class SequelizeAdapter extends AbstractAdapter {
575
578
  generateSequelizeModel(sequelize, modelName, definition);
576
579
  }
577
580
  generateAssociationsFromSchema(sequelize.models, schemas);
578
- if (alter === true) {
581
+ if (alter) {
579
582
  await sequelize.sync();
580
583
  }
581
584
  }
@@ -1,22 +1,29 @@
1
1
  import { EventEmitter } from 'events';
2
- import { Adminizer } from '../../Adminizer';
3
- import { INotification, INotificationEvent } from '../../../interfaces/types';
2
+ import { Adminizer } from '../Adminizer';
3
+ import { INotification, INotificationEvent } from '../../interfaces/types';
4
+ import { NotificationAPModel } from "../../models/NotificationAP";
5
+ import { UserAP } from "../../models/UserAP";
4
6
  export declare abstract class AbstractNotificationService extends EventEmitter {
5
- protected clients: Map<string, (event: INotificationEvent) => void>;
7
+ protected clients: Map<number, Map<string, (event: INotificationEvent) => void>>;
6
8
  protected adminizer: Adminizer;
7
9
  abstract readonly notificationClass: string;
8
10
  abstract readonly icon: string;
9
11
  abstract readonly iconColor: string;
10
12
  constructor(adminizer: Adminizer);
11
- addClient(clientId: string, sendFn: (event: INotificationEvent) => void): void;
13
+ private _bindAccessRight;
14
+ addClient(clientId: string, sendFn: (event: INotificationEvent) => void, user: UserAP): void;
12
15
  removeClient(clientId: string): void;
16
+ getUserClients(userId: number): Map<string, (event: INotificationEvent) => void>;
17
+ getClientCount(): number;
18
+ getAllClients(): Map<string, (event: INotificationEvent) => void>;
13
19
  abstract dispatchNotification(notification: Omit<INotification, 'id' | 'createdAt' | 'notificationClass' | 'icon'>): Promise<boolean>;
14
20
  protected broadcast(event: INotificationEvent): void;
15
21
  sendHeartbeat(clientId: string): void;
16
- protected generateId(): string;
17
- getClientCount(): number;
18
22
  protected createUserNotification(notificationId: string, userId?: number): Promise<void>;
19
23
  protected getUserNotification(notificationId: string, userId: number): Promise<any>;
20
- getNotifications(userId: number, limit?: number, unreadOnly?: boolean): Promise<INotification[]>;
24
+ getNotifications(userId: number, limit?: number, skip?: number, unreadOnly?: boolean): Promise<INotification[]>;
25
+ protected prepareNotification(notificationsDB: NotificationAPModel[], userId: number): Promise<INotification[]>;
26
+ search(s: string, userId: number): Promise<INotification[]>;
21
27
  markAsRead(userId: number, id: string): Promise<void>;
28
+ markAllAsRead(userId: number): Promise<void>;
22
29
  }
@@ -0,0 +1,233 @@
1
+ import { EventEmitter } from 'events';
2
+ import { Adminizer } from '../Adminizer';
3
+ export class AbstractNotificationService extends EventEmitter {
4
+ clients = new Map();
5
+ adminizer;
6
+ constructor(adminizer) {
7
+ super();
8
+ this.adminizer = adminizer;
9
+ this._bindAccessRight();
10
+ }
11
+ _bindAccessRight() {
12
+ setTimeout(() => {
13
+ this.adminizer.accessRightsHelper.registerToken({
14
+ id: `notification-${this.notificationClass}`,
15
+ name: this.notificationClass,
16
+ description: `Access to notification ${this.notificationClass}`,
17
+ department: 'notification',
18
+ });
19
+ }, 100);
20
+ }
21
+ // Добавление клиента
22
+ addClient(clientId, sendFn, user) {
23
+ const userId = user.id;
24
+ // Если у пользователя еще нет Map клиентов - создаем
25
+ if (!this.clients.has(userId)) {
26
+ this.clients.set(userId, new Map());
27
+ }
28
+ // Получаем Map клиентов пользователя и добавляем нового клиента
29
+ const userClients = this.clients.get(userId);
30
+ userClients.set(clientId, sendFn);
31
+ Adminizer.log.info(`[${this.notificationClass}] Client ${clientId} connected for user ${userId}. Total users: ${this.clients.size}, user clients: ${userClients.size}`);
32
+ }
33
+ // Удаление клиента
34
+ removeClient(clientId) {
35
+ // Ищем клиента во всех пользовательских Map
36
+ for (const [userId, userClients] of this.clients.entries()) {
37
+ if (userClients.has(clientId)) {
38
+ userClients.delete(clientId);
39
+ // Если у пользователя больше нет клиентов - удаляем его Map
40
+ if (userClients.size === 0) {
41
+ this.clients.delete(userId);
42
+ }
43
+ Adminizer.log.info(`[${this.notificationClass}] Client ${clientId} disconnected from user ${userId}. Total users: ${this.clients.size}`);
44
+ return;
45
+ }
46
+ }
47
+ Adminizer.log.warn(`[${this.notificationClass}] Client ${clientId} not found for removal`);
48
+ }
49
+ // Получение клиентов конкретного пользователя
50
+ getUserClients(userId) {
51
+ return this.clients.get(userId) || new Map();
52
+ }
53
+ // Получение количества подключенных клиентов
54
+ getClientCount() {
55
+ return this.clients.size;
56
+ }
57
+ // Получение всех клиентов (для обратной совместимости)
58
+ getAllClients() {
59
+ const allClients = new Map();
60
+ for (const userClients of this.clients.values()) {
61
+ for (const [clientId, sendFn] of userClients.entries()) {
62
+ allClients.set(clientId, sendFn);
63
+ }
64
+ }
65
+ return allClients;
66
+ }
67
+ // Рассылка события всем клиентам
68
+ broadcast(event) {
69
+ for (const userClients of this.clients.values()) {
70
+ userClients.forEach((sendFn, clientId) => {
71
+ try {
72
+ sendFn(event);
73
+ }
74
+ catch (error) {
75
+ Adminizer.log.error(`[${this.notificationClass}] Error sending to client ${clientId}:`, error);
76
+ this.removeClient(clientId);
77
+ }
78
+ });
79
+ }
80
+ }
81
+ // Отправка heartbeat
82
+ sendHeartbeat(clientId) {
83
+ for (const userClients of this.clients.values()) {
84
+ const sendFn = userClients.get(clientId);
85
+ if (sendFn) {
86
+ const heartbeatEvent = {
87
+ type: 'heartbeat',
88
+ data: 'ping'
89
+ };
90
+ sendFn(heartbeatEvent);
91
+ return;
92
+ }
93
+ }
94
+ }
95
+ // Метод для создания записи в UserNotificationAP
96
+ async createUserNotification(notificationId, userId) {
97
+ if (this.adminizer.modelHandler.model.has('usernotificationap') && userId) {
98
+ try {
99
+ await this.adminizer.modelHandler.model.get('usernotificationap')["_create"]({
100
+ userId: userId,
101
+ notificationId: notificationId,
102
+ read: false
103
+ });
104
+ }
105
+ catch (error) {
106
+ Adminizer.log.error(`Error creating user notification record:`, error);
107
+ }
108
+ }
109
+ }
110
+ // Метод для получения UserNotificationAP по ID уведомления и пользователя
111
+ async getUserNotification(notificationId, userId) {
112
+ if (this.adminizer.modelHandler.model.has('usernotificationap')) {
113
+ try {
114
+ return await this.adminizer.modelHandler.model.get('usernotificationap')["_findOne"]({
115
+ where: { notificationId: notificationId, userId: userId }
116
+ });
117
+ }
118
+ catch (error) {
119
+ Adminizer.log.error(`Error getting user notification:`, error);
120
+ return null;
121
+ }
122
+ }
123
+ return null;
124
+ }
125
+ async getNotifications(userId, limit = 20, skip = 0, unreadOnly = false) {
126
+ if (!this.adminizer.modelHandler.model.has('notificationap')) {
127
+ return [];
128
+ }
129
+ try {
130
+ let query = { notificationClass: this.notificationClass };
131
+ // Если запрашиваются уведомления для конкретного пользователя получаем ID уведомлений пользователя
132
+ const userNotifications = await this.adminizer.modelHandler.model.get('usernotificationap')["_find"]({
133
+ where: {
134
+ userId: userId
135
+ }
136
+ }, { populate: [['notificationId', {}]] });
137
+ const notificationIds = userNotifications.map((un) => un.notificationId.id);
138
+ if (unreadOnly) {
139
+ // Только непрочитанные
140
+ query.id = userNotifications
141
+ .filter((un) => !un.read)
142
+ .map((un) => un.notificationId.id);
143
+ }
144
+ else {
145
+ query.id = notificationIds;
146
+ }
147
+ if (query.id.length === 0)
148
+ return [];
149
+ const notificationsDB = await this.adminizer.modelHandler.model.get('notificationap')["_find"]({
150
+ where: query,
151
+ sort: 'createdAt DESC',
152
+ limit: limit,
153
+ skip: skip
154
+ });
155
+ return await this.prepareNotification(notificationsDB, userId);
156
+ }
157
+ catch (error) {
158
+ Adminizer.log.error('Error fetching notifications:', error);
159
+ return [];
160
+ }
161
+ }
162
+ async prepareNotification(notificationsDB, userId) {
163
+ let notifications = [];
164
+ for (const notification of notificationsDB) {
165
+ let readStatus = false;
166
+ // Получаем статус прочтения из UserNotificationAP
167
+ const userNotification = await this.getUserNotification(notification.id, userId);
168
+ readStatus = userNotification ? userNotification.read : false;
169
+ notifications.push({
170
+ ...notification,
171
+ read: readStatus,
172
+ icon: {
173
+ icon: this.icon,
174
+ iconColor: this.iconColor
175
+ },
176
+ });
177
+ }
178
+ return notifications;
179
+ }
180
+ async search(s, userId) {
181
+ try {
182
+ const userNotifications = await this.adminizer.modelHandler.model.get('usernotificationap')["_find"]({
183
+ where: {
184
+ userId: userId
185
+ }
186
+ }, { populate: [['notificationId', {}]] });
187
+ const notificationIds = userNotifications.map((un) => un.notificationId.id);
188
+ if (notificationIds.length === 0)
189
+ return [];
190
+ const notificationsDB = await this.adminizer.modelHandler.model.get('notificationap')["_find"]({
191
+ where: {
192
+ message: { contains: s },
193
+ id: notificationIds,
194
+ notificationClass: this.notificationClass
195
+ },
196
+ sort: "createdAt DESC"
197
+ });
198
+ return await this.prepareNotification(notificationsDB, userId);
199
+ }
200
+ catch (error) {
201
+ Adminizer.log.error('Error searching notifications:', error);
202
+ return [];
203
+ }
204
+ }
205
+ async markAsRead(userId, id) {
206
+ try {
207
+ const userNotification = await this.getUserNotification(id, userId);
208
+ if (userNotification) {
209
+ await this.adminizer.modelHandler.model.get('usernotificationap')["_update"]({
210
+ id: userNotification.id
211
+ }, { read: true });
212
+ }
213
+ }
214
+ catch (error) {
215
+ Adminizer.log.error('Error marking notification as read:', error);
216
+ }
217
+ }
218
+ async markAllAsRead(userId) {
219
+ try {
220
+ const userNotifications = await this.adminizer.modelHandler.model.get('usernotificationap')["_find"]({
221
+ where: { userId: userId }
222
+ });
223
+ for (const userNotification of userNotifications) {
224
+ await this.adminizer.modelHandler.model.get('usernotificationap')["_update"]({
225
+ where: { id: userNotification.id },
226
+ }, { read: true });
227
+ }
228
+ }
229
+ catch (error) {
230
+ Adminizer.log.error('Error marking all notifications as read:', error);
231
+ }
232
+ }
233
+ }
@@ -1,5 +1,5 @@
1
1
  import { AbstractNotificationService } from './AbstractNotificationService';
2
- import { INotification } from '../../../interfaces/types';
2
+ import { INotification } from '../../interfaces/types';
3
3
  export declare class GeneralNotificationService extends AbstractNotificationService {
4
4
  readonly notificationClass = "general";
5
5
  readonly icon: string;
@@ -1,5 +1,5 @@
1
1
  import { AbstractNotificationService } from './AbstractNotificationService';
2
- import { Adminizer } from '../../Adminizer';
2
+ import { Adminizer } from '../Adminizer';
3
3
  export class GeneralNotificationService extends AbstractNotificationService {
4
4
  notificationClass = 'general';
5
5
  icon = 'info';
@@ -23,7 +23,9 @@ export class GeneralNotificationService extends AbstractNotificationService {
23
23
  const users = await this.adminizer.modelHandler.model.get('userap')["_find"]({});
24
24
  for (const user of users) {
25
25
  try {
26
- await this.createUserNotification(notificationDB.id, user.id);
26
+ if (this.adminizer.accessRightsHelper.hasPermission(`notification-${this.notificationClass}`, user)) {
27
+ await this.createUserNotification(notificationDB.id, user.id);
28
+ }
27
29
  }
28
30
  catch (error) {
29
31
  Adminizer.log.error('Error creating UserNotificationAP:', error);
@@ -0,0 +1,27 @@
1
+ import { EventEmitter } from 'events';
2
+ import { AbstractNotificationService } from "./AbstractNotificationService";
3
+ import { INotification } from '../../interfaces/types';
4
+ export declare class NotificationHandler extends EventEmitter {
5
+ private services;
6
+ constructor();
7
+ /**
8
+ * Register a new notification service
9
+ * @param service
10
+ */
11
+ registerService(service: AbstractNotificationService): void;
12
+ /**
13
+ * Get a notification service by class
14
+ * @param notificationClass
15
+ */
16
+ getService(notificationClass: string): AbstractNotificationService | undefined;
17
+ /**
18
+ * Get all registered services
19
+ */
20
+ getAllServices(): AbstractNotificationService[];
21
+ /**
22
+ * Dispatch a notification to a service
23
+ * @param notificationClass
24
+ * @param notification
25
+ */
26
+ dispatchNotification(notificationClass: string, notification: Omit<INotification, 'id' | 'createdAt' | 'notificationClass' | 'icon'>): Promise<boolean>;
27
+ }