adminforth 1.1.92 → 1.1.93
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/baseConnector.ts +2 -2
- package/dataConnectors/mongo.ts +2 -2
- package/dataConnectors/postgres.ts +2 -2
- package/dataConnectors/sqlite.ts +2 -2
- package/dist/plugins/TwoFactorsAuthPlugin/index.js +2 -3
- package/dist/spa/spa/src/components/Toast.vue +1 -1
- package/index.ts +5 -5
- package/modules/codeInjector.ts +2 -2
- package/package.json +2 -2
- package/plugins/AuditLogPlugin/index.ts +3 -3
- package/plugins/ForeignInlineListPlugin/index.ts +4 -4
- package/plugins/S3UploadPlugin/custom/s3uploader.vue +24 -4
- package/plugins/S3UploadPlugin/index.ts +4 -4
- package/plugins/S3UploadPlugin/package.json +1 -1
- package/plugins/TwoFactorsAuthPlugin/dist/auth.js +108 -0
- package/plugins/TwoFactorsAuthPlugin/dist/dataConnectors/baseConnector.js +90 -0
- package/plugins/TwoFactorsAuthPlugin/dist/dataConnectors/mongo.js +191 -0
- package/plugins/TwoFactorsAuthPlugin/dist/dataConnectors/postgres.js +295 -0
- package/plugins/TwoFactorsAuthPlugin/dist/dataConnectors/sqlite.js +246 -0
- package/plugins/TwoFactorsAuthPlugin/dist/index.js +1186 -0
- package/plugins/TwoFactorsAuthPlugin/dist/modules/codeInjector.js +546 -0
- package/plugins/TwoFactorsAuthPlugin/dist/modules/styleGenerator.js +43 -0
- package/plugins/TwoFactorsAuthPlugin/dist/modules/styles.js +92 -0
- package/plugins/TwoFactorsAuthPlugin/dist/modules/utils.js +301 -0
- package/plugins/TwoFactorsAuthPlugin/dist/plugins/TwoFactorsAuthPlugin/index.js +149 -0
- package/plugins/TwoFactorsAuthPlugin/dist/plugins/TwoFactorsAuthPlugin/types.js +1 -0
- package/plugins/TwoFactorsAuthPlugin/dist/plugins/base.js +34 -0
- package/plugins/TwoFactorsAuthPlugin/dist/servers/express.js +230 -0
- package/plugins/TwoFactorsAuthPlugin/dist/types/AdminForthConfig.js +105 -0
- package/plugins/TwoFactorsAuthPlugin/index.ts +10 -10
- package/plugins/TwoFactorsAuthPlugin/package-lock.json +2 -2
- package/plugins/TwoFactorsAuthPlugin/package.json +8 -6
- package/plugins/TwoFactorsAuthPlugin/tsconfig.json +112 -0
- package/plugins/base.ts +4 -4
- package/servers/express.ts +4 -4
- package/spa/src/components/Toast.vue +1 -1
- package/tsconfig.json +1 -1
- package/types/AdminForthConfig.ts +34 -28
- package/types/FrontendAPI.ts +2 -1
|
@@ -0,0 +1,546 @@
|
|
|
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 { exec, spawn } from 'child_process';
|
|
11
|
+
import crypto from 'crypto';
|
|
12
|
+
import filewatcher from 'filewatcher';
|
|
13
|
+
import fs from 'fs';
|
|
14
|
+
import fsExtra from 'fs-extra';
|
|
15
|
+
import os from 'os';
|
|
16
|
+
import path from 'path';
|
|
17
|
+
import { promisify } from 'util';
|
|
18
|
+
import { ADMIN_FORTH_ABSOLUTE_PATH, getComponentNameFromPath } from './utils.js';
|
|
19
|
+
import { StylesGenerator } from './styleGenerator.js';
|
|
20
|
+
let TMP_DIR;
|
|
21
|
+
try {
|
|
22
|
+
TMP_DIR = os.tmpdir();
|
|
23
|
+
}
|
|
24
|
+
catch (e) {
|
|
25
|
+
TMP_DIR = '/tmp';
|
|
26
|
+
}
|
|
27
|
+
const execAsync = promisify(exec);
|
|
28
|
+
function hashify(obj) {
|
|
29
|
+
return crypto.createHash('sha256').update(JSON.stringify(obj)).digest('hex');
|
|
30
|
+
}
|
|
31
|
+
function notifyWatcherIssue(limit) {
|
|
32
|
+
console.log('Ran out of file handles after watching %s files.', limit);
|
|
33
|
+
console.log('Falling back to polling which uses more CPU.');
|
|
34
|
+
console.log('Run ulimit -n 10000 to increase the limit for open files.');
|
|
35
|
+
}
|
|
36
|
+
class CodeInjector {
|
|
37
|
+
cleanup() {
|
|
38
|
+
console.log('Cleaning up...');
|
|
39
|
+
this.allWatchers.forEach((watcher) => {
|
|
40
|
+
watcher.removeAll();
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
constructor(adminforth) {
|
|
44
|
+
this.allWatchers = [];
|
|
45
|
+
this.allComponentNames = {};
|
|
46
|
+
this.srcFoldersToSync = {};
|
|
47
|
+
this.adminforth = adminforth;
|
|
48
|
+
['SIGINT', 'SIGTERM', 'SIGQUIT']
|
|
49
|
+
.forEach(signal => process.on(signal, () => {
|
|
50
|
+
this.cleanup();
|
|
51
|
+
process.exit();
|
|
52
|
+
}));
|
|
53
|
+
}
|
|
54
|
+
// async runShell({command, verbose = false}) {
|
|
55
|
+
// console.log(`⚙️ Running shell ${command}...`);
|
|
56
|
+
// console.time(`${command} done in`);
|
|
57
|
+
// const { stdout: out, stderr: err } = await execAsync(command);
|
|
58
|
+
// console.timeEnd(`${command} done in`);
|
|
59
|
+
// console.log(`Command ${command} output:`, out, err);
|
|
60
|
+
// }
|
|
61
|
+
runNpmShell(_a) {
|
|
62
|
+
return __awaiter(this, arguments, void 0, function* ({ command, verbose = false, cwd }) {
|
|
63
|
+
const nodeBinary = process.execPath; // Path to the Node.js binary running this script
|
|
64
|
+
const npmPath = path.join(path.dirname(nodeBinary), 'npm'); // Path to the npm executable
|
|
65
|
+
const env = Object.assign({ VITE_ADMINFORTH_PUBLIC_PATH: this.adminforth.config.baseUrl, FORCE_COLOR: '1' }, process.env);
|
|
66
|
+
console.log(`⚙️ Running npm ${command}...`);
|
|
67
|
+
console.time(`npm ${command} done in`);
|
|
68
|
+
const { stdout: out, stderr: err } = yield execAsync(`${nodeBinary} ${npmPath} ${command}`, {
|
|
69
|
+
cwd,
|
|
70
|
+
env,
|
|
71
|
+
});
|
|
72
|
+
console.timeEnd(`npm ${command} done in`);
|
|
73
|
+
if (verbose) {
|
|
74
|
+
console.log(`npm ${command} output:`, out);
|
|
75
|
+
}
|
|
76
|
+
if (err) {
|
|
77
|
+
console.error(`npm ${command} errors/warnings:`, err);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
rmTmpDir() {
|
|
82
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
83
|
+
// remove spa_tmp folder if it is exists
|
|
84
|
+
try {
|
|
85
|
+
yield fs.promises.rm(CodeInjector.SPA_TMP_PATH, { recursive: true });
|
|
86
|
+
}
|
|
87
|
+
catch (e) {
|
|
88
|
+
// ignore
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
prepareSources(_a) {
|
|
93
|
+
return __awaiter(this, arguments, void 0, function* ({ filesUpdated, verbose = false }) {
|
|
94
|
+
var _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
95
|
+
// check SPA_TMP_PATH exists and create if not
|
|
96
|
+
try {
|
|
97
|
+
yield fs.promises.access(CodeInjector.SPA_TMP_PATH, fs.constants.F_OK);
|
|
98
|
+
}
|
|
99
|
+
catch (e) {
|
|
100
|
+
yield fs.promises.mkdir(CodeInjector.SPA_TMP_PATH, { recursive: true });
|
|
101
|
+
}
|
|
102
|
+
const icons = [];
|
|
103
|
+
let routes = '';
|
|
104
|
+
let routerComponents = '';
|
|
105
|
+
const collectAssetsFromMenu = (menu) => {
|
|
106
|
+
menu.forEach((item) => {
|
|
107
|
+
var _a, _b, _c, _d;
|
|
108
|
+
if (item.icon) {
|
|
109
|
+
icons.push(item.icon);
|
|
110
|
+
}
|
|
111
|
+
if (item.component) {
|
|
112
|
+
if (Object.keys(item).includes('isStaticRoute')) {
|
|
113
|
+
if (!item.isStaticRoute) {
|
|
114
|
+
routes += `{
|
|
115
|
+
path: '${item.path}',
|
|
116
|
+
name: '${item.path}',
|
|
117
|
+
component: () => import('${item.component}'),
|
|
118
|
+
meta: { title: '${((_a = item === null || item === void 0 ? void 0 : item.meta) === null || _a === void 0 ? void 0 : _a.title) || item.path.replace('/', '')}'}
|
|
119
|
+
},\n`;
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
routes += `{
|
|
123
|
+
path: '${item.path}',
|
|
124
|
+
name: '${item.path}',
|
|
125
|
+
component: ${getComponentNameFromPath(item.component)},
|
|
126
|
+
meta: { title: '${((_b = item === null || item === void 0 ? void 0 : item.meta) === null || _b === void 0 ? void 0 : _b.title) || item.path.replace('/', '')}'}
|
|
127
|
+
},\n`;
|
|
128
|
+
const componentName = `${getComponentNameFromPath(item.component)}`;
|
|
129
|
+
routerComponents += `import ${componentName} from '${item.component}';\n`;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
if (item.homepage) {
|
|
134
|
+
routes += `{
|
|
135
|
+
path: '${item.path}',
|
|
136
|
+
name: '${item.path}',
|
|
137
|
+
component: ${getComponentNameFromPath(item.component)},
|
|
138
|
+
meta: { title: '${((_c = item === null || item === void 0 ? void 0 : item.meta) === null || _c === void 0 ? void 0 : _c.title) || item.path.replace('/', '')}'}
|
|
139
|
+
},\n`;
|
|
140
|
+
const componentName = `${getComponentNameFromPath(item.component)}`;
|
|
141
|
+
routerComponents += `import ${componentName} from '${item.component}';\n`;
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
routes += `{
|
|
145
|
+
path: '${item.path}',
|
|
146
|
+
name: '${item.path}',
|
|
147
|
+
component: () => import('${item.component}'),
|
|
148
|
+
meta: { title: '${((_d = item === null || item === void 0 ? void 0 : item.meta) === null || _d === void 0 ? void 0 : _d.title) || item.path.replace('/', '')}'}
|
|
149
|
+
},\n`;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
if (item.children) {
|
|
154
|
+
collectAssetsFromMenu(item.children);
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
};
|
|
158
|
+
const registerCustomPages = (config) => {
|
|
159
|
+
if (config.customization.customPages) {
|
|
160
|
+
config.customization.customPages.forEach((page) => {
|
|
161
|
+
var _a, _b, _c, _d;
|
|
162
|
+
routes += `{
|
|
163
|
+
path: '${page.path}',
|
|
164
|
+
name: '${page.path}',
|
|
165
|
+
component: () => import('${((_a = page === null || page === void 0 ? void 0 : page.component) === null || _a === void 0 ? void 0 : _a.file) || page.component}'),
|
|
166
|
+
meta: { title: '${((_b = page.meta) === null || _b === void 0 ? void 0 : _b.title) || page.path.replace('/', '')}',customLayout:${(_d = (_c = page === null || page === void 0 ? void 0 : page.component) === null || _c === void 0 ? void 0 : _c.meta) === null || _d === void 0 ? void 0 : _d.customLayout}}
|
|
167
|
+
},`;
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
registerCustomPages(this.adminforth.config);
|
|
172
|
+
collectAssetsFromMenu(this.adminforth.config.menu);
|
|
173
|
+
if (filesUpdated) {
|
|
174
|
+
// copy only updated files
|
|
175
|
+
yield Promise.all(filesUpdated.map((file) => __awaiter(this, void 0, void 0, function* () {
|
|
176
|
+
const src = path.join(ADMIN_FORTH_ABSOLUTE_PATH, 'spa', file);
|
|
177
|
+
const dest = path.join(CodeInjector.SPA_TMP_PATH, file);
|
|
178
|
+
// overwrite:true can't be used to not destroy cache
|
|
179
|
+
yield fsExtra.copy(src, dest, {
|
|
180
|
+
dereference: true, // needed to dereference types
|
|
181
|
+
});
|
|
182
|
+
if (process.env.HEAVY_DEBUG) {
|
|
183
|
+
console.log('🪲 await fsExtra.copy filtering', src, dest);
|
|
184
|
+
}
|
|
185
|
+
})));
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
if (process.env.HEAVY_DEBUG) {
|
|
189
|
+
console.log(`🪲 await fsExtra.copy from ${path.join(ADMIN_FORTH_ABSOLUTE_PATH, 'spa')}, ${CodeInjector.SPA_TMP_PATH}`);
|
|
190
|
+
}
|
|
191
|
+
// try to rm SPA_TMP_PATH/src/types directory
|
|
192
|
+
try {
|
|
193
|
+
yield fs.promises.rm(path.join(CodeInjector.SPA_TMP_PATH, 'src', 'types'), { recursive: true });
|
|
194
|
+
}
|
|
195
|
+
catch (e) {
|
|
196
|
+
// ignore
|
|
197
|
+
}
|
|
198
|
+
// overwrite can't be used to not destroy cache
|
|
199
|
+
yield fsExtra.copy(path.join(ADMIN_FORTH_ABSOLUTE_PATH, 'spa'), CodeInjector.SPA_TMP_PATH, {
|
|
200
|
+
filter: (src) => {
|
|
201
|
+
if (process.env.HEAVY_DEBUG) {
|
|
202
|
+
console.log('🪲 await fsExtra.copy filtering', src);
|
|
203
|
+
}
|
|
204
|
+
return !src.includes('/adminforth/spa/node_modules') && !src.includes('/adminforth/spa/dist');
|
|
205
|
+
},
|
|
206
|
+
dereference: true, // needed to dereference types
|
|
207
|
+
});
|
|
208
|
+
// copy whole custom directory
|
|
209
|
+
if ((_b = this.adminforth.config.customization) === null || _b === void 0 ? void 0 : _b.customComponentsDir) {
|
|
210
|
+
// resolve customComponentsDir to absolute path, so ./aa will be resolved to /path/to/current/dir/aa
|
|
211
|
+
const customCompAbsPath = path.resolve(this.adminforth.config.customization.customComponentsDir);
|
|
212
|
+
this.srcFoldersToSync[customCompAbsPath] = './';
|
|
213
|
+
}
|
|
214
|
+
// if this.adminforth.config.customization.favicon is set, copy it to assets
|
|
215
|
+
const customFav = (_c = this.adminforth.config.customization) === null || _c === void 0 ? void 0 : _c.favicon;
|
|
216
|
+
if (customFav) {
|
|
217
|
+
const faviconPath = path.join((_d = this.adminforth.config.customization) === null || _d === void 0 ? void 0 : _d.customComponentsDir, customFav.replace('@@/', ''));
|
|
218
|
+
const dest = path.join(CodeInjector.SPA_TMP_PATH, 'public', 'assets', customFav.replace('@@/', ''));
|
|
219
|
+
// make sure all folders in dest exist
|
|
220
|
+
yield fsExtra.ensureDir(path.dirname(dest));
|
|
221
|
+
yield fsExtra.copy(faviconPath, dest);
|
|
222
|
+
}
|
|
223
|
+
for (const [src, dest] of Object.entries(this.srcFoldersToSync)) {
|
|
224
|
+
const to = path.join(CodeInjector.SPA_TMP_PATH, 'src', 'custom', dest);
|
|
225
|
+
if (process.env.HEAVY_DEBUG) {
|
|
226
|
+
console.log(`🪲 await fsExtra.copy from ${src}, ${to}`);
|
|
227
|
+
}
|
|
228
|
+
yield fsExtra.copy(src, to, {
|
|
229
|
+
recursive: true,
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
//collect all 'icon' fields from resources bulkActions
|
|
234
|
+
this.adminforth.config.resources.forEach((resource) => {
|
|
235
|
+
var _a;
|
|
236
|
+
if ((_a = resource.options) === null || _a === void 0 ? void 0 : _a.bulkActions) {
|
|
237
|
+
resource.options.bulkActions.forEach((action) => {
|
|
238
|
+
if (action.icon) {
|
|
239
|
+
icons.push(action.icon);
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
const uniqueIcons = Array.from(new Set(icons));
|
|
245
|
+
// icons are collectionName:iconName. Get list of all unique collection names:
|
|
246
|
+
const collections = new Set(icons.map((icon) => icon.split(':')[0]));
|
|
247
|
+
// package names @iconify-prerendered/vue-<collection name>
|
|
248
|
+
const packageNames = Array.from(collections).map((collection) => `@iconify-prerendered/vue-${collection}`);
|
|
249
|
+
// for each icon generate import statement
|
|
250
|
+
const iconImports = uniqueIcons.map((icon) => {
|
|
251
|
+
const [collection, iconName] = icon.split(':');
|
|
252
|
+
const PascalIconName = 'Icon' + iconName.split('-').map((part, index) => {
|
|
253
|
+
return part[0].toUpperCase() + part.slice(1);
|
|
254
|
+
}).join('');
|
|
255
|
+
return `import { ${PascalIconName} } from '@iconify-prerendered/vue-${collection}';`;
|
|
256
|
+
}).join('\n');
|
|
257
|
+
// for each custom component generate import statement
|
|
258
|
+
const customResourceComponents = [];
|
|
259
|
+
this.adminforth.config.resources.forEach((resource) => {
|
|
260
|
+
var _a;
|
|
261
|
+
resource.columns.forEach((column) => {
|
|
262
|
+
if (column.components) {
|
|
263
|
+
Object.values(column.components).forEach(({ file }) => {
|
|
264
|
+
if (!customResourceComponents.includes(file)) {
|
|
265
|
+
if (file === undefined) {
|
|
266
|
+
throw new Error('file is undefined from field.components, field:' + JSON.stringify(column));
|
|
267
|
+
}
|
|
268
|
+
customResourceComponents.push(file);
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
});
|
|
273
|
+
(Object.values(((_a = resource.options) === null || _a === void 0 ? void 0 : _a.pageInjections) || {})).forEach((injection) => {
|
|
274
|
+
Object.values(injection).forEach((filePathes) => {
|
|
275
|
+
filePathes.forEach(({ file }) => {
|
|
276
|
+
if (!customResourceComponents.includes(file)) {
|
|
277
|
+
if (file === undefined) {
|
|
278
|
+
throw new Error('file is undefined');
|
|
279
|
+
}
|
|
280
|
+
customResourceComponents.push(file);
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
});
|
|
286
|
+
customResourceComponents.forEach((filePath) => {
|
|
287
|
+
const componentName = getComponentNameFromPath(filePath);
|
|
288
|
+
this.allComponentNames[filePath] = componentName;
|
|
289
|
+
});
|
|
290
|
+
// console.log('🔧 Injecting code into Vue sources...', this.allComponentNames);
|
|
291
|
+
let customComponentsImports = '';
|
|
292
|
+
for (const [targetPath, component] of Object.entries(this.allComponentNames)) {
|
|
293
|
+
customComponentsImports += `import ${component} from '${targetPath}';\n`;
|
|
294
|
+
}
|
|
295
|
+
// Generate Vue.component statements for each icon
|
|
296
|
+
const iconComponents = uniqueIcons.map((icon) => {
|
|
297
|
+
const [collection, iconName] = icon.split(':');
|
|
298
|
+
const PascalIconName = 'Icon' + iconName.split('-').map((part, index) => {
|
|
299
|
+
return part[0].toUpperCase() + part.slice(1);
|
|
300
|
+
}).join('');
|
|
301
|
+
return `app.component('${PascalIconName}', ${PascalIconName});`;
|
|
302
|
+
}).join('\n');
|
|
303
|
+
// Generate Vue.component statements for each custom component
|
|
304
|
+
let customComponentsComponents = '';
|
|
305
|
+
for (const name of Object.values(this.allComponentNames)) {
|
|
306
|
+
customComponentsComponents += `app.component('${name}', ${name});\n`;
|
|
307
|
+
}
|
|
308
|
+
let imports = iconImports + '\n';
|
|
309
|
+
imports += customComponentsImports + '\n';
|
|
310
|
+
if ((_e = this.adminforth.config.customization) === null || _e === void 0 ? void 0 : _e.vueUsesFile) {
|
|
311
|
+
imports += `import addCustomUses from '${this.adminforth.config.customization.vueUsesFile}';\n`;
|
|
312
|
+
}
|
|
313
|
+
// inject that code into spa_tmp/src/App.vue
|
|
314
|
+
const appVuePath = path.join(CodeInjector.SPA_TMP_PATH, 'src', 'main.ts');
|
|
315
|
+
let appVueContent = yield fs.promises.readFile(appVuePath, 'utf-8');
|
|
316
|
+
appVueContent = appVueContent.replace('/* IMPORTANT:ADMINFORTH IMPORTS */', imports);
|
|
317
|
+
appVueContent = appVueContent.replace('/* IMPORTANT:ADMINFORTH COMPONENT REGISTRATIONS */', iconComponents + '\n' + customComponentsComponents + '\n');
|
|
318
|
+
if ((_f = this.adminforth.config.customization) === null || _f === void 0 ? void 0 : _f.vueUsesFile) {
|
|
319
|
+
appVueContent = appVueContent.replace('/* IMPORTANT:ADMINFORTH CUSTOM USES */', 'addCustomUses(app);');
|
|
320
|
+
}
|
|
321
|
+
yield fs.promises.writeFile(appVuePath, appVueContent);
|
|
322
|
+
// generate tailwind extend styles
|
|
323
|
+
const stylesGenerator = new StylesGenerator((_g = this.adminforth.config.customization) === null || _g === void 0 ? void 0 : _g.styles);
|
|
324
|
+
const stylesText = JSON.stringify(stylesGenerator.mergeStyles(), null, 2).slice(1, -1);
|
|
325
|
+
let tailwindConfigPath = path.join(CodeInjector.SPA_TMP_PATH, 'tailwind.config.js');
|
|
326
|
+
let tailwindConfigContent = yield fs.promises.readFile(tailwindConfigPath, 'utf-8');
|
|
327
|
+
tailwindConfigContent = tailwindConfigContent.replace('/* IMPORTANT:ADMINFORTH TAILWIND STYLES */', stylesText);
|
|
328
|
+
yield fs.promises.writeFile(tailwindConfigPath, tailwindConfigContent);
|
|
329
|
+
const routerVuePath = path.join(CodeInjector.SPA_TMP_PATH, 'src', 'router', 'index.ts');
|
|
330
|
+
let routerVueContent = yield fs.promises.readFile(routerVuePath, 'utf-8');
|
|
331
|
+
routerVueContent = routerVueContent.replace('/* IMPORTANT:ADMINFORTH ROUTES IMPORTS */', routerComponents);
|
|
332
|
+
// inject title to index.html
|
|
333
|
+
const indexHtmlPath = path.join(CodeInjector.SPA_TMP_PATH, 'index.html');
|
|
334
|
+
let indexHtmlContent = yield fs.promises.readFile(indexHtmlPath, 'utf-8');
|
|
335
|
+
indexHtmlContent = indexHtmlContent.replace('/* IMPORTANT:ADMINFORTH TITLE */', `${this.adminforth.config.customization.title || 'AdminForth'}`);
|
|
336
|
+
indexHtmlContent = indexHtmlContent.replace('/* IMPORTANT:ADMINFORTH FAVICON */', ((_h = this.adminforth.config.customization.favicon) === null || _h === void 0 ? void 0 : _h.replace('@@/', `${this.adminforth.baseUrlSlashed}assets/`))
|
|
337
|
+
||
|
|
338
|
+
`${this.adminforth.baseUrlSlashed}assets/favicon.png`);
|
|
339
|
+
yield fs.promises.writeFile(indexHtmlPath, indexHtmlContent);
|
|
340
|
+
/* generate custom routes */
|
|
341
|
+
const homepageMenuItem = this.adminforth.config.menu.find((mi) => mi.homepage);
|
|
342
|
+
let childrenHomePageMenuItem = this.adminforth.config.menu.find((mi) => { var _a; return mi.children && ((_a = mi.children) === null || _a === void 0 ? void 0 : _a.find((mi) => mi.homepage)); });
|
|
343
|
+
let childrenHomepage = (_j = childrenHomePageMenuItem === null || childrenHomePageMenuItem === void 0 ? void 0 : childrenHomePageMenuItem.children) === null || _j === void 0 ? void 0 : _j.find((mi) => mi.homepage);
|
|
344
|
+
let homePagePath = (homepageMenuItem === null || homepageMenuItem === void 0 ? void 0 : homepageMenuItem.path) || `/resource/${childrenHomepage === null || childrenHomepage === void 0 ? void 0 : childrenHomepage.resourceId}`;
|
|
345
|
+
if (!homePagePath) {
|
|
346
|
+
homePagePath = ((_k = this.adminforth.config.menu.filter((mi) => mi.path)[0]) === null || _k === void 0 ? void 0 : _k.path) || `/resource/${(_l = this.adminforth.config.menu.filter((mi) => mi.children)[0]) === null || _l === void 0 ? void 0 : _l.resourceId}`;
|
|
347
|
+
}
|
|
348
|
+
routes += `{
|
|
349
|
+
path: '/',
|
|
350
|
+
name: 'home',
|
|
351
|
+
//redirect to login
|
|
352
|
+
redirect: '${homePagePath}'
|
|
353
|
+
},\n`;
|
|
354
|
+
routerVueContent = routerVueContent.replace('/* IMPORTANT:ADMINFORTH ROUTES */', routes);
|
|
355
|
+
yield fs.promises.writeFile(routerVuePath, routerVueContent);
|
|
356
|
+
/* hash checking */
|
|
357
|
+
const spaPackageLockPath = path.join(CodeInjector.SPA_TMP_PATH, 'package-lock.json');
|
|
358
|
+
const spaPackageLock = JSON.parse(yield fs.promises.readFile(spaPackageLockPath, 'utf-8'));
|
|
359
|
+
const spaLockHash = hashify(spaPackageLock);
|
|
360
|
+
/* customPackageLock */
|
|
361
|
+
const usersPackagePath = path.join('./package.json');
|
|
362
|
+
const usersPackage = JSON.parse(yield fs.promises.readFile(usersPackagePath, 'utf-8'));
|
|
363
|
+
const usersLockPath = path.join('./package-lock.json');
|
|
364
|
+
const usersLock = JSON.parse(yield fs.promises.readFile(usersLockPath, 'utf-8'));
|
|
365
|
+
const usersLockHash = hashify(usersLock);
|
|
366
|
+
const packagesNamesHash = hashify(packageNames);
|
|
367
|
+
const fullHash = `${spaLockHash}::${packagesNamesHash}::${usersLockHash}`;
|
|
368
|
+
const hashPath = path.join(CodeInjector.SPA_TMP_PATH, 'node_modules', '.adminforth_hash');
|
|
369
|
+
try {
|
|
370
|
+
const existingHash = yield fs.promises.readFile(hashPath, 'utf-8');
|
|
371
|
+
if (existingHash === fullHash) {
|
|
372
|
+
console.log('Hashes match, skipping npm ci/install');
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
else {
|
|
376
|
+
if (verbose) {
|
|
377
|
+
console.log(`Hashes do not match: existing ${existingHash} new ${fullHash}, proceeding with npm ci/install`);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
catch (e) {
|
|
382
|
+
// ignore
|
|
383
|
+
if (verbose) {
|
|
384
|
+
console.log('Hash file does not exist, proceeding with npm ci/install');
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
yield this.runNpmShell({ command: 'ci', verbose, cwd: CodeInjector.SPA_TMP_PATH });
|
|
388
|
+
// get packages with version from customPackage
|
|
389
|
+
const IGNORE_PACKAGES = ['tsx', 'typescript', 'express', 'nodemon', 'adminforth'];
|
|
390
|
+
const customPackgeNames = [...Object.keys(usersPackage.dependencies), ...Object.keys(usersPackage.devDependencies || [])]
|
|
391
|
+
.filter((packageName) => !IGNORE_PACKAGES.includes(packageName))
|
|
392
|
+
.reduce((acc, packageName) => {
|
|
393
|
+
const version = usersLock.packages[`node_modules/${packageName}`].version;
|
|
394
|
+
acc.push(`${packageName}@${version}`);
|
|
395
|
+
return acc;
|
|
396
|
+
}, []);
|
|
397
|
+
if (packageNames.length) {
|
|
398
|
+
const npmInstallCommand = `install ${[...packageNames, ...customPackgeNames].join(' ')}`;
|
|
399
|
+
yield this.runNpmShell({ command: npmInstallCommand, cwd: CodeInjector.SPA_TMP_PATH });
|
|
400
|
+
}
|
|
401
|
+
yield fs.promises.writeFile(hashPath, fullHash);
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
watchForReprepare(_a) {
|
|
405
|
+
return __awaiter(this, arguments, void 0, function* ({ verbose }) {
|
|
406
|
+
const spaPath = path.join(ADMIN_FORTH_ABSOLUTE_PATH, 'spa');
|
|
407
|
+
// get list of all subdirectories in spa recursively
|
|
408
|
+
const directories = [];
|
|
409
|
+
const collectDirectories = (dir) => __awaiter(this, void 0, void 0, function* () {
|
|
410
|
+
const files = yield fs.promises.readdir(dir, { withFileTypes: true });
|
|
411
|
+
for (const file of files) {
|
|
412
|
+
if (['node_modules', 'dist'].includes(file.name)) {
|
|
413
|
+
continue;
|
|
414
|
+
}
|
|
415
|
+
if (file.isDirectory()) {
|
|
416
|
+
directories.push(path.join(dir, file.name));
|
|
417
|
+
yield collectDirectories(path.join(dir, file.name));
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
});
|
|
421
|
+
yield collectDirectories(spaPath);
|
|
422
|
+
if (process.env.HEAVY_DEBUG) {
|
|
423
|
+
console.log('🔎 Watching for changes in:', directories.join(','));
|
|
424
|
+
}
|
|
425
|
+
const watcher = filewatcher();
|
|
426
|
+
directories.forEach((dir) => {
|
|
427
|
+
watcher.add(dir);
|
|
428
|
+
});
|
|
429
|
+
watcher.on('change', (file, x) => __awaiter(this, void 0, void 0, function* () {
|
|
430
|
+
console.log(`File ${file} changed ${x}, preparing sources...`);
|
|
431
|
+
yield this.prepareSources({ filesUpdated: [file.replace(spaPath + '/', '')] });
|
|
432
|
+
}));
|
|
433
|
+
watcher.on('fallback', notifyWatcherIssue);
|
|
434
|
+
this.allWatchers.push(watcher);
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
watchCustomComponentsForCopy(_a) {
|
|
438
|
+
return __awaiter(this, arguments, void 0, function* ({ verbose, customComponentsDir, destination }) {
|
|
439
|
+
if (!customComponentsDir) {
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
// check if folder exists
|
|
443
|
+
try {
|
|
444
|
+
yield fs.promises.access(customComponentsDir, fs.constants.F_OK);
|
|
445
|
+
}
|
|
446
|
+
catch (e) {
|
|
447
|
+
if (verbose) {
|
|
448
|
+
console.log(`Custom components dir ${customComponentsDir} does not exist, skipping watching`);
|
|
449
|
+
}
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
// get all subdirs
|
|
453
|
+
const directories = [];
|
|
454
|
+
const files = [];
|
|
455
|
+
const collectDirectories = (dir) => __awaiter(this, void 0, void 0, function* () {
|
|
456
|
+
directories.push(dir);
|
|
457
|
+
const filesAndDirs = yield fs.promises.readdir(dir, { withFileTypes: true });
|
|
458
|
+
yield Promise.all(filesAndDirs.map((file) => __awaiter(this, void 0, void 0, function* () {
|
|
459
|
+
const isDir = fs.lstatSync(path.join(dir, file.name)).isDirectory();
|
|
460
|
+
if (isDir) {
|
|
461
|
+
yield collectDirectories(path.join(dir, file.name));
|
|
462
|
+
}
|
|
463
|
+
else {
|
|
464
|
+
files.push(path.join(dir, file.name));
|
|
465
|
+
}
|
|
466
|
+
})));
|
|
467
|
+
});
|
|
468
|
+
yield collectDirectories(customComponentsDir);
|
|
469
|
+
const watcher = filewatcher();
|
|
470
|
+
files.forEach((file) => {
|
|
471
|
+
process.env.HEAVY_DEBUG && console.log(`🔎 Watching for changes in file ${file}`);
|
|
472
|
+
watcher.add(file);
|
|
473
|
+
});
|
|
474
|
+
if (process.env.HEAVY_DEBUG) {
|
|
475
|
+
console.log('🔎 Watching for changes in:', directories.join(','));
|
|
476
|
+
}
|
|
477
|
+
watcher.on('change', (fileOrDir) => __awaiter(this, void 0, void 0, function* () {
|
|
478
|
+
// copy one file
|
|
479
|
+
const relativeFilename = fileOrDir.replace(customComponentsDir + '/', '');
|
|
480
|
+
if (process.env.HEAVY_DEBUG) {
|
|
481
|
+
console.log(`🔎 fileOrDir ${fileOrDir} changed`);
|
|
482
|
+
console.log(`🔎 relativeFilename ${relativeFilename}`);
|
|
483
|
+
console.log(`🔎 customComponentsDir ${customComponentsDir}`);
|
|
484
|
+
console.log(`🔎 destination ${destination}`);
|
|
485
|
+
}
|
|
486
|
+
const isFile = fs.lstatSync(fileOrDir).isFile();
|
|
487
|
+
if (isFile) {
|
|
488
|
+
const destPath = path.join(CodeInjector.SPA_TMP_PATH, 'src', 'custom', destination, relativeFilename);
|
|
489
|
+
process.env.HEAVY_DEBUG && console.log(`🔎 Copying file ${fileOrDir} to ${destPath}`);
|
|
490
|
+
yield fsExtra.copy(fileOrDir, destPath);
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
493
|
+
else {
|
|
494
|
+
// for now do nothing
|
|
495
|
+
}
|
|
496
|
+
}));
|
|
497
|
+
watcher.on('fallback', notifyWatcherIssue);
|
|
498
|
+
this.allWatchers.push(watcher);
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
bundleNow(_a) {
|
|
502
|
+
return __awaiter(this, arguments, void 0, function* ({ hotReload = false, verbose = false }) {
|
|
503
|
+
this.adminforth.runningHotReload = hotReload;
|
|
504
|
+
yield this.prepareSources({ verbose });
|
|
505
|
+
if (hotReload) {
|
|
506
|
+
yield Promise.all([
|
|
507
|
+
this.watchForReprepare({ verbose }),
|
|
508
|
+
...Object.entries(this.srcFoldersToSync).map((_b) => __awaiter(this, [_b], void 0, function* ([src, dest]) {
|
|
509
|
+
yield this.watchCustomComponentsForCopy({
|
|
510
|
+
verbose,
|
|
511
|
+
customComponentsDir: src,
|
|
512
|
+
destination: dest,
|
|
513
|
+
});
|
|
514
|
+
})),
|
|
515
|
+
]);
|
|
516
|
+
}
|
|
517
|
+
console.log('AdminForth bundling');
|
|
518
|
+
const cwd = CodeInjector.SPA_TMP_PATH;
|
|
519
|
+
if (!hotReload) {
|
|
520
|
+
// probably add option to build with tsh check (plain 'build')
|
|
521
|
+
yield this.runNpmShell({ command: 'run build-only', verbose, cwd });
|
|
522
|
+
}
|
|
523
|
+
else {
|
|
524
|
+
const command = 'run dev';
|
|
525
|
+
console.log(`⚙️ Running npm ${command}...`);
|
|
526
|
+
const nodeBinary = process.execPath;
|
|
527
|
+
const npmPath = path.join(path.dirname(nodeBinary), 'npm');
|
|
528
|
+
const env = Object.assign({ VITE_ADMINFORTH_PUBLIC_PATH: this.adminforth.config.baseUrl, FORCE_COLOR: '1' }, process.env);
|
|
529
|
+
const devServer = spawn(`${nodeBinary}`, [`${npmPath}`, ...command.split(' ')], {
|
|
530
|
+
cwd,
|
|
531
|
+
env,
|
|
532
|
+
});
|
|
533
|
+
devServer.stdout.on('data', (data) => {
|
|
534
|
+
console.log(`[AdminForth SPA]:`);
|
|
535
|
+
process.stdout.write(data);
|
|
536
|
+
});
|
|
537
|
+
devServer.stderr.on('data', (data) => {
|
|
538
|
+
console.error(`[AdminForth SPA ERR]:`);
|
|
539
|
+
process.stdout.write(data);
|
|
540
|
+
});
|
|
541
|
+
}
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
CodeInjector.SPA_TMP_PATH = path.join(TMP_DIR, 'adminforth', 'spa_tmp');
|
|
546
|
+
export default CodeInjector;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { transformObject, deepMerge, createRGBA, parseColorForAliases, darkenRGBA, lightenRGBA } from "./utils.js";
|
|
2
|
+
import { styles } from "./styles.js";
|
|
3
|
+
export class StylesGenerator {
|
|
4
|
+
constructor(styleConfig) {
|
|
5
|
+
this.styleConfig = styleConfig;
|
|
6
|
+
this.defaultStyles = styles();
|
|
7
|
+
}
|
|
8
|
+
generatePlainStyles(styleObj) {
|
|
9
|
+
let plainCustomStyles = {};
|
|
10
|
+
if (styleObj) {
|
|
11
|
+
Object.keys(styleObj).forEach((k) => {
|
|
12
|
+
plainCustomStyles[k] = transformObject(styleObj[k]);
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
return plainCustomStyles;
|
|
16
|
+
}
|
|
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);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
mergeStyles() {
|
|
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
|
+
});
|
|
41
|
+
return mergedStyles;
|
|
42
|
+
}
|
|
43
|
+
}
|