adminforth 1.2.98 → 1.2.100

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 (36) hide show
  1. package/auth.ts +6 -5
  2. package/dataConnectors/baseConnector.ts +91 -21
  3. package/dataConnectors/clickhouse.ts +58 -37
  4. package/dataConnectors/mongo.ts +44 -14
  5. package/dataConnectors/postgres.ts +53 -36
  6. package/dataConnectors/sqlite.ts +44 -35
  7. package/dist/auth.js +6 -6
  8. package/dist/dataConnectors/baseConnector.js +75 -17
  9. package/dist/dataConnectors/clickhouse.js +59 -48
  10. package/dist/dataConnectors/mongo.js +28 -12
  11. package/dist/dataConnectors/postgres.js +62 -52
  12. package/dist/dataConnectors/sqlite.js +62 -45
  13. package/dist/index.js +52 -33
  14. package/dist/modules/configValidator.js +25 -16
  15. package/dist/modules/operationalResource.js +88 -0
  16. package/dist/modules/restApi.js +106 -96
  17. package/dist/modules/utils.js +16 -0
  18. package/dist/types/AdminForthConfig.js +37 -0
  19. package/index.ts +76 -43
  20. package/modules/configValidator.ts +26 -21
  21. package/modules/operationalResource.ts +91 -0
  22. package/modules/restApi.ts +91 -80
  23. package/modules/utils.ts +20 -0
  24. package/package.json +3 -2
  25. package/spa/src/App.vue +12 -9
  26. package/spa/src/components/Filters.vue +1 -1
  27. package/spa/src/components/ResourceForm.vue +7 -4
  28. package/spa/src/components/ResourceListTable.vue +124 -112
  29. package/spa/src/components/SkeleteLoader.vue +4 -4
  30. package/spa/src/components/ValueRenderer.vue +1 -1
  31. package/spa/src/router/index.ts +0 -8
  32. package/spa/src/spa_types/core.ts +1 -0
  33. package/spa/src/stores/filters.ts +3 -2
  34. package/spa/src/views/ListView.vue +16 -25
  35. package/spa/src/views/LoginView.vue +26 -9
  36. package/types/AdminForthConfig.ts +140 -38
@@ -47,67 +47,58 @@ export default class AdminForthRestAPI {
47
47
  const { username, password } = body;
48
48
  let adminUser;
49
49
  let toReturn = { ok: true, allowedLogin: true };
50
- let token;
51
- if (this.adminforth.config.rootUser
52
- && username === this.adminforth.config.rootUser.username
53
- && password === this.adminforth.config.rootUser.password) {
54
- this.adminforth.auth.setAuthCookie({ response, username, pk: null });
55
- adminUser = { isRoot: true, dbUser: null, pk: null, username: this.adminforth.config.rootUser.username };
56
- }
57
- else {
58
- // get resource from db
59
- if (!this.adminforth.config.auth) {
60
- throw new Error('No config.auth defined');
61
- }
62
- const userResource = this.adminforth.config.resources.find((res) => res.resourceId === this.adminforth.config.auth.resourceId);
63
- // if there is no passwordHashField, in columns, add it, with backendOnly and showIn: []
64
- if (!userResource.dataSourceColumns.find((col) => col.name === this.adminforth.config.auth.passwordHashField)) {
65
- userResource.dataSourceColumns.push({
66
- name: this.adminforth.config.auth.passwordHashField,
67
- backendOnly: true,
68
- showIn: [],
69
- type: AdminForthDataTypes.STRING,
70
- });
71
- console.log('Adding passwordHashField to userResource', userResource);
72
- }
73
- const userRecord = (_b = (yield this.adminforth.connectors[userResource.dataSource].getData({
74
- resource: userResource,
75
- filters: [
76
- { field: this.adminforth.config.auth.usernameField, operator: AdminForthFilterOperators.EQ, value: username },
77
- ],
78
- limit: 1,
79
- offset: 0,
80
- sort: [],
81
- })).data) === null || _b === void 0 ? void 0 : _b[0];
82
- if (!userRecord) {
83
- return { error: 'User not found' };
84
- }
85
- const passwordHash = userRecord[this.adminforth.config.auth.passwordHashField];
86
- const valid = yield AdminForthAuth.verifyPassword(password, passwordHash);
87
- if (valid) {
88
- adminUser = {
89
- isRoot: false, dbUser: userRecord,
90
- pk: userRecord[userResource.columns.find((col) => col.primaryKey).name],
91
- username,
92
- };
93
- const beforeLoginConfirmation = this.adminforth.config.auth.beforeLoginConfirmation;
94
- if (beforeLoginConfirmation === null || beforeLoginConfirmation === void 0 ? void 0 : beforeLoginConfirmation.length) {
95
- for (const hook of beforeLoginConfirmation) {
96
- const resp = yield hook({ adminUser, response });
97
- if ((_c = resp === null || resp === void 0 ? void 0 : resp.body) === null || _c === void 0 ? void 0 : _c.redirectTo) {
98
- toReturn = { ok: resp.ok, redirectTo: (_d = resp === null || resp === void 0 ? void 0 : resp.body) === null || _d === void 0 ? void 0 : _d.redirectTo, allowedLogin: (_e = resp === null || resp === void 0 ? void 0 : resp.body) === null || _e === void 0 ? void 0 : _e.allowedLogin };
99
- break;
100
- }
50
+ // get resource from db
51
+ if (!this.adminforth.config.auth) {
52
+ throw new Error('No config.auth defined we need it to find user, please follow the docs');
53
+ }
54
+ const userResource = this.adminforth.config.resources.find((res) => res.resourceId === this.adminforth.config.auth.usersResourceId);
55
+ // if there is no passwordHashField, in columns, add it, with backendOnly and showIn: []
56
+ if (!userResource.dataSourceColumns.find((col) => col.name === this.adminforth.config.auth.passwordHashField)) {
57
+ userResource.dataSourceColumns.push({
58
+ name: this.adminforth.config.auth.passwordHashField,
59
+ backendOnly: true,
60
+ showIn: [],
61
+ type: AdminForthDataTypes.STRING,
62
+ });
63
+ console.log('Adding passwordHashField to userResource', userResource);
64
+ }
65
+ const userRecord = (_b = (yield this.adminforth.connectors[userResource.dataSource].getData({
66
+ resource: userResource,
67
+ filters: [
68
+ { field: this.adminforth.config.auth.usernameField, operator: AdminForthFilterOperators.EQ, value: username },
69
+ ],
70
+ limit: 1,
71
+ offset: 0,
72
+ sort: [],
73
+ })).data) === null || _b === void 0 ? void 0 : _b[0];
74
+ if (!userRecord) {
75
+ return { error: 'User not found' };
76
+ }
77
+ const passwordHash = userRecord[this.adminforth.config.auth.passwordHashField];
78
+ const valid = yield AdminForthAuth.verifyPassword(password, passwordHash);
79
+ if (valid) {
80
+ adminUser = {
81
+ dbUser: userRecord,
82
+ pk: userRecord[userResource.columns.find((col) => col.primaryKey).name],
83
+ username,
84
+ };
85
+ const beforeLoginConfirmation = this.adminforth.config.auth.beforeLoginConfirmation;
86
+ if (beforeLoginConfirmation === null || beforeLoginConfirmation === void 0 ? void 0 : beforeLoginConfirmation.length) {
87
+ for (const hook of beforeLoginConfirmation) {
88
+ const resp = yield hook({ adminUser, response });
89
+ if ((_c = resp === null || resp === void 0 ? void 0 : resp.body) === null || _c === void 0 ? void 0 : _c.redirectTo) {
90
+ toReturn = { ok: resp.ok, redirectTo: (_d = resp === null || resp === void 0 ? void 0 : resp.body) === null || _d === void 0 ? void 0 : _d.redirectTo, allowedLogin: (_e = resp === null || resp === void 0 ? void 0 : resp.body) === null || _e === void 0 ? void 0 : _e.allowedLogin };
91
+ break;
101
92
  }
102
93
  }
103
- if (toReturn.allowedLogin) {
104
- this.adminforth.auth.setAuthCookie({ response, username, pk: userRecord[userResource.columns.find((col) => col.primaryKey).name] });
105
- }
106
94
  }
107
- else {
108
- return { error: INVALID_MESSAGE };
95
+ if (toReturn.allowedLogin) {
96
+ this.adminforth.auth.setAuthCookie({ response, username, pk: userRecord[userResource.columns.find((col) => col.primaryKey).name] });
109
97
  }
110
98
  }
99
+ else {
100
+ return { error: INVALID_MESSAGE };
101
+ }
111
102
  return toReturn;
112
103
  })
113
104
  });
@@ -138,12 +129,13 @@ export default class AdminForthRestAPI {
138
129
  throw new Error('No config.auth defined');
139
130
  }
140
131
  const usernameField = this.adminforth.config.auth.usernameField;
141
- const resource = this.adminforth.config.resources.find((res) => res.resourceId === this.adminforth.config.auth.resourceId);
132
+ const resource = this.adminforth.config.resources.find((res) => res.resourceId === this.adminforth.config.auth.usersResourceId);
142
133
  const usernameColumn = resource.columns.find((col) => col.name === usernameField);
143
134
  return {
144
135
  brandName: this.adminforth.config.customization.brandName,
145
136
  usernameFieldName: usernameColumn.label,
146
137
  loginBackgroundImage: this.adminforth.config.auth.loginBackgroundImage,
138
+ loginBackgroundPosition: this.adminforth.config.auth.loginBackgroundPosition,
147
139
  title: (_j = this.adminforth.config.customization) === null || _j === void 0 ? void 0 : _j.title,
148
140
  demoCredentials: this.adminforth.config.auth.demoCredentials,
149
141
  loginPromptHTML: this.adminforth.config.auth.loginPromptHTML,
@@ -154,18 +146,12 @@ export default class AdminForthRestAPI {
154
146
  method: 'GET',
155
147
  path: '/get_base_config',
156
148
  handler: (_k) => __awaiter(this, [_k], void 0, function* ({ input, adminUser, cookies }) {
157
- var _l, _m, _o, _p, _q;
149
+ var _l, _m, _o, _p;
158
150
  let username = '';
159
151
  let userFullName = '';
160
- if (adminUser.isRoot) {
161
- // isRoot can be in JWT token still when rootUser deleted, so we check for rootUser?"
162
- username = ((_l = this.adminforth.config.rootUser) === null || _l === void 0 ? void 0 : _l.username) || 'RootUser';
163
- }
164
- else {
165
- const dbUser = adminUser.dbUser;
166
- username = dbUser[this.adminforth.config.auth.usernameField];
167
- userFullName = dbUser[this.adminforth.config.auth.userFullNameField];
168
- }
152
+ const dbUser = adminUser.dbUser;
153
+ username = dbUser[this.adminforth.config.auth.usernameField];
154
+ userFullName = dbUser[this.adminforth.config.auth.userFullNameField];
169
155
  const userData = {
170
156
  [this.adminforth.config.auth.usernameField]: username,
171
157
  [this.adminforth.config.auth.userFullNameField]: userFullName
@@ -213,7 +199,7 @@ export default class AdminForthRestAPI {
213
199
  yield processMenuItem(newMenuItem);
214
200
  newMenu.push(newMenuItem);
215
201
  }
216
- const announcementBadge = (_o = (_m = this.adminforth.config.customization).announcementBadge) === null || _o === void 0 ? void 0 : _o.call(_m, adminUser);
202
+ const announcementBadge = (_m = (_l = this.adminforth.config.customization).announcementBadge) === null || _m === void 0 ? void 0 : _m.call(_l, adminUser);
217
203
  return {
218
204
  user: userData,
219
205
  resources: this.adminforth.config.resources.map((res) => ({
@@ -223,13 +209,14 @@ export default class AdminForthRestAPI {
223
209
  menu: newMenu,
224
210
  config: {
225
211
  brandName: this.adminforth.config.customization.brandName,
212
+ showBrandNameInSidebar: this.adminforth.config.customization.showBrandNameInSidebar,
226
213
  brandLogo: this.adminforth.config.customization.brandLogo,
227
214
  datesFormat: this.adminforth.config.customization.datesFormat,
228
215
  deleteConfirmation: this.adminforth.config.deleteConfirmation,
229
216
  auth: this.adminforth.config.auth,
230
217
  usernameField: this.adminforth.config.auth.usernameField,
231
- title: (_p = this.adminforth.config.customization) === null || _p === void 0 ? void 0 : _p.title,
232
- emptyFieldPlaceholder: (_q = this.adminforth.config.customization) === null || _q === void 0 ? void 0 : _q.emptyFieldPlaceholder,
218
+ title: (_o = this.adminforth.config.customization) === null || _o === void 0 ? void 0 : _o.title,
219
+ emptyFieldPlaceholder: (_p = this.adminforth.config.customization) === null || _p === void 0 ? void 0 : _p.emptyFieldPlaceholder,
233
220
  announcementBadge,
234
221
  },
235
222
  adminUser,
@@ -247,7 +234,7 @@ export default class AdminForthRestAPI {
247
234
  server.endpoint({
248
235
  method: 'POST',
249
236
  path: '/get_resource',
250
- handler: (_r) => __awaiter(this, [_r], void 0, function* ({ body, adminUser }) {
237
+ handler: (_q) => __awaiter(this, [_q], void 0, function* ({ body, adminUser }) {
251
238
  const { resourceId } = body;
252
239
  if (!this.adminforth.statuses.dbDiscover) {
253
240
  return { error: 'Database discovery not started' };
@@ -278,8 +265,8 @@ export default class AdminForthRestAPI {
278
265
  server.endpoint({
279
266
  method: 'POST',
280
267
  path: '/get_resource_data',
281
- handler: (_s) => __awaiter(this, [_s], void 0, function* ({ body, adminUser }) {
282
- var _t, _u, _v, _w;
268
+ handler: (_r) => __awaiter(this, [_r], void 0, function* ({ body, adminUser }) {
269
+ var _s, _t, _u, _v;
283
270
  const { resourceId, source } = body;
284
271
  if (['show', 'list'].includes(source) === false) {
285
272
  return { error: 'Invalid source, should be list or show' };
@@ -299,7 +286,7 @@ export default class AdminForthRestAPI {
299
286
  if (!allowed) {
300
287
  return { error };
301
288
  }
302
- for (const hook of listify((_u = (_t = resource.hooks) === null || _t === void 0 ? void 0 : _t[source]) === null || _u === void 0 ? void 0 : _u.beforeDatasourceRequest)) {
289
+ for (const hook of listify((_t = (_s = resource.hooks) === null || _s === void 0 ? void 0 : _s[source]) === null || _t === void 0 ? void 0 : _t.beforeDatasourceRequest)) {
303
290
  const resp = yield hook({ resource, query: body, adminUser });
304
291
  if (!resp || (!resp.ok && !resp.error)) {
305
292
  throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
@@ -332,6 +319,7 @@ export default class AdminForthRestAPI {
332
319
  offset,
333
320
  filters,
334
321
  sort,
322
+ getTotals: true,
335
323
  });
336
324
  // for foreign keys, add references
337
325
  yield Promise.all(resource.columns.filter((col) => col.foreignResource).map((col) => __awaiter(this, void 0, void 0, function* () {
@@ -378,7 +366,7 @@ export default class AdminForthRestAPI {
378
366
  item._label = resource.recordLabel(item);
379
367
  });
380
368
  // only after adminforth made all post processing, give user ability to edit it
381
- for (const hook of listify((_w = (_v = resource.hooks) === null || _v === void 0 ? void 0 : _v[source]) === null || _w === void 0 ? void 0 : _w.afterDatasourceResponse)) {
369
+ for (const hook of listify((_v = (_u = resource.hooks) === null || _u === void 0 ? void 0 : _u[source]) === null || _v === void 0 ? void 0 : _v.afterDatasourceResponse)) {
382
370
  const resp = yield hook({ resource, response: data.data, adminUser });
383
371
  if (!resp || (!resp.ok && !resp.error)) {
384
372
  throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
@@ -393,8 +381,8 @@ export default class AdminForthRestAPI {
393
381
  server.endpoint({
394
382
  method: 'POST',
395
383
  path: '/get_resource_foreign_data',
396
- handler: (_x) => __awaiter(this, [_x], void 0, function* ({ body, adminUser }) {
397
- var _y, _z, _0, _1;
384
+ handler: (_w) => __awaiter(this, [_w], void 0, function* ({ body, adminUser }) {
385
+ var _x, _y, _z, _0;
398
386
  const { resourceId, column } = body;
399
387
  if (!this.adminforth.statuses.dbDiscover) {
400
388
  return { error: 'Database discovery not started' };
@@ -415,7 +403,7 @@ export default class AdminForthRestAPI {
415
403
  }
416
404
  const targetResourceId = columnConfig.foreignResource.resourceId;
417
405
  const targetResource = this.adminforth.config.resources.find((res) => res.resourceId == targetResourceId);
418
- for (const hook of listify((_z = (_y = columnConfig.foreignResource.hooks) === null || _y === void 0 ? void 0 : _y.dropdownList) === null || _z === void 0 ? void 0 : _z.beforeDatasourceRequest)) {
406
+ for (const hook of listify((_y = (_x = columnConfig.foreignResource.hooks) === null || _x === void 0 ? void 0 : _x.dropdownList) === null || _y === void 0 ? void 0 : _y.beforeDatasourceRequest)) {
419
407
  const resp = yield hook({ query: body, adminUser, resource: targetResource });
420
408
  if (!resp || (!resp.ok && !resp.error)) {
421
409
  throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
@@ -444,7 +432,7 @@ export default class AdminForthRestAPI {
444
432
  const response = {
445
433
  items
446
434
  };
447
- for (const hook of listify((_1 = (_0 = columnConfig.foreignResource.hooks) === null || _0 === void 0 ? void 0 : _0.dropdownList) === null || _1 === void 0 ? void 0 : _1.afterDatasourceResponse)) {
435
+ for (const hook of listify((_0 = (_z = columnConfig.foreignResource.hooks) === null || _z === void 0 ? void 0 : _z.dropdownList) === null || _0 === void 0 ? void 0 : _0.afterDatasourceResponse)) {
448
436
  const resp = yield hook({ response, adminUser, resource: targetResource });
449
437
  if (!resp || (!resp.ok && !resp.error)) {
450
438
  throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
@@ -459,7 +447,7 @@ export default class AdminForthRestAPI {
459
447
  server.endpoint({
460
448
  method: 'POST',
461
449
  path: '/get_min_max_for_columns',
462
- handler: (_2) => __awaiter(this, [_2], void 0, function* ({ body }) {
450
+ handler: (_1) => __awaiter(this, [_1], void 0, function* ({ body }) {
463
451
  const { resourceId } = body;
464
452
  if (!this.adminforth.statuses.dbDiscover) {
465
453
  return { error: 'Database discovery not started' };
@@ -488,7 +476,7 @@ export default class AdminForthRestAPI {
488
476
  server.endpoint({
489
477
  method: 'POST',
490
478
  path: '/create_record',
491
- handler: (_3) => __awaiter(this, [_3], void 0, function* ({ body, adminUser }) {
479
+ handler: (_2) => __awaiter(this, [_2], void 0, function* ({ body, adminUser }) {
492
480
  const resource = this.adminforth.config.resources.find((res) => res.resourceId == body['resourceId']);
493
481
  if (!resource) {
494
482
  return { error: `Resource '${body['resourceId']}' not found` };
@@ -501,19 +489,20 @@ export default class AdminForthRestAPI {
501
489
  const { record } = body;
502
490
  const response = yield this.adminforth.createResourceRecord({ resource, record, adminUser });
503
491
  if (response.error) {
504
- return { error: response.error };
492
+ return { error: response.error, ok: false };
505
493
  }
506
494
  const connector = this.adminforth.connectors[resource.dataSource];
507
495
  return {
508
- newRecordId: record[connector.getPrimaryKey(resource)]
496
+ newRecordId: response.createdRecord[connector.getPrimaryKey(resource)],
497
+ ok: true
509
498
  };
510
499
  })
511
500
  });
512
501
  server.endpoint({
513
502
  method: 'POST',
514
503
  path: '/update_record',
515
- handler: (_4) => __awaiter(this, [_4], void 0, function* ({ body, adminUser }) {
516
- var _5, _6, _7, _8;
504
+ handler: (_3) => __awaiter(this, [_3], void 0, function* ({ body, adminUser }) {
505
+ var _4, _5, _6, _7;
517
506
  const resource = this.adminforth.config.resources.find((res) => res.resourceId == body['resourceId']);
518
507
  if (!resource) {
519
508
  return { error: `Resource '${body['resourceId']}' not found` };
@@ -532,8 +521,13 @@ export default class AdminForthRestAPI {
532
521
  return { error };
533
522
  }
534
523
  // execute hook if needed
535
- for (const hook of listify((_6 = (_5 = resource.hooks) === null || _5 === void 0 ? void 0 : _5.edit) === null || _6 === void 0 ? void 0 : _6.beforeSave)) {
536
- const resp = yield hook({ resource, record, adminUser });
524
+ for (const hook of listify((_5 = (_4 = resource.hooks) === null || _4 === void 0 ? void 0 : _4.edit) === null || _5 === void 0 ? void 0 : _5.beforeSave)) {
525
+ const resp = yield hook({
526
+ recordId,
527
+ resource,
528
+ record,
529
+ adminUser
530
+ });
537
531
  if (!resp || (!resp.ok && !resp.error)) {
538
532
  throw new Error(`Hook beforeSave must return object with {ok: true} or { error: 'Error' } `);
539
533
  }
@@ -559,8 +553,14 @@ export default class AdminForthRestAPI {
559
553
  yield connector.updateRecord({ resource, recordId, newValues });
560
554
  }
561
555
  // execute hook if needed
562
- for (const hook of listify((_8 = (_7 = resource.hooks) === null || _7 === void 0 ? void 0 : _7.edit) === null || _8 === void 0 ? void 0 : _8.afterSave)) {
563
- const resp = yield hook({ resource, record, adminUser, oldRecord });
556
+ for (const hook of listify((_7 = (_6 = resource.hooks) === null || _6 === void 0 ? void 0 : _6.edit) === null || _7 === void 0 ? void 0 : _7.afterSave)) {
557
+ const resp = yield hook({
558
+ resource,
559
+ record,
560
+ adminUser,
561
+ oldRecord,
562
+ recordId,
563
+ });
564
564
  if (!resp || (!resp.ok && !resp.error)) {
565
565
  throw new Error(`Hook afterSave must return object with {ok: true} or { error: 'Error' } `);
566
566
  }
@@ -576,8 +576,8 @@ export default class AdminForthRestAPI {
576
576
  server.endpoint({
577
577
  method: 'POST',
578
578
  path: '/delete_record',
579
- handler: (_9) => __awaiter(this, [_9], void 0, function* ({ body, adminUser }) {
580
- var _10, _11, _12, _13;
579
+ handler: (_8) => __awaiter(this, [_8], void 0, function* ({ body, adminUser }) {
580
+ var _9, _10, _11, _12;
581
581
  const resource = this.adminforth.config.resources.find((res) => res.resourceId == body['resourceId']);
582
582
  const record = yield this.adminforth.connectors[resource.dataSource].getRecordByPrimaryKey(resource, body['primaryKey']);
583
583
  if (!resource) {
@@ -595,8 +595,13 @@ export default class AdminForthRestAPI {
595
595
  return { error };
596
596
  }
597
597
  // execute hook if needed
598
- for (const hook of listify((_11 = (_10 = resource.hooks) === null || _10 === void 0 ? void 0 : _10.delete) === null || _11 === void 0 ? void 0 : _11.beforeSave)) {
599
- const resp = yield hook({ resource, record, adminUser });
598
+ for (const hook of listify((_10 = (_9 = resource.hooks) === null || _9 === void 0 ? void 0 : _9.delete) === null || _10 === void 0 ? void 0 : _10.beforeSave)) {
599
+ const resp = yield hook({
600
+ resource,
601
+ record,
602
+ adminUser,
603
+ recordId: body['primaryKey']
604
+ });
600
605
  if (!resp || (!resp.ok && !resp.error)) {
601
606
  throw new Error(`Hook beforeSave must return object with {ok: true} or { error: 'Error' } `);
602
607
  }
@@ -607,8 +612,13 @@ export default class AdminForthRestAPI {
607
612
  const connector = this.adminforth.connectors[resource.dataSource];
608
613
  yield connector.deleteRecord({ resource, recordId: body['primaryKey'] });
609
614
  // execute hook if needed
610
- for (const hook of listify((_13 = (_12 = resource.hooks) === null || _12 === void 0 ? void 0 : _12.delete) === null || _13 === void 0 ? void 0 : _13.afterSave)) {
611
- const resp = yield hook({ resource, record, adminUser });
615
+ for (const hook of listify((_12 = (_11 = resource.hooks) === null || _11 === void 0 ? void 0 : _11.delete) === null || _12 === void 0 ? void 0 : _12.afterSave)) {
616
+ const resp = yield hook({
617
+ resource,
618
+ record,
619
+ adminUser,
620
+ recordId: body['primaryKey']
621
+ });
612
622
  if (!resp || (!resp.ok && !resp.error)) {
613
623
  throw new Error(`Hook afterSave must return object with {ok: true} or { error: 'Error' } `);
614
624
  }
@@ -624,7 +634,7 @@ export default class AdminForthRestAPI {
624
634
  server.endpoint({
625
635
  method: 'POST',
626
636
  path: '/start_bulk_action',
627
- handler: (_14) => __awaiter(this, [_14], void 0, function* ({ body, adminUser }) {
637
+ handler: (_13) => __awaiter(this, [_13], void 0, function* ({ body, adminUser }) {
628
638
  const { resourceId, actionId, recordIds } = body;
629
639
  const resource = this.adminforth.config.resources.find((res) => res.resourceId == resourceId);
630
640
  if (!resource) {
@@ -1,6 +1,7 @@
1
1
  import path from 'path';
2
2
  import { fileURLToPath } from 'url';
3
3
  import fs from 'fs';
4
+ import Fuse from 'fuse.js';
4
5
  // @ts-ignore-next-line
5
6
  const csscolors = {
6
7
  "aliceblue": "#f0f8ff",
@@ -307,3 +308,18 @@ export function inverseRGBA(rgba) {
307
308
  let brightness = (r * 299 + g * 587 + b * 114) / 1000;
308
309
  return brightness > 128 ? 'rgba(0,0,0,1)' : 'rgba(255,255,255,1)';
309
310
  }
311
+ export function suggestIfTypo(names, name) {
312
+ if (!name) {
313
+ return null;
314
+ }
315
+ const options = {
316
+ includeScore: true, // Includes score in the results to see how close matches are
317
+ threshold: 0.3, // Defines the fuzziness (lower values mean stricter matches)
318
+ };
319
+ const fuse = new Fuse(names.filter((n) => !!n), options);
320
+ // Search for a resource
321
+ const result = fuse.search(name);
322
+ if (result.length > 0) {
323
+ return result[0].item;
324
+ }
325
+ }
@@ -53,6 +53,43 @@ export var AdminForthResourcePages;
53
53
  AdminForthResourcePages["create"] = "create";
54
54
  AdminForthResourcePages["filter"] = "filter";
55
55
  })(AdminForthResourcePages || (AdminForthResourcePages = {}));
56
+ export class Filters {
57
+ static EQ(field, value) {
58
+ return { field, operator: AdminForthFilterOperators.EQ, value };
59
+ }
60
+ static NEQ(field, value) {
61
+ return { field, operator: AdminForthFilterOperators.NE, value };
62
+ }
63
+ static GT(field, value) {
64
+ return { field, operator: AdminForthFilterOperators.GT, value };
65
+ }
66
+ static GTE(field, value) {
67
+ return { field, operator: AdminForthFilterOperators.GTE, value };
68
+ }
69
+ static LT(field, value) {
70
+ return { field, operator: AdminForthFilterOperators.LT, value };
71
+ }
72
+ static LTE(field, value) {
73
+ return { field, operator: AdminForthFilterOperators.LTE, value };
74
+ }
75
+ static IN(field, value) {
76
+ return { field, operator: AdminForthFilterOperators.IN, value };
77
+ }
78
+ static NOT_IN(field, value) {
79
+ return { field, operator: AdminForthFilterOperators.NIN, value };
80
+ }
81
+ static LIKE(field, value) {
82
+ return { field, operator: AdminForthFilterOperators.LIKE, value };
83
+ }
84
+ }
85
+ export class Sorts {
86
+ static ASC(field) {
87
+ return { field, direction: AdminForthSortDirections.asc };
88
+ }
89
+ static DESC(field) {
90
+ return { field, direction: AdminForthSortDirections.desc };
91
+ }
92
+ }
56
93
  export var AllowedActionsEnum;
57
94
  (function (AllowedActionsEnum) {
58
95
  AllowedActionsEnum["show"] = "show";