代码拉取完成,页面将自动刷新
后端基于基于node
,express
,ssh2
,websocket
前端基于react
,xterm.js
项目代码:node-ssh
npm
包yarn add express-ws ssh2 utf8
src/utils/createNewServer.ts
const SSHClient = require('ssh2').Client;
const utf8 = require('utf8');
export const createNewServer = (machineConfig: any, socket: any) => {
const ssh = new SSHClient();
const { host, username, password } = machineConfig;
// 连接成功
ssh.on('ready', function () {
socket.send('\r\n*** SSH CONNECTION SUCCESS ***\r\n');
ssh.shell(function (err: any, stream: any) {
// 出错
if (err) {
return socket.send('\r\n*** SSH SHELL ERROR: ' + err.message + ' ***\r\n');
}
// 前端发送消息
socket.on('message', function (data: any) {
stream.write(data);
});
// 通过sh发送消息给前端
stream.on('data', function (d: any) {
socket.send(utf8.decode(d.toString('binary')));
// 关闭连接
}).on('close', function () {
ssh.end();
});
})
// 关闭连接
}).on('close', function () {
socket.send('\r\n*** SSH CONNECTION CLOSED ***\r\n');
// 连接错误
}).on('error', function (err: any) {
socket.send('\r\n*** SSH CONNECTION ERROR: ' + err.message + ' ***\r\n');
// 连接
}).connect({
port: 22,
host,
username,
password
});
}
src/main.ts
const express = require('express');
const app = express();
const expressWs = require('express-ws')(app);
import { createNewServer } from './utils/createNewServer';
app.get('/', function (req: any, res: any, next: any) {
res.end();
});
app.ws('/', function (ws: any, req: any) {
createNewServer({
host: '192.168.2.94',
username: 'megaium',
password: 'Megaium!'
}, ws)
});
app.listen(3000)
*.ts
import React, { useEffect, useState } from 'react';
import { Terminal } from 'xterm';
import { WebLinksAddon } from 'xterm-addon-web-links';
import { FitAddon } from 'xterm-addon-fit';
import 'xterm/css/xterm.css';
import styles from './index.less';
const FontSize: number = 14;
const Col = 80;
const WebTerminal = () => {
const [webTerminal, setWebTerminal] = useState<Terminal | null>(null);
const [ws, setWs] = useState<WebSocket | null>(null);
useEffect(() => {
// 新增监听事件
if (webTerminal && ws) {
// 监听
webTerminal.onKey(e => {
const { key } = e;
ws.send(key);
});
// ws监听
ws.onmessage = e => {
console.log(e);
if (webTerminal) {
if (typeof e.data === 'string') {
webTerminal.write(e.data);
} else {
console.error('格式错误');
}
}
};
}
}, [webTerminal, ws]);
useEffect(() => {
// 初始化终端
const ele = document.getElementById('terminal');
if (ele) {
const height = ele.clientHeight;
// 初始化
const terminal = new Terminal({
cursorBlink: true,
cols: Col,
rows: Math.ceil(height / FontSize),
});
// 辅助
const fitAddon = new FitAddon();
terminal.loadAddon(new WebLinksAddon());
terminal.loadAddon(fitAddon);
terminal.open(ele);
terminal.write('Hello from \x1B[1;3;31mxterm.js\x1B[0m $ ');
fitAddon.fit();
setWebTerminal(terminal);
}
// 初始化ws连接
if (ws) ws.close();
const socket = new WebSocket('ws://192.168.2.123:3000');
socket.onopen = () => {
socket.send('connect success');
};
setWs(socket);
}, []);
return <div id="terminal" className={styles.main} />;
};
export default WebTerminal;
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。