diff --git a/netaxe/Dockerfile b/netaxe/Dockerfile index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..9085d5f310d9877d19b65b37e3a0aabd2c9c2d0b 100644 --- a/netaxe/Dockerfile +++ b/netaxe/Dockerfile @@ -0,0 +1,19 @@ +FROM registry.cn-hangzhou.aliyuncs.com/netaxe/netaxe-backend:1.0.13 + +# 更新pip版本 +RUN pip3 install -i https://pypi.doubanio.com/simple/ uwsgi --upgrade pip +COPY . /home/netaxe +# 再次切换工作目录为Django主目录 +WORKDIR /home/netaxe + + +# 安装项目所需python第三方库 +# 指定setuptools的版本,必须指定,新版本有兼容问题 +RUN set -ex \ + && /usr/local/python3/bin/pip3 install setuptools_scm -i https://mirrors.aliyun.com/pypi/simple/ \ + # && /usr/local/python3/bin/pip3 install --upgrade pip setuptools==45.2.0 -i https://mirrors.aliyun.com/pypi/simple/ \ + &&/usr/local/python3/bin/pip3 install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/ \ + && rm -rf /var/cache/yum/* +EXPOSE 8001 +EXPOSE 5555 +CMD ["sh", "start.sh"] \ No newline at end of file diff --git a/netaxe/apps/api/tools/custom_viewset_base.py b/netaxe/apps/api/tools/custom_viewset_base.py index 9bc02e09226c2239fdab8a1e80348c7624b19e43..087c2ab1de13bf2943c478e8076b5cda99a992aa 100644 --- a/netaxe/apps/api/tools/custom_viewset_base.py +++ b/netaxe/apps/api/tools/custom_viewset_base.py @@ -17,6 +17,7 @@ class CustomViewBase(viewsets.ModelViewSet): queryset = '' serializer_class = '' permission_classes = () + authentication_classes = () filter_fields = () search_fields = () filter_backends = (rest_framework.DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter,) diff --git a/netaxe/apps/api/views.py b/netaxe/apps/api/views.py index 18bb1334d6e5ccee754756780f0fe542099d2bab..3b1ca6f54b86a8b3a260174842dad1ef7c6b592a 100644 --- a/netaxe/apps/api/views.py +++ b/netaxe/apps/api/views.py @@ -8,22 +8,11 @@ from django_filters.rest_framework import DjangoFilterBackend import django_filters from rest_framework import viewsets, permissions, filters, pagination -from .serializers import PeriodicTaskSerializer -from .tools.custom_viewset_base import CustomViewBase -# from rest_framework_extensions.cache.mixins import CacheResponseMixin -from rest_framework_extensions.cache.decorators import cache_response -from rest_framework_extensions.cache.mixins import BaseCacheResponseMixin, CacheResponseMixin -from rest_framework_tracking.mixins import LoggingMixin -from .tools.custom_pagination import LargeResultsSetPagination from apps.api.serializers import * from rest_framework_extensions.key_constructor import bits from rest_framework_extensions.key_constructor.constructors import ( DefaultKeyConstructor ) -from datetime import date - -from apps.automation.models import CollectionPlan -from apps.int_utilization.models import InterfaceUsedNew class QueryParamsKeyConstructor(DefaultKeyConstructor): @@ -43,157 +32,6 @@ class LimitSet(pagination.LimitOffsetPagination): max_limit = None -# class CategoryViewSet(viewsets.ModelViewSet): -# """ -# 处理 GET POST , 处理 /api/post// GET PUT PATCH DELETE -# """ -# queryset = Category.objects.all().order_by('id') -# serializer_class = CategorySerializer -# permission_classes = (permissions.IsAuthenticated,) -# # 配置搜索功能 -# filter_backends = (DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter) -# # 如果要允许对某些字段进行过滤,可以使用filter_fields属性。 -# filter_fields = '__all__' -# pagination_class = LimitSet -# # 设置搜索的关键字 -# search_fields = '__all__' -# -# -# -# -# -# class ModelViewSet(viewsets.ModelViewSet): -# """ -# 处理 GET POST , 处理 /api/post// GET PUT PATCH DELETE -# """ -# queryset = Model.objects.all().order_by('id') -# queryset = ModelSerializer.setup_eager_loading(queryset) -# serializer_class = ModelSerializer -# permission_classes = (permissions.IsAuthenticated,) -# # 配置搜索功能 -# filter_backends = (DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter) -# filter_fields = ('vendor', 'name') -# pagination_class = LimitSet -# -# -# class AttributelViewSet(viewsets.ModelViewSet): -# """ -# 处理 设备网络属性 GET POST , 处理 /api/post// GET PUT PATCH DELETE -# """ -# queryset = Attribute.objects.all().order_by('id') -# serializer_class = AttributeSerializer -# permission_classes = (permissions.IsAuthenticated,) -# # 配置搜索功能 -# filter_backends = (DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter) -# filter_fields = '__all__' -# pagination_class = LimitSet -# -# -# class FrameworkViewSet(viewsets.ModelViewSet): -# """ -# 处理 设备网络架构 GET POST , 处理 /api/post// GET PUT PATCH DELETE -# """ -# queryset = Framework.objects.all().order_by('id') -# serializer_class = FrameworkSerializer -# permission_classes = (permissions.IsAuthenticated,) -# # 配置搜索功能 -# filter_backends = (DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter) -# filter_fields = '__all__' -# pagination_class = LimitSet -# -# -# class NetworkDeviceFilter(django_filters.FilterSet): -# """模糊字段过滤""" -# -# serial_num = django_filters.CharFilter(lookup_expr='icontains') -# manage_ip = django_filters.CharFilter(lookup_expr='icontains') -# name = django_filters.CharFilter(lookup_expr='icontains') -# -# class Meta: -# model = NetworkDevice -# fields = '__all__' -# -# -# class NetworkDeviceViewSet(LoggingMixin, viewsets.ModelViewSet): -# """ -# 处理 GET POST , 处理 /api/post// GET PUT PATCH DELETE -# """ -# logging_methods = ['POST', 'PUT', 'PATCH', 'DELETE'] -# queryset = NetworkDevice.objects.all().order_by('-id') -# queryset = NetworkDeviceSerializer.setup_eager_loading(queryset) -# serializer_class = NetworkDeviceSerializer -# permission_classes = (permissions.IsAuthenticated,) -# # authentication_classes = (authentication.JWTAuthentication,) -# # 配置搜索功能 -# filter_backends = (DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter) -# filterset_class = NetworkDeviceFilter -# # filter_fields = ('asset', 'asset__idc__name', 'asset__idc_model__name', 'asset__rack__name') -# # pagination_class = LimitSet -# pagination_class = LargeResultsSetPagination -# # 设置搜索的关键字 -# search_fields = ('serial_num', 'manage_ip', 'bridge_mac', 'category__name', 'role__name', -# 'name', 'vendor__name', 'idc__name', 'patch_version', 'idc_model__name', 'soft_version', -# 'model__name', 'netzone__name', 'attribute__name', 'framework__name', 'rack__name', -# 'u_location', 'memo', 'status', 'ha_status') -# -# # list_cache_key_func = QueryParamsKeyConstructor() -# -# def get_queryset(self): -# """ -# expires 比 expire多一个s ,用来筛选已过期的设备数据 lt 小于 gt 大于 lte小于等于 gte 大于等于 -# :return: -# """ -# expires = self.request.query_params.get('expires', None) -# search_host_list = self.request.query_params.get('search_host_list', None) -# if search_host_list: -# if search_host_list.find('-') != -1: -# return self.queryset.filter(manage_ip__in=search_host_list.split('-')) -# else: -# return self.queryset.filter(manage_ip__in=[search_host_list]) -# # return self.queryset.filter(manage_ip__in=search_host_list) -# if expires == '1': -# return self.queryset.filter(expire__lt=date.today()) -# elif expires == '0': -# return self.queryset.filter(expire__gt=date.today()) -# else: -# return self.queryset -# -# # 重新update方法主要用来捕获更改前的字段值并赋值给self.log -# def update(self, request, *args, **kwargs): -# return super().update(request, *args, **kwargs) -# -# # 拼接log记录中data字段前后变化 -# def handle_log(self): -# # Do some stuff before saving. -# # print('before', self.log['data']) -# # print(self.request) -# if self.request.POST.get('serial_num'): -# # print('PUT记录写入') -# for key in self.request.POST.keys(): -# if key == 'serial_num': -# continue -# self.log['data'][key] += " => " + str(self.request.POST[key]) -# self.log['data'].pop('serial_num') -# if self.log['data'].get('id'): -# self.log['data'].pop('id') -# if self.log['view_method'] == 'create': -# tmp = json.loads(self.log['response']) -# if isinstance(tmp['data'], dict): -# if 'id' in tmp['data'].keys(): -# self.log['path'] += str(tmp['data']['id']) + '/' -# elif self.request.data.get('serial_num'): -# # print('PATCH记录写入') -# for key in self.request.data.keys(): -# if key == 'serial_num': -# continue -# self.log['data'][key] += " => " + str(self.request.data[key]) -# self.log['data'].pop('serial_num') -# # print(self.log) -# super(NetworkDeviceViewSet, self).handle_log() -# # print('after', self.log['data']) -# # Do some stuff after saving. - - # 任务列表 class PeriodicTaskViewSet(viewsets.ModelViewSet): # queryset = PeriodicTask.objects.all().order_by('id') @@ -210,7 +48,7 @@ class PeriodicTaskViewSet(viewsets.ModelViewSet): # list_cache_key_func = QueryParamsKeyConstructor() -class IntervalScheduleViewSet(CacheResponseMixin, viewsets.ModelViewSet): +class IntervalScheduleViewSet(viewsets.ModelViewSet): queryset = IntervalSchedule.objects.all().order_by('id') serializer_class = IntervalScheduleSerializer permission_classes = (permissions.IsAuthenticated,) @@ -221,4 +59,3 @@ class IntervalScheduleViewSet(CacheResponseMixin, viewsets.ModelViewSet): pagination_class = LimitSet # 设置搜索的关键字 search_fields = '__all__' - list_cache_key_func = QueryParamsKeyConstructor() diff --git a/netaxe/apps/asset/views.py b/netaxe/apps/asset/views.py index bb55bb6485b485058e4ab5e680984f35672abcdf..d73ea7302af7662c688cc54a3c55afbbb75e6041 100644 --- a/netaxe/apps/asset/views.py +++ b/netaxe/apps/asset/views.py @@ -1,22 +1,17 @@ import json import os from datetime import date - import django_filters from django.views import View from django.http import JsonResponse, FileResponse, Http404 from django_filters.rest_framework import DjangoFilterBackend from rest_framework.views import APIView -from rest_framework import viewsets, permissions, filters - +from rest_framework import viewsets, filters from netboost.settings import MEDIA_ROOT from apps.route_backend.views import LimitSet from utils.crypt_pwd import CryptPwd -# from scripts.crypt_pwd import CryptPwd -# from utils.excel2list import excel2list -# asset import export excel -# from utils.netops_api import netOpsApi -from utils.tools.custom_pagination import LargeResultsSetPagination +from apps.api.tools.custom_pagination import LargeResultsSetPagination +from apps.api.tools.custom_viewset_base import CustomViewBase from apps.asset.models import Idc, AssetAccount, Vendor, Role, Category, Model, Attribute, Framework, NetworkDevice, \ IdcModel, NetZone, Rack from apps.asset.serializers import IdcSerializer, AssetAccountSerializer, AssetVendorSerializer, RoleSerializer, \ @@ -95,14 +90,14 @@ class DeviceAccountView(APIView): # asset IDC -class IdcViewSet(viewsets.ModelViewSet): +class IdcViewSet(CustomViewBase): """ IDC 处理 GET POST , 处理 /api/post// GET PUT PATCH DELETE """ queryset = Idc.objects.all().order_by('-id') serializer_class = IdcSerializer - # permission_classes = (permissions.IsAuthenticated,) - permission_classes = (permissions.IsAuthenticated,) + permission_classes = () + authentication_classes = () pagination_class = LargeResultsSetPagination # 配置搜索功能 filter_backends = (DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter) @@ -111,48 +106,23 @@ class IdcViewSet(viewsets.ModelViewSet): search_fields = '__all__' -class NetZoneFilter(django_filters.FilterSet): - """模糊字段过滤""" - - # vendor = django_filters.CharFilter(lookup_expr='icontains') - # memo = django_filters.CharFilter(lookup_expr='icontains') - # name = django_filters.CharFilter(lookup_expr='icontains') - - class Meta: - model = NetZone - fields = '__all__' - - -class CmdbNetzoneModelViewSet(viewsets.ModelViewSet): +class CmdbNetzoneModelViewSet(CustomViewBase): """ 处理 GET POST , 处理 /api/post// GET PUT PATCH DELETE """ - queryset = NetZone.objects.all().order_by('id') - # queryset = NetZoneSerializer.setup_eager_loading(queryset) + queryset = NetZone.objects.all().order_by('-id') serializer_class = NetZoneSerializer - # permission_classes = (permissions.IsAuthenticated,) - permission_classes = (permissions.IsAuthenticated,) + permission_classes = () + authentication_classes = () pagination_class = LargeResultsSetPagination # 配置搜索功能 filter_backends = (DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter) - filterset_class = NetZoneFilter - # filter_fields = '__all__' - # list_cache_key_func = QueryParamsKeyConstructor() - - -class RackFilter(django_filters.FilterSet): - """模糊字段过滤""" - - # vendor = django_filters.CharFilter(lookup_expr='icontains') - # memo = django_filters.CharFilter(lookup_expr='icontains') - # name = django_filters.CharFilter(lookup_expr='icontains') - - class Meta: - model = Rack - fields = '__all__' + filter_fields = '__all__' + # 设置搜索的关键字 + search_fields = '__all__' -class CmdbRackModelViewSet(viewsets.ModelViewSet): +class CmdbRackModelViewSet(CustomViewBase): """ 处理 GET POST , 处理 /api/post// GET PUT PATCH DELETE """ @@ -160,28 +130,14 @@ class CmdbRackModelViewSet(viewsets.ModelViewSet): # queryset = NetZoneSerializer.setup_eager_loading(queryset) serializer_class = CmdbRackSerializer # permission_classes = (permissions.IsAuthenticated,) - permission_classes = (permissions.IsAuthenticated,) + permission_classes = () pagination_class = LargeResultsSetPagination # 配置搜索功能 filter_backends = (DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter) - filterset_class = RackFilter - # filter_fields = '__all__' - # list_cache_key_func = QueryParamsKeyConstructor() - - -class IdcModelFilter(django_filters.FilterSet): - """模糊字段过滤""" - - # vendor = django_filters.CharFilter(lookup_expr='icontains') - # memo = django_filters.CharFilter(lookup_expr='icontains') - # name = django_filters.CharFilter(lookup_expr='icontains') - - class Meta: - model = IdcModel - fields = '__all__' + filter_fields = '__all__' -class CmdbIdcModelViewSet(viewsets.ModelViewSet): +class CmdbIdcModelViewSet(CustomViewBase): """ 处理 GET POST , 处理 /api/post// GET PUT PATCH DELETE """ @@ -189,23 +145,22 @@ class CmdbIdcModelViewSet(viewsets.ModelViewSet): queryset = IdcModelSerializer.setup_eager_loading(queryset) serializer_class = IdcModelSerializer # permission_classes = (permissions.IsAuthenticated,) - permission_classes = (permissions.IsAuthenticated,) + permission_classes = () pagination_class = LargeResultsSetPagination # 配置搜索功能 filter_backends = (DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter) - filterset_class = IdcModelFilter - # filter_fields = '__all__' - # list_cache_key_func = QueryParamsKeyConstructor() + filter_fields = '__all__' # asset account -class AccountList(viewsets.ModelViewSet): +class AccountList(CustomViewBase): """ 处理 GET POST , 处理 /api/post// GET PUT PATCH DELETE """ queryset = AssetAccount.objects.all().order_by('id') serializer_class = AssetAccountSerializer pagination_class = LimitSet + permission_classes = () # 配置搜索功能 filter_backends = (DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter) # 如果要允许对某些字段进行过滤,可以使用filter_fields属性。 @@ -216,13 +171,13 @@ class AccountList(viewsets.ModelViewSet): # asset vendor -class VendorViewSet(viewsets.ModelViewSet): +class VendorViewSet(CustomViewBase): """ 处理 GET POST , 处理 /api/post// GET PUT PATCH DELETE """ queryset = Vendor.objects.all().order_by('id') serializer_class = AssetVendorSerializer - permission_classes = (permissions.IsAuthenticated,) + permission_classes = () # 配置搜索功能 filter_backends = (DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter) # 如果要允许对某些字段进行过滤,可以使用filter_fields属性。 @@ -233,13 +188,14 @@ class VendorViewSet(viewsets.ModelViewSet): # asset role -class AssetRoleViewSet(viewsets.ModelViewSet): +class AssetRoleViewSet(CustomViewBase): """ 处理 GET POST , 处理 /api/post// GET PUT PATCH DELETE """ queryset = Role.objects.all().order_by('id') serializer_class = RoleSerializer - permission_classes = (permissions.IsAuthenticated,) + permission_classes = () + authentication_classes = () # 配置搜索功能 filter_backends = (DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter) # 如果要允许对某些字段进行过滤,可以使用filter_fields属性。 @@ -249,13 +205,13 @@ class AssetRoleViewSet(viewsets.ModelViewSet): search_fields = '__all__' -class CategoryViewSet(viewsets.ModelViewSet): +class CategoryViewSet(CustomViewBase): """ 处理 GET POST , 处理 /api/post// GET PUT PATCH DELETE """ queryset = Category.objects.all().order_by('id') serializer_class = CategorySerializer - permission_classes = (permissions.IsAuthenticated,) + permission_classes = () # 配置搜索功能 filter_backends = (DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter) # 如果要允许对某些字段进行过滤,可以使用filter_fields属性。 @@ -265,40 +221,40 @@ class CategoryViewSet(viewsets.ModelViewSet): search_fields = '__all__' -class ModelViewSet(viewsets.ModelViewSet): +class ModelViewSet(CustomViewBase): """ 处理 GET POST , 处理 /api/post// GET PUT PATCH DELETE """ queryset = Model.objects.all().order_by('id') queryset = ModelSerializer.setup_eager_loading(queryset) serializer_class = ModelSerializer - permission_classes = (permissions.IsAuthenticated,) + permission_classes = () # 配置搜索功能 filter_backends = (DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter) filter_fields = ('vendor', 'name') pagination_class = LimitSet -class AttributelViewSet(viewsets.ModelViewSet): +class AttributelViewSet(CustomViewBase): """ 处理 设备网络属性 GET POST , 处理 /api/post// GET PUT PATCH DELETE """ queryset = Attribute.objects.all().order_by('id') serializer_class = AttributeSerializer - permission_classes = (permissions.IsAuthenticated,) + permission_classes = () # 配置搜索功能 filter_backends = (DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter) filter_fields = '__all__' pagination_class = LimitSet -class FrameworkViewSet(viewsets.ModelViewSet): +class FrameworkViewSet(CustomViewBase): """ 处理 设备网络架构 GET POST , 处理 /api/post// GET PUT PATCH DELETE """ queryset = Framework.objects.all().order_by('id') serializer_class = FrameworkSerializer - permission_classes = (permissions.IsAuthenticated,) + permission_classes = () # 配置搜索功能 filter_backends = (DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter) filter_fields = '__all__' @@ -317,19 +273,23 @@ class NetworkDeviceFilter(django_filters.FilterSet): fields = '__all__' -class NetworkDeviceViewSet(viewsets.ModelViewSet): +class NetworkDeviceViewSet(CustomViewBase): """ 处理 GET POST , 处理 /api/post// GET PUT PATCH DELETE """ queryset = NetworkDevice.objects.all().order_by('-id') queryset = NetworkDeviceSerializer.setup_eager_loading(queryset) serializer_class = NetworkDeviceSerializer - permission_classes = (permissions.IsAuthenticated,) + permission_classes = () + authentication_classes = () # authentication_classes = (authentication.JWTAuthentication,) # 配置搜索功能 filter_backends = (DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter) - filterset_class = NetworkDeviceFilter - # filter_fields = ('asset', 'asset__idc__name', 'asset__idc_model__name', 'asset__rack__name') + # filterset_class = NetworkDeviceFilter + filter_fields = ('serial_num', 'manage_ip', 'bridge_mac', 'category__name', 'role__name', + 'name', 'vendor__name', 'idc__name', 'patch_version', 'idc_model__name', 'soft_version', + 'model__name', 'netzone__name', 'attribute__name', 'framework__name', 'rack__name', + 'u_location', 'memo', 'status', 'ha_status') # pagination_class = LimitSet pagination_class = LargeResultsSetPagination # 设置搜索的关键字 @@ -338,8 +298,6 @@ class NetworkDeviceViewSet(viewsets.ModelViewSet): 'model__name', 'netzone__name', 'attribute__name', 'framework__name', 'rack__name', 'u_location', 'memo', 'status', 'ha_status') - # list_cache_key_func = QueryParamsKeyConstructor() - def get_queryset(self): """ expires 比 expire多一个s ,用来筛选已过期的设备数据 lt 小于 gt 大于 lte小于等于 gte 大于等于 @@ -361,6 +319,6 @@ class NetworkDeviceViewSet(viewsets.ModelViewSet): return self.queryset # 重新update方法主要用来捕获更改前的字段值并赋值给self.log - def update(self, request, *args, **kwargs): - print('更新', super().update(request, *args, **kwargs)) - return super().update(request, *args, **kwargs) + # def update(self, request, *args, **kwargs): + # print('更新', super().update(request, *args, **kwargs)) + # return super().update(request, *args, **kwargs) diff --git a/netaxe/apps/automation/views.py b/netaxe/apps/automation/views.py index fd3df25a89206bea55abcf6605197057fac7a620..994e324677a5752ac6f41327255910cedcc98bbd 100644 --- a/netaxe/apps/automation/views.py +++ b/netaxe/apps/automation/views.py @@ -6,8 +6,7 @@ from rest_framework import viewsets, permissions, filters, pagination from apps.route_backend.views import LimitSet from apps.automation.models import CollectionPlan from apps.automation.serializers import CollectionPlanSerializer - -from utils.tools.custom_viewset_base import CustomViewBase +from apps.api.tools.custom_viewset_base import CustomViewBase class CollectionPlanFilter(django_filters.FilterSet): @@ -29,10 +28,10 @@ class CollectionPlanViewSet(CustomViewBase): queryset = CollectionPlan.objects.all().order_by('-id') queryset = CollectionPlanSerializer.setup_eager_loading(queryset) serializer_class = CollectionPlanSerializer - permission_classes = (permissions.IsAuthenticated,) + # permission_classes = (permissions.IsAuthenticated,) # 配置搜索功能 filter_backends = (DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter,) - # filter_fields = ('vendor', 'name',) - filterset_class = CollectionPlanFilter + filter_fields = ('vendor', 'name',) + # filterset_class = CollectionPlanFilter search_fields = ('vendor', 'name',) pagination_class = LimitSet diff --git a/netaxe/apps/config_center/views.py b/netaxe/apps/config_center/views.py index 4a54cc11ce904344e73412d42ffa26559a254c31..989c44bae3664a13f3553ea921252934427bd19f 100644 --- a/netaxe/apps/config_center/views.py +++ b/netaxe/apps/config_center/views.py @@ -43,7 +43,7 @@ def jinja_render(data, template): class ConfigComplianceViewSet(CustomViewBase): queryset = ConfigCompliance.objects.all().order_by('-id') serializer_class = ConfigComplianceSerializer - permission_classes = (permissions.IsAuthenticated,) + # permission_classes = (permissions.IsAuthenticated,) pagination_class = LargeResultsSetPagination # 配置搜索功能 filter_backends = (DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter) @@ -58,7 +58,7 @@ class ConfigTemplateViewSet(CustomViewBase): """ queryset = ConfigTemplate.objects.all().order_by('-id') serializer_class = ConfigTemplateSerializer - permission_classes = (permissions.IsAuthenticated,) + # permission_classes = (permissions.IsAuthenticated,) # pagination_class = LimitSet pagination_class = LargeResultsSetPagination # 配置搜索功能 @@ -76,7 +76,7 @@ class TTPTemplateViewSet(CustomViewBase): """ queryset = TTPTemplate.objects.all().order_by('-id') serializer_class = TTPTemplateSerializer - permission_classes = (permissions.IsAuthenticated,) + # permission_classes = (permissions.IsAuthenticated,) # pagination_class = LimitSet pagination_class = LargeResultsSetPagination # 配置搜索功能 @@ -98,7 +98,9 @@ class DateEncoder(json.JSONEncoder): class GitConfig(APIView): - permission_classes = (IsAuthenticated,) + permission_classes = () + authentication_classes = () + # permission_classes = (IsAuthenticated,) # authentication_classes = (JWTAuthentication, SessionAuthentication) @@ -170,6 +172,9 @@ class GitConfig(APIView): class ComplianceResults(APIView): + permission_classes = () + authentication_classes = () + def get(self, request): get_param = request.GET.dict() if 'get_results' in get_param.keys(): @@ -191,6 +196,9 @@ class ComplianceResults(APIView): class RegexTest(APIView): + permission_classes = () + authentication_classes = () + def get(self, request): get_param = request.GET.dict() data = { @@ -222,6 +230,9 @@ class RegexTest(APIView): # TTP 前端页面接口 class TTPParse(APIView): + permission_classes = () + authentication_classes = () + def get(self, request): pass @@ -251,6 +262,9 @@ class TTPParse(APIView): # TextFSM 前端页面接口 class TextFSMParse(APIView): + permission_classes = () + authentication_classes = () + def get(self, request): get_param = request.GET.dict() if 'get_tree' in get_param.keys(): @@ -324,6 +338,8 @@ class TextFSMParse(APIView): class Jinja2View(APIView): + permission_classes = () + authentication_classes = () def get(self, request): get_param = request.GET.dict() diff --git a/netaxe/apps/int_utilization/views.py b/netaxe/apps/int_utilization/views.py index 532cf221d4d205143882b9a8723472d1bfdf26e2..ca8fcd851f637f9c88229684e3089f7dd2aad67f 100644 --- a/netaxe/apps/int_utilization/views.py +++ b/netaxe/apps/int_utilization/views.py @@ -5,8 +5,8 @@ from rest_framework import viewsets, permissions, filters, pagination from .models import InterfaceUsedNew from .serializers import InterfaceUsedNewSerializer -from utils.tools.custom_viewset_base import CustomViewBase -from utils.tools.custom_pagination import LargeResultsSetPagination +from apps.api.tools.custom_viewset_base import CustomViewBase +from apps.api.tools.custom_pagination import LargeResultsSetPagination class InterfaceUsedFilter(django_filters.FilterSet): @@ -26,13 +26,15 @@ class InterfaceUsedNewViewSet(CustomViewBase): queryset = InterfaceUsedNew.objects.all().order_by('-log_time') # queryset = InterfaceUsedNewSerializer.setup_eager_loading(queryset) serializer_class = InterfaceUsedNewSerializer - permission_classes = (permissions.IsAuthenticated,) + # permission_classes = () + # authentication_classes = () pagination_class = LargeResultsSetPagination # 配置搜索功能 filter_backends = (DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter) # 如果要允许对某些字段进行过滤,可以使用filter_fields属性。 - filterset_class = InterfaceUsedFilter + # filterset_class = InterfaceUsedFilter search_fields = ('host_ip', 'host') + filter_fields = ('host_ip', 'host') ordering_fields = ('log_time', 'id') def get_queryset(self): diff --git a/netaxe/apps/open_ipam/models.py b/netaxe/apps/open_ipam/models.py index 296c3bf7ad2c73da75dee4caa91a860184f855c7..17335ad1662850d5920dd3cb4b70365c71e3589b 100644 --- a/netaxe/apps/open_ipam/models.py +++ b/netaxe/apps/open_ipam/models.py @@ -14,8 +14,6 @@ from django.db.models import Q from django.utils.translation import gettext_lazy as _ from rest_framework.exceptions import ValidationError -# from users.models import OpLogs - class CsvImportException(Exception): pass @@ -268,23 +266,6 @@ class IpAddress(models.Model): instance.save() -# class BgBu(models.Model): -# """ """ -# name = models.CharField(verbose_name='业务线名称', max_length=20, null=False, unique=True) -# -# # def user_list(self): -# # return ','.join([i.username for i in self.authusers_set.all()]) -# -# def __str__(self): -# return self.name -# -# class Meta: -# verbose_name = '业务线表' -# verbose_name_plural = '业务线表' -# db_table = 'ipam_bgbu' # 通过db_table自定义数据表名 -# indexes = [models.Index(fields=['name', ]), ] - - class TagsModel(models.Model): bg_color = models.CharField(max_length=100, blank=True, verbose_name='bg_color') compress = models.CharField(max_length=100, blank=True, verbose_name='compress') diff --git a/netaxe/apps/open_ipam/serializers.py b/netaxe/apps/open_ipam/serializers.py index 82433478f014a0969afdd811803b747a6bcbf409..bd81de3ad97bcb1f0c6080e665dd2d7d4a40d666 100644 --- a/netaxe/apps/open_ipam/serializers.py +++ b/netaxe/apps/open_ipam/serializers.py @@ -50,15 +50,3 @@ class HostsResponseSerializer(serializers.Serializer): # bgbu = serializers.CharField() description = serializers.CharField() lastOnlineTime = serializers.DateField() - - -class IntervalScheduleSerializer(serializers.ModelSerializer): - class Meta: - model = IntervalSchedule - fields = '__all__' - - -class PeriodicTaskSerializer(serializers.ModelSerializer): - class Meta: - model = PeriodicTask - fields = '__all__' diff --git a/netaxe/apps/open_ipam/urls.py b/netaxe/apps/open_ipam/urls.py index 903243fdf04b55cd07e8798c6267e44d7e14bae2..ed482d887e76b76793a3d5d53f9369d7dac7269a 100644 --- a/netaxe/apps/open_ipam/urls.py +++ b/netaxe/apps/open_ipam/urls.py @@ -1,31 +1,15 @@ from django.urls import path, include -from django.views.decorators.csrf import csrf_exempt from rest_framework.routers import DefaultRouter -from .views import SubnetHostsView, AvailableIpView, SubnetApiViewSet, IpAddressApiViewSet, SubnetAddressView, \ - IpAmSubnetTreeView, PeriodicTaskViewSet, IpAmHandelView, IntervalScheduleViewSet +from .views import SubnetHostsView, AvailableIpView, SubnetAddressView, IpAmSubnetTreeView, IpAmHandleView router = DefaultRouter() -# -router.register(r'subnet_list', SubnetApiViewSet) # 获取子网段列表 -router.register(r'ip_address_list', IpAddressApiViewSet) -router.register(r'periodic_task', PeriodicTaskViewSet) -router.register(r'interval_schedule', IntervalScheduleViewSet) + urlpatterns = [ path(r'', include(router.urls)), - # path(r'api/', include(router.urls)), - # path('jobCenter/', JobCenterView.as_view(), name='jobCenter'), - path('subnet_tree/', IpAmSubnetTreeView.as_view(), name='subnet_tree'), - path('address_handel/', csrf_exempt(IpAmHandelView.as_view()), name='address_handel'), - + path('subnet_tree/', IpAmSubnetTreeView.as_view(), name='subnet_tree'), # get_subnet_tree + path('address_handel/', IpAmHandleView.as_view(), name='address_handle'), # ip_addr_handle path('subnet//ip_address/', SubnetAddressView.as_view(), name='subnet_ip_address'), - - # 供admin后台展示,暂未修改过多逻辑 - path('subnet//hosts/', SubnetHostsView.as_view(), name='hosts'), - # path('subnet//hosts/', SubnetHostsView.as_view(), name='hosts'), - path( - 'subnet//get-next-available-ip/', - AvailableIpView.as_view(), - name='get_next_available_ip', - ), + path('subnet//hosts/', SubnetHostsView.as_view(), name='hosts'), # admin- ip_addr_by_subnet_id + path('subnet//get-next-available-ip/', AvailableIpView.as_view(), name='get_next_available_ip'), ] diff --git a/netaxe/apps/open_ipam/views.py b/netaxe/apps/open_ipam/views.py index 512799f2b00345adc31264fd6b1c95ab5015bbfd..fa0f5832083cfd8d2a74ac5d3c38bf96f5dfd910 100644 --- a/netaxe/apps/open_ipam/views.py +++ b/netaxe/apps/open_ipam/views.py @@ -1,18 +1,10 @@ import json from collections import OrderedDict import ipaddr -import netaddr -from celery import current_app -from django.db.models import Sum -from django.http import JsonResponse, HttpResponse -from django.shortcuts import render - -# Create your views here. -from django_celery_beat.models import CrontabSchedule, PeriodicTask, IntervalSchedule -from django_filters.rest_framework import DjangoFilterBackend -from kombu.utils.json import loads +from django.http import JsonResponse + from netaddr import iter_iprange -from rest_framework import serializers, pagination, viewsets, permissions, filters +from rest_framework import serializers, pagination from django.utils.translation import gettext_lazy as _ from rest_framework.authentication import SessionAuthentication from rest_framework.generics import ListAPIView, get_object_or_404, RetrieveAPIView @@ -20,28 +12,19 @@ from rest_framework.permissions import DjangoModelPermissions from rest_framework.response import Response from rest_framework.utils.urls import replace_query_param, remove_query_param from rest_framework.views import APIView - -from netboost.celery import app -# from .tasks import get_all_tasks from .models import Subnet, IpAddress, TagsModel -from .serializers import HostsResponseSerializer, SubnetSerializer, IpAddressSerializer, \ - PeriodicTaskSerializer, IntervalScheduleSerializer, TagsModelSerializer -from utils.custom.pagination import LargeResultsSetPagination -from utils.custom.viewset import CustomViewBase +from .serializers import HostsResponseSerializer, TagsModelSerializer from utils.ipam_utils import IpAmForNetwork -# from ..route_backend.tasks import get_tasks class HostsResponse(object): def __init__(self, address, used, tag, subnet, lastOnlineTime, description): - # def __init__(self, address, used, tag, subnet): self.address = address self.used = used self.tag = tag self.subnet = subnet self.lastOnlineTime = lastOnlineTime self.description = description - # self.bgbu = bgbu class HostsSet: @@ -117,6 +100,7 @@ class HostsSet: return index +# 地址列表分页及数据格式化方法 class HostsListPagination(pagination.BasePagination): limit = 256 start_query_param = 'start' @@ -196,6 +180,7 @@ class ProtectedAPIMixin(object): throttle_scope = 'ipam' +# 给admin后台使用,根据子网id获取地址信息 class SubnetHostsView(ProtectedAPIMixin, ListAPIView): subnet_model = Subnet queryset = Subnet.objects.none() @@ -209,6 +194,7 @@ class SubnetHostsView(ProtectedAPIMixin, ListAPIView): return qs +# 获取可用地址 class AvailableIpView(RetrieveAPIView): subnet_model = Subnet queryset = IpAddress.objects.none() @@ -216,77 +202,10 @@ class AvailableIpView(RetrieveAPIView): def get(self, request, *args, **kwargs): subnet = get_object_or_404(self.subnet_model, pk=self.kwargs['subnet_id']) - print(subnet) return Response(subnet.get_next_available_ip()) -# 子网网段API -class SubnetApiViewSet(CustomViewBase): - # subnet_model = Subnet - permission_classes = (permissions.IsAuthenticated,) - queryset = Subnet.objects.all().order_by('-id') - serializer_class = SubnetSerializer - pagination_class = LargeResultsSetPagination - # 配置搜索功能 - filter_backends = (DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter) - filter_fields = ('mask', 'name') - - -# IP地址Api -class IpAddressApiViewSet(CustomViewBase): - # subnet_model = Subnet - permission_classes = (permissions.IsAuthenticated,) - queryset = IpAddress.objects.all().order_by('-id') - serializer_class = IpAddressSerializer - pagination_class = LargeResultsSetPagination - # 配置搜索功能 - filter_backends = (DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter) - filter_fields = '__all__' - - -class LimitSet(pagination.LimitOffsetPagination): - # 每页默认几条 - default_limit = 10 - # 设置传入页码数参数名 - page_query_param = "page" - # 设置传入条数参数名 - limit_query_param = 'limit' - # 设置传入位置参数名 - offset_query_param = 'start' - # 最大每页显示条数 - max_limit = None - - -# 任务列表 -class PeriodicTaskViewSet(viewsets.ModelViewSet): - # queryset = PeriodicTask.objects.all().order_by('id') - queryset = PeriodicTask.objects.exclude(task__startswith='celery').order_by('id') - serializer_class = PeriodicTaskSerializer - permission_classes = (permissions.IsAuthenticated,) - # 配置搜索功能 - filter_backends = (DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter) - # 如果要允许对某些字段进行过滤,可以使用filter_fields属性。 - filter_fields = '__all__' - pagination_class = LimitSet - # 设置搜索的关键字 - search_fields = '__all__' - # list_cache_key_func = QueryParamsKeyConstructor() - - -class IntervalScheduleViewSet(viewsets.ModelViewSet): - queryset = IntervalSchedule.objects.all().order_by('id') - serializer_class = IntervalScheduleSerializer - permission_classes = (permissions.IsAuthenticated,) - # 配置搜索功能 - filter_backends = (DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter) - # 如果要允许对某些字段进行过滤,可以使用filter_fields属性。 - filter_fields = '__all__' - pagination_class = LimitSet - # 设置搜索的关键字 - search_fields = '__all__' - # list_cache_key_func = QueryParamsKeyConstructor() - - +# 根据网段ID获取对应下面IP地址信息 class SubnetAddressView(ListAPIView): subnet_model = Subnet queryset = Subnet.objects.none() @@ -302,6 +221,8 @@ class SubnetAddressView(ListAPIView): # 获取subnet_tree class IpAmSubnetTreeView(APIView): + permission_classes = () + authentication_classes = () def get(self, request): get_params = request.GET.dict() if 'subnet' in get_params: @@ -319,7 +240,9 @@ class IpAmSubnetTreeView(APIView): # 地址操作 -class IpAmHandelView(APIView): +class IpAmHandleView(APIView): + permission_classes = () + authentication_classes = () def post(self, request): update = request.POST.get('update') range_update = request.POST.get('range_update') @@ -363,12 +286,10 @@ class IpAmHandelView(APIView): IpAddress.objects.filter(ip_address=delete_info['ipaddr']).delete() res = {'message': '地址回收成功', 'code': 200, 'delete_ip_list': [j['ipaddr'] for j in delete_ip_list]} return JsonResponse(res, safe=True) - if description: Subnet.objects.filter(id=subnet_id).update(description=description) res = {'message': '网段描述更新成功', 'code': 200, } return JsonResponse(res, safe=True) - if add_subnet: try: # print(add_master_id, type(add_master_id)) @@ -417,60 +338,3 @@ class IpAmHandelView(APIView): except Exception as e: res = {'message': e, 'code': 400, } return JsonResponse(res, safe=True) - - -# # 作业中心taskList -# class JobCenterView(APIView): -# def get(self, request): -# # Operationinfo = 'None' -# get_current_tasks = request.GET.get('current_tasks') -# get_crontab_schedules = request.GET.get('crontab_schedules') -# get_queues = request.GET.get('get_queues') -# celery_app = current_app -# if get_current_tasks: -# res = get_tasks() -# # print(res.get()) -# # while True: -# # if res.ready(): -# # result = res.get() -# # break -# result = json.loads(res) -# return JsonResponse( -# {'code': 200, 'data': result['result'], 'count': len(result['result'])}) -# if get_crontab_schedules: -# from django.core import serializers -# crontab_schedules = serializers.serialize("json", CrontabSchedule.objects.all()) -# return JsonResponse( -# {'code': 200, 'data': json.loads(crontab_schedules), 'count': len(json.loads(crontab_schedules))}) -# if get_queues: -# queues_list = [queue.name for queue in app.conf.task_queues] -# return JsonResponse({'code': 200, 'data': queues_list, 'count': len(queues_list)}) -# -# def post(self, request): -# """ run tasks""" -# celery_app = current_app -# f = request.POST -# f = json.loads(f['data']) -# taskname = f['task'] -# args = f['args'] -# kwargs = f['kwargs'] -# queue = f['queue'] -# celery_app.loader.import_default_modules() -# tasks = [(celery_app.tasks.get(taskname), -# loads(args), -# loads(kwargs), -# queue)] -# if any(t[0] is None for t in tasks): -# for i, t in enumerate(tasks): -# if t[0] is None: -# break -# return JsonResponse({'code': 400, 'data': None}, safe=False) -# task_ids = [task.apply_async(args=args, kwargs=kwargs, queue=queue) -# if queue and len(queue) -# else task.apply_async(args=args, kwargs=kwargs) -# for task, args, kwargs, queue in tasks] -# if task_ids[0] == None: -# return JsonResponse({'code': 400, 'data': '执行失败'}, safe=False) -# # list: -# # print(task_ids[0],str(task_ids[0])) -# return JsonResponse({'code': 200, 'data': str(task_ids[0])}, safe=False) diff --git a/netaxe/apps/route_backend/views.py b/netaxe/apps/route_backend/views.py index f0044d6d8a9703b8096751cff4b93598c2a6eebb..ba4ed648047f13ebd87b9223fe326d86d25c75a2 100644 --- a/netaxe/apps/route_backend/views.py +++ b/netaxe/apps/route_backend/views.py @@ -5,9 +5,6 @@ from django.views import View from django.db.models import Count from django.http import JsonResponse from django_filters.rest_framework import DjangoFilterBackend -from rest_framework_extensions.cache.mixins import BaseCacheResponseMixin, CacheResponseMixin -from django_celery_beat.models import PeriodicTask, PeriodicTasks, CrontabSchedule, IntervalSchedule - # 根据角色的菜单组件 from celery import current_app from kombu.utils.json import loads @@ -15,11 +12,10 @@ from rest_framework.views import APIView from rest_framework_extensions.key_constructor import bits from rest_framework import viewsets, permissions, filters, pagination from rest_framework_extensions.key_constructor.constructors import DefaultKeyConstructor - from apps.route_backend.serializers import * from apps.asset.models import NetworkDevice from apps.automation.models import CollectionPlan - +from apps.api.tools.custom_viewset_base import CustomViewBase from .tasks import get_tasks from netboost import settings from netboost.celery import app @@ -33,6 +29,7 @@ from .serializers import CrontabSerializer, IntervalSerializer class QueryParamsKeyConstructor(DefaultKeyConstructor): all_query_params = bits.QueryParamsKeyBit() + class LimitSet(pagination.LimitOffsetPagination): # 每页默认几条 default_limit = 10 @@ -45,7 +42,11 @@ class LimitSet(pagination.LimitOffsetPagination): # 最大每页显示条数 max_limit = None + class DashboardChart(APIView): + permission_classes = () + authentication_classes = () + def get(self, request): get_params = request.GET.dict() if "device_idc_dimension" in get_params: @@ -61,6 +62,9 @@ class DashboardChart(APIView): class WebSshView(APIView): + permission_classes = () + authentication_classes = () + def get(self, request): get_param = request.GET.dict() server_obj = NetworkDevice.objects.get(id=get_param.get('pk')) @@ -101,6 +105,9 @@ class WebSshView(APIView): # 设备采集方案 class DeviceCollectView(APIView): + permission_classes = () + authentication_classes = () + def get(self, request): get_param = request.GET.dict() if all(k in get_param for k in ("vendor", "netconf_class")): @@ -131,6 +138,9 @@ class DeviceCollectView(APIView): # 自动化chart class AutomationChart(APIView): + permission_classes = () + authentication_classes = () + def get(self, request): get_params = request.GET.dict() @@ -205,6 +215,9 @@ class DispatchManageView(View): # 作业中心taskList class JobCenterView(APIView): + permission_classes = () + authentication_classes = () + def get(self, request): # Operationinfo = 'None' get_current_tasks = request.GET.get('current_tasks') @@ -258,11 +271,12 @@ class JobCenterView(APIView): return JsonResponse({'code': 200, 'data': str(task_ids[0])}, safe=False) # 任务列表 -class PeriodicTaskViewSet(viewsets.ModelViewSet): +class PeriodicTaskViewSet(CustomViewBase): # queryset = PeriodicTask.objects.all().order_by('id') queryset = PeriodicTask.objects.exclude(task__startswith='celery').order_by('id') serializer_class = PeriodicTaskSerializer - permission_classes = (permissions.IsAuthenticated,) + permission_classes = () + authentication_classes = () # 配置搜索功能 filter_backends = (DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter) # 如果要允许对某些字段进行过滤,可以使用filter_fields属性。 @@ -273,10 +287,11 @@ class PeriodicTaskViewSet(viewsets.ModelViewSet): # list_cache_key_func = QueryParamsKeyConstructor() -class IntervalScheduleViewSet(CacheResponseMixin, viewsets.ModelViewSet): +class IntervalScheduleViewSet(CustomViewBase): queryset = IntervalSchedule.objects.all().order_by('id') serializer_class = IntervalScheduleSerializer - permission_classes = (permissions.IsAuthenticated,) + permission_classes = () + authentication_classes = () # 配置搜索功能 filter_backends = (DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter) # 如果要允许对某些字段进行过滤,可以使用filter_fields属性。 @@ -284,4 +299,3 @@ class IntervalScheduleViewSet(CacheResponseMixin, viewsets.ModelViewSet): pagination_class = LimitSet # 设置搜索的关键字 search_fields = '__all__' - list_cache_key_func = QueryParamsKeyConstructor() \ No newline at end of file diff --git a/netaxe/apps/topology/tasks.py b/netaxe/apps/topology/tasks.py index ee66956850857fc9705a9c598ea846706dccea44..60a4d33ad76048a97e1c74fe3e9823582fdf94d1 100644 --- a/netaxe/apps/topology/tasks.py +++ b/netaxe/apps/topology/tasks.py @@ -44,7 +44,6 @@ class TopologyTask: return MongoNetOps.get_topology(self.topology.name) # 删除拓扑 - @property def del_graph(self): self.topology.delete() MongoNetOps.del_topology(self.topology.name) diff --git a/netaxe/apps/topology/views.py b/netaxe/apps/topology/views.py index d27e823ca650ace686c50e24db1f35a50de5f602..f5d19381e7c083643fdc697658641180f74a0b1a 100644 --- a/netaxe/apps/topology/views.py +++ b/netaxe/apps/topology/views.py @@ -14,8 +14,8 @@ from apps.topology.tasks import TopologyTask from apps.topology.models import Topology from .serializers import TopologySerializer from utils.db.mongo_ops import MongoOps, MongoNetOps -from utils.tools.custom_pagination import LargeResultsSetPagination -from utils.tools.custom_viewset_base import CustomViewBase +from apps.api.tools.custom_viewset_base import CustomViewBase +from apps.api.tools.custom_pagination import LargeResultsSetPagination # Create your views here. # 设备二层接口表 @@ -51,7 +51,9 @@ class TopologyViewSet(CustomViewBase): # 拓扑显示 class TopologyShow(APIView): - permission_classes = (IsAuthenticated,) + # permission_classes = (IsAuthenticated,) + permission_classes = () + authentication_classes = () def get(self, request): get_param = request.GET.dict() @@ -134,7 +136,7 @@ class TopologyShow(APIView): # 删除拓扑图 if all(k in post_param for k in ("name", "del_graph")): _TopologyTask = TopologyTask(post_param['name']) - _TopologyTask.del_graph + _TopologyTask.del_graph() data = { "code": 200, "data": [], @@ -195,7 +197,8 @@ class TopologyShow(APIView): # 图标库 class IconView(APIView): - permission_classes = (IsAuthenticated,) + permission_classes = () + authentication_classes = () def get(self, request): get_param = request.GET.dict() diff --git a/netaxe/apps/users/views/user.py b/netaxe/apps/users/views/user.py index 5f79600fe4cd534d7d2857d7ac1c0d71df47e93f..e1aeadae30d83791761204275f8df2986cca2c93 100644 --- a/netaxe/apps/users/views/user.py +++ b/netaxe/apps/users/views/user.py @@ -4,7 +4,7 @@ from rest_framework import serializers, permissions, filters, viewsets from rest_framework.decorators import action from rest_framework.permissions import IsAuthenticated from django_filters.rest_framework import DjangoFilterBackend -from utils.tools.custom_pagination import LargeResultsSetPagination +from apps.api.tools.custom_pagination import LargeResultsSetPagination from apps.users.models import UserProfile, BgBu from apps.users.serializers import BgBuSerializer from apps.system.views.role import RoleSerializer diff --git a/netaxe/media/ipam/netaxe_ipam_ip_fail-2023-02-16.log b/netaxe/media/ipam/netaxe_ipam_ip_fail-2023-02-16.log new file mode 100644 index 0000000000000000000000000000000000000000..7f7b8a97183a8bf7811773af85378a8adabaed05 --- /dev/null +++ b/netaxe/media/ipam/netaxe_ipam_ip_fail-2023-02-16.log @@ -0,0 +1,3 @@ +10.254.15.241 +100.80.0.13 +10.255.253.6 diff --git a/netaxe/media/ipam/netaxe_ipam_ip_fail-2023-03-13.log b/netaxe/media/ipam/netaxe_ipam_ip_fail-2023-03-13.log new file mode 100644 index 0000000000000000000000000000000000000000..bdae263b72fa0a820e62ff77148b314e69c66770 --- /dev/null +++ b/netaxe/media/ipam/netaxe_ipam_ip_fail-2023-03-13.log @@ -0,0 +1 @@ +192.168.1.10 diff --git a/netaxe/netboost/__init__.py b/netaxe/netboost/__init__.py index 1a842383513c37e246ca1fd082c13d59f6fd5023..f819806346e5d77f98630ef263b59633ba65eeeb 100644 --- a/netaxe/netboost/__init__.py +++ b/netaxe/netboost/__init__.py @@ -1,5 +1,11 @@ from __future__ import absolute_import, unicode_literals + +import sys + import pymysql + +from utils.custom.nacos import nacos +from . import settings from .celery import app as celery_app # Fake PyMySQL's version and install as MySQLdb @@ -7,4 +13,9 @@ from .celery import app as celery_app pymysql.version_info = (1, 4, 2, "final", 0) pymysql.install_as_MySQLdb() -__all__ = ['celery_app'] \ No newline at end of file +__all__ = ['celery_app'] +if sys.argv[1] not in ["makemigrations", "migrate"]: + # 注册服务 + nacosServer = nacos(ip=settings.SERVERIP, port=8848) + nacosServer.registerService(serviceIp=settings.SERVERIP, servicePort=settings.SERVERPORT, serviceName="auth", groupName="default") + nacosServer.healthyCheck() \ No newline at end of file diff --git a/netaxe/netboost/settings.py b/netaxe/netboost/settings.py index 7d4e789bc915ccbc1e273dc99c4a1833c41e7d1f..e2d421ae448679ac8dcd08e199d2480884dad65a 100644 --- a/netaxe/netboost/settings.py +++ b/netaxe/netboost/settings.py @@ -79,8 +79,10 @@ MIDDLEWARE = [ "django.contrib.auth.middleware.AuthenticationMiddleware", "django.contrib.messages.middleware.MessageMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware", - # "utils.custom.middleware.ApiLoggingMiddleware", + "utils.custom.middleware.CorsMiddleWare", ] +# MIDDLEWARE +=[''] + ROOT_URLCONF = "netboost.urls" @@ -292,7 +294,7 @@ REST_FRAMEWORK = { "django_filters.rest_framework.DjangoFilterBackend", ), "DEFAULT_PERMISSION_CLASSES": ( - "rest_framework.permissions.IsAuthenticated", + # "rest_framework.permissions.IsAuthenticated", # "rest_framework.permissions.DjangoModelPermissions", # "rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly" ), @@ -300,7 +302,7 @@ REST_FRAMEWORK = { # "rest_framework.authentication.BasicAuthentication", # "rest_framework.authentication.SessionAuthentication", # "rest_framework.authentication.TokenAuthentication", - "rest_framework_simplejwt.authentication.JWTAuthentication", + # "rest_framework_simplejwt.authentication.JWTAuthentication", # "apps.api.authentication.ExpiringTokenAuthentication", ), "EXCEPTION_HANDLER": "utils.custom.exception.CustomExceptionHandler", # 自定义的异常处理 diff --git a/netaxe/nginx.conf b/netaxe/nginx.conf new file mode 100644 index 0000000000000000000000000000000000000000..262e1808528d0b041a14727e277220ac89a15c1c --- /dev/null +++ b/netaxe/nginx.conf @@ -0,0 +1,47 @@ +upstream wsbackend { + server cmdb-server:8001; + } +server { + listen 9999; + server_name 0.0.0.0; + + keepalive_timeout 3600; + client_max_body_size 5120M; + + location / { + include uwsgi_params; + proxy_pass http://cmdb-server:8001; + proxy_redirect off; + proxy_connect_timeout 3800s; + proxy_read_timeout 3600s; + proxy_http_version 1.1; + add_header 'Access-Control-Allow-Origin' *; + #允许请求的header + add_header 'Access-Control-Allow-Headers' *; + #允许请求的方法,比如 GET,POST,PUT,DELETE + add_header 'Access-Control-Allow-Methods' *; + + } + location /ws/{ + proxy_pass http://wsbackend; + proxy_redirect off; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header Connection "upgrade"; + add_header 'Access-Control-Allow-Origin' *; + #允许请求的header + add_header 'Access-Control-Allow-Headers' *; + #允许请求的方法,比如 GET,POST,PUT,DELETE + add_header 'Access-Control-Allow-Methods' *; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-Host $server_name; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + } + location /static/ { + expires 2d; + autoindex off; + add_header Cache-Control private; + alias /home/netaxe/static/; + } +} \ No newline at end of file diff --git a/netaxe/utils/custom/middleware.py b/netaxe/utils/custom/middleware.py index d26272c9e50e483aef0ae98c4b676fa362770717..d609f71eb2a6f63e404bc938636890be64cf2bed 100644 --- a/netaxe/utils/custom/middleware.py +++ b/netaxe/utils/custom/middleware.py @@ -87,3 +87,15 @@ class ApiLoggingMiddleware(MiddlewareMixin): if self.methods == 'ALL' or request.method in self.methods: self.__handle_response(request, response) return response + + +class CorsMiddleWare(MiddlewareMixin): + def process_response(self, request, response): + if request.META.get("HTTP_REFERER") != None: + response["Access-Control-Allow-Methods"] = "*" + response["Access-Control-Allow-Credentials"] = True + response['Access-Control-Allow-Headers'] = "Authorization" + response["Access-Control-Allow-Origin"] = "/".join(request.META.get("HTTP_REFERER").split("/")[0:3]) + return response + else: + return response diff --git a/netaxe/utils/custom/pagination.py b/netaxe/utils/custom/pagination.py index 9ad1712e2a58b41104b60e98c607fb6806243017..646bdc94a8f723af85bb92c974108512bf0c5b9c 100644 --- a/netaxe/utils/custom/pagination.py +++ b/netaxe/utils/custom/pagination.py @@ -118,21 +118,3 @@ class SubnetAddressPagination(pagination.BasePagination): return replace_query_param( url, self.start_query_param, self.queryset[offset].address ) -# class LargeResultsSetPagination(PageNumberPagination): -# """ -# -# """ -# def get_paginated_response(self, data): -# code = 200 -# msg = 'success' -# if not data: -# code = 404 -# msg = "Data Not Found" -# return Response(OrderedDict([ -# ('code', code), -# ('msg', msg), -# ('count', self.page.paginator.count), -# ('next', self.get_next_link()), -# ('previous', self.get_previous_link()), -# ('results', data) -# ])) diff --git a/netaxe/utils/tools/custom_exception.py b/netaxe/utils/tools/custom_exception.py deleted file mode 100644 index 463d5945674857791194a9b1df75fb1384340fbb..0000000000000000000000000000000000000000 --- a/netaxe/utils/tools/custom_exception.py +++ /dev/null @@ -1,69 +0,0 @@ -# -*- coding: utf-8 -*- -""" -------------------------------------------------- - File Name: custom_exception - Description: - Author: Lijiamin - date: 2022/7/29 11:05 -------------------------------------------------- - Change Activity: - 2022/7/29 11:05 -------------------------------------------------- -""" -from rest_framework.views import exception_handler - - -def custom_exception_handler(exc, context): - # Call REST framework's default exception handler first, - # to get the standard error response. - response = exception_handler(exc, context) - # 这个循环是取第一个错误的提示用于渲染 - # print(response.data) - message = '' - if response is None: - return response - for index, value in enumerate(response.data): - if index == 0: - key = value - value = response.data[key] - # print('value', value, type(value[0])) - if isinstance(value, str): - message = value - else: - message = key + value[0] - # Now add the HTTP status code to the response. - if response is not None: - # print("response.data", response.data, type(response.data)) - # print("response", response, type(response)) - # print(str(response.data)) - response.data.clear() - if not response.status_code: - response.status_code = 200 - response.data['code'] = response.status_code - response.data['data'] = [] - if response.status_code == 404: - try: - # response.data['message'] = response.data.pop('detail') - response.data['message'] = "Not found" - except KeyError: - response.data['message'] = "Not found" - - if response.status_code == 400: - - # response.data['message'] = 'Input error' - response.data['message'] = message - - elif response.status_code == 401: - response.data['message'] = "Auth failed" - - elif response.status_code >= 500: - response.data['message'] = "Internal service errors" - - elif response.status_code == 403: - response.data['message'] = "Access denied" - - elif response.status_code == 405: - response.data['message'] = 'Request method error' - response.code = response.status_code - response.status_code = 200 - return response \ No newline at end of file diff --git a/netaxe/utils/tools/custom_json_response.py b/netaxe/utils/tools/custom_json_response.py deleted file mode 100644 index 4beb6a28392b32f1f67bd03280419a12acc243d1..0000000000000000000000000000000000000000 --- a/netaxe/utils/tools/custom_json_response.py +++ /dev/null @@ -1,47 +0,0 @@ -# -*- coding: utf-8 -*- -# @Time : 2020/11/12 18:24 -# @Author : LiJiaMin -# @Site : -# @File : custom_json_response.py -# @Software: PyCharm - -import six -from rest_framework.response import Response -from rest_framework.serializers import Serializer - - -class JsonResponse(Response): - """ - An HttpResponse that allows its data to be rendered into - arbitrary media types. - """ - - def __init__(self, data=None, code=None, msg=None, - status=None, - template_name=None, headers=None, - exception=False, content_type=None): - """ - Alters the init arguments slightly. - For example, drop 'template_name', and instead use 'data'. - Setting 'renderer' and 'media_type' will typically be deferred, - For example being set automatically by the `APIView`. - """ - super(Response, self).__init__(None, status=status) - - if isinstance(data, Serializer): - msg = ( - 'You passed a Serializer instance as data, but ' - 'probably meant to pass serialized `.data` or ' - '`.error`. representation.' - ) - raise AssertionError(msg) - - self.data = {"code": code, "message": msg, "data": data} - # print(self.data) - self.template_name = template_name - self.exception = exception - self.content_type = content_type - - if headers: - for name, value in six.iteritems(headers): - self[name] = value \ No newline at end of file diff --git a/netaxe/utils/tools/custom_pagination.py b/netaxe/utils/tools/custom_pagination.py deleted file mode 100644 index 6ba60b63c9566be49654aadbfed1efbc02e6514b..0000000000000000000000000000000000000000 --- a/netaxe/utils/tools/custom_pagination.py +++ /dev/null @@ -1,47 +0,0 @@ -# -*- coding: utf-8 -*- -""" -------------------------------------------------- - File Name: custom_pagination - Description: - Author: Lijiamin - date: 2022/7/29 10:54 -------------------------------------------------- - Change Activity: - 2022/7/29 10:54 -------------------------------------------------- -""" -from rest_framework.pagination import LimitOffsetPagination -from collections import OrderedDict -from rest_framework.response import Response - - -class LargeResultsSetPagination(LimitOffsetPagination): - # 每页默认几条 - default_limit = 10 - # 设置传入页码数参数名 - page_query_param = "page" - # 设置传入条数参数名 - limit_query_param = 'limit' - # 设置传入位置参数名 - offset_query_param = 'start' - # 最大每页显示条数 - max_limit = None - page_size = 10 - page_size_query_param = 'page_size' - max_page_size = 100000 - - def get_paginated_response(self, data): - code = 200 - msg = 'success' - if not data: - code = 404 - msg = "Data Not Found" - - return Response(OrderedDict([ - ('code', code), - ('msg', msg), - ('count', self.count), - ('next', self.get_next_link()), - ('previous', self.get_previous_link()), - ('results', data), - ])) diff --git a/netaxe/utils/tools/custom_viewset_base.py b/netaxe/utils/tools/custom_viewset_base.py deleted file mode 100644 index 9bc02e09226c2239fdab8a1e80348c7624b19e43..0000000000000000000000000000000000000000 --- a/netaxe/utils/tools/custom_viewset_base.py +++ /dev/null @@ -1,66 +0,0 @@ -# -*- coding: utf-8 -*- -# @Time : 2020/11/12 18:25 -# @Author : LiJiaMin -# @Site : -# @File : custom_viewset_base.py -# @Software: PyCharm - -from django_filters import rest_framework -from rest_framework import filters -from rest_framework import status -from rest_framework import viewsets - -from .custom_json_response import JsonResponse - - -class CustomViewBase(viewsets.ModelViewSet): - queryset = '' - serializer_class = '' - permission_classes = () - filter_fields = () - search_fields = () - filter_backends = (rest_framework.DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter,) - - def create(self, request, *args, **kwargs): - serializer = self.get_serializer(data=request.data) - print(request.data) - serializer.is_valid(raise_exception=True) - self.perform_create(serializer) - headers = self.get_success_headers(serializer.data) - return JsonResponse(data=serializer.data, msg="success", code=201, - status=status.HTTP_201_CREATED, headers=headers) - - def list(self, request, *args, **kwargs): - queryset = self.filter_queryset(self.get_queryset()) - page = self.paginate_queryset(queryset) - if page is not None: - serializer = self.get_serializer(page, many=True) - return self.get_paginated_response(data=serializer.data) - - serializer = self.get_serializer(queryset, many=True) - return JsonResponse(data=serializer.data, code=200, msg="success", status=status.HTTP_200_OK) - - def retrieve(self, request, *args, **kwargs): - instance = self.get_object() - serializer = self.get_serializer(instance) - return JsonResponse(data=serializer.data, code=200, msg="success", status=status.HTTP_200_OK) - - def update(self, request, *args, **kwargs): - partial = kwargs.pop('partial', False) - instance = self.get_object() - serializer = self.get_serializer(instance, data=request.data, partial=partial) - serializer.is_valid(raise_exception=True) - self.perform_update(serializer) - - if getattr(instance, '_prefetched_objects_cache', None): - # If 'prefetch_related' has been applied to a queryset, we need to - # forcibly invalidate the prefetch cache on the instance. - instance._prefetched_objects_cache = {} - - return JsonResponse(data=serializer.data, msg="success", code=200, status=status.HTTP_200_OK) - - def destroy(self, request, *args, **kwargs): - instance = self.get_object() - self.perform_destroy(instance) - # return JsonResponse(data=[], code=204, msg="delete resource success", status=status.HTTP_204_NO_CONTENT) - return JsonResponse(data=[], code=204, msg="delete resource success", status=status.HTTP_200_OK) diff --git a/web/.env.development b/web/.env.development index e100b3db9ac4db470ccbbc95909dd651866f9c50..1c2e374c956aa9fe49622b34c5164283d415cfa2 100644 --- a/web/.env.development +++ b/web/.env.development @@ -1,8 +1,9 @@ // 开发环境配置 //VITE_BASIC_URL = http://127.0.0.1:8001/ -//VITE_BASIC_URL = http://10.254.0.110:10080/ +VITE_BASIC_URL = http://10.254.2.219:9999/ +//VITE_BASIC_URL = http://10.254.0.111:9999/ //VITE_BASIC_URL = http://10.254.0.111:9999/ -//VITE_BASIC_URL = http://10.254.0.110:10089/ //VITE_BASIC_URL = http://10.254.0.111:9999/ //VITE_BASIC_URL = http://10.254.2.188:8000/ -VITE_BASIC_URL = http://10.254.0.111:9080/ +//VITE_BASIC_URL = http://10.254.0.111:9080/ +VITE_BASIC_RBAC = http://10.254.2.219:9080/ \ No newline at end of file diff --git a/web/nginx.conf b/web/nginx.conf index d5665ebc9710303ace9995090e7f841343f75d30..e40c0d7b69bf5427ab8906669befef2985e134a4 100644 --- a/web/nginx.conf +++ b/web/nginx.conf @@ -1,58 +1,81 @@ server { listen 80; server_name netaxe.com; - # keepalive_timeout 3600; - # client_max_body_size 5120M; - # access_log /var/log/nginx/netaxe.log main; - # gzip on; - # gzip_min_length 1k; - # gzip_comp_level 9; - # gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png; - # gzip_vary on; - # gzip_disable "MSIE [1-6]\."; location / { root /home; index index.html index.htm; try_files $uri $uri/ /index.html; + #允许跨域请求的域,* 代表所有 + add_header 'Access-Control-Allow-Origin' *; + #允许请求的header + add_header 'Access-Control-Allow-Headers' *; + #允许请求的方法,比如 GET,POST,PUT,DELETE + add_header 'Access-Control-Allow-Methods' *; } - + location /api { + if ($request_method = 'OPTIONS') { + #允许跨域请求的域,* 代表所有 + add_header 'Access-Control-Allow-Origin' *; + #允许请求的header + add_header 'Access-Control-Allow-Headers' *; + #允许请求的方法,比如 GET,POST,PUT,DELETE + add_header 'Access-Control-Allow-Methods' *; + return 204; + } proxy_pass http://apisix:9080/api; + + } location /ipam { + if ($request_method = 'OPTIONS') { + #允许跨域请求的域,* 代表所有 + add_header 'Access-Control-Allow-Origin' *; + #允许请求的header + add_header 'Access-Control-Allow-Headers' *; + #允许请求的方法,比如 GET,POST,PUT,DELETE + add_header 'Access-Control-Allow-Methods' *; + return 204; + } proxy_pass http://apisix:9080/ipam; } location /media { + if ($request_method = 'OPTIONS') { + #允许跨域请求的域,* 代表所有 + add_header 'Access-Control-Allow-Origin' *; + #允许请求的header + add_header 'Access-Control-Allow-Headers' *; + #允许请求的方法,比如 GET,POST,PUT,DELETE + add_header 'Access-Control-Allow-Methods' *; + return 204; + } proxy_pass http://apisix:9080/media; } - # location /api { - # proxy_pass http://netaxe-nginx:9999/api; - # } - # location /net_backend { - # proxy_pass http://netaxe-nginx:9999/backend; - # } - # location /topology { - # proxy_pass http://netaxe-nginx:9999/topology; - # } - # location /jobcenter { - # proxy_pass http://netaxe-nginx:9999/jobcenter; - # } - # location /automation { - # proxy_pass http://netaxe-nginx:9999/automation; - # } - # location /resources_manage { - # proxy_pass http://netaxe-nginx:9999/resources_manage; - # } - # location /int_utilization { - # proxy_pass http://netaxe-nginx:9999/int_utilization; - # } - # location /config_center { - # proxy_pass http://netaxe-nginx:9999/config_center; - # } + location /rbac { + if ($request_method = 'OPTIONS') { + #允许跨域请求的域,* 代表所有 + add_header 'Access-Control-Allow-Origin' *; + #允许请求的header + add_header 'Access-Control-Allow-Headers' *; + #允许请求的方法,比如 GET,POST,PUT,DELETE + add_header 'Access-Control-Allow-Methods' *; + return 204; + } + proxy_pass http://apisix:9080/rbac; + } location /ws { - proxy_pass http://netaxe-nginx:9999/ws; + if ($request_method = 'OPTIONS') { + #允许跨域请求的域,* 代表所有 + add_header 'Access-Control-Allow-Origin' *; + #允许请求的header + add_header 'Access-Control-Allow-Headers' *; + #允许请求的方法,比如 GET,POST,PUT,DELETE + add_header 'Access-Control-Allow-Methods' *; + return 204; + } + proxy_pass http://cmdb-nginx:9999/ws; proxy_connect_timeout 30000s; proxy_read_timeout 36000s; proxy_send_timeout 86000s; diff --git a/web/package.json b/web/package.json index a32fbe18d05b4fb3deaf4a382936be3fae7d276d..5319289d611836eb25a54e95d3f2f539b719a935 100644 --- a/web/package.json +++ b/web/package.json @@ -1,5 +1,5 @@ { - "name": "netaxe-web", + "name": "netaxe-cmdb-web", "version": "1.0.0", "scripts": { "dev": "vite", diff --git a/web/src/api/axios.config.ts b/web/src/api/axios.config.ts index dc7ef03db4fc51cd576031835f05b4c740bb0525..2970776eabbd514a7e4d7373bc633cfc129e946d 100644 --- a/web/src/api/axios.config.ts +++ b/web/src/api/axios.config.ts @@ -18,11 +18,11 @@ export const TEXT_PLAIN = 'text/plain; charset=UTF-8' const service = Axios.create({ // baseURL, timeout: 10 * 60 * 1000, - withCredentials: true, // 跨域请求时发送cookie + withCredentials: false, }) // 在正式发送请求之前进行拦截配置 service.interceptors.request.use( - (config) => { + (config: { headers: { [x: string]: string; Authorization?: any }; data: any }) => { !config.headers && (config.headers = {}) if (!config.headers.Authorization && Cookies.get('netops-token')) { config.headers.Authorization = Cookies.get('netops-token') ? Cookies.get('netops-token') : '' @@ -35,7 +35,7 @@ service.interceptors.request.use( } return config }, - (error) => { + (error: { response: any }) => { return Promise.reject(error.response) } ) @@ -55,7 +55,7 @@ service.interceptors.response.use( throw new Error(response.status.toString()) } }, - (error) => { + (error: any) => { if (import.meta.env.MODE === 'development') { console.log(error) } diff --git a/web/src/api/http.ts b/web/src/api/http.ts index 992fde3a2c1996b65b18c9c1d7c709b84f8c8939..271223817b6757f32adce933564b321b3ae8995f 100644 --- a/web/src/api/http.ts +++ b/web/src/api/http.ts @@ -41,7 +41,7 @@ function http({ url, data, method, headers, beforeRequest, afterRequest }: HttpO return res.data } if (res.data.code === 401) { - router.push('/login') + // router.push('/login') throw new Error('认证失败请重新登录一下') // throw new Error('认证失败请重新登录一下') @@ -65,6 +65,7 @@ function http({ url, data, method, headers, beforeRequest, afterRequest }: HttpO throw new Error(res.data.message + '请求失败,未知异常') // return res.data } + // eslint-disable-next-line @typescript-eslint/no-unused-vars const failHandler = (error: Response) => { afterRequest && afterRequest() // console.log(error) @@ -76,12 +77,12 @@ function http({ url, data, method, headers, beforeRequest, afterRequest }: HttpO return method === 'GET' ? service.get(url, { params }).then(successHandler, failHandler) : method === 'POST' - ? service.post(url, params, { headers: headers }).then(successHandler, failHandler) - : method === 'PUT' - ? service.put(url, params, { headers: headers }).then(successHandler, failHandler) - : method === 'PATCH' - ? service.patch(url, params, { headers: headers }).then(successHandler, failHandler) - : service.delete(url, params).then(successHandler, failHandler) + ? service.post(url, params, { headers: headers }).then(successHandler, failHandler) + : method === 'PUT' + ? service.put(url, params, { headers: headers }).then(successHandler, failHandler) + : method === 'PATCH' + ? service.patch(url, params, { headers: headers }).then(successHandler, failHandler) + : service.delete(url, params).then(successHandler, failHandler) } export function get({ @@ -91,6 +92,7 @@ export function get({ beforeRequest, afterRequest, }: HttpOption): Promise { + // @ts-ignore return http({ url, method, @@ -108,6 +110,7 @@ export function post({ beforeRequest, afterRequest, }: HttpOption): Promise { + // @ts-ignore return http({ url, method, @@ -126,6 +129,7 @@ export function put({ beforeRequest, afterRequest, }: HttpOption): Promise { + // @ts-ignore return http({ url, method, @@ -144,6 +148,7 @@ export function patch({ beforeRequest, afterRequest, }: HttpOption): Promise { + // @ts-ignore return http({ url, method, @@ -162,6 +167,7 @@ export function delete_fun({ beforeRequest, afterRequest, }: HttpOption): Promise { + // @ts-ignore return http({ url, method, diff --git a/web/src/api/url.ts b/web/src/api/url.ts index 36db3d8b8fa337c51deab9b81a82dfc02c804f48..4f0e105a801a3ac1ab3561a56261197a094146bb 100644 --- a/web/src/api/url.ts +++ b/web/src/api/url.ts @@ -9,8 +9,9 @@ export const getTableList = '/api/users/user/' export const getRoleList = '/api/system/role/' export const getMenuList = '/api/system/menu/' export const getDepartmentList = '/api/system/dept/' -export const getMenuListByRole = '/api/system/menu/web_router/' - +// export const getMenuListByRole = '/api/system/menu/web_router/' +export const WebRouter = '/rbac/system/menu/web_router/' +export const WebPermission = '/rbac/system/menu/web_permission/' // 调度管理 export const getdispach = '/api/backend/dispatch_page/' // 任务列表 diff --git a/web/src/assets/img_login_fg.9c0e0a4c.jpeg b/web/src/assets/img_login_fg.9c0e0a4c.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..8e390cfb1d9954e3e8fa1593fcf2030d8ef4a80e Binary files /dev/null and b/web/src/assets/img_login_fg.9c0e0a4c.jpeg differ diff --git a/web/src/assets/rack_bkg.jpeg b/web/src/assets/rack_bkg.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..8999e59f876fd3362c5fe658fc309efb1e8325d1 Binary files /dev/null and b/web/src/assets/rack_bkg.jpeg differ diff --git a/web/src/components/avatar/VAWAvatar.vue b/web/src/components/avatar/VAWAvatar.vue index e5ba1a6170099a9e0db1ac4e74cd6ae8ec772932..938d74757a0daca0ef036cd710cb2cb2a348893c 100644 --- a/web/src/components/avatar/VAWAvatar.vue +++ b/web/src/components/avatar/VAWAvatar.vue @@ -3,7 +3,7 @@
- +
{{ userStore.nickName }} @@ -17,107 +17,114 @@ diff --git a/web/src/components/logo/index.vue b/web/src/components/logo/index.vue index e524c564cce0a42e98aee701f857014993db962a..f05c4dd5e2fcd6449bd8eedc81c86c727989b264 100644 --- a/web/src/components/logo/index.vue +++ b/web/src/components/logo/index.vue @@ -2,7 +2,7 @@
- {{ projectName }} + CMDB
diff --git a/web/src/components/navbar/NavBar.vue b/web/src/components/navbar/NavBar.vue index 433eab1f5d6ed2953caaa6023fe7effee6397c44..2e3a992f189096f52d4580f94296e88f2a245a31 100644 --- a/web/src/components/navbar/NavBar.vue +++ b/web/src/components/navbar/NavBar.vue @@ -4,7 +4,7 @@
- +
diff --git a/web/src/hooks/useMenuWidth.ts b/web/src/hooks/useMenuWidth.ts index 700a31f17f55f033a0bd389301b2473bf21de3cf..81a6f7ed1e05dfc88504ab3da785a0a9ea2951dd 100644 --- a/web/src/hooks/useMenuWidth.ts +++ b/web/src/hooks/useMenuWidth.ts @@ -1,16 +1,19 @@ import Setting from '../setting' +import { ADMIN_WORK_SETTING_INFO, ADMIN_WORK_S_TENANT } from '@/store/keys' export function useMenuWidth() { - const r = document.querySelector(':root') as HTMLElement - const styles = getComputedStyle(r) - const menuWith = styles.getPropertyValue('--menu-width') + // const r = document.querySelector(':root') as HTMLElement + // const styles = getComputedStyle(r) + // const menuWith = styles.getPropertyValue('--menu-width') + // console.log(menuWith) + const menuWith = '210px' return parseInt(menuWith) } export function useChangeMenuWidth(width: Number) { - const r = document.querySelector(':root') as HTMLElement - r.style.setProperty('--menu-width', width + 'px') + // const r = document.querySelector(':root') as HTMLElement + // r.style.setProperty('--menu-width', width + 'px') localStorage.setItem( - 'setting-info', + ADMIN_WORK_SETTING_INFO, JSON.stringify( Object.assign(Setting, { sideWidth: width, diff --git a/web/src/router/index.ts b/web/src/router/index.ts index b0297a33f95f5a2e9a8bd0873d05c846545f6139..fb32a2945c0e991a8c73af0ffe94ab40383f2e08 100644 --- a/web/src/router/index.ts +++ b/web/src/router/index.ts @@ -1,5 +1,5 @@ -import {mapTwoLevelRouter} from '@/utils' -import {createRouter, createWebHistory} from 'vue-router' +import { mapTwoLevelRouter } from '@/utils' +import { createRouter, createWebHistory } from 'vue-router' const Layout = () => import('@/components/Layout.vue') @@ -51,11 +51,12 @@ export const constantRoutes = [ title: '工作台', affix: true, iconPrefix: 'iconfont', - icon: 'infomation', + icon: 'index', }, }, ], }, + { path: '/redirect', component: Layout, diff --git a/web/src/setting/index.ts b/web/src/setting/index.ts index cdd240d590e3dae6a4f436dca61837f6c9b3f6c8..669557a9433fc6dfce54bea88105a68a6d6a634a 100644 --- a/web/src/setting/index.ts +++ b/web/src/setting/index.ts @@ -1,4 +1,5 @@ -const settingInfo = JSON.parse(localStorage.getItem('setting-info') || '{}') +import { ADMIN_WORK_SETTING_INFO, ADMIN_WORK_S_TENANT } from '@/store/keys' +const settingInfo = JSON.parse(localStorage.getItem(ADMIN_WORK_SETTING_INFO) || '{}') interface Setting { projectName: string theme: 'light' | 'dark' @@ -15,15 +16,14 @@ interface Setting { isShowFullScreen: boolean } } - -export const projectName = '' +export const projectName = 'NET-AXE' export default Object.assign( { theme: 'light', - sideTheme: 'dark', + sideTheme: 'white', themeColor: 'cyan@#18a058', - layoutMode: 'ltr', + layoutMode: 'ttb', sideWidth: 210, pageAnim: 'opacity', isFixedNavBar: true, @@ -34,5 +34,5 @@ export default Object.assign( isShowFullScreen: true, }, }, - settingInfo + settingInfo, ) as Setting diff --git a/web/src/store/index.ts b/web/src/store/index.ts index d96d9cdabab6b645b7122ca0e02945585635e1c2..65a21f4fc501149445002cd6ba0f34e45de07d94 100644 --- a/web/src/store/index.ts +++ b/web/src/store/index.ts @@ -1,6 +1,7 @@ import { StoreType, SideTheme, RouteRecordRawWithHidden } from './../types/store' import { reactive } from 'vue' import { DeviceType, LayoutMode, StateType, ThemeMode } from '../types/store' +import { ADMIN_WORK_SETTING_INFO, ADMIN_WORK_S_TENANT } from '@/store/keys' import { transfromRoutes } from '../utils' import CachedViewAction from './modules/cached-view' import VisitedViewAction from './modules/visited-view' @@ -11,7 +12,7 @@ const layoutModes = ['ltr', 'lcr', 'ttb'] useChangeMenuWidth(Setting.sideWidth) function presistSettingInfo(setting: any) { - localStorage.setItem('setting-info', JSON.stringify(setting)) + localStorage.setItem(ADMIN_WORK_SETTING_INFO, JSON.stringify(setting)) } const primaryColor = Setting.themeColor.split('@')[1] diff --git a/web/src/store/keys.ts b/web/src/store/keys.ts index 70ee511eccaba84e7098b4984e10e6be152b0063..ee6224285435c20294ae7bf01112ab7ae8240414 100644 --- a/web/src/store/keys.ts +++ b/web/src/store/keys.ts @@ -1,15 +1,17 @@ -export const ADMIN_WORK_USER_INFO_KEY = 'admin-work-user-info' - -export const ADMIN_WORK_TOkEN_KEY = 'admin-work-token' - -export const NETOPS_TOKEN = 'netops-token' - -export const PUBLIC_KEY ="-----BEGIN PUBLIC KEY-----\n" + - "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqVozgu4PP91Nl+KgG7P0\n" + - "0ZHwGDvkfjx4htMTVBwM4soz6oC+UxROPIY7sIgEC9WO6nju6vLJdHepwm6pBWMf\n" + - "M+w1CFjfN6OypqND4L4pTdTWdlhkwMMBL2a2gDxceryC1PjswCjiqWu1sqXuYOkD\n" + - "DoHgork9QZkHtVL0pHvupYQ0VFynpQFdOc8gnr/hPUUVk1cp9WSeRFQGXhqfI/xB\n" + - "aaZ5yBMLNK6EAZep+1Xsj2u/3NI2tlwljlZZAAeqEu2hOeyGKBaB5Sa9duvMTR83\n" + - "lE+kogzZbdi/PXeGApuOosITXnZeLvFKrpIiLbzq+TzhLAHC7Obre2TzmldinBQn\n" + - "QQIDAQAB\n" + - "-----END PUBLIC KEY-----" \ No newline at end of file +export const ADMIN_TOKEN = 'netops-token' +export const ADMIN_WORK_S_TENANT = 'netops-work-tenant' +export const ADMIN_WORK_TOkEN_KEY = 'netops-work-token' +export const ADMIN_WORK_VISITED_KEY = 'netops-work-visited' +export const ADMIN_WORK_BUTTON_AUTH = 'netops-work-button-auth' +export const ADMIN_WORK_USER_INFO_KEY = 'netops-work-user-info' +export const ADMIN_WORK_SETTING_INFO = 'netops-work-setting-info' +export const PUBLIC_KEY = + '-----BEGIN PUBLIC KEY-----\n' + + 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqVozgu4PP91Nl+KgG7P0\n' + + '0ZHwGDvkfjx4htMTVBwM4soz6oC+UxROPIY7sIgEC9WO6nju6vLJdHepwm6pBWMf\n' + + 'M+w1CFjfN6OypqND4L4pTdTWdlhkwMMBL2a2gDxceryC1PjswCjiqWu1sqXuYOkD\n' + + 'DoHgork9QZkHtVL0pHvupYQ0VFynpQFdOc8gnr/hPUUVk1cp9WSeRFQGXhqfI/xB\n' + + 'aaZ5yBMLNK6EAZep+1Xsj2u/3NI2tlwljlZZAAeqEu2hOeyGKBaB5Sa9duvMTR83\n' + + 'lE+kogzZbdi/PXeGApuOosITXnZeLvFKrpIiLbzq+TzhLAHC7Obre2TzmldinBQn\n' + + 'QQIDAQAB\n' + + '-----END PUBLIC KEY-----' diff --git a/web/src/store/modules/user.ts b/web/src/store/modules/user.ts index f8f262f5c43d5726a24576d648ac0b30612382c3..7aeaf1ed5c96d0249965f212e191be651f269c48 100644 --- a/web/src/store/modules/user.ts +++ b/web/src/store/modules/user.ts @@ -1,7 +1,7 @@ import { defineStore } from 'pinia' import { UserState } from '../types' import layoutStore from '../index' -import { ADMIN_WORK_USER_INFO_KEY, ADMIN_WORK_TOkEN_KEY, NETOPS_TOKEN } from '../keys' +import { ADMIN_WORK_USER_INFO_KEY, ADMIN_WORK_TOkEN_KEY, ADMIN_TOKEN } from '../keys' import Avatar from '@/assets/img_avatar.gif' import Cookies from 'js-cookie' @@ -29,7 +29,7 @@ const useUserStore = defineStore('user', { this.userName = userInfo.userName this.nickName = userInfo.nickName this.image = userInfo.image || defaultAvatar - Cookies.set(NETOPS_TOKEN, 'Bearer ' + userInfo.token) + Cookies.set(ADMIN_TOKEN, 'Bearer ' + userInfo.token) // Cookies.set('csrftoken', userInfo['csrf_token']) localStorage.setItem('is_superuser', userInfo.isSuperuser + '') res() @@ -46,7 +46,7 @@ const useUserStore = defineStore('user', { this.userName = '' this.nickName = '' localStorage.clear() - Cookies.remove(NETOPS_TOKEN) + Cookies.remove(ADMIN_TOKEN) layoutStore.reset() resolve() }) diff --git a/web/src/store/modules/visited-view.ts b/web/src/store/modules/visited-view.ts index 3a5cf7216529f7c87aa15fdca257325c7a2245e2..515d36b0343a681a2b3cc1401cd83b46009095d7 100644 --- a/web/src/store/modules/visited-view.ts +++ b/web/src/store/modules/visited-view.ts @@ -8,21 +8,21 @@ export default { addVisitedView(route) { return new Promise((resolve) => { if (!(this as StoreType).state.visitedView.find((it) => it.fullPath === route.fullPath)) { - ;(this as StoreType).state.visitedView.push(route) + ; (this as StoreType).state.visitedView.push(route) this.persistentVisitedView() } - ;(this as StoreType).addCachedView && (this as StoreType).addCachedView(route) + ; (this as StoreType).addCachedView && (this as StoreType).addCachedView(route) resolve(route) }) }, removeVisitedView(route) { return new Promise((resolve) => { - ;(this as StoreType).state.visitedView.splice( + ; (this as StoreType).state.visitedView.splice( (this as StoreType).state.visitedView.indexOf(route), 1 ) - ;(this as StoreType).persistentVisitedView() - ;(this as StoreType).removeCachedView && (this as StoreType).removeCachedView(route) + ; (this as StoreType).persistentVisitedView() + ; (this as StoreType).removeCachedView && (this as StoreType).removeCachedView(route) resolve(route) }) }, @@ -30,14 +30,14 @@ export default { return new Promise((resolve) => { const selectIndex = (this as StoreType).state.visitedView.indexOf(selectRoute) if (selectIndex !== -1) { - ;(this as StoreType).state.visitedView = (this as StoreType).state.visitedView.filter( + ; (this as StoreType).state.visitedView = (this as StoreType).state.visitedView.filter( (it, index) => { return (it.meta && it.meta.affix) || index >= selectIndex } ) this.persistentVisitedView() } - ;(this as StoreType).resetCachedView && (this as StoreType).resetCachedView() + ; (this as StoreType).resetCachedView && (this as StoreType).resetCachedView() resolve(selectRoute) }) }, @@ -45,26 +45,26 @@ export default { return new Promise((resolve) => { const selectIndex = (this as StoreType).state.visitedView.indexOf(selectRoute) if (selectIndex !== -1) { - ;(this as StoreType).state.visitedView = (this as StoreType).state.visitedView.filter( + ; (this as StoreType).state.visitedView = (this as StoreType).state.visitedView.filter( (it, index) => { return (it.meta && it.meta.affix) || index <= selectIndex } ) this.persistentVisitedView() } - ;(this as StoreType).resetCachedView && (this as StoreType).resetCachedView() + ; (this as StoreType).resetCachedView && (this as StoreType).resetCachedView() resolve(selectRoute) }) }, closeAllVisitedView() { return new Promise((resolve) => { - ;(this as StoreType).state.visitedView = (this as StoreType).state.visitedView.filter( + ; (this as StoreType).state.visitedView = (this as StoreType).state.visitedView.filter( (it) => { return it.meta && it.meta.affix } ) - ;(this as StoreType).persistentVisitedView() - ;(this as StoreType).resetCachedView && (this as StoreType).resetCachedView() + ; (this as StoreType).persistentVisitedView() + ; (this as StoreType).resetCachedView && (this as StoreType).resetCachedView() resolve() }) }, @@ -79,10 +79,10 @@ export default { query: it.query, } }) - localStorage.setItem(LOCAL_STOREAGE_VISITED_KEY, JSON.stringify(tempPersistendRoutes)) + // localStorage.setItem(LOCAL_STOREAGE_VISITED_KEY, JSON.stringify(tempPersistendRoutes)) }, restoreVisitedView() { - ;(this as StoreType).state.visitedView = [...(this as StoreType).state.visitedView] + ; (this as StoreType).state.visitedView = [...(this as StoreType).state.visitedView] const originRouteString = localStorage.getItem(LOCAL_STOREAGE_VISITED_KEY) const persistentVisitedRoutes = JSON.parse(originRouteString || '[]') persistentVisitedRoutes.forEach((originRoute: RouteRecordRawWithHidden) => { @@ -91,7 +91,7 @@ export default { (it) => it.fullPath === originRoute.fullPath && it.name === originRoute.name ) ) { - ;(this as StoreType).state.visitedView.push(originRoute) + ; (this as StoreType).state.visitedView.push(originRoute) } }) }, diff --git a/web/src/types/d3Types.ts b/web/src/types/d3Types.ts new file mode 100644 index 0000000000000000000000000000000000000000..53a38f695bbf33d5b858bc778caeee2216436013 --- /dev/null +++ b/web/src/types/d3Types.ts @@ -0,0 +1,25 @@ +export class Node { + id: string; + group?: number; // 可选属性,节点分组 + x?: number; // 可选属性,节点初始位置 + y?: number; // 可选属性,节点初始位置 + + constructor(id: string, group?: number, x?: number, y?: number) { + this.id = id; + this.group = group; + this.x = x; + this.y = y; + } +} + +export class Link { + source: string; // 起始节点ID + target: string; // 目标节点ID + value?: number; // 可选属性,边权重 + + constructor(source: string, target: string, value?: number) { + this.source = source; + this.target = target; + this.value = value; + } +} \ No newline at end of file diff --git a/web/src/utils/router.ts b/web/src/utils/router.ts index d484cd25cf9e51fe8fad16ac98a1ee90731b05e1..d9ebb178ab07c7bb16622f5401b1ff5b293e68a4 100644 --- a/web/src/utils/router.ts +++ b/web/src/utils/router.ts @@ -1,21 +1,23 @@ -import router, { constantRoutes } from '../router' import Cookies from 'js-cookie' import { get } from '@/api/http' -import { baseAddress, getMenuListByRole } from '@/api/url' -import { RouteRecordRaw } from 'vue-router' -import { isExternal, mapTwoLevelRouter, toHump } from '.' -import { Layout } from '@/components' import layoutStore from '@/store' +import { Layout } from '@/components' +import { UserState } from '@/store/types' import { defineAsyncComponent } from 'vue' +import { RouteRecordRaw } from 'vue-router' +import useUserStore from '@/store/modules/user' +import router, { constantRoutes } from '../router' +import { isExternal, mapTwoLevelRouter, toHump } from '.' import LoadingComponent from '../components/loading/index.vue' +import { baseAddress, WebRouter, WebPermission } from '@/api/url' +import { ADMIN_WORK_USER_INFO_KEY, ADMIN_WORK_BUTTON_AUTH, ADMIN_WORK_S_TENANT } from '@/store/keys' interface OriginRoute { - // menuUrl: string - // menuName?: string + key: any name: string web_path: string + link_path?: string hidden?: boolean - outLink?: string affix?: boolean cacheable?: boolean iconPrefix?: string @@ -31,17 +33,31 @@ function loadComponents() { } const asynComponents = loadComponents() -// 获取路由 +const navigateID = localStorage.getItem(ADMIN_WORK_S_TENANT) + +// 获取web权限 function getRoutes() { + // console.log(layoutStore.state) return get({ - url: baseAddress + getMenuListByRole, + url: baseAddress + WebRouter, method: 'GET', - data: { parent__isnull: true }, + data: { parent__isnull: true, navigate__id: navigateID } }).then((res: any) => { return generatorRoutes(res.results) }) } +// 获取menu权限 +function getPermission() { + return get({ + url: baseAddress + WebPermission, + method: 'GET', + data: { navigate__id: navigateID } + }).then((res: any) => { + localStorage.setItem(ADMIN_WORK_BUTTON_AUTH, JSON.stringify(res.results)) + }) +} + function getComponent(it: OriginRoute) { return defineAsyncComponent({ loader: asynComponents['../views' + it.web_path + '.vue'], @@ -57,34 +73,37 @@ function getCharCount(str: string, char: string) { } function isMenu(path: string) { - return getCharCount(path, '/') === 1 + return getCharCount(path, '\/') === 1 } -function getNameByUrl(web_path: string) { - const temp = web_path.split('/') +function getNameByUrl(path: string) { + const temp = path.split('/') return toHump(temp[temp.length - 1]) } function generatorRoutes(res: Array) { const tempRoutes: Array = [] res.forEach((it) => { - const route: RouteRecordRawWithHidden = { - path: it.outLink && isExternal(it.outLink) ? it.outLink : it.web_path, - name: getNameByUrl(it.web_path), - hidden: !!it.hidden, - component: isMenu(it.web_path) ? Layout : getComponent(it), - meta: { - title: it.name, - affix: !!it.affix, - cacheable: !!it.cacheable, - icon: it.icon || 'menu', - iconPrefix: it.iconPrefix || 'iconfont', - }, - } - if (it.children) { - route.children = generatorRoutes(it.children) + if (!it.key) { + const path = it.link_path && isExternal(it.link_path) ? it.link_path : it.web_path + const route: RouteRecordRawWithHidden = { + path: path, + name: getNameByUrl(path), + hidden: !!it.hidden, + component: it.web_path && isMenu(it.web_path) ? Layout : getComponent(it), + meta: { + title: it.name, + affix: !!it.affix, + cacheable: !!it.cacheable, + icon: it.icon || 'menu', + iconPrefix: it.iconPrefix || 'iconfont', + }, + } + if (it.children) { + route.children = generatorRoutes(it.children) + } + tempRoutes.push(route) } - tempRoutes.push(route) }) return tempRoutes } @@ -97,6 +116,7 @@ function isTokenExpired(): boolean { } router.beforeEach(async (to) => { + console.log(to.path) if (whiteRoutes.includes(to.path)) { return true } else { @@ -106,85 +126,35 @@ router.beforeEach(async (to) => { query: { redirect: to.fullPath }, } } else { + // 获取租户信息 + const userInfo: UserState = JSON.parse(localStorage.getItem(ADMIN_WORK_USER_INFO_KEY) || '{}') + + const isEmptyRoute = layoutStore.isEmptyPermissionRoute() - if (isEmptyRoute) { - try { - // 加载路由 - const accessRoutes: Array = [] - const tempRoutes = await getRoutes() - accessRoutes.push(...tempRoutes) - if (localStorage.getItem('is_superuser') === 'true') { - const system_data = { - path: '/system', - name: 'System', - component: Layout, - meta: { - title: '系统配置', - iconPrefix: 'iconfont', - icon: 'setting', - }, - children: [ - { - path: 'user', - name: 'User', - component: () => import('@/views/system/user.vue'), - meta: { - title: '用户配置', - iconPrefix: 'iconfont', - icon: 'user', - }, - }, - { - path: 'department', - name: 'Department', - component: () => import('@/views/system/department.vue'), - meta: { - title: '部门配置', - iconPrefix: 'iconfont', - icon: 'apartment', - }, - }, - { - path: 'role', - name: 'Role', - component: () => import('@/views/system/role.vue'), - meta: { - title: '角色配置', - iconPrefix: 'iconfont', - icon: 'control', - }, - }, - { - path: 'menu', - name: 'Menu', - component: () => import('@/views/system/menu.vue'), - meta: { - title: '菜单配置', - iconPrefix: 'iconfont', - icon: 'menu', - }, - }, - ], - } - accessRoutes.push(system_data) - } - const mapRoutes = mapTwoLevelRouter(accessRoutes) - mapRoutes.forEach((it: any) => { - router.addRoute(it) - }) - router.addRoute({ - path: '/:pathMatch(.*)*', - redirect: '/404', - hidden: true, - } as RouteRecordRaw) - layoutStore.initPermissionRoute([...constantRoutes, ...accessRoutes]) - return { ...to, replace: true } - } catch { - return { - path: '/login', - query: { redirect: to.fullPath }, - } - } + console.log(isEmptyRoute) + if (isEmptyRoute && to.path!='/ssh') { + + // 加载路由和按钮 + const webRoutes = await getRoutes() + // console.log(webRoutes) + // const webPermission = await getPermission() + const accessRoutes: Array = [] + accessRoutes.push(...webRoutes) + + + const mapRoutes = mapTwoLevelRouter(accessRoutes) + mapRoutes.forEach((it: any) => { + router.addRoute(it) + }) + router.addRoute({ + path: '/:pathMatch(.*)*', + redirect: '/404', + hidden: true, + } as RouteRecordRaw) + // console.log('constantRoutes',JSON.stringify(constantRoutes)) + // console.log('accessRoutes',JSON.stringify(accessRoutes)) + layoutStore.initPermissionRoute([...constantRoutes, ...accessRoutes]) + return { ...to, replace: true } } else { return true } diff --git a/web/src/utils/router_bk.ts b/web/src/utils/router_bk.ts new file mode 100644 index 0000000000000000000000000000000000000000..dad67eb34edd8890166eb8f152b6d16fa3b9dd2d --- /dev/null +++ b/web/src/utils/router_bk.ts @@ -0,0 +1,163 @@ +import Cookies from 'js-cookie' +import {get} from '@/api/http' +import layoutStore from '@/store' +import {Layout} from '@/components' +import {UserState} from '@/store/types' +import {defineAsyncComponent} from 'vue' +import {RouteRecordRaw} from 'vue-router' +import router, {constantRoutes} from '../router' +import {isExternal, mapTwoLevelRouter, toHump} from '.' +import LoadingComponent from '../components/loading/index.vue' +import {baseAddress, WebPermission, WebRouter} from '@/api/url' +import {ADMIN_WORK_BUTTON_AUTH, ADMIN_WORK_S_TENANT, ADMIN_WORK_USER_INFO_KEY} from '@/store/keys' + +const navigateID = localStorage.getItem(ADMIN_WORK_S_TENANT) + +interface OriginRoute { + key: any + name: string + web_path: string + link_path?: string + hidden?: boolean + affix?: boolean + cacheable?: boolean + iconPrefix?: string + icon?: string + badge?: string | number + children: Array +} + +type RouteRecordRawWithHidden = RouteRecordRaw & { hidden: boolean } + +function loadComponents() { + return import.meta.glob('../views/**/*.vue') +} + +const asynComponents = loadComponents() + +// 获取路由 +function getRoutes() { + console.log(layoutStore.state) + return get({ + url: baseAddress + WebRouter, + method: 'GET', + data: { parent__isnull: true, navigate__id: navigateID } + }).then((res: any) => { + console.log(res) + return generatorRoutes(res.results) + }) +} + +// 获取路由 +// eslint-disable-next-line @typescript-eslint/no-unused-vars +function getPermission() { + return get({ + url: baseAddress + WebPermission, + method: 'GET', + data: { navigate__id: layoutStore.state.navigateID } + }).then((res: any) => { + localStorage.setItem(ADMIN_WORK_BUTTON_AUTH, JSON.stringify(res.results)) + }) +} + +function getComponent(it: OriginRoute) { + return defineAsyncComponent({ + loader: asynComponents['../views' + it.web_path + '.vue'], + loadingComponent: LoadingComponent, + }) +} + +function getCharCount(str: string, char: string) { + const regex = new RegExp(char, 'g') + const result = str.match(regex) + return !result ? 0 : result.length +} + +function isMenu(path: string) { + return getCharCount(path, '/') === 1 +} + +function getNameByUrl(path: string) { + const temp = path.split('/') + return toHump(temp[temp.length - 1]) +} + +function generatorRoutes(res: Array) { + const tempRoutes: Array = [] + res.forEach((it) => { + if (!it.key){ + const path = it.link_path && isExternal(it.link_path) ? it.link_path : it.web_path + const route: RouteRecordRawWithHidden = { + path: path, + name: getNameByUrl(path), + hidden: !!it.hidden, + component: it.web_path && isMenu(it.web_path) ? Layout:getComponent(it), + meta: { + title: it.name, + affix: !!it.affix, + cacheable: !!it.cacheable, + icon: it.icon || 'menu', + iconPrefix: it.iconPrefix || 'iconfont', + }, + } + if (it.children) { + route.children = generatorRoutes(it.children) + } + tempRoutes.push(route) + } + }) + return tempRoutes +} + +const whiteRoutes: string[] = ['/login', '/404', '/403', '/500'] + +function isTokenExpired(): boolean { + const token = Cookies.get('netops-token') + return !!token +} + +router.beforeEach(async (to) => { + if (whiteRoutes.includes(to.path)) { + return true + } else { + if (!isTokenExpired()) { + return { + path: '/login', + query: { redirect: to.fullPath }, + } + } else { + // 获取租户信息 + const userInfo: UserState = JSON.parse(localStorage.getItem(ADMIN_WORK_USER_INFO_KEY) || '{}') + // layoutStore.changeNavigateID(userInfo.tenantUser[0] as any) + + // 配置租户是否显示 + // layoutStore.changeIsNavigate(false) + // if (to.matched[0]?.name === "Permissions"){ + // layoutStore.changeIsNavigate(true) + // } + + const isEmptyRoute = layoutStore.isEmptyPermissionRoute() + if (isEmptyRoute) { + // 加载路由和按钮 + const webRoutes = await getRoutes() + // const webPermission = await getPermission() + const accessRoutes: Array = [] + accessRoutes.push(...webRoutes) + + const mapRoutes = mapTwoLevelRouter(accessRoutes) + mapRoutes.forEach((it: any) => { + router.addRoute(it) + }) + router.addRoute({ + path: '/:pathMatch(.*)*', + redirect: '/404', + hidden: true, + } as RouteRecordRaw) + layoutStore.initPermissionRoute([...constantRoutes, ...accessRoutes]) + return { ...to, replace: true } + } else { + return true + } + } + } +}) diff --git a/web/src/views/cmdb/interfaceused.vue b/web/src/views/cmdb/interfaceused.vue index 0afe00266331267dc2a271054bd98aeb7a26389f..0b661754c9d6a6b139e4708abfc0463618fb0a90 100644 --- a/web/src/views/cmdb/interfaceused.vue +++ b/web/src/views/cmdb/interfaceused.vue @@ -2,396 +2,364 @@
- +
diff --git a/web/src/views/cmdb/network_device.vue b/web/src/views/cmdb/network_device.vue index fedf1e5a7f64e894fe2502c684451e3695351b12..6d8769e98fbd43bf02a7846c6d96cd079df1cdfd 100644 --- a/web/src/views/cmdb/network_device.vue +++ b/web/src/views/cmdb/network_device.vue @@ -1,292 +1,171 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{ checkedRowKeysRef.length }} + + + + + + + + + + + + + 下载导入模板 + + +
+ + 取消 + + +
+
+
diff --git a/web/src/views/index/work-place.vue b/web/src/views/index/work-place.vue index 810d384df266f775dd3fcc57f9113c3972572ab5..7a1df851ea2d177889586fe205250d6ef3c5e1a3 100644 --- a/web/src/views/index/work-place.vue +++ b/web/src/views/index/work-place.vue @@ -7,18 +7,18 @@
-
早上好,Andy,青春只有一次,别让自己过得不精彩
+
CMDB工作台
今日有小雨,出门别忘记带伞哦~
-
项目数
+
应用资源
12
-
待办项
-
3/20
+
基础设施
+
2250
当前日期
@@ -27,317 +27,146 @@ - +
- + {{ item.title }}
-
diff --git a/web/src/views/login/LoginComponent.vue b/web/src/views/login/LoginComponent.vue index cd7ba96969244d625ebe04f9772508c9e6bb6f62..c20aa42fe3d178bdf3dddc14d9417fbbacbefa43 100644 --- a/web/src/views/login/LoginComponent.vue +++ b/web/src/views/login/LoginComponent.vue @@ -2,15 +2,15 @@ - - - - - - - - - - - - - - + - - - - - - - - - - - - - - @@ -121,6 +94,7 @@ import { computed, defineComponent, ref } from 'vue' import { useRoute, useRouter } from 'vue-router' import ImageBg1 from '@/assets/img_login_bg.png' + import ImageBg2 from '@/assets/img_login_fg.9c0e0a4c.jpeg' import { post, Response } from '@/api/http' import { login } from '@/api/url' import { UserState } from '@/store/types' @@ -130,9 +104,9 @@ import { PhonePortraitOutline as PhoneIcon, LockClosedOutline as PasswordIcon, - LogoGithub, - LogoAlipay, - LogoWechat, + // LogoGithub, + // LogoAlipay, + // LogoWechat, } from '@vicons/ionicons5' import useAppInfo from '@/hooks/useAppInfo' import useUserStore from '@/store/modules/user' @@ -140,7 +114,7 @@ export default defineComponent({ name: 'Login', - components: { PhoneIcon, PasswordIcon, LogoGithub, LogoAlipay, LogoWechat }, + components: { PhoneIcon, PasswordIcon }, setup() { const { version } = useAppInfo() const username = ref('') @@ -162,7 +136,7 @@ //加密 encryptStr.setKey(PUBLIC_KEY) formdata.append('username', username.value) - formdata.append('password', encryptStr.encrypt(password.value)) + formdata.append('password', encryptStr.encrypt(password.value).toString()) post({ url: login, data: formdata, @@ -191,6 +165,7 @@ loading, onLogin, ImageBg1, + ImageBg2, version, } }, @@ -222,9 +197,10 @@ display: block; position: relative; min-width: 450px; - width: 450px; + /*width: 450px;*/ & > img { + width: 100%; height: 100%; } @@ -274,12 +250,12 @@ font-weight: 500; font-size: 30px; // text-shadow: 1px 1px 2px #f5f5f5; - animation: left-to-right 1s cubic-bezier(0.175, 0.885, 0.32, 1.275); - text-shadow: 0 0 5px var(--primary-color), 0 0 15px var(--primary-color), - 0 0 50px var(--primary-color), 0 0 150px var(--primary-color); + /*animation: left-to-right 1s cubic-bezier(0.175, 0.885, 0.32, 1.275);*/ + /*text-shadow: 0 0 5px var(--primary-color), 0 0 15px var(--primary-color),*/ + /* 0 0 50px var(--primary-color), 0 0 150px var(--primary-color);*/ } - .bottom-wrapper { + .version { margin-bottom: 5%; color: #c0c0c0; font-size: 16px; @@ -293,14 +269,15 @@ justify-content: center; flex-direction: column; align-items: center; - background: linear-gradient(to bottom, var(--primary-color)); + width: 45%; + /*background: linear-gradient(to bottom, var(--primary-color));*/ .form-wrapper { width: 35%; border-radius: 5px; border: 1px solid #f0f0f0; padding: 20px; - box-shadow: 0px 0px 7px #dddddd; + /*box-shadow: 0px 0px 7px #dddddd;*/ .form-title { font-size: 26px; @@ -322,78 +299,4 @@ } } } - - .m-login-container { - position: relative; - height: 100vh; - max-height: 100vh; - overflow: hidden; - background: linear-gradient(#7a9ad7, #3b5a94, #133064); - // background-image: url(../../assets/img_login_mobile_bg_01.jpg); - .header { - height: 25vh; - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - - .the-p { - width: 100px; - height: 100px; - background: rgba(255, 255, 255, 0.2); - border: 1px solid #f5f5f5; - border-radius: 50%; - display: flex; - justify-content: center; - align-items: center; - font-size: 56px; - font-weight: bold; - } - } - - .top-line { - background-image: linear-gradient( - to right, - rgba(117, 117, 117, 0.9) 25%, - rgba(255, 255, 255, 0.3) 50%, - rgba(117, 117, 117, 0.9) 75% - ); - height: 1px; - background-color: #ffffff; - } - - .content { - height: 40vh; - margin: 5% 10%; - border-radius: 10px; - - :deep(.n-input) { - background-color: rgba(183, 183, 183, 0); - } - - :deep(.n-input .n-input__input-el, .n-input .n-input__textarea-el) { - color: #fff; - } - - :deep(.n-checkbox .n-checkbox__label) { - color: #fff; - } - } - - .footer { - position: absolute; - left: 10%; - right: 10%; - bottom: 10%; - - :deep(.n-divider .n-divider__title) { - color: #c3c3c3; - font-size: 14px; - } - - :deep(.n-divider:not(.n-divider--dashed) .n-divider__line) { - background-color: rgba(117, 117, 117); - } - } - } diff --git a/web/src/views/net_topology/show.vue b/web/src/views/net_topology/show.vue index 4aeb62854f9a48af79a74e183789486e118fca84..e484ccbed1f9ddd5f99f9c987d4c886bab85131c 100644 --- a/web/src/views/net_topology/show.vue +++ b/web/src/views/net_topology/show.vue @@ -566,9 +566,9 @@ const color = d3 .scaleOrdinal() .range(['red', 'green', 'blue', '#6b486b', '#a05d56', '#d0743c', '#ff8c00']) - const svg = ref(null) + const svg = ref(null) const zoom = ref(null) - const group = ref(null) + const group = ref(null) var svg_node = null var svg_link = null var svg_brush = null @@ -585,7 +585,6 @@ interface: [], }) const topology_value = ref(null) - // const topology_data = shallowReactive([]) as Array const topology_options = shallowReactive([]) as Array const showAddNodeButton = ref(false) function node_click(node) { @@ -1010,7 +1009,7 @@ // .force('collision', d3.forceCollide().radius(25)) svg.value = d3 - .select('svg#primary-svg') + .select(toppolgoy_svg.value) // .attr('viewBox', '0 0 3000 3000') .classed('svg-content', true) group.value = svg.value.append('g') diff --git a/web/src/views/net_topology/test.vue b/web/src/views/net_topology/test.vue new file mode 100644 index 0000000000000000000000000000000000000000..88bf5181e7a5e406d62bc54e1682d4b9d23a8e23 --- /dev/null +++ b/web/src/views/net_topology/test.vue @@ -0,0 +1,113 @@ + + + + + diff --git a/web/src/views/net_topology/test2.vue b/web/src/views/net_topology/test2.vue new file mode 100644 index 0000000000000000000000000000000000000000..7cc288459c9b28a4a8a647619ca44885ce1418c2 --- /dev/null +++ b/web/src/views/net_topology/test2.vue @@ -0,0 +1,224 @@ + + + + + diff --git a/web/vite.config.ts b/web/vite.config.ts index 66fce2a7a7758c5eea994405d28ec5bbbd17a8e6..6f97f06b1f2a509c9420a75f0b23a164ee36784a 100644 --- a/web/vite.config.ts +++ b/web/vite.config.ts @@ -6,77 +6,86 @@ import ViteComponents from 'unplugin-vue-components/vite' import { NaiveUiResolver } from 'unplugin-vue-components/resolvers' import { loadEnv } from 'vite' import vueJsx from '@vitejs/plugin-vue-jsx' + export default ({ mode }) => { - const env = loadEnv(mode, './') - const config = { - plugins: [ - vue(), - viteSvgIcons({ - iconDirs: [path.resolve(process.cwd(), 'src/icons')], - symbolId: 'icon-[dir]-[name]', - }), - vitePluginCompression({ - threshold: 1024 * 10, - }), - ViteComponents({ - resolvers: [NaiveUiResolver()], - }), - vueJsx(), - ], - resolve: { - alias: [ - { - find: '@/', - replacement: path.resolve(process.cwd(), 'src') + '/', + const env = loadEnv(mode, './') + const config = { + plugins: [ + vue(), + viteSvgIcons({ + iconDirs: [path.resolve(process.cwd(), 'src/icons')], + symbolId: 'icon-[dir]-[name]', + }), + vitePluginCompression({ + threshold: 1024 * 10, + }), + ViteComponents({ + resolvers: [NaiveUiResolver()], + }), + vueJsx(), + ], + resolve: { + alias: [ + { + find: '@/', + replacement: path.resolve(process.cwd(), 'src') + '/', + }, + ], }, - ], - }, - server: { - open: true, - port: 1005, - host: '0.0.0.0', - proxy: { - '/api': { - target: env.VITE_BASIC_URL, - ws: true, //代理websockets - changeOrigin: true, // 虚拟的站点需要更管origin - rewrite: (path: string) => path.replace(/^\/api/, '/api'), + server: { + open: true, + port: 8890, + host: '0.0.0.0', + hmr: { overlay: false }, + cors: true, + proxy: { + '/api': { + target: env.VITE_BASIC_URL, + // ws: true, //代理websockets + changeOrigin: true, // 虚拟的站点需要更管origin + rewrite: (path: string) => path.replace(/^\/api/, '/api'), + }, + '/rbac': { + target: env.VITE_BASIC_RBAC, + // ws: true, //代理websockets + changeOrigin: true, // 虚拟的站点需要更管origin + rewrite: (path: string) => path.replace(/^\/rbac/, '/rbac'), + }, + '/ipam': { + target: env.VITE_BASIC_URL, + // ws: true, //代理websockets + changeOrigin: true, // 虚拟的站点需要更管origin + rewrite: (path: string) => path.replace(/^\/ipam/, '/ipam'), + }, + '/media': { + target: env.VITE_BASIC_URL, + // ws: true, //代理websockets + changeOrigin: true, // 虚拟的站点需要更管origin + rewrite: (path: string) => path.replace(/^\/media/, '/media'), + }, + '/ws': { + target: env.VITE_BASIC_URL, + timeout: 60000, + ws: true, //代理websockets + changeOrigin: true, // 虚拟的站点需要更管origin + rewrite: (path: string) => path.replace(/^\/ws/, '/ws'), + }, + }, }, - '/ipam': { - target: env.VITE_BASIC_URL, - ws: true, //代理websockets - changeOrigin: true, // 虚拟的站点需要更管origin - rewrite: (path: string) => path.replace(/^\/ipam/, '/ipam'), - }, - '/media': { - target: env.VITE_BASIC_URL, - ws: true, //代理websockets - changeOrigin: true, // 虚拟的站点需要更管origin - rewrite: (path: string) => path.replace(/^\/media/, '/media'), - }, - '/ws': { - target: env.VITE_BASIC_URL, - timeout: 60000, - ws: true, //代理websockets - changeOrigin: true, // 虚拟的站点需要更管origin - rewrite: (path: string) => path.replace(/^\/ws/, '/ws'), - }, - }, - }, - } - if (mode === 'staging') { - return Object.assign( - { - base: '/admin-work/', - }, - config - ) - } else { - return Object.assign( - { - base: '/', - }, - config - ) - } + } + if (mode === 'staging') { + return Object.assign( + { + base: '/admin-work/', + }, + config + ) + } else { + return Object.assign( + { + base: '/', + }, + config + ) + } }