@directus/api 23.3.2 → 24.0.1
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/constants.d.ts +2 -2
- package/dist/controllers/files.js +1 -1
- package/dist/database/get-ast-from-query/lib/parse-fields.js +4 -3
- package/dist/database/helpers/index.d.ts +1 -3
- package/dist/database/helpers/index.js +1 -3
- package/dist/database/helpers/schema/dialects/mssql.d.ts +1 -0
- package/dist/database/helpers/schema/dialects/mssql.js +4 -0
- package/dist/database/helpers/schema/dialects/mysql.d.ts +1 -0
- package/dist/database/helpers/schema/dialects/mysql.js +4 -0
- package/dist/database/helpers/schema/dialects/oracle.d.ts +7 -1
- package/dist/database/helpers/schema/dialects/oracle.js +28 -0
- package/dist/database/helpers/schema/dialects/postgres.d.ts +1 -0
- package/dist/database/helpers/schema/dialects/postgres.js +4 -0
- package/dist/database/helpers/schema/dialects/sqlite.d.ts +1 -0
- package/dist/database/helpers/schema/dialects/sqlite.js +4 -0
- package/dist/database/helpers/schema/types.d.ts +4 -1
- package/dist/database/helpers/schema/types.js +13 -0
- package/dist/database/run-ast/utils/merge-with-parent-items.js +39 -15
- package/dist/middleware/respond.js +2 -1
- package/dist/operations/mail/index.d.ts +6 -3
- package/dist/operations/mail/index.js +2 -2
- package/dist/services/collections.js +1 -1
- package/dist/services/fields.d.ts +1 -1
- package/dist/services/fields.js +37 -41
- package/dist/services/files.js +6 -5
- package/dist/services/tus/data-store.d.ts +4 -4
- package/dist/services/tus/data-store.js +4 -2
- package/dist/services/tus/server.js +1 -1
- package/dist/utils/get-column.d.ts +1 -1
- package/dist/utils/get-default-index-name.d.ts +6 -3
- package/dist/utils/get-default-index-name.js +6 -5
- package/dist/utils/get-schema.js +1 -1
- package/dist/utils/sanitize-schema.d.ts +1 -1
- package/license +1 -1
- package/package.json +74 -76
- package/dist/database/helpers/nullable-update/dialects/default.d.ts +0 -3
- package/dist/database/helpers/nullable-update/dialects/default.js +0 -3
- package/dist/database/helpers/nullable-update/dialects/oracle.d.ts +0 -12
- package/dist/database/helpers/nullable-update/dialects/oracle.js +0 -16
- package/dist/database/helpers/nullable-update/index.d.ts +0 -7
- package/dist/database/helpers/nullable-update/index.js +0 -7
- package/dist/database/helpers/nullable-update/types.d.ts +0 -7
- package/dist/database/helpers/nullable-update/types.js +0 -12
package/dist/constants.d.ts
CHANGED
|
@@ -18,8 +18,8 @@ export declare const SUPPORTED_IMAGE_METADATA_FORMATS: string[];
|
|
|
18
18
|
/** Resumable uploads */
|
|
19
19
|
export declare const RESUMABLE_UPLOADS: {
|
|
20
20
|
ENABLED: boolean;
|
|
21
|
-
CHUNK_SIZE: number;
|
|
22
|
-
MAX_SIZE: number;
|
|
21
|
+
CHUNK_SIZE: number | null;
|
|
22
|
+
MAX_SIZE: number | null;
|
|
23
23
|
EXPIRATION_TIME: number;
|
|
24
24
|
SCHEDULE: string;
|
|
25
25
|
};
|
|
@@ -35,7 +35,7 @@ export const multipartHandler = (req, res, next) => {
|
|
|
35
35
|
headers,
|
|
36
36
|
defParamCharset: 'utf8',
|
|
37
37
|
limits: {
|
|
38
|
-
fileSize:
|
|
38
|
+
fileSize: bytes.parse(env['FILES_MAX_UPLOAD_SIZE']) ?? undefined,
|
|
39
39
|
},
|
|
40
40
|
});
|
|
41
41
|
const savedFiles = [];
|
|
@@ -169,10 +169,11 @@ export async function parseFields(options, context) {
|
|
|
169
169
|
continue;
|
|
170
170
|
}
|
|
171
171
|
}
|
|
172
|
+
const childQuery = { ...options.query };
|
|
172
173
|
// update query alias for children parseFields
|
|
173
174
|
const deepAlias = getDeepQuery(options.deep?.[fieldKey] || {})?.['alias'];
|
|
174
|
-
if
|
|
175
|
-
|
|
175
|
+
// reset alias to empty if none are present
|
|
176
|
+
childQuery.alias = isEmpty(deepAlias) ? {} : deepAlias;
|
|
176
177
|
child = {
|
|
177
178
|
type: relationType,
|
|
178
179
|
name: relatedCollection,
|
|
@@ -184,7 +185,7 @@ export async function parseFields(options, context) {
|
|
|
184
185
|
children: await parseFields({
|
|
185
186
|
parentCollection: relatedCollection,
|
|
186
187
|
fields: nestedFields,
|
|
187
|
-
query:
|
|
188
|
+
query: childQuery,
|
|
188
189
|
deep: options.deep?.[fieldKey] || {},
|
|
189
190
|
accountability: options.accountability,
|
|
190
191
|
}, context),
|
|
@@ -3,17 +3,15 @@ import type { Knex } from 'knex';
|
|
|
3
3
|
import * as dateHelpers from './date/index.js';
|
|
4
4
|
import * as fnHelpers from './fn/index.js';
|
|
5
5
|
import * as geometryHelpers from './geometry/index.js';
|
|
6
|
+
import * as numberHelpers from './number/index.js';
|
|
6
7
|
import * as schemaHelpers from './schema/index.js';
|
|
7
8
|
import * as sequenceHelpers from './sequence/index.js';
|
|
8
|
-
import * as numberHelpers from './number/index.js';
|
|
9
|
-
import * as nullableUpdateHelper from './nullable-update/index.js';
|
|
10
9
|
export declare function getHelpers(database: Knex): {
|
|
11
10
|
date: dateHelpers.postgres | dateHelpers.oracle | dateHelpers.mysql | dateHelpers.mssql | dateHelpers.sqlite;
|
|
12
11
|
st: geometryHelpers.postgres | geometryHelpers.mssql | geometryHelpers.mysql | geometryHelpers.sqlite | geometryHelpers.oracle | geometryHelpers.redshift;
|
|
13
12
|
schema: schemaHelpers.cockroachdb | schemaHelpers.mssql | schemaHelpers.mysql | schemaHelpers.postgres | schemaHelpers.sqlite | schemaHelpers.oracle | schemaHelpers.redshift;
|
|
14
13
|
sequence: sequenceHelpers.mysql | sequenceHelpers.postgres;
|
|
15
14
|
number: numberHelpers.cockroachdb | numberHelpers.mssql | numberHelpers.postgres | numberHelpers.sqlite | numberHelpers.oracle;
|
|
16
|
-
nullableUpdate: nullableUpdateHelper.postgres | nullableUpdateHelper.oracle;
|
|
17
15
|
};
|
|
18
16
|
export declare function getFunctions(database: Knex, schema: SchemaOverview): fnHelpers.postgres | fnHelpers.mssql | fnHelpers.mysql | fnHelpers.sqlite | fnHelpers.oracle;
|
|
19
17
|
export type Helpers = ReturnType<typeof getHelpers>;
|
|
@@ -2,10 +2,9 @@ import { getDatabaseClient } from '../index.js';
|
|
|
2
2
|
import * as dateHelpers from './date/index.js';
|
|
3
3
|
import * as fnHelpers from './fn/index.js';
|
|
4
4
|
import * as geometryHelpers from './geometry/index.js';
|
|
5
|
+
import * as numberHelpers from './number/index.js';
|
|
5
6
|
import * as schemaHelpers from './schema/index.js';
|
|
6
7
|
import * as sequenceHelpers from './sequence/index.js';
|
|
7
|
-
import * as numberHelpers from './number/index.js';
|
|
8
|
-
import * as nullableUpdateHelper from './nullable-update/index.js';
|
|
9
8
|
export function getHelpers(database) {
|
|
10
9
|
const client = getDatabaseClient(database);
|
|
11
10
|
return {
|
|
@@ -14,7 +13,6 @@ export function getHelpers(database) {
|
|
|
14
13
|
schema: new schemaHelpers[client](database),
|
|
15
14
|
sequence: new sequenceHelpers[client](database),
|
|
16
15
|
number: new numberHelpers[client](database),
|
|
17
|
-
nullableUpdate: new nullableUpdateHelper[client](database),
|
|
18
16
|
};
|
|
19
17
|
}
|
|
20
18
|
export function getFunctions(database, schema) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Knex } from 'knex';
|
|
2
2
|
import { SchemaHelper, type SortRecord, type Sql } from '../types.js';
|
|
3
3
|
export declare class SchemaHelperMSSQL extends SchemaHelper {
|
|
4
|
+
generateIndexName(type: 'unique' | 'foreign' | 'index', collection: string, fields: string | string[]): string;
|
|
4
5
|
applyLimit(rootQuery: Knex.QueryBuilder, limit: number): void;
|
|
5
6
|
applyOffset(rootQuery: Knex.QueryBuilder, offset: number): void;
|
|
6
7
|
formatUUID(uuid: string): string;
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
import { getDefaultIndexName } from '../../../../utils/get-default-index-name.js';
|
|
1
2
|
import { SchemaHelper } from '../types.js';
|
|
2
3
|
import { prepQueryParams } from '../utils/prep-query-params.js';
|
|
3
4
|
export class SchemaHelperMSSQL extends SchemaHelper {
|
|
5
|
+
generateIndexName(type, collection, fields) {
|
|
6
|
+
return getDefaultIndexName(type, collection, fields, { maxLength: 128 });
|
|
7
|
+
}
|
|
4
8
|
applyLimit(rootQuery, limit) {
|
|
5
9
|
// The ORDER BY clause is invalid in views, inline functions, derived tables, subqueries,
|
|
6
10
|
// and common table expressions, unless TOP, OFFSET or FOR XML is also specified.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Knex } from 'knex';
|
|
2
2
|
import { SchemaHelper, type SortRecord } from '../types.js';
|
|
3
3
|
export declare class SchemaHelperMySQL extends SchemaHelper {
|
|
4
|
+
generateIndexName(type: 'unique' | 'foreign' | 'index', collection: string, fields: string | string[]): string;
|
|
4
5
|
applyMultiRelationalSort(knex: Knex, dbQuery: Knex.QueryBuilder, table: string, primaryKey: string, orderByString: string, orderByFields: Knex.Raw[]): Knex.QueryBuilder;
|
|
5
6
|
getDatabaseSize(): Promise<number | null>;
|
|
6
7
|
addInnerSortFieldsToGroupBy(groupByFields: (string | Knex.Raw)[], sortRecords: SortRecord[], hasRelationalSort: boolean): void;
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { useEnv } from '@directus/env';
|
|
2
|
+
import { getDefaultIndexName } from '../../../../utils/get-default-index-name.js';
|
|
2
3
|
import { getDatabaseVersion } from '../../../index.js';
|
|
3
4
|
import { SchemaHelper } from '../types.js';
|
|
4
5
|
const env = useEnv();
|
|
5
6
|
export class SchemaHelperMySQL extends SchemaHelper {
|
|
7
|
+
generateIndexName(type, collection, fields) {
|
|
8
|
+
return getDefaultIndexName(type, collection, fields, { maxLength: 64 });
|
|
9
|
+
}
|
|
6
10
|
applyMultiRelationalSort(knex, dbQuery, table, primaryKey, orderByString, orderByFields) {
|
|
7
11
|
if (getDatabaseVersion()?.startsWith('5.7')) {
|
|
8
12
|
dbQuery.orderByRaw(`?? asc, ${orderByString}`, [`${table}.${primaryKey}`, ...orderByFields]);
|
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
import type { KNEX_TYPES } from '@directus/constants';
|
|
2
|
-
import type {
|
|
2
|
+
import type { Column } from '@directus/schema';
|
|
3
|
+
import type { Field, RawField, Relation, Type } from '@directus/types';
|
|
3
4
|
import type { Knex } from 'knex';
|
|
4
5
|
import type { Options, SortRecord, Sql } from '../types.js';
|
|
5
6
|
import { SchemaHelper } from '../types.js';
|
|
6
7
|
export declare class SchemaHelperOracle extends SchemaHelper {
|
|
8
|
+
generateIndexName(type: 'unique' | 'foreign' | 'index', collection: string, fields: string | string[]): string;
|
|
7
9
|
changeToType(table: string, column: string, type: (typeof KNEX_TYPES)[number], options?: Options): Promise<void>;
|
|
8
10
|
castA2oPrimaryKey(): string;
|
|
9
11
|
preRelationChange(relation: Partial<Relation>): void;
|
|
10
12
|
processFieldType(field: Field): Type;
|
|
11
13
|
getDatabaseSize(): Promise<number | null>;
|
|
14
|
+
/**
|
|
15
|
+
* Oracle throws an error when overwriting the nullable option for an existing column with the same value.
|
|
16
|
+
*/
|
|
17
|
+
setNullable(column: Knex.ColumnBuilder, field: RawField | Field, existing: Column | null): void;
|
|
12
18
|
prepQueryParams(queryParams: Sql): Sql;
|
|
13
19
|
prepBindings(bindings: Knex.Value[]): any;
|
|
14
20
|
addInnerSortFieldsToGroupBy(groupByFields: (string | Knex.Raw)[], sortRecords: SortRecord[], _hasRelationalSort: boolean): void;
|
|
@@ -1,6 +1,19 @@
|
|
|
1
|
+
import crypto from 'node:crypto';
|
|
2
|
+
import { getDefaultIndexName } from '../../../../utils/get-default-index-name.js';
|
|
1
3
|
import { SchemaHelper } from '../types.js';
|
|
2
4
|
import { prepQueryParams } from '../utils/prep-query-params.js';
|
|
3
5
|
export class SchemaHelperOracle extends SchemaHelper {
|
|
6
|
+
generateIndexName(type, collection, fields) {
|
|
7
|
+
// Backwards compatibility with oracle requires no hash added to the name.
|
|
8
|
+
let indexName = getDefaultIndexName(type, collection, fields, { maxLength: Infinity });
|
|
9
|
+
// Knex generates a hash of the name if it is above the allowed value
|
|
10
|
+
// https://github.com/knex/knex/blob/master/lib/dialects/oracle/utils.js#L20
|
|
11
|
+
if (indexName.length > 128) {
|
|
12
|
+
// generates the sha1 of the name and encode it with base64
|
|
13
|
+
indexName = crypto.createHash('sha1').update(indexName).digest('base64').replace('=', '');
|
|
14
|
+
}
|
|
15
|
+
return indexName;
|
|
16
|
+
}
|
|
4
17
|
async changeToType(table, column, type, options = {}) {
|
|
5
18
|
await this.changeToTypeByCopy(table, column, type, options);
|
|
6
19
|
}
|
|
@@ -39,6 +52,21 @@ export class SchemaHelperOracle extends SchemaHelper {
|
|
|
39
52
|
return null;
|
|
40
53
|
}
|
|
41
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* Oracle throws an error when overwriting the nullable option for an existing column with the same value.
|
|
57
|
+
*/
|
|
58
|
+
setNullable(column, field, existing) {
|
|
59
|
+
if (!existing) {
|
|
60
|
+
super.setNullable(column, field, existing);
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (field.schema?.is_nullable === false && existing.is_nullable === true) {
|
|
64
|
+
column.notNullable();
|
|
65
|
+
}
|
|
66
|
+
else if (field.schema?.is_nullable === true && existing.is_nullable === false) {
|
|
67
|
+
column.nullable();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
42
70
|
prepQueryParams(queryParams) {
|
|
43
71
|
return prepQueryParams(queryParams, { format: (index) => `:${index + 1}` });
|
|
44
72
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { Knex } from 'knex';
|
|
2
2
|
import { SchemaHelper, type SortRecord, type Sql } from '../types.js';
|
|
3
3
|
export declare class SchemaHelperPostgres extends SchemaHelper {
|
|
4
|
+
generateIndexName(type: 'unique' | 'foreign' | 'index', collection: string, fields: string | string[]): string;
|
|
4
5
|
getDatabaseSize(): Promise<number | null>;
|
|
5
6
|
prepQueryParams(queryParams: Sql): Sql;
|
|
6
7
|
addInnerSortFieldsToGroupBy(groupByFields: (string | Knex.Raw)[], sortRecords: SortRecord[], hasRelationalSort: boolean): void;
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { useEnv } from '@directus/env';
|
|
2
|
+
import { getDefaultIndexName } from '../../../../utils/get-default-index-name.js';
|
|
2
3
|
import { SchemaHelper } from '../types.js';
|
|
3
4
|
import { prepQueryParams } from '../utils/prep-query-params.js';
|
|
4
5
|
const env = useEnv();
|
|
5
6
|
export class SchemaHelperPostgres extends SchemaHelper {
|
|
7
|
+
generateIndexName(type, collection, fields) {
|
|
8
|
+
return getDefaultIndexName(type, collection, fields, { maxLength: 63 });
|
|
9
|
+
}
|
|
6
10
|
async getDatabaseSize() {
|
|
7
11
|
try {
|
|
8
12
|
const result = await this.knex.select(this.knex.raw(`pg_database_size(?) as size;`, [env['DB_DATABASE']]));
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { SchemaHelper } from '../types.js';
|
|
2
2
|
export declare class SchemaHelperSQLite extends SchemaHelper {
|
|
3
|
+
generateIndexName(type: 'unique' | 'foreign' | 'index', collection: string, fields: string | string[]): string;
|
|
3
4
|
preColumnChange(): Promise<boolean>;
|
|
4
5
|
postColumnChange(): Promise<void>;
|
|
5
6
|
getDatabaseSize(): Promise<number | null>;
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
import { getDefaultIndexName } from '../../../../utils/get-default-index-name.js';
|
|
1
2
|
import { SchemaHelper } from '../types.js';
|
|
2
3
|
export class SchemaHelperSQLite extends SchemaHelper {
|
|
4
|
+
generateIndexName(type, collection, fields) {
|
|
5
|
+
return getDefaultIndexName(type, collection, fields, { maxLength: Infinity });
|
|
6
|
+
}
|
|
3
7
|
async preColumnChange() {
|
|
4
8
|
const foreignCheckStatus = (await this.knex.raw('PRAGMA foreign_keys'))[0].foreign_keys === 1;
|
|
5
9
|
if (foreignCheckStatus) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { KNEX_TYPES } from '@directus/constants';
|
|
2
|
-
import type {
|
|
2
|
+
import type { Column } from '@directus/schema';
|
|
3
|
+
import type { Field, RawField, Relation, Type } from '@directus/types';
|
|
3
4
|
import type { Knex } from 'knex';
|
|
4
5
|
import type { DatabaseClient } from '../../../types/index.js';
|
|
5
6
|
import { DatabaseHelper } from '../types.js';
|
|
@@ -19,11 +20,13 @@ export type SortRecord = {
|
|
|
19
20
|
export declare abstract class SchemaHelper extends DatabaseHelper {
|
|
20
21
|
isOneOfClients(clients: DatabaseClient[]): boolean;
|
|
21
22
|
changeNullable(table: string, column: string, nullable: boolean): Promise<void>;
|
|
23
|
+
generateIndexName(type: 'unique' | 'foreign' | 'index', collection: string, fields: string | string[]): string;
|
|
22
24
|
changeToType(table: string, column: string, type: (typeof KNEX_TYPES)[number], options?: Options): Promise<void>;
|
|
23
25
|
protected changeToTypeByCopy(table: string, column: string, type: (typeof KNEX_TYPES)[number], options: Options): Promise<void>;
|
|
24
26
|
preColumnChange(): Promise<boolean>;
|
|
25
27
|
postColumnChange(): Promise<void>;
|
|
26
28
|
preRelationChange(_relation: Partial<Relation>): void;
|
|
29
|
+
setNullable(column: Knex.ColumnBuilder, field: RawField | Field, existing: Column | null): void;
|
|
27
30
|
processFieldType(field: Field): Type;
|
|
28
31
|
constraintName(existingName: string): string;
|
|
29
32
|
applyLimit(rootQuery: Knex.QueryBuilder, limit: number): void;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getDefaultIndexName } from '../../../utils/get-default-index-name.js';
|
|
1
2
|
import { getDatabaseClient } from '../../index.js';
|
|
2
3
|
import { DatabaseHelper } from '../types.js';
|
|
3
4
|
export class SchemaHelper extends DatabaseHelper {
|
|
@@ -14,6 +15,9 @@ export class SchemaHelper extends DatabaseHelper {
|
|
|
14
15
|
}
|
|
15
16
|
});
|
|
16
17
|
}
|
|
18
|
+
generateIndexName(type, collection, fields) {
|
|
19
|
+
return getDefaultIndexName(type, collection, fields);
|
|
20
|
+
}
|
|
17
21
|
async changeToType(table, column, type, options = {}) {
|
|
18
22
|
await this.knex.schema.alterTable(table, (builder) => {
|
|
19
23
|
const b = type === 'string' ? builder.string(column, options.length) : builder[type](column);
|
|
@@ -62,6 +66,15 @@ export class SchemaHelper extends DatabaseHelper {
|
|
|
62
66
|
preRelationChange(_relation) {
|
|
63
67
|
return;
|
|
64
68
|
}
|
|
69
|
+
setNullable(column, field, existing) {
|
|
70
|
+
const isNullable = field.schema?.is_nullable ?? existing?.is_nullable ?? true;
|
|
71
|
+
if (isNullable) {
|
|
72
|
+
column.nullable();
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
column.notNullable();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
65
78
|
processFieldType(field) {
|
|
66
79
|
return field.type;
|
|
67
80
|
}
|
|
@@ -7,17 +7,27 @@ export function mergeWithParentItems(schema, nestedItem, parentItem, nestedNode,
|
|
|
7
7
|
const parentItems = clone(toArray(parentItem));
|
|
8
8
|
if (nestedNode.type === 'm2o') {
|
|
9
9
|
const parentsByForeignKey = new Map();
|
|
10
|
-
|
|
10
|
+
// default all nested nodes to null
|
|
11
|
+
for (const parentItem of parentItems) {
|
|
11
12
|
const relationKey = parentItem[nestedNode.relation.field];
|
|
12
13
|
if (!parentsByForeignKey.has(relationKey)) {
|
|
13
14
|
parentsByForeignKey.set(relationKey, []);
|
|
14
15
|
}
|
|
15
16
|
parentItem[nestedNode.fieldKey] = null;
|
|
16
17
|
parentsByForeignKey.get(relationKey).push(parentItem);
|
|
17
|
-
}
|
|
18
|
-
const
|
|
18
|
+
}
|
|
19
|
+
const nestedPrimaryKeyField = schema.collections[nestedNode.relation.related_collection].primary;
|
|
20
|
+
// populate nested items where applicable
|
|
19
21
|
for (const nestedItem of nestedItems) {
|
|
20
|
-
const nestedPK = nestedItem[
|
|
22
|
+
const nestedPK = nestedItem[nestedPrimaryKeyField];
|
|
23
|
+
// user has no access to the nestedItem PK field
|
|
24
|
+
if (nestedPK === null) {
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
// Existing M2O record (i.e. valid nested PK) but not linked to this (or any) parent item
|
|
28
|
+
if (!parentsByForeignKey.has(nestedPK)) {
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
21
31
|
for (const parentItem of parentsByForeignKey.get(nestedPK)) {
|
|
22
32
|
parentItem[nestedNode.fieldKey] = nestedItem;
|
|
23
33
|
}
|
|
@@ -29,30 +39,44 @@ export function mergeWithParentItems(schema, nestedItem, parentItem, nestedNode,
|
|
|
29
39
|
const parentRelationField = nestedNode.fieldKey;
|
|
30
40
|
const nestedParentKeyField = nestedNode.relation.field;
|
|
31
41
|
const parentsByPrimaryKey = new Map();
|
|
32
|
-
|
|
33
|
-
if (!parentItem[parentRelationField])
|
|
42
|
+
for (const parentItem of parentItems) {
|
|
43
|
+
if (!parentItem[parentRelationField]) {
|
|
34
44
|
parentItem[parentRelationField] = [];
|
|
35
|
-
|
|
45
|
+
}
|
|
46
|
+
let parentPrimaryKey = parentItem[parentPrimaryKeyField];
|
|
47
|
+
// null if the user has no access to parent PK
|
|
48
|
+
if (parentPrimaryKey === null) {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
// ensure key access is type agnostic
|
|
53
|
+
parentPrimaryKey = parentPrimaryKey.toString();
|
|
54
|
+
}
|
|
36
55
|
if (parentsByPrimaryKey.has(parentPrimaryKey)) {
|
|
37
56
|
throw new Error(`Duplicate parent primary key '${parentPrimaryKey}' of '${parentCollectionName}' when merging o2m nested items`);
|
|
38
57
|
}
|
|
39
58
|
parentsByPrimaryKey.set(parentPrimaryKey, parentItem);
|
|
40
|
-
}
|
|
59
|
+
}
|
|
41
60
|
const toAddToAllParents = [];
|
|
42
|
-
|
|
43
|
-
if (nestedItem === null)
|
|
44
|
-
|
|
61
|
+
for (const nestedItem of nestedItems) {
|
|
62
|
+
if (nestedItem === null) {
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
45
65
|
if (Array.isArray(nestedItem[nestedParentKeyField])) {
|
|
46
66
|
toAddToAllParents.push(nestedItem); // TODO explain this odd case
|
|
47
|
-
|
|
67
|
+
continue; // Avoids adding the nestedItem twice
|
|
48
68
|
}
|
|
49
69
|
const parentPrimaryKey = nestedItem[nestedParentKeyField]?.[parentPrimaryKeyField] ?? nestedItem[nestedParentKeyField];
|
|
50
|
-
|
|
70
|
+
if (parentPrimaryKey === null) {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
const parentItem = parentsByPrimaryKey.get(parentPrimaryKey.toString());
|
|
74
|
+
// null if the user has no access to parent PK
|
|
51
75
|
if (!parentItem) {
|
|
52
|
-
|
|
76
|
+
continue;
|
|
53
77
|
}
|
|
54
78
|
parentItem[parentRelationField].push(nestedItem);
|
|
55
|
-
}
|
|
79
|
+
}
|
|
56
80
|
for (const [index, parentItem] of parentItems.entries()) {
|
|
57
81
|
if (fieldAllowed === false || (isArray(fieldAllowed) && !fieldAllowed[index])) {
|
|
58
82
|
parentItem[nestedNode.fieldKey] = null;
|
|
@@ -17,7 +17,8 @@ export const respond = asyncHandler(async (req, res) => {
|
|
|
17
17
|
if (env['CACHE_VALUE_MAX_SIZE'] !== false) {
|
|
18
18
|
const valueSize = res.locals['payload'] ? stringByteSize(JSON.stringify(res.locals['payload'])) : 0;
|
|
19
19
|
const maxSize = parseBytesConfiguration(env['CACHE_VALUE_MAX_SIZE']);
|
|
20
|
-
|
|
20
|
+
if (maxSize !== null)
|
|
21
|
+
exceedsMaxSize = valueSize > maxSize;
|
|
21
22
|
}
|
|
22
23
|
if ((req.method.toLowerCase() === 'get' || req.originalUrl?.startsWith('/graphql')) &&
|
|
23
24
|
req.originalUrl?.startsWith('/auth') === false &&
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
export type Options = {
|
|
2
|
-
body?: string;
|
|
3
|
-
template?: string;
|
|
4
|
-
data?: Record<string, any>;
|
|
5
2
|
to: string;
|
|
6
3
|
type: 'wysiwyg' | 'markdown' | 'template';
|
|
7
4
|
subject: string;
|
|
5
|
+
body?: string;
|
|
6
|
+
template?: string;
|
|
7
|
+
data?: Record<string, any>;
|
|
8
|
+
cc?: string;
|
|
9
|
+
bcc?: string;
|
|
10
|
+
replyTo?: string;
|
|
8
11
|
};
|
|
9
12
|
declare const _default: import("@directus/extensions").OperationApiConfig<Options>;
|
|
10
13
|
export default _default;
|
|
@@ -5,9 +5,9 @@ import { useLogger } from '../../logger/index.js';
|
|
|
5
5
|
const logger = useLogger();
|
|
6
6
|
export default defineOperationApi({
|
|
7
7
|
id: 'mail',
|
|
8
|
-
handler: async ({ body, template, data, to, type, subject }, { accountability, database, getSchema }) => {
|
|
8
|
+
handler: async ({ body, template, data, to, type, subject, cc, bcc, replyTo }, { accountability, database, getSchema }) => {
|
|
9
9
|
const mailService = new MailService({ schema: await getSchema({ database }), accountability, knex: database });
|
|
10
|
-
const mailObject = { to, subject };
|
|
10
|
+
const mailObject = { to, subject, cc, bcc, replyTo };
|
|
11
11
|
const safeBody = typeof body !== 'string' ? JSON.stringify(body) : body;
|
|
12
12
|
if (type === 'template') {
|
|
13
13
|
mailObject.template = {
|
|
@@ -101,7 +101,7 @@ export class CollectionsService {
|
|
|
101
101
|
await trx.schema.createTable(payload.collection, (table) => {
|
|
102
102
|
for (const field of payload.fields) {
|
|
103
103
|
if (field.type && ALIAS_TYPES.includes(field.type) === false) {
|
|
104
|
-
fieldsService.addColumnToTable(table, field);
|
|
104
|
+
fieldsService.addColumnToTable(table, payload.collection, field);
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
107
|
});
|
|
@@ -30,5 +30,5 @@ export declare class FieldsService {
|
|
|
30
30
|
updateField(collection: string, field: RawField, opts?: MutationOptions): Promise<string>;
|
|
31
31
|
updateFields(collection: string, fields: RawField[], opts?: MutationOptions): Promise<string[]>;
|
|
32
32
|
deleteField(collection: string, field: string, opts?: MutationOptions): Promise<void>;
|
|
33
|
-
addColumnToTable(table: Knex.CreateTableBuilder, field: RawField | Field, existing?: Column | null): void;
|
|
33
|
+
addColumnToTable(table: Knex.CreateTableBuilder, collection: string, field: RawField | Field, existing?: Column | null): void;
|
|
34
34
|
}
|
package/dist/services/fields.js
CHANGED
|
@@ -289,11 +289,11 @@ export class FieldsService {
|
|
|
289
289
|
: field;
|
|
290
290
|
if (hookAdjustedField.type && ALIAS_TYPES.includes(hookAdjustedField.type) === false) {
|
|
291
291
|
if (table) {
|
|
292
|
-
this.addColumnToTable(table, hookAdjustedField);
|
|
292
|
+
this.addColumnToTable(table, collection, hookAdjustedField);
|
|
293
293
|
}
|
|
294
294
|
else {
|
|
295
295
|
await trx.schema.alterTable(collection, (table) => {
|
|
296
|
-
this.addColumnToTable(table, hookAdjustedField);
|
|
296
|
+
this.addColumnToTable(table, collection, hookAdjustedField);
|
|
297
297
|
});
|
|
298
298
|
}
|
|
299
299
|
}
|
|
@@ -397,7 +397,7 @@ export class FieldsService {
|
|
|
397
397
|
await trx.schema.alterTable(collection, async (table) => {
|
|
398
398
|
if (!hookAdjustedField.schema)
|
|
399
399
|
return;
|
|
400
|
-
this.addColumnToTable(table, field, existingColumn);
|
|
400
|
+
this.addColumnToTable(table, collection, field, existingColumn);
|
|
401
401
|
});
|
|
402
402
|
});
|
|
403
403
|
}
|
|
@@ -583,7 +583,17 @@ export class FieldsService {
|
|
|
583
583
|
.update({ group: null })
|
|
584
584
|
.where({ group: metaRow.field, collection: metaRow.collection });
|
|
585
585
|
}
|
|
586
|
-
|
|
586
|
+
const itemsService = new ItemsService('directus_fields', {
|
|
587
|
+
knex: trx,
|
|
588
|
+
accountability: this.accountability,
|
|
589
|
+
schema: this.schema,
|
|
590
|
+
});
|
|
591
|
+
await itemsService.deleteByQuery({
|
|
592
|
+
filter: {
|
|
593
|
+
collection: { _eq: collection },
|
|
594
|
+
field: { _eq: field },
|
|
595
|
+
},
|
|
596
|
+
}, { emitEvents: false });
|
|
587
597
|
});
|
|
588
598
|
const actionEvent = {
|
|
589
599
|
event: 'fields.delete',
|
|
@@ -623,7 +633,7 @@ export class FieldsService {
|
|
|
623
633
|
}
|
|
624
634
|
}
|
|
625
635
|
}
|
|
626
|
-
addColumnToTable(table, field, existing = null) {
|
|
636
|
+
addColumnToTable(table, collection, field, existing = null) {
|
|
627
637
|
let column;
|
|
628
638
|
// Don't attempt to add a DB column for alias / corrupt fields
|
|
629
639
|
if (field.type === 'alias' || field.type === 'unknown')
|
|
@@ -664,7 +674,17 @@ export class FieldsService {
|
|
|
664
674
|
else {
|
|
665
675
|
throw new InvalidPayloadError({ reason: `Illegal type passed: "${field.type}"` });
|
|
666
676
|
}
|
|
667
|
-
|
|
677
|
+
/**
|
|
678
|
+
* The column nullability must be set on every alter or it will be dropped
|
|
679
|
+
* This is due to column.alter() not being incremental per https://knexjs.org/guide/schema-builder.html#alter
|
|
680
|
+
*/
|
|
681
|
+
this.helpers.schema.setNullable(column, field, existing);
|
|
682
|
+
/**
|
|
683
|
+
* The default value must be set on every alter or it will be dropped
|
|
684
|
+
* This is due to column.alter() not being incremental per https://knexjs.org/guide/schema-builder.html#alter
|
|
685
|
+
*/
|
|
686
|
+
const defaultValue = field.schema?.default_value !== undefined ? field.schema?.default_value : existing?.default_value;
|
|
687
|
+
if (defaultValue !== undefined) {
|
|
668
688
|
const newDefaultValueIsString = typeof defaultValue === 'string';
|
|
669
689
|
const newDefaultIsNowFunction = newDefaultValueIsString && defaultValue.toLowerCase() === 'now()';
|
|
670
690
|
const newDefaultIsCurrentTimestamp = newDefaultValueIsString && defaultValue === 'CURRENT_TIMESTAMP';
|
|
@@ -684,34 +704,6 @@ export class FieldsService {
|
|
|
684
704
|
else {
|
|
685
705
|
column.defaultTo(defaultValue);
|
|
686
706
|
}
|
|
687
|
-
};
|
|
688
|
-
// for a new item, set the default value and nullable as provided without any further considerations
|
|
689
|
-
if (!existing) {
|
|
690
|
-
if (field.schema?.default_value !== undefined) {
|
|
691
|
-
setDefaultValue(field.schema.default_value);
|
|
692
|
-
}
|
|
693
|
-
if (field.schema?.is_nullable || field.schema?.is_nullable === undefined) {
|
|
694
|
-
column.nullable();
|
|
695
|
-
}
|
|
696
|
-
else {
|
|
697
|
-
column.notNullable();
|
|
698
|
-
}
|
|
699
|
-
}
|
|
700
|
-
else {
|
|
701
|
-
// for an existing item: if nullable option changed, we have to provide the default values as well and actually vice versa
|
|
702
|
-
// see https://knexjs.org/guide/schema-builder.html#alter
|
|
703
|
-
// To overwrite a nullable option with the same value this is not possible for Oracle though, hence the DB helper
|
|
704
|
-
if (field.schema?.default_value !== undefined || field.schema?.is_nullable !== undefined) {
|
|
705
|
-
this.helpers.nullableUpdate.updateNullableValue(column, field, existing);
|
|
706
|
-
let defaultValue = null;
|
|
707
|
-
if (field.schema?.default_value !== undefined) {
|
|
708
|
-
defaultValue = field.schema.default_value;
|
|
709
|
-
}
|
|
710
|
-
else if (existing.default_value !== undefined) {
|
|
711
|
-
defaultValue = existing.default_value;
|
|
712
|
-
}
|
|
713
|
-
setDefaultValue(defaultValue);
|
|
714
|
-
}
|
|
715
707
|
}
|
|
716
708
|
if (field.schema?.is_primary_key) {
|
|
717
709
|
column.primary().notNullable();
|
|
@@ -720,19 +712,23 @@ export class FieldsService {
|
|
|
720
712
|
// primary key will already have unique/index constraints
|
|
721
713
|
if (field.schema?.is_unique === true) {
|
|
722
714
|
if (!existing || existing.is_unique === false) {
|
|
723
|
-
column.unique();
|
|
715
|
+
column.unique({ indexName: this.helpers.schema.generateIndexName('unique', collection, field.field) });
|
|
724
716
|
}
|
|
725
717
|
}
|
|
726
718
|
else if (field.schema?.is_unique === false) {
|
|
727
|
-
if (existing
|
|
728
|
-
table.dropUnique([field.field]);
|
|
719
|
+
if (existing?.is_unique === true) {
|
|
720
|
+
table.dropUnique([field.field], this.helpers.schema.generateIndexName('unique', collection, field.field));
|
|
729
721
|
}
|
|
730
722
|
}
|
|
731
|
-
if (field.schema?.is_indexed === true
|
|
732
|
-
|
|
723
|
+
if (field.schema?.is_indexed === true) {
|
|
724
|
+
if (!existing || existing.is_indexed === false) {
|
|
725
|
+
column.index(this.helpers.schema.generateIndexName('index', collection, field.field));
|
|
726
|
+
}
|
|
733
727
|
}
|
|
734
|
-
else if (field.schema?.is_indexed === false
|
|
735
|
-
|
|
728
|
+
else if (field.schema?.is_indexed === false) {
|
|
729
|
+
if (existing?.is_indexed === true) {
|
|
730
|
+
table.dropIndex([field.field], this.helpers.schema.generateIndexName('index', collection, field.field));
|
|
731
|
+
}
|
|
736
732
|
}
|
|
737
733
|
}
|
|
738
734
|
if (existing) {
|
package/dist/services/files.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useEnv } from '@directus/env';
|
|
2
|
-
import { ContentTooLargeError,
|
|
2
|
+
import { ContentTooLargeError, InvalidPayloadError, ServiceUnavailableError } from '@directus/errors';
|
|
3
3
|
import formatTitle from '@directus/format-title';
|
|
4
4
|
import { toArray } from '@directus/utils';
|
|
5
5
|
import encodeURL from 'encodeurl';
|
|
@@ -211,10 +211,11 @@ export class FilesService extends ItemsService {
|
|
|
211
211
|
*/
|
|
212
212
|
async deleteMany(keys) {
|
|
213
213
|
const storage = await getStorage();
|
|
214
|
-
const
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
}
|
|
214
|
+
const sudoFilesItemsService = new FilesService({
|
|
215
|
+
knex: this.knex,
|
|
216
|
+
schema: this.schema,
|
|
217
|
+
});
|
|
218
|
+
const files = await sudoFilesItemsService.readMany(keys, { fields: ['id', 'storage', 'filename_disk'], limit: -1 });
|
|
218
219
|
await super.deleteMany(keys);
|
|
219
220
|
for (const file of files) {
|
|
220
221
|
const disk = storage.location(file['storage']);
|
|
@@ -5,8 +5,8 @@ import { DataStore, Upload } from '@tus/utils';
|
|
|
5
5
|
export type TusDataStoreConfig = {
|
|
6
6
|
constants: {
|
|
7
7
|
ENABLED: boolean;
|
|
8
|
-
CHUNK_SIZE: number;
|
|
9
|
-
MAX_SIZE: number;
|
|
8
|
+
CHUNK_SIZE: number | null;
|
|
9
|
+
MAX_SIZE: number | null;
|
|
10
10
|
EXPIRATION_TIME: number;
|
|
11
11
|
SCHEDULE: string;
|
|
12
12
|
};
|
|
@@ -17,8 +17,8 @@ export type TusDataStoreConfig = {
|
|
|
17
17
|
accountability: Accountability | undefined;
|
|
18
18
|
};
|
|
19
19
|
export declare class TusDataStore extends DataStore {
|
|
20
|
-
protected chunkSize: number;
|
|
21
|
-
protected maxSize: number;
|
|
20
|
+
protected chunkSize: number | undefined;
|
|
21
|
+
protected maxSize: number | undefined;
|
|
22
22
|
protected expirationTime: number;
|
|
23
23
|
protected location: string;
|
|
24
24
|
protected storageDriver: TusDriver;
|
|
@@ -17,8 +17,10 @@ export class TusDataStore extends DataStore {
|
|
|
17
17
|
accountability;
|
|
18
18
|
constructor(config) {
|
|
19
19
|
super();
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
if (config.constants.CHUNK_SIZE !== null)
|
|
21
|
+
this.chunkSize = config.constants.CHUNK_SIZE;
|
|
22
|
+
if (config.constants.MAX_SIZE !== null)
|
|
23
|
+
this.maxSize = config.constants.MAX_SIZE;
|
|
22
24
|
this.expirationTime = config.constants.EXPIRATION_TIME;
|
|
23
25
|
this.location = config.location;
|
|
24
26
|
this.storageDriver = config.driver;
|
|
@@ -37,7 +37,7 @@ export async function createTusServer(context) {
|
|
|
37
37
|
path: '/files/tus',
|
|
38
38
|
datastore: store,
|
|
39
39
|
locker: getTusLocker(),
|
|
40
|
-
maxSize: RESUMABLE_UPLOADS.MAX_SIZE,
|
|
40
|
+
...(RESUMABLE_UPLOADS.MAX_SIZE !== null && { maxSize: RESUMABLE_UPLOADS.MAX_SIZE }),
|
|
41
41
|
async onUploadFinish(req, res, upload) {
|
|
42
42
|
const service = new ItemsService('directus_files', {
|
|
43
43
|
schema: req.schema,
|
|
@@ -21,5 +21,5 @@ type GetColumnOptions = OriginalCollectionName | (FunctionColumnOptions & Origin
|
|
|
21
21
|
* @param options Optional parameters
|
|
22
22
|
* @returns Knex raw instance
|
|
23
23
|
*/
|
|
24
|
-
export declare function getColumn(knex: Knex, table: string, column: string, alias:
|
|
24
|
+
export declare function getColumn(knex: Knex, table: string, column: string, alias: string | false | undefined, schema: SchemaOverview, options?: GetColumnOptions): Knex.Raw;
|
|
25
25
|
export {};
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
+
export type GetDefaultIndexNameOptions = {
|
|
2
|
+
maxLength?: number;
|
|
3
|
+
};
|
|
1
4
|
/**
|
|
2
5
|
* Generate an index name for a given collection + fields combination.
|
|
3
6
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
7
|
+
* Based on the default index name generation of knex, with the caveat that it limits the index to options.maxLength
|
|
8
|
+
* which defaults to 60 characters.
|
|
6
9
|
*
|
|
7
10
|
* @see
|
|
8
11
|
* https://github.com/knex/knex/blob/fff6eb15d7088d4198650a2c6e673dedaf3b8f36/lib/schema/tablecompiler.js#L282-L297
|
|
9
12
|
*/
|
|
10
|
-
export declare function getDefaultIndexName(type: 'unique' | 'foreign' | 'index', collection: string, fields: string | string[]): string;
|
|
13
|
+
export declare function getDefaultIndexName(type: 'unique' | 'foreign' | 'index', collection: string, fields: string | string[], options?: GetDefaultIndexNameOptions): string;
|
|
@@ -2,20 +2,21 @@ import { getSimpleHash } from '@directus/utils';
|
|
|
2
2
|
/**
|
|
3
3
|
* Generate an index name for a given collection + fields combination.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* Based on the default index name generation of knex, with the caveat that it limits the index to options.maxLength
|
|
6
|
+
* which defaults to 60 characters.
|
|
7
7
|
*
|
|
8
8
|
* @see
|
|
9
9
|
* https://github.com/knex/knex/blob/fff6eb15d7088d4198650a2c6e673dedaf3b8f36/lib/schema/tablecompiler.js#L282-L297
|
|
10
10
|
*/
|
|
11
|
-
export function getDefaultIndexName(type, collection, fields) {
|
|
11
|
+
export function getDefaultIndexName(type, collection, fields, options) {
|
|
12
|
+
const maxLength = options?.maxLength ?? 60;
|
|
12
13
|
if (!Array.isArray(fields))
|
|
13
14
|
fields = fields ? [fields] : [];
|
|
14
15
|
const table = collection.replace(/\.|-/g, '_');
|
|
15
16
|
const indexName = (table + '_' + fields.join('_') + '_' + type).toLowerCase();
|
|
16
|
-
if (indexName.length <=
|
|
17
|
+
if (indexName.length <= maxLength)
|
|
17
18
|
return indexName;
|
|
18
19
|
const suffix = `__${getSimpleHash(indexName)}_${type}`;
|
|
19
|
-
const prefix = indexName.substring(0,
|
|
20
|
+
const prefix = indexName.substring(0, maxLength - suffix.length);
|
|
20
21
|
return `${prefix}${suffix}`;
|
|
21
22
|
}
|
package/dist/utils/get-schema.js
CHANGED
|
@@ -44,10 +44,10 @@ export async function getSchema(options, attempt = 0) {
|
|
|
44
44
|
const subscription = new Promise((resolve, reject) => {
|
|
45
45
|
bus.subscribe(messageKey, busListener).catch(reject);
|
|
46
46
|
function busListener(options) {
|
|
47
|
+
cleanup();
|
|
47
48
|
if (options.schema === null) {
|
|
48
49
|
return reject();
|
|
49
50
|
}
|
|
50
|
-
cleanup();
|
|
51
51
|
setLocalSchemaCache(options.schema).catch(reject);
|
|
52
52
|
resolve(options.schema);
|
|
53
53
|
}
|
|
@@ -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, "table" | "name" | "
|
|
19
|
+
export declare function sanitizeColumn(column: Column): Pick<Column, "table" | "name" | "data_type" | "default_value" | "max_length" | "numeric_precision" | "numeric_scale" | "is_nullable" | "is_unique" | "is_indexed" | "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
|
*
|
package/license
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Licensor: Monospace, Inc.
|
|
2
2
|
|
|
3
3
|
Licensed Work: Directus
|
|
4
|
-
The Licensed Work is Copyright ©
|
|
4
|
+
The Licensed Work is Copyright © 2025 Monospace, Inc.
|
|
5
5
|
|
|
6
6
|
Additional Use Grant: You may use the Licensed Work in production as long as
|
|
7
7
|
your Total Finances do not exceed US $5,000,000 for the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@directus/api",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "24.0.1",
|
|
4
4
|
"description": "Directus is a real-time API and App dashboard for managing SQL database content",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"directus",
|
|
@@ -59,127 +59,126 @@
|
|
|
59
59
|
],
|
|
60
60
|
"dependencies": {
|
|
61
61
|
"@authenio/samlify-node-xmllint": "2.0.0",
|
|
62
|
-
"@aws-sdk/client-ses": "3.
|
|
62
|
+
"@aws-sdk/client-ses": "3.723.0",
|
|
63
63
|
"@godaddy/terminus": "4.12.1",
|
|
64
64
|
"@rollup/plugin-alias": "5.1.1",
|
|
65
|
-
"@rollup/plugin-node-resolve": "15.3.
|
|
65
|
+
"@rollup/plugin-node-resolve": "15.3.1",
|
|
66
66
|
"@rollup/plugin-virtual": "3.0.2",
|
|
67
|
-
"@tus/
|
|
68
|
-
"@tus/
|
|
69
|
-
"@tus/utils": "0.4.0",
|
|
70
|
-
"@types/cookie": "0.6.0",
|
|
67
|
+
"@tus/server": "1.10.0",
|
|
68
|
+
"@tus/utils": "0.5.0",
|
|
71
69
|
"argon2": "0.41.1",
|
|
72
70
|
"async": "3.2.6",
|
|
73
|
-
"axios": "1.7.
|
|
71
|
+
"axios": "1.7.9",
|
|
74
72
|
"busboy": "1.6.0",
|
|
75
73
|
"bytes": "3.1.2",
|
|
76
74
|
"camelcase": "8.0.0",
|
|
77
|
-
"chalk": "5.
|
|
75
|
+
"chalk": "5.4.1",
|
|
78
76
|
"chokidar": "3.6.0",
|
|
79
77
|
"commander": "12.1.0",
|
|
80
78
|
"content-disposition": "0.5.4",
|
|
81
|
-
"cookie": "0.
|
|
79
|
+
"cookie": "1.0.2",
|
|
82
80
|
"cookie-parser": "1.4.7",
|
|
83
81
|
"cors": "2.8.5",
|
|
84
82
|
"cron-parser": "4.9.0",
|
|
85
83
|
"date-fns": "4.1.0",
|
|
86
84
|
"deep-diff": "1.0.2",
|
|
87
85
|
"destroy": "1.2.0",
|
|
88
|
-
"dotenv": "16.4.
|
|
86
|
+
"dotenv": "16.4.7",
|
|
89
87
|
"encodeurl": "2.0.0",
|
|
90
88
|
"eventemitter2": "6.4.9",
|
|
91
89
|
"execa": "8.0.1",
|
|
92
90
|
"exif-reader": "2.0.1",
|
|
93
|
-
"express": "4.
|
|
91
|
+
"express": "4.21.2",
|
|
94
92
|
"flat": "6.0.1",
|
|
95
93
|
"fs-extra": "11.2.0",
|
|
96
94
|
"glob-to-regexp": "0.4.1",
|
|
97
|
-
"graphql": "16.
|
|
95
|
+
"graphql": "16.10.0",
|
|
98
96
|
"graphql-compose": "9.0.11",
|
|
99
|
-
"graphql-ws": "5.16.
|
|
97
|
+
"graphql-ws": "5.16.1",
|
|
100
98
|
"helmet": "8.0.0",
|
|
101
99
|
"icc": "3.0.0",
|
|
102
|
-
"inquirer": "12.
|
|
103
|
-
"ioredis": "5.4.
|
|
100
|
+
"inquirer": "12.3.0",
|
|
101
|
+
"ioredis": "5.4.2",
|
|
104
102
|
"ip-matching": "2.1.2",
|
|
105
|
-
"isolated-vm": "5.0.
|
|
103
|
+
"isolated-vm": "5.0.3",
|
|
106
104
|
"joi": "17.13.3",
|
|
107
105
|
"js-yaml": "4.1.0",
|
|
108
106
|
"js2xmlparser": "5.0.0",
|
|
109
107
|
"json2csv": "5.0.7",
|
|
110
108
|
"jsonwebtoken": "9.0.2",
|
|
111
|
-
"keyv": "5.
|
|
109
|
+
"keyv": "5.2.3",
|
|
112
110
|
"knex": "3.1.0",
|
|
113
111
|
"ldapjs": "2.3.3",
|
|
114
|
-
"liquidjs": "10.
|
|
112
|
+
"liquidjs": "10.20.1",
|
|
115
113
|
"lodash-es": "4.17.21",
|
|
116
|
-
"marked": "
|
|
114
|
+
"marked": "15.0.6",
|
|
117
115
|
"micromustache": "8.0.3",
|
|
118
116
|
"mime-types": "2.1.35",
|
|
119
|
-
"minimatch": "
|
|
117
|
+
"minimatch": "10.0.1",
|
|
120
118
|
"mnemonist": "0.39.8",
|
|
121
119
|
"ms": "2.1.3",
|
|
122
|
-
"nanoid": "5.0.
|
|
120
|
+
"nanoid": "5.0.9",
|
|
123
121
|
"node-machine-id": "1.1.12",
|
|
124
122
|
"node-schedule": "2.1.1",
|
|
125
|
-
"nodemailer": "6.9.
|
|
123
|
+
"nodemailer": "6.9.16",
|
|
126
124
|
"object-hash": "3.0.0",
|
|
127
125
|
"openapi3-ts": "4.4.0",
|
|
128
|
-
"openid-client": "5.7.
|
|
129
|
-
"ora": "8.1.
|
|
126
|
+
"openid-client": "5.7.1",
|
|
127
|
+
"ora": "8.1.1",
|
|
130
128
|
"otplib": "12.0.1",
|
|
131
|
-
"p-limit": "6.
|
|
129
|
+
"p-limit": "6.2.0",
|
|
132
130
|
"p-queue": "8.0.1",
|
|
133
131
|
"papaparse": "5.4.1",
|
|
134
|
-
"pino": "9.
|
|
132
|
+
"pino": "9.6.0",
|
|
135
133
|
"pino-http": "10.3.0",
|
|
136
134
|
"pino-http-print": "3.1.0",
|
|
137
|
-
"pino-pretty": "
|
|
138
|
-
"qs": "6.13.
|
|
139
|
-
"rate-limiter-flexible": "5.0.
|
|
140
|
-
"rollup": "4.
|
|
135
|
+
"pino-pretty": "13.0.0",
|
|
136
|
+
"qs": "6.13.1",
|
|
137
|
+
"rate-limiter-flexible": "5.0.4",
|
|
138
|
+
"rollup": "4.30.1",
|
|
141
139
|
"samlify": "2.8.10",
|
|
142
|
-
"sanitize-html": "2.
|
|
140
|
+
"sanitize-html": "2.14.0",
|
|
143
141
|
"sharp": "0.33.5",
|
|
144
142
|
"snappy": "7.2.2",
|
|
145
|
-
"stream-json": "1.
|
|
143
|
+
"stream-json": "1.9.1",
|
|
146
144
|
"tar": "7.4.3",
|
|
147
|
-
"tsx": "4.19.
|
|
145
|
+
"tsx": "4.19.2",
|
|
148
146
|
"wellknown": "0.5.0",
|
|
149
147
|
"ws": "8.18.0",
|
|
150
|
-
"zod": "3.
|
|
148
|
+
"zod": "3.24.1",
|
|
151
149
|
"zod-validation-error": "3.4.0",
|
|
152
|
-
"@directus/app": "13.
|
|
153
|
-
"@directus/constants": "
|
|
154
|
-
"@directus/env": "
|
|
155
|
-
"@directus/errors": "
|
|
156
|
-
"@directus/extensions": "
|
|
157
|
-
"@directus/extensions-registry": "
|
|
158
|
-
"@directus/extensions-sdk": "
|
|
159
|
-
"@directus/format-title": "
|
|
160
|
-
"@directus/memory": "
|
|
161
|
-
"@directus/
|
|
162
|
-
"@directus/schema": "12.1.1",
|
|
150
|
+
"@directus/app": "13.6.0",
|
|
151
|
+
"@directus/constants": "13.0.0",
|
|
152
|
+
"@directus/env": "5.0.0",
|
|
153
|
+
"@directus/errors": "2.0.0",
|
|
154
|
+
"@directus/extensions": "3.0.1",
|
|
155
|
+
"@directus/extensions-registry": "3.0.1",
|
|
156
|
+
"@directus/extensions-sdk": "13.0.1",
|
|
157
|
+
"@directus/format-title": "12.0.0",
|
|
158
|
+
"@directus/memory": "3.0.0",
|
|
159
|
+
"@directus/schema": "13.0.0",
|
|
163
160
|
"@directus/specs": "11.1.0",
|
|
164
|
-
"@directus/storage-driver-azure": "
|
|
165
|
-
"@directus/
|
|
166
|
-
"@directus/storage
|
|
167
|
-
"@directus/storage-driver-
|
|
168
|
-
"@directus/storage-driver-local": "
|
|
169
|
-
"@directus/storage-driver-
|
|
170
|
-
"@directus/storage-driver-
|
|
171
|
-
"@directus/
|
|
172
|
-
"@directus/
|
|
173
|
-
"@directus/
|
|
174
|
-
"directus": "
|
|
161
|
+
"@directus/storage-driver-azure": "12.0.0",
|
|
162
|
+
"@directus/pressure": "3.0.0",
|
|
163
|
+
"@directus/storage": "12.0.0",
|
|
164
|
+
"@directus/storage-driver-cloudinary": "12.0.0",
|
|
165
|
+
"@directus/storage-driver-local": "12.0.0",
|
|
166
|
+
"@directus/storage-driver-gcs": "12.0.0",
|
|
167
|
+
"@directus/storage-driver-s3": "12.0.0",
|
|
168
|
+
"@directus/storage-driver-supabase": "3.0.0",
|
|
169
|
+
"@directus/utils": "13.0.0",
|
|
170
|
+
"@directus/system-data": "3.0.0",
|
|
171
|
+
"@directus/validation": "2.0.0",
|
|
172
|
+
"directus": "11.4.1"
|
|
175
173
|
},
|
|
176
174
|
"devDependencies": {
|
|
177
|
-
"@
|
|
175
|
+
"@directus/tsconfig": "3.0.0",
|
|
176
|
+
"@ngneat/falso": "7.3.0",
|
|
178
177
|
"@types/async": "3.2.24",
|
|
179
178
|
"@types/busboy": "1.5.4",
|
|
180
|
-
"@types/bytes": "3.1.
|
|
179
|
+
"@types/bytes": "3.1.5",
|
|
181
180
|
"@types/content-disposition": "0.5.8",
|
|
182
|
-
"@types/cookie-parser": "1.4.
|
|
181
|
+
"@types/cookie-parser": "1.4.8",
|
|
183
182
|
"@types/cors": "2.8.17",
|
|
184
183
|
"@types/deep-diff": "1.0.5",
|
|
185
184
|
"@types/destroy": "1.0.3",
|
|
@@ -196,38 +195,37 @@
|
|
|
196
195
|
"@types/lodash-es": "4.17.12",
|
|
197
196
|
"@types/mime-types": "2.1.4",
|
|
198
197
|
"@types/ms": "0.7.34",
|
|
199
|
-
"@types/node": "
|
|
198
|
+
"@types/node": "22.10.5",
|
|
200
199
|
"@types/node-schedule": "2.1.7",
|
|
201
|
-
"@types/nodemailer": "6.4.
|
|
200
|
+
"@types/nodemailer": "6.4.17",
|
|
202
201
|
"@types/object-hash": "3.0.6",
|
|
203
|
-
"@types/papaparse": "5.3.
|
|
204
|
-
"@types/qs": "6.9.
|
|
202
|
+
"@types/papaparse": "5.3.15",
|
|
203
|
+
"@types/qs": "6.9.17",
|
|
205
204
|
"@types/sanitize-html": "2.13.0",
|
|
206
|
-
"@types/stream-json": "1.7.
|
|
205
|
+
"@types/stream-json": "1.7.8",
|
|
207
206
|
"@types/wellknown": "0.5.8",
|
|
208
|
-
"@types/ws": "8.5.
|
|
209
|
-
"@vitest/coverage-v8": "2.1.
|
|
207
|
+
"@types/ws": "8.5.13",
|
|
208
|
+
"@vitest/coverage-v8": "2.1.8",
|
|
210
209
|
"copyfiles": "2.4.1",
|
|
211
210
|
"form-data": "4.0.1",
|
|
212
211
|
"get-port": "7.1.0",
|
|
213
212
|
"knex-mock-client": "3.0.2",
|
|
214
|
-
"typescript": "5.
|
|
215
|
-
"vitest": "2.1.
|
|
216
|
-
"@directus/random": "
|
|
217
|
-
"@directus/
|
|
218
|
-
"@directus/types": "12.2.2"
|
|
213
|
+
"typescript": "5.7.3",
|
|
214
|
+
"vitest": "2.1.8",
|
|
215
|
+
"@directus/random": "2.0.0",
|
|
216
|
+
"@directus/types": "13.0.0"
|
|
219
217
|
},
|
|
220
218
|
"optionalDependencies": {
|
|
221
219
|
"@keyv/redis": "3.0.1",
|
|
222
|
-
"mysql2": "3.
|
|
220
|
+
"mysql2": "3.12.0",
|
|
223
221
|
"nodemailer-mailgun-transport": "2.1.5",
|
|
224
|
-
"oracledb": "6.
|
|
225
|
-
"pg": "8.13.
|
|
222
|
+
"oracledb": "6.7.1",
|
|
223
|
+
"pg": "8.13.1",
|
|
226
224
|
"sqlite3": "5.1.7",
|
|
227
225
|
"tedious": "18.6.1"
|
|
228
226
|
},
|
|
229
227
|
"engines": {
|
|
230
|
-
"node": ">=
|
|
228
|
+
"node": ">=22"
|
|
231
229
|
},
|
|
232
230
|
"scripts": {
|
|
233
231
|
"build": "tsc --project tsconfig.prod.json && copyfiles \"src/**/*.{yaml,liquid}\" -u 1 dist",
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import type { Column } from '@directus/schema';
|
|
2
|
-
import type { Field, RawField } from '@directus/types';
|
|
3
|
-
import type { Knex } from 'knex';
|
|
4
|
-
import { NullableFieldUpdateHelper } from '../types.js';
|
|
5
|
-
/**
|
|
6
|
-
* Oracle throws an error when overwriting the nullable option with same value.
|
|
7
|
-
* Therefore we need to check if the nullable option has changed and only then apply it.
|
|
8
|
-
* The default value can be set regardless of the previous value.
|
|
9
|
-
*/
|
|
10
|
-
export declare class NullableFieldUpdateHelperOracle extends NullableFieldUpdateHelper {
|
|
11
|
-
updateNullableValue(column: Knex.ColumnBuilder, field: RawField | Field, existing: Column): void;
|
|
12
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { NullableFieldUpdateHelper } from '../types.js';
|
|
2
|
-
/**
|
|
3
|
-
* Oracle throws an error when overwriting the nullable option with same value.
|
|
4
|
-
* Therefore we need to check if the nullable option has changed and only then apply it.
|
|
5
|
-
* The default value can be set regardless of the previous value.
|
|
6
|
-
*/
|
|
7
|
-
export class NullableFieldUpdateHelperOracle extends NullableFieldUpdateHelper {
|
|
8
|
-
updateNullableValue(column, field, existing) {
|
|
9
|
-
if (field.schema?.is_nullable === false && existing.is_nullable === true) {
|
|
10
|
-
column.notNullable();
|
|
11
|
-
}
|
|
12
|
-
else if (field.schema?.is_nullable === true && existing.is_nullable === false) {
|
|
13
|
-
column.nullable();
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
export { NullableFieldUpdateHelperOracle as oracle } from './dialects/oracle.js';
|
|
2
|
-
export { NullableFieldUpdateHelperDefault as postgres } from './dialects/default.js';
|
|
3
|
-
export { NullableFieldUpdateHelperDefault as mysql } from './dialects/default.js';
|
|
4
|
-
export { NullableFieldUpdateHelperDefault as cockroachdb } from './dialects/default.js';
|
|
5
|
-
export { NullableFieldUpdateHelperDefault as redshift } from './dialects/default.js';
|
|
6
|
-
export { NullableFieldUpdateHelperDefault as sqlite } from './dialects/default.js';
|
|
7
|
-
export { NullableFieldUpdateHelperDefault as mssql } from './dialects/default.js';
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
export { NullableFieldUpdateHelperOracle as oracle } from './dialects/oracle.js';
|
|
2
|
-
export { NullableFieldUpdateHelperDefault as postgres } from './dialects/default.js';
|
|
3
|
-
export { NullableFieldUpdateHelperDefault as mysql } from './dialects/default.js';
|
|
4
|
-
export { NullableFieldUpdateHelperDefault as cockroachdb } from './dialects/default.js';
|
|
5
|
-
export { NullableFieldUpdateHelperDefault as redshift } from './dialects/default.js';
|
|
6
|
-
export { NullableFieldUpdateHelperDefault as sqlite } from './dialects/default.js';
|
|
7
|
-
export { NullableFieldUpdateHelperDefault as mssql } from './dialects/default.js';
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import type { Knex } from 'knex';
|
|
2
|
-
import { DatabaseHelper } from '../types.js';
|
|
3
|
-
import type { Column } from '@directus/schema';
|
|
4
|
-
import type { Field, RawField } from '@directus/types';
|
|
5
|
-
export declare class NullableFieldUpdateHelper extends DatabaseHelper {
|
|
6
|
-
updateNullableValue(column: Knex.ColumnBuilder, field: RawField | Field, existing: Column): void;
|
|
7
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { DatabaseHelper } from '../types.js';
|
|
2
|
-
export class NullableFieldUpdateHelper extends DatabaseHelper {
|
|
3
|
-
updateNullableValue(column, field, existing) {
|
|
4
|
-
const isNullable = field.schema?.is_nullable ?? existing?.is_nullable ?? true;
|
|
5
|
-
if (isNullable) {
|
|
6
|
-
column.nullable();
|
|
7
|
-
}
|
|
8
|
-
else {
|
|
9
|
-
column.notNullable();
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
}
|