adminforth 1.1.13 → 1.1.14

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 (44) hide show
  1. package/auth.ts +17 -3
  2. package/dist/auth.js +32 -18
  3. package/dist/index.js +99 -58
  4. package/dist/modules/codeInjector.js +0 -1
  5. package/dist/modules/utils.js +2 -12
  6. package/dist/plugins/AccessControl/index.js +66 -0
  7. package/dist/plugins/AccessControl/types.js +1 -0
  8. package/dist/plugins/ForeignInlineListPlugin/index.js +1 -1
  9. package/dist/servers/express.js +2 -2
  10. package/dist/spa/spa/src/App.vue +7 -3
  11. package/dist/spa/spa/src/components/Toast.vue +5 -4
  12. package/dist/spa/spa/src/components/ValueRenderer.vue +1 -1
  13. package/dist/spa/spa/src/composables/useStores.ts +2 -1
  14. package/dist/spa/spa/src/router/index.ts +13 -1
  15. package/dist/spa/spa/src/stores/core.ts +13 -11
  16. package/dist/spa/spa/src/stores/user.ts +50 -0
  17. package/dist/spa/spa/src/utils.ts +8 -3
  18. package/dist/spa/spa/src/views/CreateView.vue +7 -0
  19. package/dist/spa/spa/src/views/EditView.vue +8 -1
  20. package/dist/spa/spa/src/views/ListView.vue +10 -1
  21. package/dist/types/AdminForthConfig.js +9 -0
  22. package/dist/types/FrontendAPI.js +4 -4
  23. package/index.ts +91 -42
  24. package/modules/codeInjector.ts +0 -1
  25. package/modules/utils.ts +2 -10
  26. package/package.json +1 -1
  27. package/plugins/AccessControl/index.ts +83 -0
  28. package/plugins/AccessControl/types.ts +14 -0
  29. package/plugins/ForeignInlineListPlugin/custom/InlineList.vue +11 -0
  30. package/plugins/ForeignInlineListPlugin/index.ts +2 -1
  31. package/servers/express.ts +2 -2
  32. package/spa/src/App.vue +7 -3
  33. package/spa/src/components/Toast.vue +5 -4
  34. package/spa/src/components/ValueRenderer.vue +1 -1
  35. package/spa/src/composables/useStores.ts +2 -1
  36. package/spa/src/router/index.ts +13 -1
  37. package/spa/src/stores/core.ts +13 -11
  38. package/spa/src/stores/user.ts +50 -0
  39. package/spa/src/utils.ts +8 -3
  40. package/spa/src/views/CreateView.vue +7 -0
  41. package/spa/src/views/EditView.vue +8 -1
  42. package/spa/src/views/ListView.vue +10 -1
  43. package/types/AdminForthConfig.ts +73 -21
  44. package/types/FrontendAPI.ts +11 -5
@@ -64,7 +64,6 @@
64
64
  aria-label="Sidebar"
65
65
  >
66
66
  <div class="h-full px-3 pb-4 overflow-y-auto bg-nav-menu-bg dark:bg-gray-800 border-r">
67
-
68
67
  <div class="flex ms-2 md:me-24 m-4 ">
69
68
  <img :src="loadFile(coreStore.config?.brandLogo || '@/assets/logo.svg')" :alt="`${ coreStore.config?.brandName } Logo`" class="h-8 me-3" />
70
69
  <span class="self-center text-header-text-size font-semibold sm:text-header-text-size whitespace-nowrap dark:text-header-text text-header-logo-color">
@@ -168,6 +167,7 @@ import { RouterLink, RouterView } from 'vue-router';
168
167
  import { initFlowbite } from 'flowbite'
169
168
  import './index.scss'
170
169
  import { useCoreStore } from '@/stores/core';
170
+ import { useUserStore } from '@/stores/user';
171
171
  import {useModalStore} from '@/stores/modal';
172
172
  import { IconMoonSolid, IconSunSolid } from '@iconify-prerendered/vue-flowbite';
173
173
  import AcceptModal from './components/AcceptModal.vue';
@@ -184,6 +184,7 @@ import { FrontendAPI } from '@/composables/useStores'
184
184
  const coreStore = useCoreStore();
185
185
  const modalStore = useModalStore();
186
186
  const toastStore = useToastStore();
187
+ const userStore = useUserStore();
187
188
  const frontendApi = new FrontendAPI();
188
189
  frontendApi.init();
189
190
  const splitAtLast = (str: string, separator: string) => {
@@ -221,8 +222,9 @@ if (opened.value.includes(label)) {
221
222
  }
222
223
 
223
224
  async function logout() {
224
- await coreStore.logout();
225
- router.push({ name: 'login' });
225
+ userStore.unauthorize();
226
+ await userStore.logout();
227
+ router.push({ name: 'login' })
226
228
  }
227
229
 
228
230
 
@@ -244,6 +246,8 @@ async function loadMenu() {
244
246
  });
245
247
  }
246
248
 
249
+
250
+
247
251
  watch(route, () => {
248
252
  title.value = `${coreStore.config?.title || coreStore.config?.brandName || 'Adminforth'} | ${ Object.values(route.params)[0] || route.meta.title || ' '}`;
249
253
  useHead({
@@ -46,16 +46,17 @@ const props = defineProps<{
46
46
  toast:{message: string;
47
47
  variant: string;
48
48
  id: string;
49
- duration?: number;}
49
+ timeout?: number|'unlimited';}
50
50
  }>();
51
51
  function closeToast() {
52
52
  emit('close');
53
53
  }
54
54
 
55
55
  onMounted(() => {
56
- setTimeout(() => {
57
- emit('close');
58
- }, props.toast.duration || 5000 );
56
+ if (props.toast.timeout === 'unlimited') return;
57
+ else {
58
+ setTimeout(() => {emit('close');}, props.toast.timeout || 10000 );
59
+ }
59
60
  });
60
61
 
61
62
  </script>
@@ -41,7 +41,7 @@ import { useRoute, useRouter } from 'vue-router';
41
41
  import { useCoreStore } from '@/stores/core';
42
42
 
43
43
  const coreStore = useCoreStore();
44
- const route = useRoute();
44
+ const route = useRoute();
45
45
 
46
46
 
47
47
  dayjs.extend(utc);
@@ -56,7 +56,8 @@ export class FrontendAPI implements FrontendAPIInterface {
56
56
  alert(params: AlertParams): void {
57
57
  this.toastStore.addToast({
58
58
  message: params.message,
59
- variant: params.variant
59
+ variant: params.variant,
60
+ timeout: params.timeout
60
61
  })
61
62
  }
62
63
 
@@ -1,5 +1,6 @@
1
1
  import { createRouter, createWebHistory } from 'vue-router'
2
2
  import ResourceParent from '@/views/ResourceParent.vue'
3
+ import { useUserStore } from '@/stores/user'
3
4
  /* IMPORTANT:ADMINFORTH ROUTES IMPORTS */
4
5
 
5
6
  const router = createRouter({
@@ -9,7 +10,15 @@ const router = createRouter({
9
10
  path: '/login',
10
11
  name: 'login',
11
12
  component: () => import('@/views/LoginView.vue'),
12
- meta: { title: 'login' }
13
+ meta: { title: 'login' },
14
+ beforeEnter: async (to, from, next) => {
15
+ const userStore = useUserStore()
16
+ if (await userStore.checkAuth(true)) {
17
+ next({ name: 'home' })
18
+ } else {
19
+ next()
20
+ }
21
+ }
13
22
  },
14
23
  {
15
24
  path: '/resource/:resourceId',
@@ -49,4 +58,7 @@ const router = createRouter({
49
58
  })
50
59
 
51
60
 
61
+
62
+
63
+
52
64
  export default router
@@ -8,7 +8,7 @@ export const useCoreStore = defineStore('core', () => {
8
8
  const resourceById: Ref<Object> = ref({});
9
9
  const menu = ref([]);
10
10
  const config = ref({});
11
- const record = ref({});
11
+ const record: Ref<any | null> = ref({});
12
12
  const resource: Ref<AdminForthResource | null> = ref(null);
13
13
 
14
14
  const resourceColumnsWithFilters = computed(() => {
@@ -67,9 +67,17 @@ export const useCoreStore = defineStore('core', () => {
67
67
  }
68
68
  });
69
69
 
70
- console.log('📦 record', respData);
71
- record.value = respData.data[0];
72
- resourceOptions.value = respData.options;
70
+ if (respData.error) {
71
+ window.adminforth.alert({
72
+ message: respData.error,
73
+ variant: 'danger',
74
+ timeout: 'unlimited'
75
+ });
76
+ record.value = {};
77
+ } else {
78
+ record.value = respData.data[0];
79
+ }
80
+
73
81
  }
74
82
 
75
83
  async function fetchResourceFull({ resourceId }: { resourceId: string }) {
@@ -103,12 +111,7 @@ export const useCoreStore = defineStore('core', () => {
103
111
  config.value = {...config.value, ...res};
104
112
  }
105
113
 
106
- async function logout() {
107
- await callAdminForthApi({
108
- path: '/logout',
109
- method: 'POST',
110
- });
111
- }
114
+
112
115
 
113
116
  const username = computed(() => {
114
117
  const usernameField = config.value.usernameField;
@@ -134,7 +137,6 @@ export const useCoreStore = defineStore('core', () => {
134
137
  fetchResourceFull,
135
138
  resourceColumnsError,
136
139
  resourceOptions,
137
- logout,
138
140
  resource,
139
141
  adminUser,
140
142
  resourceColumnsWithFilters
@@ -0,0 +1,50 @@
1
+ import {ref} from 'vue';
2
+ import {defineStore} from 'pinia';
3
+ import { callAdminForthApi } from '@/utils';
4
+
5
+ export const useUserStore = defineStore('user', () => {
6
+ const isAuthorized = ref(false);
7
+
8
+ function authorize() {
9
+ isAuthorized.value = true;
10
+ }
11
+
12
+ function unauthorize() {
13
+ isAuthorized.value = false;
14
+ }
15
+
16
+ async function logout() {
17
+ await callAdminForthApi({
18
+ path: '/logout',
19
+ method: 'POST',
20
+ });
21
+ }
22
+
23
+ async function checkAuth( skipApiCall = false){
24
+ if(isAuthorized.value) return true;
25
+ else {
26
+ if(skipApiCall) return false;
27
+ const resp = await callAdminForthApi({
28
+ path: '/check_auth',
29
+ method: 'POST',
30
+ });
31
+ if (resp.status !== 401) {
32
+ authorize();
33
+ return true;
34
+ }
35
+ else {
36
+ unauthorize();
37
+ return false;}
38
+ }
39
+
40
+ }
41
+
42
+ return {
43
+ isAuthorized,
44
+ authorize,
45
+ unauthorize,
46
+ checkAuth,
47
+ logout
48
+ }
49
+
50
+ });
@@ -4,6 +4,7 @@ import type { CoreConfig } from './spa_types/core';
4
4
  import router from "./router";
5
5
  import { useRouter } from 'vue-router';
6
6
  import { useCoreStore } from './stores/core';
7
+ import { useUserStore } from './stores/user';
7
8
 
8
9
  export async function callApi({path, method, body=undefined} ) {
9
10
  const options = {
@@ -16,14 +17,18 @@ export async function callApi({path, method, body=undefined} ) {
16
17
  const fullPath = `${import.meta.env.VITE_ADMINFORTH_PUBLIC_PATH || ''}${path}`;
17
18
  const r = await fetch(fullPath, options);
18
19
  if (r.status == 401) {
19
- console.log('router', router);
20
- router.push({name: 'login'});
20
+ useUserStore().unauthorize();
21
+ router.push({ name: 'login' });
21
22
  return null;
22
23
  }
23
24
  return await r.json();
24
25
  }
25
26
 
26
- export async function callAdminForthApi({ path, method, body=undefined }) {
27
+ export async function callAdminForthApi({ path, method, body=undefined }: {
28
+ path: string,
29
+ method: 'GET' | 'POST' | 'PUT' | 'DELETE',
30
+ body?: any
31
+ }) {
27
32
  try {
28
33
  return callApi({path: `/adminapi/v1${path}`, method, body} );
29
34
  } catch (e) {
@@ -137,6 +137,13 @@ async function saveRecord() {
137
137
  record: record.value,
138
138
  },
139
139
  });
140
+ if (response.error) {
141
+ window.adminforth.alert({
142
+ message: resp.error,
143
+ variant: 'danger',
144
+ timeout: 'unlimited',
145
+ })
146
+ }
140
147
  saving.value = false;
141
148
  if (route.query.returnTo) {
142
149
  router.push(route.query.returnTo);
@@ -145,7 +145,7 @@ async function saveRecord() {
145
145
  }
146
146
  }
147
147
 
148
- await callAdminForthApi({
148
+ const resp = await callAdminForthApi({
149
149
  method: 'POST',
150
150
  path: `/update_record`,
151
151
  body: {
@@ -154,6 +154,13 @@ async function saveRecord() {
154
154
  record: updates,
155
155
  },
156
156
  });
157
+ if (resp.error) {
158
+ window.adminforth.alert({
159
+ message: resp.error,
160
+ variant: 'danger',
161
+ timeout: 'unlimited',
162
+ })
163
+ }
157
164
  saving.value = false;
158
165
  router.push({ name: 'resource-show', params: { resourceId: route.params.resourceId, primaryKey: coreStore.record[coreStore.primaryKey] } });
159
166
  }
@@ -183,7 +183,16 @@ async function getList() {
183
183
  sort: sort.value,
184
184
  }
185
185
  });
186
-
186
+ if (data.error) {
187
+ window.adminforth.alert({
188
+ message: data.error,
189
+ variant: 'danger',
190
+ timeout: 'unlimited',
191
+ });
192
+ rows.value = [];
193
+ totalRows.value = 0;
194
+ return;
195
+ }
187
196
  rows.value = data.data?.map(row => {
188
197
  row._primaryKeyValue = row[coreStore.resource.columns.find(c => c.primaryKey).name];
189
198
  return row;
@@ -52,6 +52,15 @@ export var AdminForthResourcePages;
52
52
  AdminForthResourcePages["create"] = "create";
53
53
  AdminForthResourcePages["filter"] = "filter";
54
54
  })(AdminForthResourcePages || (AdminForthResourcePages = {}));
55
+ export var AllowedActionsEnum;
56
+ (function (AllowedActionsEnum) {
57
+ AllowedActionsEnum["show"] = "show";
58
+ AllowedActionsEnum["list"] = "list";
59
+ AllowedActionsEnum["edit"] = "edit";
60
+ AllowedActionsEnum["create"] = "create";
61
+ AllowedActionsEnum["delete"] = "delete";
62
+ AllowedActionsEnum["filter"] = "filter";
63
+ })(AllowedActionsEnum || (AllowedActionsEnum = {}));
55
64
  export var AdminForthDataTypes;
56
65
  (function (AdminForthDataTypes) {
57
66
  AdminForthDataTypes["STRING"] = "string";
@@ -1,7 +1,7 @@
1
1
  export var AlertVariant;
2
2
  (function (AlertVariant) {
3
- AlertVariant["Danger"] = "danger";
4
- AlertVariant["Success"] = "success";
5
- AlertVariant["Warning"] = "warning";
6
- AlertVariant["Info"] = "info";
3
+ AlertVariant["danger"] = "danger";
4
+ AlertVariant["success"] = "success";
5
+ AlertVariant["warning"] = "warning";
6
+ AlertVariant["info"] = "info";
7
7
  })(AlertVariant || (AlertVariant = {}));
package/index.ts CHANGED
@@ -1,5 +1,5 @@
1
1
 
2
- import Auth from './auth.js';
2
+ import AdminForthAuth from './auth.js';
3
3
  import MongoConnector from './dataConnectors/mongo.js';
4
4
  import PostgresConnector from './dataConnectors/postgres.js';
5
5
  import SQLiteConnector from './dataConnectors/sqlite.js';
@@ -8,10 +8,13 @@ import { guessLabelFromName } from './modules/utils.js';
8
8
  import ExpressServer from './servers/express.js';
9
9
  import {v1 as uuid} from 'uuid';
10
10
  import fs from 'fs';
11
- import { ADMINFORTH_VERSION } from './modules/utils.js';
11
+ import { ADMINFORTH_VERSION, listify } from './modules/utils.js';
12
12
  import { AdminForthConfig, AdminForthClass, AdminForthComponentDeclaration, AdminForthComponentDeclarationFull,
13
- AdminForthFilterOperators, AdminForthDataTypes, AdminForthResourcePages } from './types/AdminForthConfig.js';
14
- import { getFunctionList } from './modules/utils.js';
13
+ AdminForthFilterOperators, AdminForthDataTypes, AdminForthResourcePages, GenericHttpServer,
14
+ BeforeSaveFunction,
15
+ AfterSaveFunction,
16
+ BeforeDataSourceRequestFunction,
17
+ AfterDataSourceResponseFunction} from './types/AdminForthConfig.js';
15
18
  import path from 'path';
16
19
  import AdminForthPlugin from './plugins/base.js';
17
20
 
@@ -26,7 +29,7 @@ class AdminForth implements AdminForthClass {
26
29
 
27
30
  static Utils = {
28
31
  generatePasswordHash: async (password) => {
29
- return await Auth.generatePasswordHash(password);
32
+ return await AdminForthAuth.generatePasswordHash(password);
30
33
  }
31
34
  }
32
35
 
@@ -36,7 +39,7 @@ class AdminForth implements AdminForthClass {
36
39
 
37
40
  config: AdminForthConfig;
38
41
  express: ExpressServer;
39
- auth: Auth;
42
+ auth: AdminForthAuth;
40
43
  codeInjector: CodeInjector;
41
44
  connectors: any;
42
45
  connectorClasses: any;
@@ -57,7 +60,7 @@ class AdminForth implements AdminForthClass {
57
60
  this.validateConfig(); // revalidate after plugins
58
61
 
59
62
  this.express = new ExpressServer(this);
60
- this.auth = new Auth();
63
+ this.auth = new AdminForthAuth(this);
61
64
  this.connectors = {};
62
65
  this.statuses = {};
63
66
 
@@ -237,7 +240,7 @@ class AdminForth implements AdminForthClass {
237
240
  if(res.options?.bulkActions){
238
241
  let bulkActions = res.options.bulkActions;
239
242
 
240
- if(!Array.isArray(bulkActions)){
243
+ if (!Array.isArray(bulkActions)) {
241
244
  errors.push(`Resource "${res.resourceId}" bulkActions must be an array`);
242
245
  bulkActions = [];
243
246
  }
@@ -294,7 +297,40 @@ class AdminForth implements AdminForthClass {
294
297
  } else {
295
298
  res.options.allowedActions = DEFAULT_ALLOWED_ACTIONS;
296
299
  }
297
- })
300
+
301
+ // transform all hooks Functions to array of functions
302
+ if (res.hooks) {
303
+ for (const value of [res.hooks.show, res.hooks.list]) {
304
+ if (value) {
305
+ if (value.beforeDatasourceRequest) {
306
+ if (!Array.isArray(value.beforeDatasourceRequest)) {
307
+ value.beforeDatasourceRequest = [value.beforeDatasourceRequest];
308
+ }
309
+ }
310
+ if (value.afterDatasourceResponse) {
311
+ if (!Array.isArray(value.afterDatasourceResponse)) {
312
+ value.afterDatasourceResponse = [value.afterDatasourceResponse];
313
+ }
314
+ }
315
+ }
316
+ }
317
+ for (const value of [res.hooks.create, res.hooks.edit, res.hooks.delete]) {
318
+ if (value) {
319
+
320
+ if (value.beforeSave) {
321
+ if (!Array.isArray(value.beforeSave)) {
322
+ value.beforeSave = [value.beforeSave];
323
+ }
324
+ }
325
+ if (value.afterSave) {
326
+ if (!Array.isArray(value.afterSave)) {
327
+ value.afterSave = [value.afterSave];
328
+ }
329
+ }
330
+ }
331
+ }
332
+ }
333
+ });
298
334
 
299
335
 
300
336
 
@@ -373,7 +409,6 @@ class AdminForth implements AdminForthClass {
373
409
  for (const resource of this.config.resources) {
374
410
  for (const column of resource.columns) {
375
411
  if (column.components) {
376
- console.log('🔧🔧🔧 Validating components for resource', column.components);
377
412
 
378
413
  for (const [key, comp] of Object.entries(column.components as Record<string, AdminForthComponentDeclarationFull>)) {
379
414
  let ignoreExistsCheck = false;
@@ -461,7 +496,24 @@ class AdminForth implements AdminForthClass {
461
496
  this.codeInjector.bundleNow({ hotReload, verbose });
462
497
  }
463
498
 
464
- setupEndpoints(server) {
499
+ async getUserByPk(pk: string) {
500
+ const resource = this.config.resources.find((res) => res.resourceId === this.config.auth.resourceId);
501
+ if (!resource) {
502
+ throw new Error('No auth resource found');
503
+ }
504
+ const users = await this.connectors[resource.dataSource].getData({
505
+ resource,
506
+ filters: [
507
+ { field: resource.columns.find((col) => col.primaryKey).name, operator: AdminForthFilterOperators.EQ, value: pk },
508
+ ],
509
+ limit: 1,
510
+ offset: 0,
511
+ sort: [],
512
+ });
513
+ return users.data[0] || null;
514
+ }
515
+
516
+ setupEndpoints(server: GenericHttpServer) {
465
517
  server.endpoint({
466
518
  noAuth: true,
467
519
  method: 'POST',
@@ -507,7 +559,7 @@ class AdminForth implements AdminForthClass {
507
559
 
508
560
  const passwordHash = userRecord[this.config.auth.passwordHashField];
509
561
  console.log('User record', userRecord, passwordHash) // why does it has no hash?
510
- const valid = await Auth.verifyPassword(password, passwordHash);
562
+ const valid = await AdminForthAuth.verifyPassword(password, passwordHash);
511
563
  if (valid) {
512
564
  token = this.auth.issueJWT({
513
565
  username, pk: userRecord[userResource.columns.find((col) => col.primaryKey).name]
@@ -522,6 +574,14 @@ class AdminForth implements AdminForthClass {
522
574
  },
523
575
  });
524
576
 
577
+ server.endpoint({
578
+ method: 'POST',
579
+ path: '/check_auth',
580
+ handler: async ({ adminUser }) => {
581
+ return { ok: true };
582
+ },
583
+ });
584
+
525
585
  server.endpoint({
526
586
  noAuth: true,
527
587
  method: 'POST',
@@ -559,27 +619,14 @@ class AdminForth implements AdminForthClass {
559
619
  method: 'GET',
560
620
  path: '/get_base_config',
561
621
  handler: async ({input, adminUser, cookies}) => {
562
- const cookieParsed = this.auth.verify(cookies['adminforth_jwt']);
563
622
  let username = ''
564
623
  let userFullName = ''
565
- if (cookieParsed['pk'] == null) {
624
+ if (adminUser.isRoot) {
566
625
  username = this.config.rootUser.username;
567
626
  } else {
568
- const userResource = this.config.resources.find((res) => res.resourceId === this.config.auth.resourceId);
569
- const user = await this.connectors[userResource.dataSource].getData({
570
- resource: userResource,
571
- filters: [
572
- { field: userResource.columns.find((col) => col.primaryKey).name, operator: AdminForthFilterOperators.EQ, value: cookieParsed['pk'] },
573
- ],
574
- limit: 1,
575
- offset: 0,
576
- sort: [],
577
- });
578
- if (!user.data.length) {
579
- return { error: 'Unauthorized' };
580
- }
581
- username = user.data[0][this.config.auth.usernameField];
582
- userFullName = user.data[0][this.config.auth.userFullNameField];
627
+ const dbUser = adminUser.dbUser;
628
+ username = dbUser[this.config.auth.usernameField];
629
+ userFullName =dbUser[this.config.auth.userFullNameField];
583
630
  }
584
631
 
585
632
  const userData = {
@@ -609,6 +656,8 @@ class AdminForth implements AdminForthClass {
609
656
  },
610
657
  });
611
658
 
659
+
660
+
612
661
  server.endpoint({
613
662
  method: 'POST',
614
663
  path: '/get_resource',
@@ -647,7 +696,7 @@ class AdminForth implements AdminForthClass {
647
696
  return { error: `Resource ${resourceId} not found` };
648
697
  }
649
698
 
650
- for (const hook of getFunctionList(resource.hooks?.[source]?.beforeDatasourceRequest)) {
699
+ for (const hook of listify(resource.hooks?.[source]?.beforeDatasourceRequest)) {
651
700
  const resp = await hook({ resource, query: body, adminUser });
652
701
  if (!resp || (!resp.ok && !resp.error)) {
653
702
  throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
@@ -678,7 +727,7 @@ class AdminForth implements AdminForthClass {
678
727
  // nonsense
679
728
  return { data: [], total: 0 };
680
729
  }
681
- }
730
+ }
682
731
 
683
732
  const data = await this.connectors[resource.dataSource].getData({
684
733
  resource,
@@ -723,7 +772,7 @@ class AdminForth implements AdminForthClass {
723
772
  })
724
773
  );
725
774
 
726
- for (const hook of getFunctionList(resource.hooks?.[source]?.afterDatasourceResponse)) {
775
+ for (const hook of listify(resource.hooks?.[source]?.afterDatasourceResponse)) {
727
776
  const resp = await hook({ resource, response: data.data, adminUser });
728
777
  if (!resp || (!resp.ok && !resp.error)) {
729
778
  throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
@@ -778,8 +827,8 @@ class AdminForth implements AdminForthClass {
778
827
  const targetResourceId = columnConfig.foreignResource.resourceId;
779
828
  const targetResource = this.config.resources.find((res) => res.resourceId == targetResourceId);
780
829
 
781
- for (const hook of getFunctionList(columnConfig.foreignResource.hooks?.dropdownList?.beforeDatasourceRequest)) {
782
- const resp = await hook({ query: body, adminUser });
830
+ for (const hook of listify(columnConfig.foreignResource.hooks?.dropdownList?.beforeDatasourceRequest as BeforeDataSourceRequestFunction[])) {
831
+ const resp = await hook({ query: body, adminUser, resource: targetResource });
783
832
  if (!resp || (!resp.ok && !resp.error)) {
784
833
  throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
785
834
  }
@@ -809,8 +858,8 @@ class AdminForth implements AdminForthClass {
809
858
  items
810
859
  };
811
860
 
812
- for (const hook of getFunctionList(columnConfig.foreignResource.hooks?.dropdownList?.afterDatasourceResponse)) {
813
- const resp = await hook({ response, adminUser });
861
+ for (const hook of listify(columnConfig.foreignResource.hooks?.dropdownList?.afterDatasourceResponse as AfterDataSourceResponseFunction[])) {
862
+ const resp = await hook({ response, adminUser, resource: targetResource });
814
863
  if (!resp || (!resp.ok && !resp.error)) {
815
864
  throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
816
865
  }
@@ -866,7 +915,7 @@ class AdminForth implements AdminForthClass {
866
915
 
867
916
  const record = body['record'];
868
917
  // execute hook if needed
869
- for (const hook of getFunctionList(resource.hooks?.create?.beforeSave)) {
918
+ for (const hook of listify(resource.hooks?.create?.beforeSave as BeforeSaveFunction[])) {
870
919
  const resp = await hook({ resource, record, adminUser });
871
920
  if (!resp || (!resp.ok && !resp.error)) {
872
921
  throw new Error(`Hook beforeSave must return object with {ok: true} or { error: 'Error' } `);
@@ -912,7 +961,7 @@ class AdminForth implements AdminForthClass {
912
961
  const connector = this.connectors[resource.dataSource];
913
962
  await connector.createRecord({ resource, record });
914
963
  // execute hook if needed
915
- for (const hook of getFunctionList(resource.hooks?.create?.afterSave)) {
964
+ for (const hook of listify(resource.hooks?.create?.afterSave as AfterSaveFunction[])) {
916
965
  const resp = await hook({ resource, record, adminUser });
917
966
  if (!resp || (!resp.ok && !resp.error)) {
918
967
  throw new Error(`Hook afterSave must return object with {ok: true} or { error: 'Error' } `);
@@ -947,7 +996,7 @@ class AdminForth implements AdminForthClass {
947
996
  const record = body['record'];
948
997
 
949
998
  // execute hook if needed
950
- for (const hook of getFunctionList(resource.hooks?.edit?.beforeSave)) {
999
+ for (const hook of listify(resource.hooks?.edit?.beforeSave as BeforeSaveFunction[])) {
951
1000
  const resp = await hook({ resource, record, adminUser });
952
1001
  if (!resp || (!resp.ok && !resp.error)) {
953
1002
  throw new Error(`Hook beforeSave must return object with {ok: true} or { error: 'Error' } `);
@@ -978,7 +1027,7 @@ class AdminForth implements AdminForthClass {
978
1027
  }
979
1028
 
980
1029
  // execute hook if needed
981
- for (const hook of getFunctionList(resource.hooks?.edit?.afterSave)) {
1030
+ for (const hook of listify(resource.hooks?.edit?.afterSave as AfterSaveFunction[])) {
982
1031
  const resp = await hook({ resource, record, adminUser });
983
1032
  if (!resp || (!resp.ok && !resp.error)) {
984
1033
  throw new Error(`Hook afterSave must return object with {ok: true} or { error: 'Error' } `);
@@ -1011,7 +1060,7 @@ class AdminForth implements AdminForthClass {
1011
1060
  }
1012
1061
 
1013
1062
  // execute hook if needed
1014
- for (const hook of getFunctionList(resource.hooks?.delete?.beforeSave)) {
1063
+ for (const hook of listify(resource.hooks?.delete?.beforeSave as BeforeSaveFunction[])) {
1015
1064
  const resp = await hook({ resource, record, adminUser });
1016
1065
  if (!resp || (!resp.ok && !resp.error)) {
1017
1066
  throw new Error(`Hook beforeSave must return object with {ok: true} or { error: 'Error' } `);
@@ -1026,7 +1075,7 @@ class AdminForth implements AdminForthClass {
1026
1075
  await connector.deleteRecord({ resource, recordId: body['primaryKey']});
1027
1076
 
1028
1077
  // execute hook if needed
1029
- for (const hook of getFunctionList(resource.hooks?.delete?.afterSave)) {
1078
+ for (const hook of listify(resource.hooks?.delete?.afterSave as BeforeSaveFunction[])) {
1030
1079
  const resp = await hook({ resource, record, adminUser });
1031
1080
  if (!resp || (!resp.ok && !resp.error)) {
1032
1081
  throw new Error(`Hook afterSave must return object with {ok: true} or { error: 'Error' } `);
@@ -282,7 +282,6 @@ class CodeInjector implements CodeInjectorType {
282
282
 
283
283
  });
284
284
 
285
- console.log('🔧 🔧 Injecting code into Vue sources...', customResourceComponents);
286
285
  customResourceComponents.forEach((filePath) => {
287
286
  const componentName = getComponentNameFromPath(filePath);
288
287
  this.allComponentNames[filePath] = componentName;
package/modules/utils.ts CHANGED
@@ -34,14 +34,6 @@ export function getComponentNameFromPath(filePath) {
34
34
  return filePath.replace(/@/g, '').replace(/\./g, '').replace(/\//g, '');
35
35
  }
36
36
 
37
- export function getFunctionList(param?: Function | Array<Function>) {
38
- if (param) {
39
- if (Array.isArray(param)) {
40
- return param;
41
- } else {
42
- return [param];
43
- }
44
- } else {
45
- return [];
46
- }
37
+ export function listify(param?: Array<Function>) {
38
+ return param || [];
47
39
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adminforth",
3
- "version": "1.1.13",
3
+ "version": "1.1.14",
4
4
  "description": "OpenSource Vue3 powered forth-generation admin panel",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",