diff --git a/backend/api/controller/connect.go b/backend/api/controller/connect.go index f53eec1..f2bc1d3 100644 --- a/backend/api/controller/connect.go +++ b/backend/api/controller/connect.go @@ -49,7 +49,6 @@ func read(sess *gsession.Session) error { for { select { case <-sess.Gctx.Done(): - fmt.Println("done read-------------------------------") return nil default: if sess.SessionType == model.SESSIONTYPE_WEB { @@ -128,7 +127,7 @@ func HandleSsh(sess *gsession.Session) (err error) { write(sess) return nil case <-sess.IdleTk.C: - writeErrMsg(sess, "idle timeout") + writeErrMsg(sess, "idle timeout\n\n") return &ApiError{Code: ErrIdleTimeout, Data: map[string]any{"second": int64(sess.IdleTimout.Seconds())}} case <-tk1m.C: if mysql.DB.Model(asset).Where("id = ?", sess.AssetId).First(asset).Error != nil { @@ -137,10 +136,10 @@ func HandleSsh(sess *gsession.Session) (err error) { if checkTime(asset.AccessAuth) { continue } - writeErrMsg(sess, "invalid access time") + writeErrMsg(sess, "invalid access time\n\n") return &ApiError{Code: ErrAccessTime} case closeBy := <-chs.CloseChan: - writeErrMsg(sess, "closed by admin") + writeErrMsg(sess, "closed by admin\n\n") logger.L().Info("closed by", zap.String("admin", closeBy)) return &ApiError{Code: ErrAdminClose, Data: map[string]any{"admin": closeBy}} case err := <-chs.ErrChan: @@ -376,6 +375,7 @@ func connectSsh(ctx *gin.Context, sess *gsession.Session, asset *model.Asset, ac } }) sess.G.Go(func() error { + defer sshSess.Close() defer sess.Chans.Rout.Close() defer sess.Chans.Win.Close() for { @@ -383,7 +383,6 @@ func connectSsh(ctx *gin.Context, sess *gsession.Session, asset *model.Asset, ac case <-sess.Gctx.Done(): return nil case <-chs.AwayChan: - sshSess.Close() return fmt.Errorf("away") case window := <-chs.WindowChan: if err := sshSess.WindowChange(window.Height, window.Width); err != nil { @@ -425,7 +424,9 @@ func newGuacdSession(ctx *gin.Context, connectionId, sessionId string, asset *mo func connectGuacd(ctx *gin.Context, sess *gsession.Session, asset *model.Asset, account *model.Account, gateway *model.Gateway) (err error) { chs := sess.Chans defer func() { - chs.ErrChan <- err + if err != nil { + chs.ErrChan <- err + } }() w, h, dpi := cast.ToInt(ctx.Query("w")), cast.ToInt(ctx.Query("h")), cast.ToInt(ctx.Query("dpi")) @@ -436,37 +437,22 @@ func connectGuacd(ctx *gin.Context, sess *gsession.Session, asset *model.Asset, return } session := newGuacdSession(ctx, t.ConnectionId, t.SessionId, asset, account, gateway) - if err = gsession.UpsertSession(session); err != nil { - return - } + session.GuacdTunnel = t chs.ErrChan <- nil - g, gctx := errgroup.WithContext(context.Background()) - g.Go(func() error { + sess.G.Go(func() error { for { select { - case <-gctx.Done(): - return nil - case <-time.After(time.Minute): - close(session.Chans.AwayChan) - return nil - } - } - }) - g.Go(func() error { - for { - select { - case <-gctx.Done(): + case <-sess.Gctx.Done(): return nil default: p, err := t.Read() - if isCtxDone(gctx) { - return nil - } + // if isCtxDone(sess.Gctx) { + // return nil + // } if err != nil { - logger.L().Debug("read instruction failed", zap.Error(err)) return err } if len(p) <= 0 { @@ -477,7 +463,7 @@ func connectGuacd(ctx *gin.Context, sess *gsession.Session, asset *model.Asset, } } }) - g.Go(func() error { + sess.G.Go(func() error { defer func() { t.Disconnect() session.Status = model.SESSIONSTATUS_OFFLINE @@ -489,7 +475,7 @@ func connectGuacd(ctx *gin.Context, sess *gsession.Session, asset *model.Asset, }() for { select { - case <-gctx.Done(): + case <-sess.Gctx.Done(): return nil case <-chs.AwayChan: return fmt.Errorf("away") @@ -498,11 +484,10 @@ func connectGuacd(ctx *gin.Context, sess *gsession.Session, asset *model.Asset, } } }) - if err = g.Wait(); err != nil { - logger.L().Warn("doGuacd stopped", zap.Error(err)) - } - return err + sess.G.Wait() + + return } // Connect godoc diff --git a/backend/api/controller/controller.go b/backend/api/controller/controller.go index 05dac70..fc83666 100644 --- a/backend/api/controller/controller.go +++ b/backend/api/controller/controller.go @@ -143,7 +143,11 @@ func doDelete[T model.Model](ctx *gin.Context, needAcl bool, md T, dcs ...delete if err = mysql.DB.Model(md).Where("id = ?", id).First(md).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { - ctx.JSON(http.StatusOK, defaultHttpResponse) + ctx.JSON(http.StatusOK, HttpResponse{ + Data: map[string]any{ + "id": md.GetId(), + }, + }) return } ctx.AbortWithError(http.StatusBadRequest, &ApiError{Code: ErrInternal, Data: map[string]any{"err": err}}) diff --git a/backend/api/guacd/conn.go b/backend/api/guacd/conn.go index 8f0c1cd..46fb923 100644 --- a/backend/api/guacd/conn.go +++ b/backend/api/guacd/conn.go @@ -14,7 +14,6 @@ import ( ggateway "github.com/veops/oneterm/gateway" "github.com/veops/oneterm/logger" "github.com/veops/oneterm/model" - "github.com/veops/oneterm/util" ) const ( @@ -74,7 +73,7 @@ func NewTunnel(connectionId string, w, h, dpi int, protocol string, asset *model "hostname": asset.Ip, "port": port, "username": account.Account, - "password": util.DecryptAES(account.Password), + "password": account.Password, "disable-copy": "false", "disable-paste": "false", } @@ -167,6 +166,9 @@ func (t *Tunnel) handshake() (err error) { } func (t *Tunnel) Write(p []byte) (n int, err error) { + if t == nil || t.writer == nil { + return + } n, err = t.writer.Write(p) if err != nil { return diff --git a/backend/sshsrv/view.go b/backend/sshsrv/view.go index bb02ef9..cbd4f22 100644 --- a/backend/sshsrv/view.go +++ b/backend/sshsrv/view.go @@ -175,7 +175,7 @@ func (m *view) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } case errMsg: if msg != nil { - return m, tea.Printf(" [ERROR] %s", errStyle.Render(msg.Error())) + return m, tea.Printf(" [ERROR] %s\n\n", errStyle.Render(msg.Error())) } } m.textinput, tiCmd = m.textinput.Update(msg) diff --git a/deploy/docker-compose.yaml b/deploy/docker-compose.yaml index 3c9cb5b..29bfbd4 100644 --- a/deploy/docker-compose.yaml +++ b/deploy/docker-compose.yaml @@ -2,10 +2,7 @@ version: "3.0" services: oneterm-api: - image: oneterm:dev - build: - context: . - dockerfile: ../backend/dockerfile + image: registry.cn-hangzhou.aliyuncs.com/veops/oneterm-api:latest container_name: oneterm-api volumes: - ./volume/replay:/replay @@ -21,7 +18,7 @@ services: - oneterm-api oneterm-guacd: - image: registry.cn-hangzhou.aliyuncs.com/veops/oneterm-guacd:24.3 + image: registry.cn-hangzhou.aliyuncs.com/veops/oneterm-guacd:latest container_name: oneterm-guacd user: root restart: always @@ -64,7 +61,7 @@ services: - redis oneterm-ui: - image: registry.cn-hangzhou.aliyuncs.com/veops/oneterm-ui:24.3 + image: registry.cn-hangzhou.aliyuncs.com/veops/oneterm-ui:latest container_name: oneterm-ui depends_on: - oneterm-api @@ -74,7 +71,7 @@ services: ACL_API_HOST: acl-api:5000 NGINX_PORT: 80 volumes: - - ./docs/nginx.oneterm.conf.example:/etc/nginx/conf.d/nginx.oneterm.conf.example + - ./nginx.oneterm.conf.example:/etc/nginx/conf.d/nginx.oneterm.conf.example restart: always command: - /bin/sh diff --git a/oneterm-ui/Dockerfile b/oneterm-ui/Dockerfile new file mode 100644 index 0000000..4c5e15a --- /dev/null +++ b/oneterm-ui/Dockerfile @@ -0,0 +1,9 @@ +FROM node:16.20.0-alpine AS builder +WORKDIR /oneterm-ui +COPY . . +RUN yarn config set registry https://registry.npmmirror.com/ +RUN yarn install +RUN yarn build + +FROM nginx:alpine +COPY --from=0 /oneterm-ui/dist /etc/nginx/html \ No newline at end of file