adminforth 1.1.65 → 1.1.67
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 +19 -4
- package/dataConnectors/mongo.ts +7 -13
- package/dataConnectors/postgres.ts +2 -2
- package/dataConnectors/sqlite.ts +2 -2
- package/dist/auth.js +14 -4
- package/dist/dataConnectors/mongo.js +1 -9
- package/dist/index.js +4 -9
- package/dist/plugins/AuditLogPlugin/custom/AuditLogView.vue +7 -6
- package/dist/plugins/AuditLogPlugin/index.js +17 -13
- package/dist/servers/express.js +1 -1
- package/dist/spa/index.html +2 -2
- package/dist/spa/package-lock.json +17 -4
- package/dist/spa/package.json +2 -1
- package/dist/spa/spa/src/App.vue +24 -12
- package/dist/spa/spa/src/composables/useStores.ts +16 -4
- package/dist/spa/spa/src/main.ts +1 -1
- package/dist/spa/src/App.vue +125 -45
- package/dist/spa/src/assets/logo.svg +19 -1
- package/dist/spa/src/components/AcceptModal.vue +2 -9
- package/dist/spa/src/components/BreadcrumbsWithButtons.vue +1 -1
- package/dist/spa/src/components/CustomDatePicker.vue +16 -3
- package/dist/spa/src/components/CustomDateRangePicker.vue +11 -2
- package/dist/spa/src/components/Dropdown.vue +6 -1
- package/dist/spa/src/components/Filters.vue +40 -19
- package/dist/spa/src/components/ResourceForm.vue +26 -24
- package/dist/spa/src/components/ResourceListTable.vue +419 -0
- package/dist/spa/src/components/Toast.vue +66 -0
- package/dist/spa/src/components/ValueRenderer.vue +14 -8
- package/dist/spa/src/composables/useFrontendApi.ts +26 -0
- package/dist/spa/src/composables/useStores.ts +113 -0
- package/dist/spa/src/main.ts +1 -1
- package/dist/spa/src/router/index.ts +30 -20
- package/dist/spa/src/spa_types/core.ts +51 -0
- package/dist/spa/src/stores/core.ts +48 -31
- package/dist/spa/src/stores/filters.ts +22 -0
- package/dist/spa/src/stores/modal.ts +13 -3
- package/dist/spa/src/stores/toast.ts +15 -0
- package/dist/spa/src/stores/user.ts +54 -0
- package/dist/spa/src/utils.ts +62 -7
- package/dist/spa/src/views/CreateView.vue +67 -15
- package/dist/spa/src/views/EditView.vue +45 -11
- package/dist/spa/src/views/ListView.vue +82 -366
- package/dist/spa/src/views/LoginView.vue +16 -4
- package/dist/spa/src/views/ShowView.vue +105 -21
- package/dist/spa/tailwind.config.js +1 -1
- package/dist/spa/vite.config.ts +6 -0
- package/index.ts +7 -12
- package/package.json +1 -1
- package/plugins/AuditLogPlugin/custom/AuditLogView.vue +7 -6
- package/plugins/AuditLogPlugin/index.ts +22 -15
- package/servers/express.ts +1 -1
- package/spa/src/App.vue +24 -12
- package/spa/src/composables/useStores.ts +16 -4
- package/spa/src/main.ts +1 -1
- package/types/AdminForthConfig.ts +18 -4
- package/types/FrontendAPI.ts +1 -15
- package/dist/plugins/AccessControl/index.js +0 -66
- package/dist/plugins/AccessControl/types.js +0 -1
- package/dist/spa/public/favicon.ico +0 -0
- package/dist/spa/spa/public/favicon.ico +0 -0
- package/dist/types.js +0 -30
- /package/dist/spa/{spa/public → public/assets}/favicon.png +0 -0
package/auth.ts
CHANGED
|
@@ -3,6 +3,7 @@ import jwt from 'jsonwebtoken';
|
|
|
3
3
|
|
|
4
4
|
import crypto from 'crypto';
|
|
5
5
|
import AdminForth from './index.js';
|
|
6
|
+
import { th } from '@faker-js/faker';
|
|
6
7
|
|
|
7
8
|
// Function to generate a password hash using PBKDF2
|
|
8
9
|
function calcPasswordHash(password, salt, iterations = 100000, keyLength = 64, digest = 'sha512') {
|
|
@@ -26,7 +27,18 @@ class AdminForthAuth {
|
|
|
26
27
|
this.adminforth = adminforth;
|
|
27
28
|
}
|
|
28
29
|
|
|
29
|
-
|
|
30
|
+
removeAuthCookie(response) {
|
|
31
|
+
response.setHeader('Set-Cookie', `adminforth_jwt=; Path=${this.adminforth.config.baseUrl || '/'}; HttpOnly; SameSite=Strict; Expires=Thu, 01 Jan 1970 00:00:00 GMT`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
setAuthCookie({ response, username, pk}: {
|
|
35
|
+
response: any, username: string, pk: string | null
|
|
36
|
+
}) {
|
|
37
|
+
const token = this.issueJWT({ username, pk, }, 'auth');
|
|
38
|
+
response.setHeader('Set-Cookie', `adminforth_jwt=${token}; Path=${this.adminforth.config.baseUrl || '/'}; HttpOnly; SameSite=Strict`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
issueJWT(payload: Object, type: string) {
|
|
30
42
|
// read ADMINFORH_SECRET from environment if not drop error
|
|
31
43
|
const secret = process.env.ADMINFORTH_SECRET;
|
|
32
44
|
if (!secret) {
|
|
@@ -35,10 +47,10 @@ class AdminForthAuth {
|
|
|
35
47
|
|
|
36
48
|
// issue JWT token
|
|
37
49
|
const expiresIn = process.env.ADMINFORTH_AUTH_EXPIRESIN || '24h';
|
|
38
|
-
return jwt.sign(payload, secret, { expiresIn });
|
|
50
|
+
return jwt.sign({...payload, t: type}, secret, { expiresIn });
|
|
39
51
|
}
|
|
40
52
|
|
|
41
|
-
async verify(jwtToken) {
|
|
53
|
+
async verify(jwtToken: string, mustHaveType: string): Promise<Object> {
|
|
42
54
|
// read ADMINFORH_SECRET from environment if not drop error
|
|
43
55
|
const secret = process.env.ADMINFORTH_SECRET;
|
|
44
56
|
if (!secret) {
|
|
@@ -58,7 +70,10 @@ class AdminForthAuth {
|
|
|
58
70
|
}
|
|
59
71
|
return null;
|
|
60
72
|
}
|
|
61
|
-
const { pk } = decoded;
|
|
73
|
+
const { pk, t } = decoded;
|
|
74
|
+
if (t !== mustHaveType) {
|
|
75
|
+
throw new Error(`Invalid token type during verification: ${t}, must be ${mustHaveType}`);
|
|
76
|
+
}
|
|
62
77
|
if (pk === null) {
|
|
63
78
|
decoded.isRoot = true;
|
|
64
79
|
} else {
|
package/dataConnectors/mongo.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import dayjs from 'dayjs';
|
|
2
2
|
import { MongoClient } from 'mongodb';
|
|
3
|
-
import { AdminForthDataTypes, AdminForthFilterOperators, AdminForthSortDirections } from '../types/AdminForthConfig.js';
|
|
3
|
+
import { AdminForthDataTypes, AdminForthFilterOperators, AdminForthSortDirections, AdminForthDataSourceConnector } from '../types/AdminForthConfig.js';
|
|
4
4
|
|
|
5
|
-
class MongoConnector {
|
|
5
|
+
class MongoConnector implements AdminForthDataSourceConnector {
|
|
6
6
|
db: MongoClient
|
|
7
7
|
|
|
8
|
-
constructor({ url }) {
|
|
8
|
+
constructor({ url }: { url: string }) {
|
|
9
9
|
this.db = new MongoClient(url);
|
|
10
10
|
(async () => {
|
|
11
11
|
try {
|
|
@@ -65,16 +65,10 @@ class MongoConnector {
|
|
|
65
65
|
|
|
66
66
|
getFieldValue(field, value) {
|
|
67
67
|
if (field.type == AdminForthDataTypes.DATETIME) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
return dayjs.unix(+value).toISOString();
|
|
73
|
-
} else if (field._underlineType == 'varchar') {
|
|
74
|
-
return dayjs.unix(+value).toISOString();
|
|
75
|
-
} else {
|
|
76
|
-
throw new Error(`AdminForth does not support row type: ${field._underlineType} for timestamps, use VARCHAR (with iso strings) or TIMESTAMP/INT (with unix timestamps)`);
|
|
77
|
-
}
|
|
68
|
+
if (!value) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
return dayjs.unix(value).toISOString();
|
|
78
72
|
|
|
79
73
|
} else if (field.type == AdminForthDataTypes.DATE) {
|
|
80
74
|
if (!value) {
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import dayjs from 'dayjs';
|
|
2
2
|
import pkg from 'pg';
|
|
3
|
-
import { AdminForthDataTypes, AdminForthFilterOperators, AdminForthSortDirections } from '../types/AdminForthConfig.js';
|
|
3
|
+
import { AdminForthDataTypes, AdminForthFilterOperators, AdminForthSortDirections, AdminForthDataSourceConnector } from '../types/AdminForthConfig.js';
|
|
4
4
|
|
|
5
5
|
const { Client } = pkg;
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
class PostgresConnector {
|
|
8
|
+
class PostgresConnector implements AdminForthDataSourceConnector {
|
|
9
9
|
|
|
10
10
|
db: any;
|
|
11
11
|
|
package/dataConnectors/sqlite.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import betterSqlite3 from 'better-sqlite3';
|
|
2
|
-
import { AdminForthDataTypes, AdminForthFilterOperators, AdminForthSortDirections } from '../types/AdminForthConfig.js';
|
|
2
|
+
import { AdminForthDataTypes, AdminForthFilterOperators, AdminForthSortDirections, AdminForthDataSourceConnector } from '../types/AdminForthConfig.js';
|
|
3
3
|
|
|
4
4
|
import dayjs from 'dayjs';
|
|
5
5
|
|
|
6
|
-
class SQLiteConnector {
|
|
6
|
+
class SQLiteConnector implements AdminForthDataSourceConnector {
|
|
7
7
|
|
|
8
8
|
db: any;
|
|
9
9
|
|
package/dist/auth.js
CHANGED
|
@@ -27,7 +27,14 @@ class AdminForthAuth {
|
|
|
27
27
|
constructor(adminforth) {
|
|
28
28
|
this.adminforth = adminforth;
|
|
29
29
|
}
|
|
30
|
-
|
|
30
|
+
removeAuthCookie(response) {
|
|
31
|
+
response.setHeader('Set-Cookie', `adminforth_jwt=; Path=${this.adminforth.config.baseUrl || '/'}; HttpOnly; SameSite=Strict; Expires=Thu, 01 Jan 1970 00:00:00 GMT`);
|
|
32
|
+
}
|
|
33
|
+
setAuthCookie({ response, username, pk }) {
|
|
34
|
+
const token = this.issueJWT({ username, pk, }, 'auth');
|
|
35
|
+
response.setHeader('Set-Cookie', `adminforth_jwt=${token}; Path=${this.adminforth.config.baseUrl || '/'}; HttpOnly; SameSite=Strict`);
|
|
36
|
+
}
|
|
37
|
+
issueJWT(payload, type) {
|
|
31
38
|
// read ADMINFORH_SECRET from environment if not drop error
|
|
32
39
|
const secret = process.env.ADMINFORTH_SECRET;
|
|
33
40
|
if (!secret) {
|
|
@@ -35,9 +42,9 @@ class AdminForthAuth {
|
|
|
35
42
|
}
|
|
36
43
|
// issue JWT token
|
|
37
44
|
const expiresIn = process.env.ADMINFORTH_AUTH_EXPIRESIN || '24h';
|
|
38
|
-
return jwt.sign(payload, secret, { expiresIn });
|
|
45
|
+
return jwt.sign(Object.assign(Object.assign({}, payload), { t: type }), secret, { expiresIn });
|
|
39
46
|
}
|
|
40
|
-
verify(jwtToken) {
|
|
47
|
+
verify(jwtToken, mustHaveType) {
|
|
41
48
|
return __awaiter(this, void 0, void 0, function* () {
|
|
42
49
|
// read ADMINFORH_SECRET from environment if not drop error
|
|
43
50
|
const secret = process.env.ADMINFORTH_SECRET;
|
|
@@ -61,7 +68,10 @@ class AdminForthAuth {
|
|
|
61
68
|
}
|
|
62
69
|
return null;
|
|
63
70
|
}
|
|
64
|
-
const { pk } = decoded;
|
|
71
|
+
const { pk, t } = decoded;
|
|
72
|
+
if (t !== mustHaveType) {
|
|
73
|
+
throw new Error(`Invalid token type during verification: ${t}, must be ${mustHaveType}`);
|
|
74
|
+
}
|
|
65
75
|
if (pk === null) {
|
|
66
76
|
decoded.isRoot = true;
|
|
67
77
|
}
|
|
@@ -71,15 +71,7 @@ class MongoConnector {
|
|
|
71
71
|
if (!value) {
|
|
72
72
|
return null;
|
|
73
73
|
}
|
|
74
|
-
|
|
75
|
-
return dayjs.unix(+value).toISOString();
|
|
76
|
-
}
|
|
77
|
-
else if (field._underlineType == 'varchar') {
|
|
78
|
-
return dayjs.unix(+value).toISOString();
|
|
79
|
-
}
|
|
80
|
-
else {
|
|
81
|
-
throw new Error(`AdminForth does not support row type: ${field._underlineType} for timestamps, use VARCHAR (with iso strings) or TIMESTAMP/INT (with unix timestamps)`);
|
|
82
|
-
}
|
|
74
|
+
return dayjs.unix(value).toISOString();
|
|
83
75
|
}
|
|
84
76
|
else if (field.type == AdminForthDataTypes.DATE) {
|
|
85
77
|
if (!value) {
|
package/dist/index.js
CHANGED
|
@@ -541,7 +541,7 @@ class AdminForth {
|
|
|
541
541
|
const { username, password } = body;
|
|
542
542
|
let token;
|
|
543
543
|
if (username === this.config.rootUser.username && password === this.config.rootUser.password) {
|
|
544
|
-
|
|
544
|
+
this.auth.setAuthCookie({ response, username, pk: null });
|
|
545
545
|
}
|
|
546
546
|
else {
|
|
547
547
|
// get resource from db
|
|
@@ -575,15 +575,12 @@ class AdminForth {
|
|
|
575
575
|
console.log('User record', userRecord, passwordHash); // why does it has no hash?
|
|
576
576
|
const valid = yield AdminForthAuth.verifyPassword(password, passwordHash);
|
|
577
577
|
if (valid) {
|
|
578
|
-
|
|
579
|
-
username, pk: userRecord[userResource.columns.find((col) => col.primaryKey).name]
|
|
580
|
-
});
|
|
578
|
+
this.auth.setAuthCookie({ response, username, pk: userRecord[userResource.columns.find((col) => col.primaryKey).name] });
|
|
581
579
|
}
|
|
582
580
|
else {
|
|
583
581
|
return { error: INVALID_MESSAGE };
|
|
584
582
|
}
|
|
585
583
|
}
|
|
586
|
-
response.setHeader('Set-Cookie', `adminforth_jwt=${token}; Path=${this.config.baseUrl || '/'}; HttpOnly; SameSite=Strict`);
|
|
587
584
|
return { ok: true };
|
|
588
585
|
}),
|
|
589
586
|
});
|
|
@@ -599,7 +596,7 @@ class AdminForth {
|
|
|
599
596
|
method: 'POST',
|
|
600
597
|
path: '/logout',
|
|
601
598
|
handler: (_e) => __awaiter(this, [_e], void 0, function* ({ response }) {
|
|
602
|
-
|
|
599
|
+
this.auth.removeAuthCookie({ response });
|
|
603
600
|
return { ok: true };
|
|
604
601
|
}),
|
|
605
602
|
});
|
|
@@ -633,11 +630,9 @@ class AdminForth {
|
|
|
633
630
|
let userFullName = '';
|
|
634
631
|
if (adminUser.isRoot) {
|
|
635
632
|
username = this.config.rootUser.username;
|
|
636
|
-
console.log(username, 'this is the root');
|
|
637
633
|
}
|
|
638
634
|
else {
|
|
639
635
|
const dbUser = adminUser.dbUser;
|
|
640
|
-
console.log(dbUser, 'this is the db user', adminUser, 'this is the admin user');
|
|
641
636
|
username = dbUser[this.config.auth.usernameField];
|
|
642
637
|
userFullName = dbUser[this.config.auth.userFullNameField];
|
|
643
638
|
}
|
|
@@ -1031,7 +1026,7 @@ class AdminForth {
|
|
|
1031
1026
|
}
|
|
1032
1027
|
// execute hook if needed
|
|
1033
1028
|
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)) {
|
|
1034
|
-
const resp = yield hook({ resource, record, adminUser });
|
|
1029
|
+
const resp = yield hook({ resource, record, adminUser, oldRecord });
|
|
1035
1030
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
1036
1031
|
throw new Error(`Hook afterSave must return object with {ok: true} or { error: 'Error' } `);
|
|
1037
1032
|
}
|
|
@@ -2,19 +2,20 @@
|
|
|
2
2
|
import VueDiff from 'vue-diff';
|
|
3
3
|
import { ref } from 'vue';
|
|
4
4
|
import 'vue-diff/dist/index.css';
|
|
5
|
-
|
|
6
|
-
const props = defineProps(['prev', 'current', 'meta']);
|
|
5
|
+
import { app } from '@/main';
|
|
7
6
|
|
|
8
7
|
app.use(VueDiff);
|
|
8
|
+
|
|
9
|
+
const props = defineProps(['column', 'record', 'meta', 'resource', 'adminUser']);
|
|
9
10
|
</script>
|
|
10
11
|
|
|
11
12
|
<template>
|
|
12
13
|
<Diff
|
|
13
14
|
:mode="'split'"
|
|
14
15
|
:theme="'light'"
|
|
15
|
-
:language="'
|
|
16
|
-
:prev="props.
|
|
17
|
-
:current="props.
|
|
16
|
+
:language="'JSON'"
|
|
17
|
+
:prev="JSON.stringify(JSON.parse(props.record[props.meta.resourceColumns.resourceDataColumnName]).oldRecord, null, 2)"
|
|
18
|
+
:current="JSON.stringify(JSON.parse(props.record[props.meta.resourceColumns.resourceDataColumnName]).newRecord, null, 2)"
|
|
18
19
|
/>
|
|
19
|
-
|
|
20
|
+
</template>
|
|
20
21
|
|
|
@@ -11,20 +11,25 @@ import AdminForthPlugin from "../base.js";
|
|
|
11
11
|
class AuditLogPlugin extends AdminForthPlugin {
|
|
12
12
|
constructor(options) {
|
|
13
13
|
super(options, import.meta.url);
|
|
14
|
-
this.createLogRecord = (resource, action, data, user) => __awaiter(this, void 0, void 0, function* () {
|
|
14
|
+
this.createLogRecord = (resource, action, data, user, oldRecord) => __awaiter(this, void 0, void 0, function* () {
|
|
15
15
|
var _a;
|
|
16
16
|
const recordIdFieldName = (_a = resource.columns.find((c) => c.primaryKey === true)) === null || _a === void 0 ? void 0 : _a.name;
|
|
17
|
-
const recordId = data[recordIdFieldName];
|
|
17
|
+
const recordId = (data === null || data === void 0 ? void 0 : data[recordIdFieldName]) || (oldRecord === null || oldRecord === void 0 ? void 0 : oldRecord[recordIdFieldName]);
|
|
18
|
+
const connector = this.adminforth.connectors[resource.dataSource];
|
|
19
|
+
const newRecord = yield connector.getRecordByPrimaryKey(resource, recordId);
|
|
20
|
+
let newData = {
|
|
21
|
+
'oldRecord': oldRecord || {},
|
|
22
|
+
'newRecord': newRecord
|
|
23
|
+
};
|
|
18
24
|
const record = {
|
|
19
25
|
[this.options.resourceColumns.resourceIdColumnName]: resource.resourceId,
|
|
20
26
|
[this.options.resourceColumns.resourceActionColumnName]: action,
|
|
21
|
-
[this.options.resourceColumns.resourceDataColumnName]:
|
|
27
|
+
[this.options.resourceColumns.resourceDataColumnName]: newData,
|
|
22
28
|
[this.options.resourceColumns.resourceUserIdColumnName]: user.pk,
|
|
23
29
|
[this.options.resourceColumns.resourceRecordIdColumnName]: recordId,
|
|
24
30
|
[this.options.resourceColumns.resourceCreatedColumnName]: new Date()
|
|
25
31
|
};
|
|
26
32
|
const auditLogResource = this.adminforth.config.resources.find((r) => r.resourceId === this.auditLogResource);
|
|
27
|
-
console.log('rec', record);
|
|
28
33
|
yield this.adminforth.createResourceRecord({ resource: auditLogResource, record, adminUser: user });
|
|
29
34
|
return { ok: true };
|
|
30
35
|
});
|
|
@@ -49,18 +54,17 @@ class AuditLogPlugin extends AdminForthPlugin {
|
|
|
49
54
|
};
|
|
50
55
|
}
|
|
51
56
|
diffColumn.components = {
|
|
52
|
-
|
|
57
|
+
show: {
|
|
53
58
|
file: this.componentPath('AuditLogView.vue'),
|
|
54
59
|
meta: Object.assign(Object.assign({}, this.options), { pluginInstanceId: this.pluginInstanceId })
|
|
55
60
|
}
|
|
56
61
|
};
|
|
57
|
-
resource.columns.push(diffColumn);
|
|
58
62
|
return;
|
|
59
63
|
}
|
|
60
64
|
const defaultHooks = {
|
|
61
|
-
create: {
|
|
62
|
-
edit: {
|
|
63
|
-
delete: {
|
|
65
|
+
create: { afterSave: [] },
|
|
66
|
+
edit: { afterSave: [] },
|
|
67
|
+
delete: { afterSave: [] }
|
|
64
68
|
};
|
|
65
69
|
if (!resource.hooks) {
|
|
66
70
|
resource.hooks = defaultHooks;
|
|
@@ -69,11 +73,11 @@ class AuditLogPlugin extends AdminForthPlugin {
|
|
|
69
73
|
resource.hooks = Object.assign(Object.assign({}, defaultHooks), resource.hooks);
|
|
70
74
|
}
|
|
71
75
|
Object.keys(resource.hooks).forEach((hook) => {
|
|
72
|
-
if (!Array.isArray(resource.hooks[hook].
|
|
73
|
-
resource.hooks[hook].
|
|
76
|
+
if (!Array.isArray(resource.hooks[hook].afterSave)) {
|
|
77
|
+
resource.hooks[hook].afterSave = [resource.hooks[hook].afterSave];
|
|
74
78
|
}
|
|
75
|
-
resource.hooks[hook].
|
|
76
|
-
return yield this.createLogRecord(resource, hook, record, adminUser);
|
|
79
|
+
resource.hooks[hook].afterSave.push((_a) => __awaiter(this, [_a], void 0, function* ({ resource, record, adminUser, oldRecord }) {
|
|
80
|
+
return yield this.createLogRecord(resource, hook, record, adminUser, oldRecord);
|
|
77
81
|
}));
|
|
78
82
|
});
|
|
79
83
|
});
|
package/dist/servers/express.js
CHANGED
|
@@ -149,7 +149,7 @@ class ExpressServer {
|
|
|
149
149
|
res.status(401).send('Unauthorized by AdminForth');
|
|
150
150
|
return;
|
|
151
151
|
}
|
|
152
|
-
const adminforthUser = yield this.adminforth.auth.verify(jwt);
|
|
152
|
+
const adminforthUser = yield this.adminforth.auth.verify(jwt, 'auth');
|
|
153
153
|
if (!adminforthUser) {
|
|
154
154
|
res.status(401).send('Unauthorized by AdminForth');
|
|
155
155
|
}
|
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="/* IMPORTANT:ADMINFORTH FAVICON */">
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
|
-
<title
|
|
7
|
+
<title>/* IMPORTANT:ADMINFORTH TITLE */</title>
|
|
8
8
|
<!--
|
|
9
9
|
<script>
|
|
10
10
|
// On page load or when changing themes, best to add inline in `head` to avoid FOUC
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"flowbite-datepicker": "^1.2.6",
|
|
18
18
|
"pinia": "^2.1.7",
|
|
19
19
|
"unhead": "^1.9.12",
|
|
20
|
+
"uuid": "^10.0.0",
|
|
20
21
|
"vue": "^3.4.21",
|
|
21
22
|
"vue-router": "^4.3.0",
|
|
22
23
|
"vue-slider-component": "^4.1.0-beta.7"
|
|
@@ -36,7 +37,7 @@
|
|
|
36
37
|
"sass": "^1.77.2",
|
|
37
38
|
"tailwindcss": "^3.4.3",
|
|
38
39
|
"typescript": "~5.4.0",
|
|
39
|
-
"vite": "^5.2.
|
|
40
|
+
"vite": "^5.2.13",
|
|
40
41
|
"vue-tsc": "^2.0.11"
|
|
41
42
|
}
|
|
42
43
|
},
|
|
@@ -4032,10 +4033,22 @@
|
|
|
4032
4033
|
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
|
4033
4034
|
"dev": true
|
|
4034
4035
|
},
|
|
4036
|
+
"node_modules/uuid": {
|
|
4037
|
+
"version": "10.0.0",
|
|
4038
|
+
"resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz",
|
|
4039
|
+
"integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==",
|
|
4040
|
+
"funding": [
|
|
4041
|
+
"https://github.com/sponsors/broofa",
|
|
4042
|
+
"https://github.com/sponsors/ctavan"
|
|
4043
|
+
],
|
|
4044
|
+
"bin": {
|
|
4045
|
+
"uuid": "dist/bin/uuid"
|
|
4046
|
+
}
|
|
4047
|
+
},
|
|
4035
4048
|
"node_modules/vite": {
|
|
4036
|
-
"version": "5.2.
|
|
4037
|
-
"resolved": "https://registry.npmjs.org/vite/-/vite-5.2.
|
|
4038
|
-
"integrity": "sha512
|
|
4049
|
+
"version": "5.2.13",
|
|
4050
|
+
"resolved": "https://registry.npmjs.org/vite/-/vite-5.2.13.tgz",
|
|
4051
|
+
"integrity": "sha512-SSq1noJfY9pR3I1TUENL3rQYDQCFqgD+lM6fTRAM8Nv6Lsg5hDLaXkjETVeBt+7vZBCMoibD+6IWnT2mJ+Zb/A==",
|
|
4039
4052
|
"dev": true,
|
|
4040
4053
|
"dependencies": {
|
|
4041
4054
|
"esbuild": "^0.20.1",
|
package/dist/spa/package.json
CHANGED
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"flowbite-datepicker": "^1.2.6",
|
|
22
22
|
"pinia": "^2.1.7",
|
|
23
23
|
"unhead": "^1.9.12",
|
|
24
|
+
"uuid": "^10.0.0",
|
|
24
25
|
"vue": "^3.4.21",
|
|
25
26
|
"vue-router": "^4.3.0",
|
|
26
27
|
"vue-slider-component": "^4.1.0-beta.7"
|
|
@@ -40,7 +41,7 @@
|
|
|
40
41
|
"sass": "^1.77.2",
|
|
41
42
|
"tailwindcss": "^3.4.3",
|
|
42
43
|
"typescript": "~5.4.0",
|
|
43
|
-
"vite": "^5.2.
|
|
44
|
+
"vite": "^5.2.13",
|
|
44
45
|
"vue-tsc": "^2.0.11"
|
|
45
46
|
}
|
|
46
47
|
}
|
package/dist/spa/spa/src/App.vue
CHANGED
|
@@ -163,7 +163,7 @@
|
|
|
163
163
|
</style>
|
|
164
164
|
|
|
165
165
|
<script setup lang="ts">
|
|
166
|
-
import { computed, onMounted, ref, watch, defineComponent } from 'vue';
|
|
166
|
+
import { computed, onMounted, ref, watch, defineComponent, onBeforeMount } from 'vue';
|
|
167
167
|
import { RouterLink, RouterView } from 'vue-router';
|
|
168
168
|
import { initFlowbite } from 'flowbite'
|
|
169
169
|
import './index.scss'
|
|
@@ -211,6 +211,8 @@ const theme = ref('light');
|
|
|
211
211
|
function toggleTheme() {
|
|
212
212
|
theme.value = theme.value === 'light' ? 'dark' : 'light';
|
|
213
213
|
document.documentElement.classList.toggle('dark');
|
|
214
|
+
window.localStorage.setItem('theme', theme.value);
|
|
215
|
+
|
|
214
216
|
}
|
|
215
217
|
|
|
216
218
|
function clickOnMenuItem (label: string) {
|
|
@@ -237,14 +239,8 @@ async function initRouter() {
|
|
|
237
239
|
}
|
|
238
240
|
|
|
239
241
|
async function loadMenu() {
|
|
240
|
-
await coreStore.fetchMenuAndResource()
|
|
242
|
+
await coreStore.fetchMenuAndResource();
|
|
241
243
|
loginRedirectCheckIsReady.value = true;
|
|
242
|
-
|
|
243
|
-
coreStore.menu.forEach((item, i) => {
|
|
244
|
-
if (item.open) {
|
|
245
|
-
opened.value.push(i);
|
|
246
|
-
}
|
|
247
|
-
});
|
|
248
244
|
}
|
|
249
245
|
|
|
250
246
|
|
|
@@ -256,16 +252,32 @@ watch(route, () => {
|
|
|
256
252
|
})
|
|
257
253
|
});
|
|
258
254
|
|
|
255
|
+
watch (()=>coreStore.menu, () => {
|
|
256
|
+
coreStore.menu.forEach((item, i) => {
|
|
257
|
+
if (item.open) {
|
|
258
|
+
opened.value.push(i);
|
|
259
|
+
};
|
|
260
|
+
});
|
|
261
|
+
})
|
|
262
|
+
|
|
263
|
+
watch([loggedIn, routerIsReady, loginRedirectCheckIsReady], ([l,r,lr]) => {
|
|
264
|
+
if (l && r && lr) {
|
|
265
|
+
setTimeout(() => {
|
|
266
|
+
initFlowbite();
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
})
|
|
270
|
+
|
|
259
271
|
// initialize components based on data attribute selectors
|
|
260
272
|
onMounted(async () => {
|
|
261
273
|
loadMenu(); // run this in async mode
|
|
262
274
|
// before init flowbite we have to wait router initialized because it affects dom(our v-ifs) and fetch menu
|
|
263
275
|
await initRouter()
|
|
264
|
-
|
|
265
|
-
initFlowbite();
|
|
266
|
-
});
|
|
267
|
-
|
|
276
|
+
})
|
|
268
277
|
|
|
278
|
+
onBeforeMount(()=>{
|
|
279
|
+
theme.value = window.localStorage.getItem('theme') || 'light';
|
|
280
|
+
document.documentElement.classList.toggle('dark', theme.value === 'dark');
|
|
269
281
|
})
|
|
270
282
|
|
|
271
283
|
</script>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { FrontendAPIInterface, ConfirmParams, AlertParams,
|
|
1
|
+
import type { FrontendAPIInterface, ConfirmParams, AlertParams, } from '../types/FrontendAPI';
|
|
2
|
+
import type { AdminForthFilterOperators } from '@/types/AdminForthConfig';
|
|
2
3
|
import { useToastStore } from '../stores/toast';
|
|
3
4
|
import { useModalStore } from '../stores/modal';
|
|
4
5
|
import { useCoreStore } from '@/stores/core';
|
|
@@ -6,9 +7,20 @@ import { useFiltersStore } from '@/stores/filters';
|
|
|
6
7
|
import router from '@/router'
|
|
7
8
|
import type { AdminForthResourceColumn } from '@/types/AdminForthConfig';
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
type FilterParams = {
|
|
11
|
+
/**
|
|
12
|
+
* Field of resource to filter
|
|
13
|
+
*/
|
|
14
|
+
field: string;
|
|
15
|
+
/**
|
|
16
|
+
* Operator of filter
|
|
17
|
+
*/
|
|
18
|
+
operator: AdminForthFilterOperators;
|
|
19
|
+
/**
|
|
20
|
+
* Value of filter
|
|
21
|
+
*/
|
|
22
|
+
value: string | number | boolean ;
|
|
23
|
+
}
|
|
12
24
|
|
|
13
25
|
declare global {
|
|
14
26
|
interface Window {
|
package/dist/spa/spa/src/main.ts
CHANGED