@directus/api 28.0.3 → 29.1.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/auth/drivers/openid.js +12 -6
- package/dist/cli/commands/init/index.js +2 -2
- package/dist/cli/commands/init/questions.d.ts +1 -1
- package/dist/cli/commands/schema/apply.js +1 -1
- package/dist/cli/utils/create-db-connection.d.ts +1 -1
- package/dist/cli/utils/create-env/index.d.ts +1 -1
- package/dist/cli/utils/drivers.d.ts +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/controllers/assets.js +1 -1
- package/dist/controllers/extensions.js +4 -4
- package/dist/database/get-ast-from-query/lib/parse-fields.js +3 -1
- package/dist/database/helpers/index.d.ts +4 -4
- package/dist/database/helpers/schema/types.d.ts +1 -2
- package/dist/database/index.d.ts +1 -1
- package/dist/database/migrations/20210225A-add-relations-sort-field.js +1 -1
- package/dist/database/migrations/20240924B-populate-versioning-deltas.js +1 -1
- package/dist/database/migrations/20250718A-add-direction.d.ts +3 -0
- package/dist/database/migrations/20250718A-add-direction.js +10 -0
- package/dist/database/run-ast/lib/apply-query/filter/get-filter-type.d.ts +2 -2
- package/dist/database/run-ast/utils/apply-parent-filters.js +2 -0
- package/dist/database/run-ast/utils/get-field-alias.js +2 -0
- package/dist/database/run-ast/utils/remove-temporary-fields.js +8 -2
- package/dist/extensions/lib/get-extensions-settings.d.ts +1 -1
- package/dist/extensions/lib/sandbox/generate-api-extensions-sandbox-entrypoint.d.ts +1 -1
- package/dist/extensions/manager.d.ts +5 -13
- package/dist/extensions/manager.js +39 -28
- package/dist/flows.d.ts +1 -2
- package/dist/operations/throw-error/index.d.ts +7 -0
- package/dist/operations/throw-error/index.js +11 -0
- package/dist/permissions/modules/validate-access/lib/validate-item-access.js +2 -1
- package/dist/permissions/utils/fetch-share-info.d.ts +1 -1
- package/dist/services/access.d.ts +1 -2
- package/dist/services/access.js +1 -1
- package/dist/services/activity.d.ts +1 -1
- package/dist/services/assets.d.ts +1 -3
- package/dist/services/authentication.d.ts +1 -2
- package/dist/services/collections.d.ts +3 -10
- package/dist/services/comments.d.ts +1 -2
- package/dist/services/dashboards.d.ts +1 -1
- package/dist/services/extensions.d.ts +2 -4
- package/dist/services/fields.d.ts +1 -2
- package/dist/services/fields.js +2 -8
- package/dist/services/files.d.ts +2 -3
- package/dist/services/flows.d.ts +1 -2
- package/dist/services/folders.d.ts +1 -1
- package/dist/services/graphql/errors/format.d.ts +1 -1
- package/dist/services/graphql/errors/format.js +0 -1
- package/dist/services/graphql/index.d.ts +1 -3
- package/dist/services/graphql/resolvers/system-admin.d.ts +1 -1
- package/dist/services/graphql/resolvers/system-global.d.ts +1 -1
- package/dist/services/graphql/resolvers/system.d.ts +1 -1
- package/dist/services/graphql/schema/get-types.d.ts +1 -1
- package/dist/services/graphql/schema/get-types.js +0 -1
- package/dist/services/import-export.d.ts +2 -4
- package/dist/services/import-export.js +5 -3
- package/dist/services/items.d.ts +2 -12
- package/dist/services/items.js +2 -1
- package/dist/services/mail/index.d.ts +2 -4
- package/dist/services/mail/index.js +11 -5
- package/dist/services/meta.d.ts +1 -2
- package/dist/services/notifications.d.ts +1 -2
- package/dist/services/operations.d.ts +1 -2
- package/dist/services/panels.d.ts +1 -1
- package/dist/services/payload.d.ts +9 -17
- package/dist/services/payload.js +1 -1
- package/dist/services/permissions.d.ts +1 -3
- package/dist/services/policies.d.ts +1 -2
- package/dist/services/policies.js +1 -1
- package/dist/services/presets.d.ts +1 -1
- package/dist/services/relations.d.ts +2 -3
- package/dist/services/revisions.d.ts +1 -2
- package/dist/services/roles.d.ts +1 -2
- package/dist/services/roles.js +1 -1
- package/dist/services/schema.d.ts +1 -2
- package/dist/services/server.d.ts +1 -2
- package/dist/services/settings.d.ts +1 -1
- package/dist/services/shares.d.ts +1 -2
- package/dist/services/specifications.d.ts +1 -2
- package/dist/services/tfa.d.ts +1 -2
- package/dist/services/translations.d.ts +1 -3
- package/dist/services/users.d.ts +1 -2
- package/dist/services/users.js +1 -1
- package/dist/services/utils.d.ts +1 -2
- package/dist/services/versions.d.ts +1 -2
- package/dist/services/webhooks.d.ts +1 -2
- package/dist/services/websocket.d.ts +1 -3
- package/dist/types/ast.d.ts +2 -0
- package/dist/types/auth.d.ts +0 -6
- package/dist/types/index.d.ts +0 -7
- package/dist/types/index.js +0 -7
- package/dist/utils/apply-diff.d.ts +1 -2
- package/dist/utils/apply-diff.js +1 -1
- package/dist/utils/apply-snapshot.d.ts +1 -2
- package/dist/utils/get-ip-from-req.js +1 -1
- package/dist/utils/get-service.d.ts +1 -1
- package/dist/utils/get-snapshot-diff.d.ts +1 -1
- package/dist/utils/get-snapshot-diff.js +1 -1
- package/dist/utils/get-snapshot.d.ts +1 -2
- package/dist/utils/get-snapshot.js +8 -3
- package/dist/utils/schedule.js +4 -1
- package/dist/utils/should-clear-cache.d.ts +1 -1
- package/dist/utils/transformations.d.ts +1 -2
- package/dist/utils/validate-diff.d.ts +1 -1
- package/dist/utils/validate-diff.js +1 -1
- package/dist/utils/validate-snapshot.d.ts +1 -1
- package/dist/utils/validate-snapshot.js +1 -1
- package/dist/utils/validate-user-count-integrity.d.ts +1 -8
- package/dist/utils/validate-user-count-integrity.js +1 -9
- package/dist/websocket/authenticate.d.ts +2 -1
- package/dist/websocket/authenticate.js +25 -4
- package/dist/websocket/controllers/base.d.ts +2 -1
- package/dist/websocket/controllers/base.js +12 -15
- package/dist/websocket/controllers/graphql.js +5 -3
- package/dist/websocket/controllers/logs.js +1 -1
- package/dist/websocket/controllers/rest.d.ts +1 -1
- package/dist/websocket/controllers/rest.js +1 -1
- package/dist/websocket/errors.d.ts +1 -1
- package/dist/websocket/handlers/heartbeat.d.ts +1 -1
- package/dist/websocket/handlers/heartbeat.js +1 -1
- package/dist/websocket/messages.d.ts +57 -308
- package/dist/websocket/messages.js +5 -10
- package/dist/websocket/utils/items.d.ts +1 -1
- package/dist/websocket/utils/wait-for-message.d.ts +1 -1
- package/dist/websocket/utils/wait-for-message.js +1 -1
- package/package.json +86 -85
- package/dist/extensions/types.d.ts +0 -19
- package/dist/extensions/types.js +0 -1
- package/dist/types/assets.d.ts +0 -22
- package/dist/types/assets.js +0 -51
- package/dist/types/database.d.ts +0 -3
- package/dist/types/database.js +0 -1
- package/dist/types/graphql.d.ts +0 -14
- package/dist/types/graphql.js +0 -1
- package/dist/types/items.d.ts +0 -52
- package/dist/types/items.js +0 -1
- package/dist/types/services.d.ts +0 -22
- package/dist/types/services.js +0 -1
- package/dist/types/snapshot.d.ts +0 -55
- package/dist/types/snapshot.js +0 -13
- package/dist/types/webhooks.d.ts +0 -15
- package/dist/types/webhooks.js +0 -1
|
@@ -32,8 +32,9 @@ export class OpenIDAuthDriver extends LocalAuthDriver {
|
|
|
32
32
|
super(options, config);
|
|
33
33
|
const env = useEnv();
|
|
34
34
|
const logger = useLogger();
|
|
35
|
-
const { issuerUrl, clientId, clientSecret, provider, issuerDiscoveryMustSucceed } = config;
|
|
36
|
-
|
|
35
|
+
const { issuerUrl, clientId, clientSecret, clientPrivateKeys, clientTokenEndpointAuthMethod, provider, issuerDiscoveryMustSucceed, } = config;
|
|
36
|
+
const isPrivateKeyJwtAuthMethod = clientTokenEndpointAuthMethod === 'private_key_jwt';
|
|
37
|
+
if (!issuerUrl || !clientId || !(clientSecret || (isPrivateKeyJwtAuthMethod && clientPrivateKeys)) || !provider) {
|
|
37
38
|
logger.error('Invalid provider config');
|
|
38
39
|
throw new InvalidProviderConfigError({ provider });
|
|
39
40
|
}
|
|
@@ -66,7 +67,8 @@ export class OpenIDAuthDriver extends LocalAuthDriver {
|
|
|
66
67
|
if (this.client)
|
|
67
68
|
return this.client;
|
|
68
69
|
const logger = useLogger();
|
|
69
|
-
const { issuerUrl, clientId, clientSecret, provider } = this.config;
|
|
70
|
+
const { issuerUrl, clientId, clientSecret, clientPrivateKeys, clientTokenEndpointAuthMethod, provider } = this.config;
|
|
71
|
+
const isPrivateKeyJwtAuthMethod = clientTokenEndpointAuthMethod === 'private_key_jwt';
|
|
70
72
|
// extract client http overrides/options
|
|
71
73
|
const clientHttpOptions = getConfigFromEnv(`AUTH_${provider.toUpperCase()}_CLIENT_HTTP_`);
|
|
72
74
|
if (clientHttpOptions) {
|
|
@@ -87,17 +89,21 @@ export class OpenIDAuthDriver extends LocalAuthDriver {
|
|
|
87
89
|
}
|
|
88
90
|
// extract client overrides/options excluding CLIENT_ID and CLIENT_SECRET as they are passed directly
|
|
89
91
|
const clientOptionsOverrides = getConfigFromEnv(`AUTH_${provider.toUpperCase()}_CLIENT_`, {
|
|
90
|
-
omitKey: [
|
|
92
|
+
omitKey: [
|
|
93
|
+
`AUTH_${provider.toUpperCase()}_CLIENT_ID`,
|
|
94
|
+
`AUTH_${provider.toUpperCase()}_CLIENT_SECRET`,
|
|
95
|
+
`AUTH_${provider.toUpperCase()}_CLIENT_PRIVATE_KEYS`,
|
|
96
|
+
],
|
|
91
97
|
omitPrefix: [`AUTH_${provider.toUpperCase()}_CLIENT_HTTP_`],
|
|
92
98
|
type: 'underscore',
|
|
93
99
|
});
|
|
94
100
|
const client = new issuer.Client({
|
|
95
101
|
client_id: clientId,
|
|
96
|
-
client_secret: clientSecret,
|
|
102
|
+
...(!isPrivateKeyJwtAuthMethod && { client_secret: clientSecret }),
|
|
97
103
|
redirect_uris: [this.redirectUrl],
|
|
98
104
|
response_types: ['code'],
|
|
99
105
|
...clientOptionsOverrides,
|
|
100
|
-
});
|
|
106
|
+
}, isPrivateKeyJwtAuthMethod ? { keys: clientPrivateKeys } : undefined);
|
|
101
107
|
if (clientHttpOptions) {
|
|
102
108
|
client[custom.http_options] = (_, options) => {
|
|
103
109
|
return {
|
|
@@ -60,7 +60,7 @@ export default async function init() {
|
|
|
60
60
|
default: 'admin@example.com',
|
|
61
61
|
validate: (input) => {
|
|
62
62
|
const emailSchema = Joi.string().email().required();
|
|
63
|
-
const { error } = emailSchema.validate(input);
|
|
63
|
+
const { error } = emailSchema.validate(input.trim());
|
|
64
64
|
if (error)
|
|
65
65
|
throw new Error('The email entered is not a valid email address!');
|
|
66
66
|
return true;
|
|
@@ -87,7 +87,7 @@ export default async function init() {
|
|
|
87
87
|
await db('directus_users').insert({
|
|
88
88
|
...defaultAdminUser,
|
|
89
89
|
id: randomUUID(),
|
|
90
|
-
email: firstUser.email,
|
|
90
|
+
email: firstUser.email.trim(),
|
|
91
91
|
password: firstUser.password,
|
|
92
92
|
role,
|
|
93
93
|
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { parseJSON } from '@directus/utils';
|
|
2
|
+
import { DiffKind } from '@directus/types';
|
|
2
3
|
import chalk from 'chalk';
|
|
3
4
|
import { promises as fs } from 'fs';
|
|
4
5
|
import inquirer from 'inquirer';
|
|
@@ -6,7 +7,6 @@ import { load as loadYaml } from 'js-yaml';
|
|
|
6
7
|
import path from 'path';
|
|
7
8
|
import getDatabase, { isInstalled, validateDatabaseConnection } from '../../../database/index.js';
|
|
8
9
|
import { useLogger } from '../../../logger/index.js';
|
|
9
|
-
import { DiffKind } from '../../../types/index.js';
|
|
10
10
|
import { isNestedMetaUpdate } from '../../../utils/apply-diff.js';
|
|
11
11
|
import { applySnapshot } from '../../../utils/apply-snapshot.js';
|
|
12
12
|
import { getSnapshotDiff } from '../../../utils/get-snapshot-diff.js';
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import type { Driver } from '
|
|
1
|
+
import type { Driver } from '@directus/types';
|
|
2
2
|
import type { Credentials } from '../create-db-connection.js';
|
|
3
3
|
export default function createEnv(client: Driver, credentials: Credentials, directory: string): Promise<void>;
|
package/dist/constants.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { CookieOptions } from 'express';
|
|
2
|
-
import type { TransformationParams } from '
|
|
2
|
+
import type { TransformationParams } from '@directus/types';
|
|
3
3
|
export declare const SYSTEM_ASSET_ALLOW_LIST: TransformationParams[];
|
|
4
4
|
export declare const ASSET_TRANSFORM_QUERY_KEYS: readonly ["key", "transforms", "width", "height", "format", "fit", "quality", "withoutEnlargement", "focal_point_x", "focal_point_y"];
|
|
5
5
|
export declare const FILTER_VARIABLES: string[];
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useEnv } from '@directus/env';
|
|
2
2
|
import { InvalidQueryError, RangeNotSatisfiableError } from '@directus/errors';
|
|
3
|
+
import { TransformationMethods } from '@directus/types';
|
|
3
4
|
import { parseJSON } from '@directus/utils';
|
|
4
5
|
import contentDisposition from 'content-disposition';
|
|
5
6
|
import { Router } from 'express';
|
|
@@ -10,7 +11,6 @@ import { useLogger } from '../logger/index.js';
|
|
|
10
11
|
import useCollection from '../middleware/use-collection.js';
|
|
11
12
|
import { AssetsService } from '../services/assets.js';
|
|
12
13
|
import { PayloadService } from '../services/payload.js';
|
|
13
|
-
import { TransformationMethods } from '../types/assets.js';
|
|
14
14
|
import asyncHandler from '../utils/async-handler.js';
|
|
15
15
|
import { getCacheControlHeader } from '../utils/get-cache-headers.js';
|
|
16
16
|
import { getConfigFromEnv } from '../utils/get-config-from-env.js';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useEnv } from '@directus/env';
|
|
2
2
|
import { ErrorCode, ForbiddenError, isDirectusError, RouteNotFoundError } from '@directus/errors';
|
|
3
|
-
import { EXTENSION_TYPES } from '@directus/
|
|
3
|
+
import { EXTENSION_TYPES } from '@directus/constants';
|
|
4
4
|
import { account, describe, list, } from '@directus/extensions-registry';
|
|
5
5
|
import { isIn } from '@directus/utils';
|
|
6
6
|
import express from 'express';
|
|
@@ -189,10 +189,10 @@ router.get('/sources/:chunk', asyncHandler(async (req, res) => {
|
|
|
189
189
|
const extensionManager = getExtensionManager();
|
|
190
190
|
let source;
|
|
191
191
|
if (chunk === 'index.js') {
|
|
192
|
-
source = extensionManager.
|
|
192
|
+
source = await extensionManager.getAppExtensionChunk();
|
|
193
193
|
}
|
|
194
194
|
else {
|
|
195
|
-
source = extensionManager.getAppExtensionChunk(chunk);
|
|
195
|
+
source = await extensionManager.getAppExtensionChunk(chunk);
|
|
196
196
|
}
|
|
197
197
|
if (source === null) {
|
|
198
198
|
throw new RouteNotFoundError({ path: req.path });
|
|
@@ -200,6 +200,6 @@ router.get('/sources/:chunk', asyncHandler(async (req, res) => {
|
|
|
200
200
|
res.setHeader('Content-Type', 'application/javascript; charset=UTF-8');
|
|
201
201
|
res.setHeader('Cache-Control', getCacheControlHeader(req, getMilliseconds(env['EXTENSIONS_CACHE_TTL']), false, false));
|
|
202
202
|
res.setHeader('Vary', 'Origin, Cache-Control');
|
|
203
|
-
|
|
203
|
+
source.pipe(res);
|
|
204
204
|
}));
|
|
205
205
|
export default router;
|
|
@@ -27,10 +27,12 @@ export async function parseFields(options, context) {
|
|
|
27
27
|
: null;
|
|
28
28
|
const relationalStructure = Object.create(null);
|
|
29
29
|
for (const fieldKey of fields) {
|
|
30
|
+
let alias = false;
|
|
30
31
|
let name = fieldKey;
|
|
31
32
|
if (options.query.alias) {
|
|
32
33
|
// check for field alias (is one of the key)
|
|
33
34
|
if (name in options.query.alias) {
|
|
35
|
+
alias = true;
|
|
34
36
|
name = options.query.alias[fieldKey];
|
|
35
37
|
}
|
|
36
38
|
}
|
|
@@ -100,7 +102,7 @@ export async function parseFields(options, context) {
|
|
|
100
102
|
}
|
|
101
103
|
continue;
|
|
102
104
|
}
|
|
103
|
-
children.push({ type: 'field', name, fieldKey, whenCase: [] });
|
|
105
|
+
children.push({ type: 'field', name, fieldKey, whenCase: [], alias });
|
|
104
106
|
}
|
|
105
107
|
}
|
|
106
108
|
for (const [fieldKey, nestedFields] of Object.entries(relationalStructure)) {
|
|
@@ -9,11 +9,11 @@ import * as schemaHelpers from './schema/index.js';
|
|
|
9
9
|
import * as sequenceHelpers from './sequence/index.js';
|
|
10
10
|
export declare function getHelpers(database: Knex): {
|
|
11
11
|
date: dateHelpers.postgres | dateHelpers.oracle | dateHelpers.mysql | dateHelpers.mssql | dateHelpers.sqlite;
|
|
12
|
-
st: geometryHelpers.
|
|
13
|
-
schema: schemaHelpers.
|
|
12
|
+
st: geometryHelpers.mysql | geometryHelpers.postgres | geometryHelpers.sqlite | geometryHelpers.oracle | geometryHelpers.mssql | geometryHelpers.redshift;
|
|
13
|
+
schema: schemaHelpers.mysql | schemaHelpers.postgres | schemaHelpers.cockroachdb | schemaHelpers.sqlite | schemaHelpers.oracle | schemaHelpers.mssql | schemaHelpers.redshift;
|
|
14
14
|
sequence: sequenceHelpers.mysql | sequenceHelpers.postgres;
|
|
15
|
-
number: numberHelpers.cockroachdb | numberHelpers.
|
|
15
|
+
number: numberHelpers.cockroachdb | numberHelpers.postgres | numberHelpers.sqlite | numberHelpers.oracle | numberHelpers.mssql;
|
|
16
16
|
capabilities: capabilitiesHelpers.postgres | capabilitiesHelpers.oracle | capabilitiesHelpers.mysql;
|
|
17
17
|
};
|
|
18
|
-
export declare function getFunctions(database: Knex, schema: SchemaOverview): fnHelpers.
|
|
18
|
+
export declare function getFunctions(database: Knex, schema: SchemaOverview): fnHelpers.mysql | fnHelpers.postgres | fnHelpers.sqlite | fnHelpers.oracle | fnHelpers.mssql;
|
|
19
19
|
export type Helpers = ReturnType<typeof getHelpers>;
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import type { KNEX_TYPES } from '@directus/constants';
|
|
2
2
|
import type { Column } from '@directus/schema';
|
|
3
|
-
import type { Field, RawField, Relation, Type } from '@directus/types';
|
|
3
|
+
import type { DatabaseClient, Field, RawField, Relation, Type } from '@directus/types';
|
|
4
4
|
import type { Knex } from 'knex';
|
|
5
|
-
import type { DatabaseClient } from '../../../types/index.js';
|
|
6
5
|
import { DatabaseHelper } from '../types.js';
|
|
7
6
|
export type Options = {
|
|
8
7
|
nullable?: boolean;
|
package/dist/database/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { SchemaInspector } from '@directus/schema';
|
|
2
|
+
import type { DatabaseClient } from '@directus/types';
|
|
2
3
|
import type { Knex } from 'knex';
|
|
3
|
-
import type { DatabaseClient } from '../types/index.js';
|
|
4
4
|
export default getDatabase;
|
|
5
5
|
export declare function getDatabase(): Knex;
|
|
6
6
|
export declare function getSchemaInspector(database?: Knex): SchemaInspector;
|
|
@@ -10,7 +10,7 @@ export async function up(knex) {
|
|
|
10
10
|
.from('directus_fields')
|
|
11
11
|
.whereIn('interface', ['one-to-many', 'm2a-builder', 'many-to-many']);
|
|
12
12
|
for (const field of fieldsWithSort) {
|
|
13
|
-
const options = typeof field.options === 'string' ? parseJSON(field.options) : field.options ?? {};
|
|
13
|
+
const options = typeof field.options === 'string' ? parseJSON(field.options) : (field.options ?? {});
|
|
14
14
|
if ('sortField' in options) {
|
|
15
15
|
await knex('directus_relations')
|
|
16
16
|
.update({
|
|
@@ -16,7 +16,7 @@ export async function up(knex) {
|
|
|
16
16
|
.from('directus_revisions')
|
|
17
17
|
.where('version', '=', missingDeltaVersion.id)
|
|
18
18
|
.orderBy('id');
|
|
19
|
-
const deltas = revisions.map((revision) => typeof revision.delta === 'string' ? parseJSON(revision.delta) : revision.delta ?? {});
|
|
19
|
+
const deltas = revisions.map((revision) => typeof revision.delta === 'string' ? parseJSON(revision.delta) : (revision.delta ?? {}));
|
|
20
20
|
const consolidatedDelta = assign({}, ...deltas);
|
|
21
21
|
await trx('directus_versions')
|
|
22
22
|
.update({
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export async function up(knex) {
|
|
2
|
+
await knex.schema.alterTable('directus_users', (table) => {
|
|
3
|
+
table.string('text_direction').defaultTo('auto').notNullable();
|
|
4
|
+
});
|
|
5
|
+
}
|
|
6
|
+
export async function down(knex) {
|
|
7
|
+
await knex.schema.alterTable('directus_users', (table) => {
|
|
8
|
+
table.dropColumn('text_direction');
|
|
9
|
+
});
|
|
10
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { FieldOverview } from '@directus/types';
|
|
2
2
|
export declare function getFilterType(fields: Record<string, FieldOverview>, key: string, collection?: string): {
|
|
3
|
-
type: "string" | "boolean" | "json" | "
|
|
3
|
+
type: "string" | "boolean" | "json" | "text" | "integer" | "float" | "alias" | "uuid" | "binary" | "time" | "dateTime" | "timestamp" | "bigInteger" | "date" | "decimal" | "hash" | "csv" | "geometry" | "geometry.Point" | "geometry.LineString" | "geometry.Polygon" | "geometry.MultiPoint" | "geometry.MultiLineString" | "geometry.MultiPolygon" | "unknown";
|
|
4
4
|
special?: never;
|
|
5
5
|
} | {
|
|
6
|
-
type: "string" | "boolean" | "json" | "
|
|
6
|
+
type: "string" | "boolean" | "json" | "text" | "integer" | "float" | "alias" | "uuid" | "binary" | "time" | "dateTime" | "timestamp" | "bigInteger" | "date" | "decimal" | "hash" | "csv" | "geometry" | "geometry.Point" | "geometry.LineString" | "geometry.Polygon" | "geometry.MultiPoint" | "geometry.MultiLineString" | "geometry.MultiPolygon" | "unknown";
|
|
7
7
|
special: string[];
|
|
8
8
|
};
|
|
@@ -20,6 +20,7 @@ export function applyParentFilters(schema, nestedCollectionNodes, parentItem) {
|
|
|
20
20
|
name: nestedNode.relation.field,
|
|
21
21
|
fieldKey: nestedNode.relation.field,
|
|
22
22
|
whenCase: [],
|
|
23
|
+
alias: false,
|
|
23
24
|
});
|
|
24
25
|
}
|
|
25
26
|
if (nestedNode.relation.meta?.sort_field) {
|
|
@@ -28,6 +29,7 @@ export function applyParentFilters(schema, nestedCollectionNodes, parentItem) {
|
|
|
28
29
|
name: nestedNode.relation.meta.sort_field,
|
|
29
30
|
fieldKey: nestedNode.relation.meta.sort_field,
|
|
30
31
|
whenCase: [],
|
|
32
|
+
alias: false,
|
|
31
33
|
});
|
|
32
34
|
}
|
|
33
35
|
const foreignField = nestedNode.relation.field;
|
|
@@ -38,9 +38,15 @@ export function removeTemporaryFields(schema, rawItem, ast, primaryKeyField, par
|
|
|
38
38
|
}
|
|
39
39
|
else {
|
|
40
40
|
const fields = [];
|
|
41
|
+
const aliasFields = [];
|
|
41
42
|
const nestedCollectionNodes = [];
|
|
42
43
|
for (const child of ast.children) {
|
|
43
|
-
|
|
44
|
+
if ('alias' in child && child.alias === true) {
|
|
45
|
+
aliasFields.push(child.fieldKey);
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
fields.push(child.fieldKey);
|
|
49
|
+
}
|
|
44
50
|
if (child.type !== 'field' && child.type !== 'functionField') {
|
|
45
51
|
nestedCollectionNodes.push(child);
|
|
46
52
|
}
|
|
@@ -65,7 +71,7 @@ export function removeTemporaryFields(schema, rawItem, ast, primaryKeyField, par
|
|
|
65
71
|
: schema.collections[nestedNode.relation.collection].primary, item);
|
|
66
72
|
}
|
|
67
73
|
const fieldsWithFunctionsApplied = fields.map((field) => applyFunctionToColumnName(field));
|
|
68
|
-
item = fields.length > 0 ? pick(rawItem, fieldsWithFunctionsApplied) : rawItem[primaryKeyField];
|
|
74
|
+
item = fields.length > 0 ? pick(rawItem, fieldsWithFunctionsApplied, aliasFields) : rawItem[primaryKeyField];
|
|
69
75
|
items.push(item);
|
|
70
76
|
}
|
|
71
77
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Extension, ExtensionSettings } from '@directus/
|
|
1
|
+
import type { Extension, ExtensionSettings } from '@directus/types';
|
|
2
2
|
/**
|
|
3
3
|
* Loads stored settings for all extensions. Creates empty new rows in extensions tables for
|
|
4
4
|
* extensions that don't have settings yet, and remove any settings for extensions that are no
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ApiExtensionType, HybridExtensionType } from '@directus/
|
|
1
|
+
import type { ApiExtensionType, HybridExtensionType } from '@directus/types';
|
|
2
2
|
import type { Router } from 'express';
|
|
3
3
|
/**
|
|
4
4
|
* Generate the JS to run in the isolate to create the extension's entrypoint to the host
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type { Extension } from '@directus/
|
|
1
|
+
import type { Extension, ExtensionManagerOptions } from '@directus/types';
|
|
2
2
|
import { Router } from 'express';
|
|
3
|
-
import type {
|
|
3
|
+
import type { ReadStream } from 'node:fs';
|
|
4
4
|
export declare class ExtensionManager {
|
|
5
5
|
private options;
|
|
6
6
|
/**
|
|
@@ -14,11 +14,6 @@ export declare class ExtensionManager {
|
|
|
14
14
|
* Settings for the extensions that are loaded within the current process
|
|
15
15
|
*/
|
|
16
16
|
private extensionsSettings;
|
|
17
|
-
/**
|
|
18
|
-
* App extensions rolled up into a single bundle. Any chunks from the bundle will be available
|
|
19
|
-
* under appExtensionChunks
|
|
20
|
-
*/
|
|
21
|
-
private appExtensionsBundle;
|
|
22
17
|
/**
|
|
23
18
|
* Individual filename chunks from the rollup bundle. Used to improve the performance by allowing
|
|
24
19
|
* extensions to split up their bundle into multiple smaller chunks
|
|
@@ -97,13 +92,10 @@ export declare class ExtensionManager {
|
|
|
97
92
|
forceSync: boolean;
|
|
98
93
|
}): Promise<unknown>;
|
|
99
94
|
/**
|
|
100
|
-
* Return the previously generated app
|
|
101
|
-
|
|
102
|
-
getAppExtensionsBundle(): string | null;
|
|
103
|
-
/**
|
|
104
|
-
* Return the previously generated app extension bundle chunk by name
|
|
95
|
+
* Return the previously generated app extension bundle chunk by name.
|
|
96
|
+
* Providing no name will return the entry bundle.
|
|
105
97
|
*/
|
|
106
|
-
getAppExtensionChunk(name
|
|
98
|
+
getAppExtensionChunk(name?: string): Promise<ReadStream | null>;
|
|
107
99
|
/**
|
|
108
100
|
* Return the scoped router for custom endpoints
|
|
109
101
|
*/
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useEnv } from '@directus/env';
|
|
2
|
-
import { APP_SHARED_DEPS
|
|
2
|
+
import { APP_SHARED_DEPS } from '@directus/extensions';
|
|
3
|
+
import { HYBRID_EXTENSION_TYPES } from '@directus/constants';
|
|
3
4
|
import { generateExtensionsEntrypoint } from '@directus/extensions/node';
|
|
4
5
|
import { isTypeIn, toBoolean } from '@directus/utils';
|
|
5
6
|
import { pathToRelativeUrl, processId } from '@directus/utils/node';
|
|
@@ -12,9 +13,10 @@ import ivm from 'isolated-vm';
|
|
|
12
13
|
import { clone, debounce, isPlainObject } from 'lodash-es';
|
|
13
14
|
import { readFile, readdir } from 'node:fs/promises';
|
|
14
15
|
import os from 'node:os';
|
|
15
|
-
import { dirname } from 'node:path';
|
|
16
|
+
import { dirname, join } from 'node:path';
|
|
16
17
|
import { fileURLToPath } from 'node:url';
|
|
17
18
|
import path from 'path';
|
|
19
|
+
import { rolldown } from 'rolldown';
|
|
18
20
|
import { rollup } from 'rollup';
|
|
19
21
|
import { useBus } from '../bus/index.js';
|
|
20
22
|
import getDatabase from '../database/index.js';
|
|
@@ -37,6 +39,7 @@ import { generateApiExtensionsSandboxEntrypoint } from './lib/sandbox/generate-a
|
|
|
37
39
|
import { instantiateSandboxSdk } from './lib/sandbox/sdk/instantiate.js';
|
|
38
40
|
import { syncExtensions } from './lib/sync-extensions.js';
|
|
39
41
|
import { wrapEmbeds } from './lib/wrap-embeds.js';
|
|
42
|
+
import DriverLocal from '@directus/storage-driver-local';
|
|
40
43
|
// Workaround for https://github.com/rollup/plugins/issues/1329
|
|
41
44
|
const virtual = virtualDefault;
|
|
42
45
|
const alias = aliasDefault;
|
|
@@ -63,16 +66,11 @@ export class ExtensionManager {
|
|
|
63
66
|
* Settings for the extensions that are loaded within the current process
|
|
64
67
|
*/
|
|
65
68
|
extensionsSettings = [];
|
|
66
|
-
/**
|
|
67
|
-
* App extensions rolled up into a single bundle. Any chunks from the bundle will be available
|
|
68
|
-
* under appExtensionChunks
|
|
69
|
-
*/
|
|
70
|
-
appExtensionsBundle = null;
|
|
71
69
|
/**
|
|
72
70
|
* Individual filename chunks from the rollup bundle. Used to improve the performance by allowing
|
|
73
71
|
* extensions to split up their bundle into multiple smaller chunks
|
|
74
72
|
*/
|
|
75
|
-
appExtensionChunks =
|
|
73
|
+
appExtensionChunks = [];
|
|
76
74
|
/**
|
|
77
75
|
* Callbacks to be able to unregister extensions
|
|
78
76
|
*/
|
|
@@ -220,7 +218,7 @@ export class ExtensionManager {
|
|
|
220
218
|
}
|
|
221
219
|
await Promise.all([this.registerInternalOperations(), this.registerApiExtensions()]);
|
|
222
220
|
if (env['SERVE_APP']) {
|
|
223
|
-
|
|
221
|
+
await this.generateExtensionBundle();
|
|
224
222
|
}
|
|
225
223
|
this.isLoaded = true;
|
|
226
224
|
emitter.emitAction('extensions.load', {
|
|
@@ -234,7 +232,6 @@ export class ExtensionManager {
|
|
|
234
232
|
async unload() {
|
|
235
233
|
await this.unregisterApiExtensions();
|
|
236
234
|
this.localEmitter.offAll();
|
|
237
|
-
this.appExtensionsBundle = null;
|
|
238
235
|
this.isLoaded = false;
|
|
239
236
|
emitter.emitAction('extensions.unload', {
|
|
240
237
|
extensions: this.extensions,
|
|
@@ -289,16 +286,24 @@ export class ExtensionManager {
|
|
|
289
286
|
return promise;
|
|
290
287
|
}
|
|
291
288
|
/**
|
|
292
|
-
* Return the previously generated app
|
|
293
|
-
|
|
294
|
-
getAppExtensionsBundle() {
|
|
295
|
-
return this.appExtensionsBundle;
|
|
296
|
-
}
|
|
297
|
-
/**
|
|
298
|
-
* Return the previously generated app extension bundle chunk by name
|
|
289
|
+
* Return the previously generated app extension bundle chunk by name.
|
|
290
|
+
* Providing no name will return the entry bundle.
|
|
299
291
|
*/
|
|
300
|
-
getAppExtensionChunk(name) {
|
|
301
|
-
|
|
292
|
+
async getAppExtensionChunk(name) {
|
|
293
|
+
let file;
|
|
294
|
+
if (!name) {
|
|
295
|
+
file = this.appExtensionChunks[0];
|
|
296
|
+
}
|
|
297
|
+
else if (this.appExtensionChunks.includes(name)) {
|
|
298
|
+
file = name;
|
|
299
|
+
}
|
|
300
|
+
if (!file)
|
|
301
|
+
return null;
|
|
302
|
+
const tempDir = join(env['TEMP_PATH'], 'app-extensions');
|
|
303
|
+
const tmpStorage = new DriverLocal({ root: tempDir });
|
|
304
|
+
if ((await tmpStorage.exists(file)) === false)
|
|
305
|
+
return null;
|
|
306
|
+
return await tmpStorage.read(file);
|
|
302
307
|
}
|
|
303
308
|
/**
|
|
304
309
|
* Return the scoped router for custom endpoints
|
|
@@ -369,6 +374,7 @@ export class ExtensionManager {
|
|
|
369
374
|
*/
|
|
370
375
|
async generateExtensionBundle() {
|
|
371
376
|
const logger = useLogger();
|
|
377
|
+
const env = useEnv();
|
|
372
378
|
const sharedDepsMapping = await getSharedDepsMapping(APP_SHARED_DEPS);
|
|
373
379
|
const internalImports = Object.entries(sharedDepsMapping).map(([name, path]) => ({
|
|
374
380
|
find: name,
|
|
@@ -376,26 +382,30 @@ export class ExtensionManager {
|
|
|
376
382
|
}));
|
|
377
383
|
const entrypoint = generateExtensionsEntrypoint({ module: this.moduleExtensions, registry: this.registryExtensions, local: this.localExtensions }, this.extensionsSettings);
|
|
378
384
|
try {
|
|
379
|
-
|
|
385
|
+
/** Opt In for now. Should be @deprecated later to always use rolldown! */
|
|
386
|
+
const rollDirection = (env['EXTENSIONS_ROLLDOWN'] ?? false) ? rolldown : rollup;
|
|
387
|
+
const bundle = await rollDirection({
|
|
380
388
|
input: 'entry',
|
|
381
389
|
external: Object.values(sharedDepsMapping),
|
|
382
390
|
makeAbsoluteExternalsRelative: false,
|
|
383
391
|
plugins: [virtual({ entry: entrypoint }), alias({ entries: internalImports }), nodeResolve({ browser: true })],
|
|
384
392
|
});
|
|
385
|
-
const
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
393
|
+
const tempDir = join(env['TEMP_PATH'], 'app-extensions');
|
|
394
|
+
const { output } = await bundle.write({
|
|
395
|
+
format: 'es',
|
|
396
|
+
dir: tempDir,
|
|
397
|
+
});
|
|
398
|
+
this.appExtensionChunks = output.reduce((acc, chunk) => {
|
|
399
|
+
if (chunk.type === 'chunk')
|
|
400
|
+
acc.push(chunk.fileName);
|
|
401
|
+
return acc;
|
|
402
|
+
}, []);
|
|
391
403
|
await bundle.close();
|
|
392
|
-
return output[0].code;
|
|
393
404
|
}
|
|
394
405
|
catch (error) {
|
|
395
406
|
logger.warn(`Couldn't bundle App extensions`);
|
|
396
407
|
logger.warn(error);
|
|
397
408
|
}
|
|
398
|
-
return null;
|
|
399
409
|
}
|
|
400
410
|
async registerSandboxedApiExtension(extension) {
|
|
401
411
|
const logger = useLogger();
|
|
@@ -412,6 +422,7 @@ export class ExtensionManager {
|
|
|
412
422
|
},
|
|
413
423
|
});
|
|
414
424
|
const context = await isolate.createContext();
|
|
425
|
+
context.global.setSync('process', { env: { NODE_ENV: process.env['NODE_ENV'] ?? 'production' } }, { copy: true });
|
|
415
426
|
const module = await isolate.compileModule(extensionCode, { filename: `file://${entrypointPath}` });
|
|
416
427
|
const sdkModule = await instantiateSandboxSdk(isolate, extension.sandbox?.requestedScopes ?? {});
|
|
417
428
|
await module.instantiate(context, (specifier) => {
|
package/dist/flows.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type { OperationHandler } from '@directus/
|
|
2
|
-
import type { Accountability, SchemaOverview } from '@directus/types';
|
|
1
|
+
import type { Accountability, SchemaOverview, OperationHandler } from '@directus/types';
|
|
3
2
|
export declare function getFlowManager(): FlowManager;
|
|
4
3
|
declare class FlowManager {
|
|
5
4
|
private isLoaded;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { createError, InternalServerError } from '@directus/errors';
|
|
2
|
+
import { defineOperationApi } from '@directus/extensions';
|
|
3
|
+
const FALLBACK_ERROR = new InternalServerError();
|
|
4
|
+
export default defineOperationApi({
|
|
5
|
+
id: 'throw-error',
|
|
6
|
+
handler: ({ code, status, message }) => {
|
|
7
|
+
const statusCode = parseInt(status);
|
|
8
|
+
const error = createError(code ?? FALLBACK_ERROR.code, message ?? FALLBACK_ERROR.message, isNaN(statusCode) ? FALLBACK_ERROR.status : statusCode);
|
|
9
|
+
throw new error();
|
|
10
|
+
},
|
|
11
|
+
});
|
|
@@ -13,7 +13,8 @@ export async function validateItemAccess(options, context) {
|
|
|
13
13
|
name: options.collection,
|
|
14
14
|
query: { limit: options.primaryKeys.length },
|
|
15
15
|
// Act as if every field was a "normal" field
|
|
16
|
-
children: options.fields?.map((field) => ({ type: 'field', name: field, fieldKey: field, whenCase: [] })) ??
|
|
16
|
+
children: options.fields?.map((field) => ({ type: 'field', name: field, fieldKey: field, whenCase: [], alias: false })) ??
|
|
17
|
+
[],
|
|
17
18
|
cases: [],
|
|
18
19
|
};
|
|
19
20
|
await processAst({ ast, ...options }, context);
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import type { Item, PrimaryKey } from '@directus/types';
|
|
2
|
-
import type { AbstractServiceOptions, MutationOptions } from '../types/index.js';
|
|
1
|
+
import type { AbstractServiceOptions, Item, MutationOptions, PrimaryKey } from '@directus/types';
|
|
3
2
|
import { ItemsService } from './items.js';
|
|
4
3
|
export declare class AccessService extends ItemsService {
|
|
5
4
|
constructor(options: AbstractServiceOptions);
|
package/dist/services/access.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { UserIntegrityCheckFlag } from '@directus/types';
|
|
1
2
|
import { clearSystemCache } from '../cache.js';
|
|
2
|
-
import { UserIntegrityCheckFlag } from '../utils/validate-user-count-integrity.js';
|
|
3
3
|
import { ItemsService } from './items.js';
|
|
4
4
|
export class AccessService extends ItemsService {
|
|
5
5
|
constructor(options) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AbstractServiceOptions } from '
|
|
1
|
+
import type { AbstractServiceOptions } from '@directus/types';
|
|
2
2
|
import { ItemsService } from './items.js';
|
|
3
3
|
export declare class ActivityService extends ItemsService {
|
|
4
4
|
constructor(options: AbstractServiceOptions);
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import type { Range, Stat } from '@directus/
|
|
2
|
-
import type { Accountability, SchemaOverview } from '@directus/types';
|
|
1
|
+
import type { AbstractServiceOptions, Accountability, Range, Stat, SchemaOverview, TransformationSet } from '@directus/types';
|
|
3
2
|
import type { Knex } from 'knex';
|
|
4
3
|
import type { Readable } from 'node:stream';
|
|
5
|
-
import type { AbstractServiceOptions, TransformationSet } from '../types/index.js';
|
|
6
4
|
import { FilesService } from './files.js';
|
|
7
5
|
export declare class AssetsService {
|
|
8
6
|
knex: Knex;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import type { Accountability, SchemaOverview } from '@directus/types';
|
|
1
|
+
import type { AbstractServiceOptions, Accountability, LoginResult, SchemaOverview } from '@directus/types';
|
|
2
2
|
import type { Knex } from 'knex';
|
|
3
|
-
import type { AbstractServiceOptions, LoginResult } from '../types/index.js';
|
|
4
3
|
import { ActivityService } from './activity.js';
|
|
5
4
|
export declare class AuthenticationService {
|
|
6
5
|
knex: Knex;
|