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.
- package/Readme.md +12 -0
- package/assets/RelationDialogStack-CnHFOYyR.css +1 -0
- package/assets/RelationDialogStack-DI2bZxQX.js +20 -0
- package/assets/add-4213XScp.js +1 -0
- package/assets/add-group--F1XxfJ1.js +1 -0
- package/assets/add-group-form-DKRXC2n-.js +1 -0
- package/assets/add-user-b9HfMq7z.js +1 -0
- package/assets/add-user-form-UXAToWQd.js +1 -0
- package/assets/app-Clx8f5hd.css +1 -0
- package/assets/app-layout-C02ja4OI.js +1 -0
- package/assets/app-shell-D8wtkECO.js +1 -0
- package/assets/app-sidebar-BR5_zwxj.js +1 -0
- package/assets/app-sidebar-header-BZbIBQjq.js +1 -0
- package/assets/app.js +59 -7990
- package/assets/breadcrumbs-CODaqVkB.js +1 -0
- package/assets/catalog-6ZZjWdFs.js +6 -0
- package/assets/dashboard-CftAdKiH.js +3 -0
- package/assets/date-picker-DSOkNiNo.js +1 -0
- package/assets/del-modal-Cd4M7af8.js +1 -0
- package/assets/dynamic-controls-BsrJhPDY.js +1 -0
- package/assets/geo-json-CzR5JBXL.js +1 -0
- package/assets/handsontable-BI98OF5R.js +336 -0
- package/assets/history-DLHSU7Ak.js +1 -0
- package/assets/home-NcQ7nnU0.js +2 -0
- package/assets/icon-C2JNaLxH.js +1 -0
- package/assets/init-user-D2HSyXup.js +1 -0
- package/assets/input-error-CH2caTtO.js +1 -0
- package/assets/jsoneditor-DVNQmA6G.js +3869 -0
- package/assets/list-CFU0-TgN.js +4 -0
- package/assets/login-DFSLEO17.js +1 -0
- package/assets/manifest.json +246 -273
- package/assets/material-icon-Bx9pGQiQ.js +1 -0
- package/assets/media-manager-WcKe1EvD.js +6 -0
- package/assets/module-CT7PC6pc.js +1 -0
- package/assets/monaco-Bl4zdWCn.js +11 -0
- package/assets/nav-footer-BiSQkPt4.js +1 -0
- package/assets/nav-main-rTH0PfDw.js +1 -0
- package/assets/nav-user-Bj6cWs0a.js +1 -0
- package/assets/notification-Bi-PX87D.js +20 -0
- package/assets/pagination-render-AKJvTIRA.js +1 -0
- package/assets/register-BTP4uxsK.js +1 -0
- package/assets/ru-eW0WKkga.js +1 -0
- package/assets/theme-switcher-Dm8EOGzx.js +1 -0
- package/assets/toast-editor-DOg_gvSp.js +668 -0
- package/assets/user-filters-list-YUfR4XMI.js +1 -0
- package/assets/user-info-BgSbT02v.js +1 -0
- package/assets/user-menu-content-TQTXHIv6.js +1 -0
- package/assets/{vanilla-picker-l5rcX3cq.js → vanilla-picker-B3LgXoKV.js} +1 -7
- package/assets/welcome-vLwTu1R6.js +1 -0
- package/controllers/add.d.ts +1 -1
- package/controllers/add.js +20 -26
- package/controllers/addGroup.d.ts +1 -1
- package/controllers/addGroup.js +7 -15
- package/controllers/addUser.d.ts +1 -1
- package/controllers/addUser.js +10 -16
- package/controllers/catalog/Catalog.d.ts +1 -1
- package/controllers/catalog/Catalog.js +1 -10
- package/controllers/catalog/FrontendCatalogAdapter.d.ts +3 -3
- package/controllers/catalog/FrontendCatalogAdapter.js +0 -6
- package/controllers/catalog/FrontentCatalogAdapter.d.ts +3 -3
- package/controllers/catalog/FrontentCatalogAdapter.js +0 -4
- package/controllers/ckeditorUpload.js +2 -18
- package/controllers/dashboard.d.ts +1 -1
- package/controllers/dashboard.js +2 -5
- package/controllers/edit.js +24 -37
- package/controllers/editGroup.d.ts +1 -1
- package/controllers/editGroup.js +10 -21
- package/controllers/editUser.d.ts +1 -1
- package/controllers/editUser.js +11 -23
- package/controllers/exportData.d.ts +6 -0
- package/controllers/exportData.js +315 -0
- package/controllers/feed.d.ts +6 -0
- package/controllers/feed.js +73 -0
- package/controllers/feedback/feedbackController.d.ts +1 -1
- package/controllers/feedback/feedbackController.js +5 -7
- package/controllers/filter-fields/columns.d.ts +11 -0
- package/controllers/filter-fields/columns.js +106 -0
- package/controllers/filter-fields/filterFields.d.ts +10 -0
- package/controllers/filter-fields/filterFields.js +141 -0
- package/controllers/filter-fields/getAllUserFilters.d.ts +6 -0
- package/controllers/filter-fields/getAllUserFilters.js +115 -0
- package/controllers/filter-fields/groups.d.ts +5 -0
- package/controllers/filter-fields/groups.js +26 -0
- package/controllers/filter-fields/savedFilters.d.ts +25 -0
- package/controllers/filter-fields/savedFilters.js +317 -0
- package/controllers/getAllWidgets.d.ts +1 -1
- package/controllers/getAllWidgets.js +1 -9
- package/controllers/history-actions/HistoryController.js +6 -31
- package/controllers/initUser.js +6 -5
- package/controllers/inlineUpdate.d.ts +7 -0
- package/controllers/inlineUpdate.js +148 -0
- package/controllers/list.d.ts +1 -1
- package/controllers/list.js +383 -70
- package/controllers/listUserFilters.d.ts +5 -0
- package/controllers/listUserFilters.js +26 -0
- package/controllers/login.js +2 -2
- package/controllers/media-manager/mediaManagerApi.js +0 -9
- package/controllers/navigation/edit.js +3 -9
- package/controllers/notifications/NotificationController.d.ts +1 -1
- package/controllers/notifications/NotificationController.js +12 -36
- package/controllers/register.js +10 -9
- package/controllers/remove.js +10 -19
- package/controllers/timezones.d.ts +1 -1
- package/controllers/timezones.js +0 -6
- package/controllers/userApiKey.d.ts +7 -0
- package/controllers/userApiKey.js +36 -0
- package/controllers/view.js +14 -26
- package/controllers/welcome.d.ts +1 -1
- package/controllers/welcome.js +0 -4
- package/controllers/widgets/Action.d.ts +1 -1
- package/controllers/widgets/Action.js +1 -9
- package/controllers/widgets/Custom.d.ts +1 -1
- package/controllers/widgets/Custom.js +1 -9
- package/controllers/widgets/Info.d.ts +1 -1
- package/controllers/widgets/Info.js +1 -9
- package/controllers/widgets/filterInfo.d.ts +1 -0
- package/controllers/widgets/filterInfo.js +44 -0
- package/controllers/widgets/switch.d.ts +1 -1
- package/controllers/widgets/switch.js +1 -9
- package/controllers/widgetsDB.d.ts +1 -1
- package/controllers/widgetsDB.js +2 -8
- package/helpers/accessRightsHelper.d.ts +1 -1
- package/helpers/accessRightsHelper.js +5 -4
- package/helpers/apiKeyHelper.d.ts +5 -0
- package/helpers/apiKeyHelper.js +8 -0
- package/helpers/configHelper.js +12 -15
- package/helpers/controllerHelper.d.ts +14 -21
- package/helpers/controllerHelper.js +50 -83
- package/helpers/fieldsHelper.js +3 -3
- package/helpers/filterDatetimeHelper.d.ts +5 -0
- package/helpers/filterDatetimeHelper.js +84 -0
- package/helpers/inertiaActionsHelper.d.ts +2 -2
- package/helpers/inertiaActionsHelper.js +5 -4
- package/helpers/inertiaAddHelper.d.ts +3 -414
- package/helpers/inertiaAddHelper.js +39 -15
- package/helpers/inertiaGroupHelper.d.ts +2 -2
- package/helpers/inertiaGroupHelper.js +3 -3
- package/helpers/inertiaListHelper.d.ts +7 -3
- package/helpers/inertiaListHelper.js +26 -12
- package/helpers/inertiaMenuHelper.d.ts +1 -0
- package/helpers/inertiaMenuHelper.js +15 -3
- package/helpers/inertiaUserHelper.d.ts +3 -2
- package/helpers/inertiaUserHelper.js +7 -3
- package/helpers/menuHelper.d.ts +5 -2
- package/helpers/menuHelper.js +64 -12
- package/helpers/viteHelper.js +1 -2
- package/index.d.ts +6 -2
- package/index.js +6 -2
- package/interfaces/adminpanelConfig.d.ts +126 -39
- package/interfaces/internalModelAccess.d.ts +37 -0
- package/interfaces/internalModelAccess.js +1 -0
- package/interfaces/listQueryBuilder.d.ts +22 -0
- package/interfaces/listQueryBuilder.js +1 -0
- package/interfaces/queryCriteria.d.ts +54 -0
- package/interfaces/queryCriteria.js +1 -0
- package/interfaces/types.d.ts +1 -13
- package/lib/Adminizer.d.ts +4 -2
- package/lib/Adminizer.js +29 -10
- package/lib/DataAccessor.d.ts +8 -5
- package/lib/DataAccessor.js +89 -42
- package/lib/I18n.d.ts +5 -1
- package/lib/I18n.js +45 -19
- package/lib/MiddlewareManager.d.ts +8 -0
- package/lib/MiddlewareManager.js +53 -0
- package/lib/ai-assistant/OpenAiModelService.d.ts +3 -3
- package/lib/ai-assistant/OpenAiModelService.js +23 -24
- package/lib/catalog/Navigation.d.ts +1 -0
- package/lib/catalog/Navigation.js +12 -11
- package/lib/controls/jsoneditor/JsonEditor.d.ts +1 -2
- package/lib/controls/markdown/ToastUiEditor.d.ts +1 -2
- package/lib/feedback/FeedbackHandler.js +2 -2
- package/lib/filters/ConditionValidator.d.ts +107 -0
- package/lib/filters/ConditionValidator.js +483 -0
- package/lib/filters/CustomFilterHandler.d.ts +122 -0
- package/lib/filters/CustomFilterHandler.js +131 -0
- package/lib/filters/FilterCustomFieldHandler.d.ts +2 -0
- package/lib/filters/FilterCustomFieldHandler.js +2 -0
- package/lib/filters/FilterService.d.ts +82 -0
- package/lib/filters/FilterService.js +323 -0
- package/lib/filters/index.d.ts +6 -0
- package/lib/filters/index.js +4 -0
- package/lib/history-actions/AbstractHistoryAdapter.d.ts +5 -4
- package/lib/history-actions/AbstractHistoryAdapter.js +45 -30
- package/lib/history-actions/DefaultHistoryAdapter.d.ts +2 -0
- package/lib/history-actions/DefaultHistoryAdapter.js +12 -6
- package/lib/history-actions/HistoryHandler.js +1 -1
- package/lib/list-query-builder/ListQueryBuilder.d.ts +117 -0
- package/lib/list-query-builder/ListQueryBuilder.js +707 -0
- package/lib/media-manager/AbstractMediaManager.d.ts +9 -8
- package/lib/media-manager/DefaultMediaManager.d.ts +2 -0
- package/lib/media-manager/DefaultMediaManager.js +27 -15
- package/lib/media-manager/Items.d.ts +4 -1
- package/lib/media-manager/Items.js +62 -59
- package/lib/media-manager/MediaManagerHandler.js +1 -1
- package/lib/media-manager/helpers/MediaManagerHelper.d.ts +5 -1
- package/lib/media-manager/helpers/MediaManagerHelper.js +38 -4
- package/lib/model/AbstractModel.d.ts +33 -25
- package/lib/model/AbstractModel.js +54 -2
- package/lib/model/InternalModelAccessFactory.d.ts +18 -0
- package/lib/model/InternalModelAccessFactory.js +82 -0
- package/lib/model/ModelHandler.d.ts +6 -0
- package/lib/model/ModelHandler.js +14 -0
- package/lib/model/adapter/sequelize.d.ts +32 -10
- package/lib/model/adapter/sequelize.js +409 -62
- package/lib/model/adapter/typeorm.d.ts +62 -0
- package/lib/model/adapter/typeorm.js +777 -0
- package/lib/model/internalModelAccessToken.d.ts +1 -0
- package/lib/model/internalModelAccessToken.js +1 -0
- package/lib/model/internalModelRepository.d.ts +3 -0
- package/lib/model/internalModelRepository.js +4 -0
- package/lib/notifications/AbstractNotificationService.d.ts +3 -0
- package/lib/notifications/AbstractNotificationService.js +42 -36
- package/lib/notifications/GeneralNotificationService.js +37 -40
- package/lib/notifications/SystemNotificationService.js +33 -36
- package/lib/requestProcessor.js +0 -3
- package/lib/ui-i18n/getUiTranslations.d.ts +1 -0
- package/lib/ui-i18n/getUiTranslations.js +3 -0
- package/lib/ui-i18n/uiTranslationKeys.d.ts +6 -0
- package/lib/ui-i18n/uiTranslationKeys.js +268 -0
- package/lib/widgets/widgetHandler.d.ts +7 -2
- package/lib/widgets/widgetHandler.js +98 -18
- package/models/FilterAP.d.ts +116 -0
- package/models/FilterAP.js +68 -0
- package/models/FilterColumnAP.d.ts +32 -0
- package/models/FilterColumnAP.js +21 -0
- package/models/GroupAP.d.ts +1 -1
- package/models/HistoryActionsAP.d.ts +3 -3
- package/models/MediaManagerMetaAP.d.ts +1 -1
- package/models/NavigationAP.d.ts +1 -1
- package/models/UserAP.d.ts +6 -1
- package/models/UserAP.js +4 -0
- package/package.json +39 -32
- package/policies/authPolicies.d.ts +12 -0
- package/policies/authPolicies.js +104 -0
- package/policies/permissionResolvers.d.ts +12 -0
- package/policies/permissionResolvers.js +52 -0
- package/services/FeedService.d.ts +70 -0
- package/services/FeedService.js +334 -0
- package/system/Router.d.ts +1 -0
- package/system/Router.js +136 -59
- package/system/bindAccessRights.js +11 -30
- package/system/bindAuthorization.js +18 -18
- package/system/bindCors.js +16 -1
- package/system/bindCustomFilterHandlers.d.ts +2 -0
- package/system/bindCustomFilterHandlers.js +4 -0
- package/system/bindInertia.js +18 -61
- package/system/bindReqFunctions.js +10 -2
- package/system/bindTranslations.js +7 -3
- package/system/buildInternalModelAccess.d.ts +4 -0
- package/system/buildInternalModelAccess.js +59 -0
- package/system/defaults.js +18 -14
- package/system/routePolicies.d.ts +2 -0
- package/system/routePolicies.js +29 -0
- package/translations/en.json +151 -39
- package/translations/ru.json +211 -64
- package/ui/components/handsontable.js +13 -27
- package/ui/components/ui/dialog-stack.js +1 -3
- package/ui/components/ui/popover.js +1 -1
- package/ui/components/ui/sidebar.js +1 -1
- package/assets/RelationDialogStack-D9UVEF9s.js +0 -407
- package/assets/RelationDialogStack-DV8OITPR.css +0 -4
- package/assets/add-BlvzWQKF.js +0 -1
- package/assets/add-group-1Uoq9NWe.js +0 -1
- package/assets/add-group-form-B01gwDGu.js +0 -1
- package/assets/add-user-BQCAsaRy.js +0 -1
- package/assets/add-user-form-Cgwc9JkI.js +0 -1
- package/assets/app-DaOMy1nl.css +0 -72
- package/assets/app-layout-CAm60qoE.js +0 -1
- package/assets/app-shell-QWCtn9ye.js +0 -1
- package/assets/app-sidebar-CfaYbnSy.js +0 -1
- package/assets/app-sidebar-header-BsPFDhZ8.js +0 -1
- package/assets/breadcrumbs-CZZirDj8.js +0 -1
- package/assets/catalog-k5myk7fk.js +0 -6
- package/assets/dashboard-BkL9ybBw.js +0 -3
- package/assets/date-picker-B5AZ_7Q9.js +0 -1
- package/assets/del-modal-BoyXZloG.js +0 -1
- package/assets/dynamic-controls-k61T9W2P.js +0 -1
- package/assets/form-1WGY0YgG.js +0 -1
- package/assets/form-DJapG3qF.js +0 -1
- package/assets/geo-json-DsMUHo0l.js +0 -4
- package/assets/handsontable-PxYdjN8c.js +0 -298
- package/assets/history-CluyR-yJ.js +0 -1
- package/assets/home-Dug9zBzM.js +0 -2
- package/assets/icon-DfDLMG5f.js +0 -1
- package/assets/init-user-CF80MEnT.js +0 -1
- package/assets/input-error-DuuqFgZa.js +0 -1
- package/assets/jsoneditor-De1FvcYu.js +0 -3882
- package/assets/list-CuexshNt.js +0 -1
- package/assets/list-table-BsEWjTKY.js +0 -22
- package/assets/login-eDemYzN-.js +0 -1
- package/assets/material-icon-DMT8KdmE.js +0 -1
- package/assets/media-manager-DfYa_mT_.js +0 -14
- package/assets/module-Cd4Tm6uq.js +0 -1
- package/assets/monaco-BhBYtg3d.js +0 -11
- package/assets/nav-footer-D3olYxtz.js +0 -1
- package/assets/nav-main-BctqyzeV.js +0 -1
- package/assets/nav-user-Bn1Lt-BZ.js +0 -1
- package/assets/notification-D64qvni-.js +0 -27
- package/assets/pagination-render-DxD78ahJ.js +0 -1
- package/assets/register-349PwYbe.js +0 -1
- package/assets/theme-switcher-DfExsHW2.js +0 -1
- package/assets/toast-editor-ESK5iWPx.js +0 -845
- package/assets/user-info-DtBK5Kh8.js +0 -1
- package/assets/user-menu-content-0XYOCMaA.js +0 -1
- package/assets/welcome-PDp-gUei.js +0 -1
- package/controllers/form.d.ts +0 -1
- package/controllers/form.js +0 -86
- package/helpers/formHelper.d.ts +0 -6
- package/helpers/formHelper.js +0 -38
- package/helpers/inertiaFromHelper.d.ts +0 -11
- package/helpers/inertiaFromHelper.js +0 -98
- package/helpers/runtimeMode.d.ts +0 -1
- package/helpers/runtimeMode.js +0 -3
- package/lib/PolicyManager.d.ts +0 -8
- package/lib/PolicyManager.js +0 -53
- package/lib/datatable/NodeTable.d.ts +0 -49
- package/lib/datatable/NodeTable.js +0 -207
- package/lib/model/adapter/waterline.d.ts +0 -32
- package/lib/model/adapter/waterline.js +0 -180
- package/migrations/index.d.ts +0 -3
- package/migrations/index.js +0 -4
- package/migrations/knex/20250106000001_adminizer_init.d.ts +0 -8
- package/migrations/knex/20250106000001_adminizer_init.js +0 -164
- package/migrations/umzug/0001.d.ts +0 -7
- package/migrations/umzug/0001.js +0 -121
- package/migrations/umzugExports.d.ts +0 -2
- package/migrations/umzugExports.js +0 -9
- package/system/bindForms.d.ts +0 -2
- package/system/bindForms.js +0 -20
- /package/{policies → middlewares}/addMessages.d.ts +0 -0
- /package/{policies → middlewares}/addMessages.js +0 -0
- /package/{policies → middlewares}/checkAuth.d.ts +0 -0
- /package/{policies → middlewares}/checkAuth.js +0 -0
- /package/{policies → middlewares}/setLocale.d.ts +0 -0
- /package/{policies → middlewares}/setLocale.js +0 -0
package/controllers/edit.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { ControllerHelper } from "../helpers/controllerHelper.js";
|
|
2
2
|
import { RequestProcessor } from "../lib/requestProcessor.js";
|
|
3
3
|
import { FieldsHelper } from "../helpers/fieldsHelper.js";
|
|
4
|
-
import {
|
|
5
|
-
import { detachMediaManagerField, getRelationsMediaManager, saveRelationsMediaManager } from "../lib/media-manager/helpers/MediaManagerHelper.js";
|
|
4
|
+
import { detachMediaManagerField, getRelationsMediaManager, isMediaManagerFieldConfig, saveRelationsMediaManager, updateCurrentHistoryMediaManagerData } from "../lib/media-manager/helpers/MediaManagerHelper.js";
|
|
6
5
|
import { DataAccessor } from "../lib/DataAccessor.js";
|
|
7
6
|
import { Adminizer } from "../lib/Adminizer.js";
|
|
8
7
|
import inertiaAddHelper from "../helpers/inertiaAddHelper.js";
|
|
@@ -11,27 +10,19 @@ export default async function edit(req, res) {
|
|
|
11
10
|
if (!req.params.id) {
|
|
12
11
|
return res.status(404).send({ error: 'Not Found' });
|
|
13
12
|
}
|
|
14
|
-
let
|
|
15
|
-
if (!
|
|
13
|
+
let modelResource = ControllerHelper.findModelResource(req);
|
|
14
|
+
if (!modelResource.model) {
|
|
16
15
|
return res.status(404).send({ error: 'Not Found' });
|
|
17
16
|
}
|
|
18
|
-
if (!
|
|
19
|
-
return res.redirect(`${req.adminizer.config.routePrefix}/${
|
|
20
|
-
}
|
|
21
|
-
if (req.adminizer.config.auth.enable) {
|
|
22
|
-
if (!req.user) {
|
|
23
|
-
return redirectToLogin(req, res);
|
|
24
|
-
}
|
|
25
|
-
else if (!req.adminizer.accessRightsHelper.hasPermission(`update-${entity.name}-model`, req.user)) {
|
|
26
|
-
return res.sendStatus(403);
|
|
27
|
-
}
|
|
17
|
+
if (!modelResource.config.edit) {
|
|
18
|
+
return res.redirect(`${req.adminizer.config.routePrefix}/${modelResource.uri}`);
|
|
28
19
|
}
|
|
29
20
|
let record;
|
|
30
21
|
let dataAccessor;
|
|
31
22
|
const id = req.params.id;
|
|
32
23
|
try {
|
|
33
|
-
dataAccessor = new DataAccessor(req.adminizer, req.user,
|
|
34
|
-
record = await
|
|
24
|
+
dataAccessor = new DataAccessor(req.adminizer, req.user, modelResource, "edit");
|
|
25
|
+
record = await modelResource.model.findOne({ where: { id: id } }, dataAccessor);
|
|
35
26
|
if (!record)
|
|
36
27
|
return res.status(404).send("Adminpanel > Record not found");
|
|
37
28
|
}
|
|
@@ -45,11 +36,11 @@ export default async function edit(req, res) {
|
|
|
45
36
|
fields = await FieldsHelper.loadAssociations(req, fields, "edit");
|
|
46
37
|
// Save
|
|
47
38
|
if (req.method.toUpperCase() === 'POST') {
|
|
48
|
-
const
|
|
39
|
+
const identifierField = modelResource.config.identifierField || req.adminizer.config.identifierField;
|
|
49
40
|
delete req.body.redirectUrl;
|
|
50
41
|
let reqData = RequestProcessor.processRequest(req, fields);
|
|
51
42
|
let params = {};
|
|
52
|
-
params[
|
|
43
|
+
params[modelResource.config.identifierField || req.adminizer.config.identifierField] = req.params.id;
|
|
53
44
|
/**
|
|
54
45
|
* Here means reqData adapt for model data, but rawReqData is processed for widget processing
|
|
55
46
|
*/
|
|
@@ -65,8 +56,7 @@ export default async function edit(req, res) {
|
|
|
65
56
|
reqData[prop] = null;
|
|
66
57
|
}
|
|
67
58
|
let fieldConfigConfig = fields[prop].config;
|
|
68
|
-
//
|
|
69
|
-
// TODO check if adding and deleting associations works for other orm (this code was written for waterline)
|
|
59
|
+
// Normalize empty association payloads before passing them to the adapter
|
|
70
60
|
if (fields[prop] && fields[prop].model && (fields[prop].model.type === 'association-many' || fields[prop].model.type === 'association')) {
|
|
71
61
|
if (!reqData[prop] || !reqData[prop].length) {
|
|
72
62
|
reqData[prop] = fields[prop].model.type === 'association' ? null : [];
|
|
@@ -77,7 +67,7 @@ export default async function edit(req, res) {
|
|
|
77
67
|
}
|
|
78
68
|
}
|
|
79
69
|
}
|
|
80
|
-
if (fieldConfigConfig
|
|
70
|
+
if (isMediaManagerFieldConfig(fieldConfigConfig)) {
|
|
81
71
|
detachMediaManagerField(reqData, rawReqData, prop);
|
|
82
72
|
continue;
|
|
83
73
|
}
|
|
@@ -102,14 +92,15 @@ export default async function edit(req, res) {
|
|
|
102
92
|
reqData[prop] = reqData[prop].join("");
|
|
103
93
|
}
|
|
104
94
|
}
|
|
105
|
-
// callback before save
|
|
106
|
-
let
|
|
107
|
-
if (typeof
|
|
108
|
-
reqData =
|
|
95
|
+
// callback before save modelResource
|
|
96
|
+
let editConfig = modelResource.config.edit;
|
|
97
|
+
if (typeof editConfig.entityModifier === "function") {
|
|
98
|
+
reqData = editConfig.entityModifier(reqData);
|
|
109
99
|
}
|
|
110
100
|
try {
|
|
111
|
-
let newRecord = await
|
|
112
|
-
await saveRelationsMediaManager(req.adminizer, fields, rawReqData,
|
|
101
|
+
let newRecord = await modelResource.model.update(params, reqData, dataAccessor);
|
|
102
|
+
await saveRelationsMediaManager(req.adminizer, fields, rawReqData, modelResource.model.modelname, newRecord[0].id);
|
|
103
|
+
await updateCurrentHistoryMediaManagerData(req.adminizer, fields, rawReqData, modelResource.name, newRecord[0].id);
|
|
113
104
|
Adminizer.log.debug(`Record was updated: `, newRecord);
|
|
114
105
|
if (req.body.jsonPopupCatalog) {
|
|
115
106
|
return res.json({ record: newRecord });
|
|
@@ -120,19 +111,15 @@ export default async function edit(req, res) {
|
|
|
120
111
|
for (const section of req.adminizer.config.navigation.sections) {
|
|
121
112
|
let navigation = req.adminizer.catalogHandler.getCatalog('navigation');
|
|
122
113
|
navigation.setId(section);
|
|
123
|
-
let navItem = navigation.itemTypes.find(item => item.type ===
|
|
114
|
+
let navItem = navigation.itemTypes.find(item => item.type === modelResource.name.toLowerCase());
|
|
124
115
|
if (navItem) {
|
|
125
116
|
await navItem.updateModelItems(newRecord[0].id, { record: newRecord[0] }, section);
|
|
126
117
|
}
|
|
127
118
|
}
|
|
128
119
|
}
|
|
129
120
|
req.flash.setFlashMessage('success', req.i18n.__('Record was updated'));
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
}
|
|
133
|
-
else {
|
|
134
|
-
return req.Inertia.redirect(`${req.adminizer.config.routePrefix}/model/${entity.name}`);
|
|
135
|
-
}
|
|
121
|
+
const redirectId = newRecord[0]?.[identifierField] ?? req.params.id;
|
|
122
|
+
return req.Inertia.redirect(`${req.adminizer.config.routePrefix}/model/${modelResource.name}/edit/${redirectId}`);
|
|
136
123
|
}
|
|
137
124
|
}
|
|
138
125
|
catch (e) {
|
|
@@ -143,16 +130,16 @@ export default async function edit(req, res) {
|
|
|
143
130
|
} // END POST
|
|
144
131
|
for (const field of Object.keys(fields)) {
|
|
145
132
|
const fieldConfigConfig = fields[field].config;
|
|
146
|
-
if (fieldConfigConfig
|
|
133
|
+
if (isMediaManagerFieldConfig(fieldConfigConfig)) {
|
|
147
134
|
record[field] = await getRelationsMediaManager(req.adminizer, {
|
|
148
135
|
mediaManagerId: fieldConfigConfig.options?.id ?? "default",
|
|
149
|
-
model:
|
|
136
|
+
model: modelResource.model.modelname,
|
|
150
137
|
widgetName: field,
|
|
151
138
|
modelId: id
|
|
152
139
|
});
|
|
153
140
|
}
|
|
154
141
|
}
|
|
155
|
-
const props = inertiaAddHelper(req,
|
|
142
|
+
const props = inertiaAddHelper(req, modelResource, fields, record);
|
|
156
143
|
if (req.query?.without_layout) {
|
|
157
144
|
return res.json({
|
|
158
145
|
props: props
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export default function editGroup(req: ReqType, res: ResType): Promise<
|
|
1
|
+
export default function editGroup(req: ReqType, res: ResType): Promise<import("express").Response<any, Record<string, any>>>;
|
package/controllers/editGroup.js
CHANGED
|
@@ -1,33 +1,25 @@
|
|
|
1
1
|
import { ControllerHelper } from "../helpers/controllerHelper.js";
|
|
2
2
|
import { Adminizer } from "../lib/Adminizer.js";
|
|
3
3
|
import { inertiaGroupHelper } from "../helpers/inertiaGroupHelper.js";
|
|
4
|
-
import { redirectToLogin } from '../helpers/inertiaAutHelper.js';
|
|
5
4
|
export default async function editGroup(req, res) {
|
|
6
|
-
let
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}
|
|
11
|
-
else if (!req.adminizer.accessRightsHelper.hasPermission(`update-${entity.name}-model`, req.user)) {
|
|
12
|
-
return res.sendStatus(403);
|
|
13
|
-
}
|
|
14
|
-
}
|
|
5
|
+
let modelResource = ControllerHelper.findModelResource(req);
|
|
6
|
+
const internalUsers = req.adminizer.modelHandler.internal("users");
|
|
7
|
+
const userModel = internalUsers.get("UserAP");
|
|
8
|
+
const groupModel = internalUsers.get("GroupAP");
|
|
15
9
|
//Check id
|
|
16
10
|
if (!req.params.id) {
|
|
17
11
|
return res.status(404).send({ error: 'Not Found' });
|
|
18
12
|
}
|
|
19
13
|
let users;
|
|
20
14
|
try {
|
|
21
|
-
|
|
22
|
-
users = await req.adminizer.modelHandler.model.get("UserAP")["_find"](({ isAdministrator: false }));
|
|
15
|
+
users = await userModel.find({ where: { isAdministrator: false } });
|
|
23
16
|
}
|
|
24
17
|
catch (e) {
|
|
25
18
|
Adminizer.log.error(e);
|
|
26
19
|
}
|
|
27
20
|
let group;
|
|
28
21
|
try {
|
|
29
|
-
|
|
30
|
-
group = await req.adminizer.modelHandler.model.get("GroupAP")["_findOne"]({ id: req.params.id });
|
|
22
|
+
group = await groupModel.findOne({ where: { id: Number(req.params.id) } });
|
|
31
23
|
}
|
|
32
24
|
catch (e) {
|
|
33
25
|
Adminizer.log.error('Admin edit error: ');
|
|
@@ -62,8 +54,7 @@ export default async function editGroup(req, res) {
|
|
|
62
54
|
}
|
|
63
55
|
let updatedGroup;
|
|
64
56
|
try {
|
|
65
|
-
|
|
66
|
-
updatedGroup = await req.adminizer.modelHandler.model.get("GroupAP")["_updateOne"]({ id: group.id }, {
|
|
57
|
+
updatedGroup = await groupModel.updateOne({ where: { id: group.id } }, {
|
|
67
58
|
name: req.body.name, description: req.body.description,
|
|
68
59
|
users: usersInThisGroup, tokens: tokensOfThisGroup
|
|
69
60
|
});
|
|
@@ -79,8 +70,7 @@ export default async function editGroup(req, res) {
|
|
|
79
70
|
}
|
|
80
71
|
if (reloadNeeded) {
|
|
81
72
|
try {
|
|
82
|
-
|
|
83
|
-
group = await req.adminizer.modelHandler.model.get("GroupAP")["_findOne"]({ id: req.params.id });
|
|
73
|
+
group = await groupModel.findOne({ where: { id: Number(req.params.id) } });
|
|
84
74
|
}
|
|
85
75
|
catch (e) {
|
|
86
76
|
Adminizer.log.error('Admin edit error: ');
|
|
@@ -88,14 +78,13 @@ export default async function editGroup(req, res) {
|
|
|
88
78
|
return res.status(500).send({ error: 'Internal Server Error' });
|
|
89
79
|
}
|
|
90
80
|
try {
|
|
91
|
-
|
|
92
|
-
users = await req.adminizer.modelHandler.model.get("UserAP")["_find"]({ isAdministrator: false });
|
|
81
|
+
users = await userModel.find({ where: { isAdministrator: false } });
|
|
93
82
|
}
|
|
94
83
|
catch (e) {
|
|
95
84
|
Adminizer.log.error(e);
|
|
96
85
|
}
|
|
97
86
|
}
|
|
98
|
-
const props = inertiaGroupHelper(
|
|
87
|
+
const props = inertiaGroupHelper(modelResource, req, users, groupedTokens, group);
|
|
99
88
|
return req.Inertia.render({
|
|
100
89
|
component: 'add-group',
|
|
101
90
|
props: props
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export default function (req: ReqType, res: ResType): Promise<
|
|
1
|
+
export default function (req: ReqType, res: ResType): Promise<import("express").Response<any, Record<string, any>>>;
|
package/controllers/editUser.js
CHANGED
|
@@ -2,25 +2,18 @@ import { ControllerHelper } from "../helpers/controllerHelper.js";
|
|
|
2
2
|
import { Adminizer } from "../lib/Adminizer.js";
|
|
3
3
|
import { generate } from "password-hash";
|
|
4
4
|
import { inertiaUserHelper } from "../helpers/inertiaUserHelper.js";
|
|
5
|
-
import { redirectToLogin } from '../helpers/inertiaAutHelper.js';
|
|
6
5
|
export default async function (req, res) {
|
|
7
|
-
let
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
}
|
|
12
|
-
else if (!req.adminizer.accessRightsHelper.hasPermission(`update-${entity.name}-model`, req.user)) {
|
|
13
|
-
return res.sendStatus(403);
|
|
14
|
-
}
|
|
15
|
-
}
|
|
6
|
+
let modelResource = ControllerHelper.findModelResource(req);
|
|
7
|
+
const internalUsers = req.adminizer.modelHandler.internal("users");
|
|
8
|
+
const userModel = internalUsers.get("UserAP");
|
|
9
|
+
const groupModel = internalUsers.get("GroupAP");
|
|
16
10
|
// Check id
|
|
17
11
|
if (!req.params.id) {
|
|
18
12
|
return res.status(404).send({ error: 'Not Found' });
|
|
19
13
|
}
|
|
20
14
|
let user;
|
|
21
15
|
try {
|
|
22
|
-
|
|
23
|
-
user = await req.adminizer.modelHandler.model.get("UserAP")["_findOne"]({ id: req.params.id });
|
|
16
|
+
user = await userModel.findOne({ where: { id: Number(req.params.id) } });
|
|
24
17
|
}
|
|
25
18
|
catch (e) {
|
|
26
19
|
Adminizer.log.error('Admin edit error: ');
|
|
@@ -29,8 +22,7 @@ export default async function (req, res) {
|
|
|
29
22
|
}
|
|
30
23
|
let groups;
|
|
31
24
|
try {
|
|
32
|
-
|
|
33
|
-
groups = await req.adminizer.modelHandler.model.get("GroupAP")["_find"]({});
|
|
25
|
+
groups = await groupModel.find({});
|
|
34
26
|
}
|
|
35
27
|
catch (e) {
|
|
36
28
|
Adminizer.log.error(e);
|
|
@@ -55,8 +47,7 @@ export default async function (req, res) {
|
|
|
55
47
|
}
|
|
56
48
|
let updatedUser;
|
|
57
49
|
try {
|
|
58
|
-
|
|
59
|
-
updatedUser = await req.adminizer.modelHandler.model.get("UserAP")["_updateOne"]({ id: user.id }, {
|
|
50
|
+
updatedUser = await userModel.updateOne({ where: { id: user.id } }, {
|
|
60
51
|
login: req.body.login, fullName: req.body.fullName,
|
|
61
52
|
email: req.body.email, timezone: req.body.timezone, expires: req.body.date,
|
|
62
53
|
locale: locale, isAdministrator: isAdministrator, isConfirmed: isConfirmed, groups: userGroups
|
|
@@ -64,8 +55,7 @@ export default async function (req, res) {
|
|
|
64
55
|
if (req.body.userPassword) {
|
|
65
56
|
let passwordHashed = generate(req.body.login + req.body.userPassword + process.env.AP_PASSWORD_SALT);
|
|
66
57
|
let password = 'masked';
|
|
67
|
-
|
|
68
|
-
updatedUser = await req.adminizer.modelHandler.model.get("UserAP")["_updateOne"]({ id: user.id }, {
|
|
58
|
+
updatedUser = await userModel.updateOne({ where: { id: user.id } }, {
|
|
69
59
|
login: req.body.login,
|
|
70
60
|
passwordHashed: passwordHashed
|
|
71
61
|
});
|
|
@@ -82,8 +72,7 @@ export default async function (req, res) {
|
|
|
82
72
|
}
|
|
83
73
|
if (reloadNeeded) {
|
|
84
74
|
try {
|
|
85
|
-
|
|
86
|
-
user = await req.adminizer.modelHandler.model.get("UserAP")["_findOne"]({ id: req.params.id });
|
|
75
|
+
user = await userModel.findOne({ where: { id: Number(req.params.id) } });
|
|
87
76
|
}
|
|
88
77
|
catch (e) {
|
|
89
78
|
Adminizer.log.error('Admin edit error: ');
|
|
@@ -91,14 +80,13 @@ export default async function (req, res) {
|
|
|
91
80
|
return res.status(500).send({ error: 'Internal Server Error' });
|
|
92
81
|
}
|
|
93
82
|
try {
|
|
94
|
-
|
|
95
|
-
groups = await req.adminizer.modelHandler.model.get("GroupAP")["_find"]({});
|
|
83
|
+
groups = await groupModel.find({});
|
|
96
84
|
}
|
|
97
85
|
catch (e) {
|
|
98
86
|
Adminizer.log.error(e);
|
|
99
87
|
}
|
|
100
88
|
}
|
|
101
|
-
const props = inertiaUserHelper(
|
|
89
|
+
const props = inertiaUserHelper(modelResource, req, groups, user);
|
|
102
90
|
return req.Inertia.render({
|
|
103
91
|
component: 'add-user',
|
|
104
92
|
props: props
|
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
import { ControllerHelper } from "../helpers/controllerHelper.js";
|
|
2
|
+
import { DataAccessor } from "../lib/DataAccessor.js";
|
|
3
|
+
import { ListQueryBuilder } from "../lib/list-query-builder/ListQueryBuilder.js";
|
|
4
|
+
import { Adminizer } from "../lib/Adminizer.js";
|
|
5
|
+
import { FilterService } from "../lib/filters/FilterService.js";
|
|
6
|
+
import { convertDatetimeConditions } from "../helpers/filterDatetimeHelper.js";
|
|
7
|
+
/**
|
|
8
|
+
* Main export controller
|
|
9
|
+
* POST /adminizer/model/:model/export
|
|
10
|
+
* Body: { filterId, format, fields }
|
|
11
|
+
*/
|
|
12
|
+
export default async function exportData(req, res) {
|
|
13
|
+
const modelResource = ControllerHelper.findModelResource(req);
|
|
14
|
+
if (!modelResource.model) {
|
|
15
|
+
return res.status(404).json({ error: 'Model not found' });
|
|
16
|
+
}
|
|
17
|
+
const { filterId, format, selectedFields: bodySelectedFields } = req.body;
|
|
18
|
+
// Validate format
|
|
19
|
+
const exportFormat = format || 'json';
|
|
20
|
+
if (!['json', 'csv', 'xlsx'].includes(exportFormat)) {
|
|
21
|
+
return res.status(400).json({ error: 'Invalid export format. Supported: json, csv, xlsx' });
|
|
22
|
+
}
|
|
23
|
+
const dataAccessor = new DataAccessor(req.adminizer, req.user, modelResource, "list");
|
|
24
|
+
const fields = dataAccessor.getFieldsConfig();
|
|
25
|
+
// Load filter if provided
|
|
26
|
+
let filters = [];
|
|
27
|
+
let savedColumns = [];
|
|
28
|
+
if (filterId) {
|
|
29
|
+
try {
|
|
30
|
+
if (filterId === 'temporary' && req.session?.temporaryFilters?.[modelResource.name]) {
|
|
31
|
+
const tempFilter = req.session.temporaryFilters[modelResource.name];
|
|
32
|
+
filters = convertDatetimeConditions(tempFilter.conditions || [], { dropEmptyValues: true });
|
|
33
|
+
if (tempFilter.columns && Array.isArray(tempFilter.columns)) {
|
|
34
|
+
savedColumns = tempFilter.columns.map((col, index) => ({
|
|
35
|
+
id: index + 1,
|
|
36
|
+
filter: 'temporary',
|
|
37
|
+
fieldName: col.fieldName,
|
|
38
|
+
order: col.order !== undefined ? col.order : index
|
|
39
|
+
}));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
else if (filterId !== 'temporary') {
|
|
43
|
+
const filterService = new FilterService(req.adminizer);
|
|
44
|
+
const result = await filterService.getFilterWithColumns(filterId, req.user);
|
|
45
|
+
if (result.filter && result.filter.conditions) {
|
|
46
|
+
filters = convertDatetimeConditions(result.filter.conditions, { dropEmptyValues: true });
|
|
47
|
+
}
|
|
48
|
+
savedColumns = result.columns || [];
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
Adminizer.log.error('Error loading filter for export:', err);
|
|
53
|
+
return res.status(500).json({ error: 'Error loading filter' });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// Determine fields to export
|
|
57
|
+
let displayFields = fields;
|
|
58
|
+
// Apply selected fields from request body
|
|
59
|
+
if (bodySelectedFields && Array.isArray(bodySelectedFields) && bodySelectedFields.length > 0) {
|
|
60
|
+
const validFields = bodySelectedFields.filter(f => fields[f]);
|
|
61
|
+
if (validFields.length > 0) {
|
|
62
|
+
displayFields = validFields.reduce((acc, key) => {
|
|
63
|
+
if (fields[key]) {
|
|
64
|
+
acc[key] = fields[key];
|
|
65
|
+
}
|
|
66
|
+
return acc;
|
|
67
|
+
}, {});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// Apply custom columns from filter
|
|
71
|
+
if (savedColumns.length > 0) {
|
|
72
|
+
displayFields = applyCustomColumnsForExport(displayFields, savedColumns);
|
|
73
|
+
}
|
|
74
|
+
// Fetch ALL data (no pagination)
|
|
75
|
+
const queryParams = {
|
|
76
|
+
page: 1,
|
|
77
|
+
limit: 10000, // Large limit to get all records
|
|
78
|
+
filters: filters.length > 0 ? filters : undefined,
|
|
79
|
+
sort: 'id',
|
|
80
|
+
sortDirection: 'ASC',
|
|
81
|
+
fields: Object.keys(displayFields)
|
|
82
|
+
};
|
|
83
|
+
const listQueryBuilder = new ListQueryBuilder(modelResource.model, fields, dataAccessor, req.adminizer.customFilterHandler);
|
|
84
|
+
try {
|
|
85
|
+
const result = await listQueryBuilder.execute(queryParams);
|
|
86
|
+
const records = result.data;
|
|
87
|
+
// Prepare human-readable data for export
|
|
88
|
+
const exportData = prepareExportData(records, displayFields, req);
|
|
89
|
+
// Generate file based on format
|
|
90
|
+
switch (exportFormat) {
|
|
91
|
+
case 'json':
|
|
92
|
+
return sendJsonExport(res, exportData, modelResource.name);
|
|
93
|
+
case 'csv':
|
|
94
|
+
return sendCsvExport(res, exportData, modelResource.name, displayFields, req);
|
|
95
|
+
case 'xlsx':
|
|
96
|
+
return sendXlsxExport(res, exportData, modelResource.name, displayFields, req);
|
|
97
|
+
default:
|
|
98
|
+
return res.status(400).json({ error: 'Invalid export format' });
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
catch (err) {
|
|
102
|
+
Adminizer.log.error('Export error:', err);
|
|
103
|
+
return res.status(500).json({ error: 'Export failed', message: err.message });
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Apply custom column configuration for export
|
|
108
|
+
*/
|
|
109
|
+
function applyCustomColumnsForExport(fields, columns) {
|
|
110
|
+
const sortedColumns = [...columns].sort((a, b) => (a.order || 0) - (b.order || 0));
|
|
111
|
+
const visibleColumns = sortedColumns.filter(col => fields[col.fieldName]);
|
|
112
|
+
if (visibleColumns.length === 0) {
|
|
113
|
+
return fields;
|
|
114
|
+
}
|
|
115
|
+
const result = {};
|
|
116
|
+
for (const col of visibleColumns) {
|
|
117
|
+
if (fields[col.fieldName]) {
|
|
118
|
+
result[col.fieldName] = fields[col.fieldName];
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return result;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Prepare data for export - convert values to human-readable format
|
|
125
|
+
*/
|
|
126
|
+
function prepareExportData(records, displayFields, req) {
|
|
127
|
+
return records.map(record => {
|
|
128
|
+
const row = {};
|
|
129
|
+
for (const [key, field] of Object.entries(displayFields)) {
|
|
130
|
+
const fieldConfig = field;
|
|
131
|
+
const config = fieldConfig.config;
|
|
132
|
+
const title = (typeof config === 'object' && config !== null && config.title)
|
|
133
|
+
? req.i18n.__(config.title)
|
|
134
|
+
: key;
|
|
135
|
+
const value = record[key];
|
|
136
|
+
// Format value based on field type
|
|
137
|
+
row[title] = formatExportValue(value, fieldConfig);
|
|
138
|
+
}
|
|
139
|
+
return row;
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Format value for export based on type
|
|
144
|
+
*/
|
|
145
|
+
function formatExportValue(value, field) {
|
|
146
|
+
if (value === null || value === undefined) {
|
|
147
|
+
return '';
|
|
148
|
+
}
|
|
149
|
+
const fieldType = field.model?.type;
|
|
150
|
+
// Handle relationships
|
|
151
|
+
if (field.model?.model) {
|
|
152
|
+
const displayField = field.modelConfig?.titleField ??
|
|
153
|
+
(typeof value === 'object' && value ? (value.title || value.name || value.id) : value);
|
|
154
|
+
return typeof displayField === 'object' ? JSON.stringify(displayField) : String(displayField);
|
|
155
|
+
}
|
|
156
|
+
// Handle associations (hasMany)
|
|
157
|
+
if (fieldType === 'association-many' || fieldType === 'association') {
|
|
158
|
+
if (Array.isArray(value)) {
|
|
159
|
+
return value.map((item) => {
|
|
160
|
+
if (typeof item === 'object' && item) {
|
|
161
|
+
return item.title || item.name || item.id || JSON.stringify(item);
|
|
162
|
+
}
|
|
163
|
+
return String(item);
|
|
164
|
+
}).join(', ');
|
|
165
|
+
}
|
|
166
|
+
return String(value);
|
|
167
|
+
}
|
|
168
|
+
// Handle JSON fields
|
|
169
|
+
if (fieldType === 'json') {
|
|
170
|
+
if (typeof value === 'object') {
|
|
171
|
+
return JSON.stringify(value, null, 2);
|
|
172
|
+
}
|
|
173
|
+
return String(value);
|
|
174
|
+
}
|
|
175
|
+
// Handle boolean
|
|
176
|
+
if (fieldType === 'boolean') {
|
|
177
|
+
return value ? 'Yes' : 'No';
|
|
178
|
+
}
|
|
179
|
+
// Handle dates
|
|
180
|
+
if (fieldType === 'date' || fieldType === 'datetime') {
|
|
181
|
+
if (value instanceof Date) {
|
|
182
|
+
return value.toISOString();
|
|
183
|
+
}
|
|
184
|
+
return String(value);
|
|
185
|
+
}
|
|
186
|
+
// Handle arrays
|
|
187
|
+
if (Array.isArray(value)) {
|
|
188
|
+
return value.join(', ');
|
|
189
|
+
}
|
|
190
|
+
// Default: convert to string
|
|
191
|
+
return String(value);
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Send JSON export response
|
|
195
|
+
*/
|
|
196
|
+
function sendJsonExport(res, data, modelName) {
|
|
197
|
+
const filename = `${modelName}_export_${Date.now()}.json`;
|
|
198
|
+
res.setHeader('Content-Type', 'application/json');
|
|
199
|
+
res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);
|
|
200
|
+
res.setHeader('Content-Length', Buffer.byteLength(JSON.stringify(data, null, 2)));
|
|
201
|
+
return res.json(data);
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Escape value for CSV
|
|
205
|
+
*/
|
|
206
|
+
function escapeCsvValue(value) {
|
|
207
|
+
if (value === null || value === undefined) {
|
|
208
|
+
return '';
|
|
209
|
+
}
|
|
210
|
+
const str = String(value);
|
|
211
|
+
// If value contains comma, newline, or quote, wrap in quotes
|
|
212
|
+
if (str.includes(',') || str.includes('\n') || str.includes('"') || str.includes('\r')) {
|
|
213
|
+
return `"${str.replace(/"/g, '""')}"`;
|
|
214
|
+
}
|
|
215
|
+
return str;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Send CSV export response
|
|
219
|
+
*/
|
|
220
|
+
function sendCsvExport(res, data, modelName, fields, req) {
|
|
221
|
+
const filename = `${modelName}_export_${Date.now()}.csv`;
|
|
222
|
+
// Get headers (preserve order from fields)
|
|
223
|
+
const headers = Object.keys(fields).map(key => {
|
|
224
|
+
const field = fields[key];
|
|
225
|
+
const config = field.config;
|
|
226
|
+
return (typeof config === 'object' && config !== null && config.title)
|
|
227
|
+
? req.i18n.__(config.title)
|
|
228
|
+
: key;
|
|
229
|
+
});
|
|
230
|
+
// Build CSV content
|
|
231
|
+
const csvRows = [];
|
|
232
|
+
// Add BOM for Excel compatibility with UTF-8
|
|
233
|
+
const bom = '\uFEFF';
|
|
234
|
+
// Header row
|
|
235
|
+
csvRows.push(headers.map(h => escapeCsvValue(h)).join(','));
|
|
236
|
+
// Data rows
|
|
237
|
+
for (const row of data) {
|
|
238
|
+
const values = headers.map(header => {
|
|
239
|
+
const value = row[header];
|
|
240
|
+
return escapeCsvValue(value);
|
|
241
|
+
});
|
|
242
|
+
csvRows.push(values.join(','));
|
|
243
|
+
}
|
|
244
|
+
const csvContent = bom + csvRows.join('\r\n');
|
|
245
|
+
const buffer = Buffer.from(csvContent, 'utf-8');
|
|
246
|
+
res.setHeader('Content-Type', 'text/csv; charset=utf-8');
|
|
247
|
+
res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);
|
|
248
|
+
res.setHeader('Content-Length', buffer.length);
|
|
249
|
+
return res.send(buffer);
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Send XLSX export response
|
|
253
|
+
* Uses xlsx library if available
|
|
254
|
+
*/
|
|
255
|
+
async function sendXlsxExport(res, data, modelName, fields, req) {
|
|
256
|
+
let XLSX;
|
|
257
|
+
try {
|
|
258
|
+
XLSX = await import('xlsx');
|
|
259
|
+
}
|
|
260
|
+
catch (err) {
|
|
261
|
+
Adminizer.log.error('xlsx library not installed. Install with: npm install xlsx');
|
|
262
|
+
return res.status(500).json({
|
|
263
|
+
error: 'XLSX export requires xlsx library',
|
|
264
|
+
message: 'Please install xlsx: npm install xlsx'
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
const filename = `${modelName}_export_${Date.now()}.xlsx`;
|
|
268
|
+
// Get headers (preserve order from fields)
|
|
269
|
+
const headers = Object.keys(fields).map(key => {
|
|
270
|
+
const field = fields[key];
|
|
271
|
+
const config = field.config;
|
|
272
|
+
return (typeof config === 'object' && config !== null && config.title)
|
|
273
|
+
? req.i18n.__(config.title)
|
|
274
|
+
: key;
|
|
275
|
+
});
|
|
276
|
+
// Create workbook
|
|
277
|
+
const wb = XLSX.default.utils.book_new();
|
|
278
|
+
// Prepare data array with headers
|
|
279
|
+
const wsData = [headers];
|
|
280
|
+
// Add rows
|
|
281
|
+
for (const row of data) {
|
|
282
|
+
const values = headers.map(header => {
|
|
283
|
+
const value = row[header];
|
|
284
|
+
if (value === '' || value === null || value === undefined) {
|
|
285
|
+
return null;
|
|
286
|
+
}
|
|
287
|
+
return value;
|
|
288
|
+
});
|
|
289
|
+
wsData.push(values);
|
|
290
|
+
}
|
|
291
|
+
// Create worksheet
|
|
292
|
+
const ws = XLSX.default.utils.aoa_to_sheet(wsData);
|
|
293
|
+
// Auto-size columns (optional)
|
|
294
|
+
const colWidths = headers.map((header, i) => {
|
|
295
|
+
let maxWidth = header.length;
|
|
296
|
+
for (const row of data) {
|
|
297
|
+
const val = String(row[header] || '');
|
|
298
|
+
if (val.length > maxWidth) {
|
|
299
|
+
maxWidth = val.length;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
// Cap at 50 characters, minimum 10
|
|
303
|
+
return { wch: Math.min(Math.max(maxWidth + 2, 10), 50) };
|
|
304
|
+
});
|
|
305
|
+
ws['!cols'] = colWidths;
|
|
306
|
+
// Add worksheet to workbook
|
|
307
|
+
const sheetName = modelName.substring(0, 31); // Excel sheet name limit
|
|
308
|
+
XLSX.default.utils.book_append_sheet(wb, ws, sheetName);
|
|
309
|
+
// Generate buffer
|
|
310
|
+
const buffer = XLSX.default.write(wb, { type: 'buffer', bookType: 'xlsx' });
|
|
311
|
+
res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
|
|
312
|
+
res.setHeader('Content-Disposition', `attachment; filename="${filename}"`);
|
|
313
|
+
res.setHeader('Content-Length', buffer.length);
|
|
314
|
+
return res.send(buffer);
|
|
315
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public feed controller — exports data by apiKey with userKey authentication
|
|
3
|
+
* GET /adminizer/api/feed/:apiKey.json?userKey=xxx
|
|
4
|
+
* GET /adminizer/api/feed/:apiKey.xml?userKey=xxx
|
|
5
|
+
*/
|
|
6
|
+
export default function feedController(req: ReqType, res: ResType): Promise<ResType>;
|