diff --git a/backend/app/api/v1/entry.go b/backend/app/api/v1/entry.go index bfc3aa075..4eb49a8a9 100644 --- a/backend/app/api/v1/entry.go +++ b/backend/app/api/v1/entry.go @@ -10,5 +10,6 @@ var ApiGroupApp = new(ApiGroup) var ( userService = service.ServiceGroupApp.UserService + hostService = service.ServiceGroupApp.HostService operationService = service.ServiceGroupApp.OperationService ) diff --git a/backend/app/api/v1/host.go b/backend/app/api/v1/host.go new file mode 100644 index 000000000..92d29586e --- /dev/null +++ b/backend/app/api/v1/host.go @@ -0,0 +1,95 @@ +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) Create(c *gin.Context) { + var req dto.HostCreate + 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 + } + host, err := hostService.Create(req) + if err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + helper.SuccessWithData(c, host) +} + +func (b *BaseApi) PageHosts(c *gin.Context) { + var req dto.PageInfo + if err := c.ShouldBindJSON(&req); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err) + return + } + + total, list, err := hostService.Page(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) DeleteHost(c *gin.Context) { + var req dto.BatchDeleteReq + 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 := hostService.BatchDelete(req.Ids); err != nil { + helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err) + return + } + helper.SuccessWithData(c, nil) +} + +func (b *BaseApi) UpdateHost(c *gin.Context) { + var req dto.HostUpdate + 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 + upMap["addr"] = req.Addr + upMap["port"] = req.Port + upMap["user"] = req.User + upMap["auth_mode"] = req.AuthMode + upMap["password"] = req.Password + upMap["private_key"] = req.PrivateKey + if err := hostService.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/terminal.go b/backend/app/api/v1/terminal.go index cff584ac3..295754682 100644 --- a/backend/app/api/v1/terminal.go +++ b/backend/app/api/v1/terminal.go @@ -6,6 +6,7 @@ import ( "time" "github.com/1Panel-dev/1Panel/global" + "github.com/1Panel-dev/1Panel/utils/copier" "github.com/1Panel-dev/1Panel/utils/ssh" "github.com/1Panel-dev/1Panel/utils/terminal" "github.com/gin-gonic/gin" @@ -13,14 +14,6 @@ import ( ) func (b *BaseApi) WsSsh(c *gin.Context) { - host := ssh.ConnInfo{ - Addr: "172.16.10.111", - Port: 22, - User: "root", - AuthMode: "password", - Password: "Calong@2015", - } - wsConn, err := upGrader.Upgrade(c.Writer, c.Request, nil) if err != nil { global.LOG.Errorf("gin context http handler failed, err: %v", err) @@ -28,6 +21,20 @@ func (b *BaseApi) WsSsh(c *gin.Context) { } defer wsConn.Close() + id, err := strconv.Atoi(c.Query("id")) + if wshandleError(wsConn, err) { + return + } + host, err := hostService.GetConnInfo(uint(id)) + if wshandleError(wsConn, err) { + return + } + var connInfo ssh.ConnInfo + err = copier.Copy(&connInfo, &host) + if wshandleError(wsConn, err) { + return + } + cols, err := strconv.Atoi(c.DefaultQuery("cols", "80")) if wshandleError(wsConn, err) { return @@ -37,18 +44,18 @@ func (b *BaseApi) WsSsh(c *gin.Context) { return } - client, err := host.NewClient() + client, err := connInfo.NewClient() if wshandleError(wsConn, err) { return } defer client.Close() - ssConn, err := host.NewSshConn(cols, rows) + ssConn, err := connInfo.NewSshConn(cols, rows) if wshandleError(wsConn, err) { return } defer ssConn.Close() - sws, err := terminal.NewLogicSshWsSession(cols, rows, true, host.Client, wsConn) + sws, err := terminal.NewLogicSshWsSession(cols, rows, true, connInfo.Client, wsConn) if wshandleError(wsConn, err) { return } diff --git a/backend/app/dto/host.go b/backend/app/dto/host.go new file mode 100644 index 000000000..d6b192d6c --- /dev/null +++ b/backend/app/dto/host.go @@ -0,0 +1,39 @@ +package dto + +import "time" + +type HostCreate struct { + Name string `json:"name" validate:"required,name"` + Addr string `json:"addr" validate:"required,ip"` + Port uint `json:"port" validate:"required,number,max=65535,min=1"` + User string `json:"user" validate:"required"` + AuthMode string `json:"authMode" validate:"oneof=password key"` + PrivateKey string `json:"privateKey"` + Password string `json:"password"` + + Description string `json:"description"` +} + +type HostInfo struct { + ID uint `json:"id"` + CreatedAt time.Time `json:"createdAt"` + Name string `json:"name"` + Addr string `json:"addr"` + Port uint `json:"port"` + User string `json:"user"` + AuthMode string `json:"authMode"` + + Description string `json:"description"` +} + +type HostUpdate struct { + Name string `json:"name" validate:"required,name"` + Addr string `json:"addr" validate:"required,ip"` + Port uint `json:"port" validate:"required,number,max=65535,min=1"` + User string `json:"user" validate:"required"` + AuthMode string `json:"authMode" validate:"oneof=password key"` + PrivateKey string `json:"privateKey"` + Password string `json:"password"` + + Description string `json:"description"` +} diff --git a/backend/app/dto/user.go b/backend/app/dto/user.go index 01008e474..7d16283e8 100644 --- a/backend/app/dto/user.go +++ b/backend/app/dto/user.go @@ -26,7 +26,7 @@ type UserUpdate struct { Email string `json:"email" validate:"required,email"` } -type UserBack struct { +type UserInfo struct { ID uint `json:"id"` Name string `json:"name"` Email string `json:"email"` diff --git a/backend/app/model/host.go b/backend/app/model/host.go new file mode 100644 index 000000000..ded37e07f --- /dev/null +++ b/backend/app/model/host.go @@ -0,0 +1,16 @@ +package model + +import "gorm.io/gorm" + +type Host struct { + gorm.Model + 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"` + User string `gorm:"type:varchar(64);not null" json:"user"` + AuthMode string `gorm:"type:varchar(16);not null" json:"authMode"` + Password string `gorm:"type:varchar(64)" json:"password"` + PrivateKey string `gorm:"type:varchar(256)" json:"privateKey"` + + Description string `gorm:"type:varchar(256)" json:"description"` +} diff --git a/backend/app/repo/entry.go b/backend/app/repo/entry.go index e59eb58bb..8270ebe7c 100644 --- a/backend/app/repo/entry.go +++ b/backend/app/repo/entry.go @@ -2,6 +2,7 @@ package repo type RepoGroup struct { UserRepo + HostRepo OperationRepo CommonRepo } diff --git a/backend/app/repo/host.go b/backend/app/repo/host.go new file mode 100644 index 000000000..403a59dde --- /dev/null +++ b/backend/app/repo/host.go @@ -0,0 +1,58 @@ +package repo + +import ( + "github.com/1Panel-dev/1Panel/app/model" + "github.com/1Panel-dev/1Panel/global" +) + +type HostRepo struct{} + +type IHostRepo interface { + Get(opts ...DBOption) (model.Host, error) + Page(limit, offset int, opts ...DBOption) (int64, []model.Host, error) + Create(host *model.Host) error + Update(id uint, vars map[string]interface{}) error + Delete(opts ...DBOption) error +} + +func NewIHostService() IHostRepo { + return &HostRepo{} +} + +func (u *HostRepo) Get(opts ...DBOption) (model.Host, error) { + var host model.Host + db := global.DB + for _, opt := range opts { + db = opt(db) + } + err := db.First(&host).Error + return host, err +} + +func (u *HostRepo) Page(page, size int, opts ...DBOption) (int64, []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 +} + +func (u *HostRepo) Create(host *model.Host) error { + return global.DB.Create(host).Error +} + +func (u *HostRepo) Update(id uint, vars map[string]interface{}) error { + return global.DB.Model(&model.Host{}).Where("id = ?", id).Updates(vars).Error +} + +func (u *HostRepo) Delete(opts ...DBOption) error { + db := global.DB + for _, opt := range opts { + db = opt(db) + } + return db.Delete(&model.Host{}).Error +} diff --git a/backend/app/service/entry.go b/backend/app/service/entry.go index fdabc3884..6c1b40fd4 100644 --- a/backend/app/service/entry.go +++ b/backend/app/service/entry.go @@ -4,6 +4,7 @@ import "github.com/1Panel-dev/1Panel/app/repo" type ServiceGroup struct { UserService + HostService OperationService } @@ -11,6 +12,7 @@ var ServiceGroupApp = new(ServiceGroup) var ( userRepo = repo.RepoGroupApp.UserRepo + hostRepo = repo.RepoGroupApp.HostRepo operationRepo = repo.RepoGroupApp.OperationRepo commonRepo = repo.RepoGroupApp.CommonRepo ) diff --git a/backend/app/service/host.go b/backend/app/service/host.go new file mode 100644 index 000000000..2afa1ef2d --- /dev/null +++ b/backend/app/service/host.go @@ -0,0 +1,76 @@ +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 HostService struct{} + +type IHostService interface { + GetConnInfo(id uint) (*model.Host, error) + Page(search dto.PageInfo) (int64, interface{}, error) + Create(hostDto dto.HostCreate) (*dto.HostInfo, error) + Update(id uint, upMap map[string]interface{}) error + BatchDelete(ids []uint) error +} + +func NewIHostService() IHostService { + return &HostService{} +} + +func (u *HostService) GetConnInfo(id uint) (*model.Host, error) { + host, err := hostRepo.Get(commonRepo.WithByID(id)) + if err != nil { + return nil, constant.ErrRecordNotFound + } + return &host, err +} + +func (u *HostService) Page(search dto.PageInfo) (int64, interface{}, error) { + total, hosts, err := hostRepo.Page(search.Page, search.PageSize) + var dtoHosts []dto.HostInfo + 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()) + } + dtoHosts = append(dtoHosts, item) + } + return total, dtoHosts, err +} + +func (u *HostService) Create(hostDto dto.HostCreate) (*dto.HostInfo, error) { + host, _ := hostRepo.Get(commonRepo.WithByName(hostDto.Name)) + if host.ID != 0 { + return nil, constant.ErrRecordExist + } + if err := copier.Copy(&host, &hostDto); err != nil { + return nil, errors.WithMessage(constant.ErrStructTransform, err.Error()) + } + if err := hostRepo.Create(&host); err != nil { + return nil, err + } + var hostinfo dto.HostInfo + if err := copier.Copy(&hostinfo, &host); err != nil { + return nil, errors.WithMessage(constant.ErrStructTransform, err.Error()) + } + return &hostinfo, nil +} + +func (u *HostService) BatchDelete(ids []uint) error { + if len(ids) == 1 { + host, _ := hostRepo.Get(commonRepo.WithByID(ids[0])) + if host.ID == 0 { + return constant.ErrRecordNotFound + } + } + return hostRepo.Delete(commonRepo.WithIdsIn(ids)) +} + +func (u *HostService) Update(id uint, upMap map[string]interface{}) error { + return hostRepo.Update(id, upMap) +} diff --git a/backend/app/service/operation_log.go b/backend/app/service/operation_log.go index a03461cfc..35c750966 100644 --- a/backend/app/service/operation_log.go +++ b/backend/app/service/operation_log.go @@ -56,7 +56,7 @@ func (u *OperationService) BatchDelete(ids []uint) error { } func filterSensitive(vars string) string { - var Sensitives = []string{"password", "Password"} + var Sensitives = []string{"password", "Password", "privateKey"} ops := make(map[string]interface{}) if err := json.Unmarshal([]byte(vars), &ops); err != nil { return vars diff --git a/backend/app/service/user.go b/backend/app/service/user.go index 9a8572ae8..3dd297e89 100644 --- a/backend/app/service/user.go +++ b/backend/app/service/user.go @@ -17,7 +17,7 @@ import ( type UserService struct{} type IUserService interface { - Get(name uint) (*dto.UserBack, error) + Get(id uint) (*dto.UserInfo, error) Page(search dto.UserPage) (int64, interface{}, error) Register(userDto dto.UserCreate) error Login(c *gin.Context, info dto.Login) (*dto.UserLoginInfo, error) @@ -32,12 +32,12 @@ func NewIUserService() IUserService { return &UserService{} } -func (u *UserService) Get(id uint) (*dto.UserBack, error) { +func (u *UserService) Get(id uint) (*dto.UserInfo, error) { user, err := userRepo.Get(commonRepo.WithByID(id)) if err != nil { return nil, constant.ErrRecordNotFound } - var dtoUser dto.UserBack + var dtoUser dto.UserInfo if err := copier.Copy(&dtoUser, &user); err != nil { return nil, errors.WithMessage(constant.ErrStructTransform, err.Error()) } @@ -46,9 +46,9 @@ func (u *UserService) Get(id uint) (*dto.UserBack, error) { func (u *UserService) Page(search dto.UserPage) (int64, interface{}, error) { total, users, err := userRepo.Page(search.Page, search.PageSize, commonRepo.WithLikeName(search.Name)) - var dtoUsers []dto.UserBack + var dtoUsers []dto.UserInfo for _, user := range users { - var item dto.UserBack + var item dto.UserInfo if err := copier.Copy(&item, &user); err != nil { return 0, nil, errors.WithMessage(constant.ErrStructTransform, err.Error()) } diff --git a/backend/init/migration/migrate.go b/backend/init/migration/migrate.go index 111cc6736..6c34a30cf 100644 --- a/backend/init/migration/migrate.go +++ b/backend/init/migration/migrate.go @@ -12,6 +12,7 @@ func Init() { migrations.InitTable, migrations.AddData, migrations.AddTableOperationLog, + migrations.AddTableHost, }) if err := m.Migrate(); err != nil { global.LOG.Error(err) diff --git a/backend/init/migration/migrations/init.go b/backend/init/migration/migrations/init.go index 841eb7678..d7dee98f1 100644 --- a/backend/init/migration/migrations/init.go +++ b/backend/init/migration/migrations/init.go @@ -31,3 +31,10 @@ var AddTableOperationLog = &gormigrate.Migration{ return tx.AutoMigrate(&model.OperationLog{}) }, } + +var AddTableHost = &gormigrate.Migration{ + ID: "20200818-add-table-host", + Migrate: func(tx *gorm.DB) error { + return tx.AutoMigrate(&model.Host{}) + }, +} diff --git a/backend/init/router/router.go b/backend/init/router/router.go index bb242ae0c..5eabba438 100644 --- a/backend/init/router/router.go +++ b/backend/init/router/router.go @@ -39,6 +39,7 @@ func Routers() *gin.Engine { { systemRouter.InitBaseRouter(PrivateGroup) systemRouter.InitUserRouter(PrivateGroup) + systemRouter.InitHostRouter(PrivateGroup) systemRouter.InitTerminalRouter(PrivateGroup) systemRouter.InitOperationLogRouter(PrivateGroup) } diff --git a/backend/router/entry.go b/backend/router/entry.go index f4d080cb5..e8a21d374 100644 --- a/backend/router/entry.go +++ b/backend/router/entry.go @@ -3,6 +3,7 @@ package router type RouterGroup struct { BaseRouter UserRouter + HostRouter OperationLogRouter } diff --git a/backend/router/ro_host.go b/backend/router/ro_host.go new file mode 100644 index 000000000..a7983937f --- /dev/null +++ b/backend/router/ro_host.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 HostRouter struct{} + +func (s *HostRouter) InitHostRouter(Router *gin.RouterGroup) { + userRouter := Router.Group("hosts") + userRouter.Use(middleware.JwtAuth()).Use(middleware.SessionAuth()) + withRecordRouter := userRouter.Use(middleware.OperationRecord()) + baseApi := v1.ApiGroupApp.BaseApi + { + withRecordRouter.POST("", baseApi.Create) + withRecordRouter.POST("/del", baseApi.DeleteHost) + userRouter.POST("/search", baseApi.PageHosts) + userRouter.PUT(":id", baseApi.UpdateHost) + } +} diff --git a/frontend/src/api/interface/host.ts b/frontend/src/api/interface/host.ts new file mode 100644 index 000000000..4ae3cd6d8 --- /dev/null +++ b/frontend/src/api/interface/host.ts @@ -0,0 +1,24 @@ +import { CommonModel } from '.'; + +export namespace Host { + export interface Host extends CommonModel { + name: string; + addr: string; + port: number; + user: string; + authMode: string; + description: string; + } + export interface HostOperate { + id: number; + name: string; + addr: string; + port: number; + user: string; + authMode: string; + privateKey: string; + password: string; + + description: string; + } +} diff --git a/frontend/src/api/interface/index.ts b/frontend/src/api/interface/index.ts index 8beffffce..91eb55181 100644 --- a/frontend/src/api/interface/index.ts +++ b/frontend/src/api/interface/index.ts @@ -1,23 +1,19 @@ -// * 请求响应参数(不包含data) export interface Result { code: number; message: string; } -// * 请求响应参数(包含data) export interface ResultData { code: number; message: string; data: T; } -// * 分页响应参数 export interface ResPage { items: T[]; total: number; } -// * 分页请求参数 export interface ReqPage { page: number; pageSize: number; diff --git a/frontend/src/api/modules/host.ts b/frontend/src/api/modules/host.ts new file mode 100644 index 000000000..9bc117c6a --- /dev/null +++ b/frontend/src/api/modules/host.ts @@ -0,0 +1,20 @@ +import http from '@/api'; +import { ResPage, ReqPage } from '../interface'; +import { Host } from '../interface/host'; + +export const getHostList = (params: ReqPage) => { + return http.post>(`/hosts/search`, params); +}; + +export const addHost = (params: Host.HostOperate) => { + return http.post(`/hosts`, params); +}; + +export const editHost = (params: Host.HostOperate) => { + console.log(params.id); + return http.put(`/hosts/` + params.id, params); +}; + +export const deleteHost = (params: { ids: number[] }) => { + return http.post(`/hosts/del`, params); +}; diff --git a/frontend/src/routers/modules/terminal.ts b/frontend/src/routers/modules/terminal.ts index d5e2495cd..08133bb4f 100644 --- a/frontend/src/routers/modules/terminal.ts +++ b/frontend/src/routers/modules/terminal.ts @@ -13,7 +13,7 @@ const terminalRouter = { { path: '/terminal', name: 'Terminal', - component: () => import('@/views/terminal/index2.vue'), + component: () => import('@/views/terminal/index.vue'), meta: { keepAlive: true, requiresAuth: true, diff --git a/frontend/src/views/demos/table/operate/index.vue b/frontend/src/views/demos/table/operate/index.vue index 056fa9881..f40d9d293 100644 --- a/frontend/src/views/demos/table/operate/index.vue +++ b/frontend/src/views/demos/table/operate/index.vue @@ -49,9 +49,9 @@ const props = withDefaults(defineProps(), { }); const rules = reactive({ - name: [Rules.required, Rules.name], - email: [Rules.required, Rules.email], - password: [Rules.required], + name: [Rules.requiredInput, Rules.name], + email: [Rules.requiredInput, Rules.email], + password: [Rules.requiredInput], }); const submitForm = async (formEl: FormInstance | undefined) => { diff --git a/frontend/src/views/terminal/index.vue b/frontend/src/views/terminal/index.vue index b68826f04..d71ce5021 100644 --- a/frontend/src/views/terminal/index.vue +++ b/frontend/src/views/terminal/index.vue @@ -1,21 +1,253 @@