adminforth 1.0.73 → 1.0.74
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dataConnectors/mongo.ts +5 -6
- package/dataConnectors/postgres.ts +16 -15
- package/dataConnectors/sqlite.ts +17 -12
- package/dist/dataConnectors/mongo.js +5 -5
- package/dist/dataConnectors/postgres.js +15 -15
- package/dist/dataConnectors/sqlite.js +15 -12
- package/dist/index.js +36 -13
- package/dist/modules/codeInjector.js +29 -6
- package/dist/spa/spa/src/utils.ts +11 -0
- package/dist/spa/spa/src/views/LoginView.vue +5 -0
- package/dist/types/AdminForthConfig.js +33 -1
- package/index.ts +40 -17
- package/modules/codeInjector.ts +33 -6
- package/package.json +1 -1
- package/plugins/base.ts +4 -5
- package/spa/src/utils.ts +11 -0
- package/spa/src/views/LoginView.vue +5 -0
- package/types/AdminForthConfig.ts +53 -14
- package/types.ts +0 -36
package/dataConnectors/mongo.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import dayjs from 'dayjs';
|
|
2
2
|
import { MongoClient } from 'mongodb';
|
|
3
|
-
import { AdminForthFilterOperators, AdminForthSortDirections
|
|
4
|
-
|
|
3
|
+
import { AdminForthDataTypes, AdminForthFilterOperators, AdminForthSortDirections } from '../types/AdminForthConfig.js';
|
|
5
4
|
|
|
6
5
|
class MongoConnector {
|
|
7
6
|
db: MongoClient
|
|
@@ -65,7 +64,7 @@ class MongoConnector {
|
|
|
65
64
|
}
|
|
66
65
|
|
|
67
66
|
getFieldValue(field, value) {
|
|
68
|
-
if (field.type ==
|
|
67
|
+
if (field.type == AdminForthDataTypes.DATETIME) {
|
|
69
68
|
if (!value) {
|
|
70
69
|
return null;
|
|
71
70
|
}
|
|
@@ -76,7 +75,7 @@ class MongoConnector {
|
|
|
76
75
|
} else {
|
|
77
76
|
throw new Error(`AdminForth does not support row type: ${field._underlineType} for timestamps, use VARCHAR (with iso strings) or TIMESTAMP/INT (with unix timestamps)`);
|
|
78
77
|
}
|
|
79
|
-
} else if (field.type ==
|
|
78
|
+
} else if (field.type == AdminForthDataTypes.BOOLEAN) {
|
|
80
79
|
return !!value;
|
|
81
80
|
}
|
|
82
81
|
return value;
|
|
@@ -103,7 +102,7 @@ class MongoConnector {
|
|
|
103
102
|
}
|
|
104
103
|
|
|
105
104
|
setFieldValue(field, value) {
|
|
106
|
-
if (field.type ==
|
|
105
|
+
if (field.type == AdminForthDataTypes.DATETIME) {
|
|
107
106
|
if (!value) {
|
|
108
107
|
return null;
|
|
109
108
|
}
|
|
@@ -114,7 +113,7 @@ class MongoConnector {
|
|
|
114
113
|
// value is iso string now, convert to unix timestamp
|
|
115
114
|
return dayjs(value).toISOString();
|
|
116
115
|
}
|
|
117
|
-
} else if (field.type ==
|
|
116
|
+
} else if (field.type == AdminForthDataTypes.BOOLEAN) {
|
|
118
117
|
return value ? 1 : 0;
|
|
119
118
|
}
|
|
120
119
|
return value;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import dayjs from 'dayjs';
|
|
2
2
|
import pkg from 'pg';
|
|
3
|
-
import { AdminForthFilterOperators, AdminForthSortDirections
|
|
3
|
+
import { AdminForthDataTypes, AdminForthFilterOperators, AdminForthSortDirections } from '../types/AdminForthConfig.js';
|
|
4
|
+
|
|
4
5
|
const { Client } = pkg;
|
|
5
6
|
|
|
6
7
|
|
|
@@ -72,44 +73,44 @@ class PostgresConnector {
|
|
|
72
73
|
const field: any = {};
|
|
73
74
|
const baseType = row.type.toLowerCase();
|
|
74
75
|
if (baseType == 'int') {
|
|
75
|
-
field.type =
|
|
76
|
+
field.type = AdminForthDataTypes.INTEGER;
|
|
76
77
|
field._underlineType = 'int';
|
|
77
78
|
|
|
78
79
|
} else if (baseType.includes('float') || baseType.includes('double')) {
|
|
79
|
-
field.type =
|
|
80
|
+
field.type = AdminForthDataTypes.FLOAT;
|
|
80
81
|
field._underlineType = 'float';
|
|
81
82
|
|
|
82
83
|
} else if (baseType.includes('bool')) {
|
|
83
|
-
field.type =
|
|
84
|
+
field.type = AdminForthDataTypes.BOOLEAN;
|
|
84
85
|
field._underlineType = 'bool';
|
|
85
86
|
|
|
86
87
|
} else if (baseType == 'uuid') {
|
|
87
|
-
field.type =
|
|
88
|
+
field.type = AdminForthDataTypes.STRING;
|
|
88
89
|
field._underlineType = 'uuid';
|
|
89
90
|
|
|
90
91
|
} else if (baseType.includes('character varying')) {
|
|
91
|
-
field.type =
|
|
92
|
+
field.type = AdminForthDataTypes.STRING;
|
|
92
93
|
field._underlineType = 'varchar';
|
|
93
94
|
const length = baseType.match(/\d+/);
|
|
94
95
|
field.maxLength = length ? parseInt(length[0]) : null;
|
|
95
96
|
|
|
96
97
|
} else if (baseType == 'text') {
|
|
97
|
-
field.type =
|
|
98
|
+
field.type = AdminForthDataTypes.TEXT;
|
|
98
99
|
field._underlineType = 'text';
|
|
99
100
|
|
|
100
101
|
} else if (baseType.includes('decimal(')) {
|
|
101
|
-
field.type =
|
|
102
|
+
field.type = AdminForthDataTypes.DECIMAL;
|
|
102
103
|
field._underlineType = 'decimal';
|
|
103
104
|
const [precision, scale] = baseType.match(/\d+/g);
|
|
104
105
|
field.precision = parseInt(precision);
|
|
105
106
|
field.scale = parseInt(scale);
|
|
106
107
|
|
|
107
108
|
} else if (baseType == 'real') {
|
|
108
|
-
field.type =
|
|
109
|
+
field.type = AdminForthDataTypes.FLOAT;
|
|
109
110
|
field._underlineType = 'real';
|
|
110
111
|
|
|
111
112
|
} else if (baseType.includes('date') || baseType.includes('time')) {
|
|
112
|
-
field.type =
|
|
113
|
+
field.type = AdminForthDataTypes.DATETIME;
|
|
113
114
|
field._underlineType = 'timestamp';
|
|
114
115
|
|
|
115
116
|
} else {
|
|
@@ -125,7 +126,7 @@ class PostgresConnector {
|
|
|
125
126
|
}
|
|
126
127
|
|
|
127
128
|
getFieldValue(field, value) {
|
|
128
|
-
if (field.type ==
|
|
129
|
+
if (field.type == AdminForthDataTypes.DATETIME) {
|
|
129
130
|
if (!value) {
|
|
130
131
|
return null;
|
|
131
132
|
}
|
|
@@ -165,7 +166,7 @@ class PostgresConnector {
|
|
|
165
166
|
}
|
|
166
167
|
|
|
167
168
|
setFieldValue(field, value) {
|
|
168
|
-
if (field.type ==
|
|
169
|
+
if (field.type == AdminForthDataTypes.DATETIME) {
|
|
169
170
|
if (!value) {
|
|
170
171
|
return null;
|
|
171
172
|
}
|
|
@@ -174,14 +175,14 @@ class PostgresConnector {
|
|
|
174
175
|
} else if (field._underlineType == 'varchar') {
|
|
175
176
|
return dayjs(value).toISOString();
|
|
176
177
|
}
|
|
177
|
-
} else if (field.type ==
|
|
178
|
+
} else if (field.type == AdminForthDataTypes.BOOLEAN) {
|
|
178
179
|
return value ? 1 : 0;
|
|
179
180
|
}
|
|
180
181
|
return value;
|
|
181
182
|
}
|
|
182
183
|
|
|
183
184
|
async getData({ resource, limit, offset, sort, filters }) {
|
|
184
|
-
const columns = resource.dataSourceColumns.
|
|
185
|
+
const columns = resource.dataSourceColumns.map((col) => `"${col.name}"`).join(', ');
|
|
185
186
|
const tableName = resource.table;
|
|
186
187
|
|
|
187
188
|
let totalCounter = 1;
|
|
@@ -229,7 +230,7 @@ class PostgresConnector {
|
|
|
229
230
|
const orderBy = sort.length ? `ORDER BY ${sort.map((s) => `${s.field} ${this.SortDirectionsMap[s.direction]}`).join(', ')}` : '';
|
|
230
231
|
const selectQuery = `SELECT ${columns} FROM ${tableName} ${where} ${orderBy} ${limitOffset}`;
|
|
231
232
|
if (process.env.HEAVY_DEBUG) {
|
|
232
|
-
console.log('
|
|
233
|
+
console.log('🪲 PG selectQuery:', selectQuery, 'params:', d);
|
|
233
234
|
}
|
|
234
235
|
const stmt = await this.db.query(selectQuery, d);
|
|
235
236
|
const rows = stmt.rows;
|
package/dataConnectors/sqlite.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import betterSqlite3 from 'better-sqlite3';
|
|
2
|
-
import {
|
|
2
|
+
import { AdminForthDataTypes, AdminForthFilterOperators, AdminForthSortDirections } from '../types/AdminForthConfig.js';
|
|
3
|
+
|
|
3
4
|
import dayjs from 'dayjs';
|
|
4
5
|
|
|
5
6
|
class SQLiteConnector {
|
|
@@ -21,30 +22,30 @@ class SQLiteConnector {
|
|
|
21
22
|
const field: any = {};
|
|
22
23
|
const baseType = row.type.toLowerCase();
|
|
23
24
|
if (baseType == 'int') {
|
|
24
|
-
field.type =
|
|
25
|
+
field.type = AdminForthDataTypes.INTEGER;
|
|
25
26
|
field._underlineType = 'int';
|
|
26
27
|
} else if (baseType.includes('varchar(')) {
|
|
27
|
-
field.type =
|
|
28
|
+
field.type = AdminForthDataTypes.STRING;
|
|
28
29
|
field._underlineType = 'varchar';
|
|
29
30
|
const length = baseType.match(/\d+/);
|
|
30
31
|
field.maxLength = length ? parseInt(length[0]) : null;
|
|
31
32
|
} else if (baseType == 'text') {
|
|
32
|
-
field.type =
|
|
33
|
+
field.type = AdminForthDataTypes.TEXT;
|
|
33
34
|
field._underlineType = 'text';
|
|
34
35
|
} else if (baseType.includes('decimal(')) {
|
|
35
|
-
field.type =
|
|
36
|
+
field.type = AdminForthDataTypes.DECIMAL;
|
|
36
37
|
field._underlineType = 'decimal';
|
|
37
38
|
const [precision, scale] = baseType.match(/\d+/g);
|
|
38
39
|
field.precision = parseInt(precision);
|
|
39
40
|
field.scale = parseInt(scale);
|
|
40
41
|
} else if (baseType == 'real') {
|
|
41
|
-
field.type =
|
|
42
|
+
field.type = AdminForthDataTypes.FLOAT; //8-byte IEEE floating point number. It
|
|
42
43
|
field._underlineType = 'real';
|
|
43
44
|
} else if (baseType == 'timestamp') {
|
|
44
|
-
field.type =
|
|
45
|
+
field.type = AdminForthDataTypes.DATETIME;
|
|
45
46
|
field._underlineType = 'timestamp';
|
|
46
47
|
} else if (baseType == 'boolean') {
|
|
47
|
-
field.type =
|
|
48
|
+
field.type = AdminForthDataTypes.BOOLEAN;
|
|
48
49
|
field._underlineType = 'boolean';
|
|
49
50
|
} else {
|
|
50
51
|
field.type = 'unknown'
|
|
@@ -67,7 +68,7 @@ class SQLiteConnector {
|
|
|
67
68
|
}
|
|
68
69
|
|
|
69
70
|
getFieldValue(field, value) {
|
|
70
|
-
if (field.type ==
|
|
71
|
+
if (field.type == AdminForthDataTypes.DATETIME) {
|
|
71
72
|
if (!value) {
|
|
72
73
|
return null;
|
|
73
74
|
}
|
|
@@ -78,7 +79,7 @@ class SQLiteConnector {
|
|
|
78
79
|
} else {
|
|
79
80
|
throw new Error(`AdminForth does not support row type: ${field._underlineType} for timestamps, use VARCHAR (with iso strings) or TIMESTAMP/INT (with unix timestamps). Issue in field "${field.name}"`);
|
|
80
81
|
}
|
|
81
|
-
} else if (field.type ==
|
|
82
|
+
} else if (field.type == AdminForthDataTypes.BOOLEAN) {
|
|
82
83
|
return !!value;
|
|
83
84
|
}
|
|
84
85
|
return value;
|
|
@@ -100,7 +101,7 @@ class SQLiteConnector {
|
|
|
100
101
|
}
|
|
101
102
|
|
|
102
103
|
setFieldValue(field, value) {
|
|
103
|
-
if (field.type ==
|
|
104
|
+
if (field.type == AdminForthDataTypes.DATETIME) {
|
|
104
105
|
if (!value) {
|
|
105
106
|
return null;
|
|
106
107
|
}
|
|
@@ -111,7 +112,7 @@ class SQLiteConnector {
|
|
|
111
112
|
// value is iso string now, convert to unix timestamp
|
|
112
113
|
return dayjs(value).toISOString();
|
|
113
114
|
}
|
|
114
|
-
} else if (field.type ==
|
|
115
|
+
} else if (field.type == AdminForthDataTypes.BOOLEAN) {
|
|
115
116
|
return value ? 1 : 0;
|
|
116
117
|
}
|
|
117
118
|
|
|
@@ -183,6 +184,10 @@ class SQLiteConnector {
|
|
|
183
184
|
const q = `SELECT ${columns} FROM ${tableName} ${where} ${orderBy} LIMIT ? OFFSET ?`;
|
|
184
185
|
const stmt = this.db.prepare(q);
|
|
185
186
|
const d = [...filterValues, limit, offset];
|
|
187
|
+
|
|
188
|
+
if (process.env.HEAVY_DEBUG) {
|
|
189
|
+
console.log('🪲 SQLITE Query', q, 'params:', d);
|
|
190
|
+
}
|
|
186
191
|
const rows = stmt.all(d);
|
|
187
192
|
|
|
188
193
|
const total = this.db.prepare(`SELECT COUNT(*) FROM ${tableName} ${where}`).get([...filterValues])['COUNT(*)'];
|
|
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
};
|
|
10
10
|
import dayjs from 'dayjs';
|
|
11
11
|
import { MongoClient } from 'mongodb';
|
|
12
|
-
import { AdminForthFilterOperators, AdminForthSortDirections
|
|
12
|
+
import { AdminForthDataTypes, AdminForthFilterOperators, AdminForthSortDirections } from '../types/AdminForthConfig.js';
|
|
13
13
|
class MongoConnector {
|
|
14
14
|
constructor({ url }) {
|
|
15
15
|
this.OperatorsMap = {
|
|
@@ -67,7 +67,7 @@ class MongoConnector {
|
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
getFieldValue(field, value) {
|
|
70
|
-
if (field.type ==
|
|
70
|
+
if (field.type == AdminForthDataTypes.DATETIME) {
|
|
71
71
|
if (!value) {
|
|
72
72
|
return null;
|
|
73
73
|
}
|
|
@@ -81,7 +81,7 @@ class MongoConnector {
|
|
|
81
81
|
throw new Error(`AdminForth does not support row type: ${field._underlineType} for timestamps, use VARCHAR (with iso strings) or TIMESTAMP/INT (with unix timestamps)`);
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
|
-
else if (field.type ==
|
|
84
|
+
else if (field.type == AdminForthDataTypes.BOOLEAN) {
|
|
85
85
|
return !!value;
|
|
86
86
|
}
|
|
87
87
|
return value;
|
|
@@ -108,7 +108,7 @@ class MongoConnector {
|
|
|
108
108
|
});
|
|
109
109
|
}
|
|
110
110
|
setFieldValue(field, value) {
|
|
111
|
-
if (field.type ==
|
|
111
|
+
if (field.type == AdminForthDataTypes.DATETIME) {
|
|
112
112
|
if (!value) {
|
|
113
113
|
return null;
|
|
114
114
|
}
|
|
@@ -121,7 +121,7 @@ class MongoConnector {
|
|
|
121
121
|
return dayjs(value).toISOString();
|
|
122
122
|
}
|
|
123
123
|
}
|
|
124
|
-
else if (field.type ==
|
|
124
|
+
else if (field.type == AdminForthDataTypes.BOOLEAN) {
|
|
125
125
|
return value ? 1 : 0;
|
|
126
126
|
}
|
|
127
127
|
return value;
|
|
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
};
|
|
10
10
|
import dayjs from 'dayjs';
|
|
11
11
|
import pkg from 'pg';
|
|
12
|
-
import { AdminForthFilterOperators, AdminForthSortDirections
|
|
12
|
+
import { AdminForthDataTypes, AdminForthFilterOperators, AdminForthSortDirections } from '../types/AdminForthConfig.js';
|
|
13
13
|
const { Client } = pkg;
|
|
14
14
|
class PostgresConnector {
|
|
15
15
|
constructor({ url }) {
|
|
@@ -73,44 +73,44 @@ class PostgresConnector {
|
|
|
73
73
|
const field = {};
|
|
74
74
|
const baseType = row.type.toLowerCase();
|
|
75
75
|
if (baseType == 'int') {
|
|
76
|
-
field.type =
|
|
76
|
+
field.type = AdminForthDataTypes.INTEGER;
|
|
77
77
|
field._underlineType = 'int';
|
|
78
78
|
}
|
|
79
79
|
else if (baseType.includes('float') || baseType.includes('double')) {
|
|
80
|
-
field.type =
|
|
80
|
+
field.type = AdminForthDataTypes.FLOAT;
|
|
81
81
|
field._underlineType = 'float';
|
|
82
82
|
}
|
|
83
83
|
else if (baseType.includes('bool')) {
|
|
84
|
-
field.type =
|
|
84
|
+
field.type = AdminForthDataTypes.BOOLEAN;
|
|
85
85
|
field._underlineType = 'bool';
|
|
86
86
|
}
|
|
87
87
|
else if (baseType == 'uuid') {
|
|
88
|
-
field.type =
|
|
88
|
+
field.type = AdminForthDataTypes.STRING;
|
|
89
89
|
field._underlineType = 'uuid';
|
|
90
90
|
}
|
|
91
91
|
else if (baseType.includes('character varying')) {
|
|
92
|
-
field.type =
|
|
92
|
+
field.type = AdminForthDataTypes.STRING;
|
|
93
93
|
field._underlineType = 'varchar';
|
|
94
94
|
const length = baseType.match(/\d+/);
|
|
95
95
|
field.maxLength = length ? parseInt(length[0]) : null;
|
|
96
96
|
}
|
|
97
97
|
else if (baseType == 'text') {
|
|
98
|
-
field.type =
|
|
98
|
+
field.type = AdminForthDataTypes.TEXT;
|
|
99
99
|
field._underlineType = 'text';
|
|
100
100
|
}
|
|
101
101
|
else if (baseType.includes('decimal(')) {
|
|
102
|
-
field.type =
|
|
102
|
+
field.type = AdminForthDataTypes.DECIMAL;
|
|
103
103
|
field._underlineType = 'decimal';
|
|
104
104
|
const [precision, scale] = baseType.match(/\d+/g);
|
|
105
105
|
field.precision = parseInt(precision);
|
|
106
106
|
field.scale = parseInt(scale);
|
|
107
107
|
}
|
|
108
108
|
else if (baseType == 'real') {
|
|
109
|
-
field.type =
|
|
109
|
+
field.type = AdminForthDataTypes.FLOAT;
|
|
110
110
|
field._underlineType = 'real';
|
|
111
111
|
}
|
|
112
112
|
else if (baseType.includes('date') || baseType.includes('time')) {
|
|
113
|
-
field.type =
|
|
113
|
+
field.type = AdminForthDataTypes.DATETIME;
|
|
114
114
|
field._underlineType = 'timestamp';
|
|
115
115
|
}
|
|
116
116
|
else {
|
|
@@ -126,7 +126,7 @@ class PostgresConnector {
|
|
|
126
126
|
});
|
|
127
127
|
}
|
|
128
128
|
getFieldValue(field, value) {
|
|
129
|
-
if (field.type ==
|
|
129
|
+
if (field.type == AdminForthDataTypes.DATETIME) {
|
|
130
130
|
if (!value) {
|
|
131
131
|
return null;
|
|
132
132
|
}
|
|
@@ -166,7 +166,7 @@ class PostgresConnector {
|
|
|
166
166
|
});
|
|
167
167
|
}
|
|
168
168
|
setFieldValue(field, value) {
|
|
169
|
-
if (field.type ==
|
|
169
|
+
if (field.type == AdminForthDataTypes.DATETIME) {
|
|
170
170
|
if (!value) {
|
|
171
171
|
return null;
|
|
172
172
|
}
|
|
@@ -177,14 +177,14 @@ class PostgresConnector {
|
|
|
177
177
|
return dayjs(value).toISOString();
|
|
178
178
|
}
|
|
179
179
|
}
|
|
180
|
-
else if (field.type ==
|
|
180
|
+
else if (field.type == AdminForthDataTypes.BOOLEAN) {
|
|
181
181
|
return value ? 1 : 0;
|
|
182
182
|
}
|
|
183
183
|
return value;
|
|
184
184
|
}
|
|
185
185
|
getData(_a) {
|
|
186
186
|
return __awaiter(this, arguments, void 0, function* ({ resource, limit, offset, sort, filters }) {
|
|
187
|
-
const columns = resource.dataSourceColumns.
|
|
187
|
+
const columns = resource.dataSourceColumns.map((col) => `"${col.name}"`).join(', ');
|
|
188
188
|
const tableName = resource.table;
|
|
189
189
|
let totalCounter = 1;
|
|
190
190
|
const where = filters.length ? `WHERE ${filters.map((f, i) => {
|
|
@@ -232,7 +232,7 @@ class PostgresConnector {
|
|
|
232
232
|
const orderBy = sort.length ? `ORDER BY ${sort.map((s) => `${s.field} ${this.SortDirectionsMap[s.direction]}`).join(', ')}` : '';
|
|
233
233
|
const selectQuery = `SELECT ${columns} FROM ${tableName} ${where} ${orderBy} ${limitOffset}`;
|
|
234
234
|
if (process.env.HEAVY_DEBUG) {
|
|
235
|
-
console.log('
|
|
235
|
+
console.log('🪲 PG selectQuery:', selectQuery, 'params:', d);
|
|
236
236
|
}
|
|
237
237
|
const stmt = yield this.db.query(selectQuery, d);
|
|
238
238
|
const rows = stmt.rows;
|
|
@@ -8,7 +8,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
10
|
import betterSqlite3 from 'better-sqlite3';
|
|
11
|
-
import {
|
|
11
|
+
import { AdminForthDataTypes, AdminForthFilterOperators, AdminForthSortDirections } from '../types/AdminForthConfig.js';
|
|
12
12
|
import dayjs from 'dayjs';
|
|
13
13
|
class SQLiteConnector {
|
|
14
14
|
constructor({ url }) {
|
|
@@ -41,36 +41,36 @@ class SQLiteConnector {
|
|
|
41
41
|
const field = {};
|
|
42
42
|
const baseType = row.type.toLowerCase();
|
|
43
43
|
if (baseType == 'int') {
|
|
44
|
-
field.type =
|
|
44
|
+
field.type = AdminForthDataTypes.INTEGER;
|
|
45
45
|
field._underlineType = 'int';
|
|
46
46
|
}
|
|
47
47
|
else if (baseType.includes('varchar(')) {
|
|
48
|
-
field.type =
|
|
48
|
+
field.type = AdminForthDataTypes.STRING;
|
|
49
49
|
field._underlineType = 'varchar';
|
|
50
50
|
const length = baseType.match(/\d+/);
|
|
51
51
|
field.maxLength = length ? parseInt(length[0]) : null;
|
|
52
52
|
}
|
|
53
53
|
else if (baseType == 'text') {
|
|
54
|
-
field.type =
|
|
54
|
+
field.type = AdminForthDataTypes.TEXT;
|
|
55
55
|
field._underlineType = 'text';
|
|
56
56
|
}
|
|
57
57
|
else if (baseType.includes('decimal(')) {
|
|
58
|
-
field.type =
|
|
58
|
+
field.type = AdminForthDataTypes.DECIMAL;
|
|
59
59
|
field._underlineType = 'decimal';
|
|
60
60
|
const [precision, scale] = baseType.match(/\d+/g);
|
|
61
61
|
field.precision = parseInt(precision);
|
|
62
62
|
field.scale = parseInt(scale);
|
|
63
63
|
}
|
|
64
64
|
else if (baseType == 'real') {
|
|
65
|
-
field.type =
|
|
65
|
+
field.type = AdminForthDataTypes.FLOAT; //8-byte IEEE floating point number. It
|
|
66
66
|
field._underlineType = 'real';
|
|
67
67
|
}
|
|
68
68
|
else if (baseType == 'timestamp') {
|
|
69
|
-
field.type =
|
|
69
|
+
field.type = AdminForthDataTypes.DATETIME;
|
|
70
70
|
field._underlineType = 'timestamp';
|
|
71
71
|
}
|
|
72
72
|
else if (baseType == 'boolean') {
|
|
73
|
-
field.type =
|
|
73
|
+
field.type = AdminForthDataTypes.BOOLEAN;
|
|
74
74
|
field._underlineType = 'boolean';
|
|
75
75
|
}
|
|
76
76
|
else {
|
|
@@ -93,7 +93,7 @@ class SQLiteConnector {
|
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
95
|
getFieldValue(field, value) {
|
|
96
|
-
if (field.type ==
|
|
96
|
+
if (field.type == AdminForthDataTypes.DATETIME) {
|
|
97
97
|
if (!value) {
|
|
98
98
|
return null;
|
|
99
99
|
}
|
|
@@ -107,7 +107,7 @@ class SQLiteConnector {
|
|
|
107
107
|
throw new Error(`AdminForth does not support row type: ${field._underlineType} for timestamps, use VARCHAR (with iso strings) or TIMESTAMP/INT (with unix timestamps). Issue in field "${field.name}"`);
|
|
108
108
|
}
|
|
109
109
|
}
|
|
110
|
-
else if (field.type ==
|
|
110
|
+
else if (field.type == AdminForthDataTypes.BOOLEAN) {
|
|
111
111
|
return !!value;
|
|
112
112
|
}
|
|
113
113
|
return value;
|
|
@@ -129,7 +129,7 @@ class SQLiteConnector {
|
|
|
129
129
|
});
|
|
130
130
|
}
|
|
131
131
|
setFieldValue(field, value) {
|
|
132
|
-
if (field.type ==
|
|
132
|
+
if (field.type == AdminForthDataTypes.DATETIME) {
|
|
133
133
|
if (!value) {
|
|
134
134
|
return null;
|
|
135
135
|
}
|
|
@@ -142,7 +142,7 @@ class SQLiteConnector {
|
|
|
142
142
|
return dayjs(value).toISOString();
|
|
143
143
|
}
|
|
144
144
|
}
|
|
145
|
-
else if (field.type ==
|
|
145
|
+
else if (field.type == AdminForthDataTypes.BOOLEAN) {
|
|
146
146
|
return value ? 1 : 0;
|
|
147
147
|
}
|
|
148
148
|
return value;
|
|
@@ -188,6 +188,9 @@ class SQLiteConnector {
|
|
|
188
188
|
const q = `SELECT ${columns} FROM ${tableName} ${where} ${orderBy} LIMIT ? OFFSET ?`;
|
|
189
189
|
const stmt = this.db.prepare(q);
|
|
190
190
|
const d = [...filterValues, limit, offset];
|
|
191
|
+
if (process.env.HEAVY_DEBUG) {
|
|
192
|
+
console.log('🪲 SQLITE Query', q, 'params:', d);
|
|
193
|
+
}
|
|
191
194
|
const rows = stmt.all(d);
|
|
192
195
|
const total = this.db.prepare(`SELECT COUNT(*) FROM ${tableName} ${where}`).get([...filterValues])['COUNT(*)'];
|
|
193
196
|
// run all fields via getFieldValue
|
package/dist/index.js
CHANGED
|
@@ -23,8 +23,9 @@ import ExpressServer from './servers/express.js';
|
|
|
23
23
|
import { v1 as uuid } from 'uuid';
|
|
24
24
|
import fs from 'fs';
|
|
25
25
|
import { ADMINFORTH_VERSION } from './modules/utils.js';
|
|
26
|
-
import { AdminForthFilterOperators,
|
|
26
|
+
import { AdminForthFilterOperators, AdminForthDataTypes } from './types/AdminForthConfig.js';
|
|
27
27
|
import { getFunctionList } from './modules/utils.js';
|
|
28
|
+
import path from 'path';
|
|
28
29
|
const AVAILABLE_SHOW_IN = ['list', 'edit', 'create', 'filter', 'show'];
|
|
29
30
|
const DEFAULT_ALLOWED_ACTIONS = { create: true, edit: true, show: true, delete: true };
|
|
30
31
|
class AdminForth {
|
|
@@ -51,8 +52,18 @@ class AdminForth {
|
|
|
51
52
|
}
|
|
52
53
|
;
|
|
53
54
|
}
|
|
55
|
+
checkCustomFileExists(filePath) {
|
|
56
|
+
if (filePath.startsWith('@@/')) {
|
|
57
|
+
const checkPath = path.join(this.config.customization.customComponentsDir, filePath.replace('@@/', ''));
|
|
58
|
+
if (!fs.existsSync(checkPath)) {
|
|
59
|
+
return [`File file ${filePath} does not exist in ${this.config.customization.customComponentsDir}`];
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return [];
|
|
63
|
+
}
|
|
54
64
|
validateConfig() {
|
|
55
65
|
var _b;
|
|
66
|
+
const errors = [];
|
|
56
67
|
if (this.config.rootUser) {
|
|
57
68
|
if (!this.config.rootUser.username) {
|
|
58
69
|
throw new Error('rootUser.username is required');
|
|
@@ -69,6 +80,12 @@ class AdminForth {
|
|
|
69
80
|
if (!this.config.auth.passwordHashField) {
|
|
70
81
|
throw new Error('No config.auth.passwordHashField defined');
|
|
71
82
|
}
|
|
83
|
+
if (!this.config.auth.usernameField) {
|
|
84
|
+
throw new Error('No config.auth.usernameField defined');
|
|
85
|
+
}
|
|
86
|
+
if (this.config.auth.loginBackgroundImage) {
|
|
87
|
+
errors.push(...this.checkCustomFileExists(this.config.auth.loginBackgroundImage));
|
|
88
|
+
}
|
|
72
89
|
const userResource = this.config.resources.find((res) => res.resourceId === this.config.auth.resourceId);
|
|
73
90
|
if (!userResource) {
|
|
74
91
|
throw new Error(`Resource with id "${this.config.auth.resourceId}" not found`);
|
|
@@ -80,7 +97,6 @@ class AdminForth {
|
|
|
80
97
|
if (!this.config.customization.customComponentsDir) {
|
|
81
98
|
this.config.customization.customComponentsDir = './custom';
|
|
82
99
|
}
|
|
83
|
-
const errors = [];
|
|
84
100
|
if (!this.config.baseUrl) {
|
|
85
101
|
this.config.baseUrl = '';
|
|
86
102
|
}
|
|
@@ -88,10 +104,7 @@ class AdminForth {
|
|
|
88
104
|
this.config.customization.brandName = 'AdminForth';
|
|
89
105
|
}
|
|
90
106
|
if (this.config.customization.brandLogo) {
|
|
91
|
-
|
|
92
|
-
errors.push(`Brand logo must start with @@ and be placed in custom directory`);
|
|
93
|
-
}
|
|
94
|
-
// todo check file exist
|
|
107
|
+
errors.push(...this.checkCustomFileExists(this.config.customization.brandLogo));
|
|
95
108
|
}
|
|
96
109
|
if (!this.config.customization.datesFormat) {
|
|
97
110
|
this.config.customization.datesFormat = 'MMM D, YYYY HH:mm:ss';
|
|
@@ -356,6 +369,16 @@ class AdminForth {
|
|
|
356
369
|
throw new Error('No config.auth defined');
|
|
357
370
|
}
|
|
358
371
|
const userResource = this.config.resources.find((res) => res.resourceId === this.config.auth.resourceId);
|
|
372
|
+
// if there is no passwordHashField, in columns, add it, with backendOnly and showIn: []
|
|
373
|
+
if (!userResource.dataSourceColumns.find((col) => col.name === this.config.auth.passwordHashField)) {
|
|
374
|
+
userResource.dataSourceColumns.push({
|
|
375
|
+
name: this.config.auth.passwordHashField,
|
|
376
|
+
backendOnly: true,
|
|
377
|
+
showIn: [],
|
|
378
|
+
type: _a.Types.STRING,
|
|
379
|
+
});
|
|
380
|
+
console.log('Adding passwordHashField to userResource', userResource);
|
|
381
|
+
}
|
|
359
382
|
const userRecord = (_c = (yield this.connectors[userResource.dataSource].getData({
|
|
360
383
|
resource: userResource,
|
|
361
384
|
filters: [
|
|
@@ -680,12 +703,12 @@ class AdminForth {
|
|
|
680
703
|
const item = yield this.connectors[resource.dataSource].getMinMaxForColumns({
|
|
681
704
|
resource,
|
|
682
705
|
columns: resource.columns.filter((col) => [
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
706
|
+
AdminForthDataTypes.INTEGER,
|
|
707
|
+
AdminForthDataTypes.FLOAT,
|
|
708
|
+
AdminForthDataTypes.DATE,
|
|
709
|
+
AdminForthDataTypes.DATETIME,
|
|
710
|
+
AdminForthDataTypes.TIME,
|
|
711
|
+
AdminForthDataTypes.DECIMAL,
|
|
689
712
|
].includes(col.type) && col.allowMinMaxQuery === true),
|
|
690
713
|
});
|
|
691
714
|
return item;
|
|
@@ -883,7 +906,7 @@ class AdminForth {
|
|
|
883
906
|
}
|
|
884
907
|
}
|
|
885
908
|
_a = AdminForth, _AdminForth_defaultConfig = new WeakMap();
|
|
886
|
-
AdminForth.Types =
|
|
909
|
+
AdminForth.Types = AdminForthDataTypes;
|
|
887
910
|
AdminForth.Utils = {
|
|
888
911
|
generatePasswordHash: (password) => __awaiter(void 0, void 0, void 0, function* () {
|
|
889
912
|
return yield Auth.generatePasswordHash(password);
|
|
@@ -29,10 +29,29 @@ function hashify(obj) {
|
|
|
29
29
|
return crypto.createHash('sha256').update(JSON.stringify(obj)).digest('hex');
|
|
30
30
|
}
|
|
31
31
|
class CodeInjector {
|
|
32
|
+
cleanup() {
|
|
33
|
+
console.log('Cleaning up...');
|
|
34
|
+
this.allWatchers.forEach((watcher) => {
|
|
35
|
+
watcher.removeAll();
|
|
36
|
+
});
|
|
37
|
+
}
|
|
32
38
|
constructor(adminforth) {
|
|
39
|
+
this.allWatchers = [];
|
|
33
40
|
this.allComponentNames = {};
|
|
34
41
|
this.srcFoldersToSync = {};
|
|
35
42
|
this.adminforth = adminforth;
|
|
43
|
+
process.on('SIGINT', () => {
|
|
44
|
+
console.log('Received SIGINT.');
|
|
45
|
+
this.cleanup();
|
|
46
|
+
});
|
|
47
|
+
process.on('SIGTERM', () => {
|
|
48
|
+
console.log('Received SIGTERM.');
|
|
49
|
+
this.cleanup();
|
|
50
|
+
});
|
|
51
|
+
process.on('exit', () => {
|
|
52
|
+
console.log('Exiting.');
|
|
53
|
+
this.cleanup();
|
|
54
|
+
});
|
|
36
55
|
}
|
|
37
56
|
// async runShell({command, verbose = false}) {
|
|
38
57
|
// console.log(`⚙️ Running shell ${command}...`);
|
|
@@ -154,6 +173,13 @@ class CodeInjector {
|
|
|
154
173
|
if (process.env.HEAVY_DEBUG) {
|
|
155
174
|
console.log(`🪲 await fsExtra.copy from ${path.join(ADMIN_FORTH_ABSOLUTE_PATH, 'spa')}, ${CodeInjector.SPA_TMP_PATH}`);
|
|
156
175
|
}
|
|
176
|
+
// try to rm SPA_TMP_PATH/src/types directory
|
|
177
|
+
try {
|
|
178
|
+
yield fs.promises.rm(path.join(CodeInjector.SPA_TMP_PATH, 'src', 'types'), { recursive: true });
|
|
179
|
+
}
|
|
180
|
+
catch (e) {
|
|
181
|
+
// ignore
|
|
182
|
+
}
|
|
157
183
|
yield fsExtra.copy(path.join(ADMIN_FORTH_ABSOLUTE_PATH, 'spa'), CodeInjector.SPA_TMP_PATH, {
|
|
158
184
|
filter: (src) => {
|
|
159
185
|
if (process.env.HEAVY_DEBUG) {
|
|
@@ -162,6 +188,7 @@ class CodeInjector {
|
|
|
162
188
|
return !src.includes('/adminforth/spa/node_modules') && !src.includes('/adminforth/spa/dist');
|
|
163
189
|
},
|
|
164
190
|
overwrite: true,
|
|
191
|
+
dereference: true, // needed to dereference types
|
|
165
192
|
});
|
|
166
193
|
// copy whole custom directory
|
|
167
194
|
if ((_b = this.adminforth.config.customization) === null || _b === void 0 ? void 0 : _b.customComponentsDir) {
|
|
@@ -358,9 +385,7 @@ class CodeInjector {
|
|
|
358
385
|
console.log(`File ${file} changed, preparing sources...`);
|
|
359
386
|
yield this.prepareSources({ filesUpdated: [file.replace(spaPath + '/', '')] });
|
|
360
387
|
}));
|
|
361
|
-
|
|
362
|
-
watcher.removeAll();
|
|
363
|
-
});
|
|
388
|
+
this.allWatchers.push(watcher);
|
|
364
389
|
});
|
|
365
390
|
}
|
|
366
391
|
watchCustomComponentsForCopy(_a) {
|
|
@@ -405,9 +430,7 @@ class CodeInjector {
|
|
|
405
430
|
recursive: true,
|
|
406
431
|
});
|
|
407
432
|
}));
|
|
408
|
-
|
|
409
|
-
watcher.removeAll();
|
|
410
|
-
});
|
|
433
|
+
this.allWatchers.push(watcher);
|
|
411
434
|
});
|
|
412
435
|
}
|
|
413
436
|
bundleNow(_a) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { onMounted, ref, resolveComponent } from 'vue';
|
|
2
2
|
|
|
3
3
|
import router from "./router";
|
|
4
|
+
import { useCoreStore } from './stores/core';
|
|
4
5
|
|
|
5
6
|
export async function callApi({path, method, body=undefined} ) {
|
|
6
7
|
const options = {
|
|
@@ -62,3 +63,13 @@ export const loadFile = (file: string) => {
|
|
|
62
63
|
}
|
|
63
64
|
return baseUrl;
|
|
64
65
|
}
|
|
66
|
+
|
|
67
|
+
// export function checkEmptyValues(value: any, viewType:'show' | 'list' | 'create' | 'edit') {
|
|
68
|
+
// const config: = useCoreStore().config;
|
|
69
|
+
// const emptyFieldPlaceholder = config.emptyFieldPlaceholder?.[viewType] || '---';
|
|
70
|
+
|
|
71
|
+
// if (value === null || value === undefined || value === '') {
|
|
72
|
+
// return emptyFieldPlaceholder;
|
|
73
|
+
// }
|
|
74
|
+
// return value;
|
|
75
|
+
// }
|
|
@@ -7,6 +7,10 @@
|
|
|
7
7
|
'background-blend-mode': 'darken'
|
|
8
8
|
}: {}"
|
|
9
9
|
>
|
|
10
|
+
|
|
11
|
+
{{ coreStore.config?.brandLogo }}
|
|
12
|
+
<img
|
|
13
|
+
:src="loadFile('@@/logo2.png')" class="w-40 h-40" alt="logo" />
|
|
10
14
|
<!-- Main modal -->
|
|
11
15
|
<div id="authentication-modal" tabindex="-1" class=" overflow-y-auto overflow-x-hidden z-50 min-w-[400px] justify-center items-center md:inset-0 h-[calc(100%-1rem)] max-h-full">
|
|
12
16
|
<div class="relative p-4 w-full max-w-md max-h-full">
|
|
@@ -114,6 +118,7 @@ async function login() {
|
|
|
114
118
|
} else {
|
|
115
119
|
error.value = null;
|
|
116
120
|
router.push('/');
|
|
121
|
+
await router.isReady();
|
|
117
122
|
await coreStore.fetchMenuAndResource();
|
|
118
123
|
setTimeout(() => {
|
|
119
124
|
initFlowbite();
|
|
@@ -1 +1,33 @@
|
|
|
1
|
-
export
|
|
1
|
+
export var AdminForthDataTypes;
|
|
2
|
+
(function (AdminForthDataTypes) {
|
|
3
|
+
AdminForthDataTypes["STRING"] = "string";
|
|
4
|
+
AdminForthDataTypes["INTEGER"] = "integer";
|
|
5
|
+
AdminForthDataTypes["FLOAT"] = "float";
|
|
6
|
+
AdminForthDataTypes["DECIMAL"] = "decimal";
|
|
7
|
+
AdminForthDataTypes["BOOLEAN"] = "boolean";
|
|
8
|
+
AdminForthDataTypes["DATE"] = "date";
|
|
9
|
+
AdminForthDataTypes["DATETIME"] = "datetime";
|
|
10
|
+
AdminForthDataTypes["TIME"] = "time";
|
|
11
|
+
AdminForthDataTypes["TEXT"] = "text";
|
|
12
|
+
AdminForthDataTypes["JSON"] = "json";
|
|
13
|
+
})(AdminForthDataTypes || (AdminForthDataTypes = {}));
|
|
14
|
+
export var AdminForthFilterOperators;
|
|
15
|
+
(function (AdminForthFilterOperators) {
|
|
16
|
+
AdminForthFilterOperators["EQ"] = "eq";
|
|
17
|
+
AdminForthFilterOperators["NE"] = "ne";
|
|
18
|
+
AdminForthFilterOperators["GT"] = "gt";
|
|
19
|
+
AdminForthFilterOperators["LT"] = "lt";
|
|
20
|
+
AdminForthFilterOperators["GTE"] = "gte";
|
|
21
|
+
AdminForthFilterOperators["LTE"] = "lte";
|
|
22
|
+
AdminForthFilterOperators["LIKE"] = "like";
|
|
23
|
+
AdminForthFilterOperators["ILIKE"] = "ilike";
|
|
24
|
+
AdminForthFilterOperators["IN"] = "in";
|
|
25
|
+
AdminForthFilterOperators["NIN"] = "nin";
|
|
26
|
+
})(AdminForthFilterOperators || (AdminForthFilterOperators = {}));
|
|
27
|
+
;
|
|
28
|
+
export var AdminForthSortDirections;
|
|
29
|
+
(function (AdminForthSortDirections) {
|
|
30
|
+
AdminForthSortDirections["ASC"] = "asc";
|
|
31
|
+
AdminForthSortDirections["DESC"] = "desc";
|
|
32
|
+
})(AdminForthSortDirections || (AdminForthSortDirections = {}));
|
|
33
|
+
;
|
package/index.ts
CHANGED
|
@@ -9,9 +9,9 @@ import ExpressServer from './servers/express.js';
|
|
|
9
9
|
import {v1 as uuid} from 'uuid';
|
|
10
10
|
import fs from 'fs';
|
|
11
11
|
import { ADMINFORTH_VERSION } from './modules/utils.js';
|
|
12
|
-
import {
|
|
13
|
-
import { AdminForthConfig } from './types/AdminForthConfig.js';
|
|
12
|
+
import { AdminForthConfig, AdminForthClass, AdminForthFilterOperators, AdminForthDataTypes } from './types/AdminForthConfig.js';
|
|
14
13
|
import { getFunctionList } from './modules/utils.js';
|
|
14
|
+
import path from 'path';
|
|
15
15
|
|
|
16
16
|
const AVAILABLE_SHOW_IN = ['list', 'edit', 'create', 'filter', 'show'];
|
|
17
17
|
const DEFAULT_ALLOWED_ACTIONS = {create: true, edit: true, show: true, delete: true};
|
|
@@ -22,9 +22,8 @@ type ValidationObject = {
|
|
|
22
22
|
message: string,
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
class AdminForth {
|
|
26
|
-
static Types =
|
|
27
|
-
|
|
25
|
+
class AdminForth implements AdminForthClass {
|
|
26
|
+
static Types = AdminForthDataTypes;
|
|
28
27
|
|
|
29
28
|
static Utils = {
|
|
30
29
|
generatePasswordHash: async (password) => {
|
|
@@ -48,7 +47,6 @@ class AdminForth {
|
|
|
48
47
|
dbDiscover?: 'running' | 'done',
|
|
49
48
|
}
|
|
50
49
|
|
|
51
|
-
|
|
52
50
|
constructor(config: AdminForthConfig) {
|
|
53
51
|
this.config = {...this.#defaultConfig,...config};
|
|
54
52
|
this.codeInjector = new CodeInjector(this);
|
|
@@ -72,7 +70,19 @@ class AdminForth {
|
|
|
72
70
|
};
|
|
73
71
|
}
|
|
74
72
|
|
|
73
|
+
checkCustomFileExists(filePath: string): Array<string> {
|
|
74
|
+
if (filePath.startsWith('@@/')) {
|
|
75
|
+
const checkPath = path.join(this.config.customization.customComponentsDir, filePath.replace('@@/', ''));
|
|
76
|
+
if (!fs.existsSync(checkPath)) {
|
|
77
|
+
return [`File file ${filePath} does not exist in ${this.config.customization.customComponentsDir}`];
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
|
|
75
83
|
validateConfig() {
|
|
84
|
+
const errors = [];
|
|
85
|
+
|
|
76
86
|
if (this.config.rootUser) {
|
|
77
87
|
if (!this.config.rootUser.username) {
|
|
78
88
|
throw new Error('rootUser.username is required');
|
|
@@ -91,6 +101,12 @@ class AdminForth {
|
|
|
91
101
|
if (!this.config.auth.passwordHashField) {
|
|
92
102
|
throw new Error('No config.auth.passwordHashField defined');
|
|
93
103
|
}
|
|
104
|
+
if (!this.config.auth.usernameField) {
|
|
105
|
+
throw new Error('No config.auth.usernameField defined');
|
|
106
|
+
}
|
|
107
|
+
if (this.config.auth.loginBackgroundImage) {
|
|
108
|
+
errors.push(...this.checkCustomFileExists(this.config.auth.loginBackgroundImage));
|
|
109
|
+
}
|
|
94
110
|
const userResource = this.config.resources.find((res) => res.resourceId === this.config.auth.resourceId);
|
|
95
111
|
if (!userResource) {
|
|
96
112
|
throw new Error(`Resource with id "${this.config.auth.resourceId}" not found`);
|
|
@@ -106,7 +122,6 @@ class AdminForth {
|
|
|
106
122
|
}
|
|
107
123
|
|
|
108
124
|
|
|
109
|
-
const errors = [];
|
|
110
125
|
if (!this.config.baseUrl) {
|
|
111
126
|
this.config.baseUrl = '';
|
|
112
127
|
}
|
|
@@ -114,10 +129,7 @@ class AdminForth {
|
|
|
114
129
|
this.config.customization.brandName = 'AdminForth';
|
|
115
130
|
}
|
|
116
131
|
if (this.config.customization.brandLogo) {
|
|
117
|
-
|
|
118
|
-
errors.push(`Brand logo must start with @@ and be placed in custom directory`);
|
|
119
|
-
}
|
|
120
|
-
// todo check file exist
|
|
132
|
+
errors.push(...this.checkCustomFileExists(this.config.customization.brandLogo));
|
|
121
133
|
}
|
|
122
134
|
|
|
123
135
|
|
|
@@ -422,6 +434,17 @@ class AdminForth {
|
|
|
422
434
|
throw new Error('No config.auth defined');
|
|
423
435
|
}
|
|
424
436
|
const userResource = this.config.resources.find((res) => res.resourceId === this.config.auth.resourceId);
|
|
437
|
+
// if there is no passwordHashField, in columns, add it, with backendOnly and showIn: []
|
|
438
|
+
if (!userResource.dataSourceColumns.find((col) => col.name === this.config.auth.passwordHashField)) {
|
|
439
|
+
userResource.dataSourceColumns.push({
|
|
440
|
+
name: this.config.auth.passwordHashField,
|
|
441
|
+
backendOnly: true,
|
|
442
|
+
showIn: [],
|
|
443
|
+
type: AdminForth.Types.STRING,
|
|
444
|
+
});
|
|
445
|
+
console.log('Adding passwordHashField to userResource', userResource)
|
|
446
|
+
}
|
|
447
|
+
|
|
425
448
|
const userRecord = (
|
|
426
449
|
await this.connectors[userResource.dataSource].getData({
|
|
427
450
|
resource: userResource,
|
|
@@ -775,12 +798,12 @@ class AdminForth {
|
|
|
775
798
|
const item = await this.connectors[resource.dataSource].getMinMaxForColumns({
|
|
776
799
|
resource,
|
|
777
800
|
columns: resource.columns.filter((col) => [
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
801
|
+
AdminForthDataTypes.INTEGER,
|
|
802
|
+
AdminForthDataTypes.FLOAT,
|
|
803
|
+
AdminForthDataTypes.DATE,
|
|
804
|
+
AdminForthDataTypes.DATETIME,
|
|
805
|
+
AdminForthDataTypes.TIME,
|
|
806
|
+
AdminForthDataTypes.DECIMAL,
|
|
784
807
|
].includes(col.type) && col.allowMinMaxQuery === true),
|
|
785
808
|
});
|
|
786
809
|
return item;
|
package/modules/codeInjector.ts
CHANGED
|
@@ -11,6 +11,7 @@ import { ADMIN_FORTH_ABSOLUTE_PATH } from './utils.js';
|
|
|
11
11
|
import { getComponentNameFromPath } from './utils.js';
|
|
12
12
|
|
|
13
13
|
|
|
14
|
+
|
|
14
15
|
let TMP_DIR;
|
|
15
16
|
|
|
16
17
|
try {
|
|
@@ -29,14 +30,36 @@ function hashify(obj) {
|
|
|
29
30
|
|
|
30
31
|
class CodeInjector {
|
|
31
32
|
|
|
33
|
+
allWatchers = [];
|
|
32
34
|
adminforth: AdminForth;
|
|
33
35
|
allComponentNames: { [key: string]: string } = {};
|
|
34
36
|
srcFoldersToSync: { [key: string]: string } = {};
|
|
35
37
|
|
|
36
38
|
static SPA_TMP_PATH = path.join(TMP_DIR, 'adminforth', 'spa_tmp');
|
|
37
39
|
|
|
40
|
+
cleanup() {
|
|
41
|
+
console.log('Cleaning up...');
|
|
42
|
+
this.allWatchers.forEach((watcher) => {
|
|
43
|
+
watcher.removeAll();
|
|
44
|
+
});
|
|
45
|
+
}
|
|
38
46
|
constructor(adminforth) {
|
|
39
47
|
this.adminforth = adminforth;
|
|
48
|
+
|
|
49
|
+
process.on('SIGINT', () => {
|
|
50
|
+
console.log('Received SIGINT.');
|
|
51
|
+
this.cleanup();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
process.on('SIGTERM', () => {
|
|
55
|
+
console.log('Received SIGTERM.');
|
|
56
|
+
this.cleanup();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
process.on('exit', () => {
|
|
60
|
+
console.log('Exiting.');
|
|
61
|
+
this.cleanup();
|
|
62
|
+
});
|
|
40
63
|
}
|
|
41
64
|
|
|
42
65
|
// async runShell({command, verbose = false}) {
|
|
@@ -164,6 +187,13 @@ class CodeInjector {
|
|
|
164
187
|
console.log(`🪲 await fsExtra.copy from ${path.join(ADMIN_FORTH_ABSOLUTE_PATH, 'spa')}, ${CodeInjector.SPA_TMP_PATH}`);
|
|
165
188
|
}
|
|
166
189
|
|
|
190
|
+
// try to rm SPA_TMP_PATH/src/types directory
|
|
191
|
+
try {
|
|
192
|
+
await fs.promises.rm(path.join(CodeInjector.SPA_TMP_PATH, 'src', 'types'), { recursive: true });
|
|
193
|
+
} catch (e) {
|
|
194
|
+
// ignore
|
|
195
|
+
}
|
|
196
|
+
|
|
167
197
|
await fsExtra.copy(path.join(ADMIN_FORTH_ABSOLUTE_PATH, 'spa'), CodeInjector.SPA_TMP_PATH, {
|
|
168
198
|
filter: (src) => {
|
|
169
199
|
if (process.env.HEAVY_DEBUG) {
|
|
@@ -173,6 +203,7 @@ class CodeInjector {
|
|
|
173
203
|
return !src.includes('/adminforth/spa/node_modules') && !src.includes('/adminforth/spa/dist');
|
|
174
204
|
},
|
|
175
205
|
overwrite: true,
|
|
206
|
+
dereference: true, // needed to dereference types
|
|
176
207
|
});
|
|
177
208
|
|
|
178
209
|
// copy whole custom directory
|
|
@@ -416,9 +447,7 @@ async watchForReprepare({ verbose }) {
|
|
|
416
447
|
await this.prepareSources({ filesUpdated: [file.replace(spaPath + '/', '')] });
|
|
417
448
|
}
|
|
418
449
|
)
|
|
419
|
-
|
|
420
|
-
watcher.removeAll();
|
|
421
|
-
});
|
|
450
|
+
this.allWatchers.push(watcher);
|
|
422
451
|
}
|
|
423
452
|
|
|
424
453
|
async watchCustomComponentsForCopy({ verbose }) {
|
|
@@ -471,9 +500,7 @@ async watchForReprepare({ verbose }) {
|
|
|
471
500
|
});
|
|
472
501
|
}
|
|
473
502
|
)
|
|
474
|
-
|
|
475
|
-
watcher.removeAll();
|
|
476
|
-
});
|
|
503
|
+
this.allWatchers.push(watcher);
|
|
477
504
|
}
|
|
478
505
|
|
|
479
506
|
async bundleNow({hotReload = false, verbose = false}: {hotReload: boolean, verbose: boolean}) {
|
package/package.json
CHANGED
package/plugins/base.ts
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import { AdminForthResource } from '../types/AdminForthConfig.js';
|
|
2
|
-
import AdminForth from '../index.js';
|
|
1
|
+
import { AdminForthResource, AdminForthPluginType, AdminForthClass } from '../types/AdminForthConfig.js';
|
|
3
2
|
import { getComponentNameFromPath } from '../modules/utils.js';
|
|
4
3
|
import { currentFileDir } from '../modules/utils.js';
|
|
5
4
|
import path from 'path';
|
|
6
5
|
import fs from 'fs';
|
|
7
6
|
|
|
8
|
-
export default class AdminForthPlugin {
|
|
7
|
+
export default class AdminForthPlugin implements AdminForthPluginType {
|
|
9
8
|
|
|
10
|
-
adminforth:
|
|
9
|
+
adminforth: AdminForthClass;
|
|
11
10
|
pluginDir: string;
|
|
12
11
|
customFolderName: string = 'custom';
|
|
13
12
|
|
|
@@ -16,7 +15,7 @@ export default class AdminForthPlugin {
|
|
|
16
15
|
this.pluginDir = currentFileDir(metaUrl);
|
|
17
16
|
}
|
|
18
17
|
|
|
19
|
-
modifyResourceConfig(adminforth:
|
|
18
|
+
modifyResourceConfig(adminforth: AdminForthClass, resourceConfig: AdminForthResource) {
|
|
20
19
|
this.adminforth = adminforth;
|
|
21
20
|
}
|
|
22
21
|
|
package/spa/src/utils.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { onMounted, ref, resolveComponent } from 'vue';
|
|
2
2
|
|
|
3
3
|
import router from "./router";
|
|
4
|
+
import { useCoreStore } from './stores/core';
|
|
4
5
|
|
|
5
6
|
export async function callApi({path, method, body=undefined} ) {
|
|
6
7
|
const options = {
|
|
@@ -62,3 +63,13 @@ export const loadFile = (file: string) => {
|
|
|
62
63
|
}
|
|
63
64
|
return baseUrl;
|
|
64
65
|
}
|
|
66
|
+
|
|
67
|
+
// export function checkEmptyValues(value: any, viewType:'show' | 'list' | 'create' | 'edit') {
|
|
68
|
+
// const config: = useCoreStore().config;
|
|
69
|
+
// const emptyFieldPlaceholder = config.emptyFieldPlaceholder?.[viewType] || '---';
|
|
70
|
+
|
|
71
|
+
// if (value === null || value === undefined || value === '') {
|
|
72
|
+
// return emptyFieldPlaceholder;
|
|
73
|
+
// }
|
|
74
|
+
// return value;
|
|
75
|
+
// }
|
|
@@ -7,6 +7,10 @@
|
|
|
7
7
|
'background-blend-mode': 'darken'
|
|
8
8
|
}: {}"
|
|
9
9
|
>
|
|
10
|
+
|
|
11
|
+
{{ coreStore.config?.brandLogo }}
|
|
12
|
+
<img
|
|
13
|
+
:src="loadFile('@@/logo2.png')" class="w-40 h-40" alt="logo" />
|
|
10
14
|
<!-- Main modal -->
|
|
11
15
|
<div id="authentication-modal" tabindex="-1" class=" overflow-y-auto overflow-x-hidden z-50 min-w-[400px] justify-center items-center md:inset-0 h-[calc(100%-1rem)] max-h-full">
|
|
12
16
|
<div class="relative p-4 w-full max-w-md max-h-full">
|
|
@@ -114,6 +118,7 @@ async function login() {
|
|
|
114
118
|
} else {
|
|
115
119
|
error.value = null;
|
|
116
120
|
router.push('/');
|
|
121
|
+
await router.isReady();
|
|
117
122
|
await coreStore.fetchMenuAndResource();
|
|
118
123
|
setTimeout(() => {
|
|
119
124
|
initFlowbite();
|
|
@@ -1,4 +1,23 @@
|
|
|
1
|
-
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
export interface CodeInjector {
|
|
4
|
+
srcFoldersToSync: Object;
|
|
5
|
+
allComponentNames: Object;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface AdminForthClass {
|
|
9
|
+
config: AdminForthConfig;
|
|
10
|
+
codeInjector: CodeInjector;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
export interface AdminForthPluginType {
|
|
15
|
+
adminforth: AdminForthClass;
|
|
16
|
+
pluginDir: string;
|
|
17
|
+
customFolderName: string;
|
|
18
|
+
modifyResourceConfig(adminforth: AdminForthClass, resourceConfig: AdminForthResource): void;
|
|
19
|
+
componentPath(componentFile: string): string;
|
|
20
|
+
}
|
|
2
21
|
|
|
3
22
|
export type AdminForthConfigMenuItem = {
|
|
4
23
|
type?: 'heading' | 'group' | 'resource' | 'page' | 'gap' | 'divider',
|
|
@@ -20,7 +39,7 @@ export type AdminForthConfigMenuItem = {
|
|
|
20
39
|
export type AdminForthResourceColumn = {
|
|
21
40
|
name: string,
|
|
22
41
|
label?: string,
|
|
23
|
-
type?:
|
|
42
|
+
type?: AdminForthDataTypes,
|
|
24
43
|
primaryKey?: boolean,
|
|
25
44
|
required?: boolean | { create: boolean, edit: boolean },
|
|
26
45
|
editingNote?: string | { create: string, edit: string },
|
|
@@ -48,8 +67,9 @@ export type AdminForthResource = {
|
|
|
48
67
|
table: string,
|
|
49
68
|
dataSource: string,
|
|
50
69
|
columns: Array<AdminForthResourceColumn>,
|
|
70
|
+
dataSourceColumns?: Array<AdminForthResourceColumn>,
|
|
51
71
|
itemLabel?: Function,
|
|
52
|
-
plugins?: Array<
|
|
72
|
+
plugins?: Array<AdminForthPluginType>,
|
|
53
73
|
hooks?: {
|
|
54
74
|
show?: {
|
|
55
75
|
beforeDatasourceRequest?: Function | Array<Function>,
|
|
@@ -153,18 +173,37 @@ export type AdminForthResourceColumnComponent = {
|
|
|
153
173
|
list?: string,
|
|
154
174
|
}
|
|
155
175
|
|
|
176
|
+
export enum AdminForthDataTypes {
|
|
177
|
+
STRING = 'string',
|
|
178
|
+
INTEGER = 'integer',
|
|
179
|
+
FLOAT = 'float',
|
|
180
|
+
DECIMAL = 'decimal',
|
|
181
|
+
BOOLEAN = 'boolean',
|
|
182
|
+
DATE = 'date',
|
|
183
|
+
DATETIME = 'datetime',
|
|
184
|
+
TIME = 'time',
|
|
185
|
+
TEXT = 'text',
|
|
186
|
+
JSON = 'json',
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export enum AdminForthFilterOperators {
|
|
190
|
+
EQ = 'eq',
|
|
191
|
+
NE = 'ne',
|
|
192
|
+
GT = 'gt',
|
|
193
|
+
LT = 'lt',
|
|
194
|
+
GTE = 'gte',
|
|
195
|
+
LTE = 'lte',
|
|
196
|
+
LIKE = 'like',
|
|
197
|
+
ILIKE = 'ilike',
|
|
198
|
+
IN = 'in',
|
|
199
|
+
NIN = 'nin',
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
export enum AdminForthSortDirections {
|
|
203
|
+
ASC = 'asc',
|
|
204
|
+
DESC = 'desc',
|
|
205
|
+
};
|
|
156
206
|
|
|
157
|
-
export type AdminForthTypesValues =
|
|
158
|
-
| 'string'
|
|
159
|
-
| 'integer'
|
|
160
|
-
| 'float'
|
|
161
|
-
| 'decimal'
|
|
162
|
-
| 'boolean'
|
|
163
|
-
| 'date'
|
|
164
|
-
| 'datetime'
|
|
165
|
-
| 'time'
|
|
166
|
-
| 'text'
|
|
167
|
-
| 'json';
|
|
168
207
|
|
|
169
208
|
export type AdminForthResourceColumnEnumElement = {
|
|
170
209
|
value: string | null,
|
package/types.ts
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
// typical types which AdminForth supports
|
|
4
|
-
//
|
|
5
|
-
export const AdminForthTypes = {
|
|
6
|
-
STRING: 'string',
|
|
7
|
-
INTEGER: 'integer',
|
|
8
|
-
FLOAT: 'float',
|
|
9
|
-
DECIMAL: 'decimal',
|
|
10
|
-
BOOLEAN: 'boolean',
|
|
11
|
-
DATE: 'date',
|
|
12
|
-
DATETIME: 'datetime',
|
|
13
|
-
TIME: 'time',
|
|
14
|
-
TEXT: 'text',
|
|
15
|
-
JSON: 'json',
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export type AdminForthTypesValues = keyof typeof AdminForthTypes;
|
|
19
|
-
|
|
20
|
-
export const AdminForthFilterOperators = {
|
|
21
|
-
EQ: 'eq',
|
|
22
|
-
NE: 'ne',
|
|
23
|
-
GT: 'gt',
|
|
24
|
-
LT: 'lt',
|
|
25
|
-
GTE: 'gte',
|
|
26
|
-
LTE: 'lte',
|
|
27
|
-
LIKE: 'like',
|
|
28
|
-
ILIKE: 'ilike',
|
|
29
|
-
IN: 'in',
|
|
30
|
-
NIN: 'nin',
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
export const AdminForthSortDirections = {
|
|
34
|
-
ASC: 'asc',
|
|
35
|
-
DESC: 'desc',
|
|
36
|
-
};
|