@directus/api 13.1.1 → 13.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 (219) hide show
  1. package/dist/app.js +4 -4
  2. package/dist/auth/drivers/ldap.js +3 -2
  3. package/dist/auth/drivers/local.js +1 -1
  4. package/dist/auth/drivers/oauth2.js +1 -1
  5. package/dist/auth/drivers/openid.js +1 -1
  6. package/dist/auth/drivers/saml.js +1 -1
  7. package/dist/auth.js +1 -1
  8. package/dist/cli/index.js +1 -1
  9. package/dist/controllers/activity.js +1 -1
  10. package/dist/controllers/assets.js +2 -2
  11. package/dist/controllers/auth.js +1 -1
  12. package/dist/controllers/collections.js +1 -1
  13. package/dist/controllers/dashboards.js +1 -1
  14. package/dist/controllers/extensions.js +11 -7
  15. package/dist/controllers/fields.js +1 -1
  16. package/dist/controllers/files.js +1 -1
  17. package/dist/controllers/flows.js +1 -1
  18. package/dist/controllers/folders.js +1 -1
  19. package/dist/controllers/items.js +1 -1
  20. package/dist/controllers/not-found.js +1 -1
  21. package/dist/controllers/notifications.js +1 -1
  22. package/dist/controllers/operations.js +1 -1
  23. package/dist/controllers/panels.js +1 -1
  24. package/dist/controllers/permissions.js +1 -1
  25. package/dist/controllers/presets.js +1 -1
  26. package/dist/controllers/relations.js +1 -1
  27. package/dist/controllers/roles.js +1 -1
  28. package/dist/controllers/schema.js +1 -1
  29. package/dist/controllers/server.js +1 -1
  30. package/dist/controllers/settings.js +1 -1
  31. package/dist/controllers/shares.js +1 -1
  32. package/dist/controllers/translations.js +1 -1
  33. package/dist/controllers/users.js +1 -1
  34. package/dist/controllers/utils.js +37 -18
  35. package/dist/controllers/webhooks.js +1 -1
  36. package/dist/database/errors/dialects/mssql.js +1 -1
  37. package/dist/database/errors/dialects/mysql.js +1 -1
  38. package/dist/database/errors/dialects/oracle.js +1 -1
  39. package/dist/database/errors/dialects/postgres.js +1 -1
  40. package/dist/database/errors/dialects/sqlite.js +1 -1
  41. package/dist/database/helpers/schema/dialects/mysql.js +1 -1
  42. package/dist/database/helpers/sequence/dialects/postgres.d.ts +5 -2
  43. package/dist/database/helpers/sequence/dialects/postgres.js +6 -3
  44. package/dist/database/migrations/20231009A-update-csv-fields-to-text.d.ts +3 -0
  45. package/dist/database/migrations/20231009A-update-csv-fields-to-text.js +44 -0
  46. package/dist/database/run-ast.js +1 -1
  47. package/dist/database/seeds/run.js +1 -1
  48. package/dist/extensions/get-extensions.d.ts +47 -0
  49. package/dist/extensions/get-extensions.js +9 -0
  50. package/dist/extensions/get-shared-deps-mapping.d.ts +1 -0
  51. package/dist/extensions/get-shared-deps-mapping.js +26 -0
  52. package/dist/extensions/index.d.ts +2 -0
  53. package/dist/extensions/index.js +9 -0
  54. package/dist/{extensions.d.ts → extensions/manager.d.ts} +4 -11
  55. package/dist/{extensions.js → extensions/manager.js} +28 -86
  56. package/dist/extensions/normalize-extension-info.d.ts +5 -0
  57. package/dist/extensions/normalize-extension-info.js +30 -0
  58. package/dist/extensions/types.d.ts +23 -0
  59. package/dist/extensions/wrap-embeds.d.ts +4 -0
  60. package/dist/extensions/wrap-embeds.js +8 -0
  61. package/dist/flows.d.ts +1 -1
  62. package/dist/flows.js +1 -1
  63. package/dist/middleware/check-ip.js +1 -1
  64. package/dist/middleware/collection-exists.js +1 -1
  65. package/dist/middleware/error-handler.js +1 -1
  66. package/dist/middleware/graphql.js +1 -1
  67. package/dist/middleware/rate-limiter-global.js +1 -1
  68. package/dist/middleware/rate-limiter-ip.js +1 -1
  69. package/dist/middleware/respond.js +1 -1
  70. package/dist/middleware/validate-batch.js +1 -1
  71. package/dist/operations/condition/index.d.ts +1 -1
  72. package/dist/operations/condition/index.js +2 -1
  73. package/dist/operations/exec/index.d.ts +1 -1
  74. package/dist/operations/exec/index.js +1 -1
  75. package/dist/operations/item-create/index.d.ts +1 -1
  76. package/dist/operations/item-create/index.js +2 -1
  77. package/dist/operations/item-delete/index.d.ts +1 -1
  78. package/dist/operations/item-delete/index.js +2 -1
  79. package/dist/operations/item-read/index.d.ts +1 -1
  80. package/dist/operations/item-read/index.js +2 -1
  81. package/dist/operations/item-update/index.d.ts +1 -1
  82. package/dist/operations/item-update/index.js +2 -1
  83. package/dist/operations/json-web-token/index.d.ts +1 -1
  84. package/dist/operations/json-web-token/index.js +2 -1
  85. package/dist/operations/log/index.d.ts +1 -1
  86. package/dist/operations/log/index.js +2 -1
  87. package/dist/operations/mail/index.d.ts +1 -1
  88. package/dist/operations/mail/index.js +1 -1
  89. package/dist/operations/notification/index.d.ts +1 -1
  90. package/dist/operations/notification/index.js +2 -1
  91. package/dist/operations/request/index.d.ts +1 -1
  92. package/dist/operations/request/index.js +3 -2
  93. package/dist/operations/sleep/index.d.ts +1 -1
  94. package/dist/operations/sleep/index.js +1 -1
  95. package/dist/operations/transform/index.d.ts +1 -1
  96. package/dist/operations/transform/index.js +2 -1
  97. package/dist/operations/trigger/index.d.ts +1 -1
  98. package/dist/operations/trigger/index.js +2 -1
  99. package/dist/services/activity.js +1 -1
  100. package/dist/services/assets.d.ts +1 -1
  101. package/dist/services/assets.js +2 -2
  102. package/dist/services/authentication.js +2 -2
  103. package/dist/services/authorization.js +1 -1
  104. package/dist/services/collections.js +3 -3
  105. package/dist/services/fields.d.ts +2 -2
  106. package/dist/services/fields.js +4 -4
  107. package/dist/services/files.d.ts +4 -1
  108. package/dist/services/files.js +5 -5
  109. package/dist/services/graphql/index.js +2 -2
  110. package/dist/services/graphql/subscription.js +3 -3
  111. package/dist/services/import-export/import-worker.d.ts +9 -0
  112. package/dist/services/import-export/import-worker.js +9 -0
  113. package/dist/services/{import-export.d.ts → import-export/index.d.ts} +2 -2
  114. package/dist/services/{import-export.js → import-export/index.js} +51 -42
  115. package/dist/services/index.d.ts +1 -1
  116. package/dist/services/index.js +1 -1
  117. package/dist/services/items.js +2 -2
  118. package/dist/services/mail/index.js +1 -1
  119. package/dist/services/meta.js +1 -1
  120. package/dist/services/payload.js +1 -1
  121. package/dist/services/permissions.d.ts +2 -2
  122. package/dist/services/permissions.js +1 -1
  123. package/dist/services/relations.js +1 -1
  124. package/dist/services/revisions.js +1 -1
  125. package/dist/services/roles.js +1 -1
  126. package/dist/services/schema.js +1 -1
  127. package/dist/services/shares.js +1 -1
  128. package/dist/services/tfa.js +1 -1
  129. package/dist/services/translations.js +1 -1
  130. package/dist/services/users.js +2 -2
  131. package/dist/services/utils.d.ts +1 -0
  132. package/dist/services/utils.js +8 -2
  133. package/dist/services/websocket.js +11 -1
  134. package/dist/types/index.d.ts +0 -1
  135. package/dist/types/index.js +0 -1
  136. package/dist/utils/apply-query.js +1 -1
  137. package/dist/utils/get-accountability-for-token.js +1 -1
  138. package/dist/utils/get-ast-from-query.js +1 -1
  139. package/dist/utils/get-column-path.js +1 -1
  140. package/dist/utils/get-column.js +1 -1
  141. package/dist/utils/get-default-value.d.ts +1 -2
  142. package/dist/utils/get-permissions.js +1 -1
  143. package/dist/utils/jwt.js +1 -1
  144. package/dist/utils/redact-object.js +9 -3
  145. package/dist/utils/transformations.d.ts +2 -1
  146. package/dist/utils/validate-diff.js +1 -1
  147. package/dist/utils/validate-keys.js +1 -1
  148. package/dist/utils/validate-query.js +1 -1
  149. package/dist/utils/validate-snapshot.js +1 -1
  150. package/dist/websocket/controllers/base.js +1 -1
  151. package/dist/websocket/controllers/index.d.ts +1 -1
  152. package/dist/websocket/controllers/index.js +0 -7
  153. package/dist/websocket/handlers/heartbeat.js +6 -1
  154. package/dist/websocket/handlers/subscribe.js +11 -16
  155. package/dist/websocket/utils/items.d.ts +4 -14
  156. package/dist/websocket/utils/items.js +59 -64
  157. package/dist/worker-pool.d.ts +2 -0
  158. package/dist/worker-pool.js +11 -0
  159. package/package.json +24 -22
  160. package/dist/errors/codes.d.ts +0 -29
  161. package/dist/errors/codes.js +0 -30
  162. package/dist/errors/contains-null-values.d.ts +0 -7
  163. package/dist/errors/contains-null-values.js +0 -4
  164. package/dist/errors/content-too-large.d.ts +0 -1
  165. package/dist/errors/content-too-large.js +0 -3
  166. package/dist/errors/forbidden.d.ts +0 -1
  167. package/dist/errors/forbidden.js +0 -3
  168. package/dist/errors/hit-rate-limit.d.ts +0 -6
  169. package/dist/errors/hit-rate-limit.js +0 -8
  170. package/dist/errors/illegal-asset-transformation.d.ts +0 -4
  171. package/dist/errors/illegal-asset-transformation.js +0 -3
  172. package/dist/errors/index.d.ts +0 -28
  173. package/dist/errors/index.js +0 -28
  174. package/dist/errors/invalid-credentials.d.ts +0 -1
  175. package/dist/errors/invalid-credentials.js +0 -3
  176. package/dist/errors/invalid-foreign-key.d.ts +0 -6
  177. package/dist/errors/invalid-foreign-key.js +0 -14
  178. package/dist/errors/invalid-ip.d.ts +0 -1
  179. package/dist/errors/invalid-ip.js +0 -3
  180. package/dist/errors/invalid-otp.d.ts +0 -1
  181. package/dist/errors/invalid-otp.js +0 -3
  182. package/dist/errors/invalid-payload.d.ts +0 -5
  183. package/dist/errors/invalid-payload.js +0 -4
  184. package/dist/errors/invalid-provider-config.d.ts +0 -5
  185. package/dist/errors/invalid-provider-config.js +0 -3
  186. package/dist/errors/invalid-provider.d.ts +0 -1
  187. package/dist/errors/invalid-provider.js +0 -3
  188. package/dist/errors/invalid-query.d.ts +0 -5
  189. package/dist/errors/invalid-query.js +0 -4
  190. package/dist/errors/invalid-token.d.ts +0 -1
  191. package/dist/errors/invalid-token.js +0 -3
  192. package/dist/errors/method-not-allowed.d.ts +0 -6
  193. package/dist/errors/method-not-allowed.js +0 -6
  194. package/dist/errors/not-null-violation.d.ts +0 -6
  195. package/dist/errors/not-null-violation.js +0 -14
  196. package/dist/errors/range-not-satisfiable.d.ts +0 -7
  197. package/dist/errors/range-not-satisfiable.js +0 -7
  198. package/dist/errors/record-not-unique.d.ts +0 -6
  199. package/dist/errors/record-not-unique.js +0 -14
  200. package/dist/errors/route-not-found.d.ts +0 -5
  201. package/dist/errors/route-not-found.js +0 -4
  202. package/dist/errors/service-unavailable.d.ts +0 -7
  203. package/dist/errors/service-unavailable.js +0 -4
  204. package/dist/errors/token-expired.d.ts +0 -1
  205. package/dist/errors/token-expired.js +0 -3
  206. package/dist/errors/unexpected-response.d.ts +0 -1
  207. package/dist/errors/unexpected-response.js +0 -3
  208. package/dist/errors/unprocessable-content.d.ts +0 -5
  209. package/dist/errors/unprocessable-content.js +0 -4
  210. package/dist/errors/unsupported-media-type.d.ts +0 -6
  211. package/dist/errors/unsupported-media-type.js +0 -4
  212. package/dist/errors/user-suspended.d.ts +0 -1
  213. package/dist/errors/user-suspended.js +0 -3
  214. package/dist/errors/value-out-of-range.d.ts +0 -6
  215. package/dist/errors/value-out-of-range.js +0 -14
  216. package/dist/errors/value-too-long.d.ts +0 -6
  217. package/dist/errors/value-too-long.js +0 -14
  218. package/dist/types/files.d.ts +0 -29
  219. /package/dist/{types/files.js → extensions/types.js} +0 -0
@@ -6,5 +6,5 @@ export type Options = {
6
6
  type: 'wysiwyg' | 'markdown' | 'template';
7
7
  subject: string;
8
8
  };
9
- declare const _default: import("@directus/types").OperationApiConfig<Options>;
9
+ declare const _default: import("@directus/extensions").OperationApiConfig<Options>;
10
10
  export default _default;
@@ -1,4 +1,4 @@
1
- import { defineOperationApi } from '@directus/utils';
1
+ import { defineOperationApi } from '@directus/extensions';
2
2
  import { MailService } from '../../services/mail/index.js';
3
3
  import { md } from '../../utils/md.js';
4
4
  export default defineOperationApi({
@@ -6,5 +6,5 @@ type Options = {
6
6
  collection?: string;
7
7
  item?: string;
8
8
  };
9
- declare const _default: import("@directus/types").OperationApiConfig<Options>;
9
+ declare const _default: import("@directus/extensions").OperationApiConfig<Options>;
10
10
  export default _default;
@@ -1,4 +1,5 @@
1
- import { defineOperationApi, optionToString, toArray } from '@directus/utils';
1
+ import { defineOperationApi } from '@directus/extensions';
2
+ import { optionToString, toArray } from '@directus/utils';
2
3
  import { NotificationsService } from '../../services/notifications.js';
3
4
  import { getAccountabilityForRole } from '../../utils/get-accountability-for-role.js';
4
5
  export default defineOperationApi({
@@ -7,5 +7,5 @@ type Options = {
7
7
  value: string;
8
8
  }[] | null;
9
9
  };
10
- declare const _default: import("@directus/types").OperationApiConfig<Options>;
10
+ declare const _default: import("@directus/extensions").OperationApiConfig<Options>;
11
11
  export default _default;
@@ -1,7 +1,8 @@
1
- import { defineOperationApi, isValidJSON } from '@directus/utils';
1
+ import { defineOperationApi } from '@directus/extensions';
2
+ import { isValidJSON } from '@directus/utils';
3
+ import { isAxiosError } from 'axios';
2
4
  import encodeUrl from 'encodeurl';
3
5
  import { getAxios } from '../../request/index.js';
4
- import { isAxiosError } from 'axios';
5
6
  export default defineOperationApi({
6
7
  id: 'request',
7
8
  handler: async ({ url, method, body, headers }) => {
@@ -1,5 +1,5 @@
1
1
  type Options = {
2
2
  milliseconds: string | number;
3
3
  };
4
- declare const _default: import("@directus/types").OperationApiConfig<Options>;
4
+ declare const _default: import("@directus/extensions").OperationApiConfig<Options>;
5
5
  export default _default;
@@ -1,4 +1,4 @@
1
- import { defineOperationApi } from '@directus/utils';
1
+ import { defineOperationApi } from '@directus/extensions';
2
2
  export default defineOperationApi({
3
3
  id: 'sleep',
4
4
  handler: async ({ milliseconds }) => {
@@ -1,5 +1,5 @@
1
1
  type Options = {
2
2
  json: string | Record<string, any>;
3
3
  };
4
- declare const _default: import("@directus/types").OperationApiConfig<Options>;
4
+ declare const _default: import("@directus/extensions").OperationApiConfig<Options>;
5
5
  export default _default;
@@ -1,4 +1,5 @@
1
- import { defineOperationApi, optionToObject } from '@directus/utils';
1
+ import { defineOperationApi } from '@directus/extensions';
2
+ import { optionToObject } from '@directus/utils';
2
3
  export default defineOperationApi({
3
4
  id: 'transform',
4
5
  handler: ({ json }) => {
@@ -4,5 +4,5 @@ type Options = {
4
4
  iterationMode?: 'serial' | 'batch' | 'parallel';
5
5
  batchSize?: number;
6
6
  };
7
- declare const _default: import("@directus/types").OperationApiConfig<Options>;
7
+ declare const _default: import("@directus/extensions").OperationApiConfig<Options>;
8
8
  export default _default;
@@ -1,4 +1,5 @@
1
- import { defineOperationApi, optionToObject } from '@directus/utils';
1
+ import { defineOperationApi } from '@directus/extensions';
2
+ import { optionToObject } from '@directus/utils';
2
3
  import { omit } from 'lodash-es';
3
4
  import { getFlowManager } from '../../flows.js';
4
5
  export default defineOperationApi({
@@ -3,7 +3,7 @@ import { isDirectusError } from '@directus/errors';
3
3
  import { uniq } from 'lodash-es';
4
4
  import validateUUID from 'uuid-validate';
5
5
  import env from '../env.js';
6
- import { ErrorCode } from '../errors/index.js';
6
+ import { ErrorCode } from '@directus/errors';
7
7
  import logger from '../logger.js';
8
8
  import { getPermissions } from '../utils/get-permissions.js';
9
9
  import { Url } from '../utils/url.js';
@@ -10,7 +10,7 @@ export declare class AssetsService {
10
10
  accountability: Accountability | null;
11
11
  authorizationService: AuthorizationService;
12
12
  constructor(options: AbstractServiceOptions);
13
- getAsset(id: string, transformation: TransformationSet, range?: Range): Promise<{
13
+ getAsset(id: string, transformation?: TransformationSet, range?: Range): Promise<{
14
14
  stream: Readable;
15
15
  file: any;
16
16
  stat: Stat;
@@ -7,7 +7,7 @@ import validateUUID from 'uuid-validate';
7
7
  import { SUPPORTED_IMAGE_TRANSFORM_FORMATS } from '../constants.js';
8
8
  import getDatabase from '../database/index.js';
9
9
  import env from '../env.js';
10
- import { ForbiddenError, IllegalAssetTransformationError, RangeNotSatisfiableError, ServiceUnavailableError, } from '../errors/index.js';
10
+ import { ForbiddenError, IllegalAssetTransformationError, RangeNotSatisfiableError, ServiceUnavailableError, } from '@directus/errors';
11
11
  import logger from '../logger.js';
12
12
  import { getStorage } from '../storage/index.js';
13
13
  import { getMilliseconds } from '../utils/get-milliseconds.js';
@@ -78,7 +78,7 @@ export class AssetsService {
78
78
  }
79
79
  }
80
80
  const type = file.type;
81
- const transforms = TransformationUtils.resolvePreset(transformation, file);
81
+ const transforms = transformation ? TransformationUtils.resolvePreset(transformation, file) : [];
82
82
  if (type && transforms.length > 0 && SUPPORTED_IMAGE_TRANSFORM_FORMATS.includes(type)) {
83
83
  const maybeNewFormat = TransformationUtils.maybeExtractFormat(transforms);
84
84
  const assetFilename = path.basename(file.filename_disk, path.extname(file.filename_disk)) +
@@ -7,8 +7,8 @@ import { DEFAULT_AUTH_PROVIDER } from '../constants.js';
7
7
  import getDatabase from '../database/index.js';
8
8
  import emitter from '../emitter.js';
9
9
  import env from '../env.js';
10
- import { InvalidCredentialsError, InvalidProviderError, UserSuspendedError } from '../errors/index.js';
11
- import { InvalidOtpError } from '../errors/index.js';
10
+ import { InvalidCredentialsError, InvalidProviderError, UserSuspendedError } from '@directus/errors';
11
+ import { InvalidOtpError } from '@directus/errors';
12
12
  import { createRateLimiter } from '../rate-limiter.js';
13
13
  import { getMilliseconds } from '../utils/get-milliseconds.js';
14
14
  import { stall } from '../utils/stall.js';
@@ -3,7 +3,7 @@ import { FailedValidationError, joiValidationErrorItemToErrorExtensions } from '
3
3
  import { cloneDeep, flatten, isArray, isNil, merge, reduce, uniq, uniqWith } from 'lodash-es';
4
4
  import { GENERATE_SPECIAL } from '../constants.js';
5
5
  import getDatabase from '../database/index.js';
6
- import { ForbiddenError } from '../errors/forbidden.js';
6
+ import { ForbiddenError } from '@directus/errors';
7
7
  import { getRelationInfo } from '../utils/get-relation-info.js';
8
8
  import { stripFunction } from '../utils/strip-function.js';
9
9
  import { ItemsService } from './items.js';
@@ -8,9 +8,9 @@ import getDatabase, { getSchemaInspector } from '../database/index.js';
8
8
  import { systemCollectionRows } from '../database/system-data/collections/index.js';
9
9
  import emitter from '../emitter.js';
10
10
  import env from '../env.js';
11
- import { ForbiddenError, InvalidPayloadError } from '../errors/index.js';
12
- import { FieldsService } from '../services/fields.js';
13
- import { ItemsService } from '../services/items.js';
11
+ import { ForbiddenError, InvalidPayloadError } from '@directus/errors';
12
+ import { FieldsService } from './fields.js';
13
+ import { ItemsService } from './items.js';
14
14
  import { getSchema } from '../utils/get-schema.js';
15
15
  import { shouldClearCache } from '../utils/should-clear-cache.js';
16
16
  export class CollectionsService {
@@ -3,8 +3,8 @@ import type { Accountability, Field, RawField, SchemaOverview, Type } from '@dir
3
3
  import type Keyv from 'keyv';
4
4
  import type { Knex } from 'knex';
5
5
  import type { Helpers } from '../database/helpers/index.js';
6
- import { ItemsService } from '../services/items.js';
7
- import { PayloadService } from '../services/payload.js';
6
+ import { ItemsService } from './items.js';
7
+ import { PayloadService } from './payload.js';
8
8
  import type { AbstractServiceOptions, MutationOptions } from '../types/index.js';
9
9
  export declare class FieldsService {
10
10
  knex: Knex;
@@ -9,9 +9,9 @@ import { getHelpers } from '../database/helpers/index.js';
9
9
  import getDatabase, { getSchemaInspector } from '../database/index.js';
10
10
  import { systemFieldRows } from '../database/system-data/fields/index.js';
11
11
  import emitter from '../emitter.js';
12
- import { ForbiddenError, InvalidPayloadError } from '../errors/index.js';
13
- import { ItemsService } from '../services/items.js';
14
- import { PayloadService } from '../services/payload.js';
12
+ import { ForbiddenError, InvalidPayloadError } from '@directus/errors';
13
+ import { ItemsService } from './items.js';
14
+ import { PayloadService } from './payload.js';
15
15
  import getDefaultValue from '../utils/get-default-value.js';
16
16
  import getLocalType from '../utils/get-local-type.js';
17
17
  import { getSchema } from '../utils/get-schema.js';
@@ -541,7 +541,7 @@ export class FieldsService {
541
541
  column = table[type](field.field, field.schema?.numeric_precision ?? 10, field.schema?.numeric_scale ?? 5);
542
542
  }
543
543
  else if (field.type === 'csv') {
544
- column = table.string(field.field);
544
+ column = table.text(field.field);
545
545
  }
546
546
  else if (field.type === 'hash') {
547
547
  column = table.string(field.field, 255);
@@ -1,7 +1,9 @@
1
1
  /// <reference types="node" resolution-mode="require"/>
2
+ import type { File } from '@directus/types';
2
3
  import type { Readable } from 'node:stream';
3
- import type { AbstractServiceOptions, File, Metadata, MutationOptions, PrimaryKey } from '../types/index.js';
4
+ import type { AbstractServiceOptions, MutationOptions, PrimaryKey } from '../types/index.js';
4
5
  import { ItemsService } from './items.js';
6
+ type Metadata = Partial<Pick<File, 'height' | 'width' | 'description' | 'title' | 'tags' | 'metadata'>>;
5
7
  export declare class FilesService extends ItemsService {
6
8
  constructor(options: AbstractServiceOptions);
7
9
  /**
@@ -32,3 +34,4 @@ export declare class FilesService extends ItemsService {
32
34
  */
33
35
  deleteMany(keys: PrimaryKey[]): Promise<PrimaryKey[]>;
34
36
  }
37
+ export {};
@@ -14,7 +14,7 @@ import url from 'url';
14
14
  import { SUPPORTED_IMAGE_METADATA_FORMATS } from '../constants.js';
15
15
  import emitter from '../emitter.js';
16
16
  import env from '../env.js';
17
- import { ForbiddenError, InvalidPayloadError, ServiceUnavailableError } from '../errors/index.js';
17
+ import { ForbiddenError, InvalidPayloadError, ServiceUnavailableError } from '@directus/errors';
18
18
  import logger from '../logger.js';
19
19
  import { getAxios } from '../request/index.js';
20
20
  import { getStorage } from '../storage/index.js';
@@ -115,12 +115,12 @@ export class FilesService extends ItemsService {
115
115
  }
116
116
  const metadata = {};
117
117
  if (sharpMetadata.orientation && sharpMetadata.orientation >= 5) {
118
- metadata.height = sharpMetadata.width;
119
- metadata.width = sharpMetadata.height;
118
+ metadata.height = sharpMetadata.width ?? null;
119
+ metadata.width = sharpMetadata.height ?? null;
120
120
  }
121
121
  else {
122
- metadata.width = sharpMetadata.width;
123
- metadata.height = sharpMetadata.height;
122
+ metadata.width = sharpMetadata.width ?? null;
123
+ metadata.height = sharpMetadata.height ?? null;
124
124
  }
125
125
  // Backward-compatible layout as it used to be with 'exifr'
126
126
  const fullMetadata = {};
@@ -9,8 +9,8 @@ import { clearSystemCache, getCache } from '../../cache.js';
9
9
  import { DEFAULT_AUTH_PROVIDER, GENERATE_SPECIAL } from '../../constants.js';
10
10
  import getDatabase from '../../database/index.js';
11
11
  import env from '../../env.js';
12
- import { ErrorCode, ForbiddenError, InvalidPayloadError } from '../../errors/index.js';
13
- import { getExtensionManager } from '../../extensions.js';
12
+ import { ErrorCode, ForbiddenError, InvalidPayloadError } from '@directus/errors';
13
+ import { getExtensionManager } from '../../extensions/index.js';
14
14
  import { generateHash } from '../../utils/generate-hash.js';
15
15
  import { getGraphQLType } from '../../utils/get-graphql-type.js';
16
16
  import { getMilliseconds } from '../../utils/get-milliseconds.js';
@@ -2,7 +2,7 @@ import { EventEmitter, on } from 'events';
2
2
  import { getMessenger } from '../../messenger.js';
3
3
  import { getSchema } from '../../utils/get-schema.js';
4
4
  import { refreshAccountability } from '../../websocket/authenticate.js';
5
- import { getSinglePayload } from '../../websocket/utils/items.js';
5
+ import { getPayload } from '../../websocket/utils/items.js';
6
6
  const messages = createPubSub(new EventEmitter());
7
7
  export function bindPubSub() {
8
8
  const messenger = getMessenger();
@@ -35,7 +35,7 @@ export function createSubscriptionGenerator(self, event) {
35
35
  if (eventData['action'] === 'create') {
36
36
  try {
37
37
  subscription.item = eventData['key'];
38
- const result = await getSinglePayload(subscription, accountability, schema, eventData);
38
+ const result = await getPayload(subscription, accountability, schema, eventData);
39
39
  yield {
40
40
  [event]: {
41
41
  key: eventData['key'],
@@ -52,7 +52,7 @@ export function createSubscriptionGenerator(self, event) {
52
52
  for (const key of eventData['keys']) {
53
53
  try {
54
54
  subscription.item = key;
55
- const result = await getSinglePayload(subscription, accountability, schema, eventData);
55
+ const result = await getPayload(subscription, accountability, schema, eventData);
56
56
  yield {
57
57
  [event]: {
58
58
  key,
@@ -0,0 +1,9 @@
1
+ import type { Accountability, SchemaOverview } from '@directus/types';
2
+ export type ImportWorkerData = {
3
+ collection: string;
4
+ mimeType: string;
5
+ filePath: string;
6
+ accountability: Accountability | undefined;
7
+ schema: SchemaOverview;
8
+ };
9
+ export default function ({ collection, mimeType, filePath, accountability, schema }: ImportWorkerData): Promise<void>;
@@ -0,0 +1,9 @@
1
+ import { createReadStream } from 'node:fs';
2
+ import { ImportService } from './index.js';
3
+ export default async function ({ collection, mimeType, filePath, accountability, schema }) {
4
+ const service = new ImportService({
5
+ accountability: accountability,
6
+ schema: schema,
7
+ });
8
+ await service.import(collection, mimeType, createReadStream(filePath));
9
+ }
@@ -1,8 +1,8 @@
1
1
  /// <reference types="node" resolution-mode="require"/>
2
- import type { Accountability, Query, SchemaOverview } from '@directus/types';
2
+ import type { Accountability, File, Query, SchemaOverview } from '@directus/types';
3
3
  import type { Knex } from 'knex';
4
4
  import type { Readable } from 'node:stream';
5
- import type { AbstractServiceOptions, File } from '../types/index.js';
5
+ import type { AbstractServiceOptions } from '../../types/index.js';
6
6
  type ExportFormat = 'csv' | 'json' | 'xml' | 'yaml';
7
7
  export declare class ImportService {
8
8
  knex: Knex;
@@ -1,28 +1,25 @@
1
1
  import { parseJSON, toArray } from '@directus/utils';
2
2
  import { queue } from 'async';
3
- import csv from 'csv-parser';
4
3
  import destroyStream from 'destroy';
5
4
  import { dump as toYAML } from 'js-yaml';
6
5
  import { parse as toXML } from 'js2xmlparser';
7
6
  import { Parser as CSVParser, transforms as CSVTransforms } from 'json2csv';
8
- import { set, transform } from 'lodash-es';
9
7
  import { createReadStream } from 'node:fs';
10
8
  import { appendFile } from 'node:fs/promises';
9
+ import Papa from 'papaparse';
11
10
  import StreamArray from 'stream-json/streamers/StreamArray.js';
12
- import stripBomStream from 'strip-bom-stream';
13
- import { file as createTmpFile } from 'tmp-promise';
14
- import getDatabase from '../database/index.js';
15
- import emitter from '../emitter.js';
16
- import env from '../env.js';
17
- import { ForbiddenError, InvalidPayloadError, ServiceUnavailableError, UnsupportedMediaTypeError, } from '../errors/index.js';
18
- import logger from '../logger.js';
19
- import { getDateFormatted } from '../utils/get-date-formatted.js';
20
- import { userName } from '../utils/user-name.js';
21
- import { FilesService } from './files.js';
22
- import { ItemsService } from './items.js';
23
- import { NotificationsService } from './notifications.js';
24
- import { UsersService } from './users.js';
25
- import { Url } from '../utils/url.js';
11
+ import getDatabase from '../../database/index.js';
12
+ import emitter from '../../emitter.js';
13
+ import env from '../../env.js';
14
+ import { ForbiddenError, InvalidPayloadError, ServiceUnavailableError, UnsupportedMediaTypeError, } from '@directus/errors';
15
+ import logger from '../../logger.js';
16
+ import { getDateFormatted } from '../../utils/get-date-formatted.js';
17
+ import { Url } from '../../utils/url.js';
18
+ import { userName } from '../../utils/user-name.js';
19
+ import { FilesService } from '../files.js';
20
+ import { ItemsService } from '../items.js';
21
+ import { NotificationsService } from '../notifications.js';
22
+ import { UsersService } from '../users.js';
26
23
  export class ImportService {
27
24
  knex;
28
25
  accountability;
@@ -97,30 +94,34 @@ export class ImportService {
97
94
  const saveQueue = queue(async (value) => {
98
95
  return await service.upsertOne(value, { bypassEmitAction: (action) => nestedActionEvents.push(action) });
99
96
  });
97
+ const transform = (value) => {
98
+ if (value.length === 0)
99
+ return;
100
+ try {
101
+ const parsedJson = parseJSON(value);
102
+ if (typeof parsedJson === 'number') {
103
+ return value;
104
+ }
105
+ return parsedJson;
106
+ }
107
+ catch {
108
+ return value;
109
+ }
110
+ };
111
+ const PapaOptions = {
112
+ header: true,
113
+ transform,
114
+ };
100
115
  return new Promise((resolve, reject) => {
101
116
  stream
102
- .pipe(stripBomStream())
103
- .pipe(csv())
104
- .on('data', (value) => {
105
- const obj = transform(value, (result, value, key) => {
106
- if (value.length === 0) {
107
- delete result[key];
117
+ .pipe(Papa.parse(Papa.NODE_STREAM_INPUT, PapaOptions))
118
+ .on('data', (obj) => {
119
+ // Filter out all undefined fields
120
+ for (const field in obj) {
121
+ if (obj[field] === undefined) {
122
+ delete obj[field];
108
123
  }
109
- else {
110
- try {
111
- const parsedJson = parseJSON(value);
112
- if (typeof parsedJson === 'number') {
113
- set(result, key, value);
114
- }
115
- else {
116
- set(result, key, parsedJson);
117
- }
118
- }
119
- catch {
120
- set(result, key, value);
121
- }
122
- }
123
- });
124
+ }
124
125
  saveQueue.push(obj);
125
126
  })
126
127
  .on('error', (err) => {
@@ -128,6 +129,9 @@ export class ImportService {
128
129
  reject(new InvalidPayloadError({ reason: err.message }));
129
130
  })
130
131
  .on('end', () => {
132
+ // In case of empty CSV file
133
+ if (!saveQueue.started)
134
+ return resolve();
131
135
  saveQueue.drain(() => {
132
136
  for (const nestedActionEvent of nestedActionEvents) {
133
137
  emitter.emitAction(nestedActionEvent.event, nestedActionEvent.meta, nestedActionEvent.context);
@@ -157,7 +161,11 @@ export class ExportService {
157
161
  * FilesService upload method.
158
162
  */
159
163
  async exportToFile(collection, query, format, options) {
164
+ const { createTmpFile } = await import('@directus/utils/node');
165
+ const tmpFile = await createTmpFile().catch(() => null);
160
166
  try {
167
+ if (!tmpFile)
168
+ throw new Error('Failed to create temporary file for export');
161
169
  const mimeTypes = {
162
170
  csv: 'text/csv',
163
171
  json: 'application/json',
@@ -165,7 +173,6 @@ export class ExportService {
165
173
  yaml: 'text/yaml',
166
174
  };
167
175
  const database = getDatabase();
168
- const { path, cleanup } = await createTmpFile();
169
176
  await database.transaction(async (trx) => {
170
177
  const service = new ItemsService(collection, {
171
178
  accountability: this.accountability,
@@ -185,7 +192,7 @@ export class ExportService {
185
192
  },
186
193
  })
187
194
  .then((result) => Number(result?.[0]?.['count'] ?? 0));
188
- const count = query.limit ? Math.min(totalCount, query.limit) : totalCount;
195
+ const count = query.limit && query.limit > -1 ? Math.min(totalCount, query.limit) : totalCount;
189
196
  const requestedLimit = query.limit ?? -1;
190
197
  const batchesRequired = Math.ceil(count / env['EXPORT_BATCH_SIZE']);
191
198
  let readCount = 0;
@@ -202,7 +209,7 @@ export class ExportService {
202
209
  });
203
210
  readCount += result.length;
204
211
  if (result.length) {
205
- await appendFile(path, this.transform(result, format, {
212
+ await appendFile(tmpFile.path, this.transform(result, format, {
206
213
  includeHeader: batch === 0,
207
214
  includeFooter: batch + 1 === batchesRequired,
208
215
  }));
@@ -223,7 +230,7 @@ export class ExportService {
223
230
  storage: options?.file?.storage ?? storage,
224
231
  type: mimeTypes[format],
225
232
  };
226
- const savedFile = await filesService.uploadOne(createReadStream(path), fileWithDefaults);
233
+ const savedFile = await filesService.uploadOne(createReadStream(tmpFile.path), fileWithDefaults);
227
234
  if (this.accountability?.user) {
228
235
  const notificationsService = new NotificationsService({
229
236
  accountability: this.accountability,
@@ -250,7 +257,6 @@ Your export of ${collection} is ready. <a href="${href}">Click here to view.</a>
250
257
  item: savedFile,
251
258
  });
252
259
  }
253
- await cleanup();
254
260
  }
255
261
  catch (err) {
256
262
  logger.error(err, `Couldn't export ${collection}: ${err.message}`);
@@ -267,6 +273,9 @@ Your export of ${collection} is ready. <a href="${href}">Click here to view.</a>
267
273
  });
268
274
  }
269
275
  }
276
+ finally {
277
+ await tmpFile?.cleanup();
278
+ }
270
279
  }
271
280
  /**
272
281
  * Transform a given input object / array to the given type
@@ -9,7 +9,7 @@ export * from './files.js';
9
9
  export * from './flows.js';
10
10
  export * from './folders.js';
11
11
  export * from './graphql/index.js';
12
- export * from './import-export.js';
12
+ export * from './import-export/index.js';
13
13
  export * from './items.js';
14
14
  export * from './mail/index.js';
15
15
  export * from './meta.js';
@@ -9,7 +9,7 @@ export * from './files.js';
9
9
  export * from './flows.js';
10
10
  export * from './folders.js';
11
11
  export * from './graphql/index.js';
12
- export * from './import-export.js';
12
+ export * from './import-export/index.js';
13
13
  export * from './items.js';
14
14
  export * from './mail/index.js';
15
15
  export * from './meta.js';
@@ -6,9 +6,9 @@ import getDatabase from '../database/index.js';
6
6
  import runAST from '../database/run-ast.js';
7
7
  import emitter from '../emitter.js';
8
8
  import env from '../env.js';
9
- import { ForbiddenError } from '../errors/index.js';
9
+ import { ForbiddenError } from '@directus/errors';
10
10
  import { translateDatabaseError } from '../database/errors/translate.js';
11
- import { InvalidPayloadError } from '../errors/index.js';
11
+ import { InvalidPayloadError } from '@directus/errors';
12
12
  import getASTFromQuery from '../utils/get-ast-from-query.js';
13
13
  import { shouldClearCache } from '../utils/should-clear-cache.js';
14
14
  import { validateKeys } from '../utils/validate-keys.js';
@@ -4,7 +4,7 @@ import path from 'path';
4
4
  import { fileURLToPath } from 'url';
5
5
  import getDatabase from '../../database/index.js';
6
6
  import env from '../../env.js';
7
- import { InvalidPayloadError } from '../../errors/index.js';
7
+ import { InvalidPayloadError } from '@directus/errors';
8
8
  import logger from '../../logger.js';
9
9
  import getMailer from '../../mailer.js';
10
10
  import { Url } from '../../utils/url.js';
@@ -1,5 +1,5 @@
1
1
  import getDatabase from '../database/index.js';
2
- import { ForbiddenError } from '../errors/index.js';
2
+ import { ForbiddenError } from '@directus/errors';
3
3
  import { applyFilter, applySearch } from '../utils/apply-query.js';
4
4
  export class MetaService {
5
5
  knex;
@@ -7,7 +7,7 @@ import { v4 as uuid } from 'uuid';
7
7
  import { parse as wktToGeoJSON } from 'wellknown';
8
8
  import { getHelpers } from '../database/helpers/index.js';
9
9
  import getDatabase from '../database/index.js';
10
- import { ForbiddenError, InvalidPayloadError } from '../errors/index.js';
10
+ import { ForbiddenError, InvalidPayloadError } from '@directus/errors';
11
11
  import { generateHash } from '../utils/generate-hash.js';
12
12
  import { ItemsService } from './items.js';
13
13
  /**
@@ -1,7 +1,7 @@
1
1
  import type { PermissionsAction, Query } from '@directus/types';
2
2
  import type Keyv from 'keyv';
3
- import type { QueryOptions } from '../services/items.js';
4
- import { ItemsService } from '../services/items.js';
3
+ import type { QueryOptions } from './items.js';
4
+ import { ItemsService } from './items.js';
5
5
  import type { AbstractServiceOptions, Item, MutationOptions, PrimaryKey } from '../types/index.js';
6
6
  export declare class PermissionsService extends ItemsService {
7
7
  systemCache: Keyv<any>;
@@ -1,6 +1,6 @@
1
1
  import { clearSystemCache, getCache } from '../cache.js';
2
2
  import { appAccessMinimalPermissions } from '../database/system-data/app-access-permissions/index.js';
3
- import { ItemsService } from '../services/items.js';
3
+ import { ItemsService } from './items.js';
4
4
  import { filterItems } from '../utils/filter-items.js';
5
5
  export class PermissionsService extends ItemsService {
6
6
  systemCache;
@@ -5,7 +5,7 @@ import { getHelpers } from '../database/helpers/index.js';
5
5
  import getDatabase, { getSchemaInspector } from '../database/index.js';
6
6
  import { systemRelationRows } from '../database/system-data/relations/index.js';
7
7
  import emitter from '../emitter.js';
8
- import { ForbiddenError, InvalidPayloadError } from '../errors/index.js';
8
+ import { ForbiddenError, InvalidPayloadError } from '@directus/errors';
9
9
  import { getDefaultIndexName } from '../utils/get-default-index-name.js';
10
10
  import { getSchema } from '../utils/get-schema.js';
11
11
  import { ItemsService } from './items.js';
@@ -1,4 +1,4 @@
1
- import { ForbiddenError, InvalidPayloadError } from '../errors/index.js';
1
+ import { ForbiddenError, InvalidPayloadError } from '@directus/errors';
2
2
  import { ItemsService } from './items.js';
3
3
  export class RevisionsService extends ItemsService {
4
4
  constructor(options) {
@@ -1,4 +1,4 @@
1
- import { ForbiddenError, UnprocessableContentError } from '../errors/index.js';
1
+ import { ForbiddenError, UnprocessableContentError } from '@directus/errors';
2
2
  import { ItemsService } from './items.js';
3
3
  import { PermissionsService } from './permissions.js';
4
4
  import { PresetsService } from './presets.js';
@@ -1,5 +1,5 @@
1
1
  import getDatabase from '../database/index.js';
2
- import { ForbiddenError } from '../errors/index.js';
2
+ import { ForbiddenError } from '@directus/errors';
3
3
  import { applyDiff } from '../utils/apply-diff.js';
4
4
  import { getSnapshotDiff } from '../utils/get-snapshot-diff.js';
5
5
  import { getSnapshot } from '../utils/get-snapshot.js';