@directus/api 10.0.0 → 10.2.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 (113) hide show
  1. package/dist/__mocks__/cache.d.mts +5 -0
  2. package/dist/__mocks__/cache.mjs +7 -0
  3. package/dist/__utils__/items-utils.d.ts +2 -0
  4. package/dist/__utils__/items-utils.js +31 -0
  5. package/dist/__utils__/schemas.d.ts +13 -0
  6. package/dist/__utils__/schemas.js +301 -0
  7. package/dist/__utils__/snapshots.d.ts +5 -0
  8. package/dist/__utils__/snapshots.js +894 -0
  9. package/dist/app.js +17 -0
  10. package/dist/auth/drivers/oauth2.js +1 -1
  11. package/dist/auth/drivers/openid.js +1 -1
  12. package/dist/cli/commands/bootstrap/index.js +1 -0
  13. package/dist/cli/utils/create-env/env-stub.liquid +4 -0
  14. package/dist/controllers/assets.js +6 -10
  15. package/dist/controllers/flows.js +4 -1
  16. package/dist/controllers/schema.js +20 -16
  17. package/dist/controllers/utils.js +4 -2
  18. package/dist/database/migrations/run.js +3 -2
  19. package/dist/database/run-ast.js +3 -3
  20. package/dist/env.js +10 -0
  21. package/dist/extensions.js +4 -5
  22. package/dist/flows.d.ts +4 -1
  23. package/dist/flows.js +12 -10
  24. package/dist/services/assets.d.ts +2 -2
  25. package/dist/services/collections.js +2 -1
  26. package/dist/services/fields.js +3 -1
  27. package/dist/services/graphql/index.js +13 -2
  28. package/dist/services/items.js +1 -1
  29. package/dist/services/permissions.js +18 -0
  30. package/dist/services/server.js +4 -0
  31. package/dist/types/assets.d.ts +6 -1
  32. package/dist/utils/apply-query.js +14 -4
  33. package/dist/utils/sanitize-error.d.ts +1 -0
  34. package/dist/utils/sanitize-error.js +7 -0
  35. package/dist/utils/sanitize-query.js +11 -2
  36. package/dist/utils/transformations.d.ts +2 -2
  37. package/dist/utils/transformations.js +29 -10
  38. package/dist/utils/validate-query.js +3 -1
  39. package/package.json +22 -23
  40. package/dist/app.test.d.ts +0 -1
  41. package/dist/controllers/files.test.d.ts +0 -1
  42. package/dist/database/migrations/run.test.d.ts +0 -1
  43. package/dist/env.test.d.ts +0 -1
  44. package/dist/logger.test.d.ts +0 -1
  45. package/dist/middleware/authenticate.test.d.ts +0 -1
  46. package/dist/middleware/extract-token.test.d.ts +0 -1
  47. package/dist/middleware/validate-batch.test.d.ts +0 -1
  48. package/dist/operations/condition/index.test.d.ts +0 -1
  49. package/dist/operations/exec/index.test.d.ts +0 -1
  50. package/dist/operations/item-create/index.test.d.ts +0 -1
  51. package/dist/operations/item-delete/index.test.d.ts +0 -1
  52. package/dist/operations/item-read/index.test.d.ts +0 -1
  53. package/dist/operations/item-update/index.test.d.ts +0 -1
  54. package/dist/operations/log/index.test.d.ts +0 -1
  55. package/dist/operations/mail/index.test.d.ts +0 -1
  56. package/dist/operations/notification/index.test.d.ts +0 -1
  57. package/dist/operations/request/index.test.d.ts +0 -1
  58. package/dist/operations/sleep/index.test.d.ts +0 -1
  59. package/dist/operations/transform/index.test.d.ts +0 -1
  60. package/dist/operations/trigger/index.test.d.ts +0 -1
  61. package/dist/request/index.test.d.ts +0 -1
  62. package/dist/request/request-interceptor.test.d.ts +0 -1
  63. package/dist/request/response-interceptor.test.d.ts +0 -1
  64. package/dist/request/validate-ip.test.d.ts +0 -1
  65. package/dist/services/files.test.d.ts +0 -1
  66. package/dist/services/graphql/utils/process-error.test.d.ts +0 -1
  67. package/dist/services/items.test.d.ts +0 -1
  68. package/dist/services/payload.test.d.ts +0 -1
  69. package/dist/services/roles.test.d.ts +0 -1
  70. package/dist/services/schema.test.d.ts +0 -1
  71. package/dist/services/specifications.test.d.ts +0 -1
  72. package/dist/services/users.test.d.ts +0 -1
  73. package/dist/services/webhooks.test.d.ts +0 -1
  74. package/dist/storage/get-storage-driver.test.d.ts +0 -1
  75. package/dist/storage/index.test.d.ts +0 -1
  76. package/dist/storage/register-drivers.test.d.ts +0 -1
  77. package/dist/storage/register-locations.test.d.ts +0 -1
  78. package/dist/utils/apply-diff.test.d.ts +0 -1
  79. package/dist/utils/apply-function-to-column-name.test.d.ts +0 -1
  80. package/dist/utils/apply-snapshot.test.d.ts +0 -1
  81. package/dist/utils/async-handler.test.d.ts +0 -1
  82. package/dist/utils/calculate-field-depth.test.d.ts +0 -1
  83. package/dist/utils/filter-items.test.d.ts +0 -1
  84. package/dist/utils/get-auth-providers.test.d.ts +0 -1
  85. package/dist/utils/get-cache-headers.test.d.ts +0 -1
  86. package/dist/utils/get-cache-key.test.d.ts +0 -1
  87. package/dist/utils/get-collection-from-alias.test.d.ts +0 -1
  88. package/dist/utils/get-column-path.test.d.ts +0 -1
  89. package/dist/utils/get-config-from-env.test.d.ts +0 -1
  90. package/dist/utils/get-date-formatted.test.d.ts +0 -1
  91. package/dist/utils/get-graphql-query-and-variables.test.d.ts +0 -1
  92. package/dist/utils/get-milliseconds.test.d.ts +0 -1
  93. package/dist/utils/get-relation-info.test.d.ts +0 -1
  94. package/dist/utils/get-relation-type.test.d.ts +0 -1
  95. package/dist/utils/get-string-byte-size.test.d.ts +0 -1
  96. package/dist/utils/get-versioned-hash.test.d.ts +0 -1
  97. package/dist/utils/is-directus-jwt.test.d.ts +0 -1
  98. package/dist/utils/jwt.test.d.ts +0 -1
  99. package/dist/utils/map-values-deep.test.d.ts +0 -1
  100. package/dist/utils/md.test.d.ts +0 -1
  101. package/dist/utils/merge-permissions.test.d.ts +0 -1
  102. package/dist/utils/sanitize-query.test.d.ts +0 -1
  103. package/dist/utils/sanitize-schema.test.d.ts +0 -1
  104. package/dist/utils/should-skip-cache.test.d.ts +0 -1
  105. package/dist/utils/stall.test.d.ts +0 -1
  106. package/dist/utils/strip-function.test.d.ts +0 -1
  107. package/dist/utils/url.test.d.ts +0 -1
  108. package/dist/utils/user-name.test.d.ts +0 -1
  109. package/dist/utils/validate-diff.test.d.ts +0 -1
  110. package/dist/utils/validate-env.test.d.ts +0 -1
  111. package/dist/utils/validate-keys.test.d.ts +0 -1
  112. package/dist/utils/validate-query.test.d.ts +0 -1
  113. package/dist/utils/validate-snapshot.test.d.ts +0 -1
@@ -188,8 +188,9 @@ export function applyFilter(knex, schema, rootQuery, rootFilter, collection, ali
188
188
  if (key === '_or' || key === '_and') {
189
189
  // If the _or array contains an empty object (full permissions), we should short-circuit and ignore all other
190
190
  // permission checks, as {} already matches full permissions.
191
- if (key === '_or' && value.some((subFilter) => Object.keys(subFilter).length === 0))
191
+ if (key === '_or' && value.some((subFilter) => Object.keys(subFilter).length === 0)) {
192
192
  continue;
193
+ }
193
194
  value.forEach((subFilter) => {
194
195
  addJoins(dbQuery, subFilter, collection);
195
196
  });
@@ -197,7 +198,7 @@ export function applyFilter(knex, schema, rootQuery, rootFilter, collection, ali
197
198
  }
198
199
  const filterPath = getFilterPath(key, value);
199
200
  if (filterPath.length > 1 ||
200
- (!(key.includes('(') && key.includes(')')) && schema.collections[collection].fields[key].type === 'alias')) {
201
+ (!(key.includes('(') && key.includes(')')) && schema.collections[collection]?.fields[key]?.type === 'alias')) {
201
202
  const hasMultiRelational = addJoin({
202
203
  path: filterPath,
203
204
  collection,
@@ -237,7 +238,7 @@ export function applyFilter(knex, schema, rootQuery, rootFilter, collection, ali
237
238
  const { relation, relationType } = getRelationInfo(relations, collection, pathRoot);
238
239
  const { operator: filterOperator, value: filterValue } = getOperation(key, value);
239
240
  if (filterPath.length > 1 ||
240
- (!(key.includes('(') && key.includes(')')) && schema.collections[collection].fields[key].type === 'alias')) {
241
+ (!(key.includes('(') && key.includes(')')) && schema.collections[collection]?.fields[key]?.type === 'alias')) {
241
242
  if (!relation)
242
243
  continue;
243
244
  if (relationType === 'o2m' || relationType === 'o2a') {
@@ -484,8 +485,10 @@ export async function applySearch(schema, dbQuery, searchQuery, collection) {
484
485
  }
485
486
  else if (['bigInteger', 'integer', 'decimal', 'float'].includes(field.type)) {
486
487
  const number = Number(searchQuery);
487
- if (!isNaN(number))
488
+ // only cast finite base10 numeric values
489
+ if (validateNumber(searchQuery, number)) {
488
490
  this.orWhere({ [`${collection}.${name}`]: number });
491
+ }
489
492
  }
490
493
  else if (field.type === 'uuid' && validate(searchQuery)) {
491
494
  this.orWhere({ [`${collection}.${name}`]: searchQuery });
@@ -493,6 +496,13 @@ export async function applySearch(schema, dbQuery, searchQuery, collection) {
493
496
  });
494
497
  });
495
498
  }
499
+ function validateNumber(value, parsed) {
500
+ if (isNaN(parsed) || !Number.isFinite(parsed))
501
+ return false;
502
+ // casting parsed value back to string should be equal the original value
503
+ // (prevent unintended number parsing, e.g. String(7) !== "ob111")
504
+ return String(parsed) === value;
505
+ }
496
506
  export function applyAggregate(dbQuery, aggregate, collection) {
497
507
  for (const [operation, fields] of Object.entries(aggregate)) {
498
508
  if (!fields)
@@ -0,0 +1 @@
1
+ export declare function sanitizeError<T extends Error>(error: T): T;
@@ -0,0 +1,7 @@
1
+ export function sanitizeError(error) {
2
+ // clear the stack
3
+ if (error.stack !== undefined) {
4
+ delete error.stack;
5
+ }
6
+ return error;
7
+ }
@@ -1,15 +1,24 @@
1
1
  import { parseFilter, parseJSON } from '@directus/utils';
2
2
  import { flatten, get, isPlainObject, merge, set } from 'lodash-es';
3
+ import { getEnv } from '../env.js';
3
4
  import logger from '../logger.js';
4
5
  import { Meta } from '../types/index.js';
5
6
  export function sanitizeQuery(rawQuery, accountability) {
6
7
  const query = {};
8
+ const env = getEnv();
9
+ const hasMaxLimit = 'QUERY_LIMIT_MAX' in env &&
10
+ Number(env['QUERY_LIMIT_MAX']) >= 0 &&
11
+ !Number.isNaN(Number(env['QUERY_LIMIT_MAX'])) &&
12
+ Number.isFinite(Number(env['QUERY_LIMIT_MAX']));
7
13
  if (rawQuery['limit'] !== undefined) {
8
14
  const limit = sanitizeLimit(rawQuery['limit']);
9
15
  if (typeof limit === 'number') {
10
- query.limit = limit;
16
+ query.limit = limit === -1 && hasMaxLimit ? Number(env['QUERY_LIMIT_MAX']) : limit;
11
17
  }
12
18
  }
19
+ else if (hasMaxLimit) {
20
+ query.limit = Math.min(Number(env['QUERY_LIMIT_DEFAULT']), Number(env['QUERY_LIMIT_MAX']));
21
+ }
13
22
  if (rawQuery['fields']) {
14
23
  query.fields = sanitizeFields(rawQuery['fields']);
15
24
  }
@@ -25,7 +34,7 @@ export function sanitizeQuery(rawQuery, accountability) {
25
34
  if (rawQuery['filter']) {
26
35
  query.filter = sanitizeFilter(rawQuery['filter'], accountability || null);
27
36
  }
28
- if (rawQuery['offset']) {
37
+ if (rawQuery['offset'] !== undefined) {
29
38
  query.offset = sanitizeOffset(rawQuery['offset']);
30
39
  }
31
40
  if (rawQuery['page']) {
@@ -1,5 +1,5 @@
1
- import type { File, Transformation, TransformationParams } from '../types/index.js';
2
- export declare function resolvePreset(input: TransformationParams, file: File): Transformation[];
1
+ import type { File, Transformation, TransformationSet } from '../types/index.js';
2
+ export declare function resolvePreset({ transformationParams, acceptFormat }: TransformationSet, file: File): Transformation[];
3
3
  /**
4
4
  * Try to extract a file format from an array of `Transformation`'s.
5
5
  */
@@ -1,25 +1,44 @@
1
- export function resolvePreset(input, file) {
2
- const transforms = input.transforms ?? [];
3
- if (input.format || input.quality)
1
+ export function resolvePreset({ transformationParams, acceptFormat }, file) {
2
+ const transforms = transformationParams.transforms ? [...transformationParams.transforms] : [];
3
+ if (transformationParams.format || transformationParams.quality) {
4
4
  transforms.push([
5
5
  'toFormat',
6
- input.format || file.type.split('/')[1],
6
+ getFormat(file, transformationParams.format, acceptFormat),
7
7
  {
8
- quality: input.quality ? Number(input.quality) : undefined,
8
+ quality: transformationParams.quality ? Number(transformationParams.quality) : undefined,
9
9
  },
10
10
  ]);
11
- if (input.width || input.height)
11
+ }
12
+ if (transformationParams.width || transformationParams.height) {
12
13
  transforms.push([
13
14
  'resize',
14
15
  {
15
- width: input.width ? Number(input.width) : undefined,
16
- height: input.height ? Number(input.height) : undefined,
17
- fit: input.fit,
18
- withoutEnlargement: input.withoutEnlargement ? Boolean(input.withoutEnlargement) : undefined,
16
+ width: transformationParams.width ? Number(transformationParams.width) : undefined,
17
+ height: transformationParams.height ? Number(transformationParams.height) : undefined,
18
+ fit: transformationParams.fit,
19
+ withoutEnlargement: transformationParams.withoutEnlargement
20
+ ? Boolean(transformationParams.withoutEnlargement)
21
+ : undefined,
19
22
  },
20
23
  ]);
24
+ }
21
25
  return transforms;
22
26
  }
27
+ function getFormat(file, format, acceptFormat) {
28
+ const fileType = file.type?.split('/')[1];
29
+ if (format) {
30
+ if (format !== 'auto') {
31
+ return format;
32
+ }
33
+ if (acceptFormat) {
34
+ return acceptFormat;
35
+ }
36
+ if (fileType && ['avif', 'webp', 'tiff'].includes(fileType)) {
37
+ return 'png';
38
+ }
39
+ }
40
+ return fileType || 'jpg';
41
+ }
23
42
  /**
24
43
  * Try to extract a file format from an array of `Transformation`'s.
25
44
  */
@@ -9,7 +9,9 @@ const querySchema = Joi.object({
9
9
  group: Joi.array().items(Joi.string()),
10
10
  sort: Joi.array().items(Joi.string()),
11
11
  filter: Joi.object({}).unknown(),
12
- limit: Joi.number().integer().min(-1),
12
+ limit: 'QUERY_LIMIT_MAX' in env && env['QUERY_LIMIT_MAX'] !== -1
13
+ ? Joi.number().integer().min(-1).max(env['QUERY_LIMIT_MAX']) // min should be 0
14
+ : Joi.number().integer().min(-1),
13
15
  offset: Joi.number().integer().min(0),
14
16
  page: Joi.number().integer().min(0),
15
17
  meta: Joi.array().items(Joi.string().valid('total_count', 'filter_count')),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@directus/api",
3
- "version": "10.0.0",
3
+ "version": "10.2.0",
4
4
  "description": "Directus is a real-time API and App dashboard for managing SQL database content",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -58,9 +58,7 @@
58
58
  },
59
59
  "main": "dist/index.js",
60
60
  "files": [
61
- "dist",
62
- "!**/*.test.js",
63
- "!**/__*__/"
61
+ "dist"
64
62
  ],
65
63
  "dependencies": {
66
64
  "@authenio/samlify-node-xmllint": "2.0.0",
@@ -141,22 +139,23 @@
141
139
  "tsx": "3.12.6",
142
140
  "uuid": "9.0.0",
143
141
  "uuid-validate": "0.0.3",
144
- "vm2": "3.9.17",
142
+ "vm2": "3.9.18",
145
143
  "wellknown": "0.5.0",
146
- "@directus/app": "10.0.0",
147
- "@directus/constants": "10.0.0",
148
- "@directus/exceptions": "10.0.0",
149
- "@directus/extensions-sdk": "10.0.0",
150
- "@directus/schema": "10.0.0",
151
- "@directus/specs": "10.0.0",
152
- "@directus/storage": "10.0.0",
153
- "@directus/storage-driver-azure": "10.0.0",
154
- "@directus/storage-driver-cloudinary": "10.0.0",
155
- "@directus/storage-driver-gcs": "10.0.0",
156
- "@directus/storage-driver-local": "10.0.0",
157
- "@directus/storage-driver-s3": "10.0.0",
158
- "@directus/update-check": "10.0.0",
159
- "@directus/utils": "10.0.0"
144
+ "@directus/app": "10.2.0",
145
+ "@directus/constants": "10.1.0",
146
+ "@directus/exceptions": "10.0.1",
147
+ "@directus/extensions-sdk": "10.1.1",
148
+ "@directus/pressure": "1.0.1",
149
+ "@directus/schema": "10.0.1",
150
+ "@directus/specs": "10.1.0",
151
+ "@directus/storage": "10.0.1",
152
+ "@directus/storage-driver-azure": "10.0.2",
153
+ "@directus/storage-driver-cloudinary": "10.0.2",
154
+ "@directus/storage-driver-gcs": "10.0.2",
155
+ "@directus/storage-driver-local": "10.0.2",
156
+ "@directus/storage-driver-s3": "10.0.2",
157
+ "@directus/update-check": "10.0.1",
158
+ "@directus/utils": "10.0.2"
160
159
  },
161
160
  "devDependencies": {
162
161
  "@directus/tsconfig": "0.0.7",
@@ -197,14 +196,14 @@
197
196
  "@types/uuid": "9.0.1",
198
197
  "@types/uuid-validate": "0.0.1",
199
198
  "@types/wellknown": "0.5.4",
200
- "@vitest/coverage-c8": "0.30.1",
199
+ "@vitest/coverage-c8": "0.31.0",
201
200
  "copyfiles": "2.4.1",
202
201
  "form-data": "4.0.0",
203
202
  "knex-mock-client": "2.0.0",
204
203
  "supertest": "6.3.3",
205
204
  "typescript": "5.0.4",
206
- "vitest": "0.30.1",
207
- "@directus/types": "10.0.0"
205
+ "vitest": "0.31.0",
206
+ "@directus/types": "10.0.1"
208
207
  },
209
208
  "optionalDependencies": {
210
209
  "@keyv/redis": "2.5.7",
@@ -221,7 +220,7 @@
221
220
  "node": ">=18.0.0"
222
221
  },
223
222
  "scripts": {
224
- "build": "tsc --build && copyfiles \"src/**/*.{yaml,liquid}\" -u 1 dist",
223
+ "build": "tsc --project tsconfig.prod.json && copyfiles \"src/**/*.{yaml,liquid}\" -u 1 dist",
225
224
  "cli": "NODE_ENV=development SERVE_APP=false tsx src/cli/run.ts",
226
225
  "dev": "NODE_ENV=development SERVE_APP=false tsx watch --clear-screen=false src/start.ts",
227
226
  "test": "vitest --watch=false"
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- import '../../src/types/express.d.ts';
@@ -1 +0,0 @@
1
- import '../../src/types/express.d.ts';
@@ -1 +0,0 @@
1
- import '../../src/types/express.d.ts';
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- import '../../src/types/express.d.ts';
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};
@@ -1 +0,0 @@
1
- export {};