继续完善文件管理

This commit is contained in:
dushixiang 2021-02-04 19:50:16 +08:00
parent 1444891d96
commit 248815538d
6 changed files with 233 additions and 112 deletions

View File

@ -107,14 +107,14 @@ func SetupRoutes() *echo.Echo {
sessions.POST("/:id/connect", SessionConnectEndpoint) sessions.POST("/:id/connect", SessionConnectEndpoint)
sessions.POST("/:id/disconnect", Admin(SessionDisconnectEndpoint)) sessions.POST("/:id/disconnect", Admin(SessionDisconnectEndpoint))
sessions.POST("/:id/resize", SessionResizeEndpoint) sessions.POST("/:id/resize", SessionResizeEndpoint)
sessions.POST("/:id/upload", SessionUploadEndpoint)
sessions.GET("/:id/download", SessionDownloadEndpoint)
sessions.GET("/:id/ls", SessionLsEndpoint) sessions.GET("/:id/ls", SessionLsEndpoint)
sessions.GET("/:id/download", SessionDownloadEndpoint)
sessions.POST("/:id/upload", SessionUploadEndpoint)
sessions.POST("/:id/mkdir", SessionMkDirEndpoint) sessions.POST("/:id/mkdir", SessionMkDirEndpoint)
sessions.DELETE("/:id/rm", SessionRmEndpoint) sessions.POST("/:id/rm", SessionRmEndpoint)
sessions.POST("/:id/rename", SessionRenameEndpoint)
sessions.DELETE("/:id", SessionDeleteEndpoint) sessions.DELETE("/:id", SessionDeleteEndpoint)
sessions.GET("/:id/recording", SessionRecordingEndpoint) sessions.GET("/:id/recording", SessionRecordingEndpoint)
sessions.GET("/:id/status", SessionGetStatusEndpoint)
} }
resourceSharers := e.Group("/resource-sharers") resourceSharers := e.Group("/resource-sharers")

View File

@ -442,7 +442,7 @@ func SessionLsEndpoint(c echo.Context) error {
return Success(c, files) return Success(c, files)
} }
return err return errors.New("当前协议不支持此操作")
} }
func SessionMkDirEndpoint(c echo.Context) error { func SessionMkDirEndpoint(c echo.Context) error {
@ -473,7 +473,7 @@ func SessionMkDirEndpoint(c echo.Context) error {
return Success(c, nil) return Success(c, nil)
} }
return nil return errors.New("当前协议不支持此操作")
} }
func SessionRmEndpoint(c echo.Context) error { func SessionRmEndpoint(c echo.Context) error {
@ -497,22 +497,22 @@ func SessionRmEndpoint(c echo.Context) error {
} }
if stat.IsDir() { if stat.IsDir() {
fileInfos, err := tun.Subject.SftpClient.ReadDir(key) fileInfos, err := sftpClient.ReadDir(key)
if err != nil { if err != nil {
return err return err
} }
for i := range fileInfos { for i := range fileInfos {
if err := tun.Subject.SftpClient.Remove(path.Join(key, fileInfos[i].Name())); err != nil { if err := sftpClient.Remove(path.Join(key, fileInfos[i].Name())); err != nil {
return err return err
} }
} }
if err := tun.Subject.SftpClient.RemoveDirectory(key); err != nil { if err := sftpClient.RemoveDirectory(key); err != nil {
return err return err
} }
} else { } else {
if err := tun.Subject.SftpClient.Remove(key); err != nil { if err := sftpClient.Remove(key); err != nil {
return err return err
} }
} }
@ -531,7 +531,43 @@ func SessionRmEndpoint(c echo.Context) error {
return Success(c, nil) return Success(c, nil)
} }
return nil return errors.New("当前协议不支持此操作")
}
func SessionRenameEndpoint(c echo.Context) error {
sessionId := c.Param("id")
session, err := model.FindSessionById(sessionId)
if err != nil {
return err
}
oldName := c.QueryParam("oldName")
newName := c.QueryParam("newName")
if "ssh" == session.Protocol {
tun, ok := global.Store.Get(sessionId)
if !ok {
return errors.New("获取sftp客户端失败")
}
sftpClient := tun.Subject.SftpClient
if err := sftpClient.Rename(oldName, newName); err != nil {
return err
}
return Success(c, nil)
} else if "rdp" == session.Protocol {
drivePath, err := model.GetDrivePath()
if err != nil {
return err
}
if err := os.Rename(path.Join(drivePath, oldName), path.Join(drivePath, newName)); err != nil {
return err
}
return Success(c, nil)
}
return errors.New("当前协议不支持此操作")
} }
func SessionRecordingEndpoint(c echo.Context) error { func SessionRecordingEndpoint(c echo.Context) error {
@ -551,14 +587,3 @@ func SessionRecordingEndpoint(c echo.Context) error {
logrus.Debugf("读取录屏文件:%v,是否存在: %v, 是否为文件: %v", recording, utils.FileExists(recording), utils.IsFile(recording)) logrus.Debugf("读取录屏文件:%v,是否存在: %v, 是否为文件: %v", recording, utils.FileExists(recording), utils.IsFile(recording))
return c.File(recording) return c.File(recording)
} }
func SessionGetStatusEndpoint(c echo.Context) error {
sessionId := c.Param("id")
session, err := model.FindSessionById(sessionId)
if err != nil {
return err
}
return Success(c, H{
"status": session.Status,
})
}

View File

@ -106,11 +106,11 @@ class Access extends Component {
}) })
if (this.state.protocol === 'ssh') { if (this.state.protocol === 'ssh') {
if (data.data && data.data.length > 0) { if (data.data && data.data.length > 0) {
message.info('您输入的内容已复制到远程服务器上,使用右键将自动粘贴。'); // message.info('您输入的内容已复制到远程服务器上,使用右键将自动粘贴。');
} }
} else { } else {
if (data.data && data.data.length > 0) { if (data.data && data.data.length > 0) {
message.info('您输入的内容已复制到远程服务器上'); // message.info('您输入的内容已复制到远程服务器上');
} }
} }
@ -282,9 +282,13 @@ class Access extends Component {
// Set clipboard contents once stream is finished // Set clipboard contents once stream is finished
reader.onend = async () => { reader.onend = async () => {
message.info('您选择的内容已复制到您的粘贴板中,在右侧的输入框中可同时查看到。'); // message.info('您选择的内容已复制到您的粘贴板中,在右侧的输入框中可同时查看到。');
this.setState({ this.setState({
clipboardText: data clipboardText: data
}, () => {
// 选中粘贴板文本框中的内容
// 调用API粘贴进
}); });
if (navigator.clipboard) { if (navigator.clipboard) {
@ -306,9 +310,9 @@ class Access extends Component {
}; };
onKeyDown = (keysym) => { onKeyDown = (keysym) => {
if (this.state.clipboardVisible || this.state.fileSystemVisible) { // if (this.state.clipboardVisible || this.state.fileSystemVisible) {
return true; // return true;
} // }
this.state.client.sendKeyEvent(1, keysym); this.state.client.sendKeyEvent(1, keysym);
if (keysym === 65288) { if (keysym === 65288) {
return false; return false;
@ -416,8 +420,11 @@ class Access extends Component {
} }
}; };
const sink = new Guacamole.InputSink();
display.appendChild(sink.getElement());
// Keyboard // Keyboard
const keyboard = new Guacamole.Keyboard(document); const keyboard = new Guacamole.Keyboard(sink.getElement());
keyboard.onkeydown = this.onKeyDown; keyboard.onkeydown = this.onKeyDown;
keyboard.onkeyup = this.onKeyUp; keyboard.onkeyup = this.onKeyUp;

View File

@ -5,28 +5,28 @@
-ms-user-select: none; -ms-user-select: none;
} }
@-webkit-keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes fadeIn { @keyframes fadeIn {
0% { from {
transform: translateY(-25%); opacity: 0;
} }
50%{
transform: translateY(4%); to {
} opacity: 1;
65%{
transform: translateY(-2%);
}
80%{
transform: translateY(2%);
}
95%{
transform: translateY(-1%);
}
100% {
transform: translateY(0%);
} }
} }
.popup { .popup {
-webkit-animation-name: fadeIn;
animation-name: fadeIn; animation-name: fadeIn;
animation-duration: 0.4s; animation-duration: 0.4s;
background-clip: padding-box; background-clip: padding-box;

View File

@ -4,6 +4,7 @@ import {
CloudDownloadOutlined, CloudDownloadOutlined,
CloudUploadOutlined, CloudUploadOutlined,
DeleteOutlined, DeleteOutlined,
EditOutlined,
ExclamationCircleOutlined, ExclamationCircleOutlined,
FileExcelOutlined, FileExcelOutlined,
FileImageOutlined, FileImageOutlined,
@ -23,18 +24,15 @@ import qs from "qs";
import request from "../../common/request"; import request from "../../common/request";
import {server} from "../../common/constants"; import {server} from "../../common/constants";
import Upload from "antd/es/upload"; import Upload from "antd/es/upload";
import {download, getToken, isEmpty, renderSize} from "../../utils/utils"; import {download, getFileName, getToken, isEmpty, renderSize} from "../../utils/utils";
import './FileSystem.css' import './FileSystem.css'
const formItemLayout = {
labelCol: {span: 6},
wrapperCol: {span: 14},
};
const {confirm} = Modal; const {confirm} = Modal;
class FileSystem extends Component { class FileSystem extends Component {
mkdirFormRef = React.createRef(); mkdirFormRef = React.createRef();
renameFormRef = React.createRef();
state = { state = {
sessionId: undefined, sessionId: undefined,
@ -57,36 +55,6 @@ class FileSystem extends Component {
}); });
} }
handleOk = async (values) => {
let params = {
'dir': this.state.selectedRow.key + '/' + values['dir']
}
let paramStr = qs.stringify(params);
this.setState({
confirmLoading: true
})
let result = await request.post(`/sessions/${this.state.sessionId}/mkdir?${paramStr}`);
if (result.code === 1) {
message.success('创建成功');
this.loadFiles(this.state.currentDirectory);
} else {
message.error(result.message);
}
this.setState({
confirmLoading: false,
confirmVisible: false
})
}
upload = () => {
this.setState({
uploadVisible: true
})
}
download = () => { download = () => {
download(`${server}/sessions/${this.state.sessionId}/download?file=${this.state.selectedRow.key}`); download(`${server}/sessions/${this.state.sessionId}/download?file=${this.state.selectedRow.key}`);
} }
@ -97,9 +65,9 @@ class FileSystem extends Component {
message.warning('请至少选择一个文件或目录'); message.warning('请至少选择一个文件或目录');
} }
let title = ''; let title;
if (selectedRowKeys.length === 1) { if (selectedRowKeys.length === 1) {
let file = selectedRowKeys[0].substring(selectedRowKeys[0].lastIndexOf('/') + 1, selectedRowKeys[0].length); let file = getFileName(selectedRowKeys[0]);
title = <p>您确认要删除"{file}"</p>; title = <p>您确认要删除"{file}"</p>;
} else { } else {
title = `您确认要删除所选的${selectedRowKeys.length}项目吗?`; title = `您确认要删除所选的${selectedRowKeys.length}项目吗?`;
@ -114,7 +82,7 @@ class FileSystem extends Component {
if (rowKey === '..') { if (rowKey === '..') {
continue; continue;
} }
let result = await request.delete(`/sessions/${this.state.sessionId}/rm?key=${rowKey}`); let result = await request.post(`/sessions/${this.state.sessionId}/rm?key=${rowKey}`);
if (result['code'] !== 1) { if (result['code'] !== 1) {
message.error(result['message']); message.error(result['message']);
} }
@ -158,7 +126,8 @@ class FileSystem extends Component {
this.setState({ this.setState({
files: items, files: items,
currentDirectory: key, currentDirectory: key,
selectedRow: {} selectedRow: {},
selectedRowKeys: []
}) })
} finally { } finally {
this.setState({ this.setState({
@ -182,7 +151,6 @@ class FileSystem extends Component {
getNodeTreeRightClickMenu = () => { getNodeTreeRightClickMenu = () => {
const {pageX, pageY, visible} = {...this.state.dropdown}; const {pageX, pageY, visible} = {...this.state.dropdown};
if (visible) { if (visible) {
console.log(pageX, pageY)
const tmpStyle = { const tmpStyle = {
left: `${pageX}px`, left: `${pageX}px`,
top: `${pageY}px`, top: `${pageY}px`,
@ -195,10 +163,24 @@ class FileSystem extends Component {
disableDownload = false; disableDownload = false;
} }
let disableRename = true;
if (this.state.selectedRowKeys.length === 1) {
disableRename = false;
}
return ( return (
<ul className="popup" style={tmpStyle}> <ul className="popup" style={tmpStyle}>
<li><Button type={'text'} size={'small'} icon={<CloudDownloadOutlined/>} onClick={this.download} <li><Button type={'text'} size={'small'} icon={<CloudDownloadOutlined/>} onClick={this.download}
disabled={disableDownload}>下载</Button></li> disabled={disableDownload}>下载</Button></li>
<li><Button type={'text'} size={'small'} icon={<EditOutlined/>} disabled={disableRename}
onClick={() => {
this.setState({
renameVisible: true
})
}}
>重命名</Button></li>
<li><Button type={'text'} size={'small'} icon={<DeleteOutlined/>} onClick={this.rmdir}>删除</Button> <li><Button type={'text'} size={'small'} icon={<DeleteOutlined/>} onClick={this.rmdir}>删除</Button>
</li> </li>
</ul> </ul>
@ -365,7 +347,7 @@ class FileSystem extends Component {
</Row> </Row>
); );
const {loading, selectedRowKeys} = this.state; const {selectedRowKeys} = this.state;
const rowSelection = { const rowSelection = {
selectedRowKeys, selectedRowKeys,
onChange: (selectedRowKeys) => { onChange: (selectedRowKeys) => {
@ -465,10 +447,12 @@ class FileSystem extends Component {
<Modal <Modal
title="上传文件" title="上传文件"
visible={this.state.uploadVisible} visible={this.state.uploadVisible}
centered={true}
onOk={() => { onOk={() => {
this.setState({ this.setState({
uploadVisible: false uploadVisible: false
}) })
this.loadFiles(this.state.currentDirectory);
}} }}
confirmLoading={this.state.uploadLoading} confirmLoading={this.state.uploadLoading}
onCancel={() => { onCancel={() => {
@ -484,34 +468,135 @@ class FileSystem extends Component {
</Modal> </Modal>
<Modal {
title="创建文件夹" this.state.mkdirVisible ?
visible={this.state.mkdirVisible} <Modal
onOk={() => { title="创建文件夹"
this.mkdirFormRef.current visible={this.state.mkdirVisible}
.validateFields() centered={true}
.then(values => { onOk={() => {
this.mkdirFormRef.current.resetFields(); this.mkdirFormRef.current
this.handleOk(values); .validateFields()
}) .then(async values => {
.catch(info => { this.mkdirFormRef.current.resetFields();
let params = {
'dir': this.state.currentDirectory + '/' + values['dir']
}
let paramStr = qs.stringify(params);
}); this.setState({
}} confirmLoading: true
confirmLoading={this.state.confirmLoading} })
onCancel={() => { let result = await request.post(`/sessions/${this.state.sessionId}/mkdir?${paramStr}`);
this.setState({ if (result.code === 1) {
mkdirVisible: false message.success('创建成功');
}) this.loadFiles(this.state.currentDirectory);
}} } else {
> message.error(result.message);
<Form ref={this.mkdirFormRef}> }
this.setState({
confirmLoading: false,
mkdirVisible: false
})
})
.catch(info => {
});
}}
confirmLoading={this.state.confirmLoading}
onCancel={() => {
this.setState({
mkdirVisible: false
})
}}
>
<Form ref={this.mkdirFormRef}>
<Form.Item name='dir' rules={[{required: true, message: '请输入文件夹名称'}]}>
<Input autoComplete="off" placeholder="请输入文件夹名称"/>
</Form.Item>
</Form>
</Modal> : undefined
}
{
this.state.renameVisible ?
<Modal
title="重命名"
visible={this.state.renameVisible}
centered={true}
onOk={() => {
this.renameFormRef.current
.validateFields()
.then(async values => {
this.renameFormRef.current.resetFields();
try {
let currentDirectory = this.state.currentDirectory;
if (!currentDirectory.endsWith("/")) {
currentDirectory += '/';
}
let params = {
'oldName': this.state.selectedRowKeys[0],
'newName': currentDirectory + values['newName'],
}
if (params['oldName'] === params['newName']) {
message.success('重命名成功');
return;
}
let paramStr = qs.stringify(params);
this.setState({
confirmLoading: true
})
let result = await request.post(`/sessions/${this.state.sessionId}/rename?${paramStr}`);
if (result['code'] === 1) {
message.success('重命名成功');
let files = this.state.files;
for (let i = 0; i < files.length; i++) {
if (files['key'] === params['oldName']) {
files[i].path = params['newName'];
files[i].key = params['newName'];
files[i].name = getFileName(params['newName']);
break;
}
}
this.setState({
files: files
})
} else {
message.error(result.message);
}
} finally {
this.setState({
confirmLoading: false,
renameVisible: false
})
}
})
.catch(info => {
});
}}
confirmLoading={this.state.confirmLoading}
onCancel={() => {
this.setState({
renameVisible: false
})
}}
>
<Form ref={this.renameFormRef}
initialValues={{newName: getFileName(this.state.selectedRowKeys[0])}}>
<Form.Item name='newName' rules={[{required: true, message: '请输入新的名称'}]}>
<Input autoComplete="off" placeholder="新的名称"/>
</Form.Item>
</Form>
</Modal> : undefined
}
<Form.Item name='dir' rules={[{required: true, message: '请输入文件夹名称'}]}>
<Input autoComplete="off" placeholder="请输入文件夹名称"/>
</Form.Item>
</Form>
</Modal>
{this.getNodeTreeRightClickMenu()} {this.getNodeTreeRightClickMenu()}
</div> </div>

View File

@ -221,4 +221,8 @@ export function renderSize(value) {
let size = srcSize / Math.pow(1024, index); let size = srcSize / Math.pow(1024, index);
size = size.toFixed(2); size = size.toFixed(2);
return size + ' ' + unitArr[index]; return size + ' ' + unitArr[index];
}
export function getFileName(fullFileName){
return fullFileName.substring(fullFileName.lastIndexOf('/') + 1, fullFileName.length);
} }