refactor(file,shell,process): 去 gin 化——标准库 http.HandlerFunc + webhttp.Router 适配层
- 新增 webhttp 包: Router 最小路由接口(5 动词) + ServeMuxRouter 适配器 + WriteJSON 统一响应 - file/shell/process 三个被控能力包改以标准库 handler 形态注册, 不再绑死 gin - 由此 gin 及其连带重依赖(quic-go/http3、mongo bson、ugorji codec、goccy、validator)退出被控端二进制 - shell: recDir 死参数修复——原实现硬编码 ./rec, 传入的录制目录不生效; 现录制写入与记录列表同源 - shell: 终端创建失败由 panic 改为记日志后关连接(net/http 按连接兜底恢复, 不再依赖框架 Recovery) - file: 上传落盘不再沿用 SaveUploadedFile 的 MkdirAll+Chmod(Windows 盘根 Chmod 报参数不正确), 保留 MkdirAll 去掉 Chmod - 消费方: remote-client 纯 ServeMux 承载; remote-service 保留 gin 栈经 gin.WrapH 桥接(middle 链照常生效) 破坏性变更: RegisterRouters* 形参 gin.RouterGroup → webhttp.Router, 升级 tag v0.5.0
This commit is contained in:
+124
-131
@@ -1,31 +1,25 @@
|
||||
package shell
|
||||
|
||||
// 移植自 remote-backend-server internal/handlers/shell(去 DB 依赖, ConPTY 终端 + asciinema 录制)。
|
||||
// 标准库 http.HandlerFunc 形态, 路由注册经 webhttp.Router 适配(去 gin 化)。
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/runletapp/go-console"
|
||||
|
||||
"github.com/sirupsen/logrus"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"git.zeroonesoft.cn/golib/remote-core/common"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type windowSize struct {
|
||||
Rows uint16 `json:"rows"`
|
||||
Cols uint16 `json:"cols"`
|
||||
X uint16
|
||||
Y uint16
|
||||
}
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/runletapp/go-console"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
"git.zeroonesoft.cn/golib/remote-core/common"
|
||||
"git.zeroonesoft.cn/golib/remote-core/webhttp"
|
||||
)
|
||||
|
||||
var upgrader = websocket.Upgrader{
|
||||
ReadBufferSize: 1024,
|
||||
@@ -46,159 +40,158 @@ type ResizeDataType struct {
|
||||
Rows int `json:"rows"`
|
||||
}
|
||||
|
||||
// RegisterRoutersWebShell 注册路由
|
||||
func RegisterRoutersWebShell(r *gin.RouterGroup, recDir string) {
|
||||
r.GET("/web/shell", getWebShell(recDir))
|
||||
// recStoreDir 会话录制目录, Register 时注入; 录制写入与记录列表两处共用。
|
||||
// 原实现硬编码 ./rec, recDir 形参是死参数(remote-service 传 RunDir/rec 不生效)。
|
||||
var recStoreDir = "./rec"
|
||||
|
||||
// RegisterRoutersWebShell 注册路由。recDir 为会话录制目录, 传空退回 ./rec。
|
||||
func RegisterRoutersWebShell(r webhttp.Router, recDir string) {
|
||||
if recDir != "" {
|
||||
recStoreDir = recDir
|
||||
}
|
||||
r.GET("/web/shell", getWebShell)
|
||||
r.GET("/web/shell/record", RecoderList)
|
||||
}
|
||||
|
||||
func getWebShell(recDir string) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
func getWebShell(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
conn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
|
||||
if err != nil {
|
||||
logrus.Errorf("Unable to upgrade connection")
|
||||
return
|
||||
}
|
||||
conn, err := upgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
logrus.Errorf("Unable to upgrade connection")
|
||||
return
|
||||
}
|
||||
|
||||
proc, err := console.New(180, 30)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
arr := make([]string, 0)
|
||||
if runtime.GOOS == "windows" {
|
||||
if ok, _ := common.PathExists("C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe"); ok {
|
||||
arr = append(arr, "powershell.exe") // powershell.exe cmd.exe
|
||||
} else {
|
||||
arr = append(arr, "cmd.exe")
|
||||
}
|
||||
proc, err := console.New(180, 30)
|
||||
if err != nil {
|
||||
logrus.Errorf("创建终端失败:%s", err.Error())
|
||||
conn.Close()
|
||||
return
|
||||
}
|
||||
|
||||
arr := make([]string, 0)
|
||||
if runtime.GOOS == "windows" {
|
||||
if ok, _ := common.PathExists("C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe"); ok {
|
||||
arr = append(arr, "powershell.exe") // powershell.exe cmd.exe
|
||||
} else {
|
||||
arr = []string{"bash"} // powershell.exe cmd.exe
|
||||
arr = append(arr, "cmd.exe")
|
||||
}
|
||||
|
||||
if err := proc.Start(arr); err != nil {
|
||||
logrus.Errorf("启动出错:%s", err.Error())
|
||||
panic(err)
|
||||
}
|
||||
} else {
|
||||
arr = []string{"bash"} // powershell.exe cmd.exe
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
logrus.Errorf("Unable to start pty/cmd")
|
||||
conn.WriteMessage(websocket.TextMessage, []byte(err.Error()))
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
proc.Close()
|
||||
conn.Close()
|
||||
}()
|
||||
if err := proc.Start(arr); err != nil {
|
||||
logrus.Errorf("启动出错:%s", err.Error())
|
||||
conn.WriteMessage(websocket.TextMessage, []byte(err.Error()))
|
||||
conn.Close()
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
proc.Close()
|
||||
conn.Close()
|
||||
}()
|
||||
|
||||
var recorder *Recorder
|
||||
var recorder *Recorder
|
||||
|
||||
os.MkdirAll("./rec", 0766)
|
||||
fileName := path.Join("./rec/", fmt.Sprintf("%s.cast", time.Now().Format("20060102_150405")))
|
||||
logrus.Debugf("记录文件名:%s", fileName)
|
||||
f, err := os.OpenFile(fileName, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0766)
|
||||
if err != nil {
|
||||
conn.WriteMessage(websocket.TextMessage, []byte(err.Error()))
|
||||
logrus.Errorf("创建记录文件失败")
|
||||
return
|
||||
}
|
||||
os.MkdirAll(recStoreDir, 0766)
|
||||
fileName := filepath.Join(recStoreDir, fmt.Sprintf("%s.cast", time.Now().Format("20060102_150405")))
|
||||
logrus.Debugf("记录文件名:%s", fileName)
|
||||
f, err := os.OpenFile(fileName, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0766)
|
||||
if err != nil {
|
||||
conn.WriteMessage(websocket.TextMessage, []byte(err.Error()))
|
||||
logrus.Errorf("创建记录文件失败")
|
||||
return
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
recorder = NewRecorder(f)
|
||||
defer f.Close()
|
||||
recorder = NewRecorder(f)
|
||||
|
||||
if recorder != nil {
|
||||
recorder.Lock()
|
||||
recorder.WriteHeader(30, 150)
|
||||
recorder.Unlock()
|
||||
}
|
||||
|
||||
go func() {
|
||||
for {
|
||||
buf := make([]byte, 0x10000)
|
||||
read, err := proc.Read(buf)
|
||||
if err != nil {
|
||||
conn.WriteMessage(websocket.TextMessage, []byte(err.Error()))
|
||||
logrus.Errorf("读取错误:%s", err.Error())
|
||||
return
|
||||
}
|
||||
conn.WriteMessage(websocket.BinaryMessage, buf[:read])
|
||||
if recorder != nil {
|
||||
recorder.Lock()
|
||||
recorder.WriteData(InputType, string(buf[:read]))
|
||||
recorder.Unlock()
|
||||
}
|
||||
}
|
||||
}()
|
||||
if recorder != nil {
|
||||
recorder.Lock()
|
||||
recorder.WriteHeader(30, 150)
|
||||
recorder.Unlock()
|
||||
}
|
||||
|
||||
go func() {
|
||||
for {
|
||||
messageType, reader, err := conn.NextReader()
|
||||
buf := make([]byte, 0x10000)
|
||||
read, err := proc.Read(buf)
|
||||
if err != nil {
|
||||
logrus.Errorf("Unable to grab next reader")
|
||||
conn.WriteMessage(websocket.TextMessage, []byte(err.Error()))
|
||||
logrus.Errorf("读取错误:%s", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if messageType != websocket.TextMessage {
|
||||
logrus.Errorf("无效的消息")
|
||||
//conn.WriteMessage(websocket.TextMessage, []byte("Unexpected text message"))
|
||||
//continue
|
||||
conn.WriteMessage(websocket.BinaryMessage, buf[:read])
|
||||
if recorder != nil {
|
||||
recorder.Lock()
|
||||
recorder.WriteData(InputType, string(buf[:read]))
|
||||
recorder.Unlock()
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
buf := make([]byte, 0x10000)
|
||||
n, err := reader.Read(buf)
|
||||
for {
|
||||
messageType, reader, err := conn.NextReader()
|
||||
if err != nil {
|
||||
logrus.Errorf("Unable to grab next reader")
|
||||
return
|
||||
}
|
||||
|
||||
if messageType != websocket.TextMessage {
|
||||
logrus.Errorf("无效的消息")
|
||||
//conn.WriteMessage(websocket.TextMessage, []byte("Unexpected text message"))
|
||||
//continue
|
||||
}
|
||||
|
||||
buf := make([]byte, 0x10000)
|
||||
n, err := reader.Read(buf)
|
||||
|
||||
if err != nil {
|
||||
logrus.Errorf("Error after copying %d bytes", n)
|
||||
}
|
||||
|
||||
if n > 0 {
|
||||
|
||||
opData := OpDataType{}
|
||||
newBuf := buf[:n]
|
||||
err = json.Unmarshal(newBuf, &opData)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error after copying %d bytes", n)
|
||||
logrus.Errorf("解析错误:%s", err.Error())
|
||||
}
|
||||
if opData.Op == "stdin" {
|
||||
value, ok := opData.Data.(string)
|
||||
if ok {
|
||||
n, err = proc.Write([]byte(value))
|
||||
if err != nil {
|
||||
logrus.Errorf("写放数据错误g %d bytes", n)
|
||||
}
|
||||
} else {
|
||||
|
||||
if n > 0 {
|
||||
|
||||
opData := OpDataType{}
|
||||
newBuf := buf[:n]
|
||||
fmt.Println("收到数据", string(newBuf))
|
||||
err = json.Unmarshal(newBuf, &opData)
|
||||
if err != nil {
|
||||
logrus.Errorf("解析错误:%s", err.Error())
|
||||
}
|
||||
if opData.Op == "stdin" {
|
||||
value, ok := opData.Data.(string)
|
||||
if ok {
|
||||
|
||||
n, err = proc.Write([]byte(value))
|
||||
if err != nil {
|
||||
logrus.Errorf("写放数据错误g %d bytes", n)
|
||||
} else {
|
||||
fmt.Println("写入成功")
|
||||
}
|
||||
} else {
|
||||
|
||||
} else if opData.Op == "resize" {
|
||||
// Data 反序列化后是 map[string]interface{}, 不能直接断言 ResizeDataType
|
||||
// (此前断言恒失败, 前端 resize 被静默吞掉, PTY 固定 180x30)
|
||||
raw, merr := json.Marshal(opData.Data)
|
||||
var size ResizeDataType
|
||||
if merr == nil && json.Unmarshal(raw, &size) == nil && size.Cols > 0 && size.Rows > 0 {
|
||||
if serr := proc.SetSize(size.Cols, size.Rows); serr != nil {
|
||||
logrus.Errorf("重置大小错误: %s", serr.Error())
|
||||
}
|
||||
|
||||
} else if opData.Op == "resize" {
|
||||
// Data 反序列化后是 map[string]interface{}, 不能直接断言 ResizeDataType
|
||||
// (此前断言恒失败, 前端 resize 被静默吞掉, PTY 固定 180x30)
|
||||
raw, merr := json.Marshal(opData.Data)
|
||||
var size ResizeDataType
|
||||
if merr == nil && json.Unmarshal(raw, &size) == nil && size.Cols > 0 && size.Rows > 0 {
|
||||
if serr := proc.SetSize(size.Cols, size.Rows); serr != nil {
|
||||
logrus.Errorf("重置大小错误: %s", serr.Error())
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func RecoderList(c *gin.Context) {
|
||||
files, err := ioutil.ReadDir("./rec")
|
||||
func RecoderList(w http.ResponseWriter, _ *http.Request) {
|
||||
files, err := os.ReadDir(recStoreDir)
|
||||
if err != nil {
|
||||
c.AbortWithStatusJSON(200, gin.H{"ok": false, "msg": err.Error()})
|
||||
webhttp.WriteJSON(w, http.StatusOK, map[string]any{"ok": false, "msg": err.Error()})
|
||||
return
|
||||
}
|
||||
var filesName []string
|
||||
@@ -211,5 +204,5 @@ func RecoderList(c *gin.Context) {
|
||||
filesName = append(filesName, f.Name())
|
||||
}
|
||||
}
|
||||
c.JSON(200, filesName)
|
||||
webhttp.WriteJSON(w, http.StatusOK, filesName)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user