directus 9.22.4 → 9.23.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/dist/app.js +5 -4
- package/dist/auth/drivers/ldap.d.ts +2 -2
- package/dist/auth/drivers/ldap.js +8 -8
- package/dist/auth/drivers/oauth2.js +2 -2
- package/dist/auth/drivers/openid.js +2 -2
- package/dist/cache.js +4 -4
- package/dist/cli/commands/schema/apply.js +19 -17
- package/dist/cli/utils/create-db-connection.d.ts +2 -1
- package/dist/cli/utils/create-env/env-stub.liquid +1 -1
- package/dist/cli/utils/drivers.d.ts +3 -9
- package/dist/constants.d.ts +2 -8
- package/dist/constants.js +3 -7
- package/dist/controllers/assets.js +5 -5
- package/dist/controllers/extensions.js +7 -7
- package/dist/controllers/files.js +1 -1
- package/dist/controllers/graphql.js +8 -0
- package/dist/controllers/schema.d.ts +2 -0
- package/dist/controllers/schema.js +98 -0
- package/dist/database/helpers/schema/dialects/cockroachdb.d.ts +1 -1
- package/dist/database/helpers/schema/dialects/oracle.d.ts +4 -1
- package/dist/database/helpers/schema/dialects/oracle.js +25 -0
- package/dist/database/helpers/schema/types.d.ts +8 -6
- package/dist/database/helpers/schema/types.js +7 -1
- package/dist/database/index.d.ts +2 -1
- package/dist/database/run-ast.js +2 -2
- package/dist/env.js +9 -2
- package/dist/extensions.js +1 -1
- package/dist/flows.js +17 -8
- package/dist/middleware/cache.js +2 -2
- package/dist/middleware/respond.js +14 -9
- package/dist/operations/request/index.js +2 -1
- package/dist/operations/trigger/index.d.ts +2 -0
- package/dist/operations/trigger/index.js +26 -9
- package/dist/request/index.d.ts +5 -0
- package/dist/request/index.js +18 -0
- package/dist/request/index.test.d.ts +1 -0
- package/dist/request/request-interceptor.d.ts +2 -0
- package/dist/request/request-interceptor.js +33 -0
- package/dist/request/request-interceptor.test.d.ts +1 -0
- package/dist/request/response-interceptor.d.ts +2 -0
- package/dist/request/response-interceptor.js +9 -0
- package/dist/request/response-interceptor.test.d.ts +1 -0
- package/dist/request/validate-ip.d.ts +1 -0
- package/dist/request/validate-ip.js +27 -0
- package/dist/request/validate-ip.test.d.ts +1 -0
- package/dist/services/assets.d.ts +1 -1
- package/dist/services/assets.js +11 -2
- package/dist/services/authentication.js +5 -5
- package/dist/services/fields.js +1 -0
- package/dist/services/files.js +44 -88
- package/dist/services/graphql/index.js +14 -8
- package/dist/services/graphql/utils/process-error.js +22 -9
- package/dist/services/import-export.d.ts +4 -2
- package/dist/services/import-export.js +17 -3
- package/dist/services/import-export.test.d.ts +1 -0
- package/dist/services/index.d.ts +1 -0
- package/dist/services/index.js +1 -0
- package/dist/services/items.js +34 -15
- package/dist/services/relations.js +2 -0
- package/dist/services/roles.js +32 -11
- package/dist/services/schema.d.ts +15 -0
- package/dist/services/schema.js +58 -0
- package/dist/services/schema.test.d.ts +1 -0
- package/dist/services/shares.d.ts +2 -2
- package/dist/services/shares.js +9 -9
- package/dist/services/users.js +74 -47
- package/dist/types/assets.d.ts +1 -1
- package/dist/types/database.d.ts +3 -0
- package/dist/types/database.js +4 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +1 -0
- package/dist/types/items.d.ts +5 -0
- package/dist/types/snapshot.d.ts +22 -0
- package/dist/types/snapshot.js +14 -0
- package/dist/utils/apply-diff.d.ts +9 -0
- package/dist/utils/apply-diff.js +259 -0
- package/dist/utils/apply-diff.test.d.ts +1 -0
- package/dist/utils/apply-query.js +8 -6
- package/dist/utils/apply-snapshot.d.ts +1 -3
- package/dist/utils/apply-snapshot.js +4 -234
- package/dist/utils/get-cache-headers.d.ts +3 -1
- package/dist/utils/get-cache-headers.js +20 -19
- package/dist/utils/get-cache-headers.test.d.ts +1 -0
- package/dist/utils/get-milliseconds.d.ts +4 -0
- package/dist/utils/get-milliseconds.js +15 -0
- package/dist/utils/get-milliseconds.test.d.ts +1 -0
- package/dist/utils/get-snapshot-diff.js +11 -7
- package/dist/utils/get-snapshot.js +29 -6
- package/dist/utils/get-versioned-hash.d.ts +1 -0
- package/dist/utils/get-versioned-hash.js +12 -0
- package/dist/utils/get-versioned-hash.test.d.ts +1 -0
- package/dist/utils/map-values-deep.d.ts +1 -0
- package/dist/utils/map-values-deep.js +29 -0
- package/dist/utils/map-values-deep.test.d.ts +1 -0
- package/dist/utils/sanitize-schema.d.ts +30 -0
- package/dist/utils/sanitize-schema.js +80 -0
- package/dist/utils/sanitize-schema.test.d.ts +1 -0
- package/dist/utils/track.js +3 -3
- package/dist/utils/url.js +2 -6
- package/dist/utils/url.test.d.ts +1 -0
- package/dist/utils/validate-diff.d.ts +7 -0
- package/dist/utils/validate-diff.js +114 -0
- package/dist/utils/validate-diff.test.d.ts +1 -0
- package/dist/utils/validate-query.js +1 -1
- package/dist/utils/validate-query.test.d.ts +1 -0
- package/dist/utils/validate-snapshot.d.ts +5 -0
- package/dist/utils/validate-snapshot.js +71 -0
- package/dist/utils/validate-snapshot.test.d.ts +1 -0
- package/dist/utils/with-timeout.d.ts +1 -0
- package/dist/utils/with-timeout.js +16 -0
- package/dist/webhooks.js +3 -2
- package/package.json +54 -53
package/dist/services/files.js
CHANGED
|
@@ -1,56 +1,29 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
-
if (mod && mod.__esModule) return mod;
|
|
20
|
-
var result = {};
|
|
21
|
-
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
-
__setModuleDefault(result, mod);
|
|
23
|
-
return result;
|
|
24
|
-
};
|
|
25
2
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
4
|
};
|
|
28
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
6
|
exports.FilesService = void 0;
|
|
30
7
|
const utils_1 = require("@directus/shared/utils");
|
|
31
|
-
const dns_1 = require("dns");
|
|
32
8
|
const encodeurl_1 = __importDefault(require("encodeurl"));
|
|
33
9
|
const exif_reader_1 = __importDefault(require("exif-reader"));
|
|
34
10
|
const icc_1 = require("icc");
|
|
35
11
|
const lodash_1 = require("lodash");
|
|
36
12
|
const mime_types_1 = require("mime-types");
|
|
37
|
-
const net_1 = __importDefault(require("net"));
|
|
38
13
|
const promises_1 = require("node:stream/promises");
|
|
39
|
-
const os_1 = __importDefault(require("os"));
|
|
40
14
|
const path_1 = __importDefault(require("path"));
|
|
41
15
|
const sharp_1 = __importDefault(require("sharp"));
|
|
42
|
-
const url_1 =
|
|
43
|
-
const util_1 = require("util");
|
|
16
|
+
const url_1 = __importDefault(require("url"));
|
|
44
17
|
const emitter_1 = __importDefault(require("../emitter"));
|
|
45
18
|
const env_1 = __importDefault(require("../env"));
|
|
46
19
|
const exceptions_1 = require("../exceptions");
|
|
47
20
|
const logger_1 = __importDefault(require("../logger"));
|
|
21
|
+
const index_1 = require("../request/index");
|
|
48
22
|
const storage_1 = require("../storage");
|
|
49
23
|
const parse_image_metadata_1 = require("../utils/parse-image-metadata");
|
|
50
24
|
const items_1 = require("./items");
|
|
51
25
|
// @ts-ignore
|
|
52
26
|
const format_title_1 = __importDefault(require("@directus/format-title"));
|
|
53
|
-
const lookupDNS = (0, util_1.promisify)(dns_1.lookup);
|
|
54
27
|
class FilesService extends items_1.ItemsService {
|
|
55
28
|
constructor(options) {
|
|
56
29
|
super('directus_files', options);
|
|
@@ -112,7 +85,7 @@ class FilesService extends items_1.ItemsService {
|
|
|
112
85
|
schema: this.schema,
|
|
113
86
|
});
|
|
114
87
|
await sudoService.updateOne(primaryKey, payload, { emitEvents: false });
|
|
115
|
-
if (this.cache && env_1.default.CACHE_AUTO_PURGE) {
|
|
88
|
+
if (this.cache && env_1.default.CACHE_AUTO_PURGE && (opts === null || opts === void 0 ? void 0 : opts.autoPurgeCache) !== false) {
|
|
116
89
|
await this.cache.clear();
|
|
117
90
|
}
|
|
118
91
|
if ((opts === null || opts === void 0 ? void 0 : opts.emitEvents) !== false) {
|
|
@@ -135,8 +108,10 @@ class FilesService extends items_1.ItemsService {
|
|
|
135
108
|
return new Promise((resolve, reject) => {
|
|
136
109
|
(0, promises_1.pipeline)(stream, (0, sharp_1.default)().metadata(async (err, sharpMetadata) => {
|
|
137
110
|
var _a, _b, _c, _d;
|
|
138
|
-
if (err)
|
|
111
|
+
if (err) {
|
|
139
112
|
reject(err);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
140
115
|
const metadata = {};
|
|
141
116
|
if (sharpMetadata.orientation && sharpMetadata.orientation >= 5) {
|
|
142
117
|
metadata.height = sharpMetadata.width;
|
|
@@ -149,26 +124,50 @@ class FilesService extends items_1.ItemsService {
|
|
|
149
124
|
// Backward-compatible layout as it used to be with 'exifr'
|
|
150
125
|
const fullMetadata = {};
|
|
151
126
|
if (sharpMetadata.exif) {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
127
|
+
try {
|
|
128
|
+
const { image, thumbnail, interoperability, ...rest } = (0, exif_reader_1.default)(sharpMetadata.exif);
|
|
129
|
+
if (image) {
|
|
130
|
+
fullMetadata.ifd0 = image;
|
|
131
|
+
}
|
|
132
|
+
if (thumbnail) {
|
|
133
|
+
fullMetadata.ifd1 = thumbnail;
|
|
134
|
+
}
|
|
135
|
+
if (interoperability) {
|
|
136
|
+
fullMetadata.interop = interoperability;
|
|
137
|
+
}
|
|
138
|
+
Object.assign(fullMetadata, rest);
|
|
158
139
|
}
|
|
159
|
-
|
|
160
|
-
|
|
140
|
+
catch (err) {
|
|
141
|
+
logger_1.default.warn(`Couldn't extract EXIF metadata from file`);
|
|
142
|
+
logger_1.default.warn(err);
|
|
161
143
|
}
|
|
162
|
-
Object.assign(fullMetadata, rest);
|
|
163
144
|
}
|
|
164
145
|
if (sharpMetadata.icc) {
|
|
165
|
-
|
|
146
|
+
try {
|
|
147
|
+
fullMetadata.icc = (0, icc_1.parse)(sharpMetadata.icc);
|
|
148
|
+
}
|
|
149
|
+
catch (err) {
|
|
150
|
+
logger_1.default.warn(`Couldn't extract ICC profile data from file`);
|
|
151
|
+
logger_1.default.warn(err);
|
|
152
|
+
}
|
|
166
153
|
}
|
|
167
154
|
if (sharpMetadata.iptc) {
|
|
168
|
-
|
|
155
|
+
try {
|
|
156
|
+
fullMetadata.iptc = (0, parse_image_metadata_1.parseIptc)(sharpMetadata.iptc);
|
|
157
|
+
}
|
|
158
|
+
catch (err) {
|
|
159
|
+
logger_1.default.warn(`Couldn't extract IPTC Photo Metadata from file`);
|
|
160
|
+
logger_1.default.warn(err);
|
|
161
|
+
}
|
|
169
162
|
}
|
|
170
163
|
if (sharpMetadata.xmp) {
|
|
171
|
-
|
|
164
|
+
try {
|
|
165
|
+
fullMetadata.xmp = (0, parse_image_metadata_1.parseXmp)(sharpMetadata.xmp);
|
|
166
|
+
}
|
|
167
|
+
catch (err) {
|
|
168
|
+
logger_1.default.warn(`Couldn't extract XMP data from file`);
|
|
169
|
+
logger_1.default.warn(err);
|
|
170
|
+
}
|
|
172
171
|
}
|
|
173
172
|
if (((_a = fullMetadata === null || fullMetadata === void 0 ? void 0 : fullMetadata.iptc) === null || _a === void 0 ? void 0 : _a.Caption) && typeof fullMetadata.iptc.Caption === 'string') {
|
|
174
173
|
metadata.description = (_b = fullMetadata.iptc) === null || _b === void 0 ? void 0 : _b.Caption;
|
|
@@ -203,62 +202,19 @@ class FilesService extends items_1.ItemsService {
|
|
|
203
202
|
*/
|
|
204
203
|
async importOne(importURL, body) {
|
|
205
204
|
var _a, _b, _c;
|
|
206
|
-
const axios = (await import('axios')).default;
|
|
207
205
|
const fileCreatePermissions = (_b = (_a = this.accountability) === null || _a === void 0 ? void 0 : _a.permissions) === null || _b === void 0 ? void 0 : _b.find((permission) => permission.collection === 'directus_files' && permission.action === 'create');
|
|
208
206
|
if (this.accountability && ((_c = this.accountability) === null || _c === void 0 ? void 0 : _c.admin) !== true && !fileCreatePermissions) {
|
|
209
207
|
throw new exceptions_1.ForbiddenException();
|
|
210
208
|
}
|
|
211
|
-
let resolvedUrl;
|
|
212
|
-
try {
|
|
213
|
-
resolvedUrl = new url_1.URL(importURL);
|
|
214
|
-
}
|
|
215
|
-
catch (err) {
|
|
216
|
-
logger_1.default.warn(err, `Requested URL ${importURL} isn't a valid URL`);
|
|
217
|
-
throw new exceptions_1.ServiceUnavailableException(`Couldn't fetch file from url "${importURL}"`, {
|
|
218
|
-
service: 'external-file',
|
|
219
|
-
});
|
|
220
|
-
}
|
|
221
|
-
let ip = resolvedUrl.hostname;
|
|
222
|
-
if (net_1.default.isIP(ip) === 0) {
|
|
223
|
-
try {
|
|
224
|
-
ip = (await lookupDNS(ip)).address;
|
|
225
|
-
}
|
|
226
|
-
catch (err) {
|
|
227
|
-
logger_1.default.warn(err, `Couldn't lookup the DNS for url ${importURL}`);
|
|
228
|
-
throw new exceptions_1.ServiceUnavailableException(`Couldn't fetch file from url "${importURL}"`, {
|
|
229
|
-
service: 'external-file',
|
|
230
|
-
});
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
if (env_1.default.IMPORT_IP_DENY_LIST.includes('0.0.0.0')) {
|
|
234
|
-
const networkInterfaces = os_1.default.networkInterfaces();
|
|
235
|
-
for (const networkInfo of Object.values(networkInterfaces)) {
|
|
236
|
-
if (!networkInfo)
|
|
237
|
-
continue;
|
|
238
|
-
for (const info of networkInfo) {
|
|
239
|
-
if (info.address === ip) {
|
|
240
|
-
logger_1.default.warn(`Requested URL ${importURL} resolves to localhost.`);
|
|
241
|
-
throw new exceptions_1.ServiceUnavailableException(`Couldn't fetch file from url "${importURL}"`, {
|
|
242
|
-
service: 'external-file',
|
|
243
|
-
});
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
if (env_1.default.IMPORT_IP_DENY_LIST.includes(ip)) {
|
|
249
|
-
logger_1.default.warn(`Requested URL ${importURL} resolves to a denied IP address.`);
|
|
250
|
-
throw new exceptions_1.ServiceUnavailableException(`Couldn't fetch file from url "${importURL}"`, {
|
|
251
|
-
service: 'external-file',
|
|
252
|
-
});
|
|
253
|
-
}
|
|
254
209
|
let fileResponse;
|
|
255
210
|
try {
|
|
211
|
+
const axios = await (0, index_1.getAxios)();
|
|
256
212
|
fileResponse = await axios.get((0, encodeurl_1.default)(importURL), {
|
|
257
213
|
responseType: 'stream',
|
|
258
214
|
});
|
|
259
215
|
}
|
|
260
216
|
catch (err) {
|
|
261
|
-
logger_1.default.warn(err, `Couldn't fetch file from
|
|
217
|
+
logger_1.default.warn(err, `Couldn't fetch file from URL "${importURL}"`);
|
|
262
218
|
throw new exceptions_1.ServiceUnavailableException(`Couldn't fetch file from url "${importURL}"`, {
|
|
263
219
|
service: 'external-file',
|
|
264
220
|
});
|
|
@@ -9,9 +9,7 @@ const utils_1 = require("@directus/shared/utils");
|
|
|
9
9
|
const argon2_1 = __importDefault(require("argon2"));
|
|
10
10
|
const graphql_1 = require("graphql");
|
|
11
11
|
const graphql_compose_1 = require("graphql-compose");
|
|
12
|
-
const process_error_1 = __importDefault(require("./utils/process-error"));
|
|
13
12
|
const lodash_1 = require("lodash");
|
|
14
|
-
const ms_1 = __importDefault(require("ms"));
|
|
15
13
|
const cache_1 = require("../../cache");
|
|
16
14
|
const constants_1 = require("../../constants");
|
|
17
15
|
const database_1 = __importDefault(require("../../database"));
|
|
@@ -46,14 +44,16 @@ const tfa_1 = require("../tfa");
|
|
|
46
44
|
const users_1 = require("../users");
|
|
47
45
|
const utils_2 = require("../utils");
|
|
48
46
|
const webhooks_1 = require("../webhooks");
|
|
47
|
+
const process_error_1 = __importDefault(require("./utils/process-error"));
|
|
49
48
|
const date_1 = require("./types/date");
|
|
50
49
|
const geojson_1 = require("./types/geojson");
|
|
51
50
|
const string_or_float_1 = require("./types/string-or-float");
|
|
52
51
|
const void_1 = require("./types/void");
|
|
53
|
-
const add_path_to_validation_error_1 = require("./utils/add-path-to-validation-error");
|
|
54
|
-
const hash_1 = require("./types/hash");
|
|
55
|
-
const bigint_1 = require("./types/bigint");
|
|
56
52
|
const constants_2 = require("@directus/shared/constants");
|
|
53
|
+
const get_milliseconds_1 = require("../../utils/get-milliseconds");
|
|
54
|
+
const bigint_1 = require("./types/bigint");
|
|
55
|
+
const hash_1 = require("./types/hash");
|
|
56
|
+
const add_path_to_validation_error_1 = require("./utils/add-path-to-validation-error");
|
|
57
57
|
const validationRules = Array.from(graphql_1.specifiedRules);
|
|
58
58
|
if (env_1.default.GRAPHQL_INTROSPECTION === false) {
|
|
59
59
|
validationRules.push(graphql_1.NoSchemaIntrospectionCustomRule);
|
|
@@ -842,6 +842,12 @@ class GraphQLService {
|
|
|
842
842
|
limit: {
|
|
843
843
|
type: graphql_1.GraphQLInt,
|
|
844
844
|
},
|
|
845
|
+
offset: {
|
|
846
|
+
type: graphql_1.GraphQLInt,
|
|
847
|
+
},
|
|
848
|
+
page: {
|
|
849
|
+
type: graphql_1.GraphQLInt,
|
|
850
|
+
},
|
|
845
851
|
search: {
|
|
846
852
|
type: graphql_1.GraphQLString,
|
|
847
853
|
},
|
|
@@ -1614,7 +1620,7 @@ class GraphQLService {
|
|
|
1614
1620
|
accountability: this.accountability,
|
|
1615
1621
|
schema: this.schema,
|
|
1616
1622
|
});
|
|
1617
|
-
return await service.
|
|
1623
|
+
return await service.health();
|
|
1618
1624
|
},
|
|
1619
1625
|
},
|
|
1620
1626
|
});
|
|
@@ -1656,7 +1662,7 @@ class GraphQLService {
|
|
|
1656
1662
|
res === null || res === void 0 ? void 0 : res.cookie(env_1.default.REFRESH_TOKEN_COOKIE_NAME, result.refreshToken, {
|
|
1657
1663
|
httpOnly: true,
|
|
1658
1664
|
domain: env_1.default.REFRESH_TOKEN_COOKIE_DOMAIN,
|
|
1659
|
-
maxAge: (0,
|
|
1665
|
+
maxAge: (0, get_milliseconds_1.getMilliseconds)(env_1.default.REFRESH_TOKEN_TTL),
|
|
1660
1666
|
secure: (_a = env_1.default.REFRESH_TOKEN_COOKIE_SECURE) !== null && _a !== void 0 ? _a : false,
|
|
1661
1667
|
sameSite: env_1.default.REFRESH_TOKEN_COOKIE_SAME_SITE || 'strict',
|
|
1662
1668
|
});
|
|
@@ -1695,7 +1701,7 @@ class GraphQLService {
|
|
|
1695
1701
|
res === null || res === void 0 ? void 0 : res.cookie(env_1.default.REFRESH_TOKEN_COOKIE_NAME, result.refreshToken, {
|
|
1696
1702
|
httpOnly: true,
|
|
1697
1703
|
domain: env_1.default.REFRESH_TOKEN_COOKIE_DOMAIN,
|
|
1698
|
-
maxAge: (0,
|
|
1704
|
+
maxAge: (0, get_milliseconds_1.getMilliseconds)(env_1.default.REFRESH_TOKEN_TTL),
|
|
1699
1705
|
secure: (_a = env_1.default.REFRESH_TOKEN_COOKIE_SECURE) !== null && _a !== void 0 ? _a : false,
|
|
1700
1706
|
sameSite: env_1.default.REFRESH_TOKEN_COOKIE_SAME_SITE || 'strict',
|
|
1701
1707
|
});
|
|
@@ -4,23 +4,36 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
const logger_1 = __importDefault(require("../../../logger"));
|
|
7
|
+
const exceptions_1 = require("@directus/shared/exceptions");
|
|
7
8
|
const processError = (accountability, error) => {
|
|
8
9
|
logger_1.default.error(error);
|
|
9
|
-
|
|
10
|
+
const { originalError } = error;
|
|
11
|
+
if (originalError instanceof exceptions_1.BaseException) {
|
|
10
12
|
return {
|
|
11
|
-
|
|
13
|
+
message: originalError.message,
|
|
12
14
|
extensions: {
|
|
13
|
-
code:
|
|
15
|
+
code: originalError.code,
|
|
16
|
+
...originalError.extensions,
|
|
14
17
|
},
|
|
15
18
|
};
|
|
16
19
|
}
|
|
17
20
|
else {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
if ((accountability === null || accountability === void 0 ? void 0 : accountability.admin) === true) {
|
|
22
|
+
return {
|
|
23
|
+
...error,
|
|
24
|
+
extensions: {
|
|
25
|
+
code: 'INTERNAL_SERVER_ERROR',
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
return {
|
|
31
|
+
message: 'An unexpected error occurred.',
|
|
32
|
+
extensions: {
|
|
33
|
+
code: 'INTERNAL_SERVER_ERROR',
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
24
37
|
}
|
|
25
38
|
};
|
|
26
39
|
exports.default = processError;
|
|
@@ -3,6 +3,7 @@ import { Accountability, Query, SchemaOverview } from '@directus/shared/types';
|
|
|
3
3
|
import { Knex } from 'knex';
|
|
4
4
|
import { AbstractServiceOptions, File } from '../types';
|
|
5
5
|
import type { Readable } from 'node:stream';
|
|
6
|
+
type ExportFormat = 'csv' | 'json' | 'xml' | 'yaml';
|
|
6
7
|
export declare class ImportService {
|
|
7
8
|
knex: Knex;
|
|
8
9
|
accountability: Accountability | null;
|
|
@@ -22,14 +23,15 @@ export declare class ExportService {
|
|
|
22
23
|
* until all the data is retrieved. Uploads the result as a new file using the regular
|
|
23
24
|
* FilesService upload method.
|
|
24
25
|
*/
|
|
25
|
-
exportToFile(collection: string, query: Partial<Query>, format:
|
|
26
|
+
exportToFile(collection: string, query: Partial<Query>, format: ExportFormat, options?: {
|
|
26
27
|
file?: Partial<File>;
|
|
27
28
|
}): Promise<void>;
|
|
28
29
|
/**
|
|
29
30
|
* Transform a given input object / array to the given type
|
|
30
31
|
*/
|
|
31
|
-
transform(input: Record<string, any>[], format:
|
|
32
|
+
transform(input: Record<string, any>[], format: ExportFormat, options?: {
|
|
32
33
|
includeHeader?: boolean;
|
|
33
34
|
includeFooter?: boolean;
|
|
34
35
|
}): string;
|
|
35
36
|
}
|
|
37
|
+
export {};
|
|
@@ -9,6 +9,7 @@ const async_1 = require("async");
|
|
|
9
9
|
const csv_parser_1 = __importDefault(require("csv-parser"));
|
|
10
10
|
const destroy_1 = __importDefault(require("destroy"));
|
|
11
11
|
const fs_extra_1 = require("fs-extra");
|
|
12
|
+
const js_yaml_1 = require("js-yaml");
|
|
12
13
|
const js2xmlparser_1 = require("js2xmlparser");
|
|
13
14
|
const json2csv_1 = require("json2csv");
|
|
14
15
|
const lodash_1 = require("lodash");
|
|
@@ -23,6 +24,7 @@ const get_date_formatted_1 = require("../utils/get-date-formatted");
|
|
|
23
24
|
const files_1 = require("./files");
|
|
24
25
|
const items_1 = require("./items");
|
|
25
26
|
const notifications_1 = require("./notifications");
|
|
27
|
+
const emitter_1 = __importDefault(require("../emitter"));
|
|
26
28
|
class ImportService {
|
|
27
29
|
constructor(options) {
|
|
28
30
|
this.knex = options.knex || (0, database_1.default)();
|
|
@@ -50,6 +52,7 @@ class ImportService {
|
|
|
50
52
|
}
|
|
51
53
|
importJSON(collection, stream) {
|
|
52
54
|
const extractJSON = StreamArray_1.default.withParser();
|
|
55
|
+
const nestedActionEvents = [];
|
|
53
56
|
return this.knex.transaction((trx) => {
|
|
54
57
|
const service = new items_1.ItemsService(collection, {
|
|
55
58
|
knex: trx,
|
|
@@ -57,7 +60,7 @@ class ImportService {
|
|
|
57
60
|
accountability: this.accountability,
|
|
58
61
|
});
|
|
59
62
|
const saveQueue = (0, async_1.queue)(async (value) => {
|
|
60
|
-
return await service.upsertOne(value);
|
|
63
|
+
return await service.upsertOne(value, { bypassEmitAction: (params) => nestedActionEvents.push(params) });
|
|
61
64
|
});
|
|
62
65
|
return new Promise((resolve, reject) => {
|
|
63
66
|
stream.pipe(extractJSON);
|
|
@@ -74,6 +77,9 @@ class ImportService {
|
|
|
74
77
|
});
|
|
75
78
|
extractJSON.on('end', () => {
|
|
76
79
|
saveQueue.drain(() => {
|
|
80
|
+
for (const nestedActionEvent of nestedActionEvents) {
|
|
81
|
+
emitter_1.default.emitAction(nestedActionEvent.event, nestedActionEvent.meta, nestedActionEvent.context);
|
|
82
|
+
}
|
|
77
83
|
return resolve();
|
|
78
84
|
});
|
|
79
85
|
});
|
|
@@ -81,6 +87,7 @@ class ImportService {
|
|
|
81
87
|
});
|
|
82
88
|
}
|
|
83
89
|
importCSV(collection, stream) {
|
|
90
|
+
const nestedActionEvents = [];
|
|
84
91
|
return this.knex.transaction((trx) => {
|
|
85
92
|
const service = new items_1.ItemsService(collection, {
|
|
86
93
|
knex: trx,
|
|
@@ -88,7 +95,7 @@ class ImportService {
|
|
|
88
95
|
accountability: this.accountability,
|
|
89
96
|
});
|
|
90
97
|
const saveQueue = (0, async_1.queue)(async (value) => {
|
|
91
|
-
return await service.upsertOne(value);
|
|
98
|
+
return await service.upsertOne(value, { bypassEmitAction: (action) => nestedActionEvents.push(action) });
|
|
92
99
|
});
|
|
93
100
|
return new Promise((resolve, reject) => {
|
|
94
101
|
stream
|
|
@@ -122,6 +129,9 @@ class ImportService {
|
|
|
122
129
|
})
|
|
123
130
|
.on('end', () => {
|
|
124
131
|
saveQueue.drain(() => {
|
|
132
|
+
for (const nestedActionEvent of nestedActionEvents) {
|
|
133
|
+
emitter_1.default.emitAction(nestedActionEvent.event, nestedActionEvent.meta, nestedActionEvent.context);
|
|
134
|
+
}
|
|
125
135
|
return resolve();
|
|
126
136
|
});
|
|
127
137
|
});
|
|
@@ -148,9 +158,10 @@ class ExportService {
|
|
|
148
158
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
149
159
|
try {
|
|
150
160
|
const mimeTypes = {
|
|
151
|
-
xml: 'text/xml',
|
|
152
161
|
csv: 'text/csv',
|
|
153
162
|
json: 'application/json',
|
|
163
|
+
xml: 'text/xml',
|
|
164
|
+
yaml: 'text/yaml',
|
|
154
165
|
};
|
|
155
166
|
const database = (0, database_1.default)();
|
|
156
167
|
const { path, cleanup } = await (0, tmp_promise_1.file)();
|
|
@@ -277,6 +288,9 @@ class ExportService {
|
|
|
277
288
|
}
|
|
278
289
|
return string;
|
|
279
290
|
}
|
|
291
|
+
if (format === 'yaml') {
|
|
292
|
+
return (0, js_yaml_1.dump)(input);
|
|
293
|
+
}
|
|
280
294
|
throw new exceptions_1.ServiceUnavailableException(`Illegal export type used: "${format}"`, { service: 'export' });
|
|
281
295
|
}
|
|
282
296
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/services/index.d.ts
CHANGED
package/dist/services/index.js
CHANGED
|
@@ -39,6 +39,7 @@ __exportStar(require("./presets"), exports);
|
|
|
39
39
|
__exportStar(require("./relations"), exports);
|
|
40
40
|
__exportStar(require("./revisions"), exports);
|
|
41
41
|
__exportStar(require("./roles"), exports);
|
|
42
|
+
__exportStar(require("./schema"), exports);
|
|
42
43
|
__exportStar(require("./server"), exports);
|
|
43
44
|
__exportStar(require("./settings"), exports);
|
|
44
45
|
__exportStar(require("./specifications"), exports);
|
package/dist/services/items.js
CHANGED
|
@@ -86,6 +86,9 @@ class ItemsService {
|
|
|
86
86
|
const payloadWithPresets = this.accountability
|
|
87
87
|
? await authorizationService.validatePayload('create', this.collection, payloadAfterHooks)
|
|
88
88
|
: payloadAfterHooks;
|
|
89
|
+
if (opts === null || opts === void 0 ? void 0 : opts.preMutationException) {
|
|
90
|
+
throw opts.preMutationException;
|
|
91
|
+
}
|
|
89
92
|
const { payload: payloadWithM2O, revisions: revisionsM2O, nestedActionEvents: nestedActionEventsM2O, } = await payloadService.processM2O(payloadWithPresets, opts);
|
|
90
93
|
const { payload: payloadWithA2O, revisions: revisionsA2O, nestedActionEvents: nestedActionEventsA2O, } = await payloadService.processA2O(payloadWithM2O, opts);
|
|
91
94
|
const payloadWithoutAliases = (0, lodash_1.pick)(payloadWithA2O, (0, lodash_1.without)(fields, ...aliases));
|
|
@@ -180,14 +183,19 @@ class ItemsService {
|
|
|
180
183
|
accountability: this.accountability,
|
|
181
184
|
},
|
|
182
185
|
};
|
|
183
|
-
if (
|
|
184
|
-
|
|
186
|
+
if (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) {
|
|
187
|
+
opts.bypassEmitAction(actionEvent);
|
|
185
188
|
}
|
|
186
189
|
else {
|
|
187
|
-
|
|
190
|
+
emitter_1.default.emitAction(actionEvent.event, actionEvent.meta, actionEvent.context);
|
|
188
191
|
}
|
|
189
192
|
for (const nestedActionEvent of nestedActionEvents) {
|
|
190
|
-
|
|
193
|
+
if (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) {
|
|
194
|
+
opts.bypassEmitAction(nestedActionEvent);
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
emitter_1.default.emitAction(nestedActionEvent.event, nestedActionEvent.meta, nestedActionEvent.context);
|
|
198
|
+
}
|
|
191
199
|
}
|
|
192
200
|
}
|
|
193
201
|
if (this.cache && env_1.default.CACHE_AUTO_PURGE && (opts === null || opts === void 0 ? void 0 : opts.autoPurgeCache) !== false) {
|
|
@@ -211,7 +219,7 @@ class ItemsService {
|
|
|
211
219
|
const primaryKey = await service.createOne(payload, {
|
|
212
220
|
...(opts || {}),
|
|
213
221
|
autoPurgeCache: false,
|
|
214
|
-
bypassEmitAction: (params) =>
|
|
222
|
+
bypassEmitAction: (params) => nestedActionEvents.push(params),
|
|
215
223
|
});
|
|
216
224
|
primaryKeys.push(primaryKey);
|
|
217
225
|
}
|
|
@@ -219,11 +227,11 @@ class ItemsService {
|
|
|
219
227
|
});
|
|
220
228
|
if ((opts === null || opts === void 0 ? void 0 : opts.emitEvents) !== false) {
|
|
221
229
|
for (const nestedActionEvent of nestedActionEvents) {
|
|
222
|
-
if (
|
|
223
|
-
|
|
230
|
+
if (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) {
|
|
231
|
+
opts.bypassEmitAction(nestedActionEvent);
|
|
224
232
|
}
|
|
225
233
|
else {
|
|
226
|
-
|
|
234
|
+
emitter_1.default.emitAction(nestedActionEvent.event, nestedActionEvent.meta, nestedActionEvent.context);
|
|
227
235
|
}
|
|
228
236
|
}
|
|
229
237
|
}
|
|
@@ -412,6 +420,9 @@ class ItemsService {
|
|
|
412
420
|
const payloadWithPresets = this.accountability
|
|
413
421
|
? await authorizationService.validatePayload('update', this.collection, payloadAfterHooks)
|
|
414
422
|
: payloadAfterHooks;
|
|
423
|
+
if (opts === null || opts === void 0 ? void 0 : opts.preMutationException) {
|
|
424
|
+
throw opts.preMutationException;
|
|
425
|
+
}
|
|
415
426
|
await this.knex.transaction(async (trx) => {
|
|
416
427
|
const payloadService = new payload_1.PayloadService(this.collection, {
|
|
417
428
|
accountability: this.accountability,
|
|
@@ -508,14 +519,19 @@ class ItemsService {
|
|
|
508
519
|
accountability: this.accountability,
|
|
509
520
|
},
|
|
510
521
|
};
|
|
511
|
-
if (
|
|
512
|
-
|
|
522
|
+
if (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) {
|
|
523
|
+
opts.bypassEmitAction(actionEvent);
|
|
513
524
|
}
|
|
514
525
|
else {
|
|
515
|
-
|
|
526
|
+
emitter_1.default.emitAction(actionEvent.event, actionEvent.meta, actionEvent.context);
|
|
516
527
|
}
|
|
517
528
|
for (const nestedActionEvent of nestedActionEvents) {
|
|
518
|
-
|
|
529
|
+
if (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) {
|
|
530
|
+
opts.bypassEmitAction(nestedActionEvent);
|
|
531
|
+
}
|
|
532
|
+
else {
|
|
533
|
+
emitter_1.default.emitAction(nestedActionEvent.event, nestedActionEvent.meta, nestedActionEvent.context);
|
|
534
|
+
}
|
|
519
535
|
}
|
|
520
536
|
}
|
|
521
537
|
return keys;
|
|
@@ -596,6 +612,9 @@ class ItemsService {
|
|
|
596
612
|
});
|
|
597
613
|
await authorizationService.checkAccess('delete', this.collection, keys);
|
|
598
614
|
}
|
|
615
|
+
if (opts === null || opts === void 0 ? void 0 : opts.preMutationException) {
|
|
616
|
+
throw opts.preMutationException;
|
|
617
|
+
}
|
|
599
618
|
if ((opts === null || opts === void 0 ? void 0 : opts.emitEvents) !== false) {
|
|
600
619
|
await emitter_1.default.emitFilter(this.eventScope === 'items' ? ['items.delete', `${this.collection}.items.delete`] : `${this.eventScope}.delete`, keys, {
|
|
601
620
|
collection: this.collection,
|
|
@@ -642,11 +661,11 @@ class ItemsService {
|
|
|
642
661
|
accountability: this.accountability,
|
|
643
662
|
},
|
|
644
663
|
};
|
|
645
|
-
if (
|
|
646
|
-
|
|
664
|
+
if (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) {
|
|
665
|
+
opts.bypassEmitAction(actionEvent);
|
|
647
666
|
}
|
|
648
667
|
else {
|
|
649
|
-
|
|
668
|
+
emitter_1.default.emitAction(actionEvent.event, actionEvent.meta, actionEvent.context);
|
|
650
669
|
}
|
|
651
670
|
}
|
|
652
671
|
return keys;
|
|
@@ -155,6 +155,7 @@ class RelationsService {
|
|
|
155
155
|
throw new exceptions_1.InvalidPayloadException(`Field "${relation.field}" in collection "${relation.collection}" already has an associated relationship`);
|
|
156
156
|
}
|
|
157
157
|
const runPostColumnChange = await this.helpers.schema.preColumnChange();
|
|
158
|
+
this.helpers.schema.preRelationChange(relation);
|
|
158
159
|
const nestedActionEvents = [];
|
|
159
160
|
try {
|
|
160
161
|
const metaRow = {
|
|
@@ -225,6 +226,7 @@ class RelationsService {
|
|
|
225
226
|
throw new exceptions_1.InvalidPayloadException(`Field "${field}" in collection "${collection}" doesn't have a relationship.`);
|
|
226
227
|
}
|
|
227
228
|
const runPostColumnChange = await this.helpers.schema.preColumnChange();
|
|
229
|
+
this.helpers.schema.preRelationChange(relation);
|
|
228
230
|
const nestedActionEvents = [];
|
|
229
231
|
try {
|
|
230
232
|
await this.knex.transaction(async (trx) => {
|