@directus/api 11.0.1 → 12.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app.js +3 -4
- package/dist/auth/auth.d.ts +4 -4
- package/dist/auth/auth.js +2 -2
- package/dist/auth/drivers/ldap.js +20 -17
- package/dist/auth/drivers/local.js +5 -5
- package/dist/auth/drivers/oauth2.js +16 -16
- package/dist/auth/drivers/openid.js +18 -17
- package/dist/auth/drivers/saml.js +6 -7
- package/dist/auth.js +3 -2
- package/dist/cache.js +3 -13
- package/dist/cli/utils/create-env/env-stub.liquid +5 -7
- package/dist/controllers/activity.js +7 -6
- package/dist/controllers/assets.js +25 -12
- package/dist/controllers/auth.js +8 -7
- package/dist/controllers/collections.js +4 -3
- package/dist/controllers/dashboards.js +5 -4
- package/dist/controllers/extensions.js +3 -3
- package/dist/controllers/fields.js +9 -8
- package/dist/controllers/files.js +11 -11
- package/dist/controllers/flows.js +5 -4
- package/dist/controllers/folders.js +5 -4
- package/dist/controllers/items.js +14 -13
- package/dist/controllers/not-found.js +2 -2
- package/dist/controllers/notifications.js +5 -4
- package/dist/controllers/operations.js +5 -4
- package/dist/controllers/panels.js +5 -4
- package/dist/controllers/permissions.js +5 -4
- package/dist/controllers/presets.js +5 -4
- package/dist/controllers/relations.js +6 -5
- package/dist/controllers/roles.js +5 -4
- package/dist/controllers/schema.js +8 -8
- package/dist/controllers/server.js +2 -2
- package/dist/controllers/settings.js +3 -2
- package/dist/controllers/shares.js +7 -6
- package/dist/controllers/translations.js +6 -5
- package/dist/controllers/users.js +22 -21
- package/dist/controllers/utils.js +10 -10
- package/dist/controllers/webhooks.js +5 -4
- package/dist/{exceptions/database → database/errors}/dialects/mssql.js +8 -18
- package/dist/{exceptions/database → database/errors}/dialects/mysql.js +9 -19
- package/dist/{exceptions/database → database/errors}/dialects/oracle.js +2 -2
- package/dist/{exceptions/database → database/errors}/dialects/postgres.js +7 -18
- package/dist/{exceptions/database → database/errors}/dialects/sqlite.js +7 -10
- package/dist/{exceptions/database → database/errors}/translate.js +1 -1
- package/dist/database/migrations/run.js +10 -1
- package/dist/emitter.d.ts +3 -2
- package/dist/emitter.js +12 -4
- package/dist/env.js +21 -17
- package/dist/errors/codes.d.ts +29 -0
- package/dist/errors/codes.js +30 -0
- package/dist/errors/contains-null-values.d.ts +7 -0
- package/dist/errors/contains-null-values.js +4 -0
- package/dist/errors/content-too-large.d.ts +1 -0
- package/dist/errors/content-too-large.js +3 -0
- package/dist/errors/forbidden.d.ts +1 -0
- package/dist/errors/forbidden.js +3 -0
- package/dist/errors/hit-rate-limit.d.ts +6 -0
- package/dist/errors/hit-rate-limit.js +8 -0
- package/dist/errors/illegal-asset-transformation.d.ts +4 -0
- package/dist/errors/illegal-asset-transformation.js +3 -0
- package/dist/errors/index.d.ts +28 -0
- package/dist/errors/index.js +28 -0
- package/dist/errors/invalid-credentials.d.ts +1 -0
- package/dist/errors/invalid-credentials.js +3 -0
- package/dist/errors/invalid-foreign-key.d.ts +6 -0
- package/dist/errors/invalid-foreign-key.js +14 -0
- package/dist/errors/invalid-ip.d.ts +1 -0
- package/dist/errors/invalid-ip.js +3 -0
- package/dist/errors/invalid-otp.d.ts +1 -0
- package/dist/errors/invalid-otp.js +3 -0
- package/dist/errors/invalid-payload.d.ts +5 -0
- package/dist/errors/invalid-payload.js +4 -0
- package/dist/errors/invalid-provider-config.d.ts +5 -0
- package/dist/errors/invalid-provider-config.js +3 -0
- package/dist/errors/invalid-provider.d.ts +1 -0
- package/dist/errors/invalid-provider.js +3 -0
- package/dist/errors/invalid-query.d.ts +5 -0
- package/dist/errors/invalid-query.js +4 -0
- package/dist/errors/invalid-token.d.ts +1 -0
- package/dist/errors/invalid-token.js +3 -0
- package/dist/errors/method-not-allowed.d.ts +6 -0
- package/dist/errors/method-not-allowed.js +6 -0
- package/dist/errors/not-null-violation.d.ts +6 -0
- package/dist/errors/not-null-violation.js +14 -0
- package/dist/errors/range-not-satisfiable.d.ts +7 -0
- package/dist/errors/range-not-satisfiable.js +7 -0
- package/dist/errors/record-not-unique.d.ts +6 -0
- package/dist/errors/record-not-unique.js +14 -0
- package/dist/errors/route-not-found.d.ts +5 -0
- package/dist/errors/route-not-found.js +4 -0
- package/dist/errors/service-unavailable.d.ts +7 -0
- package/dist/errors/service-unavailable.js +4 -0
- package/dist/errors/token-expired.d.ts +1 -0
- package/dist/errors/token-expired.js +3 -0
- package/dist/errors/unexpected-response.d.ts +1 -0
- package/dist/errors/unexpected-response.js +3 -0
- package/dist/errors/unprocessable-content.d.ts +5 -0
- package/dist/errors/unprocessable-content.js +4 -0
- package/dist/errors/unsupported-media-type.d.ts +6 -0
- package/dist/errors/unsupported-media-type.js +4 -0
- package/dist/errors/user-suspended.d.ts +1 -0
- package/dist/errors/user-suspended.js +3 -0
- package/dist/errors/value-out-of-range.d.ts +6 -0
- package/dist/errors/value-out-of-range.js +14 -0
- package/dist/errors/value-too-long.d.ts +6 -0
- package/dist/errors/value-too-long.js +14 -0
- package/dist/extensions.js +0 -4
- package/dist/flows.js +6 -8
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -2
- package/dist/messenger.d.ts +3 -3
- package/dist/messenger.js +18 -9
- package/dist/middleware/authenticate.js +2 -38
- package/dist/middleware/check-ip.js +2 -2
- package/dist/middleware/collection-exists.js +2 -2
- package/dist/middleware/error-handler.js +7 -7
- package/dist/middleware/graphql.js +11 -9
- package/dist/middleware/rate-limiter-global.d.ts +2 -2
- package/dist/middleware/rate-limiter-global.js +2 -3
- package/dist/middleware/rate-limiter-ip.d.ts +2 -2
- package/dist/middleware/rate-limiter-ip.js +2 -3
- package/dist/middleware/validate-batch.js +3 -4
- package/dist/rate-limiter.js +2 -9
- package/dist/server.js +10 -0
- package/dist/services/activity.js +3 -2
- package/dist/services/assets.js +9 -10
- package/dist/services/authentication.js +12 -11
- package/dist/services/authorization.d.ts +1 -1
- package/dist/services/authorization.js +16 -16
- package/dist/services/collections.js +17 -16
- package/dist/services/fields.js +16 -14
- package/dist/services/files.js +7 -6
- package/dist/services/graphql/errors/execution.d.ts +6 -0
- package/dist/services/graphql/errors/execution.js +2 -0
- package/dist/services/graphql/errors/index.d.ts +2 -0
- package/dist/services/graphql/errors/index.js +2 -0
- package/dist/services/graphql/errors/validation.d.ts +6 -0
- package/dist/services/graphql/errors/validation.js +2 -0
- package/dist/services/graphql/index.d.ts +2 -8
- package/dist/services/graphql/index.js +125 -66
- package/dist/services/graphql/subscription.d.ts +16 -0
- package/dist/services/graphql/subscription.js +77 -0
- package/dist/services/graphql/utils/process-error.js +3 -3
- package/dist/services/import-export.js +7 -7
- package/dist/services/index.d.ts +1 -0
- package/dist/services/index.js +1 -0
- package/dist/services/items.js +14 -13
- package/dist/services/mail/index.js +3 -3
- package/dist/services/meta.js +3 -3
- package/dist/services/payload.js +11 -7
- package/dist/services/relations.js +32 -22
- package/dist/services/revisions.js +3 -3
- package/dist/services/roles.js +10 -9
- package/dist/services/schema.js +5 -5
- package/dist/services/server.js +24 -0
- package/dist/services/shares.js +4 -4
- package/dist/services/tfa.js +6 -6
- package/dist/services/translations.d.ts +2 -2
- package/dist/services/translations.js +4 -4
- package/dist/services/users.js +26 -29
- package/dist/services/utils.js +4 -4
- package/dist/services/websocket.d.ts +14 -0
- package/dist/services/websocket.js +26 -0
- package/dist/synchronization.js +3 -3
- package/dist/types/items.d.ts +2 -2
- package/dist/utils/apply-diff.js +13 -4
- package/dist/utils/apply-query.js +22 -13
- package/dist/utils/get-accountability-for-role.js +1 -2
- package/dist/utils/get-accountability-for-token.d.ts +2 -0
- package/dist/utils/get-accountability-for-token.js +50 -0
- package/dist/utils/get-column-path.js +5 -3
- package/dist/utils/get-column.js +3 -3
- package/dist/utils/get-service.d.ts +7 -0
- package/dist/utils/get-service.js +49 -0
- package/dist/utils/jwt.js +5 -5
- package/dist/utils/redact.d.ts +4 -0
- package/dist/utils/redact.js +15 -1
- package/dist/utils/to-boolean.d.ts +4 -0
- package/dist/utils/to-boolean.js +6 -0
- package/dist/utils/validate-diff.js +23 -9
- package/dist/utils/validate-keys.js +3 -3
- package/dist/utils/validate-query.d.ts +2 -0
- package/dist/utils/validate-query.js +27 -21
- package/dist/utils/validate-snapshot.js +11 -5
- package/dist/websocket/authenticate.d.ts +6 -0
- package/dist/websocket/authenticate.js +59 -0
- package/dist/websocket/controllers/base.d.ts +42 -0
- package/dist/websocket/controllers/base.js +279 -0
- package/dist/websocket/controllers/graphql.d.ts +12 -0
- package/dist/websocket/controllers/graphql.js +102 -0
- package/dist/websocket/controllers/hooks.d.ts +1 -0
- package/dist/websocket/controllers/hooks.js +122 -0
- package/dist/websocket/controllers/index.d.ts +10 -0
- package/dist/websocket/controllers/index.js +31 -0
- package/dist/websocket/controllers/rest.d.ts +9 -0
- package/dist/websocket/controllers/rest.js +47 -0
- package/dist/websocket/errors.d.ts +16 -0
- package/dist/websocket/errors.js +55 -0
- package/dist/websocket/handlers/heartbeat.d.ts +11 -0
- package/dist/websocket/handlers/heartbeat.js +72 -0
- package/dist/websocket/handlers/index.d.ts +4 -0
- package/dist/websocket/handlers/index.js +11 -0
- package/dist/websocket/handlers/items.d.ts +6 -0
- package/dist/websocket/handlers/items.js +103 -0
- package/dist/websocket/handlers/subscribe.d.ts +43 -0
- package/dist/websocket/handlers/subscribe.js +278 -0
- package/dist/websocket/messages.d.ts +311 -0
- package/dist/websocket/messages.js +96 -0
- package/dist/websocket/types.d.ts +34 -0
- package/dist/websocket/types.js +1 -0
- package/dist/websocket/utils/get-expires-at-for-token.d.ts +1 -0
- package/dist/websocket/utils/get-expires-at-for-token.js +8 -0
- package/dist/websocket/utils/message.d.ts +4 -0
- package/dist/websocket/utils/message.js +27 -0
- package/dist/websocket/utils/wait-for-message.d.ts +4 -0
- package/dist/websocket/utils/wait-for-message.js +45 -0
- package/package.json +21 -16
- package/dist/exceptions/content-too-large.d.ts +0 -4
- package/dist/exceptions/content-too-large.js +0 -6
- package/dist/exceptions/database/contains-null-values.d.ts +0 -9
- package/dist/exceptions/database/contains-null-values.js +0 -6
- package/dist/exceptions/database/invalid-foreign-key.d.ts +0 -10
- package/dist/exceptions/database/invalid-foreign-key.js +0 -11
- package/dist/exceptions/database/not-null-violation.d.ts +0 -9
- package/dist/exceptions/database/not-null-violation.js +0 -6
- package/dist/exceptions/database/record-not-unique.d.ts +0 -10
- package/dist/exceptions/database/record-not-unique.js +0 -11
- package/dist/exceptions/database/value-out-of-range.d.ts +0 -10
- package/dist/exceptions/database/value-out-of-range.js +0 -11
- package/dist/exceptions/database/value-too-long.d.ts +0 -9
- package/dist/exceptions/database/value-too-long.js +0 -11
- package/dist/exceptions/forbidden.d.ts +0 -6
- package/dist/exceptions/forbidden.js +0 -13
- package/dist/exceptions/graphql-validation.d.ts +0 -4
- package/dist/exceptions/graphql-validation.js +0 -6
- package/dist/exceptions/hit-rate-limit.d.ts +0 -9
- package/dist/exceptions/hit-rate-limit.js +0 -6
- package/dist/exceptions/illegal-asset-transformation.d.ts +0 -4
- package/dist/exceptions/illegal-asset-transformation.js +0 -6
- package/dist/exceptions/index.d.ts +0 -21
- package/dist/exceptions/index.js +0 -21
- package/dist/exceptions/invalid-config.d.ts +0 -4
- package/dist/exceptions/invalid-config.js +0 -6
- package/dist/exceptions/invalid-credentials.d.ts +0 -4
- package/dist/exceptions/invalid-credentials.js +0 -6
- package/dist/exceptions/invalid-ip.d.ts +0 -4
- package/dist/exceptions/invalid-ip.js +0 -6
- package/dist/exceptions/invalid-otp.d.ts +0 -4
- package/dist/exceptions/invalid-otp.js +0 -6
- package/dist/exceptions/invalid-payload.d.ts +0 -4
- package/dist/exceptions/invalid-payload.js +0 -6
- package/dist/exceptions/invalid-provider.d.ts +0 -4
- package/dist/exceptions/invalid-provider.js +0 -6
- package/dist/exceptions/invalid-query.d.ts +0 -4
- package/dist/exceptions/invalid-query.js +0 -6
- package/dist/exceptions/invalid-token.d.ts +0 -4
- package/dist/exceptions/invalid-token.js +0 -6
- package/dist/exceptions/method-not-allowed.d.ts +0 -8
- package/dist/exceptions/method-not-allowed.js +0 -6
- package/dist/exceptions/range-not-satisfiable.d.ts +0 -5
- package/dist/exceptions/range-not-satisfiable.js +0 -9
- package/dist/exceptions/route-not-found.d.ts +0 -4
- package/dist/exceptions/route-not-found.js +0 -6
- package/dist/exceptions/service-unavailable.d.ts +0 -9
- package/dist/exceptions/service-unavailable.js +0 -6
- package/dist/exceptions/token-expired.d.ts +0 -4
- package/dist/exceptions/token-expired.js +0 -6
- package/dist/exceptions/unexpected-response.d.ts +0 -4
- package/dist/exceptions/unexpected-response.js +0 -6
- package/dist/exceptions/unprocessable-entity.d.ts +0 -4
- package/dist/exceptions/unprocessable-entity.js +0 -6
- package/dist/exceptions/unsupported-media-type.d.ts +0 -4
- package/dist/exceptions/unsupported-media-type.js +0 -6
- package/dist/exceptions/user-suspended.d.ts +0 -4
- package/dist/exceptions/user-suspended.js +0 -6
- /package/dist/{exceptions/database → database/errors}/dialects/mssql.d.ts +0 -0
- /package/dist/{exceptions/database → database/errors}/dialects/mysql.d.ts +0 -0
- /package/dist/{exceptions/database → database/errors}/dialects/oracle.d.ts +0 -0
- /package/dist/{exceptions/database → database/errors}/dialects/postgres.d.ts +0 -0
- /package/dist/{exceptions/database → database/errors}/dialects/sqlite.d.ts +0 -0
- /package/dist/{exceptions/database → database/errors}/dialects/types.d.ts +0 -0
- /package/dist/{exceptions/database → database/errors}/dialects/types.js +0 -0
- /package/dist/{exceptions/database → database/errors}/translate.d.ts +0 -0
|
@@ -8,7 +8,7 @@ import getDatabase, { getSchemaInspector } from '../database/index.js';
|
|
|
8
8
|
import { systemCollectionRows } from '../database/system-data/collections/index.js';
|
|
9
9
|
import emitter from '../emitter.js';
|
|
10
10
|
import env from '../env.js';
|
|
11
|
-
import {
|
|
11
|
+
import { ForbiddenError, InvalidPayloadError } from '../errors/index.js';
|
|
12
12
|
import { FieldsService } from '../services/fields.js';
|
|
13
13
|
import { ItemsService } from '../services/items.js';
|
|
14
14
|
import { getSchema } from '../utils/get-schema.js';
|
|
@@ -36,12 +36,12 @@ export class CollectionsService {
|
|
|
36
36
|
*/
|
|
37
37
|
async createOne(payload, opts) {
|
|
38
38
|
if (this.accountability && this.accountability.admin !== true) {
|
|
39
|
-
throw new
|
|
39
|
+
throw new ForbiddenError();
|
|
40
40
|
}
|
|
41
41
|
if (!payload.collection)
|
|
42
|
-
throw new
|
|
42
|
+
throw new InvalidPayloadError({ reason: `"collection" is required` });
|
|
43
43
|
if (payload.collection.startsWith('directus_')) {
|
|
44
|
-
throw new
|
|
44
|
+
throw new InvalidPayloadError({ reason: `Collections can't start with "directus_"` });
|
|
45
45
|
}
|
|
46
46
|
const nestedActionEvents = [];
|
|
47
47
|
try {
|
|
@@ -51,7 +51,7 @@ export class CollectionsService {
|
|
|
51
51
|
...Object.keys(this.schema.collections),
|
|
52
52
|
];
|
|
53
53
|
if (existingCollections.includes(payload.collection)) {
|
|
54
|
-
throw new
|
|
54
|
+
throw new InvalidPayloadError({ reason: `Collection "${payload.collection}" already exists` });
|
|
55
55
|
}
|
|
56
56
|
// Create the collection/fields in a transaction so it'll be reverted in case of errors or
|
|
57
57
|
// permission problems. This might not work reliably in MySQL, as it doesn't support DDL in
|
|
@@ -255,7 +255,7 @@ export class CollectionsService {
|
|
|
255
255
|
async readOne(collectionKey) {
|
|
256
256
|
const result = await this.readMany([collectionKey]);
|
|
257
257
|
if (result.length === 0)
|
|
258
|
-
throw new
|
|
258
|
+
throw new ForbiddenError();
|
|
259
259
|
return result[0];
|
|
260
260
|
}
|
|
261
261
|
/**
|
|
@@ -270,7 +270,7 @@ export class CollectionsService {
|
|
|
270
270
|
const collectionsYouHavePermissionToRead = permissions.map(({ collection }) => collection);
|
|
271
271
|
for (const collectionKey of collectionKeys) {
|
|
272
272
|
if (collectionsYouHavePermissionToRead.includes(collectionKey) === false) {
|
|
273
|
-
throw new
|
|
273
|
+
throw new ForbiddenError();
|
|
274
274
|
}
|
|
275
275
|
}
|
|
276
276
|
}
|
|
@@ -283,7 +283,7 @@ export class CollectionsService {
|
|
|
283
283
|
*/
|
|
284
284
|
async updateOne(collectionKey, data, opts) {
|
|
285
285
|
if (this.accountability && this.accountability.admin !== true) {
|
|
286
|
-
throw new
|
|
286
|
+
throw new ForbiddenError();
|
|
287
287
|
}
|
|
288
288
|
const nestedActionEvents = [];
|
|
289
289
|
try {
|
|
@@ -336,10 +336,10 @@ export class CollectionsService {
|
|
|
336
336
|
*/
|
|
337
337
|
async updateBatch(data, opts) {
|
|
338
338
|
if (this.accountability && this.accountability.admin !== true) {
|
|
339
|
-
throw new
|
|
339
|
+
throw new ForbiddenError();
|
|
340
340
|
}
|
|
341
341
|
if (!Array.isArray(data)) {
|
|
342
|
-
throw new
|
|
342
|
+
throw new InvalidPayloadError({ reason: 'Input should be an array of collection changes' });
|
|
343
343
|
}
|
|
344
344
|
const collectionKey = 'collection';
|
|
345
345
|
const collectionKeys = [];
|
|
@@ -352,8 +352,9 @@ export class CollectionsService {
|
|
|
352
352
|
schema: this.schema,
|
|
353
353
|
});
|
|
354
354
|
for (const payload of data) {
|
|
355
|
-
if (!payload[collectionKey])
|
|
356
|
-
throw new
|
|
355
|
+
if (!payload[collectionKey]) {
|
|
356
|
+
throw new InvalidPayloadError({ reason: `Collection in update misses collection key` });
|
|
357
|
+
}
|
|
357
358
|
await collectionItemsService.updateOne(payload[collectionKey], omit(payload, collectionKey), {
|
|
358
359
|
autoPurgeCache: false,
|
|
359
360
|
autoPurgeSystemCache: false,
|
|
@@ -385,7 +386,7 @@ export class CollectionsService {
|
|
|
385
386
|
*/
|
|
386
387
|
async updateMany(collectionKeys, data, opts) {
|
|
387
388
|
if (this.accountability && this.accountability.admin !== true) {
|
|
388
|
-
throw new
|
|
389
|
+
throw new ForbiddenError();
|
|
389
390
|
}
|
|
390
391
|
const nestedActionEvents = [];
|
|
391
392
|
try {
|
|
@@ -427,14 +428,14 @@ export class CollectionsService {
|
|
|
427
428
|
*/
|
|
428
429
|
async deleteOne(collectionKey, opts) {
|
|
429
430
|
if (this.accountability && this.accountability.admin !== true) {
|
|
430
|
-
throw new
|
|
431
|
+
throw new ForbiddenError();
|
|
431
432
|
}
|
|
432
433
|
const nestedActionEvents = [];
|
|
433
434
|
try {
|
|
434
435
|
const collections = await this.readByQuery();
|
|
435
436
|
const collectionToBeDeleted = collections.find((collection) => collection.collection === collectionKey);
|
|
436
437
|
if (!!collectionToBeDeleted === false) {
|
|
437
|
-
throw new
|
|
438
|
+
throw new ForbiddenError();
|
|
438
439
|
}
|
|
439
440
|
await this.knex.transaction(async (trx) => {
|
|
440
441
|
if (collectionToBeDeleted.schema) {
|
|
@@ -531,7 +532,7 @@ export class CollectionsService {
|
|
|
531
532
|
*/
|
|
532
533
|
async deleteMany(collectionKeys, opts) {
|
|
533
534
|
if (this.accountability && this.accountability.admin !== true) {
|
|
534
|
-
throw new
|
|
535
|
+
throw new ForbiddenError();
|
|
535
536
|
}
|
|
536
537
|
const nestedActionEvents = [];
|
|
537
538
|
try {
|
package/dist/services/fields.js
CHANGED
|
@@ -4,12 +4,12 @@ import { addFieldFlag, toArray } from '@directus/utils';
|
|
|
4
4
|
import { isEqual, isNil } from 'lodash-es';
|
|
5
5
|
import { clearSystemCache, getCache } from '../cache.js';
|
|
6
6
|
import { ALIAS_TYPES } from '../constants.js';
|
|
7
|
+
import { translateDatabaseError } from '../database/errors/translate.js';
|
|
7
8
|
import { getHelpers } from '../database/helpers/index.js';
|
|
8
9
|
import getDatabase, { getSchemaInspector } from '../database/index.js';
|
|
9
10
|
import { systemFieldRows } from '../database/system-data/fields/index.js';
|
|
10
11
|
import emitter from '../emitter.js';
|
|
11
|
-
import {
|
|
12
|
-
import { ForbiddenException, InvalidPayloadException } from '../exceptions/index.js';
|
|
12
|
+
import { ForbiddenError, InvalidPayloadError } from '../errors/index.js';
|
|
13
13
|
import { ItemsService } from '../services/items.js';
|
|
14
14
|
import { PayloadService } from '../services/payload.js';
|
|
15
15
|
import getDefaultValue from '../utils/get-default-value.js';
|
|
@@ -48,7 +48,7 @@ export class FieldsService {
|
|
|
48
48
|
async readAll(collection) {
|
|
49
49
|
let fields;
|
|
50
50
|
if (this.accountability && this.accountability.admin !== true && this.hasReadAccess === false) {
|
|
51
|
-
throw new
|
|
51
|
+
throw new ForbiddenError();
|
|
52
52
|
}
|
|
53
53
|
const nonAuthorizedItemsService = new ItemsService('directus_fields', {
|
|
54
54
|
knex: this.knex,
|
|
@@ -125,7 +125,7 @@ export class FieldsService {
|
|
|
125
125
|
allowedFieldsInCollection[permission.collection] = permission.fields ?? [];
|
|
126
126
|
});
|
|
127
127
|
if (collection && collection in allowedFieldsInCollection === false) {
|
|
128
|
-
throw new
|
|
128
|
+
throw new ForbiddenError();
|
|
129
129
|
}
|
|
130
130
|
return result.filter((field) => {
|
|
131
131
|
if (field.collection in allowedFieldsInCollection === false)
|
|
@@ -151,17 +151,17 @@ export class FieldsService {
|
|
|
151
151
|
async readOne(collection, field) {
|
|
152
152
|
if (this.accountability && this.accountability.admin !== true) {
|
|
153
153
|
if (this.hasReadAccess === false) {
|
|
154
|
-
throw new
|
|
154
|
+
throw new ForbiddenError();
|
|
155
155
|
}
|
|
156
156
|
const permissions = this.accountability.permissions.find((permission) => {
|
|
157
157
|
return permission.action === 'read' && permission.collection === collection;
|
|
158
158
|
});
|
|
159
159
|
if (!permissions || !permissions.fields)
|
|
160
|
-
throw new
|
|
160
|
+
throw new ForbiddenError();
|
|
161
161
|
if (permissions.fields.includes('*') === false) {
|
|
162
162
|
const allowedFields = permissions.fields;
|
|
163
163
|
if (allowedFields.includes(field) === false)
|
|
164
|
-
throw new
|
|
164
|
+
throw new ForbiddenError();
|
|
165
165
|
}
|
|
166
166
|
}
|
|
167
167
|
let column = undefined;
|
|
@@ -179,7 +179,7 @@ export class FieldsService {
|
|
|
179
179
|
// Do nothing
|
|
180
180
|
}
|
|
181
181
|
if (!column && !fieldInfo)
|
|
182
|
-
throw new
|
|
182
|
+
throw new ForbiddenError();
|
|
183
183
|
const type = getLocalType(column, fieldInfo);
|
|
184
184
|
const columnWithCastDefaultValue = column
|
|
185
185
|
? {
|
|
@@ -199,7 +199,7 @@ export class FieldsService {
|
|
|
199
199
|
async createField(collection, field, table, // allows collection creation to
|
|
200
200
|
opts) {
|
|
201
201
|
if (this.accountability && this.accountability.admin !== true) {
|
|
202
|
-
throw new
|
|
202
|
+
throw new ForbiddenError();
|
|
203
203
|
}
|
|
204
204
|
const runPostColumnChange = await this.helpers.schema.preColumnChange();
|
|
205
205
|
const nestedActionEvents = [];
|
|
@@ -208,7 +208,9 @@ export class FieldsService {
|
|
|
208
208
|
isNil(await this.knex.select('id').from('directus_fields').where({ collection, field: field.field }).first()) === false;
|
|
209
209
|
// Check if field already exists, either as a column, or as a row in directus_fields
|
|
210
210
|
if (exists) {
|
|
211
|
-
throw new
|
|
211
|
+
throw new InvalidPayloadError({
|
|
212
|
+
reason: `Field "${field.field}" already exists in collection "${collection}"`,
|
|
213
|
+
});
|
|
212
214
|
}
|
|
213
215
|
// Add flag for specific database type overrides
|
|
214
216
|
const flagToAdd = this.helpers.date.fieldFlagForField(field.type);
|
|
@@ -287,7 +289,7 @@ export class FieldsService {
|
|
|
287
289
|
}
|
|
288
290
|
async updateField(collection, field, opts) {
|
|
289
291
|
if (this.accountability && this.accountability.admin !== true) {
|
|
290
|
-
throw new
|
|
292
|
+
throw new ForbiddenError();
|
|
291
293
|
}
|
|
292
294
|
const runPostColumnChange = await this.helpers.schema.preColumnChange();
|
|
293
295
|
const nestedActionEvents = [];
|
|
@@ -307,7 +309,7 @@ export class FieldsService {
|
|
|
307
309
|
(hookAdjustedField.type === 'alias' ||
|
|
308
310
|
this.schema.collections[collection].fields[field.field]?.type === 'alias') &&
|
|
309
311
|
hookAdjustedField.type !== (this.schema.collections[collection].fields[field.field]?.type ?? 'alias')) {
|
|
310
|
-
throw new
|
|
312
|
+
throw new InvalidPayloadError({ reason: 'Alias type cannot be changed' });
|
|
311
313
|
}
|
|
312
314
|
if (hookAdjustedField.schema) {
|
|
313
315
|
const existingColumn = await this.schemaInspector.columnInfo(collection, hookAdjustedField.field);
|
|
@@ -384,7 +386,7 @@ export class FieldsService {
|
|
|
384
386
|
}
|
|
385
387
|
async deleteField(collection, field, opts) {
|
|
386
388
|
if (this.accountability && this.accountability.admin !== true) {
|
|
387
|
-
throw new
|
|
389
|
+
throw new ForbiddenError();
|
|
388
390
|
}
|
|
389
391
|
const runPostColumnChange = await this.helpers.schema.preColumnChange();
|
|
390
392
|
const nestedActionEvents = [];
|
|
@@ -551,7 +553,7 @@ export class FieldsService {
|
|
|
551
553
|
column = table[field.type](field.field);
|
|
552
554
|
}
|
|
553
555
|
else {
|
|
554
|
-
throw new
|
|
556
|
+
throw new InvalidPayloadError({ reason: `Illegal type passed: "${field.type}"` });
|
|
555
557
|
}
|
|
556
558
|
if (field.schema?.default_value !== undefined) {
|
|
557
559
|
if (typeof field.schema.default_value === 'string' &&
|
package/dist/services/files.js
CHANGED
|
@@ -12,7 +12,7 @@ import url from 'url';
|
|
|
12
12
|
import { SUPPORTED_IMAGE_METADATA_FORMATS } from '../constants.js';
|
|
13
13
|
import emitter from '../emitter.js';
|
|
14
14
|
import env from '../env.js';
|
|
15
|
-
import {
|
|
15
|
+
import { ForbiddenError, InvalidPayloadError, ServiceUnavailableError } from '../errors/index.js';
|
|
16
16
|
import logger from '../logger.js';
|
|
17
17
|
import { getAxios } from '../request/index.js';
|
|
18
18
|
import { getStorage } from '../storage/index.js';
|
|
@@ -67,7 +67,7 @@ export class FilesService extends ItemsService {
|
|
|
67
67
|
logger.warn(`Couldn't save file ${payload.filename_disk}`);
|
|
68
68
|
logger.warn(err);
|
|
69
69
|
await this.deleteOne(primaryKey);
|
|
70
|
-
throw new
|
|
70
|
+
throw new ServiceUnavailableError({ service: 'files', reason: `Couldn't save file ${payload.filename_disk}` });
|
|
71
71
|
}
|
|
72
72
|
const { size } = await storage.location(data.storage).stat(payload.filename_disk);
|
|
73
73
|
payload.filesize = size;
|
|
@@ -202,7 +202,7 @@ export class FilesService extends ItemsService {
|
|
|
202
202
|
async importOne(importURL, body) {
|
|
203
203
|
const fileCreatePermissions = this.accountability?.permissions?.find((permission) => permission.collection === 'directus_files' && permission.action === 'create');
|
|
204
204
|
if (this.accountability && this.accountability?.admin !== true && !fileCreatePermissions) {
|
|
205
|
-
throw new
|
|
205
|
+
throw new ForbiddenError();
|
|
206
206
|
}
|
|
207
207
|
let fileResponse;
|
|
208
208
|
try {
|
|
@@ -213,8 +213,9 @@ export class FilesService extends ItemsService {
|
|
|
213
213
|
}
|
|
214
214
|
catch (err) {
|
|
215
215
|
logger.warn(err, `Couldn't fetch file from URL "${importURL}"`);
|
|
216
|
-
throw new
|
|
216
|
+
throw new ServiceUnavailableError({
|
|
217
217
|
service: 'external-file',
|
|
218
|
+
reason: `Couldn't fetch file from url "${importURL}"`,
|
|
218
219
|
});
|
|
219
220
|
}
|
|
220
221
|
const parsedURL = url.parse(fileResponse.request.res.responseUrl);
|
|
@@ -234,7 +235,7 @@ export class FilesService extends ItemsService {
|
|
|
234
235
|
*/
|
|
235
236
|
async createOne(data, opts) {
|
|
236
237
|
if (!data.type) {
|
|
237
|
-
throw new
|
|
238
|
+
throw new InvalidPayloadError({ reason: `"type" is required` });
|
|
238
239
|
}
|
|
239
240
|
const key = await super.createOne(data, opts);
|
|
240
241
|
return key;
|
|
@@ -253,7 +254,7 @@ export class FilesService extends ItemsService {
|
|
|
253
254
|
const storage = await getStorage();
|
|
254
255
|
const files = await super.readMany(keys, { fields: ['id', 'storage'], limit: -1 });
|
|
255
256
|
if (!files) {
|
|
256
|
-
throw new
|
|
257
|
+
throw new ForbiddenError();
|
|
257
258
|
}
|
|
258
259
|
await super.deleteMany(keys);
|
|
259
260
|
for (const file of files) {
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { DirectusError } from '@directus/errors';
|
|
2
2
|
import type { Accountability, Filter, 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
7
|
import type { AbstractServiceOptions, GraphQLParams, Item } from '../../types/index.js';
|
|
8
|
-
import { ItemsService } from '../items.js';
|
|
9
8
|
export declare class GraphQLService {
|
|
10
9
|
accountability: Accountability | null;
|
|
11
10
|
knex: Knex;
|
|
@@ -62,12 +61,7 @@ export declare class GraphQLService {
|
|
|
62
61
|
/**
|
|
63
62
|
* Convert Directus-Exception into a GraphQL format, so it can be returned by GraphQL properly.
|
|
64
63
|
*/
|
|
65
|
-
formatError(error:
|
|
66
|
-
/**
|
|
67
|
-
* Select the correct service for the given collection. This allows the individual services to run
|
|
68
|
-
* their custom checks (f.e. it allows UsersService to prevent updating TFA secret from outside)
|
|
69
|
-
*/
|
|
70
|
-
getService(collection: string): ItemsService;
|
|
64
|
+
formatError(error: DirectusError | DirectusError[]): GraphQLError;
|
|
71
65
|
/**
|
|
72
66
|
* Replace all fragments in a selectionset for the actual selection set as defined in the fragment
|
|
73
67
|
* Effectively merges the selections with the fragments used in those selections
|