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. 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.ensureDirSync(path.resolve(ROOTPATH, appconfig.paths.data, './cache'))
  85. fs.emptyDirSync(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'), '644')
  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'), '644')
  95. }
  96. } catch (err) {
  97. winston.error(err)
  98. }
  99. winston.info('[SERVER.Local] Data and Repository directories are OK.')
  100. return
  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(/[^a-z0-9-]+/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('File ' + f + ' already exists.')
  137. }).catch((err) => {
  138. if (err.code === 'ENOENT') {
  139. return f
  140. }
  141. throw err
  142. })
  143. }
  144. }