test add xtermjs
This commit is contained in:
parent
3ab5894576
commit
40f4fa3ee4
19
package-lock.json
generated
19
package-lock.json
generated
@ -33,7 +33,9 @@
|
|||||||
"react-scripts": "5.0.1",
|
"react-scripts": "5.0.1",
|
||||||
"serve": "^14.2.1",
|
"serve": "^14.2.1",
|
||||||
"typescript": "^4.9.5",
|
"typescript": "^4.9.5",
|
||||||
"web-vitals": "^2.1.4"
|
"web-vitals": "^2.1.4",
|
||||||
|
"xterm": "^5.3.0",
|
||||||
|
"xterm-addon-fit": "^0.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@adobe/css-tools": {
|
"node_modules/@adobe/css-tools": {
|
||||||
@ -17952,6 +17954,21 @@
|
|||||||
"resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
|
||||||
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="
|
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="
|
||||||
},
|
},
|
||||||
|
"node_modules/xterm": {
|
||||||
|
"version": "5.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/xterm/-/xterm-5.3.0.tgz",
|
||||||
|
"integrity": "sha512-8QqjlekLUFTrU6x7xck1MsPzPA571K5zNqWm0M0oroYEWVOptZ0+ubQSkQ3uxIEhcIHRujJy6emDWX4A7qyFzg==",
|
||||||
|
"deprecated": "This package is now deprecated. Move to @xterm/xterm instead."
|
||||||
|
},
|
||||||
|
"node_modules/xterm-addon-fit": {
|
||||||
|
"version": "0.8.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/xterm-addon-fit/-/xterm-addon-fit-0.8.0.tgz",
|
||||||
|
"integrity": "sha512-yj3Np7XlvxxhYF/EJ7p3KHaMt6OdwQ+HDu573Vx1lRXsVxOcnVJs51RgjZOouIZOczTsskaS+CpXspK81/DLqw==",
|
||||||
|
"deprecated": "This package is now deprecated. Move to @xterm/addon-fit instead.",
|
||||||
|
"peerDependencies": {
|
||||||
|
"xterm": "^5.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/y18n": {
|
"node_modules/y18n": {
|
||||||
"version": "5.0.8",
|
"version": "5.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
|
||||||
|
@ -28,7 +28,9 @@
|
|||||||
"react-scripts": "5.0.1",
|
"react-scripts": "5.0.1",
|
||||||
"serve": "^14.2.1",
|
"serve": "^14.2.1",
|
||||||
"typescript": "^4.9.5",
|
"typescript": "^4.9.5",
|
||||||
"web-vitals": "^2.1.4"
|
"web-vitals": "^2.1.4",
|
||||||
|
"xterm": "^5.3.0",
|
||||||
|
"xterm-addon-fit": "^0.8.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "react-scripts start",
|
"start": "react-scripts start",
|
||||||
|
@ -571,7 +571,16 @@ export const SERVER_ACTIONS: ActionInfo[] = [
|
|||||||
title: "Set User Permissions",
|
title: "Set User Permissions",
|
||||||
definitions: definitions,
|
definitions: definitions,
|
||||||
},
|
},
|
||||||
}
|
},
|
||||||
|
{
|
||||||
|
name: "Start",
|
||||||
|
args: {
|
||||||
|
},
|
||||||
|
requestType: "post",
|
||||||
|
endpoint: "/servers/{server_id}/attach",
|
||||||
|
permissions: Permission.RunCommand,
|
||||||
|
response_action: "Ignore"
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
export const CREATE_SERVER_ACTION: ActionInfo = {
|
export const CREATE_SERVER_ACTION: ActionInfo = {
|
||||||
|
@ -16,6 +16,8 @@ import Popper from '@mui/material/Popper';
|
|||||||
import MenuItem from '@mui/material/MenuItem';
|
import MenuItem from '@mui/material/MenuItem';
|
||||||
import MenuList from '@mui/material/MenuList';
|
import MenuList from '@mui/material/MenuList';
|
||||||
import { Permission } from "./actions";
|
import { Permission } from "./actions";
|
||||||
|
import { Terminal } from "xterm";
|
||||||
|
import TerminalComponent from "./terminal";
|
||||||
|
|
||||||
export const apiAuthenticatedContext: Context<[boolean, Dispatch<boolean>]> = createContext([false, (value: boolean) => {}] as [boolean, Dispatch<boolean>])
|
export const apiAuthenticatedContext: Context<[boolean, Dispatch<boolean>]> = createContext([false, (value: boolean) => {}] as [boolean, Dispatch<boolean>])
|
||||||
|
|
||||||
@ -216,12 +218,12 @@ export function ActionItem(p: { action: ActionInfo, identifierSubstring?: string
|
|||||||
const user = useContext(UserInfoContext)
|
const user = useContext(UserInfoContext)
|
||||||
|
|
||||||
const [form, setForm] = useState(false);
|
const [form, setForm] = useState(false);
|
||||||
|
const [terminal, setTerminal] = useState(null as string|null);
|
||||||
const [formData, setFormData]: [RJSFSchema, Dispatch<RJSFSchema>] = useState({})
|
const [formData, setFormData]: [RJSFSchema, Dispatch<RJSFSchema>] = useState({})
|
||||||
console.log(formData)
|
const url = p.action.endpoint.replaceAll(`{${identifierSubstring}}`, actionIdentifier)
|
||||||
|
|
||||||
|
|
||||||
function handleSubmit() {
|
function handleSubmit() {
|
||||||
let url = p.action.endpoint.replaceAll(`{${identifierSubstring}}`, actionIdentifier)
|
|
||||||
let promise: Promise<AxiosResponse<any, any>>|null = null
|
let promise: Promise<AxiosResponse<any, any>>|null = null
|
||||||
switch (p.action.requestType) {
|
switch (p.action.requestType) {
|
||||||
case 'post': {
|
case 'post': {
|
||||||
@ -263,7 +265,7 @@ export function ActionItem(p: { action: ActionInfo, identifierSubstring?: string
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (<>
|
return (<>
|
||||||
<Button variant={p.variant} disabled={!isUserAllowed(user, p.action)} onClick={() => { if (p.onClick) { p.onClick() } setForm(true) }} sx={p.sx}>{p.action.name}</Button >
|
<Button variant={p.variant} disabled={!isUserAllowed(user, p.action)} onClick={() => { if (p.onClick) { p.onClick() } p.action.response_action == 'Terminal'?setTerminal(url):setForm(true) }} sx={p.sx}>{p.action.name}</Button >
|
||||||
<Modal
|
<Modal
|
||||||
onClose={() => { setForm(false); setFormData({}); }}
|
onClose={() => { setForm(false); setFormData({}); }}
|
||||||
open={form}
|
open={form}
|
||||||
@ -272,6 +274,14 @@ export function ActionItem(p: { action: ActionInfo, identifierSubstring?: string
|
|||||||
<Form validator={validator} widgets={{TextWidget: CustomField}} schema={p.action.args} onChange={onFormChange} formData={formData} onSubmit={handleSubmit} />
|
<Form validator={validator} widgets={{TextWidget: CustomField}} schema={p.action.args} onChange={onFormChange} formData={formData} onSubmit={handleSubmit} />
|
||||||
</Box>
|
</Box>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
<Modal
|
||||||
|
onClose={() => { setTerminal(null); }}
|
||||||
|
open={terminal != null}
|
||||||
|
>
|
||||||
|
<Box sx={formModalStyle}>
|
||||||
|
<TerminalComponent websocket={terminal} />
|
||||||
|
</Box>
|
||||||
|
</Modal>
|
||||||
</>)
|
</>)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
51
src/terminal.tsx
Normal file
51
src/terminal.tsx
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
// src/components/Terminal.tsx
|
||||||
|
import React, { useEffect, useRef } from 'react';
|
||||||
|
import { Terminal } from 'xterm';
|
||||||
|
import { FitAddon } from 'xterm-addon-fit';
|
||||||
|
import 'xterm/css/xterm.css';
|
||||||
|
|
||||||
|
function TerminalComponent (p: {websocket: string|null}) {
|
||||||
|
const {websocket} = p
|
||||||
|
const terminalRef = useRef<HTMLDivElement>(null);
|
||||||
|
const terminal = useRef<Terminal | null>(null);
|
||||||
|
const fitAddon = useRef<FitAddon | null>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (terminalRef.current) {
|
||||||
|
terminal.current = new Terminal();
|
||||||
|
fitAddon.current = new FitAddon();
|
||||||
|
terminal.current.loadAddon(fitAddon.current);
|
||||||
|
terminal.current.open(terminalRef.current);
|
||||||
|
fitAddon.current.fit();
|
||||||
|
if (websocket == null){
|
||||||
|
|
||||||
|
return ()=>{
|
||||||
|
terminal.current?.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const socket = new WebSocket(websocket);
|
||||||
|
|
||||||
|
socket.addEventListener('open', () => {
|
||||||
|
terminal.current?.write('Connected to the WebSocket server\r\n');
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.addEventListener('message', (event) => {
|
||||||
|
terminal.current?.write(event.data);
|
||||||
|
});
|
||||||
|
|
||||||
|
terminal.current.onData((data) => {
|
||||||
|
socket.send(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
terminal.current?.dispose();
|
||||||
|
socket.close();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return <div ref={terminalRef} style={{ width: '100%', height: '100%' }}></div>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TerminalComponent;
|
Loading…
x
Reference in New Issue
Block a user