From 9ffc601cf067ec3f8504dee11eee47e078dbe0a0 Mon Sep 17 00:00:00 2001
From: ssongliu <73214554+ssongliu@users.noreply.github.com>
Date: Wed, 16 Apr 2025 17:37:07 +0800
Subject: [PATCH] feat: Change the local connection mode of the terminal
(#8409)
---
agent/app/api/v2/terminal.go | 105 ++++++++++++++----
agent/router/ro_host.go | 2 +
agent/utils/terminal/local_cmd.go | 11 +-
core/app/service/host.go | 25 +----
frontend/src/lang/modules/en.ts | 1 -
frontend/src/lang/modules/ja.ts | 1 -
frontend/src/lang/modules/ko.ts | 1 -
frontend/src/lang/modules/ms.ts | 1 -
frontend/src/lang/modules/pt-br.ts | 2 -
frontend/src/lang/modules/ru.ts | 2 -
frontend/src/lang/modules/zh-Hant.ts | 1 -
frontend/src/lang/modules/zh.ts | 1 -
frontend/src/views/terminal/host/index.vue | 8 +-
.../src/views/terminal/host/operate/index.vue | 12 +-
.../views/terminal/terminal/host-create.vue | 61 +---------
.../src/views/terminal/terminal/index.vue | 75 ++++++-------
16 files changed, 138 insertions(+), 171 deletions(-)
diff --git a/agent/app/api/v2/terminal.go b/agent/app/api/v2/terminal.go
index 864b21224..2d71d4a95 100644
--- a/agent/app/api/v2/terminal.go
+++ b/agent/app/api/v2/terminal.go
@@ -18,6 +18,55 @@ import (
"github.com/pkg/errors"
)
+func (b *BaseApi) WsSSH(c *gin.Context) {
+ 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()
+
+ if global.CONF.Base.IsDemo {
+ if wshandleError(wsConn, errors.New(" demo server, prohibit this operation!")) {
+ return
+ }
+ }
+
+ cols, err := strconv.Atoi(c.DefaultQuery("cols", "80"))
+ if wshandleError(wsConn, errors.WithMessage(err, "invalid param cols in request")) {
+ return
+ }
+ rows, err := strconv.Atoi(c.DefaultQuery("rows", "40"))
+ if wshandleError(wsConn, errors.WithMessage(err, "invalid param rows in request")) {
+ return
+ }
+ name, err := loadExecutor()
+ if wshandleError(wsConn, err) {
+ return
+ }
+ slave, err := terminal.NewCommand(name)
+ if wshandleError(wsConn, err) {
+ return
+ }
+ defer slave.Close()
+
+ tty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave, false)
+ 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 (b *BaseApi) ContainerWsSSH(c *gin.Context) {
wsConn, err := upGrader.Upgrade(c.Writer, c.Request, nil)
if err != nil {
@@ -42,7 +91,7 @@ func (b *BaseApi) ContainerWsSSH(c *gin.Context) {
}
source := c.Query("source")
var containerID string
- var initCmd string
+ var initCmd []string
switch source {
case "redis":
containerID, initCmd, err = loadRedisInitCmd(c)
@@ -59,11 +108,11 @@ func (b *BaseApi) ContainerWsSSH(c *gin.Context) {
return
}
pidMap := loadMapFromDockerTop(containerID)
- slave, err := terminal.NewCommand("clear && " + initCmd)
+ slave, err := terminal.NewCommand("docker", initCmd...)
if wshandleError(wsConn, err) {
return
}
- defer killBash(containerID, strings.ReplaceAll(initCmd, fmt.Sprintf("docker exec -it %s ", containerID), ""), pidMap)
+ defer killBash(containerID, strings.ReplaceAll(strings.Join(initCmd, " "), fmt.Sprintf("exec -it %s ", containerID), ""), pidMap)
defer slave.Close()
tty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave, false)
@@ -83,62 +132,63 @@ func (b *BaseApi) ContainerWsSSH(c *gin.Context) {
}
}
-func loadRedisInitCmd(c *gin.Context) (string, string, error) {
+func loadRedisInitCmd(c *gin.Context) (string, []string, error) {
name := c.Query("name")
from := c.Query("from")
- commands := "redis-cli"
+ commands := []string{"exec", "-it"}
database, err := databaseService.Get(name)
if err != nil {
- return "", "", fmt.Errorf("no such database in db, err: %v", err)
+ return "", nil, fmt.Errorf("no such database in db, err: %v", err)
}
if from == "local" {
redisInfo, err := appInstallService.LoadConnInfo(dto.OperationWithNameAndType{Name: name, Type: "redis"})
if err != nil {
- return "", "", fmt.Errorf("no such app in db, err: %v", err)
+ return "", nil, fmt.Errorf("no such app in db, err: %v", err)
}
name = redisInfo.ContainerName
+ commands = append(commands, []string{name, "redis-cli"}...)
if len(database.Password) != 0 {
- commands = "redis-cli -a " + database.Password + " --no-auth-warning"
+ commands = append(commands, []string{"-a", database.Password, "--no-auth-warning"}...)
}
} else {
- commands = fmt.Sprintf("redis-cli -h %s -p %v", database.Address, database.Port)
- if len(database.Password) != 0 {
- commands = fmt.Sprintf("redis-cli -h %s -p %v -a %s --no-auth-warning", database.Address, database.Port, database.Password)
- }
name = "1Panel-redis-cli-tools"
+ commands = append(commands, []string{name, "redis-cli", "-h", database.Address, "-p", fmt.Sprintf("%v", database.Port)}...)
+ if len(database.Password) != 0 {
+ commands = append(commands, []string{"-a", database.Password, "--no-auth-warning"}...)
+ }
}
- return name, fmt.Sprintf("docker exec -it %s %s", name, commands), nil
+ return name, commands, nil
}
-func loadOllamaInitCmd(c *gin.Context) (string, string, error) {
+func loadOllamaInitCmd(c *gin.Context) (string, []string, error) {
name := c.Query("name")
if cmd.CheckIllegal(name) {
- return "", "", fmt.Errorf("ollama model %s contains illegal characters", name)
+ return "", nil, fmt.Errorf("ollama model %s contains illegal characters", name)
}
ollamaInfo, err := appInstallService.LoadConnInfo(dto.OperationWithNameAndType{Name: "", Type: "ollama"})
if err != nil {
- return "", "", fmt.Errorf("no such app in db, err: %v", err)
+ return "", nil, fmt.Errorf("no such app in db, err: %v", err)
}
containerName := ollamaInfo.ContainerName
- return containerName, fmt.Sprintf("docker exec -it %s ollama run %s", containerName, name), nil
+ return containerName, []string{"exec", "-it", containerName, "ollama", "run", name}, nil
}
-func loadContainerInitCmd(c *gin.Context) (string, string, error) {
+func loadContainerInitCmd(c *gin.Context) (string, []string, error) {
containerID := c.Query("containerid")
command := c.Query("command")
user := c.Query("user")
if cmd.CheckIllegal(user, containerID, command) {
- return "", "", fmt.Errorf("the command contains illegal characters. command: %s, user: %s, containerID: %s", command, user, containerID)
+ return "", nil, fmt.Errorf("the command contains illegal characters. command: %s, user: %s, containerID: %s", command, user, containerID)
}
if len(command) == 0 || len(containerID) == 0 {
- return "", "", fmt.Errorf("error param of command: %s or containerID: %s", command, containerID)
+ return "", nil, fmt.Errorf("error param of command: %s or containerID: %s", command, containerID)
}
- command = fmt.Sprintf("docker exec -it %s %s", containerID, command)
+ commands := []string{"exec", "-it", containerID, command}
if len(user) != 0 {
- command = fmt.Sprintf("docker exec -it -u %s %s %s", user, containerID, command)
+ commands = []string{"exec", "-it", "-u", user, containerID, command}
}
- return containerID, command, nil
+ return containerID, commands, nil
}
func wshandleError(ws *websocket.Conn, err error) bool {
@@ -204,3 +254,12 @@ var upGrader = websocket.Upgrader{
return true
},
}
+
+func loadExecutor() (string, error) {
+ std, err := cmd.RunDefaultWithStdoutBashC("echo $SHELL")
+ if err != nil {
+ return "", fmt.Errorf("load default executor failed, err: %s", std)
+ }
+
+ return strings.ReplaceAll(std, "\n", ""), nil
+}
diff --git a/agent/router/ro_host.go b/agent/router/ro_host.go
index 4e599c716..d155bd853 100644
--- a/agent/router/ro_host.go
+++ b/agent/router/ro_host.go
@@ -46,5 +46,7 @@ func (s *HostRouter) InitRouter(Router *gin.RouterGroup) {
hostRouter.POST("/tool/supervisor/process", baseApi.OperateProcess)
hostRouter.GET("/tool/supervisor/process", baseApi.GetProcess)
hostRouter.POST("/tool/supervisor/process/file", baseApi.GetProcessFile)
+
+ hostRouter.GET("/exec", baseApi.WsSSH)
}
}
diff --git a/agent/utils/terminal/local_cmd.go b/agent/utils/terminal/local_cmd.go
index 20c5c07c9..8551ca560 100644
--- a/agent/utils/terminal/local_cmd.go
+++ b/agent/utils/terminal/local_cmd.go
@@ -25,22 +25,20 @@ type LocalCommand struct {
pty *os.File
}
-func NewCommand(initCmd string) (*LocalCommand, error) {
- cmd := exec.Command("bash")
+func NewCommand(name string, arg ...string) (*LocalCommand, error) {
+ cmd := exec.Command(name, arg...)
if term := os.Getenv("TERM"); term != "" {
cmd.Env = append(os.Environ(), "TERM="+term)
} else {
cmd.Env = append(os.Environ(), "TERM=xterm")
}
+ homeDir, _ := os.UserHomeDir()
+ cmd.Dir = homeDir
pty, err := pty.Start(cmd)
if err != nil {
return nil, errors.Wrapf(err, "failed to start command")
}
- if len(initCmd) != 0 {
- time.Sleep(100 * time.Millisecond)
- _, _ = pty.Write([]byte(initCmd + "\n"))
- }
lcmd := &LocalCommand{
closeSignal: DefaultCloseSignal,
@@ -99,4 +97,5 @@ func (lcmd *LocalCommand) Wait(quitChan chan bool) {
global.LOG.Errorf("ssh session wait failed, err: %v", err)
setQuit(quitChan)
}
+ setQuit(quitChan)
}
diff --git a/core/app/service/host.go b/core/app/service/host.go
index 1dfc63758..ec2c4fc9e 100644
--- a/core/app/service/host.go
+++ b/core/app/service/host.go
@@ -79,16 +79,9 @@ func (u *HostService) TestLocalConn(id uint) bool {
host model.Host
err error
)
- if id == 0 {
- host, err = hostRepo.Get(hostRepo.WithByAddr("127.0.0.1"))
- if err != nil {
- return false
- }
- } else {
- host, err = hostRepo.Get(repo.WithByID(id))
- if err != nil {
- return false
- }
+ host, err = hostRepo.Get(repo.WithByID(id))
+ if err != nil {
+ return false
}
var connInfo ssh.ConnInfo
if err := copier.Copy(&connInfo, &host); err != nil {
@@ -206,11 +199,7 @@ func (u *HostService) SearchForTree(search dto.SearchForTree) ([]dto.HostTree, e
func (u *HostService) GetHostByID(id uint) (*dto.HostInfo, error) {
var item dto.HostInfo
var host model.Host
- if id == 0 {
- host, _ = hostRepo.Get(repo.WithByName("local"))
- } else {
- host, _ = hostRepo.Get(repo.WithByID(id))
- }
+ host, _ = hostRepo.Get(repo.WithByID(id))
if host.ID == 0 {
return nil, buserr.New("ErrRecordNotFound")
}
@@ -246,9 +235,6 @@ func (u *HostService) GetHostByID(id uint) (*dto.HostInfo, error) {
}
func (u *HostService) Create(req dto.HostOperate) (*dto.HostInfo, error) {
- if req.Name == "local" {
- return nil, buserr.New("ErrRecordExist")
- }
hostItem, _ := hostRepo.Get(hostRepo.WithByAddr(req.Addr), hostRepo.WithByUser(req.User), hostRepo.WithByPort(req.Port))
if hostItem.ID != 0 {
return nil, buserr.New("ErrRecordExist")
@@ -305,9 +291,6 @@ func (u *HostService) Delete(ids []uint) error {
if host.ID == 0 {
return buserr.New("ErrRecordNotFound")
}
- if host.Name == "local" {
- return errors.New("the local connection information cannot be deleted!")
- }
if err := hostRepo.Delete(repo.WithByID(id)); err != nil {
return err
}
diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts
index 64dd7e2e0..cf6a75dfb 100644
--- a/frontend/src/lang/modules/en.ts
+++ b/frontend/src/lang/modules/en.ts
@@ -1092,7 +1092,6 @@ const message = {
terminal: {
local: 'Local',
localHelper: 'The `local` name is used only for system local identification',
- connLocalErr: 'Unable to automatically authenticate, please fill in the local server login information!',
testConn: 'Test connection',
saveAndConn: 'Save and Connect',
connTestOk: 'Connection information available',
diff --git a/frontend/src/lang/modules/ja.ts b/frontend/src/lang/modules/ja.ts
index 5f82837f1..6470537c5 100644
--- a/frontend/src/lang/modules/ja.ts
+++ b/frontend/src/lang/modules/ja.ts
@@ -1050,7 +1050,6 @@ const message = {
terminal: {
local: 'ローカル',
localHelper: 'ローカル名はシステムのローカル識別にのみ使用されます。',
- connLocalErr: '自動的に認証できない場合は、ローカルサーバーのログイン情報を入力してください。',
testConn: 'テスト接続',
saveAndConn: '保存して接続します',
connTestOk: '利用可能な接続情報',
diff --git a/frontend/src/lang/modules/ko.ts b/frontend/src/lang/modules/ko.ts
index 3529a6af3..58e870ca3 100644
--- a/frontend/src/lang/modules/ko.ts
+++ b/frontend/src/lang/modules/ko.ts
@@ -1043,7 +1043,6 @@ const message = {
terminal: {
local: '로컬',
localHelper: '로컬 이름은 시스템 로컬 식별에만 사용됩니다.',
- connLocalErr: '자동 인증에 실패했습니다. 로컬 서버 로그인 정보를 입력해주세요.',
testConn: '연결 테스트',
saveAndConn: '저장 후 연결',
connTestOk: '연결 정보가 유효합니다.',
diff --git a/frontend/src/lang/modules/ms.ts b/frontend/src/lang/modules/ms.ts
index 785453280..809de4a66 100644
--- a/frontend/src/lang/modules/ms.ts
+++ b/frontend/src/lang/modules/ms.ts
@@ -1079,7 +1079,6 @@ const message = {
terminal: {
local: 'Tempatan',
localHelper: 'Nama tempatan hanya digunakan untuk pengenalan sistem tempatan.',
- connLocalErr: 'Tidak dapat mengesahkan secara automatik, sila isi maklumat log masuk pelayan tempatan.',
testConn: 'Uji sambungan',
saveAndConn: 'Simpan dan sambung',
connTestOk: 'Maklumat sambungan tersedia',
diff --git a/frontend/src/lang/modules/pt-br.ts b/frontend/src/lang/modules/pt-br.ts
index b47e8d0d7..47ddf6c99 100644
--- a/frontend/src/lang/modules/pt-br.ts
+++ b/frontend/src/lang/modules/pt-br.ts
@@ -1069,8 +1069,6 @@ const message = {
terminal: {
local: 'Local',
localHelper: 'O nome local é usado apenas para identificação local do sistema.',
- connLocalErr:
- 'Não foi possível autenticar automaticamente, por favor, preencha as informações de login do servidor local.',
testConn: 'Testar conexão',
saveAndConn: 'Salvar e conectar',
connTestOk: 'Informações de conexão disponíveis',
diff --git a/frontend/src/lang/modules/ru.ts b/frontend/src/lang/modules/ru.ts
index e1affa5f9..fb938414c 100644
--- a/frontend/src/lang/modules/ru.ts
+++ b/frontend/src/lang/modules/ru.ts
@@ -1074,8 +1074,6 @@ const message = {
terminal: {
local: 'Локальный',
localHelper: 'Локальное имя используется только для локальной идентификации системы.',
- connLocalErr:
- 'Невозможно автоматически аутентифицироваться, пожалуйста, заполните информацию для входа на локальный сервер.',
testConn: 'Проверить подключение',
saveAndConn: 'Сохранить и подключиться',
connTestOk: 'Информация о подключении доступна',
diff --git a/frontend/src/lang/modules/zh-Hant.ts b/frontend/src/lang/modules/zh-Hant.ts
index c7d3337dd..65140d2ad 100644
--- a/frontend/src/lang/modules/zh-Hant.ts
+++ b/frontend/src/lang/modules/zh-Hant.ts
@@ -1040,7 +1040,6 @@ const message = {
terminal: {
local: '本機',
localHelper: 'local 名稱僅用於系統本機標識',
- connLocalErr: '無法自動認證,請填寫本地服務器的登錄信息!',
testConn: '連接測試',
saveAndConn: '保存並連接',
connTestOk: '連接信息可用',
diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts
index cb9445935..732acfe54 100644
--- a/frontend/src/lang/modules/zh.ts
+++ b/frontend/src/lang/modules/zh.ts
@@ -1038,7 +1038,6 @@ const message = {
terminal: {
local: '本机',
localHelper: 'local 名称仅用于系统本机标识',
- connLocalErr: '无法自动认证,请填写本地服务器的登录信息!',
testConn: '连接测试',
saveAndConn: '保存并连接',
connTestOk: '连接信息可用',
diff --git a/frontend/src/views/terminal/host/index.vue b/frontend/src/views/terminal/host/index.vue
index 44356943e..b0037f737 100644
--- a/frontend/src/views/terminal/host/index.vue
+++ b/frontend/src/views/terminal/host/index.vue
@@ -34,7 +34,7 @@
:data="data"
@search="search"
>
-
+
@@ -106,9 +106,6 @@ const acceptParams = () => {
search();
};
-function selectable(row) {
- return row.name !== 'local';
-}
const dialogRef = ref();
const onOpenDialog = async (
title: string,
@@ -174,9 +171,6 @@ const buttons = [
},
{
label: i18n.global.t('commons.button.delete'),
- disabled: (row: any) => {
- return row.name === 'local';
- },
click: (row: Host.Host) => {
onBatchDelete(row);
},
diff --git a/frontend/src/views/terminal/host/operate/index.vue b/frontend/src/views/terminal/host/operate/index.vue
index 09c26f500..96dbf0947 100644
--- a/frontend/src/views/terminal/host/operate/index.vue
+++ b/frontend/src/views/terminal/host/operate/index.vue
@@ -61,8 +61,7 @@
- local
-
+
@@ -103,12 +102,10 @@ const drawerVisible = ref(false);
const dialogData = ref({
title: '',
});
-const itemName = ref();
const groupList = ref();
const acceptParams = (params: DialogProps): void => {
dialogData.value = params;
- itemName.value = params.rowData.name;
title.value = i18n.global.t('commons.button.' + dialogData.value.title);
drawerVisible.value = true;
loadGroups();
@@ -126,14 +123,7 @@ const rules = reactive({
port: [Rules.requiredInput, Rules.port],
user: [Rules.requiredInput],
authMode: [Rules.requiredSelect],
- name: [{ validator: checkName, trigger: 'blur' }],
});
-function checkName(rule: any, value: any, callback: any) {
- if (value === 'local' && dialogData.value.title !== 'edit') {
- return callback(new Error(i18n.global.t('terminal.localHelper')));
- }
- callback();
-}
const loadGroups = async () => {
const res = await getGroupList('host');
diff --git a/frontend/src/views/terminal/terminal/host-create.vue b/frontend/src/views/terminal/terminal/host-create.vue
index 27bc4d9f2..60d941c10 100644
--- a/frontend/src/views/terminal/terminal/host-create.vue
+++ b/frontend/src/views/terminal/terminal/host-create.vue
@@ -1,14 +1,6 @@
-
@@ -55,8 +47,7 @@
- local
-
+
@@ -80,7 +71,7 @@
import { ElForm } from 'element-plus';
import { Host } from '@/api/interface/host';
import { Rules } from '@/global/form-rules';
-import { addHost, editHost, getHostByID, testByInfo } from '@/api/modules/terminal';
+import { addHost, testByInfo } from '@/api/modules/terminal';
import i18n from '@/lang';
import { reactive, ref } from 'vue';
import { MsgError, MsgSuccess } from '@/utils/message';
@@ -116,21 +107,9 @@ const rules = reactive({
authMode: [Rules.requiredSelect],
password: [Rules.requiredInput],
privateKey: [Rules.requiredInput],
- name: [{ validator: checkName, trigger: 'blur' }],
});
-function checkName(rule: any, value: any, callback: any) {
- if (value === 'local' && !isLocal.value) {
- return callback(new Error(i18n.global.t('terminal.localHelper')));
- }
- callback();
-}
-const isLocal = ref(false);
-interface DialogProps {
- isLocal: boolean;
-}
-const acceptParams = (props: DialogProps) => {
- isLocal.value = props.isLocal;
+const acceptParams = () => {
loadGroups();
dialogVisible.value = true;
};
@@ -150,29 +129,7 @@ const loadGroups = async () => {
break;
}
}
- if (isLocal.value) {
- loadLocal();
- } else {
- setDefault();
- }
-};
-const loadLocal = async () => {
- await getHostByID(0)
- .then((res) => {
- hostInfo.id = res.data.id || 0;
- hostInfo.addr = res.data.addr || '';
- hostInfo.name = 'local';
- hostInfo.groupID = res.data.groupID || defaultGroup.value;
- hostInfo.port = res.data.port || 22;
- hostInfo.user = res.data.user || 'root';
- hostInfo.authMode = res.data.authMode || 'password';
- hostInfo.password = res.data.password || '';
- hostInfo.privateKey = res.data.privateKey || '';
- hostInfo.description = res.data.description || '';
- })
- .catch(() => {
- setDefault();
- });
+ setDefault();
};
const setDefault = () => {
@@ -204,19 +161,13 @@ const submitAddHost = (formEl: FormInstance | undefined, ops: string) => {
});
break;
case 'saveAndConn':
- let res;
- if (hostInfo.id == 0) {
- res = await addHost(hostInfo);
- } else {
- res = await editHost(hostInfo);
- }
+ const res = await addHost(hostInfo);
dialogVisible.value = false;
let title = res.data.user + '@' + res.data.addr + ':' + res.data.port;
if (res.data.name.length !== 0) {
title = res.data.name + '-' + title;
}
- let isLocal = hostInfo.name === 'local';
- emit('on-conn-terminal', title, res.data.id, isLocal);
+ emit('on-conn-terminal', title, res.data.id);
emit('load-host-tree');
break;
}
diff --git a/frontend/src/views/terminal/terminal/index.vue b/frontend/src/views/terminal/terminal/index.vue
index 71254115a..ebd337470 100644
--- a/frontend/src/views/terminal/terminal/index.vue
+++ b/frontend/src/views/terminal/terminal/index.vue
@@ -103,7 +103,7 @@
>
- {{ $t('commons.table.default') }}
+ {{ $t('commons.table.default') }}
{{ node.label }}
@@ -173,8 +173,6 @@ const loadTooltip = () => {
return i18n.global.t('commons.button.' + (globalStore.isFullScreen ? 'quitFullscreen' : 'fullscreen'));
};
-const localHostID = ref();
-
let timer: NodeJS.Timer | null = null;
const terminalValue = ref();
const terminalTabs = ref([]) as any;
@@ -209,26 +207,7 @@ const acceptParams = async () => {
timer = setInterval(() => {
syncTerminal();
}, 1000 * 5);
- for (let gIndex = 0; gIndex < hostTree.value.length; gIndex++) {
- if (!hostTree.value[gIndex].children) {
- continue;
- }
- for (let i = 0; i < hostTree.value[gIndex].children.length; i++) {
- if (hostTree.value[gIndex].children[i].label.startsWith('local - ')) {
- localHostID.value = hostTree.value[gIndex].children[i].id;
- hostTree.value[gIndex].children.splice(i, 1);
- if (hostTree.value[gIndex].children.length === 0) {
- hostTree.value.splice(gIndex, 1);
- }
- if (terminalTabs.value.length !== 0) {
- return;
- }
- onNewLocal();
- return;
- }
- }
- }
-
+ onNewLocal();
if (!mobile.value) {
screenfull.on('change', () => {
globalStore.isFullScreen = screenfull.isFullscreen;
@@ -329,17 +308,34 @@ function beforeLeave(activeName: string) {
}
const onNewSsh = () => {
- dialogRef.value!.acceptParams({ isLocal: false });
+ dialogRef.value!.acceptParams();
};
const onNewLocal = () => {
- onConnTerminal(i18n.global.t('terminal.localhost'), localHostID.value, false);
+ terminalTabs.value.push({
+ index: tabIndex,
+ title: i18n.global.t('terminal.localhost'),
+ wsID: 0,
+ status: 'online',
+ latency: 0,
+ });
+ terminalValue.value = tabIndex;
+ nextTick(() => {
+ ctx.refs[`t-${terminalValue.value}`] &&
+ ctx.refs[`t-${terminalValue.value}`][0].acceptParams({
+ endpoint: '/api/v2/hosts/exec',
+ initCmd: initCmd.value,
+ error: '',
+ });
+ initCmd.value = '';
+ });
+ tabIndex++;
};
const onClickConn = (node: Node, data: Tree) => {
if (node.level === 1) {
return;
}
- onConnTerminal(node.label, data.id, false);
+ onConnTerminal(node.label, data.id);
};
const onReconnect = async (item: any) => {
@@ -347,6 +343,20 @@ const onReconnect = async (item: any) => {
ctx.refs[`t-${item.index}`] && ctx.refs[`t-${item.index}`][0].onClose();
}
item.Refresh = !item.Refresh;
+ if (item.wsID === 0) {
+ nextTick(() => {
+ ctx.refs[`t-${item.index}`] &&
+ ctx.refs[`t-${item.index}`][0].acceptParams({
+ endpoint: '/api/v2/hosts/exec',
+ initCmd: initCmd.value,
+ error: '',
+ });
+ initCmd.value = '';
+ });
+ syncTerminal();
+ return;
+ }
+
const res = await testByID(item.wsID);
nextTick(() => {
ctx.refs[`t-${item.index}`] &&
@@ -359,16 +369,8 @@ const onReconnect = async (item: any) => {
syncTerminal();
};
-const onConnTerminal = async (title: string, wsID: number, isLocal?: boolean) => {
+const onConnTerminal = async (title: string, wsID: number) => {
const res = await testByID(wsID);
- if (isLocal) {
- for (const tab of terminalTabs.value) {
- if (tab.title === i18n.global.t('terminal.localhost')) {
- onReconnect(tab);
- }
- }
- return;
- }
terminalTabs.value.push({
index: tabIndex,
title: title,
@@ -377,9 +379,6 @@ const onConnTerminal = async (title: string, wsID: number, isLocal?: boolean) =>
latency: 0,
});
terminalValue.value = tabIndex;
- if (!res.data && title === i18n.global.t('terminal.localhost')) {
- dialogRef.value!.acceptParams({ isLocal: true });
- }
nextTick(() => {
ctx.refs[`t-${terminalValue.value}`] &&
ctx.refs[`t-${terminalValue.value}`][0].acceptParams({