fix: Fix the issue of abnormal terminal settings (#10621)

This commit is contained in:
ssongliu 2025-10-13 21:29:14 +08:00 committed by GitHub
parent 597000ce52
commit dde40064d2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 152 additions and 90 deletions

View file

@ -1,7 +1,6 @@
package v2
import (
"encoding/base64"
"encoding/json"
"github.com/1Panel-dev/1Panel/agent/app/api/v2/helper"
@ -77,26 +76,7 @@ func (b *BaseApi) LoadBaseDir(c *gin.Context) {
// @Security Timestamp
// @Router /settings/ssh/conn [get]
func (b *BaseApi) LoadLocalConn(c *gin.Context) {
connInfoInDB := settingService.GetSettingByKey("LocalSSHConn")
if len(connInfoInDB) == 0 {
helper.Success(c)
return
}
var data dto.SSHConnData
if err := json.Unmarshal([]byte(connInfoInDB), &data); err != nil {
helper.Success(c)
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)
helper.SuccessWithData(c, settingService.GetLocalConn())
}
func (b *BaseApi) CheckLocalConn(c *gin.Context) {

View file

@ -69,6 +69,8 @@ type SSHConnData struct {
Password string `json:"password"`
PrivateKey string `json:"privateKey"`
PassPhrase string `json:"passPhrase"`
LocalSSHConnShow string `json:"localSSHConnShow"`
}
type SystemProxy struct {

View file

@ -8,7 +8,6 @@ import (
"github.com/1Panel-dev/1Panel/agent/app/dto"
"github.com/1Panel-dev/1Panel/agent/app/model"
"github.com/1Panel-dev/1Panel/agent/buserr"
"github.com/1Panel-dev/1Panel/agent/constant"
"github.com/1Panel-dev/1Panel/agent/utils/encrypt"
"github.com/1Panel-dev/1Panel/agent/utils/ssh"
"github.com/jinzhu/copier"
@ -23,6 +22,7 @@ type ISettingService interface {
TestConnByInfo(req dto.SSHConnData) bool
SaveConnInfo(req dto.SSHConnData) error
GetSystemProxy() (*dto.SystemProxy, error)
GetLocalConn() dto.SSHConnData
GetSettingByKey(key string) string
}
@ -119,7 +119,6 @@ func (u *SettingService) SaveConnInfo(req dto.SSHConnData) error {
localConn, _ := json.Marshal(&connItem)
connAfterEncrypt, _ := encrypt.StringEncrypt(string(localConn))
_ = settingRepo.Update("LocalSSHConn", connAfterEncrypt)
_ = settingRepo.Update("LocalSSHConnShow", constant.StatusEnable)
return nil
}
@ -134,6 +133,29 @@ func (u *SettingService) GetSystemProxy() (*dto.SystemProxy, error) {
return &systemProxy, nil
}
func (u *SettingService) GetLocalConn() dto.SSHConnData {
var data dto.SSHConnData
connItem, _ := settingRepo.GetValueByKey("LocalSSHConn")
if len(connItem) == 0 {
return data
}
connInfoInDB, _ := encrypt.StringDecrypt(connItem)
data.LocalSSHConnShow, _ = settingRepo.GetValueByKey("LocalSSHConnShow")
if err := json.Unmarshal([]byte(connInfoInDB), &data); err != nil {
return data
}
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))
}
return data
}
func (u *SettingService) GetSettingByKey(key string) string {
switch key {
case "LocalSSHConn":

View file

@ -237,6 +237,9 @@ func (s *SubTask) Execute() error {
case err = <-done:
if err != nil {
s.RootTask.Log(i18n.GetWithNameAndErr("SubTaskFailed", subTaskName, err))
if err.Error() == i18n.GetMsgByKey("ErrShutDown") {
return err
}
} else {
s.RootTask.Log(i18n.GetWithName("SubTaskSuccess", subTaskName))
return nil

View file

@ -754,6 +754,12 @@ func (f FileOp) Decompress(srcFile string, dst string, cType CompressType, secre
if cType == TarGz && strings.Contains(err.Error(), "bad decrypt") {
return buserr.New("ErrBadDecrypt")
}
if cType == TarGz && cmd.Which("file") {
std, _ := cmd.RunDefaultWithStdoutBashCf("file %s", srcFile)
if strings.Contains(std, "openssl enc'd data with salted password") && len(secret) == 0 {
return buserr.New("ErrBadDecrypt")
}
}
} else {
if cType == Rar || cType == X7z {
return err

View file

@ -13,12 +13,12 @@ import (
)
// @Tags Command
// @Summary Export command
// @Summary Upload command csv for list
// @Success 200 {string} path
// @Security ApiKeyAuth
// @Security Timestamp
// @Router /core/commands/upload [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"导出快速命令","formatEN":"export quick commands"}
// @Router /core/commands/import [post]
// @x-panel-log {"bodyKeys":[],"paramKeys":[],"BeforeFunctions":[],"formatZH":"上传快速命令文件","formatEN":"upload quick commands with csv"}
func (b *BaseApi) UploadCommandCsv(c *gin.Context) {
form, err := c.MultipartForm()
if err != nil {
@ -37,7 +37,7 @@ func (b *BaseApi) UploadCommandCsv(c *gin.Context) {
return
}
groupRepo := repo.NewIGroupRepo()
group, _ := groupRepo.Get(groupRepo.WithByDefault(true))
group, _ := groupRepo.Get(repo.WithByType("command"), groupRepo.WithByDefault(true))
var commands []dto.CommandInfo
for {
record, err := reader.Read()
@ -91,7 +91,12 @@ func (b *BaseApi) ImportCommands(c *gin.Context) {
return
}
groupRepo := repo.NewIGroupRepo()
group, _ := groupRepo.Get(repo.WithByType("command"), groupRepo.WithByDefault(true))
for _, item := range req.Items {
if item.GroupID == 0 {
item.GroupID = group.ID
}
_ = commandService.Create(item)
}
helper.Success(c)

View file

@ -3,6 +3,7 @@ package service
import (
"encoding/json"
"fmt"
mathRand "math/rand"
"net/http"
"os"
"path"
@ -29,6 +30,7 @@ import (
type ScriptService struct{}
type IScriptService interface {
Run()
Search(ctx *gin.Context, req dto.SearchPageWithGroup) (int64, interface{}, error)
Create(req dto.ScriptOperate) error
Update(req dto.ScriptOperate) error
@ -163,6 +165,27 @@ func (u *ScriptService) Update(req dto.ScriptOperate) error {
return nil
}
func StartSync() {
if global.ScriptSyncJobID != 0 {
global.Cron.Remove(global.ScriptSyncJobID)
}
service := NewIScriptService()
scriptSync, _ := repo.NewISettingRepo().GetValueByKey("ScriptSync")
if !global.CONF.Base.IsOffLine && scriptSync == constant.StatusEnable {
id, err := global.Cron.AddJob(fmt.Sprintf("%v %v * * *", mathRand.Intn(60), mathRand.Intn(3)), service)
if err != nil {
global.LOG.Errorf("[core] can not add script sync corn job: %s", err.Error())
}
global.LOG.Info("add job for script library sync successful")
global.ScriptSyncJobID = id
}
}
func (u *ScriptService) Run() {
if err := u.Sync(dto.OperateByTaskID{}); err != nil {
global.LOG.Errorf("sync scripts from remote failed, err: %v", err)
}
}
func LoadScriptInfo(id uint) (model.ScriptLibrary, error) {
return scriptRepo.Get(repo.WithByID(id))
}

View file

@ -152,6 +152,12 @@ func (u *SettingService) Update(key, value string) error {
}
case "UpgradeBackupCopies":
dropBackupCopies()
case "ScriptSync":
if value == constant.StatusEnable {
StartSync()
} else {
global.Cron.Remove(global.ScriptSyncJobID)
}
}
return nil

View file

@ -26,6 +26,8 @@ var (
I18nForCmd *i18n.Localizer
Cron *cron.Cron
ScriptSyncJobID cron.EntryID
)
type DBOption func(*gorm.DB) *gorm.DB

View file

@ -1,12 +1,9 @@
package cron
import (
"fmt"
mathRand "math/rand"
"time"
"github.com/1Panel-dev/1Panel/core/app/repo"
"github.com/1Panel-dev/1Panel/core/constant"
"github.com/1Panel-dev/1Panel/core/app/service"
"github.com/1Panel-dev/1Panel/core/global"
"github.com/1Panel-dev/1Panel/core/init/cron/job"
"github.com/1Panel-dev/1Panel/core/utils/common"
@ -21,13 +18,6 @@ func Init() {
global.LOG.Errorf("[core] can not add backup token refresh corn job: %s", err.Error())
}
scriptSync, _ := repo.NewISettingRepo().GetValueByKey("ScriptSync")
if !global.CONF.Base.IsOffLine && scriptSync == constant.StatusEnable {
scriptJob := job.NewScriptJob()
if _, err := global.Cron.AddJob(fmt.Sprintf("%v %v * * *", mathRand.Intn(60), mathRand.Intn(3)), scriptJob); err != nil {
global.LOG.Errorf("[core] can not add script sync corn job: %s", err.Error())
}
}
service.StartSync()
global.Cron.Start()
}

View file

@ -1,19 +0,0 @@
package job
import (
"github.com/1Panel-dev/1Panel/core/app/dto"
"github.com/1Panel-dev/1Panel/core/app/service"
"github.com/1Panel-dev/1Panel/core/global"
)
type script struct{}
func NewScriptJob() *script {
return &script{}
}
func (s *script) Run() {
if err := service.NewIScriptService().Sync(dto.OperateByTaskID{}); err != nil {
global.LOG.Errorf("sync scripts from remote failed, err: %v", err)
}
}

View file

@ -49,6 +49,8 @@ export namespace Host {
privateKey: string;
passPhrase: string;
password: string;
localSSHConnShow: string;
}
export interface GroupChange {
id: number;

View file

@ -1196,7 +1196,10 @@ const message = {
terminal: {
local: 'Local',
defaultConn: 'Default Connection',
defaultConnHelper: 'Whether to connect to the host by default after opening the terminal',
defaultConnHelper:
'This operation will automatically connect to the node terminal after opening the terminal for {0}. Continue?',
localConnJump:
'Default connection information is maintained in [Terminal - Settings]. If connection fails, please edit there!',
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',

View file

@ -1198,7 +1198,10 @@ const message = {
terminal: {
local: 'Local',
defaultConn: 'Conexión predeterminada',
defaultConnHelper: 'Indica si se conecta al host por defecto después de abrir la terminal',
defaultConnHelper:
'Esta operación conectará automáticamente al terminal del nodo después de abrir el terminal para {0}. ¿Continuar?',
localConnJump:
'La información de conexión predeterminada se mantiene en [Terminal - Configuración]. Si la conexión falla, ¡edite allí!',
localHelper: 'El nombre `local` se utiliza solo para identificación interna del sistema',
connLocalErr:
'No se puede autenticar automáticamente, por favor introduzca la información de inicio de sesión del servidor local.',

View file

@ -1155,7 +1155,10 @@ const message = {
terminal: {
local: 'ローカル',
defaultConn: 'デフォルト接続',
defaultConnHelper: 'ターミナルを開いた後にデフォルトでホストに接続するかどうか',
defaultConnHelper:
'この操作は{0}のターミナルを開いた後自動的にノードターミナルに接続します続行しますか',
localConnJump:
'デフォルト接続情報はターミナル - 設定で管理されています接続に失敗した場合はこちらで編集してください',
localHelper: 'ローカル名はシステムのローカル識別にのみ使用されます',
connLocalErr: '自動的に認証できない場合はローカルサーバーのログイン情報を入力してください',
testConn: 'テスト接続',

View file

@ -1147,7 +1147,8 @@ const message = {
terminal: {
local: '로컬',
defaultConn: '기본 연결',
defaultConnHelper: '터미널을 기본적으로 호스트에 연결할지 여부',
defaultConnHelper: ' 작업은 {0} 터미널을 자동으로 노드 터미널에 연결됩니다. 계속하시겠습니까?',
localConnJump: '기본 연결 정보는 [터미널 - 설정]에서 관리됩니다. 연결 실패 해당 위치에서 편집하세요!',
localHelper: '로컬 이름은 시스템 로컬 식별에만 사용됩니다.',
connLocalErr: '자동 인증에 실패했습니다. 로컬 서버 로그인 정보를 입력해주세요.',
testConn: '연결 테스트',

View file

@ -1186,7 +1186,10 @@ const message = {
terminal: {
local: 'Tempatan',
defaultConn: 'Sambungan Lalai',
defaultConnHelper: 'Sama ada untuk menyambung ke hos secara lalai selepas membuka terminal',
defaultConnHelper:
'Operasi ini akan menyambung secara automatik ke terminal nod selepas membuka terminal untuk {0}. Teruskan?',
localConnJump:
'Maklumat sambungan lalai dikekalkan dalam [Terminal - Tetapan]. Jika sambungan gagal, sila edit di sana!',
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',

View file

@ -1180,7 +1180,10 @@ const message = {
terminal: {
local: 'Local',
defaultConn: 'Conexão Padrão',
defaultConnHelper: 'Se deve conectar ao host por padrão após abrir o terminal',
defaultConnHelper:
'Esta operação conectará automaticamente ao terminal do após abrir o terminal para {0}. Continuar?',
localConnJump:
'As informações de conexão padrão são mantidas em [Terminal - Configurações]. Se a conexão falhar, edite !',
localHelper: 'O nome local é usado apenas para identificação local do sistema.',
connLocalErr:
'Невозможно автоматически аутентифицироваться, пожалуйста, заполните информацию для входа на локальный сервер.',

View file

@ -1184,7 +1184,10 @@ const message = {
terminal: {
local: 'Локальный',
defaultConn: 'Соединение по умолчанию',
defaultConnHelper: 'Подключаться ли к хосту по умолчанию после открытия терминала',
defaultConnHelper:
'Эта операция автоматически подключится к узловому терминалу после открытия терминала для {0}. Продолжить?',
localConnJump:
'Информация о соединении по умолчанию поддерживается в [Терминал - Настройки]. Если соединение не удается, отредактируйте там!',
localHelper: 'Локальное имя используется только для локальной идентификации системы.',
connLocalErr:
'Невозможно выполнить автоматическую аутентификацию, пожалуйста, заполните информацию для входа на локальный сервер',

View file

@ -1210,7 +1210,10 @@ const message = {
terminal: {
local: 'Yerel',
defaultConn: 'Varsayılan Bağlantı',
defaultConnHelper: 'Terminal ıldıktan sonra varsayılan olarak ana bilgisayara bağlanılsın mı',
defaultConnHelper:
'Bu işlem, {0} için terminal ıldıktan sonra otomatik olarak düğüm terminaline bağlanacaktır. Devam etmek istiyor musunuz?',
localConnJump:
'Varsayılan bağlantı bilgileri [Terminal - Ayarlar] bölümünde yönetilir. Bağlantı başarısız olursa lütfen oradan düzenleyin!',
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',

View file

@ -1137,7 +1137,8 @@ const message = {
terminal: {
local: '本機',
defaultConn: '預設連接',
defaultConnHelper: '開啟終端後是否預設連線到主機',
defaultConnHelper: '該操作將{0}開啟終端後自動連線到所在節點終端是否繼續',
localConnJump: '預設連線資訊在終端 - 設定中維護連線失敗可前往編輯',
localHelper: 'local 名稱僅用於系統本機標識',
connLocalErr: '無法自動認證請填寫本機伺服器的登入資訊',
testConn: '連接測試',

View file

@ -1137,7 +1137,8 @@ const message = {
terminal: {
local: '本机',
defaultConn: '默认连接',
defaultConnHelper: '打开终端后是否默认连接主机',
defaultConnHelper: '该操作将{0}打开终端后自动连接所在节点终端是否继续',
localConnJump: '默认连接信息在 终端 - 配置 中维护连接失败可前往编辑',
localHelper: 'local 名称仅用于系统本机标识',
connLocalErr: '无法自动认证请填写本地服务器的登录信息',
testConn: '连接测试',

View file

@ -8,7 +8,8 @@
:fullScreen="true"
>
<template #content>
<Terminal style="height: calc(100vh - 120px)" ref="terminalRef"></Terminal>
<el-alert :closable="false" :title="$t('terminal.localConnJump')" type="info" />
<Terminal class="mt-2" style="height: calc(100vh - 160px)" ref="terminalRef"></Terminal>
<div>
<el-cascader
v-model="quickCmd"

View file

@ -97,6 +97,7 @@ const groupList = ref();
const acceptParams = (): void => {
visible.value = true;
loadGroups();
currentGroup.value = '';
data.value = [];
};

View file

@ -78,18 +78,6 @@
@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') }}
@ -97,6 +85,19 @@
<el-button @click="search(true)" plain>{{ $t('commons.button.reset') }}</el-button>
<el-button @click="onSave" type="primary">{{ $t('commons.button.save') }}</el-button>
</el-form-item>
<el-divider border-style="dashed" />
<el-form-item :label="$t('terminal.defaultConn')">
<el-switch v-model="form.showDefaultConn" @change="changeShow" />
</el-form-item>
<el-form-item :label="$t('xpack.node.connInfo')">
<el-input disabled v-model="form.defaultConn">
<template #append>
<el-button @click="dialogRef.acceptParams(false)" icon="Setting">
{{ $t('commons.button.set') }}
</el-button>
</template>
</el-input>
</el-form-item>
</el-col>
</el-row>
</el-form>
@ -109,7 +110,7 @@
<script lang="ts" setup>
import { ref, reactive } from 'vue';
import { ElForm } from 'element-plus';
import { getAgentSettingByKey, getTerminalInfo, updateAgentSetting, UpdateTerminalInfo } from '@/api/modules/setting';
import { 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';
@ -117,6 +118,7 @@ import { FitAddon } from '@xterm/addon-fit';
import i18n from '@/lang';
import { MsgSuccess } from '@/utils/message';
import { TerminalStore } from '@/store';
import { loadLocalConn } from '@/api/modules/terminal';
const loading = ref(false);
const terminalStore = TerminalStore();
@ -134,7 +136,9 @@ const form = reactive({
cursorStyle: 'underline',
scrollback: 1000,
scrollSensitivity: 10,
showDefaultConn: false,
defaultConn: '',
});
const acceptParams = () => {
@ -166,18 +170,28 @@ const search = async (withReset?: boolean) => {
};
const loadConnShow = async () => {
await getAgentSettingByKey('LocalSSHConnShow').then((res) => {
form.showDefaultConn = res.data === 'Enable';
await loadLocalConn().then((res) => {
form.showDefaultConn = res.data.localSSHConnShow === 'Enable';
if (res.data.addr && res.data.port && res.data.user) {
form.defaultConn = res.data.user + '@' + res.data.addr + ':' + res.data.port;
} else {
form.defaultConn = '-';
}
});
};
const changeShow = async () => {
if (form.showDefaultConn) {
dialogRef.value.acceptParams(true);
return;
}
await updateAgentSetting({ key: 'LocalSSHConnShow', value: 'Disable' }).then(() => {
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
let op = form.showDefaultConn ? i18n.global.t('xpack.waf.allow') : i18n.global.t('xpack.waf.deny');
ElMessageBox.confirm(i18n.global.t('terminal.defaultConnHelper', [op]), i18n.global.t('terminal.defaultConn'), {
confirmButtonText: i18n.global.t('commons.button.confirm'),
cancelButtonText: i18n.global.t('commons.button.cancel'),
type: 'info',
}).then(async () => {
await updateAgentSetting({ key: 'LocalSSHConnShow', value: form.showDefaultConn ? 'Enable' : 'Disable' }).then(
() => {
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
},
);
});
};