@directus/api 14.1.2 → 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 +8 -6
- package/dist/auth/drivers/ldap.js +7 -4
- package/dist/auth/drivers/local.js +3 -2
- package/dist/auth/drivers/oauth2.js +11 -5
- package/dist/auth/drivers/openid.js +11 -5
- 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 +46 -34
- 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/index.js +2 -2
- 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.d.ts +2 -1
- package/dist/database/index.js +11 -3
- 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/database/system-data/relations/relations.yaml +4 -0
- package/dist/emitter.d.ts +1 -0
- package/dist/emitter.js +4 -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 +7 -5
- package/dist/extensions/lib/sandbox/register/call-reference.js +4 -2
- package/dist/extensions/lib/sandbox/register/route.d.ts +1 -0
- package/dist/extensions/lib/sandbox/sdk/generators/log.js +2 -1
- package/dist/extensions/lib/sync-extensions.js +6 -4
- package/dist/extensions/manager.d.ts +5 -0
- package/dist/extensions/manager.js +84 -34
- package/dist/flows.js +13 -7
- package/dist/logger.d.ts +7 -6
- package/dist/logger.js +116 -91
- 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 +16 -12
- 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 -0
- package/dist/redis/index.js +3 -0
- package/dist/redis/lib/create-redis.d.ts +7 -0
- package/dist/redis/lib/create-redis.js +12 -0
- package/dist/redis/lib/use-redis.d.ts +16 -0
- package/dist/redis/lib/use-redis.js +22 -0
- 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.d.ts +2 -0
- 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 +75 -40
- 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 +96 -18
- 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/payload.js +3 -3
- package/dist/services/relations.js +19 -10
- package/dist/services/server.js +7 -7
- package/dist/services/shares.js +3 -2
- package/dist/services/specifications.js +5 -4
- package/dist/services/users.js +24 -13
- 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/index.d.ts +4 -0
- package/dist/telemetry/index.js +4 -0
- package/dist/telemetry/lib/get-report.d.ts +5 -0
- package/dist/telemetry/lib/get-report.js +42 -0
- package/dist/telemetry/lib/init-telemetry.d.ts +11 -0
- package/dist/telemetry/lib/init-telemetry.js +30 -0
- package/dist/telemetry/lib/send-report.d.ts +5 -0
- package/dist/telemetry/lib/send-report.js +23 -0
- package/dist/telemetry/lib/track.d.ts +10 -0
- package/dist/telemetry/lib/track.js +30 -0
- package/dist/telemetry/types/report.d.ts +58 -0
- package/dist/telemetry/types/report.js +1 -0
- package/dist/telemetry/utils/get-item-count.d.ts +26 -0
- package/dist/telemetry/utils/get-item-count.js +36 -0
- package/dist/telemetry/utils/get-random-wait-time.d.ts +5 -0
- package/dist/telemetry/utils/get-random-wait-time.js +5 -0
- package/dist/telemetry/utils/get-user-count.d.ts +7 -0
- package/dist/telemetry/utils/get-user-count.js +30 -0
- package/dist/telemetry/utils/get-user-item-count.d.ts +13 -0
- package/dist/telemetry/utils/get-user-item-count.js +18 -0
- package/dist/types/assets.d.ts +2 -0
- package/dist/utils/apply-diff.js +2 -1
- package/dist/utils/apply-query.js +2 -2
- 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-cache-key.js +1 -1
- 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.d.ts +1 -1
- package/dist/utils/get-ip-from-req.js +5 -3
- 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 +5 -5
- package/dist/utils/get-versioned-hash.js +1 -1
- 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/md.d.ts +1 -1
- package/dist/utils/md.js +3 -2
- 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 +8 -3
- package/dist/utils/validate-snapshot.js +3 -3
- package/dist/utils/validate-storage.js +4 -2
- package/dist/webhooks.js +4 -3
- package/dist/websocket/controllers/base.d.ts +2 -0
- package/dist/websocket/controllers/base.js +12 -6
- package/dist/websocket/controllers/graphql.d.ts +2 -0
- package/dist/websocket/controllers/graphql.js +5 -3
- package/dist/websocket/controllers/hooks.js +3 -2
- package/dist/websocket/controllers/index.d.ts +2 -0
- package/dist/websocket/controllers/index.js +4 -2
- package/dist/websocket/controllers/rest.d.ts +2 -0
- 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/dist/websocket/types.d.ts +3 -1
- package/package.json +114 -115
- 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 -13
- package/dist/env.js +0 -505
- package/dist/messenger.d.ts +0 -24
- package/dist/messenger.js +0 -64
- package/dist/utils/package.d.ts +0 -2
- package/dist/utils/package.js +0 -6
- package/dist/utils/telemetry.d.ts +0 -1
- package/dist/utils/telemetry.js +0 -23
- package/dist/utils/to-boolean.d.ts +0 -4
- package/dist/utils/to-boolean.js +0 -6
package/dist/flows.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { Action } from '@directus/constants';
|
|
2
|
+
import { useEnv } from '@directus/env';
|
|
3
|
+
import { ForbiddenError } from '@directus/errors';
|
|
2
4
|
import { applyOptionsData, getRedactedString, isValidJSON, parseJSON, toArray } from '@directus/utils';
|
|
3
5
|
import { omit, pick } from 'lodash-es';
|
|
4
6
|
import { get } from 'micromustache';
|
|
7
|
+
import { useBus } from './bus/index.js';
|
|
5
8
|
import getDatabase from './database/index.js';
|
|
6
9
|
import emitter from './emitter.js';
|
|
7
|
-
import
|
|
8
|
-
import { ForbiddenError } from '@directus/errors';
|
|
9
|
-
import logger from './logger.js';
|
|
10
|
-
import { getMessenger } from './messenger.js';
|
|
10
|
+
import { useLogger } from './logger.js';
|
|
11
11
|
import { ActivityService } from './services/activity.js';
|
|
12
12
|
import { FlowsService } from './services/flows.js';
|
|
13
13
|
import * as services from './services/index.js';
|
|
@@ -40,9 +40,11 @@ class FlowManager {
|
|
|
40
40
|
reloadQueue;
|
|
41
41
|
envs;
|
|
42
42
|
constructor() {
|
|
43
|
+
const env = useEnv();
|
|
44
|
+
const logger = useLogger();
|
|
43
45
|
this.reloadQueue = new JobQueue();
|
|
44
46
|
this.envs = env['FLOWS_ENV_ALLOW_LIST'] ? pick(env, toArray(env['FLOWS_ENV_ALLOW_LIST'])) : {};
|
|
45
|
-
const messenger =
|
|
47
|
+
const messenger = useBus();
|
|
46
48
|
messenger.subscribe('flows', (event) => {
|
|
47
49
|
if (event['type'] === 'reload') {
|
|
48
50
|
this.reloadQueue.enqueue(async () => {
|
|
@@ -63,7 +65,7 @@ class FlowManager {
|
|
|
63
65
|
}
|
|
64
66
|
}
|
|
65
67
|
async reload() {
|
|
66
|
-
const messenger =
|
|
68
|
+
const messenger = useBus();
|
|
67
69
|
messenger.publish('flows', { type: 'reload' });
|
|
68
70
|
}
|
|
69
71
|
addOperation(id, operation) {
|
|
@@ -73,6 +75,7 @@ class FlowManager {
|
|
|
73
75
|
this.operations.delete(id);
|
|
74
76
|
}
|
|
75
77
|
async runOperationFlow(id, data, context) {
|
|
78
|
+
const logger = useLogger();
|
|
76
79
|
if (!(id in this.operationFlowHandlers)) {
|
|
77
80
|
logger.warn(`Couldn't find operation triggered flow with id "${id}"`);
|
|
78
81
|
return null;
|
|
@@ -81,6 +84,7 @@ class FlowManager {
|
|
|
81
84
|
return handler(data, context);
|
|
82
85
|
}
|
|
83
86
|
async runWebhookFlow(id, data, context) {
|
|
87
|
+
const logger = useLogger();
|
|
84
88
|
if (!(id in this.webhookFlowHandlers)) {
|
|
85
89
|
logger.warn(`Couldn't find webhook or manual triggered flow with id "${id}"`);
|
|
86
90
|
throw new ForbiddenError();
|
|
@@ -89,6 +93,7 @@ class FlowManager {
|
|
|
89
93
|
return handler(data, context);
|
|
90
94
|
}
|
|
91
95
|
async load() {
|
|
96
|
+
const logger = useLogger();
|
|
92
97
|
const flowsService = new FlowsService({ knex: getDatabase(), schema: await getSchema() });
|
|
93
98
|
const flows = await flowsService.readByQuery({
|
|
94
99
|
filter: { status: { _eq: 'active' } },
|
|
@@ -305,6 +310,7 @@ class FlowManager {
|
|
|
305
310
|
return undefined;
|
|
306
311
|
}
|
|
307
312
|
async executeOperation(operation, keyedData, context = {}) {
|
|
313
|
+
const logger = useLogger();
|
|
308
314
|
if (!this.operations.has(operation.type)) {
|
|
309
315
|
logger.warn(`Couldn't find operation ${operation.type}`);
|
|
310
316
|
return { successor: null, status: 'unknown', data: null, options: null };
|
|
@@ -314,7 +320,7 @@ class FlowManager {
|
|
|
314
320
|
try {
|
|
315
321
|
let result = await handler(options, {
|
|
316
322
|
services,
|
|
317
|
-
env,
|
|
323
|
+
env: useEnv(),
|
|
318
324
|
database: getDatabase(),
|
|
319
325
|
logger,
|
|
320
326
|
getSchema,
|
package/dist/logger.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/// <reference types="qs" />
|
|
2
2
|
import type { RequestHandler } from 'express';
|
|
3
|
-
import { type
|
|
4
|
-
export declare const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
export declare const
|
|
8
|
-
export
|
|
3
|
+
import { type Logger } from 'pino';
|
|
4
|
+
export declare const _cache: {
|
|
5
|
+
logger: Logger<never> | undefined;
|
|
6
|
+
};
|
|
7
|
+
export declare const useLogger: () => Logger<never>;
|
|
8
|
+
export declare const createLogger: () => Logger<never>;
|
|
9
|
+
export declare const createExpressLogger: () => RequestHandler<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>;
|
package/dist/logger.js
CHANGED
|
@@ -1,111 +1,136 @@
|
|
|
1
|
+
import { useEnv } from '@directus/env';
|
|
1
2
|
import { REDACTED_TEXT, toArray } from '@directus/utils';
|
|
2
3
|
import { merge } from 'lodash-es';
|
|
3
4
|
import { URL } from 'node:url';
|
|
4
5
|
import { pino } from 'pino';
|
|
5
6
|
import { pinoHttp, stdSerializers } from 'pino-http';
|
|
6
|
-
import env from './env.js';
|
|
7
7
|
import { getConfigFromEnv } from './utils/get-config-from-env.js';
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
export const httpLoggerOptions = {
|
|
16
|
-
level: env['LOG_LEVEL'] || 'info',
|
|
17
|
-
redact: {
|
|
18
|
-
paths: ['req.headers.authorization', 'req.headers.cookie'],
|
|
19
|
-
censor: REDACTED_TEXT,
|
|
20
|
-
},
|
|
8
|
+
export const _cache = { logger: undefined };
|
|
9
|
+
export const useLogger = () => {
|
|
10
|
+
if (_cache.logger) {
|
|
11
|
+
return _cache.logger;
|
|
12
|
+
}
|
|
13
|
+
_cache.logger = createLogger();
|
|
14
|
+
return _cache.logger;
|
|
21
15
|
};
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
16
|
+
export const createLogger = () => {
|
|
17
|
+
const env = useEnv();
|
|
18
|
+
const pinoOptions = {
|
|
19
|
+
level: env['LOG_LEVEL'] || 'info',
|
|
20
|
+
redact: {
|
|
21
|
+
paths: ['req.headers.authorization', 'req.headers.cookie'],
|
|
22
|
+
censor: REDACTED_TEXT,
|
|
28
23
|
},
|
|
29
24
|
};
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
translateTime: 'SYS:HH:MM:ss',
|
|
35
|
-
relativeUrl: true,
|
|
36
|
-
prettyOptions: {
|
|
25
|
+
if (env['LOG_STYLE'] !== 'raw') {
|
|
26
|
+
pinoOptions.transport = {
|
|
27
|
+
target: 'pino-pretty',
|
|
28
|
+
options: {
|
|
37
29
|
ignore: 'hostname,pid',
|
|
38
30
|
sync: true,
|
|
39
31
|
},
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
const loggerEnvConfig = getConfigFromEnv('LOGGER_', 'LOGGER_HTTP');
|
|
35
|
+
// Expose custom log levels into formatter function
|
|
36
|
+
if (loggerEnvConfig['levels']) {
|
|
37
|
+
const customLogLevels = {};
|
|
38
|
+
for (const el of toArray(loggerEnvConfig['levels'])) {
|
|
39
|
+
const key_val = el.split(':');
|
|
40
|
+
customLogLevels[key_val[0].trim()] = key_val[1].trim();
|
|
41
|
+
}
|
|
42
|
+
pinoOptions.formatters = {
|
|
43
|
+
level(label, number) {
|
|
44
|
+
return {
|
|
45
|
+
severity: customLogLevels[label] || 'info',
|
|
46
|
+
level: number,
|
|
47
|
+
};
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
delete loggerEnvConfig['levels'];
|
|
51
|
+
}
|
|
52
|
+
return pino(merge(pinoOptions, loggerEnvConfig));
|
|
53
|
+
};
|
|
54
|
+
export const createExpressLogger = () => {
|
|
55
|
+
const env = useEnv();
|
|
56
|
+
const httpLoggerEnvConfig = getConfigFromEnv('LOGGER_HTTP', ['LOGGER_HTTP_LOGGER']);
|
|
57
|
+
const loggerEnvConfig = getConfigFromEnv('LOGGER_', 'LOGGER_HTTP');
|
|
58
|
+
const httpLoggerOptions = {
|
|
59
|
+
level: env['LOG_LEVEL'] || 'info',
|
|
60
|
+
redact: {
|
|
61
|
+
paths: ['req.headers.authorization', 'req.headers.cookie'],
|
|
62
|
+
censor: REDACTED_TEXT,
|
|
40
63
|
},
|
|
41
64
|
};
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
65
|
+
if (env['LOG_STYLE'] !== 'raw') {
|
|
66
|
+
httpLoggerOptions.transport = {
|
|
67
|
+
target: 'pino-http-print',
|
|
68
|
+
options: {
|
|
69
|
+
all: true,
|
|
70
|
+
translateTime: 'SYS:HH:MM:ss',
|
|
71
|
+
relativeUrl: true,
|
|
72
|
+
prettyOptions: {
|
|
73
|
+
ignore: 'hostname,pid',
|
|
74
|
+
sync: true,
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
if (env['LOG_STYLE'] === 'raw') {
|
|
80
|
+
httpLoggerOptions.redact = {
|
|
81
|
+
paths: ['req.headers.authorization', 'req.headers.cookie', 'res.headers'],
|
|
82
|
+
censor: (value, pathParts) => {
|
|
83
|
+
const path = pathParts.join('.');
|
|
84
|
+
if (path === 'res.headers') {
|
|
85
|
+
if ('set-cookie' in value) {
|
|
86
|
+
value['set-cookie'] = REDACTED_TEXT;
|
|
87
|
+
}
|
|
88
|
+
return value;
|
|
51
89
|
}
|
|
52
|
-
return
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
},
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
const loggerEnvConfig = getConfigFromEnv('LOGGER_', 'LOGGER_HTTP');
|
|
59
|
-
// Expose custom log levels into formatter function
|
|
60
|
-
if (loggerEnvConfig['levels']) {
|
|
61
|
-
const customLogLevels = {};
|
|
62
|
-
for (const el of toArray(loggerEnvConfig['levels'])) {
|
|
63
|
-
const key_val = el.split(':');
|
|
64
|
-
customLogLevels[key_val[0].trim()] = key_val[1].trim();
|
|
90
|
+
return REDACTED_TEXT;
|
|
91
|
+
},
|
|
92
|
+
};
|
|
65
93
|
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
output.url = redactQuery(output.url);
|
|
104
|
-
return output;
|
|
94
|
+
// Expose custom log levels into formatter function
|
|
95
|
+
if (loggerEnvConfig['levels']) {
|
|
96
|
+
const customLogLevels = {};
|
|
97
|
+
for (const el of toArray(loggerEnvConfig['levels'])) {
|
|
98
|
+
const key_val = el.split(':');
|
|
99
|
+
customLogLevels[key_val[0].trim()] = key_val[1].trim();
|
|
100
|
+
}
|
|
101
|
+
httpLoggerOptions.formatters = {
|
|
102
|
+
level(label, number) {
|
|
103
|
+
return {
|
|
104
|
+
severity: customLogLevels[label] || 'info',
|
|
105
|
+
level: number,
|
|
106
|
+
};
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
delete loggerEnvConfig['levels'];
|
|
110
|
+
}
|
|
111
|
+
if (env['LOG_HTTP_IGNORE_PATHS']) {
|
|
112
|
+
const ignorePathsSet = new Set(env['LOG_HTTP_IGNORE_PATHS']);
|
|
113
|
+
httpLoggerEnvConfig['autoLogging'] = {
|
|
114
|
+
ignore: (req) => {
|
|
115
|
+
if (!req.url)
|
|
116
|
+
return false;
|
|
117
|
+
const { pathname } = new URL(req.url, 'http://example.com/');
|
|
118
|
+
return ignorePathsSet.has(pathname);
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
return pinoHttp({
|
|
123
|
+
logger: pino(merge(httpLoggerOptions, loggerEnvConfig)),
|
|
124
|
+
...httpLoggerEnvConfig,
|
|
125
|
+
serializers: {
|
|
126
|
+
req(request) {
|
|
127
|
+
const output = stdSerializers.req(request);
|
|
128
|
+
output.url = redactQuery(output.url);
|
|
129
|
+
return output;
|
|
130
|
+
},
|
|
105
131
|
},
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
export default logger;
|
|
132
|
+
});
|
|
133
|
+
};
|
|
109
134
|
function redactQuery(originalPath) {
|
|
110
135
|
const url = new URL(originalPath, 'http://example.com/');
|
|
111
136
|
if (url.searchParams.has('access_token')) {
|
package/dist/mailer.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { useEnv } from '@directus/env';
|
|
1
2
|
import nodemailer from 'nodemailer';
|
|
2
|
-
import
|
|
3
|
-
import logger from './logger.js';
|
|
3
|
+
import { useLogger } from './logger.js';
|
|
4
4
|
import { getConfigFromEnv } from './utils/get-config-from-env.js';
|
|
5
5
|
import { createRequire } from 'node:module';
|
|
6
6
|
const require = createRequire(import.meta.url);
|
|
@@ -8,6 +8,8 @@ let transporter;
|
|
|
8
8
|
export default function getMailer() {
|
|
9
9
|
if (transporter)
|
|
10
10
|
return transporter;
|
|
11
|
+
const env = useEnv();
|
|
12
|
+
const logger = useLogger();
|
|
11
13
|
const transportName = env['EMAIL_TRANSPORT'].toLowerCase();
|
|
12
14
|
if (transportName === 'sendmail') {
|
|
13
15
|
transporter = nodemailer.createTransport({
|
package/dist/middleware/cache.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
|
+
import { useEnv } from '@directus/env';
|
|
1
2
|
import { getCache, getCacheValue } from '../cache.js';
|
|
2
|
-
import
|
|
3
|
-
import logger from '../logger.js';
|
|
3
|
+
import { useLogger } from '../logger.js';
|
|
4
4
|
import asyncHandler from '../utils/async-handler.js';
|
|
5
5
|
import { getCacheControlHeader } from '../utils/get-cache-headers.js';
|
|
6
6
|
import { getCacheKey } from '../utils/get-cache-key.js';
|
|
7
7
|
import { shouldSkipCache } from '../utils/should-skip-cache.js';
|
|
8
8
|
const checkCacheMiddleware = asyncHandler(async (req, res, next) => {
|
|
9
|
+
const env = useEnv();
|
|
9
10
|
const { cache } = getCache();
|
|
11
|
+
const logger = useLogger();
|
|
10
12
|
if (req.method.toLowerCase() !== 'get' && req.originalUrl?.startsWith('/graphql') === false)
|
|
11
13
|
return next();
|
|
12
14
|
if (env['CACHE_ENABLED'] !== true)
|
|
@@ -1,18 +1,37 @@
|
|
|
1
|
-
import getDatabase from '../database/index.js';
|
|
2
1
|
import { InvalidIpError } from '@directus/errors';
|
|
2
|
+
import getDatabase from '../database/index.js';
|
|
3
|
+
import { useLogger } from '../logger.js';
|
|
3
4
|
import asyncHandler from '../utils/async-handler.js';
|
|
5
|
+
import { ipInNetworks } from '../utils/ip-in-networks.js';
|
|
4
6
|
export const checkIP = asyncHandler(async (req, _res, next) => {
|
|
5
7
|
const database = getDatabase();
|
|
8
|
+
const logger = useLogger();
|
|
9
|
+
const { role: roleId, ip } = req.accountability;
|
|
6
10
|
const query = database.select('ip_access').from('directus_roles');
|
|
7
|
-
if (
|
|
8
|
-
query.where({ id:
|
|
11
|
+
if (roleId) {
|
|
12
|
+
query.where({ id: roleId });
|
|
9
13
|
}
|
|
10
14
|
else {
|
|
11
15
|
query.whereNull('id');
|
|
12
16
|
}
|
|
13
17
|
const role = await query.first();
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
18
|
+
if (!role?.ip_access)
|
|
19
|
+
return next();
|
|
20
|
+
const ipAllowList = role.ip_access.split(',').filter((ip) => ip);
|
|
21
|
+
if (ipAllowList.length > 0) {
|
|
22
|
+
if (!ip)
|
|
23
|
+
throw new InvalidIpError();
|
|
24
|
+
let allowed;
|
|
25
|
+
try {
|
|
26
|
+
allowed = ipInNetworks(ip, ipAllowList);
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
logger.warn(`Invalid IP access configuration for role "${roleId}"`);
|
|
30
|
+
logger.warn(error);
|
|
31
|
+
throw new InvalidIpError();
|
|
32
|
+
}
|
|
33
|
+
if (!allowed)
|
|
34
|
+
throw new InvalidIpError();
|
|
35
|
+
}
|
|
17
36
|
return next();
|
|
18
37
|
});
|
package/dist/middleware/cors.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { useEnv } from '@directus/env';
|
|
1
2
|
import cors from 'cors';
|
|
2
|
-
import env from '../env.js';
|
|
3
3
|
let corsMiddleware = (_req, _res, next) => next();
|
|
4
|
+
const env = useEnv();
|
|
4
5
|
if (env['CORS_ENABLED'] === true) {
|
|
5
6
|
corsMiddleware = cors({
|
|
6
7
|
origin: env['CORS_ORIGIN'] || true,
|
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import { isDirectusError } from '@directus/errors';
|
|
1
|
+
import { ErrorCode, MethodNotAllowedError, isDirectusError } from '@directus/errors';
|
|
2
2
|
import { toArray } from '@directus/utils';
|
|
3
|
+
import { getNodeEnv } from '@directus/utils/node';
|
|
3
4
|
import getDatabase from '../database/index.js';
|
|
4
5
|
import emitter from '../emitter.js';
|
|
5
|
-
import
|
|
6
|
-
import { ErrorCode, MethodNotAllowedError } from '@directus/errors';
|
|
7
|
-
import logger from '../logger.js';
|
|
6
|
+
import { useLogger } from '../logger.js';
|
|
8
7
|
// Note: keep all 4 parameters here. That's how Express recognizes it's the error handler, even if
|
|
9
8
|
// we don't use next
|
|
10
9
|
const errorHandler = (err, req, res, _next) => {
|
|
10
|
+
const logger = useLogger();
|
|
11
11
|
let payload = {
|
|
12
12
|
errors: [],
|
|
13
13
|
};
|
|
14
14
|
const errors = toArray(err);
|
|
15
15
|
let status = null;
|
|
16
16
|
for (const err of errors) {
|
|
17
|
-
if (
|
|
17
|
+
if (getNodeEnv() === 'development') {
|
|
18
18
|
err.extensions = {
|
|
19
19
|
...(err.extensions || {}),
|
|
20
20
|
stack: err.stack,
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useEnv } from '@directus/env';
|
|
2
2
|
import { HitRateLimitError } from '@directus/errors';
|
|
3
|
-
import
|
|
3
|
+
import { useLogger } from '../logger.js';
|
|
4
4
|
import { createRateLimiter } from '../rate-limiter.js';
|
|
5
5
|
import asyncHandler from '../utils/async-handler.js';
|
|
6
6
|
import { validateEnv } from '../utils/validate-env.js';
|
|
7
7
|
const RATE_LIMITER_GLOBAL_KEY = 'global-rate-limit';
|
|
8
|
+
const env = useEnv();
|
|
9
|
+
const logger = useLogger();
|
|
8
10
|
let checkRateLimit = (_req, _res, next) => next();
|
|
9
11
|
export let rateLimiterGlobal;
|
|
10
12
|
if (env['RATE_LIMITER_GLOBAL_ENABLED'] === true) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useEnv } from '@directus/env';
|
|
2
2
|
import { HitRateLimitError } from '@directus/errors';
|
|
3
3
|
import { createRateLimiter } from '../rate-limiter.js';
|
|
4
4
|
import asyncHandler from '../utils/async-handler.js';
|
|
@@ -6,21 +6,25 @@ import { getIPFromReq } from '../utils/get-ip-from-req.js';
|
|
|
6
6
|
import { validateEnv } from '../utils/validate-env.js';
|
|
7
7
|
let checkRateLimit = (_req, _res, next) => next();
|
|
8
8
|
export let rateLimiter;
|
|
9
|
+
const env = useEnv();
|
|
9
10
|
if (env['RATE_LIMITER_ENABLED'] === true) {
|
|
10
11
|
validateEnv(['RATE_LIMITER_STORE', 'RATE_LIMITER_DURATION', 'RATE_LIMITER_POINTS']);
|
|
11
12
|
rateLimiter = createRateLimiter('RATE_LIMITER');
|
|
12
13
|
checkRateLimit = asyncHandler(async (req, res, next) => {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
14
|
+
const ip = getIPFromReq(req);
|
|
15
|
+
if (ip) {
|
|
16
|
+
try {
|
|
17
|
+
await rateLimiter.consume(ip, 1);
|
|
18
|
+
}
|
|
19
|
+
catch (rateLimiterRes) {
|
|
20
|
+
if (rateLimiterRes instanceof Error)
|
|
21
|
+
throw rateLimiterRes;
|
|
22
|
+
res.set('Retry-After', String(Math.round(rateLimiterRes.msBeforeNext / 1000)));
|
|
23
|
+
throw new HitRateLimitError({
|
|
24
|
+
limit: +env['RATE_LIMITER_POINTS'],
|
|
25
|
+
reset: new Date(Date.now() + rateLimiterRes.msBeforeNext),
|
|
26
|
+
});
|
|
27
|
+
}
|
|
24
28
|
}
|
|
25
29
|
next();
|
|
26
30
|
});
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { useEnv } from '@directus/env';
|
|
1
2
|
import { parse as parseBytesConfiguration } from 'bytes';
|
|
2
3
|
import { assign } from 'lodash-es';
|
|
3
4
|
import { getCache, setCacheValue } from '../cache.js';
|
|
4
|
-
import
|
|
5
|
-
import logger from '../logger.js';
|
|
5
|
+
import { useLogger } from '../logger.js';
|
|
6
6
|
import { ExportService } from '../services/import-export/index.js';
|
|
7
7
|
import { VersionsService } from '../services/versions.js';
|
|
8
8
|
import asyncHandler from '../utils/async-handler.js';
|
|
@@ -12,6 +12,8 @@ import { getDateFormatted } from '../utils/get-date-formatted.js';
|
|
|
12
12
|
import { getMilliseconds } from '../utils/get-milliseconds.js';
|
|
13
13
|
import { stringByteSize } from '../utils/get-string-byte-size.js';
|
|
14
14
|
export const respond = asyncHandler(async (req, res) => {
|
|
15
|
+
const env = useEnv();
|
|
16
|
+
const logger = useLogger();
|
|
15
17
|
const { cache } = getCache();
|
|
16
18
|
let exceedsMaxSize = false;
|
|
17
19
|
if (env['CACHE_VALUE_MAX_SIZE'] !== false) {
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { defineOperationApi } from '@directus/extensions';
|
|
2
2
|
import { optionToString } from '@directus/utils';
|
|
3
|
-
import
|
|
3
|
+
import { useLogger } from '../../logger.js';
|
|
4
4
|
export default defineOperationApi({
|
|
5
5
|
id: 'log',
|
|
6
6
|
handler: ({ message }) => {
|
|
7
|
+
const logger = useLogger();
|
|
7
8
|
logger.info(optionToString(message));
|
|
8
9
|
},
|
|
9
10
|
});
|
package/dist/rate-limiter.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { IRateLimiterOptions, IRateLimiterStoreOptions, RateLimiterAbstract } from 'rate-limiter-flexible';
|
|
2
|
+
import { RateLimiterRes } from 'rate-limiter-flexible';
|
|
2
3
|
type IRateLimiterOptionsOverrides = Partial<IRateLimiterOptions> | Partial<IRateLimiterStoreOptions>;
|
|
3
4
|
export declare function createRateLimiter(configPrefix?: string, configOverrides?: IRateLimiterOptionsOverrides): RateLimiterAbstract;
|
|
4
|
-
export {};
|
|
5
|
+
export { RateLimiterRes };
|
package/dist/rate-limiter.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import { useEnv } from '@directus/env';
|
|
1
2
|
import { merge } from 'lodash-es';
|
|
2
|
-
import { RateLimiterMemory, RateLimiterRedis } from 'rate-limiter-flexible';
|
|
3
|
-
import env from './env.js';
|
|
3
|
+
import { RateLimiterMemory, RateLimiterRedis, RateLimiterRes } from 'rate-limiter-flexible';
|
|
4
4
|
import { getConfigFromEnv } from './utils/get-config-from-env.js';
|
|
5
5
|
import { createRequire } from 'node:module';
|
|
6
6
|
const require = createRequire(import.meta.url);
|
|
7
7
|
export function createRateLimiter(configPrefix = 'RATE_LIMITER', configOverrides) {
|
|
8
|
+
const env = useEnv();
|
|
8
9
|
switch (env['RATE_LIMITER_STORE']) {
|
|
9
10
|
case 'redis':
|
|
10
11
|
return new RateLimiterRedis(getConfig('redis', configPrefix, configOverrides));
|
|
@@ -13,10 +14,12 @@ export function createRateLimiter(configPrefix = 'RATE_LIMITER', configOverrides
|
|
|
13
14
|
return new RateLimiterMemory(getConfig('memory', configPrefix, configOverrides));
|
|
14
15
|
}
|
|
15
16
|
}
|
|
17
|
+
export { RateLimiterRes };
|
|
16
18
|
function getConfig(store = 'memory', configPrefix = 'RATE_LIMITER', overrides) {
|
|
17
19
|
const config = getConfigFromEnv(`${configPrefix}_`, `${configPrefix}_${store}_`);
|
|
18
20
|
if (store === 'redis') {
|
|
19
21
|
const Redis = require('ioredis');
|
|
22
|
+
const env = useEnv();
|
|
20
23
|
config.storeClient = new Redis(env[`REDIS`] || getConfigFromEnv(`REDIS_`));
|
|
21
24
|
}
|
|
22
25
|
delete config.enabled;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { useEnv } from '@directus/env';
|
|
2
|
+
import { Redis } from 'ioredis';
|
|
3
|
+
import { getConfigFromEnv } from '../../utils/get-config-from-env.js';
|
|
4
|
+
/**
|
|
5
|
+
* Create a new Redis instance based on the global env configuration
|
|
6
|
+
*
|
|
7
|
+
* @returns New Redis instance based on global configuration
|
|
8
|
+
*/
|
|
9
|
+
export const createRedis = () => {
|
|
10
|
+
const env = useEnv();
|
|
11
|
+
return new Redis(env['REDIS'] ?? getConfigFromEnv('REDIS'));
|
|
12
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Redis } from 'ioredis';
|
|
2
|
+
/**
|
|
3
|
+
* Memoization cache for useRedis
|
|
4
|
+
*
|
|
5
|
+
* @see {@link useRedis}
|
|
6
|
+
*/
|
|
7
|
+
export declare const _cache: {
|
|
8
|
+
redis: Redis | undefined;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Access the globally shared Redis instance
|
|
12
|
+
* Creates new Redis instance on first invocation
|
|
13
|
+
*
|
|
14
|
+
* @returns Globally shared Redis instance
|
|
15
|
+
*/
|
|
16
|
+
export declare const useRedis: () => Redis;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Redis } from 'ioredis';
|
|
2
|
+
import { createRedis } from './create-redis.js';
|
|
3
|
+
/**
|
|
4
|
+
* Memoization cache for useRedis
|
|
5
|
+
*
|
|
6
|
+
* @see {@link useRedis}
|
|
7
|
+
*/
|
|
8
|
+
export const _cache = {
|
|
9
|
+
redis: undefined,
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Access the globally shared Redis instance
|
|
13
|
+
* Creates new Redis instance on first invocation
|
|
14
|
+
*
|
|
15
|
+
* @returns Globally shared Redis instance
|
|
16
|
+
*/
|
|
17
|
+
export const useRedis = () => {
|
|
18
|
+
if (_cache.redis)
|
|
19
|
+
return _cache.redis;
|
|
20
|
+
_cache.redis = createRedis();
|
|
21
|
+
return _cache.redis;
|
|
22
|
+
};
|