diff --git a/backend/app/api/v1/terminal.go b/backend/app/api/v1/terminal.go index 623edbe9c..347b72506 100644 --- a/backend/app/api/v1/terminal.go +++ b/backend/app/api/v1/terminal.go @@ -79,48 +79,6 @@ func (b *BaseApi) WsSsh(c *gin.Context) { } } -func (b *BaseApi) LocalWsSsh(c *gin.Context) { - cols, err := strconv.Atoi(c.DefaultQuery("cols", "80")) - if err != nil { - helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) - return - } - rows, err := strconv.Atoi(c.DefaultQuery("rows", "40")) - if err != nil { - helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) - return - } - - wsConn, err := upGrader.Upgrade(c.Writer, c.Request, nil) - if err != nil { - global.LOG.Errorf("gin context http handler failed, err: %v", err) - return - } - defer wsConn.Close() - - slave, err := terminal.NewCommand() - if wshandleError(wsConn, err) { - return - } - defer slave.Close() - - tty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave) - if wshandleError(wsConn, err) { - return - } - - quitChan := make(chan bool, 3) - tty.Start(quitChan) - go slave.Wait(quitChan) - - <-quitChan - - global.LOG.Info("websocket finished") - if wshandleError(wsConn, err) { - return - } -} - func wshandleError(ws *websocket.Conn, err error) bool { if err != nil { global.LOG.Errorf("handler ws faled:, err: %v", err) diff --git a/backend/app/model/host.go b/backend/app/model/host.go index 39d5e90c3..9a21ef052 100644 --- a/backend/app/model/host.go +++ b/backend/app/model/host.go @@ -4,7 +4,7 @@ type Host struct { BaseModel GroupBelong string `gorm:"type:varchar(64);not null" json:"groupBelong"` Name string `gorm:"type:varchar(64);unique;not null" json:"name"` - Addr string `gorm:"type:varchar(16);unique;not null" json:"addr"` + Addr string `gorm:"type:varchar(16);not null" json:"addr"` Port int `gorm:"type:varchar(5);not null" json:"port"` User string `gorm:"type:varchar(64);not null" json:"user"` AuthMode string `gorm:"type:varchar(16);not null" json:"authMode"` diff --git a/backend/app/repo/host.go b/backend/app/repo/host.go index 2cb486ae2..de4d53bdd 100644 --- a/backend/app/repo/host.go +++ b/backend/app/repo/host.go @@ -12,6 +12,9 @@ type IHostRepo interface { Get(opts ...DBOption) (model.Host, error) GetList(opts ...DBOption) ([]model.Host, error) WithByInfo(info string) DBOption + WithByPort(port uint) DBOption + WithByUser(user string) DBOption + WithByAddr(addr string) DBOption Create(host *model.Host) error ChangeGroup(oldGroup, newGroup string) error Update(id uint, vars map[string]interface{}) error @@ -52,6 +55,22 @@ func (c *HostRepo) WithByInfo(info string) DBOption { } } +func (u *HostRepo) WithByPort(port uint) DBOption { + return func(g *gorm.DB) *gorm.DB { + return g.Where("port = ?", port) + } +} +func (u *HostRepo) WithByUser(user string) DBOption { + return func(g *gorm.DB) *gorm.DB { + return g.Where("user = ?", user) + } +} +func (u *HostRepo) WithByAddr(addr string) DBOption { + return func(g *gorm.DB) *gorm.DB { + return g.Where("addr = ?", addr) + } +} + func (u *HostRepo) Create(host *model.Host) error { return global.DB.Create(host).Error } diff --git a/backend/app/service/host.go b/backend/app/service/host.go index 1062947ec..78249eb99 100644 --- a/backend/app/service/host.go +++ b/backend/app/service/host.go @@ -57,14 +57,43 @@ func (u *HostService) SearchForTree(search dto.SearchForTree) ([]dto.HostTree, e return datas, err } -func (u *HostService) Create(hostDto dto.HostOperate) (*dto.HostInfo, error) { - host, _ := hostRepo.Get(commonRepo.WithByName(hostDto.Name)) +func (u *HostService) Create(req dto.HostOperate) (*dto.HostInfo, error) { + host, _ := hostRepo.Get(commonRepo.WithByName(req.Name)) if host.ID != 0 { return nil, constant.ErrRecordExist } - if err := copier.Copy(&host, &hostDto); err != nil { + if err := copier.Copy(&host, &req); err != nil { return nil, errors.WithMessage(constant.ErrStructTransform, err.Error()) } + var sameHostID uint + if req.Addr == "127.0.0.1" { + hostSame, _ := hostRepo.Get(hostRepo.WithByAddr(req.Addr)) + sameHostID = hostSame.ID + } else { + hostSame, _ := hostRepo.Get(hostRepo.WithByAddr(req.Addr), hostRepo.WithByUser(req.User), hostRepo.WithByPort(req.Port)) + sameHostID = hostSame.ID + } + if sameHostID != 0 { + host.ID = sameHostID + upMap := make(map[string]interface{}) + upMap["name"] = req.Name + upMap["group_belong"] = req.GroupBelong + upMap["addr"] = req.Addr + upMap["port"] = req.Port + upMap["user"] = req.User + upMap["auth_mode"] = req.AuthMode + upMap["password"] = req.Password + upMap["private_key"] = req.PrivateKey + if err := hostRepo.Update(sameHostID, upMap); err != nil { + return nil, err + } + var hostinfo dto.HostInfo + if err := copier.Copy(&hostinfo, &host); err != nil { + return nil, errors.WithMessage(constant.ErrStructTransform, err.Error()) + } + return &hostinfo, nil + } + if err := hostRepo.Create(&host); err != nil { return nil, err } diff --git a/backend/router/ro_terminal.go b/backend/router/ro_terminal.go index 20a21c3f3..9f06e3fa4 100644 --- a/backend/router/ro_terminal.go +++ b/backend/router/ro_terminal.go @@ -17,6 +17,5 @@ func (s *TerminalRouter) InitTerminalRouter(Router *gin.RouterGroup) { baseApi := v1.ApiGroupApp.BaseApi { terminalRouter.GET("", baseApi.WsSsh) - terminalRouter.GET("/local", baseApi.LocalWsSsh) } } diff --git a/backend/utils/terminal/local_cmd.go b/backend/utils/terminal/local_cmd.go deleted file mode 100644 index 5ff0338c7..000000000 --- a/backend/utils/terminal/local_cmd.go +++ /dev/null @@ -1,115 +0,0 @@ -package terminal - -import ( - "os" - "os/exec" - "syscall" - "time" - "unsafe" - - "github.com/1Panel-dev/1Panel/backend/global" - "github.com/kr/pty" - "github.com/pkg/errors" -) - -const ( - DefaultCloseSignal = syscall.SIGINT - DefaultCloseTimeout = 10 * time.Second -) - -type LocalCommand struct { - command string - - closeSignal syscall.Signal - closeTimeout time.Duration - - cmd *exec.Cmd - pty *os.File - ptyClosed chan struct{} -} - -func NewCommand() (*LocalCommand, error) { - command := "sh" - cmd := exec.Command(command) - cmd.Dir = "/" - - pty, err := pty.Start(cmd) - if err != nil { - return nil, errors.Wrapf(err, "failed to start command `%s`", command) - } - ptyClosed := make(chan struct{}) - - lcmd := &LocalCommand{ - command: command, - closeSignal: DefaultCloseSignal, - closeTimeout: DefaultCloseTimeout, - - cmd: cmd, - pty: pty, - ptyClosed: ptyClosed, - } - - return lcmd, nil -} - -func (lcmd *LocalCommand) Read(p []byte) (n int, err error) { - return lcmd.pty.Read(p) -} - -func (lcmd *LocalCommand) Write(p []byte) (n int, err error) { - return lcmd.pty.Write(p) -} - -func (lcmd *LocalCommand) Close() error { - if lcmd.cmd != nil && lcmd.cmd.Process != nil { - _ = lcmd.cmd.Process.Signal(lcmd.closeSignal) - } - for { - select { - case <-lcmd.ptyClosed: - return nil - case <-lcmd.closeTimeoutC(): - _ = lcmd.cmd.Process.Signal(syscall.SIGKILL) - } - } -} - -func (lcmd *LocalCommand) ResizeTerminal(width int, height int) error { - window := struct { - row uint16 - col uint16 - x uint16 - y uint16 - }{ - uint16(height), - uint16(width), - 0, - 0, - } - _, _, errno := syscall.Syscall( - syscall.SYS_IOCTL, - lcmd.pty.Fd(), - syscall.TIOCSWINSZ, - uintptr(unsafe.Pointer(&window)), - ) - if errno != 0 { - return errno - } else { - return nil - } -} - -func (lcmd *LocalCommand) Wait(quitChan chan bool) { - if err := lcmd.cmd.Wait(); err != nil { - global.LOG.Errorf("ssh session wait failed, err: %v", err) - setQuit(quitChan) - } -} - -func (lcmd *LocalCommand) closeTimeoutC() <-chan time.Time { - if lcmd.closeTimeout >= 0 { - return time.After(lcmd.closeTimeout) - } - - return make(chan time.Time) -} diff --git a/backend/utils/terminal/ws_local_session.go b/backend/utils/terminal/ws_local_session.go deleted file mode 100644 index 136658ebc..000000000 --- a/backend/utils/terminal/ws_local_session.go +++ /dev/null @@ -1,99 +0,0 @@ -package terminal - -import ( - "encoding/base64" - "encoding/json" - "sync" - - "github.com/1Panel-dev/1Panel/backend/global" - "github.com/gorilla/websocket" - "github.com/pkg/errors" -) - -type LocalWsSession struct { - slave *LocalCommand - wsConn *websocket.Conn - - writeMutex sync.Mutex -} - -func NewLocalWsSession(cols, rows int, wsConn *websocket.Conn, slave *LocalCommand) (*LocalWsSession, error) { - if err := slave.ResizeTerminal(cols, rows); err != nil { - global.LOG.Errorf("ssh pty change windows size failed, err: %v", err) - } - - return &LocalWsSession{ - slave: slave, - wsConn: wsConn, - }, nil -} - -func (sws *LocalWsSession) Start(quitChan chan bool) { - go sws.handleSlaveEvent(quitChan) - go sws.receiveWsMsg(quitChan) -} - -func (sws *LocalWsSession) handleSlaveEvent(exitCh chan bool) { - defer setQuit(exitCh) - - buffer := make([]byte, 1024) - for { - select { - case <-exitCh: - return - default: - n, _ := sws.slave.Read(buffer) - _ = sws.masterWrite(buffer[:n]) - } - } -} - -func (sws *LocalWsSession) masterWrite(data []byte) error { - sws.writeMutex.Lock() - defer sws.writeMutex.Unlock() - err := sws.wsConn.WriteMessage(websocket.TextMessage, data) - if err != nil { - return errors.Wrapf(err, "failed to write to master") - } - - return nil -} - -func (sws *LocalWsSession) receiveWsMsg(exitCh chan bool) { - wsConn := sws.wsConn - defer setQuit(exitCh) - for { - select { - case <-exitCh: - return - default: - _, wsData, err := wsConn.ReadMessage() - if err != nil { - global.LOG.Errorf("reading webSocket message failed, err: %v", err) - return - } - msgObj := wsMsg{} - _ = json.Unmarshal(wsData, &msgObj) - switch msgObj.Type { - case wsMsgResize: - if msgObj.Cols > 0 && msgObj.Rows > 0 { - if err := sws.slave.ResizeTerminal(msgObj.Cols, msgObj.Rows); err != nil { - global.LOG.Errorf("ssh pty change windows size failed, err: %v", err) - } - } - case wsMsgCmd: - decodeBytes, err := base64.StdEncoding.DecodeString(msgObj.Cmd) - if err != nil { - global.LOG.Errorf("websock cmd string base64 decoding failed, err: %v", err) - } - sws.sendWebsocketInputCommandToSshSessionStdinPipe(decodeBytes) - } - } - } -} - -func (sws *LocalWsSession) sendWebsocketInputCommandToSshSessionStdinPipe(cmdBytes []byte) { - if _, err := sws.slave.Write(cmdBytes); err != nil { - global.LOG.Errorf("ws cmd bytes write to ssh.stdin pipe failed, err: %v", err) - } -} diff --git a/frontend/src/routers/modules/host.ts b/frontend/src/routers/modules/host.ts index c15199927..e61a26f15 100644 --- a/frontend/src/routers/modules/host.ts +++ b/frontend/src/routers/modules/host.ts @@ -29,27 +29,7 @@ const hostRouter = { { path: '/hosts/terminal', name: 'Terminal', - component: () => import('@/views/host/terminal/terminal/index.vue'), - meta: { - title: 'menu.terminal', - keepAlive: true, - }, - }, - { - path: '/hosts/host', - name: 'TerminalHost', - hidden: true, - component: () => import('@/views/host/terminal/host/index.vue'), - meta: { - title: 'menu.terminal', - keepAlive: true, - }, - }, - { - path: '/hosts/command', - name: 'TerminalCommand', - hidden: true, - component: () => import('@/views/host/terminal/command/index.vue'), + component: () => import('@/views/host/terminal/index.vue'), meta: { title: 'menu.terminal', keepAlive: true, diff --git a/frontend/src/views/container/compose/index.vue b/frontend/src/views/container/compose/index.vue index 1731d2cfe..ae9140d42 100644 --- a/frontend/src/views/container/compose/index.vue +++ b/frontend/src/views/container/compose/index.vue @@ -170,7 +170,7 @@ const buttons = [ onEdit(row); }, disabled: (row: any) => { - return row.createdBy !== 'local'; + return row.createdBy !== 'Local'; }, }, { @@ -179,7 +179,7 @@ const buttons = [ onDelete(row); }, disabled: (row: any) => { - return row.createdBy === 'apps'; + return row.createdBy === 'Apps'; }, }, ]; diff --git a/frontend/src/views/host/terminal/command/index.vue b/frontend/src/views/host/terminal/command/index.vue index 7a2c6d372..d60e915d7 100644 --- a/frontend/src/views/host/terminal/command/index.vue +++ b/frontend/src/views/host/terminal/command/index.vue @@ -1,6 +1,5 @@