@directus/api 32.0.1 → 32.1.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/auth/drivers/oauth2.d.ts +1 -2
- package/dist/auth/drivers/oauth2.js +22 -17
- package/dist/auth/drivers/openid.d.ts +1 -2
- package/dist/auth/drivers/openid.js +18 -13
- package/dist/auth/drivers/saml.js +2 -2
- package/dist/auth/utils/generate-callback-url.d.ts +8 -0
- package/dist/auth/utils/generate-callback-url.js +11 -0
- package/dist/auth/utils/is-login-redirect-allowed.d.ts +8 -0
- package/dist/{utils → auth/utils}/is-login-redirect-allowed.js +16 -8
- package/dist/controllers/extensions.js +6 -0
- package/dist/extensions/lib/installation/manager.js +13 -3
- package/dist/mcp/tools/prompts/flows.md +57 -12
- package/dist/mcp/tools/prompts/operations.md +57 -479
- package/dist/middleware/respond.js +5 -0
- package/dist/services/graphql/schema/parse-query.js +2 -2
- package/dist/services/graphql/utils/filter-replace-m2a.d.ts +7 -3
- package/dist/services/graphql/utils/filter-replace-m2a.js +15 -9
- package/dist/services/import-export.js +6 -4
- package/dist/services/mail/index.d.ts +15 -2
- package/dist/services/mail/index.js +5 -4
- package/dist/services/notifications.js +2 -0
- package/dist/services/tus/data-store.d.ts +1 -1
- package/dist/services/tus/data-store.js +5 -5
- package/dist/utils/validate-query.js +1 -1
- package/package.json +29 -29
- package/dist/utils/is-login-redirect-allowed.d.ts +0 -4
|
@@ -1,42 +1,48 @@
|
|
|
1
1
|
import { getRelation } from '@directus/utils';
|
|
2
2
|
import { getRelationType } from '../../../utils/get-relation-type.js';
|
|
3
|
-
export function filterReplaceM2A(filter_arg, collection, schema) {
|
|
3
|
+
export function filterReplaceM2A(filter_arg, collection, schema, options) {
|
|
4
4
|
const filter = filter_arg;
|
|
5
5
|
for (const key in filter) {
|
|
6
|
-
const
|
|
6
|
+
const parts = key.split('__');
|
|
7
|
+
let field = parts[0];
|
|
8
|
+
const any_collection = parts[1];
|
|
7
9
|
if (!field)
|
|
8
10
|
continue;
|
|
11
|
+
field = options?.aliasMap?.[field] ?? field;
|
|
9
12
|
const relation = getRelation(schema.relations, collection, field);
|
|
10
13
|
const type = relation ? getRelationType({ relation, collection, field }) : null;
|
|
11
14
|
if (type === 'o2m' && relation) {
|
|
12
|
-
filter[key] = filterReplaceM2A(filter[key], relation.collection, schema);
|
|
15
|
+
filter[key] = filterReplaceM2A(filter[key], relation.collection, schema, options);
|
|
13
16
|
}
|
|
14
17
|
else if (type === 'm2o' && relation) {
|
|
15
|
-
filter[key] = filterReplaceM2A(filter[key], relation.related_collection, schema);
|
|
18
|
+
filter[key] = filterReplaceM2A(filter[key], relation.related_collection, schema, options);
|
|
16
19
|
}
|
|
17
20
|
else if (type === 'a2o' &&
|
|
18
21
|
relation &&
|
|
19
22
|
any_collection &&
|
|
20
23
|
relation.meta?.one_allowed_collections?.includes(any_collection)) {
|
|
21
|
-
filter[`${field}:${any_collection}`] = filterReplaceM2A(filter[key], any_collection, schema);
|
|
24
|
+
filter[`${field}:${any_collection}`] = filterReplaceM2A(filter[key], any_collection, schema, options);
|
|
22
25
|
delete filter[key];
|
|
23
26
|
}
|
|
24
27
|
else if (Array.isArray(filter[key])) {
|
|
25
|
-
filter[key] = filter[key].map((item) => filterReplaceM2A(item, collection, schema));
|
|
28
|
+
filter[key] = filter[key].map((item) => filterReplaceM2A(item, collection, schema, options));
|
|
26
29
|
}
|
|
27
30
|
else if (typeof filter[key] === 'object') {
|
|
28
|
-
filter[key] = filterReplaceM2A(filter[key], collection, schema);
|
|
31
|
+
filter[key] = filterReplaceM2A(filter[key], collection, schema, options);
|
|
29
32
|
}
|
|
30
33
|
}
|
|
31
34
|
return filter;
|
|
32
35
|
}
|
|
33
|
-
export function filterReplaceM2ADeep(deep_arg, collection, schema) {
|
|
36
|
+
export function filterReplaceM2ADeep(deep_arg, collection, schema, options) {
|
|
34
37
|
const deep = deep_arg;
|
|
35
38
|
for (const key in deep) {
|
|
36
39
|
if (key.startsWith('_') === false) {
|
|
37
|
-
const
|
|
40
|
+
const parts = key.split('__');
|
|
41
|
+
let field = parts[0];
|
|
42
|
+
const any_collection = parts[1];
|
|
38
43
|
if (!field)
|
|
39
44
|
continue;
|
|
45
|
+
field = options?.aliasMap?.[field] || deep._alias?.[field] || field;
|
|
40
46
|
const relation = getRelation(schema.relations, collection, field);
|
|
41
47
|
if (!relation)
|
|
42
48
|
continue;
|
|
@@ -443,6 +443,7 @@ export class ExportService {
|
|
|
443
443
|
throw new Error('Failed to create temporary file for export');
|
|
444
444
|
const mimeTypes = {
|
|
445
445
|
csv: 'text/csv',
|
|
446
|
+
csv_utf8: 'text/csv; charset=utf-8',
|
|
446
447
|
json: 'application/json',
|
|
447
448
|
xml: 'text/xml',
|
|
448
449
|
yaml: 'text/yaml',
|
|
@@ -485,7 +486,7 @@ export class ExportService {
|
|
|
485
486
|
readCount += result.length;
|
|
486
487
|
if (result.length) {
|
|
487
488
|
let csvHeadings = null;
|
|
488
|
-
if (format
|
|
489
|
+
if (format.startsWith('csv')) {
|
|
489
490
|
if (!query.fields)
|
|
490
491
|
query.fields = ['*'];
|
|
491
492
|
// to ensure the all headings are included in the CSV file, all possible fields need to be determined.
|
|
@@ -593,14 +594,15 @@ Your export of ${collection} is ready. <a href="${href}">Click here to view.</a>
|
|
|
593
594
|
}
|
|
594
595
|
return string;
|
|
595
596
|
}
|
|
596
|
-
if (format
|
|
597
|
+
if (format.startsWith('csv')) {
|
|
597
598
|
if (input.length === 0)
|
|
598
599
|
return '';
|
|
599
600
|
const transforms = [CSVTransforms.flatten({ separator: '.' })];
|
|
600
601
|
const header = options?.includeHeader !== false;
|
|
602
|
+
const withBOM = format === 'csv_utf8';
|
|
601
603
|
const transformOptions = options?.fields
|
|
602
|
-
? { transforms, header, fields: options?.fields }
|
|
603
|
-
: { transforms, header };
|
|
604
|
+
? { transforms, header, fields: options?.fields, withBOM }
|
|
605
|
+
: { transforms, header, withBOM };
|
|
604
606
|
let string = new CSVParser(transformOptions).parse(input);
|
|
605
607
|
if (options?.includeHeader === false) {
|
|
606
608
|
string = '\n' + string;
|
|
@@ -7,13 +7,26 @@ export type EmailOptions = SendMailOptions & {
|
|
|
7
7
|
data: Record<string, any>;
|
|
8
8
|
};
|
|
9
9
|
};
|
|
10
|
+
export type DefaultTemplateData = {
|
|
11
|
+
projectName: string;
|
|
12
|
+
projectColor: string;
|
|
13
|
+
projectLogo: string;
|
|
14
|
+
projectUrl: string;
|
|
15
|
+
};
|
|
10
16
|
export declare class MailService {
|
|
11
17
|
schema: SchemaOverview;
|
|
12
18
|
accountability: Accountability | null;
|
|
13
19
|
knex: Knex;
|
|
14
20
|
mailer: Transporter;
|
|
15
21
|
constructor(opts: AbstractServiceOptions);
|
|
16
|
-
send<T>(
|
|
22
|
+
send<T>(data: EmailOptions, options?: {
|
|
23
|
+
defaultTemplateData: DefaultTemplateData;
|
|
24
|
+
}): Promise<T | null>;
|
|
17
25
|
private renderTemplate;
|
|
18
|
-
|
|
26
|
+
getDefaultTemplateData(): Promise<{
|
|
27
|
+
projectName: any;
|
|
28
|
+
projectColor: any;
|
|
29
|
+
projectLogo: string;
|
|
30
|
+
projectUrl: any;
|
|
31
|
+
}>;
|
|
19
32
|
}
|
|
@@ -37,14 +37,15 @@ export class MailService {
|
|
|
37
37
|
});
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
|
-
async send(options) {
|
|
40
|
+
async send(data, options) {
|
|
41
41
|
await useEmailRateLimiterQueue();
|
|
42
|
-
const payload = await emitter.emitFilter(`email.send`,
|
|
42
|
+
const payload = await emitter.emitFilter(`email.send`, data, {});
|
|
43
43
|
if (!payload)
|
|
44
44
|
return null;
|
|
45
45
|
const { template, ...emailOptions } = payload;
|
|
46
|
-
let { html } =
|
|
47
|
-
|
|
46
|
+
let { html } = data;
|
|
47
|
+
// option for providing tempalate data was added to prevent transaction race conditions with preceding promises
|
|
48
|
+
const defaultTemplateData = options?.defaultTemplateData ?? (await this.getDefaultTemplateData());
|
|
48
49
|
if (isObject(emailOptions.from) && (!emailOptions.from.name || !emailOptions.from.address)) {
|
|
49
50
|
throw new InvalidPayloadError({ reason: 'A name and address property are required in the "from" object' });
|
|
50
51
|
}
|
|
@@ -48,6 +48,8 @@ export class NotificationsService extends ItemsService {
|
|
|
48
48
|
},
|
|
49
49
|
to: user['email'],
|
|
50
50
|
subject: data.subject,
|
|
51
|
+
}, {
|
|
52
|
+
defaultTemplateData: await mailService.getDefaultTemplateData(),
|
|
51
53
|
})
|
|
52
54
|
.catch((error) => {
|
|
53
55
|
logger.error(error, `Could not send notification via mail`);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { TusDriver } from '@directus/storage';
|
|
2
2
|
import type { Accountability, File, SchemaOverview } from '@directus/types';
|
|
3
|
-
import stream from 'node:stream';
|
|
4
3
|
import { DataStore, Upload } from '@tus/utils';
|
|
4
|
+
import stream from 'node:stream';
|
|
5
5
|
export type TusDataStoreConfig = {
|
|
6
6
|
constants: {
|
|
7
7
|
ENABLED: boolean;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import formatTitle from '@directus/format-title';
|
|
2
|
+
import { DataStore, ERRORS, Upload } from '@tus/utils';
|
|
3
|
+
import { omit } from 'lodash-es';
|
|
2
4
|
import { extension } from 'mime-types';
|
|
3
5
|
import { extname } from 'node:path';
|
|
4
6
|
import stream from 'node:stream';
|
|
5
|
-
import { DataStore, ERRORS, Upload } from '@tus/utils';
|
|
6
|
-
import { ItemsService } from '../items.js';
|
|
7
|
-
import { useLogger } from '../../logger/index.js';
|
|
8
7
|
import getDatabase from '../../database/index.js';
|
|
9
|
-
import {
|
|
8
|
+
import { useLogger } from '../../logger/index.js';
|
|
9
|
+
import { ItemsService } from '../items.js';
|
|
10
10
|
export class TusDataStore extends DataStore {
|
|
11
11
|
chunkSize;
|
|
12
12
|
maxSize;
|
|
@@ -66,7 +66,7 @@ export class TusDataStore extends DataStore {
|
|
|
66
66
|
upload.metadata['replace_id'] = upload.metadata['id'];
|
|
67
67
|
}
|
|
68
68
|
const fileData = {
|
|
69
|
-
...omit(upload.metadata, ['id']),
|
|
69
|
+
...omit(upload.metadata, ['id', 'replace_id']),
|
|
70
70
|
tus_id: upload.id,
|
|
71
71
|
tus_data: upload,
|
|
72
72
|
filesize: upload.size,
|
|
@@ -20,7 +20,7 @@ const querySchema = Joi.object({
|
|
|
20
20
|
page: Joi.number().integer().min(0),
|
|
21
21
|
meta: Joi.array().items(Joi.string().valid('total_count', 'filter_count')),
|
|
22
22
|
search: Joi.string(),
|
|
23
|
-
export: Joi.string().valid('csv', 'json', 'xml', 'yaml'),
|
|
23
|
+
export: Joi.string().valid('csv', 'csv_utf8', 'json', 'xml', 'yaml'),
|
|
24
24
|
version: Joi.string(),
|
|
25
25
|
versionRaw: Joi.boolean(),
|
|
26
26
|
aggregate: Joi.object(),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@directus/api",
|
|
3
|
-
"version": "32.0
|
|
3
|
+
"version": "32.1.0",
|
|
4
4
|
"description": "Directus is a real-time API and App dashboard for managing SQL database content",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"directus",
|
|
@@ -59,9 +59,9 @@
|
|
|
59
59
|
],
|
|
60
60
|
"dependencies": {
|
|
61
61
|
"@authenio/samlify-node-xmllint": "2.0.0",
|
|
62
|
-
"@aws-sdk/client-sesv2": "3.
|
|
62
|
+
"@aws-sdk/client-sesv2": "3.928.0",
|
|
63
63
|
"@godaddy/terminus": "4.12.1",
|
|
64
|
-
"@modelcontextprotocol/sdk": "1.
|
|
64
|
+
"@modelcontextprotocol/sdk": "1.21.1",
|
|
65
65
|
"@rollup/plugin-alias": "5.1.1",
|
|
66
66
|
"@rollup/plugin-node-resolve": "16.0.3",
|
|
67
67
|
"@rollup/plugin-virtual": "3.0.2",
|
|
@@ -93,17 +93,17 @@
|
|
|
93
93
|
"flat": "6.0.1",
|
|
94
94
|
"fs-extra": "11.3.2",
|
|
95
95
|
"glob-to-regexp": "0.4.1",
|
|
96
|
-
"graphql": "16.
|
|
96
|
+
"graphql": "16.12.0",
|
|
97
97
|
"graphql-compose": "9.1.0",
|
|
98
98
|
"graphql-ws": "6.0.6",
|
|
99
99
|
"helmet": "8.1.0",
|
|
100
100
|
"icc": "3.0.0",
|
|
101
|
-
"inquirer": "12.
|
|
101
|
+
"inquirer": "12.11.0",
|
|
102
102
|
"ioredis": "5.8.2",
|
|
103
103
|
"ip-matching": "2.1.2",
|
|
104
104
|
"isolated-vm": "5.0.3",
|
|
105
105
|
"joi": "18.0.1",
|
|
106
|
-
"js-yaml": "4.1.
|
|
106
|
+
"js-yaml": "4.1.1",
|
|
107
107
|
"js2xmlparser": "5.0.0",
|
|
108
108
|
"json2csv": "5.0.7",
|
|
109
109
|
"jsonwebtoken": "9.0.2",
|
|
@@ -120,7 +120,7 @@
|
|
|
120
120
|
"ms": "2.1.3",
|
|
121
121
|
"nanoid": "5.1.6",
|
|
122
122
|
"node-machine-id": "1.1.12",
|
|
123
|
-
"cron": "4.3.
|
|
123
|
+
"cron": "4.3.4",
|
|
124
124
|
"nodemailer": "7.0.10",
|
|
125
125
|
"object-hash": "3.0.0",
|
|
126
126
|
"openapi3-ts": "4.5.0",
|
|
@@ -143,7 +143,7 @@
|
|
|
143
143
|
"rollup": "4.52.5",
|
|
144
144
|
"samlify": "2.10.1",
|
|
145
145
|
"sanitize-html": "2.17.0",
|
|
146
|
-
"sharp": "0.34.
|
|
146
|
+
"sharp": "0.34.5",
|
|
147
147
|
"snappy": "7.3.3",
|
|
148
148
|
"stream-json": "1.9.1",
|
|
149
149
|
"tar": "7.5.2",
|
|
@@ -153,30 +153,30 @@
|
|
|
153
153
|
"ws": "8.18.3",
|
|
154
154
|
"zod": "4.1.12",
|
|
155
155
|
"zod-validation-error": "4.0.2",
|
|
156
|
+
"@directus/app": "14.2.0",
|
|
157
|
+
"@directus/env": "5.3.2",
|
|
156
158
|
"@directus/constants": "14.0.0",
|
|
157
|
-
"@directus/app": "14.1.1",
|
|
158
159
|
"@directus/errors": "2.0.5",
|
|
159
|
-
"@directus/
|
|
160
|
-
"@directus/extensions": "
|
|
161
|
-
"@directus/extensions-registry": "3.0.13",
|
|
162
|
-
"@directus/extensions-sdk": "17.0.1",
|
|
160
|
+
"@directus/extensions-registry": "3.0.14",
|
|
161
|
+
"@directus/extensions-sdk": "17.0.3",
|
|
163
162
|
"@directus/format-title": "12.1.1",
|
|
164
|
-
"@directus/
|
|
165
|
-
"@directus/pressure": "3.0.
|
|
166
|
-
"@directus/schema-builder": "0.0.8",
|
|
163
|
+
"@directus/extensions": "3.0.14",
|
|
164
|
+
"@directus/pressure": "3.0.12",
|
|
167
165
|
"@directus/schema": "13.0.4",
|
|
168
|
-
"@directus/
|
|
166
|
+
"@directus/memory": "3.0.12",
|
|
167
|
+
"@directus/schema-builder": "0.0.9",
|
|
169
168
|
"@directus/storage": "12.0.3",
|
|
170
|
-
"@directus/
|
|
171
|
-
"@directus/storage-driver-
|
|
172
|
-
"@directus/storage-driver-
|
|
169
|
+
"@directus/specs": "11.2.0",
|
|
170
|
+
"@directus/storage-driver-azure": "12.0.12",
|
|
171
|
+
"@directus/storage-driver-cloudinary": "12.0.12",
|
|
172
|
+
"@directus/storage-driver-gcs": "12.0.12",
|
|
173
|
+
"@directus/storage-driver-s3": "12.0.12",
|
|
173
174
|
"@directus/storage-driver-local": "12.0.3",
|
|
174
|
-
"@directus/storage-driver-
|
|
175
|
-
"@directus/
|
|
176
|
-
"@directus/
|
|
177
|
-
"@directus/utils": "13.0.
|
|
178
|
-
"
|
|
179
|
-
"directus": "11.13.1"
|
|
175
|
+
"@directus/storage-driver-supabase": "3.0.12",
|
|
176
|
+
"@directus/system-data": "3.4.2",
|
|
177
|
+
"@directus/validation": "2.0.12",
|
|
178
|
+
"@directus/utils": "13.0.13",
|
|
179
|
+
"directus": "11.13.3"
|
|
180
180
|
},
|
|
181
181
|
"devDependencies": {
|
|
182
182
|
"@directus/tsconfig": "3.0.0",
|
|
@@ -205,7 +205,7 @@
|
|
|
205
205
|
"@types/node": "22.13.14",
|
|
206
206
|
"@types/nodemailer": "7.0.3",
|
|
207
207
|
"@types/object-hash": "3.0.6",
|
|
208
|
-
"@types/papaparse": "5.
|
|
208
|
+
"@types/papaparse": "5.5.0",
|
|
209
209
|
"@types/proxy-addr": "2.0.3",
|
|
210
210
|
"@types/qs": "6.14.0",
|
|
211
211
|
"@types/sanitize-html": "2.16.0",
|
|
@@ -219,8 +219,8 @@
|
|
|
219
219
|
"knex-mock-client": "3.0.2",
|
|
220
220
|
"typescript": "5.9.3",
|
|
221
221
|
"vitest": "3.2.4",
|
|
222
|
-
"@directus/
|
|
223
|
-
"@directus/
|
|
222
|
+
"@directus/types": "13.4.0",
|
|
223
|
+
"@directus/schema-builder": "0.0.9"
|
|
224
224
|
},
|
|
225
225
|
"optionalDependencies": {
|
|
226
226
|
"@keyv/redis": "3.0.1",
|