directus 9.23.3 → 9.23.4
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 +12 -12
- package/dist/auth/drivers/ldap.js +22 -22
- package/dist/auth/drivers/local.js +7 -7
- package/dist/auth/drivers/oauth2.js +27 -25
- package/dist/auth/drivers/openid.js +32 -30
- package/dist/auth/drivers/saml.js +10 -10
- package/dist/auth.js +4 -3
- package/dist/cache.js +16 -11
- package/dist/cli/commands/bootstrap/index.js +5 -4
- package/dist/cli/utils/create-db-connection.js +1 -1
- package/dist/cli/utils/create-env/index.js +1 -1
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +6 -5
- package/dist/controllers/activity.js +9 -9
- package/dist/controllers/assets.js +19 -18
- package/dist/controllers/auth.js +13 -13
- package/dist/controllers/collections.js +10 -10
- package/dist/controllers/dashboards.js +9 -9
- package/dist/controllers/extensions.js +3 -3
- package/dist/controllers/fields.js +16 -16
- package/dist/controllers/files.js +16 -15
- package/dist/controllers/flows.js +11 -11
- package/dist/controllers/folders.js +9 -9
- package/dist/controllers/graphql.js +6 -6
- package/dist/controllers/items.js +17 -17
- package/dist/controllers/notifications.js +9 -9
- package/dist/controllers/operations.js +9 -9
- package/dist/controllers/panels.js +9 -9
- package/dist/controllers/permissions.js +9 -9
- package/dist/controllers/presets.js +9 -9
- package/dist/controllers/relations.js +10 -10
- package/dist/controllers/revisions.js +3 -3
- package/dist/controllers/roles.js +9 -9
- package/dist/controllers/schema.js +5 -5
- package/dist/controllers/server.js +7 -7
- package/dist/controllers/settings.js +2 -2
- package/dist/controllers/shares.js +13 -13
- package/dist/controllers/users.js +16 -16
- package/dist/controllers/utils.js +5 -5
- package/dist/controllers/webhooks.js +9 -9
- package/dist/database/helpers/fn/types.d.ts +0 -1
- package/dist/database/helpers/fn/types.js +0 -2
- package/dist/database/helpers/index.d.ts +3 -3
- package/dist/database/index.js +5 -5
- package/dist/database/migrations/20210805B-change-image-metadata-structure.js +15 -15
- package/dist/database/migrations/run.js +1 -1
- package/dist/database/run-ast.js +4 -4
- package/dist/database/system-data/collections/index.js +2 -2
- package/dist/database/system-data/fields/index.js +3 -3
- package/dist/env.js +1 -1
- package/dist/extensions.js +10 -10
- package/dist/flows.js +33 -31
- package/dist/logger.d.ts +1 -0
- package/dist/logger.js +32 -32
- package/dist/mailer.js +16 -16
- package/dist/messenger.js +4 -4
- package/dist/middleware/authenticate.js +1 -1
- package/dist/middleware/cache.js +11 -11
- package/dist/middleware/collection-exists.js +3 -3
- package/dist/middleware/cors.js +7 -7
- package/dist/middleware/error-handler.js +2 -2
- package/dist/middleware/extract-token.js +2 -2
- package/dist/middleware/graphql.js +12 -6
- package/dist/middleware/rate-limiter-global.js +5 -5
- package/dist/middleware/rate-limiter-ip.js +2 -2
- package/dist/middleware/respond.js +16 -16
- package/dist/middleware/sanitize-query.js +1 -1
- package/dist/operations/exec/index.js +2 -2
- package/dist/rate-limiter.js +1 -1
- package/dist/request/validate-ip.js +2 -2
- package/dist/server.js +4 -4
- package/dist/services/activity.js +14 -14
- package/dist/services/assets.js +6 -6
- package/dist/services/authentication.js +9 -9
- package/dist/services/collections.js +9 -9
- package/dist/services/fields.js +5 -5
- package/dist/services/files.js +12 -12
- package/dist/services/graphql/index.js +100 -98
- package/dist/services/import-export.js +6 -6
- package/dist/services/items.js +6 -6
- package/dist/services/mail/index.js +5 -5
- package/dist/services/meta.js +1 -0
- package/dist/services/notifications.js +4 -4
- package/dist/services/revisions.js +3 -3
- package/dist/services/roles.js +5 -5
- package/dist/services/server.js +27 -27
- package/dist/services/shares.js +9 -9
- package/dist/services/specifications.js +5 -3
- package/dist/services/users.d.ts +1 -5
- package/dist/services/users.js +24 -27
- package/dist/storage/register-locations.js +1 -1
- package/dist/utils/apply-query.js +2 -1
- package/dist/utils/dynamic-import.js +1 -1
- package/dist/utils/generate-hash.js +1 -1
- package/dist/utils/get-ast-from-query.js +1 -1
- package/dist/utils/get-auth-providers.js +1 -1
- package/dist/utils/get-cache-headers.js +3 -3
- package/dist/utils/get-collection-from-alias.js +1 -0
- package/dist/utils/get-default-value.js +1 -1
- package/dist/utils/get-ip-from-req.js +2 -2
- package/dist/utils/get-permissions.js +11 -11
- package/dist/utils/get-schema.js +2 -2
- package/dist/utils/is-url-allowed.js +5 -2
- package/dist/utils/sanitize-query.js +26 -26
- package/dist/utils/should-skip-cache.js +2 -2
- package/dist/utils/track.js +16 -16
- package/dist/utils/validate-query.js +1 -1
- package/dist/utils/validate-storage.js +8 -8
- package/dist/webhooks.js +2 -2
- package/package.json +13 -13
- package/dist/utils/redact-header-cookies.d.ts +0 -1
- package/dist/utils/redact-header-cookies.js +0 -11
- /package/dist/{utils/redact-header-cookies.test.d.ts → logger.test.d.ts} +0 -0
package/dist/mailer.js
CHANGED
|
@@ -11,12 +11,12 @@ let transporter;
|
|
|
11
11
|
function getMailer() {
|
|
12
12
|
if (transporter)
|
|
13
13
|
return transporter;
|
|
14
|
-
const transportName = env_1.default
|
|
14
|
+
const transportName = env_1.default['EMAIL_TRANSPORT'].toLowerCase();
|
|
15
15
|
if (transportName === 'sendmail') {
|
|
16
16
|
transporter = nodemailer_1.default.createTransport({
|
|
17
17
|
sendmail: true,
|
|
18
|
-
newline: env_1.default
|
|
19
|
-
path: env_1.default
|
|
18
|
+
newline: env_1.default['EMAIL_SENDMAIL_NEW_LINE'] || 'unix',
|
|
19
|
+
path: env_1.default['EMAIL_SENDMAIL_PATH'] || '/usr/sbin/sendmail',
|
|
20
20
|
});
|
|
21
21
|
}
|
|
22
22
|
else if (transportName === 'ses') {
|
|
@@ -29,20 +29,20 @@ function getMailer() {
|
|
|
29
29
|
}
|
|
30
30
|
else if (transportName === 'smtp') {
|
|
31
31
|
let auth = false;
|
|
32
|
-
if (env_1.default
|
|
32
|
+
if (env_1.default['EMAIL_SMTP_USER'] || env_1.default['EMAIL_SMTP_PASSWORD']) {
|
|
33
33
|
auth = {
|
|
34
|
-
user: env_1.default
|
|
35
|
-
pass: env_1.default
|
|
34
|
+
user: env_1.default['EMAIL_SMTP_USER'],
|
|
35
|
+
pass: env_1.default['EMAIL_SMTP_PASSWORD'],
|
|
36
36
|
};
|
|
37
37
|
}
|
|
38
38
|
const tls = (0, get_config_from_env_1.getConfigFromEnv)('EMAIL_SMTP_TLS_');
|
|
39
39
|
transporter = nodemailer_1.default.createTransport({
|
|
40
|
-
name: env_1.default
|
|
41
|
-
pool: env_1.default
|
|
42
|
-
host: env_1.default
|
|
43
|
-
port: env_1.default
|
|
44
|
-
secure: env_1.default
|
|
45
|
-
ignoreTLS: env_1.default
|
|
40
|
+
name: env_1.default['EMAIL_SMTP_NAME'],
|
|
41
|
+
pool: env_1.default['EMAIL_SMTP_POOL'],
|
|
42
|
+
host: env_1.default['EMAIL_SMTP_HOST'],
|
|
43
|
+
port: env_1.default['EMAIL_SMTP_PORT'],
|
|
44
|
+
secure: env_1.default['EMAIL_SMTP_SECURE'],
|
|
45
|
+
ignoreTLS: env_1.default['EMAIL_SMTP_IGNORE_TLS'],
|
|
46
46
|
auth,
|
|
47
47
|
tls,
|
|
48
48
|
});
|
|
@@ -51,16 +51,16 @@ function getMailer() {
|
|
|
51
51
|
const mg = require('nodemailer-mailgun-transport');
|
|
52
52
|
transporter = nodemailer_1.default.createTransport(mg({
|
|
53
53
|
auth: {
|
|
54
|
-
api_key: env_1.default
|
|
55
|
-
domain: env_1.default
|
|
54
|
+
api_key: env_1.default['EMAIL_MAILGUN_API_KEY'],
|
|
55
|
+
domain: env_1.default['EMAIL_MAILGUN_DOMAIN'],
|
|
56
56
|
},
|
|
57
|
-
host: env_1.default
|
|
57
|
+
host: env_1.default['EMAIL_MAILGUN_HOST'] || 'api.mailgun.net',
|
|
58
58
|
}));
|
|
59
59
|
}
|
|
60
60
|
else if (transportName === 'sendgrid') {
|
|
61
61
|
const sg = require('nodemailer-sendgrid');
|
|
62
62
|
transporter = nodemailer_1.default.createTransport(sg({
|
|
63
|
-
apiKey: env_1.default
|
|
63
|
+
apiKey: env_1.default['EMAIL_SENDGRID_API_KEY'],
|
|
64
64
|
}));
|
|
65
65
|
}
|
|
66
66
|
else {
|
package/dist/messenger.js
CHANGED
|
@@ -30,9 +30,9 @@ class MessengerRedis {
|
|
|
30
30
|
sub;
|
|
31
31
|
constructor() {
|
|
32
32
|
const config = (0, get_config_from_env_1.getConfigFromEnv)('MESSENGER_REDIS');
|
|
33
|
-
this.pub = new ioredis_1.default(env_1.default
|
|
34
|
-
this.sub = new ioredis_1.default(env_1.default
|
|
35
|
-
this.namespace = env_1.default
|
|
33
|
+
this.pub = new ioredis_1.default(env_1.default['MESSENGER_REDIS'] ?? config);
|
|
34
|
+
this.sub = new ioredis_1.default(env_1.default['MESSENGER_REDIS'] ?? config);
|
|
35
|
+
this.namespace = env_1.default['MESSENGER_NAMESPACE'] ?? 'directus';
|
|
36
36
|
}
|
|
37
37
|
publish(channel, payload) {
|
|
38
38
|
this.pub.publish(`${this.namespace}:${channel}`, JSON.stringify(payload));
|
|
@@ -55,7 +55,7 @@ let messenger;
|
|
|
55
55
|
function getMessenger() {
|
|
56
56
|
if (messenger)
|
|
57
57
|
return messenger;
|
|
58
|
-
if (env_1.default
|
|
58
|
+
if (env_1.default['MESSENGER_STORE'] === 'redis') {
|
|
59
59
|
messenger = new MessengerRedis();
|
|
60
60
|
}
|
|
61
61
|
else {
|
|
@@ -45,7 +45,7 @@ const handler = async (req, res, next) => {
|
|
|
45
45
|
req.accountability = defaultAccountability;
|
|
46
46
|
if (req.token) {
|
|
47
47
|
if ((0, is_directus_jwt_1.default)(req.token)) {
|
|
48
|
-
const payload = (0, jwt_1.verifyAccessJWT)(req.token, env_1.default
|
|
48
|
+
const payload = (0, jwt_1.verifyAccessJWT)(req.token, env_1.default['SECRET']);
|
|
49
49
|
req.accountability.role = payload.role;
|
|
50
50
|
req.accountability.admin = payload.admin_access === true || payload.admin_access == 1;
|
|
51
51
|
req.accountability.app = payload.app_access === true || payload.app_access == 1;
|
package/dist/middleware/cache.js
CHANGED
|
@@ -14,13 +14,13 @@ const checkCacheMiddleware = (0, async_handler_1.default)(async (req, res, next)
|
|
|
14
14
|
const { cache } = (0, cache_1.getCache)();
|
|
15
15
|
if (req.method.toLowerCase() !== 'get' && req.originalUrl?.startsWith('/graphql') === false)
|
|
16
16
|
return next();
|
|
17
|
-
if (env_1.default
|
|
17
|
+
if (env_1.default['CACHE_ENABLED'] !== true)
|
|
18
18
|
return next();
|
|
19
19
|
if (!cache)
|
|
20
20
|
return next();
|
|
21
21
|
if ((0, should_skip_cache_1.shouldSkipCache)(req)) {
|
|
22
|
-
if (env_1.default
|
|
23
|
-
res.setHeader(`${env_1.default
|
|
22
|
+
if (env_1.default['CACHE_STATUS_HEADER'])
|
|
23
|
+
res.setHeader(`${env_1.default['CACHE_STATUS_HEADER']}`, 'MISS');
|
|
24
24
|
return next();
|
|
25
25
|
}
|
|
26
26
|
const key = (0, get_cache_key_1.getCacheKey)(req);
|
|
@@ -30,8 +30,8 @@ const checkCacheMiddleware = (0, async_handler_1.default)(async (req, res, next)
|
|
|
30
30
|
}
|
|
31
31
|
catch (err) {
|
|
32
32
|
logger_1.default.warn(err, `[cache] Couldn't read key ${key}. ${err.message}`);
|
|
33
|
-
if (env_1.default
|
|
34
|
-
res.setHeader(`${env_1.default
|
|
33
|
+
if (env_1.default['CACHE_STATUS_HEADER'])
|
|
34
|
+
res.setHeader(`${env_1.default['CACHE_STATUS_HEADER']}`, 'MISS');
|
|
35
35
|
return next();
|
|
36
36
|
}
|
|
37
37
|
if (cachedData) {
|
|
@@ -41,20 +41,20 @@ const checkCacheMiddleware = (0, async_handler_1.default)(async (req, res, next)
|
|
|
41
41
|
}
|
|
42
42
|
catch (err) {
|
|
43
43
|
logger_1.default.warn(err, `[cache] Couldn't read key ${`${key}__expires_at`}. ${err.message}`);
|
|
44
|
-
if (env_1.default
|
|
45
|
-
res.setHeader(`${env_1.default
|
|
44
|
+
if (env_1.default['CACHE_STATUS_HEADER'])
|
|
45
|
+
res.setHeader(`${env_1.default['CACHE_STATUS_HEADER']}`, 'MISS');
|
|
46
46
|
return next();
|
|
47
47
|
}
|
|
48
48
|
const cacheTTL = cacheExpiryDate ? cacheExpiryDate - Date.now() : undefined;
|
|
49
49
|
res.setHeader('Cache-Control', (0, get_cache_headers_1.getCacheControlHeader)(req, cacheTTL, true, true));
|
|
50
50
|
res.setHeader('Vary', 'Origin, Cache-Control');
|
|
51
|
-
if (env_1.default
|
|
52
|
-
res.setHeader(`${env_1.default
|
|
51
|
+
if (env_1.default['CACHE_STATUS_HEADER'])
|
|
52
|
+
res.setHeader(`${env_1.default['CACHE_STATUS_HEADER']}`, 'HIT');
|
|
53
53
|
return res.json(cachedData);
|
|
54
54
|
}
|
|
55
55
|
else {
|
|
56
|
-
if (env_1.default
|
|
57
|
-
res.setHeader(`${env_1.default
|
|
56
|
+
if (env_1.default['CACHE_STATUS_HEADER'])
|
|
57
|
+
res.setHeader(`${env_1.default['CACHE_STATUS_HEADER']}`, 'MISS');
|
|
58
58
|
return next();
|
|
59
59
|
}
|
|
60
60
|
});
|
|
@@ -10,12 +10,12 @@ const collections_1 = require("../database/system-data/collections");
|
|
|
10
10
|
const exceptions_1 = require("../exceptions");
|
|
11
11
|
const async_handler_1 = __importDefault(require("../utils/async-handler"));
|
|
12
12
|
const collectionExists = (0, async_handler_1.default)(async (req, res, next) => {
|
|
13
|
-
if (!req.params
|
|
13
|
+
if (!req.params['collection'])
|
|
14
14
|
return next();
|
|
15
|
-
if (req.params
|
|
15
|
+
if (req.params['collection'] in req.schema.collections === false) {
|
|
16
16
|
throw new exceptions_1.ForbiddenException();
|
|
17
17
|
}
|
|
18
|
-
req.collection = req.params
|
|
18
|
+
req.collection = req.params['collection'];
|
|
19
19
|
if (req.collection.startsWith('directus_')) {
|
|
20
20
|
const systemRow = collections_1.systemCollectionRows.find((collection) => {
|
|
21
21
|
return collection?.collection === req.collection;
|
package/dist/middleware/cors.js
CHANGED
|
@@ -6,14 +6,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
const cors_1 = __importDefault(require("cors"));
|
|
7
7
|
const env_1 = __importDefault(require("../env"));
|
|
8
8
|
let corsMiddleware = (req, res, next) => next();
|
|
9
|
-
if (env_1.default
|
|
9
|
+
if (env_1.default['CORS_ENABLED'] === true) {
|
|
10
10
|
corsMiddleware = (0, cors_1.default)({
|
|
11
|
-
origin: env_1.default
|
|
12
|
-
methods: env_1.default
|
|
13
|
-
allowedHeaders: env_1.default
|
|
14
|
-
exposedHeaders: env_1.default
|
|
15
|
-
credentials: env_1.default
|
|
16
|
-
maxAge: env_1.default
|
|
11
|
+
origin: env_1.default['CORS_ORIGIN'] || true,
|
|
12
|
+
methods: env_1.default['CORS_METHODS'] || 'GET,POST,PATCH,DELETE',
|
|
13
|
+
allowedHeaders: env_1.default['CORS_ALLOWED_HEADERS'],
|
|
14
|
+
exposedHeaders: env_1.default['CORS_EXPOSED_HEADERS'],
|
|
15
|
+
credentials: env_1.default['CORS_CREDENTIALS'] || undefined,
|
|
16
|
+
maxAge: env_1.default['CORS_MAX_AGE'] || undefined,
|
|
17
17
|
});
|
|
18
18
|
}
|
|
19
19
|
exports.default = corsMiddleware;
|
|
@@ -32,7 +32,7 @@ const errorHandler = (err, req, res, _next) => {
|
|
|
32
32
|
res.status(status);
|
|
33
33
|
}
|
|
34
34
|
for (const err of errors) {
|
|
35
|
-
if (env_1.default
|
|
35
|
+
if (env_1.default['NODE_ENV'] === 'development') {
|
|
36
36
|
err.extensions = {
|
|
37
37
|
...(err.extensions || {}),
|
|
38
38
|
stack: err.stack,
|
|
@@ -49,7 +49,7 @@ const errorHandler = (err, req, res, _next) => {
|
|
|
49
49
|
},
|
|
50
50
|
});
|
|
51
51
|
if (err instanceof exceptions_2.MethodNotAllowedException) {
|
|
52
|
-
res.header('Allow', err.extensions
|
|
52
|
+
res.header('Allow', err.extensions['allow'].join(', '));
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
else {
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
11
|
const extractToken = (req, res, next) => {
|
|
12
12
|
let token = null;
|
|
13
|
-
if (req.query && req.query
|
|
14
|
-
token = req.query
|
|
13
|
+
if (req.query && req.query['access_token']) {
|
|
14
|
+
token = req.query['access_token'];
|
|
15
15
|
}
|
|
16
16
|
if (req.headers && req.headers.authorization) {
|
|
17
17
|
const parts = req.headers.authorization.split(' ');
|
|
@@ -17,10 +17,10 @@ exports.parseGraphQL = (0, async_handler_1.default)(async (req, res, next) => {
|
|
|
17
17
|
let operationName = null;
|
|
18
18
|
let document;
|
|
19
19
|
if (req.method === 'GET') {
|
|
20
|
-
query = req.query
|
|
21
|
-
if (req.query
|
|
20
|
+
query = req.query['query'] || null;
|
|
21
|
+
if (req.query['variables']) {
|
|
22
22
|
try {
|
|
23
|
-
variables = (0, utils_1.parseJSON)(req.query
|
|
23
|
+
variables = (0, utils_1.parseJSON)(req.query['variables']);
|
|
24
24
|
}
|
|
25
25
|
catch {
|
|
26
26
|
throw new exceptions_1.InvalidQueryException(`Variables are invalid JSON.`);
|
|
@@ -29,7 +29,7 @@ exports.parseGraphQL = (0, async_handler_1.default)(async (req, res, next) => {
|
|
|
29
29
|
else {
|
|
30
30
|
variables = {};
|
|
31
31
|
}
|
|
32
|
-
operationName = req.query
|
|
32
|
+
operationName = req.query['operationName'] || null;
|
|
33
33
|
}
|
|
34
34
|
else {
|
|
35
35
|
query = req.body.query || null;
|
|
@@ -56,8 +56,14 @@ exports.parseGraphQL = (0, async_handler_1.default)(async (req, res, next) => {
|
|
|
56
56
|
}
|
|
57
57
|
// Prevent caching responses when mutations are made
|
|
58
58
|
if (operationAST?.operation === 'mutation') {
|
|
59
|
-
res.locals
|
|
59
|
+
res.locals['cache'] = false;
|
|
60
60
|
}
|
|
61
|
-
res.locals
|
|
61
|
+
res.locals['graphqlParams'] = {
|
|
62
|
+
document,
|
|
63
|
+
query,
|
|
64
|
+
variables,
|
|
65
|
+
operationName,
|
|
66
|
+
contextValue: { req, res },
|
|
67
|
+
};
|
|
62
68
|
return next();
|
|
63
69
|
});
|
|
@@ -13,7 +13,7 @@ const async_handler_1 = __importDefault(require("../utils/async-handler"));
|
|
|
13
13
|
const validate_env_1 = require("../utils/validate-env");
|
|
14
14
|
const RATE_LIMITER_GLOBAL_KEY = 'global-rate-limit';
|
|
15
15
|
let checkRateLimit = (_req, _res, next) => next();
|
|
16
|
-
if (env_1.default
|
|
16
|
+
if (env_1.default['RATE_LIMITER_GLOBAL_ENABLED'] === true) {
|
|
17
17
|
(0, validate_env_1.validateEnv)(['RATE_LIMITER_GLOBAL_STORE', 'RATE_LIMITER_GLOBAL_DURATION', 'RATE_LIMITER_GLOBAL_POINTS']);
|
|
18
18
|
validateConfiguration();
|
|
19
19
|
exports.rateLimiterGlobal = (0, rate_limiter_1.createRateLimiter)('RATE_LIMITER_GLOBAL');
|
|
@@ -26,7 +26,7 @@ if (env_1.default.RATE_LIMITER_GLOBAL_ENABLED === true) {
|
|
|
26
26
|
throw rateLimiterRes;
|
|
27
27
|
res.set('Retry-After', String(Math.round(rateLimiterRes.msBeforeNext / 1000)));
|
|
28
28
|
throw new index_1.HitRateLimitException(`Too many requests, retry after ${(0, ms_1.default)(rateLimiterRes.msBeforeNext)}.`, {
|
|
29
|
-
limit: +env_1.default
|
|
29
|
+
limit: +env_1.default['RATE_LIMITER_GLOBAL_POINTS'],
|
|
30
30
|
reset: new Date(Date.now() + rateLimiterRes.msBeforeNext),
|
|
31
31
|
});
|
|
32
32
|
}
|
|
@@ -35,12 +35,12 @@ if (env_1.default.RATE_LIMITER_GLOBAL_ENABLED === true) {
|
|
|
35
35
|
}
|
|
36
36
|
exports.default = checkRateLimit;
|
|
37
37
|
function validateConfiguration() {
|
|
38
|
-
if (env_1.default
|
|
38
|
+
if (env_1.default['RATE_LIMITER_ENABLED'] !== true) {
|
|
39
39
|
logger_1.default.error(`The IP based rate limiter needs to be enabled when using the global rate limiter.`);
|
|
40
40
|
process.exit(1);
|
|
41
41
|
}
|
|
42
|
-
const globalPointsPerSec = Number(env_1.default
|
|
43
|
-
const regularPointsPerSec = Number(env_1.default
|
|
42
|
+
const globalPointsPerSec = Number(env_1.default['RATE_LIMITER_GLOBAL_POINTS']) / Math.max(Number(env_1.default['RATE_LIMITER_GLOBAL_DURATION']), 1);
|
|
43
|
+
const regularPointsPerSec = Number(env_1.default['RATE_LIMITER_POINTS']) / Math.max(Number(env_1.default['RATE_LIMITER_DURATION']), 1);
|
|
44
44
|
if (globalPointsPerSec <= regularPointsPerSec) {
|
|
45
45
|
logger_1.default.error(`The global rate limiter needs to allow more requests per second than the IP based rate limiter.`);
|
|
46
46
|
process.exit(1);
|
|
@@ -12,7 +12,7 @@ const async_handler_1 = __importDefault(require("../utils/async-handler"));
|
|
|
12
12
|
const get_ip_from_req_1 = require("../utils/get-ip-from-req");
|
|
13
13
|
const validate_env_1 = require("../utils/validate-env");
|
|
14
14
|
let checkRateLimit = (_req, _res, next) => next();
|
|
15
|
-
if (env_1.default
|
|
15
|
+
if (env_1.default['RATE_LIMITER_ENABLED'] === true) {
|
|
16
16
|
(0, validate_env_1.validateEnv)(['RATE_LIMITER_STORE', 'RATE_LIMITER_DURATION', 'RATE_LIMITER_POINTS']);
|
|
17
17
|
exports.rateLimiter = (0, rate_limiter_1.createRateLimiter)('RATE_LIMITER');
|
|
18
18
|
checkRateLimit = (0, async_handler_1.default)(async (req, res, next) => {
|
|
@@ -24,7 +24,7 @@ if (env_1.default.RATE_LIMITER_ENABLED === true) {
|
|
|
24
24
|
throw rateLimiterRes;
|
|
25
25
|
res.set('Retry-After', String(Math.round(rateLimiterRes.msBeforeNext / 1000)));
|
|
26
26
|
throw new exceptions_1.HitRateLimitException(`Too many requests, retry after ${(0, ms_1.default)(rateLimiterRes.msBeforeNext)}.`, {
|
|
27
|
-
limit: +env_1.default
|
|
27
|
+
limit: +env_1.default['RATE_LIMITER_POINTS'],
|
|
28
28
|
reset: new Date(Date.now() + rateLimiterRes.msBeforeNext),
|
|
29
29
|
});
|
|
30
30
|
}
|
|
@@ -18,26 +18,26 @@ const get_string_byte_size_1 = require("../utils/get-string-byte-size");
|
|
|
18
18
|
exports.respond = (0, async_handler_1.default)(async (req, res) => {
|
|
19
19
|
const { cache } = (0, cache_1.getCache)();
|
|
20
20
|
let exceedsMaxSize = false;
|
|
21
|
-
if (env_1.default
|
|
22
|
-
const valueSize = res.locals
|
|
23
|
-
const maxSize = (0, bytes_1.parse)(env_1.default
|
|
21
|
+
if (env_1.default['CACHE_VALUE_MAX_SIZE'] !== false) {
|
|
22
|
+
const valueSize = res.locals['payload'] ? (0, get_string_byte_size_1.stringByteSize)(JSON.stringify(res.locals['payload'])) : 0;
|
|
23
|
+
const maxSize = (0, bytes_1.parse)(env_1.default['CACHE_VALUE_MAX_SIZE']);
|
|
24
24
|
exceedsMaxSize = valueSize > maxSize;
|
|
25
25
|
}
|
|
26
26
|
if ((req.method.toLowerCase() === 'get' || req.originalUrl?.startsWith('/graphql')) &&
|
|
27
|
-
env_1.default
|
|
27
|
+
env_1.default['CACHE_ENABLED'] === true &&
|
|
28
28
|
cache &&
|
|
29
29
|
!req.sanitizedQuery.export &&
|
|
30
|
-
res.locals
|
|
30
|
+
res.locals['cache'] !== false &&
|
|
31
31
|
exceedsMaxSize === false) {
|
|
32
32
|
const key = (0, get_cache_key_1.getCacheKey)(req);
|
|
33
33
|
try {
|
|
34
|
-
await (0, cache_1.setCacheValue)(cache, key, res.locals
|
|
35
|
-
await (0, cache_1.setCacheValue)(cache, `${key}__expires_at`, { exp: Date.now() + (0, get_milliseconds_1.getMilliseconds)(env_1.default
|
|
34
|
+
await (0, cache_1.setCacheValue)(cache, key, res.locals['payload'], (0, get_milliseconds_1.getMilliseconds)(env_1.default['CACHE_TTL']));
|
|
35
|
+
await (0, cache_1.setCacheValue)(cache, `${key}__expires_at`, { exp: Date.now() + (0, get_milliseconds_1.getMilliseconds)(env_1.default['CACHE_TTL'], 0) });
|
|
36
36
|
}
|
|
37
37
|
catch (err) {
|
|
38
38
|
logger_1.default.warn(err, `[cache] Couldn't set key ${key}. ${err}`);
|
|
39
39
|
}
|
|
40
|
-
res.setHeader('Cache-Control', (0, get_cache_headers_1.getCacheControlHeader)(req, (0, get_milliseconds_1.getMilliseconds)(env_1.default
|
|
40
|
+
res.setHeader('Cache-Control', (0, get_cache_headers_1.getCacheControlHeader)(req, (0, get_milliseconds_1.getMilliseconds)(env_1.default['CACHE_TTL']), true, true));
|
|
41
41
|
res.setHeader('Vary', 'Origin, Cache-Control');
|
|
42
42
|
}
|
|
43
43
|
else {
|
|
@@ -58,29 +58,29 @@ exports.respond = (0, async_handler_1.default)(async (req, res) => {
|
|
|
58
58
|
if (req.sanitizedQuery.export === 'json') {
|
|
59
59
|
res.attachment(`${filename}.json`);
|
|
60
60
|
res.set('Content-Type', 'application/json');
|
|
61
|
-
return res.status(200).send(exportService.transform(res.locals
|
|
61
|
+
return res.status(200).send(exportService.transform(res.locals['payload']?.data, 'json'));
|
|
62
62
|
}
|
|
63
63
|
if (req.sanitizedQuery.export === 'xml') {
|
|
64
64
|
res.attachment(`${filename}.xml`);
|
|
65
65
|
res.set('Content-Type', 'text/xml');
|
|
66
|
-
return res.status(200).send(exportService.transform(res.locals
|
|
66
|
+
return res.status(200).send(exportService.transform(res.locals['payload']?.data, 'xml'));
|
|
67
67
|
}
|
|
68
68
|
if (req.sanitizedQuery.export === 'csv') {
|
|
69
69
|
res.attachment(`${filename}.csv`);
|
|
70
70
|
res.set('Content-Type', 'text/csv');
|
|
71
|
-
return res.status(200).send(exportService.transform(res.locals
|
|
71
|
+
return res.status(200).send(exportService.transform(res.locals['payload']?.data, 'csv'));
|
|
72
72
|
}
|
|
73
73
|
if (req.sanitizedQuery.export === 'yaml') {
|
|
74
74
|
res.attachment(`${filename}.yaml`);
|
|
75
75
|
res.set('Content-Type', 'text/yaml');
|
|
76
|
-
return res.status(200).send(exportService.transform(res.locals
|
|
76
|
+
return res.status(200).send(exportService.transform(res.locals['payload']?.data, 'yaml'));
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
|
-
if (Buffer.isBuffer(res.locals
|
|
80
|
-
return res.end(res.locals
|
|
79
|
+
if (Buffer.isBuffer(res.locals['payload'])) {
|
|
80
|
+
return res.end(res.locals['payload']);
|
|
81
81
|
}
|
|
82
|
-
else if (res.locals
|
|
83
|
-
return res.json(res.locals
|
|
82
|
+
else if (res.locals['payload']) {
|
|
83
|
+
return res.json(res.locals['payload']);
|
|
84
84
|
}
|
|
85
85
|
else {
|
|
86
86
|
return res.status(204).end();
|
|
@@ -11,7 +11,7 @@ const sanitizeQueryMiddleware = (req, _res, next) => {
|
|
|
11
11
|
if (!req.query)
|
|
12
12
|
return;
|
|
13
13
|
req.sanitizedQuery = (0, sanitize_query_1.sanitizeQuery)({
|
|
14
|
-
fields: req.query
|
|
14
|
+
fields: req.query['fields'] || '*',
|
|
15
15
|
...req.query,
|
|
16
16
|
}, req.accountability || null);
|
|
17
17
|
Object.freeze(req.sanitizedQuery);
|
|
@@ -6,10 +6,10 @@ const node_module_1 = require("node:module");
|
|
|
6
6
|
exports.default = (0, utils_1.defineOperationApi)({
|
|
7
7
|
id: 'exec',
|
|
8
8
|
handler: async ({ code }, { data, env }) => {
|
|
9
|
-
const allowedModules = env
|
|
9
|
+
const allowedModules = env['FLOWS_EXEC_ALLOWED_MODULES'] ? (0, utils_1.toArray)(env['FLOWS_EXEC_ALLOWED_MODULES']) : [];
|
|
10
10
|
const allowedModulesBuiltIn = [];
|
|
11
11
|
const allowedModulesExternal = [];
|
|
12
|
-
const allowedEnv = data
|
|
12
|
+
const allowedEnv = data['$env'] ?? {};
|
|
13
13
|
const opts = {
|
|
14
14
|
eval: false,
|
|
15
15
|
wasm: false,
|
package/dist/rate-limiter.js
CHANGED
|
@@ -9,7 +9,7 @@ const rate_limiter_flexible_1 = require("rate-limiter-flexible");
|
|
|
9
9
|
const env_1 = __importDefault(require("./env"));
|
|
10
10
|
const get_config_from_env_1 = require("./utils/get-config-from-env");
|
|
11
11
|
function createRateLimiter(configPrefix = 'RATE_LIMITER', configOverrides) {
|
|
12
|
-
switch (env_1.default
|
|
12
|
+
switch (env_1.default['RATE_LIMITER_STORE']) {
|
|
13
13
|
case 'redis':
|
|
14
14
|
return new rate_limiter_flexible_1.RateLimiterRedis(getConfig('redis', configPrefix, configOverrides));
|
|
15
15
|
case 'memcache':
|
|
@@ -8,10 +8,10 @@ const node_os_1 = __importDefault(require("node:os"));
|
|
|
8
8
|
const env_1 = require("../env");
|
|
9
9
|
const validateIP = async (ip, url) => {
|
|
10
10
|
const env = (0, env_1.getEnv)();
|
|
11
|
-
if (env
|
|
11
|
+
if (env['IMPORT_IP_DENY_LIST'].includes(ip)) {
|
|
12
12
|
throw new Error(`Requested URL "${url}" resolves to a denied IP address`);
|
|
13
13
|
}
|
|
14
|
-
if (env
|
|
14
|
+
if (env['IMPORT_IP_DENY_LIST'].includes('0.0.0.0')) {
|
|
15
15
|
const networkInterfaces = node_os_1.default.networkInterfaces();
|
|
16
16
|
for (const networkInfo of Object.values(networkInterfaces)) {
|
|
17
17
|
if (!networkInfo)
|
package/dist/server.js
CHANGED
|
@@ -106,7 +106,7 @@ async function createServer() {
|
|
|
106
106
|
(0, terminus_1.createTerminus)(server, terminusOptions);
|
|
107
107
|
return server;
|
|
108
108
|
async function beforeShutdown() {
|
|
109
|
-
if (env_1.default
|
|
109
|
+
if (env_1.default['NODE_ENV'] !== 'development') {
|
|
110
110
|
logger_1.default.info('Shutting down...');
|
|
111
111
|
}
|
|
112
112
|
}
|
|
@@ -121,7 +121,7 @@ async function createServer() {
|
|
|
121
121
|
schema: null,
|
|
122
122
|
accountability: null,
|
|
123
123
|
});
|
|
124
|
-
if (env_1.default
|
|
124
|
+
if (env_1.default['NODE_ENV'] !== 'development') {
|
|
125
125
|
logger_1.default.info('Directus shut down OK. Bye bye!');
|
|
126
126
|
}
|
|
127
127
|
}
|
|
@@ -129,8 +129,8 @@ async function createServer() {
|
|
|
129
129
|
exports.createServer = createServer;
|
|
130
130
|
async function startServer() {
|
|
131
131
|
const server = await createServer();
|
|
132
|
-
const host = env_1.default
|
|
133
|
-
const port = env_1.default
|
|
132
|
+
const host = env_1.default['HOST'];
|
|
133
|
+
const port = env_1.default['PORT'];
|
|
134
134
|
server
|
|
135
135
|
.listen(port, host, () => {
|
|
136
136
|
(0, update_check_1.default)(package_json_1.default)
|
|
@@ -26,9 +26,9 @@ class ActivityService extends items_1.ItemsService {
|
|
|
26
26
|
this.usersService = new users_1.UsersService({ schema: this.schema });
|
|
27
27
|
}
|
|
28
28
|
async createOne(data, opts) {
|
|
29
|
-
if (data
|
|
29
|
+
if (data['action'] === types_1.Action.COMMENT && typeof data['comment'] === 'string') {
|
|
30
30
|
const usersRegExp = new RegExp(/@[0-9A-F]{8}-[0-9A-F]{4}-4[0-9A-F]{3}-[89AB][0-9A-F]{3}-[0-9A-F]{12}/gi);
|
|
31
|
-
const mentions = (0, lodash_1.uniq)(data
|
|
31
|
+
const mentions = (0, lodash_1.uniq)(data['comment'].match(usersRegExp) ?? []);
|
|
32
32
|
const sender = await this.usersService.readOne(this.accountability.user, {
|
|
33
33
|
fields: ['id', 'first_name', 'last_name', 'email'],
|
|
34
34
|
});
|
|
@@ -39,24 +39,24 @@ class ActivityService extends items_1.ItemsService {
|
|
|
39
39
|
});
|
|
40
40
|
const accountability = {
|
|
41
41
|
user: userID,
|
|
42
|
-
role: user
|
|
43
|
-
admin: user
|
|
44
|
-
app: user
|
|
42
|
+
role: user['role']?.id ?? null,
|
|
43
|
+
admin: user['role']?.admin_access ?? null,
|
|
44
|
+
app: user['role']?.app_access ?? null,
|
|
45
45
|
};
|
|
46
46
|
accountability.permissions = await (0, get_permissions_1.getPermissions)(accountability, this.schema);
|
|
47
47
|
const authorizationService = new authorization_1.AuthorizationService({ schema: this.schema, accountability });
|
|
48
48
|
const usersService = new users_1.UsersService({ schema: this.schema, accountability });
|
|
49
49
|
try {
|
|
50
|
-
await authorizationService.checkAccess('read', data
|
|
50
|
+
await authorizationService.checkAccess('read', data['collection'], data['item']);
|
|
51
51
|
const templateData = await usersService.readByQuery({
|
|
52
52
|
fields: ['id', 'first_name', 'last_name', 'email'],
|
|
53
53
|
filter: { id: { _in: mentions.map((mention) => mention.substring(1)) } },
|
|
54
54
|
});
|
|
55
55
|
const userPreviews = templateData.reduce((acc, user) => {
|
|
56
|
-
acc[user
|
|
56
|
+
acc[user['id']] = `<em>${(0, user_name_1.userName)(user)}</em>`;
|
|
57
57
|
return acc;
|
|
58
58
|
}, {});
|
|
59
|
-
let comment = data
|
|
59
|
+
let comment = data['comment'];
|
|
60
60
|
for (const mention of mentions) {
|
|
61
61
|
const uuid = mention.substring(1);
|
|
62
62
|
// We only match on UUIDs in the first place. This is just an extra sanity check
|
|
@@ -72,17 +72,17 @@ ${(0, user_name_1.userName)(sender)} has mentioned you in a comment:
|
|
|
72
72
|
|
|
73
73
|
${comment}
|
|
74
74
|
|
|
75
|
-
<a href="${new url_1.Url(env_1.default
|
|
76
|
-
.addPath('admin', 'content', data
|
|
75
|
+
<a href="${new url_1.Url(env_1.default['PUBLIC_URL'])
|
|
76
|
+
.addPath('admin', 'content', data['collection'], data['item'])
|
|
77
77
|
.toString()}">Click here to view.</a>
|
|
78
78
|
`;
|
|
79
79
|
await this.notificationsService.createOne({
|
|
80
80
|
recipient: userID,
|
|
81
|
-
sender: sender
|
|
82
|
-
subject: `You were mentioned in ${data
|
|
81
|
+
sender: sender['id'],
|
|
82
|
+
subject: `You were mentioned in ${data['collection']}`,
|
|
83
83
|
message,
|
|
84
|
-
collection: data
|
|
85
|
-
item: data
|
|
84
|
+
collection: data['collection'],
|
|
85
|
+
item: data['item'],
|
|
86
86
|
});
|
|
87
87
|
}
|
|
88
88
|
catch (err) {
|
package/dist/services/assets.js
CHANGED
|
@@ -131,24 +131,24 @@ class AssetsService {
|
|
|
131
131
|
const { width, height } = file;
|
|
132
132
|
if (!width ||
|
|
133
133
|
!height ||
|
|
134
|
-
width > env_1.default
|
|
135
|
-
height > env_1.default
|
|
134
|
+
width > env_1.default['ASSETS_TRANSFORM_IMAGE_MAX_DIMENSION'] ||
|
|
135
|
+
height > env_1.default['ASSETS_TRANSFORM_IMAGE_MAX_DIMENSION']) {
|
|
136
136
|
throw new exceptions_1.IllegalAssetTransformation(`Image is too large to be transformed, or image size couldn't be determined.`);
|
|
137
137
|
}
|
|
138
138
|
const { queue, process } = sharp_1.default.counters();
|
|
139
|
-
if (queue + process > env_1.default
|
|
139
|
+
if (queue + process > env_1.default['ASSETS_TRANSFORM_MAX_CONCURRENT']) {
|
|
140
140
|
throw new service_unavailable_1.ServiceUnavailableException('Server too busy', {
|
|
141
141
|
service: 'files',
|
|
142
142
|
});
|
|
143
143
|
}
|
|
144
144
|
const readStream = await storage.location(file.storage).read(file.filename_disk, range);
|
|
145
145
|
const transformer = (0, sharp_1.default)({
|
|
146
|
-
limitInputPixels: Math.pow(env_1.default
|
|
146
|
+
limitInputPixels: Math.pow(env_1.default['ASSETS_TRANSFORM_IMAGE_MAX_DIMENSION'], 2),
|
|
147
147
|
sequentialRead: true,
|
|
148
|
-
failOn: env_1.default
|
|
148
|
+
failOn: env_1.default['ASSETS_INVALID_IMAGE_SENSITIVITY_LEVEL'],
|
|
149
149
|
});
|
|
150
150
|
transformer.timeout({
|
|
151
|
-
seconds: (0, lodash_1.clamp)(Math.round((0, get_milliseconds_1.getMilliseconds)(env_1.default
|
|
151
|
+
seconds: (0, lodash_1.clamp)(Math.round((0, get_milliseconds_1.getMilliseconds)(env_1.default['ASSETS_TRANSFORM_TIMEOUT'], 0) / 1000), 1, 3600),
|
|
152
152
|
});
|
|
153
153
|
if (transforms.find((transform) => transform[0] === 'rotate') === undefined)
|
|
154
154
|
transformer.rotate();
|
|
@@ -40,7 +40,7 @@ class AuthenticationService {
|
|
|
40
40
|
*/
|
|
41
41
|
async login(providerName = constants_1.DEFAULT_AUTH_PROVIDER, payload, otp) {
|
|
42
42
|
const { nanoid } = await import('nanoid');
|
|
43
|
-
const STALL_TIME = env_1.default
|
|
43
|
+
const STALL_TIME = env_1.default['LOGIN_STALL_TIME'];
|
|
44
44
|
const timeStart = perf_hooks_1.performance.now();
|
|
45
45
|
const provider = (0, auth_1.getAuthProvider)(providerName);
|
|
46
46
|
let userId;
|
|
@@ -150,12 +150,12 @@ class AuthenticationService {
|
|
|
150
150
|
schema: this.schema,
|
|
151
151
|
accountability: this.accountability,
|
|
152
152
|
});
|
|
153
|
-
const accessToken = jsonwebtoken_1.default.sign(customClaims, env_1.default
|
|
154
|
-
expiresIn: env_1.default
|
|
153
|
+
const accessToken = jsonwebtoken_1.default.sign(customClaims, env_1.default['SECRET'], {
|
|
154
|
+
expiresIn: env_1.default['ACCESS_TOKEN_TTL'],
|
|
155
155
|
issuer: 'directus',
|
|
156
156
|
});
|
|
157
157
|
const refreshToken = nanoid(64);
|
|
158
|
-
const refreshTokenExpiration = new Date(Date.now() + (0, get_milliseconds_1.getMilliseconds)(env_1.default
|
|
158
|
+
const refreshTokenExpiration = new Date(Date.now() + (0, get_milliseconds_1.getMilliseconds)(env_1.default['REFRESH_TOKEN_TTL'], 0));
|
|
159
159
|
await this.knex('directus_sessions').insert({
|
|
160
160
|
token: refreshToken,
|
|
161
161
|
user: user.id,
|
|
@@ -185,7 +185,7 @@ class AuthenticationService {
|
|
|
185
185
|
return {
|
|
186
186
|
accessToken,
|
|
187
187
|
refreshToken,
|
|
188
|
-
expires: (0, get_milliseconds_1.getMilliseconds)(env_1.default
|
|
188
|
+
expires: (0, get_milliseconds_1.getMilliseconds)(env_1.default['ACCESS_TOKEN_TTL']),
|
|
189
189
|
id: user.id,
|
|
190
190
|
};
|
|
191
191
|
}
|
|
@@ -280,12 +280,12 @@ class AuthenticationService {
|
|
|
280
280
|
schema: this.schema,
|
|
281
281
|
accountability: this.accountability,
|
|
282
282
|
});
|
|
283
|
-
const accessToken = jsonwebtoken_1.default.sign(customClaims, env_1.default
|
|
284
|
-
expiresIn: env_1.default
|
|
283
|
+
const accessToken = jsonwebtoken_1.default.sign(customClaims, env_1.default['SECRET'], {
|
|
284
|
+
expiresIn: env_1.default['ACCESS_TOKEN_TTL'],
|
|
285
285
|
issuer: 'directus',
|
|
286
286
|
});
|
|
287
287
|
const newRefreshToken = nanoid(64);
|
|
288
|
-
const refreshTokenExpiration = new Date(Date.now() + (0, get_milliseconds_1.getMilliseconds)(env_1.default
|
|
288
|
+
const refreshTokenExpiration = new Date(Date.now() + (0, get_milliseconds_1.getMilliseconds)(env_1.default['REFRESH_TOKEN_TTL'], 0));
|
|
289
289
|
await this.knex('directus_sessions')
|
|
290
290
|
.update({
|
|
291
291
|
token: newRefreshToken,
|
|
@@ -298,7 +298,7 @@ class AuthenticationService {
|
|
|
298
298
|
return {
|
|
299
299
|
accessToken,
|
|
300
300
|
refreshToken: newRefreshToken,
|
|
301
|
-
expires: (0, get_milliseconds_1.getMilliseconds)(env_1.default
|
|
301
|
+
expires: (0, get_milliseconds_1.getMilliseconds)(env_1.default['ACCESS_TOKEN_TTL']),
|
|
302
302
|
id: record.user_id,
|
|
303
303
|
};
|
|
304
304
|
}
|