adminforth 2.4.0-next.32 → 2.4.0-next.320
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 +14 -4
- package/commands/createApp/templates/api.ts.hbs +10 -0
- package/commands/createApp/templates/custom/tsconfig.json.hbs +2 -3
- package/commands/createApp/templates/index.ts.hbs +12 -1
- package/commands/createApp/templates/package.json.hbs +1 -1
- package/commands/createApp/templates/prisma.config.ts.hbs +8 -0
- package/commands/createApp/templates/schema.prisma.hbs +0 -1
- package/commands/createApp/utils.js +10 -0
- package/commands/createCustomComponent/configLoader.js +17 -4
- package/commands/createCustomComponent/main.js +13 -7
- package/commands/createCustomComponent/templates/customCrud/beforeActionButtons.vue.hbs +38 -0
- package/commands/createCustomComponent/templates/customCrud/saveButton.vue.hbs +28 -0
- package/commands/createPlugin/templates/custom/tsconfig.json.hbs +2 -5
- package/commands/createPlugin/templates/package.json.hbs +1 -1
- package/commands/generateModels.js +30 -22
- package/dist/auth.d.ts +9 -1
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +21 -2
- package/dist/auth.js.map +1 -1
- package/dist/dataConnectors/baseConnector.d.ts +1 -1
- package/dist/dataConnectors/baseConnector.d.ts.map +1 -1
- package/dist/dataConnectors/baseConnector.js +70 -18
- package/dist/dataConnectors/baseConnector.js.map +1 -1
- package/dist/dataConnectors/clickhouse.d.ts.map +1 -1
- package/dist/dataConnectors/clickhouse.js +15 -0
- package/dist/dataConnectors/clickhouse.js.map +1 -1
- package/dist/dataConnectors/mongo.d.ts.map +1 -1
- package/dist/dataConnectors/mongo.js +50 -15
- package/dist/dataConnectors/mongo.js.map +1 -1
- package/dist/dataConnectors/mysql.d.ts.map +1 -1
- package/dist/dataConnectors/mysql.js +11 -0
- package/dist/dataConnectors/mysql.js.map +1 -1
- package/dist/dataConnectors/postgres.d.ts.map +1 -1
- package/dist/dataConnectors/postgres.js +43 -14
- package/dist/dataConnectors/postgres.js.map +1 -1
- package/dist/dataConnectors/sqlite.d.ts.map +1 -1
- package/dist/dataConnectors/sqlite.js +11 -0
- package/dist/dataConnectors/sqlite.js.map +1 -1
- package/dist/index.d.ts +11 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +44 -21
- package/dist/index.js.map +1 -1
- package/dist/modules/codeInjector.d.ts +2 -0
- package/dist/modules/codeInjector.d.ts.map +1 -1
- package/dist/modules/codeInjector.js +62 -6
- package/dist/modules/codeInjector.js.map +1 -1
- package/dist/modules/configValidator.d.ts +6 -0
- package/dist/modules/configValidator.d.ts.map +1 -1
- package/dist/modules/configValidator.js +202 -25
- package/dist/modules/configValidator.js.map +1 -1
- package/dist/modules/restApi.d.ts +1 -1
- package/dist/modules/restApi.d.ts.map +1 -1
- package/dist/modules/restApi.js +177 -31
- package/dist/modules/restApi.js.map +1 -1
- package/dist/modules/styles.d.ts +499 -13
- package/dist/modules/styles.d.ts.map +1 -1
- package/dist/modules/styles.js +555 -31
- package/dist/modules/styles.js.map +1 -1
- package/dist/modules/utils.d.ts +7 -15
- package/dist/modules/utils.d.ts.map +1 -1
- package/dist/modules/utils.js +45 -68
- package/dist/modules/utils.js.map +1 -1
- package/dist/servers/express.d.ts +5 -0
- package/dist/servers/express.d.ts.map +1 -1
- package/dist/servers/express.js +40 -1
- package/dist/servers/express.js.map +1 -1
- package/dist/spa/index.html +1 -1
- package/dist/spa/package-lock.json +1208 -708
- package/dist/spa/package.json +34 -34
- package/dist/spa/src/App.vue +61 -173
- package/dist/spa/src/adminforth.ts +42 -18
- package/dist/spa/src/afcl/AreaChart.vue +0 -1
- package/dist/spa/src/afcl/BarChart.vue +2 -2
- package/dist/spa/src/afcl/Button.vue +6 -6
- package/dist/spa/src/afcl/ButtonGroup.vue +91 -0
- package/dist/spa/src/afcl/Card.vue +25 -0
- package/dist/spa/src/afcl/Checkbox.vue +21 -13
- package/dist/spa/src/afcl/CountryFlag.vue +4 -1
- package/dist/spa/src/{components/CustomDatePicker.vue → afcl/DatePicker.vue} +95 -9
- package/dist/spa/src/afcl/Dialog.vue +47 -27
- package/dist/spa/src/afcl/Dropzone.vue +145 -48
- package/dist/spa/src/afcl/Input.vue +14 -6
- package/dist/spa/src/afcl/JsonViewer.vue +25 -0
- package/dist/spa/src/afcl/LinkButton.vue +3 -3
- package/dist/spa/src/afcl/PieChart.vue +5 -5
- package/dist/spa/src/afcl/ProgressBar.vue +7 -7
- package/dist/spa/src/afcl/Select.vue +82 -34
- package/dist/spa/src/afcl/Skeleton.vue +6 -6
- package/dist/spa/src/afcl/Table.vue +313 -75
- package/dist/spa/src/afcl/Textarea.vue +31 -0
- package/dist/spa/src/afcl/Toggle.vue +32 -0
- package/dist/spa/src/afcl/Tooltip.vue +28 -18
- package/dist/spa/src/afcl/VerticalTabs.vue +21 -7
- package/dist/spa/src/afcl/index.ts +6 -3
- package/dist/spa/src/components/AcceptModal.vue +48 -14
- package/dist/spa/src/components/Breadcrumbs.vue +5 -5
- package/dist/spa/src/components/CallActionWrapper.vue +15 -0
- package/dist/spa/src/components/ColumnValueInput.vue +38 -18
- package/dist/spa/src/components/ColumnValueInputWrapper.vue +4 -3
- package/dist/spa/src/components/CustomDateRangePicker.vue +9 -8
- package/dist/spa/src/components/CustomRangePicker.vue +37 -21
- package/dist/spa/src/components/ErrorMessage.vue +21 -0
- package/dist/spa/src/components/Filters.vue +195 -132
- package/dist/spa/src/components/GroupsTable.vue +9 -8
- package/dist/spa/src/components/MenuLink.vue +95 -23
- package/dist/spa/src/components/ResourceForm.vue +94 -51
- package/dist/spa/src/components/ResourceListTable.vue +119 -94
- package/dist/spa/src/components/ResourceListTableVirtual.vue +118 -87
- package/dist/spa/src/components/ShowTable.vue +21 -15
- package/dist/spa/src/components/Sidebar.vue +472 -0
- package/dist/spa/src/components/SingleSkeletLoader.vue +6 -6
- package/dist/spa/src/components/SkeleteLoader.vue +3 -3
- package/dist/spa/src/components/ThreeDotsMenu.vue +84 -15
- package/dist/spa/src/components/Toast.vue +40 -29
- package/dist/spa/src/components/UserMenuSettingsButton.vue +69 -0
- package/dist/spa/src/components/ValueRenderer.vue +44 -17
- package/dist/spa/src/controls/BoolToggle.vue +34 -0
- package/dist/spa/src/i18n.ts +5 -3
- package/dist/spa/src/main.ts +1 -1
- package/dist/spa/src/renderers/CompactField.vue +1 -1
- package/dist/spa/src/renderers/CompactUUID.vue +1 -1
- package/dist/spa/src/router/index.ts +8 -0
- package/dist/spa/src/shims-vue.d.ts +5 -0
- package/dist/spa/src/spa_types/core.ts +13 -1
- package/dist/spa/src/stores/core.ts +13 -1
- package/dist/spa/src/stores/filters.ts +33 -2
- package/dist/spa/src/stores/modal.ts +6 -1
- package/dist/spa/src/stores/toast.ts +22 -3
- package/dist/spa/src/types/Back.ts +167 -23
- package/dist/spa/src/types/Common.ts +92 -32
- package/dist/spa/src/types/FrontendAPI.ts +31 -5
- package/dist/spa/src/types/adapters/CaptchaAdapter.ts +34 -0
- package/dist/spa/src/types/adapters/EmailAdapter.ts +2 -4
- package/dist/spa/src/types/adapters/ImageVisionAdapter.ts +30 -0
- package/dist/spa/src/types/adapters/KeyValueAdapter.ts +16 -0
- package/dist/spa/src/types/adapters/StorageAdapter.ts +4 -2
- package/dist/spa/src/types/adapters/index.ts +3 -0
- package/dist/spa/src/utils.ts +291 -11
- package/dist/spa/src/views/CreateView.vue +63 -21
- package/dist/spa/src/views/EditView.vue +55 -22
- package/dist/spa/src/views/ListView.vue +144 -87
- package/dist/spa/src/views/LoginView.vue +26 -35
- package/dist/spa/src/views/ResourceParent.vue +2 -2
- package/dist/spa/src/views/SettingsView.vue +121 -0
- package/dist/spa/src/views/ShowView.vue +83 -53
- package/dist/spa/src/websocket.ts +6 -1
- package/dist/spa/tsconfig.app.json +1 -1
- package/dist/spa/vite.config.ts +45 -2
- package/dist/types/Back.d.ts +150 -14
- package/dist/types/Back.d.ts.map +1 -1
- package/dist/types/Back.js +15 -0
- package/dist/types/Back.js.map +1 -1
- package/dist/types/Common.d.ts +107 -29
- package/dist/types/Common.d.ts.map +1 -1
- package/dist/types/Common.js.map +1 -1
- package/dist/types/FrontendAPI.d.ts +31 -3
- package/dist/types/FrontendAPI.d.ts.map +1 -1
- package/dist/types/FrontendAPI.js.map +1 -1
- package/dist/types/adapters/CaptchaAdapter.d.ts +30 -0
- package/dist/types/adapters/CaptchaAdapter.d.ts.map +1 -0
- package/dist/types/adapters/CaptchaAdapter.js +5 -0
- package/dist/types/adapters/CaptchaAdapter.js.map +1 -0
- package/dist/types/adapters/EmailAdapter.d.ts +2 -3
- package/dist/types/adapters/EmailAdapter.d.ts.map +1 -1
- package/dist/types/adapters/ImageVisionAdapter.d.ts +25 -0
- package/dist/types/adapters/ImageVisionAdapter.d.ts.map +1 -0
- package/dist/types/adapters/ImageVisionAdapter.js +2 -0
- package/dist/types/adapters/ImageVisionAdapter.js.map +1 -0
- package/dist/types/adapters/KeyValueAdapter.d.ts +10 -0
- package/dist/types/adapters/KeyValueAdapter.d.ts.map +1 -0
- package/dist/types/adapters/KeyValueAdapter.js +2 -0
- package/dist/types/adapters/KeyValueAdapter.js.map +1 -0
- package/dist/types/adapters/StorageAdapter.d.ts +2 -0
- package/dist/types/adapters/StorageAdapter.d.ts.map +1 -1
- package/dist/types/adapters/index.d.ts +3 -0
- package/dist/types/adapters/index.d.ts.map +1 -1
- package/package.json +4 -2
package/dist/modules/restApi.js
CHANGED
|
@@ -1,7 +1,28 @@
|
|
|
1
|
-
import { ADMINFORTH_VERSION, listify } from './utils.js';
|
|
1
|
+
import { ADMINFORTH_VERSION, listify, getLoginPromptHTML } from './utils.js';
|
|
2
2
|
import AdminForthAuth from "../auth.js";
|
|
3
3
|
import { ActionCheckSource, AdminForthDataTypes, AdminForthFilterOperators, AdminForthResourcePages, AllowedActionsEnum } from "../types/Common.js";
|
|
4
4
|
import { filtersTools } from "../modules/filtersTools.js";
|
|
5
|
+
async function resolveBoolOrFn(val, ctx) {
|
|
6
|
+
if (typeof val === 'function') {
|
|
7
|
+
return !!(await (val)(ctx));
|
|
8
|
+
}
|
|
9
|
+
return !!val;
|
|
10
|
+
}
|
|
11
|
+
async function isBackendOnly(col, ctx) {
|
|
12
|
+
return await resolveBoolOrFn(col.backendOnly, ctx);
|
|
13
|
+
}
|
|
14
|
+
async function isShown(col, page, ctx) {
|
|
15
|
+
const s = col.showIn || {};
|
|
16
|
+
if (s[page] !== undefined)
|
|
17
|
+
return await resolveBoolOrFn(s[page], ctx);
|
|
18
|
+
if (s.all !== undefined)
|
|
19
|
+
return await resolveBoolOrFn(s.all, ctx);
|
|
20
|
+
return true;
|
|
21
|
+
}
|
|
22
|
+
async function isFilledOnCreate(col) {
|
|
23
|
+
const fillOnCreate = !!col.fillOnCreate;
|
|
24
|
+
return fillOnCreate;
|
|
25
|
+
}
|
|
5
26
|
export async function interpretResource(adminUser, resource, meta, source, adminforth) {
|
|
6
27
|
if (process.env.HEAVY_DEBUG) {
|
|
7
28
|
console.log('🪲Interpreting resource', resource.resourceId, source, 'adminUser', adminUser);
|
|
@@ -44,7 +65,7 @@ export default class AdminForthRestAPI {
|
|
|
44
65
|
constructor(adminforth) {
|
|
45
66
|
this.adminforth = adminforth;
|
|
46
67
|
}
|
|
47
|
-
async processLoginCallbacks(adminUser, toReturn, response, extra) {
|
|
68
|
+
async processLoginCallbacks(adminUser, toReturn, response, extra, rememberMeDays) {
|
|
48
69
|
var _a, _b, _c;
|
|
49
70
|
const beforeLoginConfirmation = this.adminforth.config.auth.beforeLoginConfirmation;
|
|
50
71
|
for (const hook of listify(beforeLoginConfirmation)) {
|
|
@@ -53,6 +74,7 @@ export default class AdminForthRestAPI {
|
|
|
53
74
|
response,
|
|
54
75
|
adminforth: this.adminforth,
|
|
55
76
|
extra,
|
|
77
|
+
rememberMeDays
|
|
56
78
|
});
|
|
57
79
|
if (((_a = resp === null || resp === void 0 ? void 0 : resp.body) === null || _a === void 0 ? void 0 : _a.redirectTo) || (resp === null || resp === void 0 ? void 0 : resp.error)) {
|
|
58
80
|
// delete all items from toReturn and add these:
|
|
@@ -109,9 +131,11 @@ export default class AdminForthRestAPI {
|
|
|
109
131
|
pk: userRecord[userResource.columns.find((col) => col.primaryKey).name],
|
|
110
132
|
username,
|
|
111
133
|
};
|
|
112
|
-
|
|
134
|
+
const expireInDays = rememberMe ? this.adminforth.config.auth.rememberMeDays || 30 : 1;
|
|
135
|
+
await this.processLoginCallbacks(adminUser, toReturn, response, {
|
|
136
|
+
body, headers, query, cookies, requestUrl,
|
|
137
|
+
}, expireInDays);
|
|
113
138
|
if (toReturn.allowedLogin) {
|
|
114
|
-
const expireInDays = rememberMe && this.adminforth.config.auth.rememberMeDays;
|
|
115
139
|
this.adminforth.auth.setAuthCookie({
|
|
116
140
|
expireInDays,
|
|
117
141
|
response,
|
|
@@ -142,6 +166,17 @@ export default class AdminForthRestAPI {
|
|
|
142
166
|
return { ok: true };
|
|
143
167
|
},
|
|
144
168
|
});
|
|
169
|
+
server.endpoint({
|
|
170
|
+
noAuth: true,
|
|
171
|
+
method: 'GET',
|
|
172
|
+
path: '/get_login_form_config',
|
|
173
|
+
handler: async ({ tr }) => {
|
|
174
|
+
const loginPromptHTML = await getLoginPromptHTML(this.adminforth.config.auth.loginPromptHTML);
|
|
175
|
+
return {
|
|
176
|
+
loginPromptHTML: await tr(loginPromptHTML, 'system.loginPromptHTML'),
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
});
|
|
145
180
|
server.endpoint({
|
|
146
181
|
noAuth: true,
|
|
147
182
|
method: 'GET',
|
|
@@ -161,21 +196,24 @@ export default class AdminForthRestAPI {
|
|
|
161
196
|
usernameFieldName: usernameColumn.label,
|
|
162
197
|
loginBackgroundImage: this.adminforth.config.auth.loginBackgroundImage,
|
|
163
198
|
loginBackgroundPosition: this.adminforth.config.auth.loginBackgroundPosition,
|
|
199
|
+
removeBackgroundBlendMode: this.adminforth.config.auth.removeBackgroundBlendMode,
|
|
164
200
|
title: (_a = this.adminforth.config.customization) === null || _a === void 0 ? void 0 : _a.title,
|
|
165
201
|
demoCredentials: this.adminforth.config.auth.demoCredentials,
|
|
166
|
-
loginPromptHTML: await tr(this.adminforth.config.auth.loginPromptHTML, 'system.loginPromptHTML'),
|
|
167
202
|
loginPageInjections: this.adminforth.config.customization.loginPageInjections,
|
|
168
203
|
globalInjections: {
|
|
169
204
|
everyPageBottom: this.adminforth.config.customization.globalInjections.everyPageBottom,
|
|
205
|
+
sidebarTop: this.adminforth.config.customization.globalInjections.sidebarTop,
|
|
170
206
|
},
|
|
171
207
|
rememberMeDays: this.adminforth.config.auth.rememberMeDays,
|
|
208
|
+
singleTheme: this.adminforth.config.customization.singleTheme,
|
|
209
|
+
customHeadItems: this.adminforth.config.customization.customHeadItems,
|
|
172
210
|
};
|
|
173
211
|
},
|
|
174
212
|
});
|
|
175
213
|
server.endpoint({
|
|
176
214
|
method: 'GET',
|
|
177
215
|
path: '/get_base_config',
|
|
178
|
-
handler: async ({ input, adminUser, cookies, tr }) => {
|
|
216
|
+
handler: async ({ input, adminUser, cookies, tr, response }) => {
|
|
179
217
|
var _a, _b, _c;
|
|
180
218
|
let username = '';
|
|
181
219
|
let userFullName = '';
|
|
@@ -183,6 +221,10 @@ export default class AdminForthRestAPI {
|
|
|
183
221
|
if (!this.adminforth.config.auth) {
|
|
184
222
|
throw new Error('No config.auth defined');
|
|
185
223
|
}
|
|
224
|
+
response.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate');
|
|
225
|
+
response.setHeader('Pragma', 'no-cache');
|
|
226
|
+
response.setHeader('Expires', '0');
|
|
227
|
+
response.setHeader('Surrogate-Control', 'no-store');
|
|
186
228
|
const dbUser = adminUser.dbUser;
|
|
187
229
|
username = dbUser[this.adminforth.config.auth.usernameField];
|
|
188
230
|
userFullName = dbUser[this.adminforth.config.auth.userFullNameField];
|
|
@@ -233,15 +275,19 @@ export default class AdminForthRestAPI {
|
|
|
233
275
|
usernameFieldName: usernameColumn.label,
|
|
234
276
|
loginBackgroundImage: this.adminforth.config.auth.loginBackgroundImage,
|
|
235
277
|
loginBackgroundPosition: this.adminforth.config.auth.loginBackgroundPosition,
|
|
278
|
+
removeBackgroundBlendMode: this.adminforth.config.auth.removeBackgroundBlendMode,
|
|
236
279
|
title: (_c = this.adminforth.config.customization) === null || _c === void 0 ? void 0 : _c.title,
|
|
237
280
|
demoCredentials: this.adminforth.config.auth.demoCredentials,
|
|
238
|
-
loginPromptHTML: await tr(this.adminforth.config.auth.loginPromptHTML, 'system.loginPromptHTML'),
|
|
239
281
|
loginPageInjections: this.adminforth.config.customization.loginPageInjections,
|
|
240
282
|
rememberMeDays: this.adminforth.config.auth.rememberMeDays,
|
|
283
|
+
singleTheme: this.adminforth.config.customization.singleTheme,
|
|
284
|
+
customHeadItems: this.adminforth.config.customization.customHeadItems,
|
|
241
285
|
};
|
|
242
286
|
const loggedInPart = {
|
|
243
287
|
showBrandNameInSidebar: this.adminforth.config.customization.showBrandNameInSidebar,
|
|
288
|
+
showBrandLogoInSidebar: this.adminforth.config.customization.showBrandLogoInSidebar,
|
|
244
289
|
brandLogo: this.adminforth.config.customization.brandLogo,
|
|
290
|
+
iconOnlySidebar: this.adminforth.config.customization.iconOnlySidebar,
|
|
245
291
|
datesFormat: this.adminforth.config.customization.datesFormat,
|
|
246
292
|
timeFormat: this.adminforth.config.customization.timeFormat,
|
|
247
293
|
auth: this.adminforth.config.auth,
|
|
@@ -251,6 +297,7 @@ export default class AdminForthRestAPI {
|
|
|
251
297
|
announcementBadge,
|
|
252
298
|
globalInjections: this.adminforth.config.customization.globalInjections,
|
|
253
299
|
userFullnameField: this.adminforth.config.auth.userFullNameField,
|
|
300
|
+
settingPages: this.adminforth.config.auth.userMenuSettingsPages,
|
|
254
301
|
};
|
|
255
302
|
// translate menu labels
|
|
256
303
|
const translateRoutines = [];
|
|
@@ -268,19 +315,37 @@ export default class AdminForthRestAPI {
|
|
|
268
315
|
if (menuItem.children) {
|
|
269
316
|
menuItem.children.forEach(processItem);
|
|
270
317
|
}
|
|
318
|
+
if (menuItem.pageLabel) {
|
|
319
|
+
translateRoutines.push((async () => {
|
|
320
|
+
menuItem.pageLabel = await tr(menuItem.pageLabel, `UserMenu.${menuItem.pageLabel}`);
|
|
321
|
+
})());
|
|
322
|
+
}
|
|
271
323
|
};
|
|
272
324
|
newMenu.forEach((menuItem) => {
|
|
273
325
|
processItem(menuItem);
|
|
274
326
|
});
|
|
327
|
+
if (this.adminforth.config.auth.userMenuSettingsPages) {
|
|
328
|
+
this.adminforth.config.auth.userMenuSettingsPages.forEach((page) => {
|
|
329
|
+
processItem(page);
|
|
330
|
+
});
|
|
331
|
+
}
|
|
275
332
|
await Promise.all(translateRoutines);
|
|
276
333
|
// strip all backendOnly fields or not described in adminForth fields from dbUser
|
|
277
334
|
// (when user defines column and does not set backendOnly, we assume it is not backendOnly)
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
335
|
+
const ctx = {
|
|
336
|
+
adminUser,
|
|
337
|
+
resource: userResource,
|
|
338
|
+
meta: {},
|
|
339
|
+
source: ActionCheckSource.ShowRequest,
|
|
340
|
+
adminforth: this.adminforth,
|
|
341
|
+
};
|
|
342
|
+
for (const key of Object.keys(adminUser.dbUser)) {
|
|
343
|
+
const col = userResource.columns.find((c) => c.name === key);
|
|
344
|
+
const bo = col ? await isBackendOnly(col, ctx) : true;
|
|
345
|
+
if (!col || bo) {
|
|
281
346
|
delete adminUser.dbUser[key];
|
|
282
347
|
}
|
|
283
|
-
}
|
|
348
|
+
}
|
|
284
349
|
return {
|
|
285
350
|
user: userData,
|
|
286
351
|
resources: this.adminforth.config.resources.map((res) => ({
|
|
@@ -435,7 +500,7 @@ export default class AdminForthRestAPI {
|
|
|
435
500
|
method: 'POST',
|
|
436
501
|
path: '/get_resource_data',
|
|
437
502
|
handler: async ({ body, adminUser, headers, query, cookies, requestUrl }) => {
|
|
438
|
-
var _a, _b, _c, _d, _e;
|
|
503
|
+
var _a, _b, _c, _d, _e, _f;
|
|
439
504
|
const { resourceId, source } = body;
|
|
440
505
|
if (['show', 'list', 'edit'].includes(source) === false) {
|
|
441
506
|
return { error: 'Invalid source, should be list or show' };
|
|
@@ -632,22 +697,38 @@ export default class AdminForthRestAPI {
|
|
|
632
697
|
}
|
|
633
698
|
});
|
|
634
699
|
}));
|
|
700
|
+
const pkField = (_d = resource.columns.find((col) => col.primaryKey)) === null || _d === void 0 ? void 0 : _d.name;
|
|
635
701
|
// remove all columns which are not defined in resources, or defined but backendOnly
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
702
|
+
{
|
|
703
|
+
const ctx = {
|
|
704
|
+
adminUser,
|
|
705
|
+
resource,
|
|
706
|
+
meta,
|
|
707
|
+
source: {
|
|
708
|
+
show: ActionCheckSource.ShowRequest,
|
|
709
|
+
list: ActionCheckSource.ListRequest,
|
|
710
|
+
edit: ActionCheckSource.EditLoadRequest,
|
|
711
|
+
}[source],
|
|
712
|
+
adminforth: this.adminforth,
|
|
713
|
+
};
|
|
714
|
+
for (const item of data.data) {
|
|
715
|
+
for (const key of Object.keys(item)) {
|
|
716
|
+
const col = resource.columns.find((c) => c.name === key);
|
|
717
|
+
const bo = col ? await isBackendOnly(col, ctx) : true;
|
|
718
|
+
if (!col || bo) {
|
|
719
|
+
delete item[key];
|
|
720
|
+
}
|
|
640
721
|
}
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
}
|
|
722
|
+
item._label = resource.recordLabel(item);
|
|
723
|
+
}
|
|
724
|
+
}
|
|
644
725
|
if (source === 'list' && resource.options.listTableClickUrl) {
|
|
645
726
|
await Promise.all(data.data.map(async (item) => {
|
|
646
727
|
item._clickUrl = await resource.options.listTableClickUrl(item, adminUser);
|
|
647
728
|
}));
|
|
648
729
|
}
|
|
649
730
|
// only after adminforth made all post processing, give user ability to edit it
|
|
650
|
-
for (const hook of listify((
|
|
731
|
+
for (const hook of listify((_f = (_e = resource.hooks) === null || _e === void 0 ? void 0 : _e[hookSource]) === null || _f === void 0 ? void 0 : _f.afterDatasourceResponse)) {
|
|
651
732
|
const resp = await hook({
|
|
652
733
|
resource,
|
|
653
734
|
query: body,
|
|
@@ -672,7 +753,7 @@ export default class AdminForthRestAPI {
|
|
|
672
753
|
method: 'POST',
|
|
673
754
|
path: '/get_resource_foreign_data',
|
|
674
755
|
handler: async ({ body, adminUser, headers, query, cookies, requestUrl }) => {
|
|
675
|
-
const { resourceId, column } = body;
|
|
756
|
+
const { resourceId, column, search } = body;
|
|
676
757
|
if (!this.adminforth.statuses.dbDiscover) {
|
|
677
758
|
return { error: 'Database discovery not started' };
|
|
678
759
|
}
|
|
@@ -742,6 +823,43 @@ export default class AdminForthRestAPI {
|
|
|
742
823
|
throw new Error(`Wrong filter object value: ${JSON.stringify(filters)}`);
|
|
743
824
|
}
|
|
744
825
|
}
|
|
826
|
+
if (search && search.trim() && columnConfig.foreignResource.searchableFields) {
|
|
827
|
+
const searchableFields = Array.isArray(columnConfig.foreignResource.searchableFields)
|
|
828
|
+
? columnConfig.foreignResource.searchableFields
|
|
829
|
+
: [columnConfig.foreignResource.searchableFields];
|
|
830
|
+
const searchOperator = columnConfig.foreignResource.searchIsCaseSensitive
|
|
831
|
+
? AdminForthFilterOperators.LIKE
|
|
832
|
+
: AdminForthFilterOperators.ILIKE;
|
|
833
|
+
const availableSearchFields = searchableFields.filter((fieldName) => {
|
|
834
|
+
const fieldExists = targetResource.columns.some(col => col.name === fieldName);
|
|
835
|
+
if (!fieldExists) {
|
|
836
|
+
process.env.HEAVY_DEBUG && console.log(`⚠️ Field '${fieldName}' not found in polymorphic target resource '${targetResource.resourceId}', skipping in search filter.`);
|
|
837
|
+
}
|
|
838
|
+
return fieldExists;
|
|
839
|
+
});
|
|
840
|
+
if (availableSearchFields.length === 0) {
|
|
841
|
+
process.env.HEAVY_DEBUG && console.log(`⚠️ No searchable fields available in polymorphic target resource '${targetResource.resourceId}', skipping resource.`);
|
|
842
|
+
resolve({ items: [] });
|
|
843
|
+
return;
|
|
844
|
+
}
|
|
845
|
+
const searchFilters = availableSearchFields.map((fieldName) => {
|
|
846
|
+
const filter = {
|
|
847
|
+
field: fieldName,
|
|
848
|
+
operator: searchOperator,
|
|
849
|
+
value: search.trim(),
|
|
850
|
+
};
|
|
851
|
+
return filter;
|
|
852
|
+
});
|
|
853
|
+
if (searchFilters.length > 1) {
|
|
854
|
+
normalizedFilters.subFilters.push({
|
|
855
|
+
operator: AdminForthFilterOperators.OR,
|
|
856
|
+
subFilters: searchFilters,
|
|
857
|
+
});
|
|
858
|
+
}
|
|
859
|
+
else if (searchFilters.length === 1) {
|
|
860
|
+
normalizedFilters.subFilters.push(searchFilters[0]);
|
|
861
|
+
}
|
|
862
|
+
}
|
|
745
863
|
const dbDataItems = await this.adminforth.connectors[targetResource.dataSource].getData({
|
|
746
864
|
resource: targetResource,
|
|
747
865
|
limit,
|
|
@@ -835,6 +953,7 @@ export default class AdminForthRestAPI {
|
|
|
835
953
|
return { error };
|
|
836
954
|
}
|
|
837
955
|
const { record } = body;
|
|
956
|
+
// todo if showIn.create is function, code below will be buggy (will not detect required fact)
|
|
838
957
|
for (const column of resource.columns) {
|
|
839
958
|
if (((_a = column.required) === null || _a === void 0 ? void 0 : _a.create) &&
|
|
840
959
|
record[column.name] === undefined &&
|
|
@@ -842,11 +961,29 @@ export default class AdminForthRestAPI {
|
|
|
842
961
|
return { error: `Column '${column.name}' is required`, ok: false };
|
|
843
962
|
}
|
|
844
963
|
}
|
|
964
|
+
const ctxCreate = {
|
|
965
|
+
adminUser,
|
|
966
|
+
resource,
|
|
967
|
+
meta: { requestBody: body },
|
|
968
|
+
source: ActionCheckSource.CreateRequest,
|
|
969
|
+
adminforth: this.adminforth,
|
|
970
|
+
};
|
|
971
|
+
for (const column of resource.columns) {
|
|
972
|
+
if ((_b = column.required) === null || _b === void 0 ? void 0 : _b.create) {
|
|
973
|
+
const shown = await isShown(column, 'create', ctxCreate);
|
|
974
|
+
if (shown && record[column.name] === undefined) {
|
|
975
|
+
return { error: `Column '${column.name}' is required`, ok: false };
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
}
|
|
845
979
|
for (const column of resource.columns) {
|
|
846
980
|
const fieldName = column.name;
|
|
847
981
|
if (fieldName in record) {
|
|
848
|
-
|
|
849
|
-
|
|
982
|
+
const shown = await isShown(column, 'create', ctxCreate); //
|
|
983
|
+
const bo = await isBackendOnly(column, ctxCreate);
|
|
984
|
+
const filledOnCreate = await isFilledOnCreate(column);
|
|
985
|
+
if ((!shown && !filledOnCreate) || bo) {
|
|
986
|
+
return { error: `Field "${fieldName}" cannot be modified as it is restricted from creation (backendOnly or showIn.create is false, please set it to true)`, ok: false };
|
|
850
987
|
}
|
|
851
988
|
}
|
|
852
989
|
}
|
|
@@ -890,7 +1027,7 @@ export default class AdminForthRestAPI {
|
|
|
890
1027
|
}
|
|
891
1028
|
const response = await this.adminforth.createResourceRecord({ resource, record, adminUser, extra: { body, query, headers, cookies, requestUrl } });
|
|
892
1029
|
if (response.error) {
|
|
893
|
-
return { error: response.error, ok: false };
|
|
1030
|
+
return { error: response.error, ok: false, newRecordId: response.newRecordId };
|
|
894
1031
|
}
|
|
895
1032
|
const connector = this.adminforth.connectors[resource.dataSource];
|
|
896
1033
|
return {
|
|
@@ -903,7 +1040,7 @@ export default class AdminForthRestAPI {
|
|
|
903
1040
|
method: 'POST',
|
|
904
1041
|
path: '/update_record',
|
|
905
1042
|
handler: async ({ body, adminUser, query, headers, cookies, requestUrl }) => {
|
|
906
|
-
var _a, _b
|
|
1043
|
+
var _a, _b;
|
|
907
1044
|
const resource = this.adminforth.config.resources.find((res) => res.resourceId == body['resourceId']);
|
|
908
1045
|
if (!resource) {
|
|
909
1046
|
return { error: `Resource '${body['resourceId']}' not found` };
|
|
@@ -921,21 +1058,30 @@ export default class AdminForthRestAPI {
|
|
|
921
1058
|
if (!allowed) {
|
|
922
1059
|
return { error: allowedError };
|
|
923
1060
|
}
|
|
1061
|
+
const ctxEdit = {
|
|
1062
|
+
adminUser,
|
|
1063
|
+
resource,
|
|
1064
|
+
meta: { requestBody: body, newRecord: record, oldRecord, pk: recordId },
|
|
1065
|
+
source: ActionCheckSource.EditRequest,
|
|
1066
|
+
adminforth: this.adminforth,
|
|
1067
|
+
};
|
|
924
1068
|
for (const column of resource.columns) {
|
|
925
1069
|
const fieldName = column.name;
|
|
926
1070
|
if (fieldName in record) {
|
|
927
|
-
|
|
928
|
-
|
|
1071
|
+
const shown = await isShown(column, 'edit', ctxEdit);
|
|
1072
|
+
const bo = await isBackendOnly(column, ctxEdit);
|
|
1073
|
+
if (!shown || column.editReadonly || bo) {
|
|
1074
|
+
return { error: `Field "${fieldName}" cannot be modified as it is restricted from editing (backendOnly or showIn.edit is false, please set it to true)`, ok: false };
|
|
929
1075
|
}
|
|
930
1076
|
}
|
|
931
1077
|
}
|
|
932
1078
|
// for polymorphic foreign resources, we need to find out the value for polymorphicOn column
|
|
933
1079
|
for (const column of resource.columns) {
|
|
934
|
-
if (((
|
|
1080
|
+
if (((_a = column.foreignResource) === null || _a === void 0 ? void 0 : _a.polymorphicOn) && record[column.name] === null) {
|
|
935
1081
|
const systemResource = column.foreignResource.polymorphicResources.find(pr => pr.resourceId === null);
|
|
936
1082
|
record[column.foreignResource.polymorphicOn] = systemResource.whenValue;
|
|
937
1083
|
}
|
|
938
|
-
else if (((
|
|
1084
|
+
else if (((_b = column.foreignResource) === null || _b === void 0 ? void 0 : _b.polymorphicOn) && record[column.name]) {
|
|
939
1085
|
let newPolymorphicOnValue = null;
|
|
940
1086
|
if (record[column.name]) {
|
|
941
1087
|
const targetResources = {};
|
|
@@ -1047,7 +1193,7 @@ export default class AdminForthRestAPI {
|
|
|
1047
1193
|
method: 'POST',
|
|
1048
1194
|
path: '/start_custom_action',
|
|
1049
1195
|
handler: async ({ body, adminUser, tr }) => {
|
|
1050
|
-
const { resourceId, actionId, recordId } = body;
|
|
1196
|
+
const { resourceId, actionId, recordId, extra } = body;
|
|
1051
1197
|
const resource = this.adminforth.config.resources.find((res) => res.resourceId == resourceId);
|
|
1052
1198
|
if (!resource) {
|
|
1053
1199
|
return { error: await tr(`Resource {resourceId} not found`, 'errors', { resourceId }) };
|
|
@@ -1071,7 +1217,7 @@ export default class AdminForthRestAPI {
|
|
|
1071
1217
|
redirectUrl: action.url
|
|
1072
1218
|
};
|
|
1073
1219
|
}
|
|
1074
|
-
const response = await action.action({ recordId, adminUser, resource, tr, adminforth: this.adminforth });
|
|
1220
|
+
const response = await action.action({ recordId, adminUser, resource, tr, adminforth: this.adminforth, extra });
|
|
1075
1221
|
return Object.assign({ actionId,
|
|
1076
1222
|
recordId,
|
|
1077
1223
|
resourceId }, response);
|