adminforth 1.1.58 → 1.1.60
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 +68 -63
- package/dist/modules/styleGenerator.js +22 -18
- package/dist/modules/styles.js +44 -36
- package/dist/modules/utils.js +74 -0
- package/dist/plugins/AccessControl/index.js +66 -0
- package/dist/plugins/AccessControl/types.js +1 -0
- package/dist/plugins/AuditLogPlugin/custom/AuditLogView.vue +20 -0
- package/dist/plugins/AuditLogPlugin/index.js +83 -0
- package/dist/plugins/AuditLogPlugin/types.js +1 -0
- package/dist/spa/index.html +2 -2
- package/dist/spa/package-lock.json +4 -17
- package/dist/spa/package.json +1 -2
- package/dist/spa/public/favicon.ico +0 -0
- package/dist/spa/spa/package-lock.json +111 -0
- package/dist/spa/spa/package.json +1 -0
- package/dist/spa/spa/public/favicon.ico +0 -0
- package/dist/spa/spa/src/App.vue +4 -4
- package/dist/spa/spa/src/components/MenuLink.vue +1 -1
- package/dist/spa/spa/src/components/ResourceForm.vue +1 -1
- package/dist/spa/spa/src/composables/useStores.ts +7 -1
- package/dist/spa/src/App.vue +45 -125
- package/dist/spa/src/assets/logo.svg +1 -19
- package/dist/spa/src/components/AcceptModal.vue +9 -2
- package/dist/spa/src/components/BreadcrumbsWithButtons.vue +1 -1
- package/dist/spa/src/components/CustomDatePicker.vue +3 -16
- package/dist/spa/src/components/CustomDateRangePicker.vue +2 -11
- package/dist/spa/src/components/Dropdown.vue +1 -6
- package/dist/spa/src/components/Filters.vue +19 -40
- package/dist/spa/src/components/ResourceForm.vue +24 -26
- package/dist/spa/src/components/ValueRenderer.vue +8 -14
- package/dist/spa/src/main.ts +1 -1
- package/dist/spa/src/router/index.ts +20 -30
- package/dist/spa/src/stores/core.ts +31 -48
- package/dist/spa/src/stores/modal.ts +3 -13
- package/dist/spa/src/utils.ts +7 -62
- package/dist/spa/src/views/CreateView.vue +15 -67
- package/dist/spa/src/views/EditView.vue +11 -45
- package/dist/spa/src/views/ListView.vue +366 -82
- package/dist/spa/src/views/LoginView.vue +4 -16
- package/dist/spa/src/views/ShowView.vue +21 -105
- package/dist/spa/tailwind.config.js +1 -1
- package/dist/spa/vite.config.ts +0 -6
- package/dist/types.js +30 -0
- package/index.ts +62 -61
- package/modules/styleGenerator.ts +25 -18
- package/modules/styles.ts +46 -36
- package/modules/utils.ts +94 -1
- package/package.json +2 -1
- package/plugins/AuditLogPlugin/custom/AuditLogView.vue +20 -0
- package/plugins/AuditLogPlugin/index.ts +99 -0
- package/plugins/AuditLogPlugin/types.ts +40 -0
- package/spa/package-lock.json +111 -0
- package/spa/package.json +1 -0
- package/spa/src/App.vue +4 -4
- package/spa/src/components/MenuLink.vue +1 -1
- package/spa/src/components/ResourceForm.vue +1 -1
- package/spa/src/composables/useStores.ts +7 -1
- package/types/AdminForthConfig.ts +6 -1
- package/dist/spa/src/components/ResourceListTable.vue +0 -419
- package/dist/spa/src/components/Toast.vue +0 -66
- package/dist/spa/src/composables/useFrontendApi.ts +0 -26
- package/dist/spa/src/composables/useStores.ts +0 -113
- package/dist/spa/src/spa_types/core.ts +0 -51
- package/dist/spa/src/stores/filters.ts +0 -22
- package/dist/spa/src/stores/toast.ts +0 -15
- package/dist/spa/src/stores/user.ts +0 -54
- package/plugins/AuditLog/index.ts +0 -29
- package/plugins/AuditLog/types.ts +0 -4
- /package/dist/spa/{public/assets → spa/public}/favicon.png +0 -0
package/dist/index.js
CHANGED
|
@@ -23,7 +23,7 @@ import ExpressServer from './servers/express.js';
|
|
|
23
23
|
import { v1 as uuid } from 'uuid';
|
|
24
24
|
import fs from 'fs';
|
|
25
25
|
import { ADMINFORTH_VERSION, listify } from './modules/utils.js';
|
|
26
|
-
import { AdminForthFilterOperators, AdminForthDataTypes, AdminForthResourcePages, AllowedActionsEnum, ActionCheckSource } from './types/AdminForthConfig.js';
|
|
26
|
+
import { AdminForthFilterOperators, AdminForthDataTypes, AdminForthResourcePages, AllowedActionsEnum, ActionCheckSource, } from './types/AdminForthConfig.js';
|
|
27
27
|
import path from 'path';
|
|
28
28
|
//get array from enum AdminForthResourcePages
|
|
29
29
|
class AdminForth {
|
|
@@ -473,6 +473,63 @@ class AdminForth {
|
|
|
473
473
|
return users.data[0] || null;
|
|
474
474
|
});
|
|
475
475
|
}
|
|
476
|
+
createResourceRecord(_b) {
|
|
477
|
+
return __awaiter(this, arguments, void 0, function* ({ resource, record, adminUser }) {
|
|
478
|
+
var _c, _d, _e, _f, _g;
|
|
479
|
+
for (const column of resource.columns) {
|
|
480
|
+
if (column.fillOnCreate) {
|
|
481
|
+
if (record[column.name] === undefined) {
|
|
482
|
+
record[column.name] = column.fillOnCreate({
|
|
483
|
+
initialRecord: record, adminUser
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
if (((_c = column.required) === null || _c === void 0 ? void 0 : _c.create) && record[column.name] === undefined) {
|
|
488
|
+
return { error: `Column '${column.name}' is required` };
|
|
489
|
+
}
|
|
490
|
+
if (column.isUnique) {
|
|
491
|
+
const existingRecord = yield this.connectors[resource.dataSource].getData({
|
|
492
|
+
resource,
|
|
493
|
+
filters: [{ field: column.name, operator: AdminForthFilterOperators.EQ, value: record[column.name] }],
|
|
494
|
+
limit: 1,
|
|
495
|
+
sort: [],
|
|
496
|
+
offset: 0
|
|
497
|
+
});
|
|
498
|
+
if (existingRecord.data.length > 0) {
|
|
499
|
+
return { error: `Record with ${column.name} ${record[column.name]} already exists` };
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
// execute hook if needed
|
|
504
|
+
for (const hook of listify((_e = (_d = resource.hooks) === null || _d === void 0 ? void 0 : _d.create) === null || _e === void 0 ? void 0 : _e.beforeSave)) {
|
|
505
|
+
const resp = yield hook({ resource, record, adminUser });
|
|
506
|
+
if (!resp || (!resp.ok && !resp.error)) {
|
|
507
|
+
throw new Error(`Hook beforeSave must return object with {ok: true} or { error: 'Error' } `);
|
|
508
|
+
}
|
|
509
|
+
if (resp.error) {
|
|
510
|
+
return { error: resp.error };
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
// remove virtual columns from record
|
|
514
|
+
for (const column of resource.columns.filter((col) => col.virtual)) {
|
|
515
|
+
if (record[column.name]) {
|
|
516
|
+
delete record[column.name];
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
const connector = this.connectors[resource.dataSource];
|
|
520
|
+
yield connector.createRecord({ resource, record });
|
|
521
|
+
// execute hook if needed
|
|
522
|
+
for (const hook of listify((_g = (_f = resource.hooks) === null || _f === void 0 ? void 0 : _f.create) === null || _g === void 0 ? void 0 : _g.afterSave)) {
|
|
523
|
+
const resp = yield hook({ resource, record, adminUser });
|
|
524
|
+
if (!resp || (!resp.ok && !resp.error)) {
|
|
525
|
+
throw new Error(`Hook afterSave must return object with {ok: true} or { error: 'Error' } `);
|
|
526
|
+
}
|
|
527
|
+
if (resp.error) {
|
|
528
|
+
return { error: resp.error };
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
});
|
|
532
|
+
}
|
|
476
533
|
setupEndpoints(server) {
|
|
477
534
|
server.endpoint({
|
|
478
535
|
noAuth: true,
|
|
@@ -904,7 +961,6 @@ class AdminForth {
|
|
|
904
961
|
method: 'POST',
|
|
905
962
|
path: '/create_record',
|
|
906
963
|
handler: (_y) => __awaiter(this, [_y], void 0, function* ({ body, adminUser }) {
|
|
907
|
-
var _z, _0, _1, _2, _3;
|
|
908
964
|
const resource = this.config.resources.find((res) => res.resourceId == body['resourceId']);
|
|
909
965
|
if (!resource) {
|
|
910
966
|
return { error: `Resource '${body['resourceId']}' not found` };
|
|
@@ -914,59 +970,8 @@ class AdminForth {
|
|
|
914
970
|
if (!allowed) {
|
|
915
971
|
return { error };
|
|
916
972
|
}
|
|
917
|
-
|
|
918
|
-
// execute hook if needed
|
|
919
|
-
for (const hook of listify((_0 = (_z = resource.hooks) === null || _z === void 0 ? void 0 : _z.create) === null || _0 === void 0 ? void 0 : _0.beforeSave)) {
|
|
920
|
-
const resp = yield hook({ resource, record, adminUser });
|
|
921
|
-
if (!resp || (!resp.ok && !resp.error)) {
|
|
922
|
-
throw new Error(`Hook beforeSave must return object with {ok: true} or { error: 'Error' } `);
|
|
923
|
-
}
|
|
924
|
-
if (resp.error) {
|
|
925
|
-
return { error: resp.error };
|
|
926
|
-
}
|
|
927
|
-
}
|
|
928
|
-
for (const column of resource.columns) {
|
|
929
|
-
if (column.fillOnCreate) {
|
|
930
|
-
if (body['record'][column.name] === undefined) {
|
|
931
|
-
body['record'][column.name] = column.fillOnCreate({
|
|
932
|
-
initialRecord: body['record'], adminUser
|
|
933
|
-
});
|
|
934
|
-
}
|
|
935
|
-
}
|
|
936
|
-
if (((_1 = column.required) === null || _1 === void 0 ? void 0 : _1.create) && body['record'][column.name] === undefined) {
|
|
937
|
-
return { error: `Column '${column.name}' is required` };
|
|
938
|
-
}
|
|
939
|
-
if (column.isUnique) {
|
|
940
|
-
const existingRecord = yield this.connectors[resource.dataSource].getData({
|
|
941
|
-
resource,
|
|
942
|
-
filters: [{ field: column.name, operator: AdminForthFilterOperators.EQ, value: body['record'][column.name] }],
|
|
943
|
-
limit: 1,
|
|
944
|
-
sort: [],
|
|
945
|
-
offset: 0
|
|
946
|
-
});
|
|
947
|
-
if (existingRecord.data.length > 0) {
|
|
948
|
-
return { error: `Record with ${column.name} ${body['record'][column.name]} already exists` };
|
|
949
|
-
}
|
|
950
|
-
}
|
|
951
|
-
}
|
|
952
|
-
// remove virtual columns from record
|
|
953
|
-
for (const column of resource.columns.filter((col) => col.virtual)) {
|
|
954
|
-
if (record[column.name]) {
|
|
955
|
-
delete record[column.name];
|
|
956
|
-
}
|
|
957
|
-
}
|
|
973
|
+
yield this.createResourceRecord({ resource, record: body['record'], adminUser });
|
|
958
974
|
const connector = this.connectors[resource.dataSource];
|
|
959
|
-
yield connector.createRecord({ resource, record });
|
|
960
|
-
// execute hook if needed
|
|
961
|
-
for (const hook of listify((_3 = (_2 = resource.hooks) === null || _2 === void 0 ? void 0 : _2.create) === null || _3 === void 0 ? void 0 : _3.afterSave)) {
|
|
962
|
-
const resp = yield hook({ resource, record, adminUser });
|
|
963
|
-
if (!resp || (!resp.ok && !resp.error)) {
|
|
964
|
-
throw new Error(`Hook afterSave must return object with {ok: true} or { error: 'Error' } `);
|
|
965
|
-
}
|
|
966
|
-
if (resp.error) {
|
|
967
|
-
return { error: resp.error };
|
|
968
|
-
}
|
|
969
|
-
}
|
|
970
975
|
return {
|
|
971
976
|
newRecordId: body['record'][connector.getPrimaryKey(resource)]
|
|
972
977
|
};
|
|
@@ -975,8 +980,8 @@ class AdminForth {
|
|
|
975
980
|
server.endpoint({
|
|
976
981
|
method: 'POST',
|
|
977
982
|
path: '/update_record',
|
|
978
|
-
handler: (
|
|
979
|
-
var
|
|
983
|
+
handler: (_z) => __awaiter(this, [_z], void 0, function* ({ body, adminUser }) {
|
|
984
|
+
var _0, _1, _2, _3;
|
|
980
985
|
const resource = this.config.resources.find((res) => res.resourceId == body['resourceId']);
|
|
981
986
|
if (!resource) {
|
|
982
987
|
return { error: `Resource '${body['resourceId']}' not found` };
|
|
@@ -995,7 +1000,7 @@ class AdminForth {
|
|
|
995
1000
|
return { error };
|
|
996
1001
|
}
|
|
997
1002
|
// execute hook if needed
|
|
998
|
-
for (const hook of listify((
|
|
1003
|
+
for (const hook of listify((_1 = (_0 = resource.hooks) === null || _0 === void 0 ? void 0 : _0.edit) === null || _1 === void 0 ? void 0 : _1.beforeSave)) {
|
|
999
1004
|
const resp = yield hook({ resource, record, adminUser });
|
|
1000
1005
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
1001
1006
|
throw new Error(`Hook beforeSave must return object with {ok: true} or { error: 'Error' } `);
|
|
@@ -1022,7 +1027,7 @@ class AdminForth {
|
|
|
1022
1027
|
yield connector.updateRecord({ resource, recordId, record, newValues });
|
|
1023
1028
|
}
|
|
1024
1029
|
// execute hook if needed
|
|
1025
|
-
for (const hook of listify((
|
|
1030
|
+
for (const hook of listify((_3 = (_2 = resource.hooks) === null || _2 === void 0 ? void 0 : _2.edit) === null || _3 === void 0 ? void 0 : _3.afterSave)) {
|
|
1026
1031
|
const resp = yield hook({ resource, record, adminUser });
|
|
1027
1032
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
1028
1033
|
throw new Error(`Hook afterSave must return object with {ok: true} or { error: 'Error' } `);
|
|
@@ -1039,8 +1044,8 @@ class AdminForth {
|
|
|
1039
1044
|
server.endpoint({
|
|
1040
1045
|
method: 'POST',
|
|
1041
1046
|
path: '/delete_record',
|
|
1042
|
-
handler: (
|
|
1043
|
-
var
|
|
1047
|
+
handler: (_4) => __awaiter(this, [_4], void 0, function* ({ body, adminUser }) {
|
|
1048
|
+
var _5, _6, _7, _8;
|
|
1044
1049
|
const resource = this.config.resources.find((res) => res.resourceId == body['resourceId']);
|
|
1045
1050
|
const record = yield this.connectors[resource.dataSource].getRecordByPrimaryKey(resource, body['primaryKey']);
|
|
1046
1051
|
if (!resource) {
|
|
@@ -1058,7 +1063,7 @@ class AdminForth {
|
|
|
1058
1063
|
return { error };
|
|
1059
1064
|
}
|
|
1060
1065
|
// execute hook if needed
|
|
1061
|
-
for (const hook of listify((
|
|
1066
|
+
for (const hook of listify((_6 = (_5 = resource.hooks) === null || _5 === void 0 ? void 0 : _5.delete) === null || _6 === void 0 ? void 0 : _6.beforeSave)) {
|
|
1062
1067
|
const resp = yield hook({ resource, record, adminUser });
|
|
1063
1068
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
1064
1069
|
throw new Error(`Hook beforeSave must return object with {ok: true} or { error: 'Error' } `);
|
|
@@ -1070,7 +1075,7 @@ class AdminForth {
|
|
|
1070
1075
|
const connector = this.connectors[resource.dataSource];
|
|
1071
1076
|
yield connector.deleteRecord({ resource, recordId: body['primaryKey'] });
|
|
1072
1077
|
// execute hook if needed
|
|
1073
|
-
for (const hook of listify((
|
|
1078
|
+
for (const hook of listify((_8 = (_7 = resource.hooks) === null || _7 === void 0 ? void 0 : _7.delete) === null || _8 === void 0 ? void 0 : _8.afterSave)) {
|
|
1074
1079
|
const resp = yield hook({ resource, record, adminUser });
|
|
1075
1080
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
1076
1081
|
throw new Error(`Hook afterSave must return object with {ok: true} or { error: 'Error' } `);
|
|
@@ -1087,7 +1092,7 @@ class AdminForth {
|
|
|
1087
1092
|
server.endpoint({
|
|
1088
1093
|
method: 'POST',
|
|
1089
1094
|
path: '/start_bulk_action',
|
|
1090
|
-
handler: (
|
|
1095
|
+
handler: (_9) => __awaiter(this, [_9], void 0, function* ({ body }) {
|
|
1091
1096
|
const { resourceId, actionId, recordIds } = body;
|
|
1092
1097
|
const resource = this.config.resources.find((res) => res.resourceId == resourceId);
|
|
1093
1098
|
if (!resource) {
|
|
@@ -1,11 +1,7 @@
|
|
|
1
|
-
import { transformObject, deepMerge } from "./utils.js";
|
|
1
|
+
import { transformObject, deepMerge, createRGBA, parseColorForAliases, darkenRGBA, lightenRGBA } from "./utils.js";
|
|
2
2
|
import { styles } from "./styles.js";
|
|
3
|
-
const aliasObject = { lightSidebarText: 'lightSidebarIcons', darkSidebarText: 'darkSidebarIcons', lightSidebarTextHover: 'lightSidebarIconsHover', darkSidebarTextHover: 'darkSidebarIconsHover' };
|
|
4
3
|
export class StylesGenerator {
|
|
5
4
|
constructor(styleConfig) {
|
|
6
|
-
if (!styleConfig) {
|
|
7
|
-
console.log("No styles provided using default styles.");
|
|
8
|
-
}
|
|
9
5
|
this.styleConfig = styleConfig;
|
|
10
6
|
this.defaultStyles = styles();
|
|
11
7
|
}
|
|
@@ -18,22 +14,30 @@ export class StylesGenerator {
|
|
|
18
14
|
}
|
|
19
15
|
return plainCustomStyles;
|
|
20
16
|
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
17
|
+
changeAlias(str, mergedStyles) {
|
|
18
|
+
const { aliasMatch, opacityMatch, darkenMatch, lightenMatch } = parseColorForAliases(str);
|
|
19
|
+
if (!aliasMatch) {
|
|
20
|
+
return str;
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
const alias = aliasMatch[1];
|
|
24
|
+
let opacity = opacityMatch ? parseFloat(opacityMatch[1]) : 1;
|
|
25
|
+
const color = mergedStyles[alias];
|
|
26
|
+
if (darkenMatch) {
|
|
27
|
+
return darkenRGBA(createRGBA(color, opacity));
|
|
28
|
+
}
|
|
29
|
+
if (lightenMatch) {
|
|
30
|
+
return lightenRGBA(createRGBA(color, opacity));
|
|
31
|
+
}
|
|
32
|
+
return createRGBA(color, opacity);
|
|
24
33
|
}
|
|
25
|
-
let colors = plateStyles.colors;
|
|
26
|
-
Object.keys(aliasObject).forEach((aliasName) => {
|
|
27
|
-
if (colors[aliasObject[aliasName]])
|
|
28
|
-
return;
|
|
29
|
-
if (!colors[aliasName])
|
|
30
|
-
return;
|
|
31
|
-
colors[aliasObject[aliasName]] = colors[aliasName];
|
|
32
|
-
});
|
|
33
|
-
return plateStyles;
|
|
34
34
|
}
|
|
35
35
|
mergeStyles() {
|
|
36
|
-
let mergedStyles = deepMerge(this.defaultStyles, this.
|
|
36
|
+
let mergedStyles = deepMerge(this.defaultStyles, this.generatePlainStyles(this.styleConfig));
|
|
37
|
+
let colors = mergedStyles.colors;
|
|
38
|
+
Object.entries(colors).forEach(([key, value]) => {
|
|
39
|
+
colors[key] = this.changeAlias(value, colors);
|
|
40
|
+
});
|
|
37
41
|
return mergedStyles;
|
|
38
42
|
}
|
|
39
43
|
}
|
package/dist/modules/styles.js
CHANGED
|
@@ -2,26 +2,22 @@ export const styles = () => ({
|
|
|
2
2
|
colors: {
|
|
3
3
|
lightHtml: "#FFFFFF", // main background
|
|
4
4
|
lightNavbar: "#FFFFFF", // navbar background
|
|
5
|
-
lightNavbarHover: "#D3D3D3", // in development
|
|
6
|
-
lightNavbarText: "#333333", // in development
|
|
7
|
-
lightNavbarTextHover: "#333333", // in development
|
|
8
|
-
lightNavbarIcons: "#333333", // in development
|
|
9
|
-
lightNavbarIconsHover: "#333333", // in development
|
|
10
|
-
lightNavbarLogo: "#333333", // in development
|
|
11
5
|
lightNavbarBorder: "rgb(229 231 235)", // border
|
|
6
|
+
lightNavbarText: "#111827", // navbar text
|
|
7
|
+
lightNavbarTextHover: "alias:lightNavbarText darken", // navbar text hover
|
|
8
|
+
lightNavbarTextActive: "alias:lightNavbarText darken", // navbar text active
|
|
9
|
+
lightNavbarIcons: "alias:lightNavbarText opacity:0.7", // navbar icons
|
|
12
10
|
lightSidebar: "#ffffff", // sidebar background
|
|
13
|
-
lightSidebarBorder: "
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
lightSidebarIconsHover: "#333333", // sidebar list item icons hover
|
|
24
|
-
lightSidebarHeading: "#333333", // sidebar heading
|
|
11
|
+
lightSidebarBorder: "alias:lightSidebarText opacity:0.3", // sidebar right border
|
|
12
|
+
lightSidebarItemHover: "alias:lightSidebarText opacity:0.1", // sidebar list item hover
|
|
13
|
+
lightSidebarItemActive: "alias:lightSidebarText opacity:0.4", // sidebar list item active
|
|
14
|
+
lightSidebarText: "#1f2937", // sidebar list item text
|
|
15
|
+
lightSidebarTextHover: "alias:lightSidebarText darken", // sidebar list item text hover
|
|
16
|
+
lightSidebarTextActive: "alias:lightSidebarText darken", // sidebar list item text active
|
|
17
|
+
lightSidebarDevider: "alias:lightSidebarText opacity:0.3", // sidebar devider
|
|
18
|
+
lightSidebarIcons: "alias:lightSidebarText opacity:0.7", // sidebar list item icons
|
|
19
|
+
lightSidebarIconsHover: "alias:lightSidebarText", // sidebar list item icons hover
|
|
20
|
+
lightSidebarHeading: "alias:lightSidebarText opacity:0.3", // sidebar heading
|
|
25
21
|
lightList: "#FFFFFF", // list view background
|
|
26
22
|
lightListTable: "#FFFFFF", // list view table background
|
|
27
23
|
lightListTableHeading: "rgb(249 250 251)", // list view table heading
|
|
@@ -30,33 +26,45 @@ export const styles = () => ({
|
|
|
30
26
|
lightListTableRowHover: "rgb(249 250 251)", // list view row hover
|
|
31
27
|
lightListBreadcrumbsText: "#666666", // list view breadcrumbs text
|
|
32
28
|
lightListBorder: "#DDDDDD", // list view rows border
|
|
29
|
+
// lightListButton: "#FFFFFF", // list view button
|
|
30
|
+
// lightListButtonBorder: "#d1d5db", // list view button border
|
|
31
|
+
// lightListButtonHover: "#f3f4f6", // list view button hover
|
|
32
|
+
// lightListButtonBorderHover: "#f3f4f6", // list view button border hover
|
|
33
|
+
// lightListButtonActive: "#f3f4f6", // list view button active
|
|
34
|
+
// lightListButtonDisabled: "#f3f4f6", // list view button disabled
|
|
35
|
+
// lightListButtonDisabledText: "#f3f4f6", // list view button disabled text
|
|
36
|
+
// lightListButtonIcon: "#333333", // list view button icon
|
|
33
37
|
lightForm: "#FFFFFF", // show view background
|
|
34
38
|
lightFormBorder: "#F5F5F5", // show view rows border
|
|
35
39
|
lightFormHeading: "rgb(249 250 251)", // show view heading
|
|
40
|
+
lightButtons: "#FFFFFF", // button background
|
|
41
|
+
lightButtonsBorder: "#DDDDDD", // button border
|
|
42
|
+
lightButtonsText: "#111827", // button text
|
|
43
|
+
lightButtonsHover: "#f3f4f6", // button hover
|
|
44
|
+
lightButtonsBorderHover: "#f3f4f6", // button border hover
|
|
45
|
+
lightButtonsActive: "#f3f4f6", // button active
|
|
46
|
+
lightButtonsDisabled: "#f3f4f6", // button disabled
|
|
47
|
+
lightButtonsDisabledText: "#f3f4f6", // button disabled text
|
|
48
|
+
lightButtonsIcon: "#333333", // button icon
|
|
36
49
|
// colors for dark theme
|
|
37
50
|
darkHtml: "#111827",
|
|
38
51
|
darkNavbar: "#111827",
|
|
39
|
-
darkNavbarHover: "#444444",
|
|
40
|
-
darkNavbarText: "#FFFFFF",
|
|
41
|
-
darkNavbarTextHover: "#FFFFFF",
|
|
42
|
-
darkNavbarIcons: "#FFFFFF",
|
|
43
|
-
darkNavbarIconsHover: "#FFFFFF",
|
|
44
|
-
darkNavbarLogo: "#FFFFFF",
|
|
45
52
|
darkNavbarBorder: "#e5e7eb",
|
|
53
|
+
darkNavbarText: "#9ca3af",
|
|
54
|
+
darkNavbarTextHover: "alias:darkNavbarText lighten",
|
|
55
|
+
darkNavbarTextActive: "alias:darkNavbarText lighten",
|
|
56
|
+
darkNavbarIcons: "alias:darkNavbarText opacity:0.7",
|
|
46
57
|
darkSidebar: "#1f2937",
|
|
47
|
-
darkSidebarBorder: "
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
darkSidebarItemHover: "#374151",
|
|
51
|
-
darkSidebarItemActive: "#52525b",
|
|
52
|
-
darkSidebarItemActiveText: "#FFFFFF",
|
|
58
|
+
darkSidebarBorder: "alias:darkSidebarText opacity:0.3",
|
|
59
|
+
darkSidebarItemHover: "alias:darkSidebarText opacity:0.1",
|
|
60
|
+
darkSidebarItemActive: "alias:darkSidebarText opacity:0.4",
|
|
53
61
|
darkSidebarText: "#9ca3af",
|
|
54
|
-
darkSidebarTextHover: "
|
|
55
|
-
darkSidebarTextActive: "
|
|
56
|
-
darkSidebarDevider: "
|
|
57
|
-
darkSidebarIcons: "
|
|
58
|
-
darkSidebarIconsHover: "
|
|
59
|
-
darkSidebarHeading: "
|
|
62
|
+
darkSidebarTextHover: "alias:darkSidebarText lighten",
|
|
63
|
+
darkSidebarTextActive: "alias:darkSidebarText lighten",
|
|
64
|
+
darkSidebarDevider: "alias:darkSidebarText opacity:0.3",
|
|
65
|
+
darkSidebarIcons: "alias:darkSidebarText opacity:0.7",
|
|
66
|
+
darkSidebarIconsHover: "alias:darkSidebarText",
|
|
67
|
+
darkSidebarHeading: "alias:darkSidebarText opacity:0.3",
|
|
60
68
|
darkList: "#111111",
|
|
61
69
|
darkListTable: "#1f2937",
|
|
62
70
|
darkListTableHeading: "#334155",
|
package/dist/modules/utils.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
2
|
import { fileURLToPath } from 'url';
|
|
3
3
|
import fs from 'fs';
|
|
4
|
+
var csscolors = require('css-color-names');
|
|
4
5
|
export function guessLabelFromName(name) {
|
|
5
6
|
if (name.includes('_')) {
|
|
6
7
|
return name.split('_').map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(' ');
|
|
@@ -75,3 +76,76 @@ export function transformObject(obj, parentKey = '', result = {}) {
|
|
|
75
76
|
}
|
|
76
77
|
return result;
|
|
77
78
|
}
|
|
79
|
+
function hexToRGBA(hex, opacity) {
|
|
80
|
+
var c;
|
|
81
|
+
if (/^#([A-Fa-f0-9]{3}){1,2}$/.test(hex)) {
|
|
82
|
+
c = hex.substring(1).split('');
|
|
83
|
+
if (c.length == 3) {
|
|
84
|
+
c = [c[0], c[0], c[1], c[1], c[2], c[2]];
|
|
85
|
+
}
|
|
86
|
+
c = '0x' + c.join('');
|
|
87
|
+
return 'rgba(' + [(c >> 16) & 255, (c >> 8) & 255, c & 255].join(',') + ',' + opacity + ')';
|
|
88
|
+
}
|
|
89
|
+
throw new Error('Bad Hex');
|
|
90
|
+
}
|
|
91
|
+
export function createRGBA(color, opacity) {
|
|
92
|
+
if (!color)
|
|
93
|
+
return;
|
|
94
|
+
else if (color.startsWith("rgba")) {
|
|
95
|
+
//add opacity to existing rgba color
|
|
96
|
+
const rgb = color.match(/[\d.]+/g);
|
|
97
|
+
console.log('rgb', rgb);
|
|
98
|
+
return `rgba(${rgb[0]}, ${rgb[1]}, ${rgb[2]}, ${Number(rgb[3]) * opacity})`;
|
|
99
|
+
}
|
|
100
|
+
else if (color.startsWith("rgb")) {
|
|
101
|
+
const rgb = color.match(/\d+/g);
|
|
102
|
+
return `rgba(${rgb[0]}, ${rgb[1]}, ${rgb[2]}, ${opacity})`;
|
|
103
|
+
}
|
|
104
|
+
else if (color.startsWith("#")) {
|
|
105
|
+
return hexToRGBA(color, opacity);
|
|
106
|
+
}
|
|
107
|
+
else if (csscolors[color]) {
|
|
108
|
+
return hexToRGBA(csscolors[color], opacity);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
return color;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
export function parseColorForAliases(str) {
|
|
115
|
+
const aliasRegex = /alias:([^ ]+)/;
|
|
116
|
+
const opacityRegex = /opacity:([0-9.]+)/;
|
|
117
|
+
const darkenRegex = /darken/;
|
|
118
|
+
const lightenRegex = /lighten/;
|
|
119
|
+
// Extract alias and properties
|
|
120
|
+
const aliasMatch = str.match(aliasRegex);
|
|
121
|
+
const opacityMatch = str.match(opacityRegex);
|
|
122
|
+
const darkenMatch = str.match(darkenRegex);
|
|
123
|
+
const lightenMatch = str.match(lightenRegex);
|
|
124
|
+
return { aliasMatch, opacityMatch, darkenMatch, lightenMatch };
|
|
125
|
+
}
|
|
126
|
+
export function darkenRGBA(rgba) {
|
|
127
|
+
let amount = 0.2;
|
|
128
|
+
// Extract the RGBA components
|
|
129
|
+
let [r, g, b, a] = rgba.match(/\d+/g).map(Number);
|
|
130
|
+
// Ensure amount is between 0 and 1
|
|
131
|
+
amount = Math.max(0, Math.min(1, amount));
|
|
132
|
+
// Calculate the new RGB values
|
|
133
|
+
r = Math.max(0, r * (1 - amount));
|
|
134
|
+
g = Math.max(0, g * (1 - amount));
|
|
135
|
+
b = Math.max(0, b * (1 - amount));
|
|
136
|
+
// Return the new RGBA color
|
|
137
|
+
return `rgba(${Math.round(r)}, ${Math.round(g)}, ${Math.round(b)}, ${a})`;
|
|
138
|
+
}
|
|
139
|
+
export function lightenRGBA(rgba) {
|
|
140
|
+
let amount = 0.2;
|
|
141
|
+
// Extract the RGBA components
|
|
142
|
+
let [r, g, b, a] = rgba.match(/\d+/g).map(Number);
|
|
143
|
+
// Ensure amount is between 0 and 1
|
|
144
|
+
amount = Math.max(0, Math.min(1, amount));
|
|
145
|
+
// Calculate the new RGB values
|
|
146
|
+
r = Math.min(255, r * (1 + amount));
|
|
147
|
+
g = Math.min(255, g * (1 + amount));
|
|
148
|
+
b = Math.min(255, b * (1 + amount));
|
|
149
|
+
// Return the new RGBA color
|
|
150
|
+
return `rgba(${Math.round(r)}, ${Math.round(g)}, ${Math.round(b)}, ${a})`;
|
|
151
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { AllowedActionsEnum } from "../../types/AdminForthConfig.js";
|
|
11
|
+
import AdminForthPlugin from "../base.js";
|
|
12
|
+
class AccessControlPlugin extends AdminForthPlugin {
|
|
13
|
+
constructor(options) {
|
|
14
|
+
super(options, import.meta.url);
|
|
15
|
+
this.options = options;
|
|
16
|
+
}
|
|
17
|
+
modifyResourceConfig(adminforth, resourceConfig) {
|
|
18
|
+
super.modifyResourceConfig(adminforth, resourceConfig);
|
|
19
|
+
this.adminforth = adminforth;
|
|
20
|
+
if (!resourceConfig.hooks) {
|
|
21
|
+
resourceConfig.hooks = {};
|
|
22
|
+
}
|
|
23
|
+
const checkAccess = (adminUser, action, meta) => __awaiter(this, void 0, void 0, function* () {
|
|
24
|
+
const hasAccessOrError = yield this.options.hasAccess(adminUser, action, meta);
|
|
25
|
+
if (hasAccessOrError === true) {
|
|
26
|
+
return { ok: true };
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
return { ok: false, error: hasAccessOrError || AccessControlPlugin.defaultError };
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
const bindHookCheck = (action, hookName) => {
|
|
33
|
+
if (!resourceConfig.hooks[action]) {
|
|
34
|
+
resourceConfig.hooks[action] = {};
|
|
35
|
+
}
|
|
36
|
+
if (!resourceConfig.hooks[action][hookName]) {
|
|
37
|
+
resourceConfig.hooks[action][hookName] = [];
|
|
38
|
+
}
|
|
39
|
+
if (hookName === 'beforeDatasourceRequest') {
|
|
40
|
+
resourceConfig.hooks[action][hookName].unshift((_a) => __awaiter(this, [_a], void 0, function* ({ adminUser, query }) {
|
|
41
|
+
return checkAccess(adminUser, action, { query });
|
|
42
|
+
}));
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
resourceConfig.hooks[action][hookName].unshift((_b) => __awaiter(this, [_b], void 0, function* ({ adminUser, record }) {
|
|
46
|
+
return checkAccess(adminUser, action, { record });
|
|
47
|
+
}));
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
// List check
|
|
51
|
+
bindHookCheck(AllowedActionsEnum.list, 'beforeDatasourceRequest');
|
|
52
|
+
// Show check
|
|
53
|
+
bindHookCheck(AllowedActionsEnum.show, 'beforeDatasourceRequest');
|
|
54
|
+
// Edit check
|
|
55
|
+
bindHookCheck(AllowedActionsEnum.edit, 'beforeDatasourceRequest');
|
|
56
|
+
// create check
|
|
57
|
+
bindHookCheck(AllowedActionsEnum.create, 'beforeSave');
|
|
58
|
+
// edit check
|
|
59
|
+
bindHookCheck(AllowedActionsEnum.edit, 'beforeSave');
|
|
60
|
+
// delete check
|
|
61
|
+
bindHookCheck(AllowedActionsEnum.delete, 'beforeSave');
|
|
62
|
+
console.log('resourceConfig', resourceConfig);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
AccessControlPlugin.defaultError = 'Sorry, you do not have access to this resource.';
|
|
66
|
+
export default AccessControlPlugin;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
import VueDiff from 'vue-diff';
|
|
3
|
+
import { ref } from 'vue';
|
|
4
|
+
import 'vue-diff/dist/index.css';
|
|
5
|
+
|
|
6
|
+
const props = defineProps(['prev', 'current', 'meta']);
|
|
7
|
+
|
|
8
|
+
app.use(VueDiff);
|
|
9
|
+
</script>
|
|
10
|
+
|
|
11
|
+
<template>
|
|
12
|
+
<Diff
|
|
13
|
+
:mode="'split'"
|
|
14
|
+
:theme="'light'"
|
|
15
|
+
:language="'plaintext'"
|
|
16
|
+
:prev="props.prev"
|
|
17
|
+
:current="props.current"
|
|
18
|
+
/>
|
|
19
|
+
</template>
|
|
20
|
+
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import AdminForthPlugin from "../base.js";
|
|
11
|
+
class AuditLogPlugin extends AdminForthPlugin {
|
|
12
|
+
constructor(options) {
|
|
13
|
+
super(options, import.meta.url);
|
|
14
|
+
this.createLogRecord = (resource, action, data, user) => __awaiter(this, void 0, void 0, function* () {
|
|
15
|
+
var _a;
|
|
16
|
+
const recordIdFieldName = (_a = resource.columns.find((c) => c.primaryKey === true)) === null || _a === void 0 ? void 0 : _a.name;
|
|
17
|
+
const recordId = data[recordIdFieldName];
|
|
18
|
+
const record = {
|
|
19
|
+
[this.options.resourceColumns.resourceIdColumnName]: resource.resourceId,
|
|
20
|
+
[this.options.resourceColumns.resourceActionColumnName]: action,
|
|
21
|
+
[this.options.resourceColumns.resourceDataColumnName]: data,
|
|
22
|
+
[this.options.resourceColumns.resourceUserIdColumnName]: user.pk,
|
|
23
|
+
[this.options.resourceColumns.resourceRecordIdColumnName]: recordId,
|
|
24
|
+
[this.options.resourceColumns.resourceCreatedColumnName]: new Date()
|
|
25
|
+
};
|
|
26
|
+
const auditLogResource = this.adminforth.config.resources.find((r) => r.resourceId === this.auditLogResource);
|
|
27
|
+
console.log('rec', record);
|
|
28
|
+
yield this.adminforth.createResourceRecord({ resource: auditLogResource, record, adminUser: user });
|
|
29
|
+
return { ok: true };
|
|
30
|
+
});
|
|
31
|
+
this.options = options;
|
|
32
|
+
}
|
|
33
|
+
modifyResourceConfig(adminforth, resourceConfig) {
|
|
34
|
+
super.modifyResourceConfig(adminforth, resourceConfig);
|
|
35
|
+
this.adminforth = adminforth;
|
|
36
|
+
this.auditLogResource = resourceConfig.resourceId;
|
|
37
|
+
this.adminforth.config.resources.forEach((resource) => {
|
|
38
|
+
var _a;
|
|
39
|
+
if ((_a = this.options.excludeResourceIds) === null || _a === void 0 ? void 0 : _a.includes(resource.resourceId)) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (this.auditLogResource === resource.resourceId) {
|
|
43
|
+
let diffColumn = resource.columns.find((c) => c.name === this.options.resourceColumns.resourceDataColumnName);
|
|
44
|
+
if (!diffColumn) {
|
|
45
|
+
diffColumn = {
|
|
46
|
+
name: this.options.resourceColumns.resourceDataColumnName,
|
|
47
|
+
components: {},
|
|
48
|
+
// type: AdminForthDataTypes.STRING
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
diffColumn.components = {
|
|
52
|
+
showRow: {
|
|
53
|
+
file: this.componentPath('AuditLogView.vue'),
|
|
54
|
+
meta: Object.assign(Object.assign({}, this.options), { pluginInstanceId: this.pluginInstanceId })
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
resource.columns.push(diffColumn);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const defaultHooks = {
|
|
61
|
+
create: { beforeSave: [] },
|
|
62
|
+
edit: { beforeSave: [] },
|
|
63
|
+
delete: { beforeSave: [] }
|
|
64
|
+
};
|
|
65
|
+
if (!resource.hooks) {
|
|
66
|
+
resource.hooks = defaultHooks;
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
resource.hooks = Object.assign(Object.assign({}, defaultHooks), resource.hooks);
|
|
70
|
+
}
|
|
71
|
+
Object.keys(resource.hooks).forEach((hook) => {
|
|
72
|
+
if (!Array.isArray(resource.hooks[hook].beforeSave)) {
|
|
73
|
+
resource.hooks[hook].beforeSave = [resource.hooks[hook].beforeSave];
|
|
74
|
+
}
|
|
75
|
+
resource.hooks[hook].beforeSave.push((_a) => __awaiter(this, [_a], void 0, function* ({ resource, record, adminUser }) {
|
|
76
|
+
return yield this.createLogRecord(resource, hook, record, adminUser);
|
|
77
|
+
}));
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
AuditLogPlugin.defaultError = 'Sorry, you do not have access to this resource.';
|
|
83
|
+
export default AuditLogPlugin;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/spa/index.html
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
<html lang="en">
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
|
-
<link rel="icon" href="
|
|
5
|
+
<link rel="icon" href="/favicon.ico">
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
|
-
<title
|
|
7
|
+
<title>Vite App</title>
|
|
8
8
|
<!--
|
|
9
9
|
<script>
|
|
10
10
|
// On page load or when changing themes, best to add inline in `head` to avoid FOUC
|