@directus/api 15.0.0 → 16.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/app.js +6 -4
- package/dist/auth/drivers/ldap.js +7 -4
- package/dist/auth/drivers/local.js +3 -2
- package/dist/auth/drivers/oauth2.js +9 -2
- package/dist/auth/drivers/openid.js +9 -2
- package/dist/auth/drivers/saml.js +6 -4
- package/dist/auth.js +7 -4
- package/dist/bus/index.d.ts +1 -0
- package/dist/bus/index.js +1 -0
- package/dist/bus/lib/use-bus.d.ts +9 -0
- package/dist/bus/lib/use-bus.js +21 -0
- package/dist/cache.js +9 -9
- package/dist/cli/commands/bootstrap/index.js +6 -2
- package/dist/cli/commands/count/index.js +2 -1
- package/dist/cli/commands/database/install.js +2 -1
- package/dist/cli/commands/database/migrate.js +2 -1
- package/dist/cli/commands/roles/create.js +2 -1
- package/dist/cli/commands/schema/apply.js +2 -1
- package/dist/cli/commands/schema/snapshot.js +6 -5
- package/dist/cli/commands/users/create.js +4 -3
- package/dist/cli/commands/users/passwd.js +5 -4
- package/dist/cli/load-extensions.js +4 -2
- package/dist/cli/utils/create-env/env-stub.liquid +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +4 -1
- package/dist/controllers/assets.js +5 -3
- package/dist/controllers/auth.js +5 -4
- package/dist/controllers/extensions.js +18 -6
- package/dist/controllers/files.js +3 -3
- package/dist/controllers/schema.js +3 -2
- package/dist/controllers/shares.js +3 -3
- package/dist/database/helpers/index.d.ts +1 -1
- package/dist/database/index.js +9 -2
- package/dist/database/migrations/20210518A-add-foreign-key-constraints.js +3 -1
- package/dist/database/migrations/20210519A-add-system-fk-triggers.js +3 -1
- package/dist/database/migrations/20210802A-replace-groups.js +2 -1
- package/dist/database/migrations/20230721A-require-shares-fields.js +2 -1
- package/dist/database/migrations/20231215A-add-focalpoints.d.ts +3 -0
- package/dist/database/migrations/20231215A-add-focalpoints.js +12 -0
- package/dist/database/migrations/run.js +2 -1
- package/dist/database/run-ast.js +5 -2
- package/dist/database/system-data/app-access-permissions/app-access-permissions.yaml +0 -7
- package/dist/database/system-data/fields/files.yaml +16 -0
- package/dist/emitter.js +3 -1
- package/dist/extensions/lib/get-extensions-path.d.ts +1 -1
- package/dist/extensions/lib/get-extensions-path.js +2 -1
- package/dist/extensions/lib/get-extensions.d.ts +1 -1
- package/dist/extensions/lib/get-extensions.js +32 -8
- package/dist/extensions/lib/get-shared-deps-mapping.js +6 -4
- package/dist/extensions/lib/sandbox/register/call-reference.js +4 -2
- package/dist/extensions/lib/sandbox/sdk/generators/log.js +2 -1
- package/dist/extensions/lib/sync-extensions.js +6 -4
- package/dist/extensions/manager.js +43 -19
- package/dist/flows.js +13 -7
- package/dist/logger.d.ts +7 -7
- package/dist/logger.js +116 -92
- package/dist/mailer.js +4 -2
- package/dist/middleware/cache.js +4 -2
- package/dist/middleware/check-ip.js +25 -6
- package/dist/middleware/cors.js +2 -1
- package/dist/middleware/error-handler.js +5 -5
- package/dist/middleware/rate-limiter-global.js +4 -2
- package/dist/middleware/rate-limiter-ip.js +2 -1
- package/dist/middleware/respond.js +4 -2
- package/dist/operations/log/index.js +2 -1
- package/dist/rate-limiter.d.ts +2 -1
- package/dist/rate-limiter.js +5 -2
- package/dist/redis/index.d.ts +3 -2
- package/dist/redis/index.js +3 -2
- package/dist/redis/{create-redis.js → lib/create-redis.js} +2 -2
- package/dist/redis/utils/redis-config-available.d.ts +4 -0
- package/dist/redis/utils/redis-config-available.js +8 -0
- package/dist/request/request-interceptor.js +7 -5
- package/dist/request/response-interceptor.js +2 -2
- package/dist/request/validate-ip.d.ts +1 -1
- package/dist/request/validate-ip.js +23 -7
- package/dist/server.js +11 -7
- package/dist/services/activity.js +5 -4
- package/dist/services/assets.d.ts +2 -0
- package/dist/services/assets.js +9 -4
- package/dist/services/authentication.js +17 -9
- package/dist/services/collections.js +5 -4
- package/dist/services/extensions.d.ts +15 -9
- package/dist/services/extensions.js +74 -39
- package/dist/services/fields.js +9 -4
- package/dist/services/files.d.ts +2 -2
- package/dist/services/files.js +22 -14
- package/dist/services/graphql/index.js +46 -3
- package/dist/services/graphql/subscription.js +2 -2
- package/dist/services/graphql/types/bigint.js +16 -5
- package/dist/services/graphql/utils/process-error.d.ts +4 -1
- package/dist/services/graphql/utils/process-error.js +10 -8
- package/dist/services/import-export/index.js +5 -3
- package/dist/services/items.js +12 -8
- package/dist/services/mail/index.js +4 -2
- package/dist/services/notifications.js +7 -3
- package/dist/services/relations.js +19 -10
- package/dist/services/server.js +5 -4
- package/dist/services/shares.js +3 -2
- package/dist/services/specifications.js +2 -1
- package/dist/services/users.js +20 -9
- package/dist/services/versions.js +6 -5
- package/dist/services/webhooks.d.ts +2 -2
- package/dist/services/webhooks.js +2 -2
- package/dist/services/websocket.d.ts +1 -1
- package/dist/services/websocket.js +4 -3
- package/dist/storage/register-drivers.js +2 -1
- package/dist/storage/register-locations.js +2 -1
- package/dist/synchronization.js +3 -1
- package/dist/telemetry/lib/get-report.js +1 -1
- package/dist/telemetry/lib/init-telemetry.js +2 -2
- package/dist/telemetry/lib/send-report.js +1 -1
- package/dist/telemetry/lib/track.js +2 -3
- package/dist/telemetry/utils/get-user-count.js +1 -1
- package/dist/types/assets.d.ts +2 -0
- package/dist/utils/apply-diff.js +2 -1
- package/dist/utils/apply-query.js +0 -11
- package/dist/utils/delete-from-require-cache.js +2 -1
- package/dist/utils/get-accountability-for-token.js +3 -2
- package/dist/utils/get-auth-providers.js +2 -1
- package/dist/utils/get-cache-headers.js +5 -2
- package/dist/utils/get-config-from-env.js +2 -1
- package/dist/utils/get-default-value.js +4 -3
- package/dist/utils/get-ip-from-req.js +4 -2
- package/dist/utils/get-permissions.js +5 -3
- package/dist/utils/get-schema.js +5 -2
- package/dist/utils/get-snapshot-diff.js +7 -9
- package/dist/utils/get-snapshot.js +4 -4
- package/dist/utils/ip-in-networks.d.ts +6 -0
- package/dist/utils/ip-in-networks.js +13 -0
- package/dist/utils/is-url-allowed.js +2 -1
- package/dist/utils/job-queue.d.ts +1 -0
- package/dist/utils/job-queue.js +3 -0
- package/dist/utils/sanitize-query.js +7 -2
- package/dist/utils/sanitize-schema.d.ts +1 -1
- package/dist/utils/should-clear-cache.js +2 -1
- package/dist/utils/should-skip-cache.js +2 -1
- package/dist/utils/transformations.js +95 -12
- package/dist/utils/validate-env.js +4 -2
- package/dist/utils/validate-query.js +7 -3
- package/dist/utils/validate-storage.js +4 -2
- package/dist/webhooks.js +4 -3
- package/dist/websocket/controllers/base.js +12 -6
- package/dist/websocket/controllers/graphql.js +4 -2
- package/dist/websocket/controllers/hooks.js +3 -2
- package/dist/websocket/controllers/index.js +4 -2
- package/dist/websocket/controllers/rest.js +4 -2
- package/dist/websocket/errors.js +2 -1
- package/dist/websocket/handlers/heartbeat.js +4 -3
- package/dist/websocket/handlers/subscribe.d.ts +2 -2
- package/dist/websocket/handlers/subscribe.js +5 -4
- package/package.json +57 -57
- package/dist/__utils__/items-utils.d.ts +0 -2
- package/dist/__utils__/items-utils.js +0 -31
- package/dist/__utils__/mock-env.d.ts +0 -18
- package/dist/__utils__/mock-env.js +0 -41
- package/dist/__utils__/schemas.d.ts +0 -13
- package/dist/__utils__/schemas.js +0 -301
- package/dist/__utils__/snapshots.d.ts +0 -5
- package/dist/__utils__/snapshots.js +0 -903
- package/dist/env.d.ts +0 -14
- package/dist/env.js +0 -511
- package/dist/messenger.d.ts +0 -24
- package/dist/messenger.js +0 -64
- package/dist/utils/to-boolean.d.ts +0 -4
- package/dist/utils/to-boolean.js +0 -6
- /package/dist/redis/{create-redis.d.ts → lib/create-redis.d.ts} +0 -0
- /package/dist/redis/{use-redis.d.ts → lib/use-redis.d.ts} +0 -0
- /package/dist/redis/{use-redis.js → lib/use-redis.js} +0 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { clamp } from 'lodash-es';
|
|
1
2
|
export function resolvePreset({ transformationParams, acceptFormat }, file) {
|
|
2
3
|
const transforms = transformationParams.transforms ? [...transformationParams.transforms] : [];
|
|
3
4
|
if (transformationParams.format || transformationParams.quality) {
|
|
@@ -9,18 +10,54 @@ export function resolvePreset({ transformationParams, acceptFormat }, file) {
|
|
|
9
10
|
},
|
|
10
11
|
]);
|
|
11
12
|
}
|
|
12
|
-
if (transformationParams.width || transformationParams.height) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
13
|
+
if ((transformationParams.width || transformationParams.height) && file.width && file.height) {
|
|
14
|
+
const toWidth = transformationParams.width ? Number(transformationParams.width) : undefined;
|
|
15
|
+
const toHeight = transformationParams.height ? Number(transformationParams.height) : undefined;
|
|
16
|
+
const toFocalPointX = transformationParams.focal_point_x
|
|
17
|
+
? Number(transformationParams.focal_point_x)
|
|
18
|
+
: file.focal_point_x;
|
|
19
|
+
const toFocalPointY = transformationParams.focal_point_y
|
|
20
|
+
? Number(transformationParams.focal_point_y)
|
|
21
|
+
: file.focal_point_y;
|
|
22
|
+
/*
|
|
23
|
+
* Focal point cropping only works with a fixed size (width x height) when `cover`ing,
|
|
24
|
+
* since the other modes show the whole image. Sharp by default also simply scales up/down
|
|
25
|
+
* when only supplied with one dimension, so we **must** check, else we break existing behaviour.
|
|
26
|
+
* See: https://sharp.pixelplumbing.com/api-resize#resize
|
|
27
|
+
* Also only crop to focal point when explicitly defined so that users can still `cover` with
|
|
28
|
+
* other parameters like `position` and `gravity` - Else fall back to regular behaviour
|
|
29
|
+
*/
|
|
30
|
+
if ((transformationParams.fit === undefined || transformationParams.fit === 'cover') &&
|
|
31
|
+
toWidth &&
|
|
32
|
+
toHeight &&
|
|
33
|
+
toFocalPointX !== null &&
|
|
34
|
+
toFocalPointY !== null) {
|
|
35
|
+
const transformArgs = getResizeArguments({ w: file.width, h: file.height }, { w: toWidth, h: toHeight }, { x: toFocalPointX, y: toFocalPointY });
|
|
36
|
+
transforms.push([
|
|
37
|
+
'resize',
|
|
38
|
+
{
|
|
39
|
+
width: transformArgs.width,
|
|
40
|
+
height: transformArgs.height,
|
|
41
|
+
fit: transformationParams.fit,
|
|
42
|
+
withoutEnlargement: transformationParams.withoutEnlargement
|
|
43
|
+
? Boolean(transformationParams.withoutEnlargement)
|
|
44
|
+
: undefined,
|
|
45
|
+
},
|
|
46
|
+
], ['extract', transformArgs.region]);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
transforms.push([
|
|
50
|
+
'resize',
|
|
51
|
+
{
|
|
52
|
+
width: toWidth,
|
|
53
|
+
height: toHeight,
|
|
54
|
+
fit: transformationParams.fit,
|
|
55
|
+
withoutEnlargement: transformationParams.withoutEnlargement
|
|
56
|
+
? Boolean(transformationParams.withoutEnlargement)
|
|
57
|
+
: undefined,
|
|
58
|
+
},
|
|
59
|
+
]);
|
|
60
|
+
}
|
|
24
61
|
}
|
|
25
62
|
return transforms;
|
|
26
63
|
}
|
|
@@ -47,3 +84,49 @@ export function maybeExtractFormat(transforms) {
|
|
|
47
84
|
const lastToFormat = toFormats[toFormats.length - 1];
|
|
48
85
|
return lastToFormat ? lastToFormat[1]?.toString() : undefined;
|
|
49
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* Resize an image but keep it centered on the focal point.
|
|
89
|
+
* Based on the method outlined in https://github.com/lovell/sharp/issues/1198#issuecomment-384591756
|
|
90
|
+
*/
|
|
91
|
+
function getResizeArguments(original, target, focalPoint) {
|
|
92
|
+
const { width, height, factor } = getIntermediateDimensions(original, target);
|
|
93
|
+
const region = getExtractionRegion(factor, focalPoint ?? { x: original.w / 2, y: original.h / 2 }, target, {
|
|
94
|
+
w: width,
|
|
95
|
+
h: height,
|
|
96
|
+
});
|
|
97
|
+
return { width, height, region };
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Calculates the dimensions of the intermediate (resized) image.
|
|
101
|
+
*/
|
|
102
|
+
function getIntermediateDimensions(original, target) {
|
|
103
|
+
const hRatio = original.h / target.h;
|
|
104
|
+
const wRatio = original.w / target.w;
|
|
105
|
+
let factor;
|
|
106
|
+
let width;
|
|
107
|
+
let height;
|
|
108
|
+
if (hRatio < wRatio) {
|
|
109
|
+
factor = hRatio;
|
|
110
|
+
height = Math.round(target.h);
|
|
111
|
+
width = Math.round(original.w / factor);
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
factor = wRatio;
|
|
115
|
+
width = Math.round(target.w);
|
|
116
|
+
height = Math.round(original.h / factor);
|
|
117
|
+
}
|
|
118
|
+
return { width, height, factor };
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Calculates the Region to extract from the intermediate image.
|
|
122
|
+
*/
|
|
123
|
+
function getExtractionRegion(factor, focalPoint, target, intermediate) {
|
|
124
|
+
const newXCenter = focalPoint.x / factor;
|
|
125
|
+
const newYCenter = focalPoint.y / factor;
|
|
126
|
+
return {
|
|
127
|
+
left: clamp(Math.round(newXCenter - target.w / 2), 0, intermediate.w - target.w),
|
|
128
|
+
top: clamp(Math.round(newYCenter - target.h / 2), 0, intermediate.h - target.h),
|
|
129
|
+
width: target.w,
|
|
130
|
+
height: target.h,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import { useEnv } from '@directus/env';
|
|
2
|
+
import { useLogger } from '../logger.js';
|
|
3
3
|
export function validateEnv(requiredKeys) {
|
|
4
|
+
const env = useEnv();
|
|
5
|
+
const logger = useLogger();
|
|
4
6
|
for (const requiredKey of requiredKeys) {
|
|
5
7
|
if (requiredKey in env === false) {
|
|
6
8
|
logger.error(`"${requiredKey}" Environment Variable is missing.`);
|
|
@@ -1,16 +1,20 @@
|
|
|
1
|
+
import { useEnv } from '@directus/env';
|
|
2
|
+
import { InvalidQueryError } from '@directus/errors';
|
|
1
3
|
import Joi from 'joi';
|
|
2
4
|
import { isPlainObject, uniq } from 'lodash-es';
|
|
3
5
|
import { stringify } from 'wellknown';
|
|
4
|
-
import env from '../env.js';
|
|
5
|
-
import { InvalidQueryError } from '@directus/errors';
|
|
6
6
|
import { calculateFieldDepth } from './calculate-field-depth.js';
|
|
7
|
+
const env = useEnv();
|
|
7
8
|
const querySchema = Joi.object({
|
|
8
9
|
fields: Joi.array().items(Joi.string()),
|
|
9
10
|
group: Joi.array().items(Joi.string()),
|
|
10
11
|
sort: Joi.array().items(Joi.string()),
|
|
11
12
|
filter: Joi.object({}).unknown(),
|
|
12
13
|
limit: 'QUERY_LIMIT_MAX' in env && env['QUERY_LIMIT_MAX'] !== -1
|
|
13
|
-
? Joi.number()
|
|
14
|
+
? Joi.number()
|
|
15
|
+
.integer()
|
|
16
|
+
.min(-1)
|
|
17
|
+
.max(env['QUERY_LIMIT_MAX']) // min should be 0
|
|
14
18
|
: Joi.number().integer().min(-1),
|
|
15
19
|
offset: Joi.number().integer().min(0),
|
|
16
20
|
page: Joi.number().integer().min(0),
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
+
import { useEnv } from '@directus/env';
|
|
1
2
|
import { toArray } from '@directus/utils';
|
|
2
3
|
import { constants } from 'fs';
|
|
3
4
|
import { access } from 'node:fs/promises';
|
|
4
5
|
import path from 'path';
|
|
5
|
-
import env from '../env.js';
|
|
6
6
|
import { getExtensionsPath } from '../extensions/lib/get-extensions-path.js';
|
|
7
|
-
import
|
|
7
|
+
import { useLogger } from '../logger.js';
|
|
8
8
|
export async function validateStorage() {
|
|
9
|
+
const env = useEnv();
|
|
10
|
+
const logger = useLogger();
|
|
9
11
|
if (env['DB_CLIENT'] === 'sqlite3') {
|
|
10
12
|
try {
|
|
11
13
|
await access(path.dirname(env['DB_FILENAME']), constants.R_OK | constants.W_OK);
|
package/dist/webhooks.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import { useBus } from './bus/index.js';
|
|
1
2
|
import getDatabase from './database/index.js';
|
|
2
3
|
import emitter from './emitter.js';
|
|
3
|
-
import
|
|
4
|
-
import { getMessenger } from './messenger.js';
|
|
4
|
+
import { useLogger } from './logger.js';
|
|
5
5
|
import { getAxios } from './request/index.js';
|
|
6
6
|
import { WebhooksService } from './services/webhooks.js';
|
|
7
7
|
import { getSchema } from './utils/get-schema.js';
|
|
@@ -10,7 +10,7 @@ let registered = [];
|
|
|
10
10
|
const reloadQueue = new JobQueue();
|
|
11
11
|
export async function init() {
|
|
12
12
|
await register();
|
|
13
|
-
const messenger =
|
|
13
|
+
const messenger = useBus();
|
|
14
14
|
messenger.subscribe('webhooks', (event) => {
|
|
15
15
|
if (event['type'] === 'reload') {
|
|
16
16
|
reloadQueue.enqueue(async () => {
|
|
@@ -42,6 +42,7 @@ export function unregister() {
|
|
|
42
42
|
registered = [];
|
|
43
43
|
}
|
|
44
44
|
function createHandler(webhook, event) {
|
|
45
|
+
const logger = useLogger();
|
|
45
46
|
return async (meta, context) => {
|
|
46
47
|
if (webhook.collections.includes(meta['collection']) === false)
|
|
47
48
|
return;
|
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useEnv } from '@directus/env';
|
|
2
|
+
import { InvalidProviderConfigError, TokenExpiredError } from '@directus/errors';
|
|
3
|
+
import { parseJSON, toBoolean } from '@directus/utils';
|
|
2
4
|
import { parse } from 'url';
|
|
3
5
|
import { v4 as uuid } from 'uuid';
|
|
4
6
|
import WebSocket, { WebSocketServer } from 'ws';
|
|
5
7
|
import { fromZodError } from 'zod-validation-error';
|
|
6
8
|
import emitter from '../../emitter.js';
|
|
7
|
-
import
|
|
8
|
-
import { InvalidProviderConfigError, TokenExpiredError } from '@directus/errors';
|
|
9
|
-
import logger from '../../logger.js';
|
|
9
|
+
import { useLogger } from '../../logger.js';
|
|
10
10
|
import { createRateLimiter } from '../../rate-limiter.js';
|
|
11
11
|
import { getAccountabilityForToken } from '../../utils/get-accountability-for-token.js';
|
|
12
|
-
import { toBoolean } from '../../utils/to-boolean.js';
|
|
13
12
|
import { authenticateConnection, authenticationSuccess } from '../authenticate.js';
|
|
14
13
|
import { WebSocketError, handleWebSocketError } from '../errors.js';
|
|
15
14
|
import { AuthMode, WebSocketAuthMessage, WebSocketMessage } from '../messages.js';
|
|
@@ -18,6 +17,7 @@ import { getMessageType } from '../utils/message.js';
|
|
|
18
17
|
import { waitForAnyMessage, waitForMessageType } from '../utils/wait-for-message.js';
|
|
19
18
|
import { registerWebSocketEvents } from './hooks.js';
|
|
20
19
|
const TOKEN_CHECK_INTERVAL = 15 * 60 * 1000; // 15 minutes
|
|
20
|
+
const logger = useLogger();
|
|
21
21
|
export default class SocketController {
|
|
22
22
|
server;
|
|
23
23
|
clients;
|
|
@@ -27,7 +27,11 @@ export default class SocketController {
|
|
|
27
27
|
rateLimiter;
|
|
28
28
|
authInterval;
|
|
29
29
|
constructor(httpServer, configPrefix) {
|
|
30
|
-
this.server = new WebSocketServer({
|
|
30
|
+
this.server = new WebSocketServer({
|
|
31
|
+
noServer: true,
|
|
32
|
+
// @ts-ignore TODO Remove once @types/ws has been updated
|
|
33
|
+
autoPong: false,
|
|
34
|
+
});
|
|
31
35
|
this.clients = new Set();
|
|
32
36
|
this.authInterval = null;
|
|
33
37
|
const { endpoint, authentication, maxConnections } = this.getEnvironmentConfig(configPrefix);
|
|
@@ -40,6 +44,7 @@ export default class SocketController {
|
|
|
40
44
|
registerWebSocketEvents();
|
|
41
45
|
}
|
|
42
46
|
getEnvironmentConfig(configPrefix) {
|
|
47
|
+
const env = useEnv();
|
|
43
48
|
const endpoint = String(env[`${configPrefix}_PATH`]);
|
|
44
49
|
const authMode = AuthMode.safeParse(String(env[`${configPrefix}_AUTH`]).toLowerCase());
|
|
45
50
|
const authTimeout = Number(env[`${configPrefix}_AUTH_TIMEOUT`]) * 1000;
|
|
@@ -60,6 +65,7 @@ export default class SocketController {
|
|
|
60
65
|
};
|
|
61
66
|
}
|
|
62
67
|
getRateLimiter() {
|
|
68
|
+
const env = useEnv();
|
|
63
69
|
if (toBoolean(env['RATE_LIMITER_ENABLED']) === true) {
|
|
64
70
|
return createRateLimiter('RATE_LIMITER', {
|
|
65
71
|
keyPrefix: 'websocket',
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { useEnv } from '@directus/env';
|
|
1
2
|
import { CloseCode, MessageType, makeServer } from 'graphql-ws';
|
|
2
|
-
import
|
|
3
|
-
import logger from '../../logger.js';
|
|
3
|
+
import { useLogger } from '../../logger.js';
|
|
4
4
|
import { bindPubSub } from '../../services/graphql/subscription.js';
|
|
5
5
|
import { GraphQLService } from '../../services/index.js';
|
|
6
6
|
import { getSchema } from '../../utils/get-schema.js';
|
|
@@ -9,10 +9,12 @@ import { handleWebSocketError } from '../errors.js';
|
|
|
9
9
|
import { ConnectionParams, WebSocketMessage } from '../messages.js';
|
|
10
10
|
import { getMessageType } from '../utils/message.js';
|
|
11
11
|
import SocketController from './base.js';
|
|
12
|
+
const logger = useLogger();
|
|
12
13
|
export class GraphQLSubscriptionController extends SocketController {
|
|
13
14
|
gql;
|
|
14
15
|
constructor(httpServer) {
|
|
15
16
|
super(httpServer, 'WEBSOCKETS_GRAPHQL');
|
|
17
|
+
const env = useEnv();
|
|
16
18
|
this.server.on('connection', (ws, auth) => {
|
|
17
19
|
this.bindEvents(this.createClient(ws, auth));
|
|
18
20
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { useBus } from '../../bus/index.js';
|
|
1
2
|
import emitter from '../../emitter.js';
|
|
2
|
-
import { getMessenger } from '../../messenger.js';
|
|
3
3
|
let actionsRegistered = false;
|
|
4
4
|
export function registerWebSocketEvents() {
|
|
5
5
|
if (actionsRegistered)
|
|
@@ -21,6 +21,7 @@ export function registerWebSocketEvents() {
|
|
|
21
21
|
'settings',
|
|
22
22
|
'shares',
|
|
23
23
|
'users',
|
|
24
|
+
'versions',
|
|
24
25
|
'webhooks',
|
|
25
26
|
]);
|
|
26
27
|
registerFieldsHooks();
|
|
@@ -128,7 +129,7 @@ function registerSortHooks() {
|
|
|
128
129
|
* @param transform Transformer function
|
|
129
130
|
*/
|
|
130
131
|
function registerAction(event, transform) {
|
|
131
|
-
const messenger =
|
|
132
|
+
const messenger = useBus();
|
|
132
133
|
emitter.onAction(event, async (data) => {
|
|
133
134
|
// push the event through the Redis pub/sub
|
|
134
135
|
messenger.publish('websocket.event', transform(data));
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { toBoolean } from '
|
|
1
|
+
import { useEnv } from '@directus/env';
|
|
2
|
+
import { toBoolean } from '@directus/utils';
|
|
3
3
|
import { GraphQLSubscriptionController } from './graphql.js';
|
|
4
4
|
import { WebSocketController } from './rest.js';
|
|
5
5
|
let websocketController;
|
|
6
6
|
let subscriptionController;
|
|
7
7
|
export function createWebSocketController(server) {
|
|
8
|
+
const env = useEnv();
|
|
8
9
|
if (toBoolean(env['WEBSOCKETS_REST_ENABLED'])) {
|
|
9
10
|
websocketController = new WebSocketController(server);
|
|
10
11
|
}
|
|
@@ -13,6 +14,7 @@ export function getWebSocketController() {
|
|
|
13
14
|
return websocketController;
|
|
14
15
|
}
|
|
15
16
|
export function createSubscriptionController(server) {
|
|
17
|
+
const env = useEnv();
|
|
16
18
|
if (toBoolean(env['WEBSOCKETS_GRAPHQL_ENABLED'])) {
|
|
17
19
|
subscriptionController = new GraphQLSubscriptionController(server);
|
|
18
20
|
}
|
|
@@ -1,14 +1,16 @@
|
|
|
1
|
+
import { useEnv } from '@directus/env';
|
|
1
2
|
import { parseJSON } from '@directus/utils';
|
|
2
3
|
import emitter from '../../emitter.js';
|
|
3
|
-
import
|
|
4
|
-
import logger from '../../logger.js';
|
|
4
|
+
import { useLogger } from '../../logger.js';
|
|
5
5
|
import { refreshAccountability } from '../authenticate.js';
|
|
6
6
|
import { WebSocketError, handleWebSocketError } from '../errors.js';
|
|
7
7
|
import { WebSocketMessage } from '../messages.js';
|
|
8
8
|
import SocketController from './base.js';
|
|
9
|
+
const logger = useLogger();
|
|
9
10
|
export class WebSocketController extends SocketController {
|
|
10
11
|
constructor(httpServer) {
|
|
11
12
|
super(httpServer, 'WEBSOCKETS_REST');
|
|
13
|
+
const env = useEnv();
|
|
12
14
|
this.server.on('connection', (ws, auth) => {
|
|
13
15
|
this.bindEvents(this.createClient(ws, auth));
|
|
14
16
|
});
|
package/dist/websocket/errors.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { isDirectusError } from '@directus/errors';
|
|
2
2
|
import { ZodError } from 'zod';
|
|
3
3
|
import { fromZodError } from 'zod-validation-error';
|
|
4
|
-
import
|
|
4
|
+
import { useLogger } from '../logger.js';
|
|
5
5
|
export class WebSocketError extends Error {
|
|
6
6
|
type;
|
|
7
7
|
code;
|
|
@@ -38,6 +38,7 @@ export class WebSocketError extends Error {
|
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
export function handleWebSocketError(client, error, type) {
|
|
41
|
+
const logger = useLogger();
|
|
41
42
|
if (isDirectusError(error)) {
|
|
42
43
|
client.send(WebSocketError.fromError(error, type).toMessage());
|
|
43
44
|
return;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import { useEnv } from '@directus/env';
|
|
2
|
+
import { ServiceUnavailableError } from '@directus/errors';
|
|
3
|
+
import { toBoolean } from '@directus/utils';
|
|
1
4
|
import emitter from '../../emitter.js';
|
|
2
|
-
import env from '../../env.js';
|
|
3
|
-
import { toBoolean } from '../../utils/to-boolean.js';
|
|
4
5
|
import { WebSocketController, getWebSocketController } from '../controllers/index.js';
|
|
5
6
|
import { WebSocketMessage } from '../messages.js';
|
|
6
7
|
import { fmtMessage, getMessageType } from '../utils/message.js';
|
|
7
|
-
|
|
8
|
+
const env = useEnv();
|
|
8
9
|
const HEARTBEAT_FREQUENCY = Number(env['WEBSOCKETS_HEARTBEAT_PERIOD']) * 1000;
|
|
9
10
|
export class HeartbeatHandler {
|
|
10
11
|
pulse;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type Bus } from '@directus/memory';
|
|
2
2
|
import type { WebSocketEvent } from '../messages.js';
|
|
3
3
|
import { WebSocketSubscribeMessage } from '../messages.js';
|
|
4
4
|
import type { Subscription, WebSocketClient } from '../types.js';
|
|
@@ -7,7 +7,7 @@ import type { Subscription, WebSocketClient } from '../types.js';
|
|
|
7
7
|
*/
|
|
8
8
|
export declare class SubscribeHandler {
|
|
9
9
|
subscriptions: Record<string, Set<Subscription>>;
|
|
10
|
-
protected messenger:
|
|
10
|
+
protected messenger: Bus;
|
|
11
11
|
/**
|
|
12
12
|
* Initialize the handler
|
|
13
13
|
*/
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import emitter from '../../emitter.js';
|
|
2
1
|
import { InvalidPayloadError } from '@directus/errors';
|
|
3
|
-
import {
|
|
2
|
+
import {} from '@directus/memory';
|
|
3
|
+
import { useBus } from '../../bus/index.js';
|
|
4
|
+
import emitter from '../../emitter.js';
|
|
4
5
|
import { getSchema } from '../../utils/get-schema.js';
|
|
5
6
|
import { sanitizeQuery } from '../../utils/sanitize-query.js';
|
|
6
7
|
import { refreshAccountability } from '../authenticate.js';
|
|
7
8
|
import { WebSocketError, handleWebSocketError } from '../errors.js';
|
|
8
9
|
import { WebSocketSubscribeMessage } from '../messages.js';
|
|
9
|
-
import { fmtMessage, getMessageType } from '../utils/message.js';
|
|
10
10
|
import { getPayload } from '../utils/items.js';
|
|
11
|
+
import { fmtMessage, getMessageType } from '../utils/message.js';
|
|
11
12
|
/**
|
|
12
13
|
* Handler responsible for subscriptions
|
|
13
14
|
*/
|
|
@@ -21,7 +22,7 @@ export class SubscribeHandler {
|
|
|
21
22
|
*/
|
|
22
23
|
constructor() {
|
|
23
24
|
this.subscriptions = {};
|
|
24
|
-
this.messenger =
|
|
25
|
+
this.messenger = useBus();
|
|
25
26
|
this.bindWebSocket();
|
|
26
27
|
// listen to the Redis pub/sub and dispatch
|
|
27
28
|
this.messenger.subscribe('websocket.event', (message) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@directus/api",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "16.0.0",
|
|
4
4
|
"description": "Directus is a real-time API and App dashboard for managing SQL database content",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"directus",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
],
|
|
60
60
|
"dependencies": {
|
|
61
61
|
"@authenio/samlify-node-xmllint": "2.0.0",
|
|
62
|
-
"@aws-sdk/client-ses": "3.
|
|
62
|
+
"@aws-sdk/client-ses": "3.504.0",
|
|
63
63
|
"@directus/format-title": "10.1.0",
|
|
64
64
|
"@godaddy/terminus": "4.12.1",
|
|
65
65
|
"@rollup/plugin-alias": "5.1.0",
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"@rollup/plugin-virtual": "3.0.2",
|
|
68
68
|
"argon2": "0.31.2",
|
|
69
69
|
"async": "3.2.5",
|
|
70
|
-
"axios": "1.6.
|
|
70
|
+
"axios": "1.6.7",
|
|
71
71
|
"busboy": "1.6.0",
|
|
72
72
|
"bytes": "3.1.2",
|
|
73
73
|
"camelcase": "8.0.0",
|
|
@@ -78,27 +78,28 @@
|
|
|
78
78
|
"cookie-parser": "1.4.6",
|
|
79
79
|
"cors": "2.8.5",
|
|
80
80
|
"cron-parser": "4.9.0",
|
|
81
|
-
"date-fns": "3.
|
|
81
|
+
"date-fns": "3.3.1",
|
|
82
82
|
"deep-diff": "1.0.2",
|
|
83
83
|
"destroy": "1.2.0",
|
|
84
|
-
"dotenv": "16.
|
|
84
|
+
"dotenv": "16.4.1",
|
|
85
85
|
"encodeurl": "1.0.2",
|
|
86
86
|
"eventemitter2": "6.4.9",
|
|
87
87
|
"execa": "8.0.1",
|
|
88
|
-
"exif-reader": "
|
|
88
|
+
"exif-reader": "2.0.0",
|
|
89
89
|
"express": "4.18.2",
|
|
90
90
|
"flat": "6.0.1",
|
|
91
91
|
"fs-extra": "11.2.0",
|
|
92
92
|
"glob-to-regexp": "0.4.1",
|
|
93
93
|
"graphql": "16.8.1",
|
|
94
94
|
"graphql-compose": "9.0.10",
|
|
95
|
-
"graphql-ws": "5.14.
|
|
95
|
+
"graphql-ws": "5.14.3",
|
|
96
96
|
"helmet": "7.1.0",
|
|
97
97
|
"icc": "3.0.0",
|
|
98
|
-
"inquirer": "9.2.
|
|
98
|
+
"inquirer": "9.2.14",
|
|
99
99
|
"ioredis": "5.3.2",
|
|
100
|
-
"
|
|
101
|
-
"
|
|
100
|
+
"ip-matching": "2.1.2",
|
|
101
|
+
"isolated-vm": "4.7.2",
|
|
102
|
+
"joi": "17.12.0",
|
|
102
103
|
"js-yaml": "4.1.0",
|
|
103
104
|
"js2xmlparser": "5.0.0",
|
|
104
105
|
"json2csv": "5.0.7",
|
|
@@ -108,61 +109,63 @@
|
|
|
108
109
|
"ldapjs": "2.3.3",
|
|
109
110
|
"liquidjs": "10.10.0",
|
|
110
111
|
"lodash-es": "4.17.21",
|
|
111
|
-
"marked": "11.
|
|
112
|
+
"marked": "11.2.0",
|
|
112
113
|
"micromustache": "8.0.3",
|
|
113
114
|
"mime-types": "2.1.35",
|
|
114
115
|
"minimatch": "9.0.3",
|
|
115
116
|
"ms": "2.1.3",
|
|
116
|
-
"nanoid": "5.0.
|
|
117
|
+
"nanoid": "5.0.5",
|
|
117
118
|
"node-machine-id": "1.1.12",
|
|
118
119
|
"node-schedule": "2.1.1",
|
|
119
|
-
"nodemailer": "6.9.
|
|
120
|
+
"nodemailer": "6.9.9",
|
|
120
121
|
"object-hash": "3.0.0",
|
|
121
|
-
"openapi3-ts": "4.2.
|
|
122
|
-
"openid-client": "5.6.
|
|
123
|
-
"ora": "
|
|
122
|
+
"openapi3-ts": "4.2.1",
|
|
123
|
+
"openid-client": "5.6.4",
|
|
124
|
+
"ora": "8.0.1",
|
|
124
125
|
"otplib": "12.0.1",
|
|
125
126
|
"p-limit": "5.0.0",
|
|
126
127
|
"p-queue": "8.0.1",
|
|
127
128
|
"papaparse": "5.4.1",
|
|
128
|
-
"pino": "8.
|
|
129
|
-
"pino-http": "
|
|
129
|
+
"pino": "8.18.0",
|
|
130
|
+
"pino-http": "9.0.0",
|
|
130
131
|
"pino-http-print": "3.1.0",
|
|
131
|
-
"pino-pretty": "10.3.
|
|
132
|
+
"pino-pretty": "10.3.1",
|
|
132
133
|
"qs": "6.11.2",
|
|
133
|
-
"rate-limiter-flexible": "4.0.
|
|
134
|
-
"rollup": "4.9.
|
|
134
|
+
"rate-limiter-flexible": "4.0.1",
|
|
135
|
+
"rollup": "4.9.6",
|
|
135
136
|
"samlify": "2.8.10",
|
|
136
137
|
"sanitize-html": "2.11.0",
|
|
137
|
-
"sharp": "0.33.
|
|
138
|
+
"sharp": "0.33.2",
|
|
138
139
|
"snappy": "7.2.2",
|
|
139
140
|
"stream-json": "1.8.0",
|
|
140
|
-
"tinypool": "0.8.
|
|
141
|
+
"tinypool": "0.8.2",
|
|
141
142
|
"tsx": "4.7.0",
|
|
142
143
|
"uuid": "9.0.1",
|
|
143
144
|
"uuid-validate": "0.0.3",
|
|
144
145
|
"wellknown": "0.5.0",
|
|
145
|
-
"ws": "8.
|
|
146
|
+
"ws": "8.16.0",
|
|
146
147
|
"zod": "3.22.4",
|
|
147
|
-
"zod-validation-error": "
|
|
148
|
-
"@directus/constants": "11.0.
|
|
149
|
-
"@directus/
|
|
150
|
-
"@directus/
|
|
151
|
-
"@directus/
|
|
152
|
-
"@directus/
|
|
153
|
-
"@directus/
|
|
154
|
-
"@directus/
|
|
155
|
-
"@directus/storage": "10.0.8",
|
|
156
|
-
"@directus/storage-driver-azure": "10.0.15",
|
|
148
|
+
"zod-validation-error": "3.0.0",
|
|
149
|
+
"@directus/constants": "11.0.3",
|
|
150
|
+
"@directus/env": "1.0.0",
|
|
151
|
+
"@directus/extensions": "0.3.0",
|
|
152
|
+
"@directus/extensions-sdk": "10.3.1",
|
|
153
|
+
"@directus/memory": "1.0.1",
|
|
154
|
+
"@directus/app": "10.14.0",
|
|
155
|
+
"@directus/errors": "0.2.2",
|
|
157
156
|
"@directus/schema": "11.0.1",
|
|
158
|
-
"@directus/
|
|
159
|
-
"@directus/
|
|
160
|
-
"@directus/storage-driver-
|
|
161
|
-
"@directus/
|
|
162
|
-
"@directus/
|
|
163
|
-
"@directus/storage-driver-s3": "10.0.
|
|
164
|
-
"@directus/storage-driver-
|
|
165
|
-
"directus": "
|
|
157
|
+
"@directus/pressure": "1.0.15",
|
|
158
|
+
"@directus/specs": "10.2.6",
|
|
159
|
+
"@directus/storage-driver-azure": "10.0.16",
|
|
160
|
+
"@directus/storage": "10.0.9",
|
|
161
|
+
"@directus/storage-driver-cloudinary": "10.0.16",
|
|
162
|
+
"@directus/storage-driver-s3": "10.0.16",
|
|
163
|
+
"@directus/storage-driver-gcs": "10.0.16",
|
|
164
|
+
"@directus/storage-driver-supabase": "1.0.8",
|
|
165
|
+
"@directus/storage-driver-local": "10.0.16",
|
|
166
|
+
"@directus/validation": "0.0.11",
|
|
167
|
+
"directus": "10.9.0",
|
|
168
|
+
"@directus/utils": "11.0.4"
|
|
166
169
|
},
|
|
167
170
|
"devDependencies": {
|
|
168
171
|
"@ngneat/falso": "7.1.1",
|
|
@@ -175,9 +178,8 @@
|
|
|
175
178
|
"@types/deep-diff": "1.0.5",
|
|
176
179
|
"@types/destroy": "1.0.3",
|
|
177
180
|
"@types/encodeurl": "1.0.2",
|
|
178
|
-
"@types/exif-reader": "1.0.0",
|
|
179
181
|
"@types/express": "4.17.21",
|
|
180
|
-
"@types/express-serve-static-core": "4.17.
|
|
182
|
+
"@types/express-serve-static-core": "4.17.42",
|
|
181
183
|
"@types/fs-extra": "11.0.4",
|
|
182
184
|
"@types/glob-to-regexp": "0.4.4",
|
|
183
185
|
"@types/inquirer": "9.0.7",
|
|
@@ -188,37 +190,35 @@
|
|
|
188
190
|
"@types/lodash-es": "4.17.12",
|
|
189
191
|
"@types/mime-types": "2.1.4",
|
|
190
192
|
"@types/ms": "0.7.34",
|
|
191
|
-
"@types/node": "18.19.
|
|
192
|
-
"@types/node-schedule": "2.1.
|
|
193
|
+
"@types/node": "18.19.12",
|
|
194
|
+
"@types/node-schedule": "2.1.6",
|
|
193
195
|
"@types/nodemailer": "6.4.14",
|
|
194
196
|
"@types/object-hash": "3.0.6",
|
|
195
197
|
"@types/papaparse": "5.3.14",
|
|
196
|
-
"@types/qs": "6.9.
|
|
197
|
-
"@types/sanitize-html": "2.
|
|
198
|
+
"@types/qs": "6.9.11",
|
|
199
|
+
"@types/sanitize-html": "2.11.0",
|
|
198
200
|
"@types/stream-json": "1.7.7",
|
|
199
|
-
"@types/
|
|
200
|
-
"@types/uuid": "9.0.7",
|
|
201
|
+
"@types/uuid": "9.0.8",
|
|
201
202
|
"@types/uuid-validate": "0.0.3",
|
|
202
203
|
"@types/wellknown": "0.5.8",
|
|
203
204
|
"@types/ws": "8.5.10",
|
|
204
|
-
"@vitest/coverage-v8": "1.
|
|
205
|
+
"@vitest/coverage-v8": "1.2.2",
|
|
205
206
|
"copyfiles": "2.4.1",
|
|
206
207
|
"form-data": "4.0.0",
|
|
207
208
|
"knex-mock-client": "2.0.1",
|
|
208
|
-
"supertest": "6.3.3",
|
|
209
209
|
"typescript": "5.3.3",
|
|
210
|
-
"vitest": "1.
|
|
210
|
+
"vitest": "1.2.2",
|
|
211
211
|
"@directus/tsconfig": "1.0.1",
|
|
212
|
-
"@directus/types": "11.0.
|
|
212
|
+
"@directus/types": "11.0.4"
|
|
213
213
|
},
|
|
214
214
|
"optionalDependencies": {
|
|
215
|
-
"@keyv/redis": "2.8.
|
|
215
|
+
"@keyv/redis": "2.8.4",
|
|
216
216
|
"mysql": "2.18.1",
|
|
217
217
|
"nodemailer-mailgun-transport": "2.1.5",
|
|
218
218
|
"nodemailer-sendgrid": "1.0.3",
|
|
219
|
-
"oracledb": "6.
|
|
219
|
+
"oracledb": "6.3.0",
|
|
220
220
|
"pg": "8.11.3",
|
|
221
|
-
"sqlite3": "5.1.
|
|
221
|
+
"sqlite3": "5.1.7",
|
|
222
222
|
"tedious": "16.6.1"
|
|
223
223
|
},
|
|
224
224
|
"engines": {
|