@directus/api 14.1.2 → 16.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 (207) hide show
  1. package/dist/app.js +8 -6
  2. package/dist/auth/drivers/ldap.js +7 -4
  3. package/dist/auth/drivers/local.js +3 -2
  4. package/dist/auth/drivers/oauth2.js +11 -5
  5. package/dist/auth/drivers/openid.js +11 -5
  6. package/dist/auth/drivers/saml.js +6 -4
  7. package/dist/auth.js +7 -4
  8. package/dist/bus/index.d.ts +1 -0
  9. package/dist/bus/index.js +1 -0
  10. package/dist/bus/lib/use-bus.d.ts +9 -0
  11. package/dist/bus/lib/use-bus.js +21 -0
  12. package/dist/cache.js +9 -9
  13. package/dist/cli/commands/bootstrap/index.js +6 -2
  14. package/dist/cli/commands/count/index.js +2 -1
  15. package/dist/cli/commands/database/install.js +2 -1
  16. package/dist/cli/commands/database/migrate.js +2 -1
  17. package/dist/cli/commands/roles/create.js +2 -1
  18. package/dist/cli/commands/schema/apply.js +46 -34
  19. package/dist/cli/commands/schema/snapshot.js +6 -5
  20. package/dist/cli/commands/users/create.js +4 -3
  21. package/dist/cli/commands/users/passwd.js +5 -4
  22. package/dist/cli/index.js +2 -2
  23. package/dist/cli/load-extensions.js +4 -2
  24. package/dist/cli/utils/create-env/env-stub.liquid +1 -1
  25. package/dist/constants.d.ts +1 -1
  26. package/dist/constants.js +4 -1
  27. package/dist/controllers/assets.js +5 -3
  28. package/dist/controllers/auth.js +5 -4
  29. package/dist/controllers/extensions.js +18 -6
  30. package/dist/controllers/files.js +3 -3
  31. package/dist/controllers/schema.js +3 -2
  32. package/dist/controllers/shares.js +3 -3
  33. package/dist/database/helpers/index.d.ts +1 -1
  34. package/dist/database/index.d.ts +2 -1
  35. package/dist/database/index.js +11 -3
  36. package/dist/database/migrations/20210518A-add-foreign-key-constraints.js +3 -1
  37. package/dist/database/migrations/20210519A-add-system-fk-triggers.js +3 -1
  38. package/dist/database/migrations/20210802A-replace-groups.js +2 -1
  39. package/dist/database/migrations/20230721A-require-shares-fields.js +2 -1
  40. package/dist/database/migrations/20231215A-add-focalpoints.d.ts +3 -0
  41. package/dist/database/migrations/20231215A-add-focalpoints.js +12 -0
  42. package/dist/database/migrations/run.js +2 -1
  43. package/dist/database/run-ast.js +5 -2
  44. package/dist/database/system-data/app-access-permissions/app-access-permissions.yaml +0 -7
  45. package/dist/database/system-data/fields/files.yaml +16 -0
  46. package/dist/database/system-data/relations/relations.yaml +4 -0
  47. package/dist/emitter.d.ts +1 -0
  48. package/dist/emitter.js +4 -1
  49. package/dist/extensions/lib/get-extensions-path.d.ts +1 -1
  50. package/dist/extensions/lib/get-extensions-path.js +2 -1
  51. package/dist/extensions/lib/get-extensions.d.ts +1 -1
  52. package/dist/extensions/lib/get-extensions.js +32 -8
  53. package/dist/extensions/lib/get-shared-deps-mapping.js +7 -5
  54. package/dist/extensions/lib/sandbox/register/call-reference.js +4 -2
  55. package/dist/extensions/lib/sandbox/register/route.d.ts +1 -0
  56. package/dist/extensions/lib/sandbox/sdk/generators/log.js +2 -1
  57. package/dist/extensions/lib/sync-extensions.js +6 -4
  58. package/dist/extensions/manager.d.ts +5 -0
  59. package/dist/extensions/manager.js +84 -34
  60. package/dist/flows.js +13 -7
  61. package/dist/logger.d.ts +7 -6
  62. package/dist/logger.js +116 -91
  63. package/dist/mailer.js +4 -2
  64. package/dist/middleware/cache.js +4 -2
  65. package/dist/middleware/check-ip.js +25 -6
  66. package/dist/middleware/cors.js +2 -1
  67. package/dist/middleware/error-handler.js +5 -5
  68. package/dist/middleware/rate-limiter-global.js +4 -2
  69. package/dist/middleware/rate-limiter-ip.js +16 -12
  70. package/dist/middleware/respond.js +4 -2
  71. package/dist/operations/log/index.js +2 -1
  72. package/dist/rate-limiter.d.ts +2 -1
  73. package/dist/rate-limiter.js +5 -2
  74. package/dist/redis/index.d.ts +3 -0
  75. package/dist/redis/index.js +3 -0
  76. package/dist/redis/lib/create-redis.d.ts +7 -0
  77. package/dist/redis/lib/create-redis.js +12 -0
  78. package/dist/redis/lib/use-redis.d.ts +16 -0
  79. package/dist/redis/lib/use-redis.js +22 -0
  80. package/dist/redis/utils/redis-config-available.d.ts +4 -0
  81. package/dist/redis/utils/redis-config-available.js +8 -0
  82. package/dist/request/request-interceptor.js +7 -5
  83. package/dist/request/response-interceptor.js +2 -2
  84. package/dist/request/validate-ip.d.ts +1 -1
  85. package/dist/request/validate-ip.js +23 -7
  86. package/dist/server.d.ts +2 -0
  87. package/dist/server.js +11 -7
  88. package/dist/services/activity.js +5 -4
  89. package/dist/services/assets.d.ts +2 -0
  90. package/dist/services/assets.js +9 -4
  91. package/dist/services/authentication.js +17 -9
  92. package/dist/services/collections.js +5 -4
  93. package/dist/services/extensions.d.ts +15 -9
  94. package/dist/services/extensions.js +75 -40
  95. package/dist/services/fields.js +9 -4
  96. package/dist/services/files.d.ts +2 -2
  97. package/dist/services/files.js +22 -14
  98. package/dist/services/graphql/index.js +96 -18
  99. package/dist/services/graphql/subscription.js +2 -2
  100. package/dist/services/graphql/types/bigint.js +16 -5
  101. package/dist/services/graphql/utils/process-error.d.ts +4 -1
  102. package/dist/services/graphql/utils/process-error.js +10 -8
  103. package/dist/services/import-export/index.js +5 -3
  104. package/dist/services/items.js +12 -8
  105. package/dist/services/mail/index.js +4 -2
  106. package/dist/services/notifications.js +7 -3
  107. package/dist/services/payload.js +3 -3
  108. package/dist/services/relations.js +19 -10
  109. package/dist/services/server.js +7 -7
  110. package/dist/services/shares.js +3 -2
  111. package/dist/services/specifications.js +5 -4
  112. package/dist/services/users.js +24 -13
  113. package/dist/services/versions.js +6 -5
  114. package/dist/services/webhooks.d.ts +2 -2
  115. package/dist/services/webhooks.js +2 -2
  116. package/dist/services/websocket.d.ts +1 -1
  117. package/dist/services/websocket.js +4 -3
  118. package/dist/storage/register-drivers.js +2 -1
  119. package/dist/storage/register-locations.js +2 -1
  120. package/dist/synchronization.js +3 -1
  121. package/dist/telemetry/index.d.ts +4 -0
  122. package/dist/telemetry/index.js +4 -0
  123. package/dist/telemetry/lib/get-report.d.ts +5 -0
  124. package/dist/telemetry/lib/get-report.js +42 -0
  125. package/dist/telemetry/lib/init-telemetry.d.ts +11 -0
  126. package/dist/telemetry/lib/init-telemetry.js +30 -0
  127. package/dist/telemetry/lib/send-report.d.ts +5 -0
  128. package/dist/telemetry/lib/send-report.js +23 -0
  129. package/dist/telemetry/lib/track.d.ts +10 -0
  130. package/dist/telemetry/lib/track.js +30 -0
  131. package/dist/telemetry/types/report.d.ts +58 -0
  132. package/dist/telemetry/types/report.js +1 -0
  133. package/dist/telemetry/utils/get-item-count.d.ts +26 -0
  134. package/dist/telemetry/utils/get-item-count.js +36 -0
  135. package/dist/telemetry/utils/get-random-wait-time.d.ts +5 -0
  136. package/dist/telemetry/utils/get-random-wait-time.js +5 -0
  137. package/dist/telemetry/utils/get-user-count.d.ts +7 -0
  138. package/dist/telemetry/utils/get-user-count.js +30 -0
  139. package/dist/telemetry/utils/get-user-item-count.d.ts +13 -0
  140. package/dist/telemetry/utils/get-user-item-count.js +18 -0
  141. package/dist/types/assets.d.ts +2 -0
  142. package/dist/utils/apply-diff.js +2 -1
  143. package/dist/utils/apply-query.js +2 -2
  144. package/dist/utils/delete-from-require-cache.js +2 -1
  145. package/dist/utils/get-accountability-for-token.js +3 -2
  146. package/dist/utils/get-auth-providers.js +2 -1
  147. package/dist/utils/get-cache-headers.js +5 -2
  148. package/dist/utils/get-cache-key.js +1 -1
  149. package/dist/utils/get-config-from-env.js +2 -1
  150. package/dist/utils/get-default-value.js +4 -3
  151. package/dist/utils/get-ip-from-req.d.ts +1 -1
  152. package/dist/utils/get-ip-from-req.js +5 -3
  153. package/dist/utils/get-permissions.js +5 -3
  154. package/dist/utils/get-schema.js +5 -2
  155. package/dist/utils/get-snapshot-diff.js +7 -9
  156. package/dist/utils/get-snapshot.js +5 -5
  157. package/dist/utils/get-versioned-hash.js +1 -1
  158. package/dist/utils/ip-in-networks.d.ts +6 -0
  159. package/dist/utils/ip-in-networks.js +13 -0
  160. package/dist/utils/is-url-allowed.js +2 -1
  161. package/dist/utils/job-queue.d.ts +1 -0
  162. package/dist/utils/job-queue.js +3 -0
  163. package/dist/utils/md.d.ts +1 -1
  164. package/dist/utils/md.js +3 -2
  165. package/dist/utils/sanitize-query.js +7 -2
  166. package/dist/utils/sanitize-schema.d.ts +1 -1
  167. package/dist/utils/should-clear-cache.js +2 -1
  168. package/dist/utils/should-skip-cache.js +2 -1
  169. package/dist/utils/transformations.js +95 -12
  170. package/dist/utils/validate-env.js +4 -2
  171. package/dist/utils/validate-query.js +8 -3
  172. package/dist/utils/validate-snapshot.js +3 -3
  173. package/dist/utils/validate-storage.js +4 -2
  174. package/dist/webhooks.js +4 -3
  175. package/dist/websocket/controllers/base.d.ts +2 -0
  176. package/dist/websocket/controllers/base.js +12 -6
  177. package/dist/websocket/controllers/graphql.d.ts +2 -0
  178. package/dist/websocket/controllers/graphql.js +5 -3
  179. package/dist/websocket/controllers/hooks.js +3 -2
  180. package/dist/websocket/controllers/index.d.ts +2 -0
  181. package/dist/websocket/controllers/index.js +4 -2
  182. package/dist/websocket/controllers/rest.d.ts +2 -0
  183. package/dist/websocket/controllers/rest.js +4 -2
  184. package/dist/websocket/errors.js +2 -1
  185. package/dist/websocket/handlers/heartbeat.js +4 -3
  186. package/dist/websocket/handlers/subscribe.d.ts +2 -2
  187. package/dist/websocket/handlers/subscribe.js +5 -4
  188. package/dist/websocket/types.d.ts +3 -1
  189. package/package.json +114 -115
  190. package/dist/__utils__/items-utils.d.ts +0 -2
  191. package/dist/__utils__/items-utils.js +0 -31
  192. package/dist/__utils__/mock-env.d.ts +0 -18
  193. package/dist/__utils__/mock-env.js +0 -41
  194. package/dist/__utils__/schemas.d.ts +0 -13
  195. package/dist/__utils__/schemas.js +0 -301
  196. package/dist/__utils__/snapshots.d.ts +0 -5
  197. package/dist/__utils__/snapshots.js +0 -903
  198. package/dist/env.d.ts +0 -13
  199. package/dist/env.js +0 -505
  200. package/dist/messenger.d.ts +0 -24
  201. package/dist/messenger.js +0 -64
  202. package/dist/utils/package.d.ts +0 -2
  203. package/dist/utils/package.js +0 -6
  204. package/dist/utils/telemetry.d.ts +0 -1
  205. package/dist/utils/telemetry.js +0 -23
  206. package/dist/utils/to-boolean.d.ts +0 -4
  207. package/dist/utils/to-boolean.js +0 -6
@@ -5,13 +5,14 @@ import inquirer from 'inquirer';
5
5
  import { load as loadYaml } from 'js-yaml';
6
6
  import path from 'path';
7
7
  import getDatabase, { isInstalled, validateDatabaseConnection } from '../../../database/index.js';
8
- import logger from '../../../logger.js';
8
+ import { useLogger } from '../../../logger.js';
9
9
  import { DiffKind } from '../../../types/index.js';
10
10
  import { isNestedMetaUpdate } from '../../../utils/apply-diff.js';
11
11
  import { applySnapshot } from '../../../utils/apply-snapshot.js';
12
12
  import { getSnapshotDiff } from '../../../utils/get-snapshot-diff.js';
13
13
  import { getSnapshot } from '../../../utils/get-snapshot.js';
14
14
  export async function apply(snapshotPath, options) {
15
+ const logger = useLogger();
15
16
  const filename = path.resolve(process.cwd(), snapshotPath);
16
17
  const database = getDatabase();
17
18
  await validateDatabaseConnection(database);
@@ -41,92 +42,90 @@ export async function apply(snapshotPath, options) {
41
42
  const dryRun = options?.dryRun === true;
42
43
  const promptForChanges = !dryRun && options?.yes !== true;
43
44
  if (dryRun || promptForChanges) {
44
- let message = '';
45
+ const sections = [];
45
46
  if (snapshotDiff.collections.length > 0) {
46
- message += chalk.black.underline.bold('Collections:');
47
+ const lines = [chalk.underline.bold('Collections:')];
47
48
  for (const { collection, diff } of snapshotDiff.collections) {
48
49
  if (diff[0]?.kind === DiffKind.EDIT) {
49
- message += `\n - ${chalk.blue('Update')} ${collection}`;
50
+ lines.push(` - ${chalk.magenta('Update')} ${collection}`);
50
51
  for (const change of diff) {
51
52
  if (change.kind === DiffKind.EDIT) {
52
- const path = change.path.slice(1).join('.');
53
- message += `\n - Set ${path} to ${change.rhs}`;
53
+ const path = formatPath(change.path);
54
+ lines.push(` - Set ${path} to ${change.rhs}`);
54
55
  }
55
56
  }
56
57
  }
57
58
  else if (diff[0]?.kind === DiffKind.DELETE) {
58
- message += `\n - ${chalk.red('Delete')} ${collection}`;
59
+ lines.push(` - ${chalk.red('Delete')} ${collection}`);
59
60
  }
60
61
  else if (diff[0]?.kind === DiffKind.NEW) {
61
- message += `\n - ${chalk.green('Create')} ${collection}`;
62
+ lines.push(` - ${chalk.green('Create')} ${collection}`);
62
63
  }
63
64
  else if (diff[0]?.kind === DiffKind.ARRAY) {
64
- message += `\n - ${chalk.blue('Update')} ${collection}`;
65
+ lines.push(` - ${chalk.magenta('Update')} ${collection}`);
65
66
  }
66
67
  }
68
+ sections.push(lines.join('\n'));
67
69
  }
68
70
  if (snapshotDiff.fields.length > 0) {
69
- message += '\n\n' + chalk.black.underline.bold('Fields:');
71
+ const lines = [chalk.underline.bold('Fields:')];
70
72
  for (const { collection, field, diff } of snapshotDiff.fields) {
71
73
  if (diff[0]?.kind === DiffKind.EDIT || isNestedMetaUpdate(diff[0])) {
72
- message += `\n - ${chalk.blue('Update')} ${collection}.${field}`;
74
+ lines.push(` - ${chalk.magenta('Update')} ${collection}.${field}`);
73
75
  for (const change of diff) {
74
- const path = change.path.slice(1).join('.');
76
+ const path = formatPath(change.path);
75
77
  if (change.kind === DiffKind.EDIT) {
76
- message += `\n - Set ${path} to ${change.rhs}`;
78
+ lines.push(` - Set ${path} to ${change.rhs}`);
77
79
  }
78
80
  else if (change.kind === DiffKind.DELETE) {
79
- message += `\n - Remove ${path}`;
81
+ lines.push(` - Remove ${path}`);
80
82
  }
81
83
  else if (change.kind === DiffKind.NEW) {
82
- message += `\n - Add ${path} and set it to ${change.rhs}`;
84
+ lines.push(` - Add ${path} and set it to ${change.rhs}`);
83
85
  }
84
86
  }
85
87
  }
86
88
  else if (diff[0]?.kind === DiffKind.DELETE) {
87
- message += `\n - ${chalk.red('Delete')} ${collection}.${field}`;
89
+ lines.push(` - ${chalk.red('Delete')} ${collection}.${field}`);
88
90
  }
89
91
  else if (diff[0]?.kind === DiffKind.NEW) {
90
- message += `\n - ${chalk.green('Create')} ${collection}.${field}`;
92
+ lines.push(` - ${chalk.green('Create')} ${collection}.${field}`);
91
93
  }
92
94
  else if (diff[0]?.kind === DiffKind.ARRAY) {
93
- message += `\n - ${chalk.blue('Update')} ${collection}.${field}`;
95
+ lines.push(` - ${chalk.magenta('Update')} ${collection}.${field}`);
94
96
  }
95
97
  }
98
+ sections.push(lines.join('\n'));
96
99
  }
97
100
  if (snapshotDiff.relations.length > 0) {
98
- message += '\n\n' + chalk.black.underline.bold('Relations:');
101
+ const lines = [chalk.underline.bold('Relations:')];
99
102
  for (const { collection, field, related_collection, diff } of snapshotDiff.relations) {
103
+ const relatedCollection = formatRelatedCollection(related_collection);
100
104
  if (diff[0]?.kind === DiffKind.EDIT) {
101
- message += `\n - ${chalk.blue('Update')} ${collection}.${field}`;
105
+ lines.push(` - ${chalk.magenta('Update')} ${collection}.${field}${relatedCollection}`);
102
106
  for (const change of diff) {
103
107
  if (change.kind === DiffKind.EDIT) {
104
- const path = change.path.slice(1).join('.');
105
- message += `\n - Set ${path} to ${change.rhs}`;
108
+ const path = formatPath(change.path);
109
+ lines.push(` - Set ${path} to ${change.rhs}`);
106
110
  }
107
111
  }
108
112
  }
109
113
  else if (diff[0]?.kind === DiffKind.DELETE) {
110
- message += `\n - ${chalk.red('Delete')} ${collection}.${field}`;
114
+ lines.push(` - ${chalk.red('Delete')} ${collection}.${field}${relatedCollection}`);
111
115
  }
112
116
  else if (diff[0]?.kind === DiffKind.NEW) {
113
- message += `\n - ${chalk.green('Create')} ${collection}.${field}`;
117
+ lines.push(` - ${chalk.green('Create')} ${collection}.${field}${relatedCollection}`);
114
118
  }
115
119
  else if (diff[0]?.kind === DiffKind.ARRAY) {
116
- message += `\n - ${chalk.blue('Update')} ${collection}.${field}`;
117
- }
118
- else {
119
- continue;
120
- }
121
- // Related collection doesn't exist for a2o relationship types
122
- if (related_collection) {
123
- message += `-> ${related_collection}`;
120
+ lines.push(` - ${chalk.magenta('Update')} ${collection}.${field}${relatedCollection}`);
124
121
  }
125
122
  }
123
+ sections.push(lines.join('\n'));
126
124
  }
127
- message = 'The following changes will be applied:\n\n' + chalk.black(message);
125
+ const message = 'The following changes will be applied:\n\n' + sections.join('\n\n');
128
126
  if (dryRun) {
129
- logger.info(message);
127
+ // eslint-disable-next-line no-console
128
+ console.log(message);
130
129
  process.exit(0);
131
130
  }
132
131
  const { proceed } = await inquirer.prompt([
@@ -151,3 +150,16 @@ export async function apply(snapshotPath, options) {
151
150
  process.exit(1);
152
151
  }
153
152
  }
153
+ function formatPath(path) {
154
+ if (path.length === 1) {
155
+ return path.toString();
156
+ }
157
+ return path.slice(1).join('.');
158
+ }
159
+ function formatRelatedCollection(relatedCollection) {
160
+ // Related collection doesn't exist for a2o relationship types
161
+ if (relatedCollection) {
162
+ return ` → ${relatedCollection}`;
163
+ }
164
+ return '';
165
+ }
@@ -1,12 +1,13 @@
1
- import getDatabase from '../../../database/index.js';
2
- import logger from '../../../logger.js';
3
- import { getSnapshot } from '../../../utils/get-snapshot.js';
4
- import { constants as fsConstants, promises as fs } from 'fs';
5
- import path from 'path';
1
+ import { promises as fs, constants as fsConstants } from 'fs';
6
2
  import inquirer from 'inquirer';
7
3
  import { dump as toYaml } from 'js-yaml';
4
+ import path from 'path';
5
+ import getDatabase from '../../../database/index.js';
6
+ import { useLogger } from '../../../logger.js';
7
+ import { getSnapshot } from '../../../utils/get-snapshot.js';
8
8
  export async function snapshot(snapshotPath, options) {
9
9
  const database = getDatabase();
10
+ const logger = useLogger();
10
11
  try {
11
12
  const snapshot = await getSnapshot({ database });
12
13
  let snapshotString;
@@ -1,9 +1,10 @@
1
- import { getSchema } from '../../../utils/get-schema.js';
2
- import { UsersService } from '../../../services/users.js';
3
1
  import getDatabase from '../../../database/index.js';
4
- import logger from '../../../logger.js';
2
+ import { useLogger } from '../../../logger.js';
3
+ import { UsersService } from '../../../services/users.js';
4
+ import { getSchema } from '../../../utils/get-schema.js';
5
5
  export default async function usersCreate({ email, password, role, }) {
6
6
  const database = getDatabase();
7
+ const logger = useLogger();
7
8
  if (!email || !password || !role) {
8
9
  logger.error('Email, password, role are required');
9
10
  process.exit(1);
@@ -1,10 +1,11 @@
1
- import { getSchema } from '../../../utils/get-schema.js';
2
- import { generateHash } from '../../../utils/generate-hash.js';
3
- import { UsersService } from '../../../services/users.js';
4
1
  import getDatabase from '../../../database/index.js';
5
- import logger from '../../../logger.js';
2
+ import { useLogger } from '../../../logger.js';
3
+ import { UsersService } from '../../../services/users.js';
4
+ import { generateHash } from '../../../utils/generate-hash.js';
5
+ import { getSchema } from '../../../utils/get-schema.js';
6
6
  export default async function usersPasswd({ email, password }) {
7
7
  const database = getDatabase();
8
+ const logger = useLogger();
8
9
  if (!email || !password) {
9
10
  logger.error('Email and password are required');
10
11
  process.exit(1);
package/dist/cli/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { Command, Option } from 'commander';
2
+ import { version } from 'directus/version';
2
3
  import emitter from '../emitter.js';
3
4
  import { startServer } from '../server.js';
4
- import * as pkg from '../utils/package.js';
5
5
  import bootstrap from './commands/bootstrap/index.js';
6
6
  import count from './commands/count/index.js';
7
7
  import dbInstall from './commands/database/install.js';
@@ -20,7 +20,7 @@ export async function createCli() {
20
20
  await loadExtensions();
21
21
  await emitter.emitInit('cli.before', { program });
22
22
  program.name('directus').usage('[command] [options]');
23
- program.version(pkg.version, '-v, --version');
23
+ program.version(version, '-v, --version');
24
24
  program.command('start').description('Start the Directus API').action(startServer);
25
25
  program.command('init').description('Create a new Directus Project').action(init);
26
26
  // Security
@@ -1,8 +1,10 @@
1
+ import { useEnv } from '@directus/env';
1
2
  import { isInstalled, validateMigrations } from '../database/index.js';
2
- import env from '../env.js';
3
3
  import { getExtensionManager } from '../extensions/index.js';
4
- import logger from '../logger.js';
4
+ import { useLogger } from '../logger.js';
5
5
  export const loadExtensions = async () => {
6
+ const env = useEnv();
7
+ const logger = useLogger();
6
8
  if (!('DB_CLIENT' in env))
7
9
  return;
8
10
  const installed = await isInstalled();
@@ -187,7 +187,7 @@ STORAGE_LOCAL_ROOT="./uploads"
187
187
 
188
188
  ## A comma-separated list of metadata keys to collect during file upload. Use * for all
189
189
  # Extracting all metadata might cause memory issues when the file has an unusually large set of metadata
190
- # [ifd0.Make,ifd0.Model,exif.FNumber,exif.ExposureTime,exif.FocalLength,exif.ISO]
190
+ # [ifd0.Make,ifd0.Model,exif.FNumber,exif.ExposureTime,exif.FocalLength,exif.ISOSpeedRatings]
191
191
  # FILE_METADATA_ALLOW_LIST=
192
192
 
193
193
  ####################################################################################################
@@ -1,7 +1,7 @@
1
1
  import type { CookieOptions } from 'express';
2
2
  import type { TransformationParams } from './types/index.js';
3
3
  export declare const SYSTEM_ASSET_ALLOW_LIST: TransformationParams[];
4
- export declare const ASSET_TRANSFORM_QUERY_KEYS: Array<keyof TransformationParams>;
4
+ export declare const ASSET_TRANSFORM_QUERY_KEYS: readonly ["key", "transforms", "width", "height", "format", "fit", "quality", "withoutEnlargement", "focal_point_x", "focal_point_y"];
5
5
  export declare const FILTER_VARIABLES: string[];
6
6
  export declare const ALIAS_TYPES: string[];
7
7
  export declare const DEFAULT_AUTH_PROVIDER = "default";
package/dist/constants.js CHANGED
@@ -1,5 +1,6 @@
1
- import env from './env.js';
1
+ import { useEnv } from '@directus/env';
2
2
  import { getMilliseconds } from './utils/get-milliseconds.js';
3
+ const env = useEnv();
3
4
  export const SYSTEM_ASSET_ALLOW_LIST = [
4
5
  {
5
6
  key: 'system-small-cover',
@@ -41,6 +42,8 @@ export const ASSET_TRANSFORM_QUERY_KEYS = [
41
42
  'fit',
42
43
  'quality',
43
44
  'withoutEnlargement',
45
+ 'focal_point_x',
46
+ 'focal_point_y',
44
47
  ];
45
48
  export const FILTER_VARIABLES = ['$NOW', '$CURRENT_USER', '$CURRENT_ROLE'];
46
49
  export const ALIAS_TYPES = ['alias', 'o2m', 'm2m', 'm2a', 'o2a', 'files', 'translations'];
@@ -1,12 +1,12 @@
1
+ import { useEnv } from '@directus/env';
2
+ import { InvalidQueryError, RangeNotSatisfiableError } from '@directus/errors';
1
3
  import { parseJSON } from '@directus/utils';
2
4
  import contentDisposition from 'content-disposition';
3
5
  import { Router } from 'express';
4
6
  import { merge, pick } from 'lodash-es';
5
7
  import { ASSET_TRANSFORM_QUERY_KEYS, SYSTEM_ASSET_ALLOW_LIST } from '../constants.js';
6
8
  import getDatabase from '../database/index.js';
7
- import env from '../env.js';
8
- import { InvalidQueryError, RangeNotSatisfiableError } from '@directus/errors';
9
- import logger from '../logger.js';
9
+ import { useLogger } from '../logger.js';
10
10
  import useCollection from '../middleware/use-collection.js';
11
11
  import { AssetsService } from '../services/assets.js';
12
12
  import { PayloadService } from '../services/payload.js';
@@ -16,6 +16,7 @@ import { getCacheControlHeader } from '../utils/get-cache-headers.js';
16
16
  import { getConfigFromEnv } from '../utils/get-config-from-env.js';
17
17
  import { getMilliseconds } from '../utils/get-milliseconds.js';
18
18
  const router = Router();
19
+ const env = useEnv();
19
20
  router.use(useCollection('directus_files'));
20
21
  router.get('/:pk/:filename?',
21
22
  // Validate query params
@@ -109,6 +110,7 @@ asyncHandler(async (req, res, next) => {
109
110
  }),
110
111
  // Return file
111
112
  asyncHandler(async (req, res) => {
113
+ const logger = useLogger();
112
114
  const id = req.params['pk'].substring(0, 36);
113
115
  const service = new AssetsService({
114
116
  accountability: req.accountability,
@@ -1,10 +1,9 @@
1
- import { isDirectusError } from '@directus/errors';
1
+ import { useEnv } from '@directus/env';
2
+ import { ErrorCode, InvalidPayloadError, isDirectusError } from '@directus/errors';
2
3
  import { Router } from 'express';
3
4
  import { createLDAPAuthRouter, createLocalAuthRouter, createOAuth2AuthRouter, createOpenIDAuthRouter, createSAMLAuthRouter, } from '../auth/drivers/index.js';
4
5
  import { COOKIE_OPTIONS, DEFAULT_AUTH_PROVIDER } from '../constants.js';
5
- import env from '../env.js';
6
- import { ErrorCode, InvalidPayloadError } from '@directus/errors';
7
- import logger from '../logger.js';
6
+ import { useLogger } from '../logger.js';
8
7
  import { respond } from '../middleware/respond.js';
9
8
  import { AuthenticationService } from '../services/authentication.js';
10
9
  import { UsersService } from '../services/users.js';
@@ -12,6 +11,8 @@ import asyncHandler from '../utils/async-handler.js';
12
11
  import { getAuthProviders } from '../utils/get-auth-providers.js';
13
12
  import { getIPFromReq } from '../utils/get-ip-from-req.js';
14
13
  const router = Router();
14
+ const env = useEnv();
15
+ const logger = useLogger();
15
16
  const authProviders = getAuthProviders();
16
17
  for (const authProvider of authProviders) {
17
18
  let authRouter;
@@ -1,14 +1,15 @@
1
- import { ForbiddenError, RouteNotFoundError } from '@directus/errors';
1
+ import { useEnv } from '@directus/env';
2
+ import { ErrorCode, ForbiddenError, RouteNotFoundError, isDirectusError } from '@directus/errors';
2
3
  import express from 'express';
3
- import env from '../env.js';
4
4
  import { getExtensionManager } from '../extensions/index.js';
5
5
  import { respond } from '../middleware/respond.js';
6
6
  import useCollection from '../middleware/use-collection.js';
7
- import { ExtensionsService } from '../services/extensions.js';
7
+ import { ExtensionReadError, ExtensionsService } from '../services/extensions.js';
8
8
  import asyncHandler from '../utils/async-handler.js';
9
9
  import { getCacheControlHeader } from '../utils/get-cache-headers.js';
10
10
  import { getMilliseconds } from '../utils/get-milliseconds.js';
11
11
  const router = express.Router();
12
+ const env = useEnv();
12
13
  router.use(useCollection('directus_extensions'));
13
14
  router.get('/', asyncHandler(async (req, res, next) => {
14
15
  const service = new ExtensionsService({
@@ -29,9 +30,20 @@ router.patch('/:bundleOrName/:name?', asyncHandler(async (req, res, next) => {
29
30
  if (bundle === undefined || !name) {
30
31
  throw new ForbiddenError();
31
32
  }
32
- await service.updateOne(bundle, name, req.body);
33
- const updated = await service.readOne(bundle, name);
34
- res.locals['payload'] = { data: updated || null };
33
+ try {
34
+ const result = await service.updateOne(bundle, name, req.body);
35
+ res.locals['payload'] = { data: result || null };
36
+ }
37
+ catch (error) {
38
+ let finalError = error;
39
+ if (error instanceof ExtensionReadError) {
40
+ finalError = error.originalError;
41
+ if (isDirectusError(finalError, ErrorCode.Forbidden)) {
42
+ return next();
43
+ }
44
+ }
45
+ throw finalError;
46
+ }
35
47
  return next();
36
48
  }), respond);
37
49
  router.get('/sources/:chunk', asyncHandler(async (req, res) => {
@@ -1,4 +1,5 @@
1
- import { isDirectusError } from '@directus/errors';
1
+ import { useEnv } from '@directus/env';
2
+ import { ErrorCode, InvalidPayloadError, isDirectusError } from '@directus/errors';
2
3
  import formatTitle from '@directus/format-title';
3
4
  import { toArray } from '@directus/utils';
4
5
  import Busboy from 'busboy';
@@ -7,8 +8,6 @@ import express from 'express';
7
8
  import Joi from 'joi';
8
9
  import { minimatch } from 'minimatch';
9
10
  import path from 'path';
10
- import env from '../env.js';
11
- import { ErrorCode, InvalidPayloadError } from '@directus/errors';
12
11
  import { respond } from '../middleware/respond.js';
13
12
  import useCollection from '../middleware/use-collection.js';
14
13
  import { validateBatch } from '../middleware/validate-batch.js';
@@ -17,6 +16,7 @@ import { MetaService } from '../services/meta.js';
17
16
  import asyncHandler from '../utils/async-handler.js';
18
17
  import { sanitizeQuery } from '../utils/sanitize-query.js';
19
18
  const router = express.Router();
19
+ const env = useEnv();
20
20
  router.use(useCollection('directus_files'));
21
21
  export const multipartHandler = (req, res, next) => {
22
22
  if (req.is('multipart/form-data') === false)
@@ -1,9 +1,9 @@
1
+ import { InvalidPayloadError, UnsupportedMediaTypeError } from '@directus/errors';
1
2
  import { parseJSON } from '@directus/utils';
2
3
  import Busboy from 'busboy';
3
4
  import express from 'express';
4
5
  import { load as loadYaml } from 'js-yaml';
5
- import { InvalidPayloadError, UnsupportedMediaTypeError } from '@directus/errors';
6
- import logger from '../logger.js';
6
+ import { useLogger } from '../logger.js';
7
7
  import { respond } from '../middleware/respond.js';
8
8
  import { SchemaService } from '../services/schema.js';
9
9
  import asyncHandler from '../utils/async-handler.js';
@@ -36,6 +36,7 @@ const schemaMultipartHandler = (req, res, next) => {
36
36
  let isFileIncluded = false;
37
37
  let upload = null;
38
38
  busboy.on('file', async (_, fileStream, { mimeType }) => {
39
+ const logger = useLogger();
39
40
  if (isFileIncluded)
40
41
  return next(new InvalidPayloadError({ reason: `More than one file was included in the body` }));
41
42
  isFileIncluded = true;
@@ -1,9 +1,8 @@
1
- import { isDirectusError } from '@directus/errors';
1
+ import { useEnv } from '@directus/env';
2
+ import { ErrorCode, InvalidPayloadError, isDirectusError } from '@directus/errors';
2
3
  import express from 'express';
3
4
  import Joi from 'joi';
4
5
  import { COOKIE_OPTIONS, UUID_REGEX } from '../constants.js';
5
- import env from '../env.js';
6
- import { ErrorCode, InvalidPayloadError } from '@directus/errors';
7
6
  import { respond } from '../middleware/respond.js';
8
7
  import useCollection from '../middleware/use-collection.js';
9
8
  import { validateBatch } from '../middleware/validate-batch.js';
@@ -11,6 +10,7 @@ import { SharesService } from '../services/shares.js';
11
10
  import asyncHandler from '../utils/async-handler.js';
12
11
  import { sanitizeQuery } from '../utils/sanitize-query.js';
13
12
  const router = express.Router();
13
+ const env = useEnv();
14
14
  router.use(useCollection('directus_shares'));
15
15
  const sharedLoginSchema = Joi.object({
16
16
  share: Joi.string().required(),
@@ -6,7 +6,7 @@ import * as geometryHelpers from './geometry/index.js';
6
6
  import * as schemaHelpers from './schema/index.js';
7
7
  import * as sequenceHelpers from './sequence/index.js';
8
8
  export declare function getHelpers(database: Knex): {
9
- date: dateHelpers.mysql | dateHelpers.postgres | dateHelpers.mssql | dateHelpers.sqlite | dateHelpers.oracle;
9
+ date: dateHelpers.postgres | dateHelpers.oracle | dateHelpers.mysql | dateHelpers.mssql | dateHelpers.sqlite;
10
10
  st: geometryHelpers.mysql | geometryHelpers.postgres | geometryHelpers.mssql | geometryHelpers.sqlite | geometryHelpers.oracle | geometryHelpers.redshift;
11
11
  schema: schemaHelpers.mysql | schemaHelpers.cockroachdb | schemaHelpers.mssql | schemaHelpers.postgres | schemaHelpers.sqlite | schemaHelpers.oracle;
12
12
  sequence: sequenceHelpers.mysql | sequenceHelpers.postgres;
@@ -1,7 +1,8 @@
1
1
  import type { SchemaInspector } from '@directus/schema';
2
2
  import type { Knex } from 'knex';
3
3
  import type { DatabaseClient } from '../types/index.js';
4
- export default function getDatabase(): Knex;
4
+ export default getDatabase;
5
+ export declare function getDatabase(): Knex;
5
6
  export declare function getSchemaInspector(): SchemaInspector;
6
7
  /**
7
8
  * Get database version. Value currently exists for MySQL only.
@@ -1,3 +1,4 @@
1
+ import { useEnv } from '@directus/env';
1
2
  import { createInspector } from '@directus/schema';
2
3
  import fse from 'fs-extra';
3
4
  import knex from 'knex';
@@ -7,9 +8,8 @@ import { fileURLToPath } from 'node:url';
7
8
  import path from 'path';
8
9
  import { performance } from 'perf_hooks';
9
10
  import { promisify } from 'util';
10
- import env from '../env.js';
11
11
  import { getExtensionsPath } from '../extensions/lib/get-extensions-path.js';
12
- import logger from '../logger.js';
12
+ import { useLogger } from '../logger.js';
13
13
  import { getConfigFromEnv } from '../utils/get-config-from-env.js';
14
14
  import { validateEnv } from '../utils/validate-env.js';
15
15
  import { getHelpers } from './helpers/index.js';
@@ -17,10 +17,13 @@ let database = null;
17
17
  let inspector = null;
18
18
  let databaseVersion = null;
19
19
  const __dirname = dirname(fileURLToPath(import.meta.url));
20
- export default function getDatabase() {
20
+ export default getDatabase;
21
+ export function getDatabase() {
21
22
  if (database) {
22
23
  return database;
23
24
  }
25
+ const env = useEnv();
26
+ const logger = useLogger();
24
27
  const { client, version, searchPath, connectionString, pool: poolConfig = {}, ...connectionConfig } = getConfigFromEnv('DB_', ['DB_EXCLUDE_TABLES']);
25
28
  const requiredEnvVars = ['DB_CLIENT'];
26
29
  switch (client) {
@@ -164,6 +167,7 @@ export async function hasDatabaseConnection(database) {
164
167
  }
165
168
  export async function validateDatabaseConnection(database) {
166
169
  database = database ?? getDatabase();
170
+ const logger = useLogger();
167
171
  try {
168
172
  if (getDatabaseClient(database) === 'oracle') {
169
173
  await database.raw('select 1 from DUAL');
@@ -208,6 +212,7 @@ export async function isInstalled() {
208
212
  }
209
213
  export async function validateMigrations() {
210
214
  const database = getDatabase();
215
+ const logger = useLogger();
211
216
  try {
212
217
  let migrationFiles = await fse.readdir(path.join(__dirname, 'migrations'));
213
218
  const customMigrationsPath = path.resolve(getExtensionsPath(), 'migrations');
@@ -233,6 +238,7 @@ export async function validateDatabaseExtensions() {
233
238
  const client = getDatabaseClient(database);
234
239
  const helpers = getHelpers(database);
235
240
  const geometrySupport = await helpers.st.supported();
241
+ const logger = useLogger();
236
242
  if (!geometrySupport) {
237
243
  switch (client) {
238
244
  case 'postgres':
@@ -247,7 +253,9 @@ export async function validateDatabaseExtensions() {
247
253
  }
248
254
  }
249
255
  async function validateDatabaseCharset(database) {
256
+ const env = useEnv();
250
257
  database = database ?? getDatabase();
258
+ const logger = useLogger();
251
259
  if (getDatabaseClient(database) === 'mysql') {
252
260
  const { collation } = await database.select(database.raw(`@@collation_database as collation`)).first();
253
261
  const tables = await database('information_schema.tables')
@@ -1,7 +1,8 @@
1
1
  import { createInspector } from '@directus/schema';
2
- import logger from '../../logger.js';
2
+ import { useLogger } from '../../logger.js';
3
3
  import { getDefaultIndexName } from '../../utils/get-default-index-name.js';
4
4
  export async function up(knex) {
5
+ const logger = useLogger();
5
6
  const inspector = createInspector(knex);
6
7
  const foreignKeys = await inspector.foreignKeys();
7
8
  const relations = await knex
@@ -87,6 +88,7 @@ export async function up(knex) {
87
88
  }
88
89
  }
89
90
  export async function down(knex) {
91
+ const logger = useLogger();
90
92
  const relations = await knex
91
93
  .select('many_collection', 'many_field', 'one_collection')
92
94
  .from('directus_relations');
@@ -1,5 +1,5 @@
1
1
  import { createInspector } from '@directus/schema';
2
- import logger from '../../logger.js';
2
+ import { useLogger } from '../../logger.js';
3
3
  /**
4
4
  * Things to keep in mind:
5
5
  *
@@ -77,6 +77,7 @@ const updates = [
77
77
  },
78
78
  ];
79
79
  export async function up(knex) {
80
+ const logger = useLogger();
80
81
  const inspector = createInspector(knex);
81
82
  const foreignKeys = await inspector.foreignKeys();
82
83
  for (const update of updates) {
@@ -123,6 +124,7 @@ export async function up(knex) {
123
124
  }
124
125
  }
125
126
  export async function down(knex) {
127
+ const logger = useLogger();
126
128
  for (const update of updates) {
127
129
  for (const constraint of update.constraints) {
128
130
  try {
@@ -1,6 +1,7 @@
1
1
  import { parseJSON } from '@directus/utils';
2
- import logger from '../../logger.js';
2
+ import { useLogger } from '../../logger.js';
3
3
  export async function up(knex) {
4
+ const logger = useLogger();
4
5
  const dividerGroups = await knex.select('*').from('directus_fields').where('interface', '=', 'group-divider');
5
6
  for (const dividerGroup of dividerGroups) {
6
7
  const newOptions = { showHeader: true };
@@ -1,5 +1,5 @@
1
1
  import { createInspector } from '@directus/schema';
2
- import logger from '../../logger.js';
2
+ import { useLogger } from '../../logger.js';
3
3
  import { getHelpers } from '../helpers/index.js';
4
4
  export async function up(knex) {
5
5
  const helper = getHelpers(knex).schema;
@@ -33,6 +33,7 @@ export async function down(knex) {
33
33
  * Temporarily drop foreign key constraint for MySQL instances, see https://github.com/directus/directus/issues/19399
34
34
  */
35
35
  async function dropConstraint(knex) {
36
+ const logger = useLogger();
36
37
  const inspector = createInspector(knex);
37
38
  const foreignKeys = await inspector.foreignKeys('directus_shares');
38
39
  const collectionForeignKeys = foreignKeys.filter((fk) => fk.column === 'collection');
@@ -0,0 +1,3 @@
1
+ import type { Knex } from 'knex';
2
+ export declare function up(knex: Knex): Promise<void>;
3
+ export declare function down(knex: Knex): Promise<void>;
@@ -0,0 +1,12 @@
1
+ export async function up(knex) {
2
+ await knex.schema.alterTable('directus_files', (table) => {
3
+ table.integer('focal_point_x').nullable();
4
+ table.integer('focal_point_y').nullable();
5
+ });
6
+ }
7
+ export async function down(knex) {
8
+ await knex.schema.alterTable('directus_files', (table) => {
9
+ table.dropColumn('focal_point_x');
10
+ table.dropColumn('focal_point_y');
11
+ });
12
+ }
@@ -6,10 +6,11 @@ import { fileURLToPath } from 'node:url';
6
6
  import path from 'path';
7
7
  import { flushCaches } from '../../cache.js';
8
8
  import { getExtensionsPath } from '../../extensions/lib/get-extensions-path.js';
9
- import logger from '../../logger.js';
9
+ import { useLogger } from '../../logger.js';
10
10
  import getModuleDefault from '../../utils/get-module-default.js';
11
11
  const __dirname = dirname(fileURLToPath(import.meta.url));
12
12
  export default async function run(database, direction, log = true) {
13
+ const logger = useLogger();
13
14
  let migrationFiles = await fse.readdir(__dirname);
14
15
  const customMigrationsPath = path.resolve(getExtensionsPath(), 'migrations');
15
16
  let customMigrationFiles = ((await fse.pathExists(customMigrationsPath)) && (await fse.readdir(customMigrationsPath))) || [];