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.

177 lines
4.6 KiB

  1. 'use strict'
  2. /* global lang, winston */
  3. const path = require('path')
  4. const Promise = require('bluebird')
  5. const fs = Promise.promisifyAll(require('fs-extra'))
  6. const multer = require('multer')
  7. const os = require('os')
  8. const _ = require('lodash')
  9. /**
  10. * Local Data Storage
  11. */
  12. module.exports = {
  13. _uploadsPath: './repo/uploads',
  14. _uploadsThumbsPath: './data/thumbs',
  15. uploadImgHandler: null,
  16. /**
  17. * Initialize Local Data Storage model
  18. *
  19. * @return {Object} Local Data Storage model instance
  20. */
  21. init () {
  22. this._uploadsPath = path.resolve(ROOTPATH, appconfig.paths.repo, 'uploads')
  23. this._uploadsThumbsPath = path.resolve(ROOTPATH, appconfig.paths.data, 'thumbs')
  24. this.createBaseDirectories(appconfig)
  25. this.initMulter(appconfig)
  26. return this
  27. },
  28. /**
  29. * Init Multer upload handlers
  30. *
  31. * @param {Object} appconfig The application config
  32. * @return {boolean} Void
  33. */
  34. initMulter (appconfig) {
  35. let maxFileSizes = {
  36. img: appconfig.uploads.maxImageFileSize * 1024 * 1024,
  37. file: appconfig.uploads.maxOtherFileSize * 1024 * 1024
  38. }
  39. // -> IMAGES
  40. this.uploadImgHandler = multer({
  41. storage: multer.diskStorage({
  42. destination: (req, f, cb) => {
  43. cb(null, path.resolve(ROOTPATH, appconfig.paths.data, 'temp-upload'))
  44. }
  45. }),
  46. fileFilter: (req, f, cb) => {
  47. // -> Check filesize
  48. if (f.size > maxFileSizes.img) {
  49. return cb(null, false)
  50. }
  51. // -> Check MIME type (quick check only)
  52. if (!_.includes(['image/png', 'image/jpeg', 'image/gif', 'image/webp'], f.mimetype)) {
  53. return cb(null, false)
  54. }
  55. cb(null, true)
  56. }
  57. }).array('imgfile', 20)
  58. // -> FILES
  59. this.uploadFileHandler = multer({
  60. storage: multer.diskStorage({
  61. destination: (req, f, cb) => {
  62. cb(null, path.resolve(ROOTPATH, appconfig.paths.data, 'temp-upload'))
  63. }
  64. }),
  65. fileFilter: (req, f, cb) => {
  66. // -> Check filesize
  67. if (f.size > maxFileSizes.file) {
  68. return cb(null, false)
  69. }
  70. cb(null, true)
  71. }
  72. }).array('binfile', 20)
  73. return true
  74. },
  75. /**
  76. * Creates a base directories (Synchronous).
  77. *
  78. * @param {Object} appconfig The application config
  79. * @return {Void} Void
  80. */
  81. createBaseDirectories (appconfig) {
  82. winston.info('Checking data directories...')
  83. try {
  84. fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.paths.data))
  85. fs.emptyDirSync(path.resolve(ROOTPATH, appconfig.paths.data))
  86. fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.paths.data, './cache'))
  87. fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.paths.data, './thumbs'))
  88. fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.paths.data, './temp-upload'))
  89. if (os.type() !== 'Windows_NT') {
  90. fs.chmodSync(path.resolve(ROOTPATH, appconfig.paths.data, './temp-upload'), '755')
  91. }
  92. fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.paths.repo))
  93. fs.ensureDirSync(path.resolve(ROOTPATH, appconfig.paths.repo, './uploads'))
  94. if (os.type() !== 'Windows_NT') {
  95. fs.chmodSync(path.resolve(ROOTPATH, appconfig.paths.repo, './uploads'), '755')
  96. }
  97. } catch (err) {
  98. winston.error(err)
  99. }
  100. winston.info('Data and Repository directories are OK.')
  101. },
  102. /**
  103. * Gets the uploads path.
  104. *
  105. * @return {String} The uploads path.
  106. */
  107. getUploadsPath () {
  108. return this._uploadsPath
  109. },
  110. /**
  111. * Gets the thumbnails folder path.
  112. *
  113. * @return {String} The thumbs path.
  114. */
  115. getThumbsPath () {
  116. return this._uploadsThumbsPath
  117. },
  118. /**
  119. * Check if filename is valid and unique
  120. *
  121. * @param {String} f The filename
  122. * @param {String} fld The containing folder
  123. * @param {boolean} isImage Indicates if image
  124. * @return {Promise<String>} Promise of the accepted filename
  125. */
  126. validateUploadsFilename (f, fld, isImage) {
  127. let fObj = path.parse(f)
  128. let fname = _.chain(fObj.name).trim().toLower().kebabCase().value().replace(new RegExp('[^a-z0-9-' + appdata.regex.cjk + appdata.regex.arabic + ']', 'g'), '')
  129. let fext = _.toLower(fObj.ext)
  130. if (isImage && !_.includes(['.jpg', '.jpeg', '.png', '.gif', '.webp'], fext)) {
  131. fext = '.png'
  132. }
  133. f = fname + fext
  134. let fpath = path.resolve(this._uploadsPath, fld, f)
  135. return fs.statAsync(fpath).then((s) => {
  136. throw new Error(lang.t('errors:fileexists', { path: f }))
  137. }).catch((err) => {
  138. if (err.code === 'ENOENT') {
  139. return f
  140. }
  141. throw err
  142. })
  143. }
  144. }