1
0
mirror of https://github.com/1Panel-dev/1Panel.git synced 2025-01-19 08:19:15 +08:00

feat: 增加主机测试连接按钮

This commit is contained in:
ssongliu 2022-09-01 10:25:38 +08:00 committed by ssongliu
parent 0fdf519808
commit 3c770ce63b
12 changed files with 128 additions and 71 deletions

View File

@ -9,7 +9,7 @@ import (
)
func (b *BaseApi) CreateCommand(c *gin.Context) {
var req dto.CommandCreate
var req dto.CommandOperate
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
@ -45,7 +45,7 @@ func (b *BaseApi) SearchCommand(c *gin.Context) {
}
func (b *BaseApi) ListCommand(c *gin.Context) {
list, err := commandService.Search()
list, err := commandService.List()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
@ -55,7 +55,7 @@ func (b *BaseApi) ListCommand(c *gin.Context) {
}
func (b *BaseApi) DeleteCommand(c *gin.Context) {
var req dto.DeleteByName
var req dto.BatchDeleteReq
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
@ -65,7 +65,7 @@ func (b *BaseApi) DeleteCommand(c *gin.Context) {
return
}
if err := commandService.Delete(req.Name); err != nil {
if err := commandService.Delete(req.Ids); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
@ -73,7 +73,7 @@ func (b *BaseApi) DeleteCommand(c *gin.Context) {
}
func (b *BaseApi) UpdateCommand(c *gin.Context) {
var req dto.CommandUpdate
var req dto.CommandOperate
if err := c.ShouldBindJSON(&req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
@ -90,6 +90,7 @@ func (b *BaseApi) UpdateCommand(c *gin.Context) {
upMap := make(map[string]interface{})
upMap["name"] = req.Name
upMap["command"] = req.Command
if err := commandService.Update(id, upMap); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return

View File

@ -6,6 +6,7 @@ import (
"github.com/1Panel-dev/1Panel/constant"
"github.com/1Panel-dev/1Panel/global"
"github.com/1Panel-dev/1Panel/utils/copier"
"github.com/1Panel-dev/1Panel/utils/ssh"
"github.com/gin-gonic/gin"
)
@ -27,6 +28,32 @@ func (b *BaseApi) CreateHost(c *gin.Context) {
helper.SuccessWithData(c, host)
}
func (b *BaseApi) TestConn(c *gin.Context) {
var req dto.HostOperate
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
}
var connInfo ssh.ConnInfo
if err := copier.Copy(&connInfo, &req); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, constant.ErrStructTransform)
return
}
client, err := connInfo.NewClient()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
defer client.Close()
helper.SuccessWithData(c, nil)
}
func (b *BaseApi) HostTree(c *gin.Context) {
var req dto.SearchForTree
if err := c.ShouldBindJSON(&req); err != nil {
@ -63,17 +90,13 @@ func (b *BaseApi) GetHostInfo(c *gin.Context) {
}
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 {
id, err := helper.GetParamID(c)
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrBadRequest, constant.ErrTypeInvalidParams, err)
return
}
if err := hostService.BatchDelete(req.Ids); err != nil {
if err := hostService.Delete(id); err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}

View File

@ -51,7 +51,7 @@ func (b *BaseApi) WsSsh(c *gin.Context) {
defer wsConn.Close()
client, err := connInfo.NewClient()
if wshandleError(wsConn, errors.WithMessage(err, " Failed to set up the connection. Please check the host information")) {
if wshandleError(wsConn, errors.WithMessage(err, "failed to set up the connection. Please check the host information")) {
return
}
defer client.Close()

View File

@ -1,16 +1,12 @@
package dto
type CommandCreate struct {
type CommandOperate 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 {
ID string `json:"id"`
ID uint `json:"id"`
Name string `json:"name"`
Command string `json:"command"`
}

View File

@ -11,18 +11,18 @@ import (
type CommandService struct{}
type ICommandService interface {
Search() ([]model.Command, error)
List() ([]model.Command, error)
SearchWithPage(search dto.SearchWithPage) (int64, interface{}, error)
Create(commandDto dto.CommandCreate) error
Create(commandDto dto.CommandOperate) error
Update(id uint, upMap map[string]interface{}) error
Delete(name string) error
Delete(ids []uint) error
}
func NewICommandService() ICommandService {
return &CommandService{}
}
func (u *CommandService) Search() ([]model.Command, error) {
func (u *CommandService) List() ([]model.Command, error) {
commands, err := commandRepo.GetList()
if err != nil {
return nil, constant.ErrRecordNotFound
@ -43,7 +43,7 @@ func (u *CommandService) SearchWithPage(search dto.SearchWithPage) (int64, inter
return total, dtoCommands, err
}
func (u *CommandService) Create(commandDto dto.CommandCreate) error {
func (u *CommandService) Create(commandDto dto.CommandOperate) error {
command, _ := commandRepo.Get(commonRepo.WithByName(commandDto.Name))
if command.ID != 0 {
return constant.ErrRecordExist
@ -57,12 +57,15 @@ func (u *CommandService) Create(commandDto dto.CommandCreate) error {
return nil
}
func (u *CommandService) Delete(name string) error {
command, _ := commandRepo.Get(commonRepo.WithByName(name))
if command.ID == 0 {
return constant.ErrRecordNotFound
func (u *CommandService) Delete(ids []uint) error {
if len(ids) == 1 {
command, _ := commandRepo.Get(commonRepo.WithByID(ids[0]))
if command.ID == 0 {
return constant.ErrRecordNotFound
}
return commandRepo.Delete(commonRepo.WithByID(ids[0]))
}
return commandRepo.Delete(commonRepo.WithByID(command.ID))
return commandRepo.Delete(commonRepo.WithIdsIn(ids))
}
func (u *CommandService) Update(id uint, upMap map[string]interface{}) error {

View File

@ -17,7 +17,7 @@ type IHostService interface {
SearchForTree(search dto.SearchForTree) ([]dto.HostTree, error)
Create(hostDto dto.HostOperate) (*dto.HostInfo, error)
Update(id uint, upMap map[string]interface{}) error
BatchDelete(ids []uint) error
Delete(id uint) error
}
func NewIHostService() IHostService {
@ -75,14 +75,12 @@ func (u *HostService) Create(hostDto dto.HostOperate) (*dto.HostInfo, 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
}
func (u *HostService) Delete(id uint) error {
host, _ := hostRepo.Get(commonRepo.WithByID(id))
if host.ID == 0 {
return constant.ErrRecordNotFound
}
return hostRepo.Delete(commonRepo.WithIdsIn(ids))
return hostRepo.Delete(commonRepo.WithByID(id))
}
func (u *HostService) Update(id uint, upMap map[string]interface{}) error {

View File

@ -16,8 +16,9 @@ func (s *HostRouter) InitHostRouter(Router *gin.RouterGroup) {
{
withRecordRouter.POST("", baseApi.CreateHost)
withRecordRouter.DELETE(":id", baseApi.DeleteHost)
withRecordRouter.PUT(":id", baseApi.UpdateHost)
hostRouter.POST("/search", baseApi.HostTree)
hostRouter.POST("/testconn", baseApi.TestConn)
hostRouter.GET(":id", baseApi.GetHostInfo)
hostRouter.PUT(":id", baseApi.UpdateHost)
}
}

View File

@ -13,6 +13,10 @@ export const addHost = (params: Host.HostOperate) => {
return http.post<Host.HostOperate>(`/hosts`, params);
};
export const testConn = (params: Host.HostOperate) => {
return http.post<Host.HostOperate>(`/hosts/testconn`, params);
};
export const editHost = (params: Host.HostOperate) => {
console.log(params.id);
return http.put(`/hosts/` + params.id, params);

View File

@ -90,8 +90,16 @@ export default {
logout: 'Logout',
},
terminal: {
connHistory: 'historys',
hostHistory: 'History record',
conn: 'connection',
testConn: 'Test connection',
connTestOk: 'Connection information available',
hostList: 'Host information',
createConn: 'Create a connection',
createGroup: 'Create a group',
expand: 'Expand all',
fold: 'All contract',
groupDeleteHelper:
'After the group is removed, all connections in the group will be migrated to the default group. Confirm the information',
addHost: 'Add Host',
localhost: 'Localhost',
name: 'Name',
@ -108,6 +116,8 @@ export default {
detail: {
users: 'User',
hosts: 'Host',
groups: 'Group',
commands: 'Command',
auth: 'User',
login: ' login',
logout: ' logout',

View File

@ -94,7 +94,14 @@ export default {
},
terminal: {
conn: '连接',
testConn: '连接测试',
connTestOk: '连接信息可用',
hostList: '主机信息',
createConn: '创建连接',
createGroup: '创建分组',
expand: '全部展开',
fold: '全部收缩',
groupDeleteHelper: '移除组后组内所有连接将迁移到 default 组内是否确认',
quickCmd: '快捷命令',
command: '命令',
addHost: '添加主机',
@ -114,7 +121,7 @@ export default {
users: '用户',
hosts: '主机',
groups: '组',
command: '快捷命令',
commands: '快捷命令',
auth: '用户',
post: '创建',
put: '更新',

View File

@ -83,7 +83,6 @@ const submitAddCommand = (formEl: FormInstance | undefined) => {
if (!formEl) return;
formEl.validate(async (valid) => {
if (!valid) return;
console.log(commandInfo.id);
if (operate.value === 'create') {
await addCommand(commandInfo);
} else {
@ -97,12 +96,11 @@ const submitAddCommand = (formEl: FormInstance | undefined) => {
const onEdit = async (row: Command.CommandInfo | null) => {
if (row !== null) {
console.log(row.id);
// commandInfo.id = row.id;
// commandInfo.name = row.name;
// commandInfo.command = row.command;
// operate.value = 'edit';
// cmdVisiable.value = true;
commandInfo.id = row.id;
commandInfo.name = row.name;
commandInfo.command = row.command;
operate.value = 'edit';
cmdVisiable.value = true;
}
};
@ -142,9 +140,6 @@ const search = async () => {
commandSearch.pageSize = paginationConfig.pageSize;
const res = await getCommandPage(commandSearch);
data.value = res.data.items;
for (const d of data.value) {
d.id = d.id + '';
}
paginationConfig.total = res.data.total;
};

View File

@ -2,16 +2,16 @@
<el-row style="margin: 20px; margin-left: 20px" class="row-box" :gutter="20">
<el-col :span="8">
<el-card class="el-card">
<el-tooltip class="box-item" effect="dark" content="创建连接" placement="top-start">
<el-tooltip class="box-item" effect="dark" :content="$t('terminal.createConn')" placement="top-start">
<el-button icon="Plus" @click="restHostForm" size="small" />
</el-tooltip>
<el-tooltip class="box-item" effect="dark" content="创建分组" placement="top-start">
<el-tooltip class="box-item" effect="dark" :content="$t('terminal.createGroup')" placement="top-start">
<el-button icon="FolderAdd" @click="onGroupCreate" size="small" />
</el-tooltip>
<el-tooltip class="box-item" effect="dark" content="展开" placement="top-start">
<el-tooltip class="box-item" effect="dark" :content="$t('terminal.expand')" placement="top-start">
<el-button icon="Expand" @click="setTreeStatus(true)" size="small" />
</el-tooltip>
<el-tooltip class="box-item" effect="dark" content="收缩" placement="top-start">
<el-tooltip class="box-item" effect="dark" :content="$t('terminal.fold')" placement="top-start">
<el-button icon="Fold" @click="setTreeStatus(false)" size="small" />
</el-tooltip>
<el-input
@ -69,7 +69,7 @@
<el-input clearable v-model="hostInfo.name" />
</el-form-item>
<el-form-item :label="$t('commons.table.group')" prop="groupBelong">
<el-select v-model="hostInfo.groupBelong" clearable style="width: 100%">
<el-select filterable v-model="hostInfo.groupBelong" clearable style="width: 100%">
<el-option v-for="item in groupList" :key="item.id" :label="item.name" :value="item.name" />
</el-select>
</el-form-item>
@ -105,10 +105,21 @@
<el-button @click="restHostForm">
{{ $t('commons.button.reset') }}
</el-button>
<el-button v-if="hostOperation === 'create'" type="primary" @click="submitAddHost(hostInfoRef)">
<el-button @click="submitAddHost(hostInfoRef, 'testconn')">
{{ $t('terminal.testConn') }}
</el-button>
<el-button
v-if="hostOperation === 'create'"
type="primary"
@click="submitAddHost(hostInfoRef, 'create')"
>
{{ $t('commons.button.create') }}
</el-button>
<el-button v-if="hostOperation === 'edit'" type="primary" @click="submitAddHost(hostInfoRef)">
<el-button
v-if="hostOperation === 'edit'"
type="primary"
@click="submitAddHost(hostInfoRef, 'edit')"
>
{{ $t('commons.button.confirm') }}
</el-button>
</el-form-item>
@ -124,7 +135,7 @@ import type { ElForm } from 'element-plus';
import { Rules } from '@/global/form-rues';
import { Host } from '@/api/interface/host';
import { Group } from '@/api/interface/group';
import { getHostList, getHostInfo, addHost, editHost, deleteHost } from '@/api/modules/host';
import { testConn, getHostList, getHostInfo, addHost, editHost, deleteHost } from '@/api/modules/host';
import { getGroupList, addGroup, editGroup, deleteGroup } from '@/api/modules/group';
import { useDeleteData } from '@/hooks/use-delete-data';
import { ElMessage } from 'element-plus';
@ -203,18 +214,29 @@ function restHostForm() {
}
}
const submitAddHost = (formEl: FormInstance | undefined) => {
const submitAddHost = (formEl: FormInstance | undefined, ops: string) => {
if (!formEl) return;
formEl.validate(async (valid) => {
if (!valid) return;
if (hostOperation.value === 'create') {
await addHost(hostInfo);
} else {
await editHost(hostInfo);
console.log(ops);
switch (ops) {
case 'create':
await addHost(hostInfo);
restHostForm();
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
loadHostTree();
break;
case 'edit':
await editHost(hostInfo);
restHostForm();
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
loadHostTree();
break;
case 'testconn':
await testConn(hostInfo);
ElMessage.success(i18n.global.t('terminal.connTestOk'));
break;
}
restHostForm();
ElMessage.success(i18n.global.t('commons.msg.operationSuccess'));
loadHostTree();
});
};
@ -224,7 +246,6 @@ const onGroupCreate = () => {
groupOperation.value = 'create';
};
const onCreateGroup = async () => {
console.log(groupOperation.value);
if (groupOperation.value === 'create') {
let group = { id: 0, name: groupInputValue.value, type: 'host' };
await addGroup(group);
@ -246,7 +267,7 @@ const onDelete = async (node: Node, data: Tree) => {
return;
}
if (node.level === 1) {
await useDeleteData(deleteGroup, data.id - 10000, '移除组后,组内所有连接将迁移到 default 组内,是否确认?');
await useDeleteData(deleteGroup, data.id - 10000, i18n.global.t('terminal.groupDeleteHelper'));
loadGroups();
} else {
await useDeleteData(deleteHost, data.id, 'commons.msg.delete');
@ -259,13 +280,11 @@ const onEdit = async (node: Node, data: Tree) => {
if (node.level === 1 && data.label === 'default') {
return;
}
console.log(node.level === 1);
if (node.level === 1) {
groupInputShow.value = true;
groupInputValue.value = data.label;
currentGroupID.value = data.id - 10000;
groupOperation.value = 'edit';
console.log(groupOperation.value);
return;
} else {
const res = await getHostInfo(data.id);