From 6427337728f5e7782b273a2aaa8188dc3616cf04 Mon Sep 17 00:00:00 2001 From: CityFun <31820853+zhengkunwang223@users.noreply.github.com> Date: Mon, 21 Jul 2025 17:56:42 +0800 Subject: [PATCH] feat: Adapt to Redis Cluster (#9588) --- agent/app/api/v2/backup.go | 6 ++--- agent/app/api/v2/database_redis.go | 8 +++--- agent/app/dto/database.go | 2 ++ agent/app/service/backup_redis.go | 15 ++++++----- agent/app/service/database_redis.go | 26 +++++++++---------- frontend/src/api/interface/database.ts | 1 + frontend/src/api/modules/app.ts | 5 ++-- frontend/src/api/modules/database.ts | 11 +++++--- frontend/src/utils/app.ts | 2 +- frontend/src/views/database/redis/index.vue | 2 +- .../views/database/redis/setting/index.vue | 12 ++++++--- .../redis/setting/persistence/index.vue | 11 +++++--- 12 files changed, 59 insertions(+), 42 deletions(-) diff --git a/agent/app/api/v2/backup.go b/agent/app/api/v2/backup.go index 7f003923e..e008c0fab 100644 --- a/agent/app/api/v2/backup.go +++ b/agent/app/api/v2/backup.go @@ -421,12 +421,12 @@ func (b *BaseApi) Recover(c *gin.Context) { } req.File = downloadPath switch req.Type { - case "mysql", "mariadb": + case "mysql", "mariadb", constant.AppMysqlCluster: if err := backupService.MysqlRecover(req); err != nil { helper.InternalServer(c, err) return } - case constant.AppPostgresql: + case constant.AppPostgresql, constant.AppPostgresqlCluster: if err := backupService.PostgresqlRecover(req); err != nil { helper.InternalServer(c, err) return @@ -436,7 +436,7 @@ func (b *BaseApi) Recover(c *gin.Context) { helper.InternalServer(c, err) return } - case "redis": + case "redis", constant.AppRedisCluster: if err := backupService.RedisRecover(req); err != nil { helper.InternalServer(c, err) return diff --git a/agent/app/api/v2/database_redis.go b/agent/app/api/v2/database_redis.go index 2a0657089..83033c8d8 100644 --- a/agent/app/api/v2/database_redis.go +++ b/agent/app/api/v2/database_redis.go @@ -33,13 +33,13 @@ func (b *BaseApi) LoadRedisStatus(c *gin.Context) { // @Tags Database Redis // @Summary Load redis conf // @Accept json -// @Param request body dto.OperationWithName true "request" +// @Param request body dto.LoadRedisStatus true "request" // @Success 200 {object} dto.RedisConf // @Security ApiKeyAuth // @Security Timestamp // @Router /databases/redis/conf [post] func (b *BaseApi) LoadRedisConf(c *gin.Context) { - var req dto.OperationWithName + var req dto.LoadRedisStatus if err := helper.CheckBind(&req, c); err != nil { return } @@ -55,13 +55,13 @@ func (b *BaseApi) LoadRedisConf(c *gin.Context) { // @Tags Database Redis // @Summary Load redis persistence conf // @Accept json -// @Param request body dto.OperationWithName true "request" +// @Param request body dto.LoadRedisStatus true "request" // @Success 200 {object} dto.RedisPersistence // @Security ApiKeyAuth // @Security Timestamp // @Router /databases/redis/persistence/conf [post] func (b *BaseApi) LoadPersistenceConf(c *gin.Context) { - var req dto.OperationWithName + var req dto.LoadRedisStatus if err := helper.CheckBind(&req, c); err != nil { return } diff --git a/agent/app/dto/database.go b/agent/app/dto/database.go index 79bfb85d5..022f1b180 100644 --- a/agent/app/dto/database.go +++ b/agent/app/dto/database.go @@ -174,6 +174,7 @@ type RedisConfUpdate struct { Timeout string `json:"timeout"` Maxclients string `json:"maxclients"` Maxmemory string `json:"maxmemory"` + DBType string `json:"dbType" validate:"required,oneof=redis redis-cluster"` } type RedisConfPersistenceUpdate struct { Database string `json:"database" validate:"required"` @@ -181,6 +182,7 @@ type RedisConfPersistenceUpdate struct { Appendonly string `json:"appendonly"` Appendfsync string `json:"appendfsync"` Save string `json:"save"` + DBType string `json:"dbType" validate:"required,oneof=redis redis-cluster"` } type RedisConf struct { diff --git a/agent/app/service/backup_redis.go b/agent/app/service/backup_redis.go index cc6736290..441f2aa5e 100644 --- a/agent/app/service/backup_redis.go +++ b/agent/app/service/backup_redis.go @@ -23,7 +23,7 @@ import ( ) func (u *BackupService) RedisBackup(req dto.CommonBackup) error { - redisInfo, err := appInstallRepo.LoadBaseInfo("redis", req.Name) + redisInfo, err := appInstallRepo.LoadBaseInfo(req.Type, req.Name) if err != nil { return err } @@ -64,7 +64,7 @@ func (u *BackupService) RedisBackup(req dto.CommonBackup) error { } func (u *BackupService) RedisRecover(req dto.CommonRecover) error { - redisInfo, err := appInstallRepo.LoadBaseInfo("redis", req.Name) + redisInfo, err := appInstallRepo.LoadBaseInfo(req.Type, req.Name) if err != nil { return err } @@ -76,6 +76,7 @@ func (u *BackupService) RedisRecover(req dto.CommonRecover) error { } func handleRedisBackup(redisInfo *repo.RootInfo, parentTask *task.Task, backupDir, fileName, secret, taskID string) error { + var ( err error itemTask *task.Task @@ -102,7 +103,7 @@ func handleRedisBackup(redisInfo *repo.RootInfo, parentTask *task.Task, backupDi } if strings.HasSuffix(fileName, ".tar.gz") { - redisDataDir := fmt.Sprintf("%s/%s/%s/data/appendonlydir", global.Dir.AppInstallDir, "redis", redisInfo.Name) + redisDataDir := fmt.Sprintf("%s/%s/%s/data/appendonlydir", global.Dir.AppInstallDir, redisInfo.Key, redisInfo.Name) if err := fileOp.TarGzCompressPro(true, redisDataDir, path.Join(backupDir, fileName), secret, ""); err != nil { return err } @@ -111,14 +112,14 @@ func handleRedisBackup(redisInfo *repo.RootInfo, parentTask *task.Task, backupDi if strings.HasSuffix(fileName, ".aof") { stdout1, err := cmd.RunDefaultWithStdoutBashCf("docker cp %s:/data/appendonly.aof %s/%s", redisInfo.ContainerName, backupDir, fileName) if err != nil { - return errors.New(string(stdout1)) + return errors.New(stdout1) } return nil } stdout1, err1 := cmd.RunDefaultWithStdoutBashCf("docker cp %s:/data/dump.rdb %s/%s", redisInfo.ContainerName, backupDir, fileName) if err1 != nil { - return errors.New(string(stdout1)) + return errors.New(stdout1) } return nil } @@ -196,12 +197,12 @@ func handleRedisRecover(redisInfo *repo.RootInfo, parentTask *task.Task, recover } }() } - composeDir := fmt.Sprintf("%s/redis/%s", global.Dir.AppInstallDir, redisInfo.Name) + composeDir := fmt.Sprintf("%s/%s/%s", global.Dir.AppInstallDir, redisInfo.Key, redisInfo.Name) if _, err := compose.Down(composeDir + "/docker-compose.yml"); err != nil { return err } if appendonly == "yes" && strings.HasPrefix(redisInfo.Version, "7.") { - redisDataDir := fmt.Sprintf("%s/%s/%s/data", global.Dir.AppInstallDir, "redis", redisInfo.Name) + redisDataDir := fmt.Sprintf("%s/%s/%s/data", global.Dir.AppInstallDir, redisInfo.Key, redisInfo.Name) if err := fileOp.TarGzExtractPro(recoverFile, redisDataDir, secret); err != nil { return err } diff --git a/agent/app/service/database_redis.go b/agent/app/service/database_redis.go index 57ef7a96c..f8b3fbbc7 100644 --- a/agent/app/service/database_redis.go +++ b/agent/app/service/database_redis.go @@ -29,8 +29,8 @@ type IRedisService interface { ChangePassword(info dto.ChangeRedisPass) error LoadStatus(req dto.LoadRedisStatus) (*dto.RedisStatus, error) - LoadConf(req dto.OperationWithName) (*dto.RedisConf, error) - LoadPersistenceConf(req dto.OperationWithName) (*dto.RedisPersistence, error) + LoadConf(req dto.LoadRedisStatus) (*dto.RedisConf, error) + LoadPersistenceConf(req dto.LoadRedisStatus) (*dto.RedisPersistence, error) CheckHasCli() bool InstallCli() error @@ -41,7 +41,7 @@ func NewIRedisService() IRedisService { } func (u *RedisService) UpdateConf(req dto.RedisConfUpdate) error { - redisInfo, err := appInstallRepo.LoadBaseInfo("redis", req.Database) + redisInfo, err := appInstallRepo.LoadBaseInfo(req.DBType, req.Database) if err != nil { return err } @@ -50,7 +50,7 @@ func (u *RedisService) UpdateConf(req dto.RedisConfUpdate) error { confs = append(confs, redisConfig{key: "timeout", value: req.Timeout}) confs = append(confs, redisConfig{key: "maxclients", value: req.Maxclients}) confs = append(confs, redisConfig{key: "maxmemory", value: req.Maxmemory}) - if err := confSet(redisInfo.Name, "", confs); err != nil { + if err := confSet(redisInfo.Name, req.DBType, "", confs); err != nil { return err } if _, err := compose.Restart(fmt.Sprintf("%s/redis/%s/docker-compose.yml", global.Dir.AppInstallDir, redisInfo.Name)); err != nil { @@ -107,7 +107,7 @@ func (u *RedisService) ChangePassword(req dto.ChangeRedisPass) error { } func (u *RedisService) UpdatePersistenceConf(req dto.RedisConfPersistenceUpdate) error { - redisInfo, err := appInstallRepo.LoadBaseInfo("redis", req.Database) + redisInfo, err := appInstallRepo.LoadBaseInfo(req.DBType, req.Database) if err != nil { return err } @@ -119,10 +119,10 @@ func (u *RedisService) UpdatePersistenceConf(req dto.RedisConfPersistenceUpdate) confs = append(confs, redisConfig{key: "appendonly", value: req.Appendonly}) confs = append(confs, redisConfig{key: "appendfsync", value: req.Appendfsync}) } - if err := confSet(redisInfo.Name, req.Type, confs); err != nil { + if err := confSet(redisInfo.Name, req.DBType, req.Type, confs); err != nil { return err } - if _, err := compose.Restart(fmt.Sprintf("%s/redis/%s/docker-compose.yml", global.Dir.AppInstallDir, redisInfo.Name)); err != nil { + if _, err := compose.Restart(fmt.Sprintf("%s/%s/%s/docker-compose.yml", global.Dir.AppInstallDir, req.DBType, redisInfo.Name)); err != nil { return err } @@ -157,8 +157,8 @@ func (u *RedisService) LoadStatus(req dto.LoadRedisStatus) (*dto.RedisStatus, er return &info, nil } -func (u *RedisService) LoadConf(req dto.OperationWithName) (*dto.RedisConf, error) { - redisInfo, err := appInstallRepo.LoadBaseInfo("redis", req.Name) +func (u *RedisService) LoadConf(req dto.LoadRedisStatus) (*dto.RedisConf, error) { + redisInfo, err := appInstallRepo.LoadBaseInfo(req.Type, req.Name) if err != nil { return nil, err } @@ -174,8 +174,8 @@ func (u *RedisService) LoadConf(req dto.OperationWithName) (*dto.RedisConf, erro return &item, nil } -func (u *RedisService) LoadPersistenceConf(req dto.OperationWithName) (*dto.RedisPersistence, error) { - redisInfo, err := appInstallRepo.LoadBaseInfo("redis", req.Name) +func (u *RedisService) LoadPersistenceConf(req dto.LoadRedisStatus) (*dto.RedisPersistence, error) { + redisInfo, err := appInstallRepo.LoadBaseInfo(req.Type, req.Name) if err != nil { return nil, err } @@ -214,8 +214,8 @@ type redisConfig struct { value string } -func confSet(redisName string, updateType string, changeConf []redisConfig) error { - path := fmt.Sprintf("%s/redis/%s/conf/redis.conf", global.Dir.AppInstallDir, redisName) +func confSet(redisName string, redisType string, updateType string, changeConf []redisConfig) error { + path := fmt.Sprintf("%s/%s/%s/conf/redis.conf", global.Dir.AppInstallDir, redisType, redisName) lineBytes, err := os.ReadFile(path) if err != nil { return err diff --git a/frontend/src/api/interface/database.ts b/frontend/src/api/interface/database.ts index 3bb23913d..fd69c224b 100644 --- a/frontend/src/api/interface/database.ts +++ b/frontend/src/api/interface/database.ts @@ -232,6 +232,7 @@ export namespace Database { appendonly: string; appendfsync: string; save: string; + dbType: string; } export interface RedisStatus { tcp_port: string; diff --git a/frontend/src/api/modules/app.ts b/frontend/src/api/modules/app.ts index 7f1af01ca..eb31ac938 100644 --- a/frontend/src/api/modules/app.ts +++ b/frontend/src/api/modules/app.ts @@ -78,8 +78,9 @@ export const getAppInstalledByID = (installID: number, node?: string) => { return http.get(`apps/installed/info/${installID}${params}`); }; -export const installedOp = (op: App.AppInstalledOp) => { - return http.post('apps/installed/op', op, TimeoutEnum.T_40S); +export const installedOp = (op: App.AppInstalledOp, node?: string) => { + const params = node ? `?operateNode=${node}` : ''; + return http.post(`apps/installed/op${params}`, op, TimeoutEnum.T_40S); }; export const syncInstalledApp = () => { diff --git a/frontend/src/api/modules/database.ts b/frontend/src/api/modules/database.ts index 7fafde8f6..f7f3bcff5 100644 --- a/frontend/src/api/modules/database.ts +++ b/frontend/src/api/modules/database.ts @@ -111,11 +111,14 @@ export const loadRemoteAccess = (type: string, database: string) => { export const loadRedisStatus = (type: string, database: string) => { return http.post(`/databases/redis/status`, { type: type, name: database }); }; -export const loadRedisConf = (database: string) => { - return http.post(`/databases/redis/conf`, { name: database }); +export const loadRedisConf = (type: string, database: string) => { + return http.post(`/databases/redis/conf`, { type: type, name: database }); }; -export const redisPersistenceConf = (database: string) => { - return http.post(`/databases/redis/persistence/conf`, { name: database }); +export const redisPersistenceConf = (type: string, database: string) => { + return http.post(`/databases/redis/persistence/conf`, { + type: type, + name: database, + }); }; export const checkRedisCli = () => { return http.get(`/databases/redis/check`); diff --git a/frontend/src/utils/app.ts b/frontend/src/utils/app.ts index 2799846bf..d7edb173b 100644 --- a/frontend/src/utils/app.ts +++ b/frontend/src/utils/app.ts @@ -19,7 +19,7 @@ export const jumpToInstall = (type: string, key: string) => { case 'redis-cluster': jumpToPath(router, '/xpack/cluster/redis'); return true; - case 'postgres-cluster': + case 'postgresql-cluster': jumpToPath(router, '/xpack/cluster/postgres'); return true; } diff --git a/frontend/src/views/database/redis/index.vue b/frontend/src/views/database/redis/index.vue index ed4d16e81..7ea1ad0eb 100644 --- a/frontend/src/views/database/redis/index.vue +++ b/frontend/src/views/database/redis/index.vue @@ -13,7 +13,7 @@