@directus/api 15.0.0 → 17.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/permissions.js +11 -2
- package/dist/controllers/schema.js +3 -2
- package/dist/controllers/shares.js +3 -3
- package/dist/controllers/utils.js +13 -32
- 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 +5 -3
- 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 -6
- package/dist/services/authentication.js +17 -9
- package/dist/services/authorization.d.ts +1 -1
- package/dist/services/authorization.js +15 -3
- 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.d.ts → import-export.d.ts} +1 -1
- package/dist/services/{import-export/index.js → import-export.js} +14 -12
- package/dist/services/index.d.ts +1 -1
- package/dist/services/index.js +1 -1
- 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/permissions.d.ts +3 -2
- package/dist/services/permissions.js +76 -1
- package/dist/services/relations.js +19 -10
- package/dist/services/roles.js +83 -15
- package/dist/services/server.js +7 -5
- 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/types/items.d.ts +4 -12
- package/dist/types/items.js +0 -4
- 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/services/import-export/import-worker.d.ts +0 -9
- package/dist/services/import-export/import-worker.js +0 -9
- package/dist/utils/to-boolean.d.ts +0 -4
- package/dist/utils/to-boolean.js +0 -6
- package/dist/worker-pool.d.ts +0 -2
- package/dist/worker-pool.js +0 -19
- /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,9 +1,9 @@
|
|
|
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
|
|
6
|
-
import { ExportService } from '../services/import-export/index.js';
|
|
5
|
+
import { useLogger } from '../logger.js';
|
|
6
|
+
import { ExportService } from '../services/import-export.js';
|
|
7
7
|
import { VersionsService } from '../services/versions.js';
|
|
8
8
|
import asyncHandler from '../utils/async-handler.js';
|
|
9
9
|
import { getCacheControlHeader } from '../utils/get-cache-headers.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;
|
package/dist/redis/index.d.ts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export {
|
|
1
|
+
export { createRedis } from './lib/create-redis.js';
|
|
2
|
+
export { useRedis } from './lib/use-redis.js';
|
|
3
|
+
export { redisConfigAvailable } from './utils/redis-config-available.js';
|
package/dist/redis/index.js
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export {
|
|
1
|
+
export { createRedis } from './lib/create-redis.js';
|
|
2
|
+
export { useRedis } from './lib/use-redis.js';
|
|
3
|
+
export { redisConfigAvailable } from './utils/redis-config-available.js';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { useEnv } from '@directus/env';
|
|
1
2
|
import { Redis } from 'ioredis';
|
|
2
|
-
import {
|
|
3
|
-
import { getConfigFromEnv } from '../utils/get-config-from-env.js';
|
|
3
|
+
import { getConfigFromEnv } from '../../utils/get-config-from-env.js';
|
|
4
4
|
/**
|
|
5
5
|
* Create a new Redis instance based on the global env configuration
|
|
6
6
|
*
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { useEnv } from '@directus/env';
|
|
2
|
+
/**
|
|
3
|
+
* Check if Redis configuration exists in the current project's environment configuration
|
|
4
|
+
*/
|
|
5
|
+
export const redisConfigAvailable = () => {
|
|
6
|
+
const env = useEnv();
|
|
7
|
+
return 'REDIS' in env || Object.keys(env).some((key) => key.startsWith('REDIS_'));
|
|
8
|
+
};
|
|
@@ -2,9 +2,10 @@ import axios from 'axios';
|
|
|
2
2
|
import { lookup } from 'node:dns/promises';
|
|
3
3
|
import { isIP } from 'node:net';
|
|
4
4
|
import { URL } from 'node:url';
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
5
|
+
import { useLogger } from '../logger.js';
|
|
6
|
+
import { validateIp } from './validate-ip.js';
|
|
7
7
|
export const requestInterceptor = async (config) => {
|
|
8
|
+
const logger = useLogger();
|
|
8
9
|
const uri = axios.getUri(config);
|
|
9
10
|
const { hostname } = new URL(uri);
|
|
10
11
|
let ip;
|
|
@@ -13,14 +14,15 @@ export const requestInterceptor = async (config) => {
|
|
|
13
14
|
const dns = await lookup(hostname);
|
|
14
15
|
ip = dns.address;
|
|
15
16
|
}
|
|
16
|
-
catch (
|
|
17
|
-
logger.warn(
|
|
17
|
+
catch (error) {
|
|
18
|
+
logger.warn(`Couldn't lookup the DNS for URL "${uri}"`);
|
|
19
|
+
logger.warn(error);
|
|
18
20
|
throw new Error(`Requested URL "${uri}" resolves to a denied IP address`);
|
|
19
21
|
}
|
|
20
22
|
}
|
|
21
23
|
else {
|
|
22
24
|
ip = hostname;
|
|
23
25
|
}
|
|
24
|
-
|
|
26
|
+
validateIp(ip, uri);
|
|
25
27
|
return config;
|
|
26
28
|
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { validateIp } from './validate-ip.js';
|
|
2
2
|
export const responseInterceptor = async (config) => {
|
|
3
|
-
|
|
3
|
+
validateIp(config.request.socket.remoteAddress, config.request.url);
|
|
4
4
|
return config;
|
|
5
5
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare
|
|
1
|
+
export declare function validateIp(ip: string, url: string): void;
|
|
@@ -1,19 +1,35 @@
|
|
|
1
|
+
import { useEnv } from '@directus/env';
|
|
1
2
|
import os from 'node:os';
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
import { useLogger } from '../logger.js';
|
|
4
|
+
import { ipInNetworks } from '../utils/ip-in-networks.js';
|
|
5
|
+
const deniedError = (url) => new Error(`Requested URL "${url}" resolves to a denied IP address`);
|
|
6
|
+
export function validateIp(ip, url) {
|
|
7
|
+
const env = useEnv();
|
|
8
|
+
const logger = useLogger();
|
|
9
|
+
const ipDenyList = env['IMPORT_IP_DENY_LIST'];
|
|
10
|
+
if (ipDenyList.length === 0)
|
|
11
|
+
return;
|
|
12
|
+
let denied;
|
|
13
|
+
try {
|
|
14
|
+
denied = ipInNetworks(ip, ipDenyList);
|
|
6
15
|
}
|
|
7
|
-
|
|
16
|
+
catch (error) {
|
|
17
|
+
logger.warn(`Invalid "IMPORT_IP_DENY_LIST" configuration`);
|
|
18
|
+
logger.warn(error);
|
|
19
|
+
throw deniedError(url);
|
|
20
|
+
}
|
|
21
|
+
if (denied)
|
|
22
|
+
throw deniedError(url);
|
|
23
|
+
if (ipDenyList.includes('0.0.0.0')) {
|
|
8
24
|
const networkInterfaces = os.networkInterfaces();
|
|
9
25
|
for (const networkInfo of Object.values(networkInterfaces)) {
|
|
10
26
|
if (!networkInfo)
|
|
11
27
|
continue;
|
|
12
28
|
for (const info of networkInfo) {
|
|
13
29
|
if (info.address === ip) {
|
|
14
|
-
throw
|
|
30
|
+
throw deniedError(url);
|
|
15
31
|
}
|
|
16
32
|
}
|
|
17
33
|
}
|
|
18
34
|
}
|
|
19
|
-
}
|
|
35
|
+
}
|
package/dist/server.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { useEnv } from '@directus/env';
|
|
2
|
+
import { toBoolean } from '@directus/utils';
|
|
3
|
+
import { getNodeEnv } from '@directus/utils/node';
|
|
1
4
|
import { createTerminus } from '@godaddy/terminus';
|
|
2
5
|
import * as http from 'http';
|
|
3
6
|
import * as https from 'https';
|
|
@@ -7,13 +10,14 @@ import url from 'url';
|
|
|
7
10
|
import createApp from './app.js';
|
|
8
11
|
import getDatabase from './database/index.js';
|
|
9
12
|
import emitter from './emitter.js';
|
|
10
|
-
import
|
|
11
|
-
import logger from './logger.js';
|
|
13
|
+
import { useLogger } from './logger.js';
|
|
12
14
|
import { getConfigFromEnv } from './utils/get-config-from-env.js';
|
|
13
|
-
import {
|
|
15
|
+
import { getIPFromReq } from './utils/get-ip-from-req.js';
|
|
14
16
|
import { createSubscriptionController, createWebSocketController, getSubscriptionController, getWebSocketController, } from './websocket/controllers/index.js';
|
|
15
17
|
import { startWebSocketHandlers } from './websocket/handlers/index.js';
|
|
16
18
|
export let SERVER_ONLINE = true;
|
|
19
|
+
const env = useEnv();
|
|
20
|
+
const logger = useLogger();
|
|
17
21
|
export async function createServer() {
|
|
18
22
|
const server = http.createServer(await createApp());
|
|
19
23
|
Object.assign(server, getConfigFromEnv('SERVER_'));
|
|
@@ -57,7 +61,7 @@ export async function createServer() {
|
|
|
57
61
|
size: metrics.out,
|
|
58
62
|
headers: res.getHeaders(),
|
|
59
63
|
},
|
|
60
|
-
ip: req
|
|
64
|
+
ip: getIPFromReq(req),
|
|
61
65
|
duration: elapsedMilliseconds.toFixed(),
|
|
62
66
|
};
|
|
63
67
|
emitter.emitAction('response', info, {
|
|
@@ -86,7 +90,7 @@ export async function createServer() {
|
|
|
86
90
|
createTerminus(server, terminusOptions);
|
|
87
91
|
return server;
|
|
88
92
|
async function beforeShutdown() {
|
|
89
|
-
if (
|
|
93
|
+
if (getNodeEnv() !== 'development') {
|
|
90
94
|
logger.info('Shutting down...');
|
|
91
95
|
}
|
|
92
96
|
SERVER_ONLINE = false;
|
|
@@ -104,7 +108,7 @@ export async function createServer() {
|
|
|
104
108
|
schema: null,
|
|
105
109
|
accountability: null,
|
|
106
110
|
});
|
|
107
|
-
if (
|
|
111
|
+
if (getNodeEnv() !== 'development') {
|
|
108
112
|
logger.info('Directus shut down OK. Bye bye!');
|
|
109
113
|
}
|
|
110
114
|
}
|
|
@@ -112,7 +116,7 @@ export async function createServer() {
|
|
|
112
116
|
export async function startServer() {
|
|
113
117
|
const server = await createServer();
|
|
114
118
|
const host = env['HOST'];
|
|
115
|
-
const port = env['PORT'];
|
|
119
|
+
const port = parseInt(env['PORT']);
|
|
116
120
|
server
|
|
117
121
|
.listen(port, host, () => {
|
|
118
122
|
logger.info(`Server started at http://${host}:${port}`);
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { Action } from '@directus/constants';
|
|
2
|
-
import {
|
|
2
|
+
import { useEnv } from '@directus/env';
|
|
3
|
+
import { ErrorCode, isDirectusError } from '@directus/errors';
|
|
3
4
|
import { uniq } from 'lodash-es';
|
|
4
5
|
import validateUUID from 'uuid-validate';
|
|
5
|
-
import
|
|
6
|
-
import { ErrorCode } from '@directus/errors';
|
|
7
|
-
import logger from '../logger.js';
|
|
6
|
+
import { useLogger } from '../logger.js';
|
|
8
7
|
import { getPermissions } from '../utils/get-permissions.js';
|
|
9
8
|
import { Url } from '../utils/url.js';
|
|
10
9
|
import { userName } from '../utils/user-name.js';
|
|
@@ -12,6 +11,8 @@ import { AuthorizationService } from './authorization.js';
|
|
|
12
11
|
import { ItemsService } from './items.js';
|
|
13
12
|
import { NotificationsService } from './notifications.js';
|
|
14
13
|
import { UsersService } from './users.js';
|
|
14
|
+
const env = useEnv();
|
|
15
|
+
const logger = useLogger();
|
|
15
16
|
export class ActivityService extends ItemsService {
|
|
16
17
|
notificationsService;
|
|
17
18
|
usersService;
|
|
@@ -5,10 +5,12 @@ import type { Knex } from 'knex';
|
|
|
5
5
|
import type { Readable } from 'node:stream';
|
|
6
6
|
import type { AbstractServiceOptions, TransformationSet } from '../types/index.js';
|
|
7
7
|
import { AuthorizationService } from './authorization.js';
|
|
8
|
+
import { FilesService } from './files.js';
|
|
8
9
|
export declare class AssetsService {
|
|
9
10
|
knex: Knex;
|
|
10
11
|
accountability: Accountability | null;
|
|
11
12
|
authorizationService: AuthorizationService;
|
|
13
|
+
filesService: FilesService;
|
|
12
14
|
constructor(options: AbstractServiceOptions);
|
|
13
15
|
getAsset(id: string, transformation?: TransformationSet, range?: Range): Promise<{
|
|
14
16
|
stream: Readable;
|
package/dist/services/assets.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { useEnv } from '@directus/env';
|
|
2
|
+
import { ForbiddenError, IllegalAssetTransformationError, RangeNotSatisfiableError, ServiceUnavailableError, } from '@directus/errors';
|
|
1
3
|
import { clamp } from 'lodash-es';
|
|
2
4
|
import { contentType } from 'mime-types';
|
|
3
5
|
import hash from 'object-hash';
|
|
@@ -6,20 +8,23 @@ import sharp from 'sharp';
|
|
|
6
8
|
import validateUUID from 'uuid-validate';
|
|
7
9
|
import { SUPPORTED_IMAGE_TRANSFORM_FORMATS } from '../constants.js';
|
|
8
10
|
import getDatabase from '../database/index.js';
|
|
9
|
-
import
|
|
10
|
-
import { ForbiddenError, IllegalAssetTransformationError, RangeNotSatisfiableError, ServiceUnavailableError, } from '@directus/errors';
|
|
11
|
-
import logger from '../logger.js';
|
|
11
|
+
import { useLogger } from '../logger.js';
|
|
12
12
|
import { getStorage } from '../storage/index.js';
|
|
13
13
|
import { getMilliseconds } from '../utils/get-milliseconds.js';
|
|
14
14
|
import * as TransformationUtils from '../utils/transformations.js';
|
|
15
15
|
import { AuthorizationService } from './authorization.js';
|
|
16
|
+
import { FilesService } from './files.js';
|
|
17
|
+
const env = useEnv();
|
|
18
|
+
const logger = useLogger();
|
|
16
19
|
export class AssetsService {
|
|
17
20
|
knex;
|
|
18
21
|
accountability;
|
|
19
22
|
authorizationService;
|
|
23
|
+
filesService;
|
|
20
24
|
constructor(options) {
|
|
21
25
|
this.knex = options.knex || getDatabase();
|
|
22
26
|
this.accountability = options.accountability || null;
|
|
27
|
+
this.filesService = new FilesService({ ...options, accountability: null });
|
|
23
28
|
this.authorizationService = new AuthorizationService(options);
|
|
24
29
|
}
|
|
25
30
|
async getAsset(id, transformation, range) {
|
|
@@ -40,9 +45,7 @@ export class AssetsService {
|
|
|
40
45
|
if (systemPublicKeys.includes(id) === false && this.accountability?.admin !== true) {
|
|
41
46
|
await this.authorizationService.checkAccess('read', 'directus_files', id);
|
|
42
47
|
}
|
|
43
|
-
const file = (await this.
|
|
44
|
-
if (!file)
|
|
45
|
-
throw new ForbiddenError();
|
|
48
|
+
const file = (await this.filesService.readOne(id, { limit: 1 }));
|
|
46
49
|
const exists = await storage.location(file.storage).exists(file.filename_disk);
|
|
47
50
|
if (!exists)
|
|
48
51
|
throw new ForbiddenError();
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { Action } from '@directus/constants';
|
|
2
|
+
import { useEnv } from '@directus/env';
|
|
3
|
+
import { InvalidCredentialsError, InvalidOtpError, InvalidProviderError, ServiceUnavailableError, UserSuspendedError, } from '@directus/errors';
|
|
2
4
|
import jwt from 'jsonwebtoken';
|
|
3
5
|
import { clone, cloneDeep } from 'lodash-es';
|
|
4
6
|
import { performance } from 'perf_hooks';
|
|
@@ -6,15 +8,13 @@ import { getAuthProvider } from '../auth.js';
|
|
|
6
8
|
import { DEFAULT_AUTH_PROVIDER } from '../constants.js';
|
|
7
9
|
import getDatabase from '../database/index.js';
|
|
8
10
|
import emitter from '../emitter.js';
|
|
9
|
-
import
|
|
10
|
-
import { InvalidCredentialsError, InvalidProviderError, UserSuspendedError } from '@directus/errors';
|
|
11
|
-
import { InvalidOtpError } from '@directus/errors';
|
|
12
|
-
import { createRateLimiter } from '../rate-limiter.js';
|
|
11
|
+
import { RateLimiterRes, createRateLimiter } from '../rate-limiter.js';
|
|
13
12
|
import { getMilliseconds } from '../utils/get-milliseconds.js';
|
|
14
13
|
import { stall } from '../utils/stall.js';
|
|
15
14
|
import { ActivityService } from './activity.js';
|
|
16
15
|
import { SettingsService } from './settings.js';
|
|
17
16
|
import { TFAService } from './tfa.js';
|
|
17
|
+
const env = useEnv();
|
|
18
18
|
const loginAttemptsLimiter = createRateLimiter('RATE_LIMITER', { duration: 0 });
|
|
19
19
|
export class AuthenticationService {
|
|
20
20
|
knex;
|
|
@@ -100,11 +100,19 @@ export class AuthenticationService {
|
|
|
100
100
|
try {
|
|
101
101
|
await loginAttemptsLimiter.consume(user.id);
|
|
102
102
|
}
|
|
103
|
-
catch {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
103
|
+
catch (error) {
|
|
104
|
+
if (error instanceof RateLimiterRes && error.remainingPoints === 0) {
|
|
105
|
+
await this.knex('directus_users').update({ status: 'suspended' }).where({ id: user.id });
|
|
106
|
+
user.status = 'suspended';
|
|
107
|
+
// This means that new attempts after the user has been re-activated will be accepted
|
|
108
|
+
await loginAttemptsLimiter.set(user.id, 0, 0);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
throw new ServiceUnavailableError({
|
|
112
|
+
service: 'authentication',
|
|
113
|
+
reason: 'Rate limiter unreachable',
|
|
114
|
+
});
|
|
115
|
+
}
|
|
108
116
|
}
|
|
109
117
|
}
|
|
110
118
|
try {
|
|
@@ -13,5 +13,5 @@ export declare class AuthorizationService {
|
|
|
13
13
|
* Checks if the provided payload matches the configured permissions, and adds the presets to the payload.
|
|
14
14
|
*/
|
|
15
15
|
validatePayload(action: PermissionsAction, collection: string, data: Partial<Item>): Partial<Item>;
|
|
16
|
-
checkAccess(action: PermissionsAction, collection: string, pk
|
|
16
|
+
checkAccess(action: PermissionsAction, collection: string, pk?: PrimaryKey | PrimaryKey[]): Promise<void>;
|
|
17
17
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import { ForbiddenError } from '@directus/errors';
|
|
1
2
|
import { validatePayload } from '@directus/utils';
|
|
2
3
|
import { FailedValidationError, joiValidationErrorItemToErrorExtensions } from '@directus/validation';
|
|
3
4
|
import { cloneDeep, flatten, isArray, isNil, merge, reduce, uniq, uniqWith } from 'lodash-es';
|
|
4
5
|
import { GENERATE_SPECIAL } from '../constants.js';
|
|
5
6
|
import getDatabase from '../database/index.js';
|
|
6
|
-
import { ForbiddenError } from '@directus/errors';
|
|
7
7
|
import { getRelationInfo } from '../utils/get-relation-info.js';
|
|
8
8
|
import { stripFunction } from '../utils/strip-function.js';
|
|
9
9
|
import { ItemsService } from './items.js';
|
|
@@ -430,15 +430,27 @@ export class AuthorizationService {
|
|
|
430
430
|
};
|
|
431
431
|
if (Array.isArray(pk)) {
|
|
432
432
|
const result = await itemsService.readMany(pk, { ...query, limit: pk.length }, { permissionsAction: action });
|
|
433
|
-
|
|
433
|
+
// for the unexpected case that the result is not an array (for example due to filter hook)
|
|
434
|
+
if (!isArray(result))
|
|
434
435
|
throw new ForbiddenError();
|
|
435
436
|
if (result.length !== pk.length)
|
|
436
437
|
throw new ForbiddenError();
|
|
437
438
|
}
|
|
438
|
-
else {
|
|
439
|
+
else if (pk) {
|
|
439
440
|
const result = await itemsService.readOne(pk, query, { permissionsAction: action });
|
|
440
441
|
if (!result)
|
|
441
442
|
throw new ForbiddenError();
|
|
442
443
|
}
|
|
444
|
+
else {
|
|
445
|
+
query.limit = 1;
|
|
446
|
+
const result = await itemsService.readByQuery(query, { permissionsAction: action });
|
|
447
|
+
// for the unexpected case that the result is not an array (for example due to filter hook)
|
|
448
|
+
if (!isArray(result))
|
|
449
|
+
throw new ForbiddenError();
|
|
450
|
+
// for create action, an empty array is expected - for other actions, the first item is expected to be available
|
|
451
|
+
const access = action === 'create' ? result.length === 0 : !!result[0];
|
|
452
|
+
if (!access)
|
|
453
|
+
throw new ForbiddenError();
|
|
454
|
+
}
|
|
443
455
|
}
|
|
444
456
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { useEnv } from '@directus/env';
|
|
2
|
+
import { ForbiddenError, InvalidPayloadError } from '@directus/errors';
|
|
1
3
|
import { createInspector } from '@directus/schema';
|
|
2
4
|
import { addFieldFlag } from '@directus/utils';
|
|
3
5
|
import { chunk, groupBy, merge, omit } from 'lodash-es';
|
|
@@ -7,12 +9,10 @@ import { getHelpers } from '../database/helpers/index.js';
|
|
|
7
9
|
import getDatabase, { getSchemaInspector } from '../database/index.js';
|
|
8
10
|
import { systemCollectionRows } from '../database/system-data/collections/index.js';
|
|
9
11
|
import emitter from '../emitter.js';
|
|
10
|
-
import env from '../env.js';
|
|
11
|
-
import { ForbiddenError, InvalidPayloadError } from '@directus/errors';
|
|
12
|
-
import { FieldsService } from './fields.js';
|
|
13
|
-
import { ItemsService } from './items.js';
|
|
14
12
|
import { getSchema } from '../utils/get-schema.js';
|
|
15
13
|
import { shouldClearCache } from '../utils/should-clear-cache.js';
|
|
14
|
+
import { FieldsService } from './fields.js';
|
|
15
|
+
import { ItemsService } from './items.js';
|
|
16
16
|
export class CollectionsService {
|
|
17
17
|
knex;
|
|
18
18
|
helpers;
|
|
@@ -206,6 +206,7 @@ export class CollectionsService {
|
|
|
206
206
|
* Read all collections. Currently doesn't support any query.
|
|
207
207
|
*/
|
|
208
208
|
async readByQuery() {
|
|
209
|
+
const env = useEnv();
|
|
209
210
|
const collectionItemsService = new ItemsService('directus_collections', {
|
|
210
211
|
knex: this.knex,
|
|
211
212
|
schema: this.schema,
|
|
@@ -1,28 +1,34 @@
|
|
|
1
1
|
import type { ApiOutput, ExtensionSettings } from '@directus/extensions';
|
|
2
|
-
import type { SchemaInspector } from '@directus/schema';
|
|
3
2
|
import type { Accountability, DeepPartial, SchemaOverview } from '@directus/types';
|
|
4
|
-
import type Keyv from 'keyv';
|
|
5
3
|
import type { Knex } from 'knex';
|
|
6
|
-
import type { Helpers } from '../database/helpers/index.js';
|
|
7
4
|
import type { ExtensionManager } from '../extensions/manager.js';
|
|
8
5
|
import type { AbstractServiceOptions } from '../types/index.js';
|
|
9
6
|
import { ItemsService } from './items.js';
|
|
10
|
-
|
|
7
|
+
export declare class ExtensionReadError extends Error {
|
|
8
|
+
originalError: unknown;
|
|
9
|
+
constructor(originalError: unknown);
|
|
10
|
+
}
|
|
11
11
|
export declare class ExtensionsService {
|
|
12
12
|
knex: Knex;
|
|
13
|
-
permissionsService: PermissionsService;
|
|
14
|
-
schemaInspector: SchemaInspector;
|
|
15
13
|
accountability: Accountability | null;
|
|
16
14
|
schema: SchemaOverview;
|
|
17
15
|
extensionsItemService: ItemsService<ExtensionSettings>;
|
|
18
|
-
systemCache: Keyv<any>;
|
|
19
|
-
helpers: Helpers;
|
|
20
16
|
extensionsManager: ExtensionManager;
|
|
21
17
|
constructor(options: AbstractServiceOptions);
|
|
22
18
|
readAll(): Promise<ApiOutput[]>;
|
|
23
19
|
readOne(bundle: string | null, name: string): Promise<ApiOutput>;
|
|
24
|
-
updateOne(bundle: string | null, name: string, data: DeepPartial<ApiOutput>): Promise<
|
|
20
|
+
updateOne(bundle: string | null, name: string, data: DeepPartial<ApiOutput>): Promise<ApiOutput>;
|
|
25
21
|
private getKey;
|
|
22
|
+
/**
|
|
23
|
+
* Sync a bundles enabled status
|
|
24
|
+
* - If the extension or extensions parent is not a bundle changes are skipped
|
|
25
|
+
* - If a bundles status is toggled, all children are set to that status
|
|
26
|
+
* - If an entries status is toggled, then if the:
|
|
27
|
+
* - Parent bundle is non-partial throws UnprocessableContentError
|
|
28
|
+
* - Entry status change resulted in all children being disabled then the parent bundle is disabled
|
|
29
|
+
* - Entry status change resulted in at least one child being enabled then the parent bundle is enabled
|
|
30
|
+
*/
|
|
31
|
+
private checkBundleAndSyncStatus;
|
|
26
32
|
/**
|
|
27
33
|
* Combine the settings stored in the database with the information available from the installed
|
|
28
34
|
* extensions into the standardized extensions api output
|