uploads.js 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126
  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. return res.json({ ok: false, msg: 'Invalid Folder' });
  35. }
  36. Promise.map(req.files, (f) => {
  37. let destFilename = '';
  38. let destFilePath = '';
  39. return lcdata.validateUploadsFilename(f.originalname, destFolder).then((fname) => {
  40. destFilename = fname;
  41. destFilePath = path.resolve(destFolderPath, destFilename);
  42. return readChunk(f.path, 0, 262);
  43. }).then((buf) => {
  44. //-> Check MIME type by magic number
  45. let mimeInfo = fileType(buf);
  46. if(!_.includes(['image/png', 'image/jpeg', 'image/gif', 'image/webp'], mimeInfo.mime)) {
  47. return Promise.reject(new Error('Invalid file type.'));
  48. }
  49. return true;
  50. }).then(() => {
  51. //-> Move file to final destination
  52. return fs.moveAsync(f.path, destFilePath, { clobber: false });
  53. }).then(() => {
  54. return {
  55. ok: true,
  56. filename: destFilename,
  57. filesize: f.size
  58. };
  59. }).reflect();
  60. }, {concurrency: 3}).then((results) => {
  61. let uplResults = _.map(results, (r) => {
  62. if(r.isFulfilled()) {
  63. return r.value();
  64. } else {
  65. return {
  66. ok: false,
  67. msg: r.reason().message
  68. }
  69. }
  70. });
  71. res.json({ ok: true, results: uplResults });
  72. }).catch((err) => {
  73. res.json({ ok: false, msg: err.message });
  74. });
  75. });
  76. });
  77. router.get('/*', (req, res, next) => {
  78. let fileName = req.params[0];
  79. if(!validPathRe.test(fileName)) {
  80. return res.sendStatus(404).end();
  81. }
  82. //todo: Authentication-based access
  83. res.sendFile(fileName, {
  84. root: git.getRepoPath() + '/uploads/',
  85. dotfiles: 'deny'
  86. }, (err) => {
  87. if (err) {
  88. res.status(err.status).end();
  89. }
  90. });
  91. });
  92. module.exports = router;