UNPKG

smac

Version:

Scriptcraft SMA Server controller

276 lines (275 loc) 11.7 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; result["default"] = mod; return result; }; Object.defineProperty(exports, "__esModule", { value: true }); const extract_zip_1 = __importDefault(require("extract-zip")); const fs = __importStar(require("fs-extra")); const ghetto_monad_1 = require("ghetto-monad"); const os_1 = __importDefault(require("os")); const path_1 = __importDefault(require("path")); const progress_1 = __importDefault(require("progress")); const request_1 = __importDefault(require("request")); const request_progress_1 = __importDefault(require("request-progress")); const rimraf_1 = __importDefault(require("rimraf")); const url_1 = __importDefault(require("url")); const util_1 = __importDefault(require("util")); const gitHubIssuesUrl = 'https://github.com/Magikcraft/scriptcraft-sma/issues'; function downloadZipFile(worldSpec, targetPath) { return __awaiter(this, void 0, void 0, function* () { const tmpPath = findSuitableTempDirectory(worldSpec.name); if (tmpPath.isNothing) { return tmpPath; } const fileName = worldSpec.downloadUrl.split('/').pop(); const downloadedFile = path_1.default.join(tmpPath.value, fileName || worldSpec.name); if (fs.existsSync(downloadedFile)) { console.log('Worlds already downloaded as', downloadedFile); } else { console.log(`Downloading world ${worldSpec.name}`); console.log('Downloading', worldSpec.downloadUrl); console.log('Saving to', downloadedFile); const download = yield requestWorldZip(getRequestOptions(worldSpec), downloadedFile); if (download.isNothing || download.isError) { return download; } } const source = yield extractDownload(downloadedFile); if (source.isError || source.isNothing) { return source; } const worldPath = yield copyIntoPlace(source.value, targetPath); return worldPath; }); } exports.downloadZipFile = downloadZipFile; function findSuitableTempDirectory(worldName) { var now = Date.now(); var candidateTmpDirs = [ process.env.npm_config_tmp, os_1.default.tmpdir(), path_1.default.join(process.cwd(), 'tmp'), ]; for (var i = 0; i < candidateTmpDirs.length; i++) { var candidatePath = candidateTmpDirs[i]; if (!candidatePath) continue; try { candidatePath = path_1.default.join(path_1.default.resolve(candidatePath), worldName); //@ts-ignore fs.mkdirsSync(candidatePath, '0777'); // Make double sure we have 0777 permissions; some operating systems // default umask does not allow write by default. fs.chmodSync(candidatePath, '0777'); var testFile = path_1.default.join(candidatePath, now + '.tmp'); fs.writeFileSync(testFile, 'test'); fs.unlinkSync(testFile); return new ghetto_monad_1.Result(candidatePath); } catch (e) { console.log(candidatePath, 'is not writable:', e.message); } } console.error('Can not find a writable tmp directory, please report issue ' + `on ${gitHubIssuesUrl} with as much ` + 'information as possible.'); return new ghetto_monad_1.Nothing(); } function requestWorldZip(requestOptions, filePath) { return __awaiter(this, void 0, void 0, function* () { return new Promise(resolve => { const writePath = filePath + '-download-' + Date.now(); console.log('Receiving...'); var bar = null; request_progress_1.default(request_1.default(requestOptions, (error, response, body) => { console.log(''); if (!error && response.statusCode === 200) { fs.writeFileSync(writePath, body); console.log('Received ' + Math.floor(body.length / 1024) + 'K total.'); fs.renameSync(writePath, filePath); resolve(new ghetto_monad_1.Result(filePath)); } else if (response) { console.error('Error requesting archive.\n' + 'Status: ' + response.statusCode + '\n' + 'Request options: ' + JSON.stringify(requestOptions, null, 2) + '\n' + 'Response headers: ' + JSON.stringify(response.headers, null, 2) + '\n' + 'Make sure your network and proxy settings are correct.\n\n' + 'If you continue to have issues, please report this full log at ' + gitHubIssuesUrl); resolve(new ghetto_monad_1.Nothing()); } else { resolve(handleRequestError(error)); } })) .on('progress', function (state) { try { if (!bar) { bar = new progress_1.default(' [:bar] :percent', { total: state.size.total, width: 40, }); } bar.curr = state.size.transferred; bar.tick(); } catch (e) { // It doesn't really matter if the progress bar doesn't update. } }) .on('error', handleRequestError); }); }); } function getRequestOptions(worldSpec) { let strictSSL = !!process.env.npm_config_strict_ssl; if (process.version == 'v0.10.34') { console.log('Node v0.10.34 detected, turning off strict ssl due to https://github.com/joyent/node/issues/8894'); strictSSL = false; } const options = { uri: worldSpec.downloadUrl, encoding: null, followRedirect: true, headers: {}, strictSSL: strictSSL, }; const proxyUrl = process.env.npm_config_https_proxy || process.env.npm_config_http_proxy || process.env.npm_config_proxy; if (proxyUrl) { // Print using proxy var proxy = url_1.default.parse(proxyUrl); if (proxy.auth) { // Mask password proxy.auth = proxy.auth.replace(/:.*$/, ':******'); } console.log('Using proxy ' + url_1.default.format(proxy)); // Enable proxy options.proxy = proxyUrl; } // Use the user-agent string from the npm config options.headers['User-Agent'] = process.env.npm_config_user_agent; // Use certificate authority settings from npm let ca = process.env.npm_config_ca; if (!ca && process.env.npm_config_cafile) { try { ca = fs .readFileSync(process.env.npm_config_cafile, { encoding: 'utf8', }) .split(/\n(?=-----BEGIN CERTIFICATE-----)/g); // Comments at the beginning of the file result in the first // item not containing a certificate - in this case the // download will fail if (ca.length > 0 && !/-----BEGIN CERTIFICATE-----/.test(ca[0])) { ca.shift(); } } catch (e) { console.error('Could not read cafile', process.env.npm_config_cafile, e); } } if (ca) { console.log('Using npmconf ca'); options.agentOptions = { ca: ca, }; options.ca = ca; } return options; } exports.getRequestOptions = getRequestOptions; function extractDownload(filePath) { return new Promise((resolve, reject) => { // extract to a unique directory in case multiple processes are // installing and extracting at once const extractedPath = filePath + '-extract-' + Date.now(); var options = { cwd: extractedPath }; // @ts-ignore fs.mkdirsSync(extractedPath, '0777'); // Make double sure we have 0777 permissions; some operating systems // default umask does not allow write by default. fs.chmodSync(extractedPath, '0777'); if (filePath.substr(-4) === '.zip') { console.log('Extracting zip contents'); extract_zip_1.default(path_1.default.resolve(filePath), { dir: extractedPath }, function (err) { if (err) { console.error('Error extracting zip'); resolve(new ghetto_monad_1.ErrorResult(err)); } else { resolve(new ghetto_monad_1.Result(extractedPath)); } }); } }); } exports.extractDownload = extractDownload; function handleRequestError(error) { if (error && error.stack && error.stack.indexOf('SELF_SIGNED_CERT_IN_CHAIN') != -1) { console.error('Error making request, SELF_SIGNED_CERT_IN_CHAIN. ' + 'Please read https://github.com/Medium/phantomjs#i-am-behind-a-corporate-proxy-that-uses-self-signed-ssl-certificates-to-intercept-encrypted-traffic'); return new ghetto_monad_1.ErrorResult(new Error('SSL Error during download')); } else if (error) { console.error('Error making request.\n' + error.stack + '\n\n' + `Please report this full log at ${gitHubIssuesUrl}`); return new ghetto_monad_1.ErrorResult(new Error()); } else { console.error('Something unexpected happened, please report this full ' + `log at ${gitHubIssuesUrl}`); return new ghetto_monad_1.ErrorResult(new Error()); } } function copyIntoPlace(extractedPath, targetPath) { return __awaiter(this, void 0, void 0, function* () { const rm = util_1.default.promisify(rimraf_1.default); console.log('Removing', targetPath); try { yield rm(targetPath); // Look for the extracted directory, so we can rename it. console.log(`Copying extracted worlds to ${targetPath}`); yield fs.move(extractedPath, targetPath, { overwrite: true, }); } catch (error) { console.log('Error copying ' + extractedPath + ' to ' + targetPath); console.log(error); return new ghetto_monad_1.ErrorResult(error); } console.log('Copied worlds to ' + targetPath); return new ghetto_monad_1.Result(targetPath); }); }