adminforth 1.3.27 → 1.3.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dataConnectors/baseConnector.ts +19 -7
- package/dataConnectors/clickhouse.ts +1 -2
- package/dataConnectors/mongo.ts +1 -1
- package/dataConnectors/postgres.ts +1 -1
- package/dataConnectors/sqlite.ts +3 -3
- package/dist/dataConnectors/baseConnector.js +16 -4
- package/dist/dataConnectors/clickhouse.js +1 -2
- package/dist/dataConnectors/mongo.js +1 -1
- package/dist/dataConnectors/postgres.js +1 -1
- package/dist/dataConnectors/sqlite.js +3 -3
- package/dist/index.js +113 -21
- package/dist/modules/codeInjector.js +8 -8
- package/dist/modules/restApi.js +23 -88
- package/index.ts +128 -24
- package/modules/codeInjector.ts +8 -8
- package/modules/operationalResource.ts +1 -1
- package/modules/restApi.ts +23 -92
- package/package.json +1 -1
- package/spa/src/components/ResourceForm.vue +14 -16
- package/spa/src/utils.ts +34 -1
- package/spa/src/views/CreateView.vue +2 -1
- package/spa/src/views/EditView.vue +3 -2
- package/types/AdminForthConfig.ts +37 -10
package/dist/modules/restApi.js
CHANGED
|
@@ -7,7 +7,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
|
-
import { AdminForthFilterOperators, AdminForthDataTypes, AllowedActionsEnum, ActionCheckSource } from "../types/AdminForthConfig.js";
|
|
10
|
+
import { AdminForthFilterOperators, AdminForthDataTypes, AllowedActionsEnum, ActionCheckSource, AdminForthResourcePages } from "../types/AdminForthConfig.js";
|
|
11
11
|
import { ADMINFORTH_VERSION, listify } from './utils.js';
|
|
12
12
|
import AdminForthAuth from "../auth.js";
|
|
13
13
|
export function interpretResource(adminUser, resource, meta, source) {
|
|
@@ -19,7 +19,7 @@ export function interpretResource(adminUser, resource, meta, source) {
|
|
|
19
19
|
const allowedActions = {};
|
|
20
20
|
yield Promise.all(Object.entries(((_a = resource.options) === null || _a === void 0 ? void 0 : _a.allowedActions) || {}).map((_b) => __awaiter(this, [_b], void 0, function* ([key, value]) {
|
|
21
21
|
if (process.env.HEAVY_DEBUG) {
|
|
22
|
-
console.log(
|
|
22
|
+
console.log(`🪲🚥check allowed ${key}, ${value}`);
|
|
23
23
|
}
|
|
24
24
|
// if callable then call
|
|
25
25
|
if (typeof value === 'function') {
|
|
@@ -485,6 +485,7 @@ export default class AdminForthRestAPI {
|
|
|
485
485
|
method: 'POST',
|
|
486
486
|
path: '/create_record',
|
|
487
487
|
handler: (_2) => __awaiter(this, [_2], void 0, function* ({ body, adminUser }) {
|
|
488
|
+
var _3;
|
|
488
489
|
const resource = this.adminforth.config.resources.find((res) => res.resourceId == body['resourceId']);
|
|
489
490
|
if (!resource) {
|
|
490
491
|
return { error: `Resource '${body['resourceId']}' not found` };
|
|
@@ -495,6 +496,13 @@ export default class AdminForthRestAPI {
|
|
|
495
496
|
return { error };
|
|
496
497
|
}
|
|
497
498
|
const { record } = body;
|
|
499
|
+
for (const column of resource.columns) {
|
|
500
|
+
if (((_3 = column.required) === null || _3 === void 0 ? void 0 : _3.create) &&
|
|
501
|
+
record[column.name] === undefined &&
|
|
502
|
+
column.showIn.includes(AdminForthResourcePages.create)) {
|
|
503
|
+
return { error: `Column '${column.name}' is required`, ok: false };
|
|
504
|
+
}
|
|
505
|
+
}
|
|
498
506
|
const response = yield this.adminforth.createResourceRecord({ resource, record, adminUser });
|
|
499
507
|
if (response.error) {
|
|
500
508
|
return { error: response.error, ok: false };
|
|
@@ -509,8 +517,7 @@ export default class AdminForthRestAPI {
|
|
|
509
517
|
server.endpoint({
|
|
510
518
|
method: 'POST',
|
|
511
519
|
path: '/update_record',
|
|
512
|
-
handler: (
|
|
513
|
-
var _4, _5, _6, _7;
|
|
520
|
+
handler: (_4) => __awaiter(this, [_4], void 0, function* ({ body, adminUser }) {
|
|
514
521
|
const resource = this.adminforth.config.resources.find((res) => res.resourceId == body['resourceId']);
|
|
515
522
|
if (!resource) {
|
|
516
523
|
return { error: `Resource '${body['resourceId']}' not found` };
|
|
@@ -524,68 +531,23 @@ export default class AdminForthRestAPI {
|
|
|
524
531
|
}
|
|
525
532
|
const record = body['record'];
|
|
526
533
|
const { allowedActions } = yield interpretResource(adminUser, resource, { requestBody: body, newRecord: record, oldRecord }, ActionCheckSource.EditRequest);
|
|
527
|
-
const { allowed, error } = checkAccess(AllowedActionsEnum.edit, allowedActions);
|
|
534
|
+
const { allowed, error: allowedError } = checkAccess(AllowedActionsEnum.edit, allowedActions);
|
|
528
535
|
if (!allowed) {
|
|
529
|
-
return {
|
|
530
|
-
}
|
|
531
|
-
// execute hook if needed
|
|
532
|
-
for (const hook of listify((_5 = (_4 = resource.hooks) === null || _4 === void 0 ? void 0 : _4.edit) === null || _5 === void 0 ? void 0 : _5.beforeSave)) {
|
|
533
|
-
const resp = yield hook({
|
|
534
|
-
recordId,
|
|
535
|
-
resource,
|
|
536
|
-
record,
|
|
537
|
-
adminUser
|
|
538
|
-
});
|
|
539
|
-
if (!resp || (!resp.ok && !resp.error)) {
|
|
540
|
-
throw new Error(`Hook beforeSave must return object with {ok: true} or { error: 'Error' } `);
|
|
541
|
-
}
|
|
542
|
-
if (resp.error) {
|
|
543
|
-
return { error: resp.error };
|
|
544
|
-
}
|
|
536
|
+
return { allowedError };
|
|
545
537
|
}
|
|
546
|
-
const
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
const column = resource.columns.find((col) => col.name === recordField);
|
|
550
|
-
if (column) {
|
|
551
|
-
if (!column.virtual) {
|
|
552
|
-
newValues[recordField] = connector.setFieldValue(column, record[recordField]);
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
else {
|
|
556
|
-
newValues[recordField] = record[recordField];
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
if (Object.keys(newValues).length > 0) {
|
|
561
|
-
yield connector.updateRecord({ resource, recordId, newValues });
|
|
562
|
-
}
|
|
563
|
-
// execute hook if needed
|
|
564
|
-
for (const hook of listify((_7 = (_6 = resource.hooks) === null || _6 === void 0 ? void 0 : _6.edit) === null || _7 === void 0 ? void 0 : _7.afterSave)) {
|
|
565
|
-
const resp = yield hook({
|
|
566
|
-
resource,
|
|
567
|
-
record,
|
|
568
|
-
adminUser,
|
|
569
|
-
oldRecord,
|
|
570
|
-
recordId,
|
|
571
|
-
});
|
|
572
|
-
if (!resp || (!resp.ok && !resp.error)) {
|
|
573
|
-
throw new Error(`Hook afterSave must return object with {ok: true} or { error: 'Error' } `);
|
|
574
|
-
}
|
|
575
|
-
if (resp.error) {
|
|
576
|
-
return { error: resp.error };
|
|
577
|
-
}
|
|
538
|
+
const { error } = yield this.adminforth.updateResourceRecord({ resource, record, adminUser, oldRecord, recordId });
|
|
539
|
+
if (error) {
|
|
540
|
+
return { error };
|
|
578
541
|
}
|
|
579
542
|
return {
|
|
580
|
-
|
|
543
|
+
ok: true
|
|
581
544
|
};
|
|
582
545
|
})
|
|
583
546
|
});
|
|
584
547
|
server.endpoint({
|
|
585
548
|
method: 'POST',
|
|
586
549
|
path: '/delete_record',
|
|
587
|
-
handler: (
|
|
588
|
-
var _9, _10, _11, _12;
|
|
550
|
+
handler: (_5) => __awaiter(this, [_5], void 0, function* ({ body, adminUser }) {
|
|
589
551
|
const resource = this.adminforth.config.resources.find((res) => res.resourceId == body['resourceId']);
|
|
590
552
|
const record = yield this.adminforth.connectors[resource.dataSource].getRecordByPrimaryKey(resource, body['primaryKey']);
|
|
591
553
|
if (!resource) {
|
|
@@ -602,39 +564,12 @@ export default class AdminForthRestAPI {
|
|
|
602
564
|
if (!allowed) {
|
|
603
565
|
return { error };
|
|
604
566
|
}
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
resource,
|
|
609
|
-
record,
|
|
610
|
-
adminUser,
|
|
611
|
-
recordId: body['primaryKey']
|
|
612
|
-
});
|
|
613
|
-
if (!resp || (!resp.ok && !resp.error)) {
|
|
614
|
-
throw new Error(`Hook beforeSave must return object with {ok: true} or { error: 'Error' } `);
|
|
615
|
-
}
|
|
616
|
-
if (resp.error) {
|
|
617
|
-
return { error: resp.error };
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
const connector = this.adminforth.connectors[resource.dataSource];
|
|
621
|
-
yield connector.deleteRecord({ resource, recordId: body['primaryKey'] });
|
|
622
|
-
// execute hook if needed
|
|
623
|
-
for (const hook of listify((_12 = (_11 = resource.hooks) === null || _11 === void 0 ? void 0 : _11.delete) === null || _12 === void 0 ? void 0 : _12.afterSave)) {
|
|
624
|
-
const resp = yield hook({
|
|
625
|
-
resource,
|
|
626
|
-
record,
|
|
627
|
-
adminUser,
|
|
628
|
-
recordId: body['primaryKey']
|
|
629
|
-
});
|
|
630
|
-
if (!resp || (!resp.ok && !resp.error)) {
|
|
631
|
-
throw new Error(`Hook afterSave must return object with {ok: true} or { error: 'Error' } `);
|
|
632
|
-
}
|
|
633
|
-
if (resp.error) {
|
|
634
|
-
return { error: resp.error };
|
|
635
|
-
}
|
|
567
|
+
const { error: deleteError } = yield this.adminforth.deleteResourceRecord({ resource, record, adminUser, recordId: body['primaryKey'] });
|
|
568
|
+
if (deleteError) {
|
|
569
|
+
return { error: deleteError };
|
|
636
570
|
}
|
|
637
571
|
return {
|
|
572
|
+
ok: true,
|
|
638
573
|
recordId: body['primaryKey']
|
|
639
574
|
};
|
|
640
575
|
})
|
|
@@ -642,7 +577,7 @@ export default class AdminForthRestAPI {
|
|
|
642
577
|
server.endpoint({
|
|
643
578
|
method: 'POST',
|
|
644
579
|
path: '/start_bulk_action',
|
|
645
|
-
handler: (
|
|
580
|
+
handler: (_6) => __awaiter(this, [_6], void 0, function* ({ body, adminUser }) {
|
|
646
581
|
const { resourceId, actionId, recordIds } = body;
|
|
647
582
|
const resource = this.adminforth.config.resources.find((res) => res.resourceId == resourceId);
|
|
648
583
|
if (!resource) {
|
package/index.ts
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
AfterSaveFunction,
|
|
18
18
|
AdminUser,
|
|
19
19
|
AdminForthResource,
|
|
20
|
+
IAdminForthDataSourceConnectorBase,
|
|
20
21
|
} from './types/AdminForthConfig.js';
|
|
21
22
|
import AdminForthPlugin from './basePlugin.js';
|
|
22
23
|
import ConfigValidator from './modules/configValidator.js';
|
|
@@ -38,6 +39,17 @@ class AdminForth implements IAdminForth {
|
|
|
38
39
|
static Utils = {
|
|
39
40
|
generatePasswordHash: async (password) => {
|
|
40
41
|
return await AdminForthAuth.generatePasswordHash(password);
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
PASSWORD_VALIDATORS: {
|
|
45
|
+
UP_LOW_NUM_SPECIAL: {
|
|
46
|
+
regExp: '^(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*[!@#\\$%\\^&\\*\\(\\)\\-_=\\+\\[\\]\\{\\}\\|;:\',\\.<>\\/\\?]).+$',
|
|
47
|
+
message: 'Password must include at least one uppercase letter, one lowercase letter, one number, and one special character'
|
|
48
|
+
},
|
|
49
|
+
UP_LOW_NUM: {
|
|
50
|
+
regExp: '^(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9]).+$',
|
|
51
|
+
message: 'Password must include at least one uppercase letter, one lowercase letter, and one number'
|
|
52
|
+
},
|
|
41
53
|
}
|
|
42
54
|
}
|
|
43
55
|
|
|
@@ -49,7 +61,9 @@ class AdminForth implements IAdminForth {
|
|
|
49
61
|
express: ExpressServer;
|
|
50
62
|
auth: AdminForthAuth;
|
|
51
63
|
codeInjector: CodeInjector;
|
|
52
|
-
connectors
|
|
64
|
+
connectors: {
|
|
65
|
+
[dataSourceId: string]: IAdminForthDataSourceConnectorBase,
|
|
66
|
+
};
|
|
53
67
|
connectorClasses: any;
|
|
54
68
|
runningHotReload: boolean;
|
|
55
69
|
activatedPlugins: Array<AdminForthPlugin>;
|
|
@@ -202,18 +216,7 @@ class AdminForth implements IAdminForth {
|
|
|
202
216
|
async createResourceRecord(
|
|
203
217
|
{ resource, record, adminUser }:
|
|
204
218
|
{ resource: AdminForthResource, record: any, adminUser: AdminUser }
|
|
205
|
-
): Promise<{
|
|
206
|
-
|
|
207
|
-
for (const column of resource.columns) {
|
|
208
|
-
// TODO: assuming specifity for AdminForthResourcePages.create better to move it to api for this button
|
|
209
|
-
if (
|
|
210
|
-
(column.required as {create?: boolean, edit?: boolean}) ?.create &&
|
|
211
|
-
record[column.name] === undefined &&
|
|
212
|
-
column.showIn.includes(AdminForthResourcePages.create)
|
|
213
|
-
) {
|
|
214
|
-
return { error: `Column '${column.name}' is required`, ok: false };
|
|
215
|
-
}
|
|
216
|
-
}
|
|
219
|
+
): Promise<{ error?: string, createdRecord?: any }> {
|
|
217
220
|
|
|
218
221
|
// execute hook if needed
|
|
219
222
|
for (const hook of listify(resource.hooks?.create?.beforeSave as BeforeSaveFunction[])) {
|
|
@@ -223,28 +226,28 @@ class AdminForth implements IAdminForth {
|
|
|
223
226
|
}
|
|
224
227
|
|
|
225
228
|
if (resp.error) {
|
|
226
|
-
return { error: resp.error
|
|
229
|
+
return { error: resp.error };
|
|
227
230
|
}
|
|
228
231
|
}
|
|
229
232
|
|
|
230
233
|
// remove virtual columns from record
|
|
231
234
|
for (const column of resource.columns.filter((col) => col.virtual)) {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
+
if (record[column.name]) {
|
|
236
|
+
delete record[column.name];
|
|
237
|
+
}
|
|
235
238
|
}
|
|
236
239
|
const connector = this.connectors[resource.dataSource];
|
|
237
|
-
process.env.HEAVY_DEBUG && console.log('
|
|
238
|
-
const {
|
|
239
|
-
if (
|
|
240
|
-
return {
|
|
240
|
+
process.env.HEAVY_DEBUG && console.log('🪲🆕 creating record createResourceRecord', record);
|
|
241
|
+
const { error, createdRecord } = await connector.createRecord({ resource, record, adminUser });
|
|
242
|
+
if ( error ) {
|
|
243
|
+
return { error };
|
|
241
244
|
}
|
|
242
245
|
|
|
243
246
|
const primaryKey = record[resource.columns.find((col) => col.primaryKey).name];
|
|
244
247
|
|
|
245
248
|
// execute hook if needed
|
|
246
249
|
for (const hook of listify(resource.hooks?.create?.afterSave as AfterSaveFunction[])) {
|
|
247
|
-
console.log('Hook afterSave', hook);
|
|
250
|
+
process.env.HEAVY_DEBUG && console.log('🪲 Hook afterSave', hook);
|
|
248
251
|
const resp = await hook({
|
|
249
252
|
recordId: primaryKey,
|
|
250
253
|
resource,
|
|
@@ -257,11 +260,112 @@ class AdminForth implements IAdminForth {
|
|
|
257
260
|
}
|
|
258
261
|
|
|
259
262
|
if (resp.error) {
|
|
260
|
-
return { error: resp.error
|
|
263
|
+
return { error: resp.error };
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return { error, createdRecord };
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* record is partial record with only changed fields
|
|
272
|
+
*/
|
|
273
|
+
async updateResourceRecord(
|
|
274
|
+
{ resource, recordId, record, oldRecord, adminUser }:
|
|
275
|
+
{ resource: AdminForthResource, recordId: any, record: any, oldRecord: any, adminUser: AdminUser }
|
|
276
|
+
): Promise<{ error?: string }> {
|
|
277
|
+
|
|
278
|
+
// execute hook if needed
|
|
279
|
+
for (const hook of listify(resource.hooks?.edit?.beforeSave as BeforeSaveFunction[])) {
|
|
280
|
+
const resp = await hook({
|
|
281
|
+
recordId,
|
|
282
|
+
resource,
|
|
283
|
+
record,
|
|
284
|
+
oldRecord,
|
|
285
|
+
adminUser
|
|
286
|
+
});
|
|
287
|
+
if (!resp || (!resp.ok && !resp.error)) {
|
|
288
|
+
throw new Error(`Hook beforeSave must return object with {ok: true} or { error: 'Error' } `);
|
|
289
|
+
}
|
|
290
|
+
if (resp.error) {
|
|
291
|
+
return { error: resp.error };
|
|
261
292
|
}
|
|
262
293
|
}
|
|
294
|
+
const newValues = {};
|
|
295
|
+
const connector = this.connectors[resource.dataSource];
|
|
296
|
+
|
|
297
|
+
for (const recordField in record) {
|
|
298
|
+
if (record[recordField] !== oldRecord[recordField]) {
|
|
299
|
+
// leave only changed fields to reduce data transfer/modifications in db
|
|
300
|
+
const column = resource.columns.find((col) => col.name === recordField);
|
|
301
|
+
if (!column || !column.virtual) {
|
|
302
|
+
// exclude virtual columns
|
|
303
|
+
newValues[recordField] = record[recordField];
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
263
307
|
|
|
264
|
-
|
|
308
|
+
if (Object.keys(newValues).length > 0) {
|
|
309
|
+
await connector.updateRecord({ resource, recordId, newValues });
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// execute hook if needed
|
|
313
|
+
for (const hook of listify(resource.hooks?.edit?.afterSave as AfterSaveFunction[])) {
|
|
314
|
+
const resp = await hook({
|
|
315
|
+
resource,
|
|
316
|
+
record,
|
|
317
|
+
adminUser,
|
|
318
|
+
oldRecord,
|
|
319
|
+
recordId,
|
|
320
|
+
});
|
|
321
|
+
if (!resp || (!resp.ok && !resp.error)) {
|
|
322
|
+
throw new Error(`Hook afterSave must return object with {ok: true} or { error: 'Error' } `);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
return { error: resp.error };
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
async deleteResourceRecord(
|
|
330
|
+
{ resource, recordId, adminUser, record }:
|
|
331
|
+
{ resource: AdminForthResource, recordId: any, adminUser: AdminUser, record: any }
|
|
332
|
+
): Promise<{ error?: string }> {
|
|
333
|
+
// execute hook if needed
|
|
334
|
+
for (const hook of listify(resource.hooks?.delete?.beforeSave as BeforeSaveFunction[])) {
|
|
335
|
+
const resp = await hook({
|
|
336
|
+
resource,
|
|
337
|
+
record,
|
|
338
|
+
adminUser,
|
|
339
|
+
recordId,
|
|
340
|
+
});
|
|
341
|
+
if (!resp || (!resp.ok && !resp.error)) {
|
|
342
|
+
throw new Error(`Hook beforeSave must return object with {ok: true} or { error: 'Error' } `);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
if (resp.error) {
|
|
346
|
+
return { error: resp.error };
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
const connector = this.connectors[resource.dataSource];
|
|
351
|
+
await connector.deleteRecord({ resource, recordId});
|
|
352
|
+
|
|
353
|
+
// execute hook if needed
|
|
354
|
+
for (const hook of listify(resource.hooks?.delete?.afterSave as BeforeSaveFunction[])) {
|
|
355
|
+
const resp = await hook({
|
|
356
|
+
resource,
|
|
357
|
+
record,
|
|
358
|
+
adminUser,
|
|
359
|
+
recordId,
|
|
360
|
+
});
|
|
361
|
+
if (!resp || (!resp.ok && !resp.error)) {
|
|
362
|
+
throw new Error(`Hook afterSave must return object with {ok: true} or { error: 'Error' } `);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
if (resp.error) {
|
|
366
|
+
return { error: resp.error };
|
|
367
|
+
}
|
|
368
|
+
}
|
|
265
369
|
}
|
|
266
370
|
|
|
267
371
|
resource(resourceId: string): IOperationalResource {
|
package/modules/codeInjector.ts
CHANGED
|
@@ -236,13 +236,13 @@ class CodeInjector implements ICodeInjector {
|
|
|
236
236
|
dereference: true, // needed to dereference types
|
|
237
237
|
});
|
|
238
238
|
if (process.env.HEAVY_DEBUG) {
|
|
239
|
-
console.log('
|
|
239
|
+
console.log('🪲⚙️ fsExtra.copy copy single file', src, dest);
|
|
240
240
|
}
|
|
241
241
|
|
|
242
242
|
}));
|
|
243
243
|
} else {
|
|
244
244
|
if (process.env.HEAVY_DEBUG) {
|
|
245
|
-
console.log(
|
|
245
|
+
console.log(`🪲⚙️ fsExtra.copy from ${path.join(ADMIN_FORTH_ABSOLUTE_PATH, 'spa')}, -> ${CodeInjector.SPA_TMP_PATH}`);
|
|
246
246
|
}
|
|
247
247
|
|
|
248
248
|
// try to rm SPA_TMP_PATH/src/types directory
|
|
@@ -257,7 +257,7 @@ class CodeInjector implements ICodeInjector {
|
|
|
257
257
|
filter: (src) => {
|
|
258
258
|
const filterPasses = !src.includes('/adminforth/spa/node_modules') && !src.includes('/adminforth/spa/dist')
|
|
259
259
|
if (process.env.HEAVY_DEBUG && !filterPasses) {
|
|
260
|
-
console.log('
|
|
260
|
+
console.log('🪲⚙️ fsExtra.copy filtered out', src);
|
|
261
261
|
}
|
|
262
262
|
|
|
263
263
|
return filterPasses
|
|
@@ -287,7 +287,7 @@ class CodeInjector implements ICodeInjector {
|
|
|
287
287
|
for (const [src, dest] of Object.entries(this.srcFoldersToSync)) {
|
|
288
288
|
const to = path.join(CodeInjector.SPA_TMP_PATH, 'src', 'custom', dest);
|
|
289
289
|
if (process.env.HEAVY_DEBUG) {
|
|
290
|
-
console.log(
|
|
290
|
+
console.log(`🪲⚙️ fsExtra.copy from ${src}, ${to}`);
|
|
291
291
|
}
|
|
292
292
|
|
|
293
293
|
await fsExtra.copy(src, to, {
|
|
@@ -534,7 +534,7 @@ class CodeInjector implements ICodeInjector {
|
|
|
534
534
|
await collectDirectories(spaPath);
|
|
535
535
|
|
|
536
536
|
if (process.env.HEAVY_DEBUG) {
|
|
537
|
-
console.log('
|
|
537
|
+
console.log('🪲🔎 Watch for:', directories.join(','));
|
|
538
538
|
}
|
|
539
539
|
|
|
540
540
|
const watcher = filewatcher();
|
|
@@ -593,12 +593,12 @@ class CodeInjector implements ICodeInjector {
|
|
|
593
593
|
|
|
594
594
|
const watcher = filewatcher();
|
|
595
595
|
files.forEach((file) => {
|
|
596
|
-
process.env.HEAVY_DEBUG && console.log(
|
|
596
|
+
process.env.HEAVY_DEBUG && console.log(`🪲🔎 Watch for file ${file}`);
|
|
597
597
|
watcher.add(file);
|
|
598
598
|
});
|
|
599
599
|
|
|
600
600
|
if (process.env.HEAVY_DEBUG) {
|
|
601
|
-
console.log('
|
|
601
|
+
console.log('🪲🔎 Watch for:', directories.join(','));
|
|
602
602
|
}
|
|
603
603
|
|
|
604
604
|
watcher.on(
|
|
@@ -657,7 +657,7 @@ class CodeInjector implements ICodeInjector {
|
|
|
657
657
|
await this.runNpmShell({command: 'run build-only', cwd});
|
|
658
658
|
} else {
|
|
659
659
|
const command = 'run dev';
|
|
660
|
-
console.log(
|
|
660
|
+
console.log(`🪲⚙️ spawn: npm ${command}...`);
|
|
661
661
|
const nodeBinary = process.execPath;
|
|
662
662
|
const npmPath = path.join(path.dirname(nodeBinary), 'npm');
|
|
663
663
|
const env = {
|
package/modules/restApi.ts
CHANGED
|
@@ -13,7 +13,8 @@ import {
|
|
|
13
13
|
AfterDataSourceResponseFunction,
|
|
14
14
|
BeforeDataSourceRequestFunction,
|
|
15
15
|
AfterSaveFunction,
|
|
16
|
-
AllowedActionsResolved
|
|
16
|
+
AllowedActionsResolved,
|
|
17
|
+
AdminForthResourcePages
|
|
17
18
|
|
|
18
19
|
} from "../types/AdminForthConfig.js";
|
|
19
20
|
|
|
@@ -32,7 +33,7 @@ export async function interpretResource(adminUser: AdminUser, resource: AdminFor
|
|
|
32
33
|
Object.entries(resource.options?.allowedActions || {}).map(
|
|
33
34
|
async ([key, value]: [string, AllowedActionValue]) => {
|
|
34
35
|
if (process.env.HEAVY_DEBUG) {
|
|
35
|
-
console.log(
|
|
36
|
+
console.log(`🪲🚥check allowed ${key}, ${value}`)
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
// if callable then call
|
|
@@ -575,6 +576,16 @@ export default class AdminForthRestAPI {
|
|
|
575
576
|
|
|
576
577
|
const { record } = body;
|
|
577
578
|
|
|
579
|
+
for (const column of resource.columns) {
|
|
580
|
+
if (
|
|
581
|
+
(column.required as {create?: boolean, edit?: boolean})?.create &&
|
|
582
|
+
record[column.name] === undefined &&
|
|
583
|
+
column.showIn.includes(AdminForthResourcePages.create)
|
|
584
|
+
) {
|
|
585
|
+
return { error: `Column '${column.name}' is required`, ok: false };
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
|
|
578
589
|
const response = await this.adminforth.createResourceRecord({ resource, record, adminUser });
|
|
579
590
|
if (response.error) {
|
|
580
591
|
return { error: response.error, ok: false };
|
|
@@ -607,66 +618,17 @@ export default class AdminForthRestAPI {
|
|
|
607
618
|
|
|
608
619
|
const { allowedActions } = await interpretResource(adminUser, resource, { requestBody: body, newRecord: record, oldRecord}, ActionCheckSource.EditRequest);
|
|
609
620
|
|
|
610
|
-
const { allowed, error } = checkAccess(AllowedActionsEnum.edit, allowedActions);
|
|
621
|
+
const { allowed, error: allowedError } = checkAccess(AllowedActionsEnum.edit, allowedActions);
|
|
611
622
|
if (!allowed) {
|
|
612
|
-
return {
|
|
623
|
+
return { allowedError };
|
|
613
624
|
}
|
|
614
625
|
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
recordId,
|
|
619
|
-
resource,
|
|
620
|
-
record,
|
|
621
|
-
adminUser
|
|
622
|
-
});
|
|
623
|
-
if (!resp || (!resp.ok && !resp.error)) {
|
|
624
|
-
throw new Error(`Hook beforeSave must return object with {ok: true} or { error: 'Error' } `);
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
if (resp.error) {
|
|
628
|
-
return { error: resp.error };
|
|
629
|
-
}
|
|
630
|
-
}
|
|
631
|
-
const newValues = {};
|
|
632
|
-
|
|
633
|
-
for (const recordField in record) {
|
|
634
|
-
if (record[recordField] !== oldRecord[recordField]) {
|
|
635
|
-
const column = resource.columns.find((col) => col.name === recordField);
|
|
636
|
-
if (column) {
|
|
637
|
-
if (!column.virtual) {
|
|
638
|
-
newValues[recordField] = connector.setFieldValue(column, record[recordField]);
|
|
639
|
-
}
|
|
640
|
-
} else {
|
|
641
|
-
newValues[recordField] = record[recordField];
|
|
642
|
-
}
|
|
643
|
-
}
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
if (Object.keys(newValues).length > 0) {
|
|
647
|
-
await connector.updateRecord({ resource, recordId, newValues});
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
// execute hook if needed
|
|
651
|
-
for (const hook of listify(resource.hooks?.edit?.afterSave as AfterSaveFunction[])) {
|
|
652
|
-
const resp = await hook({
|
|
653
|
-
resource,
|
|
654
|
-
record,
|
|
655
|
-
adminUser,
|
|
656
|
-
oldRecord,
|
|
657
|
-
recordId,
|
|
658
|
-
});
|
|
659
|
-
if (!resp || (!resp.ok && !resp.error)) {
|
|
660
|
-
throw new Error(`Hook afterSave must return object with {ok: true} or { error: 'Error' } `);
|
|
661
|
-
}
|
|
662
|
-
|
|
663
|
-
if (resp.error) {
|
|
664
|
-
return { error: resp.error };
|
|
665
|
-
}
|
|
626
|
+
const { error } = await this.adminforth.updateResourceRecord({ resource, record, adminUser, oldRecord, recordId });
|
|
627
|
+
if (error) {
|
|
628
|
+
return { error };
|
|
666
629
|
}
|
|
667
|
-
|
|
668
630
|
return {
|
|
669
|
-
|
|
631
|
+
ok: true
|
|
670
632
|
}
|
|
671
633
|
}
|
|
672
634
|
});
|
|
@@ -693,43 +655,12 @@ export default class AdminForthRestAPI {
|
|
|
693
655
|
return { error };
|
|
694
656
|
}
|
|
695
657
|
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
resource,
|
|
700
|
-
record,
|
|
701
|
-
adminUser,
|
|
702
|
-
recordId: body['primaryKey']
|
|
703
|
-
});
|
|
704
|
-
if (!resp || (!resp.ok && !resp.error)) {
|
|
705
|
-
throw new Error(`Hook beforeSave must return object with {ok: true} or { error: 'Error' } `);
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
if (resp.error) {
|
|
709
|
-
return { error: resp.error };
|
|
710
|
-
}
|
|
711
|
-
}
|
|
712
|
-
|
|
713
|
-
const connector = this.adminforth.connectors[resource.dataSource];
|
|
714
|
-
await connector.deleteRecord({ resource, recordId: body['primaryKey']});
|
|
715
|
-
|
|
716
|
-
// execute hook if needed
|
|
717
|
-
for (const hook of listify(resource.hooks?.delete?.afterSave as BeforeSaveFunction[])) {
|
|
718
|
-
const resp = await hook({
|
|
719
|
-
resource,
|
|
720
|
-
record,
|
|
721
|
-
adminUser,
|
|
722
|
-
recordId: body['primaryKey']
|
|
723
|
-
});
|
|
724
|
-
if (!resp || (!resp.ok && !resp.error)) {
|
|
725
|
-
throw new Error(`Hook afterSave must return object with {ok: true} or { error: 'Error' } `);
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
if (resp.error) {
|
|
729
|
-
return { error: resp.error };
|
|
730
|
-
}
|
|
658
|
+
const { error: deleteError } = await this.adminforth.deleteResourceRecord({ resource, record, adminUser, recordId: body['primaryKey'] });
|
|
659
|
+
if (deleteError) {
|
|
660
|
+
return { error: deleteError };
|
|
731
661
|
}
|
|
732
662
|
return {
|
|
663
|
+
ok: true,
|
|
733
664
|
recordId: body['primaryKey']
|
|
734
665
|
}
|
|
735
666
|
}
|