linagora-rse
Version:
182 lines (141 loc) • 5.53 kB
JavaScript
const Busboy = require('busboy');
const ObjectId = require('mongoose').Types.ObjectId;
const filestore = require('../../core/filestore');
const logger = require('../../core/logger');
const esnConfig = require('../../core/esn-config');
module.exports = {
create,
get,
remove
};
function create(req, res) {
const size = parseInt(req.query.size, 10);
if (isNaN(size) || size < 1) {
return res.status(400).json({ error: { code: 400, message: 'Bad Parameter', details: 'size parameter should be a positive integer' }});
}
const fileId = new ObjectId();
const options = {};
const metadata = {};
if (req.query.name) {
options.filename = req.query.name;
}
if (req.user) {
metadata.creator = { objectType: 'user', id: req.user._id };
}
if (req.headers['content-type'] && req.headers['content-type'].indexOf('multipart/form-data') === 0) {
return getUploadLimit().then(limit => onMultipartFormRequest({ limit }));
}
saveStream(req);
function onMultipartFormRequest({ limit }) {
const busboy = new Busboy({ headers: req.headers, limits: { fileSize: limit } });
let nb = 0;
busboy.once('file', (fieldname, file) => {
logger.debug(`${fileId} - Storing file from '${fieldname}'`);
nb++;
saveStream(file);
file.on('limit', () => {
logger.warn(`${fileId} - File limit (${limit} bytes) has been reached`);
});
});
busboy.on('finish', () => {
logger.debug(`${fileId} - ${nb} file(s) have been saved`);
if (nb === 0) {
res.status(400).json({ error: { code: 400, message: 'Bad request', details: 'The form data must contain an attachment' } });
}
});
busboy.on('filesLimit', err => {
logger.warn(`${fileId} - File limit (${limit} bytes) has been reached`, err);
});
req.pipe(busboy);
}
function saveStream(stream) {
let interrupted = false;
req.on('close', () => {
interrupted = true;
});
logger.debug(`${fileId} - Storing file fileId=${fileId}, mime=${req.query.mimetype}, metadata=${JSON.stringify(metadata)}`);
filestore.store(fileId, req.query.mimetype, metadata, stream, options, (err, saved) => {
if (err) {
logger.error(`${fileId} - Can not store file`, err);
return res.status(500).json({ error: { code: 500, message: 'Server error', details: err.message || err } });
}
if (saved.length !== size || interrupted) {
logger.error(`${fileId} - Error while storing file: saved.length=${saved.length}, size=${size}, interrupted=${interrupted}`);
return filestore.delete(fileId, err => {
if (err) {
logger.error(`${fileId} - File can not be deleted`, err);
}
res.status(412).json({ error: { code: 412, message: 'File size mismatch', details: `File size given by user agent is ${size} and file size returned by storage system is ${saved.length}` }});
});
}
res.status(201).json({ _id: fileId });
});
}
}
function getUploadLimit() {
return esnConfig('maxSizeUpload').get()
.then(maxSizeUpload => (maxSizeUpload || Infinity))
.catch(err => {
logger.warn('Can not get maxSizeUpload from configuration, default to Infinity', err);
return Infinity;
});
}
function get(req, res) {
if (!req.params.id) {
return res.status(400).json({ error: { code: 400, message: 'Bad Request', details: 'Missing id parameter' }});
}
filestore.get(req.params.id, (err, fileMeta, readStream) => {
if (err) {
return res.status(503).json({ error: { code: 503, message: 'Server error', details: err.message || err }});
}
if (!readStream) {
if (req.accepts('html')) {
res.status(404).end();
res.render('commons/404', { url: req.url });
} else {
res.status(404).json({ error: { code: 404, message: 'Not Found', details: 'Could not find file' }});
}
}
if (fileMeta) {
const modSince = req.get('If-Modified-Since');
const clientMod = new Date(modSince);
const serverMod = fileMeta.uploadDate;
clientMod.setMilliseconds(0);
serverMod.setMilliseconds(0);
try {
if (modSince && clientMod.getTime() === serverMod.getTime()) {
return res.status(304).end();
}
res.set('Last-Modified', fileMeta.uploadDate);
res.type(fileMeta.contentType);
if (fileMeta.filename) {
res.set('Content-Disposition', 'inline; filename="' +
fileMeta.filename.replace(/[^!#$%&'*+\-.^_`|~\d\w]/g, '') + '"');
}
if (fileMeta.length) {
res.set('Content-Length', fileMeta.length);
}
} catch (error) {
return res.status(500).json({ error: { code: 500, message: 'Server error', details: error.message || error }});
}
}
res.status(200);
readStream.pipe(res);
});
}
function remove(req, res) {
if (!req.params.id) {
return res.status(400).json({error: { code: 400, message: 'Bad request', details: 'Missing id parameter' }});
}
const meta = req.fileMeta;
if (meta.metadata.referenced) {
return res.status(409).json({error: { code: 409, message: 'Conflict', details: 'File is used and can not be deleted' }});
}
filestore.delete(req.params.id, err => {
if (err) {
logger.error('Can not delete file from store', err);
return res.status(500).json({error: { code: 500, message: 'Server Error', details: err.message || err }});
}
res.status(204).end();
});
}