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/app/dto"
|
||||||
"github.com/1Panel-dev/1Panel/backend/constant"
|
"github.com/1Panel-dev/1Panel/backend/constant"
|
||||||
"github.com/1Panel-dev/1Panel/backend/global"
|
"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/docker"
|
||||||
"github.com/1Panel-dev/1Panel/backend/utils/terminal"
|
"github.com/1Panel-dev/1Panel/backend/utils/terminal"
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
@ -65,6 +66,86 @@ func (b *BaseApi) UpdateRedisConf(c *gin.Context) {
|
|||||||
helper.SuccessWithData(c, nil)
|
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) {
|
func (b *BaseApi) UpdateRedisConfByFile(c *gin.Context) {
|
||||||
var req dto.RedisConfUpdateByFile
|
var req dto.RedisConfUpdateByFile
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
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)
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
return
|
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)
|
file, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0640)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
@ -87,6 +168,14 @@ func (b *BaseApi) UpdateRedisConfByFile(c *gin.Context) {
|
|||||||
_, _ = write.WriteString(req.File)
|
_, _ = write.WriteString(req.File)
|
||||||
write.Flush()
|
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)
|
helper.SuccessWithData(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,12 +145,18 @@ type RecoverDB struct {
|
|||||||
type RedisConfUpdate struct {
|
type RedisConfUpdate struct {
|
||||||
Timeout string `json:"timeout"`
|
Timeout string `json:"timeout"`
|
||||||
Maxclients string `json:"maxclients"`
|
Maxclients string `json:"maxclients"`
|
||||||
Databases string `json:"databases"`
|
|
||||||
Requirepass string `json:"requirepass"`
|
Requirepass string `json:"requirepass"`
|
||||||
Maxmemory string `json:"maxmemory"`
|
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 {
|
type RedisConfUpdateByFile struct {
|
||||||
File string `json:"file"`
|
File string `json:"file"`
|
||||||
|
RestartNow bool `json:"restartNow"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type RedisConf struct {
|
type RedisConf struct {
|
||||||
@ -158,13 +164,11 @@ type RedisConf struct {
|
|||||||
ContainerName string `json:"containerName"`
|
ContainerName string `json:"containerName"`
|
||||||
Timeout string `json:"timeout"`
|
Timeout string `json:"timeout"`
|
||||||
Maxclients string `json:"maxclients"`
|
Maxclients string `json:"maxclients"`
|
||||||
Databases string `json:"databases"`
|
|
||||||
Requirepass string `json:"requirepass"`
|
Requirepass string `json:"requirepass"`
|
||||||
Maxmemory string `json:"maxmemory"`
|
Maxmemory string `json:"maxmemory"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type RedisPersistence struct {
|
type RedisPersistence struct {
|
||||||
Dir string `json:"dir"`
|
|
||||||
Appendonly string `json:"appendonly"`
|
Appendonly string `json:"appendonly"`
|
||||||
Appendfsync string `json:"appendfsync"`
|
Appendfsync string `json:"appendfsync"`
|
||||||
Save string `json:"save"`
|
Save string `json:"save"`
|
||||||
@ -185,3 +189,18 @@ type RedisStatus struct {
|
|||||||
KeyspaceMisses string `json:"keyspace_misses"`
|
KeyspaceMisses string `json:"keyspace_misses"`
|
||||||
LatestForkUsec string `json:"latest_fork_usec"`
|
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)
|
LoadRunningVersion(keys []string) ([]string, error)
|
||||||
LoadBaseInfoByName(name string) (*RootInfo, error)
|
LoadBaseInfoByName(name string) (*RootInfo, error)
|
||||||
LoadRedisBaseInfo() (*RootInfo, error)
|
LoadRedisBaseInfo() (*RootInfo, error)
|
||||||
UpdateMysqlConf(id uint, vars map[string]interface{}) error
|
UpdateDatabasePassword(id uint, vars map[string]interface{}) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewIMysqlRepo() IMysqlRepo {
|
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
|
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 {
|
if err := global.DB.Model(&model.AppInstall{}).Where("id = ?", id).Updates(vars).Error; err != nil {
|
||||||
return err
|
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),
|
"param": strings.ReplaceAll(app.Param, app.Password, info.Value),
|
||||||
"env": strings.ReplaceAll(app.Env, 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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func backupMysql(backupType, baseDir, backupDir, mysqkName, dbName, fileName string) error {
|
func backupMysql(backupType, baseDir, backupDir, mysqlName, dbName, fileName string) error {
|
||||||
app, err := mysqlRepo.LoadBaseInfoByName(mysqkName)
|
app, err := mysqlRepo.LoadBaseInfoByName(mysqlName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,19 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
"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"
|
_ "github.com/go-sql-driver/mysql"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -17,84 +21,93 @@ type RedisService struct{}
|
|||||||
|
|
||||||
type IRedisService interface {
|
type IRedisService interface {
|
||||||
UpdateConf(req dto.RedisConfUpdate) error
|
UpdateConf(req dto.RedisConfUpdate) error
|
||||||
|
UpdatePersistenceConf(req dto.RedisConfPersistenceUpdate) error
|
||||||
|
|
||||||
LoadStatus() (*dto.RedisStatus, error)
|
LoadStatus() (*dto.RedisStatus, error)
|
||||||
LoadConf() (*dto.RedisConf, error)
|
LoadConf() (*dto.RedisConf, error)
|
||||||
LoadPersistenceConf() (*dto.RedisPersistence, error)
|
LoadPersistenceConf() (*dto.RedisPersistence, error)
|
||||||
|
|
||||||
// Backup(db dto.BackupDB) error
|
Backup() error
|
||||||
// Recover(db dto.RecoverDB) error
|
SearchBackupListWithPage(req dto.PageInfo) (int64, interface{}, error)
|
||||||
|
Recover(req dto.RedisBackupRecover) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewIRedisService() IRedisService {
|
func NewIRedisService() IRedisService {
|
||||||
return &RedisService{}
|
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 {
|
func (u *RedisService) UpdateConf(req dto.RedisConfUpdate) error {
|
||||||
redisInfo, err := mysqlRepo.LoadRedisBaseInfo()
|
redisInfo, err := mysqlRepo.LoadRedisBaseInfo()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer file.Close()
|
|
||||||
|
|
||||||
reader := bufio.NewReader(file)
|
if req.Type == "rbd" {
|
||||||
pos := int64(0)
|
if err := configSetStr(redisInfo.ContainerName, redisInfo.Password, "save", req.Save); err != nil {
|
||||||
for {
|
return err
|
||||||
line, err := reader.ReadString('\n')
|
|
||||||
if err != nil {
|
|
||||||
if err == io.EOF {
|
|
||||||
break
|
|
||||||
} else {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if bytes := updateConfFile(line, "timeout", req.Timeout); len(bytes) != 0 {
|
} else {
|
||||||
_, _ = file.WriteAt(bytes, pos)
|
if err := configSetStr(redisInfo.ContainerName, redisInfo.Password, "appendonly", req.Appendonly); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
if bytes := updateConfFile(line, "maxclients", req.Maxclients); len(bytes) != 0 {
|
if err := configSetStr(redisInfo.ContainerName, redisInfo.Password, "appendfsync", req.Appendfsync); err != nil {
|
||||||
_, _ = file.WriteAt(bytes, pos)
|
return err
|
||||||
}
|
}
|
||||||
if bytes := updateConfFile(line, "databases", req.Databases); len(bytes) != 0 {
|
}
|
||||||
_, _ = file.WriteAt(bytes, pos)
|
commands := append(redisExec(redisInfo.ContainerName, redisInfo.Password), []string{"config", "rewrite"}...)
|
||||||
}
|
cmd := exec.Command("docker", commands...)
|
||||||
if bytes := updateConfFile(line, "requirepass", req.Requirepass); len(bytes) != 0 {
|
stdout, err := cmd.CombinedOutput()
|
||||||
_, _ = file.WriteAt(bytes, pos)
|
if err != nil {
|
||||||
}
|
return errors.New(string(stdout))
|
||||||
if bytes := updateConfFile(line, "maxmemory", req.Maxmemory); len(bytes) != 0 {
|
|
||||||
_, _ = file.WriteAt(bytes, pos)
|
|
||||||
}
|
|
||||||
pos += int64(len(line))
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *RedisService) LoadStatus() (*dto.RedisStatus, error) {
|
func (u *RedisService) LoadStatus() (*dto.RedisStatus, error) {
|
||||||
client, err := newRedisClient()
|
redisInfo, err := mysqlRepo.LoadRedisBaseInfo()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
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 {
|
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)
|
rowMap := make(map[string]string)
|
||||||
for _, v := range rows {
|
for _, v := range rows {
|
||||||
itemRow := strings.Split(v, ":")
|
itemRow := strings.Split(v, ":")
|
||||||
@ -116,19 +129,22 @@ func (u *RedisService) LoadConf() (*dto.RedisConf, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
client := redis.NewClient(&redis.Options{
|
|
||||||
Addr: fmt.Sprintf("localhost:%v", redisInfo.Port),
|
|
||||||
Password: redisInfo.Password,
|
|
||||||
DB: 0,
|
|
||||||
})
|
|
||||||
var item dto.RedisConf
|
var item dto.RedisConf
|
||||||
item.ContainerName = redisInfo.ContainerName
|
item.ContainerName = redisInfo.ContainerName
|
||||||
item.Name = redisInfo.Name
|
item.Name = redisInfo.Name
|
||||||
item.Timeout = configGetStr(client, "timeout")
|
if item.Timeout, err = configGetStr(redisInfo.ContainerName, redisInfo.Password, "timeout"); err != nil {
|
||||||
item.Maxclients = configGetStr(client, "maxclients")
|
return nil, err
|
||||||
item.Databases = configGetStr(client, "databases")
|
}
|
||||||
item.Requirepass = configGetStr(client, "requirepass")
|
if item.Maxclients, err = configGetStr(redisInfo.ContainerName, redisInfo.Password, "maxclients"); err != nil {
|
||||||
item.Maxmemory = configGetStr(client, "maxmemory")
|
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
|
return &item, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,38 +153,153 @@ func (u *RedisService) LoadPersistenceConf() (*dto.RedisPersistence, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
client := redis.NewClient(&redis.Options{
|
|
||||||
Addr: fmt.Sprintf("localhost:%v", redisInfo.Port),
|
|
||||||
Password: redisInfo.Password,
|
|
||||||
DB: 0,
|
|
||||||
})
|
|
||||||
var item dto.RedisPersistence
|
var item dto.RedisPersistence
|
||||||
item.Dir = configGetStr(client, "dir")
|
if item.Appendonly, err = configGetStr(redisInfo.ContainerName, redisInfo.Password, "appendonly"); err != nil {
|
||||||
item.Appendonly = configGetStr(client, "appendonly")
|
return nil, err
|
||||||
item.Appendfsync = configGetStr(client, "appendfsync")
|
}
|
||||||
item.Save = configGetStr(client, "save")
|
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
|
return &item, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func configGetStr(client *redis.Client, param string) string {
|
func (u *RedisService) Backup() error {
|
||||||
item, _ := client.ConfigGet(param).Result()
|
redisInfo, err := mysqlRepo.LoadRedisBaseInfo()
|
||||||
if len(item) == 2 {
|
if err != nil {
|
||||||
if value, ok := item[1].(string); ok {
|
return err
|
||||||
return value
|
}
|
||||||
|
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 {
|
func (u *RedisService) Recover(req dto.RedisBackupRecover) error {
|
||||||
var bytes []byte
|
redisInfo, err := mysqlRepo.LoadRedisBaseInfo()
|
||||||
if strings.HasPrefix(line, param) || strings.HasPrefix(line, "# "+param) {
|
if err != nil {
|
||||||
if len(value) == 0 || value == "0" {
|
return err
|
||||||
bytes = []byte(fmt.Sprintf("# %s", param))
|
|
||||||
} else {
|
|
||||||
bytes = []byte(fmt.Sprintf("%s %v", param, value))
|
|
||||||
}
|
|
||||||
return bytes
|
|
||||||
}
|
}
|
||||||
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
|
||||||
}
|
}
|
||||||
|
@ -1,45 +1,35 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||||
"github.com/go-redis/redis"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMysql(t *testing.T) {
|
func TestMysql(t *testing.T) {
|
||||||
client := redis.NewClient(&redis.Options{
|
cmd := exec.Command("docker", "exec", "1Panel-redis-7.0.5-zgVH-K859", "redis-cli", "config", "get", "save")
|
||||||
Addr: "172.16.10.143:6379",
|
stdout, err := cmd.CombinedOutput()
|
||||||
Password: "",
|
if err != nil {
|
||||||
DB: 0,
|
fmt.Println(string(stdout))
|
||||||
})
|
}
|
||||||
fmt.Println(client.Ping().Result())
|
|
||||||
|
|
||||||
var item dto.RedisPersistence
|
rows := strings.Split(string(stdout), "\r\n")
|
||||||
dir, _ := client.ConfigGet("dir").Result()
|
rowMap := make(map[string]string)
|
||||||
if len(dir) == 2 {
|
for _, v := range rows {
|
||||||
if value, ok := dir[1].(string); ok {
|
itemRow := strings.Split(v, "\n")
|
||||||
item.Dir = value
|
if len(itemRow) == 3 {
|
||||||
|
rowMap[itemRow[0]] = itemRow[1]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
appendonly, _ := client.ConfigGet("appendonly").Result()
|
var info dto.RedisStatus
|
||||||
if len(appendonly) == 2 {
|
arr, err := json.Marshal(rowMap)
|
||||||
if value, ok := appendonly[1].(string); ok {
|
if err != nil {
|
||||||
item.Appendonly = value
|
fmt.Println(err)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
appendfsync, _ := client.ConfigGet("appendfsync").Result()
|
_ = json.Unmarshal(arr, &info)
|
||||||
if len(appendfsync) == 2 {
|
fmt.Println(info)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,12 @@ func (s *DatabaseRouter) InitDatabaseRouter(Router *gin.RouterGroup) {
|
|||||||
cmdRouter.GET("/redis/status", baseApi.LoadRedisStatus)
|
cmdRouter.GET("/redis/status", baseApi.LoadRedisStatus)
|
||||||
cmdRouter.GET("/redis/conf", baseApi.LoadRedisConf)
|
cmdRouter.GET("/redis/conf", baseApi.LoadRedisConf)
|
||||||
cmdRouter.GET("/redis/exec", baseApi.RedisExec)
|
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", baseApi.UpdateRedisConf)
|
||||||
cmdRouter.POST("/redis/conf/update/byfile", baseApi.UpdateRedisConfByFile)
|
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 {
|
export interface RedisConfUpdate {
|
||||||
timeout: string;
|
timeout: string;
|
||||||
maxclients: string;
|
maxclients: string;
|
||||||
databases: string;
|
|
||||||
requirepass: string;
|
requirepass: string;
|
||||||
maxmemory: string;
|
maxmemory: string;
|
||||||
}
|
}
|
||||||
|
export interface RedisConfPersistenceUpdate {
|
||||||
|
type: string;
|
||||||
|
appendonly: string;
|
||||||
|
appendfsync: string;
|
||||||
|
save: string;
|
||||||
|
}
|
||||||
export interface RedisConfUpdateByFile {
|
export interface RedisConfUpdateByFile {
|
||||||
file: string;
|
file: string;
|
||||||
|
restartNow: boolean;
|
||||||
}
|
}
|
||||||
export interface RedisStatus {
|
export interface RedisStatus {
|
||||||
tcp_port: string;
|
tcp_port: string;
|
||||||
@ -138,14 +144,26 @@ export namespace Database {
|
|||||||
name: string;
|
name: string;
|
||||||
timeout: number;
|
timeout: number;
|
||||||
maxclients: number;
|
maxclients: number;
|
||||||
databases: number;
|
|
||||||
requirepass: string;
|
requirepass: string;
|
||||||
maxmemory: number;
|
maxmemory: number;
|
||||||
}
|
}
|
||||||
export interface RedisPersistenceConf {
|
export interface RedisPersistenceConf {
|
||||||
dir: string;
|
|
||||||
appendonly: string;
|
appendonly: string;
|
||||||
appendfsync: string;
|
appendfsync: string;
|
||||||
save: 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 http from '@/api';
|
||||||
import { ResPage } from '../interface';
|
import { ReqPage, ResPage } from '../interface';
|
||||||
import { Backup } from '../interface/backup';
|
import { Backup } from '../interface/backup';
|
||||||
import { Database } from '../interface/database';
|
import { Database } from '../interface/database';
|
||||||
|
|
||||||
@ -59,9 +59,24 @@ export const loadRedisConf = () => {
|
|||||||
export const RedisPersistenceConf = () => {
|
export const RedisPersistenceConf = () => {
|
||||||
return http.get<Database.RedisPersistenceConf>(`/databases/redis/persistence/conf`);
|
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) => {
|
export const updateRedisConf = (params: Database.RedisConfUpdate) => {
|
||||||
return http.post(`/databases/redis/conf/update`, params);
|
return http.post(`/databases/redis/conf/update`, params);
|
||||||
};
|
};
|
||||||
export const updateRedisConfByFile = (params: Database.RedisConfUpdateByFile) => {
|
export const updateRedisConfByFile = (params: Database.RedisConfUpdateByFile) => {
|
||||||
return http.post(`/databases/redis/conf/update/byfile`, params);
|
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: '查找数据库键命中率',
|
hit: '查找数据库键命中率',
|
||||||
latestForkUsec: '最近一次 fork() 操作耗费的微秒数',
|
latestForkUsec: '最近一次 fork() 操作耗费的微秒数',
|
||||||
|
|
||||||
|
baseConf: '基础配置',
|
||||||
|
allConf: '全部配置',
|
||||||
|
restartNow: '立即重启',
|
||||||
|
restartNowHelper1: '修改配置后需要',
|
||||||
|
restartNowHelper2: '重启 redis 生效',
|
||||||
|
restartNowHelper3: ',若您的数据需要持久化请先执行 save 操作。',
|
||||||
|
|
||||||
persistence: '持久化',
|
persistence: '持久化',
|
||||||
persistenceDir: '持久化路径',
|
rdbHelper1: '秒內,插入',
|
||||||
persistenceDirHelper: '设置后将在选择的目录下新建 redis_cache 目录并赋予 redis 权限',
|
rdbHelper2: '条数据',
|
||||||
aofPersistence: 'AOF 持久化',
|
rdbHelper3: '符合任意一个条件将会触发RDB持久化',
|
||||||
rdbPersistence: 'RDB 持久化',
|
rdbInfo: '规则列表存在 0 值,请确认后重试!',
|
||||||
drbHelper1: '秒內,插入',
|
|
||||||
drbHelper2: '条数据',
|
|
||||||
drbHelper3: '符合任意一个条件将会触发RDB持久化',
|
|
||||||
},
|
},
|
||||||
container: {
|
container: {
|
||||||
operatorHelper: '将对选中容器进行 {0} 操作,是否继续?',
|
operatorHelper: '将对选中容器进行 {0} 操作,是否继续?',
|
||||||
|
@ -1,38 +1,147 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-if="persistenceShow">
|
<div v-if="persistenceShow">
|
||||||
<el-card style="margin-top: 5px">
|
<el-row :gutter="20" style="margin-top: 5px" class="row-box">
|
||||||
<el-form :model="form" ref="formRef" :rules="rules" label-width="120px">
|
<el-col :span="12">
|
||||||
<el-row>
|
<el-card class="el-card">
|
||||||
<el-col :span="1"><br /></el-col>
|
<template #header>
|
||||||
<el-col :span="10">
|
<div class="card-header">
|
||||||
<el-form>
|
<span>AOF {{ $t('database.persistence') }}</span>
|
||||||
<el-form-item label="appendonly" prop="appendonly">
|
</div>
|
||||||
<el-switch v-model="form.appendonly"></el-switch>
|
</template>
|
||||||
</el-form-item>
|
<el-form :model="form" ref="formRef" :rules="rules" label-width="120px">
|
||||||
<el-form-item label="appendfsync" prop="appendfsync">
|
<el-row>
|
||||||
<el-radio-group v-model="form.appendfsync">
|
<el-col :span="1"><br /></el-col>
|
||||||
<el-radio label="always">always</el-radio>
|
<el-col :span="10">
|
||||||
<el-radio label="everysec">everysec</el-radio>
|
<el-form>
|
||||||
<el-radio label="no">no</el-radio>
|
<el-form-item label="appendonly" prop="appendonly">
|
||||||
</el-radio-group>
|
<el-switch
|
||||||
</el-form-item>
|
active-value="yes"
|
||||||
</el-form>
|
inactive-value="no"
|
||||||
</el-col>
|
v-model="form.appendonly"
|
||||||
</el-row>
|
></el-switch>
|
||||||
</el-form>
|
</el-form-item>
|
||||||
|
<el-form-item label="appendfsync" prop="appendfsync">
|
||||||
|
<el-radio-group v-model="form.appendfsync">
|
||||||
|
<el-radio label="always">always</el-radio>
|
||||||
|
<el-radio label="everysec">everysec</el-radio>
|
||||||
|
<el-radio label="no">no</el-radio>
|
||||||
|
</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>
|
</el-card>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<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 { 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 { 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({
|
const form = reactive({
|
||||||
appendonly: '',
|
appendonly: '',
|
||||||
appendfsync: 'no',
|
appendfsync: 'no',
|
||||||
|
saves: [] as Array<saveStruct>,
|
||||||
});
|
});
|
||||||
const rules = reactive({
|
const rules = reactive({
|
||||||
appendonly: [Rules.requiredSelect],
|
appendonly: [Rules.requiredSelect],
|
||||||
@ -44,36 +153,124 @@ const persistenceShow = ref(false);
|
|||||||
const acceptParams = (): void => {
|
const acceptParams = (): void => {
|
||||||
persistenceShow.value = true;
|
persistenceShow.value = true;
|
||||||
loadform();
|
loadform();
|
||||||
|
loadBackupRecords();
|
||||||
};
|
};
|
||||||
const onClose = (): void => {
|
const onClose = (): void => {
|
||||||
persistenceShow.value = false;
|
persistenceShow.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// const onSave = async (formEl: FormInstance | undefined, key: string) => {
|
const data = ref();
|
||||||
// if (!formEl) return;
|
const selects = ref<any>([]);
|
||||||
// const result = await formEl.validateField(key, callback);
|
const paginationConfig = reactive({
|
||||||
// if (!result) {
|
currentPage: 1,
|
||||||
// return;
|
pageSize: 10,
|
||||||
// }
|
total: 0,
|
||||||
// // let changeForm = {
|
});
|
||||||
// // paramName: key,
|
|
||||||
// // value: val + '',
|
const handleAdd = () => {
|
||||||
// // };
|
let item = {
|
||||||
// // await updateRedisConf(changeForm);
|
second: 0,
|
||||||
// ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
count: 0,
|
||||||
// };
|
};
|
||||||
// function callback(error: any) {
|
form.saves.push(item);
|
||||||
// if (error) {
|
};
|
||||||
// return error.message;
|
const handleDelete = (index: number) => {
|
||||||
// } else {
|
form.saves.splice(index, 1);
|
||||||
// return;
|
};
|
||||||
// }
|
|
||||||
// }
|
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 () => {
|
const loadform = async () => {
|
||||||
|
form.saves = [];
|
||||||
const res = await RedisPersistenceConf();
|
const res = await RedisPersistenceConf();
|
||||||
form.appendonly = res.data?.appendonly;
|
form.appendonly = res.data?.appendonly;
|
||||||
form.appendfsync = res.data?.appendfsync;
|
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({
|
defineExpose({
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
<div v-if="settingShow">
|
<div v-if="settingShow">
|
||||||
<el-card style="margin-top: 5px">
|
<el-card style="margin-top: 5px">
|
||||||
<el-radio-group v-model="confShowType">
|
<el-radio-group v-model="confShowType">
|
||||||
<el-radio-button label="base">基础配置</el-radio-button>
|
<el-radio-button label="base">{{ $t('database.baseConf') }}</el-radio-button>
|
||||||
<el-radio-button label="all">全部配置</el-radio-button>
|
<el-radio-button label="all">{{ $t('database.allConf') }}</el-radio-button>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
<el-form v-if="confShowType === 'base'" :model="form" ref="formRef" :rules="rules" label-width="120px">
|
<el-form v-if="confShowType === 'base'" :model="form" ref="formRef" :rules="rules" label-width="120px">
|
||||||
<el-row style="margin-top: 20px">
|
<el-row style="margin-top: 20px">
|
||||||
@ -23,9 +23,6 @@
|
|||||||
<el-form-item :label="$t('database.maxclients')" prop="maxclients">
|
<el-form-item :label="$t('database.maxclients')" prop="maxclients">
|
||||||
<el-input clearable type="number" v-model.number="form.maxclients" />
|
<el-input clearable type="number" v-model.number="form.maxclients" />
|
||||||
</el-form-item>
|
</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-form-item :label="$t('database.maxmemory')" prop="maxmemory">
|
||||||
<el-input clearable type="number" v-model.number="form.maxmemory" />
|
<el-input clearable type="number" v-model.number="form.maxmemory" />
|
||||||
<span class="input-help">{{ $t('database.maxmemoryHelper') }}</span>
|
<span class="input-help">{{ $t('database.maxmemoryHelper') }}</span>
|
||||||
@ -53,11 +50,40 @@
|
|||||||
v-model="mysqlConf"
|
v-model="mysqlConf"
|
||||||
:readOnly="true"
|
: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') }}
|
{{ $t('commons.button.save') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
</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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -75,20 +101,20 @@ import { Rules } from '@/global/form-rules';
|
|||||||
const extensions = [javascript(), oneDark];
|
const extensions = [javascript(), oneDark];
|
||||||
const confShowType = ref('base');
|
const confShowType = ref('base');
|
||||||
|
|
||||||
|
const restartNow = ref(false);
|
||||||
|
const saveVisiable = ref(false);
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
name: '',
|
name: '',
|
||||||
port: 3306,
|
port: 3306,
|
||||||
requirepass: '',
|
requirepass: '',
|
||||||
timeout: 0,
|
timeout: 0,
|
||||||
maxclients: 0,
|
maxclients: 0,
|
||||||
databases: 0,
|
|
||||||
maxmemory: 0,
|
maxmemory: 0,
|
||||||
});
|
});
|
||||||
const rules = reactive({
|
const rules = reactive({
|
||||||
port: [Rules.port],
|
port: [Rules.port],
|
||||||
timeout: [Rules.number],
|
timeout: [Rules.number],
|
||||||
maxclients: [Rules.number],
|
maxclients: [Rules.number],
|
||||||
databases: [Rules.number],
|
|
||||||
maxmemory: [Rules.number],
|
maxmemory: [Rules.number],
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -106,17 +132,20 @@ const onClose = (): void => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const onSave = async (formEl: FormInstance | undefined) => {
|
const onSave = async (formEl: FormInstance | undefined) => {
|
||||||
|
if (confShowType.value === 'all') {
|
||||||
|
onSaveFile();
|
||||||
|
}
|
||||||
if (!formEl) return;
|
if (!formEl) return;
|
||||||
formEl.validate(async (valid) => {
|
formEl.validate(async (valid) => {
|
||||||
if (!valid) return;
|
if (!valid) return;
|
||||||
let param = {
|
let param = {
|
||||||
timeout: form.timeout + '',
|
timeout: form.timeout + '',
|
||||||
maxclients: form.maxclients + '',
|
maxclients: form.maxclients + '',
|
||||||
databases: form.databases + '',
|
|
||||||
requirepass: form.requirepass,
|
requirepass: form.requirepass,
|
||||||
maxmemory: form.maxmemory + '',
|
maxmemory: form.maxmemory + '',
|
||||||
};
|
};
|
||||||
await updateRedisConf(param);
|
await updateRedisConf(param);
|
||||||
|
saveVisiable.value = false;
|
||||||
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -124,8 +153,11 @@ const onSave = async (formEl: FormInstance | undefined) => {
|
|||||||
const onSaveFile = async () => {
|
const onSaveFile = async () => {
|
||||||
let param = {
|
let param = {
|
||||||
file: mysqlConf.value,
|
file: mysqlConf.value,
|
||||||
|
restartNow: restartNow.value,
|
||||||
};
|
};
|
||||||
await updateRedisConfByFile(param);
|
await updateRedisConfByFile(param);
|
||||||
|
saveVisiable.value = false;
|
||||||
|
restartNow.value = false;
|
||||||
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -134,7 +166,6 @@ const loadform = async () => {
|
|||||||
form.name = res.data?.name;
|
form.name = res.data?.name;
|
||||||
form.timeout = Number(res.data?.timeout);
|
form.timeout = Number(res.data?.timeout);
|
||||||
form.maxclients = Number(res.data?.maxclients);
|
form.maxclients = Number(res.data?.maxclients);
|
||||||
form.databases = Number(res.data?.databases);
|
|
||||||
form.requirepass = res.data?.requirepass;
|
form.requirepass = res.data?.requirepass;
|
||||||
form.maxmemory = Number(res.data?.maxmemory);
|
form.maxmemory = Number(res.data?.maxmemory);
|
||||||
loadMysqlConf(`/opt/1Panel/data/apps/redis/${form.name}/conf/redis.conf`);
|
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/gin-gonic/gin v1.8.1
|
||||||
github.com/go-gormigrate/gormigrate/v2 v2.0.2
|
github.com/go-gormigrate/gormigrate/v2 v2.0.2
|
||||||
github.com/go-playground/validator/v10 v10.11.0
|
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/go-sql-driver/mysql v1.6.0
|
||||||
github.com/gogf/gf v1.16.9
|
github.com/gogf/gf v1.16.9
|
||||||
github.com/golang-jwt/jwt/v4 v4.4.2
|
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/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/nwaples/rardecode/v2 v2.0.0-beta.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/go-digest v1.0.0 // indirect
|
||||||
github.com/opencontainers/runc v1.1.4 // indirect
|
github.com/opencontainers/runc v1.1.4 // indirect
|
||||||
github.com/pelletier/go-toml v1.9.5 // 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.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 h1:0W+xRM511GY47Yy3bZUbJVitCNg2BOGlCyvTqsp/xIw=
|
||||||
github.com/go-playground/validator/v10 v10.11.0/go.mod h1:i+3WkQ1FvaUjjxh1kSvIA4dMGDBiPU55YFDl0WbKdWU=
|
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 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
||||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
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-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 h1:IcB+Aqpx/iMHu5Yooh7jEzJk1JZ7Pjtmys2ukPr7EeM=
|
||||||
github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
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=
|
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 h1:e3mzJFJs4k83GXBEiTaQ5HgSc/kOK8q0rDaRO0MPaOk=
|
||||||
github.com/nwaples/rardecode/v2 v2.0.0-beta.2/go.mod h1:yntwv/HfMc/Hbvtq9I19D1n58te3h6KsqCf3GxyfBGY=
|
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.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/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.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
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.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.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
|
||||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
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-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 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.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.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.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.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.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-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 v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||||
github.com/opencontainers/go-digest v1.0.0-rc1/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-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-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-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-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-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/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-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-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-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-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-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/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-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-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-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-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-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/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.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.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/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/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.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
Loading…
x
Reference in New Issue
Block a user