directus 9.21.0 → 9.22.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.
Files changed (117) hide show
  1. package/dist/app.js +9 -5
  2. package/dist/app.test.d.ts +1 -0
  3. package/dist/auth/drivers/openid.js +3 -1
  4. package/dist/cli/commands/bootstrap/index.js +2 -2
  5. package/dist/cli/commands/schema/apply.js +0 -2
  6. package/dist/cli/commands/schema/snapshot.js +0 -2
  7. package/dist/cli/commands/security/secret.js +2 -2
  8. package/dist/cli/utils/create-env/env-stub.liquid +3 -3
  9. package/dist/cli/utils/create-env/index.js +5 -8
  10. package/dist/constants.d.ts +1 -0
  11. package/dist/constants.js +2 -1
  12. package/dist/controllers/assets.js +9 -7
  13. package/dist/controllers/files.js +2 -1
  14. package/dist/controllers/utils.js +2 -2
  15. package/dist/database/helpers/fn/dialects/mssql.js +2 -1
  16. package/dist/database/helpers/fn/dialects/mysql.js +3 -2
  17. package/dist/database/helpers/fn/dialects/oracle.js +2 -1
  18. package/dist/database/helpers/fn/dialects/postgres.js +2 -1
  19. package/dist/database/helpers/fn/dialects/sqlite.js +2 -1
  20. package/dist/database/helpers/fn/types.d.ts +1 -0
  21. package/dist/database/helpers/fn/types.js +5 -4
  22. package/dist/database/helpers/index.d.ts +1 -1
  23. package/dist/database/helpers/schema/dialects/mssql.d.ts +6 -0
  24. package/dist/database/helpers/schema/dialects/mssql.js +14 -0
  25. package/dist/database/helpers/schema/dialects/mysql.d.ts +5 -0
  26. package/dist/database/helpers/schema/dialects/mysql.js +19 -0
  27. package/dist/database/helpers/schema/dialects/oracle.d.ts +1 -0
  28. package/dist/database/helpers/schema/dialects/oracle.js +3 -0
  29. package/dist/database/helpers/schema/index.d.ts +2 -2
  30. package/dist/database/helpers/schema/index.js +4 -4
  31. package/dist/database/helpers/schema/types.d.ts +5 -0
  32. package/dist/database/helpers/schema/types.js +13 -0
  33. package/dist/database/index.d.ts +6 -0
  34. package/dist/database/index.js +20 -1
  35. package/dist/database/migrations/20211007A-update-presets.js +2 -2
  36. package/dist/database/migrations/run.js +7 -31
  37. package/dist/database/run-ast.js +132 -6
  38. package/dist/database/system-data/fields/index.js +2 -1
  39. package/dist/env.js +3 -2
  40. package/dist/exceptions/range-not-satisfiable.d.ts +1 -1
  41. package/dist/extensions.d.ts +7 -1
  42. package/dist/extensions.js +41 -15
  43. package/dist/logger.js +27 -1
  44. package/dist/middleware/schema.js +1 -1
  45. package/dist/operations/request/index.d.ts +1 -2
  46. package/dist/operations/request/index.js +2 -2
  47. package/dist/services/assets.d.ts +4 -3
  48. package/dist/services/assets.js +13 -11
  49. package/dist/services/authentication.js +4 -3
  50. package/dist/services/authorization.js +1 -1
  51. package/dist/services/collections.js +7 -7
  52. package/dist/services/fields.d.ts +3 -2
  53. package/dist/services/fields.js +40 -20
  54. package/dist/services/files.d.ts +4 -3
  55. package/dist/services/files.js +92 -68
  56. package/dist/services/flows.d.ts +0 -2
  57. package/dist/services/flows.js +0 -14
  58. package/dist/services/flows.test.d.ts +1 -0
  59. package/dist/services/graphql/index.d.ts +5 -1
  60. package/dist/services/graphql/index.js +29 -31
  61. package/dist/services/import-export.d.ts +4 -3
  62. package/dist/services/items.js +7 -1
  63. package/dist/services/meta.js +2 -2
  64. package/dist/services/operations.d.ts +0 -2
  65. package/dist/services/operations.js +0 -12
  66. package/dist/services/operations.test.d.ts +1 -0
  67. package/dist/services/permissions.d.ts +0 -5
  68. package/dist/services/permissions.js +0 -25
  69. package/dist/services/permissions.test.d.ts +1 -0
  70. package/dist/services/relations.d.ts +2 -2
  71. package/dist/services/relations.js +24 -16
  72. package/dist/services/roles.js +0 -3
  73. package/dist/services/roles.test.d.ts +1 -0
  74. package/dist/services/server.js +8 -6
  75. package/dist/services/shares.js +2 -2
  76. package/dist/services/specifications.js +12 -1
  77. package/dist/services/users.js +10 -4
  78. package/dist/services/webhooks.d.ts +0 -2
  79. package/dist/services/webhooks.js +0 -10
  80. package/dist/services/webhooks.test.d.ts +1 -0
  81. package/dist/storage/get-storage-driver.d.ts +3 -0
  82. package/dist/storage/get-storage-driver.js +20 -0
  83. package/dist/storage/get-storage-driver.test.d.ts +1 -0
  84. package/dist/storage/index.d.ts +5 -0
  85. package/dist/storage/index.js +20 -0
  86. package/dist/storage/index.test.d.ts +1 -0
  87. package/dist/storage/register-drivers.d.ts +2 -0
  88. package/dist/storage/register-drivers.js +22 -0
  89. package/dist/storage/register-drivers.test.d.ts +1 -0
  90. package/dist/storage/register-locations.d.ts +2 -0
  91. package/dist/storage/register-locations.js +17 -0
  92. package/dist/storage/register-locations.test.d.ts +1 -0
  93. package/dist/utils/apply-query.d.ts +27 -3
  94. package/dist/utils/apply-query.js +180 -127
  95. package/dist/utils/apply-snapshot.js +32 -13
  96. package/dist/utils/dynamic-import.d.ts +1 -0
  97. package/dist/utils/dynamic-import.js +7 -0
  98. package/dist/utils/get-collection-from-alias.d.ts +6 -0
  99. package/dist/utils/get-collection-from-alias.js +15 -0
  100. package/dist/utils/get-collection-from-alias.test.d.ts +1 -0
  101. package/dist/utils/get-column-path.d.ts +14 -8
  102. package/dist/utils/get-column-path.js +24 -7
  103. package/dist/utils/get-column.d.ts +8 -1
  104. package/dist/utils/get-column.js +10 -3
  105. package/dist/utils/get-config-from-env.js +3 -2
  106. package/dist/utils/get-default-value.d.ts +1 -1
  107. package/dist/utils/get-schema.d.ts +6 -2
  108. package/dist/utils/get-schema.js +1 -1
  109. package/dist/utils/get-snapshot.js +1 -1
  110. package/dist/utils/parse-image-metadata.d.ts +3 -0
  111. package/dist/utils/parse-image-metadata.js +73 -0
  112. package/dist/utils/track.js +2 -2
  113. package/dist/utils/validate-env.js +3 -2
  114. package/dist/webhooks.js +2 -2
  115. package/package.json +18 -22
  116. package/dist/storage.d.ts +0 -3
  117. package/dist/storage.js +0 -61
@@ -8,7 +8,6 @@ const types_1 = require("@directus/shared/types");
8
8
  const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
9
9
  const lodash_1 = require("lodash");
10
10
  const ms_1 = __importDefault(require("ms"));
11
- const nanoid_1 = require("nanoid");
12
11
  const perf_hooks_1 = require("perf_hooks");
13
12
  const auth_1 = require("../auth");
14
13
  const constants_1 = require("../constants");
@@ -37,6 +36,7 @@ class AuthenticationService {
37
36
  */
38
37
  async login(providerName = constants_1.DEFAULT_AUTH_PROVIDER, payload, otp) {
39
38
  var _a, _b, _c;
39
+ const { nanoid } = await import('nanoid');
40
40
  const STALL_TIME = env_1.default.LOGIN_STALL_TIME;
41
41
  const timeStart = perf_hooks_1.performance.now();
42
42
  const provider = (0, auth_1.getAuthProvider)(providerName);
@@ -151,7 +151,7 @@ class AuthenticationService {
151
151
  expiresIn: env_1.default.ACCESS_TOKEN_TTL,
152
152
  issuer: 'directus',
153
153
  });
154
- const refreshToken = (0, nanoid_1.nanoid)(64);
154
+ const refreshToken = nanoid(64);
155
155
  const refreshTokenExpiration = new Date(Date.now() + (0, ms_1.default)(env_1.default.REFRESH_TOKEN_TTL));
156
156
  await this.knex('directus_sessions').insert({
157
157
  token: refreshToken,
@@ -187,6 +187,7 @@ class AuthenticationService {
187
187
  };
188
188
  }
189
189
  async refresh(refreshToken) {
190
+ const { nanoid } = await import('nanoid');
190
191
  if (!refreshToken) {
191
192
  throw new exceptions_1.InvalidCredentialsException();
192
193
  }
@@ -280,7 +281,7 @@ class AuthenticationService {
280
281
  expiresIn: env_1.default.ACCESS_TOKEN_TTL,
281
282
  issuer: 'directus',
282
283
  });
283
- const newRefreshToken = (0, nanoid_1.nanoid)(64);
284
+ const newRefreshToken = nanoid(64);
284
285
  const refreshTokenExpiration = new Date(Date.now() + (0, ms_1.default)(env_1.default.REFRESH_TOKEN_TTL));
285
286
  await this.knex('directus_sessions')
286
287
  .update({
@@ -178,7 +178,7 @@ class AuthorizationService {
178
178
  (result[relation.collection] || (result[relation.collection] = new Set())).add(relation.field);
179
179
  }
180
180
  }
181
- // m2a filter in the form of `item:collection`
181
+ // a2o filter in the form of `item:collection`
182
182
  else if (filterKey.includes(':')) {
183
183
  const [field, collectionScope] = filterKey.split(':');
184
184
  if (collection) {
@@ -157,7 +157,7 @@ class CollectionsService {
157
157
  await (0, cache_1.clearSystemCache)();
158
158
  }
159
159
  if ((opts === null || opts === void 0 ? void 0 : opts.emitEvents) !== false && nestedActionEvents.length > 0) {
160
- const updatedSchema = await (0, get_schema_1.getSchema)({ accountability: this.accountability || undefined });
160
+ const updatedSchema = await (0, get_schema_1.getSchema)();
161
161
  for (const nestedActionEvent of nestedActionEvents) {
162
162
  nestedActionEvent.context.schema = updatedSchema;
163
163
  emitter_1.default.emitAction(nestedActionEvent.event, nestedActionEvent.meta, nestedActionEvent.context);
@@ -198,7 +198,7 @@ class CollectionsService {
198
198
  await (0, cache_1.clearSystemCache)();
199
199
  }
200
200
  if ((opts === null || opts === void 0 ? void 0 : opts.emitEvents) !== false && nestedActionEvents.length > 0) {
201
- const updatedSchema = await (0, get_schema_1.getSchema)({ accountability: this.accountability || undefined });
201
+ const updatedSchema = await (0, get_schema_1.getSchema)();
202
202
  for (const nestedActionEvent of nestedActionEvents) {
203
203
  nestedActionEvent.context.schema = updatedSchema;
204
204
  emitter_1.default.emitAction(nestedActionEvent.event, nestedActionEvent.meta, nestedActionEvent.context);
@@ -343,7 +343,7 @@ class CollectionsService {
343
343
  await (0, cache_1.clearSystemCache)();
344
344
  }
345
345
  if ((opts === null || opts === void 0 ? void 0 : opts.emitEvents) !== false && nestedActionEvents.length > 0) {
346
- const updatedSchema = await (0, get_schema_1.getSchema)({ accountability: this.accountability || undefined });
346
+ const updatedSchema = await (0, get_schema_1.getSchema)();
347
347
  for (const nestedActionEvent of nestedActionEvents) {
348
348
  nestedActionEvent.context.schema = updatedSchema;
349
349
  emitter_1.default.emitAction(nestedActionEvent.event, nestedActionEvent.meta, nestedActionEvent.context);
@@ -391,7 +391,7 @@ class CollectionsService {
391
391
  await (0, cache_1.clearSystemCache)();
392
392
  }
393
393
  if ((opts === null || opts === void 0 ? void 0 : opts.emitEvents) !== false && nestedActionEvents.length > 0) {
394
- const updatedSchema = await (0, get_schema_1.getSchema)({ accountability: this.accountability || undefined });
394
+ const updatedSchema = await (0, get_schema_1.getSchema)();
395
395
  for (const nestedActionEvent of nestedActionEvents) {
396
396
  nestedActionEvent.context.schema = updatedSchema;
397
397
  emitter_1.default.emitAction(nestedActionEvent.event, nestedActionEvent.meta, nestedActionEvent.context);
@@ -433,7 +433,7 @@ class CollectionsService {
433
433
  await (0, cache_1.clearSystemCache)();
434
434
  }
435
435
  if ((opts === null || opts === void 0 ? void 0 : opts.emitEvents) !== false && nestedActionEvents.length > 0) {
436
- const updatedSchema = await (0, get_schema_1.getSchema)({ accountability: this.accountability || undefined });
436
+ const updatedSchema = await (0, get_schema_1.getSchema)();
437
437
  for (const nestedActionEvent of nestedActionEvents) {
438
438
  nestedActionEvent.context.schema = updatedSchema;
439
439
  emitter_1.default.emitAction(nestedActionEvent.event, nestedActionEvent.meta, nestedActionEvent.context);
@@ -538,7 +538,7 @@ class CollectionsService {
538
538
  await (0, cache_1.clearSystemCache)();
539
539
  }
540
540
  if ((opts === null || opts === void 0 ? void 0 : opts.emitEvents) !== false && nestedActionEvents.length > 0) {
541
- const updatedSchema = await (0, get_schema_1.getSchema)({ accountability: this.accountability || undefined });
541
+ const updatedSchema = await (0, get_schema_1.getSchema)();
542
542
  for (const nestedActionEvent of nestedActionEvents) {
543
543
  nestedActionEvent.context.schema = updatedSchema;
544
544
  emitter_1.default.emitAction(nestedActionEvent.event, nestedActionEvent.meta, nestedActionEvent.context);
@@ -579,7 +579,7 @@ class CollectionsService {
579
579
  await (0, cache_1.clearSystemCache)();
580
580
  }
581
581
  if ((opts === null || opts === void 0 ? void 0 : opts.emitEvents) !== false && nestedActionEvents.length > 0) {
582
- const updatedSchema = await (0, get_schema_1.getSchema)({ accountability: this.accountability || undefined });
582
+ const updatedSchema = await (0, get_schema_1.getSchema)();
583
583
  for (const nestedActionEvent of nestedActionEvents) {
584
584
  nestedActionEvent.context.schema = updatedSchema;
585
585
  emitter_1.default.emitAction(nestedActionEvent.event, nestedActionEvent.meta, nestedActionEvent.context);
@@ -24,8 +24,9 @@ export declare class FieldsService {
24
24
  createField(collection: string, field: Partial<Field> & {
25
25
  field: string;
26
26
  type: Type | null;
27
- }, table?: Knex.CreateTableBuilder): Promise<void>;
28
- updateField(collection: string, field: RawField): Promise<string>;
27
+ }, table?: Knex.CreateTableBuilder, // allows collection creation to
28
+ opts?: MutationOptions): Promise<void>;
29
+ updateField(collection: string, field: RawField, opts?: MutationOptions): Promise<string>;
29
30
  deleteField(collection: string, field: string, opts?: MutationOptions): Promise<void>;
30
31
  addColumnToTable(table: Knex.CreateTableBuilder, field: RawField | Field, alter?: Column | null): void;
31
32
  }
@@ -218,8 +218,8 @@ class FieldsService {
218
218
  };
219
219
  return data;
220
220
  }
221
- async createField(collection, field, table // allows collection creation to
222
- ) {
221
+ async createField(collection, field, table, // allows collection creation to
222
+ opts) {
223
223
  if (this.accountability && this.accountability.admin !== true) {
224
224
  throw new exceptions_1.ForbiddenException();
225
225
  }
@@ -267,7 +267,7 @@ class FieldsService {
267
267
  field: hookAdjustedField.field,
268
268
  }, { emitEvents: false });
269
269
  }
270
- nestedActionEvents.push({
270
+ const actionEvent = {
271
271
  event: 'fields.create',
272
272
  meta: {
273
273
  payload: hookAdjustedField,
@@ -279,25 +279,35 @@ class FieldsService {
279
279
  schema: this.schema,
280
280
  accountability: this.accountability,
281
281
  },
282
- });
282
+ };
283
+ if (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) {
284
+ opts.bypassEmitAction(actionEvent);
285
+ }
286
+ else {
287
+ nestedActionEvents.push(actionEvent);
288
+ }
283
289
  });
284
290
  }
285
291
  finally {
286
292
  if (runPostColumnChange) {
287
293
  await this.helpers.schema.postColumnChange();
288
294
  }
289
- if (this.cache && env_1.default.CACHE_AUTO_PURGE) {
295
+ if (this.cache && env_1.default.CACHE_AUTO_PURGE && (opts === null || opts === void 0 ? void 0 : opts.autoPurgeCache) !== false) {
290
296
  await this.cache.clear();
291
297
  }
292
- await (0, cache_1.clearSystemCache)();
293
- const updatedSchema = await (0, get_schema_1.getSchema)({ accountability: this.accountability || undefined });
294
- for (const nestedActionEvent of nestedActionEvents) {
295
- nestedActionEvent.context.schema = updatedSchema;
296
- emitter_1.default.emitAction(nestedActionEvent.event, nestedActionEvent.meta, nestedActionEvent.context);
298
+ if ((opts === null || opts === void 0 ? void 0 : opts.autoPurgeSystemCache) !== false) {
299
+ await (0, cache_1.clearSystemCache)();
300
+ }
301
+ if ((opts === null || opts === void 0 ? void 0 : opts.emitEvents) !== false && nestedActionEvents.length > 0) {
302
+ const updatedSchema = await (0, get_schema_1.getSchema)();
303
+ for (const nestedActionEvent of nestedActionEvents) {
304
+ nestedActionEvent.context.schema = updatedSchema;
305
+ emitter_1.default.emitAction(nestedActionEvent.event, nestedActionEvent.meta, nestedActionEvent.context);
306
+ }
297
307
  }
298
308
  }
299
309
  }
300
- async updateField(collection, field) {
310
+ async updateField(collection, field, opts) {
301
311
  var _a, _b, _c;
302
312
  if (this.accountability && this.accountability.admin !== true) {
303
313
  throw new exceptions_1.ForbiddenException();
@@ -353,7 +363,7 @@ class FieldsService {
353
363
  }, { emitEvents: false });
354
364
  }
355
365
  }
356
- nestedActionEvents.push({
366
+ const actionEvent = {
357
367
  event: 'fields.update',
358
368
  meta: {
359
369
  payload: hookAdjustedField,
@@ -365,21 +375,31 @@ class FieldsService {
365
375
  schema: this.schema,
366
376
  accountability: this.accountability,
367
377
  },
368
- });
378
+ };
379
+ if (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) {
380
+ opts.bypassEmitAction(actionEvent);
381
+ }
382
+ else {
383
+ nestedActionEvents.push(actionEvent);
384
+ }
369
385
  return field.field;
370
386
  }
371
387
  finally {
372
388
  if (runPostColumnChange) {
373
389
  await this.helpers.schema.postColumnChange();
374
390
  }
375
- if (this.cache && env_1.default.CACHE_AUTO_PURGE) {
391
+ if (this.cache && env_1.default.CACHE_AUTO_PURGE && (opts === null || opts === void 0 ? void 0 : opts.autoPurgeCache) !== false) {
376
392
  await this.cache.clear();
377
393
  }
378
- await (0, cache_1.clearSystemCache)();
379
- const updatedSchema = await (0, get_schema_1.getSchema)({ accountability: this.accountability || undefined });
380
- for (const nestedActionEvent of nestedActionEvents) {
381
- nestedActionEvent.context.schema = updatedSchema;
382
- emitter_1.default.emitAction(nestedActionEvent.event, nestedActionEvent.meta, nestedActionEvent.context);
394
+ if ((opts === null || opts === void 0 ? void 0 : opts.autoPurgeSystemCache) !== false) {
395
+ await (0, cache_1.clearSystemCache)();
396
+ }
397
+ if ((opts === null || opts === void 0 ? void 0 : opts.emitEvents) !== false && nestedActionEvents.length > 0) {
398
+ const updatedSchema = await (0, get_schema_1.getSchema)();
399
+ for (const nestedActionEvent of nestedActionEvents) {
400
+ nestedActionEvent.context.schema = updatedSchema;
401
+ emitter_1.default.emitAction(nestedActionEvent.event, nestedActionEvent.meta, nestedActionEvent.context);
402
+ }
383
403
  }
384
404
  }
385
405
  }
@@ -507,7 +527,7 @@ class FieldsService {
507
527
  await (0, cache_1.clearSystemCache)();
508
528
  }
509
529
  if ((opts === null || opts === void 0 ? void 0 : opts.emitEvents) !== false && nestedActionEvents.length > 0) {
510
- const updatedSchema = await (0, get_schema_1.getSchema)({ accountability: this.accountability || undefined });
530
+ const updatedSchema = await (0, get_schema_1.getSchema)();
511
531
  for (const nestedActionEvent of nestedActionEvents) {
512
532
  nestedActionEvent.context.schema = updatedSchema;
513
533
  emitter_1.default.emitAction(nestedActionEvent.event, nestedActionEvent.meta, nestedActionEvent.context);
@@ -1,19 +1,20 @@
1
1
  /// <reference types="node" />
2
- import { AbstractServiceOptions, File, PrimaryKey, MutationOptions, Metadata } from '../types';
2
+ import type { Readable } from 'node:stream';
3
+ import { AbstractServiceOptions, File, Metadata, MutationOptions, PrimaryKey } from '../types';
3
4
  import { ItemsService } from './items';
4
5
  export declare class FilesService extends ItemsService {
5
6
  constructor(options: AbstractServiceOptions);
6
7
  /**
7
8
  * Upload a single new file to the configured storage adapter
8
9
  */
9
- uploadOne(stream: NodeJS.ReadableStream, data: Partial<File> & {
10
+ uploadOne(stream: Readable, data: Partial<File> & {
10
11
  filename_download: string;
11
12
  storage: string;
12
13
  }, primaryKey?: PrimaryKey, opts?: MutationOptions): Promise<PrimaryKey>;
13
14
  /**
14
15
  * Extract metadata from a buffer's content
15
16
  */
16
- getMetadata(bufferContent: any, allowList?: any): Promise<Metadata>;
17
+ getMetadata(stream: Readable, allowList?: any): Promise<Metadata>;
17
18
  /**
18
19
  * Import a single file from an external URL
19
20
  */
@@ -27,26 +27,29 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
27
27
  };
28
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
29
  exports.FilesService = void 0;
30
- const format_title_1 = __importDefault(require("@directus/format-title"));
31
- const axios_1 = __importDefault(require("axios"));
32
- const exifr_1 = __importDefault(require("exifr"));
30
+ const utils_1 = require("@directus/shared/utils");
31
+ const dns_1 = require("dns");
32
+ const encodeurl_1 = __importDefault(require("encodeurl"));
33
+ const exif_reader_1 = __importDefault(require("exif-reader"));
34
+ const icc_1 = require("icc");
33
35
  const lodash_1 = require("lodash");
34
36
  const mime_types_1 = require("mime-types");
37
+ const net_1 = __importDefault(require("net"));
38
+ const promises_1 = require("node:stream/promises");
39
+ const os_1 = __importDefault(require("os"));
35
40
  const path_1 = __importDefault(require("path"));
36
41
  const sharp_1 = __importDefault(require("sharp"));
37
42
  const url_1 = __importStar(require("url"));
38
43
  const util_1 = require("util");
39
- const dns_1 = require("dns");
40
44
  const emitter_1 = __importDefault(require("../emitter"));
41
45
  const env_1 = __importDefault(require("../env"));
42
46
  const exceptions_1 = require("../exceptions");
43
47
  const logger_1 = __importDefault(require("../logger"));
44
- const storage_1 = __importDefault(require("../storage"));
45
- const utils_1 = require("@directus/shared/utils");
48
+ const storage_1 = require("../storage");
49
+ const parse_image_metadata_1 = require("../utils/parse-image-metadata");
46
50
  const items_1 = require("./items");
47
- const net_1 = __importDefault(require("net"));
48
- const os_1 = __importDefault(require("os"));
49
- const encodeurl_1 = __importDefault(require("encodeurl"));
51
+ // @ts-ignore
52
+ const format_title_1 = __importDefault(require("@directus/format-title"));
50
53
  const lookupDNS = (0, util_1.promisify)(dns_1.lookup);
51
54
  class FilesService extends items_1.ItemsService {
52
55
  constructor(options) {
@@ -57,6 +60,7 @@ class FilesService extends items_1.ItemsService {
57
60
  */
58
61
  async uploadOne(stream, data, primaryKey, opts) {
59
62
  var _a, _b, _c, _d, _e, _f;
63
+ const storage = await (0, storage_1.getStorage)();
60
64
  const payload = (0, lodash_1.clone)(data);
61
65
  if ('folder' in payload === false) {
62
66
  const settings = await this.knex.select('storage_default_folder').from('directus_settings').first();
@@ -68,9 +72,9 @@ class FilesService extends items_1.ItemsService {
68
72
  await this.updateOne(primaryKey, payload, { emitEvents: false });
69
73
  // If the file you're uploading already exists, we'll consider this upload a replace. In that case, we'll
70
74
  // delete the previously saved file and thumbnails to ensure they're generated fresh
71
- const disk = storage_1.default.disk(payload.storage);
72
- for await (const file of disk.flatList(String(primaryKey))) {
73
- await disk.delete(file.path);
75
+ const disk = storage.location(payload.storage);
76
+ for await (const filepath of disk.list(String(primaryKey))) {
77
+ await disk.delete(filepath);
74
78
  }
75
79
  }
76
80
  else {
@@ -82,18 +86,18 @@ class FilesService extends items_1.ItemsService {
82
86
  payload.type = 'application/octet-stream';
83
87
  }
84
88
  try {
85
- await storage_1.default.disk(data.storage).put(payload.filename_disk, stream, payload.type);
89
+ await storage.location(data.storage).write(payload.filename_disk, stream, payload.type);
86
90
  }
87
91
  catch (err) {
88
92
  logger_1.default.warn(`Couldn't save file ${payload.filename_disk}`);
89
93
  logger_1.default.warn(err);
90
94
  throw new exceptions_1.ServiceUnavailableException(`Couldn't save file ${payload.filename_disk}`, { service: 'files' });
91
95
  }
92
- const { size } = await storage_1.default.disk(data.storage).getStat(payload.filename_disk);
96
+ const { size } = await storage.location(data.storage).stat(payload.filename_disk);
93
97
  payload.filesize = size;
94
98
  if (['image/jpeg', 'image/png', 'image/webp', 'image/gif', 'image/tiff'].includes(payload.type)) {
95
- const buffer = await storage_1.default.disk(data.storage).getBuffer(payload.filename_disk);
96
- const { height, width, description, title, tags, metadata } = await this.getMetadata(buffer.content);
99
+ const stream = await storage.location(data.storage).read(payload.filename_disk);
100
+ const { height, width, description, title, tags, metadata } = await this.getMetadata(stream);
97
101
  (_a = payload.height) !== null && _a !== void 0 ? _a : (payload.height = height);
98
102
  (_b = payload.width) !== null && _b !== void 0 ? _b : (payload.width = width);
99
103
  (_c = payload.description) !== null && _c !== void 0 ? _c : (payload.description = description);
@@ -127,60 +131,79 @@ class FilesService extends items_1.ItemsService {
127
131
  /**
128
132
  * Extract metadata from a buffer's content
129
133
  */
130
- async getMetadata(bufferContent, allowList = env_1.default.FILE_METADATA_ALLOW_LIST) {
131
- const metadata = {};
132
- try {
133
- const sharpMetadata = await (0, sharp_1.default)(bufferContent, {}).metadata();
134
- if (sharpMetadata.orientation && sharpMetadata.orientation >= 5) {
135
- metadata.height = sharpMetadata.width;
136
- metadata.width = sharpMetadata.height;
137
- }
138
- else {
139
- metadata.width = sharpMetadata.width;
140
- metadata.height = sharpMetadata.height;
141
- }
142
- }
143
- catch (err) {
144
- logger_1.default.warn(`Couldn't extract sharp metadata from file`);
145
- logger_1.default.warn(err);
146
- }
147
- try {
148
- const exifrMetadata = await exifr_1.default.parse(bufferContent, {
149
- icc: false,
150
- iptc: true,
151
- ifd1: true,
152
- interop: true,
153
- translateValues: true,
154
- reviveValues: true,
155
- mergeOutput: false,
156
- });
157
- if (allowList === '*' || (allowList === null || allowList === void 0 ? void 0 : allowList[0]) === '*') {
158
- metadata.metadata = exifrMetadata;
159
- }
160
- else {
161
- metadata.metadata = (0, lodash_1.pick)(exifrMetadata, allowList);
162
- }
163
- if (!metadata.description && (exifrMetadata === null || exifrMetadata === void 0 ? void 0 : exifrMetadata.Caption)) {
164
- metadata.description = exifrMetadata.Caption;
165
- }
166
- if (exifrMetadata === null || exifrMetadata === void 0 ? void 0 : exifrMetadata.Headline) {
167
- metadata.title = exifrMetadata.Headline;
168
- }
169
- if (exifrMetadata === null || exifrMetadata === void 0 ? void 0 : exifrMetadata.Keywords) {
170
- metadata.tags = exifrMetadata.Keywords;
171
- }
172
- }
173
- catch (err) {
174
- logger_1.default.warn(`Couldn't extract EXIF metadata from file`);
175
- logger_1.default.warn(err);
176
- }
177
- return metadata;
134
+ async getMetadata(stream, allowList = env_1.default.FILE_METADATA_ALLOW_LIST) {
135
+ return new Promise((resolve, reject) => {
136
+ (0, promises_1.pipeline)(stream, (0, sharp_1.default)().metadata(async (err, sharpMetadata) => {
137
+ var _a, _b, _c, _d;
138
+ if (err)
139
+ reject(err);
140
+ const metadata = {};
141
+ if (sharpMetadata.orientation && sharpMetadata.orientation >= 5) {
142
+ metadata.height = sharpMetadata.width;
143
+ metadata.width = sharpMetadata.height;
144
+ }
145
+ else {
146
+ metadata.width = sharpMetadata.width;
147
+ metadata.height = sharpMetadata.height;
148
+ }
149
+ // Backward-compatible layout as it used to be with 'exifr'
150
+ const fullMetadata = {};
151
+ if (sharpMetadata.exif) {
152
+ const { image, thumbnail, interoperability, ...rest } = (0, exif_reader_1.default)(sharpMetadata.exif);
153
+ if (image) {
154
+ fullMetadata.ifd0 = image;
155
+ }
156
+ if (thumbnail) {
157
+ fullMetadata.ifd1 = thumbnail;
158
+ }
159
+ if (interoperability) {
160
+ fullMetadata.interop = interoperability;
161
+ }
162
+ Object.assign(fullMetadata, rest);
163
+ }
164
+ if (sharpMetadata.icc) {
165
+ fullMetadata.icc = (0, icc_1.parse)(sharpMetadata.icc);
166
+ }
167
+ if (sharpMetadata.iptc) {
168
+ fullMetadata.iptc = (0, parse_image_metadata_1.parseIptc)(sharpMetadata.iptc);
169
+ }
170
+ if (sharpMetadata.xmp) {
171
+ fullMetadata.xmp = (0, parse_image_metadata_1.parseXmp)(sharpMetadata.xmp);
172
+ }
173
+ 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
+ metadata.description = (_b = fullMetadata.iptc) === null || _b === void 0 ? void 0 : _b.Caption;
175
+ }
176
+ if (((_c = fullMetadata === null || fullMetadata === void 0 ? void 0 : fullMetadata.iptc) === null || _c === void 0 ? void 0 : _c.Headline) && typeof fullMetadata.iptc.Headline === 'string') {
177
+ metadata.title = fullMetadata.iptc.Headline;
178
+ }
179
+ if ((_d = fullMetadata === null || fullMetadata === void 0 ? void 0 : fullMetadata.iptc) === null || _d === void 0 ? void 0 : _d.Keywords) {
180
+ metadata.tags = fullMetadata.iptc.Keywords;
181
+ }
182
+ if (allowList === '*' || (allowList === null || allowList === void 0 ? void 0 : allowList[0]) === '*') {
183
+ metadata.metadata = fullMetadata;
184
+ }
185
+ else {
186
+ metadata.metadata = (0, lodash_1.pick)(fullMetadata, allowList);
187
+ }
188
+ // Fix (incorrectly parsed?) values starting / ending with spaces,
189
+ // limited to one level and string values only
190
+ for (const section of Object.keys(metadata.metadata)) {
191
+ for (const [key, value] of Object.entries(metadata.metadata[section])) {
192
+ if (typeof value === 'string') {
193
+ metadata.metadata[section][key] = value.trim();
194
+ }
195
+ }
196
+ }
197
+ resolve(metadata);
198
+ }));
199
+ });
178
200
  }
179
201
  /**
180
202
  * Import a single file from an external URL
181
203
  */
182
204
  async importOne(importURL, body) {
183
205
  var _a, _b, _c;
206
+ const axios = (await import('axios')).default;
184
207
  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');
185
208
  if (this.accountability && ((_c = this.accountability) === null || _c === void 0 ? void 0 : _c.admin) !== true && !fileCreatePermissions) {
186
209
  throw new exceptions_1.ForbiddenException();
@@ -230,7 +253,7 @@ class FilesService extends items_1.ItemsService {
230
253
  }
231
254
  let fileResponse;
232
255
  try {
233
- fileResponse = await axios_1.default.get((0, encodeurl_1.default)(importURL), {
256
+ fileResponse = await axios.get((0, encodeurl_1.default)(importURL), {
234
257
  responseType: 'stream',
235
258
  });
236
259
  }
@@ -273,16 +296,17 @@ class FilesService extends items_1.ItemsService {
273
296
  * Delete multiple files
274
297
  */
275
298
  async deleteMany(keys, opts) {
299
+ const storage = await (0, storage_1.getStorage)();
276
300
  const files = await super.readMany(keys, { fields: ['id', 'storage'], limit: -1 });
277
301
  if (!files) {
278
302
  throw new exceptions_1.ForbiddenException();
279
303
  }
280
304
  await super.deleteMany(keys);
281
305
  for (const file of files) {
282
- const disk = storage_1.default.disk(file.storage);
306
+ const disk = storage.location(file.storage);
283
307
  // Delete file + thumbnails
284
- for await (const { path } of disk.flatList(file.id)) {
285
- await disk.delete(path);
308
+ for await (const filepath of disk.list(file.id)) {
309
+ await disk.delete(filepath);
286
310
  }
287
311
  }
288
312
  if (this.cache && env_1.default.CACHE_AUTO_PURGE && (opts === null || opts === void 0 ? void 0 : opts.autoPurgeCache) !== false) {
@@ -5,9 +5,7 @@ export declare class FlowsService extends ItemsService<FlowRaw> {
5
5
  constructor(options: AbstractServiceOptions);
6
6
  createOne(data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey>;
7
7
  createMany(data: Partial<Item>[], opts?: MutationOptions): Promise<PrimaryKey[]>;
8
- updateOne(key: PrimaryKey, data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey>;
9
8
  updateBatch(data: Partial<Item>[], opts?: MutationOptions): Promise<PrimaryKey[]>;
10
9
  updateMany(keys: PrimaryKey[], data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey[]>;
11
- deleteOne(key: PrimaryKey, opts?: MutationOptions): Promise<PrimaryKey>;
12
10
  deleteMany(keys: PrimaryKey[], opts?: MutationOptions): Promise<PrimaryKey[]>;
13
11
  }
@@ -19,12 +19,6 @@ class FlowsService extends items_1.ItemsService {
19
19
  await flowManager.reload();
20
20
  return result;
21
21
  }
22
- async updateOne(key, data, opts) {
23
- const flowManager = (0, flows_1.getFlowManager)();
24
- const result = await super.updateOne(key, data, opts);
25
- await flowManager.reload();
26
- return result;
27
- }
28
22
  async updateBatch(data, opts) {
29
23
  const flowManager = (0, flows_1.getFlowManager)();
30
24
  const result = await super.updateBatch(data, opts);
@@ -37,14 +31,6 @@ class FlowsService extends items_1.ItemsService {
37
31
  await flowManager.reload();
38
32
  return result;
39
33
  }
40
- async deleteOne(key, opts) {
41
- const flowManager = (0, flows_1.getFlowManager)();
42
- // this is to prevent foreign key constraint error on directus_operations resolve/reject during cascade deletion
43
- await this.knex('directus_operations').update({ resolve: null, reject: null }).where('flow', key);
44
- const result = await super.deleteOne(key, opts);
45
- await flowManager.reload();
46
- return result;
47
- }
48
34
  async deleteMany(keys, opts) {
49
35
  const flowManager = (0, flows_1.getFlowManager)();
50
36
  // this is to prevent foreign key constraint error on directus_operations resolve/reject during cascade deletion
@@ -0,0 +1 @@
1
+ export {};
@@ -1,5 +1,5 @@
1
1
  import { BaseException } from '@directus/shared/exceptions';
2
- import { Accountability, Query, SchemaOverview } from '@directus/shared/types';
2
+ import { Accountability, Filter, Query, SchemaOverview } from '@directus/shared/types';
3
3
  import { ArgumentNode, FormattedExecutionResult, FragmentDefinitionNode, GraphQLError, GraphQLResolveInfo, GraphQLSchema, SelectionNode } from 'graphql';
4
4
  import { ObjectTypeComposer, SchemaComposer } from 'graphql-compose';
5
5
  import { Knex } from 'knex';
@@ -54,6 +54,10 @@ export declare class GraphQLService {
54
54
  * Resolve the aggregation query based on the requested aggregated fields
55
55
  */
56
56
  getAggregateQuery(rawQuery: Query, selections: readonly SelectionNode[]): Query;
57
+ /**
58
+ * Replace functions from GraphQL format to Directus-Filter format
59
+ */
60
+ replaceFuncs(filter?: Filter | null): null | undefined | Filter;
57
61
  /**
58
62
  * Convert Directus-Exception into a GraphQL format, so it can be returned by GraphQL properly.
59
63
  */