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,334 @@
1
+ import { Adminizer } from "../lib/Adminizer.js";
2
+ import { DataAccessor } from "../lib/DataAccessor.js";
3
+ import { ListQueryBuilder } from "../lib/list-query-builder/ListQueryBuilder.js";
4
+ import { ControllerHelper } from "../helpers/controllerHelper.js";
5
+ import { convertDatetimeConditions } from "../helpers/filterDatetimeHelper.js";
6
+ /**
7
+ * FeedService — generates public feed exports (Atom XML, JSON) by apiKey
8
+ * No authentication required — access is granted via apiKey alone
9
+ */
10
+ export class FeedService {
11
+ adminizer;
12
+ constructor(adminizer) {
13
+ this.adminizer = adminizer;
14
+ }
15
+ /**
16
+ * Find filter by apiKey and verify it's enabled
17
+ */
18
+ async findFilterByApiKey(apiKey) {
19
+ if (!apiKey) {
20
+ return null;
21
+ }
22
+ return await this.adminizer.modelHandler
23
+ .internal("feed")
24
+ .get("FilterAP")
25
+ .findOne({ where: { apiKey, apiEnabled: true, visibility: 'private' } });
26
+ }
27
+ /**
28
+ * Fetch data for a filter (no auth required — public API)
29
+ * Bypasses permission checks by building fields directly from model config
30
+ * Also bypasses DataAccessor to avoid user-based filtering
31
+ */
32
+ async fetchFilterData(filter, adminizer, user) {
33
+ const modelName = filter.modelName;
34
+ const adminizerInstance = adminizer || this.adminizer;
35
+ if (!user)
36
+ throw 'User is not defined';
37
+ Adminizer.log.info(`FeedService: === START fetchFilterData === filter=${filter.id}, model=${modelName}`);
38
+ // Create a minimal request-like object to use ControllerHelper
39
+ const mockReq = {
40
+ adminizer: adminizerInstance,
41
+ params: { modelResourceName: modelName },
42
+ query: {},
43
+ originalUrl: `/adminizer/model/${modelName}/list`,
44
+ url: `/adminizer/model/${modelName}/list`
45
+ };
46
+ // Get ModelResource for model
47
+ const modelResource = ControllerHelper.findModelResource(mockReq);
48
+ if (!modelResource || !modelResource.model || !modelResource.model.attributes) {
49
+ throw new Error(`Model '${modelName}' not found`);
50
+ }
51
+ // Build fields directly from model attributes (bypass permission checks for public API)
52
+ const fields = this.buildFieldsFromAttributes(modelResource.model.attributes, modelResource.config?.fields || {}, adminizerInstance);
53
+ // Use saved columns if available
54
+ let displayFields = fields;
55
+ if (filter.columns && filter.columns.length > 0) {
56
+ displayFields = this.applyCustomColumns(fields, filter.columns);
57
+ }
58
+ // Convert datetime conditions (same as exportData.ts)
59
+ const rawConditions = filter.conditions || [];
60
+ const convertedConditions = convertDatetimeConditions(rawConditions, { dropEmptyValues: true });
61
+ const createdAtSortField = this.resolveCreatedAtSortField(modelResource.model.attributes, displayFields);
62
+ // Build query params for where clause construction
63
+ const queryParams = {
64
+ page: 1,
65
+ limit: 20,
66
+ filters: convertedConditions.length > 0 ? convertedConditions : undefined,
67
+ sort: createdAtSortField,
68
+ sortDirection: 'DESC',
69
+ fields: Object.keys(displayFields)
70
+ };
71
+ // Build where clause using ListQueryBuilder
72
+ // Create an administrator DataAccessor to avoid user-based filtering
73
+ const dataAccessor = new DataAccessor(adminizerInstance, user, modelResource, "list");
74
+ const listQueryBuilder = new ListQueryBuilder(modelResource.model, fields, dataAccessor, adminizerInstance.customFilterHandler);
75
+ const result = await listQueryBuilder.execute(queryParams);
76
+ Adminizer.log.info(`FeedService: === END fetchFilterData === records=${result.data?.length ?? 0}`);
77
+ return {
78
+ records: result.data,
79
+ fields: displayFields,
80
+ modelName,
81
+ modelResource
82
+ };
83
+ }
84
+ /**
85
+ * Build fields object directly from model attributes (bypasses permission checks)
86
+ */
87
+ buildFieldsFromAttributes(attributes, configFields = {}, adminizerInstance) {
88
+ const admin = adminizerInstance || this.adminizer;
89
+ const result = {};
90
+ for (const [key, modelField] of Object.entries(attributes)) {
91
+ // Skip primary keys for associations
92
+ if (modelField.primaryKeyForAssociation === true) {
93
+ continue;
94
+ }
95
+ // Handle short type notation: fieldName: 'string'
96
+ let normalizedField = typeof modelField === 'string' ? { type: modelField } : modelField;
97
+ // Get field config from model config
98
+ const fieldConfig = configFields[key] || {};
99
+ result[key] = {
100
+ config: fieldConfig,
101
+ populated: undefined,
102
+ model: normalizedField,
103
+ modelConfig: admin.config.models[Object.keys(admin.config.models).find(k => k.toLowerCase() === Object.keys(attributes)[0]?.split('.')[0])?.toLowerCase() || ''] || {}
104
+ };
105
+ }
106
+ return result;
107
+ }
108
+ /**
109
+ * Generate JSON feed
110
+ */
111
+ async generateJsonFeed(filter, adminizer, user) {
112
+ const { records, fields, modelName } = await this.fetchFilterData(filter, adminizer, user);
113
+ const exportData = this.prepareExportData(records, fields);
114
+ return {
115
+ feed: {
116
+ title: filter.name,
117
+ description: filter.description || '',
118
+ modelName,
119
+ generatedAt: new Date().toISOString(),
120
+ totalItems: records.length,
121
+ apiKey: filter.apiKey,
122
+ items: exportData
123
+ }
124
+ };
125
+ }
126
+ /**
127
+ * Generate Atom XML feed
128
+ */
129
+ async generateAtomFeed(filter, adminizer, user) {
130
+ const { records, fields, modelName } = await this.fetchFilterData(filter, adminizer, user);
131
+ const exportData = this.prepareExportData(records, fields);
132
+ const entries = exportData.map((item, index) => {
133
+ const entryId = item.id || item.ID || index;
134
+ const titleField = this.findTitleField(fields);
135
+ const title = titleField ? String(item[titleField] || `Item ${entryId}`) : `Item ${entryId}`;
136
+ const updated = this.findDateField(fields, item) || new Date();
137
+ return {
138
+ id: String(entryId),
139
+ title,
140
+ updated: new Date(updated),
141
+ summary: title,
142
+ content: item,
143
+ link: `/adminizer/model/${modelName}/view/${entryId}`
144
+ };
145
+ });
146
+ return this.buildAtomXml(filter, modelName, entries, adminizer);
147
+ }
148
+ /**
149
+ * Build Atom XML string
150
+ */
151
+ buildAtomXml(filter, modelName, entries, adminizerInstance) {
152
+ const admin = adminizerInstance || this.adminizer;
153
+ const now = new Date().toISOString();
154
+ const routePrefix = admin.config.routePrefix || '/adminizer';
155
+ const baseUrl = `/${routePrefix.replace(/^\//, '')}`;
156
+ let xml = `<?xml version="1.0" encoding="UTF-8"?>\n`;
157
+ xml += `<feed xmlns="http://www.w3.org/2005/Atom">\n`;
158
+ xml += ` <id>${baseUrl}/api/feed/${filter.apiKey}</id>\n`;
159
+ xml += ` <title>${this.escapeXml(filter.name)}</title>\n`;
160
+ xml += ` <subtitle>${this.escapeXml(filter.description || `Feed for ${modelName}`)}</subtitle>\n`;
161
+ xml += ` <updated>${now}</updated>\n`;
162
+ xml += ` <link href="${baseUrl}/api/feed/${filter.apiKey}.xml" rel="self" />\n`;
163
+ xml += ` <generator>Adminizer</generator>\n`;
164
+ for (const entry of entries) {
165
+ xml += ` <entry>\n`;
166
+ xml += ` <id>${this.escapeXml(String(entry.id))}</id>\n`;
167
+ xml += ` <title>${this.escapeXml(entry.title)}</title>\n`;
168
+ xml += ` <updated>${entry.updated.toISOString()}</updated>\n`;
169
+ xml += ` <summary>${this.escapeXml(entry.summary || '')}</summary>\n`;
170
+ if (entry.link) {
171
+ xml += ` <link href="${this.escapeXml(entry.link)}" rel="alternate" />\n`;
172
+ }
173
+ // Add content as structured XML
174
+ xml += ` <content type="html">\n`;
175
+ xml += ` <![CDATA[\n`;
176
+ xml += ` <table>\n`;
177
+ for (const [key, value] of Object.entries(entry.content)) {
178
+ const displayValue = value === null || value === undefined ? '' : String(value);
179
+ xml += ` <tr><th>${this.escapeXml(key)}</th><td>${this.escapeXml(displayValue)}</td></tr>\n`;
180
+ }
181
+ xml += ` </table>\n`;
182
+ xml += ` ]]>\n`;
183
+ xml += ` </content>\n`;
184
+ // Also add raw data as JSON in a custom element
185
+ xml += ` <data>${this.escapeXml(JSON.stringify(entry.content))}</data>\n`;
186
+ xml += ` </entry>\n`;
187
+ }
188
+ xml += `</feed>`;
189
+ return xml;
190
+ }
191
+ /**
192
+ * Escape XML special characters
193
+ */
194
+ escapeXml(str) {
195
+ if (str === null || str === undefined)
196
+ return '';
197
+ const s = String(str);
198
+ return s
199
+ .replace(/&/g, '&amp;')
200
+ .replace(/</g, '&lt;')
201
+ .replace(/>/g, '&gt;')
202
+ .replace(/"/g, '&quot;')
203
+ .replace(/'/g, '&apos;');
204
+ }
205
+ /**
206
+ * Prepare data for export — convert values to human-readable format
207
+ */
208
+ prepareExportData(records, fields) {
209
+ return records.map(record => {
210
+ const row = {};
211
+ for (const [key, field] of Object.entries(fields)) {
212
+ const fieldConfig = field;
213
+ row[key] = this.formatExportValue(fieldConfig, record[key]);
214
+ }
215
+ return row;
216
+ });
217
+ }
218
+ /**
219
+ * Format value for export based on type
220
+ */
221
+ formatExportValue(field, value) {
222
+ if (value === null || value === undefined) {
223
+ return '';
224
+ }
225
+ const fieldType = field.model?.type;
226
+ // Handle relationships
227
+ if (field.model?.model) {
228
+ const displayField = field.modelConfig?.titleField ??
229
+ (typeof value === 'object' && value ? (value.title || value.name || value.id) : value);
230
+ return typeof displayField === 'object' ? JSON.stringify(displayField) : String(displayField);
231
+ }
232
+ // Handle associations (hasMany)
233
+ if (fieldType === 'association-many' || fieldType === 'association') {
234
+ if (Array.isArray(value)) {
235
+ return value.map((item) => {
236
+ if (typeof item === 'object' && item) {
237
+ return item.title || item.name || item.id || JSON.stringify(item);
238
+ }
239
+ return String(item);
240
+ }).join(', ');
241
+ }
242
+ return String(value);
243
+ }
244
+ // Handle JSON fields
245
+ if (fieldType === 'json') {
246
+ if (typeof value === 'object') {
247
+ return JSON.stringify(value, null, 2);
248
+ }
249
+ return String(value);
250
+ }
251
+ // Handle boolean
252
+ if (fieldType === 'boolean') {
253
+ return value ? 'Yes' : 'No';
254
+ }
255
+ // Handle dates
256
+ if (fieldType === 'date' || fieldType === 'datetime') {
257
+ if (value instanceof Date) {
258
+ return value.toISOString();
259
+ }
260
+ return String(value);
261
+ }
262
+ // Handle arrays
263
+ if (Array.isArray(value)) {
264
+ return value.join(', ');
265
+ }
266
+ // Default: convert to string
267
+ return String(value);
268
+ }
269
+ /**
270
+ * Apply custom column configuration
271
+ */
272
+ applyCustomColumns(fields, columns) {
273
+ const sortedColumns = [...columns].sort((a, b) => (a.order || 0) - (b.order || 0));
274
+ const visibleColumns = sortedColumns.filter(col => fields[col.fieldName]);
275
+ if (visibleColumns.length === 0) {
276
+ return fields;
277
+ }
278
+ const result = {};
279
+ for (const col of visibleColumns) {
280
+ if (fields[col.fieldName]) {
281
+ result[col.fieldName] = fields[col.fieldName];
282
+ }
283
+ }
284
+ return result;
285
+ }
286
+ /**
287
+ * Resolve the best field for "newest first" sorting.
288
+ * Priority: createdAt -> created_at -> id
289
+ */
290
+ resolveCreatedAtSortField(attributes, fields) {
291
+ const candidates = ['createdAt', 'created_at'];
292
+ for (const candidate of candidates) {
293
+ if (attributes?.[candidate] || fields?.[candidate]) {
294
+ return candidate;
295
+ }
296
+ }
297
+ return 'id';
298
+ }
299
+ /**
300
+ * Find title field in fields config
301
+ */
302
+ findTitleField(fields) {
303
+ // First look for configured title field
304
+ for (const [key, field] of Object.entries(fields)) {
305
+ const f = field;
306
+ if (f.modelConfig?.titleField) {
307
+ return key;
308
+ }
309
+ }
310
+ // Fallback to common title field names
311
+ const candidates = ['title', 'name', 'label', 'caption'];
312
+ for (const name of candidates) {
313
+ if (fields[name]) {
314
+ return name;
315
+ }
316
+ }
317
+ return null;
318
+ }
319
+ /**
320
+ * Find date field in fields config
321
+ */
322
+ findDateField(fields, record) {
323
+ const candidates = ['updatedAt', 'updated_at', 'modifiedAt', 'createdAt', 'created_at'];
324
+ for (const name of candidates) {
325
+ if (record[name]) {
326
+ const date = new Date(record[name]);
327
+ if (!isNaN(date.getTime())) {
328
+ return date;
329
+ }
330
+ }
331
+ }
332
+ return new Date();
333
+ }
334
+ }
@@ -10,5 +10,6 @@ export default class Router {
10
10
  */
11
11
  bind(): Promise<void>;
12
12
  unbindModelRoutes(model: string): void;
13
+ private getRouterStack;
13
14
  bindModelRoutes(model: string, policies?: MiddlewareType[]): Promise<void>;
14
15
  }