From 75bfc3a1149d69e2b1d414d089a8c1bf88dda776 Mon Sep 17 00:00:00 2001 From: CityFun <31820853+zhengkunwang223@users.noreply.github.com> Date: Wed, 16 Jul 2025 15:52:04 +0800 Subject: [PATCH] feat: Scheduled tasks support backing up MySQL cluster databases (#9526) --- agent/app/service/app_utils.go | 5 +---- agent/app/service/cronjob_backup.go | 6 +++--- core/constant/common.go | 4 ++++ core/constant/status.go | 1 + core/i18n/lang/en.yaml | 4 +++- core/i18n/lang/ja.yaml | 4 +++- core/i18n/lang/ko.yaml | 4 +++- core/i18n/lang/ms.yml | 4 +++- core/i18n/lang/pt-BR.yaml | 4 +++- core/i18n/lang/ru.yaml | 4 +++- core/i18n/lang/tr.yaml | 4 +++- core/i18n/lang/zh-Hant.yaml | 4 +++- core/i18n/lang/zh.yaml | 4 +++- frontend/src/api/modules/app.ts | 5 +++-- frontend/src/components/status/index.vue | 1 + frontend/src/views/cronjob/cronjob/operate/index.vue | 2 ++ 16 files changed, 42 insertions(+), 18 deletions(-) diff --git a/agent/app/service/app_utils.go b/agent/app/service/app_utils.go index 1142141e6..0b2befa16 100644 --- a/agent/app/service/app_utils.go +++ b/agent/app/service/app_utils.go @@ -658,10 +658,7 @@ func upgradeInstall(req request.AppInstallUpgrade) error { } command := exec.Command("/bin/bash", "-c", fmt.Sprintf("cp -rn %s/* %s || true", detailDir, install.GetPath())) - stdout, _ := command.CombinedOutput() - if stdout != nil { - t.Logger.Printf("upgrade app [%s] [%s] cp file log : %s ", install.App.Key, install.Name, string(stdout)) - } + _, _ = command.CombinedOutput() sourceScripts := path.Join(detailDir, "scripts") if fileOp.Stat(sourceScripts) { dstScripts := path.Join(install.GetPath(), "scripts") diff --git a/agent/app/service/cronjob_backup.go b/agent/app/service/cronjob_backup.go index 78730e8aa..e478426e9 100644 --- a/agent/app/service/cronjob_backup.go +++ b/agent/app/service/cronjob_backup.go @@ -165,7 +165,7 @@ func (u *CronjobService) handleDatabase(cronjob model.Cronjob, startTime time.Ti backupDir := path.Join(global.Dir.TmpDir, fmt.Sprintf("database/%s/%s/%s", dbInfo.DBType, record.Name, dbInfo.Name)) record.FileName = fmt.Sprintf("db_%s_%s.sql.gz", dbInfo.Name, startTime.Format(constant.DateTimeSlimLayout)+common.RandStrAndNum(5)) - if cronjob.DBType == "mysql" || cronjob.DBType == "mariadb" { + if cronjob.DBType == "mysql" || cronjob.DBType == "mariadb" || cronjob.DBType == "mysql-cluster" { if err := doMysqlBackup(dbInfo, backupDir, record.FileName); err != nil { if retry < int(cronjob.RetryTimes) || !cronjob.IgnoreErr { retry++ @@ -354,7 +354,7 @@ type DatabaseHelper struct { func loadDbsForJob(cronjob model.Cronjob) []DatabaseHelper { var dbs []DatabaseHelper if cronjob.DBName == "all" { - if cronjob.DBType == "mysql" || cronjob.DBType == "mariadb" { + if cronjob.DBType == "mysql" || cronjob.DBType == "mariadb" || cronjob.DBType == "mysql-cluster" { databaseService := NewIDatabaseService() mysqlItems, _ := databaseService.LoadItems(cronjob.DBType) for _, mysql := range mysqlItems { @@ -381,7 +381,7 @@ func loadDbsForJob(cronjob model.Cronjob) []DatabaseHelper { dbNames := strings.Split(cronjob.DBName, ",") for _, name := range dbNames { itemID, _ := strconv.Atoi(name) - if cronjob.DBType == "mysql" || cronjob.DBType == "mariadb" { + if cronjob.DBType == "mysql" || cronjob.DBType == "mariadb" || cronjob.DBType == "mysql-cluster" { mysqlItem, _ := mysqlRepo.Get(repo.WithByID(uint(itemID))) dbs = append(dbs, DatabaseHelper{ ID: mysqlItem.ID, diff --git a/core/constant/common.go b/core/constant/common.go index 8f916b372..d904c3a79 100644 --- a/core/constant/common.go +++ b/core/constant/common.go @@ -164,6 +164,10 @@ var WebUrlMap = map[string]struct{}{ "/xpack/node": {}, "/xpack/exchange/file": {}, "/xpack/app": {}, + + "/xpack/cluster/mysql": {}, + "/xpack/cluster/postgres": {}, + "/xpack/cluster/redis": {}, } var DynamicRoutes = []string{ diff --git a/core/constant/status.go b/core/constant/status.go index 39b8eb7fd..5c39a3c21 100644 --- a/core/constant/status.go +++ b/core/constant/status.go @@ -23,4 +23,5 @@ const ( StatusInstalling = "Installing" StatusNormal = "Normal" StatusDeleted = "Deleted" + StatusLoading = "Loading" ) diff --git a/core/i18n/lang/en.yaml b/core/i18n/lang/en.yaml index 93fad7599..d1b8d073f 100644 --- a/core/i18n/lang/en.yaml +++ b/core/i18n/lang/en.yaml @@ -201,4 +201,6 @@ ErrExpiredToken: 'Token has expired, please reset and scan again.' #cluster ErrMasterDelete: "Unable to delete the master node, please delete the slave nodes first." -ClusterNameIsExist: "Cluster name already exists." \ No newline at end of file +ClusterNameIsExist: "Cluster name already exists." +AppStatusUnHealthy: "Application status acquisition is abnormal, please check the installation node status in the node list." +MasterNodePortNotAvailable: "Node {{ .name }} port {{ .port }} connectivity verification failed, please check firewall/security group settings and master node status." \ No newline at end of file diff --git a/core/i18n/lang/ja.yaml b/core/i18n/lang/ja.yaml index fb81b4fea..2421cdad4 100644 --- a/core/i18n/lang/ja.yaml +++ b/core/i18n/lang/ja.yaml @@ -202,4 +202,6 @@ ErrExpiredToken: 'トークンの有効期限が切れました。リセット #cluster ErrMasterDelete: "マスターノードを削除できません。スレーブノードを先に削除してください。" -ClusterNameIsExist: "クラスタ名は既に存在します。" \ No newline at end of file +ClusterNameIsExist: "クラスタ名は既に存在します。" +AppStatusUnHealthy: "アプリケーションのステータス取得が異常です。ノードリストでインストールノードのステータスを確認してください。" +MasterNodePortNotAvailable: "ノード {{ .name }} のポート {{ .port }} の接続性検証が失敗しました。ファイアウォール/セキュリティグループの設定とマスターノードのステータスを確認してください。" \ No newline at end of file diff --git a/core/i18n/lang/ko.yaml b/core/i18n/lang/ko.yaml index 096c2a521..b5a37712d 100644 --- a/core/i18n/lang/ko.yaml +++ b/core/i18n/lang/ko.yaml @@ -201,4 +201,6 @@ ErrExpiredToken: '토큰이 만료되었습니다. 재설정 후 다시 스캔 #cluster ErrMasterDelete: "마스터 노드를 삭제할 수 없습니다. 슬레이브 노드를 먼저 삭제하세요." -ClusterNameIsExist: "클러스터 이름이 이미 존재합니다." \ No newline at end of file +ClusterNameIsExist: "클러스터 이름이 이미 존재합니다." +AppStatusUnHealthy: "애플리케이션 상태 획득이 비정상입니다. 노드 목록에서 설치 노드 상태를 확인하세요." +MasterNodePortNotAvailable: "노드 {{ .name }} 포트 {{ .port }} 연결성 검증에 실패했습니다. 방화벽/보안 그룹 설정 및 마스터 노드 상태를 확인하세요." \ No newline at end of file diff --git a/core/i18n/lang/ms.yml b/core/i18n/lang/ms.yml index 9ad888b93..a6ebb80ae 100644 --- a/core/i18n/lang/ms.yml +++ b/core/i18n/lang/ms.yml @@ -196,4 +196,6 @@ SystemMode: "Mod: " #cluster ErrMasterDelete: "Tidak dapat menghapus nod utama, sila hapuskan nod perantara dahulu." -ClusterNameIsExist: "Nama kluster sudah wujud." \ No newline at end of file +ClusterNameIsExist: "Nama kluster sudah wujud." +AppStatusUnHealthy: "Pengambilan status aplikasi tidak normal, sila periksa status nod pemasangan dalam senarai nod." +MasterNodePortNotAvailable: "Pengesahan kesambungan pelabuhan {{ .name }} nod {{ .port }} gagal, sila periksa tetapan firewall/kumpulan keselamatan dan status nod utama." \ No newline at end of file diff --git a/core/i18n/lang/pt-BR.yaml b/core/i18n/lang/pt-BR.yaml index 214599468..61a53076d 100644 --- a/core/i18n/lang/pt-BR.yaml +++ b/core/i18n/lang/pt-BR.yaml @@ -201,4 +201,6 @@ ErrExpiredToken: 'O token expirou, por favor, reinicie e escaneie novamente.' #cluster ErrMasterDelete: "Não é possível excluir o nó mestre, exclua os nós escravos primeiro." -ClusterNameIsExist: "O nome do cluster já existe." \ No newline at end of file +ClusterNameIsExist: "O nome do cluster já existe." +AppStatusUnHealthy: "A aquisição do status do aplicativo está anormal, verifique o status dos nós de instalação na lista de nós." +MasterNodePortNotAvailable: "A verificação de conectividade da porta {{ .port }} do nó {{ .name }} falhou, verifique as configurações de firewall/grupo de segurança e o status do nó mestre." \ No newline at end of file diff --git a/core/i18n/lang/ru.yaml b/core/i18n/lang/ru.yaml index d28974088..287d4d29e 100644 --- a/core/i18n/lang/ru.yaml +++ b/core/i18n/lang/ru.yaml @@ -201,4 +201,6 @@ ErrExpiredToken: 'Токен истек, пожалуйста, сбросьте #cluster ErrMasterDelete: "Невозможно удалить основной узел, сначала удалите подчиненные узлы." -ClusterNameIsExist: "Имя кластера уже существует." \ No newline at end of file +ClusterNameIsExist: "Имя кластера уже существует." +AppStatusUnHealthy: "Получение статуса приложения аномально, пожалуйста, проверьте статус узлов установки в списке узлов." +MasterNodePortNotAvailable: "Проверка подключения порта {{ .port }} узла {{ .name }} не удалась, пожалуйста, проверьте настройки брандмауэра/группы безопасности и статус главного узла." \ No newline at end of file diff --git a/core/i18n/lang/tr.yaml b/core/i18n/lang/tr.yaml index ebe08a755..3c0d6a827 100644 --- a/core/i18n/lang/tr.yaml +++ b/core/i18n/lang/tr.yaml @@ -199,4 +199,6 @@ ErrExpiredToken: 'Token süresi dolmuş, lütfen sıfırlayın ve tekrar tarayı #cluster ErrMasterDelete: "Ana düğümü silinemiyor, lütfen önce alt düğümleri silin." -ClusterNameIsExist: "Küme adı zaten var." \ No newline at end of file +ClusterNameIsExist: "Küme adı zaten var." +AppStatusUnHealthy: "Uygulama durumu alımı anormal, lütfen düğüm listesindeki yükleme düğümü durumunu kontrol edin." +MasterNodePortNotAvailable: "Düğüm {{ .name }} portu {{ .port }} bağlantı doğrulaması başarısız oldu, lütfen güvenlik duvarı/güvenlik grubu ayarlarını ve ana düğüm durumunu kontrol edin." \ No newline at end of file diff --git a/core/i18n/lang/zh-Hant.yaml b/core/i18n/lang/zh-Hant.yaml index da459d8ed..c51ab4729 100644 --- a/core/i18n/lang/zh-Hant.yaml +++ b/core/i18n/lang/zh-Hant.yaml @@ -210,4 +210,6 @@ ErrExpiredToken: '令牌過期,請重設後再次掃碼' #cluster ErrMasterDelete: "無法刪除主節點,請先刪除從節點。" -ClusterNameIsExist: "集群名稱已存在。" \ No newline at end of file +ClusterNameIsExist: "集群名稱已存在。" +AppStatusUnHealthy: "應用獲取狀態異常,請在節點列表檢查安裝節點狀態。" +MasterNodePortNotAvailable: "節點 {{ .name }} 端口 {{ .port }} 連通性校驗失敗,請檢查防火牆/安全組設置和主節點狀態。" \ No newline at end of file diff --git a/core/i18n/lang/zh.yaml b/core/i18n/lang/zh.yaml index 05dddee2f..d52d76b7b 100644 --- a/core/i18n/lang/zh.yaml +++ b/core/i18n/lang/zh.yaml @@ -210,4 +210,6 @@ ErrExpiredToken: '令牌过期,请重置后再次扫码' #cluster ErrMasterDelete: "无法删除主节点,请先删除从节点" -ClusterNameIsExist: "集群名称已存在" \ No newline at end of file +ClusterNameIsExist: "集群名称已存在" +AppStatusUnHealthy: "应用获取状态异常,请在节点列表检查安装节点状态" +MasterNodePortNotAvailable: "节点 {{ .name }} 端口 {{ .port }} 连通性校验失败,请检查防火墙/安全组设置和主节点状态" \ No newline at end of file diff --git a/frontend/src/api/modules/app.ts b/frontend/src/api/modules/app.ts index c05e2d746..7f1af01ca 100644 --- a/frontend/src/api/modules/app.ts +++ b/frontend/src/api/modules/app.ts @@ -73,8 +73,9 @@ export const getAppInstalled = (search: App.AppInstalledSearch) => { return http.post>('apps/installed/search', search); }; -export const getAppInstalledByID = (installID: number, node: string) => { - return http.get(`apps/installed/info/${installID}?operateNode=${node}`); +export const getAppInstalledByID = (installID: number, node?: string) => { + const params = node ? `?operateNode=${node}` : ''; + return http.get(`apps/installed/info/${installID}${params}`); }; export const installedOp = (op: App.AppInstalledOp) => { diff --git a/frontend/src/components/status/index.vue b/frontend/src/components/status/index.vue index e4ee7ba8e..c09ef3d05 100644 --- a/frontend/src/components/status/index.vue +++ b/frontend/src/components/status/index.vue @@ -99,6 +99,7 @@ const loadingStatus = [ 'sending', 'waiting', 'executing', + 'loading', ]; const stopStatus = ['stopped', 'exited', 'disable']; diff --git a/frontend/src/views/cronjob/cronjob/operate/index.vue b/frontend/src/views/cronjob/cronjob/operate/index.vue index 10ee2b67a..2a53729dd 100644 --- a/frontend/src/views/cronjob/cronjob/operate/index.vue +++ b/frontend/src/views/cronjob/cronjob/operate/index.vue @@ -282,8 +282,10 @@ + +