From 6ba4b750f2e634157ee6c3ef973414675580fdf3 Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 1 Nov 2022 20:37:04 +0800 Subject: [PATCH] =?UTF-8?q?perf:=20=E4=BF=AE=E6=94=B9=20publications?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/terminal/api/applet/applet.py | 52 +++++++++---- apps/terminal/api/applet/host.py | 7 -- apps/terminal/models/applet/applet.py | 10 ++- apps/terminal/serializers/__init__.py | 1 + apps/terminal/serializers/applet.py | 95 +++--------------------- apps/terminal/serializers/applet_host.py | 88 ++++++++++++++++++++++ apps/terminal/signal_handlers.py | 10 ++- 7 files changed, 154 insertions(+), 109 deletions(-) create mode 100644 apps/terminal/serializers/applet_host.py diff --git a/apps/terminal/api/applet/applet.py b/apps/terminal/api/applet/applet.py index 54391cc48..aed2a6b70 100644 --- a/apps/terminal/api/applet/applet.py +++ b/apps/terminal/api/applet/applet.py @@ -1,5 +1,7 @@ import shutil import zipfile +from typing import Callable + import yaml import os.path @@ -7,6 +9,7 @@ from django.core.files.storage import default_storage from rest_framework import viewsets from django.http import HttpResponse from rest_framework.decorators import action +from rest_framework.request import Request from rest_framework.response import Response from rest_framework.serializers import ValidationError @@ -18,21 +21,10 @@ from terminal.serializers import AppletUploadSerializer __all__ = ['AppletViewSet', 'AppletPublicationViewSet'] -class AppletViewSet(viewsets.ModelViewSet): - queryset = Applet.objects.all() - serializer_class = serializers.AppletSerializer - rbac_perms = { - 'upload': 'terminal.add_applet', - 'download': 'terminal.view_applet', - } - - def perform_destroy(self, instance): - if not instance.name: - raise ValidationError('Applet is not null') - path = default_storage.path('applets/{}'.format(instance.name)) - if os.path.exists(path): - shutil.rmtree(path) - instance.delete() +class DownloadUploadMixin: + get_serializer: Callable + request: Request + get_object: Callable def extract_and_check_file(self, request): serializer = self.get_serializer(data=self.request.data) @@ -88,7 +80,7 @@ class AppletViewSet(viewsets.ModelViewSet): @action(detail=True, methods=['get']) def download(self, request, *args, **kwargs): - instance = super().get_object() + instance = self.get_object() path = default_storage.path('applets/{}'.format(instance.name)) zip_path = shutil.make_archive(path, 'zip', path) with open(zip_path, 'rb') as f: @@ -97,6 +89,34 @@ class AppletViewSet(viewsets.ModelViewSet): return response +class AppletViewSet(DownloadUploadMixin, viewsets.ModelViewSet): + queryset = Applet.objects.all() + serializer_class = serializers.AppletSerializer + rbac_perms = { + 'upload': 'terminal.add_applet', + 'download': 'terminal.view_applet', + } + + def filter_queryset(self, queryset): + queryset = list(super().filter_queryset(queryset)) + host = self.request.query_params.get('host') + if not host: + return queryset + + publication_mapper = {p.applet: p for p in AppletPublication.objects.filter(host_id=host)} + for applet in queryset: + applet.publication = publication_mapper.get(applet) + return queryset + + def perform_destroy(self, instance): + if not instance.name: + raise ValidationError('Applet is not null') + path = default_storage.path('applets/{}'.format(instance.name)) + if os.path.exists(path): + shutil.rmtree(path) + instance.delete() + + class AppletPublicationViewSet(viewsets.ModelViewSet): queryset = AppletPublication.objects.all() serializer_class = serializers.AppletPublicationSerializer diff --git a/apps/terminal/api/applet/host.py b/apps/terminal/api/applet/host.py index d9c7cde7e..e5dee8c7f 100644 --- a/apps/terminal/api/applet/host.py +++ b/apps/terminal/api/applet/host.py @@ -14,13 +14,6 @@ class AppletHostViewSet(viewsets.ModelViewSet): serializer_class = serializers.AppletHostSerializer queryset = AppletHost.objects.all() - @action(methods=['get'], detail=True, url_path='') - def not_published_applets(self, request, *args, **kwargs): - instance = self.get_object() - applets = Applet.objects.exclude(id__in=instance.applets.all()) - serializer = serializers.AppletSerializer(applets, many=True) - return Response(serializer.data) - class AppletHostDeploymentViewSet(viewsets.ModelViewSet): serializer_class = serializers.AppletHostDeploymentSerializer diff --git a/apps/terminal/models/applet/applet.py b/apps/terminal/models/applet/applet.py index 80be76e89..844158136 100644 --- a/apps/terminal/models/applet/applet.py +++ b/apps/terminal/models/applet/applet.py @@ -6,6 +6,7 @@ from django.core.files.storage import default_storage from django.db import models from django.utils.translation import gettext_lazy as _ +from common.utils import lazyproperty from common.db.models import JMSBaseModel @@ -26,7 +27,10 @@ class Applet(JMSBaseModel): protocols = models.JSONField(default=list, verbose_name=_('Protocol')) tags = models.JSONField(default=list, verbose_name=_('Tags')) comment = models.TextField(default='', blank=True, verbose_name=_('Comment')) - hosts = models.ManyToManyField(through_fields=('applet', 'host'), through='AppletPublication', to='AppletHost', verbose_name=_('Hosts')) + hosts = models.ManyToManyField( + through_fields=('applet', 'host'), through='AppletPublication', + to='AppletHost', verbose_name=_('Hosts') + ) def __str__(self): return self.name @@ -50,6 +54,10 @@ class Applet(JMSBaseModel): return None return os.path.join(settings.MEDIA_URL, 'applets', self.name, 'icon.png') + @lazyproperty + def publication(self): + return None + class AppletPublication(JMSBaseModel): applet = models.ForeignKey('Applet', on_delete=models.PROTECT, related_name='publications', verbose_name=_('Applet')) diff --git a/apps/terminal/serializers/__init__.py b/apps/terminal/serializers/__init__.py index c14d4fba3..019c3623c 100644 --- a/apps/terminal/serializers/__init__.py +++ b/apps/terminal/serializers/__init__.py @@ -6,3 +6,4 @@ from .storage import * from .sharing import * from .endpoint import * from .applet import * +from .applet_host import * diff --git a/apps/terminal/serializers/applet.py b/apps/terminal/serializers/applet.py index 9530120b9..3b98b446b 100644 --- a/apps/terminal/serializers/applet.py +++ b/apps/terminal/serializers/applet.py @@ -2,35 +2,15 @@ from rest_framework import serializers from django.utils.translation import gettext_lazy as _ from common.drf.fields import ObjectRelatedField, LabeledChoiceField -from common.validators import ProjectUniqueValidator -from assets.models import Platform -from assets.serializers import HostSerializer -from ..models import Applet, AppletPublication, AppletHost, AppletHostDeployment +from ..models import Applet, AppletPublication, AppletHost __all__ = [ 'AppletSerializer', 'AppletPublicationSerializer', - 'AppletHostSerializer', 'AppletHostDeploymentSerializer', 'AppletUploadSerializer', ] -class AppletSerializer(serializers.ModelSerializer): - icon = serializers.ReadOnlyField(label=_("Icon")) - type = LabeledChoiceField(choices=Applet.Type.choices, label=_("Type")) - - class Meta: - model = Applet - fields_mini = ['id', 'name', 'display_name'] - read_only_fields = [ - 'icon', 'date_created', 'date_updated' - ] - fields = fields_mini + [ - 'version', 'author', 'type', 'protocols', - 'tags', 'comment' - ] + read_only_fields - - class AppletUploadSerializer(serializers.Serializer): file = serializers.FileField() @@ -48,69 +28,18 @@ class AppletPublicationSerializer(serializers.ModelSerializer): ] + read_only_fields -class DeployOptionsSerializer(serializers.Serializer): - LICENSE_MODE_CHOICES = ( - (4, _('Per Session')), - (2, _('Per Device')), - ) - SESSION_PER_USER = ( - (1, _("Disabled")), - (0, _("Enabled")), - ) - RDS_Licensing = serializers.BooleanField(default=False, label=_("RDS Licensing")) - RDS_LicenseServer = serializers.CharField(default='127.0.0.1', label=_('RDS License Server'), max_length=1024) - RDS_LicensingMode = serializers.ChoiceField(choices=LICENSE_MODE_CHOICES, default=4, label=_('RDS Licensing Mode')) - RDS_fSingleSessionPerUser = serializers.ChoiceField(choices=SESSION_PER_USER, default=1, label=_("RDS fSingleSessionPerUser")) - RDS_MaxDisconnectionTime = serializers.IntegerField(default=60000, label=_("RDS Max Disconnection Time")) - RDS_RemoteAppLogoffTimeLimit = serializers.IntegerField(default=0, label=_("RDS Remote App Logoff Time Limit")) +class AppletSerializer(serializers.ModelSerializer): + icon = serializers.ReadOnlyField(label=_("Icon")) + type = LabeledChoiceField(choices=Applet.Type.choices, label=_("Type")) + publication = AppletPublicationSerializer(allow_null=True, label=_("Publication")) - -class AppletHostSerializer(HostSerializer): - deploy_options = DeployOptionsSerializer(required=False, label=_("Deploy options")) - - class Meta(HostSerializer.Meta): - model = AppletHost - fields = HostSerializer.Meta.fields + [ - 'account_automation', 'status', 'date_synced', 'deploy_options' - ] - extra_kwargs = { - 'status': {'read_only': True}, - 'date_synced': {'read_only': True} - } - - def __init__(self, *args, data=None, **kwargs): - if data: - self.set_initial_data(data) - kwargs['data'] = data - super().__init__(*args, **kwargs) - - @staticmethod - def set_initial_data(data): - platform = Platform.objects.get(name='RemoteAppHost') - data.update({ - 'platform': platform.id, - 'nodes_display': [ - 'RemoteAppHosts' - ] - }) - - def get_validators(self): - validators = super().get_validators() - # 不知道为啥没有继承过来 - uniq_validator = ProjectUniqueValidator( - queryset=AppletHost.objects.all(), - fields=('org_id', 'name') - ) - validators.append(uniq_validator) - return validators - - -class AppletHostDeploymentSerializer(serializers.ModelSerializer): class Meta: - model = AppletHostDeployment - fields_mini = ['id', 'host', 'status'] + model = Applet + fields_mini = ['id', 'name', 'display_name'] read_only_fields = [ - 'status', 'date_created', 'date_updated', - 'date_start', 'date_finished' + 'publication', 'icon', 'date_created', 'date_updated', ] - fields = fields_mini + ['comment'] + read_only_fields + fields = fields_mini + [ + 'version', 'author', 'type', 'protocols', + 'tags', 'comment' + ] + read_only_fields diff --git a/apps/terminal/serializers/applet_host.py b/apps/terminal/serializers/applet_host.py new file mode 100644 index 000000000..b87ee78d0 --- /dev/null +++ b/apps/terminal/serializers/applet_host.py @@ -0,0 +1,88 @@ +from rest_framework import serializers +from django.utils.translation import gettext_lazy as _ + +from common.validators import ProjectUniqueValidator +from assets.models import Platform +from assets.serializers import HostSerializer +from ..models import AppletHost, AppletHostDeployment, Applet +from .applet import AppletSerializer + + +__all__ = [ + 'AppletHostSerializer', 'AppletHostDeploymentSerializer', +] + + +class DeployOptionsSerializer(serializers.Serializer): + LICENSE_MODE_CHOICES = ( + (4, _('Per Session')), + (2, _('Per Device')), + ) + SESSION_PER_USER = ( + (1, _("Disabled")), + (0, _("Enabled")), + ) + RDS_Licensing = serializers.BooleanField(default=False, label=_("RDS Licensing")) + RDS_LicenseServer = serializers.CharField(default='127.0.0.1', label=_('RDS License Server'), max_length=1024) + RDS_LicensingMode = serializers.ChoiceField(choices=LICENSE_MODE_CHOICES, default=4, label=_('RDS Licensing Mode')) + RDS_fSingleSessionPerUser = serializers.ChoiceField(choices=SESSION_PER_USER, default=1, label=_("RDS fSingleSessionPerUser")) + RDS_MaxDisconnectionTime = serializers.IntegerField(default=60000, label=_("RDS Max Disconnection Time")) + RDS_RemoteAppLogoffTimeLimit = serializers.IntegerField(default=0, label=_("RDS Remote App Logoff Time Limit")) + + +class AppletHostSerializer(HostSerializer): + deploy_options = DeployOptionsSerializer(required=False, label=_("Deploy options")) + + class Meta(HostSerializer.Meta): + model = AppletHost + fields = HostSerializer.Meta.fields + [ + 'account_automation', 'status', 'date_synced', 'deploy_options' + ] + extra_kwargs = { + 'status': {'read_only': True}, + 'date_synced': {'read_only': True} + } + + def __init__(self, *args, data=None, **kwargs): + if data: + self.set_initial_data(data) + kwargs['data'] = data + super().__init__(*args, **kwargs) + + @staticmethod + def set_initial_data(data): + platform = Platform.objects.get(name='RemoteAppHost') + data.update({ + 'platform': platform.id, + 'nodes_display': [ + 'RemoteAppHosts' + ] + }) + + def get_validators(self): + validators = super().get_validators() + # 不知道为啥没有继承过来 + uniq_validator = ProjectUniqueValidator( + queryset=AppletHost.objects.all(), + fields=('org_id', 'name') + ) + validators.append(uniq_validator) + return validators + + +class HostAppletSerializer(AppletSerializer): + publication = serializers.SerializerMethodField() + + class Meta(AppletSerializer.Meta): + fields = AppletSerializer.Meta.fields + ['publication'] + + +class AppletHostDeploymentSerializer(serializers.ModelSerializer): + class Meta: + model = AppletHostDeployment + fields_mini = ['id', 'host', 'status'] + read_only_fields = [ + 'status', 'date_created', 'date_updated', + 'date_start', 'date_finished' + ] + fields = fields_mini + ['comment'] + read_only_fields diff --git a/apps/terminal/signal_handlers.py b/apps/terminal/signal_handlers.py index 5c92fd2ab..bac9c3253 100644 --- a/apps/terminal/signal_handlers.py +++ b/apps/terminal/signal_handlers.py @@ -10,9 +10,15 @@ from .models import Applet, AppletHost @receiver(post_save, sender=AppletHost) def on_applet_host_create(sender, instance, created=False, **kwargs): - pass + if not created: + return + applets = Applet.objects.all() + instance.applets.set(applets) @receiver(post_save, sender=Applet) def on_applet_create(sender, instance, created=False, **kwargs): - pass + if not created: + return + hosts = AppletHost.objects.all() + instance.hosts.set(hosts)