@directus/api 13.1.1 → 14.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__utils__/snapshots.js +9 -0
- package/dist/app.js +6 -4
- package/dist/auth/drivers/ldap.js +3 -2
- package/dist/auth/drivers/local.js +1 -1
- package/dist/auth/drivers/oauth2.js +1 -1
- package/dist/auth/drivers/openid.js +1 -1
- package/dist/auth/drivers/saml.js +1 -1
- package/dist/auth.js +1 -1
- package/dist/cli/index.js +7 -4
- package/dist/controllers/activity.js +1 -1
- package/dist/controllers/assets.js +2 -2
- package/dist/controllers/auth.js +1 -1
- package/dist/controllers/collections.js +1 -1
- package/dist/controllers/dashboards.js +1 -1
- package/dist/controllers/extensions.js +29 -16
- package/dist/controllers/fields.js +1 -1
- package/dist/controllers/files.js +1 -1
- package/dist/controllers/flows.js +1 -1
- package/dist/controllers/folders.js +1 -1
- package/dist/controllers/items.js +1 -1
- package/dist/controllers/not-found.js +1 -1
- 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/presets.js +1 -1
- package/dist/controllers/relations.js +1 -1
- package/dist/controllers/roles.js +1 -1
- package/dist/controllers/schema.js +1 -1
- package/dist/controllers/server.js +1 -1
- package/dist/controllers/settings.js +1 -1
- 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 +37 -18
- package/dist/controllers/versions.d.ts +2 -0
- package/dist/controllers/versions.js +188 -0
- package/dist/controllers/webhooks.js +1 -1
- package/dist/database/errors/dialects/mssql.js +1 -1
- package/dist/database/errors/dialects/mysql.js +1 -1
- package/dist/database/errors/dialects/oracle.js +1 -1
- package/dist/database/errors/dialects/postgres.js +1 -1
- package/dist/database/errors/dialects/sqlite.js +1 -1
- package/dist/database/helpers/schema/dialects/mysql.js +1 -1
- package/dist/database/helpers/sequence/dialects/postgres.d.ts +5 -2
- package/dist/database/helpers/sequence/dialects/postgres.js +6 -3
- package/dist/database/migrations/20230823A-add-content-versioning.d.ts +3 -0
- package/dist/database/migrations/20230823A-add-content-versioning.js +36 -0
- package/dist/database/migrations/20230927A-themes.d.ts +3 -0
- package/dist/database/migrations/20230927A-themes.js +49 -0
- package/dist/database/migrations/20231009A-update-csv-fields-to-text.d.ts +3 -0
- package/dist/database/migrations/20231009A-update-csv-fields-to-text.js +44 -0
- package/dist/database/migrations/20231009B-update-panel-options.d.ts +3 -0
- package/dist/database/migrations/20231009B-update-panel-options.js +77 -0
- package/dist/database/migrations/20231010A-add-extensions.d.ts +3 -0
- package/dist/database/migrations/20231010A-add-extensions.js +9 -0
- package/dist/database/run-ast.js +2 -2
- package/dist/database/seeds/run.js +1 -1
- package/dist/database/system-data/collections/collections.yaml +6 -0
- package/dist/database/system-data/fields/activity.yaml +4 -4
- package/dist/database/system-data/fields/collections.yaml +19 -0
- package/dist/database/system-data/fields/extensions.yaml +10 -0
- package/dist/database/system-data/fields/revisions.yaml +3 -0
- package/dist/database/system-data/fields/settings.yaml +73 -17
- package/dist/database/system-data/fields/users.yaml +48 -12
- package/dist/database/system-data/fields/versions.yaml +38 -0
- package/dist/database/system-data/fields/webhooks.yaml +9 -9
- package/dist/database/system-data/relations/relations.yaml +88 -20
- package/dist/env.js +4 -0
- package/dist/extensions/index.d.ts +2 -0
- package/dist/extensions/index.js +9 -0
- package/dist/extensions/lib/get-extensions-settings.d.ts +7 -0
- package/dist/extensions/lib/get-extensions-settings.js +39 -0
- package/dist/extensions/lib/get-extensions.d.ts +1 -0
- package/dist/extensions/lib/get-extensions.js +11 -0
- package/dist/extensions/lib/get-shared-deps-mapping.d.ts +1 -0
- package/dist/extensions/lib/get-shared-deps-mapping.js +26 -0
- package/dist/extensions/lib/sandbox/generate-api-extensions-sandbox-entrypoint.d.ts +31 -0
- package/dist/extensions/lib/sandbox/generate-api-extensions-sandbox-entrypoint.js +80 -0
- package/dist/extensions/lib/sandbox/generate-host-function-reference.d.ts +11 -0
- package/dist/extensions/lib/sandbox/generate-host-function-reference.js +28 -0
- package/dist/extensions/lib/sandbox/register/action.d.ts +6 -0
- package/dist/extensions/lib/sandbox/register/action.js +18 -0
- package/dist/extensions/lib/sandbox/register/call-reference.d.ts +5 -0
- package/dist/extensions/lib/sandbox/register/call-reference.js +20 -0
- package/dist/extensions/lib/sandbox/register/filter.d.ts +6 -0
- package/dist/extensions/lib/sandbox/register/filter.js +21 -0
- package/dist/extensions/lib/sandbox/register/index.d.ts +5 -0
- package/dist/extensions/lib/sandbox/register/index.js +5 -0
- package/dist/extensions/lib/sandbox/register/operation.d.ts +6 -0
- package/dist/extensions/lib/sandbox/register/operation.js +19 -0
- package/dist/extensions/lib/sandbox/register/route.d.ts +17 -0
- package/dist/extensions/lib/sandbox/register/route.js +44 -0
- package/dist/extensions/lib/sandbox/sdk/generators/index.d.ts +3 -0
- package/dist/extensions/lib/sandbox/sdk/generators/index.js +3 -0
- package/dist/extensions/lib/sandbox/sdk/generators/log.d.ts +3 -0
- package/dist/extensions/lib/sandbox/sdk/generators/log.js +11 -0
- package/dist/extensions/lib/sandbox/sdk/generators/request.d.ts +12 -0
- package/dist/extensions/lib/sandbox/sdk/generators/request.js +49 -0
- package/dist/extensions/lib/sandbox/sdk/generators/sleep.d.ts +3 -0
- package/dist/extensions/lib/sandbox/sdk/generators/sleep.js +11 -0
- package/dist/extensions/lib/sandbox/sdk/index.d.ts +2 -0
- package/dist/extensions/lib/sandbox/sdk/index.js +2 -0
- package/dist/extensions/lib/sandbox/sdk/instantiate.d.ts +11 -0
- package/dist/extensions/lib/sandbox/sdk/instantiate.js +28 -0
- package/dist/extensions/lib/sandbox/sdk/sdk.d.ts +20 -0
- package/dist/extensions/lib/sandbox/sdk/sdk.js +11 -0
- package/dist/extensions/lib/sandbox/sdk/utils/index.d.ts +1 -0
- package/dist/extensions/lib/sandbox/sdk/utils/index.js +1 -0
- package/dist/extensions/lib/sandbox/sdk/utils/wrap.d.ts +11 -0
- package/dist/extensions/lib/sandbox/sdk/utils/wrap.js +17 -0
- package/dist/extensions/lib/wrap-embeds.d.ts +4 -0
- package/dist/extensions/lib/wrap-embeds.js +8 -0
- package/dist/extensions/manager.d.ts +158 -0
- package/dist/extensions/manager.js +604 -0
- package/dist/extensions/types.d.ts +19 -0
- package/dist/flows.d.ts +2 -2
- package/dist/flows.js +7 -7
- package/dist/middleware/check-ip.js +1 -1
- package/dist/middleware/collection-exists.js +1 -1
- package/dist/middleware/error-handler.js +1 -1
- package/dist/middleware/graphql.js +1 -1
- package/dist/middleware/rate-limiter-global.js +1 -1
- package/dist/middleware/rate-limiter-ip.js +1 -1
- package/dist/middleware/respond.js +13 -1
- package/dist/middleware/validate-batch.js +1 -1
- package/dist/operations/condition/index.d.ts +1 -1
- package/dist/operations/condition/index.js +2 -1
- package/dist/operations/exec/index.d.ts +1 -1
- package/dist/operations/exec/index.js +1 -1
- package/dist/operations/item-create/index.d.ts +1 -1
- package/dist/operations/item-create/index.js +2 -1
- package/dist/operations/item-delete/index.d.ts +1 -1
- package/dist/operations/item-delete/index.js +2 -1
- package/dist/operations/item-read/index.d.ts +1 -1
- package/dist/operations/item-read/index.js +2 -1
- package/dist/operations/item-update/index.d.ts +1 -1
- package/dist/operations/item-update/index.js +2 -1
- package/dist/operations/json-web-token/index.d.ts +1 -1
- package/dist/operations/json-web-token/index.js +2 -1
- package/dist/operations/log/index.d.ts +1 -1
- package/dist/operations/log/index.js +2 -1
- package/dist/operations/mail/index.d.ts +1 -1
- package/dist/operations/mail/index.js +1 -1
- package/dist/operations/notification/index.d.ts +1 -1
- package/dist/operations/notification/index.js +2 -1
- package/dist/operations/request/index.d.ts +1 -1
- package/dist/operations/request/index.js +3 -2
- package/dist/operations/sleep/index.d.ts +1 -1
- package/dist/operations/sleep/index.js +1 -1
- package/dist/operations/transform/index.d.ts +1 -1
- package/dist/operations/transform/index.js +2 -1
- package/dist/operations/trigger/index.d.ts +1 -1
- package/dist/operations/trigger/index.js +2 -1
- package/dist/services/activity.js +1 -1
- package/dist/services/assets.d.ts +1 -1
- package/dist/services/assets.js +3 -3
- package/dist/services/authentication.js +2 -2
- package/dist/services/authorization.js +1 -1
- package/dist/services/collections.js +3 -3
- package/dist/services/extensions.d.ts +31 -0
- package/dist/services/extensions.js +121 -0
- package/dist/services/fields.d.ts +2 -2
- package/dist/services/fields.js +4 -4
- package/dist/services/files.d.ts +4 -1
- package/dist/services/files.js +5 -5
- package/dist/services/graphql/index.d.ts +1 -1
- package/dist/services/graphql/index.js +87 -24
- package/dist/services/graphql/subscription.js +3 -3
- package/dist/services/import-export/import-worker.d.ts +9 -0
- package/dist/services/import-export/import-worker.js +9 -0
- package/dist/services/{import-export.d.ts → import-export/index.d.ts} +2 -2
- package/dist/services/{import-export.js → import-export/index.js} +51 -42
- package/dist/services/index.d.ts +3 -1
- package/dist/services/index.js +3 -1
- package/dist/services/items.js +2 -2
- package/dist/services/mail/index.js +1 -1
- package/dist/services/meta.js +1 -1
- package/dist/services/payload.js +1 -1
- package/dist/services/permissions.d.ts +2 -2
- package/dist/services/permissions.js +1 -1
- package/dist/services/relations.js +1 -1
- package/dist/services/revisions.js +1 -1
- package/dist/services/roles.js +1 -1
- package/dist/services/schema.js +1 -1
- package/dist/services/server.js +3 -1
- package/dist/services/shares.js +1 -1
- package/dist/services/tfa.js +1 -1
- package/dist/services/translations.js +1 -1
- package/dist/services/users.js +4 -2
- package/dist/services/utils.d.ts +1 -0
- package/dist/services/utils.js +8 -2
- package/dist/services/versions.d.ts +21 -0
- package/dist/services/versions.js +232 -0
- package/dist/services/websocket.js +11 -1
- package/dist/types/collection.d.ts +1 -0
- package/dist/types/index.d.ts +0 -1
- package/dist/types/index.js +0 -1
- package/dist/utils/apply-query.d.ts +1 -1
- package/dist/utils/apply-query.js +31 -3
- package/dist/utils/delete-from-require-cache.d.ts +1 -0
- package/dist/utils/delete-from-require-cache.js +5 -0
- package/dist/utils/get-accountability-for-token.js +1 -1
- package/dist/utils/get-ast-from-query.js +1 -1
- package/dist/utils/get-column-path.js +1 -1
- package/dist/utils/get-column.js +1 -1
- package/dist/utils/get-default-value.d.ts +1 -2
- package/dist/utils/get-permissions.js +1 -1
- package/dist/utils/get-service.js +3 -1
- package/dist/utils/import-file-url.d.ts +5 -0
- package/dist/utils/import-file-url.js +6 -0
- package/dist/utils/job-queue.d.ts +2 -3
- package/dist/utils/jwt.js +1 -1
- package/dist/utils/redact-object.js +9 -3
- package/dist/utils/sanitize-query.js +3 -0
- package/dist/utils/transformations.d.ts +2 -1
- package/dist/utils/validate-diff.js +1 -1
- package/dist/utils/validate-keys.js +1 -1
- package/dist/utils/validate-query.js +2 -1
- package/dist/utils/validate-snapshot.js +1 -1
- package/dist/websocket/controllers/base.js +1 -1
- package/dist/websocket/controllers/index.d.ts +1 -1
- package/dist/websocket/controllers/index.js +0 -7
- package/dist/websocket/handlers/heartbeat.js +6 -1
- package/dist/websocket/handlers/subscribe.js +11 -16
- package/dist/websocket/utils/items.d.ts +4 -14
- package/dist/websocket/utils/items.js +59 -64
- package/dist/worker-pool.d.ts +2 -0
- package/dist/worker-pool.js +11 -0
- package/package.json +34 -31
- package/dist/errors/codes.d.ts +0 -29
- package/dist/errors/codes.js +0 -30
- package/dist/errors/contains-null-values.d.ts +0 -7
- package/dist/errors/contains-null-values.js +0 -4
- package/dist/errors/content-too-large.d.ts +0 -1
- package/dist/errors/content-too-large.js +0 -3
- package/dist/errors/forbidden.d.ts +0 -1
- package/dist/errors/forbidden.js +0 -3
- package/dist/errors/hit-rate-limit.d.ts +0 -6
- package/dist/errors/hit-rate-limit.js +0 -8
- package/dist/errors/illegal-asset-transformation.d.ts +0 -4
- package/dist/errors/illegal-asset-transformation.js +0 -3
- package/dist/errors/index.d.ts +0 -28
- package/dist/errors/index.js +0 -28
- package/dist/errors/invalid-credentials.d.ts +0 -1
- package/dist/errors/invalid-credentials.js +0 -3
- package/dist/errors/invalid-foreign-key.d.ts +0 -6
- package/dist/errors/invalid-foreign-key.js +0 -14
- package/dist/errors/invalid-ip.d.ts +0 -1
- package/dist/errors/invalid-ip.js +0 -3
- package/dist/errors/invalid-otp.d.ts +0 -1
- package/dist/errors/invalid-otp.js +0 -3
- package/dist/errors/invalid-payload.d.ts +0 -5
- package/dist/errors/invalid-payload.js +0 -4
- package/dist/errors/invalid-provider-config.d.ts +0 -5
- package/dist/errors/invalid-provider-config.js +0 -3
- package/dist/errors/invalid-provider.d.ts +0 -1
- package/dist/errors/invalid-provider.js +0 -3
- package/dist/errors/invalid-query.d.ts +0 -5
- package/dist/errors/invalid-query.js +0 -4
- package/dist/errors/invalid-token.d.ts +0 -1
- package/dist/errors/invalid-token.js +0 -3
- package/dist/errors/method-not-allowed.d.ts +0 -6
- package/dist/errors/method-not-allowed.js +0 -6
- package/dist/errors/not-null-violation.d.ts +0 -6
- package/dist/errors/not-null-violation.js +0 -14
- package/dist/errors/range-not-satisfiable.d.ts +0 -7
- package/dist/errors/range-not-satisfiable.js +0 -7
- package/dist/errors/record-not-unique.d.ts +0 -6
- package/dist/errors/record-not-unique.js +0 -14
- package/dist/errors/route-not-found.d.ts +0 -5
- package/dist/errors/route-not-found.js +0 -4
- package/dist/errors/service-unavailable.d.ts +0 -7
- package/dist/errors/service-unavailable.js +0 -4
- package/dist/errors/token-expired.d.ts +0 -1
- package/dist/errors/token-expired.js +0 -3
- package/dist/errors/unexpected-response.d.ts +0 -1
- package/dist/errors/unexpected-response.js +0 -3
- package/dist/errors/unprocessable-content.d.ts +0 -5
- package/dist/errors/unprocessable-content.js +0 -4
- package/dist/errors/unsupported-media-type.d.ts +0 -6
- package/dist/errors/unsupported-media-type.js +0 -4
- package/dist/errors/user-suspended.d.ts +0 -1
- package/dist/errors/user-suspended.js +0 -3
- package/dist/errors/value-out-of-range.d.ts +0 -6
- package/dist/errors/value-out-of-range.js +0 -14
- package/dist/errors/value-too-long.d.ts +0 -6
- package/dist/errors/value-too-long.js +0 -14
- package/dist/extensions.d.ts +0 -51
- package/dist/extensions.js +0 -487
- package/dist/types/files.d.ts +0 -29
- /package/dist/{types/files.js → extensions/types.js} +0 -0
package/dist/services/users.js
CHANGED
|
@@ -6,8 +6,8 @@ import { cloneDeep, isEmpty } from 'lodash-es';
|
|
|
6
6
|
import { performance } from 'perf_hooks';
|
|
7
7
|
import getDatabase from '../database/index.js';
|
|
8
8
|
import env from '../env.js';
|
|
9
|
-
import { ForbiddenError } from '
|
|
10
|
-
import { InvalidPayloadError, RecordNotUniqueError, UnprocessableContentError } from '
|
|
9
|
+
import { ForbiddenError } from '@directus/errors';
|
|
10
|
+
import { InvalidPayloadError, RecordNotUniqueError, UnprocessableContentError } from '@directus/errors';
|
|
11
11
|
import isUrlAllowed from '../utils/is-url-allowed.js';
|
|
12
12
|
import { verifyJWT } from '../utils/jwt.js';
|
|
13
13
|
import { stall } from '../utils/stall.js';
|
|
@@ -274,7 +274,9 @@ export class UsersService extends ItemsService {
|
|
|
274
274
|
catch (err) {
|
|
275
275
|
(opts || (opts = {})).preMutationError = err;
|
|
276
276
|
}
|
|
277
|
+
// Manual constraint, see https://github.com/directus/directus/pull/19912
|
|
277
278
|
await this.knex('directus_notifications').update({ sender: null }).whereIn('sender', keys);
|
|
279
|
+
await this.knex('directus_versions').update({ user_updated: null }).whereIn('user_updated', keys);
|
|
278
280
|
await super.deleteMany(keys, opts);
|
|
279
281
|
return keys;
|
|
280
282
|
}
|
package/dist/services/utils.d.ts
CHANGED
package/dist/services/utils.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { flushCaches, getCache } from '../cache.js';
|
|
1
2
|
import getDatabase from '../database/index.js';
|
|
2
3
|
import { systemCollectionRows } from '../database/system-data/collections/index.js';
|
|
3
4
|
import emitter from '../emitter.js';
|
|
4
|
-
import { ForbiddenError, InvalidPayloadError } from '
|
|
5
|
-
import { getCache } from '../cache.js';
|
|
5
|
+
import { ForbiddenError, InvalidPayloadError } from '@directus/errors';
|
|
6
6
|
import { shouldClearCache } from '../utils/should-clear-cache.js';
|
|
7
7
|
export class UtilsService {
|
|
8
8
|
knex;
|
|
@@ -112,4 +112,10 @@ export class UtilsService {
|
|
|
112
112
|
accountability: this.accountability,
|
|
113
113
|
});
|
|
114
114
|
}
|
|
115
|
+
async clearCache() {
|
|
116
|
+
if (this.accountability?.admin !== true) {
|
|
117
|
+
throw new ForbiddenError();
|
|
118
|
+
}
|
|
119
|
+
return flushCaches(true);
|
|
120
|
+
}
|
|
115
121
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Item, PrimaryKey, Query } from '@directus/types';
|
|
2
|
+
import type { AbstractServiceOptions, MutationOptions } from '../types/index.js';
|
|
3
|
+
import { AuthorizationService } from './authorization.js';
|
|
4
|
+
import { ItemsService } from './items.js';
|
|
5
|
+
export declare class VersionsService extends ItemsService {
|
|
6
|
+
authorizationService: AuthorizationService;
|
|
7
|
+
constructor(options: AbstractServiceOptions);
|
|
8
|
+
private validateCreateData;
|
|
9
|
+
getMainItem(collection: string, item: PrimaryKey, query?: Query): Promise<Item>;
|
|
10
|
+
verifyHash(collection: string, item: PrimaryKey, hash: string): Promise<{
|
|
11
|
+
outdated: boolean;
|
|
12
|
+
mainHash: string;
|
|
13
|
+
}>;
|
|
14
|
+
getVersionSavesById(id: PrimaryKey): Promise<Partial<Item>[]>;
|
|
15
|
+
getVersionSaves(key: string, collection: string, item: string | undefined): Promise<Partial<Item>[] | null>;
|
|
16
|
+
createOne(data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey>;
|
|
17
|
+
createMany(data: Partial<Item>[], opts?: MutationOptions): Promise<PrimaryKey[]>;
|
|
18
|
+
updateMany(keys: PrimaryKey[], data: Partial<Item>, opts?: MutationOptions): Promise<PrimaryKey[]>;
|
|
19
|
+
save(key: PrimaryKey, data: Partial<Item>): Promise<Partial<Item>>;
|
|
20
|
+
promote(version: PrimaryKey, mainHash: string, fields?: string[]): Promise<import("../types/items.js").PrimaryKey>;
|
|
21
|
+
}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import { Action } from '@directus/constants';
|
|
2
|
+
import { InvalidPayloadError, UnprocessableContentError } from '@directus/errors';
|
|
3
|
+
import Joi from 'joi';
|
|
4
|
+
import { assign, pick } from 'lodash-es';
|
|
5
|
+
import objectHash from 'object-hash';
|
|
6
|
+
import getDatabase from '../database/index.js';
|
|
7
|
+
import emitter from '../emitter.js';
|
|
8
|
+
import { ActivityService } from './activity.js';
|
|
9
|
+
import { AuthorizationService } from './authorization.js';
|
|
10
|
+
import { ItemsService } from './items.js';
|
|
11
|
+
import { PayloadService } from './payload.js';
|
|
12
|
+
import { RevisionsService } from './revisions.js';
|
|
13
|
+
export class VersionsService extends ItemsService {
|
|
14
|
+
authorizationService;
|
|
15
|
+
constructor(options) {
|
|
16
|
+
super('directus_versions', options);
|
|
17
|
+
this.authorizationService = new AuthorizationService({
|
|
18
|
+
accountability: this.accountability,
|
|
19
|
+
knex: this.knex,
|
|
20
|
+
schema: this.schema,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
async validateCreateData(data) {
|
|
24
|
+
if (!data['key'])
|
|
25
|
+
throw new InvalidPayloadError({ reason: `"key" is required` });
|
|
26
|
+
// Reserves the "main" version key for the version query parameter
|
|
27
|
+
if (data['key'] === 'main')
|
|
28
|
+
throw new InvalidPayloadError({ reason: `"main" is a reserved version key` });
|
|
29
|
+
if (!data['collection']) {
|
|
30
|
+
throw new InvalidPayloadError({ reason: `"collection" is required` });
|
|
31
|
+
}
|
|
32
|
+
if (!data['item'])
|
|
33
|
+
throw new InvalidPayloadError({ reason: `"item" is required` });
|
|
34
|
+
const { CollectionsService } = await import('./collections.js');
|
|
35
|
+
const collectionsService = new CollectionsService({
|
|
36
|
+
accountability: null,
|
|
37
|
+
knex: this.knex,
|
|
38
|
+
schema: this.schema,
|
|
39
|
+
});
|
|
40
|
+
const existingCollection = await collectionsService.readOne(data['collection']);
|
|
41
|
+
if (!existingCollection.meta?.versioning) {
|
|
42
|
+
throw new UnprocessableContentError({
|
|
43
|
+
reason: `Content Versioning is not enabled for collection "${data['collection']}"`,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
const existingVersions = await super.readByQuery({
|
|
47
|
+
aggregate: { count: ['*'] },
|
|
48
|
+
filter: { key: { _eq: data['key'] }, collection: { _eq: data['collection'] }, item: { _eq: data['item'] } },
|
|
49
|
+
});
|
|
50
|
+
if (existingVersions[0]['count'] > 0) {
|
|
51
|
+
throw new UnprocessableContentError({
|
|
52
|
+
reason: `Version "${data['key']}" already exists for item "${data['item']}" in collection "${data['collection']}"`,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
// will throw an error if the accountability does not have permission to read the item
|
|
56
|
+
await this.authorizationService.checkAccess('read', data['collection'], data['item']);
|
|
57
|
+
}
|
|
58
|
+
async getMainItem(collection, item, query) {
|
|
59
|
+
// will throw an error if the accountability does not have permission to read the item
|
|
60
|
+
await this.authorizationService.checkAccess('read', collection, item);
|
|
61
|
+
const itemsService = new ItemsService(collection, {
|
|
62
|
+
knex: this.knex,
|
|
63
|
+
accountability: this.accountability,
|
|
64
|
+
schema: this.schema,
|
|
65
|
+
});
|
|
66
|
+
return await itemsService.readOne(item, query);
|
|
67
|
+
}
|
|
68
|
+
async verifyHash(collection, item, hash) {
|
|
69
|
+
const mainItem = await this.getMainItem(collection, item);
|
|
70
|
+
const mainHash = objectHash(mainItem);
|
|
71
|
+
return { outdated: hash !== mainHash, mainHash };
|
|
72
|
+
}
|
|
73
|
+
async getVersionSavesById(id) {
|
|
74
|
+
const revisionsService = new RevisionsService({
|
|
75
|
+
knex: this.knex,
|
|
76
|
+
schema: this.schema,
|
|
77
|
+
});
|
|
78
|
+
const result = await revisionsService.readByQuery({
|
|
79
|
+
filter: { version: { _eq: id } },
|
|
80
|
+
});
|
|
81
|
+
return result.map((revision) => revision['delta']);
|
|
82
|
+
}
|
|
83
|
+
async getVersionSaves(key, collection, item) {
|
|
84
|
+
const filter = {
|
|
85
|
+
key: { _eq: key },
|
|
86
|
+
collection: { _eq: collection },
|
|
87
|
+
};
|
|
88
|
+
if (item) {
|
|
89
|
+
filter['item'] = { _eq: item };
|
|
90
|
+
}
|
|
91
|
+
const versions = await this.readByQuery({ filter });
|
|
92
|
+
if (!versions?.[0])
|
|
93
|
+
return null;
|
|
94
|
+
const saves = await this.getVersionSavesById(versions[0]['id']);
|
|
95
|
+
return saves;
|
|
96
|
+
}
|
|
97
|
+
async createOne(data, opts) {
|
|
98
|
+
await this.validateCreateData(data);
|
|
99
|
+
const mainItem = await this.getMainItem(data['collection'], data['item']);
|
|
100
|
+
data['hash'] = objectHash(mainItem);
|
|
101
|
+
return super.createOne(data, opts);
|
|
102
|
+
}
|
|
103
|
+
async createMany(data, opts) {
|
|
104
|
+
if (!Array.isArray(data)) {
|
|
105
|
+
throw new InvalidPayloadError({ reason: 'Input should be an array of items' });
|
|
106
|
+
}
|
|
107
|
+
const keyCombos = new Set();
|
|
108
|
+
for (const item of data) {
|
|
109
|
+
await this.validateCreateData(item);
|
|
110
|
+
const keyCombo = `${item['key']}-${item['collection']}-${item['item']}`;
|
|
111
|
+
if (keyCombos.has(keyCombo)) {
|
|
112
|
+
throw new UnprocessableContentError({
|
|
113
|
+
reason: `Cannot create multiple versions on "${item['item']}" in collection "${item['collection']}" with the same key "${item['key']}"`,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
keyCombos.add(keyCombo);
|
|
117
|
+
const mainItem = await this.getMainItem(item['collection'], item['item']);
|
|
118
|
+
item['hash'] = objectHash(mainItem);
|
|
119
|
+
}
|
|
120
|
+
return super.createMany(data, opts);
|
|
121
|
+
}
|
|
122
|
+
async updateMany(keys, data, opts) {
|
|
123
|
+
// Only allow updates on "key" and "name" fields
|
|
124
|
+
const versionUpdateSchema = Joi.object({
|
|
125
|
+
key: Joi.string(),
|
|
126
|
+
name: Joi.string().allow(null).optional(),
|
|
127
|
+
});
|
|
128
|
+
const { error } = versionUpdateSchema.validate(data);
|
|
129
|
+
if (error)
|
|
130
|
+
throw new InvalidPayloadError({ reason: error.message });
|
|
131
|
+
if ('key' in data) {
|
|
132
|
+
// Reserves the "main" version key for the version query parameter
|
|
133
|
+
if (data['key'] === 'main')
|
|
134
|
+
throw new InvalidPayloadError({ reason: `"main" is a reserved version key` });
|
|
135
|
+
const keyCombos = new Set();
|
|
136
|
+
for (const pk of keys) {
|
|
137
|
+
const { collection, item } = await this.readOne(pk, { fields: ['collection', 'item'] });
|
|
138
|
+
const keyCombo = `${data['key']}-${collection}-${item}`;
|
|
139
|
+
if (keyCombos.has(keyCombo)) {
|
|
140
|
+
throw new UnprocessableContentError({
|
|
141
|
+
reason: `Cannot update multiple versions on "${item}" in collection "${collection}" to the same key "${data['key']}"`,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
keyCombos.add(keyCombo);
|
|
145
|
+
const existingVersions = await super.readByQuery({
|
|
146
|
+
aggregate: { count: ['*'] },
|
|
147
|
+
filter: { id: { _neq: pk }, key: { _eq: data['key'] }, collection: { _eq: collection }, item: { _eq: item } },
|
|
148
|
+
});
|
|
149
|
+
if (existingVersions[0]['count'] > 0) {
|
|
150
|
+
throw new UnprocessableContentError({
|
|
151
|
+
reason: `Version "${data['key']}" already exists for item "${item}" in collection "${collection}"`,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return super.updateMany(keys, data, opts);
|
|
157
|
+
}
|
|
158
|
+
async save(key, data) {
|
|
159
|
+
const version = await super.readOne(key);
|
|
160
|
+
const payloadService = new PayloadService(this.collection, {
|
|
161
|
+
accountability: this.accountability,
|
|
162
|
+
knex: this.knex,
|
|
163
|
+
schema: this.schema,
|
|
164
|
+
});
|
|
165
|
+
const activityService = new ActivityService({
|
|
166
|
+
knex: this.knex,
|
|
167
|
+
schema: this.schema,
|
|
168
|
+
});
|
|
169
|
+
const revisionsService = new RevisionsService({
|
|
170
|
+
knex: this.knex,
|
|
171
|
+
schema: this.schema,
|
|
172
|
+
});
|
|
173
|
+
const activity = await activityService.createOne({
|
|
174
|
+
action: Action.VERSION_SAVE,
|
|
175
|
+
user: this.accountability?.user ?? null,
|
|
176
|
+
collection: version['collection'],
|
|
177
|
+
ip: this.accountability?.ip ?? null,
|
|
178
|
+
user_agent: this.accountability?.userAgent ?? null,
|
|
179
|
+
origin: this.accountability?.origin ?? null,
|
|
180
|
+
item: version['item'],
|
|
181
|
+
});
|
|
182
|
+
const revisionDelta = await payloadService.prepareDelta(data);
|
|
183
|
+
await revisionsService.createOne({
|
|
184
|
+
activity,
|
|
185
|
+
version: key,
|
|
186
|
+
collection: version['collection'],
|
|
187
|
+
item: version['item'],
|
|
188
|
+
data: revisionDelta,
|
|
189
|
+
delta: revisionDelta,
|
|
190
|
+
});
|
|
191
|
+
return data;
|
|
192
|
+
}
|
|
193
|
+
async promote(version, mainHash, fields) {
|
|
194
|
+
const { id, collection, item } = (await this.readOne(version));
|
|
195
|
+
// will throw an error if the accountability does not have permission to update the item
|
|
196
|
+
await this.authorizationService.checkAccess('update', collection, item);
|
|
197
|
+
const { outdated } = await this.verifyHash(collection, item, mainHash);
|
|
198
|
+
if (outdated) {
|
|
199
|
+
throw new UnprocessableContentError({
|
|
200
|
+
reason: `Main item has changed since this version was last updated`,
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
const saves = await this.getVersionSavesById(id);
|
|
204
|
+
const versionResult = assign({}, ...saves);
|
|
205
|
+
const payloadToUpdate = fields ? pick(versionResult, fields) : versionResult;
|
|
206
|
+
const itemsService = new ItemsService(collection, {
|
|
207
|
+
accountability: this.accountability,
|
|
208
|
+
schema: this.schema,
|
|
209
|
+
});
|
|
210
|
+
const payloadAfterHooks = await emitter.emitFilter(['items.promote', `${collection}.items.promote`], payloadToUpdate, {
|
|
211
|
+
collection,
|
|
212
|
+
item,
|
|
213
|
+
version,
|
|
214
|
+
}, {
|
|
215
|
+
database: getDatabase(),
|
|
216
|
+
schema: this.schema,
|
|
217
|
+
accountability: this.accountability,
|
|
218
|
+
});
|
|
219
|
+
const updatedItemKey = await itemsService.updateOne(item, payloadAfterHooks);
|
|
220
|
+
emitter.emitAction(['items.promote', `${collection}.items.promote`], {
|
|
221
|
+
payload: payloadAfterHooks,
|
|
222
|
+
collection,
|
|
223
|
+
item: updatedItemKey,
|
|
224
|
+
version,
|
|
225
|
+
}, {
|
|
226
|
+
database: getDatabase(),
|
|
227
|
+
schema: this.schema,
|
|
228
|
+
accountability: this.accountability,
|
|
229
|
+
});
|
|
230
|
+
return updatedItemKey;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
import { getWebSocketController } from '../websocket/controllers/index.js';
|
|
2
|
+
import { ServiceUnavailableError } from '@directus/errors';
|
|
3
|
+
import { toBoolean } from '../utils/to-boolean.js';
|
|
2
4
|
import emitter from '../emitter.js';
|
|
5
|
+
import env from '../env.js';
|
|
3
6
|
export class WebSocketService {
|
|
4
7
|
controller;
|
|
5
8
|
constructor() {
|
|
6
|
-
|
|
9
|
+
if (!toBoolean(env['WEBSOCKETS_ENABLED']) || !toBoolean(env['WEBSOCKETS_REST_ENABLED'])) {
|
|
10
|
+
throw new ServiceUnavailableError({ service: 'ws', reason: 'WebSocket server is disabled' });
|
|
11
|
+
}
|
|
12
|
+
const controller = getWebSocketController();
|
|
13
|
+
if (!controller) {
|
|
14
|
+
throw new ServiceUnavailableError({ service: 'ws', reason: 'WebSocket server is not initialized' });
|
|
15
|
+
}
|
|
16
|
+
this.controller = controller;
|
|
7
17
|
}
|
|
8
18
|
on(event, callback) {
|
|
9
19
|
emitter.onAction('websocket.' + event, callback);
|
package/dist/types/index.d.ts
CHANGED
package/dist/types/index.js
CHANGED
|
@@ -18,7 +18,7 @@ export type ColumnSortRecord = {
|
|
|
18
18
|
order: 'asc' | 'desc';
|
|
19
19
|
column: string;
|
|
20
20
|
};
|
|
21
|
-
export declare function applySort(knex: Knex, schema: SchemaOverview, rootQuery: Knex.QueryBuilder,
|
|
21
|
+
export declare function applySort(knex: Knex, schema: SchemaOverview, rootQuery: Knex.QueryBuilder, query: Query, collection: string, aliasMap: AliasMap, returnRecords?: boolean): {
|
|
22
22
|
sortRecords: {
|
|
23
23
|
order: "asc" | "desc";
|
|
24
24
|
column: any;
|
|
@@ -3,7 +3,7 @@ import { clone, isPlainObject } from 'lodash-es';
|
|
|
3
3
|
import { customAlphabet } from 'nanoid/non-secure';
|
|
4
4
|
import validate from 'uuid-validate';
|
|
5
5
|
import { getHelpers } from '../database/helpers/index.js';
|
|
6
|
-
import { InvalidQueryError } from '
|
|
6
|
+
import { InvalidQueryError } from '@directus/errors';
|
|
7
7
|
import { getColumnPath } from './get-column-path.js';
|
|
8
8
|
import { getColumn } from './get-column.js';
|
|
9
9
|
import { getRelationInfo } from './get-relation-info.js';
|
|
@@ -24,7 +24,7 @@ export default function applyQuery(knex, collection, dbQuery, query, schema, opt
|
|
|
24
24
|
applyOffset(knex, dbQuery, query.limit * (query.page - 1));
|
|
25
25
|
}
|
|
26
26
|
if (query.sort && !options?.isInnerQuery && !options?.hasMultiRelationalSort) {
|
|
27
|
-
const sortResult = applySort(knex, schema, dbQuery, query
|
|
27
|
+
const sortResult = applySort(knex, schema, dbQuery, query, collection, aliasMap);
|
|
28
28
|
if (!hasJoins) {
|
|
29
29
|
hasJoins = sortResult.hasJoins;
|
|
30
30
|
}
|
|
@@ -128,7 +128,9 @@ function addJoin({ path, collection, aliasMap, rootQuery, schema, relations, kne
|
|
|
128
128
|
}
|
|
129
129
|
}
|
|
130
130
|
}
|
|
131
|
-
export function applySort(knex, schema, rootQuery,
|
|
131
|
+
export function applySort(knex, schema, rootQuery, query, collection, aliasMap, returnRecords = false) {
|
|
132
|
+
const rootSort = query.sort;
|
|
133
|
+
const aggregate = query?.aggregate;
|
|
132
134
|
const relations = schema.relations;
|
|
133
135
|
let hasJoins = false;
|
|
134
136
|
let hasMultiRelationalSort = false;
|
|
@@ -141,6 +143,32 @@ export function applySort(knex, schema, rootQuery, rootSort, collection, aliasMa
|
|
|
141
143
|
if (column[0].startsWith('-')) {
|
|
142
144
|
column[0] = column[0].substring(1);
|
|
143
145
|
}
|
|
146
|
+
// Is the column name one of the aggregate functions used in the query if there is any?
|
|
147
|
+
if (Object.keys(aggregate ?? {}).includes(column[0])) {
|
|
148
|
+
// If so, return the column name without the order prefix
|
|
149
|
+
const operation = column[0];
|
|
150
|
+
// Get the field for the aggregate function
|
|
151
|
+
const field = column[1];
|
|
152
|
+
// If the operation is countAll there is no field.
|
|
153
|
+
if (operation === 'countAll') {
|
|
154
|
+
return {
|
|
155
|
+
order,
|
|
156
|
+
column: 'countAll',
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
// If the operation is a root count there is no field.
|
|
160
|
+
if (operation === 'count' && (field === '*' || !field)) {
|
|
161
|
+
return {
|
|
162
|
+
order,
|
|
163
|
+
column: 'count',
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
// Return the column name with the operation and field name
|
|
167
|
+
return {
|
|
168
|
+
order,
|
|
169
|
+
column: returnRecords ? column[0] : `${operation}->${field}`,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
144
172
|
if (column.length === 1) {
|
|
145
173
|
const pathRoot = column[0].split(':')[0];
|
|
146
174
|
const { relation, relationType } = getRelationInfo(relations, collection, pathRoot);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function deleteFromRequireCache(modulePath: string): void;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import getDatabase from '../database/index.js';
|
|
2
2
|
import env from '../env.js';
|
|
3
|
-
import { InvalidCredentialsError } from '
|
|
3
|
+
import { InvalidCredentialsError } from '@directus/errors';
|
|
4
4
|
import isDirectusJWT from './is-directus-jwt.js';
|
|
5
5
|
import { verifyAccessJWT } from './jwt.js';
|
|
6
6
|
export async function getAccountabilityForToken(token, accountability) {
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import { REGEX_BETWEEN_PARENS } from '@directus/constants';
|
|
5
5
|
import { cloneDeep, isEmpty, mapKeys, omitBy, uniq } from 'lodash-es';
|
|
6
|
-
import { getRelationType } from '
|
|
6
|
+
import { getRelationType } from './get-relation-type.js';
|
|
7
7
|
export default async function getASTFromQuery(collection, query, schema, options) {
|
|
8
8
|
query = cloneDeep(query);
|
|
9
9
|
const accountability = options?.accountability;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { InvalidQueryError } from '
|
|
1
|
+
import { InvalidQueryError } from '@directus/errors';
|
|
2
2
|
import { getRelationInfo } from './get-relation-info.js';
|
|
3
3
|
/**
|
|
4
4
|
* Converts a Directus field list path to the correct SQL names based on the constructed alias map.
|
package/dist/utils/get-column.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { REGEX_BETWEEN_PARENS } from '@directus/constants';
|
|
2
2
|
import { getFunctionsForType } from '@directus/utils';
|
|
3
3
|
import { getFunctions } from '../database/helpers/index.js';
|
|
4
|
-
import { InvalidQueryError } from '
|
|
4
|
+
import { InvalidQueryError } from '@directus/errors';
|
|
5
5
|
import { applyFunctionToColumnName } from './apply-function-to-column-name.js';
|
|
6
6
|
/**
|
|
7
7
|
* Return column prefixed by table. If column includes functions (like `year(date_created)`), the
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type { SchemaOverview } from '@directus/schema
|
|
2
|
-
import type { Column } from '@directus/schema';
|
|
1
|
+
import type { Column, SchemaOverview } from '@directus/schema';
|
|
3
2
|
import type { FieldMeta } from '@directus/types';
|
|
4
3
|
export default function getDefaultValue(column: SchemaOverview[string]['columns'][string] | Column, field?: {
|
|
5
4
|
special?: FieldMeta['special'];
|
|
@@ -8,7 +8,7 @@ import env from '../env.js';
|
|
|
8
8
|
import logger from '../logger.js';
|
|
9
9
|
import { RolesService } from '../services/roles.js';
|
|
10
10
|
import { UsersService } from '../services/users.js';
|
|
11
|
-
import { mergePermissions } from '
|
|
11
|
+
import { mergePermissions } from './merge-permissions.js';
|
|
12
12
|
import { mergePermissionsForShare } from './merge-permissions-for-share.js';
|
|
13
13
|
export async function getPermissions(accountability, schema) {
|
|
14
14
|
const database = getDatabase();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ActivityService, DashboardsService, FilesService, FlowsService, FoldersService, ItemsService, NotificationsService, OperationsService, PanelsService, PermissionsService, PresetsService, RevisionsService, RolesService, SettingsService, SharesService, UsersService, WebhooksService, } from '../services/index.js';
|
|
1
|
+
import { ActivityService, DashboardsService, FilesService, FlowsService, FoldersService, ItemsService, NotificationsService, OperationsService, PanelsService, PermissionsService, PresetsService, RevisionsService, RolesService, SettingsService, SharesService, UsersService, VersionsService, WebhooksService, } from '../services/index.js';
|
|
2
2
|
/**
|
|
3
3
|
* Select the correct service for the given collection. This allows the individual services to run
|
|
4
4
|
* their custom checks (f.e. it allows UsersService to prevent updating TFA secret from outside)
|
|
@@ -43,6 +43,8 @@ export function getService(collection, opts) {
|
|
|
43
43
|
return new UsersService(opts);
|
|
44
44
|
case 'directus_webhooks':
|
|
45
45
|
return new WebhooksService(opts);
|
|
46
|
+
case 'directus_versions':
|
|
47
|
+
return new VersionsService(opts);
|
|
46
48
|
default:
|
|
47
49
|
return new ItemsService(collection, opts);
|
|
48
50
|
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { dirname } from 'node:path';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { pathToRelativeUrl } from '@directus/utils/node';
|
|
4
|
+
export function importFileUrl(url, root, options = {}) {
|
|
5
|
+
return import(`./${pathToRelativeUrl(url, dirname(fileURLToPath(root)))}${options.fresh ? `?t=${Date.now()}` : ''}`);
|
|
6
|
+
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
type
|
|
1
|
+
import type { PromiseCallback } from '@directus/types';
|
|
2
2
|
export declare class JobQueue {
|
|
3
3
|
private running;
|
|
4
4
|
private jobs;
|
|
5
5
|
constructor();
|
|
6
|
-
enqueue(job:
|
|
6
|
+
enqueue(job: PromiseCallback): void;
|
|
7
7
|
private run;
|
|
8
8
|
}
|
|
9
|
-
export {};
|
package/dist/utils/jwt.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import jwt from 'jsonwebtoken';
|
|
2
|
-
import { InvalidTokenError, ServiceUnavailableError, TokenExpiredError } from '
|
|
2
|
+
import { InvalidTokenError, ServiceUnavailableError, TokenExpiredError } from '@directus/errors';
|
|
3
3
|
export function verifyJWT(token, secret) {
|
|
4
4
|
let payload;
|
|
5
5
|
try {
|
|
@@ -12,7 +12,6 @@ import { isObject } from '@directus/utils';
|
|
|
12
12
|
export function redactObject(input, redact, replacement) {
|
|
13
13
|
const wildcardChars = ['*', '**'];
|
|
14
14
|
const clone = JSON.parse(JSON.stringify(input, getReplacer(replacement, redact.values)));
|
|
15
|
-
const visited = new WeakSet();
|
|
16
15
|
if (redact.keys) {
|
|
17
16
|
traverse(clone, redact.keys);
|
|
18
17
|
}
|
|
@@ -21,7 +20,6 @@ export function redactObject(input, redact, replacement) {
|
|
|
21
20
|
if (checkKeyPaths.length === 0) {
|
|
22
21
|
return;
|
|
23
22
|
}
|
|
24
|
-
visited.add(object);
|
|
25
23
|
const REDACTED_TEXT = replacement();
|
|
26
24
|
const globalCheckPaths = [];
|
|
27
25
|
for (const key of Object.keys(object)) {
|
|
@@ -73,7 +71,7 @@ export function redactObject(input, redact, replacement) {
|
|
|
73
71
|
}
|
|
74
72
|
}
|
|
75
73
|
const value = object[key];
|
|
76
|
-
if (isObject(value)
|
|
74
|
+
if (isObject(value)) {
|
|
77
75
|
traverse(value, [...globalCheckPaths, ...localCheckPaths]);
|
|
78
76
|
}
|
|
79
77
|
}
|
|
@@ -86,7 +84,15 @@ export function getReplacer(replacement, values) {
|
|
|
86
84
|
const filteredValues = values
|
|
87
85
|
? Object.entries(values).filter(([_k, v]) => typeof v === 'string' && v.length > 0)
|
|
88
86
|
: [];
|
|
87
|
+
const seen = new WeakSet();
|
|
89
88
|
return (_key, value) => {
|
|
89
|
+
// Skip circular values
|
|
90
|
+
if (isObject(value)) {
|
|
91
|
+
if (seen.has(value)) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
seen.add(value);
|
|
95
|
+
}
|
|
90
96
|
if (value instanceof Error) {
|
|
91
97
|
return {
|
|
92
98
|
name: value.name,
|
|
@@ -46,6 +46,9 @@ export function sanitizeQuery(rawQuery, accountability) {
|
|
|
46
46
|
if (rawQuery['search'] && typeof rawQuery['search'] === 'string') {
|
|
47
47
|
query.search = rawQuery['search'];
|
|
48
48
|
}
|
|
49
|
+
if (rawQuery['version']) {
|
|
50
|
+
query.version = rawQuery['version'];
|
|
51
|
+
}
|
|
49
52
|
if (rawQuery['export']) {
|
|
50
53
|
query.export = rawQuery['export'];
|
|
51
54
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type { File
|
|
1
|
+
import type { File } from '@directus/types';
|
|
2
|
+
import type { Transformation, TransformationSet } from '../types/index.js';
|
|
2
3
|
export declare function resolvePreset({ transformationParams, acceptFormat }: TransformationSet, file: File): Transformation[];
|
|
3
4
|
/**
|
|
4
5
|
* Try to extract a file format from an array of `Transformation`'s.
|
|
@@ -2,7 +2,7 @@ import Joi from 'joi';
|
|
|
2
2
|
import { isPlainObject, uniq } from 'lodash-es';
|
|
3
3
|
import { stringify } from 'wellknown';
|
|
4
4
|
import env from '../env.js';
|
|
5
|
-
import { InvalidQueryError } from '
|
|
5
|
+
import { InvalidQueryError } from '@directus/errors';
|
|
6
6
|
import { calculateFieldDepth } from './calculate-field-depth.js';
|
|
7
7
|
const querySchema = Joi.object({
|
|
8
8
|
fields: Joi.array().items(Joi.string()),
|
|
@@ -17,6 +17,7 @@ const querySchema = Joi.object({
|
|
|
17
17
|
meta: Joi.array().items(Joi.string().valid('total_count', 'filter_count')),
|
|
18
18
|
search: Joi.string(),
|
|
19
19
|
export: Joi.string().valid('csv', 'json', 'xml', 'yaml'),
|
|
20
|
+
version: Joi.string(),
|
|
20
21
|
aggregate: Joi.object(),
|
|
21
22
|
deep: Joi.object(),
|
|
22
23
|
alias: Joi.object(),
|
|
@@ -2,7 +2,7 @@ import { TYPES } from '@directus/constants';
|
|
|
2
2
|
import Joi from 'joi';
|
|
3
3
|
import { ALIAS_TYPES } from '../constants.js';
|
|
4
4
|
import { getDatabaseClient } from '../database/index.js';
|
|
5
|
-
import { InvalidPayloadError } from '
|
|
5
|
+
import { InvalidPayloadError } from '@directus/errors';
|
|
6
6
|
import { DatabaseClients } from '../types/index.js';
|
|
7
7
|
import { version as currentDirectusVersion } from './package.js';
|
|
8
8
|
const snapshotJoiSchema = Joi.object({
|