Browse Source

feat: Redis 数据库支持快速命令 (#5018)

Refs #3220
pull/5024/head
ssongliu 6 months ago committed by GitHub
parent
commit
8870b92ceb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 86
      backend/app/api/v1/command.go
  2. 6
      backend/app/dto/command.go
  3. 6
      backend/app/model/command.go
  4. 50
      backend/app/repo/command.go
  5. 62
      backend/app/service/command.go
  6. 1
      backend/init/migration/migrate.go
  7. 10
      backend/init/migration/migrations/v_1_10.go
  8. 5
      backend/router/ro_host.go
  9. 164
      cmd/server/docs/docs.go
  10. 164
      cmd/server/docs/swagger.json
  11. 104
      cmd/server/docs/swagger.yaml
  12. 5
      frontend/src/api/interface/command.ts
  13. 13
      frontend/src/api/modules/host.ts
  14. 1
      frontend/src/lang/modules/en.ts
  15. 1
      frontend/src/lang/modules/tw.ts
  16. 1
      frontend/src/lang/modules/zh.ts
  17. 164
      frontend/src/views/database/redis/command/index.vue
  18. 80
      frontend/src/views/database/redis/index.vue

86
backend/app/api/v1/command.go

@ -29,6 +29,28 @@ func (b *BaseApi) CreateCommand(c *gin.Context) {
helper.SuccessWithData(c, nil) helper.SuccessWithData(c, nil)
} }
// @Tags Redis Command
// @Summary Create redis command
// @Description 创建 Redis 快速命令
// @Accept json
// @Param request body dto.RedisCommand true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /hosts/command/redis [post]
// @x-panel-log {"bodyKeys":["name","command"],"paramKeys":[],"BeforeFunctions":[],"formatZH":"创建 redis 快捷命令 [name][command]","formatEN":"create quick command for redis [name][command]"}
func (b *BaseApi) CreateRedisCommand(c *gin.Context) {
var req dto.RedisCommand
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := commandService.CreateRedisCommand(req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Command // @Tags Command
// @Summary Page commands // @Summary Page commands
// @Description 获取快速命令列表分页 // @Description 获取快速命令列表分页
@ -55,6 +77,32 @@ func (b *BaseApi) SearchCommand(c *gin.Context) {
}) })
} }
// @Tags Redis Command
// @Summary Page redis commands
// @Description 获取 redis 快速命令列表分页
// @Accept json
// @Param request body dto.SearchWithPage true "request"
// @Success 200 {object} dto.PageResult
// @Security ApiKeyAuth
// @Router /hosts/command/redis/search [post]
func (b *BaseApi) SearchRedisCommand(c *gin.Context) {
var req dto.SearchWithPage
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
total, list, err := commandService.SearchRedisCommandWithPage(req)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, dto.PageResult{
Items: list,
Total: total,
})
}
// @Tags Command // @Tags Command
// @Summary Tree commands // @Summary Tree commands
// @Description 获取快速命令树 // @Description 获取快速命令树
@ -72,6 +120,22 @@ func (b *BaseApi) SearchCommandTree(c *gin.Context) {
helper.SuccessWithData(c, list) helper.SuccessWithData(c, list)
} }
// @Tags Redis Command
// @Summary List redis commands
// @Description 获取 redis 快速命令列表
// @Success 200 {Array} dto.RedisCommand
// @Security ApiKeyAuth
// @Router /hosts/command/redis [get]
func (b *BaseApi) ListRedisCommand(c *gin.Context) {
list, err := commandService.ListRedisCommand()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, list)
}
// @Tags Command // @Tags Command
// @Summary List commands // @Summary List commands
// @Description 获取快速命令列表 // @Description 获取快速命令列表
@ -110,6 +174,28 @@ func (b *BaseApi) DeleteCommand(c *gin.Context) {
helper.SuccessWithData(c, nil) helper.SuccessWithData(c, nil)
} }
// @Tags Redis Command
// @Summary Delete redis command
// @Description 删除 redis 快速命令
// @Accept json
// @Param request body dto.BatchDeleteReq true "request"
// @Success 200
// @Security ApiKeyAuth
// @Router /hosts/command/redis/del [post]
// @x-panel-log {"bodyKeys":["ids"],"paramKeys":[],"BeforeFunctions":[{"input_column":"id","input_value":"ids","isList":true,"db":"redis_commands","output_column":"name","output_value":"names"}],"formatZH":"删除 redis 快捷命令 [names]","formatEN":"delete quick command of redis [names]"}
func (b *BaseApi) DeleteRedisCommand(c *gin.Context) {
var req dto.BatchDeleteReq
if err := helper.CheckBindAndValidate(&req, c); err != nil {
return
}
if err := commandService.DeleteRedisCommand(req.Ids); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, nil)
}
// @Tags Command // @Tags Command
// @Summary Update command // @Summary Update command
// @Description 更新快速命令 // @Description 更新快速命令

6
backend/app/dto/command.go

@ -30,3 +30,9 @@ type CommandTree struct {
Label string `json:"label"` Label string `json:"label"`
Children []CommandInfo `json:"children"` Children []CommandInfo `json:"children"`
} }
type RedisCommand struct {
ID uint `json:"id"`
Name string `json:"name"`
Command string `json:"command"`
}

6
backend/app/model/command.go

@ -6,3 +6,9 @@ type Command struct {
GroupID uint `gorm:"type:decimal" json:"groupID"` GroupID uint `gorm:"type:decimal" json:"groupID"`
Command string `gorm:"type:varchar(256);not null" json:"command"` Command string `gorm:"type:varchar(256);not null" json:"command"`
} }
type RedisCommand struct {
BaseModel
Name string `gorm:"type:varchar(64);unique;not null" json:"name"`
Command string `gorm:"type:varchar(256);not null" json:"command"`
}

50
backend/app/repo/command.go

@ -17,6 +17,12 @@ type ICommandRepo interface {
Delete(opts ...DBOption) error Delete(opts ...DBOption) error
Get(opts ...DBOption) (model.Command, error) Get(opts ...DBOption) (model.Command, error)
WithLikeName(name string) DBOption WithLikeName(name string) DBOption
PageRedis(limit, offset int, opts ...DBOption) (int64, []model.RedisCommand, error)
GetRedis(opts ...DBOption) (model.RedisCommand, error)
GetRedisList(opts ...DBOption) ([]model.RedisCommand, error)
CreateRedis(command *model.RedisCommand) error
DeleteRedis(opts ...DBOption) error
} }
func NewICommandRepo() ICommandRepo { func NewICommandRepo() ICommandRepo {
@ -33,6 +39,16 @@ func (u *CommandRepo) Get(opts ...DBOption) (model.Command, error) {
return command, err return command, err
} }
func (u *CommandRepo) GetRedis(opts ...DBOption) (model.RedisCommand, error) {
var command model.RedisCommand
db := global.DB
for _, opt := range opts {
db = opt(db)
}
err := db.First(&command).Error
return command, err
}
func (u *CommandRepo) Page(page, size int, opts ...DBOption) (int64, []model.Command, error) { func (u *CommandRepo) Page(page, size int, opts ...DBOption) (int64, []model.Command, error) {
var users []model.Command var users []model.Command
db := global.DB.Model(&model.Command{}) db := global.DB.Model(&model.Command{})
@ -45,6 +61,18 @@ func (u *CommandRepo) Page(page, size int, opts ...DBOption) (int64, []model.Com
return count, users, err return count, users, err
} }
func (u *CommandRepo) PageRedis(page, size int, opts ...DBOption) (int64, []model.RedisCommand, error) {
var users []model.RedisCommand
db := global.DB.Model(&model.RedisCommand{})
for _, opt := range opts {
db = opt(db)
}
count := int64(0)
db = db.Count(&count)
err := db.Limit(size).Offset(size * (page - 1)).Find(&users).Error
return count, users, err
}
func (u *CommandRepo) GetList(opts ...DBOption) ([]model.Command, error) { func (u *CommandRepo) GetList(opts ...DBOption) ([]model.Command, error) {
var commands []model.Command var commands []model.Command
db := global.DB.Model(&model.Command{}) db := global.DB.Model(&model.Command{})
@ -55,6 +83,16 @@ func (u *CommandRepo) GetList(opts ...DBOption) ([]model.Command, error) {
return commands, err return commands, err
} }
func (u *CommandRepo) GetRedisList(opts ...DBOption) ([]model.RedisCommand, error) {
var commands []model.RedisCommand
db := global.DB.Model(&model.RedisCommand{})
for _, opt := range opts {
db = opt(db)
}
err := db.Find(&commands).Error
return commands, err
}
func (c *CommandRepo) WithByInfo(info string) DBOption { func (c *CommandRepo) WithByInfo(info string) DBOption {
return func(g *gorm.DB) *gorm.DB { return func(g *gorm.DB) *gorm.DB {
if len(info) == 0 { if len(info) == 0 {
@ -69,6 +107,10 @@ func (u *CommandRepo) Create(command *model.Command) error {
return global.DB.Create(command).Error return global.DB.Create(command).Error
} }
func (u *CommandRepo) CreateRedis(command *model.RedisCommand) error {
return global.DB.Create(command).Error
}
func (u *CommandRepo) Update(id uint, vars map[string]interface{}) error { func (u *CommandRepo) Update(id uint, vars map[string]interface{}) error {
return global.DB.Model(&model.Command{}).Where("id = ?", id).Updates(vars).Error return global.DB.Model(&model.Command{}).Where("id = ?", id).Updates(vars).Error
} }
@ -81,6 +123,14 @@ func (u *CommandRepo) Delete(opts ...DBOption) error {
return db.Delete(&model.Command{}).Error return db.Delete(&model.Command{}).Error
} }
func (u *CommandRepo) DeleteRedis(opts ...DBOption) error {
db := global.DB
for _, opt := range opts {
db = opt(db)
}
return db.Delete(&model.RedisCommand{}).Error
}
func (a CommandRepo) WithLikeName(name string) DBOption { func (a CommandRepo) WithLikeName(name string) DBOption {
return func(g *gorm.DB) *gorm.DB { return func(g *gorm.DB) *gorm.DB {
if len(name) == 0 { if len(name) == 0 {

62
backend/app/service/command.go

@ -16,6 +16,11 @@ type ICommandService interface {
Create(commandDto dto.CommandOperate) error Create(commandDto dto.CommandOperate) error
Update(id uint, upMap map[string]interface{}) error Update(id uint, upMap map[string]interface{}) error
Delete(ids []uint) error Delete(ids []uint) error
SearchRedisCommandWithPage(search dto.SearchWithPage) (int64, interface{}, error)
ListRedisCommand() ([]dto.RedisCommand, error)
CreateRedisCommand(commandDto dto.RedisCommand) error
DeleteRedisCommand(ids []uint) error
} }
func NewICommandService() ICommandService { func NewICommandService() ICommandService {
@ -115,3 +120,60 @@ func (u *CommandService) Delete(ids []uint) error {
func (u *CommandService) Update(id uint, upMap map[string]interface{}) error { func (u *CommandService) Update(id uint, upMap map[string]interface{}) error {
return commandRepo.Update(id, upMap) return commandRepo.Update(id, upMap)
} }
func (u *CommandService) SearchRedisCommandWithPage(search dto.SearchWithPage) (int64, interface{}, error) {
total, commands, err := commandRepo.PageRedis(search.Page, search.PageSize, commandRepo.WithLikeName(search.Info))
if err != nil {
return 0, nil, err
}
var dtoCommands []dto.RedisCommand
for _, command := range commands {
var item dto.RedisCommand
if err := copier.Copy(&item, &command); err != nil {
return 0, nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
}
dtoCommands = append(dtoCommands, item)
}
return total, dtoCommands, err
}
func (u *CommandService) ListRedisCommand() ([]dto.RedisCommand, error) {
commands, err := commandRepo.GetRedisList()
if err != nil {
return nil, constant.ErrRecordNotFound
}
var dtoCommands []dto.RedisCommand
for _, command := range commands {
var item dto.RedisCommand
if err := copier.Copy(&item, &command); err != nil {
return nil, errors.WithMessage(constant.ErrStructTransform, err.Error())
}
dtoCommands = append(dtoCommands, item)
}
return dtoCommands, err
}
func (u *CommandService) CreateRedisCommand(commandDto dto.RedisCommand) error {
command, _ := commandRepo.GetRedis(commonRepo.WithByName(commandDto.Name))
if command.ID != 0 {
return constant.ErrRecordExist
}
if err := copier.Copy(&command, &commandDto); err != nil {
return errors.WithMessage(constant.ErrStructTransform, err.Error())
}
if err := commandRepo.CreateRedis(&command); err != nil {
return err
}
return nil
}
func (u *CommandService) DeleteRedisCommand(ids []uint) error {
if len(ids) == 1 {
command, _ := commandRepo.GetRedis(commonRepo.WithByID(ids[0]))
if command.ID == 0 {
return constant.ErrRecordNotFound
}
return commandRepo.DeleteRedis(commonRepo.WithByID(ids[0]))
}
return commandRepo.DeleteRedis(commonRepo.WithIdsIn(ids))
}

1
backend/init/migration/migrate.go

@ -84,6 +84,7 @@ func Init() {
migrations.AddDeveloperSetting, migrations.AddDeveloperSetting,
migrations.AddWebsiteSSLColumn, migrations.AddWebsiteSSLColumn,
migrations.AddRedisCommand,
}) })
if err := m.Migrate(); err != nil { if err := m.Migrate(); err != nil {
global.LOG.Error(err) global.LOG.Error(err)

10
backend/init/migration/migrations/v_1_10.go

@ -164,3 +164,13 @@ var AddWebsiteSSLColumn = &gormigrate.Migration{
return nil return nil
}, },
} }
var AddRedisCommand = &gormigrate.Migration{
ID: "20240515-add-redis-command",
Migrate: func(tx *gorm.DB) error {
if err := tx.AutoMigrate(&model.RedisCommand{}); err != nil {
return err
}
return nil
},
}

5
backend/router/ro_host.go

@ -56,6 +56,11 @@ func (s *HostRouter) InitRouter(Router *gin.RouterGroup) {
hostRouter.GET("/command/tree", baseApi.SearchCommandTree) hostRouter.GET("/command/tree", baseApi.SearchCommandTree)
hostRouter.POST("/command/update", baseApi.UpdateCommand) hostRouter.POST("/command/update", baseApi.UpdateCommand)
hostRouter.GET("/command/redis", baseApi.ListRedisCommand)
hostRouter.POST("/command/redis", baseApi.CreateRedisCommand)
hostRouter.POST("/command/redis/search", baseApi.SearchRedisCommand)
hostRouter.POST("/command/redis/del", baseApi.DeleteRedisCommand)
hostRouter.POST("/tool", baseApi.GetToolStatus) hostRouter.POST("/tool", baseApi.GetToolStatus)
hostRouter.POST("/tool/init", baseApi.InitToolConfig) hostRouter.POST("/tool/init", baseApi.InitToolConfig)
hostRouter.POST("/tool/operate", baseApi.OperateTool) hostRouter.POST("/tool/operate", baseApi.OperateTool)

164
cmd/server/docs/docs.go

@ -7647,6 +7647,156 @@ const docTemplate = `{
} }
} }
}, },
"/hosts/command/redis": {
"get": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "获取 redis 快速命令列表",
"tags": [
"Redis Command"
],
"summary": "List redis commands",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "Array"
}
}
}
},
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "创建 Redis 快速命令",
"consumes": [
"application/json"
],
"tags": [
"Redis Command"
],
"summary": "Create redis command",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.RedisCommand"
}
}
],
"responses": {
"200": {
"description": "OK"
}
},
"x-panel-log": {
"BeforeFunctions": [],
"bodyKeys": [
"name",
"command"
],
"formatEN": "create quick command for redis [name][command]",
"formatZH": "创建 redis 快捷命令 [name][command]",
"paramKeys": []
}
}
},
"/hosts/command/redis/del": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "删除 redis 快速命令",
"consumes": [
"application/json"
],
"tags": [
"Redis Command"
],
"summary": "Delete redis command",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.BatchDeleteReq"
}
}
],
"responses": {
"200": {
"description": "OK"
}
},
"x-panel-log": {
"BeforeFunctions": [
{
"db": "redis_commands",
"input_column": "id",
"input_value": "ids",
"isList": true,
"output_column": "name",
"output_value": "names"
}
],
"bodyKeys": [
"ids"
],
"formatEN": "delete quick command of redis [names]",
"formatZH": "删除 redis 快捷命令 [names]",
"paramKeys": []
}
}
},
"/hosts/command/redis/search": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "获取 redis 快速命令列表分页",
"consumes": [
"application/json"
],
"tags": [
"Redis Command"
],
"summary": "Page redis commands",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.SearchWithPage"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.PageResult"
}
}
}
}
},
"/hosts/command/search": { "/hosts/command/search": {
"post": { "post": {
"security": [ "security": [
@ -17324,6 +17474,20 @@ const docTemplate = `{
} }
} }
}, },
"dto.RedisCommand": {
"type": "object",
"properties": {
"command": {
"type": "string"
},
"id": {
"type": "integer"
},
"name": {
"type": "string"
}
}
},
"dto.RedisConf": { "dto.RedisConf": {
"type": "object", "type": "object",
"required": [ "required": [

164
cmd/server/docs/swagger.json

@ -7640,6 +7640,156 @@
} }
} }
}, },
"/hosts/command/redis": {
"get": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "获取 redis 快速命令列表",
"tags": [
"Redis Command"
],
"summary": "List redis commands",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "Array"
}
}
}
},
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "创建 Redis 快速命令",
"consumes": [
"application/json"
],
"tags": [
"Redis Command"
],
"summary": "Create redis command",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.RedisCommand"
}
}
],
"responses": {
"200": {
"description": "OK"
}
},
"x-panel-log": {
"BeforeFunctions": [],
"bodyKeys": [
"name",
"command"
],
"formatEN": "create quick command for redis [name][command]",
"formatZH": "创建 redis 快捷命令 [name][command]",
"paramKeys": []
}
}
},
"/hosts/command/redis/del": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "删除 redis 快速命令",
"consumes": [
"application/json"
],
"tags": [
"Redis Command"
],
"summary": "Delete redis command",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.BatchDeleteReq"
}
}
],
"responses": {
"200": {
"description": "OK"
}
},
"x-panel-log": {
"BeforeFunctions": [
{
"db": "redis_commands",
"input_column": "id",
"input_value": "ids",
"isList": true,
"output_column": "name",
"output_value": "names"
}
],
"bodyKeys": [
"ids"
],
"formatEN": "delete quick command of redis [names]",
"formatZH": "删除 redis 快捷命令 [names]",
"paramKeys": []
}
}
},
"/hosts/command/redis/search": {
"post": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "获取 redis 快速命令列表分页",
"consumes": [
"application/json"
],
"tags": [
"Redis Command"
],
"summary": "Page redis commands",
"parameters": [
{
"description": "request",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/dto.SearchWithPage"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/dto.PageResult"
}
}
}
}
},
"/hosts/command/search": { "/hosts/command/search": {
"post": { "post": {
"security": [ "security": [
@ -17317,6 +17467,20 @@
} }
} }
}, },
"dto.RedisCommand": {
"type": "object",
"properties": {
"command": {
"type": "string"
},
"id": {
"type": "integer"
},
"name": {
"type": "string"
}
}
},
"dto.RedisConf": { "dto.RedisConf": {
"type": "object", "type": "object",
"required": [ "required": [

104
cmd/server/docs/swagger.yaml

@ -2225,6 +2225,15 @@ definitions:
- page - page
- pageSize - pageSize
type: object type: object
dto.RedisCommand:
properties:
command:
type: string
id:
type: integer
name:
type: string
type: object
dto.RedisConf: dto.RedisConf:
properties: properties:
containerName: containerName:
@ -9958,6 +9967,101 @@ paths:
formatEN: delete quick command [names] formatEN: delete quick command [names]
formatZH: 删除快捷命令 [names] formatZH: 删除快捷命令 [names]
paramKeys: [] paramKeys: []
/hosts/command/redis:
get:
description: 获取 redis 快速命令列表
responses:
"200":
description: OK
schema:
type: Array
security:
- ApiKeyAuth: []
summary: List redis commands
tags:
- Redis Command
post:
consumes:
- application/json
description: 创建 Redis 快速命令
parameters:
- description: request
in: body
name: request
required: true
schema:
$ref: '#/definitions/dto.RedisCommand'
responses:
"200":
description: OK
security:
- ApiKeyAuth: []
summary: Create redis command
tags:
- Redis Command
x-panel-log:
BeforeFunctions: []
bodyKeys:
- name
- command
formatEN: create quick command for redis [name][command]
formatZH: 创建 redis 快捷命令 [name][command]
paramKeys: []
/hosts/command/redis/del:
post:
consumes:
- application/json
description: 删除 redis 快速命令
parameters:
- description: request
in: body
name: request
required: true
schema:
$ref: '#/definitions/dto.BatchDeleteReq'
responses:
"200":
description: OK
security:
- ApiKeyAuth: []
summary: Delete redis command
tags:
- Redis Command
x-panel-log:
BeforeFunctions:
- db: redis_commands
input_column: id
input_value: ids
isList: true
output_column: name
output_value: names
bodyKeys:
- ids
formatEN: delete quick command of redis [names]
formatZH: 删除 redis 快捷命令 [names]
paramKeys: []
/hosts/command/redis/search:
post:
consumes:
- application/json
description: 获取 redis 快速命令列表分页
parameters:
- description: request
in: body
name: request
required: true
schema:
$ref: '#/definitions/dto.SearchWithPage'
responses:
"200":
description: OK
schema:
$ref: '#/definitions/dto.PageResult'
security:
- ApiKeyAuth: []
summary: Page redis commands
tags:
- Redis Command
/hosts/command/search: /hosts/command/search:
post: post:
consumes: consumes:

5
frontend/src/api/interface/command.ts

@ -11,4 +11,9 @@ export namespace Command {
groupID: number; groupID: number;
command: string; command: string;
} }
export interface RedisCommand {
id: number;
name: string;
command: string;
}
} }

13
frontend/src/api/modules/host.ts

@ -72,6 +72,19 @@ export const deleteCommand = (params: { ids: number[] }) => {
return http.post(`/hosts/command/del`, params); return http.post(`/hosts/command/del`, params);
}; };
export const getRedisCommandList = () => {
return http.get<Array<Command.RedisCommand>>(`/hosts/command/redis`, {});
};
export const getRedisCommandPage = (params: SearchWithPage) => {
return http.post<ResPage<Command.RedisCommand>>(`/hosts/command/redis/search`, params);
};
export const addRedisCommand = (params: Command.RedisCommand) => {
return http.post(`/hosts/command/redis`, params);
};
export const deleteRedisCommand = (params: { ids: number[] }) => {
return http.post(`/hosts/command/redis/del`, params);
};
// firewall // firewall
export const loadFireBaseInfo = () => { export const loadFireBaseInfo = () => {
return http.get<Host.FirewallBase>(`/hosts/firewall/base`); return http.get<Host.FirewallBase>(`/hosts/firewall/base`);

1
frontend/src/lang/modules/en.ts

@ -515,6 +515,7 @@ const message = {
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!', redisCliHelper: 'redis-cli service not detected, please enable the service first!',
redisQuickCmd: 'Redis quick command',
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',

1
frontend/src/lang/modules/tw.ts

@ -503,6 +503,7 @@ const message = {
hit: '查找數據庫鍵命中率', hit: '查找數據庫鍵命中率',
latestForkUsec: '最近一次 fork() 操作耗費的微秒數', latestForkUsec: '最近一次 fork() 操作耗費的微秒數',
redisCliHelper: '未檢測到 redis-cli 服務請先啟用服務', redisCliHelper: '未檢測到 redis-cli 服務請先啟用服務',
redisQuickCmd: 'Redis 快速命令',
recoverHelper: '即將使用 [{0}] 對數據進行覆蓋是否繼續?', recoverHelper: '即將使用 [{0}] 對數據進行覆蓋是否繼續?',
submitIt: '覆蓋數據', submitIt: '覆蓋數據',

1
frontend/src/lang/modules/zh.ts

@ -503,6 +503,7 @@ const message = {
hit: '查找数据库键命中率', hit: '查找数据库键命中率',
latestForkUsec: '最近一次 fork() 操作耗费的微秒数', latestForkUsec: '最近一次 fork() 操作耗费的微秒数',
redisCliHelper: '未检测到 redis-cli 服务请先启用服务', redisCliHelper: '未检测到 redis-cli 服务请先启用服务',
redisQuickCmd: 'Redis 快速命令',
recoverHelper: '即将使用 [{0}] 对数据进行覆盖是否继续?', recoverHelper: '即将使用 [{0}] 对数据进行覆盖是否继续?',
submitIt: '覆盖数据', submitIt: '覆盖数据',

164
frontend/src/views/database/redis/command/index.vue

@ -0,0 +1,164 @@
<template>
<div>
<el-drawer
v-model="drawerVisible"
:destroy-on-close="true"
@close="handleClose"
:close-on-click-modal="false"
:close-on-press-escape="false"
size="50%"
>
<template #header>
<DrawerHeader :header="$t('database.redisQuickCmd')" :back="handleClose" />
</template>
<el-button type="primary" @click="handleCmdAdd()">
{{ $t('commons.button.add') }}
</el-button>
<el-button @click="batchDelete(null)">
{{ $t('commons.button.delete') }}
</el-button>
<el-table :data="data" class="mt-5" @selection-change="handleSelectionChange">
<el-table-column type="selection" fix />
<el-table-column :label="$t('commons.table.name')" min-width="50">
<template #default="{ row }">
<el-input v-if="row.isOn" v-model="row.name" />
<span v-else>{{ row.name }}</span>
</template>
</el-table-column>
<el-table-column :label="$t('terminal.quickCommand')" min-width="120">
<template #default="{ row }">
<el-input v-if="row.isOn" v-model="row.command" />
<span v-else>{{ row.name }}</span>
</template>
</el-table-column>
<el-table-column min-width="40">
<template #default="scope">
<el-button link type="primary" @click="handleCmdCreate(scope.row)">
{{ $t('commons.button.save') }}
</el-button>
<el-button link type="primary" @click="handleCmdDelete(scope.$index)">
{{ $t('commons.button.delete') }}
</el-button>
</template>
</el-table-column>
</el-table>
<template #footer>
<span class="dialog-footer">
<el-button @click="handleClose">{{ $t('commons.button.cancel') }}</el-button>
</span>
</template>
</el-drawer>
<OpDialog ref="opRef" @search="search" />
</div>
</template>
<script setup lang="ts">
import { Command } from '@/api/interface/command';
import { addRedisCommand, deleteRedisCommand, getRedisCommandPage } from '@/api/modules/host';
import { reactive, ref } from 'vue';
import i18n from '@/lang';
import DrawerHeader from '@/components/drawer-header/index.vue';
import { MsgSuccess } from '@/utils/message';
const drawerVisible = ref();
const loading = ref();
const data = ref();
const selects = ref<any>([]);
const paginationConfig = reactive({
currentPage: 1,
pageSize: 10,
total: 0,
});
const opRef = ref();
const emit = defineEmits(['reload']);
const acceptParams = () => {
drawerVisible.value = true;
search();
};
const handleSelectionChange = (val: any[]) => {
selects.value = val;
};
const handleCmdAdd = () => {
let item = {
name: '',
command: '',
isOn: true,
};
data.value.push(item);
};
const handleCmdDelete = (index: number) => {
batchDelete(data.value[index]);
data.value.splice(index, 1);
};
const handleCmdCreate = async (row: any) => {
loading.value = true;
await addRedisCommand(row)
.then(() => {
loading.value = false;
row.isOn = false;
MsgSuccess(i18n.global.t('commons.msg.operationSuccess'));
search();
})
.catch(() => {
loading.value = false;
});
return;
};
const batchDelete = async (row: Command.RedisCommand | null) => {
let names = [];
let ids = [];
if (row) {
ids = [row.id];
names = [row.name];
} else {
selects.value.forEach((item: Command.CommandInfo) => {
ids.push(item.id);
names.push(item.name);
});
}
opRef.value.acceptParams({
title: i18n.global.t('commons.button.delete'),
names: names,
msg: i18n.global.t('commons.msg.operatorHelper', [
i18n.global.t('terminal.quickCommand'),
i18n.global.t('commons.button.delete'),
]),
api: deleteRedisCommand,
params: { ids: ids },
});
};
const search = async () => {
let params = {
page: paginationConfig.currentPage,
pageSize: paginationConfig.pageSize,
info: '',
};
loading.value = true;
await getRedisCommandPage(params)
.then((res) => {
loading.value = false;
data.value = res.data.items || [];
paginationConfig.total = res.data.total;
})
.catch(() => {
loading.value = false;
});
};
const handleClose = () => {
drawerVisible.value = false;
emit('reload');
};
defineExpose({
acceptParams,
});
</script>

80
frontend/src/views/database/redis/index.vue

@ -8,7 +8,7 @@
</div> </div>
</el-card> </el-card>
</div> </div>
<LayoutContent :title="'Redis ' + $t('menu.database')"> <LayoutContent>
<template #app v-if="currentDB?.from === 'local'"> <template #app v-if="currentDB?.from === 'local'">
<AppStatus <AppStatus
:app-key="'redis'" :app-key="'redis'"
@ -62,29 +62,39 @@
<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>
<template #main v-if="redisIsExist && !isOnSetting">
<Terminal
:style="{ height: `calc(100vh - ${loadHeight()})` }"
:key="isRefresh"
ref="terminalRef"
v-show="redisStatus === 'Running' && terminalShow"
/>
<el-empty
v-if="redisStatus !== 'Running' || (currentDB.from === 'remote' && !redisCliExist)"
:style="{ height: `calc(100vh - ${loadHeight()})`, 'background-color': '#000' }"
:description="
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>
</LayoutContent> </LayoutContent>
<div v-if="redisIsExist && !isOnSetting" class="mt-5">
<Terminal
:style="{ height: `calc(100vh - ${loadHeight()})` }"
:key="isRefresh"
ref="terminalRef"
v-show="redisStatus === 'Running' && terminalShow"
/>
<el-empty
v-if="redisStatus !== 'Running' || (currentDB.from === 'remote' && !redisCliExist)"
:style="{ height: `calc(100vh - ${loadHeight()})`, 'background-color': '#000' }"
:description="
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>
<div>
<el-select v-model="quickCmd" clearable filterable @change="quickInput" style="width: 90%">
<template #prefix>{{ $t('terminal.quickCommand') }}</template>
<el-option v-for="cmd in quickCmdList" :key="cmd.id" :label="cmd.name" :value="cmd.command" />
</el-select>
<el-button @click="onSetQuickCmd" icon="Setting" style="width: 10%">
{{ $t('commons.button.set') }}
</el-button>
</div>
</div>
<Setting ref="settingRef" style="margin-top: 30px" /> <Setting ref="settingRef" style="margin-top: 30px" />
<Password ref="passwordRef" @check-exist="reOpenTerminal" @close-terminal="closeTerminal(true)" /> <Password ref="passwordRef" @check-exist="reOpenTerminal" @close-terminal="closeTerminal(true)" />
<el-dialog <el-dialog
@ -107,6 +117,7 @@
</el-dialog> </el-dialog>
<PortJumpDialog ref="dialogPortJumpRef" /> <PortJumpDialog ref="dialogPortJumpRef" />
<QuickCmd ref="dialogQuickCmdRef" @reload="loadQuickCmd" />
</div> </div>
</template> </template>
@ -116,6 +127,7 @@ import Password from '@/views/database/redis/password/index.vue';
import Terminal from '@/components/terminal/index.vue'; import Terminal from '@/components/terminal/index.vue';
import AppStatus from '@/components/app-status/index.vue'; import AppStatus from '@/components/app-status/index.vue';
import PortJumpDialog from '@/components/port-jump/index.vue'; import PortJumpDialog from '@/components/port-jump/index.vue';
import QuickCmd from '@/views/database/redis/command/index.vue';
import { nextTick, onBeforeUnmount, onMounted, ref } from 'vue'; 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';
@ -124,6 +136,7 @@ import { listDatabases, checkRedisCli, installRedisCli } from '@/api/modules/dat
import { Database } from '@/api/interface/database'; import { Database } from '@/api/interface/database';
import { MsgSuccess } from '@/utils/message'; import { MsgSuccess } from '@/utils/message';
import i18n from '@/lang'; import i18n from '@/lang';
import { getRedisCommandList } from '@/api/modules/host';
const globalStore = GlobalStore(); const globalStore = GlobalStore();
const loading = ref(false); const loading = ref(false);
@ -148,6 +161,10 @@ const dbOptionsRemote = ref<Array<Database.DatabaseOption>>([]);
const currentDB = ref<Database.DatabaseOption>(); const currentDB = ref<Database.DatabaseOption>();
const currentDBName = ref(); const currentDBName = ref();
const quickCmd = ref();
const quickCmdList = ref([]);
const dialogQuickCmdRef = ref();
const dialogPortJumpRef = ref(); const dialogPortJumpRef = ref();
const isRefresh = ref(); const isRefresh = ref();
@ -160,7 +177,7 @@ const onSetting = async () => {
}; };
const loadHeight = () => { const loadHeight = () => {
return globalStore.openMenuTabs ? '470px' : '440px'; return globalStore.openMenuTabs ? '470px' : '380px';
}; };
const goDashboard = async () => { const goDashboard = async () => {
@ -288,7 +305,6 @@ const initTerminal = async () => {
.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') {
@ -331,8 +347,24 @@ const installCli = async () => {
}); });
}; };
const loadQuickCmd = async () => {
const res = await getRedisCommandList();
quickCmdList.value = res.data || [];
};
const quickInput = (val: any) => {
if (val) {
terminalRef.value?.sendMsg(val + '\n');
}
};
const onSetQuickCmd = () => {
dialogQuickCmdRef.value.acceptParams();
};
onMounted(() => { onMounted(() => {
loadDBOptions(); loadDBOptions();
loadQuickCmd();
loadDashboardPort(); loadDashboardPort();
checkCliValid(); checkCliValid();
}); });

Loading…
Cancel
Save