优化接入功能&重构接入文件操作
This commit is contained in:
parent
29fb520e48
commit
32b31eba1a
@ -11,6 +11,7 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"next-terminal/pkg/global"
|
"next-terminal/pkg/global"
|
||||||
|
"next-terminal/pkg/guacd"
|
||||||
"next-terminal/pkg/model"
|
"next-terminal/pkg/model"
|
||||||
"next-terminal/pkg/utils"
|
"next-terminal/pkg/utils"
|
||||||
"os"
|
"os"
|
||||||
@ -18,7 +19,6 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func SessionPagingEndpoint(c echo.Context) error {
|
func SessionPagingEndpoint(c echo.Context) error {
|
||||||
@ -40,10 +40,10 @@ func SessionPagingEndpoint(c echo.Context) error {
|
|||||||
if status == model.Disconnected && len(items[i].Recording) > 0 {
|
if status == model.Disconnected && len(items[i].Recording) > 0 {
|
||||||
|
|
||||||
var recording string
|
var recording string
|
||||||
if items[i].Protocol == "rdp" || items[i].Protocol == "vnc" {
|
if items[i].Mode == model.Naive {
|
||||||
recording = items[i].Recording + "/recording"
|
|
||||||
} else {
|
|
||||||
recording = items[i].Recording
|
recording = items[i].Recording
|
||||||
|
} else {
|
||||||
|
recording = items[i].Recording + "/recording"
|
||||||
}
|
}
|
||||||
|
|
||||||
if utils.FileExists(recording) {
|
if utils.FileExists(recording) {
|
||||||
@ -100,7 +100,7 @@ func SessionDisconnectEndpoint(c echo.Context) error {
|
|||||||
|
|
||||||
split := strings.Split(sessionIds, ",")
|
split := strings.Split(sessionIds, ",")
|
||||||
for i := range split {
|
for i := range split {
|
||||||
CloseSessionById(split[i], ForcedDisconnect, "强制断开")
|
CloseSessionById(split[i], ForcedDisconnect, "forced disconnect")
|
||||||
}
|
}
|
||||||
return Success(c, nil)
|
return Success(c, nil)
|
||||||
}
|
}
|
||||||
@ -154,15 +154,11 @@ func CloseWebSocket(ws *websocket.Conn, c int, t string) {
|
|||||||
if ws == nil {
|
if ws == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ws.SetCloseHandler(func(code int, text string) error {
|
err := guacd.NewInstruction("error", "", strconv.Itoa(c))
|
||||||
var message []byte
|
_ = ws.WriteMessage(websocket.TextMessage, []byte(err.String()))
|
||||||
if code != websocket.CloseNoStatusReceived {
|
disconnect := guacd.NewInstruction("disconnect")
|
||||||
message = websocket.FormatCloseMessage(c, t)
|
_ = ws.WriteMessage(websocket.TextMessage, []byte(disconnect.String()))
|
||||||
}
|
//defer ws.Close()
|
||||||
_ = ws.WriteControl(websocket.CloseMessage, message, time.Now().Add(time.Second))
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
defer ws.Close()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func SessionResizeEndpoint(c echo.Context) error {
|
func SessionResizeEndpoint(c echo.Context) error {
|
||||||
@ -186,6 +182,14 @@ func SessionResizeEndpoint(c echo.Context) error {
|
|||||||
|
|
||||||
func SessionCreateEndpoint(c echo.Context) error {
|
func SessionCreateEndpoint(c echo.Context) error {
|
||||||
assetId := c.QueryParam("assetId")
|
assetId := c.QueryParam("assetId")
|
||||||
|
mode := c.QueryParam("mode")
|
||||||
|
|
||||||
|
if mode == model.Naive {
|
||||||
|
mode = model.Naive
|
||||||
|
} else {
|
||||||
|
mode = model.Guacd
|
||||||
|
}
|
||||||
|
|
||||||
user, _ := GetCurrentAccount(c)
|
user, _ := GetCurrentAccount(c)
|
||||||
|
|
||||||
if model.TypeUser == user.Type {
|
if model.TypeUser == user.Type {
|
||||||
@ -218,6 +222,7 @@ func SessionCreateEndpoint(c echo.Context) error {
|
|||||||
Status: model.NoConnect,
|
Status: model.NoConnect,
|
||||||
Creator: user.ID,
|
Creator: user.ID,
|
||||||
ClientIP: c.RealIP(),
|
ClientIP: c.RealIP(),
|
||||||
|
Mode: mode,
|
||||||
}
|
}
|
||||||
|
|
||||||
if asset.AccountType == "credential" {
|
if asset.AccountType == "credential" {
|
||||||
@ -356,6 +361,8 @@ type File struct {
|
|||||||
IsDir bool `json:"isDir"`
|
IsDir bool `json:"isDir"`
|
||||||
Mode string `json:"mode"`
|
Mode string `json:"mode"`
|
||||||
IsLink bool `json:"isLink"`
|
IsLink bool `json:"isLink"`
|
||||||
|
ModTime utils.JsonTime `json:"modTime"`
|
||||||
|
Size int64 `json:"size"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func SessionLsEndpoint(c echo.Context) error {
|
func SessionLsEndpoint(c echo.Context) error {
|
||||||
@ -387,12 +394,20 @@ func SessionLsEndpoint(c echo.Context) error {
|
|||||||
|
|
||||||
var files = make([]File, 0)
|
var files = make([]File, 0)
|
||||||
for i := range fileInfos {
|
for i := range fileInfos {
|
||||||
|
|
||||||
|
// 忽略因此文件
|
||||||
|
if strings.HasPrefix(fileInfos[i].Name(), ".") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
file := File{
|
file := File{
|
||||||
Name: fileInfos[i].Name(),
|
Name: fileInfos[i].Name(),
|
||||||
Path: path.Join(remoteDir, fileInfos[i].Name()),
|
Path: path.Join(remoteDir, fileInfos[i].Name()),
|
||||||
IsDir: fileInfos[i].IsDir(),
|
IsDir: fileInfos[i].IsDir(),
|
||||||
Mode: fileInfos[i].Mode().String(),
|
Mode: fileInfos[i].Mode().String(),
|
||||||
IsLink: fileInfos[i].Mode()&os.ModeSymlink == os.ModeSymlink,
|
IsLink: fileInfos[i].Mode()&os.ModeSymlink == os.ModeSymlink,
|
||||||
|
ModTime: utils.NewJsonTime(fileInfos[i].ModTime()),
|
||||||
|
Size: fileInfos[i].Size(),
|
||||||
}
|
}
|
||||||
|
|
||||||
files = append(files, file)
|
files = append(files, file)
|
||||||
@ -417,6 +432,8 @@ func SessionLsEndpoint(c echo.Context) error {
|
|||||||
IsDir: fileInfos[i].IsDir(),
|
IsDir: fileInfos[i].IsDir(),
|
||||||
Mode: fileInfos[i].Mode().String(),
|
Mode: fileInfos[i].Mode().String(),
|
||||||
IsLink: fileInfos[i].Mode()&os.ModeSymlink == os.ModeSymlink,
|
IsLink: fileInfos[i].Mode()&os.ModeSymlink == os.ModeSymlink,
|
||||||
|
ModTime: utils.NewJsonTime(fileInfos[i].ModTime()),
|
||||||
|
Size: fileInfos[i].Size(),
|
||||||
}
|
}
|
||||||
|
|
||||||
files = append(files, file)
|
files = append(files, file)
|
||||||
@ -539,10 +556,10 @@ func SessionRecordingEndpoint(c echo.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var recording string
|
var recording string
|
||||||
if session.Protocol == "rdp" || session.Protocol == "vnc" {
|
if session.Mode == model.Naive {
|
||||||
recording = session.Recording + "/recording"
|
|
||||||
} else {
|
|
||||||
recording = session.Recording
|
recording = session.Recording
|
||||||
|
} else {
|
||||||
|
recording = session.Recording + "/recording"
|
||||||
}
|
}
|
||||||
|
|
||||||
logrus.Debugf("读取录屏文件:%v,是否存在: %v, 是否为文件: %v", recording, utils.FileExists(recording), utils.IsFile(recording))
|
logrus.Debugf("读取录屏文件:%v,是否存在: %v, 是否为文件: %v", recording, utils.FileExists(recording), utils.IsFile(recording))
|
||||||
|
@ -15,10 +15,9 @@ import (
|
|||||||
const (
|
const (
|
||||||
TunnelClosed int = -1
|
TunnelClosed int = -1
|
||||||
Normal int = 0
|
Normal int = 0
|
||||||
NotFoundSession int = 2000
|
NotFoundSession int = 800
|
||||||
NewTunnelError int = 2001
|
NewTunnelError int = 801
|
||||||
NewSftpClientError int = 2002
|
ForcedDisconnect int = 802
|
||||||
ForcedDisconnect int = 2003
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TunEndpoint(c echo.Context) error {
|
func TunEndpoint(c echo.Context) error {
|
||||||
@ -145,7 +144,7 @@ func TunEndpoint(c echo.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tun := global.Tun{
|
tun := global.Tun{
|
||||||
Protocol: configuration.Protocol,
|
Protocol: session.Protocol,
|
||||||
Tunnel: tunnel,
|
Tunnel: tunnel,
|
||||||
WebSocket: ws,
|
WebSocket: ws,
|
||||||
}
|
}
|
||||||
|
@ -20,9 +20,14 @@ func (r *Tun) Close() {
|
|||||||
if r.Protocol == "rdp" || r.Protocol == "vnc" {
|
if r.Protocol == "rdp" || r.Protocol == "vnc" {
|
||||||
_ = r.Tunnel.Close()
|
_ = r.Tunnel.Close()
|
||||||
} else {
|
} else {
|
||||||
|
if r.SshClient != nil {
|
||||||
_ = r.SshClient.Close()
|
_ = r.SshClient.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
if r.SftpClient != nil {
|
||||||
_ = r.SftpClient.Close()
|
_ = r.SftpClient.Close()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type Observable struct {
|
type Observable struct {
|
||||||
|
@ -13,6 +13,11 @@ const (
|
|||||||
Disconnected = "disconnected"
|
Disconnected = "disconnected"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
Guacd = "guacd"
|
||||||
|
Naive = "naive"
|
||||||
|
)
|
||||||
|
|
||||||
type Session struct {
|
type Session struct {
|
||||||
ID string `gorm:"primary_key" json:"id"`
|
ID string `gorm:"primary_key" json:"id"`
|
||||||
Protocol string `json:"protocol"`
|
Protocol string `json:"protocol"`
|
||||||
@ -34,6 +39,7 @@ type Session struct {
|
|||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
ConnectedTime utils.JsonTime `json:"connectedTime"`
|
ConnectedTime utils.JsonTime `json:"connectedTime"`
|
||||||
DisconnectedTime utils.JsonTime `json:"disconnectedTime"`
|
DisconnectedTime utils.JsonTime `json:"disconnectedTime"`
|
||||||
|
Mode string `json:"mode"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Session) TableName() string {
|
func (r *Session) TableName() string {
|
||||||
@ -60,6 +66,7 @@ type SessionVo struct {
|
|||||||
CreatorName string `json:"creatorName"`
|
CreatorName string `json:"creatorName"`
|
||||||
Code int `json:"code"`
|
Code int `json:"code"`
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
|
Mode string `json:"mode"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func FindPageSession(pageIndex, pageSize int, status, userId, clientIp, assetId, protocol string) (results []SessionVo, total int64, err error) {
|
func FindPageSession(pageIndex, pageSize int, status, userId, clientIp, assetId, protocol string) (results []SessionVo, total int64, err error) {
|
||||||
@ -69,7 +76,7 @@ func FindPageSession(pageIndex, pageSize int, status, userId, clientIp, assetId,
|
|||||||
|
|
||||||
params = append(params, status)
|
params = append(params, status)
|
||||||
|
|
||||||
itemSql := "SELECT s.id, s.protocol,s.recording, s.connection_id, s.asset_id, s.creator, s.client_ip, s.width, s.height, s.ip, s.port, s.username, s.status, s.connected_time, s.disconnected_time,s.code, s.message, a.name AS asset_name, u.nickname AS creator_name FROM sessions s LEFT JOIN assets a ON s.asset_id = a.id LEFT JOIN users u ON s.creator = u.id WHERE s.STATUS = ? "
|
itemSql := "SELECT s.id,s.mode, s.protocol,s.recording, s.connection_id, s.asset_id, s.creator, s.client_ip, s.width, s.height, s.ip, s.port, s.username, s.status, s.connected_time, s.disconnected_time,s.code, s.message, a.name AS asset_name, u.nickname AS creator_name FROM sessions s LEFT JOIN assets a ON s.asset_id = a.id LEFT JOIN users u ON s.creator = u.id WHERE s.STATUS = ? "
|
||||||
countSql := "select count(*) from sessions as s where s.status = ? "
|
countSql := "select count(*) from sessions as s where s.status = ? "
|
||||||
|
|
||||||
if len(userId) > 0 {
|
if len(userId) > 0 {
|
||||||
|
@ -40,7 +40,7 @@ import {isEmpty, NT_PACKAGE} from "./utils/utils";
|
|||||||
import {isAdmin} from "./service/permission";
|
import {isAdmin} from "./service/permission";
|
||||||
import UserGroup from "./components/user/UserGroup";
|
import UserGroup from "./components/user/UserGroup";
|
||||||
import LoginLog from "./components/session/LoginLog";
|
import LoginLog from "./components/session/LoginLog";
|
||||||
import AccessSSH from "./components/access/AccessSSH";
|
import AccessNaive from "./components/access/AccessNaive";
|
||||||
|
|
||||||
const {Footer, Sider} = Layout;
|
const {Footer, Sider} = Layout;
|
||||||
|
|
||||||
@ -114,7 +114,7 @@ class App extends Component {
|
|||||||
|
|
||||||
<Switch>
|
<Switch>
|
||||||
<Route path="/access" component={Access}/>
|
<Route path="/access" component={Access}/>
|
||||||
<Route path="/access-ssh" component={AccessSSH}/>
|
<Route path="/access-naive" component={AccessNaive}/>
|
||||||
<Route path="/login"><Login updateUser={this.updateUser}/></Route>
|
<Route path="/login"><Login updateUser={this.updateUser}/></Route>
|
||||||
|
|
||||||
<Route path="/">
|
<Route path="/">
|
||||||
|
@ -1,50 +1,28 @@
|
|||||||
import React, {Component} from 'react';
|
import React, {Component} from 'react';
|
||||||
import Guacamole from 'guacamole-common-js';
|
import Guacamole from 'guacamole-common-js';
|
||||||
import {
|
import {Affix, Button, Col, Drawer, Dropdown, Form, Input, Menu, message, Modal, Row, Space, Tooltip} from 'antd'
|
||||||
Affix,
|
|
||||||
Alert,
|
|
||||||
Button,
|
|
||||||
Card,
|
|
||||||
Col,
|
|
||||||
Drawer,
|
|
||||||
Dropdown,
|
|
||||||
Form,
|
|
||||||
Input,
|
|
||||||
Menu,
|
|
||||||
message,
|
|
||||||
Modal,
|
|
||||||
Row,
|
|
||||||
Space,
|
|
||||||
Spin,
|
|
||||||
Tooltip,
|
|
||||||
Tree
|
|
||||||
} from 'antd'
|
|
||||||
import qs from "qs";
|
import qs from "qs";
|
||||||
import request from "../../common/request";
|
import request from "../../common/request";
|
||||||
import {server, wsServer} from "../../common/constants";
|
import {wsServer} from "../../common/constants";
|
||||||
import {
|
import {
|
||||||
AppstoreTwoTone,
|
AppstoreTwoTone,
|
||||||
CloudDownloadOutlined,
|
CloudDownloadOutlined,
|
||||||
CloudUploadOutlined,
|
CloudUploadOutlined,
|
||||||
CopyOutlined,
|
CopyTwoTone,
|
||||||
DeleteOutlined,
|
DeleteOutlined,
|
||||||
DesktopOutlined,
|
DesktopOutlined,
|
||||||
ExclamationCircleOutlined,
|
ExclamationCircleOutlined,
|
||||||
ExpandOutlined,
|
ExpandOutlined,
|
||||||
FileZipOutlined,
|
|
||||||
FolderAddOutlined,
|
FolderAddOutlined,
|
||||||
LoadingOutlined,
|
LoadingOutlined,
|
||||||
ReloadOutlined,
|
ReloadOutlined
|
||||||
UploadOutlined
|
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import Upload from "antd/es/upload";
|
import {exitFull, getToken, isEmpty, requestFullScreen} from "../../utils/utils";
|
||||||
import {download, exitFull, getToken, isEmpty, requestFullScreen} from "../../utils/utils";
|
|
||||||
import './Access.css'
|
import './Access.css'
|
||||||
import Draggable from 'react-draggable';
|
import Draggable from 'react-draggable';
|
||||||
|
import FileSystem from "./FileSystem";
|
||||||
|
|
||||||
const {TextArea} = Input;
|
const {TextArea} = Input;
|
||||||
const {DirectoryTree} = Tree;
|
|
||||||
const {SubMenu} = Menu;
|
|
||||||
|
|
||||||
const STATE_IDLE = 0;
|
const STATE_IDLE = 0;
|
||||||
const STATE_CONNECTING = 1;
|
const STATE_CONNECTING = 1;
|
||||||
@ -53,16 +31,10 @@ const STATE_CONNECTED = 3;
|
|||||||
const STATE_DISCONNECTING = 4;
|
const STATE_DISCONNECTING = 4;
|
||||||
const STATE_DISCONNECTED = 5;
|
const STATE_DISCONNECTED = 5;
|
||||||
|
|
||||||
const antIcon = <LoadingOutlined/>;
|
|
||||||
|
|
||||||
const formItemLayout = {
|
|
||||||
labelCol: {span: 6},
|
|
||||||
wrapperCol: {span: 14},
|
|
||||||
};
|
|
||||||
|
|
||||||
class Access extends Component {
|
class Access extends Component {
|
||||||
|
|
||||||
formRef = React.createRef()
|
formRef = React.createRef();
|
||||||
|
clipboardFormRef = React.createRef();
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
sessionId: '',
|
sessionId: '',
|
||||||
@ -73,19 +45,12 @@ class Access extends Component {
|
|||||||
containerOverflow: 'hidden',
|
containerOverflow: 'hidden',
|
||||||
containerWidth: 0,
|
containerWidth: 0,
|
||||||
containerHeight: 0,
|
containerHeight: 0,
|
||||||
fileSystemVisible: false,
|
|
||||||
fileSystem: {
|
|
||||||
loading: false,
|
|
||||||
object: null,
|
|
||||||
currentDirectory: '/',
|
|
||||||
files: [],
|
|
||||||
},
|
|
||||||
uploadAction: '',
|
uploadAction: '',
|
||||||
uploadHeaders: {},
|
uploadHeaders: {},
|
||||||
keyboard: {},
|
keyboard: {},
|
||||||
protocol: '',
|
protocol: '',
|
||||||
treeData: [],
|
treeData: [],
|
||||||
selectNode: {},
|
|
||||||
confirmVisible: false,
|
confirmVisible: false,
|
||||||
confirmLoading: false,
|
confirmLoading: false,
|
||||||
uploadVisible: false,
|
uploadVisible: false,
|
||||||
@ -97,10 +62,11 @@ class Access extends Component {
|
|||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
|
|
||||||
let params = new URLSearchParams(this.props.location.search);
|
let urlParams = new URLSearchParams(this.props.location.search);
|
||||||
let assetsId = params.get('assetsId');
|
let assetId = urlParams.get('assetId');
|
||||||
let protocol = params.get('protocol');
|
document.title = urlParams.get('assetName');
|
||||||
let sessionId = await this.createSession(assetsId);
|
let protocol = urlParams.get('protocol');
|
||||||
|
let sessionId = await this.createSession(assetId);
|
||||||
if (isEmpty(sessionId)) {
|
if (isEmpty(sessionId)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -150,11 +116,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.success('您输入的内容已复制到远程服务器上,使用右键将自动粘贴。');
|
message.info('您输入的内容已复制到远程服务器上,使用右键将自动粘贴。');
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (data.data && data.data.length > 0) {
|
if (data.data && data.data.length > 0) {
|
||||||
// message.success('您输入的内容已复制到远程服务器上');
|
message.info('您输入的内容已复制到远程服务器上');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -162,7 +128,7 @@ class Access extends Component {
|
|||||||
|
|
||||||
onTunnelStateChange = (state) => {
|
onTunnelStateChange = (state) => {
|
||||||
if (state === Guacamole.Tunnel.State.CLOSED) {
|
if (state === Guacamole.Tunnel.State.CLOSED) {
|
||||||
this.showMessage('连接已关闭');
|
console.log('web socket 已关闭');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -277,8 +243,14 @@ class Access extends Component {
|
|||||||
case 783:
|
case 783:
|
||||||
this.showMessage('错误的请求类型');
|
this.showMessage('错误的请求类型');
|
||||||
break;
|
break;
|
||||||
case 797:
|
case 800:
|
||||||
this.showMessage('客户端连接数量过多');
|
this.showMessage('会话不存在');
|
||||||
|
break;
|
||||||
|
case 801:
|
||||||
|
this.showMessage('创建隧道失败');
|
||||||
|
break;
|
||||||
|
case 802:
|
||||||
|
this.showMessage('管理员强制断开了此会话');
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
this.showMessage('未知错误。');
|
this.showMessage('未知错误。');
|
||||||
@ -320,7 +292,7 @@ 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.success('您选择的内容已复制到您的粘贴板中,在右侧的输入框中可同时查看到。');
|
message.info('您选择的内容已复制到您的粘贴板中,在右侧的输入框中可同时查看到。');
|
||||||
this.setState({
|
this.setState({
|
||||||
clipboardText: data
|
clipboardText: data
|
||||||
});
|
});
|
||||||
@ -343,17 +315,6 @@ class Access extends Component {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
uploadChange = (info) => {
|
|
||||||
if (info.file.status !== 'uploading') {
|
|
||||||
|
|
||||||
}
|
|
||||||
if (info.file.status === 'done') {
|
|
||||||
message.success(`${info.file.name} 文件上传成功。`, 3);
|
|
||||||
} else if (info.file.status === 'error') {
|
|
||||||
message.error(`${info.file.name} 文件上传失败。`, 10);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onKeyDown = (keysym) => {
|
onKeyDown = (keysym) => {
|
||||||
if (true === this.state.clipboardVisible || true === this.state.confirmVisible) {
|
if (true === this.state.clipboardVisible || true === this.state.confirmVisible) {
|
||||||
return true;
|
return true;
|
||||||
@ -368,20 +329,6 @@ class Access extends Component {
|
|||||||
this.state.client.sendKeyEvent(0, keysym);
|
this.state.client.sendKeyEvent(0, keysym);
|
||||||
};
|
};
|
||||||
|
|
||||||
showFileSystem = () => {
|
|
||||||
this.setState({
|
|
||||||
fileSystemVisible: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.loadDirData('/');
|
|
||||||
};
|
|
||||||
|
|
||||||
hideFileSystem = () => {
|
|
||||||
this.setState({
|
|
||||||
fileSystemVisible: false,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
fullScreen = () => {
|
fullScreen = () => {
|
||||||
let fs = this.state.fullScreen;
|
let fs = this.state.fullScreen;
|
||||||
if (fs) {
|
if (fs) {
|
||||||
@ -400,43 +347,12 @@ class Access extends Component {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
showClipboard = () => {
|
|
||||||
this.setState({
|
|
||||||
clipboardVisible: true
|
|
||||||
}, () => {
|
|
||||||
let element = document.getElementById('clipboard');
|
|
||||||
if (element) {
|
|
||||||
element.value = this.state.clipboardText;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
hideClipboard = () => {
|
|
||||||
this.setState({
|
|
||||||
clipboardVisible: false
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
updateClipboardFormTextarea = () => {
|
|
||||||
let clipboardText = document.getElementById('clipboard').value;
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
clipboardText: clipboardText
|
|
||||||
});
|
|
||||||
|
|
||||||
this.sendClipboard({
|
|
||||||
'data': clipboardText,
|
|
||||||
'type': 'text/plain'
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
async createSession(assetsId) {
|
async createSession(assetsId) {
|
||||||
let result = await request.post(`/sessions?assetId=${assetsId}`);
|
let result = await request.post(`/sessions?assetId=${assetsId}&mode=guacd`);
|
||||||
if (result['code'] !== 1) {
|
if (result['code'] !== 1) {
|
||||||
this.showMessage(result['message']);
|
this.showMessage(result['message']);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
document.title = result['data']['name'];
|
|
||||||
return result['data']['id'];
|
return result['data']['id'];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -516,23 +432,6 @@ class Access extends Component {
|
|||||||
keyboard.onkeydown = this.onKeyDown;
|
keyboard.onkeydown = this.onKeyDown;
|
||||||
keyboard.onkeyup = this.onKeyUp;
|
keyboard.onkeyup = this.onKeyUp;
|
||||||
|
|
||||||
let stateChecker = setInterval(async () => {
|
|
||||||
let result = await request.get(`/sessions/${sessionId}/status`);
|
|
||||||
if (result['code'] !== 1) {
|
|
||||||
clearInterval(stateChecker);
|
|
||||||
} else {
|
|
||||||
let session = result['data'];
|
|
||||||
if (session['status'] === 'connected') {
|
|
||||||
clearInterval(stateChecker);
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (session['status'] === 'disconnected') {
|
|
||||||
this.showMessage(session['message']);
|
|
||||||
clearInterval(stateChecker);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, 1000)
|
|
||||||
this.setState({
|
this.setState({
|
||||||
client: client,
|
client: client,
|
||||||
containerWidth: width,
|
containerWidth: width,
|
||||||
@ -620,185 +519,6 @@ class Access extends Component {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
onSelect = (keys, event) => {
|
|
||||||
this.setState({
|
|
||||||
selectNode: {
|
|
||||||
key: keys[0],
|
|
||||||
isLeaf: event.node.isLeaf
|
|
||||||
}
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
handleOk = async (values) => {
|
|
||||||
let params = {
|
|
||||||
'dir': this.state.selectNode.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('创建成功');
|
|
||||||
let parentPath = this.state.selectNode.key;
|
|
||||||
let items = await this.getTreeNodes(parentPath);
|
|
||||||
this.setState({
|
|
||||||
treeData: this.updateTreeData(this.state.treeData, parentPath, items),
|
|
||||||
selectNode: {}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
message.error(result.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
confirmLoading: false,
|
|
||||||
confirmVisible: false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
handleConfirmCancel = () => {
|
|
||||||
this.setState({
|
|
||||||
confirmVisible: false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
handleUploadCancel = () => {
|
|
||||||
this.setState({
|
|
||||||
uploadVisible: false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
mkdir = () => {
|
|
||||||
if (!this.state.selectNode.key || this.state.selectNode.isLeaf) {
|
|
||||||
message.warning('请选择一个目录');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.setState({
|
|
||||||
confirmVisible: true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
upload = () => {
|
|
||||||
if (!this.state.selectNode.key || this.state.selectNode.isLeaf) {
|
|
||||||
message.warning('请选择一个目录进行上传');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.setState({
|
|
||||||
uploadVisible: true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
download = () => {
|
|
||||||
if (!this.state.selectNode.key || !this.state.selectNode.isLeaf) {
|
|
||||||
message.warning('当前只支持下载文件');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
download(`${server}/sessions/${this.state.sessionId}/download?file=${this.state.selectNode.key}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
rmdir = async () => {
|
|
||||||
if (!this.state.selectNode.key) {
|
|
||||||
message.warning('请选择一个文件或目录');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let result;
|
|
||||||
if (this.state.selectNode.isLeaf) {
|
|
||||||
result = await request.delete(`/sessions/${this.state.sessionId}/rm?file=${this.state.selectNode.key}`);
|
|
||||||
} else {
|
|
||||||
result = await request.delete(`/sessions/${this.state.sessionId}/rmdir?dir=${this.state.selectNode.key}`);
|
|
||||||
}
|
|
||||||
if (result.code !== 1) {
|
|
||||||
message.error(result.message);
|
|
||||||
} else {
|
|
||||||
message.success('删除成功');
|
|
||||||
let path = this.state.selectNode.key;
|
|
||||||
let parentPath = path.substring(0, path.lastIndexOf('/'));
|
|
||||||
let items = await this.getTreeNodes(parentPath);
|
|
||||||
this.setState({
|
|
||||||
treeData: this.updateTreeData(this.state.treeData, parentPath, items),
|
|
||||||
selectNode: {}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
refresh = async () => {
|
|
||||||
if (!this.state.selectNode.key || this.state.selectNode.isLeaf) {
|
|
||||||
await this.loadDirData('/');
|
|
||||||
} else {
|
|
||||||
let key = this.state.selectNode.key;
|
|
||||||
let items = await this.getTreeNodes(key);
|
|
||||||
this.setState({
|
|
||||||
treeData: this.updateTreeData(this.state.treeData, key, items),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
message.success('刷新目录成功');
|
|
||||||
}
|
|
||||||
|
|
||||||
onRightClick = ({event, node}) => {
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
loadDirData = async (key) => {
|
|
||||||
let items = await this.getTreeNodes(key);
|
|
||||||
this.setState({
|
|
||||||
treeData: items,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
getTreeNodes = async (key) => {
|
|
||||||
const url = server + '/sessions/' + this.state.sessionId + '/ls?dir=' + key;
|
|
||||||
|
|
||||||
let result = await request.get(url);
|
|
||||||
|
|
||||||
if (result.code !== 1) {
|
|
||||||
message.error(result['message']);
|
|
||||||
message.error(result['message']);
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
let data = result.data;
|
|
||||||
|
|
||||||
data = data.sort(((a, b) => a.name.localeCompare(b.name)));
|
|
||||||
|
|
||||||
return data.map(item => {
|
|
||||||
return {
|
|
||||||
title: item['name'],
|
|
||||||
key: item['path'],
|
|
||||||
isLeaf: !item['isDir'] && !item['isLink'],
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
onLoadData = ({key, children}) => {
|
|
||||||
|
|
||||||
return new Promise(async (resolve) => {
|
|
||||||
if (children) {
|
|
||||||
resolve();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let items = await this.getTreeNodes(key);
|
|
||||||
this.setState({
|
|
||||||
treeData: this.updateTreeData(this.state.treeData, key, items),
|
|
||||||
});
|
|
||||||
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
updateTreeData = (list, key, children) => {
|
|
||||||
return list.map((node) => {
|
|
||||||
if (node.key === key) {
|
|
||||||
return {...node, children};
|
|
||||||
} else if (node.children) {
|
|
||||||
return {...node, children: this.updateTreeData(node.children, key, children)};
|
|
||||||
}
|
|
||||||
|
|
||||||
return node;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
sendCombinationKey = (keys) => {
|
sendCombinationKey = (keys) => {
|
||||||
for (let i = 0; i < keys.length; i++) {
|
for (let i = 0; i < keys.length; i++) {
|
||||||
this.state.client.sendKeyEvent(1, keys[i]);
|
this.state.client.sendKeyEvent(1, keys[i]);
|
||||||
@ -810,52 +530,8 @@ class Access extends Component {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|
||||||
const title = (
|
|
||||||
<Row>
|
|
||||||
<Space>
|
|
||||||
远程文件管理
|
|
||||||
|
|
||||||
|
|
||||||
<Tooltip title="创建文件夹">
|
|
||||||
<Button type="primary" size="small" icon={<FolderAddOutlined/>}
|
|
||||||
onClick={this.mkdir} ghost/>
|
|
||||||
</Tooltip>
|
|
||||||
|
|
||||||
<Tooltip title="上传">
|
|
||||||
<Button type="primary" size="small" icon={<CloudUploadOutlined/>}
|
|
||||||
onClick={this.upload} ghost/>
|
|
||||||
</Tooltip>
|
|
||||||
|
|
||||||
<Tooltip title="下载">
|
|
||||||
<Button type="primary" size="small" icon={<CloudDownloadOutlined/>}
|
|
||||||
onClick={this.download} ghost/>
|
|
||||||
</Tooltip>
|
|
||||||
|
|
||||||
<Tooltip title="删除文件">
|
|
||||||
<Button type="dashed" size="small" icon={<DeleteOutlined/>} onClick={this.rmdir}
|
|
||||||
danger/>
|
|
||||||
</Tooltip>
|
|
||||||
|
|
||||||
<Tooltip title="刷新">
|
|
||||||
<Button type="primary" size="small" icon={<ReloadOutlined/>} onClick={this.refresh}
|
|
||||||
ghost/>
|
|
||||||
</Tooltip>
|
|
||||||
</Space>
|
|
||||||
</Row>
|
|
||||||
);
|
|
||||||
|
|
||||||
const menu = (
|
const menu = (
|
||||||
<Menu>
|
<Menu>
|
||||||
<Menu.Item key="1" icon={<CopyOutlined/>} onClick={this.showClipboard}>
|
|
||||||
剪贴板
|
|
||||||
</Menu.Item>
|
|
||||||
<Menu.Item key="2" icon={<FileZipOutlined/>} onClick={this.showFileSystem}>
|
|
||||||
文件管理
|
|
||||||
</Menu.Item>
|
|
||||||
<Menu.Item key="3" icon={<ExpandOutlined/>} onClick={this.fullScreen}>
|
|
||||||
{this.state.fullScreenBtnText}
|
|
||||||
</Menu.Item>
|
|
||||||
<SubMenu title="发送快捷键" icon={<DesktopOutlined/>}>
|
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
onClick={() => this.sendCombinationKey(['65507', '65513', '65535'])}>Ctrl+Alt+Delete</Menu.Item>
|
onClick={() => this.sendCombinationKey(['65507', '65513', '65535'])}>Ctrl+Alt+Delete</Menu.Item>
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
@ -870,7 +546,6 @@ class Access extends Component {
|
|||||||
onClick={() => this.sendCombinationKey(['65515', '120'])}>Windows+X</Menu.Item>
|
onClick={() => this.sendCombinationKey(['65515', '120'])}>Windows+X</Menu.Item>
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
onClick={() => this.sendCombinationKey(['65515'])}>Windows</Menu.Item>
|
onClick={() => this.sendCombinationKey(['65515'])}>Windows</Menu.Item>
|
||||||
</SubMenu>
|
|
||||||
</Menu>
|
</Menu>
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -885,100 +560,106 @@ class Access extends Component {
|
|||||||
<div id="display"/>
|
<div id="display"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Modal
|
|
||||||
title="创建文件夹"
|
|
||||||
visible={this.state.confirmVisible}
|
|
||||||
onOk={() => {
|
|
||||||
this.formRef.current
|
|
||||||
.validateFields()
|
|
||||||
.then(values => {
|
|
||||||
this.formRef.current.resetFields();
|
|
||||||
this.handleOk(values);
|
|
||||||
})
|
|
||||||
.catch(info => {
|
|
||||||
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
confirmLoading={this.state.confirmLoading}
|
|
||||||
onCancel={this.handleConfirmCancel}
|
|
||||||
>
|
|
||||||
<Form ref={this.formRef} {...formItemLayout}>
|
|
||||||
|
|
||||||
<Form.Item label="文件夹名称" name='dir' rules={[{required: true, message: '请输入文件夹名称'}]}>
|
|
||||||
<Input autoComplete="off" placeholder="请输入文件夹名称"/>
|
|
||||||
</Form.Item>
|
|
||||||
</Form>
|
|
||||||
</Modal>
|
|
||||||
|
|
||||||
<Modal
|
|
||||||
title="上传文件"
|
|
||||||
visible={this.state.uploadVisible}
|
|
||||||
onOk={() => {
|
|
||||||
|
|
||||||
}}
|
|
||||||
confirmLoading={this.state.uploadLoading}
|
|
||||||
onCancel={this.handleUploadCancel}
|
|
||||||
>
|
|
||||||
<Upload
|
|
||||||
action={server + '/sessions/' + this.state.sessionId + '/upload?X-Auth-Token=' + getToken() + '&dir=' + this.state.selectNode.key}>
|
|
||||||
<Button icon={<UploadOutlined/>}>上传文件</Button>
|
|
||||||
</Upload>
|
|
||||||
</Modal>
|
|
||||||
|
|
||||||
|
|
||||||
<Draggable>
|
<Draggable>
|
||||||
<Affix style={{position: 'absolute', top: 50, right: 100}}>
|
<Affix style={{position: 'absolute', top: 50, right: 100}}>
|
||||||
|
<Button icon={<ExpandOutlined/>} onClick={() => {
|
||||||
|
this.fullScreen();
|
||||||
|
}}/>
|
||||||
|
</Affix>
|
||||||
|
</Draggable>
|
||||||
|
|
||||||
|
<Draggable>
|
||||||
|
<Affix style={{position: 'absolute', top: 50, right: 150}}>
|
||||||
|
<Button icon={<CopyTwoTone/>} onClick={() => {
|
||||||
|
this.setState({
|
||||||
|
clipboardVisible: true
|
||||||
|
});
|
||||||
|
}}/>
|
||||||
|
</Affix>
|
||||||
|
</Draggable>
|
||||||
|
|
||||||
|
<Draggable>
|
||||||
|
<Affix style={{position: 'absolute', top: 100, right: 100}}>
|
||||||
|
<Button icon={<AppstoreTwoTone/>} onClick={() => {
|
||||||
|
this.setState({
|
||||||
|
fileSystemVisible: true,
|
||||||
|
});
|
||||||
|
}}/>
|
||||||
|
</Affix>
|
||||||
|
</Draggable>
|
||||||
|
|
||||||
|
<Draggable>
|
||||||
|
<Affix style={{position: 'absolute', top: 100, right: 150}}>
|
||||||
<Dropdown overlay={menu} trigger={['click']} placement="bottomLeft">
|
<Dropdown overlay={menu} trigger={['click']} placement="bottomLeft">
|
||||||
<Button icon={<AppstoreTwoTone/>}/>
|
<Button icon={<DesktopOutlined/>}/>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
</Affix>
|
</Affix>
|
||||||
</Draggable>
|
</Draggable>
|
||||||
|
|
||||||
<Drawer
|
<Drawer
|
||||||
title={title}
|
title={'会话详情'}
|
||||||
placement="right"
|
placement="right"
|
||||||
width={window.innerWidth * 0.3}
|
width={window.innerWidth * 0.5}
|
||||||
closable={true}
|
closable={true}
|
||||||
maskClosable={false}
|
maskClosable={false}
|
||||||
onClose={this.hideFileSystem}
|
onClose={()=>{
|
||||||
|
this.setState({
|
||||||
|
fileSystemVisible: false
|
||||||
|
});
|
||||||
|
}}
|
||||||
visible={this.state.fileSystemVisible}
|
visible={this.state.fileSystemVisible}
|
||||||
>
|
>
|
||||||
|
|
||||||
|
|
||||||
<Row style={{marginTop: 10}}>
|
<Row style={{marginTop: 10}}>
|
||||||
<Col span={24}>
|
<Col span={24}>
|
||||||
<Card title={this.state.fileSystem.currentDirectory} bordered={true} size="small">
|
<FileSystem sessionId={this.state.sessionId}/>
|
||||||
<Spin indicator={antIcon} spinning={this.state.fileSystem.loading}>
|
|
||||||
|
|
||||||
<DirectoryTree
|
|
||||||
// multiple
|
|
||||||
onSelect={this.onSelect}
|
|
||||||
loadData={this.onLoadData}
|
|
||||||
treeData={this.state.treeData}
|
|
||||||
onRightClick={this.onRightClick}
|
|
||||||
/>
|
|
||||||
|
|
||||||
</Spin>
|
|
||||||
</Card>
|
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
</Drawer>
|
</Drawer>
|
||||||
|
|
||||||
<Drawer
|
{
|
||||||
|
this.state.clipboardVisible ?
|
||||||
|
<Modal
|
||||||
title="剪贴板"
|
title="剪贴板"
|
||||||
placement="right"
|
maskClosable={false}
|
||||||
width={window.innerWidth * 0.3}
|
|
||||||
onClose={this.hideClipboard}
|
|
||||||
visible={this.state.clipboardVisible}
|
visible={this.state.clipboardVisible}
|
||||||
|
onOk={() => {
|
||||||
|
this.clipboardFormRef.current
|
||||||
|
.validateFields()
|
||||||
|
.then(values => {
|
||||||
|
let clipboardText = values['clipboard'];
|
||||||
|
|
||||||
|
this.sendClipboard({
|
||||||
|
'data': clipboardText,
|
||||||
|
'type': 'text/plain'
|
||||||
|
});
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
clipboardText: clipboardText,
|
||||||
|
clipboardVisible: false
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(info => {
|
||||||
|
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
confirmLoading={this.state.confirmLoading}
|
||||||
|
onCancel={() => {
|
||||||
|
this.setState({
|
||||||
|
clipboardVisible: false
|
||||||
|
})
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
|
<Form ref={this.clipboardFormRef} initialValues={{'clipboard': this.state.clipboardText}}>
|
||||||
|
<Form.Item name='clipboard' rules={[{required: false}]}>
|
||||||
|
<TextArea id='clipboard' rows={10}/>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
</Modal>
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
|
||||||
<Alert message="复制/剪切的文本将出现在这里。对下面文本内容所作的修改将会影响远程电脑上的剪贴板。" type="info" showIcon closable/>
|
|
||||||
|
|
||||||
<div style={{marginTop: 10, marginBottom: 10}}>
|
|
||||||
<TextArea id='clipboard' rows={10} onBlur={this.updateClipboardFormTextarea}/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</Drawer>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -3,14 +3,13 @@ import "xterm/css/xterm.css"
|
|||||||
import {Terminal} from "xterm";
|
import {Terminal} from "xterm";
|
||||||
import qs from "qs";
|
import qs from "qs";
|
||||||
import {wsServer} from "../../common/constants";
|
import {wsServer} from "../../common/constants";
|
||||||
import "./Console.css"
|
|
||||||
import {getToken, isEmpty} from "../../utils/utils";
|
import {getToken, isEmpty} from "../../utils/utils";
|
||||||
import {FitAddon} from 'xterm-addon-fit';
|
import {FitAddon} from 'xterm-addon-fit';
|
||||||
import "./Access.css"
|
import "./Access.css"
|
||||||
import request from "../../common/request";
|
import request from "../../common/request";
|
||||||
import {message} from "antd";
|
import {message} from "antd";
|
||||||
|
|
||||||
class AccessSSH extends Component {
|
class AccessNaive extends Component {
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
width: window.innerWidth,
|
width: window.innerWidth,
|
||||||
@ -24,6 +23,7 @@ class AccessSSH extends Component {
|
|||||||
|
|
||||||
let urlParams = new URLSearchParams(this.props.location.search);
|
let urlParams = new URLSearchParams(this.props.location.search);
|
||||||
let assetId = urlParams.get('assetId');
|
let assetId = urlParams.get('assetId');
|
||||||
|
document.title = urlParams.get('assetName');
|
||||||
|
|
||||||
let sessionId = await this.createSession(assetId);
|
let sessionId = await this.createSession(assetId);
|
||||||
if (isEmpty(sessionId)) {
|
if (isEmpty(sessionId)) {
|
||||||
@ -144,12 +144,11 @@ class AccessSSH extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async createSession(assetsId) {
|
async createSession(assetsId) {
|
||||||
let result = await request.post(`/sessions?assetId=${assetsId}`);
|
let result = await request.post(`/sessions?assetId=${assetsId}&mode=naive`);
|
||||||
if (result['code'] !== 1) {
|
if (result['code'] !== 1) {
|
||||||
this.showMessage(result['message']);
|
this.showMessage(result['message']);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
document.title = result['data']['name'];
|
|
||||||
return result['data']['id'];
|
return result['data']['id'];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,4 +202,4 @@ class AccessSSH extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default AccessSSH;
|
export default AccessNaive;
|
474
web/src/components/access/FileSystem.js
Normal file
474
web/src/components/access/FileSystem.js
Normal file
@ -0,0 +1,474 @@
|
|||||||
|
import React, {Component} from 'react';
|
||||||
|
import {Button, Card, Form, Input, message, Modal, Row, Space, Table, Tooltip, Tree} from "antd";
|
||||||
|
import {
|
||||||
|
CloudDownloadOutlined,
|
||||||
|
CloudUploadOutlined,
|
||||||
|
DeleteOutlined,
|
||||||
|
FileExcelTwoTone,
|
||||||
|
FileImageTwoTone,
|
||||||
|
FileMarkdownTwoTone,
|
||||||
|
FilePdfTwoTone,
|
||||||
|
FileTextTwoTone,
|
||||||
|
FileTwoTone,
|
||||||
|
FileWordTwoTone,
|
||||||
|
FileZipTwoTone,
|
||||||
|
FolderAddOutlined,
|
||||||
|
FolderTwoTone,
|
||||||
|
LinkOutlined,
|
||||||
|
LoadingOutlined,
|
||||||
|
ReloadOutlined,
|
||||||
|
UploadOutlined
|
||||||
|
} from "@ant-design/icons";
|
||||||
|
import qs from "qs";
|
||||||
|
import request from "../../common/request";
|
||||||
|
import {server} from "../../common/constants";
|
||||||
|
import Upload from "antd/es/upload";
|
||||||
|
import {download, getToken, renderSize} from "../../utils/utils";
|
||||||
|
|
||||||
|
const antIcon = <LoadingOutlined/>;
|
||||||
|
|
||||||
|
const formItemLayout = {
|
||||||
|
labelCol: {span: 6},
|
||||||
|
wrapperCol: {span: 14},
|
||||||
|
};
|
||||||
|
|
||||||
|
const {DirectoryTree} = Tree;
|
||||||
|
|
||||||
|
class FileSystem extends Component {
|
||||||
|
|
||||||
|
state = {
|
||||||
|
sessionId: undefined,
|
||||||
|
currentDirectory: '/',
|
||||||
|
files: [],
|
||||||
|
loading: false,
|
||||||
|
selectNode: {},
|
||||||
|
selectedRowKeys: []
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
let sessionId = this.props.sessionId;
|
||||||
|
this.setState({
|
||||||
|
sessionId: sessionId
|
||||||
|
}, () => {
|
||||||
|
this.loadFiles('/');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onSelect = (keys, event) => {
|
||||||
|
this.setState({
|
||||||
|
selectNode: {
|
||||||
|
key: keys[0],
|
||||||
|
isLeaf: event.node.isLeaf
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
handleOk = async (values) => {
|
||||||
|
let params = {
|
||||||
|
'dir': this.state.selectNode.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('创建成功');
|
||||||
|
let parentPath = this.state.selectNode.key;
|
||||||
|
let items = await this.getTreeNodes(parentPath);
|
||||||
|
this.setState({
|
||||||
|
treeData: this.updateTreeData(this.state.treeData, parentPath, items),
|
||||||
|
selectNode: {}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
message.error(result.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
confirmLoading: false,
|
||||||
|
confirmVisible: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
handleConfirmCancel = () => {
|
||||||
|
this.setState({
|
||||||
|
confirmVisible: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
handleUploadCancel = () => {
|
||||||
|
this.setState({
|
||||||
|
uploadVisible: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
mkdir = () => {
|
||||||
|
if (!this.state.selectNode.key || this.state.selectNode.isLeaf) {
|
||||||
|
message.warning('请选择一个目录');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.setState({
|
||||||
|
confirmVisible: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
upload = () => {
|
||||||
|
if (!this.state.selectNode.key || this.state.selectNode.isLeaf) {
|
||||||
|
message.warning('请选择一个目录进行上传');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.setState({
|
||||||
|
uploadVisible: true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
download = () => {
|
||||||
|
if (!this.state.selectNode.key || !this.state.selectNode.isLeaf) {
|
||||||
|
message.warning('当前只支持下载文件');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
download(`${server}/sessions/${this.state.sessionId}/download?file=${this.state.selectNode.key}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
rmdir = async () => {
|
||||||
|
if (!this.state.selectNode.key) {
|
||||||
|
message.warning('请选择一个文件或目录');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let result;
|
||||||
|
if (this.state.selectNode.isLeaf) {
|
||||||
|
result = await request.delete(`/sessions/${this.state.sessionId}/rm?file=${this.state.selectNode.key}`);
|
||||||
|
} else {
|
||||||
|
result = await request.delete(`/sessions/${this.state.sessionId}/rmdir?dir=${this.state.selectNode.key}`);
|
||||||
|
}
|
||||||
|
if (result.code !== 1) {
|
||||||
|
message.error(result.message);
|
||||||
|
} else {
|
||||||
|
message.success('删除成功');
|
||||||
|
let path = this.state.selectNode.key;
|
||||||
|
let parentPath = path.substring(0, path.lastIndexOf('/'));
|
||||||
|
let items = await this.getTreeNodes(parentPath);
|
||||||
|
this.setState({
|
||||||
|
treeData: this.updateTreeData(this.state.treeData, parentPath, items),
|
||||||
|
selectNode: {}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh = async () => {
|
||||||
|
if (!this.state.selectNode.key || this.state.selectNode.isLeaf) {
|
||||||
|
await this.loadDirData('/');
|
||||||
|
} else {
|
||||||
|
let key = this.state.selectNode.key;
|
||||||
|
let items = await this.getTreeNodes(key);
|
||||||
|
this.setState({
|
||||||
|
treeData: this.updateTreeData(this.state.treeData, key, items),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
message.success('刷新目录成功');
|
||||||
|
}
|
||||||
|
|
||||||
|
loadFiles = async (key) => {
|
||||||
|
this.setState({
|
||||||
|
loading: true
|
||||||
|
})
|
||||||
|
try {
|
||||||
|
let result = await request.get(`${server}/sessions/${this.state.sessionId}/ls?dir=${key}`);
|
||||||
|
if (result['code'] !== 1) {
|
||||||
|
message.error(result['message']);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = result['data'];
|
||||||
|
|
||||||
|
const items = data.map(item => {
|
||||||
|
return {'key': item['path'], ...item}
|
||||||
|
})
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
files: items
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
this.setState({
|
||||||
|
loading: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
loadDirData = async (key) => {
|
||||||
|
let items = await this.getTreeNodes(key);
|
||||||
|
this.setState({
|
||||||
|
treeData: items,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getTreeNodes = async (key) => {
|
||||||
|
const url = server + '/sessions/' + this.state.sessionId + '/ls?dir=' + key;
|
||||||
|
|
||||||
|
let result = await request.get(url);
|
||||||
|
|
||||||
|
if (result.code !== 1) {
|
||||||
|
message.error(result['message']);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = result.data;
|
||||||
|
|
||||||
|
return data.map(item => {
|
||||||
|
return {
|
||||||
|
title: item['name'],
|
||||||
|
key: item['path'],
|
||||||
|
isLeaf: !item['isDir'] && !item['isLink'],
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
onLoadData = ({key, children}) => {
|
||||||
|
|
||||||
|
return new Promise(async (resolve) => {
|
||||||
|
if (children) {
|
||||||
|
resolve();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let items = await this.getTreeNodes(key);
|
||||||
|
this.setState({
|
||||||
|
treeData: this.updateTreeData(this.state.treeData, key, items),
|
||||||
|
});
|
||||||
|
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
updateTreeData = (list, key, children) => {
|
||||||
|
return list.map((node) => {
|
||||||
|
if (node.key === key) {
|
||||||
|
return {...node, children};
|
||||||
|
} else if (node.children) {
|
||||||
|
return {...node, children: this.updateTreeData(node.children, key, children)};
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
uploadChange = (info) => {
|
||||||
|
if (info.file.status !== 'uploading') {
|
||||||
|
|
||||||
|
}
|
||||||
|
if (info.file.status === 'done') {
|
||||||
|
message.success(`${info.file.name} 文件上传成功。`, 3);
|
||||||
|
} else if (info.file.status === 'error') {
|
||||||
|
message.error(`${info.file.name} 文件上传失败。`, 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: '名称',
|
||||||
|
dataIndex: 'name',
|
||||||
|
key: 'name',
|
||||||
|
render: (value, item) => {
|
||||||
|
let icon;
|
||||||
|
if (item['isDir']) {
|
||||||
|
icon = <FolderTwoTone/>;
|
||||||
|
} else {
|
||||||
|
if (item['isLink']) {
|
||||||
|
icon = <LinkOutlined/>;
|
||||||
|
} else {
|
||||||
|
const fileExtension = item['name'].split('.').pop().toLowerCase();
|
||||||
|
switch (fileExtension) {
|
||||||
|
case "doc":
|
||||||
|
case "docx":
|
||||||
|
icon = <FileWordTwoTone/>;
|
||||||
|
break;
|
||||||
|
case "xls":
|
||||||
|
case "xlsx":
|
||||||
|
icon = <FileExcelTwoTone/>;
|
||||||
|
break;
|
||||||
|
case "bmp":
|
||||||
|
case "jpg":
|
||||||
|
case "jpeg":
|
||||||
|
case "png":
|
||||||
|
case "tif":
|
||||||
|
case "gif":
|
||||||
|
case "pcx":
|
||||||
|
case "tga":
|
||||||
|
case "exif":
|
||||||
|
case "svg":
|
||||||
|
case "psd":
|
||||||
|
case "ai":
|
||||||
|
case "webp":
|
||||||
|
icon = <FileImageTwoTone/>;
|
||||||
|
break;
|
||||||
|
case "md":
|
||||||
|
icon = <FileMarkdownTwoTone/>;
|
||||||
|
break;
|
||||||
|
case "pdf":
|
||||||
|
icon = <FilePdfTwoTone/>;
|
||||||
|
break;
|
||||||
|
case "txt":
|
||||||
|
icon = <FileTextTwoTone/>;
|
||||||
|
break;
|
||||||
|
case "zip":
|
||||||
|
case "gz":
|
||||||
|
case "tar":
|
||||||
|
icon = <FileZipTwoTone/>;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
icon = <FileTwoTone/>;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return <>{icon} {item['name']}</>;
|
||||||
|
},
|
||||||
|
sorter: (a, b) => a.name - b.name,
|
||||||
|
sortDirections: ['descend', 'ascend'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '大小',
|
||||||
|
dataIndex: 'size',
|
||||||
|
key: 'size',
|
||||||
|
render: (value, item) => {
|
||||||
|
if (!item['isDir'] && !item['isLink']) {
|
||||||
|
return renderSize(value)
|
||||||
|
}
|
||||||
|
return '-';
|
||||||
|
},
|
||||||
|
sorter: (a, b) => a.size - b.size,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '修改日期',
|
||||||
|
dataIndex: 'modTime',
|
||||||
|
key: 'modTime',
|
||||||
|
sorter: (a, b) => a.modTime - b.modTime,
|
||||||
|
sortDirections: ['descend', 'ascend'],
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const {loading, selectedRowKeys} = this.state;
|
||||||
|
const rowSelection = {
|
||||||
|
selectedRowKeys,
|
||||||
|
onChange: () => {
|
||||||
|
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const hasSelected = selectedRowKeys.length > 0;
|
||||||
|
|
||||||
|
const title = (
|
||||||
|
<Row>
|
||||||
|
<Space>
|
||||||
|
远程文件管理
|
||||||
|
|
||||||
|
|
||||||
|
<Tooltip title="创建文件夹">
|
||||||
|
<Button type="primary" size="small" icon={<FolderAddOutlined/>}
|
||||||
|
onClick={this.mkdir} ghost/>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
|
<Tooltip title="上传">
|
||||||
|
<Button type="primary" size="small" icon={<CloudUploadOutlined/>}
|
||||||
|
onClick={this.upload} ghost/>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
|
<Tooltip title="下载">
|
||||||
|
<Button type="primary" size="small" icon={<CloudDownloadOutlined/>}
|
||||||
|
onClick={this.download} ghost/>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
|
<Tooltip title="删除文件">
|
||||||
|
<Button type="dashed" size="small" icon={<DeleteOutlined/>} onClick={this.rmdir}
|
||||||
|
danger/>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
|
<Tooltip title="刷新">
|
||||||
|
<Button type="primary" size="small" icon={<ReloadOutlined/>} onClick={this.refresh}
|
||||||
|
ghost/>
|
||||||
|
</Tooltip>
|
||||||
|
</Space>
|
||||||
|
</Row>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Card title={title} bordered={true} size="small">
|
||||||
|
<Table columns={columns}
|
||||||
|
rowSelection={rowSelection}
|
||||||
|
dataSource={this.state.files}
|
||||||
|
size={'small'}
|
||||||
|
pagination={false}
|
||||||
|
loading={this.state.loading}
|
||||||
|
onRow={record => {
|
||||||
|
return {
|
||||||
|
onClick: event => {
|
||||||
|
|
||||||
|
}, // 点击行
|
||||||
|
onDoubleClick: event => {
|
||||||
|
this.loadFiles(record['path']);
|
||||||
|
},
|
||||||
|
onContextMenu: event => {
|
||||||
|
},
|
||||||
|
onMouseEnter: event => {
|
||||||
|
}, // 鼠标移入行
|
||||||
|
onMouseLeave: event => {
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Modal
|
||||||
|
title="上传文件"
|
||||||
|
visible={this.state.uploadVisible}
|
||||||
|
onOk={() => {
|
||||||
|
|
||||||
|
}}
|
||||||
|
confirmLoading={this.state.uploadLoading}
|
||||||
|
onCancel={this.handleUploadCancel}
|
||||||
|
>
|
||||||
|
<Upload
|
||||||
|
action={server + '/sessions/' + this.state.sessionId + '/upload?X-Auth-Token=' + getToken() + '&dir=' + this.state.selectNode.key}>
|
||||||
|
<Button icon={<UploadOutlined/>}>上传文件</Button>
|
||||||
|
</Upload>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
|
||||||
|
<Modal
|
||||||
|
title="创建文件夹"
|
||||||
|
visible={this.state.confirmVisible}
|
||||||
|
onOk={() => {
|
||||||
|
this.formRef.current
|
||||||
|
.validateFields()
|
||||||
|
.then(values => {
|
||||||
|
this.formRef.current.resetFields();
|
||||||
|
this.handleOk(values);
|
||||||
|
})
|
||||||
|
.catch(info => {
|
||||||
|
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
confirmLoading={this.state.confirmLoading}
|
||||||
|
onCancel={this.handleConfirmCancel}
|
||||||
|
>
|
||||||
|
<Form ref={this.formRef} {...formItemLayout}>
|
||||||
|
|
||||||
|
<Form.Item label="文件夹名称" name='dir' rules={[{required: true, message: '请输入文件夹名称'}]}>
|
||||||
|
<Input autoComplete="off" placeholder="请输入文件夹名称"/>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FileSystem;
|
@ -13,7 +13,7 @@ const STATE_CONNECTED = 3;
|
|||||||
const STATE_DISCONNECTING = 4;
|
const STATE_DISCONNECTING = 4;
|
||||||
const STATE_DISCONNECTED = 5;
|
const STATE_DISCONNECTED = 5;
|
||||||
|
|
||||||
class Access extends Component {
|
class Monitor extends Component {
|
||||||
|
|
||||||
formRef = React.createRef()
|
formRef = React.createRef()
|
||||||
|
|
||||||
@ -103,76 +103,6 @@ class Access extends Component {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
onError = (status) => {
|
|
||||||
|
|
||||||
console.log('通道异常。', status);
|
|
||||||
|
|
||||||
switch (status.code) {
|
|
||||||
case 256:
|
|
||||||
this.showMessage('未支持的访问');
|
|
||||||
break;
|
|
||||||
case 512:
|
|
||||||
this.showMessage('远程服务异常');
|
|
||||||
break;
|
|
||||||
case 513:
|
|
||||||
this.showMessage('服务器忙碌');
|
|
||||||
break;
|
|
||||||
case 514:
|
|
||||||
this.showMessage('服务器连接超时');
|
|
||||||
break;
|
|
||||||
case 515:
|
|
||||||
this.showMessage('远程服务异常');
|
|
||||||
break;
|
|
||||||
case 516:
|
|
||||||
this.showMessage('资源未找到');
|
|
||||||
break;
|
|
||||||
case 517:
|
|
||||||
this.showMessage('资源冲突');
|
|
||||||
break;
|
|
||||||
case 518:
|
|
||||||
this.showMessage('资源已关闭');
|
|
||||||
break;
|
|
||||||
case 519:
|
|
||||||
this.showMessage('远程服务未找到');
|
|
||||||
break;
|
|
||||||
case 520:
|
|
||||||
this.showMessage('远程服务不可用');
|
|
||||||
break;
|
|
||||||
case 521:
|
|
||||||
this.showMessage('会话冲突');
|
|
||||||
break;
|
|
||||||
case 522:
|
|
||||||
this.showMessage('会话连接超时');
|
|
||||||
break;
|
|
||||||
case 523:
|
|
||||||
this.showMessage('会话已关闭');
|
|
||||||
break;
|
|
||||||
case 768:
|
|
||||||
this.showMessage('网络不可达');
|
|
||||||
break;
|
|
||||||
case 769:
|
|
||||||
this.showMessage('服务器密码验证失败');
|
|
||||||
break;
|
|
||||||
case 771:
|
|
||||||
this.showMessage('客户端被禁止');
|
|
||||||
break;
|
|
||||||
case 776:
|
|
||||||
this.showMessage('客户端连接超时');
|
|
||||||
break;
|
|
||||||
case 781:
|
|
||||||
this.showMessage('客户端异常');
|
|
||||||
break;
|
|
||||||
case 783:
|
|
||||||
this.showMessage('错误的请求类型');
|
|
||||||
break;
|
|
||||||
case 797:
|
|
||||||
this.showMessage('客户端连接数量过多');
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
this.showMessage('未知错误。');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
showMessage(message) {
|
showMessage(message) {
|
||||||
Modal.error({
|
Modal.error({
|
||||||
title: '提示',
|
title: '提示',
|
||||||
@ -189,7 +119,6 @@ class Access extends Component {
|
|||||||
|
|
||||||
// 处理客户端的状态变化事件
|
// 处理客户端的状态变化事件
|
||||||
client.onstatechange = this.onClientStateChange;
|
client.onstatechange = this.onClientStateChange;
|
||||||
client.onerror = this.onError;
|
|
||||||
const display = document.getElementById("display");
|
const display = document.getElementById("display");
|
||||||
|
|
||||||
// Add client to display div
|
// Add client to display div
|
||||||
@ -245,4 +174,4 @@ class Access extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Access;
|
export default Monitor;
|
||||||
|
@ -249,7 +249,7 @@ class Asset extends Component {
|
|||||||
} else {
|
} else {
|
||||||
asset['tags'] = asset['tags'].split(',');
|
asset['tags'] = asset['tags'].split(',');
|
||||||
}
|
}
|
||||||
}else {
|
} else {
|
||||||
asset['tags'] = [];
|
asset['tags'] = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -313,17 +313,22 @@ class Asset extends Component {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
access = async (id, protocol) => {
|
access = async (record) => {
|
||||||
|
const id = record['id'];
|
||||||
|
const protocol = record['protocol'];
|
||||||
|
const name = record['name'];
|
||||||
|
|
||||||
message.loading({content: '正在检测资产是否在线...', key: id});
|
message.loading({content: '正在检测资产是否在线...', key: id});
|
||||||
let result = await request.post(`/assets/${id}/tcping`);
|
let result = await request.post(`/assets/${id}/tcping`);
|
||||||
if (result.code === 1) {
|
if (result.code === 1) {
|
||||||
if (result.data === true) {
|
if (result.data === true) {
|
||||||
message.success({content: '检测完成,您访问的资产在线,即将打开窗口进行访问。', key: id, duration: 3});
|
message.success({content: '检测完成,您访问的资产在线,即将打开窗口进行访问。', key: id, duration: 3});
|
||||||
if(protocol === 'ssh'){
|
window.open(`#/access?assetId=${id}&assetName=${name}&protocol=${protocol}`);
|
||||||
window.open(`#/access-ssh?assetId=${id}`);
|
// if (protocol === 'ssh') {
|
||||||
}else {
|
// window.open(`#/access-naive?assetId=${id}&assetName=${name}`);
|
||||||
window.open(`#/access?assetsId=${id}&protocol=${protocol}`);
|
// } else {
|
||||||
}
|
// window.open(`#/access?assetId=${id}&assetName=${name}&protocol=${protocol}`);
|
||||||
|
// }
|
||||||
} else {
|
} else {
|
||||||
message.warn('您访问的资产未在线,请确认网络状态。', 10);
|
message.warn('您访问的资产未在线,请确认网络状态。', 10);
|
||||||
}
|
}
|
||||||
@ -530,7 +535,7 @@ class Asset extends Component {
|
|||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Button type="link" size='small'
|
<Button type="link" size='small'
|
||||||
onClick={() => this.access(record.id, record.protocol)}>接入</Button>
|
onClick={() => this.access(record)}>接入</Button>
|
||||||
|
|
||||||
<Dropdown overlay={menu}>
|
<Dropdown overlay={menu}>
|
||||||
<Button type="link" size='small'>
|
<Button type="link" size='small'>
|
||||||
|
@ -489,10 +489,9 @@ class OfflineSession extends Component {
|
|||||||
maskClosable={false}
|
maskClosable={false}
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
this.state.selectedRow['protocol'] === 'rdp' || this.state.selectedRow['protocol'] === 'vnc' ?
|
this.state.selectedRow['mode'] === 'naive' ?
|
||||||
<Playback sessionId={this.state.selectedRow['id']}/>
|
|
||||||
:
|
|
||||||
<iframe
|
<iframe
|
||||||
|
title='recording'
|
||||||
style={{
|
style={{
|
||||||
width: '100%',
|
width: '100%',
|
||||||
// height: this.state.iFrameHeight,
|
// height: this.state.iFrameHeight,
|
||||||
@ -510,6 +509,7 @@ class OfflineSession extends Component {
|
|||||||
height={window.innerHeight * 0.8}
|
height={window.innerHeight * 0.8}
|
||||||
frameBorder="0"
|
frameBorder="0"
|
||||||
/>
|
/>
|
||||||
|
: <Playback sessionId={this.state.selectedRow['id']}/>
|
||||||
}
|
}
|
||||||
|
|
||||||
</Modal> : undefined
|
</Modal> : undefined
|
||||||
|
@ -210,3 +210,15 @@ export function exitFull() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function renderSize(value) {
|
||||||
|
if (null == value || value === '' || value === 0) {
|
||||||
|
return "0 Bytes";
|
||||||
|
}
|
||||||
|
const unitArr = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
|
||||||
|
let srcSize = parseFloat(value);
|
||||||
|
let index = Math.floor(Math.log(srcSize) / Math.log(1024));
|
||||||
|
let size = srcSize / Math.pow(1024, index);
|
||||||
|
size = size.toFixed(2);
|
||||||
|
return size + unitArr[index];
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user