@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
package/dist/types/items.d.ts
CHANGED
|
@@ -1,20 +1,12 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* I know this looks a little silly, but it allows us to explicitly differentiate between when we're
|
|
3
|
-
* expecting an item vs any other generic object.
|
|
4
|
-
*/
|
|
5
1
|
import type { DirectusError } from '@directus/errors';
|
|
6
2
|
import type { EventContext } from '@directus/types';
|
|
7
3
|
import type { MutationTracker } from '../services/items.js';
|
|
8
4
|
export type Item = Record<string, any>;
|
|
9
5
|
export type PrimaryKey = string | number;
|
|
10
|
-
export type Alterations = {
|
|
11
|
-
create:
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
update: {
|
|
15
|
-
[key: string]: any;
|
|
16
|
-
}[];
|
|
17
|
-
delete: (number | string)[];
|
|
6
|
+
export type Alterations<T extends Item = Item, K extends keyof T | undefined = undefined> = {
|
|
7
|
+
create: Partial<T>[];
|
|
8
|
+
update: (K extends keyof T ? Partial<T> & Pick<T, K> : Partial<T>)[];
|
|
9
|
+
delete: (K extends keyof T ? T[K] : PrimaryKey)[];
|
|
18
10
|
};
|
|
19
11
|
export type MutationOptions = {
|
|
20
12
|
/**
|
package/dist/types/items.js
CHANGED
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);
|
|
@@ -408,9 +408,6 @@ export function applyFilter(knex, schema, rootQuery, rootFilter, collection, ali
|
|
|
408
408
|
if (['integer', 'float', 'decimal'].includes(type)) {
|
|
409
409
|
compareValue = Number(compareValue);
|
|
410
410
|
}
|
|
411
|
-
else if (type === 'bigInteger') {
|
|
412
|
-
compareValue = BigInt(compareValue);
|
|
413
|
-
}
|
|
414
411
|
}
|
|
415
412
|
// Cast filter value (compareValue) based on type of field being filtered against
|
|
416
413
|
const [collection, field] = key.split('.');
|
|
@@ -433,14 +430,6 @@ export function applyFilter(knex, schema, rootQuery, rootFilter, collection, ali
|
|
|
433
430
|
compareValue = Number(compareValue);
|
|
434
431
|
}
|
|
435
432
|
}
|
|
436
|
-
if (type === 'bigInteger') {
|
|
437
|
-
if (Array.isArray(compareValue)) {
|
|
438
|
-
compareValue = compareValue.map((val) => BigInt(val));
|
|
439
|
-
}
|
|
440
|
-
else {
|
|
441
|
-
compareValue = BigInt(compareValue);
|
|
442
|
-
}
|
|
443
|
-
}
|
|
444
433
|
}
|
|
445
434
|
if (operator === '_eq') {
|
|
446
435
|
dbQuery[logical].where(selectionRaw, '=', compareValue);
|
|
@@ -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,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,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']);
|
|
@@ -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
|
|
@@ -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
|
@@ -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),
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
+
import { useEnv } from '@directus/env';
|
|
1
2
|
import { toArray } from '@directus/utils';
|
|
2
3
|
import { constants } from 'fs';
|
|
3
4
|
import { access } from 'node:fs/promises';
|
|
4
5
|
import path from 'path';
|
|
5
|
-
import env from '../env.js';
|
|
6
6
|
import { getExtensionsPath } from '../extensions/lib/get-extensions-path.js';
|
|
7
|
-
import
|
|
7
|
+
import { useLogger } from '../logger.js';
|
|
8
8
|
export async function validateStorage() {
|
|
9
|
+
const env = useEnv();
|
|
10
|
+
const logger = useLogger();
|
|
9
11
|
if (env['DB_CLIENT'] === 'sqlite3') {
|
|
10
12
|
try {
|
|
11
13
|
await access(path.dirname(env['DB_FILENAME']), constants.R_OK | constants.W_OK);
|