mirror of https://github.com/openspug/spug
update host edit
parent
591a4d1659
commit
8917b1fe76
|
@ -20,6 +20,7 @@ def fetch_children(data):
|
|||
def merge_children(data, prefix, childes):
|
||||
for item in childes:
|
||||
name = f'{prefix}/{item["title"]}'
|
||||
item['name'] = name
|
||||
if item['children']:
|
||||
merge_children(data, name, item['children'])
|
||||
else:
|
||||
|
@ -36,8 +37,9 @@ class GroupView(View):
|
|||
grp = Group.objects.create(name='Default', sort_id=1)
|
||||
data[grp.id] = grp.to_view()
|
||||
|
||||
merge_children(data2, '', data.values())
|
||||
return json_response({'treeData': list(data.values()), 'groups': data2})
|
||||
data = list(data.values())
|
||||
merge_children(data2, '', data)
|
||||
return json_response({'treeData': data, 'groups': data2})
|
||||
|
||||
def post(self, request):
|
||||
form, error = JsonParser(
|
||||
|
|
|
@ -10,7 +10,6 @@ from libs.ssh import SSH
|
|||
|
||||
class Host(models.Model, ModelMixin):
|
||||
name = models.CharField(max_length=50)
|
||||
zone = models.CharField(max_length=50)
|
||||
hostname = models.CharField(max_length=50)
|
||||
port = models.IntegerField()
|
||||
username = models.CharField(max_length=50)
|
||||
|
@ -52,6 +51,7 @@ class Group(models.Model, ModelMixin):
|
|||
def to_view(self):
|
||||
return {
|
||||
'key': self.id,
|
||||
'value': self.id,
|
||||
'title': self.name,
|
||||
'children': []
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ class HostView(View):
|
|||
def post(self, request):
|
||||
form, error = JsonParser(
|
||||
Argument('id', type=int, required=False),
|
||||
Argument('zone', help='请输入主机类型'),
|
||||
Argument('group_ids', type=list, filter=lambda x: len(x), help='请选择主机分组'),
|
||||
Argument('name', help='请输主机名称'),
|
||||
Argument('username', handler=str.strip, help='请输入登录用户名'),
|
||||
Argument('hostname', handler=str.strip, help='请输入主机名或IP'),
|
||||
|
@ -47,14 +47,16 @@ class HostView(View):
|
|||
pkey=form.pkey) is False:
|
||||
return json_response('auth fail')
|
||||
|
||||
if form.id:
|
||||
Host.objects.filter(pk=form.pop('id')).update(**form)
|
||||
elif Host.objects.filter(name=form.name, deleted_by_id__isnull=True).exists():
|
||||
group_ids = form.pop('group_ids')
|
||||
other = Host.objects.filter(name=form.name, deleted_by_id__isnull=True).first()
|
||||
if other and (not form.id or other.id != form.id):
|
||||
return json_response(error=f'已存在的主机名称【{form.name}】')
|
||||
if form.id:
|
||||
Host.objects.filter(pk=form.id).update(**form)
|
||||
host = Host.objects.get(pk=form.id)
|
||||
else:
|
||||
host = Host.objects.create(created_by=request.user, **form)
|
||||
if request.user.role:
|
||||
request.user.role.add_host_perm(host.id)
|
||||
host.groups.set(group_ids)
|
||||
return json_response(error=error)
|
||||
|
||||
def patch(self, request):
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
import React from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
import { Drawer} from 'antd';
|
||||
import store from './store';
|
||||
|
||||
export default observer(function () {
|
||||
return (
|
||||
<Drawer
|
||||
width={500}
|
||||
title={store.record.name}
|
||||
placement="right"
|
||||
onClose={() => store.detailVisible = false}
|
||||
visible={store.detailVisible}>
|
||||
|
||||
</Drawer>
|
||||
)
|
||||
})
|
|
@ -6,7 +6,7 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { observer } from 'mobx-react';
|
||||
import { ExclamationCircleOutlined, UploadOutlined } from '@ant-design/icons';
|
||||
import { Modal, Form, Input, Select, Button, Upload, message } from 'antd';
|
||||
import { Modal, Form, Input, TreeSelect, Button, Upload, Alert, message } from 'antd';
|
||||
import { http, X_TOKEN } from 'libs';
|
||||
import store from './store';
|
||||
|
||||
|
@ -102,12 +102,13 @@ export default observer(function () {
|
|||
confirmLoading={loading}
|
||||
onOk={handleSubmit}>
|
||||
<Form form={form} labelCol={{span: 6}} wrapperCol={{span: 14}} initialValues={info}>
|
||||
<Form.Item required name="zone" label="主机类别">
|
||||
<Select placeholder="请选择主机类别/区域/分组">
|
||||
{store.zones.map(item => (
|
||||
<Select.Option value={item} key={item}>{item}</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
<Form.Item required name="group_ids" label="主机分组">
|
||||
<TreeSelect
|
||||
multiple
|
||||
treeNodeLabelProp="name"
|
||||
treeData={store.treeData}
|
||||
showCheckedStrategy={TreeSelect.SHOW_CHILD}
|
||||
placeholder="请选择分组"/>
|
||||
</Form.Item>
|
||||
<Form.Item required name="name" label="主机名称">
|
||||
<Input placeholder="请输入主机名称"/>
|
||||
|
@ -133,7 +134,7 @@ export default observer(function () {
|
|||
<Input.TextArea placeholder="请输入主机备注信息"/>
|
||||
</Form.Item>
|
||||
<Form.Item wrapperCol={{span: 14, offset: 6}}>
|
||||
<span role="img" aria-label="notice">⚠️ 首次验证时需要输入登录用户名对应的密码,但不会存储该密码。</span>
|
||||
<Alert showIcon type="info" message="首次验证时需要输入登录用户名对应的密码,该密码会用于配置密钥连接,不会存储该密码。" />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
|
|
|
@ -62,7 +62,11 @@ class ComTable extends React.Component {
|
|||
showTotal: total => `共 ${total} 条`,
|
||||
pageSizeOptions: ['10', '20', '50', '100']
|
||||
}}>
|
||||
<Table.Column showSorterTooltip={false} title="主机名称" dataIndex="name" sorter={(a, b) => a.name.localeCompare(b.name)}/>
|
||||
<Table.Column
|
||||
showSorterTooltip={false}
|
||||
title="主机名称"
|
||||
render={info => <Action.Button onClick={() => store.showDetail(info)}>{info.name}</Action.Button>}
|
||||
sorter={(a, b) => a.name.localeCompare(b.name)}/>
|
||||
<Table.Column title="连接地址" dataIndex="hostname" sorter={(a, b) => a.name.localeCompare(b.name)}/>
|
||||
<Table.Column hide width={100} title="端口" dataIndex="port"/>
|
||||
<Table.Column hide ellipsis title="备注信息" dataIndex="desc"/>
|
||||
|
|
|
@ -11,6 +11,7 @@ import Group from './Group';
|
|||
import ComTable from './Table';
|
||||
import ComForm from './Form';
|
||||
import ComImport from './Import';
|
||||
import Detail from './Detail';
|
||||
import store from './store';
|
||||
|
||||
export default observer(function () {
|
||||
|
@ -30,6 +31,7 @@ export default observer(function () {
|
|||
</Col>
|
||||
</Row>
|
||||
|
||||
<Detail/>
|
||||
{store.formVisible && <ComForm/>}
|
||||
{store.importVisible && <ComImport/>}
|
||||
</AuthDiv>
|
||||
|
|
|
@ -17,6 +17,7 @@ class Store {
|
|||
@observable isFetching = false;
|
||||
@observable formVisible = false;
|
||||
@observable importVisible = false;
|
||||
@observable detailVisible = false;
|
||||
|
||||
@observable f_name;
|
||||
@observable f_host;
|
||||
|
@ -59,6 +60,11 @@ class Store {
|
|||
this.record = info
|
||||
}
|
||||
|
||||
showDetail = (info) => {
|
||||
this.record = info;
|
||||
this.detailVisible = true;
|
||||
}
|
||||
|
||||
_updateGroupCount = () => {
|
||||
if (this.treeData.length && this.records.length) {
|
||||
const counter = {};
|
||||
|
@ -83,6 +89,7 @@ class Store {
|
|||
host_ids = host_ids.concat(this._updateCount(counter, child))
|
||||
}
|
||||
item.host_ids = Array.from(new Set(host_ids));
|
||||
if (item.key === this.group.key) this.group = item;
|
||||
return item.host_ids
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue