diff --git a/README.md b/README.md index 9a9e8d9..6182067 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,8 @@ go版本多用户webssh管理工具 ssh2ws部分代码修改自https://github.com/hequan2017/go-webssh +2020/12/17 新增WEB_SFTP功能,拖动文件到终端窗口里即可上传 + 服务端不保存用户明文密码,且不保存解密秘钥,如需对其他用户开放,请不要修改此部分代码,以免造成不必要的损失! @@ -14,10 +16,11 @@ Gin + gorm ## 更新日志 2020/12/14 修复无操作自动断开、修复网络延迟造成的js加载延迟问题 2020/12/16 前端新增文件/文件夹拖动到Terminal的自动解析功能(SFTP需要),修改layer弹出窗口逻辑,增加回车提交事件 + 2020/12/17 增加在线sftp文件上传功能 * ## 开发计划 ✔ ssh功能 -× 服务器文件管理 +✔ sftp文件上传功能 ## 在线演示 [点击进入SSH云管理平台](https://www.do18.cn) diff --git a/common/core/ssh_shell_conn.go b/common/core/ssh_shell_conn.go index 4f5f009..21553fb 100644 --- a/common/core/ssh_shell_conn.go +++ b/common/core/ssh_shell_conn.go @@ -194,7 +194,7 @@ func (ssConn *SshConn) SessionWait(quitChan chan bool) { // log.Println("ssh session wait failed") // setQuit(quitChan) //} - timer := time.NewTicker(time.Second * 3) + timer := time.NewTicker(time.Second * 5) defer timer.Stop() for { select { diff --git a/common/sftp_clients/sftp.go b/common/sftp_clients/sftp.go new file mode 100644 index 0000000..3c774b9 --- /dev/null +++ b/common/sftp_clients/sftp.go @@ -0,0 +1,181 @@ +package sftp_clients + +import ( + "encoding/base64" + "encoding/json" + "github.com/gorilla/websocket" + "github.com/pkg/sftp" + "log" + "path" + "sync" + "time" +) + +const ( + getpwd = "getpwd" + upload = "upload" +) + +type sftp_req struct { + Type string `json:"type"` + FilePath string `json:"filepath"` + FileName string `json:"filename"` + FileData string `json:"filedata"` +} + +type sftp_resp struct { + Code int `json:"code"` + Type string `json:"type"` + Msg string `json:"msg"` + Data string `json:"data"` +} + +type MyClient struct { + Uid uint + Sftp *sftp.Client +} + +type clients struct { + sync.RWMutex + C map[string]*MyClient +} + +var Client clients + +func init() { + Client = clients{C: make(map[string]*MyClient, 1000)} +} + +func (c *MyClient) ReceiveWsMsg(wsConn *websocket.Conn, exitCh chan bool) { + defer setQuit(exitCh) + go c.SessionWait(wsConn, exitCh) + for { + select { + case <-exitCh: + return + default: + _, wsData, err := wsConn.ReadMessage() + if err != nil { + log.Println(err.Error()) + //logrus.WithError(err).Error("reading webSocket message failed") + return + } + //unmashal bytes into struct + msgObj := sftp_req{} + if err := json.Unmarshal(wsData, &msgObj); err != nil { + log.Println("unmarshal websocket message failed:", string(wsData)) + continue + } + msgresp := sftp_resp{} + switch msgObj.Type { + case getpwd: + msgresp.Code = 200 + msgresp.Type = "pwd" + path, err := c.Sftp.Getwd() + if err != nil { + msgresp.Code = 404 + msgresp.Msg = "服务器Path获取失败" + msgresp.Data = err.Error() + log.Println("sftp getpwd err:", err.Error()) + } + msgresp.Data = path + msg, _ := json.Marshal(msgresp) + if err := wsConn.WriteMessage(websocket.TextMessage, msg); err != nil { + log.Println("sftp client getpwd err:", err.Error()) + return + } + case upload: + msgresp.Code = 200 + msgresp.Type = "upload" + io_data, err := base64.StdEncoding.DecodeString(msgObj.FileData) + if err != nil { + msgresp.Code = 401 + msgresp.Msg = "文件解析失败" + msgresp.Data = err.Error() + log.Println("sftp base64decode err:", err.Error()) + msg, _ := json.Marshal(msgresp) + if err := wsConn.WriteMessage(websocket.TextMessage, msg); err != nil { + log.Println("sftp base64decode send err:", err.Error()) + return + } + } + + if err := c.Sftp.MkdirAll(msgObj.FilePath); err != nil { + msgresp.Code = 402 + msgresp.Msg = "服务器创建目录失败" + msgresp.Data = err.Error() + log.Println("sftp mkdir err:", err.Error()) + msg, _ := json.Marshal(msgresp) + if err := wsConn.WriteMessage(websocket.TextMessage, msg); err != nil { + log.Println("sftp mkdir send err:", err.Error()) + return + } + continue + } + + file, err := c.Sftp.Create(path.Join(msgObj.FilePath, msgObj.FileName)) + if err != nil { + msgresp.Code = 403 + msgresp.Msg = "服务器文件创建失败" + msgresp.Data = err.Error() + log.Println("sftp create file err:", err.Error()) + msg, _ := json.Marshal(msgresp) + if err := wsConn.WriteMessage(websocket.TextMessage, msg); err != nil { + log.Println("sftp create file send err:", err.Error()) + return + } + continue + } + + defer file.Close() + + if _, err := file.Write(io_data); err != nil { + msgresp.Code = 405 + msgresp.Msg = "服务器文件写入失败" + msgresp.Data = err.Error() + log.Println("sftp write file err:", err.Error()) + msg, _ := json.Marshal(msgresp) + if err := wsConn.WriteMessage(websocket.TextMessage, msg); err != nil { + log.Println("sftp write file send err:", err.Error()) + return + } + continue + } + file.Close() + filepath := path.Join(msgObj.FilePath, msgObj.FileName) + msgresp.Code = 200 + msgresp.Msg = "OK" + msgresp.Data = filepath + //log.Println("file write ok") + msg, _ := json.Marshal(msgresp) + if err := wsConn.WriteMessage(websocket.TextMessage, msg); err != nil { + log.Println("sftp write file send err:", err.Error()) + return + } + } + } + } +} + +func (c *MyClient) SessionWait(wsConn *websocket.Conn, quitChan chan bool) { + timer := time.NewTicker(time.Second * 30) + defer timer.Stop() + defer setQuit(quitChan) + for { + select { + case <-timer.C: + { + if err := wsConn.WriteMessage(websocket.TextMessage, []byte("pong")); err != nil { + log.Println("sftp pong send err :", err.Error()) + return + } + } + case <-quitChan: + return + } + } +} + +func setQuit(ch chan bool) { + ch <- true +} diff --git a/controller/stfp.go b/controller/stfp.go new file mode 100644 index 0000000..bba47ac --- /dev/null +++ b/controller/stfp.go @@ -0,0 +1,114 @@ +package controller + +import ( + "encoding/json" + "fmt" + "github.com/gin-gonic/gin" + "github.com/gorilla/websocket" + "log" + "ssh_manage/common" + "ssh_manage/common/core" + "ssh_manage/common/sftp_clients" + "ssh_manage/errcode" + "ssh_manage/model/Apiform" +) + +type sftp_req struct { + Type string `json:"type"` + Token string `json:"token"` +} + +type sftp_resp struct { + Code int `json:"code"` + Type string `json:"type"` + Msg string `json:"msg"` + Data string `json:"data"` +} + +func Sftp_ssh(c *gin.Context) { + wsConn, err := upGrader.Upgrade(c.Writer, c.Request, nil) + if core.HandleError(c, err) { + return + } + defer wsConn.Close() + + var auth Apiform.WsAuth + + if c.ShouldBindUri(&auth) != nil { + wsConn.WriteMessage(websocket.TextMessage, []byte("参数错误\r\n")) + wsConn.Close() + return + } + + for { + _, wsData, err := wsConn.ReadMessage() + if err != nil { + log.Println(err.Error()) + wsConn.Close() + //logrus.WithError(err).Error("reading webSocket message failed") + return + } + //unmashal bytes into struct + msgObj := sftp_req{} + if err := json.Unmarshal(wsData, &msgObj); err != nil { + log.Println("Auth : unmarshal websocket message failed:", string(wsData)) + continue + } + resp_msg := sftp_resp{} + token := msgObj.Token + claims, err := common.ParseToken(token) + valid := claims.Valid() + if valid != nil || err != nil { + resp_msg.Code = errcode.S_auth_fmt_err + resp_msg.Msg = "身份令牌校验不通过" + resp_msg.Data = err.Error() + msg, _ := json.Marshal(resp_msg) + if err := wsConn.WriteMessage(websocket.TextMessage, msg); err != nil { + log.Println("sftp token fmt err:", err) + } + wsConn.Close() + return + } + if claims.Userid != sftp_clients.Client.C[auth.Sid].Uid { //身份与缓存不符合 + resp_msg.Code = errcode.S_auth_fmt_err + resp_msg.Msg = "用户权限不通过" + resp_msg.Data = err.Error() + msg, _ := json.Marshal(resp_msg) + if err := wsConn.WriteMessage(websocket.TextMessage, msg); err != nil { + log.Println("sftp server_user err:", err) + } + wsConn.Close() + return + } + + path, err := sftp_clients.Client.C[auth.Sid].Sftp.Getwd() + if err != nil { + resp_msg.Code = errcode.S_send_err + resp_msg.Type = "connect" + resp_msg.Msg = "SFTP连接失败" + msg, _ := json.Marshal(resp_msg) + if err := wsConn.WriteMessage(websocket.TextMessage, msg); err != nil { + log.Println("sftp connect err:", err) + } + return + } + + resp_msg.Code = 200 + resp_msg.Type = "connect" + resp_msg.Msg = "连接成功" + resp_msg.Data = path + msg, _ := json.Marshal(resp_msg) + if err := wsConn.WriteMessage(websocket.TextMessage, msg); err != nil { + log.Println("sftp return err:", err) + return + } + + break + //break + } + quitChan := make(chan bool, 2) + go sftp_clients.Client.C[auth.Sid].ReceiveWsMsg(wsConn, quitChan) + <-quitChan //任意协程退出则结束 + fmt.Println("Sftp Exit") + log.Println("sftp websocket finished") +} diff --git a/controller/term.go b/controller/term.go index e6f555d..501d970 100644 --- a/controller/term.go +++ b/controller/term.go @@ -10,6 +10,7 @@ import ( "net/http" "ssh_manage/common" "ssh_manage/common/core" + "ssh_manage/common/sftp_clients" "ssh_manage/database" "ssh_manage/model/Apiform" "strconv" @@ -52,8 +53,8 @@ func WsSsh(c *gin.Context) { var ser_info Apiform.SerInfo //接收反序列化数据 var auth Apiform.WsAuth - if c.ShouldBindUri(&auth) != nil{ - wsConn.WriteMessage(websocket.TextMessage,[]byte("参数错误\r\n")) + if c.ShouldBindUri(&auth) != nil { + wsConn.WriteMessage(websocket.TextMessage, []byte("参数错误\r\n")) wsConn.Close() return } @@ -83,20 +84,20 @@ func WsSsh(c *gin.Context) { cache := database.Cache.Get() defer cache.Close() //log.Println(auth) - s_info,err := redis.Bytes(cache.Do("GET", auth.Sid)) + s_info, err := redis.Bytes(cache.Do("GET", auth.Sid)) //log.Println(string(s_info)) - if err != nil || len(s_info) == 0{ + if err != nil || len(s_info) == 0 { wsConn.WriteMessage(websocket.TextMessage, []byte("连接超时,请重试!\r\n")) wsConn.Close() return } - if json.Unmarshal(s_info,&ser_info) != nil{ + if json.Unmarshal(s_info, &ser_info) != nil { wsConn.WriteMessage(websocket.TextMessage, []byte("服务器信息获取失败,请重试!\r\n")) wsConn.Close() return } //log.Println(ser_info) - if claims.Userid != ser_info.BindUser{ //验证权限 + if claims.Userid != ser_info.BindUser { //验证权限 wsConn.WriteMessage(websocket.TextMessage, []byte("权限验证失败,请重试!\r\n")) wsConn.Close() return @@ -110,10 +111,18 @@ func WsSsh(c *gin.Context) { } defer client.Close() //startTime := time.Now() - ssConn, err := core.NewSshConn(cols, rows, client) //加入sftp客户端 + ssConn, err := core.NewSshConn(cols, rows, client) //加入sftp客户端 if core.WshandleError(wsConn, err) { return } + sftp_clients.Client.Lock() + sftp_clients.Client.C[auth.Sid] = &sftp_clients.MyClient{ser_info.BindUser, ssConn.SftpClient} + sftp_clients.Client.Unlock() + defer func() { + sftp_clients.Client.Lock() + delete(sftp_clients.Client.C, auth.Sid) //释放SFTP客户端 + sftp_clients.Client.Unlock() + }() defer ssConn.Close() quitChan := make(chan bool, 3) diff --git a/serve.go b/serve.go index eeb6fc3..85bc02b 100644 --- a/serve.go +++ b/serve.go @@ -55,6 +55,7 @@ func main() { api.POST("/login", controller.Login) api.POST("/send", controller.Send) api.GET("/term/:sid", controller.WsSsh) + api.GET("/sftp/:sid", controller.Sftp_ssh) api.Use(middleware.Auth()).GET("/userinfo", controller.Info) api.Use(middleware.Auth()).POST("/nickname", controller.UpdataNick) api.Use(middleware.Auth()).POST("/addser", controller.Addser) diff --git a/static/js/console.js b/static/js/console.js index 31f9b83..a550cc4 100644 --- a/static/js/console.js +++ b/static/js/console.js @@ -65,7 +65,7 @@ getinfo = function () { } , parseData: function (res) { //将原始数据解析成 table 组件所规定的数据 if (res.token) { - window.localStorage.setItem("token", result.token) //更新token + window.localStorage.setItem("token", res.token) //更新token } if (res.code == 301 || res.code == 302) { //Token校验失败或过期 layer.msg(res.msg, function () { diff --git a/static/js/fileupload.js b/static/js/fileupload.js index 525c17b..4f48616 100644 --- a/static/js/fileupload.js +++ b/static/js/fileupload.js @@ -8,8 +8,8 @@ let fileDrop = { filesList: [], // 文件列表数组 errorLength: 0, //上传失败文件数量 isUpload: true, //上传状态,是否可以上传 - //uploadSuspend:[], //上传暂停参数 - isUploadNumber: 800,//限制单次上传数量 + uploadSuspend: false, //上传暂停参数 + isUploadNumber: 150,//限制单次上传数量 uploadAllSize: 0, // 上传文件总大小 uploadedSize: 0, // 已上传文件大小 topUploadedSize: 0, // 上一次文件上传大小 @@ -19,6 +19,30 @@ let fileDrop = { timerSpeed: 0, //速度 uploading: false, cancel: false, + done: false, +} + +function file_init() { + fileDrop = { + startTime: 0, + endTime: 0, + uploadLength: 0, //上传数量 + //splitSize: 1024 * 1024 * 2, //文件上传分片大小 + filesList: [], // 文件列表数组 + errorLength: 0, //上传失败文件数量 + isUpload: true, //上传状态,是否可以上传 + //uploadSuspend:[], //上传暂停参数 + isUploadNumber: 800,//限制单次上传数量 + uploadAllSize: 0, // 上传文件总大小 + uploadedSize: 0, // 已上传文件大小 + topUploadedSize: 0, // 上一次文件上传大小 + uploadExpectTime: 0, // 预计上传时间 + //initTimer:0, // 初始化计时 + speedInterval: null, //平局速度定时器 + timerSpeed: 0, //速度 + uploading: false, + cancel: false, + } } dropbox.addEventListener("dragleave", function (e) { @@ -39,8 +63,9 @@ dropbox.addEventListener("dragover", function (e) { dropbox.addEventListener("drop", changes, false); function changes(e) { - if(!is_login){ + if (!sftp_ready) { layer.msg("请等待服务器连接!") + return false } e.preventDefault(); let items = e.dataTransfer.items, time, num = 0 @@ -67,10 +92,11 @@ function changes(e) { if (typeof (filesAndDirs[i].getFilesAndDirectories) == 'function') { update_sync(filesAndDirs[i]) } else { - if (num > 100) { + if (num > fileDrop.isUploadNumber) { //fileDrop.isUpload = false; - layer.msg(' '+ fileDrop.isUploadNumber +'份,无法上传,请压缩后上传!。',{icon:2,area:'405px'}); + layer.msg(' ' + fileDrop.isUploadNumber + '份,无法上传,请压缩后上传!。', {icon: 2, area: '405px'}); //clearTimeout(time); + file_init() return false; } fileDrop.filesList.push({ @@ -94,30 +120,34 @@ function changes(e) { } //console.log(fileDrop.filesList) layer.load(1, { - shade: [0.1,'#fff'] //0.1透明度的白色背景 + shade: [0.1, '#fff'] //0.1透明度的白色背景 }); + getpwd(getpwd_callback) //采用回调函数的方式检查SFTP服务是否可用 +} + +function getpwd_callback(msg) { + server_pwd = msg.data setTimeout(function () { layer.closeAll('loading') open_upload_window() - },3000) - + }, 1500) } function open_upload_window() { - + //console.log(fileDrop.filesList[0]) let template = ` - - - + + + - - - + + + @@ -130,8 +160,8 @@ function open_upload_window() { closeBtn: 1, maxmin: true, area: ['550px', '455px'], - btn: ['开始上传', '取消上传'], - title: '上传文件', + btn: [fileDrop.uploadSuspend ? "继续上传" : "开始上传", '取消上传'], + title: '上传文件到:' + session_path, skin: 'file_dir_uploads', shade: 0.4, shadeClose: false, @@ -140,13 +170,139 @@ function open_upload_window() { for (let i = 0; i < fileDrop.filesList.length; i++) { $("#file_upload tbody").append(create_row(i, fileDrop.filesList[i])); } - } + }, + yes: function (index) { + if (fileDrop.filesList.length <= 0) { + layer.msg("请选择要上传的文件!", {icon: 0}) + file_init() + layer.close(index) + return false + } + if (fileDrop.uploading && fileDrop.uploadSuspend == false) { + return false + } + //console.log("开始上传") + $('.layui-layer-btn0').css({ + 'cursor': 'no-drop', + 'background': '#4e8ccc' + }).attr('data-upload', 'true').text('上传中'); + fileDrop.uploadSuspend = false + fileDrop.uploading = true + start_upload() + }, + btn2: function (index) { + if(fileDrop.done){ + layer.close(index); + file_init() + return false + } + if (fileDrop.uploading) { + layer.confirm('是否取消上传当前列表的文件,若取消上传,已上传的文件,需用户手动删除,是否取消上传?', { + title: '取消上传文件', + icon: 0, + btn: ['取消上传', '继续上传'] + }, function (indexs) { + layer.close(index); + layer.close(indexs); + file_init() + }, function () { + fileDrop.uploadSuspend = true //开启暂停标识,用于继续上传功能 + open_upload_window() + }); + //return false; + } else { + layer.close(index); + file_init() + } + }, + cancel: function (index) { + if(fileDrop.done){ + layer.close(index); + file_init() + return false + } + if (fileDrop.uploading) { + layer.confirm('是否取消上传当前列表的文件,若取消上传,已上传的文件,需用户手动删除,是否取消上传?', { + title: '取消上传文件', + icon: 0, + btn: ['取消上传', '继续上传'] + }, function (indexs) { + layer.close(index); + layer.close(indexs); + file_init() + }, function () { + fileDrop.uploadSuspend = true //开启暂停标识,用于继续上传功能 + open_upload_window() + }); + //return false; + } else { + layer.close(index); + file_init() + } + }, }); } +function start_upload(file_index, status, msg) { + file_index = file_index == null ? 0 : file_index + if (status) { //这个参数只在上传回调中传递,用于更新文件上传结果,如果这个参数存在,表示file_index已经走完上传流程,故file_index需自增1,代表开始下一个文件的上传 + // if(msg){ + // upload_view(file_index,msg) + // }else{ + // upload_view(file_index,status) + // } + fileDrop.filesList[file_index].upload = status //更新文件列表中的状态 + upload_view(file_index, msg == undefined ? status : msg) + file_index++ + } + let reader = new FileReader(); //这个可以选择函数外声明,可避免重复创建 + if (reader == undefined) { + file_init() + layer.msg("您的浏览器暂不支持在线上传文件!") + return false + } + reader.onload = function () { + let fileData = _arrayBufferToBase64(this.result); + fileDrop.filesList[file_index].upload = 1 //修改状态为上传中 + upload_view(file_index, 1) //修改视图内容 + sendFile(fileDrop.filesList[file_index], fileData, file_index, start_upload) + //console.log(fileData); + } + if (file_index < fileDrop.filesList.length) { //采用回调方式上传,进度更可控 + if (fileDrop.filesList[file_index].upload < 2) { //可继续上传未上传的文件 + let f_id = "#file_" + file_index + " td"; + $(f_id)[2].innerHTML = "解析中"; + reader.readAsArrayBuffer(fileDrop.filesList[file_index].file) + } + } else { + layer.msg("上传完成") + $('.layui-layer-btn0').text('上传完成'); + $('.layui-layer-btn1').text('关闭窗口'); + fileDrop.done = true + } + // for (let i = 0; i < fileDrop.filesList.length; i++) { + // reader.readAsArrayBuffer(fileDrop.filesList[i].file); + // } +} + +function upload_view(file_index, status) { + let f_id = "#file_" + file_index + " td"; + $(f_id)[2].innerHTML = getstatu(status); +} + +function _arrayBufferToBase64(buffer) { + let binary = ''; + let bytes = new Uint8Array(buffer); + let len = bytes.byteLength; + for (var i = 0; i < len; i++) { + binary += String.fromCharCode(bytes[i]); + } + return window.btoa(binary); +} + function create_row(index, file) { - console.log(file) - return "" + //console.log(file) + return "" } function getstatu(statu) { @@ -158,8 +314,10 @@ function getstatu(statu) { return "未上传" } else if (statu == 1) { return "上传中" - } else { + } else if (statu == 2) { return "已上传" + } else { + return "" + statu + "" } } } diff --git a/static/js/sftp.js b/static/js/sftp.js new file mode 100644 index 0000000..2eacdde --- /dev/null +++ b/static/js/sftp.js @@ -0,0 +1,109 @@ +var sftp = null; +var sftp_ready = false; +var server_pwd = ""; +var session_path = ""; +var callback_success = undefined; +//var callback_error = undefined; +var upload_file_index = 0; + +let sendmsg = { + type: "", + filepath: "", + filename: "", + filedata: "", +} + +let msg_type = { + connect: "connect", + getpwd: "getpwd", + upload: "upload", +} + +let file_status = { + success: 2, //上传成功 + error: -1, //上传失败 +} + +create_sftp = function () { + sftp = new WebSocket(ws_p + '://' + window.location.host + '/v1/sftp/' + GetQueryString("sid")); + sftp.onopen = function () { + sftp.send(JSON.stringify(auth)); //验证权限 + + sftp.onmessage = default_msg; + sftp.onerror = function (e) { + sftp_ready = false + console.log(e); + layer.msg("SFTP连接出错") + }; + + sftp.onclose = function (e) { + sftp_ready = false + console.log(e); + layer.msg("SFTP连接断开") + }; + }; +} + +default_msg = function (msg) { + if (isJSON(msg.data)) { + msg = JSON.parse(msg.data) + if (msg.type == msg_type.connect && msg.code == 200) { + sftp_ready = true + server_pwd = msg.data //初始化工作路径 + } else if (msg.type == msg_type.upload) { + if (msg.code == 200) { + callback_success(upload_file_index, file_status.success) + } else { + //layer.msg("文件上传失败:" + msg.msg) //可以回调修改状态文字内容,这里为了图方便,暂时不写了;(觉得不妥,又写上了) + callback_success(upload_file_index, file_status.error,msg.msg) //上传失败正常回调,开始处理下一个 + } + } + } +} + +getpwd = function (callback) { + sendmsg.type = msg_type.getpwd + sftp.onmessage = function (msg) { + if (isJSON(msg.data)) { + msg = JSON.parse(msg.data) + if (msg.code == 200) { + if (session_path == "") { + session_path = server_pwd + } + callback(msg) + } else { + layer.msg(msg.msg) + } + sftp.onmessage = default_msg + } + } + sftp.send(JSON.stringify(sendmsg)) +} + +sendFile = function (fileinfo, fileData, file_index, callback) { + sendmsg.type = msg_type.upload + sendmsg.filepath = session_path + fileinfo.path + sendmsg.filename = fileinfo.name + sendmsg.filedata = fileData + callback_success = callback + upload_file_index = file_index + sftp.send(JSON.stringify(sendmsg)) +} + +isJSON = function (str) { + if (typeof str == 'string') { + try { + var obj = JSON.parse(str); + if (typeof obj == 'object' && obj) { + return true; + } else { + return false; + } + + } catch (e) { + //console.log('error:' + str + '!!!' + e); + return false; + } + } + //console.log('It is not a string!') +} \ No newline at end of file diff --git a/static/xterm/main.js b/static/xterm/main.js index 305d614..892807e 100644 --- a/static/xterm/main.js +++ b/static/xterm/main.js @@ -1,4 +1,23 @@ -var is_login = false; +// var is_login = { watchValue:false }; +// var lastTimeValue=is_login.watchValue; +// Object.defineProperty(is_login, 'watchValue', { +// get: function() { +// console.log('get:' + watchValue); +// return watchValue; +// }, +// set: function(value) { +// watchValue = value; +// if(lastTimeValue!=watchValue){ +// lastTimeValue=watchValue; +// console.log('value changed!! set: ' + watchValue); +// if(watchValue == true){ +// create_sftp() +// } +// } +// } +// }); + +var is_login = false const protocol = document.location.protocol.split(':')[0]; var ws_p = "ws"; if (protocol == "https") { @@ -15,7 +34,16 @@ const auth = { type: "auth", token: token, } + +function completeLoading() { + console.log("Loading Success") + if (document.readyState == "complete") { + layer.close(index); + } +} + const socket = new WebSocket(ws_p + '://' + window.location.host + '/v1/term/' + GetQueryString("sid")); + const term = new Terminal({cols: 180, rows: 50, screenKeys: true, cursorBlink: true, cursorStyle: "block"}); term.open(document.getElementById('terms')); window.onresize = function () { @@ -45,20 +73,51 @@ socket.onopen = function () { }); socket.onmessage = function (msg) { - if(!is_login){ + if (!is_login) { + term.clear() + create_sftp() is_login = true } term.write(msg.data); + update_path(msg.data) }; socket.onerror = function (e) { is_login = false + layer.msg("链接出错:" + JSON.stringify(e)) console.log(e); }; socket.onclose = function (e) { is_login = false - console.log(e); - term.write("连接已断开:" + e.reason + "\r\n"); + layer.msg("链接断开:" + JSON.stringify(e)) + term.write("连接已断开" + "\r\n"); //term.destroy(); }; -}; \ No newline at end of file +}; + +function update_path(str) { //判断是否有效路径后更新当前所在路径,用于SFTP功能 + //console.log(str) + let splice = String.fromCharCode(7) + let start = str.indexOf(":") + let end = str.indexOf(splice) + if (start >= 0 && end >= 0) { + let path = trimStr(str.substring(start + 1, end)) + let verify = path.indexOf(" ") + if(verify == -1){ + //console.log(path) + if(path.substr(0,1) == "~" || path.substr(0,1) == "/"){ + if(path.substr(0,1) == "~"){ //替换~为用户目录 + path = server_pwd + path.substr(1) + } + //console.log(path) + if(session_path != path){ + session_path = path + } + } + } + } +} + +function trimStr(str){ + return str.replace(/(^\s*)|(\s*$)/g,""); +} \ No newline at end of file diff --git a/view/s_list.html b/view/s_list.html index 923d78e..f494cf6 100644 --- a/view/s_list.html +++ b/view/s_list.html @@ -44,7 +44,7 @@ - + diff --git a/view/term.html b/view/term.html index d0cadf9..7392927 100644 --- a/view/term.html +++ b/view/term.html @@ -38,7 +38,8 @@ - + + - + \ No newline at end of file
文件路径文件大小状态文件路径(共` + fileDrop.uploadLength + `个文件)文件大小(共` + to_size(fileDrop.uploadAllSize) + `)文件状态
" + file.local + " " + file.size + " " + getstatu(file.upload) + "
" + file.local + " " + file.size + " " + getstatu(file.upload) + "