@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.
- package/dist/app.js +2 -0
- package/dist/auth/auth.d.ts +2 -1
- package/dist/auth/auth.js +7 -2
- package/dist/auth/drivers/ldap.d.ts +0 -2
- package/dist/auth/drivers/ldap.js +9 -7
- package/dist/auth/drivers/oauth2.d.ts +0 -2
- package/dist/auth/drivers/oauth2.js +11 -8
- package/dist/auth/drivers/openid.d.ts +0 -2
- package/dist/auth/drivers/openid.js +11 -8
- package/dist/auth/drivers/saml.d.ts +0 -2
- package/dist/auth/drivers/saml.js +5 -5
- package/dist/auth.js +1 -2
- package/dist/cli/commands/bootstrap/index.js +12 -33
- package/dist/cli/commands/init/index.js +1 -1
- package/dist/cli/commands/schema/apply.d.ts +4 -0
- package/dist/cli/commands/schema/apply.js +26 -3
- package/dist/controllers/collections.js +7 -2
- package/dist/controllers/fields.js +31 -8
- package/dist/controllers/server.js +26 -1
- package/dist/controllers/settings.js +9 -2
- package/dist/controllers/users.js +2 -2
- package/dist/database/helpers/fn/types.js +3 -3
- package/dist/database/helpers/schema/dialects/cockroachdb.d.ts +2 -1
- package/dist/database/helpers/schema/dialects/cockroachdb.js +13 -0
- package/dist/database/helpers/schema/dialects/mssql.d.ts +2 -1
- package/dist/database/helpers/schema/dialects/mssql.js +23 -0
- package/dist/database/helpers/schema/dialects/mysql.d.ts +2 -1
- package/dist/database/helpers/schema/dialects/mysql.js +25 -0
- package/dist/database/helpers/schema/dialects/oracle.d.ts +2 -1
- package/dist/database/helpers/schema/dialects/oracle.js +13 -0
- package/dist/database/helpers/schema/dialects/postgres.d.ts +2 -1
- package/dist/database/helpers/schema/dialects/postgres.js +13 -0
- package/dist/database/helpers/schema/types.d.ts +5 -0
- package/dist/database/helpers/schema/types.js +6 -0
- package/dist/database/migrations/20251012A-add-field-searchable.d.ts +3 -0
- package/dist/database/migrations/20251012A-add-field-searchable.js +10 -0
- package/dist/database/migrations/20251014A-add-project-owner.d.ts +3 -0
- package/dist/database/migrations/20251014A-add-project-owner.js +37 -0
- package/dist/database/migrations/20251028A-add-retention-indexes.d.ts +3 -0
- package/dist/database/migrations/20251028A-add-retention-indexes.js +42 -0
- package/dist/database/run-ast/lib/apply-query/add-join.js +2 -2
- package/dist/database/run-ast/lib/apply-query/filter/get-filter-type.d.ts +2 -2
- package/dist/database/run-ast/lib/apply-query/index.d.ts +0 -1
- package/dist/database/run-ast/lib/apply-query/index.js +4 -6
- package/dist/database/run-ast/lib/apply-query/search.js +2 -0
- package/dist/database/run-ast/lib/get-db-query.js +7 -6
- package/dist/database/run-ast/utils/generate-alias.d.ts +6 -0
- package/dist/database/run-ast/utils/generate-alias.js +57 -0
- package/dist/flows.js +1 -0
- package/dist/mcp/schema.d.ts +14 -14
- package/dist/mcp/schema.js +6 -6
- package/dist/mcp/server.d.ts +9 -3
- package/dist/mcp/server.js +1 -1
- package/dist/mcp/tools/collections.d.ts +1 -1
- package/dist/mcp/tools/fields.d.ts +1 -1
- package/dist/mcp/tools/files.d.ts +25 -25
- package/dist/mcp/tools/flows.d.ts +36 -36
- package/dist/mcp/tools/folders.d.ts +18 -18
- package/dist/mcp/tools/items.d.ts +18 -18
- package/dist/mcp/tools/operations.d.ts +19 -19
- package/dist/mcp/tools/prompts/items.md +1 -1
- package/dist/metrics/lib/create-metrics.js +16 -25
- package/dist/middleware/collection-exists.js +2 -2
- package/dist/operations/mail/index.js +3 -1
- package/dist/operations/mail/rate-limiter.d.ts +1 -0
- package/dist/operations/mail/rate-limiter.js +29 -0
- package/dist/permissions/modules/process-payload/process-payload.js +3 -10
- package/dist/permissions/modules/validate-access/validate-access.js +2 -3
- package/dist/schedules/metrics.js +6 -2
- package/dist/schedules/project.d.ts +4 -0
- package/dist/schedules/project.js +27 -0
- package/dist/services/collections.d.ts +3 -3
- package/dist/services/collections.js +16 -1
- package/dist/services/fields.d.ts +21 -5
- package/dist/services/fields.js +105 -28
- package/dist/services/graphql/resolvers/query.js +1 -1
- package/dist/services/graphql/resolvers/system-admin.js +49 -5
- package/dist/services/graphql/schema/parse-query.js +8 -8
- package/dist/services/graphql/utils/aggregate-query.d.ts +1 -1
- package/dist/services/graphql/utils/aggregate-query.js +5 -1
- package/dist/services/graphql/utils/filter-replace-m2a.js +2 -1
- package/dist/services/import-export.d.ts +9 -1
- package/dist/services/import-export.js +287 -101
- package/dist/services/items.d.ts +1 -1
- package/dist/services/items.js +36 -20
- package/dist/services/mail/index.js +2 -0
- package/dist/services/mail/rate-limiter.d.ts +1 -0
- package/dist/services/mail/rate-limiter.js +29 -0
- package/dist/services/meta.js +28 -24
- package/dist/services/schema.js +4 -1
- package/dist/services/server.d.ts +1 -0
- package/dist/services/server.js +14 -18
- package/dist/services/settings.d.ts +2 -1
- package/dist/services/settings.js +15 -0
- package/dist/services/tus/server.js +14 -9
- package/dist/telemetry/lib/get-report.js +4 -4
- package/dist/telemetry/lib/send-report.d.ts +6 -1
- package/dist/telemetry/lib/send-report.js +3 -1
- package/dist/telemetry/types/report.d.ts +17 -1
- package/dist/telemetry/utils/get-settings.d.ts +9 -0
- package/dist/telemetry/utils/get-settings.js +14 -0
- package/dist/test-utils/README.md +760 -0
- package/dist/test-utils/cache.d.ts +51 -0
- package/dist/test-utils/cache.js +59 -0
- package/dist/test-utils/database.d.ts +48 -0
- package/dist/test-utils/database.js +52 -0
- package/dist/test-utils/emitter.d.ts +35 -0
- package/dist/test-utils/emitter.js +38 -0
- package/dist/test-utils/fields-service.d.ts +28 -0
- package/dist/test-utils/fields-service.js +36 -0
- package/dist/test-utils/items-service.d.ts +23 -0
- package/dist/test-utils/items-service.js +37 -0
- package/dist/test-utils/knex.d.ts +164 -0
- package/dist/test-utils/knex.js +268 -0
- package/dist/test-utils/schema.d.ts +26 -0
- package/dist/test-utils/schema.js +35 -0
- package/dist/types/auth.d.ts +0 -2
- package/dist/utils/apply-diff.js +15 -0
- package/dist/utils/create-admin.d.ts +11 -0
- package/dist/utils/create-admin.js +50 -0
- package/dist/utils/get-schema.js +5 -3
- package/dist/utils/get-snapshot-diff.js +49 -5
- package/dist/utils/get-snapshot.js +13 -7
- package/dist/utils/sanitize-schema.d.ts +11 -4
- package/dist/utils/sanitize-schema.js +9 -6
- package/dist/utils/schedule.js +15 -19
- package/dist/utils/validate-diff.js +31 -0
- package/dist/utils/validate-snapshot.js +7 -0
- package/dist/websocket/controllers/hooks.js +12 -20
- package/dist/websocket/messages.d.ts +3 -3
- package/package.json +63 -65
- package/dist/cli/utils/defaults.d.ts +0 -4
- package/dist/cli/utils/defaults.js +0 -17
- package/dist/telemetry/utils/get-project-id.d.ts +0 -2
- package/dist/telemetry/utils/get-project-id.js +0 -4
package/dist/services/meta.js
CHANGED
|
@@ -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
|
|
5
|
-
import {
|
|
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
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
}
|
package/dist/services/schema.js
CHANGED
|
@@ -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 &&
|
|
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
|
}
|
package/dist/services/server.js
CHANGED
|
@@ -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(
|
|
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
|
|
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,
|
|
44
|
+
async onUploadFinish(req, upload) {
|
|
45
|
+
const schema = await getSchema();
|
|
44
46
|
const service = new ItemsService('directus_files', {
|
|
45
|
-
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
|
|
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
|
|
96
|
+
schema,
|
|
95
97
|
accountability: req.accountability,
|
|
96
98
|
});
|
|
97
|
-
return
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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
|
+
};
|