adminizer 4.7.0-build.224 → 5.0.0-build.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Readme.md +2 -1
- package/assets/add-De0wyF0A.js +1 -0
- package/assets/add-group-CMWybgIR.js +1 -0
- package/assets/add-user-BxP23daH.js +1 -0
- package/assets/app-CtWuWD5N.css +1 -0
- package/assets/app-layout-GYMU2HO1.js +1 -0
- package/assets/app.js +45 -50
- package/assets/catalog-tImW1BnS.js +6 -0
- package/assets/controls/chunks/_commonjsHelpers-C6fGbg64.js +6 -0
- package/assets/controls/chunks/index-CjiZ2FIa.js +93 -0
- package/assets/controls/chunks/jsx-runtime-jIxVPx5o.js +31 -0
- package/assets/controls/chunks/use-appearance-CFLOwiSX.js +38 -0
- package/assets/controls/chunks/use-window-size-BXgBKW65.js +19 -0
- package/assets/controls/chunks/vanilla-picker-Dg97NL4h.js +529 -0
- package/assets/controls/ckeditor.es.js +83683 -0
- package/assets/controls/handsontable.css +1 -0
- package/assets/controls/handsontable.es.js +70585 -0
- package/assets/controls/jsoneditor.css +1 -0
- package/assets/controls/jsoneditor.es.js +42082 -0
- package/assets/controls/leaflet.css +1 -0
- package/assets/controls/leaflet.es.js +10299 -0
- package/assets/controls/monaco.es.js +618 -0
- package/assets/controls/toast-ui.css +1 -0
- package/assets/controls/toast-ui.es.js +41797 -0
- package/assets/dashboard-CQ4atH3N.js +3 -0
- package/assets/debounce-BMmXVQ06.js +1 -0
- package/assets/history-BileQPEi.js +1 -0
- package/assets/init-user-8aUohVDq.js +1 -0
- package/assets/list-Pz_5mfSq.js +4 -0
- package/assets/login-CO4laf6m.js +1 -0
- package/assets/manifest.json +66 -602
- package/assets/media-manager-BDbpSSjH.js +6 -0
- package/assets/media-manager-BZPjl98I.css +1 -0
- package/assets/module-CQiOdgZN.js +1 -0
- package/assets/notification-BIyv1smE.js +20 -0
- package/assets/register-DBYIMXGQ.js +1 -0
- package/assets/ru-rYKdQbWF.js +1 -0
- package/assets/use-appearance-_EbbjHTh.js +1 -0
- package/assets/user-filters-list-Aq8CvCeh.js +1 -0
- package/assets/welcome-DxhWYzUL.js +1 -0
- package/controllers/add.d.ts +1 -1
- package/controllers/addGroup.js +3 -3
- package/controllers/addUser.js +3 -3
- package/controllers/catalog/Catalog.d.ts +1 -1
- package/controllers/catalog/Catalog.js +2 -1
- package/controllers/catalog/FrontendCatalogAdapter.d.ts +2 -12
- package/controllers/catalog/FrontendCatalogAdapter.js +3 -16
- package/controllers/catalog/FrontentCatalogAdapter.d.ts +2 -12
- package/controllers/catalog/FrontentCatalogAdapter.js +0 -16
- package/controllers/edit.js +6 -11
- package/controllers/editGroup.d.ts +1 -1
- package/controllers/editGroup.js +3 -3
- package/controllers/editUser.d.ts +1 -1
- package/controllers/editUser.js +3 -3
- package/controllers/feed.js +1 -1
- package/controllers/filter-fields/columns.js +1 -1
- package/controllers/filter-fields/getAllUserFilters.js +2 -2
- package/controllers/filter-fields/groups.js +2 -2
- package/controllers/filter-fields/savedFilters.js +1 -1
- package/controllers/history-actions/HistoryController.js +1 -1
- package/controllers/initUser.js +2 -2
- package/controllers/list.d.ts +1 -1
- package/controllers/login.d.ts +1 -1
- package/controllers/login.js +3 -3
- package/controllers/media-manager/ThumbController.js +2 -2
- package/controllers/media-manager/mediaManagerApi.js +13 -15
- package/controllers/register.d.ts +1 -1
- package/controllers/register.js +5 -5
- package/controllers/remove.d.ts +1 -1
- package/controllers/userApiKey.js +2 -2
- package/controllers/view.d.ts +1 -1
- package/controllers/view.js +5 -5
- package/controllers/widgets/Action.js +5 -2
- package/controllers/widgets/filterInfo.js +1 -1
- package/files/favicon.png +0 -0
- package/helpers/accessRightsHelper.d.ts +6 -4
- package/helpers/accessRightsHelper.js +13 -0
- package/helpers/diffHelpers.js +1 -1
- package/helpers/fieldsHelper.d.ts +1 -1
- package/helpers/fieldsHelper.js +8 -4
- package/helpers/filterDatetimeHelper.d.ts +1 -1
- package/helpers/inertiaAddHelper.d.ts +6 -5
- package/helpers/inertiaAddHelper.js +45 -37
- package/helpers/inertiaAutHelper.js +5 -5
- package/helpers/inertiaGroupHelper.d.ts +4 -4
- package/helpers/inertiaMenuHelper.js +1 -1
- package/helpers/inertiaUserHelper.d.ts +3 -3
- package/helpers/inertiaUserHelper.js +1 -1
- package/helpers/menuHelper.d.ts +2 -2
- package/index.d.ts +19 -6
- package/index.js +19 -6
- package/interfaces/adminpanelConfig.d.ts +10 -33
- package/interfaces/global.d.ts +10 -15
- package/interfaces/listQueryBuilder.d.ts +1 -1
- package/lib/Adminizer.d.ts +25 -2
- package/lib/Adminizer.js +124 -50
- package/lib/DataAccessor.d.ts +9 -5
- package/lib/DataAccessor.js +89 -42
- package/lib/access-rights/AccessRightsHandler.d.ts +17 -0
- package/lib/access-rights/AccessRightsHandler.js +63 -0
- package/lib/ai-assistant/AbstractAiModelService.d.ts +5 -8
- package/lib/ai-assistant/AbstractAiModelService.js +3 -14
- package/lib/ai-assistant/AiAssistantHandler.d.ts +2 -2
- package/lib/app-manager/AdminizerApp.d.ts +228 -0
- package/lib/app-manager/AdminizerApp.js +17 -0
- package/lib/app-manager/AppManager.d.ts +18 -0
- package/lib/app-manager/AppManager.js +537 -0
- package/lib/app-manager/AssetHandler.d.ts +17 -0
- package/lib/app-manager/AssetHandler.js +67 -0
- package/lib/app-manager/ConfigLayerHandler.d.ts +18 -0
- package/lib/app-manager/ConfigLayerHandler.js +119 -0
- package/lib/app-manager/ControllerHandler.d.ts +25 -0
- package/lib/app-manager/ControllerHandler.js +140 -0
- package/lib/catalog/AbstractCatalog.d.ts +28 -19
- package/lib/catalog/AbstractCatalog.js +41 -10
- package/lib/catalog/CatalogHandler.d.ts +13 -0
- package/lib/catalog/CatalogHandler.js +48 -2
- package/lib/catalog/CatalogTemplateComponentHandler.d.ts +23 -0
- package/lib/catalog/CatalogTemplateComponentHandler.js +59 -0
- package/lib/controls/Control.d.ts +17 -0
- package/lib/controls/Control.js +1 -0
- package/lib/controls/ControlsHandler.d.ts +5 -6
- package/lib/controls/ControlsHandler.js +0 -13
- package/lib/controls/codeEditor/MonacoEditor.d.ts +3 -4
- package/lib/controls/codeEditor/MonacoEditor.js +13 -8
- package/lib/controls/geojsoneditor/GeoEditor.d.ts +3 -4
- package/lib/controls/geojsoneditor/GeoEditor.js +14 -9
- package/lib/controls/jsoneditor/JsonEditor.d.ts +5 -6
- package/lib/controls/jsoneditor/JsonEditor.js +14 -13
- package/lib/controls/markdown/ToastUiEditor.d.ts +5 -6
- package/lib/controls/markdown/ToastUiEditor.js +14 -13
- package/lib/controls/table/Handsontable.d.ts +5 -6
- package/lib/controls/table/Handsontable.js +14 -13
- package/lib/controls/wysiwyg/CKeditor.d.ts +5 -6
- package/lib/controls/wysiwyg/CKeditor.js +14 -13
- package/lib/filters/ConditionValidator.d.ts +1 -1
- package/lib/filters/FilterService.d.ts +18 -18
- package/lib/filters/FilterService.js +11 -11
- package/lib/history-actions/AbstractHistoryAdapter.d.ts +14 -14
- package/lib/history-actions/AbstractHistoryAdapter.js +5 -6
- package/lib/history-actions/DefaultHistoryAdapter.d.ts +7 -7
- package/lib/history-actions/DefaultHistoryAdapter.js +2 -2
- package/lib/list-query-builder/ListQueryBuilder.d.ts +1 -1
- package/lib/media-manager/AbstractMediaManager.d.ts +14 -3
- package/lib/media-manager/AbstractMediaManager.js +16 -5
- package/lib/media-manager/MediaManagerHandler.d.ts +3 -0
- package/lib/media-manager/MediaManagerHandler.js +30 -5
- package/lib/media-manager/Thumb.js +7 -13
- package/lib/media-manager/helpers/MediaManagerHelper.d.ts +6 -4
- package/lib/media-manager/helpers/MediaManagerHelper.js +18 -18
- package/lib/model/AbstractModel.d.ts +9 -5
- package/lib/model/AbstractModel.js +9 -13
- package/lib/model/InternalModelAccessFactory.js +9 -10
- package/lib/model/ModelHandler.d.ts +45 -7
- package/lib/model/ModelHandler.js +168 -11
- package/lib/model/adapter/sequelize.d.ts +2 -4
- package/lib/model/adapter/sequelize.js +16 -193
- package/lib/model/adapter/typeorm.d.ts +2 -4
- package/lib/model/adapter/typeorm.js +2 -35
- package/lib/notifications/AbstractNotificationService.d.ts +9 -9
- package/lib/notifications/AbstractNotificationService.js +8 -8
- package/lib/notifications/GeneralNotificationService.js +1 -1
- package/lib/notifications/SystemNotificationService.js +1 -1
- package/lib/ui-i18n/uiTranslationKeys.d.ts +1 -1
- package/lib/ui-i18n/uiTranslationKeys.js +1 -0
- package/lib/widgets/abstractCustom.d.ts +5 -2
- package/lib/widgets/abstractCustom.js +4 -0
- package/lib/widgets/widgetHandler.d.ts +5 -2
- package/lib/widgets/widgetHandler.js +60 -14
- package/models/{FilterAP.d.ts → Filter.d.ts} +4 -70
- package/models/Filter.js +1 -0
- package/models/FilterColumn.d.ts +10 -0
- package/models/FilterColumn.js +1 -0
- package/models/Group.d.ts +8 -0
- package/models/Group.js +1 -0
- package/models/HistoryActions.d.ts +14 -0
- package/models/HistoryActions.js +1 -0
- package/models/MediaManagerAP.d.ts +2 -54
- package/models/MediaManagerAP.js +1 -51
- package/models/MediaManagerAssociationsAP.d.ts +1 -35
- package/models/MediaManagerAssociationsAP.js +1 -33
- package/models/MediaManagerMetaAP.d.ts +2 -30
- package/models/MediaManagerMetaAP.js +1 -27
- package/models/Notification.d.ts +13 -0
- package/models/Notification.js +1 -0
- package/models/User.d.ts +23 -0
- package/models/User.js +1 -0
- package/models/UserNotification.d.ts +7 -0
- package/models/UserNotification.js +1 -0
- package/package.json +25 -30
- package/services/FeedService.d.ts +5 -5
- package/services/FeedService.js +1 -1
- package/system/Router.js +2 -10
- package/system/bindAccessRights.js +1 -1
- package/system/bindAuthorization.js +2 -2
- package/system/bindControls.js +7 -6
- package/system/bindDashboardWidgets.js +3 -2
- package/system/bindDev.js +1 -1
- package/system/bindHistory.d.ts +1 -1
- package/system/bindHistory.js +1 -1
- package/system/bindInertia.js +0 -6
- package/system/bindMediaManager.js +4 -17
- package/system/bindNotifications.d.ts +1 -1
- package/system/bindNotifications.js +1 -1
- package/system/bindReqFunctions.d.ts +1 -0
- package/system/bindReqFunctions.js +20 -3
- package/system/buildInternalModelAccess.js +2 -15
- package/system/defaults.js +5 -10
- package/system/systemModelContracts.d.ts +22 -0
- package/system/systemModelContracts.js +155 -0
- package/system/validateSystemModels.d.ts +2 -0
- package/system/validateSystemModels.js +101 -0
- package/translations/ar.json +2 -1
- package/translations/cn.json +2 -1
- package/translations/de.json +2 -1
- package/translations/en.json +1 -0
- package/translations/es.json +2 -1
- package/translations/fr.json +2 -1
- package/translations/hi.json +2 -1
- package/translations/jp.json +2 -1
- package/translations/ko.json +2 -1
- package/translations/pt.json +2 -1
- package/translations/ru.json +2 -1
- package/translations/ua.json +2 -1
- package/translations/uz.json +2 -1
- package/translations/vi.json +2 -1
- package/assets/RelationDialogStack-DhThUMep.js +0 -20
- package/assets/add-group-L8lMigab.js +0 -1
- package/assets/add-group-form-o7F6t6qW.js +0 -1
- package/assets/add-user-BE4-TNJw.js +0 -1
- package/assets/add-user-form-BP_8TjxN.js +0 -1
- package/assets/add-w398ZkhF.js +0 -1
- package/assets/app-C7J5ccrI.css +0 -1
- package/assets/app-layout-Bg455MIY.js +0 -1
- package/assets/app-shell-BT2kF0We.js +0 -1
- package/assets/app-sidebar-CZfPUXck.js +0 -1
- package/assets/app-sidebar-header-UVcKhNm6.js +0 -1
- package/assets/breadcrumbs-CrqIBT84.js +0 -1
- package/assets/catalog-btkCCwiv.js +0 -6
- package/assets/dashboard-Bvasc1cr.js +0 -3
- package/assets/date-picker-kxQWOxHk.js +0 -1
- package/assets/del-modal-C39LR5aQ.js +0 -1
- package/assets/dynamic-controls-DyPP5sTC.js +0 -1
- package/assets/geo-json-CGNQFl6J.js +0 -1
- package/assets/handsontable-Dv59QFH5.js +0 -336
- package/assets/history-DwKR8MPu.js +0 -1
- package/assets/home-BrtAkCRj.js +0 -2
- package/assets/icon-DpUVVHmK.js +0 -1
- package/assets/init-user-8PiKR1zX.js +0 -1
- package/assets/input-error-Cbt-Y3Fj.js +0 -1
- package/assets/jsoneditor-CiPXPMho.js +0 -3869
- package/assets/list-DGsL_qii.js +0 -4
- package/assets/login-Br_FcBU_.js +0 -1
- package/assets/material-icon-DOkM71fv.js +0 -1
- package/assets/module-EgfgIEhF.js +0 -1
- package/assets/monaco-ompgKQ3_.js +0 -11
- package/assets/nav-footer-DzLwfBAE.js +0 -1
- package/assets/nav-main-CSEbjYgs.js +0 -1
- package/assets/nav-user-DJ0hfhfH.js +0 -1
- package/assets/notification-B19ZLro7.js +0 -20
- package/assets/pagination-render-8GvJxq55.js +0 -1
- package/assets/register-CZzIeT2G.js +0 -1
- package/assets/ru-1NdRIQNV.js +0 -1
- package/assets/theme-switcher-CBbEj1u-.js +0 -1
- package/assets/toast-editor-DwVqd1rz.js +0 -668
- package/assets/user-filters-list-7Im79QyS.js +0 -1
- package/assets/user-info-YiU17Mmy.js +0 -1
- package/assets/user-menu-content-gdpNF5-8.js +0 -1
- package/assets/vanilla-picker-B3LgXoKV.js +0 -2
- package/assets/vendor-l0sNRNKZ.js +0 -1
- package/assets/welcome-nT0eFWHb.js +0 -1
- package/controllers/navigation/edit.d.ts +0 -1
- package/controllers/navigation/edit.js +0 -8
- package/lib/ai-assistant/DummyAiModelService.d.ts +0 -8
- package/lib/ai-assistant/DummyAiModelService.js +0 -14
- package/lib/ai-assistant/OpenAiModelService.d.ts +0 -24
- package/lib/ai-assistant/OpenAiModelService.js +0 -184
- package/lib/catalog/Navigation.d.ts +0 -49
- package/lib/catalog/Navigation.js +0 -492
- package/lib/controls/AbstractControls.d.ts +0 -30
- package/lib/controls/AbstractControls.js +0 -14
- package/lib/media-manager/DefaultMediaManager.d.ts +0 -20
- package/lib/media-manager/DefaultMediaManager.js +0 -127
- package/lib/media-manager/Items.d.ts +0 -50
- package/lib/media-manager/Items.js +0 -296
- package/models/FilterAP.js +0 -68
- package/models/FilterColumnAP.d.ts +0 -32
- package/models/FilterColumnAP.js +0 -21
- package/models/GroupAP.d.ts +0 -31
- package/models/GroupAP.js +0 -22
- package/models/HistoryActionsAP.d.ts +0 -56
- package/models/HistoryActionsAP.js +0 -42
- package/models/NavigationAP.d.ts +0 -22
- package/models/NavigationAP.js +0 -16
- package/models/NotificationAP.d.ts +0 -44
- package/models/NotificationAP.js +0 -30
- package/models/UserAP.d.ts +0 -81
- package/models/UserAP.js +0 -57
- package/models/UserNotificationAP.d.ts +0 -26
- package/models/UserNotificationAP.js +0 -18
- package/system/bindAiAssistant.d.ts +0 -2
- package/system/bindAiAssistant.js +0 -30
- package/system/bindModels.d.ts +0 -2
- package/system/bindModels.js +0 -48
- package/system/bindNavigation.d.ts +0 -2
- package/system/bindNavigation.js +0 -38
- /package/assets/{RelationDialogStack-B00Ot5qS.css → controls/ckeditor.css} +0 -0
package/lib/DataAccessor.js
CHANGED
|
@@ -47,14 +47,29 @@ export class DataAccessor {
|
|
|
47
47
|
this.actionVerb = getTokenAction(this.action);
|
|
48
48
|
}
|
|
49
49
|
getModelConfig(modelName) {
|
|
50
|
+
const resolvedModelName = this.adminizer.modelHandler.resolveModelName(modelName);
|
|
50
51
|
const entry = Object.entries(this.adminizer.config.models)
|
|
51
|
-
.find(([key]) =>
|
|
52
|
+
.find(([key, config]) => {
|
|
53
|
+
if (key.toLowerCase() === resolvedModelName.toLowerCase()) {
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
if (!isObject(config)) {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
return config.model?.toLowerCase() === resolvedModelName.toLowerCase();
|
|
60
|
+
});
|
|
52
61
|
const config = entry ? entry[1] : undefined;
|
|
53
62
|
return isObject(config) ? config : undefined;
|
|
54
63
|
}
|
|
55
64
|
accessRelationModel(modelName) {
|
|
56
65
|
return this.adminizer.modelHandler.internal("data-accessor").get(modelName);
|
|
57
66
|
}
|
|
67
|
+
resolveRelationTarget(modelName) {
|
|
68
|
+
if (!modelName) {
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
return this.adminizer.modelHandler.resolveModelName(modelName).toLowerCase();
|
|
72
|
+
}
|
|
58
73
|
/**
|
|
59
74
|
* Retrieves the fields for the given ModelResource based on action type,
|
|
60
75
|
* taking into account access rights and configuration settings.
|
|
@@ -116,21 +131,22 @@ export class DataAccessor {
|
|
|
116
131
|
let populatedModelFieldsConfig = {};
|
|
117
132
|
if (modelField.type === "association" || modelField.type === "association-many") {
|
|
118
133
|
const modelName = modelField.model || modelField.collection;
|
|
119
|
-
const
|
|
120
|
-
if (
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
134
|
+
const resolvedModelName = modelName ? this.adminizer.modelHandler.resolveModelName(modelName) : undefined;
|
|
135
|
+
if (modelName && resolvedModelName) {
|
|
136
|
+
const tokenId = `read-${resolvedModelName}-${MODEL_TOKEN_SUFFIX}`;
|
|
137
|
+
if (!this.adminizer.accessRightsHelper.hasPermission(tokenId, this.user)) {
|
|
138
|
+
Adminizer.log.silly(`No access rights to model: ${this.modelResource.model.modelname}`);
|
|
139
|
+
return undefined;
|
|
140
|
+
}
|
|
125
141
|
const model = this.adminizer.modelHandler.model.get(modelName);
|
|
126
142
|
if (model) {
|
|
127
|
-
populatedModelFieldsConfig = this.getAssociatedFieldsConfig(
|
|
128
|
-
const modelCfg = this.getModelConfig(
|
|
143
|
+
populatedModelFieldsConfig = this.getAssociatedFieldsConfig(resolvedModelName);
|
|
144
|
+
const modelCfg = this.getModelConfig(resolvedModelName);
|
|
129
145
|
if (modelCfg) {
|
|
130
146
|
associatedModelConfig = modelCfg;
|
|
131
147
|
}
|
|
132
148
|
else {
|
|
133
|
-
Adminizer.log.error(`DataAccessor > getFieldsConfig > Model config not found: ${
|
|
149
|
+
Adminizer.log.error(`DataAccessor > getFieldsConfig > Model config not found: ${resolvedModelName}`);
|
|
134
150
|
}
|
|
135
151
|
}
|
|
136
152
|
else {
|
|
@@ -167,15 +183,16 @@ export class DataAccessor {
|
|
|
167
183
|
return orderedFields;
|
|
168
184
|
}
|
|
169
185
|
getAssociatedFieldsConfig(modelName) {
|
|
170
|
-
const
|
|
171
|
-
const
|
|
186
|
+
const resolvedModelName = this.adminizer.modelHandler.resolveModelName(modelName);
|
|
187
|
+
const model = this.adminizer.modelHandler.model.get(resolvedModelName);
|
|
188
|
+
const modelConfig = this.getModelConfig(resolvedModelName);
|
|
172
189
|
if (!model || !modelConfig) {
|
|
173
190
|
return undefined;
|
|
174
191
|
}
|
|
175
192
|
// Check if user has access to the associated model
|
|
176
|
-
const tokenId = `read-${
|
|
193
|
+
const tokenId = `read-${resolvedModelName}-${MODEL_TOKEN_SUFFIX}`;
|
|
177
194
|
if (!this.adminizer.accessRightsHelper.hasPermission(tokenId, this.user)) {
|
|
178
|
-
Adminizer.log.debug(`getAssociatedFieldsConfig > No access rights to ${this.actionVerb} model: ${
|
|
195
|
+
Adminizer.log.debug(`getAssociatedFieldsConfig > No access rights to ${this.actionVerb} model: ${resolvedModelName}`);
|
|
179
196
|
return undefined;
|
|
180
197
|
}
|
|
181
198
|
const associatedFields = {};
|
|
@@ -248,10 +265,28 @@ export class DataAccessor {
|
|
|
248
265
|
return !userGroups || !userGroups.includes(this.adminizer.config.registration?.defaultUserGroup);
|
|
249
266
|
}
|
|
250
267
|
}
|
|
268
|
+
getSelectedFields(select) {
|
|
269
|
+
if (!select) {
|
|
270
|
+
return undefined;
|
|
271
|
+
}
|
|
272
|
+
if (Array.isArray(select)) {
|
|
273
|
+
return new Set(select);
|
|
274
|
+
}
|
|
275
|
+
return new Set(Object.entries(select)
|
|
276
|
+
.filter(([, enabled]) => enabled)
|
|
277
|
+
.map(([field]) => field));
|
|
278
|
+
}
|
|
279
|
+
getPopulateCriteria(criteria, fieldKey) {
|
|
280
|
+
const populateConfig = criteria?.populate?.[fieldKey];
|
|
281
|
+
return populateConfig && populateConfig !== true ? populateConfig : undefined;
|
|
282
|
+
}
|
|
283
|
+
canUseSelectedFieldFallback() {
|
|
284
|
+
return this.user.isAdministrator || this.adminizer.config.auth?.enable === false;
|
|
285
|
+
}
|
|
251
286
|
/**
|
|
252
287
|
* Returns filtered record applying config from this.fields on this record
|
|
253
288
|
* @data - record from a specific model */
|
|
254
|
-
process(record) {
|
|
289
|
+
process(record, criteria) {
|
|
255
290
|
// Initialize fields configuration, if it was not already set
|
|
256
291
|
if (!this.fields) {
|
|
257
292
|
this.fields = this.getFieldsConfig();
|
|
@@ -269,6 +304,8 @@ export class DataAccessor {
|
|
|
269
304
|
// Check access to the field
|
|
270
305
|
if (this.checkFieldAccess(fieldKey, fieldConfig.config)) {
|
|
271
306
|
const fieldType = fieldConfig.config.type;
|
|
307
|
+
const populateCriteria = this.getPopulateCriteria(criteria, fieldKey);
|
|
308
|
+
const selectedAssociatedFields = this.getSelectedFields(populateCriteria?.select);
|
|
272
309
|
// Handle fields that are not associations
|
|
273
310
|
if (fieldType !== 'association' && fieldType !== 'association-many') {
|
|
274
311
|
filteredRecord[fieldKey] = fieldValue;
|
|
@@ -278,7 +315,7 @@ export class DataAccessor {
|
|
|
278
315
|
if (Array.isArray(fieldValue)) {
|
|
279
316
|
// If the field value is an array of objects
|
|
280
317
|
filteredRecord[fieldKey] = (fieldValue.every(item => typeof item === 'object')
|
|
281
|
-
? fieldValue.map(associatedRecord => this.filterAssociatedRecord(associatedRecord, fieldConfig.populated))
|
|
318
|
+
? fieldValue.map(associatedRecord => this.filterAssociatedRecord(associatedRecord, fieldConfig.populated, selectedAssociatedFields))
|
|
282
319
|
: fieldValue); // If the array contains IDs, pass them as is (it can contain ids, because this function also can be called before saving something)
|
|
283
320
|
}
|
|
284
321
|
else {
|
|
@@ -290,7 +327,7 @@ export class DataAccessor {
|
|
|
290
327
|
else if (fieldType === 'association') {
|
|
291
328
|
if (fieldValue && typeof fieldValue === 'object') {
|
|
292
329
|
// If the field value is an object
|
|
293
|
-
filteredRecord[fieldKey] = this.filterAssociatedRecord(fieldValue, fieldConfig.populated);
|
|
330
|
+
filteredRecord[fieldKey] = this.filterAssociatedRecord(fieldValue, fieldConfig.populated, selectedAssociatedFields);
|
|
294
331
|
}
|
|
295
332
|
else {
|
|
296
333
|
// If the field value is an ID or null, pass it as is (it can contain id, because this function also can be called before saving something)
|
|
@@ -310,23 +347,30 @@ export class DataAccessor {
|
|
|
310
347
|
return filteredRecord;
|
|
311
348
|
}
|
|
312
349
|
/** Filters associated records (simplified process() function) */
|
|
313
|
-
filterAssociatedRecord(associatedRecord, associatedFieldsConfig) {
|
|
314
|
-
if (!associatedFieldsConfig) {
|
|
350
|
+
filterAssociatedRecord(associatedRecord, associatedFieldsConfig, selectedFields) {
|
|
351
|
+
if (!associatedFieldsConfig && !selectedFields) {
|
|
315
352
|
return {};
|
|
316
353
|
}
|
|
317
354
|
const filteredAssociatedRecord = {};
|
|
318
355
|
for (const assocFieldKey in associatedRecord) {
|
|
319
|
-
|
|
356
|
+
if (selectedFields && !selectedFields.has(assocFieldKey)) {
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
359
|
+
const assocFieldConfig = associatedFieldsConfig?.[assocFieldKey];
|
|
320
360
|
const assocFieldValue = associatedRecord[assocFieldKey];
|
|
321
361
|
if (assocFieldConfig && this.checkFieldAccess(assocFieldKey, assocFieldConfig.config)) {
|
|
322
362
|
filteredAssociatedRecord[assocFieldKey] = assocFieldValue;
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
365
|
+
if (selectedFields && this.canUseSelectedFieldFallback()) {
|
|
366
|
+
filteredAssociatedRecord[assocFieldKey] = assocFieldValue;
|
|
323
367
|
}
|
|
324
368
|
}
|
|
325
369
|
return filteredAssociatedRecord;
|
|
326
370
|
}
|
|
327
371
|
/** Process for an array of records */
|
|
328
|
-
processMany(records) {
|
|
329
|
-
return records.map(record => this.process(record));
|
|
372
|
+
processMany(records, criteria) {
|
|
373
|
+
return records.map(record => this.process(record, criteria));
|
|
330
374
|
}
|
|
331
375
|
async sanitizeUserRelationAccess(criteria = {}) {
|
|
332
376
|
let sanitizedCriteria = {};
|
|
@@ -341,33 +385,35 @@ export class DataAccessor {
|
|
|
341
385
|
const userAccessRelation = modelConfig.userAccessRelation;
|
|
342
386
|
if (typeof userAccessRelation === 'string') {
|
|
343
387
|
let accessField = userAccessRelation;
|
|
344
|
-
// Check if the relation points to `
|
|
388
|
+
// Check if the relation points to `User` or `Group` in the model's attributes
|
|
345
389
|
const modelAttributes = this.modelResource.model.attributes;
|
|
346
390
|
const relation = modelAttributes[accessField];
|
|
347
|
-
|
|
391
|
+
const relationModel = this.resolveRelationTarget(relation?.model);
|
|
392
|
+
const relationCollection = this.resolveRelationTarget(relation?.collection);
|
|
393
|
+
if (!relation || !['user', 'group'].includes(relationModel ?? relationCollection ?? "")) {
|
|
348
394
|
throw new Error(`Invalid userAccessRelation configuration for model ${modelName}`);
|
|
349
395
|
}
|
|
350
396
|
// Determine if the current user matches the access criteria
|
|
351
|
-
if (
|
|
352
|
-
if (
|
|
353
|
-
// Filter by the user's ID if related to
|
|
397
|
+
if (relationModel) {
|
|
398
|
+
if (relationModel === 'user') {
|
|
399
|
+
// Filter by the user's ID if related to User as a model
|
|
354
400
|
sanitizedCriteria = { ...sanitizedCriteria, [accessField]: this.user.id };
|
|
355
401
|
}
|
|
356
|
-
else if (
|
|
357
|
-
// Filter by user's group membership if related to
|
|
402
|
+
else if (relationModel === 'group') {
|
|
403
|
+
// Filter by user's group membership if related to Group as a model
|
|
358
404
|
const userGroups = this.user.groups?.map((group) => group.id);
|
|
359
405
|
sanitizedCriteria = { ...sanitizedCriteria, [accessField]: { in: userGroups } };
|
|
360
406
|
}
|
|
361
407
|
}
|
|
362
408
|
/** Warning: collection relation access is not supported and needs adapter-level processing */
|
|
363
|
-
if (
|
|
409
|
+
if (relationCollection) {
|
|
364
410
|
Adminizer.log.warn(`Collection relation is not supported and was not tested. You may have an error here: ${JSON.stringify(relation, null, 2)}`);
|
|
365
|
-
if (
|
|
366
|
-
// Ensure user's ID is part of the associated collection to
|
|
411
|
+
if (relationCollection === 'user') {
|
|
412
|
+
// Ensure user's ID is part of the associated collection to User
|
|
367
413
|
sanitizedCriteria = { ...sanitizedCriteria, [accessField]: { contains: this.user.id } };
|
|
368
414
|
}
|
|
369
|
-
else if (
|
|
370
|
-
// Ensure user's groups intersect with the collection to
|
|
415
|
+
else if (relationCollection === 'group') {
|
|
416
|
+
// Ensure user's groups intersect with the collection to Group
|
|
371
417
|
const userGroups = this.user.groups?.map((group) => group.id);
|
|
372
418
|
sanitizedCriteria = { ...sanitizedCriteria, [accessField]: { intersects: userGroups } };
|
|
373
419
|
}
|
|
@@ -390,9 +436,9 @@ export class DataAccessor {
|
|
|
390
436
|
// Validate the `via` field in the intermediate model
|
|
391
437
|
const intermediateAttributes = intermediateModel.attributes;
|
|
392
438
|
const viaRelation = intermediateAttributes[via];
|
|
393
|
-
if (!viaRelation || viaRelation.model
|
|
439
|
+
if (!viaRelation || this.resolveRelationTarget(viaRelation.model) !== 'user') {
|
|
394
440
|
throw new Error(`Unsupported or invalid via field "${via}" in intermediate model "${intermediateRelation.model}". ` +
|
|
395
|
-
`Currently, only relations to "
|
|
441
|
+
`Currently, only relations to "User" are supported`);
|
|
396
442
|
}
|
|
397
443
|
// Fetch all intermediate records associated with the user
|
|
398
444
|
const intermediateRecords = await this.accessRelationModel(intermediateRelation.model).find({ where: { [via]: this.user.id } });
|
|
@@ -418,16 +464,17 @@ export class DataAccessor {
|
|
|
418
464
|
if (updatedRecord[accessField] && !this.user.isAdministrator) {
|
|
419
465
|
delete updatedRecord[accessField];
|
|
420
466
|
}
|
|
421
|
-
// Check if the relation points to `
|
|
467
|
+
// Check if the relation points to `User` or `Group` in the model's attributes
|
|
422
468
|
const modelAttributes = this.modelResource.model.attributes;
|
|
423
469
|
const relation = modelAttributes[accessField];
|
|
424
|
-
|
|
425
|
-
|
|
470
|
+
const relationModel = this.resolveRelationTarget(relation?.model);
|
|
471
|
+
if (relation && ['user', 'group'].includes(relationModel ?? "")) {
|
|
472
|
+
if (relationModel === 'user') {
|
|
426
473
|
if (!this.user.isAdministrator) {
|
|
427
474
|
updatedRecord[accessField] = this.user.id;
|
|
428
475
|
}
|
|
429
476
|
}
|
|
430
|
-
else if (
|
|
477
|
+
else if (relationModel === 'group') {
|
|
431
478
|
const userGroups = this.user.groups || [];
|
|
432
479
|
if (userGroups.length === 1) {
|
|
433
480
|
updatedRecord[accessField] = userGroups[0].id;
|
|
@@ -458,9 +505,9 @@ export class DataAccessor {
|
|
|
458
505
|
// Validate the `via` field in the intermediate model
|
|
459
506
|
const intermediateAttributes = intermediateModel.attributes;
|
|
460
507
|
const viaRelation = intermediateAttributes[via];
|
|
461
|
-
if (!viaRelation ||
|
|
508
|
+
if (!viaRelation || this.resolveRelationTarget(viaRelation.model) !== 'user') {
|
|
462
509
|
throw new Error(`Unsupported or invalid via field "${via}" in intermediate model "${intermediateRelation.model}". ` +
|
|
463
|
-
`Currently, only relations to "
|
|
510
|
+
`Currently, only relations to "User" are supported`);
|
|
464
511
|
}
|
|
465
512
|
const intermediatePk = (intermediateModel.primaryKey ?? 'id');
|
|
466
513
|
const chosenId = typeof updatedRecord[field] === 'object'
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { AccessRightsToken } from "../../interfaces/types";
|
|
2
|
+
import type { Adminizer } from "../Adminizer";
|
|
3
|
+
export interface AccessRightsRecord {
|
|
4
|
+
id: string;
|
|
5
|
+
appName: string;
|
|
6
|
+
token: AccessRightsToken;
|
|
7
|
+
}
|
|
8
|
+
export declare class AccessRightsHandler {
|
|
9
|
+
private adminizer;
|
|
10
|
+
private records;
|
|
11
|
+
constructor(adminizer: Adminizer);
|
|
12
|
+
register(appName: string, token: AccessRightsToken): string;
|
|
13
|
+
unregister(id: string): void;
|
|
14
|
+
getByApp(appName: string): AccessRightsRecord[];
|
|
15
|
+
private getRecordId;
|
|
16
|
+
private getTokenOwner;
|
|
17
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
export class AccessRightsHandler {
|
|
2
|
+
adminizer;
|
|
3
|
+
records = new Map();
|
|
4
|
+
constructor(adminizer) {
|
|
5
|
+
this.adminizer = adminizer;
|
|
6
|
+
}
|
|
7
|
+
register(appName, token) {
|
|
8
|
+
const normalizedToken = {
|
|
9
|
+
...token,
|
|
10
|
+
id: token.id.toLowerCase(),
|
|
11
|
+
};
|
|
12
|
+
const id = this.getRecordId(appName, normalizedToken.id);
|
|
13
|
+
if (this.records.has(id)) {
|
|
14
|
+
throw new Error(`Access rights token "${normalizedToken.id}" is already registered for app "${appName}"`);
|
|
15
|
+
}
|
|
16
|
+
const existingOwner = this.getTokenOwner(normalizedToken.id);
|
|
17
|
+
if (existingOwner && existingOwner !== appName) {
|
|
18
|
+
throw new Error(`Access rights token "${normalizedToken.id}" is already registered by app "${existingOwner}"`);
|
|
19
|
+
}
|
|
20
|
+
if (!existingOwner && this.adminizer.accessRightsHelper.hasToken(normalizedToken.id)) {
|
|
21
|
+
throw new Error(`Access rights token "${normalizedToken.id}" is already registered`);
|
|
22
|
+
}
|
|
23
|
+
this.adminizer.accessRightsHelper.registerToken(normalizedToken);
|
|
24
|
+
this.records.set(id, {
|
|
25
|
+
id,
|
|
26
|
+
appName,
|
|
27
|
+
token: normalizedToken,
|
|
28
|
+
});
|
|
29
|
+
this.adminizer.emitter.emit("app:access-right:registered", {
|
|
30
|
+
appName,
|
|
31
|
+
resourceId: id,
|
|
32
|
+
tokenId: normalizedToken.id,
|
|
33
|
+
});
|
|
34
|
+
return id;
|
|
35
|
+
}
|
|
36
|
+
unregister(id) {
|
|
37
|
+
const record = this.records.get(id);
|
|
38
|
+
if (!record) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
this.adminizer.accessRightsHelper.unregisterToken(record.token.id);
|
|
42
|
+
this.records.delete(id);
|
|
43
|
+
this.adminizer.emitter.emit("app:access-right:unregistered", {
|
|
44
|
+
appName: record.appName,
|
|
45
|
+
resourceId: id,
|
|
46
|
+
tokenId: record.token.id,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
getByApp(appName) {
|
|
50
|
+
return Array.from(this.records.values()).filter((record) => record.appName === appName);
|
|
51
|
+
}
|
|
52
|
+
getRecordId(appName, tokenId) {
|
|
53
|
+
return `${appName}:${tokenId}`;
|
|
54
|
+
}
|
|
55
|
+
getTokenOwner(tokenId) {
|
|
56
|
+
for (const record of this.records.values()) {
|
|
57
|
+
if (record.token.id === tokenId) {
|
|
58
|
+
return record.appName;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -1,17 +1,14 @@
|
|
|
1
|
-
import { Adminizer } from '../Adminizer';
|
|
2
1
|
import { AiAssistantMessage, AiAssistantModelInfo } from '../../interfaces/types';
|
|
3
|
-
import {
|
|
2
|
+
import { User } from '../../models/User';
|
|
4
3
|
/**
|
|
5
|
-
* Base class for AI assistant models.
|
|
6
|
-
*
|
|
4
|
+
* Base class for AI assistant models. App modules register access rights
|
|
5
|
+
* and expose model metadata through this service.
|
|
7
6
|
*/
|
|
8
7
|
export declare abstract class AbstractAiModelService {
|
|
9
|
-
protected readonly adminizer: Adminizer;
|
|
10
8
|
readonly id: string;
|
|
11
9
|
readonly name: string;
|
|
12
10
|
readonly description?: string;
|
|
13
|
-
protected constructor(
|
|
14
|
-
protected registerAccessRight(): void;
|
|
11
|
+
protected constructor(metadata: AiAssistantModelInfo);
|
|
15
12
|
getMetadata(): AiAssistantModelInfo;
|
|
16
|
-
abstract generateReply(prompt: string, history: AiAssistantMessage[], user:
|
|
13
|
+
abstract generateReply(prompt: string, history: AiAssistantMessage[], user: User): Promise<string>;
|
|
17
14
|
}
|
|
@@ -1,26 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Base class for AI assistant models.
|
|
3
|
-
*
|
|
2
|
+
* Base class for AI assistant models. App modules register access rights
|
|
3
|
+
* and expose model metadata through this service.
|
|
4
4
|
*/
|
|
5
5
|
export class AbstractAiModelService {
|
|
6
|
-
adminizer;
|
|
7
6
|
id;
|
|
8
7
|
name;
|
|
9
8
|
description;
|
|
10
|
-
constructor(
|
|
11
|
-
this.adminizer = adminizer;
|
|
9
|
+
constructor(metadata) {
|
|
12
10
|
this.id = metadata.id;
|
|
13
11
|
this.name = metadata.name;
|
|
14
12
|
this.description = metadata.description;
|
|
15
|
-
this.registerAccessRight();
|
|
16
|
-
}
|
|
17
|
-
registerAccessRight() {
|
|
18
|
-
this.adminizer.accessRightsHelper.registerToken({
|
|
19
|
-
id: `ai-assistant-${this.id}`,
|
|
20
|
-
name: this.name,
|
|
21
|
-
description: `Access to AI assistant model ${this.name}`,
|
|
22
|
-
department: 'AI Assistant',
|
|
23
|
-
});
|
|
24
13
|
}
|
|
25
14
|
getMetadata() {
|
|
26
15
|
return {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Adminizer } from '../Adminizer';
|
|
2
2
|
import { AbstractAiModelService } from './AbstractAiModelService';
|
|
3
3
|
import { AiAssistantMessage, AiAssistantModelInfo } from '../../interfaces/types';
|
|
4
|
-
import {
|
|
4
|
+
import { User } from '../../models/User';
|
|
5
5
|
export declare class AiAssistantHandler {
|
|
6
6
|
private readonly adminizer;
|
|
7
7
|
private readonly models;
|
|
@@ -12,7 +12,7 @@ export declare class AiAssistantHandler {
|
|
|
12
12
|
getModels(): AiAssistantModelInfo[];
|
|
13
13
|
getHistory(userId: number, modelId: string): AiAssistantMessage[];
|
|
14
14
|
resetHistory(userId: number, modelId: string): void;
|
|
15
|
-
sendMessage(user:
|
|
15
|
+
sendMessage(user: User, modelId: string, prompt: string): Promise<{
|
|
16
16
|
history: AiAssistantMessage[];
|
|
17
17
|
}>;
|
|
18
18
|
private getSessionKey;
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import type { ActionType, AdminpanelConfig, AdminpanelIcon, ModelConfig } from "../../interfaces/adminpanelConfig";
|
|
2
|
+
import type { AccessRightsToken, INotification, ModelResource } from "../../interfaces/types";
|
|
3
|
+
import type { AbstractCatalog } from "../catalog/AbstractCatalog";
|
|
4
|
+
import type { CatalogTemplateComponentResource } from "../catalog/CatalogTemplateComponentHandler";
|
|
5
|
+
import type { AppModelAccess } from "../model/ModelHandler";
|
|
6
|
+
import type { AbstractMediaManager } from "../media-manager/AbstractMediaManager";
|
|
7
|
+
import type { Config, ControlType } from "../controls/Control";
|
|
8
|
+
import type { User } from "../../models/User";
|
|
9
|
+
import type { DataAccessor } from "../DataAccessor";
|
|
10
|
+
import type { AbstractAiModelService } from "../ai-assistant/AbstractAiModelService";
|
|
11
|
+
export type AppDisposer = () => void | Promise<void>;
|
|
12
|
+
export type AppEventName = string | symbol;
|
|
13
|
+
export type AppEventHandler<TPayload = any> = (payload: TPayload, runtime: AppRuntime) => void | Promise<void>;
|
|
14
|
+
export type AppControllerMethod = "get" | "post" | "put" | "patch" | "delete" | "all";
|
|
15
|
+
export type AppControllerPolicyMode = "ui" | "api";
|
|
16
|
+
export type AppControllerPolicy = {
|
|
17
|
+
type: "auth";
|
|
18
|
+
mode?: AppControllerPolicyMode;
|
|
19
|
+
} | {
|
|
20
|
+
type: "auth-enabled";
|
|
21
|
+
} | {
|
|
22
|
+
type: "admin";
|
|
23
|
+
mode?: AppControllerPolicyMode;
|
|
24
|
+
} | {
|
|
25
|
+
type: "permission";
|
|
26
|
+
token: string;
|
|
27
|
+
mode?: AppControllerPolicyMode;
|
|
28
|
+
} | {
|
|
29
|
+
type: "any-permission";
|
|
30
|
+
tokens: string[];
|
|
31
|
+
mode?: AppControllerPolicyMode;
|
|
32
|
+
};
|
|
33
|
+
export type AppConfigPatch = {
|
|
34
|
+
[K in keyof AdminpanelConfig]?: AdminpanelConfig[K] extends (...args: any[]) => any ? AdminpanelConfig[K] : AdminpanelConfig[K] extends Array<infer U> ? U[] : AdminpanelConfig[K] extends object ? Partial<AdminpanelConfig[K]> : AdminpanelConfig[K];
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* A controller owned by an Adminizer app.
|
|
38
|
+
*
|
|
39
|
+
* Before the middleware runs, ControllerHandler assigns an AppRuntime scoped
|
|
40
|
+
* to the owning app to `req.runtime`. App controllers must use that runtime
|
|
41
|
+
* instead of reading `req.adminizer`.
|
|
42
|
+
*/
|
|
43
|
+
export interface AppController {
|
|
44
|
+
id?: string;
|
|
45
|
+
route: string;
|
|
46
|
+
method: AppControllerMethod;
|
|
47
|
+
middleware: MiddlewareType;
|
|
48
|
+
policies?: AppControllerPolicy[];
|
|
49
|
+
}
|
|
50
|
+
export interface AppAsset {
|
|
51
|
+
id: string;
|
|
52
|
+
filePath: string;
|
|
53
|
+
route?: string;
|
|
54
|
+
devUrl?: string;
|
|
55
|
+
}
|
|
56
|
+
export interface AppControlResource {
|
|
57
|
+
id?: string;
|
|
58
|
+
type: ControlType;
|
|
59
|
+
name: string;
|
|
60
|
+
config?: Config;
|
|
61
|
+
component: AppAsset;
|
|
62
|
+
stylesheet?: AppAsset;
|
|
63
|
+
}
|
|
64
|
+
export interface AppWidgetBaseResource {
|
|
65
|
+
id: string;
|
|
66
|
+
name: string;
|
|
67
|
+
description: string;
|
|
68
|
+
icon?: AdminpanelIcon | string;
|
|
69
|
+
department: string;
|
|
70
|
+
backgroundCSS?: string | null;
|
|
71
|
+
size?: {
|
|
72
|
+
h: number;
|
|
73
|
+
w: number;
|
|
74
|
+
} | null;
|
|
75
|
+
accessRightsToken?: string;
|
|
76
|
+
group?: string;
|
|
77
|
+
}
|
|
78
|
+
export interface AppWidgetContext {
|
|
79
|
+
runtime: AppRuntime;
|
|
80
|
+
}
|
|
81
|
+
export interface AppWidgetInfoContext extends AppWidgetContext {
|
|
82
|
+
user?: ReqType["user"];
|
|
83
|
+
}
|
|
84
|
+
export interface AppInfoWidgetResource extends AppWidgetBaseResource {
|
|
85
|
+
type: "info";
|
|
86
|
+
link?: string;
|
|
87
|
+
linkType?: "self" | "blank";
|
|
88
|
+
getInfo(context: AppWidgetInfoContext): Promise<string> | string;
|
|
89
|
+
}
|
|
90
|
+
export interface AppActionWidgetResource extends AppWidgetBaseResource {
|
|
91
|
+
type: "action";
|
|
92
|
+
action(context: AppWidgetContext): Promise<unknown> | unknown;
|
|
93
|
+
}
|
|
94
|
+
export interface AppSwitchWidgetResource extends AppWidgetBaseResource {
|
|
95
|
+
type: "switcher";
|
|
96
|
+
getState(context: AppWidgetContext): Promise<boolean> | boolean;
|
|
97
|
+
switchIt(context: AppWidgetContext): Promise<boolean> | boolean;
|
|
98
|
+
}
|
|
99
|
+
export interface AppLinkWidgetLink {
|
|
100
|
+
name: string;
|
|
101
|
+
description: string;
|
|
102
|
+
icon?: AdminpanelIcon | string;
|
|
103
|
+
link: string;
|
|
104
|
+
linkType: "self" | "blank";
|
|
105
|
+
backgroundCSS?: string | null;
|
|
106
|
+
}
|
|
107
|
+
export interface AppLinkWidgetResource extends AppWidgetBaseResource {
|
|
108
|
+
type: "link";
|
|
109
|
+
links: AppLinkWidgetLink[];
|
|
110
|
+
}
|
|
111
|
+
export interface AppCustomWidgetResource extends AppWidgetBaseResource {
|
|
112
|
+
type: "custom";
|
|
113
|
+
component: AppAsset;
|
|
114
|
+
}
|
|
115
|
+
export type AppWidgetResource = AppInfoWidgetResource | AppActionWidgetResource | AppSwitchWidgetResource | AppLinkWidgetResource | AppCustomWidgetResource;
|
|
116
|
+
/**
|
|
117
|
+
* Public capabilities available to an Adminizer app at runtime.
|
|
118
|
+
*
|
|
119
|
+
* The runtime is scoped to the app that registered the resource. In
|
|
120
|
+
* particular, `models` exposes only models declared through
|
|
121
|
+
* `AppSetupContext.modelAccess()`.
|
|
122
|
+
*
|
|
123
|
+
* App code must not retain or depend on the Adminizer instance. Controllers
|
|
124
|
+
* receive this object through `req.runtime`; catalog factories and event
|
|
125
|
+
* listeners receive it as an argument.
|
|
126
|
+
*/
|
|
127
|
+
export interface AppRuntime {
|
|
128
|
+
models: AppModelAccess;
|
|
129
|
+
config: AppRuntimeConfig;
|
|
130
|
+
notifications: AppRuntimeNotifications;
|
|
131
|
+
apps: AppRuntimeApps;
|
|
132
|
+
}
|
|
133
|
+
export interface AppRuntimeConfig {
|
|
134
|
+
readonly routePrefix: string;
|
|
135
|
+
getModelConfig(modelName: string): ModelConfig | undefined;
|
|
136
|
+
}
|
|
137
|
+
export type AppRuntimeAppState = "registered" | "enabled" | "disabled" | "unregistered" | "failed";
|
|
138
|
+
export interface AppRuntimeApp {
|
|
139
|
+
readonly name: string;
|
|
140
|
+
readonly version: string;
|
|
141
|
+
readonly state: AppRuntimeAppState;
|
|
142
|
+
}
|
|
143
|
+
export interface AppRuntimeApps {
|
|
144
|
+
list(): AppRuntimeApp[];
|
|
145
|
+
get(name: string): AppRuntimeApp | undefined;
|
|
146
|
+
enable(name: string): Promise<void>;
|
|
147
|
+
disable(name: string): Promise<void>;
|
|
148
|
+
unregister(name: string): Promise<void>;
|
|
149
|
+
}
|
|
150
|
+
export interface AppRuntimeNotifications {
|
|
151
|
+
send(notification: Omit<INotification, "id" | "createdAt" | "icon">): Promise<boolean>;
|
|
152
|
+
}
|
|
153
|
+
export interface AppModelAccessResource {
|
|
154
|
+
id?: string;
|
|
155
|
+
models: string[];
|
|
156
|
+
}
|
|
157
|
+
export interface AppModelResource {
|
|
158
|
+
name: string;
|
|
159
|
+
adapter?: string;
|
|
160
|
+
}
|
|
161
|
+
export interface AppCatalogFactoryResource {
|
|
162
|
+
id: string;
|
|
163
|
+
templates?: CatalogTemplateComponentResource[];
|
|
164
|
+
factory: (runtime: AppRuntime) => AbstractCatalog | Promise<AbstractCatalog>;
|
|
165
|
+
}
|
|
166
|
+
export interface AppMediaManagerResource {
|
|
167
|
+
factory: (runtime: AppRuntime) => AbstractMediaManager | Promise<AbstractMediaManager>;
|
|
168
|
+
}
|
|
169
|
+
export interface AppAiAssistantContext {
|
|
170
|
+
runtime: AppRuntime;
|
|
171
|
+
routePrefix: string;
|
|
172
|
+
getModelResources(): ModelResource[];
|
|
173
|
+
resolveModelResource(modelName: string): ModelResource | undefined;
|
|
174
|
+
hasPermission(token: string, user: User): boolean;
|
|
175
|
+
createDataAccessor(modelResource: ModelResource, user: User, action: ActionType): DataAccessor;
|
|
176
|
+
}
|
|
177
|
+
export interface AppAiAssistantResource {
|
|
178
|
+
models: Array<(context: AppAiAssistantContext) => AbstractAiModelService | Promise<AbstractAiModelService>>;
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Resource registration API provided while an app is being enabled.
|
|
182
|
+
*
|
|
183
|
+
* Apps use this context to declare everything they own. They must not accept
|
|
184
|
+
* an Adminizer instance in their constructor or setup method. This keeps app
|
|
185
|
+
* lifecycle, resource cleanup, model permissions, and runtime capabilities
|
|
186
|
+
* under AppManager control.
|
|
187
|
+
*/
|
|
188
|
+
export interface AppSetupContext {
|
|
189
|
+
asset(asset: AppAsset): string;
|
|
190
|
+
control(control: AppControlResource): void;
|
|
191
|
+
widget(widget: AppWidgetResource): void;
|
|
192
|
+
controller(controller: AppController): string;
|
|
193
|
+
config(config: AppConfigPatch, id?: string): void;
|
|
194
|
+
accessRight(token: AccessRightsToken): void;
|
|
195
|
+
catalog(catalog: AppCatalogFactoryResource): void;
|
|
196
|
+
mediaManager(resource: AppMediaManagerResource): void;
|
|
197
|
+
model(model: AppModelResource): void;
|
|
198
|
+
modelAccess(access: AppModelAccessResource): void;
|
|
199
|
+
aiAssistant(resource: AppAiAssistantResource): void;
|
|
200
|
+
listener(event: AppEventName, handler: AppEventHandler): void;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Base contract for an Adminizer app.
|
|
204
|
+
*
|
|
205
|
+
* An app is created from its own configuration and optional app-level
|
|
206
|
+
* dependencies, then enabled with `adminizer.appManager.enable(app)`.
|
|
207
|
+
* The Adminizer instance belongs to the host application and must not be
|
|
208
|
+
* passed into or stored by the app.
|
|
209
|
+
*
|
|
210
|
+
* Use `setup(ctx)` for resource registration. At runtime, use:
|
|
211
|
+
* - `req.runtime` in app controllers;
|
|
212
|
+
* - the `runtime` factory argument in catalogs;
|
|
213
|
+
* - the `runtime` handler argument in event listeners.
|
|
214
|
+
*/
|
|
215
|
+
export declare abstract class AbstractAdminizerApp<TConfig = unknown> {
|
|
216
|
+
abstract readonly name: string;
|
|
217
|
+
abstract readonly version: string;
|
|
218
|
+
readonly config?: TConfig;
|
|
219
|
+
protected constructor();
|
|
220
|
+
/**
|
|
221
|
+
* Declares resources owned by the app.
|
|
222
|
+
*
|
|
223
|
+
* Do not add an Adminizer parameter. Runtime access is intentionally
|
|
224
|
+
* provided through registered controller requests, factories, and
|
|
225
|
+
* listeners.
|
|
226
|
+
*/
|
|
227
|
+
abstract setup(ctx: AppSetupContext): void | Promise<void>;
|
|
228
|
+
}
|