A suite to track Project Diva score statistics and ratings / D4DJ event data.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
projectdivar/server/node_modules/ngrok/download.js

197 lines
5.7 KiB

4 years ago
/*
- downloads archived binary from ngrok cdn (if not found in local cache)
- stores it in home dir as a local cache
- extracts executable to module's bin folder
*/
function downloadNgrok(callback, options) {
options = options || {};
const os = require('os');
const fs = require('fs');
const path = require('path');
const readline = require('readline');
const Zip = require('decompress-zip');
const request = require('request');
const { Transform } = require('stream');
const cafilePath = options.cafilePath || process.env.NGROK_ROOT_CA_PATH;
const cdnUrl = getCdnUrl();
const cacheUrl = getCacheUrl();
const maxAttempts = 3;
let attempts = 0;
if (hasCache()) {
console.log('ngrok - cached download found at ' + cacheUrl);
extract(retry);
} else {
download(retry);
}
function getCdnUrl() {
const arch = options.arch || process.env.NGROK_ARCH || os.platform() + os.arch();
const cdn = options.cdnUrl || process.env.NGROK_CDN_URL || 'https://bin.equinox.io';
const cdnPath = options.cdnPath || process.env.NGROK_CDN_PATH || '/c/4VmDzA7iaHb/ngrok-stable-';
const cdnFiles = {
darwinia32: cdn + cdnPath + 'darwin-386.zip',
darwinx64: cdn + cdnPath + 'darwin-amd64.zip',
linuxarm: cdn + cdnPath + 'linux-arm.zip',
linuxarm64: cdn + cdnPath + 'linux-arm64.zip',
androidarm: cdn + cdnPath + 'linux-arm.zip',
androidarm64: cdn + cdnPath + 'linux-arm64.zip',
linuxia32: cdn + cdnPath + 'linux-386.zip',
linuxx64: cdn + cdnPath + 'linux-amd64.zip',
win32ia32: cdn + cdnPath + 'windows-386.zip',
win32x64: cdn + cdnPath + 'windows-amd64.zip',
freebsdia32: cdn + cdnPath + 'freebsd-386.zip',
freebsdx64: cdn + cdnPath + 'freebsd-amd64.zip'
};
const url = cdnFiles[arch];
if (!url) {
console.error('ngrok - platform ' + arch + ' is not supported.');
process.exit(1);
}
return url;
}
function getCacheUrl() {
let dir;
try {
dir = path.join(os.homedir(), '.ngrok');
if (!fs.existsSync(dir) || !fs.statSync(dir).isDirectory()) {
fs.mkdirSync(dir);
}
} catch (err) {
dir = path.join(__dirname, 'bin');
}
const name = Buffer.from(cdnUrl).toString('base64');
return path.join(dir, name + '.zip');
}
function hasCache() {
if (options.ignoreCache || process.env.NGROK_IGNORE_CACHE === 'true') {
return false;
}
return fs.existsSync(cacheUrl) && fs.statSync(cacheUrl).size;
}
function download(cb) {
console.log('ngrok - downloading binary ' + cdnUrl);
const ca = tryToReadCaFile();
const options = {
url: cdnUrl,
ca
};
const downloadStream = request
.get(options)
.on('response', res => {
if (!/2\d\d/.test(res.statusCode)) {
res.pause();
return downloadStream.emit('error', new Error('wrong status code: ' + res.statusCode));
}
const total = res.headers['content-length'];
const progress = progressStream('ngrok - downloading progress: ', total);
res.pipe(progress).pipe(outputStream);
})
.on('error', e => {
console.warn('ngrok - error downloading binary', e);
cb(e);
});
const outputStream = fs
.createWriteStream(cacheUrl)
.on('error', e => {
console.log('ngrok - error storing binary to local file', e);
cb(e);
})
.on('finish', () => {
console.log('\nngrok - binary downloaded to ' + cacheUrl);
extract(cb);
});
}
function progressStream(msg, total) {
let downloaded = 0;
let shouldClearLine = false;
const log = () => {
if (shouldClearLine) {
readline.clearLine(process.stdout);
readline.cursorTo(process.stdout, 0);
}
let progress = downloaded + (total ? '/' + total : '');
process.stdout.write(msg + progress);
shouldClearLine = true;
};
if (total > 0) log();
return new Transform({
transform(data, enc, cb) {
downloaded += data.length;
log();
cb(null, data);
}
});
}
function extract(cb) {
console.log('ngrok - unpacking binary');
const moduleBinPath = path.join(__dirname, 'bin');
new Zip(cacheUrl)
.extract({ path: moduleBinPath })
.once('error', error)
.once('extract', () => {
const suffix = os.platform() === 'win32' ? '.exe' : '';
if (suffix === '.exe') {
fs.writeFileSync(path.join(moduleBinPath, 'ngrok.cmd'), 'ngrok.exe');
}
const target = path.join(moduleBinPath, 'ngrok' + suffix);
fs.chmodSync(target, 0755);
if (!fs.existsSync(target) || fs.statSync(target).size <= 0) {
return error(new Error('corrupted file ' + target));
}
console.log('ngrok - binary unpacked to ' + target);
cb(null);
});
function error(e) {
console.warn('ngrok - error unpacking binary', e);
cb(e);
}
}
function retry(err) {
attempts++;
if (err && attempts === maxAttempts) {
console.error('ngrok - install failed', err);
return callback(err);
}
if (err) {
console.warn('ngrok - install failed, retrying');
return setTimeout(download, 500, retry);
}
callback();
}
function tryToReadCaFile() {
try {
const caString = fs.existsSync(cafilePath) && fs.readFileSync(cafilePath).toString();
const caContents =
caString &&
caString
.split('-----END CERTIFICATE-----')
.filter(c => c.trim().startsWith('-----BEGIN CERTIFICATE-----'))
.map(c => `${c}-----END CERTIFICATE-----`);
return caContents.length > 0 ? caContents : undefined;
} catch (error) {
console.warn(error);
return undefined;
}
}
}
module.exports = downloadNgrok;