From fe1290e933d43abf650ef5d19f62e808a844d879 Mon Sep 17 00:00:00 2001 From: ssongliu <73214554+ssongliu@users.noreply.github.com> Date: Tue, 31 Oct 2023 14:14:30 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=BF=AB=E9=80=9F=E5=91=BD=E4=BB=A4?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=88=86=E7=BB=84=20(#2732)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refs #2725 --- backend/app/api/v1/command.go | 20 +++- backend/app/dto/command.go | 28 ++++-- backend/app/model/command.go | 1 + backend/app/service/command.go | 43 ++++++++- backend/app/service/group.go | 5 + backend/init/migration/migrate.go | 1 + backend/init/migration/migrations/v_1_8.go | 17 ++++ backend/router/ro_host.go | 1 + cmd/server/docs/docs.go | 58 ++++++++++-- cmd/server/docs/swagger.json | 58 ++++++++++-- cmd/server/docs/swagger.yaml | 38 +++++++- frontend/src/api/interface/command.ts | 2 + frontend/src/api/modules/command.ts | 23 ----- frontend/src/api/modules/host.ts | 3 + frontend/src/components/group/change.vue | 93 +++++++++++++++++++ .../src/views/host/terminal/command/index.vue | 86 +++++++++++++++-- .../src/views/host/terminal/host/index.vue | 21 ++++- .../views/host/terminal/terminal/index.vue | 28 +++--- 18 files changed, 451 insertions(+), 75 deletions(-) delete mode 100644 frontend/src/api/modules/command.ts create mode 100644 frontend/src/components/group/change.vue diff --git a/backend/app/api/v1/command.go b/backend/app/api/v1/command.go index dc2e0f474..6dcf220c3 100644 --- a/backend/app/api/v1/command.go +++ b/backend/app/api/v1/command.go @@ -38,7 +38,7 @@ func (b *BaseApi) CreateCommand(c *gin.Context) { // @Security ApiKeyAuth // @Router /hosts/command/search [post] func (b *BaseApi) SearchCommand(c *gin.Context) { - var req dto.SearchWithPage + var req dto.SearchCommandWithPage if err := helper.CheckBindAndValidate(&req, c); err != nil { return } @@ -55,6 +55,23 @@ func (b *BaseApi) SearchCommand(c *gin.Context) { }) } +// @Tags Command +// @Summary Tree commands +// @Description 获取快速命令树 +// @Accept json +// @Success 200 {Array} dto.CommandTree +// @Security ApiKeyAuth +// @Router /hosts/command/tree [get] +func (b *BaseApi) SearchCommandTree(c *gin.Context) { + list, err := commandService.SearchForTree() + if err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + + helper.SuccessWithData(c, list) +} + // @Tags Command // @Summary List commands // @Description 获取快速命令列表 @@ -110,6 +127,7 @@ func (b *BaseApi) UpdateCommand(c *gin.Context) { upMap := make(map[string]interface{}) upMap["name"] = req.Name + upMap["group_id"] = req.GroupID upMap["command"] = req.Command if err := commandService.Update(req.ID, upMap); err != nil { helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) diff --git a/backend/app/dto/command.go b/backend/app/dto/command.go index e253fdfe2..04f5721df 100644 --- a/backend/app/dto/command.go +++ b/backend/app/dto/command.go @@ -1,13 +1,29 @@ package dto +type SearchCommandWithPage struct { + SearchWithPage + GroupID uint `json:"groupID"` + Info string `json:"info"` +} + type CommandOperate struct { - ID uint `json:"id"` - Name string `json:"name" validate:"required"` - Command string `json:"command" validate:"required"` + ID uint `json:"id"` + GroupID uint `json:"groupID"` + GroupBelong string `json:"groupBelong"` + Name string `json:"name" validate:"required"` + Command string `json:"command" validate:"required"` } type CommandInfo struct { - ID uint `json:"id"` - Name string `json:"name"` - Command string `json:"command"` + ID uint `json:"id"` + GroupID uint `json:"groupID"` + Name string `json:"name"` + Command string `json:"command"` + GroupBelong string `json:"groupBelong"` +} + +type CommandTree struct { + ID uint `json:"id"` + Label string `json:"label"` + Children []CommandInfo `json:"children"` } diff --git a/backend/app/model/command.go b/backend/app/model/command.go index 482ca9e28..a95fbe41f 100644 --- a/backend/app/model/command.go +++ b/backend/app/model/command.go @@ -3,5 +3,6 @@ package model type Command struct { BaseModel Name string `gorm:"type:varchar(64);unique;not null" json:"name"` + GroupID uint `gorm:"type:decimal" json:"groupID"` Command string `gorm:"type:varchar(256);not null" json:"command"` } diff --git a/backend/app/service/command.go b/backend/app/service/command.go index 17dc5ddd9..a2ceb2210 100644 --- a/backend/app/service/command.go +++ b/backend/app/service/command.go @@ -11,7 +11,8 @@ type CommandService struct{} type ICommandService interface { List() ([]dto.CommandInfo, error) - SearchWithPage(search dto.SearchWithPage) (int64, interface{}, error) + SearchForTree() ([]dto.CommandTree, error) + SearchWithPage(search dto.SearchCommandWithPage) (int64, interface{}, error) Create(commandDto dto.CommandOperate) error Update(id uint, upMap map[string]interface{}) error Delete(ids []uint) error @@ -37,14 +38,50 @@ func (u *CommandService) List() ([]dto.CommandInfo, error) { return dtoCommands, err } -func (u *CommandService) SearchWithPage(search dto.SearchWithPage) (int64, interface{}, error) { - total, commands, err := commandRepo.Page(search.Page, search.PageSize, commonRepo.WithLikeName(search.Info)) +func (u *CommandService) SearchForTree() ([]dto.CommandTree, error) { + cmdList, err := commandRepo.GetList() + if err != nil { + return nil, err + } + groups, err := groupRepo.GetList(commonRepo.WithByType("command")) + if err != nil { + return nil, err + } + var lists []dto.CommandTree + for _, group := range groups { + var data dto.CommandTree + data.ID = group.ID + 10000 + data.Label = group.Name + for _, cmd := range cmdList { + if cmd.GroupID == group.ID { + data.Children = append(data.Children, dto.CommandInfo{ID: cmd.ID, Name: cmd.Name, Command: cmd.Command}) + } + } + if len(data.Children) != 0 { + lists = append(lists, data) + } + } + return lists, err +} + +func (u *CommandService) SearchWithPage(search dto.SearchCommandWithPage) (int64, interface{}, error) { + total, commands, err := commandRepo.Page(search.Page, search.PageSize, commonRepo.WithLikeName(search.Info), commonRepo.WithByGroupID(search.GroupID)) + if err != nil { + return 0, nil, err + } + groups, _ := groupRepo.GetList(commonRepo.WithByType("command")) var dtoCommands []dto.CommandInfo for _, command := range commands { var item dto.CommandInfo if err := copier.Copy(&item, &command); err != nil { return 0, nil, errors.WithMessage(constant.ErrStructTransform, err.Error()) } + for _, group := range groups { + if command.GroupID == group.ID { + item.GroupBelong = group.Name + item.GroupID = group.ID + } + } dtoCommands = append(dtoCommands, item) } return total, dtoCommands, err diff --git a/backend/app/service/group.go b/backend/app/service/group.go index 11c5f87c4..33823bdb3 100644 --- a/backend/app/service/group.go +++ b/backend/app/service/group.go @@ -62,6 +62,11 @@ func (u *GroupService) Delete(id uint) error { if len(websites) > 0 { return buserr.New(constant.ErrGroupIsUsed) } + case "command": + commands, _ := commandRepo.GetList(commonRepo.WithByGroupID(id)) + if len(commands) > 0 { + return buserr.New(constant.ErrGroupIsUsed) + } case "host": hosts, _ := hostRepo.GetList(commonRepo.WithByGroupID(id)) if len(hosts) > 0 { diff --git a/backend/init/migration/migrate.go b/backend/init/migration/migrate.go index 580b95099..1220077ac 100644 --- a/backend/init/migration/migrate.go +++ b/backend/init/migration/migrate.go @@ -50,6 +50,7 @@ func Init() { migrations.AddFavorite, migrations.AddBindAddress, + migrations.AddCommandGroup, }) if err := m.Migrate(); err != nil { global.LOG.Error(err) diff --git a/backend/init/migration/migrations/v_1_8.go b/backend/init/migration/migrations/v_1_8.go index 0549e3dac..2404d53e3 100644 --- a/backend/init/migration/migrations/v_1_8.go +++ b/backend/init/migration/migrations/v_1_8.go @@ -28,3 +28,20 @@ var AddBindAddress = &gormigrate.Migration{ return nil }, } + +var AddCommandGroup = &gormigrate.Migration{ + ID: "20231030-add-command-group", + Migrate: func(tx *gorm.DB) error { + if err := tx.AutoMigrate(&model.Command{}); err != nil { + return err + } + defaultCommand := &model.Group{IsDefault: true, Name: "默认", Type: "command"} + if err := tx.Create(defaultCommand).Error; err != nil { + return err + } + if err := tx.Model(&model.Command{}).Where("1 = 1").Update("group_id", defaultCommand.ID).Error; err != nil { + return err + } + return nil + }, +} diff --git a/backend/router/ro_host.go b/backend/router/ro_host.go index 7c16ac56d..e6ad3f55d 100644 --- a/backend/router/ro_host.go +++ b/backend/router/ro_host.go @@ -49,6 +49,7 @@ func (s *HostRouter) InitHostRouter(Router *gin.RouterGroup) { hostRouter.POST("/command", baseApi.CreateCommand) hostRouter.POST("/command/del", baseApi.DeleteCommand) hostRouter.POST("/command/search", baseApi.SearchCommand) + hostRouter.GET("/command/tree", baseApi.SearchCommandTree) hostRouter.POST("/command/update", baseApi.UpdateCommand) hostRouter.POST("/tool", baseApi.GetToolStatus) diff --git a/cmd/server/docs/docs.go b/cmd/server/docs/docs.go index 33a75d3d8..f3e52ae9c 100644 --- a/cmd/server/docs/docs.go +++ b/cmd/server/docs/docs.go @@ -5261,7 +5261,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/request.FileOption" + "$ref": "#/definitions/request.FileContentReq" } } ], @@ -7076,6 +7076,31 @@ const docTemplate = `{ } } }, + "/hosts/command/tree": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "获取快速命令树", + "consumes": [ + "application/json" + ], + "tags": [ + "Command" + ], + "summary": "Tree commands", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "Array" + } + } + } + } + }, "/hosts/command/update": { "post": { "security": [ @@ -12733,6 +12758,12 @@ const docTemplate = `{ "command": { "type": "string" }, + "groupBelong": { + "type": "string" + }, + "groupID": { + "type": "integer" + }, "id": { "type": "integer" }, @@ -12751,6 +12782,12 @@ const docTemplate = `{ "command": { "type": "string" }, + "groupBelong": { + "type": "string" + }, + "groupID": { + "type": "integer" + }, "id": { "type": "integer" }, @@ -14866,7 +14903,6 @@ const docTemplate = `{ "dto.OperationWithNameAndType": { "type": "object", "required": [ - "name", "type" ], "properties": { @@ -15052,7 +15088,6 @@ const docTemplate = `{ "dto.RecordSearch": { "type": "object", "required": [ - "name", "page", "pageSize", "type" @@ -16463,6 +16498,9 @@ const docTemplate = `{ "pageSize" ], "properties": { + "all": { + "type": "boolean" + }, "name": { "type": "string" }, @@ -16652,6 +16690,17 @@ const docTemplate = `{ } } }, + "request.FileContentReq": { + "type": "object", + "required": [ + "path" + ], + "properties": { + "path": { + "type": "string" + } + } + }, "request.FileCreate": { "type": "object", "required": [ @@ -17822,9 +17871,6 @@ const docTemplate = `{ }, "request.WebsiteInstallCheckReq": { "type": "object", - "required": [ - "InstallIds" - ], "properties": { "InstallIds": { "type": "array", diff --git a/cmd/server/docs/swagger.json b/cmd/server/docs/swagger.json index 1a9053808..30ae5bae8 100644 --- a/cmd/server/docs/swagger.json +++ b/cmd/server/docs/swagger.json @@ -5254,7 +5254,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/request.FileOption" + "$ref": "#/definitions/request.FileContentReq" } } ], @@ -7069,6 +7069,31 @@ } } }, + "/hosts/command/tree": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "获取快速命令树", + "consumes": [ + "application/json" + ], + "tags": [ + "Command" + ], + "summary": "Tree commands", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "Array" + } + } + } + } + }, "/hosts/command/update": { "post": { "security": [ @@ -12726,6 +12751,12 @@ "command": { "type": "string" }, + "groupBelong": { + "type": "string" + }, + "groupID": { + "type": "integer" + }, "id": { "type": "integer" }, @@ -12744,6 +12775,12 @@ "command": { "type": "string" }, + "groupBelong": { + "type": "string" + }, + "groupID": { + "type": "integer" + }, "id": { "type": "integer" }, @@ -14859,7 +14896,6 @@ "dto.OperationWithNameAndType": { "type": "object", "required": [ - "name", "type" ], "properties": { @@ -15045,7 +15081,6 @@ "dto.RecordSearch": { "type": "object", "required": [ - "name", "page", "pageSize", "type" @@ -16456,6 +16491,9 @@ "pageSize" ], "properties": { + "all": { + "type": "boolean" + }, "name": { "type": "string" }, @@ -16645,6 +16683,17 @@ } } }, + "request.FileContentReq": { + "type": "object", + "required": [ + "path" + ], + "properties": { + "path": { + "type": "string" + } + } + }, "request.FileCreate": { "type": "object", "required": [ @@ -17815,9 +17864,6 @@ }, "request.WebsiteInstallCheckReq": { "type": "object", - "required": [ - "InstallIds" - ], "properties": { "InstallIds": { "type": "array", diff --git a/cmd/server/docs/swagger.yaml b/cmd/server/docs/swagger.yaml index aab8f9ee9..85a1bda72 100644 --- a/cmd/server/docs/swagger.yaml +++ b/cmd/server/docs/swagger.yaml @@ -206,6 +206,10 @@ definitions: properties: command: type: string + groupBelong: + type: string + groupID: + type: integer id: type: integer name: @@ -215,6 +219,10 @@ definitions: properties: command: type: string + groupBelong: + type: string + groupID: + type: integer id: type: integer name: @@ -1656,7 +1664,6 @@ definitions: type: type: string required: - - name - type type: object dto.Options: @@ -1790,7 +1797,6 @@ definitions: type: type: string required: - - name - page - pageSize - type @@ -2712,6 +2718,8 @@ definitions: type: object request.AppInstalledSearch: properties: + all: + type: boolean name: type: string page: @@ -2840,6 +2848,13 @@ definitions: - name - type type: object + request.FileContentReq: + properties: + path: + type: string + required: + - path + type: object request.FileCreate: properties: content: @@ -3632,8 +3647,6 @@ definitions: items: type: integer type: array - required: - - InstallIds type: object request.WebsiteLogReq: properties: @@ -7643,7 +7656,7 @@ paths: name: request required: true schema: - $ref: '#/definitions/request.FileOption' + $ref: '#/definitions/request.FileContentReq' responses: "200": description: OK @@ -8803,6 +8816,21 @@ paths: summary: Page commands tags: - Command + /hosts/command/tree: + get: + consumes: + - application/json + description: 获取快速命令树 + responses: + "200": + description: OK + schema: + type: Array + security: + - ApiKeyAuth: [] + summary: Tree commands + tags: + - Command /hosts/command/update: post: consumes: diff --git a/frontend/src/api/interface/command.ts b/frontend/src/api/interface/command.ts index 1f1c1ec8f..b399c43e0 100644 --- a/frontend/src/api/interface/command.ts +++ b/frontend/src/api/interface/command.ts @@ -4,11 +4,13 @@ export namespace Command { export interface CommandInfo { id: number; name: string; + groupID: number; command: string; } export interface CommandOperate { id: number; name: string; + groupID: number; command: string; } export interface CommandSearch extends ReqPage { diff --git a/frontend/src/api/modules/command.ts b/frontend/src/api/modules/command.ts deleted file mode 100644 index 8daf10dc1..000000000 --- a/frontend/src/api/modules/command.ts +++ /dev/null @@ -1,23 +0,0 @@ -import http from '@/api'; -import { ResPage } from '../interface'; -import { Command } from '../interface/command'; - -export const getCommandList = () => { - return http.get>(`/commands`, {}); -}; - -export const getCommandPage = (params: Command.CommandSearch) => { - return http.post>(`/commands/search`, params); -}; - -export const addCommand = (params: Command.CommandOperate) => { - return http.post(`/commands`, params); -}; - -export const editCommand = (params: Command.CommandOperate) => { - return http.post(`/commands/update`, params); -}; - -export const deleteCommand = (params: { ids: number[] }) => { - return http.post(`/commands/del`, params); -}; diff --git a/frontend/src/api/modules/host.ts b/frontend/src/api/modules/host.ts index 15a62ad46..12f4d54df 100644 --- a/frontend/src/api/modules/host.ts +++ b/frontend/src/api/modules/host.ts @@ -59,6 +59,9 @@ export const getCommandList = () => { export const getCommandPage = (params: Command.CommandSearch) => { return http.post>(`/hosts/command/search`, params); }; +export const getCommandTree = () => { + return http.get(`/hosts/command/tree`); +}; export const addCommand = (params: Command.CommandOperate) => { return http.post(`/hosts/command`, params); }; diff --git a/frontend/src/components/group/change.vue b/frontend/src/components/group/change.vue new file mode 100644 index 000000000..56cfb1957 --- /dev/null +++ b/frontend/src/components/group/change.vue @@ -0,0 +1,93 @@ + + + diff --git a/frontend/src/views/host/terminal/command/index.vue b/frontend/src/views/host/terminal/command/index.vue index 2d0e00a62..c07c3f192 100644 --- a/frontend/src/views/host/terminal/command/index.vue +++ b/frontend/src/views/host/terminal/command/index.vue @@ -8,10 +8,22 @@ {{ $t('commons.button.create') }}{{ $t('terminal.quickCommand') }} + + {{ $t('terminal.group') }} + {{ $t('commons.button.delete') }} + @@ -57,6 +76,13 @@ + + +
+ +
+
+
@@ -74,12 +100,16 @@ + +