@directus/api 32.2.0 → 33.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/ai/chat/lib/create-ui-stream.d.ts +1 -1
- package/dist/ai/tools/assets/index.js +1 -1
- package/dist/ai/tools/collections/index.js +2 -2
- package/dist/ai/tools/fields/index.js +2 -2
- package/dist/ai/tools/files/index.js +1 -1
- package/dist/ai/tools/flows/index.js +1 -1
- package/dist/ai/tools/folders/index.js +1 -1
- package/dist/ai/tools/items/index.js +2 -2
- package/dist/ai/tools/relations/index.js +1 -1
- package/dist/ai/tools/trigger-flow/index.js +1 -1
- package/dist/app.js +8 -8
- package/dist/auth/drivers/ldap.js +2 -2
- package/dist/auth/drivers/local.js +1 -1
- package/dist/auth/drivers/oauth2.d.ts +1 -2
- package/dist/auth/drivers/oauth2.js +22 -17
- package/dist/auth/drivers/openid.d.ts +1 -2
- package/dist/auth/drivers/openid.js +18 -13
- package/dist/auth/drivers/saml.js +3 -3
- package/dist/auth/utils/generate-callback-url.d.ts +11 -0
- package/dist/auth/utils/generate-callback-url.js +40 -0
- package/dist/auth/utils/is-login-redirect-allowed.d.ts +7 -0
- package/dist/{utils → auth/utils}/is-login-redirect-allowed.js +12 -9
- package/dist/cache.js +2 -2
- package/dist/cli/commands/bootstrap/index.js +2 -2
- package/dist/cli/commands/database/install.js +1 -1
- package/dist/cli/commands/database/migrate.js +1 -1
- package/dist/cli/commands/init/index.js +2 -2
- package/dist/cli/commands/roles/create.js +4 -4
- package/dist/cli/commands/schema/apply.js +3 -3
- package/dist/cli/commands/schema/snapshot.js +1 -1
- package/dist/cli/utils/create-db-connection.d.ts +1 -1
- package/dist/cli/utils/create-db-connection.js +1 -1
- package/dist/cli/utils/create-env/index.js +1 -1
- package/dist/constants.d.ts +7 -3
- package/dist/constants.js +7 -3
- package/dist/controllers/access.js +1 -1
- package/dist/controllers/assets.js +1 -1
- package/dist/controllers/extensions.js +1 -1
- package/dist/controllers/fields.js +2 -2
- package/dist/controllers/files.js +1 -1
- package/dist/controllers/items.js +1 -1
- package/dist/controllers/not-found.js +1 -1
- package/dist/controllers/relations.js +1 -1
- package/dist/database/errors/dialects/mysql.d.ts +1 -1
- package/dist/database/errors/dialects/postgres.d.ts +1 -1
- package/dist/database/errors/dialects/sqlite.d.ts +1 -1
- package/dist/database/errors/translate.d.ts +1 -1
- package/dist/database/errors/translate.js +1 -1
- package/dist/database/helpers/date/dialects/mssql.js +1 -1
- package/dist/database/helpers/date/dialects/mysql.js +1 -1
- package/dist/database/helpers/date/types.js +1 -1
- package/dist/database/helpers/schema/dialects/cockroachdb.d.ts +1 -0
- package/dist/database/helpers/schema/dialects/cockroachdb.js +24 -1
- package/dist/database/helpers/schema/dialects/mssql.d.ts +1 -1
- package/dist/database/helpers/schema/dialects/mysql.d.ts +2 -1
- package/dist/database/helpers/schema/dialects/mysql.js +16 -3
- package/dist/database/helpers/schema/dialects/postgres.d.ts +1 -1
- package/dist/database/helpers/schema/types.d.ts +13 -0
- package/dist/database/helpers/schema/types.js +24 -0
- package/dist/database/index.js +4 -4
- package/dist/database/migrations/20220429A-add-flows.js +1 -1
- package/dist/database/migrations/20230526A-migrate-translation-strings.js +1 -1
- package/dist/database/migrations/20231009A-update-csv-fields-to-text.js +1 -1
- package/dist/database/migrations/20240204A-marketplace.js +9 -7
- package/dist/database/migrations/20240311A-deprecate-webhooks.d.ts +15 -0
- package/dist/database/migrations/20240311A-deprecate-webhooks.js +1 -1
- package/dist/database/migrations/20240806A-permissions-policies.js +2 -2
- package/dist/database/migrations/20240924A-migrate-legacy-comments.js +1 -1
- package/dist/database/migrations/20251014A-add-project-owner.js +1 -1
- package/dist/database/migrations/20251224A-remove-webhooks.d.ts +3 -0
- package/dist/database/migrations/20251224A-remove-webhooks.js +19 -0
- package/dist/database/migrations/20260113A-add-revisions-index.d.ts +3 -0
- package/dist/database/migrations/20260113A-add-revisions-index.js +41 -0
- package/dist/database/migrations/run.js +3 -3
- package/dist/database/run-ast/lib/apply-query/filter/get-filter-type.d.ts +2 -2
- package/dist/database/run-ast/lib/apply-query/filter/get-filter-type.js +1 -1
- package/dist/database/run-ast/lib/apply-query/filter/operator.js +1 -1
- package/dist/database/run-ast/lib/apply-query/sort.js +1 -1
- package/dist/database/run-ast/utils/get-column-pre-processor.js +2 -2
- package/dist/database/run-ast/utils/get-column.js +1 -1
- package/dist/database/seeds/run.js +3 -3
- package/dist/extensions/lib/get-extensions-path.js +1 -1
- package/dist/extensions/lib/get-extensions-settings.js +1 -1
- package/dist/extensions/lib/get-extensions.js +1 -1
- package/dist/extensions/lib/get-shared-deps-mapping.js +3 -3
- package/dist/extensions/lib/installation/manager.js +3 -3
- package/dist/extensions/lib/sandbox/register/route.d.ts +1 -1
- package/dist/extensions/lib/sync/status.js +1 -1
- package/dist/extensions/lib/sync/sync.js +7 -7
- package/dist/extensions/lib/sync/utils.js +2 -2
- package/dist/extensions/manager.d.ts +1 -1
- package/dist/extensions/manager.js +8 -8
- package/dist/flows.d.ts +1 -1
- package/dist/logger/index.js +1 -1
- package/dist/logger/logs-stream.d.ts +1 -1
- package/dist/logger/logs-stream.js +1 -1
- package/dist/mailer.js +1 -1
- package/dist/metrics/lib/create-metrics.js +2 -2
- package/dist/middleware/authenticate.js +3 -3
- package/dist/middleware/collection-exists.js +1 -1
- package/dist/middleware/extract-token.js +1 -1
- package/dist/middleware/graphql.js +2 -2
- package/dist/middleware/validate-batch.js +1 -1
- package/dist/operations/exec/index.js +2 -1
- package/dist/operations/mail/index.js +1 -1
- package/dist/operations/mail/rate-limiter.js +2 -2
- package/dist/permissions/cache.js +5 -0
- package/dist/permissions/modules/fetch-allowed-collections/fetch-allowed-collections.js +1 -1
- package/dist/permissions/modules/fetch-allowed-field-map/fetch-allowed-field-map.js +1 -1
- package/dist/permissions/modules/fetch-inconsistent-field-map/fetch-inconsistent-field-map.js +2 -2
- package/dist/permissions/modules/process-ast/lib/inject-cases.js +1 -1
- package/dist/permissions/modules/process-ast/process-ast.js +1 -1
- package/dist/permissions/modules/process-payload/process-payload.js +1 -1
- package/dist/permissions/modules/validate-access/lib/validate-item-access.d.ts +13 -1
- package/dist/permissions/modules/validate-access/lib/validate-item-access.js +54 -6
- package/dist/permissions/modules/validate-access/validate-access.js +3 -2
- package/dist/rate-limiter.js +1 -1
- package/dist/request/is-denied-ip.js +1 -1
- package/dist/schedules/project.js +1 -1
- package/dist/schedules/telemetry.js +1 -1
- package/dist/schedules/tus.js +1 -1
- package/dist/server.js +4 -4
- package/dist/services/assets.d.ts +2 -1
- package/dist/services/assets.js +35 -8
- package/dist/services/authentication.js +2 -2
- package/dist/services/collections.js +1 -1
- package/dist/services/extensions.d.ts +1 -1
- package/dist/services/files/utils/get-metadata.d.ts +1 -1
- package/dist/services/files/utils/get-metadata.js +1 -1
- package/dist/services/files.d.ts +1 -1
- package/dist/services/files.js +4 -4
- package/dist/services/graphql/index.d.ts +1 -1
- package/dist/services/graphql/index.js +1 -1
- package/dist/services/graphql/resolvers/mutation.js +1 -1
- package/dist/services/graphql/schema/get-types.d.ts +1 -1
- package/dist/services/graphql/schema/read.js +1 -1
- package/dist/services/graphql/subscription.d.ts +1 -1
- package/dist/services/graphql/types/date.js +1 -1
- package/dist/services/graphql/types/hash.js +1 -1
- package/dist/services/graphql/utils/add-path-to-validation-error.js +1 -1
- package/dist/services/import-export.d.ts +1 -1
- package/dist/services/import-export.js +2 -2
- package/dist/services/index.d.ts +0 -1
- package/dist/services/index.js +0 -1
- package/dist/services/mail/index.js +2 -2
- package/dist/services/mail/rate-limiter.js +2 -2
- package/dist/services/payload.js +2 -2
- package/dist/services/schema.js +1 -1
- package/dist/services/server.js +12 -4
- package/dist/services/settings.js +2 -2
- package/dist/services/tfa.js +1 -1
- package/dist/services/translations.js +1 -1
- package/dist/services/tus/data-store.d.ts +1 -3
- package/dist/services/tus/data-store.js +2 -5
- package/dist/services/tus/server.js +6 -6
- package/dist/services/users.js +4 -4
- package/dist/services/versions.js +1 -1
- package/dist/telemetry/lib/send-report.d.ts +1 -1
- package/dist/telemetry/lib/send-report.js +1 -1
- package/dist/telemetry/lib/track.js +1 -1
- package/dist/test-utils/knex.js +1 -1
- package/dist/types/collection.d.ts +1 -1
- package/dist/utils/async-handler.d.ts +1 -1
- package/dist/utils/calculate-field-depth.js +1 -1
- package/dist/utils/compress.js +1 -1
- package/dist/utils/deep-map-response.js +2 -2
- package/dist/utils/get-cache-key.js +1 -1
- package/dist/utils/get-field-system-rows.js +1 -1
- package/dist/utils/get-ip-from-req.d.ts +1 -1
- package/dist/utils/get-ip-from-req.js +1 -1
- package/dist/utils/get-local-type.js +7 -3
- package/dist/utils/get-service.js +1 -3
- package/dist/utils/get-snapshot-diff.js +1 -1
- package/dist/utils/is-url-allowed.js +1 -1
- package/dist/utils/jwt.js +1 -1
- package/dist/utils/sanitize-schema.d.ts +1 -1
- package/dist/utils/should-clear-cache.d.ts +1 -1
- package/dist/utils/should-skip-cache.js +2 -2
- package/dist/utils/validate-diff.js +1 -1
- package/dist/utils/validate-snapshot.js +3 -3
- package/dist/utils/validate-storage.js +2 -2
- package/dist/utils/verify-session-jwt.js +1 -1
- package/dist/utils/versioning/deep-map-with-schema.js +2 -2
- package/dist/websocket/controllers/base.d.ts +2 -2
- package/dist/websocket/controllers/base.js +3 -3
- package/dist/websocket/controllers/graphql.d.ts +1 -1
- package/dist/websocket/controllers/graphql.js +1 -1
- package/dist/websocket/controllers/logs.d.ts +1 -1
- package/dist/websocket/controllers/rest.d.ts +1 -1
- package/dist/websocket/controllers/rest.js +2 -2
- package/dist/websocket/handlers/heartbeat.js +1 -1
- package/dist/websocket/handlers/items.js +2 -2
- package/dist/websocket/handlers/subscribe.js +1 -1
- package/dist/websocket/types.d.ts +1 -1
- package/dist/websocket/utils/wait-for-message.js +1 -1
- package/package.json +24 -24
- package/dist/controllers/webhooks.d.ts +0 -2
- package/dist/controllers/webhooks.js +0 -74
- package/dist/services/webhooks.d.ts +0 -14
- package/dist/services/webhooks.js +0 -32
- package/dist/utils/is-login-redirect-allowed.d.ts +0 -4
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { toBoolean } from '@directus/utils';
|
|
2
2
|
import { fetchPermittedAstRootFields } from '../../../../database/run-ast/modules/fetch-permitted-ast-root-fields.js';
|
|
3
|
+
import { fetchPermissions } from '../../../lib/fetch-permissions.js';
|
|
4
|
+
import { fetchPolicies } from '../../../lib/fetch-policies.js';
|
|
5
|
+
import { fetchAllowedFields } from '../../fetch-allowed-fields/fetch-allowed-fields.js';
|
|
6
|
+
import { injectCases } from '../../process-ast/lib/inject-cases.js';
|
|
3
7
|
import { processAst } from '../../process-ast/process-ast.js';
|
|
4
8
|
export async function validateItemAccess(options, context) {
|
|
5
9
|
const primaryKeyField = context.schema.collections[options.collection]?.primary;
|
|
@@ -24,18 +28,62 @@ export async function validateItemAccess(options, context) {
|
|
|
24
28
|
_in: options.primaryKeys,
|
|
25
29
|
},
|
|
26
30
|
};
|
|
31
|
+
let hasItemRules;
|
|
32
|
+
let permissionedFields;
|
|
33
|
+
// Inject the root fields after the permissions have been processed, as to not require access to all collection fields
|
|
34
|
+
if (options.returnAllowedRootFields) {
|
|
35
|
+
const allowedFields = await fetchAllowedFields({ accountability: options.accountability, action: options.action, collection: options.collection }, context);
|
|
36
|
+
const schemaFields = Object.keys(context.schema.collections[options.collection].fields);
|
|
37
|
+
const hasWildcard = allowedFields.includes('*');
|
|
38
|
+
permissionedFields = hasWildcard ? schemaFields : allowedFields;
|
|
39
|
+
const policies = await fetchPolicies(options.accountability, context);
|
|
40
|
+
const permissions = await fetchPermissions({ action: options.action, policies, collections: [options.collection], accountability: options.accountability }, context);
|
|
41
|
+
// Only inject cases if there are item-level permission rules
|
|
42
|
+
hasItemRules = permissions.some((p) => p.permissions && Object.keys(p.permissions).length > 0);
|
|
43
|
+
if (hasItemRules) {
|
|
44
|
+
// Create children only for fields that exist in schema and are allowed by permissions
|
|
45
|
+
ast.children = permissionedFields.map((field) => ({
|
|
46
|
+
type: 'field',
|
|
47
|
+
name: field,
|
|
48
|
+
fieldKey: field,
|
|
49
|
+
whenCase: [],
|
|
50
|
+
alias: false,
|
|
51
|
+
}));
|
|
52
|
+
injectCases(ast, permissions);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
27
55
|
const items = await fetchPermittedAstRootFields(ast, {
|
|
28
56
|
schema: context.schema,
|
|
29
57
|
accountability: options.accountability,
|
|
30
58
|
knex: context.knex,
|
|
31
59
|
action: options.action,
|
|
32
60
|
});
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
if (
|
|
36
|
-
return
|
|
61
|
+
const hasAccess = items && items.length === options.primaryKeys.length;
|
|
62
|
+
if (!hasAccess) {
|
|
63
|
+
if (options.returnAllowedRootFields) {
|
|
64
|
+
return { accessAllowed: false, allowedRootFields: [] };
|
|
65
|
+
}
|
|
66
|
+
return { accessAllowed: false };
|
|
67
|
+
}
|
|
68
|
+
let accessAllowed = true;
|
|
69
|
+
// If specific fields were requested, verify they are all accessible
|
|
70
|
+
if (options.fields) {
|
|
71
|
+
accessAllowed = items.every((item) => options.fields.every((field) => toBoolean(item[field])));
|
|
72
|
+
}
|
|
73
|
+
// If returnAllowedRootFields, return intersection of allowed fields across all items
|
|
74
|
+
if (options.returnAllowedRootFields) {
|
|
75
|
+
// If there are no item-level rules, return the permissioned fields directly
|
|
76
|
+
if (!hasItemRules) {
|
|
77
|
+
return {
|
|
78
|
+
accessAllowed,
|
|
79
|
+
allowedRootFields: permissionedFields,
|
|
80
|
+
};
|
|
37
81
|
}
|
|
38
|
-
|
|
82
|
+
const allowedRootFields = items.length > 0 ? Object.keys(items[0]).filter((field) => items.every((item) => item[field] === 1)) : [];
|
|
83
|
+
return {
|
|
84
|
+
accessAllowed,
|
|
85
|
+
allowedRootFields,
|
|
86
|
+
};
|
|
39
87
|
}
|
|
40
|
-
return
|
|
88
|
+
return { accessAllowed };
|
|
41
89
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ForbiddenError } from '@directus/errors';
|
|
2
|
+
import { createCollectionForbiddenError } from '../process-ast/utils/validate-path/create-error.js';
|
|
2
3
|
import { validateCollectionAccess } from './lib/validate-collection-access.js';
|
|
3
4
|
import { validateItemAccess } from './lib/validate-item-access.js';
|
|
4
|
-
import { createCollectionForbiddenError } from '../process-ast/utils/validate-path/create-error.js';
|
|
5
5
|
/**
|
|
6
6
|
* Validate if the current user has access to perform action against the given collection and
|
|
7
7
|
* optional primary keys. This is done by reading the item from the database using the access
|
|
@@ -20,7 +20,8 @@ export async function validateAccess(options, context) {
|
|
|
20
20
|
// from the database. If no keys are passed, we can simply check if the collection+action combo
|
|
21
21
|
// exists within permissions
|
|
22
22
|
if (options.primaryKeys) {
|
|
23
|
-
|
|
23
|
+
const result = await validateItemAccess(options, context);
|
|
24
|
+
access = result.accessAllowed;
|
|
24
25
|
}
|
|
25
26
|
else {
|
|
26
27
|
access = await validateCollectionAccess(options, context);
|
package/dist/rate-limiter.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
1
2
|
import { useEnv } from '@directus/env';
|
|
2
3
|
import { merge } from 'lodash-es';
|
|
3
4
|
import { RateLimiterMemory, RateLimiterRedis, RateLimiterRes } from 'rate-limiter-flexible';
|
|
4
5
|
import { getConfigFromEnv } from './utils/get-config-from-env.js';
|
|
5
|
-
import { createRequire } from 'node:module';
|
|
6
6
|
const require = createRequire(import.meta.url);
|
|
7
7
|
export function createRateLimiter(configPrefix = 'RATE_LIMITER', configOverrides) {
|
|
8
8
|
const env = useEnv();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import os from 'node:os';
|
|
1
2
|
import { useEnv } from '@directus/env';
|
|
2
3
|
import { ipInNetworks } from '@directus/utils/node';
|
|
3
4
|
import { matches } from 'ip-matching';
|
|
4
|
-
import os from 'node:os';
|
|
5
5
|
import { useLogger } from '../logger/index.js';
|
|
6
6
|
export function isDeniedIp(ip) {
|
|
7
7
|
const env = useEnv();
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { version } from 'directus/version';
|
|
1
2
|
import { random } from 'lodash-es';
|
|
2
3
|
import getDatabase from '../database/index.js';
|
|
3
4
|
import { sendReport } from '../telemetry/index.js';
|
|
4
5
|
import { scheduleSynchronizedJob } from '../utils/schedule.js';
|
|
5
|
-
import { version } from 'directus/version';
|
|
6
6
|
/**
|
|
7
7
|
* Schedule the project status job
|
|
8
8
|
*/
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { useEnv } from '@directus/env';
|
|
2
2
|
import { toBoolean } from '@directus/utils';
|
|
3
3
|
import { getCache } from '../cache.js';
|
|
4
|
-
import { scheduleSynchronizedJob } from '../utils/schedule.js';
|
|
5
4
|
import { track } from '../telemetry/index.js';
|
|
5
|
+
import { scheduleSynchronizedJob } from '../utils/schedule.js';
|
|
6
6
|
/**
|
|
7
7
|
* Exported to be able to test the anonymous callback function
|
|
8
8
|
*/
|
package/dist/schedules/tus.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { RESUMABLE_UPLOADS } from '../constants.js';
|
|
2
|
-
import { getSchema } from '../utils/get-schema.js';
|
|
3
2
|
import { createTusServer } from '../services/tus/index.js';
|
|
3
|
+
import { getSchema } from '../utils/get-schema.js';
|
|
4
4
|
import { scheduleSynchronizedJob, validateCron } from '../utils/schedule.js';
|
|
5
5
|
/**
|
|
6
6
|
* Schedule the tus cleanup
|
package/dist/server.js
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
|
+
import * as http from 'http';
|
|
2
|
+
import * as https from 'https';
|
|
3
|
+
import url from 'url';
|
|
1
4
|
import { useEnv } from '@directus/env';
|
|
2
5
|
import { toBoolean } from '@directus/utils';
|
|
3
6
|
import { getNodeEnv } from '@directus/utils/node';
|
|
4
7
|
import { createTerminus } from '@godaddy/terminus';
|
|
5
|
-
import * as http from 'http';
|
|
6
|
-
import * as https from 'https';
|
|
7
8
|
import { once } from 'lodash-es';
|
|
8
9
|
import qs from 'qs';
|
|
9
|
-
import url from 'url';
|
|
10
10
|
import createApp from './app.js';
|
|
11
11
|
import getDatabase from './database/index.js';
|
|
12
12
|
import emitter from './emitter.js';
|
|
13
13
|
import { useLogger } from './logger/index.js';
|
|
14
|
+
import { getAddress } from './utils/get-address.js';
|
|
14
15
|
import { getConfigFromEnv } from './utils/get-config-from-env.js';
|
|
15
16
|
import { getIPFromReq } from './utils/get-ip-from-req.js';
|
|
16
|
-
import { getAddress } from './utils/get-address.js';
|
|
17
17
|
import { createLogsController, createSubscriptionController, createWebSocketController, getLogsController, getSubscriptionController, getWebSocketController, } from './websocket/controllers/index.js';
|
|
18
18
|
import { startWebSocketHandlers } from './websocket/handlers/index.js';
|
|
19
19
|
export let SERVER_ONLINE = true;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import type { Readable } from 'node:stream';
|
|
1
2
|
import type { AbstractServiceOptions, Accountability, Range, SchemaOverview, Stat, TransformationSet } from '@directus/types';
|
|
2
3
|
import archiver from 'archiver';
|
|
3
4
|
import type { Knex } from 'knex';
|
|
4
|
-
import type { Readable } from 'node:stream';
|
|
5
5
|
import { FilesService } from './files.js';
|
|
6
6
|
export declare class AssetsService {
|
|
7
7
|
knex: Knex;
|
|
@@ -9,6 +9,7 @@ export declare class AssetsService {
|
|
|
9
9
|
schema: SchemaOverview;
|
|
10
10
|
sudoFilesService: FilesService;
|
|
11
11
|
constructor(options: AbstractServiceOptions);
|
|
12
|
+
private sanitizeFields;
|
|
12
13
|
private zip;
|
|
13
14
|
zipFiles(files: string[]): Promise<{
|
|
14
15
|
archive: archiver.Archiver;
|
package/dist/services/assets.js
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
|
+
import path from 'path';
|
|
1
2
|
import { useEnv } from '@directus/env';
|
|
2
3
|
import { ForbiddenError, IllegalAssetTransformationError, InvalidPayloadError, InvalidQueryError, RangeNotSatisfiableError, ServiceUnavailableError, } from '@directus/errors';
|
|
3
4
|
import archiver from 'archiver';
|
|
4
5
|
import { clamp } from 'lodash-es';
|
|
5
6
|
import { contentType, extension } from 'mime-types';
|
|
6
7
|
import hash from 'object-hash';
|
|
7
|
-
import path from 'path';
|
|
8
8
|
import sharp from 'sharp';
|
|
9
9
|
import { SUPPORTED_IMAGE_TRANSFORM_FORMATS } from '../constants.js';
|
|
10
10
|
import getDatabase from '../database/index.js';
|
|
11
11
|
import { useLogger } from '../logger/index.js';
|
|
12
|
-
import {
|
|
12
|
+
import { validateItemAccess } from '../permissions/modules/validate-access/lib/validate-item-access.js';
|
|
13
13
|
import { getStorage } from '../storage/index.js';
|
|
14
14
|
import { getMilliseconds } from '../utils/get-milliseconds.js';
|
|
15
15
|
import { isValidUuid } from '../utils/is-valid-uuid.js';
|
|
16
16
|
import * as TransformationUtils from '../utils/transformations.js';
|
|
17
17
|
import { NameDeduper } from './assets/name-deduper.js';
|
|
18
|
-
import { FilesService } from './files.js';
|
|
19
18
|
import { getSharpInstance } from './files/lib/get-sharp-instance.js';
|
|
19
|
+
import { FilesService } from './files.js';
|
|
20
20
|
import { FoldersService } from './folders.js';
|
|
21
21
|
const env = useEnv();
|
|
22
22
|
const logger = useLogger();
|
|
@@ -31,6 +31,20 @@ export class AssetsService {
|
|
|
31
31
|
this.schema = options.schema;
|
|
32
32
|
this.sudoFilesService = new FilesService({ ...options, accountability: null });
|
|
33
33
|
}
|
|
34
|
+
sanitizeFields(file, allowedFields) {
|
|
35
|
+
if (allowedFields.includes('*')) {
|
|
36
|
+
return file;
|
|
37
|
+
}
|
|
38
|
+
const bypassFields = ['type', 'filesize'];
|
|
39
|
+
const fieldsToKeep = new Set([...allowedFields, ...bypassFields]);
|
|
40
|
+
const filteredFile = {};
|
|
41
|
+
for (const field of fieldsToKeep) {
|
|
42
|
+
if (field in file) {
|
|
43
|
+
filteredFile[field] = file[field];
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return filteredFile;
|
|
47
|
+
}
|
|
34
48
|
zip(options) {
|
|
35
49
|
if (options.files.length === 0) {
|
|
36
50
|
throw new InvalidPayloadError({ reason: 'No files found in the selected folders tree' });
|
|
@@ -135,13 +149,22 @@ export class AssetsService {
|
|
|
135
149
|
*/
|
|
136
150
|
if (!isValidUuid(id))
|
|
137
151
|
throw new ForbiddenError();
|
|
138
|
-
|
|
139
|
-
|
|
152
|
+
let allowedFields = ['*'];
|
|
153
|
+
if (!systemPublicKeys.includes(id) && this.accountability && this.accountability.admin !== true) {
|
|
154
|
+
// Use validateItemAccess to check access and get allowed fields
|
|
155
|
+
const { allowedRootFields, accessAllowed } = await validateItemAccess({
|
|
140
156
|
accountability: this.accountability,
|
|
141
157
|
action: 'read',
|
|
142
158
|
collection: 'directus_files',
|
|
143
159
|
primaryKeys: [id],
|
|
160
|
+
returnAllowedRootFields: true,
|
|
144
161
|
}, { knex: this.knex, schema: this.schema });
|
|
162
|
+
if (!accessAllowed) {
|
|
163
|
+
throw new ForbiddenError({
|
|
164
|
+
reason: `You don't have permission to perform "read" for collection "directus_files" or it does not exist.`,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
allowedFields = allowedRootFields;
|
|
145
168
|
}
|
|
146
169
|
const file = (await this.sudoFilesService.readOne(id, { limit: 1 }));
|
|
147
170
|
const exists = await storage.location(file.storage).exists(file.filename_disk);
|
|
@@ -195,7 +218,7 @@ export class AssetsService {
|
|
|
195
218
|
const assetStream = () => storage.location(file.storage).read(assetFilename, { range });
|
|
196
219
|
return {
|
|
197
220
|
stream: deferStream ? assetStream : await assetStream(),
|
|
198
|
-
file,
|
|
221
|
+
file: this.sanitizeFields(file, allowedFields),
|
|
199
222
|
stat: await storage.location(file.storage).stat(assetFilename),
|
|
200
223
|
};
|
|
201
224
|
}
|
|
@@ -260,13 +283,17 @@ export class AssetsService {
|
|
|
260
283
|
return {
|
|
261
284
|
stream: deferStream ? assetStream : await assetStream(),
|
|
262
285
|
stat: await storage.location(file.storage).stat(assetFilename),
|
|
263
|
-
file,
|
|
286
|
+
file: this.sanitizeFields(file, allowedFields),
|
|
264
287
|
};
|
|
265
288
|
}
|
|
266
289
|
else {
|
|
267
290
|
const assetStream = () => storage.location(file.storage).read(file.filename_disk, { range, version });
|
|
268
291
|
const stat = await storage.location(file.storage).stat(file.filename_disk);
|
|
269
|
-
return {
|
|
292
|
+
return {
|
|
293
|
+
stream: deferStream ? assetStream : await assetStream(),
|
|
294
|
+
file: this.sanitizeFields(file, allowedFields),
|
|
295
|
+
stat,
|
|
296
|
+
};
|
|
270
297
|
}
|
|
271
298
|
}
|
|
272
299
|
}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
+
import { performance } from 'perf_hooks';
|
|
1
2
|
import { Action } from '@directus/constants';
|
|
2
3
|
import { useEnv } from '@directus/env';
|
|
3
4
|
import { InvalidCredentialsError, InvalidOtpError, ServiceUnavailableError, UserSuspendedError, } from '@directus/errors';
|
|
4
5
|
import jwt from 'jsonwebtoken';
|
|
5
6
|
import { clone, cloneDeep } from 'lodash-es';
|
|
6
|
-
import { performance } from 'perf_hooks';
|
|
7
7
|
import { getAuthProvider } from '../auth.js';
|
|
8
8
|
import { DEFAULT_AUTH_PROVIDER } from '../constants.js';
|
|
9
9
|
import getDatabase from '../database/index.js';
|
|
10
10
|
import emitter from '../emitter.js';
|
|
11
11
|
import { fetchRolesTree } from '../permissions/lib/fetch-roles-tree.js';
|
|
12
12
|
import { fetchGlobalAccess } from '../permissions/modules/fetch-global-access/fetch-global-access.js';
|
|
13
|
-
import {
|
|
13
|
+
import { createRateLimiter, RateLimiterRes } from '../rate-limiter.js';
|
|
14
14
|
import { getMilliseconds } from '../utils/get-milliseconds.js';
|
|
15
15
|
import { getSecret } from '../utils/get-secret.js';
|
|
16
16
|
import { stall } from '../utils/stall.js';
|
|
@@ -14,10 +14,10 @@ import { validateAccess } from '../permissions/modules/validate-access/validate-
|
|
|
14
14
|
import { getSchema } from '../utils/get-schema.js';
|
|
15
15
|
import { shouldClearCache } from '../utils/should-clear-cache.js';
|
|
16
16
|
import { transaction } from '../utils/transaction.js';
|
|
17
|
-
import { FieldsService } from './fields.js';
|
|
18
17
|
import { buildCollectionAndFieldRelations } from './fields/build-collection-and-field-relations.js';
|
|
19
18
|
import { getCollectionMetaUpdates } from './fields/get-collection-meta-updates.js';
|
|
20
19
|
import { getCollectionRelationList } from './fields/get-collection-relation-list.js';
|
|
20
|
+
import { FieldsService } from './fields.js';
|
|
21
21
|
import { ItemsService } from './items.js';
|
|
22
22
|
export class CollectionsService {
|
|
23
23
|
knex;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ApiOutput,
|
|
1
|
+
import type { ApiOutput, ExtensionManager, ExtensionSettings } from '@directus/types';
|
|
2
2
|
import type { AbstractServiceOptions, Accountability, DeepPartial, SchemaOverview } from '@directus/types';
|
|
3
3
|
import type { Knex } from 'knex';
|
|
4
4
|
import { ItemsService } from './items.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { File } from '@directus/types';
|
|
2
1
|
import type { Readable } from 'node:stream';
|
|
2
|
+
import type { File } from '@directus/types';
|
|
3
3
|
export type Metadata = Partial<Pick<File, 'height' | 'width' | 'description' | 'title' | 'tags' | 'metadata'>>;
|
|
4
4
|
export declare function getMetadata(stream: Readable, allowList?: string | string[]): Promise<Metadata>;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { pipeline } from 'node:stream/promises';
|
|
1
2
|
import { useEnv } from '@directus/env';
|
|
2
3
|
import exif, {} from 'exif-reader';
|
|
3
4
|
import { parse as parseIcc } from 'icc';
|
|
4
5
|
import { pick } from 'lodash-es';
|
|
5
|
-
import { pipeline } from 'node:stream/promises';
|
|
6
6
|
import { useLogger } from '../../../logger/index.js';
|
|
7
7
|
import { getSharpInstance } from '../lib/get-sharp-instance.js';
|
|
8
8
|
import { parseIptc, parseXmp } from './parse-image-metadata.js';
|
package/dist/services/files.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { AbstractServiceOptions, BusboyFileStream, File, MutationOptions, PrimaryKey, Query, QueryOptions } from '@directus/types';
|
|
2
1
|
import type { Readable } from 'node:stream';
|
|
2
|
+
import type { AbstractServiceOptions, BusboyFileStream, File, MutationOptions, PrimaryKey, Query, QueryOptions } from '@directus/types';
|
|
3
3
|
import { ItemsService } from './items.js';
|
|
4
4
|
export declare class FilesService extends ItemsService<File> {
|
|
5
5
|
constructor(options: AbstractServiceOptions);
|
package/dist/services/files.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import { PassThrough as PassThroughStream, Transform as TransformStream } from 'node:stream';
|
|
2
|
+
import zlib from 'node:zlib';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import url from 'url';
|
|
1
5
|
import { useEnv } from '@directus/env';
|
|
2
6
|
import { ContentTooLargeError, InvalidPayloadError, ServiceUnavailableError } from '@directus/errors';
|
|
3
7
|
import formatTitle from '@directus/format-title';
|
|
@@ -5,10 +9,6 @@ import { toArray } from '@directus/utils';
|
|
|
5
9
|
import encodeURL from 'encodeurl';
|
|
6
10
|
import { clone, cloneDeep } from 'lodash-es';
|
|
7
11
|
import { extension } from 'mime-types';
|
|
8
|
-
import { PassThrough as PassThroughStream, Transform as TransformStream } from 'node:stream';
|
|
9
|
-
import zlib from 'node:zlib';
|
|
10
|
-
import path from 'path';
|
|
11
|
-
import url from 'url';
|
|
12
12
|
import { RESUMABLE_UPLOADS } from '../constants.js';
|
|
13
13
|
import emitter from '../emitter.js';
|
|
14
14
|
import { useLogger } from '../logger/index.js';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AbstractServiceOptions, Accountability,
|
|
1
|
+
import type { AbstractServiceOptions, Accountability, GQLScope, GraphQLParams, Item, PrimaryKey, Query, SchemaOverview } from '@directus/types';
|
|
2
2
|
import type { FormattedExecutionResult, GraphQLSchema } from 'graphql';
|
|
3
3
|
import type { Knex } from 'knex';
|
|
4
4
|
export declare class GraphQLService {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useEnv } from '@directus/env';
|
|
2
|
-
import {
|
|
2
|
+
import { execute, NoSchemaIntrospectionCustomRule, specifiedRules, validate } from 'graphql';
|
|
3
3
|
import getDatabase from '../../database/index.js';
|
|
4
4
|
import { getService } from '../../utils/get-service.js';
|
|
5
5
|
import { formatError } from './errors/format.js';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getService } from '../../../utils/get-service.js';
|
|
2
2
|
import { formatError } from '../errors/format.js';
|
|
3
|
-
import { replaceFragmentsInSelections } from '../utils/replace-fragments.js';
|
|
4
3
|
import { getQuery } from '../schema/parse-query.js';
|
|
4
|
+
import { replaceFragmentsInSelections } from '../utils/replace-fragments.js';
|
|
5
5
|
export async function resolveMutation(gql, args, info) {
|
|
6
6
|
const action = info.fieldName.split('_')[0];
|
|
7
7
|
let collection = info.fieldName.substring(action.length + 1);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import type { GQLScope } from '@directus/types';
|
|
1
2
|
import type { SchemaComposer } from 'graphql-compose';
|
|
2
3
|
import { ObjectTypeComposer } from 'graphql-compose';
|
|
3
|
-
import type { GQLScope } from '@directus/types';
|
|
4
4
|
import { type InconsistentFields, type Schema } from './index.js';
|
|
5
5
|
/**
|
|
6
6
|
* Construct an object of types for every collection, using the permitted fields per action type
|
|
@@ -9,8 +9,8 @@ import { GraphQLDate } from '../types/date.js';
|
|
|
9
9
|
import { GraphQLGeoJSON } from '../types/geojson.js';
|
|
10
10
|
import { GraphQLHash } from '../types/hash.js';
|
|
11
11
|
import { GraphQLStringOrFloat } from '../types/string-or-float.js';
|
|
12
|
-
import { SYSTEM_DENY_LIST } from './index.js';
|
|
13
12
|
import { getTypes } from './get-types.js';
|
|
13
|
+
import { SYSTEM_DENY_LIST } from './index.js';
|
|
14
14
|
/**
|
|
15
15
|
* Create readable types and attach resolvers for each. Also prepares full filter argument structures
|
|
16
16
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { GraphQLService } from './index.js';
|
|
2
1
|
import type { GraphQLResolveInfo } from 'graphql';
|
|
2
|
+
import type { GraphQLService } from './index.js';
|
|
3
3
|
export declare function bindPubSub(): void;
|
|
4
4
|
export declare function createSubscriptionGenerator(gql: GraphQLService, event: string): (_x: unknown, _y: unknown, _z: unknown, request: GraphQLResolveInfo) => AsyncGenerator<{
|
|
5
5
|
[event]: {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import type { Readable } from 'node:stream';
|
|
1
2
|
import type { AbstractServiceOptions, Accountability, DirectusError, ExportFormat, File, Query, SchemaOverview } from '@directus/types';
|
|
2
3
|
import type { Knex } from 'knex';
|
|
3
|
-
import type { Readable } from 'node:stream';
|
|
4
4
|
import type { FieldNode, FunctionFieldNode, NestedCollectionNode } from '../types/index.js';
|
|
5
5
|
export declare function createErrorTracker(): {
|
|
6
6
|
addCapturedError: (err: any, rowNumber: number) => void;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { createReadStream, createWriteStream } from 'node:fs';
|
|
2
|
+
import { appendFile } from 'node:fs/promises';
|
|
1
3
|
import { useEnv } from '@directus/env';
|
|
2
4
|
import { createError, ErrorCode, ForbiddenError, InvalidPayloadError, ServiceUnavailableError, UnsupportedMediaTypeError, } from '@directus/errors';
|
|
3
5
|
import { isSystemCollection } from '@directus/system-data';
|
|
@@ -9,8 +11,6 @@ import { dump as toYAML } from 'js-yaml';
|
|
|
9
11
|
import { parse as toXML } from 'js2xmlparser';
|
|
10
12
|
import { Parser as CSVParser, transforms as CSVTransforms } from 'json2csv';
|
|
11
13
|
import { set } from 'lodash-es';
|
|
12
|
-
import { createReadStream, createWriteStream } from 'node:fs';
|
|
13
|
-
import { appendFile } from 'node:fs/promises';
|
|
14
14
|
import Papa from 'papaparse';
|
|
15
15
|
import StreamArray from 'stream-json/streamers/StreamArray.js';
|
|
16
16
|
import { parseFields } from '../database/get-ast-from-query/lib/parse-fields.js';
|
package/dist/services/index.d.ts
CHANGED
package/dist/services/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import { fileURLToPath } from 'url';
|
|
1
3
|
import { useEnv } from '@directus/env';
|
|
2
4
|
import { InvalidPayloadError } from '@directus/errors';
|
|
3
5
|
import { isObject } from '@directus/utils';
|
|
4
6
|
import fse from 'fs-extra';
|
|
5
7
|
import { Liquid } from 'liquidjs';
|
|
6
|
-
import path from 'path';
|
|
7
|
-
import { fileURLToPath } from 'url';
|
|
8
8
|
import getDatabase from '../../database/index.js';
|
|
9
9
|
import emitter from '../../emitter.js';
|
|
10
10
|
import { useLogger } from '../../logger/index.js';
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { useEnv } from '@directus/env';
|
|
2
|
+
import { EmailLimitExceededError } from '@directus/errors';
|
|
3
|
+
import { toBoolean } from '@directus/utils';
|
|
2
4
|
import { RateLimiterQueue } from 'rate-limiter-flexible';
|
|
3
5
|
import { createRateLimiter } from '../../rate-limiter.js';
|
|
4
|
-
import { toBoolean } from '@directus/utils';
|
|
5
|
-
import { EmailLimitExceededError } from '@directus/errors';
|
|
6
6
|
let emailRateLimiterQueue;
|
|
7
7
|
const env = useEnv();
|
|
8
8
|
if (toBoolean(env['RATE_LIMITER_EMAIL_ENABLED']) === true) {
|
package/dist/services/payload.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
1
2
|
import { ForbiddenError, InvalidPayloadError } from '@directus/errors';
|
|
2
3
|
import { UserIntegrityCheckFlag } from '@directus/types';
|
|
3
4
|
import { parseJSON, toArray } from '@directus/utils';
|
|
@@ -5,12 +6,11 @@ import { format, isValid, parseISO } from 'date-fns';
|
|
|
5
6
|
import { unflatten } from 'flat';
|
|
6
7
|
import Joi from 'joi';
|
|
7
8
|
import { clone, cloneDeep, isNil, isObject, isPlainObject, pick } from 'lodash-es';
|
|
8
|
-
import { randomUUID } from 'node:crypto';
|
|
9
9
|
import { parse as wktToGeoJSON } from 'wellknown';
|
|
10
10
|
import { getHelpers } from '../database/helpers/index.js';
|
|
11
11
|
import getDatabase from '../database/index.js';
|
|
12
|
-
import { generateHash } from '../utils/generate-hash.js';
|
|
13
12
|
import { decrypt, encrypt } from '../utils/encrypt.js';
|
|
13
|
+
import { generateHash } from '../utils/generate-hash.js';
|
|
14
14
|
import { getSecret } from '../utils/get-secret.js';
|
|
15
15
|
/**
|
|
16
16
|
* Process a given payload for a collection to ensure the special fields (hash, uuid, date etc) are
|
package/dist/services/schema.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import getDatabase from '../database/index.js';
|
|
2
1
|
import { ForbiddenError } from '@directus/errors';
|
|
2
|
+
import getDatabase from '../database/index.js';
|
|
3
3
|
import { applyDiff } from '../utils/apply-diff.js';
|
|
4
4
|
import { getSnapshotDiff } from '../utils/get-snapshot-diff.js';
|
|
5
5
|
import { getSnapshot } from '../utils/get-snapshot.js';
|
package/dist/services/server.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
+
import { Readable } from 'node:stream';
|
|
2
|
+
import { performance } from 'perf_hooks';
|
|
1
3
|
import { useEnv } from '@directus/env';
|
|
2
4
|
import { toArray, toBoolean } from '@directus/utils';
|
|
3
5
|
import { version } from 'directus/version';
|
|
4
6
|
import { merge } from 'lodash-es';
|
|
5
|
-
import { Readable } from 'node:stream';
|
|
6
|
-
import { performance } from 'perf_hooks';
|
|
7
7
|
import { getCache } from '../cache.js';
|
|
8
|
-
import { RESUMABLE_UPLOADS } from '../constants.js';
|
|
8
|
+
import { FILE_UPLOADS, RESUMABLE_UPLOADS } from '../constants.js';
|
|
9
9
|
import getDatabase, { hasDatabaseConnection } from '../database/index.js';
|
|
10
10
|
import { useLogger } from '../logger/index.js';
|
|
11
11
|
import getMailer from '../mailer.js';
|
|
@@ -57,9 +57,10 @@ export class ServerService {
|
|
|
57
57
|
],
|
|
58
58
|
});
|
|
59
59
|
info['project'] = projectInfo;
|
|
60
|
-
info['mcp_enabled'] = toBoolean(env['MCP_ENABLED'] ?? true);
|
|
61
60
|
info['setupCompleted'] = setupComplete;
|
|
62
61
|
if (this.accountability?.user) {
|
|
62
|
+
info['mcp_enabled'] = toBoolean(env['MCP_ENABLED'] ?? true);
|
|
63
|
+
info['ai_enabled'] = toBoolean(env['AI_ENABLED'] ?? true);
|
|
63
64
|
if (env['RATE_LIMITER_ENABLED']) {
|
|
64
65
|
info['rateLimit'] = {
|
|
65
66
|
points: env['RATE_LIMITER_POINTS'],
|
|
@@ -112,8 +113,15 @@ export class ServerService {
|
|
|
112
113
|
else {
|
|
113
114
|
info['websocket'] = false;
|
|
114
115
|
}
|
|
116
|
+
if (FILE_UPLOADS.MAX_CONCURRENCY && FILE_UPLOADS.MAX_CONCURRENCY !== Infinity) {
|
|
117
|
+
info['uploads'] = {
|
|
118
|
+
maxConcurrency: FILE_UPLOADS.MAX_CONCURRENCY,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
115
121
|
if (RESUMABLE_UPLOADS.ENABLED) {
|
|
116
122
|
info['uploads'] = {
|
|
123
|
+
...info['uploads'],
|
|
124
|
+
tus: true,
|
|
117
125
|
chunkSize: RESUMABLE_UPLOADS.CHUNK_SIZE,
|
|
118
126
|
};
|
|
119
127
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { ItemsService } from './items.js';
|
|
2
|
-
import { sendReport } from '../telemetry/index.js';
|
|
3
1
|
import { version } from 'directus/version';
|
|
2
|
+
import { sendReport } from '../telemetry/index.js';
|
|
3
|
+
import { ItemsService } from './items.js';
|
|
4
4
|
export class SettingsService extends ItemsService {
|
|
5
5
|
constructor(options) {
|
|
6
6
|
super('directus_settings', options);
|
package/dist/services/tfa.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { InvalidPayloadError } from '@directus/errors';
|
|
2
2
|
import { authenticator } from 'otplib';
|
|
3
|
+
import { DEFAULT_AUTH_PROVIDER } from '../constants.js';
|
|
3
4
|
import getDatabase from '../database/index.js';
|
|
4
5
|
import { ItemsService } from './items.js';
|
|
5
|
-
import { DEFAULT_AUTH_PROVIDER } from '../constants.js';
|
|
6
6
|
export class TFAService {
|
|
7
7
|
knex;
|
|
8
8
|
itemsService;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import getDatabase from '../database/index.js';
|
|
2
1
|
import { InvalidPayloadError } from '@directus/errors';
|
|
2
|
+
import getDatabase from '../database/index.js';
|
|
3
3
|
import { ItemsService } from './items.js';
|
|
4
4
|
export class TranslationsService extends ItemsService {
|
|
5
5
|
constructor(options) {
|