test add xtermjs

This commit is contained in:
ACoolName 2024-05-27 00:34:42 +03:00
parent 3ab5894576
commit 40f4fa3ee4
5 changed files with 99 additions and 10 deletions

19
package-lock.json generated
View File

@ -33,7 +33,9 @@
"react-scripts": "5.0.1",
"serve": "^14.2.1",
"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": {
@ -17952,6 +17954,21 @@
"resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
"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": {
"version": "5.0.8",
"resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",

View File

@ -28,7 +28,9 @@
"react-scripts": "5.0.1",
"serve": "^14.2.1",
"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": {
"start": "react-scripts start",

View File

@ -571,7 +571,16 @@ export const SERVER_ACTIONS: ActionInfo[] = [
title: "Set User Permissions",
definitions: definitions,
},
}
},
{
name: "Start",
args: {
},
requestType: "post",
endpoint: "/servers/{server_id}/attach",
permissions: Permission.RunCommand,
response_action: "Ignore"
},
]
export const CREATE_SERVER_ACTION: ActionInfo = {

View File

@ -16,6 +16,8 @@ import Popper from '@mui/material/Popper';
import MenuItem from '@mui/material/MenuItem';
import MenuList from '@mui/material/MenuList';
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>])
@ -216,12 +218,12 @@ export function ActionItem(p: { action: ActionInfo, identifierSubstring?: string
const user = useContext(UserInfoContext)
const [form, setForm] = useState(false);
const [terminal, setTerminal] = useState(null as string|null);
const [formData, setFormData]: [RJSFSchema, Dispatch<RJSFSchema>] = useState({})
console.log(formData)
const url = p.action.endpoint.replaceAll(`{${identifierSubstring}}`, actionIdentifier)
function handleSubmit() {
let url = p.action.endpoint.replaceAll(`{${identifierSubstring}}`, actionIdentifier)
let promise: Promise<AxiosResponse<any, any>>|null = null
switch (p.action.requestType) {
case 'post': {
@ -263,7 +265,7 @@ export function ActionItem(p: { action: ActionInfo, identifierSubstring?: string
}
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
onClose={() => { setForm(false); setFormData({}); }}
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} />
</Box>
</Modal>
<Modal
onClose={() => { setTerminal(null); }}
open={terminal != null}
>
<Box sx={formModalStyle}>
<TerminalComponent websocket={terminal} />
</Box>
</Modal>
</>)
}

51
src/terminal.tsx Normal file
View 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;