- 修复操作磁盘空间失败的问题
- 移除不可用的菜单 - 修复接入页面粘贴板等按钮不可点击的问题 - 修复接入RDP协议时无法自适应窗口大小的问题
This commit is contained in:
parent
8f51644cae
commit
a52ad2e356
@ -3,14 +3,14 @@ package api
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"next-terminal/server/common"
|
||||
"next-terminal/server/common/maps"
|
||||
"next-terminal/server/common/nt"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"next-terminal/server/common"
|
||||
"next-terminal/server/common/maps"
|
||||
"next-terminal/server/common/nt"
|
||||
"next-terminal/server/model"
|
||||
"next-terminal/server/repository"
|
||||
"next-terminal/server/service"
|
||||
|
@ -216,6 +216,9 @@ var DefaultMenu = []*model.Menu{
|
||||
model.NewMenu("job-log", "日志", "job",
|
||||
model.NewPermission("GET", "/jobs/:id/logs/paging"),
|
||||
),
|
||||
model.NewMenu("job-log-clear", "日志清空", "job",
|
||||
model.NewPermission("DELETE", "/jobs/:id/logs"),
|
||||
),
|
||||
|
||||
model.NewMenu("storage", "磁盘空间", "ops",
|
||||
model.NewPermission("GET", "/storages/paging"),
|
||||
|
@ -30,8 +30,6 @@ const STATE_CONNECTED = 3;
|
||||
const STATE_DISCONNECTING = 4;
|
||||
const STATE_DISCONNECTED = 5;
|
||||
|
||||
let clientState = STATE_IDLE;
|
||||
|
||||
const Guacd = () => {
|
||||
|
||||
let [searchParams] = useSearchParams();
|
||||
@ -48,6 +46,7 @@ const Guacd = () => {
|
||||
height = window.innerHeight;
|
||||
}
|
||||
|
||||
let [box, setBox] = useState({width, height});
|
||||
let [guacd, setGuacd] = useState({});
|
||||
let [session, setSession] = useState({});
|
||||
let [clipboardText, setClipboardText] = useState('');
|
||||
@ -57,106 +56,106 @@ const Guacd = () => {
|
||||
|
||||
useEffect(() => {
|
||||
document.title = assetName;
|
||||
|
||||
const renderDisplay = (sessionId, protocol, width, height) => {
|
||||
let tunnel = new Guacamole.WebSocketTunnel(`${wsServer}/sessions/${sessionId}/tunnel`);
|
||||
let client = new Guacamole.Client(tunnel);
|
||||
|
||||
// 处理从虚拟机收到的剪贴板内容
|
||||
client.onclipboard = handleClipboardReceived;
|
||||
|
||||
// 处理客户端的状态变化事件
|
||||
client.onstatechange = (state) => {
|
||||
onClientStateChange(state, sessionId);
|
||||
};
|
||||
|
||||
client.onerror = onError;
|
||||
tunnel.onerror = onError;
|
||||
|
||||
// Get display div from document
|
||||
const displayEle = document.getElementById("display");
|
||||
|
||||
// Add client to display div
|
||||
const element = client.getDisplay().getElement();
|
||||
displayEle.appendChild(element);
|
||||
|
||||
let dpi = 96;
|
||||
if (protocol === 'telnet') {
|
||||
dpi = dpi * 2;
|
||||
}
|
||||
|
||||
let token = getToken();
|
||||
|
||||
let params = {
|
||||
'width': width,
|
||||
'height': height,
|
||||
'dpi': dpi,
|
||||
'X-Auth-Token': token
|
||||
};
|
||||
|
||||
let paramStr = qs.stringify(params);
|
||||
|
||||
client.connect(paramStr);
|
||||
let display = client.getDisplay();
|
||||
display.onresize = function (width, height) {
|
||||
display.scale(Math.min(
|
||||
window.innerHeight / display.getHeight(),
|
||||
window.innerWidth / display.getHeight()
|
||||
))
|
||||
}
|
||||
|
||||
const mouse = new Guacamole.Mouse(element);
|
||||
|
||||
mouse.onmousedown = mouse.onmouseup = function (mouseState) {
|
||||
client.sendMouseState(mouseState);
|
||||
}
|
||||
|
||||
mouse.onmousemove = function (mouseState) {
|
||||
client.getDisplay().showCursor(false);
|
||||
mouseState.x = mouseState.x / display.getScale();
|
||||
mouseState.y = mouseState.y / display.getScale();
|
||||
client.sendMouseState(mouseState);
|
||||
};
|
||||
|
||||
const touch = new Guacamole.Mouse.Touchpad(element); // or Guacamole.Touchscreen
|
||||
|
||||
touch.onmousedown = touch.onmousemove = touch.onmouseup = function (state) {
|
||||
client.sendMouseState(state);
|
||||
};
|
||||
|
||||
const sink = new Guacamole.InputSink();
|
||||
displayEle.appendChild(sink.getElement());
|
||||
sink.focus();
|
||||
|
||||
const keyboard = new Guacamole.Keyboard(sink.getElement());
|
||||
|
||||
keyboard.onkeydown = (keysym) => {
|
||||
client.sendKeyEvent(1, keysym);
|
||||
if (keysym === 65288) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
keyboard.onkeyup = (keysym) => {
|
||||
client.sendKeyEvent(0, keysym);
|
||||
};
|
||||
|
||||
setGuacd({
|
||||
client,
|
||||
sink,
|
||||
});
|
||||
}
|
||||
|
||||
const x = async () => {
|
||||
let session = await sessionApi.create(assetId, 'guacd');
|
||||
if (!strings.hasText(session['id'])) {
|
||||
return;
|
||||
}
|
||||
setSession(session);
|
||||
renderDisplay(session['id'], protocol, width, height);
|
||||
}
|
||||
x();
|
||||
createSession();
|
||||
}, [assetId, assetName]);
|
||||
|
||||
const createSession = async () => {
|
||||
let session = await sessionApi.create(assetId, 'guacd');
|
||||
if (!strings.hasText(session['id'])) {
|
||||
return;
|
||||
}
|
||||
setSession(session);
|
||||
renderDisplay(session['id'], protocol, width, height);
|
||||
}
|
||||
|
||||
const renderDisplay = (sessionId, protocol, width, height) => {
|
||||
let tunnel = new Guacamole.WebSocketTunnel(`${wsServer}/sessions/${sessionId}/tunnel`);
|
||||
let client = new Guacamole.Client(tunnel);
|
||||
|
||||
// 处理从虚拟机收到的剪贴板内容
|
||||
client.onclipboard = handleClipboardReceived;
|
||||
|
||||
// 处理客户端的状态变化事件
|
||||
client.onstatechange = (state) => {
|
||||
onClientStateChange(state, sessionId);
|
||||
};
|
||||
|
||||
client.onerror = onError;
|
||||
tunnel.onerror = onError;
|
||||
|
||||
// Get display div from document
|
||||
const displayEle = document.getElementById("display");
|
||||
|
||||
// Add client to display div
|
||||
const element = client.getDisplay().getElement();
|
||||
displayEle.appendChild(element);
|
||||
|
||||
let dpi = 96;
|
||||
if (protocol === 'telnet') {
|
||||
dpi = dpi * 2;
|
||||
}
|
||||
|
||||
let token = getToken();
|
||||
|
||||
let params = {
|
||||
'width': width,
|
||||
'height': height,
|
||||
'dpi': dpi,
|
||||
'X-Auth-Token': token
|
||||
};
|
||||
|
||||
let paramStr = qs.stringify(params);
|
||||
|
||||
client.connect(paramStr);
|
||||
let display = client.getDisplay();
|
||||
display.onresize = function (width, height) {
|
||||
display.scale(Math.min(
|
||||
window.innerHeight / display.getHeight(),
|
||||
window.innerWidth / display.getHeight()
|
||||
))
|
||||
}
|
||||
|
||||
const mouse = new Guacamole.Mouse(element);
|
||||
|
||||
mouse.onmousedown = mouse.onmouseup = function (mouseState) {
|
||||
client.sendMouseState(mouseState);
|
||||
}
|
||||
|
||||
mouse.onmousemove = function (mouseState) {
|
||||
client.getDisplay().showCursor(false);
|
||||
mouseState.x = mouseState.x / display.getScale();
|
||||
mouseState.y = mouseState.y / display.getScale();
|
||||
client.sendMouseState(mouseState);
|
||||
};
|
||||
|
||||
const touch = new Guacamole.Mouse.Touchpad(element); // or Guacamole.Touchscreen
|
||||
|
||||
touch.onmousedown = touch.onmousemove = touch.onmouseup = function (state) {
|
||||
client.sendMouseState(state);
|
||||
};
|
||||
|
||||
const sink = new Guacamole.InputSink();
|
||||
displayEle.appendChild(sink.getElement());
|
||||
sink.focus();
|
||||
|
||||
const keyboard = new Guacamole.Keyboard(sink.getElement());
|
||||
|
||||
keyboard.onkeydown = (keysym) => {
|
||||
client.sendKeyEvent(1, keysym);
|
||||
if (keysym === 65288) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
keyboard.onkeyup = (keysym) => {
|
||||
client.sendKeyEvent(0, keysym);
|
||||
};
|
||||
|
||||
setGuacd({
|
||||
client,
|
||||
sink,
|
||||
});
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
let resize = debounce(() => {
|
||||
onWindowResize();
|
||||
@ -175,10 +174,15 @@ const Guacd = () => {
|
||||
const onWindowResize = () => {
|
||||
if (guacd.client && !fixedSize) {
|
||||
const display = guacd.client.getDisplay();
|
||||
display.scale(Math.min(
|
||||
window.innerHeight / display.getHeight(),
|
||||
window.innerWidth / display.getHeight()
|
||||
));
|
||||
let width = window.innerWidth;
|
||||
let height = window.innerHeight;
|
||||
setBox({width, height});
|
||||
let scale = Math.min(
|
||||
height / display.getHeight(),
|
||||
width / display.getHeight()
|
||||
);
|
||||
display.scale(scale);
|
||||
guacd.client.sendSize(width, height);
|
||||
}
|
||||
}
|
||||
|
||||
@ -226,7 +230,7 @@ const Guacd = () => {
|
||||
if (navigator.clipboard) {
|
||||
await navigator.clipboard.writeText(data);
|
||||
}
|
||||
message.info('您选择的内容已复制到您的粘贴板中,在右侧的输入框中可同时查看到。');
|
||||
message.success('您选择的内容已复制到您的粘贴板中,在右侧的输入框中可同时查看到。');
|
||||
};
|
||||
} else {
|
||||
let reader = new Guacamole.BlobReader(stream, mimetype);
|
||||
@ -237,9 +241,6 @@ const Guacd = () => {
|
||||
};
|
||||
|
||||
const sendClipboard = (data) => {
|
||||
if (clientState !== STATE_CONNECTED) {
|
||||
return;
|
||||
}
|
||||
if (!guacd.client) {
|
||||
return;
|
||||
}
|
||||
@ -266,7 +267,6 @@ const Guacd = () => {
|
||||
}
|
||||
|
||||
const onClientStateChange = (state, sessionId) => {
|
||||
clientState = state;
|
||||
const key = 'message';
|
||||
switch (state) {
|
||||
case STATE_IDLE:
|
||||
@ -309,6 +309,7 @@ const Guacd = () => {
|
||||
for (let j = 0; j < keys.length; j++) {
|
||||
guacd.client.sendKeyEvent(0, keys[j]);
|
||||
}
|
||||
message.success('发送组合键成功');
|
||||
}
|
||||
|
||||
const showMessage = (msg) => {
|
||||
@ -441,19 +442,19 @@ const Guacd = () => {
|
||||
return (
|
||||
<div>
|
||||
<div className="container" style={{
|
||||
width: width,
|
||||
height: height,
|
||||
margin: '0 auto'
|
||||
width: box.width,
|
||||
height: box.height,
|
||||
margin: '0 auto',
|
||||
backgroundColor: '#1b1b1b'
|
||||
}}>
|
||||
<div id="display"/>
|
||||
</div>
|
||||
|
||||
<Draggable>
|
||||
<Affix style={{position: 'absolute', top: 50, right: 50}}>
|
||||
<Button icon={<ExpandOutlined/>} disabled={clientState !== STATE_CONNECTED}
|
||||
onClick={() => {
|
||||
fullScreen();
|
||||
}}/>
|
||||
<Button icon={<ExpandOutlined/>} onClick={() => {
|
||||
fullScreen();
|
||||
}}/>
|
||||
</Affix>
|
||||
</Draggable>
|
||||
|
||||
@ -461,7 +462,7 @@ const Guacd = () => {
|
||||
session['copy'] === '1' || session['paste'] === '1' ?
|
||||
<Draggable>
|
||||
<Affix style={{position: 'absolute', top: 50, right: 100}}>
|
||||
<Button icon={<CopyOutlined/>} disabled={clientState !== STATE_CONNECTED}
|
||||
<Button icon={<CopyOutlined/>}
|
||||
onClick={() => {
|
||||
setClipboardVisible(true);
|
||||
}}/>
|
||||
@ -475,8 +476,7 @@ const Guacd = () => {
|
||||
<Draggable>
|
||||
<Affix style={{position: 'absolute', top: 100, right: 100}}>
|
||||
<Dropdown overlay={hotKeyMenu} trigger={['click']} placement="bottomLeft">
|
||||
<Button icon={<WindowsOutlined/>}
|
||||
disabled={clientState !== STATE_CONNECTED}/>
|
||||
<Button icon={<WindowsOutlined/>}/>
|
||||
</Dropdown>
|
||||
</Affix>
|
||||
</Draggable>
|
||||
@ -486,8 +486,7 @@ const Guacd = () => {
|
||||
(protocol === 'rdp' && session['fileSystem'] === '1') &&
|
||||
<Draggable>
|
||||
<Affix style={{position: 'absolute', top: 100, right: 50}}>
|
||||
<Button icon={<FolderOutlined/>}
|
||||
disabled={clientState !== STATE_CONNECTED} onClick={() => {
|
||||
<Button icon={<FolderOutlined/>} onClick={() => {
|
||||
setFileSystemVisible(true);
|
||||
}}/>
|
||||
</Affix>
|
||||
@ -499,8 +498,7 @@ const Guacd = () => {
|
||||
<Draggable>
|
||||
<Affix style={{position: 'absolute', top: 100, right: 100}}>
|
||||
<Dropdown overlay={hotKeyMenu} trigger={['click']} placement="bottomLeft">
|
||||
<Button icon={<WindowsOutlined/>}
|
||||
disabled={clientState !== STATE_CONNECTED}/>
|
||||
<Button icon={<WindowsOutlined/>}/>
|
||||
</Dropdown>
|
||||
</Affix>
|
||||
</Draggable>
|
||||
|
@ -1,6 +1,9 @@
|
||||
import React, {useEffect} from 'react';
|
||||
import {Form, Input, Modal, Switch} from "antd";
|
||||
import React, {useState} from 'react';
|
||||
import {Form, Input, InputNumber, Modal, Select, Switch} from "antd";
|
||||
import storageApi from "../../api/storage";
|
||||
import {renderSize} from "../../utils/utils";
|
||||
import {useQuery} from "react-query";
|
||||
import strings from "../../utils/strings";
|
||||
|
||||
const formItemLayout = {
|
||||
labelCol: {span: 6},
|
||||
@ -17,23 +20,38 @@ const StorageModal = ({
|
||||
|
||||
const [form] = Form.useForm();
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
const getItem = async () => {
|
||||
let data = await storageApi.getById(id);
|
||||
if (data) {
|
||||
form.setFieldsValue(data);
|
||||
useQuery('getStorageById', () => storageApi.getById(id), {
|
||||
enabled: visible && strings.hasText(id),
|
||||
onSuccess: data => {
|
||||
if (data['limitSize'] > 0) {
|
||||
let limitSize = renderSize(data['limitSize']);
|
||||
let ss = limitSize.split(' ');
|
||||
data['limitSize'] = parseInt(ss[0]);
|
||||
setUnit(ss[1]);
|
||||
} else {
|
||||
data['limitSize'] = -1;
|
||||
}
|
||||
}
|
||||
if (visible && id) {
|
||||
getItem();
|
||||
} else {
|
||||
form.setFieldsValue({
|
||||
isShare: false,
|
||||
});
|
||||
}
|
||||
}, [visible])
|
||||
form.setFieldsValue(data);
|
||||
},
|
||||
});
|
||||
|
||||
let [unit, setUnit] = useState('MB');
|
||||
|
||||
const selectAfter = (
|
||||
<Select value={unit} style={{width: 65}} onChange={(value) => {
|
||||
setUnit(value);
|
||||
}}>
|
||||
<Select.Option value="B">B</Select.Option>
|
||||
<Select.Option value="KB">KB</Select.Option>
|
||||
<Select.Option value="MB">MB</Select.Option>
|
||||
<Select.Option value="GB">GB</Select.Option>
|
||||
<Select.Option value="TB">TB</Select.Option>
|
||||
<Select.Option value="PB">PB</Select.Option>
|
||||
<Select.Option value="EB">EB</Select.Option>
|
||||
<Select.Option value="ZB">ZB</Select.Option>
|
||||
<Select.Option value="YB">YB</Select.Option>
|
||||
</Select>
|
||||
);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
@ -45,6 +63,35 @@ const StorageModal = ({
|
||||
form
|
||||
.validateFields()
|
||||
.then(async values => {
|
||||
let limitSize = values['limitSize'];
|
||||
switch (unit) {
|
||||
case 'B':
|
||||
break;
|
||||
case 'KB':
|
||||
limitSize = limitSize * 1024;
|
||||
break;
|
||||
case 'MB':
|
||||
limitSize = limitSize * 1024 * 1024;
|
||||
break;
|
||||
case 'GB':
|
||||
limitSize = limitSize * 1024 * 1024 * 1024;
|
||||
break;
|
||||
case 'TB':
|
||||
limitSize = limitSize * 1024 * 1024 * 1024 * 1024 * 1024;
|
||||
break;
|
||||
case 'EB':
|
||||
limitSize = limitSize * 1024 * 1024 * 1024 * 1024 * 1024 * 1024;
|
||||
break;
|
||||
case 'ZB':
|
||||
limitSize = limitSize * 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024;
|
||||
break;
|
||||
case 'YB':
|
||||
limitSize = limitSize * 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
values['limitSize'] = limitSize;
|
||||
let ok = await handleOk(values);
|
||||
if (ok) {
|
||||
form.resetFields();
|
||||
@ -53,6 +100,7 @@ const StorageModal = ({
|
||||
}}
|
||||
onCancel={() => {
|
||||
form.resetFields();
|
||||
setUnit('MB');
|
||||
handleCancel();
|
||||
}}
|
||||
confirmLoading={confirmLoading}
|
||||
@ -76,7 +124,7 @@ const StorageModal = ({
|
||||
|
||||
<Form.Item label="大小限制" name='limitSize' rules={[{required: true, message: '请输入大小限制'}]}
|
||||
tooltip='无限制请填写-1'>
|
||||
<Input type={'number'} min={-1} suffix="MB"/>
|
||||
<InputNumber min={-1} addonAfter={selectAfter} style={{width: 275}}/>
|
||||
</Form.Item>
|
||||
|
||||
</Form>
|
||||
|
@ -369,7 +369,7 @@ class Setting extends Component {
|
||||
<Form ref={this.sshSettingFormRef} name="ssh" onFinish={this.changeProperties}
|
||||
layout="vertical">
|
||||
|
||||
<Title level={4}>SSH/TELNET配置</Title>
|
||||
<Title level={4}>TELNET配置</Title>
|
||||
|
||||
<Form.Item
|
||||
{...formItemLayout}
|
||||
|
@ -83,11 +83,6 @@ export const routers = [
|
||||
label: '登录日志',
|
||||
icon: <LoginOutlined/>,
|
||||
},
|
||||
{
|
||||
key: 'storage-log',
|
||||
label: '文件日志',
|
||||
icon: <LoginOutlined/>,
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -95,11 +90,6 @@ export const routers = [
|
||||
label: '系统运维',
|
||||
icon: <ControlOutlined/>,
|
||||
children: [
|
||||
// {
|
||||
// key: 'batch-command',
|
||||
// label: '批量命令',
|
||||
// icon: <BlockOutlined/>,
|
||||
// },
|
||||
{
|
||||
key: 'job',
|
||||
label: '计划任务',
|
||||
|
@ -217,7 +217,7 @@ export function exitFull() {
|
||||
|
||||
export function renderSize(value) {
|
||||
if (null == value || value === '' || value === 0) {
|
||||
return "0 Bytes";
|
||||
return "0 B";
|
||||
}
|
||||
const unitArr = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
|
||||
let srcSize = parseFloat(value);
|
||||
|
Loading…
Reference in New Issue
Block a user