All files / src upgradeDB.coffee

21.11% Statements 19/90
5.88% Branches 1/17
0% Functions 0/33
20.45% Lines 18/88
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 106 107 108 109 110 111 112 113 114 115 116 117 118 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 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 1891x 1x 1x 1x 1x 1x 1x 1x   1x                           1x   1x                                                       1x                                                                             1x                                                                                       1x                                 1x 1x 1x   1x                                                              
dbVersion = require('./model/dbVersion').dbVersion
Keystore = require('./model/keystore').Keystore
Client = require('./model/clients').Client
User = require('./model/users').User
Visualizer = require('./model/visualizer').Visualizer
logger = require 'winston'
pem = require 'pem'
Q = require 'q'
 
dedupName = (name, names, num) ->
  if num
    newName = "#{name} #{num}"
  else
    newName = name
  if newName in names
    if not num
      num = 1
    return dedupName(name, names, ++num)
  else
    return newName
 
# push new upgrade functions to this array, function must return a promise
# Warning: only add new function below existing functions, order matters!
upgradeFuncs = []
 
upgradeFuncs.push
  description: "Ensure that all certs have a fingerprint property"
  func: ->
    defer = Q.defer()
 
    Keystore.findOne (err, keystore) ->
      return defer.resolve() if not keystore
 
      # convert server cert
      pem.getFingerprint keystore.cert.data, (err, obj) ->
        keystore.cert.fingerprint = obj.fingerprint
 
        promises = []
        for cert, i in keystore.ca
          caDefer = Q.defer()
          promises.push caDefer.promise
          do (caDefer, i) ->
            pem.getFingerprint cert.data, (err, obj) ->
              keystore.ca[i].fingerprint = obj.fingerprint
              caDefer.resolve()
 
        Q.all(promises).then ->
          keystore.save (err) ->
            logger.error "Failed to save keystore: #{err}" if err?
            defer.resolve()
 
    return defer.promise
 
upgradeFuncs.push
  description: "Convert clients link to certs via their domain to use the cert fingerprint instead"
  func: ->
    defer = Q.defer()
 
    Client.find (err, clients) ->
      if err?
        logger.error "Couldn't fetch all clients to upgrade db: #{err}"
        return defer.reject()
 
      Keystore.findOne (err, keystore) ->
        if err?
          logger.error "Couldn't fetch keystore to upgrade db: #{err}"
          return defer.reject()
 
        promises = []
        for client in clients
          clientDefer = Q.defer()
          promises.push clientDefer.promise
 
          if keystore?.ca?
            for cert in keystore.ca
              if client.clientDomain is cert.commonName and not client.certFingerprint?
                client.certFingerprint = cert.fingerprint
                break
 
          do (clientDefer) ->
            client.save (err) ->
              if err?
                logger.error "Couldn't save client #{client.clientID} while upgrading db: #{err}"
                return clientDefer.reject()
 
              clientDefer.resolve()
 
        Q.all(promises).then ->
          defer.resolve()
 
    return defer.promise
 
upgradeFuncs.push
  description: "Migrate visualizer setting from a user's profile to a shared collection"
  func: ->
    defer = Q.defer()
    User.find (err, users) ->
      if err
        return Q.defer().reject(err)
 
      visNames = []
      promises = []
      users.forEach (user) ->
        if user.settings?.visualizer?
          vis = user.settings.visualizer
          if vis.components.length > 0 or vis.mediators.length > 0 or vis.channels.length > 0
            userDefer = Q.defer()
            promises.push userDefer.promise
 
            name = "#{user.firstname} #{user.surname}'s visualizer"
            name = dedupName name, visNames
            vis.name = name
            visNames.push name
 
            vis = new Visualizer vis
            logger.debug "Migrating visualizer from user profile #{user.email}, using visualizer name '#{name}'"
            vis.save (err, vis) ->
              if err
                logger.error "Error migrating visualizer from user profile #{user.email}: #{err.stack}"
                return userDefer.reject err
 
              # delete the visualizer settings from this user profile
              user.set 'settings.visualizer', null
              user.save (err, user) ->
                if err then return userDefer.reject err
                return userDefer.resolve()
 
      Q.all(promises).then ->
        defer.resolve()
      .catch (err) ->
        defer.reject err
 
    return defer.promise
 
# add new upgrade functions here ^^
 
runUpgradeFunc = (i, dbVer) ->
  logger.info "  \u2022 Running update: #{upgradeFuncs[i].description}..."
  defer = Q.defer()
  # run upgrade function
  upgradeFuncs[i].func().then ->
    # update the datbase version
    dbVer.version = i
    dbVer.lastUpdated = new Date()
    dbVer.save (err) ->
      logger.error err if err?
      logger.info "  \u2713 Done."
      defer.resolve()
  .catch (err) ->
    defer.reject err
  return defer.promise
 
if process.env.NODE_ENV == "test"
  exports.upgradeFuncs = upgradeFuncs
  exports.runUpgradeFunc = runUpgradeFunc
  exports.dedupName = dedupName
 
exports.upgradeDb = (callback) ->
  dbVersion.findOne (err, dbVer) ->
    if dbVer is null
      dbVer = new dbVersion
        version: -1
        lastUpdated: new Date()
 
    # check if the database version need to be upgraded
    if dbVer.version < (upgradeFuncs.length - 1)
      logger.info 'Upgrading the database...'
      promise = null
      # call each database upgrade function sequentially
      for i in [(dbVer.version + 1)..(upgradeFuncs.length - 1)]
        do (i) ->
          if not promise?
            promise = runUpgradeFunc(i, dbVer)
          else
            promise = promise.then -> runUpgradeFunc(i, dbVer)
 
      promise.then ->
        logger.info 'Completed database upgrade'
        callback()
      .catch (err) ->
        logger.error "There was an error upgrading your database, you will need to fix this manually to continue. #{err.stack}"
        process.exit()
    else
      logger.info 'No database upgrades needed'
      callback()
 
if not module.parent
  exports.upgradeDb(-> process.exit())