directus 9.7.1 → 9.9.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/__mocks__/cache.d.ts +5 -0
- package/dist/__mocks__/cache.js +7 -0
- package/dist/auth/drivers/ldap.js +10 -11
- package/dist/auth/drivers/oauth2.js +9 -4
- package/dist/auth/drivers/openid.js +7 -4
- package/dist/cache.js +2 -2
- package/dist/cli/commands/schema/apply.js +9 -3
- package/dist/controllers/assets.js +5 -0
- package/dist/controllers/files.d.ts +2 -0
- package/dist/controllers/files.js +13 -5
- package/dist/database/helpers/date/dialects/default.d.ts +3 -0
- package/dist/database/helpers/date/dialects/default.js +7 -0
- package/dist/database/helpers/date/dialects/mssql.d.ts +1 -9
- package/dist/database/helpers/date/dialects/mssql.js +4 -23
- package/dist/database/helpers/date/dialects/mysql.d.ts +2 -9
- package/dist/database/helpers/date/dialects/mysql.js +7 -22
- package/dist/database/helpers/date/dialects/oracle.d.ts +1 -9
- package/dist/database/helpers/date/dialects/oracle.js +7 -23
- package/dist/database/helpers/date/dialects/sqlite.d.ts +1 -9
- package/dist/database/helpers/date/dialects/sqlite.js +8 -24
- package/dist/database/helpers/date/index.d.ts +4 -4
- package/dist/database/helpers/date/index.js +9 -9
- package/dist/database/helpers/date/types.d.ts +3 -9
- package/dist/database/helpers/date/types.js +10 -0
- package/dist/database/helpers/fn/dialects/mssql.d.ts +13 -0
- package/dist/database/helpers/fn/dialects/mssql.js +42 -0
- package/dist/database/helpers/{date/dialects/postgres.d.ts → fn/dialects/mysql.d.ts} +3 -2
- package/dist/database/helpers/fn/dialects/mysql.js +42 -0
- package/dist/database/helpers/fn/dialects/oracle.d.ts +13 -0
- package/dist/database/helpers/fn/dialects/oracle.js +42 -0
- package/dist/database/helpers/fn/dialects/postgres.d.ts +13 -0
- package/dist/database/helpers/{date → fn}/dialects/postgres.js +18 -3
- package/dist/database/helpers/fn/dialects/sqlite.d.ts +13 -0
- package/dist/database/helpers/fn/dialects/sqlite.js +42 -0
- package/dist/database/helpers/fn/index.d.ts +7 -0
- package/dist/database/helpers/fn/index.js +17 -0
- package/dist/database/helpers/fn/types.d.ts +18 -0
- package/dist/database/helpers/fn/types.js +27 -0
- package/dist/database/helpers/index.d.ts +4 -1
- package/dist/database/helpers/index.js +7 -1
- package/dist/database/migrations/20220308A-add-bookmark-icon-and-color.d.ts +3 -0
- package/dist/database/migrations/20220308A-add-bookmark-icon-and-color.js +17 -0
- package/dist/database/migrations/20220322A-rename-field-typecast-flags.js +6 -2
- package/dist/database/migrations/20220323A-add-field-validation.d.ts +3 -0
- package/dist/database/migrations/20220323A-add-field-validation.js +17 -0
- package/dist/database/migrations/20220325A-fix-typecast-flags.d.ts +3 -0
- package/dist/database/migrations/20220325A-fix-typecast-flags.js +49 -0
- package/dist/database/migrations/20220325B-add-default-language.d.ts +3 -0
- package/dist/database/migrations/20220325B-add-default-language.js +28 -0
- package/dist/database/migrations/20220402A-remove-default-value-panel-icon.d.ts +3 -0
- package/dist/database/migrations/20220402A-remove-default-value-panel-icon.js +22 -0
- package/dist/database/run-ast.js +7 -5
- package/dist/database/system-data/fields/activity.yaml +4 -4
- package/dist/database/system-data/fields/collections.yaml +1 -1
- package/dist/database/system-data/fields/fields.yaml +9 -0
- package/dist/database/system-data/fields/presets.yaml +14 -0
- package/dist/database/system-data/fields/settings.yaml +12 -1
- package/dist/database/system-data/fields/users.yaml +3 -0
- package/dist/env.js +5 -3
- package/dist/exceptions/index.d.ts +1 -0
- package/dist/exceptions/index.js +1 -0
- package/dist/exceptions/token-expired.d.ts +4 -0
- package/dist/exceptions/token-expired.js +10 -0
- package/dist/logger.js +2 -1
- package/dist/middleware/cache.js +10 -0
- package/dist/services/activity.js +4 -1
- package/dist/services/authorization.d.ts +1 -1
- package/dist/services/authorization.js +174 -48
- package/dist/services/collections.d.ts +2 -0
- package/dist/services/collections.js +232 -198
- package/dist/services/fields.js +210 -174
- package/dist/services/files.d.ts +5 -1
- package/dist/services/files.js +59 -40
- package/dist/services/graphql.d.ts +2 -3
- package/dist/services/graphql.js +53 -10
- package/dist/services/items.js +5 -3
- package/dist/services/payload.d.ts +2 -1
- package/dist/services/payload.js +28 -21
- package/dist/services/relations.js +93 -81
- package/dist/services/server.js +1 -0
- package/dist/services/shares.js +2 -1
- package/dist/services/specifications.js +1 -3
- package/dist/services/users.js +7 -2
- package/dist/types/files.d.ts +8 -0
- package/dist/utils/apply-query.js +38 -10
- package/dist/utils/apply-snapshot.d.ts +3 -1
- package/dist/utils/apply-snapshot.js +34 -5
- package/dist/utils/get-ast-from-query.js +15 -3
- package/dist/utils/get-column.d.ts +6 -5
- package/dist/utils/get-column.js +16 -8
- package/dist/utils/get-graphql-type.js +1 -0
- package/dist/utils/get-local-type.js +5 -0
- package/dist/utils/get-schema.d.ts +1 -1
- package/dist/utils/get-schema.js +18 -10
- package/dist/utils/jwt.js +1 -1
- package/dist/utils/reduce-schema.js +20 -12
- package/dist/utils/track.js +3 -2
- package/dist/utils/url.d.ts +1 -1
- package/dist/utils/url.js +1 -1
- package/dist/utils/validate-query.js +19 -15
- package/dist/utils/validate-storage.js +3 -1
- package/example.env +4 -0
- package/package.json +14 -13
package/dist/services/fields.js
CHANGED
|
@@ -40,6 +40,7 @@ const utils_1 = require("@directus/shared/utils");
|
|
|
40
40
|
const lodash_1 = require("lodash");
|
|
41
41
|
const relations_1 = require("./relations");
|
|
42
42
|
const helpers_1 = require("../database/helpers");
|
|
43
|
+
const constants_2 = require("@directus/shared/constants");
|
|
43
44
|
class FieldsService {
|
|
44
45
|
constructor(options) {
|
|
45
46
|
this.knex = options.knex || (0, database_1.default)();
|
|
@@ -60,6 +61,7 @@ class FieldsService {
|
|
|
60
61
|
}));
|
|
61
62
|
}
|
|
62
63
|
async readAll(collection) {
|
|
64
|
+
var _a, _b, _c, _d;
|
|
63
65
|
let fields;
|
|
64
66
|
if (this.accountability && this.accountability.admin !== true && this.hasReadAccess === false) {
|
|
65
67
|
throw new exceptions_1.ForbiddenException();
|
|
@@ -151,6 +153,15 @@ class FieldsService {
|
|
|
151
153
|
return allowedFields.includes(field.field);
|
|
152
154
|
});
|
|
153
155
|
}
|
|
156
|
+
// Update specific database type overrides
|
|
157
|
+
for (const field of result) {
|
|
158
|
+
if ((_b = (_a = field.meta) === null || _a === void 0 ? void 0 : _a.special) === null || _b === void 0 ? void 0 : _b.includes('cast-timestamp')) {
|
|
159
|
+
field.type = 'timestamp';
|
|
160
|
+
}
|
|
161
|
+
else if ((_d = (_c = field.meta) === null || _c === void 0 ? void 0 : _c.special) === null || _d === void 0 ? void 0 : _d.includes('cast-datetime')) {
|
|
162
|
+
field.type = 'dateTime';
|
|
163
|
+
}
|
|
164
|
+
}
|
|
154
165
|
return result;
|
|
155
166
|
}
|
|
156
167
|
async readOne(collection, field) {
|
|
@@ -184,6 +195,8 @@ class FieldsService {
|
|
|
184
195
|
catch {
|
|
185
196
|
// Do nothing
|
|
186
197
|
}
|
|
198
|
+
if (!column && !fieldInfo)
|
|
199
|
+
throw new exceptions_1.ForbiddenException();
|
|
187
200
|
const type = (0, get_local_type_1.default)(column, fieldInfo);
|
|
188
201
|
const data = {
|
|
189
202
|
collection,
|
|
@@ -199,211 +212,227 @@ class FieldsService {
|
|
|
199
212
|
if (this.accountability && this.accountability.admin !== true) {
|
|
200
213
|
throw new exceptions_1.ForbiddenException();
|
|
201
214
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
false;
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
215
|
+
try {
|
|
216
|
+
const exists = field.field in this.schema.collections[collection].fields ||
|
|
217
|
+
(0, lodash_1.isNil)(await this.knex.select('id').from('directus_fields').where({ collection, field: field.field }).first()) === false;
|
|
218
|
+
// Check if field already exists, either as a column, or as a row in directus_fields
|
|
219
|
+
if (exists) {
|
|
220
|
+
throw new exceptions_1.InvalidPayloadException(`Field "${field.field}" already exists in collection "${collection}"`);
|
|
221
|
+
}
|
|
222
|
+
// Add flag for specific database type overrides
|
|
223
|
+
const flagToAdd = this.helpers.date.fieldFlagForField(field.type);
|
|
224
|
+
if (flagToAdd) {
|
|
225
|
+
(0, utils_1.addFieldFlag)(field, flagToAdd);
|
|
226
|
+
}
|
|
227
|
+
await this.knex.transaction(async (trx) => {
|
|
228
|
+
const itemsService = new items_1.ItemsService('directus_fields', {
|
|
229
|
+
knex: trx,
|
|
230
|
+
accountability: this.accountability,
|
|
231
|
+
schema: this.schema,
|
|
232
|
+
});
|
|
233
|
+
const hookAdjustedField = await emitter_1.default.emitFilter(`fields.create`, field, {
|
|
234
|
+
collection: collection,
|
|
235
|
+
}, {
|
|
236
|
+
database: trx,
|
|
237
|
+
schema: this.schema,
|
|
238
|
+
accountability: this.accountability,
|
|
239
|
+
});
|
|
240
|
+
if (hookAdjustedField.type && constants_1.ALIAS_TYPES.includes(hookAdjustedField.type) === false) {
|
|
241
|
+
if (table) {
|
|
242
|
+
this.addColumnToTable(table, hookAdjustedField);
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
await trx.schema.alterTable(collection, (table) => {
|
|
246
|
+
this.addColumnToTable(table, hookAdjustedField);
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
if (hookAdjustedField.meta) {
|
|
251
|
+
await itemsService.createOne({
|
|
252
|
+
...hookAdjustedField.meta,
|
|
253
|
+
collection: collection,
|
|
254
|
+
field: hookAdjustedField.field,
|
|
255
|
+
}, { emitEvents: false });
|
|
256
|
+
}
|
|
257
|
+
emitter_1.default.emitAction(`fields.create`, {
|
|
258
|
+
payload: hookAdjustedField,
|
|
259
|
+
key: hookAdjustedField.field,
|
|
260
|
+
collection: collection,
|
|
261
|
+
}, {
|
|
262
|
+
database: (0, database_1.default)(),
|
|
263
|
+
schema: this.schema,
|
|
264
|
+
accountability: this.accountability,
|
|
265
|
+
});
|
|
214
266
|
});
|
|
215
|
-
|
|
267
|
+
}
|
|
268
|
+
finally {
|
|
269
|
+
if (this.cache && env_1.default.CACHE_AUTO_PURGE) {
|
|
270
|
+
await this.cache.clear();
|
|
271
|
+
}
|
|
272
|
+
await (0, cache_1.clearSystemCache)();
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
async updateField(collection, field) {
|
|
276
|
+
if (this.accountability && this.accountability.admin !== true) {
|
|
277
|
+
throw new exceptions_1.ForbiddenException();
|
|
278
|
+
}
|
|
279
|
+
try {
|
|
280
|
+
const hookAdjustedField = await emitter_1.default.emitFilter(`fields.update`, field, {
|
|
281
|
+
keys: [field.field],
|
|
216
282
|
collection: collection,
|
|
217
283
|
}, {
|
|
218
|
-
database:
|
|
284
|
+
database: this.knex,
|
|
219
285
|
schema: this.schema,
|
|
220
286
|
accountability: this.accountability,
|
|
221
287
|
});
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
288
|
+
const record = field.meta
|
|
289
|
+
? await this.knex.select('id').from('directus_fields').where({ collection, field: field.field }).first()
|
|
290
|
+
: null;
|
|
291
|
+
if (hookAdjustedField.schema) {
|
|
292
|
+
const existingColumn = await this.schemaInspector.columnInfo(collection, hookAdjustedField.field);
|
|
293
|
+
if (!(0, lodash_1.isEqual)(existingColumn, hookAdjustedField.schema)) {
|
|
294
|
+
try {
|
|
295
|
+
await this.knex.schema.alterTable(collection, (table) => {
|
|
296
|
+
if (!hookAdjustedField.schema)
|
|
297
|
+
return;
|
|
298
|
+
this.addColumnToTable(table, field, existingColumn);
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
catch (err) {
|
|
302
|
+
throw await (0, translate_1.translateDatabaseError)(err);
|
|
303
|
+
}
|
|
230
304
|
}
|
|
231
305
|
}
|
|
232
306
|
if (hookAdjustedField.meta) {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
307
|
+
if (record) {
|
|
308
|
+
await this.itemsService.updateOne(record.id, {
|
|
309
|
+
...hookAdjustedField.meta,
|
|
310
|
+
collection: collection,
|
|
311
|
+
field: hookAdjustedField.field,
|
|
312
|
+
}, { emitEvents: false });
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
await this.itemsService.createOne({
|
|
316
|
+
...hookAdjustedField.meta,
|
|
317
|
+
collection: collection,
|
|
318
|
+
field: hookAdjustedField.field,
|
|
319
|
+
}, { emitEvents: false });
|
|
320
|
+
}
|
|
238
321
|
}
|
|
239
|
-
emitter_1.default.emitAction(`fields.
|
|
322
|
+
emitter_1.default.emitAction(`fields.update`, {
|
|
240
323
|
payload: hookAdjustedField,
|
|
241
|
-
|
|
324
|
+
keys: [hookAdjustedField.field],
|
|
242
325
|
collection: collection,
|
|
243
326
|
}, {
|
|
244
327
|
database: (0, database_1.default)(),
|
|
245
328
|
schema: this.schema,
|
|
246
329
|
accountability: this.accountability,
|
|
247
330
|
});
|
|
248
|
-
|
|
249
|
-
if (this.cache && env_1.default.CACHE_AUTO_PURGE) {
|
|
250
|
-
await this.cache.clear();
|
|
251
|
-
}
|
|
252
|
-
await (0, cache_1.clearSystemCache)();
|
|
253
|
-
}
|
|
254
|
-
async updateField(collection, field) {
|
|
255
|
-
if (this.accountability && this.accountability.admin !== true) {
|
|
256
|
-
throw new exceptions_1.ForbiddenException();
|
|
331
|
+
return field.field;
|
|
257
332
|
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
}, {
|
|
262
|
-
database: this.knex,
|
|
263
|
-
schema: this.schema,
|
|
264
|
-
accountability: this.accountability,
|
|
265
|
-
});
|
|
266
|
-
const record = field.meta
|
|
267
|
-
? await this.knex.select('id').from('directus_fields').where({ collection, field: field.field }).first()
|
|
268
|
-
: null;
|
|
269
|
-
if (hookAdjustedField.schema) {
|
|
270
|
-
const existingColumn = await this.schemaInspector.columnInfo(collection, hookAdjustedField.field);
|
|
271
|
-
if (!(0, lodash_1.isEqual)(existingColumn, hookAdjustedField.schema)) {
|
|
272
|
-
try {
|
|
273
|
-
await this.knex.schema.alterTable(collection, (table) => {
|
|
274
|
-
if (!hookAdjustedField.schema)
|
|
275
|
-
return;
|
|
276
|
-
this.addColumnToTable(table, field, existingColumn);
|
|
277
|
-
});
|
|
278
|
-
}
|
|
279
|
-
catch (err) {
|
|
280
|
-
throw await (0, translate_1.translateDatabaseError)(err);
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
if (hookAdjustedField.meta) {
|
|
285
|
-
if (record) {
|
|
286
|
-
await this.itemsService.updateOne(record.id, {
|
|
287
|
-
...hookAdjustedField.meta,
|
|
288
|
-
collection: collection,
|
|
289
|
-
field: hookAdjustedField.field,
|
|
290
|
-
}, { emitEvents: false });
|
|
291
|
-
}
|
|
292
|
-
else {
|
|
293
|
-
await this.itemsService.createOne({
|
|
294
|
-
...hookAdjustedField.meta,
|
|
295
|
-
collection: collection,
|
|
296
|
-
field: hookAdjustedField.field,
|
|
297
|
-
}, { emitEvents: false });
|
|
333
|
+
finally {
|
|
334
|
+
if (this.cache && env_1.default.CACHE_AUTO_PURGE) {
|
|
335
|
+
await this.cache.clear();
|
|
298
336
|
}
|
|
337
|
+
await (0, cache_1.clearSystemCache)();
|
|
299
338
|
}
|
|
300
|
-
if (this.cache && env_1.default.CACHE_AUTO_PURGE) {
|
|
301
|
-
await this.cache.clear();
|
|
302
|
-
}
|
|
303
|
-
await (0, cache_1.clearSystemCache)();
|
|
304
|
-
emitter_1.default.emitAction(`fields.update`, {
|
|
305
|
-
payload: hookAdjustedField,
|
|
306
|
-
keys: [hookAdjustedField.field],
|
|
307
|
-
collection: collection,
|
|
308
|
-
}, {
|
|
309
|
-
database: (0, database_1.default)(),
|
|
310
|
-
schema: this.schema,
|
|
311
|
-
accountability: this.accountability,
|
|
312
|
-
});
|
|
313
|
-
return field.field;
|
|
314
339
|
}
|
|
315
340
|
async deleteField(collection, field) {
|
|
316
341
|
if (this.accountability && this.accountability.admin !== true) {
|
|
317
342
|
throw new exceptions_1.ForbiddenException();
|
|
318
343
|
}
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
accountability: this.accountability,
|
|
325
|
-
});
|
|
326
|
-
await this.knex.transaction(async (trx) => {
|
|
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
|
-
}
|
|
335
|
-
const relations = this.schema.relations.filter((relation) => {
|
|
336
|
-
var _a;
|
|
337
|
-
return ((relation.collection === collection && relation.field === field) ||
|
|
338
|
-
(relation.related_collection === collection && ((_a = relation.meta) === null || _a === void 0 ? void 0 : _a.one_field) === field));
|
|
339
|
-
});
|
|
340
|
-
const relationsService = new relations_1.RelationsService({
|
|
341
|
-
knex: trx,
|
|
342
|
-
accountability: this.accountability,
|
|
344
|
+
try {
|
|
345
|
+
await emitter_1.default.emitFilter('fields.delete', [field], {
|
|
346
|
+
collection: collection,
|
|
347
|
+
}, {
|
|
348
|
+
database: this.knex,
|
|
343
349
|
schema: this.schema,
|
|
344
|
-
});
|
|
345
|
-
const fieldsService = new FieldsService({
|
|
346
|
-
knex: trx,
|
|
347
350
|
accountability: this.accountability,
|
|
348
|
-
schema: this.schema,
|
|
349
351
|
});
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
352
|
+
await this.knex.transaction(async (trx) => {
|
|
353
|
+
var _a, _b;
|
|
354
|
+
if (this.schema.collections[collection] &&
|
|
355
|
+
field in this.schema.collections[collection].fields &&
|
|
356
|
+
this.schema.collections[collection].fields[field].alias === false) {
|
|
357
|
+
await trx.schema.table(collection, (table) => {
|
|
358
|
+
table.dropColumn(field);
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
const relations = this.schema.relations.filter((relation) => {
|
|
362
|
+
var _a;
|
|
363
|
+
return ((relation.collection === collection && relation.field === field) ||
|
|
364
|
+
(relation.related_collection === collection && ((_a = relation.meta) === null || _a === void 0 ? void 0 : _a.one_field) === field));
|
|
365
|
+
});
|
|
366
|
+
const relationsService = new relations_1.RelationsService({
|
|
367
|
+
knex: trx,
|
|
368
|
+
accountability: this.accountability,
|
|
369
|
+
schema: this.schema,
|
|
370
|
+
});
|
|
371
|
+
const fieldsService = new FieldsService({
|
|
372
|
+
knex: trx,
|
|
373
|
+
accountability: this.accountability,
|
|
374
|
+
schema: this.schema,
|
|
375
|
+
});
|
|
376
|
+
for (const relation of relations) {
|
|
377
|
+
const isM2O = relation.collection === collection && relation.field === field;
|
|
378
|
+
// If the current field is a m2o, delete the related o2m if it exists and remove the relationship
|
|
379
|
+
if (isM2O) {
|
|
380
|
+
await relationsService.deleteOne(collection, field);
|
|
381
|
+
if (relation.related_collection && ((_a = relation.meta) === null || _a === void 0 ? void 0 : _a.one_field)) {
|
|
382
|
+
await fieldsService.deleteField(relation.related_collection, relation.meta.one_field);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
// If the current field is a o2m, just delete the one field config from the relation
|
|
386
|
+
if (!isM2O && ((_b = relation.meta) === null || _b === void 0 ? void 0 : _b.one_field)) {
|
|
387
|
+
await trx('directus_relations')
|
|
388
|
+
.update({ one_field: null })
|
|
389
|
+
.where({ many_collection: relation.collection, many_field: relation.field });
|
|
357
390
|
}
|
|
358
391
|
}
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
392
|
+
const collectionMeta = await trx
|
|
393
|
+
.select('archive_field', 'sort_field')
|
|
394
|
+
.from('directus_collections')
|
|
395
|
+
.where({ collection })
|
|
396
|
+
.first();
|
|
397
|
+
const collectionMetaUpdates = {};
|
|
398
|
+
if ((collectionMeta === null || collectionMeta === void 0 ? void 0 : collectionMeta.archive_field) === field) {
|
|
399
|
+
collectionMetaUpdates.archive_field = null;
|
|
364
400
|
}
|
|
401
|
+
if ((collectionMeta === null || collectionMeta === void 0 ? void 0 : collectionMeta.sort_field) === field) {
|
|
402
|
+
collectionMetaUpdates.sort_field = null;
|
|
403
|
+
}
|
|
404
|
+
if (Object.keys(collectionMetaUpdates).length > 0) {
|
|
405
|
+
await trx('directus_collections').update(collectionMetaUpdates).where({ collection });
|
|
406
|
+
}
|
|
407
|
+
// Cleanup directus_fields
|
|
408
|
+
const metaRow = await trx
|
|
409
|
+
.select('collection', 'field')
|
|
410
|
+
.from('directus_fields')
|
|
411
|
+
.where({ collection, field })
|
|
412
|
+
.first();
|
|
413
|
+
if (metaRow) {
|
|
414
|
+
// Handle recursive FK constraints
|
|
415
|
+
await trx('directus_fields')
|
|
416
|
+
.update({ group: null })
|
|
417
|
+
.where({ group: metaRow.field, collection: metaRow.collection });
|
|
418
|
+
}
|
|
419
|
+
await trx('directus_fields').delete().where({ collection, field });
|
|
420
|
+
});
|
|
421
|
+
emitter_1.default.emitAction('fields.delete', {
|
|
422
|
+
payload: [field],
|
|
423
|
+
collection: collection,
|
|
424
|
+
}, {
|
|
425
|
+
database: this.knex,
|
|
426
|
+
schema: this.schema,
|
|
427
|
+
accountability: this.accountability,
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
finally {
|
|
431
|
+
if (this.cache && env_1.default.CACHE_AUTO_PURGE) {
|
|
432
|
+
await this.cache.clear();
|
|
365
433
|
}
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
.from('directus_collections')
|
|
369
|
-
.where({ collection })
|
|
370
|
-
.first();
|
|
371
|
-
const collectionMetaUpdates = {};
|
|
372
|
-
if ((collectionMeta === null || collectionMeta === void 0 ? void 0 : collectionMeta.archive_field) === field) {
|
|
373
|
-
collectionMetaUpdates.archive_field = null;
|
|
374
|
-
}
|
|
375
|
-
if ((collectionMeta === null || collectionMeta === void 0 ? void 0 : collectionMeta.sort_field) === field) {
|
|
376
|
-
collectionMetaUpdates.sort_field = null;
|
|
377
|
-
}
|
|
378
|
-
if (Object.keys(collectionMetaUpdates).length > 0) {
|
|
379
|
-
await trx('directus_collections').update(collectionMetaUpdates).where({ collection });
|
|
380
|
-
}
|
|
381
|
-
// Cleanup directus_fields
|
|
382
|
-
const metaRow = await trx
|
|
383
|
-
.select('collection', 'field')
|
|
384
|
-
.from('directus_fields')
|
|
385
|
-
.where({ collection, field })
|
|
386
|
-
.first();
|
|
387
|
-
if (metaRow) {
|
|
388
|
-
// Handle recursive FK constraints
|
|
389
|
-
await trx('directus_fields')
|
|
390
|
-
.update({ group: null })
|
|
391
|
-
.where({ group: metaRow.field, collection: metaRow.collection });
|
|
392
|
-
}
|
|
393
|
-
await trx('directus_fields').delete().where({ collection, field });
|
|
394
|
-
});
|
|
395
|
-
if (this.cache && env_1.default.CACHE_AUTO_PURGE) {
|
|
396
|
-
await this.cache.clear();
|
|
397
|
-
}
|
|
398
|
-
await (0, cache_1.clearSystemCache)();
|
|
399
|
-
emitter_1.default.emitAction('fields.delete', {
|
|
400
|
-
payload: [field],
|
|
401
|
-
collection: collection,
|
|
402
|
-
}, {
|
|
403
|
-
database: this.knex,
|
|
404
|
-
schema: this.schema,
|
|
405
|
-
accountability: this.accountability,
|
|
406
|
-
});
|
|
434
|
+
await (0, cache_1.clearSystemCache)();
|
|
435
|
+
}
|
|
407
436
|
}
|
|
408
437
|
addColumnToTable(table, field, alter = null) {
|
|
409
438
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
@@ -441,9 +470,16 @@ class FieldsService {
|
|
|
441
470
|
column = table[field.type](field.field);
|
|
442
471
|
}
|
|
443
472
|
if (((_f = field.schema) === null || _f === void 0 ? void 0 : _f.default_value) !== undefined) {
|
|
444
|
-
if (typeof field.schema.default_value === 'string' &&
|
|
473
|
+
if (typeof field.schema.default_value === 'string' &&
|
|
474
|
+
(field.schema.default_value.toLowerCase() === 'now()' || field.schema.default_value === 'CURRENT_TIMESTAMP')) {
|
|
445
475
|
column.defaultTo(this.knex.fn.now());
|
|
446
476
|
}
|
|
477
|
+
else if (typeof field.schema.default_value === 'string' &&
|
|
478
|
+
field.schema.default_value.includes('CURRENT_TIMESTAMP(') &&
|
|
479
|
+
field.schema.default_value.includes(')')) {
|
|
480
|
+
const precision = field.schema.default_value.match(constants_2.REGEX_BETWEEN_PARENS)[1];
|
|
481
|
+
column.defaultTo(this.knex.fn.now(Number(precision)));
|
|
482
|
+
}
|
|
447
483
|
else if (typeof field.schema.default_value === 'string' &&
|
|
448
484
|
['"null"', 'null'].includes(field.schema.default_value.toLowerCase())) {
|
|
449
485
|
column.defaultTo(null);
|
package/dist/services/files.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
-
import { AbstractServiceOptions, File, PrimaryKey, MutationOptions } from '../types';
|
|
2
|
+
import { AbstractServiceOptions, File, PrimaryKey, MutationOptions, Metadata } from '../types';
|
|
3
3
|
import { ItemsService } from './items';
|
|
4
4
|
export declare class FilesService extends ItemsService {
|
|
5
5
|
constructor(options: AbstractServiceOptions);
|
|
@@ -10,6 +10,10 @@ export declare class FilesService extends ItemsService {
|
|
|
10
10
|
filename_download: string;
|
|
11
11
|
storage: string;
|
|
12
12
|
}, primaryKey?: PrimaryKey, opts?: MutationOptions): Promise<PrimaryKey>;
|
|
13
|
+
/**
|
|
14
|
+
* Extract metadata from a buffer's content
|
|
15
|
+
*/
|
|
16
|
+
getMetadata(bufferContent: any, allowList?: any): Promise<Metadata>;
|
|
13
17
|
/**
|
|
14
18
|
* Import a single file from an external URL
|
|
15
19
|
*/
|
package/dist/services/files.js
CHANGED
|
@@ -88,46 +88,13 @@ class FilesService extends items_1.ItemsService {
|
|
|
88
88
|
payload.filesize = size;
|
|
89
89
|
if (['image/jpeg', 'image/png', 'image/webp', 'image/gif', 'image/tiff'].includes(payload.type)) {
|
|
90
90
|
const buffer = await storage_1.default.disk(data.storage).getBuffer(payload.filename_disk);
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
payload.width = meta.width;
|
|
99
|
-
payload.height = meta.height;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
catch (err) {
|
|
103
|
-
logger_1.default.warn(`Couldn't extract sharp metadata from file`);
|
|
104
|
-
logger_1.default.warn(err);
|
|
105
|
-
}
|
|
106
|
-
payload.metadata = {};
|
|
107
|
-
try {
|
|
108
|
-
payload.metadata = await exifr_1.default.parse(buffer.content, {
|
|
109
|
-
icc: false,
|
|
110
|
-
iptc: true,
|
|
111
|
-
ifd1: true,
|
|
112
|
-
interop: true,
|
|
113
|
-
translateValues: true,
|
|
114
|
-
reviveValues: true,
|
|
115
|
-
mergeOutput: false,
|
|
116
|
-
});
|
|
117
|
-
if ((_b = (_a = payload.metadata) === null || _a === void 0 ? void 0 : _a.iptc) === null || _b === void 0 ? void 0 : _b.Headline) {
|
|
118
|
-
payload.title = payload.metadata.iptc.Headline;
|
|
119
|
-
}
|
|
120
|
-
if (!payload.description && ((_d = (_c = payload.metadata) === null || _c === void 0 ? void 0 : _c.iptc) === null || _d === void 0 ? void 0 : _d.Caption)) {
|
|
121
|
-
payload.description = payload.metadata.iptc.Caption;
|
|
122
|
-
}
|
|
123
|
-
if ((_f = (_e = payload.metadata) === null || _e === void 0 ? void 0 : _e.iptc) === null || _f === void 0 ? void 0 : _f.Keywords) {
|
|
124
|
-
payload.tags = payload.metadata.iptc.Keywords;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
catch (err) {
|
|
128
|
-
logger_1.default.warn(`Couldn't extract EXIF metadata from file`);
|
|
129
|
-
logger_1.default.warn(err);
|
|
130
|
-
}
|
|
91
|
+
const { height, width, description, title, tags, metadata } = await this.getMetadata(buffer.content);
|
|
92
|
+
(_a = payload.height) !== null && _a !== void 0 ? _a : (payload.height = height);
|
|
93
|
+
(_b = payload.width) !== null && _b !== void 0 ? _b : (payload.width = width);
|
|
94
|
+
(_c = payload.description) !== null && _c !== void 0 ? _c : (payload.description = description);
|
|
95
|
+
(_d = payload.title) !== null && _d !== void 0 ? _d : (payload.title = title);
|
|
96
|
+
(_e = payload.tags) !== null && _e !== void 0 ? _e : (payload.tags = tags);
|
|
97
|
+
(_f = payload.metadata) !== null && _f !== void 0 ? _f : (payload.metadata = metadata);
|
|
131
98
|
}
|
|
132
99
|
// We do this in a service without accountability. Even if you don't have update permissions to the file,
|
|
133
100
|
// we still want to be able to set the extracted values from the file on create
|
|
@@ -152,6 +119,58 @@ class FilesService extends items_1.ItemsService {
|
|
|
152
119
|
}
|
|
153
120
|
return primaryKey;
|
|
154
121
|
}
|
|
122
|
+
/**
|
|
123
|
+
* Extract metadata from a buffer's content
|
|
124
|
+
*/
|
|
125
|
+
async getMetadata(bufferContent, allowList = env_1.default.FILE_METADATA_ALLOW_LIST) {
|
|
126
|
+
const metadata = {};
|
|
127
|
+
try {
|
|
128
|
+
const sharpMetadata = await (0, sharp_1.default)(bufferContent, {}).metadata();
|
|
129
|
+
if (sharpMetadata.orientation && sharpMetadata.orientation >= 5) {
|
|
130
|
+
metadata.height = sharpMetadata.width;
|
|
131
|
+
metadata.width = sharpMetadata.height;
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
metadata.width = sharpMetadata.width;
|
|
135
|
+
metadata.height = sharpMetadata.height;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
catch (err) {
|
|
139
|
+
logger_1.default.warn(`Couldn't extract sharp metadata from file`);
|
|
140
|
+
logger_1.default.warn(err);
|
|
141
|
+
}
|
|
142
|
+
try {
|
|
143
|
+
const exifrMetadata = await exifr_1.default.parse(bufferContent, {
|
|
144
|
+
icc: false,
|
|
145
|
+
iptc: true,
|
|
146
|
+
ifd1: true,
|
|
147
|
+
interop: true,
|
|
148
|
+
translateValues: true,
|
|
149
|
+
reviveValues: true,
|
|
150
|
+
mergeOutput: false,
|
|
151
|
+
});
|
|
152
|
+
if (allowList === '*' || (allowList === null || allowList === void 0 ? void 0 : allowList[0]) === '*') {
|
|
153
|
+
metadata.metadata = exifrMetadata;
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
metadata.metadata = (0, lodash_1.pick)(exifrMetadata, allowList);
|
|
157
|
+
}
|
|
158
|
+
if (!metadata.description && (exifrMetadata === null || exifrMetadata === void 0 ? void 0 : exifrMetadata.Caption)) {
|
|
159
|
+
metadata.description = exifrMetadata.Caption;
|
|
160
|
+
}
|
|
161
|
+
if (exifrMetadata === null || exifrMetadata === void 0 ? void 0 : exifrMetadata.Headline) {
|
|
162
|
+
metadata.title = exifrMetadata.Headline;
|
|
163
|
+
}
|
|
164
|
+
if (exifrMetadata === null || exifrMetadata === void 0 ? void 0 : exifrMetadata.Keywords) {
|
|
165
|
+
metadata.tags = exifrMetadata.Keywords;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
catch (err) {
|
|
169
|
+
logger_1.default.warn(`Couldn't extract EXIF metadata from file`);
|
|
170
|
+
logger_1.default.warn(err);
|
|
171
|
+
}
|
|
172
|
+
return metadata;
|
|
173
|
+
}
|
|
155
174
|
/**
|
|
156
175
|
* Import a single file from an external URL
|
|
157
176
|
*/
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
+
import { BaseException } from '@directus/shared/exceptions';
|
|
2
|
+
import { Accountability, Query, SchemaOverview } from '@directus/shared/types';
|
|
1
3
|
import { ArgumentNode, FormattedExecutionResult, FragmentDefinitionNode, GraphQLError, GraphQLResolveInfo, GraphQLScalarType, GraphQLSchema, ObjectFieldNode, SelectionNode } from 'graphql';
|
|
2
|
-
import { SchemaOverview } from '@directus/shared/types';
|
|
3
4
|
import { ObjectTypeComposer, SchemaComposer } from 'graphql-compose';
|
|
4
5
|
import { Knex } from 'knex';
|
|
5
|
-
import { BaseException } from '@directus/shared/exceptions';
|
|
6
|
-
import { Accountability, Query } from '@directus/shared/types';
|
|
7
6
|
import { AbstractServiceOptions, GraphQLParams, Item } from '../types';
|
|
8
7
|
import { ItemsService } from './items';
|
|
9
8
|
export declare const GraphQLGeoJSON: GraphQLScalarType;
|