adminforth 1.0.17 → 1.0.24
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.js → mongo.ts} +15 -10
- package/dataConnectors/{postgres.js → postgres.ts} +6 -2
- package/dataConnectors/{sqlite.js → sqlite.ts} +6 -2
- package/dist/auth.js +68 -0
- package/dist/dataConnectors/mongo.js +204 -0
- package/dist/dataConnectors/postgres.js +298 -0
- package/dist/dataConnectors/sqlite.js +261 -0
- package/dist/index.js +693 -0
- package/dist/modules/codeInjector.js +337 -0
- package/dist/modules/utils.js +12 -0
- package/dist/servers/express.js +210 -0
- package/dist/spa/src/main.js +16 -0
- package/dist/spa/src/router/index.js +79 -0
- package/dist/spa/src/stores/core.js +154 -0
- package/dist/spa/src/stores/modal.js +35 -0
- package/dist/spa/src/utils.js +59 -0
- package/dist/spa/vite.config.js +44 -0
- package/dist/spa_tmp/src/custom/custom/vueUses.js +10 -0
- package/dist/spa_tmp/src/main.js +29 -0
- package/dist/spa_tmp/src/router/index.js +83 -0
- package/dist/spa_tmp/src/stores/core.js +150 -0
- package/dist/spa_tmp/src/stores/modal.js +35 -0
- package/dist/spa_tmp/src/utils.js +59 -0
- package/dist/spa_tmp/vite.config.js +43 -0
- package/dist/types.js +30 -0
- package/{index.js → index.ts} +115 -6
- package/modules/{codeInjector.js → codeInjector.ts} +17 -13
- package/package.json +9 -3
- package/servers/{express.js → express.ts} +8 -2
- package/spa/package.json +2 -2
- package/spa/src/views/ListView.vue +1 -1
- package/tsconfig.json +112 -0
- package/{types.js → types.ts} +2 -0
- /package/{auth.js → auth.ts} +0 -0
- /package/modules/{utils.js → utils.ts} +0 -0
|
@@ -0,0 +1,337 @@
|
|
|
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 fs from 'fs';
|
|
11
|
+
import fsExtra from 'fs-extra';
|
|
12
|
+
import filewatcher from 'filewatcher';
|
|
13
|
+
import { exec, spawn } from 'child_process';
|
|
14
|
+
import { promisify } from 'util';
|
|
15
|
+
import path from 'path';
|
|
16
|
+
import { fileURLToPath } from 'url';
|
|
17
|
+
import crypto from 'crypto';
|
|
18
|
+
import os from 'os';
|
|
19
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
20
|
+
const __dirname = path.join(path.dirname(__filename), '..');
|
|
21
|
+
let TMP_DIR;
|
|
22
|
+
try {
|
|
23
|
+
TMP_DIR = os.tmpdir();
|
|
24
|
+
}
|
|
25
|
+
catch (e) {
|
|
26
|
+
TMP_DIR = '/tmp';
|
|
27
|
+
}
|
|
28
|
+
const execAsync = promisify(exec);
|
|
29
|
+
function hashify(obj) {
|
|
30
|
+
return crypto.createHash('sha256').update(JSON.stringify(obj)).digest('hex');
|
|
31
|
+
}
|
|
32
|
+
class CodeInjector {
|
|
33
|
+
constructor(adminforth) {
|
|
34
|
+
this.adminforth = adminforth;
|
|
35
|
+
}
|
|
36
|
+
// async runShell({command, verbose = false}) {
|
|
37
|
+
// console.log(`⚙️ Running shell ${command}...`);
|
|
38
|
+
// console.time(`${command} done in`);
|
|
39
|
+
// const { stdout: out, stderr: err } = await execAsync(command);
|
|
40
|
+
// console.timeEnd(`${command} done in`);
|
|
41
|
+
// console.log(`Command ${command} output:`, out, err);
|
|
42
|
+
// }
|
|
43
|
+
runNpmShell(_a) {
|
|
44
|
+
return __awaiter(this, arguments, void 0, function* ({ command, verbose = false, cwd }) {
|
|
45
|
+
const nodeBinary = process.execPath; // Path to the Node.js binary running this script
|
|
46
|
+
const npmPath = path.join(path.dirname(nodeBinary), 'npm'); // Path to the npm executable
|
|
47
|
+
const env = Object.assign({ VUE_APP_ADMINFORTH_PUBLIC_PATH: this.adminforth.config.baseUrl, FORCE_COLOR: '1' }, process.env);
|
|
48
|
+
console.log(`⚙️ Running npm ${command}...`);
|
|
49
|
+
console.time(`npm ${command} done in`);
|
|
50
|
+
const { stdout: out, stderr: err } = yield execAsync(`${nodeBinary} ${npmPath} ${command}`, {
|
|
51
|
+
cwd,
|
|
52
|
+
env,
|
|
53
|
+
});
|
|
54
|
+
console.timeEnd(`npm ${command} done in`);
|
|
55
|
+
if (verbose) {
|
|
56
|
+
console.log(`npm ${command} output:`, out);
|
|
57
|
+
}
|
|
58
|
+
if (err) {
|
|
59
|
+
console.error(`npm ${command} errors/warnings:`, err);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
rmTmpDir() {
|
|
64
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
65
|
+
// remove spa_tmp folder if it is exists
|
|
66
|
+
const spaTmpPath = CodeInjector.SPA_TMP_PATH;
|
|
67
|
+
try {
|
|
68
|
+
yield fs.promises.rm(spaTmpPath, { recursive: true });
|
|
69
|
+
}
|
|
70
|
+
catch (e) {
|
|
71
|
+
// ignore
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
prepareSources(_a) {
|
|
76
|
+
return __awaiter(this, arguments, void 0, function* ({ filesUpdated, verbose = false }) {
|
|
77
|
+
var _b, _c, _d;
|
|
78
|
+
const spaTmpPath = CodeInjector.SPA_TMP_PATH;
|
|
79
|
+
// check SPA_TMP_PATH exists and create if not
|
|
80
|
+
try {
|
|
81
|
+
yield fs.promises.access(spaTmpPath, fs.constants.F_OK);
|
|
82
|
+
}
|
|
83
|
+
catch (e) {
|
|
84
|
+
yield fs.promises.mkdir(spaTmpPath, { recursive: true });
|
|
85
|
+
}
|
|
86
|
+
const icons = [];
|
|
87
|
+
let routes = '';
|
|
88
|
+
const collectAssetsFromMenu = (menu) => {
|
|
89
|
+
menu.forEach((item) => {
|
|
90
|
+
if (item.icon) {
|
|
91
|
+
icons.push(item.icon);
|
|
92
|
+
}
|
|
93
|
+
if (item.component) {
|
|
94
|
+
routes += `{
|
|
95
|
+
path: '${item.path}',
|
|
96
|
+
name: '${item.path}',
|
|
97
|
+
component: import('${item.component}'),
|
|
98
|
+
},\n`;
|
|
99
|
+
}
|
|
100
|
+
if (item.children) {
|
|
101
|
+
collectAssetsFromMenu(item.children);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
};
|
|
105
|
+
collectAssetsFromMenu(this.adminforth.config.menu);
|
|
106
|
+
// create spa_tmp folder, or ignore if it exists
|
|
107
|
+
try {
|
|
108
|
+
yield fs.promises.mkdir(spaTmpPath);
|
|
109
|
+
}
|
|
110
|
+
catch (e) {
|
|
111
|
+
// ignore
|
|
112
|
+
}
|
|
113
|
+
if (filesUpdated) {
|
|
114
|
+
// copy only updated files
|
|
115
|
+
yield Promise.all(filesUpdated.map((file) => __awaiter(this, void 0, void 0, function* () {
|
|
116
|
+
const src = path.join(__dirname, 'spa', file);
|
|
117
|
+
const dest = path.join(spaTmpPath, file);
|
|
118
|
+
yield fsExtra.copy(src, dest);
|
|
119
|
+
})));
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
yield fsExtra.copy(path.join(__dirname, 'spa'), spaTmpPath, {
|
|
123
|
+
filter: (src) => {
|
|
124
|
+
return !src.includes('/adminforth/spa/node_modules') && !src.includes('/adminforth/spa/dist');
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
// copy whole custom directory
|
|
128
|
+
if ((_b = this.adminforth.config.customization) === null || _b === void 0 ? void 0 : _b.customComponentsDir) {
|
|
129
|
+
yield fsExtra.copy(this.adminforth.config.customization.customComponentsDir, path.join(CodeInjector.SPA_TMP_PATH, 'src', 'custom'), {
|
|
130
|
+
recursive: true,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
//collect all 'icon' fields from resources bulkActions
|
|
135
|
+
this.adminforth.config.resources.forEach((resource) => {
|
|
136
|
+
var _a;
|
|
137
|
+
if ((_a = resource.options) === null || _a === void 0 ? void 0 : _a.bulkActions) {
|
|
138
|
+
resource.options.bulkActions.forEach((action) => {
|
|
139
|
+
if (action.icon) {
|
|
140
|
+
icons.push(action.icon);
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
const uniqueIcons = Array.from(new Set(icons));
|
|
146
|
+
// icons are collectionName:iconName. Get list of all unique collection names:
|
|
147
|
+
const collections = new Set(icons.map((icon) => icon.split(':')[0]));
|
|
148
|
+
// package names @iconify-prerendered/vue-<collection name>
|
|
149
|
+
const packageNames = Array.from(collections).map((collection) => `@iconify-prerendered/vue-${collection}`);
|
|
150
|
+
// for each icon generate import statement
|
|
151
|
+
const iconImports = uniqueIcons.map((icon) => {
|
|
152
|
+
const [collection, iconName] = icon.split(':');
|
|
153
|
+
const PascalIconName = 'Icon' + iconName.split('-').map((part, index) => {
|
|
154
|
+
return part[0].toUpperCase() + part.slice(1);
|
|
155
|
+
}).join('');
|
|
156
|
+
return `import { ${PascalIconName} } from '@iconify-prerendered/vue-${collection}';`;
|
|
157
|
+
}).join('\n');
|
|
158
|
+
// Generate Vue.component statements for each icon
|
|
159
|
+
const iconComponents = uniqueIcons.map((icon) => {
|
|
160
|
+
const [collection, iconName] = icon.split(':');
|
|
161
|
+
const PascalIconName = 'Icon' + iconName.split('-').map((part, index) => {
|
|
162
|
+
return part[0].toUpperCase() + part.slice(1);
|
|
163
|
+
}).join('');
|
|
164
|
+
return `app.component('${PascalIconName}', ${PascalIconName});`;
|
|
165
|
+
}).join('\n');
|
|
166
|
+
let imports = iconImports + '\n';
|
|
167
|
+
if ((_c = this.adminforth.config.customization) === null || _c === void 0 ? void 0 : _c.vueUsesFile) {
|
|
168
|
+
imports += `import addCustomUses from '${this.adminforth.config.customization.vueUsesFile}';\n`;
|
|
169
|
+
}
|
|
170
|
+
// inject that code into spa_tmp/src/App.vue
|
|
171
|
+
const appVuePath = path.join(spaTmpPath, 'src', 'main.ts');
|
|
172
|
+
let appVueContent = yield fs.promises.readFile(appVuePath, 'utf-8');
|
|
173
|
+
appVueContent = appVueContent.replace('/* IMPORTANT:ADMINFORTH IMPORTS */', imports);
|
|
174
|
+
appVueContent = appVueContent.replace('/* IMPORTANT:ADMINFORTH COMPONENT REGISTRATIONS */', iconComponents + '\n');
|
|
175
|
+
if ((_d = this.adminforth.config.customization) === null || _d === void 0 ? void 0 : _d.vueUsesFile) {
|
|
176
|
+
appVueContent = appVueContent.replace('/* IMPORTANT:ADMINFORTH CUSTOM USES */', 'addCustomUses(app);');
|
|
177
|
+
}
|
|
178
|
+
yield fs.promises.writeFile(appVuePath, appVueContent);
|
|
179
|
+
/* generate custom rotes */
|
|
180
|
+
const routerVuePath = path.join(spaTmpPath, 'src', 'router', 'index.ts');
|
|
181
|
+
let routerVueContent = yield fs.promises.readFile(routerVuePath, 'utf-8');
|
|
182
|
+
routerVueContent = routerVueContent.replace('/* IMPORTANT:ADMINFORTH ROUTES */', routes);
|
|
183
|
+
yield fs.promises.writeFile(routerVuePath, routerVueContent);
|
|
184
|
+
/* hash checking */
|
|
185
|
+
const packageLockPath = path.join(spaTmpPath, 'package-lock.json');
|
|
186
|
+
const packageLock = JSON.parse(yield fs.promises.readFile(packageLockPath, 'utf-8'));
|
|
187
|
+
const lockHash = hashify(packageLock);
|
|
188
|
+
/* customPackageLock */
|
|
189
|
+
const customPackagePath = path.join('./package.json');
|
|
190
|
+
const customPackage = JSON.parse(yield fs.promises.readFile(customPackagePath, 'utf-8'));
|
|
191
|
+
const customPackageHash = hashify(customPackage);
|
|
192
|
+
const customLockPath = path.join('./package-lock.json');
|
|
193
|
+
const customLock = JSON.parse(yield fs.promises.readFile(customLockPath, 'utf-8'));
|
|
194
|
+
const customLockHash = hashify(customLock);
|
|
195
|
+
const packagesNamesHash = hashify(packageNames);
|
|
196
|
+
const fullHash = hashify([lockHash, packagesNamesHash, customLockHash]);
|
|
197
|
+
const hashPath = path.join(spaTmpPath, 'node_modules', '.adminforth_hash');
|
|
198
|
+
try {
|
|
199
|
+
const existingHash = yield fs.promises.readFile(hashPath, 'utf-8');
|
|
200
|
+
if (existingHash === fullHash) {
|
|
201
|
+
console.log('Hashes match, skipping npm ci/install');
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
catch (e) {
|
|
206
|
+
// ignore
|
|
207
|
+
}
|
|
208
|
+
yield this.runNpmShell({ command: 'ci', verbose, cwd: spaTmpPath });
|
|
209
|
+
// get packages with version from customPackage
|
|
210
|
+
const customPackgeNames = [...Object.keys(customPackage.dependencies), ...Object.keys(customPackage.devDependencies || [])].reduce((acc, packageName) => {
|
|
211
|
+
const version = customLock.packages[`node_modules/${packageName}`].version;
|
|
212
|
+
acc.push(`${packageName}@${version}`);
|
|
213
|
+
return acc;
|
|
214
|
+
}, []);
|
|
215
|
+
if (packageNames.length) {
|
|
216
|
+
const npmInstallCommand = `install ${[...packageNames, ...customPackgeNames].join(' ')}`;
|
|
217
|
+
yield this.runNpmShell({ command: npmInstallCommand, cwd: spaTmpPath });
|
|
218
|
+
}
|
|
219
|
+
yield fs.promises.writeFile(hashPath, fullHash);
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
watchForReprepare(_a) {
|
|
223
|
+
return __awaiter(this, arguments, void 0, function* ({ verbose }) {
|
|
224
|
+
const spaPath = path.join(__dirname, 'spa');
|
|
225
|
+
// get list of all subdirectories in spa recursively
|
|
226
|
+
const directories = [];
|
|
227
|
+
const collectDirectories = (dir) => __awaiter(this, void 0, void 0, function* () {
|
|
228
|
+
const files = yield fs.promises.readdir(dir, { withFileTypes: true });
|
|
229
|
+
for (const file of files) {
|
|
230
|
+
if (file.isDirectory() && ['node_modules', 'dist'].indexOf(file.name) === -1) {
|
|
231
|
+
directories.push(path.join(dir, file.name));
|
|
232
|
+
yield collectDirectories(path.join(dir, file.name));
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
yield collectDirectories(spaPath);
|
|
237
|
+
if (verbose) {
|
|
238
|
+
console.log('🔎 Watching for changes in:', directories.join(','));
|
|
239
|
+
}
|
|
240
|
+
const watcher = filewatcher();
|
|
241
|
+
directories.forEach((dir) => {
|
|
242
|
+
watcher.add(dir);
|
|
243
|
+
});
|
|
244
|
+
watcher.on('change', (file) => __awaiter(this, void 0, void 0, function* () {
|
|
245
|
+
console.log(`File ${file} changed, preparing sources...`);
|
|
246
|
+
yield this.prepareSources({ filesUpdated: [file.replace(spaPath + '/', '')] });
|
|
247
|
+
}));
|
|
248
|
+
process.on('exit', () => {
|
|
249
|
+
watcher.removeAll();
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
watchCustomComponentsForCopy(_a) {
|
|
254
|
+
return __awaiter(this, arguments, void 0, function* ({ verbose }) {
|
|
255
|
+
const customComponentsDir = this.adminforth.config.customization.customComponentsDir;
|
|
256
|
+
// check if folder exists
|
|
257
|
+
try {
|
|
258
|
+
yield fs.promises.access(customComponentsDir, fs.constants.F_OK);
|
|
259
|
+
}
|
|
260
|
+
catch (e) {
|
|
261
|
+
if (verbose) {
|
|
262
|
+
console.log(`Custom components dir ${customComponentsDir} does not exist, skipping watching`);
|
|
263
|
+
}
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
// get all subdirs
|
|
267
|
+
const directories = [];
|
|
268
|
+
const collectDirectories = (dir) => __awaiter(this, void 0, void 0, function* () {
|
|
269
|
+
directories.push(dir);
|
|
270
|
+
const files = yield fs.promises.readdir(dir, { withFileTypes: true });
|
|
271
|
+
for (const file of files) {
|
|
272
|
+
if (file.isDirectory()) {
|
|
273
|
+
yield collectDirectories(path.join(dir, file.name));
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
yield collectDirectories(customComponentsDir);
|
|
278
|
+
const watcher = filewatcher();
|
|
279
|
+
directories.forEach((dir) => {
|
|
280
|
+
watcher.add(dir);
|
|
281
|
+
});
|
|
282
|
+
if (verbose) {
|
|
283
|
+
console.log('🔎 Watching for changes in:', directories.join(','));
|
|
284
|
+
}
|
|
285
|
+
watcher.on('change', (file) => __awaiter(this, void 0, void 0, function* () {
|
|
286
|
+
// copy one file
|
|
287
|
+
// TODO: non optimal, copy only changed file, test on both nested and parent dir
|
|
288
|
+
if (verbose) {
|
|
289
|
+
console.log(`🔎 File ${file} changed, copying to spa_tmp...`);
|
|
290
|
+
}
|
|
291
|
+
yield fsExtra.copy(this.adminforth.config.customization.customComponentsDir, path.join(CodeInjector.SPA_TMP_PATH, 'src', 'custom'), {
|
|
292
|
+
recursive: true,
|
|
293
|
+
});
|
|
294
|
+
}));
|
|
295
|
+
process.on('exit', () => {
|
|
296
|
+
watcher.removeAll();
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
bundleNow(_a) {
|
|
301
|
+
return __awaiter(this, arguments, void 0, function* ({ hotReload = false, verbose = false }) {
|
|
302
|
+
this.adminforth.runningHotReload = hotReload;
|
|
303
|
+
yield this.prepareSources({ verbose });
|
|
304
|
+
if (hotReload) {
|
|
305
|
+
yield this.watchForReprepare({ verbose });
|
|
306
|
+
yield this.watchCustomComponentsForCopy({ verbose });
|
|
307
|
+
}
|
|
308
|
+
console.log('AdminForth bundling');
|
|
309
|
+
const cwd = CodeInjector.SPA_TMP_PATH;
|
|
310
|
+
if (!hotReload) {
|
|
311
|
+
// probably add option to build with tsh check (plain 'build')
|
|
312
|
+
yield this.runNpmShell({ command: 'run build-only', verbose, cwd });
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
const command = 'run dev';
|
|
316
|
+
console.log(`⚙️ Running npm ${command}...`);
|
|
317
|
+
const nodeBinary = process.execPath;
|
|
318
|
+
const npmPath = path.join(path.dirname(nodeBinary), 'npm');
|
|
319
|
+
const env = Object.assign({ VUE_APP_ADMINFORTH_PUBLIC_PATH: this.adminforth.config.baseUrl, FORCE_COLOR: '1' }, process.env);
|
|
320
|
+
const devServer = spawn(`${nodeBinary}`, [`${npmPath}`, ...command.split(' ')], {
|
|
321
|
+
cwd,
|
|
322
|
+
env,
|
|
323
|
+
});
|
|
324
|
+
devServer.stdout.on('data', (data) => {
|
|
325
|
+
console.log(`[AdminForth SPA]:`);
|
|
326
|
+
process.stdout.write(data);
|
|
327
|
+
});
|
|
328
|
+
devServer.stderr.on('data', (data) => {
|
|
329
|
+
console.error(`[AdminForth SPA ERR]:`);
|
|
330
|
+
process.stdout.write(data);
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
CodeInjector.SPA_TMP_PATH = path.join(TMP_DIR, 'adminforth', 'spa_tmp');
|
|
337
|
+
export default CodeInjector;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export function guessLabelFromName(name) {
|
|
2
|
+
if (name.includes('_')) {
|
|
3
|
+
return name.split('_').map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(' ');
|
|
4
|
+
}
|
|
5
|
+
else if (name.includes('-')) {
|
|
6
|
+
return name.split('-').map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(' ');
|
|
7
|
+
}
|
|
8
|
+
else {
|
|
9
|
+
// split by capital letters
|
|
10
|
+
return name.split(/(?=[A-Z])/).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(' ');
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -0,0 +1,210 @@
|
|
|
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 path from 'path';
|
|
11
|
+
import { fileURLToPath } from 'url';
|
|
12
|
+
import fs from 'fs';
|
|
13
|
+
import CodeInjector from '../modules/codeInjector.js';
|
|
14
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
15
|
+
const __dirname = path.dirname(__filename);
|
|
16
|
+
function replaceAtStart(string, substring) {
|
|
17
|
+
if (string.startsWith(substring)) {
|
|
18
|
+
return string.slice(substring.length);
|
|
19
|
+
}
|
|
20
|
+
return string;
|
|
21
|
+
}
|
|
22
|
+
function proxyTo(url, res) {
|
|
23
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
24
|
+
const r = yield fetch(url);
|
|
25
|
+
const body = yield r.text();
|
|
26
|
+
res.status(r.status);
|
|
27
|
+
r.headers.forEach((value, name) => {
|
|
28
|
+
res.setHeader(name, value);
|
|
29
|
+
});
|
|
30
|
+
res.send(body);
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
function parseExpressCookie(req) {
|
|
34
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
35
|
+
const cookies = req.headers.cookie;
|
|
36
|
+
if (!cookies) {
|
|
37
|
+
return {};
|
|
38
|
+
}
|
|
39
|
+
const parts = cookies.split('; ');
|
|
40
|
+
const result = {};
|
|
41
|
+
parts.forEach(part => {
|
|
42
|
+
const [key, value] = part.split('=');
|
|
43
|
+
result[key] = value;
|
|
44
|
+
});
|
|
45
|
+
return result;
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
const respondNoServer = (title, explanation) => {
|
|
49
|
+
return `
|
|
50
|
+
<!DOCTYPE html>
|
|
51
|
+
<html lang="en">
|
|
52
|
+
<head>
|
|
53
|
+
<meta charset="UTF-8">
|
|
54
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
|
55
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
56
|
+
<title>AdminForth</title>
|
|
57
|
+
</head>
|
|
58
|
+
<body>
|
|
59
|
+
<div class="center">
|
|
60
|
+
<h1>Oops!</h1>
|
|
61
|
+
<h2>${title}</h2>
|
|
62
|
+
<p>${explanation}</p>
|
|
63
|
+
</div>
|
|
64
|
+
<style>
|
|
65
|
+
body {
|
|
66
|
+
font-family: Arial, sans-serif;
|
|
67
|
+
background-color: #f0f0f0;
|
|
68
|
+
margin: 0;
|
|
69
|
+
padding: 0;
|
|
70
|
+
}
|
|
71
|
+
.center {
|
|
72
|
+
display: flex;
|
|
73
|
+
justify-content: center;
|
|
74
|
+
align-items: center;
|
|
75
|
+
height: 100vh;
|
|
76
|
+
flex-direction: column;
|
|
77
|
+
}
|
|
78
|
+
</style>
|
|
79
|
+
<script>
|
|
80
|
+
setTimeout(() => {
|
|
81
|
+
location.reload();
|
|
82
|
+
}, 1500);
|
|
83
|
+
</script>
|
|
84
|
+
</body>
|
|
85
|
+
`;
|
|
86
|
+
};
|
|
87
|
+
class ExpressServer {
|
|
88
|
+
constructor(adminforth) {
|
|
89
|
+
this.adminforth = adminforth;
|
|
90
|
+
}
|
|
91
|
+
setupSpaServer() {
|
|
92
|
+
const prefix = this.adminforth.config.baseUrl;
|
|
93
|
+
const slashedPrefix = prefix.endsWith('/') ? prefix : `${prefix}/`;
|
|
94
|
+
if (this.adminforth.runningHotReload) {
|
|
95
|
+
const handler = (req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
96
|
+
// proxy using fetch to webpack dev server
|
|
97
|
+
try {
|
|
98
|
+
yield proxyTo(`http://localhost:5173${req.url}`, res);
|
|
99
|
+
}
|
|
100
|
+
catch (e) {
|
|
101
|
+
res.status(500).send(respondNoServer('AdminForth SPA is not ready yet', 'Vite is still starting up. Please wait a moment...'));
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
this.expressApp.get(`${slashedPrefix}assets/*`, handler);
|
|
106
|
+
this.expressApp.get(`${prefix}*`, handler);
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
this.expressApp.get(`${slashedPrefix}assets/*`, (req, res) => {
|
|
110
|
+
res.sendFile(path.join(CodeInjector.SPA_TMP_PATH, 'dist', replaceAtStart(req.url, prefix)));
|
|
111
|
+
});
|
|
112
|
+
this.expressApp.get(`${prefix}*`, (req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
113
|
+
const fullPath = path.join(CodeInjector.SPA_TMP_PATH, 'dist', 'index.html');
|
|
114
|
+
let fileExists = true;
|
|
115
|
+
try {
|
|
116
|
+
yield fs.promises.access(fullPath, fs.constants.F_OK);
|
|
117
|
+
}
|
|
118
|
+
catch (e) {
|
|
119
|
+
fileExists = false;
|
|
120
|
+
}
|
|
121
|
+
if (!fileExists) {
|
|
122
|
+
res.status(500).send(respondNoServer(`${this.adminforth.config.brandName} is still warming up`, 'Please wait a moment...'));
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
res.sendFile(fullPath);
|
|
126
|
+
}));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
serve(app) {
|
|
130
|
+
this.expressApp = app;
|
|
131
|
+
this.adminforth.setupEndpoints(this);
|
|
132
|
+
this.setupSpaServer();
|
|
133
|
+
}
|
|
134
|
+
authorize(handler) {
|
|
135
|
+
return (req, res, next) => __awaiter(this, void 0, void 0, function* () {
|
|
136
|
+
const cookies = yield parseExpressCookie(req);
|
|
137
|
+
const jwt = cookies['adminforth_jwt'];
|
|
138
|
+
if (!jwt) {
|
|
139
|
+
res.status(401).send('Unauthorized by AdminForth');
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
const adminforthUser = this.adminforth.auth.verify(jwt);
|
|
143
|
+
if (!adminforthUser) {
|
|
144
|
+
res.status(401).send('Unauthorized by AdminForth');
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
req.adminUser = adminforthUser;
|
|
148
|
+
handler(req, res, next);
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
endpoint({ method = 'GET', path, handler, noAuth = false }) {
|
|
153
|
+
if (!path.startsWith('/')) {
|
|
154
|
+
throw new Error(`Path must start with /, got: ${path}`);
|
|
155
|
+
}
|
|
156
|
+
const fullPath = `${this.adminforth.config.baseUrl}/adminapi/v1${path}`;
|
|
157
|
+
const expressHandler = (req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
158
|
+
let body = req.body || {};
|
|
159
|
+
if (typeof body === 'string') {
|
|
160
|
+
try {
|
|
161
|
+
body = JSON.parse(body);
|
|
162
|
+
}
|
|
163
|
+
catch (e) {
|
|
164
|
+
console.error('Failed to parse body', e);
|
|
165
|
+
res.status(400).send('Invalid JSON body');
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
const query = req.query;
|
|
169
|
+
const adminUser = req.adminUser;
|
|
170
|
+
const headers = req.headers;
|
|
171
|
+
const cookies = yield parseExpressCookie(req);
|
|
172
|
+
const response = {
|
|
173
|
+
headers: {},
|
|
174
|
+
status: 200,
|
|
175
|
+
message: undefined,
|
|
176
|
+
setHeader(name, value) {
|
|
177
|
+
this.headers[name] = value;
|
|
178
|
+
},
|
|
179
|
+
setStatus(code, message) {
|
|
180
|
+
this.status = code;
|
|
181
|
+
this.message = message;
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
const input = { body, query, headers, cookies, response, _raw_express_req: req, _raw_express_res: res };
|
|
185
|
+
let output;
|
|
186
|
+
try {
|
|
187
|
+
output = yield handler(input);
|
|
188
|
+
}
|
|
189
|
+
catch (e) {
|
|
190
|
+
console.error('Error in handler', e);
|
|
191
|
+
// print full stack trace
|
|
192
|
+
console.error(e.stack);
|
|
193
|
+
res.status(500).send('Internal server error');
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
Object.keys(response.headers).forEach((name) => {
|
|
197
|
+
res.setHeader(name, response.headers[name]);
|
|
198
|
+
});
|
|
199
|
+
const resp = res.status(response.status);
|
|
200
|
+
if (response.message) {
|
|
201
|
+
resp.send(response.message);
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
res.json(output);
|
|
205
|
+
});
|
|
206
|
+
console.log(`Adding endpoint ${method} ${fullPath}`);
|
|
207
|
+
this.expressApp[method.toLowerCase()](fullPath, noAuth ? expressHandler : this.authorize(expressHandler));
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
export default ExpressServer;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const vue_1 = require("vue");
|
|
7
|
+
const pinia_1 = require("pinia");
|
|
8
|
+
/* IMPORTANT:ADMINFORTH IMPORTS */
|
|
9
|
+
const App_vue_1 = __importDefault(require("./App.vue"));
|
|
10
|
+
const router_1 = __importDefault(require("./router"));
|
|
11
|
+
const app = (0, vue_1.createApp)(App_vue_1.default);
|
|
12
|
+
/* IMPORTANT:ADMINFORTH COMPONENT REGISTRATIONS */
|
|
13
|
+
app.use((0, pinia_1.createPinia)());
|
|
14
|
+
app.use(router_1.default);
|
|
15
|
+
/* IMPORTANT:ADMINFORTH CUSTOM USES */
|
|
16
|
+
app.mount('#app');
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
28
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
+
const vue_router_1 = require("vue-router");
|
|
30
|
+
const HomeView_vue_1 = __importDefault(require("../views/HomeView.vue"));
|
|
31
|
+
const ResourceParent_vue_1 = __importDefault(require("@/views/ResourceParent.vue"));
|
|
32
|
+
const ListView_vue_1 = __importDefault(require("@/views/ListView.vue"));
|
|
33
|
+
const ShowView_vue_1 = __importDefault(require("@/views/ShowView.vue"));
|
|
34
|
+
const EditView_vue_1 = __importDefault(require("@/views/EditView.vue"));
|
|
35
|
+
const CreateView_vue_1 = __importDefault(require("@/views/CreateView.vue"));
|
|
36
|
+
const router = (0, vue_router_1.createRouter)({
|
|
37
|
+
history: (0, vue_router_1.createWebHistory)(import.meta.env.BASE_URL),
|
|
38
|
+
routes: [
|
|
39
|
+
{
|
|
40
|
+
path: '/',
|
|
41
|
+
name: 'home',
|
|
42
|
+
component: HomeView_vue_1.default
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
path: '/login',
|
|
46
|
+
name: 'login',
|
|
47
|
+
component: () => Promise.resolve().then(() => __importStar(require('@/views/LoginView.vue')))
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
path: '/resource/:resourceId',
|
|
51
|
+
component: ResourceParent_vue_1.default,
|
|
52
|
+
name: 'resource',
|
|
53
|
+
children: [
|
|
54
|
+
{
|
|
55
|
+
path: '',
|
|
56
|
+
component: ListView_vue_1.default,
|
|
57
|
+
name: 'resource-list'
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
path: 'show/:primaryKey',
|
|
61
|
+
component: ShowView_vue_1.default,
|
|
62
|
+
name: 'resource-show'
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
path: 'edit/:primaryKey',
|
|
66
|
+
component: EditView_vue_1.default,
|
|
67
|
+
name: 'resource-edit'
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
path: 'create',
|
|
71
|
+
component: CreateView_vue_1.default,
|
|
72
|
+
name: 'resource-create'
|
|
73
|
+
},
|
|
74
|
+
]
|
|
75
|
+
},
|
|
76
|
+
/* IMPORTANT:ADMINFORTH ROUTES */
|
|
77
|
+
]
|
|
78
|
+
});
|
|
79
|
+
exports.default = router;
|