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.
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 +177 -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 +167 -23
  130. package/dist/spa/src/types/Common.ts +92 -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 +150 -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 +107 -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];
@@ -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
- Object.keys(adminUser.dbUser).forEach((key) => {
279
- const col = userResource.columns.find((col) => col.name === key);
280
- if (!col || col.backendOnly) {
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
- 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];
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
- item._label = resource.recordLabel(item);
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((_e = (_d = resource.hooks) === null || _d === void 0 ? void 0 : _d[hookSource]) === null || _e === void 0 ? void 0 : _e.afterDatasourceResponse)) {
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
- 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 };
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, _c;
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
- 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 };
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 (((_b = column.foreignResource) === null || _b === void 0 ? void 0 : _b.polymorphicOn) && record[column.name] === null) {
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 (((_c = column.foreignResource) === null || _c === void 0 ? void 0 : _c.polymorphicOn) && record[column.name]) {
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);