mirror of
				https://github.com/1Panel-dev/1Panel.git
				synced 2025-10-31 19:26:02 +08:00 
			
		
		
		
	fix: 对于 Redis 终端、容器终端也同步改造
This commit is contained in:
		
							parent
							
								
									fa83199d7b
								
							
						
					
					
						commit
						74b6af64e9
					
				
					 7 changed files with 82 additions and 292 deletions
				
			
		|  | @ -1,6 +1,8 @@ | |||
| package v1 | ||||
| 
 | ||||
| import ( | ||||
| 	"encoding/base64" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
| 	"strconv" | ||||
|  | @ -196,7 +198,15 @@ func wshandleError(ws *websocket.Conn, err error) bool { | |||
| 		global.LOG.Errorf("handler ws faled:, err: %v", err) | ||||
| 		dt := time.Now().Add(time.Second) | ||||
| 		if ctlerr := ws.WriteControl(websocket.CloseMessage, []byte(err.Error()), dt); ctlerr != nil { | ||||
| 			_ = ws.WriteMessage(websocket.TextMessage, []byte(err.Error())) | ||||
| 			wsData, err := json.Marshal(terminal.WsMsg{ | ||||
| 				Type: terminal.WsMsgCmd, | ||||
| 				Data: base64.StdEncoding.EncodeToString([]byte(err.Error())), | ||||
| 			}) | ||||
| 			if err != nil { | ||||
| 				_ = ws.WriteMessage(websocket.TextMessage, []byte("{\"type\":\"cmd\",\"data\":\"failed to encoding to json\"}")) | ||||
| 			} else { | ||||
| 				_ = ws.WriteMessage(websocket.TextMessage, wsData) | ||||
| 			} | ||||
| 		} | ||||
| 		return true | ||||
| 	} | ||||
|  |  | |||
|  | @ -52,11 +52,17 @@ func (sws *LocalWsSession) handleSlaveEvent(exitCh chan bool) { | |||
| func (sws *LocalWsSession) masterWrite(data []byte) error { | ||||
| 	sws.writeMutex.Lock() | ||||
| 	defer sws.writeMutex.Unlock() | ||||
| 	err := sws.wsConn.WriteMessage(websocket.TextMessage, data) | ||||
| 	wsData, err := json.Marshal(WsMsg{ | ||||
| 		Type: WsMsgCmd, | ||||
| 		Data: base64.StdEncoding.EncodeToString(data), | ||||
| 	}) | ||||
| 	if err != nil { | ||||
| 		return errors.Wrapf(err, "failed to encoding to json") | ||||
| 	} | ||||
| 	err = sws.wsConn.WriteMessage(websocket.TextMessage, wsData) | ||||
| 	if err != nil { | ||||
| 		return errors.Wrapf(err, "failed to write to master") | ||||
| 	} | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
|  | @ -74,21 +80,27 @@ func (sws *LocalWsSession) receiveWsMsg(exitCh chan bool) { | |||
| 				global.LOG.Errorf("reading webSocket message failed, err: %v", err) | ||||
| 				return | ||||
| 			} | ||||
| 			msgObj := wsMsg{} | ||||
| 			msgObj := WsMsg{} | ||||
| 			_ = json.Unmarshal(wsData, &msgObj) | ||||
| 			switch msgObj.Type { | ||||
| 			case wsMsgResize: | ||||
| 			case WsMsgResize: | ||||
| 				if msgObj.Cols > 0 && msgObj.Rows > 0 { | ||||
| 					if err := sws.slave.ResizeTerminal(msgObj.Cols, msgObj.Rows); err != nil { | ||||
| 						global.LOG.Errorf("ssh pty change windows size failed, err: %v", err) | ||||
| 					} | ||||
| 				} | ||||
| 			case wsMsgCmd: | ||||
| 			case WsMsgCmd: | ||||
| 				decodeBytes, err := base64.StdEncoding.DecodeString(msgObj.Data) | ||||
| 				if err != nil { | ||||
| 					global.LOG.Errorf("websock cmd string base64 decoding failed, err: %v", err) | ||||
| 				} | ||||
| 				sws.sendWebsocketInputCommandToSshSessionStdinPipe(decodeBytes) | ||||
| 			case WsMsgHeartbeat: | ||||
| 				// 接收到心跳包后将心跳包原样返回,可以用于网络延迟检测等情况 | ||||
| 				err = wsConn.WriteMessage(websocket.TextMessage, wsData) | ||||
| 				if err != nil { | ||||
| 					global.LOG.Errorf("ssh sending heartbeat to webSocket failed, err: %v", err) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  |  | |||
|  | @ -35,17 +35,17 @@ func (w *safeBuffer) Reset() { | |||
| } | ||||
| 
 | ||||
| const ( | ||||
| 	wsMsgCmd       = "cmd" | ||||
| 	wsMsgResize    = "resize" | ||||
| 	wsMsgHeartbeat = "heartbeat" | ||||
| 	WsMsgCmd       = "cmd" | ||||
| 	WsMsgResize    = "resize" | ||||
| 	WsMsgHeartbeat = "heartbeat" | ||||
| ) | ||||
| 
 | ||||
| type wsMsg struct { | ||||
| type WsMsg struct { | ||||
| 	Type      string `json:"type"` | ||||
| 	Data      string `json:"data,omitempty"`      // wsMsgCmd | ||||
| 	Cols      int    `json:"cols,omitempty"`      // wsMsgResize | ||||
| 	Rows      int    `json:"rows,omitempty"`      // wsMsgResize | ||||
| 	Timestamp int    `json:"timestamp,omitempty"` // wsMsgHeartbeat | ||||
| 	Data      string `json:"data,omitempty"`      // WsMsgCmd | ||||
| 	Cols      int    `json:"cols,omitempty"`      // WsMsgResize | ||||
| 	Rows      int    `json:"rows,omitempty"`      // WsMsgResize | ||||
| 	Timestamp int    `json:"timestamp,omitempty"` // WsMsgHeartbeat | ||||
| } | ||||
| 
 | ||||
| type LogicSshWsSession struct { | ||||
|  | @ -127,22 +127,22 @@ func (sws *LogicSshWsSession) receiveWsMsg(exitCh chan bool) { | |||
| 			if err != nil { | ||||
| 				return | ||||
| 			} | ||||
| 			msgObj := wsMsg{} | ||||
| 			msgObj := WsMsg{} | ||||
| 			_ = json.Unmarshal(wsData, &msgObj) | ||||
| 			switch msgObj.Type { | ||||
| 			case wsMsgResize: | ||||
| 			case WsMsgResize: | ||||
| 				if msgObj.Cols > 0 && msgObj.Rows > 0 { | ||||
| 					if err := sws.session.WindowChange(msgObj.Rows, msgObj.Cols); err != nil { | ||||
| 						global.LOG.Errorf("ssh pty change windows size failed, err: %v", err) | ||||
| 					} | ||||
| 				} | ||||
| 			case wsMsgCmd: | ||||
| 			case WsMsgCmd: | ||||
| 				decodeBytes, err := base64.StdEncoding.DecodeString(msgObj.Data) | ||||
| 				if err != nil { | ||||
| 					global.LOG.Errorf("websock cmd string base64 decoding failed, err: %v", err) | ||||
| 				} | ||||
| 				sws.sendWebsocketInputCommandToSshSessionStdinPipe(decodeBytes) | ||||
| 			case wsMsgHeartbeat: | ||||
| 			case WsMsgHeartbeat: | ||||
| 				// 接收到心跳包后将心跳包原样返回,可以用于网络延迟检测等情况 | ||||
| 				err = wsConn.WriteMessage(websocket.TextMessage, wsData) | ||||
| 				if err != nil { | ||||
|  | @ -173,8 +173,8 @@ func (sws *LogicSshWsSession) sendComboOutput(exitCh chan bool) { | |||
| 			} | ||||
| 			bs := sws.comboOutput.Bytes() | ||||
| 			if len(bs) > 0 { | ||||
| 				wsData, err := json.Marshal(wsMsg{ | ||||
| 					Type: wsMsgCmd, | ||||
| 				wsData, err := json.Marshal(WsMsg{ | ||||
| 					Type: WsMsgCmd, | ||||
| 					Data: base64.StdEncoding.EncodeToString(bs), | ||||
| 				}) | ||||
| 				if err != nil { | ||||
|  |  | |||
|  | @ -68,14 +68,16 @@ const initError = (errorInfo: string) => { | |||
|     } | ||||
| }; | ||||
| 
 | ||||
| function onClose() { | ||||
| function onClose(isKeepShow: boolean = false) { | ||||
|     window.removeEventListener('resize', changeTerminalSize); | ||||
|     try { | ||||
|         terminalSocket.value?.close(); | ||||
|     } catch {} | ||||
|     try { | ||||
|         term.value.dispose(); | ||||
|     } catch {} | ||||
|     if (!isKeepShow) { | ||||
|         try { | ||||
|             term.value.dispose(); | ||||
|         } catch {} | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // terminal 相关代码 start | ||||
|  | @ -162,7 +164,7 @@ const onWSReceive = (message: MessageEvent) => { | |||
|     switch (wsMsg.type) { | ||||
|         case 'cmd': { | ||||
|             term.value.element && term.value.focus(); | ||||
|             term.value.write(Base64.decode(wsMsg.data)); | ||||
|             wsMsg.data && term.value.write(Base64.decode(wsMsg.data)); // 这里理论上不用判断,但是Redis和Ctr还没实现Alive处理,所以exit后会一直发数据,todo | ||||
|             break; | ||||
|         } | ||||
|         case 'heartbeat': { | ||||
|  |  | |||
|  | @ -44,7 +44,7 @@ | |||
|                 {{ $t('commons.button.conn') }} | ||||
|             </el-button> | ||||
|             <el-button v-else @click="handleClose()">{{ $t('commons.button.disconn') }}</el-button> | ||||
|             <div style="height: calc(100vh - 302px)" :id="'terminal-exec'"></div> | ||||
|             <Terminal style="height: calc(100vh - 302px)" ref="terminalRef"></Terminal> | ||||
|         </el-form> | ||||
|     </el-drawer> | ||||
| </template> | ||||
|  | @ -52,24 +52,12 @@ | |||
| <script lang="ts" setup> | ||||
| import { reactive, ref } from 'vue'; | ||||
| import { ElForm, FormInstance } from 'element-plus'; | ||||
| import { Terminal } from 'xterm'; | ||||
| import { AttachAddon } from 'xterm-addon-attach'; | ||||
| import { Base64 } from 'js-base64'; | ||||
| import 'xterm/css/xterm.css'; | ||||
| import { FitAddon } from 'xterm-addon-fit'; | ||||
| import { Rules } from '@/global/form-rules'; | ||||
| import { isJson } from '@/utils/util'; | ||||
| import Terminal from '@/components/terminal/index.vue'; | ||||
| import DrawerHeader from '@/components/drawer-header/index.vue'; | ||||
| 
 | ||||
| const terminalVisiable = ref(false); | ||||
| const terminalOpen = ref(false); | ||||
| const fitAddon = new FitAddon(); | ||||
| let terminalSocket = ref(null) as unknown as WebSocket; | ||||
| let term = ref(null) as unknown as Terminal; | ||||
| const loading = ref(true); | ||||
| const runRealTerminal = () => { | ||||
|     loading.value = false; | ||||
| }; | ||||
| const form = reactive({ | ||||
|     isCustom: false, | ||||
|     command: '', | ||||
|  | @ -78,6 +66,7 @@ const form = reactive({ | |||
| }); | ||||
| type FormInstance = InstanceType<typeof ElForm>; | ||||
| const formRef = ref<FormInstance>(); | ||||
| const terminalRef = ref<InstanceType<typeof Terminal> | null>(null); | ||||
| 
 | ||||
| interface DialogProps { | ||||
|     containerID: string; | ||||
|  | @ -89,125 +78,31 @@ const acceptParams = async (params: DialogProps): Promise<void> => { | |||
|     form.user = ''; | ||||
|     form.command = '/bin/bash'; | ||||
|     terminalOpen.value = false; | ||||
|     window.addEventListener('resize', changeTerminalSize); | ||||
| }; | ||||
| 
 | ||||
| const onChangeCommand = async () => { | ||||
|     form.command = ''; | ||||
| }; | ||||
| 
 | ||||
| const onWSReceive = (message: any) => { | ||||
|     if (!isJson(message.data)) { | ||||
|         return; | ||||
|     } | ||||
|     const data = JSON.parse(message.data); | ||||
|     term.element && term.focus(); | ||||
|     term.write(data.Data); | ||||
| }; | ||||
| 
 | ||||
| const errorRealTerminal = (ex: any) => { | ||||
|     let message = ex.message; | ||||
|     if (!message) message = 'disconnected'; | ||||
|     term.write(`\x1b[31m${message}\x1b[m\r\n`); | ||||
| }; | ||||
| 
 | ||||
| const closeRealTerminal = (ev: CloseEvent) => { | ||||
|     term.write(ev.reason); | ||||
| }; | ||||
| 
 | ||||
| const initTerm = (formEl: FormInstance | undefined) => { | ||||
|     if (!formEl) return; | ||||
|     formEl.validate(async (valid) => { | ||||
|         if (!valid) return; | ||||
|         let href = window.location.href; | ||||
|         let protocol = href.split('//')[0] === 'http:' ? 'ws' : 'wss'; | ||||
|         let ipLocal = href.split('//')[1].split('/')[0]; | ||||
|         terminalOpen.value = true; | ||||
|         let ifm = document.getElementById('terminal-exec') as HTMLInputElement | null; | ||||
|         term = new Terminal({ | ||||
|             lineHeight: 1.2, | ||||
|             fontSize: 12, | ||||
|             fontFamily: "Monaco, Menlo, Consolas, 'Courier New', monospace", | ||||
|             theme: { | ||||
|                 background: '#000000', | ||||
|             }, | ||||
|             cursorBlink: true, | ||||
|             cursorStyle: 'underline', | ||||
|             scrollback: 100, | ||||
|             tabStopWidth: 4, | ||||
|         terminalRef.value!.acceptParams({ | ||||
|             endpoint: '/api/v1/containers/exec', | ||||
|             args: `containerid=${form.containerID}&user=${form.user}&command=${form.command}`, | ||||
|             error: '', | ||||
|         }); | ||||
|         if (ifm) { | ||||
|             term.open(ifm); | ||||
|             terminalSocket = new WebSocket( | ||||
|                 `${protocol}://${ipLocal}/api/v1/containers/exec?containerid=${form.containerID}&cols=${term.cols}&rows=${term.rows}&user=${form.user}&command=${form.command}`, | ||||
|             ); | ||||
|             terminalSocket.onopen = runRealTerminal; | ||||
|             terminalSocket.onmessage = onWSReceive; | ||||
|             terminalSocket.onclose = closeRealTerminal; | ||||
|             terminalSocket.onerror = errorRealTerminal; | ||||
|             term.onData((data: any) => { | ||||
|                 if (isWsOpen()) { | ||||
|                     terminalSocket.send( | ||||
|                         JSON.stringify({ | ||||
|                             type: 'cmd', | ||||
|                             cmd: Base64.encode(data), | ||||
|                         }), | ||||
|                     ); | ||||
|                 } | ||||
|             }); | ||||
|             term.loadAddon(new AttachAddon(terminalSocket)); | ||||
|             term.loadAddon(fitAddon); | ||||
|             setTimeout(() => { | ||||
|                 fitAddon.fit(); | ||||
|                 if (isWsOpen()) { | ||||
|                     terminalSocket.send( | ||||
|                         JSON.stringify({ | ||||
|                             type: 'resize', | ||||
|                             cols: term.cols, | ||||
|                             rows: term.rows, | ||||
|                         }), | ||||
|                     ); | ||||
|                 } | ||||
|             }, 30); | ||||
|         } | ||||
|     }); | ||||
| }; | ||||
| 
 | ||||
| const fitTerm = () => { | ||||
|     fitAddon.fit(); | ||||
| }; | ||||
| 
 | ||||
| const isWsOpen = () => { | ||||
|     const readyState = terminalSocket && terminalSocket.readyState; | ||||
|     if (readyState) { | ||||
|         return readyState === 1; | ||||
|     } | ||||
|     return false; | ||||
| }; | ||||
| 
 | ||||
| function handleClose() { | ||||
|     window.removeEventListener('resize', changeTerminalSize); | ||||
|     if (isWsOpen()) { | ||||
|         terminalSocket && terminalSocket.close(); | ||||
|         term.dispose(); | ||||
|     } | ||||
|     terminalRef.value?.onClose(); | ||||
|     terminalVisiable.value = false; | ||||
|     terminalOpen.value = false; | ||||
| } | ||||
| 
 | ||||
| function changeTerminalSize() { | ||||
|     fitTerm(); | ||||
|     const { cols, rows } = term; | ||||
|     if (isWsOpen()) { | ||||
|         terminalSocket.send( | ||||
|             JSON.stringify({ | ||||
|                 type: 'resize', | ||||
|                 cols: cols, | ||||
|                 rows: rows, | ||||
|             }), | ||||
|         ); | ||||
|     } | ||||
| } | ||||
| defineExpose({ | ||||
|     acceptParams, | ||||
| }); | ||||
|  |  | |||
|  | @ -20,7 +20,12 @@ | |||
|                 </div> | ||||
|             </template> | ||||
|             <template #main v-if="redisIsExist && !isOnSetting"> | ||||
|                 <Terminal :key="isRefresh" ref="terminalRef" /> | ||||
|                 <Terminal | ||||
|                     style="height: calc(100vh - 370px)" | ||||
|                     :key="isRefresh" | ||||
|                     ref="terminalRef" | ||||
|                     v-show="terminalShow" | ||||
|                 /> | ||||
|             </template> | ||||
|         </LayoutContent> | ||||
| 
 | ||||
|  | @ -55,7 +60,7 @@ | |||
| import LayoutContent from '@/layout/layout-content.vue'; | ||||
| import Setting from '@/views/database/redis/setting/index.vue'; | ||||
| import Password from '@/views/database/redis/password/index.vue'; | ||||
| import Terminal from '@/views/database/redis/terminal/index.vue'; | ||||
| import Terminal from '@/components/terminal/index.vue'; | ||||
| import AppStatus from '@/components/app-status/index.vue'; | ||||
| import { nextTick, onBeforeUnmount, ref } from 'vue'; | ||||
| import { App } from '@/api/interface/app'; | ||||
|  | @ -65,12 +70,13 @@ import router from '@/routers'; | |||
| const loading = ref(false); | ||||
| const maskShow = ref(true); | ||||
| 
 | ||||
| const terminalRef = ref(); | ||||
| const terminalRef = ref<InstanceType<typeof Terminal> | null>(null); | ||||
| const settingRef = ref(); | ||||
| const isOnSetting = ref(false); | ||||
| const redisIsExist = ref(false); | ||||
| const redisStatus = ref(); | ||||
| const redisName = ref(); | ||||
| const terminalShow = ref(false); | ||||
| 
 | ||||
| const redisCommandPort = ref(); | ||||
| const commandVisiable = ref(false); | ||||
|  | @ -80,6 +86,7 @@ const isRefresh = ref(); | |||
| const onSetting = async () => { | ||||
|     isOnSetting.value = true; | ||||
|     terminalRef.value?.onClose(false); | ||||
|     terminalShow.value = false; | ||||
|     settingRef.value!.acceptParams({ status: redisStatus.value, redisName: redisName.value }); | ||||
| }; | ||||
| 
 | ||||
|  | @ -114,19 +121,32 @@ const checkExist = (data: App.CheckInstalled) => { | |||
|     if (redisStatus.value === 'Running') { | ||||
|         loadDashboardPort(); | ||||
|         nextTick(() => { | ||||
|             terminalRef.value.acceptParams(); | ||||
|             terminalShow.value = true; | ||||
|             terminalRef.value.acceptParams({ | ||||
|                 endpoint: '/api/v1/databases/redis/exec', | ||||
|                 args: '', | ||||
|                 error: '', | ||||
|             }); | ||||
|         }); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| const initTerminal = async () => { | ||||
|     if (redisStatus.value === 'Running') { | ||||
|         terminalRef.value.acceptParams(); | ||||
|         nextTick(() => { | ||||
|             terminalShow.value = true; | ||||
|             terminalRef.value.acceptParams({ | ||||
|                 endpoint: '/api/v1/databases/redis/exec', | ||||
|                 args: '', | ||||
|                 error: '', | ||||
|             }); | ||||
|         }); | ||||
|     } | ||||
| }; | ||||
| const closeTerminal = async (isKeepShow: boolean) => { | ||||
|     isRefresh.value = !isRefresh.value; | ||||
|     terminalRef.value?.onClose(isKeepShow); | ||||
|     terminalShow.value = isKeepShow; | ||||
| }; | ||||
| 
 | ||||
| const onBefore = () => { | ||||
|  |  | |||
|  | @ -1,149 +0,0 @@ | |||
| <template> | ||||
|     <div v-show="terminalShow" style="height: 100%"> | ||||
|         <div style="height: calc(100vh - 370px)" :id="'terminal-exec'"></div> | ||||
|     </div> | ||||
| </template> | ||||
| 
 | ||||
| <script lang="ts" setup> | ||||
| import { ref } from 'vue'; | ||||
| import { Terminal } from 'xterm'; | ||||
| import { AttachAddon } from 'xterm-addon-attach'; | ||||
| import { Base64 } from 'js-base64'; | ||||
| import 'xterm/css/xterm.css'; | ||||
| import { FitAddon } from 'xterm-addon-fit'; | ||||
| import { isJson } from '@/utils/util'; | ||||
| 
 | ||||
| const fitAddon = new FitAddon(); | ||||
| let terminalSocket = ref(null) as unknown as WebSocket; | ||||
| let term = ref(null) as unknown as Terminal; | ||||
| const loading = ref(true); | ||||
| const runRealTerminal = () => { | ||||
|     loading.value = false; | ||||
| }; | ||||
| 
 | ||||
| const terminalShow = ref(false); | ||||
| 
 | ||||
| const acceptParams = async (): Promise<void> => { | ||||
|     terminalShow.value = true; | ||||
|     initTerm(); | ||||
|     window.addEventListener('resize', changeTerminalSize); | ||||
| }; | ||||
| const onClose = async (isKeepShow: boolean) => { | ||||
|     window.removeEventListener('resize', changeTerminalSize); | ||||
|     if (isWsOpen()) { | ||||
|         terminalSocket && terminalSocket.close(); | ||||
|     } | ||||
|     if (!isKeepShow) { | ||||
|         term.dispose(); | ||||
|     } | ||||
|     terminalShow.value = isKeepShow; | ||||
| }; | ||||
| 
 | ||||
| const onWSReceive = (message: any) => { | ||||
|     if (!isJson(message.data)) { | ||||
|         return; | ||||
|     } | ||||
|     const data = JSON.parse(message.data); | ||||
|     term.element && term.focus(); | ||||
|     term.write(data.Data); | ||||
| }; | ||||
| 
 | ||||
| const errorRealTerminal = (ex: any) => { | ||||
|     let message = ex.message; | ||||
|     if (!message) message = 'disconnected'; | ||||
|     term.write(`\x1b[31m${message}\x1b[m\r\n`); | ||||
| }; | ||||
| 
 | ||||
| const closeRealTerminal = (ev: CloseEvent) => { | ||||
|     term.write(ev.reason); | ||||
| }; | ||||
| 
 | ||||
| const initTerm = () => { | ||||
|     let ifm = document.getElementById('terminal-exec') as HTMLInputElement | null; | ||||
|     let href = window.location.href; | ||||
|     let protocol = href.split('//')[0] === 'http:' ? 'ws' : 'wss'; | ||||
|     let ipLocal = href.split('//')[1].split('/')[0]; | ||||
|     term = new Terminal({ | ||||
|         lineHeight: 1.2, | ||||
|         fontSize: 12, | ||||
|         fontFamily: "Monaco, Menlo, Consolas, 'Courier New', monospace", | ||||
|         theme: { | ||||
|             background: '#000000', | ||||
|         }, | ||||
|         cursorBlink: true, | ||||
|         cursorStyle: 'underline', | ||||
|         scrollback: 100, | ||||
|         tabStopWidth: 4, | ||||
|     }); | ||||
|     if (ifm) { | ||||
|         term.open(ifm); | ||||
|         terminalSocket = new WebSocket( | ||||
|             `${protocol}://${ipLocal}/api/v1/databases/redis/exec?cols=${term.cols}&rows=${term.rows}`, | ||||
|         ); | ||||
|         terminalSocket.onopen = runRealTerminal; | ||||
|         terminalSocket.onmessage = onWSReceive; | ||||
|         terminalSocket.onclose = closeRealTerminal; | ||||
|         terminalSocket.onerror = errorRealTerminal; | ||||
|         term.onData((data: any) => { | ||||
|             if (isWsOpen()) { | ||||
|                 terminalSocket.send( | ||||
|                     JSON.stringify({ | ||||
|                         type: 'cmd', | ||||
|                         cmd: Base64.encode(data), | ||||
|                     }), | ||||
|                 ); | ||||
|             } | ||||
|         }); | ||||
|         term.loadAddon(new AttachAddon(terminalSocket)); | ||||
|         term.loadAddon(fitAddon); | ||||
|         setTimeout(() => { | ||||
|             fitAddon.fit(); | ||||
|             if (isWsOpen()) { | ||||
|                 terminalSocket.send( | ||||
|                     JSON.stringify({ | ||||
|                         type: 'resize', | ||||
|                         cols: term.cols, | ||||
|                         rows: term.rows, | ||||
|                     }), | ||||
|                 ); | ||||
|             } | ||||
|         }, 30); | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| const fitTerm = () => { | ||||
|     fitAddon.fit(); | ||||
| }; | ||||
| 
 | ||||
| const isWsOpen = () => { | ||||
|     const readyState = terminalSocket && terminalSocket.readyState; | ||||
|     if (readyState) { | ||||
|         return readyState === 1; | ||||
|     } | ||||
|     return false; | ||||
| }; | ||||
| 
 | ||||
| function changeTerminalSize() { | ||||
|     fitTerm(); | ||||
|     const { cols, rows } = term; | ||||
|     if (isWsOpen()) { | ||||
|         terminalSocket.send( | ||||
|             JSON.stringify({ | ||||
|                 type: 'resize', | ||||
|                 cols: cols, | ||||
|                 rows: rows, | ||||
|             }), | ||||
|         ); | ||||
|     } | ||||
| } | ||||
| defineExpose({ | ||||
|     acceptParams, | ||||
|     onClose, | ||||
| }); | ||||
| </script> | ||||
| <style lang="scss" scoped> | ||||
| #terminal { | ||||
|     width: 100%; | ||||
|     height: 100%; | ||||
| } | ||||
| </style> | ||||
		Loading…
	
	Add table
		
		Reference in a new issue