adminforth 1.1.23 → 1.1.24
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/dist/index.js +12 -12
- package/dist/plugins/AccessControl/index.js +3 -56
- package/dist/plugins/AuditLog/index.js +13 -0
- package/dist/plugins/AuditLog/types.js +1 -0
- package/dist/plugins/plugins/ForeignInlineListPlugin/custom/InlineList.vue +248 -0
- package/dist/spa/index.html +2 -2
- package/dist/spa/package-lock.json +1 -189
- package/dist/spa/package.json +1 -6
- package/dist/spa/spa/src/components/ShowTableItem.vue +14 -0
- package/dist/spa/spa/src/views/HomeView.vue +8 -0
- package/dist/spa/src/App.vue +57 -76
- package/dist/spa/src/components/AcceptModal.vue +1 -1
- package/dist/spa/src/components/CustomDatePicker.vue +3 -3
- package/dist/spa/src/components/CustomDateRangePicker.vue +3 -3
- package/dist/spa/src/components/Dropdown.vue +4 -13
- package/dist/spa/src/components/Filters.vue +22 -55
- package/dist/spa/src/components/MenuLink.vue +2 -2
- package/dist/spa/src/components/ResourceForm.vue +99 -157
- package/dist/spa/src/components/ValueRenderer.vue +1 -11
- package/dist/spa/src/router/index.ts +2 -3
- package/dist/spa/src/stores/core.ts +33 -22
- package/dist/spa/src/utils.ts +3 -5
- package/dist/spa/src/views/CreateView.vue +7 -15
- package/dist/spa/src/views/EditView.vue +8 -36
- package/dist/spa/src/views/HomeView.vue +8 -0
- package/dist/spa/src/views/ListView.vue +23 -35
- package/dist/spa/src/views/LoginView.vue +1 -1
- package/dist/spa/src/views/ResourceParent.vue +1 -1
- package/dist/spa/src/views/ShowView.vue +9 -20
- package/dist/spa/tailwind.config.js +2 -5
- package/dist/types/AdminForthConfig.js +9 -0
- package/index.ts +16 -18
- package/package.json +1 -1
- package/plugins/AuditLog/index.ts +29 -0
- package/plugins/AuditLog/types.ts +4 -0
- package/types/AdminForthConfig.ts +23 -1
- package/dist/spa/src/components/CustomRangePicker.vue +0 -148
|
@@ -23,12 +23,11 @@
|
|
|
23
23
|
|
|
24
24
|
<ResourceForm
|
|
25
25
|
v-else
|
|
26
|
-
:record="
|
|
26
|
+
:record="coreStore.record"
|
|
27
27
|
:resourceColumns="coreStore.resourceColumns"
|
|
28
28
|
@update:record="onUpdateRecord"
|
|
29
29
|
@update:isValid="isValid = $event"
|
|
30
30
|
:validating="validating"
|
|
31
|
-
:customComponentsPerColumn="editComponentsPerColumn"
|
|
32
31
|
>
|
|
33
32
|
</ResourceForm>
|
|
34
33
|
</div>
|
|
@@ -37,14 +36,14 @@
|
|
|
37
36
|
|
|
38
37
|
<script setup>
|
|
39
38
|
|
|
40
|
-
import
|
|
41
|
-
import
|
|
42
|
-
import SingleSkeletLoader from '@/components/SingleSkeletLoader.vue';
|
|
39
|
+
import { ref, computed, onMounted } from 'vue';
|
|
40
|
+
import { useRoute, useRouter } from 'vue-router';
|
|
43
41
|
import { useCoreStore } from '@/stores/core';
|
|
44
|
-
import
|
|
42
|
+
import ResourceForm from '@/components/ResourceForm.vue';
|
|
43
|
+
import BreadcrumbsWithButtons from '@/components/BreadcrumbsWithButtons.vue';
|
|
45
44
|
import { IconFloppyDiskSolid } from '@iconify-prerendered/vue-flowbite';
|
|
46
|
-
import
|
|
47
|
-
import {
|
|
45
|
+
import SingleSkeletLoader from '@/components/SingleSkeletLoader.vue';
|
|
46
|
+
import { callAdminForthApi } from '@/utils';
|
|
48
47
|
|
|
49
48
|
const coreStore = useCoreStore();
|
|
50
49
|
|
|
@@ -59,24 +58,11 @@ const loading = ref(false);
|
|
|
59
58
|
const saving = ref(false);
|
|
60
59
|
|
|
61
60
|
const record = ref({});
|
|
62
|
-
let editComponentsPerColumn = {};
|
|
63
61
|
|
|
64
62
|
async function onUpdateRecord(newRecord) {
|
|
65
63
|
record.value = newRecord;
|
|
66
64
|
}
|
|
67
65
|
|
|
68
|
-
const editableRecord = computed(() => {
|
|
69
|
-
const newRecord = { ...coreStore.record };
|
|
70
|
-
if (!coreStore.resourceColumns) {
|
|
71
|
-
return {};
|
|
72
|
-
}
|
|
73
|
-
coreStore.resourceColumns.forEach(column => {
|
|
74
|
-
if (column.foreignResource) {
|
|
75
|
-
newRecord[column.name] = newRecord[column.name]?.pk
|
|
76
|
-
}
|
|
77
|
-
});
|
|
78
|
-
return newRecord;
|
|
79
|
-
})
|
|
80
66
|
|
|
81
67
|
onMounted(async () => {
|
|
82
68
|
loading.value = true;
|
|
@@ -89,12 +75,6 @@ onMounted(async () => {
|
|
|
89
75
|
primaryKey: route.params.primaryKey,
|
|
90
76
|
});
|
|
91
77
|
|
|
92
|
-
editComponentsPerColumn = coreStore.resourceColumns.reduce((acc, column) => {
|
|
93
|
-
if (column.component?.edit) {
|
|
94
|
-
acc[column.name] = getCustomComponent(column.component.edit);
|
|
95
|
-
}
|
|
96
|
-
return acc;
|
|
97
|
-
}, {});
|
|
98
78
|
loading.value = false;
|
|
99
79
|
});
|
|
100
80
|
|
|
@@ -107,21 +87,13 @@ async function saveRecord() {
|
|
|
107
87
|
}
|
|
108
88
|
|
|
109
89
|
saving.value = true;
|
|
110
|
-
|
|
111
|
-
const updates = {};
|
|
112
|
-
for (const key in record.value) {
|
|
113
|
-
if (record.value[key] !== coreStore.record[key]) {
|
|
114
|
-
updates[key] = record.value[key];
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
90
|
await callAdminForthApi({
|
|
119
91
|
method: 'POST',
|
|
120
92
|
path: `/update_record`,
|
|
121
93
|
body: {
|
|
122
94
|
resourceId: route.params.resourceId,
|
|
123
95
|
recordId: route.params.primaryKey,
|
|
124
|
-
record:
|
|
96
|
+
record: record.value,
|
|
125
97
|
},
|
|
126
98
|
});
|
|
127
99
|
saving.value = false;
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
</BreadcrumbsWithButtons>
|
|
66
66
|
|
|
67
67
|
<!-- table -->
|
|
68
|
-
<div class="relative overflow-x-auto shadow-
|
|
68
|
+
<div class="relative overflow-x-auto shadow-md sm:rounded-lg dark:shadow-2xl">
|
|
69
69
|
|
|
70
70
|
|
|
71
71
|
<!-- skelet loader -->
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
</div>
|
|
91
91
|
|
|
92
92
|
<table v-else class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400">
|
|
93
|
-
<thead class="text-xs text-
|
|
93
|
+
<thead class="text-xs text-gray-700 bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
|
|
94
94
|
<tr>
|
|
95
95
|
<th scope="col" class="p-4">
|
|
96
96
|
<div v-if="rows && rows.length" class="flex items-center">
|
|
@@ -127,7 +127,7 @@
|
|
|
127
127
|
</tr>
|
|
128
128
|
</thead>
|
|
129
129
|
<tbody>
|
|
130
|
-
<tr v-if="!rows" class="bg-
|
|
130
|
+
<tr v-if="!rows" class="bg-white border-b dark:bg-gray-800 dark:border-gray-700">
|
|
131
131
|
<td :colspan="coreStore.resourceColumns.length + 2">
|
|
132
132
|
|
|
133
133
|
<div role="status"
|
|
@@ -171,7 +171,7 @@
|
|
|
171
171
|
</div>
|
|
172
172
|
</td>
|
|
173
173
|
</tr>
|
|
174
|
-
<tr v-else-if="rows.length === 0" class="bg-
|
|
174
|
+
<tr v-else-if="rows.length === 0" class="bg-white border-b dark:bg-gray-800 dark:border-gray-700">
|
|
175
175
|
<td :colspan="coreStore.resourceColumns.length + 2">
|
|
176
176
|
|
|
177
177
|
<div id="toast-simple"
|
|
@@ -185,7 +185,7 @@
|
|
|
185
185
|
</tr>
|
|
186
186
|
|
|
187
187
|
<tr v-else v-for="(row, rowI) in rows" :key="row.id"
|
|
188
|
-
class="bg-
|
|
188
|
+
class="bg-white border-b dark:bg-gray-800 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600">
|
|
189
189
|
<td class="w-4 p-4">
|
|
190
190
|
<div class="flex items center">
|
|
191
191
|
<input
|
|
@@ -198,12 +198,7 @@
|
|
|
198
198
|
</div>
|
|
199
199
|
</td>
|
|
200
200
|
<td v-for="c in columnsListed" class="px-6 py-4">
|
|
201
|
-
|
|
202
|
-
<component
|
|
203
|
-
:is="listComponentsPerColumn[c.name] || ValueRenderer"
|
|
204
|
-
:column="c"
|
|
205
|
-
:row="row"
|
|
206
|
-
/>
|
|
201
|
+
<ValueRenderer :column="c" :row="row"/>
|
|
207
202
|
</td>
|
|
208
203
|
<td class="flex items-center px-6 py-4">
|
|
209
204
|
|
|
@@ -315,28 +310,29 @@
|
|
|
315
310
|
</template>
|
|
316
311
|
|
|
317
312
|
<script setup>
|
|
313
|
+
import {ref, onMounted, watch, computed} from 'vue';
|
|
314
|
+
import {callAdminForthApi, getIcon} from '@/utils';
|
|
315
|
+
import {useRoute} from 'vue-router';
|
|
316
|
+
import {useCoreStore} from '@/stores/core';
|
|
317
|
+
import {useModalStore} from '@/stores/modal';
|
|
318
318
|
import BreadcrumbsWithButtons from '@/components/BreadcrumbsWithButtons.vue';
|
|
319
|
-
import {
|
|
320
|
-
import { useModalStore } from '@/stores/modal';
|
|
321
|
-
import { callAdminForthApi, getIcon } from '@/utils';
|
|
322
|
-
import { initFlowbite } from 'flowbite';
|
|
323
|
-
import { computed, onMounted, ref, watch } from 'vue';
|
|
324
|
-
import { useRoute } from 'vue-router';
|
|
319
|
+
import {initFlowbite} from 'flowbite'
|
|
325
320
|
|
|
326
321
|
import ValueRenderer from '@/components/ValueRenderer.vue';
|
|
327
|
-
import { getCustomComponent } from '@/utils';
|
|
328
322
|
|
|
329
323
|
import {
|
|
330
|
-
|
|
331
|
-
|
|
324
|
+
IconChevronDoubleLeftOutline,
|
|
325
|
+
IconChevronDoubleRightOutline,
|
|
326
|
+
IconInboxFullSolid,
|
|
327
|
+
IconInboxOutline,
|
|
328
|
+
IconPlusOutline
|
|
332
329
|
} from '@iconify-prerendered/vue-flowbite';
|
|
333
330
|
|
|
334
331
|
import {
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
IconTrashBinSolid
|
|
332
|
+
IconEyeSolid,
|
|
333
|
+
IconTrashBinSolid,
|
|
334
|
+
IconPenSolid,
|
|
335
|
+
IconFilterOutline, IconBanOutline
|
|
340
336
|
} from '@iconify-prerendered/vue-flowbite';
|
|
341
337
|
|
|
342
338
|
import Filters from '@/components/Filters.vue';
|
|
@@ -361,6 +357,7 @@ const totalRows = ref(0);
|
|
|
361
357
|
const allCheckedActions = ref([]);
|
|
362
358
|
const allowedActions = ref({});
|
|
363
359
|
|
|
360
|
+
|
|
364
361
|
const DEFAULT_PAGE_SIZE = 10;
|
|
365
362
|
|
|
366
363
|
const columnsListed = computed(() => coreStore.resourceColumns?.filter(c => c.showIn.includes('list')));
|
|
@@ -379,7 +376,6 @@ async function selectAll(value) {
|
|
|
379
376
|
|
|
380
377
|
const pageSize = computed(() => coreStore.resourceById[route.params.resourceId]?.pageSize || DEFAULT_PAGE_SIZE);
|
|
381
378
|
const totalPages = computed(() => Math.ceil(totalRows.value / pageSize.value));
|
|
382
|
-
let listComponentsPerColumn = {};
|
|
383
379
|
const allFromThisPageChecked = computed(() => {
|
|
384
380
|
if (!rows.value) return false;
|
|
385
381
|
return rows.value.every((r) => checkboxes.value.includes(r.id));
|
|
@@ -423,7 +419,6 @@ async function getList() {
|
|
|
423
419
|
path: '/get_resource_data',
|
|
424
420
|
method: 'POST',
|
|
425
421
|
body: {
|
|
426
|
-
source: 'list',
|
|
427
422
|
resourceId: route.params.resourceId,
|
|
428
423
|
limit: pageSize.value,
|
|
429
424
|
offset: (page.value - 1) * pageSize.value,
|
|
@@ -431,14 +426,7 @@ async function getList() {
|
|
|
431
426
|
sort: sort.value,
|
|
432
427
|
}
|
|
433
428
|
});
|
|
434
|
-
|
|
435
|
-
if (column.component?.list) {
|
|
436
|
-
acc[column.name] = getCustomComponent(column.component.list);
|
|
437
|
-
}
|
|
438
|
-
return acc;
|
|
439
|
-
}, {});
|
|
440
|
-
|
|
441
|
-
fetchStatus.value.pending = false;
|
|
429
|
+
fetchStatus.value.pending = false;
|
|
442
430
|
rows.value = data.data?.map(row => {
|
|
443
431
|
row._primaryKeyValue = row[coreStore.resourceColumns.find(c => c.primaryKey).name];
|
|
444
432
|
return row;
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
<div id="authentication-modal" tabindex="-1" class=" overflow-y-auto overflow-x-hidden z-50 min-w-[400px] justify-center items-center md:inset-0 h-[calc(100%-1rem)] max-h-full">
|
|
12
12
|
<div class="relative p-4 w-full max-w-md max-h-full">
|
|
13
13
|
<!-- Modal content -->
|
|
14
|
-
<div class="relative bg-white rounded-lg shadow dark:bg-gray-700
|
|
14
|
+
<div class="relative bg-white rounded-lg shadow dark:bg-gray-700">
|
|
15
15
|
<!-- Modal header -->
|
|
16
16
|
<div class="flex items-center justify-between p-4 md:p-5 border-b rounded-t dark:border-gray-600">
|
|
17
17
|
<h3 class="text-xl font-semibold text-gray-900 dark:text-white">
|
|
@@ -27,10 +27,10 @@
|
|
|
27
27
|
</div>
|
|
28
28
|
<div
|
|
29
29
|
v-else
|
|
30
|
-
class="relative overflow-x-auto shadow-
|
|
30
|
+
class="relative overflow-x-auto shadow-md sm:rounded-lg"
|
|
31
31
|
>
|
|
32
32
|
<table class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400">
|
|
33
|
-
<thead class="text-xs text-gray-700 uppercase bg-
|
|
33
|
+
<thead class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
|
|
34
34
|
<tr>
|
|
35
35
|
<th scope="col" class="px-6 py-3">
|
|
36
36
|
Field
|
|
@@ -42,17 +42,13 @@
|
|
|
42
42
|
</thead>
|
|
43
43
|
<tbody>
|
|
44
44
|
<tr v-for="column in coreStore.resourceColumns?.filter(c => c.showIn.includes('show'))" :key="column.name"
|
|
45
|
-
class="bg-
|
|
45
|
+
class="odd:bg-white odd:dark:bg-gray-900 even:bg-gray-50 even:dark:bg-gray-800 border-b dark:border-gray-700"
|
|
46
46
|
>
|
|
47
47
|
<td class="px-6 py-4 whitespace-nowrap">
|
|
48
48
|
{{ column.label }}
|
|
49
49
|
</td>
|
|
50
50
|
<td class="px-6 py-4 whitespace-nowrap whitespace-pre-wrap">
|
|
51
|
-
<
|
|
52
|
-
:is="showComponentsPerColumn[column.name] || ValueRenderer"
|
|
53
|
-
:column="column"
|
|
54
|
-
:row="coreStore.record"
|
|
55
|
-
/>
|
|
51
|
+
<ValueRenderer :column="column" :row="coreStore.record" />
|
|
56
52
|
</td>
|
|
57
53
|
</tr>
|
|
58
54
|
|
|
@@ -67,19 +63,18 @@
|
|
|
67
63
|
|
|
68
64
|
<script setup>
|
|
69
65
|
|
|
66
|
+
import { ref, computed, onMounted } from 'vue';
|
|
67
|
+
import { useRoute } from 'vue-router';
|
|
68
|
+
import { callAdminForthApi } from '@/utils';
|
|
70
69
|
import BreadcrumbsWithButtons from '@/components/BreadcrumbsWithButtons.vue';
|
|
71
|
-
import ValueRenderer from '@/components/ValueRenderer.vue';
|
|
72
|
-
import { useCoreStore } from '@/stores/core';
|
|
73
|
-
import { getCustomComponent } from '@/utils';
|
|
74
70
|
import { IconPenSolid, IconTrashBinSolid } from '@iconify-prerendered/vue-flowbite';
|
|
75
|
-
import {
|
|
76
|
-
import
|
|
71
|
+
import { useCoreStore } from '@/stores/core';
|
|
72
|
+
import ValueRenderer from '@/components/ValueRenderer.vue';
|
|
77
73
|
|
|
78
74
|
|
|
79
75
|
const item = ref(null);
|
|
80
76
|
const route = useRoute();
|
|
81
77
|
const loading = ref(false);
|
|
82
|
-
let showComponentsPerColumn = {};
|
|
83
78
|
|
|
84
79
|
const coreStore = useCoreStore();
|
|
85
80
|
|
|
@@ -92,12 +87,6 @@ onMounted(async () => {
|
|
|
92
87
|
resourceId: route.params.resourceId,
|
|
93
88
|
primaryKey: route.params.primaryKey,
|
|
94
89
|
});
|
|
95
|
-
showComponentsPerColumn = coreStore.resourceColumns.reduce((acc, column) => {
|
|
96
|
-
if (column.component?.show) {
|
|
97
|
-
acc[column.name] = getCustomComponent(column.component.show);
|
|
98
|
-
}
|
|
99
|
-
return acc;
|
|
100
|
-
}, {});
|
|
101
90
|
loading.value = false;
|
|
102
91
|
});
|
|
103
92
|
|
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
/** @type {import('tailwindcss').Config} */
|
|
2
2
|
export default {
|
|
3
|
-
content: ["./src/**/*.{vue, js}",
|
|
3
|
+
content: ["./src/**/*.{vue, js}", "./node_modules/flowbite/**/*.js"],
|
|
4
4
|
theme: {
|
|
5
|
-
extend: {
|
|
6
|
-
/* IMPORTANT:ADMINFORTH TAILWIND STYLES */
|
|
7
|
-
}
|
|
5
|
+
extend: {},
|
|
8
6
|
},
|
|
9
|
-
|
|
10
7
|
darkMode: 'class',
|
|
11
8
|
plugins: [
|
|
12
9
|
require('flowbite/plugin'),
|
|
@@ -61,6 +61,15 @@ export var AllowedActionsEnum;
|
|
|
61
61
|
AllowedActionsEnum["delete"] = "delete";
|
|
62
62
|
AllowedActionsEnum["filter"] = "filter";
|
|
63
63
|
})(AllowedActionsEnum || (AllowedActionsEnum = {}));
|
|
64
|
+
export var ActionCheckSource;
|
|
65
|
+
(function (ActionCheckSource) {
|
|
66
|
+
ActionCheckSource["DisplayButtons"] = "displayButtons";
|
|
67
|
+
ActionCheckSource["ListRequest"] = "listRequest";
|
|
68
|
+
ActionCheckSource["ShowRequest"] = "showRequest";
|
|
69
|
+
ActionCheckSource["EditRequest"] = "editRequest";
|
|
70
|
+
ActionCheckSource["CreateRequest"] = "createRequest";
|
|
71
|
+
ActionCheckSource["DeleteRequest"] = "deleteRequest";
|
|
72
|
+
})(ActionCheckSource || (ActionCheckSource = {}));
|
|
64
73
|
export var AdminForthDataTypes;
|
|
65
74
|
(function (AdminForthDataTypes) {
|
|
66
75
|
AdminForthDataTypes["STRING"] = "string";
|
package/index.ts
CHANGED
|
@@ -16,7 +16,8 @@ import { AdminForthConfig, AdminForthClass, AdminForthComponentDeclaration, Admi
|
|
|
16
16
|
BeforeDataSourceRequestFunction,
|
|
17
17
|
AfterDataSourceResponseFunction, AllowedActionValue, AdminUser,
|
|
18
18
|
AdminForthResource,
|
|
19
|
-
AllowedActionsEnum
|
|
19
|
+
AllowedActionsEnum,
|
|
20
|
+
ActionCheckSource
|
|
20
21
|
} from './types/AdminForthConfig.js';
|
|
21
22
|
import path from 'path';
|
|
22
23
|
import AdminForthPlugin from './plugins/base.js';
|
|
@@ -696,10 +697,6 @@ class AdminForth implements AdminForthClass {
|
|
|
696
697
|
}
|
|
697
698
|
newMenu.push(newMenuItem)
|
|
698
699
|
}
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
700
|
|
|
704
701
|
return {
|
|
705
702
|
user: userData,
|
|
@@ -724,13 +721,13 @@ class AdminForth implements AdminForthClass {
|
|
|
724
721
|
},
|
|
725
722
|
});
|
|
726
723
|
|
|
727
|
-
async function interpretResource(adminUser: AdminUser, resource: AdminForthResource, meta: any) {
|
|
724
|
+
async function interpretResource(adminUser: AdminUser, resource: AdminForthResource, meta: any, source: ActionCheckSource) {
|
|
728
725
|
await Promise.all(
|
|
729
726
|
Object.entries(resource.options?.allowedActions || {}).map(
|
|
730
727
|
async ([key, value]: [string, AllowedActionValue]) => {
|
|
731
728
|
// if callable then call
|
|
732
729
|
if (typeof value === 'function') {
|
|
733
|
-
resource.options.allowedActions[key] = await value( adminUser, resource, meta );
|
|
730
|
+
resource.options.allowedActions[key] = await value({ adminUser, resource, meta, source });
|
|
734
731
|
}
|
|
735
732
|
})
|
|
736
733
|
);
|
|
@@ -761,7 +758,7 @@ class AdminForth implements AdminForthClass {
|
|
|
761
758
|
return { error: `Resource ${resourceId} not found` };
|
|
762
759
|
}
|
|
763
760
|
|
|
764
|
-
await interpretResource(adminUser, resource, {});
|
|
761
|
+
await interpretResource(adminUser, resource, {}, ActionCheckSource.DisplayButtons);
|
|
765
762
|
|
|
766
763
|
// exclude "plugins" key
|
|
767
764
|
return { resource: { ...resource, plugins: undefined } };
|
|
@@ -786,7 +783,7 @@ class AdminForth implements AdminForthClass {
|
|
|
786
783
|
return { error: `Resource ${resourceId} not found` };
|
|
787
784
|
}
|
|
788
785
|
|
|
789
|
-
await interpretResource(adminUser, resource, { requestBody: body });
|
|
786
|
+
await interpretResource(adminUser, resource, { requestBody: body }, ActionCheckSource.DisplayButtons);
|
|
790
787
|
|
|
791
788
|
const { allowed, error } = checkAccess(source as AllowedActionsEnum, resource);
|
|
792
789
|
console.log('allowed', allowed, error);
|
|
@@ -1011,7 +1008,7 @@ class AdminForth implements AdminForthClass {
|
|
|
1011
1008
|
if (!resource) {
|
|
1012
1009
|
return { error: `Resource '${body['resourceId']}' not found` };
|
|
1013
1010
|
}
|
|
1014
|
-
await interpretResource(adminUser, resource, { requestBody: body
|
|
1011
|
+
await interpretResource(adminUser, resource, { requestBody: body}, ActionCheckSource.CreateRequest);
|
|
1015
1012
|
|
|
1016
1013
|
const { allowed, error } = checkAccess(AllowedActionsEnum.create, resource);
|
|
1017
1014
|
if (!allowed) {
|
|
@@ -1092,13 +1089,7 @@ class AdminForth implements AdminForthClass {
|
|
|
1092
1089
|
if (!resource) {
|
|
1093
1090
|
return { error: `Resource '${body['resourceId']}' not found` };
|
|
1094
1091
|
}
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
const { allowed, error } = checkAccess(AllowedActionsEnum.edit, resource);
|
|
1098
|
-
if (!allowed) {
|
|
1099
|
-
return { error };
|
|
1100
|
-
}
|
|
1101
|
-
|
|
1092
|
+
|
|
1102
1093
|
const recordId = body['recordId'];
|
|
1103
1094
|
const connector = this.connectors[resource.dataSource];
|
|
1104
1095
|
const oldRecord = await connector.getRecordByPrimaryKey(resource, recordId)
|
|
@@ -1108,6 +1099,13 @@ class AdminForth implements AdminForthClass {
|
|
|
1108
1099
|
}
|
|
1109
1100
|
const record = body['record'];
|
|
1110
1101
|
|
|
1102
|
+
await interpretResource(adminUser, resource, { requestBody: body, record, oldRecord}, ActionCheckSource.EditRequest);
|
|
1103
|
+
|
|
1104
|
+
const { allowed, error } = checkAccess(AllowedActionsEnum.edit, resource);
|
|
1105
|
+
if (!allowed) {
|
|
1106
|
+
return { error };
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1111
1109
|
// execute hook if needed
|
|
1112
1110
|
for (const hook of listify(resource.hooks?.edit?.beforeSave as BeforeSaveFunction[])) {
|
|
1113
1111
|
const resp = await hook({ resource, record, adminUser });
|
|
@@ -1172,7 +1170,7 @@ class AdminForth implements AdminForthClass {
|
|
|
1172
1170
|
return { error: `Resource '${resource.resourceId}' does not allow delete action` };
|
|
1173
1171
|
}
|
|
1174
1172
|
|
|
1175
|
-
await interpretResource(adminUser, resource, { requestBody: body });
|
|
1173
|
+
await interpretResource(adminUser, resource, { requestBody: body }, ActionCheckSource.DeleteRequest);
|
|
1176
1174
|
|
|
1177
1175
|
const { allowed, error } = checkAccess(AllowedActionsEnum.delete, resource);
|
|
1178
1176
|
if (!allowed) {
|
package/package.json
CHANGED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AdminForthResource, AdminForthClass, BeforeDataSourceRequestFunction,
|
|
3
|
+
AllowedActionsEnum,
|
|
4
|
+
BeforeSaveFunction
|
|
5
|
+
} from "../../types/AdminForthConfig.js";
|
|
6
|
+
import AdminForthPlugin from "../base.js";
|
|
7
|
+
import { PluginOptions } from "./types.js";
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
export default class AuditLogPlugin extends AdminForthPlugin {
|
|
12
|
+
|
|
13
|
+
options: PluginOptions;
|
|
14
|
+
adminforth: AdminForthClass;
|
|
15
|
+
|
|
16
|
+
constructor(options: PluginOptions) {
|
|
17
|
+
super(options, import.meta.url);
|
|
18
|
+
this.options = options;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
static defaultError = 'Sorry, you do not have access to this resource.'
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
modifyResourceConfig(adminforth: AdminForthClass, resourceConfig: AdminForthResource) {
|
|
25
|
+
super.modifyResourceConfig(adminforth, resourceConfig);
|
|
26
|
+
this.adminforth = adminforth;
|
|
27
|
+
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -838,7 +838,29 @@ export enum AllowedActionsEnum {
|
|
|
838
838
|
* or function which returns Boolean or string with error message
|
|
839
839
|
*
|
|
840
840
|
*/
|
|
841
|
-
export type AllowedActionValue = boolean | ((adminUser
|
|
841
|
+
export type AllowedActionValue = boolean | (({adminUser, resource, meta, source}: {
|
|
842
|
+
adminUser: AdminUser,
|
|
843
|
+
resource: AdminForthResource,
|
|
844
|
+
|
|
845
|
+
/**
|
|
846
|
+
* Meta object which will pass request information just in case
|
|
847
|
+
*/
|
|
848
|
+
meta: any,
|
|
849
|
+
|
|
850
|
+
/**
|
|
851
|
+
* Source of the check
|
|
852
|
+
*/
|
|
853
|
+
source: ActionCheckSource,
|
|
854
|
+
}) => Promise<boolean | string>);
|
|
855
|
+
|
|
856
|
+
export enum ActionCheckSource {
|
|
857
|
+
DisplayButtons = 'displayButtons',
|
|
858
|
+
ListRequest = 'listRequest',
|
|
859
|
+
ShowRequest = 'showRequest',
|
|
860
|
+
EditRequest = 'editRequest',
|
|
861
|
+
CreateRequest = 'createRequest',
|
|
862
|
+
DeleteRequest = 'deleteRequest',
|
|
863
|
+
}
|
|
842
864
|
|
|
843
865
|
/**
|
|
844
866
|
* Object which describes allowed actions for user.
|