mirror of
https://github.com/Foltik/Shimapan
synced 2024-12-04 11:24:32 -05:00
108 lines
3.5 KiB
JavaScript
108 lines
3.5 KiB
JavaScript
const Busboy = require('busboy');
|
|
const is = require('type-is');
|
|
const config = require('config');
|
|
|
|
const authenticate = require('../auth/authenticate');
|
|
const disk = require('./disk');
|
|
const identifier = require('./id');
|
|
|
|
const uploadMultipart = async (req, res, next) => {
|
|
if (!is(req, ['multipart']))
|
|
return res.status(400).json({message: 'Bad request.'});
|
|
|
|
// Store whether the user has authenticated, because an api key might be included with the form later
|
|
// If not authenticated with a session, we'll have to wait for key authentication from the multipart form data
|
|
let status = await authenticate(req, 'file.upload');
|
|
|
|
// Function to call once the file is sent or an error is encountered
|
|
let isDone = false;
|
|
const done = async (err, data) => {
|
|
if (isDone) return;
|
|
isDone = true;
|
|
|
|
req.unpipe(busboy);
|
|
busboy.removeAllListeners();
|
|
req.on('readable', req.read.bind(req));
|
|
|
|
if (err) {
|
|
if (data.path)
|
|
await disk.remove(data.path);
|
|
|
|
if (err === 'LIMIT_FILE_SIZE')
|
|
return res.status(413).json({message: 'File too large.'});
|
|
else {
|
|
console.log(err.stack);
|
|
return res.status(500).json({message: 'Internal server error.'});
|
|
}
|
|
} else {
|
|
req.file = data.file;
|
|
next();
|
|
}
|
|
};
|
|
|
|
// Create the busboy object to parse the multipart data
|
|
const busboy = new Busboy({
|
|
headers: req.headers,
|
|
limits: {
|
|
fileSize: config.get('Upload.maxSize')
|
|
}
|
|
});
|
|
|
|
req.body = {};
|
|
busboy.on('field', (fieldName, value) => {
|
|
req.body[fieldName] = value;
|
|
});
|
|
|
|
let fileReceived = false;
|
|
busboy.on('file', async (fieldName, stream, name, encoding, mime) => {
|
|
// Only process one file, discard everything after that
|
|
if (fileReceived)
|
|
return req.unpipe(busboy);
|
|
fileReceived = true;
|
|
|
|
// If a key was encountered and we are not authenticated, try to authenticate with it before the final check
|
|
if (req.body.key && !status.authenticated)
|
|
status = await authenticate(req, 'file.upload', status);
|
|
|
|
// Finally, check if we have auth before preceeding, keys should have been processed by now
|
|
if (!status.authenticated)
|
|
return res.status(401).json({message: 'Unauthorized.'});
|
|
if (!status.permission)
|
|
return res.status(403).json({message: 'Forbidden.'});
|
|
|
|
// Don't attach to the files object if there is no file
|
|
if (!name) return stream.resume();
|
|
|
|
// Generate an ID for the file, saving it with that filename
|
|
const path = config.get('Upload.path');
|
|
const newName = await identifier.generate();
|
|
const finalPath = path + '/' + newName;
|
|
|
|
// Set the file attributes
|
|
file = {
|
|
originalName: name,
|
|
name: newName,
|
|
path: finalPath,
|
|
encoding: encoding,
|
|
mime: mime
|
|
};
|
|
|
|
// Ensure the output directory exists
|
|
await disk.mkdir(path);
|
|
|
|
// Handle errors
|
|
stream.on('error', err => done(err, {path: finalPath}));
|
|
stream.on('limit', () => done('LIMIT_FILE_SIZE', {path: finalPath}));
|
|
|
|
file.size = await disk.write(finalPath, stream);
|
|
|
|
await done(null, {file: file});
|
|
});
|
|
|
|
busboy.on('error', err => done(err));
|
|
busboy.on('finished', () => done(null, {file: file}));
|
|
|
|
req.pipe(busboy);
|
|
};
|
|
|
|
module.exports = uploadMultipart; |