- 修复操作磁盘空间失败的问题

- 移除不可用的菜单
- 修复接入页面粘贴板等按钮不可点击的问题
- 修复接入RDP协议时无法自适应窗口大小的问题
This commit is contained in:
dushixiang 2022-11-06 17:46:10 +08:00
parent 8f51644cae
commit a52ad2e356
7 changed files with 196 additions and 157 deletions

View File

@ -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"

View File

@ -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"),

View File

@ -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>

View File

@ -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>

View File

@ -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}

View File

@ -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: '计划任务',

View File

@ -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);