mirror of https://github.com/jumpserver/jumpserver
Co-authored-by: xinwen <coderWen@126.com>pull/5208/head
parent
79a371eb6c
commit
5863e3e008
|
@ -5,11 +5,13 @@ from collections import namedtuple, defaultdict
|
|||
from rest_framework import status
|
||||
from rest_framework.serializers import ValidationError
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.decorators import action
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.shortcuts import get_object_or_404, Http404
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.db.models.signals import m2m_changed
|
||||
|
||||
from common.const.http import POST
|
||||
from common.exceptions import SomeoneIsDoingThis
|
||||
from common.const.signals import PRE_REMOVE, POST_REMOVE
|
||||
from assets.models import Asset
|
||||
|
@ -19,6 +21,7 @@ from common.const.distributed_lock_key import UPDATE_NODE_TREE_LOCK_KEY
|
|||
from orgs.mixins.api import OrgModelViewSet
|
||||
from orgs.mixins import generics
|
||||
from orgs.lock import org_level_transaction_lock
|
||||
from assets.tasks import check_node_assets_amount_period_task
|
||||
from ..hands import IsOrgAdmin
|
||||
from ..models import Node
|
||||
from ..tasks import (
|
||||
|
@ -46,6 +49,11 @@ class NodeViewSet(OrgModelViewSet):
|
|||
permission_classes = (IsOrgAdmin,)
|
||||
serializer_class = serializers.NodeSerializer
|
||||
|
||||
@action(methods=[POST], detail=False, url_name='launch-check-assets-amount-task')
|
||||
def launch_check_assets_amount_task(self, request):
|
||||
task = check_node_assets_amount_period_task.delay()
|
||||
return Response(data={'task': task.id})
|
||||
|
||||
# 仅支持根节点指直接创建,子节点下的节点需要通过children接口创建
|
||||
def perform_create(self, serializer):
|
||||
child_key = Node.org_root().get_next_child_key()
|
||||
|
|
|
@ -1,13 +1,19 @@
|
|||
from celery import shared_task
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from ops.celery.decorator import register_as_period_task
|
||||
from assets.utils import check_node_assets_amount
|
||||
|
||||
from common.utils.lock import AcquireFailed
|
||||
from common.utils import get_logger
|
||||
|
||||
logger = get_logger(__file__)
|
||||
|
||||
|
||||
@register_as_period_task(crontab='0 2 * * *')
|
||||
@shared_task(queue='celery_heavy_tasks')
|
||||
def check_node_assets_amount_celery_task():
|
||||
check_node_assets_amount()
|
||||
@register_as_period_task(crontab='0 2 * * *')
|
||||
def check_node_assets_amount_period_task():
|
||||
try:
|
||||
check_node_assets_amount()
|
||||
except AcquireFailed:
|
||||
logger.error(_('The task of self-checking is already running and cannot be started repeatedly'))
|
||||
|
|
|
@ -5,6 +5,7 @@ import time
|
|||
from django.db.models import Q
|
||||
|
||||
from common.utils import get_logger, dict_get_any, is_uuid, get_object_or_none
|
||||
from common.utils.lock import DistributedLock
|
||||
from common.http import is_true
|
||||
from .models import Asset, Node
|
||||
|
||||
|
@ -12,6 +13,7 @@ from .models import Asset, Node
|
|||
logger = get_logger(__file__)
|
||||
|
||||
|
||||
@DistributedLock(name="assets.node.check_node_assets_amount", blocking=False)
|
||||
def check_node_assets_amount():
|
||||
for node in Node.objects.all():
|
||||
logger.info(f'Check node assets amount: {node}')
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
import inspect
|
||||
|
||||
|
||||
def copy_function_args(func, locals_dict: dict):
|
||||
signature = inspect.signature(func)
|
||||
keys = signature.parameters.keys()
|
||||
kwargs = {}
|
||||
for k in keys:
|
||||
kwargs[k] = locals_dict.get(k)
|
||||
return kwargs
|
|
@ -0,0 +1,55 @@
|
|||
from functools import wraps
|
||||
|
||||
from redis_lock import Lock as RedisLock
|
||||
from redis import Redis
|
||||
|
||||
from common.utils import get_logger
|
||||
from common.utils.inspect import copy_function_args
|
||||
from apps.jumpserver.const import CONFIG
|
||||
|
||||
logger = get_logger(__file__)
|
||||
|
||||
|
||||
class AcquireFailed(RuntimeError):
|
||||
pass
|
||||
|
||||
|
||||
class DistributedLock(RedisLock):
|
||||
def __init__(self, name, blocking=True, expire=60*2, auto_renewal=True):
|
||||
"""
|
||||
使用 redis 构造的分布式锁
|
||||
|
||||
:param name:
|
||||
锁的名字,要全局唯一
|
||||
:param blocking:
|
||||
该参数只在锁作为装饰器或者 `with` 时有效。
|
||||
:param expire:
|
||||
锁的过期时间,注意不一定是锁到这个时间就释放了,分两种情况
|
||||
当 `auto_renewal=False` 时,锁会释放
|
||||
当 `auto_renewal=True` 时,如果过期之前程序还没释放锁,我们会延长锁的存活时间。
|
||||
这里的作用是防止程序意外终止没有释放锁,导致死锁。
|
||||
"""
|
||||
self.kwargs_copy = copy_function_args(self.__init__, locals())
|
||||
redis = Redis(host=CONFIG.REDIS_HOST, port=CONFIG.REDIS_PORT, password=CONFIG.REDIS_PASSWORD)
|
||||
super().__init__(redis_client=redis, name=name, expire=expire, auto_renewal=auto_renewal)
|
||||
self._blocking = blocking
|
||||
|
||||
def __enter__(self):
|
||||
acquired = self.acquire(blocking=self._blocking)
|
||||
if self._blocking and not acquired:
|
||||
raise EnvironmentError("Lock wasn't acquired, but blocking=True")
|
||||
if not acquired:
|
||||
raise AcquireFailed
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type=None, exc_value=None, traceback=None):
|
||||
self.release()
|
||||
|
||||
def __call__(self, func):
|
||||
@wraps(func)
|
||||
def inner(*args, **kwds):
|
||||
# 要创建一个新的锁对象
|
||||
with self.__class__(**self.kwargs_copy):
|
||||
return func(*args, **kwds)
|
||||
|
||||
return inner
|
Binary file not shown.
|
@ -8,7 +8,7 @@ msgid ""
|
|||
msgstr ""
|
||||
"Project-Id-Version: JumpServer 0.3.3\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-12-09 18:14+0800\n"
|
||||
"POT-Creation-Date: 2020-12-10 17:04+0800\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: ibuler <ibuler@qq.com>\n"
|
||||
"Language-Team: JumpServer team<ibuler@qq.com>\n"
|
||||
|
@ -41,7 +41,7 @@ msgstr "远程应用"
|
|||
#: orgs/models.py:23 perms/models/base.py:48 settings/models.py:27
|
||||
#: terminal/models.py:28 terminal/models.py:372 terminal/models.py:404
|
||||
#: terminal/models.py:441 users/forms/profile.py:20 users/models/group.py:15
|
||||
#: users/models/user.py:501 users/templates/users/_select_user_modal.html:13
|
||||
#: users/models/user.py:495 users/templates/users/_select_user_modal.html:13
|
||||
#: users/templates/users/user_asset_permission.html:37
|
||||
#: users/templates/users/user_asset_permission.html:154
|
||||
#: users/templates/users/user_database_app_permission.html:36
|
||||
|
@ -94,7 +94,7 @@ msgstr "类型"
|
|||
#: assets/models/label.py:23 ops/models/adhoc.py:37 orgs/models.py:26
|
||||
#: perms/models/base.py:56 settings/models.py:32 terminal/models.py:38
|
||||
#: terminal/models.py:411 terminal/models.py:448 tickets/models/ticket.py:43
|
||||
#: users/models/group.py:16 users/models/user.py:534
|
||||
#: users/models/group.py:16 users/models/user.py:528
|
||||
#: users/templates/users/user_detail.html:115
|
||||
#: users/templates/users/user_granted_database_app.html:38
|
||||
#: users/templates/users/user_granted_remote_app.html:37
|
||||
|
@ -182,7 +182,7 @@ msgstr "参数"
|
|||
#: assets/models/cmd_filter.py:26 assets/models/cmd_filter.py:60
|
||||
#: assets/models/group.py:21 common/db/models.py:67 common/mixins/models.py:49
|
||||
#: orgs/models.py:24 orgs/models.py:400 perms/models/base.py:54
|
||||
#: users/models/user.py:542 users/serializers/group.py:35
|
||||
#: users/models/user.py:536 users/serializers/group.py:35
|
||||
#: users/templates/users/user_detail.html:97
|
||||
#: xpack/plugins/change_auth_plan/models.py:81 xpack/plugins/cloud/models.py:58
|
||||
#: xpack/plugins/cloud/models.py:156 xpack/plugins/gathered_user/models.py:30
|
||||
|
@ -237,7 +237,7 @@ msgstr "目标URL"
|
|||
#: authentication/forms.py:11
|
||||
#: authentication/templates/authentication/login.html:21
|
||||
#: authentication/templates/authentication/xpack_login.html:101
|
||||
#: ops/models/adhoc.py:148 users/forms/profile.py:19 users/models/user.py:499
|
||||
#: ops/models/adhoc.py:148 users/forms/profile.py:19 users/models/user.py:493
|
||||
#: users/templates/users/_select_user_modal.html:14
|
||||
#: users/templates/users/user_detail.html:53
|
||||
#: users/templates/users/user_list.html:15
|
||||
|
@ -251,7 +251,8 @@ msgstr "用户名"
|
|||
#: applications/serializers/remote_app.py:71
|
||||
#: applications/serializers/remote_app.py:79
|
||||
#: applications/serializers/remote_app.py:86 assets/models/base.py:236
|
||||
#: assets/serializers/asset_user.py:71 authentication/forms.py:13
|
||||
#: assets/serializers/asset_user.py:71 audits/signals_handler.py:42
|
||||
#: authentication/forms.py:13
|
||||
#: authentication/templates/authentication/login.html:29
|
||||
#: authentication/templates/authentication/xpack_login.html:109
|
||||
#: users/forms/user.py:22 users/forms/user.py:193
|
||||
|
@ -295,15 +296,15 @@ msgstr "删除失败,存在关联资产"
|
|||
msgid "Number required"
|
||||
msgstr "需要为数字"
|
||||
|
||||
#: assets/api/node.py:58
|
||||
#: assets/api/node.py:66
|
||||
msgid "You can't update the root node name"
|
||||
msgstr "不能修改根节点名称"
|
||||
|
||||
#: assets/api/node.py:65
|
||||
#: assets/api/node.py:73
|
||||
msgid "You can't delete the root node ({})"
|
||||
msgstr "不能删除根节点 ({})"
|
||||
|
||||
#: assets/api/node.py:68
|
||||
#: assets/api/node.py:76
|
||||
msgid "Deletion failed and the node contains children or assets"
|
||||
msgstr "删除失败,节点包含子节点或资产"
|
||||
|
||||
|
@ -364,7 +365,7 @@ msgstr "节点"
|
|||
|
||||
#: assets/models/asset.py:200 assets/models/cmd_filter.py:22
|
||||
#: assets/models/domain.py:56 assets/models/label.py:22
|
||||
#: authentication/models.py:48
|
||||
#: authentication/models.py:46
|
||||
msgid "Is active"
|
||||
msgstr "激活"
|
||||
|
||||
|
@ -484,7 +485,7 @@ msgstr "带宽"
|
|||
msgid "Contact"
|
||||
msgstr "联系人"
|
||||
|
||||
#: assets/models/cluster.py:22 users/models/user.py:520
|
||||
#: assets/models/cluster.py:22 users/models/user.py:514
|
||||
#: users/templates/users/user_detail.html:62
|
||||
msgid "Phone"
|
||||
msgstr "手机"
|
||||
|
@ -510,7 +511,7 @@ msgid "Default"
|
|||
msgstr "默认"
|
||||
|
||||
#: assets/models/cluster.py:36 assets/models/label.py:14
|
||||
#: users/models/user.py:661
|
||||
#: users/models/user.py:655
|
||||
msgid "System"
|
||||
msgstr "系统"
|
||||
|
||||
|
@ -611,8 +612,8 @@ msgid "Default asset group"
|
|||
msgstr "默认资产组"
|
||||
|
||||
#: assets/models/label.py:15 audits/models.py:36 audits/models.py:56
|
||||
#: audits/models.py:69 audits/serializers.py:80 authentication/models.py:46
|
||||
#: authentication/models.py:90 orgs/models.py:18 orgs/models.py:396
|
||||
#: audits/models.py:69 audits/serializers.py:81 authentication/models.py:44
|
||||
#: authentication/models.py:88 orgs/models.py:18 orgs/models.py:396
|
||||
#: perms/forms/asset_permission.py:83 perms/forms/database_app_permission.py:38
|
||||
#: perms/forms/remote_app_permission.py:40 perms/models/asset_permission.py:169
|
||||
#: perms/models/base.py:49 templates/index.html:78
|
||||
|
@ -621,7 +622,7 @@ msgstr "默认资产组"
|
|||
#: tickets/models/ticket.py:30 tickets/models/ticket.py:136
|
||||
#: tickets/serializers/request_asset_perm.py:66
|
||||
#: tickets/serializers/ticket.py:31 users/forms/group.py:15
|
||||
#: users/models/user.py:159 users/models/user.py:649
|
||||
#: users/models/user.py:159 users/models/user.py:643
|
||||
#: users/serializers/group.py:20
|
||||
#: users/templates/users/user_asset_permission.html:38
|
||||
#: users/templates/users/user_asset_permission.html:64
|
||||
|
@ -715,7 +716,7 @@ msgstr "登录模式"
|
|||
msgid "SFTP Root"
|
||||
msgstr "SFTP根路径"
|
||||
|
||||
#: assets/models/user.py:110 authentication/models.py:88
|
||||
#: assets/models/user.py:110 authentication/models.py:86
|
||||
msgid "Token"
|
||||
msgstr ""
|
||||
|
||||
|
@ -809,14 +810,14 @@ msgid "Backend"
|
|||
msgstr "后端"
|
||||
|
||||
#: assets/serializers/asset_user.py:75 users/forms/profile.py:148
|
||||
#: users/models/user.py:531 users/templates/users/user_password_update.html:48
|
||||
#: users/models/user.py:525 users/templates/users/user_password_update.html:48
|
||||
#: users/templates/users/user_profile.html:69
|
||||
#: users/templates/users/user_profile_update.html:46
|
||||
#: users/templates/users/user_pubkey_update.html:46
|
||||
msgid "Public key"
|
||||
msgstr "SSH公钥"
|
||||
|
||||
#: assets/serializers/asset_user.py:79 users/models/user.py:528
|
||||
#: assets/serializers/asset_user.py:79 users/models/user.py:522
|
||||
msgid "Private key"
|
||||
msgstr "ssh私钥"
|
||||
|
||||
|
@ -936,6 +937,11 @@ msgstr "更新节点资产硬件信息: {}"
|
|||
msgid "Gather assets users"
|
||||
msgstr "收集资产上的用户"
|
||||
|
||||
#: assets/tasks/nodes_amount.py:19
|
||||
msgid ""
|
||||
"The task of self-checking is already running and cannot be started repeatedly"
|
||||
msgstr "自检程序已经在运行,不能重复启动"
|
||||
|
||||
#: assets/tasks/push_system_user.py:184
|
||||
#: assets/tasks/system_user_connectivity.py:89
|
||||
msgid "System user is dynamic: {}"
|
||||
|
@ -1125,14 +1131,14 @@ msgstr "登录IP"
|
|||
msgid "Login city"
|
||||
msgstr "登录城市"
|
||||
|
||||
#: audits/models.py:103 audits/serializers.py:37
|
||||
#: audits/models.py:103 audits/serializers.py:38
|
||||
msgid "User agent"
|
||||
msgstr "用户代理"
|
||||
|
||||
#: audits/models.py:104
|
||||
#: authentication/templates/authentication/_mfa_confirm_modal.html:14
|
||||
#: authentication/templates/authentication/login_otp.html:6
|
||||
#: users/forms/profile.py:52 users/models/user.py:523
|
||||
#: users/forms/profile.py:52 users/models/user.py:517
|
||||
#: users/serializers/user.py:232 users/templates/users/user_detail.html:77
|
||||
#: users/templates/users/user_profile.html:87
|
||||
msgid "MFA"
|
||||
|
@ -1153,6 +1159,10 @@ msgstr "状态"
|
|||
msgid "Date login"
|
||||
msgstr "登录日期"
|
||||
|
||||
#: audits/models.py:108
|
||||
msgid "Login backend"
|
||||
msgstr "登录引擎"
|
||||
|
||||
#: audits/serializers.py:15
|
||||
msgid "Operate for display"
|
||||
msgstr "操作(显示名称)"
|
||||
|
@ -1165,32 +1175,40 @@ msgstr "状态(显示名称)"
|
|||
msgid "MFA for display"
|
||||
msgstr "多因子认证状态(显示名称)"
|
||||
|
||||
#: audits/serializers.py:65 audits/serializers.py:77 ops/models/adhoc.py:244
|
||||
#: audits/serializers.py:66 audits/serializers.py:78 ops/models/adhoc.py:244
|
||||
#: terminal/serializers/session.py:34
|
||||
msgid "Is success"
|
||||
msgstr "是否成功"
|
||||
|
||||
#: audits/serializers.py:76 ops/models/command.py:24
|
||||
#: audits/serializers.py:77 ops/models/command.py:24
|
||||
#: xpack/plugins/cloud/models.py:222
|
||||
msgid "Result"
|
||||
msgstr "结果"
|
||||
|
||||
#: audits/serializers.py:78
|
||||
#: audits/serializers.py:79
|
||||
msgid "Hosts"
|
||||
msgstr "主机"
|
||||
|
||||
#: audits/serializers.py:79
|
||||
#: audits/serializers.py:80
|
||||
msgid "Run as"
|
||||
msgstr "运行用户"
|
||||
|
||||
#: audits/serializers.py:81
|
||||
#: audits/serializers.py:82
|
||||
msgid "Run as for display"
|
||||
msgstr "运行用户(显示名称)"
|
||||
|
||||
#: audits/serializers.py:82
|
||||
#: audits/serializers.py:83
|
||||
msgid "User for display"
|
||||
msgstr "用户(显示名称)"
|
||||
|
||||
#: audits/signals_handler.py:38
|
||||
msgid "SSH Key"
|
||||
msgstr "SSH 密钥"
|
||||
|
||||
#: audits/signals_handler.py:43
|
||||
msgid "SSO"
|
||||
msgstr ""
|
||||
|
||||
#: authentication/api/mfa.py:60
|
||||
msgid "Code is invalid"
|
||||
msgstr "Code无效"
|
||||
|
@ -1333,7 +1351,7 @@ msgstr "你的密码过于简单,为了安全,请修改"
|
|||
|
||||
#: authentication/errors.py:227 authentication/views/login.py:262
|
||||
msgid "Your password has expired, please reset before logging in"
|
||||
msgstr "您的密码已过期,请先修改再登录"
|
||||
msgstr "您的密码已过期,先修改再登录"
|
||||
|
||||
#: authentication/forms.py:26 authentication/forms.py:34
|
||||
#: authentication/templates/authentication/login.html:39
|
||||
|
@ -1342,7 +1360,7 @@ msgstr "您的密码已过期,请先修改再登录"
|
|||
msgid "MFA code"
|
||||
msgstr "多因子认证验证码"
|
||||
|
||||
#: authentication/models.py:22
|
||||
#: authentication/models.py:20
|
||||
#: authentication/templates/authentication/_access_key_modal.html:32
|
||||
#: perms/models/base.py:51 users/templates/users/_select_user_modal.html:18
|
||||
#: users/templates/users/user_detail.html:132
|
||||
|
@ -1350,24 +1368,24 @@ msgstr "多因子认证验证码"
|
|||
msgid "Active"
|
||||
msgstr "激活中"
|
||||
|
||||
#: authentication/models.py:42
|
||||
#: authentication/models.py:40
|
||||
msgid "Private Token"
|
||||
msgstr "SSH密钥"
|
||||
|
||||
#: authentication/models.py:47 users/templates/users/user_detail.html:258
|
||||
#: authentication/models.py:45 users/templates/users/user_detail.html:258
|
||||
msgid "Reviewers"
|
||||
msgstr "审批人"
|
||||
|
||||
#: authentication/models.py:56 tickets/models/ticket.py:23
|
||||
#: authentication/models.py:54 tickets/models/ticket.py:23
|
||||
#: users/templates/users/user_detail.html:250
|
||||
msgid "Login confirm"
|
||||
msgstr "登录复核"
|
||||
|
||||
#: authentication/models.py:66
|
||||
#: authentication/models.py:64
|
||||
msgid "City"
|
||||
msgstr "城市"
|
||||
|
||||
#: authentication/models.py:89
|
||||
#: authentication/models.py:87
|
||||
msgid "Expired"
|
||||
msgstr "过期时间"
|
||||
|
||||
|
@ -1857,7 +1875,7 @@ msgstr "组织管理员"
|
|||
msgid "Organization auditor"
|
||||
msgstr "组织审计员"
|
||||
|
||||
#: orgs/models.py:397 users/forms/user.py:27 users/models/user.py:511
|
||||
#: orgs/models.py:397 users/forms/user.py:27 users/models/user.py:505
|
||||
#: users/templates/users/_select_user_modal.html:15
|
||||
#: users/templates/users/user_detail.html:73
|
||||
#: users/templates/users/user_list.html:16
|
||||
|
@ -1890,7 +1908,7 @@ msgstr "提示:RDP 协议不支持单独控制上传或下载文件"
|
|||
#: perms/forms/asset_permission.py:86 perms/forms/database_app_permission.py:41
|
||||
#: perms/forms/remote_app_permission.py:43 perms/models/base.py:50
|
||||
#: templates/_nav.html:21 users/forms/user.py:168 users/models/group.py:31
|
||||
#: users/models/user.py:507 users/templates/users/_select_user_modal.html:16
|
||||
#: users/models/user.py:501 users/templates/users/_select_user_modal.html:16
|
||||
#: users/templates/users/user_asset_permission.html:39
|
||||
#: users/templates/users/user_asset_permission.html:67
|
||||
#: users/templates/users/user_database_app_permission.html:38
|
||||
|
@ -1964,7 +1982,7 @@ msgid "Asset permission"
|
|||
msgstr "资产授权"
|
||||
|
||||
#: perms/models/base.py:53 tickets/serializers/request_asset_perm.py:31
|
||||
#: users/models/user.py:539 users/templates/users/user_detail.html:93
|
||||
#: users/models/user.py:533 users/templates/users/user_detail.html:93
|
||||
#: users/templates/users/user_profile.html:120
|
||||
msgid "Date expired"
|
||||
msgstr "失效日期"
|
||||
|
@ -3116,7 +3134,7 @@ msgstr "确认密码"
|
|||
msgid "Password does not match"
|
||||
msgstr "密码不一致"
|
||||
|
||||
#: users/forms/profile.py:89 users/models/user.py:503
|
||||
#: users/forms/profile.py:89 users/models/user.py:497
|
||||
#: users/templates/users/user_detail.html:57
|
||||
#: users/templates/users/user_profile.html:59
|
||||
msgid "Email"
|
||||
|
@ -3157,7 +3175,7 @@ msgstr "不能和原来的密钥相同"
|
|||
msgid "Not a valid ssh public key"
|
||||
msgstr "SSH密钥不合法"
|
||||
|
||||
#: users/forms/user.py:31 users/models/user.py:546
|
||||
#: users/forms/user.py:31 users/models/user.py:540
|
||||
#: users/templates/users/user_detail.html:89
|
||||
#: users/templates/users/user_list.html:18
|
||||
#: users/templates/users/user_profile.html:102
|
||||
|
@ -3203,27 +3221,27 @@ msgstr "系统审计员"
|
|||
msgid "Force enable"
|
||||
msgstr "强制启用"
|
||||
|
||||
#: users/models/user.py:490
|
||||
#: users/models/user.py:485
|
||||
msgid "Local"
|
||||
msgstr "数据库"
|
||||
|
||||
#: users/models/user.py:514
|
||||
#: users/models/user.py:508
|
||||
msgid "Avatar"
|
||||
msgstr "头像"
|
||||
|
||||
#: users/models/user.py:517 users/templates/users/user_detail.html:68
|
||||
#: users/models/user.py:511 users/templates/users/user_detail.html:68
|
||||
msgid "Wechat"
|
||||
msgstr "微信"
|
||||
|
||||
#: users/models/user.py:550
|
||||
#: users/models/user.py:544
|
||||
msgid "Date password last updated"
|
||||
msgstr "最后更新密码日期"
|
||||
|
||||
#: users/models/user.py:657
|
||||
#: users/models/user.py:651
|
||||
msgid "Administrator"
|
||||
msgstr "管理员"
|
||||
|
||||
#: users/models/user.py:660
|
||||
#: users/models/user.py:654
|
||||
msgid "Administrator is the super user of system"
|
||||
msgstr "Administrator是初始的超级管理员"
|
||||
|
||||
|
|
Loading…
Reference in New Issue