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.

162 lines
4.3 KiB

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