diff --git a/backend/app.yaml b/backend/app.yaml index 3ea59f2c8..dfb1db868 100644 --- a/backend/app.yaml +++ b/backend/app.yaml @@ -37,6 +37,7 @@ sqlite: log: level: info + time_zone: Asia/Shanghai path: /opt/1Panel/log log_name: 1Panel log_suffix: .log diff --git a/backend/app/api/v1/command.go b/backend/app/api/v1/command.go new file mode 100644 index 000000000..cf3cfd705 --- /dev/null +++ b/backend/app/api/v1/command.go @@ -0,0 +1,98 @@ +package v1 + +import ( + "github.com/1Panel-dev/1Panel/app/api/v1/helper" + "github.com/1Panel-dev/1Panel/app/dto" + "github.com/1Panel-dev/1Panel/constant" + "github.com/1Panel-dev/1Panel/global" + "github.com/gin-gonic/gin" +) + +func (b *BaseApi) CreateCommand(c *gin.Context) { + var req dto.CommandCreate + if err := c.ShouldBindJSON(&req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + if err := global.VALID.Struct(req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + if err := commandService.Create(req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + helper.SuccessWithData(c, nil) +} + +func (b *BaseApi) SearchCommand(c *gin.Context) { + var req dto.SearchWithPage + if err := c.ShouldBindJSON(&req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + + total, list, err := commandService.SearchWithPage(req) + if err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + + helper.SuccessWithData(c, dto.PageResult{ + Items: list, + Total: total, + }) +} + +func (b *BaseApi) ListCommand(c *gin.Context) { + list, err := commandService.Search() + if err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + + helper.SuccessWithData(c, list) +} + +func (b *BaseApi) DeleteCommand(c *gin.Context) { + var req dto.DeleteByName + if err := c.ShouldBindJSON(&req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + if err := global.VALID.Struct(req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + + if err := commandService.Delete(req.Name); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + helper.SuccessWithData(c, nil) +} + +func (b *BaseApi) UpdateCommand(c *gin.Context) { + var req dto.CommandUpdate + if err := c.ShouldBindJSON(&req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + if err := global.VALID.Struct(req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + id, err := helper.GetParamID(c) + if err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + + upMap := make(map[string]interface{}) + upMap["name"] = req.Name + if err := commandService.Update(id, upMap); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + helper.SuccessWithData(c, nil) +} diff --git a/backend/app/api/v1/entry.go b/backend/app/api/v1/entry.go index 4eb49a8a9..60b75b664 100644 --- a/backend/app/api/v1/entry.go +++ b/backend/app/api/v1/entry.go @@ -11,5 +11,7 @@ var ApiGroupApp = new(ApiGroup) var ( userService = service.ServiceGroupApp.UserService hostService = service.ServiceGroupApp.HostService + groupService = service.ServiceGroupApp.GroupService + commandService = service.ServiceGroupApp.CommandService operationService = service.ServiceGroupApp.OperationService ) diff --git a/backend/app/api/v1/group.go b/backend/app/api/v1/group.go new file mode 100644 index 000000000..359c1ee3a --- /dev/null +++ b/backend/app/api/v1/group.go @@ -0,0 +1,79 @@ +package v1 + +import ( + "github.com/1Panel-dev/1Panel/app/api/v1/helper" + "github.com/1Panel-dev/1Panel/app/dto" + "github.com/1Panel-dev/1Panel/constant" + "github.com/1Panel-dev/1Panel/global" + "github.com/gin-gonic/gin" +) + +func (b *BaseApi) CreateGroup(c *gin.Context) { + var req dto.GroupCreate + if err := c.ShouldBindJSON(&req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + if err := global.VALID.Struct(req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + if err := groupService.Create(req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + helper.SuccessWithData(c, nil) +} + +func (b *BaseApi) DeleteGroup(c *gin.Context) { + var req dto.DeleteByName + if err := c.ShouldBindJSON(&req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + if err := global.VALID.Struct(req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + + if err := groupService.Delete(req.Name); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + helper.SuccessWithData(c, nil) +} + +func (b *BaseApi) UpdateGroup(c *gin.Context) { + var req dto.GroupUpdate + if err := c.ShouldBindJSON(&req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + if err := global.VALID.Struct(req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + id, err := helper.GetParamID(c) + if err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + + upMap := make(map[string]interface{}) + upMap["name"] = req.Name + if err := groupService.Update(id, upMap); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + helper.SuccessWithData(c, nil) +} + +func (b *BaseApi) ListGroup(c *gin.Context) { + list, err := groupService.Search() + if err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + + helper.SuccessWithData(c, list) +} diff --git a/backend/app/api/v1/host.go b/backend/app/api/v1/host.go index 73fdf872c..470736fc3 100644 --- a/backend/app/api/v1/host.go +++ b/backend/app/api/v1/host.go @@ -8,7 +8,7 @@ import ( "github.com/gin-gonic/gin" ) -func (b *BaseApi) Create(c *gin.Context) { +func (b *BaseApi) CreateHost(c *gin.Context) { var req dto.HostCreate if err := c.ShouldBindJSON(&req); err != nil { helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) @@ -26,23 +26,20 @@ func (b *BaseApi) Create(c *gin.Context) { helper.SuccessWithData(c, host) } -func (b *BaseApi) PageHosts(c *gin.Context) { - var req dto.SearchWithPage +func (b *BaseApi) HostTree(c *gin.Context) { + var req dto.SearchForTree if err := c.ShouldBindJSON(&req); err != nil { helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) return } - total, list, err := hostService.Search(req) + data, err := hostService.SearchForTree(req) if err != nil { helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) return } - helper.SuccessWithData(c, dto.PageResult{ - Items: list, - Total: total, - }) + helper.SuccessWithData(c, data) } func (b *BaseApi) DeleteHost(c *gin.Context) { @@ -81,6 +78,7 @@ func (b *BaseApi) UpdateHost(c *gin.Context) { upMap := make(map[string]interface{}) upMap["name"] = req.Name + upMap["group"] = req.Group upMap["addr"] = req.Addr upMap["port"] = req.Port upMap["user"] = req.User diff --git a/backend/app/api/v1/user.go b/backend/app/api/v1/user.go index ef7b2bac4..e67d3235b 100644 --- a/backend/app/api/v1/user.go +++ b/backend/app/api/v1/user.go @@ -68,7 +68,7 @@ func (b *BaseApi) Register(c *gin.Context) { } func (b *BaseApi) PageUsers(c *gin.Context) { - var req dto.UserPage + var req dto.SearchWithPage if err := c.ShouldBindJSON(&req); err != nil { helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) return diff --git a/backend/app/dto/command.go b/backend/app/dto/command.go new file mode 100644 index 000000000..1aafc5c42 --- /dev/null +++ b/backend/app/dto/command.go @@ -0,0 +1,15 @@ +package dto + +type CommandCreate struct { + Name string `json:"name" validate:"required"` + Command string `json:"command" validate:"required"` +} + +type CommandUpdate struct { + Name string `json:"name" validate:"required"` +} + +type CommandInfo struct { + Name string `json:"name"` + Command string `json:"command"` +} diff --git a/backend/app/dto/common_req.go b/backend/app/dto/common_req.go index 6b198dcb2..f484dcf42 100644 --- a/backend/app/dto/common_req.go +++ b/backend/app/dto/common_req.go @@ -1,5 +1,10 @@ package dto +type SearchWithPage struct { + PageInfo + Name string `json:"name" validate:"required"` +} + type PageInfo struct { Page int `json:"page" validate:"required,number"` PageSize int `json:"pageSize" validate:"required,number"` @@ -13,6 +18,10 @@ type BatchDeleteReq struct { Ids []uint `json:"ids" validate:"required"` } +type DeleteByName struct { + Name string `json:"name" validate:"required"` +} + type OperationWithNameAndType struct { Name string `json:"name" validate:"required"` Type string `json:"type" validate:"required"` diff --git a/backend/app/dto/group.go b/backend/app/dto/group.go new file mode 100644 index 000000000..1b373093c --- /dev/null +++ b/backend/app/dto/group.go @@ -0,0 +1,10 @@ +package dto + +type GroupCreate struct { + Name string `json:"name" validate:"required"` + Type string `json:"type" validate:"required"` +} + +type GroupUpdate struct { + Name string `json:"name" validate:"required"` +} diff --git a/backend/app/dto/host.go b/backend/app/dto/host.go index 8fb5baa30..3be6e5ad5 100644 --- a/backend/app/dto/host.go +++ b/backend/app/dto/host.go @@ -1,8 +1,11 @@ package dto -import "time" +import ( + "time" +) type HostCreate struct { + Group string `json:"group" validate:"required"` Name string `json:"name" validate:"required"` Addr string `json:"addr" validate:"required,ip"` Port uint `json:"port" validate:"required,number,max=65535,min=1"` @@ -14,14 +17,14 @@ type HostCreate struct { Description string `json:"description"` } -type SearchWithPage struct { - PageInfo +type SearchForTree struct { Info string `json:"info"` } type HostInfo struct { ID uint `json:"id"` CreatedAt time.Time `json:"createdAt"` + Group string `json:"group"` Name string `json:"name"` Addr string `json:"addr"` Port uint `json:"port"` @@ -31,7 +34,17 @@ type HostInfo struct { Description string `json:"description"` } +type HostTree struct { + Label string `json:"label"` + Children []TreeChild `json:"children"` +} + +type TreeChild struct { + Label string `json:"label"` +} + type HostUpdate struct { + Group string `json:"group" validate:"required"` Name string `json:"name" validate:"required"` Addr string `json:"addr" validate:"required,ip"` Port uint `json:"port" validate:"required,number,max=65535,min=1"` diff --git a/backend/app/dto/user.go b/backend/app/dto/user.go index 7d16283e8..cb4c2df6f 100644 --- a/backend/app/dto/user.go +++ b/backend/app/dto/user.go @@ -10,11 +10,6 @@ type UserCreate struct { Email string `json:"email" validate:"required,email"` } -type UserPage struct { - PageInfo - Name string `json:"name" validate:"required"` -} - type CaptchaResponse struct { CaptchaID string `json:"captchaID"` ImagePath string `json:"imagePath"` diff --git a/backend/app/model/command.go b/backend/app/model/command.go new file mode 100644 index 000000000..dd318ba7b --- /dev/null +++ b/backend/app/model/command.go @@ -0,0 +1,9 @@ +package model + +import "gorm.io/gorm" + +type Command struct { + gorm.Model + Name string `gorm:"type:varchar(64));unique;not null" json:"name"` + Command string `gorm:"type:varchar(256);unique;not null" json:"command"` +} diff --git a/backend/app/model/group.go b/backend/app/model/group.go new file mode 100644 index 000000000..ab60d50d9 --- /dev/null +++ b/backend/app/model/group.go @@ -0,0 +1,9 @@ +package model + +import "gorm.io/gorm" + +type Group struct { + gorm.Model + Name string `gorm:"type:varchar(64);not null" json:"name"` + Type string `gorm:"type:varchar(16);unique;not null" json:"type"` +} diff --git a/backend/app/model/host.go b/backend/app/model/host.go index ded37e07f..6b5c78d26 100644 --- a/backend/app/model/host.go +++ b/backend/app/model/host.go @@ -4,6 +4,7 @@ import "gorm.io/gorm" type Host struct { gorm.Model + Group string `gorm:"type:varchar(64);not null" json:"group"` Name string `gorm:"type:varchar(64);unique;not null" json:"name"` Addr string `gorm:"type:varchar(16);unique;not null" json:"addr"` Port int `gorm:"type:varchar(5);not null" json:"port"` diff --git a/backend/app/repo/command.go b/backend/app/repo/command.go new file mode 100644 index 000000000..1447bf58c --- /dev/null +++ b/backend/app/repo/command.go @@ -0,0 +1,80 @@ +package repo + +import ( + "github.com/1Panel-dev/1Panel/app/model" + "github.com/1Panel-dev/1Panel/global" + "gorm.io/gorm" +) + +type CommandRepo struct{} + +type ICommandRepo interface { + GetList(opts ...DBOption) ([]model.Command, error) + Page(limit, offset int, opts ...DBOption) (int64, []model.Command, error) + WithByInfo(info string) DBOption + Create(command *model.Command) error + Update(id uint, vars map[string]interface{}) error + Delete(opts ...DBOption) error +} + +func NewICommandService() ICommandRepo { + return &CommandRepo{} +} + +func (u *CommandRepo) Get(opts ...DBOption) (model.Command, error) { + var command model.Command + 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) { + var users []model.Command + db := global.DB.Model(&model.Command{}) + 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) { + var commands []model.Command + db := global.DB.Model(&model.Command{}) + for _, opt := range opts { + db = opt(db) + } + err := db.Find(&commands).Error + return commands, err +} + +func (c *CommandRepo) WithByInfo(info string) DBOption { + return func(g *gorm.DB) *gorm.DB { + if len(info) == 0 { + return g + } + infoStr := "%" + info + "%" + return g.Where("name LIKE ? OR addr LIKE ?", infoStr, infoStr) + } +} + +func (u *CommandRepo) Create(command *model.Command) error { + return global.DB.Create(command).Error +} + +func (u *CommandRepo) Update(id uint, vars map[string]interface{}) error { + return global.DB.Model(&model.Command{}).Where("id = ?", id).Updates(vars).Error +} + +func (u *CommandRepo) Delete(opts ...DBOption) error { + db := global.DB + for _, opt := range opts { + db = opt(db) + } + return db.Delete(&model.Command{}).Error +} diff --git a/backend/app/repo/entry.go b/backend/app/repo/entry.go index 8270ebe7c..084ed7ae8 100644 --- a/backend/app/repo/entry.go +++ b/backend/app/repo/entry.go @@ -3,6 +3,8 @@ package repo type RepoGroup struct { UserRepo HostRepo + GroupRepo + CommandRepo OperationRepo CommonRepo } diff --git a/backend/app/repo/group.go b/backend/app/repo/group.go new file mode 100644 index 000000000..0ba8e205c --- /dev/null +++ b/backend/app/repo/group.go @@ -0,0 +1,56 @@ +package repo + +import ( + "github.com/1Panel-dev/1Panel/app/model" + "github.com/1Panel-dev/1Panel/global" +) + +type GroupRepo struct{} + +type IGroupRepo interface { + Get(opts ...DBOption) (model.Group, error) + GetList(opts ...DBOption) ([]model.Group, error) + Create(group *model.Group) error + Update(id uint, vars map[string]interface{}) error + Delete(opts ...DBOption) error +} + +func NewIGroupService() IGroupRepo { + return &GroupRepo{} +} + +func (u *GroupRepo) Get(opts ...DBOption) (model.Group, error) { + var group model.Group + db := global.DB + for _, opt := range opts { + db = opt(db) + } + err := db.First(&group).Error + return group, err +} + +func (u *GroupRepo) GetList(opts ...DBOption) ([]model.Group, error) { + var groups []model.Group + db := global.DB.Model(&model.Group{}) + for _, opt := range opts { + db = opt(db) + } + err := db.Find(&groups).Error + return groups, err +} + +func (u *GroupRepo) Create(group *model.Group) error { + return global.DB.Create(group).Error +} + +func (u *GroupRepo) Update(id uint, vars map[string]interface{}) error { + return global.DB.Model(&model.Group{}).Where("id = ?", id).Updates(vars).Error +} + +func (u *GroupRepo) Delete(opts ...DBOption) error { + db := global.DB + for _, opt := range opts { + db = opt(db) + } + return db.Delete(&model.Group{}).Error +} diff --git a/backend/app/repo/host.go b/backend/app/repo/host.go index dec22e81c..db0c10a6e 100644 --- a/backend/app/repo/host.go +++ b/backend/app/repo/host.go @@ -10,7 +10,7 @@ type HostRepo struct{} type IHostRepo interface { Get(opts ...DBOption) (model.Host, error) - Page(limit, offset int, opts ...DBOption) (int64, []model.Host, error) + GetList(opts ...DBOption) ([]model.Host, error) WithByInfo(info string) DBOption Create(host *model.Host) error Update(id uint, vars map[string]interface{}) error @@ -31,20 +31,21 @@ func (u *HostRepo) Get(opts ...DBOption) (model.Host, error) { return host, err } -func (u *HostRepo) Page(page, size int, opts ...DBOption) (int64, []model.Host, error) { +func (u *HostRepo) GetList(opts ...DBOption) ([]model.Host, error) { var hosts []model.Host db := global.DB.Model(&model.Host{}) for _, opt := range opts { db = opt(db) } - count := int64(0) - db = db.Count(&count) - err := db.Limit(size).Offset(size * (page - 1)).Find(&hosts).Error - return count, hosts, err + err := db.Find(&hosts).Error + return hosts, err } func (c *HostRepo) WithByInfo(info string) DBOption { return func(g *gorm.DB) *gorm.DB { + if len(info) == 0 { + return g + } infoStr := "%" + info + "%" return g.Where("name LIKE ? OR addr LIKE ?", infoStr, infoStr) } diff --git a/backend/app/service/command.go b/backend/app/service/command.go new file mode 100644 index 000000000..107f10d03 --- /dev/null +++ b/backend/app/service/command.go @@ -0,0 +1,70 @@ +package service + +import ( + "github.com/1Panel-dev/1Panel/app/dto" + "github.com/1Panel-dev/1Panel/app/model" + "github.com/1Panel-dev/1Panel/constant" + "github.com/jinzhu/copier" + "github.com/pkg/errors" +) + +type CommandService struct{} + +type ICommandService interface { + Search() ([]model.Command, error) + SearchWithPage(search dto.SearchWithPage) (int64, interface{}, error) + Create(commandDto dto.CommandCreate) error + Update(id uint, upMap map[string]interface{}) error + Delete(name string) error +} + +func NewICommandService() ICommandService { + return &CommandService{} +} + +func (u *CommandService) Search() ([]model.Command, error) { + commands, err := commandRepo.GetList() + if err != nil { + return nil, constant.ErrRecordNotFound + } + return commands, err +} + +func (u *CommandService) SearchWithPage(search dto.SearchWithPage) (int64, interface{}, error) { + total, commands, err := commandRepo.Page(search.Page, search.PageSize, commonRepo.WithLikeName(search.Name)) + 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()) + } + dtoCommands = append(dtoCommands, item) + } + return total, dtoCommands, err +} + +func (u *CommandService) Create(commandDto dto.CommandCreate) error { + command, _ := commandRepo.Get(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.Create(&command); err != nil { + return err + } + return nil +} + +func (u *CommandService) Delete(name string) error { + command, _ := commandRepo.Get(commonRepo.WithByName(name)) + if command.ID == 0 { + return constant.ErrRecordNotFound + } + return commandRepo.Delete(commonRepo.WithByID(command.ID)) +} + +func (u *CommandService) Update(id uint, upMap map[string]interface{}) error { + return commandRepo.Update(id, upMap) +} diff --git a/backend/app/service/entry.go b/backend/app/service/entry.go index 6c1b40fd4..b1ba8e5b4 100644 --- a/backend/app/service/entry.go +++ b/backend/app/service/entry.go @@ -5,6 +5,8 @@ import "github.com/1Panel-dev/1Panel/app/repo" type ServiceGroup struct { UserService HostService + GroupService + CommandService OperationService } @@ -13,6 +15,8 @@ var ServiceGroupApp = new(ServiceGroup) var ( userRepo = repo.RepoGroupApp.UserRepo hostRepo = repo.RepoGroupApp.HostRepo + groupRepo = repo.RepoGroupApp.GroupRepo + commandRepo = repo.RepoGroupApp.CommandRepo operationRepo = repo.RepoGroupApp.OperationRepo commonRepo = repo.RepoGroupApp.CommonRepo ) diff --git a/backend/app/service/group.go b/backend/app/service/group.go new file mode 100644 index 000000000..1cb254880 --- /dev/null +++ b/backend/app/service/group.go @@ -0,0 +1,56 @@ +package service + +import ( + "github.com/1Panel-dev/1Panel/app/dto" + "github.com/1Panel-dev/1Panel/app/model" + "github.com/1Panel-dev/1Panel/constant" + "github.com/jinzhu/copier" + "github.com/pkg/errors" +) + +type GroupService struct{} + +type IGroupService interface { + Search() ([]model.Group, error) + Create(groupDto dto.GroupCreate) error + Update(id uint, upMap map[string]interface{}) error + Delete(name string) error +} + +func NewIGroupService() IGroupService { + return &GroupService{} +} + +func (u *GroupService) Search() ([]model.Group, error) { + groups, err := groupRepo.GetList() + if err != nil { + return nil, constant.ErrRecordNotFound + } + return groups, err +} + +func (u *GroupService) Create(groupDto dto.GroupCreate) error { + group, _ := groupRepo.Get(commonRepo.WithByName(groupDto.Name), commonRepo.WithByName(groupDto.Name)) + if group.ID != 0 { + return constant.ErrRecordExist + } + if err := copier.Copy(&group, &groupDto); err != nil { + return errors.WithMessage(constant.ErrStructTransform, err.Error()) + } + if err := groupRepo.Create(&group); err != nil { + return err + } + return nil +} + +func (u *GroupService) Delete(name string) error { + group, _ := groupRepo.Get(commonRepo.WithByName(name)) + if group.ID == 0 { + return constant.ErrRecordNotFound + } + return groupRepo.Delete(commonRepo.WithByID(group.ID)) +} + +func (u *GroupService) Update(id uint, upMap map[string]interface{}) error { + return groupRepo.Update(id, upMap) +} diff --git a/backend/app/service/host.go b/backend/app/service/host.go index b3547c4f5..1ed597c7b 100644 --- a/backend/app/service/host.go +++ b/backend/app/service/host.go @@ -1,6 +1,8 @@ package service import ( + "fmt" + "github.com/1Panel-dev/1Panel/app/dto" "github.com/1Panel-dev/1Panel/app/model" "github.com/1Panel-dev/1Panel/constant" @@ -12,7 +14,7 @@ type HostService struct{} type IHostService interface { GetConnInfo(id uint) (*model.Host, error) - Search(search dto.SearchWithPage) (int64, interface{}, error) + SearchForTree(search dto.SearchForTree) ([]dto.HostTree, error) Create(hostDto dto.HostCreate) (*dto.HostInfo, error) Update(id uint, upMap map[string]interface{}) error BatchDelete(ids []uint) error @@ -30,17 +32,25 @@ func (u *HostService) GetConnInfo(id uint) (*model.Host, error) { return &host, err } -func (u *HostService) Search(search dto.SearchWithPage) (int64, interface{}, error) { - total, hosts, err := hostRepo.Page(search.Page, search.PageSize, hostRepo.WithByInfo(search.Info)) - var dtoHosts []dto.HostInfo +func (u *HostService) SearchForTree(search dto.SearchForTree) ([]dto.HostTree, error) { + hosts, err := hostRepo.GetList(hostRepo.WithByInfo(search.Info)) + distinctMap := make(map[string][]string) for _, host := range hosts { - var item dto.HostInfo - if err := copier.Copy(&item, &host); err != nil { - return 0, nil, errors.WithMessage(constant.ErrStructTransform, err.Error()) + if _, ok := distinctMap[host.Group]; !ok { + distinctMap[host.Group] = []string{fmt.Sprintf("%s@%s:%d", host.User, host.Addr, host.Port)} + } else { + distinctMap[host.Group] = append(distinctMap[host.Group], fmt.Sprintf("%s@%s:%d", host.User, host.Addr, host.Port)) } - dtoHosts = append(dtoHosts, item) } - return total, dtoHosts, err + var data []dto.HostTree + for key, value := range distinctMap { + var children []dto.TreeChild + for _, label := range value { + children = append(children, dto.TreeChild{Label: label}) + } + data = append(data, dto.HostTree{Label: key, Children: children}) + } + return data, err } func (u *HostService) Create(hostDto dto.HostCreate) (*dto.HostInfo, error) { diff --git a/backend/app/service/user.go b/backend/app/service/user.go index 3dd297e89..a1af9630e 100644 --- a/backend/app/service/user.go +++ b/backend/app/service/user.go @@ -5,7 +5,6 @@ import ( "github.com/1Panel-dev/1Panel/app/model" "github.com/1Panel-dev/1Panel/constant" "github.com/1Panel-dev/1Panel/global" - "github.com/1Panel-dev/1Panel/init/session/psession" "github.com/1Panel-dev/1Panel/utils/encrypt" "github.com/1Panel-dev/1Panel/utils/jwt" "github.com/gin-gonic/gin" @@ -18,7 +17,7 @@ type UserService struct{} type IUserService interface { Get(id uint) (*dto.UserInfo, error) - Page(search dto.UserPage) (int64, interface{}, error) + Page(search dto.SearchWithPage) (int64, interface{}, error) Register(userDto dto.UserCreate) error Login(c *gin.Context, info dto.Login) (*dto.UserLoginInfo, error) LogOut(c *gin.Context) error @@ -44,7 +43,7 @@ func (u *UserService) Get(id uint) (*dto.UserInfo, error) { return &dtoUser, err } -func (u *UserService) Page(search dto.UserPage) (int64, interface{}, error) { +func (u *UserService) Page(search dto.SearchWithPage) (int64, interface{}, error) { total, users, err := userRepo.Page(search.Page, search.PageSize, commonRepo.WithLikeName(search.Name)) var dtoUsers []dto.UserInfo for _, user := range users { @@ -92,13 +91,9 @@ func (u *UserService) Login(c *gin.Context, info dto.Login) (*dto.UserLoginInfo, } return &dto.UserLoginInfo{Name: user.Name, Token: token}, err } - sessionUser := psession.SessionUser{ - ID: user.ID, - Name: user.Name, - } lifeTime := global.CONF.Session.ExpiresTime sID, _ := c.Cookie(global.CONF.Session.SessionName) - sessionUser, err = global.SESSION.Get(sID) + sessionUser, err := global.SESSION.Get(sID) if err != nil { sID = uuid.NewV4().String() c.SetCookie(global.CONF.Session.SessionName, sID, lifeTime, "", "", false, false) diff --git a/backend/cmd/gotty/gotty b/backend/cmd/gotty/gotty deleted file mode 100755 index b56d2334e..000000000 Binary files a/backend/cmd/gotty/gotty and /dev/null differ diff --git a/backend/configs/log.go b/backend/configs/log.go index b2bf121e7..4fba3b48a 100644 --- a/backend/configs/log.go +++ b/backend/configs/log.go @@ -2,6 +2,7 @@ package configs type LogConfig struct { Level string `mapstructure:"level"` + TimeZone string `mapstructure:"timeZone"` Path string `mapstructure:"path"` LogName string `mapstructure:"log_name"` LogSuffix string `mapstructure:"log_suffix"` diff --git a/backend/init/log/log.go b/backend/init/log/log.go index dbdca0797..c37e0025b 100644 --- a/backend/init/log/log.go +++ b/backend/init/log/log.go @@ -1,7 +1,10 @@ package log import ( + "fmt" "path" + "strings" + "time" "github.com/1Panel-dev/1Panel/configs" "github.com/1Panel-dev/1Panel/global" @@ -20,10 +23,10 @@ func setOutput(log *logrus.Logger, config configs.LogConfig) { filePath := path.Join(config.Path, config.LogName+config.LogSuffix) logPrint := &lumberjack.Logger{ Filename: filePath, - MaxSize: config.LogSize, // 日志文件大小,单位是 MB - MaxBackups: config.LogBackup, // 最大过期日志保留个数 - MaxAge: config.LogData, // 保留过期文件最大时间,单位 天 - Compress: true, // 是否压缩日志,默认是不压缩。这里设置为true,压缩日志 + MaxSize: config.LogSize, + MaxBackups: config.LogBackup, + MaxAge: config.LogData, + Compress: true, } level, err := logrus.ParseLevel(config.Level) if err != nil { @@ -31,4 +34,24 @@ func setOutput(log *logrus.Logger, config configs.LogConfig) { } log.SetOutput(logPrint) log.SetLevel(level) + log.SetFormatter(new(MineFormatter)) +} + +type MineFormatter struct{} + +const TimeFormat = "2006-01-02 15:04:05" + +func (s *MineFormatter) Format(entry *logrus.Entry) ([]byte, error) { + var cstSh, _ = time.LoadLocation(global.CONF.LogConfig.TimeZone) + detailInfo := "" + if entry.Caller != nil { + funcion := strings.ReplaceAll(entry.Caller.Function, "github.com/1Panel-dev/1Panel/", "") + detailInfo = fmt.Sprintf("(%s: %d)", funcion, entry.Caller.Line) + } + if len(entry.Data) == 0 { + msg := fmt.Sprintf("[%s] [%s] %s %s \n", time.Now().In(cstSh).Format(TimeFormat), strings.ToUpper(entry.Level.String()), entry.Message, detailInfo) + return []byte(msg), nil + } + msg := fmt.Sprintf("[%s] [%s] %s %s {%v} \n", time.Now().In(cstSh).Format(TimeFormat), strings.ToUpper(entry.Level.String()), entry.Message, detailInfo, entry.Data) + return []byte(msg), nil } diff --git a/backend/init/migration/migrate.go b/backend/init/migration/migrate.go index 6c34a30cf..46e818141 100644 --- a/backend/init/migration/migrate.go +++ b/backend/init/migration/migrate.go @@ -18,5 +18,5 @@ func Init() { global.LOG.Error(err) panic(err) } - global.LOG.Infof("Migration did run successfully") + global.LOG.Info("Migration did run successfully") } diff --git a/backend/router/command.go b/backend/router/command.go new file mode 100644 index 000000000..e5778ed87 --- /dev/null +++ b/backend/router/command.go @@ -0,0 +1,24 @@ +package router + +import ( + v1 "github.com/1Panel-dev/1Panel/app/api/v1" + "github.com/1Panel-dev/1Panel/middleware" + + "github.com/gin-gonic/gin" +) + +type CommandRouter struct{} + +func (s *CommandRouter) InitCommandRouter(Router *gin.RouterGroup) { + userRouter := Router.Group("commands") + userRouter.Use(middleware.JwtAuth()).Use(middleware.SessionAuth()) + withRecordRouter := userRouter.Use(middleware.OperationRecord()) + baseApi := v1.ApiGroupApp.BaseApi + { + withRecordRouter.POST("", baseApi.CreateCommand) + withRecordRouter.POST("/del", baseApi.DeleteCommand) + userRouter.POST("/search", baseApi.SearchCommand) + userRouter.GET("", baseApi.ListCommand) + userRouter.PUT(":id", baseApi.UpdateCommand) + } +} diff --git a/backend/router/group.go b/backend/router/group.go new file mode 100644 index 000000000..c63212200 --- /dev/null +++ b/backend/router/group.go @@ -0,0 +1,23 @@ +package router + +import ( + v1 "github.com/1Panel-dev/1Panel/app/api/v1" + "github.com/1Panel-dev/1Panel/middleware" + + "github.com/gin-gonic/gin" +) + +type GroupRouter struct{} + +func (s *GroupRouter) InitGroupRouter(Router *gin.RouterGroup) { + userRouter := Router.Group("group") + userRouter.Use(middleware.JwtAuth()).Use(middleware.SessionAuth()) + withRecordRouter := userRouter.Use(middleware.OperationRecord()) + baseApi := v1.ApiGroupApp.BaseApi + { + withRecordRouter.POST("", baseApi.CreateGroup) + withRecordRouter.POST("/del", baseApi.DeleteGroup) + userRouter.GET("", baseApi.ListGroup) + userRouter.PUT(":id", baseApi.UpdateGroup) + } +} diff --git a/backend/router/ro_host.go b/backend/router/ro_host.go index a7983937f..ef3da773f 100644 --- a/backend/router/ro_host.go +++ b/backend/router/ro_host.go @@ -15,9 +15,9 @@ func (s *HostRouter) InitHostRouter(Router *gin.RouterGroup) { withRecordRouter := userRouter.Use(middleware.OperationRecord()) baseApi := v1.ApiGroupApp.BaseApi { - withRecordRouter.POST("", baseApi.Create) + withRecordRouter.POST("", baseApi.CreateHost) withRecordRouter.POST("/del", baseApi.DeleteHost) - userRouter.POST("/search", baseApi.PageHosts) + userRouter.POST("/search", baseApi.HostTree) userRouter.PUT(":id", baseApi.UpdateHost) } } diff --git a/backend/utils/terminal/ws_local_session.go b/backend/utils/terminal/ws_local_session.go index dc95838dc..538a3dd38 100644 --- a/backend/utils/terminal/ws_local_session.go +++ b/backend/utils/terminal/ws_local_session.go @@ -80,9 +80,7 @@ func (sws *LocalWsSession) receiveWsMsg(exitCh chan bool) { return } msgObj := wsMsg{} - if err := json.Unmarshal(wsData, &msgObj); err != nil { - global.LOG.Errorf("unmarshal websocket message %s failed, err: %v", wsData, err) - } + _ = json.Unmarshal(wsData, &msgObj) switch msgObj.Type { case wsMsgResize: if msgObj.Cols > 0 && msgObj.Rows > 0 { diff --git a/backend/utils/terminal/ws_session.go b/backend/utils/terminal/ws_session.go index 4fb33a335..9b9139602 100644 --- a/backend/utils/terminal/ws_session.go +++ b/backend/utils/terminal/ws_session.go @@ -54,7 +54,7 @@ type LogicSshWsSession struct { session *ssh.Session wsConn *websocket.Conn isAdmin bool - IsFlagged bool `comment:"当前session是否包含禁止命令"` + IsFlagged bool } func NewLogicSshWsSession(cols, rows int, isAdmin bool, sshClient *ssh.Client, wsConn *websocket.Conn) (*LogicSshWsSession, error) { @@ -127,9 +127,7 @@ func (sws *LogicSshWsSession) receiveWsMsg(exitCh chan bool) { return } msgObj := wsMsg{} - if err := json.Unmarshal(wsData, &msgObj); err != nil { - global.LOG.Errorf("unmarshal websocket message %s failed, err: %v", wsData, err) - } + _ = json.Unmarshal(wsData, &msgObj) switch msgObj.Type { case wsMsgResize: if msgObj.Cols > 0 && msgObj.Rows > 0 { diff --git a/frontend/src/api/interface/host.ts b/frontend/src/api/interface/host.ts index c3ccee7fa..7dd2fc887 100644 --- a/frontend/src/api/interface/host.ts +++ b/frontend/src/api/interface/host.ts @@ -1,6 +1,10 @@ -import { CommonModel, ReqPage } from '.'; +import { CommonModel } from '.'; export namespace Host { + export interface HostTree { + label: string; + children: Array; + } export interface Host extends CommonModel { name: string; addr: string; @@ -21,7 +25,7 @@ export namespace Host { description: string; } - export interface ReqSearchWithPage extends ReqPage { + export interface ReqSearch { info?: string; } } diff --git a/frontend/src/api/modules/host.ts b/frontend/src/api/modules/host.ts index 66cf601ca..75e0d1e2e 100644 --- a/frontend/src/api/modules/host.ts +++ b/frontend/src/api/modules/host.ts @@ -1,9 +1,8 @@ import http from '@/api'; -import { ResPage } from '../interface'; import { Host } from '../interface/host'; -export const getHostList = (params: Host.ReqSearchWithPage) => { - return http.post>(`/hosts/search`, params); +export const getHostList = (params: Host.ReqSearch) => { + return http.post>(`/hosts/search`, params); }; export const addHost = (params: Host.HostOperate) => { diff --git a/frontend/src/api/modules/operation-log.ts b/frontend/src/api/modules/operation-log.ts index 38aa38d75..539591916 100644 --- a/frontend/src/api/modules/operation-log.ts +++ b/frontend/src/api/modules/operation-log.ts @@ -5,7 +5,3 @@ import { ResOperationLog } from '../interface/operation-log'; export const getOperationList = (info: ReqPage) => { return http.post>(`/operations`, info); }; - -export const deleteOperation = (params: { ids: number[] }) => { - return http.post(`/operations/del`, params); -}; diff --git a/frontend/src/lang/modules/zh.ts b/frontend/src/lang/modules/zh.ts index 7cb142f60..89f55eb69 100644 --- a/frontend/src/lang/modules/zh.ts +++ b/frontend/src/lang/modules/zh.ts @@ -91,8 +91,9 @@ export default { logout: '退出登录', }, terminal: { - connHistory: '历史连接', - hostHistory: '历史主机信息', + conn: '连接', + hostList: '主机信息', + quickCmd: '快捷命令', addHost: '添加主机', localhost: '本地服务器', name: '名称', diff --git a/frontend/src/routers/modules/demo.ts b/frontend/src/routers/modules/demo.ts index 3b4e84e02..ad2b7c862 100644 --- a/frontend/src/routers/modules/demo.ts +++ b/frontend/src/routers/modules/demo.ts @@ -15,9 +15,6 @@ const demoRouter = { path: '/demos/table', name: 'Table', component: () => import('@/views/demos/table/index.vue'), - meta: { - keepAlive: true, - }, }, { path: '/demos/table/:op/:id?', @@ -27,7 +24,6 @@ const demoRouter = { component: () => import('@/views/demos/table/operate/index.vue'), meta: { activeMenu: '/demos/table', - keepAlive: true, }, }, ], diff --git a/frontend/src/routers/modules/operation-log.ts b/frontend/src/routers/modules/operation-log.ts index 61bc1248a..1b6562c07 100644 --- a/frontend/src/routers/modules/operation-log.ts +++ b/frontend/src/routers/modules/operation-log.ts @@ -15,7 +15,6 @@ const operationRouter = { name: 'OperationLog', component: () => import('@/views/operation-log/index.vue'), meta: { - keepAlive: true, requiresAuth: true, key: 'OperationLog', }, diff --git a/frontend/src/routers/modules/terminal.ts b/frontend/src/routers/modules/terminal.ts index 08133bb4f..3042d10ba 100644 --- a/frontend/src/routers/modules/terminal.ts +++ b/frontend/src/routers/modules/terminal.ts @@ -1,4 +1,5 @@ import { Layout } from '@/routers/constant'; +import i18n from '@/lang'; const terminalRouter = { sort: 2, @@ -6,18 +7,44 @@ const terminalRouter = { component: Layout, redirect: '/terminal', meta: { - title: 'menu.terminal', + title: i18n.global.t('menu.terminal'), icon: 'monitor', }, children: [ { - path: '/terminal', + path: '/terminals/terminal', name: 'Terminal', component: () => import('@/views/terminal/index.vue'), meta: { - keepAlive: true, requiresAuth: true, key: 'Terminal', + title: i18n.global.t('terminal.conn'), + icon: 'connection', + activeMenu: '/terminals', + }, + }, + { + path: '/terminals/host', + name: 'Host', + component: () => import('@/views/terminal/host/index.vue'), + meta: { + requiresAuth: true, + key: 'Host', + title: i18n.global.t('terminal.hostList'), + icon: 'platform', + activeMenu: '/terminals', + }, + }, + { + path: '/terminals/command', + name: 'Command', + component: () => import('@/views/terminal/command/index.vue'), + meta: { + requiresAuth: true, + key: 'Command', + title: i18n.global.t('terminal.quickCmd'), + icon: 'reading', + activeMenu: '/terminals', }, }, ], diff --git a/frontend/src/styles/element.scss b/frontend/src/styles/element.scss index d133be3e7..e67c6228d 100644 --- a/frontend/src/styles/element.scss +++ b/frontend/src/styles/element.scss @@ -178,3 +178,15 @@ } border-radius: 5px; } + +.row-box { + display: flex; + flex-flow: wrap; + } +.row-box .el-card { + min-width: 100%; + height: 100%; + margin-right: 20px; + border: 0; + // box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%); +} \ No newline at end of file diff --git a/frontend/src/views/operation-log/index.vue b/frontend/src/views/operation-log/index.vue index 1e209466e..67ebbbf13 100644 --- a/frontend/src/views/operation-log/index.vue +++ b/frontend/src/views/operation-log/index.vue @@ -1,80 +1,74 @@ diff --git a/frontend/src/views/terminal/index.vue b/frontend/src/views/terminal/index.vue index 186cc767a..6c4528b35 100644 --- a/frontend/src/views/terminal/index.vue +++ b/frontend/src/views/terminal/index.vue @@ -1,173 +1,189 @@