Skip to content

Commit 4d7a662

Browse files
author
ssongliu
committed
feat: Change the local connection mode of the terminal
1 parent 0ee7e1d commit 4d7a662

16 files changed

Lines changed: 137 additions & 170 deletions

File tree

agent/app/api/v2/terminal.go

Lines changed: 81 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,55 @@ import (
1818
"github.com/pkg/errors"
1919
)
2020

21+
func (b *BaseApi) WsSSH(c *gin.Context) {
22+
wsConn, err := upGrader.Upgrade(c.Writer, c.Request, nil)
23+
if err != nil {
24+
global.LOG.Errorf("gin context http handler failed, err: %v", err)
25+
return
26+
}
27+
defer wsConn.Close()
28+
29+
if global.CONF.Base.IsDemo {
30+
if wshandleError(wsConn, errors.New(" demo server, prohibit this operation!")) {
31+
return
32+
}
33+
}
34+
35+
cols, err := strconv.Atoi(c.DefaultQuery("cols", "80"))
36+
if wshandleError(wsConn, errors.WithMessage(err, "invalid param cols in request")) {
37+
return
38+
}
39+
rows, err := strconv.Atoi(c.DefaultQuery("rows", "40"))
40+
if wshandleError(wsConn, errors.WithMessage(err, "invalid param rows in request")) {
41+
return
42+
}
43+
name, err := loadExecutor()
44+
if wshandleError(wsConn, err) {
45+
return
46+
}
47+
slave, err := terminal.NewCommand(name)
48+
if wshandleError(wsConn, err) {
49+
return
50+
}
51+
defer slave.Close()
52+
53+
tty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave, false)
54+
if wshandleError(wsConn, err) {
55+
return
56+
}
57+
58+
quitChan := make(chan bool, 3)
59+
tty.Start(quitChan)
60+
go slave.Wait(quitChan)
61+
62+
<-quitChan
63+
64+
global.LOG.Info("websocket finished")
65+
if wshandleError(wsConn, err) {
66+
return
67+
}
68+
}
69+
2170
func (b *BaseApi) ContainerWsSSH(c *gin.Context) {
2271
wsConn, err := upGrader.Upgrade(c.Writer, c.Request, nil)
2372
if err != nil {
@@ -42,7 +91,7 @@ func (b *BaseApi) ContainerWsSSH(c *gin.Context) {
4291
}
4392
source := c.Query("source")
4493
var containerID string
45-
var initCmd string
94+
var initCmd []string
4695
switch source {
4796
case "redis":
4897
containerID, initCmd, err = loadRedisInitCmd(c)
@@ -59,11 +108,11 @@ func (b *BaseApi) ContainerWsSSH(c *gin.Context) {
59108
return
60109
}
61110
pidMap := loadMapFromDockerTop(containerID)
62-
slave, err := terminal.NewCommand("clear && " + initCmd)
111+
slave, err := terminal.NewCommand("docker", initCmd...)
63112
if wshandleError(wsConn, err) {
64113
return
65114
}
66-
defer killBash(containerID, strings.ReplaceAll(initCmd, fmt.Sprintf("docker exec -it %s ", containerID), ""), pidMap)
115+
defer killBash(containerID, strings.ReplaceAll(strings.Join(initCmd, " "), fmt.Sprintf("exec -it %s ", containerID), ""), pidMap)
67116
defer slave.Close()
68117

69118
tty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave, false)
@@ -83,62 +132,63 @@ func (b *BaseApi) ContainerWsSSH(c *gin.Context) {
83132
}
84133
}
85134

86-
func loadRedisInitCmd(c *gin.Context) (string, string, error) {
135+
func loadRedisInitCmd(c *gin.Context) (string, []string, error) {
87136
name := c.Query("name")
88137
from := c.Query("from")
89-
commands := "redis-cli"
138+
commands := []string{"exec", "-it"}
90139
database, err := databaseService.Get(name)
91140
if err != nil {
92-
return "", "", fmt.Errorf("no such database in db, err: %v", err)
141+
return "", nil, fmt.Errorf("no such database in db, err: %v", err)
93142
}
94143
if from == "local" {
95144
redisInfo, err := appInstallService.LoadConnInfo(dto.OperationWithNameAndType{Name: name, Type: "redis"})
96145
if err != nil {
97-
return "", "", fmt.Errorf("no such app in db, err: %v", err)
146+
return "", nil, fmt.Errorf("no such app in db, err: %v", err)
98147
}
99148
name = redisInfo.ContainerName
149+
commands = append(commands, []string{name, "redis-cli"}...)
100150
if len(database.Password) != 0 {
101-
commands = "redis-cli -a " + database.Password + " --no-auth-warning"
151+
commands = append(commands, []string{"-a", database.Password, "--no-auth-warning"}...)
102152
}
103153
} else {
104-
commands = fmt.Sprintf("redis-cli -h %s -p %v", database.Address, database.Port)
154+
name = "1Panel-redis-cli-tools"
155+
commands = append(commands, []string{name, "redis-cli", "-h", database.Address, "-p", fmt.Sprintf("%v", database.Port)}...)
105156
if len(database.Password) != 0 {
106-
commands = fmt.Sprintf("redis-cli -h %s -p %v -a %s --no-auth-warning", database.Address, database.Port, database.Password)
157+
commands = append(commands, []string{"-a", database.Password, "--no-auth-warning"}...)
107158
}
108-
name = "1Panel-redis-cli-tools"
109159
}
110-
return name, fmt.Sprintf("docker exec -it %s %s", name, commands), nil
160+
return name, commands, nil
111161
}
112162

113-
func loadOllamaInitCmd(c *gin.Context) (string, string, error) {
163+
func loadOllamaInitCmd(c *gin.Context) (string, []string, error) {
114164
name := c.Query("name")
115165
if cmd.CheckIllegal(name) {
116-
return "", "", fmt.Errorf("ollama model %s contains illegal characters", name)
166+
return "", nil, fmt.Errorf("ollama model %s contains illegal characters", name)
117167
}
118168
ollamaInfo, err := appInstallService.LoadConnInfo(dto.OperationWithNameAndType{Name: "", Type: "ollama"})
119169
if err != nil {
120-
return "", "", fmt.Errorf("no such app in db, err: %v", err)
170+
return "", nil, fmt.Errorf("no such app in db, err: %v", err)
121171
}
122172
containerName := ollamaInfo.ContainerName
123-
return containerName, fmt.Sprintf("docker exec -it %s ollama run %s", containerName, name), nil
173+
return containerName, []string{"exec", "-it", containerName, "ollama", "run", name}, nil
124174
}
125175

126-
func loadContainerInitCmd(c *gin.Context) (string, string, error) {
176+
func loadContainerInitCmd(c *gin.Context) (string, []string, error) {
127177
containerID := c.Query("containerid")
128178
command := c.Query("command")
129179
user := c.Query("user")
130180
if cmd.CheckIllegal(user, containerID, command) {
131-
return "", "", fmt.Errorf("the command contains illegal characters. command: %s, user: %s, containerID: %s", command, user, containerID)
181+
return "", nil, fmt.Errorf("the command contains illegal characters. command: %s, user: %s, containerID: %s", command, user, containerID)
132182
}
133183
if len(command) == 0 || len(containerID) == 0 {
134-
return "", "", fmt.Errorf("error param of command: %s or containerID: %s", command, containerID)
184+
return "", nil, fmt.Errorf("error param of command: %s or containerID: %s", command, containerID)
135185
}
136-
command = fmt.Sprintf("docker exec -it %s %s", containerID, command)
186+
commands := []string{"exec", "-it", containerID, command}
137187
if len(user) != 0 {
138-
command = fmt.Sprintf("docker exec -it -u %s %s %s", user, containerID, command)
188+
commands = []string{"exec", "-it", "-u", user, containerID, command}
139189
}
140190

141-
return containerID, command, nil
191+
return containerID, commands, nil
142192
}
143193

144194
func wshandleError(ws *websocket.Conn, err error) bool {
@@ -204,3 +254,12 @@ var upGrader = websocket.Upgrader{
204254
return true
205255
},
206256
}
257+
258+
func loadExecutor() (string, error) {
259+
std, err := cmd.RunDefaultWithStdoutBashC("echo $SHELL")
260+
if err != nil {
261+
return "", fmt.Errorf("load default executor failed, err: %s", std)
262+
}
263+
264+
return strings.ReplaceAll(std, "\n", ""), nil
265+
}

agent/router/ro_host.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,5 +46,7 @@ func (s *HostRouter) InitRouter(Router *gin.RouterGroup) {
4646
hostRouter.POST("/tool/supervisor/process", baseApi.OperateProcess)
4747
hostRouter.GET("/tool/supervisor/process", baseApi.GetProcess)
4848
hostRouter.POST("/tool/supervisor/process/file", baseApi.GetProcessFile)
49+
50+
hostRouter.GET("/exec", baseApi.WsSSH)
4951
}
5052
}

agent/utils/terminal/local_cmd.go

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,22 +25,20 @@ type LocalCommand struct {
2525
pty *os.File
2626
}
2727

28-
func NewCommand(initCmd string) (*LocalCommand, error) {
29-
cmd := exec.Command("bash")
28+
func NewCommand(name string, arg ...string) (*LocalCommand, error) {
29+
cmd := exec.Command(name, arg...)
3030
if term := os.Getenv("TERM"); term != "" {
3131
cmd.Env = append(os.Environ(), "TERM="+term)
3232
} else {
3333
cmd.Env = append(os.Environ(), "TERM=xterm")
3434
}
35+
homeDir, _ := os.UserHomeDir()
36+
cmd.Dir = homeDir
3537

3638
pty, err := pty.Start(cmd)
3739
if err != nil {
3840
return nil, errors.Wrapf(err, "failed to start command")
3941
}
40-
if len(initCmd) != 0 {
41-
time.Sleep(100 * time.Millisecond)
42-
_, _ = pty.Write([]byte(initCmd + "\n"))
43-
}
4442

4543
lcmd := &LocalCommand{
4644
closeSignal: DefaultCloseSignal,
@@ -99,4 +97,5 @@ func (lcmd *LocalCommand) Wait(quitChan chan bool) {
9997
global.LOG.Errorf("ssh session wait failed, err: %v", err)
10098
setQuit(quitChan)
10199
}
100+
setQuit(quitChan)
102101
}

core/app/service/host.go

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -79,16 +79,9 @@ func (u *HostService) TestLocalConn(id uint) bool {
7979
host model.Host
8080
err error
8181
)
82-
if id == 0 {
83-
host, err = hostRepo.Get(hostRepo.WithByAddr("127.0.0.1"))
84-
if err != nil {
85-
return false
86-
}
87-
} else {
88-
host, err = hostRepo.Get(repo.WithByID(id))
89-
if err != nil {
90-
return false
91-
}
82+
host, err = hostRepo.Get(repo.WithByID(id))
83+
if err != nil {
84+
return false
9285
}
9386
var connInfo ssh.ConnInfo
9487
if err := copier.Copy(&connInfo, &host); err != nil {
@@ -206,11 +199,7 @@ func (u *HostService) SearchForTree(search dto.SearchForTree) ([]dto.HostTree, e
206199
func (u *HostService) GetHostByID(id uint) (*dto.HostInfo, error) {
207200
var item dto.HostInfo
208201
var host model.Host
209-
if id == 0 {
210-
host, _ = hostRepo.Get(repo.WithByName("local"))
211-
} else {
212-
host, _ = hostRepo.Get(repo.WithByID(id))
213-
}
202+
host, _ = hostRepo.Get(repo.WithByID(id))
214203
if host.ID == 0 {
215204
return nil, buserr.New("ErrRecordNotFound")
216205
}
@@ -246,9 +235,6 @@ func (u *HostService) GetHostByID(id uint) (*dto.HostInfo, error) {
246235
}
247236

248237
func (u *HostService) Create(req dto.HostOperate) (*dto.HostInfo, error) {
249-
if req.Name == "local" {
250-
return nil, buserr.New("ErrRecordExist")
251-
}
252238
hostItem, _ := hostRepo.Get(hostRepo.WithByAddr(req.Addr), hostRepo.WithByUser(req.User), hostRepo.WithByPort(req.Port))
253239
if hostItem.ID != 0 {
254240
return nil, buserr.New("ErrRecordExist")
@@ -305,9 +291,6 @@ func (u *HostService) Delete(ids []uint) error {
305291
if host.ID == 0 {
306292
return buserr.New("ErrRecordNotFound")
307293
}
308-
if host.Name == "local" {
309-
return errors.New("the local connection information cannot be deleted!")
310-
}
311294
if err := hostRepo.Delete(repo.WithByID(id)); err != nil {
312295
return err
313296
}

frontend/src/lang/modules/en.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1092,7 +1092,6 @@ const message = {
10921092
terminal: {
10931093
local: 'Local',
10941094
localHelper: 'The `local` name is used only for system local identification',
1095-
connLocalErr: 'Unable to automatically authenticate, please fill in the local server login information!',
10961095
testConn: 'Test connection',
10971096
saveAndConn: 'Save and Connect',
10981097
connTestOk: 'Connection information available',

frontend/src/lang/modules/ja.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1050,7 +1050,6 @@ const message = {
10501050
terminal: {
10511051
local: 'ローカル',
10521052
localHelper: 'ローカル名はシステムのローカル識別にのみ使用されます。',
1053-
connLocalErr: '自動的に認証できない場合はローカルサーバーのログイン情報を入力してください。',
10541053
testConn: 'テスト接続',
10551054
saveAndConn: '保存して接続します',
10561055
connTestOk: '利用可能な接続情報',

frontend/src/lang/modules/ko.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1043,7 +1043,6 @@ const message = {
10431043
terminal: {
10441044
local: '로',
10451045
localHelper: '로 이름은 시스템 로컬 식별에만 사용됩니다.',
1046-
connLocalErr: '자 인증에 실패했습니다. 로컬 서버 로그인 정보를 입력해주세요.',
10471046
testConn: '연 테스트',
10481047
saveAndConn: '저 연결',
10491048
connTestOk: '연 정보가 유효합니다.',

frontend/src/lang/modules/ms.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1079,7 +1079,6 @@ const message = {
10791079
terminal: {
10801080
local: 'Tempatan',
10811081
localHelper: 'Nama tempatan hanya digunakan untuk pengenalan sistem tempatan.',
1082-
connLocalErr: 'Tidak dapat mengesahkan secara automatik, sila isi maklumat log masuk pelayan tempatan.',
10831082
testConn: 'Uji sambungan',
10841083
saveAndConn: 'Simpan dan sambung',
10851084
connTestOk: 'Maklumat sambungan tersedia',

frontend/src/lang/modules/pt-br.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1069,8 +1069,6 @@ const message = {
10691069
terminal: {
10701070
local: 'Local',
10711071
localHelper: 'O nome local é usado apenas para identificação local do sistema.',
1072-
connLocalErr:
1073-
'Não foi possível autenticar automaticamente, por favor, preencha as informações de login do servidor local.',
10741072
testConn: 'Testar conexão',
10751073
saveAndConn: 'Salvar e conectar',
10761074
connTestOk: 'Informações de conexão disponíveis',

frontend/src/lang/modules/ru.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1074,8 +1074,6 @@ const message = {
10741074
terminal: {
10751075
local: 'Локальный',
10761076
localHelper: 'Локальное имя используется только для локальной идентификации системы.',
1077-
connLocalErr:
1078-
'Невозможно автоматически аутентифицироваться, пожалуйста, заполните информацию для входа на локальный сервер.',
10791077
testConn: 'Проверить подключение',
10801078
saveAndConn: 'Сохранить и подключиться',
10811079
connTestOk: 'Информация о подключении доступна',

0 commit comments

Comments
 (0)