directus 9.6.0 → 9.8.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/app.js +3 -1
- package/dist/auth/drivers/oauth2.js +3 -0
- package/dist/auth/drivers/openid.js +3 -0
- package/dist/cache.d.ts +4 -1
- package/dist/cache.js +29 -7
- package/dist/cli/commands/schema/apply.d.ts +1 -0
- package/dist/cli/commands/schema/apply.js +9 -5
- package/dist/cli/index.js +1 -0
- package/dist/controllers/assets.js +9 -1
- package/dist/controllers/utils.js +18 -1
- 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/sqlite.d.ts +0 -9
- package/dist/database/helpers/date/dialects/sqlite.js +0 -24
- package/dist/database/helpers/date/index.d.ts +6 -6
- package/dist/database/helpers/date/index.js +13 -13
- package/dist/database/helpers/date/types.d.ts +0 -9
- package/dist/database/helpers/{date → fn}/dialects/mssql.d.ts +3 -2
- package/dist/database/helpers/{date → fn}/dialects/mssql.js +14 -3
- package/dist/database/helpers/{date → fn}/dialects/mysql.d.ts +3 -2
- package/dist/database/helpers/{date → fn}/dialects/mysql.js +14 -3
- package/dist/database/helpers/{date → fn}/dialects/oracle.d.ts +3 -2
- package/dist/database/helpers/{date → fn}/dialects/oracle.js +14 -3
- package/dist/database/helpers/{date → fn}/dialects/postgres.d.ts +3 -2
- package/dist/database/helpers/{date → fn}/dialects/postgres.js +14 -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/index.js +0 -3
- 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/20220314A-add-translation-strings.d.ts +3 -0
- package/dist/database/migrations/20220314A-add-translation-strings.js +15 -0
- package/dist/database/migrations/20220322A-rename-field-typecast-flags.d.ts +3 -0
- package/dist/database/migrations/20220322A-rename-field-typecast-flags.js +77 -0
- 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/run.js +1 -1
- package/dist/database/run-ast.js +11 -3
- package/dist/database/system-data/fields/activity.yaml +4 -4
- package/dist/database/system-data/fields/collections.yaml +4 -4
- package/dist/database/system-data/fields/fields.yaml +17 -8
- package/dist/database/system-data/fields/files.yaml +2 -2
- package/dist/database/system-data/fields/panels.yaml +2 -2
- package/dist/database/system-data/fields/permissions.yaml +4 -4
- package/dist/database/system-data/fields/presets.yaml +17 -3
- package/dist/database/system-data/fields/relations.yaml +1 -1
- package/dist/database/system-data/fields/revisions.yaml +2 -2
- package/dist/database/system-data/fields/roles.yaml +4 -4
- package/dist/database/system-data/fields/settings.yaml +19 -4
- package/dist/database/system-data/fields/users.yaml +2 -2
- package/dist/database/system-data/fields/webhooks.yaml +4 -4
- package/dist/env.js +6 -2
- package/dist/exceptions/database/dialects/mysql.js +23 -17
- package/dist/logger.js +2 -1
- package/dist/middleware/respond.js +7 -28
- package/dist/services/activity.js +4 -1
- package/dist/services/authorization.d.ts +1 -1
- package/dist/services/authorization.js +156 -14
- package/dist/services/collections.js +222 -198
- package/dist/services/fields.js +184 -173
- package/dist/services/files.js +69 -3
- package/dist/services/graphql.js +2 -2
- package/dist/services/import-export.d.ts +34 -0
- package/dist/services/import-export.js +270 -0
- package/dist/services/index.d.ts +2 -1
- package/dist/services/index.js +2 -1
- package/dist/services/items.js +1 -0
- package/dist/services/payload.js +11 -9
- package/dist/services/permissions.js +10 -10
- package/dist/services/relations.js +93 -78
- package/dist/services/server.js +1 -0
- package/dist/services/shares.js +2 -1
- package/dist/services/users.js +3 -1
- package/dist/utils/apply-query.js +21 -3
- package/dist/utils/get-column.d.ts +6 -5
- package/dist/utils/get-column.js +16 -8
- package/dist/utils/get-date-formatted.d.ts +1 -0
- package/dist/utils/get-date-formatted.js +14 -0
- package/dist/utils/get-local-type.js +2 -2
- package/dist/utils/get-permissions.js +1 -1
- package/dist/utils/get-schema.d.ts +1 -1
- package/dist/utils/get-schema.js +16 -11
- 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-storage.js +3 -1
- package/package.json +13 -12
- package/dist/services/import.d.ts +0 -13
- package/dist/services/import.js +0 -118
package/dist/services/fields.js
CHANGED
|
@@ -199,211 +199,222 @@ class FieldsService {
|
|
|
199
199
|
if (this.accountability && this.accountability.admin !== true) {
|
|
200
200
|
throw new exceptions_1.ForbiddenException();
|
|
201
201
|
}
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
false;
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
202
|
+
try {
|
|
203
|
+
const exists = field.field in this.schema.collections[collection].fields ||
|
|
204
|
+
(0, lodash_1.isNil)(await this.knex.select('id').from('directus_fields').where({ collection, field: field.field }).first()) === false;
|
|
205
|
+
// Check if field already exists, either as a column, or as a row in directus_fields
|
|
206
|
+
if (exists) {
|
|
207
|
+
throw new exceptions_1.InvalidPayloadException(`Field "${field.field}" already exists in collection "${collection}"`);
|
|
208
|
+
}
|
|
209
|
+
await this.knex.transaction(async (trx) => {
|
|
210
|
+
const itemsService = new items_1.ItemsService('directus_fields', {
|
|
211
|
+
knex: trx,
|
|
212
|
+
accountability: this.accountability,
|
|
213
|
+
schema: this.schema,
|
|
214
|
+
});
|
|
215
|
+
const hookAdjustedField = await emitter_1.default.emitFilter(`fields.create`, field, {
|
|
216
|
+
collection: collection,
|
|
217
|
+
}, {
|
|
218
|
+
database: trx,
|
|
219
|
+
schema: this.schema,
|
|
220
|
+
accountability: this.accountability,
|
|
221
|
+
});
|
|
222
|
+
if (hookAdjustedField.type && constants_1.ALIAS_TYPES.includes(hookAdjustedField.type) === false) {
|
|
223
|
+
if (table) {
|
|
224
|
+
this.addColumnToTable(table, hookAdjustedField);
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
await trx.schema.alterTable(collection, (table) => {
|
|
228
|
+
this.addColumnToTable(table, hookAdjustedField);
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
if (hookAdjustedField.meta) {
|
|
233
|
+
await itemsService.createOne({
|
|
234
|
+
...hookAdjustedField.meta,
|
|
235
|
+
collection: collection,
|
|
236
|
+
field: hookAdjustedField.field,
|
|
237
|
+
}, { emitEvents: false });
|
|
238
|
+
}
|
|
239
|
+
emitter_1.default.emitAction(`fields.create`, {
|
|
240
|
+
payload: hookAdjustedField,
|
|
241
|
+
key: hookAdjustedField.field,
|
|
242
|
+
collection: collection,
|
|
243
|
+
}, {
|
|
244
|
+
database: (0, database_1.default)(),
|
|
245
|
+
schema: this.schema,
|
|
246
|
+
accountability: this.accountability,
|
|
247
|
+
});
|
|
214
248
|
});
|
|
215
|
-
|
|
249
|
+
}
|
|
250
|
+
finally {
|
|
251
|
+
if (this.cache && env_1.default.CACHE_AUTO_PURGE) {
|
|
252
|
+
await this.cache.clear();
|
|
253
|
+
}
|
|
254
|
+
await (0, cache_1.clearSystemCache)();
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
async updateField(collection, field) {
|
|
258
|
+
if (this.accountability && this.accountability.admin !== true) {
|
|
259
|
+
throw new exceptions_1.ForbiddenException();
|
|
260
|
+
}
|
|
261
|
+
try {
|
|
262
|
+
const hookAdjustedField = await emitter_1.default.emitFilter(`fields.update`, field, {
|
|
263
|
+
keys: [field.field],
|
|
216
264
|
collection: collection,
|
|
217
265
|
}, {
|
|
218
|
-
database:
|
|
266
|
+
database: this.knex,
|
|
219
267
|
schema: this.schema,
|
|
220
268
|
accountability: this.accountability,
|
|
221
269
|
});
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
270
|
+
const record = field.meta
|
|
271
|
+
? await this.knex.select('id').from('directus_fields').where({ collection, field: field.field }).first()
|
|
272
|
+
: null;
|
|
273
|
+
if (hookAdjustedField.schema) {
|
|
274
|
+
const existingColumn = await this.schemaInspector.columnInfo(collection, hookAdjustedField.field);
|
|
275
|
+
if (!(0, lodash_1.isEqual)(existingColumn, hookAdjustedField.schema)) {
|
|
276
|
+
try {
|
|
277
|
+
await this.knex.schema.alterTable(collection, (table) => {
|
|
278
|
+
if (!hookAdjustedField.schema)
|
|
279
|
+
return;
|
|
280
|
+
this.addColumnToTable(table, field, existingColumn);
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
catch (err) {
|
|
284
|
+
throw await (0, translate_1.translateDatabaseError)(err);
|
|
285
|
+
}
|
|
230
286
|
}
|
|
231
287
|
}
|
|
232
288
|
if (hookAdjustedField.meta) {
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
289
|
+
if (record) {
|
|
290
|
+
await this.itemsService.updateOne(record.id, {
|
|
291
|
+
...hookAdjustedField.meta,
|
|
292
|
+
collection: collection,
|
|
293
|
+
field: hookAdjustedField.field,
|
|
294
|
+
}, { emitEvents: false });
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
await this.itemsService.createOne({
|
|
298
|
+
...hookAdjustedField.meta,
|
|
299
|
+
collection: collection,
|
|
300
|
+
field: hookAdjustedField.field,
|
|
301
|
+
}, { emitEvents: false });
|
|
302
|
+
}
|
|
238
303
|
}
|
|
239
|
-
emitter_1.default.emitAction(`fields.
|
|
304
|
+
emitter_1.default.emitAction(`fields.update`, {
|
|
240
305
|
payload: hookAdjustedField,
|
|
241
|
-
|
|
306
|
+
keys: [hookAdjustedField.field],
|
|
242
307
|
collection: collection,
|
|
243
308
|
}, {
|
|
244
309
|
database: (0, database_1.default)(),
|
|
245
310
|
schema: this.schema,
|
|
246
311
|
accountability: this.accountability,
|
|
247
312
|
});
|
|
248
|
-
|
|
249
|
-
if (this.cache && env_1.default.CACHE_AUTO_PURGE) {
|
|
250
|
-
await this.cache.clear();
|
|
313
|
+
return field.field;
|
|
251
314
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
if (this.accountability && this.accountability.admin !== true) {
|
|
256
|
-
throw new exceptions_1.ForbiddenException();
|
|
257
|
-
}
|
|
258
|
-
const hookAdjustedField = await emitter_1.default.emitFilter(`fields.update`, field, {
|
|
259
|
-
keys: [field.field],
|
|
260
|
-
collection: collection,
|
|
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
|
-
}
|
|
315
|
+
finally {
|
|
316
|
+
if (this.cache && env_1.default.CACHE_AUTO_PURGE) {
|
|
317
|
+
await this.cache.clear();
|
|
282
318
|
}
|
|
319
|
+
await (0, cache_1.clearSystemCache)();
|
|
283
320
|
}
|
|
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 });
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
if (this.cache && env_1.default.CACHE_AUTO_PURGE) {
|
|
301
|
-
await this.cache.clear();
|
|
302
|
-
}
|
|
303
|
-
await this.systemCache.clear();
|
|
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
321
|
}
|
|
315
322
|
async deleteField(collection, field) {
|
|
316
323
|
if (this.accountability && this.accountability.admin !== true) {
|
|
317
324
|
throw new exceptions_1.ForbiddenException();
|
|
318
325
|
}
|
|
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,
|
|
326
|
+
try {
|
|
327
|
+
await emitter_1.default.emitFilter('fields.delete', [field], {
|
|
328
|
+
collection: collection,
|
|
329
|
+
}, {
|
|
330
|
+
database: this.knex,
|
|
343
331
|
schema: this.schema,
|
|
344
|
-
});
|
|
345
|
-
const fieldsService = new FieldsService({
|
|
346
|
-
knex: trx,
|
|
347
332
|
accountability: this.accountability,
|
|
348
|
-
schema: this.schema,
|
|
349
333
|
});
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
334
|
+
await this.knex.transaction(async (trx) => {
|
|
335
|
+
var _a, _b;
|
|
336
|
+
if (this.schema.collections[collection] &&
|
|
337
|
+
field in this.schema.collections[collection].fields &&
|
|
338
|
+
this.schema.collections[collection].fields[field].alias === false) {
|
|
339
|
+
await trx.schema.table(collection, (table) => {
|
|
340
|
+
table.dropColumn(field);
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
const relations = this.schema.relations.filter((relation) => {
|
|
344
|
+
var _a;
|
|
345
|
+
return ((relation.collection === collection && relation.field === field) ||
|
|
346
|
+
(relation.related_collection === collection && ((_a = relation.meta) === null || _a === void 0 ? void 0 : _a.one_field) === field));
|
|
347
|
+
});
|
|
348
|
+
const relationsService = new relations_1.RelationsService({
|
|
349
|
+
knex: trx,
|
|
350
|
+
accountability: this.accountability,
|
|
351
|
+
schema: this.schema,
|
|
352
|
+
});
|
|
353
|
+
const fieldsService = new FieldsService({
|
|
354
|
+
knex: trx,
|
|
355
|
+
accountability: this.accountability,
|
|
356
|
+
schema: this.schema,
|
|
357
|
+
});
|
|
358
|
+
for (const relation of relations) {
|
|
359
|
+
const isM2O = relation.collection === collection && relation.field === field;
|
|
360
|
+
// If the current field is a m2o, delete the related o2m if it exists and remove the relationship
|
|
361
|
+
if (isM2O) {
|
|
362
|
+
await relationsService.deleteOne(collection, field);
|
|
363
|
+
if (relation.related_collection && ((_a = relation.meta) === null || _a === void 0 ? void 0 : _a.one_field)) {
|
|
364
|
+
await fieldsService.deleteField(relation.related_collection, relation.meta.one_field);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
// If the current field is a o2m, just delete the one field config from the relation
|
|
368
|
+
if (!isM2O && ((_b = relation.meta) === null || _b === void 0 ? void 0 : _b.one_field)) {
|
|
369
|
+
await trx('directus_relations')
|
|
370
|
+
.update({ one_field: null })
|
|
371
|
+
.where({ many_collection: relation.collection, many_field: relation.field });
|
|
357
372
|
}
|
|
358
373
|
}
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
374
|
+
const collectionMeta = await trx
|
|
375
|
+
.select('archive_field', 'sort_field')
|
|
376
|
+
.from('directus_collections')
|
|
377
|
+
.where({ collection })
|
|
378
|
+
.first();
|
|
379
|
+
const collectionMetaUpdates = {};
|
|
380
|
+
if ((collectionMeta === null || collectionMeta === void 0 ? void 0 : collectionMeta.archive_field) === field) {
|
|
381
|
+
collectionMetaUpdates.archive_field = null;
|
|
364
382
|
}
|
|
383
|
+
if ((collectionMeta === null || collectionMeta === void 0 ? void 0 : collectionMeta.sort_field) === field) {
|
|
384
|
+
collectionMetaUpdates.sort_field = null;
|
|
385
|
+
}
|
|
386
|
+
if (Object.keys(collectionMetaUpdates).length > 0) {
|
|
387
|
+
await trx('directus_collections').update(collectionMetaUpdates).where({ collection });
|
|
388
|
+
}
|
|
389
|
+
// Cleanup directus_fields
|
|
390
|
+
const metaRow = await trx
|
|
391
|
+
.select('collection', 'field')
|
|
392
|
+
.from('directus_fields')
|
|
393
|
+
.where({ collection, field })
|
|
394
|
+
.first();
|
|
395
|
+
if (metaRow) {
|
|
396
|
+
// Handle recursive FK constraints
|
|
397
|
+
await trx('directus_fields')
|
|
398
|
+
.update({ group: null })
|
|
399
|
+
.where({ group: metaRow.field, collection: metaRow.collection });
|
|
400
|
+
}
|
|
401
|
+
await trx('directus_fields').delete().where({ collection, field });
|
|
402
|
+
});
|
|
403
|
+
emitter_1.default.emitAction('fields.delete', {
|
|
404
|
+
payload: [field],
|
|
405
|
+
collection: collection,
|
|
406
|
+
}, {
|
|
407
|
+
database: this.knex,
|
|
408
|
+
schema: this.schema,
|
|
409
|
+
accountability: this.accountability,
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
finally {
|
|
413
|
+
if (this.cache && env_1.default.CACHE_AUTO_PURGE) {
|
|
414
|
+
await this.cache.clear();
|
|
365
415
|
}
|
|
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 this.systemCache.clear();
|
|
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
|
-
});
|
|
416
|
+
await (0, cache_1.clearSystemCache)();
|
|
417
|
+
}
|
|
407
418
|
}
|
|
408
419
|
addColumnToTable(table, field, alter = null) {
|
|
409
420
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
package/dist/services/files.js
CHANGED
|
@@ -1,4 +1,23 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
5
|
+
}) : (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
o[k2] = m[k];
|
|
8
|
+
}));
|
|
9
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
10
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
11
|
+
}) : function(o, v) {
|
|
12
|
+
o["default"] = v;
|
|
13
|
+
});
|
|
14
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
15
|
+
if (mod && mod.__esModule) return mod;
|
|
16
|
+
var result = {};
|
|
17
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
18
|
+
__setModuleDefault(result, mod);
|
|
19
|
+
return result;
|
|
20
|
+
};
|
|
2
21
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
22
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
23
|
};
|
|
@@ -11,7 +30,9 @@ const lodash_1 = require("lodash");
|
|
|
11
30
|
const mime_types_1 = require("mime-types");
|
|
12
31
|
const path_1 = __importDefault(require("path"));
|
|
13
32
|
const sharp_1 = __importDefault(require("sharp"));
|
|
14
|
-
const url_1 =
|
|
33
|
+
const url_1 = __importStar(require("url"));
|
|
34
|
+
const util_1 = require("util");
|
|
35
|
+
const dns_1 = require("dns");
|
|
15
36
|
const emitter_1 = __importDefault(require("../emitter"));
|
|
16
37
|
const env_1 = __importDefault(require("../env"));
|
|
17
38
|
const exceptions_1 = require("../exceptions");
|
|
@@ -19,6 +40,9 @@ const logger_1 = __importDefault(require("../logger"));
|
|
|
19
40
|
const storage_1 = __importDefault(require("../storage"));
|
|
20
41
|
const utils_1 = require("@directus/shared/utils");
|
|
21
42
|
const items_1 = require("./items");
|
|
43
|
+
const net_1 = __importDefault(require("net"));
|
|
44
|
+
const os_1 = __importDefault(require("os"));
|
|
45
|
+
const lookupDNS = (0, util_1.promisify)(dns_1.lookup);
|
|
22
46
|
class FilesService extends items_1.ItemsService {
|
|
23
47
|
constructor(options) {
|
|
24
48
|
super('directus_files', options);
|
|
@@ -137,6 +161,49 @@ class FilesService extends items_1.ItemsService {
|
|
|
137
161
|
if (this.accountability && ((_c = this.accountability) === null || _c === void 0 ? void 0 : _c.admin) !== true && !fileCreatePermissions) {
|
|
138
162
|
throw new exceptions_1.ForbiddenException();
|
|
139
163
|
}
|
|
164
|
+
let resolvedUrl;
|
|
165
|
+
try {
|
|
166
|
+
resolvedUrl = new url_1.URL(importURL);
|
|
167
|
+
}
|
|
168
|
+
catch (err) {
|
|
169
|
+
logger_1.default.warn(err, `Requested URL ${importURL} isn't a valid URL`);
|
|
170
|
+
throw new exceptions_1.ServiceUnavailableException(`Couldn't fetch file from url "${importURL}"`, {
|
|
171
|
+
service: 'external-file',
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
let ip = resolvedUrl.hostname;
|
|
175
|
+
if (net_1.default.isIP(ip) === 0) {
|
|
176
|
+
try {
|
|
177
|
+
ip = (await lookupDNS(ip)).address;
|
|
178
|
+
}
|
|
179
|
+
catch (err) {
|
|
180
|
+
logger_1.default.warn(err, `Couldn't lookup the DNS for url ${importURL}`);
|
|
181
|
+
throw new exceptions_1.ServiceUnavailableException(`Couldn't fetch file from url "${importURL}"`, {
|
|
182
|
+
service: 'external-file',
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
if (env_1.default.IMPORT_IP_DENY_LIST.includes('0.0.0.0')) {
|
|
187
|
+
const networkInterfaces = os_1.default.networkInterfaces();
|
|
188
|
+
for (const networkInfo of Object.values(networkInterfaces)) {
|
|
189
|
+
if (!networkInfo)
|
|
190
|
+
continue;
|
|
191
|
+
for (const info of networkInfo) {
|
|
192
|
+
if (info.address === ip) {
|
|
193
|
+
logger_1.default.warn(`Requested URL ${importURL} resolves to localhost.`);
|
|
194
|
+
throw new exceptions_1.ServiceUnavailableException(`Couldn't fetch file from url "${importURL}"`, {
|
|
195
|
+
service: 'external-file',
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
if (env_1.default.IMPORT_IP_DENY_LIST.includes(ip)) {
|
|
202
|
+
logger_1.default.warn(`Requested URL ${importURL} resolves to a denied IP address.`);
|
|
203
|
+
throw new exceptions_1.ServiceUnavailableException(`Couldn't fetch file from url "${importURL}"`, {
|
|
204
|
+
service: 'external-file',
|
|
205
|
+
});
|
|
206
|
+
}
|
|
140
207
|
let fileResponse;
|
|
141
208
|
try {
|
|
142
209
|
fileResponse = await axios_1.default.get(importURL, {
|
|
@@ -144,8 +211,7 @@ class FilesService extends items_1.ItemsService {
|
|
|
144
211
|
});
|
|
145
212
|
}
|
|
146
213
|
catch (err) {
|
|
147
|
-
logger_1.default.warn(`Couldn't fetch file from url "${importURL}"`);
|
|
148
|
-
logger_1.default.warn(err);
|
|
214
|
+
logger_1.default.warn(err, `Couldn't fetch file from url "${importURL}"`);
|
|
149
215
|
throw new exceptions_1.ServiceUnavailableException(`Couldn't fetch file from url "${importURL}"`, {
|
|
150
216
|
service: 'external-file',
|
|
151
217
|
});
|
package/dist/services/graphql.js
CHANGED
|
@@ -1732,9 +1732,9 @@ class GraphQLService {
|
|
|
1732
1732
|
if (((_a = this.accountability) === null || _a === void 0 ? void 0 : _a.admin) !== true) {
|
|
1733
1733
|
throw new exceptions_1.ForbiddenException();
|
|
1734
1734
|
}
|
|
1735
|
-
const { cache
|
|
1735
|
+
const { cache } = (0, cache_1.getCache)();
|
|
1736
1736
|
await (cache === null || cache === void 0 ? void 0 : cache.clear());
|
|
1737
|
-
await
|
|
1737
|
+
await (0, cache_1.clearSystemCache)();
|
|
1738
1738
|
return;
|
|
1739
1739
|
},
|
|
1740
1740
|
},
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { Knex } from 'knex';
|
|
3
|
+
import { AbstractServiceOptions, File } from '../types';
|
|
4
|
+
import { Accountability, Query, SchemaOverview } from '@directus/shared/types';
|
|
5
|
+
export declare class ImportService {
|
|
6
|
+
knex: Knex;
|
|
7
|
+
accountability: Accountability | null;
|
|
8
|
+
schema: SchemaOverview;
|
|
9
|
+
constructor(options: AbstractServiceOptions);
|
|
10
|
+
import(collection: string, mimetype: string, stream: NodeJS.ReadableStream): Promise<void>;
|
|
11
|
+
importJSON(collection: string, stream: NodeJS.ReadableStream): Promise<void>;
|
|
12
|
+
importCSV(collection: string, stream: NodeJS.ReadableStream): Promise<void>;
|
|
13
|
+
}
|
|
14
|
+
export declare class ExportService {
|
|
15
|
+
knex: Knex;
|
|
16
|
+
accountability: Accountability | null;
|
|
17
|
+
schema: SchemaOverview;
|
|
18
|
+
constructor(options: AbstractServiceOptions);
|
|
19
|
+
/**
|
|
20
|
+
* Export the query results as a named file. Will query in batches, and keep appending a tmp file
|
|
21
|
+
* until all the data is retrieved. Uploads the result as a new file using the regular
|
|
22
|
+
* FilesService upload method.
|
|
23
|
+
*/
|
|
24
|
+
exportToFile(collection: string, query: Partial<Query>, format: 'xml' | 'csv' | 'json', options?: {
|
|
25
|
+
file?: Partial<File>;
|
|
26
|
+
}): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Transform a given input object / array to the given type
|
|
29
|
+
*/
|
|
30
|
+
transform(input: Record<string, any>[], format: 'xml' | 'csv' | 'json', options?: {
|
|
31
|
+
includeHeader?: boolean;
|
|
32
|
+
includeFooter?: boolean;
|
|
33
|
+
}): string;
|
|
34
|
+
}
|