/** * Copyright (c) OpenSpug Organization. https://github.com/openspug/spug * Copyright (c) * Released under the AGPL-3.0 License. */ import React, { useEffect, useRef, useState } from 'react'; import { observer } from 'mobx-react'; import { PageHeader } from 'antd'; import { LoadingOutlined, CheckCircleOutlined, ExclamationCircleOutlined, CodeOutlined, ClockCircleOutlined, } from '@ant-design/icons'; import { FitAddon } from 'xterm-addon-fit'; import { Terminal } from 'xterm'; import style from './index.module.less'; import { http, X_TOKEN } from 'libs'; import store from './store'; import gStore from 'gStore'; let gCurrent; function OutView(props) { const el = useRef() const [term] = useState(new Terminal()); const [fitPlugin] = useState(new FitAddon()); const [current, setCurrent] = useState(Object.keys(store.outputs)[0]) useEffect(() => { store.tag = '' gCurrent = current term.setOption('disableStdin', true) term.setOption('fontSize', gStore.terminal.fontSize) term.setOption('fontFamily', gStore.terminal.fontFamily) term.setOption('theme', {background: '#2b2b2b', foreground: '#A9B7C6', cursor: '#2b2b2b'}) term.attachCustomKeyEventHandler((arg) => { if (arg.ctrlKey && arg.code === 'KeyC' && arg.type === 'keydown') { document.execCommand('copy') return false } return true }) term.loadAddon(fitPlugin) term.open(el.current) fitPlugin.fit() term.write('\x1b[36m### WebSocket connecting ...\x1b[0m') const resize = () => fitPlugin.fit(); window.addEventListener('resize', resize) return () => window.removeEventListener('resize', resize); // eslint-disable-next-line react-hooks/exhaustive-deps }, []) useEffect(() => { const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; const socket = new WebSocket(`${protocol}//${window.location.host}/api/ws/subscribe/${store.token}/?x-token=${X_TOKEN}`); socket.onopen = () => { const message = '\r\x1b[K\x1b[36m### Waiting for scheduling ...\x1b[0m' for (let key of Object.keys(store.outputs)) { store.outputs[key].data = message } term.write(message) socket.send('ok'); fitPlugin.fit() const formData = fitPlugin.proposeDimensions() formData.token = store.token http.patch('/api/exec/do/', formData) } socket.onmessage = e => { if (e.data === 'pong') { socket.send('ping') } else { _handleData(e.data) } } socket.onclose = () => { for (let key of Object.keys(store.outputs)) { if (store.outputs[key].status === -2) { store.outputs[key].status = -1 } store.outputs[key].data += '\r\n\x1b[31mWebsocket connection failed!\x1b[0m' term.write('\r\n\x1b[31mWebsocket connection failed!\x1b[0m') } } return () => socket && socket.close() // eslint-disable-next-line react-hooks/exhaustive-deps }, []) function _handleData(message) { const {key, data, status} = JSON.parse(message); if (status !== undefined) { store.outputs[key].status = status; } if (data) { store.outputs[key].data += data if (String(key) === gCurrent) term.write(data) } } function handleSwitch(key) { setCurrent(key) gCurrent = key term.clear() term.write(store.outputs[key].data) } function openTerminal(key) { window.open(`/ssh?id=${key}`) } const {tag, items, counter} = store return (
store.updateTag('0')}>
{counter['0']}
store.updateTag('1')}>
{counter['1']}
store.updateTag('2')}>
{counter['2']}
{items.map(([key, item]) => (
handleSwitch(key)}> {item.status === -2 ? ( ) : item.status === 0 ? ( ) : ( )}
{item.title}
))}
{store.outputs[current].title}
openTerminal(current)}/>
) } export default observer(OutView)