@directus/api 32.2.0 → 33.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/ai/chat/controllers/chat.post.js +19 -4
- package/dist/ai/chat/lib/create-ui-stream.d.ts +8 -7
- package/dist/ai/chat/lib/create-ui-stream.js +28 -25
- package/dist/ai/chat/middleware/load-settings.js +31 -7
- package/dist/ai/chat/models/chat-request.d.ts +135 -2
- package/dist/ai/chat/models/chat-request.js +56 -2
- package/dist/ai/chat/models/providers.d.ts +16 -2
- package/dist/ai/chat/models/providers.js +16 -2
- package/dist/ai/chat/utils/chat-request-tool-to-ai-sdk-tool.js +3 -4
- package/dist/ai/chat/utils/format-context.d.ts +5 -0
- package/dist/ai/chat/utils/format-context.js +122 -0
- package/dist/ai/mcp/server.d.ts +27 -1
- package/dist/ai/providers/index.d.ts +3 -0
- package/dist/ai/providers/index.js +3 -0
- package/dist/ai/providers/options.d.ts +14 -0
- package/dist/ai/providers/options.js +26 -0
- package/dist/ai/providers/registry.d.ts +6 -0
- package/dist/ai/providers/registry.js +65 -0
- package/dist/ai/providers/types.d.ts +34 -0
- package/dist/ai/providers/types.js +1 -0
- 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 +6 -3
- package/dist/ai/tools/items/prompt.md +7 -9
- package/dist/ai/tools/relations/index.js +1 -1
- package/dist/ai/tools/schema.js +1 -1
- package/dist/ai/tools/trigger-flow/index.js +1 -1
- package/dist/app.js +12 -8
- package/dist/auth/drivers/ldap.d.ts +1 -1
- package/dist/auth/drivers/ldap.js +144 -139
- 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.d.ts +12 -0
- package/dist/cache.js +27 -3
- 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/env-stub.liquid +3 -0
- 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/deployment.js +481 -0
- package/dist/controllers/extensions.js +1 -1
- package/dist/controllers/fields.js +8 -6
- 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/get-ast-from-query/lib/parse-fields.js +2 -2
- 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/20260110A-add-ai-provider-settings.d.ts +3 -0
- package/dist/database/migrations/20260110A-add-ai-provider-settings.js +35 -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/20260128A-add-collaborative-editing.d.ts +3 -0
- package/dist/database/migrations/20260128A-add-collaborative-editing.js +10 -0
- package/dist/database/migrations/20260204A-add-deployment.d.ts +3 -0
- package/dist/database/migrations/20260204A-add-deployment.js +32 -0
- package/dist/database/migrations/run.js +3 -3
- package/dist/database/run-ast/lib/apply-query/add-join.js +1 -1
- 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/index.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/deployment/deployment.d.ts +94 -0
- package/dist/deployment/deployment.js +29 -0
- package/dist/deployment/drivers/index.d.ts +1 -0
- package/dist/deployment/drivers/index.js +1 -0
- package/dist/deployment/drivers/vercel.d.ts +32 -0
- package/dist/deployment/drivers/vercel.js +208 -0
- package/dist/deployment/index.d.ts +2 -0
- package/dist/deployment/index.js +2 -0
- package/dist/deployment.d.ts +24 -0
- package/dist/deployment.js +39 -0
- 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/respond.js +27 -14
- 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-ast/utils/find-related-collection.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 +14 -2
- package/dist/permissions/modules/validate-access/lib/validate-item-access.js +72 -13
- 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 +6 -5
- 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/deployment-projects.d.ts +20 -0
- package/dist/services/deployment-projects.js +34 -0
- package/dist/services/deployment-runs.d.ts +13 -0
- package/dist/services/deployment-runs.js +6 -0
- package/dist/services/deployment.d.ts +40 -0
- package/dist/services/deployment.js +202 -0
- 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/resolvers/system-admin.js +2 -3
- 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/graphql/utils/filter-replace-m2a.js +3 -4
- package/dist/services/import-export.d.ts +1 -1
- package/dist/services/import-export.js +2 -2
- package/dist/services/index.d.ts +3 -1
- package/dist/services/index.js +3 -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 +13 -4
- package/dist/services/settings.js +2 -2
- package/dist/services/specifications.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/get-report.js +2 -0
- 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/telemetry/types/report.d.ts +8 -0
- package/dist/telemetry/utils/get-settings.d.ts +2 -0
- package/dist/telemetry/utils/get-settings.js +5 -0
- 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.d.ts +1 -1
- package/dist/utils/deep-map-response.js +2 -2
- package/dist/utils/get-cache-key.js +1 -1
- package/dist/utils/get-column-path.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 +7 -3
- package/dist/utils/get-snapshot-diff.js +1 -1
- package/dist/utils/is-field-allowed.d.ts +4 -0
- package/dist/utils/is-field-allowed.js +9 -0
- 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/handle-version.js +1 -1
- package/dist/websocket/collab/calculate-cache-metadata.d.ts +9 -0
- package/dist/websocket/collab/calculate-cache-metadata.js +121 -0
- package/dist/websocket/collab/collab.d.ts +63 -0
- package/dist/websocket/collab/collab.js +481 -0
- package/dist/websocket/collab/constants.d.ts +1 -0
- package/dist/websocket/collab/constants.js +13 -0
- package/dist/websocket/collab/filter-to-fields.d.ts +2 -0
- package/dist/websocket/collab/filter-to-fields.js +11 -0
- package/dist/websocket/collab/messenger.d.ts +43 -0
- package/dist/websocket/collab/messenger.js +225 -0
- package/dist/websocket/collab/payload-permissions.d.ts +18 -0
- package/dist/websocket/collab/payload-permissions.js +158 -0
- package/dist/websocket/collab/permissions-cache.d.ts +52 -0
- package/dist/websocket/collab/permissions-cache.js +204 -0
- package/dist/websocket/collab/room.d.ts +125 -0
- package/dist/websocket/collab/room.js +593 -0
- package/dist/websocket/collab/store.d.ts +7 -0
- package/dist/websocket/collab/store.js +33 -0
- package/dist/websocket/collab/types.d.ts +21 -0
- package/dist/websocket/collab/types.js +1 -0
- package/dist/websocket/collab/verify-permissions.d.ts +11 -0
- package/dist/websocket/collab/verify-permissions.js +100 -0
- 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/index.d.ts +2 -0
- package/dist/websocket/handlers/index.js +9 -0
- 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/items.d.ts +2 -2
- package/dist/websocket/utils/message.d.ts +1 -1
- package/dist/websocket/utils/message.js +2 -2
- package/dist/websocket/utils/wait-for-message.js +1 -1
- package/package.json +35 -33
- 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/get-relation-info.d.ts +0 -6
- package/dist/utils/get-relation-info.js +0 -43
- package/dist/utils/get-relation-type.d.ts +0 -6
- package/dist/utils/get-relation-type.js +0 -18
- package/dist/utils/is-login-redirect-allowed.d.ts +0 -4
- package/dist/utils/versioning/deep-map-with-schema.d.ts +0 -23
- package/dist/utils/versioning/deep-map-with-schema.js +0 -81
- /package/dist/controllers/{webhooks.d.ts → deployment.d.ts} +0 -0
|
@@ -13,7 +13,7 @@ import { AuthenticationService } from '../../services/authentication.js';
|
|
|
13
13
|
import asyncHandler from '../../utils/async-handler.js';
|
|
14
14
|
import { getConfigFromEnv } from '../../utils/get-config-from-env.js';
|
|
15
15
|
import { getSchema } from '../../utils/get-schema.js';
|
|
16
|
-
import { isLoginRedirectAllowed } from '
|
|
16
|
+
import { isLoginRedirectAllowed } from '../utils/is-login-redirect-allowed.js';
|
|
17
17
|
import { LocalAuthDriver } from './local.js';
|
|
18
18
|
// Register the samlify schema validator
|
|
19
19
|
samlify.setSchemaValidator(validator);
|
|
@@ -95,7 +95,7 @@ export function createSAMLAuthRouter(providerName) {
|
|
|
95
95
|
const parsedUrl = new URL(url);
|
|
96
96
|
if (req.query['redirect']) {
|
|
97
97
|
const redirect = req.query['redirect'];
|
|
98
|
-
if (isLoginRedirectAllowed(
|
|
98
|
+
if (!isLoginRedirectAllowed(providerName, redirect)) {
|
|
99
99
|
throw new InvalidPayloadError({ reason: `URL "${redirect}" can't be used to redirect after login` });
|
|
100
100
|
}
|
|
101
101
|
parsedUrl.searchParams.append('RelayState', redirect);
|
|
@@ -117,7 +117,7 @@ export function createSAMLAuthRouter(providerName) {
|
|
|
117
117
|
const logger = useLogger();
|
|
118
118
|
const relayState = req.body?.RelayState;
|
|
119
119
|
const authMode = (env[`AUTH_${providerName.toUpperCase()}_MODE`] ?? 'session');
|
|
120
|
-
if (relayState && isLoginRedirectAllowed(
|
|
120
|
+
if (relayState && isLoginRedirectAllowed(providerName, relayState) === false) {
|
|
121
121
|
throw new InvalidPayloadError({ reason: `URL "${relayState}" can't be used to redirect after login` });
|
|
122
122
|
}
|
|
123
123
|
try {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dynamically generate the callback URL for OAuth2/OpenID SSO providers
|
|
3
|
+
*
|
|
4
|
+
* Uses AUTH_ALLOWED_PUBLIC_URLS to find an alternate PUBLIC_URL based on the origins protocol and host.
|
|
5
|
+
* Defaults to the PUBLIC_URL if no match is found.
|
|
6
|
+
*
|
|
7
|
+
* @param providerName SSO provider name
|
|
8
|
+
* @param requestOrigin Origin of the request (protocol + host)
|
|
9
|
+
* @returns Callback URL
|
|
10
|
+
*/
|
|
11
|
+
export declare function generateCallbackUrl(providerName: string, requestOrigin: string): string;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { useEnv } from '@directus/env';
|
|
2
|
+
import { toArray } from '@directus/utils';
|
|
3
|
+
import { Url } from '../../utils/url.js';
|
|
4
|
+
/**
|
|
5
|
+
* Find a matching public URL based on the request origins protocol and host
|
|
6
|
+
*
|
|
7
|
+
* @param requestOrigin - The origin of the request
|
|
8
|
+
* @param allowedPublicUrls - The allowed public URLs from AUTH_ALLOWED_PUBLIC_URLS
|
|
9
|
+
* @returns The matching public URL
|
|
10
|
+
*/
|
|
11
|
+
function findMatchingPublicUrl(requestOrigin, allowedPublicUrls) {
|
|
12
|
+
for (const allowedUrl of allowedPublicUrls) {
|
|
13
|
+
if (!URL.canParse(allowedUrl))
|
|
14
|
+
continue;
|
|
15
|
+
const { protocol, host } = new URL(allowedUrl);
|
|
16
|
+
const allowedUrlOrigin = `${protocol}//${host}`;
|
|
17
|
+
if (requestOrigin === allowedUrlOrigin) {
|
|
18
|
+
return allowedUrl;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Dynamically generate the callback URL for OAuth2/OpenID SSO providers
|
|
25
|
+
*
|
|
26
|
+
* Uses AUTH_ALLOWED_PUBLIC_URLS to find an alternate PUBLIC_URL based on the origins protocol and host.
|
|
27
|
+
* Defaults to the PUBLIC_URL if no match is found.
|
|
28
|
+
*
|
|
29
|
+
* @param providerName SSO provider name
|
|
30
|
+
* @param requestOrigin Origin of the request (protocol + host)
|
|
31
|
+
* @returns Callback URL
|
|
32
|
+
*/
|
|
33
|
+
export function generateCallbackUrl(providerName, requestOrigin) {
|
|
34
|
+
const env = useEnv();
|
|
35
|
+
const publicUrl = env['PUBLIC_URL'];
|
|
36
|
+
const allowedPublicUrls = env['AUTH_ALLOWED_PUBLIC_URLS'] ? toArray(env['AUTH_ALLOWED_PUBLIC_URLS']) : [];
|
|
37
|
+
const matchedUrl = findMatchingPublicUrl(requestOrigin, allowedPublicUrls);
|
|
38
|
+
// Use matched public URL or fallback to PUBLIC_URL for backward compatibility
|
|
39
|
+
return new Url(matchedUrl || publicUrl).addPath('auth', 'login', providerName, 'callback').toString();
|
|
40
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checks if the defined redirect after successful SSO login is in the allow list
|
|
3
|
+
* @param provider SSO provider name
|
|
4
|
+
* @param redirect URL to redirect to after login
|
|
5
|
+
* @returns True if the redirect is allowed, false otherwise
|
|
6
|
+
*/
|
|
7
|
+
export declare function isLoginRedirectAllowed(provider: string, redirect: unknown): boolean;
|
|
@@ -1,26 +1,28 @@
|
|
|
1
1
|
import { useEnv } from '@directus/env';
|
|
2
2
|
import { toArray } from '@directus/utils';
|
|
3
|
-
import { useLogger } from '
|
|
4
|
-
import isUrlAllowed from '
|
|
3
|
+
import { useLogger } from '../../logger/index.js';
|
|
4
|
+
import isUrlAllowed from '../../utils/is-url-allowed.js';
|
|
5
5
|
/**
|
|
6
6
|
* Checks if the defined redirect after successful SSO login is in the allow list
|
|
7
|
+
* @param provider SSO provider name
|
|
8
|
+
* @param redirect URL to redirect to after login
|
|
9
|
+
* @returns True if the redirect is allowed, false otherwise
|
|
7
10
|
*/
|
|
8
|
-
export function isLoginRedirectAllowed(
|
|
11
|
+
export function isLoginRedirectAllowed(provider, redirect) {
|
|
9
12
|
if (!redirect)
|
|
10
13
|
return true; // empty redirect
|
|
11
14
|
if (typeof redirect !== 'string')
|
|
12
15
|
return false; // invalid type
|
|
13
16
|
const env = useEnv();
|
|
14
17
|
const publicUrl = env['PUBLIC_URL'];
|
|
15
|
-
if (URL.canParse(redirect)
|
|
16
|
-
if (redirect.startsWith('//')
|
|
18
|
+
if (!URL.canParse(redirect)) {
|
|
19
|
+
if (!redirect.startsWith('//')) {
|
|
17
20
|
// should be a relative path like `/admin/test`
|
|
18
21
|
return true;
|
|
19
22
|
}
|
|
20
23
|
// domain without protocol `//example.com/test`
|
|
21
24
|
return false;
|
|
22
25
|
}
|
|
23
|
-
const { protocol: redirectProtocol, hostname: redirectDomain } = new URL(redirect);
|
|
24
26
|
const envKey = `AUTH_${provider.toUpperCase()}_REDIRECT_ALLOW_LIST`;
|
|
25
27
|
if (envKey in env) {
|
|
26
28
|
if (isUrlAllowed(redirect, [...toArray(env[envKey]), publicUrl]))
|
|
@@ -30,7 +32,8 @@ export function isLoginRedirectAllowed(redirect, provider) {
|
|
|
30
32
|
useLogger().error('Invalid PUBLIC_URL for login redirect');
|
|
31
33
|
return false;
|
|
32
34
|
}
|
|
33
|
-
|
|
34
|
-
const { protocol: publicProtocol,
|
|
35
|
-
|
|
35
|
+
const { protocol: redirectProtocol, host: redirectHost } = new URL(redirect);
|
|
36
|
+
const { protocol: publicProtocol, host: publicHost } = new URL(publicUrl);
|
|
37
|
+
// allow redirects to the defined PUBLIC_URL (protocol + host including port)
|
|
38
|
+
return `${redirectProtocol}//${redirectHost}` === `${publicProtocol}//${publicHost}`;
|
|
36
39
|
}
|
package/dist/cache.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import Keyv from 'keyv';
|
|
|
3
3
|
export declare function getCache(): {
|
|
4
4
|
cache: Keyv | null;
|
|
5
5
|
systemCache: Keyv;
|
|
6
|
+
deploymentCache: Keyv;
|
|
6
7
|
localSchemaCache: Keyv;
|
|
7
8
|
lockCache: Keyv;
|
|
8
9
|
};
|
|
@@ -17,3 +18,14 @@ export declare function setMemorySchemaCache(schema: SchemaOverview): void;
|
|
|
17
18
|
export declare function getMemorySchemaCache(): Readonly<SchemaOverview> | undefined;
|
|
18
19
|
export declare function setCacheValue(cache: Keyv, key: string, value: Record<string, any> | Record<string, any>[], ttl?: number): Promise<void>;
|
|
19
20
|
export declare function getCacheValue(cache: Keyv, key: string): Promise<any>;
|
|
21
|
+
/**
|
|
22
|
+
* Store a value in cache with its expiration timestamp for TTL tracking
|
|
23
|
+
*/
|
|
24
|
+
export declare function setCacheValueWithExpiry(cache: Keyv, key: string, value: Record<string, any> | Record<string, any>[], ttl: number): Promise<void>;
|
|
25
|
+
/**
|
|
26
|
+
* Get a cached value along with its remaining TTL
|
|
27
|
+
*/
|
|
28
|
+
export declare function getCacheValueWithTTL(cache: Keyv, key: string): Promise<{
|
|
29
|
+
data: any;
|
|
30
|
+
remainingTTL: number;
|
|
31
|
+
} | undefined>;
|
package/dist/cache.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createRequire } from 'node:module';
|
|
1
2
|
import { useEnv } from '@directus/env';
|
|
2
3
|
import Keyv, {} from 'keyv';
|
|
3
4
|
import { useBus } from './bus/index.js';
|
|
@@ -5,16 +6,16 @@ import { useLogger } from './logger/index.js';
|
|
|
5
6
|
import { clearCache as clearPermissionCache } from './permissions/cache.js';
|
|
6
7
|
import { redisConfigAvailable } from './redis/index.js';
|
|
7
8
|
import { compress, decompress } from './utils/compress.js';
|
|
9
|
+
import { freezeSchema, unfreezeSchema } from './utils/freeze-schema.js';
|
|
8
10
|
import { getConfigFromEnv } from './utils/get-config-from-env.js';
|
|
9
11
|
import { getMilliseconds } from './utils/get-milliseconds.js';
|
|
10
12
|
import { validateEnv } from './utils/validate-env.js';
|
|
11
|
-
import { createRequire } from 'node:module';
|
|
12
|
-
import { freezeSchema, unfreezeSchema } from './utils/freeze-schema.js';
|
|
13
13
|
const logger = useLogger();
|
|
14
14
|
const env = useEnv();
|
|
15
15
|
const require = createRequire(import.meta.url);
|
|
16
16
|
let cache = null;
|
|
17
17
|
let systemCache = null;
|
|
18
|
+
let deploymentCache = null;
|
|
18
19
|
let lockCache = null;
|
|
19
20
|
let messengerSubscribed = false;
|
|
20
21
|
let localSchemaCache = null;
|
|
@@ -40,6 +41,11 @@ export function getCache() {
|
|
|
40
41
|
systemCache = getKeyvInstance(env['CACHE_STORE'], getMilliseconds(env['CACHE_SYSTEM_TTL']), '_system');
|
|
41
42
|
systemCache.on('error', (err) => logger.warn(err, `[system-cache] ${err}`));
|
|
42
43
|
}
|
|
44
|
+
if (deploymentCache === null) {
|
|
45
|
+
const ttl = getMilliseconds(env['CACHE_DEPLOYMENT_TTL']) || 5000; // Default 5s
|
|
46
|
+
deploymentCache = getKeyvInstance(env['CACHE_STORE'], ttl, '_deployment');
|
|
47
|
+
deploymentCache.on('error', (err) => logger.warn(err, `[deployment-cache] ${err}`));
|
|
48
|
+
}
|
|
43
49
|
if (localSchemaCache === null) {
|
|
44
50
|
localSchemaCache = getKeyvInstance('memory', getMilliseconds(env['CACHE_SYSTEM_TTL']), '_schema');
|
|
45
51
|
localSchemaCache.on('error', (err) => logger.warn(err, `[schema-cache] ${err}`));
|
|
@@ -48,7 +54,7 @@ export function getCache() {
|
|
|
48
54
|
lockCache = getKeyvInstance(env['CACHE_STORE'], undefined, '_lock');
|
|
49
55
|
lockCache.on('error', (err) => logger.warn(err, `[lock-cache] ${err}`));
|
|
50
56
|
}
|
|
51
|
-
return { cache, systemCache, localSchemaCache, lockCache };
|
|
57
|
+
return { cache, systemCache, deploymentCache, localSchemaCache, lockCache };
|
|
52
58
|
}
|
|
53
59
|
export async function flushCaches(forced) {
|
|
54
60
|
const { cache } = getCache();
|
|
@@ -107,6 +113,24 @@ export async function getCacheValue(cache, key) {
|
|
|
107
113
|
const decompressed = await decompress(value);
|
|
108
114
|
return decompressed;
|
|
109
115
|
}
|
|
116
|
+
/**
|
|
117
|
+
* Store a value in cache with its expiration timestamp for TTL tracking
|
|
118
|
+
*/
|
|
119
|
+
export async function setCacheValueWithExpiry(cache, key, value, ttl) {
|
|
120
|
+
await setCacheValue(cache, key, value, ttl);
|
|
121
|
+
await setCacheValue(cache, `${key}__expires_at`, { exp: Date.now() + ttl }, ttl);
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Get a cached value along with its remaining TTL
|
|
125
|
+
*/
|
|
126
|
+
export async function getCacheValueWithTTL(cache, key) {
|
|
127
|
+
const value = await getCacheValue(cache, key);
|
|
128
|
+
if (!value)
|
|
129
|
+
return undefined;
|
|
130
|
+
const expiryData = await getCacheValue(cache, `${key}__expires_at`);
|
|
131
|
+
const remainingTTL = expiryData?.exp ? Math.max(0, expiryData.exp - Date.now()) : 0;
|
|
132
|
+
return { data: value, remainingTTL };
|
|
133
|
+
}
|
|
110
134
|
function getKeyvInstance(store, ttl, namespaceSuffix) {
|
|
111
135
|
switch (store) {
|
|
112
136
|
case 'redis':
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { useEnv } from '@directus/env';
|
|
2
|
+
import { email } from 'zod';
|
|
2
3
|
import getDatabase, { hasDatabaseConnection, isInstalled, validateDatabaseConnection, } from '../../../database/index.js';
|
|
3
4
|
import runMigrations from '../../../database/migrations/run.js';
|
|
4
5
|
import installDatabase from '../../../database/seeds/run.js';
|
|
5
6
|
import { useLogger } from '../../../logger/index.js';
|
|
6
7
|
import { SettingsService } from '../../../services/settings.js';
|
|
7
|
-
import { getSchema } from '../../../utils/get-schema.js';
|
|
8
8
|
import { createAdmin } from '../../../utils/create-admin.js';
|
|
9
|
-
import {
|
|
9
|
+
import { getSchema } from '../../../utils/get-schema.js';
|
|
10
10
|
export default async function bootstrap({ skipAdminInit }) {
|
|
11
11
|
const logger = useLogger();
|
|
12
12
|
logger.info('Initializing bootstrap...');
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import installSeeds from '../../../database/seeds/run.js';
|
|
2
1
|
import getDatabase from '../../../database/index.js';
|
|
2
|
+
import installSeeds from '../../../database/seeds/run.js';
|
|
3
3
|
import { useLogger } from '../../../logger/index.js';
|
|
4
4
|
export default async function start() {
|
|
5
5
|
const database = getDatabase();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import run from '../../../database/migrations/run.js';
|
|
2
1
|
import getDatabase from '../../../database/index.js';
|
|
2
|
+
import run from '../../../database/migrations/run.js';
|
|
3
3
|
import { useLogger } from '../../../logger/index.js';
|
|
4
4
|
export default async function migrate(direction) {
|
|
5
5
|
const database = getDatabase();
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
1
2
|
import chalk from 'chalk';
|
|
2
3
|
import { execa } from 'execa';
|
|
3
4
|
import inquirer from 'inquirer';
|
|
4
5
|
import Joi from 'joi';
|
|
5
|
-
import { randomUUID } from 'node:crypto';
|
|
6
6
|
import ora from 'ora';
|
|
7
7
|
import runMigrations from '../../../database/migrations/run.js';
|
|
8
8
|
import runSeed from '../../../database/seeds/run.js';
|
|
9
|
+
import { defaultAdminPolicy, defaultAdminRole, defaultAdminUser } from '../../../utils/create-admin.js';
|
|
9
10
|
import { generateHash } from '../../../utils/generate-hash.js';
|
|
10
11
|
import createDBConnection from '../../utils/create-db-connection.js';
|
|
11
12
|
import createEnv from '../../utils/create-env/index.js';
|
|
12
|
-
import { defaultAdminPolicy, defaultAdminRole, defaultAdminUser } from '../../../utils/create-admin.js';
|
|
13
13
|
import { drivers, getDriverForClient } from '../../utils/drivers.js';
|
|
14
14
|
import { databaseQuestions } from './questions.js';
|
|
15
15
|
export default async function init() {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { getSchema } from '../../../utils/get-schema.js';
|
|
2
|
-
import { RolesService } from '../../../services/roles.js';
|
|
3
|
-
import { PoliciesService } from '../../../services/index.js';
|
|
4
|
-
import { AccessService } from '../../../services/index.js';
|
|
5
1
|
import getDatabase from '../../../database/index.js';
|
|
6
2
|
import { useLogger } from '../../../logger/index.js';
|
|
3
|
+
import { PoliciesService } from '../../../services/index.js';
|
|
4
|
+
import { AccessService } from '../../../services/index.js';
|
|
5
|
+
import { RolesService } from '../../../services/roles.js';
|
|
6
|
+
import { getSchema } from '../../../utils/get-schema.js';
|
|
7
7
|
export default async function rolesCreate({ role: name, admin, app, }) {
|
|
8
8
|
const database = getDatabase();
|
|
9
9
|
const logger = useLogger();
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { promises as fs } from 'fs';
|
|
2
|
+
import path from 'path';
|
|
2
3
|
import { DiffKind } from '@directus/types';
|
|
4
|
+
import { parseJSON } from '@directus/utils';
|
|
3
5
|
import chalk from 'chalk';
|
|
4
|
-
import { promises as fs } from 'fs';
|
|
5
6
|
import inquirer from 'inquirer';
|
|
6
7
|
import { load as loadYaml } from 'js-yaml';
|
|
7
|
-
import path from 'path';
|
|
8
8
|
import getDatabase, { isInstalled, validateDatabaseConnection } from '../../../database/index.js';
|
|
9
9
|
import { useLogger } from '../../../logger/index.js';
|
|
10
10
|
import { isNestedMetaUpdate } from '../../../utils/apply-diff.js';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { promises as fs, constants as fsConstants } from 'fs';
|
|
2
|
+
import path from 'path';
|
|
2
3
|
import inquirer from 'inquirer';
|
|
3
4
|
import { dump as toYaml } from 'js-yaml';
|
|
4
|
-
import path from 'path';
|
|
5
5
|
import getDatabase from '../../../database/index.js';
|
|
6
6
|
import { useLogger } from '../../../logger/index.js';
|
|
7
7
|
import { getSnapshot } from '../../../utils/get-snapshot.js';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import knex from 'knex';
|
|
2
1
|
import { dirname } from 'node:path';
|
|
3
2
|
import { fileURLToPath } from 'node:url';
|
|
4
3
|
import path from 'path';
|
|
4
|
+
import knex from 'knex';
|
|
5
5
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
6
6
|
export default function createDBConnection(client, credentials) {
|
|
7
7
|
let connection = {};
|
|
@@ -148,6 +148,9 @@ CACHE_ENABLED=false
|
|
|
148
148
|
# How long the cache is persisted ["5m"]
|
|
149
149
|
# CACHE_TTL="30m"
|
|
150
150
|
|
|
151
|
+
# How long deployment provider data is cached ["5s"]
|
|
152
|
+
# CACHE_DEPLOYMENT_TTL="5s"
|
|
153
|
+
|
|
151
154
|
# How to scope the cache data ["system-cache"]
|
|
152
155
|
# CACHE_NAMESPACE="system-cache"
|
|
153
156
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
|
-
import { Liquid } from 'liquidjs';
|
|
3
2
|
import { dirname } from 'node:path';
|
|
4
3
|
import { fileURLToPath } from 'node:url';
|
|
5
4
|
import path from 'path';
|
|
6
5
|
import { promisify } from 'util';
|
|
6
|
+
import { Liquid } from 'liquidjs';
|
|
7
7
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
8
|
const readFile = promisify(fs.readFile);
|
|
9
9
|
const writeFile = promisify(fs.writeFile);
|
package/dist/constants.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { CookieOptions } from 'express';
|
|
2
1
|
import type { TransformationParams } from '@directus/types';
|
|
2
|
+
import type { CookieOptions } from 'express';
|
|
3
3
|
export declare const SYSTEM_ASSET_ALLOW_LIST: TransformationParams[];
|
|
4
4
|
export declare const ASSET_TRANSFORM_QUERY_KEYS: readonly ["key", "transforms", "width", "height", "format", "fit", "quality", "withoutEnlargement", "focal_point_x", "focal_point_y"];
|
|
5
5
|
export declare const FILTER_VARIABLES: string[];
|
|
@@ -15,11 +15,15 @@ export declare const OAS_REQUIRED_SCHEMAS: string[];
|
|
|
15
15
|
export declare const SUPPORTED_IMAGE_TRANSFORM_FORMATS: string[];
|
|
16
16
|
/** Formats where metadata extraction is supported */
|
|
17
17
|
export declare const SUPPORTED_IMAGE_METADATA_FORMATS: string[];
|
|
18
|
-
/**
|
|
18
|
+
/** File uploads */
|
|
19
|
+
export declare const FILE_UPLOADS: {
|
|
20
|
+
MAX_SIZE: number | null;
|
|
21
|
+
MAX_CONCURRENCY: number;
|
|
22
|
+
};
|
|
23
|
+
/** Resumable uploads (TUS) */
|
|
19
24
|
export declare const RESUMABLE_UPLOADS: {
|
|
20
25
|
ENABLED: boolean;
|
|
21
26
|
CHUNK_SIZE: number | null;
|
|
22
|
-
MAX_SIZE: number | null;
|
|
23
27
|
EXPIRATION_TIME: number;
|
|
24
28
|
SCHEDULE: string;
|
|
25
29
|
};
|
package/dist/constants.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { getMilliseconds } from './utils/get-milliseconds.js';
|
|
2
1
|
import { useEnv } from '@directus/env';
|
|
3
2
|
import { toBoolean } from '@directus/utils';
|
|
4
3
|
import bytes from 'bytes';
|
|
4
|
+
import { getMilliseconds } from './utils/get-milliseconds.js';
|
|
5
5
|
const env = useEnv();
|
|
6
6
|
export const SYSTEM_ASSET_ALLOW_LIST = [
|
|
7
7
|
{
|
|
@@ -87,11 +87,15 @@ export const SUPPORTED_IMAGE_METADATA_FORMATS = [
|
|
|
87
87
|
'image/tiff',
|
|
88
88
|
'image/avif',
|
|
89
89
|
];
|
|
90
|
-
/**
|
|
90
|
+
/** File uploads */
|
|
91
|
+
export const FILE_UPLOADS = {
|
|
92
|
+
MAX_SIZE: bytes.parse(env['FILES_MAX_UPLOAD_SIZE']),
|
|
93
|
+
MAX_CONCURRENCY: Number(env['FILES_MAX_UPLOAD_CONCURRENCY']),
|
|
94
|
+
};
|
|
95
|
+
/** Resumable uploads (TUS) */
|
|
91
96
|
export const RESUMABLE_UPLOADS = {
|
|
92
97
|
ENABLED: toBoolean(env['TUS_ENABLED']),
|
|
93
98
|
CHUNK_SIZE: bytes.parse(env['TUS_CHUNK_SIZE']),
|
|
94
|
-
MAX_SIZE: bytes.parse(env['FILES_MAX_UPLOAD_SIZE']),
|
|
95
99
|
EXPIRATION_TIME: getMilliseconds(env['TUS_UPLOAD_EXPIRATION'], 600_000 /* 10min */),
|
|
96
100
|
SCHEDULE: String(env['TUS_CLEANUP_SCHEDULE']),
|
|
97
101
|
};
|
|
@@ -3,8 +3,8 @@ import express from 'express';
|
|
|
3
3
|
import { respond } from '../middleware/respond.js';
|
|
4
4
|
import useCollection from '../middleware/use-collection.js';
|
|
5
5
|
import { validateBatch } from '../middleware/validate-batch.js';
|
|
6
|
-
import { MetaService } from '../services/meta.js';
|
|
7
6
|
import { AccessService } from '../services/access.js';
|
|
7
|
+
import { MetaService } from '../services/meta.js';
|
|
8
8
|
import asyncHandler from '../utils/async-handler.js';
|
|
9
9
|
import { sanitizeQuery } from '../utils/sanitize-query.js';
|
|
10
10
|
const router = express.Router();
|
|
@@ -189,7 +189,7 @@ asyncHandler(async (req, res) => {
|
|
|
189
189
|
}
|
|
190
190
|
}
|
|
191
191
|
const { stream, file, stat } = await service.getAsset(id, { transformationParams, acceptFormat }, range, true);
|
|
192
|
-
const filename = req.params['filename'] ?? file.filename_download;
|
|
192
|
+
const filename = req.params['filename'] ?? file.filename_download ?? file.id;
|
|
193
193
|
res.attachment(filename);
|
|
194
194
|
res.setHeader('Content-Type', file.type);
|
|
195
195
|
res.setHeader('Accept-Ranges', 'bytes');
|