mirror of https://github.com/jumpserver/jumpserver
pref: 类型树支持资产
parent
7ca2fdca89
commit
e0e57a71aa
|
@ -1,6 +1,5 @@
|
||||||
# ~*~ coding: utf-8 ~*~
|
# ~*~ coding: utf-8 ~*~
|
||||||
|
|
||||||
from django.core.exceptions import PermissionDenied
|
|
||||||
from rest_framework.generics import get_object_or_404
|
from rest_framework.generics import get_object_or_404
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
|
||||||
|
@ -12,44 +11,20 @@ from orgs.utils import current_org
|
||||||
from .mixin import SerializeToTreeNodeMixin
|
from .mixin import SerializeToTreeNodeMixin
|
||||||
from .. import serializers
|
from .. import serializers
|
||||||
from ..const import AllTypes
|
from ..const import AllTypes
|
||||||
from ..models import Node
|
from ..models import Node, Platform, Asset
|
||||||
|
|
||||||
logger = get_logger(__file__)
|
logger = get_logger(__file__)
|
||||||
__all__ = [
|
__all__ = [
|
||||||
'NodeChildrenApi',
|
'NodeChildrenApi',
|
||||||
'NodeListAsTreeApi',
|
|
||||||
'NodeChildrenAsTreeApi',
|
'NodeChildrenAsTreeApi',
|
||||||
'CategoryTreeApi',
|
'CategoryTreeApi',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class NodeListAsTreeApi(generics.ListAPIView):
|
|
||||||
"""
|
|
||||||
获取节点列表树
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"id": "",
|
|
||||||
"name": "",
|
|
||||||
"pId": "",
|
|
||||||
"meta": ""
|
|
||||||
}
|
|
||||||
]
|
|
||||||
"""
|
|
||||||
model = Node
|
|
||||||
serializer_class = TreeNodeSerializer
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def to_tree_queryset(queryset):
|
|
||||||
queryset = [node.as_tree_node() for node in queryset]
|
|
||||||
return queryset
|
|
||||||
|
|
||||||
def filter_queryset(self, queryset):
|
|
||||||
queryset = super().filter_queryset(queryset)
|
|
||||||
queryset = self.to_tree_queryset(queryset)
|
|
||||||
return queryset
|
|
||||||
|
|
||||||
|
|
||||||
class NodeChildrenApi(generics.ListCreateAPIView):
|
class NodeChildrenApi(generics.ListCreateAPIView):
|
||||||
|
"""
|
||||||
|
节点的增删改查
|
||||||
|
"""
|
||||||
serializer_class = serializers.NodeSerializer
|
serializer_class = serializers.NodeSerializer
|
||||||
search_fields = ('value',)
|
search_fields = ('value',)
|
||||||
|
|
||||||
|
@ -141,33 +116,38 @@ class NodeChildrenAsTreeApi(SerializeToTreeNodeMixin, NodeChildrenApi):
|
||||||
def list(self, request, *args, **kwargs):
|
def list(self, request, *args, **kwargs):
|
||||||
nodes = self.filter_queryset(self.get_queryset()).order_by('value')
|
nodes = self.filter_queryset(self.get_queryset()).order_by('value')
|
||||||
nodes = self.serialize_nodes(nodes, with_asset_amount=True)
|
nodes = self.serialize_nodes(nodes, with_asset_amount=True)
|
||||||
assets = self.get_assets()
|
assets = self.get_assets_as_node()
|
||||||
data = [*nodes, *assets]
|
data = [*nodes, *assets]
|
||||||
return Response(data=data)
|
return Response(data=data)
|
||||||
|
|
||||||
def get_assets(self):
|
def get_assets_as_node(self):
|
||||||
include_assets = self.request.query_params.get('assets', '0') == '1'
|
include_assets = self.request.query_params.get('assets', '0') == '1'
|
||||||
if not self.instance or not include_assets:
|
if not self.instance or not include_assets:
|
||||||
return []
|
return []
|
||||||
assets = self.instance.get_assets().only(
|
assets = self.instance.get_assets_for_tree()
|
||||||
"id", "name", "address", "platform_id",
|
|
||||||
"org_id", "is_active",
|
|
||||||
).prefetch_related('platform')
|
|
||||||
return self.serialize_assets(assets, self.instance.key)
|
return self.serialize_assets(assets, self.instance.key)
|
||||||
|
|
||||||
|
|
||||||
class CategoryTreeApi(SerializeToTreeNodeMixin, generics.ListAPIView):
|
class CategoryTreeApi(SerializeToTreeNodeMixin, generics.ListAPIView):
|
||||||
serializer_class = TreeNodeSerializer
|
serializer_class = TreeNodeSerializer
|
||||||
|
rbac_perms = {
|
||||||
|
'GET': 'assets.view_asset',
|
||||||
|
'list': 'assets.view_asset',
|
||||||
|
}
|
||||||
|
|
||||||
def check_permissions(self, request):
|
def get_assets(self):
|
||||||
if not request.user.has_perm('assets.view_asset'):
|
key = self.request.query_params.get('key')
|
||||||
raise PermissionDenied
|
platform = Platform.objects.filter(id=key).first()
|
||||||
return True
|
if not platform:
|
||||||
|
return []
|
||||||
|
assets = Asset.objects.filter(platform=platform).prefetch_related('platform')
|
||||||
|
return self.serialize_assets(assets, key)
|
||||||
|
|
||||||
def list(self, request, *args, **kwargs):
|
def list(self, request, *args, **kwargs):
|
||||||
if request.query_params.get('key'):
|
include_asset = bool(self.request.query_params.get('assets'))
|
||||||
nodes = []
|
|
||||||
|
if include_asset and self.request.query_params.get('key'):
|
||||||
|
nodes = self.get_assets()
|
||||||
else:
|
else:
|
||||||
nodes = AllTypes.to_tree_nodes()
|
nodes = AllTypes.to_tree_nodes(include_asset)
|
||||||
serializer = self.get_serializer(nodes, many=True)
|
return Response(data=nodes)
|
||||||
return Response(data=serializer.data)
|
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
|
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from common.db.models import ChoicesMixin
|
from common.db.models import ChoicesMixin
|
||||||
from common.tree import TreeNode
|
|
||||||
from .category import Category
|
from .category import Category
|
||||||
from .cloud import CloudTypes
|
from .cloud import CloudTypes
|
||||||
from .database import DatabaseTypes
|
from .database import DatabaseTypes
|
||||||
|
@ -134,34 +135,34 @@ class AllTypes(ChoicesMixin):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def choice_to_node(choice, pid, opened=True, is_parent=True, meta=None):
|
def choice_to_node(choice, pid, opened=True, is_parent=True, meta=None):
|
||||||
node = TreeNode(**{
|
node = {
|
||||||
'id': pid + '_' + choice.name,
|
'id': pid + '_' + choice.name,
|
||||||
'name': choice.label,
|
'name': choice.label,
|
||||||
'title': choice.label,
|
'title': choice.label,
|
||||||
'pId': pid,
|
'pId': pid,
|
||||||
'open': opened,
|
'open': opened,
|
||||||
'isParent': is_parent,
|
'isParent': is_parent,
|
||||||
})
|
}
|
||||||
if meta:
|
if meta:
|
||||||
node.meta = meta
|
node['meta'] = meta
|
||||||
return node
|
return node
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def platform_to_node(cls, p, pid):
|
def platform_to_node(cls, p, pid, include_asset):
|
||||||
node = TreeNode(**{
|
node = {
|
||||||
'id': '{}'.format(p.id),
|
'id': '{}'.format(p.id),
|
||||||
'name': p.name,
|
'name': p.name,
|
||||||
'title': p.name,
|
'title': p.name,
|
||||||
'pId': pid,
|
'pId': pid,
|
||||||
'isParent': True,
|
'isParent': include_asset,
|
||||||
'meta': {
|
'meta': {
|
||||||
'type': 'platform'
|
'type': 'platform'
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
return node
|
return node
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def to_tree_nodes(cls):
|
def to_tree_nodes(cls, include_asset):
|
||||||
from ..models import Asset, Platform
|
from ..models import Asset, Platform
|
||||||
asset_platforms = Asset.objects.all().values_list('platform_id', flat=True)
|
asset_platforms = Asset.objects.all().values_list('platform_id', flat=True)
|
||||||
platform_count = defaultdict(int)
|
platform_count = defaultdict(int)
|
||||||
|
@ -177,26 +178,29 @@ class AllTypes(ChoicesMixin):
|
||||||
category_type_mapper[p.category] += platform_count[p.id]
|
category_type_mapper[p.category] += platform_count[p.id]
|
||||||
tp_platforms[p.category + '_' + p.type].append(p)
|
tp_platforms[p.category + '_' + p.type].append(p)
|
||||||
|
|
||||||
root = TreeNode(id='ROOT', name='所有类型', title='所有类型', open=True, isParent=True)
|
root = dict(id='ROOT', name=_('All types'), title='所有类型', open=True, isParent=True)
|
||||||
nodes = [root]
|
nodes = [root]
|
||||||
for category, type_cls in cls.category_types():
|
for category, type_cls in cls.category_types():
|
||||||
|
# Category 格式化
|
||||||
meta = {'type': 'category', 'category': category.value}
|
meta = {'type': 'category', 'category': category.value}
|
||||||
category_node = cls.choice_to_node(category, 'ROOT', meta=meta)
|
category_node = cls.choice_to_node(category, 'ROOT', meta=meta)
|
||||||
category_count = category_type_mapper.get(category, 0)
|
category_count = category_type_mapper.get(category, 0)
|
||||||
category_node.name += f'({category_count})'
|
category_node['name'] += f'({category_count})'
|
||||||
nodes.append(category_node)
|
nodes.append(category_node)
|
||||||
|
|
||||||
tps = type_cls.get_types()
|
# Type 格式化
|
||||||
for tp in tps:
|
types = type_cls.get_types()
|
||||||
|
for tp in types:
|
||||||
meta = {'type': 'type', 'category': category.value, '_type': tp.value}
|
meta = {'type': 'type', 'category': category.value, '_type': tp.value}
|
||||||
tp_node = cls.choice_to_node(tp, category_node.id, opened=False, meta=meta)
|
tp_node = cls.choice_to_node(tp, category_node['id'], opened=False, meta=meta)
|
||||||
tp_count = category_type_mapper.get(category + '_' + tp, 0)
|
tp_count = category_type_mapper.get(category + '_' + tp, 0)
|
||||||
tp_node.name += f'({tp_count})'
|
tp_node['name'] += f'({tp_count})'
|
||||||
nodes.append(tp_node)
|
nodes.append(tp_node)
|
||||||
|
|
||||||
|
# Platform 格式化
|
||||||
for p in tp_platforms.get(category + '_' + tp, []):
|
for p in tp_platforms.get(category + '_' + tp, []):
|
||||||
platform_node = cls.platform_to_node(p, tp_node.id)
|
platform_node = cls.platform_to_node(p, tp_node['id'], include_asset)
|
||||||
platform_node.name += f'({platform_count.get(p.id, 0)})'
|
platform_node['name'] += f'({platform_count.get(p.id, 0)})'
|
||||||
nodes.append(platform_node)
|
nodes.append(platform_node)
|
||||||
return nodes
|
return nodes
|
||||||
|
|
||||||
|
|
|
@ -430,6 +430,12 @@ class NodeAssetsMixin(NodeAllAssetsMappingMixin):
|
||||||
assets = Asset.objects.filter(nodes=self)
|
assets = Asset.objects.filter(nodes=self)
|
||||||
return assets.distinct()
|
return assets.distinct()
|
||||||
|
|
||||||
|
def get_assets_for_tree(self):
|
||||||
|
return self.get_assets().only(
|
||||||
|
"id", "name", "address", "platform_id",
|
||||||
|
"org_id", "is_active"
|
||||||
|
).prefetch_related('platform')
|
||||||
|
|
||||||
def get_valid_assets(self):
|
def get_valid_assets(self):
|
||||||
return self.get_assets().valid()
|
return self.get_assets().valid()
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,7 @@ urlpatterns = [
|
||||||
name='account-secret-history'),
|
name='account-secret-history'),
|
||||||
|
|
||||||
path('nodes/category/tree/', api.CategoryTreeApi.as_view(), name='asset-category-tree'),
|
path('nodes/category/tree/', api.CategoryTreeApi.as_view(), name='asset-category-tree'),
|
||||||
path('nodes/tree/', api.NodeListAsTreeApi.as_view(), name='node-tree'),
|
# path('nodes/tree/', api.NodeListAsTreeApi.as_view(), name='node-tree'),
|
||||||
path('nodes/children/tree/', api.NodeChildrenAsTreeApi.as_view(), name='node-children-tree'),
|
path('nodes/children/tree/', api.NodeChildrenAsTreeApi.as_view(), name='node-children-tree'),
|
||||||
path('nodes/<uuid:pk>/children/', api.NodeChildrenApi.as_view(), name='node-children'),
|
path('nodes/<uuid:pk>/children/', api.NodeChildrenApi.as_view(), name='node-children'),
|
||||||
path('nodes/children/', api.NodeChildrenApi.as_view(), name='node-children-2'),
|
path('nodes/children/', api.NodeChildrenApi.as_view(), name='node-children-2'),
|
||||||
|
|
Loading…
Reference in New Issue