directus 9.21.2 → 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 (103) hide show
  1. package/dist/app.js +9 -5
  2. package/dist/app.test.d.ts +1 -0
  3. package/dist/cli/commands/bootstrap/index.js +2 -2
  4. package/dist/cli/commands/security/secret.js +2 -2
  5. package/dist/cli/utils/create-env/env-stub.liquid +3 -3
  6. package/dist/cli/utils/create-env/index.js +5 -8
  7. package/dist/constants.d.ts +1 -0
  8. package/dist/constants.js +2 -1
  9. package/dist/controllers/assets.js +9 -7
  10. package/dist/controllers/files.js +2 -1
  11. package/dist/controllers/utils.js +2 -2
  12. package/dist/database/helpers/fn/dialects/mssql.js +2 -1
  13. package/dist/database/helpers/fn/dialects/mysql.js +2 -1
  14. package/dist/database/helpers/fn/dialects/oracle.js +2 -1
  15. package/dist/database/helpers/fn/dialects/postgres.js +2 -1
  16. package/dist/database/helpers/fn/dialects/sqlite.js +2 -1
  17. package/dist/database/helpers/fn/types.d.ts +1 -0
  18. package/dist/database/helpers/fn/types.js +5 -4
  19. package/dist/database/helpers/index.d.ts +1 -1
  20. package/dist/database/helpers/schema/dialects/mssql.d.ts +6 -0
  21. package/dist/database/helpers/schema/dialects/mssql.js +14 -0
  22. package/dist/database/helpers/schema/dialects/mysql.d.ts +5 -0
  23. package/dist/database/helpers/schema/dialects/mysql.js +19 -0
  24. package/dist/database/helpers/schema/dialects/oracle.d.ts +1 -0
  25. package/dist/database/helpers/schema/dialects/oracle.js +3 -0
  26. package/dist/database/helpers/schema/index.d.ts +2 -2
  27. package/dist/database/helpers/schema/index.js +4 -4
  28. package/dist/database/helpers/schema/types.d.ts +5 -0
  29. package/dist/database/helpers/schema/types.js +13 -0
  30. package/dist/database/index.d.ts +6 -0
  31. package/dist/database/index.js +20 -1
  32. package/dist/database/migrations/20211007A-update-presets.js +2 -2
  33. package/dist/database/migrations/run.js +7 -31
  34. package/dist/database/run-ast.js +132 -6
  35. package/dist/database/system-data/fields/index.js +2 -1
  36. package/dist/env.js +3 -2
  37. package/dist/exceptions/range-not-satisfiable.d.ts +1 -1
  38. package/dist/extensions.d.ts +7 -1
  39. package/dist/extensions.js +41 -15
  40. package/dist/logger.js +27 -1
  41. package/dist/operations/request/index.d.ts +1 -2
  42. package/dist/operations/request/index.js +2 -2
  43. package/dist/services/assets.d.ts +4 -3
  44. package/dist/services/assets.js +13 -11
  45. package/dist/services/authentication.js +4 -3
  46. package/dist/services/authorization.js +1 -1
  47. package/dist/services/files.d.ts +4 -3
  48. package/dist/services/files.js +92 -68
  49. package/dist/services/flows.d.ts +0 -2
  50. package/dist/services/flows.js +0 -14
  51. package/dist/services/flows.test.d.ts +1 -0
  52. package/dist/services/graphql/index.d.ts +5 -1
  53. package/dist/services/graphql/index.js +29 -31
  54. package/dist/services/import-export.d.ts +4 -3
  55. package/dist/services/items.js +7 -1
  56. package/dist/services/meta.js +2 -2
  57. package/dist/services/operations.d.ts +0 -2
  58. package/dist/services/operations.js +0 -12
  59. package/dist/services/operations.test.d.ts +1 -0
  60. package/dist/services/permissions.d.ts +0 -5
  61. package/dist/services/permissions.js +0 -25
  62. package/dist/services/permissions.test.d.ts +1 -0
  63. package/dist/services/roles.js +0 -3
  64. package/dist/services/roles.test.d.ts +1 -0
  65. package/dist/services/server.js +8 -6
  66. package/dist/services/shares.js +2 -2
  67. package/dist/services/specifications.js +12 -1
  68. package/dist/services/webhooks.d.ts +0 -2
  69. package/dist/services/webhooks.js +0 -10
  70. package/dist/services/webhooks.test.d.ts +1 -0
  71. package/dist/storage/get-storage-driver.d.ts +3 -0
  72. package/dist/storage/get-storage-driver.js +20 -0
  73. package/dist/storage/get-storage-driver.test.d.ts +1 -0
  74. package/dist/storage/index.d.ts +5 -0
  75. package/dist/storage/index.js +20 -0
  76. package/dist/storage/index.test.d.ts +1 -0
  77. package/dist/storage/register-drivers.d.ts +2 -0
  78. package/dist/storage/register-drivers.js +22 -0
  79. package/dist/storage/register-drivers.test.d.ts +1 -0
  80. package/dist/storage/register-locations.d.ts +2 -0
  81. package/dist/storage/register-locations.js +17 -0
  82. package/dist/storage/register-locations.test.d.ts +1 -0
  83. package/dist/utils/apply-query.d.ts +27 -3
  84. package/dist/utils/apply-query.js +180 -127
  85. package/dist/utils/dynamic-import.d.ts +1 -0
  86. package/dist/utils/dynamic-import.js +7 -0
  87. package/dist/utils/get-collection-from-alias.d.ts +6 -0
  88. package/dist/utils/get-collection-from-alias.js +15 -0
  89. package/dist/utils/get-collection-from-alias.test.d.ts +1 -0
  90. package/dist/utils/get-column-path.d.ts +14 -8
  91. package/dist/utils/get-column-path.js +24 -7
  92. package/dist/utils/get-column.d.ts +8 -1
  93. package/dist/utils/get-column.js +10 -3
  94. package/dist/utils/get-config-from-env.js +3 -2
  95. package/dist/utils/get-default-value.d.ts +1 -1
  96. package/dist/utils/parse-image-metadata.d.ts +3 -0
  97. package/dist/utils/parse-image-metadata.js +73 -0
  98. package/dist/utils/track.js +2 -2
  99. package/dist/utils/validate-env.js +3 -2
  100. package/dist/webhooks.js +2 -2
  101. package/package.json +17 -11
  102. package/dist/storage.d.ts +0 -3
  103. 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) {
@@ -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
  */
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.GraphQLService = void 0;
7
7
  const types_1 = require("@directus/shared/types");
8
+ const utils_1 = require("@directus/shared/utils");
8
9
  const argon2_1 = __importDefault(require("argon2"));
9
10
  const graphql_1 = require("graphql");
10
11
  const graphql_compose_1 = require("graphql-compose");
@@ -13,7 +14,6 @@ const lodash_1 = require("lodash");
13
14
  const ms_1 = __importDefault(require("ms"));
14
15
  const cache_1 = require("../../cache");
15
16
  const constants_1 = require("../../constants");
16
- const constants_2 = require("@directus/shared/constants");
17
17
  const database_1 = __importDefault(require("../../database"));
18
18
  const env_1 = __importDefault(require("../../env"));
19
19
  const exceptions_1 = require("../../exceptions");
@@ -44,7 +44,7 @@ const shares_1 = require("../shares");
44
44
  const specifications_1 = require("../specifications");
45
45
  const tfa_1 = require("../tfa");
46
46
  const users_1 = require("../users");
47
- const utils_1 = require("../utils");
47
+ const utils_2 = require("../utils");
48
48
  const webhooks_1 = require("../webhooks");
49
49
  const date_1 = require("./types/date");
50
50
  const geojson_1 = require("./types/geojson");
@@ -53,6 +53,7 @@ const void_1 = require("./types/void");
53
53
  const add_path_to_validation_error_1 = require("./utils/add-path-to-validation-error");
54
54
  const hash_1 = require("./types/hash");
55
55
  const bigint_1 = require("./types/bigint");
56
+ const constants_2 = require("@directus/shared/constants");
56
57
  const validationRules = Array.from(graphql_1.specifiedRules);
57
58
  if (env_1.default.GRAPHQL_INTROSPECTION === false) {
58
59
  validationRules.push(graphql_1.NoSchemaIntrospectionCustomRule);
@@ -1111,14 +1112,7 @@ class GraphQLService {
1111
1112
  // Transform count(a.b.c) into a.b.count(c)
1112
1113
  if ((_c = query.fields) === null || _c === void 0 ? void 0 : _c.length) {
1113
1114
  for (let fieldIndex = 0; fieldIndex < query.fields.length; fieldIndex++) {
1114
- if (query.fields[fieldIndex].includes('(') && query.fields[fieldIndex].includes(')')) {
1115
- const functionName = query.fields[fieldIndex].split('(')[0];
1116
- const columnNames = query.fields[fieldIndex].match(constants_2.REGEX_BETWEEN_PARENS)[1].split('.');
1117
- if (columnNames.length > 1) {
1118
- const column = columnNames.pop();
1119
- query.fields[fieldIndex] = columnNames.join('.') + '.' + functionName + '(' + column + ')';
1120
- }
1121
- }
1115
+ query.fields[fieldIndex] = (0, utils_1.parseFilterFunctionPath)(query.fields[fieldIndex]);
1122
1116
  }
1123
1117
  }
1124
1118
  const result = await this.read(collection, query);
@@ -1347,28 +1341,10 @@ class GraphQLService {
1347
1341
  }
1348
1342
  return (0, lodash_1.uniq)(fields);
1349
1343
  };
1350
- const replaceFuncs = (filter) => {
1351
- if (!filter)
1352
- return filter;
1353
- return replaceFuncDeep(filter);
1354
- function replaceFuncDeep(filter) {
1355
- return (0, lodash_1.transform)(filter, (result, value, key) => {
1356
- let currentKey = key;
1357
- if (typeof key === 'string' && key.endsWith('_func')) {
1358
- const functionName = Object.keys(value)[0];
1359
- currentKey = `${functionName}(${currentKey.slice(0, -5)})`;
1360
- result[currentKey] = Object.values(value)[0];
1361
- }
1362
- else {
1363
- result[currentKey] =
1364
- (value === null || value === void 0 ? void 0 : value.constructor) === Object || (value === null || value === void 0 ? void 0 : value.constructor) === Array ? replaceFuncDeep(value) : value;
1365
- }
1366
- });
1367
- }
1368
- };
1369
1344
  query.alias = parseAliases(selections);
1370
1345
  query.fields = parseFields(selections);
1371
- query.filter = replaceFuncs(query.filter);
1346
+ query.filter = this.replaceFuncs(query.filter);
1347
+ query.deep = this.replaceFuncs(query.deep);
1372
1348
  (0, validate_query_1.validateQuery)(query);
1373
1349
  return query;
1374
1350
  }
@@ -1393,9 +1369,31 @@ class GraphQLService {
1393
1369
  return selectionNode.name.value;
1394
1370
  })) !== null && _b !== void 0 ? _b : [];
1395
1371
  }
1372
+ query.filter = this.replaceFuncs(query.filter);
1396
1373
  (0, validate_query_1.validateQuery)(query);
1397
1374
  return query;
1398
1375
  }
1376
+ /**
1377
+ * Replace functions from GraphQL format to Directus-Filter format
1378
+ */
1379
+ replaceFuncs(filter) {
1380
+ if (!filter)
1381
+ return filter;
1382
+ return replaceFuncDeep(filter);
1383
+ function replaceFuncDeep(filter) {
1384
+ return (0, lodash_1.transform)(filter, (result, value, key) => {
1385
+ const isFunctionKey = typeof key === 'string' && key.endsWith('_func') && constants_2.FUNCTIONS.includes(Object.keys(value)[0]);
1386
+ if (isFunctionKey) {
1387
+ const functionName = Object.keys(value)[0];
1388
+ const fieldName = key.slice(0, -5);
1389
+ result[`${functionName}(${fieldName})`] = Object.values(value)[0];
1390
+ }
1391
+ else {
1392
+ result[key] = (value === null || value === void 0 ? void 0 : value.constructor) === Object || (value === null || value === void 0 ? void 0 : value.constructor) === Array ? replaceFuncDeep(value) : value;
1393
+ }
1394
+ });
1395
+ }
1396
+ }
1399
1397
  /**
1400
1398
  * Convert Directus-Exception into a GraphQL format, so it can be returned by GraphQL properly.
1401
1399
  */
@@ -1870,7 +1868,7 @@ class GraphQLService {
1870
1868
  to: new graphql_1.GraphQLNonNull(graphql_1.GraphQLID),
1871
1869
  },
1872
1870
  resolve: async (_, args) => {
1873
- const service = new utils_1.UtilsService({
1871
+ const service = new utils_2.UtilsService({
1874
1872
  accountability: this.accountability,
1875
1873
  schema: this.schema,
1876
1874
  });
@@ -2,14 +2,15 @@
2
2
  import { Accountability, Query, SchemaOverview } from '@directus/shared/types';
3
3
  import { Knex } from 'knex';
4
4
  import { AbstractServiceOptions, File } from '../types';
5
+ import type { Readable } from 'node:stream';
5
6
  export declare class ImportService {
6
7
  knex: Knex;
7
8
  accountability: Accountability | null;
8
9
  schema: SchemaOverview;
9
10
  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>;
11
+ import(collection: string, mimetype: string, stream: Readable): Promise<void>;
12
+ importJSON(collection: string, stream: Readable): Promise<void>;
13
+ importCSV(collection: string, stream: Readable): Promise<void>;
13
14
  }
14
15
  export declare class ExportService {
15
16
  knex: Knex;
@@ -8,6 +8,7 @@ const types_1 = require("@directus/shared/types");
8
8
  const lodash_1 = require("lodash");
9
9
  const cache_1 = require("../cache");
10
10
  const database_1 = __importDefault(require("../database"));
11
+ const helpers_1 = require("../database/helpers");
11
12
  const run_ast_1 = __importDefault(require("../database/run-ast"));
12
13
  const emitter_1 = __importDefault(require("../emitter"));
13
14
  const env_1 = __importDefault(require("../env"));
@@ -98,7 +99,12 @@ class ItemsService {
98
99
  .returning(primaryKeyField)
99
100
  .then((result) => result[0]);
100
101
  const returnedKey = typeof result === 'object' ? result[primaryKeyField] : result;
101
- primaryKey = primaryKey !== null && primaryKey !== void 0 ? primaryKey : returnedKey;
102
+ if (this.schema.collections[this.collection].fields[primaryKeyField].type === 'uuid') {
103
+ primaryKey = (0, helpers_1.getHelpers)(trx).schema.formatUUID(primaryKey !== null && primaryKey !== void 0 ? primaryKey : returnedKey);
104
+ }
105
+ else {
106
+ primaryKey = primaryKey !== null && primaryKey !== void 0 ? primaryKey : returnedKey;
107
+ }
102
108
  }
103
109
  catch (err) {
104
110
  throw await (0, translate_1.translateDatabaseError)(err);
@@ -40,7 +40,7 @@ class MetaService {
40
40
  if (!permissionsRecord)
41
41
  throw new exceptions_1.ForbiddenException();
42
42
  const permissions = (_d = permissionsRecord.permissions) !== null && _d !== void 0 ? _d : {};
43
- (0, apply_query_1.applyFilter)(this.knex, this.schema, dbQuery, permissions, collection);
43
+ (0, apply_query_1.applyFilter)(this.knex, this.schema, dbQuery, permissions, collection, {});
44
44
  }
45
45
  const result = await dbQuery;
46
46
  return Number((_e = result === null || result === void 0 ? void 0 : result.count) !== null && _e !== void 0 ? _e : 0);
@@ -64,7 +64,7 @@ class MetaService {
64
64
  }
65
65
  }
66
66
  if (Object.keys(filter).length > 0) {
67
- (0, apply_query_1.applyFilter)(this.knex, this.schema, dbQuery, filter, collection);
67
+ (0, apply_query_1.applyFilter)(this.knex, this.schema, dbQuery, filter, collection, {});
68
68
  }
69
69
  if (query.search) {
70
70
  (0, apply_query_1.applySearch)(this.schema, dbQuery, query.search, collection);
@@ -5,9 +5,7 @@ export declare class OperationsService extends ItemsService<OperationRaw> {
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 OperationsService 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,12 +31,6 @@ class OperationsService 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
- const result = await super.deleteOne(key, opts);
43
- await flowManager.reload();
44
- return result;
45
- }
46
34
  async deleteMany(keys, opts) {
47
35
  const flowManager = (0, flows_1.getFlowManager)();
48
36
  const result = await super.deleteMany(keys, opts);
@@ -0,0 +1 @@
1
+ export {};
@@ -10,13 +10,8 @@ export declare class PermissionsService extends ItemsService {
10
10
  readMany(keys: PrimaryKey[], query?: Query, opts?: QueryOptions): Promise<Partial<Item>[]>;
11
11
  createOne(data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey>;
12
12
  createMany(data: Partial<Item>[], opts?: MutationOptions): Promise<PrimaryKey[]>;
13
- updateByQuery(query: Query, data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey[]>;
14
- updateOne(key: PrimaryKey, data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey>;
15
13
  updateBatch(data: Partial<Item>[], opts?: MutationOptions): Promise<PrimaryKey[]>;
16
14
  updateMany(keys: PrimaryKey[], data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey[]>;
17
- upsertOne(payload: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey>;
18
15
  upsertMany(payloads: Partial<Item>[], opts?: MutationOptions): Promise<PrimaryKey[]>;
19
- deleteByQuery(query: Query, opts?: MutationOptions): Promise<PrimaryKey[]>;
20
- deleteOne(key: PrimaryKey, opts?: MutationOptions): Promise<PrimaryKey>;
21
16
  deleteMany(keys: PrimaryKey[], opts?: MutationOptions): Promise<PrimaryKey[]>;
22
17
  }