mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-01-19 16:29:17 +08:00
feat: 完成 redis 命令行模式功能
This commit is contained in:
parent
bc5b269691
commit
b3bc8769b3
1372
apps/redis/7.0.5/conf/redis.conf
Normal file
1372
apps/redis/7.0.5/conf/redis.conf
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,88 +1,23 @@
|
|||||||
package v1
|
package v1
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
||||||
"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/docker"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/utils/terminal"
|
||||||
|
"github.com/docker/docker/api/types"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (b *BaseApi) SetRedis(c *gin.Context) {
|
|
||||||
var req dto.RedisDataSet
|
|
||||||
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.Set(req); err != nil {
|
|
||||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
helper.SuccessWithData(c, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *BaseApi) SearchRedis(c *gin.Context) {
|
|
||||||
var req dto.SearchRedisWithPage
|
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
|
||||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
total, list, err := redisService.SearchWithPage(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) DeleteRedis(c *gin.Context) {
|
|
||||||
var req dto.RedisDelBatch
|
|
||||||
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.Delete(req); err != nil {
|
|
||||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
helper.SuccessWithData(c, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *BaseApi) LoadRedisRunningVersion(c *gin.Context) {
|
|
||||||
list, err := redisService.LoadRedisRunningVersion()
|
|
||||||
if err != nil {
|
|
||||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
helper.SuccessWithData(c, list)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (b *BaseApi) LoadRedisStatus(c *gin.Context) {
|
func (b *BaseApi) LoadRedisStatus(c *gin.Context) {
|
||||||
var req dto.RedisBaseReq
|
data, err := redisService.LoadStatus()
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := redisService.LoadState(req)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
return
|
return
|
||||||
@ -92,17 +27,7 @@ func (b *BaseApi) LoadRedisStatus(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *BaseApi) LoadRedisConf(c *gin.Context) {
|
func (b *BaseApi) LoadRedisConf(c *gin.Context) {
|
||||||
var req dto.RedisBaseReq
|
data, err := redisService.LoadConf()
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := redisService.LoadConf(req)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
return
|
return
|
||||||
@ -111,22 +36,14 @@ func (b *BaseApi) LoadRedisConf(c *gin.Context) {
|
|||||||
helper.SuccessWithData(c, data)
|
helper.SuccessWithData(c, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BaseApi) CleanRedis(c *gin.Context) {
|
func (b *BaseApi) LoadPersistenceConf(c *gin.Context) {
|
||||||
var req dto.RedisBaseReq
|
data, err := redisService.LoadPersistenceConf()
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
if 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.CleanAll(req); err != nil {
|
|
||||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
helper.SuccessWithData(c, nil)
|
|
||||||
|
helper.SuccessWithData(c, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *BaseApi) UpdateRedisConf(c *gin.Context) {
|
func (b *BaseApi) UpdateRedisConf(c *gin.Context) {
|
||||||
@ -145,3 +62,64 @@ func (b *BaseApi) UpdateRedisConf(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
helper.SuccessWithData(c, nil)
|
helper.SuccessWithData(c, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *BaseApi) RedisExec(c *gin.Context) {
|
||||||
|
redisConf, err := redisService.LoadConf()
|
||||||
|
if err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cols, err := strconv.Atoi(c.DefaultQuery("cols", "80"))
|
||||||
|
if err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
rows, err := strconv.Atoi(c.DefaultQuery("rows", "40"))
|
||||||
|
if err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
wsConn, err := upGrader.Upgrade(c.Writer, c.Request, nil)
|
||||||
|
if err != nil {
|
||||||
|
global.LOG.Errorf("gin context http handler failed, err: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer wsConn.Close()
|
||||||
|
|
||||||
|
client, err := docker.NewDockerClient()
|
||||||
|
if wshandleError(wsConn, errors.WithMessage(err, "New docker client failed.")) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
auth := "redis-cli"
|
||||||
|
if len(redisConf.Requirepass) != 0 {
|
||||||
|
auth = fmt.Sprintf("redis-cli -a %s --no-auth-warning", redisConf.Requirepass)
|
||||||
|
}
|
||||||
|
conf := types.ExecConfig{Tty: true, Cmd: []string{"bash"}, AttachStderr: true, AttachStdin: true, AttachStdout: true, User: "root"}
|
||||||
|
ir, err := client.ContainerExecCreate(context.TODO(), redisConf.ContainerName, conf)
|
||||||
|
if wshandleError(wsConn, errors.WithMessage(err, "failed to set exec conf.")) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
hr, err := client.ContainerExecAttach(c, ir.ID, types.ExecStartCheck{Detach: false, Tty: true})
|
||||||
|
if wshandleError(wsConn, errors.WithMessage(err, "failed to set up the connection.")) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer hr.Close()
|
||||||
|
|
||||||
|
sws, err := terminal.NewExecConn(cols, rows, wsConn, hr.Conn, auth)
|
||||||
|
if wshandleError(wsConn, err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
quitChan := make(chan bool, 3)
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
sws.Start(ctx, quitChan)
|
||||||
|
<-quitChan
|
||||||
|
cancel()
|
||||||
|
|
||||||
|
if wshandleError(wsConn, err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -2,8 +2,8 @@ package v1
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
"github.com/1Panel-dev/1Panel/backend/app/api/v1/helper"
|
||||||
@ -240,18 +240,13 @@ func (b *BaseApi) LoadFromFile(c *gin.Context) {
|
|||||||
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
file, err := os.Open(req.Path)
|
|
||||||
|
content, err := ioutil.ReadFile(req.Path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer file.Close()
|
helper.SuccessWithData(c, string(content))
|
||||||
buf := make([]byte, 1024*2)
|
|
||||||
if _, err := file.Read(buf); err != nil {
|
|
||||||
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
helper.SuccessWithData(c, string(buf))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var wsUpgrade = websocket.Upgrader{
|
var wsUpgrade = websocket.Upgrader{
|
||||||
|
@ -138,39 +138,6 @@ type RecoverDB struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// redis
|
// redis
|
||||||
type SearchRedisWithPage struct {
|
|
||||||
PageInfo
|
|
||||||
RedisName string `json:"redisName" validate:"required"`
|
|
||||||
DB int `json:"db" validate:"required"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type RedisData struct {
|
|
||||||
Key string `json:"key"`
|
|
||||||
Value string `json:"value"`
|
|
||||||
Type string `json:"type"`
|
|
||||||
Length int64 `json:"length"`
|
|
||||||
Expiration int64 `json:"expiration"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type RedisDataSet struct {
|
|
||||||
RedisName string `json:"redisName" validate:"required"`
|
|
||||||
DB int `json:"db"`
|
|
||||||
Key string `json:"key" validate:"required"`
|
|
||||||
Value string `json:"value" validate:"required"`
|
|
||||||
Expiration int64 `json:"expiration"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type RedisDelBatch struct {
|
|
||||||
RedisName string `json:"redisName" validate:"required"`
|
|
||||||
DB int `json:"db" validate:"required"`
|
|
||||||
Names []string `json:"names" validate:"required"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type RedisBaseReq struct {
|
|
||||||
RedisName string `json:"redisName" validate:"required"`
|
|
||||||
DB int `json:"db"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type RedisConfUpdate struct {
|
type RedisConfUpdate struct {
|
||||||
RedisName string `json:"redisName" validate:"required"`
|
RedisName string `json:"redisName" validate:"required"`
|
||||||
DB int `json:"db"`
|
DB int `json:"db"`
|
||||||
@ -179,12 +146,16 @@ type RedisConfUpdate struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type RedisConf struct {
|
type RedisConf struct {
|
||||||
Timeout string `json:"timeout"`
|
Name string `json:"name"`
|
||||||
Maxclients string `json:"maxclients"`
|
ContainerName string `json:"containerName"`
|
||||||
Databases string `json:"databases"`
|
Timeout string `json:"timeout"`
|
||||||
Requirepass string `json:"requirepass"`
|
Maxclients string `json:"maxclients"`
|
||||||
Maxmemory string `json:"maxmemory"`
|
Databases string `json:"databases"`
|
||||||
|
Requirepass string `json:"requirepass"`
|
||||||
|
Maxmemory string `json:"maxmemory"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RedisPersistence struct {
|
||||||
Dir string `json:"dir"`
|
Dir string `json:"dir"`
|
||||||
Appendonly string `json:"appendonly"`
|
Appendonly string `json:"appendonly"`
|
||||||
Appendfsync string `json:"appendfsync"`
|
Appendfsync string `json:"appendfsync"`
|
||||||
|
@ -21,7 +21,7 @@ type IMysqlRepo interface {
|
|||||||
Update(id uint, vars map[string]interface{}) error
|
Update(id uint, vars map[string]interface{}) error
|
||||||
LoadRunningVersion(keys []string) ([]string, error)
|
LoadRunningVersion(keys []string) ([]string, error)
|
||||||
LoadBaseInfoByName(name string) (*RootInfo, error)
|
LoadBaseInfoByName(name string) (*RootInfo, error)
|
||||||
LoadRedisBaseInfoByName(name string) (*RootInfo, error)
|
LoadRedisBaseInfo() (*RootInfo, error)
|
||||||
UpdateMysqlConf(id uint, vars map[string]interface{}) error
|
UpdateMysqlConf(id uint, vars map[string]interface{}) error
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,7 +129,7 @@ func (u *MysqlRepo) LoadBaseInfoByName(name string) (*RootInfo, error) {
|
|||||||
return &info, nil
|
return &info, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *MysqlRepo) LoadRedisBaseInfoByName(name string) (*RootInfo, error) {
|
func (u *MysqlRepo) LoadRedisBaseInfo() (*RootInfo, error) {
|
||||||
var (
|
var (
|
||||||
app model.App
|
app model.App
|
||||||
appInstall model.AppInstall
|
appInstall model.AppInstall
|
||||||
@ -138,7 +138,7 @@ func (u *MysqlRepo) LoadRedisBaseInfoByName(name string) (*RootInfo, error) {
|
|||||||
if err := global.DB.Where("key = ?", "redis").First(&app).Error; err != nil {
|
if err := global.DB.Where("key = ?", "redis").First(&app).Error; err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := global.DB.Where("app_id = ? AND name = ?", app.ID, name).First(&appInstall).Error; err != nil {
|
if err := global.DB.Where("app_id = ?", app.ID).First(&appInstall).Error; err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
envMap := make(map[string]interface{})
|
envMap := make(map[string]interface{})
|
||||||
|
@ -4,7 +4,6 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"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/go-redis/redis"
|
||||||
@ -14,16 +13,11 @@ import (
|
|||||||
type RedisService struct{}
|
type RedisService struct{}
|
||||||
|
|
||||||
type IRedisService interface {
|
type IRedisService interface {
|
||||||
SearchWithPage(search dto.SearchRedisWithPage) (int64, interface{}, error)
|
|
||||||
Set(setData dto.RedisDataSet) error
|
|
||||||
Delete(info dto.RedisDelBatch) error
|
|
||||||
|
|
||||||
UpdateConf(req dto.RedisConfUpdate) error
|
UpdateConf(req dto.RedisConfUpdate) error
|
||||||
|
|
||||||
CleanAll(req dto.RedisBaseReq) error
|
LoadStatus() (*dto.RedisStatus, error)
|
||||||
LoadState(req dto.RedisBaseReq) (*dto.RedisStatus, error)
|
LoadConf() (*dto.RedisConf, error)
|
||||||
LoadConf(req dto.RedisBaseReq) (*dto.RedisConf, error)
|
LoadPersistenceConf() (*dto.RedisPersistence, error)
|
||||||
LoadRedisRunningVersion() ([]string, error)
|
|
||||||
|
|
||||||
// Backup(db dto.BackupDB) error
|
// Backup(db dto.BackupDB) error
|
||||||
// Recover(db dto.RecoverDB) error
|
// Recover(db dto.RecoverDB) error
|
||||||
@ -33,67 +27,21 @@ func NewIRedisService() IRedisService {
|
|||||||
return &RedisService{}
|
return &RedisService{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newRedisClient(name string, db int) (*redis.Client, error) {
|
func newRedisClient() (*redis.Client, error) {
|
||||||
redisInfo, err := mysqlRepo.LoadRedisBaseInfoByName(name)
|
redisInfo, err := mysqlRepo.LoadRedisBaseInfo()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
client := redis.NewClient(&redis.Options{
|
client := redis.NewClient(&redis.Options{
|
||||||
Addr: fmt.Sprintf("localhost:%v", redisInfo.Port),
|
Addr: fmt.Sprintf("localhost:%v", redisInfo.Port),
|
||||||
Password: "eYVX7EwVmmxKPCDmwMtyKVge8oLd2t81",
|
Password: redisInfo.Password,
|
||||||
DB: db,
|
DB: 0,
|
||||||
})
|
})
|
||||||
return client, nil
|
return client, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *RedisService) SearchWithPage(search dto.SearchRedisWithPage) (int64, interface{}, error) {
|
|
||||||
client, err := newRedisClient(search.RedisName, search.DB)
|
|
||||||
if err != nil {
|
|
||||||
return 0, nil, err
|
|
||||||
}
|
|
||||||
total, err := client.DbSize().Result()
|
|
||||||
if err != nil {
|
|
||||||
return 0, nil, err
|
|
||||||
}
|
|
||||||
keys, _, err := client.Scan(uint64((search.Page-1)*search.PageSize), "*", int64(search.PageSize)).Result()
|
|
||||||
if err != nil {
|
|
||||||
return 0, nil, err
|
|
||||||
}
|
|
||||||
var data []dto.RedisData
|
|
||||||
for _, key := range keys {
|
|
||||||
var dataItem dto.RedisData
|
|
||||||
dataItem.Key = key
|
|
||||||
value, err := client.Get(key).Result()
|
|
||||||
if err != nil {
|
|
||||||
return 0, nil, err
|
|
||||||
}
|
|
||||||
dataItem.Value = value
|
|
||||||
typeVal, err := client.Type(key).Result()
|
|
||||||
if err != nil {
|
|
||||||
return 0, nil, err
|
|
||||||
}
|
|
||||||
dataItem.Type = typeVal
|
|
||||||
length, err := client.StrLen(key).Result()
|
|
||||||
if err != nil {
|
|
||||||
return 0, nil, err
|
|
||||||
}
|
|
||||||
dataItem.Length = length
|
|
||||||
ttl, err := client.TTL(key).Result()
|
|
||||||
if err != nil {
|
|
||||||
return 0, nil, err
|
|
||||||
}
|
|
||||||
dataItem.Expiration = int64(ttl / 1000000000)
|
|
||||||
data = append(data, dataItem)
|
|
||||||
}
|
|
||||||
return total, data, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *RedisService) LoadRedisRunningVersion() ([]string, error) {
|
|
||||||
return mysqlRepo.LoadRunningVersion([]string{"redis"})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *RedisService) UpdateConf(req dto.RedisConfUpdate) error {
|
func (u *RedisService) UpdateConf(req dto.RedisConfUpdate) error {
|
||||||
client, err := newRedisClient(req.RedisName, 0)
|
client, err := newRedisClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -107,50 +55,8 @@ func (u *RedisService) UpdateConf(req dto.RedisConfUpdate) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *RedisService) Set(setData dto.RedisDataSet) error {
|
func (u *RedisService) LoadStatus() (*dto.RedisStatus, error) {
|
||||||
client, err := newRedisClient(setData.RedisName, setData.DB)
|
client, err := newRedisClient()
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
value, _ := client.Get(setData.Key).Result()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if len(value) != 0 {
|
|
||||||
if _, err := client.Del(setData.Key).Result(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if _, err := client.Set(setData.Key, setData.Value, time.Duration(setData.Expiration*int64(time.Second))).Result(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *RedisService) Delete(req dto.RedisDelBatch) error {
|
|
||||||
client, err := newRedisClient(req.RedisName, req.DB)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if _, err := client.Del(req.Names...).Result(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *RedisService) CleanAll(req dto.RedisBaseReq) error {
|
|
||||||
client, err := newRedisClient(req.RedisName, req.DB)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if _, err := client.FlushAll().Result(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *RedisService) LoadState(req dto.RedisBaseReq) (*dto.RedisStatus, error) {
|
|
||||||
client, err := newRedisClient(req.RedisName, req.DB)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -175,18 +81,38 @@ func (u *RedisService) LoadState(req dto.RedisBaseReq) (*dto.RedisStatus, error)
|
|||||||
return &info, nil
|
return &info, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *RedisService) LoadConf(req dto.RedisBaseReq) (*dto.RedisConf, error) {
|
func (u *RedisService) LoadConf() (*dto.RedisConf, error) {
|
||||||
client, err := newRedisClient(req.RedisName, req.DB)
|
redisInfo, err := mysqlRepo.LoadRedisBaseInfo()
|
||||||
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.Name = redisInfo.Name
|
||||||
item.Timeout = configGetStr(client, "timeout")
|
item.Timeout = configGetStr(client, "timeout")
|
||||||
item.Maxclients = configGetStr(client, "maxclients")
|
item.Maxclients = configGetStr(client, "maxclients")
|
||||||
item.Databases = configGetStr(client, "databases")
|
item.Databases = configGetStr(client, "databases")
|
||||||
item.Requirepass = configGetStr(client, "requirepass")
|
item.Requirepass = configGetStr(client, "requirepass")
|
||||||
item.Maxmemory = configGetStr(client, "maxmemory")
|
item.Maxmemory = configGetStr(client, "maxmemory")
|
||||||
|
return &item, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *RedisService) LoadPersistenceConf() (*dto.RedisPersistence, 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,
|
||||||
|
})
|
||||||
|
var item dto.RedisPersistence
|
||||||
item.Dir = configGetStr(client, "dir")
|
item.Dir = configGetStr(client, "dir")
|
||||||
item.Appendonly = configGetStr(client, "appendonly")
|
item.Appendonly = configGetStr(client, "appendonly")
|
||||||
item.Appendfsync = configGetStr(client, "appendfsync")
|
item.Appendfsync = configGetStr(client, "appendfsync")
|
||||||
|
@ -10,12 +10,13 @@ import (
|
|||||||
|
|
||||||
func TestMysql(t *testing.T) {
|
func TestMysql(t *testing.T) {
|
||||||
client := redis.NewClient(&redis.Options{
|
client := redis.NewClient(&redis.Options{
|
||||||
Addr: "localhost:6379",
|
Addr: "172.16.10.143:6379",
|
||||||
Password: "eYVX7EwVmmxKPCDmwMtyKVge8oLd2t81",
|
Password: "",
|
||||||
DB: 0,
|
DB: 0,
|
||||||
})
|
})
|
||||||
|
fmt.Println(client.Ping().Result())
|
||||||
|
|
||||||
var item dto.RedisConf
|
var item dto.RedisPersistence
|
||||||
dir, _ := client.ConfigGet("dir").Result()
|
dir, _ := client.ConfigGet("dir").Result()
|
||||||
if len(dir) == 2 {
|
if len(dir) == 2 {
|
||||||
if value, ok := dir[1].(string); ok {
|
if value, ok := dir[1].(string); ok {
|
||||||
|
@ -35,13 +35,9 @@ func (s *DatabaseRouter) InitDatabaseRouter(Router *gin.RouterGroup) {
|
|||||||
cmdRouter.GET("/versions", baseApi.LoadVersions)
|
cmdRouter.GET("/versions", baseApi.LoadVersions)
|
||||||
cmdRouter.GET("/dbs/:name", baseApi.ListDBNameByVersion)
|
cmdRouter.GET("/dbs/:name", baseApi.ListDBNameByVersion)
|
||||||
|
|
||||||
cmdRouter.POST("/redis/search", baseApi.SearchRedis)
|
cmdRouter.GET("/redis/persistence/conf", baseApi.LoadPersistenceConf)
|
||||||
withRecordRouter.POST("/redis", baseApi.SetRedis)
|
cmdRouter.GET("/redis/status", baseApi.LoadRedisStatus)
|
||||||
withRecordRouter.POST("/redis/del", baseApi.DeleteRedis)
|
cmdRouter.GET("/redis/conf", baseApi.LoadRedisConf)
|
||||||
withRecordRouter.POST("/redis/cleanall", baseApi.CleanRedis)
|
cmdRouter.GET("/redis/exec", baseApi.RedisExec)
|
||||||
withRecordRouter.POST("/redis/status", baseApi.LoadRedisStatus)
|
|
||||||
withRecordRouter.POST("/redis/conf/update", baseApi.UpdateRedisConf)
|
|
||||||
withRecordRouter.POST("/redis/conf", baseApi.LoadRedisConf)
|
|
||||||
cmdRouter.GET("/redis/versions", baseApi.LoadRedisRunningVersion)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,8 +20,11 @@ type ExecWsSession struct {
|
|||||||
writeMutex sync.Mutex
|
writeMutex sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewExecConn(cols, rows int, wsConn *websocket.Conn, hijacked net.Conn) (*ExecWsSession, error) {
|
func NewExecConn(cols, rows int, wsConn *websocket.Conn, hijacked net.Conn, commands ...string) (*ExecWsSession, error) {
|
||||||
_, _ = hijacked.Write([]byte(fmt.Sprintf("stty cols %d rows %d && clear \r", cols, rows)))
|
_, _ = hijacked.Write([]byte(fmt.Sprintf("stty cols %d rows %d && clear \r", cols, rows)))
|
||||||
|
for _, command := range commands {
|
||||||
|
_, _ = hijacked.Write([]byte(fmt.Sprintf("%s \r", command)))
|
||||||
|
}
|
||||||
|
|
||||||
return &ExecWsSession{
|
return &ExecWsSession{
|
||||||
conn: hijacked,
|
conn: hijacked,
|
||||||
|
@ -105,41 +105,12 @@ export namespace Database {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// redis
|
// redis
|
||||||
export interface SearchRedisWithPage extends ReqPage {
|
|
||||||
redisName: string;
|
|
||||||
db: number;
|
|
||||||
}
|
|
||||||
export interface RedisBaseReq {
|
|
||||||
redisName: string;
|
|
||||||
db: number;
|
|
||||||
}
|
|
||||||
export interface RedisConfUpdate {
|
export interface RedisConfUpdate {
|
||||||
redisName: string;
|
redisName: string;
|
||||||
db: number;
|
db: number;
|
||||||
paramName: string;
|
paramName: string;
|
||||||
value: string;
|
value: string;
|
||||||
}
|
}
|
||||||
export interface RedisData {
|
|
||||||
redisName: string;
|
|
||||||
db: number;
|
|
||||||
key: string;
|
|
||||||
value: string;
|
|
||||||
type: string;
|
|
||||||
length: number;
|
|
||||||
expiration: number;
|
|
||||||
}
|
|
||||||
export interface RedisDataSet {
|
|
||||||
redisName: string;
|
|
||||||
db: number;
|
|
||||||
key: string;
|
|
||||||
value: string;
|
|
||||||
expiration: number;
|
|
||||||
}
|
|
||||||
export interface RedisDelBatch {
|
|
||||||
redisName: string;
|
|
||||||
db: number;
|
|
||||||
names: Array<string>;
|
|
||||||
}
|
|
||||||
export interface RedisStatus {
|
export interface RedisStatus {
|
||||||
tcp_port: string;
|
tcp_port: string;
|
||||||
uptime_in_days: string;
|
uptime_in_days: string;
|
||||||
@ -156,12 +127,14 @@ export namespace Database {
|
|||||||
latest_fork_usec: string;
|
latest_fork_usec: string;
|
||||||
}
|
}
|
||||||
export interface RedisConf {
|
export interface RedisConf {
|
||||||
|
name: string;
|
||||||
timeout: number;
|
timeout: number;
|
||||||
maxclients: number;
|
maxclients: number;
|
||||||
databases: number;
|
databases: number;
|
||||||
requirepass: string;
|
requirepass: string;
|
||||||
maxmemory: number;
|
maxmemory: number;
|
||||||
|
}
|
||||||
|
export interface RedisPersistenceConf {
|
||||||
dir: string;
|
dir: string;
|
||||||
appendonly: string;
|
appendonly: string;
|
||||||
appendfsync: string;
|
appendfsync: string;
|
||||||
|
@ -6,9 +6,6 @@ import { Database } from '../interface/database';
|
|||||||
export const searchMysqlDBs = (params: Database.Search) => {
|
export const searchMysqlDBs = (params: Database.Search) => {
|
||||||
return http.post<ResPage<Database.MysqlDBInfo>>(`databases/search`, params);
|
return http.post<ResPage<Database.MysqlDBInfo>>(`databases/search`, params);
|
||||||
};
|
};
|
||||||
export const searchRedisDBs = (params: Database.SearchRedisWithPage) => {
|
|
||||||
return http.post<ResPage<Database.RedisData>>(`databases/redis/search`, params);
|
|
||||||
};
|
|
||||||
export const listDBByVersion = (params: string) => {
|
export const listDBByVersion = (params: string) => {
|
||||||
return http.get(`databases/dbs/${params}`);
|
return http.get(`databases/dbs/${params}`);
|
||||||
};
|
};
|
||||||
@ -50,24 +47,15 @@ export const loadVersions = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// redis
|
// redis
|
||||||
export const setRedis = (params: Database.RedisDataSet) => {
|
export const loadRedisStatus = () => {
|
||||||
return http.post(`/databases/redis`, params);
|
return http.get<Database.RedisStatus>(`/databases/redis/status`);
|
||||||
};
|
};
|
||||||
export const deleteRedisKey = (params: Database.RedisDelBatch) => {
|
export const loadRedisConf = () => {
|
||||||
return http.post(`/databases/redis/del`, params);
|
return http.get<Database.RedisConf>(`/databases/redis/conf`);
|
||||||
};
|
};
|
||||||
export const cleanRedisKey = (params: Database.RedisBaseReq) => {
|
export const RedisPersistenceConf = () => {
|
||||||
return http.post(`/databases/redis/clean`, params);
|
return http.get<Database.RedisPersistenceConf>(`/databases/redis/persistence/conf`);
|
||||||
};
|
|
||||||
export const loadRedisStatus = (params: Database.RedisBaseReq) => {
|
|
||||||
return http.post<Database.RedisStatus>(`/databases/redis/status`, params);
|
|
||||||
};
|
|
||||||
export const loadRedisConf = (params: Database.RedisBaseReq) => {
|
|
||||||
return http.post<Database.RedisConf>(`/databases/redis/conf`, params);
|
|
||||||
};
|
};
|
||||||
export const updateRedisConf = (params: Database.RedisConfUpdate) => {
|
export const updateRedisConf = (params: Database.RedisConfUpdate) => {
|
||||||
return http.post(`/databases/redis/conf/update`, params);
|
return http.get(`/databases/redis/conf/update`, params);
|
||||||
};
|
|
||||||
export const loadRedisVersions = () => {
|
|
||||||
return http.get(`/databases/redis/versions`);
|
|
||||||
};
|
};
|
||||||
|
@ -219,6 +219,8 @@ export default {
|
|||||||
maxConnectionsHelper: '最大连接数',
|
maxConnectionsHelper: '最大连接数',
|
||||||
restart: '重启数据库',
|
restart: '重启数据库',
|
||||||
|
|
||||||
|
status: '当前状态',
|
||||||
|
terminal: '终端模式',
|
||||||
key: '键',
|
key: '键',
|
||||||
value: '值',
|
value: '值',
|
||||||
type: '数据类型',
|
type: '数据类型',
|
||||||
|
@ -1,290 +1,59 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<Submenu activeName="redis" />
|
<Submenu activeName="redis" />
|
||||||
<el-dropdown size="default" split-button style="margin-top: 20px; margin-bottom: 5px">
|
<el-button style="margin-top: 20px" size="default" icon="Tickets" @click="changeView('status')">
|
||||||
{{ redisName }}
|
{{ $t('database.status') }}
|
||||||
<template #dropdown>
|
</el-button>
|
||||||
<el-dropdown-menu v-model="redisName">
|
<el-button style="margin-top: 20px" size="default" icon="Setting" @click="changeView('setting')">
|
||||||
<el-dropdown-item v-for="item in redisNames" :key="item" @click="onChangeName(item)">
|
|
||||||
{{ item }}
|
|
||||||
</el-dropdown-item>
|
|
||||||
</el-dropdown-menu>
|
|
||||||
</template>
|
|
||||||
</el-dropdown>
|
|
||||||
<el-button
|
|
||||||
v-if="!isOnSetting"
|
|
||||||
style="margin-top: 20px; margin-left: 10px"
|
|
||||||
size="default"
|
|
||||||
icon="Setting"
|
|
||||||
@click="onSetting"
|
|
||||||
>
|
|
||||||
{{ $t('database.setting') }}
|
{{ $t('database.setting') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button
|
<el-button style="margin-top: 20px" size="default" icon="Files" @click="changeView('persistence')">
|
||||||
v-if="isOnSetting"
|
{{ $t('database.persistence') }}
|
||||||
style="margin-top: 20px; margin-left: 10px"
|
</el-button>
|
||||||
size="default"
|
<el-button style="margin-top: 20px" size="default" icon="Setting" @click="changeView('terminal')">
|
||||||
icon="Back"
|
{{ $t('database.terminal') }}
|
||||||
@click="onBacklist"
|
|
||||||
>
|
|
||||||
{{ $t('commons.button.back') }}列表
|
|
||||||
</el-button>
|
</el-button>
|
||||||
|
|
||||||
<Setting ref="settingRef"></Setting>
|
<el-card style="height: calc(100vh - 178px); margin-top: 5px">
|
||||||
<el-card v-if="!isOnSetting">
|
<Status ref="statusRef"></Status>
|
||||||
<ComplexTable :pagination-config="paginationConfig" v-model:selects="selects" @search="search" :data="data">
|
<Setting ref="settingRef"></Setting>
|
||||||
<template #toolbar>
|
<Terminal ref="terminalRef"></Terminal>
|
||||||
<el-button type="primary" @click="onOperate">{{ $t('commons.button.create') }}</el-button>
|
|
||||||
<el-button type="primary" @click="onCleanAll">{{ $t('database.cleanAll') }}</el-button>
|
|
||||||
<el-button type="danger" plain :disabled="selects.length === 0" @click="onBatchDelete(null)">
|
|
||||||
{{ $t('commons.button.delete') }}
|
|
||||||
</el-button>
|
|
||||||
<el-select v-model="currentDB" @change="search" style="margin-left: 20px">
|
|
||||||
<el-option
|
|
||||||
v-for="item in dbOptions"
|
|
||||||
:key="item.label"
|
|
||||||
:value="item.value"
|
|
||||||
:label="item.label"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</template>
|
|
||||||
<el-table-column type="selection" fix />
|
|
||||||
<el-table-column :label="$t('database.key')" prop="key" />
|
|
||||||
<el-table-column :label="$t('database.value')" prop="value" />
|
|
||||||
<el-table-column :label="$t('database.type')" prop="type" />
|
|
||||||
<el-table-column :label="$t('database.length')" prop="length" />
|
|
||||||
<el-table-column :label="$t('database.expiration')" prop="expiration">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<span v-if="row.expiration === -1">{{ $t('database.forever') }}</span>
|
|
||||||
<span v-else>{{ row.expiration }} {{ $t('database.second') }}</span>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<fu-table-operations :buttons="buttons" :label="$t('commons.table.operate')" fix />
|
|
||||||
</ComplexTable>
|
|
||||||
</el-card>
|
</el-card>
|
||||||
|
|
||||||
<el-dialog v-model="redisVisiable" :destroy-on-close="true" width="30%">
|
|
||||||
<template #header>
|
|
||||||
<div class="card-header">
|
|
||||||
<span>{{ $t('database.changePassword') }}</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<el-form>
|
|
||||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="80px">
|
|
||||||
<el-form-item prop="db">
|
|
||||||
<el-select v-model="form.db">
|
|
||||||
<el-option
|
|
||||||
v-for="item in dbOptions"
|
|
||||||
:key="item.label"
|
|
||||||
:value="item.value"
|
|
||||||
:label="item.label"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item :label="$t('database.key')" prop="key">
|
|
||||||
<el-input clearable v-model="form.key"></el-input>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item :label="$t('database.value')" prop="value">
|
|
||||||
<el-input clearable v-model="form.value"></el-input>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item :label="$t('database.expiration')" prop="expiration">
|
|
||||||
<el-input type="number" clearable v-model.number="form.expiration">
|
|
||||||
<template #append>{{ $t('database.second') }}</template>
|
|
||||||
</el-input>
|
|
||||||
<span class="input-help">{{ $t('database.expirationHelper') }}</span>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</el-form>
|
|
||||||
<template #footer>
|
|
||||||
<span class="dialog-footer">
|
|
||||||
<el-button @click="redisVisiable = false">{{ $t('commons.button.cancel') }}</el-button>
|
|
||||||
<el-button @click="submit(formRef)">
|
|
||||||
{{ $t('commons.button.confirm') }}
|
|
||||||
</el-button>
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
</el-dialog>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import ComplexTable from '@/components/complex-table/index.vue';
|
|
||||||
import Submenu from '@/views/database/index.vue';
|
import Submenu from '@/views/database/index.vue';
|
||||||
|
import Status from '@/views/database/redis/status/index.vue';
|
||||||
import Setting from '@/views/database/redis/setting/index.vue';
|
import Setting from '@/views/database/redis/setting/index.vue';
|
||||||
import { onMounted, reactive, ref } from 'vue';
|
import Terminal from '@/views/database/redis/terminal/index.vue';
|
||||||
import { cleanRedisKey, deleteRedisKey, loadRedisVersions, searchRedisDBs, setRedis } from '@/api/modules/database';
|
import { onMounted, ref } from 'vue';
|
||||||
import i18n from '@/lang';
|
|
||||||
import { useDeleteData } from '@/hooks/use-delete-data';
|
|
||||||
import { Database } from '@/api/interface/database';
|
|
||||||
import { ElForm, ElMessage, ElMessageBox } from 'element-plus';
|
|
||||||
import { Rules } from '@/global/form-rules';
|
|
||||||
|
|
||||||
const selects = ref<any>([]);
|
const statusRef = ref();
|
||||||
const currentDB = ref(0);
|
|
||||||
const dbOptions = ref([
|
|
||||||
{ label: 'DB0', value: 0 },
|
|
||||||
{ label: 'DB1', value: 1 },
|
|
||||||
{ label: 'DB2', value: 2 },
|
|
||||||
{ label: 'DB3', value: 3 },
|
|
||||||
{ label: 'DB4', value: 4 },
|
|
||||||
{ label: 'DB5', value: 5 },
|
|
||||||
{ label: 'DB6', value: 6 },
|
|
||||||
{ label: 'DB7', value: 7 },
|
|
||||||
{ label: 'DB8', value: 8 },
|
|
||||||
{ label: 'DB9', value: 9 },
|
|
||||||
]);
|
|
||||||
|
|
||||||
const data = ref();
|
|
||||||
const paginationConfig = reactive({
|
|
||||||
currentPage: 1,
|
|
||||||
pageSize: 10,
|
|
||||||
total: 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
type FormInstance = InstanceType<typeof ElForm>;
|
|
||||||
const formRef = ref<FormInstance>();
|
|
||||||
const form = reactive({
|
|
||||||
redisName: '',
|
|
||||||
key: '',
|
|
||||||
value: '',
|
|
||||||
db: 0,
|
|
||||||
expiration: 0,
|
|
||||||
});
|
|
||||||
const rules = reactive({
|
|
||||||
db: [Rules.requiredSelect],
|
|
||||||
key: [Rules.requiredInput],
|
|
||||||
value: [Rules.requiredInput],
|
|
||||||
expiration: [Rules.requiredInput, Rules.number],
|
|
||||||
});
|
|
||||||
const redisVisiable = ref(false);
|
|
||||||
|
|
||||||
const redisNames = ref();
|
|
||||||
const redisName = ref();
|
|
||||||
const isOnSetting = ref(false);
|
|
||||||
const settingRef = ref();
|
const settingRef = ref();
|
||||||
const onSetting = async () => {
|
const terminalRef = ref();
|
||||||
isOnSetting.value = true;
|
|
||||||
let params = {
|
|
||||||
redisName: redisName.value,
|
|
||||||
db: currentDB.value,
|
|
||||||
};
|
|
||||||
settingRef.value!.acceptParams(params);
|
|
||||||
};
|
|
||||||
const onBacklist = async () => {
|
|
||||||
isOnSetting.value = false;
|
|
||||||
search();
|
|
||||||
settingRef.value!.onClose();
|
|
||||||
};
|
|
||||||
|
|
||||||
const loadRunningNames = async () => {
|
const changeView = async (params: string) => {
|
||||||
const res = await loadRedisVersions();
|
switch (params) {
|
||||||
redisNames.value = res.data;
|
case 'status':
|
||||||
if (redisNames.value.length != 0) {
|
settingRef.value!.onClose();
|
||||||
redisName.value = redisNames.value[0];
|
terminalRef.value!.onClose();
|
||||||
search();
|
statusRef.value!.acceptParams(params);
|
||||||
}
|
break;
|
||||||
};
|
case 'setting':
|
||||||
const onChangeName = async (val: string) => {
|
statusRef.value!.onClose();
|
||||||
redisName.value = val;
|
terminalRef.value!.onClose();
|
||||||
search();
|
settingRef.value!.acceptParams(params);
|
||||||
if (isOnSetting.value) {
|
break;
|
||||||
let params = {
|
case 'terminal':
|
||||||
redisName: redisName.value,
|
statusRef.value!.onClose();
|
||||||
};
|
settingRef.value!.onClose();
|
||||||
settingRef.value!.acceptParams(params);
|
terminalRef.value!.acceptParams(params);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const search = async () => {
|
|
||||||
let params = {
|
|
||||||
page: paginationConfig.currentPage,
|
|
||||||
pageSize: paginationConfig.pageSize,
|
|
||||||
redisName: redisName.value,
|
|
||||||
db: currentDB.value,
|
|
||||||
};
|
|
||||||
const res = await searchRedisDBs(params);
|
|
||||||
data.value = res.data.items || [];
|
|
||||||
paginationConfig.total = res.data.total;
|
|
||||||
};
|
|
||||||
|
|
||||||
const onBatchDelete = async (row: Database.RedisData | null) => {
|
|
||||||
let names: Array<string> = [];
|
|
||||||
if (row) {
|
|
||||||
names.push(row.key);
|
|
||||||
} else {
|
|
||||||
selects.value.forEach((item: Database.RedisData) => {
|
|
||||||
names.push(item.key);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
let params = {
|
|
||||||
redisName: redisName.value,
|
|
||||||
db: form.db,
|
|
||||||
names: names,
|
|
||||||
};
|
|
||||||
await useDeleteData(deleteRedisKey, params, 'commons.msg.delete', true);
|
|
||||||
search();
|
|
||||||
};
|
|
||||||
|
|
||||||
const onCleanAll = async () => {
|
|
||||||
ElMessageBox.confirm(i18n.global.t('commons.msg.delete') + '?', i18n.global.t('database.cleanAll'), {
|
|
||||||
confirmButtonText: i18n.global.t('commons.button.confirm'),
|
|
||||||
cancelButtonText: i18n.global.t('commons.button.cancel'),
|
|
||||||
type: 'warning',
|
|
||||||
draggable: true,
|
|
||||||
}).then(async () => {
|
|
||||||
let params = {
|
|
||||||
redisName: redisName.value,
|
|
||||||
db: currentDB.value,
|
|
||||||
};
|
|
||||||
await cleanRedisKey(params);
|
|
||||||
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
|
||||||
search();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const onOperate = async (row: Database.RedisData | undefined) => {
|
|
||||||
if (row) {
|
|
||||||
form.db = currentDB.value;
|
|
||||||
form.key = row.key;
|
|
||||||
form.value = row.value;
|
|
||||||
form.expiration = row.expiration === -1 ? 0 : row.expiration;
|
|
||||||
} else {
|
|
||||||
form.db = currentDB.value;
|
|
||||||
form.key = '';
|
|
||||||
form.value = '';
|
|
||||||
form.expiration = 0;
|
|
||||||
}
|
|
||||||
redisVisiable.value = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const submit = async (formEl: FormInstance | undefined) => {
|
|
||||||
if (!formEl) return;
|
|
||||||
formEl.validate(async (valid) => {
|
|
||||||
if (!valid) return;
|
|
||||||
form.redisName = redisName.value;
|
|
||||||
await setRedis(form);
|
|
||||||
redisVisiable.value = false;
|
|
||||||
currentDB.value = form.db;
|
|
||||||
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
|
||||||
search();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const buttons = [
|
|
||||||
{
|
|
||||||
label: i18n.global.t('commons.button.edit'),
|
|
||||||
click: (row: Database.RedisData) => {
|
|
||||||
onOperate(row);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: i18n.global.t('commons.button.delete'),
|
|
||||||
click: (row: Database.RedisData) => {
|
|
||||||
onBatchDelete(row);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
loadRunningNames();
|
changeView('status');
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
81
frontend/src/views/database/redis/persistence/index.vue
Normal file
81
frontend/src/views/database/redis/persistence/index.vue
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
<template>
|
||||||
|
<div v-if="persistenceShow">
|
||||||
|
<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-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-col>
|
||||||
|
</el-row>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { RedisPersistenceConf } from '@/api/modules/database';
|
||||||
|
import { Rules } from '@/global/form-rules';
|
||||||
|
import { FormInstance } from 'element-plus';
|
||||||
|
import { reactive, ref } from 'vue';
|
||||||
|
|
||||||
|
const form = reactive({
|
||||||
|
appendonly: '',
|
||||||
|
appendfsync: 'no',
|
||||||
|
});
|
||||||
|
const rules = reactive({
|
||||||
|
appendonly: [Rules.requiredSelect],
|
||||||
|
appendfsync: [Rules.requiredSelect],
|
||||||
|
});
|
||||||
|
const formRef = ref<FormInstance>();
|
||||||
|
|
||||||
|
const persistenceShow = ref(false);
|
||||||
|
const acceptParams = (): void => {
|
||||||
|
persistenceShow.value = true;
|
||||||
|
loadform();
|
||||||
|
};
|
||||||
|
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 loadform = async () => {
|
||||||
|
const res = await RedisPersistenceConf();
|
||||||
|
form.appendonly = res.data?.appendonly;
|
||||||
|
form.appendfsync = res.data?.appendfsync;
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
acceptParams,
|
||||||
|
onClose,
|
||||||
|
});
|
||||||
|
</script>
|
@ -1,238 +1,86 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="demo-collapse" v-if="onSetting">
|
<div v-if="settingShow">
|
||||||
<el-card>
|
<el-radio-group v-model="confShowType">
|
||||||
<el-collapse v-model="activeName" accordion>
|
<el-radio-button label="base">基础配置</el-radio-button>
|
||||||
<el-collapse-item :title="$t('database.baseSetting')" name="1">
|
<el-radio-button label="all">全部配置</el-radio-button>
|
||||||
<el-form :model="baseInfo" ref="panelFormRef" :rules="rules" label-width="120px">
|
</el-radio-group>
|
||||||
<el-row>
|
<el-form v-if="confShowType === 'base'" :model="baseInfo" ref="panelFormRef" :rules="rules" label-width="120px">
|
||||||
<el-col :span="1"><br /></el-col>
|
<el-row style="margin-top: 20px">
|
||||||
<el-col :span="10">
|
<el-col :span="1"><br /></el-col>
|
||||||
<el-form-item :label="$t('setting.port')" prop="port">
|
<el-col :span="10">
|
||||||
<el-input clearable type="number" v-model.number="baseInfo.port">
|
<el-form-item :label="$t('setting.port')" prop="port">
|
||||||
<template #append>
|
<el-input clearable type="number" v-model.number="baseInfo.port" />
|
||||||
<el-button
|
</el-form-item>
|
||||||
@click="onSave(panelFormRef, 'port', baseInfo.port)"
|
<el-form-item :label="$t('setting.password')" prop="requirepass">
|
||||||
icon="Collection"
|
<el-input type="password" show-password clearable v-model="baseInfo.requirepass" />
|
||||||
>
|
<span class="input-help">{{ $t('database.requirepassHelper') }}</span>
|
||||||
{{ $t('commons.button.save') }}
|
</el-form-item>
|
||||||
</el-button>
|
<el-form-item :label="$t('database.timeout')" prop="timeout">
|
||||||
</template>
|
<el-input clearable type="number" v-model.number="baseInfo.timeout" />
|
||||||
</el-input>
|
<span class="input-help">{{ $t('database.timeoutHelper') }}</span>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item :label="$t('setting.password')" prop="requirepass">
|
<el-form-item :label="$t('database.maxclients')" prop="maxclients">
|
||||||
<el-input type="password" show-password clearable v-model="baseInfo.requirepass">
|
<el-input clearable type="number" v-model.number="baseInfo.maxclients" />
|
||||||
<template #append>
|
</el-form-item>
|
||||||
<el-button
|
<el-form-item :label="$t('database.databases')" prop="databases">
|
||||||
@click="onSave(panelFormRef, 'password', baseInfo.requirepass)"
|
<el-input clearable type="number" v-model.number="baseInfo.databases" />
|
||||||
icon="Collection"
|
</el-form-item>
|
||||||
>
|
<el-form-item :label="$t('database.maxmemory')" prop="maxmemory">
|
||||||
{{ $t('commons.button.save') }}
|
<el-input clearable type="number" v-model.number="baseInfo.maxmemory" />
|
||||||
</el-button>
|
<span class="input-help">{{ $t('database.maxmemoryHelper') }}</span>
|
||||||
</template>
|
</el-form-item>
|
||||||
</el-input>
|
<el-form-item>
|
||||||
<span class="input-help">{{ $t('database.requirepassHelper') }}</span>
|
<el-button type="primary" size="default" style="width: 90px">
|
||||||
</el-form-item>
|
{{ $t('commons.button.save') }}
|
||||||
<el-form-item :label="$t('database.timeout')" prop="timeout">
|
</el-button>
|
||||||
<el-input clearable type="number" v-model.number="baseInfo.timeout">
|
</el-form-item>
|
||||||
<template #append>
|
</el-col>
|
||||||
<el-button
|
</el-row>
|
||||||
@click="onSave(panelFormRef, 'timeout', baseInfo.timeout)"
|
</el-form>
|
||||||
icon="Collection"
|
<div v-if="confShowType === 'all'">
|
||||||
>
|
<codemirror
|
||||||
{{ $t('commons.button.save') }}
|
:autofocus="true"
|
||||||
</el-button>
|
placeholder="None data"
|
||||||
</template>
|
:indent-with-tab="true"
|
||||||
</el-input>
|
:tabSize="4"
|
||||||
<span class="input-help">{{ $t('database.timeoutHelper') }}</span>
|
style="margin-top: 10px; height: calc(100vh - 280px)"
|
||||||
</el-form-item>
|
:lineWrapping="true"
|
||||||
<el-form-item :label="$t('database.maxclients')" prop="maxclients">
|
:matchBrackets="true"
|
||||||
<el-input clearable type="number" v-model.number="baseInfo.maxclients">
|
theme="cobalt"
|
||||||
<template #append>
|
:styleActiveLine="true"
|
||||||
<el-button
|
:extensions="extensions"
|
||||||
@click="onSave(panelFormRef, 'maxclients', baseInfo.maxclients)"
|
v-model="mysqlConf"
|
||||||
icon="Collection"
|
:readOnly="true"
|
||||||
>
|
/>
|
||||||
{{ $t('commons.button.save') }}
|
<el-button type="primary" size="default" style="width: 90px; margin-top: 5px">
|
||||||
</el-button>
|
{{ $t('commons.button.save') }}
|
||||||
</template>
|
</el-button>
|
||||||
</el-input>
|
</div>
|
||||||
</el-form-item>
|
|
||||||
<el-form-item :label="$t('database.databases')" prop="databases">
|
|
||||||
<el-input clearable type="number" v-model.number="baseInfo.databases">
|
|
||||||
<template #append>
|
|
||||||
<el-button
|
|
||||||
@click="onSave(panelFormRef, 'databases', baseInfo.databases)"
|
|
||||||
icon="Collection"
|
|
||||||
>
|
|
||||||
{{ $t('commons.button.save') }}
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-input>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item :label="$t('database.maxmemory')" prop="maxmemory">
|
|
||||||
<el-input clearable type="number" v-model.number="baseInfo.maxmemory">
|
|
||||||
<template #append>
|
|
||||||
<el-button
|
|
||||||
@click="onSave(panelFormRef, 'maxmemory', baseInfo.maxmemory)"
|
|
||||||
icon="Collection"
|
|
||||||
>
|
|
||||||
{{ $t('commons.button.save') }}
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
</el-input>
|
|
||||||
<span class="input-help">{{ $t('database.maxmemoryHelper') }}</span>
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
</el-form>
|
|
||||||
</el-collapse-item>
|
|
||||||
<el-collapse-item :title="$t('database.confChange')" name="2">
|
|
||||||
<codemirror
|
|
||||||
:autofocus="true"
|
|
||||||
placeholder="None data"
|
|
||||||
:indent-with-tab="true"
|
|
||||||
:tabSize="4"
|
|
||||||
style="margin-top: 10px; max-height: 500px"
|
|
||||||
:lineWrapping="true"
|
|
||||||
:matchBrackets="true"
|
|
||||||
theme="cobalt"
|
|
||||||
:styleActiveLine="true"
|
|
||||||
:extensions="extensions"
|
|
||||||
v-model="mysqlConf"
|
|
||||||
:readOnly="true"
|
|
||||||
/>
|
|
||||||
<el-button
|
|
||||||
type="primary"
|
|
||||||
style="width: 120px; margin-top: 10px"
|
|
||||||
@click="onSave(panelFormRef, 'remoteAccess', baseInfo.port)"
|
|
||||||
>
|
|
||||||
{{ $t('commons.button.save') }}
|
|
||||||
</el-button>
|
|
||||||
</el-collapse-item>
|
|
||||||
<el-collapse-item :title="$t('database.currentStatus')" name="3">
|
|
||||||
<el-row>
|
|
||||||
<el-col :span="1"><br /></el-col>
|
|
||||||
<el-col :span="12">
|
|
||||||
<table style="margin-top: 20px; width: 100%" class="myTable">
|
|
||||||
<tr>
|
|
||||||
<td>uptime_in_days</td>
|
|
||||||
<td>{{ redisStatus!.uptime_in_days }}</td>
|
|
||||||
<td>{{ $t('database.uptimeInDays') }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>tcp_port</td>
|
|
||||||
<td>{{ redisStatus!.tcp_port }}</td>
|
|
||||||
<td>{{ $t('database.tcpPort') }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>connected_clients</td>
|
|
||||||
<td>{{ redisStatus!.connected_clients }}</td>
|
|
||||||
<td>{{ $t('database.connectedClients') }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>used_memory_rss</td>
|
|
||||||
<td>{{ redisStatus!.used_memory_rss }}</td>
|
|
||||||
<td>{{ $t('database.usedMemoryRss') }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>used_memory</td>
|
|
||||||
<td>{{ redisStatus!.used_memory }}</td>
|
|
||||||
<td>{{ $t('database.usedMemory') }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>mem_fragmentation_ratio</td>
|
|
||||||
<td>{{ redisStatus!.mem_fragmentation_ratio }}</td>
|
|
||||||
<td>{{ $t('database.tmpTableToDBHelper') }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>total_connections_received</td>
|
|
||||||
<td>{{ redisStatus!.total_connections_received }}</td>
|
|
||||||
<td>{{ $t('database.totalConnectionsReceived') }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>total_commands_processed</td>
|
|
||||||
<td>{{ redisStatus!.total_commands_processed }}</td>
|
|
||||||
<td>{{ $t('database.totalCommandsProcessed') }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>instantaneous_ops_per_sec</td>
|
|
||||||
<td>{{ redisStatus!.instantaneous_ops_per_sec }}</td>
|
|
||||||
<td>{{ $t('database.instantaneousOpsPerSec') }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>keyspace_hits</td>
|
|
||||||
<td>{{ redisStatus!.keyspace_hits }}</td>
|
|
||||||
<td>{{ $t('database.keyspaceHits') }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>keyspace_misses</td>
|
|
||||||
<td>{{ redisStatus!.keyspace_misses }}</td>
|
|
||||||
<td>{{ $t('database.keyspaceMisses') }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>hit</td>
|
|
||||||
<td>{{ redisStatus!.hit }}</td>
|
|
||||||
<td>{{ $t('database.hit') }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>latest_fork_usec</td>
|
|
||||||
<td>{{ redisStatus!.latest_fork_usec }}</td>
|
|
||||||
<td>{{ $t('database.latestForkUsec') }}</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
</el-collapse-item>
|
|
||||||
<el-collapse-item :title="$t('database.persistence')" name="4">
|
|
||||||
<el-form :model="baseInfo" ref="panelFormRef" label-width="120px">
|
|
||||||
<el-row>
|
|
||||||
<el-col :span="1"><br /></el-col>
|
|
||||||
<el-col :span="10">
|
|
||||||
<el-form-item label="appendonly" prop="appendonly">
|
|
||||||
<el-switch v-model="baseInfo.appendonly"></el-switch>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="appendfsync" prop="appendfsync">
|
|
||||||
<el-radio-group v-model="baseInfo.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-col>
|
|
||||||
</el-row>
|
|
||||||
</el-form>
|
|
||||||
</el-collapse-item>
|
|
||||||
</el-collapse>
|
|
||||||
</el-card>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ElMessage, FormInstance } from 'element-plus';
|
import { FormInstance } from 'element-plus';
|
||||||
import { reactive, ref } from 'vue';
|
import { reactive, ref } from 'vue';
|
||||||
import { Codemirror } from 'vue-codemirror';
|
import { Codemirror } from 'vue-codemirror';
|
||||||
import { javascript } from '@codemirror/lang-javascript';
|
import { javascript } from '@codemirror/lang-javascript';
|
||||||
import { oneDark } from '@codemirror/theme-one-dark';
|
import { oneDark } from '@codemirror/theme-one-dark';
|
||||||
import { LoadFile } from '@/api/modules/files';
|
import { LoadFile } from '@/api/modules/files';
|
||||||
import { loadRedisConf, loadRedisStatus, updateRedisConf } from '@/api/modules/database';
|
import { loadRedisConf } from '@/api/modules/database';
|
||||||
import i18n from '@/lang';
|
// import i18n from '@/lang';
|
||||||
import { Rules } from '@/global/form-rules';
|
import { Rules } from '@/global/form-rules';
|
||||||
|
|
||||||
const extensions = [javascript(), oneDark];
|
const extensions = [javascript(), oneDark];
|
||||||
const activeName = ref('1');
|
const confShowType = ref('base');
|
||||||
|
|
||||||
const baseInfo = reactive({
|
const baseInfo = reactive({
|
||||||
|
name: '',
|
||||||
port: 3306,
|
port: 3306,
|
||||||
requirepass: '',
|
requirepass: '',
|
||||||
timeout: 0,
|
timeout: 0,
|
||||||
maxclients: 0,
|
maxclients: 0,
|
||||||
databases: 0,
|
databases: 0,
|
||||||
maxmemory: 0,
|
maxmemory: 0,
|
||||||
|
|
||||||
dir: '',
|
|
||||||
appendonly: '',
|
|
||||||
appendfsync: '',
|
|
||||||
save: '',
|
|
||||||
});
|
});
|
||||||
const rules = reactive({
|
const rules = reactive({
|
||||||
port: [Rules.port],
|
port: [Rules.port],
|
||||||
@ -240,86 +88,51 @@ const rules = reactive({
|
|||||||
maxclients: [Rules.number],
|
maxclients: [Rules.number],
|
||||||
databases: [Rules.number],
|
databases: [Rules.number],
|
||||||
maxmemory: [Rules.number],
|
maxmemory: [Rules.number],
|
||||||
|
|
||||||
appendonly: [Rules.requiredSelect],
|
|
||||||
appendfsync: [Rules.requiredSelect],
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const panelFormRef = ref<FormInstance>();
|
const panelFormRef = ref<FormInstance>();
|
||||||
const mysqlConf = ref();
|
const mysqlConf = ref();
|
||||||
|
|
||||||
let redisStatus = reactive({
|
const settingShow = ref<boolean>(false);
|
||||||
tcp_port: '',
|
|
||||||
uptime_in_days: '',
|
|
||||||
connected_clients: '',
|
|
||||||
used_memory: '',
|
|
||||||
used_memory_rss: '',
|
|
||||||
used_memory_peak: '',
|
|
||||||
mem_fragmentation_ratio: '',
|
|
||||||
total_connections_received: '',
|
|
||||||
total_commands_processed: '',
|
|
||||||
instantaneous_ops_per_sec: '',
|
|
||||||
keyspace_hits: '',
|
|
||||||
keyspace_misses: '',
|
|
||||||
hit: '',
|
|
||||||
latest_fork_usec: '',
|
|
||||||
});
|
|
||||||
|
|
||||||
const onSetting = ref<boolean>(false);
|
const acceptParams = (): void => {
|
||||||
const redisName = ref();
|
settingShow.value = true;
|
||||||
const db = ref();
|
|
||||||
|
|
||||||
interface DialogProps {
|
|
||||||
redisName: string;
|
|
||||||
db: number;
|
|
||||||
}
|
|
||||||
const acceptParams = (params: DialogProps): void => {
|
|
||||||
onSetting.value = true;
|
|
||||||
redisName.value = params.redisName;
|
|
||||||
db.value = params.db;
|
|
||||||
loadBaseInfo();
|
loadBaseInfo();
|
||||||
loadStatus();
|
|
||||||
};
|
};
|
||||||
const onClose = (): void => {
|
const onClose = (): void => {
|
||||||
onSetting.value = false;
|
settingShow.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
const onSave = async (formEl: FormInstance | undefined, key: string, val: any) => {
|
// const onSave = async (formEl: FormInstance | undefined, key: string) => {
|
||||||
if (!formEl) return;
|
// if (!formEl) return;
|
||||||
const result = await formEl.validateField(key, callback);
|
// const result = await formEl.validateField(key, callback);
|
||||||
if (!result) {
|
// if (!result) {
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
let changeForm = {
|
// // let changeForm = {
|
||||||
redisName: redisName.value,
|
// // paramName: key,
|
||||||
db: 0,
|
// // value: val + '',
|
||||||
paramName: key,
|
// // };
|
||||||
value: val + '',
|
// // await updateRedisConf(changeForm);
|
||||||
};
|
// ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
||||||
await updateRedisConf(changeForm);
|
// };
|
||||||
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
|
// function callback(error: any) {
|
||||||
};
|
// if (error) {
|
||||||
function callback(error: any) {
|
// return error.message;
|
||||||
if (error) {
|
// } else {
|
||||||
return error.message;
|
// return;
|
||||||
} else {
|
// }
|
||||||
return;
|
// }
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const loadBaseInfo = async () => {
|
const loadBaseInfo = async () => {
|
||||||
let params = {
|
const res = await loadRedisConf();
|
||||||
redisName: redisName.value,
|
baseInfo.name = res.data?.name;
|
||||||
db: db.value,
|
|
||||||
};
|
|
||||||
const res = await loadRedisConf(params);
|
|
||||||
baseInfo.timeout = Number(res.data?.timeout);
|
baseInfo.timeout = Number(res.data?.timeout);
|
||||||
baseInfo.maxclients = Number(res.data?.maxclients);
|
baseInfo.maxclients = Number(res.data?.maxclients);
|
||||||
baseInfo.databases = Number(res.data?.databases);
|
baseInfo.databases = Number(res.data?.databases);
|
||||||
baseInfo.requirepass = res.data?.requirepass;
|
baseInfo.requirepass = res.data?.requirepass;
|
||||||
baseInfo.maxmemory = Number(res.data?.maxmemory);
|
baseInfo.maxmemory = Number(res.data?.maxmemory);
|
||||||
baseInfo.appendonly = res.data?.appendonly;
|
loadMysqlConf(`/opt/1Panel/data/apps/redis/${baseInfo.name}/conf/redis.conf`);
|
||||||
baseInfo.appendfsync = res.data?.appendfsync;
|
|
||||||
loadMysqlConf(`/opt/1Panel/data/apps/redis/${redisName.value}/conf/redis.conf`);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const loadMysqlConf = async (path: string) => {
|
const loadMysqlConf = async (path: string) => {
|
||||||
@ -327,32 +140,6 @@ const loadMysqlConf = async (path: string) => {
|
|||||||
mysqlConf.value = res.data;
|
mysqlConf.value = res.data;
|
||||||
};
|
};
|
||||||
|
|
||||||
const loadStatus = async () => {
|
|
||||||
let params = {
|
|
||||||
redisName: redisName.value,
|
|
||||||
db: db.value,
|
|
||||||
};
|
|
||||||
const res = await loadRedisStatus(params);
|
|
||||||
let hit = (
|
|
||||||
(Number(res.data.keyspace_hits) / (Number(res.data.keyspace_hits) + Number(res.data.keyspace_misses))) *
|
|
||||||
100
|
|
||||||
).toFixed(2);
|
|
||||||
|
|
||||||
redisStatus.uptime_in_days = res.data.uptime_in_days;
|
|
||||||
redisStatus.tcp_port = res.data.tcp_port;
|
|
||||||
redisStatus.connected_clients = res.data.connected_clients;
|
|
||||||
redisStatus.used_memory_rss = (Number(res.data.used_memory_rss) / 1024 / 1024).toFixed(2) + ' MB';
|
|
||||||
redisStatus.used_memory = (Number(res.data.used_memory) / 1024 / 1024).toFixed(2) + ' MB';
|
|
||||||
redisStatus.mem_fragmentation_ratio = res.data.mem_fragmentation_ratio;
|
|
||||||
redisStatus.total_connections_received = res.data.total_connections_received;
|
|
||||||
redisStatus.total_commands_processed = res.data.total_commands_processed;
|
|
||||||
redisStatus.instantaneous_ops_per_sec = res.data.instantaneous_ops_per_sec;
|
|
||||||
redisStatus.keyspace_hits = res.data.keyspace_hits;
|
|
||||||
redisStatus.keyspace_misses = res.data.keyspace_misses;
|
|
||||||
redisStatus.hit = hit;
|
|
||||||
redisStatus.latest_fork_usec = res.data.latest_fork_usec;
|
|
||||||
};
|
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
acceptParams,
|
acceptParams,
|
||||||
onClose,
|
onClose,
|
||||||
|
135
frontend/src/views/database/redis/status/index.vue
Normal file
135
frontend/src/views/database/redis/status/index.vue
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
<template>
|
||||||
|
<div v-if="statusShow">
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="1"><br /></el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<table style="margin-top: 20px; width: 100%" class="myTable">
|
||||||
|
<tr>
|
||||||
|
<td>uptime_in_days</td>
|
||||||
|
<td>{{ redisStatus!.uptime_in_days }}</td>
|
||||||
|
<td>{{ $t('database.uptimeInDays') }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>tcp_port</td>
|
||||||
|
<td>{{ redisStatus!.tcp_port }}</td>
|
||||||
|
<td>{{ $t('database.tcpPort') }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>connected_clients</td>
|
||||||
|
<td>{{ redisStatus!.connected_clients }}</td>
|
||||||
|
<td>{{ $t('database.connectedClients') }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>used_memory_rss</td>
|
||||||
|
<td>{{ redisStatus!.used_memory_rss }}</td>
|
||||||
|
<td>{{ $t('database.usedMemoryRss') }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>used_memory</td>
|
||||||
|
<td>{{ redisStatus!.used_memory }}</td>
|
||||||
|
<td>{{ $t('database.usedMemory') }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>mem_fragmentation_ratio</td>
|
||||||
|
<td>{{ redisStatus!.mem_fragmentation_ratio }}</td>
|
||||||
|
<td>{{ $t('database.tmpTableToDBHelper') }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>total_connections_received</td>
|
||||||
|
<td>{{ redisStatus!.total_connections_received }}</td>
|
||||||
|
<td>{{ $t('database.totalConnectionsReceived') }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>total_commands_processed</td>
|
||||||
|
<td>{{ redisStatus!.total_commands_processed }}</td>
|
||||||
|
<td>{{ $t('database.totalCommandsProcessed') }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>instantaneous_ops_per_sec</td>
|
||||||
|
<td>{{ redisStatus!.instantaneous_ops_per_sec }}</td>
|
||||||
|
<td>{{ $t('database.instantaneousOpsPerSec') }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>keyspace_hits</td>
|
||||||
|
<td>{{ redisStatus!.keyspace_hits }}</td>
|
||||||
|
<td>{{ $t('database.keyspaceHits') }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>keyspace_misses</td>
|
||||||
|
<td>{{ redisStatus!.keyspace_misses }}</td>
|
||||||
|
<td>{{ $t('database.keyspaceMisses') }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>hit</td>
|
||||||
|
<td>{{ redisStatus!.hit }}</td>
|
||||||
|
<td>{{ $t('database.hit') }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>latest_fork_usec</td>
|
||||||
|
<td>{{ redisStatus!.latest_fork_usec }}</td>
|
||||||
|
<td>{{ $t('database.latestForkUsec') }}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { loadRedisStatus } from '@/api/modules/database';
|
||||||
|
import { reactive, ref } from 'vue';
|
||||||
|
|
||||||
|
const redisStatus = reactive({
|
||||||
|
tcp_port: '',
|
||||||
|
uptime_in_days: '',
|
||||||
|
connected_clients: '',
|
||||||
|
used_memory: '',
|
||||||
|
used_memory_rss: '',
|
||||||
|
used_memory_peak: '',
|
||||||
|
mem_fragmentation_ratio: '',
|
||||||
|
total_connections_received: '',
|
||||||
|
total_commands_processed: '',
|
||||||
|
instantaneous_ops_per_sec: '',
|
||||||
|
keyspace_hits: '',
|
||||||
|
keyspace_misses: '',
|
||||||
|
hit: '',
|
||||||
|
latest_fork_usec: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
const statusShow = ref(false);
|
||||||
|
|
||||||
|
const acceptParams = (): void => {
|
||||||
|
statusShow.value = true;
|
||||||
|
loadStatus();
|
||||||
|
};
|
||||||
|
const onClose = (): void => {
|
||||||
|
statusShow.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadStatus = async () => {
|
||||||
|
const res = await loadRedisStatus();
|
||||||
|
let hit = (
|
||||||
|
(Number(res.data.keyspace_hits) / (Number(res.data.keyspace_hits) + Number(res.data.keyspace_misses))) *
|
||||||
|
100
|
||||||
|
).toFixed(2);
|
||||||
|
|
||||||
|
redisStatus.uptime_in_days = res.data.uptime_in_days;
|
||||||
|
redisStatus.tcp_port = res.data.tcp_port;
|
||||||
|
redisStatus.connected_clients = res.data.connected_clients;
|
||||||
|
redisStatus.used_memory_rss = (Number(res.data.used_memory_rss) / 1024 / 1024).toFixed(2) + ' MB';
|
||||||
|
redisStatus.used_memory = (Number(res.data.used_memory) / 1024 / 1024).toFixed(2) + ' MB';
|
||||||
|
redisStatus.mem_fragmentation_ratio = res.data.mem_fragmentation_ratio;
|
||||||
|
redisStatus.total_connections_received = res.data.total_connections_received;
|
||||||
|
redisStatus.total_commands_processed = res.data.total_commands_processed;
|
||||||
|
redisStatus.instantaneous_ops_per_sec = res.data.instantaneous_ops_per_sec;
|
||||||
|
redisStatus.keyspace_hits = res.data.keyspace_hits;
|
||||||
|
redisStatus.keyspace_misses = res.data.keyspace_misses;
|
||||||
|
redisStatus.hit = hit;
|
||||||
|
redisStatus.latest_fork_usec = res.data.latest_fork_usec;
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
acceptParams,
|
||||||
|
onClose,
|
||||||
|
});
|
||||||
|
</script>
|
154
frontend/src/views/database/redis/terminal/index.vue
Normal file
154
frontend/src/views/database/redis/terminal/index.vue
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
<template>
|
||||||
|
<div v-show="terminalShow" style="height: 100%">
|
||||||
|
<div style="height: calc(100vh - 220px)" :id="'terminal-exec'"></div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { Terminal } from 'xterm';
|
||||||
|
import { AttachAddon } from 'xterm-addon-attach';
|
||||||
|
import { Base64 } from 'js-base64';
|
||||||
|
import 'xterm/css/xterm.css';
|
||||||
|
import { FitAddon } from 'xterm-addon-fit';
|
||||||
|
|
||||||
|
const fitAddon = new FitAddon();
|
||||||
|
let terminalSocket = ref(null) as unknown as WebSocket;
|
||||||
|
let term = ref(null) as unknown as Terminal;
|
||||||
|
const loading = ref(true);
|
||||||
|
const runRealTerminal = () => {
|
||||||
|
loading.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const terminalShow = ref(false);
|
||||||
|
|
||||||
|
const acceptParams = async (): Promise<void> => {
|
||||||
|
terminalShow.value = true;
|
||||||
|
initTerm();
|
||||||
|
window.addEventListener('resize', changeTerminalSize);
|
||||||
|
};
|
||||||
|
const onClose = async () => {
|
||||||
|
window.removeEventListener('resize', changeTerminalSize);
|
||||||
|
if (isWsOpen()) {
|
||||||
|
terminalSocket && terminalSocket.close();
|
||||||
|
term.dispose();
|
||||||
|
}
|
||||||
|
terminalShow.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onWSReceive = (message: any) => {
|
||||||
|
if (!isJson(message.data)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const data = JSON.parse(message.data);
|
||||||
|
term.element && term.focus();
|
||||||
|
term.write(data.Data);
|
||||||
|
};
|
||||||
|
|
||||||
|
function isJson(str: string) {
|
||||||
|
try {
|
||||||
|
if (typeof JSON.parse(str) === 'object') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const errorRealTerminal = (ex: any) => {
|
||||||
|
let message = ex.message;
|
||||||
|
if (!message) message = 'disconnected';
|
||||||
|
term.write(`\x1b[31m${message}\x1b[m\r\n`);
|
||||||
|
};
|
||||||
|
|
||||||
|
const closeRealTerminal = (ev: CloseEvent) => {
|
||||||
|
term.write(ev.reason);
|
||||||
|
};
|
||||||
|
|
||||||
|
const initTerm = () => {
|
||||||
|
let ifm = document.getElementById('terminal-exec') as HTMLInputElement | null;
|
||||||
|
console.log(ifm);
|
||||||
|
term = new Terminal({
|
||||||
|
lineHeight: 1.2,
|
||||||
|
fontSize: 12,
|
||||||
|
fontFamily: "Monaco, Menlo, Consolas, 'Courier New', monospace",
|
||||||
|
theme: {
|
||||||
|
background: '#000000',
|
||||||
|
},
|
||||||
|
cursorBlink: true,
|
||||||
|
cursorStyle: 'underline',
|
||||||
|
scrollback: 100,
|
||||||
|
tabStopWidth: 4,
|
||||||
|
});
|
||||||
|
if (ifm) {
|
||||||
|
term.open(ifm);
|
||||||
|
terminalSocket = new WebSocket(
|
||||||
|
`ws://localhost:9999/api/v1/databases/redis/exec?cols=${term.cols}&rows=${term.rows}`,
|
||||||
|
);
|
||||||
|
terminalSocket.onopen = runRealTerminal;
|
||||||
|
terminalSocket.onmessage = onWSReceive;
|
||||||
|
terminalSocket.onclose = closeRealTerminal;
|
||||||
|
terminalSocket.onerror = errorRealTerminal;
|
||||||
|
term.onData((data: any) => {
|
||||||
|
if (isWsOpen()) {
|
||||||
|
terminalSocket.send(
|
||||||
|
JSON.stringify({
|
||||||
|
type: 'cmd',
|
||||||
|
cmd: Base64.encode(data),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
term.loadAddon(new AttachAddon(terminalSocket));
|
||||||
|
term.loadAddon(fitAddon);
|
||||||
|
setTimeout(() => {
|
||||||
|
fitAddon.fit();
|
||||||
|
if (isWsOpen()) {
|
||||||
|
terminalSocket.send(
|
||||||
|
JSON.stringify({
|
||||||
|
type: 'resize',
|
||||||
|
cols: term.cols,
|
||||||
|
rows: term.rows,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}, 30);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const fitTerm = () => {
|
||||||
|
fitAddon.fit();
|
||||||
|
};
|
||||||
|
|
||||||
|
const isWsOpen = () => {
|
||||||
|
const readyState = terminalSocket && terminalSocket.readyState;
|
||||||
|
if (readyState) {
|
||||||
|
return readyState === 1;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
function changeTerminalSize() {
|
||||||
|
fitTerm();
|
||||||
|
const { cols, rows } = term;
|
||||||
|
if (isWsOpen()) {
|
||||||
|
terminalSocket.send(
|
||||||
|
JSON.stringify({
|
||||||
|
type: 'resize',
|
||||||
|
cols: cols,
|
||||||
|
rows: rows,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defineExpose({
|
||||||
|
acceptParams,
|
||||||
|
onClose,
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
#terminal {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
</style>
|
Loading…
x
Reference in New Issue
Block a user