diff --git a/src/routes/files.ts b/src/routes/files.ts index 86421e88d1..4a105e14bd 100644 --- a/src/routes/files.ts +++ b/src/routes/files.ts @@ -3,8 +3,6 @@ import asyncHandler from 'express-async-handler'; import Busboy from 'busboy'; import sanitizeQuery from '../middleware/sanitize-query'; import * as FilesService from '../services/files'; -import logger from '../logger'; -import { InvalidPayloadException } from '../exceptions'; import useCollection from '../middleware/use-collection'; import { Item } from '../types'; @@ -17,14 +15,23 @@ const multipartHandler = (operation: 'create' | 'update') => const busboy = new Busboy({ headers: req.headers }); const savedFiles: Item[] = []; + const accountability = { + role: req.role, + admin: req.admin, + ip: req.ip, + userAgent: req.get('user-agent'), + user: req.user, + }; + /** * The order of the fields in multipart/form-data is important. We require that all fields * are provided _before_ the files. This allows us to set the storage location, and create * the row in directus_files async during the upload of the actual file. */ - let disk: string; + let disk: string = (process.env.STORAGE_LOCATIONS as string).split(',')[0].trim(); let payload: Partial = {}; + let fileCount = 0; busboy.on('field', (fieldname, val) => { if (fieldname === 'storage') { @@ -35,9 +42,7 @@ const multipartHandler = (operation: 'create' | 'update') => }); busboy.on('file', async (fieldname, fileStream, filename, encoding, mimetype) => { - if (!disk) { - return busboy.emit('error', new InvalidPayloadException('No storage provided.')); - } + fileCount++; payload = { ...payload, @@ -46,48 +51,40 @@ const multipartHandler = (operation: 'create' | 'update') => type: mimetype, }; + if (!payload.storage) { + payload.storage = disk; + } + if (req.user) { payload.uploaded_by = req.user; } - fileStream.on('end', () => { - logger.info(`File ${filename} uploaded to ${disk}.`); - }); - try { if (operation === 'create') { - const pk = await FilesService.createFile(payload, fileStream, { - role: req.role, - admin: req.admin, - ip: req.ip, - userAgent: req.get('user-agent'), - user: req.user, - }); - const file = await FilesService.readFile(pk, req.sanitizedQuery, { - role: req.role, - admin: req.admin, - }); + const pk = await FilesService.createFile(payload, fileStream, accountability); + const file = await FilesService.readFile( + pk, + req.sanitizedQuery, + accountability + ); savedFiles.push(file); + tryDone(); } else { const pk = await FilesService.updateFile( req.params.pk, payload, - { - role: req.role, - admin: req.admin, - ip: req.ip, - userAgent: req.get('user-agent'), - user: req.user, - }, + accountability, fileStream ); - const file = await FilesService.readFile(pk, req.sanitizedQuery, { - role: req.role, - admin: req.admin, - }); + const file = await FilesService.readFile( + pk, + req.sanitizedQuery, + accountability + ); savedFiles.push(file); + tryDone(); } } catch (err) { busboy.emit('error', err); @@ -99,10 +96,20 @@ const multipartHandler = (operation: 'create' | 'update') => }); busboy.on('finish', () => { - res.status(200).json({ data: savedFiles || null }); + tryDone(); }); - return req.pipe(busboy); + req.pipe(busboy); + + function tryDone() { + if (savedFiles.length === fileCount) { + if (fileCount === 1) { + return res.status(200).json({ data: savedFiles[0] }); + } else { + return res.status(200).json({ data: savedFiles }); + } + } + } }); router.post('/', sanitizeQuery, multipartHandler('create')); diff --git a/src/services/files.ts b/src/services/files.ts index 95b97ab035..85c0b53cd2 100644 --- a/src/services/files.ts +++ b/src/services/files.ts @@ -52,8 +52,11 @@ export const createFile = async ( }); } + const pk = await ItemsService.createItem('directus_files', payload, accountability); + await storage.disk(data.storage).put(payload.filename_disk, stream.pipe(pipeline)); - return await ItemsService.createItem('directus_files', payload, accountability); + + return pk; }; export const readFiles = async (query: Query, accountability?: Accountability) => { diff --git a/src/services/payload.ts b/src/services/payload.ts index 36580e94ca..970513bbbf 100644 --- a/src/services/payload.ts +++ b/src/services/payload.ts @@ -99,6 +99,16 @@ export async function processValues( }) ); + if (['create', 'update'].includes(operation)) { + processedPayload.forEach((record) => { + for (const [key, value] of Object.entries(record)) { + if (Array.isArray(value) || (typeof value === 'object' && value !== null)) { + record[key] = JSON.stringify(value); + } + } + }); + } + if (Array.isArray(payload)) { return processedPayload; }