@directus/api 15.0.0 → 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 (169) hide show
  1. package/dist/app.js +6 -4
  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 +9 -2
  5. package/dist/auth/drivers/openid.js +9 -2
  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 +2 -1
  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/load-extensions.js +4 -2
  23. package/dist/cli/utils/create-env/env-stub.liquid +1 -1
  24. package/dist/constants.d.ts +1 -1
  25. package/dist/constants.js +4 -1
  26. package/dist/controllers/assets.js +5 -3
  27. package/dist/controllers/auth.js +5 -4
  28. package/dist/controllers/extensions.js +18 -6
  29. package/dist/controllers/files.js +3 -3
  30. package/dist/controllers/schema.js +3 -2
  31. package/dist/controllers/shares.js +3 -3
  32. package/dist/database/helpers/index.d.ts +1 -1
  33. package/dist/database/index.js +9 -2
  34. package/dist/database/migrations/20210518A-add-foreign-key-constraints.js +3 -1
  35. package/dist/database/migrations/20210519A-add-system-fk-triggers.js +3 -1
  36. package/dist/database/migrations/20210802A-replace-groups.js +2 -1
  37. package/dist/database/migrations/20230721A-require-shares-fields.js +2 -1
  38. package/dist/database/migrations/20231215A-add-focalpoints.d.ts +3 -0
  39. package/dist/database/migrations/20231215A-add-focalpoints.js +12 -0
  40. package/dist/database/migrations/run.js +2 -1
  41. package/dist/database/run-ast.js +5 -2
  42. package/dist/database/system-data/app-access-permissions/app-access-permissions.yaml +0 -7
  43. package/dist/database/system-data/fields/files.yaml +16 -0
  44. package/dist/emitter.js +3 -1
  45. package/dist/extensions/lib/get-extensions-path.d.ts +1 -1
  46. package/dist/extensions/lib/get-extensions-path.js +2 -1
  47. package/dist/extensions/lib/get-extensions.d.ts +1 -1
  48. package/dist/extensions/lib/get-extensions.js +32 -8
  49. package/dist/extensions/lib/get-shared-deps-mapping.js +6 -4
  50. package/dist/extensions/lib/sandbox/register/call-reference.js +4 -2
  51. package/dist/extensions/lib/sandbox/sdk/generators/log.js +2 -1
  52. package/dist/extensions/lib/sync-extensions.js +6 -4
  53. package/dist/extensions/manager.js +43 -19
  54. package/dist/flows.js +13 -7
  55. package/dist/logger.d.ts +7 -7
  56. package/dist/logger.js +116 -92
  57. package/dist/mailer.js +4 -2
  58. package/dist/middleware/cache.js +4 -2
  59. package/dist/middleware/check-ip.js +25 -6
  60. package/dist/middleware/cors.js +2 -1
  61. package/dist/middleware/error-handler.js +5 -5
  62. package/dist/middleware/rate-limiter-global.js +4 -2
  63. package/dist/middleware/rate-limiter-ip.js +2 -1
  64. package/dist/middleware/respond.js +4 -2
  65. package/dist/operations/log/index.js +2 -1
  66. package/dist/rate-limiter.d.ts +2 -1
  67. package/dist/rate-limiter.js +5 -2
  68. package/dist/redis/index.d.ts +3 -2
  69. package/dist/redis/index.js +3 -2
  70. package/dist/redis/{create-redis.js → lib/create-redis.js} +2 -2
  71. package/dist/redis/utils/redis-config-available.d.ts +4 -0
  72. package/dist/redis/utils/redis-config-available.js +8 -0
  73. package/dist/request/request-interceptor.js +7 -5
  74. package/dist/request/response-interceptor.js +2 -2
  75. package/dist/request/validate-ip.d.ts +1 -1
  76. package/dist/request/validate-ip.js +23 -7
  77. package/dist/server.js +11 -7
  78. package/dist/services/activity.js +5 -4
  79. package/dist/services/assets.d.ts +2 -0
  80. package/dist/services/assets.js +9 -4
  81. package/dist/services/authentication.js +17 -9
  82. package/dist/services/collections.js +5 -4
  83. package/dist/services/extensions.d.ts +15 -9
  84. package/dist/services/extensions.js +74 -39
  85. package/dist/services/fields.js +9 -4
  86. package/dist/services/files.d.ts +2 -2
  87. package/dist/services/files.js +22 -14
  88. package/dist/services/graphql/index.js +46 -3
  89. package/dist/services/graphql/subscription.js +2 -2
  90. package/dist/services/graphql/types/bigint.js +16 -5
  91. package/dist/services/graphql/utils/process-error.d.ts +4 -1
  92. package/dist/services/graphql/utils/process-error.js +10 -8
  93. package/dist/services/import-export/index.js +5 -3
  94. package/dist/services/items.js +12 -8
  95. package/dist/services/mail/index.js +4 -2
  96. package/dist/services/notifications.js +7 -3
  97. package/dist/services/relations.js +19 -10
  98. package/dist/services/server.js +5 -4
  99. package/dist/services/shares.js +3 -2
  100. package/dist/services/specifications.js +2 -1
  101. package/dist/services/users.js +20 -9
  102. package/dist/services/versions.js +6 -5
  103. package/dist/services/webhooks.d.ts +2 -2
  104. package/dist/services/webhooks.js +2 -2
  105. package/dist/services/websocket.d.ts +1 -1
  106. package/dist/services/websocket.js +4 -3
  107. package/dist/storage/register-drivers.js +2 -1
  108. package/dist/storage/register-locations.js +2 -1
  109. package/dist/synchronization.js +3 -1
  110. package/dist/telemetry/lib/get-report.js +1 -1
  111. package/dist/telemetry/lib/init-telemetry.js +2 -2
  112. package/dist/telemetry/lib/send-report.js +1 -1
  113. package/dist/telemetry/lib/track.js +2 -3
  114. package/dist/telemetry/utils/get-user-count.js +1 -1
  115. package/dist/types/assets.d.ts +2 -0
  116. package/dist/utils/apply-diff.js +2 -1
  117. package/dist/utils/apply-query.js +0 -11
  118. package/dist/utils/delete-from-require-cache.js +2 -1
  119. package/dist/utils/get-accountability-for-token.js +3 -2
  120. package/dist/utils/get-auth-providers.js +2 -1
  121. package/dist/utils/get-cache-headers.js +5 -2
  122. package/dist/utils/get-config-from-env.js +2 -1
  123. package/dist/utils/get-default-value.js +4 -3
  124. package/dist/utils/get-ip-from-req.js +4 -2
  125. package/dist/utils/get-permissions.js +5 -3
  126. package/dist/utils/get-schema.js +5 -2
  127. package/dist/utils/get-snapshot-diff.js +7 -9
  128. package/dist/utils/get-snapshot.js +4 -4
  129. package/dist/utils/ip-in-networks.d.ts +6 -0
  130. package/dist/utils/ip-in-networks.js +13 -0
  131. package/dist/utils/is-url-allowed.js +2 -1
  132. package/dist/utils/job-queue.d.ts +1 -0
  133. package/dist/utils/job-queue.js +3 -0
  134. package/dist/utils/sanitize-query.js +7 -2
  135. package/dist/utils/sanitize-schema.d.ts +1 -1
  136. package/dist/utils/should-clear-cache.js +2 -1
  137. package/dist/utils/should-skip-cache.js +2 -1
  138. package/dist/utils/transformations.js +95 -12
  139. package/dist/utils/validate-env.js +4 -2
  140. package/dist/utils/validate-query.js +7 -3
  141. package/dist/utils/validate-storage.js +4 -2
  142. package/dist/webhooks.js +4 -3
  143. package/dist/websocket/controllers/base.js +12 -6
  144. package/dist/websocket/controllers/graphql.js +4 -2
  145. package/dist/websocket/controllers/hooks.js +3 -2
  146. package/dist/websocket/controllers/index.js +4 -2
  147. package/dist/websocket/controllers/rest.js +4 -2
  148. package/dist/websocket/errors.js +2 -1
  149. package/dist/websocket/handlers/heartbeat.js +4 -3
  150. package/dist/websocket/handlers/subscribe.d.ts +2 -2
  151. package/dist/websocket/handlers/subscribe.js +5 -4
  152. package/package.json +57 -57
  153. package/dist/__utils__/items-utils.d.ts +0 -2
  154. package/dist/__utils__/items-utils.js +0 -31
  155. package/dist/__utils__/mock-env.d.ts +0 -18
  156. package/dist/__utils__/mock-env.js +0 -41
  157. package/dist/__utils__/schemas.d.ts +0 -13
  158. package/dist/__utils__/schemas.js +0 -301
  159. package/dist/__utils__/snapshots.d.ts +0 -5
  160. package/dist/__utils__/snapshots.js +0 -903
  161. package/dist/env.d.ts +0 -14
  162. package/dist/env.js +0 -511
  163. package/dist/messenger.d.ts +0 -24
  164. package/dist/messenger.js +0 -64
  165. package/dist/utils/to-boolean.d.ts +0 -4
  166. package/dist/utils/to-boolean.js +0 -6
  167. /package/dist/redis/{create-redis.d.ts → lib/create-redis.d.ts} +0 -0
  168. /package/dist/redis/{use-redis.d.ts → lib/use-redis.d.ts} +0 -0
  169. /package/dist/redis/{use-redis.js → lib/use-redis.js} +0 -0
package/dist/app.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { useEnv } from '@directus/env';
1
2
  import { InvalidPayloadError, ServiceUnavailableError } from '@directus/errors';
2
3
  import { handlePressure } from '@directus/pressure';
3
4
  import cookieParser from 'cookie-parser';
@@ -40,10 +41,9 @@ import versionsRouter from './controllers/versions.js';
40
41
  import webhooksRouter from './controllers/webhooks.js';
41
42
  import { isInstalled, validateDatabaseConnection, validateDatabaseExtensions, validateMigrations, } from './database/index.js';
42
43
  import emitter from './emitter.js';
43
- import env from './env.js';
44
44
  import { getExtensionManager } from './extensions/index.js';
45
45
  import { getFlowManager } from './flows.js';
46
- import logger, { expressLogger } from './logger.js';
46
+ import { createExpressLogger, useLogger } from './logger.js';
47
47
  import authenticate from './middleware/authenticate.js';
48
48
  import cache from './middleware/cache.js';
49
49
  import { checkIP } from './middleware/check-ip.js';
@@ -55,14 +55,16 @@ import rateLimiterGlobal from './middleware/rate-limiter-global.js';
55
55
  import rateLimiter from './middleware/rate-limiter-ip.js';
56
56
  import sanitizeQuery from './middleware/sanitize-query.js';
57
57
  import schema from './middleware/schema.js';
58
+ import { initTelemetry } from './telemetry/index.js';
58
59
  import { getConfigFromEnv } from './utils/get-config-from-env.js';
59
60
  import { Url } from './utils/url.js';
60
61
  import { validateEnv } from './utils/validate-env.js';
61
62
  import { validateStorage } from './utils/validate-storage.js';
62
63
  import { init as initWebhooks } from './webhooks.js';
63
- import { initTelemetry } from './telemetry/index.js';
64
64
  const require = createRequire(import.meta.url);
65
65
  export default async function createApp() {
66
+ const env = useEnv();
67
+ const logger = useLogger();
66
68
  const helmet = await import('helmet');
67
69
  validateEnv(['KEY', 'SECRET']);
68
70
  if (!new Url(env['PUBLIC_URL']).isAbsolute()) {
@@ -124,7 +126,7 @@ export default async function createApp() {
124
126
  }
125
127
  await emitter.emitInit('app.before', { app });
126
128
  await emitter.emitInit('middlewares.before', { app });
127
- app.use(expressLogger);
129
+ app.use(createExpressLogger());
128
130
  app.use((_req, res, next) => {
129
131
  res.setHeader('X-Powered-By', 'Directus');
130
132
  next();
@@ -1,12 +1,11 @@
1
- import { isDirectusError } from '@directus/errors';
1
+ import { useEnv } from '@directus/env';
2
+ import { ErrorCode, InvalidCredentialsError, InvalidPayloadError, InvalidProviderConfigError, InvalidProviderError, ServiceUnavailableError, UnexpectedResponseError, isDirectusError, } from '@directus/errors';
2
3
  import { Router } from 'express';
3
4
  import Joi from 'joi';
4
5
  import ldap from 'ldapjs';
5
6
  import getDatabase from '../../database/index.js';
6
7
  import emitter from '../../emitter.js';
7
- import env from '../../env.js';
8
- import { ErrorCode, InvalidCredentialsError, InvalidPayloadError, InvalidProviderError, InvalidProviderConfigError, ServiceUnavailableError, UnexpectedResponseError, } from '@directus/errors';
9
- import logger from '../../logger.js';
8
+ import { useLogger } from '../../logger.js';
10
9
  import { respond } from '../../middleware/respond.js';
11
10
  import { AuthenticationService } from '../../services/authentication.js';
12
11
  import { UsersService } from '../../services/users.js';
@@ -24,6 +23,7 @@ export class LDAPAuthDriver extends AuthDriver {
24
23
  config;
25
24
  constructor(options, config) {
26
25
  super(options, config);
26
+ const logger = useLogger();
27
27
  const { bindDn, bindPassword, userDn, provider, clientUrl } = config;
28
28
  if (bindDn === undefined ||
29
29
  bindPassword === undefined ||
@@ -42,6 +42,7 @@ export class LDAPAuthDriver extends AuthDriver {
42
42
  this.config = config;
43
43
  }
44
44
  async validateBindClient() {
45
+ const logger = useLogger();
45
46
  const { bindDn, bindPassword, provider } = this.config;
46
47
  return new Promise((resolve, reject) => {
47
48
  // Healthcheck bind user
@@ -168,6 +169,7 @@ export class LDAPAuthDriver extends AuthDriver {
168
169
  if (!payload['identifier']) {
169
170
  throw new InvalidCredentialsError();
170
171
  }
172
+ const logger = useLogger();
171
173
  await this.validateBindClient();
172
174
  const { userDn, userScope, userAttribute, groupDn, groupScope, groupAttribute, defaultRoleId } = this.config;
173
175
  const userInfo = await this.fetchUserInfo(userDn, new ldap.EqualityFilter({
@@ -292,6 +294,7 @@ export function createLDAPAuthRouter(provider) {
292
294
  otp: Joi.string(),
293
295
  }).unknown();
294
296
  router.post('/', asyncHandler(async (req, res, next) => {
297
+ const env = useEnv();
295
298
  const accountability = {
296
299
  ip: getIPFromReq(req),
297
300
  role: null,
@@ -1,10 +1,10 @@
1
+ import { InvalidCredentialsError, InvalidPayloadError } from '@directus/errors';
1
2
  import argon2 from 'argon2';
2
3
  import { Router } from 'express';
3
4
  import Joi from 'joi';
4
5
  import { performance } from 'perf_hooks';
5
6
  import { COOKIE_OPTIONS } from '../../constants.js';
6
- import env from '../../env.js';
7
- import { InvalidCredentialsError, InvalidPayloadError } from '@directus/errors';
7
+ import { useEnv } from '@directus/env';
8
8
  import { respond } from '../../middleware/respond.js';
9
9
  import { AuthenticationService } from '../../services/authentication.js';
10
10
  import asyncHandler from '../../utils/async-handler.js';
@@ -36,6 +36,7 @@ export class LocalAuthDriver extends AuthDriver {
36
36
  }
37
37
  }
38
38
  export function createLocalAuthRouter(provider) {
39
+ const env = useEnv();
39
40
  const router = Router();
40
41
  const userLoginSchema = Joi.object({
41
42
  email: Joi.string().email().required(),
@@ -1,3 +1,4 @@
1
+ import { useEnv } from '@directus/env';
1
2
  import { ErrorCode, InvalidCredentialsError, InvalidProviderConfigError, InvalidProviderError, InvalidTokenError, isDirectusError, ServiceUnavailableError, } from '@directus/errors';
2
3
  import { parseJSON } from '@directus/utils';
3
4
  import express, { Router } from 'express';
@@ -7,8 +8,7 @@ import { errors, generators, Issuer } from 'openid-client';
7
8
  import { getAuthProvider } from '../../auth.js';
8
9
  import getDatabase from '../../database/index.js';
9
10
  import emitter from '../../emitter.js';
10
- import env from '../../env.js';
11
- import logger from '../../logger.js';
11
+ import { useLogger } from '../../logger.js';
12
12
  import { respond } from '../../middleware/respond.js';
13
13
  import { AuthenticationService } from '../../services/authentication.js';
14
14
  import { UsersService } from '../../services/users.js';
@@ -25,6 +25,8 @@ export class OAuth2AuthDriver extends LocalAuthDriver {
25
25
  config;
26
26
  constructor(options, config) {
27
27
  super(options, config);
28
+ const env = useEnv();
29
+ const logger = useLogger();
28
30
  const { authorizeUrl, accessUrl, profileUrl, clientId, clientSecret, ...additionalConfig } = config;
29
31
  if (!authorizeUrl || !accessUrl || !profileUrl || !clientId || !clientSecret || !additionalConfig['provider']) {
30
32
  logger.error('Invalid provider config');
@@ -81,6 +83,7 @@ export class OAuth2AuthDriver extends LocalAuthDriver {
81
83
  return user?.id;
82
84
  }
83
85
  async getUserID(payload) {
86
+ const logger = useLogger();
84
87
  if (!payload['code'] || !payload['codeVerifier'] || !payload['state']) {
85
88
  logger.warn('[OAuth2] No code, codeVerifier or state in payload');
86
89
  throw new InvalidCredentialsError();
@@ -160,6 +163,7 @@ export class OAuth2AuthDriver extends LocalAuthDriver {
160
163
  return this.refresh(user);
161
164
  }
162
165
  async refresh(user) {
166
+ const logger = useLogger();
163
167
  let authData = user.auth_data;
164
168
  if (typeof authData === 'string') {
165
169
  try {
@@ -186,6 +190,7 @@ export class OAuth2AuthDriver extends LocalAuthDriver {
186
190
  }
187
191
  }
188
192
  const handleError = (e) => {
193
+ const logger = useLogger();
189
194
  if (e instanceof errors.OPError) {
190
195
  if (e.error === 'invalid_grant') {
191
196
  // Invalid token
@@ -209,6 +214,7 @@ const handleError = (e) => {
209
214
  };
210
215
  export function createOAuth2AuthRouter(providerName) {
211
216
  const router = Router();
217
+ const env = useEnv();
212
218
  router.get('/', (req, res) => {
213
219
  const provider = getAuthProvider(providerName);
214
220
  const codeVerifier = provider.generateCodeVerifier();
@@ -227,6 +233,7 @@ export function createOAuth2AuthRouter(providerName) {
227
233
  res.redirect(303, `./callback?${new URLSearchParams(req.body)}`);
228
234
  }, respond);
229
235
  router.get('/callback', asyncHandler(async (req, res, next) => {
236
+ const logger = useLogger();
230
237
  let tokenData;
231
238
  try {
232
239
  tokenData = jwt.verify(req.cookies[`oauth2.${providerName}`], env['SECRET'], {
@@ -1,3 +1,4 @@
1
+ import { useEnv } from '@directus/env';
1
2
  import { ErrorCode, InvalidCredentialsError, InvalidProviderConfigError, InvalidProviderError, InvalidTokenError, isDirectusError, ServiceUnavailableError, } from '@directus/errors';
2
3
  import { parseJSON } from '@directus/utils';
3
4
  import express, { Router } from 'express';
@@ -7,8 +8,7 @@ import { errors, generators, Issuer } from 'openid-client';
7
8
  import { getAuthProvider } from '../../auth.js';
8
9
  import getDatabase from '../../database/index.js';
9
10
  import emitter from '../../emitter.js';
10
- import env from '../../env.js';
11
- import logger from '../../logger.js';
11
+ import { useLogger } from '../../logger.js';
12
12
  import { respond } from '../../middleware/respond.js';
13
13
  import { AuthenticationService } from '../../services/authentication.js';
14
14
  import { UsersService } from '../../services/users.js';
@@ -25,6 +25,8 @@ export class OpenIDAuthDriver extends LocalAuthDriver {
25
25
  config;
26
26
  constructor(options, config) {
27
27
  super(options, config);
28
+ const env = useEnv();
29
+ const logger = useLogger();
28
30
  const { issuerUrl, clientId, clientSecret, ...additionalConfig } = config;
29
31
  if (!issuerUrl || !clientId || !clientSecret || !additionalConfig['provider']) {
30
32
  logger.error('Invalid provider config');
@@ -93,6 +95,7 @@ export class OpenIDAuthDriver extends LocalAuthDriver {
93
95
  return user?.id;
94
96
  }
95
97
  async getUserID(payload) {
98
+ const logger = useLogger();
96
99
  if (!payload['code'] || !payload['codeVerifier'] || !payload['state']) {
97
100
  logger.warn('[OpenID] No code, codeVerifier or state in payload');
98
101
  throw new InvalidCredentialsError();
@@ -180,6 +183,7 @@ export class OpenIDAuthDriver extends LocalAuthDriver {
180
183
  return this.refresh(user);
181
184
  }
182
185
  async refresh(user) {
186
+ const logger = useLogger();
183
187
  let authData = user.auth_data;
184
188
  if (typeof authData === 'string') {
185
189
  try {
@@ -207,6 +211,7 @@ export class OpenIDAuthDriver extends LocalAuthDriver {
207
211
  }
208
212
  }
209
213
  const handleError = (e) => {
214
+ const logger = useLogger();
210
215
  if (e instanceof errors.OPError) {
211
216
  if (e.error === 'invalid_grant') {
212
217
  // Invalid token
@@ -229,6 +234,7 @@ const handleError = (e) => {
229
234
  return e;
230
235
  };
231
236
  export function createOpenIDAuthRouter(providerName) {
237
+ const env = useEnv();
232
238
  const router = Router();
233
239
  router.get('/', asyncHandler(async (req, res) => {
234
240
  const provider = getAuthProvider(providerName);
@@ -248,6 +254,7 @@ export function createOpenIDAuthRouter(providerName) {
248
254
  res.redirect(303, `./callback?${new URLSearchParams(req.body)}`);
249
255
  }, respond);
250
256
  router.get('/callback', asyncHandler(async (req, res, next) => {
257
+ const logger = useLogger();
251
258
  let tokenData;
252
259
  try {
253
260
  tokenData = jwt.verify(req.cookies[`openid.${providerName}`], env['SECRET'], {
@@ -1,14 +1,13 @@
1
1
  import * as validator from '@authenio/samlify-node-xmllint';
2
- import { isDirectusError } from '@directus/errors';
2
+ import { useEnv } from '@directus/env';
3
+ import { ErrorCode, InvalidCredentialsError, InvalidProviderError, isDirectusError } from '@directus/errors';
3
4
  import express, { Router } from 'express';
4
5
  import * as samlify from 'samlify';
5
6
  import { getAuthProvider } from '../../auth.js';
6
7
  import { COOKIE_OPTIONS } from '../../constants.js';
7
8
  import getDatabase from '../../database/index.js';
8
9
  import emitter from '../../emitter.js';
9
- import env from '../../env.js';
10
- import { ErrorCode, InvalidCredentialsError, InvalidProviderError } from '@directus/errors';
11
- import logger from '../../logger.js';
10
+ import { useLogger } from '../../logger.js';
12
11
  import { respond } from '../../middleware/respond.js';
13
12
  import { AuthenticationService } from '../../services/authentication.js';
14
13
  import { UsersService } from '../../services/users.js';
@@ -38,6 +37,7 @@ export class SAMLAuthDriver extends LocalAuthDriver {
38
37
  return user?.id;
39
38
  }
40
39
  async getUserID(payload) {
40
+ const logger = useLogger();
41
41
  const { provider, emailKey, identifierKey, givenNameKey, familyNameKey, allowPublicRegistration } = this.config;
42
42
  const email = payload[emailKey ?? 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress'];
43
43
  const identifier = payload[identifierKey ?? 'http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier'];
@@ -79,6 +79,7 @@ export class SAMLAuthDriver extends LocalAuthDriver {
79
79
  }
80
80
  export function createSAMLAuthRouter(providerName) {
81
81
  const router = Router();
82
+ const env = useEnv();
82
83
  router.get('/metadata', asyncHandler(async (_req, res) => {
83
84
  const { sp } = getAuthProvider(providerName);
84
85
  return res.header('Content-Type', 'text/xml').send(sp.getMetadata());
@@ -106,6 +107,7 @@ export function createSAMLAuthRouter(providerName) {
106
107
  return res.redirect(context);
107
108
  }));
108
109
  router.post('/acs', express.urlencoded({ extended: false }), asyncHandler(async (req, res, next) => {
110
+ const logger = useLogger();
109
111
  const relayState = req.body?.RelayState;
110
112
  try {
111
113
  const { sp, idp } = getAuthProvider(providerName);
package/dist/auth.js CHANGED
@@ -1,15 +1,15 @@
1
+ import { useEnv } from '@directus/env';
2
+ import { InvalidProviderConfigError } from '@directus/errors';
1
3
  import { toArray } from '@directus/utils';
2
4
  import { LDAPAuthDriver, LocalAuthDriver, OAuth2AuthDriver, OpenIDAuthDriver, SAMLAuthDriver, } from './auth/drivers/index.js';
3
5
  import { DEFAULT_AUTH_PROVIDER } from './constants.js';
4
6
  import getDatabase from './database/index.js';
5
- import env from './env.js';
6
- import { InvalidProviderConfigError } from '@directus/errors';
7
- import logger from './logger.js';
7
+ import { useLogger } from './logger.js';
8
8
  import { getConfigFromEnv } from './utils/get-config-from-env.js';
9
9
  import { getSchema } from './utils/get-schema.js';
10
- const providerNames = toArray(env['AUTH_PROVIDERS']);
11
10
  const providers = new Map();
12
11
  export function getAuthProvider(provider) {
12
+ const logger = useLogger();
13
13
  if (!providers.has(provider)) {
14
14
  logger.error('Auth provider not configured');
15
15
  throw new InvalidProviderConfigError({ provider });
@@ -17,7 +17,10 @@ export function getAuthProvider(provider) {
17
17
  return providers.get(provider);
18
18
  }
19
19
  export async function registerAuthProviders() {
20
+ const env = useEnv();
21
+ const logger = useLogger();
20
22
  const options = { knex: getDatabase(), schema: await getSchema() };
23
+ const providerNames = toArray(env['AUTH_PROVIDERS']);
21
24
  // Register default provider if not disabled
22
25
  if (!env['AUTH_DISABLE_DEFAULT']) {
23
26
  const defaultProvider = getProviderInstance('local', options);
@@ -0,0 +1 @@
1
+ export { useBus } from './lib/use-bus.js';
@@ -0,0 +1 @@
1
+ export { useBus } from './lib/use-bus.js';
@@ -0,0 +1,9 @@
1
+ import { type Bus } from '@directus/memory';
2
+ export declare const _cache: {
3
+ bus: Bus | undefined;
4
+ };
5
+ /**
6
+ * Returns globally shared message bus. If Redis is available, will use a redis-driven pub/sub bus.
7
+ * Otherwise will default to a local-only bus.
8
+ */
9
+ export declare const useBus: () => Bus;
@@ -0,0 +1,21 @@
1
+ import { createBus } from '@directus/memory';
2
+ import { redisConfigAvailable, useRedis } from '../../redis/index.js';
3
+ export const _cache = {
4
+ bus: undefined,
5
+ };
6
+ /**
7
+ * Returns globally shared message bus. If Redis is available, will use a redis-driven pub/sub bus.
8
+ * Otherwise will default to a local-only bus.
9
+ */
10
+ export const useBus = () => {
11
+ if (_cache.bus) {
12
+ return _cache.bus;
13
+ }
14
+ if (redisConfigAvailable()) {
15
+ _cache.bus = createBus({ type: 'redis', redis: useRedis(), namespace: 'directus:bus' });
16
+ }
17
+ else {
18
+ _cache.bus = createBus({ type: 'local' });
19
+ }
20
+ return _cache.bus;
21
+ };
package/dist/cache.js CHANGED
@@ -1,13 +1,16 @@
1
+ import { useEnv } from '@directus/env';
1
2
  import { getSimpleHash } from '@directus/utils';
2
3
  import Keyv from 'keyv';
3
- import env from './env.js';
4
- import logger from './logger.js';
5
- import { getMessenger } from './messenger.js';
4
+ import { useBus } from './bus/index.js';
5
+ import { useLogger } from './logger.js';
6
+ import { redisConfigAvailable } from './redis/index.js';
6
7
  import { compress, decompress } from './utils/compress.js';
7
8
  import { getConfigFromEnv } from './utils/get-config-from-env.js';
8
9
  import { getMilliseconds } from './utils/get-milliseconds.js';
9
10
  import { validateEnv } from './utils/validate-env.js';
10
11
  import { createRequire } from 'node:module';
12
+ const logger = useLogger();
13
+ const env = useEnv();
11
14
  const require = createRequire(import.meta.url);
12
15
  let cache = null;
13
16
  let systemCache = null;
@@ -15,11 +18,8 @@ let localSchemaCache = null;
15
18
  let sharedSchemaCache = null;
16
19
  let lockCache = null;
17
20
  let messengerSubscribed = false;
18
- const messenger = getMessenger();
19
- if (env['MESSENGER_STORE'] === 'redis' &&
20
- env['CACHE_STORE'] === 'memory' &&
21
- env['CACHE_AUTO_PURGE'] &&
22
- !messengerSubscribed) {
21
+ const messenger = useBus();
22
+ if (redisConfigAvailable() && env['CACHE_STORE'] === 'memory' && env['CACHE_AUTO_PURGE'] && !messengerSubscribed) {
23
23
  messengerSubscribed = true;
24
24
  messenger.subscribe('schemaChanged', async (opts) => {
25
25
  if (cache && opts?.['autoPurgeCache'] !== false) {
@@ -122,7 +122,7 @@ function getConfig(store = 'memory', ttl, namespaceSuffix = '') {
122
122
  };
123
123
  if (store === 'redis') {
124
124
  const KeyvRedis = require('@keyv/redis');
125
- config.store = new KeyvRedis(env['REDIS'] || getConfigFromEnv('REDIS'));
125
+ config.store = new KeyvRedis(env['REDIS'] || getConfigFromEnv('REDIS'), { useRedisSets: false });
126
126
  }
127
127
  return config;
128
128
  }
@@ -1,15 +1,17 @@
1
+ import { useEnv } from '@directus/env';
1
2
  import getDatabase, { hasDatabaseConnection, isInstalled, validateDatabaseConnection, } from '../../../database/index.js';
2
3
  import runMigrations from '../../../database/migrations/run.js';
3
4
  import installDatabase from '../../../database/seeds/run.js';
4
- import env from '../../../env.js';
5
- import logger from '../../../logger.js';
5
+ import { useLogger } from '../../../logger.js';
6
6
  import { RolesService } from '../../../services/roles.js';
7
7
  import { SettingsService } from '../../../services/settings.js';
8
8
  import { UsersService } from '../../../services/users.js';
9
9
  import { getSchema } from '../../../utils/get-schema.js';
10
10
  import { defaultAdminRole, defaultAdminUser } from '../../utils/defaults.js';
11
11
  export default async function bootstrap({ skipAdminInit }) {
12
+ const logger = useLogger();
12
13
  logger.info('Initializing bootstrap...');
14
+ const env = useEnv();
13
15
  const database = getDatabase();
14
16
  await waitForDatabase(database);
15
17
  if ((await isInstalled()) === false) {
@@ -52,6 +54,8 @@ async function waitForDatabase(database) {
52
54
  return database;
53
55
  }
54
56
  async function createDefaultAdmin(schema) {
57
+ const logger = useLogger();
58
+ const env = useEnv();
55
59
  const { nanoid } = await import('nanoid');
56
60
  logger.info('Setting up first admin role...');
57
61
  const rolesService = new RolesService({ schema });
@@ -1,7 +1,8 @@
1
1
  import getDatabase from '../../../database/index.js';
2
- import logger from '../../../logger.js';
2
+ import { useLogger } from '../../../logger.js';
3
3
  export default async function count(collection) {
4
4
  const database = getDatabase();
5
+ const logger = useLogger();
5
6
  if (!collection) {
6
7
  logger.error('Collection is required');
7
8
  process.exit(1);
@@ -1,8 +1,9 @@
1
1
  import installSeeds from '../../../database/seeds/run.js';
2
2
  import getDatabase from '../../../database/index.js';
3
- import logger from '../../../logger.js';
3
+ import { useLogger } from '../../../logger.js';
4
4
  export default async function start() {
5
5
  const database = getDatabase();
6
+ const logger = useLogger();
6
7
  try {
7
8
  await installSeeds(database);
8
9
  database.destroy();
@@ -1,8 +1,9 @@
1
1
  import run from '../../../database/migrations/run.js';
2
2
  import getDatabase from '../../../database/index.js';
3
- import logger from '../../../logger.js';
3
+ import { useLogger } from '../../../logger.js';
4
4
  export default async function migrate(direction) {
5
5
  const database = getDatabase();
6
+ const logger = useLogger();
6
7
  try {
7
8
  logger.info('Running migrations...');
8
9
  await run(database, direction);
@@ -1,9 +1,10 @@
1
1
  import { getSchema } from '../../../utils/get-schema.js';
2
2
  import { RolesService } from '../../../services/roles.js';
3
3
  import getDatabase from '../../../database/index.js';
4
- import logger from '../../../logger.js';
4
+ import { useLogger } from '../../../logger.js';
5
5
  export default async function rolesCreate({ role: name, admin }) {
6
6
  const database = getDatabase();
7
+ const logger = useLogger();
7
8
  if (!name) {
8
9
  logger.error('Name is required');
9
10
  process.exit(1);
@@ -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);
@@ -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);
@@ -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,