All files / lib/service config.ts

54.05% Statements 20/37
25% Branches 3/12
87.5% Functions 7/8
57.14% Lines 20/35

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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          16x                             16x 16x   16x             16x       16x 16x 16x         18x     18x 1x   18x 17x         71x       2x 2x           2x 2x       16x   16x                                                        
import Debug from 'debug'
import { v4 as uuid } from 'uuid/v4'
import redis from 'redis'
import Crowi from 'server/crowi'
 
const debug = Debug('crowi:service:config')
 
export default class Config {
  crowi: Crowi
 
  config: any
 
  pubSub: {
    id: uuid
    publisher: redis.RedisClient | null
    subscriber: redis.RedisClient | null
    channel: string
  }
 
  constructor(crowi: Crowi) {
    this.crowi = crowi
    this.config = {}
 
    this.pubSub = {
      id: uuid(),
      publisher: null,
      subscriber: null,
      channel: 'config',
    }
 
    this.setupPubSub()
  }
 
  async load() {
    const Config = this.crowi.model('Config')
    const config = await Config.loadAllConfig()
    this.set(config)
  }
 
  set(config) {
    // FIXME: Deep copy to avoid deleting itself.
    config = JSON.parse(JSON.stringify(config))
    // FIXME: Treat as a mutable object always.
    //        We should always get config using `crowi.getConfig()` *just before* referencing config.
    for (const key of Object.keys(this.config)) {
      delete this.config[key]
    }
    for (const key of Object.keys(config)) {
      this.config[key] = config[key]
    }
  }
 
  get() {
    return this.config || {}
  }
 
  notifyUpdated() {
    const { publisher, channel, id } = this.pubSub
    Iif (publisher) {
      publisher.publish(channel, JSON.stringify({ id }))
    }
  }
 
  update(config) {
    this.set(config)
    this.notifyUpdated()
  }
 
  setupPubSub() {
    const { redisOpts } = this.crowi
 
    Iif (redisOpts) {
      this.pubSub.publisher = redis.createClient(redisOpts)
      this.pubSub.subscriber = redis.createClient(redisOpts)
 
      const { pubSub } = this
      const { subscriber } = pubSub
 
      debug('PubSubId', pubSub.id)
 
      if (subscriber) {
        subscriber.on('message', async (channel, message) => {
          if (channel !== pubSub.channel) return
 
          const { id } = JSON.parse(message)
          if (id === pubSub.id) return
 
          await this.load()
 
          await Promise.all([this.crowi.setupSlack(), this.crowi.setupMailer()])
 
          debug(`Config updated by ${pubSub.id}`)
        })
 
        subscriber.subscribe(pubSub.channel)
      }
    }
  }
}