修改默认ssh接入为原生+xterm.js
This commit is contained in:
parent
8eb11a73a7
commit
b13ae6b049
@ -50,6 +50,7 @@ func (w *NextWriter) Read() ([]byte, int, error) {
|
||||
}
|
||||
|
||||
const (
|
||||
Connected = "connected"
|
||||
Data = "data"
|
||||
Resize = "resize"
|
||||
Closed = "closed"
|
||||
@ -61,8 +62,8 @@ type Message struct {
|
||||
}
|
||||
|
||||
type WindowSize struct {
|
||||
Height int `json:"height"`
|
||||
Width int `json:"width"`
|
||||
Cols int `json:"cols"`
|
||||
Rows int `json:"rows"`
|
||||
}
|
||||
|
||||
func SSHEndpoint(c echo.Context) error {
|
||||
@ -136,8 +137,8 @@ func SSHEndpoint(c echo.Context) error {
|
||||
}
|
||||
|
||||
msg := Message{
|
||||
Type: Data,
|
||||
Content: "Connect to server successfully.",
|
||||
Type: Connected,
|
||||
Content: "Connect to server successfully.\r\n",
|
||||
}
|
||||
_ = WriteMessage(ws, msg)
|
||||
|
||||
@ -199,7 +200,7 @@ func SSHEndpoint(c echo.Context) error {
|
||||
logrus.Warnf("解析SSH会话窗口大小失败: %v", err)
|
||||
continue
|
||||
}
|
||||
if err := session.WindowChange(winSize.Height, winSize.Height); err != nil {
|
||||
if err := session.WindowChange(winSize.Rows, winSize.Cols); err != nil {
|
||||
logrus.Warnf("更改SSH会话窗口大小失败: %v", err)
|
||||
continue
|
||||
}
|
||||
|
@ -40,6 +40,7 @@ import {isEmpty, NT_PACKAGE} from "./utils/utils";
|
||||
import {isAdmin} from "./service/permission";
|
||||
import UserGroup from "./components/user/UserGroup";
|
||||
import LoginLog from "./components/session/LoginLog";
|
||||
import AccessSSH from "./components/access/AccessSSH";
|
||||
|
||||
const {Footer, Sider} = Layout;
|
||||
|
||||
@ -113,6 +114,7 @@ class App extends Component {
|
||||
|
||||
<Switch>
|
||||
<Route path="/access" component={Access}/>
|
||||
<Route path="/access-ssh" component={AccessSSH}/>
|
||||
<Route path="/login"><Login updateUser={this.updateUser}/></Route>
|
||||
|
||||
<Route path="/">
|
||||
|
179
web/src/components/access/AccessSSH.js
Normal file
179
web/src/components/access/AccessSSH.js
Normal file
@ -0,0 +1,179 @@
|
||||
import React, {Component} from 'react';
|
||||
import "xterm/css/xterm.css"
|
||||
import {Terminal} from "xterm";
|
||||
import qs from "qs";
|
||||
import {wsServer} from "../../common/constants";
|
||||
import "./Console.css"
|
||||
import {getToken} from "../../utils/utils";
|
||||
import {FitAddon} from 'xterm-addon-fit';
|
||||
|
||||
class AccessSSH extends Component {
|
||||
|
||||
state = {
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
term: undefined,
|
||||
webSocket: undefined,
|
||||
fitAddon: undefined
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
|
||||
let urlParams = new URLSearchParams(this.props.location.search);
|
||||
let assetId = urlParams.get('assetId');
|
||||
|
||||
let params = {
|
||||
'width': this.state.width,
|
||||
'height': this.state.height,
|
||||
'assetId': assetId
|
||||
};
|
||||
|
||||
let paramStr = qs.stringify(params);
|
||||
|
||||
let term = new Terminal({
|
||||
fontFamily: 'monaco, Consolas, "Lucida Console", monospace',
|
||||
fontSize: 14,
|
||||
theme: {
|
||||
background: '#1b1b1b',
|
||||
lineHeight: 17
|
||||
},
|
||||
rightClickSelectsWord: true,
|
||||
});
|
||||
|
||||
term.open(this.refs.terminal);
|
||||
const fitAddon = new FitAddon();
|
||||
term.loadAddon(fitAddon);
|
||||
fitAddon.fit();
|
||||
term.focus();
|
||||
|
||||
term.writeln('Trying to connect to the server ...');
|
||||
|
||||
term.onSelectionChange(async () => {
|
||||
let selection = term.getSelection();
|
||||
this.setState({
|
||||
selection: selection
|
||||
})
|
||||
if (navigator.clipboard) {
|
||||
await navigator.clipboard.writeText(selection);
|
||||
}
|
||||
});
|
||||
|
||||
term.attachCustomKeyEventHandler((e) => {
|
||||
if (e.ctrlKey && e.key === 'c' && this.state.selection) {
|
||||
return false;
|
||||
}
|
||||
return !(e.ctrlKey && e.key === 'v');
|
||||
});
|
||||
|
||||
term.onData(data => {
|
||||
let webSocket = this.state.webSocket;
|
||||
if (webSocket !== undefined) {
|
||||
webSocket.send(JSON.stringify({type: 'data', content: data}));
|
||||
}
|
||||
});
|
||||
|
||||
let token = getToken();
|
||||
|
||||
let webSocket = new WebSocket(wsServer + '/ssh?X-Auth-Token=' + token + '&' + paramStr);
|
||||
|
||||
let pingInterval;
|
||||
webSocket.onopen = (e => {
|
||||
pingInterval = setInterval(() => {
|
||||
webSocket.send(JSON.stringify({type: 'ping'}))
|
||||
}, 5000);
|
||||
|
||||
let terminalSize = {
|
||||
cols: term.cols,
|
||||
rows: term.rows
|
||||
}
|
||||
webSocket.send(JSON.stringify({type: 'resize', content: JSON.stringify(terminalSize)}));
|
||||
});
|
||||
|
||||
webSocket.onerror = (e) => {
|
||||
term.writeln("Failed to connect to server.");
|
||||
}
|
||||
webSocket.onclose = (e) => {
|
||||
term.writeln("Connection is closed.");
|
||||
if (pingInterval) {
|
||||
clearInterval(pingInterval);
|
||||
}
|
||||
}
|
||||
|
||||
webSocket.onmessage = (e) => {
|
||||
let msg = JSON.parse(e.data);
|
||||
switch (msg['type']) {
|
||||
case 'connected':
|
||||
console.log(msg['content'])
|
||||
this.onWindowResize();
|
||||
break;
|
||||
case 'data':
|
||||
term.write(msg['content']);
|
||||
break;
|
||||
case 'closed':
|
||||
term.writeln(`\x1B[1;3;31m${msg['content']}\x1B[0m `)
|
||||
webSocket.close();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({
|
||||
term: term,
|
||||
webSocket: webSocket,
|
||||
fitAddon: fitAddon
|
||||
});
|
||||
|
||||
window.addEventListener('resize', this.onWindowResize);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
let webSocket = this.state.webSocket;
|
||||
if (webSocket) {
|
||||
webSocket.close()
|
||||
}
|
||||
}
|
||||
|
||||
terminalSize() {
|
||||
return {
|
||||
cols: Math.floor(this.state.width / 7.5),
|
||||
rows: Math.floor(window.innerHeight / 17),
|
||||
}
|
||||
}
|
||||
|
||||
onWindowResize = (e) => {
|
||||
let term = this.state.term;
|
||||
let fitAddon = this.state.fitAddon;
|
||||
let webSocket = this.state.webSocket;
|
||||
|
||||
this.setState({
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
}, () => {
|
||||
if (webSocket && webSocket.readyState === WebSocket.OPEN) {
|
||||
fitAddon.fit();
|
||||
term.focus();
|
||||
let terminalSize = {
|
||||
cols: term.cols,
|
||||
rows: term.rows
|
||||
}
|
||||
webSocket.send(JSON.stringify({type: 'resize', content: JSON.stringify(terminalSize)}));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<div ref='terminal' id='terminal' style={{
|
||||
height: this.state.height,
|
||||
width: this.state.width,
|
||||
overflowX: 'hidden',
|
||||
overflowY: 'hidden'
|
||||
}}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default AccessSSH;
|
@ -319,7 +319,11 @@ class Asset extends Component {
|
||||
if (result.code === 1) {
|
||||
if (result.data === true) {
|
||||
message.success({content: '检测完成,您访问的资产在线,即将打开窗口进行访问。', key: id, duration: 3});
|
||||
if(protocol === 'ssh'){
|
||||
window.open(`#/access-ssh?assetId=${id}`);
|
||||
}else {
|
||||
window.open(`#/access?assetsId=${id}&protocol=${protocol}`);
|
||||
}
|
||||
} else {
|
||||
message.warn('您访问的资产未在线,请确认网络状态。', 10);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user