mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2025-01-31 14:08:06 +08:00
feat: Redis 支持远程数据库 (#5014)
This commit is contained in:
parent
c56435970a
commit
94cebfd8cc
@ -16,7 +16,7 @@ import (
|
|||||||
// @Param request body dto.OperationWithName true "request"
|
// @Param request body dto.OperationWithName true "request"
|
||||||
// @Success 200 {object} dto.RedisStatus
|
// @Success 200 {object} dto.RedisStatus
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Router /databases/redis/status [get]
|
// @Router /databases/redis/status [post]
|
||||||
func (b *BaseApi) LoadRedisStatus(c *gin.Context) {
|
func (b *BaseApi) LoadRedisStatus(c *gin.Context) {
|
||||||
var req dto.OperationWithName
|
var req dto.OperationWithName
|
||||||
if err := helper.CheckBind(&req, c); err != nil {
|
if err := helper.CheckBind(&req, c); err != nil {
|
||||||
@ -38,7 +38,7 @@ func (b *BaseApi) LoadRedisStatus(c *gin.Context) {
|
|||||||
// @Param request body dto.OperationWithName true "request"
|
// @Param request body dto.OperationWithName true "request"
|
||||||
// @Success 200 {object} dto.RedisConf
|
// @Success 200 {object} dto.RedisConf
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Router /databases/redis/conf [get]
|
// @Router /databases/redis/conf [post]
|
||||||
func (b *BaseApi) LoadRedisConf(c *gin.Context) {
|
func (b *BaseApi) LoadRedisConf(c *gin.Context) {
|
||||||
var req dto.OperationWithName
|
var req dto.OperationWithName
|
||||||
if err := helper.CheckBind(&req, c); err != nil {
|
if err := helper.CheckBind(&req, c); err != nil {
|
||||||
@ -60,7 +60,7 @@ func (b *BaseApi) LoadRedisConf(c *gin.Context) {
|
|||||||
// @Param request body dto.OperationWithName true "request"
|
// @Param request body dto.OperationWithName true "request"
|
||||||
// @Success 200 {object} dto.RedisPersistence
|
// @Success 200 {object} dto.RedisPersistence
|
||||||
// @Security ApiKeyAuth
|
// @Security ApiKeyAuth
|
||||||
// @Router /databases/redis/persistence/conf [get]
|
// @Router /databases/redis/persistence/conf [post]
|
||||||
func (b *BaseApi) LoadPersistenceConf(c *gin.Context) {
|
func (b *BaseApi) LoadPersistenceConf(c *gin.Context) {
|
||||||
var req dto.OperationWithName
|
var req dto.OperationWithName
|
||||||
if err := helper.CheckBind(&req, c); err != nil {
|
if err := helper.CheckBind(&req, c); err != nil {
|
||||||
@ -75,6 +75,25 @@ func (b *BaseApi) LoadPersistenceConf(c *gin.Context) {
|
|||||||
helper.SuccessWithData(c, data)
|
helper.SuccessWithData(c, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *BaseApi) CheckHasCli(c *gin.Context) {
|
||||||
|
helper.SuccessWithData(c, redisService.CheckHasCli())
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Tags Database Redis
|
||||||
|
// @Summary Install redis-cli
|
||||||
|
// @Description 安装 redis cli
|
||||||
|
// @Success 200
|
||||||
|
// @Security ApiKeyAuth
|
||||||
|
// @Router /databases/redis/install/cli [post]
|
||||||
|
func (b *BaseApi) InstallCli(c *gin.Context) {
|
||||||
|
if err := redisService.InstallCli(); err != nil {
|
||||||
|
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
helper.SuccessWithOutData(c)
|
||||||
|
}
|
||||||
|
|
||||||
// @Tags Database Redis
|
// @Tags Database Redis
|
||||||
// @Summary Update redis conf
|
// @Summary Update redis conf
|
||||||
// @Description 更新 redis 配置信息
|
// @Description 更新 redis 配置信息
|
||||||
|
@ -3,6 +3,7 @@ package v1
|
|||||||
import (
|
import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@ -88,23 +89,38 @@ func (b *BaseApi) RedisWsSsh(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
name := c.Query("name")
|
name := c.Query("name")
|
||||||
redisInfo, err := appInstallService.LoadConnInfo(dto.OperationWithNameAndType{Type: "redis", Name: name})
|
from := c.Query("from")
|
||||||
if wshandleError(wsConn, errors.WithMessage(err, "invalid param rows in request")) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
defer wsConn.Close()
|
defer wsConn.Close()
|
||||||
commands := []string{"redis-cli"}
|
commands := []string{"redis-cli"}
|
||||||
if len(redisInfo.Password) != 0 {
|
database, err := databaseService.Get(name)
|
||||||
commands = []string{"redis-cli", "-a", redisInfo.Password, "--no-auth-warning"}
|
if wshandleError(wsConn, errors.WithMessage(err, "no such database in db")) {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
pidMap := loadMapFromDockerTop(redisInfo.Password)
|
if from == "local" {
|
||||||
itemCmds := append([]string{"exec", "-it", redisInfo.ContainerName}, commands...)
|
redisInfo, err := appInstallService.LoadConnInfo(dto.OperationWithNameAndType{Name: name, Type: "redis"})
|
||||||
|
if wshandleError(wsConn, errors.WithMessage(err, "no such database in db")) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
name = redisInfo.ContainerName
|
||||||
|
if len(database.Password) != 0 {
|
||||||
|
commands = []string{"redis-cli", "-a", database.Password, "--no-auth-warning"}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
itemPort := fmt.Sprintf("%v", database.Port)
|
||||||
|
commands = []string{"redis-cli", "-h", database.Address, "-p", itemPort}
|
||||||
|
if len(database.Password) != 0 {
|
||||||
|
commands = []string{"redis-cli", "-h", database.Address, "-p", itemPort, "-a", database.Password, "--no-auth-warning"}
|
||||||
|
}
|
||||||
|
name = "1Panel-redis-cli-tools"
|
||||||
|
}
|
||||||
|
|
||||||
|
pidMap := loadMapFromDockerTop(name)
|
||||||
|
itemCmds := append([]string{"exec", "-it", name}, commands...)
|
||||||
slave, err := terminal.NewCommand(itemCmds)
|
slave, err := terminal.NewCommand(itemCmds)
|
||||||
if wshandleError(wsConn, err) {
|
if wshandleError(wsConn, err) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer killBash(redisInfo.ContainerName, strings.Join(commands, " "), pidMap)
|
defer killBash(name, strings.Join(commands, " "), pidMap)
|
||||||
defer slave.Close()
|
defer slave.Close()
|
||||||
|
|
||||||
tty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave, false)
|
tty, err := terminal.NewLocalWsSession(cols, rows, wsConn, slave, false)
|
||||||
|
@ -7,7 +7,8 @@ import (
|
|||||||
"path"
|
"path"
|
||||||
|
|
||||||
"github.com/1Panel-dev/1Panel/backend/utils/postgresql"
|
"github.com/1Panel-dev/1Panel/backend/utils/postgresql"
|
||||||
client2 "github.com/1Panel-dev/1Panel/backend/utils/postgresql/client"
|
pg_client "github.com/1Panel-dev/1Panel/backend/utils/postgresql/client"
|
||||||
|
redis_client "github.com/1Panel-dev/1Panel/backend/utils/redis"
|
||||||
|
|
||||||
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
"github.com/1Panel-dev/1Panel/backend/app/dto"
|
||||||
"github.com/1Panel-dev/1Panel/backend/buserr"
|
"github.com/1Panel-dev/1Panel/backend/buserr"
|
||||||
@ -113,7 +114,7 @@ func (u *DatabaseService) LoadItems(dbType string) ([]dto.DatabaseItem, error) {
|
|||||||
func (u *DatabaseService) CheckDatabase(req dto.DatabaseCreate) bool {
|
func (u *DatabaseService) CheckDatabase(req dto.DatabaseCreate) bool {
|
||||||
switch req.Type {
|
switch req.Type {
|
||||||
case constant.AppPostgresql:
|
case constant.AppPostgresql:
|
||||||
_, err := postgresql.NewPostgresqlClient(client2.DBInfo{
|
_, err := postgresql.NewPostgresqlClient(pg_client.DBInfo{
|
||||||
From: "remote",
|
From: "remote",
|
||||||
Address: req.Address,
|
Address: req.Address,
|
||||||
Port: req.Port,
|
Port: req.Port,
|
||||||
@ -122,6 +123,13 @@ func (u *DatabaseService) CheckDatabase(req dto.DatabaseCreate) bool {
|
|||||||
Timeout: 6,
|
Timeout: 6,
|
||||||
})
|
})
|
||||||
return err == nil
|
return err == nil
|
||||||
|
case constant.AppRedis:
|
||||||
|
_, err := redis_client.NewRedisClient(redis_client.DBInfo{
|
||||||
|
Address: req.Address,
|
||||||
|
Port: req.Port,
|
||||||
|
Password: req.Password,
|
||||||
|
})
|
||||||
|
return err == nil
|
||||||
case "mysql", "mariadb":
|
case "mysql", "mariadb":
|
||||||
_, err := mysql.NewMysqlClient(client.DBInfo{
|
_, err := mysql.NewMysqlClient(client.DBInfo{
|
||||||
From: "remote",
|
From: "remote",
|
||||||
@ -153,7 +161,7 @@ func (u *DatabaseService) Create(req dto.DatabaseCreate) error {
|
|||||||
}
|
}
|
||||||
switch req.Type {
|
switch req.Type {
|
||||||
case constant.AppPostgresql:
|
case constant.AppPostgresql:
|
||||||
if _, err := postgresql.NewPostgresqlClient(client2.DBInfo{
|
if _, err := postgresql.NewPostgresqlClient(pg_client.DBInfo{
|
||||||
From: "remote",
|
From: "remote",
|
||||||
Address: req.Address,
|
Address: req.Address,
|
||||||
Port: req.Port,
|
Port: req.Port,
|
||||||
@ -163,6 +171,14 @@ func (u *DatabaseService) Create(req dto.DatabaseCreate) error {
|
|||||||
}); err != nil {
|
}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
case constant.AppRedis:
|
||||||
|
if _, err := redis_client.NewRedisClient(redis_client.DBInfo{
|
||||||
|
Address: req.Address,
|
||||||
|
Port: req.Port,
|
||||||
|
Password: req.Password,
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
case "mysql", "mariadb":
|
case "mysql", "mariadb":
|
||||||
if _, err := mysql.NewMysqlClient(client.DBInfo{
|
if _, err := mysql.NewMysqlClient(client.DBInfo{
|
||||||
From: "remote",
|
From: "remote",
|
||||||
@ -249,7 +265,7 @@ func (u *DatabaseService) Delete(req dto.DatabaseDelete) error {
|
|||||||
func (u *DatabaseService) Update(req dto.DatabaseUpdate) error {
|
func (u *DatabaseService) Update(req dto.DatabaseUpdate) error {
|
||||||
switch req.Type {
|
switch req.Type {
|
||||||
case constant.AppPostgresql:
|
case constant.AppPostgresql:
|
||||||
if _, err := postgresql.NewPostgresqlClient(client2.DBInfo{
|
if _, err := postgresql.NewPostgresqlClient(pg_client.DBInfo{
|
||||||
From: "remote",
|
From: "remote",
|
||||||
Address: req.Address,
|
Address: req.Address,
|
||||||
Port: req.Port,
|
Port: req.Port,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -11,6 +12,8 @@ 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/utils/compose"
|
"github.com/1Panel-dev/1Panel/backend/utils/compose"
|
||||||
|
"github.com/1Panel-dev/1Panel/backend/utils/docker"
|
||||||
|
"github.com/docker/docker/api/types/container"
|
||||||
_ "github.com/go-sql-driver/mysql"
|
_ "github.com/go-sql-driver/mysql"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -24,6 +27,9 @@ type IRedisService interface {
|
|||||||
LoadStatus(req dto.OperationWithName) (*dto.RedisStatus, error)
|
LoadStatus(req dto.OperationWithName) (*dto.RedisStatus, error)
|
||||||
LoadConf(req dto.OperationWithName) (*dto.RedisConf, error)
|
LoadConf(req dto.OperationWithName) (*dto.RedisConf, error)
|
||||||
LoadPersistenceConf(req dto.OperationWithName) (*dto.RedisPersistence, error)
|
LoadPersistenceConf(req dto.OperationWithName) (*dto.RedisPersistence, error)
|
||||||
|
|
||||||
|
CheckHasCli() bool
|
||||||
|
InstallCli() error
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewIRedisService() IRedisService {
|
func NewIRedisService() IRedisService {
|
||||||
@ -50,6 +56,33 @@ func (u *RedisService) UpdateConf(req dto.RedisConfUpdate) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *RedisService) CheckHasCli() bool {
|
||||||
|
client, err := docker.NewDockerClient()
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
defer client.Close()
|
||||||
|
containerLists, err := client.ContainerList(context.Background(), container.ListOptions{})
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, item := range containerLists {
|
||||||
|
if strings.ReplaceAll(item.Names[0], "/", "") == "1Panel-redis-cli-tools" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (u *RedisService) InstallCli() error {
|
||||||
|
item := dto.ContainerOperate{
|
||||||
|
Name: "1Panel-redis-cli-tools",
|
||||||
|
Image: "redis:7.2.4",
|
||||||
|
Network: "1panel-network",
|
||||||
|
}
|
||||||
|
return NewIContainerService().ContainerCreate(item)
|
||||||
|
}
|
||||||
|
|
||||||
func (u *RedisService) ChangePassword(req dto.ChangeRedisPass) error {
|
func (u *RedisService) ChangePassword(req dto.ChangeRedisPass) error {
|
||||||
if err := updateInstallInfoInDB("redis", req.Database, "password", req.Value); err != nil {
|
if err := updateInstallInfoInDB("redis", req.Database, "password", req.Value); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -39,6 +39,8 @@ func (s *DatabaseRouter) InitRouter(Router *gin.RouterGroup) {
|
|||||||
cmdRouter.POST("/redis/status", baseApi.LoadRedisStatus)
|
cmdRouter.POST("/redis/status", baseApi.LoadRedisStatus)
|
||||||
cmdRouter.POST("/redis/conf", baseApi.LoadRedisConf)
|
cmdRouter.POST("/redis/conf", baseApi.LoadRedisConf)
|
||||||
cmdRouter.GET("/redis/exec", baseApi.RedisWsSsh)
|
cmdRouter.GET("/redis/exec", baseApi.RedisWsSsh)
|
||||||
|
cmdRouter.GET("/redis/check", baseApi.CheckHasCli)
|
||||||
|
cmdRouter.POST("/redis/install/cli", baseApi.InstallCli)
|
||||||
cmdRouter.POST("/redis/password", baseApi.ChangeRedisPassword)
|
cmdRouter.POST("/redis/password", baseApi.ChangeRedisPassword)
|
||||||
cmdRouter.POST("/redis/conf/update", baseApi.UpdateRedisConf)
|
cmdRouter.POST("/redis/conf/update", baseApi.UpdateRedisConf)
|
||||||
cmdRouter.POST("/redis/persistence/update", baseApi.UpdateRedisPersistenceConf)
|
cmdRouter.POST("/redis/persistence/update", baseApi.UpdateRedisPersistenceConf)
|
||||||
|
26
backend/utils/redis/redis.go
Normal file
26
backend/utils/redis/redis.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package redis
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/go-redis/redis"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DBInfo struct {
|
||||||
|
Address string `json:"address"`
|
||||||
|
Port uint `json:"port"`
|
||||||
|
Password string `json:"password"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRedisClient(conn DBInfo) (*redis.Client, error) {
|
||||||
|
client := redis.NewClient(&redis.Options{
|
||||||
|
Addr: fmt.Sprintf("%s:%v", conn.Address, conn.Port),
|
||||||
|
Password: conn.Password,
|
||||||
|
DB: 0,
|
||||||
|
})
|
||||||
|
|
||||||
|
if _, err := client.Ping().Result(); err != nil {
|
||||||
|
return client, err
|
||||||
|
}
|
||||||
|
return client, nil
|
||||||
|
}
|
@ -5110,7 +5110,7 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/databases/redis/conf": {
|
"/databases/redis/conf": {
|
||||||
"get": {
|
"post": {
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
"ApiKeyAuth": []
|
"ApiKeyAuth": []
|
||||||
@ -5185,6 +5185,25 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/databases/redis/install/cli": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "安装 redis cli",
|
||||||
|
"tags": [
|
||||||
|
"Database Redis"
|
||||||
|
],
|
||||||
|
"summary": "Install redis-cli",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/databases/redis/password": {
|
"/databases/redis/password": {
|
||||||
"post": {
|
"post": {
|
||||||
"security": [
|
"security": [
|
||||||
@ -5226,7 +5245,7 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/databases/redis/persistence/conf": {
|
"/databases/redis/persistence/conf": {
|
||||||
"get": {
|
"post": {
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
"ApiKeyAuth": []
|
"ApiKeyAuth": []
|
||||||
@ -5302,7 +5321,7 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/databases/redis/status": {
|
"/databases/redis/status": {
|
||||||
"get": {
|
"post": {
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
"ApiKeyAuth": []
|
"ApiKeyAuth": []
|
||||||
|
@ -5103,7 +5103,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/databases/redis/conf": {
|
"/databases/redis/conf": {
|
||||||
"get": {
|
"post": {
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
"ApiKeyAuth": []
|
"ApiKeyAuth": []
|
||||||
@ -5178,6 +5178,25 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/databases/redis/install/cli": {
|
||||||
|
"post": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"ApiKeyAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "安装 redis cli",
|
||||||
|
"tags": [
|
||||||
|
"Database Redis"
|
||||||
|
],
|
||||||
|
"summary": "Install redis-cli",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/databases/redis/password": {
|
"/databases/redis/password": {
|
||||||
"post": {
|
"post": {
|
||||||
"security": [
|
"security": [
|
||||||
@ -5219,7 +5238,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/databases/redis/persistence/conf": {
|
"/databases/redis/persistence/conf": {
|
||||||
"get": {
|
"post": {
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
"ApiKeyAuth": []
|
"ApiKeyAuth": []
|
||||||
@ -5295,7 +5314,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/databases/redis/status": {
|
"/databases/redis/status": {
|
||||||
"get": {
|
"post": {
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
"ApiKeyAuth": []
|
"ApiKeyAuth": []
|
||||||
|
@ -8349,7 +8349,7 @@ paths:
|
|||||||
tags:
|
tags:
|
||||||
- Database Postgresql
|
- Database Postgresql
|
||||||
/databases/redis/conf:
|
/databases/redis/conf:
|
||||||
get:
|
post:
|
||||||
consumes:
|
consumes:
|
||||||
- application/json
|
- application/json
|
||||||
description: 获取 redis 配置信息
|
description: 获取 redis 配置信息
|
||||||
@ -8396,6 +8396,17 @@ paths:
|
|||||||
formatEN: update the redis database configuration information
|
formatEN: update the redis database configuration information
|
||||||
formatZH: 更新 redis 数据库配置信息
|
formatZH: 更新 redis 数据库配置信息
|
||||||
paramKeys: []
|
paramKeys: []
|
||||||
|
/databases/redis/install/cli:
|
||||||
|
post:
|
||||||
|
description: 安装 redis cli
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
security:
|
||||||
|
- ApiKeyAuth: []
|
||||||
|
summary: Install redis-cli
|
||||||
|
tags:
|
||||||
|
- Database Redis
|
||||||
/databases/redis/password:
|
/databases/redis/password:
|
||||||
post:
|
post:
|
||||||
consumes:
|
consumes:
|
||||||
@ -8423,7 +8434,7 @@ paths:
|
|||||||
formatZH: 修改 redis 数据库密码
|
formatZH: 修改 redis 数据库密码
|
||||||
paramKeys: []
|
paramKeys: []
|
||||||
/databases/redis/persistence/conf:
|
/databases/redis/persistence/conf:
|
||||||
get:
|
post:
|
||||||
consumes:
|
consumes:
|
||||||
- application/json
|
- application/json
|
||||||
description: 获取 redis 持久化配置
|
description: 获取 redis 持久化配置
|
||||||
@ -8471,7 +8482,7 @@ paths:
|
|||||||
formatZH: redis 数据库持久化配置更新
|
formatZH: redis 数据库持久化配置更新
|
||||||
paramKeys: []
|
paramKeys: []
|
||||||
/databases/redis/status:
|
/databases/redis/status:
|
||||||
get:
|
post:
|
||||||
consumes:
|
consumes:
|
||||||
- application/json
|
- application/json
|
||||||
description: 获取 redis 状态信息
|
description: 获取 redis 状态信息
|
||||||
|
@ -117,6 +117,12 @@ export const loadRedisConf = (database: string) => {
|
|||||||
export const redisPersistenceConf = (database: string) => {
|
export const redisPersistenceConf = (database: string) => {
|
||||||
return http.post<Database.RedisPersistenceConf>(`/databases/redis/persistence/conf`, { name: database });
|
return http.post<Database.RedisPersistenceConf>(`/databases/redis/persistence/conf`, { name: database });
|
||||||
};
|
};
|
||||||
|
export const checkRedisCli = () => {
|
||||||
|
return http.get<boolean>(`/databases/redis/check`);
|
||||||
|
};
|
||||||
|
export const installRedisCli = () => {
|
||||||
|
return http.post(`/databases/redis/install/cli`, {}, TimeoutEnum.T_5M);
|
||||||
|
};
|
||||||
export const changeRedisPassword = (database: string, password: string) => {
|
export const changeRedisPassword = (database: string, password: string) => {
|
||||||
if (password) {
|
if (password) {
|
||||||
password = Base64.encode(password);
|
password = Base64.encode(password);
|
||||||
|
@ -514,6 +514,7 @@ const message = {
|
|||||||
keyspaceMisses: 'Number of failed attempts to find the database key',
|
keyspaceMisses: 'Number of failed attempts to find the database key',
|
||||||
hit: 'Find the database key hit ratio',
|
hit: 'Find the database key hit ratio',
|
||||||
latestForkUsec: 'The number of microseconds spent on the last fork() operation',
|
latestForkUsec: 'The number of microseconds spent on the last fork() operation',
|
||||||
|
redisCliHelper: 'redis-cli service not detected, please enable the service first!',
|
||||||
|
|
||||||
recoverHelper: 'Data is about to be overwritten with [{0}]. Do you want to continue?',
|
recoverHelper: 'Data is about to be overwritten with [{0}]. Do you want to continue?',
|
||||||
submitIt: 'Overwrite the data',
|
submitIt: 'Overwrite the data',
|
||||||
|
@ -502,6 +502,7 @@ const message = {
|
|||||||
keyspaceMisses: '查找數據庫鍵失敗的次數',
|
keyspaceMisses: '查找數據庫鍵失敗的次數',
|
||||||
hit: '查找數據庫鍵命中率',
|
hit: '查找數據庫鍵命中率',
|
||||||
latestForkUsec: '最近一次 fork() 操作耗費的微秒數',
|
latestForkUsec: '最近一次 fork() 操作耗費的微秒數',
|
||||||
|
redisCliHelper: '未檢測到 redis-cli 服務,請先啟用服務!',
|
||||||
|
|
||||||
recoverHelper: '即將使用 [{0}] 對數據進行覆蓋,是否繼續?',
|
recoverHelper: '即將使用 [{0}] 對數據進行覆蓋,是否繼續?',
|
||||||
submitIt: '覆蓋數據',
|
submitIt: '覆蓋數據',
|
||||||
|
@ -502,6 +502,7 @@ const message = {
|
|||||||
keyspaceMisses: '查找数据库键失败的次数',
|
keyspaceMisses: '查找数据库键失败的次数',
|
||||||
hit: '查找数据库键命中率',
|
hit: '查找数据库键命中率',
|
||||||
latestForkUsec: '最近一次 fork() 操作耗费的微秒数',
|
latestForkUsec: '最近一次 fork() 操作耗费的微秒数',
|
||||||
|
redisCliHelper: '未检测到 redis-cli 服务,请先启用服务!',
|
||||||
|
|
||||||
recoverHelper: '即将使用 [{0}] 对数据进行覆盖,是否继续?',
|
recoverHelper: '即将使用 [{0}] 对数据进行覆盖,是否继续?',
|
||||||
submitIt: '覆盖数据',
|
submitIt: '覆盖数据',
|
||||||
|
@ -89,6 +89,16 @@ const databaseRouter = {
|
|||||||
requiresAuth: false,
|
requiresAuth: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'redis/remote',
|
||||||
|
name: 'Redis-Remote',
|
||||||
|
component: () => import('@/views/database/redis/remote/index.vue'),
|
||||||
|
hidden: true,
|
||||||
|
meta: {
|
||||||
|
activeMenu: '/databases',
|
||||||
|
requiresAuth: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -28,6 +28,7 @@ export interface GlobalState {
|
|||||||
device: DeviceType;
|
device: DeviceType;
|
||||||
lastFilePath: string;
|
lastFilePath: string;
|
||||||
currentDB: string;
|
currentDB: string;
|
||||||
|
currentRedisDB: string;
|
||||||
showEntranceWarn: boolean;
|
showEntranceWarn: boolean;
|
||||||
defaultNetwork: string;
|
defaultNetwork: string;
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@ const GlobalStore = defineStore({
|
|||||||
device: DeviceType.Desktop,
|
device: DeviceType.Desktop,
|
||||||
lastFilePath: '',
|
lastFilePath: '',
|
||||||
currentDB: '',
|
currentDB: '',
|
||||||
|
currentRedisDB: '',
|
||||||
showEntranceWarn: true,
|
showEntranceWarn: true,
|
||||||
defaultNetwork: 'all',
|
defaultNetwork: 'all',
|
||||||
|
|
||||||
@ -80,6 +81,9 @@ const GlobalStore = defineStore({
|
|||||||
setCurrentDB(name: string) {
|
setCurrentDB(name: string) {
|
||||||
this.currentDB = name;
|
this.currentDB = name;
|
||||||
},
|
},
|
||||||
|
setCurrentRedisDB(name: string) {
|
||||||
|
this.currentRedisDB = name;
|
||||||
|
},
|
||||||
setShowEntranceWarn(show: boolean) {
|
setShowEntranceWarn(show: boolean) {
|
||||||
this.showEntranceWarn = show;
|
this.showEntranceWarn = show;
|
||||||
},
|
},
|
||||||
|
54
frontend/src/views/database/redis/check/index.vue
Normal file
54
frontend/src/views/database/redis/check/index.vue
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
v-model="open"
|
||||||
|
:title="$t('app.checkTitle')"
|
||||||
|
width="50%"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
:destroy-on-close="true"
|
||||||
|
>
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="20" :offset="2" v-if="open">
|
||||||
|
<el-alert
|
||||||
|
type="error"
|
||||||
|
:description="$t('app.deleteHelper', [$t('app.database')])"
|
||||||
|
center
|
||||||
|
show-icon
|
||||||
|
:closable="false"
|
||||||
|
/>
|
||||||
|
<br />
|
||||||
|
<el-descriptions border :column="1">
|
||||||
|
<el-descriptions-item>
|
||||||
|
<template #label>
|
||||||
|
<a href="javascript:void(0);" @click="toApp()">{{ $t('app.app') }}</a>
|
||||||
|
</template>
|
||||||
|
{{ installData.join(',') }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
interface InstallProps {
|
||||||
|
items: Array<string>;
|
||||||
|
}
|
||||||
|
const installData = ref();
|
||||||
|
let open = ref(false);
|
||||||
|
|
||||||
|
const acceptParams = (props: InstallProps) => {
|
||||||
|
installData.value = props.items;
|
||||||
|
open.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const toApp = () => {
|
||||||
|
router.push({ name: 'AppInstalled' });
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
acceptParams,
|
||||||
|
});
|
||||||
|
</script>
|
@ -1,5 +1,13 @@
|
|||||||
<template>
|
<template>
|
||||||
<div v-loading="loading">
|
<div v-loading="loading">
|
||||||
|
<div class="app-status" style="margin-top: 20px" v-if="currentDB?.from === 'remote'">
|
||||||
|
<el-card>
|
||||||
|
<div>
|
||||||
|
<el-tag style="float: left" effect="dark" type="success">Redis</el-tag>
|
||||||
|
<el-tag class="status-content">{{ $t('app.version') }}: {{ currentDB?.version }}</el-tag>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
<LayoutContent :title="'Redis ' + $t('menu.database')">
|
<LayoutContent :title="'Redis ' + $t('menu.database')">
|
||||||
<template #app v-if="currentDB?.from === 'local'">
|
<template #app v-if="currentDB?.from === 'local'">
|
||||||
<AppStatus
|
<AppStatus
|
||||||
@ -35,9 +43,6 @@
|
|||||||
<el-tooltip v-else :content="item.database" placement="top">
|
<el-tooltip v-else :content="item.database" placement="top">
|
||||||
<span>{{ item.database.substring(0, 25) }}...</span>
|
<span>{{ item.database.substring(0, 25) }}...</span>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<el-tag class="tagClass">
|
|
||||||
{{ item.type === 'mysql' ? 'MySQL' : 'MariaDB' }}
|
|
||||||
</el-tag>
|
|
||||||
</el-option>
|
</el-option>
|
||||||
</div>
|
</div>
|
||||||
<el-button link type="primary" class="jumpAdd" @click="goRouter('remote')" icon="Position">
|
<el-button link type="primary" class="jumpAdd" @click="goRouter('remote')" icon="Position">
|
||||||
@ -51,6 +56,9 @@
|
|||||||
<el-button type="primary" plain @click="onChangePassword">
|
<el-button type="primary" plain @click="onChangePassword">
|
||||||
{{ $t('database.databaseConnInfo') }}
|
{{ $t('database.databaseConnInfo') }}
|
||||||
</el-button>
|
</el-button>
|
||||||
|
<el-button @click="goRemoteDB" type="primary" plain>
|
||||||
|
{{ $t('database.remoteDB') }}
|
||||||
|
</el-button>
|
||||||
<el-button type="primary" plain @click="goDashboard" icon="Position">Redis-Commander</el-button>
|
<el-button type="primary" plain @click="goDashboard" icon="Position">Redis-Commander</el-button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -59,13 +67,21 @@
|
|||||||
:style="{ height: `calc(100vh - ${loadHeight()})` }"
|
:style="{ height: `calc(100vh - ${loadHeight()})` }"
|
||||||
:key="isRefresh"
|
:key="isRefresh"
|
||||||
ref="terminalRef"
|
ref="terminalRef"
|
||||||
v-show="terminalShow && redisStatus === 'Running'"
|
v-show="redisStatus === 'Running' && terminalShow"
|
||||||
/>
|
/>
|
||||||
<el-empty
|
<el-empty
|
||||||
v-if="redisStatus !== 'Running'"
|
v-if="redisStatus !== 'Running' || (currentDB.from === 'remote' && !redisCliExist)"
|
||||||
:style="{ height: `calc(100vh - ${loadHeight()})`, 'background-color': '#000' }"
|
:style="{ height: `calc(100vh - ${loadHeight()})`, 'background-color': '#000' }"
|
||||||
:description="$t('commons.service.serviceNotStarted', ['Redis'])"
|
:description="
|
||||||
></el-empty>
|
currentDB.from !== 'remote'
|
||||||
|
? $t('commons.service.serviceNotStarted', ['Redis'])
|
||||||
|
: $t('database.redisCliHelper')
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<el-button v-if="currentDB.from === 'remote'" type="primary" @click="installCli">
|
||||||
|
{{ $t('commons.button.enable') }}
|
||||||
|
</el-button>
|
||||||
|
</el-empty>
|
||||||
</template>
|
</template>
|
||||||
</LayoutContent>
|
</LayoutContent>
|
||||||
|
|
||||||
@ -104,8 +120,10 @@ import { nextTick, onBeforeUnmount, onMounted, ref } from 'vue';
|
|||||||
import { CheckAppInstalled, GetAppPort } from '@/api/modules/app';
|
import { CheckAppInstalled, GetAppPort } from '@/api/modules/app';
|
||||||
import router from '@/routers';
|
import router from '@/routers';
|
||||||
import { GlobalStore } from '@/store';
|
import { GlobalStore } from '@/store';
|
||||||
import { listDatabases } from '@/api/modules/database';
|
import { listDatabases, checkRedisCli, installRedisCli } from '@/api/modules/database';
|
||||||
import { Database } from '@/api/interface/database';
|
import { Database } from '@/api/interface/database';
|
||||||
|
import { MsgSuccess } from '@/utils/message';
|
||||||
|
import i18n from '@/lang';
|
||||||
const globalStore = GlobalStore();
|
const globalStore = GlobalStore();
|
||||||
|
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
@ -121,6 +139,8 @@ const terminalShow = ref(false);
|
|||||||
const redisCommandPort = ref();
|
const redisCommandPort = ref();
|
||||||
const commandVisible = ref(false);
|
const commandVisible = ref(false);
|
||||||
|
|
||||||
|
const redisCliExist = ref();
|
||||||
|
|
||||||
const appKey = ref('redis');
|
const appKey = ref('redis');
|
||||||
const appName = ref();
|
const appName = ref();
|
||||||
const dbOptionsLocal = ref<Array<Database.DatabaseOption>>([]);
|
const dbOptionsLocal = ref<Array<Database.DatabaseOption>>([]);
|
||||||
@ -153,6 +173,12 @@ const goDashboard = async () => {
|
|||||||
const getAppDetail = (key: string) => {
|
const getAppDetail = (key: string) => {
|
||||||
router.push({ name: 'AppAll', query: { install: key } });
|
router.push({ name: 'AppAll', query: { install: key } });
|
||||||
};
|
};
|
||||||
|
const goRemoteDB = async () => {
|
||||||
|
if (currentDB.value) {
|
||||||
|
globalStore.setCurrentRedisDB(currentDBName.value);
|
||||||
|
}
|
||||||
|
router.push({ name: 'Redis-Remote' });
|
||||||
|
};
|
||||||
|
|
||||||
const loadDashboardPort = async () => {
|
const loadDashboardPort = async () => {
|
||||||
const res = await GetAppPort('redis-commander', '');
|
const res = await GetAppPort('redis-commander', '');
|
||||||
@ -237,17 +263,39 @@ const reOpenTerminal = async () => {
|
|||||||
|
|
||||||
const initTerminal = async () => {
|
const initTerminal = async () => {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
|
if (currentDB.value.from === 'remote') {
|
||||||
|
if (!redisCliExist.value) {
|
||||||
|
loading.value = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
isRefresh.value = !isRefresh.value;
|
||||||
|
loading.value = false;
|
||||||
|
redisIsExist.value = true;
|
||||||
|
nextTick(() => {
|
||||||
|
terminalShow.value = true;
|
||||||
|
redisStatus.value = 'Running';
|
||||||
|
terminalRef.value.acceptParams({
|
||||||
|
endpoint: '/api/v1/databases/redis/exec',
|
||||||
|
args: `name=${currentDBName.value}&from=${currentDB.value.from}`,
|
||||||
|
error: '',
|
||||||
|
initCmd: '',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
isRefresh.value = !isRefresh.value;
|
||||||
|
return;
|
||||||
|
}
|
||||||
await CheckAppInstalled('redis', currentDBName.value)
|
await CheckAppInstalled('redis', currentDBName.value)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
redisIsExist.value = res.data.isExist;
|
redisIsExist.value = res.data.isExist;
|
||||||
redisStatus.value = res.data.status;
|
redisStatus.value = res.data.status;
|
||||||
|
console.log(redisStatus.value);
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
if (res.data.status === 'Running') {
|
if (res.data.status === 'Running') {
|
||||||
terminalShow.value = true;
|
terminalShow.value = true;
|
||||||
terminalRef.value.acceptParams({
|
terminalRef.value.acceptParams({
|
||||||
endpoint: '/api/v1/databases/redis/exec',
|
endpoint: '/api/v1/databases/redis/exec',
|
||||||
args: `name=${currentDBName.value}`,
|
args: `name=${currentDBName.value}&from=${currentDB.value.from}`,
|
||||||
error: '',
|
error: '',
|
||||||
initCmd: '',
|
initCmd: '',
|
||||||
});
|
});
|
||||||
@ -266,9 +314,27 @@ const closeTerminal = async (isKeepShow: boolean) => {
|
|||||||
terminalShow.value = isKeepShow;
|
terminalShow.value = isKeepShow;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const checkCliValid = async () => {
|
||||||
|
const res = await checkRedisCli();
|
||||||
|
redisCliExist.value = res.data;
|
||||||
|
};
|
||||||
|
const installCli = async () => {
|
||||||
|
loading.value = true;
|
||||||
|
await installRedisCli()
|
||||||
|
.then(() => {
|
||||||
|
loading.value = false;
|
||||||
|
redisCliExist.value = true;
|
||||||
|
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
loadDBOptions();
|
loadDBOptions();
|
||||||
loadDashboardPort();
|
loadDashboardPort();
|
||||||
|
checkCliValid();
|
||||||
});
|
});
|
||||||
const onBefore = () => {
|
const onBefore = () => {
|
||||||
closeTerminal(false);
|
closeTerminal(false);
|
||||||
|
100
frontend/src/views/database/redis/remote/delete/index.vue
Normal file
100
frontend/src/views/database/redis/remote/delete/index.vue
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
v-model="dialogVisible"
|
||||||
|
:title="$t('commons.button.delete') + ' - ' + deleteMysqlReq.database"
|
||||||
|
width="30%"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
>
|
||||||
|
<el-form ref="deleteForm" v-loading="loading" @submit.prevent>
|
||||||
|
<el-form-item>
|
||||||
|
<el-checkbox v-model="deleteMysqlReq.forceDelete" :label="$t('app.forceDelete')" />
|
||||||
|
<span class="input-help">
|
||||||
|
{{ $t('app.forceDeleteHelper') }}
|
||||||
|
</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-checkbox v-model="deleteMysqlReq.deleteBackup" :label="$t('app.deleteBackup')" />
|
||||||
|
<span class="input-help">
|
||||||
|
{{ $t('database.deleteBackupHelper') }}
|
||||||
|
</span>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<div>
|
||||||
|
<span style="font-size: 12px">{{ $t('database.delete') }}</span>
|
||||||
|
<span style="font-size: 12px; color: red; font-weight: 500">{{ deleteMysqlReq.database }}</span>
|
||||||
|
<span style="font-size: 12px">{{ $t('database.deleteHelper') }}</span>
|
||||||
|
</div>
|
||||||
|
<el-input v-model="delMysqlInfo" :placeholder="deleteMysqlReq.database"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="dialogVisible = false" :disabled="loading">
|
||||||
|
{{ $t('commons.button.cancel') }}
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
@click="submit"
|
||||||
|
:disabled="delMysqlInfo != deleteMysqlReq.database || loading"
|
||||||
|
>
|
||||||
|
{{ $t('commons.button.confirm') }}
|
||||||
|
</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { FormInstance } from 'element-plus';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import i18n from '@/lang';
|
||||||
|
import { deleteDatabase } from '@/api/modules/database';
|
||||||
|
import { MsgSuccess } from '@/utils/message';
|
||||||
|
|
||||||
|
let deleteMysqlReq = ref({
|
||||||
|
id: 0,
|
||||||
|
database: '',
|
||||||
|
deleteBackup: false,
|
||||||
|
forceDelete: false,
|
||||||
|
});
|
||||||
|
let dialogVisible = ref(false);
|
||||||
|
let loading = ref(false);
|
||||||
|
let delMysqlInfo = ref('');
|
||||||
|
|
||||||
|
const deleteForm = ref<FormInstance>();
|
||||||
|
|
||||||
|
interface DialogProps {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
database: string;
|
||||||
|
}
|
||||||
|
const emit = defineEmits<{ (e: 'search'): void }>();
|
||||||
|
|
||||||
|
const acceptParams = async (prop: DialogProps) => {
|
||||||
|
delMysqlInfo.value = '';
|
||||||
|
deleteMysqlReq.value = {
|
||||||
|
id: prop.id,
|
||||||
|
database: prop.database,
|
||||||
|
deleteBackup: false,
|
||||||
|
forceDelete: false,
|
||||||
|
};
|
||||||
|
dialogVisible.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const submit = async () => {
|
||||||
|
loading.value = true;
|
||||||
|
deleteDatabase(deleteMysqlReq.value)
|
||||||
|
.then(() => {
|
||||||
|
loading.value = false;
|
||||||
|
emit('search');
|
||||||
|
MsgSuccess(i18n.global.t('commons.msg.deleteSuccess'));
|
||||||
|
dialogVisible.value = false;
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
acceptParams,
|
||||||
|
});
|
||||||
|
</script>
|
176
frontend/src/views/database/redis/remote/index.vue
Normal file
176
frontend/src/views/database/redis/remote/index.vue
Normal file
@ -0,0 +1,176 @@
|
|||||||
|
<template>
|
||||||
|
<div v-loading="loading">
|
||||||
|
<LayoutContent>
|
||||||
|
<template #title>
|
||||||
|
<back-button name="Redis" :header="$t('database.remoteDB')" />
|
||||||
|
</template>
|
||||||
|
<template #toolbar>
|
||||||
|
<el-row>
|
||||||
|
<el-col :xs="24" :sm="20" :md="20" :lg="20" :xl="20">
|
||||||
|
<el-button type="primary" @click="onOpenDialog('create')">
|
||||||
|
{{ $t('database.createRemoteDB') }}
|
||||||
|
</el-button>
|
||||||
|
</el-col>
|
||||||
|
<el-col :xs="24" :sm="4" :md="4" :lg="4" :xl="4">
|
||||||
|
<TableSearch @search="search()" v-model:searchName="searchName" />
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</template>
|
||||||
|
<template #main>
|
||||||
|
<ComplexTable :pagination-config="paginationConfig" @sort-change="search" @search="search" :data="data">
|
||||||
|
<el-table-column show-overflow-tooltip :label="$t('commons.table.name')" prop="name" sortable />
|
||||||
|
<el-table-column show-overflow-tooltip :label="$t('database.address')" prop="address" />
|
||||||
|
<el-table-column :label="$t('commons.login.password')" prop="password">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<div class="flex items-center">
|
||||||
|
<div class="star-center">
|
||||||
|
<span v-if="!row.showPassword">**********</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span v-if="row.showPassword">
|
||||||
|
{{ row.password }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<el-button
|
||||||
|
v-if="!row.showPassword"
|
||||||
|
link
|
||||||
|
@click="row.showPassword = true"
|
||||||
|
icon="View"
|
||||||
|
class="ml-1.5"
|
||||||
|
></el-button>
|
||||||
|
<el-button
|
||||||
|
v-if="row.showPassword"
|
||||||
|
link
|
||||||
|
@click="row.showPassword = false"
|
||||||
|
icon="Hide"
|
||||||
|
class="ml-1.5"
|
||||||
|
></el-button>
|
||||||
|
<div>
|
||||||
|
<CopyButton :content="row.password" type="icon" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
prop="description"
|
||||||
|
:label="$t('commons.table.description')"
|
||||||
|
show-overflow-tooltip
|
||||||
|
/>
|
||||||
|
<el-table-column
|
||||||
|
prop="createdAt"
|
||||||
|
:label="$t('commons.table.date')"
|
||||||
|
:formatter="dateFormat"
|
||||||
|
show-overflow-tooltip
|
||||||
|
/>
|
||||||
|
<fu-table-operations
|
||||||
|
width="170px"
|
||||||
|
:buttons="buttons"
|
||||||
|
:ellipsis="10"
|
||||||
|
:label="$t('commons.table.operate')"
|
||||||
|
fix
|
||||||
|
/>
|
||||||
|
</ComplexTable>
|
||||||
|
</template>
|
||||||
|
</LayoutContent>
|
||||||
|
|
||||||
|
<AppResources ref="checkRef"></AppResources>
|
||||||
|
<OperateDialog ref="dialogRef" @search="search" />
|
||||||
|
<DeleteDialog ref="deleteRef" @search="search" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { dateFormat } from '@/utils/util';
|
||||||
|
import { onMounted, reactive, ref } from 'vue';
|
||||||
|
import { deleteCheckDatabase, searchDatabases } from '@/api/modules/database';
|
||||||
|
import AppResources from '@/views/database/redis/check/index.vue';
|
||||||
|
import OperateDialog from '@/views/database/redis/remote/operate/index.vue';
|
||||||
|
import DeleteDialog from '@/views/database/redis/remote/delete/index.vue';
|
||||||
|
import i18n from '@/lang';
|
||||||
|
import { Database } from '@/api/interface/database';
|
||||||
|
|
||||||
|
const loading = ref(false);
|
||||||
|
|
||||||
|
const dialogRef = ref();
|
||||||
|
const checkRef = ref();
|
||||||
|
const deleteRef = ref();
|
||||||
|
|
||||||
|
const data = ref();
|
||||||
|
const paginationConfig = reactive({
|
||||||
|
cacheSizeKey: 'redis-remote-page-size',
|
||||||
|
currentPage: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
total: 0,
|
||||||
|
orderBy: 'created_at',
|
||||||
|
order: 'null',
|
||||||
|
});
|
||||||
|
const searchName = ref();
|
||||||
|
|
||||||
|
const search = async (column?: any) => {
|
||||||
|
paginationConfig.orderBy = column?.order ? column.prop : paginationConfig.orderBy;
|
||||||
|
paginationConfig.order = column?.order ? column.order : paginationConfig.order;
|
||||||
|
let params = {
|
||||||
|
page: paginationConfig.currentPage,
|
||||||
|
pageSize: paginationConfig.pageSize,
|
||||||
|
info: searchName.value,
|
||||||
|
type: 'redis',
|
||||||
|
orderBy: paginationConfig.orderBy,
|
||||||
|
order: paginationConfig.order,
|
||||||
|
};
|
||||||
|
const res = await searchDatabases(params);
|
||||||
|
data.value = res.data.items || [];
|
||||||
|
paginationConfig.total = res.data.total;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onOpenDialog = async (
|
||||||
|
title: string,
|
||||||
|
rowData: Partial<Database.DatabaseInfo> = {
|
||||||
|
name: '',
|
||||||
|
type: 'redis',
|
||||||
|
from: 'remote',
|
||||||
|
version: '7.2.x',
|
||||||
|
address: '',
|
||||||
|
port: 6379,
|
||||||
|
username: '-',
|
||||||
|
password: '',
|
||||||
|
description: '',
|
||||||
|
},
|
||||||
|
) => {
|
||||||
|
let params = {
|
||||||
|
title,
|
||||||
|
rowData: { ...rowData },
|
||||||
|
};
|
||||||
|
dialogRef.value!.acceptParams(params);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onDelete = async (row: Database.DatabaseInfo) => {
|
||||||
|
const res = await deleteCheckDatabase(row.id);
|
||||||
|
if (res.data && res.data.length > 0) {
|
||||||
|
checkRef.value.acceptParams({ items: res.data });
|
||||||
|
} else {
|
||||||
|
deleteRef.value.acceptParams({
|
||||||
|
id: row.id,
|
||||||
|
database: row.name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const buttons = [
|
||||||
|
{
|
||||||
|
label: i18n.global.t('commons.button.edit'),
|
||||||
|
click: (row: Database.DatabaseInfo) => {
|
||||||
|
onOpenDialog('edit', row);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: i18n.global.t('commons.button.delete'),
|
||||||
|
click: (row: Database.DatabaseInfo) => {
|
||||||
|
onDelete(row);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
search();
|
||||||
|
});
|
||||||
|
</script>
|
174
frontend/src/views/database/redis/remote/operate/index.vue
Normal file
174
frontend/src/views/database/redis/remote/operate/index.vue
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
<template>
|
||||||
|
<el-drawer
|
||||||
|
v-model="drawerVisible"
|
||||||
|
:destroy-on-close="true"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
:close-on-press-escape="false"
|
||||||
|
size="50%"
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<DrawerHeader
|
||||||
|
:hideResource="dialogData.title === 'create'"
|
||||||
|
:header="title"
|
||||||
|
:resource="dialogData.rowData?.name"
|
||||||
|
:back="handleClose"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<el-form ref="formRef" v-loading="loading" label-position="top" :model="dialogData.rowData" :rules="rules">
|
||||||
|
<el-row type="flex" justify="center">
|
||||||
|
<el-col :span="22">
|
||||||
|
<el-form-item :label="$t('commons.table.name')" prop="name">
|
||||||
|
<el-input
|
||||||
|
v-if="dialogData.title === 'create'"
|
||||||
|
clearable
|
||||||
|
v-model.trim="dialogData.rowData!.name"
|
||||||
|
/>
|
||||||
|
<el-tag v-else>{{ dialogData.rowData!.name }}</el-tag>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('database.version')" prop="version">
|
||||||
|
<el-radio-group v-model="dialogData.rowData!.version" @change="isOK = false">
|
||||||
|
<el-radio label="6.x" value="6.x" />
|
||||||
|
<el-radio label="7.x" value="7.x" />
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('database.address')" prop="address">
|
||||||
|
<el-input @change="isOK = false" clearable v-model.trim="dialogData.rowData!.address" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('commons.table.port')" prop="port">
|
||||||
|
<el-input @change="isOK = false" clearable v-model.number="dialogData.rowData!.port" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('commons.login.password')" prop="password">
|
||||||
|
<el-input
|
||||||
|
@change="isOK = false"
|
||||||
|
type="password"
|
||||||
|
clearable
|
||||||
|
show-password
|
||||||
|
v-model.trim="dialogData.rowData!.password"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item :label="$t('commons.table.description')" prop="description">
|
||||||
|
<el-input clearable v-model.trim="dialogData.rowData!.description" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<span class="dialog-footer">
|
||||||
|
<el-button @click="drawerVisible = false">{{ $t('commons.button.cancel') }}</el-button>
|
||||||
|
<el-button @click="onSubmit(formRef, 'check')">
|
||||||
|
{{ $t('terminal.testConn') }}
|
||||||
|
</el-button>
|
||||||
|
<el-button type="primary" :disabled="!isOK" @click="onSubmit(formRef, dialogData.title)">
|
||||||
|
{{ $t('commons.button.confirm') }}
|
||||||
|
</el-button>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-drawer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { reactive, ref } from 'vue';
|
||||||
|
import i18n from '@/lang';
|
||||||
|
import { ElForm } from 'element-plus';
|
||||||
|
import { Database } from '@/api/interface/database';
|
||||||
|
import DrawerHeader from '@/components/drawer-header/index.vue';
|
||||||
|
import { MsgError, MsgSuccess } from '@/utils/message';
|
||||||
|
import { Rules } from '@/global/form-rules';
|
||||||
|
import { addDatabase, checkDatabase, editDatabase } from '@/api/modules/database';
|
||||||
|
|
||||||
|
interface DialogProps {
|
||||||
|
title: string;
|
||||||
|
rowData?: Database.DatabaseInfo;
|
||||||
|
getTableList?: () => Promise<any>;
|
||||||
|
}
|
||||||
|
const title = ref<string>('');
|
||||||
|
const drawerVisible = ref(false);
|
||||||
|
const dialogData = ref<DialogProps>({
|
||||||
|
title: '',
|
||||||
|
});
|
||||||
|
const isOK = ref(false);
|
||||||
|
const loading = ref();
|
||||||
|
|
||||||
|
const acceptParams = (params: DialogProps): void => {
|
||||||
|
dialogData.value = params;
|
||||||
|
if (dialogData.value.rowData.version.startsWith('6.')) {
|
||||||
|
dialogData.value.rowData.version = '6.x';
|
||||||
|
}
|
||||||
|
if (dialogData.value.rowData.version.startsWith('7.')) {
|
||||||
|
dialogData.value.rowData.version = '7,x';
|
||||||
|
}
|
||||||
|
title.value = i18n.global.t('database.' + dialogData.value.title + 'RemoteDB');
|
||||||
|
drawerVisible.value = true;
|
||||||
|
};
|
||||||
|
const emit = defineEmits<{ (e: 'search'): void }>();
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
drawerVisible.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const rules = reactive({
|
||||||
|
name: [Rules.requiredInput],
|
||||||
|
type: [Rules.requiredSelect],
|
||||||
|
version: [Rules.requiredSelect],
|
||||||
|
address: [Rules.ipV4V6OrDomain],
|
||||||
|
port: [Rules.port],
|
||||||
|
});
|
||||||
|
|
||||||
|
type FormInstance = InstanceType<typeof ElForm>;
|
||||||
|
const formRef = ref<FormInstance>();
|
||||||
|
|
||||||
|
const onSubmit = async (formEl: FormInstance | undefined, operation: string) => {
|
||||||
|
if (!formEl) return;
|
||||||
|
formEl.validate(async (valid) => {
|
||||||
|
if (!valid) return;
|
||||||
|
dialogData.value.rowData.from = 'remote';
|
||||||
|
loading.value = true;
|
||||||
|
dialogData.value.rowData.rootCert = dialogData.value.rowData.hasCA ? dialogData.value.rowData.rootCert : '';
|
||||||
|
if (operation === 'check') {
|
||||||
|
await checkDatabase(dialogData.value.rowData)
|
||||||
|
.then((res) => {
|
||||||
|
loading.value = false;
|
||||||
|
if (res.data) {
|
||||||
|
isOK.value = true;
|
||||||
|
MsgSuccess(i18n.global.t('terminal.connTestOk'));
|
||||||
|
} else {
|
||||||
|
MsgError(i18n.global.t('terminal.connTestFailed'));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
loading.value = false;
|
||||||
|
MsgError(i18n.global.t('terminal.connTestFailed'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (operation === 'create') {
|
||||||
|
await addDatabase(dialogData.value.rowData)
|
||||||
|
.then(() => {
|
||||||
|
loading.value = false;
|
||||||
|
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||||
|
emit('search');
|
||||||
|
drawerVisible.value = false;
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (operation === 'edit') {
|
||||||
|
await editDatabase(dialogData.value.rowData)
|
||||||
|
.then(() => {
|
||||||
|
loading.value = false;
|
||||||
|
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
|
||||||
|
emit('search');
|
||||||
|
drawerVisible.value = false;
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
acceptParams,
|
||||||
|
});
|
||||||
|
</script>
|
1
go.mod
1
go.mod
@ -19,6 +19,7 @@ require (
|
|||||||
github.com/go-acme/lego/v4 v4.15.0
|
github.com/go-acme/lego/v4 v4.15.0
|
||||||
github.com/go-gormigrate/gormigrate/v2 v2.1.1
|
github.com/go-gormigrate/gormigrate/v2 v2.1.1
|
||||||
github.com/go-playground/validator/v10 v10.18.0
|
github.com/go-playground/validator/v10 v10.18.0
|
||||||
|
github.com/go-redis/redis v6.15.9+incompatible
|
||||||
github.com/go-sql-driver/mysql v1.7.1
|
github.com/go-sql-driver/mysql v1.7.1
|
||||||
github.com/goh-chunlin/go-onedrive v1.1.1
|
github.com/goh-chunlin/go-onedrive v1.1.1
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.0
|
github.com/golang-jwt/jwt/v4 v4.5.0
|
||||||
|
5
go.sum
5
go.sum
@ -328,6 +328,8 @@ github.com/go-playground/validator/v10 v10.8.0/go.mod h1:9JhgTzTaE31GZDpH/HSvHiR
|
|||||||
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.18.0 h1:BvolUXjp4zuvkZ5YN5t7ebzbhlUtPsPm2S9NAZ5nl9U=
|
github.com/go-playground/validator/v10 v10.18.0 h1:BvolUXjp4zuvkZ5YN5t7ebzbhlUtPsPm2S9NAZ5nl9U=
|
||||||
github.com/go-playground/validator/v10 v10.18.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
github.com/go-playground/validator/v10 v10.18.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||||
|
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.3.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
github.com/go-sql-driver/mysql v1.3.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||||
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
|
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
|
||||||
@ -474,6 +476,7 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
|||||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
|
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec h1:qv2VnGeEQHchGaZ/u7lxST/RaJw+cv273q79D81Xbog=
|
||||||
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
|
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
|
||||||
|
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||||
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
|
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
|
||||||
@ -1256,6 +1259,7 @@ gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8
|
|||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||||
|
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||||
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
|
gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
|
||||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||||
@ -1265,6 +1269,7 @@ gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
|||||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
gopkg.in/rethinkdb/rethinkdb-go.v6 v6.2.1 h1:d4KQkxAaAiRY2h5Zqis161Pv91A37uZyJOx73duwUwM=
|
gopkg.in/rethinkdb/rethinkdb-go.v6 v6.2.1 h1:d4KQkxAaAiRY2h5Zqis161Pv91A37uZyJOx73duwUwM=
|
||||||
gopkg.in/rethinkdb/rethinkdb-go.v6 v6.2.1/go.mod h1:WbjuEoo1oadwzQ4apSDU+JTvmllEHtsNHS6y7vFc7iw=
|
gopkg.in/rethinkdb/rethinkdb-go.v6 v6.2.1/go.mod h1:WbjuEoo1oadwzQ4apSDU+JTvmllEHtsNHS6y7vFc7iw=
|
||||||
|
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.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
Loading…
x
Reference in New Issue
Block a user