|
|
@ -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 |
|
|
|
} |
|
|
|
}) |
|
|
|
} |
|
|
|
} |