@directus/api 11.1.0 → 12.0.1
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/env.js +6 -13
- 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.js +4 -4
- package/dist/middleware/authenticate.js +1 -1
- 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/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 +34 -18
- package/dist/services/fields.js +24 -16
- 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 -2
- package/dist/services/graphql/index.js +30 -12
- 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 -4
- 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/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/synchronization.js +3 -3
- package/dist/types/items.d.ts +2 -2
- package/dist/utils/apply-diff.js +2 -2
- package/dist/utils/apply-query.js +17 -7
- package/dist/utils/get-accountability-for-role.js +1 -2
- package/dist/utils/get-accountability-for-token.js +3 -3
- package/dist/utils/get-column-path.js +5 -3
- package/dist/utils/get-column.js +3 -3
- package/dist/utils/get-service.d.ts +1 -1
- package/dist/utils/get-service.js +1 -1
- package/dist/utils/jwt.js +5 -5
- 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.js +12 -15
- package/dist/websocket/controllers/base.js +18 -15
- package/dist/websocket/controllers/graphql.js +2 -2
- package/dist/websocket/controllers/index.js +3 -7
- package/dist/websocket/controllers/rest.js +3 -3
- package/dist/websocket/{exceptions.d.ts → errors.d.ts} +5 -5
- package/dist/websocket/{exceptions.js → errors.js} +10 -10
- package/dist/websocket/handlers/items.js +5 -5
- package/dist/websocket/handlers/subscribe.js +8 -8
- package/package.json +15 -15
- 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
package/dist/services/fields.js
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { KNEX_TYPES, REGEX_BETWEEN_PARENS } from '@directus/constants';
|
|
2
2
|
import { createInspector } from '@directus/schema';
|
|
3
3
|
import { addFieldFlag, toArray } from '@directus/utils';
|
|
4
|
-
import { isEqual, isNil } from 'lodash-es';
|
|
4
|
+
import { isEqual, isNil, merge } 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);
|
|
@@ -239,8 +241,14 @@ export class FieldsService {
|
|
|
239
241
|
}
|
|
240
242
|
}
|
|
241
243
|
if (hookAdjustedField.meta) {
|
|
244
|
+
const existingSortRecord = await trx
|
|
245
|
+
.from('directus_fields')
|
|
246
|
+
.where(hookAdjustedField.meta?.group ? { collection, group: hookAdjustedField.meta.group } : { collection })
|
|
247
|
+
.max('sort', { as: 'max' })
|
|
248
|
+
.first();
|
|
249
|
+
const newSortValue = existingSortRecord?.max ? existingSortRecord.max + 1 : 1;
|
|
242
250
|
await itemsService.createOne({
|
|
243
|
-
...hookAdjustedField.meta,
|
|
251
|
+
...merge({ sort: newSortValue }, hookAdjustedField.meta),
|
|
244
252
|
collection: collection,
|
|
245
253
|
field: hookAdjustedField.field,
|
|
246
254
|
}, { emitEvents: false });
|
|
@@ -287,7 +295,7 @@ export class FieldsService {
|
|
|
287
295
|
}
|
|
288
296
|
async updateField(collection, field, opts) {
|
|
289
297
|
if (this.accountability && this.accountability.admin !== true) {
|
|
290
|
-
throw new
|
|
298
|
+
throw new ForbiddenError();
|
|
291
299
|
}
|
|
292
300
|
const runPostColumnChange = await this.helpers.schema.preColumnChange();
|
|
293
301
|
const nestedActionEvents = [];
|
|
@@ -307,7 +315,7 @@ export class FieldsService {
|
|
|
307
315
|
(hookAdjustedField.type === 'alias' ||
|
|
308
316
|
this.schema.collections[collection].fields[field.field]?.type === 'alias') &&
|
|
309
317
|
hookAdjustedField.type !== (this.schema.collections[collection].fields[field.field]?.type ?? 'alias')) {
|
|
310
|
-
throw new
|
|
318
|
+
throw new InvalidPayloadError({ reason: 'Alias type cannot be changed' });
|
|
311
319
|
}
|
|
312
320
|
if (hookAdjustedField.schema) {
|
|
313
321
|
const existingColumn = await this.schemaInspector.columnInfo(collection, hookAdjustedField.field);
|
|
@@ -384,7 +392,7 @@ export class FieldsService {
|
|
|
384
392
|
}
|
|
385
393
|
async deleteField(collection, field, opts) {
|
|
386
394
|
if (this.accountability && this.accountability.admin !== true) {
|
|
387
|
-
throw new
|
|
395
|
+
throw new ForbiddenError();
|
|
388
396
|
}
|
|
389
397
|
const runPostColumnChange = await this.helpers.schema.preColumnChange();
|
|
390
398
|
const nestedActionEvents = [];
|
|
@@ -551,7 +559,7 @@ export class FieldsService {
|
|
|
551
559
|
column = table[field.type](field.field);
|
|
552
560
|
}
|
|
553
561
|
else {
|
|
554
|
-
throw new
|
|
562
|
+
throw new InvalidPayloadError({ reason: `Illegal type passed: "${field.type}"` });
|
|
555
563
|
}
|
|
556
564
|
if (field.schema?.default_value !== undefined) {
|
|
557
565
|
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,4 +1,4 @@
|
|
|
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';
|
|
@@ -61,7 +61,7 @@ export declare class GraphQLService {
|
|
|
61
61
|
/**
|
|
62
62
|
* Convert Directus-Exception into a GraphQL format, so it can be returned by GraphQL properly.
|
|
63
63
|
*/
|
|
64
|
-
formatError(error:
|
|
64
|
+
formatError(error: DirectusError | DirectusError[]): GraphQLError;
|
|
65
65
|
/**
|
|
66
66
|
* Replace all fragments in a selectionset for the actual selection set as defined in the fragment
|
|
67
67
|
* Effectively merges the selections with the fragments used in those selections
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Action, FUNCTIONS } from '@directus/constants';
|
|
2
|
+
import { isDirectusError } from '@directus/errors';
|
|
2
3
|
import { parseFilterFunctionPath } from '@directus/utils';
|
|
3
4
|
import argon2 from 'argon2';
|
|
4
5
|
import { GraphQLBoolean, GraphQLEnumType, GraphQLError, GraphQLFloat, GraphQLID, GraphQLInt, GraphQLList, GraphQLNonNull, GraphQLObjectType, GraphQLScalarType, GraphQLSchema, GraphQLString, GraphQLUnionType, NoSchemaIntrospectionCustomRule, execute, specifiedRules, validate, } from 'graphql';
|
|
@@ -8,13 +9,15 @@ import { clearSystemCache, getCache } from '../../cache.js';
|
|
|
8
9
|
import { DEFAULT_AUTH_PROVIDER, GENERATE_SPECIAL } from '../../constants.js';
|
|
9
10
|
import getDatabase from '../../database/index.js';
|
|
10
11
|
import env from '../../env.js';
|
|
11
|
-
import {
|
|
12
|
+
import { ErrorCode, ForbiddenError, InvalidPayloadError } from '../../errors/index.js';
|
|
12
13
|
import { getExtensionManager } from '../../extensions.js';
|
|
13
14
|
import { generateHash } from '../../utils/generate-hash.js';
|
|
14
15
|
import { getGraphQLType } from '../../utils/get-graphql-type.js';
|
|
15
16
|
import { getMilliseconds } from '../../utils/get-milliseconds.js';
|
|
17
|
+
import { getService } from '../../utils/get-service.js';
|
|
16
18
|
import { reduceSchema } from '../../utils/reduce-schema.js';
|
|
17
19
|
import { sanitizeQuery } from '../../utils/sanitize-query.js';
|
|
20
|
+
import { toBoolean } from '../../utils/to-boolean.js';
|
|
18
21
|
import { validateQuery } from '../../utils/validate-query.js';
|
|
19
22
|
import { ActivityService } from '../activity.js';
|
|
20
23
|
import { AuthenticationService } from '../authentication.js';
|
|
@@ -28,6 +31,8 @@ import { SpecificationService } from '../specifications.js';
|
|
|
28
31
|
import { TFAService } from '../tfa.js';
|
|
29
32
|
import { UsersService } from '../users.js';
|
|
30
33
|
import { UtilsService } from '../utils.js';
|
|
34
|
+
import { GraphQLExecutionError, GraphQLValidationError } from './errors/index.js';
|
|
35
|
+
import { createSubscriptionGenerator } from './subscription.js';
|
|
31
36
|
import { GraphQLBigInt } from './types/bigint.js';
|
|
32
37
|
import { GraphQLDate } from './types/date.js';
|
|
33
38
|
import { GraphQLGeoJSON } from './types/geojson.js';
|
|
@@ -36,9 +41,6 @@ import { GraphQLStringOrFloat } from './types/string-or-float.js';
|
|
|
36
41
|
import { GraphQLVoid } from './types/void.js';
|
|
37
42
|
import { addPathToValidationError } from './utils/add-path-to-validation-error.js';
|
|
38
43
|
import processError from './utils/process-error.js';
|
|
39
|
-
import { createSubscriptionGenerator } from './subscription.js';
|
|
40
|
-
import { getService } from '../../utils/get-service.js';
|
|
41
|
-
import { toBoolean } from '../../utils/to-boolean.js';
|
|
42
44
|
const validationRules = Array.from(specifiedRules);
|
|
43
45
|
if (env['GRAPHQL_INTROSPECTION'] === false) {
|
|
44
46
|
validationRules.push(NoSchemaIntrospectionCustomRule);
|
|
@@ -72,7 +74,7 @@ export class GraphQLService {
|
|
|
72
74
|
const schema = this.getSchema();
|
|
73
75
|
const validationErrors = validate(schema, document, validationRules).map((validationError) => addPathToValidationError(validationError));
|
|
74
76
|
if (validationErrors.length > 0) {
|
|
75
|
-
throw new
|
|
77
|
+
throw new GraphQLValidationError({ errors: validationErrors });
|
|
76
78
|
}
|
|
77
79
|
let result;
|
|
78
80
|
try {
|
|
@@ -85,7 +87,7 @@ export class GraphQLService {
|
|
|
85
87
|
});
|
|
86
88
|
}
|
|
87
89
|
catch (err) {
|
|
88
|
-
throw new
|
|
90
|
+
throw new GraphQLExecutionError({ errors: [err.message] });
|
|
89
91
|
}
|
|
90
92
|
const formattedResult = {};
|
|
91
93
|
if (result['data'])
|
|
@@ -431,12 +433,24 @@ export class GraphQLService {
|
|
|
431
433
|
_nstarts_with: {
|
|
432
434
|
type: GraphQLString,
|
|
433
435
|
},
|
|
436
|
+
_istarts_with: {
|
|
437
|
+
type: GraphQLString,
|
|
438
|
+
},
|
|
439
|
+
_nistarts_with: {
|
|
440
|
+
type: GraphQLString,
|
|
441
|
+
},
|
|
434
442
|
_ends_with: {
|
|
435
443
|
type: GraphQLString,
|
|
436
444
|
},
|
|
437
445
|
_nends_with: {
|
|
438
446
|
type: GraphQLString,
|
|
439
447
|
},
|
|
448
|
+
_iends_with: {
|
|
449
|
+
type: GraphQLString,
|
|
450
|
+
},
|
|
451
|
+
_niends_with: {
|
|
452
|
+
type: GraphQLString,
|
|
453
|
+
},
|
|
440
454
|
_in: {
|
|
441
455
|
type: new GraphQLList(GraphQLString),
|
|
442
456
|
},
|
|
@@ -1739,7 +1753,9 @@ export class GraphQLService {
|
|
|
1739
1753
|
});
|
|
1740
1754
|
const currentRefreshToken = args['refresh_token'] || req?.cookies[env['REFRESH_TOKEN_COOKIE_NAME']];
|
|
1741
1755
|
if (!currentRefreshToken) {
|
|
1742
|
-
throw new
|
|
1756
|
+
throw new InvalidPayloadError({
|
|
1757
|
+
reason: `"refresh_token" is required in either the JSON payload or Cookie`,
|
|
1758
|
+
});
|
|
1743
1759
|
}
|
|
1744
1760
|
const result = await authenticationService.refresh(currentRefreshToken);
|
|
1745
1761
|
if (args['mode'] === 'cookie') {
|
|
@@ -1779,7 +1795,9 @@ export class GraphQLService {
|
|
|
1779
1795
|
});
|
|
1780
1796
|
const currentRefreshToken = args['refresh_token'] || req?.cookies[env['REFRESH_TOKEN_COOKIE_NAME']];
|
|
1781
1797
|
if (!currentRefreshToken) {
|
|
1782
|
-
throw new
|
|
1798
|
+
throw new InvalidPayloadError({
|
|
1799
|
+
reason: `"refresh_token" is required in either the JSON payload or Cookie`,
|
|
1800
|
+
});
|
|
1783
1801
|
}
|
|
1784
1802
|
await authenticationService.logout(currentRefreshToken);
|
|
1785
1803
|
return true;
|
|
@@ -1806,7 +1824,7 @@ export class GraphQLService {
|
|
|
1806
1824
|
await service.requestPasswordReset(args['email'], args['reset_url'] || null);
|
|
1807
1825
|
}
|
|
1808
1826
|
catch (err) {
|
|
1809
|
-
if (err
|
|
1827
|
+
if (isDirectusError(err, ErrorCode.InvalidPayload)) {
|
|
1810
1828
|
throw err;
|
|
1811
1829
|
}
|
|
1812
1830
|
}
|
|
@@ -1892,7 +1910,7 @@ export class GraphQLService {
|
|
|
1892
1910
|
});
|
|
1893
1911
|
const otpValid = await service.verifyOTP(this.accountability.user, args['otp']);
|
|
1894
1912
|
if (otpValid === false) {
|
|
1895
|
-
throw new
|
|
1913
|
+
throw new InvalidPayloadError({ reason: `"otp" is invalid` });
|
|
1896
1914
|
}
|
|
1897
1915
|
await service.disableTFA(this.accountability.user);
|
|
1898
1916
|
return true;
|
|
@@ -1906,7 +1924,7 @@ export class GraphQLService {
|
|
|
1906
1924
|
resolve: async (_, args) => {
|
|
1907
1925
|
const { nanoid } = await import('nanoid');
|
|
1908
1926
|
if (args['length'] && Number(args['length']) > 500) {
|
|
1909
|
-
throw new
|
|
1927
|
+
throw new InvalidPayloadError({ reason: `"length" can't be more than 500 characters` });
|
|
1910
1928
|
}
|
|
1911
1929
|
return nanoid(args['length'] ? Number(args['length']) : 32);
|
|
1912
1930
|
},
|
|
@@ -1965,7 +1983,7 @@ export class GraphQLService {
|
|
|
1965
1983
|
type: GraphQLVoid,
|
|
1966
1984
|
resolve: async () => {
|
|
1967
1985
|
if (this.accountability?.admin !== true) {
|
|
1968
|
-
throw new
|
|
1986
|
+
throw new ForbiddenError();
|
|
1969
1987
|
}
|
|
1970
1988
|
const { cache } = getCache();
|
|
1971
1989
|
await cache?.clear();
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isDirectusError } from '@directus/errors';
|
|
2
2
|
import logger from '../../../logger.js';
|
|
3
3
|
const processError = (accountability, error) => {
|
|
4
4
|
logger.error(error);
|
|
5
5
|
const { originalError } = error;
|
|
6
|
-
if (originalError
|
|
6
|
+
if (isDirectusError(originalError)) {
|
|
7
7
|
return {
|
|
8
8
|
message: originalError.message,
|
|
9
9
|
extensions: {
|
|
10
10
|
code: originalError.code,
|
|
11
|
-
...originalError.extensions,
|
|
11
|
+
...(originalError.extensions ?? {}),
|
|
12
12
|
},
|
|
13
13
|
};
|
|
14
14
|
}
|
|
@@ -14,7 +14,7 @@ import { file as createTmpFile } from 'tmp-promise';
|
|
|
14
14
|
import getDatabase from '../database/index.js';
|
|
15
15
|
import emitter from '../emitter.js';
|
|
16
16
|
import env from '../env.js';
|
|
17
|
-
import {
|
|
17
|
+
import { ForbiddenError, InvalidPayloadError, ServiceUnavailableError, UnsupportedMediaTypeError, } from '../errors/index.js';
|
|
18
18
|
import logger from '../logger.js';
|
|
19
19
|
import { getDateFormatted } from '../utils/get-date-formatted.js';
|
|
20
20
|
import { FilesService } from './files.js';
|
|
@@ -31,11 +31,11 @@ export class ImportService {
|
|
|
31
31
|
}
|
|
32
32
|
async import(collection, mimetype, stream) {
|
|
33
33
|
if (this.accountability?.admin !== true && collection.startsWith('directus_'))
|
|
34
|
-
throw new
|
|
34
|
+
throw new ForbiddenError();
|
|
35
35
|
const createPermissions = this.accountability?.permissions?.find((permission) => permission.collection === collection && permission.action === 'create');
|
|
36
36
|
const updatePermissions = this.accountability?.permissions?.find((permission) => permission.collection === collection && permission.action === 'update');
|
|
37
37
|
if (this.accountability?.admin !== true && (!createPermissions || !updatePermissions)) {
|
|
38
|
-
throw new
|
|
38
|
+
throw new ForbiddenError();
|
|
39
39
|
}
|
|
40
40
|
switch (mimetype) {
|
|
41
41
|
case 'application/json':
|
|
@@ -44,7 +44,7 @@ export class ImportService {
|
|
|
44
44
|
case 'application/vnd.ms-excel':
|
|
45
45
|
return await this.importCSV(collection, stream);
|
|
46
46
|
default:
|
|
47
|
-
throw new
|
|
47
|
+
throw new UnsupportedMediaTypeError({ mediaType: mimetype, where: 'file import' });
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
importJSON(collection, stream) {
|
|
@@ -67,7 +67,7 @@ export class ImportService {
|
|
|
67
67
|
extractJSON.on('error', (err) => {
|
|
68
68
|
destroyStream(stream);
|
|
69
69
|
destroyStream(extractJSON);
|
|
70
|
-
reject(new
|
|
70
|
+
reject(new InvalidPayloadError({ reason: err.message }));
|
|
71
71
|
});
|
|
72
72
|
saveQueue.error((err) => {
|
|
73
73
|
reject(err);
|
|
@@ -122,7 +122,7 @@ export class ImportService {
|
|
|
122
122
|
})
|
|
123
123
|
.on('error', (err) => {
|
|
124
124
|
destroyStream(stream);
|
|
125
|
-
reject(new
|
|
125
|
+
reject(new InvalidPayloadError({ reason: err.message }));
|
|
126
126
|
})
|
|
127
127
|
.on('end', () => {
|
|
128
128
|
saveQueue.drain(() => {
|
|
@@ -288,6 +288,6 @@ export class ExportService {
|
|
|
288
288
|
if (format === 'yaml') {
|
|
289
289
|
return toYAML(input);
|
|
290
290
|
}
|
|
291
|
-
throw new
|
|
291
|
+
throw new ServiceUnavailableError({ service: 'export', reason: `Illegal export type used: "${format}"` });
|
|
292
292
|
}
|
|
293
293
|
}
|
package/dist/services/index.d.ts
CHANGED
package/dist/services/index.js
CHANGED
package/dist/services/items.js
CHANGED
|
@@ -6,8 +6,9 @@ import getDatabase from '../database/index.js';
|
|
|
6
6
|
import runAST from '../database/run-ast.js';
|
|
7
7
|
import emitter from '../emitter.js';
|
|
8
8
|
import env from '../env.js';
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
9
|
+
import { ForbiddenError } from '../errors/index.js';
|
|
10
|
+
import { translateDatabaseError } from '../database/errors/translate.js';
|
|
11
|
+
import { InvalidPayloadError } from '../errors/index.js';
|
|
11
12
|
import getASTFromQuery from '../utils/get-ast-from-query.js';
|
|
12
13
|
import { shouldClearCache } from '../utils/should-clear-cache.js';
|
|
13
14
|
import { validateKeys } from '../utils/validate-keys.js';
|
|
@@ -36,7 +37,7 @@ export class ItemsService {
|
|
|
36
37
|
trackMutations(count) {
|
|
37
38
|
mutationCount += count;
|
|
38
39
|
if (mutationCount > maxCount) {
|
|
39
|
-
throw new
|
|
40
|
+
throw new InvalidPayloadError({ reason: `Exceeded max batch mutation limit of ${maxCount}` });
|
|
40
41
|
}
|
|
41
42
|
},
|
|
42
43
|
getCount() {
|
|
@@ -108,8 +109,8 @@ export class ItemsService {
|
|
|
108
109
|
const payloadWithPresets = this.accountability
|
|
109
110
|
? authorizationService.validatePayload('create', this.collection, payloadAfterHooks)
|
|
110
111
|
: payloadAfterHooks;
|
|
111
|
-
if (opts.
|
|
112
|
-
throw opts.
|
|
112
|
+
if (opts.preMutationError) {
|
|
113
|
+
throw opts.preMutationError;
|
|
113
114
|
}
|
|
114
115
|
const { payload: payloadWithM2O, revisions: revisionsM2O, nestedActionEvents: nestedActionEventsM2O, } = await payloadService.processM2O(payloadWithPresets, opts);
|
|
115
116
|
const { payload: payloadWithA2O, revisions: revisionsA2O, nestedActionEvents: nestedActionEventsA2O, } = await payloadService.processA2O(payloadWithM2O, opts);
|
|
@@ -303,7 +304,7 @@ export class ItemsService {
|
|
|
303
304
|
stripNonRequested: opts?.stripNonRequested !== undefined ? opts.stripNonRequested : true,
|
|
304
305
|
});
|
|
305
306
|
if (records === null) {
|
|
306
|
-
throw new
|
|
307
|
+
throw new ForbiddenError();
|
|
307
308
|
}
|
|
308
309
|
const filteredRecords = opts?.emitEvents !== false
|
|
309
310
|
? await emitter.emitFilter(this.eventScope === 'items' ? ['items.read', `${this.collection}.items.read`] : `${this.eventScope}.read`, records, {
|
|
@@ -338,7 +339,7 @@ export class ItemsService {
|
|
|
338
339
|
const queryWithKey = assign({}, query, { filter: filterWithKey });
|
|
339
340
|
const results = await this.readByQuery(queryWithKey, opts);
|
|
340
341
|
if (results.length === 0) {
|
|
341
|
-
throw new
|
|
342
|
+
throw new ForbiddenError();
|
|
342
343
|
}
|
|
343
344
|
return results[0];
|
|
344
345
|
}
|
|
@@ -380,7 +381,7 @@ export class ItemsService {
|
|
|
380
381
|
*/
|
|
381
382
|
async updateBatch(data, opts = {}) {
|
|
382
383
|
if (!Array.isArray(data)) {
|
|
383
|
-
throw new
|
|
384
|
+
throw new InvalidPayloadError({ reason: 'Input should be an array of items' });
|
|
384
385
|
}
|
|
385
386
|
if (!opts.mutationTracker)
|
|
386
387
|
opts.mutationTracker = this.createMutationTracker();
|
|
@@ -395,7 +396,7 @@ export class ItemsService {
|
|
|
395
396
|
});
|
|
396
397
|
for (const item of data) {
|
|
397
398
|
if (!item[primaryKeyField])
|
|
398
|
-
throw new
|
|
399
|
+
throw new InvalidPayloadError({ reason: `Item in update misses primary key` });
|
|
399
400
|
const combinedOpts = Object.assign({ autoPurgeCache: false }, opts);
|
|
400
401
|
keys.push(await service.updateOne(item[primaryKeyField], omit(item, primaryKeyField), combinedOpts));
|
|
401
402
|
}
|
|
@@ -454,8 +455,8 @@ export class ItemsService {
|
|
|
454
455
|
const payloadWithPresets = this.accountability
|
|
455
456
|
? authorizationService.validatePayload('update', this.collection, payloadAfterHooks)
|
|
456
457
|
: payloadAfterHooks;
|
|
457
|
-
if (opts.
|
|
458
|
-
throw opts.
|
|
458
|
+
if (opts.preMutationError) {
|
|
459
|
+
throw opts.preMutationError;
|
|
459
460
|
}
|
|
460
461
|
await this.knex.transaction(async (trx) => {
|
|
461
462
|
const payloadService = new PayloadService(this.collection, {
|
|
@@ -654,8 +655,8 @@ export class ItemsService {
|
|
|
654
655
|
});
|
|
655
656
|
await authorizationService.checkAccess('delete', this.collection, keys);
|
|
656
657
|
}
|
|
657
|
-
if (opts.
|
|
658
|
-
throw opts.
|
|
658
|
+
if (opts.preMutationError) {
|
|
659
|
+
throw opts.preMutationError;
|
|
659
660
|
}
|
|
660
661
|
if (opts.emitEvents !== false) {
|
|
661
662
|
await emitter.emitFilter(this.eventScope === 'items' ? ['items.delete', `${this.collection}.items.delete`] : `${this.eventScope}.delete`, keys, {
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import fse from 'fs-extra';
|
|
2
2
|
import { Liquid } from 'liquidjs';
|
|
3
3
|
import path from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
4
5
|
import getDatabase from '../../database/index.js';
|
|
5
6
|
import env from '../../env.js';
|
|
6
|
-
import {
|
|
7
|
+
import { InvalidPayloadError } from '../../errors/index.js';
|
|
7
8
|
import logger from '../../logger.js';
|
|
8
9
|
import getMailer from '../../mailer.js';
|
|
9
10
|
import { Url } from '../../utils/url.js';
|
|
10
|
-
import { fileURLToPath } from 'url';
|
|
11
11
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
12
12
|
const liquidEngine = new Liquid({
|
|
13
13
|
root: [path.resolve(env['EXTENSIONS_PATH'], 'templates'), path.resolve(__dirname, 'templates')],
|
|
@@ -60,7 +60,7 @@ export class MailService {
|
|
|
60
60
|
const systemTemplatePath = path.join(__dirname, 'templates', template + '.liquid');
|
|
61
61
|
const templatePath = (await fse.pathExists(customTemplatePath)) ? customTemplatePath : systemTemplatePath;
|
|
62
62
|
if ((await fse.pathExists(templatePath)) === false) {
|
|
63
|
-
throw new
|
|
63
|
+
throw new InvalidPayloadError({ reason: `Template "${template}" doesn't exist` });
|
|
64
64
|
}
|
|
65
65
|
const templateString = await fse.readFile(templatePath, 'utf8');
|
|
66
66
|
const html = await liquidEngine.parseAndRender(templateString, variables);
|
package/dist/services/meta.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import getDatabase from '../database/index.js';
|
|
2
|
-
import {
|
|
2
|
+
import { ForbiddenError } from '../errors/index.js';
|
|
3
3
|
import { applyFilter, applySearch } from '../utils/apply-query.js';
|
|
4
4
|
export class MetaService {
|
|
5
5
|
knex;
|
|
@@ -10,7 +10,6 @@ export class MetaService {
|
|
|
10
10
|
this.accountability = options.accountability || null;
|
|
11
11
|
this.schema = options.schema;
|
|
12
12
|
}
|
|
13
|
-
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
|
14
13
|
async getMetaForQuery(collection, query) {
|
|
15
14
|
if (!query || !query.meta)
|
|
16
15
|
return;
|
|
@@ -35,7 +34,7 @@ export class MetaService {
|
|
|
35
34
|
return permission.action === 'read' && permission.collection === collection;
|
|
36
35
|
});
|
|
37
36
|
if (!permissionsRecord)
|
|
38
|
-
throw new
|
|
37
|
+
throw new ForbiddenError();
|
|
39
38
|
const permissions = permissionsRecord.permissions ?? {};
|
|
40
39
|
applyFilter(this.knex, this.schema, dbQuery, permissions, collection, {});
|
|
41
40
|
}
|
|
@@ -50,7 +49,7 @@ export class MetaService {
|
|
|
50
49
|
return permission.action === 'read' && permission.collection === collection;
|
|
51
50
|
});
|
|
52
51
|
if (!permissionsRecord)
|
|
53
|
-
throw new
|
|
52
|
+
throw new ForbiddenError();
|
|
54
53
|
const permissions = permissionsRecord.permissions ?? {};
|
|
55
54
|
if (Object.keys(filter).length > 0) {
|
|
56
55
|
filter = { _and: [permissions, filter] };
|