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.

175 lines
4.5 KiB

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