@directus/api 23.3.1 → 24.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/auth/drivers/openid.js +4 -2
- package/dist/constants.d.ts +2 -2
- package/dist/controllers/files.js +1 -1
- 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 +1 -0
- package/dist/database/helpers/schema/dialects/oracle.js +13 -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 +1 -0
- package/dist/database/helpers/schema/types.js +4 -0
- package/dist/database/run-ast/utils/merge-with-parent-items.js +39 -15
- package/dist/middleware/respond.js +2 -1
- package/dist/services/collections.js +1 -1
- package/dist/services/fields.d.ts +1 -1
- package/dist/services/fields.js +21 -9
- 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/license +1 -1
- package/package.json +75 -77
|
@@ -175,9 +175,11 @@ export class OpenIDAuthDriver extends LocalAuthDriver {
|
|
|
175
175
|
// user that is about to be updated
|
|
176
176
|
let emitPayload = {
|
|
177
177
|
auth_data: userPayload.auth_data,
|
|
178
|
-
// Make sure a user's role gets updated if his openid group or role mapping changes
|
|
179
|
-
role: role,
|
|
180
178
|
};
|
|
179
|
+
// Make sure a user's role gets updated if their openid group or role mapping changes
|
|
180
|
+
if (this.config['roleMapping']) {
|
|
181
|
+
emitPayload['role'] = role;
|
|
182
|
+
}
|
|
181
183
|
if (syncUserInfo) {
|
|
182
184
|
emitPayload = {
|
|
183
185
|
...emitPayload,
|
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 = [];
|
|
@@ -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]);
|
|
@@ -4,6 +4,7 @@ import type { Knex } from 'knex';
|
|
|
4
4
|
import type { Options, SortRecord, Sql } from '../types.js';
|
|
5
5
|
import { SchemaHelper } from '../types.js';
|
|
6
6
|
export declare class SchemaHelperOracle extends SchemaHelper {
|
|
7
|
+
generateIndexName(type: 'unique' | 'foreign' | 'index', collection: string, fields: string | string[]): string;
|
|
7
8
|
changeToType(table: string, column: string, type: (typeof KNEX_TYPES)[number], options?: Options): Promise<void>;
|
|
8
9
|
castA2oPrimaryKey(): string;
|
|
9
10
|
preRelationChange(relation: Partial<Relation>): 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
|
}
|
|
@@ -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) {
|
|
@@ -19,6 +19,7 @@ export type SortRecord = {
|
|
|
19
19
|
export declare abstract class SchemaHelper extends DatabaseHelper {
|
|
20
20
|
isOneOfClients(clients: DatabaseClient[]): boolean;
|
|
21
21
|
changeNullable(table: string, column: string, nullable: boolean): Promise<void>;
|
|
22
|
+
generateIndexName(type: 'unique' | 'foreign' | 'index', collection: string, fields: string | string[]): string;
|
|
22
23
|
changeToType(table: string, column: string, type: (typeof KNEX_TYPES)[number], options?: Options): Promise<void>;
|
|
23
24
|
protected changeToTypeByCopy(table: string, column: string, type: (typeof KNEX_TYPES)[number], options: Options): Promise<void>;
|
|
24
25
|
preColumnChange(): Promise<boolean>;
|
|
@@ -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);
|
|
@@ -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 &&
|
|
@@ -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')
|
|
@@ -718,21 +728,23 @@ export class FieldsService {
|
|
|
718
728
|
}
|
|
719
729
|
else if (!existing?.is_primary_key) {
|
|
720
730
|
// primary key will already have unique/index constraints
|
|
731
|
+
const uniqueIndexName = this.helpers.schema.generateIndexName('unique', collection, field.field);
|
|
721
732
|
if (field.schema?.is_unique === true) {
|
|
722
733
|
if (!existing || existing.is_unique === false) {
|
|
723
|
-
column.unique();
|
|
734
|
+
column.unique({ indexName: uniqueIndexName });
|
|
724
735
|
}
|
|
725
736
|
}
|
|
726
737
|
else if (field.schema?.is_unique === false) {
|
|
727
738
|
if (existing && existing.is_unique === true) {
|
|
728
|
-
table.dropUnique([field.field]);
|
|
739
|
+
table.dropUnique([field.field], uniqueIndexName);
|
|
729
740
|
}
|
|
730
741
|
}
|
|
742
|
+
const indexName = this.helpers.schema.generateIndexName('index', collection, field.field);
|
|
731
743
|
if (field.schema?.is_indexed === true && !existing?.is_indexed) {
|
|
732
|
-
column.index();
|
|
744
|
+
column.index(indexName);
|
|
733
745
|
}
|
|
734
746
|
else if (field.schema?.is_indexed === false && existing?.is_indexed) {
|
|
735
|
-
table.dropIndex([field.field]);
|
|
747
|
+
table.dropIndex([field.field], indexName);
|
|
736
748
|
}
|
|
737
749
|
}
|
|
738
750
|
if (existing) {
|
|
@@ -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/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.0",
|
|
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
|
-
"papaparse": "5.
|
|
134
|
-
"pino": "9.
|
|
131
|
+
"papaparse": "5.5.0",
|
|
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/
|
|
153
|
-
"@directus/
|
|
154
|
-
"@directus/
|
|
155
|
-
"@directus/
|
|
156
|
-
"@directus/
|
|
157
|
-
"@directus/extensions-
|
|
158
|
-
"@directus/extensions-
|
|
159
|
-
"@directus/
|
|
160
|
-
"@directus/
|
|
161
|
-
"@directus/
|
|
150
|
+
"@directus/constants": "13.0.0",
|
|
151
|
+
"@directus/app": "13.5.0",
|
|
152
|
+
"@directus/env": "5.0.0",
|
|
153
|
+
"@directus/errors": "2.0.0",
|
|
154
|
+
"@directus/extensions": "3.0.0",
|
|
155
|
+
"@directus/extensions-sdk": "13.0.0",
|
|
156
|
+
"@directus/extensions-registry": "3.0.0",
|
|
157
|
+
"@directus/format-title": "12.0.0",
|
|
158
|
+
"@directus/memory": "3.0.0",
|
|
159
|
+
"@directus/pressure": "3.0.0",
|
|
160
|
+
"@directus/schema": "13.0.0",
|
|
162
161
|
"@directus/specs": "11.1.0",
|
|
163
|
-
"@directus/storage": "
|
|
164
|
-
"@directus/
|
|
165
|
-
"@directus/storage-driver-
|
|
166
|
-
"@directus/storage-driver-
|
|
167
|
-
"@directus/storage-driver-
|
|
168
|
-
"@directus/storage-driver-
|
|
169
|
-
"@directus/
|
|
170
|
-
"@directus/
|
|
171
|
-
"@directus/
|
|
172
|
-
"@directus/
|
|
173
|
-
"
|
|
174
|
-
"directus": "11.3.4"
|
|
162
|
+
"@directus/storage": "12.0.0",
|
|
163
|
+
"@directus/storage-driver-azure": "12.0.0",
|
|
164
|
+
"@directus/storage-driver-gcs": "12.0.0",
|
|
165
|
+
"@directus/storage-driver-local": "12.0.0",
|
|
166
|
+
"@directus/storage-driver-cloudinary": "12.0.0",
|
|
167
|
+
"@directus/storage-driver-supabase": "3.0.0",
|
|
168
|
+
"@directus/system-data": "3.0.0",
|
|
169
|
+
"@directus/utils": "13.0.0",
|
|
170
|
+
"@directus/storage-driver-s3": "12.0.0",
|
|
171
|
+
"@directus/validation": "2.0.0",
|
|
172
|
+
"directus": "11.4.0"
|
|
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",
|