diff --git a/backend/app/api/v1/database_redis.go b/backend/app/api/v1/database_redis.go index 900f2130c..bf0abf5e4 100644 --- a/backend/app/api/v1/database_redis.go +++ b/backend/app/api/v1/database_redis.go @@ -11,6 +11,7 @@ import ( "github.com/1Panel-dev/1Panel/backend/app/dto" "github.com/1Panel-dev/1Panel/backend/constant" "github.com/1Panel-dev/1Panel/backend/global" + "github.com/1Panel-dev/1Panel/backend/utils/compose" "github.com/1Panel-dev/1Panel/backend/utils/docker" "github.com/1Panel-dev/1Panel/backend/utils/terminal" "github.com/docker/docker/api/types" @@ -65,6 +66,86 @@ func (b *BaseApi) UpdateRedisConf(c *gin.Context) { helper.SuccessWithData(c, nil) } +func (b *BaseApi) UpdateRedisPersistenceConf(c *gin.Context) { + var req dto.RedisConfPersistenceUpdate + if err := c.ShouldBindJSON(&req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + if err := global.VALID.Struct(req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + if err := redisService.UpdatePersistenceConf(req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + helper.SuccessWithData(c, nil) +} + +func (b *BaseApi) RedisBackup(c *gin.Context) { + if err := redisService.Backup(); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + helper.SuccessWithData(c, nil) +} + +func (b *BaseApi) RedisRecover(c *gin.Context) { + var req dto.RedisBackupRecover + if err := c.ShouldBindJSON(&req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + if err := global.VALID.Struct(req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + + if err := redisService.Recover(req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + helper.SuccessWithData(c, nil) +} + +func (b *BaseApi) RedisBackupList(c *gin.Context) { + var req dto.PageInfo + if err := c.ShouldBindJSON(&req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + + total, list, err := redisService.SearchBackupListWithPage(req) + if err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + + helper.SuccessWithData(c, dto.PageResult{ + Items: list, + Total: total, + }) +} + +func (b *BaseApi) RedisBackupDelete(c *gin.Context) { + var req dto.RedisBackupDelete + if err := c.ShouldBindJSON(&req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + + for _, name := range req.Names { + fullPath := fmt.Sprintf("%s/%s", req.FileDir, name) + if err := os.Remove(fullPath); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + } + + helper.SuccessWithData(c, nil) +} + func (b *BaseApi) UpdateRedisConfByFile(c *gin.Context) { var req dto.RedisConfUpdateByFile if err := c.ShouldBindJSON(&req); err != nil { @@ -76,7 +157,7 @@ func (b *BaseApi) UpdateRedisConfByFile(c *gin.Context) { helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) return } - path := fmt.Sprintf("/opt/1Panel/data/apps/redis/%s/conf/redis.conf", redisInfo.Name) + path := fmt.Sprintf("%s/redis/%s/conf/redis.conf", constant.AppInstallDir, redisInfo.Name) file, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0640) if err != nil { helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) @@ -87,6 +168,14 @@ func (b *BaseApi) UpdateRedisConfByFile(c *gin.Context) { _, _ = write.WriteString(req.File) write.Flush() + if req.RestartNow { + composeDir := fmt.Sprintf("%s/redis/%s/docker-compose.yml", constant.AppInstallDir, redisInfo.Name) + if _, err := compose.Restart(composeDir); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + } + helper.SuccessWithData(c, nil) } diff --git a/backend/app/dto/database.go b/backend/app/dto/database.go index c36eeea2d..8abe74f18 100644 --- a/backend/app/dto/database.go +++ b/backend/app/dto/database.go @@ -145,12 +145,18 @@ type RecoverDB struct { type RedisConfUpdate struct { Timeout string `json:"timeout"` Maxclients string `json:"maxclients"` - Databases string `json:"databases"` Requirepass string `json:"requirepass"` Maxmemory string `json:"maxmemory"` } +type RedisConfPersistenceUpdate struct { + Type string `json:"type" validate:"required,oneof=aof rbd"` + Appendonly string `json:"appendonly"` + Appendfsync string `json:"appendfsync"` + Save string `json:"save"` +} type RedisConfUpdateByFile struct { - File string `json:"file"` + File string `json:"file"` + RestartNow bool `json:"restartNow"` } type RedisConf struct { @@ -158,13 +164,11 @@ type RedisConf struct { ContainerName string `json:"containerName"` Timeout string `json:"timeout"` Maxclients string `json:"maxclients"` - Databases string `json:"databases"` Requirepass string `json:"requirepass"` Maxmemory string `json:"maxmemory"` } type RedisPersistence struct { - Dir string `json:"dir"` Appendonly string `json:"appendonly"` Appendfsync string `json:"appendfsync"` Save string `json:"save"` @@ -185,3 +189,18 @@ type RedisStatus struct { KeyspaceMisses string `json:"keyspace_misses"` LatestForkUsec string `json:"latest_fork_usec"` } + +type RedisBackupRecords struct { + FileName string `json:"fileName"` + FileDir string `json:"fileDir"` + CreatedAt string `json:"createdAt"` + Size int `json:"size"` +} +type RedisBackupRecover struct { + FileName string `json:"fileName"` + FileDir string `json:"fileDir"` +} +type RedisBackupDelete struct { + FileDir string `json:"fileDir"` + Names []string `json:"names"` +} diff --git a/backend/app/repo/databse_mysql.go b/backend/app/repo/databse_mysql.go index 17a5bb616..5cbbcc94a 100644 --- a/backend/app/repo/databse_mysql.go +++ b/backend/app/repo/databse_mysql.go @@ -22,7 +22,7 @@ type IMysqlRepo interface { LoadRunningVersion(keys []string) ([]string, error) LoadBaseInfoByName(name string) (*RootInfo, error) LoadRedisBaseInfo() (*RootInfo, error) - UpdateMysqlConf(id uint, vars map[string]interface{}) error + UpdateDatabasePassword(id uint, vars map[string]interface{}) error } func NewIMysqlRepo() IMysqlRepo { @@ -181,7 +181,7 @@ func (u *MysqlRepo) Update(id uint, vars map[string]interface{}) error { return global.DB.Model(&model.DatabaseMysql{}).Where("id = ?", id).Updates(vars).Error } -func (u *MysqlRepo) UpdateMysqlConf(id uint, vars map[string]interface{}) error { +func (u *MysqlRepo) UpdateDatabasePassword(id uint, vars map[string]interface{}) error { if err := global.DB.Model(&model.AppInstall{}).Where("id = ?", id).Updates(vars).Error; err != nil { return err } diff --git a/backend/app/service/database_mysql.go b/backend/app/service/database_mysql.go index 4a5a3ab78..0e6277492 100644 --- a/backend/app/service/database_mysql.go +++ b/backend/app/service/database_mysql.go @@ -221,7 +221,7 @@ func (u *MysqlService) ChangeInfo(info dto.ChangeDBInfo) error { } } } - _ = mysqlRepo.UpdateMysqlConf(app.ID, map[string]interface{}{ + _ = mysqlRepo.UpdateDatabasePassword(app.ID, map[string]interface{}{ "param": strings.ReplaceAll(app.Param, app.Password, info.Value), "env": strings.ReplaceAll(app.Env, app.Password, info.Value), }) @@ -444,8 +444,8 @@ func excuteSql(containerName, password, command string) error { return nil } -func backupMysql(backupType, baseDir, backupDir, mysqkName, dbName, fileName string) error { - app, err := mysqlRepo.LoadBaseInfoByName(mysqkName) +func backupMysql(backupType, baseDir, backupDir, mysqlName, dbName, fileName string) error { + app, err := mysqlRepo.LoadBaseInfoByName(mysqlName) if err != nil { return err } diff --git a/backend/app/service/database_redis.go b/backend/app/service/database_redis.go index b8c7db244..085c6719f 100644 --- a/backend/app/service/database_redis.go +++ b/backend/app/service/database_redis.go @@ -1,15 +1,19 @@ package service import ( - "bufio" "encoding/json" + "errors" "fmt" - "io" + "io/ioutil" "os" + "os/exec" + "path/filepath" "strings" + "time" "github.com/1Panel-dev/1Panel/backend/app/dto" - "github.com/go-redis/redis" + "github.com/1Panel-dev/1Panel/backend/constant" + "github.com/1Panel-dev/1Panel/backend/utils/compose" _ "github.com/go-sql-driver/mysql" ) @@ -17,84 +21,93 @@ type RedisService struct{} type IRedisService interface { UpdateConf(req dto.RedisConfUpdate) error + UpdatePersistenceConf(req dto.RedisConfPersistenceUpdate) error LoadStatus() (*dto.RedisStatus, error) LoadConf() (*dto.RedisConf, error) LoadPersistenceConf() (*dto.RedisPersistence, error) - // Backup(db dto.BackupDB) error - // Recover(db dto.RecoverDB) error + Backup() error + SearchBackupListWithPage(req dto.PageInfo) (int64, interface{}, error) + Recover(req dto.RedisBackupRecover) error } func NewIRedisService() IRedisService { return &RedisService{} } -func newRedisClient() (*redis.Client, error) { - redisInfo, err := mysqlRepo.LoadRedisBaseInfo() - if err != nil { - return nil, err - } - client := redis.NewClient(&redis.Options{ - Addr: fmt.Sprintf("localhost:%v", redisInfo.Port), - Password: redisInfo.Password, - DB: 0, - }) - return client, nil -} - func (u *RedisService) UpdateConf(req dto.RedisConfUpdate) error { redisInfo, err := mysqlRepo.LoadRedisBaseInfo() if err != nil { return err } - file, err := os.OpenFile(fmt.Sprintf("/opt/1Panel/data/apps/redis/%s/conf/redis.conf", redisInfo.Name), os.O_RDWR, 0666) + if err := configSetStr(redisInfo.ContainerName, redisInfo.Password, "timeout", req.Timeout); err != nil { + return err + } + if err := configSetStr(redisInfo.ContainerName, redisInfo.Password, "maxclients", req.Maxclients); err != nil { + return err + } + if err := mysqlRepo.UpdateDatabasePassword(redisInfo.ID, map[string]interface{}{ + "param": strings.ReplaceAll(redisInfo.Param, redisInfo.Password, req.Requirepass), + "env": strings.ReplaceAll(redisInfo.Env, redisInfo.Password, req.Requirepass), + }); err != nil { + return err + } + if err := configSetStr(redisInfo.ContainerName, redisInfo.Password, "requirepass", req.Requirepass); err != nil { + return err + } + if err := configSetStr(redisInfo.ContainerName, redisInfo.Password, "maxmemory", req.Maxmemory); err != nil { + return err + } + + commands := append(redisExec(redisInfo.ContainerName, redisInfo.Password), []string{"config", "rewrite"}...) + cmd := exec.Command("docker", commands...) + stdout, err := cmd.CombinedOutput() + if err != nil { + return errors.New(string(stdout)) + } + return nil +} + +func (u *RedisService) UpdatePersistenceConf(req dto.RedisConfPersistenceUpdate) error { + redisInfo, err := mysqlRepo.LoadRedisBaseInfo() if err != nil { return err } - defer file.Close() - reader := bufio.NewReader(file) - pos := int64(0) - for { - line, err := reader.ReadString('\n') - if err != nil { - if err == io.EOF { - break - } else { - return err - } + if req.Type == "rbd" { + if err := configSetStr(redisInfo.ContainerName, redisInfo.Password, "save", req.Save); err != nil { + return err } - if bytes := updateConfFile(line, "timeout", req.Timeout); len(bytes) != 0 { - _, _ = file.WriteAt(bytes, pos) + } else { + if err := configSetStr(redisInfo.ContainerName, redisInfo.Password, "appendonly", req.Appendonly); err != nil { + return err } - if bytes := updateConfFile(line, "maxclients", req.Maxclients); len(bytes) != 0 { - _, _ = file.WriteAt(bytes, pos) + if err := configSetStr(redisInfo.ContainerName, redisInfo.Password, "appendfsync", req.Appendfsync); err != nil { + return err } - if bytes := updateConfFile(line, "databases", req.Databases); len(bytes) != 0 { - _, _ = file.WriteAt(bytes, pos) - } - if bytes := updateConfFile(line, "requirepass", req.Requirepass); len(bytes) != 0 { - _, _ = file.WriteAt(bytes, pos) - } - if bytes := updateConfFile(line, "maxmemory", req.Maxmemory); len(bytes) != 0 { - _, _ = file.WriteAt(bytes, pos) - } - pos += int64(len(line)) + } + commands := append(redisExec(redisInfo.ContainerName, redisInfo.Password), []string{"config", "rewrite"}...) + cmd := exec.Command("docker", commands...) + stdout, err := cmd.CombinedOutput() + if err != nil { + return errors.New(string(stdout)) } return nil } func (u *RedisService) LoadStatus() (*dto.RedisStatus, error) { - client, err := newRedisClient() + redisInfo, err := mysqlRepo.LoadRedisBaseInfo() if err != nil { return nil, err } - stdStr, err := client.Info().Result() + commands := append(redisExec(redisInfo.ContainerName, redisInfo.Password), "info") + cmd := exec.Command("docker", commands...) + stdout, err := cmd.CombinedOutput() if err != nil { - return nil, err + return nil, errors.New(string(stdout)) } - rows := strings.Split(stdStr, "\r\n") + rows := strings.Split(string(stdout), "\r\n") rowMap := make(map[string]string) for _, v := range rows { itemRow := strings.Split(v, ":") @@ -116,19 +129,22 @@ func (u *RedisService) LoadConf() (*dto.RedisConf, error) { if err != nil { return nil, err } - client := redis.NewClient(&redis.Options{ - Addr: fmt.Sprintf("localhost:%v", redisInfo.Port), - Password: redisInfo.Password, - DB: 0, - }) + var item dto.RedisConf item.ContainerName = redisInfo.ContainerName item.Name = redisInfo.Name - item.Timeout = configGetStr(client, "timeout") - item.Maxclients = configGetStr(client, "maxclients") - item.Databases = configGetStr(client, "databases") - item.Requirepass = configGetStr(client, "requirepass") - item.Maxmemory = configGetStr(client, "maxmemory") + if item.Timeout, err = configGetStr(redisInfo.ContainerName, redisInfo.Password, "timeout"); err != nil { + return nil, err + } + if item.Maxclients, err = configGetStr(redisInfo.ContainerName, redisInfo.Password, "maxclients"); err != nil { + return nil, err + } + if item.Requirepass, err = configGetStr(redisInfo.ContainerName, redisInfo.Password, "requirepass"); err != nil { + return nil, err + } + if item.Maxmemory, err = configGetStr(redisInfo.ContainerName, redisInfo.Password, "maxmemory"); err != nil { + return nil, err + } return &item, nil } @@ -137,38 +153,153 @@ func (u *RedisService) LoadPersistenceConf() (*dto.RedisPersistence, error) { if err != nil { return nil, err } - client := redis.NewClient(&redis.Options{ - Addr: fmt.Sprintf("localhost:%v", redisInfo.Port), - Password: redisInfo.Password, - DB: 0, - }) + var item dto.RedisPersistence - item.Dir = configGetStr(client, "dir") - item.Appendonly = configGetStr(client, "appendonly") - item.Appendfsync = configGetStr(client, "appendfsync") - item.Save = configGetStr(client, "save") + if item.Appendonly, err = configGetStr(redisInfo.ContainerName, redisInfo.Password, "appendonly"); err != nil { + return nil, err + } + if item.Appendfsync, err = configGetStr(redisInfo.ContainerName, redisInfo.Password, "appendfsync"); err != nil { + return nil, err + } + if item.Save, err = configGetStr(redisInfo.ContainerName, redisInfo.Password, "save"); err != nil { + return nil, err + } return &item, nil } -func configGetStr(client *redis.Client, param string) string { - item, _ := client.ConfigGet(param).Result() - if len(item) == 2 { - if value, ok := item[1].(string); ok { - return value +func (u *RedisService) Backup() error { + redisInfo, err := mysqlRepo.LoadRedisBaseInfo() + if err != nil { + return err + } + commands := append(redisExec(redisInfo.ContainerName, redisInfo.Password), "save") + cmd := exec.Command("docker", commands...) + if stdout, err := cmd.CombinedOutput(); err != nil { + return errors.New(string(stdout)) + } + name := fmt.Sprintf("%s.rdb", time.Now().Format("20060102150405")) + + backupLocal, err := backupRepo.Get(commonRepo.WithByType("LOCAL")) + if err != nil { + return err + } + localDir, err := loadLocalDir(backupLocal) + if err != nil { + return err + } + + backupDir := fmt.Sprintf("database/redis/%s/", redisInfo.Name) + fullDir := fmt.Sprintf("%s/%s", localDir, backupDir) + if _, err := os.Stat(fullDir); err != nil && os.IsNotExist(err) { + if err = os.MkdirAll(fullDir, os.ModePerm); err != nil { + if err != nil { + return fmt.Errorf("mkdir %s failed, err: %v", fullDir, err) + } } } - return "" + cmd2 := exec.Command("docker", "cp", fmt.Sprintf("%s:/data/dump.rdb", redisInfo.ContainerName), fmt.Sprintf("%s/%s", fullDir, name)) + if stdout, err := cmd2.CombinedOutput(); err != nil { + return errors.New(string(stdout)) + } + return nil } -func updateConfFile(line, param string, value string) []byte { - var bytes []byte - if strings.HasPrefix(line, param) || strings.HasPrefix(line, "# "+param) { - if len(value) == 0 || value == "0" { - bytes = []byte(fmt.Sprintf("# %s", param)) - } else { - bytes = []byte(fmt.Sprintf("%s %v", param, value)) - } - return bytes +func (u *RedisService) Recover(req dto.RedisBackupRecover) error { + redisInfo, err := mysqlRepo.LoadRedisBaseInfo() + if err != nil { + return err } - return bytes + composeDir := fmt.Sprintf("%s/redis/%s", constant.AppInstallDir, redisInfo.Name) + if _, err := compose.Down(composeDir + "/docker-compose.yml"); err != nil { + return err + } + + fullName := fmt.Sprintf("%s/%s", req.FileDir, req.FileName) + input, err := ioutil.ReadFile(fullName) + if err != nil { + return err + } + if err = ioutil.WriteFile(composeDir+"/data/dump.rdb", input, 0640); err != nil { + return err + } + if _, err := compose.Up(composeDir + "/docker-compose.yml"); err != nil { + return err + } + + return nil +} + +func (u *RedisService) SearchBackupListWithPage(req dto.PageInfo) (int64, interface{}, error) { + var ( + list []dto.RedisBackupRecords + backDatas []dto.RedisBackupRecords + ) + redisInfo, err := mysqlRepo.LoadRedisBaseInfo() + if err != nil { + return 0, nil, err + } + backupLocal, err := backupRepo.Get(commonRepo.WithByType("LOCAL")) + if err != nil { + return 0, nil, err + } + localDir, err := loadLocalDir(backupLocal) + if err != nil { + return 0, nil, err + } + backupDir := fmt.Sprintf("%s/database/redis/%s", localDir, redisInfo.Name) + _ = filepath.Walk(backupDir, func(path string, info os.FileInfo, err error) error { + if !info.IsDir() { + list = append(list, dto.RedisBackupRecords{ + CreatedAt: info.ModTime().Format("2006-01-02 15:04:05"), + Size: int(info.Size()), + FileDir: backupDir, + FileName: info.Name(), + }) + } + return nil + }) + total, start, end := len(list), (req.Page-1)*req.PageSize, req.Page*req.PageSize + if start > total { + backDatas = make([]dto.RedisBackupRecords, 0) + } else { + if end >= total { + end = total + } + backDatas = list[start:end] + } + return int64(total), backDatas, nil +} + +func configGetStr(containerName, password, param string) (string, error) { + commands := append(redisExec(containerName, password), []string{"config", "get", param}...) + cmd := exec.Command("docker", commands...) + stdout, err := cmd.CombinedOutput() + if err != nil { + return "", errors.New(string(stdout)) + } + rows := strings.Split(string(stdout), "\r\n") + for _, v := range rows { + itemRow := strings.Split(v, "\n") + if len(itemRow) == 3 { + return itemRow[1], nil + } + } + return "", nil +} +func configSetStr(containerName, password, param, value string) error { + commands := append(redisExec(containerName, password), []string{"config", "set", param, value}...) + cmd := exec.Command("docker", commands...) + stdout, err := cmd.CombinedOutput() + if err != nil { + return errors.New(string(stdout)) + } + return nil +} + +func redisExec(containerName, password string) []string { + cmds := []string{"exec", containerName, "redis-cli", "-a", password, "--no-auth-warning"} + if len(password) == 0 { + cmds = []string{"exec", containerName, "redis-cli"} + } + return cmds } diff --git a/backend/app/service/database_test.go b/backend/app/service/database_test.go index f4a52c075..d07b82456 100644 --- a/backend/app/service/database_test.go +++ b/backend/app/service/database_test.go @@ -1,45 +1,35 @@ package service import ( + "encoding/json" "fmt" + "os/exec" + "strings" "testing" "github.com/1Panel-dev/1Panel/backend/app/dto" - "github.com/go-redis/redis" ) func TestMysql(t *testing.T) { - client := redis.NewClient(&redis.Options{ - Addr: "172.16.10.143:6379", - Password: "", - DB: 0, - }) - fmt.Println(client.Ping().Result()) + cmd := exec.Command("docker", "exec", "1Panel-redis-7.0.5-zgVH-K859", "redis-cli", "config", "get", "save") + stdout, err := cmd.CombinedOutput() + if err != nil { + fmt.Println(string(stdout)) + } - var item dto.RedisPersistence - dir, _ := client.ConfigGet("dir").Result() - if len(dir) == 2 { - if value, ok := dir[1].(string); ok { - item.Dir = value + rows := strings.Split(string(stdout), "\r\n") + rowMap := make(map[string]string) + for _, v := range rows { + itemRow := strings.Split(v, "\n") + if len(itemRow) == 3 { + rowMap[itemRow[0]] = itemRow[1] } } - appendonly, _ := client.ConfigGet("appendonly").Result() - if len(appendonly) == 2 { - if value, ok := appendonly[1].(string); ok { - item.Appendonly = value - } + var info dto.RedisStatus + arr, err := json.Marshal(rowMap) + if err != nil { + fmt.Println(err) } - appendfsync, _ := client.ConfigGet("appendfsync").Result() - if len(appendfsync) == 2 { - if value, ok := appendfsync[1].(string); ok { - item.Appendfsync = value - } - } - save, _ := client.ConfigGet("save").Result() - if len(save) == 2 { - if value, ok := save[1].(string); ok { - item.Save = value - } - } - fmt.Println(item) + _ = json.Unmarshal(arr, &info) + fmt.Println(info) } diff --git a/backend/router/ro_database.go b/backend/router/ro_database.go index 048f734e8..e2e62de29 100644 --- a/backend/router/ro_database.go +++ b/backend/router/ro_database.go @@ -40,7 +40,12 @@ func (s *DatabaseRouter) InitDatabaseRouter(Router *gin.RouterGroup) { cmdRouter.GET("/redis/status", baseApi.LoadRedisStatus) cmdRouter.GET("/redis/conf", baseApi.LoadRedisConf) cmdRouter.GET("/redis/exec", baseApi.RedisExec) + cmdRouter.POST("/redis/backup", baseApi.RedisBackup) + cmdRouter.POST("/redis/recover", baseApi.RedisRecover) + cmdRouter.POST("/redis/backup/records", baseApi.RedisBackupList) + cmdRouter.POST("/redis/backup/del", baseApi.RedisBackupDelete) cmdRouter.POST("/redis/conf/update", baseApi.UpdateRedisConf) cmdRouter.POST("/redis/conf/update/byfile", baseApi.UpdateRedisConfByFile) + cmdRouter.POST("/redis/conf/update/persistence", baseApi.UpdateRedisPersistenceConf) } } diff --git a/frontend/src/api/interface/database.ts b/frontend/src/api/interface/database.ts index e7167803c..f3be93a90 100644 --- a/frontend/src/api/interface/database.ts +++ b/frontend/src/api/interface/database.ts @@ -112,12 +112,18 @@ export namespace Database { export interface RedisConfUpdate { timeout: string; maxclients: string; - databases: string; requirepass: string; maxmemory: string; } + export interface RedisConfPersistenceUpdate { + type: string; + appendonly: string; + appendfsync: string; + save: string; + } export interface RedisConfUpdateByFile { file: string; + restartNow: boolean; } export interface RedisStatus { tcp_port: string; @@ -138,14 +144,26 @@ export namespace Database { name: string; timeout: number; maxclients: number; - databases: number; requirepass: string; maxmemory: number; } export interface RedisPersistenceConf { - dir: string; appendonly: string; appendfsync: string; save: string; } + export interface RedisBackupRecord { + fileName: string; + fileDir: string; + createdAt: string; + size: string; + } + export interface RedisBackupDelete { + fileDir: string; + names: Array; + } + export interface RedisRecover { + fileName: string; + fileDir: string; + } } diff --git a/frontend/src/api/modules/database.ts b/frontend/src/api/modules/database.ts index 880078423..8cf3061d5 100644 --- a/frontend/src/api/modules/database.ts +++ b/frontend/src/api/modules/database.ts @@ -1,5 +1,5 @@ import http from '@/api'; -import { ResPage } from '../interface'; +import { ReqPage, ResPage } from '../interface'; import { Backup } from '../interface/backup'; import { Database } from '../interface/database'; @@ -59,9 +59,24 @@ export const loadRedisConf = () => { export const RedisPersistenceConf = () => { return http.get(`/databases/redis/persistence/conf`); }; +export const updateRedisPersistenceConf = (params: Database.RedisConfPersistenceUpdate) => { + return http.post(`/databases/redis/conf/update/persistence`, params); +}; export const updateRedisConf = (params: Database.RedisConfUpdate) => { return http.post(`/databases/redis/conf/update`, params); }; export const updateRedisConfByFile = (params: Database.RedisConfUpdateByFile) => { return http.post(`/databases/redis/conf/update/byfile`, params); }; +export const backupRedis = () => { + return http.post(`/databases/redis/backup`); +}; +export const recoverRedis = (param: Database.RedisRecover) => { + return http.post(`/databases/redis/recover`, param); +}; +export const redisBackupRedisRecords = (param: ReqPage) => { + return http.post>(`/databases/redis/backup/records`, param); +}; +export const deleteBackupRedis = (param: Database.RedisBackupDelete) => { + return http.post(`/databases/redis/backup/del`, param); +}; diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index 31e4cdc4f..8fe45b932 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -252,14 +252,18 @@ export default { hit: '查找数据库键命中率', latestForkUsec: '最近一次 fork() 操作耗费的微秒数', + baseConf: '基础配置', + allConf: '全部配置', + restartNow: '立即重启', + restartNowHelper1: '修改配置后需要', + restartNowHelper2: '重启 redis 生效', + restartNowHelper3: ',若您的数据需要持久化请先执行 save 操作。', + persistence: '持久化', - persistenceDir: '持久化路径', - persistenceDirHelper: '设置后将在选择的目录下新建 redis_cache 目录并赋予 redis 权限', - aofPersistence: 'AOF 持久化', - rdbPersistence: 'RDB 持久化', - drbHelper1: '秒內,插入', - drbHelper2: '条数据', - drbHelper3: '符合任意一个条件将会触发RDB持久化', + rdbHelper1: '秒內,插入', + rdbHelper2: '条数据', + rdbHelper3: '符合任意一个条件将会触发RDB持久化', + rdbInfo: '规则列表存在 0 值,请确认后重试!', }, container: { operatorHelper: '将对选中容器进行 {0} 操作,是否继续?', diff --git a/frontend/src/views/database/redis/persistence/index.vue b/frontend/src/views/database/redis/persistence/index.vue index 74c14ac64..a084bbae6 100644 --- a/frontend/src/views/database/redis/persistence/index.vue +++ b/frontend/src/views/database/redis/persistence/index.vue @@ -1,38 +1,147 @@