adminforth 2.4.0-next.32 → 2.4.0-next.321

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.
Files changed (177) hide show
  1. package/commands/callTsProxy.js +14 -4
  2. package/commands/createApp/templates/api.ts.hbs +10 -0
  3. package/commands/createApp/templates/custom/tsconfig.json.hbs +2 -3
  4. package/commands/createApp/templates/index.ts.hbs +12 -1
  5. package/commands/createApp/templates/package.json.hbs +1 -1
  6. package/commands/createApp/templates/prisma.config.ts.hbs +8 -0
  7. package/commands/createApp/templates/schema.prisma.hbs +0 -1
  8. package/commands/createApp/utils.js +10 -0
  9. package/commands/createCustomComponent/configLoader.js +17 -4
  10. package/commands/createCustomComponent/main.js +13 -7
  11. package/commands/createCustomComponent/templates/customCrud/beforeActionButtons.vue.hbs +38 -0
  12. package/commands/createCustomComponent/templates/customCrud/saveButton.vue.hbs +28 -0
  13. package/commands/createPlugin/templates/custom/tsconfig.json.hbs +2 -5
  14. package/commands/createPlugin/templates/package.json.hbs +1 -1
  15. package/commands/generateModels.js +30 -22
  16. package/dist/auth.d.ts +9 -1
  17. package/dist/auth.d.ts.map +1 -1
  18. package/dist/auth.js +21 -2
  19. package/dist/auth.js.map +1 -1
  20. package/dist/dataConnectors/baseConnector.d.ts +1 -1
  21. package/dist/dataConnectors/baseConnector.d.ts.map +1 -1
  22. package/dist/dataConnectors/baseConnector.js +70 -18
  23. package/dist/dataConnectors/baseConnector.js.map +1 -1
  24. package/dist/dataConnectors/clickhouse.d.ts.map +1 -1
  25. package/dist/dataConnectors/clickhouse.js +15 -0
  26. package/dist/dataConnectors/clickhouse.js.map +1 -1
  27. package/dist/dataConnectors/mongo.d.ts.map +1 -1
  28. package/dist/dataConnectors/mongo.js +50 -15
  29. package/dist/dataConnectors/mongo.js.map +1 -1
  30. package/dist/dataConnectors/mysql.d.ts.map +1 -1
  31. package/dist/dataConnectors/mysql.js +11 -0
  32. package/dist/dataConnectors/mysql.js.map +1 -1
  33. package/dist/dataConnectors/postgres.d.ts.map +1 -1
  34. package/dist/dataConnectors/postgres.js +43 -14
  35. package/dist/dataConnectors/postgres.js.map +1 -1
  36. package/dist/dataConnectors/sqlite.d.ts.map +1 -1
  37. package/dist/dataConnectors/sqlite.js +11 -0
  38. package/dist/dataConnectors/sqlite.js.map +1 -1
  39. package/dist/index.d.ts +11 -1
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +44 -21
  42. package/dist/index.js.map +1 -1
  43. package/dist/modules/codeInjector.d.ts +2 -0
  44. package/dist/modules/codeInjector.d.ts.map +1 -1
  45. package/dist/modules/codeInjector.js +62 -6
  46. package/dist/modules/codeInjector.js.map +1 -1
  47. package/dist/modules/configValidator.d.ts +6 -0
  48. package/dist/modules/configValidator.d.ts.map +1 -1
  49. package/dist/modules/configValidator.js +202 -25
  50. package/dist/modules/configValidator.js.map +1 -1
  51. package/dist/modules/restApi.d.ts +1 -1
  52. package/dist/modules/restApi.d.ts.map +1 -1
  53. package/dist/modules/restApi.js +184 -31
  54. package/dist/modules/restApi.js.map +1 -1
  55. package/dist/modules/styles.d.ts +499 -13
  56. package/dist/modules/styles.d.ts.map +1 -1
  57. package/dist/modules/styles.js +555 -31
  58. package/dist/modules/styles.js.map +1 -1
  59. package/dist/modules/utils.d.ts +7 -15
  60. package/dist/modules/utils.d.ts.map +1 -1
  61. package/dist/modules/utils.js +45 -68
  62. package/dist/modules/utils.js.map +1 -1
  63. package/dist/servers/express.d.ts +5 -0
  64. package/dist/servers/express.d.ts.map +1 -1
  65. package/dist/servers/express.js +40 -1
  66. package/dist/servers/express.js.map +1 -1
  67. package/dist/spa/index.html +1 -1
  68. package/dist/spa/package-lock.json +1208 -708
  69. package/dist/spa/package.json +34 -34
  70. package/dist/spa/src/App.vue +61 -173
  71. package/dist/spa/src/adminforth.ts +42 -18
  72. package/dist/spa/src/afcl/AreaChart.vue +0 -1
  73. package/dist/spa/src/afcl/BarChart.vue +2 -2
  74. package/dist/spa/src/afcl/Button.vue +6 -6
  75. package/dist/spa/src/afcl/ButtonGroup.vue +91 -0
  76. package/dist/spa/src/afcl/Card.vue +25 -0
  77. package/dist/spa/src/afcl/Checkbox.vue +21 -13
  78. package/dist/spa/src/afcl/CountryFlag.vue +4 -1
  79. package/dist/spa/src/{components/CustomDatePicker.vue → afcl/DatePicker.vue} +95 -9
  80. package/dist/spa/src/afcl/Dialog.vue +47 -27
  81. package/dist/spa/src/afcl/Dropzone.vue +145 -48
  82. package/dist/spa/src/afcl/Input.vue +14 -6
  83. package/dist/spa/src/afcl/JsonViewer.vue +25 -0
  84. package/dist/spa/src/afcl/LinkButton.vue +3 -3
  85. package/dist/spa/src/afcl/PieChart.vue +5 -5
  86. package/dist/spa/src/afcl/ProgressBar.vue +7 -7
  87. package/dist/spa/src/afcl/Select.vue +82 -34
  88. package/dist/spa/src/afcl/Skeleton.vue +6 -6
  89. package/dist/spa/src/afcl/Table.vue +313 -75
  90. package/dist/spa/src/afcl/Textarea.vue +31 -0
  91. package/dist/spa/src/afcl/Toggle.vue +32 -0
  92. package/dist/spa/src/afcl/Tooltip.vue +28 -18
  93. package/dist/spa/src/afcl/VerticalTabs.vue +21 -7
  94. package/dist/spa/src/afcl/index.ts +6 -3
  95. package/dist/spa/src/components/AcceptModal.vue +48 -14
  96. package/dist/spa/src/components/Breadcrumbs.vue +5 -5
  97. package/dist/spa/src/components/CallActionWrapper.vue +15 -0
  98. package/dist/spa/src/components/ColumnValueInput.vue +38 -18
  99. package/dist/spa/src/components/ColumnValueInputWrapper.vue +4 -3
  100. package/dist/spa/src/components/CustomDateRangePicker.vue +9 -8
  101. package/dist/spa/src/components/CustomRangePicker.vue +37 -21
  102. package/dist/spa/src/components/ErrorMessage.vue +21 -0
  103. package/dist/spa/src/components/Filters.vue +195 -132
  104. package/dist/spa/src/components/GroupsTable.vue +9 -8
  105. package/dist/spa/src/components/MenuLink.vue +95 -23
  106. package/dist/spa/src/components/ResourceForm.vue +94 -51
  107. package/dist/spa/src/components/ResourceListTable.vue +119 -94
  108. package/dist/spa/src/components/ResourceListTableVirtual.vue +118 -87
  109. package/dist/spa/src/components/ShowTable.vue +21 -15
  110. package/dist/spa/src/components/Sidebar.vue +472 -0
  111. package/dist/spa/src/components/SingleSkeletLoader.vue +6 -6
  112. package/dist/spa/src/components/SkeleteLoader.vue +3 -3
  113. package/dist/spa/src/components/ThreeDotsMenu.vue +84 -15
  114. package/dist/spa/src/components/Toast.vue +40 -29
  115. package/dist/spa/src/components/UserMenuSettingsButton.vue +69 -0
  116. package/dist/spa/src/components/ValueRenderer.vue +44 -17
  117. package/dist/spa/src/controls/BoolToggle.vue +34 -0
  118. package/dist/spa/src/i18n.ts +5 -3
  119. package/dist/spa/src/main.ts +1 -1
  120. package/dist/spa/src/renderers/CompactField.vue +1 -1
  121. package/dist/spa/src/renderers/CompactUUID.vue +1 -1
  122. package/dist/spa/src/router/index.ts +8 -0
  123. package/dist/spa/src/shims-vue.d.ts +5 -0
  124. package/dist/spa/src/spa_types/core.ts +13 -1
  125. package/dist/spa/src/stores/core.ts +13 -1
  126. package/dist/spa/src/stores/filters.ts +33 -2
  127. package/dist/spa/src/stores/modal.ts +6 -1
  128. package/dist/spa/src/stores/toast.ts +22 -3
  129. package/dist/spa/src/types/Back.ts +168 -23
  130. package/dist/spa/src/types/Common.ts +93 -32
  131. package/dist/spa/src/types/FrontendAPI.ts +31 -5
  132. package/dist/spa/src/types/adapters/CaptchaAdapter.ts +34 -0
  133. package/dist/spa/src/types/adapters/EmailAdapter.ts +2 -4
  134. package/dist/spa/src/types/adapters/ImageVisionAdapter.ts +30 -0
  135. package/dist/spa/src/types/adapters/KeyValueAdapter.ts +16 -0
  136. package/dist/spa/src/types/adapters/StorageAdapter.ts +4 -2
  137. package/dist/spa/src/types/adapters/index.ts +3 -0
  138. package/dist/spa/src/utils.ts +291 -11
  139. package/dist/spa/src/views/CreateView.vue +63 -21
  140. package/dist/spa/src/views/EditView.vue +55 -22
  141. package/dist/spa/src/views/ListView.vue +144 -87
  142. package/dist/spa/src/views/LoginView.vue +26 -35
  143. package/dist/spa/src/views/ResourceParent.vue +2 -2
  144. package/dist/spa/src/views/SettingsView.vue +121 -0
  145. package/dist/spa/src/views/ShowView.vue +83 -53
  146. package/dist/spa/src/websocket.ts +6 -1
  147. package/dist/spa/tsconfig.app.json +1 -1
  148. package/dist/spa/vite.config.ts +45 -2
  149. package/dist/types/Back.d.ts +151 -14
  150. package/dist/types/Back.d.ts.map +1 -1
  151. package/dist/types/Back.js +15 -0
  152. package/dist/types/Back.js.map +1 -1
  153. package/dist/types/Common.d.ts +108 -29
  154. package/dist/types/Common.d.ts.map +1 -1
  155. package/dist/types/Common.js.map +1 -1
  156. package/dist/types/FrontendAPI.d.ts +31 -3
  157. package/dist/types/FrontendAPI.d.ts.map +1 -1
  158. package/dist/types/FrontendAPI.js.map +1 -1
  159. package/dist/types/adapters/CaptchaAdapter.d.ts +30 -0
  160. package/dist/types/adapters/CaptchaAdapter.d.ts.map +1 -0
  161. package/dist/types/adapters/CaptchaAdapter.js +5 -0
  162. package/dist/types/adapters/CaptchaAdapter.js.map +1 -0
  163. package/dist/types/adapters/EmailAdapter.d.ts +2 -3
  164. package/dist/types/adapters/EmailAdapter.d.ts.map +1 -1
  165. package/dist/types/adapters/ImageVisionAdapter.d.ts +25 -0
  166. package/dist/types/adapters/ImageVisionAdapter.d.ts.map +1 -0
  167. package/dist/types/adapters/ImageVisionAdapter.js +2 -0
  168. package/dist/types/adapters/ImageVisionAdapter.js.map +1 -0
  169. package/dist/types/adapters/KeyValueAdapter.d.ts +10 -0
  170. package/dist/types/adapters/KeyValueAdapter.d.ts.map +1 -0
  171. package/dist/types/adapters/KeyValueAdapter.js +2 -0
  172. package/dist/types/adapters/KeyValueAdapter.js.map +1 -0
  173. package/dist/types/adapters/StorageAdapter.d.ts +2 -0
  174. package/dist/types/adapters/StorageAdapter.d.ts.map +1 -1
  175. package/dist/types/adapters/index.d.ts +3 -0
  176. package/dist/types/adapters/index.d.ts.map +1 -1
  177. package/package.json +4 -2
@@ -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
- await this.processLoginCallbacks(adminUser, toReturn, response, { body, headers, query, cookies, requestUrl });
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];
@@ -228,20 +270,31 @@ export default class AdminForthRestAPI {
228
270
  newMenu.push(newMenuItem);
229
271
  }
230
272
  const announcementBadge = (_b = (_a = this.adminforth.config.customization).announcementBadge) === null || _b === void 0 ? void 0 : _b.call(_a, adminUser);
273
+ const settingPages = [];
274
+ for (const settingPage of this.adminforth.config.auth.userMenuSettingsPages || []) {
275
+ if (settingPage.isVisible) {
276
+ const isVisible = await settingPage.isVisible(adminUser);
277
+ settingPages.push(Object.assign(Object.assign({}, settingPage), { isVisible }));
278
+ }
279
+ }
231
280
  const publicPart = {
232
281
  brandName: this.adminforth.config.customization.brandName,
233
282
  usernameFieldName: usernameColumn.label,
234
283
  loginBackgroundImage: this.adminforth.config.auth.loginBackgroundImage,
235
284
  loginBackgroundPosition: this.adminforth.config.auth.loginBackgroundPosition,
285
+ removeBackgroundBlendMode: this.adminforth.config.auth.removeBackgroundBlendMode,
236
286
  title: (_c = this.adminforth.config.customization) === null || _c === void 0 ? void 0 : _c.title,
237
287
  demoCredentials: this.adminforth.config.auth.demoCredentials,
238
- loginPromptHTML: await tr(this.adminforth.config.auth.loginPromptHTML, 'system.loginPromptHTML'),
239
288
  loginPageInjections: this.adminforth.config.customization.loginPageInjections,
240
289
  rememberMeDays: this.adminforth.config.auth.rememberMeDays,
290
+ singleTheme: this.adminforth.config.customization.singleTheme,
291
+ customHeadItems: this.adminforth.config.customization.customHeadItems,
241
292
  };
242
293
  const loggedInPart = {
243
294
  showBrandNameInSidebar: this.adminforth.config.customization.showBrandNameInSidebar,
295
+ showBrandLogoInSidebar: this.adminforth.config.customization.showBrandLogoInSidebar,
244
296
  brandLogo: this.adminforth.config.customization.brandLogo,
297
+ iconOnlySidebar: this.adminforth.config.customization.iconOnlySidebar,
245
298
  datesFormat: this.adminforth.config.customization.datesFormat,
246
299
  timeFormat: this.adminforth.config.customization.timeFormat,
247
300
  auth: this.adminforth.config.auth,
@@ -251,6 +304,7 @@ export default class AdminForthRestAPI {
251
304
  announcementBadge,
252
305
  globalInjections: this.adminforth.config.customization.globalInjections,
253
306
  userFullnameField: this.adminforth.config.auth.userFullNameField,
307
+ settingPages: settingPages,
254
308
  };
255
309
  // translate menu labels
256
310
  const translateRoutines = [];
@@ -268,19 +322,37 @@ export default class AdminForthRestAPI {
268
322
  if (menuItem.children) {
269
323
  menuItem.children.forEach(processItem);
270
324
  }
325
+ if (menuItem.pageLabel) {
326
+ translateRoutines.push((async () => {
327
+ menuItem.pageLabel = await tr(menuItem.pageLabel, `UserMenu.${menuItem.pageLabel}`);
328
+ })());
329
+ }
271
330
  };
272
331
  newMenu.forEach((menuItem) => {
273
332
  processItem(menuItem);
274
333
  });
334
+ if (this.adminforth.config.auth.userMenuSettingsPages) {
335
+ this.adminforth.config.auth.userMenuSettingsPages.forEach((page) => {
336
+ processItem(page);
337
+ });
338
+ }
275
339
  await Promise.all(translateRoutines);
276
340
  // strip all backendOnly fields or not described in adminForth fields from dbUser
277
341
  // (when user defines column and does not set backendOnly, we assume it is not backendOnly)
278
- Object.keys(adminUser.dbUser).forEach((key) => {
279
- const col = userResource.columns.find((col) => col.name === key);
280
- if (!col || col.backendOnly) {
342
+ const ctx = {
343
+ adminUser,
344
+ resource: userResource,
345
+ meta: {},
346
+ source: ActionCheckSource.ShowRequest,
347
+ adminforth: this.adminforth,
348
+ };
349
+ for (const key of Object.keys(adminUser.dbUser)) {
350
+ const col = userResource.columns.find((c) => c.name === key);
351
+ const bo = col ? await isBackendOnly(col, ctx) : true;
352
+ if (!col || bo) {
281
353
  delete adminUser.dbUser[key];
282
354
  }
283
- });
355
+ }
284
356
  return {
285
357
  user: userData,
286
358
  resources: this.adminforth.config.resources.map((res) => ({
@@ -435,7 +507,7 @@ export default class AdminForthRestAPI {
435
507
  method: 'POST',
436
508
  path: '/get_resource_data',
437
509
  handler: async ({ body, adminUser, headers, query, cookies, requestUrl }) => {
438
- var _a, _b, _c, _d, _e;
510
+ var _a, _b, _c, _d, _e, _f;
439
511
  const { resourceId, source } = body;
440
512
  if (['show', 'list', 'edit'].includes(source) === false) {
441
513
  return { error: 'Invalid source, should be list or show' };
@@ -632,22 +704,38 @@ export default class AdminForthRestAPI {
632
704
  }
633
705
  });
634
706
  }));
707
+ const pkField = (_d = resource.columns.find((col) => col.primaryKey)) === null || _d === void 0 ? void 0 : _d.name;
635
708
  // remove all columns which are not defined in resources, or defined but backendOnly
636
- data.data.forEach((item) => {
637
- Object.keys(item).forEach((key) => {
638
- if (!resource.columns.find((col) => col.name === key) || resource.columns.find((col) => col.name === key && col.backendOnly)) {
639
- delete item[key];
709
+ {
710
+ const ctx = {
711
+ adminUser,
712
+ resource,
713
+ meta,
714
+ source: {
715
+ show: ActionCheckSource.ShowRequest,
716
+ list: ActionCheckSource.ListRequest,
717
+ edit: ActionCheckSource.EditLoadRequest,
718
+ }[source],
719
+ adminforth: this.adminforth,
720
+ };
721
+ for (const item of data.data) {
722
+ for (const key of Object.keys(item)) {
723
+ const col = resource.columns.find((c) => c.name === key);
724
+ const bo = col ? await isBackendOnly(col, ctx) : true;
725
+ if (!col || bo) {
726
+ delete item[key];
727
+ }
640
728
  }
641
- });
642
- item._label = resource.recordLabel(item);
643
- });
729
+ item._label = resource.recordLabel(item);
730
+ }
731
+ }
644
732
  if (source === 'list' && resource.options.listTableClickUrl) {
645
733
  await Promise.all(data.data.map(async (item) => {
646
734
  item._clickUrl = await resource.options.listTableClickUrl(item, adminUser);
647
735
  }));
648
736
  }
649
737
  // only after adminforth made all post processing, give user ability to edit it
650
- for (const hook of listify((_e = (_d = resource.hooks) === null || _d === void 0 ? void 0 : _d[hookSource]) === null || _e === void 0 ? void 0 : _e.afterDatasourceResponse)) {
738
+ 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
739
  const resp = await hook({
652
740
  resource,
653
741
  query: body,
@@ -672,7 +760,7 @@ export default class AdminForthRestAPI {
672
760
  method: 'POST',
673
761
  path: '/get_resource_foreign_data',
674
762
  handler: async ({ body, adminUser, headers, query, cookies, requestUrl }) => {
675
- const { resourceId, column } = body;
763
+ const { resourceId, column, search } = body;
676
764
  if (!this.adminforth.statuses.dbDiscover) {
677
765
  return { error: 'Database discovery not started' };
678
766
  }
@@ -742,6 +830,43 @@ export default class AdminForthRestAPI {
742
830
  throw new Error(`Wrong filter object value: ${JSON.stringify(filters)}`);
743
831
  }
744
832
  }
833
+ if (search && search.trim() && columnConfig.foreignResource.searchableFields) {
834
+ const searchableFields = Array.isArray(columnConfig.foreignResource.searchableFields)
835
+ ? columnConfig.foreignResource.searchableFields
836
+ : [columnConfig.foreignResource.searchableFields];
837
+ const searchOperator = columnConfig.foreignResource.searchIsCaseSensitive
838
+ ? AdminForthFilterOperators.LIKE
839
+ : AdminForthFilterOperators.ILIKE;
840
+ const availableSearchFields = searchableFields.filter((fieldName) => {
841
+ const fieldExists = targetResource.columns.some(col => col.name === fieldName);
842
+ if (!fieldExists) {
843
+ process.env.HEAVY_DEBUG && console.log(`⚠️ Field '${fieldName}' not found in polymorphic target resource '${targetResource.resourceId}', skipping in search filter.`);
844
+ }
845
+ return fieldExists;
846
+ });
847
+ if (availableSearchFields.length === 0) {
848
+ process.env.HEAVY_DEBUG && console.log(`⚠️ No searchable fields available in polymorphic target resource '${targetResource.resourceId}', skipping resource.`);
849
+ resolve({ items: [] });
850
+ return;
851
+ }
852
+ const searchFilters = availableSearchFields.map((fieldName) => {
853
+ const filter = {
854
+ field: fieldName,
855
+ operator: searchOperator,
856
+ value: search.trim(),
857
+ };
858
+ return filter;
859
+ });
860
+ if (searchFilters.length > 1) {
861
+ normalizedFilters.subFilters.push({
862
+ operator: AdminForthFilterOperators.OR,
863
+ subFilters: searchFilters,
864
+ });
865
+ }
866
+ else if (searchFilters.length === 1) {
867
+ normalizedFilters.subFilters.push(searchFilters[0]);
868
+ }
869
+ }
745
870
  const dbDataItems = await this.adminforth.connectors[targetResource.dataSource].getData({
746
871
  resource: targetResource,
747
872
  limit,
@@ -835,6 +960,7 @@ export default class AdminForthRestAPI {
835
960
  return { error };
836
961
  }
837
962
  const { record } = body;
963
+ // todo if showIn.create is function, code below will be buggy (will not detect required fact)
838
964
  for (const column of resource.columns) {
839
965
  if (((_a = column.required) === null || _a === void 0 ? void 0 : _a.create) &&
840
966
  record[column.name] === undefined &&
@@ -842,11 +968,29 @@ export default class AdminForthRestAPI {
842
968
  return { error: `Column '${column.name}' is required`, ok: false };
843
969
  }
844
970
  }
971
+ const ctxCreate = {
972
+ adminUser,
973
+ resource,
974
+ meta: { requestBody: body },
975
+ source: ActionCheckSource.CreateRequest,
976
+ adminforth: this.adminforth,
977
+ };
978
+ for (const column of resource.columns) {
979
+ if ((_b = column.required) === null || _b === void 0 ? void 0 : _b.create) {
980
+ const shown = await isShown(column, 'create', ctxCreate);
981
+ if (shown && record[column.name] === undefined) {
982
+ return { error: `Column '${column.name}' is required`, ok: false };
983
+ }
984
+ }
985
+ }
845
986
  for (const column of resource.columns) {
846
987
  const fieldName = column.name;
847
988
  if (fieldName in record) {
848
- if (!((_b = column.showIn) === null || _b === void 0 ? void 0 : _b.create) || column.backendOnly) {
849
- return { error: `Field "${fieldName}" cannot be modified as it is restricted from creation (showIn.create is false, please set it to true)`, ok: false };
989
+ const shown = await isShown(column, 'create', ctxCreate); //
990
+ const bo = await isBackendOnly(column, ctxCreate);
991
+ const filledOnCreate = await isFilledOnCreate(column);
992
+ if ((!shown && !filledOnCreate) || bo) {
993
+ 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
994
  }
851
995
  }
852
996
  }
@@ -890,7 +1034,7 @@ export default class AdminForthRestAPI {
890
1034
  }
891
1035
  const response = await this.adminforth.createResourceRecord({ resource, record, adminUser, extra: { body, query, headers, cookies, requestUrl } });
892
1036
  if (response.error) {
893
- return { error: response.error, ok: false };
1037
+ return { error: response.error, ok: false, newRecordId: response.newRecordId };
894
1038
  }
895
1039
  const connector = this.adminforth.connectors[resource.dataSource];
896
1040
  return {
@@ -903,7 +1047,7 @@ export default class AdminForthRestAPI {
903
1047
  method: 'POST',
904
1048
  path: '/update_record',
905
1049
  handler: async ({ body, adminUser, query, headers, cookies, requestUrl }) => {
906
- var _a, _b, _c;
1050
+ var _a, _b;
907
1051
  const resource = this.adminforth.config.resources.find((res) => res.resourceId == body['resourceId']);
908
1052
  if (!resource) {
909
1053
  return { error: `Resource '${body['resourceId']}' not found` };
@@ -921,21 +1065,30 @@ export default class AdminForthRestAPI {
921
1065
  if (!allowed) {
922
1066
  return { error: allowedError };
923
1067
  }
1068
+ const ctxEdit = {
1069
+ adminUser,
1070
+ resource,
1071
+ meta: { requestBody: body, newRecord: record, oldRecord, pk: recordId },
1072
+ source: ActionCheckSource.EditRequest,
1073
+ adminforth: this.adminforth,
1074
+ };
924
1075
  for (const column of resource.columns) {
925
1076
  const fieldName = column.name;
926
1077
  if (fieldName in record) {
927
- if (!((_a = column.showIn) === null || _a === void 0 ? void 0 : _a.edit) || column.editReadonly || column.backendOnly) {
928
- return { error: `Field "${fieldName}" cannot be modified as it is restricted from editing (showIn.edit is false, please set it to true)`, ok: false };
1078
+ const shown = await isShown(column, 'edit', ctxEdit);
1079
+ const bo = await isBackendOnly(column, ctxEdit);
1080
+ if (!shown || column.editReadonly || bo) {
1081
+ 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
1082
  }
930
1083
  }
931
1084
  }
932
1085
  // for polymorphic foreign resources, we need to find out the value for polymorphicOn column
933
1086
  for (const column of resource.columns) {
934
- if (((_b = column.foreignResource) === null || _b === void 0 ? void 0 : _b.polymorphicOn) && record[column.name] === null) {
1087
+ if (((_a = column.foreignResource) === null || _a === void 0 ? void 0 : _a.polymorphicOn) && record[column.name] === null) {
935
1088
  const systemResource = column.foreignResource.polymorphicResources.find(pr => pr.resourceId === null);
936
1089
  record[column.foreignResource.polymorphicOn] = systemResource.whenValue;
937
1090
  }
938
- else if (((_c = column.foreignResource) === null || _c === void 0 ? void 0 : _c.polymorphicOn) && record[column.name]) {
1091
+ else if (((_b = column.foreignResource) === null || _b === void 0 ? void 0 : _b.polymorphicOn) && record[column.name]) {
939
1092
  let newPolymorphicOnValue = null;
940
1093
  if (record[column.name]) {
941
1094
  const targetResources = {};
@@ -1047,7 +1200,7 @@ export default class AdminForthRestAPI {
1047
1200
  method: 'POST',
1048
1201
  path: '/start_custom_action',
1049
1202
  handler: async ({ body, adminUser, tr }) => {
1050
- const { resourceId, actionId, recordId } = body;
1203
+ const { resourceId, actionId, recordId, extra } = body;
1051
1204
  const resource = this.adminforth.config.resources.find((res) => res.resourceId == resourceId);
1052
1205
  if (!resource) {
1053
1206
  return { error: await tr(`Resource {resourceId} not found`, 'errors', { resourceId }) };
@@ -1071,7 +1224,7 @@ export default class AdminForthRestAPI {
1071
1224
  redirectUrl: action.url
1072
1225
  };
1073
1226
  }
1074
- const response = await action.action({ recordId, adminUser, resource, tr, adminforth: this.adminforth });
1227
+ const response = await action.action({ recordId, adminUser, resource, tr, adminforth: this.adminforth, extra });
1075
1228
  return Object.assign({ actionId,
1076
1229
  recordId,
1077
1230
  resourceId }, response);