A simple file sharing site with an easy to use API and online panel.
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

108 linhas
3.5KB

  1. const Busboy = require('busboy');
  2. const is = require('type-is');
  3. const config = require('config');
  4. const authenticate = require('../auth/authenticate');
  5. const disk = require('./disk');
  6. const identifier = require('./id');
  7. const uploadMultipart = async (req, res, next) => {
  8. if (!is(req, ['multipart']))
  9. return res.status(400).json({message: 'Bad request.'});
  10. // Store whether the user has authenticated, because an api key might be included with the form later
  11. // If not authenticated with a session, we'll have to wait for key authentication from the multipart form data
  12. let status = await authenticate(req, 'file.upload');
  13. // Function to call once the file is sent or an error is encountered
  14. let isDone = false;
  15. const done = async (err, data) => {
  16. if (isDone) return;
  17. isDone = true;
  18. req.unpipe(busboy);
  19. busboy.removeAllListeners();
  20. req.on('readable', req.read.bind(req));
  21. if (err) {
  22. if (data.path)
  23. await disk.remove(data.path);
  24. if (err === 'LIMIT_FILE_SIZE')
  25. return res.status(413).json({message: 'File too large.'});
  26. else {
  27. console.log(err.stack);
  28. return res.status(500).json({message: 'Internal server error.'});
  29. }
  30. } else {
  31. req.file = data.file;
  32. next();
  33. }
  34. };
  35. // Create the busboy object to parse the multipart data
  36. const busboy = new Busboy({
  37. headers: req.headers,
  38. limits: {
  39. fileSize: config.get('Upload.maxSize')
  40. }
  41. });
  42. req.body = {};
  43. busboy.on('field', (fieldName, value) => {
  44. req.body[fieldName] = value;
  45. });
  46. let fileReceived = false;
  47. busboy.on('file', async (fieldName, stream, name, encoding, mime) => {
  48. // Only process one file, discard everything after that
  49. if (fileReceived)
  50. return req.unpipe(busboy);
  51. fileReceived = true;
  52. // If a key was encountered and we are not authenticated, try to authenticate with it before the final check
  53. if (req.body.key && !status.authenticated)
  54. status = await authenticate(req, 'file.upload', status);
  55. // Finally, check if we have auth before preceeding, keys should have been processed by now
  56. if (!status.authenticated)
  57. return res.status(401).json({message: 'Unauthorized.'});
  58. if (!status.permission)
  59. return res.status(403).json({message: 'Forbidden.'});
  60. // Don't attach to the files object if there is no file
  61. if (!name) return stream.resume();
  62. // Generate an ID for the file, saving it with that filename
  63. const path = config.get('Upload.path');
  64. const newName = await identifier.generate();
  65. const finalPath = path + '/' + newName;
  66. // Set the file attributes
  67. file = {
  68. originalName: name,
  69. name: newName,
  70. path: finalPath,
  71. encoding: encoding,
  72. mime: mime
  73. };
  74. // Ensure the output directory exists
  75. await disk.mkdir(path);
  76. // Handle errors
  77. stream.on('error', err => done(err, {path: finalPath}));
  78. stream.on('limit', () => done('LIMIT_FILE_SIZE', {path: finalPath}));
  79. file.size = await disk.write(finalPath, stream);
  80. await done(null, {file: file});
  81. });
  82. busboy.on('error', err => done(err));
  83. busboy.on('finished', () => done(null, {file: file}));
  84. req.pipe(busboy);
  85. };
  86. module.exports = uploadMultipart;