adminforth 2.4.0-next.33 → 2.4.0-next.331
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 +209 -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 +199 -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 +132 -174
- package/dist/spa/src/adminforth.ts +41 -17
- 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 +3 -3
- 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 +99 -51
- package/dist/spa/src/components/ResourceListTable.vue +121 -95
- package/dist/spa/src/components/ResourceListTableVirtual.vue +119 -88
- 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 +15 -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 +168 -23
- package/dist/spa/src/types/Common.ts +109 -32
- package/dist/spa/src/types/FrontendAPI.ts +32 -23
- 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 +88 -22
- 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 +151 -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 +123 -29
- package/dist/types/Common.d.ts.map +1 -1
- package/dist/types/Common.js.map +1 -1
- package/dist/types/FrontendAPI.d.ts +32 -18
- 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,29 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Filters, } from "../types/Back.js";
|
|
2
|
+
import { ADMINFORTH_VERSION, listify, getLoginPromptHTML } from './utils.js';
|
|
2
3
|
import AdminForthAuth from "../auth.js";
|
|
3
4
|
import { ActionCheckSource, AdminForthDataTypes, AdminForthFilterOperators, AdminForthResourcePages, AllowedActionsEnum } from "../types/Common.js";
|
|
4
5
|
import { filtersTools } from "../modules/filtersTools.js";
|
|
6
|
+
async function resolveBoolOrFn(val, ctx) {
|
|
7
|
+
if (typeof val === 'function') {
|
|
8
|
+
return !!(await (val)(ctx));
|
|
9
|
+
}
|
|
10
|
+
return !!val;
|
|
11
|
+
}
|
|
12
|
+
async function isBackendOnly(col, ctx) {
|
|
13
|
+
return await resolveBoolOrFn(col.backendOnly, ctx);
|
|
14
|
+
}
|
|
15
|
+
async function isShown(col, page, ctx) {
|
|
16
|
+
const s = col.showIn || {};
|
|
17
|
+
if (s[page] !== undefined)
|
|
18
|
+
return await resolveBoolOrFn(s[page], ctx);
|
|
19
|
+
if (s.all !== undefined)
|
|
20
|
+
return await resolveBoolOrFn(s.all, ctx);
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
async function isFilledOnCreate(col) {
|
|
24
|
+
const fillOnCreate = !!col.fillOnCreate;
|
|
25
|
+
return fillOnCreate;
|
|
26
|
+
}
|
|
5
27
|
export async function interpretResource(adminUser, resource, meta, source, adminforth) {
|
|
6
28
|
if (process.env.HEAVY_DEBUG) {
|
|
7
29
|
console.log('🪲Interpreting resource', resource.resourceId, source, 'adminUser', adminUser);
|
|
@@ -44,7 +66,7 @@ export default class AdminForthRestAPI {
|
|
|
44
66
|
constructor(adminforth) {
|
|
45
67
|
this.adminforth = adminforth;
|
|
46
68
|
}
|
|
47
|
-
async processLoginCallbacks(adminUser, toReturn, response, extra) {
|
|
69
|
+
async processLoginCallbacks(adminUser, toReturn, response, extra, rememberMeDays) {
|
|
48
70
|
var _a, _b, _c;
|
|
49
71
|
const beforeLoginConfirmation = this.adminforth.config.auth.beforeLoginConfirmation;
|
|
50
72
|
for (const hook of listify(beforeLoginConfirmation)) {
|
|
@@ -53,6 +75,7 @@ export default class AdminForthRestAPI {
|
|
|
53
75
|
response,
|
|
54
76
|
adminforth: this.adminforth,
|
|
55
77
|
extra,
|
|
78
|
+
rememberMeDays
|
|
56
79
|
});
|
|
57
80
|
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
81
|
// delete all items from toReturn and add these:
|
|
@@ -109,9 +132,11 @@ export default class AdminForthRestAPI {
|
|
|
109
132
|
pk: userRecord[userResource.columns.find((col) => col.primaryKey).name],
|
|
110
133
|
username,
|
|
111
134
|
};
|
|
112
|
-
|
|
135
|
+
const expireInDays = rememberMe ? this.adminforth.config.auth.rememberMeDays || 30 : 1;
|
|
136
|
+
await this.processLoginCallbacks(adminUser, toReturn, response, {
|
|
137
|
+
body, headers, query, cookies, requestUrl,
|
|
138
|
+
}, expireInDays);
|
|
113
139
|
if (toReturn.allowedLogin) {
|
|
114
|
-
const expireInDays = rememberMe && this.adminforth.config.auth.rememberMeDays;
|
|
115
140
|
this.adminforth.auth.setAuthCookie({
|
|
116
141
|
expireInDays,
|
|
117
142
|
response,
|
|
@@ -142,6 +167,17 @@ export default class AdminForthRestAPI {
|
|
|
142
167
|
return { ok: true };
|
|
143
168
|
},
|
|
144
169
|
});
|
|
170
|
+
server.endpoint({
|
|
171
|
+
noAuth: true,
|
|
172
|
+
method: 'GET',
|
|
173
|
+
path: '/get_login_form_config',
|
|
174
|
+
handler: async ({ tr }) => {
|
|
175
|
+
const loginPromptHTML = await getLoginPromptHTML(this.adminforth.config.auth.loginPromptHTML);
|
|
176
|
+
return {
|
|
177
|
+
loginPromptHTML: await tr(loginPromptHTML, 'system.loginPromptHTML'),
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
});
|
|
145
181
|
server.endpoint({
|
|
146
182
|
noAuth: true,
|
|
147
183
|
method: 'GET',
|
|
@@ -161,21 +197,24 @@ export default class AdminForthRestAPI {
|
|
|
161
197
|
usernameFieldName: usernameColumn.label,
|
|
162
198
|
loginBackgroundImage: this.adminforth.config.auth.loginBackgroundImage,
|
|
163
199
|
loginBackgroundPosition: this.adminforth.config.auth.loginBackgroundPosition,
|
|
200
|
+
removeBackgroundBlendMode: this.adminforth.config.auth.removeBackgroundBlendMode,
|
|
164
201
|
title: (_a = this.adminforth.config.customization) === null || _a === void 0 ? void 0 : _a.title,
|
|
165
202
|
demoCredentials: this.adminforth.config.auth.demoCredentials,
|
|
166
|
-
loginPromptHTML: await tr(this.adminforth.config.auth.loginPromptHTML, 'system.loginPromptHTML'),
|
|
167
203
|
loginPageInjections: this.adminforth.config.customization.loginPageInjections,
|
|
168
204
|
globalInjections: {
|
|
169
205
|
everyPageBottom: this.adminforth.config.customization.globalInjections.everyPageBottom,
|
|
206
|
+
sidebarTop: this.adminforth.config.customization.globalInjections.sidebarTop,
|
|
170
207
|
},
|
|
171
208
|
rememberMeDays: this.adminforth.config.auth.rememberMeDays,
|
|
209
|
+
singleTheme: this.adminforth.config.customization.singleTheme,
|
|
210
|
+
customHeadItems: this.adminforth.config.customization.customHeadItems,
|
|
172
211
|
};
|
|
173
212
|
},
|
|
174
213
|
});
|
|
175
214
|
server.endpoint({
|
|
176
215
|
method: 'GET',
|
|
177
216
|
path: '/get_base_config',
|
|
178
|
-
handler: async ({ input, adminUser, cookies, tr }) => {
|
|
217
|
+
handler: async ({ input, adminUser, cookies, tr, response }) => {
|
|
179
218
|
var _a, _b, _c;
|
|
180
219
|
let username = '';
|
|
181
220
|
let userFullName = '';
|
|
@@ -183,6 +222,10 @@ export default class AdminForthRestAPI {
|
|
|
183
222
|
if (!this.adminforth.config.auth) {
|
|
184
223
|
throw new Error('No config.auth defined');
|
|
185
224
|
}
|
|
225
|
+
response.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate');
|
|
226
|
+
response.setHeader('Pragma', 'no-cache');
|
|
227
|
+
response.setHeader('Expires', '0');
|
|
228
|
+
response.setHeader('Surrogate-Control', 'no-store');
|
|
186
229
|
const dbUser = adminUser.dbUser;
|
|
187
230
|
username = dbUser[this.adminforth.config.auth.usernameField];
|
|
188
231
|
userFullName = dbUser[this.adminforth.config.auth.userFullNameField];
|
|
@@ -228,20 +271,31 @@ export default class AdminForthRestAPI {
|
|
|
228
271
|
newMenu.push(newMenuItem);
|
|
229
272
|
}
|
|
230
273
|
const announcementBadge = (_b = (_a = this.adminforth.config.customization).announcementBadge) === null || _b === void 0 ? void 0 : _b.call(_a, adminUser);
|
|
274
|
+
const settingPages = [];
|
|
275
|
+
for (const settingPage of this.adminforth.config.auth.userMenuSettingsPages || []) {
|
|
276
|
+
if (settingPage.isVisible) {
|
|
277
|
+
const isVisible = await settingPage.isVisible(adminUser);
|
|
278
|
+
settingPages.push(Object.assign(Object.assign({}, settingPage), { isVisible }));
|
|
279
|
+
}
|
|
280
|
+
}
|
|
231
281
|
const publicPart = {
|
|
232
282
|
brandName: this.adminforth.config.customization.brandName,
|
|
233
283
|
usernameFieldName: usernameColumn.label,
|
|
234
284
|
loginBackgroundImage: this.adminforth.config.auth.loginBackgroundImage,
|
|
235
285
|
loginBackgroundPosition: this.adminforth.config.auth.loginBackgroundPosition,
|
|
286
|
+
removeBackgroundBlendMode: this.adminforth.config.auth.removeBackgroundBlendMode,
|
|
236
287
|
title: (_c = this.adminforth.config.customization) === null || _c === void 0 ? void 0 : _c.title,
|
|
237
288
|
demoCredentials: this.adminforth.config.auth.demoCredentials,
|
|
238
|
-
loginPromptHTML: await tr(this.adminforth.config.auth.loginPromptHTML, 'system.loginPromptHTML'),
|
|
239
289
|
loginPageInjections: this.adminforth.config.customization.loginPageInjections,
|
|
240
290
|
rememberMeDays: this.adminforth.config.auth.rememberMeDays,
|
|
291
|
+
singleTheme: this.adminforth.config.customization.singleTheme,
|
|
292
|
+
customHeadItems: this.adminforth.config.customization.customHeadItems,
|
|
241
293
|
};
|
|
242
294
|
const loggedInPart = {
|
|
243
295
|
showBrandNameInSidebar: this.adminforth.config.customization.showBrandNameInSidebar,
|
|
296
|
+
showBrandLogoInSidebar: this.adminforth.config.customization.showBrandLogoInSidebar,
|
|
244
297
|
brandLogo: this.adminforth.config.customization.brandLogo,
|
|
298
|
+
iconOnlySidebar: this.adminforth.config.customization.iconOnlySidebar,
|
|
245
299
|
datesFormat: this.adminforth.config.customization.datesFormat,
|
|
246
300
|
timeFormat: this.adminforth.config.customization.timeFormat,
|
|
247
301
|
auth: this.adminforth.config.auth,
|
|
@@ -251,6 +305,7 @@ export default class AdminForthRestAPI {
|
|
|
251
305
|
announcementBadge,
|
|
252
306
|
globalInjections: this.adminforth.config.customization.globalInjections,
|
|
253
307
|
userFullnameField: this.adminforth.config.auth.userFullNameField,
|
|
308
|
+
settingPages: settingPages,
|
|
254
309
|
};
|
|
255
310
|
// translate menu labels
|
|
256
311
|
const translateRoutines = [];
|
|
@@ -268,19 +323,37 @@ export default class AdminForthRestAPI {
|
|
|
268
323
|
if (menuItem.children) {
|
|
269
324
|
menuItem.children.forEach(processItem);
|
|
270
325
|
}
|
|
326
|
+
if (menuItem.pageLabel) {
|
|
327
|
+
translateRoutines.push((async () => {
|
|
328
|
+
menuItem.pageLabel = await tr(menuItem.pageLabel, `UserMenu.${menuItem.pageLabel}`);
|
|
329
|
+
})());
|
|
330
|
+
}
|
|
271
331
|
};
|
|
272
332
|
newMenu.forEach((menuItem) => {
|
|
273
333
|
processItem(menuItem);
|
|
274
334
|
});
|
|
335
|
+
if (this.adminforth.config.auth.userMenuSettingsPages) {
|
|
336
|
+
this.adminforth.config.auth.userMenuSettingsPages.forEach((page) => {
|
|
337
|
+
processItem(page);
|
|
338
|
+
});
|
|
339
|
+
}
|
|
275
340
|
await Promise.all(translateRoutines);
|
|
276
341
|
// strip all backendOnly fields or not described in adminForth fields from dbUser
|
|
277
342
|
// (when user defines column and does not set backendOnly, we assume it is not backendOnly)
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
343
|
+
const ctx = {
|
|
344
|
+
adminUser,
|
|
345
|
+
resource: userResource,
|
|
346
|
+
meta: {},
|
|
347
|
+
source: ActionCheckSource.ShowRequest,
|
|
348
|
+
adminforth: this.adminforth,
|
|
349
|
+
};
|
|
350
|
+
for (const key of Object.keys(adminUser.dbUser)) {
|
|
351
|
+
const col = userResource.columns.find((c) => c.name === key);
|
|
352
|
+
const bo = col ? await isBackendOnly(col, ctx) : true;
|
|
353
|
+
if (!col || bo) {
|
|
281
354
|
delete adminUser.dbUser[key];
|
|
282
355
|
}
|
|
283
|
-
}
|
|
356
|
+
}
|
|
284
357
|
return {
|
|
285
358
|
user: userData,
|
|
286
359
|
resources: this.adminforth.config.resources.map((res) => ({
|
|
@@ -435,7 +508,7 @@ export default class AdminForthRestAPI {
|
|
|
435
508
|
method: 'POST',
|
|
436
509
|
path: '/get_resource_data',
|
|
437
510
|
handler: async ({ body, adminUser, headers, query, cookies, requestUrl }) => {
|
|
438
|
-
var _a, _b, _c, _d, _e;
|
|
511
|
+
var _a, _b, _c, _d, _e, _f;
|
|
439
512
|
const { resourceId, source } = body;
|
|
440
513
|
if (['show', 'list', 'edit'].includes(source) === false) {
|
|
441
514
|
return { error: 'Invalid source, should be list or show' };
|
|
@@ -632,22 +705,38 @@ export default class AdminForthRestAPI {
|
|
|
632
705
|
}
|
|
633
706
|
});
|
|
634
707
|
}));
|
|
708
|
+
const pkField = (_d = resource.columns.find((col) => col.primaryKey)) === null || _d === void 0 ? void 0 : _d.name;
|
|
635
709
|
// remove all columns which are not defined in resources, or defined but backendOnly
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
710
|
+
{
|
|
711
|
+
const ctx = {
|
|
712
|
+
adminUser,
|
|
713
|
+
resource,
|
|
714
|
+
meta,
|
|
715
|
+
source: {
|
|
716
|
+
show: ActionCheckSource.ShowRequest,
|
|
717
|
+
list: ActionCheckSource.ListRequest,
|
|
718
|
+
edit: ActionCheckSource.EditLoadRequest,
|
|
719
|
+
}[source],
|
|
720
|
+
adminforth: this.adminforth,
|
|
721
|
+
};
|
|
722
|
+
for (const item of data.data) {
|
|
723
|
+
for (const key of Object.keys(item)) {
|
|
724
|
+
const col = resource.columns.find((c) => c.name === key);
|
|
725
|
+
const bo = col ? await isBackendOnly(col, ctx) : true;
|
|
726
|
+
if (!col || bo) {
|
|
727
|
+
delete item[key];
|
|
728
|
+
}
|
|
640
729
|
}
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
}
|
|
730
|
+
item._label = resource.recordLabel(item);
|
|
731
|
+
}
|
|
732
|
+
}
|
|
644
733
|
if (source === 'list' && resource.options.listTableClickUrl) {
|
|
645
734
|
await Promise.all(data.data.map(async (item) => {
|
|
646
735
|
item._clickUrl = await resource.options.listTableClickUrl(item, adminUser);
|
|
647
736
|
}));
|
|
648
737
|
}
|
|
649
738
|
// only after adminforth made all post processing, give user ability to edit it
|
|
650
|
-
for (const hook of listify((
|
|
739
|
+
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
740
|
const resp = await hook({
|
|
652
741
|
resource,
|
|
653
742
|
query: body,
|
|
@@ -672,7 +761,7 @@ export default class AdminForthRestAPI {
|
|
|
672
761
|
method: 'POST',
|
|
673
762
|
path: '/get_resource_foreign_data',
|
|
674
763
|
handler: async ({ body, adminUser, headers, query, cookies, requestUrl }) => {
|
|
675
|
-
const { resourceId, column } = body;
|
|
764
|
+
const { resourceId, column, search } = body;
|
|
676
765
|
if (!this.adminforth.statuses.dbDiscover) {
|
|
677
766
|
return { error: 'Database discovery not started' };
|
|
678
767
|
}
|
|
@@ -742,6 +831,43 @@ export default class AdminForthRestAPI {
|
|
|
742
831
|
throw new Error(`Wrong filter object value: ${JSON.stringify(filters)}`);
|
|
743
832
|
}
|
|
744
833
|
}
|
|
834
|
+
if (search && search.trim() && columnConfig.foreignResource.searchableFields) {
|
|
835
|
+
const searchableFields = Array.isArray(columnConfig.foreignResource.searchableFields)
|
|
836
|
+
? columnConfig.foreignResource.searchableFields
|
|
837
|
+
: [columnConfig.foreignResource.searchableFields];
|
|
838
|
+
const searchOperator = columnConfig.foreignResource.searchIsCaseSensitive
|
|
839
|
+
? AdminForthFilterOperators.LIKE
|
|
840
|
+
: AdminForthFilterOperators.ILIKE;
|
|
841
|
+
const availableSearchFields = searchableFields.filter((fieldName) => {
|
|
842
|
+
const fieldExists = targetResource.columns.some(col => col.name === fieldName);
|
|
843
|
+
if (!fieldExists) {
|
|
844
|
+
process.env.HEAVY_DEBUG && console.log(`⚠️ Field '${fieldName}' not found in polymorphic target resource '${targetResource.resourceId}', skipping in search filter.`);
|
|
845
|
+
}
|
|
846
|
+
return fieldExists;
|
|
847
|
+
});
|
|
848
|
+
if (availableSearchFields.length === 0) {
|
|
849
|
+
process.env.HEAVY_DEBUG && console.log(`⚠️ No searchable fields available in polymorphic target resource '${targetResource.resourceId}', skipping resource.`);
|
|
850
|
+
resolve({ items: [] });
|
|
851
|
+
return;
|
|
852
|
+
}
|
|
853
|
+
const searchFilters = availableSearchFields.map((fieldName) => {
|
|
854
|
+
const filter = {
|
|
855
|
+
field: fieldName,
|
|
856
|
+
operator: searchOperator,
|
|
857
|
+
value: search.trim(),
|
|
858
|
+
};
|
|
859
|
+
return filter;
|
|
860
|
+
});
|
|
861
|
+
if (searchFilters.length > 1) {
|
|
862
|
+
normalizedFilters.subFilters.push({
|
|
863
|
+
operator: AdminForthFilterOperators.OR,
|
|
864
|
+
subFilters: searchFilters,
|
|
865
|
+
});
|
|
866
|
+
}
|
|
867
|
+
else if (searchFilters.length === 1) {
|
|
868
|
+
normalizedFilters.subFilters.push(searchFilters[0]);
|
|
869
|
+
}
|
|
870
|
+
}
|
|
745
871
|
const dbDataItems = await this.adminforth.connectors[targetResource.dataSource].getData({
|
|
746
872
|
resource: targetResource,
|
|
747
873
|
limit,
|
|
@@ -835,6 +961,7 @@ export default class AdminForthRestAPI {
|
|
|
835
961
|
return { error };
|
|
836
962
|
}
|
|
837
963
|
const { record } = body;
|
|
964
|
+
// todo if showIn.create is function, code below will be buggy (will not detect required fact)
|
|
838
965
|
for (const column of resource.columns) {
|
|
839
966
|
if (((_a = column.required) === null || _a === void 0 ? void 0 : _a.create) &&
|
|
840
967
|
record[column.name] === undefined &&
|
|
@@ -842,11 +969,36 @@ export default class AdminForthRestAPI {
|
|
|
842
969
|
return { error: `Column '${column.name}' is required`, ok: false };
|
|
843
970
|
}
|
|
844
971
|
}
|
|
972
|
+
const primaryKeyColumn = resource.columns.find((col) => col.primaryKey);
|
|
973
|
+
if (record[primaryKeyColumn.name] !== undefined) {
|
|
974
|
+
const existingRecord = await this.adminforth.resource(resource.resourceId).get([Filters.EQ(primaryKeyColumn.name, record[primaryKeyColumn.name])]);
|
|
975
|
+
if (existingRecord) {
|
|
976
|
+
return { error: `Record with ${primaryKeyColumn.name} '${record[primaryKeyColumn.name]}' already exists`, ok: false };
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
const ctxCreate = {
|
|
980
|
+
adminUser,
|
|
981
|
+
resource,
|
|
982
|
+
meta: { requestBody: body },
|
|
983
|
+
source: ActionCheckSource.CreateRequest,
|
|
984
|
+
adminforth: this.adminforth,
|
|
985
|
+
};
|
|
986
|
+
for (const column of resource.columns) {
|
|
987
|
+
if ((_b = column.required) === null || _b === void 0 ? void 0 : _b.create) {
|
|
988
|
+
const shown = await isShown(column, 'create', ctxCreate);
|
|
989
|
+
if (shown && record[column.name] === undefined) {
|
|
990
|
+
return { error: `Column '${column.name}' is required`, ok: false };
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
}
|
|
845
994
|
for (const column of resource.columns) {
|
|
846
995
|
const fieldName = column.name;
|
|
847
996
|
if (fieldName in record) {
|
|
848
|
-
|
|
849
|
-
|
|
997
|
+
const shown = await isShown(column, 'create', ctxCreate); //
|
|
998
|
+
const bo = await isBackendOnly(column, ctxCreate);
|
|
999
|
+
const filledOnCreate = await isFilledOnCreate(column);
|
|
1000
|
+
if ((!shown && !filledOnCreate) || bo) {
|
|
1001
|
+
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
1002
|
}
|
|
851
1003
|
}
|
|
852
1004
|
}
|
|
@@ -890,7 +1042,7 @@ export default class AdminForthRestAPI {
|
|
|
890
1042
|
}
|
|
891
1043
|
const response = await this.adminforth.createResourceRecord({ resource, record, adminUser, extra: { body, query, headers, cookies, requestUrl } });
|
|
892
1044
|
if (response.error) {
|
|
893
|
-
return { error: response.error, ok: false };
|
|
1045
|
+
return { error: response.error, ok: false, newRecordId: response.newRecordId };
|
|
894
1046
|
}
|
|
895
1047
|
const connector = this.adminforth.connectors[resource.dataSource];
|
|
896
1048
|
return {
|
|
@@ -903,7 +1055,7 @@ export default class AdminForthRestAPI {
|
|
|
903
1055
|
method: 'POST',
|
|
904
1056
|
path: '/update_record',
|
|
905
1057
|
handler: async ({ body, adminUser, query, headers, cookies, requestUrl }) => {
|
|
906
|
-
var _a, _b
|
|
1058
|
+
var _a, _b;
|
|
907
1059
|
const resource = this.adminforth.config.resources.find((res) => res.resourceId == body['resourceId']);
|
|
908
1060
|
if (!resource) {
|
|
909
1061
|
return { error: `Resource '${body['resourceId']}' not found` };
|
|
@@ -921,21 +1073,37 @@ export default class AdminForthRestAPI {
|
|
|
921
1073
|
if (!allowed) {
|
|
922
1074
|
return { error: allowedError };
|
|
923
1075
|
}
|
|
1076
|
+
const primaryKeyColumn = resource.columns.find((col) => col.primaryKey);
|
|
1077
|
+
if (record[primaryKeyColumn.name] !== undefined) {
|
|
1078
|
+
const existingRecord = await this.adminforth.resource(resource.resourceId).get([Filters.EQ(primaryKeyColumn.name, record[primaryKeyColumn.name])]);
|
|
1079
|
+
if (existingRecord) {
|
|
1080
|
+
return { error: `Record with ${primaryKeyColumn.name} '${record[primaryKeyColumn.name]}' already exists`, ok: false };
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
const ctxEdit = {
|
|
1084
|
+
adminUser,
|
|
1085
|
+
resource,
|
|
1086
|
+
meta: { requestBody: body, newRecord: record, oldRecord, pk: recordId },
|
|
1087
|
+
source: ActionCheckSource.EditRequest,
|
|
1088
|
+
adminforth: this.adminforth,
|
|
1089
|
+
};
|
|
924
1090
|
for (const column of resource.columns) {
|
|
925
1091
|
const fieldName = column.name;
|
|
926
1092
|
if (fieldName in record) {
|
|
927
|
-
|
|
928
|
-
|
|
1093
|
+
const shown = await isShown(column, 'edit', ctxEdit);
|
|
1094
|
+
const bo = await isBackendOnly(column, ctxEdit);
|
|
1095
|
+
if (!shown || column.editReadonly || bo) {
|
|
1096
|
+
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
1097
|
}
|
|
930
1098
|
}
|
|
931
1099
|
}
|
|
932
1100
|
// for polymorphic foreign resources, we need to find out the value for polymorphicOn column
|
|
933
1101
|
for (const column of resource.columns) {
|
|
934
|
-
if (((
|
|
1102
|
+
if (((_a = column.foreignResource) === null || _a === void 0 ? void 0 : _a.polymorphicOn) && record[column.name] === null) {
|
|
935
1103
|
const systemResource = column.foreignResource.polymorphicResources.find(pr => pr.resourceId === null);
|
|
936
1104
|
record[column.foreignResource.polymorphicOn] = systemResource.whenValue;
|
|
937
1105
|
}
|
|
938
|
-
else if (((
|
|
1106
|
+
else if (((_b = column.foreignResource) === null || _b === void 0 ? void 0 : _b.polymorphicOn) && record[column.name]) {
|
|
939
1107
|
let newPolymorphicOnValue = null;
|
|
940
1108
|
if (record[column.name]) {
|
|
941
1109
|
const targetResources = {};
|
|
@@ -1047,7 +1215,7 @@ export default class AdminForthRestAPI {
|
|
|
1047
1215
|
method: 'POST',
|
|
1048
1216
|
path: '/start_custom_action',
|
|
1049
1217
|
handler: async ({ body, adminUser, tr }) => {
|
|
1050
|
-
const { resourceId, actionId, recordId } = body;
|
|
1218
|
+
const { resourceId, actionId, recordId, extra } = body;
|
|
1051
1219
|
const resource = this.adminforth.config.resources.find((res) => res.resourceId == resourceId);
|
|
1052
1220
|
if (!resource) {
|
|
1053
1221
|
return { error: await tr(`Resource {resourceId} not found`, 'errors', { resourceId }) };
|
|
@@ -1071,7 +1239,7 @@ export default class AdminForthRestAPI {
|
|
|
1071
1239
|
redirectUrl: action.url
|
|
1072
1240
|
};
|
|
1073
1241
|
}
|
|
1074
|
-
const response = await action.action({ recordId, adminUser, resource, tr, adminforth: this.adminforth });
|
|
1242
|
+
const response = await action.action({ recordId, adminUser, resource, tr, adminforth: this.adminforth, extra });
|
|
1075
1243
|
return Object.assign({ actionId,
|
|
1076
1244
|
recordId,
|
|
1077
1245
|
resourceId }, response);
|