You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

128 lines
2.7 KiB

  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).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.get('/*', (req, res, next) => {
  81. let fileName = req.params[0];
  82. if(!validPathRe.test(fileName)) {
  83. return res.sendStatus(404).end();
  84. }
  85. //todo: Authentication-based access
  86. res.sendFile(fileName, {
  87. root: git.getRepoPath() + '/uploads/',
  88. dotfiles: 'deny'
  89. }, (err) => {
  90. if (err) {
  91. res.status(err.status).end();
  92. }
  93. });
  94. });
  95. module.exports = router;