adminforth 1.3.19 → 1.3.21
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 +10 -7
- package/dist/auth.js +10 -8
- package/dist/modules/codeInjector.js +2 -2
- package/dist/modules/configValidator.js +6 -0
- package/dist/modules/restApi.js +2 -0
- package/index.ts +1 -1
- package/modules/codeInjector.ts +7 -1
- package/modules/configValidator.ts +7 -0
- package/modules/restApi.ts +3 -0
- package/package.json +1 -1
- package/spa/src/App.vue +5 -1
- package/spa/src/components/ResourceForm.vue +8 -11
- package/spa/src/components/ValueRenderer.vue +11 -1
- package/spa/src/views/ListView.vue +0 -1
- package/spa/src/views/LoginView.vue +15 -0
- package/types/AdminForthConfig.ts +17 -2
package/auth.ts
CHANGED
|
@@ -63,7 +63,7 @@ class AdminForthAuth {
|
|
|
63
63
|
return jwt.sign({...payload, t: type}, secret, { expiresIn });
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
async verify(jwtToken: string, mustHaveType: string): Promise<Object> {
|
|
66
|
+
async verify(jwtToken: string, mustHaveType: string, decodeUser: boolean | undefined = true): Promise<Object> {
|
|
67
67
|
// read ADMINFORH_SECRET from environment if not drop error
|
|
68
68
|
const secret = process.env.ADMINFORTH_SECRET;
|
|
69
69
|
if (!secret) {
|
|
@@ -88,13 +88,16 @@ class AdminForthAuth {
|
|
|
88
88
|
console.error(`Invalid token type during verification: ${t}, must be ${mustHaveType}`);
|
|
89
89
|
return null;
|
|
90
90
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
91
|
+
if (decodeUser !== false) {
|
|
92
|
+
const dbUser = await this.adminforth.getUserByPk(pk);
|
|
93
|
+
if (!dbUser) {
|
|
94
|
+
console.error(`User with pk ${pk} not found in database`);
|
|
95
|
+
// will logout user which was deleted
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
decoded.dbUser = dbUser;
|
|
96
99
|
}
|
|
97
|
-
|
|
100
|
+
|
|
98
101
|
return decoded;
|
|
99
102
|
}
|
|
100
103
|
|
package/dist/auth.js
CHANGED
|
@@ -51,8 +51,8 @@ class AdminForthAuth {
|
|
|
51
51
|
const expiresIn = process.env.ADMINFORTH_AUTH_EXPIRESIN || '24h';
|
|
52
52
|
return jwt.sign(Object.assign(Object.assign({}, payload), { t: type }), secret, { expiresIn });
|
|
53
53
|
}
|
|
54
|
-
verify(
|
|
55
|
-
return __awaiter(this,
|
|
54
|
+
verify(jwtToken_1, mustHaveType_1) {
|
|
55
|
+
return __awaiter(this, arguments, void 0, function* (jwtToken, mustHaveType, decodeUser = true) {
|
|
56
56
|
// read ADMINFORH_SECRET from environment if not drop error
|
|
57
57
|
const secret = process.env.ADMINFORTH_SECRET;
|
|
58
58
|
if (!secret) {
|
|
@@ -80,13 +80,15 @@ class AdminForthAuth {
|
|
|
80
80
|
console.error(`Invalid token type during verification: ${t}, must be ${mustHaveType}`);
|
|
81
81
|
return null;
|
|
82
82
|
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
83
|
+
if (decodeUser !== false) {
|
|
84
|
+
const dbUser = yield this.adminforth.getUserByPk(pk);
|
|
85
|
+
if (!dbUser) {
|
|
86
|
+
console.error(`User with pk ${pk} not found in database`);
|
|
87
|
+
// will logout user which was deleted
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
decoded.dbUser = dbUser;
|
|
88
91
|
}
|
|
89
|
-
decoded.dbUser = dbUser;
|
|
90
92
|
return decoded;
|
|
91
93
|
});
|
|
92
94
|
}
|
|
@@ -195,12 +195,12 @@ class CodeInjector {
|
|
|
195
195
|
const registerCustomPages = (config) => {
|
|
196
196
|
if (config.customization.customPages) {
|
|
197
197
|
config.customization.customPages.forEach((page) => {
|
|
198
|
-
var _a, _b, _c
|
|
198
|
+
var _a, _b, _c;
|
|
199
199
|
routes += `{
|
|
200
200
|
path: '${page.path}',
|
|
201
201
|
name: '${page.path}',
|
|
202
202
|
component: () => import('${((_a = page === null || page === void 0 ? void 0 : page.component) === null || _a === void 0 ? void 0 : _a.file) || page.component}'),
|
|
203
|
-
meta:
|
|
203
|
+
meta: ${JSON.stringify(Object.assign(Object.assign({}, (((_b = page === null || page === void 0 ? void 0 : page.component) === null || _b === void 0 ? void 0 : _b.meta) || {})), { title: ((_c = page.meta) === null || _c === void 0 ? void 0 : _c.title) || page.path.replace('/', '') }))}
|
|
204
204
|
},`;
|
|
205
205
|
});
|
|
206
206
|
}
|
|
@@ -119,6 +119,12 @@ export default class ConfigValidator {
|
|
|
119
119
|
if (((_a = this.config) === null || _a === void 0 ? void 0 : _a.customization.brandName) === undefined) {
|
|
120
120
|
this.config.customization.brandName = 'AdminForth';
|
|
121
121
|
}
|
|
122
|
+
if (this.config.customization.loginPageInjections === undefined) {
|
|
123
|
+
this.config.customization.loginPageInjections = {};
|
|
124
|
+
}
|
|
125
|
+
if (this.config.customization.loginPageInjections.underInputs === undefined) {
|
|
126
|
+
this.config.customization.loginPageInjections.underInputs = [];
|
|
127
|
+
}
|
|
122
128
|
if (this.config.customization.brandLogo) {
|
|
123
129
|
errors.push(...this.checkCustomFileExists(this.config.customization.brandLogo));
|
|
124
130
|
}
|
package/dist/modules/restApi.js
CHANGED
|
@@ -71,6 +71,7 @@ export default class AdminForthRestAPI {
|
|
|
71
71
|
offset: 0,
|
|
72
72
|
sort: [],
|
|
73
73
|
})).data) === null || _b === void 0 ? void 0 : _b[0];
|
|
74
|
+
console.log('userRecord', userRecord, this.adminforth.config.auth.usernameField, username);
|
|
74
75
|
if (!userRecord) {
|
|
75
76
|
return { error: 'User not found' };
|
|
76
77
|
}
|
|
@@ -139,6 +140,7 @@ export default class AdminForthRestAPI {
|
|
|
139
140
|
title: (_j = this.adminforth.config.customization) === null || _j === void 0 ? void 0 : _j.title,
|
|
140
141
|
demoCredentials: this.adminforth.config.auth.demoCredentials,
|
|
141
142
|
loginPromptHTML: this.adminforth.config.auth.loginPromptHTML,
|
|
143
|
+
loginPageInjections: this.adminforth.config.customization.loginPageInjections,
|
|
142
144
|
};
|
|
143
145
|
}),
|
|
144
146
|
});
|
package/index.ts
CHANGED
|
@@ -264,7 +264,7 @@ class AdminForth implements IAdminForth {
|
|
|
264
264
|
return { ok, error, createdRecord };
|
|
265
265
|
}
|
|
266
266
|
|
|
267
|
-
resource(resourceId: string) {
|
|
267
|
+
resource(resourceId: string): IOperationalResource {
|
|
268
268
|
if (this.statuses.dbDiscover !== 'done') {
|
|
269
269
|
if (this.statuses.dbDiscover === 'running') {
|
|
270
270
|
throw new Error('Database discovery is running. You can\'t use data API while database discovery is not finished.\n'+
|
package/modules/codeInjector.ts
CHANGED
|
@@ -10,6 +10,7 @@ import AdminForth from '../index.js';
|
|
|
10
10
|
import { ADMIN_FORTH_ABSOLUTE_PATH, getComponentNameFromPath, transformObject, deepMerge } from './utils.js';
|
|
11
11
|
import { ICodeInjector } from '../types/AdminForthConfig.js';
|
|
12
12
|
import { StylesGenerator } from './styleGenerator.js';
|
|
13
|
+
import { title } from 'process';
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
let TMP_DIR;
|
|
@@ -211,7 +212,12 @@ class CodeInjector implements ICodeInjector {
|
|
|
211
212
|
path: '${page.path}',
|
|
212
213
|
name: '${page.path}',
|
|
213
214
|
component: () => import('${page?.component?.file || page.component}'),
|
|
214
|
-
meta:
|
|
215
|
+
meta: ${
|
|
216
|
+
JSON.stringify({
|
|
217
|
+
...(page?.component?.meta || {}),
|
|
218
|
+
title: page.meta?.title || page.path.replace('/', '')
|
|
219
|
+
})
|
|
220
|
+
}
|
|
215
221
|
},`})
|
|
216
222
|
|
|
217
223
|
}}
|
|
@@ -127,6 +127,13 @@ export default class ConfigValidator implements IConfigValidator {
|
|
|
127
127
|
if (this.config?.customization.brandName === undefined) {
|
|
128
128
|
this.config.customization.brandName = 'AdminForth';
|
|
129
129
|
}
|
|
130
|
+
if (this.config.customization.loginPageInjections === undefined) {
|
|
131
|
+
this.config.customization.loginPageInjections = {};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (this.config.customization.loginPageInjections.underInputs === undefined) {
|
|
135
|
+
this.config.customization.loginPageInjections.underInputs = [];
|
|
136
|
+
}
|
|
130
137
|
if (this.config.customization.brandLogo) {
|
|
131
138
|
errors.push(...this.checkCustomFileExists(this.config.customization.brandLogo));
|
|
132
139
|
}
|
package/modules/restApi.ts
CHANGED
|
@@ -95,6 +95,8 @@ export default class AdminForthRestAPI {
|
|
|
95
95
|
})
|
|
96
96
|
).data?.[0];
|
|
97
97
|
|
|
98
|
+
console.log('userRecord', userRecord, this.adminforth.config.auth.usernameField, username);
|
|
99
|
+
|
|
98
100
|
if (!userRecord) {
|
|
99
101
|
return { error: 'User not found' };
|
|
100
102
|
}
|
|
@@ -170,6 +172,7 @@ export default class AdminForthRestAPI {
|
|
|
170
172
|
title: this.adminforth.config.customization?.title,
|
|
171
173
|
demoCredentials: this.adminforth.config.auth.demoCredentials,
|
|
172
174
|
loginPromptHTML: this.adminforth.config.auth.loginPromptHTML,
|
|
175
|
+
loginPageInjections: this.adminforth.config.customization.loginPageInjections,
|
|
173
176
|
};
|
|
174
177
|
},
|
|
175
178
|
});
|
package/package.json
CHANGED
package/spa/src/App.vue
CHANGED
|
@@ -297,7 +297,11 @@ async function initRouter() {
|
|
|
297
297
|
}
|
|
298
298
|
|
|
299
299
|
async function loadMenu() {
|
|
300
|
-
await
|
|
300
|
+
await initRouter();
|
|
301
|
+
if (!route.meta.customLayout) {
|
|
302
|
+
// for custom layouts we don't need to fetch menu
|
|
303
|
+
await coreStore.fetchMenuAndResource();
|
|
304
|
+
}
|
|
301
305
|
loginRedirectCheckIsReady.value = true;
|
|
302
306
|
}
|
|
303
307
|
|
|
@@ -6,16 +6,14 @@
|
|
|
6
6
|
<form autocomplete="off" @submit.prevent>
|
|
7
7
|
<table class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400 ">
|
|
8
8
|
<thead class="text-xs text-gray-700 uppercase bg-lightFormHeading dark:bg-gray-700 dark:text-gray-400 block md:table-row-group">
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
</tr>
|
|
9
|
+
<tr>
|
|
10
|
+
<th scope="col" class="px-6 py-3 hidden md:table-cell">
|
|
11
|
+
Field
|
|
12
|
+
</th>
|
|
13
|
+
<th scope="col" class="px-6 py-3 w-5/6 hidden md:table-cell">
|
|
14
|
+
Value
|
|
15
|
+
</th>
|
|
16
|
+
</tr>
|
|
19
17
|
</thead>
|
|
20
18
|
<tbody>
|
|
21
19
|
<tr v-for="column, i in editableColumns" :key="column.name"
|
|
@@ -147,7 +145,6 @@
|
|
|
147
145
|
|
|
148
146
|
import CustomDatePicker from "@/components/CustomDatePicker.vue";
|
|
149
147
|
import Dropdown from '@/components/Dropdown.vue';
|
|
150
|
-
import { useCoreStore } from '@/stores/core';
|
|
151
148
|
import { callAdminForthApi, getCustomComponent } from '@/utils';
|
|
152
149
|
import { IconExclamationCircleSolid, IconEyeSlashSolid, IconEyeSolid } from '@iconify-prerendered/vue-flowbite';
|
|
153
150
|
import { computedAsync } from '@vueuse/core';
|
|
@@ -56,8 +56,18 @@ const props = defineProps({
|
|
|
56
56
|
|
|
57
57
|
function protectAgainstXSS(value) {
|
|
58
58
|
return sanitizeHtml(value, {
|
|
59
|
+
allowedTags: [
|
|
60
|
+
"address", "article", "aside", "footer", "header", "h1", "h2", "h3", "h4",
|
|
61
|
+
"h5", "h6", "hgroup", "main", "nav", "section", "blockquote", "dd", "div",
|
|
62
|
+
"dl", "dt", "figcaption", "figure", "hr", "li", "main", "ol", "p", "pre",
|
|
63
|
+
"ul", "a", "abbr", "b", "bdi", "bdo", "br", "cite", "code", "data", "dfn",
|
|
64
|
+
"em", "i", "kbd", "mark", "q", "rb", "rp", "rt", "rtc", "ruby", "s", "samp",
|
|
65
|
+
"small", "span", "strong", "sub", "sup", "time", "u", "var", "wbr", "caption",
|
|
66
|
+
"col", "colgroup", "table", "tbody", "td", "tfoot", "th", "thead", "tr", 'img'
|
|
67
|
+
],
|
|
59
68
|
allowedAttributes: {
|
|
60
|
-
|
|
69
|
+
'li': [ 'data-list' ],
|
|
70
|
+
'img': [ 'src', 'srcset', 'alt', 'title', 'width', 'height', 'loading' ]
|
|
61
71
|
}
|
|
62
72
|
});
|
|
63
73
|
}
|
|
@@ -110,7 +110,6 @@
|
|
|
110
110
|
import BreadcrumbsWithButtons from '@/components/BreadcrumbsWithButtons.vue';
|
|
111
111
|
import ResourceListTable from '@/components/ResourceListTable.vue';
|
|
112
112
|
import { useCoreStore } from '@/stores/core';
|
|
113
|
-
import { useModalStore } from '@/stores/modal';
|
|
114
113
|
import { useFiltersStore } from '@/stores/filters';
|
|
115
114
|
import { callAdminForthApi, getIcon } from '@/utils';
|
|
116
115
|
import { initFlowbite } from 'flowbite';
|
|
@@ -56,6 +56,19 @@
|
|
|
56
56
|
</button>
|
|
57
57
|
</div>
|
|
58
58
|
|
|
59
|
+
<div class="flex items-start mb-5 hidden">
|
|
60
|
+
<div class="flex items-center h-5">
|
|
61
|
+
<input id="remember" type="checkbox" value="" class="w-4 h-4 border border-gray-300 rounded bg-gray-50 focus:ring-3 focus:ring-blue-300 dark:bg-gray-700 dark:border-gray-600 dark:focus:ring-blue-600 dark:ring-offset-gray-800 dark:focus:ring-offset-gray-800" required />
|
|
62
|
+
</div>
|
|
63
|
+
<label for="remember" class="ms-2 text-sm font-medium text-gray-900 dark:text-gray-300">Remember me</label>
|
|
64
|
+
</div>
|
|
65
|
+
|
|
66
|
+
<component
|
|
67
|
+
v-for="c in coreStore?.config?.loginPageInjections?.underInputs || []"
|
|
68
|
+
:is="getCustomComponent(c)"
|
|
69
|
+
:meta="c.meta"
|
|
70
|
+
/>
|
|
71
|
+
|
|
59
72
|
<div v-if="error" class="flex items-center p-4 mb-4 text-sm text-red-800 rounded-lg bg-red-50 dark:bg-gray-800 dark:text-red-400" role="alert">
|
|
60
73
|
<svg class="flex-shrink-0 inline w-4 h-4 me-3" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
|
|
61
74
|
<path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM9.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM12 15H8a1 1 0 0 1 0-2h1v-3H8a1 1 0 0 1 0-2h2a1 1 0 0 1 1 1v4h1a1 1 0 0 1 0 2Z"/>
|
|
@@ -66,6 +79,7 @@
|
|
|
66
79
|
</div>
|
|
67
80
|
</div>
|
|
68
81
|
|
|
82
|
+
|
|
69
83
|
|
|
70
84
|
<div v-if="coreStore.config?.loginPromptHTML"
|
|
71
85
|
class="flex items-center p-4 mb-4 text-sm text-gray-800 rounded-lg bg-gray-50 dark:bg-gray-800 dark:text-gray-400" role="alert"
|
|
@@ -101,6 +115,7 @@
|
|
|
101
115
|
|
|
102
116
|
<script setup>
|
|
103
117
|
|
|
118
|
+
import { getCustomComponent } from '@/utils';
|
|
104
119
|
import { onMounted, ref, computed } from 'vue';
|
|
105
120
|
import { useCoreStore } from '@/stores/core';
|
|
106
121
|
import { useUserStore } from '@/stores/user';
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Express } from 'express';
|
|
2
|
+
import OperationalResource from '../modules/operationalResource.js';
|
|
2
3
|
|
|
3
4
|
export interface ICodeInjector {
|
|
4
5
|
srcFoldersToSync: Object;
|
|
@@ -237,7 +238,7 @@ export interface IAdminForthDataSourceConnectorConstructor {
|
|
|
237
238
|
}
|
|
238
239
|
|
|
239
240
|
export interface IAdminForthAuth {
|
|
240
|
-
verify(jwt : string, mustHaveType: string): Promise<any>;
|
|
241
|
+
verify(jwt : string, mustHaveType: string, decodeUser?: boolean): Promise<any>;
|
|
241
242
|
issueJWT(payload: Object, type: string): string;
|
|
242
243
|
|
|
243
244
|
removeCustomCookie({response, name}: {response: any, name: string}): void;
|
|
@@ -291,6 +292,11 @@ export interface IAdminForth {
|
|
|
291
292
|
*/
|
|
292
293
|
bundleNow({ hotReload, verbose }: { hotReload: boolean, verbose: boolean }): Promise<void>;
|
|
293
294
|
|
|
295
|
+
/**
|
|
296
|
+
* Resource to get access to operational resources for data api fetching and manipulation.
|
|
297
|
+
*/
|
|
298
|
+
resource(resourceId: string): IOperationalResource;
|
|
299
|
+
|
|
294
300
|
/**
|
|
295
301
|
* This method will be automatically called from AdminForth HTTP adapter to serve AdminForth SPA.
|
|
296
302
|
*/
|
|
@@ -1326,6 +1332,15 @@ export type AdminForthConfig = {
|
|
|
1326
1332
|
* Execution is done on admin app load.
|
|
1327
1333
|
*/
|
|
1328
1334
|
announcementBadge?: (user: AdminUser) => { text?: string, html?: string, closable?: boolean, title?: string } | null,
|
|
1335
|
+
|
|
1336
|
+
/**
|
|
1337
|
+
* Custom panel components or array of components which will be displayed in the login form
|
|
1338
|
+
* right after the inputs. Use it to add custom authorization methods like social login or other custom fields e.g. 'reset'
|
|
1339
|
+
* password link.
|
|
1340
|
+
*/
|
|
1341
|
+
loginPageInjections?: {
|
|
1342
|
+
underInputs?: AdminForthComponentDeclaration | Array<AdminForthComponentDeclaration>,
|
|
1343
|
+
}
|
|
1329
1344
|
}
|
|
1330
1345
|
|
|
1331
1346
|
/**
|
|
@@ -1400,7 +1415,7 @@ export interface IOperationalResource {
|
|
|
1400
1415
|
|
|
1401
1416
|
list: (filter: IAdminForthFilter | IAdminForthFilter[], limit: number, offset: number, sort: IAdminForthSort | IAdminForthSort[]) => Promise<any[]>;
|
|
1402
1417
|
|
|
1403
|
-
count: (filter: IAdminForthFilter | IAdminForthFilter[]) => Promise<number>;
|
|
1418
|
+
count: (filter: IAdminForthFilter | IAdminForthFilter[] | undefined) => Promise<number>;
|
|
1404
1419
|
|
|
1405
1420
|
create: (record: any) => Promise<{ ok: boolean; createdRecord: any; error?: string; }>;
|
|
1406
1421
|
|