From 74cad45856d253961079a3c3f62fce74cd46997a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=91=A8=E8=85=BE?= Date: Wed, 9 Jun 2021 07:50:01 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8A=BD=E5=8F=96=E5=88=B0service=E5=B1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- back/api/terminal.ts | 106 ++++---------------------------- back/services/terminal.ts | 123 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 134 insertions(+), 95 deletions(-) create mode 100644 back/services/terminal.ts diff --git a/back/api/terminal.ts b/back/api/terminal.ts index e0438d65..881b4ffe 100644 --- a/back/api/terminal.ts +++ b/back/api/terminal.ts @@ -1,113 +1,29 @@ import { Router } from 'express'; -import * as pty from 'node-pty'; -import os from 'os'; +import Container from 'typedi'; +import { Logger } from 'winston'; +import TerminalService from '../services/terminal'; // Whether to use binary transport. -const USE_BINARY = os.platform() !== 'win32'; const route = Router(); export default (app: Router) => { - const terminals = {}; - const logs = {}; + const logger: Logger = Container.get('logger'); + app.use('/', route); route.post('/terminals', (req, res) => { - const env = Object.assign({}, process.env); - env['COLORTERM'] = 'truecolor'; - var cols = parseInt(req.query.cols), - rows = parseInt(req.query.rows), - term = pty.spawn(process.platform === 'win32' ? 'cmd.exe' : 'bash', [], { - name: 'xterm-256color', - cols: cols || 80, - rows: rows || 24, - cwd: process.platform === 'win32' ? undefined : env.PWD, - env: env, - encoding: USE_BINARY ? null : 'utf8', - }); - - console.log('Created terminal with PID: ' + term.pid); - terminals[term.pid] = term; - logs[term.pid] = ''; - term.on('data', function (data) { - logs[term.pid] += data; - }); + const terminalService = Container.get(TerminalService); + const term = terminalService.createTerminal(req); res.send(term.pid.toString()); res.end(); }); route.post('/terminals/:pid/size', (req, res) => { - var pid = parseInt(req.params.pid), - cols = parseInt(req.query.cols), - rows = parseInt(req.query.rows), - term = terminals[pid]; - - term.resize(cols, rows); - console.log( - 'Resized terminal ' + - pid + - ' to ' + - cols + - ' cols and ' + - rows + - ' rows.', - ); + const terminalService = Container.get(TerminalService); + terminalService.resizeTerminal(req); res.end(); }); route.ws('/terminals/:pid', function (ws, req) { - var term = terminals[parseInt(req.params.pid)]; - console.log('Connected to terminal ' + term.pid); - ws.send(logs[term.pid]); - - // string message buffering - function buffer(socket, timeout) { - let s = ''; - let sender = null; - return (data) => { - s += data; - if (!sender) { - sender = setTimeout(() => { - socket.send(s); - s = ''; - sender = null; - }, timeout); - } - }; - } - // binary message buffering - function bufferUtf8(socket, timeout) { - let buffer = []; - let sender = null; - let length = 0; - return (data) => { - buffer.push(data); - length += data.length; - if (!sender) { - sender = setTimeout(() => { - socket.send(Buffer.concat(buffer, length)); - buffer = []; - sender = null; - length = 0; - }, timeout); - } - }; - } - const send = USE_BINARY ? bufferUtf8(ws, 5) : buffer(ws, 5); - - term.on('data', function (data) { - try { - send(data); - } catch (ex) { - // The WebSocket is not open, ignore - } - }); - ws.on('message', function (msg) { - term.write(msg); - }); - ws.on('close', function () { - term.kill(); - console.log('Closed terminal ' + term.pid); - // Clean things up - delete terminals[term.pid]; - delete logs[term.pid]; - }); + const terminalService = Container.get(TerminalService); + terminalService.listenTerminal(ws, req); }); }; diff --git a/back/services/terminal.ts b/back/services/terminal.ts new file mode 100644 index 00000000..1833ea32 --- /dev/null +++ b/back/services/terminal.ts @@ -0,0 +1,123 @@ +import { Inject, Service } from 'typedi'; +import os from 'os'; +import * as pty from 'node-pty'; +import winston from 'winston'; + +const USE_BINARY = os.platform() !== 'win32'; + +@Service() +export default class TerminalService { + private terminals: Record; + private logs: Record; + constructor(@Inject('logger') private logger: winston.Logger) { + this.terminals = {}; + this.logs = {}; + } + + createTerminal(req) { + const that = this; + const env = Object.assign({}, process.env); + env['COLORTERM'] = 'truecolor'; + const cols = parseInt(req.query.cols); + const rows = parseInt(req.query.rows); + const term = pty.spawn( + process.platform === 'win32' ? 'cmd.exe' : 'bash', + [], + { + name: 'xterm-256color', + cols: cols || 80, + rows: rows || 24, + cwd: process.platform === 'win32' ? undefined : env.PWD, + env: env, + encoding: USE_BINARY ? null : 'utf8', + }, + ); + + that.logger.silly('Created terminal with PID: ' + term.pid); + that.terminals[term.pid] = term; + that.logs[term.pid] = ''; + term.on('data', function (data) { + that.logs[term.pid] += data; + }); + return term; + } + + resizeTerminal(req) { + const that = this; + const pid = parseInt(req.params.pid); + const cols = parseInt(req.query.cols); + const rows = parseInt(req.query.rows); + const term = that.terminals[pid]; + + term.resize(cols, rows); + that.logger.silly( + 'Resized terminal ' + + pid + + ' to ' + + cols + + ' cols and ' + + rows + + ' rows.', + ); + } + + listenTerminal(ws, req) { + const that = this; + var term = that.terminals[parseInt(req.params.pid)]; + that.logger.silly('Connected to terminal ' + term.pid); + ws.send(that.logs[term.pid]); + + // string message buffering + function buffer(socket, timeout) { + let s = ''; + let sender = null; + return (data) => { + s += data; + if (!sender) { + sender = setTimeout(() => { + socket.send(s); + s = ''; + sender = null; + }, timeout); + } + }; + } + // binary message buffering + function bufferUtf8(socket, timeout) { + let buffer = []; + let sender = null; + let length = 0; + return (data) => { + buffer.push(data); + length += data.length; + if (!sender) { + sender = setTimeout(() => { + socket.send(Buffer.concat(buffer, length)); + buffer = []; + sender = null; + length = 0; + }, timeout); + } + }; + } + const send = USE_BINARY ? bufferUtf8(ws, 5) : buffer(ws, 5); + + term.on('data', function (data) { + try { + send(data); + } catch (ex) { + // The WebSocket is not open, ignore + } + }); + ws.on('message', function (msg) { + term.write(msg); + }); + ws.on('close', function () { + term.kill(); + that.logger.silly('Closed terminal ' + term.pid); + // Clean things up + delete that.terminals[term.pid]; + delete that.logs[term.pid]; + }); + } +}