adminforth 1.2.98 → 1.3.1
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/auth.ts +6 -5
- package/dataConnectors/baseConnector.ts +46 -17
- package/dataConnectors/clickhouse.ts +58 -37
- package/dataConnectors/mongo.ts +45 -14
- package/dataConnectors/postgres.ts +53 -36
- package/dataConnectors/sqlite.ts +37 -35
- package/dist/auth.js +6 -6
- package/dist/dataConnectors/baseConnector.js +32 -10
- package/dist/dataConnectors/clickhouse.js +59 -48
- package/dist/dataConnectors/mongo.js +29 -12
- package/dist/dataConnectors/postgres.js +62 -52
- package/dist/dataConnectors/sqlite.js +52 -45
- package/dist/index.js +36 -15
- package/dist/modules/configValidator.js +10 -14
- package/dist/modules/operationalResource.js +72 -0
- package/dist/modules/restApi.js +75 -90
- package/dist/modules/utils.js +18 -0
- package/dist/types/AdminForthConfig.js +37 -0
- package/index.ts +45 -17
- package/modules/configValidator.ts +11 -16
- package/modules/operationalResource.ts +73 -0
- package/modules/restApi.ts +59 -72
- package/modules/utils.ts +22 -0
- package/package.json +2 -1
- package/types/AdminForthConfig.ts +114 -35
package/dist/index.js
CHANGED
|
@@ -19,16 +19,18 @@ import PostgresConnector from './dataConnectors/postgres.js';
|
|
|
19
19
|
import SQLiteConnector from './dataConnectors/sqlite.js';
|
|
20
20
|
import CodeInjector from './modules/codeInjector.js';
|
|
21
21
|
import ExpressServer from './servers/express.js';
|
|
22
|
-
import { ADMINFORTH_VERSION, listify } from './modules/utils.js';
|
|
22
|
+
import { ADMINFORTH_VERSION, listify, suggestIfTypo } from './modules/utils.js';
|
|
23
23
|
import { AdminForthFilterOperators, AdminForthDataTypes, AdminForthResourcePages, } from './types/AdminForthConfig.js';
|
|
24
24
|
import AdminForthPlugin from './basePlugin.js';
|
|
25
25
|
import ConfigValidator from './modules/configValidator.js';
|
|
26
26
|
import AdminForthRestAPI, { interpretResource } from './modules/restApi.js';
|
|
27
27
|
import ClickhouseConnector from './dataConnectors/clickhouse.js';
|
|
28
|
+
import OperationalResource from './modules/operationalResource.js';
|
|
28
29
|
// exports
|
|
29
30
|
export * from './types/AdminForthConfig.js';
|
|
30
31
|
export { interpretResource };
|
|
31
32
|
export { AdminForthPlugin };
|
|
33
|
+
export { suggestIfTypo };
|
|
32
34
|
class AdminForth {
|
|
33
35
|
constructor(config) {
|
|
34
36
|
_AdminForth_defaultConfig.set(this, {
|
|
@@ -85,20 +87,22 @@ class AdminForth {
|
|
|
85
87
|
this.config.dataSources.forEach((ds) => {
|
|
86
88
|
const dbType = ds.url.split(':')[0];
|
|
87
89
|
if (!this.config.databaseConnectors[dbType]) {
|
|
88
|
-
throw new Error(`Database type ${dbType} is not supported, consider using
|
|
90
|
+
throw new Error(`Database type '${dbType}' is not supported, consider using one of ${Object.keys(this.connectorClasses).join(', ')} or create your own data-source connector`);
|
|
89
91
|
}
|
|
90
92
|
this.connectors[ds.id] = new this.config.databaseConnectors[dbType]({ url: ds.url });
|
|
91
93
|
});
|
|
92
94
|
yield Promise.all(this.config.resources.map((res) => __awaiter(this, void 0, void 0, function* () {
|
|
93
95
|
if (!this.connectors[res.dataSource]) {
|
|
94
|
-
|
|
96
|
+
const similar = suggestIfTypo(Object.keys(this.connectors), res.dataSource);
|
|
97
|
+
throw new Error(`Resource '${res.table}' refers to unknown dataSource '${res.dataSource}' ${similar
|
|
98
|
+
? `. Did you mean '${similar}'?` : 'Available dataSources: ' + Object.keys(this.connectors).join(', ')}`);
|
|
95
99
|
}
|
|
96
100
|
const fieldTypes = yield this.connectors[res.dataSource].discoverFields(res);
|
|
97
101
|
if (fieldTypes !== null && !Object.keys(fieldTypes).length) {
|
|
98
102
|
throw new Error(`Table '${res.table}' (In resource '${res.resourceId}') has no fields or does not exist`);
|
|
99
103
|
}
|
|
100
104
|
if (fieldTypes === null) {
|
|
101
|
-
console.error(
|
|
105
|
+
console.error(`⛔ DataSource ${res.dataSource} was not able to perform field discovery. It will not work properly`);
|
|
102
106
|
return;
|
|
103
107
|
}
|
|
104
108
|
if (!res.columns) {
|
|
@@ -106,7 +110,8 @@ class AdminForth {
|
|
|
106
110
|
}
|
|
107
111
|
res.columns.forEach((col, i) => {
|
|
108
112
|
if (!fieldTypes[col.name] && !col.virtual) {
|
|
109
|
-
|
|
113
|
+
const similar = suggestIfTypo(Object.keys(fieldTypes), col.name);
|
|
114
|
+
throw new Error(`Resource '${res.table}' has no column '${col.name}'. ${similar ? `Did you mean '${similar}'?` : ''}`);
|
|
110
115
|
}
|
|
111
116
|
// first find discovered values, but allow override
|
|
112
117
|
res.columns[i] = Object.assign(Object.assign({}, fieldTypes[col.name]), col);
|
|
@@ -118,6 +123,10 @@ class AdminForth {
|
|
|
118
123
|
}
|
|
119
124
|
})));
|
|
120
125
|
this.statuses.dbDiscover = 'done';
|
|
126
|
+
this.operationalResources = {};
|
|
127
|
+
this.config.resources.forEach((resource) => {
|
|
128
|
+
this.operationalResources[resource.resourceId] = new OperationalResource(this.connectors[resource.dataSource], resource);
|
|
129
|
+
});
|
|
121
130
|
// console.log('⚙️⚙️⚙️ Database discovery done', JSON.stringify(this.config.resources, null, 2));
|
|
122
131
|
});
|
|
123
132
|
}
|
|
@@ -128,9 +137,11 @@ class AdminForth {
|
|
|
128
137
|
}
|
|
129
138
|
getUserByPk(pk) {
|
|
130
139
|
return __awaiter(this, void 0, void 0, function* () {
|
|
131
|
-
const resource = this.config.resources.find((res) => res.resourceId === this.config.auth.
|
|
140
|
+
const resource = this.config.resources.find((res) => res.resourceId === this.config.auth.usersResourceId);
|
|
132
141
|
if (!resource) {
|
|
133
|
-
|
|
142
|
+
const similar = suggestIfTypo(this.config.resources.map((res) => res.resourceId), this.config.auth.usersResourceId);
|
|
143
|
+
throw new Error(`No resource with ${this.config.auth.usersResourceId} found. ${similar ?
|
|
144
|
+
`Did you mean '${similar}' in config.auth.usersResourceId?` : 'Please set correct resource in config.auth.usersResourceId'}`);
|
|
134
145
|
}
|
|
135
146
|
const users = yield this.connectors[resource.dataSource].getData({
|
|
136
147
|
resource,
|
|
@@ -148,13 +159,6 @@ class AdminForth {
|
|
|
148
159
|
return __awaiter(this, arguments, void 0, function* ({ resource, record, adminUser }) {
|
|
149
160
|
var _c, _d, _e, _f, _g;
|
|
150
161
|
for (const column of resource.columns) {
|
|
151
|
-
if (column.fillOnCreate) {
|
|
152
|
-
if (record[column.name] === undefined) {
|
|
153
|
-
record[column.name] = column.fillOnCreate({
|
|
154
|
-
initialRecord: record, adminUser
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
162
|
if (((_c = column.required) === null || _c === void 0 ? void 0 : _c.create) &&
|
|
159
163
|
record[column.name] === undefined &&
|
|
160
164
|
column.showIn.includes(AdminForthResourcePages.create)) {
|
|
@@ -191,7 +195,7 @@ class AdminForth {
|
|
|
191
195
|
}
|
|
192
196
|
const connector = this.connectors[resource.dataSource];
|
|
193
197
|
process.env.HEAVY_DEBUG && console.log('🪲🪲🪲🪲 creating record createResourceRecord', record);
|
|
194
|
-
yield connector.createRecord({ resource, record });
|
|
198
|
+
yield connector.createRecord({ resource, record, adminUser });
|
|
195
199
|
// execute hook if needed
|
|
196
200
|
for (const hook of listify((_g = (_f = resource.hooks) === null || _f === void 0 ? void 0 : _f.create) === null || _g === void 0 ? void 0 : _g.afterSave)) {
|
|
197
201
|
console.log('Hook afterSave', hook);
|
|
@@ -206,6 +210,23 @@ class AdminForth {
|
|
|
206
210
|
return { ok: true };
|
|
207
211
|
});
|
|
208
212
|
}
|
|
213
|
+
resource(resourceId) {
|
|
214
|
+
if (this.statuses.dbDiscover !== 'done') {
|
|
215
|
+
if (this.statuses.dbDiscover === 'running') {
|
|
216
|
+
throw new Error('Database discovery is running. You can\'t use data API while database discovery is not finished.\n' +
|
|
217
|
+
'Consider moving your code to a place where it will be executed after database discovery is already done (after await admin.discoverDatabases())');
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
throw new Error('Database discovery is not yet started. You can\'t use data API before database discovery is done. \n' +
|
|
221
|
+
'Call admin.discoverDatabases() first and await it before using data API');
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
if (!this.operationalResources[resourceId]) {
|
|
225
|
+
const closeName = suggestIfTypo(Object.keys(this.operationalResources), resourceId);
|
|
226
|
+
throw new Error(`Resource with id '${resourceId}' not found${closeName ? `. Did you mean '${closeName}'?` : ''}`);
|
|
227
|
+
}
|
|
228
|
+
return this.operationalResources[resourceId];
|
|
229
|
+
}
|
|
209
230
|
setupEndpoints(server) {
|
|
210
231
|
this.restApi.registerEndpoints(server);
|
|
211
232
|
}
|
|
@@ -10,7 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
import { AdminForthResourcePages, AllowedActionsEnum, } from "../types/AdminForthConfig.js";
|
|
11
11
|
import fs from 'fs';
|
|
12
12
|
import path from 'path';
|
|
13
|
-
import { guessLabelFromName } from './utils.js';
|
|
13
|
+
import { guessLabelFromName, suggestIfTypo } from './utils.js';
|
|
14
14
|
import crypto from 'crypto';
|
|
15
15
|
export default class ConfigValidator {
|
|
16
16
|
constructor(adminforth, config) {
|
|
@@ -47,15 +47,6 @@ export default class ConfigValidator {
|
|
|
47
47
|
validateConfig() {
|
|
48
48
|
var _a;
|
|
49
49
|
const errors = [];
|
|
50
|
-
if (this.config.rootUser) {
|
|
51
|
-
if (!this.config.rootUser.username) {
|
|
52
|
-
throw new Error('rootUser.username is required');
|
|
53
|
-
}
|
|
54
|
-
if (!this.config.rootUser.password) {
|
|
55
|
-
throw new Error('rootUser.password is required');
|
|
56
|
-
}
|
|
57
|
-
console.log('\n ☝️☝️☝️ [INSECURE ALERT] config.rootUser is set, please create a new user to login in backoffice and remove config.rootUser from config ASAP when you are in production\n');
|
|
58
|
-
}
|
|
59
50
|
if (!this.config.customization.customComponentsDir) {
|
|
60
51
|
this.config.customization.customComponentsDir = './custom';
|
|
61
52
|
}
|
|
@@ -67,8 +58,12 @@ export default class ConfigValidator {
|
|
|
67
58
|
this.config.customization.customComponentsDir = undefined;
|
|
68
59
|
}
|
|
69
60
|
if (this.config.auth) {
|
|
70
|
-
|
|
71
|
-
|
|
61
|
+
// TODO: remove in future releases
|
|
62
|
+
if (!this.config.auth.usersResourceId && this.config.auth.resourceId) {
|
|
63
|
+
this.config.auth.usersResourceId = this.config.auth.resourceId;
|
|
64
|
+
}
|
|
65
|
+
if (!this.config.auth.usersResourceId) {
|
|
66
|
+
throw new Error('No config.auth.usersResourceId defined');
|
|
72
67
|
}
|
|
73
68
|
if (!this.config.auth.passwordHashField) {
|
|
74
69
|
throw new Error('No config.auth.passwordHashField defined');
|
|
@@ -79,9 +74,10 @@ export default class ConfigValidator {
|
|
|
79
74
|
if (this.config.auth.loginBackgroundImage) {
|
|
80
75
|
errors.push(...this.checkCustomFileExists(this.config.auth.loginBackgroundImage));
|
|
81
76
|
}
|
|
82
|
-
const userResource = this.config.resources.find((res) => res.resourceId === this.config.auth.
|
|
77
|
+
const userResource = this.config.resources.find((res) => res.resourceId === this.config.auth.usersResourceId);
|
|
83
78
|
if (!userResource) {
|
|
84
|
-
|
|
79
|
+
const similar = suggestIfTypo(this.config.resources.map((res) => res.resourceId || res.table), this.config.auth.usersResourceId);
|
|
80
|
+
throw new Error(`Resource with id "${this.config.auth.usersResourceId}" not found. ${similar ? `Did you mean "${similar}"?` : ''}`);
|
|
85
81
|
}
|
|
86
82
|
if (!this.config.auth.beforeLoginConfirmation) {
|
|
87
83
|
this.config.auth.beforeLoginConfirmation = [];
|
|
@@ -0,0 +1,72 @@
|
|
|
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
|
+
function filtersIfFilter(filter) {
|
|
11
|
+
return (Array.isArray(filter) ? filter : [filter]);
|
|
12
|
+
}
|
|
13
|
+
function sortsIfSort(sort) {
|
|
14
|
+
return (Array.isArray(sort) ? sort : [sort]);
|
|
15
|
+
}
|
|
16
|
+
export default class OperationalResource {
|
|
17
|
+
constructor(dataConnector, resourceConfig) {
|
|
18
|
+
this.dataConnector = dataConnector;
|
|
19
|
+
this.resourceConfig = resourceConfig;
|
|
20
|
+
}
|
|
21
|
+
get(filter) {
|
|
22
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
23
|
+
return (yield this.dataConnector.getData({
|
|
24
|
+
resource: this.resourceConfig,
|
|
25
|
+
filters: filtersIfFilter(filter),
|
|
26
|
+
limit: 1,
|
|
27
|
+
offset: 0,
|
|
28
|
+
sort: [],
|
|
29
|
+
})).data[0] || null;
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
list(filter, limit, offset, sort) {
|
|
33
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
34
|
+
const { data } = yield this.dataConnector.getData({
|
|
35
|
+
resource: this.resourceConfig,
|
|
36
|
+
filters: filtersIfFilter(filter),
|
|
37
|
+
limit,
|
|
38
|
+
offset,
|
|
39
|
+
sort: sortsIfSort(sort),
|
|
40
|
+
getTotals: false,
|
|
41
|
+
});
|
|
42
|
+
return data;
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
count(filter) {
|
|
46
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
47
|
+
return yield this.dataConnector.getCount({
|
|
48
|
+
resource: this.resourceConfig,
|
|
49
|
+
filters: filtersIfFilter(filter),
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
create(record) {
|
|
54
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
55
|
+
return yield this.dataConnector.createRecord({ resource: this.resourceConfig, record, adminUser: null });
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
update(primaryKey, record) {
|
|
59
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
60
|
+
return yield this.dataConnector.updateRecord({
|
|
61
|
+
resource: this.resourceConfig,
|
|
62
|
+
recordId: primaryKey,
|
|
63
|
+
newValues: record
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
delete(primaryKey) {
|
|
68
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
69
|
+
return yield this.dataConnector.deleteRecord({ resource: this.resourceConfig, recordId: primaryKey });
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
}
|
package/dist/modules/restApi.js
CHANGED
|
@@ -47,67 +47,58 @@ export default class AdminForthRestAPI {
|
|
|
47
47
|
const { username, password } = body;
|
|
48
48
|
let adminUser;
|
|
49
49
|
let toReturn = { ok: true, allowedLogin: true };
|
|
50
|
-
|
|
51
|
-
if (this.adminforth.config.
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
}
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
};
|
|
93
|
-
const beforeLoginConfirmation = this.adminforth.config.auth.beforeLoginConfirmation;
|
|
94
|
-
if (beforeLoginConfirmation === null || beforeLoginConfirmation === void 0 ? void 0 : beforeLoginConfirmation.length) {
|
|
95
|
-
for (const hook of beforeLoginConfirmation) {
|
|
96
|
-
const resp = yield hook({ adminUser, response });
|
|
97
|
-
if ((_c = resp === null || resp === void 0 ? void 0 : resp.body) === null || _c === void 0 ? void 0 : _c.redirectTo) {
|
|
98
|
-
toReturn = { ok: resp.ok, redirectTo: (_d = resp === null || resp === void 0 ? void 0 : resp.body) === null || _d === void 0 ? void 0 : _d.redirectTo, allowedLogin: (_e = resp === null || resp === void 0 ? void 0 : resp.body) === null || _e === void 0 ? void 0 : _e.allowedLogin };
|
|
99
|
-
break;
|
|
100
|
-
}
|
|
50
|
+
// get resource from db
|
|
51
|
+
if (!this.adminforth.config.auth) {
|
|
52
|
+
throw new Error('No config.auth defined we need it to find user, please follow the docs');
|
|
53
|
+
}
|
|
54
|
+
const userResource = this.adminforth.config.resources.find((res) => res.resourceId === this.adminforth.config.auth.usersResourceId);
|
|
55
|
+
// if there is no passwordHashField, in columns, add it, with backendOnly and showIn: []
|
|
56
|
+
if (!userResource.dataSourceColumns.find((col) => col.name === this.adminforth.config.auth.passwordHashField)) {
|
|
57
|
+
userResource.dataSourceColumns.push({
|
|
58
|
+
name: this.adminforth.config.auth.passwordHashField,
|
|
59
|
+
backendOnly: true,
|
|
60
|
+
showIn: [],
|
|
61
|
+
type: AdminForthDataTypes.STRING,
|
|
62
|
+
});
|
|
63
|
+
console.log('Adding passwordHashField to userResource', userResource);
|
|
64
|
+
}
|
|
65
|
+
const userRecord = (_b = (yield this.adminforth.connectors[userResource.dataSource].getData({
|
|
66
|
+
resource: userResource,
|
|
67
|
+
filters: [
|
|
68
|
+
{ field: this.adminforth.config.auth.usernameField, operator: AdminForthFilterOperators.EQ, value: username },
|
|
69
|
+
],
|
|
70
|
+
limit: 1,
|
|
71
|
+
offset: 0,
|
|
72
|
+
sort: [],
|
|
73
|
+
})).data) === null || _b === void 0 ? void 0 : _b[0];
|
|
74
|
+
if (!userRecord) {
|
|
75
|
+
return { error: 'User not found' };
|
|
76
|
+
}
|
|
77
|
+
const passwordHash = userRecord[this.adminforth.config.auth.passwordHashField];
|
|
78
|
+
const valid = yield AdminForthAuth.verifyPassword(password, passwordHash);
|
|
79
|
+
if (valid) {
|
|
80
|
+
adminUser = {
|
|
81
|
+
dbUser: userRecord,
|
|
82
|
+
pk: userRecord[userResource.columns.find((col) => col.primaryKey).name],
|
|
83
|
+
username,
|
|
84
|
+
};
|
|
85
|
+
const beforeLoginConfirmation = this.adminforth.config.auth.beforeLoginConfirmation;
|
|
86
|
+
if (beforeLoginConfirmation === null || beforeLoginConfirmation === void 0 ? void 0 : beforeLoginConfirmation.length) {
|
|
87
|
+
for (const hook of beforeLoginConfirmation) {
|
|
88
|
+
const resp = yield hook({ adminUser, response });
|
|
89
|
+
if ((_c = resp === null || resp === void 0 ? void 0 : resp.body) === null || _c === void 0 ? void 0 : _c.redirectTo) {
|
|
90
|
+
toReturn = { ok: resp.ok, redirectTo: (_d = resp === null || resp === void 0 ? void 0 : resp.body) === null || _d === void 0 ? void 0 : _d.redirectTo, allowedLogin: (_e = resp === null || resp === void 0 ? void 0 : resp.body) === null || _e === void 0 ? void 0 : _e.allowedLogin };
|
|
91
|
+
break;
|
|
101
92
|
}
|
|
102
93
|
}
|
|
103
|
-
if (toReturn.allowedLogin) {
|
|
104
|
-
this.adminforth.auth.setAuthCookie({ response, username, pk: userRecord[userResource.columns.find((col) => col.primaryKey).name] });
|
|
105
|
-
}
|
|
106
94
|
}
|
|
107
|
-
|
|
108
|
-
|
|
95
|
+
if (toReturn.allowedLogin) {
|
|
96
|
+
this.adminforth.auth.setAuthCookie({ response, username, pk: userRecord[userResource.columns.find((col) => col.primaryKey).name] });
|
|
109
97
|
}
|
|
110
98
|
}
|
|
99
|
+
else {
|
|
100
|
+
return { error: INVALID_MESSAGE };
|
|
101
|
+
}
|
|
111
102
|
return toReturn;
|
|
112
103
|
})
|
|
113
104
|
});
|
|
@@ -138,7 +129,7 @@ export default class AdminForthRestAPI {
|
|
|
138
129
|
throw new Error('No config.auth defined');
|
|
139
130
|
}
|
|
140
131
|
const usernameField = this.adminforth.config.auth.usernameField;
|
|
141
|
-
const resource = this.adminforth.config.resources.find((res) => res.resourceId === this.adminforth.config.auth.
|
|
132
|
+
const resource = this.adminforth.config.resources.find((res) => res.resourceId === this.adminforth.config.auth.usersResourceId);
|
|
142
133
|
const usernameColumn = resource.columns.find((col) => col.name === usernameField);
|
|
143
134
|
return {
|
|
144
135
|
brandName: this.adminforth.config.customization.brandName,
|
|
@@ -154,18 +145,12 @@ export default class AdminForthRestAPI {
|
|
|
154
145
|
method: 'GET',
|
|
155
146
|
path: '/get_base_config',
|
|
156
147
|
handler: (_k) => __awaiter(this, [_k], void 0, function* ({ input, adminUser, cookies }) {
|
|
157
|
-
var _l, _m, _o, _p
|
|
148
|
+
var _l, _m, _o, _p;
|
|
158
149
|
let username = '';
|
|
159
150
|
let userFullName = '';
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
}
|
|
164
|
-
else {
|
|
165
|
-
const dbUser = adminUser.dbUser;
|
|
166
|
-
username = dbUser[this.adminforth.config.auth.usernameField];
|
|
167
|
-
userFullName = dbUser[this.adminforth.config.auth.userFullNameField];
|
|
168
|
-
}
|
|
151
|
+
const dbUser = adminUser.dbUser;
|
|
152
|
+
username = dbUser[this.adminforth.config.auth.usernameField];
|
|
153
|
+
userFullName = dbUser[this.adminforth.config.auth.userFullNameField];
|
|
169
154
|
const userData = {
|
|
170
155
|
[this.adminforth.config.auth.usernameField]: username,
|
|
171
156
|
[this.adminforth.config.auth.userFullNameField]: userFullName
|
|
@@ -213,7 +198,7 @@ export default class AdminForthRestAPI {
|
|
|
213
198
|
yield processMenuItem(newMenuItem);
|
|
214
199
|
newMenu.push(newMenuItem);
|
|
215
200
|
}
|
|
216
|
-
const announcementBadge = (
|
|
201
|
+
const announcementBadge = (_m = (_l = this.adminforth.config.customization).announcementBadge) === null || _m === void 0 ? void 0 : _m.call(_l, adminUser);
|
|
217
202
|
return {
|
|
218
203
|
user: userData,
|
|
219
204
|
resources: this.adminforth.config.resources.map((res) => ({
|
|
@@ -228,8 +213,8 @@ export default class AdminForthRestAPI {
|
|
|
228
213
|
deleteConfirmation: this.adminforth.config.deleteConfirmation,
|
|
229
214
|
auth: this.adminforth.config.auth,
|
|
230
215
|
usernameField: this.adminforth.config.auth.usernameField,
|
|
231
|
-
title: (
|
|
232
|
-
emptyFieldPlaceholder: (
|
|
216
|
+
title: (_o = this.adminforth.config.customization) === null || _o === void 0 ? void 0 : _o.title,
|
|
217
|
+
emptyFieldPlaceholder: (_p = this.adminforth.config.customization) === null || _p === void 0 ? void 0 : _p.emptyFieldPlaceholder,
|
|
233
218
|
announcementBadge,
|
|
234
219
|
},
|
|
235
220
|
adminUser,
|
|
@@ -247,7 +232,7 @@ export default class AdminForthRestAPI {
|
|
|
247
232
|
server.endpoint({
|
|
248
233
|
method: 'POST',
|
|
249
234
|
path: '/get_resource',
|
|
250
|
-
handler: (
|
|
235
|
+
handler: (_q) => __awaiter(this, [_q], void 0, function* ({ body, adminUser }) {
|
|
251
236
|
const { resourceId } = body;
|
|
252
237
|
if (!this.adminforth.statuses.dbDiscover) {
|
|
253
238
|
return { error: 'Database discovery not started' };
|
|
@@ -278,8 +263,8 @@ export default class AdminForthRestAPI {
|
|
|
278
263
|
server.endpoint({
|
|
279
264
|
method: 'POST',
|
|
280
265
|
path: '/get_resource_data',
|
|
281
|
-
handler: (
|
|
282
|
-
var _t, _u, _v
|
|
266
|
+
handler: (_r) => __awaiter(this, [_r], void 0, function* ({ body, adminUser }) {
|
|
267
|
+
var _s, _t, _u, _v;
|
|
283
268
|
const { resourceId, source } = body;
|
|
284
269
|
if (['show', 'list'].includes(source) === false) {
|
|
285
270
|
return { error: 'Invalid source, should be list or show' };
|
|
@@ -299,7 +284,7 @@ export default class AdminForthRestAPI {
|
|
|
299
284
|
if (!allowed) {
|
|
300
285
|
return { error };
|
|
301
286
|
}
|
|
302
|
-
for (const hook of listify((
|
|
287
|
+
for (const hook of listify((_t = (_s = resource.hooks) === null || _s === void 0 ? void 0 : _s[source]) === null || _t === void 0 ? void 0 : _t.beforeDatasourceRequest)) {
|
|
303
288
|
const resp = yield hook({ resource, query: body, adminUser });
|
|
304
289
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
305
290
|
throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
|
|
@@ -378,7 +363,7 @@ export default class AdminForthRestAPI {
|
|
|
378
363
|
item._label = resource.recordLabel(item);
|
|
379
364
|
});
|
|
380
365
|
// only after adminforth made all post processing, give user ability to edit it
|
|
381
|
-
for (const hook of listify((
|
|
366
|
+
for (const hook of listify((_v = (_u = resource.hooks) === null || _u === void 0 ? void 0 : _u[source]) === null || _v === void 0 ? void 0 : _v.afterDatasourceResponse)) {
|
|
382
367
|
const resp = yield hook({ resource, response: data.data, adminUser });
|
|
383
368
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
384
369
|
throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
|
|
@@ -393,8 +378,8 @@ export default class AdminForthRestAPI {
|
|
|
393
378
|
server.endpoint({
|
|
394
379
|
method: 'POST',
|
|
395
380
|
path: '/get_resource_foreign_data',
|
|
396
|
-
handler: (
|
|
397
|
-
var _y, _z, _0
|
|
381
|
+
handler: (_w) => __awaiter(this, [_w], void 0, function* ({ body, adminUser }) {
|
|
382
|
+
var _x, _y, _z, _0;
|
|
398
383
|
const { resourceId, column } = body;
|
|
399
384
|
if (!this.adminforth.statuses.dbDiscover) {
|
|
400
385
|
return { error: 'Database discovery not started' };
|
|
@@ -415,7 +400,7 @@ export default class AdminForthRestAPI {
|
|
|
415
400
|
}
|
|
416
401
|
const targetResourceId = columnConfig.foreignResource.resourceId;
|
|
417
402
|
const targetResource = this.adminforth.config.resources.find((res) => res.resourceId == targetResourceId);
|
|
418
|
-
for (const hook of listify((
|
|
403
|
+
for (const hook of listify((_y = (_x = columnConfig.foreignResource.hooks) === null || _x === void 0 ? void 0 : _x.dropdownList) === null || _y === void 0 ? void 0 : _y.beforeDatasourceRequest)) {
|
|
419
404
|
const resp = yield hook({ query: body, adminUser, resource: targetResource });
|
|
420
405
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
421
406
|
throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
|
|
@@ -444,7 +429,7 @@ export default class AdminForthRestAPI {
|
|
|
444
429
|
const response = {
|
|
445
430
|
items
|
|
446
431
|
};
|
|
447
|
-
for (const hook of listify((
|
|
432
|
+
for (const hook of listify((_0 = (_z = columnConfig.foreignResource.hooks) === null || _z === void 0 ? void 0 : _z.dropdownList) === null || _0 === void 0 ? void 0 : _0.afterDatasourceResponse)) {
|
|
448
433
|
const resp = yield hook({ response, adminUser, resource: targetResource });
|
|
449
434
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
450
435
|
throw new Error(`Hook must return object with {ok: true} or { error: 'Error' } `);
|
|
@@ -459,7 +444,7 @@ export default class AdminForthRestAPI {
|
|
|
459
444
|
server.endpoint({
|
|
460
445
|
method: 'POST',
|
|
461
446
|
path: '/get_min_max_for_columns',
|
|
462
|
-
handler: (
|
|
447
|
+
handler: (_1) => __awaiter(this, [_1], void 0, function* ({ body }) {
|
|
463
448
|
const { resourceId } = body;
|
|
464
449
|
if (!this.adminforth.statuses.dbDiscover) {
|
|
465
450
|
return { error: 'Database discovery not started' };
|
|
@@ -488,7 +473,7 @@ export default class AdminForthRestAPI {
|
|
|
488
473
|
server.endpoint({
|
|
489
474
|
method: 'POST',
|
|
490
475
|
path: '/create_record',
|
|
491
|
-
handler: (
|
|
476
|
+
handler: (_2) => __awaiter(this, [_2], void 0, function* ({ body, adminUser }) {
|
|
492
477
|
const resource = this.adminforth.config.resources.find((res) => res.resourceId == body['resourceId']);
|
|
493
478
|
if (!resource) {
|
|
494
479
|
return { error: `Resource '${body['resourceId']}' not found` };
|
|
@@ -512,8 +497,8 @@ export default class AdminForthRestAPI {
|
|
|
512
497
|
server.endpoint({
|
|
513
498
|
method: 'POST',
|
|
514
499
|
path: '/update_record',
|
|
515
|
-
handler: (
|
|
516
|
-
var _5, _6, _7
|
|
500
|
+
handler: (_3) => __awaiter(this, [_3], void 0, function* ({ body, adminUser }) {
|
|
501
|
+
var _4, _5, _6, _7;
|
|
517
502
|
const resource = this.adminforth.config.resources.find((res) => res.resourceId == body['resourceId']);
|
|
518
503
|
if (!resource) {
|
|
519
504
|
return { error: `Resource '${body['resourceId']}' not found` };
|
|
@@ -532,7 +517,7 @@ export default class AdminForthRestAPI {
|
|
|
532
517
|
return { error };
|
|
533
518
|
}
|
|
534
519
|
// execute hook if needed
|
|
535
|
-
for (const hook of listify((
|
|
520
|
+
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)) {
|
|
536
521
|
const resp = yield hook({ resource, record, adminUser });
|
|
537
522
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
538
523
|
throw new Error(`Hook beforeSave must return object with {ok: true} or { error: 'Error' } `);
|
|
@@ -559,7 +544,7 @@ export default class AdminForthRestAPI {
|
|
|
559
544
|
yield connector.updateRecord({ resource, recordId, newValues });
|
|
560
545
|
}
|
|
561
546
|
// execute hook if needed
|
|
562
|
-
for (const hook of listify((
|
|
547
|
+
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)) {
|
|
563
548
|
const resp = yield hook({ resource, record, adminUser, oldRecord });
|
|
564
549
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
565
550
|
throw new Error(`Hook afterSave must return object with {ok: true} or { error: 'Error' } `);
|
|
@@ -576,8 +561,8 @@ export default class AdminForthRestAPI {
|
|
|
576
561
|
server.endpoint({
|
|
577
562
|
method: 'POST',
|
|
578
563
|
path: '/delete_record',
|
|
579
|
-
handler: (
|
|
580
|
-
var _10, _11, _12
|
|
564
|
+
handler: (_8) => __awaiter(this, [_8], void 0, function* ({ body, adminUser }) {
|
|
565
|
+
var _9, _10, _11, _12;
|
|
581
566
|
const resource = this.adminforth.config.resources.find((res) => res.resourceId == body['resourceId']);
|
|
582
567
|
const record = yield this.adminforth.connectors[resource.dataSource].getRecordByPrimaryKey(resource, body['primaryKey']);
|
|
583
568
|
if (!resource) {
|
|
@@ -595,7 +580,7 @@ export default class AdminForthRestAPI {
|
|
|
595
580
|
return { error };
|
|
596
581
|
}
|
|
597
582
|
// execute hook if needed
|
|
598
|
-
for (const hook of listify((
|
|
583
|
+
for (const hook of listify((_10 = (_9 = resource.hooks) === null || _9 === void 0 ? void 0 : _9.delete) === null || _10 === void 0 ? void 0 : _10.beforeSave)) {
|
|
599
584
|
const resp = yield hook({ resource, record, adminUser });
|
|
600
585
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
601
586
|
throw new Error(`Hook beforeSave must return object with {ok: true} or { error: 'Error' } `);
|
|
@@ -607,7 +592,7 @@ export default class AdminForthRestAPI {
|
|
|
607
592
|
const connector = this.adminforth.connectors[resource.dataSource];
|
|
608
593
|
yield connector.deleteRecord({ resource, recordId: body['primaryKey'] });
|
|
609
594
|
// execute hook if needed
|
|
610
|
-
for (const hook of listify((
|
|
595
|
+
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)) {
|
|
611
596
|
const resp = yield hook({ resource, record, adminUser });
|
|
612
597
|
if (!resp || (!resp.ok && !resp.error)) {
|
|
613
598
|
throw new Error(`Hook afterSave must return object with {ok: true} or { error: 'Error' } `);
|
|
@@ -624,7 +609,7 @@ export default class AdminForthRestAPI {
|
|
|
624
609
|
server.endpoint({
|
|
625
610
|
method: 'POST',
|
|
626
611
|
path: '/start_bulk_action',
|
|
627
|
-
handler: (
|
|
612
|
+
handler: (_13) => __awaiter(this, [_13], void 0, function* ({ body, adminUser }) {
|
|
628
613
|
const { resourceId, actionId, recordIds } = body;
|
|
629
614
|
const resource = this.adminforth.config.resources.find((res) => res.resourceId == resourceId);
|
|
630
615
|
if (!resource) {
|
package/dist/modules/utils.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
2
|
import { fileURLToPath } from 'url';
|
|
3
3
|
import fs from 'fs';
|
|
4
|
+
import Fuse from 'fuse.js';
|
|
4
5
|
// @ts-ignore-next-line
|
|
5
6
|
const csscolors = {
|
|
6
7
|
"aliceblue": "#f0f8ff",
|
|
@@ -307,3 +308,20 @@ export function inverseRGBA(rgba) {
|
|
|
307
308
|
let brightness = (r * 299 + g * 587 + b * 114) / 1000;
|
|
308
309
|
return brightness > 128 ? 'rgba(0,0,0,1)' : 'rgba(255,255,255,1)';
|
|
309
310
|
}
|
|
311
|
+
export function suggestIfTypo(names, name) {
|
|
312
|
+
console.log('names', names);
|
|
313
|
+
console.log('name', name);
|
|
314
|
+
if (!name) {
|
|
315
|
+
return null;
|
|
316
|
+
}
|
|
317
|
+
const options = {
|
|
318
|
+
includeScore: true, // Includes score in the results to see how close matches are
|
|
319
|
+
threshold: 0.3, // Defines the fuzziness (lower values mean stricter matches)
|
|
320
|
+
};
|
|
321
|
+
const fuse = new Fuse(names.filter((n) => !!n), options);
|
|
322
|
+
// Search for a resource
|
|
323
|
+
const result = fuse.search(name);
|
|
324
|
+
if (result.length > 0) {
|
|
325
|
+
return result[0].item;
|
|
326
|
+
}
|
|
327
|
+
}
|