pomelo 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681
  1. #!/usr/bin/env node
  2. /**
  3. * Module dependencies.
  4. */
  5. var fs = require('fs'),
  6. os = require('os'),
  7. path = require('path'),
  8. util = require('util'),
  9. cliff = require('cliff'),
  10. mkdirp = require('mkdirp'),
  11. co = require('../lib/modules/console'),
  12. utils = require('../lib/util/utils'),
  13. starter = require('../lib/master/starter'),
  14. exec = require('child_process').exec,
  15. spawn = require('child_process').spawn,
  16. version = require('../package.json').version,
  17. adminClient = require('pomelo-admin').adminClient,
  18. constants = require('../lib/util/constants'),
  19. program = require('commander');
  20. /**
  21. * Constant Variables
  22. */
  23. var TIME_INIT = 1 * 1000;
  24. var TIME_KILL_WAIT = 5 * 1000;
  25. var KILL_CMD_LUX = 'kill -9 `ps -ef|grep node|awk \'{print $2}\'`';
  26. var KILL_CMD_WIN = 'taskkill /im node.exe /f';
  27. var CUR_DIR = process.cwd();
  28. var DEFAULT_GAME_SERVER_DIR = CUR_DIR;
  29. var DEFAULT_USERNAME = 'admin';
  30. var DEFAULT_PWD = 'admin';
  31. var DEFAULT_ENV = 'development';
  32. var DEFAULT_MASTER_HOST = '127.0.0.1';
  33. var DEFAULT_MASTER_PORT = 3005;
  34. var CONNECT_ERROR = 'Fail to connect to admin console server.';
  35. var FILEREAD_ERROR = 'Fail to read the file, please check if the application is started legally.';
  36. var CLOSEAPP_INFO = 'Closing the application......\nPlease wait......';
  37. var ADD_SERVER_INFO = 'Successfully add server.';
  38. var RESTART_SERVER_INFO = 'Successfully restart server.';
  39. var INIT_PROJ_NOTICE = '\nThe default admin user is: \n\n'+ ' username'.green + ': admin\n ' + 'password'.green+ ': admin\n\nYou can configure admin users by editing adminUser.json later.\n ';
  40. var SCRIPT_NOT_FOUND = 'Fail to find an appropriate script to run,\nplease check the current work directory or the directory specified by option `--directory`.\n'.red;
  41. var MASTER_HA_NOT_FOUND = 'Fail to find an appropriate masterha config file, \nplease check the current work directory or the arguments passed to.\n'.red;
  42. var COMMAND_ERROR = 'Illegal command format. Use `pomelo --help` to get more info.\n'.red;
  43. var DAEMON_INFO = 'The application is running in the background now.\n';
  44. program.version(version);
  45. program.command('init [path]')
  46. .description('create a new application')
  47. .action(function(path) {
  48. init(path || CUR_DIR);
  49. });
  50. program.command('start')
  51. .description('start the application')
  52. .option('-e, --env <env>', 'the used environment', DEFAULT_ENV)
  53. .option('-D, --daemon', 'enable the daemon start')
  54. .option('-d, --directory, <directory>', 'the code directory', DEFAULT_GAME_SERVER_DIR)
  55. .option('-t, --type <server-type>,', 'start server type')
  56. .option('-i, --id <server-id>', 'start server id')
  57. .action(function(opts) {
  58. start(opts);
  59. });
  60. program.command('list')
  61. .description('list the servers')
  62. .option('-u, --username <username>', 'administration user name', DEFAULT_USERNAME)
  63. .option('-p, --password <password>', 'administration password', DEFAULT_PWD)
  64. .option('-h, --host <master-host>', 'master server host', DEFAULT_MASTER_HOST)
  65. .option('-P, --port <master-port>', 'master server port', DEFAULT_MASTER_PORT)
  66. .action(function(opts) {
  67. list(opts);
  68. });
  69. program.command('add')
  70. .description('add a new server')
  71. .option('-u, --username <username>', 'administration user name', DEFAULT_USERNAME)
  72. .option('-p, --password <password>', 'administration password', DEFAULT_PWD)
  73. .option('-h, --host <master-host>', 'master server host', DEFAULT_MASTER_HOST)
  74. .option('-P, --port <master-port>', 'master server port', DEFAULT_MASTER_PORT)
  75. .action(function() {
  76. var args = [].slice.call(arguments, 0);
  77. var opts = args[args.length - 1];
  78. opts.args = args.slice(0, -1);
  79. add(opts);
  80. });
  81. program.command('stop')
  82. .description('stop the servers, for multiple servers, use `pomelo stop server-id-1 server-id-2`')
  83. .option('-u, --username <username>', 'administration user name', DEFAULT_USERNAME)
  84. .option('-p, --password <password>', 'administration password', DEFAULT_PWD)
  85. .option('-h, --host <master-host>', 'master server host', DEFAULT_MASTER_HOST)
  86. .option('-P, --port <master-port>', 'master server port', DEFAULT_MASTER_PORT)
  87. .action(function() {
  88. var args = [].slice.call(arguments, 0);
  89. var opts = args[args.length - 1];
  90. opts.serverIds = args.slice(0, -1);
  91. terminal('stop', opts);
  92. });
  93. program.command('kill')
  94. .description('kill the application')
  95. .option('-u, --username <username>', 'administration user name', DEFAULT_USERNAME)
  96. .option('-p, --password <password>', 'administration password', DEFAULT_PWD)
  97. .option('-h, --host <master-host>', 'master server host', DEFAULT_MASTER_HOST)
  98. .option('-P, --port <master-port>', 'master server port', DEFAULT_MASTER_PORT)
  99. .option('-f, --force', 'using this option would kill all the node processes')
  100. .action(function() {
  101. var args = [].slice.call(arguments, 0);
  102. var opts = args[args.length - 1];
  103. opts.serverIds = args.slice(0, -1);
  104. terminal('kill', opts);
  105. });
  106. program.command('restart')
  107. .description('restart the servers, for multiple servers, use `pomelo restart server-id-1 server-id-2`')
  108. .option('-u, --username <username>', 'administration user name', DEFAULT_USERNAME)
  109. .option('-p, --password <password>', 'administration password', DEFAULT_PWD)
  110. .option('-h, --host <master-host>', 'master server host', DEFAULT_MASTER_HOST)
  111. .option('-P, --port <master-port>', 'master server port', DEFAULT_MASTER_PORT)
  112. .option('-t, --type <server-type>,', 'start server type')
  113. .option('-i, --id <server-id>', 'start server id')
  114. .action(function(opts) {
  115. restart(opts);
  116. });
  117. program.command('masterha')
  118. .description('start all the slaves of the master')
  119. .option('-d, --directory <directory>', 'the code directory', DEFAULT_GAME_SERVER_DIR)
  120. .action(function(opts) {
  121. startMasterha(opts);
  122. });
  123. program.command('*')
  124. .action(function() {
  125. console.log(COMMAND_ERROR);
  126. });
  127. program.parse(process.argv);
  128. /**
  129. * Init application at the given directory `path`.
  130. *
  131. * @param {String} path
  132. */
  133. function init(path) {
  134. console.log(INIT_PROJ_NOTICE);
  135. connectorType(function(type) {
  136. emptyDirectory(path, function(empty) {
  137. if(empty) {
  138. process.stdin.destroy();
  139. createApplicationAt(path, type);
  140. } else {
  141. confirm('Destination is not empty, continue? (y/n) [no] ', function(force) {
  142. process.stdin.destroy();
  143. if(force) {
  144. createApplicationAt(path, type);
  145. } else {
  146. abort('Fail to init a project'.red);
  147. }
  148. });
  149. }
  150. });
  151. });
  152. }
  153. /**
  154. * Create directory and files at the given directory `path`.
  155. *
  156. * @param {String} ph
  157. */
  158. function createApplicationAt(ph, type) {
  159. var name = path.basename(path.resolve(CUR_DIR, ph));
  160. copy(path.join(__dirname, '../template/'), ph);
  161. mkdir(path.join(ph, 'game-server/logs'));
  162. mkdir(path.join(ph, 'shared'));
  163. // rmdir -r
  164. var rmdir = function(dir) {
  165. var list = fs.readdirSync(dir);
  166. for(var i = 0; i < list.length; i++) {
  167. var filename = path.join(dir, list[i]);
  168. var stat = fs.statSync(filename);
  169. if(filename === "." || filename === "..") {
  170. } else if(stat.isDirectory()) {
  171. rmdir(filename);
  172. } else {
  173. fs.unlinkSync(filename);
  174. }
  175. }
  176. fs.rmdirSync(dir);
  177. };
  178. setTimeout(function() {
  179. switch(type) {
  180. case '1':
  181. // use websocket
  182. var unlinkFiles = ['game-server/app.js.sio',
  183. 'game-server/app.js.wss',
  184. 'game-server/app.js.mqtt',
  185. 'game-server/app.js.sio.wss',
  186. 'game-server/app.js.udp',
  187. 'web-server/app.js.https',
  188. 'web-server/public/index.html.sio',
  189. 'web-server/public/js/lib/pomeloclient.js',
  190. 'web-server/public/js/lib/pomeloclient.js.wss',
  191. 'web-server/public/js/lib/build/build.js.wss',
  192. 'web-server/public/js/lib/socket.io.js'];
  193. for(var i = 0; i < unlinkFiles.length; ++i) {
  194. fs.unlinkSync(path.resolve(ph, unlinkFiles[i]));
  195. }
  196. break;
  197. case '2':
  198. // use socket.io
  199. var unlinkFiles = ['game-server/app.js',
  200. 'game-server/app.js.wss',
  201. 'game-server/app.js.udp',
  202. 'game-server/app.js.mqtt',
  203. 'game-server/app.js.sio.wss',
  204. 'web-server/app.js.https',
  205. 'web-server/public/index.html',
  206. 'web-server/public/js/lib/component.json',
  207. 'web-server/public/js/lib/pomeloclient.js.wss'];
  208. for(var i = 0; i < unlinkFiles.length; ++i) {
  209. fs.unlinkSync(path.resolve(ph, unlinkFiles[i]));
  210. }
  211. fs.renameSync(path.resolve(ph, 'game-server/app.js.sio'), path.resolve(ph, 'game-server/app.js'));
  212. fs.renameSync(path.resolve(ph, 'web-server/public/index.html.sio'), path.resolve(ph, 'web-server/public/index.html'));
  213. rmdir(path.resolve(ph, 'web-server/public/js/lib/build'));
  214. rmdir(path.resolve(ph, 'web-server/public/js/lib/local'));
  215. break;
  216. case '3':
  217. // use websocket wss
  218. var unlinkFiles = ['game-server/app.js.sio',
  219. 'game-server/app.js',
  220. 'game-server/app.js.udp',
  221. 'game-server/app.js.sio.wss',
  222. 'game-server/app.js.mqtt',
  223. 'web-server/app.js',
  224. 'web-server/public/index.html.sio',
  225. 'web-server/public/js/lib/pomeloclient.js',
  226. 'web-server/public/js/lib/pomeloclient.js.wss',
  227. 'web-server/public/js/lib/build/build.js',
  228. 'web-server/public/js/lib/socket.io.js'];
  229. for(var i = 0; i < unlinkFiles.length; ++i) {
  230. fs.unlinkSync(path.resolve(ph, unlinkFiles[i]));
  231. }
  232. fs.renameSync(path.resolve(ph, 'game-server/app.js.wss'), path.resolve(ph, 'game-server/app.js'));
  233. fs.renameSync(path.resolve(ph, 'web-server/app.js.https'), path.resolve(ph, 'web-server/app.js'));
  234. fs.renameSync(path.resolve(ph, 'web-server/public/js/lib/build/build.js.wss'), path.resolve(ph, 'web-server/public/js/lib/build/build.js'));
  235. break;
  236. case '4':
  237. // use socket.io wss
  238. var unlinkFiles = ['game-server/app.js.sio',
  239. 'game-server/app.js',
  240. 'game-server/app.js.udp',
  241. 'game-server/app.js.wss',
  242. 'game-server/app.js.mqtt',
  243. 'web-server/app.js',
  244. 'web-server/public/index.html',
  245. 'web-server/public/js/lib/pomeloclient.js'];
  246. for(var i = 0; i < unlinkFiles.length; ++i) {
  247. fs.unlinkSync(path.resolve(ph, unlinkFiles[i]));
  248. }
  249. fs.renameSync(path.resolve(ph, 'game-server/app.js.sio.wss'), path.resolve(ph, 'game-server/app.js'));
  250. fs.renameSync(path.resolve(ph, 'web-server/app.js.https'), path.resolve(ph, 'web-server/app.js'));
  251. fs.renameSync(path.resolve(ph, 'web-server/public/index.html.sio'), path.resolve(ph, 'web-server/public/index.html'));
  252. fs.renameSync(path.resolve(ph, 'web-server/public/js/lib/pomeloclient.js.wss'), path.resolve(ph, 'web-server/public/js/lib/pomeloclient.js'));
  253. rmdir(path.resolve(ph, 'web-server/public/js/lib/build'));
  254. rmdir(path.resolve(ph, 'web-server/public/js/lib/local'));
  255. fs.unlinkSync(path.resolve(ph, 'web-server/public/js/lib/component.json'));
  256. break;
  257. case '5':
  258. // use socket.io wss
  259. var unlinkFiles = ['game-server/app.js.sio',
  260. 'game-server/app.js',
  261. 'game-server/app.js.wss',
  262. 'game-server/app.js.mqtt',
  263. 'game-server/app.js.sio.wss',
  264. 'web-server/app.js.https',
  265. 'web-server/public/index.html',
  266. 'web-server/public/js/lib/component.json',
  267. 'web-server/public/js/lib/pomeloclient.js.wss'];
  268. for(var i = 0; i < unlinkFiles.length; ++i) {
  269. fs.unlinkSync(path.resolve(ph, unlinkFiles[i]));
  270. }
  271. fs.renameSync(path.resolve(ph, 'game-server/app.js.udp'), path.resolve(ph, 'game-server/app.js'));
  272. rmdir(path.resolve(ph, 'web-server/public/js/lib/build'));
  273. rmdir(path.resolve(ph, 'web-server/public/js/lib/local'));
  274. break;
  275. case '6':
  276. // use socket.io
  277. var unlinkFiles = ['game-server/app.js',
  278. 'game-server/app.js.wss',
  279. 'game-server/app.js.udp',
  280. 'game-server/app.js.sio',
  281. 'game-server/app.js.sio.wss',
  282. 'web-server/app.js.https',
  283. 'web-server/public/index.html',
  284. 'web-server/public/js/lib/component.json',
  285. 'web-server/public/js/lib/pomeloclient.js.wss'];
  286. for(var i = 0; i < unlinkFiles.length; ++i) {
  287. fs.unlinkSync(path.resolve(ph, unlinkFiles[i]));
  288. }
  289. fs.renameSync(path.resolve(ph, 'game-server/app.js.mqtt'), path.resolve(ph, 'game-server/app.js'));
  290. fs.renameSync(path.resolve(ph, 'web-server/public/index.html.sio'), path.resolve(ph, 'web-server/public/index.html'));
  291. rmdir(path.resolve(ph, 'web-server/public/js/lib/build'));
  292. rmdir(path.resolve(ph, 'web-server/public/js/lib/local'));
  293. break;
  294. }
  295. var replaceFiles = ['game-server/app.js',
  296. 'game-server/package.json',
  297. 'web-server/package.json'];
  298. for(var j = 0; j < replaceFiles.length; j++) {
  299. var str = fs.readFileSync(path.resolve(ph, replaceFiles[j])).toString();
  300. fs.writeFileSync(path.resolve(ph, replaceFiles[j]), str.replace('$', name));
  301. }
  302. var f = path.resolve(ph, 'game-server/package.json');
  303. var content = fs.readFileSync(f).toString();
  304. fs.writeFileSync(f, content.replace('#', version));
  305. }, TIME_INIT);
  306. }
  307. /**
  308. * Start application.
  309. *
  310. * @param {Object} opts options for `start` operation
  311. */
  312. function start(opts) {
  313. var absScript = path.resolve(opts.directory, 'app.js');
  314. if (!fs.existsSync(absScript)) {
  315. abort(SCRIPT_NOT_FOUND);
  316. }
  317. var logDir = path.resolve(opts.directory, 'logs');
  318. if (!fs.existsSync(logDir)) {
  319. fs.mkdir(logDir);
  320. }
  321. var ls;
  322. var type = opts.type || constants.RESERVED.ALL;
  323. var params = [absScript, 'env=' + opts.env, 'type=' + type];
  324. if(!!opts.id) {
  325. params.push('startId=' + opts.id);
  326. }
  327. if (opts.daemon) {
  328. ls = spawn(process.execPath, params, {detached: true, stdio: 'ignore'});
  329. ls.unref();
  330. console.log(DAEMON_INFO);
  331. process.exit(0);
  332. } else {
  333. ls = spawn(process.execPath, params);
  334. ls.stdout.on('data', function(data) {
  335. console.log(data.toString());
  336. });
  337. ls.stderr.on('data', function(data) {
  338. console.log(data.toString());
  339. });
  340. }
  341. }
  342. /**
  343. * List pomelo processes.
  344. *
  345. * @param {Object} opts options for `list` operation
  346. */
  347. function list(opts) {
  348. var id = 'pomelo_list_' + Date.now();
  349. connectToMaster(id, opts, function(client) {
  350. client.request(co.moduleId, {signal: 'list'}, function(err, data) {
  351. if(err) {
  352. console.error(err);
  353. }
  354. var servers = [];
  355. for(var key in data.msg) {
  356. servers.push(data.msg[key]);
  357. }
  358. var comparer = function(a, b) {
  359. if (a.serverType < b.serverType) {
  360. return -1;
  361. } else if (a.serverType > b.serverType) {
  362. return 1;
  363. } else if (a.serverId < b.serverId) {
  364. return -1;
  365. } else if (a.serverId > b.serverId) {
  366. return 1;
  367. } else {
  368. return 0;
  369. }
  370. };
  371. servers.sort(comparer);
  372. var rows = [];
  373. rows.push(['serverId', 'serverType', 'pid', 'rss(M)', 'heapTotal(M)', 'heapUsed(M)', 'uptime(m)']);
  374. servers.forEach(function(server) {
  375. rows.push([server.serverId, server.serverType, server.pid, server.rss, server.heapTotal, server.heapUsed, server.uptime]);
  376. });
  377. console.log(cliff.stringifyRows(rows, ['red', 'blue', 'green', 'cyan', 'magenta', 'white', 'yellow']));
  378. process.exit(0);
  379. });
  380. });
  381. }
  382. /**
  383. * Add server to application.
  384. *
  385. * @param {Object} opts options for `add` operation
  386. */
  387. function add(opts) {
  388. var id = 'pomelo_add_' + Date.now();
  389. connectToMaster(id, opts, function(client) {
  390. client.request(co.moduleId, { signal: 'add', args: opts.args }, function(err) {
  391. if(err) {
  392. console.error(err);
  393. }
  394. else {
  395. console.info(ADD_SERVER_INFO);
  396. }
  397. process.exit(0);
  398. });
  399. });
  400. }
  401. /**
  402. * Terminal application.
  403. *
  404. * @param {String} signal stop/kill
  405. * @param {Object} opts options for `stop/kill` operation
  406. */
  407. function terminal(signal, opts) {
  408. console.info(CLOSEAPP_INFO);
  409. // option force just for `kill`
  410. if(opts.force) {
  411. if (os.platform() === constants.PLATFORM.WIN) {
  412. exec(KILL_CMD_WIN);
  413. } else {
  414. exec(KILL_CMD_LUX);
  415. }
  416. process.exit(1);
  417. return;
  418. }
  419. var id = 'pomelo_terminal_' + Date.now();
  420. connectToMaster(id, opts, function(client) {
  421. client.request(co.moduleId, {
  422. signal: signal, ids: opts.serverIds
  423. }, function(err, msg) {
  424. if(err) {
  425. console.error(err);
  426. }
  427. if(signal === 'kill') {
  428. if(msg.code === 'ok') {
  429. console.log('All the servers have been terminated!');
  430. } else {
  431. console.log('There may be some servers remained:', msg.serverIds);
  432. }
  433. }
  434. process.exit(0);
  435. });
  436. });
  437. }
  438. function restart(opts) {
  439. var id = 'pomelo_restart_' + Date.now();
  440. var serverIds = [];
  441. var type = null;
  442. if(!!opts.id) {
  443. serverIds.push(opts.id);
  444. }
  445. if(!!opts.type) {
  446. type = opts.type;
  447. }
  448. connectToMaster(id, opts, function(client) {
  449. client.request(co.moduleId, { signal: 'restart', ids: serverIds, type: type}, function(err, fails) {
  450. if(!!err) {
  451. console.error(err);
  452. } else if(!!fails.length) {
  453. console.info('restart fails server ids: %j', fails);
  454. } else {
  455. console.info(RESTART_SERVER_INFO);
  456. }
  457. process.exit(0);
  458. });
  459. });
  460. }
  461. function connectToMaster(id, opts, cb) {
  462. var client = new adminClient({username: opts.username, password: opts.password, md5: true});
  463. client.connect(id, opts.host, opts.port, function(err) {
  464. if(err) {
  465. abort(CONNECT_ERROR + err.red);
  466. }
  467. if(typeof cb === 'function') {
  468. cb(client);
  469. }
  470. });
  471. }
  472. /**
  473. * Start master slaves.
  474. *
  475. * @param {String} option for `startMasterha` operation
  476. */
  477. function startMasterha(opts) {
  478. var configFile = path.join(opts.directory, constants.FILEPATH.MASTER_HA);
  479. if(!fs.existsSync(configFile)) {
  480. abort(MASTER_HA_NOT_FOUND);
  481. }
  482. var masterha = require(configFile).masterha;
  483. for(var i=0; i<masterha.length; i++) {
  484. var server = masterha[i];
  485. server.mode = constants.RESERVED.STAND_ALONE;
  486. server.masterha = 'true';
  487. server.home = opts.directory;
  488. runServer(server);
  489. }
  490. }
  491. /**
  492. * Check if the given directory `path` is empty.
  493. *
  494. * @param {String} path
  495. * @param {Function} fn
  496. */
  497. function emptyDirectory(path, fn) {
  498. fs.readdir(path, function(err, files) {
  499. if(err && 'ENOENT' !== err.code) {
  500. abort(FILEREAD_ERROR);
  501. }
  502. fn(!files || !files.length);
  503. });
  504. }
  505. /**
  506. * Prompt confirmation with the given `msg`.
  507. *
  508. * @param {String} msg
  509. * @param {Function} fn
  510. */
  511. function confirm(msg, fn) {
  512. prompt(msg, function(val) {
  513. fn(/^ *y(es)?/i.test(val));
  514. });
  515. }
  516. /**
  517. * Prompt input with the given `msg` and callback `fn`.
  518. *
  519. * @param {String} msg
  520. * @param {Function} fn
  521. */
  522. function prompt(msg, fn) {
  523. if(' ' === msg[msg.length - 1]) {
  524. process.stdout.write(msg);
  525. } else {
  526. console.log(msg);
  527. }
  528. process.stdin.setEncoding('ascii');
  529. process.stdin.once('data', function(data) {
  530. fn(data);
  531. }).resume();
  532. }
  533. /**
  534. * Exit with the given `str`.
  535. *
  536. * @param {String} str
  537. */
  538. function abort(str) {
  539. console.error(str);
  540. process.exit(1);
  541. }
  542. /**
  543. * Copy template files to project.
  544. *
  545. * @param {String} origin
  546. * @param {String} target
  547. */
  548. function copy(origin, target) {
  549. if(!fs.existsSync(origin)) {
  550. abort(origin + 'does not exist.');
  551. }
  552. if(!fs.existsSync(target)) {
  553. mkdir(target);
  554. console.log(' create : '.green + target);
  555. }
  556. fs.readdir(origin, function(err, datalist) {
  557. if(err) {
  558. abort(FILEREAD_ERROR);
  559. }
  560. for(var i = 0; i < datalist.length; i++) {
  561. var oCurrent = path.resolve(origin, datalist[i]);
  562. var tCurrent = path.resolve(target, datalist[i]);
  563. if(fs.statSync(oCurrent).isFile()) {
  564. fs.writeFileSync(tCurrent, fs.readFileSync(oCurrent, ''), '');
  565. console.log(' create : '.green + tCurrent);
  566. } else if(fs.statSync(oCurrent).isDirectory()) {
  567. copy(oCurrent, tCurrent);
  568. }
  569. }
  570. });
  571. }
  572. /**
  573. * Mkdir -p.
  574. *
  575. * @param {String} path
  576. * @param {Function} fn
  577. */
  578. function mkdir(path, fn) {
  579. mkdirp(path, 0755, function(err){
  580. if(err) {
  581. throw err;
  582. }
  583. console.log(' create : '.green + path);
  584. if(typeof fn === 'function') {
  585. fn();
  586. }
  587. });
  588. }
  589. /**
  590. * Get user's choice on connector selecting
  591. *
  592. * @param {Function} cb
  593. */
  594. function connectorType(cb) {
  595. prompt('Please select underly connector, 1 for websocket(native socket), 2 for socket.io, 3 for wss, 4 for socket.io(wss), 5 for udp, 6 for mqtt: [1]', function(msg) {
  596. switch(msg.trim()) {
  597. case '':
  598. cb(1);
  599. break;
  600. case '1':
  601. case '2':
  602. case '3':
  603. case '4':
  604. case '5':
  605. case '6':
  606. cb(msg.trim());
  607. break;
  608. default:
  609. console.log('Invalid choice! Please input 1 - 5.'.red + '\n');
  610. connectorType(cb);
  611. break;
  612. }
  613. });
  614. }
  615. /**
  616. * Run server.
  617. *
  618. * @param {Object} server server information
  619. */
  620. function runServer(server) {
  621. var cmd, key;
  622. var main = path.resolve(server.home, 'app.js');
  623. if(utils.isLocal(server.host)) {
  624. var options = [];
  625. options.push(main);
  626. for(key in server) {
  627. options.push(util.format('%s=%s', key, server[key]));
  628. }
  629. starter.localrun(process.execPath, null, options);
  630. } else {
  631. cmd = util.format('cd "%s" && "%s"', server.home, process.execPath);
  632. cmd += util.format(' "%s" ', main);
  633. for(key in server) {
  634. cmd += util.format(' %s=%s ', key, server[key]);
  635. }
  636. starter.sshrun(cmd, server.host);
  637. }
  638. }