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.

164 lines
6.2 KiB

  1. const { BlobServiceClient, StorageSharedKeyCredential } = require('@azure/storage-blob')
  2. const stream = require('stream')
  3. const Promise = require('bluebird')
  4. const pipeline = Promise.promisify(stream.pipeline)
  5. const pageHelper = require('../../../helpers/page.js')
  6. const _ = require('lodash')
  7. /* global WIKI */
  8. const getFilePath = (page, pathKey) => {
  9. const fileName = `${page[pathKey]}.${pageHelper.getFileExtension(page.contentType)}`
  10. const withLocaleCode = WIKI.config.lang.namespacing && WIKI.config.lang.code !== page.localeCode
  11. return withLocaleCode ? `${page.localeCode}/${fileName}` : fileName
  12. }
  13. module.exports = {
  14. async activated() {
  15. },
  16. async deactivated() {
  17. },
  18. async init() {
  19. WIKI.logger.info(`(STORAGE/AZURE) Initializing...`)
  20. const { accountName, accountKey, containerName } = this.config
  21. this.client = new BlobServiceClient(
  22. `https://${accountName}.blob.core.windows.net`,
  23. new StorageSharedKeyCredential(accountName, accountKey)
  24. )
  25. this.container = this.client.getContainerClient(containerName)
  26. try {
  27. await this.container.create()
  28. } catch (err) {
  29. if (err.statusCode !== 409) {
  30. WIKI.logger.warn(err)
  31. throw err
  32. }
  33. }
  34. WIKI.logger.info(`(STORAGE/AZURE) Initialization completed.`)
  35. },
  36. async created (page) {
  37. WIKI.logger.info(`(STORAGE/AZURE) Creating file ${page.path}...`)
  38. const filePath = getFilePath(page, 'path')
  39. const pageContent = page.injectMetadata()
  40. const blockBlobClient = this.container.getBlockBlobClient(filePath)
  41. await blockBlobClient.upload(pageContent, pageContent.length, { tier: this.config.storageTier })
  42. },
  43. async updated (page) {
  44. WIKI.logger.info(`(STORAGE/AZURE) Updating file ${page.path}...`)
  45. const filePath = getFilePath(page, 'path')
  46. const pageContent = page.injectMetadata()
  47. const blockBlobClient = this.container.getBlockBlobClient(filePath)
  48. await blockBlobClient.upload(pageContent, pageContent.length, { tier: this.config.storageTier })
  49. },
  50. async deleted (page) {
  51. WIKI.logger.info(`(STORAGE/AZURE) Deleting file ${page.path}...`)
  52. const filePath = getFilePath(page, 'path')
  53. const blockBlobClient = this.container.getBlockBlobClient(filePath)
  54. await blockBlobClient.delete({
  55. deleteSnapshots: 'include'
  56. })
  57. },
  58. async renamed(page) {
  59. WIKI.logger.info(`(STORAGE/${this.storageName}) Renaming file ${page.path} to ${page.destinationPath}...`)
  60. let sourceFilePath = getFilePath(page, 'path')
  61. let destinationFilePath = getFilePath(page, 'destinationPath')
  62. if (WIKI.config.lang.namespacing) {
  63. if (WIKI.config.lang.code !== page.localeCode) {
  64. sourceFilePath = `${page.localeCode}/${sourceFilePath}`
  65. }
  66. if (WIKI.config.lang.code !== page.destinationLocaleCode) {
  67. destinationFilePath = `${page.destinationLocaleCode}/${destinationFilePath}`
  68. }
  69. }
  70. const sourceBlockBlobClient = this.container.getBlockBlobClient(sourceFilePath)
  71. const destBlockBlobClient = this.container.getBlockBlobClient(destinationFilePath)
  72. await destBlockBlobClient.syncCopyFromURL(sourceBlockBlobClient.url)
  73. await sourceBlockBlobClient.delete({
  74. deleteSnapshots: 'include'
  75. })
  76. },
  77. /**
  78. * ASSET UPLOAD
  79. *
  80. * @param {Object} asset Asset to upload
  81. */
  82. async assetUploaded (asset) {
  83. WIKI.logger.info(`(STORAGE/AZURE) Creating new file ${asset.path}...`)
  84. const blockBlobClient = this.container.getBlockBlobClient(asset.path)
  85. await blockBlobClient.upload(asset.data, asset.data.length, { tier: this.config.storageTier })
  86. },
  87. /**
  88. * ASSET DELETE
  89. *
  90. * @param {Object} asset Asset to delete
  91. */
  92. async assetDeleted (asset) {
  93. WIKI.logger.info(`(STORAGE/AZURE) Deleting file ${asset.path}...`)
  94. const blockBlobClient = this.container.getBlockBlobClient(asset.path)
  95. await blockBlobClient.delete({
  96. deleteSnapshots: 'include'
  97. })
  98. },
  99. /**
  100. * ASSET RENAME
  101. *
  102. * @param {Object} asset Asset to rename
  103. */
  104. async assetRenamed (asset) {
  105. WIKI.logger.info(`(STORAGE/AZURE) Renaming file from ${asset.path} to ${asset.destinationPath}...`)
  106. const sourceBlockBlobClient = this.container.getBlockBlobClient(asset.path)
  107. const destBlockBlobClient = this.container.getBlockBlobClient(asset.destinationPath)
  108. await destBlockBlobClient.syncCopyFromURL(sourceBlockBlobClient.url)
  109. await sourceBlockBlobClient.delete({
  110. deleteSnapshots: 'include'
  111. })
  112. },
  113. async getLocalLocation () {
  114. },
  115. /**
  116. * HANDLERS
  117. */
  118. async exportAll() {
  119. WIKI.logger.info(`(STORAGE/AZURE) Exporting all content to Azure Blob Storage...`)
  120. // -> Pages
  121. await pipeline(
  122. WIKI.models.knex.column('path', 'localeCode', 'title', 'description', 'contentType', 'content', 'isPublished', 'updatedAt', 'createdAt').select().from('pages').where({
  123. isPrivate: false
  124. }).stream(),
  125. new stream.Transform({
  126. objectMode: true,
  127. transform: async (page, enc, cb) => {
  128. const filePath = getFilePath(page, 'path')
  129. WIKI.logger.info(`(STORAGE/AZURE) Adding page ${filePath}...`)
  130. const pageContent = pageHelper.injectPageMetadata(page)
  131. const blockBlobClient = this.container.getBlockBlobClient(filePath)
  132. await blockBlobClient.upload(pageContent, pageContent.length, { tier: this.config.storageTier })
  133. cb()
  134. }
  135. })
  136. )
  137. // -> Assets
  138. const assetFolders = await WIKI.models.assetFolders.getAllPaths()
  139. await pipeline(
  140. WIKI.models.knex.column('filename', 'folderId', 'data').select().from('assets').join('assetData', 'assets.id', '=', 'assetData.id').stream(),
  141. new stream.Transform({
  142. objectMode: true,
  143. transform: async (asset, enc, cb) => {
  144. const filename = (asset.folderId && asset.folderId > 0) ? `${_.get(assetFolders, asset.folderId)}/${asset.filename}` : asset.filename
  145. WIKI.logger.info(`(STORAGE/AZURE) Adding asset ${filename}...`)
  146. const blockBlobClient = this.container.getBlockBlobClient(filename)
  147. await blockBlobClient.upload(asset.data, asset.data.length, { tier: this.config.storageTier })
  148. cb()
  149. }
  150. })
  151. )
  152. WIKI.logger.info('(STORAGE/AZURE) All content has been pushed to Azure Blob Storage.')
  153. }
  154. }