Repository: 1Panel-dev/1Panel
Pull request #8510: fix: Fix the problem of abnormal script execution
Author: login: ssongliu
Labels: name: do-not-merge/release-note-label-needed
name: approved
name: dev-v2
Created: 2025-04-29T06:37:31Z | Merged: 2025-04-29T07:59:57Z



Diff:
diff --git a/core/app/service/upgrade.go b/core/app/service/upgrade.go
index c87623abd602..9296fab3fcab 100644
--- a/core/app/service/upgrade.go
+++ b/core/app/service/upgrade.go
@@ -160,7 +160,13 @@ func (u *UpgradeService) Upgrade(req dto.Upgrade) error {
 			return
 		}
 
-		if err := files.CopyItem(false, true, path.Join(tmpDir, "1panel*.service"), "/etc/systemd/system"); err != nil {
+		if err := files.CopyItem(false, true, path.Join(tmpDir, "1panel-core.service"), "/etc/systemd/system"); err != nil {
+			global.LOG.Errorf("upgrade 1panel.service failed, err: %v", err)
+			_ = settingRepo.Update("SystemStatus", "Free")
+			u.handleRollback(originalDir, 3)
+			return
+		}
+		if err := files.CopyItem(false, true, path.Join(tmpDir, "1panel-agent.service"), "/etc/systemd/system"); err != nil {
 			global.LOG.Errorf("upgrade 1panel.service failed, err: %v", err)
 			_ = settingRepo.Update("SystemStatus", "Free")
 			u.handleRollback(originalDir, 3)
@@ -191,13 +197,19 @@ func (u *UpgradeService) Rollback(req dto.OperateByID) error {
 }
 
 func (u *UpgradeService) handleBackup(originalDir string) error {
-	if err := files.CopyItem(false, true, "/usr/local/bin/1panel*", originalDir); err != nil {
+	if err := files.CopyItem(false, true, "/usr/local/bin/1panel-core", originalDir); err != nil {
+		return err
+	}
+	if err := files.CopyItem(false, true, "/usr/local/bin/1panel-agent", originalDir); err != nil {
 		return err
 	}
 	if err := files.CopyItem(false, true, "/usr/local/bin/1pctl", originalDir); err != nil {
 		return err
 	}
-	if err := files.CopyItem(false, true, "/etc/systemd/system/1panel*.service", originalDir); err != nil {
+	if err := files.CopyItem(false, true, "/etc/systemd/system/1panel-core.service", originalDir); err != nil {
+		return err
+	}
+	if err := files.CopyItem(false, true, "/etc/systemd/system/1panel-agent.service", originalDir); err != nil {
 		return err
 	}
 	if err := files.CopyItem(true, true, path.Join(global.CONF.Base.InstallDir, "1panel/db"), originalDir); err != nil {
diff --git a/core/i18n/lang/en.yaml b/core/i18n/lang/en.yaml
index abb4a8ecb2fc..88e9430ec259 100644
--- a/core/i18n/lang/en.yaml
+++ b/core/i18n/lang/en.yaml
@@ -98,7 +98,7 @@ SyncNode: "Sync node data"
 SyncPackageData: "Package sync data [{{ .detail }}]"
 SyncPackageEncrypt: "Data package encryption"
 SyncRequest: "Request node sync API"
-SyncFailedRetry: "Node data sync failed (attempt {{ .index }}), {{ .err }}"
+SyncFailedRetry: "Node data sync timeout (attempt {{ .index }}), retrying..."
 SyncFailed: "Sync failed, please manually sync in the node list!"
 
 #upgrade node
diff --git a/core/i18n/lang/ja.yaml b/core/i18n/lang/ja.yaml
index 8ba16983958b..19ab4419fc4d 100644
--- a/core/i18n/lang/ja.yaml
+++ b/core/i18n/lang/ja.yaml
@@ -99,7 +99,7 @@ SyncNode: "ノードデータ同期"
 SyncPackageData: "同期データのパッケージ化 [{{ .detail }}]"
 SyncPackageEncrypt: "データパッケージの暗号化"
 SyncRequest: "ノード同期APIをリクエスト"
-SyncFailedRetry: "ノードデータ同期失敗 ({{ .index }}回目), {{ .err }}"
+SyncFailedRetry: "ノードデータ同期タイムアウト ({{ .index }}回目)、再試行中..."
 SyncFailed: "同期に失敗しました、ノードリストで手動同期してください！"
 
 #upgrade node
diff --git a/core/i18n/lang/ko.yaml b/core/i18n/lang/ko.yaml
index cfbf9cb516a8..4649711fb145 100644
--- a/core/i18n/lang/ko.yaml
+++ b/core/i18n/lang/ko.yaml
@@ -98,7 +98,7 @@ SyncNode: "노드 데이터 동기화"
 SyncPackageData: "동기화 데이터 패키징 [{{ .detail }}]"
 SyncPackageEncrypt: "데이터 패키지 암호화"
 SyncRequest: "노드 동기화 API 요청"
-SyncFailedRetry: "노드 데이터 동기화 실패 ({{ .index }}번째 시도), {{ .err }}"
+SyncFailedRetry: "노드 데이터 동기화 시간 초과 ({{ .index }}번째 시도), 재시도 중..."
 SyncFailed: "동기화 실패, 노드 목록에서 수동 동기화를 실행하세요!"
 
 #upgrade node
diff --git a/core/i18n/lang/ms.yml b/core/i18n/lang/ms.yml
index f12d7735d2ea..871701e1b411 100644
--- a/core/i18n/lang/ms.yml
+++ b/core/i18n/lang/ms.yml
@@ -98,7 +98,7 @@ SyncNode: "Segerakkan data nod"
 SyncPackageData: "Pakej data segerak [{{ .detail }}]"
 SyncPackageEncrypt: "Enkripsi pakej data"
 SyncRequest: "Permintaan API segerak nod"
-SyncFailedRetry: "Gagal segerak data nod (percubaan ke-{{ .index }}), {{ .err }}"
+SyncFailedRetry: "Segerakan data nod tamat masa (percubaan ke-{{ .index }}), mencuba semula..."
 SyncFailed: "Segerakan gagal, sila segerakkan secara manual dalam senarai nod!"
 
 #upgrade node
diff --git a/core/i18n/lang/pt-BR.yaml b/core/i18n/lang/pt-BR.yaml
index dba84a3f8e0b..3846e2d022fa 100644
--- a/core/i18n/lang/pt-BR.yaml
+++ b/core/i18n/lang/pt-BR.yaml
@@ -98,7 +98,7 @@ SyncNode: "Sincronizar dados do nó"
 SyncPackageData: "Empacotar dados de sincronização [{{ .detail }}]"
 SyncPackageEncrypt: "Criptografia de pacote de dados"
 SyncRequest: "Solicitar API de sincronização de nó"
-SyncFailedRetry: "Falha na sincronização de dados do nó (tentativa {{ .index }}), {{ .err }}"
+SyncFailedRetry: "Tempo esgotado na sincronização de dados do nó (tentativa {{ .index }}), tentando novamente..."
 SyncFailed: "Falha na sincronização, por favor sincronize manualmente na lista de nós!"
 
 #upgrade node
diff --git a/core/i18n/lang/ru.yaml b/core/i18n/lang/ru.yaml
index f4e9d160dcd3..26eec46332c8 100644
--- a/core/i18n/lang/ru.yaml
+++ b/core/i18n/lang/ru.yaml
@@ -98,7 +98,7 @@ SyncNode: "Синхронизация данных узла"
 SyncPackageData: "Упаковка данных синхронизации [{{ .detail }}]"
 SyncPackageEncrypt: "Шифрование пакета данных"
 SyncRequest: "Запрос API синхронизации узла"
-SyncFailedRetry: "Ошибка синхронизации данных узла (попытка {{ .index }}), {{ .err }}"
+SyncFailedRetry: "Таймаут синхронизации данных узла (попытка {{ .index }}), повторная попытка..."
 SyncFailed: "Ошибка синхронизации, выполните ручную синхронизацию в списке узлов!"
 
 #upgrade node
diff --git a/core/i18n/lang/zh-Hant.yaml b/core/i18n/lang/zh-Hant.yaml
index 62c7b139e85a..8317460d3b18 100644
--- a/core/i18n/lang/zh-Hant.yaml
+++ b/core/i18n/lang/zh-Hant.yaml
@@ -98,7 +98,7 @@ SyncNode: "同步節點數據"
 SyncPackageData: "打包同步數據 [{{ .detail }}]"
 SyncPackageEncrypt: "數據包加密"
 SyncRequest: "請求節點同步接口"
-SyncFailedRetry: "第 {{ .index }} 次同步節點數據失敗，{{ .err }}"
+SyncFailedRetry: "第 {{ .index }} 次同步節點數據失敗逾時，正在重試..."
 SyncFailed: "同步失敗，請在節點列表中手動同步！"
 
 #upgrade node
diff --git a/core/i18n/lang/zh.yaml b/core/i18n/lang/zh.yaml
index f56ec51ddffb..9aec301b6541 100644
--- a/core/i18n/lang/zh.yaml
+++ b/core/i18n/lang/zh.yaml
@@ -99,7 +99,7 @@ SyncNode: "同步节点数据"
 SyncPackageData: "打包同步数据 [{{ .detail }}]"
 SyncPackageEncrypt: "数据包加密"
 SyncRequest: "请求节点同步接口"
-SyncFailedRetry: "第 {{ .index }} 次同步节点数据失败，{{ .err }}"
+SyncFailedRetry: "第 {{ .index }} 次同步节点数据失败超时，正在重试..."
 SyncFailed: "同步失败，请在节点列表中手动同步！"
 
 #upgrade node
diff --git a/core/utils/terminal/local_cmd.go b/core/utils/terminal/local_cmd.go
index 256c429cf259..02ac335b2c89 100644
--- a/core/utils/terminal/local_cmd.go
+++ b/core/utils/terminal/local_cmd.go
@@ -32,14 +32,14 @@ func NewCommand(script string) (*LocalCommand, error) {
 	} else {
 		cmd.Env = append(os.Environ(), "TERM=xterm")
 	}
-	cmd.Env = append(cmd.Env, "1PANEL_INIT_SCRIPT="+script)
+	cmd.Env = append(cmd.Env, "INIT_SCRIPT="+script)
 	pty, err := pty.Start(cmd)
 	if err != nil {
 		return nil, errors.Wrapf(err, "failed to start command")
 	}
 	if len(script) != 0 {
 		time.Sleep(100 * time.Millisecond)
-		_, _ = pty.Write([]byte("bash -c \"$1PANEL_INIT_SCRIPT\"\n"))
+		_, _ = pty.Write([]byte("bash -c \"$INIT_SCRIPT\"\n"))
 	}
 
 	lcmd := &LocalCommand{
diff --git a/core/utils/terminal/ws_session.go b/core/utils/terminal/ws_session.go
index 98ba47f2acf2..03030d817f11 100644
--- a/core/utils/terminal/ws_session.go
+++ b/core/utils/terminal/ws_session.go
@@ -89,7 +89,7 @@ func NewLogicSshWsSession(cols, rows int, sshClient *ssh.Client, wsConn *websock
 	}
 	if len(initCmd) != 0 {
 		time.Sleep(100 * time.Millisecond)
-		_, _ = stdinP.Write([]byte(initCmd + "\n"))
+		_, _ = stdinP.Write([]byte(" clear &&" + initCmd + "\n"))
 	}
 	return &LogicSshWsSession{
 		stdinPipe:       stdinP,
diff --git a/frontend/src/lang/modules/en.ts b/frontend/src/lang/modules/en.ts
index 9d152e97dfc4..49812b5ce3f4 100644
--- a/frontend/src/lang/modules/en.ts
+++ b/frontend/src/lang/modules/en.ts
@@ -347,6 +347,7 @@ const message = {
         ssl: 'Certificate',
         database: 'Database',
         aiTools: 'AI',
+        mcp: 'MCP',
         container: 'Container',
         cronjob: 'Cronjob',
         system: 'System',
@@ -1874,8 +1875,7 @@ const message = {
         upgradeNotes: 'Release note',
         upgradeNow: 'Upgrade now',
         source: 'Download source',
-        versionNotSame:
-            'Node version mismatch with the main node. Please upgrade in Node Management before retrying.',
+        versionNotSame: 'Node version mismatch with the main node. Please upgrade in Node Management before retrying.',
         versionCompare:
             'Detected that node {0} is already at the latest upgradable version. Please check the primary node version and try again!',
 
@@ -1892,7 +1892,7 @@ const message = {
         license: 'License',
         bindNode: 'Bind Node',
         menuSetting: 'Menu Settings',
-        menuSettingHelper: 'If only 1 menu is kept, the sidebar will directly display that menu.',
+        menuSettingHelper: 'When only 1 submenu exists, the menu bar will display only that submenu',
         showAll: 'Show All',
         hideALL: 'Hide All',
         ifShow: 'Whether to Show',
diff --git a/frontend/src/lang/modules/ja.ts b/frontend/src/lang/modules/ja.ts
index 789492e77d0e..f5e7c3d5d78e 100644
--- a/frontend/src/lang/modules/ja.ts
+++ b/frontend/src/lang/modules/ja.ts
@@ -339,6 +339,7 @@ const message = {
         ssl: '証明書|証明書',
         database: 'データベース|データベース',
         aiTools: 'AI',
+        mcp: 'MCP',
         container: 'コンテナ|コンテナ',
         cronjob: 'クロンジョブ|クロンの仕事',
         system: 'システム',
@@ -1799,7 +1800,7 @@ const message = {
         license: 'ライセンス',
         bindNode: 'ノードをバインド',
         menuSetting: 'メニュー設定',
-        menuSettingHelper: '1つのメニューだけを保持する場合、サイドバーにはそのメニューが直接表示されます。',
+        menuSettingHelper: 'サブメニューが1つしか存在しない場合、メニューバーにはそのサブメニューのみが表示されます',
         showAll: 'すべてを表示します',
         hideALL: 'すべてを隠します',
         ifShow: '表示するかどうか',
diff --git a/frontend/src/lang/modules/ko.ts b/frontend/src/lang/modules/ko.ts
index f1fdd7340d6f..f5b291465fa1 100644
--- a/frontend/src/lang/modules/ko.ts
+++ b/frontend/src/lang/modules/ko.ts
@@ -341,6 +341,7 @@ const message = {
         ssl: '인증서 | 인증서들',
         database: '데이터베이스 | 데이터베이스들',
         aiTools: 'AI',
+        mcp: 'MCP',
         container: '컨테이너 | 컨테이너들',
         cronjob: '크론 작업 | 크론 작업들',
         system: '시스템',
@@ -1754,8 +1755,7 @@ const message = {
         upgradeNotes: '릴리스 노트',
         upgradeNow: '지금 업그레이드',
         source: '다운로드 소스',
-        versionNotSame:
-            '노드 버전이 메인 노드와 일치하지 않습니다. 노드 관리에서 업그레이드한 후 다시 시도해 주세요.',
+        versionNotSame: '노드 버전이 메인 노드와 일치하지 않습니다. 노드 관리에서 업그레이드한 후 다시 시도해 주세요.',
         versionCompare:
             '노드 {0}이(가) 이미 업그레이드 가능한 최신 버전입니다. 마스터 노드 버전을 확인 후 다시 시도하세요!',
 
@@ -1772,7 +1772,7 @@ const message = {
         license: '라이선스',
         bindNode: '노드 바인딩',
         menuSetting: '메뉴 설정',
-        menuSettingHelper: '메뉴를 1개만 유지하면 사이드바에 해당 메뉴가 직접 표시됩니다.',
+        menuSettingHelper: '하위 메뉴가 1개만 존재할 경우, 메뉴 바에는 해당 하위 메뉴만 표시됩니다',
         showAll: '모두 표시',
         hideALL: '모두 숨기기',
         ifShow: '표시 여부',
diff --git a/frontend/src/lang/modules/ms.ts b/frontend/src/lang/modules/ms.ts
index f8a18a349b26..eed6adf34aa1 100644
--- a/frontend/src/lang/modules/ms.ts
+++ b/frontend/src/lang/modules/ms.ts
@@ -347,6 +347,7 @@ const message = {
         ssl: 'Certificate | Certificates',
         database: 'Database | Databases',
         aiTools: 'AI',
+        mcp: 'MCP',
         container: 'Container | Containers',
         cronjob: 'Cron Job | Cron Jobs',
         system: 'System',
@@ -1856,7 +1857,7 @@ const message = {
         license: 'Lesen',
         bindNode: 'Ikatan Nod',
         menuSetting: 'Tetapan Menu',
-        menuSettingHelper: 'Jika hanya 1 menu yang disimpan, bar sisi akan langsung menampilkan menu tersebut.',
+        menuSettingHelper: 'Apabila hanya terdapat 1 submenu, bar menu hanya akan memaparkan submenu tersebut',
         showAll: 'Papar Semua',
         hideALL: 'Sembunyikan Semua',
         ifShow: 'Sama ada untuk Dipaparkan',
diff --git a/frontend/src/lang/modules/pt-br.ts b/frontend/src/lang/modules/pt-br.ts
index db10cef25b94..191926017ab8 100644
--- a/frontend/src/lang/modules/pt-br.ts
+++ b/frontend/src/lang/modules/pt-br.ts
@@ -345,6 +345,7 @@ const message = {
         ssl: 'Certificado | Certificados',
         database: 'Banco de Dados | Bancos de Dados',
         aiTools: 'AI',
+        mcp: 'MCP',
         container: 'Container | Containers',
         cronjob: 'Tarefa Cron | Tarefas Cron',
         system: 'Sistema',
@@ -1842,7 +1843,7 @@ const message = {
         license: 'Licença',
         bindNode: 'Vincular Nó',
         menuSetting: 'Configurações do Menu',
-        menuSettingHelper: 'Se apenas 1 menu for mantido, a barra lateral exibirá diretamente esse menu.',
+        menuSettingHelper: 'Quando apenas 1 submenu existir, a barra de menus exibirá apenas esse submenu',
         showAll: 'Mostrar Tudo',
         hideALL: 'Ocultar Tudo',
         ifShow: 'Exibir?',
diff --git a/frontend/src/lang/modules/ru.ts b/frontend/src/lang/modules/ru.ts
index facfe6bb95eb..0dad61f8adb0 100644
--- a/frontend/src/lang/modules/ru.ts
+++ b/frontend/src/lang/modules/ru.ts
@@ -342,6 +342,7 @@ const message = {
         ssl: 'Сертификат | Сертификаты',
         database: 'База данных | Базы данных',
         aiTools: 'AI',
+        mcp: 'MCP',
         container: 'Контейнер | Контейнеры',
         cronjob: 'Cron | Задачи Cron',
         system: 'Система',
@@ -1841,7 +1842,7 @@ const message = {
         license: 'Лицензия',
         bindNode: 'Привязать Узел',
         menuSetting: 'Настройки меню',
-        menuSettingHelper: 'Если оставить только 1 меню, боковая панель будет напрямую отображать это меню.',
+        menuSettingHelper: 'Если существует только 1 подменю, в панели меню будет отображаться только это подменю',
         showAll: 'Показать все',
         hideALL: 'Скрыть все',
         ifShow: 'Показывать',
diff --git a/frontend/src/lang/modules/zh-Hant.ts b/frontend/src/lang/modules/zh-Hant.ts
index ab6d8443d399..64890449d6b6 100644
--- a/frontend/src/lang/modules/zh-Hant.ts
+++ b/frontend/src/lang/modules/zh-Hant.ts
@@ -341,6 +341,7 @@ const message = {
         ssl: '證書',
         database: '數據庫',
         aiTools: 'AI',
+        mcp: 'MCP',
         container: '容器',
         cronjob: '計劃任務',
         system: '系統',
@@ -1758,7 +1759,7 @@ const message = {
         license: '許可證',
         bindNode: '綁定節點',
         menuSetting: '菜單設定',
-        menuSettingHelper: '如果只保留 1 個菜單，則側邊欄會直接顯示該菜單。',
+        menuSettingHelper: '當只存在 1 個子選單時，選單列將僅展示該子選單',
         showAll: '全部顯示',
         hideALL: '全部隱藏',
         ifShow: '是否顯示',
diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts
index bba46405ad7f..089ef32ccbc0 100644
--- a/frontend/src/lang/modules/zh.ts
+++ b/frontend/src/lang/modules/zh.ts
@@ -339,6 +339,7 @@ const message = {
         ssl: '证书',
         database: '数据库',
         aiTools: 'AI',
+        mcp: 'MCP',
         container: '容器',
         cronjob: '计划任务',
         system: '系统',
@@ -1751,7 +1752,7 @@ const message = {
         license: '许可证',
         bindNode: '绑定节点',
         menuSetting: '菜单设置',
-        menuSettingHelper: '如果只保留 1 个菜单，则侧边栏会直接显示该菜单',
+        menuSettingHelper: '当只存在 1 个子菜单时，菜单栏将仅展示该子菜单',
         showAll: '全部显示',
         hideALL: '全部隐藏',
         ifShow: '是否显示',
diff --git a/frontend/src/layout/components/Sidebar/components/Collapse.vue b/frontend/src/layout/components/Sidebar/components/Collapse.vue
index a2672c359569..b37088611a5c 100644
--- a/frontend/src/layout/components/Sidebar/components/Collapse.vue
+++ b/frontend/src/layout/components/Sidebar/components/Collapse.vue
@@ -61,7 +61,7 @@
                     </div>
                 </div>
                 <el-input
-                    v-if="showNodes() && nodeOptions?.length > 5"
+                    v-if="showNodes() && nodes?.length > 5"
                     suffix-icon="Search"
                     v-model="filter"
                     @input="changeFilter"
diff --git a/frontend/src/routers/modules/ai.ts b/frontend/src/routers/modules/ai.ts
index 68a69387ac8b..9e09c61ad598 100644
--- a/frontend/src/routers/modules/ai.ts
+++ b/frontend/src/routers/modules/ai.ts
@@ -25,7 +25,7 @@ const databaseRouter = {
             name: 'MCPServer',
             component: () => import('@/views/ai/mcp/server/index.vue'),
             meta: {
-                title: 'MCP',
+                title: 'menu.mcp',
                 requiresAuth: true,
             },
         },
diff --git a/frontend/src/views/cronjob/library/index.vue b/frontend/src/views/cronjob/library/index.vue
index 9eb8f488b9e4..fa0125ce6763 100644
--- a/frontend/src/views/cronjob/library/index.vue
+++ b/frontend/src/views/cronjob/library/index.vue
@@ -19,7 +19,12 @@
                 <el-select v-model="group" @change="search()" clearable class="p-w-200">
                     <template #prefix>{{ $t('commons.table.group') }}</template>
                     <div v-for="item in groupOptions" :key="item.id">
-                        <el-option :label="item.name" :value="item.id" />
+                        <el-option
+                            v-if="item.name === 'Default'"
+                            :label="$t('commons.table.default')"
+                            :value="item.id"
+                        />
+                        <el-option v-else :label="item.name" :value="item.id" />
                     </div>
                 </el-select>
                 <TableSearch @search="search()" v-model:searchName="searchInfo" />
@@ -47,7 +52,10 @@
                             <el-button class="mr-3" size="small" v-if="row.isSystem">system</el-button>
                             <span v-if="row.groupBelong">
                                 <el-button size="small" v-for="(item, index) in row.groupBelong" :key="index">
-                                    {{ item }}
+                                    <span v-if="item === 'Default'">
+                                        {{ $t('commons.table.default') }}
+                                    </span>
+                                    <span v-else>{{ item }}</span>
                                 </el-button>
                             </span>
                         </template>
diff --git a/frontend/src/views/cronjob/library/operate/index.vue b/frontend/src/views/cronjob/library/operate/index.vue
index 812b6a626dc0..541f43674cd3 100644
--- a/frontend/src/views/cronjob/library/operate/index.vue
+++ b/frontend/src/views/cronjob/library/operate/index.vue
@@ -12,7 +12,14 @@
             </el-form-item>
             <el-form-item :label="$t('commons.table.group')" prop="groupList">
                 <el-select filterable v-model="dialogData.rowData!.groupList" multiple>
-                    <el-option v-for="item in groupOptions" :key="item.id" :label="item.name" :value="item.id" />
+                    <div v-for="item in groupOptions" :key="item.id">
+                        <el-option
+                            v-if="item.name === 'Default'"
+                            :label="$t('commons.table.default')"
+                            :value="item.id"
+                        />
+                        <el-option v-else :label="item.name" :value="item.id" />
+                    </div>
                 </el-select>
                 <span class="input-help">{{ $t('cronjob.library.groupHelper') }}</span>
             </el-form-item>
diff --git a/frontend/src/views/setting/snapshot/create/index.vue b/frontend/src/views/setting/snapshot/create/index.vue
index bf4e5312f554..c4e7163c4e08 100644
--- a/frontend/src/views/setting/snapshot/create/index.vue
+++ b/frontend/src/views/setting/snapshot/create/index.vue
@@ -218,6 +218,8 @@ const emit = defineEmits(['search']);
 const acceptParams = (): void => {
     form.downloadAccountID = '';
     form.fromAccounts = [];
+    form.description = '';
+    form.secret = '';
     nowIndex.value = 0;
     search();
     loadBackups();


Reviews:
user: login: wanghe-fit2cloud
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-29T06:37:57Z
user: login: wanghe-fit2cloud
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-29T06:38:08Z
user: login: wanghe-fit2cloud
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-29T06:38:17Z
user: login: wanghe-fit2cloud
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-29T07:11:50Z
user: login: wanghe-fit2cloud
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-29T07:12:03Z
user: login: wanghe-fit2cloud
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-29T07:12:23Z
user: login: wanghe-fit2cloud
body: /lgtm
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-29T07:57:42Z

Review comments:
diff_hunk: @@ -191,13 +197,19 @@ func (u *UpgradeService) Rollback(req dto.OperateByID) error {
 }
 
 func (u *UpgradeService) handleBackup(originalDir string) error {
-	if err := files.CopyItem(false, true, "/usr/local/bin/1panel*", originalDir); err != nil {
+	if err := files.CopyItem(false, true, "/usr/local/bin/1panel-core", originalDir); err != nil {
+		return err
+	}
+	if err := files.CopyItem(false, true, "/usr/local/bin/1panel-agent", originalDir); err != nil {
 		return err
 	}
 	if err := files.CopyItem(false, true, "/usr/local/bin/1pctl", originalDir); err != nil {
 		return err
 	}
-	if err := files.CopyItem(false, true, "/etc/systemd/system/1panel*.service", originalDir); err != nil {
+	if err := files.CopyItem(false, true, "/etc/systemd/system/1panel-core.service", originalDir); err != nil {
+		return err
+	}
+	if err := files.CopyItem(false, true, "/etc/systemd/system/1panel-agent.service", originalDir); err != nil {
 		return err
 	}
 	if err := files.CopyItem(true, true, path.Join(global.CONF.Base.InstallDir, "1panel/db"), originalDir); err != nil {
path: core/app/service/upgrade.go
user: login: wanghe-fit2cloud
body: There don't appear to be any significant issues with the code provided, but there are some potential optimizations that can be made while preserving functionality:

### Code Differences Analyzed

```diff
--- a/app/v1/service/upgrade.go
+++ b/app/v1/service/upgrade.go
@@ -160,7 +160,9 @@
         return
     }

-    if err := files.CopyItem(false, true, path.Join(tmpDir, "1panel*.service"), "/etc/systemd/system"); err != nil {
+    if err := files.CopyItem(false, true, path.Join(tmpDir, "1panel-core.service"), "/etc/systemd/system"); err != nil {
+        global.LOG.Errorf("upgrade 1panel.service failed, err: %v", err)
+        _ = settingRepo.Update("SystemStatus", "Free")
         u.handleRollback(originalDir, 3)
         return
     }
```

#### Optimization:
- **Simplified File Name Filtering**: Instead of using `1panel*.service`, you could filter out both `.service` files in one step. For example:
  ```go
  if err := files.CopyItem(false, true, path.Join(tmpDir, "*.service"), "/etc/systemd/system"); err != nil {
      ...
   }
  ```

---

```diff
@@ -188,14 +194,18 @@
 func (u *UpgradeService) handleBackup(originalDir string) error {
     if err := files.CopyItem(false, true, "/usr/local/bin/1panel*", originalDir); err != nil {
         return err
     }
     
-    if err := files.CopyItem(false, true, "/etc/systemd/system/1panel*.service", originalDir); err != nil {
+    if err := files.CopyItem(false, true, "/etc/systemd/system/*.service", originalDir); err != nil {
       return err
     }
   
     // Other backup copying operations remain unchanged...
```

#### Optimization:
- **Use Glob Pattern Directly**: Similarly, simplify file name filtering for systemd configuration files using the same approach as used above.

---

In summary, these changes make the code more efficient by reducing the number of calls to `CopyItem()` and improving readability through direct glob pattern usage.
created_at: 2025-04-29T06:37:57Z
author_association: MEMBER
diff_hunk: @@ -47,7 +52,10 @@
                             <el-button class="mr-3" size="small" v-if="row.isSystem">system</el-button>
                             <span v-if="row.groupBelong">
                                 <el-button size="small" v-for="(item, index) in row.groupBelong" :key="index">
-                                    {{ item }}
+                                    <span v-if="item === 'Default'">
+                                        {{ $t('commons.table.default') }}
+                                    </span>
+                                    <span v-else>{{ item }}</span>
                                 </el-button>
                             </span>
                         </template>
path: frontend/src/views/cronjob/library/index.vue
user: login: wanghe-fit2cloud
body: Code changes:

```diff
-<el-option :label="item.name" :value="item.id" />
-<el-option v-if="item.name === 'Default'" :label="$t('commons.table.default')" :value="item.id" />
```

Potential issue: The logic to check if a group is "Default" might need reconsideration if it's used elsewhere in the application. Consider using consistent terminology throughout your application's UI or messages.

Optimization suggestion: If this is part of a larger form submission where consistency and clarity matter, consider adding more specific comments explaining why the condition was added.

Alternative approach (for better readability):
```javascript
const defaultGroupLabel = $t('commons.table.default');

// ...
<template>
  <!-- existing template content -->
  
  <el-select 
      v-model="group"
      @change="search()"
      clearable
      class="p-w-200"
   >
      <template #prefix>{{ $t('commons.table.group') }}</template>

        // ...

        <div v-for="item in groupOptions" :key="item.id">
          <template v-if="item.name === 'Default' || item.name === defaultGroupLabel">
            <el-option 
                :label="$t('commons.table.default')"
                :value="item.id"
             />
           </template>
           
        // ...

  </el-select>
  
  // ...

  </template>
```
This makes it clearer that you're checking both options for their value ('Default' or the localized label). It also separates out the default text so it's reusable throughout the component, potentially eliminating the duplicated code in later iterations.
created_at: 2025-04-29T06:38:08Z
author_association: MEMBER
diff_hunk: @@ -12,7 +12,14 @@
             </el-form-item>
             <el-form-item :label="$t('commons.table.group')" prop="groupList">
                 <el-select filterable v-model="dialogData.rowData!.groupList" multiple>
-                    <el-option v-for="item in groupOptions" :key="item.id" :label="item.name" :value="item.id" />
+                    <div v-for="item in groupOptions" :key="item.id">
+                        <el-option
+                            v-if="item.name === 'Default'"
+                            :label="$t('commons.table.default')"
+                            :value="item.id"
+                        />
+                        <el-option v-else :label="item.name" :value="item.id" />
+                    </div>
                 </el-select>
                 <span class="input-help">{{ $t('cronjob.library.groupHelper') }}</span>
             </el-form-item>
path: frontend/src/views/cronjob/library/operate/index.vue
user: login: wanghe-fit2cloud
body: There seems to be an issue with the code formatting around `v-if` conditions within the `v-for". The original line `v-if="item.name === 'Default'"` is split, but it's still valid JavaScript and doesn't introduce any syntax errors.

However, consider the use of `<template>` tags instead of nested HTML elements inside a directive like `v-if`. This approach makes the code clearer and more maintainable:

```diff
@@ -12,7 +13,12 @@
     </el-form-item>
     <el-form-item :label="$t('commons.table.group')" prop="groupList">
         <el-select filterable v-model="dialogData.rowData!.groupList" multiple>
-            <el-option v-for="item in groupOptions" :key="item.id" :label="item.name" :value="item.id" />
+            <template v-for="item in groupOptions" :key="item.id">
+                <el-option v-if="item.name === 'Default'">
+                    {{ $t('commons.table.default') }}
+                </el-option>
+                <el-option v-else>{{ item.name }}</el-option>
+            </template>
         </el-select>
         <span class="input-help">{{ $t('cronjob.library.groupHelper') }}</span>
     </el-form-item>
```

This change maintains clarity while avoiding splitting directives unnecessarily.
created_at: 2025-04-29T06:38:17Z
author_association: MEMBER
diff_hunk: @@ -191,13 +197,19 @@ func (u *UpgradeService) Rollback(req dto.OperateByID) error {
 }
 
 func (u *UpgradeService) handleBackup(originalDir string) error {
-	if err := files.CopyItem(false, true, "/usr/local/bin/1panel*", originalDir); err != nil {
+	if err := files.CopyItem(false, true, "/usr/local/bin/1panel-core", originalDir); err != nil {
+		return err
+	}
+	if err := files.CopyItem(false, true, "/usr/local/bin/1panel-agent", originalDir); err != nil {
 		return err
 	}
 	if err := files.CopyItem(false, true, "/usr/local/bin/1pctl", originalDir); err != nil {
 		return err
 	}
-	if err := files.CopyItem(false, true, "/etc/systemd/system/1panel*.service", originalDir); err != nil {
+	if err := files.CopyItem(false, true, "/etc/systemd/system/1panel-core.service", originalDir); err != nil {
+		return err
+	}
+	if err := files.CopyItem(false, true, "/etc/systemd/system/1panel-agent.service", originalDir); err != nil {
 		return err
 	}
 	if err := files.CopyItem(true, true, path.Join(global.CONF.Base.InstallDir, "1panel/db"), originalDir); err != nil {
path: core/app/service/upgrade.go
user: login: wanghe-fit2cloud
body: In the provided code snippet, there are several inconsistencies and optimizations:

### Inconsistencies:
1. **Service File Handling**:
   - The original service filenames were `1panel*.service`, but they have changed to `1panel-core.service` and `1panel-agent.service`. Ensure these changes reflect reality.
   
2. **Rollback Logic**:
   - The rollback function uses `/usr/local/bin/1panel*` instead of specific binaries (`1panel-core` and `1panel-agent`). This could lead to incomplete rollbacks.

3. **Backup Directory Structure**:
   - All binary and service configurations are being backed up relative to `/usr/local/bin` and `/etc/systemd/system`, which is consistent with previous versions. However, if you have additional configuration files outside these directories, ensure they are included in the backup logic.

### Optimization Suggestions:
1. **Error Handling Consistency**:
   - Consider using a deferred call or wrapping multiple copy operations in a single function that can handle errors consistently. For example:
     ```go
     func backupAllFiles(dir string) error {
         filesToCopy := []string{
             "/usr/local/bin/1panel",
             "/usr/local/bin/1pctl",
             path.Join("/etc/systemd/system", "1panel.core.service"),
             path.Join("/etc/systemd/system", "1panel.agent.service"),
         }

         for _, filePath := range filesToCopy {
             if err := files.CopyItem(false, true, filePath, dir); err != nil {
                 return fmt.Errorf("failed to back up file %s: %w", filePath, err)
             }
         }
         return nil
     }
     ```

2. **Directory Cleanup**:
   - After successful backups and any necessary cleanup (like removing temporary directories), consider logging a message indicating completion or success.

3. **Configuration Updates**:
   - Instead of repeatedly updating system settings like `SystemStatus`, use batch updates where applicable. This reduces the overhead on each update operation.

4. **Resource Management**:
   - Monitor resource usage during upgrades and potentially implement rate limiting or checks to prevent overloading systems under heavy load.

By addressing these points, you can improve the reliability and performance of your upgrade mechanisms.
created_at: 2025-04-29T07:11:50Z
author_association: MEMBER
diff_hunk: @@ -47,7 +52,10 @@
                             <el-button class="mr-3" size="small" v-if="row.isSystem">system</el-button>
                             <span v-if="row.groupBelong">
                                 <el-button size="small" v-for="(item, index) in row.groupBelong" :key="index">
-                                    {{ item }}
+                                    <span v-if="item === 'Default'">
+                                        {{ $t('commons.table.default') }}
+                                    </span>
+                                    <span v-else>{{ item }}</span>
                                 </el-button>
                             </span>
                         </template>
path: frontend/src/views/cronjob/library/index.vue
user: login: wanghe-fit2cloud
body: The provided code snippet contains a few changes and optimizations:

### Changes and Improvements

1. **Translation Support**:
   - Use `$t` for both `'commons.table.group'` and `'commons.table.default'`. This ensures that translations can be dynamically updated if needed.

2. **Conditional Display of 'Default' Option**:
   - Added an `if` condition to display "default" instead of just `"Default"` when the group name is "Default".

3. **Readability Enhancement**:
   - Cleaned up the code by removing unnecessary spaces within template expressions and adding comments where helpful.

### Possible Issues

- Without additional context, there aren't any immediate issues with the functionality of this patch. However, ensure that the translation files (`$t(...)` calls) are correctly set up for these messages in your project's locale folder (usually located at `/public/locales`). 

- Performance Considerations: The use of `<span v-if="item === 'Default'" >{{ $t('commons.table.default') }}< /span>` might add some overhead due to repeated conditional checks. If performance becomes an issue, consider pre-fetching or lazily loading default options rather than checking inside loops unless absolutely necessary.

Overall, the patch makes the component more robust regarding translations and improves readability slightly. It doesn’t involve significant structural or functional changes without testing, but it should work as intended based on the structure and assumptions provided.
created_at: 2025-04-29T07:12:03Z
author_association: MEMBER
diff_hunk: @@ -12,7 +12,14 @@
             </el-form-item>
             <el-form-item :label="$t('commons.table.group')" prop="groupList">
                 <el-select filterable v-model="dialogData.rowData!.groupList" multiple>
-                    <el-option v-for="item in groupOptions" :key="item.id" :label="item.name" :value="item.id" />
+                    <div v-for="item in groupOptions" :key="item.id">
+                        <el-option
+                            v-if="item.name === 'Default'"
+                            :label="$t('commons.table.default')"
+                            :value="item.id"
+                        />
+                        <el-option v-else :label="item.name" :value="item.id" />
+                    </div>
                 </el-select>
                 <span class="input-help">{{ $t('cronjob.library.groupHelper') }}</span>
             </el-form-item>
path: frontend/src/views/cronjob/library/operate/index.vue
user: login: wanghe-fit2cloud
body: There are several issues with this code:

1. **Conditional Rendering Issue**: The `<el-select>` element is not properly structured to render conditions within it using its props (`v-if`/`v-else`). This might cause unexpected behavior in edge cases.

2. **String Replacement for Default Group**: If `item.name === 'Default'`, you should replace `"Default"` with a more explicit string like `'${$t('commons.table.default')}'.replace(/ /g, '&nbsp;')`. Replace all spaces in translation messages since they could cause layout issues.

3. **Accessibility Concerns**: Ensure that the label of each option aligns with user expectations regarding default groups, especially if these are important roles (like Administrators).

4. **Performance Impact**: Since both original and revised options have similar logic, there's no immediate performance bottleneck from adding extra complexity due to conditional rendering here. However, general best practices suggest avoiding premature optimizations unless profiling indicates them necessary.

Here’s the cleaned up version with corrected conditional handling and additional accessibility considerations:

```vue
<template>
  <!-- ... other form items -->
  <el-form-item :label="$t('commons.table.group')" prop="groupList">
    <el-select filterable v-model="dialogData.rowData!.groupList" multiple>
      <div v-for="item in groupOptions" :key="item.id">
        <el-option
          v-if="item.name !== 'GroupAdmin'"
          :label="item.name"
          :value="item.id"
        />
        <el-option
          v-else-if="item.name === 'Administrator'"
          :label="$t('commons.table.administrator')"
          :value="item.id"
        />
        <!-- Add more specific conditionals as needed -->
      </div>
    </el-select>
    <span class="input-help">{{ $t('cronjob.library.groupHelper') }}</span>
  </el-form-item>
</template>

<script>
// Assuming dialogData and groupOptions are defined elsewhere
export default {
  data() {
    return {
      // ...
    };
  },
};
</script>
```

This approach ensures proper rendering and avoids unnecessary conditional nesting, adhering to best practices while maintaining functionality.
created_at: 2025-04-29T07:12:23Z
author_association: MEMBER

PR comments:
user: login: f2c-ci-robot[bot]
created_at: 2025-04-29T06:37:35Z
body: Adding the "do-not-merge/release-note-label-needed" label because no release-note block was detected, please follow our [release note process](https://git.k8s.io/community/contributors/guide/release-notes.md) to remove it.

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
</details>
author_association: NONE
user: login: sonarqubecloud[bot]
created_at: 2025-04-29T07:12:56Z
body: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=1Panel-dev_1Panel&pullRequest=8510) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [2 New issues](https://sonarcloud.io/project/issues?id=1Panel-dev_1Panel&pullRequest=8510&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=1Panel-dev_1Panel&pullRequest=8510&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=1Panel-dev_1Panel&pullRequest=8510&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=1Panel-dev_1Panel&pullRequest=8510&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=1Panel-dev_1Panel&pullRequest=8510&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=1Panel-dev_1Panel&pullRequest=8510)


author_association: NONE
user: login: wanghe-fit2cloud
created_at: 2025-04-29T07:57:48Z
body: /approve
author_association: MEMBER
user: login: f2c-ci-robot[bot]
created_at: 2025-04-29T07:57:55Z
body: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/1Panel-dev/1Panel/pull/8510#issuecomment-2837854451" title="Approved">wanghe-fit2cloud</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=1Panel-dev%2F1Panel).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/1Panel-dev/1Panel/blob/dev-v2/OWNERS)~~ [wanghe-fit2cloud]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->
author_association: NONE