mirror of
https://github.com/0xJacky/nginx-ui.git
synced 2025-05-10 18:05:48 +02:00
* refactor: nodes analytics * feat(debug): add pprof in debug mode * refactor: websocket error handler
166 lines
3.4 KiB
Go
166 lines
3.4 KiB
Go
package pty
|
|
|
|
import (
|
|
"encoding/json"
|
|
"github.com/0xJacky/Nginx-UI/internal/helper"
|
|
"github.com/0xJacky/Nginx-UI/settings"
|
|
"github.com/creack/pty"
|
|
"github.com/gorilla/websocket"
|
|
"github.com/pkg/errors"
|
|
"github.com/uozi-tech/cosy/logger"
|
|
"os"
|
|
"os/exec"
|
|
"time"
|
|
"unicode/utf8"
|
|
)
|
|
|
|
type Pipeline struct {
|
|
Pty *os.File
|
|
cmd *exec.Cmd
|
|
ws *websocket.Conn
|
|
}
|
|
|
|
type Message struct {
|
|
Type MsgType
|
|
Data json.RawMessage
|
|
}
|
|
|
|
const bufferSize = 2048
|
|
|
|
func NewPipeLine(conn *websocket.Conn) (p *Pipeline, err error) {
|
|
c := exec.Command(settings.TerminalSettings.StartCmd)
|
|
|
|
ptmx, err := pty.StartWithSize(c, &pty.Winsize{Cols: 90, Rows: 60})
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "start pty error")
|
|
}
|
|
|
|
p = &Pipeline{
|
|
Pty: ptmx,
|
|
cmd: c,
|
|
ws: conn,
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (p *Pipeline) ReadWsAndWritePty(errorChan chan error) {
|
|
for {
|
|
msgType, payload, err := p.ws.ReadMessage()
|
|
if err != nil {
|
|
if helper.IsUnexpectedWebsocketError(err) {
|
|
errorChan <- errors.Wrap(err, "Error ReadWsAndWritePty unexpected close")
|
|
}
|
|
return
|
|
}
|
|
if msgType != websocket.TextMessage {
|
|
errorChan <- errors.Errorf("Error ReadWsAndWritePty Invalid msgType: %v", msgType)
|
|
return
|
|
}
|
|
|
|
var msg Message
|
|
err = json.Unmarshal(payload, &msg)
|
|
if err != nil {
|
|
errorChan <- errors.Wrap(err, "Error ReadWsAndWritePty json.Unmarshal")
|
|
return
|
|
}
|
|
|
|
switch msg.Type {
|
|
case TypeData:
|
|
var data string
|
|
err = json.Unmarshal(msg.Data, &data)
|
|
if err != nil {
|
|
errorChan <- errors.Wrap(err, "Error ReadWsAndWritePty json.Unmarshal msg.Data")
|
|
return
|
|
}
|
|
|
|
_, err = p.Pty.Write([]byte(data))
|
|
|
|
if err != nil {
|
|
errorChan <- errors.Wrap(err, "Error ReadWsAndWritePty write pty")
|
|
return
|
|
}
|
|
case TypeResize:
|
|
var win struct {
|
|
Cols uint16
|
|
Rows uint16
|
|
}
|
|
|
|
err = json.Unmarshal(msg.Data, &win)
|
|
if err != nil {
|
|
errorChan <- errors.Wrap(err, "Error ReadSktAndWritePty Invalid resize message")
|
|
return
|
|
}
|
|
err = pty.Setsize(p.Pty, &pty.Winsize{Rows: win.Rows, Cols: win.Cols})
|
|
if err != nil {
|
|
errorChan <- errors.Wrap(err, "Error ReadSktAndWritePty set pty size")
|
|
return
|
|
}
|
|
case TypePing:
|
|
err = p.ws.WriteControl(websocket.PongMessage, []byte{}, time.Now().Add(time.Second))
|
|
if err != nil {
|
|
errorChan <- errors.Wrap(err, "Error ReadSktAndWritePty write pong")
|
|
return
|
|
}
|
|
default:
|
|
errorChan <- errors.Errorf("Error ReadWsAndWritePty unknown msg.Type %v", msg.Type)
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func (p *Pipeline) ReadPtyAndWriteWs(errorChan chan error) {
|
|
buf := make([]byte, bufferSize)
|
|
for {
|
|
n, err := p.Pty.Read(buf)
|
|
if err != nil {
|
|
errorChan <- errors.Wrap(err, "Error ReadPtyAndWriteWs read pty")
|
|
return
|
|
}
|
|
processedOutput := validString(string(buf[:n]))
|
|
err = p.ws.WriteMessage(websocket.TextMessage, []byte(processedOutput))
|
|
if err != nil {
|
|
if helper.IsUnexpectedWebsocketError(err) {
|
|
errorChan <- errors.Wrap(err, "Error ReadPtyAndWriteWs websocket write")
|
|
}
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func (p *Pipeline) Close() {
|
|
err := p.Pty.Close()
|
|
|
|
if err != nil {
|
|
logger.Error(err)
|
|
}
|
|
|
|
err = p.cmd.Process.Kill()
|
|
|
|
if err != nil {
|
|
logger.Error(err)
|
|
}
|
|
|
|
_, err = p.cmd.Process.Wait()
|
|
|
|
if err != nil {
|
|
logger.Error(err)
|
|
}
|
|
}
|
|
|
|
func validString(s string) string {
|
|
if !utf8.ValidString(s) {
|
|
v := make([]rune, 0, len(s))
|
|
for i, r := range s {
|
|
if r == utf8.RuneError {
|
|
_, size := utf8.DecodeRuneInString(s[i:])
|
|
if size == 1 {
|
|
continue
|
|
}
|
|
}
|
|
v = append(v, r)
|
|
}
|
|
s = string(v)
|
|
}
|
|
return s
|
|
}
|