mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-01-31 14:08:06 +08:00
feat: 完成 redis 备份功能
This commit is contained in:
parent
bfe2b95334
commit
917a11457e
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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"`
|
||||
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"`
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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 != nil {
|
||||
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
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
reader := bufio.NewReader(file)
|
||||
pos := int64(0)
|
||||
for {
|
||||
line, err := reader.ReadString('\n')
|
||||
commands := append(redisExec(redisInfo.ContainerName, redisInfo.Password), []string{"config", "rewrite"}...)
|
||||
cmd := exec.Command("docker", commands...)
|
||||
stdout, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
return errors.New(string(stdout))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *RedisService) UpdatePersistenceConf(req dto.RedisConfPersistenceUpdate) error {
|
||||
redisInfo, err := mysqlRepo.LoadRedisBaseInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if req.Type == "rbd" {
|
||||
if err := configSetStr(redisInfo.ContainerName, redisInfo.Password, "save", req.Save); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := configSetStr(redisInfo.ContainerName, redisInfo.Password, "appendonly", req.Appendonly); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := configSetStr(redisInfo.ContainerName, redisInfo.Password, "appendfsync", req.Appendfsync); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if bytes := updateConfFile(line, "timeout", req.Timeout); len(bytes) != 0 {
|
||||
_, _ = file.WriteAt(bytes, pos)
|
||||
}
|
||||
if bytes := updateConfFile(line, "maxclients", req.Maxclients); len(bytes) != 0 {
|
||||
_, _ = file.WriteAt(bytes, pos)
|
||||
}
|
||||
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))
|
||||
}
|
||||
return ""
|
||||
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
|
||||
}
|
||||
|
||||
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))
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
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 (u *RedisService) Recover(req dto.RedisBackupRecover) error {
|
||||
redisInfo, err := mysqlRepo.LoadRedisBaseInfo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
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 {
|
||||
bytes = []byte(fmt.Sprintf("%s %v", param, value))
|
||||
if end >= total {
|
||||
end = total
|
||||
}
|
||||
return bytes
|
||||
backDatas = list[start:end]
|
||||
}
|
||||
return bytes
|
||||
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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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<string>;
|
||||
}
|
||||
export interface RedisRecover {
|
||||
fileName: string;
|
||||
fileDir: string;
|
||||
}
|
||||
}
|
||||
|
@ -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<Database.RedisPersistenceConf>(`/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<ResPage<Database.RedisBackupRecord>>(`/databases/redis/backup/records`, param);
|
||||
};
|
||||
export const deleteBackupRedis = (param: Database.RedisBackupDelete) => {
|
||||
return http.post(`/databases/redis/backup/del`, param);
|
||||
};
|
||||
|
@ -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} 操作,是否继续?',
|
||||
|
@ -1,13 +1,24 @@
|
||||
<template>
|
||||
<div v-if="persistenceShow">
|
||||
<el-card style="margin-top: 5px">
|
||||
<el-row :gutter="20" style="margin-top: 5px" class="row-box">
|
||||
<el-col :span="12">
|
||||
<el-card class="el-card">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>AOF {{ $t('database.persistence') }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-form :model="form" ref="formRef" :rules="rules" label-width="120px">
|
||||
<el-row>
|
||||
<el-col :span="1"><br /></el-col>
|
||||
<el-col :span="10">
|
||||
<el-form>
|
||||
<el-form-item label="appendonly" prop="appendonly">
|
||||
<el-switch v-model="form.appendonly"></el-switch>
|
||||
<el-switch
|
||||
active-value="yes"
|
||||
inactive-value="no"
|
||||
v-model="form.appendonly"
|
||||
></el-switch>
|
||||
</el-form-item>
|
||||
<el-form-item label="appendfsync" prop="appendfsync">
|
||||
<el-radio-group v-model="form.appendfsync">
|
||||
@ -17,22 +28,120 @@
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="onSave(formRef, 'aof')"
|
||||
style="bottom: 10px; width: 90px"
|
||||
>
|
||||
{{ $t('commons.button.save') }}
|
||||
</el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-card class="el-card">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>RDB {{ $t('database.persistence') }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<table style="width: 100%" class="tab-table">
|
||||
<tr v-for="(row, index) in form.saves" :key="index">
|
||||
<td width="32%">
|
||||
<el-input type="number" v-model="row.second"></el-input>
|
||||
</td>
|
||||
<td width="55px">
|
||||
<span>{{ $t('database.rdbHelper1') }}</span>
|
||||
</td>
|
||||
<td width="32%">
|
||||
<el-input type="number" v-model="row.count"></el-input>
|
||||
</td>
|
||||
<td width="12%">
|
||||
<span>{{ $t('database.rdbHelper2') }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<el-button link type="primary" style="font-size: 10px" @click="handleDelete(index)">
|
||||
{{ $t('commons.button.delete') }}
|
||||
</el-button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left">
|
||||
<el-button @click="handleAdd()">{{ $t('commons.button.add') }}</el-button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div>
|
||||
<span style="margin-left: 2px; margin-top: 5px">{{ $t('database.rdbHelper3') }}</span>
|
||||
</div>
|
||||
<el-button type="primary" @click="onSave(undefined, 'rbd')" style="margin-top: 10px; width: 90px">
|
||||
{{ $t('commons.button.save') }}
|
||||
</el-button>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-card style="margin-top: 20px">
|
||||
<ComplexTable
|
||||
:pagination-config="paginationConfig"
|
||||
v-model:selects="selects"
|
||||
@search="loadBackupRecords"
|
||||
:data="data"
|
||||
>
|
||||
<template #toolbar>
|
||||
<el-button type="primary" @click="onBackup">{{ $t('setting.backup') }}</el-button>
|
||||
<el-button type="danger" plain :disabled="selects.length === 0" @click="onBatchDelete(null)">
|
||||
{{ $t('commons.button.delete') }}
|
||||
</el-button>
|
||||
</template>
|
||||
<el-table-column type="selection" fix />
|
||||
<el-table-column :label="$t('commons.table.name')" show-overflow-tooltip prop="fileName" />
|
||||
<el-table-column :label="$t('file.dir')" show-overflow-tooltip prop="fileDir" />
|
||||
<el-table-column :label="$t('file.size')" prop="size">
|
||||
<template #default="{ row }">
|
||||
{{ computeSize(row.size) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('commons.table.createdAt')" prop="createdAt" />
|
||||
<fu-table-operations
|
||||
width="300px"
|
||||
:buttons="buttons"
|
||||
:ellipsis="10"
|
||||
:label="$t('commons.table.operate')"
|
||||
fix
|
||||
/>
|
||||
</ComplexTable>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { RedisPersistenceConf } from '@/api/modules/database';
|
||||
import ComplexTable from '@/components/complex-table/index.vue';
|
||||
import { Database } from '@/api/interface/database';
|
||||
import {
|
||||
backupRedis,
|
||||
deleteBackupRedis,
|
||||
recoverRedis,
|
||||
redisBackupRedisRecords,
|
||||
RedisPersistenceConf,
|
||||
updateRedisPersistenceConf,
|
||||
} from '@/api/modules/database';
|
||||
import { Rules } from '@/global/form-rules';
|
||||
import { FormInstance } from 'element-plus';
|
||||
import i18n from '@/lang';
|
||||
import { ElMessage, FormInstance } from 'element-plus';
|
||||
import { reactive, ref } from 'vue';
|
||||
import { useDeleteData } from '@/hooks/use-delete-data';
|
||||
import { computeSize } from '@/utils/util';
|
||||
|
||||
interface saveStruct {
|
||||
second: number;
|
||||
count: number;
|
||||
}
|
||||
const form = reactive({
|
||||
appendonly: '',
|
||||
appendfsync: 'no',
|
||||
saves: [] as Array<saveStruct>,
|
||||
});
|
||||
const rules = reactive({
|
||||
appendonly: [Rules.requiredSelect],
|
||||
@ -44,36 +153,124 @@ const persistenceShow = ref(false);
|
||||
const acceptParams = (): void => {
|
||||
persistenceShow.value = true;
|
||||
loadform();
|
||||
loadBackupRecords();
|
||||
};
|
||||
const onClose = (): void => {
|
||||
persistenceShow.value = false;
|
||||
};
|
||||
|
||||
// const onSave = async (formEl: FormInstance | undefined, key: string) => {
|
||||
// if (!formEl) return;
|
||||
// const result = await formEl.validateField(key, callback);
|
||||
// if (!result) {
|
||||
// return;
|
||||
// }
|
||||
// // let changeForm = {
|
||||
// // paramName: key,
|
||||
// // value: val + '',
|
||||
// // };
|
||||
// // await updateRedisConf(changeForm);
|
||||
// ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
||||
// };
|
||||
// function callback(error: any) {
|
||||
// if (error) {
|
||||
// return error.message;
|
||||
// } else {
|
||||
// return;
|
||||
// }
|
||||
// }
|
||||
const data = ref();
|
||||
const selects = ref<any>([]);
|
||||
const paginationConfig = reactive({
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
});
|
||||
|
||||
const handleAdd = () => {
|
||||
let item = {
|
||||
second: 0,
|
||||
count: 0,
|
||||
};
|
||||
form.saves.push(item);
|
||||
};
|
||||
const handleDelete = (index: number) => {
|
||||
form.saves.splice(index, 1);
|
||||
};
|
||||
|
||||
const loadBackupRecords = async () => {
|
||||
let params = {
|
||||
page: paginationConfig.currentPage,
|
||||
pageSize: paginationConfig.pageSize,
|
||||
};
|
||||
const res = await redisBackupRedisRecords(params);
|
||||
data.value = res.data.items || [];
|
||||
paginationConfig.total = res.data.total;
|
||||
};
|
||||
const onBackup = async () => {
|
||||
await backupRedis();
|
||||
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
||||
loadBackupRecords();
|
||||
};
|
||||
const onRecover = async (row: Database.RedisBackupRecord) => {
|
||||
let param = {
|
||||
fileName: row.fileName,
|
||||
fileDir: row.fileDir,
|
||||
};
|
||||
await recoverRedis(param);
|
||||
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
||||
};
|
||||
|
||||
const onBatchDelete = async (row: Database.RedisBackupRecord | null) => {
|
||||
let names: Array<string> = [];
|
||||
let fileDir: string = '';
|
||||
if (row) {
|
||||
fileDir = row.fileDir;
|
||||
names.push(row.fileName);
|
||||
} else {
|
||||
selects.value.forEach((item: Database.RedisBackupRecord) => {
|
||||
fileDir = item.fileDir;
|
||||
names.push(item.fileName);
|
||||
});
|
||||
}
|
||||
await useDeleteData(deleteBackupRedis, { fileDir: fileDir, names: names }, 'commons.msg.delete', true);
|
||||
loadBackupRecords();
|
||||
};
|
||||
const buttons = [
|
||||
{
|
||||
label: i18n.global.t('commons.button.recover'),
|
||||
click: (row: Database.RedisBackupRecord) => {
|
||||
onRecover(row);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: i18n.global.t('commons.button.delete'),
|
||||
click: (row: Database.RedisBackupRecord) => {
|
||||
onBatchDelete(row);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const onSave = async (formEl: FormInstance | undefined, type: string) => {
|
||||
let param = {} as Database.RedisConfPersistenceUpdate;
|
||||
if (type == 'aof') {
|
||||
if (!formEl) return;
|
||||
formEl.validate(async (valid) => {
|
||||
if (!valid) return;
|
||||
param.type = type;
|
||||
param.appendfsync = form.appendfsync;
|
||||
param.appendonly = form.appendonly;
|
||||
await updateRedisPersistenceConf(param);
|
||||
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
||||
return;
|
||||
});
|
||||
return;
|
||||
}
|
||||
let itemSaves = [] as Array<string>;
|
||||
for (const item of form.saves) {
|
||||
if (item.count === 0 || item.second === 0) {
|
||||
ElMessage.info(i18n.global.t('database.rdbInfo'));
|
||||
return;
|
||||
}
|
||||
itemSaves.push(item.second + '', item.count + '');
|
||||
}
|
||||
param.type = type;
|
||||
param.save = itemSaves.join(' ');
|
||||
await updateRedisPersistenceConf(param);
|
||||
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
||||
};
|
||||
|
||||
const loadform = async () => {
|
||||
form.saves = [];
|
||||
const res = await RedisPersistenceConf();
|
||||
form.appendonly = res.data?.appendonly;
|
||||
form.appendfsync = res.data?.appendfsync;
|
||||
let itemSaves = res.data?.save.split(' ');
|
||||
for (let i = 0; i < itemSaves.length; i++) {
|
||||
if (i % 2 === 1) {
|
||||
form.saves.push({ second: Number(itemSaves[i - 1]), count: Number(itemSaves[i]) });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
|
@ -2,8 +2,8 @@
|
||||
<div v-if="settingShow">
|
||||
<el-card style="margin-top: 5px">
|
||||
<el-radio-group v-model="confShowType">
|
||||
<el-radio-button label="base">基础配置</el-radio-button>
|
||||
<el-radio-button label="all">全部配置</el-radio-button>
|
||||
<el-radio-button label="base">{{ $t('database.baseConf') }}</el-radio-button>
|
||||
<el-radio-button label="all">{{ $t('database.allConf') }}</el-radio-button>
|
||||
</el-radio-group>
|
||||
<el-form v-if="confShowType === 'base'" :model="form" ref="formRef" :rules="rules" label-width="120px">
|
||||
<el-row style="margin-top: 20px">
|
||||
@ -23,9 +23,6 @@
|
||||
<el-form-item :label="$t('database.maxclients')" prop="maxclients">
|
||||
<el-input clearable type="number" v-model.number="form.maxclients" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('database.databases')" prop="databases">
|
||||
<el-input clearable type="number" v-model.number="form.databases" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('database.maxmemory')" prop="maxmemory">
|
||||
<el-input clearable type="number" v-model.number="form.maxmemory" />
|
||||
<span class="input-help">{{ $t('database.maxmemoryHelper') }}</span>
|
||||
@ -53,11 +50,40 @@
|
||||
v-model="mysqlConf"
|
||||
:readOnly="true"
|
||||
/>
|
||||
<el-button type="primary" size="default" @click="onSaveFile" style="width: 90px; margin-top: 5px">
|
||||
<el-button
|
||||
type="primary"
|
||||
size="default"
|
||||
@click="saveVisiable = true"
|
||||
style="width: 90px; margin-top: 5px"
|
||||
>
|
||||
{{ $t('commons.button.save') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<el-dialog v-model="saveVisiable" :destroy-on-close="true" width="30%">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>{{ $t('database.confChange') }}</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-checkbox v-model="restartNow" :label="$t('database.restartNow')" />
|
||||
<div>
|
||||
<span style="font-size: 12px">{{ $t('database.restartNowHelper1') }}</span>
|
||||
<span style="font-size: 12px; color: red; font-weight: 500">
|
||||
{{ $t('database.restartNowHelper2') }}
|
||||
</span>
|
||||
<span style="font-size: 12px">{{ $t('database.restartNowHelper3') }}</span>
|
||||
</div>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="saveVisiable = false">{{ $t('commons.button.cancel') }}</el-button>
|
||||
<el-button @click="onSaveFile()">
|
||||
{{ $t('commons.button.confirm') }}
|
||||
</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -75,20 +101,20 @@ import { Rules } from '@/global/form-rules';
|
||||
const extensions = [javascript(), oneDark];
|
||||
const confShowType = ref('base');
|
||||
|
||||
const restartNow = ref(false);
|
||||
const saveVisiable = ref(false);
|
||||
const form = reactive({
|
||||
name: '',
|
||||
port: 3306,
|
||||
requirepass: '',
|
||||
timeout: 0,
|
||||
maxclients: 0,
|
||||
databases: 0,
|
||||
maxmemory: 0,
|
||||
});
|
||||
const rules = reactive({
|
||||
port: [Rules.port],
|
||||
timeout: [Rules.number],
|
||||
maxclients: [Rules.number],
|
||||
databases: [Rules.number],
|
||||
maxmemory: [Rules.number],
|
||||
});
|
||||
|
||||
@ -106,17 +132,20 @@ const onClose = (): void => {
|
||||
};
|
||||
|
||||
const onSave = async (formEl: FormInstance | undefined) => {
|
||||
if (confShowType.value === 'all') {
|
||||
onSaveFile();
|
||||
}
|
||||
if (!formEl) return;
|
||||
formEl.validate(async (valid) => {
|
||||
if (!valid) return;
|
||||
let param = {
|
||||
timeout: form.timeout + '',
|
||||
maxclients: form.maxclients + '',
|
||||
databases: form.databases + '',
|
||||
requirepass: form.requirepass,
|
||||
maxmemory: form.maxmemory + '',
|
||||
};
|
||||
await updateRedisConf(param);
|
||||
saveVisiable.value = false;
|
||||
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
||||
});
|
||||
};
|
||||
@ -124,8 +153,11 @@ const onSave = async (formEl: FormInstance | undefined) => {
|
||||
const onSaveFile = async () => {
|
||||
let param = {
|
||||
file: mysqlConf.value,
|
||||
restartNow: restartNow.value,
|
||||
};
|
||||
await updateRedisConfByFile(param);
|
||||
saveVisiable.value = false;
|
||||
restartNow.value = false;
|
||||
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
||||
};
|
||||
|
||||
@ -134,7 +166,6 @@ const loadform = async () => {
|
||||
form.name = res.data?.name;
|
||||
form.timeout = Number(res.data?.timeout);
|
||||
form.maxclients = Number(res.data?.maxclients);
|
||||
form.databases = Number(res.data?.databases);
|
||||
form.requirepass = res.data?.requirepass;
|
||||
form.maxmemory = Number(res.data?.maxmemory);
|
||||
loadMysqlConf(`/opt/1Panel/data/apps/redis/${form.name}/conf/redis.conf`);
|
||||
|
3
go.mod
3
go.mod
@ -16,7 +16,6 @@ require (
|
||||
github.com/gin-gonic/gin v1.8.1
|
||||
github.com/go-gormigrate/gormigrate/v2 v2.0.2
|
||||
github.com/go-playground/validator/v10 v10.11.0
|
||||
github.com/go-redis/redis v6.15.9+incompatible
|
||||
github.com/go-sql-driver/mysql v1.6.0
|
||||
github.com/gogf/gf v1.16.9
|
||||
github.com/golang-jwt/jwt/v4 v4.4.2
|
||||
@ -119,8 +118,6 @@ require (
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/nwaples/rardecode/v2 v2.0.0-beta.2 // indirect
|
||||
github.com/onsi/ginkgo v1.16.5 // indirect
|
||||
github.com/onsi/gomega v1.21.1 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/runc v1.1.4 // indirect
|
||||
github.com/pelletier/go-toml v1.9.5 // indirect
|
||||
|
14
go.sum
14
go.sum
@ -401,12 +401,9 @@ github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn
|
||||
github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos=
|
||||
github.com/go-playground/validator/v10 v10.11.0 h1:0W+xRM511GY47Yy3bZUbJVitCNg2BOGlCyvTqsp/xIw=
|
||||
github.com/go-playground/validator/v10 v10.11.0/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
|
||||
github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg=
|
||||
github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
|
||||
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/goccy/go-json v0.9.7 h1:IcB+Aqpx/iMHu5Yooh7jEzJk1JZ7Pjtmys2ukPr7EeM=
|
||||
github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
|
||||
@ -718,8 +715,6 @@ github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLA
|
||||
github.com/nwaples/rardecode/v2 v2.0.0-beta.2 h1:e3mzJFJs4k83GXBEiTaQ5HgSc/kOK8q0rDaRO0MPaOk=
|
||||
github.com/nwaples/rardecode/v2 v2.0.0-beta.2/go.mod h1:yntwv/HfMc/Hbvtq9I19D1n58te3h6KsqCf3GxyfBGY=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||
@ -733,18 +728,13 @@ github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+
|
||||
github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||
github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
|
||||
github.com/onsi/gomega v1.21.1 h1:OB/euWYIExnPBohllTicTHmGTrMaqJ67nIu80j0/uEM=
|
||||
github.com/onsi/gomega v1.21.1/go.mod h1:iYAIXgPSaDHak0LCMA+AWBpIKBr8WZicMxnE8luStNc=
|
||||
github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
@ -1093,7 +1083,6 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/
|
||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
@ -1207,7 +1196,6 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@ -1310,7 +1298,6 @@ golang.org/x/tools v0.0.0-20200916195026-c9a70fc28ce3/go.mod h1:z6u4i615ZeAfBE4X
|
||||
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
@ -1453,7 +1440,6 @@ gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
Loading…
x
Reference in New Issue
Block a user