directus 9.5.0 → 9.5.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/controllers/files.js +4 -1
- package/dist/database/index.js +33 -4
- package/dist/middleware/extract-token.js +1 -1
- package/dist/services/collections.js +23 -27
- package/dist/services/fields.js +21 -21
- package/dist/services/payload.js +7 -3
- package/dist/utils/get-permissions.js +1 -1
- package/dist/utils/md.js +1 -1
- package/package.json +18 -14
|
@@ -20,7 +20,7 @@ const router = express_1.default.Router();
|
|
|
20
20
|
router.use((0, use_collection_1.default)('directus_files'));
|
|
21
21
|
const multipartHandler = (0, async_handler_1.default)(async (req, res, next) => {
|
|
22
22
|
if (req.is('multipart/form-data') === false)
|
|
23
|
-
|
|
23
|
+
return next();
|
|
24
24
|
let headers;
|
|
25
25
|
if (req.headers['content-type']) {
|
|
26
26
|
headers = req.headers;
|
|
@@ -93,6 +93,9 @@ const multipartHandler = (0, async_handler_1.default)(async (req, res, next) =>
|
|
|
93
93
|
}
|
|
94
94
|
});
|
|
95
95
|
router.post('/', multipartHandler, (0, async_handler_1.default)(async (req, res, next) => {
|
|
96
|
+
if (req.is('multipart/form-data') === false) {
|
|
97
|
+
throw new exceptions_1.UnsupportedMediaTypeException(`Unsupported Content-Type header`);
|
|
98
|
+
}
|
|
96
99
|
const service = new services_1.FilesService({
|
|
97
100
|
accountability: req.accountability,
|
|
98
101
|
schema: req.schema,
|
package/dist/database/index.js
CHANGED
|
@@ -103,16 +103,14 @@ function getDatabase() {
|
|
|
103
103
|
// act the same
|
|
104
104
|
(0, lodash_1.merge)(knexConfig, { connection: { options: { useUTC: false } } });
|
|
105
105
|
}
|
|
106
|
-
if (env_1.default.DB_CLIENT === 'mysql' && !env_1.default.DB_CHARSET) {
|
|
107
|
-
logger_1.default.warn(`DB_CHARSET hasn't been set. Please make sure DB_CHARSET matches your database's collation.`);
|
|
108
|
-
}
|
|
109
106
|
database = (0, knex_1.knex)(knexConfig);
|
|
107
|
+
validateDatabaseCharset(database);
|
|
110
108
|
const times = {};
|
|
111
109
|
database
|
|
112
110
|
.on('query', (queryInfo) => {
|
|
113
111
|
times[queryInfo.__knexUid] = perf_hooks_1.performance.now();
|
|
114
112
|
})
|
|
115
|
-
.on('query-response', (
|
|
113
|
+
.on('query-response', (_response, queryInfo) => {
|
|
116
114
|
const delta = perf_hooks_1.performance.now() - times[queryInfo.__knexUid];
|
|
117
115
|
logger_1.default.trace(`[${delta.toFixed(3)}ms] ${queryInfo.sql} [${queryInfo.bindings.join(', ')}]`);
|
|
118
116
|
delete times[queryInfo.__knexUid];
|
|
@@ -234,3 +232,34 @@ async function validateDatabaseExtensions() {
|
|
|
234
232
|
}
|
|
235
233
|
}
|
|
236
234
|
exports.validateDatabaseExtensions = validateDatabaseExtensions;
|
|
235
|
+
async function validateDatabaseCharset(database) {
|
|
236
|
+
database = database !== null && database !== void 0 ? database : getDatabase();
|
|
237
|
+
if (getDatabaseClient(database) === 'mysql') {
|
|
238
|
+
if (env_1.default.DB_CHARSET) {
|
|
239
|
+
logger_1.default.warn(`Using custom DB_CHARSET "${env_1.default.DB_CHARSET}". Using a charset different from the database's default can cause problems in relationships. Omitting DB_CHARSET is strongly recommended.`);
|
|
240
|
+
}
|
|
241
|
+
const { collation } = await database.select(database.raw(`@@collation_database as collation`)).first();
|
|
242
|
+
const tables = await database('information_schema.tables')
|
|
243
|
+
.select({ name: 'TABLE_NAME', collation: 'TABLE_COLLATION' })
|
|
244
|
+
.where({ TABLE_SCHEMA: env_1.default.DB_DATABASE });
|
|
245
|
+
const columns = await database('information_schema.columns')
|
|
246
|
+
.select({ table_name: 'TABLE_NAME', name: 'COLUMN_NAME', collation: 'COLLATION_NAME' })
|
|
247
|
+
.where({ TABLE_SCHEMA: env_1.default.DB_DATABASE })
|
|
248
|
+
.whereNot({ COLLATION_NAME: collation });
|
|
249
|
+
let inconsistencies = '';
|
|
250
|
+
for (const table of tables) {
|
|
251
|
+
const tableColumns = columns.filter((column) => column.table_name === table.name);
|
|
252
|
+
const tableHasInvalidCollation = table.collation !== collation;
|
|
253
|
+
if (tableHasInvalidCollation || tableColumns.length > 0) {
|
|
254
|
+
inconsistencies += `\t\t- Table "${table.name}": "${table.collation}"\n`;
|
|
255
|
+
for (const column of tableColumns) {
|
|
256
|
+
inconsistencies += `\t\t - Column "${column.name}": "${column.collation}"\n`;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
if (inconsistencies) {
|
|
261
|
+
logger_1.default.warn(`Some tables and columns do not match your database's default collation (${collation}):\n${inconsistencies}`);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
@@ -15,7 +15,7 @@ const extractToken = (req, res, next) => {
|
|
|
15
15
|
}
|
|
16
16
|
if (req.headers && req.headers.authorization) {
|
|
17
17
|
const parts = req.headers.authorization.split(' ');
|
|
18
|
-
if (parts.length === 2 && parts[0] === '
|
|
18
|
+
if (parts.length === 2 && parts[0].toLowerCase() === 'bearer') {
|
|
19
19
|
token = parts[1];
|
|
20
20
|
}
|
|
21
21
|
}
|
|
@@ -66,24 +66,7 @@ class CollectionsService {
|
|
|
66
66
|
// permission problems. This might not work reliably in MySQL, as it doesn't support DDL in
|
|
67
67
|
// transactions.
|
|
68
68
|
await this.knex.transaction(async (trx) => {
|
|
69
|
-
if (payload.meta) {
|
|
70
|
-
const collectionItemsService = new items_1.ItemsService('directus_collections', {
|
|
71
|
-
knex: trx,
|
|
72
|
-
accountability: this.accountability,
|
|
73
|
-
schema: this.schema,
|
|
74
|
-
});
|
|
75
|
-
await collectionItemsService.createOne({
|
|
76
|
-
...payload.meta,
|
|
77
|
-
collection: payload.collection,
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
69
|
if (payload.schema) {
|
|
81
|
-
const fieldsService = new fields_1.FieldsService({ knex: trx, schema: this.schema });
|
|
82
|
-
const fieldItemsService = new items_1.ItemsService('directus_fields', {
|
|
83
|
-
knex: trx,
|
|
84
|
-
accountability: this.accountability,
|
|
85
|
-
schema: this.schema,
|
|
86
|
-
});
|
|
87
70
|
// Directus heavily relies on the primary key of a collection, so we have to make sure that
|
|
88
71
|
// every collection that is created has a primary key. If no primary key field is created
|
|
89
72
|
// while making the collection, we default to an auto incremented id named `id`
|
|
@@ -114,18 +97,33 @@ class CollectionsService {
|
|
|
114
97
|
}
|
|
115
98
|
return field;
|
|
116
99
|
});
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
}
|
|
100
|
+
const fieldsService = new fields_1.FieldsService({ knex: trx, schema: this.schema });
|
|
101
|
+
await trx.schema.createTable(payload.collection, (table) => {
|
|
102
|
+
for (const field of payload.fields) {
|
|
103
|
+
if (field.type && constants_1.ALIAS_TYPES.includes(field.type) === false) {
|
|
104
|
+
fieldsService.addColumnToTable(table, field);
|
|
123
105
|
}
|
|
124
|
-
}
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
const fieldItemsService = new items_1.ItemsService('directus_fields', {
|
|
109
|
+
knex: trx,
|
|
110
|
+
accountability: this.accountability,
|
|
111
|
+
schema: this.schema,
|
|
125
112
|
});
|
|
126
113
|
const fieldPayloads = payload.fields.filter((field) => field.meta).map((field) => field.meta);
|
|
127
114
|
await fieldItemsService.createMany(fieldPayloads);
|
|
128
115
|
}
|
|
116
|
+
if (payload.meta) {
|
|
117
|
+
const collectionItemsService = new items_1.ItemsService('directus_collections', {
|
|
118
|
+
knex: trx,
|
|
119
|
+
accountability: this.accountability,
|
|
120
|
+
schema: this.schema,
|
|
121
|
+
});
|
|
122
|
+
await collectionItemsService.createOne({
|
|
123
|
+
...payload.meta,
|
|
124
|
+
collection: payload.collection,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
129
127
|
return payload.collection;
|
|
130
128
|
});
|
|
131
129
|
if (this.cache && env_1.default.CACHE_AUTO_PURGE && (opts === null || opts === void 0 ? void 0 : opts.autoPurgeCache) !== false) {
|
|
@@ -318,6 +316,7 @@ class CollectionsService {
|
|
|
318
316
|
}
|
|
319
317
|
await this.knex.transaction(async (trx) => {
|
|
320
318
|
var _a;
|
|
319
|
+
await trx.schema.dropTable(collectionKey);
|
|
321
320
|
// Make sure this collection isn't used as a group in any other collections
|
|
322
321
|
await trx('directus_collections').update({ group: null }).where({ group: collectionKey });
|
|
323
322
|
if (collectionToBeDeleted.meta) {
|
|
@@ -373,9 +372,6 @@ class CollectionsService {
|
|
|
373
372
|
.update({ one_allowed_collections: newAllowedCollections })
|
|
374
373
|
.where({ id: relation.meta.id });
|
|
375
374
|
}
|
|
376
|
-
await trx.transaction(async (schemaTrx) => {
|
|
377
|
-
await schemaTrx.schema.dropTable(collectionKey);
|
|
378
|
-
});
|
|
379
375
|
}
|
|
380
376
|
});
|
|
381
377
|
if (this.cache && env_1.default.CACHE_AUTO_PURGE && (opts === null || opts === void 0 ? void 0 : opts.autoPurgeCache) !== false) {
|
package/dist/services/fields.js
CHANGED
|
@@ -224,10 +224,8 @@ class FieldsService {
|
|
|
224
224
|
this.addColumnToTable(table, hookAdjustedField);
|
|
225
225
|
}
|
|
226
226
|
else {
|
|
227
|
-
await trx.
|
|
228
|
-
|
|
229
|
-
this.addColumnToTable(table, hookAdjustedField);
|
|
230
|
-
});
|
|
227
|
+
await trx.schema.alterTable(collection, (table) => {
|
|
228
|
+
this.addColumnToTable(table, hookAdjustedField);
|
|
231
229
|
});
|
|
232
230
|
}
|
|
233
231
|
}
|
|
@@ -327,6 +325,13 @@ class FieldsService {
|
|
|
327
325
|
});
|
|
328
326
|
await this.knex.transaction(async (trx) => {
|
|
329
327
|
var _a, _b;
|
|
328
|
+
if (this.schema.collections[collection] &&
|
|
329
|
+
field in this.schema.collections[collection].fields &&
|
|
330
|
+
this.schema.collections[collection].fields[field].alias === false) {
|
|
331
|
+
await trx.schema.table(collection, (table) => {
|
|
332
|
+
table.dropColumn(field);
|
|
333
|
+
});
|
|
334
|
+
}
|
|
330
335
|
const relations = this.schema.relations.filter((relation) => {
|
|
331
336
|
var _a;
|
|
332
337
|
return ((relation.collection === collection && relation.field === field) ||
|
|
@@ -386,15 +391,6 @@ class FieldsService {
|
|
|
386
391
|
.where({ group: metaRow.field, collection: metaRow.collection });
|
|
387
392
|
}
|
|
388
393
|
await trx('directus_fields').delete().where({ collection, field });
|
|
389
|
-
if (this.schema.collections[collection] &&
|
|
390
|
-
field in this.schema.collections[collection].fields &&
|
|
391
|
-
this.schema.collections[collection].fields[field].alias === false) {
|
|
392
|
-
await trx.transaction(async (schemaTrx) => {
|
|
393
|
-
await schemaTrx.schema.table(collection, (table) => {
|
|
394
|
-
table.dropColumn(field);
|
|
395
|
-
});
|
|
396
|
-
});
|
|
397
|
-
}
|
|
398
394
|
});
|
|
399
395
|
if (this.cache && env_1.default.CACHE_AUTO_PURGE) {
|
|
400
396
|
await this.cache.clear();
|
|
@@ -410,7 +406,7 @@ class FieldsService {
|
|
|
410
406
|
});
|
|
411
407
|
}
|
|
412
408
|
addColumnToTable(table, field, alter = null) {
|
|
413
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
409
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
414
410
|
let column;
|
|
415
411
|
// Don't attempt to add a DB column for alias / corrupt fields
|
|
416
412
|
if (field.type === 'alias' || field.type === 'unknown')
|
|
@@ -456,21 +452,25 @@ class FieldsService {
|
|
|
456
452
|
column.defaultTo(field.schema.default_value);
|
|
457
453
|
}
|
|
458
454
|
}
|
|
459
|
-
if (((_g = field.schema) === null || _g === void 0 ? void 0 : _g.is_nullable)
|
|
460
|
-
|
|
455
|
+
if (((_g = field.schema) === null || _g === void 0 ? void 0 : _g.is_nullable) === false) {
|
|
456
|
+
if (!alter || alter.is_nullable === true) {
|
|
457
|
+
column.notNullable();
|
|
458
|
+
}
|
|
461
459
|
}
|
|
462
|
-
else {
|
|
463
|
-
|
|
460
|
+
else if (((_h = field.schema) === null || _h === void 0 ? void 0 : _h.is_nullable) === true) {
|
|
461
|
+
if (!alter || alter.is_nullable === false) {
|
|
462
|
+
column.nullable();
|
|
463
|
+
}
|
|
464
464
|
}
|
|
465
|
-
if ((
|
|
465
|
+
if ((_j = field.schema) === null || _j === void 0 ? void 0 : _j.is_primary_key) {
|
|
466
466
|
column.primary().notNullable();
|
|
467
467
|
}
|
|
468
|
-
else if (((
|
|
468
|
+
else if (((_k = field.schema) === null || _k === void 0 ? void 0 : _k.is_unique) === true) {
|
|
469
469
|
if (!alter || alter.is_unique === false) {
|
|
470
470
|
column.unique();
|
|
471
471
|
}
|
|
472
472
|
}
|
|
473
|
-
else if (((
|
|
473
|
+
else if (((_l = field.schema) === null || _l === void 0 ? void 0 : _l.is_unique) === false) {
|
|
474
474
|
if (alter && alter.is_unique === true) {
|
|
475
475
|
table.dropUnique([field.field]);
|
|
476
476
|
}
|
package/dist/services/payload.js
CHANGED
|
@@ -100,12 +100,16 @@ class PayloadService {
|
|
|
100
100
|
return value;
|
|
101
101
|
},
|
|
102
102
|
async csv({ action, value }) {
|
|
103
|
-
if (
|
|
103
|
+
if (Array.isArray(value) === false && typeof value !== 'string')
|
|
104
104
|
return;
|
|
105
|
-
if (action === 'read' && Array.isArray(value) === false)
|
|
105
|
+
if (action === 'read' && Array.isArray(value) === false) {
|
|
106
|
+
if (value === '')
|
|
107
|
+
return [];
|
|
106
108
|
return value.split(',');
|
|
107
|
-
|
|
109
|
+
}
|
|
110
|
+
if (Array.isArray(value)) {
|
|
108
111
|
return value.join(',');
|
|
112
|
+
}
|
|
109
113
|
return value;
|
|
110
114
|
},
|
|
111
115
|
};
|
|
@@ -145,7 +145,7 @@ function processPermissions(accountability, permissions, filterContext) {
|
|
|
145
145
|
return permissions.map((permission) => {
|
|
146
146
|
permission.permissions = (0, utils_1.parseFilter)(permission.permissions, accountability, filterContext);
|
|
147
147
|
permission.validation = (0, utils_1.parseFilter)(permission.validation, accountability, filterContext);
|
|
148
|
-
permission.presets = (0, utils_1.
|
|
148
|
+
permission.presets = (0, utils_1.parsePreset)(permission.presets, accountability, filterContext);
|
|
149
149
|
return permission;
|
|
150
150
|
});
|
|
151
151
|
}
|
package/dist/utils/md.js
CHANGED
|
@@ -10,6 +10,6 @@ const sanitize_html_1 = __importDefault(require("sanitize-html"));
|
|
|
10
10
|
* Render and sanitize a markdown string
|
|
11
11
|
*/
|
|
12
12
|
function md(str) {
|
|
13
|
-
return (0, sanitize_html_1.default)((0, marked_1.
|
|
13
|
+
return (0, sanitize_html_1.default)((0, marked_1.marked)(str));
|
|
14
14
|
}
|
|
15
15
|
exports.md = md;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "directus",
|
|
3
|
-
"version": "9.5.
|
|
3
|
+
"version": "9.5.1",
|
|
4
4
|
"license": "GPL-3.0-only",
|
|
5
5
|
"homepage": "https://github.com/directus/directus#readme",
|
|
6
6
|
"description": "Directus is a real-time API and App dashboard for managing SQL database content.",
|
|
@@ -63,7 +63,8 @@
|
|
|
63
63
|
"cleanup": "rimraf dist",
|
|
64
64
|
"dev": "cross-env NODE_ENV=development SERVE_APP=false ts-node-dev --files --transpile-only --respawn --watch \".env\" --inspect=0 --exit-child -- src/start.ts",
|
|
65
65
|
"cli": "cross-env NODE_ENV=development SERVE_APP=false ts-node --script-mode --transpile-only src/cli/run.ts",
|
|
66
|
-
"test": "jest
|
|
66
|
+
"test": "jest",
|
|
67
|
+
"test:coverage": "jest --coverage",
|
|
67
68
|
"test:watch": "jest --watchAll"
|
|
68
69
|
},
|
|
69
70
|
"engines": {
|
|
@@ -77,16 +78,16 @@
|
|
|
77
78
|
],
|
|
78
79
|
"dependencies": {
|
|
79
80
|
"@aws-sdk/client-ses": "^3.40.0",
|
|
80
|
-
"@directus/app": "9.5.
|
|
81
|
-
"@directus/drive": "9.5.
|
|
82
|
-
"@directus/drive-azure": "9.5.
|
|
83
|
-
"@directus/drive-gcs": "9.5.
|
|
84
|
-
"@directus/drive-s3": "9.5.
|
|
85
|
-
"@directus/extensions-sdk": "9.5.
|
|
86
|
-
"@directus/format-title": "9.5.
|
|
87
|
-
"@directus/schema": "9.5.
|
|
88
|
-
"@directus/shared": "9.5.
|
|
89
|
-
"@directus/specs": "9.5.
|
|
81
|
+
"@directus/app": "9.5.1",
|
|
82
|
+
"@directus/drive": "9.5.1",
|
|
83
|
+
"@directus/drive-azure": "9.5.1",
|
|
84
|
+
"@directus/drive-gcs": "9.5.1",
|
|
85
|
+
"@directus/drive-s3": "9.5.1",
|
|
86
|
+
"@directus/extensions-sdk": "9.5.1",
|
|
87
|
+
"@directus/format-title": "9.5.1",
|
|
88
|
+
"@directus/schema": "9.5.1",
|
|
89
|
+
"@directus/shared": "9.5.1",
|
|
90
|
+
"@directus/specs": "9.5.1",
|
|
90
91
|
"@godaddy/terminus": "^4.9.0",
|
|
91
92
|
"@rollup/plugin-alias": "^3.1.2",
|
|
92
93
|
"@rollup/plugin-virtual": "^2.0.3",
|
|
@@ -125,7 +126,7 @@
|
|
|
125
126
|
"jsonwebtoken": "^8.5.1",
|
|
126
127
|
"keyv": "^4.0.3",
|
|
127
128
|
"knex": "^0.95.14",
|
|
128
|
-
"knex-schema-inspector": "1.7.
|
|
129
|
+
"knex-schema-inspector": "1.7.3",
|
|
129
130
|
"ldapjs": "^2.3.1",
|
|
130
131
|
"liquidjs": "^9.25.0",
|
|
131
132
|
"lodash": "^4.17.21",
|
|
@@ -172,7 +173,7 @@
|
|
|
172
173
|
"sqlite3": "^5.0.2",
|
|
173
174
|
"tedious": "^13.0.0"
|
|
174
175
|
},
|
|
175
|
-
"gitHead": "
|
|
176
|
+
"gitHead": "c0c412d30b116bd38a2690b62463f775d37db79c",
|
|
176
177
|
"devDependencies": {
|
|
177
178
|
"@types/async": "3.2.10",
|
|
178
179
|
"@types/atob": "2.1.2",
|
|
@@ -195,12 +196,15 @@
|
|
|
195
196
|
"@types/keyv": "3.1.3",
|
|
196
197
|
"@types/ldapjs": "2.2.2",
|
|
197
198
|
"@types/lodash": "4.14.177",
|
|
199
|
+
"@types/marked": "4.0.1",
|
|
198
200
|
"@types/mime-types": "2.1.1",
|
|
199
201
|
"@types/ms": "0.7.31",
|
|
200
202
|
"@types/node": "16.11.9",
|
|
201
203
|
"@types/node-cron": "2.0.5",
|
|
202
204
|
"@types/nodemailer": "6.4.4",
|
|
203
205
|
"@types/object-hash": "2.2.1",
|
|
206
|
+
"@types/pino": "6.3.12",
|
|
207
|
+
"@types/pino-http": "5.8.0",
|
|
204
208
|
"@types/qs": "6.9.7",
|
|
205
209
|
"@types/sanitize-html": "2.5.0",
|
|
206
210
|
"@types/sharp": "0.29.4",
|