moe-shell.js 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. /*
  2. * This file is part of Moeditor.
  3. *
  4. * Copyright (c) 2016 Menci <huanghaorui301@gmail.com>
  5. * Copyright (c) 2016 lucaschimweg
  6. * Copyright (c) 2016 douglaseccker
  7. * Copyright (c) 2016 PifyZ
  8. * Copyright (c) 2016 Hyuchia
  9. * Copyright (c) 2016 welksonramos
  10. * Copyright (c) 2016 caiocdasilva
  11. * Copyright (c) 2016 lawgsy <lawgsy@gmail.com>
  12. *
  13. * Moeditor is free software: you can redistribute it and/or modify
  14. * it under the terms of the GNU General Public License as published by
  15. * the Free Software Foundation, either version 3 of the License, or
  16. * (at your option) any later version.
  17. *
  18. * Moeditor is distributed in the hope that it will be useful,
  19. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  20. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  21. * GNU General Public License for more details.
  22. *
  23. * You should have received a copy of the GNU General Public License
  24. * along with Moeditor. If not, see <http://www.gnu.org/licenses/>.
  25. *
  26. * The translation providers:
  27. * - en: Menci
  28. * - zh_CN: Menci
  29. * - de: lucaschimweg
  30. * - pt: douglaseccker & welksonramos & caiocdasilva
  31. * - fr: PifyZ
  32. * - es: Hyuchia
  33. * - nl: lawgsy
  34. * - it: iamsisar
  35. *
  36. * If you want to help translate this app, please copy the `en` items of the
  37. * `strings` constant as the template, and fill each item with the translated
  38. * string. The `_name` item in a language is the language's name.
  39. *
  40. * You can translate for a language (e.g. en) or a language with specified
  41. * region (e.g. zh_CN). The app will choose the language with specified region
  42. * first, fallback to only language when the former is unavailable, and fallback
  43. * to English when they are all unavailable.
  44. *
  45. * Send a PR to us after translating.
  46. *
  47. */
  48. 'use strict'
  49. const Promise = require('bluebird');
  50. const {ipcMain} = require('electron');
  51. const thread_kill = require('./tool/thread_kill');
  52. const exec = require('child_process').exec;
  53. const util = require('util');
  54. class ShellServer {
  55. constructor() {
  56. this.shellProcess = null;
  57. this.isForce = false;
  58. this.oldbiu = null;
  59. this.drags = null;
  60. }
  61. processRunning() {
  62. return (this.shellProcess && this.shellProcess != null)
  63. }
  64. sendConsole(content, type, btnTip) {
  65. if (moeApp.shellServer.closeMsg) return;
  66. try {
  67. moeApp.shellServer.lastWindow.moeditorWindow.window.webContents.send('pop-message-shell', {
  68. subProcess: this.shellProcess,
  69. content: content,
  70. type: type,
  71. btnTip: btnTip,
  72. });
  73. } catch (e) {
  74. content.error(e)
  75. }
  76. }
  77. execCmd(command) {
  78. console.log('execute', command);
  79. let flagOK = false;
  80. clearTimeout(moeApp.shellServer.timeID);
  81. moeApp.shellServer.closeMsg = false;
  82. moeApp.shellServer.lastWindow = require('electron').BrowserWindow.getFocusedWindow();
  83. moeApp.shellServer.isForce = false;
  84. moeApp.shellServer.shellProcess = exec(command, {cwd: moeApp.hexo.config.__basedir});
  85. moeApp.shellServer.sendConsole('<i class="fa fa-spinner fa-pulse fa-fw margin-bottom"></i>'+__("Executing"), 'info', 'ban');
  86. moeApp.shellServer.shellProcess.stderr.on('data', (data) => {
  87. console.log(data);
  88. });
  89. moeApp.shellServer.shellProcess.stdout.on('data', (data) => {
  90. clearTimeout(moeApp.shellServer.timeID);
  91. if (flagOK || (/INFO Hexo is running at https?:\/+/.test(data)) ) {
  92. flagOK = true;
  93. moeApp.shellServer.sendConsole('<i class="fa fa-spinner fa-pulse fa-fw margin-bottom"></i>'+__('ServerStart'), 'info', 'stop');
  94. require('electron').shell.openExternal(data.match(/INFO Hexo is running at (https?:\/+[^\/]+\/). Press Ctrl.C to stop./i)[1])
  95. } else {
  96. moeApp.shellServer.timeID = setTimeout(() => {
  97. if (!flagOK) {
  98. moeApp.shellServer.kill(moeApp.shellServer.shellProcess)
  99. flagOK = -1;
  100. }
  101. }, 10000)
  102. }
  103. console.log(data);
  104. });
  105. moeApp.shellServer.shellProcess.on('close', (code, signal) => {
  106. if (flagOK === -1)
  107. moeApp.shellServer.sendConsole(__('Operation Execution Timeout'), 'danger', 'close');
  108. else if (moeApp.shellServer.isForce)
  109. moeApp.shellServer.sendConsole(__('Operation Canceled'), 'success', 'check');
  110. else if (code == 0)
  111. moeApp.shellServer.sendConsole(__('Operation Finished'), 'success', 'check');
  112. moeApp.shellServer.shellProcess = null;
  113. });
  114. moeApp.shellServer.shellProcess.on('error', err => {
  115. if (moeApp.shellServer.shellProcess)
  116. moeApp.shellServer.sendConsole(err, 'danger', 'close')
  117. console.error(err);
  118. })
  119. }
  120. kill(subProcess) {
  121. moeApp.shellServer.isForce = true;
  122. moeApp.shellServer.closeMsg = (typeof subProcess === "boolean");
  123. if (!subProcess)
  124. subProcess = moeApp.shellServer.shellProcess
  125. if (subProcess)
  126. thread_kill(subProcess.pid, function (err) {
  127. console.error(err);
  128. });
  129. }
  130. checkPort(ip, port) {
  131. moeApp.shellServer.isForce = false;
  132. moeApp.shellServer.closeMsg = false;
  133. return new Promise(function (resolve, reject) {
  134. if (port > 65535 || port < 1) {
  135. return reject(new Error('Port number ' + port + ' is invalid. Try a number between 1 and 65535.'));
  136. }
  137. var server = require('net').createServer();
  138. server.once('error', reject);
  139. server.once('listening', function () {
  140. server.close();
  141. resolve(`${ip}:${port}`);
  142. });
  143. server.listen(port, ip);
  144. });
  145. }
  146. serverFail(err) {
  147. if (err.code === 'EADDRINUSE') { // 端口Ip地址占用
  148. moeApp.shellServer.sendConsole(util.format(__('PortOccupied'),err.address,err.port), 'danger', 'close');
  149. }
  150. }
  151. server() {
  152. this.checkPort(moeApp.hexo.config.server.ip, moeApp.hexo.config.server.port)
  153. .then(this.execCmd('hexo s'), this.serverFail);
  154. }
  155. clean() {
  156. Promise.resolve()
  157. .then(this.execCmd('hexo clean'));
  158. }
  159. general() {
  160. Promise.resolve()
  161. .then(this.execCmd('hexo g'));
  162. }
  163. deploy() {
  164. Promise.resolve()
  165. .then(this.execCmd('hexo d'));
  166. }
  167. generalAndDeploy() {
  168. Promise.resolve()
  169. .then(this.execCmd('hexo g -d'));
  170. }
  171. stopServerForce() {
  172. const port = moeApp.hexo.config.server.port;
  173. const ip = moeApp.hexo.config.server.ip;
  174. let command = '';
  175. switch (process.platform) {
  176. case 'win32':
  177. command = 'netstat -nao | findstr ';
  178. break;
  179. case 'darwin':
  180. command = 'netstat -anp|grep ';
  181. break;
  182. default:
  183. command = 'netstat -anp|grep ';
  184. break;
  185. }
  186. this.execCmd(command + port);
  187. let portList = [];
  188. this.shellProcess.stdout.on('data', (data) => {
  189. let reg;
  190. switch (process.platform) {
  191. case 'win32':
  192. reg = new RegExp(util.format('TCP\\s+%s:%s\\s+\\d+.\\d+.\\d+.\\d+:\\d+\\s+LISTENING\\s+(\\d+)', ip, port),'i');
  193. break;
  194. case 'darwin':
  195. 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');
  196. break;
  197. default:
  198. 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');
  199. break;
  200. }
  201. if (reg.test(data)) {
  202. let pid = data.match(reg)[1]
  203. if (pid)
  204. portList.push(pid)
  205. }
  206. });
  207. this.shellProcess.on('close', (code, signal) => {
  208. if (portList.length > 0) {
  209. for (let i = 0, len = portList.length; i < len; i++) {
  210. thread_kill(portList[i])
  211. }
  212. }
  213. this.sendConsole(__('Operation Finished'), 'success', 'check');
  214. this.shellProcess = null;
  215. });
  216. }
  217. }
  218. module.exports = ShellServer;