import React, {Component} from 'react'; import "xterm/css/xterm.css" import {Terminal} from "xterm"; import qs from "qs"; import {wsServer} from "../../common/env"; import {getToken, isEmpty} from "../../utils/utils"; import {FitAddon} from 'xterm-addon-fit'; import "./Access.css" import request from "../../common/request"; import {Affix, Button, Drawer, Dropdown, Menu, message, Modal, Tooltip} from "antd"; import {CodeOutlined, ExclamationCircleOutlined, FolderOutlined, LineChartOutlined} from "@ant-design/icons"; import Draggable from "react-draggable"; import FileSystem from "../devops/FileSystem"; import Stats from "./Stats"; import Message from "./Message"; class Term extends Component { statsRef = undefined; state = { width: window.innerWidth, height: window.innerHeight, term: undefined, webSocket: undefined, fitAddon: undefined, sessionId: undefined, session: {}, enterBtnIndex: 1001, commands: [] }; componentDidMount = async () => { let urlParams = new URLSearchParams(this.props.location.search); let assetId = urlParams.get('assetId'); document.title = urlParams.get('assetName'); let session = await this.createSession(assetId); if (!session) { return; } let sessionId = session['id']; if (isEmpty(sessionId)) { return; } let term = new Terminal({ fontFamily: 'monaco, Consolas, "Lucida Console", monospace', fontSize: 15, theme: { background: '#1b1b1b' }, rightClickSelectsWord: true, }); term.open(document.getElementById('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(); console.log(`selection: [${selection}]`); 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(new Message(Message.Data, data).toString()); } }); let token = getToken(); let params = { 'cols': term.cols, 'rows': term.rows, 'sessionId': sessionId, 'X-Auth-Token': token }; let paramStr = qs.stringify(params); let webSocket = new WebSocket(wsServer + '/ssh?' + paramStr); let pingInterval; webSocket.onopen = (e => { pingInterval = setInterval(() => { webSocket.send(new Message(Message.Ping, "").toString()); }, 1000); }); 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 = Message.parse(e.data); switch (msg['type']) { case Message.Connected: term.clear(); this.updateSessionStatus(sessionId); this.getCommands(); break; case Message.Data: term.write(msg['content']); break; case Message.Closed: term.writeln(`\x1B[1;3;31m${msg['content']}\x1B[0m `) webSocket.close(); break; default: break; } } this.setState({ term: term, webSocket: webSocket, fitAddon: fitAddon, sessionId: sessionId, session: session }); window.addEventListener('resize', this.onWindowResize); window.onunload = function () { webSocket.close(); }; } componentWillUnmount() { let webSocket = this.state.webSocket; if (webSocket) { webSocket.close() } } getCommands = async () => { let result = await request.get('/commands'); if (result.code !== 1) { message.error(result.message); return; } this.setState({ commands: result['data'] }) } showMessage(msg) { message.destroy(); Modal.confirm({ title: '提示', icon: , content: msg, centered: true, okText: '重新连接', cancelText: '关闭页面', onOk() { window.location.reload(); }, onCancel() { window.close(); }, }); } async createSession(assetsId) { let result = await request.post(`/sessions?assetId=${assetsId}&mode=naive`); if (result['code'] !== 1) { this.showMessage(result['message']); return undefined; } return result['data']; } updateSessionStatus = async (sessionId) => { let result = await request.post(`/sessions/${sessionId}/connect`); if (result['code'] !== 1) { message.error(result['message']); } } 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(); this.focus(); let terminalSize = { cols: term.cols, rows: term.rows } webSocket.send(new Message(Message.Resize, window.btoa(JSON.stringify(terminalSize))).toString()); } }); }; writeCommand = (command) => { let webSocket = this.state.webSocket; if (webSocket !== undefined) { webSocket.send(new Message(Message.Data, command)); } this.focus(); } focus = () => { let term = this.state.term; if (term) { term.focus(); } } onRef = (statsRef) => { this.statsRef = statsRef; } render() { const cmdMenuItems = this.state.commands.map(item => { return { this.writeCommand(item['content']) }} key={'i-' + item['id']}>{item['name']} ; }); const cmdMenu = ( {cmdMenuItems} ); return (
); } } export default Term;