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.
- package/auth.ts +17 -3
- package/dist/auth.js +32 -18
- package/dist/index.js +99 -58
- package/dist/modules/codeInjector.js +0 -1
- package/dist/modules/utils.js +2 -12
- package/dist/plugins/AccessControl/index.js +66 -0
- package/dist/plugins/AccessControl/types.js +1 -0
- package/dist/plugins/ForeignInlineListPlugin/index.js +1 -1
- package/dist/servers/express.js +2 -2
- package/dist/spa/spa/src/App.vue +7 -3
- package/dist/spa/spa/src/components/Toast.vue +5 -4
- package/dist/spa/spa/src/components/ValueRenderer.vue +1 -1
- package/dist/spa/spa/src/composables/useStores.ts +2 -1
- package/dist/spa/spa/src/router/index.ts +13 -1
- package/dist/spa/spa/src/stores/core.ts +13 -11
- package/dist/spa/spa/src/stores/user.ts +50 -0
- package/dist/spa/spa/src/utils.ts +8 -3
- package/dist/spa/spa/src/views/CreateView.vue +7 -0
- package/dist/spa/spa/src/views/EditView.vue +8 -1
- package/dist/spa/spa/src/views/ListView.vue +10 -1
- package/dist/types/AdminForthConfig.js +9 -0
- package/dist/types/FrontendAPI.js +4 -4
- package/index.ts +91 -42
- package/modules/codeInjector.ts +0 -1
- package/modules/utils.ts +2 -10
- package/package.json +1 -1
- package/plugins/AccessControl/index.ts +83 -0
- package/plugins/AccessControl/types.ts +14 -0
- package/plugins/ForeignInlineListPlugin/custom/InlineList.vue +11 -0
- package/plugins/ForeignInlineListPlugin/index.ts +2 -1
- package/servers/express.ts +2 -2
- package/spa/src/App.vue +7 -3
- package/spa/src/components/Toast.vue +5 -4
- package/spa/src/components/ValueRenderer.vue +1 -1
- package/spa/src/composables/useStores.ts +2 -1
- package/spa/src/router/index.ts +13 -1
- package/spa/src/stores/core.ts +13 -11
- package/spa/src/stores/user.ts +50 -0
- package/spa/src/utils.ts +8 -3
- package/spa/src/views/CreateView.vue +7 -0
- package/spa/src/views/EditView.vue +8 -1
- package/spa/src/views/ListView.vue +10 -1
- package/types/AdminForthConfig.ts +73 -21
- package/types/FrontendAPI.ts +11 -5
package/dist/spa/spa/src/App.vue
CHANGED
|
@@ -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
|
-
|
|
225
|
-
|
|
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
|
-
|
|
49
|
+
timeout?: number|'unlimited';}
|
|
50
50
|
}>();
|
|
51
51
|
function closeToast() {
|
|
52
52
|
emit('close');
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
onMounted(() => {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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>
|
|
@@ -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
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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["
|
|
4
|
-
AlertVariant["
|
|
5
|
-
AlertVariant["
|
|
6
|
-
AlertVariant["
|
|
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
|
|
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
|
|
14
|
-
|
|
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
|
|
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:
|
|
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
|
|
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
|
-
|
|
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
|
|
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 (
|
|
624
|
+
if (adminUser.isRoot) {
|
|
566
625
|
username = this.config.rootUser.username;
|
|
567
626
|
} else {
|
|
568
|
-
const
|
|
569
|
-
|
|
570
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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' } `);
|
package/modules/codeInjector.ts
CHANGED
|
@@ -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
|
|
38
|
-
|
|
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
|
}
|