Files
mayfly-go/server/internal/redis/api/redis.go

223 lines
6.9 KiB
Go
Raw Normal View History

2021-09-11 14:04:09 +08:00
package api
import (
"context"
2022-09-09 18:26:08 +08:00
"mayfly-go/internal/redis/api/form"
"mayfly-go/internal/redis/api/vo"
"mayfly-go/internal/redis/application"
"mayfly-go/internal/redis/domain/entity"
2022-10-26 20:49:29 +08:00
tagapp "mayfly-go/internal/tag/application"
"mayfly-go/pkg/biz"
"mayfly-go/pkg/ginx"
2022-10-26 20:49:29 +08:00
"mayfly-go/pkg/model"
2023-01-14 16:29:52 +08:00
"mayfly-go/pkg/req"
2023-10-12 12:14:56 +08:00
"mayfly-go/pkg/utils/collx"
"mayfly-go/pkg/utils/cryptox"
"mayfly-go/pkg/utils/stringx"
"strconv"
"strings"
"github.com/gin-gonic/gin"
2023-03-17 09:46:41 +08:00
"github.com/redis/go-redis/v9"
)
type Redis struct {
2022-10-26 20:49:29 +08:00
RedisApp application.Redis
TagApp tagapp.TagTree
}
2023-01-14 16:29:52 +08:00
func (r *Redis) RedisList(rc *req.Ctx) {
queryCond, page := ginx.BindQueryAndPage[*entity.RedisQuery](rc.GinCtx, new(entity.RedisQuery))
2022-10-26 20:49:29 +08:00
// 不存在可访问标签id即没有可操作数据
tagIds := r.TagApp.ListTagIdByAccountId(rc.LoginAccount.Id)
if len(tagIds) == 0 {
rc.ResData = model.EmptyPageResult[any]()
2022-10-26 20:49:29 +08:00
return
}
queryCond.TagIds = tagIds
rc.ResData = r.RedisApp.GetPageList(queryCond, page, new([]vo.Redis))
}
func (r *Redis) RedisTags(rc *req.Ctx) {
rc.ResData = r.TagApp.ListTagByAccountIdAndResource(rc.LoginAccount.Id, new(entity.Redis))
}
2023-01-14 16:29:52 +08:00
func (r *Redis) Save(rc *req.Ctx) {
form := &form.Redis{}
redis := ginx.BindJsonAndCopyTo[*entity.Redis](rc.GinCtx, form, new(entity.Redis))
// 密码解密,并使用解密后的赋值
originPwd, err := cryptox.DefaultRsaDecrypt(redis.Password, true)
biz.ErrIsNilAppendErr(err, "解密密码错误: %s")
redis.Password = originPwd
// 密码脱敏记录日志
form.Password = "****"
rc.ReqParam = form
redis.SetBaseInfo(rc.LoginAccount)
r.RedisApp.Save(redis)
}
// 获取redis实例密码由于数据库是加密存储故提供该接口展示原文密码
2023-01-14 16:29:52 +08:00
func (r *Redis) GetRedisPwd(rc *req.Ctx) {
rid := uint64(ginx.PathParamInt(rc.GinCtx, "id"))
re := r.RedisApp.GetById(rid, "Password")
re.PwdDecrypt()
rc.ResData = re.Password
}
2023-01-14 16:29:52 +08:00
func (r *Redis) DeleteRedis(rc *req.Ctx) {
idsStr := ginx.PathParam(rc.GinCtx, "id")
rc.ReqParam = idsStr
ids := strings.Split(idsStr, ",")
for _, v := range ids {
value, err := strconv.Atoi(v)
biz.ErrIsNilAppendErr(err, "string类型转换为int异常: %s")
r.RedisApp.Delete(uint64(value))
}
}
2023-01-14 16:29:52 +08:00
func (r *Redis) RedisInfo(rc *req.Ctx) {
2022-09-29 13:14:50 +08:00
g := rc.GinCtx
ri := r.RedisApp.GetRedisInstance(uint64(ginx.PathParamInt(g, "id")), 0)
2023-02-07 16:54:44 +08:00
section := rc.GinCtx.Query("section")
2022-11-18 17:52:30 +08:00
mode := ri.Info.Mode
ctx := context.Background()
2023-02-07 16:54:44 +08:00
var redisCli *redis.Client
2022-11-18 17:52:30 +08:00
if mode == "" || mode == entity.RedisModeStandalone || mode == entity.RedisModeSentinel {
2023-02-07 16:54:44 +08:00
redisCli = ri.Cli
2022-11-18 17:52:30 +08:00
} else if mode == entity.RedisModeCluster {
host := rc.GinCtx.Query("host")
biz.NotEmpty(host, "集群模式host信息不能为空")
clusterClient := ri.ClusterCli
// 遍历集群的master节点找到该redis client
clusterClient.ForEachMaster(ctx, func(ctx context.Context, client *redis.Client) error {
if host == client.Options().Addr {
2023-02-07 16:54:44 +08:00
redisCli = client
}
return nil
})
2023-02-07 16:54:44 +08:00
if redisCli == nil {
// 遍历集群的slave节点找到该redis client
clusterClient.ForEachSlave(ctx, func(ctx context.Context, client *redis.Client) error {
if host == client.Options().Addr {
2023-02-07 16:54:44 +08:00
redisCli = client
}
return nil
})
}
2023-02-07 16:54:44 +08:00
biz.NotNil(redisCli, "该实例不在该集群中")
}
var res string
var err error
if section == "" {
res, err = redisCli.Info(ctx).Result()
2023-02-07 16:54:44 +08:00
} else {
res, err = redisCli.Info(ctx, section).Result()
}
biz.ErrIsNilAppendErr(err, "获取redis info失败: %s")
datas := strings.Split(res, "\r\n")
i := 0
length := len(datas)
2021-09-08 17:55:57 +08:00
parseMap := make(map[string]map[string]string)
for {
if i >= length {
break
}
if strings.Contains(datas[i], "#") {
key := stringx.SubString(datas[i], strings.Index(datas[i], "#")+1, stringx.Len(datas[i]))
i++
key = strings.Trim(key, " ")
2021-09-08 17:55:57 +08:00
sectionMap := make(map[string]string)
for {
if i >= length || !strings.Contains(datas[i], ":") {
break
}
pair := strings.Split(datas[i], ":")
i++
if len(pair) != 2 {
continue
}
sectionMap[pair[0]] = pair[1]
}
parseMap[key] = sectionMap
} else {
i++
}
}
rc.ResData = parseMap
}
2023-01-14 16:29:52 +08:00
func (r *Redis) ClusterInfo(rc *req.Ctx) {
g := rc.GinCtx
2022-09-29 13:14:50 +08:00
ri := r.RedisApp.GetRedisInstance(uint64(ginx.PathParamInt(g, "id")), 0)
2022-11-18 17:52:30 +08:00
biz.IsEquals(ri.Info.Mode, entity.RedisModeCluster, "非集群模式")
info, _ := ri.ClusterCli.ClusterInfo(context.Background()).Result()
nodesStr, _ := ri.ClusterCli.ClusterNodes(context.Background()).Result()
nodesRes := make([]map[string]string, 0)
nodes := strings.Split(nodesStr, "\n")
for _, node := range nodes {
if node == "" {
continue
}
nodeInfos := strings.Split(node, " ")
node := make(map[string]string)
node["nodeId"] = nodeInfos[0]
// ip:port1@port2port1指redis服务器与客户端通信的端口port2则是集群内部节点间通信的端口
node["ip"] = nodeInfos[1]
node["flags"] = nodeInfos[2]
// 如果节点是slave并且已知master节点则为master节点ID否则为符号"-"
node["masterSlaveRelation"] = nodeInfos[3]
// 最近一次发送ping的时间这个时间是一个unix毫秒时间戳0代表没有发送过
node["pingSent"] = nodeInfos[4]
// 最近一次收到pong的时间使用unix时间戳表示
node["pongRecv"] = nodeInfos[5]
// 节点的epoch值如果该节点是从节点则为其主节点的epoch值。每当节点发生失败切换时都会创建一个新的独特的递增的epoch。
// 如果多个节点竞争同一个哈希槽时epoch值更高的节点会抢夺到
node["configEpoch"] = nodeInfos[6]
// node-to-node集群总线使用的链接的状态我们使用这个链接与集群中其他节点进行通信.值可以是 connected 和 disconnected
node["linkState"] = nodeInfos[7]
// slave节点没有插槽信息
if len(nodeInfos) > 8 {
// slotmaster节点第9位为哈希槽值或者一个哈希槽范围代表当前节点可以提供服务的所有哈希槽值。如果只是一个值,那就是只有一个槽会被使用。
// 如果是一个范围,这个值表示为起始槽-结束槽,节点将处理包括起始槽和结束槽在内的所有哈希槽。
node["slot"] = nodeInfos[8]
}
nodesRes = append(nodesRes, node)
}
2023-10-12 12:14:56 +08:00
rc.ResData = collx.M{
"clusterInfo": info,
"clusterNodes": nodesRes,
}
}
// 校验查询参数中的key为必填项并返回redis实例
func (r *Redis) checkKeyAndGetRedisIns(rc *req.Ctx) (*application.RedisInstance, string) {
key := rc.GinCtx.Query("key")
biz.NotEmpty(key, "key不能为空")
return r.getRedisIns(rc), key
}
func (r *Redis) getRedisIns(rc *req.Ctx) *application.RedisInstance {
ri := r.RedisApp.GetRedisInstance(getIdAndDbNum(rc.GinCtx))
2022-11-18 17:52:30 +08:00
biz.ErrIsNilAppendErr(r.TagApp.CanAccess(rc.LoginAccount.Id, ri.Info.TagPath), "%s")
return ri
}
// 获取redis id与要操作的库号统一路径
func getIdAndDbNum(g *gin.Context) (uint64, int) {
return uint64(ginx.PathParamInt(g, "id")), ginx.PathParamInt(g, "db")
}