@directus/api 35.2.0 → 36.0.0-rc.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/models/chat-request.js +48 -48
- package/dist/ai/chat/models/object-request.js +6 -6
- package/dist/ai/chat/models/providers.js +14 -14
- package/dist/ai/chat/utils/parse-json-schema-7.js +22 -22
- package/dist/ai/mcp/server.js +44 -6
- package/dist/ai/mcp/utils.js +31 -0
- package/dist/ai/tools/assets/index.js +3 -3
- package/dist/ai/tools/collections/index.js +18 -18
- package/dist/ai/tools/fields/index.js +18 -18
- package/dist/ai/tools/files/index.js +18 -18
- package/dist/ai/tools/flows/index.js +16 -16
- package/dist/ai/tools/folders/index.js +18 -18
- package/dist/ai/tools/items/index.js +17 -17
- package/dist/ai/tools/operations/index.js +16 -16
- package/dist/ai/tools/relations/index.js +22 -22
- package/dist/ai/tools/schema/index.js +3 -3
- package/dist/ai/tools/schema.js +159 -159
- package/dist/ai/tools/system/index.js +3 -3
- package/dist/ai/tools/trigger-flow/index.js +3 -3
- package/dist/app.js +33 -9
- package/dist/auth/drivers/ldap.js +3 -1
- package/dist/auth/drivers/local.js +2 -0
- package/dist/auth/drivers/oauth2.js +3 -1
- package/dist/auth/drivers/openid.js +3 -1
- package/dist/auth/drivers/saml.js +2 -0
- package/dist/auth/utils/check-local-disabled.js +16 -0
- package/dist/auth/utils/check-sso-enabled.js +14 -0
- package/dist/auth.js +8 -5
- package/dist/cli/commands/bootstrap/index.js +3 -0
- package/dist/cli/commands/cache/clear.js +6 -1
- package/dist/cli/commands/roles/create.js +4 -1
- package/dist/cli/commands/users/create.js +3 -0
- package/dist/constants.js +8 -1
- package/dist/controllers/access.js +1 -1
- package/dist/controllers/activity.js +2 -1
- package/dist/controllers/assets.js +2 -0
- package/dist/controllers/auth.js +13 -5
- package/dist/controllers/collections.js +1 -1
- package/dist/controllers/comments.js +1 -1
- package/dist/controllers/dashboards.js +1 -1
- package/dist/controllers/fields.js +1 -1
- package/dist/controllers/files.js +3 -1
- package/dist/controllers/flows.js +6 -5
- package/dist/controllers/folders.js +1 -1
- package/dist/controllers/graphql.js +2 -0
- package/dist/controllers/items.js +3 -1
- package/dist/controllers/license.js +119 -0
- package/dist/controllers/mcp/index.js +38 -0
- package/dist/controllers/mcp/oauth-clients.js +68 -0
- package/dist/controllers/mcp/oauth-consent-page.js +316 -0
- package/dist/controllers/mcp/oauth.js +381 -0
- package/dist/controllers/mcp/templates/oauth-consent.liquid +62 -0
- package/dist/controllers/mcp/templates/oauth-error.liquid +28 -0
- package/dist/controllers/notifications.js +1 -1
- package/dist/controllers/operations.js +1 -1
- package/dist/controllers/panels.js +1 -1
- package/dist/controllers/permissions.js +1 -1
- package/dist/controllers/policies.js +1 -1
- package/dist/controllers/presets.js +1 -1
- package/dist/controllers/revisions.js +3 -2
- package/dist/controllers/roles.js +1 -1
- package/dist/controllers/server.js +38 -9
- package/dist/controllers/shares.js +1 -1
- package/dist/controllers/translations.js +1 -1
- package/dist/controllers/users.js +1 -1
- package/dist/controllers/utils.js +2 -2
- package/dist/controllers/versions.js +12 -5
- package/dist/database/get-ast-from-query/lib/convert-wildcards.js +10 -1
- package/dist/database/get-ast-from-query/lib/parse-fields.js +2 -1
- package/dist/database/helpers/fn/dialects/mysql.js +7 -12
- package/dist/database/helpers/fn/dialects/oracle.js +3 -4
- package/dist/database/helpers/fn/dialects/postgres.js +4 -26
- package/dist/database/helpers/fn/json/mysql-json-path.js +22 -0
- package/dist/database/helpers/fn/json/parse-function.js +14 -6
- package/dist/database/helpers/fn/json/postgres-json-path.js +54 -0
- package/dist/database/migrations/20260110A-add-ai-provider-settings.js +4 -4
- package/dist/database/migrations/20260217A-null-item-versions.js +14 -0
- package/dist/database/migrations/20260312A-add-ai-translation-settings.js +18 -0
- package/dist/database/migrations/20260507A-add-licensing.js +22 -0
- package/dist/database/migrations/20260512A-add-autosave-revision-interval.js +14 -0
- package/dist/database/migrations/20260512B-add-mcp-oauth.js +87 -0
- package/dist/database/run-ast/lib/apply-query/filter/operator.js +116 -33
- package/dist/database/run-ast/lib/apply-query/index.js +4 -1
- package/dist/database/run-ast/lib/apply-query/sort.js +17 -7
- package/dist/database/run-ast/lib/get-db-query.js +21 -9
- package/dist/database/run-ast/lib/parse-current-level.js +2 -1
- package/dist/database/run-ast/run-ast.js +2 -1
- package/dist/database/run-ast/utils/get-column.js +2 -1
- package/dist/extensions/lib/installation/manager.js +1 -1
- package/dist/extensions/lib/sandbox/register/operation.js +1 -1
- package/dist/extensions/lib/sync/sync.js +1 -1
- package/dist/extensions/manager.js +3 -3
- package/dist/flows.js +5 -5
- package/dist/license/entitlements/lib/collections.js +37 -0
- package/dist/license/entitlements/lib/custom-llms-enabled.js +18 -0
- package/dist/license/entitlements/lib/custom-permission-rules-enabled.js +41 -0
- package/dist/license/entitlements/lib/flows.js +29 -0
- package/dist/license/entitlements/lib/seats.js +103 -0
- package/dist/license/entitlements/lib/sso-enabled.js +45 -0
- package/dist/license/entitlements/manager.js +256 -0
- package/dist/license/index.js +4 -0
- package/dist/license/manager.js +505 -0
- package/dist/license/utils/compute-license-status.js +27 -0
- package/dist/license/utils/get-core-grace-expires-at.js +38 -0
- package/dist/license/utils/get-license-key.js +23 -0
- package/dist/license/utils/get-license-token.js +23 -0
- package/dist/license/utils/handle-license-error.js +41 -0
- package/dist/license/utils/is-in-core-grace-period.js +11 -0
- package/dist/license/utils/is-sso-bypass-allowed.js +21 -0
- package/dist/license/utils/use-rpc.js +33 -0
- package/dist/middleware/cache.js +4 -1
- package/dist/middleware/error-handler.js +11 -0
- package/dist/middleware/extract-token.js +11 -2
- package/dist/middleware/is-admin.js +16 -0
- package/dist/middleware/is-locked.js +16 -0
- package/dist/middleware/mcp-oauth-guard.js +23 -0
- package/dist/middleware/request-counter.js +5 -2
- package/dist/packages/types/dist/index.js +117 -122
- package/dist/permissions/modules/process-ast/utils/extract-paths-from-query.js +10 -1
- package/dist/permissions/utils/get-unaliased-field-key.js +2 -1
- package/dist/request/is-denied-ip.js +2 -0
- package/dist/schedules/license.js +31 -0
- package/dist/schedules/oauth-cleanup.js +26 -0
- package/dist/schedules/retention.js +1 -1
- package/dist/schedules/telemetry.js +4 -1
- package/dist/schedules/tus.js +1 -1
- package/dist/schedules/utils/duration-to-cron.js +36 -0
- package/dist/services/activity.js +15 -0
- package/dist/services/authentication.js +12 -5
- package/dist/services/collections.js +40 -10
- package/dist/services/fields.js +6 -6
- package/dist/services/flows.js +12 -0
- package/dist/services/graphql/resolvers/system-admin.js +2 -2
- package/dist/services/graphql/resolvers/system-global.js +1 -1
- package/dist/services/graphql/resolvers/system.js +34 -18
- package/dist/services/graphql/schema/get-types.js +23 -2
- package/dist/services/graphql/schema/parse-query.js +8 -0
- package/dist/services/graphql/schema/read.js +12 -0
- package/dist/services/graphql/types/json-filter.js +30 -0
- package/dist/services/index.js +6 -6
- package/dist/services/items.js +32 -14
- package/dist/services/mcp-oauth/cimd.js +307 -0
- package/dist/services/mcp-oauth/index.js +1185 -0
- package/dist/services/mcp-oauth/types/error.js +22 -0
- package/dist/services/mcp-oauth/utils/cimd-egress.js +182 -0
- package/dist/services/mcp-oauth/utils/domain.js +21 -0
- package/dist/services/mcp-oauth/utils/loopback.js +11 -0
- package/dist/services/mcp-oauth/utils/redirect.js +84 -0
- package/dist/services/mcp-oauth/utils/registration-debug.js +131 -0
- package/dist/services/payload.js +2 -1
- package/dist/services/permissions.js +31 -9
- package/dist/services/revisions.js +15 -0
- package/dist/services/server.js +21 -4
- package/dist/services/settings.js +37 -3
- package/dist/services/users.js +13 -6
- package/dist/services/utils.js +6 -1
- package/dist/services/versions.js +137 -69
- package/dist/utils/calculate-field-depth.js +1 -0
- package/dist/utils/deep-freeze.js +24 -0
- package/dist/utils/extract-function-name.js +13 -0
- package/dist/utils/generate-translations.js +5 -5
- package/dist/utils/get-accountability-for-token.js +13 -1
- package/dist/utils/get-cache-key.js +1 -1
- package/dist/utils/get-history-filter-query.js +22 -0
- package/dist/utils/get-schema.js +2 -2
- package/dist/utils/get-service.js +3 -3
- package/dist/utils/is-admin.js +9 -0
- package/dist/utils/parse-oauth-scope.js +12 -0
- package/dist/utils/sanitize-query.js +1 -1
- package/dist/utils/split-field-path.js +29 -0
- package/dist/utils/transaction.js +2 -2
- package/dist/utils/translations-validation.js +2 -2
- package/dist/utils/validate-query.js +35 -4
- package/dist/utils/validate-user-count-integrity.js +28 -5
- package/dist/utils/verify-session-jwt.js +5 -2
- package/dist/utils/versioning/handle-version.js +130 -48
- package/dist/utils/versioning/remove-circular.js +17 -0
- package/dist/websocket/authenticate.js +2 -1
- package/dist/websocket/collab/collab.js +1 -1
- package/dist/websocket/collab/room.js +1 -1
- package/dist/websocket/controllers/base.js +12 -0
- package/dist/websocket/controllers/graphql.js +1 -1
- package/dist/websocket/handlers/subscribe.js +1 -1
- package/dist/websocket/messages.js +64 -64
- package/dist/websocket/utils/items.js +2 -2
- package/license +90 -80
- package/package.json +33 -32
- package/dist/controllers/mcp.js +0 -31
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { isInCoreGracePeriod } from "./is-in-core-grace-period.js";
|
|
2
|
+
import { getLicenseManager } from "../manager.js";
|
|
3
|
+
|
|
4
|
+
//#region src/license/utils/is-sso-bypass-allowed.ts
|
|
5
|
+
/**
|
|
6
|
+
* Keep SSO reachable for unentitled installs when the license is locked or in the v12 core grace
|
|
7
|
+
* period, so admins can still sign in and resolve licensing.
|
|
8
|
+
*
|
|
9
|
+
* 'grace' only bypasses when also in the core grace period, so licensed expiry-grace doesn't qualify.
|
|
10
|
+
*/
|
|
11
|
+
async function isSSOBypassAllowed() {
|
|
12
|
+
const licenseManager = getLicenseManager();
|
|
13
|
+
const status = await licenseManager.getStatus();
|
|
14
|
+
const source = licenseManager.getSource();
|
|
15
|
+
if (status === "locked") return true;
|
|
16
|
+
if (status === "grace" && source === null) return isInCoreGracePeriod();
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
//#endregion
|
|
21
|
+
export { isSSOBypassAllowed };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { useBus } from "../../bus/lib/use-bus.js";
|
|
2
|
+
import "../../bus/index.js";
|
|
3
|
+
import { randomUUID } from "crypto";
|
|
4
|
+
|
|
5
|
+
//#region src/license/utils/use-rpc.ts
|
|
6
|
+
/**
|
|
7
|
+
* RPC means remote procedure call, allowing to call functions across multiple instances in a code native manner.
|
|
8
|
+
*
|
|
9
|
+
* Does not call the function on its OWN instance
|
|
10
|
+
*/
|
|
11
|
+
function useRPC(self, channel) {
|
|
12
|
+
const uid = randomUUID();
|
|
13
|
+
const messenger = useBus();
|
|
14
|
+
messenger.subscribe(channel, async ({ uid: id, method, args }) => {
|
|
15
|
+
if (uid == id) return;
|
|
16
|
+
const fn = self[method];
|
|
17
|
+
if (typeof fn === "function") try {
|
|
18
|
+
await fn.apply(self, args);
|
|
19
|
+
} catch {}
|
|
20
|
+
});
|
|
21
|
+
return new Proxy({}, { get(_, method) {
|
|
22
|
+
return async (...args) => {
|
|
23
|
+
await messenger.publish(channel, {
|
|
24
|
+
uid,
|
|
25
|
+
method,
|
|
26
|
+
args
|
|
27
|
+
});
|
|
28
|
+
};
|
|
29
|
+
} });
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
//#endregion
|
|
33
|
+
export { useRPC };
|
package/dist/middleware/cache.js
CHANGED
|
@@ -2,9 +2,11 @@ import async_handler_default from "../utils/async-handler.js";
|
|
|
2
2
|
import { useLogger } from "../logger/index.js";
|
|
3
3
|
import { getCache, getCacheValue } from "../cache.js";
|
|
4
4
|
import { useBufferedCounter } from "../telemetry/counter/use-buffered-counter.js";
|
|
5
|
+
import { getEntitlementManager } from "../license/entitlements/manager.js";
|
|
5
6
|
import { shouldSkipCache } from "../utils/should-skip-cache.js";
|
|
6
7
|
import { getCacheControlHeader } from "../utils/get-cache-headers.js";
|
|
7
8
|
import { getCacheKey } from "../utils/get-cache-key.js";
|
|
9
|
+
import "../license/index.js";
|
|
8
10
|
import { useEnv } from "@directus/env";
|
|
9
11
|
import { toBoolean } from "@directus/utils";
|
|
10
12
|
|
|
@@ -13,6 +15,7 @@ const checkCacheMiddleware = async_handler_default(async (req, res, next) => {
|
|
|
13
15
|
const env = useEnv();
|
|
14
16
|
const { cache } = getCache();
|
|
15
17
|
const logger = useLogger();
|
|
18
|
+
const entitlementManager = getEntitlementManager();
|
|
16
19
|
if (req.method.toLowerCase() !== "get" && req.originalUrl?.startsWith("/graphql") === false) return next();
|
|
17
20
|
if (env["CACHE_ENABLED"] !== true) return next();
|
|
18
21
|
if (!cache) return next();
|
|
@@ -42,7 +45,7 @@ const checkCacheMiddleware = async_handler_default(async (req, res, next) => {
|
|
|
42
45
|
res.setHeader("Cache-Control", getCacheControlHeader(req, cacheTTL, true, true));
|
|
43
46
|
res.setHeader("Vary", "Origin, Cache-Control");
|
|
44
47
|
if (env["CACHE_STATUS_HEADER"]) res.setHeader(`${env["CACHE_STATUS_HEADER"]}`, "HIT");
|
|
45
|
-
if (toBoolean(env["TELEMETRY"])) try {
|
|
48
|
+
if (entitlementManager.isEntitled("telemetry_required") || toBoolean(env["TELEMETRY"])) try {
|
|
46
49
|
useBufferedCounter("api-requests").increment("cached");
|
|
47
50
|
} catch (err) {
|
|
48
51
|
logger.trace(err, "Failed to increment cached request counter");
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { useLogger } from "../logger/index.js";
|
|
2
2
|
import database_default from "../database/index.js";
|
|
3
3
|
import emitter_default from "../emitter.js";
|
|
4
|
+
import { buildMcpWWWAuthenticateHeader, getMcpUrls, isMcpPath } from "../ai/mcp/utils.js";
|
|
5
|
+
import { useEnv } from "@directus/env";
|
|
4
6
|
import { ErrorCode, InternalServerError, isDirectusError } from "@directus/errors";
|
|
5
7
|
import { isObject } from "@directus/utils";
|
|
6
8
|
import { getNodeEnv } from "@directus/utils/node";
|
|
@@ -8,11 +10,20 @@ import { getNodeEnv } from "@directus/utils/node";
|
|
|
8
10
|
//#region src/middleware/error-handler.ts
|
|
9
11
|
const FALLBACK_ERROR = new InternalServerError();
|
|
10
12
|
const errorHandler = asyncErrorHandler(async (err, req, res) => {
|
|
13
|
+
const env = useEnv();
|
|
11
14
|
const logger = useLogger();
|
|
12
15
|
let errors = [];
|
|
13
16
|
let status = null;
|
|
14
17
|
const receivedErrors = Array.isArray(err) ? err : [err];
|
|
15
18
|
for (const error of receivedErrors) {
|
|
19
|
+
if ((isDirectusError(error, ErrorCode.InvalidCredentials) || isDirectusError(error, ErrorCode.InvalidToken) || isDirectusError(error, ErrorCode.TokenExpired)) && isMcpPath(req.path) && env["MCP_OAUTH_ENABLED"] === true) {
|
|
20
|
+
const { metadataUrl } = getMcpUrls();
|
|
21
|
+
res.status(401).set("WWW-Authenticate", buildMcpWWWAuthenticateHeader(metadataUrl, "invalid_token")).set("Access-Control-Expose-Headers", "WWW-Authenticate").json({
|
|
22
|
+
error: "invalid_token",
|
|
23
|
+
error_description: error.message
|
|
24
|
+
});
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
16
27
|
if (getNodeEnv() === "development" && error instanceof Error && error.stack) (error.extensions ??= {})["stack"] = error.stack;
|
|
17
28
|
if (isDirectusError(error)) {
|
|
18
29
|
logger.debug(error);
|
|
@@ -14,18 +14,27 @@ import { InvalidPayloadError } from "@directus/errors";
|
|
|
14
14
|
const extractToken = (req, _res, next) => {
|
|
15
15
|
const env = useEnv();
|
|
16
16
|
let token = null;
|
|
17
|
-
|
|
17
|
+
let tokenSource = null;
|
|
18
|
+
if (req.query && req.query["access_token"]) {
|
|
19
|
+
token = req.query["access_token"];
|
|
20
|
+
tokenSource = "query";
|
|
21
|
+
}
|
|
18
22
|
if (req.headers && req.headers.authorization) {
|
|
19
23
|
const parts = req.headers.authorization.split(" ");
|
|
20
24
|
if (parts.length === 2 && parts[0].toLowerCase() === "bearer") {
|
|
21
25
|
if (token !== null) throw new InvalidPayloadError({ reason: "The request uses more than one method for including an access token" });
|
|
22
26
|
token = parts[1];
|
|
27
|
+
tokenSource = "header";
|
|
23
28
|
}
|
|
24
29
|
}
|
|
25
30
|
if (req.cookies && req.cookies[env["SESSION_COOKIE_NAME"]]) {
|
|
26
|
-
if (token === null)
|
|
31
|
+
if (token === null) {
|
|
32
|
+
token = req.cookies[env["SESSION_COOKIE_NAME"]];
|
|
33
|
+
tokenSource = "cookie";
|
|
34
|
+
}
|
|
27
35
|
}
|
|
28
36
|
req.token = token;
|
|
37
|
+
req.tokenSource = tokenSource;
|
|
29
38
|
next();
|
|
30
39
|
};
|
|
31
40
|
var extract_token_default = extractToken;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import async_handler_default from "../utils/async-handler.js";
|
|
2
|
+
import { isAdmin } from "../utils/is-admin.js";
|
|
3
|
+
import { ForbiddenError } from "@directus/errors";
|
|
4
|
+
|
|
5
|
+
//#region src/middleware/is-admin.ts
|
|
6
|
+
/**
|
|
7
|
+
* Require the request to have been made by an admin
|
|
8
|
+
*/
|
|
9
|
+
const handler = async (req, _res, next) => {
|
|
10
|
+
if (!isAdmin(req.accountability)) throw new ForbiddenError();
|
|
11
|
+
return next();
|
|
12
|
+
};
|
|
13
|
+
var is_admin_default = async_handler_default(handler);
|
|
14
|
+
|
|
15
|
+
//#endregion
|
|
16
|
+
export { is_admin_default as default, handler };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import async_handler_default from "../utils/async-handler.js";
|
|
2
|
+
import { getLicenseManager } from "../license/manager.js";
|
|
3
|
+
import { ResourceRestrictedError } from "@directus/errors";
|
|
4
|
+
|
|
5
|
+
//#region src/middleware/is-locked.ts
|
|
6
|
+
/**
|
|
7
|
+
* Throws an error if the license is in a locked state
|
|
8
|
+
*/
|
|
9
|
+
const handler = (resource) => async_handler_default(async (_req, _res, next) => {
|
|
10
|
+
if (await getLicenseManager().isLocked()) throw new ResourceRestrictedError({ category: resource });
|
|
11
|
+
return next();
|
|
12
|
+
});
|
|
13
|
+
var is_locked_default = handler;
|
|
14
|
+
|
|
15
|
+
//#endregion
|
|
16
|
+
export { is_locked_default as default, handler };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { isMcpPath } from "../ai/mcp/utils.js";
|
|
2
|
+
import { ForbiddenError } from "@directus/errors";
|
|
3
|
+
|
|
4
|
+
//#region src/middleware/mcp-oauth-guard.ts
|
|
5
|
+
/**
|
|
6
|
+
* OAuth session route guard. If `accountability.oauth` is set, restrict to MCP endpoints only.
|
|
7
|
+
* All other Directus API routes are forbidden. Regular sessions pass through untouched.
|
|
8
|
+
*/
|
|
9
|
+
function handler(req, _res, next) {
|
|
10
|
+
if (!req.accountability?.oauth) {
|
|
11
|
+
next();
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
if (!isMcpPath(req.path) || req.method !== "GET" && req.method !== "POST") {
|
|
15
|
+
next(new ForbiddenError());
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
next();
|
|
19
|
+
}
|
|
20
|
+
var mcp_oauth_guard_default = handler;
|
|
21
|
+
|
|
22
|
+
//#endregion
|
|
23
|
+
export { mcp_oauth_guard_default as default, handler };
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { useLogger } from "../logger/index.js";
|
|
2
2
|
import { useBufferedCounter } from "../telemetry/counter/use-buffered-counter.js";
|
|
3
3
|
import { TRACKED_METHODS } from "../telemetry/utils/format-api-request-counts.js";
|
|
4
|
+
import { getEntitlementManager } from "../license/entitlements/manager.js";
|
|
5
|
+
import "../license/index.js";
|
|
4
6
|
import { useEnv } from "@directus/env";
|
|
5
7
|
import { toBoolean } from "@directus/utils";
|
|
6
8
|
|
|
7
9
|
//#region src/middleware/request-counter.ts
|
|
8
10
|
const TRACKED_METHODS_UPPER = new Set(TRACKED_METHODS.map((m) => m.toUpperCase()));
|
|
9
|
-
|
|
10
|
-
|
|
11
|
+
const env = useEnv();
|
|
12
|
+
const requestCounterMiddleware = (req, _res, next) => {
|
|
13
|
+
if (!getEntitlementManager().isEntitled("telemetry_required") && toBoolean(env["TELEMETRY"]) === false) return next();
|
|
11
14
|
if (TRACKED_METHODS_UPPER.has(req.method)) try {
|
|
12
15
|
useBufferedCounter("api-requests").increment(req.method.toLowerCase());
|
|
13
16
|
} catch (err) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import z
|
|
1
|
+
import z, { z as z$1 } from "zod";
|
|
2
2
|
import { PERMISSION_ACTIONS } from "@directus/constants";
|
|
3
3
|
|
|
4
4
|
//#region ../packages/types/dist/index.js
|
|
@@ -53,69 +53,67 @@ const DatabaseClients = [
|
|
|
53
53
|
* Supported deployment provider types
|
|
54
54
|
*/
|
|
55
55
|
const DEPLOYMENT_PROVIDER_TYPES = ["vercel", "netlify"];
|
|
56
|
-
const SplitEntrypoint = z.object({
|
|
57
|
-
app: z.string(),
|
|
58
|
-
api: z.string()
|
|
56
|
+
const SplitEntrypoint = z$1.object({
|
|
57
|
+
app: z$1.string(),
|
|
58
|
+
api: z$1.string()
|
|
59
59
|
});
|
|
60
|
-
const ExtensionSandboxRequestedScopes = z.object({
|
|
61
|
-
request: z.optional(z.object({
|
|
62
|
-
urls: z.array(z.string()),
|
|
63
|
-
methods: z.array(z.union([
|
|
64
|
-
z.literal("GET"),
|
|
65
|
-
z.literal("POST"),
|
|
66
|
-
z.literal("PATCH"),
|
|
67
|
-
z.literal("PUT"),
|
|
68
|
-
z.literal("DELETE")
|
|
60
|
+
const ExtensionSandboxRequestedScopes = z$1.object({
|
|
61
|
+
request: z$1.optional(z$1.object({
|
|
62
|
+
urls: z$1.array(z$1.string()),
|
|
63
|
+
methods: z$1.array(z$1.union([
|
|
64
|
+
z$1.literal("GET"),
|
|
65
|
+
z$1.literal("POST"),
|
|
66
|
+
z$1.literal("PATCH"),
|
|
67
|
+
z$1.literal("PUT"),
|
|
68
|
+
z$1.literal("DELETE")
|
|
69
69
|
]))
|
|
70
70
|
})),
|
|
71
|
-
log: z.optional(z.object({})),
|
|
72
|
-
sleep: z.optional(z.object({}))
|
|
71
|
+
log: z$1.optional(z$1.object({})),
|
|
72
|
+
sleep: z$1.optional(z$1.object({}))
|
|
73
73
|
});
|
|
74
|
-
const ExtensionSandboxOptions = z.optional(z.object({
|
|
75
|
-
enabled: z.boolean(),
|
|
74
|
+
const ExtensionSandboxOptions = z$1.optional(z$1.object({
|
|
75
|
+
enabled: z$1.boolean(),
|
|
76
76
|
requestedScopes: ExtensionSandboxRequestedScopes
|
|
77
77
|
}));
|
|
78
|
-
const Color = z.string();
|
|
79
|
-
const FamilyName = z.string().meta({ $ref: "FamilyName" });
|
|
80
|
-
const FontWeight = z.string().meta({ $ref: "FontWeight" });
|
|
81
|
-
const Length = z.string();
|
|
82
|
-
const Percentage = z.string();
|
|
83
|
-
const BoxShadow = z.string();
|
|
84
|
-
const Number = z.string();
|
|
85
|
-
const Size = z.string();
|
|
86
|
-
const LineWidth = z.union([
|
|
87
|
-
z.string(),
|
|
88
|
-
z.literal("thin"),
|
|
89
|
-
z.literal("medium"),
|
|
90
|
-
z.literal("thick")
|
|
78
|
+
const Color = z$1.string();
|
|
79
|
+
const FamilyName = z$1.string().meta({ $ref: "FamilyName" });
|
|
80
|
+
const FontWeight = z$1.string().meta({ $ref: "FontWeight" });
|
|
81
|
+
const Length = z$1.string();
|
|
82
|
+
const Percentage = z$1.string();
|
|
83
|
+
const BoxShadow = z$1.string();
|
|
84
|
+
const Number = z$1.string();
|
|
85
|
+
const Size = z$1.string();
|
|
86
|
+
const LineWidth = z$1.union([
|
|
87
|
+
z$1.string(),
|
|
88
|
+
z$1.literal("thin"),
|
|
89
|
+
z$1.literal("medium"),
|
|
90
|
+
z$1.literal("thick")
|
|
91
91
|
]);
|
|
92
|
-
const FormRules = z.object({
|
|
93
|
-
columnGap: z.union([Length, Percentage]).optional(),
|
|
94
|
-
rowGap: z.union([Length, Percentage]).optional(),
|
|
95
|
-
field: z.object({
|
|
96
|
-
label: z.object({
|
|
92
|
+
const FormRules = z$1.object({
|
|
93
|
+
columnGap: z$1.union([Length, Percentage]).optional(),
|
|
94
|
+
rowGap: z$1.union([Length, Percentage]).optional(),
|
|
95
|
+
field: z$1.object({
|
|
96
|
+
label: z$1.object({
|
|
97
97
|
foreground: Color.optional(),
|
|
98
98
|
fontFamily: FamilyName.optional(),
|
|
99
99
|
fontWeight: FontWeight.optional()
|
|
100
100
|
}).optional(),
|
|
101
|
-
input: z.object({
|
|
101
|
+
input: z$1.object({
|
|
102
102
|
background: Color.optional(),
|
|
103
103
|
backgroundSubdued: Color.optional(),
|
|
104
104
|
foreground: Color.optional(),
|
|
105
105
|
foregroundSubdued: Color.optional(),
|
|
106
106
|
borderColor: Color.optional(),
|
|
107
107
|
borderColorHover: Color.optional(),
|
|
108
|
-
|
|
108
|
+
focusRingColor: Color.optional(),
|
|
109
109
|
boxShadow: BoxShadow.optional(),
|
|
110
|
-
boxShadowHover: BoxShadow.optional(),
|
|
111
|
-
boxShadowFocus: BoxShadow.optional(),
|
|
112
110
|
height: Size.optional(),
|
|
113
|
-
padding: z.union([Length, Percentage]).optional()
|
|
111
|
+
padding: z$1.union([Length, Percentage]).optional()
|
|
114
112
|
}).optional()
|
|
115
113
|
}).optional()
|
|
116
114
|
}).optional();
|
|
117
|
-
const Rules = z.object({
|
|
118
|
-
borderRadius: z.union([Length, Percentage]).optional(),
|
|
115
|
+
const Rules = z$1.object({
|
|
116
|
+
borderRadius: z$1.union([Length, Percentage]).optional(),
|
|
119
117
|
borderWidth: LineWidth.optional(),
|
|
120
118
|
foreground: Color.optional(),
|
|
121
119
|
foregroundSubdued: Color.optional(),
|
|
@@ -147,41 +145,44 @@ const Rules = z.object({
|
|
|
147
145
|
dangerBackground: Color.optional(),
|
|
148
146
|
dangerSubdued: Color.optional(),
|
|
149
147
|
dangerAccent: Color.optional(),
|
|
150
|
-
fonts: z.object({
|
|
151
|
-
display: z.object({
|
|
148
|
+
fonts: z$1.object({
|
|
149
|
+
display: z$1.object({
|
|
152
150
|
fontFamily: FamilyName.optional(),
|
|
153
151
|
fontWeight: FontWeight.optional()
|
|
154
152
|
}).optional(),
|
|
155
|
-
|
|
153
|
+
title: z$1.object({
|
|
156
154
|
fontFamily: FamilyName.optional(),
|
|
157
155
|
fontWeight: FontWeight.optional()
|
|
158
156
|
}).optional(),
|
|
159
|
-
|
|
157
|
+
sans: z$1.object({
|
|
160
158
|
fontFamily: FamilyName.optional(),
|
|
161
159
|
fontWeight: FontWeight.optional()
|
|
162
160
|
}).optional(),
|
|
163
|
-
|
|
161
|
+
serif: z$1.object({
|
|
162
|
+
fontFamily: FamilyName.optional(),
|
|
163
|
+
fontWeight: FontWeight.optional()
|
|
164
|
+
}).optional(),
|
|
165
|
+
monospace: z$1.object({
|
|
164
166
|
fontFamily: FamilyName.optional(),
|
|
165
167
|
fontWeight: FontWeight.optional()
|
|
166
168
|
}).optional()
|
|
167
169
|
}).optional(),
|
|
168
|
-
|
|
170
|
+
shell: z$1.object({
|
|
169
171
|
background: Color.optional(),
|
|
170
172
|
backgroundAccent: Color.optional(),
|
|
171
173
|
borderWidth: LineWidth.optional(),
|
|
172
|
-
borderColor: Color.optional()
|
|
173
|
-
|
|
174
|
-
|
|
174
|
+
borderColor: Color.optional()
|
|
175
|
+
}).optional(),
|
|
176
|
+
navigation: z$1.object({
|
|
177
|
+
project: z$1.object({
|
|
175
178
|
foreground: Color.optional(),
|
|
176
|
-
fontFamily: FamilyName.optional()
|
|
177
|
-
borderWidth: LineWidth.optional(),
|
|
178
|
-
borderColor: Color.optional()
|
|
179
|
+
fontFamily: FamilyName.optional()
|
|
179
180
|
}).optional(),
|
|
180
|
-
modules: z.object({
|
|
181
|
+
modules: z$1.object({
|
|
181
182
|
background: Color.optional(),
|
|
182
183
|
borderWidth: LineWidth.optional(),
|
|
183
184
|
borderColor: Color.optional(),
|
|
184
|
-
button: z.object({
|
|
185
|
+
button: z$1.object({
|
|
185
186
|
foreground: Color.optional(),
|
|
186
187
|
foregroundHover: Color.optional(),
|
|
187
188
|
foregroundActive: Color.optional(),
|
|
@@ -190,8 +191,8 @@ const Rules = z.object({
|
|
|
190
191
|
backgroundActive: Color.optional()
|
|
191
192
|
}).optional()
|
|
192
193
|
}).optional(),
|
|
193
|
-
list: z.object({
|
|
194
|
-
icon: z.object({
|
|
194
|
+
list: z$1.object({
|
|
195
|
+
icon: z$1.object({
|
|
195
196
|
foreground: Color.optional(),
|
|
196
197
|
foregroundHover: Color.optional(),
|
|
197
198
|
foregroundActive: Color.optional()
|
|
@@ -203,37 +204,33 @@ const Rules = z.object({
|
|
|
203
204
|
backgroundHover: Color.optional(),
|
|
204
205
|
backgroundActive: Color.optional(),
|
|
205
206
|
fontFamily: FamilyName.optional(),
|
|
206
|
-
divider: z.object({
|
|
207
|
+
divider: z$1.object({
|
|
207
208
|
borderColor: Color.optional(),
|
|
208
209
|
borderWidth: LineWidth.optional()
|
|
209
210
|
})
|
|
210
211
|
}).optional()
|
|
211
212
|
}).optional(),
|
|
212
|
-
header: z.object({
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
headline: z.object({
|
|
218
|
-
foreground: Color.optional(),
|
|
219
|
-
fontFamily: FamilyName.optional()
|
|
220
|
-
}).optional(),
|
|
221
|
-
title: z.object({
|
|
222
|
-
foreground: Color.optional(),
|
|
223
|
-
fontFamily: FamilyName.optional(),
|
|
224
|
-
fontWeight: FontWeight.optional()
|
|
225
|
-
}).optional()
|
|
226
|
-
}).optional(),
|
|
213
|
+
header: z$1.object({ title: z$1.object({
|
|
214
|
+
foreground: Color.optional(),
|
|
215
|
+
fontFamily: FamilyName.optional(),
|
|
216
|
+
fontWeight: FontWeight.optional()
|
|
217
|
+
}).optional() }).optional(),
|
|
227
218
|
form: FormRules,
|
|
228
|
-
sidebar: z.object({
|
|
219
|
+
sidebar: z$1.object({
|
|
229
220
|
background: Color.optional(),
|
|
230
221
|
foreground: Color.optional(),
|
|
231
222
|
fontFamily: FamilyName.optional(),
|
|
232
223
|
borderWidth: LineWidth.optional(),
|
|
233
224
|
borderColor: Color.optional(),
|
|
234
|
-
section: z.object({
|
|
235
|
-
|
|
236
|
-
|
|
225
|
+
section: z$1.object({
|
|
226
|
+
borderWidth: LineWidth.optional(),
|
|
227
|
+
borderColor: Color.optional(),
|
|
228
|
+
active: z$1.object({
|
|
229
|
+
borderWidth: LineWidth.optional(),
|
|
230
|
+
borderColor: Color.optional()
|
|
231
|
+
}).optional(),
|
|
232
|
+
toggle: z$1.object({
|
|
233
|
+
icon: z$1.object({
|
|
237
234
|
foreground: Color.optional(),
|
|
238
235
|
foregroundHover: Color.optional(),
|
|
239
236
|
foregroundActive: Color.optional()
|
|
@@ -244,18 +241,16 @@ const Rules = z.object({
|
|
|
244
241
|
background: Color.optional(),
|
|
245
242
|
backgroundHover: Color.optional(),
|
|
246
243
|
backgroundActive: Color.optional(),
|
|
247
|
-
fontFamily: FamilyName.optional()
|
|
248
|
-
borderWidth: LineWidth.optional(),
|
|
249
|
-
borderColor: Color.optional()
|
|
244
|
+
fontFamily: FamilyName.optional()
|
|
250
245
|
}).optional(),
|
|
251
246
|
form: FormRules
|
|
252
247
|
}).optional()
|
|
253
248
|
}).optional(),
|
|
254
|
-
public: z.object({
|
|
249
|
+
public: z$1.object({
|
|
255
250
|
background: Color.optional(),
|
|
256
251
|
foreground: Color.optional(),
|
|
257
252
|
foregroundAccent: Color.optional(),
|
|
258
|
-
art: z.object({
|
|
253
|
+
art: z$1.object({
|
|
259
254
|
background: Color.optional(),
|
|
260
255
|
primary: Color.optional(),
|
|
261
256
|
secondary: Color.optional(),
|
|
@@ -263,42 +258,42 @@ const Rules = z.object({
|
|
|
263
258
|
}).optional(),
|
|
264
259
|
form: FormRules
|
|
265
260
|
}).optional(),
|
|
266
|
-
popover: z.object({ menu: z.object({
|
|
261
|
+
popover: z$1.object({ menu: z$1.object({
|
|
267
262
|
background: Color.optional(),
|
|
268
|
-
borderRadius: z.union([Length, Percentage]).optional(),
|
|
263
|
+
borderRadius: z$1.union([Length, Percentage]).optional(),
|
|
269
264
|
boxShadow: BoxShadow.optional()
|
|
270
265
|
}).optional() }).optional(),
|
|
271
|
-
banner: z.object({
|
|
266
|
+
banner: z$1.object({
|
|
272
267
|
background: Color.optional(),
|
|
273
|
-
padding: z.union([Length, Percentage]).optional(),
|
|
274
|
-
borderRadius: z.union([Length, Percentage]).optional(),
|
|
275
|
-
avatar: z.object({
|
|
268
|
+
padding: z$1.union([Length, Percentage]).optional(),
|
|
269
|
+
borderRadius: z$1.union([Length, Percentage]).optional(),
|
|
270
|
+
avatar: z$1.object({
|
|
276
271
|
background: Color.optional(),
|
|
277
272
|
foreground: Color.optional(),
|
|
278
|
-
borderRadius: z.union([Length, Percentage]).optional()
|
|
273
|
+
borderRadius: z$1.union([Length, Percentage]).optional()
|
|
279
274
|
}).optional(),
|
|
280
|
-
headline: z.object({
|
|
275
|
+
headline: z$1.object({
|
|
281
276
|
foreground: Color.optional(),
|
|
282
277
|
fontFamily: FamilyName.optional(),
|
|
283
278
|
fontWeight: FontWeight.optional()
|
|
284
279
|
}).optional(),
|
|
285
|
-
title: z.object({
|
|
280
|
+
title: z$1.object({
|
|
286
281
|
foreground: Color.optional(),
|
|
287
282
|
fontFamily: FamilyName.optional(),
|
|
288
283
|
fontWeight: FontWeight.optional()
|
|
289
284
|
}).optional(),
|
|
290
|
-
subtitle: z.object({
|
|
285
|
+
subtitle: z$1.object({
|
|
291
286
|
foreground: Color.optional(),
|
|
292
287
|
fontFamily: FamilyName.optional(),
|
|
293
288
|
fontWeight: FontWeight.optional()
|
|
294
289
|
}).optional(),
|
|
295
|
-
art: z.object({ foreground: Color.optional() }).optional()
|
|
290
|
+
art: z$1.object({ foreground: Color.optional() }).optional()
|
|
296
291
|
}).optional()
|
|
297
292
|
});
|
|
298
|
-
const ThemeSchema = z.object({
|
|
299
|
-
id: z.string(),
|
|
300
|
-
name: z.string(),
|
|
301
|
-
appearance: z.union([z.literal("light"), z.literal("dark")]),
|
|
293
|
+
const ThemeSchema = z$1.object({
|
|
294
|
+
id: z$1.string(),
|
|
295
|
+
name: z$1.string(),
|
|
296
|
+
appearance: z$1.union([z$1.literal("light"), z$1.literal("dark")]),
|
|
302
297
|
rules: Rules
|
|
303
298
|
});
|
|
304
299
|
/**
|
|
@@ -319,9 +314,9 @@ let UserIntegrityCheckFlag = /* @__PURE__ */ function(UserIntegrityCheckFlag$1)
|
|
|
319
314
|
UserIntegrityCheckFlag$1[UserIntegrityCheckFlag$1["All"] = 3] = "All";
|
|
320
315
|
return UserIntegrityCheckFlag$1;
|
|
321
316
|
}({});
|
|
322
|
-
const zodStringOrNumber = z.union([z.string(), z.number()]);
|
|
323
|
-
const WebSocketMessage = z.object({
|
|
324
|
-
type: z.string(),
|
|
317
|
+
const zodStringOrNumber = z$1.union([z$1.string(), z$1.number()]);
|
|
318
|
+
const WebSocketMessage = z$1.object({
|
|
319
|
+
type: z$1.string(),
|
|
325
320
|
uid: zodStringOrNumber.optional()
|
|
326
321
|
}).passthrough();
|
|
327
322
|
const TYPE = { COLLAB: "collab" };
|
|
@@ -356,35 +351,35 @@ const ACTION = {
|
|
|
356
351
|
ERROR: "error"
|
|
357
352
|
}
|
|
358
353
|
};
|
|
359
|
-
const BaseClientMessage = z
|
|
360
|
-
type: z
|
|
361
|
-
room: z
|
|
354
|
+
const BaseClientMessage = z.object({
|
|
355
|
+
type: z.literal(TYPE.COLLAB),
|
|
356
|
+
room: z.string()
|
|
362
357
|
});
|
|
363
|
-
const ClientMessage = z
|
|
364
|
-
z
|
|
365
|
-
type: z
|
|
366
|
-
action: z
|
|
367
|
-
collection: z
|
|
368
|
-
item: z
|
|
369
|
-
version: z
|
|
370
|
-
color: z
|
|
371
|
-
initialChanges: z
|
|
358
|
+
const ClientMessage = z.discriminatedUnion("action", [
|
|
359
|
+
z.object({
|
|
360
|
+
type: z.literal(TYPE.COLLAB),
|
|
361
|
+
action: z.literal(ACTION.CLIENT.JOIN),
|
|
362
|
+
collection: z.string(),
|
|
363
|
+
item: z.union([z.string(), z.number()]).nullable(),
|
|
364
|
+
version: z.string().nullable(),
|
|
365
|
+
color: z.enum(COLORS).nullable().optional(),
|
|
366
|
+
initialChanges: z.record(z.string(), z.any()).optional()
|
|
372
367
|
}),
|
|
373
|
-
BaseClientMessage.extend({ action: z
|
|
368
|
+
BaseClientMessage.extend({ action: z.literal(ACTION.CLIENT.LEAVE) }),
|
|
374
369
|
BaseClientMessage.extend({
|
|
375
|
-
action: z
|
|
376
|
-
field: z
|
|
377
|
-
changes: z
|
|
370
|
+
action: z.literal(ACTION.CLIENT.UPDATE),
|
|
371
|
+
field: z.string(),
|
|
372
|
+
changes: z.unknown().optional()
|
|
378
373
|
}),
|
|
379
374
|
BaseClientMessage.extend({
|
|
380
|
-
action: z
|
|
381
|
-
changes: z
|
|
375
|
+
action: z.literal(ACTION.CLIENT.UPDATE_ALL),
|
|
376
|
+
changes: z.record(z.string(), z.any()).optional()
|
|
382
377
|
}),
|
|
383
378
|
BaseClientMessage.extend({
|
|
384
|
-
action: z
|
|
385
|
-
field: z
|
|
379
|
+
action: z.literal(ACTION.CLIENT.FOCUS),
|
|
380
|
+
field: z.string().nullable()
|
|
386
381
|
}),
|
|
387
|
-
BaseClientMessage.extend({ action: z
|
|
382
|
+
BaseClientMessage.extend({ action: z.literal(ACTION.CLIENT.DISCARD) })
|
|
388
383
|
]);
|
|
389
384
|
|
|
390
385
|
//#endregion
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { parseJsonFunction } from "../../../../database/helpers/fn/json/parse-function.js";
|
|
2
|
+
import { extractFunctionName } from "../../../../utils/extract-function-name.js";
|
|
1
3
|
import { flattenFilter } from "./flatten-filter.js";
|
|
2
4
|
import { isEqual, uniqWith } from "lodash-es";
|
|
3
5
|
|
|
@@ -21,7 +23,14 @@ function extractPathsFromQuery(query) {
|
|
|
21
23
|
const readOnlyPaths = [];
|
|
22
24
|
if (query.filter) flattenFilter(readOnlyPaths, query.filter);
|
|
23
25
|
if (query.sort) for (const field of query.sort) {
|
|
24
|
-
const
|
|
26
|
+
const stripped = field.startsWith("-") ? field.substring(1) : field;
|
|
27
|
+
const resolved = query.alias?.[stripped] ?? stripped;
|
|
28
|
+
if (extractFunctionName(resolved) === "json") {
|
|
29
|
+
const { field: jsonField } = parseJsonFunction(resolved);
|
|
30
|
+
readOnlyPaths.push([jsonField]);
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
const parts = resolved.split(".");
|
|
25
34
|
if (query.aggregate && parts.length > 0 && parts[0] in query.aggregate) continue;
|
|
26
35
|
readOnlyPaths.push(parts);
|
|
27
36
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { parseJsonFunction } from "../../database/helpers/fn/json/parse-function.js";
|
|
2
|
+
import { extractFunctionName } from "../../utils/extract-function-name.js";
|
|
2
3
|
import { parseFilterKey } from "../../utils/parse-filter-key.js";
|
|
3
4
|
|
|
4
5
|
//#region src/permissions/utils/get-unaliased-field-key.ts
|
|
@@ -11,7 +12,7 @@ function getUnaliasedFieldKey(node) {
|
|
|
11
12
|
case "a2o":
|
|
12
13
|
case "m2o": return node.relation.field;
|
|
13
14
|
case "field": return parseFilterKey(node.name).fieldName;
|
|
14
|
-
case "functionField": if (node.name
|
|
15
|
+
case "functionField": if (extractFunctionName(node.name) === "json") return parseJsonFunction(node.name).field;
|
|
15
16
|
else return parseFilterKey(node.name).fieldName;
|
|
16
17
|
}
|
|
17
18
|
}
|
|
@@ -15,6 +15,8 @@ function isDeniedIp(ip) {
|
|
|
15
15
|
const blockNetwork = blockNetworkRaw.trim();
|
|
16
16
|
if (blockNetwork === "0.0.0.0") {
|
|
17
17
|
blockNetworkInterfaces = true;
|
|
18
|
+
blockList.parseSubnet("0.0.0.0/8");
|
|
19
|
+
blockList.parseAddress("::");
|
|
18
20
|
continue;
|
|
19
21
|
}
|
|
20
22
|
if (blockNetwork.includes("-")) {
|