UNPKG

nssr

Version:

Node.js client for ShadowsocksR

392 lines (391 loc) 17.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 __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [0, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; var program = require("commander"); var pkg = require("pjson"); var path = require("path"); var cp = require("child_process"); var fs = require("fs-extra"); var inquirer = require("inquirer"); var ora = require("ora"); var glob = require("glob"); var download = require("download-git-repo"); var semver = require("semver"); var honoka_1 = require("honoka"); var Config_1 = require("./Config"); var Validator_1 = require("./Validator"); var Logger_1 = require("./Logger"); module.exports = (function () { function Cli() { this.logger = new Logger_1.default(); this.config = new Config_1.default(); } Cli.prototype.register = function () { program .version(pkg.version) .command('init', 'initialize nssr') .command('add', 'add a server') .command('delete [server]', 'delete a server') .command('list', 'list servers') .command('start [server]', 'start server') .command('stop', 'stop server') .command('status', 'server status') .parse(process.argv); }; Cli.prototype.init = function () { return __awaiter(this, void 0, void 0, function () { var _this = this; var answers, spinner_1; return __generator(this, function (_a) { switch (_a.label) { case 0: if (!this.checkInit(false)) { return [2]; } return [4, inquirer.prompt({ type: 'confirm', name: 'initialize', message: "Do you want to initialize" + (this.hasInit() ? ' again:' : ':'), default: true })]; case 1: answers = _a.sent(); if (answers.initialize) { fs.removeSync(Config_1.default.baseDir); fs.mkdirpSync(Config_1.default.baseDir); fs.mkdirpSync(Config_1.default.serverDir); spinner_1 = ora("downloading library to " + Config_1.default.libDir); spinner_1.start(); download('shadowsocksr-backup/shadowsocksr#manyuser', Config_1.default.libDir, { clone: false }, function (err) { spinner_1.stop(); if (err) { throw err; } else { _this.logger.info("initialized to " + Config_1.default.baseDir); } }); } return [2]; } }); }); }; Cli.prototype.add = function () { return __awaiter(this, void 0, void 0, function () { var answers, serverConfigPath; return __generator(this, function (_a) { switch (_a.label) { case 0: if (!this.checkInit()) { return [2]; } return [4, inquirer.prompt([ { type: 'input', name: 'server_name', message: 'Friendly server name:', validate: function (input) { return input.trim() !== '' && input === path.basename(input); } }, { type: 'input', name: 'server', message: 'Server IP or domain:', validate: function (input) { return Validator_1.default.isIpAddress(input) || Validator_1.default.isDomain(input); } }, { type: 'input', name: 'server_port', message: 'Server port:', validate: function (input) { return Validator_1.default.isPort(input); } }, { type: 'input', name: 'local_address', message: 'Local address:', default: '127.0.0.1', validate: function (input) { return Validator_1.default.isIpAddress(input); } }, { type: 'input', name: 'local_port', message: 'Local port:', default: 1080, validate: function (input) { return Validator_1.default.isPort(input); } }, { type: 'password', name: 'password', message: 'Password:', validate: function (input) { return input.length > 0; } }, { type: 'list', name: 'method', message: 'Method:', choices: [ 'none', 'aes-256-cfb', 'aes-192-cfb', 'aes-128-cfb', 'aes-256-cfb8', 'aes-192-cfb8', 'aes-128-cfb8', 'aes-256-ctr', 'aes-192-ctr', 'aes-128-ctr', 'chacha20-ietf', 'chacha20', 'rc4-md5', 'rc4-md5-6' ], default: 'none' }, { type: 'list', name: 'protocol', message: 'Protocol:', choices: [ 'origin', 'verify_deflate', 'auth_sha1_v4', 'auth_sha1_v4_compatible', 'auth_aes128_md5', 'auth_aes128_sha1', 'auth_chain_a', 'auth_chain_b' ], default: 'origin' }, { type: 'input', name: 'protocol_param', message: 'Protocol param:', default: '' }, { type: 'list', name: 'obfs', message: 'Obfs:', choices: [ 'plain', 'http_simple', 'http_simple_compatible', 'http_post', 'http_post_compatible', 'tls1.2_ticket_auth', 'tls1.2_ticket_auth_compatible', 'tls1.2_ticket_fastauth', 'tls1.2_ticket_fastauth_compatible' ], default: 'plain' }, { type: 'input', name: 'obfs_param', message: 'Obfs param:', default: '' } ])]; case 1: answers = _a.sent(); answers.local_port = Number(answers.local_port); answers.server_port = Number(answers.server_port); serverConfigPath = path.join(Config_1.default.serverDir, answers.server_name + ".json"); delete answers.server_name; fs.writeFileSync(serverConfigPath, JSON.stringify(answers, null, 4)); this.logger.info("create server to " + serverConfigPath); return [2]; } }); }); }; Cli.prototype.delete = function () { if (!this.checkInit()) { return; } if (program.args.length === 0) { this.logger.warning('server name muse be provided'); } else { var configFile = path.join(Config_1.default.serverDir, program.args[0] + ".json"); if (!fs.existsSync(configFile)) { this.logger.warning("server \"" + program.args[0] + "\" not exist"); } else { fs.unlinkSync(configFile); this.logger.info("\"" + program.args[0] + "\" has been deleted"); } } }; Cli.prototype.start = function () { if (!this.checkInit()) { return; } this.stop(); if (program.args.length === 0) { this.logger.warning('server name muse be provided'); } else { var configFile = path.join(Config_1.default.serverDir, program.args[0] + ".json"); if (!fs.existsSync(configFile)) { this.logger.warning("server \"" + program.args[0] + "\" not exist"); } else { cp.execSync(path.join(Config_1.default.libDir, 'shadowsocks', 'local.py') + " -c " + configFile + " --pid-file " + Config_1.default.pidFile + " --log-file " + Config_1.default.logFile + " -d start"); var pid = this.getPid(); if (pid > 0) { this.logger.info("start nssr at process: " + pid); } } } }; Cli.prototype.stop = function () { if (!this.checkInit()) { return; } var pid = this.getPid(); if (pid > 0) { try { process.kill(pid); this.logger.info("kill nssr at process: " + pid); } catch (e) { } fs.unlinkSync(Config_1.default.pidFile); } }; Cli.prototype.status = function () { if (!this.checkInit()) { return; } var isRunning; var pid = this.getPid(); if (pid > 0) { try { isRunning = !!process.kill(pid, 0); } catch (e) { isRunning = e.code === 'EPERM'; } } else { isRunning = false; } if (isRunning && pid > 0) { this.logger.info("nssr found running with process: " + pid); } else { this.logger.info('nssr is not running'); } }; Cli.prototype.list = function () { var _this = this; if (!this.checkInit()) { return; } glob(path.join(Config_1.default.serverDir, '*.json'), function (err, files) { if (err) { throw err; } files.forEach(function (file) { _this.logger.info(path.basename(file, '.json')); }); }); }; Cli.prototype.hasInit = function () { if (!fs.existsSync(Config_1.default.baseDir)) { return false; } if (!fs.existsSync(Config_1.default.libDir)) { return false; } return true; }; Cli.prototype.checkInit = function (check) { if (check === void 0) { check = true; } program.version(pkg.version).parse(process.argv); this.checkUpdate().catch(function () { }); if (!check) { return true; } if (!this.hasInit()) { this.logger.warning('please initialize before use'); return false; } return true; }; Cli.prototype.getPid = function () { if (fs.existsSync(Config_1.default.pidFile)) { return Number(fs.readFileSync(Config_1.default.pidFile)); } return 0; }; Cli.prototype.checkUpdate = function () { return __awaiter(this, void 0, void 0, function () { var lastUpdateCheck, latestPkg, latestVersion, currentVersion; return __generator(this, function (_a) { switch (_a.label) { case 0: lastUpdateCheck = Number(this.config.get('lastUpdateCheck')) || 0; if (lastUpdateCheck && Date.now() - lastUpdateCheck < 1000 * 60 * 60 * 24) { return [2]; } return [4, honoka_1.default.get(Config_1.default.packageURL)]; case 1: latestPkg = _a.sent(); latestPkg = JSON.parse(latestPkg); latestVersion = latestPkg.version; currentVersion = pkg.version; if (!semver.valid(latestVersion)) { return [2]; } this.config.set('lastUpdateCheck', Date.now()); if (semver.gt(latestVersion, currentVersion)) { this.logger.info(); this.logger.warning("Your current version of nssr is out of date. The latest version is " + latestVersion + " while you're on " + currentVersion + "."); this.logger.info(); } return [2]; } }); }); }; return Cli; }());