diff --git a/pkg/api/recording.go b/pkg/api/recording.go index b795b6e..68f50ec 100644 --- a/pkg/api/recording.go +++ b/pkg/api/recording.go @@ -4,6 +4,7 @@ import ( "encoding/json" "next-terminal/pkg/utils" "os" + "path" "time" ) @@ -26,22 +27,29 @@ type Recorder struct { timestamp int } -func NewRecorder(filename string) (recorder *Recorder, err error) { +func NewRecorder(dir string) (recorder *Recorder, filename string, err error) { recorder = &Recorder{} - if utils.FileExists(filename) { - if err := os.RemoveAll(filename); err != nil { - return nil, err + if utils.FileExists(dir) { + if err := os.RemoveAll(dir); err != nil { + return nil, "", err } } + + if err = os.MkdirAll(dir, 0777); err != nil { + return + } + + filename = path.Join(dir, "recording.cast") + var file *os.File file, err = os.Create(filename) if err != nil { - return nil, err + return nil, "", err } recorder.file = file - return recorder, nil + return recorder, filename, nil } func (recorder *Recorder) Close() { diff --git a/pkg/api/session.go b/pkg/api/session.go index d12422b..32aa5bc 100644 --- a/pkg/api/session.go +++ b/pkg/api/session.go @@ -38,7 +38,13 @@ func SessionPagingEndpoint(c echo.Context) error { for i := 0; i < len(items); i++ { if status == model.Disconnected && len(items[i].Recording) > 0 { - recording := items[i].Recording + "/recording" + + var recording string + if items[i].Protocol == "rdp" || items[i].Protocol == "vnc" { + recording = items[i].Recording + "/recording" + } else { + recording = items[i].Recording + } if utils.FileExists(recording) { logrus.Debugf("检测到录屏文件[%v]存在", recording) @@ -107,12 +113,14 @@ func CloseSessionById(sessionId string, code int, reason string) { observable, _ := global.Store.Get(sessionId) if observable != nil { logrus.Debugf("会话%v创建者退出", observable.Subject.Tunnel.UUID) - _ = observable.Subject.Tunnel.Close() + observable.Subject.Close() + for i := 0; i < len(observable.Observers); i++ { - _ = observable.Observers[i].Tunnel.Close() + observable.Observers[i].Close() CloseWebSocket(observable.Observers[i].WebSocket, code, reason) logrus.Debugf("强制踢出会话%v的观察者", observable.Observers[i].Tunnel.UUID) } + CloseWebSocket(observable.Subject.WebSocket, code, reason) } global.Store.Del(sessionId) @@ -529,8 +537,15 @@ func SessionRecordingEndpoint(c echo.Context) error { if err != nil { return err } - recording := path.Join(session.Recording, "recording") - logrus.Debugf("读取录屏文件:%s", recording) + + var recording string + if session.Protocol == "rdp" || session.Protocol == "vnc" { + recording = session.Recording + "/recording" + } else { + recording = session.Recording + } + + logrus.Debugf("读取录屏文件:%v,是否存在: %v, 是否为文件: %v", recording, utils.FileExists(recording), utils.IsFile(recording)) return c.File(recording) } diff --git a/pkg/api/ssh.go b/pkg/api/ssh.go index e6f8d45..b035564 100644 --- a/pkg/api/ssh.go +++ b/pkg/api/ssh.go @@ -10,8 +10,10 @@ import ( "github.com/sirupsen/logrus" "golang.org/x/crypto/ssh" "net/http" + "next-terminal/pkg/guacd" "next-terminal/pkg/model" "next-terminal/pkg/utils" + "path" "strconv" "sync" "time" @@ -65,7 +67,7 @@ type WindowSize struct { Rows int `json:"rows"` } -func SSHEndpoint(c echo.Context) error { +func SSHEndpoint(c echo.Context) (err error) { ws, err := UpGrader.Upgrade(c.Response().Writer, c.Request(), nil) if err != nil { logrus.Errorf("升级为WebSocket协议失败:%v", err.Error()) @@ -149,30 +151,44 @@ func SSHEndpoint(c echo.Context) error { return err } + var recorder *Recorder + var recording string + property, _ := model.FindPropertyByName(guacd.RecordingPath) + if property.Value != "" { + dir := path.Join(property.Value, sessionId) + recorder, recording, err = NewRecorder(dir) + if err != nil { + msg := Message{ + Type: Closed, + Content: "创建录屏文件失败 :( " + err.Error(), + } + return WriteMessage(ws, msg) + } + + header := &Header{ + Title: "test", + Version: 2, + Height: height, + Width: width, + Env: Env{Shell: "/bin/bash", Term: "xterm-256color"}, + Timestamp: int(time.Now().Unix()), + } + + if err := recorder.WriteHeader(header); err != nil { + return err + } + + if err := model.UpdateSessionById(&model.Session{Recording: recording}, sessionId); err != nil { + return err + } + } + msg := Message{ Type: Connected, Content: "Connect to server successfully.\r\n", } _ = WriteMessage(ws, msg) - recorder, err := NewRecorder("./" + sessionId + ".cast") - if err != nil { - return err - } - - header := &Header{ - Title: "test", - Version: 2, - Height: height, - Width: width, - Env: Env{Shell: "/bin/bash", Term: "xterm-256color"}, - Timestamp: int(time.Now().Unix()), - } - - if err := recorder.WriteHeader(header); err != nil { - return err - } - var mut sync.Mutex var active = true @@ -181,7 +197,10 @@ func SSHEndpoint(c echo.Context) error { mut.Lock() if !active { logrus.Debugf("会话: %v -> %v 关闭", sshClient.LocalAddr().String(), sshClient.RemoteAddr().String()) - recorder.Close() + if recorder != nil { + recorder.Close() + } + CloseSessionById(sessionId, Normal, "正常退出") break } mut.Unlock() @@ -192,8 +211,10 @@ func SSHEndpoint(c echo.Context) error { } if n > 0 { s := string(p) - // 录屏 - _ = recorder.WriteData(s) + if recorder != nil { + // 录屏 + _ = recorder.WriteData(s) + } msg := Message{ Type: Data, Content: s, diff --git a/pkg/api/tunnel.go b/pkg/api/tunnel.go index 1e07cf8..9b4b19d 100644 --- a/pkg/api/tunnel.go +++ b/pkg/api/tunnel.go @@ -145,6 +145,7 @@ func TunEndpoint(c echo.Context) error { } tun := global.Tun{ + Protocol: configuration.Protocol, Tunnel: tunnel, WebSocket: ws, } diff --git a/pkg/global/store.go b/pkg/global/store.go index fc62b7f..1617249 100644 --- a/pkg/global/store.go +++ b/pkg/global/store.go @@ -3,16 +3,28 @@ package global import ( "github.com/gorilla/websocket" "github.com/pkg/sftp" + "golang.org/x/crypto/ssh" "next-terminal/pkg/guacd" "sync" ) type Tun struct { + Protocol string Tunnel *guacd.Tunnel + SshClient *ssh.Client SftpClient *sftp.Client WebSocket *websocket.Conn } +func (r *Tun) Close() { + if r.Protocol == "rdp" || r.Protocol == "vnc" { + _ = r.Tunnel.Close() + } else { + _ = r.SshClient.Close() + _ = r.SftpClient.Close() + } +} + type Observable struct { Subject *Tun Observers []Tun diff --git a/web/src/components/session/asciinema-player.css b/web/public/asciinema-player.css similarity index 100% rename from web/src/components/session/asciinema-player.css rename to web/public/asciinema-player.css diff --git a/web/src/components/session/asciinema-player.js b/web/public/asciinema-player.js similarity index 100% rename from web/src/components/session/asciinema-player.js rename to web/public/asciinema-player.js diff --git a/web/public/asciinema.html b/web/public/asciinema.html new file mode 100644 index 0000000..82c6f95 --- /dev/null +++ b/web/public/asciinema.html @@ -0,0 +1,38 @@ + + +
+ +