瀏覽代碼

Upgrade feature backend

NGPixel 8 年之前
父節點
當前提交
41d20db810
共有 2 個文件被更改,包括 97 次插入3 次删除
  1. 95 3
      libs/system.js
  2. 2 0
      package.json

+ 95 - 3
libs/system.js

@@ -1,10 +1,14 @@
 'use strict'
 
 const Promise = require('bluebird')
-const https = require('follow-redirects').https
+const crypto = require('crypto')
 const fs = Promise.promisifyAll(require('fs-extra'))
+const https = require('follow-redirects').https
+const klaw = require('klaw')
 const path = require('path')
+const pm2 = Promise.promisifyAll(require('pm2'))
 const tar = require('tar')
+const through2 = require('through2')
 const zlib = require('zlib')
 const _ = require('lodash')
 
@@ -13,30 +17,118 @@ module.exports = {
   _remoteFile: 'https://github.com/Requarks/wiki/releases/download/{0}/wiki-js.tar.gz',
   _installDir: '',
 
+  /**
+   * Install a version of Wiki.js
+   *
+   * @param {any} targetTag The version to install
+   * @returns {Promise} Promise of the operation
+   */
   install (targetTag) {
     let self = this
 
     self._installDir = path.resolve(ROOTPATH, appconfig.paths.data, 'install')
 
     return fs.ensureDirAsync(self._installDir).then(() => {
+      return fs.emptyDirAsync(self._installDir)
+    }).then(() => {
       let remoteURL = _.replace(self._remoteFile, '{0}', targetTag)
 
       return new Promise((resolve, reject) => {
+        /**
+         * Fetch tarball and extract to temporary folder
+         */
         https.get(remoteURL, resp => {
           if (resp.statusCode !== 200) {
             return reject(new Error('Remote file not found'))
           }
+          winston.info('[SERVER.System] Install tarball found. Downloading...')
 
           resp.pipe(zlib.createGunzip())
           .pipe(tar.Extract({ path: self._installDir }))
           .on('error', err => reject(err))
           .on('end', () => {
-            resolve(true)
+            winston.info('[SERVER.System] Tarball extracted. Comparing files...')
+            /**
+             * Replace old files
+             */
+            klaw(self._installDir)
+            .on('error', err => reject(err))
+            .on('end', () => {
+              winston.info('[SERVER.System] All files were updated successfully.')
+              resolve(true)
+            })
+            .pipe(self.replaceFile())
           })
         })
-      }).then(() => {
+      })
+    }).then(() => {
+      winston.info('[SERVER.System] Cleaning install leftovers...')
+      return fs.removeAsync(self._installDir).then(() => {
+        winston.info('[SERVER.System] Restarting Wiki.js...')
+        return pm2.restartAsync('wiki').catch(err => {
+          winston.error('Unable to restart Wiki.js via pm2... Do a manual restart!')
+          process.exit()
+        })
+      })
+    }).catch(err => {
+      winston.warn(err)
+    })
+  },
+
+  /**
+   * Replace file if different
+   */
+  replaceFile () {
+    let self = this
+    return through2.obj((item, enc, next) => {
+      if (!item.stats.isDirectory()) {
+        self.digestFile(item.path).then(sourceHash => {
+          let destFilePath = _.replace(item.path, self._installDir, ROOTPATH)
+          return self.digestFile(destFilePath).then(targetHash => {
+            if (sourceHash === targetHash) {
+              winston.log('verbose', '[SERVER.System] Skipping ' + destFilePath)
+              return fs.removeAsync(item.path).then(() => {
+                return next() || true
+              })
+            } else {
+              winston.log('verbose', '[SERVER.System] Updating ' + destFilePath + '...')
+              return fs.moveAsync(item.path, destFilePath, { overwrite: true }).then(() => {
+                return next() || true
+              })
+            }
+          })
+        }).catch(err => {
+          throw err
+        })
+      } else {
+        next()
+      }
+    })
+  },
 
+  /**
+   * Generate the hash of a file
+   *
+   * @param {String} filePath The absolute path of the file
+   * @return {Promise<String>} Promise of the hash result
+   */
+  digestFile: (filePath) => {
+    return new Promise((resolve, reject) => {
+      let hash = crypto.createHash('sha1')
+      hash.setEncoding('hex')
+      fs.createReadStream(filePath)
+      .on('error', err => { reject(err) })
+      .on('end', () => {
+        hash.end()
+        resolve(hash.read())
       })
+      .pipe(hash)
+    }).catch(err => {
+      if (err.code === 'ENOENT') {
+        return '0'
+      } else {
+        throw err
+      }
     })
   }
 }

+ 2 - 0
package.json

@@ -82,6 +82,7 @@
     "passport-local": "^1.0.0",
     "passport-windowslive": "^1.0.2",
     "passport.socketio": "^3.7.0",
+    "pm2": "^2.4.0",
     "pug": "^2.0.0-beta11",
     "read-chunk": "^2.0.0",
     "remove-markdown": "^0.1.0",
@@ -96,6 +97,7 @@
     "stopword": "^0.1.1",
     "stream-to-promise": "^2.2.0",
     "tar": "^2.2.1",
+    "through2": "^2.0.3",
     "validator": "^6.2.0",
     "validator-as-promised": "^1.0.2",
     "winston": "^2.3.0"