common.js 5.9 KB

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