@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/utils/apply-diff.js
CHANGED
|
@@ -4,12 +4,13 @@ import { flushCaches } from '../cache.js';
|
|
|
4
4
|
import { getHelpers } from '../database/helpers/index.js';
|
|
5
5
|
import getDatabase from '../database/index.js';
|
|
6
6
|
import emitter from '../emitter.js';
|
|
7
|
-
import
|
|
7
|
+
import { useLogger } from '../logger.js';
|
|
8
8
|
import { CollectionsService } from '../services/collections.js';
|
|
9
9
|
import { FieldsService } from '../services/fields.js';
|
|
10
10
|
import { RelationsService } from '../services/relations.js';
|
|
11
11
|
import { DiffKind } from '../types/index.js';
|
|
12
12
|
import { getSchema } from './get-schema.js';
|
|
13
|
+
const logger = useLogger();
|
|
13
14
|
export async function applyDiff(currentSnapshot, snapshotDiff, options) {
|
|
14
15
|
const database = options?.database ?? getDatabase();
|
|
15
16
|
const helpers = getHelpers(database);
|
|
@@ -405,7 +405,7 @@ export function applyFilter(knex, schema, rootQuery, rootFilter, collection, ali
|
|
|
405
405
|
if (column.includes('(') && column.includes(')')) {
|
|
406
406
|
const functionName = column.split('(')[0];
|
|
407
407
|
const type = getOutputTypeForFunction(functionName);
|
|
408
|
-
if (['
|
|
408
|
+
if (['integer', 'float', 'decimal'].includes(type)) {
|
|
409
409
|
compareValue = Number(compareValue);
|
|
410
410
|
}
|
|
411
411
|
}
|
|
@@ -422,7 +422,7 @@ export function applyFilter(knex, schema, rootQuery, rootFilter, collection, ali
|
|
|
422
422
|
compareValue = helpers.date.parse(compareValue);
|
|
423
423
|
}
|
|
424
424
|
}
|
|
425
|
-
if (['
|
|
425
|
+
if (['integer', 'float', 'decimal'].includes(type)) {
|
|
426
426
|
if (Array.isArray(compareValue)) {
|
|
427
427
|
compareValue = compareValue.map((val) => Number(val));
|
|
428
428
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { createRequire } from 'node:module';
|
|
2
|
-
import
|
|
2
|
+
import { useLogger } from '../logger.js';
|
|
3
3
|
const require = createRequire(import.meta.url);
|
|
4
4
|
export function deleteFromRequireCache(modulePath) {
|
|
5
|
+
const logger = useLogger();
|
|
5
6
|
try {
|
|
6
7
|
const moduleCachePath = require.resolve(modulePath);
|
|
7
8
|
delete require.cache[moduleCachePath];
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import
|
|
2
|
-
import env from '../env.js';
|
|
1
|
+
import { useEnv } from '@directus/env';
|
|
3
2
|
import { InvalidCredentialsError } from '@directus/errors';
|
|
3
|
+
import getDatabase from '../database/index.js';
|
|
4
4
|
import isDirectusJWT from './is-directus-jwt.js';
|
|
5
5
|
import { verifyAccessJWT } from './jwt.js';
|
|
6
6
|
export async function getAccountabilityForToken(token, accountability) {
|
|
7
|
+
const env = useEnv();
|
|
7
8
|
if (!accountability) {
|
|
8
9
|
accountability = {
|
|
9
10
|
user: null,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { useEnv } from '@directus/env';
|
|
1
2
|
import { toArray } from '@directus/utils';
|
|
2
|
-
import env from '../env.js';
|
|
3
3
|
export function getAuthProviders() {
|
|
4
|
+
const env = useEnv();
|
|
4
5
|
return toArray(env['AUTH_PROVIDERS'])
|
|
5
6
|
.filter((provider) => provider && env[`AUTH_${provider.toUpperCase()}_DRIVER`])
|
|
6
7
|
.map((provider) => ({
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useEnv } from '@directus/env';
|
|
2
2
|
import { shouldSkipCache } from './should-skip-cache.js';
|
|
3
3
|
/**
|
|
4
4
|
* Returns the Cache-Control header for the current request
|
|
@@ -9,6 +9,7 @@ import { shouldSkipCache } from './should-skip-cache.js';
|
|
|
9
9
|
* @param personalized Whether requests depend on the authentication status of users
|
|
10
10
|
*/
|
|
11
11
|
export function getCacheControlHeader(req, ttl, globalCacheSettings, personalized) {
|
|
12
|
+
const env = useEnv();
|
|
12
13
|
// When the user explicitly asked to skip the cache
|
|
13
14
|
if (shouldSkipCache(req))
|
|
14
15
|
return 'no-store';
|
|
@@ -29,7 +30,9 @@ export function getCacheControlHeader(req, ttl, globalCacheSettings, personalize
|
|
|
29
30
|
const ttlSeconds = Math.round(ttl / 1000);
|
|
30
31
|
headerValues.push(`max-age=${ttlSeconds}`);
|
|
31
32
|
// When the s-maxage flag should be included
|
|
32
|
-
if (globalCacheSettings &&
|
|
33
|
+
if (globalCacheSettings &&
|
|
34
|
+
Number.isInteger(env['CACHE_CONTROL_S_MAXAGE']) &&
|
|
35
|
+
env['CACHE_CONTROL_S_MAXAGE'] >= 0) {
|
|
33
36
|
headerValues.push(`s-maxage=${env['CACHE_CONTROL_S_MAXAGE']}`);
|
|
34
37
|
}
|
|
35
38
|
return headerValues.join(', ');
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import hash from 'object-hash';
|
|
2
2
|
import url from 'url';
|
|
3
3
|
import { getGraphqlQueryAndVariables } from './get-graphql-query-and-variables.js';
|
|
4
|
-
import { version } from '
|
|
4
|
+
import { version } from 'directus/version';
|
|
5
5
|
export function getCacheKey(req) {
|
|
6
6
|
const path = url.parse(req.originalUrl).pathname;
|
|
7
7
|
const isGraphQl = path?.startsWith('/graphql');
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { useEnv } from '@directus/env';
|
|
1
2
|
import camelcase from 'camelcase';
|
|
2
3
|
import { set } from 'lodash-es';
|
|
3
|
-
import env from '../env.js';
|
|
4
4
|
export function getConfigFromEnv(prefix, omitPrefix, type = 'camelcase') {
|
|
5
|
+
const env = useEnv();
|
|
5
6
|
const config = {};
|
|
6
7
|
for (const [key, value] of Object.entries(env)) {
|
|
7
8
|
if (key.toLowerCase().startsWith(prefix.toLowerCase()) === false)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { parseJSON } from '@directus/utils';
|
|
2
|
-
import
|
|
3
|
-
import
|
|
2
|
+
import { getNodeEnv } from '@directus/utils/node';
|
|
3
|
+
import { useLogger } from '../logger.js';
|
|
4
4
|
import getLocalType from './get-local-type.js';
|
|
5
5
|
export default function getDefaultValue(column, field) {
|
|
6
6
|
const type = getLocalType(column, field);
|
|
@@ -37,6 +37,7 @@ function castToBoolean(value) {
|
|
|
37
37
|
return Boolean(value);
|
|
38
38
|
}
|
|
39
39
|
function castToObject(value) {
|
|
40
|
+
const logger = useLogger();
|
|
40
41
|
if (!value)
|
|
41
42
|
return value;
|
|
42
43
|
if (typeof value === 'object')
|
|
@@ -46,7 +47,7 @@ function castToObject(value) {
|
|
|
46
47
|
return parseJSON(value);
|
|
47
48
|
}
|
|
48
49
|
catch (err) {
|
|
49
|
-
if (
|
|
50
|
+
if (getNodeEnv() === 'development') {
|
|
50
51
|
logger.error(err);
|
|
51
52
|
}
|
|
52
53
|
return value;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { Request } from 'express';
|
|
2
|
-
export declare function getIPFromReq(req: Request): string;
|
|
2
|
+
export declare function getIPFromReq(req: Request): string | null;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import { useEnv } from '@directus/env';
|
|
1
2
|
import { isIP } from 'net';
|
|
2
|
-
import
|
|
3
|
-
import logger from '../logger.js';
|
|
3
|
+
import { useLogger } from '../logger.js';
|
|
4
4
|
export function getIPFromReq(req) {
|
|
5
|
+
const env = useEnv();
|
|
6
|
+
const logger = useLogger();
|
|
5
7
|
let ip = req.ip;
|
|
6
8
|
if (env['IP_CUSTOM_HEADER']) {
|
|
7
9
|
const customIPHeaderValue = req.get(env['IP_CUSTOM_HEADER']);
|
|
@@ -13,5 +15,5 @@ export function getIPFromReq(req) {
|
|
|
13
15
|
}
|
|
14
16
|
}
|
|
15
17
|
// IP addresses starting with ::ffff: are IPv4 addresses in IPv6 format. We can strip the prefix to get back to IPv4
|
|
16
|
-
return ip
|
|
18
|
+
return ip?.startsWith('::ffff:') ? ip.substring(7) : ip ?? null;
|
|
17
19
|
}
|
|
@@ -1,18 +1,20 @@
|
|
|
1
|
+
import { useEnv } from '@directus/env';
|
|
1
2
|
import { deepMap, parseFilter, parseJSON, parsePreset } from '@directus/utils';
|
|
2
3
|
import { cloneDeep } from 'lodash-es';
|
|
3
4
|
import hash from 'object-hash';
|
|
4
5
|
import { getCache, getCacheValue, getSystemCache, setCacheValue, setSystemCache } from '../cache.js';
|
|
5
6
|
import getDatabase from '../database/index.js';
|
|
6
7
|
import { appAccessMinimalPermissions } from '../database/system-data/app-access-permissions/index.js';
|
|
7
|
-
import
|
|
8
|
-
import logger from '../logger.js';
|
|
8
|
+
import { useLogger } from '../logger.js';
|
|
9
9
|
import { RolesService } from '../services/roles.js';
|
|
10
10
|
import { UsersService } from '../services/users.js';
|
|
11
|
-
import { mergePermissions } from './merge-permissions.js';
|
|
12
11
|
import { mergePermissionsForShare } from './merge-permissions-for-share.js';
|
|
12
|
+
import { mergePermissions } from './merge-permissions.js';
|
|
13
13
|
export async function getPermissions(accountability, schema) {
|
|
14
14
|
const database = getDatabase();
|
|
15
15
|
const { cache } = getCache();
|
|
16
|
+
const env = useEnv();
|
|
17
|
+
const logger = useLogger();
|
|
16
18
|
let permissions = [];
|
|
17
19
|
const { user, role, app, admin, share_scope } = accountability;
|
|
18
20
|
const cacheKey = `permissions-${hash({ user, role, app, admin, share_scope })}`;
|
package/dist/utils/get-schema.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { useEnv } from '@directus/env';
|
|
1
2
|
import { createInspector } from '@directus/schema';
|
|
2
3
|
import { parseJSON, toArray } from '@directus/utils';
|
|
3
4
|
import { mapValues } from 'lodash-es';
|
|
@@ -6,12 +7,13 @@ import { ALIAS_TYPES } from '../constants.js';
|
|
|
6
7
|
import getDatabase from '../database/index.js';
|
|
7
8
|
import { systemCollectionRows } from '../database/system-data/collections/index.js';
|
|
8
9
|
import { systemFieldRows } from '../database/system-data/fields/index.js';
|
|
9
|
-
import
|
|
10
|
-
import logger from '../logger.js';
|
|
10
|
+
import { useLogger } from '../logger.js';
|
|
11
11
|
import { RelationsService } from '../services/relations.js';
|
|
12
12
|
import getDefaultValue from './get-default-value.js';
|
|
13
13
|
import getLocalType from './get-local-type.js';
|
|
14
|
+
const logger = useLogger();
|
|
14
15
|
export async function getSchema(options) {
|
|
16
|
+
const env = useEnv();
|
|
15
17
|
const database = options?.database || getDatabase();
|
|
16
18
|
const schemaInspector = createInspector(database);
|
|
17
19
|
let result;
|
|
@@ -42,6 +44,7 @@ export async function getSchema(options) {
|
|
|
42
44
|
return result;
|
|
43
45
|
}
|
|
44
46
|
async function getDatabaseSchema(database, schemaInspector) {
|
|
47
|
+
const env = useEnv();
|
|
45
48
|
const result = {
|
|
46
49
|
collections: {},
|
|
47
50
|
relations: [],
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import deepDiff from 'deep-diff';
|
|
2
|
-
import { orderBy } from 'lodash-es';
|
|
3
2
|
import { DiffKind } from '../types/index.js';
|
|
4
3
|
import { sanitizeCollection, sanitizeField, sanitizeRelation } from './sanitize-schema.js';
|
|
5
4
|
export function getSnapshotDiff(current, after) {
|
|
6
5
|
const diffedSnapshot = {
|
|
7
|
-
collections:
|
|
6
|
+
collections: [
|
|
8
7
|
...current.collections.map((currentCollection) => {
|
|
9
8
|
const afterCollection = after.collections.find((afterCollection) => afterCollection.collection === currentCollection.collection);
|
|
10
9
|
return {
|
|
@@ -21,8 +20,8 @@ export function getSnapshotDiff(current, after) {
|
|
|
21
20
|
collection: afterCollection.collection,
|
|
22
21
|
diff: deepDiff.diff(undefined, sanitizeCollection(afterCollection)),
|
|
23
22
|
})),
|
|
24
|
-
].filter((obj) => Array.isArray(obj.diff)),
|
|
25
|
-
fields:
|
|
23
|
+
].filter((obj) => Array.isArray(obj.diff)),
|
|
24
|
+
fields: [
|
|
26
25
|
...current.fields.map((currentField) => {
|
|
27
26
|
const afterField = after.fields.find((afterField) => afterField.collection === currentField.collection && afterField.field === currentField.field);
|
|
28
27
|
const isAutoIncrementPrimaryKey = !!currentField.schema?.is_primary_key && !!currentField.schema?.has_auto_increment;
|
|
@@ -42,8 +41,8 @@ export function getSnapshotDiff(current, after) {
|
|
|
42
41
|
field: afterField.field,
|
|
43
42
|
diff: deepDiff.diff(undefined, sanitizeField(afterField)),
|
|
44
43
|
})),
|
|
45
|
-
].filter((obj) => Array.isArray(obj.diff)),
|
|
46
|
-
relations:
|
|
44
|
+
].filter((obj) => Array.isArray(obj.diff)),
|
|
45
|
+
relations: [
|
|
47
46
|
...current.relations.map((currentRelation) => {
|
|
48
47
|
const afterRelation = after.relations.find((afterRelation) => afterRelation.collection === currentRelation.collection && afterRelation.field === currentRelation.field);
|
|
49
48
|
return {
|
|
@@ -55,8 +54,7 @@ export function getSnapshotDiff(current, after) {
|
|
|
55
54
|
}),
|
|
56
55
|
...after.relations
|
|
57
56
|
.filter((afterRelation) => {
|
|
58
|
-
const currentRelation = current.relations.find((currentRelation) => currentRelation.collection === afterRelation.collection &&
|
|
59
|
-
afterRelation.field === currentRelation.field);
|
|
57
|
+
const currentRelation = current.relations.find((currentRelation) => currentRelation.collection === afterRelation.collection && afterRelation.field === currentRelation.field);
|
|
60
58
|
return !!currentRelation === false;
|
|
61
59
|
})
|
|
62
60
|
.map((afterRelation) => ({
|
|
@@ -65,7 +63,7 @@ export function getSnapshotDiff(current, after) {
|
|
|
65
63
|
related_collection: afterRelation.related_collection,
|
|
66
64
|
diff: deepDiff.diff(undefined, sanitizeRelation(afterRelation)),
|
|
67
65
|
})),
|
|
68
|
-
].filter((obj) => Array.isArray(obj.diff)),
|
|
66
|
+
].filter((obj) => Array.isArray(obj.diff)),
|
|
69
67
|
};
|
|
70
68
|
/**
|
|
71
69
|
* When you delete a collection, we don't have to individually drop all the fields/relations as well
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
+
import { version } from 'directus/version';
|
|
1
2
|
import { fromPairs, isArray, isPlainObject, mapValues, omit, sortBy, toPairs } from 'lodash-es';
|
|
2
3
|
import getDatabase, { getDatabaseClient } from '../database/index.js';
|
|
3
4
|
import { CollectionsService } from '../services/collections.js';
|
|
4
5
|
import { FieldsService } from '../services/fields.js';
|
|
5
6
|
import { RelationsService } from '../services/relations.js';
|
|
6
7
|
import { getSchema } from './get-schema.js';
|
|
7
|
-
import { version } from './package.js';
|
|
8
8
|
import { sanitizeCollection, sanitizeField, sanitizeRelation } from './sanitize-schema.js';
|
|
9
9
|
export async function getSnapshot(options) {
|
|
10
10
|
const database = options?.database ?? getDatabase();
|
|
@@ -19,11 +19,11 @@ export async function getSnapshot(options) {
|
|
|
19
19
|
relationsService.readAll(),
|
|
20
20
|
]);
|
|
21
21
|
const collectionsFiltered = collectionsRaw.filter((item) => excludeSystem(item));
|
|
22
|
-
const fieldsFiltered = fieldsRaw.filter((item) => excludeSystem(item))
|
|
23
|
-
const relationsFiltered = relationsRaw.filter((item) => excludeSystem(item))
|
|
22
|
+
const fieldsFiltered = fieldsRaw.filter((item) => excludeSystem(item));
|
|
23
|
+
const relationsFiltered = relationsRaw.filter((item) => excludeSystem(item));
|
|
24
24
|
const collectionsSorted = sortBy(mapValues(collectionsFiltered, sortDeep), ['collection']);
|
|
25
|
-
const fieldsSorted = sortBy(mapValues(fieldsFiltered, sortDeep), ['collection', '
|
|
26
|
-
const relationsSorted = sortBy(mapValues(relationsFiltered, sortDeep), ['collection', '
|
|
25
|
+
const fieldsSorted = sortBy(mapValues(fieldsFiltered, sortDeep), ['collection', 'meta.id']).map(omitID);
|
|
26
|
+
const relationsSorted = sortBy(mapValues(relationsFiltered, sortDeep), ['collection', 'meta.id']).map(omitID);
|
|
27
27
|
return {
|
|
28
28
|
version: 1,
|
|
29
29
|
directus: version,
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checks if an IP address is contained in a list of networks
|
|
3
|
+
* @param networks List of IP addresses (192.168.0.1), CIDR notations (192.168.0.0/24) or IP ranges (192-168.0.0-192.168.2.0)
|
|
4
|
+
* @throws Will throw if list contains invalid network definitions
|
|
5
|
+
*/
|
|
6
|
+
export declare function ipInNetworks(ip: string, networks: string[]): boolean;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { matches } from 'ip-matching';
|
|
2
|
+
/**
|
|
3
|
+
* Checks if an IP address is contained in a list of networks
|
|
4
|
+
* @param networks List of IP addresses (192.168.0.1), CIDR notations (192.168.0.0/24) or IP ranges (192-168.0.0-192.168.2.0)
|
|
5
|
+
* @throws Will throw if list contains invalid network definitions
|
|
6
|
+
*/
|
|
7
|
+
export function ipInNetworks(ip, networks) {
|
|
8
|
+
for (const allowedIp of networks) {
|
|
9
|
+
if (matches(ip, allowedIp))
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { toArray } from '@directus/utils';
|
|
2
|
-
import logger from '../logger.js';
|
|
3
2
|
import { URL } from 'url';
|
|
3
|
+
import { useLogger } from '../logger.js';
|
|
4
4
|
/**
|
|
5
5
|
* Check if url matches allow list either exactly or by domain+path
|
|
6
6
|
*/
|
|
7
7
|
export default function isUrlAllowed(url, allowList) {
|
|
8
|
+
const logger = useLogger();
|
|
8
9
|
const urlAllowList = toArray(allowList);
|
|
9
10
|
if (urlAllowList.includes(url))
|
|
10
11
|
return true;
|
package/dist/utils/job-queue.js
CHANGED
package/dist/utils/md.d.ts
CHANGED
package/dist/utils/md.js
CHANGED
|
@@ -3,6 +3,7 @@ import sanitizeHTML from 'sanitize-html';
|
|
|
3
3
|
/**
|
|
4
4
|
* Render and sanitize a markdown string
|
|
5
5
|
*/
|
|
6
|
-
export function md(
|
|
7
|
-
|
|
6
|
+
export function md(value) {
|
|
7
|
+
const markdown = marked.parse(value); /* Would only be a promise if used with async extensions */
|
|
8
|
+
return sanitizeHTML(markdown);
|
|
8
9
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import { useEnv } from '@directus/env';
|
|
1
2
|
import { parseFilter, parseJSON } from '@directus/utils';
|
|
2
3
|
import { flatten, get, isPlainObject, merge, set } from 'lodash-es';
|
|
3
|
-
import
|
|
4
|
-
import logger from '../logger.js';
|
|
4
|
+
import { useLogger } from '../logger.js';
|
|
5
5
|
import { Meta } from '../types/index.js';
|
|
6
6
|
export function sanitizeQuery(rawQuery, accountability) {
|
|
7
|
+
const env = useEnv();
|
|
7
8
|
const query = {};
|
|
8
9
|
const hasMaxLimit = 'QUERY_LIMIT_MAX' in env &&
|
|
9
10
|
Number(env['QUERY_LIMIT_MAX']) >= 0 &&
|
|
@@ -84,6 +85,7 @@ function sanitizeSort(rawSort) {
|
|
|
84
85
|
return fields;
|
|
85
86
|
}
|
|
86
87
|
function sanitizeAggregate(rawAggregate) {
|
|
88
|
+
const logger = useLogger();
|
|
87
89
|
let aggregate = rawAggregate;
|
|
88
90
|
if (typeof rawAggregate === 'string') {
|
|
89
91
|
try {
|
|
@@ -102,6 +104,7 @@ function sanitizeAggregate(rawAggregate) {
|
|
|
102
104
|
return aggregate;
|
|
103
105
|
}
|
|
104
106
|
function sanitizeFilter(rawFilter, accountability) {
|
|
107
|
+
const logger = useLogger();
|
|
105
108
|
let filters = rawFilter;
|
|
106
109
|
if (typeof rawFilter === 'string') {
|
|
107
110
|
try {
|
|
@@ -137,6 +140,7 @@ function sanitizeMeta(rawMeta) {
|
|
|
137
140
|
return [rawMeta];
|
|
138
141
|
}
|
|
139
142
|
function sanitizeDeep(deep, accountability) {
|
|
143
|
+
const logger = useLogger();
|
|
140
144
|
const result = {};
|
|
141
145
|
if (typeof deep === 'string') {
|
|
142
146
|
try {
|
|
@@ -175,6 +179,7 @@ function sanitizeDeep(deep, accountability) {
|
|
|
175
179
|
}
|
|
176
180
|
}
|
|
177
181
|
function sanitizeAlias(rawAlias) {
|
|
182
|
+
const logger = useLogger();
|
|
178
183
|
let alias = rawAlias;
|
|
179
184
|
if (typeof rawAlias === 'string') {
|
|
180
185
|
try {
|
|
@@ -16,7 +16,7 @@ export declare function sanitizeCollection(collection: Collection | undefined):
|
|
|
16
16
|
* @returns sanitized field
|
|
17
17
|
*/
|
|
18
18
|
export declare function sanitizeField(field: Field | undefined, sanitizeAllSchema?: boolean): Partial<Field> | undefined;
|
|
19
|
-
export declare function sanitizeColumn(column: Column): Pick<Column, "
|
|
19
|
+
export declare function sanitizeColumn(column: Column): Pick<Column, "name" | "table" | "numeric_precision" | "numeric_scale" | "data_type" | "default_value" | "max_length" | "is_nullable" | "is_unique" | "is_primary_key" | "is_generated" | "generation_expression" | "has_auto_increment" | "foreign_key_table" | "foreign_key_column">;
|
|
20
20
|
/**
|
|
21
21
|
* Pick certain database vendor specific relation properties that should be compared when performing diff
|
|
22
22
|
*
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useEnv } from '@directus/env';
|
|
2
2
|
/**
|
|
3
3
|
* Check whether cache should be cleared
|
|
4
4
|
*
|
|
@@ -7,6 +7,7 @@ import env from '../env.js';
|
|
|
7
7
|
* @param collection Collection name to check if cache purging should be ignored
|
|
8
8
|
*/
|
|
9
9
|
export function shouldClearCache(cache, opts, collection) {
|
|
10
|
+
const env = useEnv();
|
|
10
11
|
if (env['CACHE_AUTO_PURGE']) {
|
|
11
12
|
if (collection && env['CACHE_AUTO_PURGE_IGNORE_LIST'].includes(collection)) {
|
|
12
13
|
return false;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { useEnv } from '@directus/env';
|
|
1
2
|
import { getEndpoint } from '@directus/utils';
|
|
2
3
|
import url from 'url';
|
|
3
|
-
import env from '../env.js';
|
|
4
4
|
import { Url } from './url.js';
|
|
5
5
|
/**
|
|
6
6
|
* Whether to skip caching for the current request
|
|
@@ -8,6 +8,7 @@ import { Url } from './url.js';
|
|
|
8
8
|
* @param req Express request object
|
|
9
9
|
*/
|
|
10
10
|
export function shouldSkipCache(req) {
|
|
11
|
+
const env = useEnv();
|
|
11
12
|
// Always skip cache for requests coming from the data studio based on Referer header
|
|
12
13
|
const referer = req.get('Referer');
|
|
13
14
|
if (referer) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { clamp } from 'lodash-es';
|
|
1
2
|
export function resolvePreset({ transformationParams, acceptFormat }, file) {
|
|
2
3
|
const transforms = transformationParams.transforms ? [...transformationParams.transforms] : [];
|
|
3
4
|
if (transformationParams.format || transformationParams.quality) {
|
|
@@ -9,18 +10,54 @@ export function resolvePreset({ transformationParams, acceptFormat }, file) {
|
|
|
9
10
|
},
|
|
10
11
|
]);
|
|
11
12
|
}
|
|
12
|
-
if (transformationParams.width || transformationParams.height) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
13
|
+
if ((transformationParams.width || transformationParams.height) && file.width && file.height) {
|
|
14
|
+
const toWidth = transformationParams.width ? Number(transformationParams.width) : undefined;
|
|
15
|
+
const toHeight = transformationParams.height ? Number(transformationParams.height) : undefined;
|
|
16
|
+
const toFocalPointX = transformationParams.focal_point_x
|
|
17
|
+
? Number(transformationParams.focal_point_x)
|
|
18
|
+
: file.focal_point_x;
|
|
19
|
+
const toFocalPointY = transformationParams.focal_point_y
|
|
20
|
+
? Number(transformationParams.focal_point_y)
|
|
21
|
+
: file.focal_point_y;
|
|
22
|
+
/*
|
|
23
|
+
* Focal point cropping only works with a fixed size (width x height) when `cover`ing,
|
|
24
|
+
* since the other modes show the whole image. Sharp by default also simply scales up/down
|
|
25
|
+
* when only supplied with one dimension, so we **must** check, else we break existing behaviour.
|
|
26
|
+
* See: https://sharp.pixelplumbing.com/api-resize#resize
|
|
27
|
+
* Also only crop to focal point when explicitly defined so that users can still `cover` with
|
|
28
|
+
* other parameters like `position` and `gravity` - Else fall back to regular behaviour
|
|
29
|
+
*/
|
|
30
|
+
if ((transformationParams.fit === undefined || transformationParams.fit === 'cover') &&
|
|
31
|
+
toWidth &&
|
|
32
|
+
toHeight &&
|
|
33
|
+
toFocalPointX !== null &&
|
|
34
|
+
toFocalPointY !== null) {
|
|
35
|
+
const transformArgs = getResizeArguments({ w: file.width, h: file.height }, { w: toWidth, h: toHeight }, { x: toFocalPointX, y: toFocalPointY });
|
|
36
|
+
transforms.push([
|
|
37
|
+
'resize',
|
|
38
|
+
{
|
|
39
|
+
width: transformArgs.width,
|
|
40
|
+
height: transformArgs.height,
|
|
41
|
+
fit: transformationParams.fit,
|
|
42
|
+
withoutEnlargement: transformationParams.withoutEnlargement
|
|
43
|
+
? Boolean(transformationParams.withoutEnlargement)
|
|
44
|
+
: undefined,
|
|
45
|
+
},
|
|
46
|
+
], ['extract', transformArgs.region]);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
transforms.push([
|
|
50
|
+
'resize',
|
|
51
|
+
{
|
|
52
|
+
width: toWidth,
|
|
53
|
+
height: toHeight,
|
|
54
|
+
fit: transformationParams.fit,
|
|
55
|
+
withoutEnlargement: transformationParams.withoutEnlargement
|
|
56
|
+
? Boolean(transformationParams.withoutEnlargement)
|
|
57
|
+
: undefined,
|
|
58
|
+
},
|
|
59
|
+
]);
|
|
60
|
+
}
|
|
24
61
|
}
|
|
25
62
|
return transforms;
|
|
26
63
|
}
|
|
@@ -47,3 +84,49 @@ export function maybeExtractFormat(transforms) {
|
|
|
47
84
|
const lastToFormat = toFormats[toFormats.length - 1];
|
|
48
85
|
return lastToFormat ? lastToFormat[1]?.toString() : undefined;
|
|
49
86
|
}
|
|
87
|
+
/**
|
|
88
|
+
* Resize an image but keep it centered on the focal point.
|
|
89
|
+
* Based on the method outlined in https://github.com/lovell/sharp/issues/1198#issuecomment-384591756
|
|
90
|
+
*/
|
|
91
|
+
function getResizeArguments(original, target, focalPoint) {
|
|
92
|
+
const { width, height, factor } = getIntermediateDimensions(original, target);
|
|
93
|
+
const region = getExtractionRegion(factor, focalPoint ?? { x: original.w / 2, y: original.h / 2 }, target, {
|
|
94
|
+
w: width,
|
|
95
|
+
h: height,
|
|
96
|
+
});
|
|
97
|
+
return { width, height, region };
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Calculates the dimensions of the intermediate (resized) image.
|
|
101
|
+
*/
|
|
102
|
+
function getIntermediateDimensions(original, target) {
|
|
103
|
+
const hRatio = original.h / target.h;
|
|
104
|
+
const wRatio = original.w / target.w;
|
|
105
|
+
let factor;
|
|
106
|
+
let width;
|
|
107
|
+
let height;
|
|
108
|
+
if (hRatio < wRatio) {
|
|
109
|
+
factor = hRatio;
|
|
110
|
+
height = Math.round(target.h);
|
|
111
|
+
width = Math.round(original.w / factor);
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
factor = wRatio;
|
|
115
|
+
width = Math.round(target.w);
|
|
116
|
+
height = Math.round(original.h / factor);
|
|
117
|
+
}
|
|
118
|
+
return { width, height, factor };
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Calculates the Region to extract from the intermediate image.
|
|
122
|
+
*/
|
|
123
|
+
function getExtractionRegion(factor, focalPoint, target, intermediate) {
|
|
124
|
+
const newXCenter = focalPoint.x / factor;
|
|
125
|
+
const newYCenter = focalPoint.y / factor;
|
|
126
|
+
return {
|
|
127
|
+
left: clamp(Math.round(newXCenter - target.w / 2), 0, intermediate.w - target.w),
|
|
128
|
+
top: clamp(Math.round(newYCenter - target.h / 2), 0, intermediate.h - target.h),
|
|
129
|
+
width: target.w,
|
|
130
|
+
height: target.h,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import { useEnv } from '@directus/env';
|
|
2
|
+
import { useLogger } from '../logger.js';
|
|
3
3
|
export function validateEnv(requiredKeys) {
|
|
4
|
+
const env = useEnv();
|
|
5
|
+
const logger = useLogger();
|
|
4
6
|
for (const requiredKey of requiredKeys) {
|
|
5
7
|
if (requiredKey in env === false) {
|
|
6
8
|
logger.error(`"${requiredKey}" Environment Variable is missing.`);
|
|
@@ -1,16 +1,20 @@
|
|
|
1
|
+
import { useEnv } from '@directus/env';
|
|
2
|
+
import { InvalidQueryError } from '@directus/errors';
|
|
1
3
|
import Joi from 'joi';
|
|
2
4
|
import { isPlainObject, uniq } from 'lodash-es';
|
|
3
5
|
import { stringify } from 'wellknown';
|
|
4
|
-
import env from '../env.js';
|
|
5
|
-
import { InvalidQueryError } from '@directus/errors';
|
|
6
6
|
import { calculateFieldDepth } from './calculate-field-depth.js';
|
|
7
|
+
const env = useEnv();
|
|
7
8
|
const querySchema = Joi.object({
|
|
8
9
|
fields: Joi.array().items(Joi.string()),
|
|
9
10
|
group: Joi.array().items(Joi.string()),
|
|
10
11
|
sort: Joi.array().items(Joi.string()),
|
|
11
12
|
filter: Joi.object({}).unknown(),
|
|
12
13
|
limit: 'QUERY_LIMIT_MAX' in env && env['QUERY_LIMIT_MAX'] !== -1
|
|
13
|
-
? Joi.number()
|
|
14
|
+
? Joi.number()
|
|
15
|
+
.integer()
|
|
16
|
+
.min(-1)
|
|
17
|
+
.max(env['QUERY_LIMIT_MAX']) // min should be 0
|
|
14
18
|
: Joi.number().integer().min(-1),
|
|
15
19
|
offset: Joi.number().integer().min(0),
|
|
16
20
|
page: Joi.number().integer().min(0),
|
|
@@ -96,6 +100,7 @@ function validateFilter(filter) {
|
|
|
96
100
|
validateFilterPrimitive(nested, '_eq');
|
|
97
101
|
}
|
|
98
102
|
else {
|
|
103
|
+
// @ts-ignore TODO Check which case this is supposed to cover
|
|
99
104
|
validateFilter(nested);
|
|
100
105
|
}
|
|
101
106
|
}
|