storage.js 5.04 KB
Newer Older
1 2
const Model = require('objection').Model
const path = require('path')
3
const fs = require('fs-extra')
4
const _ = require('lodash')
5
const yaml = require('js-yaml')
6
const commonHelper = require('../helpers/common')
7 8 9 10 11 12 13 14

/* global WIKI */

/**
 * Storage model
 */
module.exports = class Storage extends Model {
  static get tableName() { return 'storage' }
15
  static get idColumn() { return 'key' }
16 17 18 19

  static get jsonSchema () {
    return {
      type: 'object',
20
      required: ['key', 'isEnabled'],
21 22 23 24

      properties: {
        key: {type: 'string'},
        isEnabled: {type: 'boolean'},
25
        mode: {type: 'string'}
26 27 28 29
      }
    }
  }

30
  static get jsonAttributes() {
31
    return ['config', 'state']
32 33
  }

34
  static async getTargets() {
35
    return WIKI.models.storage.query()
36 37 38
  }

  static async refreshTargetsFromDisk() {
39
    let trx
40
    try {
41
      const dbTargets = await WIKI.models.storage.query()
42 43 44 45 46 47 48 49

      // -> Fetch definitions from disk
      const storageDirs = await fs.readdir(path.join(WIKI.SERVERPATH, 'modules/storage'))
      let diskTargets = []
      for (let dir of storageDirs) {
        const def = await fs.readFile(path.join(WIKI.SERVERPATH, 'modules/storage', dir, 'definition.yml'), 'utf8')
        diskTargets.push(yaml.safeLoad(def))
      }
50 51
      WIKI.data.storage = diskTargets.map(target => ({
        ...target,
52
        isAvailable: _.get(target, 'isAvailable', false),
53
        props: commonHelper.parseModuleProps(target.props)
54
      }))
55 56

      // -> Insert new targets
57
      let newTargets = []
58
      for (let target of WIKI.data.storage) {
59 60 61 62
        if (!_.some(dbTargets, ['key', target.key])) {
          newTargets.push({
            key: target.key,
            isEnabled: false,
63
            mode: target.defaultMode || 'push',
Nick's avatar
Nick committed
64
            syncInterval: target.schedule || 'P0D',
65
            config: _.transform(target.props, (result, value, key) => {
66
              _.set(result, key, value.default)
67
              return result
Nick's avatar
Nick committed
68 69 70
            }, {}),
            state: {
              status: 'pending',
Nick's avatar
Nick committed
71 72
              message: '',
              lastAttempt: null
Nick's avatar
Nick committed
73
            }
74
          })
75 76 77 78 79 80 81 82 83 84
        } else {
          const targetConfig = _.get(_.find(dbTargets, ['key', target.key]), 'config', {})
          await WIKI.models.storage.query().patch({
            config: _.transform(target.props, (result, value, key) => {
              if (!_.has(result, key)) {
                _.set(result, key, value.default)
              }
              return result
            }, targetConfig)
          }).where('key', target.key)
85
        }
86
      }
87
      if (newTargets.length > 0) {
88 89 90 91 92
        trx = await WIKI.models.Objection.transaction.start(WIKI.models.knex)
        for (let target of newTargets) {
          await WIKI.models.storage.query(trx).insert(target)
        }
        await trx.commit()
93 94 95 96 97 98 99
        WIKI.logger.info(`Loaded ${newTargets.length} new storage targets: [ OK ]`)
      } else {
        WIKI.logger.info(`No new storage targets found: [ SKIPPED ]`)
      }
    } catch (err) {
      WIKI.logger.error(`Failed to scan or load new storage providers: [ FAILED ]`)
      WIKI.logger.error(err)
100 101 102
      if (trx) {
        trx.rollback()
      }
103 104
    }
  }
105

Nick's avatar
Nick committed
106 107 108
  /**
   * Initialize active storage targets
   */
Nick's avatar
Nick committed
109
  static async initTargets() {
Nick's avatar
Nick committed
110
    this.targets = await WIKI.models.storage.query().where('isEnabled', true).orderBy('key')
Nick's avatar
Nick committed
111
    try {
Nick's avatar
Nick committed
112 113 114 115 116 117 118 119 120
      // -> Stop and delete existing jobs
      const prevjobs = _.remove(WIKI.scheduler.jobs, job => job.name === `sync-storage`)
      if (prevjobs.length > 0) {
        prevjobs.forEach(job => job.stop())
      }

      // -> Initialize targets
      for(let target of this.targets) {
        const targetDef = _.find(WIKI.data.storage, ['key', target.key])
Nick's avatar
Nick committed
121
        target.fn = require(`../modules/storage/${target.key}/storage`)
122 123
        target.fn.config = target.config
        target.fn.mode = target.mode
Nick's avatar
Nick committed
124 125
        try {
          await target.fn.init()
Nick's avatar
Nick committed
126 127

          // -> Save succeeded init state
Nick's avatar
Nick committed
128 129 130
          await WIKI.models.storage.query().patch({
            state: {
              status: 'operational',
Nick's avatar
Nick committed
131 132
              message: '',
              lastAttempt: new Date().toISOString()
Nick's avatar
Nick committed
133 134
            }
          }).where('key', target.key)
Nick's avatar
Nick committed
135 136 137 138 139 140 141 142 143 144

          // -> Set recurring sync job
          if (targetDef.schedule && target.syncInterval !== `P0D`) {
            WIKI.scheduler.registerJob({
              name: `sync-storage`,
              immediate: false,
              schedule: target.syncInterval,
              repeat: true
            }, target.key)
          }
Nick's avatar
Nick committed
145
        } catch (err) {
Nick's avatar
Nick committed
146
          // -> Save initialization error
Nick's avatar
Nick committed
147 148 149
          await WIKI.models.storage.query().patch({
            state: {
              status: 'error',
Nick's avatar
Nick committed
150 151
              message: err.message,
              lastAttempt: new Date().toISOString()
Nick's avatar
Nick committed
152 153 154
            }
          }).where('key', target.key)
        }
Nick's avatar
Nick committed
155 156 157 158 159 160 161
      }
    } catch (err) {
      WIKI.logger.warn(err)
      throw err
    }
  }

162
  static async pageEvent({ event, page }) {
Nick's avatar
Nick committed
163
    try {
Nick's avatar
Nick committed
164
      for(let target of this.targets) {
165
        await target.fn[event](page)
Nick's avatar
Nick committed
166 167 168 169
      }
    } catch (err) {
      WIKI.logger.warn(err)
      throw err
170 171
    }
  }
172
}