mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-12-27 09:49:28 +08:00
parent
9c22005b79
commit
a2a6503030
20 changed files with 327 additions and 39 deletions
|
|
@ -1,13 +1,12 @@
|
|||
package v2
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"os/user"
|
||||
"path"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/ssh"
|
||||
"github.com/gin-gonic/gin"
|
||||
|
|
@ -71,6 +70,35 @@ func (b *BaseApi) LoadBaseDir(c *gin.Context) {
|
|||
helper.SuccessWithData(c, global.Dir.DataDir)
|
||||
}
|
||||
|
||||
// @Tags System Setting
|
||||
// @Summary Load local conn
|
||||
// @Success 200 {object} dto.SSHConnData
|
||||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /settings/ssh [get]
|
||||
func (b *BaseApi) LoadLocalConn(c *gin.Context) {
|
||||
connInfoInDB, err := settingService.GetSSHInfo()
|
||||
if err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
var data dto.SSHConnData
|
||||
if err := json.Unmarshal([]byte(connInfoInDB), &data); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
if len(data.Password) != 0 {
|
||||
data.Password = base64.StdEncoding.EncodeToString([]byte(data.Password))
|
||||
}
|
||||
if len(data.PrivateKey) != 0 {
|
||||
data.PrivateKey = base64.StdEncoding.EncodeToString([]byte(data.PrivateKey))
|
||||
}
|
||||
if len(data.PassPhrase) != 0 {
|
||||
data.PassPhrase = base64.StdEncoding.EncodeToString([]byte(data.PassPhrase))
|
||||
}
|
||||
helper.SuccessWithData(c, data)
|
||||
}
|
||||
|
||||
func (b *BaseApi) CheckLocalConn(c *gin.Context) {
|
||||
_, err := loadLocalConn()
|
||||
helper.SuccessWithData(c, err == nil)
|
||||
|
|
@ -87,6 +115,7 @@ func (b *BaseApi) CheckLocalConnByInfo(c *gin.Context) {
|
|||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
helper.SuccessWithData(c, settingService.TestConnByInfo(req))
|
||||
}
|
||||
|
||||
|
|
@ -96,39 +125,20 @@ func (b *BaseApi) CheckLocalConnByInfo(c *gin.Context) {
|
|||
// @Security ApiKeyAuth
|
||||
// @Security Timestamp
|
||||
// @Router /settings/ssh [post]
|
||||
func (b *BaseApi) SaveLocalConnInfo(c *gin.Context) {
|
||||
func (b *BaseApi) SaveLocalConn(c *gin.Context) {
|
||||
var req dto.SSHConnData
|
||||
if err := helper.CheckBindAndValidate(&req, c); err != nil {
|
||||
return
|
||||
}
|
||||
helper.SuccessWithData(c, settingService.SaveConnInfo(req))
|
||||
|
||||
if err := settingService.SaveConnInfo(req); err != nil {
|
||||
helper.InternalServer(c, err)
|
||||
return
|
||||
}
|
||||
helper.Success(c)
|
||||
}
|
||||
|
||||
func loadLocalConn() (*ssh.SSHClient, error) {
|
||||
itemPath := ""
|
||||
currentInfo, _ := user.Current()
|
||||
if len(currentInfo.HomeDir) == 0 {
|
||||
itemPath = "/root/.ssh/id_ed25519_1panel"
|
||||
} else {
|
||||
itemPath = path.Join(currentInfo.HomeDir, ".ssh/id_ed25519_1panel")
|
||||
}
|
||||
if _, err := os.Stat(itemPath); err != nil {
|
||||
_ = sshService.CreateRootCert(dto.CreateRootCert{EncryptionMode: "ed25519", Name: "id_ed25519_1panel", Description: "1Panel Terminal"})
|
||||
}
|
||||
|
||||
privateKey, _ := os.ReadFile(itemPath)
|
||||
connWithKey := ssh.ConnInfo{
|
||||
Addr: "127.0.0.1",
|
||||
User: "root",
|
||||
Port: 22,
|
||||
AuthMode: "key",
|
||||
PrivateKey: privateKey,
|
||||
}
|
||||
client, err := ssh.NewClient(connWithKey)
|
||||
if err == nil {
|
||||
return client, nil
|
||||
}
|
||||
|
||||
connInfoInDB, err := settingService.GetSSHInfo()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -136,11 +146,20 @@ func loadLocalConn() (*ssh.SSHClient, error) {
|
|||
if len(connInfoInDB) == 0 {
|
||||
return nil, errors.New("no such ssh conn info in db!")
|
||||
}
|
||||
var connInDB ssh.ConnInfo
|
||||
var connInDB model.LocalConnInfo
|
||||
if err := json.Unmarshal([]byte(connInfoInDB), &connInDB); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ssh.NewClient(connInDB)
|
||||
sshInfo := ssh.ConnInfo{
|
||||
Addr: connInDB.Addr,
|
||||
Port: int(connInDB.Port),
|
||||
User: connInDB.User,
|
||||
AuthMode: connInDB.AuthMode,
|
||||
Password: connInDB.Password,
|
||||
PrivateKey: []byte(connInDB.PrivateKey),
|
||||
PassPhrase: []byte(connInDB.PassPhrase),
|
||||
}
|
||||
return ssh.NewClient(sshInfo)
|
||||
}
|
||||
|
||||
// @Tags System Setting
|
||||
|
|
|
|||
|
|
@ -15,3 +15,13 @@ type NodeInfo struct {
|
|||
ServerCrt string `json:"serverCrt"`
|
||||
ServerKey string `json:"serverKey"`
|
||||
}
|
||||
|
||||
type LocalConnInfo struct {
|
||||
Addr string `json:"addr"`
|
||||
Port uint `json:"port"`
|
||||
User string `json:"user"`
|
||||
AuthMode string `json:"authMode"`
|
||||
Password string `json:"password"`
|
||||
PrivateKey string `json:"privateKey"`
|
||||
PassPhrase string `json:"passPhrase"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -143,6 +143,13 @@ func (u *SettingService) GetSettingByKey(key string) string {
|
|||
case "SystemIP":
|
||||
value, _ := settingRepo.GetValueByKey(key)
|
||||
return value
|
||||
case "LocalSSHConn":
|
||||
value, _ := settingRepo.GetValueByKey(key)
|
||||
if len(value) == 0 {
|
||||
return ""
|
||||
}
|
||||
itemStr, _ := encrypt.StringDecryptWithBase64(value)
|
||||
return itemStr
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ func InitAgentDB() {
|
|||
migrations.UpdateWebsiteSSL,
|
||||
migrations.AddQuickJump,
|
||||
migrations.UpdateMcpServerAddType,
|
||||
migrations.InitLocalSSHConn,
|
||||
})
|
||||
if err := m.Migrate(); err != nil {
|
||||
global.LOG.Error(err)
|
||||
|
|
|
|||
|
|
@ -1,17 +1,23 @@
|
|||
package migrations
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/user"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/dto/request"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/model"
|
||||
"github.com/1Panel-dev/1Panel/agent/app/service"
|
||||
"github.com/1Panel-dev/1Panel/agent/constant"
|
||||
"github.com/1Panel-dev/1Panel/agent/global"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/common"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/copier"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/encrypt"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/ssh"
|
||||
"github.com/1Panel-dev/1Panel/agent/utils/xpack"
|
||||
|
||||
"github.com/go-gormigrate/gormigrate/v2"
|
||||
|
|
@ -510,3 +516,40 @@ var UpdateMcpServerAddType = &gormigrate.Migration{
|
|||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var InitLocalSSHConn = &gormigrate.Migration{
|
||||
ID: "20250905-init-local-ssh",
|
||||
Migrate: func(tx *gorm.DB) error {
|
||||
itemPath := ""
|
||||
currentInfo, _ := user.Current()
|
||||
if len(currentInfo.HomeDir) == 0 {
|
||||
itemPath = "/root/.ssh/id_ed25519_1panel"
|
||||
} else {
|
||||
itemPath = path.Join(currentInfo.HomeDir, ".ssh/id_ed25519_1panel")
|
||||
}
|
||||
if _, err := os.Stat(itemPath); err != nil {
|
||||
_ = service.NewISSHService().CreateRootCert(dto.CreateRootCert{EncryptionMode: "ed25519", Name: "id_ed25519_1panel", Description: "1Panel Terminal"})
|
||||
}
|
||||
privateKey, _ := os.ReadFile(itemPath)
|
||||
connWithKey := ssh.ConnInfo{
|
||||
Addr: "127.0.0.1",
|
||||
User: "root",
|
||||
Port: 22,
|
||||
AuthMode: "key",
|
||||
PrivateKey: privateKey,
|
||||
}
|
||||
if _, err := ssh.NewClient(connWithKey); err != nil {
|
||||
return nil
|
||||
}
|
||||
var conn model.LocalConnInfo
|
||||
_ = copier.Copy(&conn, &connWithKey)
|
||||
conn.PrivateKey = string(privateKey)
|
||||
conn.PassPhrase = ""
|
||||
localConn, _ := json.Marshal(&conn)
|
||||
connAfterEncrypt, _ := encrypt.StringEncrypt(string(localConn))
|
||||
if err := tx.Model(&model.Setting{}).Where("key = ?", "LocalSSHConn").Updates(map[string]interface{}{"value": connAfterEncrypt}).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,8 @@ func (s *SettingRouter) InitRouter(Router *gin.RouterGroup) {
|
|||
settingRouter.GET("/basedir", baseApi.LoadBaseDir)
|
||||
|
||||
settingRouter.POST("/ssh/check", baseApi.CheckLocalConn)
|
||||
settingRouter.POST("/ssh", baseApi.SaveLocalConnInfo)
|
||||
settingRouter.GET("/ssh/conn", baseApi.LoadLocalConn)
|
||||
settingRouter.POST("/ssh", baseApi.SaveLocalConn)
|
||||
settingRouter.POST("/ssh/check/info", baseApi.CheckLocalConnByInfo)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ export namespace Host {
|
|||
user: string;
|
||||
authMode: string;
|
||||
privateKey: string;
|
||||
passPhrase: string;
|
||||
password: string;
|
||||
}
|
||||
export interface GroupChange {
|
||||
|
|
|
|||
|
|
@ -60,6 +60,9 @@ export const deleteHost = (params: { ids: number[] }) => {
|
|||
};
|
||||
|
||||
// agent
|
||||
export const loadLocalConn = () => {
|
||||
return http.get<Host.HostConnTest>(`/settings/ssh/conn`);
|
||||
};
|
||||
export const testLocalConn = () => {
|
||||
return http.post<boolean>(`/settings/ssh/check`);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1165,6 +1165,8 @@ const message = {
|
|||
},
|
||||
terminal: {
|
||||
local: 'Local',
|
||||
defaultConn: 'Default Connection',
|
||||
defaultConnHelper: 'Whether to connect to the host by default after opening the terminal',
|
||||
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',
|
||||
|
|
|
|||
|
|
@ -1126,6 +1126,8 @@ const message = {
|
|||
},
|
||||
terminal: {
|
||||
local: 'ローカル',
|
||||
defaultConn: 'デフォルト接続',
|
||||
defaultConnHelper: 'ターミナルを開いた後にデフォルトでホストに接続するかどうか',
|
||||
localHelper: 'ローカル名はシステムのローカル識別にのみ使用されます。',
|
||||
connLocalErr: '自動的に認証できない場合は、ローカルサーバーのログイン情報を入力してください。',
|
||||
testConn: 'テスト接続',
|
||||
|
|
|
|||
|
|
@ -1118,6 +1118,8 @@ const message = {
|
|||
},
|
||||
terminal: {
|
||||
local: '로컬',
|
||||
defaultConn: '기본 연결',
|
||||
defaultConnHelper: '터미널을 연 후 기본적으로 호스트에 연결할지 여부',
|
||||
localHelper: '로컬 이름은 시스템 로컬 식별에만 사용됩니다.',
|
||||
connLocalErr: '자동 인증에 실패했습니다. 로컬 서버 로그인 정보를 입력해주세요.',
|
||||
testConn: '연결 테스트',
|
||||
|
|
|
|||
|
|
@ -1154,6 +1154,8 @@ const message = {
|
|||
},
|
||||
terminal: {
|
||||
local: 'Tempatan',
|
||||
defaultConn: 'Sambungan Lalai',
|
||||
defaultConnHelper: 'Sama ada untuk menyambung ke hos secara lalai selepas membuka terminal',
|
||||
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',
|
||||
|
|
|
|||
|
|
@ -1146,6 +1146,8 @@ const message = {
|
|||
},
|
||||
terminal: {
|
||||
local: 'Local',
|
||||
defaultConn: 'Conexão Padrão',
|
||||
defaultConnHelper: 'Se deve conectar ao host por padrão após abrir o terminal',
|
||||
localHelper: 'O nome local é usado apenas para identificação local do sistema.',
|
||||
connLocalErr:
|
||||
'Невозможно автоматически аутентифицироваться, пожалуйста, заполните информацию для входа на локальный сервер.',
|
||||
|
|
|
|||
|
|
@ -1149,8 +1149,11 @@ const message = {
|
|||
},
|
||||
terminal: {
|
||||
local: 'Локальный',
|
||||
defaultConn: 'Соединение по умолчанию',
|
||||
defaultConnHelper: 'Подключаться ли к хосту по умолчанию после открытия терминала',
|
||||
localHelper: 'Локальное имя используется только для локальной идентификации системы.',
|
||||
connLocalErr: '無法自動認證,請填寫本地服務器的登錄信息!',
|
||||
connLocalErr:
|
||||
'Невозможно выполнить автоматическую аутентификацию, пожалуйста, заполните информацию для входа на локальный сервер!',
|
||||
testConn: 'Проверить подключение',
|
||||
saveAndConn: 'Сохранить и подключиться',
|
||||
connTestOk: 'Информация о подключении доступна',
|
||||
|
|
|
|||
|
|
@ -1178,6 +1178,8 @@ const message = {
|
|||
},
|
||||
terminal: {
|
||||
local: 'Yerel',
|
||||
defaultConn: 'Varsayılan Bağlantı',
|
||||
defaultConnHelper: 'Terminal açıldıktan sonra varsayılan olarak ana bilgisayara bağlanılsın mı',
|
||||
localHelper: '`local` adı sadece sistem yerel tanımlaması için kullanılır',
|
||||
connLocalErr: 'Otomatik kimlik doğrulama yapılamıyor, lütfen yerel sunucu giriş bilgilerini doldurun.',
|
||||
testConn: 'Bağlantıyı test et',
|
||||
|
|
|
|||
|
|
@ -1110,6 +1110,8 @@ const message = {
|
|||
},
|
||||
terminal: {
|
||||
local: '本機',
|
||||
defaultConn: '預設連接',
|
||||
defaultConnHelper: '開啟終端後是否預設連線到主機',
|
||||
localHelper: 'local 名稱僅用於系統本機標識',
|
||||
connLocalErr: '無法自動認證,請填寫本地服務器的登錄信息!',
|
||||
testConn: '連接測試',
|
||||
|
|
|
|||
|
|
@ -1110,6 +1110,8 @@ const message = {
|
|||
},
|
||||
terminal: {
|
||||
local: '本机',
|
||||
defaultConn: '默认连接',
|
||||
defaultConnHelper: '打开终端后是否默认连接主机',
|
||||
localHelper: 'local 名称仅用于系统本机标识',
|
||||
connLocalErr: '无法自动认证,请填写本地服务器的登录信息!',
|
||||
testConn: '连接测试',
|
||||
|
|
|
|||
146
frontend/src/views/terminal/setting/default_conn/index.vue
Normal file
146
frontend/src/views/terminal/setting/default_conn/index.vue
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
<template>
|
||||
<DrawerPro v-model="drawerVisible" :header="$t('terminal.defaultConn')" @close="handleClose" size="large">
|
||||
<el-form ref="formRef" label-position="top" :model="form" :rules="rules" v-loading="loading">
|
||||
<el-form-item :label="$t('terminal.ip')" prop="addr">
|
||||
<el-input @change="isOK = false" clearable v-model.trim="form.addr" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('commons.login.username')" prop="user">
|
||||
<el-input @change="isOK = false" clearable v-model="form.user" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('terminal.authMode')" prop="authMode">
|
||||
<el-radio-group @change="isOK = false" v-model="form.authMode">
|
||||
<el-radio value="password">{{ $t('terminal.passwordMode') }}</el-radio>
|
||||
<el-radio value="key">{{ $t('terminal.keyMode') }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('commons.login.password')" v-if="form.authMode === 'password'" prop="password">
|
||||
<el-input @change="isOK = false" clearable show-password type="password" v-model="form.password" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('terminal.key')" v-if="form.authMode === 'key'" prop="privateKey">
|
||||
<el-input @change="isOK = false" clearable type="textarea" v-model="form.privateKey" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('terminal.keyPassword')" v-if="form.authMode === 'key'" prop="passPhrase">
|
||||
<el-input @change="isOK = false" type="password" show-password clearable v-model="form.passPhrase" />
|
||||
</el-form-item>
|
||||
<el-form-item style="margin-top: 10px" :label="$t('commons.table.port')" prop="port">
|
||||
<el-input @change="isOK = false" clearable v-model.number="form.port" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="handleClose">{{ $t('commons.button.cancel') }}</el-button>
|
||||
<el-button @click="onTest(formRef)">
|
||||
{{ $t('terminal.testConn') }}
|
||||
</el-button>
|
||||
<el-button type="primary" :disabled="!isOK" @click="onSave(formRef)">
|
||||
{{ $t('commons.button.confirm') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</DrawerPro>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive } from 'vue';
|
||||
import type { ElForm } from 'element-plus';
|
||||
import { Rules } from '@/global/form-rules';
|
||||
import { addHost, loadLocalConn, testByInfo } from '@/api/modules/terminal';
|
||||
import i18n from '@/lang';
|
||||
import { MsgError, MsgSuccess } from '@/utils/message';
|
||||
|
||||
const loading = ref();
|
||||
const isOK = ref(false);
|
||||
const drawerVisible = ref(false);
|
||||
|
||||
const emit = defineEmits<{ (e: 'search'): void }>();
|
||||
|
||||
type FormInstance = InstanceType<typeof ElForm>;
|
||||
const formRef = ref<FormInstance>();
|
||||
const form = reactive({
|
||||
user: 'root',
|
||||
addr: '127.0.0.1',
|
||||
port: 22,
|
||||
authMode: 'password',
|
||||
password: '',
|
||||
privateKey: '',
|
||||
passPhrase: '',
|
||||
isLocal: true,
|
||||
|
||||
id: 0,
|
||||
name: '',
|
||||
groupID: 0,
|
||||
description: '',
|
||||
rememberPassword: false,
|
||||
});
|
||||
const rules = reactive({
|
||||
addr: [Rules.ipV4V6OrDomain],
|
||||
user: [Rules.requiredInput],
|
||||
port: [Rules.requiredInput, Rules.port],
|
||||
authMode: [Rules.requiredSelect],
|
||||
password: [Rules.requiredInput],
|
||||
privateKey: [Rules.requiredInput],
|
||||
});
|
||||
|
||||
const acceptParams = (init: boolean): void => {
|
||||
if (!init) {
|
||||
search();
|
||||
}
|
||||
drawerVisible.value = true;
|
||||
};
|
||||
|
||||
const search = async () => {
|
||||
await loadLocalConn().then((res) => {
|
||||
form.addr = res.data.addr;
|
||||
form.port = res.data.port;
|
||||
form.authMode = res.data.authMode;
|
||||
form.password = res.data.password;
|
||||
form.privateKey = res.data.privateKey;
|
||||
form.passPhrase = res.data.passPhrase;
|
||||
});
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
emit('search');
|
||||
drawerVisible.value = false;
|
||||
};
|
||||
|
||||
const onSave = (formEl: FormInstance) => {
|
||||
if (!formEl) return;
|
||||
formEl.validate(async (valid) => {
|
||||
if (!valid) return;
|
||||
loading.value = true;
|
||||
await addHost(form)
|
||||
.then(() => {
|
||||
loading.value = false;
|
||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||
drawerVisible.value = false;
|
||||
emit('search');
|
||||
})
|
||||
.catch(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const onTest = (formEl: FormInstance) => {
|
||||
if (!formEl) return;
|
||||
formEl.validate(async (valid) => {
|
||||
if (!valid) return;
|
||||
loading.value = true;
|
||||
await testByInfo(form).then((res) => {
|
||||
loading.value = false;
|
||||
if (res.data) {
|
||||
isOK.value = true;
|
||||
MsgSuccess(i18n.global.t('terminal.connTestOk'));
|
||||
} else {
|
||||
isOK.value = false;
|
||||
MsgError(i18n.global.t('terminal.connTestFailed'));
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
acceptParams,
|
||||
});
|
||||
</script>
|
||||
|
|
@ -78,6 +78,18 @@
|
|||
@change="changeItem()"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('terminal.defaultConn')">
|
||||
<el-switch v-model="form.showDefaultConn" @change="changeShow" />
|
||||
<span class="input-help">{{ $t('terminal.defaultConnHelper') }}</span>
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="dialogRef.acceptParams(false)"
|
||||
v-if="form.showDefaultConn"
|
||||
link
|
||||
>
|
||||
{{ $t('commons.button.view') }}
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="onSetDefault()" plain>
|
||||
{{ $t('commons.button.setDefault') }}
|
||||
|
|
@ -90,14 +102,16 @@
|
|||
</el-form>
|
||||
</template>
|
||||
</LayoutContent>
|
||||
<OperateDialog @search="loadConnShow" ref="dialogRef" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive } from 'vue';
|
||||
import { ElForm } from 'element-plus';
|
||||
import { getTerminalInfo, UpdateTerminalInfo } from '@/api/modules/setting';
|
||||
import { getAgentSettingByKey, getTerminalInfo, updateAgentSetting, UpdateTerminalInfo } from '@/api/modules/setting';
|
||||
import { Terminal } from '@xterm/xterm';
|
||||
import OperateDialog from '@/views/terminal/setting/default_conn/index.vue';
|
||||
import '@xterm/xterm/css/xterm.css';
|
||||
import { FitAddon } from '@xterm/addon-fit';
|
||||
import i18n from '@/lang';
|
||||
|
|
@ -106,6 +120,7 @@ import { TerminalStore } from '@/store';
|
|||
|
||||
const loading = ref(false);
|
||||
const terminalStore = TerminalStore();
|
||||
const dialogRef = ref();
|
||||
|
||||
const terminalElement = ref<HTMLDivElement | null>(null);
|
||||
const fitAddon = new FitAddon();
|
||||
|
|
@ -119,10 +134,12 @@ const form = reactive({
|
|||
cursorStyle: 'underline',
|
||||
scrollback: 1000,
|
||||
scrollSensitivity: 10,
|
||||
showDefaultConn: false,
|
||||
});
|
||||
|
||||
const acceptParams = () => {
|
||||
search(true);
|
||||
loadConnShow();
|
||||
iniTerm();
|
||||
};
|
||||
|
||||
|
|
@ -148,6 +165,22 @@ const search = async (withReset?: boolean) => {
|
|||
});
|
||||
};
|
||||
|
||||
const loadConnShow = async () => {
|
||||
await getAgentSettingByKey('LocalSSHConn').then((res) => {
|
||||
form.showDefaultConn = res.data.length !== 0;
|
||||
});
|
||||
};
|
||||
|
||||
const changeShow = async () => {
|
||||
if (form.showDefaultConn) {
|
||||
dialogRef.value.acceptParams(true);
|
||||
return;
|
||||
}
|
||||
await updateAgentSetting({ key: 'LocalSSHConn', value: '' }).then(() => {
|
||||
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||
});
|
||||
};
|
||||
|
||||
const iniTerm = () => {
|
||||
term.value = new Terminal({
|
||||
lineHeight: 1.2,
|
||||
|
|
|
|||
|
|
@ -161,6 +161,7 @@ import { getHostTree, testByID, testLocalConn } from '@/api/modules/terminal';
|
|||
import { GlobalStore } from '@/store';
|
||||
import router from '@/routers';
|
||||
import { getCommandTree } from '@/api/modules/command';
|
||||
import { getAgentSettingByKey } from '@/api/modules/setting';
|
||||
|
||||
const dialogRef = ref();
|
||||
const ctx = getCurrentInstance() as any;
|
||||
|
|
@ -207,20 +208,24 @@ const initCmd = ref('');
|
|||
const acceptParams = async () => {
|
||||
globalStore.isFullScreen = false;
|
||||
loadCommandTree();
|
||||
const res = await getHostTree({});
|
||||
hostTree.value = res.data;
|
||||
loadHostTree();
|
||||
if (terminalTabs.value.length === 0) {
|
||||
await getAgentSettingByKey('LocalSSHConn').then((res) => {
|
||||
if (res.data.length !== 0) {
|
||||
onNewLocal();
|
||||
}
|
||||
});
|
||||
}
|
||||
timer = setInterval(() => {
|
||||
syncTerminal();
|
||||
}, 1000 * 5);
|
||||
if (terminalTabs.value.length === 0) {
|
||||
onNewLocal();
|
||||
}
|
||||
if (!mobile.value) {
|
||||
screenfull.on('change', () => {
|
||||
globalStore.isFullScreen = screenfull.isFullscreen;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const cleanTimer = () => {
|
||||
clearInterval(Number(timer));
|
||||
timer = null;
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue