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.
Files changed (35) hide show
  1. package/dataConnectors/{mongo.js → mongo.ts} +15 -10
  2. package/dataConnectors/{postgres.js → postgres.ts} +6 -2
  3. package/dataConnectors/{sqlite.js → sqlite.ts} +6 -2
  4. package/dist/auth.js +68 -0
  5. package/dist/dataConnectors/mongo.js +204 -0
  6. package/dist/dataConnectors/postgres.js +298 -0
  7. package/dist/dataConnectors/sqlite.js +261 -0
  8. package/dist/index.js +693 -0
  9. package/dist/modules/codeInjector.js +337 -0
  10. package/dist/modules/utils.js +12 -0
  11. package/dist/servers/express.js +210 -0
  12. package/dist/spa/src/main.js +16 -0
  13. package/dist/spa/src/router/index.js +79 -0
  14. package/dist/spa/src/stores/core.js +154 -0
  15. package/dist/spa/src/stores/modal.js +35 -0
  16. package/dist/spa/src/utils.js +59 -0
  17. package/dist/spa/vite.config.js +44 -0
  18. package/dist/spa_tmp/src/custom/custom/vueUses.js +10 -0
  19. package/dist/spa_tmp/src/main.js +29 -0
  20. package/dist/spa_tmp/src/router/index.js +83 -0
  21. package/dist/spa_tmp/src/stores/core.js +150 -0
  22. package/dist/spa_tmp/src/stores/modal.js +35 -0
  23. package/dist/spa_tmp/src/utils.js +59 -0
  24. package/dist/spa_tmp/vite.config.js +43 -0
  25. package/dist/types.js +30 -0
  26. package/{index.js → index.ts} +115 -6
  27. package/modules/{codeInjector.js → codeInjector.ts} +17 -13
  28. package/package.json +9 -3
  29. package/servers/{express.js → express.ts} +8 -2
  30. package/spa/package.json +2 -2
  31. package/spa/src/views/ListView.vue +1 -1
  32. package/tsconfig.json +112 -0
  33. package/{types.js → types.ts} +2 -0
  34. /package/{auth.js → auth.ts} +0 -0
  35. /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;