@directus/api 20.0.0-rc.0 → 20.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 +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 +2 -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 +450 -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 +24 -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.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 +214 -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/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 +35 -33
- 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 -163
- 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,17 @@ 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 { SUPPORTED_IMAGE_METADATA_FORMATS } from '../constants.js';
|
|
17
12
|
import emitter from '../emitter.js';
|
|
18
13
|
import { useLogger } from '../logger.js';
|
|
19
|
-
import { validateAccess } from '../permissions/modules/validate-access/validate-access.js';
|
|
20
14
|
import { getAxios } from '../request/index.js';
|
|
21
15
|
import { getStorage } from '../storage/index.js';
|
|
22
|
-
import {
|
|
16
|
+
import { extractMetadata } from './files/lib/extract-metadata.js';
|
|
23
17
|
import { ItemsService } from './items.js';
|
|
24
18
|
const env = useEnv();
|
|
25
19
|
const logger = useLogger();
|
|
@@ -61,7 +55,7 @@ export class FilesService extends ItemsService {
|
|
|
61
55
|
}
|
|
62
56
|
const fileExtension = path.extname(payload.filename_download) || (payload.type && '.' + extension(payload.type)) || '';
|
|
63
57
|
// The filename_disk is the FINAL filename on disk
|
|
64
|
-
payload.filename_disk
|
|
58
|
+
payload.filename_disk ||= primaryKey + (fileExtension || '');
|
|
65
59
|
// Temp filename is used for replacements
|
|
66
60
|
const tempFilenameDisk = 'temp_' + payload.filename_disk;
|
|
67
61
|
if (!payload.type) {
|
|
@@ -129,37 +123,14 @@ export class FilesService extends ItemsService {
|
|
|
129
123
|
}
|
|
130
124
|
const { size } = await storage.location(data.storage).stat(payload.filename_disk);
|
|
131
125
|
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
|
-
}
|
|
126
|
+
const metadata = await extractMetadata(data.storage, payload);
|
|
156
127
|
// We do this in a service without accountability. Even if you don't have update permissions to the file,
|
|
157
128
|
// we still want to be able to set the extracted values from the file on create
|
|
158
129
|
const sudoService = new ItemsService('directus_files', {
|
|
159
130
|
knex: this.knex,
|
|
160
131
|
schema: this.schema,
|
|
161
132
|
});
|
|
162
|
-
await sudoService.updateOne(primaryKey, payload, { emitEvents: false });
|
|
133
|
+
await sudoService.updateOne(primaryKey, { ...payload, ...metadata }, { emitEvents: false });
|
|
163
134
|
if (opts?.emitEvents !== false) {
|
|
164
135
|
emitter.emitAction('files.upload', {
|
|
165
136
|
payload,
|
|
@@ -176,116 +147,13 @@ export class FilesService extends ItemsService {
|
|
|
176
147
|
/**
|
|
177
148
|
* Extract metadata from a buffer's content
|
|
178
149
|
*/
|
|
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
150
|
/**
|
|
277
151
|
* Import a single file from an external URL
|
|
278
152
|
*/
|
|
279
153
|
async importOne(importURL, body) {
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
action: 'create',
|
|
284
|
-
collection: 'directus_files',
|
|
285
|
-
}, {
|
|
286
|
-
knex: this.knex,
|
|
287
|
-
schema: this.schema,
|
|
288
|
-
});
|
|
154
|
+
const fileCreatePermissions = this.accountability?.permissions?.find((permission) => permission.collection === 'directus_files' && permission.action === 'create');
|
|
155
|
+
if (this.accountability && this.accountability?.admin !== true && !fileCreatePermissions) {
|
|
156
|
+
throw new ForbiddenError();
|
|
289
157
|
}
|
|
290
158
|
let fileResponse;
|
|
291
159
|
try {
|
|
@@ -345,6 +213,22 @@ export class FilesService extends ItemsService {
|
|
|
345
213
|
}
|
|
346
214
|
return keys;
|
|
347
215
|
}
|
|
216
|
+
async readByQuery(query, opts) {
|
|
217
|
+
const filteredQuery = cloneDeep(query);
|
|
218
|
+
const filterPartialUploads = { tus_id: { _null: true } };
|
|
219
|
+
if (!filteredQuery.filter) {
|
|
220
|
+
filteredQuery.filter = filterPartialUploads;
|
|
221
|
+
}
|
|
222
|
+
else if ('_and' in filteredQuery.filter && Array.isArray(filteredQuery.filter['_and'])) {
|
|
223
|
+
filteredQuery.filter['_and'].push(filterPartialUploads);
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
filteredQuery.filter = {
|
|
227
|
+
_and: [filteredQuery.filter, filterPartialUploads],
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
return super.readByQuery(filteredQuery, opts);
|
|
231
|
+
}
|
|
348
232
|
}
|
|
349
233
|
function decompressResponse(stream, headers) {
|
|
350
234
|
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: {
|