uploads.js 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. "use strict";
  2. var express = require('express');
  3. var router = express.Router();
  4. var readChunk = require('read-chunk'),
  5. fileType = require('file-type'),
  6. Promise = require('bluebird'),
  7. fs = Promise.promisifyAll(require('fs-extra')),
  8. path = require('path'),
  9. _ = require('lodash');
  10. var validPathRe = new RegExp("^([a-z0-9\\/-]+\\.[a-z0-9]+)$");
  11. var validPathThumbsRe = new RegExp("^([0-9]+\\.png)$");
  12. // ==========================================
  13. // SERVE UPLOADS FILES
  14. // ==========================================
  15. router.get('/t/*', (req, res, next) => {
  16. let fileName = req.params[0];
  17. if(!validPathThumbsRe.test(fileName)) {
  18. return res.sendStatus(404).end();
  19. }
  20. //todo: Authentication-based access
  21. res.sendFile(fileName, {
  22. root: lcdata.getThumbsPath(),
  23. dotfiles: 'deny'
  24. }, (err) => {
  25. if (err) {
  26. res.status(err.status).end();
  27. }
  28. });
  29. });
  30. router.post('/img', lcdata.uploadImgHandler, (req, res, next) => {
  31. let destFolder = _.chain(req.body.folder).trim().toLower().value();
  32. upl.validateUploadsFolder(destFolder).then((destFolderPath) => {
  33. if(!destFolderPath) {
  34. res.json({ ok: false, msg: 'Invalid Folder' });
  35. return true;
  36. }
  37. Promise.map(req.files, (f) => {
  38. let destFilename = '';
  39. let destFilePath = '';
  40. return lcdata.validateUploadsFilename(f.originalname, destFolder, true).then((fname) => {
  41. destFilename = fname;
  42. destFilePath = path.resolve(destFolderPath, destFilename);
  43. return readChunk(f.path, 0, 262);
  44. }).then((buf) => {
  45. //-> Check MIME type by magic number
  46. let mimeInfo = fileType(buf);
  47. if(!_.includes(['image/png', 'image/jpeg', 'image/gif', 'image/webp'], mimeInfo.mime)) {
  48. return Promise.reject(new Error('Invalid file type.'));
  49. }
  50. return true;
  51. }).then(() => {
  52. //-> Move file to final destination
  53. return fs.moveAsync(f.path, destFilePath, { clobber: false });
  54. }).then(() => {
  55. return {
  56. ok: true,
  57. filename: destFilename,
  58. filesize: f.size
  59. };
  60. }).reflect();
  61. }, {concurrency: 3}).then((results) => {
  62. let uplResults = _.map(results, (r) => {
  63. if(r.isFulfilled()) {
  64. return r.value();
  65. } else {
  66. return {
  67. ok: false,
  68. msg: r.reason().message
  69. };
  70. }
  71. });
  72. res.json({ ok: true, results: uplResults });
  73. return true;
  74. }).catch((err) => {
  75. res.json({ ok: false, msg: err.message });
  76. return true;
  77. });
  78. });
  79. });
  80. router.post('/file', lcdata.uploadFileHandler, (req, res, next) => {
  81. let destFolder = _.chain(req.body.folder).trim().toLower().value();
  82. upl.validateUploadsFolder(destFolder).then((destFolderPath) => {
  83. if(!destFolderPath) {
  84. res.json({ ok: false, msg: 'Invalid Folder' });
  85. return true;
  86. }
  87. Promise.map(req.files, (f) => {
  88. let destFilename = '';
  89. let destFilePath = '';
  90. return lcdata.validateUploadsFilename(f.originalname, destFolder, false).then((fname) => {
  91. destFilename = fname;
  92. destFilePath = path.resolve(destFolderPath, destFilename);
  93. //-> Move file to final destination
  94. return fs.moveAsync(f.path, destFilePath, { clobber: false });
  95. }).then(() => {
  96. return {
  97. ok: true,
  98. filename: destFilename,
  99. filesize: f.size
  100. };
  101. }).reflect();
  102. }, {concurrency: 3}).then((results) => {
  103. let uplResults = _.map(results, (r) => {
  104. if(r.isFulfilled()) {
  105. return r.value();
  106. } else {
  107. return {
  108. ok: false,
  109. msg: r.reason().message
  110. };
  111. }
  112. });
  113. res.json({ ok: true, results: uplResults });
  114. return true;
  115. }).catch((err) => {
  116. res.json({ ok: false, msg: err.message });
  117. return true;
  118. });
  119. });
  120. });
  121. router.get('/*', (req, res, next) => {
  122. let fileName = req.params[0];
  123. if(!validPathRe.test(fileName)) {
  124. return res.sendStatus(404).end();
  125. }
  126. //todo: Authentication-based access
  127. res.sendFile(fileName, {
  128. root: git.getRepoPath() + '/uploads/',
  129. dotfiles: 'deny'
  130. }, (err) => {
  131. if (err) {
  132. res.status(err.status).end();
  133. }
  134. });
  135. });
  136. module.exports = router;