@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,31 @@
|
|
|
1
|
+
import { scheduleSynchronizedJob, validateCron } from "../utils/schedule.js";
|
|
2
|
+
import { durationToCron } from "./utils/duration-to-cron.js";
|
|
3
|
+
import { getLicenseManager } from "../license/manager.js";
|
|
4
|
+
|
|
5
|
+
//#region src/schedules/license.ts
|
|
6
|
+
let job = null;
|
|
7
|
+
async function stopLicenseCheck() {
|
|
8
|
+
await job?.stop();
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Schedule a license check at the license's validation_interval.
|
|
12
|
+
*/
|
|
13
|
+
async function schedule() {
|
|
14
|
+
await job?.stop();
|
|
15
|
+
const licenseManager = getLicenseManager();
|
|
16
|
+
const license = await licenseManager.getLicense();
|
|
17
|
+
if (license.meta.validation_interval === -1) return false;
|
|
18
|
+
const cron = durationToCron(license.meta.validation_interval);
|
|
19
|
+
if (!validateCron(cron)) return false;
|
|
20
|
+
job = scheduleSynchronizedJob("license-check", cron, async () => {
|
|
21
|
+
const jobLicense = await licenseManager.getLicense();
|
|
22
|
+
if (jobLicense.meta.validation_interval !== license.meta.validation_interval) {
|
|
23
|
+
await job?.stop();
|
|
24
|
+
if (jobLicense.meta.validation_interval !== -1) await schedule();
|
|
25
|
+
} else await licenseManager.refresh();
|
|
26
|
+
});
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
//#endregion
|
|
31
|
+
export { schedule as default, stopLicenseCheck };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { useLogger } from "../logger/index.js";
|
|
2
|
+
import { scheduleSynchronizedJob, validateCron } from "../utils/schedule.js";
|
|
3
|
+
import { getSchema } from "../utils/get-schema.js";
|
|
4
|
+
import { McpOAuthService } from "../services/mcp-oauth/index.js";
|
|
5
|
+
import { useEnv } from "@directus/env";
|
|
6
|
+
|
|
7
|
+
//#region src/schedules/oauth-cleanup.ts
|
|
8
|
+
async function scheduleOAuthCleanup() {
|
|
9
|
+
const env = useEnv();
|
|
10
|
+
const schedule = String(env["MCP_OAUTH_CLEANUP_SCHEDULE"]);
|
|
11
|
+
if (!validateCron(schedule)) {
|
|
12
|
+
useLogger().error(`Invalid MCP_OAUTH_CLEANUP_SCHEDULE: "${schedule}". OAuth cleanup disabled.`);
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
scheduleSynchronizedJob("oauth-cleanup", schedule, async () => {
|
|
16
|
+
try {
|
|
17
|
+
await new McpOAuthService({ schema: await getSchema() }).cleanup();
|
|
18
|
+
} catch (error) {
|
|
19
|
+
useLogger().error(error, "MCP OAuth cleanup failed");
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
//#endregion
|
|
26
|
+
export { scheduleOAuthCleanup as default };
|
|
@@ -2,9 +2,9 @@ import { useLogger } from "../logger/index.js";
|
|
|
2
2
|
import { getMilliseconds } from "../utils/get-milliseconds.js";
|
|
3
3
|
import { getHelpers } from "../database/helpers/index.js";
|
|
4
4
|
import database_default from "../database/index.js";
|
|
5
|
+
import { scheduleSynchronizedJob, validateCron } from "../utils/schedule.js";
|
|
5
6
|
import { useLock } from "../lock/lib/use-lock.js";
|
|
6
7
|
import "../lock/index.js";
|
|
7
|
-
import { scheduleSynchronizedJob, validateCron } from "../utils/schedule.js";
|
|
8
8
|
import { useEnv } from "@directus/env";
|
|
9
9
|
import { toBoolean } from "@directus/utils";
|
|
10
10
|
import { Action } from "@directus/constants";
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { getCache } from "../cache.js";
|
|
2
2
|
import { scheduleSynchronizedJob } from "../utils/schedule.js";
|
|
3
|
+
import { getEntitlementManager } from "../license/entitlements/manager.js";
|
|
3
4
|
import { track } from "../telemetry/lib/track.js";
|
|
4
5
|
import "../telemetry/index.js";
|
|
6
|
+
import "../license/index.js";
|
|
5
7
|
import { useEnv } from "@directus/env";
|
|
6
8
|
import { toBoolean } from "@directus/utils";
|
|
7
9
|
|
|
@@ -19,7 +21,8 @@ const jobCallback = () => {
|
|
|
19
21
|
* @returns Whether or not telemetry has been initialized
|
|
20
22
|
*/
|
|
21
23
|
async function schedule() {
|
|
22
|
-
|
|
24
|
+
const env = useEnv();
|
|
25
|
+
if (!getEntitlementManager().isEntitled("telemetry_required") && !toBoolean(env["TELEMETRY"])) return false;
|
|
23
26
|
scheduleSynchronizedJob("telemetry", "0 */6 * * *", jobCallback);
|
|
24
27
|
const { lockCache } = getCache();
|
|
25
28
|
if (!await lockCache.get("telemetry-lock")) {
|
package/dist/schedules/tus.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { RESUMABLE_UPLOADS } from "../constants.js";
|
|
2
|
-
import { getSchema } from "../utils/get-schema.js";
|
|
3
2
|
import { scheduleSynchronizedJob, validateCron } from "../utils/schedule.js";
|
|
3
|
+
import { getSchema } from "../utils/get-schema.js";
|
|
4
4
|
import { createTusServer } from "../services/tus/server.js";
|
|
5
5
|
import "../services/tus/index.js";
|
|
6
6
|
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { random } from "lodash-es";
|
|
2
|
+
|
|
3
|
+
//#region src/schedules/utils/duration-to-cron.ts
|
|
4
|
+
const HOURS_IN_SECONDS = 3600;
|
|
5
|
+
const ALLOWED_HOURS = new Set([
|
|
6
|
+
1,
|
|
7
|
+
2,
|
|
8
|
+
3,
|
|
9
|
+
4,
|
|
10
|
+
6,
|
|
11
|
+
8,
|
|
12
|
+
12
|
|
13
|
+
]);
|
|
14
|
+
/**
|
|
15
|
+
* Convert a duration in seconds into a cron expression
|
|
16
|
+
*
|
|
17
|
+
* - Honored intervals (hours): 1, 2, 3, 4, 6, 8, 12.
|
|
18
|
+
* - Random hour offset within [0, hours) spreads load across phase groups.
|
|
19
|
+
* - Any duration outside interval falls back to daily.
|
|
20
|
+
*
|
|
21
|
+
* 3600 (1h) → fires every hour at random minute and second
|
|
22
|
+
* 7200 (2h) → fires every 2h, phase offset 0 or 1
|
|
23
|
+
* 25200 (7h) → daily fallback
|
|
24
|
+
*/
|
|
25
|
+
function durationToCron(duration) {
|
|
26
|
+
const second = random(0, 59);
|
|
27
|
+
const minute = random(0, 59);
|
|
28
|
+
if (duration > 0 && duration % HOURS_IN_SECONDS === 0) {
|
|
29
|
+
const hours = duration / HOURS_IN_SECONDS;
|
|
30
|
+
if (ALLOWED_HOURS.has(hours)) return `${second} ${minute} ${hours === 1 ? "*" : random(0, hours - 1)}/${hours} * * *`;
|
|
31
|
+
}
|
|
32
|
+
return `${second} ${minute} ${random(0, 23)} * * *`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
//#endregion
|
|
36
|
+
export { durationToCron };
|
|
@@ -1,10 +1,25 @@
|
|
|
1
1
|
import { ItemsService } from "./items.js";
|
|
2
|
+
import { getHistoryFilterQuery } from "../utils/get-history-filter-query.js";
|
|
2
3
|
|
|
3
4
|
//#region src/services/activity.ts
|
|
4
5
|
var ActivityService = class extends ItemsService {
|
|
6
|
+
queryCache = /* @__PURE__ */ new WeakMap();
|
|
5
7
|
constructor(options) {
|
|
6
8
|
super("directus_activity", options);
|
|
7
9
|
}
|
|
10
|
+
async readByQuery(query, opts) {
|
|
11
|
+
if (this.accountability === null) return super.readByQuery(query, opts);
|
|
12
|
+
const historyQuery = this.getLimitedHistoryQuery(query);
|
|
13
|
+
return super.readByQuery(historyQuery, opts);
|
|
14
|
+
}
|
|
15
|
+
getLimitedHistoryQuery(query) {
|
|
16
|
+
let cachedQuery = this.queryCache.get(query);
|
|
17
|
+
if (!cachedQuery) {
|
|
18
|
+
cachedQuery = getHistoryFilterQuery(query, "activity_historical_timeframe", (sinceDate) => ({ timestamp: { _gte: sinceDate.toISOString() } }));
|
|
19
|
+
this.queryCache.set(query, cachedQuery);
|
|
20
|
+
}
|
|
21
|
+
return cachedQuery;
|
|
22
|
+
}
|
|
8
23
|
};
|
|
9
24
|
|
|
10
25
|
//#endregion
|
|
@@ -9,12 +9,15 @@ import { PayloadService } from "./payload.js";
|
|
|
9
9
|
import { ActivityService } from "./activity.js";
|
|
10
10
|
import { RateLimiterRes, createRateLimiter } from "../rate-limiter.js";
|
|
11
11
|
import { stall } from "../utils/stall.js";
|
|
12
|
+
import { getEntitlementManager } from "../license/entitlements/manager.js";
|
|
13
|
+
import { RevisionsService } from "./revisions.js";
|
|
12
14
|
import { SettingsService } from "./settings.js";
|
|
13
15
|
import { getAuthProvider } from "../auth.js";
|
|
14
|
-
import { RevisionsService } from "./revisions.js";
|
|
15
16
|
import { TFAService } from "./tfa.js";
|
|
17
|
+
import { getLicenseManager } from "../license/manager.js";
|
|
18
|
+
import "../license/index.js";
|
|
16
19
|
import { useEnv } from "@directus/env";
|
|
17
|
-
import { InvalidCredentialsError, InvalidOtpError, ServiceUnavailableError, UserSuspendedError } from "@directus/errors";
|
|
20
|
+
import { InvalidCredentialsError, InvalidOtpError, ResourceRestrictedError, ServiceUnavailableError, UserSuspendedError } from "@directus/errors";
|
|
18
21
|
import { clone, cloneDeep } from "lodash-es";
|
|
19
22
|
import { Action } from "@directus/constants";
|
|
20
23
|
import { performance } from "perf_hooks";
|
|
@@ -96,6 +99,7 @@ var AuthenticationService = class {
|
|
|
96
99
|
} catch (error) {
|
|
97
100
|
if (error instanceof RateLimiterRes && error.remainingPoints === 0) {
|
|
98
101
|
await this.knex("directus_users").update({ status: "suspended" }).where({ id: user.id });
|
|
102
|
+
await getEntitlementManager().clearCache("sso_enabled", "seats");
|
|
99
103
|
if (this.accountability) {
|
|
100
104
|
const activity = await this.activityService.createOne({
|
|
101
105
|
action: Action.UPDATE,
|
|
@@ -160,6 +164,7 @@ var AuthenticationService = class {
|
|
|
160
164
|
user: user.id,
|
|
161
165
|
ip: this.accountability?.ip ?? null
|
|
162
166
|
}, { knex: this.knex });
|
|
167
|
+
if (await getLicenseManager().isLocked() && globalAccess.admin === false) throw new ResourceRestrictedError({ category: "login" });
|
|
163
168
|
const tokenPayload = {
|
|
164
169
|
id: user.id,
|
|
165
170
|
role: user.role,
|
|
@@ -237,7 +242,7 @@ var AuthenticationService = class {
|
|
|
237
242
|
share_id: "d.id",
|
|
238
243
|
share_start: "d.date_start",
|
|
239
244
|
share_end: "d.date_end"
|
|
240
|
-
}).from("directus_sessions AS s").leftJoin("directus_users AS u", "s.user", "u.id").leftJoin("directus_shares AS d", "s.share", "d.id").where("s.token", refreshToken).andWhere("s.expires", ">=", /* @__PURE__ */ new Date()).andWhere((subQuery) => {
|
|
245
|
+
}).from("directus_sessions AS s").leftJoin("directus_users AS u", "s.user", "u.id").leftJoin("directus_shares AS d", "s.share", "d.id").where("s.token", refreshToken).andWhere("s.expires", ">=", /* @__PURE__ */ new Date()).andWhere("s.oauth_client", null).andWhere((subQuery) => {
|
|
241
246
|
subQuery.whereNull("d.date_end").orWhere("d.date_end", ">=", /* @__PURE__ */ new Date());
|
|
242
247
|
}).andWhere((subQuery) => {
|
|
243
248
|
subQuery.whereNull("d.date_start").orWhere("d.date_start", "<=", /* @__PURE__ */ new Date());
|
|
@@ -259,6 +264,7 @@ var AuthenticationService = class {
|
|
|
259
264
|
roles,
|
|
260
265
|
ip: this.accountability?.ip ?? null
|
|
261
266
|
}, { knex: this.knex });
|
|
267
|
+
if (await getLicenseManager().isLocked() && globalAccess.admin === false) throw new ResourceRestrictedError({ category: "login" });
|
|
262
268
|
if (record.user_id) await getAuthProvider(record.user_provider).refresh({
|
|
263
269
|
id: record.user_id,
|
|
264
270
|
first_name: record.user_first_name,
|
|
@@ -346,12 +352,13 @@ var AuthenticationService = class {
|
|
|
346
352
|
expires: sessionExpiration,
|
|
347
353
|
ip: this.accountability?.ip,
|
|
348
354
|
user_agent: this.accountability?.userAgent,
|
|
349
|
-
origin: this.accountability?.origin
|
|
355
|
+
origin: this.accountability?.origin,
|
|
356
|
+
oauth_client: sessionRecord["oauth_client"]
|
|
350
357
|
});
|
|
351
358
|
return newSessionToken;
|
|
352
359
|
}
|
|
353
360
|
async logout(refreshToken) {
|
|
354
|
-
const record = await this.knex.select("u.id", "u.first_name", "u.last_name", "u.email", "u.password", "u.status", "u.role", "u.provider", "u.external_identifier", "u.auth_data").from("directus_sessions as s").innerJoin("directus_users as u", "s.user", "u.id").where("s.token", refreshToken).first();
|
|
361
|
+
const record = await this.knex.select("u.id", "u.first_name", "u.last_name", "u.email", "u.password", "u.status", "u.role", "u.provider", "u.external_identifier", "u.auth_data").from("directus_sessions as s").innerJoin("directus_users as u", "s.user", "u.id").where("s.token", refreshToken).andWhere("s.oauth_client", null).first();
|
|
355
362
|
if (record) {
|
|
356
363
|
const user = record;
|
|
357
364
|
await getAuthProvider(user.provider).logout(clone(user));
|
|
@@ -7,12 +7,14 @@ import { validateAccess } from "../permissions/modules/validate-access/validate-
|
|
|
7
7
|
import { transaction } from "../utils/transaction.js";
|
|
8
8
|
import { shouldClearCache } from "../utils/should-clear-cache.js";
|
|
9
9
|
import { ItemsService } from "./items.js";
|
|
10
|
-
import { fetchAllowedCollections } from "../permissions/modules/fetch-allowed-collections/fetch-allowed-collections.js";
|
|
11
10
|
import { getSchema } from "../utils/get-schema.js";
|
|
11
|
+
import { getEntitlementManager } from "../license/entitlements/manager.js";
|
|
12
12
|
import { buildCollectionAndFieldRelations } from "./fields/build-collection-and-field-relations.js";
|
|
13
13
|
import { getCollectionMetaUpdates } from "./fields/get-collection-meta-updates.js";
|
|
14
14
|
import { getCollectionRelationList } from "./fields/get-collection-relation-list.js";
|
|
15
15
|
import { FieldsService } from "./fields.js";
|
|
16
|
+
import "../license/index.js";
|
|
17
|
+
import { fetchAllowedCollections } from "../permissions/modules/fetch-allowed-collections/fetch-allowed-collections.js";
|
|
16
18
|
import { useEnv } from "@directus/env";
|
|
17
19
|
import { ForbiddenError, InvalidPayloadError } from "@directus/errors";
|
|
18
20
|
import { addFieldFlag } from "@directus/utils";
|
|
@@ -48,6 +50,10 @@ var CollectionsService = class CollectionsService {
|
|
|
48
50
|
if (typeof payload.collection !== "string" || payload.collection === "") throw new InvalidPayloadError({ reason: `"collection" must be a non-empty string` });
|
|
49
51
|
if (payload.collection.startsWith("directus_")) throw new InvalidPayloadError({ reason: `Collections can't start with "directus_"` });
|
|
50
52
|
if (payload.collection.includes("/")) throw new InvalidPayloadError({ reason: `Collection name can't contain "/"` });
|
|
53
|
+
if (payload.schema && payload.meta && (!("status" in payload.meta) || payload.meta.status === "active")) await getEntitlementManager().assert("collections", {
|
|
54
|
+
adding: 1,
|
|
55
|
+
knex: this.knex
|
|
56
|
+
});
|
|
51
57
|
payload.collection = await this.helpers.schema.parseCollectionName(payload.collection);
|
|
52
58
|
const nestedActionEvents = [];
|
|
53
59
|
try {
|
|
@@ -126,7 +132,10 @@ var CollectionsService = class CollectionsService {
|
|
|
126
132
|
return payload.collection;
|
|
127
133
|
} finally {
|
|
128
134
|
if (shouldClearCache(this.cache, opts)) await this.cache.clear();
|
|
129
|
-
if (opts?.autoPurgeSystemCache !== false)
|
|
135
|
+
if (opts?.autoPurgeSystemCache !== false) {
|
|
136
|
+
await clearSystemCache({ autoPurgeCache: opts?.autoPurgeCache });
|
|
137
|
+
await getEntitlementManager().clearCache("collections");
|
|
138
|
+
}
|
|
130
139
|
if (opts?.emitEvents !== false && nestedActionEvents.length > 0) {
|
|
131
140
|
const updatedSchema = await getSchema();
|
|
132
141
|
for (const nestedActionEvent of nestedActionEvents) {
|
|
@@ -162,7 +171,10 @@ var CollectionsService = class CollectionsService {
|
|
|
162
171
|
});
|
|
163
172
|
} finally {
|
|
164
173
|
if (shouldClearCache(this.cache, opts)) await this.cache.clear();
|
|
165
|
-
if (opts?.autoPurgeSystemCache !== false)
|
|
174
|
+
if (opts?.autoPurgeSystemCache !== false) {
|
|
175
|
+
await clearSystemCache({ autoPurgeCache: opts?.autoPurgeCache });
|
|
176
|
+
await getEntitlementManager().clearCache("collections");
|
|
177
|
+
}
|
|
166
178
|
if (opts?.emitEvents !== false && nestedActionEvents.length > 0) {
|
|
167
179
|
const updatedSchema = await getSchema();
|
|
168
180
|
for (const nestedActionEvent of nestedActionEvents) {
|
|
@@ -253,7 +265,7 @@ var CollectionsService = class CollectionsService {
|
|
|
253
265
|
/**
|
|
254
266
|
* Update a single collection by name
|
|
255
267
|
*/
|
|
256
|
-
async updateOne(collectionKey,
|
|
268
|
+
async updateOne(collectionKey, payload, opts) {
|
|
257
269
|
if (this.accountability && this.accountability.admin !== true) throw new ForbiddenError();
|
|
258
270
|
const nestedActionEvents = [];
|
|
259
271
|
try {
|
|
@@ -262,8 +274,11 @@ var CollectionsService = class CollectionsService {
|
|
|
262
274
|
accountability: this.accountability,
|
|
263
275
|
schema: this.schema
|
|
264
276
|
});
|
|
265
|
-
const payload = data;
|
|
266
277
|
if (!payload.meta) return collectionKey;
|
|
278
|
+
if (payload.meta?.status === "active") await getEntitlementManager().assert("collections", {
|
|
279
|
+
adding: 1,
|
|
280
|
+
knex: this.knex
|
|
281
|
+
});
|
|
267
282
|
if (!!await this.knex.select("collection").from("directus_collections").where({ collection: collectionKey }).first()) await collectionsItemsService.updateOne(collectionKey, payload.meta, {
|
|
268
283
|
...opts,
|
|
269
284
|
bypassEmitAction: (params) => opts?.bypassEmitAction ? opts.bypassEmitAction(params) : nestedActionEvents.push(params)
|
|
@@ -278,7 +293,10 @@ var CollectionsService = class CollectionsService {
|
|
|
278
293
|
return collectionKey;
|
|
279
294
|
} finally {
|
|
280
295
|
if (shouldClearCache(this.cache, opts)) await this.cache.clear();
|
|
281
|
-
if (opts?.autoPurgeSystemCache !== false)
|
|
296
|
+
if (opts?.autoPurgeSystemCache !== false) {
|
|
297
|
+
await clearSystemCache({ autoPurgeCache: opts?.autoPurgeCache });
|
|
298
|
+
await getEntitlementManager().clearCache("collections");
|
|
299
|
+
}
|
|
282
300
|
if (opts?.emitEvents !== false && nestedActionEvents.length > 0) {
|
|
283
301
|
const updatedSchema = await getSchema();
|
|
284
302
|
for (const nestedActionEvent of nestedActionEvents) {
|
|
@@ -316,7 +334,10 @@ var CollectionsService = class CollectionsService {
|
|
|
316
334
|
});
|
|
317
335
|
} finally {
|
|
318
336
|
if (shouldClearCache(this.cache, opts)) await this.cache.clear();
|
|
319
|
-
if (opts?.autoPurgeSystemCache !== false)
|
|
337
|
+
if (opts?.autoPurgeSystemCache !== false) {
|
|
338
|
+
await clearSystemCache({ autoPurgeCache: opts?.autoPurgeCache });
|
|
339
|
+
await getEntitlementManager().clearCache("collections");
|
|
340
|
+
}
|
|
320
341
|
if (opts?.emitEvents !== false && nestedActionEvents.length > 0) {
|
|
321
342
|
const updatedSchema = await getSchema();
|
|
322
343
|
for (const nestedActionEvent of nestedActionEvents) {
|
|
@@ -349,7 +370,10 @@ var CollectionsService = class CollectionsService {
|
|
|
349
370
|
return collectionKeys;
|
|
350
371
|
} finally {
|
|
351
372
|
if (shouldClearCache(this.cache, opts)) await this.cache.clear();
|
|
352
|
-
if (opts?.autoPurgeSystemCache !== false)
|
|
373
|
+
if (opts?.autoPurgeSystemCache !== false) {
|
|
374
|
+
await clearSystemCache({ autoPurgeCache: opts?.autoPurgeCache });
|
|
375
|
+
await getEntitlementManager().clearCache("collections");
|
|
376
|
+
}
|
|
353
377
|
if (opts?.emitEvents !== false && nestedActionEvents.length > 0) {
|
|
354
378
|
const updatedSchema = await getSchema();
|
|
355
379
|
for (const nestedActionEvent of nestedActionEvents) {
|
|
@@ -434,7 +458,10 @@ var CollectionsService = class CollectionsService {
|
|
|
434
458
|
return collectionKey;
|
|
435
459
|
} finally {
|
|
436
460
|
if (shouldClearCache(this.cache, opts)) await this.cache.clear();
|
|
437
|
-
if (opts?.autoPurgeSystemCache !== false)
|
|
461
|
+
if (opts?.autoPurgeSystemCache !== false) {
|
|
462
|
+
await clearSystemCache({ autoPurgeCache: opts?.autoPurgeCache });
|
|
463
|
+
await getEntitlementManager().clearCache("collections");
|
|
464
|
+
}
|
|
438
465
|
if (opts?.emitEvents !== false && nestedActionEvents.length > 0) {
|
|
439
466
|
const updatedSchema = await getSchema();
|
|
440
467
|
for (const nestedActionEvent of nestedActionEvents) {
|
|
@@ -466,7 +493,10 @@ var CollectionsService = class CollectionsService {
|
|
|
466
493
|
return collectionKeys;
|
|
467
494
|
} finally {
|
|
468
495
|
if (shouldClearCache(this.cache, opts)) await this.cache.clear();
|
|
469
|
-
if (opts?.autoPurgeSystemCache !== false)
|
|
496
|
+
if (opts?.autoPurgeSystemCache !== false) {
|
|
497
|
+
await clearSystemCache({ autoPurgeCache: opts?.autoPurgeCache });
|
|
498
|
+
await getEntitlementManager().clearCache("collections");
|
|
499
|
+
}
|
|
470
500
|
if (opts?.emitEvents !== false && nestedActionEvents.length > 0) {
|
|
471
501
|
const updatedSchema = await getSchema();
|
|
472
502
|
for (const nestedActionEvent of nestedActionEvents) {
|
package/dist/services/fields.js
CHANGED
|
@@ -16,15 +16,15 @@ import getLocalType from "../utils/get-local-type.js";
|
|
|
16
16
|
import getDefaultValue from "../utils/get-default-value.js";
|
|
17
17
|
import { getSystemFieldRowsWithAuthProviders } from "../utils/get-field-system-rows.js";
|
|
18
18
|
import { getSchema } from "../utils/get-schema.js";
|
|
19
|
+
import { sanitizeColumn } from "../utils/sanitize-schema.js";
|
|
19
20
|
import { buildCollectionAndFieldRelations } from "./fields/build-collection-and-field-relations.js";
|
|
20
21
|
import { getCollectionMetaUpdates } from "./fields/get-collection-meta-updates.js";
|
|
21
22
|
import { getCollectionRelationList } from "./fields/get-collection-relation-list.js";
|
|
22
|
-
import { sanitizeColumn } from "../utils/sanitize-schema.js";
|
|
23
23
|
import { useEnv } from "@directus/env";
|
|
24
24
|
import { ForbiddenError, InvalidPayloadError } from "@directus/errors";
|
|
25
25
|
import { addFieldFlag, getRelations, toArray } from "@directus/utils";
|
|
26
26
|
import { isEqual, isNil, merge } from "lodash-es";
|
|
27
|
-
import { z } from "zod";
|
|
27
|
+
import { z as z$1 } from "zod";
|
|
28
28
|
import { DEFAULT_NUMERIC_PRECISION, DEFAULT_NUMERIC_SCALE, KNEX_TYPES, REGEX_BETWEEN_PARENS } from "@directus/constants";
|
|
29
29
|
import { createInspector } from "@directus/schema";
|
|
30
30
|
import { isSystemField } from "@directus/system-data";
|
|
@@ -32,10 +32,10 @@ import { isSystemField } from "@directus/system-data";
|
|
|
32
32
|
//#region src/services/fields.ts
|
|
33
33
|
const systemFieldRows$1 = getSystemFieldRowsWithAuthProviders();
|
|
34
34
|
const env = useEnv();
|
|
35
|
-
const systemFieldUpdateSchema = z.object({
|
|
36
|
-
collection: z.string().optional(),
|
|
37
|
-
field: z.string().optional(),
|
|
38
|
-
schema: z.object({ is_indexed: z.boolean().optional() }).strict()
|
|
35
|
+
const systemFieldUpdateSchema = z$1.object({
|
|
36
|
+
collection: z$1.string().optional(),
|
|
37
|
+
field: z$1.string().optional(),
|
|
38
|
+
schema: z$1.object({ is_indexed: z$1.boolean().optional() }).strict()
|
|
39
39
|
}).strict();
|
|
40
40
|
var FieldsService = class FieldsService {
|
|
41
41
|
knex;
|
package/dist/services/flows.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ItemsService } from "./items.js";
|
|
2
|
+
import { getEntitlementManager } from "../license/entitlements/manager.js";
|
|
2
3
|
import { getFlowManager } from "../flows.js";
|
|
3
4
|
|
|
4
5
|
//#region src/services/flows.ts
|
|
@@ -7,12 +8,22 @@ var FlowsService = class extends ItemsService {
|
|
|
7
8
|
super("directus_flows", options);
|
|
8
9
|
}
|
|
9
10
|
async createOne(data, opts) {
|
|
11
|
+
if (!("status" in data) || data["status"] === "active") await getEntitlementManager().assert("flows", {
|
|
12
|
+
adding: 1,
|
|
13
|
+
knex: this.knex
|
|
14
|
+
});
|
|
10
15
|
const result = await super.createOne(data, opts);
|
|
16
|
+
await getEntitlementManager().clearCache("flows");
|
|
11
17
|
await getFlowManager().reload();
|
|
12
18
|
return result;
|
|
13
19
|
}
|
|
14
20
|
async updateMany(keys, data, opts) {
|
|
21
|
+
if ("status" in data && data["status"] === "active") await getEntitlementManager().assert("flows", {
|
|
22
|
+
adding: keys.length,
|
|
23
|
+
knex: this.knex
|
|
24
|
+
});
|
|
15
25
|
const result = await super.updateMany(keys, data, opts);
|
|
26
|
+
await getEntitlementManager().clearCache("flows");
|
|
16
27
|
await getFlowManager().reload();
|
|
17
28
|
return result;
|
|
18
29
|
}
|
|
@@ -22,6 +33,7 @@ var FlowsService = class extends ItemsService {
|
|
|
22
33
|
reject: null
|
|
23
34
|
}).whereIn("flow", keys);
|
|
24
35
|
const result = await super.deleteMany(keys, opts);
|
|
36
|
+
await getEntitlementManager().clearCache("flows");
|
|
25
37
|
await getFlowManager().reload();
|
|
26
38
|
return result;
|
|
27
39
|
}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { RelationsService } from "../../relations.js";
|
|
2
|
-
import { FieldsService, systemFieldUpdateSchema } from "../../fields.js";
|
|
3
|
-
import { CollectionsService } from "../../collections.js";
|
|
4
2
|
import { ExtensionsService } from "../../extensions.js";
|
|
3
|
+
import { FieldsService, systemFieldUpdateSchema } from "../../fields.js";
|
|
5
4
|
import { getCollectionType } from "./get-collection-type.js";
|
|
6
5
|
import { getFieldType } from "./get-field-type.js";
|
|
7
6
|
import { getRelationType } from "./get-relation-type.js";
|
|
8
7
|
import "../index.js";
|
|
8
|
+
import { CollectionsService } from "../../collections.js";
|
|
9
9
|
import { InvalidPayloadError } from "@directus/errors";
|
|
10
10
|
import { isSystemField } from "@directus/system-data";
|
|
11
11
|
import { GraphQLBoolean, GraphQLID, GraphQLList, GraphQLNonNull, GraphQLString } from "graphql";
|
|
@@ -4,9 +4,9 @@ import { generateHash } from "../../../utils/generate-hash.js";
|
|
|
4
4
|
import { getSecret } from "../../../utils/get-secret.js";
|
|
5
5
|
import { createDefaultAccountability } from "../../../permissions/utils/create-default-accountability.js";
|
|
6
6
|
import { verifyAccessJWT } from "../../../utils/jwt.js";
|
|
7
|
+
import { RevisionsService } from "../../revisions.js";
|
|
7
8
|
import { UsersService } from "../../users.js";
|
|
8
9
|
import { getIPFromReq } from "../../../utils/get-ip-from-req.js";
|
|
9
|
-
import { RevisionsService } from "../../revisions.js";
|
|
10
10
|
import { TFAService } from "../../tfa.js";
|
|
11
11
|
import { AuthenticationService } from "../../authentication.js";
|
|
12
12
|
import isDirectusJWT from "../../../utils/is-directus-jwt.js";
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import database_default from "../../../database/index.js";
|
|
2
2
|
import { FilesService } from "../../files.js";
|
|
3
3
|
import { RelationsService } from "../../relations.js";
|
|
4
|
-
import { FieldsService } from "../../fields.js";
|
|
5
|
-
import { CollectionsService } from "../../collections.js";
|
|
6
4
|
import { UsersService } from "../../users.js";
|
|
5
|
+
import { FieldsService } from "../../fields.js";
|
|
7
6
|
import { fetchAccountabilityCollectionAccess } from "../../../permissions/modules/fetch-accountability-collection-access/fetch-accountability-collection-access.js";
|
|
8
7
|
import { fetchAccountabilityPolicyGlobals } from "../../../permissions/modules/fetch-accountability-policy-globals/fetch-accountability-policy-globals.js";
|
|
9
8
|
import { RolesService } from "../../roles.js";
|
|
@@ -19,6 +18,7 @@ import { resolveSystemAdmin } from "./system-admin.js";
|
|
|
19
18
|
import { globalResolvers } from "./system-global.js";
|
|
20
19
|
import { generateSchema } from "../schema/index.js";
|
|
21
20
|
import { GraphQLService } from "../index.js";
|
|
21
|
+
import { CollectionsService } from "../../collections.js";
|
|
22
22
|
import { useEnv } from "@directus/env";
|
|
23
23
|
import { toBoolean } from "@directus/utils";
|
|
24
24
|
import { GraphQLBoolean, GraphQLEnumType, GraphQLInt, GraphQLList, GraphQLNonNull, GraphQLObjectType, GraphQLString } from "graphql";
|
|
@@ -30,22 +30,38 @@ function injectSystemResolvers(gql, schemaComposer, { CreateCollectionTypes, Rea
|
|
|
30
30
|
globalResolvers(gql, schemaComposer);
|
|
31
31
|
const ServerInfo = schemaComposer.createObjectTC({
|
|
32
32
|
name: "server_info",
|
|
33
|
-
fields: {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
33
|
+
fields: {
|
|
34
|
+
project: { type: new GraphQLObjectType({
|
|
35
|
+
name: "server_info_project",
|
|
36
|
+
fields: {
|
|
37
|
+
project_name: { type: GraphQLString },
|
|
38
|
+
project_descriptor: { type: GraphQLString },
|
|
39
|
+
project_logo: { type: GraphQLString },
|
|
40
|
+
project_color: { type: GraphQLString },
|
|
41
|
+
default_language: { type: GraphQLString },
|
|
42
|
+
public_foreground: { type: GraphQLString },
|
|
43
|
+
public_background: { type: GraphQLString },
|
|
44
|
+
public_note: { type: GraphQLString },
|
|
45
|
+
custom_css: { type: GraphQLString },
|
|
46
|
+
public_registration: { type: GraphQLBoolean },
|
|
47
|
+
public_registration_verify_email: { type: GraphQLBoolean }
|
|
48
|
+
}
|
|
49
|
+
}) },
|
|
50
|
+
license: { type: new GraphQLObjectType({
|
|
51
|
+
name: "server_info_license",
|
|
52
|
+
fields: {
|
|
53
|
+
source: { type: GraphQLString },
|
|
54
|
+
entitlements: { type: new GraphQLObjectType({
|
|
55
|
+
name: "server_info_license_entitlements",
|
|
56
|
+
fields: {
|
|
57
|
+
production_enabled: { type: new GraphQLNonNull(GraphQLBoolean) },
|
|
58
|
+
ai_translations_enabled: { type: new GraphQLNonNull(GraphQLBoolean) },
|
|
59
|
+
display_powered_by: { type: new GraphQLNonNull(GraphQLString) }
|
|
60
|
+
}
|
|
61
|
+
}) }
|
|
62
|
+
}
|
|
63
|
+
}) }
|
|
64
|
+
}
|
|
49
65
|
});
|
|
50
66
|
if (gql.accountability?.user) ServerInfo.addFields({
|
|
51
67
|
rateLimit: env["RATE_LIMITER_ENABLED"] ? { type: new GraphQLObjectType({
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { applyFunctionToColumnName } from "../../../database/run-ast/utils/apply-function-to-column-name.js";
|
|
1
2
|
import { getGraphQLType } from "../../../utils/get-graphql-type.js";
|
|
2
3
|
import { SYSTEM_DENY_LIST } from "./index.js";
|
|
3
4
|
import { mapKeys, pick } from "lodash-es";
|
|
4
5
|
import { GENERATE_SPECIAL } from "@directus/constants";
|
|
5
|
-
import { GraphQLID, GraphQLInt, GraphQLNonNull, GraphQLScalarType, GraphQLUnionType } from "graphql";
|
|
6
|
+
import { GraphQLID, GraphQLInt, GraphQLNonNull, GraphQLScalarType, GraphQLString, GraphQLUnionType } from "graphql";
|
|
6
7
|
import { GraphQLJSON } from "graphql-compose";
|
|
7
8
|
|
|
8
9
|
//#region src/services/graphql/schema/get-types.ts
|
|
@@ -81,12 +82,32 @@ function getTypes(schemaComposer, scope, schema, inconsistentFields, action) {
|
|
|
81
82
|
return mapKeys(pick(obj, Object.keys(DateTimeFunctions.getFields()).map((key) => `${field.field}_${key}`)), (_value, key) => key.substring(field.field.length + 1));
|
|
82
83
|
}
|
|
83
84
|
};
|
|
84
|
-
if (field.type === "
|
|
85
|
+
if (field.type === "alias") acc[`${field.field}_func`] = {
|
|
85
86
|
type: CountFunctions,
|
|
86
87
|
resolve: (obj) => {
|
|
87
88
|
return mapKeys(pick(obj, Object.keys(CountFunctions.getFields()).map((key) => `${field.field}_${key}`)), (_value, key) => key.substring(field.field.length + 1));
|
|
88
89
|
}
|
|
89
90
|
};
|
|
91
|
+
if (field.type === "json") {
|
|
92
|
+
const JsonFieldFunctions = schemaComposer.createObjectTC({
|
|
93
|
+
name: `${collection.collection}_${field.field}_func`,
|
|
94
|
+
fields: {
|
|
95
|
+
count: {
|
|
96
|
+
type: GraphQLInt,
|
|
97
|
+
resolve: (obj) => obj[`${field.field}_count`]
|
|
98
|
+
},
|
|
99
|
+
json: {
|
|
100
|
+
type: GraphQLJSON,
|
|
101
|
+
args: { path: { type: new GraphQLNonNull(GraphQLString) } },
|
|
102
|
+
resolve: (obj, { path }) => obj[applyFunctionToColumnName(`json(${field.field}, ${path})`)]
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
acc[`${field.field}_func`] = {
|
|
107
|
+
type: JsonFieldFunctions,
|
|
108
|
+
resolve: (obj) => obj
|
|
109
|
+
};
|
|
110
|
+
}
|
|
90
111
|
}
|
|
91
112
|
return acc;
|
|
92
113
|
}, {})
|
|
@@ -65,6 +65,14 @@ async function getQuery(rawQuery, schema, selections, variableValues, accountabi
|
|
|
65
65
|
for (const subSelection of selection.selectionSet.selections) {
|
|
66
66
|
if (subSelection.kind !== "Field") continue;
|
|
67
67
|
if (subSelection.name.value.startsWith("__")) continue;
|
|
68
|
+
if (subSelection.name.value === "json" && subSelection.arguments?.length) {
|
|
69
|
+
const pathArg = subSelection.arguments.find((a) => a.name.value === "path");
|
|
70
|
+
if (pathArg) {
|
|
71
|
+
const pathValue = parseArgs([pathArg], variableValues).path;
|
|
72
|
+
children.push(`json(${rootField}, ${pathValue})`);
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
68
76
|
children.push(`${subSelection.name.value}(${rootField})`);
|
|
69
77
|
}
|
|
70
78
|
} else children = await parseFields(selection.selectionSet.selections, currentAlias ?? current, childCollection, selection.kind === "Field" ? selection.name.value : void 0);
|
|
@@ -6,6 +6,7 @@ import { GraphQLGeoJSON } from "../types/geojson.js";
|
|
|
6
6
|
import { GraphQLHash } from "../types/hash.js";
|
|
7
7
|
import { getGraphQLType } from "../../../utils/get-graphql-type.js";
|
|
8
8
|
import { resolveQuery } from "../resolvers/query.js";
|
|
9
|
+
import { GraphQLJsonFilter } from "../types/json-filter.js";
|
|
9
10
|
import { GraphQLStringOrFloat } from "../types/string-or-float.js";
|
|
10
11
|
import { getTypes } from "./get-types.js";
|
|
11
12
|
import { SYSTEM_DENY_LIST } from "./index.js";
|
|
@@ -154,6 +155,14 @@ async function getReadableTypes(gql, schemaComposer, schema, inconsistentFields)
|
|
|
154
155
|
_nempty: { type: GraphQLBoolean }
|
|
155
156
|
}
|
|
156
157
|
});
|
|
158
|
+
const JsonFilterOperators = schemaComposer.createInputTC({
|
|
159
|
+
name: "json_filter_operators",
|
|
160
|
+
fields: {
|
|
161
|
+
_json: { type: GraphQLJsonFilter },
|
|
162
|
+
_null: { type: GraphQLBoolean },
|
|
163
|
+
_nnull: { type: GraphQLBoolean }
|
|
164
|
+
}
|
|
165
|
+
});
|
|
157
166
|
const CountFunctionFilterOperators = schemaComposer.createInputTC({
|
|
158
167
|
name: "count_function_filter_operators",
|
|
159
168
|
fields: { count: { type: NumberFilterOperators } }
|
|
@@ -213,6 +222,9 @@ async function getReadableTypes(gql, schemaComposer, schema, inconsistentFields)
|
|
|
213
222
|
case GraphQLDate:
|
|
214
223
|
filterOperatorType = DateFilterOperators;
|
|
215
224
|
break;
|
|
225
|
+
case GraphQLJSON:
|
|
226
|
+
filterOperatorType = JsonFilterOperators;
|
|
227
|
+
break;
|
|
216
228
|
case GraphQLGeoJSON:
|
|
217
229
|
filterOperatorType = GeometryFilterOperators;
|
|
218
230
|
break;
|