directus 9.5.1 → 9.7.0
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/dist/app.js +3 -1
- package/dist/auth/drivers/ldap.d.ts +0 -1
- package/dist/auth/drivers/ldap.js +54 -60
- package/dist/auth/drivers/oauth2.js +3 -0
- package/dist/auth/drivers/openid.js +9 -6
- package/dist/cache.d.ts +4 -1
- package/dist/cache.js +27 -5
- package/dist/cli/commands/init/index.js +8 -0
- package/dist/cli/commands/schema/apply.d.ts +1 -0
- package/dist/cli/commands/schema/apply.js +9 -5
- package/dist/cli/index.js +1 -0
- package/dist/cli/utils/create-env/env-stub.liquid +1 -0
- package/dist/controllers/assets.js +9 -1
- package/dist/controllers/utils.js +18 -1
- package/dist/database/migrations/20220303A-remove-default-project-color.d.ts +3 -0
- package/dist/database/migrations/20220303A-remove-default-project-color.js +22 -0
- package/dist/database/run-ast.d.ts +1 -1
- package/dist/database/run-ast.js +48 -35
- package/dist/database/system-data/app-access-permissions/app-access-permissions.yaml +8 -2
- package/dist/database/system-data/fields/collections.yaml +2 -0
- package/dist/database/system-data/fields/settings.yaml +19 -3
- package/dist/env.js +8 -2
- package/dist/exceptions/database/dialects/mysql.js +23 -17
- package/dist/extensions.js +0 -2
- package/dist/middleware/authenticate.d.ts +5 -3
- package/dist/middleware/authenticate.js +22 -39
- package/dist/middleware/respond.js +7 -28
- package/dist/server.js +5 -2
- package/dist/services/authorization.js +2 -1
- package/dist/services/collections.js +9 -7
- package/dist/services/fields.js +8 -8
- package/dist/services/files.js +69 -3
- package/dist/services/graphql.js +2 -2
- package/dist/services/import-export.d.ts +34 -0
- package/dist/services/import-export.js +270 -0
- package/dist/services/index.d.ts +2 -1
- package/dist/services/index.js +2 -1
- package/dist/services/mail/templates/base.liquid +2 -2
- package/dist/services/permissions.js +10 -10
- package/dist/services/relations.js +11 -4
- package/dist/services/utils.js +10 -0
- package/dist/utils/apply-query.js +2 -24
- package/dist/utils/get-date-formatted.d.ts +1 -0
- package/dist/utils/get-date-formatted.js +14 -0
- package/dist/utils/get-permissions.js +1 -1
- package/dist/utils/get-schema.js +1 -1
- package/dist/utils/is-directus-jwt.js +4 -21
- package/dist/utils/jwt.d.ts +2 -0
- package/dist/utils/jwt.js +49 -0
- package/dist/utils/merge-permissions.js +18 -6
- package/example.env +1 -0
- package/package.json +19 -20
- package/dist/__mocks__/cache.d.ts +0 -6
- package/dist/__mocks__/cache.js +0 -8
- package/dist/services/import.d.ts +0 -13
- package/dist/services/import.js +0 -118
|
@@ -135,6 +135,10 @@ class RelationsService {
|
|
|
135
135
|
if (relation.field in this.schema.collections[relation.collection].fields === false) {
|
|
136
136
|
throw new exceptions_1.InvalidPayloadException(`Field "${relation.field}" doesn't exist in collection "${relation.collection}"`);
|
|
137
137
|
}
|
|
138
|
+
// A primary key should not be a foreign key
|
|
139
|
+
if (this.schema.collections[relation.collection].primary === relation.field) {
|
|
140
|
+
throw new exceptions_1.InvalidPayloadException(`Field "${relation.field}" in collection "${relation.collection}" is a primary key`);
|
|
141
|
+
}
|
|
138
142
|
if (relation.related_collection && relation.related_collection in this.schema.collections === false) {
|
|
139
143
|
throw new exceptions_1.InvalidPayloadException(`Collection "${relation.related_collection}" doesn't exist`);
|
|
140
144
|
}
|
|
@@ -171,7 +175,7 @@ class RelationsService {
|
|
|
171
175
|
});
|
|
172
176
|
await relationsItemService.createOne(metaRow);
|
|
173
177
|
});
|
|
174
|
-
await
|
|
178
|
+
await (0, cache_1.clearSystemCache)();
|
|
175
179
|
}
|
|
176
180
|
/**
|
|
177
181
|
* Update an existing foreign key constraint
|
|
@@ -232,7 +236,7 @@ class RelationsService {
|
|
|
232
236
|
}
|
|
233
237
|
}
|
|
234
238
|
});
|
|
235
|
-
await
|
|
239
|
+
await (0, cache_1.clearSystemCache)();
|
|
236
240
|
}
|
|
237
241
|
/**
|
|
238
242
|
* Delete an existing relationship
|
|
@@ -253,7 +257,10 @@ class RelationsService {
|
|
|
253
257
|
}
|
|
254
258
|
await this.knex.transaction(async (trx) => {
|
|
255
259
|
var _a;
|
|
256
|
-
|
|
260
|
+
const existingConstraints = await this.schemaInspector.foreignKeys();
|
|
261
|
+
const constraintNames = existingConstraints.map((key) => key.constraint_name);
|
|
262
|
+
if (((_a = existingRelation.schema) === null || _a === void 0 ? void 0 : _a.constraint_name) &&
|
|
263
|
+
constraintNames.includes(existingRelation.schema.constraint_name)) {
|
|
257
264
|
await trx.schema.alterTable(existingRelation.collection, (table) => {
|
|
258
265
|
table.dropForeign(existingRelation.field, existingRelation.schema.constraint_name);
|
|
259
266
|
});
|
|
@@ -262,7 +269,7 @@ class RelationsService {
|
|
|
262
269
|
await trx('directus_relations').delete().where({ many_collection: collection, many_field: field });
|
|
263
270
|
}
|
|
264
271
|
});
|
|
265
|
-
await
|
|
272
|
+
await (0, cache_1.clearSystemCache)();
|
|
266
273
|
}
|
|
267
274
|
/**
|
|
268
275
|
* Whether or not the current user has read access to relations
|
package/dist/services/utils.js
CHANGED
|
@@ -7,6 +7,7 @@ exports.UtilsService = void 0;
|
|
|
7
7
|
const database_1 = __importDefault(require("../database"));
|
|
8
8
|
const collections_1 = require("../database/system-data/collections");
|
|
9
9
|
const exceptions_1 = require("../exceptions");
|
|
10
|
+
const emitter_1 = __importDefault(require("../emitter"));
|
|
10
11
|
class UtilsService {
|
|
11
12
|
constructor(options) {
|
|
12
13
|
this.knex = options.knex || (0, database_1.default)();
|
|
@@ -98,6 +99,15 @@ class UtilsService {
|
|
|
98
99
|
.andWhere(sortField, '<=', sourceSortValue)
|
|
99
100
|
.andWhereNot({ [primaryKeyField]: item });
|
|
100
101
|
}
|
|
102
|
+
emitter_1.default.emitAction(['items.sort', `${collection}.items.sort`], {
|
|
103
|
+
collection,
|
|
104
|
+
item,
|
|
105
|
+
to,
|
|
106
|
+
}, {
|
|
107
|
+
database: this.knex,
|
|
108
|
+
schema: this.schema,
|
|
109
|
+
accountability: this.accountability,
|
|
110
|
+
});
|
|
101
111
|
}
|
|
102
112
|
}
|
|
103
113
|
exports.UtilsService = UtilsService;
|
|
@@ -48,29 +48,7 @@ function applyQuery(knex, collection, dbQuery, query, schema, subQuery = false)
|
|
|
48
48
|
if (query.aggregate) {
|
|
49
49
|
applyAggregate(dbQuery, query.aggregate, collection);
|
|
50
50
|
}
|
|
51
|
-
if (query.
|
|
52
|
-
const [field, keys] = query.union;
|
|
53
|
-
const queries = keys.map((key) => {
|
|
54
|
-
const unionFilter = { [field]: { _eq: key } };
|
|
55
|
-
let filter = (0, lodash_1.cloneDeep)(query.filter);
|
|
56
|
-
if (filter) {
|
|
57
|
-
if ('_and' in filter) {
|
|
58
|
-
filter._and.push(unionFilter);
|
|
59
|
-
}
|
|
60
|
-
else {
|
|
61
|
-
filter = {
|
|
62
|
-
_and: [filter, unionFilter],
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
else {
|
|
67
|
-
filter = unionFilter;
|
|
68
|
-
}
|
|
69
|
-
return knex.select('*').from(applyFilter(knex, schema, dbQuery.clone(), filter, collection, subQuery).as('foo'));
|
|
70
|
-
});
|
|
71
|
-
dbQuery = knex.unionAll(queries);
|
|
72
|
-
}
|
|
73
|
-
else if (query.filter) {
|
|
51
|
+
if (query.filter) {
|
|
74
52
|
applyFilter(knex, schema, dbQuery, query.filter, collection, subQuery);
|
|
75
53
|
}
|
|
76
54
|
return dbQuery;
|
|
@@ -234,7 +212,7 @@ function applyFilter(knex, schema, rootQuery, rootFilter, collection, subQuery =
|
|
|
234
212
|
applyFilterToQuery(`${collection}.${filterPath[0]}`, filterOperator, filterValue, logical);
|
|
235
213
|
}
|
|
236
214
|
}
|
|
237
|
-
else if (subQuery === false) {
|
|
215
|
+
else if (subQuery === false || filterPath.length > 1) {
|
|
238
216
|
if (!relation)
|
|
239
217
|
continue;
|
|
240
218
|
let pkField = `${collection}.${schema.collections[relation.related_collection].primary}`;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function getDateFormatted(): string;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getDateFormatted = void 0;
|
|
4
|
+
function getDateFormatted() {
|
|
5
|
+
const date = new Date();
|
|
6
|
+
let month = String(date.getMonth() + 1);
|
|
7
|
+
if (month.length === 1)
|
|
8
|
+
month = '0' + month;
|
|
9
|
+
let day = String(date.getDate());
|
|
10
|
+
if (day.length === 1)
|
|
11
|
+
day = '0' + day;
|
|
12
|
+
return `${date.getFullYear()}${month}${day}-${date.getHours()}${date.getMinutes()}${date.getSeconds()}`;
|
|
13
|
+
}
|
|
14
|
+
exports.getDateFormatted = getDateFormatted;
|
|
@@ -65,7 +65,7 @@ async function getPermissions(accountability, schema) {
|
|
|
65
65
|
? await getFilterContext(schema, accountability, requiredPermissionData)
|
|
66
66
|
: {};
|
|
67
67
|
if (env_1.default.CACHE_PERMISSIONS !== false) {
|
|
68
|
-
await
|
|
68
|
+
await (0, cache_1.setSystemCache)(cacheKey, { permissions, containDynamicData });
|
|
69
69
|
if (containDynamicData && env_1.default.CACHE_ENABLED !== false) {
|
|
70
70
|
await (cache === null || cache === void 0 ? void 0 : cache.set(`filterContext-${(0, object_hash_1.default)({ user, role, permissions })}`, filterContext));
|
|
71
71
|
}
|
package/dist/utils/get-schema.js
CHANGED
|
@@ -35,7 +35,7 @@ async function getSchema(options) {
|
|
|
35
35
|
else {
|
|
36
36
|
result = await getDatabaseSchema(database, schemaInspector);
|
|
37
37
|
try {
|
|
38
|
-
await
|
|
38
|
+
await (0, cache_1.setSystemCache)('schema', result);
|
|
39
39
|
}
|
|
40
40
|
catch (err) {
|
|
41
41
|
logger_1.default.warn(err, `[schema-cache] Couldn't save cache. ${err}`);
|
|
@@ -3,37 +3,20 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const
|
|
7
|
-
const logger_1 = __importDefault(require("../logger"));
|
|
6
|
+
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
|
|
8
7
|
/**
|
|
9
8
|
* Check if a given string conforms to the structure of a JWT
|
|
10
9
|
* and whether it is issued by Directus.
|
|
11
10
|
*/
|
|
12
11
|
function isDirectusJWT(string) {
|
|
13
|
-
const parts = string.split('.');
|
|
14
|
-
// JWTs have the structure header.payload.signature
|
|
15
|
-
if (parts.length !== 3)
|
|
16
|
-
return false;
|
|
17
|
-
// Check if all segments are base64 encoded
|
|
18
|
-
try {
|
|
19
|
-
(0, atob_1.default)(parts[0]);
|
|
20
|
-
(0, atob_1.default)(parts[1]);
|
|
21
|
-
(0, atob_1.default)(parts[2]);
|
|
22
|
-
}
|
|
23
|
-
catch (err) {
|
|
24
|
-
logger_1.default.error(err);
|
|
25
|
-
return false;
|
|
26
|
-
}
|
|
27
|
-
// Check if the header and payload are valid JSON
|
|
28
12
|
try {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
if (payload.iss !== 'directus')
|
|
13
|
+
const payload = jsonwebtoken_1.default.decode(string, { json: true });
|
|
14
|
+
if ((payload === null || payload === void 0 ? void 0 : payload.iss) !== 'directus')
|
|
32
15
|
return false;
|
|
16
|
+
return true;
|
|
33
17
|
}
|
|
34
18
|
catch {
|
|
35
19
|
return false;
|
|
36
20
|
}
|
|
37
|
-
return true;
|
|
38
21
|
}
|
|
39
22
|
exports.default = isDirectusJWT;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
5
|
+
}) : (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
o[k2] = m[k];
|
|
8
|
+
}));
|
|
9
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
10
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
11
|
+
}) : function(o, v) {
|
|
12
|
+
o["default"] = v;
|
|
13
|
+
});
|
|
14
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
15
|
+
if (mod && mod.__esModule) return mod;
|
|
16
|
+
var result = {};
|
|
17
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
18
|
+
__setModuleDefault(result, mod);
|
|
19
|
+
return result;
|
|
20
|
+
};
|
|
21
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
+
exports.verifyAccessJWT = void 0;
|
|
23
|
+
const jsonwebtoken_1 = __importStar(require("jsonwebtoken"));
|
|
24
|
+
const exceptions_1 = require("../exceptions");
|
|
25
|
+
function verifyAccessJWT(token, secret) {
|
|
26
|
+
let payload;
|
|
27
|
+
try {
|
|
28
|
+
payload = jsonwebtoken_1.default.verify(token, secret, {
|
|
29
|
+
issuer: 'directus',
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
catch (err) {
|
|
33
|
+
if (err instanceof jsonwebtoken_1.TokenExpiredError) {
|
|
34
|
+
throw new exceptions_1.InvalidTokenException('Token expired.');
|
|
35
|
+
}
|
|
36
|
+
else if (err instanceof jsonwebtoken_1.JsonWebTokenError) {
|
|
37
|
+
throw new exceptions_1.InvalidTokenException('Token invalid.');
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
throw new exceptions_1.ServiceUnavailableException(`Couldn't verify token.`, { service: 'jwt' });
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const { id, role, app_access, admin_access, share, share_scope } = payload;
|
|
44
|
+
if (role === undefined || app_access === undefined || admin_access === undefined) {
|
|
45
|
+
throw new exceptions_1.InvalidTokenException('Invalid token payload.');
|
|
46
|
+
}
|
|
47
|
+
return { id, role, app_access, admin_access, share, share_scope };
|
|
48
|
+
}
|
|
49
|
+
exports.verifyAccessJWT = verifyAccessJWT;
|
|
@@ -31,9 +31,15 @@ function mergePermission(strategy, currentPerm, newPerm) {
|
|
|
31
31
|
};
|
|
32
32
|
}
|
|
33
33
|
else if (currentPerm.permissions) {
|
|
34
|
-
permissions
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
// Empty {} supersedes other permissions in _OR merge
|
|
35
|
+
if (strategy === 'or' && ((0, lodash_1.isEqual)(currentPerm.permissions, {}) || (0, lodash_1.isEqual)(newPerm.permissions, {}))) {
|
|
36
|
+
permissions = {};
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
permissions = {
|
|
40
|
+
[logicalKey]: [currentPerm.permissions, newPerm.permissions],
|
|
41
|
+
};
|
|
42
|
+
}
|
|
37
43
|
}
|
|
38
44
|
else {
|
|
39
45
|
permissions = {
|
|
@@ -51,9 +57,15 @@ function mergePermission(strategy, currentPerm, newPerm) {
|
|
|
51
57
|
};
|
|
52
58
|
}
|
|
53
59
|
else if (currentPerm.validation) {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
60
|
+
// Empty {} supersedes other validations in _OR merge
|
|
61
|
+
if (strategy === 'or' && ((0, lodash_1.isEqual)(currentPerm.validation, {}) || (0, lodash_1.isEqual)(newPerm.validation, {}))) {
|
|
62
|
+
validation = {};
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
validation = {
|
|
66
|
+
[logicalKey]: [currentPerm.validation, newPerm.validation],
|
|
67
|
+
};
|
|
68
|
+
}
|
|
57
69
|
}
|
|
58
70
|
else {
|
|
59
71
|
validation = {
|
package/example.env
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "directus",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.7.0",
|
|
4
4
|
"license": "GPL-3.0-only",
|
|
5
5
|
"homepage": "https://github.com/directus/directus#readme",
|
|
6
6
|
"description": "Directus is a real-time API and App dashboard for managing SQL database content.",
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"cli": "cross-env NODE_ENV=development SERVE_APP=false ts-node --script-mode --transpile-only src/cli/run.ts",
|
|
66
66
|
"test": "jest",
|
|
67
67
|
"test:coverage": "jest --coverage",
|
|
68
|
-
"test:watch": "jest --
|
|
68
|
+
"test:watch": "jest --watch"
|
|
69
69
|
},
|
|
70
70
|
"engines": {
|
|
71
71
|
"node": ">=12.20.0"
|
|
@@ -78,23 +78,22 @@
|
|
|
78
78
|
],
|
|
79
79
|
"dependencies": {
|
|
80
80
|
"@aws-sdk/client-ses": "^3.40.0",
|
|
81
|
-
"@directus/app": "9.
|
|
82
|
-
"@directus/drive": "9.
|
|
83
|
-
"@directus/drive-azure": "9.
|
|
84
|
-
"@directus/drive-gcs": "9.
|
|
85
|
-
"@directus/drive-s3": "9.
|
|
86
|
-
"@directus/extensions-sdk": "9.
|
|
87
|
-
"@directus/format-title": "9.
|
|
88
|
-
"@directus/schema": "9.
|
|
89
|
-
"@directus/shared": "9.
|
|
90
|
-
"@directus/specs": "9.
|
|
81
|
+
"@directus/app": "9.7.0",
|
|
82
|
+
"@directus/drive": "9.7.0",
|
|
83
|
+
"@directus/drive-azure": "9.7.0",
|
|
84
|
+
"@directus/drive-gcs": "9.7.0",
|
|
85
|
+
"@directus/drive-s3": "9.7.0",
|
|
86
|
+
"@directus/extensions-sdk": "9.7.0",
|
|
87
|
+
"@directus/format-title": "9.7.0",
|
|
88
|
+
"@directus/schema": "9.7.0",
|
|
89
|
+
"@directus/shared": "9.7.0",
|
|
90
|
+
"@directus/specs": "9.7.0",
|
|
91
91
|
"@godaddy/terminus": "^4.9.0",
|
|
92
|
-
"@rollup/plugin-alias": "^3.1.
|
|
92
|
+
"@rollup/plugin-alias": "^3.1.9",
|
|
93
93
|
"@rollup/plugin-virtual": "^2.0.3",
|
|
94
94
|
"argon2": "^0.28.2",
|
|
95
95
|
"async": "^3.2.0",
|
|
96
96
|
"async-mutex": "^0.3.1",
|
|
97
|
-
"atob": "^2.1.2",
|
|
98
97
|
"axios": "^0.24.0",
|
|
99
98
|
"busboy": "^0.3.1",
|
|
100
99
|
"camelcase": "^6.2.0",
|
|
@@ -149,11 +148,12 @@
|
|
|
149
148
|
"qs": "^6.9.4",
|
|
150
149
|
"rate-limiter-flexible": "^2.2.2",
|
|
151
150
|
"resolve-cwd": "^3.0.0",
|
|
152
|
-
"rollup": "^2.
|
|
151
|
+
"rollup": "^2.67.3",
|
|
153
152
|
"sanitize-html": "^2.6.0",
|
|
154
153
|
"sharp": "^0.29.0",
|
|
155
154
|
"stream-json": "^1.7.1",
|
|
156
155
|
"supertest": "^6.1.6",
|
|
156
|
+
"tmp-promise": "^3.0.3",
|
|
157
157
|
"update-check": "^1.5.4",
|
|
158
158
|
"uuid": "^8.3.2",
|
|
159
159
|
"uuid-validate": "0.0.3",
|
|
@@ -173,10 +173,9 @@
|
|
|
173
173
|
"sqlite3": "^5.0.2",
|
|
174
174
|
"tedious": "^13.0.0"
|
|
175
175
|
},
|
|
176
|
-
"gitHead": "
|
|
176
|
+
"gitHead": "c1da41d6719d4efdc5d0196019fb7b2c6672c575",
|
|
177
177
|
"devDependencies": {
|
|
178
178
|
"@types/async": "3.2.10",
|
|
179
|
-
"@types/atob": "2.1.2",
|
|
180
179
|
"@types/body-parser": "1.19.2",
|
|
181
180
|
"@types/busboy": "0.3.1",
|
|
182
181
|
"@types/cookie-parser": "1.4.2",
|
|
@@ -189,7 +188,7 @@
|
|
|
189
188
|
"@types/flat": "5.0.2",
|
|
190
189
|
"@types/fs-extra": "9.0.13",
|
|
191
190
|
"@types/inquirer": "8.1.3",
|
|
192
|
-
"@types/jest": "27.
|
|
191
|
+
"@types/jest": "27.4.1",
|
|
193
192
|
"@types/js-yaml": "4.0.5",
|
|
194
193
|
"@types/json2csv": "5.0.3",
|
|
195
194
|
"@types/jsonwebtoken": "8.5.6",
|
|
@@ -215,9 +214,9 @@
|
|
|
215
214
|
"@types/wellknown": "0.5.1",
|
|
216
215
|
"copyfiles": "2.4.1",
|
|
217
216
|
"cross-env": "7.0.3",
|
|
218
|
-
"jest": "27.
|
|
217
|
+
"jest": "27.5.1",
|
|
219
218
|
"knex-mock-client": "1.6.1",
|
|
220
|
-
"ts-jest": "27.
|
|
219
|
+
"ts-jest": "27.1.3",
|
|
221
220
|
"ts-node-dev": "1.1.8",
|
|
222
221
|
"typescript": "4.5.2"
|
|
223
222
|
}
|
package/dist/__mocks__/cache.js
DELETED
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getCache = exports.cache = void 0;
|
|
4
|
-
exports.cache = {
|
|
5
|
-
get: jest.fn().mockResolvedValue(undefined),
|
|
6
|
-
set: jest.fn().mockResolvedValue(true),
|
|
7
|
-
};
|
|
8
|
-
exports.getCache = jest.fn().mockReturnValue({ cache: exports.cache });
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/// <reference types="node" />
|
|
2
|
-
import { Knex } from 'knex';
|
|
3
|
-
import { AbstractServiceOptions } from '../types';
|
|
4
|
-
import { Accountability, SchemaOverview } from '@directus/shared/types';
|
|
5
|
-
export declare class ImportService {
|
|
6
|
-
knex: Knex;
|
|
7
|
-
accountability: Accountability | null;
|
|
8
|
-
schema: SchemaOverview;
|
|
9
|
-
constructor(options: AbstractServiceOptions);
|
|
10
|
-
import(collection: string, mimetype: string, stream: NodeJS.ReadableStream): Promise<void>;
|
|
11
|
-
importJSON(collection: string, stream: NodeJS.ReadableStream): Promise<void>;
|
|
12
|
-
importCSV(collection: string, stream: NodeJS.ReadableStream): Promise<void>;
|
|
13
|
-
}
|
package/dist/services/import.js
DELETED
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.ImportService = void 0;
|
|
7
|
-
const database_1 = __importDefault(require("../database"));
|
|
8
|
-
const exceptions_1 = require("../exceptions");
|
|
9
|
-
const StreamArray_1 = __importDefault(require("stream-json/streamers/StreamArray"));
|
|
10
|
-
const items_1 = require("./items");
|
|
11
|
-
const async_1 = require("async");
|
|
12
|
-
const destroy_1 = __importDefault(require("destroy"));
|
|
13
|
-
const csv_parser_1 = __importDefault(require("csv-parser"));
|
|
14
|
-
const lodash_1 = require("lodash");
|
|
15
|
-
class ImportService {
|
|
16
|
-
constructor(options) {
|
|
17
|
-
this.knex = options.knex || (0, database_1.default)();
|
|
18
|
-
this.accountability = options.accountability || null;
|
|
19
|
-
this.schema = options.schema;
|
|
20
|
-
}
|
|
21
|
-
async import(collection, mimetype, stream) {
|
|
22
|
-
var _a, _b, _c, _d, _e;
|
|
23
|
-
if (collection.startsWith('directus_'))
|
|
24
|
-
throw new exceptions_1.ForbiddenException();
|
|
25
|
-
const createPermissions = (_b = (_a = this.accountability) === null || _a === void 0 ? void 0 : _a.permissions) === null || _b === void 0 ? void 0 : _b.find((permission) => permission.collection === collection && permission.action === 'create');
|
|
26
|
-
const updatePermissions = (_d = (_c = this.accountability) === null || _c === void 0 ? void 0 : _c.permissions) === null || _d === void 0 ? void 0 : _d.find((permission) => permission.collection === collection && permission.action === 'update');
|
|
27
|
-
if (((_e = this.accountability) === null || _e === void 0 ? void 0 : _e.admin) !== true && (!createPermissions || !updatePermissions)) {
|
|
28
|
-
throw new exceptions_1.ForbiddenException();
|
|
29
|
-
}
|
|
30
|
-
switch (mimetype) {
|
|
31
|
-
case 'application/json':
|
|
32
|
-
return await this.importJSON(collection, stream);
|
|
33
|
-
case 'text/csv':
|
|
34
|
-
case 'application/vnd.ms-excel':
|
|
35
|
-
return await this.importCSV(collection, stream);
|
|
36
|
-
default:
|
|
37
|
-
throw new exceptions_1.UnsupportedMediaTypeException(`Can't import files of type "${mimetype}"`);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
importJSON(collection, stream) {
|
|
41
|
-
const extractJSON = StreamArray_1.default.withParser();
|
|
42
|
-
return this.knex.transaction((trx) => {
|
|
43
|
-
const service = new items_1.ItemsService(collection, {
|
|
44
|
-
knex: trx,
|
|
45
|
-
schema: this.schema,
|
|
46
|
-
accountability: this.accountability,
|
|
47
|
-
});
|
|
48
|
-
const saveQueue = (0, async_1.queue)(async (value) => {
|
|
49
|
-
return await service.upsertOne(value);
|
|
50
|
-
});
|
|
51
|
-
return new Promise((resolve, reject) => {
|
|
52
|
-
stream.pipe(extractJSON);
|
|
53
|
-
extractJSON.on('data', ({ value }) => {
|
|
54
|
-
saveQueue.push(value);
|
|
55
|
-
});
|
|
56
|
-
extractJSON.on('error', (err) => {
|
|
57
|
-
(0, destroy_1.default)(stream);
|
|
58
|
-
(0, destroy_1.default)(extractJSON);
|
|
59
|
-
reject(new exceptions_1.InvalidPayloadException(err.message));
|
|
60
|
-
});
|
|
61
|
-
saveQueue.error((err) => {
|
|
62
|
-
reject(err);
|
|
63
|
-
});
|
|
64
|
-
extractJSON.on('end', () => {
|
|
65
|
-
saveQueue.drain(() => {
|
|
66
|
-
return resolve();
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
});
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
importCSV(collection, stream) {
|
|
73
|
-
return this.knex.transaction((trx) => {
|
|
74
|
-
const service = new items_1.ItemsService(collection, {
|
|
75
|
-
knex: trx,
|
|
76
|
-
schema: this.schema,
|
|
77
|
-
accountability: this.accountability,
|
|
78
|
-
});
|
|
79
|
-
const saveQueue = (0, async_1.queue)(async (value) => {
|
|
80
|
-
return await service.upsertOne(value);
|
|
81
|
-
});
|
|
82
|
-
return new Promise((resolve, reject) => {
|
|
83
|
-
stream
|
|
84
|
-
.pipe((0, csv_parser_1.default)())
|
|
85
|
-
.on('data', (value) => {
|
|
86
|
-
const obj = (0, lodash_1.transform)(value, (result, value, key) => {
|
|
87
|
-
if (value.length === 0) {
|
|
88
|
-
delete result[key];
|
|
89
|
-
}
|
|
90
|
-
else {
|
|
91
|
-
try {
|
|
92
|
-
const parsedJson = JSON.parse(value);
|
|
93
|
-
(0, lodash_1.set)(result, key, parsedJson);
|
|
94
|
-
}
|
|
95
|
-
catch {
|
|
96
|
-
(0, lodash_1.set)(result, key, value);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
});
|
|
100
|
-
saveQueue.push(obj);
|
|
101
|
-
})
|
|
102
|
-
.on('error', (err) => {
|
|
103
|
-
(0, destroy_1.default)(stream);
|
|
104
|
-
reject(new exceptions_1.InvalidPayloadException(err.message));
|
|
105
|
-
})
|
|
106
|
-
.on('end', () => {
|
|
107
|
-
saveQueue.drain(() => {
|
|
108
|
-
return resolve();
|
|
109
|
-
});
|
|
110
|
-
});
|
|
111
|
-
saveQueue.error((err) => {
|
|
112
|
-
reject(err);
|
|
113
|
-
});
|
|
114
|
-
});
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
exports.ImportService = ImportService;
|