adminforth 1.1.12 → 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 +107 -63
  4. package/dist/modules/codeInjector.js +9 -3
  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 +103 -51
  24. package/modules/codeInjector.ts +9 -3
  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 +13 -1
  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
@@ -24,7 +24,7 @@ export default class ForeignInlineListPlugin extends AdminForthPlugin {
24
24
  return { error: `Resource ${this.options.foreignResourceId} not found` };
25
25
  }
26
26
  // exclude "plugins" key
27
- const resourceCopy = Object.assign(Object.assign({}, resource), { plugins: undefined });
27
+ const resourceCopy = JSON.parse(JSON.stringify(Object.assign(Object.assign({}, resource), { plugins: undefined })));
28
28
  if (this.options.modifyTableResourceConfig) {
29
29
  this.options.modifyTableResourceConfig(resourceCopy);
30
30
  }
@@ -149,7 +149,7 @@ class ExpressServer {
149
149
  res.status(401).send('Unauthorized by AdminForth');
150
150
  return;
151
151
  }
152
- const adminforthUser = this.adminforth.auth.verify(jwt);
152
+ const adminforthUser = yield this.adminforth.auth.verify(jwt);
153
153
  if (!adminforthUser) {
154
154
  res.status(401).send('Unauthorized by AdminForth');
155
155
  }
@@ -191,7 +191,7 @@ class ExpressServer {
191
191
  this.message = message;
192
192
  }
193
193
  };
194
- const input = { body, query, headers, cookies, response, _raw_express_req: req, _raw_express_res: res };
194
+ const input = { body, query, headers, cookies, adminUser, response, _raw_express_req: req, _raw_express_res: res };
195
195
  let output;
196
196
  try {
197
197
  output = yield handler(input);
@@ -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 = {}));