hexo-shell.js 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. /*
  2. * This file is part of HexoEditor.
  3. *
  4. * Copyright (c) 2018 zhuzhuyule
  5. */
  6. var shellServer = (function () {
  7. 'use strict'
  8. const Promise = require('bluebird');
  9. const thread_kill = require('./thread_kill');
  10. const exec = require('child_process').exec;
  11. const util = require('util');
  12. const log = log4js.getLogger('hexo-shell.js')
  13. let _shellServer = false;
  14. class ShellServer {
  15. constructor() {
  16. this.shellProcess = null;
  17. this.isForce = false;
  18. this.oldbiu = null;
  19. this.drags = null;
  20. _shellServer = this;
  21. }
  22. processRunning() {
  23. return (this.shellProcess && this.shellProcess != null)
  24. }
  25. sendConsole(content, type, btnTip) {
  26. if (_shellServer.closeMsg) return;
  27. try {
  28. _shellServer.lastWindow.hexoeditorWindow.window.webContents.send('pop-message-shell', {
  29. subProcess: this.shellProcess,
  30. content: content,
  31. type: type,
  32. btnTip: btnTip,
  33. });
  34. } catch (e) {
  35. content.error(e)
  36. }
  37. }
  38. execCmd(command) {
  39. let flagOK = false;
  40. clearTimeout(_shellServer.timeID);
  41. _shellServer.closeMsg = false;
  42. _shellServer.lastWindow = require('electron').BrowserWindow.getFocusedWindow();
  43. _shellServer.isForce = false;
  44. log.info('Begin execute:', `[${moeApp.hexo.config.__basedir}] [${command}]`);
  45. _shellServer.shellProcess = exec(command, {cwd: moeApp.hexo.config.__basedir});
  46. _shellServer.sendConsole('<i class="fa fa-spinner fa-pulse fa-fw margin-bottom"></i>' + __("Executing"), 'info', 'ban');
  47. _shellServer.shellProcess.stderr.on('data', (data) => {
  48. log.error(data);
  49. });
  50. _shellServer.shellProcess.stdout.on('data', (data) => {
  51. clearTimeout(_shellServer.timeID);
  52. log.debug(data);
  53. if (/INFO Hexo is running at https?:\/+/.test(data)) {
  54. flagOK = true;
  55. _shellServer.sendConsole('<i class="fa fa-spinner fa-pulse fa-fw margin-bottom"></i>' + __('ServerStart'), 'info', 'stop');
  56. require('electron').shell.openExternal(data.match(/INFO Hexo is running at (https?:\/+[^\/]+\/). Press Ctrl.C to stop./i)[1])
  57. } else {
  58. _shellServer.timeID = setTimeout(() => {
  59. if (!flagOK) {
  60. _shellServer.kill(_shellServer.shellProcess)
  61. flagOK = -1;
  62. }
  63. }, 10000)
  64. }
  65. });
  66. _shellServer.shellProcess.on('close', (code, signal) => {
  67. log.info('End execute:', `[${moeApp.hexo.config.__basedir}] [${command}]`);
  68. if (flagOK === -1)
  69. _shellServer.sendConsole(__('Operation Execution Timeout'), 'danger', 'close');
  70. else if (_shellServer.isForce)
  71. _shellServer.sendConsole(__('Operation Canceled'), 'success', 'check');
  72. else if (code == 0)
  73. _shellServer.sendConsole(__('Operation Finished'), 'success', 'check');
  74. _shellServer.shellProcess = null;
  75. });
  76. _shellServer.shellProcess.on('error', err => {
  77. if (_shellServer.shellProcess)
  78. _shellServer.sendConsole(err, 'danger', 'close')
  79. log.error(err);
  80. })
  81. }
  82. kill(subProcess) {
  83. _shellServer.isForce = true;
  84. _shellServer.closeMsg = (typeof subProcess === "boolean");
  85. if (!subProcess)
  86. subProcess = _shellServer.shellProcess
  87. if (subProcess)
  88. thread_kill(subProcess.pid, function (err) {
  89. log.error(err);
  90. });
  91. }
  92. checkPort(ip, port) {
  93. _shellServer.isForce = false;
  94. _shellServer.closeMsg = false;
  95. return new Promise(function (resolve, reject) {
  96. if (port > 65535 || port < 1) {
  97. return reject(new Error('Port number ' + port + ' is invalid. Try a number between 1 and 65535.'));
  98. }
  99. var server = require('net').createServer();
  100. server.once('error', reject);
  101. server.once('listening', function () {
  102. server.close();
  103. resolve(`${ip}:${port}`);
  104. });
  105. server.listen(port, ip);
  106. });
  107. }
  108. serverFail(err) {
  109. if (err.code === 'EADDRINUSE') { // 端口Ip地址占用
  110. _shellServer.sendConsole(util.format(__('PortOccupied'), err.address, err.port), 'danger', 'close');
  111. }
  112. }
  113. server() {
  114. this.checkPort(moeApp.hexo.config.server.ip, moeApp.hexo.config.server.port)
  115. .then(this.execCmd('hexo s'), this.serverFail);
  116. }
  117. clean() {
  118. Promise.resolve()
  119. .then(this.execCmd('hexo clean'));
  120. }
  121. general() {
  122. Promise.resolve()
  123. .then(this.execCmd('hexo g'));
  124. }
  125. deploy() {
  126. Promise.resolve()
  127. .then(this.execCmd('hexo d'));
  128. }
  129. generalAndDeploy() {
  130. Promise.resolve()
  131. .then(this.execCmd('hexo g -d'));
  132. }
  133. stopServerForce() {
  134. const port = moeApp.hexo.config.server.port;
  135. const ip = moeApp.hexo.config.server.ip;
  136. let command = '';
  137. switch (process.platform) {
  138. case 'win32':
  139. command = 'netstat -nao | findstr ';
  140. break;
  141. case 'darwin':
  142. command = 'lsof -i:';
  143. break;
  144. default:
  145. command = 'netstat -anp|grep ';
  146. break;
  147. }
  148. this.execCmd(command + port);
  149. let portList = [];
  150. this.shellProcess.stdout.on('data', (data) => {
  151. let reg;
  152. switch (process.platform) {
  153. case 'win32':
  154. reg = new RegExp(util.format('TCP\\s+%s:%s\\s+\\d+.\\d+.\\d+.\\d+:\\d+\\s+LISTENING\\s+(\\d+)', ip, port), 'i');
  155. break;
  156. case 'darwin':
  157. reg = new RegExp('\\w+\\s+(\\d+)\\s+', 'i');
  158. break;
  159. default:
  160. reg = new RegExp(util.format('tcp\\s+\\d+\\s+\\d*\\s+%s:%s\\s+\\d+.\\d+.\\d+.\\d+:[*\\d]+\\s+LISTEN\\s+(\\d+)', ip, port), 'i');
  161. break;
  162. }
  163. if (reg.test(data)) {
  164. let pid = data.match(reg)[1]
  165. if (pid)
  166. portList.push(pid)
  167. }
  168. });
  169. this.shellProcess.on('close', (code, signal) => {
  170. if (portList.length > 0) {
  171. for (let i = 0, len = portList.length; i < len; i++) {
  172. thread_kill(portList[i])
  173. }
  174. }
  175. this.sendConsole(__('Operation Finished'), 'success', 'check');
  176. this.shellProcess = null;
  177. });
  178. }
  179. }
  180. return ShellServer;
  181. })();
  182. module.exports = shellServer;