@directus/api 31.0.0 → 32.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 (135) hide show
  1. package/dist/app.js +2 -0
  2. package/dist/auth/auth.d.ts +2 -1
  3. package/dist/auth/auth.js +7 -2
  4. package/dist/auth/drivers/ldap.d.ts +0 -2
  5. package/dist/auth/drivers/ldap.js +9 -7
  6. package/dist/auth/drivers/oauth2.d.ts +0 -2
  7. package/dist/auth/drivers/oauth2.js +11 -8
  8. package/dist/auth/drivers/openid.d.ts +0 -2
  9. package/dist/auth/drivers/openid.js +11 -8
  10. package/dist/auth/drivers/saml.d.ts +0 -2
  11. package/dist/auth/drivers/saml.js +5 -5
  12. package/dist/auth.js +1 -2
  13. package/dist/cli/commands/bootstrap/index.js +12 -33
  14. package/dist/cli/commands/init/index.js +1 -1
  15. package/dist/cli/commands/schema/apply.d.ts +4 -0
  16. package/dist/cli/commands/schema/apply.js +26 -3
  17. package/dist/controllers/collections.js +7 -2
  18. package/dist/controllers/fields.js +31 -8
  19. package/dist/controllers/server.js +26 -1
  20. package/dist/controllers/settings.js +9 -2
  21. package/dist/controllers/users.js +2 -2
  22. package/dist/database/helpers/fn/types.js +3 -3
  23. package/dist/database/helpers/schema/dialects/cockroachdb.d.ts +2 -1
  24. package/dist/database/helpers/schema/dialects/cockroachdb.js +13 -0
  25. package/dist/database/helpers/schema/dialects/mssql.d.ts +2 -1
  26. package/dist/database/helpers/schema/dialects/mssql.js +23 -0
  27. package/dist/database/helpers/schema/dialects/mysql.d.ts +2 -1
  28. package/dist/database/helpers/schema/dialects/mysql.js +25 -0
  29. package/dist/database/helpers/schema/dialects/oracle.d.ts +2 -1
  30. package/dist/database/helpers/schema/dialects/oracle.js +13 -0
  31. package/dist/database/helpers/schema/dialects/postgres.d.ts +2 -1
  32. package/dist/database/helpers/schema/dialects/postgres.js +13 -0
  33. package/dist/database/helpers/schema/types.d.ts +5 -0
  34. package/dist/database/helpers/schema/types.js +6 -0
  35. package/dist/database/migrations/20251012A-add-field-searchable.d.ts +3 -0
  36. package/dist/database/migrations/20251012A-add-field-searchable.js +10 -0
  37. package/dist/database/migrations/20251014A-add-project-owner.d.ts +3 -0
  38. package/dist/database/migrations/20251014A-add-project-owner.js +37 -0
  39. package/dist/database/migrations/20251028A-add-retention-indexes.d.ts +3 -0
  40. package/dist/database/migrations/20251028A-add-retention-indexes.js +42 -0
  41. package/dist/database/run-ast/lib/apply-query/add-join.js +2 -2
  42. package/dist/database/run-ast/lib/apply-query/filter/get-filter-type.d.ts +2 -2
  43. package/dist/database/run-ast/lib/apply-query/index.d.ts +0 -1
  44. package/dist/database/run-ast/lib/apply-query/index.js +4 -6
  45. package/dist/database/run-ast/lib/apply-query/search.js +2 -0
  46. package/dist/database/run-ast/lib/get-db-query.js +7 -6
  47. package/dist/database/run-ast/utils/generate-alias.d.ts +6 -0
  48. package/dist/database/run-ast/utils/generate-alias.js +57 -0
  49. package/dist/flows.js +1 -0
  50. package/dist/mcp/schema.d.ts +14 -14
  51. package/dist/mcp/schema.js +6 -6
  52. package/dist/mcp/server.d.ts +9 -3
  53. package/dist/mcp/server.js +1 -1
  54. package/dist/mcp/tools/collections.d.ts +1 -1
  55. package/dist/mcp/tools/fields.d.ts +1 -1
  56. package/dist/mcp/tools/files.d.ts +25 -25
  57. package/dist/mcp/tools/flows.d.ts +36 -36
  58. package/dist/mcp/tools/folders.d.ts +18 -18
  59. package/dist/mcp/tools/items.d.ts +18 -18
  60. package/dist/mcp/tools/operations.d.ts +19 -19
  61. package/dist/mcp/tools/prompts/items.md +1 -1
  62. package/dist/metrics/lib/create-metrics.js +16 -25
  63. package/dist/middleware/collection-exists.js +2 -2
  64. package/dist/operations/mail/index.js +3 -1
  65. package/dist/operations/mail/rate-limiter.d.ts +1 -0
  66. package/dist/operations/mail/rate-limiter.js +29 -0
  67. package/dist/permissions/modules/process-payload/process-payload.js +3 -10
  68. package/dist/permissions/modules/validate-access/validate-access.js +2 -3
  69. package/dist/schedules/metrics.js +6 -2
  70. package/dist/schedules/project.d.ts +4 -0
  71. package/dist/schedules/project.js +27 -0
  72. package/dist/services/collections.d.ts +3 -3
  73. package/dist/services/collections.js +16 -1
  74. package/dist/services/fields.d.ts +21 -5
  75. package/dist/services/fields.js +105 -28
  76. package/dist/services/graphql/resolvers/query.js +1 -1
  77. package/dist/services/graphql/resolvers/system-admin.js +49 -5
  78. package/dist/services/graphql/schema/parse-query.js +8 -8
  79. package/dist/services/graphql/utils/aggregate-query.d.ts +1 -1
  80. package/dist/services/graphql/utils/aggregate-query.js +5 -1
  81. package/dist/services/graphql/utils/filter-replace-m2a.js +2 -1
  82. package/dist/services/import-export.d.ts +9 -1
  83. package/dist/services/import-export.js +287 -101
  84. package/dist/services/items.d.ts +1 -1
  85. package/dist/services/items.js +36 -20
  86. package/dist/services/mail/index.js +2 -0
  87. package/dist/services/mail/rate-limiter.d.ts +1 -0
  88. package/dist/services/mail/rate-limiter.js +29 -0
  89. package/dist/services/meta.js +28 -24
  90. package/dist/services/schema.js +4 -1
  91. package/dist/services/server.d.ts +1 -0
  92. package/dist/services/server.js +14 -18
  93. package/dist/services/settings.d.ts +2 -1
  94. package/dist/services/settings.js +15 -0
  95. package/dist/services/tus/server.js +14 -9
  96. package/dist/telemetry/lib/get-report.js +4 -4
  97. package/dist/telemetry/lib/send-report.d.ts +6 -1
  98. package/dist/telemetry/lib/send-report.js +3 -1
  99. package/dist/telemetry/types/report.d.ts +17 -1
  100. package/dist/telemetry/utils/get-settings.d.ts +9 -0
  101. package/dist/telemetry/utils/get-settings.js +14 -0
  102. package/dist/test-utils/README.md +760 -0
  103. package/dist/test-utils/cache.d.ts +51 -0
  104. package/dist/test-utils/cache.js +59 -0
  105. package/dist/test-utils/database.d.ts +48 -0
  106. package/dist/test-utils/database.js +52 -0
  107. package/dist/test-utils/emitter.d.ts +35 -0
  108. package/dist/test-utils/emitter.js +38 -0
  109. package/dist/test-utils/fields-service.d.ts +28 -0
  110. package/dist/test-utils/fields-service.js +36 -0
  111. package/dist/test-utils/items-service.d.ts +23 -0
  112. package/dist/test-utils/items-service.js +37 -0
  113. package/dist/test-utils/knex.d.ts +164 -0
  114. package/dist/test-utils/knex.js +268 -0
  115. package/dist/test-utils/schema.d.ts +26 -0
  116. package/dist/test-utils/schema.js +35 -0
  117. package/dist/types/auth.d.ts +0 -2
  118. package/dist/utils/apply-diff.js +15 -0
  119. package/dist/utils/create-admin.d.ts +11 -0
  120. package/dist/utils/create-admin.js +50 -0
  121. package/dist/utils/get-schema.js +5 -3
  122. package/dist/utils/get-snapshot-diff.js +49 -5
  123. package/dist/utils/get-snapshot.js +13 -7
  124. package/dist/utils/sanitize-schema.d.ts +11 -4
  125. package/dist/utils/sanitize-schema.js +9 -6
  126. package/dist/utils/schedule.js +15 -19
  127. package/dist/utils/validate-diff.js +31 -0
  128. package/dist/utils/validate-snapshot.js +7 -0
  129. package/dist/websocket/controllers/hooks.js +12 -20
  130. package/dist/websocket/messages.d.ts +3 -3
  131. package/package.json +63 -65
  132. package/dist/cli/utils/defaults.d.ts +0 -4
  133. package/dist/cli/utils/defaults.js +0 -17
  134. package/dist/telemetry/utils/get-project-id.d.ts +0 -2
  135. package/dist/telemetry/utils/get-project-id.js +0 -4
@@ -1,8 +1,10 @@
1
1
  import { isArray } from 'lodash-es';
2
- import { getAstFromQuery } from '../database/get-ast-from-query/get-ast-from-query.js';
3
2
  import getDatabase from '../database/index.js';
4
- import { runAst } from '../database/run-ast/run-ast.js';
5
- import { processAst } from '../permissions/modules/process-ast/process-ast.js';
3
+ import applyQuery from '../database/run-ast/lib/apply-query/index.js';
4
+ import { fetchPermissions } from '../permissions/lib/fetch-permissions.js';
5
+ import { fetchPolicies } from '../permissions/lib/fetch-policies.js';
6
+ import { getCases } from '../permissions/modules/process-ast/lib/get-cases.js';
7
+ import { validateAccess } from '../permissions/modules/validate-access/validate-access.js';
6
8
  export class MetaService {
7
9
  knex;
8
10
  accountability;
@@ -33,27 +35,29 @@ export class MetaService {
33
35
  return this.filterCount(collection, {});
34
36
  }
35
37
  async filterCount(collection, query) {
36
- const primaryKeyName = this.schema.collections[collection].primary;
37
- const aggregateQuery = {
38
- aggregate: {
39
- countDistinct: [primaryKeyName],
40
- },
41
- search: query.search ?? null,
38
+ let permissions = [];
39
+ if (this.accountability && this.accountability.admin !== true) {
40
+ const context = { knex: this.knex, schema: this.schema };
41
+ await validateAccess({
42
+ accountability: this.accountability,
43
+ action: 'read',
44
+ collection,
45
+ }, context);
46
+ const policies = await fetchPolicies(this.accountability, context);
47
+ permissions = await fetchPermissions({ action: 'read', accountability: this.accountability, policies }, context);
48
+ }
49
+ const { cases } = getCases(collection, permissions, []);
50
+ const { query: dbQuery, hasJoins } = applyQuery(this.knex, collection, this.knex(collection), {
42
51
  filter: query.filter ?? null,
43
- };
44
- let ast = await getAstFromQuery({
45
- collection,
46
- query: aggregateQuery,
47
- accountability: this.accountability,
48
- }, {
49
- schema: this.schema,
50
- knex: this.knex,
51
- });
52
- ast = await processAst({ ast, action: 'read', accountability: this.accountability }, { knex: this.knex, schema: this.schema });
53
- const records = await runAst(ast, this.schema, this.accountability, {
54
- knex: this.knex,
55
- });
56
- return Number((isArray(records) ? records[0]?.['countDistinct'][primaryKeyName] : records?.['countDistinct'][primaryKeyName]) ??
57
- 0);
52
+ search: query.search ?? null,
53
+ }, this.schema, cases, permissions);
54
+ if (hasJoins) {
55
+ dbQuery.countDistinct({ count: [`${collection}.${this.schema.collections[collection].primary}`] });
56
+ }
57
+ else {
58
+ dbQuery.count('*', { as: 'count' });
59
+ }
60
+ const records = await dbQuery;
61
+ return Number((isArray(records) ? records[0]?.['count'] : records?.['count']) ?? 0);
58
62
  }
59
63
  }
@@ -34,7 +34,10 @@ export class SchemaService {
34
34
  validateSnapshot(snapshot, options?.force);
35
35
  const currentSnapshot = options?.currentSnapshot ?? (await getSnapshot({ database: this.knex }));
36
36
  const diff = getSnapshotDiff(currentSnapshot, snapshot);
37
- if (diff.collections.length === 0 && diff.fields.length === 0 && diff.relations.length === 0) {
37
+ if (diff.collections.length === 0 &&
38
+ diff.fields.length === 0 &&
39
+ diff.relations.length === 0 &&
40
+ (!diff.systemFields || diff.systemFields.length === 0)) {
38
41
  return null;
39
42
  }
40
43
  return diff;
@@ -7,6 +7,7 @@ export declare class ServerService {
7
7
  settingsService: SettingsService;
8
8
  schema: SchemaOverview;
9
9
  constructor(options: AbstractServiceOptions);
10
+ isSetupCompleted(): Promise<boolean>;
10
11
  serverInfo(): Promise<Record<string, any>>;
11
12
  health(): Promise<Record<string, any>>;
12
13
  }
@@ -28,8 +28,12 @@ export class ServerService {
28
28
  this.schema = options.schema;
29
29
  this.settingsService = new SettingsService({ knex: this.knex, schema: this.schema });
30
30
  }
31
+ async isSetupCompleted() {
32
+ return Boolean(await this.knex('directus_users').first());
33
+ }
31
34
  async serverInfo() {
32
35
  const info = {};
36
+ const setupComplete = await this.isSetupCompleted();
33
37
  const projectInfo = await this.settingsService.readSingleton({
34
38
  fields: [
35
39
  'project_name',
@@ -54,6 +58,7 @@ export class ServerService {
54
58
  });
55
59
  info['project'] = projectInfo;
56
60
  info['mcp_enabled'] = toBoolean(env['MCP_ENABLED'] ?? true);
61
+ info['setupCompleted'] = setupComplete;
57
62
  if (this.accountability?.user) {
58
63
  if (env['RATE_LIMITER_ENABLED']) {
59
64
  info['rateLimit'] = {
@@ -112,8 +117,9 @@ export class ServerService {
112
117
  chunkSize: RESUMABLE_UPLOADS.CHUNK_SIZE,
113
118
  };
114
119
  }
115
- info['version'] = version;
116
120
  }
121
+ if (this.accountability?.user || !setupComplete)
122
+ info['version'] = version;
117
123
  return info;
118
124
  }
119
125
  async health() {
@@ -222,8 +228,8 @@ export class ServerService {
222
228
  };
223
229
  const startTime = performance.now();
224
230
  try {
225
- await cache.set(`health-${checkID}`, true, 5);
226
- await cache.delete(`health-${checkID}`);
231
+ await cache.set(`directus-health-${checkID}`, true, 5);
232
+ await cache.delete(`directus-health-${checkID}`);
227
233
  }
228
234
  catch (err) {
229
235
  checks['cache:responseTime'][0].status = 'error';
@@ -256,8 +262,8 @@ export class ServerService {
256
262
  };
257
263
  const startTime = performance.now();
258
264
  try {
259
- await rateLimiter.consume(`health-${checkID}`, 1);
260
- await rateLimiter.delete(`health-${checkID}`);
265
+ await rateLimiter.consume(`directus-health-${checkID}`, 1);
266
+ await rateLimiter.delete(`directus-health-${checkID}`);
261
267
  }
262
268
  catch (err) {
263
269
  checks['rateLimiter:responseTime'][0].status = 'error';
@@ -292,8 +298,8 @@ export class ServerService {
292
298
  };
293
299
  const startTime = performance.now();
294
300
  try {
295
- await rateLimiterGlobal.consume(`health-${checkID}`, 1);
296
- await rateLimiterGlobal.delete(`health-${checkID}`);
301
+ await rateLimiterGlobal.consume(`directus-health-${checkID}`, 1);
302
+ await rateLimiterGlobal.delete(`directus-health-${checkID}`);
297
303
  }
298
304
  catch (err) {
299
305
  checks['rateLimiterGlobal:responseTime'][0].status = 'error';
@@ -327,17 +333,7 @@ export class ServerService {
327
333
  ];
328
334
  const startTime = performance.now();
329
335
  try {
330
- await disk.write(`health-${checkID}`, Readable.from(['check']));
331
- const fileStream = await disk.read(`health-${checkID}`);
332
- fileStream.on('data', async () => {
333
- try {
334
- fileStream.destroy();
335
- await disk.delete(`health-${checkID}`);
336
- }
337
- catch (error) {
338
- logger.error(error);
339
- }
340
- });
336
+ await disk.write('directus-health-file', Readable.from([checkID]));
341
337
  }
342
338
  catch (err) {
343
339
  checks[`storage:${location}:responseTime`][0].status = 'error';
@@ -1,5 +1,6 @@
1
- import type { AbstractServiceOptions } from '@directus/types';
1
+ import type { AbstractServiceOptions, OwnerInformation } from '@directus/types';
2
2
  import { ItemsService } from './items.js';
3
3
  export declare class SettingsService extends ItemsService {
4
4
  constructor(options: AbstractServiceOptions);
5
+ setOwner(data: OwnerInformation): Promise<import("@directus/types").PrimaryKey>;
5
6
  }
@@ -1,6 +1,21 @@
1
1
  import { ItemsService } from './items.js';
2
+ import { sendReport } from '../telemetry/index.js';
3
+ import { version } from 'directus/version';
2
4
  export class SettingsService extends ItemsService {
3
5
  constructor(options) {
4
6
  super('directus_settings', options);
5
7
  }
8
+ async setOwner(data) {
9
+ const { project_id } = await this.knex.select('project_id').from('directus_settings').first();
10
+ sendReport({ ...data, project_id, version }).catch(async () => {
11
+ await this.knex.update('project_status', 'pending').from('directus_settings');
12
+ });
13
+ return await this.upsertSingleton({
14
+ project_owner: data.project_owner,
15
+ project_usage: data.project_usage,
16
+ org_name: data.org_name,
17
+ product_updates: data.product_updates,
18
+ project_status: null,
19
+ });
20
+ }
6
21
  }
@@ -6,7 +6,7 @@
6
6
  import { useEnv } from '@directus/env';
7
7
  import { supportsTus } from '@directus/storage';
8
8
  import { toArray } from '@directus/utils';
9
- import { Server, EVENTS } from '@tus/server';
9
+ import { Server } from '@tus/server';
10
10
  import { RESUMABLE_UPLOADS } from '../../constants.js';
11
11
  import { getStorage } from '../../storage/index.js';
12
12
  import { extractMetadata } from '../files/lib/extract-metadata.js';
@@ -16,6 +16,7 @@ import { getTusLocker } from './lockers.js';
16
16
  import { pick } from 'lodash-es';
17
17
  import emitter from '../../emitter.js';
18
18
  import getDatabase from '../../database/index.js';
19
+ import { getSchema } from '../../utils/get-schema.js';
19
20
  async function createTusStore(context) {
20
21
  const env = useEnv();
21
22
  const storage = await getStorage();
@@ -40,16 +41,17 @@ export async function createTusServer(context) {
40
41
  datastore: store,
41
42
  locker: getTusLocker(),
42
43
  ...(RESUMABLE_UPLOADS.MAX_SIZE !== null && { maxSize: RESUMABLE_UPLOADS.MAX_SIZE }),
43
- async onUploadFinish(req, res, upload) {
44
+ async onUploadFinish(req, upload) {
45
+ const schema = await getSchema();
44
46
  const service = new ItemsService('directus_files', {
45
- schema: req.schema,
47
+ schema,
46
48
  });
47
49
  const file = (await service.readByQuery({
48
50
  filter: { tus_id: { _eq: upload.id } },
49
51
  limit: 1,
50
52
  }))[0];
51
53
  if (!file)
52
- return res;
54
+ return {};
53
55
  let fileData;
54
56
  // update metadata when file is replaced
55
57
  if (file.tus_data?.['metadata']?.['replace_id']) {
@@ -91,19 +93,22 @@ export async function createTusServer(context) {
91
93
  collection: 'directus_files',
92
94
  }, {
93
95
  database: getDatabase(),
94
- schema: req.schema,
96
+ schema,
95
97
  accountability: req.accountability,
96
98
  });
97
- return res;
99
+ return {
100
+ headers: {
101
+ 'Directus-File-Id': upload.metadata['id'],
102
+ },
103
+ };
98
104
  },
99
105
  generateUrl(_req, opts) {
100
106
  return env['PUBLIC_URL'] + '/files/tus/' + opts.id;
101
107
  },
108
+ allowedHeaders: env['CORS_ALLOWED_HEADERS'],
109
+ exposedHeaders: env['CORS_EXPOSED_HEADERS'],
102
110
  relativeLocation: String(env['PUBLIC_URL']).startsWith('http'),
103
111
  });
104
- server.on(EVENTS.POST_CREATE, async (_req, res, upload) => {
105
- res.setHeader('Directus-File-Id', upload.metadata['id']);
106
- });
107
112
  return [server, cleanup];
108
113
  function cleanup() {
109
114
  server.removeAllListeners();
@@ -7,8 +7,8 @@ import { getExtensionCount } from '../utils/get-extension-count.js';
7
7
  import { getFieldCount } from '../utils/get-field-count.js';
8
8
  import { getFilesizeSum } from '../utils/get-filesize-sum.js';
9
9
  import { getItemCount } from '../utils/get-item-count.js';
10
+ import { getSettings } from '../utils/get-settings.js';
10
11
  import { getUserItemCount } from '../utils/get-user-item-count.js';
11
- import { getProjectId } from '../utils/get-project-id.js';
12
12
  const basicCountTasks = [
13
13
  { collection: 'directus_dashboards' },
14
14
  { collection: 'directus_files' },
@@ -26,7 +26,7 @@ export const getReport = async () => {
26
26
  const db = getDatabase();
27
27
  const env = useEnv();
28
28
  const helpers = getHelpers(db);
29
- const [basicCounts, userCounts, userItemCount, fieldsCounts, extensionsCounts, databaseSize, filesizes, projectId] = await Promise.all([
29
+ const [basicCounts, userCounts, userItemCount, fieldsCounts, extensionsCounts, databaseSize, filesizes, settings] = await Promise.all([
30
30
  getItemCount(db, basicCountTasks),
31
31
  fetchUserCount({ knex: db }),
32
32
  getUserItemCount(db),
@@ -34,7 +34,7 @@ export const getReport = async () => {
34
34
  getExtensionCount(db),
35
35
  helpers.schema.getDatabaseSize(),
36
36
  getFilesizeSum(db),
37
- getProjectId(db),
37
+ getSettings(db),
38
38
  ]);
39
39
  return {
40
40
  url: env['PUBLIC_URL'],
@@ -55,6 +55,6 @@ export const getReport = async () => {
55
55
  extensions: extensionsCounts.totalEnabled,
56
56
  database_size: databaseSize ?? 0,
57
57
  files_size_total: filesizes.total,
58
- project_id: projectId,
58
+ ...settings,
59
59
  };
60
60
  };
@@ -1,5 +1,10 @@
1
1
  import type { TelemetryReport } from '../types/report.js';
2
+ import type { OwnerInformation } from '@directus/types';
3
+ export type OwnerReport = OwnerInformation & {
4
+ project_id: string;
5
+ version: string;
6
+ };
2
7
  /**
3
8
  * Post an anonymous usage report to the centralized intake server
4
9
  */
5
- export declare const sendReport: (report: TelemetryReport) => Promise<void>;
10
+ export declare const sendReport: (report: TelemetryReport | OwnerReport) => Promise<void>;
@@ -5,7 +5,9 @@ import { URL } from 'node:url';
5
5
  */
6
6
  export const sendReport = async (report) => {
7
7
  const env = useEnv();
8
- const url = new URL('/v1/metrics', env['TELEMETRY_URL']);
8
+ const url = 'project_owner' in report
9
+ ? new URL('/v1/owner', String(env['COMPLIANCE_URL']))
10
+ : new URL('/v1/metrics', String(env['TELEMETRY_URL']));
9
11
  const headers = {
10
12
  'Content-Type': 'application/json',
11
13
  };
@@ -74,5 +74,21 @@ export interface TelemetryReport {
74
74
  /**
75
75
  * Unique project identifier
76
76
  */
77
- project_id?: string | null;
77
+ project_id: string;
78
+ /**
79
+ * Whether the project has enabled MCP
80
+ */
81
+ mcp_enabled: boolean;
82
+ /**
83
+ * Whether the project allows deletes in MCP
84
+ */
85
+ mcp_allow_deletes: boolean;
86
+ /**
87
+ * Whether the project has enabled MCP system prompt
88
+ */
89
+ mcp_system_prompt_enabled: boolean;
90
+ /**
91
+ * Number of Visual Editor URLs configured in the system
92
+ */
93
+ visual_editor_urls: number;
78
94
  }
@@ -0,0 +1,9 @@
1
+ import type { Knex } from 'knex';
2
+ export type TelemetrySettings = {
3
+ project_id: string;
4
+ mcp_enabled: boolean;
5
+ mcp_allow_deletes: boolean;
6
+ mcp_system_prompt_enabled: boolean;
7
+ visual_editor_urls: number;
8
+ };
9
+ export declare const getSettings: (db: Knex) => Promise<TelemetrySettings>;
@@ -0,0 +1,14 @@
1
+ import { toBoolean } from '@directus/utils';
2
+ export const getSettings = async (db) => {
3
+ const settings = await db
4
+ .select('project_id', 'mcp_enabled', 'mcp_allow_deletes', 'mcp_system_prompt_enabled', 'visual_editor_urls')
5
+ .from('directus_settings')
6
+ .first();
7
+ return {
8
+ project_id: settings.project_id,
9
+ mcp_enabled: toBoolean(settings?.mcp_enabled),
10
+ mcp_allow_deletes: toBoolean(settings?.mcp_allow_deletes),
11
+ mcp_system_prompt_enabled: toBoolean(settings?.mcp_system_prompt_enabled),
12
+ visual_editor_urls: settings.visual_editor_urls ? JSON.parse(settings.visual_editor_urls).length : 0,
13
+ };
14
+ };