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.

168 lines
6.0 KiB

5 years ago
5 years ago
5 years ago
5 years ago
  1. const S3 = require('aws-sdk/clients/s3')
  2. const stream = require('stream')
  3. const Promise = require('bluebird')
  4. const pipeline = Promise.promisify(stream.pipeline)
  5. const _ = require('lodash')
  6. const pageHelper = require('../../../helpers/page.js')
  7. /* global WIKI */
  8. /**
  9. * Deduce the file path given the `page` object and the object's key to the page's path.
  10. */
  11. const getFilePath = (page, pathKey) => {
  12. const fileName = `${page[pathKey]}.${pageHelper.getFileExtension(page.contentType)}`
  13. const withLocaleCode = WIKI.config.lang.namespacing && WIKI.config.lang.code !== page.localeCode
  14. return withLocaleCode ? `${page.localeCode}/${fileName}` : fileName
  15. }
  16. /**
  17. * Can be used with S3 compatible storage.
  18. */
  19. module.exports = class S3CompatibleStorage {
  20. constructor(storageName) {
  21. this.storageName = storageName
  22. this.bucketName = ""
  23. }
  24. async activated() {
  25. // not used
  26. }
  27. async deactivated() {
  28. // not used
  29. }
  30. async init() {
  31. WIKI.logger.info(`(STORAGE/${this.storageName}) Initializing...`)
  32. const { accessKeyId, secretAccessKey, bucket } = this.config
  33. const s3Config = {
  34. accessKeyId,
  35. secretAccessKey,
  36. params: { Bucket: bucket },
  37. apiVersions: '2006-03-01'
  38. }
  39. if (!_.isNil(this.config.region)) {
  40. s3Config.region = this.config.region
  41. }
  42. if (!_.isNil(this.config.endpoint)) {
  43. s3Config.endpoint = this.config.endpoint
  44. }
  45. if (!_.isNil(this.config.sslEnabled)) {
  46. s3Config.sslEnabled = this.config.sslEnabled
  47. }
  48. if (!_.isNil(this.config.s3ForcePathStyle)) {
  49. s3Config.s3ForcePathStyle = this.config.s3ForcePathStyle
  50. }
  51. if (!_.isNil(this.config.s3BucketEndpoint)) {
  52. s3Config.s3BucketEndpoint = this.config.s3BucketEndpoint
  53. }
  54. this.s3 = new S3(s3Config)
  55. this.bucketName = bucket
  56. // determine if a bucket exists and you have permission to access it
  57. await this.s3.headBucket().promise()
  58. WIKI.logger.info(`(STORAGE/${this.storageName}) Initialization completed.`)
  59. }
  60. async created(page) {
  61. WIKI.logger.info(`(STORAGE/${this.storageName}) Creating file ${page.path}...`)
  62. const filePath = getFilePath(page, 'path')
  63. await this.s3.putObject({ Key: filePath, Body: page.injectMetadata() }).promise()
  64. }
  65. async updated(page) {
  66. WIKI.logger.info(`(STORAGE/${this.storageName}) Updating file ${page.path}...`)
  67. const filePath = getFilePath(page, 'path')
  68. await this.s3.putObject({ Key: filePath, Body: page.injectMetadata() }).promise()
  69. }
  70. async deleted(page) {
  71. WIKI.logger.info(`(STORAGE/${this.storageName}) Deleting file ${page.path}...`)
  72. const filePath = getFilePath(page, 'path')
  73. await this.s3.deleteObject({ Key: filePath }).promise()
  74. }
  75. async renamed(page) {
  76. WIKI.logger.info(`(STORAGE/${this.storageName}) Renaming file ${page.path} to ${page.destinationPath}...`)
  77. let sourceFilePath = getFilePath(page, 'path')
  78. let destinationFilePath = getFilePath(page, 'destinationPath')
  79. if (WIKI.config.lang.namespacing) {
  80. if (WIKI.config.lang.code !== page.localeCode) {
  81. sourceFilePath = `${page.localeCode}/${sourceFilePath}`
  82. }
  83. if (WIKI.config.lang.code !== page.destinationLocaleCode) {
  84. destinationFilePath = `${page.destinationLocaleCode}/${destinationFilePath}`
  85. }
  86. }
  87. await this.s3.copyObject({ CopySource: `${this.bucketName}/${sourceFilePath}`, Key: destinationFilePath }).promise()
  88. await this.s3.deleteObject({ Key: sourceFilePath }).promise()
  89. }
  90. /**
  91. * ASSET UPLOAD
  92. *
  93. * @param {Object} asset Asset to upload
  94. */
  95. async assetUploaded (asset) {
  96. WIKI.logger.info(`(STORAGE/${this.storageName}) Creating new file ${asset.path}...`)
  97. await this.s3.putObject({ Key: asset.path, Body: asset.data }).promise()
  98. }
  99. /**
  100. * ASSET DELETE
  101. *
  102. * @param {Object} asset Asset to delete
  103. */
  104. async assetDeleted (asset) {
  105. WIKI.logger.info(`(STORAGE/${this.storageName}) Deleting file ${asset.path}...`)
  106. await this.s3.deleteObject({ Key: asset.path }).promise()
  107. }
  108. /**
  109. * ASSET RENAME
  110. *
  111. * @param {Object} asset Asset to rename
  112. */
  113. async assetRenamed (asset) {
  114. WIKI.logger.info(`(STORAGE/${this.storageName}) Renaming file from ${asset.path} to ${asset.destinationPath}...`)
  115. await this.s3.copyObject({ CopySource: `${this.bucketName}/${asset.path}`, Key: asset.destinationPath }).promise()
  116. await this.s3.deleteObject({ Key: asset.path }).promise()
  117. }
  118. async getLocalLocation () {
  119. }
  120. /**
  121. * HANDLERS
  122. */
  123. async exportAll() {
  124. WIKI.logger.info(`(STORAGE/${this.storageName}) Exporting all content to the cloud provider...`)
  125. // -> Pages
  126. await pipeline(
  127. WIKI.models.knex.column('path', 'localeCode', 'title', 'description', 'contentType', 'content', 'isPublished', 'updatedAt', 'createdAt').select().from('pages').where({
  128. isPrivate: false
  129. }).stream(),
  130. new stream.Transform({
  131. objectMode: true,
  132. transform: async (page, enc, cb) => {
  133. const filePath = getFilePath(page, 'path')
  134. WIKI.logger.info(`(STORAGE/${this.storageName}) Adding page ${filePath}...`)
  135. await this.s3.putObject({ Key: filePath, Body: pageHelper.injectPageMetadata(page) }).promise()
  136. cb()
  137. }
  138. })
  139. )
  140. // -> Assets
  141. const assetFolders = await WIKI.models.assetFolders.getAllPaths()
  142. await pipeline(
  143. WIKI.models.knex.column('filename', 'folderId', 'data').select().from('assets').join('assetData', 'assets.id', '=', 'assetData.id').stream(),
  144. new stream.Transform({
  145. objectMode: true,
  146. transform: async (asset, enc, cb) => {
  147. const filename = (asset.folderId && asset.folderId > 0) ? `${_.get(assetFolders, asset.folderId)}/${asset.filename}` : asset.filename
  148. WIKI.logger.info(`(STORAGE/${this.storageName}) Adding asset ${filename}...`)
  149. await this.s3.putObject({ Key: filename, Body: asset.data }).promise()
  150. cb()
  151. }
  152. })
  153. )
  154. WIKI.logger.info(`(STORAGE/${this.storageName}) All content has been pushed to the cloud provider.`)
  155. }
  156. }