adminforth 1.0.73 → 1.0.75
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/dataConnectors/mongo.ts +5 -6
- package/dataConnectors/postgres.ts +16 -15
- package/dataConnectors/sqlite.ts +17 -12
- package/dist/dataConnectors/mongo.js +5 -5
- package/dist/dataConnectors/postgres.js +15 -15
- package/dist/dataConnectors/sqlite.js +15 -12
- package/dist/index.js +36 -13
- package/dist/modules/codeInjector.js +33 -7
- package/dist/servers/express.js +6 -8
- package/dist/spa/spa/src/main.ts +1 -1
- package/dist/spa/spa/src/utils.ts +11 -0
- package/dist/spa/spa/src/views/LoginView.vue +5 -2
- package/dist/types/AdminForthConfig.js +33 -1
- package/index.ts +40 -17
- package/modules/codeInjector.ts +38 -7
- package/package.json +2 -1
- package/plugins/base.ts +4 -5
- package/servers/express.ts +7 -9
- package/spa/src/main.ts +1 -1
- package/spa/src/utils.ts +11 -0
- package/spa/src/views/LoginView.vue +5 -2
- package/types/AdminForthConfig.ts +53 -14
- package/types.ts +0 -36
|
@@ -29,10 +29,29 @@ function hashify(obj) {
|
|
|
29
29
|
return crypto.createHash('sha256').update(JSON.stringify(obj)).digest('hex');
|
|
30
30
|
}
|
|
31
31
|
class CodeInjector {
|
|
32
|
+
cleanup() {
|
|
33
|
+
console.log('Cleaning up...');
|
|
34
|
+
this.allWatchers.forEach((watcher) => {
|
|
35
|
+
watcher.removeAll();
|
|
36
|
+
});
|
|
37
|
+
}
|
|
32
38
|
constructor(adminforth) {
|
|
39
|
+
this.allWatchers = [];
|
|
33
40
|
this.allComponentNames = {};
|
|
34
41
|
this.srcFoldersToSync = {};
|
|
35
42
|
this.adminforth = adminforth;
|
|
43
|
+
process.on('SIGINT', () => {
|
|
44
|
+
console.log('Received SIGINT.');
|
|
45
|
+
this.cleanup();
|
|
46
|
+
});
|
|
47
|
+
process.on('SIGTERM', () => {
|
|
48
|
+
console.log('Received SIGTERM.');
|
|
49
|
+
this.cleanup();
|
|
50
|
+
});
|
|
51
|
+
process.on('exit', () => {
|
|
52
|
+
console.log('Exiting.');
|
|
53
|
+
this.cleanup();
|
|
54
|
+
});
|
|
36
55
|
}
|
|
37
56
|
// async runShell({command, verbose = false}) {
|
|
38
57
|
// console.log(`⚙️ Running shell ${command}...`);
|
|
@@ -144,7 +163,10 @@ class CodeInjector {
|
|
|
144
163
|
yield Promise.all(filesUpdated.map((file) => __awaiter(this, void 0, void 0, function* () {
|
|
145
164
|
const src = path.join(ADMIN_FORTH_ABSOLUTE_PATH, 'spa', file);
|
|
146
165
|
const dest = path.join(CodeInjector.SPA_TMP_PATH, file);
|
|
147
|
-
yield fsExtra.copy(src, dest
|
|
166
|
+
yield fsExtra.copy(src, dest, {
|
|
167
|
+
overwrite: true,
|
|
168
|
+
dereference: true, // needed to dereference types
|
|
169
|
+
});
|
|
148
170
|
if (process.env.HEAVY_DEBUG) {
|
|
149
171
|
console.log('🪲 await fsExtra.copy filtering', src, dest);
|
|
150
172
|
}
|
|
@@ -154,6 +176,13 @@ class CodeInjector {
|
|
|
154
176
|
if (process.env.HEAVY_DEBUG) {
|
|
155
177
|
console.log(`🪲 await fsExtra.copy from ${path.join(ADMIN_FORTH_ABSOLUTE_PATH, 'spa')}, ${CodeInjector.SPA_TMP_PATH}`);
|
|
156
178
|
}
|
|
179
|
+
// try to rm SPA_TMP_PATH/src/types directory
|
|
180
|
+
try {
|
|
181
|
+
yield fs.promises.rm(path.join(CodeInjector.SPA_TMP_PATH, 'src', 'types'), { recursive: true });
|
|
182
|
+
}
|
|
183
|
+
catch (e) {
|
|
184
|
+
// ignore
|
|
185
|
+
}
|
|
157
186
|
yield fsExtra.copy(path.join(ADMIN_FORTH_ABSOLUTE_PATH, 'spa'), CodeInjector.SPA_TMP_PATH, {
|
|
158
187
|
filter: (src) => {
|
|
159
188
|
if (process.env.HEAVY_DEBUG) {
|
|
@@ -162,6 +191,7 @@ class CodeInjector {
|
|
|
162
191
|
return !src.includes('/adminforth/spa/node_modules') && !src.includes('/adminforth/spa/dist');
|
|
163
192
|
},
|
|
164
193
|
overwrite: true,
|
|
194
|
+
dereference: true, // needed to dereference types
|
|
165
195
|
});
|
|
166
196
|
// copy whole custom directory
|
|
167
197
|
if ((_b = this.adminforth.config.customization) === null || _b === void 0 ? void 0 : _b.customComponentsDir) {
|
|
@@ -358,9 +388,7 @@ class CodeInjector {
|
|
|
358
388
|
console.log(`File ${file} changed, preparing sources...`);
|
|
359
389
|
yield this.prepareSources({ filesUpdated: [file.replace(spaPath + '/', '')] });
|
|
360
390
|
}));
|
|
361
|
-
|
|
362
|
-
watcher.removeAll();
|
|
363
|
-
});
|
|
391
|
+
this.allWatchers.push(watcher);
|
|
364
392
|
});
|
|
365
393
|
}
|
|
366
394
|
watchCustomComponentsForCopy(_a) {
|
|
@@ -405,9 +433,7 @@ class CodeInjector {
|
|
|
405
433
|
recursive: true,
|
|
406
434
|
});
|
|
407
435
|
}));
|
|
408
|
-
|
|
409
|
-
watcher.removeAll();
|
|
410
|
-
});
|
|
436
|
+
this.allWatchers.push(watcher);
|
|
411
437
|
});
|
|
412
438
|
}
|
|
413
439
|
bundleNow(_a) {
|
package/dist/servers/express.js
CHANGED
|
@@ -11,6 +11,7 @@ import path from 'path';
|
|
|
11
11
|
import { fileURLToPath } from 'url';
|
|
12
12
|
import fs from 'fs';
|
|
13
13
|
import CodeInjector from '../modules/codeInjector.js';
|
|
14
|
+
import fetch from 'node-fetch';
|
|
14
15
|
const __filename = fileURLToPath(import.meta.url);
|
|
15
16
|
const __dirname = path.dirname(__filename);
|
|
16
17
|
function replaceAtStart(string, substring) {
|
|
@@ -21,13 +22,9 @@ function replaceAtStart(string, substring) {
|
|
|
21
22
|
}
|
|
22
23
|
function proxyTo(url, res) {
|
|
23
24
|
return __awaiter(this, void 0, void 0, function* () {
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
r.headers.forEach((value, name) => {
|
|
28
|
-
res.setHeader(name, value);
|
|
29
|
-
});
|
|
30
|
-
res.send(body);
|
|
25
|
+
const actual = yield fetch(url);
|
|
26
|
+
actual.headers.forEach((v, n) => res.setHeader(n, v));
|
|
27
|
+
actual.body.pipe(res);
|
|
31
28
|
});
|
|
32
29
|
}
|
|
33
30
|
function parseExpressCookie(req) {
|
|
@@ -93,11 +90,12 @@ class ExpressServer {
|
|
|
93
90
|
const slashedPrefix = prefix.endsWith('/') ? prefix : `${prefix}/`;
|
|
94
91
|
if (this.adminforth.runningHotReload) {
|
|
95
92
|
const handler = (req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
96
|
-
// proxy using fetch to webpack dev server
|
|
93
|
+
// proxy using fetch to webpack dev server
|
|
97
94
|
try {
|
|
98
95
|
yield proxyTo(`http://localhost:5173${req.url}`, res);
|
|
99
96
|
}
|
|
100
97
|
catch (e) {
|
|
98
|
+
// console.log('Failed to proxy', e);
|
|
101
99
|
res.status(500).send(respondNoServer('AdminForth SPA is not ready yet', 'Vite is still starting up. Please wait a moment...'));
|
|
102
100
|
return;
|
|
103
101
|
}
|
package/dist/spa/spa/src/main.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { onMounted, ref, resolveComponent } from 'vue';
|
|
2
2
|
|
|
3
3
|
import router from "./router";
|
|
4
|
+
import { useCoreStore } from './stores/core';
|
|
4
5
|
|
|
5
6
|
export async function callApi({path, method, body=undefined} ) {
|
|
6
7
|
const options = {
|
|
@@ -62,3 +63,13 @@ export const loadFile = (file: string) => {
|
|
|
62
63
|
}
|
|
63
64
|
return baseUrl;
|
|
64
65
|
}
|
|
66
|
+
|
|
67
|
+
// export function checkEmptyValues(value: any, viewType:'show' | 'list' | 'create' | 'edit') {
|
|
68
|
+
// const config: = useCoreStore().config;
|
|
69
|
+
// const emptyFieldPlaceholder = config.emptyFieldPlaceholder?.[viewType] || '---';
|
|
70
|
+
|
|
71
|
+
// if (value === null || value === undefined || value === '') {
|
|
72
|
+
// return emptyFieldPlaceholder;
|
|
73
|
+
// }
|
|
74
|
+
// return value;
|
|
75
|
+
// }
|
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
'background-blend-mode': 'darken'
|
|
8
8
|
}: {}"
|
|
9
9
|
>
|
|
10
|
-
|
|
10
|
+
|
|
11
|
+
<!-- Main modal -->
|
|
11
12
|
<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
13
|
<div class="relative p-4 w-full max-w-md max-h-full">
|
|
13
14
|
<!-- Modal content -->
|
|
@@ -78,13 +79,14 @@
|
|
|
78
79
|
|
|
79
80
|
<script setup>
|
|
80
81
|
|
|
81
|
-
import { onMounted, ref } from 'vue';
|
|
82
|
+
import { onMounted, ref, watchEffect } from 'vue';
|
|
82
83
|
import { useCoreStore } from '@/stores/core';
|
|
83
84
|
import { IconEyeSolid, IconEyeSlashSolid } from '@iconify-prerendered/vue-flowbite';
|
|
84
85
|
import { callAdminForthApi, loadFile } from '@/utils';
|
|
85
86
|
import { useRouter } from 'vue-router';
|
|
86
87
|
import { initFlowbite } from 'flowbite'
|
|
87
88
|
|
|
89
|
+
|
|
88
90
|
const router = useRouter();
|
|
89
91
|
const inProgress = ref(false);
|
|
90
92
|
|
|
@@ -114,6 +116,7 @@ async function login() {
|
|
|
114
116
|
} else {
|
|
115
117
|
error.value = null;
|
|
116
118
|
router.push('/');
|
|
119
|
+
await router.isReady();
|
|
117
120
|
await coreStore.fetchMenuAndResource();
|
|
118
121
|
setTimeout(() => {
|
|
119
122
|
initFlowbite();
|
|
@@ -1 +1,33 @@
|
|
|
1
|
-
export
|
|
1
|
+
export var AdminForthDataTypes;
|
|
2
|
+
(function (AdminForthDataTypes) {
|
|
3
|
+
AdminForthDataTypes["STRING"] = "string";
|
|
4
|
+
AdminForthDataTypes["INTEGER"] = "integer";
|
|
5
|
+
AdminForthDataTypes["FLOAT"] = "float";
|
|
6
|
+
AdminForthDataTypes["DECIMAL"] = "decimal";
|
|
7
|
+
AdminForthDataTypes["BOOLEAN"] = "boolean";
|
|
8
|
+
AdminForthDataTypes["DATE"] = "date";
|
|
9
|
+
AdminForthDataTypes["DATETIME"] = "datetime";
|
|
10
|
+
AdminForthDataTypes["TIME"] = "time";
|
|
11
|
+
AdminForthDataTypes["TEXT"] = "text";
|
|
12
|
+
AdminForthDataTypes["JSON"] = "json";
|
|
13
|
+
})(AdminForthDataTypes || (AdminForthDataTypes = {}));
|
|
14
|
+
export var AdminForthFilterOperators;
|
|
15
|
+
(function (AdminForthFilterOperators) {
|
|
16
|
+
AdminForthFilterOperators["EQ"] = "eq";
|
|
17
|
+
AdminForthFilterOperators["NE"] = "ne";
|
|
18
|
+
AdminForthFilterOperators["GT"] = "gt";
|
|
19
|
+
AdminForthFilterOperators["LT"] = "lt";
|
|
20
|
+
AdminForthFilterOperators["GTE"] = "gte";
|
|
21
|
+
AdminForthFilterOperators["LTE"] = "lte";
|
|
22
|
+
AdminForthFilterOperators["LIKE"] = "like";
|
|
23
|
+
AdminForthFilterOperators["ILIKE"] = "ilike";
|
|
24
|
+
AdminForthFilterOperators["IN"] = "in";
|
|
25
|
+
AdminForthFilterOperators["NIN"] = "nin";
|
|
26
|
+
})(AdminForthFilterOperators || (AdminForthFilterOperators = {}));
|
|
27
|
+
;
|
|
28
|
+
export var AdminForthSortDirections;
|
|
29
|
+
(function (AdminForthSortDirections) {
|
|
30
|
+
AdminForthSortDirections["ASC"] = "asc";
|
|
31
|
+
AdminForthSortDirections["DESC"] = "desc";
|
|
32
|
+
})(AdminForthSortDirections || (AdminForthSortDirections = {}));
|
|
33
|
+
;
|
package/index.ts
CHANGED
|
@@ -9,9 +9,9 @@ import ExpressServer from './servers/express.js';
|
|
|
9
9
|
import {v1 as uuid} from 'uuid';
|
|
10
10
|
import fs from 'fs';
|
|
11
11
|
import { ADMINFORTH_VERSION } from './modules/utils.js';
|
|
12
|
-
import {
|
|
13
|
-
import { AdminForthConfig } from './types/AdminForthConfig.js';
|
|
12
|
+
import { AdminForthConfig, AdminForthClass, AdminForthFilterOperators, AdminForthDataTypes } from './types/AdminForthConfig.js';
|
|
14
13
|
import { getFunctionList } from './modules/utils.js';
|
|
14
|
+
import path from 'path';
|
|
15
15
|
|
|
16
16
|
const AVAILABLE_SHOW_IN = ['list', 'edit', 'create', 'filter', 'show'];
|
|
17
17
|
const DEFAULT_ALLOWED_ACTIONS = {create: true, edit: true, show: true, delete: true};
|
|
@@ -22,9 +22,8 @@ type ValidationObject = {
|
|
|
22
22
|
message: string,
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
class AdminForth {
|
|
26
|
-
static Types =
|
|
27
|
-
|
|
25
|
+
class AdminForth implements AdminForthClass {
|
|
26
|
+
static Types = AdminForthDataTypes;
|
|
28
27
|
|
|
29
28
|
static Utils = {
|
|
30
29
|
generatePasswordHash: async (password) => {
|
|
@@ -48,7 +47,6 @@ class AdminForth {
|
|
|
48
47
|
dbDiscover?: 'running' | 'done',
|
|
49
48
|
}
|
|
50
49
|
|
|
51
|
-
|
|
52
50
|
constructor(config: AdminForthConfig) {
|
|
53
51
|
this.config = {...this.#defaultConfig,...config};
|
|
54
52
|
this.codeInjector = new CodeInjector(this);
|
|
@@ -72,7 +70,19 @@ class AdminForth {
|
|
|
72
70
|
};
|
|
73
71
|
}
|
|
74
72
|
|
|
73
|
+
checkCustomFileExists(filePath: string): Array<string> {
|
|
74
|
+
if (filePath.startsWith('@@/')) {
|
|
75
|
+
const checkPath = path.join(this.config.customization.customComponentsDir, filePath.replace('@@/', ''));
|
|
76
|
+
if (!fs.existsSync(checkPath)) {
|
|
77
|
+
return [`File file ${filePath} does not exist in ${this.config.customization.customComponentsDir}`];
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
|
|
75
83
|
validateConfig() {
|
|
84
|
+
const errors = [];
|
|
85
|
+
|
|
76
86
|
if (this.config.rootUser) {
|
|
77
87
|
if (!this.config.rootUser.username) {
|
|
78
88
|
throw new Error('rootUser.username is required');
|
|
@@ -91,6 +101,12 @@ class AdminForth {
|
|
|
91
101
|
if (!this.config.auth.passwordHashField) {
|
|
92
102
|
throw new Error('No config.auth.passwordHashField defined');
|
|
93
103
|
}
|
|
104
|
+
if (!this.config.auth.usernameField) {
|
|
105
|
+
throw new Error('No config.auth.usernameField defined');
|
|
106
|
+
}
|
|
107
|
+
if (this.config.auth.loginBackgroundImage) {
|
|
108
|
+
errors.push(...this.checkCustomFileExists(this.config.auth.loginBackgroundImage));
|
|
109
|
+
}
|
|
94
110
|
const userResource = this.config.resources.find((res) => res.resourceId === this.config.auth.resourceId);
|
|
95
111
|
if (!userResource) {
|
|
96
112
|
throw new Error(`Resource with id "${this.config.auth.resourceId}" not found`);
|
|
@@ -106,7 +122,6 @@ class AdminForth {
|
|
|
106
122
|
}
|
|
107
123
|
|
|
108
124
|
|
|
109
|
-
const errors = [];
|
|
110
125
|
if (!this.config.baseUrl) {
|
|
111
126
|
this.config.baseUrl = '';
|
|
112
127
|
}
|
|
@@ -114,10 +129,7 @@ class AdminForth {
|
|
|
114
129
|
this.config.customization.brandName = 'AdminForth';
|
|
115
130
|
}
|
|
116
131
|
if (this.config.customization.brandLogo) {
|
|
117
|
-
|
|
118
|
-
errors.push(`Brand logo must start with @@ and be placed in custom directory`);
|
|
119
|
-
}
|
|
120
|
-
// todo check file exist
|
|
132
|
+
errors.push(...this.checkCustomFileExists(this.config.customization.brandLogo));
|
|
121
133
|
}
|
|
122
134
|
|
|
123
135
|
|
|
@@ -422,6 +434,17 @@ class AdminForth {
|
|
|
422
434
|
throw new Error('No config.auth defined');
|
|
423
435
|
}
|
|
424
436
|
const userResource = this.config.resources.find((res) => res.resourceId === this.config.auth.resourceId);
|
|
437
|
+
// if there is no passwordHashField, in columns, add it, with backendOnly and showIn: []
|
|
438
|
+
if (!userResource.dataSourceColumns.find((col) => col.name === this.config.auth.passwordHashField)) {
|
|
439
|
+
userResource.dataSourceColumns.push({
|
|
440
|
+
name: this.config.auth.passwordHashField,
|
|
441
|
+
backendOnly: true,
|
|
442
|
+
showIn: [],
|
|
443
|
+
type: AdminForth.Types.STRING,
|
|
444
|
+
});
|
|
445
|
+
console.log('Adding passwordHashField to userResource', userResource)
|
|
446
|
+
}
|
|
447
|
+
|
|
425
448
|
const userRecord = (
|
|
426
449
|
await this.connectors[userResource.dataSource].getData({
|
|
427
450
|
resource: userResource,
|
|
@@ -775,12 +798,12 @@ class AdminForth {
|
|
|
775
798
|
const item = await this.connectors[resource.dataSource].getMinMaxForColumns({
|
|
776
799
|
resource,
|
|
777
800
|
columns: resource.columns.filter((col) => [
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
801
|
+
AdminForthDataTypes.INTEGER,
|
|
802
|
+
AdminForthDataTypes.FLOAT,
|
|
803
|
+
AdminForthDataTypes.DATE,
|
|
804
|
+
AdminForthDataTypes.DATETIME,
|
|
805
|
+
AdminForthDataTypes.TIME,
|
|
806
|
+
AdminForthDataTypes.DECIMAL,
|
|
784
807
|
].includes(col.type) && col.allowMinMaxQuery === true),
|
|
785
808
|
});
|
|
786
809
|
return item;
|
package/modules/codeInjector.ts
CHANGED
|
@@ -11,6 +11,7 @@ import { ADMIN_FORTH_ABSOLUTE_PATH } from './utils.js';
|
|
|
11
11
|
import { getComponentNameFromPath } from './utils.js';
|
|
12
12
|
|
|
13
13
|
|
|
14
|
+
|
|
14
15
|
let TMP_DIR;
|
|
15
16
|
|
|
16
17
|
try {
|
|
@@ -29,14 +30,36 @@ function hashify(obj) {
|
|
|
29
30
|
|
|
30
31
|
class CodeInjector {
|
|
31
32
|
|
|
33
|
+
allWatchers = [];
|
|
32
34
|
adminforth: AdminForth;
|
|
33
35
|
allComponentNames: { [key: string]: string } = {};
|
|
34
36
|
srcFoldersToSync: { [key: string]: string } = {};
|
|
35
37
|
|
|
36
38
|
static SPA_TMP_PATH = path.join(TMP_DIR, 'adminforth', 'spa_tmp');
|
|
37
39
|
|
|
40
|
+
cleanup() {
|
|
41
|
+
console.log('Cleaning up...');
|
|
42
|
+
this.allWatchers.forEach((watcher) => {
|
|
43
|
+
watcher.removeAll();
|
|
44
|
+
});
|
|
45
|
+
}
|
|
38
46
|
constructor(adminforth) {
|
|
39
47
|
this.adminforth = adminforth;
|
|
48
|
+
|
|
49
|
+
process.on('SIGINT', () => {
|
|
50
|
+
console.log('Received SIGINT.');
|
|
51
|
+
this.cleanup();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
process.on('SIGTERM', () => {
|
|
55
|
+
console.log('Received SIGTERM.');
|
|
56
|
+
this.cleanup();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
process.on('exit', () => {
|
|
60
|
+
console.log('Exiting.');
|
|
61
|
+
this.cleanup();
|
|
62
|
+
});
|
|
40
63
|
}
|
|
41
64
|
|
|
42
65
|
// async runShell({command, verbose = false}) {
|
|
@@ -153,7 +176,11 @@ class CodeInjector {
|
|
|
153
176
|
await Promise.all(filesUpdated.map(async (file) => {
|
|
154
177
|
const src = path.join(ADMIN_FORTH_ABSOLUTE_PATH, 'spa', file);
|
|
155
178
|
const dest = path.join(CodeInjector.SPA_TMP_PATH, file);
|
|
156
|
-
|
|
179
|
+
|
|
180
|
+
await fsExtra.copy(src, dest, {
|
|
181
|
+
overwrite: true,
|
|
182
|
+
dereference: true, // needed to dereference types
|
|
183
|
+
});
|
|
157
184
|
if (process.env.HEAVY_DEBUG) {
|
|
158
185
|
console.log('🪲 await fsExtra.copy filtering', src, dest);
|
|
159
186
|
}
|
|
@@ -164,6 +191,13 @@ class CodeInjector {
|
|
|
164
191
|
console.log(`🪲 await fsExtra.copy from ${path.join(ADMIN_FORTH_ABSOLUTE_PATH, 'spa')}, ${CodeInjector.SPA_TMP_PATH}`);
|
|
165
192
|
}
|
|
166
193
|
|
|
194
|
+
// try to rm SPA_TMP_PATH/src/types directory
|
|
195
|
+
try {
|
|
196
|
+
await fs.promises.rm(path.join(CodeInjector.SPA_TMP_PATH, 'src', 'types'), { recursive: true });
|
|
197
|
+
} catch (e) {
|
|
198
|
+
// ignore
|
|
199
|
+
}
|
|
200
|
+
|
|
167
201
|
await fsExtra.copy(path.join(ADMIN_FORTH_ABSOLUTE_PATH, 'spa'), CodeInjector.SPA_TMP_PATH, {
|
|
168
202
|
filter: (src) => {
|
|
169
203
|
if (process.env.HEAVY_DEBUG) {
|
|
@@ -173,6 +207,7 @@ class CodeInjector {
|
|
|
173
207
|
return !src.includes('/adminforth/spa/node_modules') && !src.includes('/adminforth/spa/dist');
|
|
174
208
|
},
|
|
175
209
|
overwrite: true,
|
|
210
|
+
dereference: true, // needed to dereference types
|
|
176
211
|
});
|
|
177
212
|
|
|
178
213
|
// copy whole custom directory
|
|
@@ -416,9 +451,7 @@ async watchForReprepare({ verbose }) {
|
|
|
416
451
|
await this.prepareSources({ filesUpdated: [file.replace(spaPath + '/', '')] });
|
|
417
452
|
}
|
|
418
453
|
)
|
|
419
|
-
|
|
420
|
-
watcher.removeAll();
|
|
421
|
-
});
|
|
454
|
+
this.allWatchers.push(watcher);
|
|
422
455
|
}
|
|
423
456
|
|
|
424
457
|
async watchCustomComponentsForCopy({ verbose }) {
|
|
@@ -471,9 +504,7 @@ async watchForReprepare({ verbose }) {
|
|
|
471
504
|
});
|
|
472
505
|
}
|
|
473
506
|
)
|
|
474
|
-
|
|
475
|
-
watcher.removeAll();
|
|
476
|
-
});
|
|
507
|
+
this.allWatchers.push(watcher);
|
|
477
508
|
}
|
|
478
509
|
|
|
479
510
|
async bundleNow({hotReload = false, verbose = false}: {hotReload: boolean, verbose: boolean}) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "adminforth",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.75",
|
|
4
4
|
"description": "OpenSource Vue3 powered forth-generation admin panel",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
"jsonwebtoken": "^9.0.2",
|
|
24
24
|
"mongodb": "6.6",
|
|
25
25
|
"pg": "^8.11.5",
|
|
26
|
+
"request": "^2.88.2",
|
|
26
27
|
"uuid": "^9.0.1"
|
|
27
28
|
},
|
|
28
29
|
"devDependencies": {
|
package/plugins/base.ts
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import { AdminForthResource } from '../types/AdminForthConfig.js';
|
|
2
|
-
import AdminForth from '../index.js';
|
|
1
|
+
import { AdminForthResource, AdminForthPluginType, AdminForthClass } from '../types/AdminForthConfig.js';
|
|
3
2
|
import { getComponentNameFromPath } from '../modules/utils.js';
|
|
4
3
|
import { currentFileDir } from '../modules/utils.js';
|
|
5
4
|
import path from 'path';
|
|
6
5
|
import fs from 'fs';
|
|
7
6
|
|
|
8
|
-
export default class AdminForthPlugin {
|
|
7
|
+
export default class AdminForthPlugin implements AdminForthPluginType {
|
|
9
8
|
|
|
10
|
-
adminforth:
|
|
9
|
+
adminforth: AdminForthClass;
|
|
11
10
|
pluginDir: string;
|
|
12
11
|
customFolderName: string = 'custom';
|
|
13
12
|
|
|
@@ -16,7 +15,7 @@ export default class AdminForthPlugin {
|
|
|
16
15
|
this.pluginDir = currentFileDir(metaUrl);
|
|
17
16
|
}
|
|
18
17
|
|
|
19
|
-
modifyResourceConfig(adminforth:
|
|
18
|
+
modifyResourceConfig(adminforth: AdminForthClass, resourceConfig: AdminForthResource) {
|
|
20
19
|
this.adminforth = adminforth;
|
|
21
20
|
}
|
|
22
21
|
|
package/servers/express.ts
CHANGED
|
@@ -5,6 +5,7 @@ import fs from 'fs';
|
|
|
5
5
|
import CodeInjector from '../modules/codeInjector.js';
|
|
6
6
|
import AdminForth from '../index.js';
|
|
7
7
|
import { Express } from 'express';
|
|
8
|
+
import fetch from 'node-fetch';
|
|
8
9
|
|
|
9
10
|
const __filename = fileURLToPath(import.meta.url);
|
|
10
11
|
const __dirname = path.dirname(__filename);
|
|
@@ -19,13 +20,9 @@ function replaceAtStart(string, substring) {
|
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
async function proxyTo(url, res) {
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
r.headers.forEach((value, name) => {
|
|
26
|
-
res.setHeader(name, value);
|
|
27
|
-
});
|
|
28
|
-
res.send(body);
|
|
23
|
+
const actual = await fetch(url);
|
|
24
|
+
actual.headers.forEach((v, n) => res.setHeader(n, v));
|
|
25
|
+
actual.body.pipe(res);
|
|
29
26
|
}
|
|
30
27
|
|
|
31
28
|
async function parseExpressCookie(req) {
|
|
@@ -99,13 +96,14 @@ class ExpressServer {
|
|
|
99
96
|
|
|
100
97
|
if (this.adminforth.runningHotReload) {
|
|
101
98
|
const handler = async (req, res) => {
|
|
102
|
-
// proxy using fetch to webpack dev server
|
|
99
|
+
// proxy using fetch to webpack dev server
|
|
103
100
|
try {
|
|
104
101
|
await proxyTo(`http://localhost:5173${req.url}`, res);
|
|
105
102
|
} catch (e) {
|
|
103
|
+
// console.log('Failed to proxy', e);
|
|
106
104
|
res.status(500).send(respondNoServer('AdminForth SPA is not ready yet', 'Vite is still starting up. Please wait a moment...'));
|
|
107
105
|
return;
|
|
108
|
-
|
|
106
|
+
}
|
|
109
107
|
}
|
|
110
108
|
this.expressApp.get(`${slashedPrefix}assets/*`, handler);
|
|
111
109
|
this.expressApp.get(`${prefix}*`, handler);
|
package/spa/src/main.ts
CHANGED
package/spa/src/utils.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { onMounted, ref, resolveComponent } from 'vue';
|
|
2
2
|
|
|
3
3
|
import router from "./router";
|
|
4
|
+
import { useCoreStore } from './stores/core';
|
|
4
5
|
|
|
5
6
|
export async function callApi({path, method, body=undefined} ) {
|
|
6
7
|
const options = {
|
|
@@ -62,3 +63,13 @@ export const loadFile = (file: string) => {
|
|
|
62
63
|
}
|
|
63
64
|
return baseUrl;
|
|
64
65
|
}
|
|
66
|
+
|
|
67
|
+
// export function checkEmptyValues(value: any, viewType:'show' | 'list' | 'create' | 'edit') {
|
|
68
|
+
// const config: = useCoreStore().config;
|
|
69
|
+
// const emptyFieldPlaceholder = config.emptyFieldPlaceholder?.[viewType] || '---';
|
|
70
|
+
|
|
71
|
+
// if (value === null || value === undefined || value === '') {
|
|
72
|
+
// return emptyFieldPlaceholder;
|
|
73
|
+
// }
|
|
74
|
+
// return value;
|
|
75
|
+
// }
|
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
'background-blend-mode': 'darken'
|
|
8
8
|
}: {}"
|
|
9
9
|
>
|
|
10
|
-
|
|
10
|
+
|
|
11
|
+
<!-- Main modal -->
|
|
11
12
|
<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
13
|
<div class="relative p-4 w-full max-w-md max-h-full">
|
|
13
14
|
<!-- Modal content -->
|
|
@@ -78,13 +79,14 @@
|
|
|
78
79
|
|
|
79
80
|
<script setup>
|
|
80
81
|
|
|
81
|
-
import { onMounted, ref } from 'vue';
|
|
82
|
+
import { onMounted, ref, watchEffect } from 'vue';
|
|
82
83
|
import { useCoreStore } from '@/stores/core';
|
|
83
84
|
import { IconEyeSolid, IconEyeSlashSolid } from '@iconify-prerendered/vue-flowbite';
|
|
84
85
|
import { callAdminForthApi, loadFile } from '@/utils';
|
|
85
86
|
import { useRouter } from 'vue-router';
|
|
86
87
|
import { initFlowbite } from 'flowbite'
|
|
87
88
|
|
|
89
|
+
|
|
88
90
|
const router = useRouter();
|
|
89
91
|
const inProgress = ref(false);
|
|
90
92
|
|
|
@@ -114,6 +116,7 @@ async function login() {
|
|
|
114
116
|
} else {
|
|
115
117
|
error.value = null;
|
|
116
118
|
router.push('/');
|
|
119
|
+
await router.isReady();
|
|
117
120
|
await coreStore.fetchMenuAndResource();
|
|
118
121
|
setTimeout(() => {
|
|
119
122
|
initFlowbite();
|