mirror of
https://github.com/jumpserver/jumpserver.git
synced 2025-06-24 22:12:00 +00:00
perf: some swagger api (#15203)
* perf: some swagger api * perf: update deps * perf: Update Dockerfile with new base image tag --------- Co-authored-by: ibuler <ibuler@qq.com> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
This commit is contained in:
parent
8b9fe3c72b
commit
5390fbacec
@ -1,4 +1,4 @@
|
||||
FROM jumpserver/core-base:20250224_065619 AS stage-build
|
||||
FROM jumpserver/core-base:20250415_032719 AS stage-build
|
||||
|
||||
ARG VERSION
|
||||
|
||||
|
@ -10,6 +10,7 @@ class VirtualAccountViewSet(OrgBulkModelViewSet):
|
||||
serializer_class = VirtualAccountSerializer
|
||||
search_fields = ('alias',)
|
||||
filterset_fields = ('alias',)
|
||||
http_method_names = ['get']
|
||||
|
||||
def get_queryset(self):
|
||||
return VirtualAccount.get_or_init_queryset()
|
||||
|
@ -39,9 +39,10 @@ class AutomationAssetsListApi(generics.ListAPIView):
|
||||
return assets
|
||||
|
||||
|
||||
class AutomationRemoveAssetApi(generics.RetrieveUpdateAPIView):
|
||||
class AutomationRemoveAssetApi(generics.UpdateAPIView):
|
||||
model = BaseAutomation
|
||||
serializer_class = serializers.UpdateAssetSerializer
|
||||
http_method_names = ['put']
|
||||
|
||||
def update(self, request, *args, **kwargs):
|
||||
instance = self.get_object()
|
||||
@ -56,9 +57,10 @@ class AutomationRemoveAssetApi(generics.RetrieveUpdateAPIView):
|
||||
return Response({'msg': 'ok'})
|
||||
|
||||
|
||||
class AutomationAddAssetApi(generics.RetrieveUpdateAPIView):
|
||||
class AutomationAddAssetApi(generics.UpdateAPIView):
|
||||
model = BaseAutomation
|
||||
serializer_class = serializers.UpdateAssetSerializer
|
||||
http_method_names = ['put']
|
||||
|
||||
def update(self, request, *args, **kwargs):
|
||||
instance = self.get_object()
|
||||
@ -72,9 +74,10 @@ class AutomationAddAssetApi(generics.RetrieveUpdateAPIView):
|
||||
return Response({"error": serializer.errors})
|
||||
|
||||
|
||||
class AutomationNodeAddRemoveApi(generics.RetrieveUpdateAPIView):
|
||||
class AutomationNodeAddRemoveApi(generics.UpdateAPIView):
|
||||
model = BaseAutomation
|
||||
serializer_class = serializers.UpdateNodeSerializer
|
||||
http_method_names = ['put']
|
||||
|
||||
def update(self, request, *args, **kwargs):
|
||||
action_params = ['add', 'remove']
|
||||
|
@ -147,6 +147,7 @@ class CheckAccountEngineViewSet(JMSModelViewSet):
|
||||
serializer_class = serializers.CheckAccountEngineSerializer
|
||||
permission_classes = [RBACPermission, IsValidLicense]
|
||||
perm_model = CheckAccountEngine
|
||||
http_method_names = ['get']
|
||||
|
||||
def get_queryset(self):
|
||||
return CheckAccountEngine.get_default_engines()
|
||||
|
@ -97,7 +97,7 @@ class AssetFilterSet(BaseFilterSet):
|
||||
return queryset.filter(protocols__name__in=value).distinct()
|
||||
|
||||
|
||||
class AssetViewSet(SuggestionMixin, OrgBulkModelViewSet):
|
||||
class BaseAssetViewSet(OrgBulkModelViewSet):
|
||||
"""
|
||||
API endpoint that allows Asset to be viewed or edited.
|
||||
"""
|
||||
@ -143,6 +143,19 @@ class AssetViewSet(SuggestionMixin, OrgBulkModelViewSet):
|
||||
return retrieve_cls
|
||||
return cls
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
if request.path.find('/api/v1/assets/assets/') > -1:
|
||||
error = _('Cannot create asset directly, you should create a host or other')
|
||||
return Response({'error': error}, status=400)
|
||||
|
||||
if not settings.XPACK_LICENSE_IS_VALID and self.model.objects.order_by().count() >= 5000:
|
||||
error = _('The number of assets exceeds the limit of 5000')
|
||||
return Response({'error': error}, status=400)
|
||||
|
||||
return super().create(request, *args, **kwargs)
|
||||
|
||||
|
||||
class AssetViewSet(SuggestionMixin, BaseAssetViewSet):
|
||||
@action(methods=["GET"], detail=True, url_path="platform")
|
||||
def platform(self, *args, **kwargs):
|
||||
asset = super().get_object()
|
||||
@ -197,17 +210,6 @@ class AssetViewSet(SuggestionMixin, OrgBulkModelViewSet):
|
||||
Protocol.objects.bulk_create(objs)
|
||||
return Response(status=status.HTTP_200_OK)
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
if request.path.find('/api/v1/assets/assets/') > -1:
|
||||
error = _('Cannot create asset directly, you should create a host or other')
|
||||
return Response({'error': error}, status=400)
|
||||
|
||||
if not settings.XPACK_LICENSE_IS_VALID and self.model.objects.order_by().count() >= 5000:
|
||||
error = _('The number of assets exceeds the limit of 5000')
|
||||
return Response({'error': error}, status=400)
|
||||
|
||||
return super().create(request, *args, **kwargs)
|
||||
|
||||
def filter_bulk_update_data(self):
|
||||
bulk_data = []
|
||||
skip_assets = []
|
||||
|
@ -1,12 +1,12 @@
|
||||
from assets.models import Cloud, Asset
|
||||
from assets.serializers import CloudSerializer
|
||||
|
||||
from .asset import AssetViewSet
|
||||
from .asset import BaseAssetViewSet
|
||||
|
||||
__all__ = ['CloudViewSet']
|
||||
|
||||
|
||||
class CloudViewSet(AssetViewSet):
|
||||
class CloudViewSet(BaseAssetViewSet):
|
||||
model = Cloud
|
||||
perm_model = Asset
|
||||
|
||||
|
@ -1,12 +1,12 @@
|
||||
from assets.models import Custom, Asset
|
||||
from assets.serializers import CustomSerializer
|
||||
|
||||
from .asset import AssetViewSet
|
||||
from .asset import BaseAssetViewSet
|
||||
|
||||
__all__ = ['CustomViewSet']
|
||||
|
||||
|
||||
class CustomViewSet(AssetViewSet):
|
||||
class CustomViewSet(BaseAssetViewSet):
|
||||
model = Custom
|
||||
perm_model = Asset
|
||||
|
||||
|
@ -1,12 +1,12 @@
|
||||
from assets.models import Database, Asset
|
||||
from assets.serializers import DatabaseSerializer
|
||||
|
||||
from .asset import AssetViewSet
|
||||
from .asset import BaseAssetViewSet
|
||||
|
||||
__all__ = ['DatabaseViewSet']
|
||||
|
||||
|
||||
class DatabaseViewSet(AssetViewSet):
|
||||
class DatabaseViewSet(BaseAssetViewSet):
|
||||
model = Database
|
||||
perm_model = Asset
|
||||
|
||||
|
@ -1,11 +1,11 @@
|
||||
from assets.serializers import DeviceSerializer
|
||||
from assets.models import Device, Asset
|
||||
from .asset import AssetViewSet
|
||||
from assets.serializers import DeviceSerializer
|
||||
from .asset import BaseAssetViewSet
|
||||
|
||||
__all__ = ['DeviceViewSet']
|
||||
|
||||
|
||||
class DeviceViewSet(AssetViewSet):
|
||||
class DeviceViewSet(BaseAssetViewSet):
|
||||
model = Device
|
||||
perm_model = Asset
|
||||
|
||||
|
@ -1,12 +1,12 @@
|
||||
from assets.models import DirectoryService, Asset
|
||||
from assets.serializers import DSSerializer
|
||||
|
||||
from .asset import AssetViewSet
|
||||
from .asset import BaseAssetViewSet
|
||||
|
||||
__all__ = ['DSViewSet']
|
||||
|
||||
|
||||
class DSViewSet(AssetViewSet):
|
||||
class DSViewSet(BaseAssetViewSet):
|
||||
model = DirectoryService
|
||||
perm_model = Asset
|
||||
|
||||
|
@ -1,12 +1,12 @@
|
||||
from assets.models import GPT, Asset
|
||||
from assets.serializers import GPTSerializer
|
||||
|
||||
from .asset import AssetViewSet
|
||||
from .asset import BaseAssetViewSet
|
||||
|
||||
__all__ = ['GPTViewSet']
|
||||
|
||||
|
||||
class GPTViewSet(AssetViewSet):
|
||||
class GPTViewSet(BaseAssetViewSet):
|
||||
model = GPT
|
||||
perm_model = Asset
|
||||
|
||||
|
@ -1,11 +1,11 @@
|
||||
from assets.models import Host, Asset
|
||||
from assets.serializers import HostSerializer
|
||||
from .asset import AssetViewSet
|
||||
from .asset import BaseAssetViewSet
|
||||
|
||||
__all__ = ['HostViewSet']
|
||||
|
||||
|
||||
class HostViewSet(AssetViewSet):
|
||||
class HostViewSet(BaseAssetViewSet):
|
||||
model = Host
|
||||
perm_model = Asset
|
||||
|
||||
|
@ -1,12 +1,12 @@
|
||||
from assets.models import Web, Asset
|
||||
from assets.serializers import WebSerializer
|
||||
|
||||
from .asset import AssetViewSet
|
||||
from .asset import BaseAssetViewSet
|
||||
|
||||
__all__ = ['WebViewSet']
|
||||
|
||||
|
||||
class WebViewSet(AssetViewSet):
|
||||
class WebViewSet(BaseAssetViewSet):
|
||||
model = Web
|
||||
perm_model = Asset
|
||||
|
||||
|
@ -188,6 +188,9 @@ class ActivityUnionLogSerializer(serializers.Serializer):
|
||||
class FileSerializer(serializers.Serializer):
|
||||
file = serializers.FileField(allow_empty_file=True)
|
||||
|
||||
class Meta:
|
||||
ref_name = 'AuditFileSerializer'
|
||||
|
||||
|
||||
class UserSessionSerializer(serializers.ModelSerializer):
|
||||
type = LabeledChoiceField(choices=LoginTypeChoices.choices, label=_("Type"))
|
||||
|
@ -2,6 +2,7 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import os
|
||||
import uuid
|
||||
|
||||
import private_storage.urls
|
||||
from django.conf import settings
|
||||
@ -74,12 +75,19 @@ urlpatterns += [
|
||||
path('core/jsi18n/', JavaScriptCatalog.as_view(), name='javascript-catalog'),
|
||||
]
|
||||
|
||||
DOC_TTL = 60 * 60
|
||||
DOC_VERSION = uuid.uuid4().hex
|
||||
cache_kwargs = {
|
||||
'cache_timeout': DOC_TTL,
|
||||
'cache_kwargs': {
|
||||
'key_prefix': 'swagger-cache-' + DOC_VERSION,
|
||||
},
|
||||
}
|
||||
# docs 路由
|
||||
urlpatterns += [
|
||||
re_path('^api/swagger(?P<format>\.json|\.yaml)$',
|
||||
views.get_swagger_view().without_ui(cache_timeout=1), name='schema-json'),
|
||||
re_path('api/docs/?', views.get_swagger_view().with_ui('swagger', cache_timeout=1), name="docs"),
|
||||
re_path('api/redoc/?', views.get_swagger_view().with_ui('redoc', cache_timeout=1), name='redoc'),
|
||||
path('api/swagger.<format>', views.get_swagger_view(False).without_ui(**cache_kwargs), name='schema-json'),
|
||||
re_path('api/docs/?', views.get_swagger_view().with_ui('swagger', **cache_kwargs), name="docs"),
|
||||
re_path('api/redoc/?', views.get_swagger_view().with_ui('redoc', **cache_kwargs), name='redoc'),
|
||||
]
|
||||
|
||||
if os.environ.get('DEBUG_TOOLBAR', False):
|
||||
|
@ -1,8 +1,54 @@
|
||||
from drf_yasg.inspectors import SwaggerAutoSchema
|
||||
|
||||
from rest_framework import permissions
|
||||
from drf_yasg.views import get_schema_view
|
||||
from drf_yasg import openapi
|
||||
from drf_yasg.generators import OpenAPISchemaGenerator
|
||||
from drf_yasg.inspectors import SwaggerAutoSchema
|
||||
from drf_yasg.views import get_schema_view
|
||||
from rest_framework import permissions
|
||||
|
||||
|
||||
class CustomSchemaGenerator(OpenAPISchemaGenerator):
|
||||
from_mcp = False
|
||||
|
||||
def get_schema(self, request=None, public=False):
|
||||
self.from_mcp = request.query_params.get('mcp') or request.path.endswith('swagger.json')
|
||||
return super().get_schema(request, public)
|
||||
|
||||
@staticmethod
|
||||
def exclude_some_paths(path):
|
||||
# 这里可以对 paths 进行处理
|
||||
excludes = ['/report/', '/render-to-json/', '/suggestions/', 'executions', 'automations']
|
||||
for p in excludes:
|
||||
if path.find(p) >= 0:
|
||||
return True
|
||||
return False
|
||||
|
||||
def exclude_some_app(self, path):
|
||||
parts = path.split('/')
|
||||
if len(parts) < 4:
|
||||
return False
|
||||
|
||||
apps = []
|
||||
if self.from_mcp:
|
||||
apps = [
|
||||
'ops', 'tickets', 'common', 'authentication',
|
||||
'settings', 'xpack', 'terminal', 'rbac'
|
||||
]
|
||||
|
||||
app_name = parts[3]
|
||||
if app_name in apps:
|
||||
return True
|
||||
return False
|
||||
|
||||
def get_operation(self, view, path, prefix, method, components, request):
|
||||
# 这里可以对 path 进行处理
|
||||
if self.exclude_some_paths(path):
|
||||
return None
|
||||
if self.exclude_some_app(path):
|
||||
return None
|
||||
operation = super().get_operation(view, path, prefix, method, components, request)
|
||||
operation_id = operation.get('operationId')
|
||||
if 'bulk' in operation_id:
|
||||
return None
|
||||
return operation
|
||||
|
||||
|
||||
class CustomSwaggerAutoSchema(SwaggerAutoSchema):
|
||||
@ -59,17 +105,25 @@ api_info = openapi.Info(
|
||||
)
|
||||
|
||||
|
||||
def get_swagger_view(version='v1'):
|
||||
def get_swagger_view(with_auth=True):
|
||||
from ..urls import api_v1
|
||||
from django.urls import path, include
|
||||
api_v1_patterns = [
|
||||
patterns = [
|
||||
path('api/v1/', include(api_v1))
|
||||
]
|
||||
patterns = api_v1_patterns
|
||||
|
||||
if with_auth:
|
||||
permission_classes = (permissions.IsAuthenticated,)
|
||||
public = False
|
||||
else:
|
||||
permission_classes = []
|
||||
public = True
|
||||
|
||||
schema_view = get_schema_view(
|
||||
api_info,
|
||||
public=public,
|
||||
patterns=patterns,
|
||||
permission_classes=(permissions.IsAuthenticated,),
|
||||
generator_class=CustomSchemaGenerator,
|
||||
permission_classes=permission_classes
|
||||
)
|
||||
return schema_view
|
||||
|
||||
|
@ -2,7 +2,6 @@
|
||||
#
|
||||
import logging
|
||||
|
||||
from django.conf import settings
|
||||
from django.db.models import Q
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django_filters import rest_framework as filters
|
||||
@ -10,7 +9,7 @@ from rest_framework import generics
|
||||
from rest_framework import status
|
||||
from rest_framework.views import APIView, Response
|
||||
|
||||
from common.api import JMSBulkModelViewSet
|
||||
from common.api import JMSModelViewSet
|
||||
from common.drf.filters import BaseFilterSet
|
||||
from common.exceptions import JMSException
|
||||
from common.permissions import WithBootstrapToken, IsServiceAccount
|
||||
@ -43,7 +42,7 @@ class TerminalFilterSet(BaseFilterSet):
|
||||
return queryset
|
||||
|
||||
|
||||
class TerminalViewSet(JMSBulkModelViewSet):
|
||||
class TerminalViewSet(JMSModelViewSet):
|
||||
queryset = Terminal.objects.filter(is_deleted=False)
|
||||
serializer_class = serializers.TerminalSerializer
|
||||
filterset_class = TerminalFilterSet
|
||||
|
@ -12,7 +12,6 @@ app_name = 'terminal'
|
||||
router = BulkRouter()
|
||||
router.register(r'sessions', api.SessionViewSet, 'session')
|
||||
router.register(r'terminals/((?P<terminal>[^/.]{36})/)?status', api.StatusViewSet, 'terminal-status')
|
||||
router.register(r'terminals/((?P<terminal>[^/.]{36})/)?sessions', api.SessionViewSet, 'terminal-sessions')
|
||||
router.register(r'terminals', api.TerminalViewSet, 'terminal')
|
||||
router.register(r'tasks', api.TaskViewSet, 'tasks')
|
||||
router.register(r'commands', api.CommandViewSet, 'command')
|
||||
|
2265
poetry.lock
generated
2265
poetry.lock
generated
File diff suppressed because it is too large
Load Diff
@ -99,7 +99,7 @@ django-private-storage = "3.1"
|
||||
drf-nested-routers = "0.93.4"
|
||||
drf-writable-nested = "0.7.0"
|
||||
rest-condition = "1.0.3"
|
||||
drf-yasg = "1.21.7"
|
||||
drf-yasg = "1.21.10"
|
||||
coreapi = "2.3.3"
|
||||
coreschema = "0.0.4"
|
||||
openapi-codec = "1.3.2"
|
||||
@ -148,7 +148,7 @@ mistune = "2.0.3"
|
||||
openai = "^1.29.0"
|
||||
xlsxwriter = "^3.1.9"
|
||||
exchangelib = "^5.1.0"
|
||||
xmlsec = "^1.3.13"
|
||||
xmlsec = "1.3.14"
|
||||
lxml = "5.2.1"
|
||||
pydantic = "^2.7.4"
|
||||
annotated-types = "^0.6.0"
|
||||
|
Loading…
Reference in New Issue
Block a user