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.

172 lines
5.9 KiB

  1. const fs = require('fs-extra')
  2. const path = require('path')
  3. const stream = require('stream')
  4. const Promise = require('bluebird')
  5. const pipeline = Promise.promisify(stream.pipeline)
  6. const klaw = require('klaw')
  7. const mime = require('mime-types').lookup
  8. const _ = require('lodash')
  9. const pageHelper = require('../../../helpers/page.js')
  10. /* global WIKI */
  11. module.exports = {
  12. assetFolders: null,
  13. async importFromDisk ({ fullPath, moduleName }) {
  14. const rootUser = await WIKI.models.users.getRootUser()
  15. await pipeline(
  16. klaw(fullPath, {
  17. filter: (f) => {
  18. return !_.includes(f, '.git')
  19. }
  20. }),
  21. new stream.Transform({
  22. objectMode: true,
  23. transform: async (file, enc, cb) => {
  24. const relPath = file.path.substr(fullPath.length + 1)
  25. if (file.stats.size < 1) {
  26. // Skip directories and zero-byte files
  27. return cb()
  28. } else if (relPath && relPath.length > 3) {
  29. WIKI.logger.info(`(STORAGE/${moduleName}) Processing ${relPath}...`)
  30. const contentType = pageHelper.getContentType(relPath)
  31. if (contentType) {
  32. // -> Page
  33. try {
  34. await this.processPage({
  35. user: rootUser,
  36. relPath: relPath,
  37. fullPath: fullPath,
  38. contentType: contentType,
  39. moduleName: moduleName
  40. })
  41. } catch (err) {
  42. WIKI.logger.warn(`(STORAGE/${moduleName}) Failed to process page ${relPath}`)
  43. WIKI.logger.warn(err)
  44. }
  45. } else {
  46. // -> Asset
  47. try {
  48. await this.processAsset({
  49. user: rootUser,
  50. relPath: relPath,
  51. file: file,
  52. contentType: contentType,
  53. moduleName: moduleName
  54. })
  55. } catch (err) {
  56. WIKI.logger.warn(`(STORAGE/${moduleName}) Failed to process asset ${relPath}`)
  57. WIKI.logger.warn(err)
  58. }
  59. }
  60. }
  61. cb()
  62. }
  63. })
  64. )
  65. this.clearFolderCache()
  66. },
  67. async processPage ({ user, fullPath, relPath, contentType, moduleName }) {
  68. const normalizedRelPath = relPath.replace(/\\/g, '/')
  69. const contentPath = pageHelper.getPagePath(normalizedRelPath)
  70. const itemContents = await fs.readFile(path.join(fullPath, relPath), 'utf8')
  71. const pageData = WIKI.models.pages.parseMetadata(itemContents, contentType)
  72. const currentPage = await WIKI.models.pages.getPageFromDb({
  73. path: contentPath.path,
  74. locale: contentPath.locale
  75. })
  76. const newTags = !_.isNil(pageData.tags) ? _.get(pageData, 'tags', '').split(', ') : false
  77. if (currentPage) {
  78. // Already in the DB, can mark as modified
  79. WIKI.logger.info(`(STORAGE/${moduleName}) Page marked as modified: ${normalizedRelPath}`)
  80. await WIKI.models.pages.updatePage({
  81. id: currentPage.id,
  82. title: _.get(pageData, 'title', currentPage.title),
  83. description: _.get(pageData, 'description', currentPage.description) || '',
  84. tags: newTags || currentPage.tags.map(t => t.tag),
  85. isPublished: _.get(pageData, 'isPublished', currentPage.isPublished),
  86. isPrivate: false,
  87. content: pageData.content,
  88. user: user,
  89. skipStorage: true
  90. })
  91. } else {
  92. // Not in the DB, can mark as new
  93. WIKI.logger.info(`(STORAGE/${moduleName}) Page marked as new: ${normalizedRelPath}`)
  94. const pageEditor = await WIKI.models.editors.getDefaultEditor(contentType)
  95. await WIKI.models.pages.createPage({
  96. path: contentPath.path,
  97. locale: contentPath.locale,
  98. title: _.get(pageData, 'title', _.last(contentPath.path.split('/'))),
  99. description: _.get(pageData, 'description', '') || '',
  100. tags: newTags || [],
  101. isPublished: _.get(pageData, 'isPublished', true),
  102. isPrivate: false,
  103. content: pageData.content,
  104. user: user,
  105. editor: pageEditor,
  106. skipStorage: true
  107. })
  108. }
  109. },
  110. async processAsset ({ user, relPath, file, moduleName }) {
  111. WIKI.logger.info(`(STORAGE/${moduleName}) Asset marked for import: ${relPath}`)
  112. // -> Get all folder paths
  113. if (!this.assetFolders) {
  114. this.assetFolders = await WIKI.models.assetFolders.getAllPaths()
  115. }
  116. // -> Find existing folder
  117. const filePathInfo = path.parse(file.path)
  118. const folderPath = path.dirname(relPath).replace(/\\/g, '/')
  119. let folderId = _.toInteger(_.findKey(this.assetFolders, fld => { return fld === folderPath })) || null
  120. // -> Create missing folder structure
  121. if (!folderId && folderPath !== '.') {
  122. const folderParts = folderPath.split('/')
  123. let currentFolderPath = []
  124. let currentFolderParentId = null
  125. for (const folderPart of folderParts) {
  126. currentFolderPath.push(folderPart)
  127. const existingFolderId = _.findKey(this.assetFolders, fld => { return fld === currentFolderPath.join('/') })
  128. if (!existingFolderId) {
  129. const newFolderObj = await WIKI.models.assetFolders.query().insert({
  130. slug: folderPart,
  131. name: folderPart,
  132. parentId: currentFolderParentId
  133. })
  134. _.set(this.assetFolders, newFolderObj.id, currentFolderPath.join('/'))
  135. currentFolderParentId = newFolderObj.id
  136. } else {
  137. currentFolderParentId = _.toInteger(existingFolderId)
  138. }
  139. }
  140. folderId = currentFolderParentId
  141. }
  142. // -> Import asset
  143. await WIKI.models.assets.upload({
  144. mode: 'import',
  145. originalname: filePathInfo.base,
  146. ext: filePathInfo.ext,
  147. mimetype: mime(filePathInfo.base) || 'application/octet-stream',
  148. size: file.stats.size,
  149. folderId: folderId,
  150. path: file.path,
  151. assetPath: relPath,
  152. user: user,
  153. skipStorage: true
  154. })
  155. },
  156. clearFolderCache () {
  157. this.assetFolders = null
  158. }
  159. }