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