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.
Files changed (307) hide show
  1. package/Readme.md +2 -1
  2. package/assets/add-De0wyF0A.js +1 -0
  3. package/assets/add-group-CMWybgIR.js +1 -0
  4. package/assets/add-user-BxP23daH.js +1 -0
  5. package/assets/app-CtWuWD5N.css +1 -0
  6. package/assets/app-layout-GYMU2HO1.js +1 -0
  7. package/assets/app.js +45 -50
  8. package/assets/catalog-tImW1BnS.js +6 -0
  9. package/assets/controls/chunks/_commonjsHelpers-C6fGbg64.js +6 -0
  10. package/assets/controls/chunks/index-CjiZ2FIa.js +93 -0
  11. package/assets/controls/chunks/jsx-runtime-jIxVPx5o.js +31 -0
  12. package/assets/controls/chunks/use-appearance-CFLOwiSX.js +38 -0
  13. package/assets/controls/chunks/use-window-size-BXgBKW65.js +19 -0
  14. package/assets/controls/chunks/vanilla-picker-Dg97NL4h.js +529 -0
  15. package/assets/controls/ckeditor.es.js +83683 -0
  16. package/assets/controls/handsontable.css +1 -0
  17. package/assets/controls/handsontable.es.js +70585 -0
  18. package/assets/controls/jsoneditor.css +1 -0
  19. package/assets/controls/jsoneditor.es.js +42082 -0
  20. package/assets/controls/leaflet.css +1 -0
  21. package/assets/controls/leaflet.es.js +10299 -0
  22. package/assets/controls/monaco.es.js +618 -0
  23. package/assets/controls/toast-ui.css +1 -0
  24. package/assets/controls/toast-ui.es.js +41797 -0
  25. package/assets/dashboard-CQ4atH3N.js +3 -0
  26. package/assets/debounce-BMmXVQ06.js +1 -0
  27. package/assets/history-BileQPEi.js +1 -0
  28. package/assets/init-user-8aUohVDq.js +1 -0
  29. package/assets/list-Pz_5mfSq.js +4 -0
  30. package/assets/login-CO4laf6m.js +1 -0
  31. package/assets/manifest.json +66 -602
  32. package/assets/media-manager-BDbpSSjH.js +6 -0
  33. package/assets/media-manager-BZPjl98I.css +1 -0
  34. package/assets/module-CQiOdgZN.js +1 -0
  35. package/assets/notification-BIyv1smE.js +20 -0
  36. package/assets/register-DBYIMXGQ.js +1 -0
  37. package/assets/ru-rYKdQbWF.js +1 -0
  38. package/assets/use-appearance-_EbbjHTh.js +1 -0
  39. package/assets/user-filters-list-Aq8CvCeh.js +1 -0
  40. package/assets/welcome-DxhWYzUL.js +1 -0
  41. package/controllers/add.d.ts +1 -1
  42. package/controllers/addGroup.js +3 -3
  43. package/controllers/addUser.js +3 -3
  44. package/controllers/catalog/Catalog.d.ts +1 -1
  45. package/controllers/catalog/Catalog.js +2 -1
  46. package/controllers/catalog/FrontendCatalogAdapter.d.ts +2 -12
  47. package/controllers/catalog/FrontendCatalogAdapter.js +3 -16
  48. package/controllers/catalog/FrontentCatalogAdapter.d.ts +2 -12
  49. package/controllers/catalog/FrontentCatalogAdapter.js +0 -16
  50. package/controllers/edit.js +6 -11
  51. package/controllers/editGroup.d.ts +1 -1
  52. package/controllers/editGroup.js +3 -3
  53. package/controllers/editUser.d.ts +1 -1
  54. package/controllers/editUser.js +3 -3
  55. package/controllers/feed.js +1 -1
  56. package/controllers/filter-fields/columns.js +1 -1
  57. package/controllers/filter-fields/getAllUserFilters.js +2 -2
  58. package/controllers/filter-fields/groups.js +2 -2
  59. package/controllers/filter-fields/savedFilters.js +1 -1
  60. package/controllers/history-actions/HistoryController.js +1 -1
  61. package/controllers/initUser.js +2 -2
  62. package/controllers/list.d.ts +1 -1
  63. package/controllers/login.d.ts +1 -1
  64. package/controllers/login.js +3 -3
  65. package/controllers/media-manager/ThumbController.js +2 -2
  66. package/controllers/media-manager/mediaManagerApi.js +13 -15
  67. package/controllers/register.d.ts +1 -1
  68. package/controllers/register.js +5 -5
  69. package/controllers/remove.d.ts +1 -1
  70. package/controllers/userApiKey.js +2 -2
  71. package/controllers/view.d.ts +1 -1
  72. package/controllers/view.js +5 -5
  73. package/controllers/widgets/Action.js +5 -2
  74. package/controllers/widgets/filterInfo.js +1 -1
  75. package/files/favicon.png +0 -0
  76. package/helpers/accessRightsHelper.d.ts +6 -4
  77. package/helpers/accessRightsHelper.js +13 -0
  78. package/helpers/diffHelpers.js +1 -1
  79. package/helpers/fieldsHelper.d.ts +1 -1
  80. package/helpers/fieldsHelper.js +8 -4
  81. package/helpers/filterDatetimeHelper.d.ts +1 -1
  82. package/helpers/inertiaAddHelper.d.ts +6 -5
  83. package/helpers/inertiaAddHelper.js +45 -37
  84. package/helpers/inertiaAutHelper.js +5 -5
  85. package/helpers/inertiaGroupHelper.d.ts +4 -4
  86. package/helpers/inertiaMenuHelper.js +1 -1
  87. package/helpers/inertiaUserHelper.d.ts +3 -3
  88. package/helpers/inertiaUserHelper.js +1 -1
  89. package/helpers/menuHelper.d.ts +2 -2
  90. package/index.d.ts +19 -6
  91. package/index.js +19 -6
  92. package/interfaces/adminpanelConfig.d.ts +10 -33
  93. package/interfaces/global.d.ts +10 -15
  94. package/interfaces/listQueryBuilder.d.ts +1 -1
  95. package/lib/Adminizer.d.ts +25 -2
  96. package/lib/Adminizer.js +124 -50
  97. package/lib/DataAccessor.d.ts +9 -5
  98. package/lib/DataAccessor.js +89 -42
  99. package/lib/access-rights/AccessRightsHandler.d.ts +17 -0
  100. package/lib/access-rights/AccessRightsHandler.js +63 -0
  101. package/lib/ai-assistant/AbstractAiModelService.d.ts +5 -8
  102. package/lib/ai-assistant/AbstractAiModelService.js +3 -14
  103. package/lib/ai-assistant/AiAssistantHandler.d.ts +2 -2
  104. package/lib/app-manager/AdminizerApp.d.ts +228 -0
  105. package/lib/app-manager/AdminizerApp.js +17 -0
  106. package/lib/app-manager/AppManager.d.ts +18 -0
  107. package/lib/app-manager/AppManager.js +537 -0
  108. package/lib/app-manager/AssetHandler.d.ts +17 -0
  109. package/lib/app-manager/AssetHandler.js +67 -0
  110. package/lib/app-manager/ConfigLayerHandler.d.ts +18 -0
  111. package/lib/app-manager/ConfigLayerHandler.js +119 -0
  112. package/lib/app-manager/ControllerHandler.d.ts +25 -0
  113. package/lib/app-manager/ControllerHandler.js +140 -0
  114. package/lib/catalog/AbstractCatalog.d.ts +28 -19
  115. package/lib/catalog/AbstractCatalog.js +41 -10
  116. package/lib/catalog/CatalogHandler.d.ts +13 -0
  117. package/lib/catalog/CatalogHandler.js +48 -2
  118. package/lib/catalog/CatalogTemplateComponentHandler.d.ts +23 -0
  119. package/lib/catalog/CatalogTemplateComponentHandler.js +59 -0
  120. package/lib/controls/Control.d.ts +17 -0
  121. package/lib/controls/Control.js +1 -0
  122. package/lib/controls/ControlsHandler.d.ts +5 -6
  123. package/lib/controls/ControlsHandler.js +0 -13
  124. package/lib/controls/codeEditor/MonacoEditor.d.ts +3 -4
  125. package/lib/controls/codeEditor/MonacoEditor.js +13 -8
  126. package/lib/controls/geojsoneditor/GeoEditor.d.ts +3 -4
  127. package/lib/controls/geojsoneditor/GeoEditor.js +14 -9
  128. package/lib/controls/jsoneditor/JsonEditor.d.ts +5 -6
  129. package/lib/controls/jsoneditor/JsonEditor.js +14 -13
  130. package/lib/controls/markdown/ToastUiEditor.d.ts +5 -6
  131. package/lib/controls/markdown/ToastUiEditor.js +14 -13
  132. package/lib/controls/table/Handsontable.d.ts +5 -6
  133. package/lib/controls/table/Handsontable.js +14 -13
  134. package/lib/controls/wysiwyg/CKeditor.d.ts +5 -6
  135. package/lib/controls/wysiwyg/CKeditor.js +14 -13
  136. package/lib/filters/ConditionValidator.d.ts +1 -1
  137. package/lib/filters/FilterService.d.ts +18 -18
  138. package/lib/filters/FilterService.js +11 -11
  139. package/lib/history-actions/AbstractHistoryAdapter.d.ts +14 -14
  140. package/lib/history-actions/AbstractHistoryAdapter.js +5 -6
  141. package/lib/history-actions/DefaultHistoryAdapter.d.ts +7 -7
  142. package/lib/history-actions/DefaultHistoryAdapter.js +2 -2
  143. package/lib/list-query-builder/ListQueryBuilder.d.ts +1 -1
  144. package/lib/media-manager/AbstractMediaManager.d.ts +14 -3
  145. package/lib/media-manager/AbstractMediaManager.js +16 -5
  146. package/lib/media-manager/MediaManagerHandler.d.ts +3 -0
  147. package/lib/media-manager/MediaManagerHandler.js +30 -5
  148. package/lib/media-manager/Thumb.js +7 -13
  149. package/lib/media-manager/helpers/MediaManagerHelper.d.ts +6 -4
  150. package/lib/media-manager/helpers/MediaManagerHelper.js +18 -18
  151. package/lib/model/AbstractModel.d.ts +9 -5
  152. package/lib/model/AbstractModel.js +9 -13
  153. package/lib/model/InternalModelAccessFactory.js +9 -10
  154. package/lib/model/ModelHandler.d.ts +45 -7
  155. package/lib/model/ModelHandler.js +168 -11
  156. package/lib/model/adapter/sequelize.d.ts +2 -4
  157. package/lib/model/adapter/sequelize.js +16 -193
  158. package/lib/model/adapter/typeorm.d.ts +2 -4
  159. package/lib/model/adapter/typeorm.js +2 -35
  160. package/lib/notifications/AbstractNotificationService.d.ts +9 -9
  161. package/lib/notifications/AbstractNotificationService.js +8 -8
  162. package/lib/notifications/GeneralNotificationService.js +1 -1
  163. package/lib/notifications/SystemNotificationService.js +1 -1
  164. package/lib/ui-i18n/uiTranslationKeys.d.ts +1 -1
  165. package/lib/ui-i18n/uiTranslationKeys.js +1 -0
  166. package/lib/widgets/abstractCustom.d.ts +5 -2
  167. package/lib/widgets/abstractCustom.js +4 -0
  168. package/lib/widgets/widgetHandler.d.ts +5 -2
  169. package/lib/widgets/widgetHandler.js +60 -14
  170. package/models/{FilterAP.d.ts → Filter.d.ts} +4 -70
  171. package/models/Filter.js +1 -0
  172. package/models/FilterColumn.d.ts +10 -0
  173. package/models/FilterColumn.js +1 -0
  174. package/models/Group.d.ts +8 -0
  175. package/models/Group.js +1 -0
  176. package/models/HistoryActions.d.ts +14 -0
  177. package/models/HistoryActions.js +1 -0
  178. package/models/MediaManagerAP.d.ts +2 -54
  179. package/models/MediaManagerAP.js +1 -51
  180. package/models/MediaManagerAssociationsAP.d.ts +1 -35
  181. package/models/MediaManagerAssociationsAP.js +1 -33
  182. package/models/MediaManagerMetaAP.d.ts +2 -30
  183. package/models/MediaManagerMetaAP.js +1 -27
  184. package/models/Notification.d.ts +13 -0
  185. package/models/Notification.js +1 -0
  186. package/models/User.d.ts +23 -0
  187. package/models/User.js +1 -0
  188. package/models/UserNotification.d.ts +7 -0
  189. package/models/UserNotification.js +1 -0
  190. package/package.json +25 -30
  191. package/services/FeedService.d.ts +5 -5
  192. package/services/FeedService.js +1 -1
  193. package/system/Router.js +2 -10
  194. package/system/bindAccessRights.js +1 -1
  195. package/system/bindAuthorization.js +2 -2
  196. package/system/bindControls.js +7 -6
  197. package/system/bindDashboardWidgets.js +3 -2
  198. package/system/bindDev.js +1 -1
  199. package/system/bindHistory.d.ts +1 -1
  200. package/system/bindHistory.js +1 -1
  201. package/system/bindInertia.js +0 -6
  202. package/system/bindMediaManager.js +4 -17
  203. package/system/bindNotifications.d.ts +1 -1
  204. package/system/bindNotifications.js +1 -1
  205. package/system/bindReqFunctions.d.ts +1 -0
  206. package/system/bindReqFunctions.js +20 -3
  207. package/system/buildInternalModelAccess.js +2 -15
  208. package/system/defaults.js +5 -10
  209. package/system/systemModelContracts.d.ts +22 -0
  210. package/system/systemModelContracts.js +155 -0
  211. package/system/validateSystemModels.d.ts +2 -0
  212. package/system/validateSystemModels.js +101 -0
  213. package/translations/ar.json +2 -1
  214. package/translations/cn.json +2 -1
  215. package/translations/de.json +2 -1
  216. package/translations/en.json +1 -0
  217. package/translations/es.json +2 -1
  218. package/translations/fr.json +2 -1
  219. package/translations/hi.json +2 -1
  220. package/translations/jp.json +2 -1
  221. package/translations/ko.json +2 -1
  222. package/translations/pt.json +2 -1
  223. package/translations/ru.json +2 -1
  224. package/translations/ua.json +2 -1
  225. package/translations/uz.json +2 -1
  226. package/translations/vi.json +2 -1
  227. package/assets/RelationDialogStack-DhThUMep.js +0 -20
  228. package/assets/add-group-L8lMigab.js +0 -1
  229. package/assets/add-group-form-o7F6t6qW.js +0 -1
  230. package/assets/add-user-BE4-TNJw.js +0 -1
  231. package/assets/add-user-form-BP_8TjxN.js +0 -1
  232. package/assets/add-w398ZkhF.js +0 -1
  233. package/assets/app-C7J5ccrI.css +0 -1
  234. package/assets/app-layout-Bg455MIY.js +0 -1
  235. package/assets/app-shell-BT2kF0We.js +0 -1
  236. package/assets/app-sidebar-CZfPUXck.js +0 -1
  237. package/assets/app-sidebar-header-UVcKhNm6.js +0 -1
  238. package/assets/breadcrumbs-CrqIBT84.js +0 -1
  239. package/assets/catalog-btkCCwiv.js +0 -6
  240. package/assets/dashboard-Bvasc1cr.js +0 -3
  241. package/assets/date-picker-kxQWOxHk.js +0 -1
  242. package/assets/del-modal-C39LR5aQ.js +0 -1
  243. package/assets/dynamic-controls-DyPP5sTC.js +0 -1
  244. package/assets/geo-json-CGNQFl6J.js +0 -1
  245. package/assets/handsontable-Dv59QFH5.js +0 -336
  246. package/assets/history-DwKR8MPu.js +0 -1
  247. package/assets/home-BrtAkCRj.js +0 -2
  248. package/assets/icon-DpUVVHmK.js +0 -1
  249. package/assets/init-user-8PiKR1zX.js +0 -1
  250. package/assets/input-error-Cbt-Y3Fj.js +0 -1
  251. package/assets/jsoneditor-CiPXPMho.js +0 -3869
  252. package/assets/list-DGsL_qii.js +0 -4
  253. package/assets/login-Br_FcBU_.js +0 -1
  254. package/assets/material-icon-DOkM71fv.js +0 -1
  255. package/assets/module-EgfgIEhF.js +0 -1
  256. package/assets/monaco-ompgKQ3_.js +0 -11
  257. package/assets/nav-footer-DzLwfBAE.js +0 -1
  258. package/assets/nav-main-CSEbjYgs.js +0 -1
  259. package/assets/nav-user-DJ0hfhfH.js +0 -1
  260. package/assets/notification-B19ZLro7.js +0 -20
  261. package/assets/pagination-render-8GvJxq55.js +0 -1
  262. package/assets/register-CZzIeT2G.js +0 -1
  263. package/assets/ru-1NdRIQNV.js +0 -1
  264. package/assets/theme-switcher-CBbEj1u-.js +0 -1
  265. package/assets/toast-editor-DwVqd1rz.js +0 -668
  266. package/assets/user-filters-list-7Im79QyS.js +0 -1
  267. package/assets/user-info-YiU17Mmy.js +0 -1
  268. package/assets/user-menu-content-gdpNF5-8.js +0 -1
  269. package/assets/vanilla-picker-B3LgXoKV.js +0 -2
  270. package/assets/vendor-l0sNRNKZ.js +0 -1
  271. package/assets/welcome-nT0eFWHb.js +0 -1
  272. package/controllers/navigation/edit.d.ts +0 -1
  273. package/controllers/navigation/edit.js +0 -8
  274. package/lib/ai-assistant/DummyAiModelService.d.ts +0 -8
  275. package/lib/ai-assistant/DummyAiModelService.js +0 -14
  276. package/lib/ai-assistant/OpenAiModelService.d.ts +0 -24
  277. package/lib/ai-assistant/OpenAiModelService.js +0 -184
  278. package/lib/catalog/Navigation.d.ts +0 -49
  279. package/lib/catalog/Navigation.js +0 -492
  280. package/lib/controls/AbstractControls.d.ts +0 -30
  281. package/lib/controls/AbstractControls.js +0 -14
  282. package/lib/media-manager/DefaultMediaManager.d.ts +0 -20
  283. package/lib/media-manager/DefaultMediaManager.js +0 -127
  284. package/lib/media-manager/Items.d.ts +0 -50
  285. package/lib/media-manager/Items.js +0 -296
  286. package/models/FilterAP.js +0 -68
  287. package/models/FilterColumnAP.d.ts +0 -32
  288. package/models/FilterColumnAP.js +0 -21
  289. package/models/GroupAP.d.ts +0 -31
  290. package/models/GroupAP.js +0 -22
  291. package/models/HistoryActionsAP.d.ts +0 -56
  292. package/models/HistoryActionsAP.js +0 -42
  293. package/models/NavigationAP.d.ts +0 -22
  294. package/models/NavigationAP.js +0 -16
  295. package/models/NotificationAP.d.ts +0 -44
  296. package/models/NotificationAP.js +0 -30
  297. package/models/UserAP.d.ts +0 -81
  298. package/models/UserAP.js +0 -57
  299. package/models/UserNotificationAP.d.ts +0 -26
  300. package/models/UserNotificationAP.js +0 -18
  301. package/system/bindAiAssistant.d.ts +0 -2
  302. package/system/bindAiAssistant.js +0 -30
  303. package/system/bindModels.d.ts +0 -2
  304. package/system/bindModels.js +0 -48
  305. package/system/bindNavigation.d.ts +0 -2
  306. package/system/bindNavigation.js +0 -38
  307. /package/assets/{RelationDialogStack-B00Ot5qS.css → controls/ckeditor.css} +0 -0
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Base contract for an Adminizer app.
3
+ *
4
+ * An app is created from its own configuration and optional app-level
5
+ * dependencies, then enabled with `adminizer.appManager.enable(app)`.
6
+ * The Adminizer instance belongs to the host application and must not be
7
+ * passed into or stored by the app.
8
+ *
9
+ * Use `setup(ctx)` for resource registration. At runtime, use:
10
+ * - `req.runtime` in app controllers;
11
+ * - the `runtime` factory argument in catalogs;
12
+ * - the `runtime` handler argument in event listeners.
13
+ */
14
+ export class AbstractAdminizerApp {
15
+ config;
16
+ constructor() { }
17
+ }
@@ -0,0 +1,18 @@
1
+ import { Adminizer } from "../Adminizer";
2
+ import { AbstractAdminizerApp, AppRuntime, AppRuntimeAppState } from "./AdminizerApp";
3
+ export type AppState = AppRuntimeAppState;
4
+ export declare class AppManager {
5
+ private adminizer;
6
+ private installedApps;
7
+ constructor(adminizer: Adminizer);
8
+ register(app: AbstractAdminizerApp): void;
9
+ enable(appOrName: AbstractAdminizerApp | string): Promise<void>;
10
+ disable(appName: string): Promise<void>;
11
+ unregister(appName: string): Promise<void>;
12
+ getInstalledApps(): string[];
13
+ getApp(name: string): AbstractAdminizerApp | undefined;
14
+ getState(name: string): AppState | undefined;
15
+ createRuntime(appName: string): AppRuntime;
16
+ private getRuntimeApp;
17
+ private disposeResources;
18
+ }
@@ -0,0 +1,537 @@
1
+ import { Adminizer } from "../Adminizer.js";
2
+ import { AiAssistantHandler } from "../ai-assistant/AiAssistantHandler.js";
3
+ import { DataAccessor } from "../DataAccessor.js";
4
+ class RuntimeAppSetupContext {
5
+ adminizer;
6
+ appName;
7
+ disposers = [];
8
+ pendingModelRegistrations = [];
9
+ pendingModelAccessRegistrations = [];
10
+ pendingAiAssistantRegistrations = [];
11
+ pendingMediaManagerRegistrations = [];
12
+ pendingCatalogRegistrations = [];
13
+ configLayerIndex = 0;
14
+ modelAccessIndex = 0;
15
+ constructor(adminizer, appName) {
16
+ this.adminizer = adminizer;
17
+ this.appName = appName;
18
+ }
19
+ asset(asset) {
20
+ const resourceId = `${this.appName}:${asset.id}`;
21
+ const url = this.adminizer.assetHandler.register(this.appName, asset);
22
+ this.disposers.push(() => this.adminizer.assetHandler.unregister(resourceId));
23
+ return url;
24
+ }
25
+ control(resource) {
26
+ const jsUrl = this.asset(resource.component);
27
+ const cssUrl = resource.stylesheet ? this.asset(resource.stylesheet) : undefined;
28
+ const control = {
29
+ name: resource.name,
30
+ type: resource.type,
31
+ getName: () => resource.name,
32
+ getConfig: () => resource.config,
33
+ getJsPath: () => jsUrl,
34
+ getCssPath: () => cssUrl,
35
+ };
36
+ const resourceId = `${this.appName}:${resource.id ?? `${resource.type}:${resource.name}`}`;
37
+ this.adminizer.controlsHandler.add(control);
38
+ this.adminizer.emitter.emit("app:control:registered", {
39
+ appName: this.appName,
40
+ resourceId,
41
+ type: resource.type,
42
+ name: resource.name,
43
+ jsUrl,
44
+ cssUrl,
45
+ });
46
+ this.disposers.push(() => {
47
+ if (this.adminizer.controlsHandler.get(resource.type, resource.name) === control) {
48
+ this.adminizer.controlsHandler.remove(resource.type, resource.name);
49
+ }
50
+ this.adminizer.emitter.emit("app:control:unregistered", {
51
+ appName: this.appName,
52
+ resourceId,
53
+ type: resource.type,
54
+ name: resource.name,
55
+ });
56
+ });
57
+ }
58
+ widget(resource) {
59
+ if (this.adminizer.widgetHandler.getById(resource.id)) {
60
+ throw new Error(`Widget "${resource.id}" is already registered`);
61
+ }
62
+ const accessRightsToken = resource.accessRightsToken ?? `widget-${resource.id}`;
63
+ if (!resource.accessRightsToken) {
64
+ this.accessRight({
65
+ id: accessRightsToken,
66
+ name: resource.name,
67
+ description: resource.description,
68
+ department: resource.department,
69
+ });
70
+ }
71
+ const widget = this.createWidget(resource, accessRightsToken);
72
+ this.adminizer.widgetHandler.add(widget);
73
+ this.adminizer.emitter.emit("app:widget:registered", {
74
+ appName: this.appName,
75
+ resourceId: `${this.appName}:${resource.id}`,
76
+ type: resource.type,
77
+ id: resource.id,
78
+ });
79
+ this.disposers.push(() => {
80
+ this.adminizer.widgetHandler.removeById(resource.id);
81
+ this.adminizer.emitter.emit("app:widget:unregistered", {
82
+ appName: this.appName,
83
+ resourceId: `${this.appName}:${resource.id}`,
84
+ type: resource.type,
85
+ id: resource.id,
86
+ });
87
+ });
88
+ }
89
+ controller(controller) {
90
+ const resourceId = `${this.appName}:${controller.id ?? `${controller.method}:${controller.route}`}`;
91
+ const url = this.adminizer.controllerHandler.register(this.appName, controller);
92
+ this.disposers.push(() => this.adminizer.controllerHandler.unregister(resourceId));
93
+ return url;
94
+ }
95
+ config(config, id = `config-${++this.configLayerIndex}`) {
96
+ const resourceId = this.adminizer.configLayerHandler.register(this.appName, id, config);
97
+ this.disposers.push(() => this.adminizer.configLayerHandler.unregister(resourceId));
98
+ }
99
+ accessRight(token) {
100
+ const resourceId = this.adminizer.accessRightsHandler.register(this.appName, token);
101
+ this.disposers.push(() => this.adminizer.accessRightsHandler.unregister(resourceId));
102
+ }
103
+ catalog(catalog) {
104
+ this.pendingCatalogRegistrations.push(() => this.registerCatalog(catalog));
105
+ }
106
+ model(model) {
107
+ this.pendingModelRegistrations.push(() => this.registerModel(model));
108
+ }
109
+ modelAccess(access) {
110
+ this.pendingModelAccessRegistrations.push(() => this.registerModelAccess(access));
111
+ }
112
+ mediaManager(resource) {
113
+ this.pendingMediaManagerRegistrations.push(() => this.registerMediaManager(resource));
114
+ }
115
+ aiAssistant(resource) {
116
+ this.pendingAiAssistantRegistrations.push(() => this.registerAiAssistant(resource));
117
+ }
118
+ listener(event, handler) {
119
+ const resourceId = `${this.appName}:${event.toString()}:${this.disposers.length + 1}`;
120
+ const listener = (payload) => handler(payload, this.adminizer.appManager.createRuntime(this.appName));
121
+ this.adminizer.emitter.on(event, listener);
122
+ this.adminizer.emitter.emit("app:listener:registered", {
123
+ appName: this.appName,
124
+ resourceId,
125
+ event,
126
+ });
127
+ this.disposers.push(() => {
128
+ this.adminizer.emitter.off(event, listener);
129
+ this.adminizer.emitter.emit("app:listener:unregistered", {
130
+ appName: this.appName,
131
+ resourceId,
132
+ event,
133
+ });
134
+ });
135
+ }
136
+ async waitForPendingRegistrations() {
137
+ await this.runRegistrationPhase(this.pendingModelRegistrations);
138
+ await this.runRegistrationPhase(this.pendingModelAccessRegistrations);
139
+ await this.runRegistrationPhase(this.pendingAiAssistantRegistrations);
140
+ await this.runRegistrationPhase(this.pendingMediaManagerRegistrations);
141
+ await this.runRegistrationPhase(this.pendingCatalogRegistrations);
142
+ }
143
+ async registerAiAssistant(resource) {
144
+ const previousHandler = this.adminizer.aiAssistantHandler;
145
+ const handler = new AiAssistantHandler(this.adminizer);
146
+ const context = this.createAiAssistantContext();
147
+ for (const modelFactory of resource.models) {
148
+ const model = await modelFactory(context);
149
+ handler.registerModel(model);
150
+ }
151
+ this.adminizer.aiAssistantHandler = handler;
152
+ this.adminizer.emitter.emit("app:ai-assistant:registered", {
153
+ appName: this.appName,
154
+ models: handler.getModels().map((model) => model.id),
155
+ });
156
+ this.disposers.push(() => {
157
+ if (this.adminizer.aiAssistantHandler === handler) {
158
+ this.adminizer.aiAssistantHandler = previousHandler;
159
+ }
160
+ this.adminizer.emitter.emit("app:ai-assistant:unregistered", {
161
+ appName: this.appName,
162
+ });
163
+ });
164
+ }
165
+ async registerMediaManager(resource) {
166
+ const manager = await resource.factory(this.adminizer.appManager.createRuntime(this.appName));
167
+ const resourceId = this.adminizer.mediaManagerHandler.register(this.appName, manager);
168
+ this.adminizer.emitter.emit("app:media-manager:registered", {
169
+ appName: this.appName,
170
+ resourceId,
171
+ managerId: manager.id,
172
+ });
173
+ this.disposers.push(() => {
174
+ this.adminizer.mediaManagerHandler.unregister(resourceId);
175
+ this.adminizer.emitter.emit("app:media-manager:unregistered", {
176
+ appName: this.appName,
177
+ resourceId,
178
+ managerId: manager.id,
179
+ });
180
+ });
181
+ }
182
+ async registerCatalog(catalogResource) {
183
+ const catalog = await catalogResource.factory(this.adminizer.appManager.createRuntime(this.appName));
184
+ const resourceId = this.adminizer.catalogHandler.register(this.appName, catalog);
185
+ this.adminizer.emitter.emit("app:catalog:registered", {
186
+ appName: this.appName,
187
+ resourceId,
188
+ slug: catalog.slug,
189
+ });
190
+ this.disposers.push(() => {
191
+ this.adminizer.catalogHandler.unregister(resourceId);
192
+ this.adminizer.emitter.emit("app:catalog:unregistered", {
193
+ appName: this.appName,
194
+ resourceId,
195
+ slug: catalog.slug,
196
+ });
197
+ });
198
+ for (const template of catalogResource.templates ?? []) {
199
+ const templateResourceId = this.adminizer.catalogTemplateComponentHandler.register(this.appName, catalog.slug, template);
200
+ this.adminizer.emitter.emit("app:catalog-template-component:registered", {
201
+ appName: this.appName,
202
+ resourceId: templateResourceId,
203
+ catalog: catalog.slug,
204
+ type: template.type,
205
+ });
206
+ this.disposers.push(() => {
207
+ this.adminizer.catalogTemplateComponentHandler.unregister(templateResourceId);
208
+ this.adminizer.emitter.emit("app:catalog-template-component:unregistered", {
209
+ appName: this.appName,
210
+ resourceId: templateResourceId,
211
+ catalog: catalog.slug,
212
+ type: template.type,
213
+ });
214
+ });
215
+ }
216
+ }
217
+ async registerModelAccess(access) {
218
+ const resourceId = this.adminizer.modelHandler.registerAppAccess(this.appName, access.id ?? `model-access-${++this.modelAccessIndex}`, access.models);
219
+ this.adminizer.emitter.emit("app:model-access:registered", {
220
+ appName: this.appName,
221
+ resourceId,
222
+ models: access.models,
223
+ });
224
+ this.disposers.push(() => {
225
+ this.adminizer.modelHandler.unregisterAppAccess(resourceId);
226
+ this.adminizer.emitter.emit("app:model-access:unregistered", {
227
+ appName: this.appName,
228
+ resourceId,
229
+ models: access.models,
230
+ });
231
+ });
232
+ }
233
+ async registerModel(model) {
234
+ const ormAdapter = this.resolveModelAdapter(model.adapter);
235
+ const registeredModel = ormAdapter.getModel(model.name);
236
+ if (!registeredModel) {
237
+ if (ormAdapter.ormType === "typeorm") {
238
+ throw new Error(`App "${this.appName}" requires TypeORM entity "${model.name}". ` +
239
+ "TypeORM entities must be registered in DataSource before initialize(). " +
240
+ "Dynamic app model installation is supported only for Sequelize.");
241
+ }
242
+ throw new Error(`App "${this.appName}" requires model "${model.name}", but it was not provided by adapter ` +
243
+ `"${ormAdapter.ormType}". Install the app model before enabling the app.`);
244
+ }
245
+ const modelInstance = new ormAdapter.Model(model.name, registeredModel);
246
+ const resourceId = this.adminizer.modelHandler.register(this.appName, model.name, modelInstance);
247
+ this.adminizer.emitter.emit("app:model:registered", {
248
+ appName: this.appName,
249
+ resourceId,
250
+ modelName: model.name,
251
+ });
252
+ this.disposers.push(() => {
253
+ this.adminizer.modelHandler.unregister(resourceId);
254
+ this.adminizer.emitter.emit("app:model:unregistered", {
255
+ appName: this.appName,
256
+ resourceId,
257
+ modelName: model.name,
258
+ });
259
+ });
260
+ }
261
+ async runRegistrationPhase(registrations) {
262
+ for (const registration of registrations) {
263
+ await registration();
264
+ }
265
+ }
266
+ createWidget(resource, accessRightsToken) {
267
+ const runtime = () => this.adminizer.appManager.createRuntime(this.appName);
268
+ const base = {
269
+ id: resource.id,
270
+ name: resource.name,
271
+ description: resource.description,
272
+ icon: resource.icon,
273
+ department: resource.department,
274
+ backgroundCSS: resource.backgroundCSS ?? null,
275
+ size: resource.size ?? null,
276
+ accessRightsToken,
277
+ group: resource.group,
278
+ };
279
+ if (resource.type === "info") {
280
+ return {
281
+ ...base,
282
+ widgetType: "info",
283
+ link: resource.link,
284
+ linkType: resource.linkType,
285
+ getInfo: (context) => resource.getInfo({
286
+ ...context,
287
+ runtime: runtime(),
288
+ }),
289
+ };
290
+ }
291
+ if (resource.type === "switcher") {
292
+ return {
293
+ ...base,
294
+ widgetType: "switcher",
295
+ getState: () => resource.getState({
296
+ runtime: runtime(),
297
+ }),
298
+ switchIt: () => resource.switchIt({
299
+ runtime: runtime(),
300
+ }),
301
+ };
302
+ }
303
+ if (resource.type === "action") {
304
+ return {
305
+ ...base,
306
+ widgetType: "action",
307
+ action: () => resource.action({
308
+ runtime: runtime(),
309
+ }),
310
+ };
311
+ }
312
+ if (resource.type === "link") {
313
+ return {
314
+ ...base,
315
+ widgetType: "link",
316
+ links: resource.links,
317
+ getLinks: async () => resource.links,
318
+ };
319
+ }
320
+ const scriptUrl = this.asset(resource.component);
321
+ return {
322
+ ...base,
323
+ widgetType: "custom",
324
+ routePrefix: this.adminizer.config.routePrefix,
325
+ jsPath: {
326
+ dev: scriptUrl,
327
+ production: scriptUrl,
328
+ },
329
+ scriptUrl,
330
+ };
331
+ }
332
+ resolveModelAdapter(adapterName) {
333
+ const resolvedAdapterName = adapterName ?? this.adminizer.config.system?.defaultORM;
334
+ if (resolvedAdapterName) {
335
+ const adapter = this.adminizer.getOrmAdapter(resolvedAdapterName);
336
+ if (!adapter) {
337
+ throw new Error(`Adapter "${resolvedAdapterName}" was not found`);
338
+ }
339
+ return adapter;
340
+ }
341
+ if (this.adminizer.ormAdapters.length === 1) {
342
+ return this.adminizer.ormAdapters[0];
343
+ }
344
+ throw new Error(`Adapter was not provided for app model registration "${this.appName}"`);
345
+ }
346
+ createAiAssistantContext() {
347
+ return {
348
+ runtime: this.adminizer.appManager.createRuntime(this.appName),
349
+ routePrefix: this.adminizer.config.routePrefix,
350
+ getModelResources: () => this.getModelResources(),
351
+ resolveModelResource: (modelName) => this.resolveModelResource(modelName),
352
+ hasPermission: (token, user) => this.adminizer.accessRightsHelper.hasPermission(token, user),
353
+ createDataAccessor: (modelResource, user, action) => new DataAccessor(this.adminizer, user, modelResource, action),
354
+ };
355
+ }
356
+ getModelResources() {
357
+ const resources = [];
358
+ for (const [configName, configValue] of Object.entries(this.adminizer.config.models ?? {})) {
359
+ const normalizedConfig = this.normalizeModelConfig(configName, configValue);
360
+ const model = this.adminizer.modelHandler.model.get(normalizedConfig.model.toLowerCase());
361
+ if (!model) {
362
+ continue;
363
+ }
364
+ resources.push({
365
+ name: configName,
366
+ uri: `${this.adminizer.config.routePrefix}/model/${configName}`,
367
+ config: normalizedConfig,
368
+ model,
369
+ });
370
+ }
371
+ return resources;
372
+ }
373
+ resolveModelResource(modelName) {
374
+ const loweredName = modelName.toLowerCase();
375
+ return this.getModelResources().find((resource) => {
376
+ const modelId = resource.config.model?.toLowerCase();
377
+ return resource.name.toLowerCase() === loweredName || modelId === loweredName;
378
+ });
379
+ }
380
+ normalizeModelConfig(name, config) {
381
+ const baseConfig = {
382
+ model: name,
383
+ icon: "description",
384
+ title: name,
385
+ list: true,
386
+ add: true,
387
+ edit: true,
388
+ remove: true,
389
+ view: true,
390
+ };
391
+ if (typeof config === "boolean") {
392
+ return config ? baseConfig : { ...baseConfig, list: false, add: false, edit: false, remove: false, view: false };
393
+ }
394
+ return { ...baseConfig, ...config };
395
+ }
396
+ }
397
+ export class AppManager {
398
+ adminizer;
399
+ installedApps = new Map();
400
+ constructor(adminizer) {
401
+ this.adminizer = adminizer;
402
+ }
403
+ register(app) {
404
+ if (this.installedApps.has(app.name)) {
405
+ throw new Error(`App "${app.name}" is already registered`);
406
+ }
407
+ this.installedApps.set(app.name, {
408
+ app,
409
+ state: "registered",
410
+ disposers: [],
411
+ });
412
+ this.adminizer.emitter.emit("app:registered", {
413
+ appName: app.name,
414
+ version: app.version,
415
+ });
416
+ }
417
+ async enable(appOrName) {
418
+ const appName = typeof appOrName === "string" ? appOrName : appOrName.name;
419
+ if (typeof appOrName !== "string" && !this.installedApps.has(appName)) {
420
+ this.register(appOrName);
421
+ }
422
+ const installed = this.installedApps.get(appName);
423
+ if (!installed) {
424
+ throw new Error(`App "${appName}" is not registered`);
425
+ }
426
+ if (this.adminizer.config.system?.defaultORM === "typeorm") {
427
+ Adminizer.log.warn(`App "${appName}" is being enabled with experimental TypeORM support. ` +
428
+ "App entities must be registered before DataSource.initialize(); dynamic model installation is unavailable.");
429
+ }
430
+ if (installed.state === "enabled") {
431
+ return;
432
+ }
433
+ installed.disposers = [];
434
+ installed.state = "registered";
435
+ this.adminizer.emitter.emit("app:enable:start", {
436
+ appName,
437
+ version: installed.app.version,
438
+ });
439
+ const ctx = new RuntimeAppSetupContext(this.adminizer, appName);
440
+ try {
441
+ await installed.app.setup(ctx);
442
+ await ctx.waitForPendingRegistrations();
443
+ installed.disposers.push(...ctx.disposers);
444
+ installed.state = "enabled";
445
+ this.adminizer.emitter.emit("app:enabled", {
446
+ appName,
447
+ version: installed.app.version,
448
+ resources: installed.disposers.length,
449
+ });
450
+ }
451
+ catch (error) {
452
+ installed.state = "failed";
453
+ installed.disposers.push(...ctx.disposers);
454
+ await this.disposeResources(installed);
455
+ this.adminizer.emitter.emit("app:enable:failed", {
456
+ appName,
457
+ error,
458
+ });
459
+ throw error;
460
+ }
461
+ }
462
+ async disable(appName) {
463
+ const installed = this.installedApps.get(appName);
464
+ if (!installed || installed.state !== "enabled") {
465
+ return;
466
+ }
467
+ this.adminizer.emitter.emit("app:disable:start", {
468
+ appName,
469
+ });
470
+ await this.disposeResources(installed);
471
+ installed.state = "disabled";
472
+ this.adminizer.emitter.emit("app:disabled", {
473
+ appName,
474
+ });
475
+ }
476
+ async unregister(appName) {
477
+ const installed = this.installedApps.get(appName);
478
+ if (!installed) {
479
+ return;
480
+ }
481
+ await this.disable(appName);
482
+ this.installedApps.delete(appName);
483
+ installed.state = "unregistered";
484
+ this.adminizer.emitter.emit("app:unregistered", {
485
+ appName,
486
+ });
487
+ }
488
+ getInstalledApps() {
489
+ return Array.from(this.installedApps.keys());
490
+ }
491
+ getApp(name) {
492
+ return this.installedApps.get(name)?.app;
493
+ }
494
+ getState(name) {
495
+ return this.installedApps.get(name)?.state;
496
+ }
497
+ createRuntime(appName) {
498
+ return {
499
+ models: this.adminizer.modelHandler.createAppAccess(appName),
500
+ config: {
501
+ routePrefix: this.adminizer.config.routePrefix,
502
+ getModelConfig: (modelName) => {
503
+ const models = this.adminizer.config.models ?? {};
504
+ const modelKey = Object.keys(models).find((key) => key.toLowerCase() === modelName.toLowerCase());
505
+ return modelKey ? models[modelKey] : undefined;
506
+ },
507
+ },
508
+ notifications: {
509
+ send: (notification) => this.adminizer.sendNotification(notification),
510
+ },
511
+ apps: {
512
+ list: () => this.getInstalledApps().map((name) => this.getRuntimeApp(name)),
513
+ get: (name) => this.getRuntimeApp(name),
514
+ enable: (name) => this.enable(name),
515
+ disable: (name) => this.disable(name),
516
+ unregister: (name) => this.unregister(name),
517
+ },
518
+ };
519
+ }
520
+ getRuntimeApp(name) {
521
+ const installed = this.installedApps.get(name);
522
+ if (!installed) {
523
+ return undefined;
524
+ }
525
+ return {
526
+ name,
527
+ version: installed.app.version,
528
+ state: installed.state,
529
+ };
530
+ }
531
+ async disposeResources(installed) {
532
+ for (const dispose of [...installed.disposers].reverse()) {
533
+ await dispose();
534
+ }
535
+ installed.disposers = [];
536
+ }
537
+ }
@@ -0,0 +1,17 @@
1
+ import type { Adminizer } from "../Adminizer";
2
+ import type { AppAsset } from "./AdminizerApp";
3
+ interface AssetRecord {
4
+ id: string;
5
+ appName: string;
6
+ url: string;
7
+ controllerId?: string;
8
+ }
9
+ export declare class AssetHandler {
10
+ private adminizer;
11
+ private assets;
12
+ constructor(adminizer: Adminizer);
13
+ register(appName: string, asset: AppAsset): string;
14
+ unregister(id: string): void;
15
+ getByApp(appName: string): AssetRecord[];
16
+ }
17
+ export {};
@@ -0,0 +1,67 @@
1
+ import path from "path";
2
+ export class AssetHandler {
3
+ adminizer;
4
+ assets = new Map();
5
+ constructor(adminizer) {
6
+ this.adminizer = adminizer;
7
+ }
8
+ register(appName, asset) {
9
+ const id = `${appName}:${asset.id}`;
10
+ if (this.assets.has(id)) {
11
+ throw new Error(`Asset "${id}" is already registered`);
12
+ }
13
+ if (process.env.ADMINIZER_ENV === "dev" && asset.devUrl) {
14
+ this.assets.set(id, {
15
+ id,
16
+ appName,
17
+ url: asset.devUrl,
18
+ });
19
+ this.adminizer.emitter.emit("app:asset:registered", {
20
+ appName,
21
+ resourceId: id,
22
+ url: asset.devUrl,
23
+ mode: "dev",
24
+ });
25
+ return asset.devUrl;
26
+ }
27
+ const fileName = path.basename(asset.filePath);
28
+ const route = asset.route ?? `/app-assets/${appName}/${asset.id}/${fileName}`;
29
+ const url = this.adminizer.controllerHandler.register(appName, {
30
+ id: `asset:${asset.id}`,
31
+ method: "get",
32
+ route,
33
+ middleware: (_req, res) => res.sendFile(asset.filePath),
34
+ });
35
+ this.assets.set(id, {
36
+ id,
37
+ appName,
38
+ url,
39
+ controllerId: `${appName}:asset:${asset.id}`,
40
+ });
41
+ this.adminizer.emitter.emit("app:asset:registered", {
42
+ appName,
43
+ resourceId: id,
44
+ url,
45
+ mode: "static",
46
+ });
47
+ return url;
48
+ }
49
+ unregister(id) {
50
+ const record = this.assets.get(id);
51
+ if (!record) {
52
+ return;
53
+ }
54
+ if (record.controllerId) {
55
+ this.adminizer.controllerHandler.unregister(record.controllerId);
56
+ }
57
+ this.assets.delete(id);
58
+ this.adminizer.emitter.emit("app:asset:unregistered", {
59
+ appName: record.appName,
60
+ resourceId: id,
61
+ url: record.url,
62
+ });
63
+ }
64
+ getByApp(appName) {
65
+ return Array.from(this.assets.values()).filter((record) => record.appName === appName);
66
+ }
67
+ }
@@ -0,0 +1,18 @@
1
+ import type { Adminizer } from "../Adminizer";
2
+ import type { AppConfigPatch } from "./AdminizerApp";
3
+ export interface ConfigLayerRecord {
4
+ id: string;
5
+ appName: string;
6
+ patch: AppConfigPatch;
7
+ }
8
+ export declare class ConfigLayerHandler {
9
+ private adminizer;
10
+ private baseConfig?;
11
+ private layers;
12
+ constructor(adminizer: Adminizer);
13
+ register(appName: string, layerId: string, patch: AppConfigPatch): string;
14
+ unregister(id: string): void;
15
+ getByApp(appName: string): ConfigLayerRecord[];
16
+ private rebuildConfig;
17
+ private getLayerId;
18
+ }