directus 9.10.0 → 9.12.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 (141) hide show
  1. package/dist/app.js +8 -1
  2. package/dist/auth/drivers/oauth2.d.ts +1 -1
  3. package/dist/auth/drivers/oauth2.js +14 -11
  4. package/dist/auth/drivers/openid.d.ts +1 -1
  5. package/dist/auth/drivers/openid.js +14 -11
  6. package/dist/cli/commands/schema/apply.js +4 -3
  7. package/dist/cli/utils/create-env/env-stub.liquid +266 -9
  8. package/dist/controllers/activity.js +1 -1
  9. package/dist/controllers/assets.js +8 -9
  10. package/dist/controllers/flows.d.ts +2 -0
  11. package/dist/controllers/flows.js +157 -0
  12. package/dist/controllers/folders.js +1 -1
  13. package/dist/controllers/notifications.js +1 -1
  14. package/dist/controllers/operations.d.ts +2 -0
  15. package/dist/controllers/operations.js +138 -0
  16. package/dist/database/helpers/date/dialects/sqlite.js +6 -2
  17. package/dist/database/index.js +15 -19
  18. package/dist/database/migrations/20210225A-add-relations-sort-field.js +2 -1
  19. package/dist/database/migrations/20210506A-rename-interfaces.js +2 -1
  20. package/dist/database/migrations/20210802A-replace-groups.js +2 -1
  21. package/dist/database/migrations/20210805A-update-groups.js +2 -1
  22. package/dist/database/migrations/20210805B-change-image-metadata-structure.js +3 -2
  23. package/dist/database/migrations/20211007A-update-presets.js +5 -4
  24. package/dist/database/migrations/20220429A-add-flows.d.ts +3 -0
  25. package/dist/database/migrations/20220429A-add-flows.js +83 -0
  26. package/dist/database/migrations/20220429B-add-color-to-insights-icon.d.ts +3 -0
  27. package/dist/database/migrations/20220429B-add-color-to-insights-icon.js +15 -0
  28. package/dist/database/migrations/20220429C-drop-non-null-from-ip-of-activity.d.ts +3 -0
  29. package/dist/database/migrations/20220429C-drop-non-null-from-ip-of-activity.js +15 -0
  30. package/dist/database/migrations/20220429D-drop-non-null-from-sender-of-notifications.d.ts +3 -0
  31. package/dist/database/migrations/20220429D-drop-non-null-from-sender-of-notifications.js +15 -0
  32. package/dist/database/run-ast.js +10 -14
  33. package/dist/database/seeds/05-activity.yaml +0 -1
  34. package/dist/database/system-data/collections/collections.yaml +4 -0
  35. package/dist/database/system-data/fields/activity.yaml +3 -0
  36. package/dist/database/system-data/fields/dashboards.yaml +3 -1
  37. package/dist/database/system-data/fields/flows.yaml +21 -0
  38. package/dist/database/system-data/fields/notifications.yaml +3 -1
  39. package/dist/database/system-data/fields/operations.yaml +19 -0
  40. package/dist/database/system-data/fields/panels.yaml +3 -1
  41. package/dist/database/system-data/fields/shares.yaml +3 -1
  42. package/dist/database/system-data/fields/users.yaml +2 -4
  43. package/dist/database/system-data/relations/relations.yaml +20 -0
  44. package/dist/env.d.ts +1 -1
  45. package/dist/env.js +167 -12
  46. package/dist/exceptions/index.d.ts +1 -0
  47. package/dist/exceptions/index.js +1 -0
  48. package/dist/exceptions/invalid-provider.d.ts +4 -0
  49. package/dist/exceptions/invalid-provider.js +10 -0
  50. package/dist/exceptions/range-not-satisfiable.d.ts +2 -2
  51. package/dist/exceptions/range-not-satisfiable.js +5 -1
  52. package/dist/extensions.d.ts +3 -0
  53. package/dist/extensions.js +73 -20
  54. package/dist/flows.d.ts +17 -0
  55. package/dist/flows.js +310 -0
  56. package/dist/messenger.d.ts +24 -0
  57. package/dist/messenger.js +64 -0
  58. package/dist/middleware/graphql.js +2 -1
  59. package/dist/operations/condition/index.d.ts +6 -0
  60. package/dist/operations/condition/index.js +15 -0
  61. package/dist/operations/item-create/index.d.ts +8 -0
  62. package/dist/operations/item-create/index.js +40 -0
  63. package/dist/operations/item-delete/index.d.ts +9 -0
  64. package/dist/operations/item-delete/index.js +45 -0
  65. package/dist/operations/item-read/index.d.ts +9 -0
  66. package/dist/operations/item-read/index.js +45 -0
  67. package/dist/operations/item-update/index.d.ts +10 -0
  68. package/dist/operations/item-update/index.js +50 -0
  69. package/dist/operations/log/index.d.ts +5 -0
  70. package/dist/operations/log/index.js +14 -0
  71. package/dist/operations/mail/index.d.ts +7 -0
  72. package/dist/operations/mail/index.js +16 -0
  73. package/dist/operations/notification/index.d.ts +8 -0
  74. package/dist/operations/notification/index.js +39 -0
  75. package/dist/operations/request/index.d.ts +9 -0
  76. package/dist/operations/request/index.js +14 -0
  77. package/dist/operations/sleep/index.d.ts +5 -0
  78. package/dist/operations/sleep/index.js +9 -0
  79. package/dist/operations/transform/index.d.ts +5 -0
  80. package/dist/operations/transform/index.js +10 -0
  81. package/dist/operations/trigger/index.d.ts +6 -0
  82. package/dist/operations/trigger/index.js +21 -0
  83. package/dist/services/activity.d.ts +1 -2
  84. package/dist/services/activity.js +10 -10
  85. package/dist/services/assets.js +27 -1
  86. package/dist/services/authentication.d.ts +2 -2
  87. package/dist/services/authentication.js +11 -8
  88. package/dist/services/authorization.js +12 -0
  89. package/dist/services/fields.js +15 -8
  90. package/dist/services/flows.d.ts +14 -0
  91. package/dist/services/flows.js +42 -0
  92. package/dist/services/graphql.js +56 -33
  93. package/dist/services/import-export.d.ts +1 -1
  94. package/dist/services/import-export.js +13 -12
  95. package/dist/services/index.d.ts +2 -0
  96. package/dist/services/index.js +2 -0
  97. package/dist/services/items.d.ts +3 -3
  98. package/dist/services/items.js +25 -2
  99. package/dist/services/mail/index.js +2 -1
  100. package/dist/services/notifications.d.ts +2 -1
  101. package/dist/services/notifications.js +4 -3
  102. package/dist/services/operations.d.ts +14 -0
  103. package/dist/services/operations.js +42 -0
  104. package/dist/services/payload.d.ts +2 -2
  105. package/dist/services/payload.js +8 -7
  106. package/dist/services/users.d.ts +4 -0
  107. package/dist/services/users.js +20 -0
  108. package/dist/services/webhooks.d.ts +2 -0
  109. package/dist/services/webhooks.js +8 -7
  110. package/dist/types/events.d.ts +18 -0
  111. package/dist/types/events.js +2 -0
  112. package/dist/types/index.d.ts +1 -1
  113. package/dist/types/index.js +1 -1
  114. package/dist/utils/apply-query.js +31 -4
  115. package/dist/utils/apply-snapshot.d.ts +3 -3
  116. package/dist/utils/apply-snapshot.js +64 -49
  117. package/dist/utils/construct-flow-tree.d.ts +2 -0
  118. package/dist/utils/construct-flow-tree.js +31 -0
  119. package/dist/utils/get-accountability-for-role.d.ts +7 -0
  120. package/dist/utils/get-accountability-for-role.js +36 -0
  121. package/dist/utils/get-ast-from-query.js +1 -7
  122. package/dist/utils/get-default-value.js +4 -3
  123. package/dist/utils/get-permissions.d.ts +1 -1
  124. package/dist/utils/get-permissions.js +9 -8
  125. package/dist/utils/get-schema.js +2 -1
  126. package/dist/utils/get-snapshot.js +22 -4
  127. package/dist/utils/operation-options.d.ts +3 -0
  128. package/dist/utils/operation-options.js +45 -0
  129. package/dist/utils/parse-json.d.ts +5 -0
  130. package/dist/utils/parse-json.js +19 -0
  131. package/dist/utils/sanitize-query.d.ts +1 -2
  132. package/dist/utils/sanitize-query.js +6 -5
  133. package/dist/utils/validate-keys.d.ts +6 -0
  134. package/dist/utils/validate-keys.js +28 -0
  135. package/dist/utils/validate-query.js +1 -1
  136. package/dist/webhooks.d.ts +2 -0
  137. package/dist/webhooks.js +17 -2
  138. package/package.json +18 -14
  139. package/dist/types/activity.d.ts +0 -9
  140. package/dist/types/activity.js +0 -13
  141. package/example.env +0 -202
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FlowsService = void 0;
4
+ const messenger_1 = require("../messenger");
5
+ const items_1 = require("./items");
6
+ class FlowsService extends items_1.ItemsService {
7
+ constructor(options) {
8
+ super('directus_flows', options);
9
+ this.messenger = (0, messenger_1.getMessenger)();
10
+ }
11
+ async createOne(data, opts) {
12
+ const result = await super.createOne(data, opts);
13
+ this.messenger.publish('flows', { type: 'reload' });
14
+ return result;
15
+ }
16
+ async createMany(data, opts) {
17
+ const result = await super.createMany(data, opts);
18
+ this.messenger.publish('flows', { type: 'reload' });
19
+ return result;
20
+ }
21
+ async updateOne(key, data, opts) {
22
+ const result = await super.updateOne(key, data, opts);
23
+ this.messenger.publish('flows', { type: 'reload' });
24
+ return result;
25
+ }
26
+ async updateMany(keys, data, opts) {
27
+ const result = await super.updateMany(keys, data, opts);
28
+ this.messenger.publish('flows', { type: 'reload' });
29
+ return result;
30
+ }
31
+ async deleteOne(key, opts) {
32
+ const result = await super.deleteOne(key, opts);
33
+ this.messenger.publish('flows', { type: 'reload' });
34
+ return result;
35
+ }
36
+ async deleteMany(keys, opts) {
37
+ const result = await super.deleteMany(keys, opts);
38
+ this.messenger.publish('flows', { type: 'reload' });
39
+ return result;
40
+ }
41
+ }
42
+ exports.FlowsService = FlowsService;
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.GraphQLService = exports.GraphQLDate = exports.GraphQLGeoJSON = void 0;
7
+ const types_1 = require("@directus/shared/types");
7
8
  const argon2_1 = __importDefault(require("argon2"));
8
9
  const graphql_1 = require("graphql");
9
10
  const graphql_compose_1 = require("graphql-compose");
@@ -15,7 +16,6 @@ const database_1 = __importDefault(require("../database"));
15
16
  const env_1 = __importDefault(require("../env"));
16
17
  const exceptions_1 = require("../exceptions");
17
18
  const extensions_1 = require("../extensions");
18
- const types_1 = require("../types");
19
19
  const generate_hash_1 = require("../utils/generate-hash");
20
20
  const get_graphql_type_1 = require("../utils/get-graphql-type");
21
21
  const reduce_schema_1 = require("../utils/reduce-schema");
@@ -26,9 +26,11 @@ const authentication_1 = require("./authentication");
26
26
  const collections_1 = require("./collections");
27
27
  const fields_1 = require("./fields");
28
28
  const files_1 = require("./files");
29
+ const flows_1 = require("./flows");
29
30
  const folders_1 = require("./folders");
30
31
  const items_1 = require("./items");
31
32
  const notifications_1 = require("./notifications");
33
+ const operations_1 = require("./operations");
32
34
  const permissions_1 = require("./permissions");
33
35
  const presets_1 = require("./presets");
34
36
  const relations_1 = require("./relations");
@@ -170,16 +172,7 @@ class GraphQLService {
170
172
  acc[collectionName] = ReadCollectionTypes[collection.collection].getResolver(collection.collection);
171
173
  if (this.schema.collections[collection.collection].singleton === false) {
172
174
  acc[`${collectionName}_by_id`] = ReadCollectionTypes[collection.collection].getResolver(`${collection.collection}_by_id`);
173
- const hasAggregate = Object.values(collection.fields).some((field) => {
174
- const graphqlType = (0, get_graphql_type_1.getGraphQLType)(field.type);
175
- if (graphqlType === graphql_1.GraphQLInt || graphqlType === graphql_1.GraphQLFloat) {
176
- return true;
177
- }
178
- return false;
179
- });
180
- if (hasAggregate) {
181
- acc[`${collectionName}_aggregated`] = ReadCollectionTypes[collection.collection].getResolver(`${collection.collection}_aggregated`);
182
- }
175
+ acc[`${collectionName}_aggregated`] = ReadCollectionTypes[collection.collection].getResolver(`${collection.collection}_aggregated`);
183
176
  }
184
177
  return acc;
185
178
  }, {}));
@@ -424,7 +417,8 @@ class GraphQLService {
424
417
  const { CollectionTypes: ReadCollectionTypes } = getTypes('read');
425
418
  const ReadableCollectionFilterTypes = {};
426
419
  const AggregatedFunctions = {};
427
- const AggregatedFilters = {};
420
+ const AggregatedFields = {};
421
+ const AggregateMethods = {};
428
422
  const StringFilterOperators = schemaComposer.createInputTC({
429
423
  name: 'string_filter_operators',
430
424
  fields: {
@@ -680,7 +674,7 @@ class GraphQLService {
680
674
  _and: [ReadableCollectionFilterTypes[collection.collection]],
681
675
  _or: [ReadableCollectionFilterTypes[collection.collection]],
682
676
  });
683
- AggregatedFilters[collection.collection] = schemaComposer.createObjectTC({
677
+ AggregatedFields[collection.collection] = schemaComposer.createObjectTC({
684
678
  name: `${collection.collection}_aggregated_fields`,
685
679
  fields: Object.values(collection.fields).reduce((acc, field) => {
686
680
  const graphqlType = (0, get_graphql_type_1.getGraphQLType)(field.type);
@@ -698,46 +692,71 @@ class GraphQLService {
698
692
  return acc;
699
693
  }, {}),
700
694
  });
701
- AggregatedFunctions[collection.collection] = schemaComposer.createObjectTC({
702
- name: `${collection.collection}_aggregated`,
703
- fields: {
704
- group: {
705
- name: 'group',
706
- type: graphql_compose_1.GraphQLJSON,
707
- },
695
+ AggregateMethods[collection.collection] = {
696
+ group: {
697
+ name: 'group',
698
+ type: graphql_compose_1.GraphQLJSON,
699
+ },
700
+ countAll: {
701
+ name: 'countAll',
702
+ type: graphql_1.GraphQLInt,
703
+ },
704
+ count: {
705
+ name: 'count',
706
+ type: schemaComposer.createObjectTC({
707
+ name: `${collection.collection}_aggregated_count`,
708
+ fields: Object.values(collection.fields).reduce((acc, field) => {
709
+ acc[field.field] = {
710
+ type: graphql_1.GraphQLInt,
711
+ description: field.note,
712
+ };
713
+ return acc;
714
+ }, {}),
715
+ }),
716
+ },
717
+ };
718
+ const hasNumericAggregates = Object.values(collection.fields).some((field) => {
719
+ const graphqlType = (0, get_graphql_type_1.getGraphQLType)(field.type);
720
+ if (graphqlType === graphql_1.GraphQLInt || graphqlType === graphql_1.GraphQLFloat) {
721
+ return true;
722
+ }
723
+ return false;
724
+ });
725
+ if (hasNumericAggregates) {
726
+ Object.assign(AggregateMethods[collection.collection], {
708
727
  avg: {
709
728
  name: 'avg',
710
- type: AggregatedFilters[collection.collection],
729
+ type: AggregatedFields[collection.collection],
711
730
  },
712
731
  sum: {
713
732
  name: 'sum',
714
- type: AggregatedFilters[collection.collection],
715
- },
716
- count: {
717
- name: 'count',
718
- type: AggregatedFilters[collection.collection],
733
+ type: AggregatedFields[collection.collection],
719
734
  },
720
735
  countDistinct: {
721
736
  name: 'countDistinct',
722
- type: AggregatedFilters[collection.collection],
737
+ type: AggregatedFields[collection.collection],
723
738
  },
724
739
  avgDistinct: {
725
740
  name: 'avgDistinct',
726
- type: AggregatedFilters[collection.collection],
741
+ type: AggregatedFields[collection.collection],
727
742
  },
728
743
  sumDistinct: {
729
744
  name: 'sumDistinct',
730
- type: AggregatedFilters[collection.collection],
745
+ type: AggregatedFields[collection.collection],
731
746
  },
732
747
  min: {
733
748
  name: 'min',
734
- type: AggregatedFilters[collection.collection],
749
+ type: AggregatedFields[collection.collection],
735
750
  },
736
751
  max: {
737
752
  name: 'max',
738
- type: AggregatedFilters[collection.collection],
753
+ type: AggregatedFields[collection.collection],
739
754
  },
740
- },
755
+ });
756
+ }
757
+ AggregatedFunctions[collection.collection] = schemaComposer.createObjectTC({
758
+ name: `${collection.collection}_aggregated`,
759
+ fields: AggregateMethods[collection.collection],
741
760
  });
742
761
  ReadCollectionTypes[collection.collection].addResolver({
743
762
  name: collection.collection,
@@ -1237,7 +1256,7 @@ class GraphQLService {
1237
1256
  if (!query.deep)
1238
1257
  query.deep = {};
1239
1258
  const args = this.parseArgs(selection.arguments, variableValues);
1240
- (0, lodash_1.set)(query.deep, current, (0, lodash_1.merge)((0, lodash_1.get)(query.deep, current), (0, lodash_1.mapKeys)((0, sanitize_query_1.sanitizeQuery)(args, this.accountability), (value, key) => `_${key}`)));
1259
+ (0, lodash_1.set)(query.deep, currentAlias !== null && currentAlias !== void 0 ? currentAlias : current, (0, lodash_1.merge)((0, lodash_1.get)(query.deep, currentAlias !== null && currentAlias !== void 0 ? currentAlias : current), (0, lodash_1.mapKeys)((0, sanitize_query_1.sanitizeQuery)(args, this.accountability), (value, key) => `_${key}`)));
1241
1260
  }
1242
1261
  }
1243
1262
  }
@@ -1337,6 +1356,10 @@ class GraphQLService {
1337
1356
  return new webhooks_1.WebhooksService(opts);
1338
1357
  case 'directus_shares':
1339
1358
  return new shares_1.SharesService(opts);
1359
+ case 'directus_flows':
1360
+ return new flows_1.FlowsService(opts);
1361
+ case 'directus_operations':
1362
+ return new operations_1.OperationsService(opts);
1340
1363
  default:
1341
1364
  return new items_1.ItemsService(collection, opts);
1342
1365
  }
@@ -1,7 +1,7 @@
1
1
  /// <reference types="node" />
2
+ import { Accountability, Query, SchemaOverview } from '@directus/shared/types';
2
3
  import { Knex } from 'knex';
3
4
  import { AbstractServiceOptions, File } from '../types';
4
- import { Accountability, Query, SchemaOverview } from '@directus/shared/types';
5
5
  export declare class ImportService {
6
6
  knex: Knex;
7
7
  accountability: Accountability | null;
@@ -4,25 +4,26 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.ExportService = exports.ImportService = void 0;
7
- const database_1 = __importDefault(require("../database"));
8
- const exceptions_1 = require("../exceptions");
9
- const StreamArray_1 = __importDefault(require("stream-json/streamers/StreamArray"));
10
- const items_1 = require("./items");
7
+ const utils_1 = require("@directus/shared/utils");
11
8
  const async_1 = require("async");
12
- const destroy_1 = __importDefault(require("destroy"));
13
9
  const csv_parser_1 = __importDefault(require("csv-parser"));
14
- const lodash_1 = require("lodash");
10
+ const destroy_1 = __importDefault(require("destroy"));
11
+ const fs_extra_1 = require("fs-extra");
15
12
  const js2xmlparser_1 = require("js2xmlparser");
16
13
  const json2csv_1 = require("json2csv");
17
- const fs_extra_1 = require("fs-extra");
14
+ const lodash_1 = require("lodash");
15
+ const StreamArray_1 = __importDefault(require("stream-json/streamers/StreamArray"));
16
+ const strip_bom_stream_1 = __importDefault(require("strip-bom-stream"));
18
17
  const tmp_promise_1 = require("tmp-promise");
18
+ const database_1 = __importDefault(require("../database"));
19
19
  const env_1 = __importDefault(require("../env"));
20
- const files_1 = require("./files");
20
+ const exceptions_1 = require("../exceptions");
21
+ const logger_1 = __importDefault(require("../logger"));
21
22
  const get_date_formatted_1 = require("../utils/get-date-formatted");
22
- const utils_1 = require("@directus/shared/utils");
23
+ const parse_json_1 = require("../utils/parse-json");
24
+ const files_1 = require("./files");
25
+ const items_1 = require("./items");
23
26
  const notifications_1 = require("./notifications");
24
- const logger_1 = __importDefault(require("../logger"));
25
- const strip_bom_stream_1 = __importDefault(require("strip-bom-stream"));
26
27
  class ImportService {
27
28
  constructor(options) {
28
29
  this.knex = options.knex || (0, database_1.default)();
@@ -101,7 +102,7 @@ class ImportService {
101
102
  }
102
103
  else {
103
104
  try {
104
- const parsedJson = JSON.parse(value);
105
+ const parsedJson = (0, parse_json_1.parseJSON)(value);
105
106
  (0, lodash_1.set)(result, key, parsedJson);
106
107
  }
107
108
  catch {
@@ -7,12 +7,14 @@ export * from './collections';
7
7
  export * from './dashboards';
8
8
  export * from './fields';
9
9
  export * from './files';
10
+ export * from './flows';
10
11
  export * from './folders';
11
12
  export * from './graphql';
12
13
  export * from './import-export';
13
14
  export * from './mail';
14
15
  export * from './meta';
15
16
  export * from './notifications';
17
+ export * from './operations';
16
18
  export * from './panels';
17
19
  export * from './payload';
18
20
  export * from './permissions';
@@ -20,12 +20,14 @@ __exportStar(require("./collections"), exports);
20
20
  __exportStar(require("./dashboards"), exports);
21
21
  __exportStar(require("./fields"), exports);
22
22
  __exportStar(require("./files"), exports);
23
+ __exportStar(require("./flows"), exports);
23
24
  __exportStar(require("./folders"), exports);
24
25
  __exportStar(require("./graphql"), exports);
25
26
  __exportStar(require("./import-export"), exports);
26
27
  __exportStar(require("./mail"), exports);
27
28
  __exportStar(require("./meta"), exports);
28
29
  __exportStar(require("./notifications"), exports);
30
+ __exportStar(require("./operations"), exports);
29
31
  __exportStar(require("./panels"), exports);
30
32
  __exportStar(require("./payload"), exports);
31
33
  __exportStar(require("./permissions"), exports);
@@ -1,7 +1,7 @@
1
- import { Knex } from 'knex';
1
+ import { Accountability, PermissionsAction, Query, SchemaOverview } from '@directus/shared/types';
2
2
  import Keyv from 'keyv';
3
- import { Accountability, Query, PermissionsAction, SchemaOverview } from '@directus/shared/types';
4
- import { AbstractService, AbstractServiceOptions, Item as AnyItem, PrimaryKey, MutationOptions } from '../types';
3
+ import { Knex } from 'knex';
4
+ import { AbstractService, AbstractServiceOptions, Item as AnyItem, MutationOptions, PrimaryKey } from '../types';
5
5
  export declare type QueryOptions = {
6
6
  stripNonRequested?: boolean;
7
7
  permissionsAction?: PermissionsAction;
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.ItemsService = void 0;
7
+ const types_1 = require("@directus/shared/types");
7
8
  const lodash_1 = require("lodash");
8
9
  const cache_1 = require("../cache");
9
10
  const database_1 = __importDefault(require("../database"));
@@ -12,11 +13,11 @@ const emitter_1 = __importDefault(require("../emitter"));
12
13
  const env_1 = __importDefault(require("../env"));
13
14
  const exceptions_1 = require("../exceptions");
14
15
  const translate_1 = require("../exceptions/database/translate");
15
- const types_1 = require("../types");
16
16
  const get_ast_from_query_1 = __importDefault(require("../utils/get-ast-from-query"));
17
17
  const authorization_1 = require("./authorization");
18
- const payload_1 = require("./payload");
19
18
  const index_1 = require("./index");
19
+ const payload_1 = require("./payload");
20
+ const validate_keys_1 = require("../utils/validate-keys");
20
21
  class ItemsService {
21
22
  constructor(collection, options) {
22
23
  this.collection = collection;
@@ -244,6 +245,7 @@ class ItemsService {
244
245
  */
245
246
  async readOne(key, query = {}, opts) {
246
247
  const primaryKeyField = this.schema.collections[this.collection].primary;
248
+ (0, validate_keys_1.validateKeys)(this.schema, this.collection, primaryKeyField, key);
247
249
  const filterWithKey = (0, lodash_1.assign)({}, query.filter, { [primaryKeyField]: { _eq: key } });
248
250
  const queryWithKey = (0, lodash_1.assign)({}, query, { filter: filterWithKey });
249
251
  const results = await this.readByQuery(queryWithKey, opts);
@@ -258,8 +260,13 @@ class ItemsService {
258
260
  async readMany(keys, query = {}, opts) {
259
261
  var _a;
260
262
  const primaryKeyField = this.schema.collections[this.collection].primary;
263
+ (0, validate_keys_1.validateKeys)(this.schema, this.collection, primaryKeyField, keys);
261
264
  const filterWithKey = { _and: [{ [primaryKeyField]: { _in: keys } }, (_a = query.filter) !== null && _a !== void 0 ? _a : {}] };
262
265
  const queryWithKey = (0, lodash_1.assign)({}, query, { filter: filterWithKey });
266
+ // Set query limit as the number of keys
267
+ if (Array.isArray(keys) && keys.length > 0 && !queryWithKey.limit) {
268
+ queryWithKey.limit = keys.length;
269
+ }
263
270
  const results = await this.readByQuery(queryWithKey, opts);
264
271
  return results;
265
272
  }
@@ -268,12 +275,16 @@ class ItemsService {
268
275
  */
269
276
  async updateByQuery(query, data, opts) {
270
277
  const keys = await this.getKeysByQuery(query);
278
+ const primaryKeyField = this.schema.collections[this.collection].primary;
279
+ (0, validate_keys_1.validateKeys)(this.schema, this.collection, primaryKeyField, keys);
271
280
  return keys.length ? await this.updateMany(keys, data, opts) : [];
272
281
  }
273
282
  /**
274
283
  * Update a single item by primary key
275
284
  */
276
285
  async updateOne(key, data, opts) {
286
+ const primaryKeyField = this.schema.collections[this.collection].primary;
287
+ (0, validate_keys_1.validateKeys)(this.schema, this.collection, primaryKeyField, key);
277
288
  await this.updateMany([key], data, opts);
278
289
  return key;
279
290
  }
@@ -282,6 +293,7 @@ class ItemsService {
282
293
  */
283
294
  async updateMany(keys, data, opts) {
284
295
  const primaryKeyField = this.schema.collections[this.collection].primary;
296
+ (0, validate_keys_1.validateKeys)(this.schema, this.collection, primaryKeyField, keys);
285
297
  const fields = Object.keys(this.schema.collections[this.collection].fields);
286
298
  const aliases = Object.values(this.schema.collections[this.collection].fields)
287
299
  .filter((field) => field.alias === true)
@@ -306,6 +318,8 @@ class ItemsService {
306
318
  accountability: this.accountability,
307
319
  })
308
320
  : payload;
321
+ // Sort keys to ensure that the order is maintained
322
+ keys.sort();
309
323
  if (this.accountability) {
310
324
  await authorizationService.checkAccess('update', this.collection, keys);
311
325
  }
@@ -409,6 +423,9 @@ class ItemsService {
409
423
  async upsertOne(payload, opts) {
410
424
  const primaryKeyField = this.schema.collections[this.collection].primary;
411
425
  const primaryKey = payload[primaryKeyField];
426
+ if (primaryKey) {
427
+ (0, validate_keys_1.validateKeys)(this.schema, this.collection, primaryKeyField, primaryKey);
428
+ }
412
429
  const exists = primaryKey &&
413
430
  !!(await this.knex
414
431
  .select(primaryKeyField)
@@ -449,12 +466,16 @@ class ItemsService {
449
466
  */
450
467
  async deleteByQuery(query, opts) {
451
468
  const keys = await this.getKeysByQuery(query);
469
+ const primaryKeyField = this.schema.collections[this.collection].primary;
470
+ (0, validate_keys_1.validateKeys)(this.schema, this.collection, primaryKeyField, keys);
452
471
  return keys.length ? await this.deleteMany(keys, opts) : [];
453
472
  }
454
473
  /**
455
474
  * Delete a single item by primary key
456
475
  */
457
476
  async deleteOne(key, opts) {
477
+ const primaryKeyField = this.schema.collections[this.collection].primary;
478
+ (0, validate_keys_1.validateKeys)(this.schema, this.collection, primaryKeyField, key);
458
479
  await this.deleteMany([key], opts);
459
480
  return key;
460
481
  }
@@ -463,10 +484,12 @@ class ItemsService {
463
484
  */
464
485
  async deleteMany(keys, opts) {
465
486
  const primaryKeyField = this.schema.collections[this.collection].primary;
487
+ (0, validate_keys_1.validateKeys)(this.schema, this.collection, primaryKeyField, keys);
466
488
  if (this.accountability && this.accountability.admin !== true) {
467
489
  const authorizationService = new authorization_1.AuthorizationService({
468
490
  accountability: this.accountability,
469
491
  schema: this.schema,
492
+ knex: this.knex,
470
493
  });
471
494
  await authorizationService.checkAccess('delete', this.collection, keys);
472
495
  }
@@ -65,13 +65,14 @@ class MailService {
65
65
  }
66
66
  async getDefaultTemplateData() {
67
67
  const projectInfo = await this.knex
68
- .select(['project_name', 'project_logo', 'project_color'])
68
+ .select(['project_name', 'project_logo', 'project_color', 'project_url'])
69
69
  .from('directus_settings')
70
70
  .first();
71
71
  return {
72
72
  projectName: (projectInfo === null || projectInfo === void 0 ? void 0 : projectInfo.project_name) || 'Directus',
73
73
  projectColor: (projectInfo === null || projectInfo === void 0 ? void 0 : projectInfo.project_color) || '#546e7a',
74
74
  projectLogo: getProjectLogoURL(projectInfo === null || projectInfo === void 0 ? void 0 : projectInfo.project_logo),
75
+ projectUrl: (projectInfo === null || projectInfo === void 0 ? void 0 : projectInfo.project_url) || '',
75
76
  };
76
77
  function getProjectLogoURL(logoID) {
77
78
  const projectLogoUrl = new url_1.Url(env_1.default.PUBLIC_URL);
@@ -1,7 +1,8 @@
1
- import { UsersService, MailService } from '.';
2
1
  import { AbstractServiceOptions, PrimaryKey, MutationOptions } from '../types';
3
2
  import { ItemsService } from './items';
4
3
  import { Notification } from '@directus/shared/types';
4
+ import { UsersService } from './users';
5
+ import { MailService } from './mail';
5
6
  export declare class NotificationsService extends ItemsService {
6
7
  usersService: UsersService;
7
8
  mailService: MailService;
@@ -1,14 +1,15 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.NotificationsService = void 0;
4
- const _1 = require(".");
5
4
  const items_1 = require("./items");
6
5
  const md_1 = require("../utils/md");
6
+ const users_1 = require("./users");
7
+ const mail_1 = require("./mail");
7
8
  class NotificationsService extends items_1.ItemsService {
8
9
  constructor(options) {
9
10
  super('directus_notifications', options);
10
- this.usersService = new _1.UsersService({ schema: this.schema });
11
- this.mailService = new _1.MailService({ schema: this.schema, accountability: this.accountability });
11
+ this.usersService = new users_1.UsersService({ schema: this.schema });
12
+ this.mailService = new mail_1.MailService({ schema: this.schema, accountability: this.accountability });
12
13
  }
13
14
  async createOne(data, opts) {
14
15
  await this.sendEmail(data);
@@ -0,0 +1,14 @@
1
+ import { OperationRaw } from '@directus/shared/types';
2
+ import { Messenger } from '../messenger';
3
+ import { AbstractServiceOptions, Item, MutationOptions, PrimaryKey } from '../types';
4
+ import { ItemsService } from './items';
5
+ export declare class OperationsService extends ItemsService<OperationRaw> {
6
+ messenger: Messenger;
7
+ constructor(options: AbstractServiceOptions);
8
+ createOne(data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey>;
9
+ createMany(data: Partial<Item>[], opts?: MutationOptions): Promise<PrimaryKey[]>;
10
+ updateOne(key: PrimaryKey, data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey>;
11
+ updateMany(keys: PrimaryKey[], data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey[]>;
12
+ deleteOne(key: PrimaryKey, opts?: MutationOptions): Promise<PrimaryKey>;
13
+ deleteMany(keys: PrimaryKey[], opts?: MutationOptions): Promise<PrimaryKey[]>;
14
+ }
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OperationsService = void 0;
4
+ const messenger_1 = require("../messenger");
5
+ const items_1 = require("./items");
6
+ class OperationsService extends items_1.ItemsService {
7
+ constructor(options) {
8
+ super('directus_operations', options);
9
+ this.messenger = (0, messenger_1.getMessenger)();
10
+ }
11
+ async createOne(data, opts) {
12
+ const result = await super.createOne(data, opts);
13
+ this.messenger.publish('flows', { type: 'reload' });
14
+ return result;
15
+ }
16
+ async createMany(data, opts) {
17
+ const result = await super.createMany(data, opts);
18
+ this.messenger.publish('flows', { type: 'reload' });
19
+ return result;
20
+ }
21
+ async updateOne(key, data, opts) {
22
+ const result = await super.updateOne(key, data, opts);
23
+ this.messenger.publish('flows', { type: 'reload' });
24
+ return result;
25
+ }
26
+ async updateMany(keys, data, opts) {
27
+ const result = await super.updateMany(keys, data, opts);
28
+ this.messenger.publish('flows', { type: 'reload' });
29
+ return result;
30
+ }
31
+ async deleteOne(key, opts) {
32
+ const result = await super.deleteOne(key, opts);
33
+ this.messenger.publish('flows', { type: 'reload' });
34
+ return result;
35
+ }
36
+ async deleteMany(keys, opts) {
37
+ const result = await super.deleteMany(keys, opts);
38
+ this.messenger.publish('flows', { type: 'reload' });
39
+ return result;
40
+ }
41
+ }
42
+ exports.OperationsService = OperationsService;
@@ -1,7 +1,7 @@
1
- import { Knex } from 'knex';
2
- import { AbstractServiceOptions, Item, PrimaryKey } from '../types';
3
1
  import { Accountability, SchemaOverview } from '@directus/shared/types';
2
+ import { Knex } from 'knex';
4
3
  import { Helpers } from '../database/helpers';
4
+ import { AbstractServiceOptions, Item, PrimaryKey } from '../types';
5
5
  declare type Action = 'create' | 'read' | 'update';
6
6
  declare type Transformers = {
7
7
  [type: string]: (context: {
@@ -4,18 +4,19 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.PayloadService = void 0;
7
+ const utils_1 = require("@directus/shared/utils");
7
8
  const date_fns_1 = require("date-fns");
9
+ const flat_1 = require("flat");
8
10
  const joi_1 = __importDefault(require("joi"));
9
11
  const lodash_1 = require("lodash");
10
12
  const uuid_1 = require("uuid");
13
+ const wellknown_1 = require("wellknown");
11
14
  const database_1 = __importDefault(require("../database"));
12
- const exceptions_1 = require("../exceptions");
13
- const utils_1 = require("@directus/shared/utils");
14
- const items_1 = require("./items");
15
- const flat_1 = require("flat");
16
15
  const helpers_1 = require("../database/helpers");
17
- const wellknown_1 = require("wellknown");
16
+ const exceptions_1 = require("../exceptions");
18
17
  const generate_hash_1 = require("../utils/generate-hash");
18
+ const parse_json_1 = require("../utils/parse-json");
19
+ const items_1 = require("./items");
19
20
  /**
20
21
  * Process a given payload for a collection to ensure the special fields (hash, uuid, date etc) are
21
22
  * handled correctly.
@@ -55,7 +56,7 @@ class PayloadService {
55
56
  if (action === 'read') {
56
57
  if (typeof value === 'string') {
57
58
  try {
58
- return JSON.parse(value);
59
+ return (0, parse_json_1.parseJSON)(value);
59
60
  }
60
61
  catch {
61
62
  return value;
@@ -196,7 +197,7 @@ class PayloadService {
196
197
  processGeometries(payloads, action) {
197
198
  const process = action == 'read'
198
199
  ? (value) => (typeof value === 'string' ? (0, wellknown_1.parse)(value) : value)
199
- : (value) => this.helpers.st.fromGeoJSON(typeof value == 'string' ? JSON.parse(value) : value);
200
+ : (value) => this.helpers.st.fromGeoJSON(typeof value == 'string' ? (0, parse_json_1.parseJSON)(value) : value);
200
201
  const fieldsInCollection = Object.entries(this.schema.collections[this.collection].fields);
201
202
  const geometryColumns = fieldsInCollection.filter(([_, field]) => field.type.startsWith('geometry'));
202
203
  for (const [name] of geometryColumns) {
@@ -18,6 +18,10 @@ export declare class UsersService extends ItemsService {
18
18
  */
19
19
  private checkPasswordPolicy;
20
20
  private checkRemainingAdminExistence;
21
+ /**
22
+ * Make sure there's at least one active admin user when updating user status
23
+ */
24
+ private checkRemainingActiveAdmin;
21
25
  /**
22
26
  * Create a new user
23
27
  */
@@ -101,6 +101,23 @@ class UsersService extends items_1.ItemsService {
101
101
  throw new exceptions_2.UnprocessableEntityException(`You can't remove the last admin user from the role.`);
102
102
  }
103
103
  }
104
+ /**
105
+ * Make sure there's at least one active admin user when updating user status
106
+ */
107
+ async checkRemainingActiveAdmin(excludeKeys) {
108
+ const otherAdminUsers = await this.knex
109
+ .count('*', { as: 'count' })
110
+ .from('directus_users')
111
+ .whereNotIn('directus_users.id', excludeKeys)
112
+ .andWhere({ 'directus_roles.admin_access': true })
113
+ .andWhere({ 'directus_users.status': 'active' })
114
+ .leftJoin('directus_roles', 'directus_users.role', 'directus_roles.id')
115
+ .first();
116
+ const otherAdminUsersCount = +((otherAdminUsers === null || otherAdminUsers === void 0 ? void 0 : otherAdminUsers.count) || 0);
117
+ if (otherAdminUsersCount === 0) {
118
+ throw new exceptions_2.UnprocessableEntityException(`You can't change the active status of the last admin user.`);
119
+ }
120
+ }
104
121
  /**
105
122
  * Create a new user
106
123
  */
@@ -149,6 +166,9 @@ class UsersService extends items_1.ItemsService {
149
166
  await this.checkRemainingAdminExistence(keys);
150
167
  }
151
168
  }
169
+ if (data.status !== undefined && data.status !== 'active') {
170
+ await this.checkRemainingActiveAdmin(keys);
171
+ }
152
172
  if (data.email) {
153
173
  if (keys.length > 1) {
154
174
  throw new record_not_unique_1.RecordNotUniqueException('email', {
@@ -1,6 +1,8 @@
1
1
  import { AbstractServiceOptions, Item, PrimaryKey, Webhook, MutationOptions } from '../types';
2
2
  import { ItemsService } from './items';
3
+ import { Messenger } from '../messenger';
3
4
  export declare class WebhooksService extends ItemsService<Webhook> {
5
+ messenger: Messenger;
4
6
  constructor(options: AbstractServiceOptions);
5
7
  createOne(data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey>;
6
8
  createMany(data: Partial<Item>[], opts?: MutationOptions): Promise<PrimaryKey[]>;