adminizer 4.6.0 → 4.7.0-build.221

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 (335) hide show
  1. package/Readme.md +12 -0
  2. package/assets/RelationDialogStack-CnHFOYyR.css +1 -0
  3. package/assets/RelationDialogStack-DI2bZxQX.js +20 -0
  4. package/assets/add-4213XScp.js +1 -0
  5. package/assets/add-group--F1XxfJ1.js +1 -0
  6. package/assets/add-group-form-DKRXC2n-.js +1 -0
  7. package/assets/add-user-b9HfMq7z.js +1 -0
  8. package/assets/add-user-form-UXAToWQd.js +1 -0
  9. package/assets/app-Clx8f5hd.css +1 -0
  10. package/assets/app-layout-C02ja4OI.js +1 -0
  11. package/assets/app-shell-D8wtkECO.js +1 -0
  12. package/assets/app-sidebar-BR5_zwxj.js +1 -0
  13. package/assets/app-sidebar-header-BZbIBQjq.js +1 -0
  14. package/assets/app.js +59 -7990
  15. package/assets/breadcrumbs-CODaqVkB.js +1 -0
  16. package/assets/catalog-6ZZjWdFs.js +6 -0
  17. package/assets/dashboard-CftAdKiH.js +3 -0
  18. package/assets/date-picker-DSOkNiNo.js +1 -0
  19. package/assets/del-modal-Cd4M7af8.js +1 -0
  20. package/assets/dynamic-controls-BsrJhPDY.js +1 -0
  21. package/assets/geo-json-CzR5JBXL.js +1 -0
  22. package/assets/handsontable-BI98OF5R.js +336 -0
  23. package/assets/history-DLHSU7Ak.js +1 -0
  24. package/assets/home-NcQ7nnU0.js +2 -0
  25. package/assets/icon-C2JNaLxH.js +1 -0
  26. package/assets/init-user-D2HSyXup.js +1 -0
  27. package/assets/input-error-CH2caTtO.js +1 -0
  28. package/assets/jsoneditor-DVNQmA6G.js +3869 -0
  29. package/assets/list-CFU0-TgN.js +4 -0
  30. package/assets/login-DFSLEO17.js +1 -0
  31. package/assets/manifest.json +246 -273
  32. package/assets/material-icon-Bx9pGQiQ.js +1 -0
  33. package/assets/media-manager-WcKe1EvD.js +6 -0
  34. package/assets/module-CT7PC6pc.js +1 -0
  35. package/assets/monaco-Bl4zdWCn.js +11 -0
  36. package/assets/nav-footer-BiSQkPt4.js +1 -0
  37. package/assets/nav-main-rTH0PfDw.js +1 -0
  38. package/assets/nav-user-Bj6cWs0a.js +1 -0
  39. package/assets/notification-Bi-PX87D.js +20 -0
  40. package/assets/pagination-render-AKJvTIRA.js +1 -0
  41. package/assets/register-BTP4uxsK.js +1 -0
  42. package/assets/ru-eW0WKkga.js +1 -0
  43. package/assets/theme-switcher-Dm8EOGzx.js +1 -0
  44. package/assets/toast-editor-DOg_gvSp.js +668 -0
  45. package/assets/user-filters-list-YUfR4XMI.js +1 -0
  46. package/assets/user-info-BgSbT02v.js +1 -0
  47. package/assets/user-menu-content-TQTXHIv6.js +1 -0
  48. package/assets/{vanilla-picker-l5rcX3cq.js → vanilla-picker-B3LgXoKV.js} +1 -7
  49. package/assets/welcome-vLwTu1R6.js +1 -0
  50. package/controllers/add.d.ts +1 -1
  51. package/controllers/add.js +20 -26
  52. package/controllers/addGroup.d.ts +1 -1
  53. package/controllers/addGroup.js +7 -15
  54. package/controllers/addUser.d.ts +1 -1
  55. package/controllers/addUser.js +10 -16
  56. package/controllers/catalog/Catalog.d.ts +1 -1
  57. package/controllers/catalog/Catalog.js +1 -10
  58. package/controllers/catalog/FrontendCatalogAdapter.d.ts +3 -3
  59. package/controllers/catalog/FrontendCatalogAdapter.js +0 -6
  60. package/controllers/catalog/FrontentCatalogAdapter.d.ts +3 -3
  61. package/controllers/catalog/FrontentCatalogAdapter.js +0 -4
  62. package/controllers/ckeditorUpload.js +2 -18
  63. package/controllers/dashboard.d.ts +1 -1
  64. package/controllers/dashboard.js +2 -5
  65. package/controllers/edit.js +24 -37
  66. package/controllers/editGroup.d.ts +1 -1
  67. package/controllers/editGroup.js +10 -21
  68. package/controllers/editUser.d.ts +1 -1
  69. package/controllers/editUser.js +11 -23
  70. package/controllers/exportData.d.ts +6 -0
  71. package/controllers/exportData.js +315 -0
  72. package/controllers/feed.d.ts +6 -0
  73. package/controllers/feed.js +73 -0
  74. package/controllers/feedback/feedbackController.d.ts +1 -1
  75. package/controllers/feedback/feedbackController.js +5 -7
  76. package/controllers/filter-fields/columns.d.ts +11 -0
  77. package/controllers/filter-fields/columns.js +106 -0
  78. package/controllers/filter-fields/filterFields.d.ts +10 -0
  79. package/controllers/filter-fields/filterFields.js +141 -0
  80. package/controllers/filter-fields/getAllUserFilters.d.ts +6 -0
  81. package/controllers/filter-fields/getAllUserFilters.js +115 -0
  82. package/controllers/filter-fields/groups.d.ts +5 -0
  83. package/controllers/filter-fields/groups.js +26 -0
  84. package/controllers/filter-fields/savedFilters.d.ts +25 -0
  85. package/controllers/filter-fields/savedFilters.js +317 -0
  86. package/controllers/getAllWidgets.d.ts +1 -1
  87. package/controllers/getAllWidgets.js +1 -9
  88. package/controllers/history-actions/HistoryController.js +6 -31
  89. package/controllers/initUser.js +6 -5
  90. package/controllers/inlineUpdate.d.ts +7 -0
  91. package/controllers/inlineUpdate.js +148 -0
  92. package/controllers/list.d.ts +1 -1
  93. package/controllers/list.js +383 -70
  94. package/controllers/listUserFilters.d.ts +5 -0
  95. package/controllers/listUserFilters.js +26 -0
  96. package/controllers/login.js +2 -2
  97. package/controllers/media-manager/mediaManagerApi.js +0 -9
  98. package/controllers/navigation/edit.js +3 -9
  99. package/controllers/notifications/NotificationController.d.ts +1 -1
  100. package/controllers/notifications/NotificationController.js +12 -36
  101. package/controllers/register.js +10 -9
  102. package/controllers/remove.js +10 -19
  103. package/controllers/timezones.d.ts +1 -1
  104. package/controllers/timezones.js +0 -6
  105. package/controllers/userApiKey.d.ts +7 -0
  106. package/controllers/userApiKey.js +36 -0
  107. package/controllers/view.js +14 -26
  108. package/controllers/welcome.d.ts +1 -1
  109. package/controllers/welcome.js +0 -4
  110. package/controllers/widgets/Action.d.ts +1 -1
  111. package/controllers/widgets/Action.js +1 -9
  112. package/controllers/widgets/Custom.d.ts +1 -1
  113. package/controllers/widgets/Custom.js +1 -9
  114. package/controllers/widgets/Info.d.ts +1 -1
  115. package/controllers/widgets/Info.js +1 -9
  116. package/controllers/widgets/filterInfo.d.ts +1 -0
  117. package/controllers/widgets/filterInfo.js +44 -0
  118. package/controllers/widgets/switch.d.ts +1 -1
  119. package/controllers/widgets/switch.js +1 -9
  120. package/controllers/widgetsDB.d.ts +1 -1
  121. package/controllers/widgetsDB.js +2 -8
  122. package/helpers/accessRightsHelper.d.ts +1 -1
  123. package/helpers/accessRightsHelper.js +5 -4
  124. package/helpers/apiKeyHelper.d.ts +5 -0
  125. package/helpers/apiKeyHelper.js +8 -0
  126. package/helpers/configHelper.js +12 -15
  127. package/helpers/controllerHelper.d.ts +14 -21
  128. package/helpers/controllerHelper.js +50 -83
  129. package/helpers/fieldsHelper.js +3 -3
  130. package/helpers/filterDatetimeHelper.d.ts +5 -0
  131. package/helpers/filterDatetimeHelper.js +84 -0
  132. package/helpers/inertiaActionsHelper.d.ts +2 -2
  133. package/helpers/inertiaActionsHelper.js +5 -4
  134. package/helpers/inertiaAddHelper.d.ts +3 -414
  135. package/helpers/inertiaAddHelper.js +39 -15
  136. package/helpers/inertiaGroupHelper.d.ts +2 -2
  137. package/helpers/inertiaGroupHelper.js +3 -3
  138. package/helpers/inertiaListHelper.d.ts +7 -3
  139. package/helpers/inertiaListHelper.js +26 -12
  140. package/helpers/inertiaMenuHelper.d.ts +1 -0
  141. package/helpers/inertiaMenuHelper.js +15 -3
  142. package/helpers/inertiaUserHelper.d.ts +3 -2
  143. package/helpers/inertiaUserHelper.js +7 -3
  144. package/helpers/menuHelper.d.ts +5 -2
  145. package/helpers/menuHelper.js +64 -12
  146. package/helpers/viteHelper.js +1 -2
  147. package/index.d.ts +6 -2
  148. package/index.js +6 -2
  149. package/interfaces/adminpanelConfig.d.ts +126 -39
  150. package/interfaces/internalModelAccess.d.ts +37 -0
  151. package/interfaces/internalModelAccess.js +1 -0
  152. package/interfaces/listQueryBuilder.d.ts +22 -0
  153. package/interfaces/listQueryBuilder.js +1 -0
  154. package/interfaces/queryCriteria.d.ts +54 -0
  155. package/interfaces/queryCriteria.js +1 -0
  156. package/interfaces/types.d.ts +1 -13
  157. package/lib/Adminizer.d.ts +4 -2
  158. package/lib/Adminizer.js +29 -10
  159. package/lib/DataAccessor.d.ts +8 -5
  160. package/lib/DataAccessor.js +89 -42
  161. package/lib/I18n.d.ts +5 -1
  162. package/lib/I18n.js +45 -19
  163. package/lib/MiddlewareManager.d.ts +8 -0
  164. package/lib/MiddlewareManager.js +53 -0
  165. package/lib/ai-assistant/OpenAiModelService.d.ts +3 -3
  166. package/lib/ai-assistant/OpenAiModelService.js +23 -24
  167. package/lib/catalog/Navigation.d.ts +1 -0
  168. package/lib/catalog/Navigation.js +12 -11
  169. package/lib/controls/jsoneditor/JsonEditor.d.ts +1 -2
  170. package/lib/controls/markdown/ToastUiEditor.d.ts +1 -2
  171. package/lib/feedback/FeedbackHandler.js +2 -2
  172. package/lib/filters/ConditionValidator.d.ts +107 -0
  173. package/lib/filters/ConditionValidator.js +483 -0
  174. package/lib/filters/CustomFilterHandler.d.ts +122 -0
  175. package/lib/filters/CustomFilterHandler.js +131 -0
  176. package/lib/filters/FilterCustomFieldHandler.d.ts +2 -0
  177. package/lib/filters/FilterCustomFieldHandler.js +2 -0
  178. package/lib/filters/FilterService.d.ts +82 -0
  179. package/lib/filters/FilterService.js +323 -0
  180. package/lib/filters/index.d.ts +6 -0
  181. package/lib/filters/index.js +4 -0
  182. package/lib/history-actions/AbstractHistoryAdapter.d.ts +5 -4
  183. package/lib/history-actions/AbstractHistoryAdapter.js +45 -30
  184. package/lib/history-actions/DefaultHistoryAdapter.d.ts +2 -0
  185. package/lib/history-actions/DefaultHistoryAdapter.js +12 -6
  186. package/lib/history-actions/HistoryHandler.js +1 -1
  187. package/lib/list-query-builder/ListQueryBuilder.d.ts +117 -0
  188. package/lib/list-query-builder/ListQueryBuilder.js +707 -0
  189. package/lib/media-manager/AbstractMediaManager.d.ts +9 -8
  190. package/lib/media-manager/DefaultMediaManager.d.ts +2 -0
  191. package/lib/media-manager/DefaultMediaManager.js +27 -15
  192. package/lib/media-manager/Items.d.ts +4 -1
  193. package/lib/media-manager/Items.js +62 -59
  194. package/lib/media-manager/MediaManagerHandler.js +1 -1
  195. package/lib/media-manager/helpers/MediaManagerHelper.d.ts +5 -1
  196. package/lib/media-manager/helpers/MediaManagerHelper.js +38 -4
  197. package/lib/model/AbstractModel.d.ts +33 -25
  198. package/lib/model/AbstractModel.js +54 -2
  199. package/lib/model/InternalModelAccessFactory.d.ts +18 -0
  200. package/lib/model/InternalModelAccessFactory.js +82 -0
  201. package/lib/model/ModelHandler.d.ts +6 -0
  202. package/lib/model/ModelHandler.js +14 -0
  203. package/lib/model/adapter/sequelize.d.ts +32 -10
  204. package/lib/model/adapter/sequelize.js +409 -62
  205. package/lib/model/adapter/typeorm.d.ts +62 -0
  206. package/lib/model/adapter/typeorm.js +777 -0
  207. package/lib/model/internalModelAccessToken.d.ts +1 -0
  208. package/lib/model/internalModelAccessToken.js +1 -0
  209. package/lib/model/internalModelRepository.d.ts +3 -0
  210. package/lib/model/internalModelRepository.js +4 -0
  211. package/lib/notifications/AbstractNotificationService.d.ts +3 -0
  212. package/lib/notifications/AbstractNotificationService.js +42 -36
  213. package/lib/notifications/GeneralNotificationService.js +37 -40
  214. package/lib/notifications/SystemNotificationService.js +33 -36
  215. package/lib/requestProcessor.js +0 -3
  216. package/lib/ui-i18n/getUiTranslations.d.ts +1 -0
  217. package/lib/ui-i18n/getUiTranslations.js +3 -0
  218. package/lib/ui-i18n/uiTranslationKeys.d.ts +6 -0
  219. package/lib/ui-i18n/uiTranslationKeys.js +268 -0
  220. package/lib/widgets/widgetHandler.d.ts +7 -2
  221. package/lib/widgets/widgetHandler.js +98 -18
  222. package/models/FilterAP.d.ts +116 -0
  223. package/models/FilterAP.js +68 -0
  224. package/models/FilterColumnAP.d.ts +32 -0
  225. package/models/FilterColumnAP.js +21 -0
  226. package/models/GroupAP.d.ts +1 -1
  227. package/models/HistoryActionsAP.d.ts +3 -3
  228. package/models/MediaManagerMetaAP.d.ts +1 -1
  229. package/models/NavigationAP.d.ts +1 -1
  230. package/models/UserAP.d.ts +6 -1
  231. package/models/UserAP.js +4 -0
  232. package/package.json +39 -32
  233. package/policies/authPolicies.d.ts +12 -0
  234. package/policies/authPolicies.js +104 -0
  235. package/policies/permissionResolvers.d.ts +12 -0
  236. package/policies/permissionResolvers.js +52 -0
  237. package/services/FeedService.d.ts +70 -0
  238. package/services/FeedService.js +334 -0
  239. package/system/Router.d.ts +1 -0
  240. package/system/Router.js +136 -59
  241. package/system/bindAccessRights.js +11 -30
  242. package/system/bindAuthorization.js +18 -18
  243. package/system/bindCors.js +16 -1
  244. package/system/bindCustomFilterHandlers.d.ts +2 -0
  245. package/system/bindCustomFilterHandlers.js +4 -0
  246. package/system/bindInertia.js +18 -61
  247. package/system/bindReqFunctions.js +10 -2
  248. package/system/bindTranslations.js +7 -3
  249. package/system/buildInternalModelAccess.d.ts +4 -0
  250. package/system/buildInternalModelAccess.js +59 -0
  251. package/system/defaults.js +18 -14
  252. package/system/routePolicies.d.ts +2 -0
  253. package/system/routePolicies.js +29 -0
  254. package/translations/en.json +151 -39
  255. package/translations/ru.json +211 -64
  256. package/ui/components/handsontable.js +13 -27
  257. package/ui/components/ui/dialog-stack.js +1 -3
  258. package/ui/components/ui/popover.js +1 -1
  259. package/ui/components/ui/sidebar.js +1 -1
  260. package/assets/RelationDialogStack-D9UVEF9s.js +0 -407
  261. package/assets/RelationDialogStack-DV8OITPR.css +0 -4
  262. package/assets/add-BlvzWQKF.js +0 -1
  263. package/assets/add-group-1Uoq9NWe.js +0 -1
  264. package/assets/add-group-form-B01gwDGu.js +0 -1
  265. package/assets/add-user-BQCAsaRy.js +0 -1
  266. package/assets/add-user-form-Cgwc9JkI.js +0 -1
  267. package/assets/app-DaOMy1nl.css +0 -72
  268. package/assets/app-layout-CAm60qoE.js +0 -1
  269. package/assets/app-shell-QWCtn9ye.js +0 -1
  270. package/assets/app-sidebar-CfaYbnSy.js +0 -1
  271. package/assets/app-sidebar-header-BsPFDhZ8.js +0 -1
  272. package/assets/breadcrumbs-CZZirDj8.js +0 -1
  273. package/assets/catalog-k5myk7fk.js +0 -6
  274. package/assets/dashboard-BkL9ybBw.js +0 -3
  275. package/assets/date-picker-B5AZ_7Q9.js +0 -1
  276. package/assets/del-modal-BoyXZloG.js +0 -1
  277. package/assets/dynamic-controls-k61T9W2P.js +0 -1
  278. package/assets/form-1WGY0YgG.js +0 -1
  279. package/assets/form-DJapG3qF.js +0 -1
  280. package/assets/geo-json-DsMUHo0l.js +0 -4
  281. package/assets/handsontable-PxYdjN8c.js +0 -298
  282. package/assets/history-CluyR-yJ.js +0 -1
  283. package/assets/home-Dug9zBzM.js +0 -2
  284. package/assets/icon-DfDLMG5f.js +0 -1
  285. package/assets/init-user-CF80MEnT.js +0 -1
  286. package/assets/input-error-DuuqFgZa.js +0 -1
  287. package/assets/jsoneditor-De1FvcYu.js +0 -3882
  288. package/assets/list-CuexshNt.js +0 -1
  289. package/assets/list-table-BsEWjTKY.js +0 -22
  290. package/assets/login-eDemYzN-.js +0 -1
  291. package/assets/material-icon-DMT8KdmE.js +0 -1
  292. package/assets/media-manager-DfYa_mT_.js +0 -14
  293. package/assets/module-Cd4Tm6uq.js +0 -1
  294. package/assets/monaco-BhBYtg3d.js +0 -11
  295. package/assets/nav-footer-D3olYxtz.js +0 -1
  296. package/assets/nav-main-BctqyzeV.js +0 -1
  297. package/assets/nav-user-Bn1Lt-BZ.js +0 -1
  298. package/assets/notification-D64qvni-.js +0 -27
  299. package/assets/pagination-render-DxD78ahJ.js +0 -1
  300. package/assets/register-349PwYbe.js +0 -1
  301. package/assets/theme-switcher-DfExsHW2.js +0 -1
  302. package/assets/toast-editor-ESK5iWPx.js +0 -845
  303. package/assets/user-info-DtBK5Kh8.js +0 -1
  304. package/assets/user-menu-content-0XYOCMaA.js +0 -1
  305. package/assets/welcome-PDp-gUei.js +0 -1
  306. package/controllers/form.d.ts +0 -1
  307. package/controllers/form.js +0 -86
  308. package/helpers/formHelper.d.ts +0 -6
  309. package/helpers/formHelper.js +0 -38
  310. package/helpers/inertiaFromHelper.d.ts +0 -11
  311. package/helpers/inertiaFromHelper.js +0 -98
  312. package/helpers/runtimeMode.d.ts +0 -1
  313. package/helpers/runtimeMode.js +0 -3
  314. package/lib/PolicyManager.d.ts +0 -8
  315. package/lib/PolicyManager.js +0 -53
  316. package/lib/datatable/NodeTable.d.ts +0 -49
  317. package/lib/datatable/NodeTable.js +0 -207
  318. package/lib/model/adapter/waterline.d.ts +0 -32
  319. package/lib/model/adapter/waterline.js +0 -180
  320. package/migrations/index.d.ts +0 -3
  321. package/migrations/index.js +0 -4
  322. package/migrations/knex/20250106000001_adminizer_init.d.ts +0 -8
  323. package/migrations/knex/20250106000001_adminizer_init.js +0 -164
  324. package/migrations/umzug/0001.d.ts +0 -7
  325. package/migrations/umzug/0001.js +0 -121
  326. package/migrations/umzugExports.d.ts +0 -2
  327. package/migrations/umzugExports.js +0 -9
  328. package/system/bindForms.d.ts +0 -2
  329. package/system/bindForms.js +0 -20
  330. /package/{policies → middlewares}/addMessages.d.ts +0 -0
  331. /package/{policies → middlewares}/addMessages.js +0 -0
  332. /package/{policies → middlewares}/checkAuth.d.ts +0 -0
  333. /package/{policies → middlewares}/checkAuth.js +0 -0
  334. /package/{policies → middlewares}/setLocale.d.ts +0 -0
  335. /package/{policies → middlewares}/setLocale.js +0 -0
@@ -0,0 +1,22 @@
1
+ import { FilterCondition } from "../models/FilterAP";
2
+ /**
3
+ * Input format for list/feed/export query builder.
4
+ * This is not the internal model query language.
5
+ */
6
+ export interface ListQueryBuilderParams {
7
+ page: number;
8
+ limit: number;
9
+ sort?: string;
10
+ sortDirection?: "ASC" | "DESC";
11
+ filters?: FilterCondition[];
12
+ globalSearch?: string;
13
+ fields?: string[];
14
+ }
15
+ export interface ListQueryBuilderResult<T = any> {
16
+ data: T[];
17
+ total: number;
18
+ filtered: number;
19
+ page: number;
20
+ limit: number;
21
+ pages: number;
22
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,54 @@
1
+ export type CriteriaPrimitive = string | number | boolean | Date | null;
2
+ export type CriteriaSortDirection = "ASC" | "DESC" | "asc" | "desc";
3
+ export type CriteriaSelect = string[] | Record<string, boolean>;
4
+ export type CriteriaSort = string | Record<string, CriteriaSortDirection>;
5
+ export interface CriteriaOperatorValue<TValue = unknown> {
6
+ eq?: TValue;
7
+ ne?: TValue;
8
+ gt?: TValue;
9
+ gte?: TValue;
10
+ lt?: TValue;
11
+ lte?: TValue;
12
+ contains?: TValue;
13
+ startsWith?: TValue;
14
+ endsWith?: TValue;
15
+ in?: TValue[];
16
+ notIn?: TValue[];
17
+ between?: [TValue, TValue];
18
+ isNull?: boolean;
19
+ isNotNull?: boolean;
20
+ regex?: unknown;
21
+ arrayContains?: unknown;
22
+ arrayOverlap?: unknown[];
23
+ jsonContains?: unknown;
24
+ }
25
+ export type CriteriaFieldInput<TValue = unknown> = TValue extends Array<infer Item> ? Item | Item[] | TValue : TValue;
26
+ export type CriteriaFieldValue<TValue = unknown> = null | CriteriaFieldInput<TValue> | CriteriaOperatorValue<CriteriaFieldInput<TValue>>;
27
+ export type CriteriaKnownFields<TModel = any> = {
28
+ [Field in Extract<keyof TModel, string>]?: CriteriaFieldValue<TModel[Field]>;
29
+ };
30
+ export interface CriteriaWhereLogic<TModel = any> {
31
+ and?: CriteriaWhere<TModel>[];
32
+ or?: CriteriaWhere<TModel>[];
33
+ not?: CriteriaWhere<TModel>;
34
+ }
35
+ export type IsAny<T> = 0 extends 1 & T ? true : false;
36
+ export type CriteriaWhere<TModel = any> = IsAny<TModel> extends true ? CriteriaWhereLogic<TModel> & {
37
+ [field: string]: unknown;
38
+ } : CriteriaWhereLogic<TModel> & CriteriaKnownFields<TModel>;
39
+ type PopulatableElement<T> = NonNullable<T> extends Array<infer Item> ? Item : NonNullable<T>;
40
+ type PopulatableKeys<TModel> = {
41
+ [K in keyof TModel]-?: NonNullable<TModel[K]> extends string | number | boolean | Date ? never : K;
42
+ }[keyof TModel];
43
+ export type CriteriaPopulate<TModel = any> = IsAny<TModel> extends true ? Record<string, true | QueryCriteria<any>> : {
44
+ [K in Extract<PopulatableKeys<TModel>, string>]?: true | QueryCriteria<PopulatableElement<TModel[K]>>;
45
+ };
46
+ export interface QueryCriteria<TModel = any> {
47
+ where?: CriteriaWhere<TModel>;
48
+ select?: CriteriaSelect;
49
+ populate?: CriteriaPopulate<TModel>;
50
+ sort?: CriteriaSort;
51
+ limit?: number;
52
+ skip?: number;
53
+ }
54
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -1,12 +1,10 @@
1
1
  import { AbstractModel } from "../lib/model/AbstractModel";
2
2
  import { ModelConfig } from "./adminpanelConfig";
3
- export type EntityType = "form" | "model";
4
- export interface Entity {
3
+ export interface ModelResource {
5
4
  name: string;
6
5
  config?: ModelConfig;
7
6
  model?: AbstractModel<any>;
8
7
  uri: string;
9
- type: EntityType;
10
8
  }
11
9
  export interface AccessRightsToken {
12
10
  name: string;
@@ -72,13 +70,3 @@ export interface AiAssistantModelInfo {
72
70
  name: string;
73
71
  description?: string;
74
72
  }
75
- export type Migration = {
76
- name: string;
77
- timestamp: number;
78
- up: (args: {
79
- context: any;
80
- }) => Promise<unknown> | unknown;
81
- down: (args: {
82
- context: any;
83
- }) => Promise<unknown> | unknown;
84
- };
@@ -2,7 +2,7 @@ import express, { Express } from "express";
2
2
  import winston from "winston";
3
3
  import EventEmitter from 'events';
4
4
  import { AdminpanelConfig } from "../interfaces/adminpanelConfig";
5
- import PolicyManager from "./PolicyManager";
5
+ import MiddlewareManager from "./MiddlewareManager";
6
6
  import Router from "./../system/Router";
7
7
  import { ModelHandler } from "./model/ModelHandler";
8
8
  import { WidgetHandler } from "./widgets/widgetHandler";
@@ -20,6 +20,7 @@ import { INotification } from "../interfaces/types";
20
20
  import { MediaManagerHandler } from "./media-manager/MediaManagerHandler";
21
21
  import { StorageServices } from "./catalog/Navigation";
22
22
  import { HistoryHandler } from "./history-actions/HistoryHandler";
23
+ import { CustomFilterHandler } from "./filters/CustomFilterHandler";
23
24
  import { FeedbackHandler } from "./feedback/FeedbackHandler";
24
25
  export declare class Adminizer {
25
26
  /**
@@ -31,7 +32,7 @@ export declare class Adminizer {
31
32
  config: AdminpanelConfig;
32
33
  private readonly _emitter;
33
34
  ormAdapters: AbstractAdapter[];
34
- policyManager: PolicyManager;
35
+ middlewareManager: MiddlewareManager;
35
36
  accessRightsHelper: AccessRightsHelper;
36
37
  configHelper: ConfigHelper;
37
38
  menuHelper: MenuHelper;
@@ -41,6 +42,7 @@ export declare class Adminizer {
41
42
  aiAssistantHandler?: AiAssistantHandler;
42
43
  modelHandler: ModelHandler;
43
44
  widgetHandler: WidgetHandler;
45
+ customFilterHandler: CustomFilterHandler;
44
46
  vite: ViteDevServer;
45
47
  controlsHandler: ControlsHandler;
46
48
  catalogHandler: CatalogHandler;
package/lib/Adminizer.js CHANGED
@@ -2,8 +2,9 @@ import express from "express";
2
2
  import cookieParser from 'cookie-parser';
3
3
  import * as path from "path";
4
4
  import winston from "winston";
5
+ import chalk from "chalk";
5
6
  import EventEmitter from 'events';
6
- import PolicyManager from "./PolicyManager.js";
7
+ import MiddlewareManager from "./MiddlewareManager.js";
7
8
  import Router from "./../system/Router.js";
8
9
  import bindAssets from "../system/bindAssets.js";
9
10
  import bindDev from "../system/bindDev.js";
@@ -13,7 +14,6 @@ import bindMediaManager from "../system/bindMediaManager.js";
13
14
  import bindAccessRights from "../system/bindAccessRights.js";
14
15
  import bindAuthorization from "../system/bindAuthorization.js";
15
16
  import bindModels from "../system/bindModels.js";
16
- import bindForms from "../system/bindForms.js";
17
17
  import bindTranslations from "../system/bindTranslations.js";
18
18
  import { ModelHandler } from "./model/ModelHandler.js";
19
19
  import { WidgetHandler } from "./widgets/widgetHandler.js";
@@ -34,8 +34,9 @@ import { bindAiAssistant } from "../system/bindAiAssistant.js";
34
34
  import { MediaManagerHandler } from "./media-manager/MediaManagerHandler.js";
35
35
  import { bindCors } from "../system/bindCors.js";
36
36
  import bindHistory from "../system/bindHistory.js";
37
- import { isAdminizerViteDevMode } from "../helpers/runtimeMode.js";
37
+ import bindCustomFilterHandlers from "../system/bindCustomFilterHandlers.js";
38
38
  import { FeedbackHandler } from "./feedback/FeedbackHandler.js";
39
+ import { buildInternalModelAccess } from "../system/buildInternalModelAccess.js";
39
40
  export class Adminizer {
40
41
  // Preconfigures
41
42
  /**
@@ -48,7 +49,7 @@ export class Adminizer {
48
49
  config;
49
50
  _emitter;
50
51
  ormAdapters;
51
- policyManager;
52
+ middlewareManager;
52
53
  accessRightsHelper;
53
54
  configHelper;
54
55
  menuHelper;
@@ -58,6 +59,7 @@ export class Adminizer {
58
59
  aiAssistantHandler;
59
60
  modelHandler;
60
61
  widgetHandler;
62
+ customFilterHandler;
61
63
  vite;
62
64
  controlsHandler;
63
65
  catalogHandler;
@@ -73,7 +75,23 @@ export class Adminizer {
73
75
  return `[${timestamp}] ${level.toUpperCase()}: ${message} ${metaString}`;
74
76
  })),
75
77
  transports: [
76
- new winston.transports.Console(),
78
+ new winston.transports.Console({
79
+ format: winston.format.combine(winston.format.timestamp(), winston.format.printf(({ timestamp, level, message, ...meta }) => {
80
+ const metaString = Object.keys(meta).length ? JSON.stringify(meta) : "";
81
+ const colors = {
82
+ error: chalk.red,
83
+ warn: chalk.yellow,
84
+ info: chalk.green,
85
+ http: chalk.magenta,
86
+ verbose: chalk.cyan,
87
+ debug: chalk.blue,
88
+ silly: chalk.gray,
89
+ };
90
+ const colorize = colors[level] ?? chalk.white;
91
+ const line = `[${timestamp}] ${level.toUpperCase()}: ${message} ${metaString}`;
92
+ return colorize(line);
93
+ }))
94
+ }),
77
95
  new winston.transports.File({ filename: "logs/app.log" }),
78
96
  ],
79
97
  });
@@ -170,17 +188,18 @@ export class Adminizer {
170
188
  // set cookie parser
171
189
  this.app.use(cookieParser());
172
190
  // Set vite middleware
173
- const isViteDev = isAdminizerViteDevMode();
191
+ const isViteDev = process.env.ADMINIZER_ENV === "dev";
174
192
  if (isViteDev)
175
193
  await this.viteMiddleware();
176
194
  this.emitter.emit('adminizer:init');
177
195
  this.modelHandler = new ModelHandler();
178
- // TODO: 'hot reload' unbind models & unbind forms
196
+ // TODO: 'hot reload' unbind models
179
197
  await bindModels(this);
180
- await bindForms(this);
198
+ this.modelHandler.configureInternalAccess(buildInternalModelAccess(this.config, this.modelHandler));
199
+ bindCustomFilterHandlers(this);
181
200
  this.config.rootPath = path.resolve(import.meta.dirname + "/..");
182
- this.policyManager = new PolicyManager(this);
183
- await this.policyManager.loadPolicies();
201
+ this.middlewareManager = new MiddlewareManager(this);
202
+ await this.middlewareManager.loadMiddlewares();
184
203
  // TODO: 'hot reload' problem with deleting access right tokens
185
204
  this.accessRightsHelper = new AccessRightsHelper(this);
186
205
  // Helpers go to construtor
@@ -1,26 +1,29 @@
1
1
  /**
2
2
  * The class manages the interaction between the user and the database entry, taking into account user permissions and the main config file settings.
3
3
  */
4
- import { Entity } from "../interfaces/types";
4
+ import { ModelResource } from "../interfaces/types";
5
5
  import { ActionType } from "../interfaces/adminpanelConfig";
6
6
  import { Fields } from "../helpers/fieldsHelper";
7
7
  import { Adminizer } from "./Adminizer";
8
8
  import { UserAP } from "../models/UserAP";
9
+ import { QueryCriteria } from "../interfaces/queryCriteria";
9
10
  export declare class DataAccessor {
10
11
  readonly adminizer: Adminizer;
11
12
  user: UserAP;
12
- entity: Entity;
13
+ modelResource: ModelResource;
13
14
  action: ActionType;
14
15
  private fields;
15
16
  private actionVerb;
16
- constructor(adminizer: Adminizer, user: UserAP, entity: Entity, action: ActionType);
17
+ constructor(adminizer: Adminizer, user: UserAP, modelResource: ModelResource, action: ActionType);
17
18
  private getModelConfig;
19
+ private accessRelationModel;
18
20
  /**
19
- * Retrieves the fields for the given entity based on action type,
21
+ * Retrieves the fields for the given ModelResource based on action type,
20
22
  * taking into account access rights and configuration settings.
21
23
  * @returns {Fields} An object with configured fields and their properties.
22
24
  */
23
25
  getFieldsConfig(): Fields;
26
+ private orderFieldsByConfig;
24
27
  private getAssociatedFieldsConfig;
25
28
  private checkFieldAccess;
26
29
  /**
@@ -31,6 +34,6 @@ export declare class DataAccessor {
31
34
  private filterAssociatedRecord;
32
35
  /** Process for an array of records */
33
36
  processMany<T>(records: T[]): Partial<T>[];
34
- sanitizeUserRelationAccess<T>(criteria: T): Promise<Partial<T>>;
37
+ sanitizeUserRelationAccess(criteria?: QueryCriteria): Promise<QueryCriteria>;
35
38
  setUserRelationAccess<T>(record: T): Promise<Partial<T>>;
36
39
  }
@@ -1,17 +1,48 @@
1
1
  import { ControllerHelper } from "../helpers/controllerHelper.js";
2
2
  import { Adminizer } from "./Adminizer.js";
3
3
  import { isObject } from "../helpers/JsUtils.js";
4
+ const MODEL_TOKEN_SUFFIX = "model";
5
+ function hasCriteriaContent(criteria) {
6
+ return Object.keys(criteria).length > 0 || Object.getOwnPropertySymbols(criteria).length > 0;
7
+ }
8
+ function isPlainCriteriaWhere(where) {
9
+ if (!isObject(where)) {
10
+ return false;
11
+ }
12
+ const prototype = Object.getPrototypeOf(where);
13
+ return prototype === Object.prototype || prototype === null;
14
+ }
15
+ function mergeCriteriaWhere(criteriaWhere, sanitizedCriteria) {
16
+ if (!hasCriteriaContent(sanitizedCriteria)) {
17
+ return criteriaWhere;
18
+ }
19
+ if (!criteriaWhere) {
20
+ return sanitizedCriteria;
21
+ }
22
+ if (isPlainCriteriaWhere(criteriaWhere)) {
23
+ return {
24
+ ...criteriaWhere,
25
+ ...sanitizedCriteria
26
+ };
27
+ }
28
+ return {
29
+ and: [
30
+ criteriaWhere,
31
+ sanitizedCriteria
32
+ ]
33
+ };
34
+ }
4
35
  export class DataAccessor {
5
36
  adminizer;
6
37
  user;
7
- entity;
38
+ modelResource;
8
39
  action;
9
40
  fields = null;
10
41
  actionVerb;
11
- constructor(adminizer, user, entity, action) {
42
+ constructor(adminizer, user, modelResource, action) {
12
43
  this.adminizer = adminizer;
13
44
  this.user = user;
14
- this.entity = entity;
45
+ this.modelResource = modelResource;
15
46
  this.action = action;
16
47
  this.actionVerb = getTokenAction(this.action);
17
48
  }
@@ -21,8 +52,11 @@ export class DataAccessor {
21
52
  const config = entry ? entry[1] : undefined;
22
53
  return isObject(config) ? config : undefined;
23
54
  }
55
+ accessRelationModel(modelName) {
56
+ return this.adminizer.modelHandler.internal("data-accessor").get(modelName);
57
+ }
24
58
  /**
25
- * Retrieves the fields for the given entity based on action type,
59
+ * Retrieves the fields for the given ModelResource based on action type,
26
60
  * taking into account access rights and configuration settings.
27
61
  * @returns {Fields} An object with configured fields and their properties.
28
62
  */
@@ -30,16 +64,16 @@ export class DataAccessor {
30
64
  if (this.fields !== null) {
31
65
  return this.fields;
32
66
  }
33
- if (!this.entity.model || !this.entity.model.attributes) {
67
+ if (!this.modelResource.model || !this.modelResource.model.attributes) {
34
68
  return {};
35
69
  }
36
70
  // get action and field configs
37
- const actionConfig = ControllerHelper.findActionConfig(this.entity, this.action);
38
- const fieldsConfig = this.entity.config?.fields || {};
39
- const modelAttributes = this.entity.model.attributes;
40
- const tokenId = `${this.actionVerb}-${this.entity.model.modelname}-${this.entity.type}`;
71
+ const actionConfig = ControllerHelper.findActionConfig(this.modelResource, this.action);
72
+ const fieldsConfig = this.modelResource.config?.fields || {};
73
+ const modelAttributes = this.modelResource.model.attributes;
74
+ const tokenId = `${this.actionVerb}-${this.modelResource.model.modelname}-${MODEL_TOKEN_SUFFIX}`;
41
75
  if (!this.adminizer.accessRightsHelper.hasPermission(tokenId, this.user)) {
42
- Adminizer.log.debug(`getFieldsConfig > No access rights to ${this.actionVerb} ${this.entity.type}: ${this.entity.model.modelname}`);
76
+ Adminizer.log.debug(`getFieldsConfig > No access rights to ${this.actionVerb} model: ${this.modelResource.model.modelname}`);
43
77
  return undefined;
44
78
  }
45
79
  const result = {};
@@ -48,7 +82,7 @@ export class DataAccessor {
48
82
  if (modelAttributes[key].primaryKeyForAssociation === true) {
49
83
  return undefined;
50
84
  }
51
- // Checks for short type in Waterline: fieldName: 'string'
85
+ // Supports shorthand model attributes: fieldName: 'string'
52
86
  if (typeof modelField === "string") {
53
87
  modelField = { type: modelField };
54
88
  }
@@ -82,9 +116,9 @@ export class DataAccessor {
82
116
  let populatedModelFieldsConfig = {};
83
117
  if (modelField.type === "association" || modelField.type === "association-many") {
84
118
  const modelName = modelField.model || modelField.collection;
85
- const tokenId = `read-${modelName}-${this.entity.type}`;
119
+ const tokenId = `read-${modelName}-${MODEL_TOKEN_SUFFIX}`;
86
120
  if (!this.adminizer.accessRightsHelper.hasPermission(tokenId, this.user)) {
87
- Adminizer.log.silly(`No access rights to ${this.entity.type}: ${this.entity.model.modelname}`);
121
+ Adminizer.log.silly(`No access rights to model: ${this.modelResource.model.modelname}`);
88
122
  return undefined;
89
123
  }
90
124
  if (modelName) {
@@ -106,15 +140,31 @@ export class DataAccessor {
106
140
  }
107
141
  // Set required and type attributes
108
142
  fldConfig.required = Boolean(fldConfig.required ?? modelField.required);
109
- // Default type for field. Could be fetched form config file or file model if not defined in config file.
143
+ // Default type for field. Could be fetched from config file or model if not defined in config file.
110
144
  fldConfig.type = (fldConfig.type || modelField.type).toLowerCase();
111
145
  // Normalize final configuration (fldConfig is always an object here, normalize never returns undefined)
112
146
  fldConfig = this.adminizer.configHelper.normalizeFieldConfig(this.adminizer, fldConfig, key, modelField);
113
147
  // Add new field to result set
114
148
  result[key] = { config: fldConfig, model: modelField, populated: populatedModelFieldsConfig, modelConfig: associatedModelConfig };
115
149
  });
116
- this.fields = result;
117
- return result;
150
+ this.fields = this.orderFieldsByConfig(result, fieldsConfig, actionConfig.fields);
151
+ return this.fields;
152
+ }
153
+ orderFieldsByConfig(fields, globalFieldsConfig = {}, actionFieldsConfig = {}) {
154
+ const orderedFields = {};
155
+ const appendField = (fieldName) => {
156
+ if (!Object.prototype.hasOwnProperty.call(fields, fieldName)) {
157
+ return;
158
+ }
159
+ if (Object.prototype.hasOwnProperty.call(orderedFields, fieldName)) {
160
+ return;
161
+ }
162
+ orderedFields[fieldName] = fields[fieldName];
163
+ };
164
+ Object.keys(actionFieldsConfig || {}).forEach(appendField);
165
+ Object.keys(globalFieldsConfig || {}).forEach(appendField);
166
+ Object.keys(fields).forEach(appendField);
167
+ return orderedFields;
118
168
  }
119
169
  getAssociatedFieldsConfig(modelName) {
120
170
  const model = this.adminizer.modelHandler.model.get(modelName);
@@ -123,9 +173,9 @@ export class DataAccessor {
123
173
  return undefined;
124
174
  }
125
175
  // Check if user has access to the associated model
126
- const tokenId = `read-${modelName}-${this.entity.type}`;
176
+ const tokenId = `read-${modelName}-${MODEL_TOKEN_SUFFIX}`;
127
177
  if (!this.adminizer.accessRightsHelper.hasPermission(tokenId, this.user)) {
128
- Adminizer.log.debug(`getAssociatedFieldsConfig > No access rights to ${this.actionVerb} ${this.entity.type}: ${modelName}`);
178
+ Adminizer.log.debug(`getAssociatedFieldsConfig > No access rights to ${this.actionVerb} model: ${modelName}`);
129
179
  return undefined;
130
180
  }
131
181
  const associatedFields = {};
@@ -181,7 +231,7 @@ export class DataAccessor {
181
231
  return associatedFields;
182
232
  }
183
233
  checkFieldAccess(key, fieldConfig) {
184
- if (this.entity.model.primaryKey === key) {
234
+ if (this.modelResource.model.primaryKey === key) {
185
235
  return true;
186
236
  }
187
237
  if (this.user.isAdministrator) {
@@ -208,7 +258,7 @@ export class DataAccessor {
208
258
  }
209
259
  const filteredRecord = {};
210
260
  // Set the primary key value
211
- const primaryKey = (this.entity.model.primaryKey ?? 'id');
261
+ const primaryKey = (this.modelResource.model.primaryKey ?? 'id');
212
262
  filteredRecord[primaryKey] = record[primaryKey];
213
263
  for (const fieldKey in record) {
214
264
  const fieldConfig = this.fields[fieldKey];
@@ -278,19 +328,21 @@ export class DataAccessor {
278
328
  processMany(records) {
279
329
  return records.map(record => this.process(record));
280
330
  }
281
- async sanitizeUserRelationAccess(criteria) {
331
+ async sanitizeUserRelationAccess(criteria = {}) {
282
332
  let sanitizedCriteria = {};
283
333
  // Retrieve model configuration from adminpanel config
284
- const modelName = this.entity.model.modelname;
285
- const modelConfig = this.entity.config;
334
+ const modelName = this.modelResource.model.modelname;
335
+ const modelConfig = this.modelResource.config;
286
336
  // Check if the model has `userAccessRelation` configured
287
337
  if (!this.user.isAdministrator && modelConfig && modelConfig.userAccessRelation) {
338
+ Adminizer.log.warn(`[userAccessRelation] Model "${modelName}" has userAccessRelation="${modelConfig.userAccessRelation}". ` +
339
+ `User "${this.user.login}" (id: ${this.user.id}) will only see records they own.`);
288
340
  // Get access field from userAccessRelation
289
341
  const userAccessRelation = modelConfig.userAccessRelation;
290
342
  if (typeof userAccessRelation === 'string') {
291
343
  let accessField = userAccessRelation;
292
344
  // Check if the relation points to `UserAP` or `GroupAP` in the model's attributes
293
- const modelAttributes = this.entity.model.attributes;
345
+ const modelAttributes = this.modelResource.model.attributes;
294
346
  const relation = modelAttributes[accessField];
295
347
  if (!relation || !['userap', 'groupap'].includes(relation.model.toLowerCase() || relation.collection.toLowerCase())) {
296
348
  throw new Error(`Invalid userAccessRelation configuration for model ${modelName}`);
@@ -307,7 +359,7 @@ export class DataAccessor {
307
359
  sanitizedCriteria = { ...sanitizedCriteria, [accessField]: { in: userGroups } };
308
360
  }
309
361
  }
310
- /** Warning: code was not tested, need further processing in waterline (intersects does not support in waterline) */
362
+ /** Warning: collection relation access is not supported and needs adapter-level processing */
311
363
  if (relation.collection) {
312
364
  Adminizer.log.warn(`Collection relation is not supported and was not tested. You may have an error here: ${JSON.stringify(relation, null, 2)}`);
313
365
  if (relation.collection.toLowerCase() === 'userap') {
@@ -325,7 +377,7 @@ export class DataAccessor {
325
377
  // If userAccessRelation is an object
326
378
  const { field, via } = userAccessRelation;
327
379
  // Get attributes of the current model and validate the intermediate relation
328
- const modelAttributes = this.entity.model.attributes;
380
+ const modelAttributes = this.modelResource.model.attributes;
329
381
  const intermediateRelation = modelAttributes[field];
330
382
  if (!intermediateRelation || !intermediateRelation.model) {
331
383
  throw new Error(`Invalid intermediate relation configuration for field "${field}" in model ${modelName}`);
@@ -343,28 +395,23 @@ export class DataAccessor {
343
395
  `Currently, only relations to "userap" are supported`);
344
396
  }
345
397
  // Fetch all intermediate records associated with the user
346
- const intermediateRecords = await intermediateModel["_find"]({ [via]: this.user.id });
398
+ const intermediateRecords = await this.accessRelationModel(intermediateRelation.model).find({ where: { [via]: this.user.id } });
347
399
  const intermediateIds = (intermediateRecords || []).map((r) => r.id);
348
400
  // Filter main model by all matching intermediate record IDs
349
401
  sanitizedCriteria = { ...sanitizedCriteria, [field]: { in: intermediateIds } };
350
402
  }
351
403
  }
352
- // TODO fix types when deleting waterline (temporary decision here)
353
- let _criteria = criteria;
354
- if (_criteria.where) {
355
- _criteria.where = { ..._criteria.where, ...sanitizedCriteria };
356
- }
357
- else {
358
- _criteria = { ..._criteria, ...sanitizedCriteria };
359
- }
360
- return _criteria;
404
+ return {
405
+ ...criteria,
406
+ where: mergeCriteriaWhere(criteria.where, sanitizedCriteria)
407
+ };
361
408
  }
362
409
  async setUserRelationAccess(record) {
363
410
  let updatedRecord = { ...record };
364
411
  // Check if model has `userAccessRelation` configured
365
- if (this.entity.config && this.entity.config.userAccessRelation) {
412
+ if (this.modelResource.config && this.modelResource.config.userAccessRelation) {
366
413
  // Get access field from userAccessRelation
367
- const userAccessRelation = this.entity.config.userAccessRelation;
414
+ const userAccessRelation = this.modelResource.config.userAccessRelation;
368
415
  if (typeof userAccessRelation === 'string') {
369
416
  let accessField = userAccessRelation;
370
417
  // only admin can set user access relation manually
@@ -372,7 +419,7 @@ export class DataAccessor {
372
419
  delete updatedRecord[accessField];
373
420
  }
374
421
  // Check if the relation points to `UserAP` or `GroupAP` in the model's attributes
375
- const modelAttributes = this.entity.model.attributes;
422
+ const modelAttributes = this.modelResource.model.attributes;
376
423
  const relation = modelAttributes[accessField];
377
424
  if (relation && ['userap', 'groupap'].includes(relation.model.toLowerCase())) {
378
425
  if (relation.model.toLowerCase() === 'userap') {
@@ -399,10 +446,10 @@ export class DataAccessor {
399
446
  if (!via) {
400
447
  throw new Error(`Invalid userAccessRelation configuration: "via" is required`);
401
448
  }
402
- const modelAttributes = this.entity.model.attributes;
449
+ const modelAttributes = this.modelResource.model.attributes;
403
450
  const intermediateRelation = modelAttributes[field];
404
451
  if (!intermediateRelation || !intermediateRelation.model) {
405
- throw new Error(`Invalid intermediate relation configuration for field "${field}" in model ${this.entity.model.modelname}`);
452
+ throw new Error(`Invalid intermediate relation configuration for field "${field}" in model ${this.modelResource.model.modelname}`);
406
453
  }
407
454
  const intermediateModel = this.adminizer.modelHandler.model.get(intermediateRelation.model.toLowerCase());
408
455
  if (!intermediateModel) {
@@ -419,7 +466,7 @@ export class DataAccessor {
419
466
  const chosenId = typeof updatedRecord[field] === 'object'
420
467
  ? updatedRecord[field][intermediatePk]
421
468
  : updatedRecord[field];
422
- const record = await intermediateModel["_findOne"]({ [intermediatePk]: chosenId, [via]: this.user.id });
469
+ const record = await this.accessRelationModel(intermediateRelation.model).findOne({ where: { [intermediatePk]: chosenId, [via]: this.user.id } });
423
470
  if (!record) {
424
471
  throw new Error(`Access denied: "${field}" does not belong to the current user`);
425
472
  }
package/lib/I18n.d.ts CHANGED
@@ -5,6 +5,7 @@ type I18nOptions = {
5
5
  locales?: string[];
6
6
  defaultLocale?: string;
7
7
  directory?: string;
8
+ missingDirectory?: string;
8
9
  extension?: string;
9
10
  cookieName?: string;
10
11
  sessionVarName?: string;
@@ -19,7 +20,10 @@ export declare class I18n {
19
20
  private devMode;
20
21
  private static locales;
21
22
  private defaultLocale;
23
+ private defaultDirectory;
22
24
  private directory;
25
+ private customDirectory?;
26
+ private missingDirectory?;
23
27
  private extension;
24
28
  private cookieName;
25
29
  private sessionVarName;
@@ -37,8 +41,8 @@ export declare class I18n {
37
41
  isPreferredLocale(): boolean;
38
42
  translate(locale: string, singular: string, plural?: string): any;
39
43
  private readFile;
40
- private writeFile;
41
44
  private locateFile;
45
+ private writeMissingTranslation;
42
46
  static appendLocale(locale: string, data: any): void;
43
47
  static getLocales(): Record<string, LocaleData>;
44
48
  }