@directus/api 20.0.0-rc.1 → 20.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app.js +9 -4
- package/dist/auth/drivers/ldap.js +4 -4
- package/dist/auth/drivers/local.js +4 -4
- package/dist/auth/drivers/oauth2.js +4 -4
- package/dist/auth/drivers/openid.js +4 -2
- package/dist/cache.js +0 -3
- package/dist/cli/commands/bootstrap/index.js +3 -8
- package/dist/cli/commands/init/index.js +10 -9
- package/dist/cli/utils/defaults.d.ts +11 -4
- package/dist/cli/utils/defaults.js +1 -7
- package/dist/constants.d.ts +9 -1
- package/dist/constants.js +10 -0
- package/dist/controllers/auth.js +16 -5
- package/dist/controllers/permissions.js +2 -14
- package/dist/controllers/roles.js +1 -22
- package/dist/controllers/{access.d.ts → tus.d.ts} +1 -0
- package/dist/controllers/tus.js +72 -0
- package/dist/controllers/users.js +55 -0
- package/dist/database/helpers/fn/types.d.ts +1 -2
- package/dist/database/helpers/fn/types.js +1 -1
- package/dist/database/helpers/geometry/dialects/mssql.d.ts +1 -1
- package/dist/database/helpers/geometry/dialects/mssql.js +2 -4
- package/dist/database/helpers/geometry/dialects/mysql.js +1 -1
- package/dist/database/helpers/geometry/dialects/oracle.d.ts +1 -1
- package/dist/database/helpers/geometry/dialects/oracle.js +3 -5
- package/dist/database/helpers/geometry/types.d.ts +1 -1
- package/dist/database/helpers/geometry/types.js +2 -4
- package/dist/database/index.js +1 -2
- package/dist/database/migrations/20240701A-add-tus-data.js +12 -0
- package/dist/database/{run-ast/types.d.ts → run-ast.d.ts} +9 -3
- package/dist/database/run-ast.js +458 -0
- package/dist/flows.js +4 -3
- package/dist/middleware/authenticate.js +7 -2
- package/dist/middleware/cache.js +1 -1
- package/dist/middleware/check-ip.d.ts +2 -0
- package/dist/middleware/check-ip.js +37 -0
- package/dist/middleware/get-permissions.d.ts +3 -0
- package/dist/middleware/get-permissions.js +10 -0
- package/dist/middleware/respond.js +1 -1
- package/dist/services/activity.js +10 -22
- package/dist/services/assets.d.ts +3 -2
- package/dist/services/assets.js +5 -10
- package/dist/services/authentication.js +26 -32
- package/dist/services/authorization.d.ts +17 -0
- package/dist/services/authorization.js +456 -0
- package/dist/services/collections.js +17 -18
- package/dist/services/fields.d.ts +1 -0
- package/dist/services/fields.js +24 -53
- package/dist/services/files/lib/extract-metadata.d.ts +3 -0
- package/dist/services/files/lib/extract-metadata.js +32 -0
- package/dist/services/files/utils/get-metadata.d.ts +5 -0
- package/dist/services/files/utils/get-metadata.js +107 -0
- package/dist/services/files.d.ts +4 -6
- package/dist/services/files.js +27 -140
- package/dist/services/graphql/index.d.ts +3 -3
- package/dist/services/graphql/index.js +22 -126
- package/dist/services/graphql/subscription.js +4 -2
- package/dist/services/import-export.js +4 -18
- package/dist/services/index.d.ts +2 -3
- package/dist/services/index.js +2 -3
- package/dist/services/items.d.ts +3 -3
- package/dist/services/items.js +44 -115
- package/dist/services/meta.js +23 -60
- package/dist/services/payload.d.ts +10 -9
- package/dist/services/payload.js +3 -18
- package/dist/services/{permissions.d.ts → permissions/index.d.ts} +7 -5
- package/dist/services/{permissions.js → permissions/index.js} +54 -30
- package/dist/{permissions → services/permissions}/lib/with-app-minimal-permissions.d.ts +1 -1
- package/dist/services/permissions/lib/with-app-minimal-permissions.js +13 -0
- package/dist/services/relations.d.ts +6 -0
- package/dist/services/relations.js +29 -26
- package/dist/services/roles.d.ts +12 -4
- package/dist/services/roles.js +424 -57
- package/dist/services/server.js +6 -0
- package/dist/services/shares.d.ts +2 -0
- package/dist/services/shares.js +8 -12
- package/dist/services/specifications.d.ts +2 -2
- package/dist/services/specifications.js +27 -39
- package/dist/services/tus/data-store.d.ts +36 -0
- package/dist/services/tus/data-store.js +216 -0
- package/dist/services/tus/index.d.ts +2 -0
- package/dist/services/tus/index.js +2 -0
- package/dist/services/tus/lockers.d.ts +36 -0
- package/dist/services/tus/lockers.js +83 -0
- package/dist/services/tus/server.d.ts +8 -0
- package/dist/services/tus/server.js +80 -0
- package/dist/services/tus/utils/wait-timeout.d.ts +1 -0
- package/dist/services/tus/utils/wait-timeout.js +13 -0
- package/dist/services/users.d.ts +5 -1
- package/dist/services/users.js +161 -78
- package/dist/services/utils.js +7 -11
- package/dist/services/versions.d.ts +2 -0
- package/dist/services/versions.js +10 -34
- package/dist/storage/register-locations.js +5 -1
- package/dist/telemetry/lib/get-report.js +2 -2
- package/dist/telemetry/utils/check-increased-user-limits.d.ts +7 -0
- package/dist/telemetry/utils/check-increased-user-limits.js +25 -0
- package/dist/telemetry/utils/get-role-counts-by-roles.d.ts +6 -0
- package/dist/telemetry/utils/get-role-counts-by-roles.js +27 -0
- package/dist/telemetry/utils/get-role-counts-by-users.d.ts +11 -0
- package/dist/telemetry/utils/get-role-counts-by-users.js +34 -0
- package/dist/telemetry/utils/get-user-count.d.ts +8 -0
- package/dist/telemetry/utils/get-user-count.js +33 -0
- package/dist/telemetry/utils/get-user-counts-by-roles.d.ts +7 -0
- package/dist/telemetry/utils/get-user-counts-by-roles.js +35 -0
- package/dist/types/ast.d.ts +1 -43
- package/dist/types/items.d.ts +0 -11
- package/dist/utils/apply-query.d.ts +3 -4
- package/dist/utils/apply-query.js +8 -37
- package/dist/utils/get-accountability-for-role.js +25 -16
- package/dist/utils/get-accountability-for-token.js +16 -17
- package/dist/utils/get-ast-from-query.d.ts +13 -0
- package/dist/utils/get-ast-from-query.js +297 -0
- package/dist/utils/get-cache-key.d.ts +1 -1
- package/dist/utils/get-cache-key.js +1 -12
- package/dist/utils/get-column.d.ts +1 -2
- package/dist/utils/get-column.js +0 -1
- package/dist/utils/get-permissions.d.ts +2 -0
- package/dist/utils/get-permissions.js +150 -0
- package/dist/utils/get-service.js +1 -5
- package/dist/utils/merge-permissions-for-share.d.ts +4 -0
- package/dist/utils/merge-permissions-for-share.js +109 -0
- package/dist/utils/merge-permissions.d.ts +3 -0
- package/dist/utils/merge-permissions.js +95 -0
- package/dist/utils/reduce-schema.d.ts +6 -4
- package/dist/utils/reduce-schema.js +34 -14
- package/dist/utils/verify-session-jwt.js +2 -1
- package/dist/websocket/authenticate.d.ts +2 -0
- package/dist/websocket/authenticate.js +12 -0
- package/dist/websocket/controllers/base.d.ts +1 -1
- package/dist/websocket/controllers/base.js +20 -16
- package/dist/websocket/controllers/graphql.js +4 -1
- package/dist/websocket/controllers/hooks.js +0 -4
- package/dist/websocket/controllers/rest.js +2 -0
- package/dist/websocket/handlers/subscribe.js +2 -0
- package/dist/websocket/utils/items.d.ts +1 -1
- package/package.json +39 -37
- package/dist/controllers/access.js +0 -148
- package/dist/controllers/policies.d.ts +0 -2
- package/dist/controllers/policies.js +0 -169
- package/dist/database/get-ast-from-query/get-ast-from-query.d.ts +0 -16
- package/dist/database/get-ast-from-query/get-ast-from-query.js +0 -82
- package/dist/database/get-ast-from-query/lib/convert-wildcards.d.ts +0 -13
- package/dist/database/get-ast-from-query/lib/convert-wildcards.js +0 -69
- package/dist/database/get-ast-from-query/lib/parse-fields.d.ts +0 -15
- package/dist/database/get-ast-from-query/lib/parse-fields.js +0 -190
- package/dist/database/get-ast-from-query/utils/get-deep-query.d.ts +0 -14
- package/dist/database/get-ast-from-query/utils/get-deep-query.js +0 -17
- package/dist/database/get-ast-from-query/utils/get-related-collection.d.ts +0 -2
- package/dist/database/get-ast-from-query/utils/get-related-collection.js +0 -13
- package/dist/database/get-ast-from-query/utils/get-relation.d.ts +0 -2
- package/dist/database/get-ast-from-query/utils/get-relation.js +0 -7
- package/dist/database/migrations/20240619A-permissions-policies.js +0 -164
- package/dist/database/run-ast/lib/get-db-query.d.ts +0 -4
- package/dist/database/run-ast/lib/get-db-query.js +0 -194
- package/dist/database/run-ast/lib/parse-current-level.d.ts +0 -7
- package/dist/database/run-ast/lib/parse-current-level.js +0 -41
- package/dist/database/run-ast/run-ast.d.ts +0 -7
- package/dist/database/run-ast/run-ast.js +0 -107
- package/dist/database/run-ast/types.js +0 -1
- package/dist/database/run-ast/utils/apply-case-when.d.ts +0 -16
- package/dist/database/run-ast/utils/apply-case-when.js +0 -26
- package/dist/database/run-ast/utils/apply-parent-filters.d.ts +0 -3
- package/dist/database/run-ast/utils/apply-parent-filters.js +0 -55
- package/dist/database/run-ast/utils/get-column-pre-processor.d.ts +0 -10
- package/dist/database/run-ast/utils/get-column-pre-processor.js +0 -57
- package/dist/database/run-ast/utils/get-field-alias.d.ts +0 -2
- package/dist/database/run-ast/utils/get-field-alias.js +0 -4
- package/dist/database/run-ast/utils/get-inner-query-column-pre-processor.d.ts +0 -5
- package/dist/database/run-ast/utils/get-inner-query-column-pre-processor.js +0 -23
- package/dist/database/run-ast/utils/merge-with-parent-items.d.ts +0 -3
- package/dist/database/run-ast/utils/merge-with-parent-items.js +0 -87
- package/dist/database/run-ast/utils/remove-temporary-fields.d.ts +0 -3
- package/dist/database/run-ast/utils/remove-temporary-fields.js +0 -73
- package/dist/permissions/cache.d.ts +0 -2
- package/dist/permissions/cache.js +0 -23
- package/dist/permissions/lib/fetch-permissions.d.ts +0 -10
- package/dist/permissions/lib/fetch-permissions.js +0 -55
- package/dist/permissions/lib/fetch-policies.d.ts +0 -7
- package/dist/permissions/lib/fetch-policies.js +0 -28
- package/dist/permissions/lib/fetch-roles-tree.d.ts +0 -3
- package/dist/permissions/lib/fetch-roles-tree.js +0 -28
- package/dist/permissions/lib/with-app-minimal-permissions.js +0 -10
- package/dist/permissions/modules/fetch-accountability-collection-access/fetch-accountability-collection-access.d.ts +0 -7
- package/dist/permissions/modules/fetch-accountability-collection-access/fetch-accountability-collection-access.js +0 -56
- package/dist/permissions/modules/fetch-accountability-policy-globals/fetch-accountability-policy-globals.d.ts +0 -3
- package/dist/permissions/modules/fetch-accountability-policy-globals/fetch-accountability-policy-globals.js +0 -16
- package/dist/permissions/modules/fetch-allowed-collections/fetch-allowed-collections.d.ts +0 -8
- package/dist/permissions/modules/fetch-allowed-collections/fetch-allowed-collections.js +0 -24
- package/dist/permissions/modules/fetch-allowed-field-map/fetch-allowed-field-map.d.ts +0 -9
- package/dist/permissions/modules/fetch-allowed-field-map/fetch-allowed-field-map.js +0 -31
- package/dist/permissions/modules/fetch-allowed-fields/fetch-allowed-fields.d.ts +0 -16
- package/dist/permissions/modules/fetch-allowed-fields/fetch-allowed-fields.js +0 -27
- package/dist/permissions/modules/fetch-global-access/fetch-global-access.d.ts +0 -10
- package/dist/permissions/modules/fetch-global-access/fetch-global-access.js +0 -23
- package/dist/permissions/modules/fetch-global-access/lib/fetch-global-access-for-roles.d.ts +0 -5
- package/dist/permissions/modules/fetch-global-access/lib/fetch-global-access-for-roles.js +0 -7
- package/dist/permissions/modules/fetch-global-access/lib/fetch-global-access-for-user.d.ts +0 -5
- package/dist/permissions/modules/fetch-global-access/lib/fetch-global-access-for-user.js +0 -10
- package/dist/permissions/modules/fetch-global-access/types.d.ts +0 -4
- package/dist/permissions/modules/fetch-global-access/types.js +0 -1
- package/dist/permissions/modules/fetch-global-access/utils/fetch-global-access-for-query.d.ts +0 -4
- package/dist/permissions/modules/fetch-global-access/utils/fetch-global-access-for-query.js +0 -27
- package/dist/permissions/modules/fetch-inconsistent-field-map/fetch-inconsistent-field-map.d.ts +0 -12
- package/dist/permissions/modules/fetch-inconsistent-field-map/fetch-inconsistent-field-map.js +0 -32
- package/dist/permissions/modules/fetch-policies-ip-access/fetch-policies-ip-access.d.ts +0 -4
- package/dist/permissions/modules/fetch-policies-ip-access/fetch-policies-ip-access.js +0 -29
- package/dist/permissions/modules/process-ast/lib/extract-fields-from-children.d.ts +0 -4
- package/dist/permissions/modules/process-ast/lib/extract-fields-from-children.js +0 -49
- package/dist/permissions/modules/process-ast/lib/extract-fields-from-query.d.ts +0 -3
- package/dist/permissions/modules/process-ast/lib/extract-fields-from-query.js +0 -56
- package/dist/permissions/modules/process-ast/lib/field-map-from-ast.d.ts +0 -4
- package/dist/permissions/modules/process-ast/lib/field-map-from-ast.js +0 -8
- package/dist/permissions/modules/process-ast/lib/inject-cases.d.ts +0 -9
- package/dist/permissions/modules/process-ast/lib/inject-cases.js +0 -93
- package/dist/permissions/modules/process-ast/process-ast.d.ts +0 -9
- package/dist/permissions/modules/process-ast/process-ast.js +0 -39
- package/dist/permissions/modules/process-ast/types.d.ts +0 -24
- package/dist/permissions/modules/process-ast/types.js +0 -1
- package/dist/permissions/modules/process-ast/utils/collections-in-field-map.d.ts +0 -2
- package/dist/permissions/modules/process-ast/utils/collections-in-field-map.js +0 -7
- package/dist/permissions/modules/process-ast/utils/dedupe-access.d.ts +0 -12
- package/dist/permissions/modules/process-ast/utils/dedupe-access.js +0 -30
- package/dist/permissions/modules/process-ast/utils/extract-paths-from-query.d.ts +0 -15
- package/dist/permissions/modules/process-ast/utils/extract-paths-from-query.js +0 -50
- package/dist/permissions/modules/process-ast/utils/find-related-collection.d.ts +0 -3
- package/dist/permissions/modules/process-ast/utils/find-related-collection.js +0 -9
- package/dist/permissions/modules/process-ast/utils/flatten-filter.d.ts +0 -3
- package/dist/permissions/modules/process-ast/utils/flatten-filter.js +0 -24
- package/dist/permissions/modules/process-ast/utils/format-a2o-key.d.ts +0 -1
- package/dist/permissions/modules/process-ast/utils/format-a2o-key.js +0 -3
- package/dist/permissions/modules/process-ast/utils/get-info-for-path.d.ts +0 -5
- package/dist/permissions/modules/process-ast/utils/get-info-for-path.js +0 -7
- package/dist/permissions/modules/process-ast/utils/has-item-permissions.d.ts +0 -2
- package/dist/permissions/modules/process-ast/utils/has-item-permissions.js +0 -3
- package/dist/permissions/modules/process-ast/utils/stringify-query-path.d.ts +0 -2
- package/dist/permissions/modules/process-ast/utils/stringify-query-path.js +0 -3
- package/dist/permissions/modules/process-ast/utils/validate-path/create-error.d.ts +0 -3
- package/dist/permissions/modules/process-ast/utils/validate-path/create-error.js +0 -16
- package/dist/permissions/modules/process-ast/utils/validate-path/validate-path-existence.d.ts +0 -2
- package/dist/permissions/modules/process-ast/utils/validate-path/validate-path-existence.js +0 -12
- package/dist/permissions/modules/process-ast/utils/validate-path/validate-path-permissions.d.ts +0 -2
- package/dist/permissions/modules/process-ast/utils/validate-path/validate-path-permissions.js +0 -28
- package/dist/permissions/modules/process-payload/lib/is-field-nullable.d.ts +0 -5
- package/dist/permissions/modules/process-payload/lib/is-field-nullable.js +0 -12
- package/dist/permissions/modules/process-payload/process-payload.d.ts +0 -13
- package/dist/permissions/modules/process-payload/process-payload.js +0 -77
- package/dist/permissions/modules/validate-access/lib/validate-collection-access.d.ts +0 -12
- package/dist/permissions/modules/validate-access/lib/validate-collection-access.js +0 -11
- package/dist/permissions/modules/validate-access/lib/validate-item-access.d.ts +0 -9
- package/dist/permissions/modules/validate-access/lib/validate-item-access.js +0 -33
- package/dist/permissions/modules/validate-access/validate-access.d.ts +0 -14
- package/dist/permissions/modules/validate-access/validate-access.js +0 -28
- package/dist/permissions/modules/validate-remaining-admin/validate-remaining-admin-count.d.ts +0 -1
- package/dist/permissions/modules/validate-remaining-admin/validate-remaining-admin-count.js +0 -8
- package/dist/permissions/modules/validate-remaining-admin/validate-remaining-admin-users.d.ts +0 -5
- package/dist/permissions/modules/validate-remaining-admin/validate-remaining-admin-users.js +0 -10
- package/dist/permissions/types.d.ts +0 -6
- package/dist/permissions/types.js +0 -1
- package/dist/permissions/utils/create-default-accountability.d.ts +0 -2
- package/dist/permissions/utils/create-default-accountability.js +0 -11
- package/dist/permissions/utils/extract-required-dynamic-variable-context.d.ts +0 -8
- package/dist/permissions/utils/extract-required-dynamic-variable-context.js +0 -27
- package/dist/permissions/utils/fetch-dynamic-variable-context.d.ts +0 -9
- package/dist/permissions/utils/fetch-dynamic-variable-context.js +0 -43
- package/dist/permissions/utils/filter-policies-by-ip.d.ts +0 -2
- package/dist/permissions/utils/filter-policies-by-ip.js +0 -15
- package/dist/permissions/utils/get-unaliased-field-key.d.ts +0 -5
- package/dist/permissions/utils/get-unaliased-field-key.js +0 -17
- package/dist/permissions/utils/process-permissions.d.ts +0 -7
- package/dist/permissions/utils/process-permissions.js +0 -9
- package/dist/permissions/utils/with-cache.d.ts +0 -10
- package/dist/permissions/utils/with-cache.js +0 -25
- package/dist/services/access.d.ts +0 -10
- package/dist/services/access.js +0 -43
- package/dist/services/policies.d.ts +0 -12
- package/dist/services/policies.js +0 -87
- package/dist/telemetry/utils/check-user-limits.d.ts +0 -5
- package/dist/telemetry/utils/check-user-limits.js +0 -19
- package/dist/utils/fetch-user-count/fetch-access-lookup.d.ts +0 -17
- package/dist/utils/fetch-user-count/fetch-access-lookup.js +0 -22
- package/dist/utils/fetch-user-count/fetch-access-roles.d.ts +0 -16
- package/dist/utils/fetch-user-count/fetch-access-roles.js +0 -37
- package/dist/utils/fetch-user-count/fetch-active-users.d.ts +0 -6
- package/dist/utils/fetch-user-count/fetch-active-users.js +0 -3
- package/dist/utils/fetch-user-count/fetch-user-count.d.ts +0 -12
- package/dist/utils/fetch-user-count/fetch-user-count.js +0 -57
- package/dist/utils/fetch-user-count/get-user-count-query.d.ts +0 -20
- package/dist/utils/fetch-user-count/get-user-count-query.js +0 -17
- package/dist/utils/validate-user-count-integrity.d.ts +0 -13
- package/dist/utils/validate-user-count-integrity.js +0 -29
- /package/dist/database/migrations/{20240619A-permissions-policies.d.ts → 20240701A-add-tus-data.d.ts} +0 -0
- /package/dist/{utils → services/files/utils}/parse-image-metadata.d.ts +0 -0
- /package/dist/{utils → services/files/utils}/parse-image-metadata.js +0 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { SUPPORTED_IMAGE_METADATA_FORMATS } from '../../../constants.js';
|
|
2
|
+
import { getStorage } from '../../../storage/index.js';
|
|
3
|
+
import { getMetadata } from '../utils/get-metadata.js';
|
|
4
|
+
export async function extractMetadata(storageLocation, data) {
|
|
5
|
+
const storage = await getStorage();
|
|
6
|
+
const fileMeta = {};
|
|
7
|
+
if (data.type && SUPPORTED_IMAGE_METADATA_FORMATS.includes(data.type)) {
|
|
8
|
+
const stream = await storage.location(storageLocation).read(data.filename_disk);
|
|
9
|
+
const { height, width, description, title, tags, metadata } = await getMetadata(stream);
|
|
10
|
+
// Note that if this is a replace file upload, the below properties are fetched and included in the data above
|
|
11
|
+
// in the `existingFile` variable... so this will ONLY set the values if they're not already set
|
|
12
|
+
if (!data.height && height) {
|
|
13
|
+
fileMeta.height = height;
|
|
14
|
+
}
|
|
15
|
+
if (!data.width && width) {
|
|
16
|
+
fileMeta.width = width;
|
|
17
|
+
}
|
|
18
|
+
if (!data.metadata && metadata) {
|
|
19
|
+
fileMeta.metadata = metadata;
|
|
20
|
+
}
|
|
21
|
+
if (!data.description && description) {
|
|
22
|
+
fileMeta.description = description;
|
|
23
|
+
}
|
|
24
|
+
if (!data.title && title) {
|
|
25
|
+
fileMeta.title = title;
|
|
26
|
+
}
|
|
27
|
+
if (!data.tags && tags) {
|
|
28
|
+
fileMeta.tags = tags;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return fileMeta;
|
|
32
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/// <reference types="node" resolution-mode="require"/>
|
|
2
|
+
import type { File } from '@directus/types';
|
|
3
|
+
import type { Readable } from 'node:stream';
|
|
4
|
+
export type Metadata = Partial<Pick<File, 'height' | 'width' | 'description' | 'title' | 'tags' | 'metadata'>>;
|
|
5
|
+
export declare function getMetadata(stream: Readable, allowList?: string | string[]): Promise<Metadata>;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import exif, {} from 'exif-reader';
|
|
2
|
+
import { parse as parseIcc } from 'icc';
|
|
3
|
+
import { pick } from 'lodash-es';
|
|
4
|
+
import { pipeline } from 'node:stream/promises';
|
|
5
|
+
import sharp from 'sharp';
|
|
6
|
+
import { useEnv } from '@directus/env';
|
|
7
|
+
import { useLogger } from '../../../logger.js';
|
|
8
|
+
import { parseIptc, parseXmp } from './parse-image-metadata.js';
|
|
9
|
+
const env = useEnv();
|
|
10
|
+
const logger = useLogger();
|
|
11
|
+
export async function getMetadata(stream, allowList = env['FILE_METADATA_ALLOW_LIST']) {
|
|
12
|
+
return new Promise((resolve, reject) => {
|
|
13
|
+
pipeline(stream, sharp().metadata(async (err, sharpMetadata) => {
|
|
14
|
+
if (err) {
|
|
15
|
+
reject(err);
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const metadata = {};
|
|
19
|
+
if (sharpMetadata.orientation && sharpMetadata.orientation >= 5) {
|
|
20
|
+
metadata.height = sharpMetadata.width ?? null;
|
|
21
|
+
metadata.width = sharpMetadata.height ?? null;
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
metadata.width = sharpMetadata.width ?? null;
|
|
25
|
+
metadata.height = sharpMetadata.height ?? null;
|
|
26
|
+
}
|
|
27
|
+
// Backward-compatible layout as it used to be with 'exifr'
|
|
28
|
+
const fullMetadata = {};
|
|
29
|
+
if (sharpMetadata.exif) {
|
|
30
|
+
try {
|
|
31
|
+
const { Image, ThumbnailTags, Iop, GPSInfo, Photo } = exif(sharpMetadata.exif);
|
|
32
|
+
if (Image) {
|
|
33
|
+
fullMetadata.ifd0 = Image;
|
|
34
|
+
}
|
|
35
|
+
if (ThumbnailTags) {
|
|
36
|
+
fullMetadata.ifd1 = ThumbnailTags;
|
|
37
|
+
}
|
|
38
|
+
if (Iop) {
|
|
39
|
+
fullMetadata.interop = Iop;
|
|
40
|
+
}
|
|
41
|
+
if (GPSInfo) {
|
|
42
|
+
fullMetadata.gps = GPSInfo;
|
|
43
|
+
}
|
|
44
|
+
if (Photo) {
|
|
45
|
+
fullMetadata.exif = Photo;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
logger.warn(`Couldn't extract Exif metadata from file`);
|
|
50
|
+
logger.warn(err);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (sharpMetadata.icc) {
|
|
54
|
+
try {
|
|
55
|
+
fullMetadata.icc = parseIcc(sharpMetadata.icc);
|
|
56
|
+
}
|
|
57
|
+
catch (err) {
|
|
58
|
+
logger.warn(`Couldn't extract ICC profile data from file`);
|
|
59
|
+
logger.warn(err);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (sharpMetadata.iptc) {
|
|
63
|
+
try {
|
|
64
|
+
fullMetadata.iptc = parseIptc(sharpMetadata.iptc);
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
logger.warn(`Couldn't extract IPTC Photo Metadata from file`);
|
|
68
|
+
logger.warn(err);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (sharpMetadata.xmp) {
|
|
72
|
+
try {
|
|
73
|
+
fullMetadata.xmp = parseXmp(sharpMetadata.xmp);
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
logger.warn(`Couldn't extract XMP data from file`);
|
|
77
|
+
logger.warn(err);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (fullMetadata?.iptc?.['Caption'] && typeof fullMetadata.iptc['Caption'] === 'string') {
|
|
81
|
+
metadata.description = fullMetadata.iptc?.['Caption'];
|
|
82
|
+
}
|
|
83
|
+
if (fullMetadata?.iptc?.['Headline'] && typeof fullMetadata.iptc['Headline'] === 'string') {
|
|
84
|
+
metadata.title = fullMetadata.iptc['Headline'];
|
|
85
|
+
}
|
|
86
|
+
if (fullMetadata?.iptc?.['Keywords']) {
|
|
87
|
+
metadata.tags = fullMetadata.iptc['Keywords'];
|
|
88
|
+
}
|
|
89
|
+
if (allowList === '*' || allowList?.[0] === '*') {
|
|
90
|
+
metadata.metadata = fullMetadata;
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
metadata.metadata = pick(fullMetadata, allowList);
|
|
94
|
+
}
|
|
95
|
+
// Fix (incorrectly parsed?) values starting / ending with spaces,
|
|
96
|
+
// limited to one level and string values only
|
|
97
|
+
for (const section of Object.keys(metadata.metadata)) {
|
|
98
|
+
for (const [key, value] of Object.entries(metadata.metadata[section])) {
|
|
99
|
+
if (typeof value === 'string') {
|
|
100
|
+
metadata.metadata[section][key] = value.trim();
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
resolve(metadata);
|
|
105
|
+
}));
|
|
106
|
+
});
|
|
107
|
+
}
|
package/dist/services/files.d.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
/// <reference types="node" resolution-mode="require"/>
|
|
2
|
-
import type { BusboyFileStream, File, PrimaryKey } from '@directus/types';
|
|
2
|
+
import type { BusboyFileStream, File, PrimaryKey, Query } from '@directus/types';
|
|
3
3
|
import type { Readable } from 'node:stream';
|
|
4
4
|
import type { AbstractServiceOptions, MutationOptions } from '../types/index.js';
|
|
5
|
-
import { ItemsService } from './items.js';
|
|
6
|
-
|
|
7
|
-
export declare class FilesService extends ItemsService {
|
|
5
|
+
import { ItemsService, type QueryOptions } from './items.js';
|
|
6
|
+
export declare class FilesService extends ItemsService<File> {
|
|
8
7
|
constructor(options: AbstractServiceOptions);
|
|
9
8
|
/**
|
|
10
9
|
* Upload a single new file to the configured storage adapter
|
|
@@ -15,7 +14,6 @@ export declare class FilesService extends ItemsService {
|
|
|
15
14
|
/**
|
|
16
15
|
* Extract metadata from a buffer's content
|
|
17
16
|
*/
|
|
18
|
-
getMetadata(stream: Readable, allowList?: string | string[]): Promise<Metadata>;
|
|
19
17
|
/**
|
|
20
18
|
* Import a single file from an external URL
|
|
21
19
|
*/
|
|
@@ -29,5 +27,5 @@ export declare class FilesService extends ItemsService {
|
|
|
29
27
|
* Delete multiple files
|
|
30
28
|
*/
|
|
31
29
|
deleteMany(keys: PrimaryKey[]): Promise<PrimaryKey[]>;
|
|
30
|
+
readByQuery(query: Query, opts?: QueryOptions | undefined): Promise<File[]>;
|
|
32
31
|
}
|
|
33
|
-
export {};
|
package/dist/services/files.js
CHANGED
|
@@ -3,23 +3,18 @@ import { ContentTooLargeError, ForbiddenError, InvalidPayloadError, ServiceUnava
|
|
|
3
3
|
import formatTitle from '@directus/format-title';
|
|
4
4
|
import { toArray } from '@directus/utils';
|
|
5
5
|
import encodeURL from 'encodeurl';
|
|
6
|
-
import
|
|
7
|
-
import { parse as parseIcc } from 'icc';
|
|
8
|
-
import { clone, pick } from 'lodash-es';
|
|
6
|
+
import { clone, cloneDeep } from 'lodash-es';
|
|
9
7
|
import { extension } from 'mime-types';
|
|
10
8
|
import { PassThrough as PassThroughStream, Transform as TransformStream } from 'node:stream';
|
|
11
|
-
import { pipeline } from 'node:stream/promises';
|
|
12
9
|
import zlib from 'node:zlib';
|
|
13
10
|
import path from 'path';
|
|
14
|
-
import sharp from 'sharp';
|
|
15
11
|
import url from 'url';
|
|
16
|
-
import {
|
|
12
|
+
import { RESUMABLE_UPLOADS } from '../constants.js';
|
|
17
13
|
import emitter from '../emitter.js';
|
|
18
14
|
import { useLogger } from '../logger.js';
|
|
19
|
-
import { validateAccess } from '../permissions/modules/validate-access/validate-access.js';
|
|
20
15
|
import { getAxios } from '../request/index.js';
|
|
21
16
|
import { getStorage } from '../storage/index.js';
|
|
22
|
-
import {
|
|
17
|
+
import { extractMetadata } from './files/lib/extract-metadata.js';
|
|
23
18
|
import { ItemsService } from './items.js';
|
|
24
19
|
const env = useEnv();
|
|
25
20
|
const logger = useLogger();
|
|
@@ -61,7 +56,7 @@ export class FilesService extends ItemsService {
|
|
|
61
56
|
}
|
|
62
57
|
const fileExtension = path.extname(payload.filename_download) || (payload.type && '.' + extension(payload.type)) || '';
|
|
63
58
|
// The filename_disk is the FINAL filename on disk
|
|
64
|
-
payload.filename_disk
|
|
59
|
+
payload.filename_disk ||= primaryKey + (fileExtension || '');
|
|
65
60
|
// Temp filename is used for replacements
|
|
66
61
|
const tempFilenameDisk = 'temp_' + payload.filename_disk;
|
|
67
62
|
if (!payload.type) {
|
|
@@ -129,37 +124,14 @@ export class FilesService extends ItemsService {
|
|
|
129
124
|
}
|
|
130
125
|
const { size } = await storage.location(data.storage).stat(payload.filename_disk);
|
|
131
126
|
payload.filesize = size;
|
|
132
|
-
|
|
133
|
-
const stream = await storage.location(data.storage).read(payload.filename_disk);
|
|
134
|
-
const { height, width, description, title, tags, metadata } = await this.getMetadata(stream);
|
|
135
|
-
if (!payload.height && height) {
|
|
136
|
-
payload.height = height;
|
|
137
|
-
}
|
|
138
|
-
if (!payload.width && width) {
|
|
139
|
-
payload.width = width;
|
|
140
|
-
}
|
|
141
|
-
if (!payload.metadata && metadata) {
|
|
142
|
-
payload.metadata = metadata;
|
|
143
|
-
}
|
|
144
|
-
// Note that if this is a replace file upload, the below properties are fetched and included in the payload above
|
|
145
|
-
// in the `existingFile` variable... so this will ONLY set the values if they're not already set
|
|
146
|
-
if (!payload.description && description) {
|
|
147
|
-
payload.description = description;
|
|
148
|
-
}
|
|
149
|
-
if (!payload.title && title) {
|
|
150
|
-
payload.title = title;
|
|
151
|
-
}
|
|
152
|
-
if (!payload.tags && tags) {
|
|
153
|
-
payload.tags = tags;
|
|
154
|
-
}
|
|
155
|
-
}
|
|
127
|
+
const metadata = await extractMetadata(data.storage, payload);
|
|
156
128
|
// We do this in a service without accountability. Even if you don't have update permissions to the file,
|
|
157
129
|
// we still want to be able to set the extracted values from the file on create
|
|
158
130
|
const sudoService = new ItemsService('directus_files', {
|
|
159
131
|
knex: this.knex,
|
|
160
132
|
schema: this.schema,
|
|
161
133
|
});
|
|
162
|
-
await sudoService.updateOne(primaryKey, payload, { emitEvents: false });
|
|
134
|
+
await sudoService.updateOne(primaryKey, { ...payload, ...metadata }, { emitEvents: false });
|
|
163
135
|
if (opts?.emitEvents !== false) {
|
|
164
136
|
emitter.emitAction('files.upload', {
|
|
165
137
|
payload,
|
|
@@ -176,116 +148,13 @@ export class FilesService extends ItemsService {
|
|
|
176
148
|
/**
|
|
177
149
|
* Extract metadata from a buffer's content
|
|
178
150
|
*/
|
|
179
|
-
async getMetadata(stream, allowList = env['FILE_METADATA_ALLOW_LIST']) {
|
|
180
|
-
return new Promise((resolve, reject) => {
|
|
181
|
-
pipeline(stream, sharp().metadata(async (err, sharpMetadata) => {
|
|
182
|
-
if (err) {
|
|
183
|
-
reject(err);
|
|
184
|
-
return;
|
|
185
|
-
}
|
|
186
|
-
const metadata = {};
|
|
187
|
-
if (sharpMetadata.orientation && sharpMetadata.orientation >= 5) {
|
|
188
|
-
metadata.height = sharpMetadata.width ?? null;
|
|
189
|
-
metadata.width = sharpMetadata.height ?? null;
|
|
190
|
-
}
|
|
191
|
-
else {
|
|
192
|
-
metadata.width = sharpMetadata.width ?? null;
|
|
193
|
-
metadata.height = sharpMetadata.height ?? null;
|
|
194
|
-
}
|
|
195
|
-
// Backward-compatible layout as it used to be with 'exifr'
|
|
196
|
-
const fullMetadata = {};
|
|
197
|
-
if (sharpMetadata.exif) {
|
|
198
|
-
try {
|
|
199
|
-
const { Image, ThumbnailTags, Iop, GPSInfo, Photo } = exif(sharpMetadata.exif);
|
|
200
|
-
if (Image) {
|
|
201
|
-
fullMetadata.ifd0 = Image;
|
|
202
|
-
}
|
|
203
|
-
if (ThumbnailTags) {
|
|
204
|
-
fullMetadata.ifd1 = ThumbnailTags;
|
|
205
|
-
}
|
|
206
|
-
if (Iop) {
|
|
207
|
-
fullMetadata.interop = Iop;
|
|
208
|
-
}
|
|
209
|
-
if (GPSInfo) {
|
|
210
|
-
fullMetadata.gps = GPSInfo;
|
|
211
|
-
}
|
|
212
|
-
if (Photo) {
|
|
213
|
-
fullMetadata.exif = Photo;
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
catch (err) {
|
|
217
|
-
logger.warn(`Couldn't extract Exif metadata from file`);
|
|
218
|
-
logger.warn(err);
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
if (sharpMetadata.icc) {
|
|
222
|
-
try {
|
|
223
|
-
fullMetadata.icc = parseIcc(sharpMetadata.icc);
|
|
224
|
-
}
|
|
225
|
-
catch (err) {
|
|
226
|
-
logger.warn(`Couldn't extract ICC profile data from file`);
|
|
227
|
-
logger.warn(err);
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
if (sharpMetadata.iptc) {
|
|
231
|
-
try {
|
|
232
|
-
fullMetadata.iptc = parseIptc(sharpMetadata.iptc);
|
|
233
|
-
}
|
|
234
|
-
catch (err) {
|
|
235
|
-
logger.warn(`Couldn't extract IPTC Photo Metadata from file`);
|
|
236
|
-
logger.warn(err);
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
if (sharpMetadata.xmp) {
|
|
240
|
-
try {
|
|
241
|
-
fullMetadata.xmp = parseXmp(sharpMetadata.xmp);
|
|
242
|
-
}
|
|
243
|
-
catch (err) {
|
|
244
|
-
logger.warn(`Couldn't extract XMP data from file`);
|
|
245
|
-
logger.warn(err);
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
if (fullMetadata?.iptc?.['Caption'] && typeof fullMetadata.iptc['Caption'] === 'string') {
|
|
249
|
-
metadata.description = fullMetadata.iptc?.['Caption'];
|
|
250
|
-
}
|
|
251
|
-
if (fullMetadata?.iptc?.['Headline'] && typeof fullMetadata.iptc['Headline'] === 'string') {
|
|
252
|
-
metadata.title = fullMetadata.iptc['Headline'];
|
|
253
|
-
}
|
|
254
|
-
if (fullMetadata?.iptc?.['Keywords']) {
|
|
255
|
-
metadata.tags = fullMetadata.iptc['Keywords'];
|
|
256
|
-
}
|
|
257
|
-
if (allowList === '*' || allowList?.[0] === '*') {
|
|
258
|
-
metadata.metadata = fullMetadata;
|
|
259
|
-
}
|
|
260
|
-
else {
|
|
261
|
-
metadata.metadata = pick(fullMetadata, allowList);
|
|
262
|
-
}
|
|
263
|
-
// Fix (incorrectly parsed?) values starting / ending with spaces,
|
|
264
|
-
// limited to one level and string values only
|
|
265
|
-
for (const section of Object.keys(metadata.metadata)) {
|
|
266
|
-
for (const [key, value] of Object.entries(metadata.metadata[section])) {
|
|
267
|
-
if (typeof value === 'string') {
|
|
268
|
-
metadata.metadata[section][key] = value.trim();
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
resolve(metadata);
|
|
273
|
-
}));
|
|
274
|
-
});
|
|
275
|
-
}
|
|
276
151
|
/**
|
|
277
152
|
* Import a single file from an external URL
|
|
278
153
|
*/
|
|
279
154
|
async importOne(importURL, body) {
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
action: 'create',
|
|
284
|
-
collection: 'directus_files',
|
|
285
|
-
}, {
|
|
286
|
-
knex: this.knex,
|
|
287
|
-
schema: this.schema,
|
|
288
|
-
});
|
|
155
|
+
const fileCreatePermissions = this.accountability?.permissions?.find((permission) => permission.collection === 'directus_files' && permission.action === 'create');
|
|
156
|
+
if (this.accountability && this.accountability?.admin !== true && !fileCreatePermissions) {
|
|
157
|
+
throw new ForbiddenError();
|
|
289
158
|
}
|
|
290
159
|
let fileResponse;
|
|
291
160
|
try {
|
|
@@ -345,6 +214,24 @@ export class FilesService extends ItemsService {
|
|
|
345
214
|
}
|
|
346
215
|
return keys;
|
|
347
216
|
}
|
|
217
|
+
async readByQuery(query, opts) {
|
|
218
|
+
const filteredQuery = cloneDeep(query);
|
|
219
|
+
if (RESUMABLE_UPLOADS.ENABLED === true) {
|
|
220
|
+
const filterPartialUploads = { tus_id: { _null: true } };
|
|
221
|
+
if (!filteredQuery.filter) {
|
|
222
|
+
filteredQuery.filter = filterPartialUploads;
|
|
223
|
+
}
|
|
224
|
+
else if ('_and' in filteredQuery.filter && Array.isArray(filteredQuery.filter['_and'])) {
|
|
225
|
+
filteredQuery.filter['_and'].push(filterPartialUploads);
|
|
226
|
+
}
|
|
227
|
+
else {
|
|
228
|
+
filteredQuery.filter = {
|
|
229
|
+
_and: [filteredQuery.filter, filterPartialUploads],
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return super.readByQuery(filteredQuery, opts);
|
|
234
|
+
}
|
|
348
235
|
}
|
|
349
236
|
function decompressResponse(stream, headers) {
|
|
350
237
|
const contentEncoding = (headers['content-encoding'] || '').toLowerCase();
|
|
@@ -20,9 +20,9 @@ export declare class GraphQLService {
|
|
|
20
20
|
/**
|
|
21
21
|
* Generate the GraphQL schema. Pulls from the schema information generated by the get-schema util.
|
|
22
22
|
*/
|
|
23
|
-
getSchema():
|
|
24
|
-
getSchema(type: 'schema'):
|
|
25
|
-
getSchema(type: 'sdl'):
|
|
23
|
+
getSchema(): GraphQLSchema;
|
|
24
|
+
getSchema(type: 'schema'): GraphQLSchema;
|
|
25
|
+
getSchema(type: 'sdl'): GraphQLSchema | string;
|
|
26
26
|
/**
|
|
27
27
|
* Generic resolver that's used for every "regular" items/system query. Converts the incoming GraphQL AST / fragments into
|
|
28
28
|
* Directus' query structure which is then executed by the services.
|
|
@@ -11,9 +11,6 @@ import { clearSystemCache, getCache } from '../../cache.js';
|
|
|
11
11
|
import { DEFAULT_AUTH_PROVIDER, GENERATE_SPECIAL, REFRESH_COOKIE_OPTIONS, SESSION_COOKIE_OPTIONS, } from '../../constants.js';
|
|
12
12
|
import getDatabase from '../../database/index.js';
|
|
13
13
|
import { rateLimiter } from '../../middleware/rate-limiter-registration.js';
|
|
14
|
-
import { fetchAllowedFieldMap } from '../../permissions/modules/fetch-allowed-field-map/fetch-allowed-field-map.js';
|
|
15
|
-
import { fetchInconsistentFieldMap } from '../../permissions/modules/fetch-inconsistent-field-map/fetch-inconsistent-field-map.js';
|
|
16
|
-
import { createDefaultAccountability } from '../../permissions/utils/create-default-accountability.js';
|
|
17
14
|
import { generateHash } from '../../utils/generate-hash.js';
|
|
18
15
|
import { getGraphQLType } from '../../utils/get-graphql-type.js';
|
|
19
16
|
import { getIPFromReq } from '../../utils/get-ip-from-req.js';
|
|
@@ -51,9 +48,6 @@ import { GraphQLVoid } from './types/void.js';
|
|
|
51
48
|
import { addPathToValidationError } from './utils/add-path-to-validation-error.js';
|
|
52
49
|
import processError from './utils/process-error.js';
|
|
53
50
|
import { sanitizeGraphqlSchema } from './utils/sanitize-gql-schema.js';
|
|
54
|
-
import { fetchAccountabilityCollectionAccess } from '../../permissions/modules/fetch-accountability-collection-access/fetch-accountability-collection-access.js';
|
|
55
|
-
import { fetchAccountabilityPolicyGlobals } from '../../permissions/modules/fetch-accountability-policy-globals/fetch-accountability-policy-globals.js';
|
|
56
|
-
import { RolesService } from '../roles.js';
|
|
57
51
|
const env = useEnv();
|
|
58
52
|
const validationRules = Array.from(specifiedRules);
|
|
59
53
|
if (env['GRAPHQL_INTROSPECTION'] === false) {
|
|
@@ -86,7 +80,7 @@ export class GraphQLService {
|
|
|
86
80
|
* Execute a GraphQL structure
|
|
87
81
|
*/
|
|
88
82
|
async execute({ document, variables, operationName, contextValue, }) {
|
|
89
|
-
const schema =
|
|
83
|
+
const schema = this.getSchema();
|
|
90
84
|
const validationErrors = validate(schema, document, validationRules).map((validationError) => addPathToValidationError(validationError));
|
|
91
85
|
if (validationErrors.length > 0) {
|
|
92
86
|
throw new GraphQLValidationError({ errors: validationErrors });
|
|
@@ -114,7 +108,7 @@ export class GraphQLService {
|
|
|
114
108
|
formattedResult.extensions = result['extensions'];
|
|
115
109
|
return formattedResult;
|
|
116
110
|
}
|
|
117
|
-
|
|
111
|
+
getSchema(type = 'schema') {
|
|
118
112
|
const key = `${this.scope}_${type}_${this.accountability?.role}_${this.accountability?.user}`;
|
|
119
113
|
const cachedSchema = cache.get(key);
|
|
120
114
|
if (cachedSchema)
|
|
@@ -122,53 +116,20 @@ export class GraphQLService {
|
|
|
122
116
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
123
117
|
const self = this;
|
|
124
118
|
const schemaComposer = new SchemaComposer();
|
|
125
|
-
let schema;
|
|
126
119
|
const sanitizedSchema = sanitizeGraphqlSchema(this.schema);
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
}, { schema: this.schema, knex: this.knex })),
|
|
141
|
-
create: reduceSchema(sanitizedSchema, await fetchAllowedFieldMap({
|
|
142
|
-
accountability: this.accountability,
|
|
143
|
-
action: 'create',
|
|
144
|
-
}, { schema: this.schema, knex: this.knex })),
|
|
145
|
-
update: reduceSchema(sanitizedSchema, await fetchAllowedFieldMap({
|
|
146
|
-
accountability: this.accountability,
|
|
147
|
-
action: 'update',
|
|
148
|
-
}, { schema: this.schema, knex: this.knex })),
|
|
149
|
-
delete: reduceSchema(sanitizedSchema, await fetchAllowedFieldMap({
|
|
150
|
-
accountability: this.accountability,
|
|
151
|
-
action: 'delete',
|
|
152
|
-
}, { schema: this.schema, knex: this.knex })),
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
const inconsistentFields = {
|
|
156
|
-
read: await fetchInconsistentFieldMap({
|
|
157
|
-
accountability: this.accountability,
|
|
158
|
-
action: 'read',
|
|
159
|
-
}, { schema: this.schema, knex: this.knex }),
|
|
160
|
-
create: await fetchInconsistentFieldMap({
|
|
161
|
-
accountability: this.accountability,
|
|
162
|
-
action: 'create',
|
|
163
|
-
}, { schema: this.schema, knex: this.knex }),
|
|
164
|
-
update: await fetchInconsistentFieldMap({
|
|
165
|
-
accountability: this.accountability,
|
|
166
|
-
action: 'update',
|
|
167
|
-
}, { schema: this.schema, knex: this.knex }),
|
|
168
|
-
delete: await fetchInconsistentFieldMap({
|
|
169
|
-
accountability: this.accountability,
|
|
170
|
-
action: 'delete',
|
|
171
|
-
}, { schema: this.schema, knex: this.knex }),
|
|
120
|
+
const schema = {
|
|
121
|
+
read: this.accountability?.admin === true
|
|
122
|
+
? sanitizedSchema
|
|
123
|
+
: reduceSchema(sanitizedSchema, this.accountability?.permissions || null, ['read']),
|
|
124
|
+
create: this.accountability?.admin === true
|
|
125
|
+
? sanitizedSchema
|
|
126
|
+
: reduceSchema(sanitizedSchema, this.accountability?.permissions || null, ['create']),
|
|
127
|
+
update: this.accountability?.admin === true
|
|
128
|
+
? sanitizedSchema
|
|
129
|
+
: reduceSchema(sanitizedSchema, this.accountability?.permissions || null, ['update']),
|
|
130
|
+
delete: this.accountability?.admin === true
|
|
131
|
+
? sanitizedSchema
|
|
132
|
+
: reduceSchema(sanitizedSchema, this.accountability?.permissions || null, ['delete']),
|
|
172
133
|
};
|
|
173
134
|
const subscriptionEventType = schemaComposer.createEnumTC({
|
|
174
135
|
name: 'EventEnum',
|
|
@@ -339,18 +300,16 @@ export class GraphQLService {
|
|
|
339
300
|
name: action === 'read' ? collection.collection : `${action}_${collection.collection}`,
|
|
340
301
|
fields: Object.values(collection.fields).reduce((acc, field) => {
|
|
341
302
|
let type = getGraphQLType(field.type, field.special);
|
|
342
|
-
const fieldIsInconsistent = inconsistentFields[action][collection.collection]?.includes(field.field);
|
|
343
303
|
// GraphQL doesn't differentiate between not-null and has-to-be-submitted. We
|
|
344
304
|
// can't non-null in update, as that would require every not-nullable field to be
|
|
345
305
|
// submitted on updates
|
|
346
306
|
if (field.nullable === false &&
|
|
347
307
|
!field.defaultValue &&
|
|
348
308
|
!GENERATE_SPECIAL.some((flag) => field.special.includes(flag)) &&
|
|
349
|
-
fieldIsInconsistent === false &&
|
|
350
309
|
action !== 'update') {
|
|
351
310
|
type = new GraphQLNonNull(type);
|
|
352
311
|
}
|
|
353
|
-
if (collection.primary === field.field
|
|
312
|
+
if (collection.primary === field.field) {
|
|
354
313
|
// permissions IDs need to be nullable https://github.com/directus/directus/issues/20509
|
|
355
314
|
if (collection.collection === 'directus_permissions') {
|
|
356
315
|
type = GraphQLID;
|
|
@@ -1803,7 +1762,7 @@ export class GraphQLService {
|
|
|
1803
1762
|
accountability: this.accountability,
|
|
1804
1763
|
scope: args['scope'] ?? 'items',
|
|
1805
1764
|
});
|
|
1806
|
-
return
|
|
1765
|
+
return service.getSchema('sdl');
|
|
1807
1766
|
},
|
|
1808
1767
|
},
|
|
1809
1768
|
server_ping: {
|
|
@@ -1856,7 +1815,7 @@ export class GraphQLService {
|
|
|
1856
1815
|
otp: GraphQLString,
|
|
1857
1816
|
},
|
|
1858
1817
|
resolve: async (_, args, { req, res }) => {
|
|
1859
|
-
const accountability =
|
|
1818
|
+
const accountability = { role: null };
|
|
1860
1819
|
if (req?.ip)
|
|
1861
1820
|
accountability.ip = req.ip;
|
|
1862
1821
|
const userAgent = req?.get('user-agent');
|
|
@@ -1896,7 +1855,7 @@ export class GraphQLService {
|
|
|
1896
1855
|
mode: AuthMode,
|
|
1897
1856
|
},
|
|
1898
1857
|
resolve: async (_, args, { req, res }) => {
|
|
1899
|
-
const accountability =
|
|
1858
|
+
const accountability = { role: null };
|
|
1900
1859
|
if (req?.ip)
|
|
1901
1860
|
accountability.ip = req.ip;
|
|
1902
1861
|
const userAgent = req?.get('user-agent');
|
|
@@ -1954,7 +1913,7 @@ export class GraphQLService {
|
|
|
1954
1913
|
mode: AuthMode,
|
|
1955
1914
|
},
|
|
1956
1915
|
resolve: async (_, args, { req, res }) => {
|
|
1957
|
-
const accountability =
|
|
1916
|
+
const accountability = { role: null };
|
|
1958
1917
|
if (req?.ip)
|
|
1959
1918
|
accountability.ip = req.ip;
|
|
1960
1919
|
const userAgent = req?.get('user-agent');
|
|
@@ -2004,7 +1963,7 @@ export class GraphQLService {
|
|
|
2004
1963
|
reset_url: GraphQLString,
|
|
2005
1964
|
},
|
|
2006
1965
|
resolve: async (_, args, { req }) => {
|
|
2007
|
-
const accountability =
|
|
1966
|
+
const accountability = { role: null };
|
|
2008
1967
|
if (req?.ip)
|
|
2009
1968
|
accountability.ip = req.ip;
|
|
2010
1969
|
const userAgent = req?.get('user-agent');
|
|
@@ -2032,7 +1991,7 @@ export class GraphQLService {
|
|
|
2032
1991
|
password: new GraphQLNonNull(GraphQLString),
|
|
2033
1992
|
},
|
|
2034
1993
|
resolve: async (_, args, { req }) => {
|
|
2035
|
-
const accountability =
|
|
1994
|
+
const accountability = { role: null };
|
|
2036
1995
|
if (req?.ip)
|
|
2037
1996
|
accountability.ip = req.ip;
|
|
2038
1997
|
const userAgent = req?.get('user-agent');
|
|
@@ -2673,69 +2632,6 @@ export class GraphQLService {
|
|
|
2673
2632
|
},
|
|
2674
2633
|
});
|
|
2675
2634
|
}
|
|
2676
|
-
if ('directus_permissions' in schema.read.collections) {
|
|
2677
|
-
schemaComposer.Query.addFields({
|
|
2678
|
-
permissions_me: {
|
|
2679
|
-
type: schemaComposer.createScalarTC({
|
|
2680
|
-
name: 'permissions_me_type',
|
|
2681
|
-
parseValue: (value) => value,
|
|
2682
|
-
serialize: (value) => value,
|
|
2683
|
-
}),
|
|
2684
|
-
resolve: async (_, _args, __, _info) => {
|
|
2685
|
-
if (!this.accountability?.user && !this.accountability?.role)
|
|
2686
|
-
return null;
|
|
2687
|
-
const result = await fetchAccountabilityCollectionAccess(this.accountability, {
|
|
2688
|
-
schema: this.schema,
|
|
2689
|
-
knex: getDatabase(),
|
|
2690
|
-
});
|
|
2691
|
-
return result;
|
|
2692
|
-
},
|
|
2693
|
-
},
|
|
2694
|
-
});
|
|
2695
|
-
}
|
|
2696
|
-
if ('directus_roles' in schema.read.collections) {
|
|
2697
|
-
schemaComposer.Query.addFields({
|
|
2698
|
-
roles_me: {
|
|
2699
|
-
type: ReadCollectionTypes['directus_roles'].List,
|
|
2700
|
-
resolve: async (_, args, __, info) => {
|
|
2701
|
-
if (!this.accountability?.user && !this.accountability?.role)
|
|
2702
|
-
return null;
|
|
2703
|
-
const service = new RolesService({
|
|
2704
|
-
accountability: this.accountability,
|
|
2705
|
-
schema: this.schema,
|
|
2706
|
-
});
|
|
2707
|
-
const selections = this.replaceFragmentsInSelections(info.fieldNodes[0]?.selectionSet?.selections, info.fragments);
|
|
2708
|
-
const query = this.getQuery(args, selections || [], info.variableValues);
|
|
2709
|
-
query.limit = -1;
|
|
2710
|
-
const roles = await service.readMany(this.accountability.roles, query);
|
|
2711
|
-
return roles;
|
|
2712
|
-
},
|
|
2713
|
-
},
|
|
2714
|
-
});
|
|
2715
|
-
}
|
|
2716
|
-
if ('directus_policies' in schema.read.collections) {
|
|
2717
|
-
schemaComposer.Query.addFields({
|
|
2718
|
-
policies_me_globals: {
|
|
2719
|
-
type: schemaComposer.createObjectTC({
|
|
2720
|
-
name: 'policy_me_globals_type',
|
|
2721
|
-
fields: {
|
|
2722
|
-
enforce_tfa: 'Boolean',
|
|
2723
|
-
app_access: 'Boolean',
|
|
2724
|
-
admin_access: 'Boolean',
|
|
2725
|
-
},
|
|
2726
|
-
}),
|
|
2727
|
-
resolve: async (_, _args, __, _info) => {
|
|
2728
|
-
if (!this.accountability?.user && !this.accountability?.role)
|
|
2729
|
-
return null;
|
|
2730
|
-
const result = await fetchAccountabilityPolicyGlobals(this.accountability, {
|
|
2731
|
-
schema: this.schema,
|
|
2732
|
-
knex: getDatabase(),
|
|
2733
|
-
});
|
|
2734
|
-
return result;
|
|
2735
|
-
},
|
|
2736
|
-
},
|
|
2737
|
-
});
|
|
2738
|
-
}
|
|
2739
2635
|
if ('directus_users' in schema.update.collections && this.accountability?.user) {
|
|
2740
2636
|
schemaComposer.Mutation.addFields({
|
|
2741
2637
|
update_users_me: {
|