storage.js 6.22 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
const SSH2Promise = require('ssh2-promise')
const _ = require('lodash')
const path = require('path')
const stream = require('stream')
const Promise = require('bluebird')
const pipeline = Promise.promisify(stream.pipeline)
const pageHelper = require('../../../helpers/page.js')

/* global WIKI */

const getFilePath = (page, pathKey) => {
  const fileName = `${page[pathKey]}.${pageHelper.getFileExtension(page.contentType)}`
  const withLocaleCode = WIKI.config.lang.namespacing && WIKI.config.lang.code !== page.localeCode
  return withLocaleCode ? `${page.localeCode}/${fileName}` : fileName
}

module.exports = {
  client: null,
  sftp: null,
  async activated() {

  },
  async deactivated() {

  },
  async init() {
    WIKI.logger.info(`(STORAGE/SFTP) Initializing...`)
    this.client = new SSH2Promise({
      host: this.config.host,
      port: this.config.port || 22,
      username: this.config.username,
      password: (this.config.authMode === 'password') ? this.config.password : null,
      privateKey: (this.config.authMode === 'privateKey') ? this.config.privateKey : null,
      passphrase: (this.config.authMode === 'privateKey') ? this.config.passphrase : null
    })
    await this.client.connect()
    this.sftp = this.client.sftp()
    try {
      await this.sftp.readdir(this.config.basePath)
    } catch (err) {
      WIKI.logger.warn(`(STORAGE/SFTP) ${err.message}`)
      throw new Error(`Unable to read specified base directory: ${err.message}`)
    }
    WIKI.logger.info(`(STORAGE/SFTP) Initialization completed.`)
  },
  async created(page) {
    WIKI.logger.info(`(STORAGE/SFTP) Creating file ${page.path}...`)
    const filePath = getFilePath(page, 'path')
    await this.ensureDirectory(filePath)
    await this.sftp.writeFile(path.posix.join(this.config.basePath, filePath), page.injectMetadata())
  },
  async updated(page) {
    WIKI.logger.info(`(STORAGE/SFTP) Updating file ${page.path}...`)
    const filePath = getFilePath(page, 'path')
    await this.ensureDirectory(filePath)
    await this.sftp.writeFile(path.posix.join(this.config.basePath, filePath), page.injectMetadata())
  },
  async deleted(page) {
    WIKI.logger.info(`(STORAGE/SFTP) Deleting file ${page.path}...`)
    const filePath = getFilePath(page, 'path')
    await this.sftp.unlink(path.posix.join(this.config.basePath, filePath))
  },
  async renamed(page) {
    WIKI.logger.info(`(STORAGE/SFTP) Renaming file ${page.path} to ${page.destinationPath}...`)
    let sourceFilePath = getFilePath(page, 'path')
    let destinationFilePath = getFilePath(page, 'destinationPath')
    if (WIKI.config.lang.namespacing) {
      if (WIKI.config.lang.code !== page.localeCode) {
        sourceFilePath = `${page.localeCode}/${sourceFilePath}`
      }
      if (WIKI.config.lang.code !== page.destinationLocaleCode) {
        destinationFilePath = `${page.destinationLocaleCode}/${destinationFilePath}`
      }
    }
    await this.ensureDirectory(destinationFilePath)
    await this.sftp.rename(path.posix.join(this.config.basePath, sourceFilePath), path.posix.join(this.config.basePath, destinationFilePath))
  },
  /**
   * ASSET UPLOAD
   *
   * @param {Object} asset Asset to upload
   */
  async assetUploaded (asset) {
    WIKI.logger.info(`(STORAGE/SFTP) Creating new file ${asset.path}...`)
    await this.ensureDirectory(asset.path)
    await this.sftp.writeFile(path.posix.join(this.config.basePath, asset.path), asset.data)
  },
  /**
   * ASSET DELETE
   *
   * @param {Object} asset Asset to delete
   */
  async assetDeleted (asset) {
    WIKI.logger.info(`(STORAGE/SFTP) Deleting file ${asset.path}...`)
    await this.sftp.unlink(path.posix.join(this.config.basePath, asset.path))
  },
  /**
   * ASSET RENAME
   *
   * @param {Object} asset Asset to rename
   */
  async assetRenamed (asset) {
    WIKI.logger.info(`(STORAGE/SFTP) Renaming file from ${asset.path} to ${asset.destinationPath}...`)
    await this.ensureDirectory(asset.destinationPath)
    await this.sftp.rename(path.posix.join(this.config.basePath, asset.path), path.posix.join(this.config.basePath, asset.destinationPath))
106 107 108
  },
  async getLocalLocation () {

109 110 111 112 113 114 115 116 117
  },
  /**
   * HANDLERS
   */
  async exportAll() {
    WIKI.logger.info(`(STORAGE/SFTP) Exporting all content to the remote server...`)

    // -> Pages
    await pipeline(
118
      WIKI.models.knex.column('path', 'localeCode', 'title', 'description', 'contentType', 'content', 'isPublished', 'updatedAt', 'createdAt').select().from('pages').where({
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
        isPrivate: false
      }).stream(),
      new stream.Transform({
        objectMode: true,
        transform: async (page, enc, cb) => {
          const filePath = getFilePath(page, 'path')
          WIKI.logger.info(`(STORAGE/SFTP) Adding page ${filePath}...`)
          await this.ensureDirectory(filePath)
          await this.sftp.writeFile(path.posix.join(this.config.basePath, filePath), pageHelper.injectPageMetadata(page))
          cb()
        }
      })
    )

    // -> Assets
    const assetFolders = await WIKI.models.assetFolders.getAllPaths()

    await pipeline(
      WIKI.models.knex.column('filename', 'folderId', 'data').select().from('assets').join('assetData', 'assets.id', '=', 'assetData.id').stream(),
      new stream.Transform({
        objectMode: true,
        transform: async (asset, enc, cb) => {
          const filename = (asset.folderId && asset.folderId > 0) ? `${_.get(assetFolders, asset.folderId)}/${asset.filename}` : asset.filename
          WIKI.logger.info(`(STORAGE/SFTP) Adding asset ${filename}...`)
          await this.ensureDirectory(filename)
          await this.sftp.writeFile(path.posix.join(this.config.basePath, filename), asset.data)
          cb()
        }
      })
    )

    WIKI.logger.info('(STORAGE/SFTP) All content has been pushed to the remote server.')
  },
  async ensureDirectory(filePath) {
    if (filePath.indexOf('/') >= 0) {
      try {
        const folderPaths = _.dropRight(filePath.split('/'))
        for (let i = 1; i <= folderPaths.length; i++) {
          const folderSection = _.take(folderPaths, i).join('/')
158 159 160 161 162 163
          const folderDir = path.posix.join(this.config.basePath, folderSection)
          try {
            await this.sftp.readdir(folderDir)
          } catch (err) {
            await this.sftp.mkdir(folderDir)
          }
164 165 166 167 168
        }
      } catch (err) {}
    }
  }
}