adminforth 1.1.74 → 1.1.75

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 (115) hide show
  1. package/auth.ts +14 -0
  2. package/dataConnectors/postgres.ts +1 -1
  3. package/dist/auth.js +7 -0
  4. package/dist/index.js +117 -65
  5. package/dist/modules/codeInjector.js +29 -15
  6. package/dist/plugins/AuditLogPlugin/index.js +2 -17
  7. package/dist/plugins/S3UploadPlugin/custom/s3uploader.vue +120 -0
  8. package/dist/plugins/S3UploadPlugin/index.js +103 -0
  9. package/dist/plugins/S3UploadPlugin/package-lock.json +431 -0
  10. package/dist/plugins/S3UploadPlugin/package.json +15 -0
  11. package/dist/plugins/TwoFactorsAuthPlugin/custom/TwoFactorsConfirmation.vue +152 -0
  12. package/dist/plugins/TwoFactorsAuthPlugin/custom/TwoFactorsSetup.vue +188 -0
  13. package/dist/plugins/TwoFactorsAuthPlugin/index.js +150 -0
  14. package/dist/plugins/TwoFactorsAuthPlugin/types.js +1 -0
  15. package/dist/spa/index.html +1 -1
  16. package/dist/spa/package-lock.json +111 -0
  17. package/dist/spa/package.json +1 -0
  18. package/dist/spa/src/App.vue +178 -133
  19. package/dist/spa/src/components/MenuLink.vue +3 -3
  20. package/dist/spa/src/components/ResourceForm.vue +96 -96
  21. package/dist/spa/src/components/ResourceListTable.vue +69 -84
  22. package/dist/spa/src/components/ValueRenderer.vue +2 -3
  23. package/dist/spa/src/composables/useStores.ts +24 -11
  24. package/dist/spa/src/main.ts +1 -1
  25. package/dist/spa/src/router/index.ts +0 -1
  26. package/dist/spa/src/utils.ts +1 -1
  27. package/dist/spa/src/views/CreateView.vue +1 -9
  28. package/dist/spa/src/views/EditView.vue +1 -9
  29. package/dist/spa/src/views/ListView.vue +12 -3
  30. package/dist/spa/src/views/LoginView.vue +6 -1
  31. package/dist/spa/src/views/ResourceParent.vue +1 -2
  32. package/dist/spa/src/views/ShowView.vue +6 -14
  33. package/dist/spa/tailwind.config.js +3 -1
  34. package/index.ts +94 -35
  35. package/modules/codeInjector.ts +38 -27
  36. package/package.json +2 -1
  37. package/plugins/AuditLogPlugin/index.ts +4 -20
  38. package/plugins/S3UploadPlugin/custom/s3uploader.vue +120 -0
  39. package/plugins/S3UploadPlugin/index.ts +112 -0
  40. package/plugins/S3UploadPlugin/package-lock.json +431 -0
  41. package/plugins/S3UploadPlugin/package.json +15 -0
  42. package/plugins/S3UploadPlugin/types.ts +76 -0
  43. package/plugins/TwoFactorsAuthPlugin/custom/TwoFactorsConfirmation.vue +152 -0
  44. package/plugins/TwoFactorsAuthPlugin/custom/TwoFactorsOtp.vue +0 -0
  45. package/plugins/TwoFactorsAuthPlugin/custom/TwoFactorsSetup.vue +188 -0
  46. package/plugins/TwoFactorsAuthPlugin/index.ts +150 -0
  47. package/plugins/TwoFactorsAuthPlugin/types.ts +10 -0
  48. package/servers/express.ts +1 -0
  49. package/spa/src/App.vue +154 -121
  50. package/spa/src/components/ResourceForm.vue +91 -91
  51. package/spa/src/router/index.ts +0 -1
  52. package/spa/src/utils.ts +1 -1
  53. package/spa/src/views/CreateView.vue +0 -1
  54. package/spa/src/views/LoginView.vue +4 -1
  55. package/types/AdminForthConfig.ts +24 -3
  56. package/dist/plugins/AuditLog/index.js +0 -13
  57. package/dist/spa/spa/.eslintrc.cjs +0 -14
  58. package/dist/spa/spa/.vscode/extensions.json +0 -6
  59. package/dist/spa/spa/README.md +0 -39
  60. package/dist/spa/spa/env.d.ts +0 -1
  61. package/dist/spa/spa/index.html +0 -23
  62. package/dist/spa/spa/package-lock.json +0 -4470
  63. package/dist/spa/spa/package.json +0 -48
  64. package/dist/spa/spa/postcss.config.js +0 -6
  65. package/dist/spa/spa/public/assets/favicon.png +0 -0
  66. package/dist/spa/spa/src/App.vue +0 -283
  67. package/dist/spa/spa/src/assets/logo.svg +0 -19
  68. package/dist/spa/spa/src/components/AcceptModal.vue +0 -45
  69. package/dist/spa/spa/src/components/Breadcrumbs.vue +0 -40
  70. package/dist/spa/spa/src/components/BreadcrumbsWithButtons.vue +0 -26
  71. package/dist/spa/spa/src/components/CustomDatePicker.vue +0 -174
  72. package/dist/spa/spa/src/components/CustomDateRangePicker.vue +0 -218
  73. package/dist/spa/spa/src/components/CustomRangePicker.vue +0 -148
  74. package/dist/spa/spa/src/components/Dropdown.vue +0 -168
  75. package/dist/spa/spa/src/components/Filters.vue +0 -211
  76. package/dist/spa/spa/src/components/HelloWorld.vue +0 -17
  77. package/dist/spa/spa/src/components/MenuLink.vue +0 -24
  78. package/dist/spa/spa/src/components/ResourceForm.vue +0 -265
  79. package/dist/spa/spa/src/components/ResourceListTable.vue +0 -404
  80. package/dist/spa/spa/src/components/SingleSkeletLoader.vue +0 -13
  81. package/dist/spa/spa/src/components/Toast.vue +0 -66
  82. package/dist/spa/spa/src/components/ValueRenderer.vue +0 -59
  83. package/dist/spa/spa/src/components/icons/IconCalendar.vue +0 -5
  84. package/dist/spa/spa/src/components/icons/IconCommunity.vue +0 -7
  85. package/dist/spa/spa/src/components/icons/IconDocumentation.vue +0 -7
  86. package/dist/spa/spa/src/components/icons/IconEcosystem.vue +0 -7
  87. package/dist/spa/spa/src/components/icons/IconSupport.vue +0 -7
  88. package/dist/spa/spa/src/components/icons/IconTime.vue +0 -5
  89. package/dist/spa/spa/src/components/icons/IconTooling.vue +0 -19
  90. package/dist/spa/spa/src/composables/useFrontendApi.ts +0 -26
  91. package/dist/spa/spa/src/composables/useStores.ts +0 -126
  92. package/dist/spa/spa/src/index.scss +0 -26
  93. package/dist/spa/spa/src/main.ts +0 -18
  94. package/dist/spa/spa/src/router/index.ts +0 -64
  95. package/dist/spa/spa/src/spa_types/core.ts +0 -51
  96. package/dist/spa/spa/src/stores/core.ts +0 -144
  97. package/dist/spa/spa/src/stores/filters.ts +0 -22
  98. package/dist/spa/spa/src/stores/modal.ts +0 -48
  99. package/dist/spa/spa/src/stores/toast.ts +0 -15
  100. package/dist/spa/spa/src/stores/user.ts +0 -54
  101. package/dist/spa/spa/src/utils.ts +0 -101
  102. package/dist/spa/spa/src/views/CreateView.vue +0 -156
  103. package/dist/spa/spa/src/views/EditView.vue +0 -157
  104. package/dist/spa/spa/src/views/ListView.vue +0 -266
  105. package/dist/spa/spa/src/views/LoginView.vue +0 -136
  106. package/dist/spa/spa/src/views/ResourceParent.vue +0 -17
  107. package/dist/spa/spa/src/views/ShowView.vue +0 -180
  108. package/dist/spa/spa/tailwind.config.js +0 -17
  109. package/dist/spa/spa/tsconfig.app.json +0 -14
  110. package/dist/spa/spa/tsconfig.json +0 -11
  111. package/dist/spa/spa/tsconfig.node.json +0 -19
  112. package/dist/spa/spa/vite.config.ts +0 -49
  113. /package/dist/plugins/{AuditLog → S3UploadPlugin}/types.js +0 -0
  114. /package/dist/{spa/spa/src/assets/base.css → plugins/TwoFactorsAuthPlugin/custom/TwoFactorsOtp.vue} +0 -0
  115. /package/dist/spa/{spa/src → src}/components/SkeleteLoader.vue +0 -0
package/auth.ts CHANGED
@@ -37,6 +37,20 @@ class AdminForthAuth {
37
37
  response.setHeader('Set-Cookie', `adminforth_jwt=${token}; Path=${this.adminforth.config.baseUrl || '/'}; HttpOnly; SameSite=Strict`);
38
38
  }
39
39
 
40
+ removeCustomCookie({response, name}) {
41
+ response.setHeader('Set-Cookie', `adminforth_test'='2'; Path=${this.adminforth.config.baseUrl || '/'}; HttpOnly; SameSite=Strict`);
42
+ }
43
+
44
+ setCustomCookie({ response, payload }: {
45
+ response: any, payload: {name: string, value: string, expiry: number, httpOnly: boolean}
46
+ }) {
47
+ const {name,value,expiry,httpOnly} = payload
48
+ response.setHeader('Set-Cookie', `adminforth_${name}=${value}; Path=${this.adminforth.config.baseUrl || '/'}; HttpOnly; SameSite=Strict; Expires=${new Date(Date.now() + expiry).toUTCString() } `);
49
+ }
50
+
51
+
52
+
53
+
40
54
  issueJWT(payload: Object, type: string) {
41
55
  // read ADMINFORH_SECRET from environment if not drop error
42
56
  const secret = process.env.ADMINFORTH_SECRET;
@@ -272,7 +272,7 @@ class PostgresConnector extends AdminForthBaseConnector implements AdminForthDat
272
272
  await this.db.query(`INSERT INTO ${tableName} (${columns.join(', ')}) VALUES (${placeholders})`, values);
273
273
  }
274
274
 
275
- async updateRecord({ resource, recordId, newValues }) {
275
+ async updateRecord({ resource, recordId, newValues }) {
276
276
  const values = [...Object.values(newValues), recordId];
277
277
  const columnsWithPlaceholders = Object.keys(newValues).map((col, i) => `"${col}" = $${i + 1}`).join(', ');
278
278
  await this.db.query(`UPDATE ${resource.table} SET ${columnsWithPlaceholders} WHERE "${this.getPrimaryKey(resource)}" = $${values.length}`, values);
package/dist/auth.js CHANGED
@@ -34,6 +34,13 @@ class AdminForthAuth {
34
34
  const token = this.issueJWT({ username, pk, }, 'auth');
35
35
  response.setHeader('Set-Cookie', `adminforth_jwt=${token}; Path=${this.adminforth.config.baseUrl || '/'}; HttpOnly; SameSite=Strict`);
36
36
  }
37
+ removeCustomCookie({ response, name }) {
38
+ response.setHeader('Set-Cookie', `adminforth_test'='2'; Path=${this.adminforth.config.baseUrl || '/'}; HttpOnly; SameSite=Strict`);
39
+ }
40
+ setCustomCookie({ response, payload }) {
41
+ const { name, value, expiry, httpOnly } = payload;
42
+ response.setHeader('Set-Cookie', `adminforth_${name}=${value}; Path=${this.adminforth.config.baseUrl || '/'}; HttpOnly; SameSite=Strict; Expires=${new Date(Date.now() + expiry).toUTCString()} `);
43
+ }
37
44
  issueJWT(payload, type) {
38
45
  // read ADMINFORH_SECRET from environment if not drop error
39
46
  const secret = process.env.ADMINFORTH_SECRET;
package/dist/index.js CHANGED
@@ -23,7 +23,7 @@ import ExpressServer from './servers/express.js';
23
23
  import { v1 as uuid } from 'uuid';
24
24
  import fs from 'fs';
25
25
  import { ADMINFORTH_VERSION, listify } from './modules/utils.js';
26
- import { AdminForthFilterOperators, AdminForthDataTypes, AdminForthResourcePages, AllowedActionsEnum, ActionCheckSource, } from './types/AdminForthConfig.js';
26
+ import { AdminForthFilterOperators, AdminForthDataTypes, AdminForthResourcePages, AllowedActionsEnum, ActionCheckSource } from './types/AdminForthConfig.js';
27
27
  import path from 'path';
28
28
  //get array from enum AdminForthResourcePages
29
29
  class AdminForth {
@@ -120,6 +120,24 @@ class AdminForth {
120
120
  if (!this.config.customization) {
121
121
  this.config.customization = {};
122
122
  }
123
+ if (!this.config.customization.customComponentsDir) {
124
+ this.config.customization.customComponentsDir = './custom';
125
+ }
126
+ try {
127
+ // check customComponentsDir exists
128
+ fs.accessSync(this.config.customization.customComponentsDir, fs.constants.R_OK);
129
+ }
130
+ catch (e) {
131
+ this.config.customization.customComponentsDir = undefined;
132
+ }
133
+ if (this.config.customization.customPages) {
134
+ this.config.customization.customPages.forEach((page, i) => {
135
+ // validate component if it's not plugin injection
136
+ if (this.codeInjector.allComponentNames.hasOwnProperty(page.component)) {
137
+ const validatedPage = this.validateComponent(page.component, errors, true);
138
+ }
139
+ });
140
+ }
123
141
  if (!this.config.baseUrl) {
124
142
  this.config.baseUrl = '';
125
143
  }
@@ -287,34 +305,41 @@ class AdminForth {
287
305
  });
288
306
  }
289
307
  // transform all hooks Functions to array of functions
290
- if (res.hooks) {
291
- for (const value of [res.hooks.show, res.hooks.list]) {
292
- if (value) {
293
- if (value.beforeDatasourceRequest) {
294
- if (!Array.isArray(value.beforeDatasourceRequest)) {
295
- value.beforeDatasourceRequest = [value.beforeDatasourceRequest];
296
- }
297
- }
298
- if (value.afterDatasourceResponse) {
299
- if (!Array.isArray(value.afterDatasourceResponse)) {
300
- value.afterDatasourceResponse = [value.afterDatasourceResponse];
301
- }
302
- }
303
- }
308
+ if (!res.hooks) {
309
+ res.hooks = {};
310
+ }
311
+ for (const hookName of ['show', 'list']) {
312
+ if (!res.hooks[hookName]) {
313
+ res.hooks[hookName] = {};
304
314
  }
305
- for (const value of [res.hooks.create, res.hooks.edit, res.hooks.delete]) {
306
- if (value) {
307
- if (value.beforeSave) {
308
- if (!Array.isArray(value.beforeSave)) {
309
- value.beforeSave = [value.beforeSave];
310
- }
311
- }
312
- if (value.afterSave) {
313
- if (!Array.isArray(value.afterSave)) {
314
- value.afterSave = [value.afterSave];
315
- }
316
- }
317
- }
315
+ if (!res.hooks[hookName].beforeDatasourceRequest) {
316
+ res.hooks[hookName].beforeDatasourceRequest = [];
317
+ }
318
+ if (!Array.isArray(res.hooks[hookName].beforeDatasourceRequest)) {
319
+ res.hooks[hookName].beforeDatasourceRequest = [res.hooks[hookName].beforeDatasourceRequest];
320
+ }
321
+ if (!res.hooks[hookName].afterDatasourceResponse) {
322
+ res.hooks[hookName].afterDatasourceResponse = [];
323
+ }
324
+ if (!Array.isArray(res.hooks[hookName].afterDatasourceResponse)) {
325
+ res.hooks[hookName].afterDatasourceResponse = [res.hooks[hookName].afterDatasourceResponse];
326
+ }
327
+ }
328
+ for (const hookName of ['create', 'edit', 'delete']) {
329
+ if (!res.hooks[hookName]) {
330
+ res.hooks[hookName] = {};
331
+ }
332
+ if (!res.hooks[hookName].beforeSave) {
333
+ res.hooks[hookName].beforeSave = [];
334
+ }
335
+ if (!Array.isArray(res.hooks[hookName].beforeSave)) {
336
+ res.hooks[hookName].beforeSave = [res.hooks[hookName].beforeSave];
337
+ }
338
+ if (!res.hooks[hookName].afterSave) {
339
+ res.hooks[hookName].afterSave = [];
340
+ }
341
+ if (!Array.isArray(res.hooks[hookName].afterSave)) {
342
+ res.hooks[hookName].afterSave = [res.hooks[hookName].afterSave];
318
343
  }
319
344
  }
320
345
  });
@@ -397,6 +422,9 @@ class AdminForth {
397
422
  if (typeof column.required === 'boolean') {
398
423
  column.required = { create: column.required, edit: column.required };
399
424
  }
425
+ if (!column.required) {
426
+ column.required = { create: false, edit: false };
427
+ }
400
428
  // same for editingNote
401
429
  if (typeof column.editingNote === 'string') {
402
430
  column.editingNote = { create: column.editingNote, edit: column.editingNote };
@@ -520,6 +548,7 @@ class AdminForth {
520
548
  yield connector.createRecord({ resource, record });
521
549
  // execute hook if needed
522
550
  for (const hook of listify((_g = (_f = resource.hooks) === null || _f === void 0 ? void 0 : _f.create) === null || _g === void 0 ? void 0 : _g.afterSave)) {
551
+ console.log('Hook afterSave', hook);
523
552
  const resp = yield hook({ resource, record, adminUser });
524
553
  if (!resp || (!resp.ok && !resp.error)) {
525
554
  throw new Error(`Hook afterSave must return object with {ok: true} or { error: 'Error' } `);
@@ -536,12 +565,15 @@ class AdminForth {
536
565
  method: 'POST',
537
566
  path: '/login',
538
567
  handler: (_b) => __awaiter(this, [_b], void 0, function* ({ body, response }) {
539
- var _c;
568
+ var _c, _d, _e, _f, _g, _h;
540
569
  const INVALID_MESSAGE = 'Invalid username or password';
541
570
  const { username, password } = body;
571
+ let adminUser;
572
+ let toReturn = { ok: true, allowedLogin: true };
542
573
  let token;
543
574
  if (username === this.config.rootUser.username && password === this.config.rootUser.password) {
544
575
  this.auth.setAuthCookie({ response, username, pk: null });
576
+ adminUser = { isRoot: true, dbUser: null, pk: null, username: this.config.rootUser.username };
545
577
  }
546
578
  else {
547
579
  // get resource from db
@@ -572,22 +604,43 @@ class AdminForth {
572
604
  return { error: 'User not found' };
573
605
  }
574
606
  const passwordHash = userRecord[this.config.auth.passwordHashField];
575
- console.log('User record', userRecord, passwordHash); // why does it has no hash?
576
607
  const valid = yield AdminForthAuth.verifyPassword(password, passwordHash);
577
608
  if (valid) {
578
- this.auth.setAuthCookie({ response, username, pk: userRecord[userResource.columns.find((col) => col.primaryKey).name] });
609
+ adminUser = {
610
+ isRoot: false, dbUser: userRecord,
611
+ pk: userRecord[userResource.columns.find((col) => col.primaryKey).name],
612
+ username
613
+ };
614
+ const beforeLoginConfirmation = this.config.auth.beforeLoginConfirmation;
615
+ if (beforeLoginConfirmation.length) {
616
+ for (const hook of beforeLoginConfirmation) {
617
+ const resp = yield hook({ userRecord: adminUser });
618
+ if ((_d = resp === null || resp === void 0 ? void 0 : resp.body) === null || _d === void 0 ? void 0 : _d.setCookie) {
619
+ (_e = resp === null || resp === void 0 ? void 0 : resp.body) === null || _e === void 0 ? void 0 : _e.setCookie.forEach((cookie) => {
620
+ this.auth.setCustomCookie({ response, payload: cookie });
621
+ });
622
+ }
623
+ if ((_f = resp === null || resp === void 0 ? void 0 : resp.body) === null || _f === void 0 ? void 0 : _f.redirectTo) {
624
+ toReturn = { ok: resp.ok, redirectTo: (_g = resp === null || resp === void 0 ? void 0 : resp.body) === null || _g === void 0 ? void 0 : _g.redirectTo, allowedLogin: (_h = resp === null || resp === void 0 ? void 0 : resp.body) === null || _h === void 0 ? void 0 : _h.allowedLogin };
625
+ break;
626
+ }
627
+ }
628
+ }
629
+ if (toReturn.allowedLogin) {
630
+ this.auth.setAuthCookie({ response, username, pk: userRecord[userResource.columns.find((col) => col.primaryKey).name] });
631
+ }
579
632
  }
580
633
  else {
581
634
  return { error: INVALID_MESSAGE };
582
635
  }
583
636
  }
584
- return { ok: true };
585
- }),
637
+ return toReturn;
638
+ })
586
639
  });
587
640
  server.endpoint({
588
641
  method: 'POST',
589
642
  path: '/check_auth',
590
- handler: (_d) => __awaiter(this, [_d], void 0, function* ({ adminUser }) {
643
+ handler: (_j) => __awaiter(this, [_j], void 0, function* ({ adminUser }) {
591
644
  return { ok: true };
592
645
  }),
593
646
  });
@@ -595,8 +648,8 @@ class AdminForth {
595
648
  noAuth: true,
596
649
  method: 'POST',
597
650
  path: '/logout',
598
- handler: (_e) => __awaiter(this, [_e], void 0, function* ({ response }) {
599
- this.auth.removeAuthCookie({ response });
651
+ handler: (_k) => __awaiter(this, [_k], void 0, function* ({ response }) {
652
+ this.auth.removeAuthCookie(response);
600
653
  return { ok: true };
601
654
  }),
602
655
  });
@@ -604,8 +657,8 @@ class AdminForth {
604
657
  noAuth: true,
605
658
  method: 'GET',
606
659
  path: '/get_public_config',
607
- handler: (_f) => __awaiter(this, [_f], void 0, function* ({ body }) {
608
- var _g;
660
+ handler: (_l) => __awaiter(this, [_l], void 0, function* ({ body }) {
661
+ var _m;
609
662
  // find resource
610
663
  if (!this.config.auth) {
611
664
  throw new Error('No config.auth defined');
@@ -617,15 +670,15 @@ class AdminForth {
617
670
  brandName: this.config.customization.brandName,
618
671
  usernameFieldName: usernameColumn.label,
619
672
  loginBackgroundImage: this.config.auth.loginBackgroundImage,
620
- title: (_g = this.config.customization) === null || _g === void 0 ? void 0 : _g.title,
673
+ title: (_m = this.config.customization) === null || _m === void 0 ? void 0 : _m.title,
621
674
  };
622
675
  }),
623
676
  });
624
677
  server.endpoint({
625
678
  method: 'GET',
626
679
  path: '/get_base_config',
627
- handler: (_h) => __awaiter(this, [_h], void 0, function* ({ input, adminUser, cookies }) {
628
- var _j, _k;
680
+ handler: (_o) => __awaiter(this, [_o], void 0, function* ({ input, adminUser, cookies }) {
681
+ var _p, _q;
629
682
  let username = '';
630
683
  let userFullName = '';
631
684
  if (adminUser.isRoot) {
@@ -672,7 +725,6 @@ class AdminForth {
672
725
  }
673
726
  newMenu.push(newMenuItem);
674
727
  }
675
- console.log(userData, 'this is the user data');
676
728
  return {
677
729
  user: userData,
678
730
  resources: this.config.resources.map((res) => ({
@@ -687,8 +739,8 @@ class AdminForth {
687
739
  deleteConfirmation: this.config.deleteConfirmation,
688
740
  auth: this.config.auth,
689
741
  usernameField: this.config.auth.usernameField,
690
- title: (_j = this.config.customization) === null || _j === void 0 ? void 0 : _j.title,
691
- emptyFieldPlaceholder: (_k = this.config.customization) === null || _k === void 0 ? void 0 : _k.emptyFieldPlaceholder,
742
+ title: (_p = this.config.customization) === null || _p === void 0 ? void 0 : _p.title,
743
+ emptyFieldPlaceholder: (_q = this.config.customization) === null || _q === void 0 ? void 0 : _q.emptyFieldPlaceholder,
692
744
  },
693
745
  adminUser,
694
746
  version: ADMINFORTH_VERSION,
@@ -727,7 +779,7 @@ class AdminForth {
727
779
  server.endpoint({
728
780
  method: 'POST',
729
781
  path: '/get_resource',
730
- handler: (_l) => __awaiter(this, [_l], void 0, function* ({ body, adminUser }) {
782
+ handler: (_r) => __awaiter(this, [_r], void 0, function* ({ body, adminUser }) {
731
783
  const { resourceId } = body;
732
784
  if (!this.statuses.dbDiscover) {
733
785
  return { error: 'Database discovery not started' };
@@ -749,8 +801,8 @@ class AdminForth {
749
801
  server.endpoint({
750
802
  method: 'POST',
751
803
  path: '/get_resource_data',
752
- handler: (_m) => __awaiter(this, [_m], void 0, function* ({ body, adminUser }) {
753
- var _o, _p, _q, _r;
804
+ handler: (_s) => __awaiter(this, [_s], void 0, function* ({ body, adminUser }) {
805
+ var _t, _u, _v, _w;
754
806
  const { resourceId, source } = body;
755
807
  if (['show', 'list'].includes(source) === false) {
756
808
  return { error: 'Invalid source, should be list or show' };
@@ -770,7 +822,7 @@ class AdminForth {
770
822
  if (!allowed) {
771
823
  return { error };
772
824
  }
773
- for (const hook of listify((_p = (_o = resource.hooks) === null || _o === void 0 ? void 0 : _o[source]) === null || _p === void 0 ? void 0 : _p.beforeDatasourceRequest)) {
825
+ 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)) {
774
826
  const resp = yield hook({ resource, query: body, adminUser });
775
827
  if (!resp || (!resp.ok && !resp.error)) {
776
828
  throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
@@ -837,7 +889,7 @@ class AdminForth {
837
889
  item[col.name] = targetDataMap[item[col.name]];
838
890
  });
839
891
  })));
840
- for (const hook of listify((_r = (_q = resource.hooks) === null || _q === void 0 ? void 0 : _q[source]) === null || _r === void 0 ? void 0 : _r.afterDatasourceResponse)) {
892
+ 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)) {
841
893
  const resp = yield hook({ resource, response: data.data, adminUser });
842
894
  if (!resp || (!resp.ok && !resp.error)) {
843
895
  throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
@@ -863,8 +915,8 @@ class AdminForth {
863
915
  server.endpoint({
864
916
  method: 'POST',
865
917
  path: '/get_resource_foreign_data',
866
- handler: (_s) => __awaiter(this, [_s], void 0, function* ({ body, adminUser }) {
867
- var _t, _u, _v, _w;
918
+ handler: (_x) => __awaiter(this, [_x], void 0, function* ({ body, adminUser }) {
919
+ var _y, _z, _0, _1;
868
920
  const { resourceId, column } = body;
869
921
  if (!this.statuses.dbDiscover) {
870
922
  return { error: 'Database discovery not started' };
@@ -885,7 +937,7 @@ class AdminForth {
885
937
  }
886
938
  const targetResourceId = columnConfig.foreignResource.resourceId;
887
939
  const targetResource = this.config.resources.find((res) => res.resourceId == targetResourceId);
888
- for (const hook of listify((_u = (_t = columnConfig.foreignResource.hooks) === null || _t === void 0 ? void 0 : _t.dropdownList) === null || _u === void 0 ? void 0 : _u.beforeDatasourceRequest)) {
940
+ 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)) {
889
941
  const resp = yield hook({ query: body, adminUser, resource: targetResource });
890
942
  if (!resp || (!resp.ok && !resp.error)) {
891
943
  throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
@@ -914,7 +966,7 @@ class AdminForth {
914
966
  const response = {
915
967
  items
916
968
  };
917
- for (const hook of listify((_w = (_v = columnConfig.foreignResource.hooks) === null || _v === void 0 ? void 0 : _v.dropdownList) === null || _w === void 0 ? void 0 : _w.afterDatasourceResponse)) {
969
+ 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)) {
918
970
  const resp = yield hook({ response, adminUser, resource: targetResource });
919
971
  if (!resp || (!resp.ok && !resp.error)) {
920
972
  throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
@@ -929,7 +981,7 @@ class AdminForth {
929
981
  server.endpoint({
930
982
  method: 'POST',
931
983
  path: '/get_min_max_for_columns',
932
- handler: (_x) => __awaiter(this, [_x], void 0, function* ({ body }) {
984
+ handler: (_2) => __awaiter(this, [_2], void 0, function* ({ body }) {
933
985
  const { resourceId } = body;
934
986
  if (!this.statuses.dbDiscover) {
935
987
  return { error: 'Database discovery not started' };
@@ -958,7 +1010,7 @@ class AdminForth {
958
1010
  server.endpoint({
959
1011
  method: 'POST',
960
1012
  path: '/create_record',
961
- handler: (_y) => __awaiter(this, [_y], void 0, function* ({ body, adminUser }) {
1013
+ handler: (_3) => __awaiter(this, [_3], void 0, function* ({ body, adminUser }) {
962
1014
  const resource = this.config.resources.find((res) => res.resourceId == body['resourceId']);
963
1015
  if (!resource) {
964
1016
  return { error: `Resource '${body['resourceId']}' not found` };
@@ -985,8 +1037,8 @@ class AdminForth {
985
1037
  server.endpoint({
986
1038
  method: 'POST',
987
1039
  path: '/update_record',
988
- handler: (_z) => __awaiter(this, [_z], void 0, function* ({ body, adminUser }) {
989
- var _0, _1, _2, _3;
1040
+ handler: (_4) => __awaiter(this, [_4], void 0, function* ({ body, adminUser }) {
1041
+ var _5, _6, _7, _8;
990
1042
  const resource = this.config.resources.find((res) => res.resourceId == body['resourceId']);
991
1043
  if (!resource) {
992
1044
  return { error: `Resource '${body['resourceId']}' not found` };
@@ -1005,7 +1057,7 @@ class AdminForth {
1005
1057
  return { error };
1006
1058
  }
1007
1059
  // execute hook if needed
1008
- for (const hook of listify((_1 = (_0 = resource.hooks) === null || _0 === void 0 ? void 0 : _0.edit) === null || _1 === void 0 ? void 0 : _1.beforeSave)) {
1060
+ 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)) {
1009
1061
  const resp = yield hook({ resource, record, adminUser });
1010
1062
  if (!resp || (!resp.ok && !resp.error)) {
1011
1063
  throw new Error(`Hook beforeSave must return object with {ok: true} or { error: 'Error' } `);
@@ -1029,10 +1081,10 @@ class AdminForth {
1029
1081
  }
1030
1082
  }
1031
1083
  if (Object.keys(newValues).length > 0) {
1032
- yield connector.updateRecord({ resource, recordId, record, newValues });
1084
+ yield connector.updateRecord({ resource, recordId, newValues });
1033
1085
  }
1034
1086
  // execute hook if needed
1035
- for (const hook of listify((_3 = (_2 = resource.hooks) === null || _2 === void 0 ? void 0 : _2.edit) === null || _3 === void 0 ? void 0 : _3.afterSave)) {
1087
+ 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)) {
1036
1088
  const resp = yield hook({ resource, record, adminUser, oldRecord });
1037
1089
  if (!resp || (!resp.ok && !resp.error)) {
1038
1090
  throw new Error(`Hook afterSave must return object with {ok: true} or { error: 'Error' } `);
@@ -1049,8 +1101,8 @@ class AdminForth {
1049
1101
  server.endpoint({
1050
1102
  method: 'POST',
1051
1103
  path: '/delete_record',
1052
- handler: (_4) => __awaiter(this, [_4], void 0, function* ({ body, adminUser }) {
1053
- var _5, _6, _7, _8;
1104
+ handler: (_9) => __awaiter(this, [_9], void 0, function* ({ body, adminUser }) {
1105
+ var _10, _11, _12, _13;
1054
1106
  const resource = this.config.resources.find((res) => res.resourceId == body['resourceId']);
1055
1107
  const record = yield this.connectors[resource.dataSource].getRecordByPrimaryKey(resource, body['primaryKey']);
1056
1108
  if (!resource) {
@@ -1068,7 +1120,7 @@ class AdminForth {
1068
1120
  return { error };
1069
1121
  }
1070
1122
  // execute hook if needed
1071
- for (const hook of listify((_6 = (_5 = resource.hooks) === null || _5 === void 0 ? void 0 : _5.delete) === null || _6 === void 0 ? void 0 : _6.beforeSave)) {
1123
+ 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)) {
1072
1124
  const resp = yield hook({ resource, record, adminUser });
1073
1125
  if (!resp || (!resp.ok && !resp.error)) {
1074
1126
  throw new Error(`Hook beforeSave must return object with {ok: true} or { error: 'Error' } `);
@@ -1080,7 +1132,7 @@ class AdminForth {
1080
1132
  const connector = this.connectors[resource.dataSource];
1081
1133
  yield connector.deleteRecord({ resource, recordId: body['primaryKey'] });
1082
1134
  // execute hook if needed
1083
- for (const hook of listify((_8 = (_7 = resource.hooks) === null || _7 === void 0 ? void 0 : _7.delete) === null || _8 === void 0 ? void 0 : _8.afterSave)) {
1135
+ 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)) {
1084
1136
  const resp = yield hook({ resource, record, adminUser });
1085
1137
  if (!resp || (!resp.ok && !resp.error)) {
1086
1138
  throw new Error(`Hook afterSave must return object with {ok: true} or { error: 'Error' } `);
@@ -1097,7 +1149,7 @@ class AdminForth {
1097
1149
  server.endpoint({
1098
1150
  method: 'POST',
1099
1151
  path: '/start_bulk_action',
1100
- handler: (_9) => __awaiter(this, [_9], void 0, function* ({ body }) {
1152
+ handler: (_14) => __awaiter(this, [_14], void 0, function* ({ body }) {
1101
1153
  const { resourceId, actionId, recordIds } = body;
1102
1154
  const resource = this.config.resources.find((res) => res.resourceId == resourceId);
1103
1155
  if (!resource) {
@@ -107,11 +107,11 @@ class CodeInjector {
107
107
  if (Object.keys(item).includes('isStaticRoute')) {
108
108
  if (!item.isStaticRoute) {
109
109
  routes += `{
110
- path: '${item.path}',
111
- name: '${item.path}',
112
- component: () => import('${item.component}'),
113
- meta: { title: '${((_a = item === null || item === void 0 ? void 0 : item.meta) === null || _a === void 0 ? void 0 : _a.title) || item.path.replace('/', '')}'}
114
- },\n`;
110
+ path: '${item.path}',
111
+ name: '${item.path}',
112
+ component: () => import('${item.component}'),
113
+ meta: { title: '${((_a = item === null || item === void 0 ? void 0 : item.meta) === null || _a === void 0 ? void 0 : _a.title) || item.path.replace('/', '')}'}
114
+ },\n`;
115
115
  }
116
116
  else {
117
117
  routes += `{
@@ -127,21 +127,21 @@ class CodeInjector {
127
127
  else {
128
128
  if (item.homepage) {
129
129
  routes += `{
130
- path: '${item.path}',
131
- name: '${item.path}',
132
- component: ${getComponentNameFromPath(item.component)},
133
- meta: { title: '${((_c = item === null || item === void 0 ? void 0 : item.meta) === null || _c === void 0 ? void 0 : _c.title) || item.path.replace('/', '')}'}
134
- },\n`;
130
+ path: '${item.path}',
131
+ name: '${item.path}',
132
+ component: ${getComponentNameFromPath(item.component)},
133
+ meta: { title: '${((_c = item === null || item === void 0 ? void 0 : item.meta) === null || _c === void 0 ? void 0 : _c.title) || item.path.replace('/', '')}'}
134
+ },\n`;
135
135
  const componentName = `${getComponentNameFromPath(item.component)}`;
136
136
  routerComponents += `import ${componentName} from '${item.component}';\n`;
137
137
  }
138
138
  else {
139
139
  routes += `{
140
- path: '${item.path}',
141
- name: '${item.path}',
142
- component: () => import('${item.component}'),
143
- meta: { title: '${((_d = item === null || item === void 0 ? void 0 : item.meta) === null || _d === void 0 ? void 0 : _d.title) || item.path.replace('/', '')}'}
144
- },\n`;
140
+ path: '${item.path}',
141
+ name: '${item.path}',
142
+ component: () => import('${item.component}'),
143
+ meta: { title: '${((_d = item === null || item === void 0 ? void 0 : item.meta) === null || _d === void 0 ? void 0 : _d.title) || item.path.replace('/', '')}'}
144
+ },\n`;
145
145
  }
146
146
  }
147
147
  }
@@ -150,6 +150,20 @@ class CodeInjector {
150
150
  }
151
151
  });
152
152
  };
153
+ const registerCustomPages = (config) => {
154
+ if (config.customization.customPages) {
155
+ config.customization.customPages.forEach((page) => {
156
+ var _a, _b, _c, _d;
157
+ routes += `{
158
+ path: '${page.path}',
159
+ name: '${page.path}',
160
+ component: () => import('${((_a = page === null || page === void 0 ? void 0 : page.component) === null || _a === void 0 ? void 0 : _a.file) || page.component}'),
161
+ meta: { title: '${((_b = page.meta) === null || _b === void 0 ? void 0 : _b.title) || page.path.replace('/', '')}',customLayout:${(_d = (_c = page === null || page === void 0 ? void 0 : page.component) === null || _c === void 0 ? void 0 : _c.meta) === null || _d === void 0 ? void 0 : _d.customLayout}}
162
+ },`;
163
+ });
164
+ }
165
+ };
166
+ registerCustomPages(this.adminforth.config);
153
167
  collectAssetsFromMenu(this.adminforth.config.menu);
154
168
  if (filesUpdated) {
155
169
  // copy only updated files
@@ -93,23 +93,8 @@ class AuditLogPlugin extends AdminForthPlugin {
93
93
  };
94
94
  return;
95
95
  }
96
- const defaultHooks = {
97
- create: { afterSave: [] },
98
- edit: { afterSave: [] },
99
- delete: { beforeSave: [] }
100
- };
101
- if (!resource.hooks) {
102
- resource.hooks = defaultHooks;
103
- }
104
- else {
105
- resource.hooks = Object.assign(Object.assign({}, defaultHooks), resource.hooks);
106
- }
107
- Object.keys(resource.hooks).forEach((hook) => {
108
- const hookToUse = hook == AllowedActionsEnum.delete ? 'beforeSave' : 'afterSave';
109
- if (!Array.isArray(resource.hooks[hook][hookToUse])) {
110
- resource.hooks[hook][hookToUse] = [resource.hooks[hook][hookToUse]];
111
- }
112
- resource.hooks[hook][hookToUse].push((_a) => __awaiter(this, [_a], void 0, function* ({ resource, record, adminUser, oldRecord }) {
96
+ ['edit', 'create', 'delete'].forEach((hook) => {
97
+ resource.hooks[hook].afterSave.push((_a) => __awaiter(this, [_a], void 0, function* ({ resource, record, adminUser, oldRecord }) {
113
98
  return yield this.createLogRecord(resource, hook, record, adminUser, oldRecord);
114
99
  }));
115
100
  });