@directus/api 13.1.1 → 14.0.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 (292) hide show
  1. package/dist/__utils__/snapshots.js +9 -0
  2. package/dist/app.js +6 -4
  3. package/dist/auth/drivers/ldap.js +3 -2
  4. package/dist/auth/drivers/local.js +1 -1
  5. package/dist/auth/drivers/oauth2.js +1 -1
  6. package/dist/auth/drivers/openid.js +1 -1
  7. package/dist/auth/drivers/saml.js +1 -1
  8. package/dist/auth.js +1 -1
  9. package/dist/cli/index.js +7 -4
  10. package/dist/controllers/activity.js +1 -1
  11. package/dist/controllers/assets.js +2 -2
  12. package/dist/controllers/auth.js +1 -1
  13. package/dist/controllers/collections.js +1 -1
  14. package/dist/controllers/dashboards.js +1 -1
  15. package/dist/controllers/extensions.js +29 -16
  16. package/dist/controllers/fields.js +1 -1
  17. package/dist/controllers/files.js +1 -1
  18. package/dist/controllers/flows.js +1 -1
  19. package/dist/controllers/folders.js +1 -1
  20. package/dist/controllers/items.js +1 -1
  21. package/dist/controllers/not-found.js +1 -1
  22. package/dist/controllers/notifications.js +1 -1
  23. package/dist/controllers/operations.js +1 -1
  24. package/dist/controllers/panels.js +1 -1
  25. package/dist/controllers/permissions.js +1 -1
  26. package/dist/controllers/presets.js +1 -1
  27. package/dist/controllers/relations.js +1 -1
  28. package/dist/controllers/roles.js +1 -1
  29. package/dist/controllers/schema.js +1 -1
  30. package/dist/controllers/server.js +1 -1
  31. package/dist/controllers/settings.js +1 -1
  32. package/dist/controllers/shares.js +1 -1
  33. package/dist/controllers/translations.js +1 -1
  34. package/dist/controllers/users.js +1 -1
  35. package/dist/controllers/utils.js +37 -18
  36. package/dist/controllers/versions.d.ts +2 -0
  37. package/dist/controllers/versions.js +188 -0
  38. package/dist/controllers/webhooks.js +1 -1
  39. package/dist/database/errors/dialects/mssql.js +1 -1
  40. package/dist/database/errors/dialects/mysql.js +1 -1
  41. package/dist/database/errors/dialects/oracle.js +1 -1
  42. package/dist/database/errors/dialects/postgres.js +1 -1
  43. package/dist/database/errors/dialects/sqlite.js +1 -1
  44. package/dist/database/helpers/schema/dialects/mysql.js +1 -1
  45. package/dist/database/helpers/sequence/dialects/postgres.d.ts +5 -2
  46. package/dist/database/helpers/sequence/dialects/postgres.js +6 -3
  47. package/dist/database/migrations/20230823A-add-content-versioning.d.ts +3 -0
  48. package/dist/database/migrations/20230823A-add-content-versioning.js +36 -0
  49. package/dist/database/migrations/20230927A-themes.d.ts +3 -0
  50. package/dist/database/migrations/20230927A-themes.js +49 -0
  51. package/dist/database/migrations/20231009A-update-csv-fields-to-text.d.ts +3 -0
  52. package/dist/database/migrations/20231009A-update-csv-fields-to-text.js +44 -0
  53. package/dist/database/migrations/20231009B-update-panel-options.d.ts +3 -0
  54. package/dist/database/migrations/20231009B-update-panel-options.js +77 -0
  55. package/dist/database/migrations/20231010A-add-extensions.d.ts +3 -0
  56. package/dist/database/migrations/20231010A-add-extensions.js +9 -0
  57. package/dist/database/run-ast.js +2 -2
  58. package/dist/database/seeds/run.js +1 -1
  59. package/dist/database/system-data/collections/collections.yaml +6 -0
  60. package/dist/database/system-data/fields/activity.yaml +4 -4
  61. package/dist/database/system-data/fields/collections.yaml +19 -0
  62. package/dist/database/system-data/fields/extensions.yaml +10 -0
  63. package/dist/database/system-data/fields/revisions.yaml +3 -0
  64. package/dist/database/system-data/fields/settings.yaml +73 -17
  65. package/dist/database/system-data/fields/users.yaml +48 -12
  66. package/dist/database/system-data/fields/versions.yaml +38 -0
  67. package/dist/database/system-data/fields/webhooks.yaml +9 -9
  68. package/dist/database/system-data/relations/relations.yaml +88 -20
  69. package/dist/env.js +4 -0
  70. package/dist/extensions/index.d.ts +2 -0
  71. package/dist/extensions/index.js +9 -0
  72. package/dist/extensions/lib/get-extensions-settings.d.ts +7 -0
  73. package/dist/extensions/lib/get-extensions-settings.js +39 -0
  74. package/dist/extensions/lib/get-extensions.d.ts +1 -0
  75. package/dist/extensions/lib/get-extensions.js +11 -0
  76. package/dist/extensions/lib/get-shared-deps-mapping.d.ts +1 -0
  77. package/dist/extensions/lib/get-shared-deps-mapping.js +26 -0
  78. package/dist/extensions/lib/sandbox/generate-api-extensions-sandbox-entrypoint.d.ts +31 -0
  79. package/dist/extensions/lib/sandbox/generate-api-extensions-sandbox-entrypoint.js +80 -0
  80. package/dist/extensions/lib/sandbox/generate-host-function-reference.d.ts +11 -0
  81. package/dist/extensions/lib/sandbox/generate-host-function-reference.js +28 -0
  82. package/dist/extensions/lib/sandbox/register/action.d.ts +6 -0
  83. package/dist/extensions/lib/sandbox/register/action.js +18 -0
  84. package/dist/extensions/lib/sandbox/register/call-reference.d.ts +5 -0
  85. package/dist/extensions/lib/sandbox/register/call-reference.js +20 -0
  86. package/dist/extensions/lib/sandbox/register/filter.d.ts +6 -0
  87. package/dist/extensions/lib/sandbox/register/filter.js +21 -0
  88. package/dist/extensions/lib/sandbox/register/index.d.ts +5 -0
  89. package/dist/extensions/lib/sandbox/register/index.js +5 -0
  90. package/dist/extensions/lib/sandbox/register/operation.d.ts +6 -0
  91. package/dist/extensions/lib/sandbox/register/operation.js +19 -0
  92. package/dist/extensions/lib/sandbox/register/route.d.ts +17 -0
  93. package/dist/extensions/lib/sandbox/register/route.js +44 -0
  94. package/dist/extensions/lib/sandbox/sdk/generators/index.d.ts +3 -0
  95. package/dist/extensions/lib/sandbox/sdk/generators/index.js +3 -0
  96. package/dist/extensions/lib/sandbox/sdk/generators/log.d.ts +3 -0
  97. package/dist/extensions/lib/sandbox/sdk/generators/log.js +11 -0
  98. package/dist/extensions/lib/sandbox/sdk/generators/request.d.ts +12 -0
  99. package/dist/extensions/lib/sandbox/sdk/generators/request.js +49 -0
  100. package/dist/extensions/lib/sandbox/sdk/generators/sleep.d.ts +3 -0
  101. package/dist/extensions/lib/sandbox/sdk/generators/sleep.js +11 -0
  102. package/dist/extensions/lib/sandbox/sdk/index.d.ts +2 -0
  103. package/dist/extensions/lib/sandbox/sdk/index.js +2 -0
  104. package/dist/extensions/lib/sandbox/sdk/instantiate.d.ts +11 -0
  105. package/dist/extensions/lib/sandbox/sdk/instantiate.js +28 -0
  106. package/dist/extensions/lib/sandbox/sdk/sdk.d.ts +20 -0
  107. package/dist/extensions/lib/sandbox/sdk/sdk.js +11 -0
  108. package/dist/extensions/lib/sandbox/sdk/utils/index.d.ts +1 -0
  109. package/dist/extensions/lib/sandbox/sdk/utils/index.js +1 -0
  110. package/dist/extensions/lib/sandbox/sdk/utils/wrap.d.ts +11 -0
  111. package/dist/extensions/lib/sandbox/sdk/utils/wrap.js +17 -0
  112. package/dist/extensions/lib/wrap-embeds.d.ts +4 -0
  113. package/dist/extensions/lib/wrap-embeds.js +8 -0
  114. package/dist/extensions/manager.d.ts +158 -0
  115. package/dist/extensions/manager.js +604 -0
  116. package/dist/extensions/types.d.ts +19 -0
  117. package/dist/flows.d.ts +2 -2
  118. package/dist/flows.js +7 -7
  119. package/dist/middleware/check-ip.js +1 -1
  120. package/dist/middleware/collection-exists.js +1 -1
  121. package/dist/middleware/error-handler.js +1 -1
  122. package/dist/middleware/graphql.js +1 -1
  123. package/dist/middleware/rate-limiter-global.js +1 -1
  124. package/dist/middleware/rate-limiter-ip.js +1 -1
  125. package/dist/middleware/respond.js +13 -1
  126. package/dist/middleware/validate-batch.js +1 -1
  127. package/dist/operations/condition/index.d.ts +1 -1
  128. package/dist/operations/condition/index.js +2 -1
  129. package/dist/operations/exec/index.d.ts +1 -1
  130. package/dist/operations/exec/index.js +1 -1
  131. package/dist/operations/item-create/index.d.ts +1 -1
  132. package/dist/operations/item-create/index.js +2 -1
  133. package/dist/operations/item-delete/index.d.ts +1 -1
  134. package/dist/operations/item-delete/index.js +2 -1
  135. package/dist/operations/item-read/index.d.ts +1 -1
  136. package/dist/operations/item-read/index.js +2 -1
  137. package/dist/operations/item-update/index.d.ts +1 -1
  138. package/dist/operations/item-update/index.js +2 -1
  139. package/dist/operations/json-web-token/index.d.ts +1 -1
  140. package/dist/operations/json-web-token/index.js +2 -1
  141. package/dist/operations/log/index.d.ts +1 -1
  142. package/dist/operations/log/index.js +2 -1
  143. package/dist/operations/mail/index.d.ts +1 -1
  144. package/dist/operations/mail/index.js +1 -1
  145. package/dist/operations/notification/index.d.ts +1 -1
  146. package/dist/operations/notification/index.js +2 -1
  147. package/dist/operations/request/index.d.ts +1 -1
  148. package/dist/operations/request/index.js +3 -2
  149. package/dist/operations/sleep/index.d.ts +1 -1
  150. package/dist/operations/sleep/index.js +1 -1
  151. package/dist/operations/transform/index.d.ts +1 -1
  152. package/dist/operations/transform/index.js +2 -1
  153. package/dist/operations/trigger/index.d.ts +1 -1
  154. package/dist/operations/trigger/index.js +2 -1
  155. package/dist/services/activity.js +1 -1
  156. package/dist/services/assets.d.ts +1 -1
  157. package/dist/services/assets.js +3 -3
  158. package/dist/services/authentication.js +2 -2
  159. package/dist/services/authorization.js +1 -1
  160. package/dist/services/collections.js +3 -3
  161. package/dist/services/extensions.d.ts +31 -0
  162. package/dist/services/extensions.js +121 -0
  163. package/dist/services/fields.d.ts +2 -2
  164. package/dist/services/fields.js +4 -4
  165. package/dist/services/files.d.ts +4 -1
  166. package/dist/services/files.js +5 -5
  167. package/dist/services/graphql/index.d.ts +1 -1
  168. package/dist/services/graphql/index.js +87 -24
  169. package/dist/services/graphql/subscription.js +3 -3
  170. package/dist/services/import-export/import-worker.d.ts +9 -0
  171. package/dist/services/import-export/import-worker.js +9 -0
  172. package/dist/services/{import-export.d.ts → import-export/index.d.ts} +2 -2
  173. package/dist/services/{import-export.js → import-export/index.js} +51 -42
  174. package/dist/services/index.d.ts +3 -1
  175. package/dist/services/index.js +3 -1
  176. package/dist/services/items.js +2 -2
  177. package/dist/services/mail/index.js +1 -1
  178. package/dist/services/meta.js +1 -1
  179. package/dist/services/payload.js +1 -1
  180. package/dist/services/permissions.d.ts +2 -2
  181. package/dist/services/permissions.js +1 -1
  182. package/dist/services/relations.js +1 -1
  183. package/dist/services/revisions.js +1 -1
  184. package/dist/services/roles.js +1 -1
  185. package/dist/services/schema.js +1 -1
  186. package/dist/services/server.js +3 -1
  187. package/dist/services/shares.js +1 -1
  188. package/dist/services/tfa.js +1 -1
  189. package/dist/services/translations.js +1 -1
  190. package/dist/services/users.js +4 -2
  191. package/dist/services/utils.d.ts +1 -0
  192. package/dist/services/utils.js +8 -2
  193. package/dist/services/versions.d.ts +21 -0
  194. package/dist/services/versions.js +232 -0
  195. package/dist/services/websocket.js +11 -1
  196. package/dist/types/collection.d.ts +1 -0
  197. package/dist/types/index.d.ts +0 -1
  198. package/dist/types/index.js +0 -1
  199. package/dist/utils/apply-query.d.ts +1 -1
  200. package/dist/utils/apply-query.js +31 -3
  201. package/dist/utils/delete-from-require-cache.d.ts +1 -0
  202. package/dist/utils/delete-from-require-cache.js +5 -0
  203. package/dist/utils/get-accountability-for-token.js +1 -1
  204. package/dist/utils/get-ast-from-query.js +1 -1
  205. package/dist/utils/get-column-path.js +1 -1
  206. package/dist/utils/get-column.js +1 -1
  207. package/dist/utils/get-default-value.d.ts +1 -2
  208. package/dist/utils/get-permissions.js +1 -1
  209. package/dist/utils/get-service.js +3 -1
  210. package/dist/utils/import-file-url.d.ts +5 -0
  211. package/dist/utils/import-file-url.js +6 -0
  212. package/dist/utils/job-queue.d.ts +2 -3
  213. package/dist/utils/jwt.js +1 -1
  214. package/dist/utils/redact-object.js +9 -3
  215. package/dist/utils/sanitize-query.js +3 -0
  216. package/dist/utils/transformations.d.ts +2 -1
  217. package/dist/utils/validate-diff.js +1 -1
  218. package/dist/utils/validate-keys.js +1 -1
  219. package/dist/utils/validate-query.js +2 -1
  220. package/dist/utils/validate-snapshot.js +1 -1
  221. package/dist/websocket/controllers/base.js +1 -1
  222. package/dist/websocket/controllers/index.d.ts +1 -1
  223. package/dist/websocket/controllers/index.js +0 -7
  224. package/dist/websocket/handlers/heartbeat.js +6 -1
  225. package/dist/websocket/handlers/subscribe.js +11 -16
  226. package/dist/websocket/utils/items.d.ts +4 -14
  227. package/dist/websocket/utils/items.js +59 -64
  228. package/dist/worker-pool.d.ts +2 -0
  229. package/dist/worker-pool.js +11 -0
  230. package/package.json +34 -31
  231. package/dist/errors/codes.d.ts +0 -29
  232. package/dist/errors/codes.js +0 -30
  233. package/dist/errors/contains-null-values.d.ts +0 -7
  234. package/dist/errors/contains-null-values.js +0 -4
  235. package/dist/errors/content-too-large.d.ts +0 -1
  236. package/dist/errors/content-too-large.js +0 -3
  237. package/dist/errors/forbidden.d.ts +0 -1
  238. package/dist/errors/forbidden.js +0 -3
  239. package/dist/errors/hit-rate-limit.d.ts +0 -6
  240. package/dist/errors/hit-rate-limit.js +0 -8
  241. package/dist/errors/illegal-asset-transformation.d.ts +0 -4
  242. package/dist/errors/illegal-asset-transformation.js +0 -3
  243. package/dist/errors/index.d.ts +0 -28
  244. package/dist/errors/index.js +0 -28
  245. package/dist/errors/invalid-credentials.d.ts +0 -1
  246. package/dist/errors/invalid-credentials.js +0 -3
  247. package/dist/errors/invalid-foreign-key.d.ts +0 -6
  248. package/dist/errors/invalid-foreign-key.js +0 -14
  249. package/dist/errors/invalid-ip.d.ts +0 -1
  250. package/dist/errors/invalid-ip.js +0 -3
  251. package/dist/errors/invalid-otp.d.ts +0 -1
  252. package/dist/errors/invalid-otp.js +0 -3
  253. package/dist/errors/invalid-payload.d.ts +0 -5
  254. package/dist/errors/invalid-payload.js +0 -4
  255. package/dist/errors/invalid-provider-config.d.ts +0 -5
  256. package/dist/errors/invalid-provider-config.js +0 -3
  257. package/dist/errors/invalid-provider.d.ts +0 -1
  258. package/dist/errors/invalid-provider.js +0 -3
  259. package/dist/errors/invalid-query.d.ts +0 -5
  260. package/dist/errors/invalid-query.js +0 -4
  261. package/dist/errors/invalid-token.d.ts +0 -1
  262. package/dist/errors/invalid-token.js +0 -3
  263. package/dist/errors/method-not-allowed.d.ts +0 -6
  264. package/dist/errors/method-not-allowed.js +0 -6
  265. package/dist/errors/not-null-violation.d.ts +0 -6
  266. package/dist/errors/not-null-violation.js +0 -14
  267. package/dist/errors/range-not-satisfiable.d.ts +0 -7
  268. package/dist/errors/range-not-satisfiable.js +0 -7
  269. package/dist/errors/record-not-unique.d.ts +0 -6
  270. package/dist/errors/record-not-unique.js +0 -14
  271. package/dist/errors/route-not-found.d.ts +0 -5
  272. package/dist/errors/route-not-found.js +0 -4
  273. package/dist/errors/service-unavailable.d.ts +0 -7
  274. package/dist/errors/service-unavailable.js +0 -4
  275. package/dist/errors/token-expired.d.ts +0 -1
  276. package/dist/errors/token-expired.js +0 -3
  277. package/dist/errors/unexpected-response.d.ts +0 -1
  278. package/dist/errors/unexpected-response.js +0 -3
  279. package/dist/errors/unprocessable-content.d.ts +0 -5
  280. package/dist/errors/unprocessable-content.js +0 -4
  281. package/dist/errors/unsupported-media-type.d.ts +0 -6
  282. package/dist/errors/unsupported-media-type.js +0 -4
  283. package/dist/errors/user-suspended.d.ts +0 -1
  284. package/dist/errors/user-suspended.js +0 -3
  285. package/dist/errors/value-out-of-range.d.ts +0 -6
  286. package/dist/errors/value-out-of-range.js +0 -14
  287. package/dist/errors/value-too-long.d.ts +0 -6
  288. package/dist/errors/value-too-long.js +0 -14
  289. package/dist/extensions.d.ts +0 -51
  290. package/dist/extensions.js +0 -487
  291. package/dist/types/files.d.ts +0 -29
  292. /package/dist/{types/files.js → extensions/types.js} +0 -0
@@ -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 = {};
@@ -1,4 +1,4 @@
1
- import type { DirectusError } from '@directus/errors';
1
+ import { type DirectusError } from '@directus/errors';
2
2
  import type { Accountability, Filter, Query, SchemaOverview } from '@directus/types';
3
3
  import type { ArgumentNode, FormattedExecutionResult, FragmentDefinitionNode, GraphQLResolveInfo, SelectionNode } from 'graphql';
4
4
  import { GraphQLError, GraphQLSchema } from 'graphql';
@@ -1,16 +1,14 @@
1
1
  import { Action, FUNCTIONS } from '@directus/constants';
2
- import { isDirectusError } from '@directus/errors';
2
+ import { ErrorCode, ForbiddenError, InvalidPayloadError, isDirectusError } from '@directus/errors';
3
3
  import { parseFilterFunctionPath } from '@directus/utils';
4
4
  import argon2 from 'argon2';
5
5
  import { GraphQLBoolean, GraphQLEnumType, GraphQLError, GraphQLFloat, GraphQLID, GraphQLInt, GraphQLList, GraphQLNonNull, GraphQLObjectType, GraphQLScalarType, GraphQLSchema, GraphQLString, GraphQLUnionType, NoSchemaIntrospectionCustomRule, execute, specifiedRules, validate, } from 'graphql';
6
6
  import { GraphQLJSON, InputTypeComposer, ObjectTypeComposer, SchemaComposer, toInputObjectType } from 'graphql-compose';
7
- import { flatten, get, mapKeys, merge, omit, pick, set, transform, uniq } from 'lodash-es';
7
+ import { assign, flatten, get, mapKeys, merge, omit, pick, set, transform, uniq } from 'lodash-es';
8
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';
14
12
  import { generateHash } from '../../utils/generate-hash.js';
15
13
  import { getGraphQLType } from '../../utils/get-graphql-type.js';
16
14
  import { getMilliseconds } from '../../utils/get-milliseconds.js';
@@ -22,6 +20,7 @@ import { validateQuery } from '../../utils/validate-query.js';
22
20
  import { ActivityService } from '../activity.js';
23
21
  import { AuthenticationService } from '../authentication.js';
24
22
  import { CollectionsService } from '../collections.js';
23
+ import { ExtensionsService } from '../extensions.js';
25
24
  import { FieldsService } from '../fields.js';
26
25
  import { FilesService } from '../files.js';
27
26
  import { RelationsService } from '../relations.js';
@@ -31,6 +30,7 @@ import { SpecificationService } from '../specifications.js';
31
30
  import { TFAService } from '../tfa.js';
32
31
  import { UsersService } from '../users.js';
33
32
  import { UtilsService } from '../utils.js';
33
+ import { VersionsService } from '../versions.js';
34
34
  import { GraphQLExecutionError, GraphQLValidationError } from './errors/index.js';
35
35
  import { createSubscriptionGenerator } from './subscription.js';
36
36
  import { GraphQLBigInt } from './types/bigint.js';
@@ -54,6 +54,7 @@ const SYSTEM_DENY_LIST = [
54
54
  'directus_relations',
55
55
  'directus_migrations',
56
56
  'directus_sessions',
57
+ 'directus_extensions',
57
58
  ];
58
59
  const READ_ONLY = ['directus_activity', 'directus_revisions'];
59
60
  export class GraphQLService {
@@ -842,6 +843,13 @@ export class GraphQLService {
842
843
  },
843
844
  };
844
845
  }
846
+ else {
847
+ resolver.args = {
848
+ version: {
849
+ type: GraphQLString,
850
+ },
851
+ };
852
+ }
845
853
  ReadCollectionTypes[collection.collection].addResolver(resolver);
846
854
  ReadCollectionTypes[collection.collection].addResolver({
847
855
  name: `${collection.collection}_aggregated`,
@@ -877,6 +885,7 @@ export class GraphQLService {
877
885
  type: ReadCollectionTypes[collection.collection],
878
886
  args: {
879
887
  id: new GraphQLNonNull(GraphQLID),
888
+ version: GraphQLString,
880
889
  },
881
890
  resolve: async ({ info, context }) => {
882
891
  const result = await self.resolveQuery(info);
@@ -1147,6 +1156,20 @@ export class GraphQLService {
1147
1156
  }
1148
1157
  }
1149
1158
  const result = await this.read(collection, query);
1159
+ if (args['version']) {
1160
+ const versionsService = new VersionsService({ accountability: this.accountability, schema: this.schema });
1161
+ const saves = await versionsService.getVersionSaves(args['version'], collection, args['id']);
1162
+ if (saves) {
1163
+ if (this.schema.collections[collection].singleton) {
1164
+ return assign(result, ...saves);
1165
+ }
1166
+ else {
1167
+ if (result?.[0] === undefined)
1168
+ return null;
1169
+ return assign(result[0], ...saves);
1170
+ }
1171
+ }
1172
+ }
1150
1173
  if (args['id']) {
1151
1174
  return result?.[0] || null;
1152
1175
  }
@@ -1597,26 +1620,6 @@ export class GraphQLService {
1597
1620
  }
1598
1621
  /** Globally available query */
1599
1622
  schemaComposer.Query.addFields({
1600
- extensions: {
1601
- type: schemaComposer.createObjectTC({
1602
- name: 'extensions',
1603
- fields: {
1604
- interfaces: new GraphQLList(GraphQLString),
1605
- displays: new GraphQLList(GraphQLString),
1606
- layouts: new GraphQLList(GraphQLString),
1607
- modules: new GraphQLList(GraphQLString),
1608
- },
1609
- }),
1610
- resolve: async () => {
1611
- const extensionManager = getExtensionManager();
1612
- return {
1613
- interfaces: extensionManager.getExtensionsList('interface'),
1614
- displays: extensionManager.getExtensionsList('display'),
1615
- layouts: extensionManager.getExtensionsList('layout'),
1616
- modules: extensionManager.getExtensionsList('module'),
1617
- };
1618
- },
1619
- },
1620
1623
  server_specs_oas: {
1621
1624
  type: GraphQLJSON,
1622
1625
  resolve: async () => {
@@ -1678,6 +1681,9 @@ export class GraphQLService {
1678
1681
  const Relation = schemaComposer.createObjectTC({
1679
1682
  name: 'directus_relations',
1680
1683
  });
1684
+ const Extension = schemaComposer.createObjectTC({
1685
+ name: 'directus_extensions',
1686
+ });
1681
1687
  /**
1682
1688
  * Globally available mutations
1683
1689
  */
@@ -2361,6 +2367,63 @@ export class GraphQLService {
2361
2367
  },
2362
2368
  },
2363
2369
  });
2370
+ Extension.addFields({
2371
+ bundle: GraphQLString,
2372
+ name: new GraphQLNonNull(GraphQLString),
2373
+ schema: schemaComposer.createObjectTC({
2374
+ name: 'directus_extensions_schema',
2375
+ fields: {
2376
+ type: GraphQLString,
2377
+ local: GraphQLBoolean,
2378
+ },
2379
+ }),
2380
+ meta: schemaComposer.createObjectTC({
2381
+ name: 'directus_extensions_meta',
2382
+ fields: {
2383
+ enabled: GraphQLBoolean,
2384
+ },
2385
+ }),
2386
+ });
2387
+ schemaComposer.Query.addFields({
2388
+ extensions: {
2389
+ type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(Extension.getType()))),
2390
+ resolve: async () => {
2391
+ const service = new ExtensionsService({
2392
+ accountability: this.accountability,
2393
+ schema: this.schema,
2394
+ });
2395
+ return await service.readAll();
2396
+ },
2397
+ },
2398
+ });
2399
+ schemaComposer.Mutation.addFields({
2400
+ update_extensions_item: {
2401
+ type: Extension,
2402
+ args: {
2403
+ bundle: GraphQLString,
2404
+ name: new GraphQLNonNull(GraphQLString),
2405
+ data: toInputObjectType(schemaComposer.createObjectTC({
2406
+ name: 'update_directus_extensions_input',
2407
+ fields: {
2408
+ meta: schemaComposer.createObjectTC({
2409
+ name: 'update_directus_extensions_input_meta',
2410
+ fields: {
2411
+ enabled: GraphQLBoolean,
2412
+ },
2413
+ }),
2414
+ },
2415
+ })),
2416
+ },
2417
+ resolve: async (_, args) => {
2418
+ const extensionsService = new ExtensionsService({
2419
+ accountability: this.accountability,
2420
+ schema: this.schema,
2421
+ });
2422
+ await extensionsService.updateOne(args['bundle'], args['name'], args['data']);
2423
+ return await extensionsService.readOne(args['bundle'], args['name']);
2424
+ },
2425
+ },
2426
+ });
2364
2427
  }
2365
2428
  if ('directus_users' in schema.read.collections) {
2366
2429
  schemaComposer.Query.addFields({
@@ -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
@@ -4,12 +4,13 @@ export * from './authentication.js';
4
4
  export * from './authorization.js';
5
5
  export * from './collections.js';
6
6
  export * from './dashboards.js';
7
+ export * from './extensions.js';
7
8
  export * from './fields.js';
8
9
  export * from './files.js';
9
10
  export * from './flows.js';
10
11
  export * from './folders.js';
11
12
  export * from './graphql/index.js';
12
- export * from './import-export.js';
13
+ export * from './import-export/index.js';
13
14
  export * from './items.js';
14
15
  export * from './mail/index.js';
15
16
  export * from './meta.js';
@@ -31,5 +32,6 @@ export * from './tfa.js';
31
32
  export * from './translations.js';
32
33
  export * from './users.js';
33
34
  export * from './utils.js';
35
+ export * from './versions.js';
34
36
  export * from './webhooks.js';
35
37
  export * from './websocket.js';
@@ -4,12 +4,13 @@ export * from './authentication.js';
4
4
  export * from './authorization.js';
5
5
  export * from './collections.js';
6
6
  export * from './dashboards.js';
7
+ export * from './extensions.js';
7
8
  export * from './fields.js';
8
9
  export * from './files.js';
9
10
  export * from './flows.js';
10
11
  export * from './folders.js';
11
12
  export * from './graphql/index.js';
12
- export * from './import-export.js';
13
+ export * from './import-export/index.js';
13
14
  export * from './items.js';
14
15
  export * from './mail/index.js';
15
16
  export * from './meta.js';
@@ -31,5 +32,6 @@ export * from './tfa.js';
31
32
  export * from './translations.js';
32
33
  export * from './users.js';
33
34
  export * from './utils.js';
35
+ export * from './versions.js';
34
36
  export * from './webhooks.js';
35
37
  export * from './websocket.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';
@@ -12,8 +12,8 @@ import { rateLimiter } from '../middleware/rate-limiter-ip.js';
12
12
  import { SERVER_ONLINE } from '../server.js';
13
13
  import { getStorage } from '../storage/index.js';
14
14
  import { version } from '../utils/package.js';
15
- import { SettingsService } from './settings.js';
16
15
  import { toBoolean } from '../utils/to-boolean.js';
16
+ import { SettingsService } from './settings.js';
17
17
  export class ServerService {
18
18
  knex;
19
19
  accountability;
@@ -33,9 +33,11 @@ export class ServerService {
33
33
  'project_descriptor',
34
34
  'project_logo',
35
35
  'project_color',
36
+ 'default_appearance',
36
37
  'default_language',
37
38
  'public_foreground',
38
39
  'public_background',
40
+ 'public_favicon',
39
41
  'public_note',
40
42
  'custom_css',
41
43
  ],
@@ -1,7 +1,7 @@
1
1
  import argon2 from 'argon2';
2
2
  import jwt from 'jsonwebtoken';
3
3
  import env from '../env.js';
4
- import { ForbiddenError, InvalidCredentialsError } from '../errors/index.js';
4
+ import { ForbiddenError, InvalidCredentialsError } from '@directus/errors';
5
5
  import { getMilliseconds } from '../utils/get-milliseconds.js';
6
6
  import { md } from '../utils/md.js';
7
7
  import { Url } from '../utils/url.js';
@@ -1,6 +1,6 @@
1
1
  import { authenticator } from 'otplib';
2
2
  import getDatabase from '../database/index.js';
3
- import { InvalidPayloadError } from '../errors/index.js';
3
+ import { InvalidPayloadError } from '@directus/errors';
4
4
  import { ItemsService } from './items.js';
5
5
  export class TFAService {
6
6
  knex;
@@ -1,5 +1,5 @@
1
1
  import getDatabase from '../database/index.js';
2
- import { InvalidPayloadError } from '../errors/index.js';
2
+ import { InvalidPayloadError } from '@directus/errors';
3
3
  import { ItemsService } from './items.js';
4
4
  export class TranslationsService extends ItemsService {
5
5
  constructor(options) {