diff --git a/apps/applications/serializers/attrs/application_category/remote_app.py b/apps/applications/serializers/attrs/application_category/remote_app.py index 11042049e..bace9173b 100644 --- a/apps/applications/serializers/attrs/application_category/remote_app.py +++ b/apps/applications/serializers/attrs/application_category/remote_app.py @@ -34,7 +34,7 @@ class ExistAssetPrimaryKeyRelatedField(serializers.PrimaryKeyRelatedField): class RemoteAppSerializer(serializers.Serializer): asset_info = serializers.SerializerMethodField() asset = ExistAssetPrimaryKeyRelatedField( - queryset=Asset.objects, required=False, label=_("Asset"), allow_null=True + queryset=Asset.objects, required=True, label=_("Asset"), allow_null=True ) path = serializers.CharField( max_length=128, label=_('Application path'), allow_null=True @@ -42,7 +42,7 @@ class RemoteAppSerializer(serializers.Serializer): def validate_asset(self, asset): if not asset: - raise serializers.ValidationError(_('This field is required')) + raise serializers.ValidationError(_('This field is required.')) return asset @staticmethod diff --git a/apps/assets/serializers/account.py b/apps/assets/serializers/account.py index df0fdb72d..acca74ce1 100644 --- a/apps/assets/serializers/account.py +++ b/apps/assets/serializers/account.py @@ -49,6 +49,7 @@ class AccountSecretSerializer(AccountSerializer): 'password': {'write_only': False}, 'private_key': {'write_only': False}, 'public_key': {'write_only': False}, + 'systemuser_display': {'label': _('System user display')} } diff --git a/apps/assets/signals_handler/authbook.py b/apps/assets/signals_handler/authbook.py index f1cb2303c..2a149d57a 100644 --- a/apps/assets/signals_handler/authbook.py +++ b/apps/assets/signals_handler/authbook.py @@ -37,6 +37,6 @@ def on_authbook_post_create(sender, instance, **kwargs): @receiver(pre_save, sender=AuthBook) def on_authbook_pre_create(sender, instance, **kwargs): # 升级版本号 - instance.version = instance.history.all().count() + 1 + instance.version += 1 # 即使在 root 组织也不怕 instance.org_id = instance.asset.org_id diff --git a/apps/authentication/urls/view_urls.py b/apps/authentication/urls/view_urls.py index 0bac07e25..92d5818cb 100644 --- a/apps/authentication/urls/view_urls.py +++ b/apps/authentication/urls/view_urls.py @@ -2,6 +2,7 @@ # from django.urls import path, include +from django.db.transaction import non_atomic_requests from .. import views from users import views as users_view @@ -10,7 +11,7 @@ app_name = 'authentication' urlpatterns = [ # login - path('login/', views.UserLoginView.as_view(), name='login'), + path('login/', non_atomic_requests(views.UserLoginView.as_view()), name='login'), path('login/otp/', views.UserLoginOtpView.as_view(), name='login-otp'), path('login/wait-confirm/', views.UserLoginWaitConfirmView.as_view(), name='login-wait-confirm'), path('login/guard/', views.UserLoginGuardView.as_view(), name='login-guard'), diff --git a/apps/authentication/views/login.py b/apps/authentication/views/login.py index 6a2481d20..3fc62e08d 100644 --- a/apps/authentication/views/login.py +++ b/apps/authentication/views/login.py @@ -4,6 +4,7 @@ from __future__ import unicode_literals import os import datetime + from django.contrib.auth import login as auth_login, logout as auth_logout from django.http import HttpResponse from django.shortcuts import reverse, redirect @@ -17,6 +18,7 @@ from django.views.generic.edit import FormView from django.conf import settings from django.urls import reverse_lazy from django.contrib.auth import BACKEND_SESSION_KEY +from django.db.transaction import atomic from common.utils import get_request_ip, FlashMessageUtil from users.utils import ( @@ -107,7 +109,8 @@ class UserLoginView(mixins.AuthMixin, FormView): self.request.session.delete_test_cookie() try: - self.check_user_auth(decrypt_passwd=True) + with atomic(): + self.check_user_auth(decrypt_passwd=True) except errors.AuthFailedError as e: form.add_error(None, e.msg) self.set_login_failed_mark() diff --git a/apps/common/management/commands/expire_caches.py b/apps/common/management/commands/expire_caches.py index fb09f47eb..37fe7605b 100644 --- a/apps/common/management/commands/expire_caches.py +++ b/apps/common/management/commands/expire_caches.py @@ -1,6 +1,7 @@ from django.core.management.base import BaseCommand from assets.signals_handler.node_assets_mapping import expire_node_assets_mapping_for_memory +from orgs.caches import OrgResourceStatisticsCache from orgs.models import Organization @@ -12,8 +13,16 @@ def expire_node_assets_mapping(): expire_node_assets_mapping_for_memory(org_id) +def expire_org_resource_statistics_cache(): + orgs = Organization.objects.all() + for org in orgs: + cache = OrgResourceStatisticsCache(org) + cache.expire() + + class Command(BaseCommand): help = 'Expire caches' def handle(self, *args, **options): expire_node_assets_mapping() + expire_org_resource_statistics_cache() diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index c8ce1bef4..2f75f059b 100644 Binary files a/apps/locale/zh/LC_MESSAGES/django.mo and b/apps/locale/zh/LC_MESSAGES/django.mo differ diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index d1b4a93af..f9bf84e0c 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -389,8 +389,8 @@ msgstr "应用路径" #: applications/serializers/attrs/application_category/remote_app.py:45 #: xpack/plugins/cloud/serializers.py:51 -msgid "This field is required" -msgstr "这个字段是必填项" +msgid "This field is required." +msgstr "该字段是必填项。" #: applications/serializers/attrs/application_type/chrome.py:17 #: applications/serializers/attrs/application_type/vmware_client.py:22