directus 9.20.3 → 9.21.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/cli/utils/create-db-connection.d.ts +1 -1
- package/dist/controllers/extensions.js +4 -13
- package/dist/database/helpers/date/dialects/sqlite.d.ts +1 -1
- package/dist/database/helpers/date/dialects/sqlite.js +4 -0
- package/dist/database/helpers/date/types.d.ts +1 -1
- package/dist/database/helpers/date/types.js +4 -0
- package/dist/database/helpers/fn/dialects/mssql.d.ts +8 -8
- package/dist/database/helpers/fn/dialects/mssql.js +22 -16
- package/dist/database/helpers/fn/dialects/mysql.d.ts +8 -8
- package/dist/database/helpers/fn/dialects/mysql.js +22 -16
- package/dist/database/helpers/fn/dialects/postgres.d.ts +8 -8
- package/dist/database/helpers/fn/dialects/postgres.js +22 -16
- package/dist/database/helpers/fn/types.d.ts +1 -1
- package/dist/database/helpers/index.d.ts +1 -1
- package/dist/database/helpers/schema/dialects/cockroachdb.d.ts +1 -0
- package/dist/database/helpers/schema/dialects/cockroachdb.js +11 -0
- package/dist/database/helpers/schema/types.d.ts +3 -2
- package/dist/database/helpers/schema/types.js +5 -0
- package/dist/database/migrations/run.js +29 -3
- package/dist/database/run-ast.d.ts +1 -1
- package/dist/database/run-ast.js +1 -1
- package/dist/env.d.ts +4 -0
- package/dist/env.js +9 -4
- package/dist/env.test.d.ts +1 -8
- package/dist/exceptions/database/contains-null-values.d.ts +1 -1
- package/dist/exceptions/database/dialects/types.d.ts +6 -6
- package/dist/exceptions/database/invalid-foreign-key.d.ts +1 -1
- package/dist/exceptions/database/not-null-violation.d.ts +1 -1
- package/dist/exceptions/database/record-not-unique.d.ts +1 -1
- package/dist/exceptions/database/value-out-of-range.d.ts +1 -1
- package/dist/exceptions/database/value-too-long.d.ts +1 -1
- package/dist/exceptions/hit-rate-limit.d.ts +1 -1
- package/dist/exceptions/method-not-allowed.d.ts +1 -1
- package/dist/exceptions/service-unavailable.d.ts +1 -1
- package/dist/extensions.d.ts +7 -7
- package/dist/extensions.js +92 -89
- package/dist/logger.d.ts +1 -0
- package/dist/messenger.d.ts +1 -1
- package/dist/middleware/authenticate.d.ts +1 -0
- package/dist/middleware/validate-batch.d.ts +2 -0
- package/dist/operations/condition/index.d.ts +1 -1
- package/dist/operations/condition/index.js +1 -1
- package/dist/operations/condition/index.test.d.ts +1 -0
- package/dist/operations/exec/index.d.ts +1 -1
- package/dist/operations/item-create/index.d.ts +1 -1
- package/dist/operations/item-delete/index.d.ts +1 -1
- package/dist/operations/item-read/index.d.ts +1 -1
- package/dist/operations/item-update/index.d.ts +1 -1
- package/dist/operations/log/index.d.ts +1 -1
- package/dist/operations/mail/index.d.ts +1 -1
- package/dist/operations/notification/index.d.ts +1 -1
- package/dist/operations/request/index.d.ts +1 -1
- package/dist/operations/sleep/index.d.ts +1 -1
- package/dist/operations/transform/index.d.ts +1 -1
- package/dist/operations/trigger/index.d.ts +1 -1
- package/dist/operations/trigger/index.js +5 -2
- package/dist/rate-limiter.d.ts +1 -1
- package/dist/services/authorization.js +7 -3
- package/dist/services/collections.d.ts +1 -1
- package/dist/services/collections.js +112 -13
- package/dist/services/fields.d.ts +2 -2
- package/dist/services/fields.js +89 -41
- package/dist/services/fields.test.d.ts +1 -0
- package/dist/services/graphql/index.js +4 -1
- package/dist/services/graphql/types/bigint.d.ts +1 -1
- package/dist/services/graphql/types/bigint.js +4 -0
- package/dist/services/graphql/utils/process-error.d.ts +4 -0
- package/dist/services/graphql/utils/process-error.js +26 -0
- package/dist/services/graphql/utils/process-error.test.d.ts +1 -0
- package/dist/services/items.d.ts +1 -1
- package/dist/services/items.js +39 -13
- package/dist/services/mail/index.d.ts +2 -2
- package/dist/services/mail/index.js +2 -1
- package/dist/services/mail/templates/base.liquid +4 -4
- package/dist/services/notifications.js +9 -4
- package/dist/services/notifications.test.d.ts +1 -0
- package/dist/services/payload.d.ts +2 -2
- package/dist/services/payload.js +14 -12
- package/dist/services/relations.d.ts +2 -2
- package/dist/services/relations.js +54 -4
- package/dist/services/users.js +2 -2
- package/dist/services/users.test.d.ts +1 -0
- package/dist/types/assets.d.ts +7 -7
- package/dist/types/ast.d.ts +7 -7
- package/dist/types/auth.d.ts +4 -4
- package/dist/types/collection.d.ts +2 -2
- package/dist/types/events.d.ts +1 -1
- package/dist/types/files.d.ts +2 -2
- package/dist/types/items.d.ts +5 -5
- package/dist/types/migration.d.ts +1 -1
- package/dist/types/revision.d.ts +1 -1
- package/dist/types/services.d.ts +1 -1
- package/dist/types/snapshot.d.ts +4 -4
- package/dist/types/webhooks.d.ts +2 -2
- package/dist/utils/get-ast-from-query.d.ts +1 -1
- package/dist/utils/get-column-path.d.ts +2 -2
- package/dist/utils/get-module-default.d.ts +1 -1
- package/dist/utils/get-relation-info.d.ts +1 -1
- package/dist/utils/job-queue.d.ts +1 -1
- package/dist/utils/merge-permissions.d.ts +1 -0
- package/dist/utils/reduce-schema.js +3 -1
- package/package.json +70 -71
- package/dist/__mocks__/cache.d.ts +0 -5
- package/dist/__mocks__/cache.js +0 -7
- package/dist/__utils__/items-utils.d.ts +0 -2
- package/dist/__utils__/items-utils.js +0 -36
- package/dist/__utils__/schemas.d.ts +0 -13
- package/dist/__utils__/schemas.js +0 -304
- package/dist/__utils__/snapshots.d.ts +0 -5
- package/dist/__utils__/snapshots.js +0 -897
- package/dist/cli/index.test.js +0 -63
- package/dist/controllers/files.test.js +0 -49
- package/dist/database/migrations/run.test.js +0 -92
- package/dist/env.test.js +0 -40
- package/dist/middleware/authenticate.test.js +0 -214
- package/dist/middleware/extract-token.test.js +0 -60
- package/dist/middleware/validate-batch.test.js +0 -82
- package/dist/operations/exec/index.test.js +0 -95
- package/dist/services/files.test.js +0 -89
- package/dist/services/items.test.js +0 -765
- package/dist/services/payload.test.js +0 -196
- package/dist/services/specifications.test.js +0 -96
- package/dist/utils/apply-snapshot.test.js +0 -305
- package/dist/utils/async-handler.test.js +0 -18
- package/dist/utils/calculate-field-depth.test.js +0 -76
- package/dist/utils/filter-items.test.js +0 -60
- package/dist/utils/get-auth-providers.test.js +0 -72
- package/dist/utils/get-cache-key.test.js +0 -74
- package/dist/utils/get-column-path.test.js +0 -136
- package/dist/utils/get-config-from-env.test.js +0 -19
- package/dist/utils/get-relation-info.test.js +0 -88
- package/dist/utils/get-relation-type.test.js +0 -69
- package/dist/utils/get-string-byte-size.test.js +0 -8
- package/dist/utils/is-directus-jwt.test.js +0 -26
- package/dist/utils/jwt.test.js +0 -36
- package/dist/utils/merge-permissions.test.js +0 -80
- package/dist/utils/validate-keys.test.js +0 -97
package/dist/services/items.js
CHANGED
|
@@ -193,19 +193,34 @@ class ItemsService {
|
|
|
193
193
|
* Create multiple new items at once. Inserts all provided records sequentially wrapped in a transaction.
|
|
194
194
|
*/
|
|
195
195
|
async createMany(data, opts) {
|
|
196
|
-
const primaryKeys = await this.knex.transaction(async (trx) => {
|
|
196
|
+
const { primaryKeys, nestedActionEvents } = await this.knex.transaction(async (trx) => {
|
|
197
197
|
const service = new ItemsService(this.collection, {
|
|
198
198
|
accountability: this.accountability,
|
|
199
199
|
schema: this.schema,
|
|
200
200
|
knex: trx,
|
|
201
201
|
});
|
|
202
202
|
const primaryKeys = [];
|
|
203
|
+
const nestedActionEvents = [];
|
|
203
204
|
for (const payload of data) {
|
|
204
|
-
const primaryKey = await service.createOne(payload, {
|
|
205
|
+
const primaryKey = await service.createOne(payload, {
|
|
206
|
+
...(opts || {}),
|
|
207
|
+
autoPurgeCache: false,
|
|
208
|
+
bypassEmitAction: (params) => (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) ? opts.bypassEmitAction(params) : nestedActionEvents.push(params),
|
|
209
|
+
});
|
|
205
210
|
primaryKeys.push(primaryKey);
|
|
206
211
|
}
|
|
207
|
-
return primaryKeys;
|
|
212
|
+
return { primaryKeys, nestedActionEvents };
|
|
208
213
|
});
|
|
214
|
+
if ((opts === null || opts === void 0 ? void 0 : opts.emitEvents) !== false) {
|
|
215
|
+
for (const nestedActionEvent of nestedActionEvents) {
|
|
216
|
+
if (!(opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction)) {
|
|
217
|
+
emitter_1.default.emitAction(nestedActionEvent.event, nestedActionEvent.meta, nestedActionEvent.context);
|
|
218
|
+
}
|
|
219
|
+
else {
|
|
220
|
+
opts.bypassEmitAction(nestedActionEvent);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
209
224
|
if (this.cache && env_1.default.CACHE_AUTO_PURGE && (opts === null || opts === void 0 ? void 0 : opts.autoPurgeCache) !== false) {
|
|
210
225
|
await this.cache.clear();
|
|
211
226
|
}
|
|
@@ -325,20 +340,31 @@ class ItemsService {
|
|
|
325
340
|
* Update multiple items in a single transaction
|
|
326
341
|
*/
|
|
327
342
|
async updateBatch(data, opts) {
|
|
343
|
+
if (!Array.isArray(data)) {
|
|
344
|
+
throw new exceptions_1.InvalidPayloadException('Input should be an array of items.');
|
|
345
|
+
}
|
|
328
346
|
const primaryKeyField = this.schema.collections[this.collection].primary;
|
|
329
347
|
const keys = [];
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
348
|
+
try {
|
|
349
|
+
await this.knex.transaction(async (trx) => {
|
|
350
|
+
const service = new ItemsService(this.collection, {
|
|
351
|
+
accountability: this.accountability,
|
|
352
|
+
knex: trx,
|
|
353
|
+
schema: this.schema,
|
|
354
|
+
});
|
|
355
|
+
for (const item of data) {
|
|
356
|
+
if (!item[primaryKeyField])
|
|
357
|
+
throw new exceptions_1.InvalidPayloadException(`Item in update misses primary key.`);
|
|
358
|
+
const combinedOpts = Object.assign({ autoPurgeCache: false }, opts);
|
|
359
|
+
keys.push(await service.updateOne(item[primaryKeyField], (0, lodash_1.omit)(item, primaryKeyField), combinedOpts));
|
|
360
|
+
}
|
|
335
361
|
});
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
362
|
+
}
|
|
363
|
+
finally {
|
|
364
|
+
if (this.cache && env_1.default.CACHE_AUTO_PURGE && (opts === null || opts === void 0 ? void 0 : opts.autoPurgeCache) !== false) {
|
|
365
|
+
await this.cache.clear();
|
|
340
366
|
}
|
|
341
|
-
}
|
|
367
|
+
}
|
|
342
368
|
return keys;
|
|
343
369
|
}
|
|
344
370
|
/**
|
|
@@ -2,7 +2,7 @@ import { Knex } from 'knex';
|
|
|
2
2
|
import { AbstractServiceOptions } from '../../types';
|
|
3
3
|
import { Accountability, SchemaOverview } from '@directus/shared/types';
|
|
4
4
|
import { Transporter, SendMailOptions } from 'nodemailer';
|
|
5
|
-
export
|
|
5
|
+
export type EmailOptions = SendMailOptions & {
|
|
6
6
|
template?: {
|
|
7
7
|
name: string;
|
|
8
8
|
data: Record<string, any>;
|
|
@@ -14,7 +14,7 @@ export declare class MailService {
|
|
|
14
14
|
knex: Knex;
|
|
15
15
|
mailer: Transporter;
|
|
16
16
|
constructor(opts: AbstractServiceOptions);
|
|
17
|
-
send(options: EmailOptions): Promise<
|
|
17
|
+
send<T>(options: EmailOptions): Promise<T>;
|
|
18
18
|
private renderTemplate;
|
|
19
19
|
private getDefaultTemplateData;
|
|
20
20
|
}
|
|
@@ -52,7 +52,8 @@ class MailService {
|
|
|
52
52
|
.map((line) => line.trim())
|
|
53
53
|
.join('\n');
|
|
54
54
|
}
|
|
55
|
-
await this.mailer.sendMail({ ...emailOptions, from, html });
|
|
55
|
+
const info = await this.mailer.sendMail({ ...emailOptions, from, html });
|
|
56
|
+
return info;
|
|
56
57
|
}
|
|
57
58
|
async renderTemplate(template, variables) {
|
|
58
59
|
const customTemplatePath = path_1.default.resolve(env_1.default.EXTENSIONS_PATH, 'templates', template + '.liquid');
|
|
@@ -125,14 +125,14 @@ blockquote > p {
|
|
|
125
125
|
<tbody>
|
|
126
126
|
<tr>
|
|
127
127
|
<td align="left" valign="top" style="-webkit-text-size-adjust:100%; -ms-text-size-adjust:100%; mso-table-lspace:0pt; mso-table-rspace:0pt; padding:0 0 30px 0">
|
|
128
|
-
<table><tbody><tr><td align="center" valign="middle" style="background-color:{{ projectColor }};width:48px;height:48px;border-radius:4px;padding:6px;">
|
|
129
|
-
<img id="logo" src="{{ projectLogo }}" alt="{{ projectName }} Logo" width="40" height="auto" border="0" style="-ms-interpolation-mode:bicubic; border:0; height:
|
|
128
|
+
<table><tbody><tr><td align="center" valign="middle" style="background-color:{{ projectColor }};max-width:48px;max-height:48px;border-radius:4px;padding:6px;">
|
|
129
|
+
<img id="logo" src="{{ projectLogo }}" alt="{{ projectName }} Logo" width="40" height="auto" border="0" style="-ms-interpolation-mode:bicubic; border:0; height:40px; line-height:100%; outline:none; text-decoration:none; display:block; width:40px; object-fit:contain;">
|
|
130
130
|
</td></tr></tbody></table>
|
|
131
131
|
</td>
|
|
132
132
|
</tr>
|
|
133
133
|
<tr>
|
|
134
134
|
<td id="content" align="left" valign="top" style="-webkit-text-size-adjust:100%; -ms-text-size-adjust:100%; mso-table-lspace:0pt; mso-table-rspace:0pt; padding:40px 50px 50px 50px; font-family:Open Sans, Helvetica, Arial, sans-serif; border-radius:4px; box-shadow:0 4px 0 #15253A; background-color:#FFFFFE; color:#172940; font-size:15px; line-height:26px; margin:0" bgcolor="#FFFFFE">
|
|
135
|
-
<div
|
|
135
|
+
<div style="color: inherit; font-size: inherit; line-height: inherit;">
|
|
136
136
|
|
|
137
137
|
{% block content %}{{ html }}{% endblock %}
|
|
138
138
|
|
|
@@ -142,7 +142,7 @@ blockquote > p {
|
|
|
142
142
|
<tr>
|
|
143
143
|
<td align="center" valign="middle" style="-webkit-text-size-adjust:100%; -ms-text-size-adjust:100%; mso-table-lspace:0pt; mso-table-rspace:0pt; padding:25px 0; font-family:Open Sans, Helvetica, Arial, sans-serif; color:#FFFFFE">
|
|
144
144
|
<p style="margin-bottom: 1em; color: #A2B5CD;font-size: 12px; line-height: 16px;">
|
|
145
|
-
Sent by the team at {{ projectName }} — <a style="-webkit-text-size-adjust:100%; -ms-text-size-adjust:100%; text-decoration:none; color:#A2B5CD"
|
|
145
|
+
Sent by the team at {{ projectName }}{% if url %} — <a style="-webkit-text-size-adjust:100%; -ms-text-size-adjust:100%; text-decoration:none; color:#A2B5CD" href="{{ url }}" target="_blank">Manage Your Account</a>{% endif %}<br>
|
|
146
146
|
{% block footer %}{% endblock %}
|
|
147
147
|
</p>
|
|
148
148
|
</td>
|
|
@@ -6,9 +6,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.NotificationsService = void 0;
|
|
7
7
|
const items_1 = require("./items");
|
|
8
8
|
const md_1 = require("../utils/md");
|
|
9
|
+
const url_1 = require("../utils/url");
|
|
9
10
|
const users_1 = require("./users");
|
|
10
11
|
const mail_1 = require("./mail");
|
|
11
12
|
const logger_1 = __importDefault(require("../logger"));
|
|
13
|
+
const env_1 = __importDefault(require("../env"));
|
|
12
14
|
class NotificationsService extends items_1.ItemsService {
|
|
13
15
|
constructor(options) {
|
|
14
16
|
super('directus_notifications', options);
|
|
@@ -28,16 +30,19 @@ class NotificationsService extends items_1.ItemsService {
|
|
|
28
30
|
return response;
|
|
29
31
|
}
|
|
30
32
|
async sendEmail(data) {
|
|
33
|
+
var _a;
|
|
31
34
|
if (data.recipient) {
|
|
32
|
-
const user = await this.usersService.readOne(data.recipient, {
|
|
35
|
+
const user = await this.usersService.readOne(data.recipient, {
|
|
36
|
+
fields: ['id', 'email', 'email_notifications', 'role.app_access'],
|
|
37
|
+
});
|
|
38
|
+
const manageUserAccountUrl = new url_1.Url(env_1.default.PUBLIC_URL).addPath('admin', 'users', user.id).toString();
|
|
39
|
+
const html = data.message ? (0, md_1.md)(data.message) : '';
|
|
33
40
|
if (user.email && user.email_notifications === true) {
|
|
34
41
|
try {
|
|
35
42
|
await this.mailService.send({
|
|
36
43
|
template: {
|
|
37
44
|
name: 'base',
|
|
38
|
-
data: {
|
|
39
|
-
html: data.message ? (0, md_1.md)(data.message) : '',
|
|
40
|
-
},
|
|
45
|
+
data: ((_a = user.role) === null || _a === void 0 ? void 0 : _a.app_access) ? { url: manageUserAccountUrl, html } : { html },
|
|
41
46
|
},
|
|
42
47
|
to: user.email,
|
|
43
48
|
subject: data.subject,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -2,8 +2,8 @@ import { Accountability, SchemaOverview } from '@directus/shared/types';
|
|
|
2
2
|
import { Knex } from 'knex';
|
|
3
3
|
import { Helpers } from '../database/helpers';
|
|
4
4
|
import { AbstractServiceOptions, ActionEventParams, Item, MutationOptions, PrimaryKey } from '../types';
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
type Action = 'create' | 'read' | 'update';
|
|
6
|
+
type Transformers = {
|
|
7
7
|
[type: string]: (context: {
|
|
8
8
|
action: Action;
|
|
9
9
|
value: any;
|
package/dist/services/payload.js
CHANGED
|
@@ -102,7 +102,9 @@ class PayloadService {
|
|
|
102
102
|
async 'cast-csv'({ action, value }) {
|
|
103
103
|
if (Array.isArray(value) === false && typeof value !== 'string')
|
|
104
104
|
return;
|
|
105
|
-
if (action === 'read'
|
|
105
|
+
if (action === 'read') {
|
|
106
|
+
if (Array.isArray(value))
|
|
107
|
+
return value;
|
|
106
108
|
if (value === '')
|
|
107
109
|
return [];
|
|
108
110
|
return value.split(',');
|
|
@@ -346,7 +348,7 @@ class PayloadService {
|
|
|
346
348
|
if (Object.keys(fieldsToUpdate).length > 0) {
|
|
347
349
|
await itemsService.updateOne(relatedPrimaryKey, relatedRecord, {
|
|
348
350
|
onRevisionCreate: (pk) => revisions.push(pk),
|
|
349
|
-
bypassEmitAction: (params) => nestedActionEvents.push(params),
|
|
351
|
+
bypassEmitAction: (params) => (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) ? opts.bypassEmitAction(params) : nestedActionEvents.push(params),
|
|
350
352
|
emitEvents: opts === null || opts === void 0 ? void 0 : opts.emitEvents,
|
|
351
353
|
});
|
|
352
354
|
}
|
|
@@ -354,7 +356,7 @@ class PayloadService {
|
|
|
354
356
|
else {
|
|
355
357
|
relatedPrimaryKey = await itemsService.createOne(relatedRecord, {
|
|
356
358
|
onRevisionCreate: (pk) => revisions.push(pk),
|
|
357
|
-
bypassEmitAction: (params) => nestedActionEvents.push(params),
|
|
359
|
+
bypassEmitAction: (params) => (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) ? opts.bypassEmitAction(params) : nestedActionEvents.push(params),
|
|
358
360
|
emitEvents: opts === null || opts === void 0 ? void 0 : opts.emitEvents,
|
|
359
361
|
});
|
|
360
362
|
}
|
|
@@ -406,7 +408,7 @@ class PayloadService {
|
|
|
406
408
|
if (Object.keys(fieldsToUpdate).length > 0) {
|
|
407
409
|
await itemsService.updateOne(relatedPrimaryKey, relatedRecord, {
|
|
408
410
|
onRevisionCreate: (pk) => revisions.push(pk),
|
|
409
|
-
bypassEmitAction: (params) => nestedActionEvents.push(params),
|
|
411
|
+
bypassEmitAction: (params) => (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) ? opts.bypassEmitAction(params) : nestedActionEvents.push(params),
|
|
410
412
|
emitEvents: opts === null || opts === void 0 ? void 0 : opts.emitEvents,
|
|
411
413
|
});
|
|
412
414
|
}
|
|
@@ -414,7 +416,7 @@ class PayloadService {
|
|
|
414
416
|
else {
|
|
415
417
|
relatedPrimaryKey = await itemsService.createOne(relatedRecord, {
|
|
416
418
|
onRevisionCreate: (pk) => revisions.push(pk),
|
|
417
|
-
bypassEmitAction: (params) => nestedActionEvents.push(params),
|
|
419
|
+
bypassEmitAction: (params) => (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) ? opts.bypassEmitAction(params) : nestedActionEvents.push(params),
|
|
418
420
|
emitEvents: opts === null || opts === void 0 ? void 0 : opts.emitEvents,
|
|
419
421
|
});
|
|
420
422
|
}
|
|
@@ -496,7 +498,7 @@ class PayloadService {
|
|
|
496
498
|
}
|
|
497
499
|
savedPrimaryKeys.push(...(await itemsService.upsertMany(recordsToUpsert, {
|
|
498
500
|
onRevisionCreate: (pk) => revisions.push(pk),
|
|
499
|
-
bypassEmitAction: (params) => nestedActionEvents.push(params),
|
|
501
|
+
bypassEmitAction: (params) => (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) ? opts.bypassEmitAction(params) : nestedActionEvents.push(params),
|
|
500
502
|
emitEvents: opts === null || opts === void 0 ? void 0 : opts.emitEvents,
|
|
501
503
|
})));
|
|
502
504
|
const query = {
|
|
@@ -519,14 +521,14 @@ class PayloadService {
|
|
|
519
521
|
if (relation.meta.one_deselect_action === 'delete') {
|
|
520
522
|
// There's no revision for a deletion
|
|
521
523
|
await itemsService.deleteByQuery(query, {
|
|
522
|
-
bypassEmitAction: (params) => nestedActionEvents.push(params),
|
|
524
|
+
bypassEmitAction: (params) => (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) ? opts.bypassEmitAction(params) : nestedActionEvents.push(params),
|
|
523
525
|
emitEvents: opts === null || opts === void 0 ? void 0 : opts.emitEvents,
|
|
524
526
|
});
|
|
525
527
|
}
|
|
526
528
|
else {
|
|
527
529
|
await itemsService.updateByQuery(query, { [relation.field]: null }, {
|
|
528
530
|
onRevisionCreate: (pk) => revisions.push(pk),
|
|
529
|
-
bypassEmitAction: (params) => nestedActionEvents.push(params),
|
|
531
|
+
bypassEmitAction: (params) => (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) ? opts.bypassEmitAction(params) : nestedActionEvents.push(params),
|
|
530
532
|
emitEvents: opts === null || opts === void 0 ? void 0 : opts.emitEvents,
|
|
531
533
|
});
|
|
532
534
|
}
|
|
@@ -567,7 +569,7 @@ class PayloadService {
|
|
|
567
569
|
}
|
|
568
570
|
await itemsService.createMany(createPayload, {
|
|
569
571
|
onRevisionCreate: (pk) => revisions.push(pk),
|
|
570
|
-
bypassEmitAction: (params) => nestedActionEvents.push(params),
|
|
572
|
+
bypassEmitAction: (params) => (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) ? opts.bypassEmitAction(params) : nestedActionEvents.push(params),
|
|
571
573
|
emitEvents: opts === null || opts === void 0 ? void 0 : opts.emitEvents,
|
|
572
574
|
});
|
|
573
575
|
}
|
|
@@ -579,7 +581,7 @@ class PayloadService {
|
|
|
579
581
|
[relation.field]: parent || payload[currentPrimaryKeyField],
|
|
580
582
|
}, {
|
|
581
583
|
onRevisionCreate: (pk) => revisions.push(pk),
|
|
582
|
-
bypassEmitAction: (params) => nestedActionEvents.push(params),
|
|
584
|
+
bypassEmitAction: (params) => (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) ? opts.bypassEmitAction(params) : nestedActionEvents.push(params),
|
|
583
585
|
emitEvents: opts === null || opts === void 0 ? void 0 : opts.emitEvents,
|
|
584
586
|
});
|
|
585
587
|
}
|
|
@@ -603,14 +605,14 @@ class PayloadService {
|
|
|
603
605
|
};
|
|
604
606
|
if (relation.meta.one_deselect_action === 'delete') {
|
|
605
607
|
await itemsService.deleteByQuery(query, {
|
|
606
|
-
bypassEmitAction: (params) => nestedActionEvents.push(params),
|
|
608
|
+
bypassEmitAction: (params) => (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) ? opts.bypassEmitAction(params) : nestedActionEvents.push(params),
|
|
607
609
|
emitEvents: opts === null || opts === void 0 ? void 0 : opts.emitEvents,
|
|
608
610
|
});
|
|
609
611
|
}
|
|
610
612
|
else {
|
|
611
613
|
await itemsService.updateByQuery(query, { [relation.field]: null }, {
|
|
612
614
|
onRevisionCreate: (pk) => revisions.push(pk),
|
|
613
|
-
bypassEmitAction: (params) => nestedActionEvents.push(params),
|
|
615
|
+
bypassEmitAction: (params) => (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) ? opts.bypassEmitAction(params) : nestedActionEvents.push(params),
|
|
614
616
|
emitEvents: opts === null || opts === void 0 ? void 0 : opts.emitEvents,
|
|
615
617
|
});
|
|
616
618
|
}
|
|
@@ -4,7 +4,7 @@ import { ItemsService, QueryOptions } from './items';
|
|
|
4
4
|
import { PermissionsService } from './permissions';
|
|
5
5
|
import SchemaInspector from '@directus/schema';
|
|
6
6
|
import Keyv from 'keyv';
|
|
7
|
-
import { AbstractServiceOptions } from '../types';
|
|
7
|
+
import { AbstractServiceOptions, MutationOptions } from '../types';
|
|
8
8
|
import { Helpers } from '../database/helpers';
|
|
9
9
|
export declare class RelationsService {
|
|
10
10
|
knex: Knex;
|
|
@@ -31,7 +31,7 @@ export declare class RelationsService {
|
|
|
31
31
|
/**
|
|
32
32
|
* Delete an existing relationship
|
|
33
33
|
*/
|
|
34
|
-
deleteOne(collection: string, field: string): Promise<void>;
|
|
34
|
+
deleteOne(collection: string, field: string, opts?: MutationOptions): Promise<void>;
|
|
35
35
|
/**
|
|
36
36
|
* Whether or not the current user has read access to relations
|
|
37
37
|
*/
|
|
@@ -37,6 +37,8 @@ const database_1 = __importStar(require("../database"));
|
|
|
37
37
|
const get_default_index_name_1 = require("../utils/get-default-index-name");
|
|
38
38
|
const cache_1 = require("../cache");
|
|
39
39
|
const helpers_1 = require("../database/helpers");
|
|
40
|
+
const emitter_1 = __importDefault(require("../emitter"));
|
|
41
|
+
const get_schema_1 = require("../utils/get-schema");
|
|
40
42
|
class RelationsService {
|
|
41
43
|
constructor(options) {
|
|
42
44
|
this.knex = options.knex || (0, database_1.default)();
|
|
@@ -153,6 +155,7 @@ class RelationsService {
|
|
|
153
155
|
throw new exceptions_1.InvalidPayloadException(`Field "${relation.field}" in collection "${relation.collection}" already has an associated relationship`);
|
|
154
156
|
}
|
|
155
157
|
const runPostColumnChange = await this.helpers.schema.preColumnChange();
|
|
158
|
+
const nestedActionEvents = [];
|
|
156
159
|
try {
|
|
157
160
|
const metaRow = {
|
|
158
161
|
...(relation.meta || {}),
|
|
@@ -181,7 +184,9 @@ class RelationsService {
|
|
|
181
184
|
// allowed to extract the relations regardless of permissions to directus_relations. This
|
|
182
185
|
// happens in `filterForbidden` down below
|
|
183
186
|
});
|
|
184
|
-
await relationsItemService.createOne(metaRow
|
|
187
|
+
await relationsItemService.createOne(metaRow, {
|
|
188
|
+
bypassEmitAction: (params) => nestedActionEvents.push(params),
|
|
189
|
+
});
|
|
185
190
|
});
|
|
186
191
|
}
|
|
187
192
|
finally {
|
|
@@ -189,6 +194,11 @@ class RelationsService {
|
|
|
189
194
|
await this.helpers.schema.postColumnChange();
|
|
190
195
|
}
|
|
191
196
|
await (0, cache_1.clearSystemCache)();
|
|
197
|
+
const updatedSchema = await (0, get_schema_1.getSchema)({ accountability: this.accountability || undefined });
|
|
198
|
+
for (const nestedActionEvent of nestedActionEvents) {
|
|
199
|
+
nestedActionEvent.context.schema = updatedSchema;
|
|
200
|
+
emitter_1.default.emitAction(nestedActionEvent.event, nestedActionEvent.meta, nestedActionEvent.context);
|
|
201
|
+
}
|
|
192
202
|
}
|
|
193
203
|
}
|
|
194
204
|
/**
|
|
@@ -211,6 +221,7 @@ class RelationsService {
|
|
|
211
221
|
throw new exceptions_1.InvalidPayloadException(`Field "${field}" in collection "${collection}" doesn't have a relationship.`);
|
|
212
222
|
}
|
|
213
223
|
const runPostColumnChange = await this.helpers.schema.preColumnChange();
|
|
224
|
+
const nestedActionEvents = [];
|
|
214
225
|
try {
|
|
215
226
|
await this.knex.transaction(async (trx) => {
|
|
216
227
|
if (existingRelation.related_collection) {
|
|
@@ -221,6 +232,8 @@ class RelationsService {
|
|
|
221
232
|
if (existingRelation === null || existingRelation === void 0 ? void 0 : existingRelation.schema) {
|
|
222
233
|
constraintName = existingRelation.schema.constraint_name || constraintName;
|
|
223
234
|
table.dropForeign(field, constraintName);
|
|
235
|
+
constraintName = this.helpers.schema.constraintName(constraintName);
|
|
236
|
+
existingRelation.schema.constraint_name = constraintName;
|
|
224
237
|
}
|
|
225
238
|
this.alterType(table, relation);
|
|
226
239
|
const builder = table
|
|
@@ -240,7 +253,9 @@ class RelationsService {
|
|
|
240
253
|
});
|
|
241
254
|
if (relation.meta) {
|
|
242
255
|
if (existingRelation === null || existingRelation === void 0 ? void 0 : existingRelation.meta) {
|
|
243
|
-
await relationsItemService.updateOne(existingRelation.meta.id, relation.meta
|
|
256
|
+
await relationsItemService.updateOne(existingRelation.meta.id, relation.meta, {
|
|
257
|
+
bypassEmitAction: (params) => nestedActionEvents.push(params),
|
|
258
|
+
});
|
|
244
259
|
}
|
|
245
260
|
else {
|
|
246
261
|
await relationsItemService.createOne({
|
|
@@ -248,6 +263,8 @@ class RelationsService {
|
|
|
248
263
|
many_collection: relation.collection,
|
|
249
264
|
many_field: relation.field,
|
|
250
265
|
one_collection: existingRelation.related_collection || null,
|
|
266
|
+
}, {
|
|
267
|
+
bypassEmitAction: (params) => nestedActionEvents.push(params),
|
|
251
268
|
});
|
|
252
269
|
}
|
|
253
270
|
}
|
|
@@ -258,12 +275,17 @@ class RelationsService {
|
|
|
258
275
|
await this.helpers.schema.postColumnChange();
|
|
259
276
|
}
|
|
260
277
|
await (0, cache_1.clearSystemCache)();
|
|
278
|
+
const updatedSchema = await (0, get_schema_1.getSchema)({ accountability: this.accountability || undefined });
|
|
279
|
+
for (const nestedActionEvent of nestedActionEvents) {
|
|
280
|
+
nestedActionEvent.context.schema = updatedSchema;
|
|
281
|
+
emitter_1.default.emitAction(nestedActionEvent.event, nestedActionEvent.meta, nestedActionEvent.context);
|
|
282
|
+
}
|
|
261
283
|
}
|
|
262
284
|
}
|
|
263
285
|
/**
|
|
264
286
|
* Delete an existing relationship
|
|
265
287
|
*/
|
|
266
|
-
async deleteOne(collection, field) {
|
|
288
|
+
async deleteOne(collection, field, opts) {
|
|
267
289
|
if (this.accountability && this.accountability.admin !== true) {
|
|
268
290
|
throw new exceptions_1.ForbiddenException();
|
|
269
291
|
}
|
|
@@ -278,6 +300,7 @@ class RelationsService {
|
|
|
278
300
|
throw new exceptions_1.InvalidPayloadException(`Field "${field}" in collection "${collection}" doesn't have a relationship.`);
|
|
279
301
|
}
|
|
280
302
|
const runPostColumnChange = await this.helpers.schema.preColumnChange();
|
|
303
|
+
const nestedActionEvents = [];
|
|
281
304
|
try {
|
|
282
305
|
await this.knex.transaction(async (trx) => {
|
|
283
306
|
var _a;
|
|
@@ -292,13 +315,40 @@ class RelationsService {
|
|
|
292
315
|
if (existingRelation.meta) {
|
|
293
316
|
await trx('directus_relations').delete().where({ many_collection: collection, many_field: field });
|
|
294
317
|
}
|
|
318
|
+
const actionEvent = {
|
|
319
|
+
event: 'relations.delete',
|
|
320
|
+
meta: {
|
|
321
|
+
payload: [field],
|
|
322
|
+
collection: collection,
|
|
323
|
+
},
|
|
324
|
+
context: {
|
|
325
|
+
database: this.knex,
|
|
326
|
+
schema: this.schema,
|
|
327
|
+
accountability: this.accountability,
|
|
328
|
+
},
|
|
329
|
+
};
|
|
330
|
+
if (opts === null || opts === void 0 ? void 0 : opts.bypassEmitAction) {
|
|
331
|
+
opts.bypassEmitAction(actionEvent);
|
|
332
|
+
}
|
|
333
|
+
else {
|
|
334
|
+
nestedActionEvents.push(actionEvent);
|
|
335
|
+
}
|
|
295
336
|
});
|
|
296
337
|
}
|
|
297
338
|
finally {
|
|
298
339
|
if (runPostColumnChange) {
|
|
299
340
|
await this.helpers.schema.postColumnChange();
|
|
300
341
|
}
|
|
301
|
-
|
|
342
|
+
if ((opts === null || opts === void 0 ? void 0 : opts.autoPurgeSystemCache) !== false) {
|
|
343
|
+
await (0, cache_1.clearSystemCache)();
|
|
344
|
+
}
|
|
345
|
+
if ((opts === null || opts === void 0 ? void 0 : opts.emitEvents) !== false && nestedActionEvents.length > 0) {
|
|
346
|
+
const updatedSchema = await (0, get_schema_1.getSchema)({ accountability: this.accountability || undefined });
|
|
347
|
+
for (const nestedActionEvent of nestedActionEvents) {
|
|
348
|
+
nestedActionEvent.context.schema = updatedSchema;
|
|
349
|
+
emitter_1.default.emitAction(nestedActionEvent.event, nestedActionEvent.meta, nestedActionEvent.context);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
302
352
|
}
|
|
303
353
|
}
|
|
304
354
|
/**
|
package/dist/services/users.js
CHANGED
|
@@ -202,10 +202,10 @@ class UsersService extends items_1.ItemsService {
|
|
|
202
202
|
if (data.tfa_secret !== undefined) {
|
|
203
203
|
throw new exceptions_2.InvalidPayloadException(`You can't change the "tfa_secret" value manually.`);
|
|
204
204
|
}
|
|
205
|
-
if (data.provider !== undefined) {
|
|
205
|
+
if (data.provider !== undefined && this.accountability && this.accountability.admin !== true) {
|
|
206
206
|
throw new exceptions_2.InvalidPayloadException(`You can't change the "provider" value manually.`);
|
|
207
207
|
}
|
|
208
|
-
if (data.external_identifier !== undefined) {
|
|
208
|
+
if (data.external_identifier !== undefined && this.accountability && this.accountability.admin !== true) {
|
|
209
209
|
throw new exceptions_2.InvalidPayloadException(`You can't change the "external_identifier" value manually.`);
|
|
210
210
|
}
|
|
211
211
|
return await super.updateMany(keys, data, opts);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/types/assets.d.ts
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import { ResizeOptions, Sharp } from 'sharp';
|
|
2
2
|
export declare const TransformationMethods: readonly ["toFormat", "jpeg", "png", "tiff", "webp", "resize", "extend", "extract", "trim", "rotate", "flip", "flop", "sharpen", "median", "blur", "flatten", "gamma", "negate", "normalise", "normalize", "clahe", "convolve", "threshold", "linear", "recomb", "modulate", "tint", "greyscale", "grayscale", "toColorspace", "toColourspace", "removeAlpha", "ensureAlpha", "extractChannel", "bandbool"];
|
|
3
|
-
|
|
4
|
-
export
|
|
3
|
+
type AllowedSharpMethods = Pick<Sharp, typeof TransformationMethods[number]>;
|
|
4
|
+
export type TransformationMap = {
|
|
5
5
|
[M in keyof AllowedSharpMethods]: readonly [M, ...Parameters<AllowedSharpMethods[M]>];
|
|
6
6
|
};
|
|
7
|
-
export
|
|
8
|
-
export
|
|
7
|
+
export type Transformation = TransformationMap[keyof TransformationMap];
|
|
8
|
+
export type TransformationParams = {
|
|
9
9
|
key?: string;
|
|
10
10
|
transforms?: Transformation[];
|
|
11
11
|
};
|
|
12
|
-
export
|
|
12
|
+
export type TransformationPreset = TransformationPresetFormat & TransformationPresetResize & TransformationParams & {
|
|
13
13
|
key: string;
|
|
14
14
|
};
|
|
15
|
-
export
|
|
15
|
+
export type TransformationPresetFormat = {
|
|
16
16
|
format?: 'jpg' | 'jpeg' | 'png' | 'webp' | 'tiff';
|
|
17
17
|
quality?: number;
|
|
18
18
|
};
|
|
19
|
-
export
|
|
19
|
+
export type TransformationPresetResize = Pick<ResizeOptions, 'width' | 'height' | 'fit' | 'withoutEnlargement'>;
|
|
20
20
|
export {};
|
package/dist/types/ast.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Query, Relation } from '@directus/shared/types';
|
|
2
|
-
export
|
|
2
|
+
export type M2ONode = {
|
|
3
3
|
type: 'm2o';
|
|
4
4
|
name: string;
|
|
5
5
|
children: (NestedCollectionNode | FieldNode | FunctionFieldNode)[];
|
|
@@ -9,7 +9,7 @@ export declare type M2ONode = {
|
|
|
9
9
|
parentKey: string;
|
|
10
10
|
relatedKey: string;
|
|
11
11
|
};
|
|
12
|
-
export
|
|
12
|
+
export type A2MNode = {
|
|
13
13
|
type: 'a2o';
|
|
14
14
|
names: string[];
|
|
15
15
|
children: {
|
|
@@ -25,7 +25,7 @@ export declare type A2MNode = {
|
|
|
25
25
|
relation: Relation;
|
|
26
26
|
parentKey: string;
|
|
27
27
|
};
|
|
28
|
-
export
|
|
28
|
+
export type O2MNode = {
|
|
29
29
|
type: 'o2m';
|
|
30
30
|
name: string;
|
|
31
31
|
children: (NestedCollectionNode | FieldNode | FunctionFieldNode)[];
|
|
@@ -35,20 +35,20 @@ export declare type O2MNode = {
|
|
|
35
35
|
parentKey: string;
|
|
36
36
|
relatedKey: string;
|
|
37
37
|
};
|
|
38
|
-
export
|
|
39
|
-
export
|
|
38
|
+
export type NestedCollectionNode = M2ONode | O2MNode | A2MNode;
|
|
39
|
+
export type FieldNode = {
|
|
40
40
|
type: 'field';
|
|
41
41
|
name: string;
|
|
42
42
|
fieldKey: string;
|
|
43
43
|
};
|
|
44
|
-
export
|
|
44
|
+
export type FunctionFieldNode = {
|
|
45
45
|
type: 'functionField';
|
|
46
46
|
name: string;
|
|
47
47
|
fieldKey: string;
|
|
48
48
|
query: Query;
|
|
49
49
|
relatedCollection: string;
|
|
50
50
|
};
|
|
51
|
-
export
|
|
51
|
+
export type AST = {
|
|
52
52
|
type: 'root';
|
|
53
53
|
name: string;
|
|
54
54
|
children: (NestedCollectionNode | FieldNode | FunctionFieldNode)[];
|
package/dist/types/auth.d.ts
CHANGED
|
@@ -18,13 +18,13 @@ export interface User {
|
|
|
18
18
|
app_access: boolean;
|
|
19
19
|
admin_access: boolean;
|
|
20
20
|
}
|
|
21
|
-
export
|
|
21
|
+
export type AuthData = Record<string, any> | null;
|
|
22
22
|
export interface Session {
|
|
23
23
|
token: string;
|
|
24
24
|
expires: Date;
|
|
25
25
|
share: string;
|
|
26
26
|
}
|
|
27
|
-
export
|
|
27
|
+
export type DirectusTokenPayload = {
|
|
28
28
|
id?: string;
|
|
29
29
|
role: string | null;
|
|
30
30
|
app_access: boolean | number;
|
|
@@ -35,7 +35,7 @@ export declare type DirectusTokenPayload = {
|
|
|
35
35
|
item: string;
|
|
36
36
|
};
|
|
37
37
|
};
|
|
38
|
-
export
|
|
38
|
+
export type ShareData = {
|
|
39
39
|
share_id: string;
|
|
40
40
|
share_role: string;
|
|
41
41
|
share_item: string;
|
|
@@ -46,7 +46,7 @@ export declare type ShareData = {
|
|
|
46
46
|
share_max_uses?: number;
|
|
47
47
|
share_password?: string;
|
|
48
48
|
};
|
|
49
|
-
export
|
|
49
|
+
export type LoginResult = {
|
|
50
50
|
accessToken: any;
|
|
51
51
|
refreshToken: any;
|
|
52
52
|
expires: any;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Table } from 'knex-schema-inspector/dist/types/table';
|
|
2
2
|
import { Field } from '@directus/shared/types';
|
|
3
|
-
export
|
|
3
|
+
export type CollectionMeta = {
|
|
4
4
|
collection: string;
|
|
5
5
|
note: string | null;
|
|
6
6
|
hidden: boolean;
|
|
@@ -11,7 +11,7 @@ export declare type CollectionMeta = {
|
|
|
11
11
|
accountability: 'all' | 'accountability' | null;
|
|
12
12
|
group: string | null;
|
|
13
13
|
};
|
|
14
|
-
export
|
|
14
|
+
export type Collection = {
|
|
15
15
|
collection: string;
|
|
16
16
|
fields?: Field[];
|
|
17
17
|
meta: CollectionMeta | null;
|
package/dist/types/events.d.ts
CHANGED
package/dist/types/files.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export
|
|
1
|
+
export type File = {
|
|
2
2
|
id: string;
|
|
3
3
|
storage: string;
|
|
4
4
|
filename_disk: string;
|
|
@@ -19,7 +19,7 @@ export declare type File = {
|
|
|
19
19
|
tags: string | null;
|
|
20
20
|
metadata: Record<string, any> | null;
|
|
21
21
|
};
|
|
22
|
-
export
|
|
22
|
+
export type Metadata = {
|
|
23
23
|
height?: number | undefined;
|
|
24
24
|
width?: number | undefined;
|
|
25
25
|
description?: string | undefined;
|