From 2261f321ee522d8bfa835f2608cd0c07293da8cc Mon Sep 17 00:00:00 2001 From: typescreep Date: Mon, 21 Jul 2025 22:44:21 +0300 Subject: [PATCH] pod terminal --- package-lock.json | 33 +++- package.json | 3 +- src/components/organisms/Factory/Factory.tsx | 3 +- .../organisms/Factory/TestWs/TestWs.tsx | 169 ------------------ .../organisms/Factory/TestWs/index.ts | 1 - .../organisms/Factory/TestWs/styled.ts | 28 --- vite.config.ts | 6 + 7 files changed, 36 insertions(+), 207 deletions(-) delete mode 100644 src/components/organisms/Factory/TestWs/TestWs.tsx delete mode 100644 src/components/organisms/Factory/TestWs/index.ts delete mode 100644 src/components/organisms/Factory/TestWs/styled.ts diff --git a/package-lock.json b/package-lock.json index 0b6050c..b089414 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,13 +11,14 @@ "@ant-design/icons": "5.6.0", "@monaco-editor/react": "4.6.0", "@originjs/vite-plugin-federation": "1.3.6", - "@prorobotech/openapi-k8s-toolkit": "0.0.1-alpha.62", + "@prorobotech/openapi-k8s-toolkit": "^0.0.1-alpha.63", "@readme/openapi-parser": "4.0.0", "@reduxjs/toolkit": "2.2.5", "@tanstack/react-query": "5.62.2", "@tanstack/react-query-devtools": "5.62.2", - "@xterm/addon-fit": "^0.10.0", - "@xterm/xterm": "^5.5.0", + "@xterm/addon-attach": "0.11.0", + "@xterm/addon-fit": "0.10.0", + "@xterm/xterm": "5.5.0", "antd": "5.26.4", "axios": "1.4.0", "cross-env": "7.0.3", @@ -39,6 +40,7 @@ "typescript": "4.9.5", "usehooks-ts": "3.1.1", "uuid": "11.0.3", + "xterm-theme": "1.1.0", "yaml": "2.7.0" }, "devDependencies": { @@ -2800,13 +2802,16 @@ } }, "node_modules/@prorobotech/openapi-k8s-toolkit": { - "version": "0.0.1-alpha.62", - "resolved": "https://registry.npmjs.org/@prorobotech/openapi-k8s-toolkit/-/openapi-k8s-toolkit-0.0.1-alpha.62.tgz", - "integrity": "sha512-Ase/PSMID8NKuZhNMn19f4rWluKlaAA62YOxY7CJq0ji1DP4hQuw3Izw8Qm/CFAMJ/J9wdnyEgFraEZ9qAeP+g==", + "version": "0.0.1-alpha.63", + "resolved": "https://registry.npmjs.org/@prorobotech/openapi-k8s-toolkit/-/openapi-k8s-toolkit-0.0.1-alpha.63.tgz", + "integrity": "sha512-eLrSeLkswEE287T3U0sGm1UhoiVJrSvZdx6DsIfOxoKYfnpa8bMArh9/baNOSc2f/vivzn+eksuCfm4No0N5yA==", "license": "MIT", "dependencies": { "@monaco-editor/react": "4.6.0", "@reduxjs/toolkit": "2.2.5", + "@xterm/addon-attach": "0.11.0", + "@xterm/addon-fit": "0.10.0", + "@xterm/xterm": "5.5.0", "axios": "1.4.0", "cross-env": "7.0.3", "dotenv": "16.4.7", @@ -2818,6 +2823,7 @@ "typescript": "4.9.5", "usehooks-ts": "3.1.1", "uuid": "11.0.3", + "xterm-theme": "1.1.0", "yaml": "2.7.0" }, "peerDependencies": { @@ -4438,6 +4444,15 @@ "vite": "^4 || ^5" } }, + "node_modules/@xterm/addon-attach": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@xterm/addon-attach/-/addon-attach-0.11.0.tgz", + "integrity": "sha512-JboCN0QAY6ZLY/SSB/Zl2cQ5zW1Eh4X3fH7BnuR1NB7xGRhzbqU2Npmpiw/3zFlxDaU88vtKzok44JKi2L2V2Q==", + "license": "MIT", + "peerDependencies": { + "@xterm/xterm": "^5.0.0" + } + }, "node_modules/@xterm/addon-fit": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/@xterm/addon-fit/-/addon-fit-0.10.0.tgz", @@ -13529,6 +13544,12 @@ "node": ">=0.4" } }, + "node_modules/xterm-theme": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/xterm-theme/-/xterm-theme-1.1.0.tgz", + "integrity": "sha512-n2GocBEbqcz4vEl4OYkU93hEVia8GWdnqchiz/0nQ/olRUyhulGf4wfha23x/D2m0imWaIavRZtt8c6kWZXdsA==", + "license": "ISC" + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", diff --git a/package.json b/package.json index f9fcec3..72840f9 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,7 @@ "@ant-design/icons": "5.6.0", "@monaco-editor/react": "4.6.0", "@originjs/vite-plugin-federation": "1.3.6", - "@prorobotech/openapi-k8s-toolkit": "0.0.1-alpha.62", + "@prorobotech/openapi-k8s-toolkit": "0.0.1-alpha.63", "@readme/openapi-parser": "4.0.0", "@reduxjs/toolkit": "2.2.5", "@tanstack/react-query": "5.62.2", @@ -49,6 +49,7 @@ "typescript": "4.9.5", "usehooks-ts": "3.1.1", "uuid": "11.0.3", + "xterm-theme": "1.1.0", "yaml": "2.7.0" }, "devDependencies": { diff --git a/src/components/organisms/Factory/Factory.tsx b/src/components/organisms/Factory/Factory.tsx index b12ea34..18c2799 100644 --- a/src/components/organisms/Factory/Factory.tsx +++ b/src/components/organisms/Factory/Factory.tsx @@ -12,7 +12,7 @@ import { useSelector } from 'react-redux' import { RootState } from 'store/store' import { BASE_API_GROUP, BASE_API_VERSION } from 'constants/customizationApiGroupAndVersion' import { HEAD_FIRST_ROW, HEAD_SECOND_ROW, FOOTER_HEIGHT, NAV_HEIGHT } from 'constants/blocksSizes' -import { TestWs } from './TestWs' +import '@xterm/xterm/css/xterm.css' type TFactoryProps = { setSidebarTags: (tags: string[]) => void @@ -60,7 +60,6 @@ export const Factory: FC = ({ setSidebarTags }) => { if (spec.withScrollableMainContentCard) { return ( - = ({ endpoint, namespace, podName }) => { - const [isLoading, setIsLoading] = useState(true) - const [error, setError] = useState() - const [terminal, setTerminal] = useState() - const terminalRef = useRef(null) - const resizeObserverRef = useRef(null) - const fitAddon = new FitAddon() - - // const decoderRef = useRef(new TextDecoder('utf-8')) - - useEffect(() => { - const terminal = new XTerm({ - cursorBlink: false, - cursorStyle: 'block', - fontFamily: 'monospace', - fontSize: 10, - }) - terminal.loadAddon(fitAddon) - // terminal.loadAddon(new WebLinksAddon()) - setTerminal(terminal) - fitAddon.fit() - - return () => { - if (terminal) { - terminal.dispose() - } - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []) - - useEffect(() => { - if (terminal) { - if (terminalRef.current) { - terminal.open(terminalRef.current) - setTerminal(terminal) - } - } - - // Initialize ResizeObserver to handle resizing - resizeObserverRef.current = new ResizeObserver(() => { - fitAddon.fit() - }) - - // Observe the terminal container for size changes - if (terminalRef.current) { - resizeObserverRef.current.observe(terminalRef.current) - } - - return () => { - // Clean up the ResizeObserver - if (resizeObserverRef.current) { - resizeObserverRef.current.disconnect() - } - } - - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [terminal]) - - useEffect(() => { - if (!terminal) { - return - } - - const socket = new WebSocket(endpoint) - - socket.onopen = () => { - socket.send( - JSON.stringify({ - type: 'init', - payload: { namespace, podName }, - }), - ) - console.log('WebSocket Client Connected') - setIsLoading(false) - } - - socket.onmessage = event => { - const data = JSON.parse(event.data) - if (data.type === 'output') { - if (data.payload.type === 'Buffer' && Array.isArray(data.payload.data)) { - // Reconstruct bytes and decode to string - // const bytes = new Uint8Array(data.payload) - // const text = decoderRef.current.decode(bytes) - - const text = Buffer.from(data.payload.data) - console.log(text) - terminal.write(text.toString('utf8')) - } else { - terminal.write(String(data.payload)) - } - } - } - - socket.onclose = () => { - console.log('WebSocket Client Closed') - } - - socket.onerror = error => { - console.error('WebSocket Error:', error) - setError(error) - } - - terminal.onData(data => { - if (data === '\u001bOP') { - // const bufferToSend = Buffer.from('\u001b[11~', 'utf8') - // socket.send(JSON.stringify({ type: 'input', payload: bufferToSend })) - socket.send(JSON.stringify({ type: 'input', payload: '\u001b[11~' })) - return - } - // const bufferToSend = Buffer.from(data, 'utf8') - // socket.send(JSON.stringify({ type: 'input', payload: bufferToSend })) - socket.send(JSON.stringify({ type: 'input', payload: data })) - }) - - // terminal.onResize(size => { - // socket.send(JSON.stringify({ type: 'resize', payload: { cols: size.cols, rows: size.rows } })) - // }) - - // eslint-disable-next-line consistent-return - return () => { - terminal.dispose() - if (socket.readyState === WebSocket.OPEN) { - socket.close() - } - } - }, [terminal, endpoint, namespace, podName]) - - return ( - <> - - -
- - - {isLoading && !error && } - {error && } - - ) -} - -export const TestWs: FC = () => { - const cluster = useSelector((state: RootState) => state.cluster.cluster) - const endpoint = `http://localhost:4002/api/clusters/${cluster}/openapi-bff/terminal/terminalPod/terminalPod` - - return ( - - ) -} diff --git a/src/components/organisms/Factory/TestWs/index.ts b/src/components/organisms/Factory/TestWs/index.ts deleted file mode 100644 index f7cb419..0000000 --- a/src/components/organisms/Factory/TestWs/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './TestWs' diff --git a/src/components/organisms/Factory/TestWs/styled.ts b/src/components/organisms/Factory/TestWs/styled.ts deleted file mode 100644 index ecb9125..0000000 --- a/src/components/organisms/Factory/TestWs/styled.ts +++ /dev/null @@ -1,28 +0,0 @@ -import styled from 'styled-components' - -const FullWidthDiv = styled.div` - display: flex; - justify-content: center; - width: 100%; -` - -type TCustomCardProps = { - $isVisible?: boolean -} - -const CustomCard = styled.div` - display: ${({ $isVisible }) => ($isVisible ? 'block' : 'none')}; - max-height: calc(100vh - 158px); - margin: 24px; - /* overflow-y: auto; */ - /* background: black; */ - - * { - scrollbar-width: thin; - } -` - -export const Styled = { - FullWidthDiv, - CustomCard, -} diff --git a/vite.config.ts b/vite.config.ts index 55c740a..14c8432 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -149,6 +149,12 @@ export default defineConfig({ // }) // }, }, + '^/api/clusters/.*/openapi-bff-ws': { + target: options?.BFF_URL, + changeOrigin: true, + secure: false, + ws: true, + }, }, }, })