@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
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { getOperation } from "../get-operation.js";
|
|
2
|
+
import { parseJsonPath } from "../../../../helpers/fn/json/parse-function.js";
|
|
1
3
|
import { getColumn } from "../../../utils/get-column.js";
|
|
2
|
-
import { getHelpers } from "../../../../helpers/index.js";
|
|
4
|
+
import { getFunctions, getHelpers } from "../../../../helpers/index.js";
|
|
3
5
|
import { InvalidQueryError } from "@directus/errors";
|
|
4
6
|
import { getOutputTypeForFunction } from "@directus/utils";
|
|
5
7
|
|
|
@@ -14,9 +16,51 @@ function castToNumber(value) {
|
|
|
14
16
|
if (Number.isNaN(num)) throw new InvalidQueryError({ reason: `Invalid numeric value` });
|
|
15
17
|
return num;
|
|
16
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* Splits "table.column" into ["table", "column"], respecting dots inside parentheses.
|
|
21
|
+
* Handles keys like "table.json(col,path.with.dots)" correctly.
|
|
22
|
+
*/
|
|
23
|
+
function splitTableColumn(key) {
|
|
24
|
+
let depth = 0;
|
|
25
|
+
for (let i = 0; i < key.length; i++) if (key[i] === "(") depth++;
|
|
26
|
+
else if (key[i] === ")") depth--;
|
|
27
|
+
else if (key[i] === "." && depth === 0) return [key.substring(0, i), key.substring(i + 1)];
|
|
28
|
+
return ["", key];
|
|
29
|
+
}
|
|
17
30
|
function applyOperator(knex, dbQuery, schema, key, operator, compareValue, logical = "and", originalCollectionName) {
|
|
18
31
|
const helpers = getHelpers(knex);
|
|
19
|
-
const [table, column] = key
|
|
32
|
+
const [table, column] = splitTableColumn(key);
|
|
33
|
+
if (operator === "_json") {
|
|
34
|
+
if (!Object.entries(compareValue).length) return;
|
|
35
|
+
const applyJsonConditions = (group, filterObj, innerLogical) => {
|
|
36
|
+
for (const [jsonPath, innerFilter] of Object.entries(filterObj)) {
|
|
37
|
+
if (jsonPath === "_or" || jsonPath === "_and") {
|
|
38
|
+
const subLogical = jsonPath === "_or" ? "or" : "and";
|
|
39
|
+
group[innerLogical].where((subGroup) => {
|
|
40
|
+
for (const subFilter of innerFilter) applyJsonConditions(subGroup, subFilter, subLogical);
|
|
41
|
+
});
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
const normalizedPath = parseJsonPath(jsonPath);
|
|
45
|
+
const innerValue = innerFilter[Object.keys(innerFilter)[0]];
|
|
46
|
+
const castNumeric = typeof innerValue === "number" || Array.isArray(innerValue) && innerValue.length > 0 && typeof innerValue[0] === "number";
|
|
47
|
+
const jsonExtractionRaw = getFunctions(knex, schema).json(table, column, {
|
|
48
|
+
type: "json",
|
|
49
|
+
jsonPath: normalizedPath,
|
|
50
|
+
originalCollectionName,
|
|
51
|
+
relationalCountOptions: void 0,
|
|
52
|
+
jsonReturnType: castNumeric ? "numeric" : "text"
|
|
53
|
+
});
|
|
54
|
+
const innerOp = getOperation(Object.keys(innerFilter)[0], Object.values(innerFilter)[0]);
|
|
55
|
+
if (!innerOp) continue;
|
|
56
|
+
applyOperatorToRaw(group, helpers, jsonExtractionRaw, innerOp.operator, innerOp.value, innerLogical);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
dbQuery[logical].where((group) => {
|
|
60
|
+
applyJsonConditions(group, compareValue, "and");
|
|
61
|
+
});
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
20
64
|
const selectionRaw = getColumn(knex, table, column, false, schema, { originalCollectionName });
|
|
21
65
|
if (operator === "_null" && compareValue !== false || operator === "_nnull" && compareValue === false || operator === "_eq" && compareValue === null) {
|
|
22
66
|
dbQuery[logical].whereNull(selectionRaw);
|
|
@@ -26,12 +70,18 @@ function applyOperator(knex, dbQuery, schema, key, operator, compareValue, logic
|
|
|
26
70
|
dbQuery[logical].whereNotNull(selectionRaw);
|
|
27
71
|
return;
|
|
28
72
|
}
|
|
29
|
-
if (operator === "_empty" && compareValue !== false || operator === "_nempty" && compareValue === false)
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
73
|
+
if (operator === "_empty" && compareValue !== false || operator === "_nempty" && compareValue === false) {
|
|
74
|
+
dbQuery[logical].andWhere((query) => {
|
|
75
|
+
query.whereNull(selectionRaw).orWhere(selectionRaw, "=", "");
|
|
76
|
+
});
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if (operator === "_nempty" && compareValue !== false || operator === "_empty" && compareValue === false) {
|
|
80
|
+
dbQuery[logical].andWhere((query) => {
|
|
81
|
+
query.whereNotNull(selectionRaw).andWhere(selectionRaw, "!=", "");
|
|
82
|
+
});
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
35
85
|
if (compareValue === void 0) return;
|
|
36
86
|
if (Array.isArray(compareValue)) compareValue = compareValue.filter((val) => val !== void 0);
|
|
37
87
|
if (column.includes("(") && column.includes(")")) {
|
|
@@ -43,7 +93,7 @@ function applyOperator(knex, dbQuery, schema, key, operator, compareValue, logic
|
|
|
43
93
|
"decimal"
|
|
44
94
|
].includes(type)) compareValue = castToNumber(compareValue);
|
|
45
95
|
}
|
|
46
|
-
const [collection, field] = key
|
|
96
|
+
const [collection, field] = splitTableColumn(key);
|
|
47
97
|
const mappedCollection = originalCollectionName || collection;
|
|
48
98
|
if (mappedCollection in schema.collections && field in schema.collections[mappedCollection].fields) {
|
|
49
99
|
const type = schema.collections[mappedCollection].fields[field].type;
|
|
@@ -60,47 +110,80 @@ function applyOperator(knex, dbQuery, schema, key, operator, compareValue, logic
|
|
|
60
110
|
"decimal"
|
|
61
111
|
].includes(type)) compareValue = castToNumber(compareValue);
|
|
62
112
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
if (operator === "
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if (operator === "
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
if (operator === "
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
if (operator === "
|
|
81
|
-
|
|
82
|
-
|
|
113
|
+
applyOperatorToRaw(dbQuery, helpers, selectionRaw, operator, compareValue, logical, key);
|
|
114
|
+
}
|
|
115
|
+
function applyOperatorToRaw(dbQuery, helpers, raw, operator, compareValue, logical, key) {
|
|
116
|
+
if (operator === "_null" && compareValue !== false || operator === "_nnull" && compareValue === false || operator === "_eq" && compareValue === null) {
|
|
117
|
+
dbQuery[logical].whereNull(raw);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
if (operator === "_nnull" && compareValue !== false || operator === "_null" && compareValue === false || operator === "_neq" && compareValue === null) {
|
|
121
|
+
dbQuery[logical].whereNotNull(raw);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if (operator === "_empty" && compareValue !== false || operator === "_nempty" && compareValue === false) {
|
|
125
|
+
dbQuery[logical].andWhere((query) => {
|
|
126
|
+
query.whereNull(raw).orWhere(raw, "=", "");
|
|
127
|
+
});
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
if (operator === "_nempty" && compareValue !== false || operator === "_empty" && compareValue === false) {
|
|
131
|
+
dbQuery[logical].andWhere((query) => {
|
|
132
|
+
query.whereNotNull(raw).andWhere(raw, "!=", "");
|
|
133
|
+
});
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
if (compareValue === void 0) return;
|
|
137
|
+
if (Array.isArray(compareValue)) compareValue = compareValue.filter((val) => val !== void 0);
|
|
138
|
+
if (operator === "_eq") dbQuery[logical].where(raw, "=", compareValue);
|
|
139
|
+
if (operator === "_neq") dbQuery[logical].whereNot(raw, compareValue);
|
|
140
|
+
if (operator === "_ieq") dbQuery[logical].whereRaw(`LOWER(??) = ?`, [raw, `${compareValue.toLowerCase()}`]);
|
|
141
|
+
if (operator === "_nieq") dbQuery[logical].whereRaw(`LOWER(??) <> ?`, [raw, `${compareValue.toLowerCase()}`]);
|
|
142
|
+
if (operator === "_contains") dbQuery[logical].where(raw, "like", `%${compareValue}%`);
|
|
143
|
+
if (operator === "_ncontains") dbQuery[logical].whereNot(raw, "like", `%${compareValue}%`);
|
|
144
|
+
if (operator === "_icontains") dbQuery[logical].whereRaw(`LOWER(??) LIKE ?`, [raw, `%${compareValue.toLowerCase()}%`]);
|
|
145
|
+
if (operator === "_nicontains") dbQuery[logical].whereRaw(`LOWER(??) NOT LIKE ?`, [raw, `%${compareValue.toLowerCase()}%`]);
|
|
146
|
+
if (operator === "_starts_with") dbQuery[logical].where(raw, "like", `${compareValue}%`);
|
|
147
|
+
if (operator === "_nstarts_with") dbQuery[logical].whereNot(raw, "like", `${compareValue}%`);
|
|
148
|
+
if (operator === "_istarts_with") dbQuery[logical].whereRaw(`LOWER(??) LIKE ?`, [raw, `${compareValue.toLowerCase()}%`]);
|
|
149
|
+
if (operator === "_nistarts_with") dbQuery[logical].whereRaw(`LOWER(??) NOT LIKE ?`, [raw, `${compareValue.toLowerCase()}%`]);
|
|
150
|
+
if (operator === "_ends_with") dbQuery[logical].where(raw, "like", `%${compareValue}`);
|
|
151
|
+
if (operator === "_nends_with") dbQuery[logical].whereNot(raw, "like", `%${compareValue}`);
|
|
152
|
+
if (operator === "_iends_with") dbQuery[logical].whereRaw(`LOWER(??) LIKE ?`, [raw, `%${compareValue.toLowerCase()}`]);
|
|
153
|
+
if (operator === "_niends_with") dbQuery[logical].whereRaw(`LOWER(??) NOT LIKE ?`, [raw, `%${compareValue.toLowerCase()}`]);
|
|
154
|
+
if (operator === "_gt") dbQuery[logical].where(raw, ">", compareValue);
|
|
155
|
+
if (operator === "_gte") dbQuery[logical].where(raw, ">=", compareValue);
|
|
156
|
+
if (operator === "_lt") dbQuery[logical].where(raw, "<", compareValue);
|
|
157
|
+
if (operator === "_lte") dbQuery[logical].where(raw, "<=", compareValue);
|
|
83
158
|
if (operator === "_in") {
|
|
84
159
|
let value = compareValue;
|
|
85
160
|
if (typeof value === "string") value = value.split(",");
|
|
86
|
-
dbQuery[logical].whereIn(
|
|
161
|
+
if (value.length === 0) dbQuery[logical].whereIn(raw, []);
|
|
162
|
+
else {
|
|
163
|
+
const placeholders = value.map(() => "?").join(", ");
|
|
164
|
+
dbQuery[logical].whereRaw(`?? in (${placeholders})`, [raw, ...value]);
|
|
165
|
+
}
|
|
87
166
|
}
|
|
88
167
|
if (operator === "_nin") {
|
|
89
168
|
let value = compareValue;
|
|
90
169
|
if (typeof value === "string") value = value.split(",");
|
|
91
|
-
dbQuery[logical].whereNotIn(
|
|
170
|
+
if (value.length === 0) dbQuery[logical].whereNotIn(raw, []);
|
|
171
|
+
else {
|
|
172
|
+
const placeholders = value.map(() => "?").join(", ");
|
|
173
|
+
dbQuery[logical].whereRaw(`?? not in (${placeholders})`, [raw, ...value]);
|
|
174
|
+
}
|
|
92
175
|
}
|
|
93
176
|
if (operator === "_between") {
|
|
94
177
|
let value = compareValue;
|
|
95
178
|
if (typeof value === "string") value = value.split(",");
|
|
96
179
|
if (value.length !== 2) return;
|
|
97
|
-
dbQuery[logical].whereBetween(
|
|
180
|
+
dbQuery[logical].whereBetween(raw, value);
|
|
98
181
|
}
|
|
99
182
|
if (operator === "_nbetween") {
|
|
100
183
|
let value = compareValue;
|
|
101
184
|
if (typeof value === "string") value = value.split(",");
|
|
102
185
|
if (value.length !== 2) return;
|
|
103
|
-
dbQuery[logical].whereNotBetween(
|
|
186
|
+
dbQuery[logical].whereNotBetween(raw, value);
|
|
104
187
|
}
|
|
105
188
|
if (operator == "_intersects") dbQuery[logical].whereRaw(helpers.st.intersects(key, compareValue));
|
|
106
189
|
if (operator == "_nintersects") dbQuery[logical].whereRaw(helpers.st.nintersects(key, compareValue));
|
|
@@ -20,7 +20,10 @@ function applyQuery(knex, collection, dbQuery, query, schema, cases, permissions
|
|
|
20
20
|
if (query.offset) applyOffset(knex, dbQuery, query.offset);
|
|
21
21
|
if (query.page && query.limit && query.limit !== -1) applyOffset(knex, dbQuery, query.limit * (query.page - 1));
|
|
22
22
|
if (query.sort && !options?.isInnerQuery && !options?.hasMultiRelationalSort) {
|
|
23
|
-
const sortResult = applySort(knex, schema, dbQuery, query.sort,
|
|
23
|
+
const sortResult = applySort(knex, schema, dbQuery, query.sort, collection, aliasMap, {
|
|
24
|
+
aggregate: query.aggregate,
|
|
25
|
+
fieldAliasMap: { ...query.alias ?? {} }
|
|
26
|
+
});
|
|
24
27
|
if (!hasJoins) hasJoins = sortResult.hasJoins;
|
|
25
28
|
}
|
|
26
29
|
const filter = joinFilterWithCases(query.filter, cases);
|
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import { getColumnPath } from "../../../../utils/get-column-path.js";
|
|
2
2
|
import { addJoin } from "./add-join.js";
|
|
3
3
|
import { getColumn } from "../../utils/get-column.js";
|
|
4
|
+
import { extractFunctionName } from "../../../../utils/extract-function-name.js";
|
|
5
|
+
import { splitFieldPath } from "../../../../utils/split-field-path.js";
|
|
4
6
|
import { getRelationInfo } from "@directus/utils";
|
|
5
7
|
|
|
6
8
|
//#region src/database/run-ast/lib/apply-query/sort.ts
|
|
7
|
-
function applySort(knex, schema, rootQuery, sort,
|
|
9
|
+
function applySort(knex, schema, rootQuery, sort, collection, aliasMap, options) {
|
|
10
|
+
const { aggregate, returnRecords = false, fieldAliasMap } = options ?? {};
|
|
8
11
|
const relations = schema.relations;
|
|
9
12
|
let hasJoins = false;
|
|
10
13
|
let hasMultiRelationalSort = false;
|
|
11
14
|
const sortRecords = sort.map((sortField) => {
|
|
12
|
-
const column = sortField
|
|
15
|
+
const column = splitFieldPath(sortField);
|
|
13
16
|
let order = "asc";
|
|
14
17
|
if (sortField.startsWith("-")) order = "desc";
|
|
15
18
|
if (column[0].startsWith("-")) column[0] = column[0].substring(1);
|
|
@@ -30,11 +33,17 @@ function applySort(knex, schema, rootQuery, sort, aggregate, collection, aliasMa
|
|
|
30
33
|
};
|
|
31
34
|
}
|
|
32
35
|
if (column.length === 1) {
|
|
33
|
-
const
|
|
36
|
+
const rawField = column[0];
|
|
37
|
+
const resolvedField = fieldAliasMap?.[rawField] ?? rawField;
|
|
38
|
+
if (extractFunctionName(resolvedField) === "json") return {
|
|
39
|
+
order,
|
|
40
|
+
column: returnRecords ? resolvedField : getColumn(knex, collection, resolvedField, false, schema, { jsonReturnType: "text" })
|
|
41
|
+
};
|
|
42
|
+
const pathRoot = resolvedField.split(":")[0];
|
|
34
43
|
const { relation, relationType } = getRelationInfo(relations, collection, pathRoot);
|
|
35
44
|
if (!relation || ["m2o", "a2o"].includes(relationType ?? "")) return {
|
|
36
45
|
order,
|
|
37
|
-
column: returnRecords ?
|
|
46
|
+
column: returnRecords ? resolvedField : getColumn(knex, collection, resolvedField, false, schema)
|
|
38
47
|
};
|
|
39
48
|
}
|
|
40
49
|
const { hasMultiRelational, isJoinAdded } = addJoin({
|
|
@@ -45,19 +54,20 @@ function applySort(knex, schema, rootQuery, sort, aggregate, collection, aliasMa
|
|
|
45
54
|
schema,
|
|
46
55
|
knex
|
|
47
56
|
});
|
|
48
|
-
const { columnPath } = getColumnPath({
|
|
57
|
+
const { columnPath, targetCollection } = getColumnPath({
|
|
49
58
|
path: column,
|
|
50
59
|
collection,
|
|
51
60
|
aliasMap,
|
|
52
61
|
relations,
|
|
53
62
|
schema
|
|
54
63
|
});
|
|
55
|
-
const [alias,
|
|
64
|
+
const [alias, ...rest] = splitFieldPath(columnPath);
|
|
65
|
+
const field = rest.join(".");
|
|
56
66
|
if (!hasJoins) hasJoins = isJoinAdded;
|
|
57
67
|
if (!hasMultiRelationalSort) hasMultiRelationalSort = hasMultiRelational;
|
|
58
68
|
return {
|
|
59
69
|
order,
|
|
60
|
-
column: returnRecords ? columnPath : getColumn(knex, alias, field, false, schema)
|
|
70
|
+
column: returnRecords ? columnPath : getColumn(knex, alias, field, false, schema, { originalCollectionName: targetCollection })
|
|
61
71
|
};
|
|
62
72
|
});
|
|
63
73
|
if (returnRecords) return {
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { generateQueryAlias } from "../utils/generate-alias.js";
|
|
2
2
|
import { applyCaseWhen } from "../utils/apply-case-when.js";
|
|
3
|
+
import { applyFunctionToColumnName } from "../utils/apply-function-to-column-name.js";
|
|
3
4
|
import { getColumn } from "../utils/get-column.js";
|
|
4
5
|
import { applyLimit } from "./apply-query/pagination.js";
|
|
6
|
+
import { extractFunctionName } from "../../../utils/extract-function-name.js";
|
|
7
|
+
import { splitFieldPath } from "../../../utils/split-field-path.js";
|
|
5
8
|
import { applySort } from "./apply-query/sort.js";
|
|
6
9
|
import applyQuery from "./apply-query/index.js";
|
|
7
10
|
import { getHelpers } from "../../helpers/index.js";
|
|
@@ -43,7 +46,13 @@ function getDBQuery({ table, fieldNodes, o2mNodes, query, cases, permissions, pe
|
|
|
43
46
|
const innerQuerySortRecords = [];
|
|
44
47
|
let hasMultiRelationalSort;
|
|
45
48
|
if (queryCopy.sort) {
|
|
46
|
-
const
|
|
49
|
+
const fieldAliasMap = { ...queryCopy.alias ?? {} };
|
|
50
|
+
for (const node of fieldNodes) if (node.type === "functionField" && extractFunctionName(node.name) === "json") fieldAliasMap[applyFunctionToColumnName(node.fieldKey)] = node.name;
|
|
51
|
+
const sortResult = applySort(knex, schema, dbQuery, queryCopy.sort, table, aliasMap, {
|
|
52
|
+
aggregate: queryCopy.aggregate,
|
|
53
|
+
returnRecords: true,
|
|
54
|
+
fieldAliasMap
|
|
55
|
+
});
|
|
47
56
|
if (sortResult) {
|
|
48
57
|
sortRecords = sortResult.sortRecords;
|
|
49
58
|
hasMultiRelationalSort = sortResult.hasMultiRelationalSort;
|
|
@@ -85,16 +94,18 @@ function getDBQuery({ table, fieldNodes, o2mNodes, query, cases, permissions, pe
|
|
|
85
94
|
if (orderByString.length !== 0) orderByString += ", ";
|
|
86
95
|
const sortAlias = generateQueryAlias(table, queryCopy, `sort_${index}_${sortRecord.column}_${sortRecord.order}`);
|
|
87
96
|
let orderByColumn;
|
|
88
|
-
|
|
89
|
-
|
|
97
|
+
const colParts = splitFieldPath(sortRecord.column);
|
|
98
|
+
if (colParts.length > 1) {
|
|
99
|
+
const [alias, ...rest] = colParts;
|
|
100
|
+
const field = rest.join(".");
|
|
90
101
|
const originalCollectionName = getCollectionFromAlias(alias, aliasMap);
|
|
91
102
|
dbQuery.select(getColumn(knex, alias, field, sortAlias, schema, { originalCollectionName }));
|
|
92
103
|
orderByString += `?? ${sortRecord.order}`;
|
|
93
104
|
orderByColumn = getColumn(knex, alias, field, false, schema, { originalCollectionName });
|
|
94
105
|
} else {
|
|
95
|
-
dbQuery.select(getColumn(knex, table, sortRecord.column, sortAlias, schema));
|
|
106
|
+
dbQuery.select(getColumn(knex, table, sortRecord.column, sortAlias, schema, { jsonReturnType: "text" }));
|
|
96
107
|
orderByString += `?? ${sortRecord.order}`;
|
|
97
|
-
orderByColumn = getColumn(knex, table, sortRecord.column, false, schema);
|
|
108
|
+
orderByColumn = getColumn(knex, table, sortRecord.column, false, schema, { jsonReturnType: "text" });
|
|
98
109
|
}
|
|
99
110
|
orderByFields.push(orderByColumn);
|
|
100
111
|
innerQuerySortRecords.push({
|
|
@@ -111,10 +122,11 @@ function getDBQuery({ table, fieldNodes, o2mNodes, query, cases, permissions, pe
|
|
|
111
122
|
dbQuery.orderByRaw(orderByString, orderByFields);
|
|
112
123
|
} else {
|
|
113
124
|
sortRecords.map((sortRecord) => {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
125
|
+
const colParts = splitFieldPath(sortRecord.column);
|
|
126
|
+
if (colParts.length > 1) {
|
|
127
|
+
const [alias, ...rest] = colParts;
|
|
128
|
+
sortRecord.column = getColumn(knex, alias, rest.join("."), false, schema, { originalCollectionName: getCollectionFromAlias(alias, aliasMap) });
|
|
129
|
+
} else sortRecord.column = getColumn(knex, table, sortRecord.column, false, schema, { jsonReturnType: "text" });
|
|
118
130
|
});
|
|
119
131
|
dbQuery.orderBy(sortRecords);
|
|
120
132
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { parseJsonFunction } from "../../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/database/run-ast/lib/parse-current-level.ts
|
|
@@ -10,7 +11,7 @@ async function parseCurrentLevel(schema, collection, children, query) {
|
|
|
10
11
|
for (const child of children) {
|
|
11
12
|
if (child.type === "field" || child.type === "functionField") {
|
|
12
13
|
let fieldName;
|
|
13
|
-
if (child.type
|
|
14
|
+
if (child.type === "functionField" && extractFunctionName(child.name) === "json") fieldName = parseJsonFunction(child.name).field;
|
|
14
15
|
else fieldName = parseFilterKey(child.name).fieldName;
|
|
15
16
|
if (columnsInCollection.includes(fieldName)) columnsToSelectInternal.push(child.fieldKey);
|
|
16
17
|
continue;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { applyFunctionToColumnName } from "./utils/apply-function-to-column-name.js";
|
|
2
|
+
import { extractFunctionName } from "../../utils/extract-function-name.js";
|
|
2
3
|
import database_default from "../index.js";
|
|
3
4
|
import { fetchPolicies } from "../../permissions/lib/fetch-policies.js";
|
|
4
5
|
import { fetchPermissions } from "../../permissions/lib/fetch-permissions.js";
|
|
@@ -57,7 +58,7 @@ async function runAst(originalAST, schema, accountability, options) {
|
|
|
57
58
|
schema
|
|
58
59
|
});
|
|
59
60
|
const aliasMap = { ...query.alias };
|
|
60
|
-
for (const child of children) if (child.type === "functionField" && child.name
|
|
61
|
+
for (const child of children) if (child.type === "functionField" && extractFunctionName(child.name) === "json") {
|
|
61
62
|
const alias = applyFunctionToColumnName(child.fieldKey);
|
|
62
63
|
aliasMap[alias] = child.name;
|
|
63
64
|
}
|
|
@@ -42,7 +42,8 @@ function getColumn(knex, table, column, alias = applyFunctionToColumnName(column
|
|
|
42
42
|
permissions: options.permissions
|
|
43
43
|
} : void 0,
|
|
44
44
|
originalCollectionName: options?.originalCollectionName,
|
|
45
|
-
jsonPath
|
|
45
|
+
jsonPath,
|
|
46
|
+
...options?.jsonReturnType && { jsonReturnType: options.jsonReturnType }
|
|
46
47
|
});
|
|
47
48
|
if (alias) return knex.raw(result + " AS ??", [alias]);
|
|
48
49
|
return result;
|
|
@@ -7,10 +7,10 @@ import { useEnv } from "@directus/env";
|
|
|
7
7
|
import { ErrorCode, ServiceUnavailableError, isDirectusError } from "@directus/errors";
|
|
8
8
|
import { move, remove } from "fs-extra";
|
|
9
9
|
import { Readable } from "node:stream";
|
|
10
|
-
import PQueue from "p-queue";
|
|
11
10
|
import { download } from "@directus/extensions-registry";
|
|
12
11
|
import { EXTENSION_PKG_KEY, ExtensionManifest } from "@directus/extensions";
|
|
13
12
|
import DriverLocal from "@directus/storage-driver-local";
|
|
13
|
+
import PQueue from "p-queue";
|
|
14
14
|
import { extract } from "tar";
|
|
15
15
|
|
|
16
16
|
//#region src/extensions/lib/installation/manager.ts
|
|
@@ -13,8 +13,8 @@ import { useEnv } from "@directus/env";
|
|
|
13
13
|
import { normalizePath } from "@directus/utils";
|
|
14
14
|
import { dirname, join, relative, resolve, sep } from "node:path";
|
|
15
15
|
import { pipeline } from "node:stream/promises";
|
|
16
|
-
import PQueue from "p-queue";
|
|
17
16
|
import { createWriteStream } from "node:fs";
|
|
17
|
+
import PQueue from "p-queue";
|
|
18
18
|
import mid from "node-machine-id";
|
|
19
19
|
|
|
20
20
|
//#region src/extensions/lib/sync/sync.ts
|
|
@@ -4,11 +4,12 @@ import { useLogger } from "../logger/index.js";
|
|
|
4
4
|
import { getExtensionsPath } from "./lib/get-extensions-path.js";
|
|
5
5
|
import database_default from "../database/index.js";
|
|
6
6
|
import emitter_default, { Emitter } from "../emitter.js";
|
|
7
|
+
import { scheduleSynchronizedJob, validateCron } from "../utils/schedule.js";
|
|
7
8
|
import { getSchema } from "../utils/get-schema.js";
|
|
9
|
+
import { getFlowManager } from "../flows.js";
|
|
8
10
|
import { deleteFromRequireCache } from "../utils/delete-from-require-cache.js";
|
|
9
11
|
import getModuleDefault from "../utils/get-module-default.js";
|
|
10
12
|
import { importFileUrl } from "../utils/import-file-url.js";
|
|
11
|
-
import { scheduleSynchronizedJob, validateCron } from "../utils/schedule.js";
|
|
12
13
|
import { getExtensionsSettings } from "./lib/get-extensions-settings.js";
|
|
13
14
|
import { getExtensions } from "./lib/get-extensions.js";
|
|
14
15
|
import { getSharedDepsMapping } from "./lib/get-shared-deps-mapping.js";
|
|
@@ -18,7 +19,6 @@ import { instantiateSandboxSdk } from "./lib/sandbox/sdk/instantiate.js";
|
|
|
18
19
|
import { syncExtensions } from "./lib/sync/sync.js";
|
|
19
20
|
import { wrapEmbeds } from "./lib/wrap-embeds.js";
|
|
20
21
|
import { services_exports } from "../services/index.js";
|
|
21
|
-
import { getFlowManager } from "../flows.js";
|
|
22
22
|
import { readFile, readdir } from "node:fs/promises";
|
|
23
23
|
import path from "path";
|
|
24
24
|
import { useEnv } from "@directus/env";
|
|
@@ -29,7 +29,6 @@ import { pathToRelativeUrl, processId } from "@directus/utils/node";
|
|
|
29
29
|
import { fileURLToPath } from "node:url";
|
|
30
30
|
import { HYBRID_EXTENSION_TYPES } from "@directus/constants";
|
|
31
31
|
import { dirname, join as join$1, relative, resolve, sep } from "node:path";
|
|
32
|
-
import PQueue from "p-queue";
|
|
33
32
|
import os from "node:os";
|
|
34
33
|
import { APP_SHARED_DEPS } from "@directus/extensions";
|
|
35
34
|
import { generateExtensionsEntrypoint } from "@directus/extensions/node";
|
|
@@ -39,6 +38,7 @@ import nodeResolveDefault from "@rollup/plugin-node-resolve";
|
|
|
39
38
|
import virtualDefault from "@rollup/plugin-virtual";
|
|
40
39
|
import chokidar from "chokidar";
|
|
41
40
|
import ivm from "isolated-vm";
|
|
41
|
+
import PQueue from "p-queue";
|
|
42
42
|
import { rolldown } from "rolldown";
|
|
43
43
|
import { rollup } from "rollup";
|
|
44
44
|
|
package/dist/flows.js
CHANGED
|
@@ -5,23 +5,23 @@ import database_default from "./database/index.js";
|
|
|
5
5
|
import { fetchPolicies } from "./permissions/lib/fetch-policies.js";
|
|
6
6
|
import { fetchPermissions } from "./permissions/lib/fetch-permissions.js";
|
|
7
7
|
import emitter_default from "./emitter.js";
|
|
8
|
-
import {
|
|
8
|
+
import { scheduleSynchronizedJob, validateCron } from "./utils/schedule.js";
|
|
9
9
|
import { ActivityService } from "./services/activity.js";
|
|
10
10
|
import { getService } from "./utils/get-service.js";
|
|
11
|
-
import {
|
|
11
|
+
import { getSchema } from "./utils/get-schema.js";
|
|
12
|
+
import { FlowsService } from "./services/flows.js";
|
|
12
13
|
import { RevisionsService } from "./services/revisions.js";
|
|
13
|
-
import { services_exports } from "./services/index.js";
|
|
14
14
|
import { constructFlowTree } from "./utils/construct-flow-tree.js";
|
|
15
15
|
import { redactObject } from "./utils/redact-object.js";
|
|
16
|
-
import {
|
|
16
|
+
import { services_exports } from "./services/index.js";
|
|
17
17
|
import { useEnv } from "@directus/env";
|
|
18
18
|
import { ForbiddenError } from "@directus/errors";
|
|
19
19
|
import { applyOptionsData, deepMap, getRedactedString, isValidJSON, parseJSON, toArray } from "@directus/utils";
|
|
20
20
|
import { pick } from "lodash-es";
|
|
21
21
|
import { Action } from "@directus/constants";
|
|
22
22
|
import { isSystemCollection } from "@directus/system-data";
|
|
23
|
-
import { get as get$1 } from "micromustache";
|
|
24
23
|
import PQueue from "p-queue";
|
|
24
|
+
import { get as get$1 } from "micromustache";
|
|
25
25
|
|
|
26
26
|
//#region src/flows.ts
|
|
27
27
|
let flowManager;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import database_default from "../../../database/index.js";
|
|
2
|
+
import { getSchema } from "../../../utils/get-schema.js";
|
|
3
|
+
import "../../../services/index.js";
|
|
4
|
+
import { CollectionsService } from "../../../services/collections.js";
|
|
5
|
+
import { useEnv } from "@directus/env";
|
|
6
|
+
import { isSystemCollection } from "@directus/system-data";
|
|
7
|
+
|
|
8
|
+
//#region src/license/entitlements/lib/collections.ts
|
|
9
|
+
async function getActiveCollections(opts) {
|
|
10
|
+
const env = useEnv();
|
|
11
|
+
const knex = opts?.knex ?? database_default();
|
|
12
|
+
return (await new CollectionsService({
|
|
13
|
+
schema: await getSchema({ database: knex }),
|
|
14
|
+
knex
|
|
15
|
+
}).readByQuery()).filter((collection) => {
|
|
16
|
+
const isFolder = collection.schema === null;
|
|
17
|
+
const isDBOnly = collection.meta === null;
|
|
18
|
+
const isDisabled = collection.meta?.status !== "active";
|
|
19
|
+
const isEnvExcluded = env["DB_EXCLUDE_TABLES"].includes(collection.collection);
|
|
20
|
+
return !isFolder && !isSystemCollection(collection.collection) && !isDBOnly && !isDisabled && !isEnvExcluded;
|
|
21
|
+
}).map((collection) => collection.collection);
|
|
22
|
+
}
|
|
23
|
+
async function countActiveCollections(opts) {
|
|
24
|
+
return (await getActiveCollections(opts)).length;
|
|
25
|
+
}
|
|
26
|
+
async function resolveCollections(collections, ctx) {
|
|
27
|
+
const collectionsService = new CollectionsService({
|
|
28
|
+
schema: await getSchema(),
|
|
29
|
+
accountability: ctx?.accountability
|
|
30
|
+
});
|
|
31
|
+
await Promise.allSettled(collections.map((collection) => {
|
|
32
|
+
return collectionsService.updateOne(collection, { meta: { status: "inactive" } });
|
|
33
|
+
}));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
//#endregion
|
|
37
|
+
export { countActiveCollections, getActiveCollections, resolveCollections };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { CUSTOM_LLM_FIELDS } from "../../../constants.js";
|
|
2
|
+
import database_default from "../../../database/index.js";
|
|
3
|
+
import { getSchema } from "../../../utils/get-schema.js";
|
|
4
|
+
import { SettingsService } from "../../../services/settings.js";
|
|
5
|
+
import "../../../services/index.js";
|
|
6
|
+
|
|
7
|
+
//#region src/license/entitlements/lib/custom-llms-enabled.ts
|
|
8
|
+
async function checkCustomLLM(opts) {
|
|
9
|
+
const knex = opts?.knex ?? database_default();
|
|
10
|
+
const data = await new SettingsService({
|
|
11
|
+
schema: await getSchema({ database: knex }),
|
|
12
|
+
knex
|
|
13
|
+
}).readSingleton({ fields: [...CUSTOM_LLM_FIELDS] });
|
|
14
|
+
return !CUSTOM_LLM_FIELDS.find((key) => data[key] !== null);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
//#endregion
|
|
18
|
+
export { checkCustomLLM };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import database_default from "../../../database/index.js";
|
|
2
|
+
import { ItemsService } from "../../../services/items.js";
|
|
3
|
+
import { getSchema } from "../../../utils/get-schema.js";
|
|
4
|
+
import "../../../services/index.js";
|
|
5
|
+
import { isEqual } from "lodash-es";
|
|
6
|
+
import { appAccessMinimalPermissions, appRecommendedPermissions } from "@directus/system-data";
|
|
7
|
+
|
|
8
|
+
//#region src/license/entitlements/lib/custom-permission-rules-enabled.ts
|
|
9
|
+
function hasCustomRule(permission) {
|
|
10
|
+
if (permission.system === true) return false;
|
|
11
|
+
return permission.fields?.includes("*") !== true || Object.keys(permission.permissions ?? {}).length > 0 || Object.keys(permission.validation ?? {}).length > 0 || Object.keys(permission.presets ?? {}).length > 0;
|
|
12
|
+
}
|
|
13
|
+
function isRecommendedAppPermission(permission) {
|
|
14
|
+
if (permission.validation || permission.presets) return false;
|
|
15
|
+
const foundPermission = appRecommendedPermissions.find((p) => p.action === permission.action && p.collection === permission.collection);
|
|
16
|
+
if (!foundPermission) return false;
|
|
17
|
+
return isEqual(foundPermission.fields ?? null, permission.fields ?? null) && isEqual(foundPermission.permissions ?? null, permission.permissions ?? null);
|
|
18
|
+
}
|
|
19
|
+
function isMinimumAppPermission(permission) {
|
|
20
|
+
const foundPermission = appAccessMinimalPermissions.find((p) => p.action === permission.action && p.collection === permission.collection);
|
|
21
|
+
if (!foundPermission) return false;
|
|
22
|
+
return isEqual(foundPermission.fields ?? null, permission.fields ?? null) && isEqual(foundPermission.permissions ?? null, permission.permissions ?? null) && isEqual(foundPermission.validation ?? null, permission.validation ?? null) && isEqual(foundPermission.presets ?? null, permission.presets ?? null);
|
|
23
|
+
}
|
|
24
|
+
async function checkCustomPermissionRules(opts) {
|
|
25
|
+
const knex = opts?.knex ?? database_default();
|
|
26
|
+
return (await new ItemsService("directus_permissions", {
|
|
27
|
+
schema: await getSchema({ database: knex }),
|
|
28
|
+
knex
|
|
29
|
+
}).readByQuery({
|
|
30
|
+
limit: -1,
|
|
31
|
+
filter: { _or: [
|
|
32
|
+
{ permissions: { _nnull: true } },
|
|
33
|
+
{ validation: { _nnull: true } },
|
|
34
|
+
{ presets: { _nnull: true } },
|
|
35
|
+
{ fields: { _nnull: true } }
|
|
36
|
+
] }
|
|
37
|
+
})).filter((p) => hasCustomRule(p) && !isRecommendedAppPermission(p)).length === 0;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
//#endregion
|
|
41
|
+
export { checkCustomPermissionRules, hasCustomRule, isMinimumAppPermission, isRecommendedAppPermission };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import database_default from "../../../database/index.js";
|
|
2
|
+
import { getSchema } from "../../../utils/get-schema.js";
|
|
3
|
+
import { FlowsService } from "../../../services/flows.js";
|
|
4
|
+
|
|
5
|
+
//#region src/license/entitlements/lib/flows.ts
|
|
6
|
+
async function getActiveFlows(opts) {
|
|
7
|
+
const knex = opts?.knex ?? database_default();
|
|
8
|
+
return await new FlowsService({
|
|
9
|
+
schema: await getSchema({ database: knex }),
|
|
10
|
+
knex
|
|
11
|
+
}).readByQuery({
|
|
12
|
+
fields: ["id", "name"],
|
|
13
|
+
filter: { status: { _eq: "active" } },
|
|
14
|
+
limit: -1
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
async function countActiveFlows(opts) {
|
|
18
|
+
return (await getActiveFlows(opts)).length;
|
|
19
|
+
}
|
|
20
|
+
async function resolveFlows(flows, ctx) {
|
|
21
|
+
const flowsService = new FlowsService({
|
|
22
|
+
schema: await getSchema(),
|
|
23
|
+
accountability: ctx?.accountability
|
|
24
|
+
});
|
|
25
|
+
await Promise.allSettled(flows.map((flow_id) => flowsService.updateOne(flow_id, { status: "inactive" })));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
//#endregion
|
|
29
|
+
export { countActiveFlows, getActiveFlows, resolveFlows };
|