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
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import { ControllerHelper } from "../../helpers/controllerHelper.js";
|
|
2
|
+
import { FilterService } from "../../lib/filters/FilterService.js";
|
|
3
|
+
import { Adminizer } from "../../lib/Adminizer.js";
|
|
4
|
+
import { GROUP_FILTER_VISIBILITY_TOKEN } from "../../policies/permissionResolvers.js";
|
|
5
|
+
/**
|
|
6
|
+
* POST /adminizer/model/:model/filter/apply
|
|
7
|
+
* Apply temporary filter (without saving) - stores filter in session keyed by model name
|
|
8
|
+
*/
|
|
9
|
+
export async function applyTemporaryFilter(req, res) {
|
|
10
|
+
const modelResource = ControllerHelper.findModelResource(req);
|
|
11
|
+
if (!modelResource.model) {
|
|
12
|
+
return res.status(404).send({ error: req.i18n.__('Model not found') });
|
|
13
|
+
}
|
|
14
|
+
const { name, conditions, columns } = req.body;
|
|
15
|
+
if (!conditions || !Array.isArray(conditions)) {
|
|
16
|
+
return res.status(400).send({ error: req.i18n.__('Conditions are required') });
|
|
17
|
+
}
|
|
18
|
+
// Store temporary filter in session keyed by model name
|
|
19
|
+
if (req.session) {
|
|
20
|
+
if (!req.session.temporaryFilters) {
|
|
21
|
+
req.session.temporaryFilters = {};
|
|
22
|
+
}
|
|
23
|
+
req.session.temporaryFilters[modelResource.name] = {
|
|
24
|
+
name: name || req.i18n.__('Temporary filter'),
|
|
25
|
+
conditions,
|
|
26
|
+
columns: columns || undefined
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
return res.json({
|
|
30
|
+
success: true,
|
|
31
|
+
filterId: 'temporary',
|
|
32
|
+
name: name || req.i18n.__('Temporary filter'),
|
|
33
|
+
conditionCount: conditions.length
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* GET /adminizer/model/:model/filter/temporary
|
|
38
|
+
* Returns temporary filter from server session.
|
|
39
|
+
*/
|
|
40
|
+
export async function getTemporaryFilter(req, res) {
|
|
41
|
+
const modelResource = ControllerHelper.findModelResource(req);
|
|
42
|
+
if (!modelResource.model) {
|
|
43
|
+
return res.status(404).send({ error: req.i18n.__('Model not found') });
|
|
44
|
+
}
|
|
45
|
+
const temporaryFilter = req.session?.temporaryFilters?.[modelResource.name];
|
|
46
|
+
if (!temporaryFilter) {
|
|
47
|
+
return res.status(404).send({ error: req.i18n.__('Temporary filter not found') });
|
|
48
|
+
}
|
|
49
|
+
return res.json({
|
|
50
|
+
filter: {
|
|
51
|
+
id: 'temporary',
|
|
52
|
+
name: temporaryFilter.name,
|
|
53
|
+
conditions: temporaryFilter.conditions || [],
|
|
54
|
+
columns: temporaryFilter.columns || undefined
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* GET /adminizer/model/:model/saved-filters
|
|
60
|
+
* Returns list of saved filters for the model
|
|
61
|
+
*/
|
|
62
|
+
export async function getSavedFilters(req, res) {
|
|
63
|
+
const modelResource = ControllerHelper.findModelResource(req);
|
|
64
|
+
if (!modelResource.model) {
|
|
65
|
+
return res.status(404).send({ error: req.i18n.__('Model not found') });
|
|
66
|
+
}
|
|
67
|
+
const filterService = new FilterService(req.adminizer);
|
|
68
|
+
// Get filters accessible by user (public filters for all users)
|
|
69
|
+
const filters = await filterService.getFiltersForModel(modelResource.name, req.user, {
|
|
70
|
+
includePublic: true,
|
|
71
|
+
includeSystem: false
|
|
72
|
+
});
|
|
73
|
+
// Load owner info for each filter
|
|
74
|
+
const userModel = req.adminizer.modelHandler.model.get('userap');
|
|
75
|
+
const filtersWithOwner = await Promise.all(filters.map(async (filter) => {
|
|
76
|
+
let ownerInfo = null;
|
|
77
|
+
if (userModel && filter.ownerId) {
|
|
78
|
+
try {
|
|
79
|
+
const owner = await userModel['_findOne']({ where: { id: filter.ownerId } });
|
|
80
|
+
if (owner) {
|
|
81
|
+
ownerInfo = {
|
|
82
|
+
id: owner.id,
|
|
83
|
+
login: owner.login,
|
|
84
|
+
fullName: owner.fullName
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
catch (e) {
|
|
89
|
+
// Ignore errors loading owner
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
...filter,
|
|
94
|
+
ownerInfo
|
|
95
|
+
};
|
|
96
|
+
}));
|
|
97
|
+
// Get result count for each filter
|
|
98
|
+
const filtersWithCount = await Promise.all(filtersWithOwner.map(async (filter) => {
|
|
99
|
+
const count = await filterService.countFilterResults(filter, req.user);
|
|
100
|
+
return {
|
|
101
|
+
...filter,
|
|
102
|
+
resultCount: count
|
|
103
|
+
};
|
|
104
|
+
}));
|
|
105
|
+
return res.json({
|
|
106
|
+
model: modelResource.name,
|
|
107
|
+
filters: filtersWithCount
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* POST /adminizer/model/:model/filter
|
|
112
|
+
* Create or update a saved filter
|
|
113
|
+
*/
|
|
114
|
+
export async function saveFilter(req, res) {
|
|
115
|
+
const modelResource = ControllerHelper.findModelResource(req);
|
|
116
|
+
if (!modelResource.model) {
|
|
117
|
+
return res.status(404).send({ error: req.i18n.__('Model not found') });
|
|
118
|
+
}
|
|
119
|
+
const { name, description, conditions, sortField, sortDirection, icon, color, filterId, columns, visibility, groupIds } = req.body;
|
|
120
|
+
if (!name) {
|
|
121
|
+
return res.status(400).send({ error: req.i18n.__('Filter name is required') });
|
|
122
|
+
}
|
|
123
|
+
if (!conditions || !Array.isArray(conditions)) {
|
|
124
|
+
return res.status(400).send({ error: req.i18n.__('Filter conditions are required') });
|
|
125
|
+
}
|
|
126
|
+
// Determine visibility and groupIds
|
|
127
|
+
const isAdmin = req.user?.isAdministrator === true;
|
|
128
|
+
const canManageGroupVisibility = isAdmin || req.adminizer.accessRightsHelper.hasPermission(GROUP_FILTER_VISIBILITY_TOKEN, req.user);
|
|
129
|
+
let effectiveVisibility = 'private';
|
|
130
|
+
let effectiveGroupIds;
|
|
131
|
+
if (visibility === 'public' && isAdmin) {
|
|
132
|
+
effectiveVisibility = 'public';
|
|
133
|
+
}
|
|
134
|
+
else if (visibility === 'groups' && canManageGroupVisibility) {
|
|
135
|
+
// Groups visibility: only if groupIds provided and user has access to those groups
|
|
136
|
+
if (groupIds && Array.isArray(groupIds) && groupIds.length > 0) {
|
|
137
|
+
effectiveVisibility = 'groups';
|
|
138
|
+
effectiveGroupIds = groupIds;
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
effectiveVisibility = 'private';
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
effectiveVisibility = 'private';
|
|
146
|
+
}
|
|
147
|
+
const filterService = new FilterService(req.adminizer);
|
|
148
|
+
try {
|
|
149
|
+
// Check if filter with same name exists for this user
|
|
150
|
+
const existingFilters = await filterService.getFiltersForModel(modelResource.name, req.user, {
|
|
151
|
+
includePublic: false,
|
|
152
|
+
includeSystem: false
|
|
153
|
+
});
|
|
154
|
+
const existingFilter = existingFilters.find(f => f.name === name && f.ownerId === req.user.id);
|
|
155
|
+
if (existingFilter && !filterId) {
|
|
156
|
+
// Filter with same name exists, ask for confirmation
|
|
157
|
+
return res.status(409).json({
|
|
158
|
+
error: req.i18n.__('Filter with this name already exists'),
|
|
159
|
+
requiresConfirmation: true,
|
|
160
|
+
existingFilter: {
|
|
161
|
+
id: existingFilter.id,
|
|
162
|
+
name: existingFilter.name
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
let filter;
|
|
167
|
+
if (filterId && existingFilter && existingFilter.id === filterId) {
|
|
168
|
+
// Update own existing filter - allow changing visibility
|
|
169
|
+
const updateData = {
|
|
170
|
+
name,
|
|
171
|
+
description,
|
|
172
|
+
conditions,
|
|
173
|
+
sortField,
|
|
174
|
+
sortDirection,
|
|
175
|
+
icon,
|
|
176
|
+
color,
|
|
177
|
+
visibility: effectiveVisibility,
|
|
178
|
+
groupIds: effectiveGroupIds
|
|
179
|
+
};
|
|
180
|
+
// If visibility changed to non-private, disable API feed
|
|
181
|
+
const wasPrivate = existingFilter.visibility === 'private';
|
|
182
|
+
const isNowPrivate = effectiveVisibility === 'private';
|
|
183
|
+
if (wasPrivate && !isNowPrivate) {
|
|
184
|
+
// Changed from private to non-private - disable feed
|
|
185
|
+
updateData.apiEnabled = false;
|
|
186
|
+
updateData.apiKey = undefined;
|
|
187
|
+
}
|
|
188
|
+
else if (!isNowPrivate) {
|
|
189
|
+
// Not private - ensure feed is disabled
|
|
190
|
+
updateData.apiEnabled = false;
|
|
191
|
+
updateData.apiKey = undefined;
|
|
192
|
+
}
|
|
193
|
+
// Handle API access toggle - only allowed for private filters owned by current user
|
|
194
|
+
const apiEnabled = req.body.apiEnabled === true;
|
|
195
|
+
const isOwner = existingFilter.ownerId === req.user.id;
|
|
196
|
+
const isPrivate = effectiveVisibility === 'private';
|
|
197
|
+
// API feed can only be enabled for private filters owned by the current user
|
|
198
|
+
if (isPrivate && isOwner && apiEnabled) {
|
|
199
|
+
updateData.apiEnabled = true;
|
|
200
|
+
// Regenerate key if requested
|
|
201
|
+
if (req.body.regenerateApiKey) {
|
|
202
|
+
updateData.apiKey = crypto.randomUUID();
|
|
203
|
+
}
|
|
204
|
+
else if (!existingFilter.apiKey) {
|
|
205
|
+
// Generate key if enabling for first time
|
|
206
|
+
updateData.apiKey = crypto.randomUUID();
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
// Keep existing key
|
|
210
|
+
updateData.apiKey = existingFilter.apiKey;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
filter = await filterService.updateFilter(filterId, updateData, req.user);
|
|
214
|
+
}
|
|
215
|
+
else if (filterId) {
|
|
216
|
+
// Update someone else's filter - preserve original visibility
|
|
217
|
+
// First get the original filter to preserve its visibility settings
|
|
218
|
+
const originalFilter = await filterService.getFilterById(filterId, req.user);
|
|
219
|
+
if (!originalFilter) {
|
|
220
|
+
return res.status(404).json({ error: req.i18n.__('Filter not found') });
|
|
221
|
+
}
|
|
222
|
+
filter = await filterService.updateFilter(filterId, {
|
|
223
|
+
name,
|
|
224
|
+
description,
|
|
225
|
+
conditions,
|
|
226
|
+
sortField,
|
|
227
|
+
sortDirection,
|
|
228
|
+
icon,
|
|
229
|
+
color,
|
|
230
|
+
// Preserve original visibility - don't allow changing someone else's filter visibility
|
|
231
|
+
visibility: originalFilter.visibility,
|
|
232
|
+
groupIds: originalFilter.groupIds
|
|
233
|
+
}, req.user);
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
// Create new filter
|
|
237
|
+
filter = await filterService.createFilter({
|
|
238
|
+
id: crypto.randomUUID(),
|
|
239
|
+
name,
|
|
240
|
+
description,
|
|
241
|
+
modelName: modelResource.name,
|
|
242
|
+
conditions,
|
|
243
|
+
sortField,
|
|
244
|
+
sortDirection,
|
|
245
|
+
icon,
|
|
246
|
+
color,
|
|
247
|
+
visibility: effectiveVisibility,
|
|
248
|
+
groupIds: effectiveGroupIds,
|
|
249
|
+
ownerId: req.user.id,
|
|
250
|
+
apiEnabled: false,
|
|
251
|
+
version: 1
|
|
252
|
+
}, req.user);
|
|
253
|
+
}
|
|
254
|
+
// Save columns if provided
|
|
255
|
+
if (columns && Array.isArray(columns)) {
|
|
256
|
+
const filterColumns = columns.map((col, index) => ({
|
|
257
|
+
fieldName: col.fieldName,
|
|
258
|
+
order: col.order !== undefined ? col.order : index
|
|
259
|
+
}));
|
|
260
|
+
await filterService.updateFilterColumns(filter.id, filterColumns, req.user);
|
|
261
|
+
}
|
|
262
|
+
// Get result count
|
|
263
|
+
const count = await filterService.countFilterResults(filter, req.user);
|
|
264
|
+
return res.json({
|
|
265
|
+
success: true,
|
|
266
|
+
filter: {
|
|
267
|
+
...filter,
|
|
268
|
+
resultCount: count
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
catch (error) {
|
|
273
|
+
Adminizer.log.error('Error saving filter:', error);
|
|
274
|
+
return res.status(500).json({
|
|
275
|
+
error: req.i18n.__('Failed to save filter'),
|
|
276
|
+
message: error.message
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* DELETE /adminizer/model/:model/filter/:id
|
|
282
|
+
* Delete a saved filter
|
|
283
|
+
*/
|
|
284
|
+
export async function deleteFilter(req, res) {
|
|
285
|
+
const modelResource = ControllerHelper.findModelResource(req);
|
|
286
|
+
if (!modelResource.model) {
|
|
287
|
+
return res.status(404).send({ error: req.i18n.__('Model not found') });
|
|
288
|
+
}
|
|
289
|
+
const filterId = Array.isArray(req.params.id) ? req.params.id[0] : String(req.params.id);
|
|
290
|
+
if (!filterId || filterId === 'undefined' || filterId === 'null') {
|
|
291
|
+
return res.status(400).send({ error: req.i18n.__('Filter ID is required') });
|
|
292
|
+
}
|
|
293
|
+
const filterService = new FilterService(req.adminizer);
|
|
294
|
+
try {
|
|
295
|
+
Adminizer.log.debug(`Deleting filter: ${filterId}`);
|
|
296
|
+
await filterService.deleteFilter(filterId, req.user);
|
|
297
|
+
// Return identical response to prevent filter existence/ownership enumeration.
|
|
298
|
+
return res.status(204).send();
|
|
299
|
+
}
|
|
300
|
+
catch (error) {
|
|
301
|
+
const errorMessage = typeof error?.message === 'string'
|
|
302
|
+
? error.message.toLowerCase()
|
|
303
|
+
: '';
|
|
304
|
+
const isNotFound = errorMessage.includes('not found');
|
|
305
|
+
const isAccessDenied = errorMessage.includes('access denied');
|
|
306
|
+
// Fail closed without disclosing whether the filter exists or belongs to another user.
|
|
307
|
+
if (isNotFound || isAccessDenied) {
|
|
308
|
+
Adminizer.log.debug(`Filter delete suppressed for id=${filterId}: not found or access denied`);
|
|
309
|
+
return res.status(204).send();
|
|
310
|
+
}
|
|
311
|
+
Adminizer.log.error('Error deleting filter:', error);
|
|
312
|
+
return res.status(500).json({
|
|
313
|
+
error: req.i18n.__('Failed to delete filter'),
|
|
314
|
+
message: error.message
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export default function getAllWidgets(req: ReqType, res: ResType): Promise<
|
|
1
|
+
export default function getAllWidgets(req: ReqType, res: ResType): Promise<ResType>;
|
|
@@ -1,13 +1,4 @@
|
|
|
1
|
-
import { redirectToLogin } from '../helpers/inertiaAutHelper.js';
|
|
2
1
|
export default async function getAllWidgets(req, res) {
|
|
3
|
-
if (req.adminizer.config.auth.enable) {
|
|
4
|
-
if (!req.user) {
|
|
5
|
-
return redirectToLogin(req, res);
|
|
6
|
-
}
|
|
7
|
-
else if (!req.adminizer.accessRightsHelper.hasPermission(`widgets`, req.user)) {
|
|
8
|
-
return res.sendStatus(403);
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
2
|
if (req.method.toUpperCase() === 'GET') {
|
|
12
3
|
try {
|
|
13
4
|
return res.json({ widgets: await req.adminizer.widgetHandler.getAll(req.user, req.i18n) });
|
|
@@ -16,4 +7,5 @@ export default async function getAllWidgets(req, res) {
|
|
|
16
7
|
return res.status(500).send({ error: e.message || 'Internal Server Error' });
|
|
17
8
|
}
|
|
18
9
|
}
|
|
10
|
+
return res.status(405).end();
|
|
19
11
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Adminizer } from "../../lib/Adminizer.js";
|
|
2
|
-
import {
|
|
2
|
+
import { getUiTranslations } from "../../lib/ui-i18n/getUiTranslations.js";
|
|
3
|
+
import { HISTORY_UI_TRANSLATION_KEYS } from "../../lib/ui-i18n/uiTranslationKeys.js";
|
|
3
4
|
export class HistoryController {
|
|
4
5
|
static async index(req, res) {
|
|
5
|
-
|
|
6
|
-
if (!HistoryController.checkHistoryPermission(req, res, isUiRequest))
|
|
6
|
+
if (!HistoryController.checkHistoryPermission(req, res))
|
|
7
7
|
return;
|
|
8
8
|
const adapter = HistoryController.getAdapter(req);
|
|
9
9
|
if (req.method.toUpperCase() === 'GET') {
|
|
@@ -29,21 +29,7 @@ export class HistoryController {
|
|
|
29
29
|
title
|
|
30
30
|
};
|
|
31
31
|
});
|
|
32
|
-
const
|
|
33
|
-
"Models": "",
|
|
34
|
-
"All": "",
|
|
35
|
-
"Users": "",
|
|
36
|
-
"Date": "",
|
|
37
|
-
"Pick a date": "",
|
|
38
|
-
"Search": "",
|
|
39
|
-
"Reset": "",
|
|
40
|
-
"Model": "",
|
|
41
|
-
"Name": "",
|
|
42
|
-
"Event": "",
|
|
43
|
-
"User": "",
|
|
44
|
-
"The end of the list has been reached": "",
|
|
45
|
-
"There are no records to display": "",
|
|
46
|
-
};
|
|
32
|
+
const i18nPage = getUiTranslations(req, HISTORY_UI_TRANSLATION_KEYS);
|
|
47
33
|
return req.Inertia.render({
|
|
48
34
|
component: 'history',
|
|
49
35
|
props: {
|
|
@@ -52,7 +38,7 @@ export class HistoryController {
|
|
|
52
38
|
users: users.map((user) => ({
|
|
53
39
|
name: user.login
|
|
54
40
|
})),
|
|
55
|
-
|
|
41
|
+
i18nPage
|
|
56
42
|
}
|
|
57
43
|
});
|
|
58
44
|
}
|
|
@@ -117,22 +103,11 @@ export class HistoryController {
|
|
|
117
103
|
const adapter = req.adminizer.config.history?.adapter ?? 'default';
|
|
118
104
|
return req.adminizer.historyHandler.get(adapter);
|
|
119
105
|
}
|
|
120
|
-
static checkHistoryPermission(req, res
|
|
106
|
+
static checkHistoryPermission(req, res) {
|
|
121
107
|
if (!req.adminizer?.historyHandler) {
|
|
122
108
|
res.status(401).json({ error: 'History system not initialized' });
|
|
123
109
|
return false;
|
|
124
110
|
}
|
|
125
|
-
if (req.adminizer.config.auth.enable) {
|
|
126
|
-
if (!req.user) {
|
|
127
|
-
if (shouldRedirectToLogin) {
|
|
128
|
-
redirectToLogin(req, res);
|
|
129
|
-
}
|
|
130
|
-
else {
|
|
131
|
-
res.status(401).json({ error: 'Unauthorized' });
|
|
132
|
-
}
|
|
133
|
-
return false;
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
111
|
const hasPermission = req.adminizer.accessRightsHelper.hasPermission(`history-${req.adminizer.config.history?.adapter ?? 'default'}`, req.user);
|
|
137
112
|
if (!hasPermission) {
|
|
138
113
|
res.sendStatus(403);
|
package/controllers/initUser.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { Adminizer } from "../lib/Adminizer.js";
|
|
2
2
|
import { generate } from "password-hash";
|
|
3
3
|
import { inertiaInitUserHelper } from "../helpers/inertiaAutHelper.js";
|
|
4
|
+
import { generateUserApiKey } from "../helpers/apiKeyHelper.js";
|
|
4
5
|
export default async function initUser(req, res) {
|
|
5
6
|
if (!req.adminizer.config.auth.enable) {
|
|
6
7
|
return res.redirect(`${req.adminizer.config.routePrefix}/`);
|
|
7
8
|
}
|
|
8
|
-
|
|
9
|
-
let admins = await
|
|
9
|
+
const userModel = req.adminizer.modelHandler.internal("auth").get("UserAP");
|
|
10
|
+
let admins = await userModel.find({ where: { isAdministrator: true } });
|
|
10
11
|
if (admins.length) {
|
|
11
12
|
res.redirect(`${req.adminizer.config.routePrefix}/model/userap/login`);
|
|
12
13
|
}
|
|
@@ -31,14 +32,14 @@ export default async function initUser(req, res) {
|
|
|
31
32
|
Adminizer.log.debug(`Created admin`);
|
|
32
33
|
let passwordHashed = generate(login + password + process.env.AP_PASSWORD_SALT);
|
|
33
34
|
password = 'masked';
|
|
34
|
-
|
|
35
|
-
await req.adminizer.modelHandler.model.get("UserAP")["_create"]({
|
|
35
|
+
await userModel.create({
|
|
36
36
|
login: login,
|
|
37
37
|
passwordHashed: passwordHashed,
|
|
38
38
|
fullName: "Administrator",
|
|
39
39
|
isActive: true,
|
|
40
40
|
...(locale !== undefined && { locale }),
|
|
41
|
-
isAdministrator: true
|
|
41
|
+
isAdministrator: true,
|
|
42
|
+
apiKey: generateUserApiKey()
|
|
42
43
|
});
|
|
43
44
|
}
|
|
44
45
|
catch (e) {
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { ControllerHelper } from "../helpers/controllerHelper.js";
|
|
2
|
+
import { Adminizer } from "../lib/Adminizer.js";
|
|
3
|
+
import { DataAccessor } from "../lib/DataAccessor.js";
|
|
4
|
+
/**
|
|
5
|
+
* Inline update controller for list view
|
|
6
|
+
* PATCH /adminizer/model/:name/inline/:id
|
|
7
|
+
*
|
|
8
|
+
* Body: { field: string, value: any }
|
|
9
|
+
*/
|
|
10
|
+
export default async function inlineUpdate(req, res) {
|
|
11
|
+
const allowedFieldTypes = new Set(['string', 'integer', 'number', 'range', 'boolean', 'float', 'email']);
|
|
12
|
+
// Check ID
|
|
13
|
+
if (!req.params.id) {
|
|
14
|
+
return res.status(400).json({ error: 'Record ID is required' });
|
|
15
|
+
}
|
|
16
|
+
// Check field
|
|
17
|
+
if (!req.body.field) {
|
|
18
|
+
return res.status(400).json({ error: 'Field name is required' });
|
|
19
|
+
}
|
|
20
|
+
const modelResource = ControllerHelper.findModelResource(req);
|
|
21
|
+
if (!modelResource.model) {
|
|
22
|
+
return res.status(404).json({ error: 'Model not found' });
|
|
23
|
+
}
|
|
24
|
+
const fieldName = req.body.field;
|
|
25
|
+
const newValue = req.body.value;
|
|
26
|
+
// Get field config
|
|
27
|
+
const dataAccessor = new DataAccessor(req.adminizer, req.user, modelResource, "edit");
|
|
28
|
+
const fields = dataAccessor.getFieldsConfig();
|
|
29
|
+
const fieldConfig = fields[fieldName];
|
|
30
|
+
if (!fieldConfig) {
|
|
31
|
+
return res.status(400).json({ error: `Field '${fieldName}' not found` });
|
|
32
|
+
}
|
|
33
|
+
const identifierField = modelResource.config.identifierField ||
|
|
34
|
+
req.adminizer.config.identifierField ||
|
|
35
|
+
modelResource.model.primaryKey ||
|
|
36
|
+
'id';
|
|
37
|
+
const blockedInlineFields = new Set(['id', 'createdat', 'updatedat']);
|
|
38
|
+
if (blockedInlineFields.has(fieldName.toLowerCase()) || fieldName === identifierField) {
|
|
39
|
+
return res.status(403).json({ error: `Field '${fieldName}' cannot be edited inline` });
|
|
40
|
+
}
|
|
41
|
+
// Check if inline edit is explicitly disabled for this field
|
|
42
|
+
const baseConfig = fieldConfig.config;
|
|
43
|
+
if (baseConfig.inlineEditable === false) {
|
|
44
|
+
return res.status(403).json({ error: `Field '${fieldName}' is not inline editable` });
|
|
45
|
+
}
|
|
46
|
+
// Check if field has displayModifier (should not be editable)
|
|
47
|
+
if (baseConfig.displayModifier) {
|
|
48
|
+
return res.status(403).json({ error: `Field '${fieldName}' has displayModifier and cannot be edited` });
|
|
49
|
+
}
|
|
50
|
+
// Check if field type supports inline editing.
|
|
51
|
+
// Prefer explicit field config type, fallback to model metadata type.
|
|
52
|
+
const fieldType = (baseConfig.type || fieldConfig.model?.type || '').toLowerCase();
|
|
53
|
+
if (!fieldType || !allowedFieldTypes.has(fieldType)) {
|
|
54
|
+
return res.status(403).json({ error: `Field '${fieldName}' type does not support inline editing` });
|
|
55
|
+
}
|
|
56
|
+
// Validate value
|
|
57
|
+
const validationError = validateInlineValue(newValue, baseConfig, fieldType);
|
|
58
|
+
if (validationError) {
|
|
59
|
+
return res.status(400).json({ error: validationError });
|
|
60
|
+
}
|
|
61
|
+
// Convert value based on field type
|
|
62
|
+
const convertedValue = convertInlineValue(newValue, fieldType);
|
|
63
|
+
// Update record
|
|
64
|
+
const id = req.params.id;
|
|
65
|
+
const params = {};
|
|
66
|
+
params[modelResource.config.identifierField || req.adminizer.config.identifierField] = id;
|
|
67
|
+
try {
|
|
68
|
+
await modelResource.model.update(params, { [fieldName]: convertedValue }, dataAccessor);
|
|
69
|
+
return res.json({
|
|
70
|
+
success: true,
|
|
71
|
+
data: {
|
|
72
|
+
id,
|
|
73
|
+
field: fieldName,
|
|
74
|
+
value: convertedValue
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
Adminizer.log.error('Inline update error:', error);
|
|
80
|
+
return res.status(500).json({ error: 'Failed to update record' });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Validate value according to inline validation rules
|
|
85
|
+
*/
|
|
86
|
+
function validateInlineValue(value, config, fieldType) {
|
|
87
|
+
const validation = config.inlineValidation;
|
|
88
|
+
// String validations
|
|
89
|
+
if (typeof value === 'string') {
|
|
90
|
+
if (validation?.minLength && value.length < validation.minLength) {
|
|
91
|
+
return `Value must be at least ${validation.minLength} characters`;
|
|
92
|
+
}
|
|
93
|
+
if (validation?.maxLength && value.length > validation.maxLength) {
|
|
94
|
+
return `Value must be at most ${validation.maxLength} characters`;
|
|
95
|
+
}
|
|
96
|
+
if (validation?.pattern && !new RegExp(validation.pattern).test(value)) {
|
|
97
|
+
return `Value does not match required pattern`;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// Number validations
|
|
101
|
+
if (typeof value === 'number' || ['integer', 'number', 'float', 'range'].includes(fieldType || '')) {
|
|
102
|
+
const numValue = Number(value);
|
|
103
|
+
if (isNaN(numValue)) {
|
|
104
|
+
return 'Invalid number';
|
|
105
|
+
}
|
|
106
|
+
if (validation?.min !== undefined && numValue < validation.min) {
|
|
107
|
+
return `Value must be at least ${validation.min}`;
|
|
108
|
+
}
|
|
109
|
+
if (validation?.max !== undefined && numValue > validation.max) {
|
|
110
|
+
return `Value must be at most ${validation.max}`;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// Custom validation
|
|
114
|
+
if (validation?.validate) {
|
|
115
|
+
const result = validation.validate(value);
|
|
116
|
+
if (typeof result === 'string') {
|
|
117
|
+
return result;
|
|
118
|
+
}
|
|
119
|
+
if (result === false) {
|
|
120
|
+
return 'Invalid value';
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Convert value to appropriate type
|
|
127
|
+
*/
|
|
128
|
+
function convertInlineValue(value, fieldType) {
|
|
129
|
+
if (value === null || value === undefined) {
|
|
130
|
+
return value;
|
|
131
|
+
}
|
|
132
|
+
switch (fieldType) {
|
|
133
|
+
case 'boolean':
|
|
134
|
+
return Boolean(value);
|
|
135
|
+
case 'integer':
|
|
136
|
+
return parseInt(value, 10);
|
|
137
|
+
case 'number':
|
|
138
|
+
case 'float':
|
|
139
|
+
return parseFloat(value);
|
|
140
|
+
case 'range':
|
|
141
|
+
return typeof value === 'string' ? parseFloat(value) : Number(value);
|
|
142
|
+
case 'email':
|
|
143
|
+
case 'string':
|
|
144
|
+
case 'text':
|
|
145
|
+
default:
|
|
146
|
+
return typeof value === 'string' ? value : String(value);
|
|
147
|
+
}
|
|
148
|
+
}
|
package/controllers/list.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export default function list(req: ReqType, res: ResType): Promise<
|
|
1
|
+
export default function list(req: ReqType, res: ResType): Promise<import("express").Response<any, Record<string, any>>>;
|