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.
- package/assets/add-1E1UsMVZ.js +1 -0
- package/assets/add-form-CGa9QbwE.js +1 -0
- package/assets/add-group-7yAK6uFY.js +1 -0
- package/assets/add-group-form-BNmpvW8K.js +1 -0
- package/assets/add-user-NoYkTfZi.js +1 -0
- package/assets/add-user-form-DrclnTOd.js +1 -0
- package/assets/{app-CVnUv8s_.css → app-D8oqdt8J.css} +1 -1
- package/assets/app-layout-i2zN9Rn-.js +1 -0
- package/assets/app-shell-CHEYhVco.js +1 -0
- package/assets/app-sidebar-DeF8WNmW.js +1 -0
- package/assets/app-sidebar-header-DdM1EOrb.js +1 -0
- package/assets/app.js +1699 -1657
- package/assets/breadcrumbs-C439DWrC.js +1 -0
- package/assets/catalog-B9hdop59.js +6 -0
- package/assets/dashboard-Bi5KeZu2.js +3 -0
- package/assets/date-picker-DbJ8nHp5.js +1 -0
- package/assets/del-modal-_b3CSNHb.js +1 -0
- package/assets/dynamic-controls-BOYmRX_z.js +1 -0
- package/assets/field-renderer-4obZ_jUo.js +407 -0
- package/assets/{field-renderer-CsBa_tMB.css → field-renderer-CRNLShJD.css} +1 -1
- package/assets/form-Bte5kzjB.js +1 -0
- package/assets/form-oUlsuu7h.js +1 -0
- package/assets/geo-json-CadjdhwW.js +4 -0
- package/assets/handsontable-PVakPtJV.js +281 -0
- package/assets/home-CsUXA-O6.js +2 -0
- package/assets/icon-DM49pZW3.js +1 -0
- package/assets/init-user-DNrB8Xt5.js +1 -0
- package/assets/input-error--IOnWghB.js +1 -0
- package/assets/jsoneditor-D95dIAP2.js +3878 -0
- package/assets/list-CdfSnSN1.js +1 -0
- package/assets/list-table-Byzv346I.js +22 -0
- package/assets/login-6Xb8bcPq.js +1 -0
- package/assets/manifest.json +331 -221
- package/assets/material-icon-BedgD2NE.js +1 -0
- package/assets/media-manager-UQT6RhI8.js +14 -0
- package/assets/module-B4-7WkY5.js +1 -0
- package/assets/monaco-ICtx6xoc.js +11 -0
- package/assets/nav-footer-BwkQkwow.js +1 -0
- package/assets/nav-main-9Eb_A0rf.js +1 -0
- package/assets/nav-user-CRAfyE-x.js +1 -0
- package/assets/notification-ma2x7vka.js +27 -0
- package/assets/pagination-render-xsEktzA8.js +1 -0
- package/assets/register-BIyjCy8M.js +1 -0
- package/assets/tabs-BK_5wdPA.js +1 -0
- package/assets/theme-switcher-CKiJbMkh.js +1 -0
- package/assets/toast-editor-BZM5RHph.js +845 -0
- package/assets/user-info-CcYdmjMZ.js +1 -0
- package/assets/user-menu-content-DbUk8ZeY.js +1 -0
- package/assets/vanilla-picker-l5rcX3cq.js +8 -0
- package/assets/vendor-wzZzN_Jv.js +17 -0
- package/assets/welcome-cWh1qnl_.js +1 -0
- package/controllers/add.js +1 -1
- package/controllers/catalog/FrontentCatalogAdapter.d.ts +4 -4
- package/controllers/ckeditorUpload.d.ts +1 -1
- package/controllers/ckeditorUpload.js +50 -26
- package/controllers/edit.js +2 -2
- package/controllers/form.js +29 -13
- package/controllers/list.js +2 -2
- package/controllers/login.js +2 -2
- package/controllers/media-manager/mediaManagerAdapter.js +13 -2
- package/controllers/media-manager/mediaManagerApi.js +4 -3
- package/controllers/navigation/edit.js +1 -1
- package/controllers/notifications/NotificationController.d.ts +11 -0
- package/controllers/notifications/NotificationController.js +219 -0
- package/controllers/remove.js +2 -1
- package/controllers/view.js +1 -1
- package/files/favicon.png +0 -0
- package/helpers/configHelper.d.ts +1 -1
- package/helpers/controllerHelper.js +1 -1
- package/helpers/diffHelpers.d.ts +2 -0
- package/helpers/diffHelpers.js +69 -0
- package/helpers/fieldsHelper.d.ts +1 -1
- package/helpers/fieldsHelper.js +1 -1
- package/helpers/formHelper.js +3 -1
- package/helpers/inertiaAddHelper.d.ts +1 -1
- package/helpers/inertiaAutHelper.js +19 -0
- package/helpers/inertiaFromHelper.js +12 -0
- package/helpers/translationHelper.d.ts +1 -1
- package/helpers/translationHelper.js +1 -1
- package/index.d.ts +16 -14
- package/index.js +16 -14
- package/interfaces/adminpanelConfig.d.ts +18 -0
- package/interfaces/global.d.ts +3 -3
- package/interfaces/types.d.ts +44 -2
- package/lib/Adminizer.d.ts +16 -8
- package/lib/Adminizer.js +95 -12
- package/lib/{v4/DataAccessor.d.ts → DataAccessor.d.ts} +5 -5
- package/lib/{v4/DataAccessor.js → DataAccessor.js} +3 -3
- package/lib/{v4/I18n.js → I18n.js} +2 -2
- package/lib/{v4/PolicyManager.d.ts → PolicyManager.d.ts} +1 -1
- package/lib/{v4/PolicyManager.js → PolicyManager.js} +2 -2
- package/lib/{v4/catalog → catalog}/AbstractCatalog.d.ts +1 -1
- package/lib/{v4/catalog → catalog}/Navigation.d.ts +2 -2
- package/lib/{v4/catalog → catalog}/Navigation.js +1 -1
- package/lib/{v4/controls → controls}/AbstractControls.d.ts +1 -1
- package/lib/{v4/controls → controls}/codeEditor/MonacoEditor.d.ts +1 -1
- package/lib/{v4/controls → controls}/geojsoneditor/GeoEditor.d.ts +1 -1
- package/lib/{v4/controls → controls}/jsoneditor/JsonEditor.d.ts +1 -1
- package/lib/{v4/controls → controls}/markdown/ToastUiEditor.d.ts +1 -1
- package/lib/{v4/controls → controls}/table/Handsontable.d.ts +1 -1
- package/lib/{v4/controls → controls}/wysiwyg/CKeditor.d.ts +1 -1
- package/lib/{v4/controls → controls}/wysiwyg/CKeditor.js +1 -1
- package/lib/{v4/datatable → datatable}/NodeTable.d.ts +1 -1
- package/lib/media-manager/AbstractMediaManager.d.ts +3 -3
- package/lib/media-manager/AbstractMediaManager.js +2 -2
- package/lib/media-manager/DefaultMediaManager.d.ts +1 -1
- package/lib/media-manager/DefaultMediaManager.js +6 -8
- package/lib/media-manager/Items.d.ts +2 -1
- package/lib/media-manager/Items.js +7 -0
- package/lib/media-manager/helpers/MediaManagerHelper.d.ts +10 -0
- package/lib/media-manager/helpers/MediaManagerHelper.js +27 -10
- package/lib/{v4/model → model}/AbstractModel.d.ts +12 -0
- package/lib/model/AbstractModel.js +178 -0
- package/lib/{v4/model → model}/ModelHandler.js +1 -1
- package/lib/{v4/model → model}/adapter/sequelize.d.ts +3 -4
- package/lib/{v4/model → model}/adapter/sequelize.js +41 -14
- package/lib/notifications/AbstractNotificationService.d.ts +163 -0
- package/lib/notifications/AbstractNotificationService.js +353 -0
- package/lib/notifications/GeneralNotificationService.d.ts +8 -0
- package/lib/notifications/GeneralNotificationService.js +61 -0
- package/lib/notifications/NotificationHandler.d.ts +27 -0
- package/lib/notifications/NotificationHandler.js +41 -0
- package/lib/notifications/SystemNotificationService.d.ts +19 -0
- package/lib/notifications/SystemNotificationService.js +173 -0
- package/lib/{v4/widgets → widgets}/abstractCustom.d.ts +1 -1
- package/lib/{v4/widgets → widgets}/abstractLink.d.ts +1 -1
- package/lib/{v4/widgets → widgets}/abstractWidgetBase.d.ts +1 -1
- package/lib/{v4/widgets → widgets}/widgetHandler.d.ts +2 -2
- package/migrations/index.d.ts +3 -0
- package/migrations/index.js +4 -0
- package/migrations/umzug/0001.d.ts +7 -0
- package/migrations/umzug/0001.js +121 -0
- package/migrations/umzugExports.d.ts +2 -0
- package/migrations/umzugExports.js +9 -0
- package/models/MediaManagerAP.d.ts +9 -3
- package/models/MediaManagerAP.js +11 -5
- package/models/MediaManagerAssociationsAP.d.ts +9 -3
- package/models/MediaManagerAssociationsAP.js +11 -5
- package/models/MediaManagerMetaAP.d.ts +9 -3
- package/models/MediaManagerMetaAP.js +11 -5
- package/models/NotificationAP.d.ts +44 -0
- package/models/NotificationAP.js +30 -0
- package/models/UserAP.d.ts +1 -1
- package/models/UserNotificationAP.d.ts +26 -0
- package/models/UserNotificationAP.js +18 -0
- package/package.json +17 -8
- package/system/Router.js +21 -7
- package/system/bindAssets.js +1 -0
- package/system/bindControls.js +6 -6
- package/system/bindInertia.js +9 -3
- package/system/bindMediaManager.js +2 -1
- package/system/bindNavigation.js +3 -4
- package/system/bindNotifications.d.ts +2 -0
- package/system/bindNotifications.js +14 -0
- package/system/bindReqFunctions.js +8 -25
- package/system/defaults.js +6 -0
- package/translations/ar.json +5 -4
- package/translations/cn.json +5 -4
- package/translations/de.json +72 -71
- package/translations/en.json +5 -4
- package/translations/es.json +5 -4
- package/translations/fr.json +5 -4
- package/translations/hi.json +5 -4
- package/translations/jp.json +5 -4
- package/translations/ko.json +5 -4
- package/translations/pt.json +5 -4
- package/translations/ru.json +2 -1
- package/translations/ua.json +5 -4
- package/translations/uz.json +73 -72
- package/translations/vi.json +5 -4
- package/ui/components/VanillaJSONEditor.d.ts +2 -0
- package/ui/components/VanillaJSONEditor.js +101 -0
- package/ui/components/handsontable.d.ts +10 -0
- package/ui/components/handsontable.js +51 -0
- package/ui/components/monaco-editor.d.ts +11 -0
- package/ui/components/monaco-editor.js +57 -0
- package/ui/components/ui/table.d.ts +1 -1
- package/ui/components/ui/table.js +2 -2
- package/ui/hooks/form-state.d.ts +4 -0
- package/ui/hooks/form-state.js +45 -0
- package/ui/hooks/use-appearance.d.ts +7 -0
- package/ui/hooks/use-appearance.js +76 -0
- package/ui/hooks/use-window-size.d.ts +5 -0
- package/ui/hooks/use-window-size.js +21 -0
- package/assets/VanillaJSONEditor-x8tDbio1.js +0 -3833
- package/assets/add-DOaZgoTz.js +0 -1
- package/assets/add-form-BjackXJe.js +0 -1
- package/assets/add-group-Br44s2D6.js +0 -1
- package/assets/add-group-form-YI6KS37x.js +0 -1
- package/assets/add-user-DWcH0s9-.js +0 -1
- package/assets/add-user-form-bv5BNX5A.js +0 -1
- package/assets/app-layout-DecN527i.js +0 -1
- package/assets/app-shell-DHzMWA1L.js +0 -1
- package/assets/app-sidebar-4JFP8UdU.js +0 -1
- package/assets/app-sidebar-header-CIKLfLsF.js +0 -1
- package/assets/breadcrumbs-Bo2z-LNC.js +0 -1
- package/assets/catalog-DVyrGTNR.js +0 -6
- package/assets/dashboard-Dy3pk9t7.js +0 -3
- package/assets/date-picker-B2z-aeuv.js +0 -1
- package/assets/debounce-D6eaSoDg.js +0 -1
- package/assets/del-modal-l3LxJWCN.js +0 -1
- package/assets/dynamic-controls-CSVVCONh.js +0 -1
- package/assets/field-renderer-BHo944Cn.js +0 -407
- package/assets/form-BXfnz6h5.js +0 -1
- package/assets/form-hP8sjbND.js +0 -1
- package/assets/form-state-702tb_BV.js +0 -1
- package/assets/geo-json-CVaNUZbb.js +0 -4
- package/assets/handsontable-CZQ5Ietj.js +0 -347
- package/assets/home-BD4MWmm9.js +0 -2
- package/assets/icon-C_X2cQ6W.js +0 -1
- package/assets/index-C148XJoK.js +0 -1
- package/assets/index-Vcq4gwWv.js +0 -1
- package/assets/init-user-DgCD4ZN1.js +0 -1
- package/assets/input-error-Bab-HPbn.js +0 -1
- package/assets/list-C-27EH39.js +0 -1
- package/assets/list-table-_OiQmr2A.js +0 -22
- package/assets/login-jzvVXgAV.js +0 -1
- package/assets/material-icon-DsiW0iQG.js +0 -1
- package/assets/media-manager-CSB_XRcE.js +0 -14
- package/assets/module-Ceq6vuwa.js +0 -1
- package/assets/monaco-editor-XX8Hz76f.js +0 -11
- package/assets/multi-select-BbKSrMw2.js +0 -1
- package/assets/nav-footer-B8gMV6Wp.js +0 -1
- package/assets/nav-main-BUp9qA8R.js +0 -1
- package/assets/nav-user-DeF3SDRj.js +0 -1
- package/assets/pagination-render-B9U_hVbB.js +0 -1
- package/assets/register-C6acWsLe.js +0 -1
- package/assets/theme-switcher-D8T9NgWG.js +0 -1
- package/assets/toast-editor-C0550yms.js +0 -845
- package/assets/use-appearance-CXP5cHaQ.js +0 -1
- package/assets/use-window-size-DLSEoR6e.js +0 -1
- package/assets/user-info-CAgkqARx.js +0 -1
- package/assets/user-menu-content-BxL0QMC9.js +0 -1
- package/assets/vanilla-picker-B6E6ObS_.js +0 -8
- package/assets/welcome-Dk7GoS5R.js +0 -1
- package/lib/v4/model/AbstractModel.js +0 -67
- package/lib/v4/model/adapter/typeorm.d.ts +0 -23
- package/lib/v4/model/adapter/typeorm.js +0 -68
- package/migrations/20230303171134-adminpanel-init.js +0 -122
- package/migrations/20231223215835-add-widgets.js +0 -32
- package/migrations/20240804191001-added-navigationap.js +0 -53
- package/migrations/20241121184236-v4-init.js +0 -134
- /package/lib/{v4/I18n.d.ts → I18n.d.ts} +0 -0
- /package/lib/{v4/POWCaptcha.d.ts → POWCaptcha.d.ts} +0 -0
- /package/lib/{v4/POWCaptcha.js → POWCaptcha.js} +0 -0
- /package/lib/{v4/catalog → catalog}/AbstractCatalog.js +0 -0
- /package/lib/{v4/catalog → catalog}/CatalogHandler.d.ts +0 -0
- /package/lib/{v4/catalog → catalog}/CatalogHandler.js +0 -0
- /package/lib/{v4/controls → controls}/AbstractControls.js +0 -0
- /package/lib/{v4/controls → controls}/ControlsHandler.d.ts +0 -0
- /package/lib/{v4/controls → controls}/ControlsHandler.js +0 -0
- /package/lib/{v4/controls → controls}/codeEditor/MonacoEditor.js +0 -0
- /package/lib/{v4/controls → controls}/geojsoneditor/GeoEditor.js +0 -0
- /package/lib/{v4/controls → controls}/jsoneditor/JsonEditor.js +0 -0
- /package/lib/{v4/controls → controls}/markdown/ToastUiEditor.js +0 -0
- /package/lib/{v4/controls → controls}/table/Handsontable.js +0 -0
- /package/lib/{v4/datatable → datatable}/NodeTable.js +0 -0
- /package/lib/{v4/helper → helper}/jwt.d.ts +0 -0
- /package/lib/{v4/helper → helper}/jwt.js +0 -0
- /package/lib/{v4/inertia → inertia}/flash.d.ts +0 -0
- /package/lib/{v4/inertia → inertia}/flash.js +0 -0
- /package/lib/{v4/inertia → inertia}/inertiaAdapter.d.ts +0 -0
- /package/lib/{v4/inertia → inertia}/inertiaAdapter.js +0 -0
- /package/lib/{v4/model → model}/ModelHandler.d.ts +0 -0
- /package/lib/{v4/model → model}/adapter/waterline.d.ts +0 -0
- /package/lib/{v4/model → model}/adapter/waterline.js +0 -0
- /package/lib/{v4/widgets → widgets}/abstractAction.d.ts +0 -0
- /package/lib/{v4/widgets → widgets}/abstractAction.js +0 -0
- /package/lib/{v4/widgets → widgets}/abstractCustom.js +0 -0
- /package/lib/{v4/widgets → widgets}/abstractInfo.d.ts +0 -0
- /package/lib/{v4/widgets → widgets}/abstractInfo.js +0 -0
- /package/lib/{v4/widgets → widgets}/abstractLink.js +0 -0
- /package/lib/{v4/widgets → widgets}/abstractSwitch.d.ts +0 -0
- /package/lib/{v4/widgets → widgets}/abstractSwitch.js +0 -0
- /package/lib/{v4/widgets → widgets}/abstractWidgetBase.js +0 -0
- /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
|
+
}
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
553
|
-
|
|
554
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
+
}
|