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