adminforth 1.0.30 → 1.0.32
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/postgres.ts +9 -6
- package/dist/dataConnectors/postgres.js +9 -6
- package/dist/index.js +97 -25
- package/dist/modules/codeInjector.js +37 -15
- package/dist/spa/spa/package-lock.json +108 -1
- package/dist/spa/spa/package.json +4 -1
- package/dist/spa/spa/src/App.vue +2 -2
- package/dist/spa/spa/src/components/AcceptModal.vue +1 -1
- package/dist/spa/spa/src/components/CustomRangePicker.vue +143 -0
- package/dist/spa/spa/src/components/Dropdown.vue +1 -2
- package/dist/spa/spa/src/components/Filters.vue +54 -21
- package/dist/spa/spa/src/components/ResourceForm.vue +38 -4
- package/dist/spa/spa/src/router/index.ts +3 -1
- package/dist/spa/spa/src/stores/core.ts +1 -14
- package/dist/spa/spa/src/views/ListView.vue +1 -2
- package/dist/spa/spa/src/views/LoginView.vue +1 -1
- package/index.ts +73 -0
- package/modules/codeInjector.ts +45 -18
- package/package.json +1 -1
- package/spa/package-lock.json +108 -1
- package/spa/package.json +4 -1
- package/spa/src/App.vue +2 -2
- package/spa/src/components/AcceptModal.vue +1 -1
- package/spa/src/components/CustomRangePicker.vue +143 -0
- package/spa/src/components/Dropdown.vue +1 -2
- package/spa/src/components/Filters.vue +54 -21
- package/spa/src/components/ResourceForm.vue +38 -4
- package/spa/src/router/index.ts +3 -1
- package/spa/src/stores/core.ts +1 -14
- package/spa/src/views/ListView.vue +1 -2
- package/spa/src/views/LoginView.vue +1 -1
|
@@ -147,7 +147,7 @@ class PostgresConnector {
|
|
|
147
147
|
|
|
148
148
|
async getRecordByPrimaryKey(resource, key) {
|
|
149
149
|
const tableName = resource.table;
|
|
150
|
-
const columns = resource.dataSourceColumns.map((col) => col.name).join(', ');
|
|
150
|
+
const columns = resource.dataSourceColumns.map((col) => `"${col.name}"`).join(', ');
|
|
151
151
|
const stmt = await this.db.query(`SELECT ${columns} FROM ${tableName} WHERE ${this.getPrimaryKey(resource)} = $1`, [key]);
|
|
152
152
|
const row = stmt.rows[0];
|
|
153
153
|
if (!row) {
|
|
@@ -177,7 +177,7 @@ class PostgresConnector {
|
|
|
177
177
|
}
|
|
178
178
|
|
|
179
179
|
async getData({ resource, limit, offset, sort, filters }) {
|
|
180
|
-
const columns = resource.dataSourceColumns.filter(c=> !c.virtual).map((col) => col.name).join(', ');
|
|
180
|
+
const columns = resource.dataSourceColumns.filter(c=> !c.virtual).map((col) => `"${col.name}"`).join(', ');
|
|
181
181
|
const tableName = resource.table;
|
|
182
182
|
|
|
183
183
|
for (const filter of filters) {
|
|
@@ -199,7 +199,7 @@ class PostgresConnector {
|
|
|
199
199
|
} else {
|
|
200
200
|
totalCounter += 1;
|
|
201
201
|
}
|
|
202
|
-
return
|
|
202
|
+
return `"${field}" ${operator} ${placeholder}`
|
|
203
203
|
}).join(' AND ')}` : '';
|
|
204
204
|
|
|
205
205
|
const filterValues = [];
|
|
@@ -267,17 +267,20 @@ class PostgresConnector {
|
|
|
267
267
|
return record[colName];
|
|
268
268
|
}
|
|
269
269
|
});
|
|
270
|
+
for (let i = 0; i < columns.length; i++) {
|
|
271
|
+
columns[i] = `"${columns[i]}"`;
|
|
272
|
+
}
|
|
270
273
|
await this.db.query(`INSERT INTO ${tableName} (${columns.join(', ')}) VALUES (${placeholders})`, values);
|
|
271
274
|
}
|
|
272
275
|
|
|
273
276
|
async updateRecord({ resource, recordId, record, newValues }) {
|
|
274
277
|
const values = [...Object.values(newValues), recordId];
|
|
275
|
-
const columnsWithPlaceholders = Object.keys(newValues).map((col, i) =>
|
|
276
|
-
await this.db.query(`UPDATE ${resource.table} SET ${columnsWithPlaceholders} WHERE ${this.getPrimaryKey(resource)} = $${values.length}`, values);
|
|
278
|
+
const columnsWithPlaceholders = Object.keys(newValues).map((col, i) => `"${col}" = $${i + 1}`).join(', ');
|
|
279
|
+
await this.db.query(`UPDATE ${resource.table} SET ${columnsWithPlaceholders} WHERE "${this.getPrimaryKey(resource)}" = $${values.length}`, values);
|
|
277
280
|
}
|
|
278
281
|
|
|
279
282
|
async deleteRecord({ resource, recordId }) {
|
|
280
|
-
await this.db.query(`DELETE FROM ${resource.table} WHERE ${this.getPrimaryKey(resource)} = $1`, [recordId]);
|
|
283
|
+
await this.db.query(`DELETE FROM ${resource.table} WHERE "${this.getPrimaryKey(resource)}" = $1`, [recordId]);
|
|
281
284
|
}
|
|
282
285
|
|
|
283
286
|
async close() {
|
|
@@ -148,7 +148,7 @@ class PostgresConnector {
|
|
|
148
148
|
getRecordByPrimaryKey(resource, key) {
|
|
149
149
|
return __awaiter(this, void 0, void 0, function* () {
|
|
150
150
|
const tableName = resource.table;
|
|
151
|
-
const columns = resource.dataSourceColumns.map((col) => col.name).join(', ');
|
|
151
|
+
const columns = resource.dataSourceColumns.map((col) => `"${col.name}"`).join(', ');
|
|
152
152
|
const stmt = yield this.db.query(`SELECT ${columns} FROM ${tableName} WHERE ${this.getPrimaryKey(resource)} = $1`, [key]);
|
|
153
153
|
const row = stmt.rows[0];
|
|
154
154
|
if (!row) {
|
|
@@ -180,7 +180,7 @@ class PostgresConnector {
|
|
|
180
180
|
}
|
|
181
181
|
getData(_a) {
|
|
182
182
|
return __awaiter(this, arguments, void 0, function* ({ resource, limit, offset, sort, filters }) {
|
|
183
|
-
const columns = resource.dataSourceColumns.filter(c => !c.virtual).map((col) => col.name).join(', ');
|
|
183
|
+
const columns = resource.dataSourceColumns.filter(c => !c.virtual).map((col) => `"${col.name}"`).join(', ');
|
|
184
184
|
const tableName = resource.table;
|
|
185
185
|
for (const filter of filters) {
|
|
186
186
|
if (!this.OperatorsMap[filter.operator]) {
|
|
@@ -202,7 +202,7 @@ class PostgresConnector {
|
|
|
202
202
|
else {
|
|
203
203
|
totalCounter += 1;
|
|
204
204
|
}
|
|
205
|
-
return
|
|
205
|
+
return `"${field}" ${operator} ${placeholder}`;
|
|
206
206
|
}).join(' AND ')}` : '';
|
|
207
207
|
const filterValues = [];
|
|
208
208
|
filters.length ? filters.forEach((f) => {
|
|
@@ -272,19 +272,22 @@ class PostgresConnector {
|
|
|
272
272
|
return record[colName];
|
|
273
273
|
}
|
|
274
274
|
});
|
|
275
|
+
for (let i = 0; i < columns.length; i++) {
|
|
276
|
+
columns[i] = `"${columns[i]}"`;
|
|
277
|
+
}
|
|
275
278
|
yield this.db.query(`INSERT INTO ${tableName} (${columns.join(', ')}) VALUES (${placeholders})`, values);
|
|
276
279
|
});
|
|
277
280
|
}
|
|
278
281
|
updateRecord(_a) {
|
|
279
282
|
return __awaiter(this, arguments, void 0, function* ({ resource, recordId, record, newValues }) {
|
|
280
283
|
const values = [...Object.values(newValues), recordId];
|
|
281
|
-
const columnsWithPlaceholders = Object.keys(newValues).map((col, i) =>
|
|
282
|
-
yield this.db.query(`UPDATE ${resource.table} SET ${columnsWithPlaceholders} WHERE ${this.getPrimaryKey(resource)} = $${values.length}`, values);
|
|
284
|
+
const columnsWithPlaceholders = Object.keys(newValues).map((col, i) => `"${col}" = $${i + 1}`).join(', ');
|
|
285
|
+
yield this.db.query(`UPDATE ${resource.table} SET ${columnsWithPlaceholders} WHERE "${this.getPrimaryKey(resource)}" = $${values.length}`, values);
|
|
283
286
|
});
|
|
284
287
|
}
|
|
285
288
|
deleteRecord(_a) {
|
|
286
289
|
return __awaiter(this, arguments, void 0, function* ({ resource, recordId }) {
|
|
287
|
-
yield this.db.query(`DELETE FROM ${resource.table} WHERE ${this.getPrimaryKey(resource)} = $1`, [recordId]);
|
|
290
|
+
yield this.db.query(`DELETE FROM ${resource.table} WHERE "${this.getPrimaryKey(resource)}" = $1`, [recordId]);
|
|
288
291
|
});
|
|
289
292
|
}
|
|
290
293
|
close() {
|
package/dist/index.js
CHANGED
|
@@ -22,6 +22,14 @@ import { guessLabelFromName } from './modules/utils.js';
|
|
|
22
22
|
import ExpressServer from './servers/express.js';
|
|
23
23
|
import { v1 as uuid } from 'uuid';
|
|
24
24
|
import fs from 'fs';
|
|
25
|
+
let package_json;
|
|
26
|
+
if (fs.existsSync('../package.json')) {
|
|
27
|
+
// in prod we are in dist folder
|
|
28
|
+
package_json = JSON.parse(fs.readFileSync('../package.json', 'utf8'));
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
package_json = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
|
|
32
|
+
}
|
|
25
33
|
import { AdminForthFilterOperators, AdminForthTypes } from './types.js';
|
|
26
34
|
const AVAILABLE_SHOW_IN = ['list', 'edit', 'create', 'filter', 'show'];
|
|
27
35
|
const DEFAULT_ALLOWED_ACTIONS = { create: true, edit: true, show: true, delete: true };
|
|
@@ -409,6 +417,7 @@ class AdminForth {
|
|
|
409
417
|
usernameField: this.config.auth.usernameField,
|
|
410
418
|
},
|
|
411
419
|
adminUser,
|
|
420
|
+
version: package_json.version,
|
|
412
421
|
};
|
|
413
422
|
}),
|
|
414
423
|
});
|
|
@@ -455,10 +464,73 @@ class AdminForth {
|
|
|
455
464
|
return Object.assign(Object.assign({}, data), { options: resource === null || resource === void 0 ? void 0 : resource.options });
|
|
456
465
|
}),
|
|
457
466
|
});
|
|
467
|
+
server.endpoint({
|
|
468
|
+
method: 'POST',
|
|
469
|
+
path: '/get_resource_foreign_data',
|
|
470
|
+
handler: (_h) => __awaiter(this, [_h], void 0, function* ({ body, adminUser }) {
|
|
471
|
+
var _j, _k, _l, _m;
|
|
472
|
+
const { resourceId, column } = body;
|
|
473
|
+
if (!this.statuses.dbDiscover) {
|
|
474
|
+
return { error: 'Database discovery not started' };
|
|
475
|
+
}
|
|
476
|
+
if (this.statuses.dbDiscover !== 'done') {
|
|
477
|
+
return { error: 'Database discovery is still in progress, please try later' };
|
|
478
|
+
}
|
|
479
|
+
const resource = this.config.resources.find((res) => res.resourceId == resourceId);
|
|
480
|
+
if (!resource) {
|
|
481
|
+
return { error: `Resource ${resourceId} not found` };
|
|
482
|
+
}
|
|
483
|
+
const columnConfig = resource.columns.find((col) => col.name == column);
|
|
484
|
+
if (!columnConfig.foreignResource) {
|
|
485
|
+
return { error: `Column ${column} in resource ${resourceId} is not a foreign key` };
|
|
486
|
+
}
|
|
487
|
+
const targetResourceId = columnConfig.foreignResource.resourceId;
|
|
488
|
+
const targetResource = this.config.resources.find((res) => res.resourceId == targetResourceId);
|
|
489
|
+
if ((_j = columnConfig.foreignResource.hooks) === null || _j === void 0 ? void 0 : _j.beforeDatasourceRequest) {
|
|
490
|
+
const resp = yield ((_k = column.foreignResource.hooks) === null || _k === void 0 ? void 0 : _k.beforeDatasourceRequest({ query: body, adminUser }));
|
|
491
|
+
if (!resp || (!resp.ok && !resp.error)) {
|
|
492
|
+
throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
|
|
493
|
+
}
|
|
494
|
+
if (resp.error) {
|
|
495
|
+
return { error: resp.error };
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
const { limit, offset, filters, sort } = body;
|
|
499
|
+
const dbDataItems = yield this.connectors[targetResource.dataSource].getData({
|
|
500
|
+
resource: targetResource,
|
|
501
|
+
limit,
|
|
502
|
+
offset,
|
|
503
|
+
filters: filters || [],
|
|
504
|
+
sort: sort || [],
|
|
505
|
+
});
|
|
506
|
+
const items = dbDataItems.data.map((item) => {
|
|
507
|
+
const pk = item[targetResource.columns.find((col) => col.primaryKey).name];
|
|
508
|
+
const labler = targetResource.itemLabel || ((record) => `${targetResource.label} ${pk}`);
|
|
509
|
+
return {
|
|
510
|
+
value: pk,
|
|
511
|
+
label: labler(item),
|
|
512
|
+
_item: item, // user might need it in hook to form new label
|
|
513
|
+
};
|
|
514
|
+
});
|
|
515
|
+
const response = {
|
|
516
|
+
items
|
|
517
|
+
};
|
|
518
|
+
if ((_l = columnConfig.foreignResource.hooks) === null || _l === void 0 ? void 0 : _l.afterDatasourceRequest) {
|
|
519
|
+
const resp = yield ((_m = column.foreignResource.hooks) === null || _m === void 0 ? void 0 : _m.afterDatasourceRequest({ response, adminUser }));
|
|
520
|
+
if (!resp || (!resp.ok && !resp.error)) {
|
|
521
|
+
throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
|
|
522
|
+
}
|
|
523
|
+
if (resp.error) {
|
|
524
|
+
return { error: resp.error };
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
return response;
|
|
528
|
+
}),
|
|
529
|
+
});
|
|
458
530
|
server.endpoint({
|
|
459
531
|
method: 'POST',
|
|
460
532
|
path: '/get_min_max_for_columns',
|
|
461
|
-
handler: (
|
|
533
|
+
handler: (_o) => __awaiter(this, [_o], void 0, function* ({ body }) {
|
|
462
534
|
const { resourceId } = body;
|
|
463
535
|
if (!this.statuses.dbDiscover) {
|
|
464
536
|
return { error: 'Database discovery not started' };
|
|
@@ -487,8 +559,8 @@ class AdminForth {
|
|
|
487
559
|
server.endpoint({
|
|
488
560
|
method: 'POST',
|
|
489
561
|
path: '/get_record',
|
|
490
|
-
handler: (
|
|
491
|
-
var
|
|
562
|
+
handler: (_p) => __awaiter(this, [_p], void 0, function* ({ body, adminUser }) {
|
|
563
|
+
var _q, _r;
|
|
492
564
|
const { resourceId, primaryKey } = body;
|
|
493
565
|
const resource = this.config.resources.find((res) => res.resourceId == resourceId);
|
|
494
566
|
const primaryKeyColumn = resource.columns.find((col) => col.primaryKey);
|
|
@@ -498,8 +570,8 @@ class AdminForth {
|
|
|
498
570
|
return { error: `Record with ${primaryKeyColumn.name} ${primaryKey} not found` };
|
|
499
571
|
}
|
|
500
572
|
// execute hook if needed
|
|
501
|
-
if ((
|
|
502
|
-
const resp = yield ((
|
|
573
|
+
if ((_q = resource.hooks) === null || _q === void 0 ? void 0 : _q.show) {
|
|
574
|
+
const resp = yield ((_r = resource.hooks) === null || _r === void 0 ? void 0 : _r.show({ resource, record, adminUser }));
|
|
503
575
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
504
576
|
throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
|
|
505
577
|
}
|
|
@@ -516,8 +588,8 @@ class AdminForth {
|
|
|
516
588
|
noAuth: true, // TODO
|
|
517
589
|
method: 'POST',
|
|
518
590
|
path: '/create_record',
|
|
519
|
-
handler: (
|
|
520
|
-
var
|
|
591
|
+
handler: (_s) => __awaiter(this, [_s], void 0, function* ({ body, adminUser }) {
|
|
592
|
+
var _t, _u, _v, _w, _x, _y, _z, _0, _1;
|
|
521
593
|
console.log('create_record', body, this.config.resources);
|
|
522
594
|
const resource = this.config.resources.find((res) => res.resourceId == body['resourceId']);
|
|
523
595
|
if (!resource) {
|
|
@@ -525,8 +597,8 @@ class AdminForth {
|
|
|
525
597
|
}
|
|
526
598
|
const record = body['record'];
|
|
527
599
|
// execute hook if needed
|
|
528
|
-
if ((
|
|
529
|
-
const resp = yield ((
|
|
600
|
+
if ((_u = (_t = resource.hooks) === null || _t === void 0 ? void 0 : _t.create) === null || _u === void 0 ? void 0 : _u.beforeSave) {
|
|
601
|
+
const resp = yield ((_w = (_v = resource.hooks) === null || _v === void 0 ? void 0 : _v.create) === null || _w === void 0 ? void 0 : _w.beforeSave({ resource, record, adminUser }));
|
|
530
602
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
531
603
|
throw new Error(`Hook beforeSave must return object with {ok: true} or { error: 'Error' } `);
|
|
532
604
|
}
|
|
@@ -542,7 +614,7 @@ class AdminForth {
|
|
|
542
614
|
});
|
|
543
615
|
}
|
|
544
616
|
}
|
|
545
|
-
if (((
|
|
617
|
+
if (((_x = column.required) === null || _x === void 0 ? void 0 : _x.create) && body['record'][column.name] === undefined) {
|
|
546
618
|
return { error: `Column '${column.name}' is required` };
|
|
547
619
|
}
|
|
548
620
|
if (column.isUnique) {
|
|
@@ -567,8 +639,8 @@ class AdminForth {
|
|
|
567
639
|
const connector = this.connectors[resource.dataSource];
|
|
568
640
|
yield connector.createRecord({ resource, record });
|
|
569
641
|
// execute hook if needed
|
|
570
|
-
if ((
|
|
571
|
-
const resp = yield ((
|
|
642
|
+
if ((_z = (_y = resource.hooks) === null || _y === void 0 ? void 0 : _y.create) === null || _z === void 0 ? void 0 : _z.afterSave) {
|
|
643
|
+
const resp = yield ((_1 = (_0 = resource.hooks) === null || _0 === void 0 ? void 0 : _0.create) === null || _1 === void 0 ? void 0 : _1.afterSave({ resource, record, adminUser }));
|
|
572
644
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
573
645
|
throw new Error(`Hook afterSave must return object with {ok: true} or { error: 'Error' } `);
|
|
574
646
|
}
|
|
@@ -585,8 +657,8 @@ class AdminForth {
|
|
|
585
657
|
noAuth: true, // TODO
|
|
586
658
|
method: 'POST',
|
|
587
659
|
path: '/update_record',
|
|
588
|
-
handler: (
|
|
589
|
-
var
|
|
660
|
+
handler: (_2) => __awaiter(this, [_2], void 0, function* ({ body, adminUser }) {
|
|
661
|
+
var _3, _4, _5, _6, _7, _8, _9, _10;
|
|
590
662
|
console.log('update_record', body);
|
|
591
663
|
const resource = this.config.resources.find((res) => res.resourceId == body['resourceId']);
|
|
592
664
|
if (!resource) {
|
|
@@ -601,8 +673,8 @@ class AdminForth {
|
|
|
601
673
|
}
|
|
602
674
|
const record = body['record'];
|
|
603
675
|
// execute hook if needed
|
|
604
|
-
if ((
|
|
605
|
-
const resp = yield ((
|
|
676
|
+
if ((_4 = (_3 = resource.hooks) === null || _3 === void 0 ? void 0 : _3.edit) === null || _4 === void 0 ? void 0 : _4.beforeSave) {
|
|
677
|
+
const resp = yield ((_6 = (_5 = resource.hooks) === null || _5 === void 0 ? void 0 : _5.edit) === null || _6 === void 0 ? void 0 : _6.beforeSave({ resource, record, adminUser }));
|
|
606
678
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
607
679
|
throw new Error(`Hook beforeSave must return object with {ok: true} or { error: 'Error' } `);
|
|
608
680
|
}
|
|
@@ -620,8 +692,8 @@ class AdminForth {
|
|
|
620
692
|
yield connector.updateRecord({ resource, recordId, record, newValues });
|
|
621
693
|
}
|
|
622
694
|
// execute hook if needed
|
|
623
|
-
if ((
|
|
624
|
-
const resp = yield ((
|
|
695
|
+
if ((_8 = (_7 = resource.hooks) === null || _7 === void 0 ? void 0 : _7.edit) === null || _8 === void 0 ? void 0 : _8.afterSave) {
|
|
696
|
+
const resp = yield ((_10 = (_9 = resource.hooks) === null || _9 === void 0 ? void 0 : _9.edit) === null || _10 === void 0 ? void 0 : _10.afterSave({ resource, record, adminUser }));
|
|
625
697
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
626
698
|
throw new Error(`Hook afterSave must return object with {ok: true} or { error: 'Error' } `);
|
|
627
699
|
}
|
|
@@ -638,16 +710,16 @@ class AdminForth {
|
|
|
638
710
|
noAuth: true, // TODO
|
|
639
711
|
method: 'POST',
|
|
640
712
|
path: '/delete_record',
|
|
641
|
-
handler: (
|
|
642
|
-
var
|
|
713
|
+
handler: (_11) => __awaiter(this, [_11], void 0, function* ({ body, adminUser }) {
|
|
714
|
+
var _12, _13, _14, _15, _16, _17, _18, _19;
|
|
643
715
|
const resource = this.config.resources.find((res) => res.resourceId == body['resourceId']);
|
|
644
716
|
const record = yield this.connectors[resource.dataSource].getRecordByPrimaryKey(resource, body['primaryKey']);
|
|
645
717
|
if (!resource) {
|
|
646
718
|
return { error: `Resource '${body['resourceId']}' not found` };
|
|
647
719
|
}
|
|
648
720
|
// execute hook if needed
|
|
649
|
-
if ((
|
|
650
|
-
const resp = yield ((
|
|
721
|
+
if ((_13 = (_12 = resource.hooks) === null || _12 === void 0 ? void 0 : _12.delete) === null || _13 === void 0 ? void 0 : _13.beforeSave) {
|
|
722
|
+
const resp = yield ((_15 = (_14 = resource.hooks) === null || _14 === void 0 ? void 0 : _14.delete) === null || _15 === void 0 ? void 0 : _15.beforeSave({ resource, record, adminUser }));
|
|
651
723
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
652
724
|
throw new Error(`Hook beforeSave must return object with {ok: true} or { error: 'Error' } `);
|
|
653
725
|
}
|
|
@@ -658,8 +730,8 @@ class AdminForth {
|
|
|
658
730
|
const connector = this.connectors[resource.dataSource];
|
|
659
731
|
yield connector.deleteRecord({ resource, recordId: body['primaryKey'] });
|
|
660
732
|
// execute hook if needed
|
|
661
|
-
if ((
|
|
662
|
-
const resp = yield ((
|
|
733
|
+
if ((_17 = (_16 = resource.hooks) === null || _16 === void 0 ? void 0 : _16.delete) === null || _17 === void 0 ? void 0 : _17.afterSave) {
|
|
734
|
+
const resp = yield ((_19 = (_18 = resource.hooks) === null || _18 === void 0 ? void 0 : _18.delete) === null || _19 === void 0 ? void 0 : _19.afterSave({ resource, record, adminUser }));
|
|
663
735
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
664
736
|
throw new Error(`Hook afterSave must return object with {ok: true} or { error: 'Error' } `);
|
|
665
737
|
}
|
|
@@ -676,7 +748,7 @@ class AdminForth {
|
|
|
676
748
|
noAuth: true, // TODO
|
|
677
749
|
method: 'POST',
|
|
678
750
|
path: '/start_bulk_action',
|
|
679
|
-
handler: (
|
|
751
|
+
handler: (_20) => __awaiter(this, [_20], void 0, function* ({ body }) {
|
|
680
752
|
const { resourceId, actionId, recordIds } = body;
|
|
681
753
|
const resource = this.config.resources.find((res) => res.resourceId == resourceId);
|
|
682
754
|
if (!resource) {
|
|
@@ -63,9 +63,8 @@ class CodeInjector {
|
|
|
63
63
|
rmTmpDir() {
|
|
64
64
|
return __awaiter(this, void 0, void 0, function* () {
|
|
65
65
|
// remove spa_tmp folder if it is exists
|
|
66
|
-
const spaTmpPath = CodeInjector.SPA_TMP_PATH;
|
|
67
66
|
try {
|
|
68
|
-
yield fs.promises.rm(
|
|
67
|
+
yield fs.promises.rm(CodeInjector.SPA_TMP_PATH, { recursive: true });
|
|
69
68
|
}
|
|
70
69
|
catch (e) {
|
|
71
70
|
// ignore
|
|
@@ -74,7 +73,7 @@ class CodeInjector {
|
|
|
74
73
|
}
|
|
75
74
|
prepareSources(_a) {
|
|
76
75
|
return __awaiter(this, arguments, void 0, function* ({ filesUpdated, verbose = false }) {
|
|
77
|
-
var _b, _c, _d;
|
|
76
|
+
var _b, _c, _d, _e, _f, _g;
|
|
78
77
|
const spaTmpPath = CodeInjector.SPA_TMP_PATH;
|
|
79
78
|
// check SPA_TMP_PATH exists and create if not
|
|
80
79
|
try {
|
|
@@ -186,23 +185,35 @@ class CodeInjector {
|
|
|
186
185
|
}
|
|
187
186
|
yield fs.promises.writeFile(appVuePath, appVueContent);
|
|
188
187
|
/* generate custom rotes */
|
|
188
|
+
const homepageMenuItem = this.adminforth.config.menu.find((mi) => mi.homepage);
|
|
189
|
+
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)); });
|
|
190
|
+
let childrenHomepage = (_e = childrenHomePageMenuItem === null || childrenHomePageMenuItem === void 0 ? void 0 : childrenHomePageMenuItem.children) === null || _e === void 0 ? void 0 : _e.find((mi) => mi.homepage);
|
|
191
|
+
let homePagePath = (homepageMenuItem === null || homepageMenuItem === void 0 ? void 0 : homepageMenuItem.path) || `resource/${childrenHomepage === null || childrenHomepage === void 0 ? void 0 : childrenHomepage.resourceId}`;
|
|
192
|
+
if (!homePagePath) {
|
|
193
|
+
homePagePath = ((_f = this.adminforth.config.menu.filter((mi) => mi.path)[0]) === null || _f === void 0 ? void 0 : _f.path) || `resource/${(_g = this.adminforth.config.menu.filter((mi) => mi.children)[0]) === null || _g === void 0 ? void 0 : _g.resourceId}`;
|
|
194
|
+
}
|
|
195
|
+
routes += `{
|
|
196
|
+
path: '/',
|
|
197
|
+
name: 'home',
|
|
198
|
+
//redirect to login
|
|
199
|
+
redirect: '/${homePagePath}'
|
|
200
|
+
},\n`;
|
|
189
201
|
const routerVuePath = path.join(spaTmpPath, 'src', 'router', 'index.ts');
|
|
190
202
|
let routerVueContent = yield fs.promises.readFile(routerVuePath, 'utf-8');
|
|
191
203
|
routerVueContent = routerVueContent.replace('/* IMPORTANT:ADMINFORTH ROUTES */', routes);
|
|
192
204
|
yield fs.promises.writeFile(routerVuePath, routerVueContent);
|
|
193
205
|
/* hash checking */
|
|
194
|
-
const
|
|
195
|
-
const
|
|
196
|
-
const
|
|
206
|
+
const spaPackageLockPath = path.join(spaTmpPath, 'package-lock.json');
|
|
207
|
+
const spaPackageLock = JSON.parse(yield fs.promises.readFile(spaPackageLockPath, 'utf-8'));
|
|
208
|
+
const spaLockHash = hashify(spaPackageLock);
|
|
197
209
|
/* customPackageLock */
|
|
198
|
-
const
|
|
199
|
-
const
|
|
200
|
-
const
|
|
201
|
-
const
|
|
202
|
-
const
|
|
203
|
-
const customLockHash = hashify(customLock);
|
|
210
|
+
const usersPackagePath = path.join('./package.json');
|
|
211
|
+
const usersPackage = JSON.parse(yield fs.promises.readFile(usersPackagePath, 'utf-8'));
|
|
212
|
+
const usersLockPath = path.join('./package-lock.json');
|
|
213
|
+
const usersLock = JSON.parse(yield fs.promises.readFile(usersLockPath, 'utf-8'));
|
|
214
|
+
const usersLockHash = hashify(usersLock);
|
|
204
215
|
const packagesNamesHash = hashify(packageNames);
|
|
205
|
-
const fullHash =
|
|
216
|
+
const fullHash = `${spaLockHash}::${packagesNamesHash}::${usersLockHash}`;
|
|
206
217
|
const hashPath = path.join(spaTmpPath, 'node_modules', '.adminforth_hash');
|
|
207
218
|
try {
|
|
208
219
|
const existingHash = yield fs.promises.readFile(hashPath, 'utf-8');
|
|
@@ -210,14 +221,25 @@ class CodeInjector {
|
|
|
210
221
|
console.log('Hashes match, skipping npm ci/install');
|
|
211
222
|
return;
|
|
212
223
|
}
|
|
224
|
+
else {
|
|
225
|
+
if (verbose) {
|
|
226
|
+
console.log(`Hashes do not match: existing ${existingHash} new ${fullHash}, proceeding with npm ci/install`);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
213
229
|
}
|
|
214
230
|
catch (e) {
|
|
215
231
|
// ignore
|
|
232
|
+
if (verbose) {
|
|
233
|
+
console.log('Hash file does not exist, proceeding with npm ci/install');
|
|
234
|
+
}
|
|
216
235
|
}
|
|
217
236
|
yield this.runNpmShell({ command: 'ci', verbose, cwd: spaTmpPath });
|
|
218
237
|
// get packages with version from customPackage
|
|
219
|
-
const
|
|
220
|
-
|
|
238
|
+
const IGNORE_PACKAGES = ['tsx', 'typescript', 'express', 'nodemon'];
|
|
239
|
+
const customPackgeNames = [...Object.keys(usersPackage.dependencies), ...Object.keys(usersPackage.devDependencies || [])]
|
|
240
|
+
.filter((packageName) => !IGNORE_PACKAGES.includes(packageName))
|
|
241
|
+
.reduce((acc, packageName) => {
|
|
242
|
+
const version = usersLock.packages[`node_modules/${packageName}`].version;
|
|
221
243
|
acc.push(`${packageName}@${version}`);
|
|
222
244
|
return acc;
|
|
223
245
|
}, []);
|
|
@@ -9,12 +9,15 @@
|
|
|
9
9
|
"version": "0.0.0",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@iconify-prerendered/vue-flowbite": "^0.23.1714023977",
|
|
12
|
+
"@vueuse/core": "^10.10.0",
|
|
12
13
|
"dayjs": "^1.11.11",
|
|
14
|
+
"debounce": "^2.1.0",
|
|
13
15
|
"flowbite": "^2.3.0",
|
|
14
16
|
"flowbite-datepicker": "^1.2.6",
|
|
15
17
|
"pinia": "^2.1.7",
|
|
16
18
|
"vue": "^3.4.21",
|
|
17
|
-
"vue-router": "^4.3.0"
|
|
19
|
+
"vue-router": "^4.3.0",
|
|
20
|
+
"vue-slider-component": "^4.1.0-beta.7"
|
|
18
21
|
},
|
|
19
22
|
"devDependencies": {
|
|
20
23
|
"@rushstack/eslint-patch": "^1.8.0",
|
|
@@ -950,6 +953,11 @@
|
|
|
950
953
|
"undici-types": "~5.26.4"
|
|
951
954
|
}
|
|
952
955
|
},
|
|
956
|
+
"node_modules/@types/web-bluetooth": {
|
|
957
|
+
"version": "0.0.20",
|
|
958
|
+
"resolved": "https://registry.npmjs.org/@types/web-bluetooth/-/web-bluetooth-0.0.20.tgz",
|
|
959
|
+
"integrity": "sha512-g9gZnnXVq7gM7v3tJCWV/qw7w+KeOlSHAhgF9RytFyifW6AF61hdT2ucrYhPq9hLs5JIryeupHV3qGk95dH9ow=="
|
|
960
|
+
},
|
|
953
961
|
"node_modules/@typescript-eslint/eslint-plugin": {
|
|
954
962
|
"version": "7.9.0",
|
|
955
963
|
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.9.0.tgz",
|
|
@@ -1330,6 +1338,89 @@
|
|
|
1330
1338
|
"integrity": "sha512-VcZK7MvpjuTPx2w6blwnwZAu5/LgBUtejFOi3pPGQFXQN5Ela03FUtd2Qtg4yWGGissVL0dr6Ro1LfOFh+PCuQ==",
|
|
1331
1339
|
"dev": true
|
|
1332
1340
|
},
|
|
1341
|
+
"node_modules/@vueuse/core": {
|
|
1342
|
+
"version": "10.10.0",
|
|
1343
|
+
"resolved": "https://registry.npmjs.org/@vueuse/core/-/core-10.10.0.tgz",
|
|
1344
|
+
"integrity": "sha512-vexJ/YXYs2S42B783rI95lMt3GzEwkxzC8Hb0Ndpd8rD+p+Lk/Za4bd797Ym7yq4jXqdSyj3JLChunF/vyYjUw==",
|
|
1345
|
+
"dependencies": {
|
|
1346
|
+
"@types/web-bluetooth": "^0.0.20",
|
|
1347
|
+
"@vueuse/metadata": "10.10.0",
|
|
1348
|
+
"@vueuse/shared": "10.10.0",
|
|
1349
|
+
"vue-demi": ">=0.14.7"
|
|
1350
|
+
},
|
|
1351
|
+
"funding": {
|
|
1352
|
+
"url": "https://github.com/sponsors/antfu"
|
|
1353
|
+
}
|
|
1354
|
+
},
|
|
1355
|
+
"node_modules/@vueuse/core/node_modules/vue-demi": {
|
|
1356
|
+
"version": "0.14.8",
|
|
1357
|
+
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.8.tgz",
|
|
1358
|
+
"integrity": "sha512-Uuqnk9YE9SsWeReYqK2alDI5YzciATE0r2SkA6iMAtuXvNTMNACJLJEXNXaEy94ECuBe4Sk6RzRU80kjdbIo1Q==",
|
|
1359
|
+
"hasInstallScript": true,
|
|
1360
|
+
"bin": {
|
|
1361
|
+
"vue-demi-fix": "bin/vue-demi-fix.js",
|
|
1362
|
+
"vue-demi-switch": "bin/vue-demi-switch.js"
|
|
1363
|
+
},
|
|
1364
|
+
"engines": {
|
|
1365
|
+
"node": ">=12"
|
|
1366
|
+
},
|
|
1367
|
+
"funding": {
|
|
1368
|
+
"url": "https://github.com/sponsors/antfu"
|
|
1369
|
+
},
|
|
1370
|
+
"peerDependencies": {
|
|
1371
|
+
"@vue/composition-api": "^1.0.0-rc.1",
|
|
1372
|
+
"vue": "^3.0.0-0 || ^2.6.0"
|
|
1373
|
+
},
|
|
1374
|
+
"peerDependenciesMeta": {
|
|
1375
|
+
"@vue/composition-api": {
|
|
1376
|
+
"optional": true
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
},
|
|
1380
|
+
"node_modules/@vueuse/metadata": {
|
|
1381
|
+
"version": "10.10.0",
|
|
1382
|
+
"resolved": "https://registry.npmjs.org/@vueuse/metadata/-/metadata-10.10.0.tgz",
|
|
1383
|
+
"integrity": "sha512-UNAo2sTCAW5ge6OErPEHb5z7NEAg3XcO9Cj7OK45aZXfLLH1QkexDcZD77HBi5zvEiLOm1An+p/4b5K3Worpug==",
|
|
1384
|
+
"funding": {
|
|
1385
|
+
"url": "https://github.com/sponsors/antfu"
|
|
1386
|
+
}
|
|
1387
|
+
},
|
|
1388
|
+
"node_modules/@vueuse/shared": {
|
|
1389
|
+
"version": "10.10.0",
|
|
1390
|
+
"resolved": "https://registry.npmjs.org/@vueuse/shared/-/shared-10.10.0.tgz",
|
|
1391
|
+
"integrity": "sha512-2aW33Ac0Uk0U+9yo3Ypg9s5KcR42cuehRWl7vnUHadQyFvCktseyxxEPBi1Eiq4D2yBGACOnqLZpx1eMc7g5Og==",
|
|
1392
|
+
"dependencies": {
|
|
1393
|
+
"vue-demi": ">=0.14.7"
|
|
1394
|
+
},
|
|
1395
|
+
"funding": {
|
|
1396
|
+
"url": "https://github.com/sponsors/antfu"
|
|
1397
|
+
}
|
|
1398
|
+
},
|
|
1399
|
+
"node_modules/@vueuse/shared/node_modules/vue-demi": {
|
|
1400
|
+
"version": "0.14.8",
|
|
1401
|
+
"resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.8.tgz",
|
|
1402
|
+
"integrity": "sha512-Uuqnk9YE9SsWeReYqK2alDI5YzciATE0r2SkA6iMAtuXvNTMNACJLJEXNXaEy94ECuBe4Sk6RzRU80kjdbIo1Q==",
|
|
1403
|
+
"hasInstallScript": true,
|
|
1404
|
+
"bin": {
|
|
1405
|
+
"vue-demi-fix": "bin/vue-demi-fix.js",
|
|
1406
|
+
"vue-demi-switch": "bin/vue-demi-switch.js"
|
|
1407
|
+
},
|
|
1408
|
+
"engines": {
|
|
1409
|
+
"node": ">=12"
|
|
1410
|
+
},
|
|
1411
|
+
"funding": {
|
|
1412
|
+
"url": "https://github.com/sponsors/antfu"
|
|
1413
|
+
},
|
|
1414
|
+
"peerDependencies": {
|
|
1415
|
+
"@vue/composition-api": "^1.0.0-rc.1",
|
|
1416
|
+
"vue": "^3.0.0-0 || ^2.6.0"
|
|
1417
|
+
},
|
|
1418
|
+
"peerDependenciesMeta": {
|
|
1419
|
+
"@vue/composition-api": {
|
|
1420
|
+
"optional": true
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
},
|
|
1333
1424
|
"node_modules/acorn": {
|
|
1334
1425
|
"version": "8.11.3",
|
|
1335
1426
|
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
|
|
@@ -1716,6 +1807,17 @@
|
|
|
1716
1807
|
"integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==",
|
|
1717
1808
|
"dev": true
|
|
1718
1809
|
},
|
|
1810
|
+
"node_modules/debounce": {
|
|
1811
|
+
"version": "2.1.0",
|
|
1812
|
+
"resolved": "https://registry.npmjs.org/debounce/-/debounce-2.1.0.tgz",
|
|
1813
|
+
"integrity": "sha512-OkL3+0pPWCqoBc/nhO9u6TIQNTK44fnBnzuVtJAbp13Naxw9R6u21x+8tVTka87AhDZ3htqZ2pSSsZl9fqL2Wg==",
|
|
1814
|
+
"engines": {
|
|
1815
|
+
"node": ">=18"
|
|
1816
|
+
},
|
|
1817
|
+
"funding": {
|
|
1818
|
+
"url": "https://github.com/sponsors/sindresorhus"
|
|
1819
|
+
}
|
|
1820
|
+
},
|
|
1719
1821
|
"node_modules/debug": {
|
|
1720
1822
|
"version": "4.3.4",
|
|
1721
1823
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
|
@@ -3970,6 +4072,11 @@
|
|
|
3970
4072
|
"vue": "^3.2.0"
|
|
3971
4073
|
}
|
|
3972
4074
|
},
|
|
4075
|
+
"node_modules/vue-slider-component": {
|
|
4076
|
+
"version": "4.1.0-beta.7",
|
|
4077
|
+
"resolved": "https://registry.npmjs.org/vue-slider-component/-/vue-slider-component-4.1.0-beta.7.tgz",
|
|
4078
|
+
"integrity": "sha512-Qb7K920ZG7PoQswoF6Ias+i3W2rd3k4fpk04JUl82kEUcN86Yg6et7bVSKWt/7VpQe8a5IT3BqCKSCOZ7AJgCA=="
|
|
4079
|
+
},
|
|
3973
4080
|
"node_modules/vue-template-compiler": {
|
|
3974
4081
|
"version": "2.7.16",
|
|
3975
4082
|
"resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.16.tgz",
|
|
@@ -13,12 +13,15 @@
|
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
15
|
"@iconify-prerendered/vue-flowbite": "^0.23.1714023977",
|
|
16
|
+
"@vueuse/core": "^10.10.0",
|
|
16
17
|
"dayjs": "^1.11.11",
|
|
18
|
+
"debounce": "^2.1.0",
|
|
17
19
|
"flowbite": "^2.3.0",
|
|
18
20
|
"flowbite-datepicker": "^1.2.6",
|
|
19
21
|
"pinia": "^2.1.7",
|
|
20
22
|
"vue": "^3.4.21",
|
|
21
|
-
"vue-router": "^4.3.0"
|
|
23
|
+
"vue-router": "^4.3.0",
|
|
24
|
+
"vue-slider-component": "^4.1.0-beta.7"
|
|
22
25
|
},
|
|
23
26
|
"devDependencies": {
|
|
24
27
|
"@rushstack/eslint-patch": "^1.8.0",
|
package/dist/spa/spa/src/App.vue
CHANGED
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
|
|
33
33
|
</button>
|
|
34
34
|
</div>
|
|
35
|
-
<div class="z-50 hidden my-4 text-base list-none bg-white divide-y divide-gray-100 rounded shadow dark:bg-gray-700 dark:divide-gray-600 dark:shadow-
|
|
35
|
+
<div class="z-50 hidden my-4 text-base list-none bg-white divide-y divide-gray-100 rounded shadow dark:shadow-black dark:bg-gray-700 dark:divide-gray-600 dark:shadow-black" id="dropdown-user">
|
|
36
36
|
<div class="px-4 py-3" role="none">
|
|
37
37
|
<p class="text-sm text-gray-900 dark:text-white" role="none" v-if="coreStore.userFullname">
|
|
38
38
|
{{ coreStore.userFullname }}
|
|
@@ -112,7 +112,7 @@
|
|
|
112
112
|
</div>
|
|
113
113
|
</div>
|
|
114
114
|
|
|
115
|
-
<div v-else>
|
|
115
|
+
<div v-else-if="routerIsReady">
|
|
116
116
|
<RouterView/>
|
|
117
117
|
</div>
|
|
118
118
|
<AcceptModal />
|
|
@@ -16,7 +16,7 @@ const modalStore = useModalStore();
|
|
|
16
16
|
<Teleport to="body">
|
|
17
17
|
<div v-if="modalStore.isOpened" class="bg-gray-900/50 dark:bg-gray-900/80 overflow-y-auto overflow-x-hidden fixed top-0 right-0 left-0 z-50 justify-center items-center w-full md:inset-0 h-[calc(100%-1rem)] max-h-full">
|
|
18
18
|
<div class="relative p-4 w-full max-w-md max-h-full top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 " >
|
|
19
|
-
<div class="relative bg-white rounded-lg shadow dark:bg-gray-700">
|
|
19
|
+
<div class="relative bg-white rounded-lg shadow dark:bg-gray-700 dark:shadow-black">
|
|
20
20
|
<button type="button"@click="modalStore.togleModal" class="absolute top-3 end-2.5 text-gray-400 bg-transparent hover:bg-gray-200 hover:text-gray-900 rounded-lg text-sm w-8 h-8 ms-auto inline-flex justify-center items-center dark:hover:bg-gray-600 dark:hover:text-white" >
|
|
21
21
|
<svg class="w-3 h-3" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 14 14">
|
|
22
22
|
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 1 6 6m0 0 6 6M7 7l6-6M7 7l-6 6"/>
|