@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
@@ -172,26 +172,27 @@ export class VersionsService extends ItemsService {
172
172
  knex: this.knex,
173
173
  schema: this.schema,
174
174
  });
175
+ const { item, collection } = version;
175
176
  const activity = await activityService.createOne({
176
177
  action: Action.VERSION_SAVE,
177
178
  user: this.accountability?.user ?? null,
178
- collection: version['collection'],
179
+ collection,
179
180
  ip: this.accountability?.ip ?? null,
180
181
  user_agent: this.accountability?.userAgent ?? null,
181
182
  origin: this.accountability?.origin ?? null,
182
- item: version['item'],
183
+ item,
183
184
  });
184
185
  const revisionDelta = await payloadService.prepareDelta(data);
185
186
  await revisionsService.createOne({
186
187
  activity,
187
188
  version: key,
188
- collection: version['collection'],
189
- item: version['item'],
189
+ collection,
190
+ item,
190
191
  data: revisionDelta,
191
192
  delta: revisionDelta,
192
193
  });
193
194
  const { cache } = getCache();
194
- if (shouldClearCache(cache, undefined, version['collection'])) {
195
+ if (shouldClearCache(cache, undefined, collection)) {
195
196
  cache.clear();
196
197
  }
197
198
  return data;
@@ -1,8 +1,8 @@
1
- import type { Messenger } from '../messenger.js';
1
+ import type { Bus } from '@directus/memory';
2
2
  import type { AbstractServiceOptions, Item, MutationOptions, PrimaryKey, Webhook } from '../types/index.js';
3
3
  import { ItemsService } from './items.js';
4
4
  export declare class WebhooksService extends ItemsService<Webhook> {
5
- messenger: Messenger;
5
+ messenger: Bus;
6
6
  constructor(options: AbstractServiceOptions);
7
7
  createOne(data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey>;
8
8
  createMany(data: Partial<Item>[], opts?: MutationOptions): Promise<PrimaryKey[]>;
@@ -1,10 +1,10 @@
1
- import { getMessenger } from '../messenger.js';
1
+ import { useBus } from '../bus/index.js';
2
2
  import { ItemsService } from './items.js';
3
3
  export class WebhooksService extends ItemsService {
4
4
  messenger;
5
5
  constructor(options) {
6
6
  super('directus_webhooks', options);
7
- this.messenger = getMessenger();
7
+ this.messenger = useBus();
8
8
  }
9
9
  async createOne(data, opts) {
10
10
  const result = await super.createOne(data, opts);
@@ -1,6 +1,6 @@
1
1
  import type { ActionHandler } from '@directus/types';
2
- import type { WebSocketClient } from '../websocket/types.js';
3
2
  import type { WebSocketMessage } from '../websocket/messages.js';
3
+ import type { WebSocketClient } from '../websocket/types.js';
4
4
  export declare class WebSocketService {
5
5
  private controller;
6
6
  constructor();
@@ -1,8 +1,9 @@
1
- import { getWebSocketController } from '../websocket/controllers/index.js';
1
+ import { useEnv } from '@directus/env';
2
2
  import { ServiceUnavailableError } from '@directus/errors';
3
- import { toBoolean } from '../utils/to-boolean.js';
3
+ import { toBoolean } from '@directus/utils';
4
4
  import emitter from '../emitter.js';
5
- import env from '../env.js';
5
+ import { getWebSocketController } from '../websocket/controllers/index.js';
6
+ const env = useEnv();
6
7
  export class WebSocketService {
7
8
  controller;
8
9
  constructor() {
@@ -1,6 +1,7 @@
1
- import env from '../env.js';
1
+ import { useEnv } from '@directus/env';
2
2
  import { getStorageDriver } from './get-storage-driver.js';
3
3
  export const registerDrivers = async (storage) => {
4
+ const env = useEnv();
4
5
  const usedDrivers = [];
5
6
  for (const [key, value] of Object.entries(env)) {
6
7
  if ((key.startsWith('STORAGE_') && key.endsWith('_DRIVER')) === false)
@@ -1,7 +1,8 @@
1
+ import { useEnv } from '@directus/env';
1
2
  import { toArray } from '@directus/utils';
2
- import env from '../env.js';
3
3
  import { getConfigFromEnv } from '../utils/get-config-from-env.js';
4
4
  export const registerLocations = async (storage) => {
5
+ const env = useEnv();
5
6
  const locations = toArray(env['STORAGE_LOCATIONS']);
6
7
  locations.forEach((location) => {
7
8
  location = location.trim();
@@ -1,10 +1,11 @@
1
+ import { useEnv } from '@directus/env';
1
2
  import { Redis } from 'ioredis';
2
- import env from './env.js';
3
3
  import { getConfigFromEnv } from './utils/get-config-from-env.js';
4
4
  let synchronizationManager;
5
5
  function getSynchronizationManager() {
6
6
  if (synchronizationManager)
7
7
  return synchronizationManager;
8
+ const env = useEnv();
8
9
  if (env['SYNCHRONIZATION_STORE'] === 'redis') {
9
10
  synchronizationManager = new SynchronizationManagerRedis();
10
11
  }
@@ -73,6 +74,7 @@ class SynchronizationManagerRedis {
73
74
  namespace;
74
75
  client;
75
76
  constructor() {
77
+ const env = useEnv();
76
78
  const config = getConfigFromEnv('REDIS');
77
79
  this.client = new Redis(env['REDIS'] ?? config);
78
80
  this.namespace = env['SYNCHRONIZATION_NAMESPACE'] ?? 'directus-sync';
@@ -1,6 +1,6 @@
1
+ import { useEnv } from '@directus/env';
1
2
  import { version } from 'directus/version';
2
3
  import { getDatabase, getDatabaseClient } from '../../database/index.js';
3
- import { useEnv } from '../../env.js';
4
4
  import { getItemCount } from '../utils/get-item-count.js';
5
5
  import { getUserCount } from '../utils/get-user-count.js';
6
6
  import { getUserItemCount } from '../utils/get-user-item-count.js';
@@ -1,7 +1,7 @@
1
+ import { useEnv } from '@directus/env';
2
+ import { toBoolean } from '@directus/utils';
1
3
  import { getCache } from '../../cache.js';
2
- import { useEnv } from '../../env.js';
3
4
  import { scheduleSynchronizedJob } from '../../utils/schedule.js';
4
- import { toBoolean } from '../../utils/to-boolean.js';
5
5
  import { track } from './track.js';
6
6
  /**
7
7
  * Exported to be able to test the anonymous callback function
@@ -1,5 +1,5 @@
1
+ import { useEnv } from '@directus/env';
1
2
  import { URL } from 'node:url';
2
- import { useEnv } from '../../env.js';
3
3
  /**
4
4
  * Post an anonymous usage report to the centralized intake server
5
5
  */
@@ -1,5 +1,5 @@
1
+ import { getNodeEnv } from '@directus/utils/node';
1
2
  import { setTimeout } from 'timers/promises';
2
- import { useEnv } from '../../env.js';
3
3
  import { useLogger } from '../../logger.js';
4
4
  import { getRandomWaitTime } from '../utils/get-random-wait-time.js';
5
5
  import { getReport } from './get-report.js';
@@ -12,7 +12,6 @@ import { sendReport } from './send-report.js';
12
12
  * @returns whether or not the tracking was successful
13
13
  */
14
14
  export const track = async (opts = { wait: true }) => {
15
- const env = useEnv();
16
15
  const logger = useLogger();
17
16
  if (opts.wait) {
18
17
  await setTimeout(getRandomWaitTime());
@@ -23,7 +22,7 @@ export const track = async (opts = { wait: true }) => {
23
22
  return true;
24
23
  }
25
24
  catch (err) {
26
- if (env['NODE_ENV'] === 'development') {
25
+ if (getNodeEnv() === 'development') {
27
26
  logger.error(err);
28
27
  }
29
28
  return false;
@@ -1,5 +1,5 @@
1
+ import { toBoolean } from '@directus/utils';
1
2
  import {} from 'knex';
2
- import { toBoolean } from '../../utils/to-boolean.js';
3
3
  export const getUserCount = async (db) => {
4
4
  const counts = {
5
5
  admin: 0,
@@ -12,6 +12,8 @@ export type TransformationParams = {
12
12
  transforms?: Transformation[];
13
13
  format?: TransformationFormat | 'auto';
14
14
  quality?: number;
15
+ focal_point_x?: number;
16
+ focal_point_y?: number;
15
17
  } & TransformationResize;
16
18
  export type TransformationSet = {
17
19
  transformationParams: TransformationParams;
@@ -4,12 +4,13 @@ import { flushCaches } from '../cache.js';
4
4
  import { getHelpers } from '../database/helpers/index.js';
5
5
  import getDatabase from '../database/index.js';
6
6
  import emitter from '../emitter.js';
7
- import logger from '../logger.js';
7
+ import { useLogger } from '../logger.js';
8
8
  import { CollectionsService } from '../services/collections.js';
9
9
  import { FieldsService } from '../services/fields.js';
10
10
  import { RelationsService } from '../services/relations.js';
11
11
  import { DiffKind } from '../types/index.js';
12
12
  import { getSchema } from './get-schema.js';
13
+ const logger = useLogger();
13
14
  export async function applyDiff(currentSnapshot, snapshotDiff, options) {
14
15
  const database = options?.database ?? getDatabase();
15
16
  const helpers = getHelpers(database);
@@ -408,9 +408,6 @@ export function applyFilter(knex, schema, rootQuery, rootFilter, collection, ali
408
408
  if (['integer', 'float', 'decimal'].includes(type)) {
409
409
  compareValue = Number(compareValue);
410
410
  }
411
- else if (type === 'bigInteger') {
412
- compareValue = BigInt(compareValue);
413
- }
414
411
  }
415
412
  // Cast filter value (compareValue) based on type of field being filtered against
416
413
  const [collection, field] = key.split('.');
@@ -433,14 +430,6 @@ export function applyFilter(knex, schema, rootQuery, rootFilter, collection, ali
433
430
  compareValue = Number(compareValue);
434
431
  }
435
432
  }
436
- if (type === 'bigInteger') {
437
- if (Array.isArray(compareValue)) {
438
- compareValue = compareValue.map((val) => BigInt(val));
439
- }
440
- else {
441
- compareValue = BigInt(compareValue);
442
- }
443
- }
444
433
  }
445
434
  if (operator === '_eq') {
446
435
  dbQuery[logical].where(selectionRaw, '=', compareValue);
@@ -1,7 +1,8 @@
1
1
  import { createRequire } from 'node:module';
2
- import logger from '../logger.js';
2
+ import { useLogger } from '../logger.js';
3
3
  const require = createRequire(import.meta.url);
4
4
  export function deleteFromRequireCache(modulePath) {
5
+ const logger = useLogger();
5
6
  try {
6
7
  const moduleCachePath = require.resolve(modulePath);
7
8
  delete require.cache[moduleCachePath];
@@ -1,9 +1,10 @@
1
- import getDatabase from '../database/index.js';
2
- import env from '../env.js';
1
+ import { useEnv } from '@directus/env';
3
2
  import { InvalidCredentialsError } from '@directus/errors';
3
+ import getDatabase from '../database/index.js';
4
4
  import isDirectusJWT from './is-directus-jwt.js';
5
5
  import { verifyAccessJWT } from './jwt.js';
6
6
  export async function getAccountabilityForToken(token, accountability) {
7
+ const env = useEnv();
7
8
  if (!accountability) {
8
9
  accountability = {
9
10
  user: null,
@@ -1,6 +1,7 @@
1
+ import { useEnv } from '@directus/env';
1
2
  import { toArray } from '@directus/utils';
2
- import env from '../env.js';
3
3
  export function getAuthProviders() {
4
+ const env = useEnv();
4
5
  return toArray(env['AUTH_PROVIDERS'])
5
6
  .filter((provider) => provider && env[`AUTH_${provider.toUpperCase()}_DRIVER`])
6
7
  .map((provider) => ({
@@ -1,4 +1,4 @@
1
- import env from '../env.js';
1
+ import { useEnv } from '@directus/env';
2
2
  import { shouldSkipCache } from './should-skip-cache.js';
3
3
  /**
4
4
  * Returns the Cache-Control header for the current request
@@ -9,6 +9,7 @@ import { shouldSkipCache } from './should-skip-cache.js';
9
9
  * @param personalized Whether requests depend on the authentication status of users
10
10
  */
11
11
  export function getCacheControlHeader(req, ttl, globalCacheSettings, personalized) {
12
+ const env = useEnv();
12
13
  // When the user explicitly asked to skip the cache
13
14
  if (shouldSkipCache(req))
14
15
  return 'no-store';
@@ -29,7 +30,9 @@ export function getCacheControlHeader(req, ttl, globalCacheSettings, personalize
29
30
  const ttlSeconds = Math.round(ttl / 1000);
30
31
  headerValues.push(`max-age=${ttlSeconds}`);
31
32
  // When the s-maxage flag should be included
32
- if (globalCacheSettings && Number.isInteger(env['CACHE_CONTROL_S_MAXAGE']) && env['CACHE_CONTROL_S_MAXAGE'] >= 0) {
33
+ if (globalCacheSettings &&
34
+ Number.isInteger(env['CACHE_CONTROL_S_MAXAGE']) &&
35
+ env['CACHE_CONTROL_S_MAXAGE'] >= 0) {
33
36
  headerValues.push(`s-maxage=${env['CACHE_CONTROL_S_MAXAGE']}`);
34
37
  }
35
38
  return headerValues.join(', ');
@@ -1,7 +1,8 @@
1
+ import { useEnv } from '@directus/env';
1
2
  import camelcase from 'camelcase';
2
3
  import { set } from 'lodash-es';
3
- import env from '../env.js';
4
4
  export function getConfigFromEnv(prefix, omitPrefix, type = 'camelcase') {
5
+ const env = useEnv();
5
6
  const config = {};
6
7
  for (const [key, value] of Object.entries(env)) {
7
8
  if (key.toLowerCase().startsWith(prefix.toLowerCase()) === false)
@@ -1,6 +1,6 @@
1
1
  import { parseJSON } from '@directus/utils';
2
- import env from '../env.js';
3
- import logger from '../logger.js';
2
+ import { getNodeEnv } from '@directus/utils/node';
3
+ import { useLogger } from '../logger.js';
4
4
  import getLocalType from './get-local-type.js';
5
5
  export default function getDefaultValue(column, field) {
6
6
  const type = getLocalType(column, field);
@@ -37,6 +37,7 @@ function castToBoolean(value) {
37
37
  return Boolean(value);
38
38
  }
39
39
  function castToObject(value) {
40
+ const logger = useLogger();
40
41
  if (!value)
41
42
  return value;
42
43
  if (typeof value === 'object')
@@ -46,7 +47,7 @@ function castToObject(value) {
46
47
  return parseJSON(value);
47
48
  }
48
49
  catch (err) {
49
- if (env['NODE_ENV'] === 'development') {
50
+ if (getNodeEnv() === 'development') {
50
51
  logger.error(err);
51
52
  }
52
53
  return value;
@@ -1,7 +1,9 @@
1
+ import { useEnv } from '@directus/env';
1
2
  import { isIP } from 'net';
2
- import env from '../env.js';
3
- import logger from '../logger.js';
3
+ import { useLogger } from '../logger.js';
4
4
  export function getIPFromReq(req) {
5
+ const env = useEnv();
6
+ const logger = useLogger();
5
7
  let ip = req.ip;
6
8
  if (env['IP_CUSTOM_HEADER']) {
7
9
  const customIPHeaderValue = req.get(env['IP_CUSTOM_HEADER']);
@@ -1,18 +1,20 @@
1
+ import { useEnv } from '@directus/env';
1
2
  import { deepMap, parseFilter, parseJSON, parsePreset } from '@directus/utils';
2
3
  import { cloneDeep } from 'lodash-es';
3
4
  import hash from 'object-hash';
4
5
  import { getCache, getCacheValue, getSystemCache, setCacheValue, setSystemCache } from '../cache.js';
5
6
  import getDatabase from '../database/index.js';
6
7
  import { appAccessMinimalPermissions } from '../database/system-data/app-access-permissions/index.js';
7
- import env from '../env.js';
8
- import logger from '../logger.js';
8
+ import { useLogger } from '../logger.js';
9
9
  import { RolesService } from '../services/roles.js';
10
10
  import { UsersService } from '../services/users.js';
11
- import { mergePermissions } from './merge-permissions.js';
12
11
  import { mergePermissionsForShare } from './merge-permissions-for-share.js';
12
+ import { mergePermissions } from './merge-permissions.js';
13
13
  export async function getPermissions(accountability, schema) {
14
14
  const database = getDatabase();
15
15
  const { cache } = getCache();
16
+ const env = useEnv();
17
+ const logger = useLogger();
16
18
  let permissions = [];
17
19
  const { user, role, app, admin, share_scope } = accountability;
18
20
  const cacheKey = `permissions-${hash({ user, role, app, admin, share_scope })}`;
@@ -1,3 +1,4 @@
1
+ import { useEnv } from '@directus/env';
1
2
  import { createInspector } from '@directus/schema';
2
3
  import { parseJSON, toArray } from '@directus/utils';
3
4
  import { mapValues } from 'lodash-es';
@@ -6,12 +7,13 @@ import { ALIAS_TYPES } from '../constants.js';
6
7
  import getDatabase from '../database/index.js';
7
8
  import { systemCollectionRows } from '../database/system-data/collections/index.js';
8
9
  import { systemFieldRows } from '../database/system-data/fields/index.js';
9
- import env from '../env.js';
10
- import logger from '../logger.js';
10
+ import { useLogger } from '../logger.js';
11
11
  import { RelationsService } from '../services/relations.js';
12
12
  import getDefaultValue from './get-default-value.js';
13
13
  import getLocalType from './get-local-type.js';
14
+ const logger = useLogger();
14
15
  export async function getSchema(options) {
16
+ const env = useEnv();
15
17
  const database = options?.database || getDatabase();
16
18
  const schemaInspector = createInspector(database);
17
19
  let result;
@@ -42,6 +44,7 @@ export async function getSchema(options) {
42
44
  return result;
43
45
  }
44
46
  async function getDatabaseSchema(database, schemaInspector) {
47
+ const env = useEnv();
45
48
  const result = {
46
49
  collections: {},
47
50
  relations: [],
@@ -1,10 +1,9 @@
1
1
  import deepDiff from 'deep-diff';
2
- import { orderBy } from 'lodash-es';
3
2
  import { DiffKind } from '../types/index.js';
4
3
  import { sanitizeCollection, sanitizeField, sanitizeRelation } from './sanitize-schema.js';
5
4
  export function getSnapshotDiff(current, after) {
6
5
  const diffedSnapshot = {
7
- collections: orderBy([
6
+ collections: [
8
7
  ...current.collections.map((currentCollection) => {
9
8
  const afterCollection = after.collections.find((afterCollection) => afterCollection.collection === currentCollection.collection);
10
9
  return {
@@ -21,8 +20,8 @@ export function getSnapshotDiff(current, after) {
21
20
  collection: afterCollection.collection,
22
21
  diff: deepDiff.diff(undefined, sanitizeCollection(afterCollection)),
23
22
  })),
24
- ].filter((obj) => Array.isArray(obj.diff)), 'collection'),
25
- fields: orderBy([
23
+ ].filter((obj) => Array.isArray(obj.diff)),
24
+ fields: [
26
25
  ...current.fields.map((currentField) => {
27
26
  const afterField = after.fields.find((afterField) => afterField.collection === currentField.collection && afterField.field === currentField.field);
28
27
  const isAutoIncrementPrimaryKey = !!currentField.schema?.is_primary_key && !!currentField.schema?.has_auto_increment;
@@ -42,8 +41,8 @@ export function getSnapshotDiff(current, after) {
42
41
  field: afterField.field,
43
42
  diff: deepDiff.diff(undefined, sanitizeField(afterField)),
44
43
  })),
45
- ].filter((obj) => Array.isArray(obj.diff)), ['collection']),
46
- relations: orderBy([
44
+ ].filter((obj) => Array.isArray(obj.diff)),
45
+ relations: [
47
46
  ...current.relations.map((currentRelation) => {
48
47
  const afterRelation = after.relations.find((afterRelation) => afterRelation.collection === currentRelation.collection && afterRelation.field === currentRelation.field);
49
48
  return {
@@ -55,8 +54,7 @@ export function getSnapshotDiff(current, after) {
55
54
  }),
56
55
  ...after.relations
57
56
  .filter((afterRelation) => {
58
- const currentRelation = current.relations.find((currentRelation) => currentRelation.collection === afterRelation.collection &&
59
- afterRelation.field === currentRelation.field);
57
+ const currentRelation = current.relations.find((currentRelation) => currentRelation.collection === afterRelation.collection && afterRelation.field === currentRelation.field);
60
58
  return !!currentRelation === false;
61
59
  })
62
60
  .map((afterRelation) => ({
@@ -65,7 +63,7 @@ export function getSnapshotDiff(current, after) {
65
63
  related_collection: afterRelation.related_collection,
66
64
  diff: deepDiff.diff(undefined, sanitizeRelation(afterRelation)),
67
65
  })),
68
- ].filter((obj) => Array.isArray(obj.diff)), ['collection']),
66
+ ].filter((obj) => Array.isArray(obj.diff)),
69
67
  };
70
68
  /**
71
69
  * When you delete a collection, we don't have to individually drop all the fields/relations as well
@@ -19,11 +19,11 @@ export async function getSnapshot(options) {
19
19
  relationsService.readAll(),
20
20
  ]);
21
21
  const collectionsFiltered = collectionsRaw.filter((item) => excludeSystem(item));
22
- const fieldsFiltered = fieldsRaw.filter((item) => excludeSystem(item)).map(omitID);
23
- const relationsFiltered = relationsRaw.filter((item) => excludeSystem(item)).map(omitID);
22
+ const fieldsFiltered = fieldsRaw.filter((item) => excludeSystem(item));
23
+ const relationsFiltered = relationsRaw.filter((item) => excludeSystem(item));
24
24
  const collectionsSorted = sortBy(mapValues(collectionsFiltered, sortDeep), ['collection']);
25
- const fieldsSorted = sortBy(mapValues(fieldsFiltered, sortDeep), ['collection', 'field']);
26
- const relationsSorted = sortBy(mapValues(relationsFiltered, sortDeep), ['collection', 'field']);
25
+ const fieldsSorted = sortBy(mapValues(fieldsFiltered, sortDeep), ['collection', 'meta.id']).map(omitID);
26
+ const relationsSorted = sortBy(mapValues(relationsFiltered, sortDeep), ['collection', 'meta.id']).map(omitID);
27
27
  return {
28
28
  version: 1,
29
29
  directus: version,
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Checks if an IP address is contained in a list of networks
3
+ * @param networks List of IP addresses (192.168.0.1), CIDR notations (192.168.0.0/24) or IP ranges (192-168.0.0-192.168.2.0)
4
+ * @throws Will throw if list contains invalid network definitions
5
+ */
6
+ export declare function ipInNetworks(ip: string, networks: string[]): boolean;
@@ -0,0 +1,13 @@
1
+ import { matches } from 'ip-matching';
2
+ /**
3
+ * Checks if an IP address is contained in a list of networks
4
+ * @param networks List of IP addresses (192.168.0.1), CIDR notations (192.168.0.0/24) or IP ranges (192-168.0.0-192.168.2.0)
5
+ * @throws Will throw if list contains invalid network definitions
6
+ */
7
+ export function ipInNetworks(ip, networks) {
8
+ for (const allowedIp of networks) {
9
+ if (matches(ip, allowedIp))
10
+ return true;
11
+ }
12
+ return false;
13
+ }
@@ -1,10 +1,11 @@
1
1
  import { toArray } from '@directus/utils';
2
- import logger from '../logger.js';
3
2
  import { URL } from 'url';
3
+ import { useLogger } from '../logger.js';
4
4
  /**
5
5
  * Check if url matches allow list either exactly or by domain+path
6
6
  */
7
7
  export default function isUrlAllowed(url, allowList) {
8
+ const logger = useLogger();
8
9
  const urlAllowList = toArray(allowList);
9
10
  if (urlAllowList.includes(url))
10
11
  return true;
@@ -5,4 +5,5 @@ export declare class JobQueue {
5
5
  constructor();
6
6
  enqueue(job: PromiseCallback): void;
7
7
  private run;
8
+ get size(): number;
8
9
  }
@@ -19,4 +19,7 @@ export class JobQueue {
19
19
  }
20
20
  this.running = false;
21
21
  }
22
+ get size() {
23
+ return this.jobs.length;
24
+ }
22
25
  }
@@ -1,9 +1,10 @@
1
+ import { useEnv } from '@directus/env';
1
2
  import { parseFilter, parseJSON } from '@directus/utils';
2
3
  import { flatten, get, isPlainObject, merge, set } from 'lodash-es';
3
- import env from '../env.js';
4
- import logger from '../logger.js';
4
+ import { useLogger } from '../logger.js';
5
5
  import { Meta } from '../types/index.js';
6
6
  export function sanitizeQuery(rawQuery, accountability) {
7
+ const env = useEnv();
7
8
  const query = {};
8
9
  const hasMaxLimit = 'QUERY_LIMIT_MAX' in env &&
9
10
  Number(env['QUERY_LIMIT_MAX']) >= 0 &&
@@ -84,6 +85,7 @@ function sanitizeSort(rawSort) {
84
85
  return fields;
85
86
  }
86
87
  function sanitizeAggregate(rawAggregate) {
88
+ const logger = useLogger();
87
89
  let aggregate = rawAggregate;
88
90
  if (typeof rawAggregate === 'string') {
89
91
  try {
@@ -102,6 +104,7 @@ function sanitizeAggregate(rawAggregate) {
102
104
  return aggregate;
103
105
  }
104
106
  function sanitizeFilter(rawFilter, accountability) {
107
+ const logger = useLogger();
105
108
  let filters = rawFilter;
106
109
  if (typeof rawFilter === 'string') {
107
110
  try {
@@ -137,6 +140,7 @@ function sanitizeMeta(rawMeta) {
137
140
  return [rawMeta];
138
141
  }
139
142
  function sanitizeDeep(deep, accountability) {
143
+ const logger = useLogger();
140
144
  const result = {};
141
145
  if (typeof deep === 'string') {
142
146
  try {
@@ -175,6 +179,7 @@ function sanitizeDeep(deep, accountability) {
175
179
  }
176
180
  }
177
181
  function sanitizeAlias(rawAlias) {
182
+ const logger = useLogger();
178
183
  let alias = rawAlias;
179
184
  if (typeof rawAlias === 'string') {
180
185
  try {
@@ -16,7 +16,7 @@ export declare function sanitizeCollection(collection: Collection | undefined):
16
16
  * @returns sanitized field
17
17
  */
18
18
  export declare function sanitizeField(field: Field | undefined, sanitizeAllSchema?: boolean): Partial<Field> | undefined;
19
- export declare function sanitizeColumn(column: Column): Pick<Column, "table" | "name" | "numeric_precision" | "numeric_scale" | "data_type" | "default_value" | "max_length" | "is_nullable" | "is_unique" | "is_primary_key" | "is_generated" | "generation_expression" | "has_auto_increment" | "foreign_key_table" | "foreign_key_column">;
19
+ export declare function sanitizeColumn(column: Column): Pick<Column, "name" | "table" | "numeric_precision" | "numeric_scale" | "data_type" | "default_value" | "max_length" | "is_nullable" | "is_unique" | "is_primary_key" | "is_generated" | "generation_expression" | "has_auto_increment" | "foreign_key_table" | "foreign_key_column">;
20
20
  /**
21
21
  * Pick certain database vendor specific relation properties that should be compared when performing diff
22
22
  *
@@ -1,4 +1,4 @@
1
- import env from '../env.js';
1
+ import { useEnv } from '@directus/env';
2
2
  /**
3
3
  * Check whether cache should be cleared
4
4
  *
@@ -7,6 +7,7 @@ import env from '../env.js';
7
7
  * @param collection Collection name to check if cache purging should be ignored
8
8
  */
9
9
  export function shouldClearCache(cache, opts, collection) {
10
+ const env = useEnv();
10
11
  if (env['CACHE_AUTO_PURGE']) {
11
12
  if (collection && env['CACHE_AUTO_PURGE_IGNORE_LIST'].includes(collection)) {
12
13
  return false;
@@ -1,6 +1,6 @@
1
+ import { useEnv } from '@directus/env';
1
2
  import { getEndpoint } from '@directus/utils';
2
3
  import url from 'url';
3
- import env from '../env.js';
4
4
  import { Url } from './url.js';
5
5
  /**
6
6
  * Whether to skip caching for the current request
@@ -8,6 +8,7 @@ import { Url } from './url.js';
8
8
  * @param req Express request object
9
9
  */
10
10
  export function shouldSkipCache(req) {
11
+ const env = useEnv();
11
12
  // Always skip cache for requests coming from the data studio based on Referer header
12
13
  const referer = req.get('Referer');
13
14
  if (referer) {