directus 9.11.0 → 9.12.1

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 (169) hide show
  1. package/dist/app.js +8 -1
  2. package/dist/cli/utils/create-env/env-stub.liquid +266 -9
  3. package/dist/controllers/activity.js +1 -1
  4. package/dist/controllers/flows.d.ts +2 -0
  5. package/dist/controllers/flows.js +157 -0
  6. package/dist/controllers/folders.js +1 -1
  7. package/dist/controllers/notifications.js +1 -1
  8. package/dist/controllers/operations.d.ts +2 -0
  9. package/dist/controllers/operations.js +138 -0
  10. package/dist/database/index.js +15 -19
  11. package/dist/database/migrations/20220429A-add-flows.d.ts +3 -0
  12. package/dist/database/migrations/20220429A-add-flows.js +83 -0
  13. package/dist/database/migrations/20220429B-add-color-to-insights-icon.d.ts +3 -0
  14. package/dist/database/migrations/20220429B-add-color-to-insights-icon.js +15 -0
  15. package/dist/database/migrations/20220429C-drop-non-null-from-ip-of-activity.d.ts +3 -0
  16. package/dist/database/migrations/20220429C-drop-non-null-from-ip-of-activity.js +15 -0
  17. package/dist/database/migrations/20220429D-drop-non-null-from-sender-of-notifications.d.ts +3 -0
  18. package/dist/database/migrations/20220429D-drop-non-null-from-sender-of-notifications.js +15 -0
  19. package/dist/database/seeds/05-activity.yaml +0 -1
  20. package/dist/database/system-data/collections/collections.yaml +4 -0
  21. package/dist/database/system-data/fields/activity.yaml +3 -0
  22. package/dist/database/system-data/fields/dashboards.yaml +3 -1
  23. package/dist/database/system-data/fields/flows.yaml +21 -0
  24. package/dist/database/system-data/fields/notifications.yaml +3 -1
  25. package/dist/database/system-data/fields/operations.yaml +19 -0
  26. package/dist/database/system-data/fields/panels.yaml +3 -1
  27. package/dist/database/system-data/fields/shares.yaml +3 -1
  28. package/dist/database/system-data/fields/users.yaml +2 -4
  29. package/dist/database/system-data/relations/relations.yaml +20 -0
  30. package/dist/env.d.ts +1 -1
  31. package/dist/env.js +165 -11
  32. package/dist/extensions.d.ts +3 -0
  33. package/dist/extensions.js +73 -20
  34. package/dist/flows.d.ts +17 -0
  35. package/dist/flows.js +310 -0
  36. package/dist/messenger.d.ts +24 -0
  37. package/dist/messenger.js +64 -0
  38. package/dist/operations/condition/index.d.ts +6 -0
  39. package/dist/operations/condition/index.js +15 -0
  40. package/dist/operations/item-create/index.d.ts +8 -0
  41. package/dist/operations/item-create/index.js +40 -0
  42. package/dist/operations/item-delete/index.d.ts +9 -0
  43. package/dist/operations/item-delete/index.js +45 -0
  44. package/dist/operations/item-read/index.d.ts +9 -0
  45. package/dist/operations/item-read/index.js +45 -0
  46. package/dist/operations/item-update/index.d.ts +10 -0
  47. package/dist/operations/item-update/index.js +50 -0
  48. package/dist/operations/log/index.d.ts +5 -0
  49. package/dist/operations/log/index.js +14 -0
  50. package/dist/operations/mail/index.d.ts +7 -0
  51. package/dist/operations/mail/index.js +16 -0
  52. package/dist/operations/notification/index.d.ts +8 -0
  53. package/dist/operations/notification/index.js +39 -0
  54. package/dist/operations/request/index.d.ts +9 -0
  55. package/dist/operations/request/index.js +14 -0
  56. package/dist/operations/sleep/index.d.ts +5 -0
  57. package/dist/operations/sleep/index.js +9 -0
  58. package/dist/operations/transform/index.d.ts +5 -0
  59. package/dist/operations/transform/index.js +10 -0
  60. package/dist/operations/trigger/index.d.ts +6 -0
  61. package/dist/operations/trigger/index.js +21 -0
  62. package/dist/services/activity.d.ts +1 -2
  63. package/dist/services/activity.js +10 -10
  64. package/dist/services/authentication.d.ts +2 -2
  65. package/dist/services/authentication.js +7 -7
  66. package/dist/services/authorization.js +12 -0
  67. package/dist/services/flows.d.ts +14 -0
  68. package/dist/services/flows.js +42 -0
  69. package/dist/services/graphql.js +13 -2
  70. package/dist/services/import-export.js +7 -3
  71. package/dist/services/index.d.ts +2 -0
  72. package/dist/services/index.js +2 -0
  73. package/dist/services/items.js +17 -1
  74. package/dist/services/mail/index.js +2 -1
  75. package/dist/services/notifications.d.ts +2 -1
  76. package/dist/services/notifications.js +4 -3
  77. package/dist/services/operations.d.ts +14 -0
  78. package/dist/services/operations.js +42 -0
  79. package/dist/services/webhooks.d.ts +2 -0
  80. package/dist/services/webhooks.js +8 -7
  81. package/dist/types/events.d.ts +18 -0
  82. package/dist/types/events.js +2 -0
  83. package/dist/types/index.d.ts +1 -1
  84. package/dist/types/index.js +1 -1
  85. package/dist/utils/{apply-query/index.d.ts → apply-query.d.ts} +0 -0
  86. package/dist/utils/{apply-query/index.js → apply-query.js} +147 -48
  87. package/dist/utils/construct-flow-tree.d.ts +2 -0
  88. package/dist/utils/construct-flow-tree.js +31 -0
  89. package/dist/utils/get-accountability-for-role.d.ts +7 -0
  90. package/dist/utils/get-accountability-for-role.js +36 -0
  91. package/dist/utils/operation-options.d.ts +3 -0
  92. package/dist/utils/operation-options.js +45 -0
  93. package/dist/utils/validate-keys.d.ts +6 -0
  94. package/dist/utils/validate-keys.js +28 -0
  95. package/dist/utils/validate-query.js +1 -1
  96. package/dist/webhooks.d.ts +2 -0
  97. package/dist/webhooks.js +17 -2
  98. package/package.json +19 -15
  99. package/dist/types/activity.d.ts +0 -9
  100. package/dist/types/activity.js +0 -13
  101. package/dist/utils/apply-query/operators/between.operator.d.ts +0 -2
  102. package/dist/utils/apply-query/operators/between.operator.js +0 -16
  103. package/dist/utils/apply-query/operators/contains.operator.d.ts +0 -2
  104. package/dist/utils/apply-query/operators/contains.operator.js +0 -9
  105. package/dist/utils/apply-query/operators/ends-with.operator.d.ts +0 -2
  106. package/dist/utils/apply-query/operators/ends-with.operator.js +0 -9
  107. package/dist/utils/apply-query/operators/equals.operator.d.ts +0 -2
  108. package/dist/utils/apply-query/operators/equals.operator.js +0 -9
  109. package/dist/utils/apply-query/operators/greather-than-equals.operator.d.ts +0 -2
  110. package/dist/utils/apply-query/operators/greather-than-equals.operator.js +0 -9
  111. package/dist/utils/apply-query/operators/greather-than.operator.d.ts +0 -2
  112. package/dist/utils/apply-query/operators/greather-than.operator.js +0 -9
  113. package/dist/utils/apply-query/operators/in.operator.d.ts +0 -2
  114. package/dist/utils/apply-query/operators/in.operator.js +0 -14
  115. package/dist/utils/apply-query/operators/index.d.ts +0 -3
  116. package/dist/utils/apply-query/operators/index.js +0 -72
  117. package/dist/utils/apply-query/operators/insensitive-contains.operator.d.ts +0 -2
  118. package/dist/utils/apply-query/operators/insensitive-contains.operator.js +0 -9
  119. package/dist/utils/apply-query/operators/insensitive-ends-with.operator.d.ts +0 -2
  120. package/dist/utils/apply-query/operators/insensitive-ends-with.operator.js +0 -9
  121. package/dist/utils/apply-query/operators/insensitive-equals.operator.d.ts +0 -2
  122. package/dist/utils/apply-query/operators/insensitive-equals.operator.js +0 -9
  123. package/dist/utils/apply-query/operators/insensitive-not-contains.operator.d.ts +0 -2
  124. package/dist/utils/apply-query/operators/insensitive-not-contains.operator.js +0 -9
  125. package/dist/utils/apply-query/operators/insensitive-not-ends-with.operator.d.ts +0 -2
  126. package/dist/utils/apply-query/operators/insensitive-not-ends-with.operator.js +0 -9
  127. package/dist/utils/apply-query/operators/insensitive-not-equals.operator.d.ts +0 -2
  128. package/dist/utils/apply-query/operators/insensitive-not-equals.operator.js +0 -9
  129. package/dist/utils/apply-query/operators/insensitive-not-starts-with.operator.d.ts +0 -2
  130. package/dist/utils/apply-query/operators/insensitive-not-starts-with.operator.js +0 -9
  131. package/dist/utils/apply-query/operators/insensitive-starts-with.operator.d.ts +0 -2
  132. package/dist/utils/apply-query/operators/insensitive-starts-with.operator.js +0 -9
  133. package/dist/utils/apply-query/operators/intersects-bbox.operator.d.ts +0 -2
  134. package/dist/utils/apply-query/operators/intersects-bbox.operator.js +0 -9
  135. package/dist/utils/apply-query/operators/intersects.operator.d.ts +0 -2
  136. package/dist/utils/apply-query/operators/intersects.operator.js +0 -9
  137. package/dist/utils/apply-query/operators/is-empty.operator.d.ts +0 -2
  138. package/dist/utils/apply-query/operators/is-empty.operator.js +0 -14
  139. package/dist/utils/apply-query/operators/is-not-empty.operator.d.ts +0 -2
  140. package/dist/utils/apply-query/operators/is-not-empty.operator.js +0 -14
  141. package/dist/utils/apply-query/operators/is-not-null.operator.d.ts +0 -2
  142. package/dist/utils/apply-query/operators/is-not-null.operator.js +0 -14
  143. package/dist/utils/apply-query/operators/is-null.operator.d.ts +0 -2
  144. package/dist/utils/apply-query/operators/is-null.operator.js +0 -14
  145. package/dist/utils/apply-query/operators/less-than-equals.operator.d.ts +0 -2
  146. package/dist/utils/apply-query/operators/less-than-equals.operator.js +0 -9
  147. package/dist/utils/apply-query/operators/less-than.operator.d.ts +0 -2
  148. package/dist/utils/apply-query/operators/less-than.operator.js +0 -9
  149. package/dist/utils/apply-query/operators/not-between.operator.d.ts +0 -2
  150. package/dist/utils/apply-query/operators/not-between.operator.js +0 -16
  151. package/dist/utils/apply-query/operators/not-contains.operator.d.ts +0 -2
  152. package/dist/utils/apply-query/operators/not-contains.operator.js +0 -9
  153. package/dist/utils/apply-query/operators/not-ends-with.operator.d.ts +0 -2
  154. package/dist/utils/apply-query/operators/not-ends-with.operator.js +0 -9
  155. package/dist/utils/apply-query/operators/not-equals.operator.d.ts +0 -2
  156. package/dist/utils/apply-query/operators/not-equals.operator.js +0 -9
  157. package/dist/utils/apply-query/operators/not-in.operator.d.ts +0 -2
  158. package/dist/utils/apply-query/operators/not-in.operator.js +0 -14
  159. package/dist/utils/apply-query/operators/not-intersects-bbox.operator.d.ts +0 -2
  160. package/dist/utils/apply-query/operators/not-intersects-bbox.operator.js +0 -9
  161. package/dist/utils/apply-query/operators/not-intersects.operator.d.ts +0 -2
  162. package/dist/utils/apply-query/operators/not-intersects.operator.js +0 -9
  163. package/dist/utils/apply-query/operators/not-starts-with.operator.d.ts +0 -2
  164. package/dist/utils/apply-query/operators/not-starts-with.operator.js +0 -9
  165. package/dist/utils/apply-query/operators/operator-register.d.ts +0 -13
  166. package/dist/utils/apply-query/operators/operator-register.js +0 -7
  167. package/dist/utils/apply-query/operators/starts-with.operator.d.ts +0 -2
  168. package/dist/utils/apply-query/operators/starts-with.operator.js +0 -9
  169. package/example.env +0 -202
package/dist/env.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  /**
3
3
  * @NOTE
4
- * See example.env for all possible keys
4
+ * For all possible keys, see: https://docs.directus.io/configuration/config-options/
5
5
  */
6
6
  var __importDefault = (this && this.__importDefault) || function (mod) {
7
7
  return (mod && mod.__esModule) ? mod : { "default": mod };
@@ -15,6 +15,155 @@ const path_1 = __importDefault(require("path"));
15
15
  const require_yaml_1 = require("./utils/require-yaml");
16
16
  const utils_1 = require("@directus/shared/utils");
17
17
  const parse_json_1 = require("./utils/parse-json");
18
+ // keeping this here for now to prevent a circular import to constants.ts
19
+ const allowedEnvironmentVars = [
20
+ // general
21
+ 'CONFIG_PATH',
22
+ 'HOST',
23
+ 'PORT',
24
+ 'PUBLIC_URL',
25
+ 'LOG_LEVEL',
26
+ 'LOG_STYLE',
27
+ 'MAX_PAYLOAD_SIZE',
28
+ 'ROOT_REDIRECT',
29
+ 'SERVE_APP',
30
+ 'GRAPHQL_INTROSPECTION',
31
+ 'LOGGER_.+',
32
+ // server
33
+ 'SERVER_.+',
34
+ // database
35
+ 'DB_.+',
36
+ // security
37
+ 'KEY',
38
+ 'SECRET',
39
+ 'ACCESS_TOKEN_TTL',
40
+ 'REFRESH_TOKEN_TTL',
41
+ 'REFRESH_TOKEN_COOKIE_DOMAIN',
42
+ 'REFRESH_TOKEN_COOKIE_SECURE',
43
+ 'REFRESH_TOKEN_COOKIE_SAME_SITE',
44
+ 'REFRESH_TOKEN_COOKIE_NAME',
45
+ 'PASSWORD_RESET_URL_ALLOW_LIST',
46
+ 'USER_INVITE_URL_ALLOW_LIST',
47
+ 'IP_TRUST_PROXY',
48
+ 'IP_CUSTOM_HEADER',
49
+ 'ASSETS_CONTENT_SECURITY_POLICY',
50
+ 'IMPORT_IP_DENY_LIST',
51
+ 'CONTENT_SECURITY_POLICY_.+',
52
+ 'HSTS_.+',
53
+ // hashing
54
+ 'HASH_.+',
55
+ // cors
56
+ 'CORS_ENABLED',
57
+ 'CORS_ORIGIN',
58
+ 'CORS_METHODS',
59
+ 'CORS_ALLOWED_HEADERS',
60
+ 'CORS_EXPOSED_HEADERS',
61
+ 'CORS_CREDENTIALS',
62
+ 'CORS_MAX_AGE',
63
+ // rate limiting
64
+ 'RATE_LIMITER_.+',
65
+ // cache
66
+ 'CACHE_ENABLED',
67
+ 'CACHE_TTL',
68
+ 'CACHE_CONTROL_S_MAXAGE',
69
+ 'CACHE_AUTO_PURGE',
70
+ 'CACHE_SYSTEM_TTL',
71
+ 'CACHE_SCHEMA',
72
+ 'CACHE_PERMISSIONS',
73
+ 'CACHE_NAMESPACE',
74
+ 'CACHE_STORE',
75
+ 'CACHE_STATUS_HEADER',
76
+ 'CACHE_REDIS',
77
+ 'CACHE_REDIS_HOST',
78
+ 'CACHE_REDIS_PORT',
79
+ 'CACHE_REDIS_PASSWORD',
80
+ 'CACHE_MEMCACHE',
81
+ // storage
82
+ 'STORAGE_LOCATIONS',
83
+ 'STORAGE_.+_DRIVER',
84
+ 'STORAGE_.+_ROOT',
85
+ 'STORAGE_.+_KEY',
86
+ 'STORAGE_.+_SECRET',
87
+ 'STORAGE_.+_BUCKET',
88
+ 'STORAGE_.+_REGION',
89
+ 'STORAGE_.+_ENDPOINT',
90
+ 'STORAGE_.+_ACL',
91
+ 'STORAGE_.+_CONTAINER_NAME',
92
+ 'STORAGE_.+_ACCOUNT_NAME',
93
+ 'STORAGE_.+_ACCOUNT_KEY',
94
+ 'STORAGE_.+_ENDPOINT',
95
+ 'STORAGE_.+_KEY_FILENAME',
96
+ 'STORAGE_.+_BUCKET',
97
+ // metadata
98
+ 'FILE_METADATA_ALLOW_LIST',
99
+ // assets
100
+ 'ASSETS_CACHE_TTL',
101
+ 'ASSETS_TRANSFORM_MAX_CONCURRENT',
102
+ 'ASSETS_TRANSFORM_IMAGE_MAX_DIMENSION',
103
+ 'ASSETS_TRANSFORM_MAX_OPERATIONS',
104
+ 'ASSETS_CONTENT_SECURITY_POLICY',
105
+ // auth
106
+ 'AUTH_PROVIDERS',
107
+ 'AUTH_DISABLE_DEFAULT',
108
+ 'AUTH_.+_DRIVER',
109
+ 'AUTH_.+_CLIENT_ID',
110
+ 'AUTH_.+_CLIENT_SECRET',
111
+ 'AUTH_.+_SCOPE',
112
+ 'AUTH_.+_AUTHORIZE_URL',
113
+ 'AUTH_.+_ACCESS_URL',
114
+ 'AUTH_.+_PROFILE_URL',
115
+ 'AUTH_.+_IDENTIFIER_KEY',
116
+ 'AUTH_.+_EMAIL_KEY',
117
+ 'AUTH_.+_FIRST_NAME_KEY',
118
+ 'AUTH_.+_LAST_NAME_KEY',
119
+ 'AUTH_.+_ALLOW_PUBLIC_REGISTRATION',
120
+ 'AUTH_.+_DEFAULT_ROLE_ID',
121
+ 'AUTH_.+_ICON',
122
+ 'AUTH_.+_PARAMS',
123
+ 'AUTH_.+_ISSUER_URL',
124
+ 'AUTH_.+_AUTH_REQUIRE_VERIFIED_EMAIL',
125
+ 'AUTH_.+_CLIENT_URL',
126
+ 'AUTH_.+_BIND_DN',
127
+ 'AUTH_.+_BIND_PASSWORD',
128
+ 'AUTH_.+_USER_DN',
129
+ 'AUTH_.+_USER_ATTRIBUTE',
130
+ 'AUTH_.+_USER_SCOPE',
131
+ 'AUTH_.+_MAIL_ATTRIBUTE',
132
+ 'AUTH_.+_FIRST_NAME_ATTRIBUTE',
133
+ 'AUTH_.+_LAST_NAME_ATTRIBUTE',
134
+ 'AUTH_.+_GROUP_DN',
135
+ 'AUTH_.+_GROUP_ATTRIBUTE',
136
+ 'AUTH_.+_GROUP_SCOPE',
137
+ // extensions
138
+ 'EXTENSIONS_PATH',
139
+ 'EXTENSIONS_AUTO_RELOAD',
140
+ // emails
141
+ 'EMAIL_FROM',
142
+ 'EMAIL_TRANSPORT',
143
+ 'EMAIL_SENDMAIL_NEW_LINE',
144
+ 'EMAIL_SENDMAIL_PATH',
145
+ 'EMAIL_SMTP_HOST',
146
+ 'EMAIL_SMTP_PORT',
147
+ 'EMAIL_SMTP_USER',
148
+ 'EMAIL_SMTP_PASSWORD',
149
+ 'EMAIL_SMTP_POOL',
150
+ 'EMAIL_SMTP_SECURE',
151
+ 'EMAIL_SMTP_IGNORE_TLS',
152
+ 'EMAIL_MAILGUN_API_KEY',
153
+ 'EMAIL_MAILGUN_DOMAIN',
154
+ 'EMAIL_MAILGUN_HOST',
155
+ 'EMAIL_SES_CREDENTIALS__ACCESS_KEY_ID',
156
+ 'EMAIL_SES_CREDENTIALS__SECRET_ACCESS_KEY',
157
+ 'EMAIL_SES_REGION',
158
+ // admin account
159
+ 'ADMIN_EMAIL',
160
+ 'ADMIN_PASSWORD',
161
+ // telemetry
162
+ 'TELEMETRY',
163
+ // limits & optimization
164
+ 'RELATIONAL_BATCH_SIZE',
165
+ 'EXPORT_BATCH_SIZE',
166
+ ].map((name) => new RegExp(`^${name}$`));
18
167
  const acceptedEnvTypes = ['string', 'number', 'regex', 'array', 'json'];
19
168
  const defaults = {
20
169
  CONFIG_PATH: path_1.default.resolve(process.cwd(), '.env'),
@@ -180,15 +329,17 @@ function processValues(env) {
180
329
  let newKey;
181
330
  if (key.length > 5 && key.endsWith('_FILE')) {
182
331
  newKey = key.slice(0, -5);
183
- if (newKey in env) {
184
- throw new Error(`Duplicate environment variable encountered: you can't use "${newKey}" and "${key}" simultaneously.`);
185
- }
186
- try {
187
- value = fs_1.default.readFileSync(value, { encoding: 'utf8' });
188
- key = newKey;
189
- }
190
- catch {
191
- throw new Error(`Failed to read value from file "${value}", defined in environment variable "${key}".`);
332
+ if (allowedEnvironmentVars.some((pattern) => pattern.test(newKey))) {
333
+ if (newKey in env) {
334
+ throw new Error(`Duplicate environment variable encountered: you can't use "${newKey}" and "${key}" simultaneously.`);
335
+ }
336
+ try {
337
+ value = fs_1.default.readFileSync(value, { encoding: 'utf8' });
338
+ key = newKey;
339
+ }
340
+ catch {
341
+ throw new Error(`Failed to read value from file "${value}", defined in environment variable "${key}".`);
342
+ }
192
343
  }
193
344
  }
194
345
  // Convert values with a type prefix
@@ -213,7 +364,7 @@ function processValues(env) {
213
364
  env[key] = tryJSON(value);
214
365
  break;
215
366
  case 'boolean':
216
- env[key] = value === 'true' || value === true || value === '1' || value === 1;
367
+ env[key] = toBoolean(value);
217
368
  }
218
369
  continue;
219
370
  }
@@ -262,3 +413,6 @@ function tryJSON(value) {
262
413
  return value;
263
414
  }
264
415
  }
416
+ function toBoolean(value) {
417
+ return value === 'true' || value === true || value === '1' || value === 1;
418
+ }
@@ -29,9 +29,12 @@ declare class ExtensionManager {
29
29
  private getSharedDepsMapping;
30
30
  private registerHooks;
31
31
  private registerEndpoints;
32
+ private registerOperations;
32
33
  private registerHook;
33
34
  private registerEndpoint;
35
+ private registerOperation;
34
36
  private unregisterHooks;
35
37
  private unregisterEndpoints;
38
+ private unregisterOperations;
36
39
  }
37
40
  export {};
@@ -45,6 +45,8 @@ const get_module_default_1 = __importDefault(require("./utils/get-module-default
45
45
  const lodash_1 = require("lodash");
46
46
  const chokidar_1 = __importDefault(require("chokidar"));
47
47
  const utils_1 = require("@directus/shared/utils");
48
+ const flows_1 = require("./flows");
49
+ const globby_1 = __importDefault(require("globby"));
48
50
  let extensionManager;
49
51
  function getExtensionManager() {
50
52
  if (extensionManager) {
@@ -63,7 +65,7 @@ class ExtensionManager {
63
65
  this.isLoaded = false;
64
66
  this.extensions = [];
65
67
  this.appExtensions = {};
66
- this.apiExtensions = { hooks: [], endpoints: [] };
68
+ this.apiExtensions = { hooks: [], endpoints: [], operations: [] };
67
69
  this.watcher = null;
68
70
  this.options = defaultOptions;
69
71
  this.apiEmitter = new emitter_1.Emitter();
@@ -131,6 +133,7 @@ class ExtensionManager {
131
133
  }
132
134
  this.registerHooks();
133
135
  this.registerEndpoints();
136
+ await this.registerOperations();
134
137
  if (env_1.default.SERVE_APP) {
135
138
  this.appExtensions = await this.generateExtensionBundles();
136
139
  }
@@ -139,6 +142,7 @@ class ExtensionManager {
139
142
  async unload() {
140
143
  this.unregisterHooks();
141
144
  this.unregisterEndpoints();
145
+ this.unregisterOperations();
142
146
  this.apiEmitter.offAll();
143
147
  if (env_1.default.SERVE_APP) {
144
148
  this.appExtensions = {};
@@ -148,8 +152,13 @@ class ExtensionManager {
148
152
  initializeWatcher() {
149
153
  if (this.options.watch && !this.watcher) {
150
154
  logger_1.default.info('Watching extensions for changes...');
151
- const localExtensionPaths = (env_1.default.SERVE_APP ? constants_1.EXTENSION_TYPES : constants_1.API_EXTENSION_TYPES).map((type) => path_1.default.posix.join(path_1.default.relative('.', env_1.default.EXTENSIONS_PATH).split(path_1.default.sep).join(path_1.default.posix.sep), (0, utils_1.pluralize)(type), '*', 'index.js'));
152
- this.watcher = chokidar_1.default.watch([path_1.default.resolve('.', 'package.json'), ...localExtensionPaths], {
155
+ const localExtensionPaths = (env_1.default.SERVE_APP ? constants_1.EXTENSION_TYPES : constants_1.API_EXTENSION_TYPES).flatMap((type) => {
156
+ const typeDir = path_1.default.posix.join(path_1.default.relative('.', env_1.default.EXTENSIONS_PATH).split(path_1.default.sep).join(path_1.default.posix.sep), (0, utils_1.pluralize)(type));
157
+ return (0, utils_1.isHybridExtension)(type)
158
+ ? [path_1.default.posix.join(typeDir, '*', 'app.js'), path_1.default.posix.join(typeDir, '*', 'api.js')]
159
+ : path_1.default.posix.join(typeDir, '*', 'index.js');
160
+ });
161
+ this.watcher = chokidar_1.default.watch([path_1.default.resolve('package.json'), ...localExtensionPaths], {
153
162
  ignoreInitial: true,
154
163
  });
155
164
  this.watcher
@@ -162,9 +171,14 @@ class ExtensionManager {
162
171
  if (this.watcher) {
163
172
  const toPackageExtensionPaths = (extensions) => extensions
164
173
  .filter((extension) => !extension.local)
165
- .map((extension) => extension.type !== 'pack'
166
- ? path_1.default.resolve(extension.path, extension.entrypoint || '')
167
- : path_1.default.resolve(extension.path, 'package.json'));
174
+ .flatMap((extension) => extension.type === constants_1.PACK_EXTENSION_TYPE
175
+ ? path_1.default.resolve(extension.path, 'package.json')
176
+ : (0, utils_1.isExtensionObject)(extension, constants_1.HYBRID_EXTENSION_TYPES)
177
+ ? [
178
+ path_1.default.resolve(extension.path, extension.entrypoint.app),
179
+ path_1.default.resolve(extension.path, extension.entrypoint.api),
180
+ ]
181
+ : path_1.default.resolve(extension.path, extension.entrypoint));
168
182
  const addedPackageExtensionPaths = toPackageExtensionPaths(added);
169
183
  const removedPackageExtensionPaths = toPackageExtensionPaths(removed);
170
184
  this.watcher.add(addedPackageExtensionPaths);
@@ -223,7 +237,10 @@ class ExtensionManager {
223
237
  const hooks = this.extensions.filter((extension) => extension.type === 'hook');
224
238
  for (const hook of hooks) {
225
239
  try {
226
- this.registerHook(hook);
240
+ const hookPath = path_1.default.resolve(hook.path, hook.entrypoint);
241
+ const hookInstance = require(hookPath);
242
+ const config = (0, get_module_default_1.default)(hookInstance);
243
+ this.registerHook(config, hookPath);
227
244
  }
228
245
  catch (error) {
229
246
  logger_1.default.warn(`Couldn't register hook "${hook.name}"`);
@@ -235,7 +252,10 @@ class ExtensionManager {
235
252
  const endpoints = this.extensions.filter((extension) => extension.type === 'endpoint');
236
253
  for (const endpoint of endpoints) {
237
254
  try {
238
- this.registerEndpoint(endpoint, this.endpointRouter);
255
+ const endpointPath = path_1.default.resolve(endpoint.path, endpoint.entrypoint);
256
+ const endpointInstance = require(endpointPath);
257
+ const config = (0, get_module_default_1.default)(endpointInstance);
258
+ this.registerEndpoint(config, endpointPath, endpoint.name, this.endpointRouter);
239
259
  }
240
260
  catch (error) {
241
261
  logger_1.default.warn(`Couldn't register endpoint "${endpoint.name}"`);
@@ -243,12 +263,33 @@ class ExtensionManager {
243
263
  }
244
264
  }
245
265
  }
246
- registerHook(hook) {
247
- const hookPath = path_1.default.resolve(hook.path, hook.entrypoint || '');
248
- const hookInstance = require(hookPath);
249
- const register = (0, get_module_default_1.default)(hookInstance);
266
+ async registerOperations() {
267
+ const internalPaths = await (0, globby_1.default)(path_1.default.posix.join(path_1.default.relative('.', __dirname).split(path_1.default.sep).join(path_1.default.posix.sep), 'operations/*/index.(js|ts)'));
268
+ const internalOperations = internalPaths.map((internalPath) => {
269
+ const dirs = internalPath.split(path_1.default.sep);
270
+ return {
271
+ name: dirs[dirs.length - 2],
272
+ path: dirs.slice(0, -1).join(path_1.default.sep),
273
+ entrypoint: { api: dirs[dirs.length - 1] },
274
+ };
275
+ });
276
+ const operations = this.extensions.filter((extension) => extension.type === 'operation');
277
+ for (const operation of [...internalOperations, ...operations]) {
278
+ try {
279
+ const operationPath = path_1.default.resolve(operation.path, operation.entrypoint.api);
280
+ const operationInstance = require(operationPath);
281
+ const config = (0, get_module_default_1.default)(operationInstance);
282
+ this.registerOperation(config, operationPath);
283
+ }
284
+ catch (error) {
285
+ logger_1.default.warn(`Couldn't register operation "${operation.name}"`);
286
+ logger_1.default.warn(error);
287
+ }
288
+ }
289
+ }
290
+ registerHook(register, path) {
250
291
  const hookHandler = {
251
- path: hookPath,
292
+ path,
252
293
  events: [],
253
294
  };
254
295
  const registerFunctions = {
@@ -309,12 +350,9 @@ class ExtensionManager {
309
350
  });
310
351
  this.apiExtensions.hooks.push(hookHandler);
311
352
  }
312
- registerEndpoint(endpoint, router) {
313
- const endpointPath = path_1.default.resolve(endpoint.path, endpoint.entrypoint || '');
314
- const endpointInstance = require(endpointPath);
315
- const mod = (0, get_module_default_1.default)(endpointInstance);
316
- const register = typeof mod === 'function' ? mod : mod.handler;
317
- const routeName = typeof mod === 'function' ? endpoint.name : mod.id;
353
+ registerEndpoint(config, path, name, router) {
354
+ const register = typeof config === 'function' ? config : config.handler;
355
+ const routeName = typeof config === 'function' ? name : config.id;
318
356
  const scopedRouter = express_1.default.Router();
319
357
  router.use(`/${routeName}`, scopedRouter);
320
358
  register(scopedRouter, {
@@ -327,7 +365,14 @@ class ExtensionManager {
327
365
  getSchema: get_schema_1.getSchema,
328
366
  });
329
367
  this.apiExtensions.endpoints.push({
330
- path: endpointPath,
368
+ path,
369
+ });
370
+ }
371
+ registerOperation(config, path) {
372
+ const flowManager = (0, flows_1.getFlowManager)();
373
+ flowManager.addOperation(config.id, config.handler);
374
+ this.apiExtensions.operations.push({
375
+ path,
331
376
  });
332
377
  }
333
378
  unregisterHooks() {
@@ -359,4 +404,12 @@ class ExtensionManager {
359
404
  this.endpointRouter.stack = [];
360
405
  this.apiExtensions.endpoints = [];
361
406
  }
407
+ unregisterOperations() {
408
+ for (const operation of this.apiExtensions.operations) {
409
+ delete require.cache[require.resolve(operation.path)];
410
+ }
411
+ const flowManager = (0, flows_1.getFlowManager)();
412
+ flowManager.clearOperations();
413
+ this.apiExtensions.operations = [];
414
+ }
362
415
  }
@@ -0,0 +1,17 @@
1
+ import { OperationHandler } from '@directus/shared/types';
2
+ export declare function getFlowManager(): FlowManager;
3
+ declare class FlowManager {
4
+ private operations;
5
+ private triggerHandlers;
6
+ private operationFlowHandlers;
7
+ private webhookFlowHandlers;
8
+ initialize(): Promise<void>;
9
+ reload(): Promise<void>;
10
+ addOperation(id: string, operation: OperationHandler): void;
11
+ clearOperations(): void;
12
+ runOperationFlow(id: string, data: unknown, context: Record<string, unknown>): Promise<unknown>;
13
+ runWebhookFlow(id: string, data: unknown, context: Record<string, unknown>): Promise<unknown>;
14
+ private executeFlow;
15
+ private executeOperation;
16
+ }
17
+ export {};