adminforth 2.27.0-next.7 → 2.27.0-next.70
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/commands/callTsProxy.js +10 -5
- package/commands/createApp/templates/api.ts.hbs +28 -9
- package/commands/createApp/templates/package.json.hbs +2 -1
- package/commands/proxy.ts +18 -10
- package/dist/basePlugin.js +1 -1
- package/dist/basePlugin.js.map +1 -1
- package/dist/commands/proxy.js +14 -10
- package/dist/commands/proxy.js.map +1 -1
- package/dist/dataConnectors/baseConnector.d.ts +10 -4
- package/dist/dataConnectors/baseConnector.d.ts.map +1 -1
- package/dist/dataConnectors/baseConnector.js +76 -54
- package/dist/dataConnectors/baseConnector.js.map +1 -1
- package/dist/dataConnectors/clickhouse.d.ts +5 -2
- package/dist/dataConnectors/clickhouse.d.ts.map +1 -1
- package/dist/dataConnectors/clickhouse.js +76 -10
- package/dist/dataConnectors/clickhouse.js.map +1 -1
- package/dist/dataConnectors/mongo.d.ts.map +1 -1
- package/dist/dataConnectors/mongo.js +3 -1
- package/dist/dataConnectors/mongo.js.map +1 -1
- package/dist/dataConnectors/mysql.d.ts.map +1 -1
- package/dist/dataConnectors/mysql.js +3 -1
- package/dist/dataConnectors/mysql.js.map +1 -1
- package/dist/dataConnectors/postgres.d.ts.map +1 -1
- package/dist/dataConnectors/postgres.js +3 -1
- package/dist/dataConnectors/postgres.js.map +1 -1
- package/dist/dataConnectors/sqlite.d.ts.map +1 -1
- package/dist/dataConnectors/sqlite.js +4 -2
- package/dist/dataConnectors/sqlite.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -1
- package/dist/modules/configValidator.d.ts.map +1 -1
- package/dist/modules/configValidator.js +16 -9
- package/dist/modules/configValidator.js.map +1 -1
- package/dist/modules/restApi.d.ts.map +1 -1
- package/dist/modules/restApi.js +598 -15
- package/dist/modules/restApi.js.map +1 -1
- package/dist/modules/styles.js +1 -1
- package/dist/modules/utils.d.ts +1 -1
- package/dist/modules/utils.d.ts.map +1 -1
- package/dist/modules/utils.js +3 -5
- package/dist/modules/utils.js.map +1 -1
- package/dist/servers/express.d.ts +18 -7
- package/dist/servers/express.d.ts.map +1 -1
- package/dist/servers/express.js +141 -1
- package/dist/servers/express.js.map +1 -1
- package/dist/servers/openapi.d.ts +25 -0
- package/dist/servers/openapi.d.ts.map +1 -0
- package/dist/servers/openapi.js +92 -0
- package/dist/servers/openapi.js.map +1 -0
- package/dist/servers/openapiDocument.d.ts +12 -0
- package/dist/servers/openapiDocument.d.ts.map +1 -0
- package/dist/servers/openapiDocument.js +313 -0
- package/dist/servers/openapiDocument.js.map +1 -0
- package/dist/spa/package-lock.json +41 -0
- package/dist/spa/package.json +4 -0
- package/dist/spa/pnpm-lock.yaml +384 -310
- package/dist/spa/pnpm-workspace.yaml +4 -0
- package/dist/spa/src/App.vue +78 -76
- package/dist/spa/src/afcl/Button.vue +2 -3
- package/dist/spa/src/afcl/Dialog.vue +1 -1
- package/dist/spa/src/afcl/Input.vue +1 -1
- package/dist/spa/src/afcl/Select.vue +8 -2
- package/dist/spa/src/afcl/Skeleton.vue +5 -0
- package/dist/spa/src/afcl/Spinner.vue +1 -1
- package/dist/spa/src/components/CallActionWrapper.vue +1 -1
- package/dist/spa/src/components/ColumnValueInput.vue +16 -3
- package/dist/spa/src/components/ColumnValueInputWrapper.vue +25 -2
- package/dist/spa/src/components/CustomRangePicker.vue +10 -14
- package/dist/spa/src/components/Filters.vue +95 -63
- package/dist/spa/src/components/GroupsTable.vue +9 -6
- package/dist/spa/src/components/MenuLink.vue +2 -2
- package/dist/spa/src/components/ResourceForm.vue +103 -9
- package/dist/spa/src/components/ResourceListTable.vue +16 -10
- package/dist/spa/src/components/ShowTable.vue +3 -3
- package/dist/spa/src/components/Sidebar.vue +29 -8
- package/dist/spa/src/components/ThreeDotsMenu.vue +25 -9
- package/dist/spa/src/components/ValueRenderer.vue +1 -0
- package/dist/spa/src/controls/BoolToggle.vue +2 -2
- package/dist/spa/src/renderers/RichText.vue +2 -2
- package/dist/spa/src/renderers/ZeroStylesRichText.vue +2 -2
- package/dist/spa/src/spa_types/core.ts +32 -0
- package/dist/spa/src/stores/core.ts +16 -2
- package/dist/spa/src/stores/filters.ts +16 -12
- package/dist/spa/src/types/Back.ts +137 -26
- package/dist/spa/src/types/Common.ts +25 -6
- package/dist/spa/src/types/adapters/CompletionAdapter.ts +27 -5
- package/dist/spa/src/types/adapters/index.ts +2 -2
- package/dist/spa/src/utils/createEditUtils.ts +65 -0
- package/dist/spa/src/utils/index.ts +2 -1
- package/dist/spa/src/utils/listUtils.ts +3 -3
- package/dist/spa/src/utils/utils.ts +42 -7
- package/dist/spa/src/utils.ts +2 -1
- package/dist/spa/src/views/CreateEditSkeleton.vue +74 -0
- package/dist/spa/src/views/CreateView.vue +24 -50
- package/dist/spa/src/views/EditView.vue +23 -40
- package/dist/spa/src/views/ListView.vue +22 -32
- package/dist/spa/src/views/ShowView.vue +66 -24
- package/dist/types/Back.d.ts +140 -32
- package/dist/types/Back.d.ts.map +1 -1
- package/dist/types/Back.js.map +1 -1
- package/dist/types/Common.d.ts +32 -6
- package/dist/types/Common.d.ts.map +1 -1
- package/dist/types/Common.js.map +1 -1
- package/dist/types/adapters/CompletionAdapter.d.ts +18 -3
- package/dist/types/adapters/CompletionAdapter.d.ts.map +1 -1
- package/dist/types/adapters/index.d.ts +1 -1
- package/dist/types/adapters/index.d.ts.map +1 -1
- package/package.json +11 -6
package/dist/modules/restApi.js
CHANGED
|
@@ -1,9 +1,20 @@
|
|
|
1
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
2
|
+
var t = {};
|
|
3
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
4
|
+
t[p] = s[p];
|
|
5
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
7
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
8
|
+
t[p[i]] = s[p[i]];
|
|
9
|
+
}
|
|
10
|
+
return t;
|
|
11
|
+
};
|
|
1
12
|
import { Filters, } from "../types/Back.js";
|
|
2
13
|
import { cascadeChildrenDelete } from './utils.js';
|
|
3
14
|
import { afLogger } from "./logger.js";
|
|
4
15
|
import { ADMINFORTH_VERSION, listify, getLoginPromptHTML, hookResponseError } from './utils.js';
|
|
5
16
|
import AdminForthAuth from "../auth.js";
|
|
6
|
-
import { ActionCheckSource, AdminForthDataTypes, AdminForthFilterOperators, AdminForthResourcePages, AllowedActionsEnum } from "../types/Common.js";
|
|
17
|
+
import { ActionCheckSource, AdminForthDataTypes, AdminForthFilterOperators, AdminForthResourcePages, AdminForthSortDirections, AllowedActionsEnum } from "../types/Common.js";
|
|
7
18
|
import { filtersTools } from "../modules/filtersTools.js";
|
|
8
19
|
async function resolveBoolOrFn(val, ctx) {
|
|
9
20
|
if (typeof val === 'function') {
|
|
@@ -26,6 +37,469 @@ async function isFilledOnCreate(col) {
|
|
|
26
37
|
const fillOnCreate = !!col.fillOnCreate;
|
|
27
38
|
return fillOnCreate;
|
|
28
39
|
}
|
|
40
|
+
function stripResourceColumnFrontendMeta(column) {
|
|
41
|
+
const { default: _default, _baseTypeDebug } = column, sanitizedColumn = __rest(column, ["default", "_baseTypeDebug"]);
|
|
42
|
+
return sanitizedColumn;
|
|
43
|
+
}
|
|
44
|
+
const SIMPLE_FILTER_OPERATORS = Object.values(AdminForthFilterOperators).filter((operator) => {
|
|
45
|
+
return operator !== AdminForthFilterOperators.AND && operator !== AdminForthFilterOperators.OR;
|
|
46
|
+
});
|
|
47
|
+
const genericObjectSchema = {
|
|
48
|
+
type: 'object',
|
|
49
|
+
additionalProperties: true,
|
|
50
|
+
};
|
|
51
|
+
const errorResponseSchema = {
|
|
52
|
+
title: 'AdminForthErrorResponse',
|
|
53
|
+
description: 'Standard error response returned by AdminForth endpoints.',
|
|
54
|
+
type: 'object',
|
|
55
|
+
required: ['error'],
|
|
56
|
+
properties: {
|
|
57
|
+
error: { type: 'string' },
|
|
58
|
+
},
|
|
59
|
+
additionalProperties: true,
|
|
60
|
+
};
|
|
61
|
+
const recordIdentifierSchema = {
|
|
62
|
+
title: 'AdminForthRecordIdentifier',
|
|
63
|
+
description: 'Record identifier accepted by AdminForth. Depending on the resource it can be a string or a number.',
|
|
64
|
+
anyOf: [
|
|
65
|
+
{ type: 'string' },
|
|
66
|
+
{ type: 'number' },
|
|
67
|
+
],
|
|
68
|
+
};
|
|
69
|
+
const actionIdentifierSchema = {
|
|
70
|
+
title: 'AdminForthActionIdentifier',
|
|
71
|
+
description: 'Action identifier accepted by AdminForth. Depending on configuration it can be a string or a number.',
|
|
72
|
+
anyOf: [
|
|
73
|
+
{ type: 'string' },
|
|
74
|
+
{ type: 'number' },
|
|
75
|
+
],
|
|
76
|
+
};
|
|
77
|
+
const namedColumnSchema = {
|
|
78
|
+
title: 'AdminForthNamedColumn',
|
|
79
|
+
type: 'object',
|
|
80
|
+
required: ['name'],
|
|
81
|
+
properties: {
|
|
82
|
+
name: { type: 'string' },
|
|
83
|
+
},
|
|
84
|
+
additionalProperties: true,
|
|
85
|
+
};
|
|
86
|
+
const validationResultSchema = {
|
|
87
|
+
title: 'AdminForthValidationResult',
|
|
88
|
+
type: 'object',
|
|
89
|
+
required: ['isValid'],
|
|
90
|
+
properties: {
|
|
91
|
+
isValid: { type: 'boolean' },
|
|
92
|
+
message: { type: 'string' },
|
|
93
|
+
},
|
|
94
|
+
additionalProperties: true,
|
|
95
|
+
};
|
|
96
|
+
const filterConditionExample = {
|
|
97
|
+
field: 'status',
|
|
98
|
+
operator: AdminForthFilterOperators.EQ,
|
|
99
|
+
value: 'active',
|
|
100
|
+
};
|
|
101
|
+
const filterGroupExample = {
|
|
102
|
+
operator: AdminForthFilterOperators.AND,
|
|
103
|
+
subFilters: [filterConditionExample],
|
|
104
|
+
};
|
|
105
|
+
const sortItemExample = {
|
|
106
|
+
field: 'createdAt',
|
|
107
|
+
direction: AdminForthSortDirections.desc,
|
|
108
|
+
};
|
|
109
|
+
const filterConditionSchema = {
|
|
110
|
+
title: 'AdminForthFilterCondition',
|
|
111
|
+
description: 'Single field comparison used in AdminForth filtering.',
|
|
112
|
+
type: 'object',
|
|
113
|
+
properties: {
|
|
114
|
+
field: { type: 'string' },
|
|
115
|
+
operator: { type: 'string', enum: SIMPLE_FILTER_OPERATORS },
|
|
116
|
+
value: {},
|
|
117
|
+
rightField: { type: 'string' },
|
|
118
|
+
insecureRawSQL: { type: 'string' },
|
|
119
|
+
insecureRawNoSQL: {},
|
|
120
|
+
},
|
|
121
|
+
additionalProperties: true,
|
|
122
|
+
examples: [filterConditionExample],
|
|
123
|
+
};
|
|
124
|
+
const filterGroupSchema = {
|
|
125
|
+
title: 'AdminForthFilterGroup',
|
|
126
|
+
description: 'Nested boolean filter group. Use this for AND or OR combinations of filter nodes.',
|
|
127
|
+
type: 'object',
|
|
128
|
+
required: ['operator', 'subFilters'],
|
|
129
|
+
properties: {
|
|
130
|
+
operator: {
|
|
131
|
+
type: 'string',
|
|
132
|
+
enum: [AdminForthFilterOperators.AND, AdminForthFilterOperators.OR],
|
|
133
|
+
},
|
|
134
|
+
subFilters: {
|
|
135
|
+
type: 'array',
|
|
136
|
+
items: { $ref: '#/$defs/filterNode' },
|
|
137
|
+
description: 'Nested filters evaluated with the selected operator.',
|
|
138
|
+
},
|
|
139
|
+
},
|
|
140
|
+
additionalProperties: true,
|
|
141
|
+
examples: [filterGroupExample],
|
|
142
|
+
};
|
|
143
|
+
const sortItemSchema = {
|
|
144
|
+
title: 'AdminForthSortItem',
|
|
145
|
+
description: 'Single sort instruction applied in order with the rest of the list.',
|
|
146
|
+
type: 'object',
|
|
147
|
+
required: ['field', 'direction'],
|
|
148
|
+
properties: {
|
|
149
|
+
field: { type: 'string' },
|
|
150
|
+
direction: { type: 'string', enum: Object.values(AdminForthSortDirections) },
|
|
151
|
+
},
|
|
152
|
+
additionalProperties: true,
|
|
153
|
+
examples: [sortItemExample],
|
|
154
|
+
};
|
|
155
|
+
const commonFilterSchemaDefs = {
|
|
156
|
+
singleFilter: filterConditionSchema,
|
|
157
|
+
filterGroup: filterGroupSchema,
|
|
158
|
+
filterNode: {
|
|
159
|
+
title: 'AdminForthFilterNode',
|
|
160
|
+
description: 'Either a single filter condition or a nested filter group.',
|
|
161
|
+
anyOf: [
|
|
162
|
+
{ $ref: '#/$defs/singleFilter' },
|
|
163
|
+
{ $ref: '#/$defs/filterGroup' },
|
|
164
|
+
],
|
|
165
|
+
examples: [filterConditionExample, filterGroupExample],
|
|
166
|
+
},
|
|
167
|
+
sortItem: sortItemSchema,
|
|
168
|
+
};
|
|
169
|
+
const commonSortSchema = {
|
|
170
|
+
title: 'AdminForthSortList',
|
|
171
|
+
description: 'Ordered list of sort instructions.',
|
|
172
|
+
type: 'array',
|
|
173
|
+
items: { $ref: '#/$defs/sortItem' },
|
|
174
|
+
examples: [[sortItemExample]],
|
|
175
|
+
};
|
|
176
|
+
const commonFiltersSchema = {
|
|
177
|
+
title: 'AdminForthFilterInput',
|
|
178
|
+
description: 'Runtime accepts either a single filter node or an array of filter nodes. The OpenAPI document normalizes this to the array form for readability.',
|
|
179
|
+
oneOf: [
|
|
180
|
+
{
|
|
181
|
+
type: 'array',
|
|
182
|
+
items: { $ref: '#/$defs/filterNode' },
|
|
183
|
+
},
|
|
184
|
+
{ $ref: '#/$defs/filterNode' },
|
|
185
|
+
],
|
|
186
|
+
};
|
|
187
|
+
function createErrorOrSuccessSchema(successSchema) {
|
|
188
|
+
return {
|
|
189
|
+
anyOf: [
|
|
190
|
+
errorResponseSchema,
|
|
191
|
+
successSchema,
|
|
192
|
+
],
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
const getResourceDataRequestSchema = {
|
|
196
|
+
type: 'object',
|
|
197
|
+
$defs: commonFilterSchemaDefs,
|
|
198
|
+
required: ['resourceId', 'source', 'limit', 'offset', 'filters', 'sort'],
|
|
199
|
+
properties: {
|
|
200
|
+
resourceId: { type: 'string' },
|
|
201
|
+
source: {
|
|
202
|
+
type: 'string',
|
|
203
|
+
enum: ['show', 'list', 'edit'],
|
|
204
|
+
description: 'Target UI context. Show and edit requests should use direct field filters that identify a single record.',
|
|
205
|
+
},
|
|
206
|
+
limit: {
|
|
207
|
+
type: 'integer',
|
|
208
|
+
description: 'Maximum number of rows to return for the current page.',
|
|
209
|
+
},
|
|
210
|
+
offset: {
|
|
211
|
+
type: 'integer',
|
|
212
|
+
description: 'Zero-based row offset used for pagination.',
|
|
213
|
+
},
|
|
214
|
+
sort: commonSortSchema,
|
|
215
|
+
filters: commonFiltersSchema,
|
|
216
|
+
},
|
|
217
|
+
additionalProperties: true,
|
|
218
|
+
allOf: [
|
|
219
|
+
{
|
|
220
|
+
if: {
|
|
221
|
+
properties: {
|
|
222
|
+
source: { enum: ['show', 'edit'] },
|
|
223
|
+
},
|
|
224
|
+
required: ['source'],
|
|
225
|
+
},
|
|
226
|
+
then: {
|
|
227
|
+
properties: {
|
|
228
|
+
filters: {
|
|
229
|
+
type: 'array',
|
|
230
|
+
items: {
|
|
231
|
+
allOf: [
|
|
232
|
+
{ $ref: '#/$defs/singleFilter' },
|
|
233
|
+
{
|
|
234
|
+
type: 'object',
|
|
235
|
+
required: ['field'],
|
|
236
|
+
},
|
|
237
|
+
],
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
},
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
],
|
|
244
|
+
};
|
|
245
|
+
const getResourceDataResponseSchema = createErrorOrSuccessSchema({
|
|
246
|
+
type: 'object',
|
|
247
|
+
required: ['data'],
|
|
248
|
+
properties: {
|
|
249
|
+
data: {
|
|
250
|
+
type: 'array',
|
|
251
|
+
items: genericObjectSchema,
|
|
252
|
+
},
|
|
253
|
+
total: { type: 'number' },
|
|
254
|
+
options: genericObjectSchema,
|
|
255
|
+
},
|
|
256
|
+
additionalProperties: true,
|
|
257
|
+
});
|
|
258
|
+
const getMenuBadgesResponseSchema = {
|
|
259
|
+
type: 'object',
|
|
260
|
+
additionalProperties: {
|
|
261
|
+
anyOf: [
|
|
262
|
+
{ type: 'string' },
|
|
263
|
+
{ type: 'number' },
|
|
264
|
+
],
|
|
265
|
+
},
|
|
266
|
+
};
|
|
267
|
+
const getResourceRequestSchema = {
|
|
268
|
+
type: 'object',
|
|
269
|
+
required: ['resourceId'],
|
|
270
|
+
properties: {
|
|
271
|
+
resourceId: { type: 'string' },
|
|
272
|
+
},
|
|
273
|
+
additionalProperties: true,
|
|
274
|
+
};
|
|
275
|
+
const getResourceResponseSchema = createErrorOrSuccessSchema({
|
|
276
|
+
type: 'object',
|
|
277
|
+
required: ['resource'],
|
|
278
|
+
properties: {
|
|
279
|
+
resource: genericObjectSchema,
|
|
280
|
+
},
|
|
281
|
+
additionalProperties: true,
|
|
282
|
+
});
|
|
283
|
+
const getResourceForeignDataRequestSchema = {
|
|
284
|
+
type: 'object',
|
|
285
|
+
$defs: commonFilterSchemaDefs,
|
|
286
|
+
required: ['resourceId', 'column', 'limit', 'offset'],
|
|
287
|
+
properties: {
|
|
288
|
+
resourceId: { type: 'string' },
|
|
289
|
+
column: { type: 'string' },
|
|
290
|
+
limit: {
|
|
291
|
+
type: 'integer',
|
|
292
|
+
description: 'Maximum number of dropdown options to return.',
|
|
293
|
+
},
|
|
294
|
+
offset: {
|
|
295
|
+
type: 'integer',
|
|
296
|
+
description: 'Zero-based offset used to fetch the next option page.',
|
|
297
|
+
},
|
|
298
|
+
search: { type: 'string' },
|
|
299
|
+
filters: commonFiltersSchema,
|
|
300
|
+
sort: commonSortSchema,
|
|
301
|
+
},
|
|
302
|
+
additionalProperties: true,
|
|
303
|
+
};
|
|
304
|
+
const getResourceForeignDataResponseSchema = createErrorOrSuccessSchema({
|
|
305
|
+
type: 'object',
|
|
306
|
+
required: ['items'],
|
|
307
|
+
properties: {
|
|
308
|
+
items: {
|
|
309
|
+
type: 'array',
|
|
310
|
+
items: {
|
|
311
|
+
type: 'object',
|
|
312
|
+
required: ['value', 'label'],
|
|
313
|
+
properties: {
|
|
314
|
+
value: {},
|
|
315
|
+
label: { type: 'string' },
|
|
316
|
+
},
|
|
317
|
+
additionalProperties: true,
|
|
318
|
+
},
|
|
319
|
+
},
|
|
320
|
+
},
|
|
321
|
+
additionalProperties: true,
|
|
322
|
+
});
|
|
323
|
+
const getMinMaxForColumnsRequestSchema = {
|
|
324
|
+
type: 'object',
|
|
325
|
+
required: ['resourceId'],
|
|
326
|
+
properties: {
|
|
327
|
+
resourceId: { type: 'string' },
|
|
328
|
+
},
|
|
329
|
+
additionalProperties: true,
|
|
330
|
+
};
|
|
331
|
+
const getMinMaxForColumnsResponseSchema = createErrorOrSuccessSchema({
|
|
332
|
+
type: 'object',
|
|
333
|
+
additionalProperties: {
|
|
334
|
+
type: 'object',
|
|
335
|
+
required: ['min', 'max'],
|
|
336
|
+
properties: {
|
|
337
|
+
min: {},
|
|
338
|
+
max: {},
|
|
339
|
+
},
|
|
340
|
+
additionalProperties: true,
|
|
341
|
+
},
|
|
342
|
+
});
|
|
343
|
+
const createRecordRequestSchema = {
|
|
344
|
+
type: 'object',
|
|
345
|
+
required: ['resourceId', 'record', 'requiredColumnsToSkip'],
|
|
346
|
+
properties: {
|
|
347
|
+
resourceId: { type: 'string' },
|
|
348
|
+
record: genericObjectSchema,
|
|
349
|
+
requiredColumnsToSkip: {
|
|
350
|
+
type: 'array',
|
|
351
|
+
items: namedColumnSchema,
|
|
352
|
+
},
|
|
353
|
+
meta: genericObjectSchema,
|
|
354
|
+
},
|
|
355
|
+
additionalProperties: true,
|
|
356
|
+
};
|
|
357
|
+
const createRecordResponseSchema = createErrorOrSuccessSchema({
|
|
358
|
+
type: 'object',
|
|
359
|
+
required: ['ok', 'newRecordId', 'redirectToRecordId'],
|
|
360
|
+
properties: {
|
|
361
|
+
ok: { const: true },
|
|
362
|
+
newRecordId: recordIdentifierSchema,
|
|
363
|
+
redirectToRecordId: recordIdentifierSchema,
|
|
364
|
+
},
|
|
365
|
+
additionalProperties: true,
|
|
366
|
+
});
|
|
367
|
+
const updateRecordRequestSchema = {
|
|
368
|
+
type: 'object',
|
|
369
|
+
required: ['resourceId', 'recordId', 'record'],
|
|
370
|
+
properties: {
|
|
371
|
+
resourceId: { type: 'string' },
|
|
372
|
+
recordId: recordIdentifierSchema,
|
|
373
|
+
record: genericObjectSchema,
|
|
374
|
+
meta: genericObjectSchema,
|
|
375
|
+
},
|
|
376
|
+
additionalProperties: true,
|
|
377
|
+
};
|
|
378
|
+
const updateRecordResponseSchema = createErrorOrSuccessSchema({
|
|
379
|
+
type: 'object',
|
|
380
|
+
required: ['ok'],
|
|
381
|
+
properties: {
|
|
382
|
+
ok: { const: true },
|
|
383
|
+
recordId: recordIdentifierSchema,
|
|
384
|
+
},
|
|
385
|
+
additionalProperties: true,
|
|
386
|
+
});
|
|
387
|
+
const deleteRecordRequestSchema = {
|
|
388
|
+
type: 'object',
|
|
389
|
+
required: ['resourceId', 'primaryKey'],
|
|
390
|
+
properties: {
|
|
391
|
+
resourceId: { type: 'string' },
|
|
392
|
+
primaryKey: recordIdentifierSchema,
|
|
393
|
+
},
|
|
394
|
+
additionalProperties: true,
|
|
395
|
+
};
|
|
396
|
+
const deleteRecordResponseSchema = createErrorOrSuccessSchema({
|
|
397
|
+
type: 'object',
|
|
398
|
+
required: ['ok', 'recordId'],
|
|
399
|
+
properties: {
|
|
400
|
+
ok: { const: true },
|
|
401
|
+
recordId: recordIdentifierSchema,
|
|
402
|
+
},
|
|
403
|
+
additionalProperties: true,
|
|
404
|
+
});
|
|
405
|
+
const startCustomActionRequestSchema = {
|
|
406
|
+
type: 'object',
|
|
407
|
+
required: ['resourceId', 'actionId', 'recordId'],
|
|
408
|
+
properties: {
|
|
409
|
+
resourceId: { type: 'string' },
|
|
410
|
+
actionId: actionIdentifierSchema,
|
|
411
|
+
recordId: recordIdentifierSchema,
|
|
412
|
+
extra: genericObjectSchema,
|
|
413
|
+
},
|
|
414
|
+
additionalProperties: true,
|
|
415
|
+
};
|
|
416
|
+
const startCustomActionResponseSchema = {
|
|
417
|
+
anyOf: [
|
|
418
|
+
errorResponseSchema,
|
|
419
|
+
{
|
|
420
|
+
type: 'object',
|
|
421
|
+
required: ['actionId', 'resourceId', 'recordId', 'redirectUrl'],
|
|
422
|
+
properties: {
|
|
423
|
+
actionId: actionIdentifierSchema,
|
|
424
|
+
resourceId: { type: 'string' },
|
|
425
|
+
recordId: recordIdentifierSchema,
|
|
426
|
+
redirectUrl: { type: 'string' },
|
|
427
|
+
},
|
|
428
|
+
additionalProperties: true,
|
|
429
|
+
},
|
|
430
|
+
{
|
|
431
|
+
type: 'object',
|
|
432
|
+
required: ['actionId', 'resourceId', 'recordId', 'ok'],
|
|
433
|
+
properties: {
|
|
434
|
+
actionId: actionIdentifierSchema,
|
|
435
|
+
resourceId: { type: 'string' },
|
|
436
|
+
recordId: recordIdentifierSchema,
|
|
437
|
+
ok: { const: true },
|
|
438
|
+
},
|
|
439
|
+
additionalProperties: true,
|
|
440
|
+
},
|
|
441
|
+
],
|
|
442
|
+
};
|
|
443
|
+
const startCustomBulkActionRequestSchema = {
|
|
444
|
+
type: 'object',
|
|
445
|
+
required: ['resourceId', 'actionId', 'recordIds'],
|
|
446
|
+
properties: {
|
|
447
|
+
resourceId: { type: 'string' },
|
|
448
|
+
actionId: actionIdentifierSchema,
|
|
449
|
+
recordIds: {
|
|
450
|
+
type: 'array',
|
|
451
|
+
items: recordIdentifierSchema,
|
|
452
|
+
},
|
|
453
|
+
extra: genericObjectSchema,
|
|
454
|
+
},
|
|
455
|
+
additionalProperties: true,
|
|
456
|
+
};
|
|
457
|
+
const startCustomBulkActionResponseSchema = createErrorOrSuccessSchema({
|
|
458
|
+
type: 'object',
|
|
459
|
+
required: ['actionId', 'resourceId', 'recordIds', 'ok'],
|
|
460
|
+
properties: {
|
|
461
|
+
actionId: actionIdentifierSchema,
|
|
462
|
+
resourceId: { type: 'string' },
|
|
463
|
+
recordIds: {
|
|
464
|
+
type: 'array',
|
|
465
|
+
items: recordIdentifierSchema,
|
|
466
|
+
},
|
|
467
|
+
ok: { const: true },
|
|
468
|
+
},
|
|
469
|
+
additionalProperties: true,
|
|
470
|
+
});
|
|
471
|
+
const validateColumnsRequestSchema = {
|
|
472
|
+
type: 'object',
|
|
473
|
+
required: ['resourceId', 'editableColumns', 'record'],
|
|
474
|
+
properties: {
|
|
475
|
+
resourceId: { type: 'string' },
|
|
476
|
+
editableColumns: {
|
|
477
|
+
type: 'array',
|
|
478
|
+
items: {
|
|
479
|
+
type: 'object',
|
|
480
|
+
required: ['name'],
|
|
481
|
+
properties: {
|
|
482
|
+
name: { type: 'string' },
|
|
483
|
+
value: {},
|
|
484
|
+
},
|
|
485
|
+
additionalProperties: true,
|
|
486
|
+
},
|
|
487
|
+
},
|
|
488
|
+
record: genericObjectSchema,
|
|
489
|
+
},
|
|
490
|
+
additionalProperties: true,
|
|
491
|
+
};
|
|
492
|
+
const validateColumnsResponseSchema = createErrorOrSuccessSchema({
|
|
493
|
+
type: 'object',
|
|
494
|
+
required: ['validationResults'],
|
|
495
|
+
properties: {
|
|
496
|
+
validationResults: {
|
|
497
|
+
type: 'object',
|
|
498
|
+
additionalProperties: validationResultSchema,
|
|
499
|
+
},
|
|
500
|
+
},
|
|
501
|
+
additionalProperties: true,
|
|
502
|
+
});
|
|
29
503
|
export async function interpretResource(adminUser, resource, meta, source, adminforth) {
|
|
30
504
|
afLogger.trace(`🪲Interpreting resource, ${resource.resourceId}, ${source}, 'adminUser', ${adminUser}`);
|
|
31
505
|
const allowedActions = {};
|
|
@@ -182,7 +656,7 @@ export default class AdminForthRestAPI {
|
|
|
182
656
|
handler: async ({ tr }) => {
|
|
183
657
|
const loginPromptHTML = await getLoginPromptHTML(this.adminforth.config.auth.loginPromptHTML);
|
|
184
658
|
return {
|
|
185
|
-
loginPromptHTML: await tr(loginPromptHTML, 'system.loginPromptHTML'),
|
|
659
|
+
loginPromptHTML: loginPromptHTML ? await tr(loginPromptHTML, 'system.loginPromptHTML') : null,
|
|
186
660
|
};
|
|
187
661
|
}
|
|
188
662
|
});
|
|
@@ -222,7 +696,7 @@ export default class AdminForthRestAPI {
|
|
|
222
696
|
server.endpoint({
|
|
223
697
|
method: 'GET',
|
|
224
698
|
path: '/get_base_config',
|
|
225
|
-
handler: async ({
|
|
699
|
+
handler: async ({ adminUser, cookies, tr, response }) => {
|
|
226
700
|
var _a, _b, _c, _d, _e;
|
|
227
701
|
let username = '';
|
|
228
702
|
let userFullName = '';
|
|
@@ -385,14 +859,17 @@ export default class AdminForthRestAPI {
|
|
|
385
859
|
server.endpoint({
|
|
386
860
|
method: 'GET',
|
|
387
861
|
path: '/get_menu_badges',
|
|
862
|
+
description: 'Computes the current menu badge values for the authenticated admin user. Static badges are returned directly, and dynamic badge callbacks are resolved for all configured menu items, including nested items.',
|
|
863
|
+
response_schema: getMenuBadgesResponseSchema,
|
|
388
864
|
handler: async ({ adminUser }) => {
|
|
389
865
|
const badges = {};
|
|
390
866
|
const badgeFunctions = [];
|
|
867
|
+
const adminforth = this.adminforth;
|
|
391
868
|
function processMenuItem(menuItem) {
|
|
392
869
|
if (menuItem.badge) {
|
|
393
870
|
if (typeof menuItem.badge === 'function') {
|
|
394
871
|
badgeFunctions.push(async () => {
|
|
395
|
-
badges[menuItem.itemId] = await menuItem.badge(adminUser);
|
|
872
|
+
badges[menuItem.itemId] = await menuItem.badge(adminUser, adminforth);
|
|
396
873
|
});
|
|
397
874
|
}
|
|
398
875
|
else {
|
|
@@ -420,8 +897,11 @@ export default class AdminForthRestAPI {
|
|
|
420
897
|
server.endpoint({
|
|
421
898
|
method: 'POST',
|
|
422
899
|
path: '/get_resource',
|
|
900
|
+
description: 'Returns the definition of a single resource. The response includes translated labels, column metadata, allowed actions, visible bulk actions, frontend action metadata, and resource options after permission checks and removal of backend-only internals.',
|
|
901
|
+
request_schema: getResourceRequestSchema,
|
|
902
|
+
response_schema: getResourceResponseSchema,
|
|
423
903
|
handler: async ({ body, adminUser, tr }) => {
|
|
424
|
-
var _a;
|
|
904
|
+
var _a, _b;
|
|
425
905
|
const { resourceId } = body;
|
|
426
906
|
if (!this.adminforth.statuses.dbDiscover) {
|
|
427
907
|
return { error: 'Database discovery not started' };
|
|
@@ -471,13 +951,16 @@ export default class AdminForthRestAPI {
|
|
|
471
951
|
await Promise.all(Object.entries(translateRoutines).map(async ([key, value]) => {
|
|
472
952
|
translated[key] = await value;
|
|
473
953
|
}));
|
|
474
|
-
const toReturn =
|
|
954
|
+
const toReturn = {
|
|
955
|
+
resourceId: resource.resourceId,
|
|
956
|
+
label: translated.resLabel,
|
|
957
|
+
columns: await Promise.all(resource.columns.map(async (inCol, i) => {
|
|
475
958
|
var _a, _b, _c;
|
|
476
|
-
const col = JSON.parse(JSON.stringify(inCol));
|
|
959
|
+
const col = JSON.parse(JSON.stringify(stripResourceColumnFrontendMeta(inCol)));
|
|
477
960
|
let validation = null;
|
|
478
961
|
if (col.validation) {
|
|
479
|
-
validation = await Promise.all(col.validation.map(async (val) => {
|
|
480
|
-
return Object.assign(Object.assign({}, val), { message: await tr(val.message, `resource.${resource.resourceId}`) });
|
|
962
|
+
validation = await Promise.all(col.validation.map(async (val, index) => {
|
|
963
|
+
return Object.assign(Object.assign({}, val), { validator: inCol.validation[index].validator ? true : false, message: await tr(val.message, `resource.${resource.resourceId}`) });
|
|
481
964
|
}));
|
|
482
965
|
}
|
|
483
966
|
let enumItems = undefined;
|
|
@@ -507,16 +990,16 @@ export default class AdminForthRestAPI {
|
|
|
507
990
|
col.foreignResource.unsetLabel = await tr(col.foreignResource.unsetLabel, `resource.${resource.resourceId}.foreignResource.unsetLabel`);
|
|
508
991
|
}
|
|
509
992
|
if (inCol.suggestOnCreate && typeof inCol.suggestOnCreate === 'function') {
|
|
510
|
-
col.suggestOnCreate = await inCol.suggestOnCreate(adminUser);
|
|
993
|
+
col.suggestOnCreate = await inCol.suggestOnCreate({ adminUser });
|
|
511
994
|
}
|
|
512
995
|
return Object.assign(Object.assign({}, col), { showIn,
|
|
513
996
|
validation, label: translated[`resCol${i}`], enum: enumItems });
|
|
514
|
-
})),
|
|
997
|
+
})),
|
|
998
|
+
options: Object.assign(Object.assign({}, resource.options), { fieldGroups: (_a = resource.options.fieldGroups) === null || _a === void 0 ? void 0 : _a.map((group, i) => {
|
|
515
999
|
var _a;
|
|
516
1000
|
return (Object.assign(Object.assign({}, group), { noTitle: (_a = group.noTitle) !== null && _a !== void 0 ? _a : false, groupName: translated[`fieldGroup${i}`] || group.groupName }));
|
|
517
|
-
}), bulkActions: allowedBulkActions.map((action, i) => (Object.assign(Object.assign({}, action), { label: action.label ? translated[`bulkAction${i}`] : action.label, confirm: action.confirm ? translated[`bulkActionConfirm${i}`] : action.confirm }))),
|
|
518
|
-
|
|
519
|
-
delete toReturn.plugins;
|
|
1001
|
+
}), bulkActions: allowedBulkActions.map((action, i) => (Object.assign(Object.assign({}, action), { label: action.label ? translated[`bulkAction${i}`] : action.label, confirm: action.confirm ? translated[`bulkActionConfirm${i}`] : action.confirm }))), actions: (_b = resource.options.actions) === null || _b === void 0 ? void 0 : _b.map((action) => (Object.assign(Object.assign({}, action), { id: action.id, hasBulkHandler: !!action.bulkHandler, bulkHandler: undefined }))), allowedActions })
|
|
1002
|
+
};
|
|
520
1003
|
return {
|
|
521
1004
|
resource: toReturn,
|
|
522
1005
|
};
|
|
@@ -525,6 +1008,9 @@ export default class AdminForthRestAPI {
|
|
|
525
1008
|
server.endpoint({
|
|
526
1009
|
method: 'POST',
|
|
527
1010
|
path: '/get_resource_data',
|
|
1011
|
+
description: 'Loads resource rows for list, show, or edit views. The endpoint validates access, applies request hooks, filters, sorting, pagination, record labels, and row click URLs, then returns the final dataset with resource options.',
|
|
1012
|
+
request_schema: getResourceDataRequestSchema,
|
|
1013
|
+
response_schema: getResourceDataResponseSchema,
|
|
528
1014
|
handler: async ({ body, adminUser, headers, query, cookies, requestUrl, abortSignal }) => {
|
|
529
1015
|
var _a, _b, _c, _d, _e, _f;
|
|
530
1016
|
const { resourceId, source } = body;
|
|
@@ -786,12 +1272,15 @@ export default class AdminForthRestAPI {
|
|
|
786
1272
|
return hookRespError;
|
|
787
1273
|
}
|
|
788
1274
|
}
|
|
789
|
-
return
|
|
1275
|
+
return data;
|
|
790
1276
|
},
|
|
791
1277
|
});
|
|
792
1278
|
server.endpoint({
|
|
793
1279
|
method: 'POST',
|
|
794
1280
|
path: '/get_resource_foreign_data',
|
|
1281
|
+
description: 'Loads dropdown options for a foreign-key column. It resolves the referenced resource or polymorphic resources, applies optional search text, hook-injected filters, pagination, and per-record labels, then returns sanitized option items.',
|
|
1282
|
+
request_schema: getResourceForeignDataRequestSchema,
|
|
1283
|
+
response_schema: getResourceForeignDataResponseSchema,
|
|
795
1284
|
handler: async ({ body, adminUser, headers, query, cookies, requestUrl }) => {
|
|
796
1285
|
const { resourceId, column, search } = body;
|
|
797
1286
|
if (!this.adminforth.statuses.dbDiscover) {
|
|
@@ -952,6 +1441,9 @@ export default class AdminForthRestAPI {
|
|
|
952
1441
|
server.endpoint({
|
|
953
1442
|
method: 'POST',
|
|
954
1443
|
path: '/get_min_max_for_columns',
|
|
1444
|
+
description: 'Returns min and max values for resource columns that explicitly opt in to min/max queries. This is used to build range-based filter controls without exposing columns that do not allow the query.',
|
|
1445
|
+
request_schema: getMinMaxForColumnsRequestSchema,
|
|
1446
|
+
response_schema: getMinMaxForColumnsResponseSchema,
|
|
955
1447
|
handler: async ({ body }) => {
|
|
956
1448
|
const { resourceId } = body;
|
|
957
1449
|
if (!this.adminforth.statuses.dbDiscover) {
|
|
@@ -981,6 +1473,9 @@ export default class AdminForthRestAPI {
|
|
|
981
1473
|
server.endpoint({
|
|
982
1474
|
method: 'POST',
|
|
983
1475
|
path: '/create_record',
|
|
1476
|
+
description: 'Creates a new record in the specified resource. The endpoint validates create permissions, required fields, hidden or backend-only field rules, polymorphic foreign keys, and resource hooks before persisting and returning the created primary key.',
|
|
1477
|
+
request_schema: createRecordRequestSchema,
|
|
1478
|
+
response_schema: createRecordResponseSchema,
|
|
984
1479
|
handler: async ({ body, adminUser, query, headers, cookies, requestUrl, response }) => {
|
|
985
1480
|
var _a, _b, _c, _d;
|
|
986
1481
|
const resource = this.adminforth.config.resources.find((res) => res.resourceId == body['resourceId']);
|
|
@@ -1110,6 +1605,9 @@ export default class AdminForthRestAPI {
|
|
|
1110
1605
|
server.endpoint({
|
|
1111
1606
|
method: 'POST',
|
|
1112
1607
|
path: '/update_record',
|
|
1608
|
+
description: 'Updates an existing record by primary key. The endpoint validates edit permissions, current record existence, hidden, backend-only, and read-only field rules, polymorphic foreign keys, and resource hooks before saving changes.',
|
|
1609
|
+
request_schema: updateRecordRequestSchema,
|
|
1610
|
+
response_schema: updateRecordResponseSchema,
|
|
1113
1611
|
handler: async ({ body, adminUser, query, headers, cookies, requestUrl, response }) => {
|
|
1114
1612
|
var _a, _b;
|
|
1115
1613
|
const resource = this.adminforth.config.resources.find((res) => res.resourceId == body['resourceId']);
|
|
@@ -1228,6 +1726,9 @@ export default class AdminForthRestAPI {
|
|
|
1228
1726
|
server.endpoint({
|
|
1229
1727
|
method: 'POST',
|
|
1230
1728
|
path: '/delete_record',
|
|
1729
|
+
description: 'Deletes an existing record by primary key. The endpoint validates delete permissions, loads the current record, executes configured cascade child deletion, and then removes the record.',
|
|
1730
|
+
request_schema: deleteRecordRequestSchema,
|
|
1731
|
+
response_schema: deleteRecordResponseSchema,
|
|
1231
1732
|
handler: async ({ body, adminUser, query, headers, cookies, requestUrl, response }) => {
|
|
1232
1733
|
const resource = this.adminforth.config.resources.find((res) => res.resourceId == body['resourceId']);
|
|
1233
1734
|
if (!resource) {
|
|
@@ -1288,6 +1789,9 @@ export default class AdminForthRestAPI {
|
|
|
1288
1789
|
server.endpoint({
|
|
1289
1790
|
method: 'POST',
|
|
1290
1791
|
path: '/start_custom_action',
|
|
1792
|
+
description: 'Executes a custom resource action for a single record. The endpoint validates the resource, action existence, and action permissions, then either returns a redirect URL or executes the action handler and returns its result together with action context.',
|
|
1793
|
+
request_schema: startCustomActionRequestSchema,
|
|
1794
|
+
response_schema: startCustomActionResponseSchema,
|
|
1291
1795
|
handler: async ({ body, adminUser, tr, cookies, response, headers }) => {
|
|
1292
1796
|
const { resourceId, actionId, recordId, extra } = body;
|
|
1293
1797
|
const resource = this.adminforth.config.resources.find((res) => res.resourceId == resourceId);
|
|
@@ -1319,6 +1823,85 @@ export default class AdminForthRestAPI {
|
|
|
1319
1823
|
resourceId }, actionResponse);
|
|
1320
1824
|
}
|
|
1321
1825
|
});
|
|
1826
|
+
server.endpoint({
|
|
1827
|
+
method: 'POST',
|
|
1828
|
+
path: '/start_custom_bulk_action',
|
|
1829
|
+
description: 'Executes a custom resource action in bulk mode for multiple records. The endpoint validates the resource, action existence, bulk handler availability, and permissions, then runs the bulk handler and returns its result together with action context.',
|
|
1830
|
+
request_schema: startCustomBulkActionRequestSchema,
|
|
1831
|
+
response_schema: startCustomBulkActionResponseSchema,
|
|
1832
|
+
handler: async ({ body, adminUser, tr, response, cookies, headers }) => {
|
|
1833
|
+
const { resourceId, actionId, recordIds, extra } = body;
|
|
1834
|
+
const resource = this.adminforth.config.resources.find((res) => res.resourceId == resourceId);
|
|
1835
|
+
if (!resource) {
|
|
1836
|
+
return { error: await tr(`Resource {resourceId} not found`, 'errors', { resourceId }) };
|
|
1837
|
+
}
|
|
1838
|
+
const { allowedActions } = await interpretResource(adminUser, resource, { requestBody: body }, ActionCheckSource.CustomActionRequest, this.adminforth);
|
|
1839
|
+
const action = resource.options.actions.find((act) => act.id == actionId);
|
|
1840
|
+
if (!action) {
|
|
1841
|
+
return { error: await tr(`Action {actionId} not found`, 'errors', { actionId }) };
|
|
1842
|
+
}
|
|
1843
|
+
if (!action.bulkHandler) {
|
|
1844
|
+
return { error: await tr(`Action "{actionId}" has no bulkHandler`, 'errors', { actionId }) };
|
|
1845
|
+
}
|
|
1846
|
+
if (action.allowed) {
|
|
1847
|
+
const execAllowed = await action.allowed({ adminUser, standardAllowedActions: allowedActions });
|
|
1848
|
+
if (!execAllowed) {
|
|
1849
|
+
return { error: await tr(`Action "{actionId}" not allowed`, 'errors', { actionId: action.name }) };
|
|
1850
|
+
}
|
|
1851
|
+
}
|
|
1852
|
+
const result = await action.bulkHandler({
|
|
1853
|
+
recordIds,
|
|
1854
|
+
adminUser,
|
|
1855
|
+
resource,
|
|
1856
|
+
tr,
|
|
1857
|
+
adminforth: this.adminforth,
|
|
1858
|
+
response,
|
|
1859
|
+
extra: Object.assign(Object.assign({}, extra), { cookies, headers }),
|
|
1860
|
+
});
|
|
1861
|
+
return Object.assign({ actionId, recordIds, resourceId }, result);
|
|
1862
|
+
}
|
|
1863
|
+
});
|
|
1864
|
+
server.endpoint({
|
|
1865
|
+
method: 'POST',
|
|
1866
|
+
path: '/validate_columns',
|
|
1867
|
+
description: 'Runs server-side custom validators for editable columns in a resource form. Only validators defined on submitted columns are executed, and the response maps each invalid column to its validation result.',
|
|
1868
|
+
request_schema: validateColumnsRequestSchema,
|
|
1869
|
+
response_schema: validateColumnsResponseSchema,
|
|
1870
|
+
handler: async ({ body, adminUser, query, headers, cookies, requestUrl, response }) => {
|
|
1871
|
+
const { resourceId, editableColumns, record } = body;
|
|
1872
|
+
const resource = this.adminforth.config.resources.find((res) => res.resourceId == resourceId);
|
|
1873
|
+
if (!resource) {
|
|
1874
|
+
return { error: `Resource '${resourceId}' not found` };
|
|
1875
|
+
}
|
|
1876
|
+
const validationResults = {};
|
|
1877
|
+
const customColumnValidatorsFunctions = [];
|
|
1878
|
+
for (const col of editableColumns) {
|
|
1879
|
+
const columnConfig = resource.columns.find((c) => c.name === col.name);
|
|
1880
|
+
if (columnConfig && columnConfig.validation) {
|
|
1881
|
+
customColumnValidatorsFunctions.push(async () => {
|
|
1882
|
+
for (const val of columnConfig.validation) {
|
|
1883
|
+
if (val.validator) {
|
|
1884
|
+
const result = await val.validator(col.value, record, this.adminforth);
|
|
1885
|
+
if (typeof result === 'object' && result.isValid === false) {
|
|
1886
|
+
validationResults[col.name] = {
|
|
1887
|
+
isValid: result.isValid,
|
|
1888
|
+
message: result.message,
|
|
1889
|
+
};
|
|
1890
|
+
break;
|
|
1891
|
+
}
|
|
1892
|
+
}
|
|
1893
|
+
}
|
|
1894
|
+
});
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1897
|
+
if (customColumnValidatorsFunctions.length) {
|
|
1898
|
+
await Promise.all(customColumnValidatorsFunctions.map((fn) => fn()));
|
|
1899
|
+
}
|
|
1900
|
+
return {
|
|
1901
|
+
validationResults
|
|
1902
|
+
};
|
|
1903
|
+
}
|
|
1904
|
+
});
|
|
1322
1905
|
// setup endpoints for all plugins
|
|
1323
1906
|
this.adminforth.activatedPlugins.forEach((plugin) => {
|
|
1324
1907
|
plugin.setupEndpoints(server);
|