@directus/api 19.0.0 → 19.0.2
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-env/env-stub.liquid +2 -2
- package/dist/controllers/assets.js +9 -12
- package/dist/controllers/collections.js +1 -2
- package/dist/controllers/dashboards.js +1 -2
- package/dist/controllers/flows.js +1 -2
- package/dist/controllers/folders.js +1 -2
- package/dist/controllers/items.js +3 -4
- package/dist/controllers/notifications.js +1 -2
- package/dist/controllers/operations.js +1 -2
- package/dist/controllers/panels.js +1 -2
- package/dist/controllers/presets.js +1 -2
- package/dist/controllers/roles.js +1 -2
- package/dist/controllers/translations.js +1 -2
- package/dist/controllers/users.js +1 -2
- package/dist/controllers/utils.js +2 -1
- package/dist/database/run-ast.js +4 -3
- package/dist/services/activity.d.ts +2 -1
- package/dist/services/authorization.d.ts +2 -2
- package/dist/services/collections.d.ts +1 -1
- package/dist/services/collections.js +8 -7
- package/dist/services/extensions.js +2 -1
- package/dist/services/fields.js +4 -3
- package/dist/services/files.d.ts +2 -2
- package/dist/services/flows.d.ts +2 -2
- package/dist/services/graphql/index.d.ts +2 -2
- package/dist/services/graphql/index.js +5 -0
- package/dist/services/import-export.js +4 -3
- package/dist/services/items.d.ts +2 -2
- package/dist/services/items.js +9 -8
- package/dist/services/notifications.d.ts +2 -2
- package/dist/services/operations.d.ts +2 -2
- package/dist/services/payload.d.ts +2 -2
- package/dist/services/permissions/index.d.ts +2 -2
- package/dist/services/relations.js +4 -3
- package/dist/services/revisions.d.ts +2 -1
- package/dist/services/roles.d.ts +2 -2
- package/dist/services/roles.js +2 -1
- package/dist/services/shares.d.ts +2 -1
- package/dist/services/shares.js +1 -1
- package/dist/services/tfa.d.ts +2 -1
- package/dist/services/tfa.js +1 -1
- package/dist/services/users.d.ts +2 -2
- package/dist/services/users.js +3 -2
- package/dist/services/utils.d.ts +5 -3
- package/dist/services/utils.js +9 -5
- package/dist/services/versions.d.ts +1 -1
- package/dist/services/webhooks.d.ts +2 -1
- package/dist/types/items.d.ts +1 -8
- package/dist/types/services.d.ts +1 -2
- package/dist/utils/apply-diff.js +2 -1
- package/dist/utils/get-schema.js +21 -12
- package/dist/utils/merge-version-data.js +1 -1
- package/dist/utils/transaction.d.ts +9 -0
- package/dist/utils/transaction.js +15 -0
- package/dist/utils/validate-keys.d.ts +1 -2
- package/dist/websocket/utils/items.d.ts +1 -1
- package/package.json +19 -19
|
@@ -312,8 +312,8 @@ EXTENSIONS_AUTO_RELOAD=false
|
|
|
312
312
|
####################################################################################################
|
|
313
313
|
### Email
|
|
314
314
|
|
|
315
|
-
# Email address from which emails are sent ["no-reply@
|
|
316
|
-
EMAIL_FROM="no-reply@
|
|
315
|
+
# Email address from which emails are sent ["no-reply@example.com"]
|
|
316
|
+
EMAIL_FROM="no-reply@example.com"
|
|
317
317
|
|
|
318
318
|
# What to use to send emails. One of
|
|
319
319
|
# sendmail, smtp, mailgun, sendgrid, ses.
|
|
@@ -180,17 +180,10 @@ asyncHandler(async (req, res) => {
|
|
|
180
180
|
res.setHeader('Content-Length', stat.size);
|
|
181
181
|
return res.end();
|
|
182
182
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
res.
|
|
187
|
-
});
|
|
188
|
-
stream.on('end', () => {
|
|
189
|
-
res.end();
|
|
190
|
-
});
|
|
191
|
-
stream.on('error', (e) => {
|
|
192
|
-
logger.error(e, `Couldn't stream file ${file.id} to the client`);
|
|
193
|
-
if (!isDataSent) {
|
|
183
|
+
stream
|
|
184
|
+
.on('error', (error) => {
|
|
185
|
+
logger.error(error, `Couldn't stream file ${file.id} to the client`);
|
|
186
|
+
if (!res.headersSent) {
|
|
194
187
|
res.removeHeader('Content-Type');
|
|
195
188
|
res.removeHeader('Content-Disposition');
|
|
196
189
|
res.removeHeader('Cache-Control');
|
|
@@ -205,7 +198,11 @@ asyncHandler(async (req, res) => {
|
|
|
205
198
|
],
|
|
206
199
|
});
|
|
207
200
|
}
|
|
208
|
-
|
|
201
|
+
else {
|
|
202
|
+
res.end();
|
|
203
|
+
}
|
|
204
|
+
})
|
|
205
|
+
.pipe(res);
|
|
209
206
|
return undefined;
|
|
210
207
|
}));
|
|
211
208
|
export default router;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { isDirectusError } from '@directus/errors';
|
|
1
|
+
import { ErrorCode, isDirectusError } from '@directus/errors';
|
|
2
2
|
import { Router } from 'express';
|
|
3
|
-
import { ErrorCode } from '@directus/errors';
|
|
4
3
|
import { respond } from '../middleware/respond.js';
|
|
5
4
|
import { validateBatch } from '../middleware/validate-batch.js';
|
|
6
5
|
import { CollectionsService } from '../services/collections.js';
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { isDirectusError } from '@directus/errors';
|
|
1
|
+
import { ErrorCode, isDirectusError } from '@directus/errors';
|
|
2
2
|
import express from 'express';
|
|
3
|
-
import { ErrorCode } from '@directus/errors';
|
|
4
3
|
import { respond } from '../middleware/respond.js';
|
|
5
4
|
import useCollection from '../middleware/use-collection.js';
|
|
6
5
|
import { validateBatch } from '../middleware/validate-batch.js';
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { isDirectusError } from '@directus/errors';
|
|
1
|
+
import { ErrorCode, isDirectusError } from '@directus/errors';
|
|
2
2
|
import express from 'express';
|
|
3
3
|
import { UUID_REGEX } from '../constants.js';
|
|
4
|
-
import { ErrorCode } from '@directus/errors';
|
|
5
4
|
import { getFlowManager } from '../flows.js';
|
|
6
5
|
import { respond } from '../middleware/respond.js';
|
|
7
6
|
import useCollection from '../middleware/use-collection.js';
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { isDirectusError } from '@directus/errors';
|
|
1
|
+
import { ErrorCode, isDirectusError } from '@directus/errors';
|
|
2
2
|
import express from 'express';
|
|
3
|
-
import { ErrorCode } from '@directus/errors';
|
|
4
3
|
import { respond } from '../middleware/respond.js';
|
|
5
4
|
import useCollection from '../middleware/use-collection.js';
|
|
6
5
|
import { validateBatch } from '../middleware/validate-batch.js';
|
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
import { isDirectusError } from '@directus/errors';
|
|
1
|
+
import { ErrorCode, ForbiddenError, RouteNotFoundError, isDirectusError } from '@directus/errors';
|
|
2
|
+
import { isSystemCollection } from '@directus/system-data';
|
|
2
3
|
import express from 'express';
|
|
3
|
-
import { ErrorCode, ForbiddenError, RouteNotFoundError } from '@directus/errors';
|
|
4
4
|
import collectionExists from '../middleware/collection-exists.js';
|
|
5
|
+
import { mergeContentVersions } from '../middleware/merge-content-versions.js';
|
|
5
6
|
import { respond } from '../middleware/respond.js';
|
|
6
7
|
import { validateBatch } from '../middleware/validate-batch.js';
|
|
7
|
-
import { mergeContentVersions } from '../middleware/merge-content-versions.js';
|
|
8
8
|
import { ItemsService } from '../services/items.js';
|
|
9
9
|
import { MetaService } from '../services/meta.js';
|
|
10
10
|
import asyncHandler from '../utils/async-handler.js';
|
|
11
11
|
import { sanitizeQuery } from '../utils/sanitize-query.js';
|
|
12
|
-
import { isSystemCollection } from '@directus/system-data';
|
|
13
12
|
const router = express.Router();
|
|
14
13
|
router.post('/:collection', collectionExists, asyncHandler(async (req, res, next) => {
|
|
15
14
|
if (isSystemCollection(req.params['collection']))
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { isDirectusError } from '@directus/errors';
|
|
1
|
+
import { ErrorCode, isDirectusError } from '@directus/errors';
|
|
2
2
|
import express from 'express';
|
|
3
|
-
import { ErrorCode } from '@directus/errors';
|
|
4
3
|
import { respond } from '../middleware/respond.js';
|
|
5
4
|
import useCollection from '../middleware/use-collection.js';
|
|
6
5
|
import { validateBatch } from '../middleware/validate-batch.js';
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
+
import { ErrorCode, isDirectusError } from '@directus/errors';
|
|
1
2
|
import express from 'express';
|
|
2
|
-
import { isDirectusError } from '@directus/errors';
|
|
3
|
-
import { ErrorCode } from '@directus/errors';
|
|
4
3
|
import { respond } from '../middleware/respond.js';
|
|
5
4
|
import useCollection from '../middleware/use-collection.js';
|
|
6
5
|
import { validateBatch } from '../middleware/validate-batch.js';
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { isDirectusError } from '@directus/errors';
|
|
1
|
+
import { ErrorCode, isDirectusError } from '@directus/errors';
|
|
2
2
|
import express from 'express';
|
|
3
|
-
import { ErrorCode } from '@directus/errors';
|
|
4
3
|
import { respond } from '../middleware/respond.js';
|
|
5
4
|
import useCollection from '../middleware/use-collection.js';
|
|
6
5
|
import { validateBatch } from '../middleware/validate-batch.js';
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { isDirectusError } from '@directus/errors';
|
|
1
|
+
import { ErrorCode, isDirectusError } from '@directus/errors';
|
|
2
2
|
import express from 'express';
|
|
3
|
-
import { ErrorCode } from '@directus/errors';
|
|
4
3
|
import { respond } from '../middleware/respond.js';
|
|
5
4
|
import useCollection from '../middleware/use-collection.js';
|
|
6
5
|
import { validateBatch } from '../middleware/validate-batch.js';
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { isDirectusError } from '@directus/errors';
|
|
1
|
+
import { ErrorCode, isDirectusError } from '@directus/errors';
|
|
2
2
|
import express from 'express';
|
|
3
|
-
import { ErrorCode } from '@directus/errors';
|
|
4
3
|
import { respond } from '../middleware/respond.js';
|
|
5
4
|
import useCollection from '../middleware/use-collection.js';
|
|
6
5
|
import { validateBatch } from '../middleware/validate-batch.js';
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { isDirectusError } from '@directus/errors';
|
|
1
|
+
import { ErrorCode, isDirectusError } from '@directus/errors';
|
|
2
2
|
import express from 'express';
|
|
3
|
-
import { ErrorCode } from '@directus/errors';
|
|
4
3
|
import { respond } from '../middleware/respond.js';
|
|
5
4
|
import useCollection from '../middleware/use-collection.js';
|
|
6
5
|
import { validateBatch } from '../middleware/validate-batch.js';
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { isDirectusError } from '@directus/errors';
|
|
1
|
+
import { ErrorCode, ForbiddenError, InvalidCredentialsError, InvalidPayloadError, isDirectusError, } from '@directus/errors';
|
|
2
2
|
import express from 'express';
|
|
3
3
|
import Joi from 'joi';
|
|
4
|
-
import { ErrorCode, ForbiddenError, InvalidCredentialsError, InvalidPayloadError } from '@directus/errors';
|
|
5
4
|
import { respond } from '../middleware/respond.js';
|
|
6
5
|
import useCollection from '../middleware/use-collection.js';
|
|
7
6
|
import { validateBatch } from '../middleware/validate-batch.js';
|
|
@@ -114,7 +114,8 @@ router.post('/cache/clear', asyncHandler(async (req, res) => {
|
|
|
114
114
|
accountability: req.accountability,
|
|
115
115
|
schema: req.schema,
|
|
116
116
|
});
|
|
117
|
-
|
|
117
|
+
const clearSystemCache = 'system' in req.query && (req.query['system'] === '' || Boolean(req.query['system']));
|
|
118
|
+
await service.clearCache({ system: clearSystemCache });
|
|
118
119
|
res.status(200).end();
|
|
119
120
|
}));
|
|
120
121
|
export default router;
|
package/dist/database/run-ast.js
CHANGED
|
@@ -324,14 +324,15 @@ function mergeWithParentItems(schema, nestedItem, parentItem, nestedNode) {
|
|
|
324
324
|
nestedItem[nestedNode.relation.field]?.[schema.collections[nestedNode.relation.related_collection].primary] == parentItem[schema.collections[nestedNode.relation.related_collection].primary]);
|
|
325
325
|
});
|
|
326
326
|
parentItem[nestedNode.fieldKey].push(...itemChildren);
|
|
327
|
+
const limit = nestedNode.query.limit ?? Number(env['QUERY_LIMIT_DEFAULT']);
|
|
327
328
|
if (nestedNode.query.page && nestedNode.query.page > 1) {
|
|
328
|
-
parentItem[nestedNode.fieldKey] = parentItem[nestedNode.fieldKey].slice(
|
|
329
|
+
parentItem[nestedNode.fieldKey] = parentItem[nestedNode.fieldKey].slice(limit * (nestedNode.query.page - 1));
|
|
329
330
|
}
|
|
330
331
|
if (nestedNode.query.offset && nestedNode.query.offset >= 0) {
|
|
331
332
|
parentItem[nestedNode.fieldKey] = parentItem[nestedNode.fieldKey].slice(nestedNode.query.offset);
|
|
332
333
|
}
|
|
333
|
-
if (
|
|
334
|
-
parentItem[nestedNode.fieldKey] = parentItem[nestedNode.fieldKey].slice(0,
|
|
334
|
+
if (limit !== -1) {
|
|
335
|
+
parentItem[nestedNode.fieldKey] = parentItem[nestedNode.fieldKey].slice(0, limit);
|
|
335
336
|
}
|
|
336
337
|
parentItem[nestedNode.fieldKey] = parentItem[nestedNode.fieldKey].sort((a, b) => {
|
|
337
338
|
// This is pre-filled in get-ast-from-query
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Item, PrimaryKey } from '@directus/types';
|
|
2
|
+
import type { AbstractServiceOptions, MutationOptions } from '../types/index.js';
|
|
2
3
|
import { ItemsService } from './items.js';
|
|
3
4
|
import { NotificationsService } from './notifications.js';
|
|
4
5
|
import { UsersService } from './users.js';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { Accountability, PermissionsAction, SchemaOverview } from '@directus/types';
|
|
1
|
+
import type { Accountability, Item, PermissionsAction, PrimaryKey, SchemaOverview } from '@directus/types';
|
|
2
2
|
import type { Knex } from 'knex';
|
|
3
|
-
import type { AST, AbstractServiceOptions
|
|
3
|
+
import type { AST, AbstractServiceOptions } from '../types/index.js';
|
|
4
4
|
import { PayloadService } from './payload.js';
|
|
5
5
|
export declare class AuthorizationService {
|
|
6
6
|
knex: Knex;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import type { SchemaInspector, Table } from '@directus/schema';
|
|
2
|
+
import { type BaseCollectionMeta } from '@directus/system-data';
|
|
2
3
|
import type { Accountability, RawField, SchemaOverview } from '@directus/types';
|
|
3
4
|
import type Keyv from 'keyv';
|
|
4
5
|
import type { Knex } from 'knex';
|
|
5
6
|
import type { Helpers } from '../database/helpers/index.js';
|
|
6
7
|
import type { AbstractServiceOptions, Collection, MutationOptions } from '../types/index.js';
|
|
7
|
-
import { type BaseCollectionMeta } from '@directus/system-data';
|
|
8
8
|
export type RawCollection = {
|
|
9
9
|
collection: string;
|
|
10
10
|
fields?: RawField[];
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useEnv } from '@directus/env';
|
|
2
2
|
import { ForbiddenError, InvalidPayloadError } from '@directus/errors';
|
|
3
3
|
import { createInspector } from '@directus/schema';
|
|
4
|
+
import { systemCollectionRows } from '@directus/system-data';
|
|
4
5
|
import { addFieldFlag } from '@directus/utils';
|
|
5
6
|
import { chunk, groupBy, merge, omit } from 'lodash-es';
|
|
6
7
|
import { clearSystemCache, getCache } from '../cache.js';
|
|
@@ -10,9 +11,9 @@ import getDatabase, { getSchemaInspector } from '../database/index.js';
|
|
|
10
11
|
import emitter from '../emitter.js';
|
|
11
12
|
import { getSchema } from '../utils/get-schema.js';
|
|
12
13
|
import { shouldClearCache } from '../utils/should-clear-cache.js';
|
|
14
|
+
import { transaction } from '../utils/transaction.js';
|
|
13
15
|
import { FieldsService } from './fields.js';
|
|
14
16
|
import { ItemsService } from './items.js';
|
|
15
|
-
import { systemCollectionRows } from '@directus/system-data';
|
|
16
17
|
export class CollectionsService {
|
|
17
18
|
knex;
|
|
18
19
|
helpers;
|
|
@@ -56,7 +57,7 @@ export class CollectionsService {
|
|
|
56
57
|
// Create the collection/fields in a transaction so it'll be reverted in case of errors or
|
|
57
58
|
// permission problems. This might not work reliably in MySQL, as it doesn't support DDL in
|
|
58
59
|
// transactions.
|
|
59
|
-
await this.knex
|
|
60
|
+
await transaction(this.knex, async (trx) => {
|
|
60
61
|
if (payload.schema) {
|
|
61
62
|
// Directus heavily relies on the primary key of a collection, so we have to make sure that
|
|
62
63
|
// every collection that is created has a primary key. If no primary key field is created
|
|
@@ -167,7 +168,7 @@ export class CollectionsService {
|
|
|
167
168
|
async createMany(payloads, opts) {
|
|
168
169
|
const nestedActionEvents = [];
|
|
169
170
|
try {
|
|
170
|
-
const collections = await this.knex
|
|
171
|
+
const collections = await transaction(this.knex, async (trx) => {
|
|
171
172
|
const service = new CollectionsService({
|
|
172
173
|
schema: this.schema,
|
|
173
174
|
accountability: this.accountability,
|
|
@@ -361,7 +362,7 @@ export class CollectionsService {
|
|
|
361
362
|
const collectionKeys = [];
|
|
362
363
|
const nestedActionEvents = [];
|
|
363
364
|
try {
|
|
364
|
-
await this.knex
|
|
365
|
+
await transaction(this.knex, async (trx) => {
|
|
365
366
|
const collectionItemsService = new CollectionsService({
|
|
366
367
|
knex: trx,
|
|
367
368
|
accountability: this.accountability,
|
|
@@ -406,7 +407,7 @@ export class CollectionsService {
|
|
|
406
407
|
}
|
|
407
408
|
const nestedActionEvents = [];
|
|
408
409
|
try {
|
|
409
|
-
await this.knex
|
|
410
|
+
await transaction(this.knex, async (trx) => {
|
|
410
411
|
const service = new CollectionsService({
|
|
411
412
|
schema: this.schema,
|
|
412
413
|
accountability: this.accountability,
|
|
@@ -453,7 +454,7 @@ export class CollectionsService {
|
|
|
453
454
|
if (!!collectionToBeDeleted === false) {
|
|
454
455
|
throw new ForbiddenError();
|
|
455
456
|
}
|
|
456
|
-
await this.knex
|
|
457
|
+
await transaction(this.knex, async (trx) => {
|
|
457
458
|
if (collectionToBeDeleted.schema) {
|
|
458
459
|
await trx.schema.dropTable(collectionKey);
|
|
459
460
|
}
|
|
@@ -552,7 +553,7 @@ export class CollectionsService {
|
|
|
552
553
|
}
|
|
553
554
|
const nestedActionEvents = [];
|
|
554
555
|
try {
|
|
555
|
-
await this.knex
|
|
556
|
+
await transaction(this.knex, async (trx) => {
|
|
556
557
|
const service = new CollectionsService({
|
|
557
558
|
schema: this.schema,
|
|
558
559
|
accountability: this.accountability,
|
|
@@ -4,6 +4,7 @@ import { describe } from '@directus/extensions-registry';
|
|
|
4
4
|
import { isObject } from '@directus/utils';
|
|
5
5
|
import getDatabase from '../database/index.js';
|
|
6
6
|
import { getExtensionManager } from '../extensions/index.js';
|
|
7
|
+
import { transaction } from '../utils/transaction.js';
|
|
7
8
|
import { ItemsService } from './items.js';
|
|
8
9
|
export class ExtensionReadError extends Error {
|
|
9
10
|
originalError;
|
|
@@ -147,7 +148,7 @@ export class ExtensionsService {
|
|
|
147
148
|
};
|
|
148
149
|
}
|
|
149
150
|
async updateOne(id, data) {
|
|
150
|
-
const result = await this.knex
|
|
151
|
+
const result = await transaction(this.knex, async (trx) => {
|
|
151
152
|
if (!isObject(data.meta)) {
|
|
152
153
|
throw new InvalidPayloadError({ reason: `"meta" is required` });
|
|
153
154
|
}
|
package/dist/services/fields.js
CHANGED
|
@@ -15,6 +15,7 @@ import getLocalType from '../utils/get-local-type.js';
|
|
|
15
15
|
import { getSchema } from '../utils/get-schema.js';
|
|
16
16
|
import { sanitizeColumn } from '../utils/sanitize-schema.js';
|
|
17
17
|
import { shouldClearCache } from '../utils/should-clear-cache.js';
|
|
18
|
+
import { transaction } from '../utils/transaction.js';
|
|
18
19
|
import { ItemsService } from './items.js';
|
|
19
20
|
import { PayloadService } from './payload.js';
|
|
20
21
|
import { RelationsService } from './relations.js';
|
|
@@ -218,7 +219,7 @@ export class FieldsService {
|
|
|
218
219
|
if (flagToAdd) {
|
|
219
220
|
addFieldFlag(field, flagToAdd);
|
|
220
221
|
}
|
|
221
|
-
await this.knex
|
|
222
|
+
await transaction(this.knex, async (trx) => {
|
|
222
223
|
const itemsService = new ItemsService('directus_fields', {
|
|
223
224
|
knex: trx,
|
|
224
225
|
accountability: this.accountability,
|
|
@@ -337,7 +338,7 @@ export class FieldsService {
|
|
|
337
338
|
const columnToCompare = opts?.bypassLimits && opts.autoPurgeSystemCache === false ? sanitizeColumn(existingColumn) : existingColumn;
|
|
338
339
|
if (!isEqual(columnToCompare, hookAdjustedField.schema)) {
|
|
339
340
|
try {
|
|
340
|
-
await this.knex
|
|
341
|
+
await transaction(this.knex, async (trx) => {
|
|
341
342
|
await trx.schema.alterTable(collection, async (table) => {
|
|
342
343
|
if (!hookAdjustedField.schema)
|
|
343
344
|
return;
|
|
@@ -451,7 +452,7 @@ export class FieldsService {
|
|
|
451
452
|
accountability: this.accountability,
|
|
452
453
|
});
|
|
453
454
|
}
|
|
454
|
-
await this.knex
|
|
455
|
+
await transaction(this.knex, async (trx) => {
|
|
455
456
|
const relations = this.schema.relations.filter((relation) => {
|
|
456
457
|
return ((relation.collection === collection && relation.field === field) ||
|
|
457
458
|
(relation.related_collection === collection && relation.meta?.one_field === field));
|
package/dist/services/files.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/// <reference types="node" resolution-mode="require"/>
|
|
2
|
-
import type { BusboyFileStream, File } from '@directus/types';
|
|
2
|
+
import type { BusboyFileStream, File, PrimaryKey } from '@directus/types';
|
|
3
3
|
import type { Readable } from 'node:stream';
|
|
4
|
-
import type { AbstractServiceOptions, MutationOptions
|
|
4
|
+
import type { AbstractServiceOptions, MutationOptions } from '../types/index.js';
|
|
5
5
|
import { ItemsService } from './items.js';
|
|
6
6
|
type Metadata = Partial<Pick<File, 'height' | 'width' | 'description' | 'title' | 'tags' | 'metadata'>>;
|
|
7
7
|
export declare class FilesService extends ItemsService {
|
package/dist/services/flows.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { FlowRaw } from '@directus/types';
|
|
2
|
-
import type { AbstractServiceOptions,
|
|
1
|
+
import type { FlowRaw, Item, PrimaryKey } from '@directus/types';
|
|
2
|
+
import type { AbstractServiceOptions, MutationOptions } from '../types/index.js';
|
|
3
3
|
import { ItemsService } from './items.js';
|
|
4
4
|
export declare class FlowsService extends ItemsService<FlowRaw> {
|
|
5
5
|
constructor(options: AbstractServiceOptions);
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { type DirectusError } from '@directus/errors';
|
|
2
|
-
import type { Accountability, Filter, Query, SchemaOverview } from '@directus/types';
|
|
2
|
+
import type { Accountability, Filter, Item, Query, SchemaOverview } from '@directus/types';
|
|
3
3
|
import type { ArgumentNode, FormattedExecutionResult, FragmentDefinitionNode, GraphQLResolveInfo, SelectionNode } from 'graphql';
|
|
4
4
|
import { GraphQLError, GraphQLSchema } from 'graphql';
|
|
5
5
|
import { ObjectTypeComposer, SchemaComposer } from 'graphql-compose';
|
|
6
6
|
import type { Knex } from 'knex';
|
|
7
|
-
import type { AbstractServiceOptions, GraphQLParams
|
|
7
|
+
import type { AbstractServiceOptions, GraphQLParams } from '../../types/index.js';
|
|
8
8
|
export declare class GraphQLService {
|
|
9
9
|
accountability: Accountability | null;
|
|
10
10
|
knex: Knex;
|
|
@@ -929,6 +929,11 @@ export class GraphQLService {
|
|
|
929
929
|
},
|
|
930
930
|
};
|
|
931
931
|
}
|
|
932
|
+
else {
|
|
933
|
+
resolver.args = {
|
|
934
|
+
version: GraphQLString,
|
|
935
|
+
};
|
|
936
|
+
}
|
|
932
937
|
ReadCollectionTypes[collection.collection].addResolver(resolver);
|
|
933
938
|
ReadCollectionTypes[collection.collection].addResolver({
|
|
934
939
|
name: `${collection.collection}_aggregated`,
|
|
@@ -16,6 +16,7 @@ import getDatabase from '../database/index.js';
|
|
|
16
16
|
import emitter from '../emitter.js';
|
|
17
17
|
import { useLogger } from '../logger.js';
|
|
18
18
|
import { getDateFormatted } from '../utils/get-date-formatted.js';
|
|
19
|
+
import { transaction } from '../utils/transaction.js';
|
|
19
20
|
import { Url } from '../utils/url.js';
|
|
20
21
|
import { userName } from '../utils/user-name.js';
|
|
21
22
|
import { FilesService } from './files.js';
|
|
@@ -54,7 +55,7 @@ export class ImportService {
|
|
|
54
55
|
importJSON(collection, stream) {
|
|
55
56
|
const extractJSON = StreamArray.withParser();
|
|
56
57
|
const nestedActionEvents = [];
|
|
57
|
-
return this.knex
|
|
58
|
+
return transaction(this.knex, (trx) => {
|
|
58
59
|
const service = new ItemsService(collection, {
|
|
59
60
|
knex: trx,
|
|
60
61
|
schema: this.schema,
|
|
@@ -92,7 +93,7 @@ export class ImportService {
|
|
|
92
93
|
if (!tmpFile)
|
|
93
94
|
throw new Error('Failed to create temporary file for import');
|
|
94
95
|
const nestedActionEvents = [];
|
|
95
|
-
return this.knex
|
|
96
|
+
return transaction(this.knex, (trx) => {
|
|
96
97
|
const service = new ItemsService(collection, {
|
|
97
98
|
knex: trx,
|
|
98
99
|
schema: this.schema,
|
|
@@ -211,7 +212,7 @@ export class ExportService {
|
|
|
211
212
|
yaml: 'text/yaml',
|
|
212
213
|
};
|
|
213
214
|
const database = getDatabase();
|
|
214
|
-
await
|
|
215
|
+
await transaction(database, async (trx) => {
|
|
215
216
|
const service = new ItemsService(collection, {
|
|
216
217
|
accountability: this.accountability,
|
|
217
218
|
schema: this.schema,
|
package/dist/services/items.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { Accountability, PermissionsAction, Query, SchemaOverview } from '@directus/types';
|
|
1
|
+
import type { Accountability, Item as AnyItem, PermissionsAction, PrimaryKey, Query, SchemaOverview } from '@directus/types';
|
|
2
2
|
import type Keyv from 'keyv';
|
|
3
3
|
import type { Knex } from 'knex';
|
|
4
|
-
import type { AbstractService, AbstractServiceOptions,
|
|
4
|
+
import type { AbstractService, AbstractServiceOptions, MutationOptions } from '../types/index.js';
|
|
5
5
|
export type QueryOptions = {
|
|
6
6
|
stripNonRequested?: boolean;
|
|
7
7
|
permissionsAction?: PermissionsAction;
|
package/dist/services/items.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Action } from '@directus/constants';
|
|
2
2
|
import { useEnv } from '@directus/env';
|
|
3
3
|
import { ForbiddenError, InvalidPayloadError } from '@directus/errors';
|
|
4
|
+
import { isSystemCollection } from '@directus/system-data';
|
|
4
5
|
import { assign, clone, cloneDeep, omit, pick, without } from 'lodash-es';
|
|
5
6
|
import { getCache } from '../cache.js';
|
|
6
7
|
import { translateDatabaseError } from '../database/errors/translate.js';
|
|
@@ -10,10 +11,10 @@ import runAST from '../database/run-ast.js';
|
|
|
10
11
|
import emitter from '../emitter.js';
|
|
11
12
|
import getASTFromQuery from '../utils/get-ast-from-query.js';
|
|
12
13
|
import { shouldClearCache } from '../utils/should-clear-cache.js';
|
|
14
|
+
import { transaction } from '../utils/transaction.js';
|
|
13
15
|
import { validateKeys } from '../utils/validate-keys.js';
|
|
14
16
|
import { AuthorizationService } from './authorization.js';
|
|
15
17
|
import { PayloadService } from './payload.js';
|
|
16
|
-
import { isSystemCollection } from '@directus/system-data';
|
|
17
18
|
const env = useEnv();
|
|
18
19
|
export class ItemsService {
|
|
19
20
|
collection;
|
|
@@ -82,7 +83,7 @@ export class ItemsService {
|
|
|
82
83
|
// changes in the DB if any of the parts contained within throws an error. This also means
|
|
83
84
|
// that any errors thrown in any nested relational changes will bubble up and cancel the whole
|
|
84
85
|
// update tree
|
|
85
|
-
const primaryKey = await this.knex
|
|
86
|
+
const primaryKey = await transaction(this.knex, async (trx) => {
|
|
86
87
|
// We're creating new services instances so they can use the transaction as their Knex interface
|
|
87
88
|
const payloadService = new PayloadService(this.collection, {
|
|
88
89
|
accountability: this.accountability,
|
|
@@ -255,11 +256,11 @@ export class ItemsService {
|
|
|
255
256
|
async createMany(data, opts = {}) {
|
|
256
257
|
if (!opts.mutationTracker)
|
|
257
258
|
opts.mutationTracker = this.createMutationTracker();
|
|
258
|
-
const { primaryKeys, nestedActionEvents } = await this.knex
|
|
259
|
+
const { primaryKeys, nestedActionEvents } = await transaction(this.knex, async (knex) => {
|
|
259
260
|
const service = new ItemsService(this.collection, {
|
|
260
261
|
accountability: this.accountability,
|
|
261
262
|
schema: this.schema,
|
|
262
|
-
knex:
|
|
263
|
+
knex: knex,
|
|
263
264
|
});
|
|
264
265
|
const primaryKeys = [];
|
|
265
266
|
const nestedActionEvents = [];
|
|
@@ -418,7 +419,7 @@ export class ItemsService {
|
|
|
418
419
|
const primaryKeyField = this.schema.collections[this.collection].primary;
|
|
419
420
|
const keys = [];
|
|
420
421
|
try {
|
|
421
|
-
await this.knex
|
|
422
|
+
await transaction(this.knex, async (trx) => {
|
|
422
423
|
const service = new ItemsService(this.collection, {
|
|
423
424
|
accountability: this.accountability,
|
|
424
425
|
knex: trx,
|
|
@@ -488,7 +489,7 @@ export class ItemsService {
|
|
|
488
489
|
if (opts.preMutationError) {
|
|
489
490
|
throw opts.preMutationError;
|
|
490
491
|
}
|
|
491
|
-
await this.knex
|
|
492
|
+
await transaction(this.knex, async (trx) => {
|
|
492
493
|
const payloadService = new PayloadService(this.collection, {
|
|
493
494
|
accountability: this.accountability,
|
|
494
495
|
knex: trx,
|
|
@@ -629,7 +630,7 @@ export class ItemsService {
|
|
|
629
630
|
async upsertMany(payloads, opts = {}) {
|
|
630
631
|
if (!opts.mutationTracker)
|
|
631
632
|
opts.mutationTracker = this.createMutationTracker();
|
|
632
|
-
const primaryKeys = await this.knex
|
|
633
|
+
const primaryKeys = await transaction(this.knex, async (trx) => {
|
|
633
634
|
const service = new ItemsService(this.collection, {
|
|
634
635
|
accountability: this.accountability,
|
|
635
636
|
schema: this.schema,
|
|
@@ -697,7 +698,7 @@ export class ItemsService {
|
|
|
697
698
|
accountability: this.accountability,
|
|
698
699
|
});
|
|
699
700
|
}
|
|
700
|
-
await this.knex
|
|
701
|
+
await transaction(this.knex, async (trx) => {
|
|
701
702
|
await trx(this.collection).whereIn(primaryKeyField, keys).delete();
|
|
702
703
|
if (this.accountability && this.schema.collections[this.collection].accountability !== null) {
|
|
703
704
|
const activityService = new ActivityService({
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Notification } from '@directus/types';
|
|
2
|
-
import type { AbstractServiceOptions, MutationOptions
|
|
1
|
+
import type { Notification, PrimaryKey } from '@directus/types';
|
|
2
|
+
import type { AbstractServiceOptions, MutationOptions } from '../types/index.js';
|
|
3
3
|
import { ItemsService } from './items.js';
|
|
4
4
|
import { MailService } from './mail/index.js';
|
|
5
5
|
import { UsersService } from './users.js';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { OperationRaw } from '@directus/types';
|
|
2
|
-
import type { AbstractServiceOptions,
|
|
1
|
+
import type { Item, OperationRaw, PrimaryKey } from '@directus/types';
|
|
2
|
+
import type { AbstractServiceOptions, MutationOptions } from '../types/index.js';
|
|
3
3
|
import { ItemsService } from './items.js';
|
|
4
4
|
export declare class OperationsService extends ItemsService<OperationRaw> {
|
|
5
5
|
constructor(options: AbstractServiceOptions);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import type { Accountability, SchemaOverview } from '@directus/types';
|
|
1
|
+
import type { Accountability, Item, PrimaryKey, SchemaOverview } from '@directus/types';
|
|
2
2
|
import type { Knex } from 'knex';
|
|
3
3
|
import type { Helpers } from '../database/helpers/index.js';
|
|
4
|
-
import type { AbstractServiceOptions, ActionEventParams,
|
|
4
|
+
import type { AbstractServiceOptions, ActionEventParams, MutationOptions } from '../types/index.js';
|
|
5
5
|
type Action = 'create' | 'read' | 'update';
|
|
6
6
|
type Transformers = {
|
|
7
7
|
[type: string]: (context: {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { ItemPermissions, PermissionsAction, Query } from '@directus/types';
|
|
1
|
+
import type { Item, ItemPermissions, PermissionsAction, PrimaryKey, Query } from '@directus/types';
|
|
2
2
|
import type Keyv from 'keyv';
|
|
3
|
-
import type { AbstractServiceOptions,
|
|
3
|
+
import type { AbstractServiceOptions, MutationOptions } from '../../types/index.js';
|
|
4
4
|
import type { QueryOptions } from '../items.js';
|
|
5
5
|
import { ItemsService } from '../items.js';
|
|
6
6
|
export declare class PermissionsService extends ItemsService {
|
|
@@ -8,6 +8,7 @@ import getDatabase, { getSchemaInspector } from '../database/index.js';
|
|
|
8
8
|
import emitter from '../emitter.js';
|
|
9
9
|
import { getDefaultIndexName } from '../utils/get-default-index-name.js';
|
|
10
10
|
import { getSchema } from '../utils/get-schema.js';
|
|
11
|
+
import { transaction } from '../utils/transaction.js';
|
|
11
12
|
import { ItemsService } from './items.js';
|
|
12
13
|
import { PermissionsService } from './permissions/index.js';
|
|
13
14
|
export class RelationsService {
|
|
@@ -150,7 +151,7 @@ export class RelationsService {
|
|
|
150
151
|
many_field: relation.field,
|
|
151
152
|
one_collection: relation.related_collection || null,
|
|
152
153
|
};
|
|
153
|
-
await this.knex
|
|
154
|
+
await transaction(this.knex, async (trx) => {
|
|
154
155
|
if (relation.related_collection) {
|
|
155
156
|
await trx.schema.alterTable(relation.collection, async (table) => {
|
|
156
157
|
this.alterType(table, relation, fieldSchema.nullable);
|
|
@@ -221,7 +222,7 @@ export class RelationsService {
|
|
|
221
222
|
this.helpers.schema.preRelationChange(relation);
|
|
222
223
|
const nestedActionEvents = [];
|
|
223
224
|
try {
|
|
224
|
-
await this.knex
|
|
225
|
+
await transaction(this.knex, async (trx) => {
|
|
225
226
|
if (existingRelation.related_collection) {
|
|
226
227
|
await trx.schema.alterTable(collection, async (table) => {
|
|
227
228
|
let constraintName = getDefaultIndexName('foreign', collection, field);
|
|
@@ -308,7 +309,7 @@ export class RelationsService {
|
|
|
308
309
|
const runPostColumnChange = await this.helpers.schema.preColumnChange();
|
|
309
310
|
const nestedActionEvents = [];
|
|
310
311
|
try {
|
|
311
|
-
await this.knex
|
|
312
|
+
await transaction(this.knex, async (trx) => {
|
|
312
313
|
const existingConstraints = await this.schemaInspector.foreignKeys();
|
|
313
314
|
const constraintNames = existingConstraints.map((key) => key.constraint_name);
|
|
314
315
|
if (existingRelation.schema?.constraint_name &&
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Item, PrimaryKey } from '@directus/types';
|
|
2
|
+
import type { AbstractServiceOptions, MutationOptions } from '../types/index.js';
|
|
2
3
|
import { ItemsService } from './items.js';
|
|
3
4
|
export declare class RevisionsService extends ItemsService {
|
|
4
5
|
constructor(options: AbstractServiceOptions);
|
package/dist/services/roles.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Query } from '@directus/types';
|
|
2
|
-
import type { AbstractServiceOptions,
|
|
1
|
+
import type { Item, PrimaryKey, Query } from '@directus/types';
|
|
2
|
+
import type { AbstractServiceOptions, MutationOptions } from '../types/index.js';
|
|
3
3
|
import { ItemsService } from './items.js';
|
|
4
4
|
export declare class RolesService extends ItemsService {
|
|
5
5
|
constructor(options: AbstractServiceOptions);
|
package/dist/services/roles.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ForbiddenError, InvalidPayloadError, UnprocessableContentError } from '@directus/errors';
|
|
2
2
|
import { getMatch } from 'ip-matching';
|
|
3
|
+
import { transaction } from '../utils/transaction.js';
|
|
3
4
|
import { ItemsService } from './items.js';
|
|
4
5
|
import { PermissionsService } from './permissions/index.js';
|
|
5
6
|
import { PresetsService } from './presets.js';
|
|
@@ -217,7 +218,7 @@ export class RolesService extends ItemsService {
|
|
|
217
218
|
catch (err) {
|
|
218
219
|
opts.preMutationError = err;
|
|
219
220
|
}
|
|
220
|
-
await this.knex
|
|
221
|
+
await transaction(this.knex, async (trx) => {
|
|
221
222
|
const itemsService = new ItemsService('directus_roles', {
|
|
222
223
|
knex: trx,
|
|
223
224
|
accountability: this.accountability,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Item, PrimaryKey } from '@directus/types';
|
|
2
|
+
import type { AbstractServiceOptions, LoginResult, MutationOptions } from '../types/index.js';
|
|
2
3
|
import { AuthorizationService } from './authorization.js';
|
|
3
4
|
import { ItemsService } from './items.js';
|
|
4
5
|
export declare class SharesService extends ItemsService {
|
package/dist/services/shares.js
CHANGED
|
@@ -2,6 +2,7 @@ import { useEnv } from '@directus/env';
|
|
|
2
2
|
import { ForbiddenError, InvalidCredentialsError } from '@directus/errors';
|
|
3
3
|
import argon2 from 'argon2';
|
|
4
4
|
import jwt from 'jsonwebtoken';
|
|
5
|
+
import { useLogger } from '../logger.js';
|
|
5
6
|
import { getMilliseconds } from '../utils/get-milliseconds.js';
|
|
6
7
|
import { md } from '../utils/md.js';
|
|
7
8
|
import { Url } from '../utils/url.js';
|
|
@@ -10,7 +11,6 @@ import { AuthorizationService } from './authorization.js';
|
|
|
10
11
|
import { ItemsService } from './items.js';
|
|
11
12
|
import { MailService } from './mail/index.js';
|
|
12
13
|
import { UsersService } from './users.js';
|
|
13
|
-
import { useLogger } from '../logger.js';
|
|
14
14
|
const env = useEnv();
|
|
15
15
|
const logger = useLogger();
|
|
16
16
|
export class SharesService extends ItemsService {
|
package/dist/services/tfa.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import type { PrimaryKey } from '@directus/types';
|
|
1
2
|
import type { Knex } from 'knex';
|
|
2
|
-
import type { AbstractServiceOptions
|
|
3
|
+
import type { AbstractServiceOptions } from '../types/index.js';
|
|
3
4
|
import { ItemsService } from './items.js';
|
|
4
5
|
export declare class TFAService {
|
|
5
6
|
knex: Knex;
|
package/dist/services/tfa.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { InvalidPayloadError } from '@directus/errors';
|
|
1
2
|
import { authenticator } from 'otplib';
|
|
2
3
|
import getDatabase from '../database/index.js';
|
|
3
|
-
import { InvalidPayloadError } from '@directus/errors';
|
|
4
4
|
import { ItemsService } from './items.js';
|
|
5
5
|
export class TFAService {
|
|
6
6
|
knex;
|
package/dist/services/users.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Query } from '@directus/types';
|
|
2
|
-
import type { AbstractServiceOptions,
|
|
1
|
+
import type { Item, PrimaryKey, Query } from '@directus/types';
|
|
2
|
+
import type { AbstractServiceOptions, MutationOptions } from '../types/index.js';
|
|
3
3
|
import { ItemsService } from './items.js';
|
|
4
4
|
export declare class UsersService extends ItemsService {
|
|
5
5
|
constructor(options: AbstractServiceOptions);
|
package/dist/services/users.js
CHANGED
|
@@ -7,14 +7,15 @@ import jwt from 'jsonwebtoken';
|
|
|
7
7
|
import { cloneDeep, isEmpty } from 'lodash-es';
|
|
8
8
|
import { performance } from 'perf_hooks';
|
|
9
9
|
import getDatabase from '../database/index.js';
|
|
10
|
+
import { useLogger } from '../logger.js';
|
|
10
11
|
import isUrlAllowed from '../utils/is-url-allowed.js';
|
|
11
12
|
import { verifyJWT } from '../utils/jwt.js';
|
|
12
13
|
import { stall } from '../utils/stall.js';
|
|
14
|
+
import { transaction } from '../utils/transaction.js';
|
|
13
15
|
import { Url } from '../utils/url.js';
|
|
14
16
|
import { ItemsService } from './items.js';
|
|
15
17
|
import { MailService } from './mail/index.js';
|
|
16
18
|
import { SettingsService } from './settings.js';
|
|
17
|
-
import { useLogger } from '../logger.js';
|
|
18
19
|
const env = useEnv();
|
|
19
20
|
const logger = useLogger();
|
|
20
21
|
export class UsersService extends ItemsService {
|
|
@@ -195,7 +196,7 @@ export class UsersService extends ItemsService {
|
|
|
195
196
|
opts.mutationTracker = this.createMutationTracker();
|
|
196
197
|
const primaryKeyField = this.schema.collections[this.collection].primary;
|
|
197
198
|
const keys = [];
|
|
198
|
-
await this.knex
|
|
199
|
+
await transaction(this.knex, async (trx) => {
|
|
199
200
|
const service = new UsersService({
|
|
200
201
|
accountability: this.accountability,
|
|
201
202
|
knex: trx,
|
package/dist/services/utils.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { Accountability, SchemaOverview } from '@directus/types';
|
|
1
|
+
import type { Accountability, PrimaryKey, SchemaOverview } from '@directus/types';
|
|
2
2
|
import type { Knex } from 'knex';
|
|
3
|
-
import type { AbstractServiceOptions
|
|
3
|
+
import type { AbstractServiceOptions } from '../types/index.js';
|
|
4
4
|
export declare class UtilsService {
|
|
5
5
|
knex: Knex;
|
|
6
6
|
accountability: Accountability | null;
|
|
@@ -10,5 +10,7 @@ export declare class UtilsService {
|
|
|
10
10
|
item: PrimaryKey;
|
|
11
11
|
to: PrimaryKey;
|
|
12
12
|
}): Promise<void>;
|
|
13
|
-
clearCache(
|
|
13
|
+
clearCache({ system }: {
|
|
14
|
+
system: boolean;
|
|
15
|
+
}): Promise<void>;
|
|
14
16
|
}
|
package/dist/services/utils.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import getDatabase from '../database/index.js';
|
|
1
|
+
import { ForbiddenError, InvalidPayloadError } from '@directus/errors';
|
|
3
2
|
import { systemCollectionRows } from '@directus/system-data';
|
|
3
|
+
import { clearSystemCache, getCache } from '../cache.js';
|
|
4
|
+
import getDatabase from '../database/index.js';
|
|
4
5
|
import emitter from '../emitter.js';
|
|
5
|
-
import { ForbiddenError, InvalidPayloadError } from '@directus/errors';
|
|
6
6
|
import { shouldClearCache } from '../utils/should-clear-cache.js';
|
|
7
7
|
export class UtilsService {
|
|
8
8
|
knex;
|
|
@@ -112,10 +112,14 @@ export class UtilsService {
|
|
|
112
112
|
accountability: this.accountability,
|
|
113
113
|
});
|
|
114
114
|
}
|
|
115
|
-
async clearCache() {
|
|
115
|
+
async clearCache({ system }) {
|
|
116
116
|
if (this.accountability?.admin !== true) {
|
|
117
117
|
throw new ForbiddenError();
|
|
118
118
|
}
|
|
119
|
-
|
|
119
|
+
const { cache } = getCache();
|
|
120
|
+
if (system) {
|
|
121
|
+
await clearSystemCache({ forced: true });
|
|
122
|
+
}
|
|
123
|
+
return cache?.clear();
|
|
120
124
|
}
|
|
121
125
|
}
|
|
@@ -17,5 +17,5 @@ export declare class VersionsService extends ItemsService {
|
|
|
17
17
|
createMany(data: Partial<Item>[], opts?: MutationOptions): Promise<PrimaryKey[]>;
|
|
18
18
|
updateMany(keys: PrimaryKey[], data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey[]>;
|
|
19
19
|
save(key: PrimaryKey, data: Partial<Item>): Promise<Partial<Item>>;
|
|
20
|
-
promote(version: PrimaryKey, mainHash: string, fields?: string[]): Promise<
|
|
20
|
+
promote(version: PrimaryKey, mainHash: string, fields?: string[]): Promise<PrimaryKey>;
|
|
21
21
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type DirectusError } from '@directus/errors';
|
|
2
2
|
import type { Bus } from '@directus/memory';
|
|
3
|
-
import type {
|
|
3
|
+
import type { PrimaryKey } from '@directus/types';
|
|
4
|
+
import type { AbstractServiceOptions, MutationOptions, Webhook } from '../types/index.js';
|
|
4
5
|
import { ItemsService } from './items.js';
|
|
5
6
|
export declare class WebhooksService extends ItemsService<Webhook> {
|
|
6
7
|
messenger: Bus;
|
package/dist/types/items.d.ts
CHANGED
|
@@ -1,13 +1,6 @@
|
|
|
1
1
|
import type { DirectusError } from '@directus/errors';
|
|
2
|
-
import type { EventContext } from '@directus/types';
|
|
2
|
+
import type { EventContext, PrimaryKey } from '@directus/types';
|
|
3
3
|
import type { MutationTracker } from '../services/items.js';
|
|
4
|
-
export type Item = Record<string, any>;
|
|
5
|
-
export type PrimaryKey = string | number;
|
|
6
|
-
export type Alterations<T extends Item = Item, K extends keyof T | undefined = undefined> = {
|
|
7
|
-
create: Partial<T>[];
|
|
8
|
-
update: (K extends keyof T ? Partial<T> & Pick<T, K> : Partial<T>)[];
|
|
9
|
-
delete: (K extends keyof T ? T[K] : PrimaryKey)[];
|
|
10
|
-
};
|
|
11
4
|
export type MutationOptions = {
|
|
12
5
|
/**
|
|
13
6
|
* Callback function that's fired whenever a revision is made in the mutation
|
package/dist/types/services.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import type { Accountability, Query, SchemaOverview } from '@directus/types';
|
|
1
|
+
import type { Accountability, Item, PrimaryKey, Query, SchemaOverview } from '@directus/types';
|
|
2
2
|
import type { Knex } from 'knex';
|
|
3
|
-
import type { Item, PrimaryKey } from './items.js';
|
|
4
3
|
export type AbstractServiceOptions = {
|
|
5
4
|
knex?: Knex | undefined;
|
|
6
5
|
accountability?: Accountability | null | undefined;
|
package/dist/utils/apply-diff.js
CHANGED
|
@@ -9,6 +9,7 @@ import { CollectionsService } from '../services/collections.js';
|
|
|
9
9
|
import { FieldsService } from '../services/fields.js';
|
|
10
10
|
import { RelationsService } from '../services/relations.js';
|
|
11
11
|
import { DiffKind } from '../types/index.js';
|
|
12
|
+
import { transaction } from '../utils/transaction.js';
|
|
12
13
|
import { getSchema } from './get-schema.js';
|
|
13
14
|
const logger = useLogger();
|
|
14
15
|
export async function applyDiff(currentSnapshot, snapshotDiff, options) {
|
|
@@ -22,7 +23,7 @@ export async function applyDiff(currentSnapshot, snapshotDiff, options) {
|
|
|
22
23
|
bypassLimits: true,
|
|
23
24
|
};
|
|
24
25
|
const runPostColumnChange = await helpers.schema.preColumnChange();
|
|
25
|
-
await
|
|
26
|
+
await transaction(database, async (trx) => {
|
|
26
27
|
const collectionsService = new CollectionsService({ knex: trx, schema });
|
|
27
28
|
const getNestedCollectionsToCreate = (currentLevelCollection) => snapshotDiff.collections.filter(({ diff }) => diff[0].rhs?.meta?.group === currentLevelCollection);
|
|
28
29
|
const getNestedCollectionsToDelete = (currentLevelCollection) => snapshotDiff.collections.filter(({ diff }) => diff[0].lhs?.meta?.group === currentLevelCollection);
|
package/dist/utils/get-schema.js
CHANGED
|
@@ -34,24 +34,33 @@ export async function getSchema(options, attempt = 0) {
|
|
|
34
34
|
const lockKey = 'schemaCache--preparing';
|
|
35
35
|
const messageKey = 'schemaCache--done';
|
|
36
36
|
const processId = await lock.increment(lockKey);
|
|
37
|
+
if (processId >= env['CACHE_SCHEMA_MAX_ITERATIONS']) {
|
|
38
|
+
await lock.delete(lockKey);
|
|
39
|
+
}
|
|
37
40
|
const currentProcessShouldHandleOperation = processId === 1;
|
|
38
41
|
if (currentProcessShouldHandleOperation === false) {
|
|
39
42
|
logger.trace('Schema cache is prepared in another process, waiting for result.');
|
|
40
|
-
return new Promise((resolve) => {
|
|
43
|
+
return new Promise((resolve, reject) => {
|
|
41
44
|
const TIMEOUT = 10000;
|
|
42
|
-
|
|
43
|
-
const callback = async () => {
|
|
44
|
-
if (timeout)
|
|
45
|
-
clearTimeout(timeout);
|
|
46
|
-
const schema = await getSchema(options, attempt + 1);
|
|
47
|
-
resolve(schema);
|
|
48
|
-
bus.unsubscribe(messageKey, callback);
|
|
49
|
-
};
|
|
50
|
-
bus.subscribe(messageKey, callback);
|
|
51
|
-
timeout = setTimeout(async () => {
|
|
45
|
+
const timeout = setTimeout(() => {
|
|
52
46
|
logger.trace('Did not receive schema callback message in time. Pulling schema...');
|
|
53
|
-
callback();
|
|
47
|
+
callback().catch(reject);
|
|
54
48
|
}, TIMEOUT);
|
|
49
|
+
bus.subscribe(messageKey, callback);
|
|
50
|
+
async function callback() {
|
|
51
|
+
try {
|
|
52
|
+
if (timeout)
|
|
53
|
+
clearTimeout(timeout);
|
|
54
|
+
const schema = await getSchema(options, attempt + 1);
|
|
55
|
+
resolve(schema);
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
reject(error);
|
|
59
|
+
}
|
|
60
|
+
finally {
|
|
61
|
+
bus.unsubscribe(messageKey, callback);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
55
64
|
});
|
|
56
65
|
}
|
|
57
66
|
try {
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Knex } from 'knex';
|
|
2
|
+
/**
|
|
3
|
+
* Execute the given handler within the current transaction or a newly created one
|
|
4
|
+
* if the current knex state isn't a transaction yet.
|
|
5
|
+
*
|
|
6
|
+
* Can be used to ensure the handler is run within a transaction,
|
|
7
|
+
* while preventing nested transactions.
|
|
8
|
+
*/
|
|
9
|
+
export declare const transaction: <T = unknown>(knex: Knex, handler: (knex: Knex) => Promise<T>) => Promise<T>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Execute the given handler within the current transaction or a newly created one
|
|
3
|
+
* if the current knex state isn't a transaction yet.
|
|
4
|
+
*
|
|
5
|
+
* Can be used to ensure the handler is run within a transaction,
|
|
6
|
+
* while preventing nested transactions.
|
|
7
|
+
*/
|
|
8
|
+
export const transaction = (knex, handler) => {
|
|
9
|
+
if (knex.isTransaction) {
|
|
10
|
+
return handler(knex);
|
|
11
|
+
}
|
|
12
|
+
else {
|
|
13
|
+
return knex.transaction((trx) => handler(trx));
|
|
14
|
+
}
|
|
15
|
+
};
|
|
@@ -39,5 +39,5 @@ export declare function getFieldsPayload(subscription: PSubscription, accountabi
|
|
|
39
39
|
* @param event Event data
|
|
40
40
|
* @returns the fetched data
|
|
41
41
|
*/
|
|
42
|
-
export declare function getItemsPayload(subscription: PSubscription, accountability: Accountability | null, schema: SchemaOverview, event?: WebSocketEvent): Promise<string | number | import("
|
|
42
|
+
export declare function getItemsPayload(subscription: PSubscription, accountability: Accountability | null, schema: SchemaOverview, event?: WebSocketEvent): Promise<string | number | import("@directus/types").Item | (string | number)[] | import("@directus/types").Item[]>;
|
|
43
43
|
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@directus/api",
|
|
3
|
-
"version": "19.0.
|
|
3
|
+
"version": "19.0.2",
|
|
4
4
|
"description": "Directus is a real-time API and App dashboard for managing SQL database content",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"directus",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
],
|
|
60
60
|
"dependencies": {
|
|
61
61
|
"@authenio/samlify-node-xmllint": "2.0.0",
|
|
62
|
-
"@aws-sdk/client-ses": "3.
|
|
62
|
+
"@aws-sdk/client-ses": "3.552.0",
|
|
63
63
|
"@godaddy/terminus": "4.12.1",
|
|
64
64
|
"@rollup/plugin-alias": "5.1.0",
|
|
65
65
|
"@rollup/plugin-node-resolve": "15.2.3",
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"@types/cookie": "0.6.0",
|
|
68
68
|
"argon2": "0.40.1",
|
|
69
69
|
"async": "3.2.5",
|
|
70
|
-
"axios": "1.6.
|
|
70
|
+
"axios": "1.6.8",
|
|
71
71
|
"busboy": "1.6.0",
|
|
72
72
|
"bytes": "3.1.2",
|
|
73
73
|
"camelcase": "8.0.0",
|
|
@@ -116,12 +116,12 @@
|
|
|
116
116
|
"minimatch": "9.0.3",
|
|
117
117
|
"mnemonist": "0.39.8",
|
|
118
118
|
"ms": "2.1.3",
|
|
119
|
-
"nanoid": "5.0.
|
|
119
|
+
"nanoid": "5.0.7",
|
|
120
120
|
"node-machine-id": "1.1.12",
|
|
121
121
|
"node-schedule": "2.1.1",
|
|
122
122
|
"nodemailer": "6.9.13",
|
|
123
123
|
"object-hash": "3.0.0",
|
|
124
|
-
"openapi3-ts": "4.
|
|
124
|
+
"openapi3-ts": "4.3.1",
|
|
125
125
|
"openid-client": "5.6.5",
|
|
126
126
|
"ora": "8.0.1",
|
|
127
127
|
"otplib": "12.0.1",
|
|
@@ -146,29 +146,29 @@
|
|
|
146
146
|
"ws": "8.16.0",
|
|
147
147
|
"zod": "3.22.4",
|
|
148
148
|
"zod-validation-error": "3.0.3",
|
|
149
|
-
"@directus/
|
|
150
|
-
"@directus/
|
|
151
|
-
"@directus/
|
|
149
|
+
"@directus/env": "1.1.2",
|
|
150
|
+
"@directus/app": "12.0.2",
|
|
151
|
+
"@directus/extensions": "1.0.3",
|
|
152
152
|
"@directus/constants": "11.0.3",
|
|
153
|
-
"@directus/extensions-registry": "1.0.
|
|
154
|
-
"@directus/
|
|
155
|
-
"@directus/extensions": "
|
|
153
|
+
"@directus/extensions-registry": "1.0.3",
|
|
154
|
+
"@directus/errors": "0.2.4",
|
|
155
|
+
"@directus/extensions-sdk": "11.0.3",
|
|
156
156
|
"@directus/format-title": "10.1.1",
|
|
157
|
-
"@directus/memory": "1.0.6",
|
|
158
157
|
"@directus/pressure": "1.0.18",
|
|
159
158
|
"@directus/schema": "11.0.1",
|
|
159
|
+
"@directus/memory": "1.0.6",
|
|
160
160
|
"@directus/specs": "10.2.8",
|
|
161
|
-
"@directus/storage": "10.0.11",
|
|
162
|
-
"@directus/storage-driver-cloudinary": "10.0.19",
|
|
163
161
|
"@directus/storage-driver-azure": "10.0.19",
|
|
164
162
|
"@directus/storage-driver-gcs": "10.0.19",
|
|
163
|
+
"@directus/storage-driver-cloudinary": "10.0.19",
|
|
165
164
|
"@directus/storage-driver-local": "10.0.18",
|
|
165
|
+
"@directus/storage": "10.0.11",
|
|
166
166
|
"@directus/storage-driver-s3": "10.0.20",
|
|
167
167
|
"@directus/storage-driver-supabase": "1.0.11",
|
|
168
|
-
"@directus/utils": "11.0.7",
|
|
169
168
|
"@directus/system-data": "1.0.2",
|
|
170
|
-
"
|
|
171
|
-
"directus": "
|
|
169
|
+
"directus": "10.10.7",
|
|
170
|
+
"@directus/utils": "11.0.7",
|
|
171
|
+
"@directus/validation": "0.0.14"
|
|
172
172
|
},
|
|
173
173
|
"devDependencies": {
|
|
174
174
|
"@ngneat/falso": "7.2.0",
|
|
@@ -211,8 +211,8 @@
|
|
|
211
211
|
"typescript": "5.3.3",
|
|
212
212
|
"vitest": "1.3.1",
|
|
213
213
|
"@directus/random": "0.2.7",
|
|
214
|
-
"@directus/
|
|
215
|
-
"@directus/
|
|
214
|
+
"@directus/types": "11.1.0",
|
|
215
|
+
"@directus/tsconfig": "1.0.1"
|
|
216
216
|
},
|
|
217
217
|
"optionalDependencies": {
|
|
218
218
|
"@keyv/redis": "2.8.4",
|