From e7dc9a2f6fa7e6a9913a3f2a18d651125bce3e07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B4=E5=B0=8F=E7=99=BD?= <296015668@qq.com> Date: Fri, 17 Nov 2023 09:49:05 +0800 Subject: [PATCH 001/111] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=20Dockerfile?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile-ce | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/Dockerfile-ce b/Dockerfile-ce index 55ac03003..be974828f 100644 --- a/Dockerfile-ce +++ b/Dockerfile-ce @@ -44,8 +44,8 @@ ARG TOOLS=" \ wget" ARG APT_MIRROR=http://mirrors.ustc.edu.cn -RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=core \ - --mount=type=cache,target=/var/lib/apt,sharing=locked,id=core \ +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=core-apt \ + --mount=type=cache,target=/var/lib/apt,sharing=locked,id=core-apt \ sed -i "s@http://.*.debian.org@${APT_MIRROR}@g" /etc/apt/sources.list \ && rm -f /etc/apt/apt.conf.d/docker-clean \ && ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ @@ -63,9 +63,9 @@ RUN --mount=type=cache,target=/root/.cache \ --mount=type=bind,source=pyproject.toml,target=/opt/jumpserver/pyproject.toml \ set -ex \ && python3 -m venv /opt/py3 \ - && . /opt/py3/bin/activate \ && pip install poetry -i ${PIP_MIRROR} \ && poetry config virtualenvs.create false \ + && . /opt/py3/bin/activate \ && poetry install FROM python:3.11-slim-bullseye @@ -75,8 +75,8 @@ ENV LANG=zh_CN.UTF-8 \ ARG DEPENDENCIES=" \ libjpeg-dev \ - libxmlsec1-openssl \ - libx11-dev" + libx11-dev \ + libxmlsec1-openssl" ARG TOOLS=" \ ca-certificates \ @@ -94,8 +94,8 @@ ARG TOOLS=" \ wget" ARG APT_MIRROR=http://mirrors.ustc.edu.cn -RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=core \ - --mount=type=cache,target=/var/lib/apt,sharing=locked,id=core \ +RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=core-apt \ + --mount=type=cache,target=/var/lib/apt,sharing=locked,id=core-apt \ sed -i "s@http://.*.debian.org@${APT_MIRROR}@g" /etc/apt/sources.list \ && rm -f /etc/apt/apt.conf.d/docker-clean \ && ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ @@ -118,7 +118,6 @@ ARG VERSION ENV VERSION=$VERSION VOLUME /opt/jumpserver/data -VOLUME /opt/jumpserver/logs EXPOSE 8080 From 814130204ac970062de4281cfb3c145e9510a12f Mon Sep 17 00:00:00 2001 From: Aaron3S Date: Fri, 17 Nov 2023 10:45:31 +0800 Subject: [PATCH 002/111] =?UTF-8?q?fix:=20=E5=88=A0=E9=99=A4debug=E4=BF=A1?= =?UTF-8?q?=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/ops/models/job.py | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/ops/models/job.py b/apps/ops/models/job.py index 56d826e86..5dea9a73e 100644 --- a/apps/ops/models/job.py +++ b/apps/ops/models/job.py @@ -320,7 +320,6 @@ class JobExecution(JMSOrgBaseModel): "login_password={{login_password}} " \ "login_port={{login_port}} " \ "%s={{login_db}}" % login_db_token - print(login_args) shell = "{} {}=\"{}\" ".format(login_args, query_token, self.current_job.args) return module, shell From ed92f10208115486934613513b52954291ed2c1e Mon Sep 17 00:00:00 2001 From: ibuler Date: Fri, 17 Nov 2023 15:37:23 +0800 Subject: [PATCH 003/111] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E7=A6=81=E7=94=A8=E9=9D=9E=E6=B4=BB=E8=B7=83=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E4=BB=BB=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/users/tasks.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/users/tasks.py b/apps/users/tasks.py index ecc59c3bc..4c7887c0f 100644 --- a/apps/users/tasks.py +++ b/apps/users/tasks.py @@ -88,8 +88,8 @@ def check_unused_users(): uncommon_users_ttl = settings.SECURITY_UNCOMMON_USERS_TTL seconds_to_subtract = uncommon_users_ttl * 24 * 60 * 60 t = timezone.now() - timedelta(seconds=seconds_to_subtract) - last_login_q = Q(last_login__lte=t) | Q(last_login__isnull=True) - api_key_q = Q(date_api_key_last_used__lte=t) | Q(date_api_key_last_used__isnull=True) + last_login_q = Q(last_login__lte=t) | (Q(last_login__isnull=True) & Q(date_joined__lte=t)) + api_key_q = Q(date_api_key_last_used__lte=t) | (Q(date_api_key_last_used__isnull=True) & Q(date_joined__lte=t)) users = User.objects \ .filter(date_joined__lt=t) \ @@ -99,6 +99,7 @@ def check_unused_users(): if not users: return + print("Some users are not used for a long time, and they will be disabled.") resource_ids = [] for user in users: From 452b3832789388f21a499189a4de0c89da8d3d13 Mon Sep 17 00:00:00 2001 From: feng <1304903146@qq.com> Date: Fri, 17 Nov 2023 22:31:01 +0800 Subject: [PATCH 004/111] =?UTF-8?q?fix:=20redis=20=E5=BC=80=E5=90=AF=20ssl?= =?UTF-8?q?=20websocket=E8=BF=9E=E6=8E=A5=E5=A4=B1=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/jumpserver/settings/libs.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/apps/jumpserver/settings/libs.py b/apps/jumpserver/settings/libs.py index 3679320c4..817db8861 100644 --- a/apps/jumpserver/settings/libs.py +++ b/apps/jumpserver/settings/libs.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- # import os -from urllib.parse import urlencode from .base import ( REDIS_SSL_CA, REDIS_SSL_CERT, REDIS_SSL_KEY, REDIS_SSL_REQUIRED, REDIS_USE_SSL, @@ -88,7 +87,6 @@ REDIS_LAYERS_HOST = { REDIS_LAYERS_SSL_PARAMS = {} if REDIS_USE_SSL: REDIS_LAYERS_SSL_PARAMS.update({ - 'ssl': REDIS_USE_SSL, 'ssl_cert_reqs': REDIS_SSL_REQUIRED, "ssl_keyfile": REDIS_SSL_KEY, "ssl_certfile": REDIS_SSL_CERT, @@ -115,9 +113,7 @@ else: protocol=REDIS_PROTOCOL, password=CONFIG.REDIS_PASSWORD, host=CONFIG.REDIS_HOST, port=CONFIG.REDIS_PORT, db=CONFIG.REDIS_DB_WS ) - REDIS_LAYERS_SSL_PARAMS.pop('ssl', None) - REDIS_LAYERS_HOST['address'] = '{}?{}'.format(REDIS_LAYERS_ADDRESS, - urlencode(REDIS_LAYERS_SSL_PARAMS)) + REDIS_LAYERS_HOST['address'] = REDIS_LAYERS_ADDRESS CHANNEL_LAYERS = { 'default': { From 07e1918fa1e14a8e0e663aae202000840ae842b4 Mon Sep 17 00:00:00 2001 From: ibuler Date: Fri, 17 Nov 2023 17:07:24 +0800 Subject: [PATCH 005/111] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E5=BB=B6?= =?UTF-8?q?=E8=BF=9F=E8=BF=90=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix: 延迟执行设置超时 perf: 修改 delay run perf: 优化 delay_run 执行 perf: 修改 delay run --- apps/authentication/backends/drf.py | 24 +++++++++++++----------- apps/common/decorators.py | 25 ++++++++++++++++++++----- apps/orgs/signal_handlers/common.py | 2 +- 3 files changed, 34 insertions(+), 17 deletions(-) diff --git a/apps/authentication/backends/drf.py b/apps/authentication/backends/drf.py index f6818e4a4..86999037d 100644 --- a/apps/authentication/backends/drf.py +++ b/apps/authentication/backends/drf.py @@ -8,7 +8,7 @@ from django.utils.translation import gettext as _ from rest_framework import authentication, exceptions from common.auth import signature -from common.decorators import delay_run +from common.decorators import merge_delay_run from common.utils import get_object_or_none, get_request_ip_or_data, contains_ip from ..models import AccessKey, PrivateToken @@ -17,22 +17,24 @@ def date_more_than(d, seconds): return d is None or (timezone.now() - d).seconds > seconds -@delay_run(ttl=60) -def update_token_last_used(token): - token.date_last_used = timezone.now() - token.save(update_fields=['date_last_used']) +@merge_delay_run(ttl=60) +def update_token_last_used(tokens=()): + for token in tokens: + token.date_last_used = timezone.now() + token.save(update_fields=['date_last_used']) -@delay_run(ttl=60) -def update_user_last_used(user): - user.date_api_key_last_used = timezone.now() - user.save(update_fields=['date_api_key_last_used']) +@merge_delay_run(ttl=60) +def update_user_last_used(users=()): + for user in users: + user.date_api_key_last_used = timezone.now() + user.save(update_fields=['date_api_key_last_used']) def after_authenticate_update_date(user, token=None): - update_user_last_used(user) + update_user_last_used(users=(user,)) if token: - update_token_last_used(token) + update_token_last_used(tokens=(token,)) class AccessTokenAuthentication(authentication.BaseAuthentication): diff --git a/apps/common/decorators.py b/apps/common/decorators.py index 353407ab1..24b2695d3 100644 --- a/apps/common/decorators.py +++ b/apps/common/decorators.py @@ -73,6 +73,7 @@ executor = ThreadPoolExecutor( ) _loop_debouncer_func_task_cache = {} _loop_debouncer_func_args_cache = {} +_loop_debouncer_func_task_time_cache = {} def get_loop(): @@ -92,6 +93,17 @@ def cancel_or_remove_debouncer_task(cache_key): def run_debouncer_func(cache_key, org, ttl, func, *args, **kwargs): cancel_or_remove_debouncer_task(cache_key) run_func_partial = functools.partial(_run_func_with_org, cache_key, org, func) + + current = time.time() + first_run_time = _loop_debouncer_func_task_time_cache.get(cache_key, None) + if first_run_time is None: + _loop_debouncer_func_task_time_cache[cache_key] = current + first_run_time = current + + if current - first_run_time > ttl: + executor.submit(run_func_partial, *args, **kwargs) + return + loop = _loop_thread.get_loop() _debouncer = Debouncer(run_func_partial, lambda: True, ttl, loop=loop, executor=executor) task = asyncio.run_coroutine_threadsafe(_debouncer(*args, **kwargs), loop=loop) @@ -130,6 +142,7 @@ def _run_func_with_org(key, org, func, *args, **kwargs): logger.error('delay run error: %s' % e) _loop_debouncer_func_task_cache.pop(key, None) _loop_debouncer_func_args_cache.pop(key, None) + _loop_debouncer_func_task_time_cache.pop(key, None) def delay_run(ttl=5, key=None): @@ -142,6 +155,9 @@ def delay_run(ttl=5, key=None): def inner(func): suffix_key_func = key if key else default_suffix_key + sigs = inspect.signature(func) + if len(sigs.parameters) != 0: + raise ValueError('Merge delay run must not arguments: %s' % func.__name__) @functools.wraps(func) def wrapper(*args, **kwargs): @@ -186,12 +202,11 @@ def merge_delay_run(ttl=5, key=None): for k, v in kwargs.items(): if not isinstance(v, (tuple, list, set)): raise ValueError('func kwargs value must be list or tuple: %s %s' % (func.__name__, v)) + v = set(v) if k not in cache_kwargs: cache_kwargs[k] = v - elif isinstance(v, set): - cache_kwargs[k] = cache_kwargs[k].union(v) else: - cache_kwargs[k] = list(cache_kwargs[k]) + list(v) + cache_kwargs[k] = cache_kwargs[k].union(v) _loop_debouncer_func_args_cache[cache_key] = cache_kwargs run_debouncer_func(cache_key, org, ttl, func, *args, **cache_kwargs) @@ -201,8 +216,8 @@ def merge_delay_run(ttl=5, key=None): @delay_run(ttl=5) -def test_delay_run(username): - print("Hello, %s, now is %s" % (username, time.time())) +def test_delay_run(): + print("Hello, now is %s" % time.time()) @merge_delay_run(ttl=5, key=lambda users=(): users[0][0]) diff --git a/apps/orgs/signal_handlers/common.py b/apps/orgs/signal_handlers/common.py index 83edeb425..57333a2e8 100644 --- a/apps/orgs/signal_handlers/common.py +++ b/apps/orgs/signal_handlers/common.py @@ -53,7 +53,7 @@ def subscribe_orgs_mapping_expire(sender, **kwargs): @delay_run(ttl=5) -def expire_user_orgs(*args): +def expire_user_orgs(): User.expire_users_rbac_perms_cache() From 2edcb2f2d3c69d6761714e1b3600d76a584e63d3 Mon Sep 17 00:00:00 2001 From: feng <1304903146@qq.com> Date: Mon, 20 Nov 2023 10:45:36 +0800 Subject: [PATCH 006/111] =?UTF-8?q?fix:=20mysql=20=E5=BC=80=E5=90=AFssl=20?= =?UTF-8?q?=E5=86=8D=E5=85=B3=E9=97=AD=20=E6=B5=8B=E8=AF=95=E5=8F=AF?= =?UTF-8?q?=E8=BF=9E=E6=8E=A5=E6=80=A7=E5=A4=B1=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../change_secret/database/mysql/main.yml | 25 ++++++++++--------- .../gather_accounts/database/mysql/main.yml | 9 ++++--- .../push_account/database/mysql/main.yml | 25 ++++++++++--------- .../verify_account/database/mysql/main.yml | 9 ++++--- .../gather_facts/database/mysql/main.yml | 9 ++++--- .../automations/ping/database/mysql/main.yml | 9 ++++--- 6 files changed, 46 insertions(+), 40 deletions(-) diff --git a/apps/accounts/automations/change_secret/database/mysql/main.yml b/apps/accounts/automations/change_secret/database/mysql/main.yml index 91b7d6f2c..a423064d3 100644 --- a/apps/accounts/automations/change_secret/database/mysql/main.yml +++ b/apps/accounts/automations/change_secret/database/mysql/main.yml @@ -3,6 +3,7 @@ vars: ansible_python_interpreter: /opt/py3/bin/python db_name: "{{ jms_asset.spec_info.db_name }}" + jms_use_ssl: "{{ jms_asset.spec_info.use_ssl }}" tasks: - name: Test MySQL connection @@ -11,10 +12,10 @@ login_password: "{{ jms_account.secret }}" login_host: "{{ jms_asset.address }}" login_port: "{{ jms_asset.port }}" - check_hostname: "{{ omit if not jms_asset.spec_info.use_ssl else jms_asset.spec_info.allow_invalid_cert }}" - ca_cert: "{{ jms_asset.secret_info.ca_cert | default(omit) }}" - client_cert: "{{ jms_asset.secret_info.client_cert | default(omit) }}" - client_key: "{{ jms_asset.secret_info.client_key | default(omit) }}" + check_hostname: "{{ omit if not jms_use_ssl else jms_asset.spec_info.allow_invalid_cert }}" + ca_cert: "{{ omit if not jms_use_ssl else jms_asset.secret_info.ca_cert }}" + client_cert: "{{ omit if not jms_use_ssl else jms_asset.secret_info.client_cert }}" + client_key: "{{ omit if not jms_use_ssl else jms_asset.secret_info.client_key }}" filter: version register: db_info @@ -28,10 +29,10 @@ login_password: "{{ jms_account.secret }}" login_host: "{{ jms_asset.address }}" login_port: "{{ jms_asset.port }}" - check_hostname: "{{ omit if not jms_asset.spec_info.use_ssl else jms_asset.spec_info.allow_invalid_cert }}" - ca_cert: "{{ jms_asset.secret_info.ca_cert | default(omit) }}" - client_cert: "{{ jms_asset.secret_info.client_cert | default(omit) }}" - client_key: "{{ jms_asset.secret_info.client_key | default(omit) }}" + check_hostname: "{{ omit if not jms_use_ssl else jms_asset.spec_info.allow_invalid_cert }}" + ca_cert: "{{ omit if not jms_use_ssl else jms_asset.secret_info.ca_cert }}" + client_cert: "{{ omit if not jms_use_ssl else jms_asset.secret_info.client_cert }}" + client_key: "{{ omit if not jms_use_ssl else jms_asset.secret_info.client_key }}" name: "{{ account.username }}" password: "{{ account.secret }}" host: "%" @@ -45,8 +46,8 @@ login_password: "{{ account.secret }}" login_host: "{{ jms_asset.address }}" login_port: "{{ jms_asset.port }}" - check_hostname: "{{ omit if not jms_asset.spec_info.use_ssl else jms_asset.spec_info.allow_invalid_cert }}" - ca_cert: "{{ jms_asset.secret_info.ca_cert | default(omit) }}" - client_cert: "{{ jms_asset.secret_info.client_cert | default(omit) }}" - client_key: "{{ jms_asset.secret_info.client_key | default(omit) }}" + check_hostname: "{{ omit if not jms_use_ssl else jms_asset.spec_info.allow_invalid_cert }}" + ca_cert: "{{ omit if not jms_use_ssl else jms_asset.secret_info.ca_cert }}" + client_cert: "{{ omit if not jms_use_ssl else jms_asset.secret_info.client_cert }}" + client_key: "{{ omit if not jms_use_ssl else jms_asset.secret_info.client_key }}" filter: version diff --git a/apps/accounts/automations/gather_accounts/database/mysql/main.yml b/apps/accounts/automations/gather_accounts/database/mysql/main.yml index e8cf3cac4..4b77359fc 100644 --- a/apps/accounts/automations/gather_accounts/database/mysql/main.yml +++ b/apps/accounts/automations/gather_accounts/database/mysql/main.yml @@ -2,6 +2,7 @@ gather_facts: no vars: ansible_python_interpreter: /opt/py3/bin/python + jms_use_ssl: "{{ jms_asset.spec_info.use_ssl }}" tasks: - name: Get info @@ -10,10 +11,10 @@ login_password: "{{ jms_account.secret }}" login_host: "{{ jms_asset.address }}" login_port: "{{ jms_asset.port }}" - check_hostname: "{{ omit if not jms_asset.spec_info.use_ssl else jms_asset.spec_info.allow_invalid_cert }}" - ca_cert: "{{ jms_asset.secret_info.ca_cert | default(omit) }}" - client_cert: "{{ jms_asset.secret_info.client_cert | default(omit) }}" - client_key: "{{ jms_asset.secret_info.client_key | default(omit) }}" + check_hostname: "{{ omit if not jms_use_ssl else jms_asset.spec_info.allow_invalid_cert }}" + ca_cert: "{{ omit if not jms_use_ssl else jms_asset.secret_info.ca_cert }}" + client_cert: "{{ omit if not jms_use_ssl else jms_asset.secret_info.client_cert }}" + client_key: "{{ omit if not jms_use_ssl else jms_asset.secret_info.client_key }}" filter: users register: db_info diff --git a/apps/accounts/automations/push_account/database/mysql/main.yml b/apps/accounts/automations/push_account/database/mysql/main.yml index 91b7d6f2c..a423064d3 100644 --- a/apps/accounts/automations/push_account/database/mysql/main.yml +++ b/apps/accounts/automations/push_account/database/mysql/main.yml @@ -3,6 +3,7 @@ vars: ansible_python_interpreter: /opt/py3/bin/python db_name: "{{ jms_asset.spec_info.db_name }}" + jms_use_ssl: "{{ jms_asset.spec_info.use_ssl }}" tasks: - name: Test MySQL connection @@ -11,10 +12,10 @@ login_password: "{{ jms_account.secret }}" login_host: "{{ jms_asset.address }}" login_port: "{{ jms_asset.port }}" - check_hostname: "{{ omit if not jms_asset.spec_info.use_ssl else jms_asset.spec_info.allow_invalid_cert }}" - ca_cert: "{{ jms_asset.secret_info.ca_cert | default(omit) }}" - client_cert: "{{ jms_asset.secret_info.client_cert | default(omit) }}" - client_key: "{{ jms_asset.secret_info.client_key | default(omit) }}" + check_hostname: "{{ omit if not jms_use_ssl else jms_asset.spec_info.allow_invalid_cert }}" + ca_cert: "{{ omit if not jms_use_ssl else jms_asset.secret_info.ca_cert }}" + client_cert: "{{ omit if not jms_use_ssl else jms_asset.secret_info.client_cert }}" + client_key: "{{ omit if not jms_use_ssl else jms_asset.secret_info.client_key }}" filter: version register: db_info @@ -28,10 +29,10 @@ login_password: "{{ jms_account.secret }}" login_host: "{{ jms_asset.address }}" login_port: "{{ jms_asset.port }}" - check_hostname: "{{ omit if not jms_asset.spec_info.use_ssl else jms_asset.spec_info.allow_invalid_cert }}" - ca_cert: "{{ jms_asset.secret_info.ca_cert | default(omit) }}" - client_cert: "{{ jms_asset.secret_info.client_cert | default(omit) }}" - client_key: "{{ jms_asset.secret_info.client_key | default(omit) }}" + check_hostname: "{{ omit if not jms_use_ssl else jms_asset.spec_info.allow_invalid_cert }}" + ca_cert: "{{ omit if not jms_use_ssl else jms_asset.secret_info.ca_cert }}" + client_cert: "{{ omit if not jms_use_ssl else jms_asset.secret_info.client_cert }}" + client_key: "{{ omit if not jms_use_ssl else jms_asset.secret_info.client_key }}" name: "{{ account.username }}" password: "{{ account.secret }}" host: "%" @@ -45,8 +46,8 @@ login_password: "{{ account.secret }}" login_host: "{{ jms_asset.address }}" login_port: "{{ jms_asset.port }}" - check_hostname: "{{ omit if not jms_asset.spec_info.use_ssl else jms_asset.spec_info.allow_invalid_cert }}" - ca_cert: "{{ jms_asset.secret_info.ca_cert | default(omit) }}" - client_cert: "{{ jms_asset.secret_info.client_cert | default(omit) }}" - client_key: "{{ jms_asset.secret_info.client_key | default(omit) }}" + check_hostname: "{{ omit if not jms_use_ssl else jms_asset.spec_info.allow_invalid_cert }}" + ca_cert: "{{ omit if not jms_use_ssl else jms_asset.secret_info.ca_cert }}" + client_cert: "{{ omit if not jms_use_ssl else jms_asset.secret_info.client_cert }}" + client_key: "{{ omit if not jms_use_ssl else jms_asset.secret_info.client_key }}" filter: version diff --git a/apps/accounts/automations/verify_account/database/mysql/main.yml b/apps/accounts/automations/verify_account/database/mysql/main.yml index 2ae3a4abd..91de7fc9f 100644 --- a/apps/accounts/automations/verify_account/database/mysql/main.yml +++ b/apps/accounts/automations/verify_account/database/mysql/main.yml @@ -2,6 +2,7 @@ gather_facts: no vars: ansible_python_interpreter: /opt/py3/bin/python + jms_use_ssl: "{{ jms_asset.spec_info.use_ssl }}" tasks: - name: Verify account @@ -10,8 +11,8 @@ login_password: "{{ account.secret }}" login_host: "{{ jms_asset.address }}" login_port: "{{ jms_asset.port }}" - check_hostname: "{{ omit if not jms_asset.spec_info.use_ssl else jms_asset.spec_info.allow_invalid_cert }}" - ca_cert: "{{ jms_asset.secret_info.ca_cert | default(omit) }}" - client_cert: "{{ jms_asset.secret_info.client_cert | default(omit) }}" - client_key: "{{ jms_asset.secret_info.client_key | default(omit) }}" + check_hostname: "{{ omit if not jms_use_ssl else jms_asset.spec_info.allow_invalid_cert }}" + ca_cert: "{{ omit if not jms_use_ssl else jms_asset.secret_info.ca_cert }}" + client_cert: "{{ omit if not jms_use_ssl else jms_asset.secret_info.client_cert }}" + client_key: "{{ omit if not jms_use_ssl else jms_asset.secret_info.client_key }}" filter: version diff --git a/apps/assets/automations/gather_facts/database/mysql/main.yml b/apps/assets/automations/gather_facts/database/mysql/main.yml index b9d1fce8c..06c6c9bd4 100644 --- a/apps/assets/automations/gather_facts/database/mysql/main.yml +++ b/apps/assets/automations/gather_facts/database/mysql/main.yml @@ -2,6 +2,7 @@ gather_facts: no vars: ansible_python_interpreter: /opt/py3/bin/python + jms_use_ssl: "{{ jms_asset.spec_info.use_ssl }}" tasks: - name: Get info @@ -10,10 +11,10 @@ login_password: "{{ jms_account.secret }}" login_host: "{{ jms_asset.address }}" login_port: "{{ jms_asset.port }}" - check_hostname: "{{ omit if not jms_asset.spec_info.use_ssl else jms_asset.spec_info.allow_invalid_cert }}" - ca_cert: "{{ jms_asset.secret_info.ca_cert | default(omit) }}" - client_cert: "{{ jms_asset.secret_info.client_cert | default(omit) }}" - client_key: "{{ jms_asset.secret_info.client_key | default(omit) }}" + check_hostname: "{{ omit if not jms_use_ssl else jms_asset.spec_info.allow_invalid_cert }}" + ca_cert: "{{ omit if not jms_use_ssl else jms_asset.secret_info.ca_cert }}" + client_cert: "{{ omit if not jms_use_ssl else jms_asset.secret_info.client_cert }}" + client_key: "{{ omit if not jms_use_ssl else jms_asset.secret_info.client_key }}" filter: version register: db_info diff --git a/apps/assets/automations/ping/database/mysql/main.yml b/apps/assets/automations/ping/database/mysql/main.yml index 2180610d4..f24c95233 100644 --- a/apps/assets/automations/ping/database/mysql/main.yml +++ b/apps/assets/automations/ping/database/mysql/main.yml @@ -2,6 +2,7 @@ gather_facts: no vars: ansible_python_interpreter: /opt/py3/bin/python + jms_use_ssl: "{{ jms_asset.spec_info.use_ssl }}" tasks: - name: Test MySQL connection @@ -10,8 +11,8 @@ login_password: "{{ jms_account.secret }}" login_host: "{{ jms_asset.address }}" login_port: "{{ jms_asset.port }}" - check_hostname: "{{ omit if not jms_asset.spec_info.use_ssl else jms_asset.spec_info.allow_invalid_cert }}" - ca_cert: "{{ jms_asset.secret_info.ca_cert | default(omit) }}" - client_cert: "{{ jms_asset.secret_info.client_cert | default(omit) }}" - client_key: "{{ jms_asset.secret_info.client_key | default(omit) }}" + check_hostname: "{{ omit if not jms_use_ssl else jms_asset.spec_info.allow_invalid_cert }}" + ca_cert: "{{ omit if not jms_use_ssl else jms_asset.secret_info.ca_cert }}" + client_cert: "{{ omit if not jms_use_ssl else jms_asset.secret_info.client_cert }}" + client_key: "{{ omit if not jms_use_ssl else jms_asset.secret_info.client_key }}" filter: version From 6e7074ba4026840009f1b0fb74411174b7419c88 Mon Sep 17 00:00:00 2001 From: feng <1304903146@qq.com> Date: Mon, 20 Nov 2023 15:27:51 +0800 Subject: [PATCH 007/111] =?UTF-8?q?fix:=20mysql=20=E5=BC=80=E5=A7=8Bssl?= =?UTF-8?q?=E5=90=8E=20=E5=86=8D=E5=85=B3=E9=97=AD=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E5=A4=B1=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../change_secret/database/mysql/main.yml | 26 +++++++++---------- .../gather_accounts/database/mysql/main.yml | 10 +++---- .../push_account/database/mysql/main.yml | 26 +++++++++---------- .../verify_account/database/mysql/main.yml | 10 +++---- .../gather_facts/database/mysql/main.yml | 10 +++---- .../automations/ping/database/mysql/main.yml | 10 +++---- 6 files changed, 46 insertions(+), 46 deletions(-) diff --git a/apps/accounts/automations/change_secret/database/mysql/main.yml b/apps/accounts/automations/change_secret/database/mysql/main.yml index a423064d3..f36eff171 100644 --- a/apps/accounts/automations/change_secret/database/mysql/main.yml +++ b/apps/accounts/automations/change_secret/database/mysql/main.yml @@ -3,7 +3,7 @@ vars: ansible_python_interpreter: /opt/py3/bin/python db_name: "{{ jms_asset.spec_info.db_name }}" - jms_use_ssl: "{{ jms_asset.spec_info.use_ssl }}" + check_ssl: "{{ jms_asset.spec_info.use_ssl and not jms_asset.spec_info.allow_invalid_cert }}" tasks: - name: Test MySQL connection @@ -12,10 +12,10 @@ login_password: "{{ jms_account.secret }}" login_host: "{{ jms_asset.address }}" login_port: "{{ jms_asset.port }}" - check_hostname: "{{ omit if not jms_use_ssl else jms_asset.spec_info.allow_invalid_cert }}" - ca_cert: "{{ omit if not jms_use_ssl else jms_asset.secret_info.ca_cert }}" - client_cert: "{{ omit if not jms_use_ssl else jms_asset.secret_info.client_cert }}" - client_key: "{{ omit if not jms_use_ssl else jms_asset.secret_info.client_key }}" + check_hostname: "{{ check_ssl if check_ssl else omit }}" + ca_cert: "{{ jms_asset.secret_info.ca_cert | default(omit) if check_ssl else omit }}" + client_cert: "{{ jms_asset.secret_info.client_cert | default(omit) if check_ssl else omit }}" + client_key: "{{ jms_asset.secret_info.client_key | default(omit) if check_ssl else omit }}" filter: version register: db_info @@ -29,10 +29,10 @@ login_password: "{{ jms_account.secret }}" login_host: "{{ jms_asset.address }}" login_port: "{{ jms_asset.port }}" - check_hostname: "{{ omit if not jms_use_ssl else jms_asset.spec_info.allow_invalid_cert }}" - ca_cert: "{{ omit if not jms_use_ssl else jms_asset.secret_info.ca_cert }}" - client_cert: "{{ omit if not jms_use_ssl else jms_asset.secret_info.client_cert }}" - client_key: "{{ omit if not jms_use_ssl else jms_asset.secret_info.client_key }}" + check_hostname: "{{ check_ssl if check_ssl else omit }}" + ca_cert: "{{ jms_asset.secret_info.ca_cert | default(omit) if check_ssl else omit }}" + client_cert: "{{ jms_asset.secret_info.client_cert | default(omit) if check_ssl else omit }}" + client_key: "{{ jms_asset.secret_info.client_key | default(omit) if check_ssl else omit }}" name: "{{ account.username }}" password: "{{ account.secret }}" host: "%" @@ -46,8 +46,8 @@ login_password: "{{ account.secret }}" login_host: "{{ jms_asset.address }}" login_port: "{{ jms_asset.port }}" - check_hostname: "{{ omit if not jms_use_ssl else jms_asset.spec_info.allow_invalid_cert }}" - ca_cert: "{{ omit if not jms_use_ssl else jms_asset.secret_info.ca_cert }}" - client_cert: "{{ omit if not jms_use_ssl else jms_asset.secret_info.client_cert }}" - client_key: "{{ omit if not jms_use_ssl else jms_asset.secret_info.client_key }}" + check_hostname: "{{ check_ssl if check_ssl else omit }}" + ca_cert: "{{ jms_asset.secret_info.ca_cert | default(omit) if check_ssl else omit }}" + client_cert: "{{ jms_asset.secret_info.client_cert | default(omit) if check_ssl else omit }}" + client_key: "{{ jms_asset.secret_info.client_key | default(omit) if check_ssl else omit }}" filter: version diff --git a/apps/accounts/automations/gather_accounts/database/mysql/main.yml b/apps/accounts/automations/gather_accounts/database/mysql/main.yml index 4b77359fc..e36925209 100644 --- a/apps/accounts/automations/gather_accounts/database/mysql/main.yml +++ b/apps/accounts/automations/gather_accounts/database/mysql/main.yml @@ -2,7 +2,7 @@ gather_facts: no vars: ansible_python_interpreter: /opt/py3/bin/python - jms_use_ssl: "{{ jms_asset.spec_info.use_ssl }}" + check_ssl: "{{ jms_asset.spec_info.use_ssl and not jms_asset.spec_info.allow_invalid_cert }}" tasks: - name: Get info @@ -11,10 +11,10 @@ login_password: "{{ jms_account.secret }}" login_host: "{{ jms_asset.address }}" login_port: "{{ jms_asset.port }}" - check_hostname: "{{ omit if not jms_use_ssl else jms_asset.spec_info.allow_invalid_cert }}" - ca_cert: "{{ omit if not jms_use_ssl else jms_asset.secret_info.ca_cert }}" - client_cert: "{{ omit if not jms_use_ssl else jms_asset.secret_info.client_cert }}" - client_key: "{{ omit if not jms_use_ssl else jms_asset.secret_info.client_key }}" + check_hostname: "{{ check_ssl if check_ssl else omit }}" + ca_cert: "{{ jms_asset.secret_info.ca_cert | default(omit) if check_ssl else omit }}" + client_cert: "{{ jms_asset.secret_info.client_cert | default(omit) if check_ssl else omit }}" + client_key: "{{ jms_asset.secret_info.client_key | default(omit) if check_ssl else omit }}" filter: users register: db_info diff --git a/apps/accounts/automations/push_account/database/mysql/main.yml b/apps/accounts/automations/push_account/database/mysql/main.yml index a423064d3..f36eff171 100644 --- a/apps/accounts/automations/push_account/database/mysql/main.yml +++ b/apps/accounts/automations/push_account/database/mysql/main.yml @@ -3,7 +3,7 @@ vars: ansible_python_interpreter: /opt/py3/bin/python db_name: "{{ jms_asset.spec_info.db_name }}" - jms_use_ssl: "{{ jms_asset.spec_info.use_ssl }}" + check_ssl: "{{ jms_asset.spec_info.use_ssl and not jms_asset.spec_info.allow_invalid_cert }}" tasks: - name: Test MySQL connection @@ -12,10 +12,10 @@ login_password: "{{ jms_account.secret }}" login_host: "{{ jms_asset.address }}" login_port: "{{ jms_asset.port }}" - check_hostname: "{{ omit if not jms_use_ssl else jms_asset.spec_info.allow_invalid_cert }}" - ca_cert: "{{ omit if not jms_use_ssl else jms_asset.secret_info.ca_cert }}" - client_cert: "{{ omit if not jms_use_ssl else jms_asset.secret_info.client_cert }}" - client_key: "{{ omit if not jms_use_ssl else jms_asset.secret_info.client_key }}" + check_hostname: "{{ check_ssl if check_ssl else omit }}" + ca_cert: "{{ jms_asset.secret_info.ca_cert | default(omit) if check_ssl else omit }}" + client_cert: "{{ jms_asset.secret_info.client_cert | default(omit) if check_ssl else omit }}" + client_key: "{{ jms_asset.secret_info.client_key | default(omit) if check_ssl else omit }}" filter: version register: db_info @@ -29,10 +29,10 @@ login_password: "{{ jms_account.secret }}" login_host: "{{ jms_asset.address }}" login_port: "{{ jms_asset.port }}" - check_hostname: "{{ omit if not jms_use_ssl else jms_asset.spec_info.allow_invalid_cert }}" - ca_cert: "{{ omit if not jms_use_ssl else jms_asset.secret_info.ca_cert }}" - client_cert: "{{ omit if not jms_use_ssl else jms_asset.secret_info.client_cert }}" - client_key: "{{ omit if not jms_use_ssl else jms_asset.secret_info.client_key }}" + check_hostname: "{{ check_ssl if check_ssl else omit }}" + ca_cert: "{{ jms_asset.secret_info.ca_cert | default(omit) if check_ssl else omit }}" + client_cert: "{{ jms_asset.secret_info.client_cert | default(omit) if check_ssl else omit }}" + client_key: "{{ jms_asset.secret_info.client_key | default(omit) if check_ssl else omit }}" name: "{{ account.username }}" password: "{{ account.secret }}" host: "%" @@ -46,8 +46,8 @@ login_password: "{{ account.secret }}" login_host: "{{ jms_asset.address }}" login_port: "{{ jms_asset.port }}" - check_hostname: "{{ omit if not jms_use_ssl else jms_asset.spec_info.allow_invalid_cert }}" - ca_cert: "{{ omit if not jms_use_ssl else jms_asset.secret_info.ca_cert }}" - client_cert: "{{ omit if not jms_use_ssl else jms_asset.secret_info.client_cert }}" - client_key: "{{ omit if not jms_use_ssl else jms_asset.secret_info.client_key }}" + check_hostname: "{{ check_ssl if check_ssl else omit }}" + ca_cert: "{{ jms_asset.secret_info.ca_cert | default(omit) if check_ssl else omit }}" + client_cert: "{{ jms_asset.secret_info.client_cert | default(omit) if check_ssl else omit }}" + client_key: "{{ jms_asset.secret_info.client_key | default(omit) if check_ssl else omit }}" filter: version diff --git a/apps/accounts/automations/verify_account/database/mysql/main.yml b/apps/accounts/automations/verify_account/database/mysql/main.yml index 91de7fc9f..e2768d2c2 100644 --- a/apps/accounts/automations/verify_account/database/mysql/main.yml +++ b/apps/accounts/automations/verify_account/database/mysql/main.yml @@ -2,7 +2,7 @@ gather_facts: no vars: ansible_python_interpreter: /opt/py3/bin/python - jms_use_ssl: "{{ jms_asset.spec_info.use_ssl }}" + check_ssl: "{{ jms_asset.spec_info.use_ssl and not jms_asset.spec_info.allow_invalid_cert }}" tasks: - name: Verify account @@ -11,8 +11,8 @@ login_password: "{{ account.secret }}" login_host: "{{ jms_asset.address }}" login_port: "{{ jms_asset.port }}" - check_hostname: "{{ omit if not jms_use_ssl else jms_asset.spec_info.allow_invalid_cert }}" - ca_cert: "{{ omit if not jms_use_ssl else jms_asset.secret_info.ca_cert }}" - client_cert: "{{ omit if not jms_use_ssl else jms_asset.secret_info.client_cert }}" - client_key: "{{ omit if not jms_use_ssl else jms_asset.secret_info.client_key }}" + check_hostname: "{{ check_ssl if check_ssl else omit }}" + ca_cert: "{{ jms_asset.secret_info.ca_cert | default(omit) if check_ssl else omit }}" + client_cert: "{{ jms_asset.secret_info.client_cert | default(omit) if check_ssl else omit }}" + client_key: "{{ jms_asset.secret_info.client_key | default(omit) if check_ssl else omit }}" filter: version diff --git a/apps/assets/automations/gather_facts/database/mysql/main.yml b/apps/assets/automations/gather_facts/database/mysql/main.yml index 06c6c9bd4..348a2150d 100644 --- a/apps/assets/automations/gather_facts/database/mysql/main.yml +++ b/apps/assets/automations/gather_facts/database/mysql/main.yml @@ -2,7 +2,7 @@ gather_facts: no vars: ansible_python_interpreter: /opt/py3/bin/python - jms_use_ssl: "{{ jms_asset.spec_info.use_ssl }}" + check_ssl: "{{ jms_asset.spec_info.use_ssl and not jms_asset.spec_info.allow_invalid_cert }}" tasks: - name: Get info @@ -11,10 +11,10 @@ login_password: "{{ jms_account.secret }}" login_host: "{{ jms_asset.address }}" login_port: "{{ jms_asset.port }}" - check_hostname: "{{ omit if not jms_use_ssl else jms_asset.spec_info.allow_invalid_cert }}" - ca_cert: "{{ omit if not jms_use_ssl else jms_asset.secret_info.ca_cert }}" - client_cert: "{{ omit if not jms_use_ssl else jms_asset.secret_info.client_cert }}" - client_key: "{{ omit if not jms_use_ssl else jms_asset.secret_info.client_key }}" + check_hostname: "{{ check_ssl if check_ssl else omit }}" + ca_cert: "{{ jms_asset.secret_info.ca_cert | default(omit) if check_ssl else omit }}" + client_cert: "{{ jms_asset.secret_info.client_cert | default(omit) if check_ssl else omit }}" + client_key: "{{ jms_asset.secret_info.client_key | default(omit) if check_ssl else omit }}" filter: version register: db_info diff --git a/apps/assets/automations/ping/database/mysql/main.yml b/apps/assets/automations/ping/database/mysql/main.yml index f24c95233..f99333bdb 100644 --- a/apps/assets/automations/ping/database/mysql/main.yml +++ b/apps/assets/automations/ping/database/mysql/main.yml @@ -2,7 +2,7 @@ gather_facts: no vars: ansible_python_interpreter: /opt/py3/bin/python - jms_use_ssl: "{{ jms_asset.spec_info.use_ssl }}" + check_ssl: "{{ jms_asset.spec_info.use_ssl and not jms_asset.spec_info.allow_invalid_cert }}" tasks: - name: Test MySQL connection @@ -11,8 +11,8 @@ login_password: "{{ jms_account.secret }}" login_host: "{{ jms_asset.address }}" login_port: "{{ jms_asset.port }}" - check_hostname: "{{ omit if not jms_use_ssl else jms_asset.spec_info.allow_invalid_cert }}" - ca_cert: "{{ omit if not jms_use_ssl else jms_asset.secret_info.ca_cert }}" - client_cert: "{{ omit if not jms_use_ssl else jms_asset.secret_info.client_cert }}" - client_key: "{{ omit if not jms_use_ssl else jms_asset.secret_info.client_key }}" + check_hostname: "{{ check_ssl if check_ssl else omit }}" + ca_cert: "{{ jms_asset.secret_info.ca_cert | default(omit) if check_ssl else omit }}" + client_cert: "{{ jms_asset.secret_info.client_cert | default(omit) if check_ssl else omit }}" + client_key: "{{ jms_asset.secret_info.client_key | default(omit) if check_ssl else omit }}" filter: version From a6de9bdde6faa3fe4c56e4e2e974c70a383de4bc Mon Sep 17 00:00:00 2001 From: feng <1304903146@qq.com> Date: Wed, 22 Nov 2023 11:09:16 +0800 Subject: [PATCH 008/111] =?UTF-8?q?perf:=20=E4=BF=AE=E6=94=B9=E7=BF=BB?= =?UTF-8?q?=E8=AF=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/common/sdk/sms/endpoint.py | 10 +++++----- apps/locale/ja/LC_MESSAGES/django.po | 22 ++++++++++++++-------- apps/locale/zh/LC_MESSAGES/django.po | 21 +++++++++++++-------- 3 files changed, 32 insertions(+), 21 deletions(-) diff --git a/apps/common/sdk/sms/endpoint.py b/apps/common/sdk/sms/endpoint.py index 6f5433fa5..7618d6da9 100644 --- a/apps/common/sdk/sms/endpoint.py +++ b/apps/common/sdk/sms/endpoint.py @@ -1,12 +1,12 @@ -from collections import OrderedDict import importlib +from collections import OrderedDict -from django.utils.translation import gettext_lazy as _ -from django.db.models import TextChoices from django.conf import settings +from django.db.models import TextChoices +from django.utils.translation import gettext_lazy as _ -from common.utils import get_logger from common.exceptions import JMSException +from common.utils import get_logger from .base import BaseSMSClient logger = get_logger(__name__) @@ -18,7 +18,7 @@ class BACKENDS(TextChoices): HUAWEI = 'huawei', _('Huawei Cloud') CMPP2 = 'cmpp2', _('CMPP v2.0') CUSTOM = 'custom', _('Custom type') - CUSTOM_FILE = 'custom_file', f"{_('Custom type')}({_('File')})" + CUSTOM_FILE = 'custom_file', _('Custom type (File)') class SMS: diff --git a/apps/locale/ja/LC_MESSAGES/django.po b/apps/locale/ja/LC_MESSAGES/django.po index 42f5fb6fe..c235e37a8 100644 --- a/apps/locale/ja/LC_MESSAGES/django.po +++ b/apps/locale/ja/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-11-16 15:05+0800\n" +"POT-Creation-Date: 2023-11-22 11:05+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -2676,21 +2676,21 @@ msgstr "認証" msgid "User invalid, disabled or expired" msgstr "ユーザーが無効、無効、または期限切れです" -#: authentication/backends/drf.py:39 +#: authentication/backends/drf.py:50 msgid "Invalid token header. No credentials provided." msgstr "無効なトークンヘッダー。資格情報は提供されていません。" -#: authentication/backends/drf.py:42 +#: authentication/backends/drf.py:53 msgid "Invalid token header. Sign string should not contain spaces." msgstr "無効なトークンヘッダー。記号文字列にはスペースを含めないでください。" -#: authentication/backends/drf.py:48 +#: authentication/backends/drf.py:59 msgid "" "Invalid token header. Sign string should not contain invalid characters." msgstr "" "無効なトークンヘッダー。署名文字列に無効な文字を含めることはできません。" -#: authentication/backends/drf.py:61 +#: authentication/backends/drf.py:72 msgid "Invalid token or cache refreshed." msgstr "無効なトークンまたはキャッシュの更新。" @@ -3780,6 +3780,9 @@ msgstr "華為雲" msgid "CMPP v2.0" msgstr "CMPP v2.0" +msgid "Custom type (File)" +msgstr "カスタム(ファイル)" + #: common/sdk/sms/endpoint.py:32 msgid "SMS provider not support: {}" msgstr "SMSプロバイダーはサポートしていません: {}" @@ -4200,7 +4203,7 @@ msgstr "Material" msgid "Material Type" msgstr "Material を選択してオプションを設定します。" -#: ops/models/job.py:536 +#: ops/models/job.py:544 msgid "Job Execution" msgstr "ジョブ実行" @@ -7674,7 +7677,10 @@ msgid "" "Determines whether the client computer should scale the content on the " "remote computer to fit the window size of the client computer when the " "window is resized." -msgstr "ウィンドウサイズを変更するときにクライアントコンピュータがクライアントコンピュータのウィンドウサイズに合わせるためにリモートコンピュータ上のコンテンツをスケーリングすべきかどうかを判断する" +msgstr "" +"ウィンドウサイズを変更するときにクライアントコンピュータがクライアントコン" +"ピュータのウィンドウサイズに合わせるためにリモートコンピュータ上のコンテンツ" +"をスケーリングすべきかどうかを判断する" #: users/serializers/preference/luna.py:51 msgid "Remote application connection method" @@ -7797,7 +7803,7 @@ msgstr "ユーザーの有効期限の定期的な検出" msgid "Check unused users" msgstr "未使用のユーザーを確認する" -#: users/tasks.py:114 +#: users/tasks.py:115 msgid "The user has not logged in recently and has been disabled." msgstr "ユーザーは最近ログインしておらず、無効になっています。" diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index dff3dd204..831ad1db2 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: JumpServer 0.3.3\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-11-16 15:05+0800\n" +"POT-Creation-Date: 2023-11-22 11:05+0800\n" "PO-Revision-Date: 2021-05-20 10:54+0800\n" "Last-Translator: ibuler \n" "Language-Team: JumpServer team\n" @@ -2656,20 +2656,20 @@ msgstr "认证" msgid "User invalid, disabled or expired" msgstr "用户无效,已禁用或已过期" -#: authentication/backends/drf.py:39 +#: authentication/backends/drf.py:50 msgid "Invalid token header. No credentials provided." msgstr "无效的令牌头。没有提供任何凭据。" -#: authentication/backends/drf.py:42 +#: authentication/backends/drf.py:53 msgid "Invalid token header. Sign string should not contain spaces." msgstr "无效的令牌头。符号字符串不应包含空格。" -#: authentication/backends/drf.py:48 +#: authentication/backends/drf.py:59 msgid "" "Invalid token header. Sign string should not contain invalid characters." msgstr "无效的令牌头。符号字符串不应包含无效字符。" -#: authentication/backends/drf.py:61 +#: authentication/backends/drf.py:72 msgid "Invalid token or cache refreshed." msgstr "刷新的令牌或缓存无效。" @@ -3735,6 +3735,9 @@ msgstr "华为云" msgid "CMPP v2.0" msgstr "CMPP v2.0" +msgid "Custom type (File)" +msgstr "自定义 (文件)" + #: common/sdk/sms/endpoint.py:32 msgid "SMS provider not support: {}" msgstr "短信服务商不支持:{}" @@ -4150,7 +4153,7 @@ msgstr "Material" msgid "Material Type" msgstr "Material 类型" -#: ops/models/job.py:536 +#: ops/models/job.py:544 msgid "Job Execution" msgstr "作业执行" @@ -7571,7 +7574,9 @@ msgid "" "Determines whether the client computer should scale the content on the " "remote computer to fit the window size of the client computer when the " "window is resized." -msgstr "确定调整窗口大小时客户端计算机是否应缩放远程计算机上的内容以适应客户端计算机的窗口大小" +msgstr "" +"确定调整窗口大小时客户端计算机是否应缩放远程计算机上的内容以适应客户端计算机" +"的窗口大小" #: users/serializers/preference/luna.py:51 msgid "Remote application connection method" @@ -7693,7 +7698,7 @@ msgstr "周期检测用户过期" msgid "Check unused users" msgstr "校验用户已过期" -#: users/tasks.py:114 +#: users/tasks.py:115 msgid "The user has not logged in recently and has been disabled." msgstr "该用户最近未登录,已被禁用。" From 6b7119ea7420ba94fcb893887395cc41350a9248 Mon Sep 17 00:00:00 2001 From: wangruidong <940853815@qq.com> Date: Wed, 22 Nov 2023 16:46:05 +0800 Subject: [PATCH 009/111] =?UTF-8?q?perf:=20ldap=E6=8E=A5=E5=8F=A3=E8=AF=B7?= =?UTF-8?q?=E6=B1=82=E6=8D=A2=E6=88=90websocket=E8=BF=9E=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/locale/ja/LC_MESSAGES/django.mo | 3 - apps/locale/ja/LC_MESSAGES/django.po | 87 ++++++++++-------- apps/locale/zh/LC_MESSAGES/django.mo | 3 - apps/locale/zh/LC_MESSAGES/django.po | 89 ++++++++++-------- apps/settings/api/ldap.py | 105 +-------------------- apps/settings/urls/api_urls.py | 3 - apps/settings/urls/ws_urls.py | 1 + apps/settings/utils/ldap.py | 22 ++--- apps/settings/ws.py | 133 ++++++++++++++++++++++++++- 9 files changed, 240 insertions(+), 206 deletions(-) delete mode 100644 apps/locale/ja/LC_MESSAGES/django.mo delete mode 100644 apps/locale/zh/LC_MESSAGES/django.mo diff --git a/apps/locale/ja/LC_MESSAGES/django.mo b/apps/locale/ja/LC_MESSAGES/django.mo deleted file mode 100644 index 80df7b71d..000000000 --- a/apps/locale/ja/LC_MESSAGES/django.mo +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c1af402f12b379a4943c141a1d75d1a0b38cfa873f29e70c9030dd25384944f6 -size 166114 diff --git a/apps/locale/ja/LC_MESSAGES/django.po b/apps/locale/ja/LC_MESSAGES/django.po index c235e37a8..2bb689180 100644 --- a/apps/locale/ja/LC_MESSAGES/django.po +++ b/apps/locale/ja/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-11-22 11:05+0800\n" +"POT-Creation-Date: 2023-11-22 16:43+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -2990,7 +2990,7 @@ msgstr "電話番号を設定して有効にする" msgid "Clear phone number to disable" msgstr "無効にする電話番号をクリアする" -#: authentication/middleware.py:94 settings/utils/ldap.py:679 +#: authentication/middleware.py:94 settings/utils/ldap.py:677 msgid "Authentication failed (before login check failed): {}" msgstr "認証に失敗しました (ログインチェックが失敗する前): {}" @@ -3780,6 +3780,7 @@ msgstr "華為雲" msgid "CMPP v2.0" msgstr "CMPP v2.0" +#: common/sdk/sms/endpoint.py:21 msgid "Custom type (File)" msgstr "カスタム(ファイル)" @@ -4724,23 +4725,16 @@ msgstr "テストの成功" msgid "Test mail sent to {}, please check" msgstr "{}に送信されたテストメールを確認してください" -#: settings/api/ldap.py:176 -msgid "Synchronization start, please wait." -msgstr "同期開始、お待ちください。" +#: settings/api/ldap.py:101 +msgid "" +"Users are not synchronized, please click the user synchronization button" +msgstr "ユーザーは同期されていません。「ユーザーを同期」ボタンをクリックしてください。" -#: settings/api/ldap.py:180 -msgid "Synchronization is running, please wait." -msgstr "同期が実行中です。しばらくお待ちください。" - -#: settings/api/ldap.py:185 -msgid "Synchronization error: {}" -msgstr "同期エラー: {}" - -#: settings/api/ldap.py:223 +#: settings/api/ldap.py:137 msgid "Get ldap users is None" msgstr "Ldapユーザーを取得するにはNone" -#: settings/api/ldap.py:233 +#: settings/api/ldap.py:147 msgid "Imported {} users successfully (Organization: {})" msgstr "{} 人のユーザーを正常にインポートしました (組織: {})" @@ -5889,100 +5883,104 @@ msgstr "LDAP ユーザーを定期的にインポートする" msgid "Registration periodic import ldap user task" msgstr "登録サイクルLDAPユーザータスクのインポート" -#: settings/utils/ldap.py:494 +#: settings/utils/ldap.py:492 msgid "ldap:// or ldaps:// protocol is used." msgstr "ldap:// または ldaps:// プロトコルが使用されます。" -#: settings/utils/ldap.py:505 +#: settings/utils/ldap.py:503 msgid "Host or port is disconnected: {}" msgstr "ホストまたはポートが切断されました: {}" -#: settings/utils/ldap.py:507 +#: settings/utils/ldap.py:505 msgid "The port is not the port of the LDAP service: {}" msgstr "ポートはLDAPサービスのポートではありません: {}" -#: settings/utils/ldap.py:509 +#: settings/utils/ldap.py:507 msgid "Please add certificate: {}" msgstr "証明書を追加してください: {}" -#: settings/utils/ldap.py:513 settings/utils/ldap.py:540 -#: settings/utils/ldap.py:570 settings/utils/ldap.py:598 +#: settings/utils/ldap.py:511 settings/utils/ldap.py:538 +#: settings/utils/ldap.py:568 settings/utils/ldap.py:596 msgid "Unknown error: {}" msgstr "不明なエラー: {}" -#: settings/utils/ldap.py:527 +#: settings/utils/ldap.py:525 msgid "Bind DN or Password incorrect" msgstr "DNまたはパスワードのバインドが正しくありません" -#: settings/utils/ldap.py:534 +#: settings/utils/ldap.py:532 msgid "Please enter Bind DN: {}" msgstr "バインドDN: {} を入力してください" -#: settings/utils/ldap.py:536 +#: settings/utils/ldap.py:534 msgid "Please enter Password: {}" msgstr "パスワードを入力してください: {}" -#: settings/utils/ldap.py:538 +#: settings/utils/ldap.py:536 msgid "Please enter correct Bind DN and Password: {}" msgstr "正しいバインドDNとパスワードを入力してください: {}" -#: settings/utils/ldap.py:556 +#: settings/utils/ldap.py:554 msgid "Invalid User OU or User search filter: {}" msgstr "無効なユーザー OU またはユーザー検索フィルター: {}" -#: settings/utils/ldap.py:587 +#: settings/utils/ldap.py:585 msgid "LDAP User attr map not include: {}" msgstr "LDAP ユーザーattrマップは含まれません: {}" -#: settings/utils/ldap.py:594 +#: settings/utils/ldap.py:592 msgid "LDAP User attr map is not dict" msgstr "LDAPユーザーattrマップはdictではありません" -#: settings/utils/ldap.py:613 +#: settings/utils/ldap.py:611 msgid "LDAP authentication is not enabled" msgstr "LDAP 認証が有効になっていない" -#: settings/utils/ldap.py:631 +#: settings/utils/ldap.py:629 msgid "Error (Invalid LDAP server): {}" msgstr "エラー (LDAPサーバーが無効): {}" -#: settings/utils/ldap.py:633 +#: settings/utils/ldap.py:631 msgid "Error (Invalid Bind DN): {}" msgstr "エラー (DNのバインドが無効): {}" -#: settings/utils/ldap.py:635 +#: settings/utils/ldap.py:633 msgid "Error (Invalid LDAP User attr map): {}" msgstr "エラー (LDAPユーザーattrマップが無効): {}" -#: settings/utils/ldap.py:637 +#: settings/utils/ldap.py:635 msgid "Error (Invalid User OU or User search filter): {}" msgstr "エラー (ユーザーOUまたはユーザー検索フィルターが無効): {}" -#: settings/utils/ldap.py:639 +#: settings/utils/ldap.py:637 msgid "Error (Not enabled LDAP authentication): {}" msgstr "エラー (LDAP認証が有効化されていません): {}" -#: settings/utils/ldap.py:641 +#: settings/utils/ldap.py:639 msgid "Error (Unknown): {}" msgstr "エラー (不明): {}" -#: settings/utils/ldap.py:644 +#: settings/utils/ldap.py:642 msgid "Succeed: Match {} s user" msgstr "成功: {} 人のユーザーに一致" -#: settings/utils/ldap.py:677 +#: settings/utils/ldap.py:653 +msgid "Please test the connection first" +msgstr "まず接続をテストしてください" + +#: settings/utils/ldap.py:675 msgid "Authentication failed (configuration incorrect): {}" msgstr "認証に失敗しました (設定が正しくありません): {}" -#: settings/utils/ldap.py:681 +#: settings/utils/ldap.py:679 msgid "Authentication failed (username or password incorrect): {}" msgstr "認証に失敗しました (ユーザー名またはパスワードが正しくありません): {}" -#: settings/utils/ldap.py:683 +#: settings/utils/ldap.py:681 msgid "Authentication failed (Unknown): {}" msgstr "認証に失敗しました (不明): {}" -#: settings/utils/ldap.py:686 +#: settings/utils/ldap.py:684 msgid "Authentication success: {}" msgstr "認証成功: {}" @@ -8719,6 +8717,15 @@ msgstr "エンタープライズプロフェッショナル版" msgid "Ultimate edition" msgstr "エンタープライズ・フラッグシップ・エディション" +#~ msgid "Synchronization start, please wait." +#~ msgstr "同期開始、お待ちください。" + +#~ msgid "Synchronization is running, please wait." +#~ msgstr "同期が実行中です。しばらくお待ちください。" + +#~ msgid "Synchronization error: {}" +#~ msgstr "同期エラー: {}" + #~ msgid "Copy" #~ msgstr "コピー" diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo deleted file mode 100644 index ef1eef0a1..000000000 --- a/apps/locale/zh/LC_MESSAGES/django.mo +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8324031d40c7cbcd9e332221ee8f607396836b87907847b7f94e879269cc97c8 -size 135739 diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index 831ad1db2..da13efde4 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: JumpServer 0.3.3\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-11-22 11:05+0800\n" +"POT-Creation-Date: 2023-11-22 16:42+0800\n" "PO-Revision-Date: 2021-05-20 10:54+0800\n" "Last-Translator: ibuler \n" "Language-Team: JumpServer team\n" @@ -2961,7 +2961,7 @@ msgstr "设置手机号码启用" msgid "Clear phone number to disable" msgstr "清空手机号码禁用" -#: authentication/middleware.py:94 settings/utils/ldap.py:679 +#: authentication/middleware.py:94 settings/utils/ldap.py:677 msgid "Authentication failed (before login check failed): {}" msgstr "认证失败 (登录前检查失败): {}" @@ -3735,6 +3735,7 @@ msgstr "华为云" msgid "CMPP v2.0" msgstr "CMPP v2.0" +#: common/sdk/sms/endpoint.py:21 msgid "Custom type (File)" msgstr "自定义 (文件)" @@ -4672,23 +4673,16 @@ msgstr "测试成功" msgid "Test mail sent to {}, please check" msgstr "邮件已经发送{}, 请检查" -#: settings/api/ldap.py:176 -msgid "Synchronization start, please wait." -msgstr "同步开始,请稍等" +#: settings/api/ldap.py:101 +msgid "" +"Users are not synchronized, please click the user synchronization button" +msgstr "用户未同步,请点击同步用户按钮" -#: settings/api/ldap.py:180 -msgid "Synchronization is running, please wait." -msgstr "同步正在运行,请稍等" - -#: settings/api/ldap.py:185 -msgid "Synchronization error: {}" -msgstr "同步错误: {}" - -#: settings/api/ldap.py:223 +#: settings/api/ldap.py:137 msgid "Get ldap users is None" msgstr "获取 LDAP 用户为 None" -#: settings/api/ldap.py:233 +#: settings/api/ldap.py:147 msgid "Imported {} users successfully (Organization: {})" msgstr "成功导入 {} 个用户 ( 组织: {} )" @@ -5809,100 +5803,106 @@ msgstr "周期导入 LDAP 用户" msgid "Registration periodic import ldap user task" msgstr "注册周期导入 LDAP 用户 任务" -#: settings/utils/ldap.py:494 +#: settings/utils/ldap.py:492 msgid "ldap:// or ldaps:// protocol is used." msgstr "使用 ldap:// 或 ldaps:// 协议" -#: settings/utils/ldap.py:505 +#: settings/utils/ldap.py:503 msgid "Host or port is disconnected: {}" msgstr "主机或端口不可连接: {}" -#: settings/utils/ldap.py:507 +#: settings/utils/ldap.py:505 msgid "The port is not the port of the LDAP service: {}" msgstr "端口不是LDAP服务端口: {}" -#: settings/utils/ldap.py:509 +#: settings/utils/ldap.py:507 msgid "Please add certificate: {}" msgstr "请添加证书" -#: settings/utils/ldap.py:513 settings/utils/ldap.py:540 -#: settings/utils/ldap.py:570 settings/utils/ldap.py:598 +#: settings/utils/ldap.py:511 settings/utils/ldap.py:538 +#: settings/utils/ldap.py:568 settings/utils/ldap.py:596 msgid "Unknown error: {}" msgstr "未知错误: {}" -#: settings/utils/ldap.py:527 +#: settings/utils/ldap.py:525 msgid "Bind DN or Password incorrect" msgstr "绑定DN或密码错误" -#: settings/utils/ldap.py:534 +#: settings/utils/ldap.py:532 msgid "Please enter Bind DN: {}" msgstr "请输入绑定DN: {}" -#: settings/utils/ldap.py:536 +#: settings/utils/ldap.py:534 msgid "Please enter Password: {}" msgstr "请输入密码: {}" -#: settings/utils/ldap.py:538 +#: settings/utils/ldap.py:536 msgid "Please enter correct Bind DN and Password: {}" msgstr "请输入正确的绑定DN和密码: {}" -#: settings/utils/ldap.py:556 +#: settings/utils/ldap.py:554 msgid "Invalid User OU or User search filter: {}" msgstr "不合法的用户OU或用户过滤器: {}" -#: settings/utils/ldap.py:587 +#: settings/utils/ldap.py:585 msgid "LDAP User attr map not include: {}" msgstr "LDAP属性映射没有包含: {}" -#: settings/utils/ldap.py:594 +#: settings/utils/ldap.py:592 msgid "LDAP User attr map is not dict" msgstr "LDAP属性映射不合法" -#: settings/utils/ldap.py:613 +#: settings/utils/ldap.py:611 msgid "LDAP authentication is not enabled" msgstr "LDAP认证没有启用" -#: settings/utils/ldap.py:631 +#: settings/utils/ldap.py:629 msgid "Error (Invalid LDAP server): {}" msgstr "错误 (不合法的LDAP服务器地址): {}" -#: settings/utils/ldap.py:633 +#: settings/utils/ldap.py:631 msgid "Error (Invalid Bind DN): {}" msgstr "错误 (不合法的绑定DN): {}" -#: settings/utils/ldap.py:635 +#: settings/utils/ldap.py:633 msgid "Error (Invalid LDAP User attr map): {}" msgstr "错误 (不合法的LDAP属性映射): {}" -#: settings/utils/ldap.py:637 +#: settings/utils/ldap.py:635 msgid "Error (Invalid User OU or User search filter): {}" msgstr "错误 (不合法的用户OU或用户过滤器): {}" -#: settings/utils/ldap.py:639 +#: settings/utils/ldap.py:637 msgid "Error (Not enabled LDAP authentication): {}" msgstr "错误 (没有启用LDAP认证): {}" -#: settings/utils/ldap.py:641 +#: settings/utils/ldap.py:639 msgid "Error (Unknown): {}" msgstr "错误 (未知): {}" -#: settings/utils/ldap.py:644 +#: settings/utils/ldap.py:642 msgid "Succeed: Match {} s user" msgstr "成功匹配 {} 个用户" -#: settings/utils/ldap.py:677 +#: settings/utils/ldap.py:653 +#, fuzzy +#| msgid "Can test asset connectivity" +msgid "Please test the connection first" +msgstr "请先测试连接" + +#: settings/utils/ldap.py:675 msgid "Authentication failed (configuration incorrect): {}" msgstr "认证失败 (配置错误): {}" -#: settings/utils/ldap.py:681 +#: settings/utils/ldap.py:679 msgid "Authentication failed (username or password incorrect): {}" msgstr "认证失败 (用户名或密码不正确): {}" -#: settings/utils/ldap.py:683 +#: settings/utils/ldap.py:681 msgid "Authentication failed (Unknown): {}" msgstr "认证失败: (未知): {}" -#: settings/utils/ldap.py:686 +#: settings/utils/ldap.py:684 msgid "Authentication success: {}" msgstr "认证成功: {}" @@ -8599,6 +8599,15 @@ msgstr "企业专业版" msgid "Ultimate edition" msgstr "企业旗舰版" +#~ msgid "Synchronization start, please wait." +#~ msgstr "同步开始,请稍等" + +#~ msgid "Synchronization is running, please wait." +#~ msgstr "同步正在运行,请稍等" + +#~ msgid "Synchronization error: {}" +#~ msgstr "同步错误: {}" + #~ msgid "Copy" #~ msgstr "复制" diff --git a/apps/settings/api/ldap.py b/apps/settings/api/ldap.py index 486da3562..9c619e24d 100644 --- a/apps/settings/api/ldap.py +++ b/apps/settings/api/ldap.py @@ -28,71 +28,6 @@ from ..utils import ( logger = get_logger(__file__) -class LDAPTestingConfigAPI(AsyncApiMixin, CreateAPIView): - serializer_class = LDAPTestConfigSerializer - perm_model = Setting - rbac_perms = { - 'POST': 'settings.change_auth', - 'create': 'settings.change_auth', - } - - def is_need_async(self): - return True - - def create(self, request, *args, **kwargs): - serializer = self.serializer_class(data=request.data) - if not serializer.is_valid(): - return Response({"error": str(serializer.errors)}, status=400) - config = self.get_ldap_config(serializer) - ok, msg = LDAPTestUtil(config).test_config() - status = 200 if ok else 400 - return Response(msg, status=status) - - @staticmethod - def get_ldap_config(serializer): - server_uri = serializer.validated_data["AUTH_LDAP_SERVER_URI"] - bind_dn = serializer.validated_data["AUTH_LDAP_BIND_DN"] - password = serializer.validated_data["AUTH_LDAP_BIND_PASSWORD"] - use_ssl = serializer.validated_data.get("AUTH_LDAP_START_TLS", False) - search_ou = serializer.validated_data["AUTH_LDAP_SEARCH_OU"] - search_filter = serializer.validated_data["AUTH_LDAP_SEARCH_FILTER"] - attr_map = serializer.validated_data["AUTH_LDAP_USER_ATTR_MAP"] - auth_ldap = serializer.validated_data.get('AUTH_LDAP', False) - - if not password: - password = settings.AUTH_LDAP_BIND_PASSWORD - - config = { - 'server_uri': server_uri, - 'bind_dn': bind_dn, - 'password': password, - 'use_ssl': use_ssl, - 'search_ou': search_ou, - 'search_filter': search_filter, - 'attr_map': attr_map, - 'auth_ldap': auth_ldap - } - return config - - -class LDAPTestingLoginAPI(APIView): - serializer_class = LDAPTestLoginSerializer - perm_model = Setting - rbac_perms = { - 'POST': 'settings.change_auth' - } - - def post(self, request): - serializer = self.serializer_class(data=request.data) - if not serializer.is_valid(): - return Response({"error": str(serializer.errors)}, status=400) - username = serializer.validated_data['username'] - password = serializer.validated_data['password'] - ok, msg = LDAPTestUtil().test_login(username, password) - status = 200 if ok else 400 - return Response(msg, status=status) - - class LDAPUserListApi(generics.ListAPIView): serializer_class = LDAPUserSerializer perm_model = Setting @@ -162,31 +97,10 @@ class LDAPUserListApi(generics.ListAPIView): # 缓存有数据 if queryset is not None: return super().list(request, *args, **kwargs) - - sync_util = LDAPSyncUtil() - # 还没有同步任务 - if sync_util.task_no_start: - ok, msg = LDAPTestUtil().test_config() - if not ok: - return Response(data={'msg': msg}, status=400) - # 任务外部设置 task running 状态 - sync_util.set_task_status(sync_util.TASK_STATUS_IS_RUNNING) - t = threading.Thread(target=sync_ldap_user) - t.start() - data = {'msg': _('Synchronization start, please wait.')} - return Response(data=data, status=409) - # 同步任务正在执行 - if sync_util.task_is_running: - data = {'msg': _('Synchronization is running, please wait.')} - return Response(data=data, status=409) - # 同步任务执行结束 - if sync_util.task_is_over: - msg = sync_util.get_task_error_msg() - data = {'error': _('Synchronization error: {}'.format(msg))} + else: + data = {'msg': _('Users are not synchronized, please click the user synchronization button')} return Response(data=data, status=400) - return super().list(request, *args, **kwargs) - class LDAPUserImportAPI(APIView): perm_model = Setting @@ -232,18 +146,3 @@ class LDAPUserImportAPI(APIView): return Response({ 'msg': _('Imported {} users successfully (Organization: {})').format(count, orgs_name) }) - - -class LDAPCacheRefreshAPI(generics.RetrieveAPIView): - perm_model = Setting - rbac_perms = { - 'retrieve': 'settings.change_auth' - } - - def retrieve(self, request, *args, **kwargs): - try: - LDAPSyncUtil().clear_cache() - except Exception as e: - logger.error(str(e)) - return Response(data={'msg': str(e)}, status=400) - return Response(data={'msg': 'success'}) diff --git a/apps/settings/urls/api_urls.py b/apps/settings/urls/api_urls.py index 97055bc83..39a755845 100644 --- a/apps/settings/urls/api_urls.py +++ b/apps/settings/urls/api_urls.py @@ -8,11 +8,8 @@ app_name = 'common' urlpatterns = [ path('mail/testing/', api.MailTestingAPI.as_view(), name='mail-testing'), - path('ldap/testing/config/', api.LDAPTestingConfigAPI.as_view(), name='ldap-testing-config'), - path('ldap/testing/login/', api.LDAPTestingLoginAPI.as_view(), name='ldap-testing-login'), path('ldap/users/', api.LDAPUserListApi.as_view(), name='ldap-user-list'), path('ldap/users/import/', api.LDAPUserImportAPI.as_view(), name='ldap-user-import'), - path('ldap/cache/refresh/', api.LDAPCacheRefreshAPI.as_view(), name='ldap-cache-refresh'), path('wecom/testing/', api.WeComTestingAPI.as_view(), name='wecom-testing'), path('dingtalk/testing/', api.DingTalkTestingAPI.as_view(), name='dingtalk-testing'), path('feishu/testing/', api.FeiShuTestingAPI.as_view(), name='feishu-testing'), diff --git a/apps/settings/urls/ws_urls.py b/apps/settings/urls/ws_urls.py index b1555c957..7e8ae6100 100644 --- a/apps/settings/urls/ws_urls.py +++ b/apps/settings/urls/ws_urls.py @@ -6,4 +6,5 @@ app_name = 'common' urlpatterns = [ path('ws/setting/tools/', ws.ToolsWebsocket.as_asgi(), name='setting-tools-ws'), + path('ws/ldap/', ws.LdapWebsocket.as_asgi(), name='ldap-ws'), ] diff --git a/apps/settings/utils/ldap.py b/apps/settings/utils/ldap.py index d5fba8ce9..60babfa4e 100644 --- a/apps/settings/utils/ldap.py +++ b/apps/settings/utils/ldap.py @@ -277,6 +277,9 @@ class LDAPCacheUtil(object): class LDAPSyncUtil(object): + class LDAPSyncUtilException(Exception): + pass + CACHE_KEY_LDAP_USERS_SYNC_TASK_ERROR_MSG = 'CACHE_KEY_LDAP_USERS_SYNC_TASK_ERROR_MSG' CACHE_KEY_LDAP_USERS_SYNC_TASK_STATUS = 'CACHE_KEY_LDAP_USERS_SYNC_TASK_STATUS' @@ -328,34 +331,29 @@ class LDAPSyncUtil(object): def get_task_error_msg(self): logger.info('Get task error msg') - error_msg = cache.get(self.CACHE_KEY_LDAP_USERS_SYNC_TASK_ERROR_MSG) + error_msg = cache.get(self.CACHE_KEY_LDAP_USERS_SYNC_TASK_ERROR_MSG, '') return error_msg def delete_task_error_msg(self): logger.info('Delete task error msg') cache.delete(self.CACHE_KEY_LDAP_USERS_SYNC_TASK_ERROR_MSG) - def pre_sync(self): - self.set_task_status(self.TASK_STATUS_IS_RUNNING) - def sync(self): users = self.server_util.search() self.cache_util.set_users(users) - def post_sync(self): - self.set_task_status(self.TASK_STATUS_IS_OVER) - def perform_sync(self): logger.info('Start perform sync ldap users from server to cache') try: - self.pre_sync() + ok, msg = LDAPTestUtil().test_config() + if not ok: + raise self.LDAPSyncUtilException(msg) self.sync() except Exception as e: error_msg = str(e) logger.error(error_msg) self.set_task_error_msg(error_msg) finally: - self.post_sync() logger.info('End perform sync ldap users from server to cache') close_old_connections() @@ -650,9 +648,9 @@ class LDAPTestUtil(object): # test login def _test_before_login_check(self, username, password): - ok, msg = self.test_config() - if not ok: - raise LDAPConfigurationError(msg) + from settings.ws import CACHE_KEY_LDAP_TEST_CONFIG_TASK_STATUS, TASK_STATUS_IS_OVER + if cache.get(CACHE_KEY_LDAP_TEST_CONFIG_TASK_STATUS) != TASK_STATUS_IS_OVER: + raise self.LDAPBeforeLoginCheckError(_('Please test the connection first')) backend = LDAPAuthorizationBackend() ok, msg = backend.pre_check(username, password) diff --git a/apps/settings/ws.py b/apps/settings/ws.py index a8ae2c398..38bc54af0 100644 --- a/apps/settings/ws.py +++ b/apps/settings/ws.py @@ -1,19 +1,38 @@ # -*- coding: utf-8 -*- # import json +import asyncio from channels.generic.websocket import AsyncJsonWebsocketConsumer +from django.core.cache import cache +from django.conf import settings from common.db.utils import close_old_connections from common.utils import get_logger +from settings.serializers import ( + LDAPTestConfigSerializer, + LDAPTestLoginSerializer +) +from settings.tasks import sync_ldap_user +from settings.utils import ( + LDAPSyncUtil, LDAPTestUtil +) from .tools import ( verbose_ping, verbose_telnet, verbose_nmap, verbose_tcpdump, verbose_traceroute ) - logger = get_logger(__name__) +CACHE_KEY_LDAP_TEST_CONFIG_MSG = 'CACHE_KEY_LDAP_TEST_CONFIG_MSG' +CACHE_KEY_LDAP_TEST_LOGIN_MSG = 'CACHE_KEY_LDAP_TEST_LOGIN_MSG' +CACHE_KEY_LDAP_SYNC_USER_MSG = 'CACHE_KEY_LDAP_SYNC_USER_MSG' +CACHE_KEY_LDAP_TEST_CONFIG_TASK_STATUS = 'CACHE_KEY_LDAP_TEST_CONFIG_TASK_STATUS' +CACHE_KEY_LDAP_TEST_LOGIN_TASK_STATUS = 'CACHE_KEY_LDAP_TEST_LOGIN_TASK_STATUS' +CACHE_KEY_LDAP_SYNC_USER_TASK_STATUS = 'CACHE_KEY_LDAP_SYNC_USER_TASK_STATUS' +TASK_STATUS_IS_RUNNING = 'RUNNING' +TASK_STATUS_IS_OVER = 'OVER' + class ToolsWebsocket(AsyncJsonWebsocketConsumer): @@ -60,7 +79,7 @@ class ToolsWebsocket(AsyncJsonWebsocketConsumer): logger.info(f'Receive request tcpdump: {params}') await verbose_tcpdump(display=self.send_msg, **params) - async def imitate_traceroute(self,dest_ips): + async def imitate_traceroute(self, dest_ips): params = {'dest_ips': dest_ips} await verbose_traceroute(display=self.send_msg, **params) @@ -78,3 +97,113 @@ class ToolsWebsocket(AsyncJsonWebsocketConsumer): async def disconnect(self, code): await self.close() close_old_connections() + + +class LdapWebsocket(AsyncJsonWebsocketConsumer): + async def connect(self): + user = self.scope["user"] + if user.is_authenticated: + await self.accept() + else: + await self.close() + + async def receive(self, text_data=None, bytes_data=None, **kwargs): + data = json.loads(text_data) + msg_type = data.pop('msg_type', 'testing_config') + try: + tool_func = getattr(self, f'run_{msg_type.lower()}') + await asyncio.to_thread(tool_func, data) + if msg_type == 'testing_config': + ok, msg = cache.get(CACHE_KEY_LDAP_TEST_CONFIG_MSG) + elif msg_type == 'sync_user': + ok, msg = cache.get(CACHE_KEY_LDAP_SYNC_USER_MSG) + else: + ok, msg = cache.get(CACHE_KEY_LDAP_TEST_LOGIN_MSG) + await self.send_msg(ok, msg) + except Exception as error: + await self.send_msg(msg='Exception: %s' % error) + + async def send_msg(self, ok=True, msg=''): + await self.send_json({'ok': ok, 'msg': f'{msg}'}) + + async def disconnect(self, code): + await self.close() + close_old_connections() + + @staticmethod + def get_ldap_config(serializer): + server_uri = serializer.validated_data["AUTH_LDAP_SERVER_URI"] + bind_dn = serializer.validated_data["AUTH_LDAP_BIND_DN"] + password = serializer.validated_data["AUTH_LDAP_BIND_PASSWORD"] + use_ssl = serializer.validated_data.get("AUTH_LDAP_START_TLS", False) + search_ou = serializer.validated_data["AUTH_LDAP_SEARCH_OU"] + search_filter = serializer.validated_data["AUTH_LDAP_SEARCH_FILTER"] + attr_map = serializer.validated_data["AUTH_LDAP_USER_ATTR_MAP"] + auth_ldap = serializer.validated_data.get('AUTH_LDAP', False) + + if not password: + password = settings.AUTH_LDAP_BIND_PASSWORD + + config = { + 'server_uri': server_uri, + 'bind_dn': bind_dn, + 'password': password, + 'use_ssl': use_ssl, + 'search_ou': search_ou, + 'search_filter': search_filter, + 'attr_map': attr_map, + 'auth_ldap': auth_ldap + } + return config + + @staticmethod + def task_is_over(task_key): + return cache.get(task_key) == TASK_STATUS_IS_OVER + + @staticmethod + def set_task_status_over(task_key): + cache.set(task_key, TASK_STATUS_IS_OVER, 120) + + @staticmethod + def set_task_msg(task_key, ok, msg): + cache.set(task_key, (ok, msg), 120) + + def run_testing_config(self, data): + while True: + if self.task_is_over(CACHE_KEY_LDAP_TEST_CONFIG_TASK_STATUS): + break + else: + serializer = LDAPTestConfigSerializer(data=data) + if not serializer.is_valid(): + self.send_msg(msg=f'error: {str(serializer.errors)}') + config = self.get_ldap_config(serializer) + ok, msg = LDAPTestUtil(config).test_config() + self.set_task_status_over(CACHE_KEY_LDAP_TEST_CONFIG_TASK_STATUS) + self.set_task_msg(CACHE_KEY_LDAP_TEST_CONFIG_MSG, ok, msg) + + def run_testing_login(self, data): + while True: + if self.task_is_over(CACHE_KEY_LDAP_TEST_LOGIN_TASK_STATUS): + break + else: + serializer = LDAPTestLoginSerializer(data=data) + if not serializer.is_valid(): + self.send_msg(msg=f'error: {str(serializer.errors)}') + username = serializer.validated_data['username'] + password = serializer.validated_data['password'] + ok, msg = LDAPTestUtil().test_login(username, password) + self.set_task_status_over(CACHE_KEY_LDAP_TEST_LOGIN_TASK_STATUS) + self.set_task_msg(CACHE_KEY_LDAP_TEST_LOGIN_MSG, ok, msg) + + def run_sync_user(self, data): + while True: + if self.task_is_over(CACHE_KEY_LDAP_SYNC_USER_TASK_STATUS): + break + else: + sync_util = LDAPSyncUtil() + sync_util.clear_cache() + sync_ldap_user() + msg = sync_util.get_task_error_msg() + ok = False if msg else True + self.set_task_status_over(CACHE_KEY_LDAP_SYNC_USER_TASK_STATUS) + self.set_task_msg(CACHE_KEY_LDAP_SYNC_USER_MSG, ok, msg) From 18f6ffe0cec703b6e64e8ee6e94c0e78de4631d8 Mon Sep 17 00:00:00 2001 From: feng <1304903146@qq.com> Date: Wed, 22 Nov 2023 18:22:51 +0800 Subject: [PATCH 010/111] =?UTF-8?q?perf:=20=E5=9B=BD=E9=99=85=E5=8C=96?= =?UTF-8?q?=E7=BF=BB=E8=AF=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../change_secret/custom/ssh/manifest.yml | 20 ++++++--- .../push_account/host/aix/manifest.yml | 41 +++++++++++++++---- .../push_account/host/posix/manifest.yml | 41 +++++++++++++++---- .../push_account/host/windows/manifest.yml | 13 ++++-- .../host/windows_rdp_verify/manifest.yml | 13 ++++-- .../verify_account/custom/rdp/manifest.yml | 6 +-- .../verify_account/custom/ssh/manifest.yml | 6 +-- .../gather_facts/host/posix/manifest.yml | 6 +-- .../gather_facts/host/windows/manifest.yml | 6 +-- apps/assets/automations/methods.py | 4 +- .../automations/ping/custom/rdp/manifest.yml | 6 +-- .../automations/ping/custom/ssh/manifest.yml | 6 +-- apps/assets/const/types.py | 29 +++++++++++-- apps/common/utils/yml.py | 9 ++-- 14 files changed, 150 insertions(+), 56 deletions(-) diff --git a/apps/accounts/automations/change_secret/custom/ssh/manifest.yml b/apps/accounts/automations/change_secret/custom/ssh/manifest.yml index c46511344..be0248d68 100644 --- a/apps/accounts/automations/change_secret/custom/ssh/manifest.yml +++ b/apps/accounts/automations/change_secret/custom/ssh/manifest.yml @@ -9,12 +9,22 @@ method: change_secret params: - name: commands type: list - label: '自定义命令' + label: "{{ 'Params commands label' | trans }}" default: [ '' ] - help_text: '自定义命令中如需包含账号的 账号、密码、SSH 连接的用户密码 字段,
请使用 {username}、{password}、{login_password}格式,执行任务时会进行替换 。
比如针对 Cisco 主机进行改密,一般需要配置五条命令:
1. enable
2. {login_password}
3. configure terminal
4. username {username} privilege 0 password {password}
5. end' + help_text: "{{ 'Params commands help text' | trans }}" i18n: SSH account change secret: - zh: 使用 SSH 命令行自定义改密 - ja: SSH コマンドライン方式でカスタムパスワード変更 - en: Custom password change by SSH command line + zh: '使用 SSH 命令行自定义改密' + ja: 'SSH コマンドライン方式でカスタムパスワード変更' + en: 'Custom password change by SSH command line' + + Params commands help text: + zh: '自定义命令中如需包含账号的 账号、密码、SSH 连接的用户密码 字段,
请使用 {username}、{password}、{login_password}格式,执行任务时会进行替换 。
比如针对 Cisco 主机进行改密,一般需要配置五条命令:
1. enable
2. {login_password}
3. configure terminal
4. username {username} privilege 0 password {password}
5. end' + ja: 'カスタム コマンドに SSH 接続用のアカウント番号、パスワード、ユーザー パスワード フィールドを含める必要がある場合は、
{ユーザー名}、{パスワード}、{login_password& を使用してください。 # 125; 形式。タスクの実行時に置き換えられます。
たとえば、Cisco ホストのパスワードを変更するには、通常、次の 5 つのコマンドを設定する必要があります:
1.enable
2.{login_password}
3 .ターミナルの設定
4. ユーザー名 {ユーザー名} 権限 0 パスワード {パスワード}
5. 終了' + en: 'If the custom command needs to include the account number, password, and user password field for SSH connection,
Please use {username}, {password}, {login_password&# 125; format, which will be replaced when executing the task.
For example, to change the password of a Cisco host, you generally need to configure five commands:
1. enable
2. {login_password}
3. configure terminal
4. username {username} privilege 0 password {password}
5. end' + + Params commands label: + zh: '自定义命令' + ja: 'カスタムコマンド' + en: 'Custom command' diff --git a/apps/accounts/automations/push_account/host/aix/manifest.yml b/apps/accounts/automations/push_account/host/aix/manifest.yml index 949d49758..ee62d7020 100644 --- a/apps/accounts/automations/push_account/host/aix/manifest.yml +++ b/apps/accounts/automations/push_account/host/aix/manifest.yml @@ -9,7 +9,7 @@ params: type: str label: 'Sudo' default: '/bin/whoami' - help_text: '使用逗号分隔多个命令,如: /bin/whoami,/sbin/ifconfig' + help_text: "{{ 'Params sudo help text' | trans }}" - name: shell type: str @@ -18,19 +18,44 @@ params: - name: home type: str - label: '家目录' + label: "{{ 'Params home label' | trans }}" default: '' - help_text: '默认家目录 /home/系统用户名: /home/username' + help_text: "{{ 'Params home help text' | trans }}" - name: groups type: str - label: '用户组' + label: "{{ 'Params groups label' | trans }}" default: '' - help_text: '请输入用户组,多个用户组使用逗号分隔(需填写已存在的用户组)' + help_text: "{{ 'Params groups help text' | trans }}" i18n: Aix account push: - zh: 使用 Ansible 模块 user 执行 Aix 账号推送 (DES) - ja: Ansible user モジュールを使用して Aix アカウントをプッシュする (DES) - en: Using Ansible module user to push account (DES) + zh: '使用 Ansible 模块 user 执行 Aix 账号推送 (DES)' + ja: 'Ansible user モジュールを使用して Aix アカウントをプッシュする (DES)' + en: 'Using Ansible module user to push account (DES)' + + Params sudo help text: + zh: '使用逗号分隔多个命令,如: /bin/whoami,/sbin/ifconfig' + ja: 'コンマで区切って複数のコマンドを入力してください。例: /bin/whoami,/sbin/ifconfig' + en: 'Use commas to separate multiple commands, such as: /bin/whoami,/sbin/ifconfig' + + Params home help text: + zh: '默认家目录 /home/{账号用户名}' + ja: 'デフォルトのホームディレクトリ /home/{アカウントユーザ名}' + en: 'Default home directory /home/{account username}' + + Params groups help text: + zh: '请输入用户组,多个用户组使用逗号分隔(需填写已存在的用户组)' + ja: 'グループを入力してください。複数のグループはコンマで区切ってください(既存のグループを入力してください)' + en: 'Please enter the group. Multiple groups are separated by commas (please enter the existing group)' + + Params home label: + zh: '家目录' + ja: 'ホームディレクトリ' + en: 'Home' + + Params groups label: + zh: '用户组' + ja: 'グループ' + en: 'Groups' diff --git a/apps/accounts/automations/push_account/host/posix/manifest.yml b/apps/accounts/automations/push_account/host/posix/manifest.yml index 0c1d31845..32964f1d6 100644 --- a/apps/accounts/automations/push_account/host/posix/manifest.yml +++ b/apps/accounts/automations/push_account/host/posix/manifest.yml @@ -10,7 +10,7 @@ params: type: str label: 'Sudo' default: '/bin/whoami' - help_text: '使用逗号分隔多个命令,如: /bin/whoami,/sbin/ifconfig' + help_text: "{{ 'Params sudo help text' | trans }}" - name: shell type: str @@ -20,18 +20,43 @@ params: - name: home type: str - label: '家目录' + label: "{{ 'Params home label' | trans }}" default: '' - help_text: '默认家目录 /home/系统用户名: /home/username' + help_text: "{{ 'Params home help text' | trans }}" - name: groups type: str - label: '用户组' + label: "{{ 'Params groups label' | trans }}" default: '' - help_text: '请输入用户组,多个用户组使用逗号分隔(需填写已存在的用户组)' + help_text: "{{ 'Params groups help text' | trans }}" i18n: Posix account push: - zh: 使用 Ansible 模块 user 执行账号推送 (sha512) - ja: Ansible user モジュールを使用してアカウントをプッシュする (sha512) - en: Using Ansible module user to push account (sha512) + zh: '使用 Ansible 模块 user 执行账号推送 (sha512)' + ja: 'Ansible user モジュールを使用してアカウントをプッシュする (sha512)' + en: 'Using Ansible module user to push account (sha512)' + + Params sudo help text: + zh: '使用逗号分隔多个命令,如: /bin/whoami,/sbin/ifconfig' + ja: 'コンマで区切って複数のコマンドを入力してください。例: /bin/whoami,/sbin/ifconfig' + en: 'Use commas to separate multiple commands, such as: /bin/whoami,/sbin/ifconfig' + + Params home help text: + zh: '默认家目录 /home/{账号用户名}' + ja: 'デフォルトのホームディレクトリ /home/{アカウントユーザ名}' + en: 'Default home directory /home/{account username}' + + Params groups help text: + zh: '请输入用户组,多个用户组使用逗号分隔(需填写已存在的用户组)' + ja: 'グループを入力してください。複数のグループはコンマで区切ってください(既存のグループを入力してください)' + en: 'Please enter the group. Multiple groups are separated by commas (please enter the existing group)' + + Params home label: + zh: '家目录' + ja: 'ホームディレクトリ' + en: 'Home' + + Params groups label: + zh: '用户组' + ja: 'グループ' + en: 'Groups' \ No newline at end of file diff --git a/apps/accounts/automations/push_account/host/windows/manifest.yml b/apps/accounts/automations/push_account/host/windows/manifest.yml index 7866e3c13..dcbdfe7f8 100644 --- a/apps/accounts/automations/push_account/host/windows/manifest.yml +++ b/apps/accounts/automations/push_account/host/windows/manifest.yml @@ -10,10 +10,15 @@ params: type: str label: '用户组' default: 'Users,Remote Desktop Users' - help_text: '请输入用户组,多个用户组使用逗号分隔(需填写已存在的用户组)' + help_text: "{{ 'Params groups help text' | trans }}" i18n: Windows account push: - zh: 使用 Ansible 模块 win_user 执行 Windows 账号推送 - ja: Ansible win_user モジュールを使用して Windows アカウントをプッシュする - en: Using Ansible module win_user to push account + zh: '使用 Ansible 模块 win_user 执行 Windows 账号推送' + ja: 'Ansible win_user モジュールを使用して Windows アカウントをプッシュする' + en: 'Using Ansible module win_user to push account' + + Params groups help text: + zh: '请输入用户组,多个用户组使用逗号分隔(需填写已存在的用户组)' + ja: 'グループを入力してください。複数のグループはコンマで区切ってください(既存のグループを入力してください)' + en: 'Please enter the group. Multiple groups are separated by commas (please enter the existing group)' diff --git a/apps/accounts/automations/push_account/host/windows_rdp_verify/manifest.yml b/apps/accounts/automations/push_account/host/windows_rdp_verify/manifest.yml index 449cf726f..d08a29ebc 100644 --- a/apps/accounts/automations/push_account/host/windows_rdp_verify/manifest.yml +++ b/apps/accounts/automations/push_account/host/windows_rdp_verify/manifest.yml @@ -10,10 +10,15 @@ params: type: str label: '用户组' default: 'Users,Remote Desktop Users' - help_text: '请输入用户组,多个用户组使用逗号分隔(需填写已存在的用户组)' + help_text: "{{ 'Params groups help text' | trans }}" i18n: Windows account push rdp verify: - zh: 使用 Ansible 模块 win_user 执行 Windows 账号推送 RDP 协议测试最后的可连接性 - ja: Ansibleモジュールwin_userがWindowsアカウントプッシュRDPプロトコルテストを実行する最後の接続性 - en: Using the Ansible module win_user performs Windows account push RDP protocol testing for final connectivity + zh: '使用 Ansible 模块 win_user 执行 Windows 账号推送(最后使用 Python 模块 pyfreerdp 验证账号的可连接性)' + ja: 'Ansible モジュール win_user を使用して Windows アカウントのプッシュを実行します (最後に Python モジュール pyfreerdp を使用してアカウントの接続性を確認します)' + en: 'Use the Ansible module win_user to perform Windows account push (finally use the Python module pyfreerdp to verify the connectability of the account)' + + Params groups help text: + zh: '请输入用户组,多个用户组使用逗号分隔(需填写已存在的用户组)' + ja: 'グループを入力してください。複数のグループはコンマで区切ってください(既存のグループを入力してください)' + en: 'Please enter the group. Multiple groups are separated by commas (please enter the existing group)' diff --git a/apps/accounts/automations/verify_account/custom/rdp/manifest.yml b/apps/accounts/automations/verify_account/custom/rdp/manifest.yml index 79fcce96b..1d68afbac 100644 --- a/apps/accounts/automations/verify_account/custom/rdp/manifest.yml +++ b/apps/accounts/automations/verify_account/custom/rdp/manifest.yml @@ -8,6 +8,6 @@ method: verify_account i18n: Windows rdp account verify: - zh: 使用 Python 模块 pyfreerdp 验证账号 - ja: Python モジュール pyfreerdp を使用してアカウントを検証する - en: Using Python module pyfreerdp to verify account + zh: '使用 Python 模块 pyfreerdp 验证账号' + ja: 'Python モジュール pyfreerdp を使用してアカウントを検証する' + en: 'Using Python module pyfreerdp to verify account' diff --git a/apps/accounts/automations/verify_account/custom/ssh/manifest.yml b/apps/accounts/automations/verify_account/custom/ssh/manifest.yml index 666266416..12d495d01 100644 --- a/apps/accounts/automations/verify_account/custom/ssh/manifest.yml +++ b/apps/accounts/automations/verify_account/custom/ssh/manifest.yml @@ -9,6 +9,6 @@ method: verify_account i18n: SSH account verify: - zh: 使用 Python 模块 paramiko 验证账号 - ja: Python モジュール paramiko を使用してアカウントを検証する - en: Using Python module paramiko to verify account + zh: '使用 Python 模块 paramiko 验证账号' + ja: 'Python モジュール paramiko を使用してアカウントを検証する' + en: 'Using Python module paramiko to verify account' diff --git a/apps/assets/automations/gather_facts/host/posix/manifest.yml b/apps/assets/automations/gather_facts/host/posix/manifest.yml index a92c496e9..d1a833311 100644 --- a/apps/assets/automations/gather_facts/host/posix/manifest.yml +++ b/apps/assets/automations/gather_facts/host/posix/manifest.yml @@ -7,6 +7,6 @@ type: method: gather_facts i18n: Gather posix facts: - zh: 使用 Ansible 指令 gather_facts 从主机获取设备信息 - en: Gather facts from asset using gather_facts - ja: gather_factsを使用してPosixから情報を収集する + zh: '使用 Ansible 指令 gather_facts 从主机获取设备信息' + en: 'Gather facts from asset using gather_facts' + ja: 'gather_factsを使用してPosixから情報を収集する' diff --git a/apps/assets/automations/gather_facts/host/windows/manifest.yml b/apps/assets/automations/gather_facts/host/windows/manifest.yml index 809208e10..7068f4d6e 100644 --- a/apps/assets/automations/gather_facts/host/windows/manifest.yml +++ b/apps/assets/automations/gather_facts/host/windows/manifest.yml @@ -7,6 +7,6 @@ type: - windows i18n: Gather facts windows: - zh: 使用 Ansible 指令 gather_facts 从 Windows 获取设备信息 - en: Gather facts from Windows using gather_facts - ja: gather_factsを使用してWindowsから情報を収集する + zh: '使用 Ansible 指令 gather_facts 从 Windows 获取设备信息' + en: 'Gather facts from Windows using gather_facts' + ja: 'gather_factsを使用してWindowsから情報を収集する' diff --git a/apps/assets/automations/methods.py b/apps/assets/automations/methods.py index 8db474d7b..b0ad883fd 100644 --- a/apps/assets/automations/methods.py +++ b/apps/assets/automations/methods.py @@ -31,7 +31,7 @@ def generate_serializer(data): return create_serializer_class(serializer_name, params) -def get_platform_automation_methods(path): +def get_platform_automation_methods(path, lang=None): methods = [] for root, dirs, files in os.walk(path, topdown=False): for name in files: @@ -40,7 +40,7 @@ def get_platform_automation_methods(path): continue with open(path, 'r', encoding='utf8') as f: - manifest = yaml_load_with_i18n(f) + manifest = yaml_load_with_i18n(f, lang) check_platform_method(manifest, path) manifest['dir'] = os.path.dirname(path) manifest['params_serializer'] = generate_serializer(manifest) diff --git a/apps/assets/automations/ping/custom/rdp/manifest.yml b/apps/assets/automations/ping/custom/rdp/manifest.yml index 77b8a855e..ab5846100 100644 --- a/apps/assets/automations/ping/custom/rdp/manifest.yml +++ b/apps/assets/automations/ping/custom/rdp/manifest.yml @@ -8,6 +8,6 @@ type: method: ping i18n: Ping by pyfreerdp: - zh: 使用 Python 模块 pyfreerdp 测试主机可连接性 - en: Ping by pyfreerdp module - ja: Pyfreerdpモジュールを使用してホストにPingする + zh: '使用 Python 模块 pyfreerdp 测试主机可连接性' + en: 'Ping by pyfreerdp module' + ja: 'Pyfreerdpモジュールを使用してホストにPingする' diff --git a/apps/assets/automations/ping/custom/ssh/manifest.yml b/apps/assets/automations/ping/custom/ssh/manifest.yml index d57a50a2e..95bb85d60 100644 --- a/apps/assets/automations/ping/custom/ssh/manifest.yml +++ b/apps/assets/automations/ping/custom/ssh/manifest.yml @@ -8,6 +8,6 @@ type: method: ping i18n: Ping by paramiko: - zh: 使用 Python 模块 paramiko 测试主机可连接性 - en: Ping by paramiko module - ja: Paramikoモジュールを使用してホストにPingする + zh: '使用 Python 模块 paramiko 测试主机可连接性' + en: 'Ping by paramiko module' + ja: 'Paramikoモジュールを使用してホストにPingする' diff --git a/apps/assets/const/types.py b/apps/assets/const/types.py index 8654002b9..c33052c64 100644 --- a/apps/assets/const/types.py +++ b/apps/assets/const/types.py @@ -2,9 +2,11 @@ import json from collections import defaultdict from copy import deepcopy +from django.conf import settings from django.utils.translation import gettext as _ from common.db.models import ChoicesMixin +from jumpserver.utils import get_current_request from .category import Category from .cloud import CloudTypes from .custom import CustomTypes @@ -22,6 +24,8 @@ class AllTypes(ChoicesMixin): CloudTypes, WebTypes, CustomTypes, GPTTypes ] _category_constrains = {} + _automation_methods = None + _current_language = settings.LANGUAGE_CODE @classmethod def choices(cls): @@ -61,9 +65,28 @@ class AllTypes(ChoicesMixin): @classmethod def get_automation_methods(cls): - from assets.automations import platform_automation_methods as asset_methods - from accounts.automations import platform_automation_methods as account_methods - return asset_methods + account_methods + from assets.automations import methods as asset + from accounts.automations import methods as account + + automation_methods = \ + asset.platform_automation_methods + \ + account.platform_automation_methods + + request = get_current_request() + if request is None: + return automation_methods + + language = request.LANGUAGE_CODE + if cls._automation_methods is not None and language == cls._current_language: + automation_methods = cls._automation_methods + else: + automation_methods = \ + asset.get_platform_automation_methods(asset.BASE_DIR, language) + \ + account.get_platform_automation_methods(account.BASE_DIR, language) + + cls._current_language = language + cls._automation_methods = automation_methods + return cls._automation_methods @classmethod def set_automation_methods(cls, category, tp_name, constraints): diff --git a/apps/common/utils/yml.py b/apps/common/utils/yml.py index 6db03ea63..c5221b948 100644 --- a/apps/common/utils/yml.py +++ b/apps/common/utils/yml.py @@ -5,20 +5,21 @@ from django.conf import settings from jinja2 import Environment -def translate(key, i18n): - lang = settings.LANGUAGE_CODE[:2] +def translate(key, i18n, lang): + lang = settings.LANGUAGE_CODE if lang is None else lang + lang = lang[:2] lang_data = i18n.get(key, {}) return lang_data.get(lang, key) -def yaml_load_with_i18n(stream): +def yaml_load_with_i18n(stream, lang): ori_text = stream.read() stream = io.StringIO(ori_text) yaml_data = yaml.safe_load(stream) i18n = yaml_data.get('i18n', {}) env = Environment() - env.filters['trans'] = lambda key: translate(key, i18n) + env.filters['trans'] = lambda key: translate(key, i18n, lang) template = env.from_string(ori_text) yaml_data = template.render() yaml_f = io.StringIO(yaml_data) From 64f3509c8c70b2a03b6110530966043133abbfef Mon Sep 17 00:00:00 2001 From: wangruidong <940853815@qq.com> Date: Thu, 23 Nov 2023 16:58:58 +0800 Subject: [PATCH 011/111] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E5=A4=87?= =?UTF-8?q?=E6=A1=88=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/jumpserver/context_processor.py | 2 ++ apps/locale/ja/LC_MESSAGES/django.mo | 3 ++ apps/locale/ja/LC_MESSAGES/django.po | 18 ++++++++--- apps/locale/zh/LC_MESSAGES/django.mo | 3 ++ apps/locale/zh/LC_MESSAGES/django.po | 14 +++++++-- apps/static/img/beian.png | Bin 0 -> 11143 bytes apps/templates/_foot_js.html | 45 +++++++++++++++++++++------ 7 files changed, 69 insertions(+), 16 deletions(-) create mode 100644 apps/locale/ja/LC_MESSAGES/django.mo create mode 100644 apps/locale/zh/LC_MESSAGES/django.mo create mode 100644 apps/static/img/beian.png diff --git a/apps/jumpserver/context_processor.py b/apps/jumpserver/context_processor.py index 7b92674bb..24a96b619 100644 --- a/apps/jumpserver/context_processor.py +++ b/apps/jumpserver/context_processor.py @@ -12,6 +12,8 @@ default_interface = dict(( ('login_title', _('JumpServer Open Source Bastion Host')), ('theme', 'classic_green'), ('theme_info', {}), + ('beian_link', ''), + ('beian_text', '') )) default_context = { diff --git a/apps/locale/ja/LC_MESSAGES/django.mo b/apps/locale/ja/LC_MESSAGES/django.mo new file mode 100644 index 000000000..0cd5372bb --- /dev/null +++ b/apps/locale/ja/LC_MESSAGES/django.mo @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:185acf00dbd3e3ef37e9faf300c438e120f257fc876e72135cecb1234dd5e453 +size 166353 diff --git a/apps/locale/ja/LC_MESSAGES/django.po b/apps/locale/ja/LC_MESSAGES/django.po index 2bb689180..b373df53b 100644 --- a/apps/locale/ja/LC_MESSAGES/django.po +++ b/apps/locale/ja/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-11-22 16:43+0800\n" +"POT-Creation-Date: 2023-11-24 10:34+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -1574,7 +1574,7 @@ msgstr "ボタンセレクターを確認する" msgid "API mode" msgstr "APIモード" -#: assets/const/types.py:224 +#: assets/const/types.py:247 msgid "All types" msgstr "いろんなタイプ" @@ -4728,7 +4728,9 @@ msgstr "{}に送信されたテストメールを確認してください" #: settings/api/ldap.py:101 msgid "" "Users are not synchronized, please click the user synchronization button" -msgstr "ユーザーは同期されていません。「ユーザーを同期」ボタンをクリックしてください。" +msgstr "" +"ユーザーは同期されていません。「ユーザーを同期」ボタンをクリックしてくださ" +"い。" #: settings/api/ldap.py:137 msgid "Get ldap users is None" @@ -8685,7 +8687,15 @@ msgstr "ログアウトページのロゴ" msgid "Theme" msgstr "テーマ" -#: xpack/plugins/interface/models.py:44 xpack/plugins/interface/models.py:85 +#: xpack/plugins/interface/models.py:42 +msgid "Beian link" +msgstr "公安オンライン申告ジャンプリンク" + +#: xpack/plugins/interface/models.py:43 +msgid "Beian text" +msgstr "公安網登録番号" + +#: xpack/plugins/interface/models.py:46 xpack/plugins/interface/models.py:87 msgid "Interface setting" msgstr "インターフェイスの設定" diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo new file mode 100644 index 000000000..73dee4284 --- /dev/null +++ b/apps/locale/zh/LC_MESSAGES/django.mo @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e4af35b685fe0eb73e4dd9790669164bb823065d994a24f5441c9cff7a6e99e7 +size 135811 diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index da13efde4..5f24678f2 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: JumpServer 0.3.3\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-11-22 16:42+0800\n" +"POT-Creation-Date: 2023-11-24 10:34+0800\n" "PO-Revision-Date: 2021-05-20 10:54+0800\n" "Last-Translator: ibuler \n" "Language-Team: JumpServer team\n" @@ -1565,7 +1565,7 @@ msgstr "确认按钮选择器" msgid "API mode" msgstr "API 模式" -#: assets/const/types.py:224 +#: assets/const/types.py:247 msgid "All types" msgstr "所有类型" @@ -8567,7 +8567,15 @@ msgstr "退出页面logo" msgid "Theme" msgstr "主题" -#: xpack/plugins/interface/models.py:44 xpack/plugins/interface/models.py:85 +#: xpack/plugins/interface/models.py:42 +msgid "Beian link" +msgstr "公安联网备案跳转链接" + +#: xpack/plugins/interface/models.py:43 +msgid "Beian text" +msgstr "公安联网备案号" + +#: xpack/plugins/interface/models.py:46 xpack/plugins/interface/models.py:87 msgid "Interface setting" msgstr "界面设置" diff --git a/apps/static/img/beian.png b/apps/static/img/beian.png new file mode 100644 index 0000000000000000000000000000000000000000..6fe667f73fa1ae8c37d33b5bece6f602a861f7bf GIT binary patch literal 11143 zcmbVy1yozzx-C%Lo#O5u+$FeM3zQ;(1c%`6THK|S;?fo^?oeEcJH_3-xIB8!fBtvw zefPe1$IBQyd+oL6n&13neIp}Dgoc_t1}X_E3=9m0qJoUp%U%5C0wN>6ym$N78NA$3 z92E4OVPMek{#>vy8JVwPU=VJsbo5;GR8@q{pmrP}Fw_*n;bG_Sf`)++mGE!?nb|;G zs7xUiR`y~vC(Z3NR90Xy8eKkBE>#C0#L`N^%L$_GrKV%%Wn(4;rjZb*67>*%A+UqE zfT%p|Z0((eJ;Z4K!7KbS{xi)!K&TUhil2jv-HeM1K*i0? z&%w>l%g@KhMg`ykaC35Ta{~C;x%q^7xrG5-RDZo_Ud%ax&4smOEhxb z%*pBQ?#|)P!vS@&;N%t(65`|nZ~_4AF9>#LPkR@T2fMv9?Y|ggAkJn^Rt_#!PA1MAq#^ChZ?@9E5>R5RePhNe2qG{a2Xd_*l6Fg}J!?2~~xHt;{|D6I4)#n_Kt= z&CUBCpf6Pe2DyO#pJ1?=usPJp4)o%(l^w_e!s%ddK|}R-AccWYTd32E;TLs0|30q> z1Zp@z&8=)-7M!)@rKuEUf&AP;{QT?y4(@-*RaF&Mw0Cv^*_%NWWyEM+!oy)@1r~;w zL(I6jxP{pH&0ors7sAiOZVC|qvkO1~0$?6)Q;>iF-@o?DK+RnL6v3bU|2z<2sM!n0 z|CYzcEocVeH3zYS0K9_iTwJ{7>>voplpSQs%MFGIfgs%cfPb;6J6XM)Ns#Tov;I*9 zeql6!f%Ag6&DgmC{ATRDf?T}pAZ`J3c5^-e7z8i_3-JKV{}lc|YDrkZ%K0VBo_`%B zZHVJvBU>w~f0U0f$n4Lt5Th~sQx6a@&0o`2|3y6hN0$HE?`{csA^pEp$v>?-L(N^> zK~4}Ui3ki7-urO|TI{Bei# zPe=M^VmSYE&i=9XZ{6!(@RyeOXZUwBetG!2okQ$jsGVM#HT?8+Ees4jpQ4PEjz?x| zvsDd5*LCIIVAx*WfAnbot+Vo*Lanb;G@6O=@NjIBRG|3ij@QVrWVR}qbKk#jSo9?r z8;%2P09$q-dEXI&qk9TrSSvozEIK zK-bsQefPC{)Q9S_QEzl5oF9ol_5Hqo1mAcG?PjeQQ#kbjQH&+%JRCSoLXnU79LqcK z4eNSQB;1AHJC|>FUMzsSKuX@1BYu60=MLj{i%8<9X|35cF%sOT>Wil~)3YtMM{s6? zSUSU}ER(9eag>HzHgTywNAa5`5_HvXPOTLPHbTO?0$Hg~q~`Mti5Y$wO15e`5WCXSBSzkWQVjqTb_Ei-DFiCWZfc2khYBQTvd$>cpiI;?v#X+ zT@S;v72w=pz!_IV@&=2FOnCXHfZ_36Omxb?IJ~IX{0qYga6qNl$cm-M(Y!cx4kF}i zkg_ zU=gRY(3CnI&%_I4=6h`Ehsjla$j0v`2t6m$^3>Y`rI=YF*qDZ#h=OkK%wmzUDI~eU zE#j-|WkL1E6pm+dQhEvHY=^c2U;NkR){gyshEA5nFL++HpW)5w*4Gaq*(eWLEZ!=F z8(<+He>zL&WXUN(hBI>W`s{89x@k0IE@PrMCXT;$LT0hFtMm*zg>xYbVABjOY|qdp zFH5+CUGW2~^0;@r3fJa(Rih&4p1;K>LI-VBsP>-N=$H2E%EI!!i1Q;u{Jrt8tGm)> zaUth_6Ha-59{I3qDDEvnThJ7GnW58(dg5ESja9zqyyk+Xo?H;x$sBXa*F>VhfWo3d z^&=H?@BT{y31CXl7{_{!PiS+HN7;v<3R}+vn>-DLmDf9=e@haqbPPES`I* z*S2Cw;S^pEg@Be#0lz--c0RF>BYqDFt*uu(l%##Qpwhx=4WG-;u_bDsdRSu6eQXnG zRn!_6={>=*hvoW%CqLKPYT?lcy)7j@511#yi4?IoG+ngcmyUf8CoMuY4(1fH0rr-w zI-E$Ux+80OB{MDisBOy%m#G>#td+ShH$Kxdx2M5BHI6>ILF7Hxt4rD2*YoRzFiWQr zI&s4ipYN9_pu+wN3!@sE@PXl6LXX~R8h(55cguV%>K5Hmi2h{-zXQ<{>>{o|>#y^L z`?K{N8|Po#;M(X*2^s>0!UxEw-q{j!+meQ!kYN`fu(=`_EC4x#mVPa%dD&#&Hq%}| z%KRYz;J$LzyDIRs_okV#J3{??9SIMN&ps$i133&*!a?xsZBoZ4@nD3fI>K?w6w~x^ zgFWXvOSOCg=TX1@(#?U7nM(#Wi*<^Ri{2FrM*jCmMvpg;3Y=T^`iCx?C)ppLo286= z?u2R!jWd^3<{#oIem=J^<)e8&9d2`EON=2xlk<|_H~rdhUmlY>z$Jz^Vc*gUf9UPH zSvkU?iC0_A>`~?W;=4Kh!Dgg)<8DFlXy`HDB_EJ?h_$9@2XrGH=EdBNL)#Vt;u1;kt-?t~l*1 z7F`ihX!)4X=x`&GehPu~T_p54v^2ne-|;#S_OZs~AbkO#nFb|}+T^?f3qBsR_MJQ) zmi0}()YOdfuZmxEUeEinP)@n$+pB1GiBiGL*bM~sg0W;mj(O&r)sm(!8fGEVk#Nq} z=<9W|Q?U%oHpL0LCo*a>_)%iu;*PS*@6Do3eX+Q)F{YDnzmN1~P|nYXJ=$xKo-xui z`VoFwN7mN^Cz3-%2+ zrR-c~Y#p`I;O{&*u9>Q@^n&gzT`2+l4O&{_l^;YC%w9z-)k|KZxl`B0t$DtQPMypB z0f}8DRrUZXJMMt0Xor|=B!yL=3MWG=q5X_>a`>19RGI3`6naz*UL+Pjv_`D>*uE1~p6$BhRLlUl9dFl>pF^3rXPF=o8zG@ly&-?hC*hOdlEouYACw za(m`rY=OgtNzsWTJovL?LrkS;=a+`iN*!Zz_8?oDZFw(fHhS9q+{j`Sh2j?rO@g;~> zWTkZdZ$tO{mP4PfF1ScVm8h9nNIbXAAB_Dm?$WL$L8nTAFjMa-*Je((;QMbAH#Y;0 z7jb1X@ZiJ9lL(pHpjomFt{9WZgtkvE5A#Pmf7}P#60D*CBva(#g1)5FKnK}i(7xIomJuuj~s{>P{G7%=0fO<=sX&) z-Zz&amo?LAM+|Cc={@g6kM2wE)S}^%HpSM1RYTPq8ZQ9`dHr-KGdWsiF0xMT@E+lh zLa_Pbg`iaJ?9}b+X|4Txays?y=H%hQ@(AI_N!tdnICR0M;GHI3N`Jfk{ZRdLSg&Rr z`G^?h5?;{T+E@xP$}P@#*DKIAVZwT-S>Xj;Pa6>Ie?{LJY;#jTO52R!c0zN@c+&a2 zoYc=L8l$LGnk@MJnEP!2Y$kSt6rwUS2*$A_)Y?9erP@{Cxy^G#>f>ZI8!D?9^8>CT zANkXSNQL-)6A?U=?;Vlq;apWNeym?HmT)J2nA_4BaM2?RJXECEIS9Ac$pd~wUn83% zS|-B@1(F}+P-f6K-VzriA6)Ce-68A|sbha;(JV&f)L@jjkHN7l!~wyV6raj{vla41 zHztsvat{d4Covd9UGv7bk}9Mj{%CJ_9<=%xs+Py%+G!my6KNYC^gVi-ZA2gbfYKp{ z%+UjG(tu7GJ&O@L5RTo*!AwC9dxN_CXk*)3Z>ut};}$MC#4m#E+Jp}N9?Rh?wf#w=OT?oWD56XeLqi|2o**CITilMZ;v z#9N2wr|5d4@q&K9d((tTyF~@$R@jD}nWjoi?;nVVC8Hf8?xcXRFH)4ObP(zswy_wT zkW@XN%{6>Slc#wD(R+r^vNdLxsoY9imEStLoUTDqoUo%r0bRmUv|$<0@iH8h1==jz z#PTA&47cCz&!HPd#-2~470^Xtk|;=3i|o|0%yl;?S%cS1o}I9iVRX-cadoUJw;e!N z8mgzABj4-T%;?aE8SH>ObJ3^?=Q==mxs8EtPL!} zgQqn`futT9asjNH4igQmzT)>9)EEZ!k@THVf}I=!5t7_cDymW?Ta?g9O{Ey{v1vg$?n(@ozubV7HvJtxU(BzvK z3)5=+G?H`s-hohh%q+M92X2CPXo z@cT{F>(_{De|Omj75=Coc_?0X$0{}RsQy?>F(ki3Tw26r463C<2Q}HnA2(XkhDh$k zbFNGlHfLfN_$F#sdkmvG8K}(Y%MHNq0D4E$O=e3*z(eNpIw^X_c)dX$KKR;~U+unV zE{)Nkn}C1uS_zx%or1P0?>Ur_j{BNWYjuxc?NDejx{`UT7ti%jSstXYT`+yH-0bl2 z&BdA()bkkXHdK%3P&dOO_P$Gau6!ExMGAP@M5>?eiuCg?9Jk~gF3Yp#8_W$KLHZ&I ziry+^2Cg>lKv)VzF@Gu^FMK-PzQ|osM(LHel%)u?0|<`wfq?)h#v2TWl2=WmbPDQ; z`g(r*ID-W_{(@30p{y7Kctd0AV;^&MhzEXBzt_y8#Uju}`GxUd-LVvojz!5lzArSt z9pD9?Td31}CGDxhz>lj#f-dZgJjyu39Hn?oUg5`=J)8fleyXW@2ybnh&}b7hluIO0QC3>aXvb6o_bS+LWYc3npH!^P8K%bXnDZ)_C640c;y0sjxe=L`A2fAy7JocT zv3JD{@pA{rKI_MD;laWrlH(QA_yK<&{|xqoW)>Ns;WyX5B+;i|u6*_Ra*w_{d7N`f z_Ri-baojgQ#jV>V24^kMu_#7RI?pV+=C76Uy9zC=eNdcSLKZd|stS}7b`*@w?q6M6 z22ZD~kLg2iF52!&K3!R~`#loxz~{9H5^r|Ky*h8{CdRVTPPCV9C8*4hU2I&*n_ijR z&`Ow3OzN9y@M;7)d;vbW5Y?8U;_<1(o4C)M%705&ixnCF_@Y_KiFg^rwLJkjJKdnAxzR{NT9-Stek;-SeStStq zq)sa9E$-)=OtYu4DfaUnkvWARjej{rpI#~J~uIhJOvoeroa2#hdI*^jL0A!yY)Y2^cmvs7Q(3!%e%ItZm}m- z3OxNW@WG=rh6|;a06vMj<(ch#aqT3d;~R`OIA(2!g0^`BzRG1Y&~)pEHu{F@u18Qi z=Mk!zkbAZHy-Z}Oy%eEX^sA6=$SZJbP;%nOE;JtZMc>T|zxWZO$H0mu@Aa#C^0Xpb ztR|EZ!MwN8(d?k%F5#|`vmms(d!OG4JHcpsjvu*n3QS}rbI3rmiI4R_kDYplwA$~r z_gwxL@hFH{%aaarbs8I>eV`l?EjB*eVT@4-PQ%8GPEg zFDrVDiC_u|b%Au4GzJ@EeXWHv^7renzp^TI9uj0mJRdXi=M7WT z0oWNTzig(SAfvG6agkR+f&_Zce$jeqhjP{ zVghPSH7c@9U6&7rQXKf0Oc*WO@3YHjQ6MWgc9tNt;2lLcV@bQ@96o9B=gkGDQNN@6 z7LwH@%9b&Mlnv#zv(OW{)(xl{(c7=~*10q>I=D6%g|5+Ww3a)9~DN2j2WE(Tc0;O()QVf+x`Ku}Lk#?|TRBEk`rSCho{NS{U^Og-|I zzm{-4xz-N6YaVT}|7PlyrgAvt7@QUTTi0@%fUFOwK@5BJpyX739GAXL@toJMM9&Y}* zE0EZw&RQ#OJCKV?a7V4-ioRpAFo4$Umt4jGaeWB&T>g4eu z)Vt!lpNm(#L-l0RVl{GSW2m>m%7+rkPUI>RWN#) zz8SZ?HaR9X*hwW8<8m;aACLQsG`^ygdv_<=j%40sTX&gob`zrSeTR7zejf9Nv$POR z9=ZOIVZjHn3#h*SMmie10R(Nefpw5o%wk=>I!|oA?lMH8Z_^-)=k9Bb_crV&*a~nn z@$@hrVbAMR{OTD+w5}MQ%(XbXGq<)O>gtR{4Kk?cv(i^8eHCwV7i2)GDAeE|s&Di4 zOMKVFhEYW)4*-sMJzt|k!W$UMBPW9HMX!eJ0LMt*^MES#1{BUrC|d){tj%|ZU%1s2 zYuHVgf6)4zsiM0vU@{dYTGa@gDt1eBWnfOC*J1D66soJiM(`l9HPVP8qih1KuV$Kl zHR!uW_`3JKUgPk039`k@r}+aGtW;{GA(*v58B<^7*n;eew9i-`I0+Wn1i_fJY`z4+ z=(l=nYdR4wsMNelHX-F^nWK)SHU#iYp|1wZMBlvewDP2wDs`N8IN>u<6M-)R~=w`3NQO(>p<-CSHr&{jVrQ zX|P$O2e7FTa3k#du4vA4RrCYD8@D_ryXCsqW*TNDG=$P@x}5_085uK}PxeeMh#Ey43< ziclPd_M)#OE#$+cuLB}=7RG17_-RU8SXMTXUiuWOt_--49InVlBLxBd(Vy${7ic?V zoi=$PhND!{YuE~V9?zq)Ev8*U%WyazWZURMz9+m z@2?S!_`Pv`3*+;6J<@t}8CCayW!(LVhgi`iz>(P#n&RV%uttU{JB7YWrr&eLW7%Un zH@^9`=flcF%lcR}-KEJaLr*s{Sw=mXI7CxAuRsztTqiQFeb(`8CS*!MlzqV2!vvBT z=jWI3Q8qjoj#{$hH}GQ%{wP0OIcJ!SF^*$btjHuw-dhR1kkBs-vwm)NKp*O zg?wTD$e$QnIl(`hWd&T_MV(gGY*wy~4y%`?Gu$BVvMKjPs9$joYLr!O&9j6LyKa+nxV0A)Js7io#AK-bbJ|y;DF?1Hukq0B^@Dt|Z>v5XSGGr?Y5Hpkr zG3t`d8rgoJQ55Hj?xR;{oAtbA1<_@1oNip?_2?QQn=)7tWB45O%&Q8~nc zQNuF~?~POdgy!j%_)z^i27kiTpD>+>o1s_W#$O|`WeN5S@-V(`-$eJ>%eM>UGjXIV zF%);P`*b)a9u}Oao3+%vrVy4ow4r?yxV=*W)gH-OzggCgHhFANQbdI>i*R%)QQg@U z6~;Vmg^@^wj|rerCUpAtT;w*sd;KJS5SKZ(c%D0Cu+!?gFEIybU!dCxnSvv})6)j% zk*~+&FTr|Rf1QT6;_Q0;B~JHj0i)$@)UCXFnXXZwT(~$SvD(X&7*-{-nfMoFo|xi6 z>E}$Zb@=(poP{er(OS7x`CS?$fr93A?NkcVktONFYmd!X+HR80usCLuu8&EA{vnkt zj=_9EULPFjO*AlRHyq5lRGwXST#X;GB!1q~mQndXuAW_fn{7Xf1wZ5U`C1w6p!n#% z90BAKoWaQhiX2HasgG%^WU`#V^-~(yiF_5OpvYcNSergV2Q8YG58GC>8s-Xx%*s+# zH7d&oiJax}t~|oUO&;Y1WafHARYz}rX?780wLQZ>+N)u+uD?|n%PMeP;LSSPI1u+M z&@7h`1P0Wyq7D48h@CfnypF;00X?eqOls8xF5zauJ_7V=v*ZUgKf!d2>coIh^J5JyMM~}GHz-iPxJA2 zIQ*<%xWH!vLaF9=hpqk*+j`wSCyPK|wqDn?kG?)x`SLTTaEi{QgWZiH#Ke@jdg6m7 zTy$R$WfD4d;kG>G-gYutcyDj)wCq8j&Ue~|keR$1K7?U#gPLB{2`@W*oFEQLNLOQIw2tNSJ@f1ytnzvg?BXq+|AyN`trkG( z;i8Mkqb}NZ*DOyV{%*6Qf^tG(H8>N2jy(MS8j@b|ZCRKsaLcAvXG zi!%YW^|)xWGI^*caT=SJO;2(#IwA(k&cK2@`7fH7wI0hJUu_D6Gs3)!W2t2(_K3D2MDa&a_YWH zxwA7PE_TKe6`GEkIxK)cI? zuSaiabDnhWcHdAB4FZkH6M<3Blg_4LKJ^{0tZ-=M_&gG?p%ga5CBd^9eZiooH~2aN z9gXq+Mo>qLhT={~CoMEY2f>DqP2ymwteX$6{V@V#7#nBH=~Ga}|aNL=PA zRzMq=_)hOc%9Ux1sK364YY3$U*Z7Ga-^rYV5?Z zKg~;1aAzau{d)M_+RsEYzi$qqfrrRZ*A#I39O^~~4+B!P$<;*9(3d?v_cSKcaO&%` zzWh|3o_&NJo?2Ur{*`{pMh1^oXMyhyDNESKZ2GY{w9<7P%lS!RRvSB% zb37~&X3Iu+2w_n8+Ytlk4fPQ0?IDTL=D4kw_CVyXORS){!-Xe$=-N#oAu-`h;Q5Dl zxZ26+!U%g~*gA0MM$&p1dzqqYkPSlNYWXj<)|-m4Q*aWf3SL4iGu^c?nfdioh0EV^ z)&l_Hl}9)YA;%E}Va|q5tjtQvI%lV1*^7b?N(ussOa*<1lhrDArHPnmV7@#qRBjk` z58k2-_$;7}hH$q{2op+U0%K>B-)XpzKi#(zD?}s`3?ZQjds|7_1R{Zg$b&EYug~W2 z9ZMo+;0Qe7bEV^{r9+Fiyr|dVg{S7a*X2pf=!!zVyy+qd9#Xc|%n=J64BuRW9XO8% zq107+B*3f~y^aQfzlF_Yc(E2O+XEW!(O&{&grDycz|41> zqZ0nv$J~nh@llyScICIcz>)j2@+s%CKg&I1yXz5NQ-4~NUwvWiq<*Sw7MGR>>X9e% z(b^l+^EH^0`LKr$A|?<@3G5)A0xNO`>`2*3^i2%Q;B{peJ~akJYc={6?4Zy&dPwRG z0c9?}(H9gmEYa+SZM?gngjDrH9O_W2(i>iZSls%91MZ^NIkK^d5JKHU>kI<-*&;>> zPY3_U-fI7YqK9yy=d>=td#HaOy|8oIFDbXjT^SEAdsjn*1eVW0L7)}<{8&7LueFpS zcYi|XYH4Rh53;5S0&?yTtd8r-Ay$0FNEbL}eA!e@2Kuf=&DVh^$^|+HvYO@^BUCLP zJb0-zVgbXa-*8yQ@V`n~#Y|w|un>(I7&4!r_HFdVQ5^U_MP4tRUq*d@gsM`LKg~!q zYkhrC1q>T$ekz5sITIB{SvA+RU4BE=ck4$K64p?#QRJbtAXl^qt=!H}4#y`XS2&l7 zR74HI7hIzfKo1WCHMJ>Q4mS;@6%}yLcPL3gS|aERrW2VyafvpDRAN?;?DQtJwxoe+ zqA6n-^7=5#>z*$8bf0e}1e30`dMh3tul;TgzZQAiMx2l>X#DcLGRUx7*!v2k74n-d zK}D9n(0-$eoMe4~i0_O6oX@KUWG#t?qO?L4-b^6jmdpWhW=aAY~YQx=ajm5%20^6Yq}w? zs)KswmrT!HMD&hLbK;N`oy^CpswT=?D;uvasy&NW6%?iA!{rp03n_i-Vov)rV(ku~ zb0l~F!?|KV*@t|F82|OjZs(Qzo#J7!26r>NZ@y|xWAk4~m#=c(VnA;fA}mIP#g_Tk zKQZsTb{tVN*a>UXmRD9U74yvVv38<4M3VJvV-}?7x!c=5Chv~>_SIvDHu_VZ(Qk=3(=I}c4w=H$-JpTEi~%BsnfOPd7!FQVh5Pyhe` literal 0 HcmV?d00001 diff --git a/apps/templates/_foot_js.html b/apps/templates/_foot_js.html index 02c0eb5ac..a27bbf672 100644 --- a/apps/templates/_foot_js.html +++ b/apps/templates/_foot_js.html @@ -10,13 +10,40 @@ +{% if INTERFACE.beian_text %} + + +{% endif %} From ba38852354c85bcd111d3b1fe0318e8ba75a11fd Mon Sep 17 00:00:00 2001 From: ibuler Date: Thu, 16 Nov 2023 11:26:26 +0800 Subject: [PATCH 012/111] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E8=B7=B3?= =?UTF-8?q?=E8=BD=AC=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/authentication/api/sso.py | 3 ++- apps/authentication/backends/oidc/views.py | 6 +++--- apps/authentication/views/dingtalk.py | 3 ++- apps/authentication/views/login.py | 3 ++- apps/authentication/views/wecom.py | 3 ++- apps/common/utils/django.py | 10 ++++++++++ apps/users/utils.py | 3 ++- apps/users/views/profile/password.py | 2 +- 8 files changed, 24 insertions(+), 9 deletions(-) diff --git a/apps/authentication/api/sso.py b/apps/authentication/api/sso.py index 0756bad4e..6e48bda41 100644 --- a/apps/authentication/api/sso.py +++ b/apps/authentication/api/sso.py @@ -14,7 +14,7 @@ from common.api import JMSGenericViewSet from common.const.http import POST, GET from common.permissions import OnlySuperUser from common.serializers import EmptySerializer -from common.utils import reverse +from common.utils import reverse, safe_next_url from common.utils.timezone import utc_now from users.models import User from ..errors import SSOAuthClosed @@ -45,6 +45,7 @@ class SSOViewSet(AuthMixin, JMSGenericViewSet): username = serializer.validated_data['username'] user = User.objects.get(username=username) next_url = serializer.validated_data.get(NEXT_URL) + next_url = safe_next_url(next_url, request=request) operator = request.user.username # TODO `created_by` 和 `created_by` 可以通过 `ThreadLocal` 统一处理 diff --git a/apps/authentication/backends/oidc/views.py b/apps/authentication/backends/oidc/views.py index 98bd2ef2a..56c1e4fb0 100644 --- a/apps/authentication/backends/oidc/views.py +++ b/apps/authentication/backends/oidc/views.py @@ -20,10 +20,11 @@ from django.core.exceptions import SuspiciousOperation from django.http import HttpResponseRedirect, QueryDict from django.urls import reverse from django.utils.crypto import get_random_string -from django.utils.http import url_has_allowed_host_and_scheme, urlencode +from django.utils.http import urlencode from django.views.generic import View from authentication.utils import build_absolute_uri_for_oidc +from common.utils import safe_next_url from .utils import get_logger logger = get_logger(__file__) @@ -100,8 +101,7 @@ class OIDCAuthRequestView(View): # Stores the "next" URL in the session if applicable. logger.debug(log_prompt.format('Stores next url in the session')) next_url = request.GET.get('next') - request.session['oidc_auth_next_url'] = next_url \ - if url_has_allowed_host_and_scheme(url=next_url, allowed_hosts=(request.get_host(),)) else None + request.session['oidc_auth_next_url'] = safe_next_url(next_url, request=request) # Redirects the user to authorization endpoint. logger.debug(log_prompt.format('Construct redirect url')) diff --git a/apps/authentication/views/dingtalk.py b/apps/authentication/views/dingtalk.py index 536ef4155..9b7f81e25 100644 --- a/apps/authentication/views/dingtalk.py +++ b/apps/authentication/views/dingtalk.py @@ -18,7 +18,7 @@ from authentication.permissions import UserConfirmation from common.sdk.im.dingtalk import URL, DingTalk from common.utils import get_logger from common.utils.common import get_request_ip -from common.utils.django import get_object_or_none, reverse +from common.utils.django import get_object_or_none, reverse, safe_next_url from common.utils.random import random_string from common.views.mixins import PermissionsMixin, UserConfirmRequiredExceptionMixin from users.models import User @@ -185,6 +185,7 @@ class DingTalkQRLoginView(DingTalkQRMixin, METAMixin, View): def get(self, request: HttpRequest): redirect_url = request.GET.get('redirect_url') or reverse('index') next_url = self.get_next_url_from_meta() or reverse('index') + next_url = safe_next_url(next_url, request=request) redirect_uri = reverse('authentication:dingtalk-qr-login-callback', external=True) redirect_uri += '?' + urlencode({ diff --git a/apps/authentication/views/login.py b/apps/authentication/views/login.py index 6514e14eb..0b88a76d8 100644 --- a/apps/authentication/views/login.py +++ b/apps/authentication/views/login.py @@ -24,7 +24,7 @@ from django.views.decorators.debug import sensitive_post_parameters from django.views.generic.base import TemplateView, RedirectView from django.views.generic.edit import FormView -from common.utils import FlashMessageUtil, static_or_direct +from common.utils import FlashMessageUtil, static_or_direct, safe_next_url from users.utils import ( redirect_user_first_login_or_index ) @@ -202,6 +202,7 @@ class UserLoginView(mixins.AuthMixin, UserLoginContextMixin, FormView): auth_name, redirect_url = auth_method['name'], auth_method['url'] next_url = request.GET.get('next') or '/' + next_url = safe_next_url(next_url, request=request) query_string = request.GET.urlencode() redirect_url = '{}?next={}&{}'.format(redirect_url, next_url, query_string) diff --git a/apps/authentication/views/wecom.py b/apps/authentication/views/wecom.py index 6817c84a0..2e6d0c1bb 100644 --- a/apps/authentication/views/wecom.py +++ b/apps/authentication/views/wecom.py @@ -19,7 +19,7 @@ from common.sdk.im.wecom import URL from common.sdk.im.wecom import WeCom from common.utils import get_logger from common.utils.common import get_request_ip -from common.utils.django import reverse, get_object_or_none +from common.utils.django import reverse, get_object_or_none, safe_next_url from common.utils.random import random_string from common.views.mixins import UserConfirmRequiredExceptionMixin, PermissionsMixin from users.models import User @@ -182,6 +182,7 @@ class WeComQRLoginView(WeComQRMixin, METAMixin, View): def get(self, request: HttpRequest): redirect_url = request.GET.get('redirect_url') or reverse('index') next_url = self.get_next_url_from_meta() or reverse('index') + next_url = safe_next_url(next_url, request=request) redirect_uri = reverse('authentication:wecom-qr-login-callback', external=True) redirect_uri += '?' + urlencode({ 'redirect_url': redirect_url, diff --git a/apps/common/utils/django.py b/apps/common/utils/django.py index ad8a218d8..fc692bc15 100644 --- a/apps/common/utils/django.py +++ b/apps/common/utils/django.py @@ -7,6 +7,7 @@ from django.db import models from django.db.models.signals import post_save, pre_save from django.shortcuts import reverse as dj_reverse from django.utils import timezone +from django.utils.http import url_has_allowed_host_and_scheme UUID_PATTERN = re.compile(r'[0-9a-zA-Z\-]{36}') @@ -94,3 +95,12 @@ def get_request_os(request): return 'linux' else: return 'unknown' + + +def safe_next_url(next_url, request=None): + safe_hosts = [*settings.ALLOWED_HOSTS] + if request: + safe_hosts.append(request.get_host()) + if not next_url or not url_has_allowed_host_and_scheme(next_url, safe_hosts): + next_url = '/' + return next_url diff --git a/apps/users/utils.py b/apps/users/utils.py index 29ce56cd1..6db4943de 100644 --- a/apps/users/utils.py +++ b/apps/users/utils.py @@ -11,7 +11,7 @@ from django.conf import settings from django.core.cache import cache from common.tasks import send_mail_async -from common.utils import reverse, get_object_or_none, ip +from common.utils import reverse, get_object_or_none, ip, safe_next_url from .models import User logger = logging.getLogger('jumpserver.users') @@ -49,6 +49,7 @@ def redirect_user_first_login_or_index(request, redirect_field_name): url = request.POST.get(redirect_field_name) if not url: url = request.GET.get(redirect_field_name) + url = safe_next_url(url, request=request) # 防止 next 地址为 None if not url or url.lower() in ['none']: url = reverse('index') diff --git a/apps/users/views/profile/password.py b/apps/users/views/profile/password.py index 87d0bf6e7..8f345db93 100644 --- a/apps/users/views/profile/password.py +++ b/apps/users/views/profile/password.py @@ -30,7 +30,7 @@ class UserVerifyPasswordView(AuthMixin, FormView): try: password = form.cleaned_data['password'] except errors.AuthFailedError as e: - form.add_error("password", _(f"Password invalid") + f'({e.msg})') + form.add_error("password", _("Password invalid") + f'({e.msg})') return self.form_invalid(form) user = authenticate(request=self.request, username=user.username, password=password) From 18670d493e3487d34c987fbf3ed7dface0f18048 Mon Sep 17 00:00:00 2001 From: wangruidong <940853815@qq.com> Date: Fri, 24 Nov 2023 17:37:14 +0800 Subject: [PATCH 013/111] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E5=B7=A5?= =?UTF-8?q?=E5=8D=95=E5=A4=84=E7=90=86=E6=8F=90=E7=A4=BA=E6=B6=88=E6=81=AF?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/tickets/templates/tickets/_msg_ticket.html | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/apps/tickets/templates/tickets/_msg_ticket.html b/apps/tickets/templates/tickets/_msg_ticket.html index 75d205080..f26d17e5f 100644 --- a/apps/tickets/templates/tickets/_msg_ticket.html +++ b/apps/tickets/templates/tickets/_msg_ticket.html @@ -1,15 +1,20 @@ {% load i18n %} -
-

+

+

{{ title | safe }}

{% for child in content %} -

{{ child.title }}

-
+

{{ child.title }}

{% for item in child.content %} -
  • - {{ item.title }}: {{ item.value }} +
  • + {{ item.title }} + {{ item.value }}
  • {% endfor %} {% endfor %} From 6d611bbbbd709d050c4e3b2fa18b1c216197e431 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Mon, 27 Nov 2023 11:22:34 +0800 Subject: [PATCH 014/111] =?UTF-8?q?feat:=20=E4=BD=9C=E4=B8=9A=E4=B8=AD?= =?UTF-8?q?=E5=BF=83=E6=95=B0=E6=8D=AE=E5=BA=93=E6=94=AF=E6=8C=81=E7=BD=91?= =?UTF-8?q?=E5=9F=9F=E5=91=BD=E4=BB=A4=E6=89=A7=E8=A1=8C=20(#12117)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: jiangweidong --- apps/assets/automations/base/manager.py | 117 ++++++++++++------------ apps/ops/models/job.py | 12 +++ 2 files changed, 70 insertions(+), 59 deletions(-) diff --git a/apps/assets/automations/base/manager.py b/apps/assets/automations/base/manager.py index 817952d46..4ebed3118 100644 --- a/apps/assets/automations/base/manager.py +++ b/apps/assets/automations/base/manager.py @@ -17,6 +17,61 @@ from ops.ansible import JMSInventory, PlaybookRunner, DefaultCallback logger = get_logger(__name__) +class SSHTunnelManager: + def __init__(self, *args, **kwargs): + self.gateway_servers = dict() + + @staticmethod + def file_to_json(path): + with open(path, 'r') as f: + d = json.load(f) + return d + + @staticmethod + def json_to_file(path, data): + with open(path, 'w') as f: + json.dump(data, f, indent=4, sort_keys=True) + + def local_gateway_prepare(self, runner): + info = self.file_to_json(runner.inventory) + servers, not_valid = [], [] + for k, host in info['all']['hosts'].items(): + jms_asset, jms_gateway = host.get('jms_asset'), host.get('gateway') + if not jms_gateway: + continue + try: + server = SSHTunnelForwarder( + (jms_gateway['address'], jms_gateway['port']), + ssh_username=jms_gateway['username'], + ssh_password=jms_gateway['secret'], + ssh_pkey=jms_gateway['private_key_path'], + remote_bind_address=(jms_asset['address'], jms_asset['port']) + ) + server.start() + except Exception as e: + err_msg = 'Gateway is not active: %s' % jms_asset.get('name', '') + print(f'\033[31m {err_msg} 原因: {e} \033[0m\n') + not_valid.append(k) + else: + host['ansible_host'] = jms_asset['address'] = host['login_host'] = '127.0.0.1' + host['ansible_port'] = jms_asset['port'] = host['login_port'] = server.local_bind_port + servers.append(server) + + # 网域不可连接的,就不继续执行此资源的后续任务了 + for a in set(not_valid): + info['all']['hosts'].pop(a) + self.json_to_file(runner.inventory, info) + self.gateway_servers[runner.id] = servers + + def local_gateway_clean(self, runner): + servers = self.gateway_servers.get(runner.id, []) + for s in servers: + try: + s.stop() + except Exception: + pass + + class PlaybookCallback(DefaultCallback): def playbook_on_stats(self, event_data, **kwargs): super().playbook_on_stats(event_data, **kwargs) @@ -37,7 +92,6 @@ class BasePlaybookManager: # 根据执行方式就行分组, 不同资产的改密、推送等操作可能会使用不同的执行方式 # 然后根据执行方式分组, 再根据 bulk_size 分组, 生成不同的 playbook self.playbooks = [] - self.gateway_servers = dict() params = self.execution.snapshot.get('params') self.params = params or {} @@ -247,66 +301,10 @@ class BasePlaybookManager: def on_runner_failed(self, runner, e): print("Runner failed: {} {}".format(e, self)) - @staticmethod - def file_to_json(path): - with open(path, 'r') as f: - d = json.load(f) - return d - @staticmethod def json_dumps(data): return json.dumps(data, indent=4, sort_keys=True) - @staticmethod - def json_to_file(path, data): - with open(path, 'w') as f: - json.dump(data, f, indent=4, sort_keys=True) - - def local_gateway_prepare(self, runner): - info = self.file_to_json(runner.inventory) - servers, not_valid = [], [] - for k, host in info['all']['hosts'].items(): - jms_asset, jms_gateway = host.get('jms_asset'), host.get('gateway') - if not jms_gateway: - continue - try: - server = SSHTunnelForwarder( - (jms_gateway['address'], jms_gateway['port']), - ssh_username=jms_gateway['username'], - ssh_password=jms_gateway['secret'], - ssh_pkey=jms_gateway['private_key_path'], - remote_bind_address=(jms_asset['address'], jms_asset['port']) - ) - server.start() - except Exception as e: - err_msg = 'Gateway is not active: %s' % jms_asset.get('name', '') - print(f'\033[31m {err_msg} 原因: {e} \033[0m\n') - not_valid.append(k) - else: - host['ansible_host'] = jms_asset['address'] = '127.0.0.1' - host['ansible_port'] = jms_asset['port'] = server.local_bind_port - servers.append(server) - - # 网域不可连接的,就不继续执行此资源的后续任务了 - for a in set(not_valid): - info['all']['hosts'].pop(a) - self.json_to_file(runner.inventory, info) - self.gateway_servers[runner.id] = servers - - def local_gateway_clean(self, runner): - servers = self.gateway_servers.get(runner.id, []) - for s in servers: - try: - s.stop() - except Exception: - pass - - def before_runner_start(self, runner): - self.local_gateway_prepare(runner) - - def after_runner_end(self, runner): - self.local_gateway_clean(runner) - def delete_runtime_dir(self): if settings.DEBUG_DEV: return @@ -326,14 +324,15 @@ class BasePlaybookManager: for i, runner in enumerate(runners, start=1): if len(runners) > 1: print(">>> 开始执行第 {} 批任务".format(i)) - self.before_runner_start(runner) + ssh_tunnel = SSHTunnelManager() + ssh_tunnel.local_gateway_prepare(runner) try: cb = runner.run(**kwargs) self.on_runner_success(runner, cb) except Exception as e: self.on_runner_failed(runner, e) finally: - self.after_runner_end(runner) + ssh_tunnel.local_gateway_clean(runner) print('\n') self.execution.status = 'success' self.execution.date_finished = timezone.now() diff --git a/apps/ops/models/job.py b/apps/ops/models/job.py index 5dea9a73e..d339dbf7f 100644 --- a/apps/ops/models/job.py +++ b/apps/ops/models/job.py @@ -20,6 +20,7 @@ from simple_history.models import HistoricalRecords from accounts.models import Account from acls.models import CommandFilterACL from assets.models import Asset +from assets.automations.base.manager import SSHTunnelManager from common.db.encoder import ModelJSONFieldEncoder from ops.ansible import JMSInventory, AdHocRunner, PlaybookRunner, CommandInBlackListException from ops.mixin import PeriodTaskModelMixin @@ -79,6 +80,13 @@ class JMSPermedInventory(JMSInventory): host['login_password'] = account.secret host['login_db'] = asset.spec_info.get('db_name', '') host['ansible_python_interpreter'] = sys.executable + if gateway: + host['gateway'] = { + 'address': gateway.address, 'port': gateway.port, + 'username': gateway.username, 'secret': gateway.password, + 'private_key_path': gateway.private_key_path + } + host['jms_asset']['port'] = protocol.port return host return super().make_account_vars(host, asset, account, automation, protocol, platform, gateway) @@ -529,6 +537,8 @@ class JobExecution(JMSOrgBaseModel): self.before_start() runner = self.get_runner() + ssh_tunnel = SSHTunnelManager() + ssh_tunnel.local_gateway_prepare(runner) try: cb = runner.run(**kwargs) self.set_result(cb) @@ -539,6 +549,8 @@ class JobExecution(JMSOrgBaseModel): except Exception as e: logging.error(e, exc_info=True) self.set_error(e) + finally: + ssh_tunnel.local_gateway_clean(runner) class Meta: verbose_name = _("Job Execution") From 6b748e5ac549058f6c53e3dda20fd7ac3ceb46c8 Mon Sep 17 00:00:00 2001 From: wangruidong <940853815@qq.com> Date: Mon, 27 Nov 2023 11:27:29 +0800 Subject: [PATCH 015/111] =?UTF-8?q?feat:=20=E7=94=A8=E6=88=B7=E8=AF=A6?= =?UTF-8?q?=E6=83=85=E5=B1=95=E7=A4=BA=E6=89=80=E6=9C=89=E4=BC=9A=E8=AF=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/terminal/api/session/session.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/terminal/api/session/session.py b/apps/terminal/api/session/session.py index c286dee83..fd8bc0ecd 100644 --- a/apps/terminal/api/session/session.py +++ b/apps/terminal/api/session/session.py @@ -62,7 +62,7 @@ class SessionFilterSet(BaseFilterSet): class Meta: model = Session fields = [ - "user", "asset", "account", "remote_addr", + "user", "user_id", "asset", "account", "remote_addr", "protocol", "is_finished", 'login_from', 'terminal' ] From 089cadeae304c20bc45e4f40139ef72cf36cfe56 Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 28 Nov 2023 12:04:03 +0800 Subject: [PATCH 016/111] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=20queryset?= =?UTF-8?q?=20count?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/jumpserver/rewriting/pagination.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/apps/jumpserver/rewriting/pagination.py b/apps/jumpserver/rewriting/pagination.py index a924d854a..cd38fd0ba 100644 --- a/apps/jumpserver/rewriting/pagination.py +++ b/apps/jumpserver/rewriting/pagination.py @@ -1,6 +1,13 @@ from django.conf import settings +from django.core.exceptions import FieldError from rest_framework.pagination import LimitOffsetPagination class MaxLimitOffsetPagination(LimitOffsetPagination): max_limit = settings.MAX_LIMIT_PER_PAGE + + def get_count(self, queryset): + try: + return queryset.values_list('id').order_by().count() + except (AttributeError, TypeError, FieldError): + return len(queryset) From 370ef114860afbacc5d1d9cd4d9f58b2b89510fc Mon Sep 17 00:00:00 2001 From: feng <1304903146@qq.com> Date: Tue, 28 Nov 2023 14:46:51 +0800 Subject: [PATCH 017/111] =?UTF-8?q?perf:=20=E9=9A=8F=E6=9C=BA=E5=AF=86?= =?UTF-8?q?=E7=A0=81=E7=94=9F=E6=88=90=E8=A7=84=E5=88=99=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E5=8F=AF=E6=8E=92=E9=99=A4=E5=AD=97=E7=AC=A6=E9=80=89=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/accounts/serializers/account/template.py | 1 + apps/accounts/utils.py | 3 ++- apps/common/utils/random.py | 13 ++++++++++++- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/apps/accounts/serializers/account/template.py b/apps/accounts/serializers/account/template.py index 635c221bf..f30821313 100644 --- a/apps/accounts/serializers/account/template.py +++ b/apps/accounts/serializers/account/template.py @@ -15,6 +15,7 @@ class PasswordRulesSerializer(serializers.Serializer): uppercase = serializers.BooleanField(default=True, label=_('Uppercase')) digit = serializers.BooleanField(default=True, label=_('Digit')) symbol = serializers.BooleanField(default=True, label=_('Special symbol')) + exclude_symbols = serializers.CharField(default='', max_length=16, label=_('Exclude symbol')) class AccountTemplateSerializer(BaseAccountSerializer): diff --git a/apps/accounts/utils.py b/apps/accounts/utils.py index 229cb0111..b8c9841f2 100644 --- a/apps/accounts/utils.py +++ b/apps/accounts/utils.py @@ -30,7 +30,8 @@ class SecretGenerator: 'lower': rules['lowercase'], 'upper': rules['uppercase'], 'digit': rules['digit'], - 'special_char': rules['symbol'] + 'special_char': rules['symbol'], + 'exclude_chars': rules['exclude_symbols'] } return random_string(**rules) diff --git a/apps/common/utils/random.py b/apps/common/utils/random.py index 505fbd041..7978267b9 100644 --- a/apps/common/utils/random.py +++ b/apps/common/utils/random.py @@ -32,7 +32,16 @@ def random_replace_char(s, chars, length): return ''.join(seq) -def random_string(length: int, lower=True, upper=True, digit=True, special_char=False, symbols=string_punctuation): +def remove_exclude_char(s, exclude_chars): + for i in exclude_chars: + s = s.replace(i, '') + return s + + +def random_string( + length: int, lower=True, upper=True, digit=True, + special_char=False, exclude_chars='', symbols=string_punctuation +): if not any([lower, upper, digit]): raise ValueError('At least one of `lower`, `upper`, `digit` must be `True`') if length < 4: @@ -44,11 +53,13 @@ def random_string(length: int, lower=True, upper=True, digit=True, special_char= (digit, string.digits), ) chars = ''.join([i[1] for i in chars_map if i[0]]) + chars = remove_exclude_char(chars, exclude_chars) texts = list(secrets.choice(chars) for __ in range(length)) texts = ''.join(texts) # 控制一下特殊字符的数量, 别随机出来太多 if special_char: + symbols = remove_exclude_char(symbols, exclude_chars) symbol_num = length // 16 + 1 texts = random_replace_char(texts, symbols, symbol_num) return texts From d2498c0d5372ddd0a019fd01812c06a86316cd92 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Tue, 28 Nov 2023 15:21:40 +0800 Subject: [PATCH 018/111] =?UTF-8?q?fix:=20sftp=E4=B8=8D=E8=83=BD=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE=E4=B8=BA=E9=BB=98=E8=AE=A4=E5=AD=98=E5=82=A8=20(#1221?= =?UTF-8?q?3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: wangruidong <940853815@qq.com> --- apps/terminal/serializers/storage.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/apps/terminal/serializers/storage.py b/apps/terminal/serializers/storage.py index caf887f21..85f52c609 100644 --- a/apps/terminal/serializers/storage.py +++ b/apps/terminal/serializers/storage.py @@ -281,3 +281,10 @@ class ReplayStorageSerializer(BaseStorageSerializer): extra_kwargs = { 'name': {'validators': [UniqueValidator(queryset=ReplayStorage.objects.all())]} } + + def validate_is_default(self, value): + if self.initial_data.get('type') == const.ReplayStorageType.sftp.value: + # sftp不能设置为默认存储 + return False + else: + return value From e2b7f67fdc7a93d923e5b020925a7360c8526cc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B4=E5=B0=8F=E7=99=BD?= <296015668@qq.com> Date: Tue, 28 Nov 2023 16:14:48 +0800 Subject: [PATCH 019/111] build(deps): bump github.com/python-greenlet/greenlet from 2.0.2 to 3.0.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 吴小白 <296015668@qq.com> --- poetry.lock | 1470 ++++++++++++++++++++++++++---------------------- pyproject.toml | 4 +- 2 files changed, 800 insertions(+), 674 deletions(-) diff --git a/poetry.lock b/poetry.lock index 959dcb844..61cf612f4 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. [[package]] name = "adal" @@ -40,111 +40,98 @@ reference = "tsinghua" [[package]] name = "aiohttp" -version = "3.8.5" +version = "3.9.1" description = "Async http client/server framework (asyncio)" optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "aiohttp-3.8.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:a94159871304770da4dd371f4291b20cac04e8c94f11bdea1c3478e557fbe0d8"}, - {file = "aiohttp-3.8.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:13bf85afc99ce6f9ee3567b04501f18f9f8dbbb2ea11ed1a2e079670403a7c84"}, - {file = "aiohttp-3.8.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2ce2ac5708501afc4847221a521f7e4b245abf5178cf5ddae9d5b3856ddb2f3a"}, - {file = "aiohttp-3.8.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96943e5dcc37a6529d18766597c491798b7eb7a61d48878611298afc1fca946c"}, - {file = "aiohttp-3.8.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2ad5c3c4590bb3cc28b4382f031f3783f25ec223557124c68754a2231d989e2b"}, - {file = "aiohttp-3.8.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0c413c633d0512df4dc7fd2373ec06cc6a815b7b6d6c2f208ada7e9e93a5061d"}, - {file = "aiohttp-3.8.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:df72ac063b97837a80d80dec8d54c241af059cc9bb42c4de68bd5b61ceb37caa"}, - {file = "aiohttp-3.8.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c48c5c0271149cfe467c0ff8eb941279fd6e3f65c9a388c984e0e6cf57538e14"}, - {file = "aiohttp-3.8.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:368a42363c4d70ab52c2c6420a57f190ed3dfaca6a1b19afda8165ee16416a82"}, - {file = "aiohttp-3.8.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7607ec3ce4993464368505888af5beb446845a014bc676d349efec0e05085905"}, - {file = "aiohttp-3.8.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:0d21c684808288a98914e5aaf2a7c6a3179d4df11d249799c32d1808e79503b5"}, - {file = "aiohttp-3.8.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:312fcfbacc7880a8da0ae8b6abc6cc7d752e9caa0051a53d217a650b25e9a691"}, - {file = "aiohttp-3.8.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ad093e823df03bb3fd37e7dec9d4670c34f9e24aeace76808fc20a507cace825"}, - {file = "aiohttp-3.8.5-cp310-cp310-win32.whl", hash = "sha256:33279701c04351a2914e1100b62b2a7fdb9a25995c4a104259f9a5ead7ed4802"}, - {file = "aiohttp-3.8.5-cp310-cp310-win_amd64.whl", hash = "sha256:6e4a280e4b975a2e7745573e3fc9c9ba0d1194a3738ce1cbaa80626cc9b4f4df"}, - {file = "aiohttp-3.8.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ae871a964e1987a943d83d6709d20ec6103ca1eaf52f7e0d36ee1b5bebb8b9b9"}, - {file = "aiohttp-3.8.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:461908b2578955045efde733719d62f2b649c404189a09a632d245b445c9c975"}, - {file = "aiohttp-3.8.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:72a860c215e26192379f57cae5ab12b168b75db8271f111019509a1196dfc780"}, - {file = "aiohttp-3.8.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cc14be025665dba6202b6a71cfcdb53210cc498e50068bc088076624471f8bb9"}, - {file = "aiohttp-3.8.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8af740fc2711ad85f1a5c034a435782fbd5b5f8314c9a3ef071424a8158d7f6b"}, - {file = "aiohttp-3.8.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:841cd8233cbd2111a0ef0a522ce016357c5e3aff8a8ce92bcfa14cef890d698f"}, - {file = "aiohttp-3.8.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5ed1c46fb119f1b59304b5ec89f834f07124cd23ae5b74288e364477641060ff"}, - {file = "aiohttp-3.8.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:84f8ae3e09a34f35c18fa57f015cc394bd1389bce02503fb30c394d04ee6b938"}, - {file = "aiohttp-3.8.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:62360cb771707cb70a6fd114b9871d20d7dd2163a0feafe43fd115cfe4fe845e"}, - {file = "aiohttp-3.8.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:23fb25a9f0a1ca1f24c0a371523546366bb642397c94ab45ad3aedf2941cec6a"}, - {file = "aiohttp-3.8.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:b0ba0d15164eae3d878260d4c4df859bbdc6466e9e6689c344a13334f988bb53"}, - {file = "aiohttp-3.8.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:5d20003b635fc6ae3f96d7260281dfaf1894fc3aa24d1888a9b2628e97c241e5"}, - {file = "aiohttp-3.8.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:0175d745d9e85c40dcc51c8f88c74bfbaef9e7afeeeb9d03c37977270303064c"}, - {file = "aiohttp-3.8.5-cp311-cp311-win32.whl", hash = "sha256:2e1b1e51b0774408f091d268648e3d57f7260c1682e7d3a63cb00d22d71bb945"}, - {file = "aiohttp-3.8.5-cp311-cp311-win_amd64.whl", hash = "sha256:043d2299f6dfdc92f0ac5e995dfc56668e1587cea7f9aa9d8a78a1b6554e5755"}, - {file = "aiohttp-3.8.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:cae533195e8122584ec87531d6df000ad07737eaa3c81209e85c928854d2195c"}, - {file = "aiohttp-3.8.5-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f21e83f355643c345177a5d1d8079f9f28b5133bcd154193b799d380331d5d3"}, - {file = "aiohttp-3.8.5-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a7a75ef35f2df54ad55dbf4b73fe1da96f370e51b10c91f08b19603c64004acc"}, - {file = "aiohttp-3.8.5-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2e2e9839e14dd5308ee773c97115f1e0a1cb1d75cbeeee9f33824fa5144c7634"}, - {file = "aiohttp-3.8.5-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c44e65da1de4403d0576473e2344828ef9c4c6244d65cf4b75549bb46d40b8dd"}, - {file = "aiohttp-3.8.5-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:78d847e4cde6ecc19125ccbc9bfac4a7ab37c234dd88fbb3c5c524e8e14da543"}, - {file = "aiohttp-3.8.5-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:c7a815258e5895d8900aec4454f38dca9aed71085f227537208057853f9d13f2"}, - {file = "aiohttp-3.8.5-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:8b929b9bd7cd7c3939f8bcfffa92fae7480bd1aa425279d51a89327d600c704d"}, - {file = "aiohttp-3.8.5-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:5db3a5b833764280ed7618393832e0853e40f3d3e9aa128ac0ba0f8278d08649"}, - {file = "aiohttp-3.8.5-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:a0215ce6041d501f3155dc219712bc41252d0ab76474615b9700d63d4d9292af"}, - {file = "aiohttp-3.8.5-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:fd1ed388ea7fbed22c4968dd64bab0198de60750a25fe8c0c9d4bef5abe13824"}, - {file = "aiohttp-3.8.5-cp36-cp36m-win32.whl", hash = "sha256:6e6783bcc45f397fdebc118d772103d751b54cddf5b60fbcc958382d7dd64f3e"}, - {file = "aiohttp-3.8.5-cp36-cp36m-win_amd64.whl", hash = "sha256:b5411d82cddd212644cf9360879eb5080f0d5f7d809d03262c50dad02f01421a"}, - {file = "aiohttp-3.8.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:01d4c0c874aa4ddfb8098e85d10b5e875a70adc63db91f1ae65a4b04d3344cda"}, - {file = "aiohttp-3.8.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5980a746d547a6ba173fd5ee85ce9077e72d118758db05d229044b469d9029a"}, - {file = "aiohttp-3.8.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2a482e6da906d5e6e653be079b29bc173a48e381600161c9932d89dfae5942ef"}, - {file = "aiohttp-3.8.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80bd372b8d0715c66c974cf57fe363621a02f359f1ec81cba97366948c7fc873"}, - {file = "aiohttp-3.8.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c1161b345c0a444ebcf46bf0a740ba5dcf50612fd3d0528883fdc0eff578006a"}, - {file = "aiohttp-3.8.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cd56db019015b6acfaaf92e1ac40eb8434847d9bf88b4be4efe5bfd260aee692"}, - {file = "aiohttp-3.8.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:153c2549f6c004d2754cc60603d4668899c9895b8a89397444a9c4efa282aaf4"}, - {file = "aiohttp-3.8.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:4a01951fabc4ce26ab791da5f3f24dca6d9a6f24121746eb19756416ff2d881b"}, - {file = "aiohttp-3.8.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bfb9162dcf01f615462b995a516ba03e769de0789de1cadc0f916265c257e5d8"}, - {file = "aiohttp-3.8.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:7dde0009408969a43b04c16cbbe252c4f5ef4574ac226bc8815cd7342d2028b6"}, - {file = "aiohttp-3.8.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:4149d34c32f9638f38f544b3977a4c24052042affa895352d3636fa8bffd030a"}, - {file = "aiohttp-3.8.5-cp37-cp37m-win32.whl", hash = "sha256:68c5a82c8779bdfc6367c967a4a1b2aa52cd3595388bf5961a62158ee8a59e22"}, - {file = "aiohttp-3.8.5-cp37-cp37m-win_amd64.whl", hash = "sha256:2cf57fb50be5f52bda004b8893e63b48530ed9f0d6c96c84620dc92fe3cd9b9d"}, - {file = "aiohttp-3.8.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:eca4bf3734c541dc4f374ad6010a68ff6c6748f00451707f39857f429ca36ced"}, - {file = "aiohttp-3.8.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1274477e4c71ce8cfe6c1ec2f806d57c015ebf84d83373676036e256bc55d690"}, - {file = "aiohttp-3.8.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:28c543e54710d6158fc6f439296c7865b29e0b616629767e685a7185fab4a6b9"}, - {file = "aiohttp-3.8.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:910bec0c49637d213f5d9877105d26e0c4a4de2f8b1b29405ff37e9fc0ad52b8"}, - {file = "aiohttp-3.8.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5443910d662db951b2e58eb70b0fbe6b6e2ae613477129a5805d0b66c54b6cb7"}, - {file = "aiohttp-3.8.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2e460be6978fc24e3df83193dc0cc4de46c9909ed92dd47d349a452ef49325b7"}, - {file = "aiohttp-3.8.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb1558def481d84f03b45888473fc5a1f35747b5f334ef4e7a571bc0dfcb11f8"}, - {file = "aiohttp-3.8.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:34dd0c107799dcbbf7d48b53be761a013c0adf5571bf50c4ecad5643fe9cfcd0"}, - {file = "aiohttp-3.8.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:aa1990247f02a54185dc0dff92a6904521172a22664c863a03ff64c42f9b5410"}, - {file = "aiohttp-3.8.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:0e584a10f204a617d71d359fe383406305a4b595b333721fa50b867b4a0a1548"}, - {file = "aiohttp-3.8.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:a3cf433f127efa43fee6b90ea4c6edf6c4a17109d1d037d1a52abec84d8f2e42"}, - {file = "aiohttp-3.8.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:c11f5b099adafb18e65c2c997d57108b5bbeaa9eeee64a84302c0978b1ec948b"}, - {file = "aiohttp-3.8.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:84de26ddf621d7ac4c975dbea4c945860e08cccde492269db4e1538a6a6f3c35"}, - {file = "aiohttp-3.8.5-cp38-cp38-win32.whl", hash = "sha256:ab88bafedc57dd0aab55fa728ea10c1911f7e4d8b43e1d838a1739f33712921c"}, - {file = "aiohttp-3.8.5-cp38-cp38-win_amd64.whl", hash = "sha256:5798a9aad1879f626589f3df0f8b79b3608a92e9beab10e5fda02c8a2c60db2e"}, - {file = "aiohttp-3.8.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a6ce61195c6a19c785df04e71a4537e29eaa2c50fe745b732aa937c0c77169f3"}, - {file = "aiohttp-3.8.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:773dd01706d4db536335fcfae6ea2440a70ceb03dd3e7378f3e815b03c97ab51"}, - {file = "aiohttp-3.8.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f83a552443a526ea38d064588613aca983d0ee0038801bc93c0c916428310c28"}, - {file = "aiohttp-3.8.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f7372f7341fcc16f57b2caded43e81ddd18df53320b6f9f042acad41f8e049a"}, - {file = "aiohttp-3.8.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ea353162f249c8097ea63c2169dd1aa55de1e8fecbe63412a9bc50816e87b761"}, - {file = "aiohttp-3.8.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e5d47ae48db0b2dcf70bc8a3bc72b3de86e2a590fc299fdbbb15af320d2659de"}, - {file = "aiohttp-3.8.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d827176898a2b0b09694fbd1088c7a31836d1a505c243811c87ae53a3f6273c1"}, - {file = "aiohttp-3.8.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3562b06567c06439d8b447037bb655ef69786c590b1de86c7ab81efe1c9c15d8"}, - {file = "aiohttp-3.8.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:4e874cbf8caf8959d2adf572a78bba17cb0e9d7e51bb83d86a3697b686a0ab4d"}, - {file = "aiohttp-3.8.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:6809a00deaf3810e38c628e9a33271892f815b853605a936e2e9e5129762356c"}, - {file = "aiohttp-3.8.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:33776e945d89b29251b33a7e7d006ce86447b2cfd66db5e5ded4e5cd0340585c"}, - {file = "aiohttp-3.8.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:eaeed7abfb5d64c539e2db173f63631455f1196c37d9d8d873fc316470dfbacd"}, - {file = "aiohttp-3.8.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e91d635961bec2d8f19dfeb41a539eb94bd073f075ca6dae6c8dc0ee89ad6f91"}, - {file = "aiohttp-3.8.5-cp39-cp39-win32.whl", hash = "sha256:00ad4b6f185ec67f3e6562e8a1d2b69660be43070bd0ef6fcec5211154c7df67"}, - {file = "aiohttp-3.8.5-cp39-cp39-win_amd64.whl", hash = "sha256:c0a9034379a37ae42dea7ac1e048352d96286626251862e448933c0f59cbd79c"}, - {file = "aiohttp-3.8.5.tar.gz", hash = "sha256:b9552ec52cc147dbf1944ac7ac98af7602e51ea2dcd076ed194ca3c0d1c7d0bc"}, + {file = "aiohttp-3.9.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e1f80197f8b0b846a8d5cf7b7ec6084493950d0882cc5537fb7b96a69e3c8590"}, + {file = "aiohttp-3.9.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c72444d17777865734aa1a4d167794c34b63e5883abb90356a0364a28904e6c0"}, + {file = "aiohttp-3.9.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9b05d5cbe9dafcdc733262c3a99ccf63d2f7ce02543620d2bd8db4d4f7a22f83"}, + {file = "aiohttp-3.9.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c4fa235d534b3547184831c624c0b7c1e262cd1de847d95085ec94c16fddcd5"}, + {file = "aiohttp-3.9.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:289ba9ae8e88d0ba16062ecf02dd730b34186ea3b1e7489046fc338bdc3361c4"}, + {file = "aiohttp-3.9.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bff7e2811814fa2271be95ab6e84c9436d027a0e59665de60edf44e529a42c1f"}, + {file = "aiohttp-3.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81b77f868814346662c96ab36b875d7814ebf82340d3284a31681085c051320f"}, + {file = "aiohttp-3.9.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3b9c7426923bb7bd66d409da46c41e3fb40f5caf679da624439b9eba92043fa6"}, + {file = "aiohttp-3.9.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:8d44e7bf06b0c0a70a20f9100af9fcfd7f6d9d3913e37754c12d424179b4e48f"}, + {file = "aiohttp-3.9.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:22698f01ff5653fe66d16ffb7658f582a0ac084d7da1323e39fd9eab326a1f26"}, + {file = "aiohttp-3.9.1-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:ca7ca5abfbfe8d39e653870fbe8d7710be7a857f8a8386fc9de1aae2e02ce7e4"}, + {file = "aiohttp-3.9.1-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:8d7f98fde213f74561be1d6d3fa353656197f75d4edfbb3d94c9eb9b0fc47f5d"}, + {file = "aiohttp-3.9.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5216b6082c624b55cfe79af5d538e499cd5f5b976820eac31951fb4325974501"}, + {file = "aiohttp-3.9.1-cp310-cp310-win32.whl", hash = "sha256:0e7ba7ff228c0d9a2cd66194e90f2bca6e0abca810b786901a569c0de082f489"}, + {file = "aiohttp-3.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:c7e939f1ae428a86e4abbb9a7c4732bf4706048818dfd979e5e2839ce0159f23"}, + {file = "aiohttp-3.9.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:df9cf74b9bc03d586fc53ba470828d7b77ce51b0582d1d0b5b2fb673c0baa32d"}, + {file = "aiohttp-3.9.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ecca113f19d5e74048c001934045a2b9368d77b0b17691d905af18bd1c21275e"}, + {file = "aiohttp-3.9.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:8cef8710fb849d97c533f259103f09bac167a008d7131d7b2b0e3a33269185c0"}, + {file = "aiohttp-3.9.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bea94403a21eb94c93386d559bce297381609153e418a3ffc7d6bf772f59cc35"}, + {file = "aiohttp-3.9.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:91c742ca59045dce7ba76cab6e223e41d2c70d79e82c284a96411f8645e2afff"}, + {file = "aiohttp-3.9.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6c93b7c2e52061f0925c3382d5cb8980e40f91c989563d3d32ca280069fd6a87"}, + {file = "aiohttp-3.9.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ee2527134f95e106cc1653e9ac78846f3a2ec1004cf20ef4e02038035a74544d"}, + {file = "aiohttp-3.9.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:11ff168d752cb41e8492817e10fb4f85828f6a0142b9726a30c27c35a1835f01"}, + {file = "aiohttp-3.9.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b8c3a67eb87394386847d188996920f33b01b32155f0a94f36ca0e0c635bf3e3"}, + {file = "aiohttp-3.9.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c7b5d5d64e2a14e35a9240b33b89389e0035e6de8dbb7ffa50d10d8b65c57449"}, + {file = "aiohttp-3.9.1-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:69985d50a2b6f709412d944ffb2e97d0be154ea90600b7a921f95a87d6f108a2"}, + {file = "aiohttp-3.9.1-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:c9110c06eaaac7e1f5562caf481f18ccf8f6fdf4c3323feab28a93d34cc646bd"}, + {file = "aiohttp-3.9.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d737e69d193dac7296365a6dcb73bbbf53bb760ab25a3727716bbd42022e8d7a"}, + {file = "aiohttp-3.9.1-cp311-cp311-win32.whl", hash = "sha256:4ee8caa925aebc1e64e98432d78ea8de67b2272252b0a931d2ac3bd876ad5544"}, + {file = "aiohttp-3.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:a34086c5cc285be878622e0a6ab897a986a6e8bf5b67ecb377015f06ed316587"}, + {file = "aiohttp-3.9.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f800164276eec54e0af5c99feb9494c295118fc10a11b997bbb1348ba1a52065"}, + {file = "aiohttp-3.9.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:500f1c59906cd142d452074f3811614be04819a38ae2b3239a48b82649c08821"}, + {file = "aiohttp-3.9.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0b0a6a36ed7e164c6df1e18ee47afbd1990ce47cb428739d6c99aaabfaf1b3af"}, + {file = "aiohttp-3.9.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69da0f3ed3496808e8cbc5123a866c41c12c15baaaead96d256477edf168eb57"}, + {file = "aiohttp-3.9.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:176df045597e674fa950bf5ae536be85699e04cea68fa3a616cf75e413737eb5"}, + {file = "aiohttp-3.9.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b796b44111f0cab6bbf66214186e44734b5baab949cb5fb56154142a92989aeb"}, + {file = "aiohttp-3.9.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f27fdaadce22f2ef950fc10dcdf8048407c3b42b73779e48a4e76b3c35bca26c"}, + {file = "aiohttp-3.9.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bcb6532b9814ea7c5a6a3299747c49de30e84472fa72821b07f5a9818bce0f66"}, + {file = "aiohttp-3.9.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:54631fb69a6e44b2ba522f7c22a6fb2667a02fd97d636048478db2fd8c4e98fe"}, + {file = "aiohttp-3.9.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4b4c452d0190c5a820d3f5c0f3cd8a28ace48c54053e24da9d6041bf81113183"}, + {file = "aiohttp-3.9.1-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:cae4c0c2ca800c793cae07ef3d40794625471040a87e1ba392039639ad61ab5b"}, + {file = "aiohttp-3.9.1-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:565760d6812b8d78d416c3c7cfdf5362fbe0d0d25b82fed75d0d29e18d7fc30f"}, + {file = "aiohttp-3.9.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:54311eb54f3a0c45efb9ed0d0a8f43d1bc6060d773f6973efd90037a51cd0a3f"}, + {file = "aiohttp-3.9.1-cp312-cp312-win32.whl", hash = "sha256:85c3e3c9cb1d480e0b9a64c658cd66b3cfb8e721636ab8b0e746e2d79a7a9eed"}, + {file = "aiohttp-3.9.1-cp312-cp312-win_amd64.whl", hash = "sha256:11cb254e397a82efb1805d12561e80124928e04e9c4483587ce7390b3866d213"}, + {file = "aiohttp-3.9.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8a22a34bc594d9d24621091d1b91511001a7eea91d6652ea495ce06e27381f70"}, + {file = "aiohttp-3.9.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:598db66eaf2e04aa0c8900a63b0101fdc5e6b8a7ddd805c56d86efb54eb66672"}, + {file = "aiohttp-3.9.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2c9376e2b09895c8ca8b95362283365eb5c03bdc8428ade80a864160605715f1"}, + {file = "aiohttp-3.9.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41473de252e1797c2d2293804e389a6d6986ef37cbb4a25208de537ae32141dd"}, + {file = "aiohttp-3.9.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9c5857612c9813796960c00767645cb5da815af16dafb32d70c72a8390bbf690"}, + {file = "aiohttp-3.9.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ffcd828e37dc219a72c9012ec44ad2e7e3066bec6ff3aaa19e7d435dbf4032ca"}, + {file = "aiohttp-3.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:219a16763dc0294842188ac8a12262b5671817042b35d45e44fd0a697d8c8361"}, + {file = "aiohttp-3.9.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f694dc8a6a3112059258a725a4ebe9acac5fe62f11c77ac4dcf896edfa78ca28"}, + {file = "aiohttp-3.9.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:bcc0ea8d5b74a41b621ad4a13d96c36079c81628ccc0b30cfb1603e3dfa3a014"}, + {file = "aiohttp-3.9.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:90ec72d231169b4b8d6085be13023ece8fa9b1bb495e4398d847e25218e0f431"}, + {file = "aiohttp-3.9.1-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:cf2a0ac0615842b849f40c4d7f304986a242f1e68286dbf3bd7a835e4f83acfd"}, + {file = "aiohttp-3.9.1-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:0e49b08eafa4f5707ecfb321ab9592717a319e37938e301d462f79b4e860c32a"}, + {file = "aiohttp-3.9.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2c59e0076ea31c08553e868cec02d22191c086f00b44610f8ab7363a11a5d9d8"}, + {file = "aiohttp-3.9.1-cp38-cp38-win32.whl", hash = "sha256:4831df72b053b1eed31eb00a2e1aff6896fb4485301d4ccb208cac264b648db4"}, + {file = "aiohttp-3.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:3135713c5562731ee18f58d3ad1bf41e1d8883eb68b363f2ffde5b2ea4b84cc7"}, + {file = "aiohttp-3.9.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:cfeadf42840c1e870dc2042a232a8748e75a36b52d78968cda6736de55582766"}, + {file = "aiohttp-3.9.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:70907533db712f7aa791effb38efa96f044ce3d4e850e2d7691abd759f4f0ae0"}, + {file = "aiohttp-3.9.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cdefe289681507187e375a5064c7599f52c40343a8701761c802c1853a504558"}, + {file = "aiohttp-3.9.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d7481f581251bb5558ba9f635db70908819caa221fc79ee52a7f58392778c636"}, + {file = "aiohttp-3.9.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:49f0c1b3c2842556e5de35f122fc0f0b721334ceb6e78c3719693364d4af8499"}, + {file = "aiohttp-3.9.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0d406b01a9f5a7e232d1b0d161b40c05275ffbcbd772dc18c1d5a570961a1ca4"}, + {file = "aiohttp-3.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d8e4450e7fe24d86e86b23cc209e0023177b6d59502e33807b732d2deb6975f"}, + {file = "aiohttp-3.9.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3c0266cd6f005e99f3f51e583012de2778e65af6b73860038b968a0a8888487a"}, + {file = "aiohttp-3.9.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab221850108a4a063c5b8a70f00dd7a1975e5a1713f87f4ab26a46e5feac5a0e"}, + {file = "aiohttp-3.9.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c88a15f272a0ad3d7773cf3a37cc7b7d077cbfc8e331675cf1346e849d97a4e5"}, + {file = "aiohttp-3.9.1-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:237533179d9747080bcaad4d02083ce295c0d2eab3e9e8ce103411a4312991a0"}, + {file = "aiohttp-3.9.1-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:02ab6006ec3c3463b528374c4cdce86434e7b89ad355e7bf29e2f16b46c7dd6f"}, + {file = "aiohttp-3.9.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04fa38875e53eb7e354ece1607b1d2fdee2d175ea4e4d745f6ec9f751fe20c7c"}, + {file = "aiohttp-3.9.1-cp39-cp39-win32.whl", hash = "sha256:82eefaf1a996060602f3cc1112d93ba8b201dbf5d8fd9611227de2003dddb3b7"}, + {file = "aiohttp-3.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:9b05d33ff8e6b269e30a7957bd3244ffbce2a7a35a81b81c382629b80af1a8bf"}, + {file = "aiohttp-3.9.1.tar.gz", hash = "sha256:8fc49a87ac269d4529da45871e2ffb6874e87779c3d0e2ccd813c0899221239d"}, ] [package.dependencies] aiosignal = ">=1.1.2" -async-timeout = ">=4.0.0a3,<5.0" attrs = ">=17.3.0" -charset-normalizer = ">=2.0,<4.0" frozenlist = ">=1.1.1" multidict = ">=4.5,<7.0" yarl = ">=1.0,<2.0" [package.extras] -speedups = ["Brotli", "aiodns", "cchardet"] +speedups = ["Brotli", "aiodns", "brotlicffi"] [package.source] type = "legacy" @@ -248,12 +235,12 @@ reference = "tsinghua" [[package]] name = "alibabacloud-openapi-util" -version = "0.2.1" +version = "0.2.2" description = "Aliyun Tea OpenApi Library for Python" optional = false python-versions = "*" files = [ - {file = "alibabacloud_openapi_util-0.2.1.tar.gz", hash = "sha256:5de2158a6e894b3364d9627090afdb6de4f6b92d1adf3a00e2c69206df30de15"}, + {file = "alibabacloud_openapi_util-0.2.2.tar.gz", hash = "sha256:ebbc3906f554cb4bf8f513e43e8a33e8b6a3d4a0ef13617a0e14c3dda8ef52a8"}, ] [package.dependencies] @@ -287,19 +274,19 @@ reference = "tsinghua" [[package]] name = "alibabacloud-tea-openapi" -version = "0.3.7" +version = "0.3.8" description = "Alibaba Cloud openapi SDK Library for Python" optional = false python-versions = ">=3.6" files = [ - {file = "alibabacloud_tea_openapi-0.3.7.tar.gz", hash = "sha256:bf677f3ddd35eeb499f08c0a38a024379c47dbcc0ba7a777a2ef7562abbe9c9f"}, + {file = "alibabacloud_tea_openapi-0.3.8.tar.gz", hash = "sha256:99796fa807433ee31e4e06239901d5dbc9cd22884f50f9bbb09b1dae9dbbb723"}, ] [package.dependencies] alibabacloud_credentials = ">=0.3.1,<1.0.0" alibabacloud_gateway_spi = ">=0.0.1,<1.0.0" alibabacloud_openapi_util = ">=0.2.1,<1.0.0" -alibabacloud_tea_util = ">=0.3.8,<1.0.0" +alibabacloud_tea_util = ">=0.3.11,<1.0.0" alibabacloud_tea_xml = ">=0.0.2,<1.0.0" [package.source] @@ -345,12 +332,12 @@ reference = "tsinghua" [[package]] name = "aliyun-python-sdk-core" -version = "2.13.36" +version = "2.14.0" description = "The core module of Aliyun Python SDK." optional = false python-versions = "*" files = [ - {file = "aliyun-python-sdk-core-2.13.36.tar.gz", hash = "sha256:20bd54984fa316da700c7f355a51ab0b816690e2a0fcefb7b5ef013fed0da928"}, + {file = "aliyun-python-sdk-core-2.14.0.tar.gz", hash = "sha256:c806815a48ffdb894cc5bce15b8259b9a3012cc0cda01be2f3dfbb844f3f4f21"}, ] [package.dependencies] @@ -402,13 +389,13 @@ reference = "tsinghua" [[package]] name = "aliyun-python-sdk-kms" -version = "2.16.1" +version = "2.16.2" description = "The kms module of Aliyun Python sdk." optional = false python-versions = "*" files = [ - {file = "aliyun-python-sdk-kms-2.16.1.tar.gz", hash = "sha256:a372737715682014bace68bd40fe83332f4fd925009a3eb110d41bc66f270e7a"}, - {file = "aliyun_python_sdk_kms-2.16.1-py2.py3-none-any.whl", hash = "sha256:9bc39c693ba83944f5dfb871b118a2925eb8a5ee214dfcce61ee2ea3b6317ef1"}, + {file = "aliyun-python-sdk-kms-2.16.2.tar.gz", hash = "sha256:f87234a8b64d457ca2338f87650db18a3ce7f7dbc9bfef71efe8f2894aded3d6"}, + {file = "aliyun_python_sdk_kms-2.16.2-py2.py3-none-any.whl", hash = "sha256:83166468817a4fbc4c958af43ec22856e1bd80f1363f56acf822206febe6b059"}, ] [package.dependencies] @@ -554,20 +541,21 @@ reference = "tsinghua" [[package]] name = "asttokens" -version = "2.2.1" +version = "2.4.1" description = "Annotate AST trees with source code positions" optional = false python-versions = "*" files = [ - {file = "asttokens-2.2.1-py2.py3-none-any.whl", hash = "sha256:6b0ac9e93fb0335014d382b8fa9b3afa7df546984258005da0b9e7095b3deb1c"}, - {file = "asttokens-2.2.1.tar.gz", hash = "sha256:4622110b2a6f30b77e1473affaa97e711bc2f07d3f10848420ff1898edbe94f3"}, + {file = "asttokens-2.4.1-py2.py3-none-any.whl", hash = "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24"}, + {file = "asttokens-2.4.1.tar.gz", hash = "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0"}, ] [package.dependencies] -six = "*" +six = ">=1.12.0" [package.extras] -test = ["astroid", "pytest"] +astroid = ["astroid (>=1,<2)", "astroid (>=2,<4)"] +test = ["astroid (>=1,<2)", "astroid (>=2,<4)", "pytest"] [package.source] type = "legacy" @@ -687,13 +675,13 @@ reference = "tsinghua" [[package]] name = "azure-core" -version = "1.29.2" +version = "1.29.5" description = "Microsoft Azure Core Library for Python" optional = false python-versions = ">=3.7" files = [ - {file = "azure-core-1.29.2.zip", hash = "sha256:beb0fe88d1043d8457318e8fb841d9caa648211092eda213c16b376401f3710d"}, - {file = "azure_core-1.29.2-py3-none-any.whl", hash = "sha256:8e6602f322dc1070caf7e17754beb53b69ffa09df0f4786009a3107e9a00c793"}, + {file = "azure-core-1.29.5.tar.gz", hash = "sha256:52983c89d394c6f881a121e5101c5fa67278ca3b1f339c8fb2ef39230c70e9ac"}, + {file = "azure_core-1.29.5-py3-none-any.whl", hash = "sha256:0fa04b7b1f7d44a4fb8468c4093deb2ea01fdf4faddbf802ed9205615f99d68c"}, ] [package.dependencies] @@ -1020,13 +1008,13 @@ reference = "tsinghua" [[package]] name = "cachetools" -version = "5.3.1" +version = "5.3.2" description = "Extensible memoizing collections and decorators" optional = false python-versions = ">=3.7" files = [ - {file = "cachetools-5.3.1-py3-none-any.whl", hash = "sha256:95ef631eeaea14ba2e36f06437f36463aac3a096799e876ee55e5cdccb102590"}, - {file = "cachetools-5.3.1.tar.gz", hash = "sha256:dce83f2d9b4e1f732a8cd44af8e8fab2dbe46201467fc98b3ef8f269092bf62b"}, + {file = "cachetools-5.3.2-py3-none-any.whl", hash = "sha256:861f35a13a451f94e301ce2bec7cac63e881232ccce7ed67fab9b5df4d3beaa1"}, + {file = "cachetools-5.3.2.tar.gz", hash = "sha256:086ee420196f7b2ab9ca2db2520aca326318b68fe5ba8bc4d49cca91add450f2"}, ] [package.source] @@ -1259,86 +1247,101 @@ reference = "tsinghua" [[package]] name = "charset-normalizer" -version = "3.2.0" +version = "3.3.2" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7.0" files = [ - {file = "charset-normalizer-3.2.0.tar.gz", hash = "sha256:3bb3d25a8e6c0aedd251753a79ae98a093c7e7b471faa3aa9a93a81431987ace"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b87549028f680ca955556e3bd57013ab47474c3124dc069faa0b6545b6c9710"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7c70087bfee18a42b4040bb9ec1ca15a08242cf5867c58726530bdf3945672ed"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a103b3a7069b62f5d4890ae1b8f0597618f628b286b03d4bc9195230b154bfa9"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94aea8eff76ee6d1cdacb07dd2123a68283cb5569e0250feab1240058f53b623"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:db901e2ac34c931d73054d9797383d0f8009991e723dab15109740a63e7f902a"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b0dac0ff919ba34d4df1b6131f59ce95b08b9065233446be7e459f95554c0dc8"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:193cbc708ea3aca45e7221ae58f0fd63f933753a9bfb498a3b474878f12caaad"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09393e1b2a9461950b1c9a45d5fd251dc7c6f228acab64da1c9c0165d9c7765c"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:baacc6aee0b2ef6f3d308e197b5d7a81c0e70b06beae1f1fcacffdbd124fe0e3"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bf420121d4c8dce6b889f0e8e4ec0ca34b7f40186203f06a946fa0276ba54029"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:c04a46716adde8d927adb9457bbe39cf473e1e2c2f5d0a16ceb837e5d841ad4f"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:aaf63899c94de41fe3cf934601b0f7ccb6b428c6e4eeb80da72c58eab077b19a"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d62e51710986674142526ab9f78663ca2b0726066ae26b78b22e0f5e571238dd"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-win32.whl", hash = "sha256:04e57ab9fbf9607b77f7d057974694b4f6b142da9ed4a199859d9d4d5c63fe96"}, - {file = "charset_normalizer-3.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:48021783bdf96e3d6de03a6e39a1171ed5bd7e8bb93fc84cc649d11490f87cea"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4957669ef390f0e6719db3613ab3a7631e68424604a7b448f079bee145da6e09"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46fb8c61d794b78ec7134a715a3e564aafc8f6b5e338417cb19fe9f57a5a9bf2"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f779d3ad205f108d14e99bb3859aa7dd8e9c68874617c72354d7ecaec2a054ac"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f25c229a6ba38a35ae6e25ca1264621cc25d4d38dca2942a7fce0b67a4efe918"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2efb1bd13885392adfda4614c33d3b68dee4921fd0ac1d3988f8cbb7d589e72a"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f30b48dd7fa1474554b0b0f3fdfdd4c13b5c737a3c6284d3cdc424ec0ffff3a"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:246de67b99b6851627d945db38147d1b209a899311b1305dd84916f2b88526c6"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd9b3b31adcb054116447ea22caa61a285d92e94d710aa5ec97992ff5eb7cf3"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8c2f5e83493748286002f9369f3e6607c565a6a90425a3a1fef5ae32a36d749d"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3170c9399da12c9dc66366e9d14da8bf7147e1e9d9ea566067bbce7bb74bd9c2"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7a4826ad2bd6b07ca615c74ab91f32f6c96d08f6fcc3902ceeedaec8cdc3bcd6"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:3b1613dd5aee995ec6d4c69f00378bbd07614702a315a2cf6c1d21461fe17c23"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9e608aafdb55eb9f255034709e20d5a83b6d60c054df0802fa9c9883d0a937aa"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-win32.whl", hash = "sha256:f2a1d0fd4242bd8643ce6f98927cf9c04540af6efa92323e9d3124f57727bfc1"}, - {file = "charset_normalizer-3.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:681eb3d7e02e3c3655d1b16059fbfb605ac464c834a0c629048a30fad2b27489"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c57921cda3a80d0f2b8aec7e25c8aa14479ea92b5b51b6876d975d925a2ea346"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41b25eaa7d15909cf3ac4c96088c1f266a9a93ec44f87f1d13d4a0e86c81b982"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f058f6963fd82eb143c692cecdc89e075fa0828db2e5b291070485390b2f1c9c"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7647ebdfb9682b7bb97e2a5e7cb6ae735b1c25008a70b906aecca294ee96cf4"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eef9df1eefada2c09a5e7a40991b9fc6ac6ef20b1372abd48d2794a316dc0449"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e03b8895a6990c9ab2cdcd0f2fe44088ca1c65ae592b8f795c3294af00a461c3"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:ee4006268ed33370957f55bf2e6f4d263eaf4dc3cfc473d1d90baff6ed36ce4a"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c4983bf937209c57240cff65906b18bb35e64ae872da6a0db937d7b4af845dd7"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:3bb7fda7260735efe66d5107fb7e6af6a7c04c7fce9b2514e04b7a74b06bf5dd"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:72814c01533f51d68702802d74f77ea026b5ec52793c791e2da806a3844a46c3"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:70c610f6cbe4b9fce272c407dd9d07e33e6bf7b4aa1b7ffb6f6ded8e634e3592"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-win32.whl", hash = "sha256:a401b4598e5d3f4a9a811f3daf42ee2291790c7f9d74b18d75d6e21dda98a1a1"}, - {file = "charset_normalizer-3.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:c0b21078a4b56965e2b12f247467b234734491897e99c1d51cee628da9786959"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:95eb302ff792e12aba9a8b8f8474ab229a83c103d74a750ec0bd1c1eea32e669"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1a100c6d595a7f316f1b6f01d20815d916e75ff98c27a01ae817439ea7726329"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6339d047dab2780cc6220f46306628e04d9750f02f983ddb37439ca47ced7149"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4b749b9cc6ee664a3300bb3a273c1ca8068c46be705b6c31cf5d276f8628a94"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a38856a971c602f98472050165cea2cdc97709240373041b69030be15047691f"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f87f746ee241d30d6ed93969de31e5ffd09a2961a051e60ae6bddde9ec3583aa"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89f1b185a01fe560bc8ae5f619e924407efca2191b56ce749ec84982fc59a32a"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e1c8a2f4c69e08e89632defbfabec2feb8a8d99edc9f89ce33c4b9e36ab63037"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2f4ac36d8e2b4cc1aa71df3dd84ff8efbe3bfb97ac41242fbcfc053c67434f46"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a386ebe437176aab38c041de1260cd3ea459c6ce5263594399880bbc398225b2"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:ccd16eb18a849fd8dcb23e23380e2f0a354e8daa0c984b8a732d9cfaba3a776d"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:e6a5bf2cba5ae1bb80b154ed68a3cfa2fa00fde979a7f50d6598d3e17d9ac20c"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:45de3f87179c1823e6d9e32156fb14c1927fcc9aba21433f088fdfb555b77c10"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-win32.whl", hash = "sha256:1000fba1057b92a65daec275aec30586c3de2401ccdcd41f8a5c1e2c87078706"}, - {file = "charset_normalizer-3.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:8b2c760cfc7042b27ebdb4a43a4453bd829a5742503599144d54a032c5dc7e9e"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:855eafa5d5a2034b4621c74925d89c5efef61418570e5ef9b37717d9c796419c"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:203f0c8871d5a7987be20c72442488a0b8cfd0f43b7973771640fc593f56321f"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e857a2232ba53ae940d3456f7533ce6ca98b81917d47adc3c7fd55dad8fab858"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e86d77b090dbddbe78867a0275cb4df08ea195e660f1f7f13435a4649e954e5"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fb39a81950ec280984b3a44f5bd12819953dc5fa3a7e6fa7a80db5ee853952"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dee8e57f052ef5353cf608e0b4c871aee320dd1b87d351c28764fc0ca55f9f4"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8700f06d0ce6f128de3ccdbc1acaea1ee264d2caa9ca05daaf492fde7c2a7200"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1920d4ff15ce893210c1f0c0e9d19bfbecb7983c76b33f046c13a8ffbd570252"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c1c76a1743432b4b60ab3358c937a3fe1341c828ae6194108a94c69028247f22"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f7560358a6811e52e9c4d142d497f1a6e10103d3a6881f18d04dbce3729c0e2c"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:c8063cf17b19661471ecbdb3df1c84f24ad2e389e326ccaf89e3fb2484d8dd7e"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:cd6dbe0238f7743d0efe563ab46294f54f9bc8f4b9bcf57c3c666cc5bc9d1299"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1249cbbf3d3b04902ff081ffbb33ce3377fa6e4c7356f759f3cd076cc138d020"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-win32.whl", hash = "sha256:6c409c0deba34f147f77efaa67b8e4bb83d2f11c8806405f76397ae5b8c0d1c9"}, - {file = "charset_normalizer-3.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:7095f6fbfaa55defb6b733cfeb14efaae7a29f0b59d8cf213be4e7ca0b857b80"}, - {file = "charset_normalizer-3.2.0-py3-none-any.whl", hash = "sha256:8e098148dd37b4ce3baca71fb394c81dc5d9c7728c95df695d2dca218edf40e6"}, + {file = "charset-normalizer-3.3.2.tar.gz", hash = "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win32.whl", hash = "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73"}, + {file = "charset_normalizer-3.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win32.whl", hash = "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab"}, + {file = "charset_normalizer-3.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win32.whl", hash = "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7"}, + {file = "charset_normalizer-3.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win32.whl", hash = "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4"}, + {file = "charset_normalizer-3.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win32.whl", hash = "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25"}, + {file = "charset_normalizer-3.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win32.whl", hash = "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f"}, + {file = "charset_normalizer-3.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d"}, + {file = "charset_normalizer-3.3.2-py3-none-any.whl", hash = "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc"}, ] [package.source] @@ -1467,13 +1470,13 @@ reference = "tsinghua" [[package]] name = "constantly" -version = "15.1.0" +version = "23.10.4" description = "Symbolic constants in Python" optional = false -python-versions = "*" +python-versions = ">=3.8" files = [ - {file = "constantly-15.1.0-py2.py3-none-any.whl", hash = "sha256:dd2fa9d6b1a51a83f0d7dd76293d734046aa176e384bf6e33b7e44880eb37c5d"}, - {file = "constantly-15.1.0.tar.gz", hash = "sha256:586372eb92059873e29eba4f9dec8381541b4d3834660707faf8ba59146dfc35"}, + {file = "constantly-23.10.4-py3-none-any.whl", hash = "sha256:3fd9b4d1c3dc1ec9757f3c52aef7e53ad9323dbe39f51dfd4c43853b68dfa3f9"}, + {file = "constantly-23.10.4.tar.gz", hash = "sha256:aa92b70a33e2ac0bb33cd745eb61776594dc48764b06c35e0efd050b7f1c7cbd"}, ] [package.source] @@ -1820,7 +1823,7 @@ reference = "tsinghua" [[package]] name = "django-cas-ng" version = "4.3.0" -description = "" +description = "Django CAS 1.0/2.0/3.0 client authentication library, support Django 2.2, 3.0, 3.1, 3.2, 4.0 and Python 3.7+" optional = false python-versions = ">=3.7" files = [ @@ -2329,6 +2332,17 @@ files = [ {file = "ephem-4.1.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8f9b27117e7a82f7f70db9cb23b5cc36d37b166a2f73c55e14d7225d0ab95afa"}, {file = "ephem-4.1.4-cp311-cp311-win32.whl", hash = "sha256:9bb21c0b117c9122c0141b0a71ee6fbbb087ed2aab4a7ab60f009e95e9f4a521"}, {file = "ephem-4.1.4-cp311-cp311-win_amd64.whl", hash = "sha256:55d7fb5c34b2e453e01fa4ca7ee375b19b438c9401ae8c4099ae4a3a37656972"}, + {file = "ephem-4.1.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f9e24aeea560dfcece3c2e313eb94e6be3e84888091455e541fa88f3a44da584"}, + {file = "ephem-4.1.4-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:653d99386932e5f78bb9cfc4495030ad9f3345eb4c2b32dca55547da8f1f0332"}, + {file = "ephem-4.1.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53786461a6d5799d5fffe76622ad51444b264d1c7263b92a6dfcac640c3da93a"}, + {file = "ephem-4.1.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:268f57f8768ccb0abbdf4cefb4781c7db812950019868f687b407b428513ee53"}, + {file = "ephem-4.1.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d630aa287255ea9fba6962f351e4e0729bb620570684d52fbfcc31b11527f09e"}, + {file = "ephem-4.1.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b5f229bbf62ecb4cd6bb3374b15d0f8ff7b3d970c2936fccd89bdf9d693907a2"}, + {file = "ephem-4.1.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:d60d56f182de54bd84fadd6ea2dd8e8ef6fdef6a698c7cafd404ecb6eeefa598"}, + {file = "ephem-4.1.4-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:404500c8d0030d75ec15bb6b98eee78ad163fd5252102c962ae6fb39c9488198"}, + {file = "ephem-4.1.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9fb020d6cc5ab1ad1cd9d3da4a6e2506beebb41d1b337d79cc20cc0a17f550f1"}, + {file = "ephem-4.1.4-cp312-cp312-win32.whl", hash = "sha256:29e71636ee4719419d03184abc85085f76989c79a61844f5e60acbf2513d2b42"}, + {file = "ephem-4.1.4-cp312-cp312-win_amd64.whl", hash = "sha256:549654f63d88e0ab6248ae25ac2939131474ab9f3a91bee6b68ca6f214747c2a"}, {file = "ephem-4.1.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:40067fc050c946c8d4c2d779805b61f063471a091e6124cbabcf61ac538011b2"}, {file = "ephem-4.1.4-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e2abe97aa2b091090012768b4d94793213cc01f0bf040dcc311a380ab08df69"}, {file = "ephem-4.1.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b2677d3a5b42aedc578de10b0eecdba6a50731f159cb28f7ad38c5f62143494"}, @@ -2435,17 +2449,17 @@ reference = "tsinghua" [[package]] name = "executing" -version = "1.2.0" +version = "2.0.1" description = "Get the currently executing AST node of a frame, and other information" optional = false -python-versions = "*" +python-versions = ">=3.5" files = [ - {file = "executing-1.2.0-py2.py3-none-any.whl", hash = "sha256:0314a69e37426e3608aada02473b4161d4caf5a4b244d1d0c48072b8fee7bacc"}, - {file = "executing-1.2.0.tar.gz", hash = "sha256:19da64c18d2d851112f09c287f8d3dbbdf725ab0e569077efb6cdcbd3497c107"}, + {file = "executing-2.0.1-py2.py3-none-any.whl", hash = "sha256:eac49ca94516ccc753f9fb5ce82603156e590b27525a8bc32cce8ae302eb61bc"}, + {file = "executing-2.0.1.tar.gz", hash = "sha256:35afe2ce3affba8ee97f2d69927fa823b08b472b7b994e36a52a964b93d16147"}, ] [package.extras] -tests = ["asttokens", "littleutils", "pytest", "rich"] +tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich"] [package.source] type = "legacy" @@ -2643,26 +2657,20 @@ reference = "tsinghua" [[package]] name = "google-api-core" -version = "2.11.1" +version = "2.14.0" description = "Google API client core library" optional = false python-versions = ">=3.7" files = [ - {file = "google-api-core-2.11.1.tar.gz", hash = "sha256:25d29e05a0058ed5f19c61c0a78b1b53adea4d9364b464d014fbda941f6d1c9a"}, - {file = "google_api_core-2.11.1-py3-none-any.whl", hash = "sha256:d92a5a92dc36dd4f4b9ee4e55528a90e432b059f93aee6ad857f9de8cc7ae94a"}, + {file = "google-api-core-2.14.0.tar.gz", hash = "sha256:5368a4502b793d9bbf812a5912e13e4e69f9bd87f6efb508460c43f5bbd1ce41"}, + {file = "google_api_core-2.14.0-py3-none-any.whl", hash = "sha256:de2fb50ed34d47ddbb2bd2dcf680ee8fead46279f4ed6b16de362aca23a18952"}, ] [package.dependencies] google-auth = ">=2.14.1,<3.0.dev0" googleapis-common-protos = ">=1.56.2,<2.0.dev0" -grpcio = [ - {version = ">=1.33.2,<2.0dev", optional = true, markers = "extra == \"grpc\""}, - {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}, -] -grpcio-status = [ - {version = ">=1.33.2,<2.0.dev0", optional = true, markers = "extra == \"grpc\""}, - {version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}, -] +grpcio = {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} +grpcio-status = {version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" requests = ">=2.18.0,<3.0.0.dev0" @@ -2678,21 +2686,19 @@ reference = "tsinghua" [[package]] name = "google-auth" -version = "2.22.0" +version = "2.23.4" description = "Google Authentication Library" optional = false -python-versions = ">=3.6" +python-versions = ">=3.7" files = [ - {file = "google-auth-2.22.0.tar.gz", hash = "sha256:164cba9af4e6e4e40c3a4f90a1a6c12ee56f14c0b4868d1ca91b32826ab334ce"}, - {file = "google_auth-2.22.0-py2.py3-none-any.whl", hash = "sha256:d61d1b40897407b574da67da1a833bdc10d5a11642566e506565d1b1a46ba873"}, + {file = "google-auth-2.23.4.tar.gz", hash = "sha256:79905d6b1652187def79d491d6e23d0cbb3a21d3c7ba0dbaa9c8a01906b13ff3"}, + {file = "google_auth-2.23.4-py2.py3-none-any.whl", hash = "sha256:d4bbc92fe4b8bfd2f3e8d88e5ba7085935da208ee38a134fc280e7ce682a05f2"}, ] [package.dependencies] cachetools = ">=2.0.0,<6.0" pyasn1-modules = ">=0.2.1" rsa = ">=3.1.4,<5" -six = ">=1.9.0" -urllib3 = "<2.0" [package.extras] aiohttp = ["aiohttp (>=3.6.2,<4.0.0.dev0)", "requests (>=2.20.0,<3.0.0.dev0)"] @@ -2729,13 +2735,13 @@ reference = "tsinghua" [[package]] name = "googleapis-common-protos" -version = "1.60.0" +version = "1.61.0" description = "Common protobufs used in Google APIs" optional = false python-versions = ">=3.7" files = [ - {file = "googleapis-common-protos-1.60.0.tar.gz", hash = "sha256:e73ebb404098db405ba95d1e1ae0aa91c3e15a71da031a2eeb6b2e23e7bc3708"}, - {file = "googleapis_common_protos-1.60.0-py2.py3-none-any.whl", hash = "sha256:69f9bbcc6acde92cab2db95ce30a70bd2b81d20b12eff3f1aabaffcbe8a93918"}, + {file = "googleapis-common-protos-1.61.0.tar.gz", hash = "sha256:8a64866a97f6304a7179873a465d6eee97b7a24ec6cfd78e0f575e96b821240b"}, + {file = "googleapis_common_protos-1.61.0-py2.py3-none-any.whl", hash = "sha256:22f1915393bb3245343f6efe87f6fe868532efc12aa26b391b15132e1279f1c0"}, ] [package.dependencies] @@ -2751,75 +2757,72 @@ reference = "tsinghua" [[package]] name = "greenlet" -version = "2.0.2" +version = "3.0.1" description = "Lightweight in-process concurrent programming" optional = false -python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*" +python-versions = ">=3.7" files = [ - {file = "greenlet-2.0.2-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:bdfea8c661e80d3c1c99ad7c3ff74e6e87184895bbaca6ee8cc61209f8b9b85d"}, - {file = "greenlet-2.0.2-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:9d14b83fab60d5e8abe587d51c75b252bcc21683f24699ada8fb275d7712f5a9"}, - {file = "greenlet-2.0.2-cp27-cp27m-win32.whl", hash = "sha256:6c3acb79b0bfd4fe733dff8bc62695283b57949ebcca05ae5c129eb606ff2d74"}, - {file = "greenlet-2.0.2-cp27-cp27m-win_amd64.whl", hash = "sha256:283737e0da3f08bd637b5ad058507e578dd462db259f7f6e4c5c365ba4ee9343"}, - {file = "greenlet-2.0.2-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:d27ec7509b9c18b6d73f2f5ede2622441de812e7b1a80bbd446cb0633bd3d5ae"}, - {file = "greenlet-2.0.2-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:30bcf80dda7f15ac77ba5af2b961bdd9dbc77fd4ac6105cee85b0d0a5fcf74df"}, - {file = "greenlet-2.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26fbfce90728d82bc9e6c38ea4d038cba20b7faf8a0ca53a9c07b67318d46088"}, - {file = "greenlet-2.0.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9190f09060ea4debddd24665d6804b995a9c122ef5917ab26e1566dcc712ceeb"}, - {file = "greenlet-2.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d75209eed723105f9596807495d58d10b3470fa6732dd6756595e89925ce2470"}, - {file = "greenlet-2.0.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3a51c9751078733d88e013587b108f1b7a1fb106d402fb390740f002b6f6551a"}, - {file = "greenlet-2.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:76ae285c8104046b3a7f06b42f29c7b73f77683df18c49ab5af7983994c2dd91"}, - {file = "greenlet-2.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:2d4686f195e32d36b4d7cf2d166857dbd0ee9f3d20ae349b6bf8afc8485b3645"}, - {file = "greenlet-2.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c4302695ad8027363e96311df24ee28978162cdcdd2006476c43970b384a244c"}, - {file = "greenlet-2.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c48f54ef8e05f04d6eff74b8233f6063cb1ed960243eacc474ee73a2ea8573ca"}, - {file = "greenlet-2.0.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a1846f1b999e78e13837c93c778dcfc3365902cfb8d1bdb7dd73ead37059f0d0"}, - {file = "greenlet-2.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a06ad5312349fec0ab944664b01d26f8d1f05009566339ac6f63f56589bc1a2"}, - {file = "greenlet-2.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:eff4eb9b7eb3e4d0cae3d28c283dc16d9bed6b193c2e1ace3ed86ce48ea8df19"}, - {file = "greenlet-2.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5454276c07d27a740c5892f4907c86327b632127dd9abec42ee62e12427ff7e3"}, - {file = "greenlet-2.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:7cafd1208fdbe93b67c7086876f061f660cfddc44f404279c1585bbf3cdc64c5"}, - {file = "greenlet-2.0.2-cp35-cp35m-macosx_10_14_x86_64.whl", hash = "sha256:910841381caba4f744a44bf81bfd573c94e10b3045ee00de0cbf436fe50673a6"}, - {file = "greenlet-2.0.2-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:18a7f18b82b52ee85322d7a7874e676f34ab319b9f8cce5de06067384aa8ff43"}, - {file = "greenlet-2.0.2-cp35-cp35m-win32.whl", hash = "sha256:03a8f4f3430c3b3ff8d10a2a86028c660355ab637cee9333d63d66b56f09d52a"}, - {file = "greenlet-2.0.2-cp35-cp35m-win_amd64.whl", hash = "sha256:4b58adb399c4d61d912c4c331984d60eb66565175cdf4a34792cd9600f21b394"}, - {file = "greenlet-2.0.2-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:703f18f3fda276b9a916f0934d2fb6d989bf0b4fb5a64825260eb9bfd52d78f0"}, - {file = "greenlet-2.0.2-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:32e5b64b148966d9cccc2c8d35a671409e45f195864560829f395a54226408d3"}, - {file = "greenlet-2.0.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2dd11f291565a81d71dab10b7033395b7a3a5456e637cf997a6f33ebdf06f8db"}, - {file = "greenlet-2.0.2-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e0f72c9ddb8cd28532185f54cc1453f2c16fb417a08b53a855c4e6a418edd099"}, - {file = "greenlet-2.0.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cd021c754b162c0fb55ad5d6b9d960db667faad0fa2ff25bb6e1301b0b6e6a75"}, - {file = "greenlet-2.0.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:3c9b12575734155d0c09d6c3e10dbd81665d5c18e1a7c6597df72fd05990c8cf"}, - {file = "greenlet-2.0.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:b9ec052b06a0524f0e35bd8790686a1da006bd911dd1ef7d50b77bfbad74e292"}, - {file = "greenlet-2.0.2-cp36-cp36m-win32.whl", hash = "sha256:dbfcfc0218093a19c252ca8eb9aee3d29cfdcb586df21049b9d777fd32c14fd9"}, - {file = "greenlet-2.0.2-cp36-cp36m-win_amd64.whl", hash = "sha256:9f35ec95538f50292f6d8f2c9c9f8a3c6540bbfec21c9e5b4b751e0a7c20864f"}, - {file = "greenlet-2.0.2-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:d5508f0b173e6aa47273bdc0a0b5ba055b59662ba7c7ee5119528f466585526b"}, - {file = "greenlet-2.0.2-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:f82d4d717d8ef19188687aa32b8363e96062911e63ba22a0cff7802a8e58e5f1"}, - {file = "greenlet-2.0.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9c59a2120b55788e800d82dfa99b9e156ff8f2227f07c5e3012a45a399620b7"}, - {file = "greenlet-2.0.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2780572ec463d44c1d3ae850239508dbeb9fed38e294c68d19a24d925d9223ca"}, - {file = "greenlet-2.0.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:937e9020b514ceedb9c830c55d5c9872abc90f4b5862f89c0887033ae33c6f73"}, - {file = "greenlet-2.0.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:36abbf031e1c0f79dd5d596bfaf8e921c41df2bdf54ee1eed921ce1f52999a86"}, - {file = "greenlet-2.0.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:18e98fb3de7dba1c0a852731c3070cf022d14f0d68b4c87a19cc1016f3bb8b33"}, - {file = "greenlet-2.0.2-cp37-cp37m-win32.whl", hash = "sha256:3f6ea9bd35eb450837a3d80e77b517ea5bc56b4647f5502cd28de13675ee12f7"}, - {file = "greenlet-2.0.2-cp37-cp37m-win_amd64.whl", hash = "sha256:7492e2b7bd7c9b9916388d9df23fa49d9b88ac0640db0a5b4ecc2b653bf451e3"}, - {file = "greenlet-2.0.2-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:b864ba53912b6c3ab6bcb2beb19f19edd01a6bfcbdfe1f37ddd1778abfe75a30"}, - {file = "greenlet-2.0.2-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:ba2956617f1c42598a308a84c6cf021a90ff3862eddafd20c3333d50f0edb45b"}, - {file = "greenlet-2.0.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc3a569657468b6f3fb60587e48356fe512c1754ca05a564f11366ac9e306526"}, - {file = "greenlet-2.0.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8eab883b3b2a38cc1e050819ef06a7e6344d4a990d24d45bc6f2cf959045a45b"}, - {file = "greenlet-2.0.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acd2162a36d3de67ee896c43effcd5ee3de247eb00354db411feb025aa319857"}, - {file = "greenlet-2.0.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:0bf60faf0bc2468089bdc5edd10555bab6e85152191df713e2ab1fcc86382b5a"}, - {file = "greenlet-2.0.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b0ef99cdbe2b682b9ccbb964743a6aca37905fda5e0452e5ee239b1654d37f2a"}, - {file = "greenlet-2.0.2-cp38-cp38-win32.whl", hash = "sha256:b80f600eddddce72320dbbc8e3784d16bd3fb7b517e82476d8da921f27d4b249"}, - {file = "greenlet-2.0.2-cp38-cp38-win_amd64.whl", hash = "sha256:4d2e11331fc0c02b6e84b0d28ece3a36e0548ee1a1ce9ddde03752d9b79bba40"}, - {file = "greenlet-2.0.2-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:88d9ab96491d38a5ab7c56dd7a3cc37d83336ecc564e4e8816dbed12e5aaefc8"}, - {file = "greenlet-2.0.2-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:561091a7be172ab497a3527602d467e2b3fbe75f9e783d8b8ce403fa414f71a6"}, - {file = "greenlet-2.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:971ce5e14dc5e73715755d0ca2975ac88cfdaefcaab078a284fea6cfabf866df"}, - {file = "greenlet-2.0.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:be4ed120b52ae4d974aa40215fcdfde9194d63541c7ded40ee12eb4dda57b76b"}, - {file = "greenlet-2.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:94c817e84245513926588caf1152e3b559ff794d505555211ca041f032abbb6b"}, - {file = "greenlet-2.0.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1a819eef4b0e0b96bb0d98d797bef17dc1b4a10e8d7446be32d1da33e095dbb8"}, - {file = "greenlet-2.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7efde645ca1cc441d6dc4b48c0f7101e8d86b54c8530141b09fd31cef5149ec9"}, - {file = "greenlet-2.0.2-cp39-cp39-win32.whl", hash = "sha256:ea9872c80c132f4663822dd2a08d404073a5a9b5ba6155bea72fb2a79d1093b5"}, - {file = "greenlet-2.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:db1a39669102a1d8d12b57de2bb7e2ec9066a6f2b3da35ae511ff93b01b5d564"}, - {file = "greenlet-2.0.2.tar.gz", hash = "sha256:e7c8dc13af7db097bed64a051d2dd49e9f0af495c26995c00a9ee842690d34c0"}, + {file = "greenlet-3.0.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f89e21afe925fcfa655965ca8ea10f24773a1791400989ff32f467badfe4a064"}, + {file = "greenlet-3.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28e89e232c7593d33cac35425b58950789962011cc274aa43ef8865f2e11f46d"}, + {file = "greenlet-3.0.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8ba29306c5de7717b5761b9ea74f9c72b9e2b834e24aa984da99cbfc70157fd"}, + {file = "greenlet-3.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:19bbdf1cce0346ef7341705d71e2ecf6f41a35c311137f29b8a2dc2341374565"}, + {file = "greenlet-3.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:599daf06ea59bfedbec564b1692b0166a0045f32b6f0933b0dd4df59a854caf2"}, + {file = "greenlet-3.0.1-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b641161c302efbb860ae6b081f406839a8b7d5573f20a455539823802c655f63"}, + {file = "greenlet-3.0.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d57e20ba591727da0c230ab2c3f200ac9d6d333860d85348816e1dca4cc4792e"}, + {file = "greenlet-3.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5805e71e5b570d490938d55552f5a9e10f477c19400c38bf1d5190d760691846"}, + {file = "greenlet-3.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:52e93b28db27ae7d208748f45d2db8a7b6a380e0d703f099c949d0f0d80b70e9"}, + {file = "greenlet-3.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:f7bfb769f7efa0eefcd039dd19d843a4fbfbac52f1878b1da2ed5793ec9b1a65"}, + {file = "greenlet-3.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91e6c7db42638dc45cf2e13c73be16bf83179f7859b07cfc139518941320be96"}, + {file = "greenlet-3.0.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1757936efea16e3f03db20efd0cd50a1c86b06734f9f7338a90c4ba85ec2ad5a"}, + {file = "greenlet-3.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:19075157a10055759066854a973b3d1325d964d498a805bb68a1f9af4aaef8ec"}, + {file = "greenlet-3.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9d21aaa84557d64209af04ff48e0ad5e28c5cca67ce43444e939579d085da72"}, + {file = "greenlet-3.0.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2847e5d7beedb8d614186962c3d774d40d3374d580d2cbdab7f184580a39d234"}, + {file = "greenlet-3.0.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:97e7ac860d64e2dcba5c5944cfc8fa9ea185cd84061c623536154d5a89237884"}, + {file = "greenlet-3.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b2c02d2ad98116e914d4f3155ffc905fd0c025d901ead3f6ed07385e19122c94"}, + {file = "greenlet-3.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:22f79120a24aeeae2b4471c711dcf4f8c736a2bb2fabad2a67ac9a55ea72523c"}, + {file = "greenlet-3.0.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:100f78a29707ca1525ea47388cec8a049405147719f47ebf3895e7509c6446aa"}, + {file = "greenlet-3.0.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:60d5772e8195f4e9ebf74046a9121bbb90090f6550f81d8956a05387ba139353"}, + {file = "greenlet-3.0.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:daa7197b43c707462f06d2c693ffdbb5991cbb8b80b5b984007de431493a319c"}, + {file = "greenlet-3.0.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ea6b8aa9e08eea388c5f7a276fabb1d4b6b9d6e4ceb12cc477c3d352001768a9"}, + {file = "greenlet-3.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d11ebbd679e927593978aa44c10fc2092bc454b7d13fdc958d3e9d508aba7d0"}, + {file = "greenlet-3.0.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dbd4c177afb8a8d9ba348d925b0b67246147af806f0b104af4d24f144d461cd5"}, + {file = "greenlet-3.0.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:20107edf7c2c3644c67c12205dc60b1bb11d26b2610b276f97d666110d1b511d"}, + {file = "greenlet-3.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8bef097455dea90ffe855286926ae02d8faa335ed8e4067326257cb571fc1445"}, + {file = "greenlet-3.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:b2d3337dcfaa99698aa2377c81c9ca72fcd89c07e7eb62ece3f23a3fe89b2ce4"}, + {file = "greenlet-3.0.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:80ac992f25d10aaebe1ee15df45ca0d7571d0f70b645c08ec68733fb7a020206"}, + {file = "greenlet-3.0.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:337322096d92808f76ad26061a8f5fccb22b0809bea39212cd6c406f6a7060d2"}, + {file = "greenlet-3.0.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b9934adbd0f6e476f0ecff3c94626529f344f57b38c9a541f87098710b18af0a"}, + {file = "greenlet-3.0.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc4d815b794fd8868c4d67602692c21bf5293a75e4b607bb92a11e821e2b859a"}, + {file = "greenlet-3.0.1-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41bdeeb552d814bcd7fb52172b304898a35818107cc8778b5101423c9017b3de"}, + {file = "greenlet-3.0.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:6e6061bf1e9565c29002e3c601cf68569c450be7fc3f7336671af7ddb4657166"}, + {file = "greenlet-3.0.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:fa24255ae3c0ab67e613556375a4341af04a084bd58764731972bcbc8baeba36"}, + {file = "greenlet-3.0.1-cp37-cp37m-win32.whl", hash = "sha256:b489c36d1327868d207002391f662a1d163bdc8daf10ab2e5f6e41b9b96de3b1"}, + {file = "greenlet-3.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:f33f3258aae89da191c6ebaa3bc517c6c4cbc9b9f689e5d8452f7aedbb913fa8"}, + {file = "greenlet-3.0.1-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:d2905ce1df400360463c772b55d8e2518d0e488a87cdea13dd2c71dcb2a1fa16"}, + {file = "greenlet-3.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a02d259510b3630f330c86557331a3b0e0c79dac3d166e449a39363beaae174"}, + {file = "greenlet-3.0.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:55d62807f1c5a1682075c62436702aaba941daa316e9161e4b6ccebbbf38bda3"}, + {file = "greenlet-3.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3fcc780ae8edbb1d050d920ab44790201f027d59fdbd21362340a85c79066a74"}, + {file = "greenlet-3.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4eddd98afc726f8aee1948858aed9e6feeb1758889dfd869072d4465973f6bfd"}, + {file = "greenlet-3.0.1-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:eabe7090db68c981fca689299c2d116400b553f4b713266b130cfc9e2aa9c5a9"}, + {file = "greenlet-3.0.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f2f6d303f3dee132b322a14cd8765287b8f86cdc10d2cb6a6fae234ea488888e"}, + {file = "greenlet-3.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d923ff276f1c1f9680d32832f8d6c040fe9306cbfb5d161b0911e9634be9ef0a"}, + {file = "greenlet-3.0.1-cp38-cp38-win32.whl", hash = "sha256:0b6f9f8ca7093fd4433472fd99b5650f8a26dcd8ba410e14094c1e44cd3ceddd"}, + {file = "greenlet-3.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:990066bff27c4fcf3b69382b86f4c99b3652bab2a7e685d968cd4d0cfc6f67c6"}, + {file = "greenlet-3.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:ce85c43ae54845272f6f9cd8320d034d7a946e9773c693b27d620edec825e376"}, + {file = "greenlet-3.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89ee2e967bd7ff85d84a2de09df10e021c9b38c7d91dead95b406ed6350c6997"}, + {file = "greenlet-3.0.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:87c8ceb0cf8a5a51b8008b643844b7f4a8264a2c13fcbcd8a8316161725383fe"}, + {file = "greenlet-3.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d6a8c9d4f8692917a3dc7eb25a6fb337bff86909febe2f793ec1928cd97bedfc"}, + {file = "greenlet-3.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fbc5b8f3dfe24784cee8ce0be3da2d8a79e46a276593db6868382d9c50d97b1"}, + {file = "greenlet-3.0.1-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85d2b77e7c9382f004b41d9c72c85537fac834fb141b0296942d52bf03fe4a3d"}, + {file = "greenlet-3.0.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:696d8e7d82398e810f2b3622b24e87906763b6ebfd90e361e88eb85b0e554dc8"}, + {file = "greenlet-3.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:329c5a2e5a0ee942f2992c5e3ff40be03e75f745f48847f118a3cfece7a28546"}, + {file = "greenlet-3.0.1-cp39-cp39-win32.whl", hash = "sha256:cf868e08690cb89360eebc73ba4be7fb461cfbc6168dd88e2fbbe6f31812cd57"}, + {file = "greenlet-3.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:ac4a39d1abae48184d420aa8e5e63efd1b75c8444dd95daa3e03f6c6310e9619"}, + {file = "greenlet-3.0.1.tar.gz", hash = "sha256:816bd9488a94cba78d93e1abb58000e8266fa9cc2aa9ccdd6eb0696acb24005b"}, ] [package.extras] -docs = ["Sphinx", "docutils (<0.18)"] +docs = ["Sphinx"] test = ["objgraph", "psutil"] [package.source] @@ -3033,13 +3036,13 @@ reference = "tsinghua" [[package]] name = "humanize" -version = "4.8.0" +version = "4.9.0" description = "Python humanize utilities" optional = false python-versions = ">=3.8" files = [ - {file = "humanize-4.8.0-py3-none-any.whl", hash = "sha256:8bc9e2bb9315e61ec06bf690151ae35aeb65651ab091266941edf97c90836404"}, - {file = "humanize-4.8.0.tar.gz", hash = "sha256:9783373bf1eec713a770ecaa7c2d7a7902c98398009dfa3d8a2df91eec9311e8"}, + {file = "humanize-4.9.0-py3-none-any.whl", hash = "sha256:ce284a76d5b1377fd8836733b983bfb0b76f1aa1c090de2566fcf008d7f6ab16"}, + {file = "humanize-4.9.0.tar.gz", hash = "sha256:582a265c931c683a7e9b8ed9559089dea7edcf6cc95be39a3cbc2c5d5ac2bcfa"}, ] [package.extras] @@ -3216,13 +3219,13 @@ reference = "tsinghua" [[package]] name = "iso8601" -version = "2.0.0" +version = "2.1.0" description = "Simple module to parse ISO 8601 dates" optional = false python-versions = ">=3.7,<4.0" files = [ - {file = "iso8601-2.0.0-py3-none-any.whl", hash = "sha256:ebe10061b932edb8a8e33cc635d661926c59b9c3bed7a4f4edca8c62d400af10"}, - {file = "iso8601-2.0.0.tar.gz", hash = "sha256:739960d37c74c77bd9bd546a76562ccb581fe3d4820ff5c3141eb49c839fda8f"}, + {file = "iso8601-2.1.0-py3-none-any.whl", hash = "sha256:aac4145c4dcb66ad8b648a02830f5e2ff6c24af20f4f482689be402db2429242"}, + {file = "iso8601-2.1.0.tar.gz", hash = "sha256:6b1d3829ee8921c4301998c909f7829fa9ed3cbdac0d3b16af2d743aed1ba8df"}, ] [package.source] @@ -3283,13 +3286,13 @@ reference = "tsinghua" [[package]] name = "jedi" -version = "0.19.0" +version = "0.19.1" description = "An autocompletion tool for Python that can be used for text editors." optional = false python-versions = ">=3.6" files = [ - {file = "jedi-0.19.0-py2.py3-none-any.whl", hash = "sha256:cb8ce23fbccff0025e9386b5cf85e892f94c9b822378f8da49970471335ac64e"}, - {file = "jedi-0.19.0.tar.gz", hash = "sha256:bcf9894f1753969cbac8022a8c2eaee06bfa3724e4192470aaffe7eb6272b0c4"}, + {file = "jedi-0.19.1-py2.py3-none-any.whl", hash = "sha256:e983c654fe5c02867aef4cdfce5a2fbb4a50adc0af145f70504238f18ef5e7e0"}, + {file = "jedi-0.19.1.tar.gz", hash = "sha256:cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd"}, ] [package.dependencies] @@ -3298,7 +3301,7 @@ parso = ">=0.8.3,<0.9.0" [package.extras] docs = ["Jinja2 (==2.11.3)", "MarkupSafe (==1.1.1)", "Pygments (==2.8.1)", "alabaster (==0.7.12)", "babel (==2.9.1)", "chardet (==4.0.0)", "commonmark (==0.8.1)", "docutils (==0.17.1)", "future (==0.18.2)", "idna (==2.10)", "imagesize (==1.2.0)", "mock (==1.0.1)", "packaging (==20.9)", "pyparsing (==2.4.7)", "pytz (==2021.1)", "readthedocs-sphinx-ext (==2.1.4)", "recommonmark (==0.5.0)", "requests (==2.25.1)", "six (==1.15.0)", "snowballstemmer (==2.1.0)", "sphinx (==1.8.5)", "sphinx-rtd-theme (==0.4.3)", "sphinxcontrib-serializinghtml (==1.1.4)", "sphinxcontrib-websupport (==1.2.4)", "urllib3 (==1.26.4)"] qa = ["flake8 (==5.0.4)", "mypy (==0.971)", "types-setuptools (==67.2.0.1)"] -testing = ["Django (<3.1)", "attrs", "colorama", "docopt", "pytest (<7.0.0)"] +testing = ["Django", "attrs", "colorama", "docopt", "pytest (<7.0.0)"] [package.source] type = "legacy" @@ -3373,6 +3376,11 @@ requests = "2.31.0" s3transfer = "0.6.1" six = "1.16.0" +[package.source] +type = "legacy" +url = "https://pypi.tuna.tsinghua.edu.cn/simple" +reference = "tsinghua" + [[package]] name = "jsonfield2" version = "4.0.0.post0" @@ -3680,6 +3688,16 @@ files = [ {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, @@ -3738,14 +3756,64 @@ reference = "tsinghua" [[package]] name = "maxminddb" -version = "2.4.0" +version = "2.5.1" description = "Reader for the MaxMind DB format" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "maxminddb-2.4.0.tar.gz", hash = "sha256:81e54e53408bd502650e5969ccba16780af659ec1db1c44b2c997e4330a5ed96"}, + {file = "maxminddb-2.5.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:62e93a8e99937bf4307eeece3ca37e1161325ebf9363c4ce195410fb5daf64a0"}, + {file = "maxminddb-2.5.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea2e27a507b53dfbf2ba2ba85c98682a1ad2dac3f9941a7bffa5cb86150d0c47"}, + {file = "maxminddb-2.5.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a01b0341bd6bee431bb8c07c7ac0ed221250c7390b125c025b7d57578e78e8a3"}, + {file = "maxminddb-2.5.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:607344b1079ea647629bf962dcea7580ec864faaad3f5aae650e2e8652121d89"}, + {file = "maxminddb-2.5.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:2c2901daebd7c8a702302315e7a58cdc38e626406ad4a05b4d48634897d5f5a3"}, + {file = "maxminddb-2.5.1-cp310-cp310-win32.whl", hash = "sha256:7805ae8c9de433c38939ada2e376706a9f6740239f61fd445927b88f5b42c267"}, + {file = "maxminddb-2.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:f1e5bd58b71f322dc6c16a95a129433b1bc229d4b714f870a61c2367425396ee"}, + {file = "maxminddb-2.5.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b0bbbd58b300aaddf985f763720bdebba9f7a73168ff9f57168117f630ad1c06"}, + {file = "maxminddb-2.5.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a6751e2e89d62d53217870bcc2a8c887dc56ae370ba1b74e52e880761916e54"}, + {file = "maxminddb-2.5.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2ecb1be961f1969be047d07743093f0dcf2f6d4ec3a06a4555587f380a96f6e7"}, + {file = "maxminddb-2.5.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1e091c2b44673c218ee2df23adbc0b6d04fd5c646cfcb6c6fe26fb849434812a"}, + {file = "maxminddb-2.5.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e09b295c401c104ae0e30f66c1a3f3c2aa4ba2cbe12a787576499356a5a4d6c1"}, + {file = "maxminddb-2.5.1-cp311-cp311-win32.whl", hash = "sha256:3d52c693baf07bba897d109b0ecb067f21fd0cc0fb266d67db456e85b80d699e"}, + {file = "maxminddb-2.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:4c67621e842c415ce336ab019a9f087305dfcf24c095b68b8e9d27848f6f6d91"}, + {file = "maxminddb-2.5.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:17ea454f61631b9815d420d48d00663f8718fc7de30be53ffcec0f73989475eb"}, + {file = "maxminddb-2.5.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef4d508c899ce0f37de731340759c68bfd1102a39a873675c71fae2c8d71ad97"}, + {file = "maxminddb-2.5.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c4e5ca423b1e310f0327536f5ed1a2c6e08d83289a7f909e021590b0b477cae2"}, + {file = "maxminddb-2.5.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:0a21abd85e10e5e0f60244b49c3db17e7e48befd4972e62a62833d91e2acbb49"}, + {file = "maxminddb-2.5.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:85a302d79577efe5bc308647394ffdc535dd5f062644c41103604ccf24931a05"}, + {file = "maxminddb-2.5.1-cp312-cp312-win32.whl", hash = "sha256:dd28c434fb44f825dde6a75df2c338d44645791b03480af66a4d993f93801e10"}, + {file = "maxminddb-2.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:b477852cf1741d9187b021e23723e64b063794bbf946a9b5b84cc222f3caf58a"}, + {file = "maxminddb-2.5.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a1e1a19f9740f586362f47862d0095b54d50b9d465babcaa8a563746132fe5be"}, + {file = "maxminddb-2.5.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d654895b546a47e85f2e071b98e377a60bb03cd643b9423017fa66fcd5adedce"}, + {file = "maxminddb-2.5.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0702da59b9670a72761b65cb1a52bc3032d8f6799bdab641cb8350ad5740580b"}, + {file = "maxminddb-2.5.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:2e20a70c1545d6626dcd4ce2d7ecf3d566d978ea64cb37e7952f93baff66b812"}, + {file = "maxminddb-2.5.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0cbd272db3202e948c9088e48dec62add071a47971d84ceb11d2cb2880f83e5a"}, + {file = "maxminddb-2.5.1-cp38-cp38-win32.whl", hash = "sha256:fbd01fc7d7b5b2befe914e8cdb5ed3a1c5476e57b765197cceff8d897f33d012"}, + {file = "maxminddb-2.5.1-cp38-cp38-win_amd64.whl", hash = "sha256:fe0af3ba9e1a78ed5f2ad32fc18d18b78ef233e7d0c627e1a77a525a7eb0c241"}, + {file = "maxminddb-2.5.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5d772be68cce812f7c4b15ae8c68e624c8b88ff83071e3903ca5b5f55e343c25"}, + {file = "maxminddb-2.5.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:910e7b3ad87d5352ed3f496bd42bffbf9f896245278b0d8e76afa1382e42a7ae"}, + {file = "maxminddb-2.5.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:892c11a8694394e97d3ac0f8d5974ea588c732d14e721f22095c58b4f584c144"}, + {file = "maxminddb-2.5.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:3ce1f42bdfce7b86cb5a56cba730fed611fb879d867e6024f0d520257bef6891"}, + {file = "maxminddb-2.5.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6667948e7501a513caef90edda2d367865097239d4c2381eb3998e9905af7209"}, + {file = "maxminddb-2.5.1-cp39-cp39-win32.whl", hash = "sha256:500d321bdefe4dcd351e4390a79b7786aab49b0536bedfa0788e5ffb0e91e421"}, + {file = "maxminddb-2.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:93f7055779caf7753810f1e2c6444af6d727393fd116ffa0767fbd54fb8c9bbf"}, + {file = "maxminddb-2.5.1-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:8cee4315da7cdd3f2a18f1ab1418953a7a9eda65e63095b01f03c7d3645d633e"}, + {file = "maxminddb-2.5.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c97eac5af102cede4b5f57cecb25e8f949fa4e4a8d812bed575539951c60ecaf"}, + {file = "maxminddb-2.5.1-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:526744b12075051fa20979090c111cc3a42a3b55e2714818270c7b84a41a8cfe"}, + {file = "maxminddb-2.5.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:fad45cd2f2e3c5fbebacb8d172a60fb22443222e549bf740a0bc7eeb849e5ce7"}, + {file = "maxminddb-2.5.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:8b98ed5c34955c48e72d35daed713ba4a6833a8a6d1204e79d2c85e644049792"}, + {file = "maxminddb-2.5.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:639aee8abd63a95baa12b94b6f3a842d51877d631879c7d08c98c68dc44a84c3"}, + {file = "maxminddb-2.5.1-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a7a73ab4bbc16b81983531c99fa102a0c7dae459db958c17fea48c981f5e764"}, + {file = "maxminddb-2.5.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:aae262da1940a67c3ba765c49e2308947ce68ff647f87630002c306433a98ca1"}, + {file = "maxminddb-2.5.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b223c53077a736c304b63cf5afceb928975fbd12ddae5afd6b71370bab7b4700"}, + {file = "maxminddb-2.5.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:969d0057ea5472e0b574c5293c4f3ecf49585362351c543e8ea55dc48b60f1eb"}, + {file = "maxminddb-2.5.1-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d4d36cf3d390f02d2bdf53d9efefb92be7bd70e07a5a86cdb79020c48c2d81b7"}, + {file = "maxminddb-2.5.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:188173c07dce0692fd5660a6eb7ea8c126d7b3a4b61496c8a8ee9e8b10186ff5"}, + {file = "maxminddb-2.5.1.tar.gz", hash = "sha256:4807d374e645bd68334e4f487ba85a27189dbc1267a98e644aa686a7927e0559"}, ] +[package.dependencies] +setuptools = ">=68.2.2" + [package.source] type = "legacy" url = "https://pypi.tuna.tsinghua.edu.cn/simple" @@ -3753,13 +3821,13 @@ reference = "tsinghua" [[package]] name = "msal" -version = "1.23.0" -description = "The Microsoft Authentication Library (MSAL) for Python library enables your app to access the Microsoft Cloud by supporting authentication of users with Microsoft Azure Active Directory accounts (AAD) and Microsoft Accounts (MSA) using industry standard OAuth2 and OpenID Connect." +version = "1.25.0" +description = "The Microsoft Authentication Library (MSAL) for Python library" optional = false -python-versions = "*" +python-versions = ">=2.7" files = [ - {file = "msal-1.23.0-py2.py3-none-any.whl", hash = "sha256:3342e0837a047007f9d479e814b559c3219767453d57920dc40a31986862048b"}, - {file = "msal-1.23.0.tar.gz", hash = "sha256:25c9a33acf84301f93d1fdbe9f1a9c60cd38af0d5fffdbfa378138fc7bc1e86b"}, + {file = "msal-1.25.0-py2.py3-none-any.whl", hash = "sha256:386df621becb506bc315a713ec3d4d5b5d6163116955c7dde23622f156b81af6"}, + {file = "msal-1.25.0.tar.gz", hash = "sha256:f44329fdb59f4f044c779164a34474b8a44ad9e4940afbc4c3a3a2bbe90324d9"}, ] [package.dependencies] @@ -3800,74 +3868,67 @@ reference = "tsinghua" [[package]] name = "msgpack" -version = "1.0.5" +version = "1.0.7" description = "MessagePack serializer" optional = false -python-versions = "*" +python-versions = ">=3.8" files = [ - {file = "msgpack-1.0.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:525228efd79bb831cf6830a732e2e80bc1b05436b086d4264814b4b2955b2fa9"}, - {file = "msgpack-1.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4f8d8b3bf1ff2672567d6b5c725a1b347fe838b912772aa8ae2bf70338d5a198"}, - {file = "msgpack-1.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cdc793c50be3f01106245a61b739328f7dccc2c648b501e237f0699fe1395b81"}, - {file = "msgpack-1.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5cb47c21a8a65b165ce29f2bec852790cbc04936f502966768e4aae9fa763cb7"}, - {file = "msgpack-1.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e42b9594cc3bf4d838d67d6ed62b9e59e201862a25e9a157019e171fbe672dd3"}, - {file = "msgpack-1.0.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:55b56a24893105dc52c1253649b60f475f36b3aa0fc66115bffafb624d7cb30b"}, - {file = "msgpack-1.0.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:1967f6129fc50a43bfe0951c35acbb729be89a55d849fab7686004da85103f1c"}, - {file = "msgpack-1.0.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:20a97bf595a232c3ee6d57ddaadd5453d174a52594bf9c21d10407e2a2d9b3bd"}, - {file = "msgpack-1.0.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d25dd59bbbbb996eacf7be6b4ad082ed7eacc4e8f3d2df1ba43822da9bfa122a"}, - {file = "msgpack-1.0.5-cp310-cp310-win32.whl", hash = "sha256:382b2c77589331f2cb80b67cc058c00f225e19827dbc818d700f61513ab47bea"}, - {file = "msgpack-1.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:4867aa2df9e2a5fa5f76d7d5565d25ec76e84c106b55509e78c1ede0f152659a"}, - {file = "msgpack-1.0.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9f5ae84c5c8a857ec44dc180a8b0cc08238e021f57abdf51a8182e915e6299f0"}, - {file = "msgpack-1.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9e6ca5d5699bcd89ae605c150aee83b5321f2115695e741b99618f4856c50898"}, - {file = "msgpack-1.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5494ea30d517a3576749cad32fa27f7585c65f5f38309c88c6d137877fa28a5a"}, - {file = "msgpack-1.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1ab2f3331cb1b54165976a9d976cb251a83183631c88076613c6c780f0d6e45a"}, - {file = "msgpack-1.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28592e20bbb1620848256ebc105fc420436af59515793ed27d5c77a217477705"}, - {file = "msgpack-1.0.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fe5c63197c55bce6385d9aee16c4d0641684628f63ace85f73571e65ad1c1e8d"}, - {file = "msgpack-1.0.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ed40e926fa2f297e8a653c954b732f125ef97bdd4c889f243182299de27e2aa9"}, - {file = "msgpack-1.0.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:b2de4c1c0538dcb7010902a2b97f4e00fc4ddf2c8cda9749af0e594d3b7fa3d7"}, - {file = "msgpack-1.0.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:bf22a83f973b50f9d38e55c6aade04c41ddda19b00c4ebc558930d78eecc64ed"}, - {file = "msgpack-1.0.5-cp311-cp311-win32.whl", hash = "sha256:c396e2cc213d12ce017b686e0f53497f94f8ba2b24799c25d913d46c08ec422c"}, - {file = "msgpack-1.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:6c4c68d87497f66f96d50142a2b73b97972130d93677ce930718f68828b382e2"}, - {file = "msgpack-1.0.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a2b031c2e9b9af485d5e3c4520f4220d74f4d222a5b8dc8c1a3ab9448ca79c57"}, - {file = "msgpack-1.0.5-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4f837b93669ce4336e24d08286c38761132bc7ab29782727f8557e1eb21b2080"}, - {file = "msgpack-1.0.5-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b1d46dfe3832660f53b13b925d4e0fa1432b00f5f7210eb3ad3bb9a13c6204a6"}, - {file = "msgpack-1.0.5-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:366c9a7b9057e1547f4ad51d8facad8b406bab69c7d72c0eb6f529cf76d4b85f"}, - {file = "msgpack-1.0.5-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:4c075728a1095efd0634a7dccb06204919a2f67d1893b6aa8e00497258bf926c"}, - {file = "msgpack-1.0.5-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:f933bbda5a3ee63b8834179096923b094b76f0c7a73c1cfe8f07ad608c58844b"}, - {file = "msgpack-1.0.5-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:36961b0568c36027c76e2ae3ca1132e35123dcec0706c4b7992683cc26c1320c"}, - {file = "msgpack-1.0.5-cp36-cp36m-win32.whl", hash = "sha256:b5ef2f015b95f912c2fcab19c36814963b5463f1fb9049846994b007962743e9"}, - {file = "msgpack-1.0.5-cp36-cp36m-win_amd64.whl", hash = "sha256:288e32b47e67f7b171f86b030e527e302c91bd3f40fd9033483f2cacc37f327a"}, - {file = "msgpack-1.0.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:137850656634abddfb88236008339fdaba3178f4751b28f270d2ebe77a563b6c"}, - {file = "msgpack-1.0.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0c05a4a96585525916b109bb85f8cb6511db1c6f5b9d9cbcbc940dc6b4be944b"}, - {file = "msgpack-1.0.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56a62ec00b636583e5cb6ad313bbed36bb7ead5fa3a3e38938503142c72cba4f"}, - {file = "msgpack-1.0.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef8108f8dedf204bb7b42994abf93882da1159728a2d4c5e82012edd92c9da9f"}, - {file = "msgpack-1.0.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1835c84d65f46900920b3708f5ba829fb19b1096c1800ad60bae8418652a951d"}, - {file = "msgpack-1.0.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:e57916ef1bd0fee4f21c4600e9d1da352d8816b52a599c46460e93a6e9f17086"}, - {file = "msgpack-1.0.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:17358523b85973e5f242ad74aa4712b7ee560715562554aa2134d96e7aa4cbbf"}, - {file = "msgpack-1.0.5-cp37-cp37m-win32.whl", hash = "sha256:cb5aaa8c17760909ec6cb15e744c3ebc2ca8918e727216e79607b7bbce9c8f77"}, - {file = "msgpack-1.0.5-cp37-cp37m-win_amd64.whl", hash = "sha256:ab31e908d8424d55601ad7075e471b7d0140d4d3dd3272daf39c5c19d936bd82"}, - {file = "msgpack-1.0.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b72d0698f86e8d9ddf9442bdedec15b71df3598199ba33322d9711a19f08145c"}, - {file = "msgpack-1.0.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:379026812e49258016dd84ad79ac8446922234d498058ae1d415f04b522d5b2d"}, - {file = "msgpack-1.0.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:332360ff25469c346a1c5e47cbe2a725517919892eda5cfaffe6046656f0b7bb"}, - {file = "msgpack-1.0.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:476a8fe8fae289fdf273d6d2a6cb6e35b5a58541693e8f9f019bfe990a51e4ba"}, - {file = "msgpack-1.0.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a9985b214f33311df47e274eb788a5893a761d025e2b92c723ba4c63936b69b1"}, - {file = "msgpack-1.0.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:48296af57cdb1d885843afd73c4656be5c76c0c6328db3440c9601a98f303d87"}, - {file = "msgpack-1.0.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:addab7e2e1fcc04bd08e4eb631c2a90960c340e40dfc4a5e24d2ff0d5a3b3edb"}, - {file = "msgpack-1.0.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:916723458c25dfb77ff07f4c66aed34e47503b2eb3188b3adbec8d8aa6e00f48"}, - {file = "msgpack-1.0.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:821c7e677cc6acf0fd3f7ac664c98803827ae6de594a9f99563e48c5a2f27eb0"}, - {file = "msgpack-1.0.5-cp38-cp38-win32.whl", hash = "sha256:1c0f7c47f0087ffda62961d425e4407961a7ffd2aa004c81b9c07d9269512f6e"}, - {file = "msgpack-1.0.5-cp38-cp38-win_amd64.whl", hash = "sha256:bae7de2026cbfe3782c8b78b0db9cbfc5455e079f1937cb0ab8d133496ac55e1"}, - {file = "msgpack-1.0.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:20c784e66b613c7f16f632e7b5e8a1651aa5702463d61394671ba07b2fc9e025"}, - {file = "msgpack-1.0.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:266fa4202c0eb94d26822d9bfd7af25d1e2c088927fe8de9033d929dd5ba24c5"}, - {file = "msgpack-1.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:18334484eafc2b1aa47a6d42427da7fa8f2ab3d60b674120bce7a895a0a85bdd"}, - {file = "msgpack-1.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:57e1f3528bd95cc44684beda696f74d3aaa8a5e58c816214b9046512240ef437"}, - {file = "msgpack-1.0.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:586d0d636f9a628ddc6a17bfd45aa5b5efaf1606d2b60fa5d87b8986326e933f"}, - {file = "msgpack-1.0.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a740fa0e4087a734455f0fc3abf5e746004c9da72fbd541e9b113013c8dc3282"}, - {file = "msgpack-1.0.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:3055b0455e45810820db1f29d900bf39466df96ddca11dfa6d074fa47054376d"}, - {file = "msgpack-1.0.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a61215eac016f391129a013c9e46f3ab308db5f5ec9f25811e811f96962599a8"}, - {file = "msgpack-1.0.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:362d9655cd369b08fda06b6657a303eb7172d5279997abe094512e919cf74b11"}, - {file = "msgpack-1.0.5-cp39-cp39-win32.whl", hash = "sha256:ac9dd47af78cae935901a9a500104e2dea2e253207c924cc95de149606dc43cc"}, - {file = "msgpack-1.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:06f5174b5f8ed0ed919da0e62cbd4ffde676a374aba4020034da05fab67b9164"}, - {file = "msgpack-1.0.5.tar.gz", hash = "sha256:c075544284eadc5cddc70f4757331d99dcbc16b2bbd4849d15f8aae4cf36d31c"}, + {file = "msgpack-1.0.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:04ad6069c86e531682f9e1e71b71c1c3937d6014a7c3e9edd2aa81ad58842862"}, + {file = "msgpack-1.0.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cca1b62fe70d761a282496b96a5e51c44c213e410a964bdffe0928e611368329"}, + {file = "msgpack-1.0.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e50ebce52f41370707f1e21a59514e3375e3edd6e1832f5e5235237db933c98b"}, + {file = "msgpack-1.0.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a7b4f35de6a304b5533c238bee86b670b75b03d31b7797929caa7a624b5dda6"}, + {file = "msgpack-1.0.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28efb066cde83c479dfe5a48141a53bc7e5f13f785b92ddde336c716663039ee"}, + {file = "msgpack-1.0.7-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4cb14ce54d9b857be9591ac364cb08dc2d6a5c4318c1182cb1d02274029d590d"}, + {file = "msgpack-1.0.7-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b573a43ef7c368ba4ea06050a957c2a7550f729c31f11dd616d2ac4aba99888d"}, + {file = "msgpack-1.0.7-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:ccf9a39706b604d884d2cb1e27fe973bc55f2890c52f38df742bc1d79ab9f5e1"}, + {file = "msgpack-1.0.7-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:cb70766519500281815dfd7a87d3a178acf7ce95390544b8c90587d76b227681"}, + {file = "msgpack-1.0.7-cp310-cp310-win32.whl", hash = "sha256:b610ff0f24e9f11c9ae653c67ff8cc03c075131401b3e5ef4b82570d1728f8a9"}, + {file = "msgpack-1.0.7-cp310-cp310-win_amd64.whl", hash = "sha256:a40821a89dc373d6427e2b44b572efc36a2778d3f543299e2f24eb1a5de65415"}, + {file = "msgpack-1.0.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:576eb384292b139821c41995523654ad82d1916da6a60cff129c715a6223ea84"}, + {file = "msgpack-1.0.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:730076207cb816138cf1af7f7237b208340a2c5e749707457d70705715c93b93"}, + {file = "msgpack-1.0.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:85765fdf4b27eb5086f05ac0491090fc76f4f2b28e09d9350c31aac25a5aaff8"}, + {file = "msgpack-1.0.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3476fae43db72bd11f29a5147ae2f3cb22e2f1a91d575ef130d2bf49afd21c46"}, + {file = "msgpack-1.0.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d4c80667de2e36970ebf74f42d1088cc9ee7ef5f4e8c35eee1b40eafd33ca5b"}, + {file = "msgpack-1.0.7-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b0bf0effb196ed76b7ad883848143427a73c355ae8e569fa538365064188b8e"}, + {file = "msgpack-1.0.7-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f9a7c509542db4eceed3dcf21ee5267ab565a83555c9b88a8109dcecc4709002"}, + {file = "msgpack-1.0.7-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:84b0daf226913133f899ea9b30618722d45feffa67e4fe867b0b5ae83a34060c"}, + {file = "msgpack-1.0.7-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ec79ff6159dffcc30853b2ad612ed572af86c92b5168aa3fc01a67b0fa40665e"}, + {file = "msgpack-1.0.7-cp311-cp311-win32.whl", hash = "sha256:3e7bf4442b310ff154b7bb9d81eb2c016b7d597e364f97d72b1acc3817a0fdc1"}, + {file = "msgpack-1.0.7-cp311-cp311-win_amd64.whl", hash = "sha256:3f0c8c6dfa6605ab8ff0611995ee30d4f9fcff89966cf562733b4008a3d60d82"}, + {file = "msgpack-1.0.7-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f0936e08e0003f66bfd97e74ee530427707297b0d0361247e9b4f59ab78ddc8b"}, + {file = "msgpack-1.0.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:98bbd754a422a0b123c66a4c341de0474cad4a5c10c164ceed6ea090f3563db4"}, + {file = "msgpack-1.0.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b291f0ee7961a597cbbcc77709374087fa2a9afe7bdb6a40dbbd9b127e79afee"}, + {file = "msgpack-1.0.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ebbbba226f0a108a7366bf4b59bf0f30a12fd5e75100c630267d94d7f0ad20e5"}, + {file = "msgpack-1.0.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e2d69948e4132813b8d1131f29f9101bc2c915f26089a6d632001a5c1349672"}, + {file = "msgpack-1.0.7-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bdf38ba2d393c7911ae989c3bbba510ebbcdf4ecbdbfec36272abe350c454075"}, + {file = "msgpack-1.0.7-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:993584fc821c58d5993521bfdcd31a4adf025c7d745bbd4d12ccfecf695af5ba"}, + {file = "msgpack-1.0.7-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:52700dc63a4676669b341ba33520f4d6e43d3ca58d422e22ba66d1736b0a6e4c"}, + {file = "msgpack-1.0.7-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e45ae4927759289c30ccba8d9fdce62bb414977ba158286b5ddaf8df2cddb5c5"}, + {file = "msgpack-1.0.7-cp312-cp312-win32.whl", hash = "sha256:27dcd6f46a21c18fa5e5deed92a43d4554e3df8d8ca5a47bf0615d6a5f39dbc9"}, + {file = "msgpack-1.0.7-cp312-cp312-win_amd64.whl", hash = "sha256:7687e22a31e976a0e7fc99c2f4d11ca45eff652a81eb8c8085e9609298916dcf"}, + {file = "msgpack-1.0.7-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5b6ccc0c85916998d788b295765ea0e9cb9aac7e4a8ed71d12e7d8ac31c23c95"}, + {file = "msgpack-1.0.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:235a31ec7db685f5c82233bddf9858748b89b8119bf4538d514536c485c15fe0"}, + {file = "msgpack-1.0.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cab3db8bab4b7e635c1c97270d7a4b2a90c070b33cbc00c99ef3f9be03d3e1f7"}, + {file = "msgpack-1.0.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bfdd914e55e0d2c9e1526de210f6fe8ffe9705f2b1dfcc4aecc92a4cb4b533d"}, + {file = "msgpack-1.0.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36e17c4592231a7dbd2ed09027823ab295d2791b3b1efb2aee874b10548b7524"}, + {file = "msgpack-1.0.7-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38949d30b11ae5f95c3c91917ee7a6b239f5ec276f271f28638dec9156f82cfc"}, + {file = "msgpack-1.0.7-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:ff1d0899f104f3921d94579a5638847f783c9b04f2d5f229392ca77fba5b82fc"}, + {file = "msgpack-1.0.7-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:dc43f1ec66eb8440567186ae2f8c447d91e0372d793dfe8c222aec857b81a8cf"}, + {file = "msgpack-1.0.7-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:dd632777ff3beaaf629f1ab4396caf7ba0bdd075d948a69460d13d44357aca4c"}, + {file = "msgpack-1.0.7-cp38-cp38-win32.whl", hash = "sha256:4e71bc4416de195d6e9b4ee93ad3f2f6b2ce11d042b4d7a7ee00bbe0358bd0c2"}, + {file = "msgpack-1.0.7-cp38-cp38-win_amd64.whl", hash = "sha256:8f5b234f567cf76ee489502ceb7165c2a5cecec081db2b37e35332b537f8157c"}, + {file = "msgpack-1.0.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:bfef2bb6ef068827bbd021017a107194956918ab43ce4d6dc945ffa13efbc25f"}, + {file = "msgpack-1.0.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:484ae3240666ad34cfa31eea7b8c6cd2f1fdaae21d73ce2974211df099a95d81"}, + {file = "msgpack-1.0.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:3967e4ad1aa9da62fd53e346ed17d7b2e922cba5ab93bdd46febcac39be636fc"}, + {file = "msgpack-1.0.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8dd178c4c80706546702c59529ffc005681bd6dc2ea234c450661b205445a34d"}, + {file = "msgpack-1.0.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6ffbc252eb0d229aeb2f9ad051200668fc3a9aaa8994e49f0cb2ffe2b7867e7"}, + {file = "msgpack-1.0.7-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:822ea70dc4018c7e6223f13affd1c5c30c0f5c12ac1f96cd8e9949acddb48a61"}, + {file = "msgpack-1.0.7-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:384d779f0d6f1b110eae74cb0659d9aa6ff35aaf547b3955abf2ab4c901c4819"}, + {file = "msgpack-1.0.7-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f64e376cd20d3f030190e8c32e1c64582eba56ac6dc7d5b0b49a9d44021b52fd"}, + {file = "msgpack-1.0.7-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5ed82f5a7af3697b1c4786053736f24a0efd0a1b8a130d4c7bfee4b9ded0f08f"}, + {file = "msgpack-1.0.7-cp39-cp39-win32.whl", hash = "sha256:f26a07a6e877c76a88e3cecac8531908d980d3d5067ff69213653649ec0f60ad"}, + {file = "msgpack-1.0.7-cp39-cp39-win_amd64.whl", hash = "sha256:1dc93e8e4653bdb5910aed79f11e165c85732067614f180f70534f056da97db3"}, + {file = "msgpack-1.0.7.tar.gz", hash = "sha256:572efc93db7a4d27e404501975ca6d2d9775705c2d922390d878fcf768d92c87"}, ] [package.source] @@ -4033,13 +4094,13 @@ reference = "tsinghua" [[package]] name = "netaddr" -version = "0.8.0" +version = "0.9.0" description = "A network address manipulation library for Python" optional = false python-versions = "*" files = [ - {file = "netaddr-0.8.0-py2.py3-none-any.whl", hash = "sha256:9666d0232c32d2656e5e5f8d735f58fd6c7457ce52fc21c98d45f2af78f990ac"}, - {file = "netaddr-0.8.0.tar.gz", hash = "sha256:d6cc57c7a07b1d9d2e917aa8b36ae8ce61c35ba3fcd1b83ca31c5a0ee2b5a243"}, + {file = "netaddr-0.9.0-py3-none-any.whl", hash = "sha256:5148b1055679d2a1ec070c521b7db82137887fabd6d7e37f5199b44f775c3bb1"}, + {file = "netaddr-0.9.0.tar.gz", hash = "sha256:7b46fa9b1a2d71fd5de9e4a3784ef339700a53a08c8040f08baf5f1194da0128"}, ] [package.source] @@ -4230,13 +4291,13 @@ reference = "tsinghua" [[package]] name = "oslo-config" -version = "9.1.1" +version = "9.2.0" description = "Oslo Configuration API" optional = false python-versions = ">=3.8" files = [ - {file = "oslo.config-9.1.1-py3-none-any.whl", hash = "sha256:7cd56e0b41b04f64dbc42e83e8164d5ef03466390f1216fbda2cb0e1c535c22c"}, - {file = "oslo.config-9.1.1.tar.gz", hash = "sha256:b07654b53d87792ae8e739962ad729c529c9938a118d891ece9ee31d59716bc9"}, + {file = "oslo.config-9.2.0-py3-none-any.whl", hash = "sha256:b98e50b19161fc76f25905ff74043e239258a3ebe799a5f9070d285e3c039dee"}, + {file = "oslo.config-9.2.0.tar.gz", hash = "sha256:ffeb01ca65a603d5525905f1a88a3319be09ce2c6ac376c4312aaec283095878"}, ] [package.dependencies] @@ -4250,7 +4311,7 @@ stevedore = ">=1.20.0" [package.extras] rst-generator = ["rst2txt (>=1.1.0)", "sphinx (>=1.8.0,!=2.1.0)"] -test = ["bandit (>=1.6.0,<1.7.0)", "coverage (>=4.0,!=4.4)", "fixtures (>=3.0.0)", "hacking (>=3.0.1,<3.1.0)", "mypy (>=0.720)", "oslo.log (>=3.36.0)", "oslotest (>=3.2.0)", "pre-commit (>=2.6.0)", "requests-mock (>=1.5.0)", "stestr (>=2.1.0)", "testscenarios (>=0.4)", "testtools (>=2.2.0)"] +test = ["bandit (>=1.7.0,<1.8.0)", "coverage (>=4.0,!=4.4)", "fixtures (>=3.0.0)", "hacking (>=3.0.1,<3.1.0)", "mypy (>=0.720)", "oslo.log (>=3.36.0)", "oslotest (>=3.2.0)", "pre-commit (>=2.6.0)", "requests-mock (>=1.5.0)", "stestr (>=2.1.0)", "testscenarios (>=0.4)", "testtools (>=2.2.0)"] [package.source] type = "legacy" @@ -4259,13 +4320,13 @@ reference = "tsinghua" [[package]] name = "oslo-i18n" -version = "6.0.0" +version = "6.2.0" description = "Oslo i18n library" optional = false python-versions = ">=3.8" files = [ - {file = "oslo.i18n-6.0.0-py3-none-any.whl", hash = "sha256:080fedf41b05d4dcd23a91d23ee2dea0863996e860a59695856269a42d939fc1"}, - {file = "oslo.i18n-6.0.0.tar.gz", hash = "sha256:ed10686b75f7c607825177a669155f4e259ce39f6143a375f6359bbcaa4a35cd"}, + {file = "oslo.i18n-6.2.0-py3-none-any.whl", hash = "sha256:5cd6d0659bec2013107d235a8cf5e61475cc9dd33ef9ffc7aa2776bc1c6b56c9"}, + {file = "oslo.i18n-6.2.0.tar.gz", hash = "sha256:70f8a4ce9871291bc609d07e31e6e5032666556992ff1ae53e78f2ed2a5abe82"}, ] [package.dependencies] @@ -4278,13 +4339,13 @@ reference = "tsinghua" [[package]] name = "oslo-serialization" -version = "5.1.1" +version = "5.2.0" description = "Oslo Serialization library" optional = false python-versions = ">=3.8" files = [ - {file = "oslo.serialization-5.1.1-py3-none-any.whl", hash = "sha256:c5dfb97ce8ddd1d2708a9a3f4a091063f6c304940c7cb39f532f7f791441fdca"}, - {file = "oslo.serialization-5.1.1.tar.gz", hash = "sha256:8abbda8b1763a06071fc28c5d8a9be547ba285f4830e68a70ff88fe11f16bf43"}, + {file = "oslo.serialization-5.2.0-py3-none-any.whl", hash = "sha256:c7ec759192a787c7e1a5e765920bb594752c75e6e0cd5a9a82c385a9088125e5"}, + {file = "oslo.serialization-5.2.0.tar.gz", hash = "sha256:9cf030d61a6cce1f47a62d4050f5e83e1bd1a1018ac671bb193aee07d15bdbc2"}, ] [package.dependencies] @@ -4292,6 +4353,7 @@ msgpack = ">=0.5.2" "oslo.utils" = ">=3.33.0" pbr = ">=2.0.0,<2.1.0 || >2.1.0" pytz = ">=2013.6" +tzdata = ">=2022.4" [package.source] type = "legacy" @@ -4300,13 +4362,13 @@ reference = "tsinghua" [[package]] name = "oslo-utils" -version = "6.2.0" +version = "6.3.0" description = "Oslo Utility library" optional = false python-versions = ">=3.8" files = [ - {file = "oslo.utils-6.2.0-py3-none-any.whl", hash = "sha256:30ba9fd431be468cd17b5d7c1a0ae6d63bb63aaaf97bf590123f13c6d95254a3"}, - {file = "oslo.utils-6.2.0.tar.gz", hash = "sha256:fe1d166f4cb004fbd6b6bc9adfbc32aedeaf3eb54eeaf70d91a224a87543c6a5"}, + {file = "oslo.utils-6.3.0-py3-none-any.whl", hash = "sha256:6bac2e56650f502caae6c0e8ba6e5eda3d7a16743d115f8836cad54538dd667f"}, + {file = "oslo.utils-6.3.0.tar.gz", hash = "sha256:758d945b2bad5bea81abed80ad33ffea1d1d793348ac5eb5b3866ba745b11d55"}, ] [package.dependencies] @@ -4318,6 +4380,7 @@ netifaces = ">=0.10.4" packaging = ">=20.4" pyparsing = ">=2.1.0" pytz = ">=2013.6" +PyYAML = ">=3.13" tzdata = ">=2022.4" [package.source] @@ -4350,13 +4413,13 @@ reference = "tsinghua" [[package]] name = "packaging" -version = "23.1" +version = "23.2" description = "Core utilities for Python packages" optional = false python-versions = ">=3.7" files = [ - {file = "packaging-23.1-py3-none-any.whl", hash = "sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61"}, - {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, + {file = "packaging-23.2-py3-none-any.whl", hash = "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"}, + {file = "packaging-23.2.tar.gz", hash = "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5"}, ] [package.source] @@ -4434,13 +4497,13 @@ reference = "tsinghua" [[package]] name = "pbr" -version = "5.11.1" +version = "6.0.0" description = "Python Build Reasonableness" optional = false python-versions = ">=2.6" files = [ - {file = "pbr-5.11.1-py2.py3-none-any.whl", hash = "sha256:567f09558bae2b3ab53cb3c1e2e33e726ff3338e7bae3db5dc954b3a44eef12b"}, - {file = "pbr-5.11.1.tar.gz", hash = "sha256:aefc51675b0b533d56bb5fd1c8c6c0522fe31896679882e1c4c63d5e4a0fccb3"}, + {file = "pbr-6.0.0-py2.py3-none-any.whl", hash = "sha256:4a7317d5e3b17a3dccb6a8cfe67dab65b20551404c52c8ed41279fa4f0cb4cda"}, + {file = "pbr-6.0.0.tar.gz", hash = "sha256:d1377122a5a00e2f940ee482999518efe16d745d423a670c27773dfbc3c9a7d9"}, ] [package.source] @@ -4450,13 +4513,13 @@ reference = "tsinghua" [[package]] name = "pexpect" -version = "4.8.0" +version = "4.9.0" description = "Pexpect allows easy control of interactive console applications." optional = false python-versions = "*" files = [ - {file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"}, - {file = "pexpect-4.8.0.tar.gz", hash = "sha256:fc65a43959d153d0114afe13997d439c22823a27cefceb5ff35c2178c6784c0c"}, + {file = "pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523"}, + {file = "pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f"}, ] [package.dependencies] @@ -4575,13 +4638,13 @@ reference = "tsinghua" [[package]] name = "portalocker" -version = "2.7.0" +version = "2.8.2" description = "Wraps the portalocker recipe for easy usage" optional = false -python-versions = ">=3.5" +python-versions = ">=3.8" files = [ - {file = "portalocker-2.7.0-py2.py3-none-any.whl", hash = "sha256:a07c5b4f3985c3cf4798369631fb7011adb498e2a46d8440efc75a8f29a0f983"}, - {file = "portalocker-2.7.0.tar.gz", hash = "sha256:032e81d534a88ec1736d03f780ba073f047a06c478b06e2937486f334e955c51"}, + {file = "portalocker-2.8.2-py3-none-any.whl", hash = "sha256:cfb86acc09b9aa7c3b43594e19be1345b9d16af3feb08bf92f23d4dce513a28e"}, + {file = "portalocker-2.8.2.tar.gz", hash = "sha256:2b035aa7828e46c58e9b31390ee1f169b98e1066ab10b9a6a861fe7e25ee4f33"}, ] [package.dependencies] @@ -4590,7 +4653,7 @@ pywin32 = {version = ">=226", markers = "platform_system == \"Windows\""} [package.extras] docs = ["sphinx (>=1.7.1)"] redis = ["redis"] -tests = ["pytest (>=5.4.1)", "pytest-cov (>=2.8.1)", "pytest-mypy (>=0.8.0)", "pytest-timeout (>=2.1.0)", "redis", "sphinx (>=6.0.0)"] +tests = ["pytest (>=5.4.1)", "pytest-cov (>=2.8.1)", "pytest-mypy (>=0.8.0)", "pytest-timeout (>=2.1.0)", "redis", "sphinx (>=6.0.0)", "types-redis"] [package.source] type = "legacy" @@ -4599,13 +4662,13 @@ reference = "tsinghua" [[package]] name = "prettytable" -version = "3.8.0" +version = "3.9.0" description = "A simple Python library for easily displaying tabular data in a visually appealing ASCII table format" optional = false python-versions = ">=3.8" files = [ - {file = "prettytable-3.8.0-py3-none-any.whl", hash = "sha256:03481bca25ae0c28958c8cd6ac5165c159ce89f7ccde04d5c899b24b68bb13b7"}, - {file = "prettytable-3.8.0.tar.gz", hash = "sha256:031eae6a9102017e8c7c7906460d150b7ed78b20fd1d8c8be4edaf88556c07ce"}, + {file = "prettytable-3.9.0-py3-none-any.whl", hash = "sha256:a71292ab7769a5de274b146b276ce938786f56c31cf7cea88b6f3775d82fe8c8"}, + {file = "prettytable-3.9.0.tar.gz", hash = "sha256:f4ed94803c23073a90620b201965e5dc0bccf1760b7a7eaf3158cab8aaffdf34"}, ] [package.dependencies] @@ -4621,13 +4684,13 @@ reference = "tsinghua" [[package]] name = "prometheus-client" -version = "0.17.1" +version = "0.19.0" description = "Python client for the Prometheus monitoring system." optional = false -python-versions = ">=3.6" +python-versions = ">=3.8" files = [ - {file = "prometheus_client-0.17.1-py3-none-any.whl", hash = "sha256:e537f37160f6807b8202a6fc4764cdd19bac5480ddd3e0d463c3002b34462101"}, - {file = "prometheus_client-0.17.1.tar.gz", hash = "sha256:21e674f39831ae3f8acde238afd9a27a37d0d2fb5a28ea094f0ce25d2cbf2091"}, + {file = "prometheus_client-0.19.0-py3-none-any.whl", hash = "sha256:c88b1e6ecf6b41cd8fb5731c7ae919bf66df6ec6fafa555cd6c0e16ca169ae92"}, + {file = "prometheus_client-0.19.0.tar.gz", hash = "sha256:4585b0d1223148c27a225b10dbec5ae9bc4c81a99a3fa80774fa6209935324e1"}, ] [package.extras] @@ -4640,13 +4703,13 @@ reference = "tsinghua" [[package]] name = "prompt-toolkit" -version = "3.0.39" +version = "3.0.41" description = "Library for building powerful interactive command lines in Python" optional = false python-versions = ">=3.7.0" files = [ - {file = "prompt_toolkit-3.0.39-py3-none-any.whl", hash = "sha256:9dffbe1d8acf91e3de75f3b544e4842382fc06c6babe903ac9acb74dc6e08d88"}, - {file = "prompt_toolkit-3.0.39.tar.gz", hash = "sha256:04505ade687dc26dc4284b1ad19a83be2f2afe83e7a828ace0c72f3a1df72aac"}, + {file = "prompt_toolkit-3.0.41-py3-none-any.whl", hash = "sha256:f36fe301fafb7470e86aaf90f036eef600a3210be4decf461a5b1ca8403d3cb2"}, + {file = "prompt_toolkit-3.0.41.tar.gz", hash = "sha256:941367d97fc815548822aa26c2a269fdc4eb21e9ec05fc5d447cf09bad5d75f0"}, ] [package.dependencies] @@ -4681,24 +4744,22 @@ reference = "tsinghua" [[package]] name = "protobuf" -version = "4.24.0" +version = "4.25.1" description = "" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "protobuf-4.24.0-cp310-abi3-win32.whl", hash = "sha256:81cb9c4621d2abfe181154354f63af1c41b00a4882fb230b4425cbaed65e8f52"}, - {file = "protobuf-4.24.0-cp310-abi3-win_amd64.whl", hash = "sha256:6c817cf4a26334625a1904b38523d1b343ff8b637d75d2c8790189a4064e51c3"}, - {file = "protobuf-4.24.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:ae97b5de10f25b7a443b40427033e545a32b0e9dda17bcd8330d70033379b3e5"}, - {file = "protobuf-4.24.0-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:567fe6b0647494845d0849e3d5b260bfdd75692bf452cdc9cb660d12457c055d"}, - {file = "protobuf-4.24.0-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:a6b1ca92ccabfd9903c0c7dde8876221dc7d8d87ad5c42e095cc11b15d3569c7"}, - {file = "protobuf-4.24.0-cp37-cp37m-win32.whl", hash = "sha256:a38400a692fd0c6944c3c58837d112f135eb1ed6cdad5ca6c5763336e74f1a04"}, - {file = "protobuf-4.24.0-cp37-cp37m-win_amd64.whl", hash = "sha256:5ab19ee50037d4b663c02218a811a5e1e7bb30940c79aac385b96e7a4f9daa61"}, - {file = "protobuf-4.24.0-cp38-cp38-win32.whl", hash = "sha256:e8834ef0b4c88666ebb7c7ec18045aa0f4325481d724daa624a4cf9f28134653"}, - {file = "protobuf-4.24.0-cp38-cp38-win_amd64.whl", hash = "sha256:8bb52a2be32db82ddc623aefcedfe1e0eb51da60e18fcc908fb8885c81d72109"}, - {file = "protobuf-4.24.0-cp39-cp39-win32.whl", hash = "sha256:ae7a1835721086013de193311df858bc12cd247abe4ef9710b715d930b95b33e"}, - {file = "protobuf-4.24.0-cp39-cp39-win_amd64.whl", hash = "sha256:44825e963008f8ea0d26c51911c30d3e82e122997c3c4568fd0385dd7bacaedf"}, - {file = "protobuf-4.24.0-py3-none-any.whl", hash = "sha256:82e6e9ebdd15b8200e8423676eab38b774624d6a1ad696a60d86a2ac93f18201"}, - {file = "protobuf-4.24.0.tar.gz", hash = "sha256:5d0ceb9de6e08311832169e601d1fc71bd8e8c779f3ee38a97a78554945ecb85"}, + {file = "protobuf-4.25.1-cp310-abi3-win32.whl", hash = "sha256:193f50a6ab78a970c9b4f148e7c750cfde64f59815e86f686c22e26b4fe01ce7"}, + {file = "protobuf-4.25.1-cp310-abi3-win_amd64.whl", hash = "sha256:3497c1af9f2526962f09329fd61a36566305e6c72da2590ae0d7d1322818843b"}, + {file = "protobuf-4.25.1-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:0bf384e75b92c42830c0a679b0cd4d6e2b36ae0cf3dbb1e1dfdda48a244f4bcd"}, + {file = "protobuf-4.25.1-cp37-abi3-manylinux2014_aarch64.whl", hash = "sha256:0f881b589ff449bf0b931a711926e9ddaad3b35089cc039ce1af50b21a4ae8cb"}, + {file = "protobuf-4.25.1-cp37-abi3-manylinux2014_x86_64.whl", hash = "sha256:ca37bf6a6d0046272c152eea90d2e4ef34593aaa32e8873fc14c16440f22d4b7"}, + {file = "protobuf-4.25.1-cp38-cp38-win32.whl", hash = "sha256:abc0525ae2689a8000837729eef7883b9391cd6aa7950249dcf5a4ede230d5dd"}, + {file = "protobuf-4.25.1-cp38-cp38-win_amd64.whl", hash = "sha256:1484f9e692091450e7edf418c939e15bfc8fc68856e36ce399aed6889dae8bb0"}, + {file = "protobuf-4.25.1-cp39-cp39-win32.whl", hash = "sha256:8bdbeaddaac52d15c6dce38c71b03038ef7772b977847eb6d374fc86636fa510"}, + {file = "protobuf-4.25.1-cp39-cp39-win_amd64.whl", hash = "sha256:becc576b7e6b553d22cbdf418686ee4daa443d7217999125c045ad56322dda10"}, + {file = "protobuf-4.25.1-py3-none-any.whl", hash = "sha256:a19731d5e83ae4737bb2a089605e636077ac001d18781b3cf489b9546c7c80d6"}, + {file = "protobuf-4.25.1.tar.gz", hash = "sha256:57d65074b4f5baa4ab5da1605c02be90ac20c8b40fb137d6a8df9f416b0d0ce2"}, ] [package.source] @@ -5137,17 +5198,18 @@ reference = "tsinghua" [[package]] name = "pygments" -version = "2.16.1" +version = "2.17.2" description = "Pygments is a syntax highlighting package written in Python." optional = false python-versions = ">=3.7" files = [ - {file = "Pygments-2.16.1-py3-none-any.whl", hash = "sha256:13fc09fa63bc8d8671a6d247e1eb303c4b343eaee81d861f3404db2935653692"}, - {file = "Pygments-2.16.1.tar.gz", hash = "sha256:1daff0494820c69bc8941e407aa20f577374ee88364ee10a98fdbe0aece96e29"}, + {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, + {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, ] [package.extras] plugins = ["importlib-metadata"] +windows-terminal = ["colorama (>=0.4.6)"] [package.source] type = "legacy" @@ -5341,9 +5403,11 @@ files = [ {file = "pymssql-2.2.8-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:049f2e3de919e8e02504780a21ebbf235e21ca8ed5c7538c5b6e705aa6c43d8c"}, {file = "pymssql-2.2.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dd86d8e3e346e34f3f03d12e333747b53a1daa74374a727f4714d5b82ee0dd5"}, {file = "pymssql-2.2.8-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:508226a0df7cb6faeda9f8e84e85743690ca427d7b27af9a73d75fcf0c1eef6e"}, + {file = "pymssql-2.2.8-cp310-cp310-win_amd64.whl", hash = "sha256:47859887adeaf184766b5e0bc845dd23611f3808f9521552063bb36eabc10092"}, {file = "pymssql-2.2.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d873e553374d5b1c57fe1c43bb75e3bcc2920678db1ef26f6bfed396c7d21b30"}, {file = "pymssql-2.2.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf31b8b76634c826a91f9999e15b7bfb0c051a0f53b319fd56481a67e5b903bb"}, {file = "pymssql-2.2.8-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:821945c2214fe666fd456c61e09a29a00e7719c9e136c801bffb3a254e9c579b"}, + {file = "pymssql-2.2.8-cp311-cp311-win_amd64.whl", hash = "sha256:cc85b609b4e60eac25fa38bbac1ff854fd2c2a276e0ca4a3614c6f97efb644bb"}, {file = "pymssql-2.2.8-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:ebe7f64d5278d807f14bea08951e02512bfbc6219fd4d4f15bb45ded885cf3d4"}, {file = "pymssql-2.2.8-cp36-cp36m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:253af3d39fc0235627966817262d5c4c94ad09dcbea59664748063470048c29c"}, {file = "pymssql-2.2.8-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c9d109df536dc5f7dd851a88d285a4c9cb12a9314b621625f4f5ab1197eb312"}, @@ -5359,11 +5423,13 @@ files = [ {file = "pymssql-2.2.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3906993300650844ec140aa58772c0f5f3e9e9d5709c061334fd1551acdcf066"}, {file = "pymssql-2.2.8-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:7309c7352e4a87c9995c3183ebfe0ff4135e955bb759109637673c61c9f0ca8d"}, {file = "pymssql-2.2.8-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:9b8d603cc1ec7ae585c5a409a1d45e8da067970c79dd550d45c238ae0aa0f79f"}, + {file = "pymssql-2.2.8-cp38-cp38-win_amd64.whl", hash = "sha256:293cb4d0339e221d877d6b19a1905082b658f0100a1e2ccc9dda10de58938901"}, {file = "pymssql-2.2.8-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:895041edd002a2e91d8a4faf0906b6fbfef29d9164bc6beb398421f5927fa40e"}, {file = "pymssql-2.2.8-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6b2d9c6d38a416c6f2db36ff1cd8e69f9a5387a46f9f4f612623192e0c9404b1"}, {file = "pymssql-2.2.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d63d6f25cf40fe6a03c49be2d4d337858362b8ab944d6684c268e4990807cf0c"}, {file = "pymssql-2.2.8-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:c83ad3ad20951f3a94894b354fa5fa9666dcd5ebb4a635dad507c7d1dd545833"}, {file = "pymssql-2.2.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:3933f7f082be74698eea835df51798dab9bc727d94d3d280bffc75ab9265f890"}, + {file = "pymssql-2.2.8-cp39-cp39-win_amd64.whl", hash = "sha256:de313375b90b0f554058992f35c4a4beb3f6ec2f5912d8cd6afb649f95b03a9f"}, {file = "pymssql-2.2.8.tar.gz", hash = "sha256:9baefbfbd07d0142756e2dfcaa804154361ac5806ab9381350aad4e780c3033e"}, ] @@ -5503,27 +5569,18 @@ reference = "tsinghua" [[package]] name = "pyspnego" -version = "0.9.1" +version = "0.10.2" description = "Windows Negotiate Authentication Client and Server" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "pyspnego-0.9.1-cp310-cp310-win32.whl", hash = "sha256:aa43f00ed1c3b8e16a658613e2557a3ff9bea26acef867705eb4ee7f5e469ac3"}, - {file = "pyspnego-0.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:d031a7fa9c9ab3b67725e35affc90f8e6504518fb3ffe21573504e72b5a2fb5e"}, - {file = "pyspnego-0.9.1-cp311-cp311-win32.whl", hash = "sha256:58a17f7ba17f6cee72149911df6cc785ce7072744a386483957b74c62da654d8"}, - {file = "pyspnego-0.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:e7a92321272e3613c30f55a3324ef6d780bba8b723be8d50c35aac8409fc4028"}, - {file = "pyspnego-0.9.1-cp37-cp37m-win32.whl", hash = "sha256:87a2c23e640f4f6ae3c391d1f56e287b72908080a0e6376f2f365da5a2117dca"}, - {file = "pyspnego-0.9.1-cp37-cp37m-win_amd64.whl", hash = "sha256:ff4fecf488369d6634afb20f9e56eb3b187fb3c883d6551601bbad4f4badab62"}, - {file = "pyspnego-0.9.1-cp38-cp38-win32.whl", hash = "sha256:7515f00418324809eb1adec0afac93da006c03baba6c6fd1a981b5401b798f56"}, - {file = "pyspnego-0.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:6366e39ba251889e2573c7d037e7feec8af86aea6c3b32f22a8af33bf88265b6"}, - {file = "pyspnego-0.9.1-cp39-cp39-win32.whl", hash = "sha256:07417f90328fb57c19a383e59b65060d4fc101441b74c34dbe4ba860775b0a3a"}, - {file = "pyspnego-0.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:4b79d5ba55ada38833d2421c44ed30a7313c00cbf34fa919dd106049616307d3"}, - {file = "pyspnego-0.9.1-py3-none-any.whl", hash = "sha256:c6aebe1fdc3990be2c137f3c3e041062243871b8161bc7adf4d269c3b6deda35"}, - {file = "pyspnego-0.9.1.tar.gz", hash = "sha256:6eea64f511bdfa192c2f80593ddf124258b0ea560327468953d18420e0ab3597"}, + {file = "pyspnego-0.10.2-py3-none-any.whl", hash = "sha256:3d5c5c28dbd0cd6a679acf45219630254db3c0e5ad4a16de521caa0585b088c0"}, + {file = "pyspnego-0.10.2.tar.gz", hash = "sha256:9a22c23aeae7b4424fdb2482450d3f8302ac012e2644e1cfe735cf468fcd12ed"}, ] [package.dependencies] cryptography = "*" +sspilib = {version = ">=0.1.0", markers = "sys_platform == \"win32\""} [package.extras] kerberos = ["gssapi (>=1.6.0)", "krb5 (>=0.3.0)"] @@ -5861,6 +5918,7 @@ files = [ {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, @@ -5868,8 +5926,15 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, + {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, + {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, + {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, @@ -5886,6 +5951,7 @@ files = [ {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, @@ -5893,6 +5959,7 @@ files = [ {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, @@ -6185,19 +6252,19 @@ reference = "tsinghua" [[package]] name = "setuptools" -version = "68.1.0" +version = "69.0.2" description = "Easily download, build, install, upgrade, and uninstall Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "setuptools-68.1.0-py3-none-any.whl", hash = "sha256:e13e1b0bc760e9b0127eda042845999b2f913e12437046e663b833aa96d89715"}, - {file = "setuptools-68.1.0.tar.gz", hash = "sha256:d59c97e7b774979a5ccb96388efc9eb65518004537e85d52e81eaee89ab6dd91"}, + {file = "setuptools-69.0.2-py3-none-any.whl", hash = "sha256:1e8fdff6797d3865f37397be788a4e3cba233608e9b509382a2777d25ebde7f2"}, + {file = "setuptools-69.0.2.tar.gz", hash = "sha256:735896e78a4742605974de002ac60562d286fa8051a7e2299445e8e8fbb01aa6"}, ] [package.extras] -docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"] testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] -testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] [package.source] type = "legacy" @@ -6321,13 +6388,13 @@ reference = "tsinghua" [[package]] name = "soupsieve" -version = "2.4.1" +version = "2.5" description = "A modern CSS selector implementation for Beautiful Soup." optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "soupsieve-2.4.1-py3-none-any.whl", hash = "sha256:1c1bfee6819544a3447586c889157365a27e10d88cde3ad3da0cf0ddf646feb8"}, - {file = "soupsieve-2.4.1.tar.gz", hash = "sha256:89d12b2d5dfcd2c9e8c22326da9d9aa9cb3dfab0a83a024f05704076ee8d35ea"}, + {file = "soupsieve-2.5-py3-none-any.whl", hash = "sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7"}, + {file = "soupsieve-2.5.tar.gz", hash = "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690"}, ] [package.source] @@ -6403,15 +6470,59 @@ type = "legacy" url = "https://pypi.tuna.tsinghua.edu.cn/simple" reference = "tsinghua" +[[package]] +name = "sspilib" +version = "0.1.0" +description = "SSPI API bindings for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "sspilib-0.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5e43f3e684e9d29c80324bd54f52dac65ac4b18d81a2dcd529dce3994369a14d"}, + {file = "sspilib-0.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1eb34eda5d362b6603707a55751f1eff81775709b821e51cb64d1d2fa2bb8b6e"}, + {file = "sspilib-0.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ffe123f056f78cbe18aaed6b15f06e252020061c3387a72615abd46699a0b24"}, + {file = "sspilib-0.1.0-cp310-cp310-win32.whl", hash = "sha256:a4151072e28ec3b7d785beac9548a3d6a4549c431eb5487a5b8a1de028e9fef0"}, + {file = "sspilib-0.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:2a19696c7b96b6bbef2b2ddf35df5a92f09b268476a348390a2f0da18cf29510"}, + {file = "sspilib-0.1.0-cp310-cp310-win_arm64.whl", hash = "sha256:d2778e5e2881405b4d359a604e2802f5b7a7ed433ff62d6073d04c203af10eb1"}, + {file = "sspilib-0.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:09d7f72ad5e4bbf9a8f1acf0d5f0c3f9fbe500f44c4a45ac24a99ece84f5654f"}, + {file = "sspilib-0.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e5705e11aaa030a61d2b0a2ce09d2b8a1962dd950e55adc7a3c87dd463c6878"}, + {file = "sspilib-0.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dced8213d311c56f5f38044716ebff5412cc156f19678659e8ffa9bb6a642bd7"}, + {file = "sspilib-0.1.0-cp311-cp311-win32.whl", hash = "sha256:d30d38d52dbd857732224e86ae3627d003cc510451083c69fa481fc7de88a7b6"}, + {file = "sspilib-0.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:61c9067168cce962f7fead42c28804c3a39a164b9a7b660200b8cfe31e3af071"}, + {file = "sspilib-0.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:b526b8e5a236553f5137b951b89a2f108f56138ad05f31fd0a51b10f80b6c3cc"}, + {file = "sspilib-0.1.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3ff356d40cd34c900f94f1591eaabd458284042af611ebc1dbf609002066dba5"}, + {file = "sspilib-0.1.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2b0fee3a52d0acef090f6c9b49953a8400fdc1c10aca7334319414a3038aa493"}, + {file = "sspilib-0.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ab52d190dad1d578ec40d1fb417a8571954f4e32f35442a14cb709f57d3acbc9"}, + {file = "sspilib-0.1.0-cp312-cp312-win32.whl", hash = "sha256:b3cf819094383ec883e9a63c11b81d622618c815c18a6c9d761d9a14d9f028d1"}, + {file = "sspilib-0.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:b83825a2c43ff84ddff72d09b098057efaabf3841d3c42888078e154cf8e9595"}, + {file = "sspilib-0.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:9aa6ab4c3fc1057251cf1f3f199daf90b99599cdfafc9eade8fdf0c01526dec8"}, + {file = "sspilib-0.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:82bff5df178386027d0112458b6971bbd18c76eb9e7be53fd61dab33d7bf8417"}, + {file = "sspilib-0.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:18393a9e6e0447cb7f319d361b65e9a0eaa5484705f16787133ffc49ad364c28"}, + {file = "sspilib-0.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88a423fbca206ba0ca811dc995d8c3af045402b7d330f033e938b24f3a1d93fc"}, + {file = "sspilib-0.1.0-cp38-cp38-win32.whl", hash = "sha256:86bd936b1ef0aa63c6d9623ad08473e74ceb15f342f6e92cbade15ed9574cd33"}, + {file = "sspilib-0.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:d4f688b94f0a64128444063e1d3d59152614175999222f6e2920681faea833f4"}, + {file = "sspilib-0.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2acef24e13e40d9dd8697eaae84ead9f417528ff741d087ec4eb4260518f4dc7"}, + {file = "sspilib-0.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4b625802d80144d856d5eb6e8f4412f186565758da4493c7ad1b88e3d6d353de"}, + {file = "sspilib-0.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c06ca1e34702bca1c750dcb5133b716f316b38dccb28d55a1a44d9842bc3f391"}, + {file = "sspilib-0.1.0-cp39-cp39-win32.whl", hash = "sha256:68496c9bd52b57a1b6d2e5529b43c30060249b8db901127b8343c4ad8cd93670"}, + {file = "sspilib-0.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:369727097f07a440099882580e284e137d9c27b7de354d63b65e327a454e7bee"}, + {file = "sspilib-0.1.0-cp39-cp39-win_arm64.whl", hash = "sha256:87d8268c0517149c51a53b3888961ebf66826bb3dbb82c4e5cf10108f5456104"}, + {file = "sspilib-0.1.0.tar.gz", hash = "sha256:58b5291553cf6220549c0f855e0e6973f4977375d8236ce47bb581efb3e9b1cf"}, +] + +[package.source] +type = "legacy" +url = "https://pypi.tuna.tsinghua.edu.cn/simple" +reference = "tsinghua" + [[package]] name = "stack-data" -version = "0.6.2" +version = "0.6.3" description = "Extract data from python stack frames and tracebacks for informative displays" optional = false python-versions = "*" files = [ - {file = "stack_data-0.6.2-py3-none-any.whl", hash = "sha256:cbb2a53eb64e5785878201a97ed7c7b94883f48b87bfb0bbe8b623c74679e4a8"}, - {file = "stack_data-0.6.2.tar.gz", hash = "sha256:32d2dd0376772d01b6cb9fc996f3c8b57a357089dec328ed4b6553d037eaf815"}, + {file = "stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695"}, + {file = "stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9"}, ] [package.dependencies] @@ -6486,13 +6597,13 @@ reference = "tsinghua" [[package]] name = "texttable" -version = "1.6.7" +version = "1.7.0" description = "module to create simple ASCII tables" optional = false python-versions = "*" files = [ - {file = "texttable-1.6.7-py2.py3-none-any.whl", hash = "sha256:b7b68139aa8a6339d2c320ca8b1dc42d13a7831a346b446cb9eb385f0c76310c"}, - {file = "texttable-1.6.7.tar.gz", hash = "sha256:290348fb67f7746931bcdfd55ac7584ecd4e5b0846ab164333f0794b121760f2"}, + {file = "texttable-1.7.0-py2.py3-none-any.whl", hash = "sha256:72227d592c82b3d7f672731ae73e4d1f88cd8e2ef5b075a7a7f01a23a3743917"}, + {file = "texttable-1.7.0.tar.gz", hash = "sha256:2d2068fb55115807d3ac77a4ca68fa48803e84ebb0ee2340f858107a36522638"}, ] [package.source] @@ -6527,18 +6638,18 @@ reference = "tsinghua" [[package]] name = "traitlets" -version = "5.9.0" +version = "5.14.0" description = "Traitlets Python configuration system" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "traitlets-5.9.0-py3-none-any.whl", hash = "sha256:9e6ec080259b9a5940c797d58b613b5e31441c2257b87c2e795c5228ae80d2d8"}, - {file = "traitlets-5.9.0.tar.gz", hash = "sha256:f6cde21a9c68cf756af02035f72d5a723bf607e862e7be33ece505abf4a3bad9"}, + {file = "traitlets-5.14.0-py3-none-any.whl", hash = "sha256:f14949d23829023013c47df20b4a76ccd1a85effb786dc060f34de7948361b33"}, + {file = "traitlets-5.14.0.tar.gz", hash = "sha256:fcdaa8ac49c04dfa0ed3ee3384ef6dfdb5d6f3741502be247279407679296772"}, ] [package.extras] docs = ["myst-parser", "pydata-sphinx-theme", "sphinx"] -test = ["argcomplete (>=2.0)", "pre-commit", "pytest", "pytest-mock"] +test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0,<7.5)", "pytest-mock", "pytest-mypy-testing"] [package.source] type = "legacy" @@ -6566,44 +6677,42 @@ reference = "tsinghua" [[package]] name = "twisted" -version = "22.10.0" +version = "23.10.0" description = "An asynchronous networking framework written in Python" optional = false -python-versions = ">=3.7.1" +python-versions = ">=3.8.0" files = [ - {file = "Twisted-22.10.0-py3-none-any.whl", hash = "sha256:86c55f712cc5ab6f6d64e02503352464f0400f66d4f079096d744080afcccbd0"}, - {file = "Twisted-22.10.0.tar.gz", hash = "sha256:32acbd40a94f5f46e7b42c109bfae2b302250945561783a8b7a059048f2d4d31"}, + {file = "twisted-23.10.0-py3-none-any.whl", hash = "sha256:4ae8bce12999a35f7fe6443e7f1893e6fe09588c8d2bed9c35cdce8ff2d5b444"}, + {file = "twisted-23.10.0.tar.gz", hash = "sha256:987847a0790a2c597197613686e2784fd54167df3a55d0fb17c8412305d76ce5"}, ] [package.dependencies] -attrs = ">=19.2.0" -Automat = ">=0.8.0" +attrs = ">=21.3.0" +automat = ">=0.8.0" constantly = ">=15.1" hyperlink = ">=17.1.1" idna = {version = ">=2.4", optional = true, markers = "extra == \"tls\""} -incremental = ">=21.3.0" +incremental = ">=22.10.0" pyopenssl = {version = ">=21.0.0", optional = true, markers = "extra == \"tls\""} service-identity = {version = ">=18.1.0", optional = true, markers = "extra == \"tls\""} twisted-iocpsupport = {version = ">=1.0.2,<2", markers = "platform_system == \"Windows\""} -typing-extensions = ">=3.6.5" -"zope.interface" = ">=4.4.2" +typing-extensions = ">=4.2.0" +zope-interface = ">=5" [package.extras] -all-non-platform = ["PyHamcrest (>=1.9.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.0.0)", "contextvars (>=2.4,<3)", "cryptography (>=2.6)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.0,<7.0)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "pyasn1", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "service-identity (>=18.1.0)"] -conch = ["appdirs (>=1.4.0)", "bcrypt (>=3.0.0)", "cryptography (>=2.6)", "pyasn1"] -conch-nacl = ["PyNaCl", "appdirs (>=1.4.0)", "bcrypt (>=3.0.0)", "cryptography (>=2.6)", "pyasn1"] -contextvars = ["contextvars (>=2.4,<3)"] -dev = ["coverage (>=6b1,<7)", "pydoctor (>=22.9.0,<22.10.0)", "pyflakes (>=2.2,<3.0)", "python-subunit (>=1.4,<2.0)", "readthedocs-sphinx-ext (>=2.1,<3.0)", "sphinx (>=5.0,<6)", "sphinx-rtd-theme (>=1.0,<2.0)", "towncrier (>=22.8,<23.0)", "twistedchecker (>=0.7,<1.0)"] -dev-release = ["pydoctor (>=22.9.0,<22.10.0)", "readthedocs-sphinx-ext (>=2.1,<3.0)", "sphinx (>=5.0,<6)", "sphinx-rtd-theme (>=1.0,<2.0)", "towncrier (>=22.8,<23.0)"] -gtk-platform = ["PyHamcrest (>=1.9.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.0.0)", "contextvars (>=2.4,<3)", "cryptography (>=2.6)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.0,<7.0)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "pyasn1", "pygobject", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "service-identity (>=18.1.0)"] +all-non-platform = ["twisted[conch,http2,serial,test,tls]", "twisted[conch,http2,serial,test,tls]"] +conch = ["appdirs (>=1.4.0)", "bcrypt (>=3.1.3)", "cryptography (>=3.3)"] +dev = ["coverage (>=6b1,<7)", "pyflakes (>=2.2,<3.0)", "python-subunit (>=1.4,<2.0)", "twisted[dev-release]", "twistedchecker (>=0.7,<1.0)"] +dev-release = ["pydoctor (>=23.9.0,<23.10.0)", "pydoctor (>=23.9.0,<23.10.0)", "sphinx (>=6,<7)", "sphinx (>=6,<7)", "sphinx-rtd-theme (>=1.3,<2.0)", "sphinx-rtd-theme (>=1.3,<2.0)", "towncrier (>=23.6,<24.0)", "towncrier (>=23.6,<24.0)"] +gtk-platform = ["pygobject", "pygobject", "twisted[all-non-platform]", "twisted[all-non-platform]"] http2 = ["h2 (>=3.0,<5.0)", "priority (>=1.1.0,<2.0)"] -macos-platform = ["PyHamcrest (>=1.9.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.0.0)", "contextvars (>=2.4,<3)", "cryptography (>=2.6)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.0,<7.0)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "pyasn1", "pyobjc-core", "pyobjc-framework-CFNetwork", "pyobjc-framework-Cocoa", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "service-identity (>=18.1.0)"] -mypy = ["PyHamcrest (>=1.9.0)", "PyNaCl", "appdirs (>=1.4.0)", "bcrypt (>=3.0.0)", "contextvars (>=2.4,<3)", "coverage (>=6b1,<7)", "cryptography (>=2.6)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.0,<7.0)", "idna (>=2.4)", "mypy (==0.930)", "mypy-zope (==0.3.4)", "priority (>=1.1.0,<2.0)", "pyasn1", "pydoctor (>=22.9.0,<22.10.0)", "pyflakes (>=2.2,<3.0)", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "python-subunit (>=1.4,<2.0)", "pywin32 (!=226)", "readthedocs-sphinx-ext (>=2.1,<3.0)", "service-identity (>=18.1.0)", "sphinx (>=5.0,<6)", "sphinx-rtd-theme (>=1.0,<2.0)", "towncrier (>=22.8,<23.0)", "twistedchecker (>=0.7,<1.0)", "types-pyOpenSSL", "types-setuptools"] -osx-platform = ["PyHamcrest (>=1.9.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.0.0)", "contextvars (>=2.4,<3)", "cryptography (>=2.6)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.0,<7.0)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "pyasn1", "pyobjc-core", "pyobjc-framework-CFNetwork", "pyobjc-framework-Cocoa", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "service-identity (>=18.1.0)"] +macos-platform = ["pyobjc-core", "pyobjc-core", "pyobjc-framework-cfnetwork", "pyobjc-framework-cfnetwork", "pyobjc-framework-cocoa", "pyobjc-framework-cocoa", "twisted[all-non-platform]", "twisted[all-non-platform]"] +mypy = ["mypy (>=1.5.1,<1.6.0)", "mypy-zope (>=1.0.1,<1.1.0)", "twisted[all-non-platform,dev]", "types-pyopenssl", "types-setuptools"] +osx-platform = ["twisted[macos-platform]", "twisted[macos-platform]"] serial = ["pyserial (>=3.0)", "pywin32 (!=226)"] -test = ["PyHamcrest (>=1.9.0)", "cython-test-exception-raiser (>=1.0.2,<2)", "hypothesis (>=6.0,<7.0)"] +test = ["cython-test-exception-raiser (>=1.0.2,<2)", "hypothesis (>=6.56)", "pyhamcrest (>=2)"] tls = ["idna (>=2.4)", "pyopenssl (>=21.0.0)", "service-identity (>=18.1.0)"] -windows-platform = ["PyHamcrest (>=1.9.0)", "appdirs (>=1.4.0)", "bcrypt (>=3.0.0)", "contextvars (>=2.4,<3)", "cryptography (>=2.6)", "cython-test-exception-raiser (>=1.0.2,<2)", "h2 (>=3.0,<5.0)", "hypothesis (>=6.0,<7.0)", "idna (>=2.4)", "priority (>=1.1.0,<2.0)", "pyasn1", "pyopenssl (>=21.0.0)", "pyserial (>=3.0)", "pywin32 (!=226)", "pywin32 (!=226)", "service-identity (>=18.1.0)"] +windows-platform = ["pywin32 (!=226)", "pywin32 (!=226)", "twisted[all-non-platform]", "twisted[all-non-platform]"] [package.source] type = "legacy" @@ -6666,13 +6775,13 @@ reference = "tsinghua" [[package]] name = "typing-extensions" -version = "4.7.1" -description = "Backported and Experimental Type Hints for Python 3.7+" +version = "4.8.0" +description = "Backported and Experimental Type Hints for Python 3.8+" optional = false -python-versions = ">=3.7" +python-versions = ">=3.8" files = [ - {file = "typing_extensions-4.7.1-py3-none-any.whl", hash = "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36"}, - {file = "typing_extensions-4.7.1.tar.gz", hash = "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"}, + {file = "typing_extensions-4.8.0-py3-none-any.whl", hash = "sha256:8f92fc8806f9a6b641eaa5318da32b44d401efaac0f6678c9bc448ba3605faa0"}, + {file = "typing_extensions-4.8.0.tar.gz", hash = "sha256:df8e4339e9cb77357558cbdbceca33c303714cf861d1eef15e1070055ae8b7ef"}, ] [package.source] @@ -6849,13 +6958,13 @@ reference = "tsinghua" [[package]] name = "wcwidth" -version = "0.2.6" +version = "0.2.12" description = "Measures the displayed width of unicode strings in a terminal" optional = false python-versions = "*" files = [ - {file = "wcwidth-0.2.6-py2.py3-none-any.whl", hash = "sha256:795b138f6875577cd91bba52baf9e445cd5118fd32723b460e30a0af30ea230e"}, - {file = "wcwidth-0.2.6.tar.gz", hash = "sha256:a5220780a404dbe3353789870978e472cfe477761f06ee55077256e509b156d0"}, + {file = "wcwidth-0.2.12-py2.py3-none-any.whl", hash = "sha256:f26ec43d96c8cbfed76a5075dac87680124fa84e0855195a6184da9c187f133c"}, + {file = "wcwidth-0.2.12.tar.gz", hash = "sha256:f01c104efdf57971bcb756f054dd58ddec5204dd15fa31d6503ea57947d97c02"}, ] [package.source] @@ -6992,86 +7101,81 @@ reference = "tsinghua" [[package]] name = "wrapt" -version = "1.15.0" +version = "1.16.0" description = "Module for decorators, wrappers and monkey patching." optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +python-versions = ">=3.6" files = [ - {file = "wrapt-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:ca1cccf838cd28d5a0883b342474c630ac48cac5df0ee6eacc9c7290f76b11c1"}, - {file = "wrapt-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e826aadda3cae59295b95343db8f3d965fb31059da7de01ee8d1c40a60398b29"}, - {file = "wrapt-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5fc8e02f5984a55d2c653f5fea93531e9836abbd84342c1d1e17abc4a15084c2"}, - {file = "wrapt-1.15.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:96e25c8603a155559231c19c0349245eeb4ac0096fe3c1d0be5c47e075bd4f46"}, - {file = "wrapt-1.15.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:40737a081d7497efea35ab9304b829b857f21558acfc7b3272f908d33b0d9d4c"}, - {file = "wrapt-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:f87ec75864c37c4c6cb908d282e1969e79763e0d9becdfe9fe5473b7bb1e5f09"}, - {file = "wrapt-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:1286eb30261894e4c70d124d44b7fd07825340869945c79d05bda53a40caa079"}, - {file = "wrapt-1.15.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:493d389a2b63c88ad56cdc35d0fa5752daac56ca755805b1b0c530f785767d5e"}, - {file = "wrapt-1.15.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:58d7a75d731e8c63614222bcb21dd992b4ab01a399f1f09dd82af17bbfc2368a"}, - {file = "wrapt-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:21f6d9a0d5b3a207cdf7acf8e58d7d13d463e639f0c7e01d82cdb671e6cb7923"}, - {file = "wrapt-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ce42618f67741d4697684e501ef02f29e758a123aa2d669e2d964ff734ee00ee"}, - {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41d07d029dd4157ae27beab04d22b8e261eddfc6ecd64ff7000b10dc8b3a5727"}, - {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:54accd4b8bc202966bafafd16e69da9d5640ff92389d33d28555c5fd4f25ccb7"}, - {file = "wrapt-1.15.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2fbfbca668dd15b744418265a9607baa970c347eefd0db6a518aaf0cfbd153c0"}, - {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:76e9c727a874b4856d11a32fb0b389afc61ce8aaf281ada613713ddeadd1cfec"}, - {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:e20076a211cd6f9b44a6be58f7eeafa7ab5720eb796975d0c03f05b47d89eb90"}, - {file = "wrapt-1.15.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a74d56552ddbde46c246b5b89199cb3fd182f9c346c784e1a93e4dc3f5ec9975"}, - {file = "wrapt-1.15.0-cp310-cp310-win32.whl", hash = "sha256:26458da5653aa5b3d8dc8b24192f574a58984c749401f98fff994d41d3f08da1"}, - {file = "wrapt-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:75760a47c06b5974aa5e01949bf7e66d2af4d08cb8c1d6516af5e39595397f5e"}, - {file = "wrapt-1.15.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ba1711cda2d30634a7e452fc79eabcadaffedf241ff206db2ee93dd2c89a60e7"}, - {file = "wrapt-1.15.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:56374914b132c702aa9aa9959c550004b8847148f95e1b824772d453ac204a72"}, - {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a89ce3fd220ff144bd9d54da333ec0de0399b52c9ac3d2ce34b569cf1a5748fb"}, - {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3bbe623731d03b186b3d6b0d6f51865bf598587c38d6f7b0be2e27414f7f214e"}, - {file = "wrapt-1.15.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3abbe948c3cbde2689370a262a8d04e32ec2dd4f27103669a45c6929bcdbfe7c"}, - {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b67b819628e3b748fd3c2192c15fb951f549d0f47c0449af0764d7647302fda3"}, - {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:7eebcdbe3677e58dd4c0e03b4f2cfa346ed4049687d839adad68cc38bb559c92"}, - {file = "wrapt-1.15.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:74934ebd71950e3db69960a7da29204f89624dde411afbfb3b4858c1409b1e98"}, - {file = "wrapt-1.15.0-cp311-cp311-win32.whl", hash = "sha256:bd84395aab8e4d36263cd1b9308cd504f6cf713b7d6d3ce25ea55670baec5416"}, - {file = "wrapt-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:a487f72a25904e2b4bbc0817ce7a8de94363bd7e79890510174da9d901c38705"}, - {file = "wrapt-1.15.0-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:4ff0d20f2e670800d3ed2b220d40984162089a6e2c9646fdb09b85e6f9a8fc29"}, - {file = "wrapt-1.15.0-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9ed6aa0726b9b60911f4aed8ec5b8dd7bf3491476015819f56473ffaef8959bd"}, - {file = "wrapt-1.15.0-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:896689fddba4f23ef7c718279e42f8834041a21342d95e56922e1c10c0cc7afb"}, - {file = "wrapt-1.15.0-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:75669d77bb2c071333417617a235324a1618dba66f82a750362eccbe5b61d248"}, - {file = "wrapt-1.15.0-cp35-cp35m-win32.whl", hash = "sha256:fbec11614dba0424ca72f4e8ba3c420dba07b4a7c206c8c8e4e73f2e98f4c559"}, - {file = "wrapt-1.15.0-cp35-cp35m-win_amd64.whl", hash = "sha256:fd69666217b62fa5d7c6aa88e507493a34dec4fa20c5bd925e4bc12fce586639"}, - {file = "wrapt-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b0724f05c396b0a4c36a3226c31648385deb6a65d8992644c12a4963c70326ba"}, - {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bbeccb1aa40ab88cd29e6c7d8585582c99548f55f9b2581dfc5ba68c59a85752"}, - {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:38adf7198f8f154502883242f9fe7333ab05a5b02de7d83aa2d88ea621f13364"}, - {file = "wrapt-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:578383d740457fa790fdf85e6d346fda1416a40549fe8db08e5e9bd281c6a475"}, - {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:a4cbb9ff5795cd66f0066bdf5947f170f5d63a9274f99bdbca02fd973adcf2a8"}, - {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:af5bd9ccb188f6a5fdda9f1f09d9f4c86cc8a539bd48a0bfdc97723970348418"}, - {file = "wrapt-1.15.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:b56d5519e470d3f2fe4aa7585f0632b060d532d0696c5bdfb5e8319e1d0f69a2"}, - {file = "wrapt-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:77d4c1b881076c3ba173484dfa53d3582c1c8ff1f914c6461ab70c8428b796c1"}, - {file = "wrapt-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:077ff0d1f9d9e4ce6476c1a924a3332452c1406e59d90a2cf24aeb29eeac9420"}, - {file = "wrapt-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5c5aa28df055697d7c37d2099a7bc09f559d5053c3349b1ad0c39000e611d317"}, - {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3a8564f283394634a7a7054b7983e47dbf39c07712d7b177b37e03f2467a024e"}, - {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:780c82a41dc493b62fc5884fb1d3a3b81106642c5c5c78d6a0d4cbe96d62ba7e"}, - {file = "wrapt-1.15.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e169e957c33576f47e21864cf3fc9ff47c223a4ebca8960079b8bd36cb014fd0"}, - {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b02f21c1e2074943312d03d243ac4388319f2456576b2c6023041c4d57cd7019"}, - {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:f2e69b3ed24544b0d3dbe2c5c0ba5153ce50dcebb576fdc4696d52aa22db6034"}, - {file = "wrapt-1.15.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:d787272ed958a05b2c86311d3a4135d3c2aeea4fc655705f074130aa57d71653"}, - {file = "wrapt-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:02fce1852f755f44f95af51f69d22e45080102e9d00258053b79367d07af39c0"}, - {file = "wrapt-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:abd52a09d03adf9c763d706df707c343293d5d106aea53483e0ec8d9e310ad5e"}, - {file = "wrapt-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:cdb4f085756c96a3af04e6eca7f08b1345e94b53af8921b25c72f096e704e145"}, - {file = "wrapt-1.15.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:230ae493696a371f1dbffaad3dafbb742a4d27a0afd2b1aecebe52b740167e7f"}, - {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:63424c681923b9f3bfbc5e3205aafe790904053d42ddcc08542181a30a7a51bd"}, - {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d6bcbfc99f55655c3d93feb7ef3800bd5bbe963a755687cbf1f490a71fb7794b"}, - {file = "wrapt-1.15.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c99f4309f5145b93eca6e35ac1a988f0dc0a7ccf9ccdcd78d3c0adf57224e62f"}, - {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:b130fe77361d6771ecf5a219d8e0817d61b236b7d8b37cc045172e574ed219e6"}, - {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:96177eb5645b1c6985f5c11d03fc2dbda9ad24ec0f3a46dcce91445747e15094"}, - {file = "wrapt-1.15.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5fe3e099cf07d0fb5a1e23d399e5d4d1ca3e6dfcbe5c8570ccff3e9208274f7"}, - {file = "wrapt-1.15.0-cp38-cp38-win32.whl", hash = "sha256:abd8f36c99512755b8456047b7be10372fca271bf1467a1caa88db991e7c421b"}, - {file = "wrapt-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:b06fa97478a5f478fb05e1980980a7cdf2712015493b44d0c87606c1513ed5b1"}, - {file = "wrapt-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2e51de54d4fb8fb50d6ee8327f9828306a959ae394d3e01a1ba8b2f937747d86"}, - {file = "wrapt-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0970ddb69bba00670e58955f8019bec4a42d1785db3faa043c33d81de2bf843c"}, - {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76407ab327158c510f44ded207e2f76b657303e17cb7a572ffe2f5a8a48aa04d"}, - {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cd525e0e52a5ff16653a3fc9e3dd827981917d34996600bbc34c05d048ca35cc"}, - {file = "wrapt-1.15.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d37ac69edc5614b90516807de32d08cb8e7b12260a285ee330955604ed9dd29"}, - {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:078e2a1a86544e644a68422f881c48b84fef6d18f8c7a957ffd3f2e0a74a0d4a"}, - {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2cf56d0e237280baed46f0b5316661da892565ff58309d4d2ed7dba763d984b8"}, - {file = "wrapt-1.15.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7dc0713bf81287a00516ef43137273b23ee414fe41a3c14be10dd95ed98a2df9"}, - {file = "wrapt-1.15.0-cp39-cp39-win32.whl", hash = "sha256:46ed616d5fb42f98630ed70c3529541408166c22cdfd4540b88d5f21006b0eff"}, - {file = "wrapt-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:eef4d64c650f33347c1f9266fa5ae001440b232ad9b98f1f43dfe7a79435c0a6"}, - {file = "wrapt-1.15.0-py3-none-any.whl", hash = "sha256:64b1df0f83706b4ef4cfb4fb0e4c2669100fd7ecacfb59e091fad300d4e04640"}, - {file = "wrapt-1.15.0.tar.gz", hash = "sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a"}, + {file = "wrapt-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4"}, + {file = "wrapt-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020"}, + {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440"}, + {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487"}, + {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf"}, + {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72"}, + {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0"}, + {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136"}, + {file = "wrapt-1.16.0-cp310-cp310-win32.whl", hash = "sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d"}, + {file = "wrapt-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2"}, + {file = "wrapt-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09"}, + {file = "wrapt-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d"}, + {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389"}, + {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060"}, + {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1"}, + {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3"}, + {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956"}, + {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d"}, + {file = "wrapt-1.16.0-cp311-cp311-win32.whl", hash = "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362"}, + {file = "wrapt-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89"}, + {file = "wrapt-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b"}, + {file = "wrapt-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36"}, + {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73"}, + {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809"}, + {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b"}, + {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81"}, + {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9"}, + {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c"}, + {file = "wrapt-1.16.0-cp312-cp312-win32.whl", hash = "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc"}, + {file = "wrapt-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8"}, + {file = "wrapt-1.16.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8"}, + {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39"}, + {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c"}, + {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40"}, + {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc"}, + {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e"}, + {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465"}, + {file = "wrapt-1.16.0-cp36-cp36m-win32.whl", hash = "sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e"}, + {file = "wrapt-1.16.0-cp36-cp36m-win_amd64.whl", hash = "sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966"}, + {file = "wrapt-1.16.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593"}, + {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292"}, + {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5"}, + {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf"}, + {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228"}, + {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f"}, + {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c"}, + {file = "wrapt-1.16.0-cp37-cp37m-win32.whl", hash = "sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c"}, + {file = "wrapt-1.16.0-cp37-cp37m-win_amd64.whl", hash = "sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00"}, + {file = "wrapt-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0"}, + {file = "wrapt-1.16.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202"}, + {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0"}, + {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e"}, + {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f"}, + {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267"}, + {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca"}, + {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6"}, + {file = "wrapt-1.16.0-cp38-cp38-win32.whl", hash = "sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b"}, + {file = "wrapt-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41"}, + {file = "wrapt-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2"}, + {file = "wrapt-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb"}, + {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8"}, + {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c"}, + {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a"}, + {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664"}, + {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f"}, + {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537"}, + {file = "wrapt-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3"}, + {file = "wrapt-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35"}, + {file = "wrapt-1.16.0-py3-none-any.whl", hash = "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1"}, + {file = "wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d"}, ] [package.source] @@ -7127,85 +7231,101 @@ reference = "tsinghua" [[package]] name = "yarl" -version = "1.9.2" +version = "1.9.3" description = "Yet another URL library" optional = false python-versions = ">=3.7" files = [ - {file = "yarl-1.9.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8c2ad583743d16ddbdf6bb14b5cd76bf43b0d0006e918809d5d4ddf7bde8dd82"}, - {file = "yarl-1.9.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:82aa6264b36c50acfb2424ad5ca537a2060ab6de158a5bd2a72a032cc75b9eb8"}, - {file = "yarl-1.9.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c0c77533b5ed4bcc38e943178ccae29b9bcf48ffd1063f5821192f23a1bd27b9"}, - {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ee4afac41415d52d53a9833ebae7e32b344be72835bbb589018c9e938045a560"}, - {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9bf345c3a4f5ba7f766430f97f9cc1320786f19584acc7086491f45524a551ac"}, - {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2a96c19c52ff442a808c105901d0bdfd2e28575b3d5f82e2f5fd67e20dc5f4ea"}, - {file = "yarl-1.9.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:891c0e3ec5ec881541f6c5113d8df0315ce5440e244a716b95f2525b7b9f3608"}, - {file = "yarl-1.9.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c3a53ba34a636a256d767c086ceb111358876e1fb6b50dfc4d3f4951d40133d5"}, - {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:566185e8ebc0898b11f8026447eacd02e46226716229cea8db37496c8cdd26e0"}, - {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:2b0738fb871812722a0ac2154be1f049c6223b9f6f22eec352996b69775b36d4"}, - {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:32f1d071b3f362c80f1a7d322bfd7b2d11e33d2adf395cc1dd4df36c9c243095"}, - {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:e9fdc7ac0d42bc3ea78818557fab03af6181e076a2944f43c38684b4b6bed8e3"}, - {file = "yarl-1.9.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:56ff08ab5df8429901ebdc5d15941b59f6253393cb5da07b4170beefcf1b2528"}, - {file = "yarl-1.9.2-cp310-cp310-win32.whl", hash = "sha256:8ea48e0a2f931064469bdabca50c2f578b565fc446f302a79ba6cc0ee7f384d3"}, - {file = "yarl-1.9.2-cp310-cp310-win_amd64.whl", hash = "sha256:50f33040f3836e912ed16d212f6cc1efb3231a8a60526a407aeb66c1c1956dde"}, - {file = "yarl-1.9.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:646d663eb2232d7909e6601f1a9107e66f9791f290a1b3dc7057818fe44fc2b6"}, - {file = "yarl-1.9.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aff634b15beff8902d1f918012fc2a42e0dbae6f469fce134c8a0dc51ca423bb"}, - {file = "yarl-1.9.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a83503934c6273806aed765035716216cc9ab4e0364f7f066227e1aaea90b8d0"}, - {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b25322201585c69abc7b0e89e72790469f7dad90d26754717f3310bfe30331c2"}, - {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:22a94666751778629f1ec4280b08eb11815783c63f52092a5953faf73be24191"}, - {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8ec53a0ea2a80c5cd1ab397925f94bff59222aa3cf9c6da938ce05c9ec20428d"}, - {file = "yarl-1.9.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:159d81f22d7a43e6eabc36d7194cb53f2f15f498dbbfa8edc8a3239350f59fe7"}, - {file = "yarl-1.9.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:832b7e711027c114d79dffb92576acd1bd2decc467dec60e1cac96912602d0e6"}, - {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:95d2ecefbcf4e744ea952d073c6922e72ee650ffc79028eb1e320e732898d7e8"}, - {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:d4e2c6d555e77b37288eaf45b8f60f0737c9efa3452c6c44626a5455aeb250b9"}, - {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:783185c75c12a017cc345015ea359cc801c3b29a2966c2655cd12b233bf5a2be"}, - {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:b8cc1863402472f16c600e3e93d542b7e7542a540f95c30afd472e8e549fc3f7"}, - {file = "yarl-1.9.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:822b30a0f22e588b32d3120f6d41e4ed021806418b4c9f0bc3048b8c8cb3f92a"}, - {file = "yarl-1.9.2-cp311-cp311-win32.whl", hash = "sha256:a60347f234c2212a9f0361955007fcf4033a75bf600a33c88a0a8e91af77c0e8"}, - {file = "yarl-1.9.2-cp311-cp311-win_amd64.whl", hash = "sha256:be6b3fdec5c62f2a67cb3f8c6dbf56bbf3f61c0f046f84645cd1ca73532ea051"}, - {file = "yarl-1.9.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:38a3928ae37558bc1b559f67410df446d1fbfa87318b124bf5032c31e3447b74"}, - {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ac9bb4c5ce3975aeac288cfcb5061ce60e0d14d92209e780c93954076c7c4367"}, - {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3da8a678ca8b96c8606bbb8bfacd99a12ad5dd288bc6f7979baddd62f71c63ef"}, - {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:13414591ff516e04fcdee8dc051c13fd3db13b673c7a4cb1350e6b2ad9639ad3"}, - {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf74d08542c3a9ea97bb8f343d4fcbd4d8f91bba5ec9d5d7f792dbe727f88938"}, - {file = "yarl-1.9.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6e7221580dc1db478464cfeef9b03b95c5852cc22894e418562997df0d074ccc"}, - {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:494053246b119b041960ddcd20fd76224149cfea8ed8777b687358727911dd33"}, - {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:52a25809fcbecfc63ac9ba0c0fb586f90837f5425edfd1ec9f3372b119585e45"}, - {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:e65610c5792870d45d7b68c677681376fcf9cc1c289f23e8e8b39c1485384185"}, - {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:1b1bba902cba32cdec51fca038fd53f8beee88b77efc373968d1ed021024cc04"}, - {file = "yarl-1.9.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:662e6016409828ee910f5d9602a2729a8a57d74b163c89a837de3fea050c7582"}, - {file = "yarl-1.9.2-cp37-cp37m-win32.whl", hash = "sha256:f364d3480bffd3aa566e886587eaca7c8c04d74f6e8933f3f2c996b7f09bee1b"}, - {file = "yarl-1.9.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6a5883464143ab3ae9ba68daae8e7c5c95b969462bbe42e2464d60e7e2698368"}, - {file = "yarl-1.9.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:5610f80cf43b6202e2c33ba3ec2ee0a2884f8f423c8f4f62906731d876ef4fac"}, - {file = "yarl-1.9.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b9a4e67ad7b646cd6f0938c7ebfd60e481b7410f574c560e455e938d2da8e0f4"}, - {file = "yarl-1.9.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:83fcc480d7549ccebe9415d96d9263e2d4226798c37ebd18c930fce43dfb9574"}, - {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5fcd436ea16fee7d4207c045b1e340020e58a2597301cfbcfdbe5abd2356c2fb"}, - {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:84e0b1599334b1e1478db01b756e55937d4614f8654311eb26012091be109d59"}, - {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3458a24e4ea3fd8930e934c129b676c27452e4ebda80fbe47b56d8c6c7a63a9e"}, - {file = "yarl-1.9.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:838162460b3a08987546e881a2bfa573960bb559dfa739e7800ceeec92e64417"}, - {file = "yarl-1.9.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f4e2d08f07a3d7d3e12549052eb5ad3eab1c349c53ac51c209a0e5991bbada78"}, - {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:de119f56f3c5f0e2fb4dee508531a32b069a5f2c6e827b272d1e0ff5ac040333"}, - {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:149ddea5abf329752ea5051b61bd6c1d979e13fbf122d3a1f9f0c8be6cb6f63c"}, - {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:674ca19cbee4a82c9f54e0d1eee28116e63bc6fd1e96c43031d11cbab8b2afd5"}, - {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:9b3152f2f5677b997ae6c804b73da05a39daa6a9e85a512e0e6823d81cdad7cc"}, - {file = "yarl-1.9.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5415d5a4b080dc9612b1b63cba008db84e908b95848369aa1da3686ae27b6d2b"}, - {file = "yarl-1.9.2-cp38-cp38-win32.whl", hash = "sha256:f7a3d8146575e08c29ed1cd287068e6d02f1c7bdff8970db96683b9591b86ee7"}, - {file = "yarl-1.9.2-cp38-cp38-win_amd64.whl", hash = "sha256:63c48f6cef34e6319a74c727376e95626f84ea091f92c0250a98e53e62c77c72"}, - {file = "yarl-1.9.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:75df5ef94c3fdc393c6b19d80e6ef1ecc9ae2f4263c09cacb178d871c02a5ba9"}, - {file = "yarl-1.9.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c027a6e96ef77d401d8d5a5c8d6bc478e8042f1e448272e8d9752cb0aff8b5c8"}, - {file = "yarl-1.9.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f3b078dbe227f79be488ffcfc7a9edb3409d018e0952cf13f15fd6512847f3f7"}, - {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:59723a029760079b7d991a401386390c4be5bfec1e7dd83e25a6a0881859e716"}, - {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b03917871bf859a81ccb180c9a2e6c1e04d2f6a51d953e6a5cdd70c93d4e5a2a"}, - {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c1012fa63eb6c032f3ce5d2171c267992ae0c00b9e164efe4d73db818465fac3"}, - {file = "yarl-1.9.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a74dcbfe780e62f4b5a062714576f16c2f3493a0394e555ab141bf0d746bb955"}, - {file = "yarl-1.9.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c56986609b057b4839968ba901944af91b8e92f1725d1a2d77cbac6972b9ed1"}, - {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2c315df3293cd521033533d242d15eab26583360b58f7ee5d9565f15fee1bef4"}, - {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:b7232f8dfbd225d57340e441d8caf8652a6acd06b389ea2d3222b8bc89cbfca6"}, - {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:53338749febd28935d55b41bf0bcc79d634881195a39f6b2f767870b72514caf"}, - {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:066c163aec9d3d073dc9ffe5dd3ad05069bcb03fcaab8d221290ba99f9f69ee3"}, - {file = "yarl-1.9.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8288d7cd28f8119b07dd49b7230d6b4562f9b61ee9a4ab02221060d21136be80"}, - {file = "yarl-1.9.2-cp39-cp39-win32.whl", hash = "sha256:b124e2a6d223b65ba8768d5706d103280914d61f5cae3afbc50fc3dfcc016623"}, - {file = "yarl-1.9.2-cp39-cp39-win_amd64.whl", hash = "sha256:61016e7d582bc46a5378ffdd02cd0314fb8ba52f40f9cf4d9a5e7dbef88dee18"}, - {file = "yarl-1.9.2.tar.gz", hash = "sha256:04ab9d4b9f587c06d801c2abfe9317b77cdf996c65a90d5e84ecc45010823571"}, + {file = "yarl-1.9.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:32435d134414e01d937cd9d6cc56e8413a8d4741dea36af5840c7750f04d16ab"}, + {file = "yarl-1.9.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:9a5211de242754b5e612557bca701f39f8b1a9408dff73c6db623f22d20f470e"}, + {file = "yarl-1.9.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:525cd69eff44833b01f8ef39aa33a9cc53a99ff7f9d76a6ef6a9fb758f54d0ff"}, + {file = "yarl-1.9.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc94441bcf9cb8c59f51f23193316afefbf3ff858460cb47b5758bf66a14d130"}, + {file = "yarl-1.9.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e36021db54b8a0475805acc1d6c4bca5d9f52c3825ad29ae2d398a9d530ddb88"}, + {file = "yarl-1.9.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e0f17d1df951336a02afc8270c03c0c6e60d1f9996fcbd43a4ce6be81de0bd9d"}, + {file = "yarl-1.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c5f3faeb8100a43adf3e7925d556801d14b5816a0ac9e75e22948e787feec642"}, + {file = "yarl-1.9.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aed37db837ecb5962469fad448aaae0f0ee94ffce2062cf2eb9aed13328b5196"}, + {file = "yarl-1.9.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:721ee3fc292f0d069a04016ef2c3a25595d48c5b8ddc6029be46f6158d129c92"}, + {file = "yarl-1.9.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:b8bc5b87a65a4e64bc83385c05145ea901b613d0d3a434d434b55511b6ab0067"}, + {file = "yarl-1.9.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:dd952b9c64f3b21aedd09b8fe958e4931864dba69926d8a90c90d36ac4e28c9a"}, + {file = "yarl-1.9.3-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:c405d482c320a88ab53dcbd98d6d6f32ada074f2d965d6e9bf2d823158fa97de"}, + {file = "yarl-1.9.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:9df9a0d4c5624790a0dea2e02e3b1b3c69aed14bcb8650e19606d9df3719e87d"}, + {file = "yarl-1.9.3-cp310-cp310-win32.whl", hash = "sha256:d34c4f80956227f2686ddea5b3585e109c2733e2d4ef12eb1b8b4e84f09a2ab6"}, + {file = "yarl-1.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:cf7a4e8de7f1092829caef66fd90eaf3710bc5efd322a816d5677b7664893c93"}, + {file = "yarl-1.9.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d61a0ca95503867d4d627517bcfdc28a8468c3f1b0b06c626f30dd759d3999fd"}, + {file = "yarl-1.9.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:73cc83f918b69110813a7d95024266072d987b903a623ecae673d1e71579d566"}, + {file = "yarl-1.9.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d81657b23e0edb84b37167e98aefb04ae16cbc5352770057893bd222cdc6e45f"}, + {file = "yarl-1.9.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26a1a8443091c7fbc17b84a0d9f38de34b8423b459fb853e6c8cdfab0eacf613"}, + {file = "yarl-1.9.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fe34befb8c765b8ce562f0200afda3578f8abb159c76de3ab354c80b72244c41"}, + {file = "yarl-1.9.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2c757f64afe53a422e45e3e399e1e3cf82b7a2f244796ce80d8ca53e16a49b9f"}, + {file = "yarl-1.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72a57b41a0920b9a220125081c1e191b88a4cdec13bf9d0649e382a822705c65"}, + {file = "yarl-1.9.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:632c7aeb99df718765adf58eacb9acb9cbc555e075da849c1378ef4d18bf536a"}, + {file = "yarl-1.9.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:b0b8c06afcf2bac5a50b37f64efbde978b7f9dc88842ce9729c020dc71fae4ce"}, + {file = "yarl-1.9.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1d93461e2cf76c4796355494f15ffcb50a3c198cc2d601ad8d6a96219a10c363"}, + {file = "yarl-1.9.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:4003f380dac50328c85e85416aca6985536812c082387255c35292cb4b41707e"}, + {file = "yarl-1.9.3-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:4d6d74a97e898c1c2df80339aa423234ad9ea2052f66366cef1e80448798c13d"}, + {file = "yarl-1.9.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b61e64b06c3640feab73fa4ff9cb64bd8182de52e5dc13038e01cfe674ebc321"}, + {file = "yarl-1.9.3-cp311-cp311-win32.whl", hash = "sha256:29beac86f33d6c7ab1d79bd0213aa7aed2d2f555386856bb3056d5fdd9dab279"}, + {file = "yarl-1.9.3-cp311-cp311-win_amd64.whl", hash = "sha256:f7271d6bd8838c49ba8ae647fc06469137e1c161a7ef97d778b72904d9b68696"}, + {file = "yarl-1.9.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:dd318e6b75ca80bff0b22b302f83a8ee41c62b8ac662ddb49f67ec97e799885d"}, + {file = "yarl-1.9.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c4b1efb11a8acd13246ffb0bee888dd0e8eb057f8bf30112e3e21e421eb82d4a"}, + {file = "yarl-1.9.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c6f034386e5550b5dc8ded90b5e2ff7db21f0f5c7de37b6efc5dac046eb19c10"}, + {file = "yarl-1.9.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd49a908cb6d387fc26acee8b7d9fcc9bbf8e1aca890c0b2fdfd706057546080"}, + {file = "yarl-1.9.3-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:aa4643635f26052401750bd54db911b6342eb1a9ac3e74f0f8b58a25d61dfe41"}, + {file = "yarl-1.9.3-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e741bd48e6a417bdfbae02e088f60018286d6c141639359fb8df017a3b69415a"}, + {file = "yarl-1.9.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7c86d0d0919952d05df880a1889a4f0aeb6868e98961c090e335671dea5c0361"}, + {file = "yarl-1.9.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3d5434b34100b504aabae75f0622ebb85defffe7b64ad8f52b8b30ec6ef6e4b9"}, + {file = "yarl-1.9.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:79e1df60f7c2b148722fb6cafebffe1acd95fd8b5fd77795f56247edaf326752"}, + {file = "yarl-1.9.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:44e91a669c43f03964f672c5a234ae0d7a4d49c9b85d1baa93dec28afa28ffbd"}, + {file = "yarl-1.9.3-cp312-cp312-musllinux_1_1_ppc64le.whl", hash = "sha256:3cfa4dbe17b2e6fca1414e9c3bcc216f6930cb18ea7646e7d0d52792ac196808"}, + {file = "yarl-1.9.3-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:88d2c3cc4b2f46d1ba73d81c51ec0e486f59cc51165ea4f789677f91a303a9a7"}, + {file = "yarl-1.9.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:cccdc02e46d2bd7cb5f38f8cc3d9db0d24951abd082b2f242c9e9f59c0ab2af3"}, + {file = "yarl-1.9.3-cp312-cp312-win32.whl", hash = "sha256:96758e56dceb8a70f8a5cff1e452daaeff07d1cc9f11e9b0c951330f0a2396a7"}, + {file = "yarl-1.9.3-cp312-cp312-win_amd64.whl", hash = "sha256:c4472fe53ebf541113e533971bd8c32728debc4c6d8cc177f2bff31d011ec17e"}, + {file = "yarl-1.9.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:126638ab961633f0940a06e1c9d59919003ef212a15869708dcb7305f91a6732"}, + {file = "yarl-1.9.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c99ddaddb2fbe04953b84d1651149a0d85214780e4d0ee824e610ab549d98d92"}, + {file = "yarl-1.9.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8dab30b21bd6fb17c3f4684868c7e6a9e8468078db00f599fb1c14e324b10fca"}, + {file = "yarl-1.9.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:828235a2a169160ee73a2fcfb8a000709edf09d7511fccf203465c3d5acc59e4"}, + {file = "yarl-1.9.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc391e3941045fd0987c77484b2799adffd08e4b6735c4ee5f054366a2e1551d"}, + {file = "yarl-1.9.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:51382c72dd5377861b573bd55dcf680df54cea84147c8648b15ac507fbef984d"}, + {file = "yarl-1.9.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:28a108cb92ce6cf867690a962372996ca332d8cda0210c5ad487fe996e76b8bb"}, + {file = "yarl-1.9.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:8f18a7832ff85dfcd77871fe677b169b1bc60c021978c90c3bb14f727596e0ae"}, + {file = "yarl-1.9.3-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:7eaf13af79950142ab2bbb8362f8d8d935be9aaf8df1df89c86c3231e4ff238a"}, + {file = "yarl-1.9.3-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:66a6dbf6ca7d2db03cc61cafe1ee6be838ce0fbc97781881a22a58a7c5efef42"}, + {file = "yarl-1.9.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:1a0a4f3aaa18580038cfa52a7183c8ffbbe7d727fe581300817efc1e96d1b0e9"}, + {file = "yarl-1.9.3-cp37-cp37m-win32.whl", hash = "sha256:946db4511b2d815979d733ac6a961f47e20a29c297be0d55b6d4b77ee4b298f6"}, + {file = "yarl-1.9.3-cp37-cp37m-win_amd64.whl", hash = "sha256:2dad8166d41ebd1f76ce107cf6a31e39801aee3844a54a90af23278b072f1ccf"}, + {file = "yarl-1.9.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:bb72d2a94481e7dc7a0c522673db288f31849800d6ce2435317376a345728225"}, + {file = "yarl-1.9.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9a172c3d5447b7da1680a1a2d6ecdf6f87a319d21d52729f45ec938a7006d5d8"}, + {file = "yarl-1.9.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:2dc72e891672343b99db6d497024bf8b985537ad6c393359dc5227ef653b2f17"}, + {file = "yarl-1.9.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b8d51817cf4b8d545963ec65ff06c1b92e5765aa98831678d0e2240b6e9fd281"}, + {file = "yarl-1.9.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:53ec65f7eee8655bebb1f6f1607760d123c3c115a324b443df4f916383482a67"}, + {file = "yarl-1.9.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cfd77e8e5cafba3fb584e0f4b935a59216f352b73d4987be3af51f43a862c403"}, + {file = "yarl-1.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e73db54c967eb75037c178a54445c5a4e7461b5203b27c45ef656a81787c0c1b"}, + {file = "yarl-1.9.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09c19e5f4404574fcfb736efecf75844ffe8610606f3fccc35a1515b8b6712c4"}, + {file = "yarl-1.9.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6280353940f7e5e2efaaabd686193e61351e966cc02f401761c4d87f48c89ea4"}, + {file = "yarl-1.9.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:c25ec06e4241e162f5d1f57c370f4078797ade95c9208bd0c60f484834f09c96"}, + {file = "yarl-1.9.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:7217234b10c64b52cc39a8d82550342ae2e45be34f5bff02b890b8c452eb48d7"}, + {file = "yarl-1.9.3-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:4ce77d289f8d40905c054b63f29851ecbfd026ef4ba5c371a158cfe6f623663e"}, + {file = "yarl-1.9.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5f74b015c99a5eac5ae589de27a1201418a5d9d460e89ccb3366015c6153e60a"}, + {file = "yarl-1.9.3-cp38-cp38-win32.whl", hash = "sha256:8a2538806be846ea25e90c28786136932ec385c7ff3bc1148e45125984783dc6"}, + {file = "yarl-1.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:6465d36381af057d0fab4e0f24ef0e80ba61f03fe43e6eeccbe0056e74aadc70"}, + {file = "yarl-1.9.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2f3c8822bc8fb4a347a192dd6a28a25d7f0ea3262e826d7d4ef9cc99cd06d07e"}, + {file = "yarl-1.9.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b7831566595fe88ba17ea80e4b61c0eb599f84c85acaa14bf04dd90319a45b90"}, + {file = "yarl-1.9.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ff34cb09a332832d1cf38acd0f604c068665192c6107a439a92abfd8acf90fe2"}, + {file = "yarl-1.9.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe8080b4f25dfc44a86bedd14bc4f9d469dfc6456e6f3c5d9077e81a5fedfba7"}, + {file = "yarl-1.9.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8535e111a064f3bdd94c0ed443105934d6f005adad68dd13ce50a488a0ad1bf3"}, + {file = "yarl-1.9.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0d155a092bf0ebf4a9f6f3b7a650dc5d9a5bbb585ef83a52ed36ba46f55cc39d"}, + {file = "yarl-1.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:778df71c8d0c8c9f1b378624b26431ca80041660d7be7c3f724b2c7a6e65d0d6"}, + {file = "yarl-1.9.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b9f9cafaf031c34d95c1528c16b2fa07b710e6056b3c4e2e34e9317072da5d1a"}, + {file = "yarl-1.9.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ca6b66f69e30f6e180d52f14d91ac854b8119553b524e0e28d5291a724f0f423"}, + {file = "yarl-1.9.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:e0e7e83f31e23c5d00ff618045ddc5e916f9e613d33c5a5823bc0b0a0feb522f"}, + {file = "yarl-1.9.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:af52725c7c39b0ee655befbbab5b9a1b209e01bb39128dce0db226a10014aacc"}, + {file = "yarl-1.9.3-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:0ab5baaea8450f4a3e241ef17e3d129b2143e38a685036b075976b9c415ea3eb"}, + {file = "yarl-1.9.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:6d350388ba1129bc867c6af1cd17da2b197dff0d2801036d2d7d83c2d771a682"}, + {file = "yarl-1.9.3-cp39-cp39-win32.whl", hash = "sha256:e2a16ef5fa2382af83bef4a18c1b3bcb4284c4732906aa69422cf09df9c59f1f"}, + {file = "yarl-1.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:d92d897cb4b4bf915fbeb5e604c7911021a8456f0964f3b8ebbe7f9188b9eabb"}, + {file = "yarl-1.9.3-py3-none-any.whl", hash = "sha256:271d63396460b6607b588555ea27a1a02b717ca2e3f2cf53bdde4013d7790929"}, + {file = "yarl-1.9.3.tar.gz", hash = "sha256:4a14907b597ec55740f63e52d7fee0e9ee09d5b9d57a4f399a7423268e457b57"}, ] [package.dependencies] @@ -7219,48 +7339,54 @@ reference = "tsinghua" [[package]] name = "zope-interface" -version = "6.0" +version = "6.1" description = "Interfaces for Python" optional = false python-versions = ">=3.7" files = [ - {file = "zope.interface-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f299c020c6679cb389814a3b81200fe55d428012c5e76da7e722491f5d205990"}, - {file = "zope.interface-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ee4b43f35f5dc15e1fec55ccb53c130adb1d11e8ad8263d68b1284b66a04190d"}, - {file = "zope.interface-6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5a158846d0fca0a908c1afb281ddba88744d403f2550dc34405c3691769cdd85"}, - {file = "zope.interface-6.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f72f23bab1848edb7472309e9898603141644faec9fd57a823ea6b4d1c4c8995"}, - {file = "zope.interface-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:48f4d38cf4b462e75fac78b6f11ad47b06b1c568eb59896db5b6ec1094eb467f"}, - {file = "zope.interface-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:87b690bbee9876163210fd3f500ee59f5803e4a6607d1b1238833b8885ebd410"}, - {file = "zope.interface-6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f2363e5fd81afb650085c6686f2ee3706975c54f331b426800b53531191fdf28"}, - {file = "zope.interface-6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:af169ba897692e9cd984a81cb0f02e46dacdc07d6cf9fd5c91e81f8efaf93d52"}, - {file = "zope.interface-6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fa90bac61c9dc3e1a563e5babb3fd2c0c1c80567e815442ddbe561eadc803b30"}, - {file = "zope.interface-6.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:89086c9d3490a0f265a3c4b794037a84541ff5ffa28bb9c24cc9f66566968464"}, - {file = "zope.interface-6.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:809fe3bf1a91393abc7e92d607976bbb8586512913a79f2bf7d7ec15bd8ea518"}, - {file = "zope.interface-6.0-cp311-cp311-win_amd64.whl", hash = "sha256:0ec9653825f837fbddc4e4b603d90269b501486c11800d7c761eee7ce46d1bbb"}, - {file = "zope.interface-6.0-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:790c1d9d8f9c92819c31ea660cd43c3d5451df1df61e2e814a6f99cebb292788"}, - {file = "zope.interface-6.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b39b8711578dcfd45fc0140993403b8a81e879ec25d53189f3faa1f006087dca"}, - {file = "zope.interface-6.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eba51599370c87088d8882ab74f637de0c4f04a6d08a312dce49368ba9ed5c2a"}, - {file = "zope.interface-6.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ee934f023f875ec2cfd2b05a937bd817efcc6c4c3f55c5778cbf78e58362ddc"}, - {file = "zope.interface-6.0-cp37-cp37m-win_amd64.whl", hash = "sha256:042f2381118b093714081fd82c98e3b189b68db38ee7d35b63c327c470ef8373"}, - {file = "zope.interface-6.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:dfbbbf0809a3606046a41f8561c3eada9db811be94138f42d9135a5c47e75f6f"}, - {file = "zope.interface-6.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:424d23b97fa1542d7be882eae0c0fc3d6827784105264a8169a26ce16db260d8"}, - {file = "zope.interface-6.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e538f2d4a6ffb6edfb303ce70ae7e88629ac6e5581870e66c306d9ad7b564a58"}, - {file = "zope.interface-6.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12175ca6b4db7621aedd7c30aa7cfa0a2d65ea3a0105393e05482d7a2d367446"}, - {file = "zope.interface-6.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c3d7dfd897a588ec27e391edbe3dd320a03684457470415870254e714126b1f"}, - {file = "zope.interface-6.0-cp38-cp38-win_amd64.whl", hash = "sha256:b3f543ae9d3408549a9900720f18c0194ac0fe810cecda2a584fd4dca2eb3bb8"}, - {file = "zope.interface-6.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d0583b75f2e70ec93f100931660328965bb9ff65ae54695fb3fa0a1255daa6f2"}, - {file = "zope.interface-6.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:23ac41d52fd15dd8be77e3257bc51bbb82469cf7f5e9a30b75e903e21439d16c"}, - {file = "zope.interface-6.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99856d6c98a326abbcc2363827e16bd6044f70f2ef42f453c0bd5440c4ce24e5"}, - {file = "zope.interface-6.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1592f68ae11e557b9ff2bc96ac8fc30b187e77c45a3c9cd876e3368c53dc5ba8"}, - {file = "zope.interface-6.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4407b1435572e3e1610797c9203ad2753666c62883b921318c5403fb7139dec2"}, - {file = "zope.interface-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:5171eb073474a5038321409a630904fd61f12dd1856dd7e9d19cd6fe092cbbc5"}, - {file = "zope.interface-6.0.tar.gz", hash = "sha256:aab584725afd10c710b8f1e6e208dbee2d0ad009f57d674cb9d1b3964037275d"}, + {file = "zope.interface-6.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:43b576c34ef0c1f5a4981163b551a8781896f2a37f71b8655fd20b5af0386abb"}, + {file = "zope.interface-6.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:67be3ca75012c6e9b109860820a8b6c9a84bfb036fbd1076246b98e56951ca92"}, + {file = "zope.interface-6.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9b9bc671626281f6045ad61d93a60f52fd5e8209b1610972cf0ef1bbe6d808e3"}, + {file = "zope.interface-6.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bbe81def9cf3e46f16ce01d9bfd8bea595e06505e51b7baf45115c77352675fd"}, + {file = "zope.interface-6.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6dc998f6de015723196a904045e5a2217f3590b62ea31990672e31fbc5370b41"}, + {file = "zope.interface-6.1-cp310-cp310-win_amd64.whl", hash = "sha256:239a4a08525c080ff833560171d23b249f7f4d17fcbf9316ef4159f44997616f"}, + {file = "zope.interface-6.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:9ffdaa5290422ac0f1688cb8adb1b94ca56cee3ad11f29f2ae301df8aecba7d1"}, + {file = "zope.interface-6.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:34c15ca9248f2e095ef2e93af2d633358c5f048c49fbfddf5fdfc47d5e263736"}, + {file = "zope.interface-6.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b012d023b4fb59183909b45d7f97fb493ef7a46d2838a5e716e3155081894605"}, + {file = "zope.interface-6.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:97806e9ca3651588c1baaebb8d0c5ee3db95430b612db354c199b57378312ee8"}, + {file = "zope.interface-6.1-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fddbab55a2473f1d3b8833ec6b7ac31e8211b0aa608df5ab09ce07f3727326de"}, + {file = "zope.interface-6.1-cp311-cp311-win_amd64.whl", hash = "sha256:a0da79117952a9a41253696ed3e8b560a425197d4e41634a23b1507efe3273f1"}, + {file = "zope.interface-6.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:e8bb9c990ca9027b4214fa543fd4025818dc95f8b7abce79d61dc8a2112b561a"}, + {file = "zope.interface-6.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b51b64432eed4c0744241e9ce5c70dcfecac866dff720e746d0a9c82f371dfa7"}, + {file = "zope.interface-6.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa6fd016e9644406d0a61313e50348c706e911dca29736a3266fc9e28ec4ca6d"}, + {file = "zope.interface-6.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0c8cf55261e15590065039696607f6c9c1aeda700ceee40c70478552d323b3ff"}, + {file = "zope.interface-6.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e30506bcb03de8983f78884807e4fd95d8db6e65b69257eea05d13d519b83ac0"}, + {file = "zope.interface-6.1-cp312-cp312-win_amd64.whl", hash = "sha256:e33e86fd65f369f10608b08729c8f1c92ec7e0e485964670b4d2633a4812d36b"}, + {file = "zope.interface-6.1-cp37-cp37m-macosx_11_0_x86_64.whl", hash = "sha256:2f8d89721834524a813f37fa174bac074ec3d179858e4ad1b7efd4401f8ac45d"}, + {file = "zope.interface-6.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:13b7d0f2a67eb83c385880489dbb80145e9d344427b4262c49fbf2581677c11c"}, + {file = "zope.interface-6.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef43ee91c193f827e49599e824385ec7c7f3cd152d74cb1dfe02cb135f264d83"}, + {file = "zope.interface-6.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e441e8b7d587af0414d25e8d05e27040d78581388eed4c54c30c0c91aad3a379"}, + {file = "zope.interface-6.1-cp37-cp37m-win_amd64.whl", hash = "sha256:f89b28772fc2562ed9ad871c865f5320ef761a7fcc188a935e21fe8b31a38ca9"}, + {file = "zope.interface-6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:70d2cef1bf529bff41559be2de9d44d47b002f65e17f43c73ddefc92f32bf00f"}, + {file = "zope.interface-6.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:ad54ed57bdfa3254d23ae04a4b1ce405954969c1b0550cc2d1d2990e8b439de1"}, + {file = "zope.interface-6.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ef467d86d3cfde8b39ea1b35090208b0447caaabd38405420830f7fd85fbdd56"}, + {file = "zope.interface-6.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6af47f10cfc54c2ba2d825220f180cc1e2d4914d783d6fc0cd93d43d7bc1c78b"}, + {file = "zope.interface-6.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c9559138690e1bd4ea6cd0954d22d1e9251e8025ce9ede5d0af0ceae4a401e43"}, + {file = "zope.interface-6.1-cp38-cp38-win_amd64.whl", hash = "sha256:964a7af27379ff4357dad1256d9f215047e70e93009e532d36dcb8909036033d"}, + {file = "zope.interface-6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:387545206c56b0315fbadb0431d5129c797f92dc59e276b3ce82db07ac1c6179"}, + {file = "zope.interface-6.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:57d0a8ce40ce440f96a2c77824ee94bf0d0925e6089df7366c2272ccefcb7941"}, + {file = "zope.interface-6.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7ebc4d34e7620c4f0da7bf162c81978fce0ea820e4fa1e8fc40ee763839805f3"}, + {file = "zope.interface-6.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5a804abc126b33824a44a7aa94f06cd211a18bbf31898ba04bd0924fbe9d282d"}, + {file = "zope.interface-6.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f294a15f7723fc0d3b40701ca9b446133ec713eafc1cc6afa7b3d98666ee1ac"}, + {file = "zope.interface-6.1-cp39-cp39-win_amd64.whl", hash = "sha256:a41f87bb93b8048fe866fa9e3d0c51e27fe55149035dcf5f43da4b56732c0a40"}, + {file = "zope.interface-6.1.tar.gz", hash = "sha256:2fdc7ccbd6eb6b7df5353012fbed6c3c5d04ceaca0038f75e601060e95345309"}, ] [package.dependencies] setuptools = "*" [package.extras] -docs = ["Sphinx", "repoze.sphinx.autointerface"] +docs = ["Sphinx", "repoze.sphinx.autointerface", "sphinx-rtd-theme"] test = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] @@ -7272,4 +7398,4 @@ reference = "tsinghua" [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "882df81581adc280c0349a83b88c42987598c2b125a21cf7c834f8a32e6fc3f3" +content-hash = "5b8fbaefc91f899fd983c7fa04091cd453f51137a1fdc17a52e94e77b53e5e70" diff --git a/pyproject.toml b/pyproject.toml index b071222d8..5d573d424 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "jumpserver" -version = "v3.5" +version = "v3.9" description = "广受欢迎的开源堡垒机" authors = ["ibuler "] license = "GPLv3" @@ -102,7 +102,7 @@ pytz = "2023.3" django-proxy = "1.2.2" python-daemon = "3.0.1" eventlet = "0.33.3" -greenlet = "2.0.2" +greenlet = "3.0.1" gunicorn = "21.2.0" celery = "5.3.1" flower = "2.0.1" From 0fdae007220d6cda3e4c128c7fe45e47bf2c555d Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Wed, 29 Nov 2023 17:45:44 +0800 Subject: [PATCH 020/111] =?UTF-8?q?perf:=20=E6=94=AF=E6=8C=81slack?= =?UTF-8?q?=E9=80=9A=E7=9F=A5=E5=92=8C=E8=AE=A4=E8=AF=81=20(#12193)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * perf: 支持slack通知和认证 * perf: 生成迁移文件 * perf: 优化获取access_token逻辑 --------- Co-authored-by: jiangweidong --- apps/audits/signal_handlers/login_log.py | 1 + apps/authentication/backends/sso.py | 13 ++ apps/authentication/urls/view_urls.py | 8 +- apps/authentication/views/__init__.py | 1 + apps/authentication/views/base.py | 76 +++++++-- apps/authentication/views/feishu.py | 54 +------ apps/authentication/views/login.py | 6 + apps/authentication/views/slack.py | 128 +++++++++++++++ apps/authentication/views/wecom.py | 60 +------ apps/common/sdk/im/slack/__init__.py | 149 ++++++++++++++++++ apps/jumpserver/conf.py | 6 + apps/jumpserver/settings/auth.py | 9 +- apps/notifications/backends/__init__.py | 1 + apps/notifications/backends/slack.py | 21 +++ apps/notifications/notifications.py | 3 + apps/settings/api/__init__.py | 1 + apps/settings/api/settings.py | 1 + apps/settings/api/slack.py | 40 +++++ apps/settings/serializers/auth/__init__.py | 1 + apps/settings/serializers/auth/base.py | 3 +- apps/settings/serializers/auth/slack.py | 15 ++ apps/settings/urls/api_urls.py | 1 + apps/static/img/login_slack_logo.png | Bin 0 -> 2221 bytes ..._unique_together_user_slack_id_and_more.py | 31 ++++ apps/users/models/user.py | 6 + apps/users/serializers/user.py | 2 +- 26 files changed, 523 insertions(+), 114 deletions(-) create mode 100644 apps/authentication/views/slack.py create mode 100644 apps/common/sdk/im/slack/__init__.py create mode 100644 apps/notifications/backends/slack.py create mode 100644 apps/settings/api/slack.py create mode 100644 apps/settings/serializers/auth/slack.py create mode 100644 apps/static/img/login_slack_logo.png create mode 100644 apps/users/migrations/0049_alter_user_unique_together_user_slack_id_and_more.py diff --git a/apps/audits/signal_handlers/login_log.py b/apps/audits/signal_handlers/login_log.py index 7765e470b..5829e4f5e 100644 --- a/apps/audits/signal_handlers/login_log.py +++ b/apps/audits/signal_handlers/login_log.py @@ -36,6 +36,7 @@ class AuthBackendLabelMapping(LazyObject): backend_label_mapping[settings.AUTH_BACKEND_AUTH_TOKEN] = _("Auth Token") backend_label_mapping[settings.AUTH_BACKEND_WECOM] = _("WeCom") backend_label_mapping[settings.AUTH_BACKEND_FEISHU] = _("FeiShu") + backend_label_mapping[settings.AUTH_BACKEND_SLACK] = _("Slack") backend_label_mapping[settings.AUTH_BACKEND_DINGTALK] = _("DingTalk") backend_label_mapping[settings.AUTH_BACKEND_TEMP_TOKEN] = _("Temporary token") backend_label_mapping[settings.AUTH_BACKEND_PASSKEY] = _("Passkey") diff --git a/apps/authentication/backends/sso.py b/apps/authentication/backends/sso.py index 7bee484dc..66f15a3a4 100644 --- a/apps/authentication/backends/sso.py +++ b/apps/authentication/backends/sso.py @@ -55,6 +55,19 @@ class FeiShuAuthentication(JMSModelBackend): pass +class SlackAuthentication(JMSModelBackend): + """ + 什么也不做呀😺 + """ + + @staticmethod + def is_enabled(): + return settings.AUTH_SLACK + + def authenticate(self, request, **kwargs): + pass + + class AuthorizationTokenAuthentication(JMSModelBackend): """ 什么也不做呀😺 diff --git a/apps/authentication/urls/view_urls.py b/apps/authentication/urls/view_urls.py index ea08eddd0..a648ded51 100644 --- a/apps/authentication/urls/view_urls.py +++ b/apps/authentication/urls/view_urls.py @@ -27,7 +27,7 @@ urlpatterns = [ path('wecom/bind/start/', views.WeComEnableStartView.as_view(), name='wecom-bind-start'), path('wecom/qr/bind/', views.WeComQRBindView.as_view(), name='wecom-qr-bind'), path('wecom/qr/login/', views.WeComQRLoginView.as_view(), name='wecom-qr-login'), - path('wecom/qr/bind//callback/', views.WeComQRBindCallbackView.as_view(), + path('wecom/qr/bind/callback/', views.WeComQRBindCallbackView.as_view(), name='wecom-qr-bind-callback'), path('wecom/qr/login/callback/', views.WeComQRLoginCallbackView.as_view(), name='wecom-qr-login-callback'), path('wecom/oauth/login/', views.WeComOAuthLoginView.as_view(), name='wecom-oauth-login'), @@ -49,6 +49,12 @@ urlpatterns = [ path('feishu/qr/bind/callback/', views.FeiShuQRBindCallbackView.as_view(), name='feishu-qr-bind-callback'), path('feishu/qr/login/callback/', views.FeiShuQRLoginCallbackView.as_view(), name='feishu-qr-login-callback'), + path('slack/bind/start/', views.SlackEnableStartView.as_view(), name='slack-bind-start'), + path('slack/qr/bind/', views.SlackQRBindView.as_view(), name='slack-qr-bind'), + path('slack/qr/login/', views.SlackQRLoginView.as_view(), name='slack-qr-login'), + path('slack/qr/bind/callback/', views.SlackQRBindCallbackView.as_view(), name='slack-qr-bind-callback'), + path('slack/qr/login/callback/', views.SlackQRLoginCallbackView.as_view(), name='slack-qr-login-callback'), + # Profile path('profile/pubkey/generate/', users_view.UserPublicKeyGenerateView.as_view(), name='user-pubkey-generate'), path('profile/mfa/', users_view.MFASettingView.as_view(), name='user-mfa-setting'), diff --git a/apps/authentication/views/__init__.py b/apps/authentication/views/__init__.py index 38cf114e1..214c8943a 100644 --- a/apps/authentication/views/__init__.py +++ b/apps/authentication/views/__init__.py @@ -5,3 +5,4 @@ from .mfa import * from .wecom import * from .dingtalk import * from .feishu import * +from .slack import * diff --git a/apps/authentication/views/base.py b/apps/authentication/views/base.py index c118680f0..598ec9a0d 100644 --- a/apps/authentication/views/base.py +++ b/apps/authentication/views/base.py @@ -2,6 +2,7 @@ from functools import lru_cache from django.conf import settings from django.db.utils import IntegrityError +from django.contrib.auth import logout as auth_logout from django.utils.module_loading import import_string from django.utils.translation import gettext_lazy as _ from django.views import View @@ -9,8 +10,10 @@ from rest_framework.request import Request from authentication import errors from authentication.mixins import AuthMixin +from authentication.notifications import OAuthBindMessage from common.utils import get_logger from common.utils.django import reverse, get_object_or_none +from common.utils.common import get_request_ip from users.models import User from users.signal_handlers import check_only_allow_exist_user_auth from .mixins import FlashMessageMixin @@ -18,9 +21,21 @@ from .mixins import FlashMessageMixin logger = get_logger(__file__) -class BaseLoginCallbackView(AuthMixin, FlashMessageMixin, View): +class IMClientMixin: client_type_path = '' client_auth_params = {} + + @property + @lru_cache(maxsize=1) + def client(self): + if not all([self.client_type_path, self.client_auth_params]): + raise NotImplementedError + client_init = {k: getattr(settings, v) for k, v in self.client_auth_params.items()} + client_type = import_string(self.client_type_path) + return client_type(**client_init) + + +class BaseLoginCallbackView(AuthMixin, FlashMessageMixin, IMClientMixin, View): user_type = '' auth_backend = None # 提示信息 @@ -34,15 +49,6 @@ class BaseLoginCallbackView(AuthMixin, FlashMessageMixin, View): def get_verify_state_failed_response(self, redirect_uri): raise NotImplementedError - @property - @lru_cache(maxsize=1) - def client(self): - if not all([self.client_type_path, self.client_auth_params]): - raise NotImplementedError - client_init = {k: getattr(settings, v) for k, v in self.client_auth_params.items()} - client_type = import_string(self.client_type_path) - return client_type(**client_init) - def create_user_if_not_exist(self, user_id, **kwargs): user = None user_attr = self.client.get_user_detail(user_id, **kwargs) @@ -99,3 +105,53 @@ class BaseLoginCallbackView(AuthMixin, FlashMessageMixin, View): response = self.get_failed_response(login_url, title=msg, msg=msg) return response return self.redirect_to_guard_view() + + +class BaseBindCallbackView(FlashMessageMixin, IMClientMixin, View): + auth_type = '' + auth_type_label = '' + + def verify_state(self): + raise NotImplementedError + + def get_verify_state_failed_response(self, redirect_uri): + raise NotImplementedError + + def get_already_bound_response(self, redirect_uri): + raise NotImplementedError + + def get(self, request: Request): + code = request.GET.get('code') + redirect_url = request.GET.get('redirect_url') + + if not self.verify_state(): + return self.get_verify_state_failed_response(redirect_url) + + user = request.user + source_id = getattr(user, f'{self.auth_type}_id', None) + if source_id: + response = self.get_already_bound_response(redirect_url) + return response + + auth_user_id, __ = self.client.get_user_id_by_code(code) + if not auth_user_id: + msg = _('%s query user failed') % self.auth_type_label + response = self.get_failed_response(redirect_url, msg, msg) + return response + + try: + setattr(user, f'{self.auth_type}_id', auth_user_id) + user.save() + except IntegrityError as e: + if e.args[0] == 1062: + msg = _('The %s is already bound to another user') % self.auth_type_label + response = self.get_failed_response(redirect_url, msg, msg) + return response + raise e + + ip = get_request_ip(request) + OAuthBindMessage(user, ip, self.auth_type_label, auth_user_id).publish_async() + msg = _('Binding %s successfully') % self.auth_type_label + auth_logout(request) + response = self.get_success_response(redirect_url, msg, msg) + return response diff --git a/apps/authentication/views/feishu.py b/apps/authentication/views/feishu.py index 4ceacc21b..0427c2b1b 100644 --- a/apps/authentication/views/feishu.py +++ b/apps/authentication/views/feishu.py @@ -1,8 +1,6 @@ from urllib.parse import urlencode from django.conf import settings -from django.contrib.auth import logout as auth_logout -from django.db.utils import IntegrityError from django.http.request import HttpRequest from django.http.response import HttpResponseRedirect from django.utils.translation import gettext_lazy as _ @@ -11,16 +9,14 @@ from rest_framework.exceptions import APIException from rest_framework.permissions import AllowAny, IsAuthenticated from authentication.const import ConfirmType -from authentication.notifications import OAuthBindMessage from authentication.permissions import UserConfirmation -from common.sdk.im.feishu import URL, FeiShu +from common.sdk.im.feishu import URL from common.utils import get_logger -from common.utils.common import get_request_ip from common.utils.django import reverse from common.utils.random import random_string from common.views.mixins import PermissionsMixin, UserConfirmRequiredExceptionMixin from users.views import UserVerifyPasswordView -from .base import BaseLoginCallbackView +from .base import BaseLoginCallbackView, BaseBindCallbackView from .mixins import FlashMessageMixin logger = get_logger(__file__) @@ -82,49 +78,13 @@ class FeiShuQRBindView(FeiShuQRMixin, View): return HttpResponseRedirect(url) -class FeiShuQRBindCallbackView(FeiShuQRMixin, View): +class FeiShuQRBindCallbackView(FeiShuQRMixin, BaseBindCallbackView): permission_classes = (IsAuthenticated,) - def get(self, request: HttpRequest): - code = request.GET.get('code') - redirect_url = request.GET.get('redirect_url') - - if not self.verify_state(): - return self.get_verify_state_failed_response(redirect_url) - - user = request.user - - if user.feishu_id: - response = self.get_already_bound_response(redirect_url) - return response - - feishu = FeiShu( - app_id=settings.FEISHU_APP_ID, - app_secret=settings.FEISHU_APP_SECRET - ) - user_id, __ = feishu.get_user_id_by_code(code) - - if not user_id: - msg = _('FeiShu query user failed') - response = self.get_failed_response(redirect_url, msg, msg) - return response - - try: - user.feishu_id = user_id - user.save() - except IntegrityError as e: - if e.args[0] == 1062: - msg = _('The FeiShu is already bound to another user') - response = self.get_failed_response(redirect_url, msg, msg) - return response - raise e - - ip = get_request_ip(request) - OAuthBindMessage(user, ip, _('FeiShu'), user_id).publish_async() - msg = _('Binding FeiShu successfully') - auth_logout(request) - response = self.get_success_response(redirect_url, msg, msg) - return response + client_type_path = 'common.sdk.im.feishu.FeiShu' + client_auth_params = {'app_id': 'FEISHU_APP_ID', 'app_secret': 'FEISHU_APP_SECRET'} + auth_type = 'feishu' + auth_type_label = _('FeiShu') class FeiShuEnableStartView(UserVerifyPasswordView): diff --git a/apps/authentication/views/login.py b/apps/authentication/views/login.py index 0b88a76d8..23ad4d814 100644 --- a/apps/authentication/views/login.py +++ b/apps/authentication/views/login.py @@ -91,6 +91,12 @@ class UserLoginContextMixin: 'url': reverse('authentication:feishu-qr-login'), 'logo': static('img/login_feishu_logo.png') }, + { + 'name': _('Slack'), + 'enabled': settings.AUTH_SLACK, + 'url': reverse('authentication:slack-qr-login'), + 'logo': static('img/login_slack_logo.png') + }, { 'name': _("Passkey"), 'enabled': settings.AUTH_PASSKEY, diff --git a/apps/authentication/views/slack.py b/apps/authentication/views/slack.py new file mode 100644 index 000000000..70edcf4c5 --- /dev/null +++ b/apps/authentication/views/slack.py @@ -0,0 +1,128 @@ +from urllib.parse import urlencode + +from django.conf import settings +from django.http.response import HttpResponseRedirect +from django.utils.translation import gettext_lazy as _ +from django.views import View +from rest_framework.exceptions import APIException +from rest_framework.permissions import AllowAny, IsAuthenticated +from rest_framework.request import Request + +from authentication.const import ConfirmType +from authentication.permissions import UserConfirmation +from common.sdk.im.slack import URL, SLACK_REDIRECT_URI_SESSION_KEY +from common.utils import get_logger +from common.utils.django import reverse +from common.utils.random import random_string +from common.views.mixins import PermissionsMixin, UserConfirmRequiredExceptionMixin +from users.views import UserVerifyPasswordView +from .base import BaseLoginCallbackView, BaseBindCallbackView +from .mixins import FlashMessageMixin + +logger = get_logger(__file__) + +SLACK_STATE_SESSION_KEY = '_slack_state' + + +class SlackMixin(UserConfirmRequiredExceptionMixin, PermissionsMixin, FlashMessageMixin, View): + def dispatch(self, request, *args, **kwargs): + try: + return super().dispatch(request, *args, **kwargs) + except APIException as e: + msg = str(e.detail) + return self.get_failed_response( + '/', + _('Slack Error'), + msg + ) + + def verify_state(self): + state = self.request.GET.get('state') + session_state = self.request.session.get(SLACK_STATE_SESSION_KEY) + if state != session_state: + return False + return True + + def get_verify_state_failed_response(self, redirect_uri): + msg = _("The system configuration is incorrect. Please contact your administrator") + return self.get_failed_response(redirect_uri, msg, msg) + + def get_qr_url(self, redirect_uri): + state = random_string(16) + self.request.session[SLACK_STATE_SESSION_KEY] = state + + params = { + 'client_id': settings.SLACK_CLIENT_ID, + 'state': state, 'scope': 'users:read,users:read.email', + 'redirect_uri': redirect_uri, + } + url = URL().AUTHORIZE + '?' + urlencode(params) + return url + + def get_already_bound_response(self, redirect_url): + msg = _('Slack is already bound') + response = self.get_failed_response(redirect_url, msg, msg) + return response + + +class SlackQRBindView(SlackMixin, View): + permission_classes = (IsAuthenticated, UserConfirmation.require(ConfirmType.RELOGIN)) + + def get(self, request: Request): + redirect_url = request.GET.get('redirect_url') + + redirect_uri = reverse('authentication:slack-qr-bind-callback', external=True) + redirect_uri += '?' + urlencode({'redirect_url': redirect_url}) + self.request.session[SLACK_REDIRECT_URI_SESSION_KEY] = redirect_uri + url = self.get_qr_url(redirect_uri) + return HttpResponseRedirect(url) + + +class SlackQRBindCallbackView(SlackMixin, BaseBindCallbackView): + permission_classes = (IsAuthenticated,) + + client_type_path = 'common.sdk.im.slack.Slack' + client_auth_params = {'client_id': 'SLACK_CLIENT_ID', 'client_secret': 'SLACK_CLIENT_SECRET'} + auth_type = 'slack' + auth_type_label = _('Slack') + + +class SlackEnableStartView(UserVerifyPasswordView): + + def get_success_url(self): + referer = self.request.META.get('HTTP_REFERER') + redirect_url = self.request.GET.get("redirect_url") + + success_url = reverse('authentication:slack-qr-bind') + success_url += '?' + urlencode({ + 'redirect_url': redirect_url or referer + }) + + return success_url + + +class SlackQRLoginView(SlackMixin, View): + permission_classes = (AllowAny,) + + def get(self, request: Request): + redirect_url = request.GET.get('redirect_url') or reverse('index') + redirect_uri = reverse('authentication:slack-qr-login-callback', external=True) + redirect_uri += '?' + urlencode({ + 'redirect_url': redirect_url, + }) + self.request.session[SLACK_REDIRECT_URI_SESSION_KEY] = redirect_uri + url = self.get_qr_url(redirect_uri) + return HttpResponseRedirect(url) + + +class SlackQRLoginCallbackView(SlackMixin, BaseLoginCallbackView): + permission_classes = (AllowAny,) + + client_type_path = 'common.sdk.im.slack.Slack' + client_auth_params = {'client_id': 'SLACK_CLIENT_ID', 'client_secret': 'SLACK_CLIENT_SECRET'} + user_type = 'slack' + auth_backend = 'AUTH_BACKEND_SLACK' + + msg_client_err = _('Slack Error') + msg_user_not_bound_err = _('Slack is not bound') + msg_not_found_user_from_client_err = _('Failed to get user from Slack') diff --git a/apps/authentication/views/wecom.py b/apps/authentication/views/wecom.py index 2e6d0c1bb..48268e516 100644 --- a/apps/authentication/views/wecom.py +++ b/apps/authentication/views/wecom.py @@ -1,8 +1,6 @@ from urllib.parse import urlencode from django.conf import settings -from django.contrib.auth import logout as auth_logout -from django.db.utils import IntegrityError from django.http.request import HttpRequest from django.http.response import HttpResponseRedirect from django.utils.translation import gettext_lazy as _ @@ -13,7 +11,6 @@ from rest_framework.permissions import IsAuthenticated, AllowAny from authentication import errors from authentication.const import ConfirmType from authentication.mixins import AuthMixin -from authentication.notifications import OAuthBindMessage from authentication.permissions import UserConfirmation from common.sdk.im.wecom import URL from common.sdk.im.wecom import WeCom @@ -24,7 +21,7 @@ from common.utils.random import random_string from common.views.mixins import UserConfirmRequiredExceptionMixin, PermissionsMixin from users.models import User from users.views import UserVerifyPasswordView -from .base import BaseLoginCallbackView +from .base import BaseLoginCallbackView, BaseBindCallbackView from .mixins import METAMixin, FlashMessageMixin logger = get_logger(__file__) @@ -104,64 +101,21 @@ class WeComQRBindView(WeComQRMixin, View): permission_classes = (IsAuthenticated, UserConfirmation.require(ConfirmType.RELOGIN)) def get(self, request: HttpRequest): - user = request.user redirect_url = request.GET.get('redirect_url') - - redirect_uri = reverse('authentication:wecom-qr-bind-callback', kwargs={'user_id': user.id}, external=True) + redirect_uri = reverse('authentication:wecom-qr-bind-callback', external=True) redirect_uri += '?' + urlencode({'redirect_url': redirect_url}) url = self.get_qr_url(redirect_uri) return HttpResponseRedirect(url) -class WeComQRBindCallbackView(WeComQRMixin, View): +class WeComQRBindCallbackView(WeComQRMixin, BaseBindCallbackView): permission_classes = (IsAuthenticated,) - def get(self, request: HttpRequest, user_id): - code = request.GET.get('code') - redirect_url = request.GET.get('redirect_url') - - if not self.verify_state(): - return self.get_verify_state_failed_response(redirect_url) - - user = get_object_or_none(User, id=user_id) - if user is None: - logger.error(f'WeComQR bind callback error, user_id invalid: user_id={user_id}') - msg = _('Invalid user_id') - response = self.get_failed_response(redirect_url, msg, msg) - return response - - if user.wecom_id: - response = self.get_already_bound_response(redirect_url) - return response - - wecom = WeCom( - corpid=settings.WECOM_CORPID, - corpsecret=settings.WECOM_SECRET, - agentid=settings.WECOM_AGENTID - ) - wecom_userid, __ = wecom.get_user_id_by_code(code) - if not wecom_userid: - msg = _('WeCom query user failed') - response = self.get_failed_response(redirect_url, msg, msg) - return response - - try: - user.wecom_id = wecom_userid - user.save() - except IntegrityError as e: - if e.args[0] == 1062: - msg = _('The WeCom is already bound to another user') - response = self.get_failed_response(redirect_url, msg, msg) - return response - raise e - - ip = get_request_ip(request) - OAuthBindMessage(user, ip, _('WeCom'), wecom_userid).publish_async() - msg = _('Binding WeCom successfully') - auth_logout(request) - response = self.get_success_response(redirect_url, msg, msg) - return response + client_type_path = 'common.sdk.im.wecom.WeCom' + client_auth_params = {'corpid': 'WECOM_CORPID', 'corpsecret': 'WECOM_SECRET', 'agentid': 'WECOM_AGENTID'} + auth_type = 'wecom' + auth_type_label = _('Wecom') class WeComEnableStartView(UserVerifyPasswordView): diff --git a/apps/common/sdk/im/slack/__init__.py b/apps/common/sdk/im/slack/__init__.py new file mode 100644 index 000000000..1f2532602 --- /dev/null +++ b/apps/common/sdk/im/slack/__init__.py @@ -0,0 +1,149 @@ +import requests +import mistune + +from rest_framework.exceptions import APIException +from django.utils.translation import gettext_lazy as _ + +from users.utils import construct_user_email +from common.utils.common import get_logger +from jumpserver.utils import get_current_request + +logger = get_logger(__name__) + + +SLACK_REDIRECT_URI_SESSION_KEY = '_slack_redirect_uri' + + +class URL: + AUTHORIZE = 'https://slack.com/oauth/v2/authorize' + ACCESS_TOKEN = 'https://slack.com/api/oauth.v2.access' + GET_USER_INFO_BY_USER_ID = 'https://slack.com/api/users.info' + SEND_MESSAGE = 'https://slack.com/api/chat.postMessage' + AUTH_TEST = 'https://slack.com/api/auth.test' + + +class SlackRenderer(mistune.Renderer): + def header(self, text, level, raw=None): + return '*' + text + '*\n' + + def double_emphasis(self, text): + return '*' + text + '*' + + def list(self, body, ordered=True): + lines = body.split('\n') + for i, line in enumerate(lines): + if not line: + continue + prefix = '• ' + lines[i] = prefix + line[4:-5] + return '\n'.join(lines) + + def link(self, link, title, content): + if title or content: + label = str(title or content).strip() + return f'<{link}|{label}>' + return f'<{link}>' + + def paragraph(self, text): + return f'{text.strip()}\n' + + def linebreak(self): + return '\n' + + +class SlackRequests: + def __init__(self, client_id=None, client_secret=None, bot_token=None): + self._client_id = client_id + self._client_secret = client_secret + self._bot_token = bot_token + self.access_token = None + self.user_id = None + + def add_token(self, headers, with_bot_token, with_access_token): + if with_access_token: + headers.update({'Authorization': f'Bearer {self.access_token}'}) + if with_bot_token: + headers.update({'Authorization': f'Bearer {self._bot_token}'}) + + def request(self, method, url, with_bot_token=True, with_access_token=False, **kwargs): + headers = kwargs.pop('headers', {}) + self.add_token(headers, with_bot_token=with_bot_token, with_access_token=with_access_token) + + func_handler = getattr(requests, method, requests.get) + data = func_handler(url, headers=headers, **kwargs).json() + if not data.get('ok'): + raise APIException( + detail=data.get('error', _('Unknown error occur')) + ) + return data + + def request_access_token(self, code): + request = get_current_request() + data = { + 'code': code, 'client_id': self._client_id, 'client_secret': self._client_secret, + 'grant_type': 'authorization_code', + 'redirect_uri': request.session.get(SLACK_REDIRECT_URI_SESSION_KEY) + } + response = self.request( + 'post', url=URL().ACCESS_TOKEN, data=data, with_bot_token=False + ) + self.access_token = response['access_token'] + self.user_id = response['authed_user']['id'] + + +class Slack: + def __init__(self, client_id=None, client_secret=None, bot_token=None, **kwargs): + self._client = SlackRequests( + client_id=client_id, client_secret=client_secret, bot_token=bot_token + ) + self.markdown = mistune.Markdown(renderer=SlackRenderer()) + + def get_user_id_by_code(self, code): + self._client.request_access_token(code) + response = self._client.request( + 'get', f'{URL().GET_USER_INFO_BY_USER_ID}?user={self._client.user_id}', + with_bot_token=False, with_access_token=True + ) + return self._client.user_id, response['user'] + + def is_valid(self): + return self._client.request('post', URL().AUTH_TEST) + + def convert_to_markdown(self, message): + blocks = [] + for line in message.split('\n'): + block = self.markdown(line) + if not block: + continue + if block.startswith('
    '): + block_item = {'type': 'divider'} + else: + block_item = { + "type": "section", + "text": {"type": "mrkdwn", "text": block} + } + blocks.append(block_item) + return {'blocks': blocks} + + def send_text(self, user_ids, msg_body): + body = self.convert_to_markdown(msg_body) + logger.info(f'Slack send text: user_ids={user_ids} msg={body}') + for user_id in user_ids: + body['channel'] = user_id + try: + self._client.request('post', URL().SEND_MESSAGE, json=body) + except APIException as e: + # 只处理可预知的错误 + logger.exception(e) + + @staticmethod + def get_user_detail(user_id, **kwargs): + # get_user_id_by_code 已经返回个人信息,这里直接解析 + user_info = kwargs['other_info'] + username = user_info.get('name') or user_id + name = user_info.get('real_name', username) + email = user_info.get('profile', {}).get('email') + email = construct_user_email(username, email) + return { + 'username': username, 'name': name, 'email': email + } diff --git a/apps/jumpserver/conf.py b/apps/jumpserver/conf.py index ea33d178a..6d014c80e 100644 --- a/apps/jumpserver/conf.py +++ b/apps/jumpserver/conf.py @@ -405,6 +405,12 @@ class Config(dict): 'FEISHU_APP_SECRET': '', 'FEISHU_VERSION': 'feishu', + # Slack + 'AUTH_SLACK': False, + 'SLACK_CLIENT_ID': '', + 'SLACK_CLIENT_SECRET': '', + 'SLACK_BOT_TOKEN': '', + 'LOGIN_REDIRECT_TO_BACKEND': '', # 'OPENID / CAS / SAML2 'LOGIN_REDIRECT_MSG_ENABLED': True, diff --git a/apps/jumpserver/settings/auth.py b/apps/jumpserver/settings/auth.py index 5b6a1cd35..94c45b135 100644 --- a/apps/jumpserver/settings/auth.py +++ b/apps/jumpserver/settings/auth.py @@ -140,6 +140,12 @@ FEISHU_APP_ID = CONFIG.FEISHU_APP_ID FEISHU_APP_SECRET = CONFIG.FEISHU_APP_SECRET FEISHU_VERSION = CONFIG.FEISHU_VERSION +# Slack auth +AUTH_SLACK = CONFIG.AUTH_SLACK +SLACK_CLIENT_ID = CONFIG.SLACK_CLIENT_ID +SLACK_CLIENT_SECRET = CONFIG.SLACK_CLIENT_SECRET +SLACK_BOT_TOKEN = CONFIG.SLACK_BOT_TOKEN + # Saml2 auth AUTH_SAML2 = CONFIG.AUTH_SAML2 AUTH_SAML2_PROVIDER_AUTHORIZATION_ENDPOINT = CONFIG.AUTH_SAML2_PROVIDER_AUTHORIZATION_ENDPOINT @@ -201,6 +207,7 @@ AUTH_BACKEND_SSO = 'authentication.backends.sso.SSOAuthentication' AUTH_BACKEND_WECOM = 'authentication.backends.sso.WeComAuthentication' AUTH_BACKEND_DINGTALK = 'authentication.backends.sso.DingTalkAuthentication' AUTH_BACKEND_FEISHU = 'authentication.backends.sso.FeiShuAuthentication' +AUTH_BACKEND_SLACK = 'authentication.backends.sso.SlackAuthentication' AUTH_BACKEND_AUTH_TOKEN = 'authentication.backends.sso.AuthorizationTokenAuthentication' AUTH_BACKEND_SAML2 = 'authentication.backends.saml2.SAML2Backend' AUTH_BACKEND_OAUTH2 = 'authentication.backends.oauth2.OAuth2Backend' @@ -216,7 +223,7 @@ AUTHENTICATION_BACKENDS = [ AUTH_BACKEND_CAS, AUTH_BACKEND_OIDC_PASSWORD, AUTH_BACKEND_OIDC_CODE, AUTH_BACKEND_SAML2, AUTH_BACKEND_OAUTH2, # 扫码模式 - AUTH_BACKEND_WECOM, AUTH_BACKEND_DINGTALK, AUTH_BACKEND_FEISHU, + AUTH_BACKEND_WECOM, AUTH_BACKEND_DINGTALK, AUTH_BACKEND_FEISHU, AUTH_BACKEND_SLACK, # Token模式 AUTH_BACKEND_AUTH_TOKEN, AUTH_BACKEND_SSO, AUTH_BACKEND_TEMP_TOKEN, AUTH_BACKEND_PASSKEY diff --git a/apps/notifications/backends/__init__.py b/apps/notifications/backends/__init__.py index 2e95bd437..047123e57 100644 --- a/apps/notifications/backends/__init__.py +++ b/apps/notifications/backends/__init__.py @@ -12,6 +12,7 @@ class BACKEND(models.TextChoices): DINGTALK = 'dingtalk', _('DingTalk') SITE_MSG = 'site_msg', _('Site message') FEISHU = 'feishu', _('FeiShu') + SLACK = 'slack', _('Slack') # SMS = 'sms', _('SMS') @property diff --git a/apps/notifications/backends/slack.py b/apps/notifications/backends/slack.py new file mode 100644 index 000000000..95e0bde60 --- /dev/null +++ b/apps/notifications/backends/slack.py @@ -0,0 +1,21 @@ +from django.conf import settings + +from common.sdk.im.slack import Slack as Client +from .base import BackendBase + + +class Slack(BackendBase): + account_field = 'slack_id' + is_enable_field_in_settings = 'AUTH_SLACK' + + def __init__(self): + self.client = Client( + bot_token=settings.SLACK_BOT_TOKEN, + ) + + def send_msg(self, users, message, subject=None): + accounts, __, __ = self.get_accounts(users) + return self.client.send_text(accounts, message) + + +backend = Slack diff --git a/apps/notifications/notifications.py b/apps/notifications/notifications.py index 19a86ac94..5ceb629c3 100644 --- a/apps/notifications/notifications.py +++ b/apps/notifications/notifications.py @@ -202,6 +202,9 @@ class Message(metaclass=MessageType): def get_site_msg_msg(self) -> dict: return self.html_msg + def get_slack_msg(self) -> dict: + return self.markdown_msg + def get_sms_msg(self) -> dict: return self.text_msg_with_sign diff --git a/apps/settings/api/__init__.py b/apps/settings/api/__init__.py index 510925590..d8b0e5c2f 100644 --- a/apps/settings/api/__init__.py +++ b/apps/settings/api/__init__.py @@ -8,3 +8,4 @@ from .settings import * from .sms import * from .vault import * from .wecom import * +from .slack import * diff --git a/apps/settings/api/settings.py b/apps/settings/api/settings.py index 6b5f096cf..e0879bd62 100644 --- a/apps/settings/api/settings.py +++ b/apps/settings/api/settings.py @@ -39,6 +39,7 @@ class SettingsApi(generics.RetrieveUpdateAPIView): 'wecom': serializers.WeComSettingSerializer, 'dingtalk': serializers.DingTalkSettingSerializer, 'feishu': serializers.FeiShuSettingSerializer, + 'slack': serializers.SlackSettingSerializer, 'auth': serializers.AuthSettingSerializer, 'oidc': serializers.OIDCSettingSerializer, 'keycloak': serializers.KeycloakSettingSerializer, diff --git a/apps/settings/api/slack.py b/apps/settings/api/slack.py new file mode 100644 index 000000000..3a4ad289d --- /dev/null +++ b/apps/settings/api/slack.py @@ -0,0 +1,40 @@ +from rest_framework.views import Response +from rest_framework.generics import GenericAPIView +from rest_framework.exceptions import APIException +from rest_framework import status +from django.utils.translation import gettext_lazy as _ + +from settings.models import Setting +from common.sdk.im.slack import Slack + +from .. import serializers + + +class SlackTestingAPI(GenericAPIView): + serializer_class = serializers.SlackSettingSerializer + rbac_perms = { + 'POST': 'settings.change_auth' + } + + def post(self, request): + serializer = self.serializer_class(data=request.data) + serializer.is_valid(raise_exception=True) + + bot_token = serializer.validated_data.get('SLACK_BOT_TOKEN') + if not bot_token: + secret = Setting.objects.filter(name='SLACK_BOT_TOKEN').first() + if secret: + bot_token = secret.cleaned_value + + bot_token = bot_token or '' + + try: + slack = Slack(bot_token=bot_token) + slack.is_valid() + return Response(status=status.HTTP_200_OK, data={'msg': _('Test success')}) + except APIException as e: + try: + error = e.detail['errmsg'] + except: + error = e.detail + return Response(status=status.HTTP_400_BAD_REQUEST, data={'error': error}) diff --git a/apps/settings/serializers/auth/__init__.py b/apps/settings/serializers/auth/__init__.py index aeca390ac..2b641114c 100644 --- a/apps/settings/serializers/auth/__init__.py +++ b/apps/settings/serializers/auth/__init__.py @@ -11,3 +11,4 @@ from .saml2 import * from .sms import * from .sso import * from .wecom import * +from .slack import * diff --git a/apps/settings/serializers/auth/base.py b/apps/settings/serializers/auth/base.py index 57034e730..fbc833124 100644 --- a/apps/settings/serializers/auth/base.py +++ b/apps/settings/serializers/auth/base.py @@ -17,7 +17,8 @@ class AuthSettingSerializer(serializers.Serializer): AUTH_RADIUS = serializers.BooleanField(required=False, label=_('RADIUS Auth')) AUTH_DINGTALK = serializers.BooleanField(default=False, label=_('DingTalk Auth')) AUTH_FEISHU = serializers.BooleanField(default=False, label=_('FeiShu Auth')) - AUTH_WECOM = serializers.BooleanField(default=False, label=_('WeCom Auth')) + AUTH_WECOM = serializers.BooleanField(default=False, label=_('Slack Auth')) + AUTH_SLACK = serializers.BooleanField(default=False, label=_('WeCom Auth')) AUTH_SSO = serializers.BooleanField(default=False, label=_("SSO Auth")) AUTH_PASSKEY = serializers.BooleanField(default=False, label=_("Passkey Auth")) FORGOT_PASSWORD_URL = serializers.CharField( diff --git a/apps/settings/serializers/auth/slack.py b/apps/settings/serializers/auth/slack.py new file mode 100644 index 000000000..019137f4c --- /dev/null +++ b/apps/settings/serializers/auth/slack.py @@ -0,0 +1,15 @@ +from django.utils.translation import gettext_lazy as _ +from rest_framework import serializers + +from common.serializers.fields import EncryptedField + +__all__ = ['SlackSettingSerializer'] + + +class SlackSettingSerializer(serializers.Serializer): + PREFIX_TITLE = _('Slack') + + AUTH_SLACK = serializers.BooleanField(default=False, label=_('Enable Slack Auth')) + SLACK_CLIENT_ID = serializers.CharField(max_length=256, required=True, label='Client ID') + SLACK_CLIENT_SECRET = EncryptedField(max_length=256, required=False, label='Client Secret') + SLACK_BOT_TOKEN = EncryptedField(max_length=256, required=False, label='Client bot Token') diff --git a/apps/settings/urls/api_urls.py b/apps/settings/urls/api_urls.py index 39a755845..2e36fa41d 100644 --- a/apps/settings/urls/api_urls.py +++ b/apps/settings/urls/api_urls.py @@ -13,6 +13,7 @@ urlpatterns = [ path('wecom/testing/', api.WeComTestingAPI.as_view(), name='wecom-testing'), path('dingtalk/testing/', api.DingTalkTestingAPI.as_view(), name='dingtalk-testing'), path('feishu/testing/', api.FeiShuTestingAPI.as_view(), name='feishu-testing'), + path('slack/testing/', api.SlackTestingAPI.as_view(), name='slack-testing'), path('sms//testing/', api.SMSTestingAPI.as_view(), name='sms-testing'), path('sms/backend/', api.SMSBackendAPI.as_view(), name='sms-backend'), path('vault/testing/', api.VaultTestingAPI.as_view(), name='vault-testing'), diff --git a/apps/static/img/login_slack_logo.png b/apps/static/img/login_slack_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..4d0797eda8ff320aa58db4206805e8ca4ef5d113 GIT binary patch literal 2221 zcmV;e2vYZnP)Px-XGugsRCr$PTW?HM*%kktHv_nKcL5o<$-2xa?RL}Xj3@#Y-F5iT25=co8ca}P z^wZk3+nTif;MSoNvBYgnwkhexHfh>sg+|geKrJM@n=mHIRuGYaZS9({%mdkfz(AWd z{F`^0Hwezan|b%mj8Q;7^3J*EoZmh7+;h%7Ls;W~w8rhn69Argz#2#3;U57HOU`{> z$y;1JX9R1f$7A*a3OpXg2%k~fhXz35KPU27K@)%%?khEuqRdrZdq1lQ9?~|7nQ~iy z?ox;wK}fyNHzENCZ8KFr!0Qnt(GX!$$C^6hwlo4U5qqTYn#0%b{&CaOxmDT*rh=F>v zNe7^?BQVDxlb+!9)9EP-UQp54X!_4Heflwiq4>&bbl29C9sWJ30PMLswslT2^|OEm z47%J^lwPiqZf|urGND3%4VVq)`CPR(g=htck_tfnwZJhV`lEncmE>+`QAWt+9c}JG z05%am0-fkBJ@Y#eS`nJ00`SU}&tIHMN&QGfFoQ?BiZVBa0BEfq1hE(Z3clC7zvhC7 z3=x{70-y>k=$QBtz)$ed@>dYtJVV+jNa7|*dZ~U z1))p}h*b}O-Vq4@zN&(t5yKZ~R|EjBT=~>KosyQ%3K_pSB)YeLdc4hByk$ZQpkx8C ztNHXxQ>oaSI-_*mJW%Ts*$iD{)ZAWTdV>jxr=W~mQ21Eq%bC5&JOYkZw;FJ3L3nCm zT>^s4e9l*P_H-;DVgeAesuO`%gN7N?iZZva`(?}ZWE=rUYxS=|{G+Jq=v0wW0oeV) z#7>3L9jBc!*$4<+FUr`EC;++ddUBOuut!fv2`GrxPlyUY!L7J8wB~s&I&A zr8T*W!Bfx!Ow1KMWoQ2?9s^WLW-92I@PkOZNom;X`wRqTh3mTZWJX+Fj<)Jw0{jyo z>TA`siBQ;6QnQ#th#dhQ_FXsIqP5p!<^j%jHs4E|{5Y(&9k1E*d_*&>C>NF*zqq5V z)#pL{x`rnNh)gBDC7#Z31#|&m8crGdG^hKu-$#vSx|yIVPz{P-rsZA&BR?JlNRp-y z(ih=fhnCOesi`bb=tkCSQzL$8SBH$3*WDBXfyzGXO5w zIFg#P>$Ps5(}lmB5N`)6OU7A3Zda5-#~>qF~&oPGG1B>pxyVw3AbPh(>( z_751?9j|dBd|BmvCZgqWAZiToTMx^CNFBiM+vJ9CCj!9eUcds<^7_?K2BJ6ZN_Eqawb!O#dS+_#3p7QAZJ(A zF|qcj%e<62n_U0H1%ZhH;MhsvYe3ZNGcGU`@Y~P1`lF84=%&JQcGVdseglYTi%&7L zd-G7k`Eacg0YF`6{BMqJiaB%}=v||_U`U{#0_B{=)gOObJY34ZbB@`c3eM*LH%;kq zeDs&wS{ft@z`~m2@8zWj@6DDnDPPgGbSXg)U@5ihs|Ri-=V8Q%0?7ivh|EN3b&r4% z0Z#ykGY=ST*)|A}=7@}<@Vg{Qr#8BLNgIp`-_*y#17ZR2Kw18c#nm7E3CxJAhS5GI z05}E#{UIp^|8%3PKjP+z)9yvK6$Jo@r!3|Di!Z>58n5!NEC5u6&E~CHWj#qtn;#(n zbk`iPoLDFntFLhT|`10oAhR~?&XtP}%j~5!1ot~w< zP2Wx4jgV9iP>u0hkEo4E-D#2m`EE)v=9`w>SYaAlcU!-K$*g1@=+|A_NNh+t0CM)> zGfX<;@`PcVzdqv0ZLE3Q5EI27mj~5aT)xM9TwXrEFw@BDOEED>p%JFwL5u6dmGhICa3Iw7+CHaOD)#C>QiSuV9^t&=RA{CCXeus$ vd<0BM4}y=thJ-{u0U#mxRsr*x_kjNcXRUK?=|RF800000NkvXXu0mjfYV#oj literal 0 HcmV?d00001 diff --git a/apps/users/migrations/0049_alter_user_unique_together_user_slack_id_and_more.py b/apps/users/migrations/0049_alter_user_unique_together_user_slack_id_and_more.py new file mode 100644 index 000000000..5847af8c2 --- /dev/null +++ b/apps/users/migrations/0049_alter_user_unique_together_user_slack_id_and_more.py @@ -0,0 +1,31 @@ +# Generated by Django 4.1.10 on 2023-11-23 08:30 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0048_wechat_phone_encrypt'), + ] + + operations = [ + migrations.AlterUniqueTogether( + name='user', + unique_together={('feishu_id',), ('dingtalk_id',), ('wecom_id',)}, + ), + migrations.AddField( + model_name='user', + name='slack_id', + field=models.CharField(default=None, max_length=128, null=True, verbose_name='Slack'), + ), + migrations.AlterField( + model_name='user', + name='source', + field=models.CharField(choices=[('local', 'Local'), ('ldap', 'LDAP/AD'), ('openid', 'OpenID'), ('radius', 'Radius'), ('cas', 'CAS'), ('saml2', 'SAML2'), ('oauth2', 'OAuth2'), ('wecom', 'WeCom'), ('dingtalk', 'DingTalk'), ('feishu', 'FeiShu'), ('slack', 'Slack'), ('custom', 'Custom')], default='local', max_length=30, verbose_name='Source'), + ), + migrations.AlterUniqueTogether( + name='user', + unique_together={('slack_id',), ('feishu_id',), ('dingtalk_id',), ('wecom_id',)}, + ), + ] diff --git a/apps/users/models/user.py b/apps/users/models/user.py index b8dd467c6..4ce6dcddb 100644 --- a/apps/users/models/user.py +++ b/apps/users/models/user.py @@ -746,6 +746,7 @@ class User(AuthMixin, TokenMixin, RoleMixin, MFAMixin, JSONFilterMixin, Abstract wecom = 'wecom', _('WeCom') dingtalk = 'dingtalk', _('DingTalk') feishu = 'feishu', _('FeiShu') + slack = 'slack', _('Slack') custom = 'custom', 'Custom' SOURCE_BACKEND_MAPPING = { @@ -778,6 +779,9 @@ class User(AuthMixin, TokenMixin, RoleMixin, MFAMixin, JSONFilterMixin, Abstract Source.feishu: [ settings.AUTH_BACKEND_FEISHU ], + Source.slack: [ + settings.AUTH_BACKEND_SLACK + ], Source.dingtalk: [ settings.AUTH_BACKEND_DINGTALK ], @@ -848,6 +852,7 @@ class User(AuthMixin, TokenMixin, RoleMixin, MFAMixin, JSONFilterMixin, Abstract wecom_id = models.CharField(null=True, default=None, max_length=128, verbose_name=_('WeCom')) dingtalk_id = models.CharField(null=True, default=None, max_length=128, verbose_name=_('DingTalk')) feishu_id = models.CharField(null=True, default=None, max_length=128, verbose_name=_('FeiShu')) + slack_id = models.CharField(null=True, default=None, max_length=128, verbose_name=_('Slack')) DATE_EXPIRED_WARNING_DAYS = 5 @@ -990,6 +995,7 @@ class User(AuthMixin, TokenMixin, RoleMixin, MFAMixin, JSONFilterMixin, Abstract ('dingtalk_id',), ('wecom_id',), ('feishu_id',), + ('slack_id',), ) permissions = [ ('invite_user', _('Can invite user')), diff --git a/apps/users/serializers/user.py b/apps/users/serializers/user.py index 59ae59b52..269672ab0 100644 --- a/apps/users/serializers/user.py +++ b/apps/users/serializers/user.py @@ -121,7 +121,7 @@ class UserSerializer(RolesSerializerMixin, CommonBulkSerializerMixin, serializer # small 指的是 不需要计算的直接能从一张表中获取到的数据 fields_small = fields_mini + fields_write_only + [ "email", "wechat", "phone", "mfa_level", "source", - "wecom_id", "dingtalk_id", "feishu_id", + "wecom_id", "dingtalk_id", "feishu_id", "slack_id", "created_by", "updated_by", "comment", # 通用字段 ] fields_date = [ From eee41008ccb5045708002db99be5595ec95771f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B4=E5=B0=8F=E7=99=BD?= <296015668@qq.com> Date: Wed, 29 Nov 2023 11:14:09 +0800 Subject: [PATCH 021/111] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=20celery=20h?= =?UTF-8?q?ealth=20=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- utils/check_celery.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/check_celery.sh b/utils/check_celery.sh index ba2a8777a..e84677081 100644 --- a/utils/check_celery.sh +++ b/utils/check_celery.sh @@ -4,5 +4,5 @@ set -e test -e /tmp/worker_ready_ansible test -e /tmp/worker_ready_celery -test -e /tmp/worker_heartbeat_ansible && test $(($(date +%s) - $(stat -c %Y /tmp/worker_heartbeat_ansible))) -lt 10 -test -e /tmp/worker_heartbeat_celery && test $(($(date +%s) - $(stat -c %Y /tmp/worker_heartbeat_celery))) -lt 10 +test -e /tmp/worker_heartbeat_ansible && test $(($(date +%s) - $(stat -c %Y /tmp/worker_heartbeat_ansible))) -lt 20 +test -e /tmp/worker_heartbeat_celery && test $(($(date +%s) - $(stat -c %Y /tmp/worker_heartbeat_celery))) -lt 20 From 3cd22f05d254c15a5e225cd98ea2943836b616a2 Mon Sep 17 00:00:00 2001 From: wangruidong <940853815@qq.com> Date: Wed, 29 Nov 2023 11:39:20 +0800 Subject: [PATCH 022/111] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E5=B7=A5?= =?UTF-8?q?=E5=8D=95=E5=A4=84=E7=90=86=E6=8F=90=E7=A4=BA=E6=B6=88=E6=81=AF?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../templates/tickets/_msg_ticket.html | 33 ++-- .../tickets/approve_check_password.html | 144 +++++++++++++----- 2 files changed, 117 insertions(+), 60 deletions(-) diff --git a/apps/tickets/templates/tickets/_msg_ticket.html b/apps/tickets/templates/tickets/_msg_ticket.html index f26d17e5f..b781e1738 100644 --- a/apps/tickets/templates/tickets/_msg_ticket.html +++ b/apps/tickets/templates/tickets/_msg_ticket.html @@ -1,34 +1,31 @@ {% load i18n %}
    -

    +

    {{ title | safe }} -

    +

    {% for child in content %} -

    {{ child.title }}

    - {% for item in child.content %} -
  • - {{ item.title }} - {{ item.value }} -
  • - {% endfor %} +
    +

    {{ child.title }}

    + {% for item in child.content %} +

    + {{ item.title }}: + {{ item.value }} +

    + {% endfor %} +
    {% endfor %}
    -
    - diff --git a/apps/tickets/templates/tickets/approve_check_password.html b/apps/tickets/templates/tickets/approve_check_password.html index fe256bc1c..b82ef76cc 100644 --- a/apps/tickets/templates/tickets/approve_check_password.html +++ b/apps/tickets/templates/tickets/approve_check_password.html @@ -5,55 +5,115 @@ {% block content %} -
    -
    -
    -

    {% trans 'Ticket information' %}

    -

    -
    +
    +
    +
    +

    {% trans 'Ticket information' %}

    {% for child in content %} -

    {{ child.title }}

    -
    - {% for item in child.content %} -
  • - {{ item.title }}: {{ item.value }} -
  • - {% endfor %} +
    +

    {{ child.title }}

    + {% for item in child.content %} +

    + {{ item.title }}: + {{ item.value }} +

    + {% endfor %} +
    {% endfor %}
    -
    -
    -
    - -

    {% trans 'Ticket approval' %}

    -

    +
    -

    - {% trans 'Hello' %} {{ user.name }}, -

    -

    - {{ prompt_msg }} -

    -
    -
    - {% csrf_token %} -
    - - -
    -
    + +

    {% trans 'Ticket approval' %}

    +

    +
    +

    + {% trans 'Hello' %} {{ user.name }}, +

    +

    + {{ prompt_msg }} +

    +
    +
    + {% csrf_token %} +
    + + +
    +
    +
    -
    - + .ticket-container { + flex-shrink: 0; + border-radius: 4px; + background: #FFF; + font-style: normal; + font-weight: 400; + line-height: 24px; /* 150% */ + + + .card { + .child_title { + padding-top: 16px; + margin: 0 0 12px 16px; + display: inline-flex; + flex-direction: column; + align-items: flex-start; + color: #1F2329; + font-size: 16px; + font-style: normal; + font-weight: 500; + } + + @media (max-width: 400px) { + margin: 8px + } + margin: 24px 0 0 24px; + width: 95%; + display: inline-block; + border-radius: 4px; + background: #F5F6F7; + } + + .card:last-child { + margin-bottom: 24px; + } + + .field-group { + font-size: 14px; + padding-inline-start: 0; + margin: 0; + width: 95%; + + .field-name { + margin: 4px 0 4px 16px; + color: #646A73; + display: inline-block; + + :is(strong) { + font-weight: 400 !important; + } + } + + .field-value { + color: #1F2329; + display: inline-block; + } + } + } + {% endblock %} From cf183003602e1f5f25e7fc0dc42071637e2b8fb0 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Thu, 30 Nov 2023 14:38:43 +0800 Subject: [PATCH 023/111] =?UTF-8?q?fix:=20=E6=B7=BB=E5=8A=A0Slack=E8=AE=A4?= =?UTF-8?q?=E8=AF=81=E5=8F=91=E9=80=81=E6=B6=88=E6=81=AF=E6=A0=BC=E5=BC=8F?= =?UTF-8?q?=E5=8C=85=20(#12229)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: jiangweidong --- poetry.lock | 55 +++++++++++++++++--------------------------------- pyproject.toml | 1 + 2 files changed, 19 insertions(+), 37 deletions(-) diff --git a/poetry.lock b/poetry.lock index 61cf612f4..9737ea286 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. [[package]] name = "adal" @@ -2332,17 +2332,6 @@ files = [ {file = "ephem-4.1.4-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:8f9b27117e7a82f7f70db9cb23b5cc36d37b166a2f73c55e14d7225d0ab95afa"}, {file = "ephem-4.1.4-cp311-cp311-win32.whl", hash = "sha256:9bb21c0b117c9122c0141b0a71ee6fbbb087ed2aab4a7ab60f009e95e9f4a521"}, {file = "ephem-4.1.4-cp311-cp311-win_amd64.whl", hash = "sha256:55d7fb5c34b2e453e01fa4ca7ee375b19b438c9401ae8c4099ae4a3a37656972"}, - {file = "ephem-4.1.4-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:f9e24aeea560dfcece3c2e313eb94e6be3e84888091455e541fa88f3a44da584"}, - {file = "ephem-4.1.4-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:653d99386932e5f78bb9cfc4495030ad9f3345eb4c2b32dca55547da8f1f0332"}, - {file = "ephem-4.1.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53786461a6d5799d5fffe76622ad51444b264d1c7263b92a6dfcac640c3da93a"}, - {file = "ephem-4.1.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:268f57f8768ccb0abbdf4cefb4781c7db812950019868f687b407b428513ee53"}, - {file = "ephem-4.1.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d630aa287255ea9fba6962f351e4e0729bb620570684d52fbfcc31b11527f09e"}, - {file = "ephem-4.1.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b5f229bbf62ecb4cd6bb3374b15d0f8ff7b3d970c2936fccd89bdf9d693907a2"}, - {file = "ephem-4.1.4-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:d60d56f182de54bd84fadd6ea2dd8e8ef6fdef6a698c7cafd404ecb6eeefa598"}, - {file = "ephem-4.1.4-cp312-cp312-musllinux_1_1_s390x.whl", hash = "sha256:404500c8d0030d75ec15bb6b98eee78ad163fd5252102c962ae6fb39c9488198"}, - {file = "ephem-4.1.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:9fb020d6cc5ab1ad1cd9d3da4a6e2506beebb41d1b337d79cc20cc0a17f550f1"}, - {file = "ephem-4.1.4-cp312-cp312-win32.whl", hash = "sha256:29e71636ee4719419d03184abc85085f76989c79a61844f5e60acbf2513d2b42"}, - {file = "ephem-4.1.4-cp312-cp312-win_amd64.whl", hash = "sha256:549654f63d88e0ab6248ae25ac2939131474ab9f3a91bee6b68ca6f214747c2a"}, {file = "ephem-4.1.4-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:40067fc050c946c8d4c2d779805b61f063471a091e6124cbabcf61ac538011b2"}, {file = "ephem-4.1.4-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:7e2abe97aa2b091090012768b4d94793213cc01f0bf040dcc311a380ab08df69"}, {file = "ephem-4.1.4-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3b2677d3a5b42aedc578de10b0eecdba6a50731f159cb28f7ad38c5f62143494"}, @@ -3688,16 +3677,6 @@ files = [ {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"}, - {file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, @@ -3819,6 +3798,22 @@ type = "legacy" url = "https://pypi.tuna.tsinghua.edu.cn/simple" reference = "tsinghua" +[[package]] +name = "mistune" +version = "0.8.4" +description = "The fastest markdown parser in pure Python" +optional = false +python-versions = "*" +files = [ + {file = "mistune-0.8.4-py2.py3-none-any.whl", hash = "sha256:88a1051873018da288eee8538d476dffe1262495144b33ecb586c4ab266bb8d4"}, + {file = "mistune-0.8.4.tar.gz", hash = "sha256:59a3429db53c50b5c6bcc8a07f8848cb00d7dc8bdb431a4ab41920d201d4756e"}, +] + +[package.source] +type = "legacy" +url = "https://pypi.tuna.tsinghua.edu.cn/simple" +reference = "tsinghua" + [[package]] name = "msal" version = "1.25.0" @@ -5403,11 +5398,9 @@ files = [ {file = "pymssql-2.2.8-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:049f2e3de919e8e02504780a21ebbf235e21ca8ed5c7538c5b6e705aa6c43d8c"}, {file = "pymssql-2.2.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dd86d8e3e346e34f3f03d12e333747b53a1daa74374a727f4714d5b82ee0dd5"}, {file = "pymssql-2.2.8-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:508226a0df7cb6faeda9f8e84e85743690ca427d7b27af9a73d75fcf0c1eef6e"}, - {file = "pymssql-2.2.8-cp310-cp310-win_amd64.whl", hash = "sha256:47859887adeaf184766b5e0bc845dd23611f3808f9521552063bb36eabc10092"}, {file = "pymssql-2.2.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d873e553374d5b1c57fe1c43bb75e3bcc2920678db1ef26f6bfed396c7d21b30"}, {file = "pymssql-2.2.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf31b8b76634c826a91f9999e15b7bfb0c051a0f53b319fd56481a67e5b903bb"}, {file = "pymssql-2.2.8-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:821945c2214fe666fd456c61e09a29a00e7719c9e136c801bffb3a254e9c579b"}, - {file = "pymssql-2.2.8-cp311-cp311-win_amd64.whl", hash = "sha256:cc85b609b4e60eac25fa38bbac1ff854fd2c2a276e0ca4a3614c6f97efb644bb"}, {file = "pymssql-2.2.8-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:ebe7f64d5278d807f14bea08951e02512bfbc6219fd4d4f15bb45ded885cf3d4"}, {file = "pymssql-2.2.8-cp36-cp36m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:253af3d39fc0235627966817262d5c4c94ad09dcbea59664748063470048c29c"}, {file = "pymssql-2.2.8-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c9d109df536dc5f7dd851a88d285a4c9cb12a9314b621625f4f5ab1197eb312"}, @@ -5423,13 +5416,11 @@ files = [ {file = "pymssql-2.2.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3906993300650844ec140aa58772c0f5f3e9e9d5709c061334fd1551acdcf066"}, {file = "pymssql-2.2.8-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:7309c7352e4a87c9995c3183ebfe0ff4135e955bb759109637673c61c9f0ca8d"}, {file = "pymssql-2.2.8-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:9b8d603cc1ec7ae585c5a409a1d45e8da067970c79dd550d45c238ae0aa0f79f"}, - {file = "pymssql-2.2.8-cp38-cp38-win_amd64.whl", hash = "sha256:293cb4d0339e221d877d6b19a1905082b658f0100a1e2ccc9dda10de58938901"}, {file = "pymssql-2.2.8-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:895041edd002a2e91d8a4faf0906b6fbfef29d9164bc6beb398421f5927fa40e"}, {file = "pymssql-2.2.8-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6b2d9c6d38a416c6f2db36ff1cd8e69f9a5387a46f9f4f612623192e0c9404b1"}, {file = "pymssql-2.2.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d63d6f25cf40fe6a03c49be2d4d337858362b8ab944d6684c268e4990807cf0c"}, {file = "pymssql-2.2.8-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:c83ad3ad20951f3a94894b354fa5fa9666dcd5ebb4a635dad507c7d1dd545833"}, {file = "pymssql-2.2.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:3933f7f082be74698eea835df51798dab9bc727d94d3d280bffc75ab9265f890"}, - {file = "pymssql-2.2.8-cp39-cp39-win_amd64.whl", hash = "sha256:de313375b90b0f554058992f35c4a4beb3f6ec2f5912d8cd6afb649f95b03a9f"}, {file = "pymssql-2.2.8.tar.gz", hash = "sha256:9baefbfbd07d0142756e2dfcaa804154361ac5806ab9381350aad4e780c3033e"}, ] @@ -5918,7 +5909,6 @@ files = [ {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, - {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, @@ -5926,15 +5916,8 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, - {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, - {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, - {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, - {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, - {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, - {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, @@ -5951,7 +5934,6 @@ files = [ {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, - {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, @@ -5959,7 +5941,6 @@ files = [ {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, - {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, @@ -7398,4 +7379,4 @@ reference = "tsinghua" [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "5b8fbaefc91f899fd983c7fa04091cd453f51137a1fdc17a52e94e77b53e5e70" +content-hash = "15901aec4160752d2162dd1af1c5afb2dd848784d78ce8f84693e4ceddf01875" diff --git a/pyproject.toml b/pyproject.toml index 5d573d424..c62fdbe7f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -142,6 +142,7 @@ channels-redis = "4.1.0" fido2 = "^1.1.2" ua-parser = "^0.18.0" user-agents = "^2.2.0" +mistune = "0.8.4" [tool.poetry.group.xpack.dependencies] From 2cad97065f78958ee3c91dc0108f75b0670e2c53 Mon Sep 17 00:00:00 2001 From: wangruidong <940853815@qq.com> Date: Thu, 30 Nov 2023 11:14:07 +0800 Subject: [PATCH 024/111] =?UTF-8?q?feat:=20=E8=B5=84=E4=BA=A7=E8=AF=A6?= =?UTF-8?q?=E6=83=85=E9=A1=B5=E9=9D=A2=E6=B7=BB=E5=8A=A0=E5=8E=86=E5=8F=B2?= =?UTF-8?q?=E6=89=A7=E8=A1=8C=E5=91=BD=E4=BB=A4=E5=88=97=E8=A1=A8=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/terminal/api/session/command.py | 5 +++++ apps/terminal/api/session/session.py | 2 +- apps/terminal/filters.py | 3 ++- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/apps/terminal/api/session/command.py b/apps/terminal/api/session/command.py index 494d001c0..387aebc3e 100644 --- a/apps/terminal/api/session/command.py +++ b/apps/terminal/api/session/command.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- # +import uuid from django.utils import timezone from rest_framework import generics from rest_framework.fields import DateTimeField @@ -166,6 +167,7 @@ class CommandViewSet(JMSBulkModelViewSet): def get_queryset(self): command_storage_id = self.request.query_params.get('command_storage_id') + asset_id = self.request.query_params.get('asset_id') if not command_storage_id: return Command.objects.none() @@ -174,6 +176,9 @@ class CommandViewSet(JMSBulkModelViewSet): raise StorageInvalid else: qs = storage.get_command_queryset() + if asset_id: + session_ids = Session.objects.filter(asset_id=asset_id).values_list('id', flat=True) + qs = qs.filter(session__in=list(session_ids)) return qs def create(self, request, *args, **kwargs): diff --git a/apps/terminal/api/session/session.py b/apps/terminal/api/session/session.py index fd8bc0ecd..8865801ac 100644 --- a/apps/terminal/api/session/session.py +++ b/apps/terminal/api/session/session.py @@ -62,7 +62,7 @@ class SessionFilterSet(BaseFilterSet): class Meta: model = Session fields = [ - "user", "user_id", "asset", "account", "remote_addr", + "user", "user_id", "asset", "asset_id", "account", "remote_addr", "protocol", "is_finished", 'login_from', 'terminal' ] diff --git a/apps/terminal/filters.py b/apps/terminal/filters.py index ae11733cf..40c048b28 100644 --- a/apps/terminal/filters.py +++ b/apps/terminal/filters.py @@ -13,11 +13,12 @@ class CommandFilter(filters.FilterSet): user = filters.CharFilter(lookup_expr='startswith') input = filters.CharFilter(lookup_expr='icontains') asset = filters.CharFilter(field_name='asset', lookup_expr='icontains') + asset_id = filters.UUIDFilter(method='do_nothing') class Meta: model = Command fields = [ - 'asset', 'account', 'user', 'session', 'risk_level', 'input', + 'asset', 'asset_id', 'account', 'user', 'session', 'risk_level', 'input', 'date_from', 'date_to', 'session_id', 'risk_level', 'command_storage_id', ] From a91cb1afd5f88d1d6226748eca6dc437b41111c6 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Tue, 5 Dec 2023 10:58:19 +0800 Subject: [PATCH 025/111] =?UTF-8?q?feat:=20=E7=B3=BB=E7=BB=9F=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE=E5=8F=AF=E9=85=8D=E7=BD=AEgpt=20(#12207)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 系统设置可配置gpt * perf: 添加gpt的terminal config --------- Co-authored-by: feng <1304903146@qq.com> --- apps/jumpserver/conf.py | 7 + apps/jumpserver/settings/custom.py | 7 + apps/settings/api/__init__.py | 3 +- apps/settings/api/chat.py | 77 ++++ apps/settings/api/settings.py | 1 + .../migrations/0010_alter_setting_options.py | 19 +- apps/settings/models.py | 1 + apps/settings/serializers/feature.py | 42 +- apps/settings/urls/api_urls.py | 1 + apps/terminal/models/component/terminal.py | 10 + poetry.lock | 365 +++++++++++++++++- pyproject.toml | 1 + 12 files changed, 526 insertions(+), 8 deletions(-) create mode 100644 apps/settings/api/chat.py diff --git a/apps/jumpserver/conf.py b/apps/jumpserver/conf.py index 6d014c80e..ec224d7c8 100644 --- a/apps/jumpserver/conf.py +++ b/apps/jumpserver/conf.py @@ -590,6 +590,13 @@ class Config(dict): 'MAX_LIMIT_PER_PAGE': 100, 'LIMIT_SUPER_PRIV': False, + + # Chat AI + 'CHAT_AI_ENABLED': False, + 'GPT_API_KEY': '', + 'GPT_BASE_URL': '', + 'GPT_PROXY': '', + 'GPT_MODEL': 'gpt-3.5-turbo', } old_config_map = { diff --git a/apps/jumpserver/settings/custom.py b/apps/jumpserver/settings/custom.py index dd75f56ec..3c0cfed58 100644 --- a/apps/jumpserver/settings/custom.py +++ b/apps/jumpserver/settings/custom.py @@ -211,3 +211,10 @@ MAX_LIMIT_PER_PAGE = CONFIG.MAX_LIMIT_PER_PAGE # Magnus DB Port MAGNUS_ORACLE_PORTS = CONFIG.MAGNUS_ORACLE_PORTS LIMIT_SUPER_PRIV = CONFIG.LIMIT_SUPER_PRIV + +# Chat AI +CHAT_AI_ENABLED = CONFIG.CHAT_AI_ENABLED +GPT_API_KEY = CONFIG.GPT_API_KEY +GPT_BASE_URL = CONFIG.GPT_BASE_URL +GPT_PROXY = CONFIG.GPT_PROXY +GPT_MODEL = CONFIG.GPT_MODEL diff --git a/apps/settings/api/__init__.py b/apps/settings/api/__init__.py index d8b0e5c2f..686a52f3b 100644 --- a/apps/settings/api/__init__.py +++ b/apps/settings/api/__init__.py @@ -1,3 +1,4 @@ +from .chat import * from .dingtalk import * from .email import * from .feishu import * @@ -5,7 +6,7 @@ from .ldap import * from .public import * from .security import * from .settings import * +from .slack import * from .sms import * from .vault import * from .wecom import * -from .slack import * diff --git a/apps/settings/api/chat.py b/apps/settings/api/chat.py new file mode 100644 index 000000000..50c93639e --- /dev/null +++ b/apps/settings/api/chat.py @@ -0,0 +1,77 @@ +import httpx +import openai +from django.conf import settings +from django.utils.translation import gettext_lazy as _ +from rest_framework import status +from rest_framework.generics import GenericAPIView +from rest_framework.views import Response + +from .. import serializers + + +class ChatAITestingAPI(GenericAPIView): + serializer_class = serializers.ChatAISettingSerializer + rbac_perms = { + 'POST': 'settings.change_chatai' + } + + def get_config(self, request): + serializer = self.serializer_class(data=request.data) + serializer.is_valid(raise_exception=True) + data = serializer.validated_data + for k, v in data.items(): + if v: + continue + # 页面没有传递值, 从 settings 中获取 + data[k] = getattr(settings, k, None) + return data + + def post(self, request): + config = self.get_config(request) + chat_ai_enabled = config['CHAT_AI_ENABLED'] + if not chat_ai_enabled: + return Response( + status=status.HTTP_400_BAD_REQUEST, + data={'msg': _('Chat AI is not enabled')} + ) + + proxy = config['GPT_PROXY'] + model = config['GPT_MODEL'] + + kwargs = { + 'base_url': config['GPT_BASE_URL'] or None, + 'api_key': config['GPT_API_KEY'], + } + if proxy: + kwargs['http_client'] = httpx.Client( + proxies=proxy, + transport=httpx.HTTPTransport(local_address='0.0.0.0') + ) + client = openai.OpenAI(**kwargs) + + ok = False + error = '' + try: + client.chat.completions.create( + messages=[ + { + "role": "user", + "content": "Say this is a test", + } + ], + model=model, + ) + ok = True + except openai.APIConnectionError as e: + error = str(e.__cause__) # an underlying Exception, likely raised within httpx. + except openai.APIStatusError as e: + error = str(e.message) + except Exception as e: + ok, error = False, str(e) + + if ok: + _status, msg = status.HTTP_200_OK, _('Test success') + else: + _status, msg = status.HTTP_400_BAD_REQUEST, error + + return Response(status=_status, data={'msg': msg}) diff --git a/apps/settings/api/settings.py b/apps/settings/api/settings.py index e0879bd62..af71cad7e 100644 --- a/apps/settings/api/settings.py +++ b/apps/settings/api/settings.py @@ -57,6 +57,7 @@ class SettingsApi(generics.RetrieveUpdateAPIView): 'cmpp2': serializers.CMPP2SMSSettingSerializer, 'custom': serializers.CustomSMSSettingSerializer, 'vault': serializers.VaultSettingSerializer, + 'chat': serializers.ChatAISettingSerializer, 'announcement': serializers.AnnouncementSettingSerializer, 'ticket': serializers.TicketSettingSerializer, 'ops': serializers.OpsSettingSerializer, diff --git a/apps/settings/migrations/0010_alter_setting_options.py b/apps/settings/migrations/0010_alter_setting_options.py index 3d52a64da..0ce372e76 100644 --- a/apps/settings/migrations/0010_alter_setting_options.py +++ b/apps/settings/migrations/0010_alter_setting_options.py @@ -4,7 +4,6 @@ from django.db import migrations class Migration(migrations.Migration): - dependencies = [ ('settings', '0009_alter_cas_username_attribute'), ] @@ -12,6 +11,22 @@ class Migration(migrations.Migration): operations = [ migrations.AlterModelOptions( name='setting', - options={'permissions': [('change_email', 'Can change email setting'), ('change_auth', 'Can change auth setting'), ('change_ops', 'Can change auth ops'), ('change_ticket', 'Can change auth ticket'), ('change_announcement', 'Can change auth announcement'), ('change_vault', 'Can change vault setting'), ('change_systemmsgsubscription', 'Can change system msg sub setting'), ('change_sms', 'Can change sms setting'), ('change_security', 'Can change security setting'), ('change_clean', 'Can change clean setting'), ('change_interface', 'Can change interface setting'), ('change_license', 'Can change license setting'), ('change_terminal', 'Can change terminal setting'), ('change_other', 'Can change other setting')], 'verbose_name': 'System setting'}, + options={'permissions': [ + ('change_email', 'Can change email setting'), + ('change_auth', 'Can change auth setting'), + ('change_ops', 'Can change auth ops'), + ('change_ticket', 'Can change auth ticket'), + ('change_announcement', 'Can change auth announcement'), + ('change_vault', 'Can change vault setting'), + ('change_chatai', 'Can change chat ai setting'), + ('change_systemmsgsubscription', 'Can change system msg sub setting'), + ('change_sms', 'Can change sms setting'), + ('change_security', 'Can change security setting'), + ('change_clean', 'Can change clean setting'), + ('change_interface', 'Can change interface setting'), + ('change_license', 'Can change license setting'), + ('change_terminal', 'Can change terminal setting'), + ('change_other', 'Can change other setting') + ], 'verbose_name': 'System setting'}, ), ] diff --git a/apps/settings/models.py b/apps/settings/models.py index dc599bbe4..660af78ec 100644 --- a/apps/settings/models.py +++ b/apps/settings/models.py @@ -163,6 +163,7 @@ class Setting(models.Model): ('change_ticket', _('Can change auth ticket')), ('change_announcement', _('Can change auth announcement')), ('change_vault', _('Can change vault setting')), + ('change_chatai', _('Can change chat ai setting')), ('change_systemmsgsubscription', _('Can change system msg sub setting')), ('change_sms', _('Can change sms setting')), ('change_security', _('Can change security setting')), diff --git a/apps/settings/serializers/feature.py b/apps/settings/serializers/feature.py index f2efe5b03..3b7f80fef 100644 --- a/apps/settings/serializers/feature.py +++ b/apps/settings/serializers/feature.py @@ -3,11 +3,13 @@ import uuid from django.utils.translation import gettext_lazy as _ from rest_framework import serializers +from assets.const import Protocol from common.serializers.fields import EncryptedField __all__ = [ 'AnnouncementSettingSerializer', 'OpsSettingSerializer', - 'VaultSettingSerializer', 'TicketSettingSerializer' + 'VaultSettingSerializer', 'TicketSettingSerializer', + 'ChatAISettingSerializer' ] @@ -54,6 +56,44 @@ class VaultSettingSerializer(serializers.Serializer): ) +class ChatAISettingSerializer(serializers.Serializer): + PREFIX_TITLE = _('Chat AI') + GPT_MODEL_CHOICES = [] + + CHAT_AI_ENABLED = serializers.BooleanField( + required=False, label=_('Enable Vault') + ) + GPT_BASE_URL = serializers.CharField( + max_length=256, allow_blank=True, required=False, label=_('Base Url') + ) + GPT_API_KEY = EncryptedField( + max_length=256, allow_blank=True, required=False, label=_('Api Key'), + ) + GPT_PROXY = serializers.CharField( + max_length=256, allow_blank=True, required=False, label=_('Proxy') + ) + GPT_MODEL = serializers.ChoiceField( + default='', choices=GPT_MODEL_CHOICES, label=_("GPT Model"), required=False, + ) + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.set_GPT_MODEL_choices() + + def set_GPT_MODEL_choices(self): + field_gpt_model = self.fields.get("GPT_MODEL") + if not field_gpt_model: + return + gpt_api_model = Protocol.gpt_protocols()[Protocol.chatgpt]['setting']['api_mode'] + choices = gpt_api_model['choices'] + field_gpt_model._choices = choices + field_gpt_model.default = gpt_api_model['default'] + cls = self.__class__ + if cls.GPT_MODEL_CHOICES: + return + cls.GPT_MODEL_CHOICES.extend(choices) + + class TicketSettingSerializer(serializers.Serializer): PREFIX_TITLE = _('Ticket') diff --git a/apps/settings/urls/api_urls.py b/apps/settings/urls/api_urls.py index 2e36fa41d..ed8cf74de 100644 --- a/apps/settings/urls/api_urls.py +++ b/apps/settings/urls/api_urls.py @@ -17,6 +17,7 @@ urlpatterns = [ path('sms//testing/', api.SMSTestingAPI.as_view(), name='sms-testing'), path('sms/backend/', api.SMSBackendAPI.as_view(), name='sms-backend'), path('vault/testing/', api.VaultTestingAPI.as_view(), name='vault-testing'), + path('chatai/testing/', api.ChatAITestingAPI.as_view(), name='chatai-testing'), path('vault/sync/', api.VaultSyncDataAPI.as_view(), name='vault-sync'), path('security/block-ip/', api.BlockIPSecurityAPI.as_view(), name='block-ip'), path('security/unlock-ip/', api.UnlockIPSecurityAPI.as_view(), name='unlock-ip'), diff --git a/apps/terminal/models/component/terminal.py b/apps/terminal/models/component/terminal.py index 9955abf03..a76d67dc6 100644 --- a/apps/terminal/models/component/terminal.py +++ b/apps/terminal/models/component/terminal.py @@ -117,6 +117,15 @@ class Terminal(StorageMixin, TerminalStatusMixin, JMSBaseModel): from settings.utils import get_login_title return {'TERMINAL_HEADER_TITLE': get_login_title()} + @staticmethod + def get_chat_ai_setting(): + return { + 'GPT_BASE_URL': settings.GPT_BASE_URL, + 'GPT_API_KEY': settings.GPT_API_KEY, + 'GPT_PROXY': settings.GPT_PROXY, + 'GPT_MODEL': settings.GPT_MODEL, + } + @property def config(self): configs = {} @@ -127,6 +136,7 @@ class Terminal(StorageMixin, TerminalStatusMixin, JMSBaseModel): configs.update(self.get_command_storage_setting()) configs.update(self.get_replay_storage_setting()) configs.update(self.get_login_title_setting()) + configs.update(self.get_chat_ai_setting()) configs.update({ 'SECURITY_MAX_IDLE_TIME': settings.SECURITY_MAX_IDLE_TIME, 'SECURITY_SESSION_SHARE': settings.SECURITY_SESSION_SHARE, diff --git a/poetry.lock b/poetry.lock index 9737ea286..889ea6e37 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. [[package]] name = "adal" @@ -425,6 +425,22 @@ type = "legacy" url = "https://pypi.tuna.tsinghua.edu.cn/simple" reference = "tsinghua" +[[package]] +name = "annotated-types" +version = "0.6.0" +description = "Reusable constraint types to use with typing.Annotated" +optional = false +python-versions = ">=3.8" +files = [ + {file = "annotated_types-0.6.0-py3-none-any.whl", hash = "sha256:0641064de18ba7a25dee8f96403ebc39113d0cb953a01429249d5c7564666a43"}, + {file = "annotated_types-0.6.0.tar.gz", hash = "sha256:563339e807e53ffd9c267e99fc6d9ea23eb8443c08f112651963e24e22f84a5d"}, +] + +[package.source] +type = "legacy" +url = "https://pypi.tuna.tsinghua.edu.cn/simple" +reference = "tsinghua" + [[package]] name = "ansible" version = "7.1.0" @@ -488,6 +504,31 @@ type = "legacy" url = "https://pypi.tuna.tsinghua.edu.cn/simple" reference = "tsinghua" +[[package]] +name = "anyio" +version = "3.7.1" +description = "High level compatibility layer for multiple asynchronous event loop implementations" +optional = false +python-versions = ">=3.7" +files = [ + {file = "anyio-3.7.1-py3-none-any.whl", hash = "sha256:91dee416e570e92c64041bd18b900d1d6fa78dff7048769ce5ac5ddad004fbb5"}, + {file = "anyio-3.7.1.tar.gz", hash = "sha256:44a3c9aba0f5defa43261a8b3efb97891f2bd7d804e0e1f56419befa1adfc780"}, +] + +[package.dependencies] +idna = ">=2.8" +sniffio = ">=1.1" + +[package.extras] +doc = ["Sphinx", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx-rtd-theme (>=1.2.2)", "sphinxcontrib-jquery"] +test = ["anyio[trio]", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "mock (>=4)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)"] +trio = ["trio (<0.22)"] + +[package.source] +type = "legacy" +url = "https://pypi.tuna.tsinghua.edu.cn/simple" +reference = "tsinghua" + [[package]] name = "appnope" version = "0.1.3" @@ -1755,6 +1796,22 @@ type = "legacy" url = "https://pypi.tuna.tsinghua.edu.cn/simple" reference = "tsinghua" +[[package]] +name = "distro" +version = "1.8.0" +description = "Distro - an OS platform information API" +optional = false +python-versions = ">=3.6" +files = [ + {file = "distro-1.8.0-py3-none-any.whl", hash = "sha256:99522ca3e365cac527b44bde033f64c6945d90eb9f769703caaec52b09bbd3ff"}, + {file = "distro-1.8.0.tar.gz", hash = "sha256:02e111d1dc6a50abb8eed6bf31c3e48ed8b0830d1ea2a1b78c61765c2513fdd8"}, +] + +[package.source] +type = "legacy" +url = "https://pypi.tuna.tsinghua.edu.cn/simple" +reference = "tsinghua" + [[package]] name = "django" version = "4.1.10" @@ -2658,8 +2715,14 @@ files = [ [package.dependencies] google-auth = ">=2.14.1,<3.0.dev0" googleapis-common-protos = ">=1.56.2,<2.0.dev0" -grpcio = {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} -grpcio-status = {version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""} +grpcio = [ + {version = ">=1.33.2,<2.0dev", optional = true, markers = "extra == \"grpc\""}, + {version = ">=1.49.1,<2.0dev", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}, +] +grpcio-status = [ + {version = ">=1.33.2,<2.0.dev0", optional = true, markers = "extra == \"grpc\""}, + {version = ">=1.49.1,<2.0.dev0", optional = true, markers = "python_version >= \"3.11\" and extra == \"grpc\""}, +] protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" requests = ">=2.18.0,<3.0.0.dev0" @@ -2959,6 +3022,32 @@ type = "legacy" url = "https://pypi.tuna.tsinghua.edu.cn/simple" reference = "tsinghua" +[[package]] +name = "httpcore" +version = "1.0.2" +description = "A minimal low-level HTTP client." +optional = false +python-versions = ">=3.8" +files = [ + {file = "httpcore-1.0.2-py3-none-any.whl", hash = "sha256:096cc05bca73b8e459a1fc3dcf585148f63e534eae4339559c9b8a8d6399acc7"}, + {file = "httpcore-1.0.2.tar.gz", hash = "sha256:9fc092e4799b26174648e54b74ed5f683132a464e95643b226e00c2ed2fa6535"}, +] + +[package.dependencies] +certifi = "*" +h11 = ">=0.13,<0.15" + +[package.extras] +asyncio = ["anyio (>=4.0,<5.0)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] +trio = ["trio (>=0.22.0,<0.23.0)"] + +[package.source] +type = "legacy" +url = "https://pypi.tuna.tsinghua.edu.cn/simple" +reference = "tsinghua" + [[package]] name = "httpsig" version = "1.3.0" @@ -2979,6 +3068,35 @@ type = "legacy" url = "https://pypi.tuna.tsinghua.edu.cn/simple" reference = "tsinghua" +[[package]] +name = "httpx" +version = "0.25.1" +description = "The next generation HTTP client." +optional = false +python-versions = ">=3.8" +files = [ + {file = "httpx-0.25.1-py3-none-any.whl", hash = "sha256:fec7d6cc5c27c578a391f7e87b9aa7d3d8fbcd034f6399f9f79b45bcc12a866a"}, + {file = "httpx-0.25.1.tar.gz", hash = "sha256:ffd96d5cf901e63863d9f1b4b6807861dbea4d301613415d9e6e57ead15fc5d0"}, +] + +[package.dependencies] +anyio = "*" +certifi = "*" +httpcore = "*" +idna = "*" +sniffio = "*" + +[package.extras] +brotli = ["brotli", "brotlicffi"] +cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] +http2 = ["h2 (>=3,<5)"] +socks = ["socksio (==1.*)"] + +[package.source] +type = "legacy" +url = "https://pypi.tuna.tsinghua.edu.cn/simple" +reference = "tsinghua" + [[package]] name = "huaweicloudsdkcore" version = "3.1.52" @@ -3677,6 +3795,16 @@ files = [ {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, @@ -4183,6 +4311,34 @@ type = "legacy" url = "https://pypi.tuna.tsinghua.edu.cn/simple" reference = "tsinghua" +[[package]] +name = "openai" +version = "1.3.7" +description = "The official Python library for the openai API" +optional = false +python-versions = ">=3.7.1" +files = [ + {file = "openai-1.3.7-py3-none-any.whl", hash = "sha256:e5c51367a910297e4d1cd33d2298fb87d7edf681edbe012873925ac16f95bee0"}, + {file = "openai-1.3.7.tar.gz", hash = "sha256:18074a0f51f9b49d1ae268c7abc36f7f33212a0c0d08ce11b7053ab2d17798de"}, +] + +[package.dependencies] +anyio = ">=3.5.0,<4" +distro = ">=1.7.0,<2" +httpx = ">=0.23.0,<1" +pydantic = ">=1.9.0,<3" +sniffio = "*" +tqdm = ">4" +typing-extensions = ">=4.5,<5" + +[package.extras] +datalib = ["numpy (>=1)", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)"] + +[package.source] +type = "legacy" +url = "https://pypi.tuna.tsinghua.edu.cn/simple" +reference = "tsinghua" + [[package]] name = "openapi-codec" version = "1.3.2" @@ -5074,6 +5230,152 @@ type = "legacy" url = "https://pypi.tuna.tsinghua.edu.cn/simple" reference = "tsinghua" +[[package]] +name = "pydantic" +version = "2.5.2" +description = "Data validation using Python type hints" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pydantic-2.5.2-py3-none-any.whl", hash = "sha256:80c50fb8e3dcecfddae1adbcc00ec5822918490c99ab31f6cf6140ca1c1429f0"}, + {file = "pydantic-2.5.2.tar.gz", hash = "sha256:ff177ba64c6faf73d7afa2e8cad38fd456c0dbe01c9954e71038001cd15a6edd"}, +] + +[package.dependencies] +annotated-types = ">=0.4.0" +pydantic-core = "2.14.5" +typing-extensions = ">=4.6.1" + +[package.extras] +email = ["email-validator (>=2.0.0)"] + +[package.source] +type = "legacy" +url = "https://pypi.tuna.tsinghua.edu.cn/simple" +reference = "tsinghua" + +[[package]] +name = "pydantic-core" +version = "2.14.5" +description = "" +optional = false +python-versions = ">=3.7" +files = [ + {file = "pydantic_core-2.14.5-cp310-cp310-macosx_10_7_x86_64.whl", hash = "sha256:7e88f5696153dc516ba6e79f82cc4747e87027205f0e02390c21f7cb3bd8abfd"}, + {file = "pydantic_core-2.14.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4641e8ad4efb697f38a9b64ca0523b557c7931c5f84e0fd377a9a3b05121f0de"}, + {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:774de879d212db5ce02dfbf5b0da9a0ea386aeba12b0b95674a4ce0593df3d07"}, + {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ebb4e035e28f49b6f1a7032920bb9a0c064aedbbabe52c543343d39341a5b2a3"}, + {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b53e9ad053cd064f7e473a5f29b37fc4cc9dc6d35f341e6afc0155ea257fc911"}, + {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:8aa1768c151cf562a9992462239dfc356b3d1037cc5a3ac829bb7f3bda7cc1f9"}, + {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eac5c82fc632c599f4639a5886f96867ffced74458c7db61bc9a66ccb8ee3113"}, + {file = "pydantic_core-2.14.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d2ae91f50ccc5810b2f1b6b858257c9ad2e08da70bf890dee02de1775a387c66"}, + {file = "pydantic_core-2.14.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6b9ff467ffbab9110e80e8c8de3bcfce8e8b0fd5661ac44a09ae5901668ba997"}, + {file = "pydantic_core-2.14.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:61ea96a78378e3bd5a0be99b0e5ed00057b71f66115f5404d0dae4819f495093"}, + {file = "pydantic_core-2.14.5-cp310-none-win32.whl", hash = "sha256:bb4c2eda937a5e74c38a41b33d8c77220380a388d689bcdb9b187cf6224c9720"}, + {file = "pydantic_core-2.14.5-cp310-none-win_amd64.whl", hash = "sha256:b7851992faf25eac90bfcb7bfd19e1f5ffa00afd57daec8a0042e63c74a4551b"}, + {file = "pydantic_core-2.14.5-cp311-cp311-macosx_10_7_x86_64.whl", hash = "sha256:4e40f2bd0d57dac3feb3a3aed50f17d83436c9e6b09b16af271b6230a2915459"}, + {file = "pydantic_core-2.14.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ab1cdb0f14dc161ebc268c09db04d2c9e6f70027f3b42446fa11c153521c0e88"}, + {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aae7ea3a1c5bb40c93cad361b3e869b180ac174656120c42b9fadebf685d121b"}, + {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:60b7607753ba62cf0739177913b858140f11b8af72f22860c28eabb2f0a61937"}, + {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2248485b0322c75aee7565d95ad0e16f1c67403a470d02f94da7344184be770f"}, + {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:823fcc638f67035137a5cd3f1584a4542d35a951c3cc68c6ead1df7dac825c26"}, + {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:96581cfefa9123accc465a5fd0cc833ac4d75d55cc30b633b402e00e7ced00a6"}, + {file = "pydantic_core-2.14.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a33324437018bf6ba1bb0f921788788641439e0ed654b233285b9c69704c27b4"}, + {file = "pydantic_core-2.14.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9bd18fee0923ca10f9a3ff67d4851c9d3e22b7bc63d1eddc12f439f436f2aada"}, + {file = "pydantic_core-2.14.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:853a2295c00f1d4429db4c0fb9475958543ee80cfd310814b5c0ef502de24dda"}, + {file = "pydantic_core-2.14.5-cp311-none-win32.whl", hash = "sha256:cb774298da62aea5c80a89bd58c40205ab4c2abf4834453b5de207d59d2e1651"}, + {file = "pydantic_core-2.14.5-cp311-none-win_amd64.whl", hash = "sha256:e87fc540c6cac7f29ede02e0f989d4233f88ad439c5cdee56f693cc9c1c78077"}, + {file = "pydantic_core-2.14.5-cp311-none-win_arm64.whl", hash = "sha256:57d52fa717ff445cb0a5ab5237db502e6be50809b43a596fb569630c665abddf"}, + {file = "pydantic_core-2.14.5-cp312-cp312-macosx_10_7_x86_64.whl", hash = "sha256:e60f112ac88db9261ad3a52032ea46388378034f3279c643499edb982536a093"}, + {file = "pydantic_core-2.14.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6e227c40c02fd873c2a73a98c1280c10315cbebe26734c196ef4514776120aeb"}, + {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0cbc7fff06a90bbd875cc201f94ef0ee3929dfbd5c55a06674b60857b8b85ed"}, + {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:103ef8d5b58596a731b690112819501ba1db7a36f4ee99f7892c40da02c3e189"}, + {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c949f04ecad823f81b1ba94e7d189d9dfb81edbb94ed3f8acfce41e682e48cef"}, + {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c1452a1acdf914d194159439eb21e56b89aa903f2e1c65c60b9d874f9b950e5d"}, + {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb4679d4c2b089e5ef89756bc73e1926745e995d76e11925e3e96a76d5fa51fc"}, + {file = "pydantic_core-2.14.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:cf9d3fe53b1ee360e2421be95e62ca9b3296bf3f2fb2d3b83ca49ad3f925835e"}, + {file = "pydantic_core-2.14.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:70f4b4851dbb500129681d04cc955be2a90b2248d69273a787dda120d5cf1f69"}, + {file = "pydantic_core-2.14.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:59986de5710ad9613ff61dd9b02bdd2f615f1a7052304b79cc8fa2eb4e336d2d"}, + {file = "pydantic_core-2.14.5-cp312-none-win32.whl", hash = "sha256:699156034181e2ce106c89ddb4b6504c30db8caa86e0c30de47b3e0654543260"}, + {file = "pydantic_core-2.14.5-cp312-none-win_amd64.whl", hash = "sha256:5baab5455c7a538ac7e8bf1feec4278a66436197592a9bed538160a2e7d11e36"}, + {file = "pydantic_core-2.14.5-cp312-none-win_arm64.whl", hash = "sha256:e47e9a08bcc04d20975b6434cc50bf82665fbc751bcce739d04a3120428f3e27"}, + {file = "pydantic_core-2.14.5-cp37-cp37m-macosx_10_7_x86_64.whl", hash = "sha256:af36f36538418f3806048f3b242a1777e2540ff9efaa667c27da63d2749dbce0"}, + {file = "pydantic_core-2.14.5-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:45e95333b8418ded64745f14574aa9bfc212cb4fbeed7a687b0c6e53b5e188cd"}, + {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e47a76848f92529879ecfc417ff88a2806438f57be4a6a8bf2961e8f9ca9ec7"}, + {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d81e6987b27bc7d101c8597e1cd2bcaa2fee5e8e0f356735c7ed34368c471550"}, + {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:34708cc82c330e303f4ce87758828ef6e457681b58ce0e921b6e97937dd1e2a3"}, + {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:652c1988019752138b974c28f43751528116bcceadad85f33a258869e641d753"}, + {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e4d090e73e0725b2904fdbdd8d73b8802ddd691ef9254577b708d413bf3006e"}, + {file = "pydantic_core-2.14.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5c7d5b5005f177764e96bd584d7bf28d6e26e96f2a541fdddb934c486e36fd59"}, + {file = "pydantic_core-2.14.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:a71891847f0a73b1b9eb86d089baee301477abef45f7eaf303495cd1473613e4"}, + {file = "pydantic_core-2.14.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:a717aef6971208f0851a2420b075338e33083111d92041157bbe0e2713b37325"}, + {file = "pydantic_core-2.14.5-cp37-none-win32.whl", hash = "sha256:de790a3b5aa2124b8b78ae5faa033937a72da8efe74b9231698b5a1dd9be3405"}, + {file = "pydantic_core-2.14.5-cp37-none-win_amd64.whl", hash = "sha256:6c327e9cd849b564b234da821236e6bcbe4f359a42ee05050dc79d8ed2a91588"}, + {file = "pydantic_core-2.14.5-cp38-cp38-macosx_10_7_x86_64.whl", hash = "sha256:ef98ca7d5995a82f43ec0ab39c4caf6a9b994cb0b53648ff61716370eadc43cf"}, + {file = "pydantic_core-2.14.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c6eae413494a1c3f89055da7a5515f32e05ebc1a234c27674a6956755fb2236f"}, + {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcf4e6d85614f7a4956c2de5a56531f44efb973d2fe4a444d7251df5d5c4dcfd"}, + {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6637560562134b0e17de333d18e69e312e0458ee4455bdad12c37100b7cad706"}, + {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:77fa384d8e118b3077cccfcaf91bf83c31fe4dc850b5e6ee3dc14dc3d61bdba1"}, + {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:16e29bad40bcf97aac682a58861249ca9dcc57c3f6be22f506501833ddb8939c"}, + {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:531f4b4252fac6ca476fbe0e6f60f16f5b65d3e6b583bc4d87645e4e5ddde331"}, + {file = "pydantic_core-2.14.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:074f3d86f081ce61414d2dc44901f4f83617329c6f3ab49d2bc6c96948b2c26b"}, + {file = "pydantic_core-2.14.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:c2adbe22ab4babbca99c75c5d07aaf74f43c3195384ec07ccbd2f9e3bddaecec"}, + {file = "pydantic_core-2.14.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0f6116a558fd06d1b7c2902d1c4cf64a5bd49d67c3540e61eccca93f41418124"}, + {file = "pydantic_core-2.14.5-cp38-none-win32.whl", hash = "sha256:fe0a5a1025eb797752136ac8b4fa21aa891e3d74fd340f864ff982d649691867"}, + {file = "pydantic_core-2.14.5-cp38-none-win_amd64.whl", hash = "sha256:079206491c435b60778cf2b0ee5fd645e61ffd6e70c47806c9ed51fc75af078d"}, + {file = "pydantic_core-2.14.5-cp39-cp39-macosx_10_7_x86_64.whl", hash = "sha256:a6a16f4a527aae4f49c875da3cdc9508ac7eef26e7977952608610104244e1b7"}, + {file = "pydantic_core-2.14.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:abf058be9517dc877227ec3223f0300034bd0e9f53aebd63cf4456c8cb1e0863"}, + {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:49b08aae5013640a3bfa25a8eebbd95638ec3f4b2eaf6ed82cf0c7047133f03b"}, + {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c2d97e906b4ff36eb464d52a3bc7d720bd6261f64bc4bcdbcd2c557c02081ed2"}, + {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3128e0bbc8c091ec4375a1828d6118bc20404883169ac95ffa8d983b293611e6"}, + {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88e74ab0cdd84ad0614e2750f903bb0d610cc8af2cc17f72c28163acfcf372a4"}, + {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c339dabd8ee15f8259ee0f202679b6324926e5bc9e9a40bf981ce77c038553db"}, + {file = "pydantic_core-2.14.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3387277f1bf659caf1724e1afe8ee7dbc9952a82d90f858ebb931880216ea955"}, + {file = "pydantic_core-2.14.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ba6b6b3846cfc10fdb4c971980a954e49d447cd215ed5a77ec8190bc93dd7bc5"}, + {file = "pydantic_core-2.14.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:ca61d858e4107ce5e1330a74724fe757fc7135190eb5ce5c9d0191729f033209"}, + {file = "pydantic_core-2.14.5-cp39-none-win32.whl", hash = "sha256:ec1e72d6412f7126eb7b2e3bfca42b15e6e389e1bc88ea0069d0cc1742f477c6"}, + {file = "pydantic_core-2.14.5-cp39-none-win_amd64.whl", hash = "sha256:c0b97ec434041827935044bbbe52b03d6018c2897349670ff8fe11ed24d1d4ab"}, + {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-macosx_10_7_x86_64.whl", hash = "sha256:79e0a2cdbdc7af3f4aee3210b1172ab53d7ddb6a2d8c24119b5706e622b346d0"}, + {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:678265f7b14e138d9a541ddabbe033012a2953315739f8cfa6d754cc8063e8ca"}, + {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:95b15e855ae44f0c6341ceb74df61b606e11f1087e87dcb7482377374aac6abe"}, + {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:09b0e985fbaf13e6b06a56d21694d12ebca6ce5414b9211edf6f17738d82b0f8"}, + {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:3ad873900297bb36e4b6b3f7029d88ff9829ecdc15d5cf20161775ce12306f8a"}, + {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:2d0ae0d8670164e10accbeb31d5ad45adb71292032d0fdb9079912907f0085f4"}, + {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:d37f8ec982ead9ba0a22a996129594938138a1503237b87318392a48882d50b7"}, + {file = "pydantic_core-2.14.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:35613015f0ba7e14c29ac6c2483a657ec740e5ac5758d993fdd5870b07a61d8b"}, + {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-macosx_10_7_x86_64.whl", hash = "sha256:ab4ea451082e684198636565224bbb179575efc1658c48281b2c866bfd4ddf04"}, + {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ce601907e99ea5b4adb807ded3570ea62186b17f88e271569144e8cca4409c7"}, + {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fb2ed8b3fe4bf4506d6dab3b93b83bbc22237e230cba03866d561c3577517d18"}, + {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:70f947628e074bb2526ba1b151cee10e4c3b9670af4dbb4d73bc8a89445916b5"}, + {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:4bc536201426451f06f044dfbf341c09f540b4ebdb9fd8d2c6164d733de5e634"}, + {file = "pydantic_core-2.14.5-pp37-pypy37_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f4791cf0f8c3104ac668797d8c514afb3431bc3305f5638add0ba1a5a37e0d88"}, + {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-macosx_10_7_x86_64.whl", hash = "sha256:038c9f763e650712b899f983076ce783175397c848da04985658e7628cbe873b"}, + {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:27548e16c79702f1e03f5628589c6057c9ae17c95b4c449de3c66b589ead0520"}, + {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c97bee68898f3f4344eb02fec316db93d9700fb1e6a5b760ffa20d71d9a46ce3"}, + {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b9b759b77f5337b4ea024f03abc6464c9f35d9718de01cfe6bae9f2e139c397e"}, + {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:439c9afe34638ace43a49bf72d201e0ffc1a800295bed8420c2a9ca8d5e3dbb3"}, + {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:ba39688799094c75ea8a16a6b544eb57b5b0f3328697084f3f2790892510d144"}, + {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:ccd4d5702bb90b84df13bd491be8d900b92016c5a455b7e14630ad7449eb03f8"}, + {file = "pydantic_core-2.14.5-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:81982d78a45d1e5396819bbb4ece1fadfe5f079335dd28c4ab3427cd95389944"}, + {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-macosx_10_7_x86_64.whl", hash = "sha256:7f8210297b04e53bc3da35db08b7302a6a1f4889c79173af69b72ec9754796b8"}, + {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:8c8a8812fe6f43a3a5b054af6ac2d7b8605c7bcab2804a8a7d68b53f3cd86e00"}, + {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:206ed23aecd67c71daf5c02c3cd19c0501b01ef3cbf7782db9e4e051426b3d0d"}, + {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2027d05c8aebe61d898d4cffd774840a9cb82ed356ba47a90d99ad768f39789"}, + {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:40180930807ce806aa71eda5a5a5447abb6b6a3c0b4b3b1b1962651906484d68"}, + {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:615a0a4bff11c45eb3c1996ceed5bdaa2f7b432425253a7c2eed33bb86d80abc"}, + {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f5e412d717366e0677ef767eac93566582518fe8be923361a5c204c1a62eaafe"}, + {file = "pydantic_core-2.14.5-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:513b07e99c0a267b1d954243845d8a833758a6726a3b5d8948306e3fe14675e3"}, + {file = "pydantic_core-2.14.5.tar.gz", hash = "sha256:6d30226dfc816dd0fdf120cae611dd2215117e4f9b124af8c60ab9093b6e8e71"}, +] + +[package.dependencies] +typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" + +[package.source] +type = "legacy" +url = "https://pypi.tuna.tsinghua.edu.cn/simple" +reference = "tsinghua" + [[package]] name = "pyexcel" version = "0.7.0" @@ -5398,9 +5700,11 @@ files = [ {file = "pymssql-2.2.8-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:049f2e3de919e8e02504780a21ebbf235e21ca8ed5c7538c5b6e705aa6c43d8c"}, {file = "pymssql-2.2.8-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0dd86d8e3e346e34f3f03d12e333747b53a1daa74374a727f4714d5b82ee0dd5"}, {file = "pymssql-2.2.8-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:508226a0df7cb6faeda9f8e84e85743690ca427d7b27af9a73d75fcf0c1eef6e"}, + {file = "pymssql-2.2.8-cp310-cp310-win_amd64.whl", hash = "sha256:47859887adeaf184766b5e0bc845dd23611f3808f9521552063bb36eabc10092"}, {file = "pymssql-2.2.8-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d873e553374d5b1c57fe1c43bb75e3bcc2920678db1ef26f6bfed396c7d21b30"}, {file = "pymssql-2.2.8-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bf31b8b76634c826a91f9999e15b7bfb0c051a0f53b319fd56481a67e5b903bb"}, {file = "pymssql-2.2.8-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:821945c2214fe666fd456c61e09a29a00e7719c9e136c801bffb3a254e9c579b"}, + {file = "pymssql-2.2.8-cp311-cp311-win_amd64.whl", hash = "sha256:cc85b609b4e60eac25fa38bbac1ff854fd2c2a276e0ca4a3614c6f97efb644bb"}, {file = "pymssql-2.2.8-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:ebe7f64d5278d807f14bea08951e02512bfbc6219fd4d4f15bb45ded885cf3d4"}, {file = "pymssql-2.2.8-cp36-cp36m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:253af3d39fc0235627966817262d5c4c94ad09dcbea59664748063470048c29c"}, {file = "pymssql-2.2.8-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c9d109df536dc5f7dd851a88d285a4c9cb12a9314b621625f4f5ab1197eb312"}, @@ -5416,11 +5720,13 @@ files = [ {file = "pymssql-2.2.8-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3906993300650844ec140aa58772c0f5f3e9e9d5709c061334fd1551acdcf066"}, {file = "pymssql-2.2.8-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:7309c7352e4a87c9995c3183ebfe0ff4135e955bb759109637673c61c9f0ca8d"}, {file = "pymssql-2.2.8-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:9b8d603cc1ec7ae585c5a409a1d45e8da067970c79dd550d45c238ae0aa0f79f"}, + {file = "pymssql-2.2.8-cp38-cp38-win_amd64.whl", hash = "sha256:293cb4d0339e221d877d6b19a1905082b658f0100a1e2ccc9dda10de58938901"}, {file = "pymssql-2.2.8-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:895041edd002a2e91d8a4faf0906b6fbfef29d9164bc6beb398421f5927fa40e"}, {file = "pymssql-2.2.8-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6b2d9c6d38a416c6f2db36ff1cd8e69f9a5387a46f9f4f612623192e0c9404b1"}, {file = "pymssql-2.2.8-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d63d6f25cf40fe6a03c49be2d4d337858362b8ab944d6684c268e4990807cf0c"}, {file = "pymssql-2.2.8-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:c83ad3ad20951f3a94894b354fa5fa9666dcd5ebb4a635dad507c7d1dd545833"}, {file = "pymssql-2.2.8-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:3933f7f082be74698eea835df51798dab9bc727d94d3d280bffc75ab9265f890"}, + {file = "pymssql-2.2.8-cp39-cp39-win_amd64.whl", hash = "sha256:de313375b90b0f554058992f35c4a4beb3f6ec2f5912d8cd6afb649f95b03a9f"}, {file = "pymssql-2.2.8.tar.gz", hash = "sha256:9baefbfbd07d0142756e2dfcaa804154361ac5806ab9381350aad4e780c3033e"}, ] @@ -5909,6 +6215,7 @@ files = [ {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, @@ -5916,8 +6223,15 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, + {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, + {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, + {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, @@ -5934,6 +6248,7 @@ files = [ {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, @@ -5941,6 +6256,7 @@ files = [ {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, @@ -6367,6 +6683,22 @@ type = "legacy" url = "https://pypi.tuna.tsinghua.edu.cn/simple" reference = "tsinghua" +[[package]] +name = "sniffio" +version = "1.3.0" +description = "Sniff out which async library your code is running under" +optional = false +python-versions = ">=3.7" +files = [ + {file = "sniffio-1.3.0-py3-none-any.whl", hash = "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"}, + {file = "sniffio-1.3.0.tar.gz", hash = "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101"}, +] + +[package.source] +type = "legacy" +url = "https://pypi.tuna.tsinghua.edu.cn/simple" +reference = "tsinghua" + [[package]] name = "soupsieve" version = "2.5" @@ -6617,6 +6949,31 @@ type = "legacy" url = "https://pypi.tuna.tsinghua.edu.cn/simple" reference = "tsinghua" +[[package]] +name = "tqdm" +version = "4.66.1" +description = "Fast, Extensible Progress Meter" +optional = false +python-versions = ">=3.7" +files = [ + {file = "tqdm-4.66.1-py3-none-any.whl", hash = "sha256:d302b3c5b53d47bce91fea46679d9c3c6508cf6332229aa1e7d8653723793386"}, + {file = "tqdm-4.66.1.tar.gz", hash = "sha256:d88e651f9db8d8551a62556d3cff9e3034274ca5d66e93197cf2490e2dcb69c7"}, +] + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[package.extras] +dev = ["pytest (>=6)", "pytest-cov", "pytest-timeout", "pytest-xdist"] +notebook = ["ipywidgets (>=6)"] +slack = ["slack-sdk"] +telegram = ["requests"] + +[package.source] +type = "legacy" +url = "https://pypi.tuna.tsinghua.edu.cn/simple" +reference = "tsinghua" + [[package]] name = "traitlets" version = "5.14.0" @@ -7379,4 +7736,4 @@ reference = "tsinghua" [metadata] lock-version = "2.0" python-versions = "^3.11" -content-hash = "15901aec4160752d2162dd1af1c5afb2dd848784d78ce8f84693e4ceddf01875" +content-hash = "397cb294c81da3ce74f1a1c3044bd778669284c0c280177162ed9d51e623a7c8" diff --git a/pyproject.toml b/pyproject.toml index c62fdbe7f..c26fc8c68 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -143,6 +143,7 @@ fido2 = "^1.1.2" ua-parser = "^0.18.0" user-agents = "^2.2.0" mistune = "0.8.4" +openai = "^1.3.7" [tool.poetry.group.xpack.dependencies] From 8291a81efd530691a0655222dc084f50bd3f16f0 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Tue, 5 Dec 2023 11:16:34 +0800 Subject: [PATCH 026/111] =?UTF-8?q?perf:=20=E6=94=AF=E6=8C=81=E5=85=A8?= =?UTF-8?q?=E5=B1=80=E7=9A=84=20labels=20(#12043)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * perf: 支持全局的 labels * perf: stash * stash * stash * stash * stash * perf: 优化 labels * stash * perf: add debug sql * perf: 修改 labels * perf: 优化提交 * perf: 优化提交 labels * perf: 基本完成 * perf: 完成 labels 搜索 * perf: 优化 labels * perf: 去掉不用 debug --------- Co-authored-by: ibuler --- apps/accounts/models/account.py | 5 +- apps/accounts/models/template.py | 3 +- apps/accounts/serializers/account/account.py | 5 +- apps/accounts/serializers/account/base.py | 6 +- apps/assets/api/__init__.py | 1 - apps/assets/api/asset/asset.py | 18 +- apps/assets/api/label.py | 43 - apps/assets/filters.py | 52 -- .../migrations/0126_remove_asset_labels.py | 18 + apps/assets/models/asset/common.py | 12 +- apps/assets/models/domain.py | 3 +- apps/assets/models/platform.py | 3 +- apps/assets/serializers/__init__.py | 9 +- apps/assets/serializers/asset/common.py | 11 +- apps/assets/serializers/domain.py | 9 +- apps/assets/serializers/label.py | 47 - apps/assets/serializers/platform.py | 10 +- apps/assets/urls/api_urls.py | 1 - apps/audits/utils.py | 15 +- apps/common/api/mixin.py | 4 +- apps/common/drf/filters.py | 44 +- apps/common/serializers/fields.py | 30 +- apps/common/serializers/mixin.py | 28 +- apps/common/signal_handlers.py | 4 + apps/jumpserver/conf.py | 5 +- apps/jumpserver/rewriting/pagination.py | 7 + apps/jumpserver/settings/base.py | 4 + apps/jumpserver/settings/custom.py | 1 + apps/jumpserver/settings/libs.py | 1 + apps/jumpserver/urls.py | 1 + apps/labels/__init__.py | 0 apps/labels/admin.py | 3 + apps/labels/api.py | 141 +++ apps/labels/apps.py | 6 + apps/labels/const.py | 0 apps/labels/migrations/0001_initial.py | 54 ++ .../migrations/0002_auto_20231103_1659.py | 52 ++ ..._alter_labeledresource_options_and_more.py | 28 + apps/labels/migrations/__init__.py | 0 apps/labels/mixins.py | 13 + apps/labels/models.py | 38 + apps/labels/serializers.py | 54 ++ apps/labels/tests.py | 3 + apps/labels/urls.py | 21 + apps/labels/views.py | 3 + apps/locale/ja/LC_MESSAGES/django.mo | 4 +- apps/locale/ja/LC_MESSAGES/django.po | 803 ++++++++++-------- apps/locale/zh/LC_MESSAGES/django.mo | 4 +- apps/locale/zh/LC_MESSAGES/django.po | 792 +++++++++-------- apps/ops/models/job.py | 3 +- apps/ops/models/playbook.py | 3 +- apps/ops/serializers/job.py | 15 +- apps/ops/serializers/playbook.py | 7 +- apps/perms/models/asset_permission.py | 3 +- apps/perms/serializers/permission.py | 7 +- apps/rbac/api/__init__.py | 2 +- apps/rbac/api/content_type.py | 11 + apps/rbac/const.py | 2 +- apps/rbac/models/permission.py | 51 ++ apps/rbac/serializers/__init__.py | 2 +- apps/rbac/serializers/content_type.py | 13 + apps/rbac/tree.py | 2 + apps/rbac/urls/api_urls.py | 2 +- apps/users/api/user.py | 4 - apps/users/models/group.py | 4 +- apps/users/models/user.py | 3 +- apps/users/serializers/group.py | 10 +- apps/users/serializers/profile.py | 2 +- apps/users/serializers/user.py | 11 +- poetry.lock | 19 + pyproject.toml | 1 + 71 files changed, 1618 insertions(+), 978 deletions(-) delete mode 100644 apps/assets/api/label.py create mode 100644 apps/assets/migrations/0126_remove_asset_labels.py delete mode 100644 apps/assets/serializers/label.py create mode 100644 apps/labels/__init__.py create mode 100644 apps/labels/admin.py create mode 100644 apps/labels/api.py create mode 100644 apps/labels/apps.py create mode 100644 apps/labels/const.py create mode 100644 apps/labels/migrations/0001_initial.py create mode 100644 apps/labels/migrations/0002_auto_20231103_1659.py create mode 100644 apps/labels/migrations/0003_alter_labeledresource_options_and_more.py create mode 100644 apps/labels/migrations/__init__.py create mode 100644 apps/labels/mixins.py create mode 100644 apps/labels/models.py create mode 100644 apps/labels/serializers.py create mode 100644 apps/labels/tests.py create mode 100644 apps/labels/urls.py create mode 100644 apps/labels/views.py create mode 100644 apps/rbac/api/content_type.py create mode 100644 apps/rbac/serializers/content_type.py diff --git a/apps/accounts/models/account.py b/apps/accounts/models/account.py index 8a9187190..c3c052b1b 100644 --- a/apps/accounts/models/account.py +++ b/apps/accounts/models/account.py @@ -4,6 +4,7 @@ from simple_history.models import HistoricalRecords from assets.models.base import AbsConnectivity from common.utils import lazyproperty +from labels.mixins import LabeledMixin from .base import BaseAccount from .mixins import VaultModelMixin from ..const import Source @@ -42,7 +43,7 @@ class AccountHistoricalRecords(HistoricalRecords): return super().create_history_model(model, inherited) -class Account(AbsConnectivity, BaseAccount): +class Account(AbsConnectivity, LabeledMixin, BaseAccount): asset = models.ForeignKey( 'assets.Asset', related_name='accounts', on_delete=models.CASCADE, verbose_name=_('Asset') @@ -71,7 +72,7 @@ class Account(AbsConnectivity, BaseAccount): ] def __str__(self): - return '{}'.format(self.username) + return '{}({})'.format(self.name, self.asset.name) @lazyproperty def platform(self): diff --git a/apps/accounts/models/template.py b/apps/accounts/models/template.py index c56be1464..63ed1b20d 100644 --- a/apps/accounts/models/template.py +++ b/apps/accounts/models/template.py @@ -3,13 +3,14 @@ from django.db.models import Count, Q from django.utils import timezone from django.utils.translation import gettext_lazy as _ +from labels.mixins import LabeledMixin from .account import Account from .base import BaseAccount, SecretWithRandomMixin __all__ = ['AccountTemplate', ] -class AccountTemplate(BaseAccount, SecretWithRandomMixin): +class AccountTemplate(LabeledMixin, BaseAccount, SecretWithRandomMixin): su_from = models.ForeignKey( 'self', related_name='su_to', null=True, on_delete=models.SET_NULL, verbose_name=_("Su from") diff --git a/apps/accounts/serializers/account/account.py b/apps/accounts/serializers/account/account.py index 8b8d9be36..c1098a6d1 100644 --- a/apps/accounts/serializers/account/account.py +++ b/apps/accounts/serializers/account/account.py @@ -66,6 +66,9 @@ class AccountCreateUpdateSerializerMixin(serializers.Serializer): name = initial_data.get('name') if name is not None: return + request = self.context.get('request') + if request and request.method == 'PATCH': + return if not name: name = initial_data.get('username') if self.instance and self.instance.name == name: @@ -238,7 +241,7 @@ class AccountSerializer(AccountCreateUpdateSerializerMixin, BaseAccountSerialize queryset = queryset.prefetch_related( 'asset', 'asset__platform', 'asset__platform__automation' - ) + ).prefetch_related('labels', 'labels__label') return queryset diff --git a/apps/accounts/serializers/account/base.py b/apps/accounts/serializers/account/base.py index 5289ea25b..23dec0d3e 100644 --- a/apps/accounts/serializers/account/base.py +++ b/apps/accounts/serializers/account/base.py @@ -5,6 +5,7 @@ from rest_framework import serializers from accounts.const import SecretType from accounts.models import BaseAccount from accounts.utils import validate_password_for_ansible, validate_ssh_key +from common.serializers import ResourceLabelsMixin from common.serializers.fields import EncryptedField, LabeledChoiceField from orgs.mixins.serializers import BulkOrgResourceModelSerializer @@ -60,8 +61,7 @@ class AuthValidateMixin(serializers.Serializer): return super().update(instance, validated_data) -class BaseAccountSerializer(AuthValidateMixin, BulkOrgResourceModelSerializer): - +class BaseAccountSerializer(AuthValidateMixin, ResourceLabelsMixin, BulkOrgResourceModelSerializer): class Meta: model = BaseAccount fields_mini = ['id', 'name', 'username'] @@ -70,7 +70,7 @@ class BaseAccountSerializer(AuthValidateMixin, BulkOrgResourceModelSerializer): 'privileged', 'is_active', 'spec_info', ] fields_other = ['created_by', 'date_created', 'date_updated', 'comment'] - fields = fields_small + fields_other + fields = fields_small + fields_other + ['labels'] read_only_fields = [ 'spec_info', 'date_verified', 'created_by', 'date_created', ] diff --git a/apps/assets/api/__init__.py b/apps/assets/api/__init__.py index 8ae0cd9bd..9eccd4977 100644 --- a/apps/assets/api/__init__.py +++ b/apps/assets/api/__init__.py @@ -2,7 +2,6 @@ from .asset import * from .category import * from .domain import * from .favorite_asset import * -from .label import * from .mixin import * from .node import * from .platform import * diff --git a/apps/assets/api/asset/asset.py b/apps/assets/api/asset/asset.py index 4b36b4bbe..729aa41bf 100644 --- a/apps/assets/api/asset/asset.py +++ b/apps/assets/api/asset/asset.py @@ -3,7 +3,6 @@ from collections import defaultdict import django_filters -from django.db.models import Q from django.shortcuts import get_object_or_404 from django.utils.translation import gettext as _ from rest_framework import status @@ -14,7 +13,7 @@ from rest_framework.status import HTTP_200_OK from accounts.tasks import push_accounts_to_assets_task, verify_accounts_connectivity_task from assets import serializers from assets.exceptions import NotSupportedTemporarilyError -from assets.filters import IpInFilterBackend, LabelFilterBackend, NodeFilterBackend +from assets.filters import IpInFilterBackend, NodeFilterBackend from assets.models import Asset, Gateway, Platform, Protocol from assets.tasks import test_assets_connectivity_manual, update_assets_hardware_info_manual from common.api import SuggestionMixin @@ -33,7 +32,6 @@ __all__ = [ class AssetFilterSet(BaseFilterSet): - labels = django_filters.CharFilter(method='filter_labels') platform = django_filters.CharFilter(method='filter_platform') domain = django_filters.CharFilter(method='filter_domain') type = django_filters.CharFilter(field_name="platform__type", lookup_expr="exact") @@ -64,7 +62,7 @@ class AssetFilterSet(BaseFilterSet): class Meta: model = Asset fields = [ - "id", "name", "address", "is_active", "labels", + "id", "name", "address", "is_active", "type", "category", "platform", ] @@ -87,16 +85,6 @@ class AssetFilterSet(BaseFilterSet): value = value.split(',') return queryset.filter(protocols__name__in=value).distinct() - @staticmethod - def filter_labels(queryset, name, value): - if ':' in value: - n, v = value.split(':', 1) - queryset = queryset.filter(labels__name=n, labels__value=v) - else: - q = Q(labels__name__contains=value) | Q(labels__value__contains=value) - queryset = queryset.filter(q).distinct() - return queryset - class AssetViewSet(SuggestionMixin, NodeFilterMixin, OrgBulkModelViewSet): """ @@ -121,7 +109,7 @@ class AssetViewSet(SuggestionMixin, NodeFilterMixin, OrgBulkModelViewSet): ("sync_platform_protocols", "assets.change_asset"), ) extra_filter_backends = [ - LabelFilterBackend, IpInFilterBackend, + IpInFilterBackend, NodeFilterBackend, AttrRulesFilterBackend ] diff --git a/apps/assets/api/label.py b/apps/assets/api/label.py deleted file mode 100644 index d970d2180..000000000 --- a/apps/assets/api/label.py +++ /dev/null @@ -1,43 +0,0 @@ -# ~*~ coding: utf-8 ~*~ -# Copyright (C) 2014-2018 Beijing DuiZhan Technology Co.,Ltd. All Rights Reserved. -# -# Licensed under the GNU General Public License v2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.gnu.org/licenses/gpl-2.0.html -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from django.db.models import Count - -from common.utils import get_logger -from orgs.mixins.api import OrgBulkModelViewSet -from ..models import Label -from .. import serializers - - -logger = get_logger(__file__) -__all__ = ['LabelViewSet'] - - -class LabelViewSet(OrgBulkModelViewSet): - model = Label - filterset_fields = ("name", "value") - search_fields = filterset_fields - serializer_class = serializers.LabelSerializer - - def list(self, request, *args, **kwargs): - if request.query_params.get("distinct"): - self.serializer_class = serializers.LabelDistinctSerializer - self.queryset = self.queryset.values("name").distinct() - return super().list(request, *args, **kwargs) - - def get_queryset(self): - self.queryset = Label.objects.prefetch_related( - 'assets').annotate(asset_count=Count("assets")) - return self.queryset diff --git a/apps/assets/filters.py b/apps/assets/filters.py index 17e8414bf..f1fe6d666 100644 --- a/apps/assets/filters.py +++ b/apps/assets/filters.py @@ -5,7 +5,6 @@ from rest_framework import filters from rest_framework.compat import coreapi, coreschema from assets.utils import get_node_from_request, is_query_node_all_assets -from .models import Label class AssetByNodeFilterBackend(filters.BaseFilterBackend): @@ -72,57 +71,6 @@ class NodeFilterBackend(filters.BaseFilterBackend): return queryset.filter(nodes__key=node.key).distinct() -class LabelFilterBackend(filters.BaseFilterBackend): - sep = ':' - query_arg = 'label' - - def get_schema_fields(self, view): - example = self.sep.join(['os', 'linux']) - return [ - coreapi.Field( - name=self.query_arg, location='query', required=False, - type='string', example=example, description='' - ) - ] - - def get_query_labels(self, request): - labels_query = request.query_params.getlist(self.query_arg) - if not labels_query: - return None - - q = None - for kv in labels_query: - if '#' in kv: - self.sep = '#' - break - - for kv in labels_query: - if self.sep not in kv: - continue - key, value = kv.strip().split(self.sep)[:2] - if not all([key, value]): - continue - if q: - q |= Q(name=key, value=value) - else: - q = Q(name=key, value=value) - if not q: - return [] - labels = Label.objects.filter(q, is_active=True) \ - .values_list('id', flat=True) - return labels - - def filter_queryset(self, request, queryset, view): - labels = self.get_query_labels(request) - if labels is None: - return queryset - if len(labels) == 0: - return queryset.none() - for label in labels: - queryset = queryset.filter(labels=label) - return queryset - - class IpInFilterBackend(filters.BaseFilterBackend): def filter_queryset(self, request, queryset, view): ips = request.query_params.get('ips') diff --git a/apps/assets/migrations/0126_remove_asset_labels.py b/apps/assets/migrations/0126_remove_asset_labels.py new file mode 100644 index 000000000..44590dc4c --- /dev/null +++ b/apps/assets/migrations/0126_remove_asset_labels.py @@ -0,0 +1,18 @@ +# Generated by Django 4.1.10 on 2023-11-22 07:33 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('assets', '0125_auto_20231011_1053'), + ('labels', '0002_auto_20231103_1659'), + ] + + operations = [ + migrations.RemoveField( + model_name='asset', + name='labels', + ), + ] diff --git a/apps/assets/models/asset/common.py b/apps/assets/models/asset/common.py index a22e552e9..93294fd5b 100644 --- a/apps/assets/models/asset/common.py +++ b/apps/assets/models/asset/common.py @@ -13,7 +13,9 @@ from django.utils.translation import gettext_lazy as _ from assets import const from common.db.fields import EncryptMixin from common.utils import lazyproperty +from labels.mixins import LabeledMixin from orgs.mixins.models import OrgManager, JMSOrgBaseModel +from rbac.models import ContentType from ..base import AbsConnectivity from ..platform import Platform @@ -150,7 +152,7 @@ class JSONFilterMixin: return None -class Asset(NodesRelationMixin, AbsConnectivity, JSONFilterMixin, JMSOrgBaseModel): +class Asset(NodesRelationMixin, LabeledMixin, AbsConnectivity, JSONFilterMixin, JMSOrgBaseModel): Category = const.Category Type = const.AllTypes @@ -162,7 +164,6 @@ class Asset(NodesRelationMixin, AbsConnectivity, JSONFilterMixin, JMSOrgBaseMode nodes = models.ManyToManyField('assets.Node', default=default_node, related_name='assets', verbose_name=_("Nodes")) is_active = models.BooleanField(default=True, verbose_name=_('Is active')) - labels = models.ManyToManyField('assets.Label', blank=True, related_name='assets', verbose_name=_("Labels")) gathered_info = models.JSONField(verbose_name=_('Gathered info'), default=dict, blank=True) # 资产的一些信息,如 硬件信息 custom_info = models.JSONField(verbose_name=_('Custom info'), default=dict) @@ -171,6 +172,13 @@ class Asset(NodesRelationMixin, AbsConnectivity, JSONFilterMixin, JMSOrgBaseMode def __str__(self): return '{0.name}({0.address})'.format(self) + def get_labels(self): + from labels.models import Label, LabeledResource + res_type = ContentType.objects.get_for_model(self.__class__) + label_ids = LabeledResource.objects.filter(res_type=res_type, res_id=self.id) \ + .values_list('label_id', flat=True) + return Label.objects.filter(id__in=label_ids) + @staticmethod def get_spec_values(instance, fields): info = {} diff --git a/apps/assets/models/domain.py b/apps/assets/models/domain.py index 587f41d7c..e424a2d46 100644 --- a/apps/assets/models/domain.py +++ b/apps/assets/models/domain.py @@ -6,6 +6,7 @@ from django.db import models from django.utils.translation import gettext_lazy as _ from common.utils import get_logger +from labels.mixins import LabeledMixin from orgs.mixins.models import JMSOrgBaseModel from .gateway import Gateway @@ -14,7 +15,7 @@ logger = get_logger(__file__) __all__ = ['Domain'] -class Domain(JMSOrgBaseModel): +class Domain(LabeledMixin, JMSOrgBaseModel): name = models.CharField(max_length=128, verbose_name=_('Name')) class Meta: diff --git a/apps/assets/models/platform.py b/apps/assets/models/platform.py index 8fed01acf..74fcb9cfb 100644 --- a/apps/assets/models/platform.py +++ b/apps/assets/models/platform.py @@ -9,6 +9,7 @@ from common.db.models import JMSBaseModel __all__ = ['Platform', 'PlatformProtocol', 'PlatformAutomation'] from common.utils import lazyproperty +from labels.mixins import LabeledMixin class PlatformProtocol(models.Model): @@ -74,7 +75,7 @@ class PlatformAutomation(models.Model): platform = models.OneToOneField('Platform', on_delete=models.CASCADE, related_name='automation', null=True) -class Platform(JMSBaseModel): +class Platform(LabeledMixin, JMSBaseModel): """ 对资产提供 约束和默认值 对资产进行抽象 diff --git a/apps/assets/serializers/__init__.py b/apps/assets/serializers/__init__.py index cbf21454d..e071e24c0 100644 --- a/apps/assets/serializers/__init__.py +++ b/apps/assets/serializers/__init__.py @@ -2,11 +2,10 @@ # from .asset import * -from .label import * -from .node import * -from .gateway import * +from .automations import * +from .cagegory import * from .domain import * from .favorite_asset import * +from .gateway import * +from .node import * from .platform import * -from .cagegory import * -from .automations import * diff --git a/apps/assets/serializers/asset/common.py b/apps/assets/serializers/asset/common.py index 75d8c4c19..6c45944de 100644 --- a/apps/assets/serializers/asset/common.py +++ b/apps/assets/serializers/asset/common.py @@ -11,13 +11,14 @@ from accounts.serializers import AccountSerializer from common.const import UUID_PATTERN from common.serializers import ( WritableNestedModelSerializer, SecretReadableMixin, - CommonModelSerializer, MethodSerializer + CommonModelSerializer, MethodSerializer, ResourceLabelsMixin ) from common.serializers.common import DictSerializer from common.serializers.fields import LabeledChoiceField +from labels.models import Label from orgs.mixins.serializers import BulkOrgResourceModelSerializer from ...const import Category, AllTypes -from ...models import Asset, Node, Platform, Label, Protocol +from ...models import Asset, Node, Platform, Protocol __all__ = [ 'AssetSerializer', 'AssetSimpleSerializer', 'MiniAssetSerializer', @@ -117,10 +118,9 @@ class AccountSecretSerializer(SecretReadableMixin, CommonModelSerializer): } -class AssetSerializer(BulkOrgResourceModelSerializer, WritableNestedModelSerializer): +class AssetSerializer(BulkOrgResourceModelSerializer, ResourceLabelsMixin, WritableNestedModelSerializer): category = LabeledChoiceField(choices=Category.choices, read_only=True, label=_('Category')) type = LabeledChoiceField(choices=AllTypes.choices(), read_only=True, label=_('Type')) - labels = AssetLabelSerializer(many=True, required=False, label=_('Label')) protocols = AssetProtocolsSerializer(many=True, required=False, label=_('Protocols'), default=()) accounts = AssetAccountSerializer(many=True, required=False, allow_null=True, write_only=True, label=_('Account')) nodes_display = serializers.ListField(read_only=False, required=False, label=_("Node path")) @@ -201,8 +201,9 @@ class AssetSerializer(BulkOrgResourceModelSerializer, WritableNestedModelSeriali @classmethod def setup_eager_loading(cls, queryset): """ Perform necessary eager loading of data. """ - queryset = queryset.prefetch_related('domain', 'nodes', 'labels', 'protocols') \ + queryset = queryset.prefetch_related('domain', 'nodes', 'protocols', ) \ .prefetch_related('platform', 'platform__automation') \ + .prefetch_related('labels', 'labels__label') \ .annotate(category=F("platform__category")) \ .annotate(type=F("platform__type")) return queryset diff --git a/apps/assets/serializers/domain.py b/apps/assets/serializers/domain.py index a22d1a9ad..d2b3e3550 100644 --- a/apps/assets/serializers/domain.py +++ b/apps/assets/serializers/domain.py @@ -3,6 +3,7 @@ from django.utils.translation import gettext_lazy as _ from rest_framework import serializers +from common.serializers import ResourceLabelsMixin from common.serializers.fields import ObjectRelatedField from orgs.mixins.serializers import BulkOrgResourceModelSerializer from .gateway import GatewayWithAccountSecretSerializer @@ -11,7 +12,7 @@ from ..models import Domain, Asset __all__ = ['DomainSerializer', 'DomainWithGatewaySerializer'] -class DomainSerializer(BulkOrgResourceModelSerializer): +class DomainSerializer(ResourceLabelsMixin, BulkOrgResourceModelSerializer): gateways = ObjectRelatedField( many=True, required=False, label=_('Gateway'), read_only=True, ) @@ -41,6 +42,12 @@ class DomainSerializer(BulkOrgResourceModelSerializer): instance = super().update(instance, validated_data) return instance + @classmethod + def setup_eager_loading(cls, queryset): + queryset = queryset \ + .prefetch_related('labels', 'labels__label') + return queryset + class DomainWithGatewaySerializer(serializers.ModelSerializer): gateways = GatewayWithAccountSecretSerializer(many=True, read_only=True) diff --git a/apps/assets/serializers/label.py b/apps/assets/serializers/label.py deleted file mode 100644 index 3d913aeea..000000000 --- a/apps/assets/serializers/label.py +++ /dev/null @@ -1,47 +0,0 @@ -# -*- coding: utf-8 -*- -# -from django.db.models import Count -from django.utils.translation import gettext_lazy as _ -from rest_framework import serializers - -from orgs.mixins.serializers import BulkOrgResourceModelSerializer -from ..models import Label - - -class LabelSerializer(BulkOrgResourceModelSerializer): - asset_count = serializers.ReadOnlyField(label=_("Assets amount")) - - class Meta: - model = Label - fields_mini = ['id', 'name'] - fields_small = fields_mini + [ - 'value', 'category', 'is_active', - 'date_created', 'comment', - ] - fields_m2m = ['asset_count', 'assets'] - fields = fields_small + fields_m2m - read_only_fields = ( - 'category', 'date_created', 'asset_count', - ) - extra_kwargs = { - 'assets': {'required': False, 'label': _('Asset')} - } - - @classmethod - def setup_eager_loading(cls, queryset): - queryset = queryset.prefetch_related('assets') \ - .annotate(asset_count=Count('assets')) - return queryset - - -class LabelDistinctSerializer(BulkOrgResourceModelSerializer): - value = serializers.SerializerMethodField() - - class Meta: - model = Label - fields = ("name", "value") - - @staticmethod - def get_value(obj): - labels = Label.objects.filter(name=obj["name"]) - return ', '.join([label.value for label in labels]) diff --git a/apps/assets/serializers/platform.py b/apps/assets/serializers/platform.py index 2a0634a43..f67df9906 100644 --- a/apps/assets/serializers/platform.py +++ b/apps/assets/serializers/platform.py @@ -5,7 +5,7 @@ from rest_framework.validators import UniqueValidator from common.serializers import ( WritableNestedModelSerializer, type_field_map, MethodSerializer, - DictSerializer, create_serializer_class + DictSerializer, create_serializer_class, ResourceLabelsMixin ) from common.serializers.fields import LabeledChoiceField from common.utils import lazyproperty @@ -123,7 +123,7 @@ class PlatformCustomField(serializers.Serializer): choices = serializers.ListField(default=list, label=_("Choices"), required=False) -class PlatformSerializer(WritableNestedModelSerializer): +class PlatformSerializer(ResourceLabelsMixin, WritableNestedModelSerializer): SU_METHOD_CHOICES = [ ("sudo", "sudo su -"), ("su", "su - "), @@ -160,6 +160,7 @@ class PlatformSerializer(WritableNestedModelSerializer): fields = fields_small + [ "protocols", "domain_enabled", "su_enabled", "su_method", "automation", "comment", "custom_fields", + "labels" ] + read_only_fields extra_kwargs = { "su_enabled": {"label": _('Su enabled')}, @@ -201,9 +202,8 @@ class PlatformSerializer(WritableNestedModelSerializer): @classmethod def setup_eager_loading(cls, queryset): - queryset = queryset.prefetch_related( - 'protocols', 'automation' - ) + queryset = queryset.prefetch_related('protocols', 'automation') \ + .prefetch_related('labels', 'labels__label') return queryset def validate_protocols(self, protocols): diff --git a/apps/assets/urls/api_urls.py b/apps/assets/urls/api_urls.py index 5644d7dc0..408550ece 100644 --- a/apps/assets/urls/api_urls.py +++ b/apps/assets/urls/api_urls.py @@ -17,7 +17,6 @@ router.register(r'clouds', api.CloudViewSet, 'cloud') router.register(r'gpts', api.GPTViewSet, 'gpt') router.register(r'customs', api.CustomViewSet, 'custom') router.register(r'platforms', api.AssetPlatformViewSet, 'platform') -router.register(r'labels', api.LabelViewSet, 'label') router.register(r'nodes', api.NodeViewSet, 'node') router.register(r'domains', api.DomainViewSet, 'domain') router.register(r'gateways', api.GatewayViewSet, 'gateway') diff --git a/apps/audits/utils.py b/apps/audits/utils.py index 44e858098..244df5d22 100644 --- a/apps/audits/utils.py +++ b/apps/audits/utils.py @@ -2,6 +2,7 @@ import copy from datetime import datetime from itertools import chain +from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation from django.db import models from common.db.fields import RelatedManager @@ -65,13 +66,19 @@ def _get_instance_field_value( continue data.setdefault(k, v) continue - data.setdefault(str(f.verbose_name), value) + elif isinstance(f, GenericRelation): + value = [str(v) for v in value.all()] + elif isinstance(f, GenericForeignKey): + continue + try: + data.setdefault(str(f.verbose_name), value) + except Exception as e: + print(f.__dict__) + raise e return data -def model_to_dict_for_operate_log( - instance, include_model_fields=True, include_related_fields=False -): +def model_to_dict_for_operate_log(instance, include_model_fields=True, include_related_fields=False): model_need_continue_fields = ['date_updated'] m2m_need_continue_fields = ['history_passwords'] diff --git a/apps/common/api/mixin.py b/apps/common/api/mixin.py index d93459104..c87d5f22a 100644 --- a/apps/common/api/mixin.py +++ b/apps/common/api/mixin.py @@ -11,7 +11,7 @@ from rest_framework.settings import api_settings from common.drf.filters import ( IDSpmFilterBackend, CustomFilterBackend, IDInFilterBackend, - IDNotFilterBackend, NotOrRelFilterBackend + IDNotFilterBackend, NotOrRelFilterBackend, LabelFilterBackend ) from common.utils import get_logger, lazyproperty from .action import RenderToJsonMixin @@ -111,7 +111,7 @@ class ExtraFilterFieldsMixin: """ default_added_filters = ( CustomFilterBackend, IDSpmFilterBackend, IDInFilterBackend, - IDNotFilterBackend, + IDNotFilterBackend, LabelFilterBackend ) filter_backends = api_settings.DEFAULT_FILTER_BACKENDS extra_filter_fields = [] diff --git a/apps/common/drf/filters.py b/apps/common/drf/filters.py index eb6878019..c58c289ab 100644 --- a/apps/common/drf/filters.py +++ b/apps/common/drf/filters.py @@ -21,7 +21,7 @@ __all__ = [ "DatetimeRangeFilterBackend", "IDSpmFilterBackend", 'IDInFilterBackend', "CustomFilterBackend", "BaseFilterSet", 'IDNotFilterBackend', - 'NotOrRelFilterBackend', + 'NotOrRelFilterBackend', 'LabelFilterBackend', ] @@ -168,6 +168,48 @@ class IDNotFilterBackend(filters.BaseFilterBackend): return queryset +class LabelFilterBackend(filters.BaseFilterBackend): + def get_schema_fields(self, view): + return [ + coreapi.Field( + name='label', location='query', required=False, + type='string', example='/api/v1/users/users?label=abc', + description='Filter by label' + ) + ] + + def filter_queryset(self, request, queryset, view): + label_id = request.query_params.get('label') + if not label_id: + return queryset + + if not hasattr(queryset, 'model'): + return queryset + + if not hasattr(queryset.model, 'labels'): + return queryset + + kwargs = {} + if ':' in label_id: + k, v = label_id.split(':', 1) + kwargs['label__name'] = k + if v != '*': + kwargs['label__value'] = v + else: + kwargs['label_id'] = label_id + + model = queryset.model + labeled_resource_cls = model.labels.field.related_model + app_label = model._meta.app_label + model_name = model._meta.model_name + + res_ids = labeled_resource_cls.objects.filter( + res_type__app_label=app_label, res_type__model=model_name, + ).filter(**kwargs).values_list('res_id', flat=True) + queryset = queryset.filter(id__in=set(res_ids)) + return queryset + + class CustomFilterBackend(filters.BaseFilterBackend): def get_schema_fields(self, view): diff --git a/apps/common/serializers/fields.py b/apps/common/serializers/fields.py index 5fe019c65..3054a5af4 100644 --- a/apps/common/serializers/fields.py +++ b/apps/common/serializers/fields.py @@ -20,7 +20,8 @@ __all__ = [ "TreeChoicesField", "LabeledMultipleChoiceField", "PhoneField", - "JSONManyToManyField" + "JSONManyToManyField", + "LabelRelatedField", ] @@ -99,6 +100,33 @@ class LabeledMultipleChoiceField(serializers.MultipleChoiceField): return data +class LabelRelatedField(serializers.RelatedField): + def __init__(self, **kwargs): + queryset = kwargs.pop("queryset", None) + if queryset is None: + from labels.models import LabeledResource + queryset = LabeledResource.objects.all() + + kwargs = {**kwargs} + read_only = kwargs.get("read_only", False) + if not read_only: + kwargs["queryset"] = queryset + super().__init__(**kwargs) + + def to_representation(self, value): + if value is None: + return value + return str(value.label) + + def to_internal_value(self, data): + from labels.models import LabeledResource, Label + if data is None: + return data + k, v = data.split(":", 1) + label, __ = Label.objects.get_or_create(name=k, value=v, defaults={'name': k, 'value': v}) + return LabeledResource(label=label) + + class ObjectRelatedField(serializers.RelatedField): default_error_messages = { "required": _("This field is required."), diff --git a/apps/common/serializers/mixin.py b/apps/common/serializers/mixin.py index d665d6346..1a749f6d3 100644 --- a/apps/common/serializers/mixin.py +++ b/apps/common/serializers/mixin.py @@ -7,6 +7,7 @@ else: from collections import Iterable from django.core.exceptions import ObjectDoesNotExist from django.db.models import NOT_PROVIDED +from django.utils.translation import gettext_lazy as _ from rest_framework import serializers from rest_framework.exceptions import ValidationError from rest_framework.fields import SkipField, empty @@ -14,14 +15,13 @@ from rest_framework.settings import api_settings from rest_framework.utils import html from common.db.fields import EncryptMixin -from common.serializers.fields import EncryptedField, LabeledChoiceField, ObjectRelatedField +from common.serializers.fields import EncryptedField, LabeledChoiceField, ObjectRelatedField, LabelRelatedField __all__ = [ 'BulkSerializerMixin', 'BulkListSerializerMixin', 'CommonSerializerMixin', 'CommonBulkSerializerMixin', 'SecretReadableMixin', 'CommonModelSerializer', - 'CommonBulkModelSerializer', - + 'CommonBulkModelSerializer', 'ResourceLabelsMixin', ] @@ -391,3 +391,25 @@ class CommonBulkSerializerMixin(BulkSerializerMixin, CommonSerializerMixin): class CommonBulkModelSerializer(CommonBulkSerializerMixin, serializers.ModelSerializer): pass + + +class ResourceLabelsMixin(serializers.Serializer): + labels = LabelRelatedField(many=True, label=_('Labels'), ) + + def update(self, instance, validated_data): + labels = validated_data.pop('labels', None) + res = super().update(instance, validated_data) + if labels is not None: + instance.labels.set(labels, bulk=False) + return res + + def create(self, validated_data): + labels = validated_data.pop('labels', None) + instance = super().create(validated_data) + if labels is not None: + instance.labels.set(labels, bulk=False) + return instance + + @classmethod + def setup_eager_loading(cls, queryset): + return queryset.prefetch_related('labels') diff --git a/apps/common/signal_handlers.py b/apps/common/signal_handlers.py index df2019b60..588a63632 100644 --- a/apps/common/signal_handlers.py +++ b/apps/common/signal_handlers.py @@ -66,6 +66,10 @@ def digest_sql_query(): for table_name, queries in table_queries.items(): if table_name.startswith('rbac_') or table_name.startswith('auth_permission'): continue + + for query in queries: + sql = query['sql'] + print(" # {}: {}".format(query['time'], sql)) if len(queries) < 3: continue print("- Table: {}".format(table_name)) diff --git a/apps/jumpserver/conf.py b/apps/jumpserver/conf.py index ec224d7c8..45d21aeab 100644 --- a/apps/jumpserver/conf.py +++ b/apps/jumpserver/conf.py @@ -586,8 +586,9 @@ class Config(dict): # FTP 文件上传下载备份阈值,单位(M),当值小于等于0时,不备份 'FTP_FILE_MAX_STORE': 100, - # API 请求次数限制 - 'MAX_LIMIT_PER_PAGE': 100, + # API 分页 + 'MAX_LIMIT_PER_PAGE': 10000, + 'DEFAULT_PAGE_SIZE': None, 'LIMIT_SUPER_PRIV': False, diff --git a/apps/jumpserver/rewriting/pagination.py b/apps/jumpserver/rewriting/pagination.py index cd38fd0ba..9a5fd263d 100644 --- a/apps/jumpserver/rewriting/pagination.py +++ b/apps/jumpserver/rewriting/pagination.py @@ -11,3 +11,10 @@ class MaxLimitOffsetPagination(LimitOffsetPagination): return queryset.values_list('id').order_by().count() except (AttributeError, TypeError, FieldError): return len(queryset) + + def paginate_queryset(self, queryset, request, view=None): + if view and hasattr(view, 'page_max_limit'): + self.max_limit = view.page_max_limit + if view and hasattr(view, 'page_default_limit'): + self.default_limit = view.page_default_limit + return super().paginate_queryset(queryset, request, view) diff --git a/apps/jumpserver/settings/base.py b/apps/jumpserver/settings/base.py index 4da1dfc7e..e2bfbc5c1 100644 --- a/apps/jumpserver/settings/base.py +++ b/apps/jumpserver/settings/base.py @@ -109,6 +109,7 @@ for host_port in ALLOWED_DOMAINS: continue CSRF_TRUSTED_ORIGINS.append('{}://*.{}'.format(schema, origin)) +CORS_ALLOWED_ORIGINS = [o.replace('*.', '') for o in CSRF_TRUSTED_ORIGINS] CSRF_FAILURE_VIEW = 'jumpserver.views.other.csrf_failure' # print("CSRF_TRUSTED_ORIGINS: ") # for origin in CSRF_TRUSTED_ORIGINS: @@ -134,6 +135,7 @@ INSTALLED_APPS = [ 'acls.apps.AclsConfig', 'notifications.apps.NotificationsConfig', 'rbac.apps.RBACConfig', + 'labels.apps.LabelsConfig', 'rest_framework', 'rest_framework_swagger', 'drf_yasg', @@ -142,6 +144,7 @@ INSTALLED_APPS = [ 'django_filters', 'bootstrap3', 'captcha', + 'corsheaders', 'private_storage', 'django_celery_beat', 'django.contrib.auth', @@ -160,6 +163,7 @@ MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.locale.LocaleMiddleware', + 'corsheaders.middleware.CorsMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', diff --git a/apps/jumpserver/settings/custom.py b/apps/jumpserver/settings/custom.py index 3c0cfed58..fa83fa9fd 100644 --- a/apps/jumpserver/settings/custom.py +++ b/apps/jumpserver/settings/custom.py @@ -207,6 +207,7 @@ SESSION_RSA_PUBLIC_KEY_NAME = 'jms_public_key' OPERATE_LOG_ELASTICSEARCH_CONFIG = CONFIG.OPERATE_LOG_ELASTICSEARCH_CONFIG MAX_LIMIT_PER_PAGE = CONFIG.MAX_LIMIT_PER_PAGE +DEFAULT_PAGE_SIZE = CONFIG.DEFAULT_PAGE_SIZE # Magnus DB Port MAGNUS_ORACLE_PORTS = CONFIG.MAGNUS_ORACLE_PORTS diff --git a/apps/jumpserver/settings/libs.py b/apps/jumpserver/settings/libs.py index 817db8861..1f2c333ec 100644 --- a/apps/jumpserver/settings/libs.py +++ b/apps/jumpserver/settings/libs.py @@ -46,6 +46,7 @@ REST_FRAMEWORK = { 'DATETIME_FORMAT': '%Y/%m/%d %H:%M:%S %z', 'DATETIME_INPUT_FORMATS': ['%Y/%m/%d %H:%M:%S %z', 'iso-8601', '%Y-%m-%d %H:%M:%S %z'], 'DEFAULT_PAGINATION_CLASS': 'jumpserver.rewriting.pagination.MaxLimitOffsetPagination', + 'PAGE_SIZE': CONFIG.DEFAULT_PAGE_SIZE, 'EXCEPTION_HANDLER': 'common.drf.exc_handlers.common_exception_handler', } diff --git a/apps/jumpserver/urls.py b/apps/jumpserver/urls.py index 8fbea643c..881d5c57a 100644 --- a/apps/jumpserver/urls.py +++ b/apps/jumpserver/urls.py @@ -28,6 +28,7 @@ api_v1 = [ path('acls/', include('acls.urls.api_urls', namespace='api-acls')), path('notifications/', include('notifications.urls.api_urls', namespace='api-notifications')), path('rbac/', include('rbac.urls.api_urls', namespace='api-rbac')), + path('labels/', include('labels.urls', namespace='api-label')), path('prometheus/metrics/', api.PrometheusMetricsApi.as_view()), ] diff --git a/apps/labels/__init__.py b/apps/labels/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/apps/labels/admin.py b/apps/labels/admin.py new file mode 100644 index 000000000..8c38f3f3d --- /dev/null +++ b/apps/labels/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/apps/labels/api.py b/apps/labels/api.py new file mode 100644 index 000000000..bd2685036 --- /dev/null +++ b/apps/labels/api.py @@ -0,0 +1,141 @@ +from django.shortcuts import get_object_or_404 +from rest_framework.decorators import action +from rest_framework.response import Response + +from common.api.generic import JMSModelViewSet +from common.utils import is_true +from orgs.mixins.api import OrgBulkModelViewSet +from orgs.mixins.models import OrgModelMixin +from orgs.utils import current_org +from rbac.models import ContentType +from rbac.serializers import ContentTypeSerializer +from . import serializers +from .models import Label, LabeledResource + +__all__ = ['LabelViewSet'] + + +class ContentTypeViewSet(JMSModelViewSet): + serializer_class = ContentTypeSerializer + http_method_names = ['get', 'head', 'options'] + rbac_perms = { + 'default': 'labels.view_contenttype', + 'resources': 'labels.view_contenttype', + } + page_default_limit = None + can_labeled_content_type = [] + model = ContentType + + @classmethod + def get_can_labeled_content_type_ids(cls): + if cls.can_labeled_content_type: + return cls.can_labeled_content_type + content_types = ContentType.objects.all() + for ct in content_types: + model_cls = ct.model_class() + if not model_cls: + continue + if model_cls._meta.parents: + continue + if 'labels' in model_cls._meta._forward_fields_map.keys(): + # if issubclass(model_cls, LabeledMixin): + cls.can_labeled_content_type.append(ct.id) + return cls.can_labeled_content_type + + def get_queryset(self): + ids = self.get_can_labeled_content_type_ids() + queryset = ContentType.objects.filter(id__in=ids) + return queryset + + @action(methods=['GET'], detail=True, serializer_class=serializers.ContentTypeResourceSerializer) + def resources(self, request, *args, **kwargs): + self.page_default_limit = 100 + content_type = self.get_object() + model = content_type.model_class() + + if issubclass(model, OrgModelMixin): + queryset = model.objects.filter(org_id=current_org.id) + else: + queryset = model.objects.all() + + keyword = request.query_params.get('search') + if keyword: + queryset = content_type.filter_queryset(queryset, keyword) + return self.get_paginated_response_from_queryset(queryset) + + +class LabelContentTypeResourceViewSet(JMSModelViewSet): + serializer_class = serializers.ContentTypeResourceSerializer + rbac_perms = { + 'default': 'labels.view_labeledresource', + 'update': 'labels.change_labeledresource', + } + ordering_fields = ('res_type', 'date_created') + + def get_queryset(self): + label_pk = self.kwargs.get('label') + res_type = self.kwargs.get('res_type') + label = get_object_or_404(Label, pk=label_pk) + content_type = get_object_or_404(ContentType, id=res_type) + bound = self.request.query_params.get('bound', '1') + res_ids = LabeledResource.objects.filter(res_type=content_type, label=label) \ + .values_list('res_id', flat=True) + res_ids = set(res_ids) + model = content_type.model_class() + if is_true(bound): + queryset = model.objects.filter(id__in=list(res_ids)) + else: + queryset = model.objects.exclude(id__in=list(res_ids)) + keyword = self.request.query_params.get('search') + if keyword: + queryset = content_type.filter_queryset(queryset, keyword) + return queryset + + def put(self, request, *args, **kwargs): + label_pk = self.kwargs.get('label') + res_type = self.kwargs.get('res_type') + content_type = get_object_or_404(ContentType, id=res_type) + label = get_object_or_404(Label, pk=label_pk) + res_ids = request.data.get('res_ids', []) + + LabeledResource.objects \ + .filter(res_type=content_type, label=label) \ + .exclude(res_id__in=res_ids).delete() + resources = [] + for res_id in res_ids: + resources.append(LabeledResource(res_type=content_type, res_id=res_id, label=label, org_id=current_org.id)) + LabeledResource.objects.bulk_create(resources, ignore_conflicts=True) + return Response({"total": len(res_ids)}) + + +class LabelViewSet(OrgBulkModelViewSet): + model = Label + filterset_fields = ("name", "value") + search_fields = filterset_fields + serializer_classes = { + 'default': serializers.LabelSerializer, + 'resource_types': ContentTypeSerializer, + } + rbac_perms = { + 'resource_types': 'labels.view_label', + 'keys': 'labels.view_label', + } + + @action(methods=['GET'], detail=False) + def keys(self, request, *args, **kwargs): + queryset = Label.objects.all() + keyword = request.query_params.get('search') + if keyword: + queryset = queryset.filter(name__icontains=keyword) + keys = queryset.values_list('name', flat=True).distinct() + return Response(keys) + + +class LabeledResourceViewSet(OrgBulkModelViewSet): + model = LabeledResource + filterset_fields = ("label__name", "label__value", "res_type", "res_id", "label") + search_fields = filterset_fields + serializer_classes = { + 'default': serializers.LabeledResourceSerializer, + } + ordering_fields = ('res_type', 'date_created') diff --git a/apps/labels/apps.py b/apps/labels/apps.py new file mode 100644 index 000000000..434a5c6df --- /dev/null +++ b/apps/labels/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class LabelsConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'labels' diff --git a/apps/labels/const.py b/apps/labels/const.py new file mode 100644 index 000000000..e69de29bb diff --git a/apps/labels/migrations/0001_initial.py b/apps/labels/migrations/0001_initial.py new file mode 100644 index 000000000..cefe74e5c --- /dev/null +++ b/apps/labels/migrations/0001_initial.py @@ -0,0 +1,54 @@ +# Generated by Django 4.1.10 on 2023-11-06 10:38 + +from django.db import migrations, models +import django.db.models.deletion +import uuid + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('contenttypes', '0002_remove_content_type_name'), + ] + + operations = [ + migrations.CreateModel( + name='Label', + fields=[ + ('created_by', models.CharField(blank=True, max_length=128, null=True, verbose_name='Created by')), + ('updated_by', models.CharField(blank=True, max_length=128, null=True, verbose_name='Updated by')), + ('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')), + ('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')), + ('comment', models.TextField(blank=True, default='', verbose_name='Comment')), + ('internal', models.BooleanField(default=False, verbose_name='Internal')), + ('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)), + ('org_id', models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization')), + ('name', models.CharField(db_index=True, max_length=64, verbose_name='Name')), + ('value', models.CharField(max_length=64, verbose_name='Value')), + ], + options={ + 'verbose_name': 'Label', + 'unique_together': {('name', 'value', 'org_id')}, + }, + ), + migrations.CreateModel( + name='LabeledResource', + fields=[ + ('created_by', models.CharField(blank=True, max_length=128, null=True, verbose_name='Created by')), + ('updated_by', models.CharField(blank=True, max_length=128, null=True, verbose_name='Updated by')), + ('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')), + ('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')), + ('comment', models.TextField(blank=True, default='', verbose_name='Comment')), + ('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)), + ('org_id', models.CharField(blank=True, db_index=True, default='', max_length=36, verbose_name='Organization')), + ('res_id', models.CharField(db_index=True, max_length=36, verbose_name='Resource ID')), + ('label', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='labels.label')), + ('res_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/apps/labels/migrations/0002_auto_20231103_1659.py b/apps/labels/migrations/0002_auto_20231103_1659.py new file mode 100644 index 000000000..49386ed39 --- /dev/null +++ b/apps/labels/migrations/0002_auto_20231103_1659.py @@ -0,0 +1,52 @@ +# Generated by Django 4.1.10 on 2023-11-03 08:59 + +from django.db import migrations + + +def migrate_assets_labels(apps, schema_editor): + old_label_model = apps.get_model('assets', 'Label') + new_label_model = apps.get_model('labels', 'Label') + asset_model = apps.get_model('assets', 'Asset') + labeled_item_model = apps.get_model('labels', 'LabeledResource') + + old_labels = old_label_model.objects.all() + new_labels = [] + old_new_label_map = {} + for label in old_labels: + new_label = new_label_model(name=label.name, value=label.value, org_id=label.org_id) + old_new_label_map[label.id] = new_label + new_labels.append(new_label) + new_label_model.objects.bulk_create(new_labels, ignore_conflicts=True) + + label_relations = asset_model.labels.through.objects.all() + bulk_size = 1000 + count = 0 + content_type = apps.get_model('contenttypes', 'contenttype').objects.get_for_model(asset_model) + + while True: + relations = label_relations[count:count + bulk_size] + if not relations: + break + count += bulk_size + + tagged_items = [] + for relation in relations: + new_label = old_new_label_map[relation.label_id] + tagged_item = labeled_item_model( + label_id=new_label.id, res_type=content_type, + res_id=relation.asset_id, org_id=new_label.org_id + ) + tagged_items.append(tagged_item) + labeled_item_model.objects.bulk_create(tagged_items, ignore_conflicts=True) + + +class Migration(migrations.Migration): + + dependencies = [ + ('labels', '0001_initial'), + ('assets', '0125_auto_20231011_1053') + ] + + operations = [ + migrations.RunPython(migrate_assets_labels), + ] diff --git a/apps/labels/migrations/0003_alter_labeledresource_options_and_more.py b/apps/labels/migrations/0003_alter_labeledresource_options_and_more.py new file mode 100644 index 000000000..852376180 --- /dev/null +++ b/apps/labels/migrations/0003_alter_labeledresource_options_and_more.py @@ -0,0 +1,28 @@ +# Generated by Django 4.1.10 on 2023-11-15 10:58 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('contenttypes', '0002_remove_content_type_name'), + ('labels', '0002_auto_20231103_1659'), + ] + + operations = [ + migrations.AlterModelOptions( + name='labeledresource', + options={'verbose_name': 'Labeled resource'}, + ), + migrations.AlterField( + model_name='labeledresource', + name='label', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='labeled_resources', to='labels.label'), + ), + migrations.AlterUniqueTogether( + name='labeledresource', + unique_together={('label', 'res_type', 'res_id', 'org_id')}, + ), + ] diff --git a/apps/labels/migrations/__init__.py b/apps/labels/migrations/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/apps/labels/mixins.py b/apps/labels/mixins.py new file mode 100644 index 000000000..23fa90386 --- /dev/null +++ b/apps/labels/mixins.py @@ -0,0 +1,13 @@ +from django.contrib.contenttypes.fields import GenericRelation +from django.db import models + +from .models import LabeledResource + +__all__ = ['LabeledMixin'] + + +class LabeledMixin(models.Model): + labels = GenericRelation(LabeledResource, object_id_field='res_id', content_type_field='res_type') + + class Meta: + abstract = True diff --git a/apps/labels/models.py b/apps/labels/models.py new file mode 100644 index 000000000..3e38da48e --- /dev/null +++ b/apps/labels/models.py @@ -0,0 +1,38 @@ +from django.contrib.contenttypes.fields import GenericForeignKey +from django.contrib.contenttypes.models import ContentType +from django.db import models +from django.utils.translation import gettext_lazy as _ + +from common.utils import lazyproperty +from orgs.mixins.models import JMSOrgBaseModel + + +class Label(JMSOrgBaseModel): + name = models.CharField(max_length=64, verbose_name=_("Name"), db_index=True) + value = models.CharField(max_length=64, unique=False, verbose_name=_("Value")) + internal = models.BooleanField(default=False, verbose_name=_("Internal")) + + class Meta: + unique_together = [('name', 'value', 'org_id')] + verbose_name = _('Label') + + @lazyproperty + def res_count(self): + return self.labeled_resources.count() + + def __str__(self): + return '{}:{}'.format(self.name, self.value) + + +class LabeledResource(JMSOrgBaseModel): + label = models.ForeignKey(Label, on_delete=models.CASCADE, related_name='labeled_resources') + res_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) + res_id = models.CharField(max_length=36, verbose_name=_("Resource ID"), db_index=True) + resource = GenericForeignKey('res_type', 'res_id') + + class Meta: + unique_together = [('label', 'res_type', 'res_id', 'org_id')] + verbose_name = _('Labeled resource') + + def __str__(self): + return '{} => {}'.format(self.label, self.resource) diff --git a/apps/labels/serializers.py b/apps/labels/serializers.py new file mode 100644 index 000000000..476b3c06f --- /dev/null +++ b/apps/labels/serializers.py @@ -0,0 +1,54 @@ +from django.contrib.contenttypes.models import ContentType +from django.db.models import Count +from django.utils.translation import gettext_lazy as _ +from rest_framework import serializers + +from common.serializers.fields import ObjectRelatedField +from orgs.mixins.serializers import BulkOrgResourceModelSerializer +from .models import Label, LabeledResource + +__all__ = ['LabelSerializer', 'LabeledResourceSerializer', 'ContentTypeResourceSerializer'] + + +class LabelSerializer(BulkOrgResourceModelSerializer): + class Meta: + model = Label + fields = ['id', 'name', 'value', 'res_count', 'date_created', 'date_updated'] + read_only_fields = ('date_created', 'date_updated', 'res_count') + extra_kwargs = { + 'res_count': {'label': _('Resource count')}, + } + + @classmethod + def setup_eager_loading(cls, queryset): + """ Perform necessary eager loading of data. """ + queryset = queryset.annotate(res_count=Count('labeled_resources')) + return queryset + + +class LabeledResourceSerializer(serializers.ModelSerializer): + res_type = ObjectRelatedField( + queryset=ContentType.objects, attrs=('app_label', 'model', 'name'), label=_("Resource type") + ) + label = ObjectRelatedField(queryset=Label.objects, attrs=('name', 'value')) + resource = serializers.CharField(label=_("Resource")) + + class Meta: + model = LabeledResource + fields = ('id', 'label', 'res_type', 'res_id', 'date_created', 'resource', 'date_updated') + read_only_fields = ('date_created', 'date_updated', 'resource') + + @classmethod + def setup_eager_loading(cls, queryset): + """ Perform necessary eager loading of data. """ + queryset = queryset.select_related('label', 'res_type') + return queryset + + +class ContentTypeResourceSerializer(serializers.Serializer): + id = serializers.CharField() + name = serializers.SerializerMethodField() + + @staticmethod + def get_name(obj): + return str(obj) diff --git a/apps/labels/tests.py b/apps/labels/tests.py new file mode 100644 index 000000000..7ce503c2d --- /dev/null +++ b/apps/labels/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/apps/labels/urls.py b/apps/labels/urls.py new file mode 100644 index 000000000..a3971917f --- /dev/null +++ b/apps/labels/urls.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# + +from rest_framework_bulk.routes import BulkRouter + +from . import api + +app_name = 'labels' + +router = BulkRouter() +router.register(r'labels', api.LabelViewSet, 'label') +router.register(r'labels/(?P
    " +#: labels/models.py:30 +msgid "Resource ID" +msgstr "リソースID" + +#: labels/models.py:35 +msgid "Labeled resource" +msgstr "" + +#: labels/serializers.py:19 +msgid "Resource count" +msgstr "リソース数" + +#: labels/serializers.py:31 +msgid "Resource type" +msgstr "リソースタイプ" + #: notifications/backends/__init__.py:13 msgid "Site message" msgstr "サイトメッセージ" @@ -3936,7 +3990,7 @@ msgstr "システムメッセージ" msgid "Publish the station message" msgstr "投稿サイトニュース" -#: ops/ansible/inventory.py:95 ops/models/job.py:61 +#: ops/ansible/inventory.py:95 ops/models/job.py:63 msgid "No account available" msgstr "利用可能なアカウントがありません" @@ -3988,7 +4042,7 @@ msgstr "ファイルキーこのフィールドは必須です" msgid "This file can not be delete" msgstr "このファイルを削除できません" -#: ops/apps.py:9 ops/notifications.py:17 rbac/tree.py:55 +#: ops/apps.py:9 ops/notifications.py:17 rbac/tree.py:57 msgid "App ops" msgstr "アプリ操作" @@ -4028,7 +4082,7 @@ msgstr "VCS" msgid "Adhoc" msgstr "コマンド#コマンド#" -#: ops/const.py:39 ops/models/job.py:138 +#: ops/const.py:39 ops/models/job.py:147 msgid "Playbook" msgstr "Playbook" @@ -4109,15 +4163,17 @@ msgstr "定期的または定期的に設定を行う必要があります" msgid "Pattern" msgstr "パターン" -#: ops/models/adhoc.py:23 ops/models/job.py:133 +#: ops/models/adhoc.py:23 ops/models/job.py:142 msgid "Module" msgstr "モジュール" -#: ops/models/adhoc.py:24 ops/models/celery.py:58 ops/models/job.py:131 +#: ops/models/adhoc.py:24 ops/models/celery.py:58 ops/models/job.py:140 #: terminal/models/component/task.py:14 msgid "Args" msgstr "アルグ" +# msgid "Creator" +# msgstr "作成者" #: ops/models/base.py:19 msgid "Account policy" msgstr "アカウント ポリシー" @@ -4126,16 +4182,16 @@ msgstr "アカウント ポリシー" msgid "Last execution" msgstr "最後の実行" -#: ops/models/base.py:22 ops/serializers/job.py:19 +#: ops/models/base.py:22 ops/serializers/job.py:18 msgid "Date last run" msgstr "最終実行日" -#: ops/models/base.py:51 ops/models/job.py:227 +#: ops/models/base.py:51 ops/models/job.py:236 #: xpack/plugins/cloud/models.py:199 msgid "Result" msgstr "結果" -#: ops/models/base.py:52 ops/models/job.py:228 +#: ops/models/base.py:52 ops/models/job.py:237 msgid "Summary" msgstr "概要" @@ -4168,51 +4224,51 @@ msgstr "発売日" msgid "Celery Task Execution" msgstr "Celery タスク実行" -#: ops/models/job.py:135 +#: ops/models/job.py:144 msgid "Chdir" msgstr "実行ディレクトリ" -#: ops/models/job.py:136 +#: ops/models/job.py:145 msgid "Timeout (Seconds)" msgstr "タイムアウト(秒)" -#: ops/models/job.py:143 +#: ops/models/job.py:152 msgid "Use Parameter Define" msgstr "パラメータ定義を使用する" -#: ops/models/job.py:144 +#: ops/models/job.py:153 msgid "Parameters define" msgstr "パラメータ定義" -#: ops/models/job.py:145 +#: ops/models/job.py:154 msgid "Runas" msgstr "ユーザーとして実行" -#: ops/models/job.py:147 +#: ops/models/job.py:156 msgid "Runas policy" msgstr "ユーザー ポリシー" -#: ops/models/job.py:211 +#: ops/models/job.py:220 msgid "Job" msgstr "ジョブ#ジョブ#" -#: ops/models/job.py:234 +#: ops/models/job.py:243 msgid "Material" msgstr "Material" -#: ops/models/job.py:236 +#: ops/models/job.py:245 msgid "Material Type" msgstr "Material を選択してオプションを設定します。" -#: ops/models/job.py:544 +#: ops/models/job.py:557 msgid "Job Execution" msgstr "ジョブ実行" -#: ops/models/playbook.py:33 +#: ops/models/playbook.py:34 msgid "CreateMethod" msgstr "创建方式" -#: ops/models/playbook.py:34 +#: ops/models/playbook.py:35 msgid "VCS URL" msgstr "VCS URL" @@ -4244,19 +4300,19 @@ msgstr "{max_threshold}%: => {value} を超える使用メモリ" msgid "CPU load more than {max_threshold}: => {value}" msgstr "{max_threshold} を超えるCPUロード: => {value}" -#: ops/serializers/job.py:17 +#: ops/serializers/job.py:16 msgid "Run after save" msgstr "保存後に実行" -#: ops/serializers/job.py:54 +#: ops/serializers/job.py:53 msgid "Job type" msgstr "タスクの種類" -#: ops/serializers/job.py:57 terminal/serializers/session.py:53 +#: ops/serializers/job.py:56 terminal/serializers/session.py:53 msgid "Is finished" msgstr "終了しました" -#: ops/serializers/job.py:58 +#: ops/serializers/job.py:57 msgid "Time cost" msgstr "時を過ごす" @@ -4347,7 +4403,7 @@ msgstr "" msgid "The organization have resource ({}) cannot be deleted" msgstr "組織のリソース ({}) は削除できません" -#: orgs/apps.py:7 rbac/tree.py:126 +#: orgs/apps.py:7 rbac/tree.py:128 msgid "App organizations" msgstr "アプリ組織" @@ -4436,7 +4492,7 @@ msgstr "転送" msgid "Clipboard" msgstr "クリップボード" -#: perms/models/asset_permission.py:88 +#: perms/models/asset_permission.py:89 msgid "Asset permission" msgstr "資産権限" @@ -4580,7 +4636,7 @@ msgstr "ファイルマネージャを表示できます" msgid "Can view System Tools" msgstr "システムツールを表示できます" -#: rbac/models/permission.py:27 rbac/models/role.py:34 +#: rbac/models/permission.py:78 rbac/models/role.py:34 msgid "Permissions" msgstr "権限" @@ -4590,7 +4646,7 @@ msgid "Scope" msgstr "スコープ" #: rbac/models/role.py:46 rbac/models/rolebinding.py:52 -#: users/models/user.py:803 +#: users/models/user.py:808 msgid "Role" msgstr "ロール" @@ -4629,7 +4685,7 @@ msgstr "システムロールバインディング" msgid "Perms" msgstr "パーマ" -#: rbac/serializers/role.py:27 users/serializers/group.py:30 +#: rbac/serializers/role.py:27 users/serializers/group.py:31 msgid "Users amount" msgstr "ユーザー数" @@ -4665,59 +4721,60 @@ msgstr "システム設定" msgid "Session audits" msgstr "セッション監査" -#: rbac/tree.py:47 +#: rbac/tree.py:49 msgid "Cloud import" msgstr "クラウドインポート" -#: rbac/tree.py:48 +#: rbac/tree.py:50 msgid "Backup account" msgstr "バックアップアカウント" -#: rbac/tree.py:49 +#: rbac/tree.py:51 msgid "Gather account" msgstr "アカウントを集める" -#: rbac/tree.py:51 +#: rbac/tree.py:53 msgid "Asset change auth" msgstr "資産の改ざん" -#: rbac/tree.py:52 +#: rbac/tree.py:54 msgid "Terminal setting" msgstr "ターミナル設定" -#: rbac/tree.py:53 +#: rbac/tree.py:55 msgid "Task Center" msgstr "タスクセンター" -#: rbac/tree.py:54 +#: rbac/tree.py:56 msgid "My assets" msgstr "私の資産" -#: rbac/tree.py:56 terminal/models/applet/applet.py:52 +#: rbac/tree.py:58 terminal/models/applet/applet.py:52 #: terminal/models/applet/applet.py:315 terminal/models/applet/host.py:30 #: terminal/serializers/applet.py:15 msgid "Applet" msgstr "リモートアプリケーション" -#: rbac/tree.py:127 +#: rbac/tree.py:129 msgid "Ticket comment" msgstr "チケットコメント" -#: rbac/tree.py:128 settings/serializers/feature.py:58 +#: rbac/tree.py:130 settings/serializers/feature.py:58 #: tickets/models/ticket/general.py:307 msgid "Ticket" msgstr "チケット" -#: rbac/tree.py:129 +#: rbac/tree.py:131 msgid "Common setting" msgstr "共通設定" -#: rbac/tree.py:130 +#: rbac/tree.py:132 msgid "View permission tree" msgstr "権限ツリーの表示" #: settings/api/dingtalk.py:31 settings/api/feishu.py:36 -#: settings/api/sms.py:160 settings/api/vault.py:40 settings/api/wecom.py:37 +#: settings/api/slack.py:34 settings/api/sms.py:160 settings/api/vault.py:40 +#: settings/api/wecom.py:37 msgid "Test success" msgstr "テストの成功" @@ -4845,22 +4902,28 @@ msgid "FeiShu Auth" msgstr "飛本 認証" #: settings/serializers/auth/base.py:20 +#, fuzzy +#| msgid "CAS Auth" +msgid "Slack Auth" +msgstr "CAS 認証" + +#: settings/serializers/auth/base.py:21 msgid "WeCom Auth" msgstr "企業微信 認証" -#: settings/serializers/auth/base.py:21 +#: settings/serializers/auth/base.py:22 msgid "SSO Auth" msgstr "SSO Token 認証" -#: settings/serializers/auth/base.py:22 +#: settings/serializers/auth/base.py:23 msgid "Passkey Auth" msgstr "Passkey 認証" -#: settings/serializers/auth/base.py:25 +#: settings/serializers/auth/base.py:26 msgid "Forgot password url" msgstr "パスワードのURLを忘れた" -#: settings/serializers/auth/base.py:28 +#: settings/serializers/auth/base.py:29 msgid "Enable login redirect msg" msgstr "ログインリダイレクトの有効化msg" @@ -5162,6 +5225,12 @@ msgstr "SP プライベートキー" msgid "SP cert" msgstr "SP 証明書" +#: settings/serializers/auth/slack.py:12 +#, fuzzy +#| msgid "Enable CAS Auth" +msgid "Enable Slack Auth" +msgstr "CAS 認証の有効化" + #: settings/serializers/auth/sms.py:17 msgid "Enable SMS" msgstr "SMSの有効化" @@ -6450,7 +6519,7 @@ msgstr "同期日" #: terminal/models/applet/host.py:28 msgid "Using same account" -msgstr "同じ名前のアカウントを使用" +msgstr "同じアカウントを使用する" #: terminal/models/applet/host.py:139 msgid "Initial" @@ -6949,7 +7018,7 @@ msgstr "エンドポイントサフィックス" msgid "HOST" msgstr "ホスト" -#: terminal/serializers/storage.py:146 users/models/user.py:823 +#: terminal/serializers/storage.py:146 users/models/user.py:828 #: xpack/plugins/cloud/serializers/account_attrs.py:206 msgid "Private key" msgstr "ssh秘密鍵" @@ -7340,11 +7409,11 @@ msgstr "'{}'という名前の権限は既に存在します" msgid "The ticket flow `{}` does not exist" msgstr "チケットフロー '{}'が存在しない" -#: tickets/templates/tickets/_msg_ticket.html:20 +#: tickets/templates/tickets/_msg_ticket.html:21 msgid "View details" msgstr "詳細の表示" -#: tickets/templates/tickets/_msg_ticket.html:25 +#: tickets/templates/tickets/_msg_ticket.html:26 msgid "Direct approval" msgstr "直接承認" @@ -7352,12 +7421,12 @@ msgstr "直接承認" msgid "Ticket information" msgstr "作業指示情報" -#: tickets/templates/tickets/approve_check_password.html:29 +#: tickets/templates/tickets/approve_check_password.html:28 #: tickets/views/approve.py:40 tickets/views/approve.py:77 msgid "Ticket approval" msgstr "作業指示の承認" -#: tickets/templates/tickets/approve_check_password.html:44 +#: tickets/templates/tickets/approve_check_password.html:43 msgid "Approval" msgstr "承認" @@ -7384,11 +7453,11 @@ msgstr "無効な承認アクション" msgid "This user is not authorized to approve this ticket" msgstr "このユーザーはこの作業指示を承認する権限がありません" -#: users/api/user.py:141 +#: users/api/user.py:137 msgid "Can not invite self" msgstr "自分自身を招待することはできません" -#: users/api/user.py:194 +#: users/api/user.py:190 msgid "Could not reset self otp, use profile reset instead" msgstr "自己otpをリセットできませんでした、代わりにプロファイルリセットを使用" @@ -7523,7 +7592,7 @@ msgstr "公開鍵は古いものと同じであってはなりません。" msgid "Not a valid ssh public key" msgstr "有効なssh公開鍵ではありません" -#: users/forms/profile.py:173 users/models/user.py:826 +#: users/forms/profile.py:173 users/models/user.py:831 #: xpack/plugins/cloud/serializers/account_attrs.py:203 msgid "Public key" msgstr "公開キー" @@ -7532,72 +7601,74 @@ msgstr "公開キー" msgid "Preference" msgstr "ユーザー設定" -#: users/models/user.py:643 users/serializers/profile.py:94 +#: users/models/user.py:644 users/serializers/profile.py:94 msgid "Force enable" msgstr "強制有効" -#: users/models/user.py:805 users/serializers/user.py:169 +#: users/models/user.py:810 users/serializers/user.py:169 msgid "Is service account" msgstr "サービスアカウントです" -#: users/models/user.py:807 +#: users/models/user.py:812 msgid "Avatar" msgstr "アバター" -#: users/models/user.py:810 +#: users/models/user.py:815 msgid "Wechat" msgstr "微信" -#: users/models/user.py:813 users/serializers/user.py:106 +#: users/models/user.py:818 users/serializers/user.py:106 msgid "Phone" msgstr "電話" -#: users/models/user.py:819 +#: users/models/user.py:824 msgid "OTP secret key" msgstr "OTP 秘密" -#: users/models/user.py:831 users/serializers/profile.py:128 +# msgid "Private key" +# msgstr "ssh秘密鍵" +#: users/models/user.py:836 users/serializers/profile.py:128 #: users/serializers/user.py:166 msgid "Is first login" msgstr "最初のログインです" -#: users/models/user.py:841 +#: users/models/user.py:846 msgid "Date password last updated" msgstr "最終更新日パスワード" -#: users/models/user.py:844 +#: users/models/user.py:849 msgid "Need update password" msgstr "更新パスワードが必要" -#: users/models/user.py:846 +#: users/models/user.py:851 msgid "Date api key used" msgstr "Api key 最後に使用した日付" -#: users/models/user.py:969 +#: users/models/user.py:975 msgid "Can not delete admin user" msgstr "管理者ユーザーを削除できませんでした" -#: users/models/user.py:995 +#: users/models/user.py:1002 msgid "Can invite user" msgstr "ユーザーを招待できます" -#: users/models/user.py:996 +#: users/models/user.py:1003 msgid "Can remove user" msgstr "ユーザーを削除できます" -#: users/models/user.py:997 +#: users/models/user.py:1004 msgid "Can match user" msgstr "ユーザーに一致できます" -#: users/models/user.py:1006 +#: users/models/user.py:1013 msgid "Administrator" msgstr "管理者" -#: users/models/user.py:1009 +#: users/models/user.py:1016 msgid "Administrator is the super user of system" msgstr "管理者はシステムのスーパーユーザーです" -#: users/models/user.py:1034 +#: users/models/user.py:1041 msgid "User password history" msgstr "ユーザーパスワード履歴" @@ -7678,10 +7749,15 @@ msgid "" "remote computer to fit the window size of the client computer when the " "window is resized." msgstr "" -"ウィンドウサイズを変更するときにクライアントコンピュータがクライアントコン" -"ピュータのウィンドウサイズに合わせるためにリモートコンピュータ上のコンテンツ" -"をスケーリングすべきかどうかを判断する" +# msgid "" +# "Determines whether the client computer should scale the content on the " +# "remote computer to fit the window size of the client computer when the " +# "window is resized." +# msgstr "" +# "ウィンドウサイズを変更するときにクライアントコンピュータがクライアントコン" +# "ピュータのウィンドウサイズに合わせるためにリモートコンピュータ上のコンテンツ" +# "をスケーリングすべきかどうかを判断する" #: users/serializers/preference/luna.py:51 msgid "Remote application connection method" msgstr "リモートアプリケーション接続方式" @@ -7762,15 +7838,15 @@ msgstr "アバターURL" msgid "MFA level" msgstr "MFA レベル" -#: users/serializers/user.py:282 +#: users/serializers/user.py:287 msgid "Select users" msgstr "ユーザーの選択" -#: users/serializers/user.py:283 +#: users/serializers/user.py:288 msgid "For security, only list several users" msgstr "セキュリティのために、複数のユーザーのみをリストします" -#: users/serializers/user.py:316 +#: users/serializers/user.py:321 msgid "name not unique" msgstr "名前が一意ではない" @@ -8687,15 +8763,7 @@ msgstr "ログアウトページのロゴ" msgid "Theme" msgstr "テーマ" -#: xpack/plugins/interface/models.py:42 -msgid "Beian link" -msgstr "公安オンライン申告ジャンプリンク" - -#: xpack/plugins/interface/models.py:43 -msgid "Beian text" -msgstr "公安網登録番号" - -#: xpack/plugins/interface/models.py:46 xpack/plugins/interface/models.py:87 +#: xpack/plugins/interface/models.py:44 xpack/plugins/interface/models.py:85 msgid "Interface setting" msgstr "インターフェイスの設定" @@ -8727,26 +8795,17 @@ msgstr "エンタープライズプロフェッショナル版" msgid "Ultimate edition" msgstr "エンタープライズ・フラッグシップ・エディション" -#~ msgid "Synchronization start, please wait." -#~ msgstr "同期開始、お待ちください。" +#~ msgid "FeiShu query user failed" +#~ msgstr "FeiShuクエリユーザーが失敗しました" -#~ msgid "Synchronization is running, please wait." -#~ msgstr "同期が実行中です。しばらくお待ちください。" +#~ msgid "The FeiShu is already bound to another user" +#~ msgstr "FeiShuはすでに別のユーザーにバインドされています" -#~ msgid "Synchronization error: {}" -#~ msgstr "同期エラー: {}" +#~ msgid "Binding FeiShu successfully" +#~ msgstr "本を飛ばすのバインドに成功" -#~ msgid "Copy" -#~ msgstr "コピー" +#~ msgid "Beian link" +#~ msgstr "公安オンライン申告ジャンプリンク" -#~ msgid "Paste" -#~ msgstr "貼り付け" - -#~ msgid "Password can not contains `'` " -#~ msgstr "パスワードには `'` を含まない" - -#~ msgid "Password can not contains `\"` " -#~ msgstr "パスワードには `\"` を含まない" - -#~ msgid "Object Storage" -#~ msgstr "オブジェクトストレージ" +#~ msgid "Beian text" +#~ msgstr "公安網登録番号" diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index 73dee4284..7d33a1c92 100644 --- a/apps/locale/zh/LC_MESSAGES/django.mo +++ b/apps/locale/zh/LC_MESSAGES/django.mo @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e4af35b685fe0eb73e4dd9790669164bb823065d994a24f5441c9cff7a6e99e7 -size 135811 +oid sha256:a46c7aff5f314cbab9849e4c11671f81b849f98fad73887ced5b2012635b8a97 +size 135290 diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index 5f24678f2..0ef39aa36 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: JumpServer 0.3.3\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-11-24 10:34+0800\n" +"POT-Creation-Date: 2023-12-05 10:16+0800\n" "PO-Revision-Date: 2021-05-20 10:54+0800\n" "Last-Translator: ibuler \n" "Language-Team: JumpServer team\n" @@ -73,7 +73,7 @@ msgstr "同名账号" msgid "Anonymous account" msgstr "匿名账号" -#: accounts/const/account.py:25 users/models/user.py:739 +#: accounts/const/account.py:25 users/models/user.py:740 msgid "Local" msgstr "数据库" @@ -90,7 +90,7 @@ msgstr "模板" msgid "Skip" msgstr "跳过" -#: accounts/const/account.py:32 audits/const.py:24 rbac/tree.py:237 +#: accounts/const/account.py:32 audits/const.py:24 rbac/tree.py:239 #: templates/_csv_import_export.html:18 templates/_csv_update_modal.html:6 msgid "Update" msgstr "更新" @@ -102,7 +102,7 @@ msgstr "更新" msgid "Failed" msgstr "失败" -#: accounts/const/automation.py:24 rbac/tree.py:50 +#: accounts/const/automation.py:24 rbac/tree.py:52 msgid "Push account" msgstr "账号推送" @@ -195,7 +195,7 @@ msgstr "仅创建" #: authentication/serializers/password_mfa.py:24 #: notifications/backends/__init__.py:10 settings/serializers/msg.py:22 #: settings/serializers/msg.py:57 users/forms/profile.py:102 -#: users/forms/profile.py:109 users/models/user.py:795 +#: users/forms/profile.py:109 users/models/user.py:800 #: users/templates/users/forgot_password.html:117 #: users/views/profile/reset.py:92 msgid "Email" @@ -232,19 +232,19 @@ msgstr "导出搜素: %s" msgid "User %s view/export secret" msgstr "用户 %s 查看/导出 了密码" -#: accounts/models/account.py:48 +#: accounts/models/account.py:49 #: accounts/models/automations/gather_account.py:16 -#: accounts/serializers/account/account.py:210 -#: accounts/serializers/account/account.py:255 +#: accounts/serializers/account/account.py:213 +#: accounts/serializers/account/account.py:258 #: accounts/serializers/account/gathered_account.py:10 #: accounts/serializers/automations/change_secret.py:106 #: accounts/serializers/automations/change_secret.py:126 #: accounts/templates/accounts/asset_account_change_info.html:7 -#: acls/serializers/base.py:123 assets/models/asset/common.py:93 -#: assets/models/asset/common.py:342 assets/models/cmd_filter.py:36 -#: assets/serializers/domain.py:19 assets/serializers/label.py:27 -#: audits/models.py:58 authentication/models/connection_token.py:36 -#: perms/models/asset_permission.py:68 perms/serializers/permission.py:34 +#: acls/serializers/base.py:123 assets/models/asset/common.py:95 +#: assets/models/asset/common.py:350 assets/models/cmd_filter.py:36 +#: assets/serializers/domain.py:20 audits/models.py:58 +#: authentication/models/connection_token.py:36 +#: perms/models/asset_permission.py:69 perms/serializers/permission.py:35 #: terminal/backends/command/models.py:17 terminal/models/session/session.py:31 #: terminal/notifications.py:155 terminal/serializers/command.py:17 #: terminal/serializers/session.py:26 @@ -254,37 +254,37 @@ msgstr "用户 %s 查看/导出 了密码" msgid "Asset" msgstr "资产" -#: accounts/models/account.py:52 accounts/models/template.py:15 -#: accounts/serializers/account/account.py:217 -#: accounts/serializers/account/account.py:265 -#: accounts/serializers/account/template.py:24 +#: accounts/models/account.py:53 accounts/models/template.py:16 +#: accounts/serializers/account/account.py:220 +#: accounts/serializers/account/account.py:268 +#: accounts/serializers/account/template.py:25 #: authentication/serializers/connect_token_secret.py:49 msgid "Su from" msgstr "切换自" -#: accounts/models/account.py:54 assets/const/protocol.py:169 +#: accounts/models/account.py:55 assets/const/protocol.py:169 #: settings/serializers/auth/cas.py:20 settings/serializers/auth/feishu.py:20 #: terminal/models/applet/applet.py:35 msgid "Version" msgstr "版本" -#: accounts/models/account.py:56 accounts/serializers/account/account.py:212 -#: users/models/user.py:838 +#: accounts/models/account.py:57 accounts/serializers/account/account.py:215 +#: users/models/user.py:843 msgid "Source" msgstr "来源" -#: accounts/models/account.py:57 +#: accounts/models/account.py:58 msgid "Source ID" msgstr "来源 ID" -#: accounts/models/account.py:60 +#: accounts/models/account.py:61 #: accounts/serializers/automations/change_secret.py:107 #: accounts/serializers/automations/change_secret.py:127 #: acls/serializers/base.py:124 acls/templates/acls/asset_login_reminder.html:7 #: assets/serializers/asset/common.py:125 assets/serializers/gateway.py:28 #: audits/models.py:59 authentication/api/connection_token.py:403 -#: ops/models/base.py:18 perms/models/asset_permission.py:74 -#: perms/serializers/permission.py:39 terminal/backends/command/models.py:18 +#: ops/models/base.py:18 perms/models/asset_permission.py:75 +#: perms/serializers/permission.py:40 terminal/backends/command/models.py:18 #: terminal/models/session/session.py:33 #: terminal/templates/terminal/_msg_command_warning.html:8 #: terminal/templates/terminal/_msg_session_sharing.html:8 @@ -292,23 +292,23 @@ msgstr "来源 ID" msgid "Account" msgstr "账号" -#: accounts/models/account.py:66 +#: accounts/models/account.py:67 msgid "Can view asset account secret" msgstr "可以查看资产账号密码" -#: accounts/models/account.py:67 +#: accounts/models/account.py:68 msgid "Can view asset history account" msgstr "可以查看资产历史账号" -#: accounts/models/account.py:68 +#: accounts/models/account.py:69 msgid "Can view asset history account secret" msgstr "可以查看资产历史账号密码" -#: accounts/models/account.py:69 +#: accounts/models/account.py:70 msgid "Can verify account" msgstr "可以验证账号" -#: accounts/models/account.py:70 +#: accounts/models/account.py:71 msgid "Can push account" msgstr "可以推送账号" @@ -349,9 +349,9 @@ msgstr "账号备份计划" #: accounts/models/automations/backup_account.py:119 #: assets/models/automations/base.py:115 audits/models.py:65 -#: ops/models/base.py:55 ops/models/celery.py:63 ops/models/job.py:231 +#: ops/models/base.py:55 ops/models/celery.py:63 ops/models/job.py:240 #: ops/templates/ops/celery_task_log.html:75 -#: perms/models/asset_permission.py:77 terminal/models/applet/host.py:141 +#: perms/models/asset_permission.py:78 terminal/models/applet/host.py:141 #: terminal/models/session/session.py:44 #: tickets/models/ticket/apply_application.py:30 #: tickets/models/ticket/apply_asset.py:19 @@ -384,7 +384,7 @@ msgstr "原因" #: accounts/models/automations/backup_account.py:135 #: accounts/serializers/automations/change_secret.py:105 #: accounts/serializers/automations/change_secret.py:128 -#: ops/serializers/job.py:56 terminal/serializers/session.py:49 +#: ops/serializers/job.py:55 terminal/serializers/session.py:49 msgid "Is success" msgstr "是否成功" @@ -457,14 +457,14 @@ msgstr "开始日期" #: accounts/models/automations/change_secret.py:42 #: assets/models/automations/base.py:116 ops/models/base.py:56 -#: ops/models/celery.py:64 ops/models/job.py:232 +#: ops/models/celery.py:64 ops/models/job.py:241 #: terminal/models/applet/host.py:142 msgid "Date finished" msgstr "结束日期" #: accounts/models/automations/change_secret.py:43 #: assets/models/automations/base.py:113 audits/models.py:208 -#: audits/serializers.py:54 ops/models/base.py:49 ops/models/job.py:223 +#: audits/serializers.py:54 ops/models/base.py:49 ops/models/job.py:232 #: terminal/models/applet/applet.py:318 terminal/models/applet/host.py:140 #: terminal/models/component/status.py:30 terminal/serializers/applet.py:18 #: terminal/serializers/applet_host.py:124 tickets/models/ticket/general.py:283 @@ -475,10 +475,10 @@ msgid "Status" msgstr "状态" #: accounts/models/automations/change_secret.py:44 -#: accounts/serializers/account/account.py:257 assets/const/automation.py:8 +#: accounts/serializers/account/account.py:260 assets/const/automation.py:8 #: authentication/templates/authentication/passkey.html:173 -#: authentication/views/base.py:27 authentication/views/base.py:28 -#: authentication/views/base.py:29 common/const/choices.py:20 +#: authentication/views/base.py:42 authentication/views/base.py:43 +#: authentication/views/base.py:44 common/const/choices.py:20 msgid "Error" msgstr "错误" @@ -504,7 +504,7 @@ msgstr "最后登录日期" #: authentication/templates/authentication/_msg_different_city.html:9 #: authentication/templates/authentication/_msg_oauth_bind.html:9 #: terminal/serializers/storage.py:136 users/forms/profile.py:32 -#: users/forms/profile.py:115 users/models/user.py:791 +#: users/forms/profile.py:115 users/models/user.py:796 #: users/templates/users/_msg_user_created.html:12 #: xpack/plugins/cloud/serializers/account_attrs.py:26 msgid "Username" @@ -548,8 +548,8 @@ msgid "Verify asset account" msgstr "账号验证" #: accounts/models/base.py:37 accounts/models/base.py:67 -#: accounts/serializers/account/account.py:437 -#: accounts/serializers/account/base.py:16 +#: accounts/serializers/account/account.py:440 +#: accounts/serializers/account/base.py:17 #: accounts/serializers/automations/change_secret.py:45 #: authentication/serializers/connect_token_secret.py:41 #: authentication/serializers/connect_token_secret.py:50 @@ -558,7 +558,7 @@ msgid "Secret type" msgstr "密文类型" #: accounts/models/base.py:39 accounts/models/mixins/vault.py:49 -#: accounts/serializers/account/base.py:19 +#: accounts/serializers/account/base.py:20 #: authentication/models/temp_token.py:10 #: authentication/templates/authentication/_access_key_modal.html:31 #: settings/serializers/auth/radius.py:19 @@ -570,7 +570,7 @@ msgstr "密钥" msgid "Secret strategy" msgstr "密文策略" -#: accounts/models/base.py:44 accounts/serializers/account/template.py:21 +#: accounts/models/base.py:44 accounts/serializers/account/template.py:22 #: accounts/serializers/automations/change_secret.py:44 msgid "Password rules" msgstr "密码规则" @@ -578,24 +578,25 @@ msgstr "密码规则" #: accounts/models/base.py:64 accounts/serializers/account/virtual.py:20 #: acls/models/base.py:35 acls/models/base.py:96 acls/models/command_acl.py:21 #: acls/serializers/base.py:35 applications/models.py:9 -#: assets/models/_user.py:22 assets/models/asset/common.py:91 -#: assets/models/asset/common.py:157 assets/models/cmd_filter.py:21 -#: assets/models/domain.py:18 assets/models/group.py:17 -#: assets/models/label.py:18 assets/models/platform.py:15 -#: assets/models/platform.py:88 assets/serializers/asset/common.py:146 +#: assets/models/_user.py:22 assets/models/asset/common.py:93 +#: assets/models/asset/common.py:159 assets/models/cmd_filter.py:21 +#: assets/models/domain.py:19 assets/models/group.py:17 +#: assets/models/label.py:18 assets/models/platform.py:16 +#: assets/models/platform.py:89 assets/serializers/asset/common.py:146 #: assets/serializers/platform.py:118 assets/serializers/platform.py:235 #: authentication/backends/passkey/models.py:10 -#: authentication/serializers/connect_token_secret.py:112 ops/mixin.py:21 -#: ops/models/adhoc.py:20 ops/models/celery.py:15 ops/models/celery.py:57 -#: ops/models/job.py:128 ops/models/playbook.py:28 ops/serializers/job.py:20 -#: orgs/models.py:82 perms/models/asset_permission.py:60 rbac/models/role.py:29 +#: authentication/serializers/connect_token_secret.py:112 labels/models.py:11 +#: ops/mixin.py:21 ops/models/adhoc.py:20 ops/models/celery.py:15 +#: ops/models/celery.py:57 ops/models/job.py:137 ops/models/playbook.py:29 +#: ops/serializers/job.py:19 orgs/models.py:82 +#: perms/models/asset_permission.py:61 rbac/models/role.py:29 #: settings/models.py:32 settings/serializers/msg.py:82 #: terminal/models/applet/applet.py:33 terminal/models/component/endpoint.py:12 #: terminal/models/component/endpoint.py:94 #: terminal/models/component/storage.py:26 terminal/models/component/task.py:13 #: terminal/models/component/terminal.py:84 tickets/api/ticket.py:87 #: users/forms/profile.py:33 users/models/group.py:13 -#: users/models/preference.py:11 users/models/user.py:793 +#: users/models/preference.py:11 users/models/user.py:798 #: xpack/plugins/cloud/models.py:32 xpack/plugins/cloud/models.py:273 #: xpack/plugins/cloud/serializers/task.py:68 msgid "Name" @@ -605,7 +606,7 @@ msgstr "名称" msgid "Privileged" msgstr "特权账号" -#: accounts/models/base.py:70 assets/models/asset/common.py:164 +#: accounts/models/base.py:70 assets/models/asset/common.py:166 #: assets/models/automations/base.py:21 assets/models/cmd_filter.py:39 #: assets/models/label.py:22 #: authentication/serializers/connect_token_secret.py:116 @@ -614,27 +615,27 @@ msgstr "特权账号" msgid "Is active" msgstr "激活" -#: accounts/models/template.py:17 assets/models/_user.py:53 +#: accounts/models/template.py:18 assets/models/_user.py:53 msgid "Auto push" msgstr "自动推送" -#: accounts/models/template.py:20 +#: accounts/models/template.py:21 msgid "Platforms" msgstr "系统平台" -#: accounts/models/template.py:22 +#: accounts/models/template.py:23 msgid "Push params" msgstr "账号推送参数" -#: accounts/models/template.py:25 xpack/plugins/cloud/models.py:325 +#: accounts/models/template.py:26 xpack/plugins/cloud/models.py:325 msgid "Account template" msgstr "账号模版" -#: accounts/models/template.py:30 +#: accounts/models/template.py:31 msgid "Can view asset account template secret" msgstr "可以查看资产账号模版密码" -#: accounts/models/template.py:31 +#: accounts/models/template.py:32 msgid "Can change asset account template secret" msgstr "可以更改资产账号模版密码" @@ -719,24 +720,24 @@ msgstr "立即推送" msgid "Exist policy" msgstr "账号存在策略" -#: accounts/serializers/account/account.py:190 applications/models.py:11 -#: assets/models/label.py:21 assets/models/platform.py:89 -#: assets/serializers/asset/common.py:121 assets/serializers/cagegory.py:12 +#: accounts/serializers/account/account.py:193 applications/models.py:11 +#: assets/models/label.py:21 assets/models/platform.py:90 +#: assets/serializers/asset/common.py:122 assets/serializers/cagegory.py:12 #: assets/serializers/platform.py:140 assets/serializers/platform.py:236 #: perms/serializers/user_permission.py:25 settings/models.py:34 #: tickets/models/ticket/apply_application.py:13 users/models/preference.py:12 msgid "Category" msgstr "类别" -#: accounts/serializers/account/account.py:191 +#: accounts/serializers/account/account.py:194 #: accounts/serializers/automations/base.py:54 acls/models/command_acl.py:24 #: acls/serializers/command_acl.py:19 applications/models.py:14 #: assets/models/_user.py:50 assets/models/automations/base.py:20 -#: assets/models/cmd_filter.py:74 assets/models/platform.py:90 -#: assets/serializers/asset/common.py:122 assets/serializers/platform.py:120 +#: assets/models/cmd_filter.py:74 assets/models/platform.py:91 +#: assets/serializers/asset/common.py:123 assets/serializers/platform.py:120 #: assets/serializers/platform.py:139 audits/serializers.py:53 #: audits/serializers.py:173 -#: authentication/serializers/connect_token_secret.py:125 ops/models/job.py:140 +#: authentication/serializers/connect_token_secret.py:125 ops/models/job.py:149 #: perms/serializers/user_permission.py:26 terminal/models/applet/applet.py:39 #: terminal/models/component/storage.py:57 #: terminal/models/component/storage.py:146 terminal/serializers/applet.py:29 @@ -748,76 +749,76 @@ msgstr "类别" msgid "Type" msgstr "类型" -#: accounts/serializers/account/account.py:206 +#: accounts/serializers/account/account.py:209 msgid "Asset not found" msgstr "资产不存在" -#: accounts/serializers/account/account.py:246 +#: accounts/serializers/account/account.py:249 msgid "Has secret" msgstr "已托管密码" -#: accounts/serializers/account/account.py:256 ops/models/celery.py:60 +#: accounts/serializers/account/account.py:259 ops/models/celery.py:60 #: tickets/models/comment.py:13 tickets/models/ticket/general.py:45 #: tickets/models/ticket/general.py:279 tickets/serializers/super_ticket.py:14 #: tickets/serializers/ticket/ticket.py:21 msgid "State" msgstr "状态" -#: accounts/serializers/account/account.py:258 +#: accounts/serializers/account/account.py:261 msgid "Changed" msgstr "已修改" -#: accounts/serializers/account/account.py:268 +#: accounts/serializers/account/account.py:271 #: accounts/serializers/automations/base.py:22 acls/models/base.py:97 #: acls/templates/acls/asset_login_reminder.html:6 #: assets/models/automations/base.py:19 #: assets/serializers/automations/base.py:20 #: authentication/api/connection_token.py:402 ops/models/base.py:17 -#: ops/models/job.py:142 ops/serializers/job.py:21 +#: ops/models/job.py:151 ops/serializers/job.py:20 #: terminal/templates/terminal/_msg_command_execute_alert.html:16 msgid "Assets" msgstr "资产" -#: accounts/serializers/account/account.py:323 +#: accounts/serializers/account/account.py:326 msgid "Account already exists" msgstr "账号已存在" -#: accounts/serializers/account/account.py:373 +#: accounts/serializers/account/account.py:376 #, python-format msgid "Asset does not support this secret type: %s" msgstr "资产不支持账号类型: %s" -#: accounts/serializers/account/account.py:405 +#: accounts/serializers/account/account.py:408 msgid "Account has exist" msgstr "账号已存在" -#: accounts/serializers/account/account.py:438 +#: accounts/serializers/account/account.py:441 #: authentication/serializers/connect_token_secret.py:158 #: authentication/templates/authentication/_access_key_modal.html:30 -#: perms/models/perm_node.py:21 users/serializers/group.py:31 +#: perms/models/perm_node.py:21 users/serializers/group.py:32 msgid "ID" msgstr "ID" -#: accounts/serializers/account/account.py:448 acls/serializers/base.py:116 +#: accounts/serializers/account/account.py:451 acls/serializers/base.py:116 #: assets/models/cmd_filter.py:24 assets/models/label.py:16 audits/models.py:54 #: audits/models.py:90 audits/models.py:172 audits/models.py:269 #: audits/serializers.py:174 authentication/models/connection_token.py:32 #: authentication/models/sso_token.py:16 #: notifications/models/notification.py:12 -#: perms/api/user_permission/mixin.py:55 perms/models/asset_permission.py:62 -#: perms/serializers/permission.py:30 rbac/builtin.py:124 +#: perms/api/user_permission/mixin.py:55 perms/models/asset_permission.py:63 +#: perms/serializers/permission.py:31 rbac/builtin.py:124 #: rbac/models/rolebinding.py:49 rbac/serializers/rolebinding.py:17 #: terminal/backends/command/models.py:16 terminal/models/session/session.py:29 #: terminal/models/session/sharing.py:34 terminal/notifications.py:156 #: terminal/notifications.py:205 terminal/serializers/command.py:16 #: terminal/templates/terminal/_msg_command_warning.html:6 #: terminal/templates/terminal/_msg_session_sharing.html:6 -#: tickets/models/comment.py:21 users/const.py:14 users/models/user.py:988 -#: users/models/user.py:1024 users/serializers/group.py:18 +#: tickets/models/comment.py:21 users/const.py:14 users/models/user.py:994 +#: users/models/user.py:1031 users/serializers/group.py:19 msgid "User" msgstr "用户" -#: accounts/serializers/account/account.py:449 +#: accounts/serializers/account/account.py:452 #: authentication/templates/authentication/_access_key_modal.html:33 #: terminal/notifications.py:158 terminal/notifications.py:207 msgid "Date" @@ -844,12 +845,12 @@ msgstr "当前只支持邮件发送" msgid "Asset type" msgstr "资产类型" -#: accounts/serializers/account/base.py:24 terminal/serializers/storage.py:149 +#: accounts/serializers/account/base.py:25 terminal/serializers/storage.py:149 msgid "Key password" msgstr "密钥密码" #: accounts/serializers/account/base.py:78 -#: assets/serializers/asset/common.py:378 +#: assets/serializers/asset/common.py:379 msgid "Spec info" msgstr "特殊信息" @@ -881,15 +882,21 @@ msgstr "数字" msgid "Special symbol" msgstr "特殊字符" -#: accounts/serializers/account/template.py:35 +#: accounts/serializers/account/template.py:18 +#, fuzzy +#| msgid "Special symbol" +msgid "Exclude symbol" +msgstr "特殊字符" + +#: accounts/serializers/account/template.py:36 msgid "Secret generation strategy for account creation" msgstr "密码生成策略,用于账号创建时,设置密码" -#: accounts/serializers/account/template.py:36 +#: accounts/serializers/account/template.py:37 msgid "Whether to automatically push the account to the asset" msgstr "是否自动推送账号到资产" -#: accounts/serializers/account/template.py:39 +#: accounts/serializers/account/template.py:40 msgid "" "Associated platform, you can configure push parameters. If not associated, " "default parameters will be used" @@ -898,13 +905,13 @@ msgstr "关联平台,可配置推送参数,如果不关联,将使用默认 #: accounts/serializers/account/virtual.py:19 assets/models/_user.py:27 #: assets/models/cmd_filter.py:40 assets/models/cmd_filter.py:88 #: assets/models/group.py:20 common/db/models.py:36 ops/models/adhoc.py:26 -#: ops/models/job.py:148 ops/models/playbook.py:31 rbac/models/role.py:37 +#: ops/models/job.py:157 ops/models/playbook.py:32 rbac/models/role.py:37 #: settings/models.py:37 terminal/models/applet/applet.py:45 #: terminal/models/applet/applet.py:319 terminal/models/applet/host.py:143 #: terminal/models/component/endpoint.py:24 #: terminal/models/component/endpoint.py:104 #: terminal/models/session/session.py:46 tickets/models/comment.py:32 -#: tickets/models/ticket/general.py:297 users/models/user.py:829 +#: tickets/models/ticket/general.py:297 users/models/user.py:834 #: xpack/plugins/cloud/models.py:39 xpack/plugins/cloud/models.py:109 msgid "Comment" msgstr "备注" @@ -920,9 +927,9 @@ msgstr "" "CACHE_LOGIN_PASSWORD_ENABLED=true,重启服务才能开启" #: accounts/serializers/automations/base.py:23 -#: assets/models/asset/common.py:163 assets/models/automations/base.py:18 +#: assets/models/asset/common.py:165 assets/models/automations/base.py:18 #: assets/models/cmd_filter.py:32 assets/serializers/automations/base.py:21 -#: perms/models/asset_permission.py:71 +#: perms/models/asset_permission.py:72 msgid "Nodes" msgstr "节点" @@ -1020,15 +1027,15 @@ msgstr "新增账号" msgid "Deleted account" msgstr "删除账号" -#: accounts/utils.py:53 +#: accounts/utils.py:54 msgid "Password can not contains `{{` or `}}`" msgstr "密码不能包含 `{{` 或 `}}` 字符" -#: accounts/utils.py:55 +#: accounts/utils.py:56 msgid "Password can not contains `{%` or `%}`" msgstr "密码不能包含 `{%` 或 `%}` 字符" -#: accounts/utils.py:66 +#: accounts/utils.py:67 msgid "private key invalid or passphrase error" msgstr "密钥不合法或密钥密码错误" @@ -1037,7 +1044,7 @@ msgid "Acls" msgstr "访问控制" #: acls/const.py:6 audits/const.py:36 terminal/const.py:11 tickets/const.py:45 -#: tickets/templates/tickets/approve_check_password.html:48 +#: tickets/templates/tickets/approve_check_password.html:47 msgid "Reject" msgstr "拒绝" @@ -1077,7 +1084,7 @@ msgstr "审批人" #: acls/models/base.py:43 authentication/models/access_key.py:25 #: authentication/models/connection_token.py:53 #: authentication/templates/authentication/_access_key_modal.html:32 -#: perms/models/asset_permission.py:81 terminal/models/session/sharing.py:29 +#: perms/models/asset_permission.py:82 terminal/models/session/sharing.py:29 #: tickets/const.py:37 msgid "Active" msgstr "激活中" @@ -1087,13 +1094,13 @@ msgid "Users" msgstr "用户管理" #: acls/models/base.py:98 assets/models/automations/base.py:17 -#: assets/models/cmd_filter.py:38 assets/serializers/asset/common.py:377 +#: assets/models/cmd_filter.py:38 assets/serializers/asset/common.py:378 #: perms/serializers/user_permission.py:75 rbac/tree.py:35 msgid "Accounts" msgstr "账号管理" #: acls/models/command_acl.py:16 assets/models/cmd_filter.py:60 -#: ops/serializers/job.py:55 terminal/const.py:85 +#: ops/serializers/job.py:54 terminal/const.py:85 #: terminal/models/session/session.py:42 terminal/serializers/command.py:18 #: terminal/templates/terminal/_msg_command_alert.html:12 #: terminal/templates/terminal/_msg_command_execute_alert.html:10 @@ -1287,7 +1294,7 @@ msgstr "应用程序" msgid "Can match application" msgstr "匹配应用" -#: assets/api/asset/asset.py:194 +#: assets/api/asset/asset.py:182 msgid "Cannot create asset directly, you should create a host or other" msgstr "不能直接创建资产, 你应该创建主机或其他资产" @@ -1315,11 +1322,11 @@ msgstr "同级别节点名字不能重复" msgid "App assets" msgstr "资产管理" -#: assets/automations/base/manager.py:133 +#: assets/automations/base/manager.py:187 msgid "{} disabled" msgstr "{} 已禁用" -#: assets/automations/base/manager.py:199 +#: assets/automations/base/manager.py:253 msgid " - Platform {} ansible disabled" msgstr " - 平台 {} Ansible 已禁用, 无法执行任务" @@ -1590,19 +1597,19 @@ msgstr "SSH公钥" #: assets/models/_user.py:28 assets/models/automations/base.py:114 #: assets/models/cmd_filter.py:41 assets/models/group.py:19 #: audits/models.py:267 common/db/models.py:34 ops/models/base.py:54 -#: ops/models/job.py:230 users/models/user.py:1025 +#: ops/models/job.py:239 users/models/user.py:1032 msgid "Date created" msgstr "创建日期" #: assets/models/_user.py:29 assets/models/cmd_filter.py:42 -#: common/db/models.py:35 users/models/user.py:847 +#: common/db/models.py:35 users/models/user.py:852 msgid "Date updated" msgstr "更新日期" #: assets/models/_user.py:30 assets/models/cmd_filter.py:44 #: assets/models/cmd_filter.py:91 assets/models/group.py:18 -#: common/db/models.py:32 users/models/user.py:836 -#: users/serializers/group.py:29 +#: common/db/models.py:32 users/models/user.py:841 +#: users/serializers/group.py:30 msgid "Created by" msgstr "创建者" @@ -1677,56 +1684,52 @@ msgstr "可以匹配系统用户" msgid "Cloud" msgstr "云服务" -#: assets/models/asset/common.py:92 assets/models/platform.py:16 +#: assets/models/asset/common.py:94 assets/models/platform.py:17 #: settings/serializers/auth/radius.py:17 settings/serializers/auth/sms.py:72 #: terminal/serializers/storage.py:133 #: xpack/plugins/cloud/serializers/account_attrs.py:73 msgid "Port" msgstr "端口" -#: assets/models/asset/common.py:158 assets/serializers/asset/common.py:147 +#: assets/models/asset/common.py:160 assets/serializers/asset/common.py:147 msgid "Address" msgstr "地址" -#: assets/models/asset/common.py:159 assets/models/platform.py:119 +#: assets/models/asset/common.py:161 assets/models/platform.py:120 #: authentication/backends/passkey/models.py:12 #: authentication/serializers/connect_token_secret.py:117 #: perms/serializers/user_permission.py:24 xpack/plugins/cloud/models.py:321 msgid "Platform" msgstr "系统平台" -#: assets/models/asset/common.py:161 assets/models/domain.py:21 +#: assets/models/asset/common.py:163 assets/models/domain.py:22 #: authentication/serializers/connect_token_secret.py:135 #: perms/serializers/user_permission.py:28 xpack/plugins/cloud/models.py:323 msgid "Domain" msgstr "网域" -#: assets/models/asset/common.py:165 -msgid "Labels" -msgstr "标签管理" - -#: assets/models/asset/common.py:166 assets/serializers/asset/common.py:379 +#: assets/models/asset/common.py:167 assets/serializers/asset/common.py:380 #: assets/serializers/asset/host.py:11 msgid "Gathered info" msgstr "收集资产硬件信息" -#: assets/models/asset/common.py:167 assets/serializers/asset/custom.py:14 +#: assets/models/asset/common.py:168 assets/serializers/asset/custom.py:14 msgid "Custom info" msgstr "自定义属性" -#: assets/models/asset/common.py:345 +#: assets/models/asset/common.py:353 msgid "Can refresh asset hardware info" msgstr "可以更新资产硬件信息" -#: assets/models/asset/common.py:346 +#: assets/models/asset/common.py:354 msgid "Can test asset connectivity" msgstr "可以测试资产连接性" -#: assets/models/asset/common.py:347 +#: assets/models/asset/common.py:355 msgid "Can match asset" msgstr "可以匹配资产" -#: assets/models/asset/common.py:348 +#: assets/models/asset/common.py:356 msgid "Can change asset nodes" msgstr "可以修改资产节点" @@ -1754,7 +1757,7 @@ msgstr "忽略证书校验" msgid "Proxy" msgstr "代理" -#: assets/models/automations/base.py:22 ops/models/job.py:226 +#: assets/models/automations/base.py:22 ops/models/job.py:235 #: settings/serializers/auth/sms.py:103 msgid "Parameters" msgstr "参数" @@ -1783,9 +1786,9 @@ msgstr "可连接性" msgid "Date verified" msgstr "校验日期" -#: assets/models/cmd_filter.py:28 perms/models/asset_permission.py:65 -#: perms/serializers/permission.py:32 users/models/group.py:25 -#: users/models/user.py:799 +#: assets/models/cmd_filter.py:28 perms/models/asset_permission.py:66 +#: perms/serializers/permission.py:33 users/models/group.py:25 +#: users/models/user.py:804 msgid "User group" msgstr "用户组" @@ -1817,7 +1820,7 @@ msgstr "命令过滤规则" msgid "Favorite asset" msgstr "收藏的资产" -#: assets/models/gateway.py:34 assets/serializers/domain.py:16 +#: assets/models/gateway.py:34 assets/serializers/domain.py:17 msgid "Gateway" msgstr "网关" @@ -1825,7 +1828,7 @@ msgstr "网关" msgid "Asset group" msgstr "资产组" -#: assets/models/group.py:31 assets/models/platform.py:19 +#: assets/models/group.py:31 assets/models/platform.py:20 #: assets/serializers/platform.py:121 #: xpack/plugins/cloud/providers/nutanix.py:30 msgid "Default" @@ -1835,7 +1838,7 @@ msgstr "默认" msgid "Default asset group" msgstr "默认资产组" -#: assets/models/label.py:15 rbac/const.py:6 users/models/user.py:1010 +#: assets/models/label.py:15 rbac/const.py:6 users/models/user.py:1017 msgid "System" msgstr "系统" @@ -1844,17 +1847,17 @@ msgstr "系统" #: assets/serializers/cagegory.py:24 #: authentication/models/connection_token.py:29 #: authentication/serializers/connect_token_secret.py:124 -#: common/serializers/common.py:86 settings/models.py:33 +#: common/serializers/common.py:86 labels/models.py:12 settings/models.py:33 #: users/models/preference.py:13 msgid "Value" msgstr "值" -#: assets/models/label.py:40 assets/serializers/asset/common.py:123 -#: assets/serializers/cagegory.py:10 assets/serializers/cagegory.py:17 -#: assets/serializers/cagegory.py:23 assets/serializers/platform.py:119 +#: assets/models/label.py:40 assets/serializers/cagegory.py:10 +#: assets/serializers/cagegory.py:17 assets/serializers/cagegory.py:23 +#: assets/serializers/platform.py:119 #: authentication/serializers/connect_token_secret.py:123 -#: common/serializers/common.py:85 perms/serializers/user_permission.py:27 -#: settings/serializers/msg.py:83 +#: common/serializers/common.py:85 labels/models.py:17 +#: perms/serializers/user_permission.py:27 settings/serializers/msg.py:83 msgid "Label" msgstr "标签" @@ -1878,7 +1881,7 @@ msgstr "全称" msgid "Parent key" msgstr "ssh私钥" -#: assets/models/node.py:553 perms/serializers/permission.py:35 +#: assets/models/node.py:553 perms/serializers/permission.py:36 #: tickets/models/ticket/apply_asset.py:14 xpack/plugins/cloud/models.py:322 msgid "Node" msgstr "节点" @@ -1887,121 +1890,121 @@ msgstr "节点" msgid "Can match node" msgstr "可以匹配节点" -#: assets/models/platform.py:17 +#: assets/models/platform.py:18 msgid "Primary" msgstr "主要的" -#: assets/models/platform.py:18 +#: assets/models/platform.py:19 msgid "Required" msgstr "必须的" -#: assets/models/platform.py:20 +#: assets/models/platform.py:21 msgid "Public" msgstr "开放的" -#: assets/models/platform.py:21 assets/serializers/platform.py:49 +#: assets/models/platform.py:22 assets/serializers/platform.py:49 #: settings/serializers/settings.py:66 #: users/templates/users/reset_password.html:29 msgid "Setting" msgstr "设置" -#: assets/models/platform.py:38 audits/const.py:56 +#: assets/models/platform.py:39 audits/const.py:56 #: authentication/backends/passkey/models.py:11 settings/models.py:36 #: terminal/serializers/applet_host.py:33 msgid "Enabled" msgstr "启用" -#: assets/models/platform.py:39 +#: assets/models/platform.py:40 msgid "Ansible config" msgstr "Ansible 配置" -#: assets/models/platform.py:41 assets/serializers/platform.py:33 +#: assets/models/platform.py:42 assets/serializers/platform.py:33 msgid "Ping enabled" msgstr "启用资产探活" -#: assets/models/platform.py:42 assets/serializers/platform.py:34 +#: assets/models/platform.py:43 assets/serializers/platform.py:34 msgid "Ping method" msgstr "资产探活方式" -#: assets/models/platform.py:43 +#: assets/models/platform.py:44 msgid "Ping params" msgstr "资产探活参数" -#: assets/models/platform.py:45 assets/models/platform.py:69 +#: assets/models/platform.py:46 assets/models/platform.py:70 #: assets/serializers/platform.py:35 msgid "Gather facts enabled" msgstr "启用收集资产信息" -#: assets/models/platform.py:47 assets/models/platform.py:71 +#: assets/models/platform.py:48 assets/models/platform.py:72 #: assets/serializers/platform.py:36 msgid "Gather facts method" msgstr "收集信息方式" -#: assets/models/platform.py:49 assets/models/platform.py:73 +#: assets/models/platform.py:50 assets/models/platform.py:74 msgid "Gather facts params" msgstr "收集信息参数" -#: assets/models/platform.py:51 assets/serializers/platform.py:39 +#: assets/models/platform.py:52 assets/serializers/platform.py:39 msgid "Change secret enabled" msgstr "启用改密" -#: assets/models/platform.py:53 assets/serializers/platform.py:40 +#: assets/models/platform.py:54 assets/serializers/platform.py:40 msgid "Change secret method" msgstr "改密方式" -#: assets/models/platform.py:55 +#: assets/models/platform.py:56 msgid "Change secret params" msgstr "改密参数" -#: assets/models/platform.py:57 assets/serializers/platform.py:41 +#: assets/models/platform.py:58 assets/serializers/platform.py:41 msgid "Push account enabled" msgstr "启用账号推送" -#: assets/models/platform.py:59 assets/serializers/platform.py:42 +#: assets/models/platform.py:60 assets/serializers/platform.py:42 msgid "Push account method" msgstr "账号推送方式" -#: assets/models/platform.py:61 +#: assets/models/platform.py:62 msgid "Push account params" msgstr "账号推送参数" -#: assets/models/platform.py:63 assets/serializers/platform.py:37 +#: assets/models/platform.py:64 assets/serializers/platform.py:37 msgid "Verify account enabled" msgstr "开启账号验证" -#: assets/models/platform.py:65 assets/serializers/platform.py:38 +#: assets/models/platform.py:66 assets/serializers/platform.py:38 msgid "Verify account method" msgstr "账号验证方式" -#: assets/models/platform.py:67 +#: assets/models/platform.py:68 msgid "Verify account params" msgstr "账号验证参数" -#: assets/models/platform.py:91 tickets/models/ticket/general.py:300 +#: assets/models/platform.py:92 tickets/models/ticket/general.py:300 msgid "Meta" msgstr "元数据" -#: assets/models/platform.py:92 +#: assets/models/platform.py:93 labels/models.py:13 msgid "Internal" msgstr "内置" -#: assets/models/platform.py:96 assets/serializers/platform.py:138 +#: assets/models/platform.py:97 assets/serializers/platform.py:138 msgid "Charset" msgstr "编码" -#: assets/models/platform.py:98 assets/serializers/platform.py:166 +#: assets/models/platform.py:99 assets/serializers/platform.py:167 msgid "Domain enabled" msgstr "启用网域" -#: assets/models/platform.py:100 assets/serializers/platform.py:165 +#: assets/models/platform.py:101 assets/serializers/platform.py:166 msgid "Su enabled" msgstr "启用账号切换" -#: assets/models/platform.py:101 assets/serializers/platform.py:144 +#: assets/models/platform.py:102 assets/serializers/platform.py:144 msgid "Su method" msgstr "账号切换方式" -#: assets/models/platform.py:102 assets/serializers/platform.py:147 +#: assets/models/platform.py:103 assets/serializers/platform.py:147 msgid "Custom fields" msgstr "自定义属性" @@ -2019,7 +2022,7 @@ msgstr "资产中批量更新平台,不符合平台类型跳过的资产" #: assets/serializers/asset/common.py:124 assets/serializers/platform.py:141 #: authentication/serializers/connect_token_secret.py:29 #: authentication/serializers/connect_token_secret.py:74 -#: perms/models/asset_permission.py:75 perms/serializers/permission.py:40 +#: perms/models/asset_permission.py:76 perms/serializers/permission.py:41 #: perms/serializers/user_permission.py:74 xpack/plugins/cloud/models.py:324 #: xpack/plugins/cloud/serializers/task.py:31 msgid "Protocols" @@ -2031,23 +2034,23 @@ msgid "Node path" msgstr "节点路径" #: assets/serializers/asset/common.py:145 -#: assets/serializers/asset/common.py:380 +#: assets/serializers/asset/common.py:381 msgid "Auto info" msgstr "自动化信息" -#: assets/serializers/asset/common.py:238 +#: assets/serializers/asset/common.py:239 msgid "Platform not exist" msgstr "平台不存在" -#: assets/serializers/asset/common.py:274 +#: assets/serializers/asset/common.py:275 msgid "port out of range (0-65535)" msgstr "端口超出范围 (0-65535)" -#: assets/serializers/asset/common.py:281 +#: assets/serializers/asset/common.py:282 msgid "Protocol is required: {}" msgstr "协议是必填的: {}" -#: assets/serializers/asset/common.py:309 +#: assets/serializers/asset/common.py:310 msgid "Invalid data" msgstr "无效的数据" @@ -2133,10 +2136,6 @@ msgstr "类型" msgid "This field must be unique." msgstr "字段必须唯一" -#: assets/serializers/label.py:12 -msgid "Assets amount" -msgstr "资产数量" - #: assets/serializers/node.py:17 msgid "value" msgstr "值" @@ -2189,11 +2188,11 @@ msgstr "选择" msgid "Automation" msgstr "自动化" -#: assets/serializers/platform.py:167 +#: assets/serializers/platform.py:168 msgid "Default Domain" msgstr "默认网域" -#: assets/serializers/platform.py:188 +#: assets/serializers/platform.py:189 msgid "type is required" msgstr "类型 该字段是必填项。" @@ -2289,7 +2288,7 @@ msgstr "删除目录" #: audits/const.py:14 audits/const.py:25 #: authentication/templates/authentication/_access_key_modal.html:65 -#: rbac/tree.py:238 +#: rbac/tree.py:240 msgid "Delete" msgstr "删除" @@ -2313,7 +2312,7 @@ msgstr "下载" msgid "Rename dir" msgstr "映射目录" -#: audits/const.py:23 rbac/tree.py:236 terminal/api/session/session.py:257 +#: audits/const.py:23 rbac/tree.py:238 terminal/api/session/session.py:257 #: terminal/templates/terminal/_msg_command_warning.html:18 #: terminal/templates/terminal/_msg_session_sharing.html:10 msgid "View" @@ -2321,7 +2320,7 @@ msgstr "查看" #: audits/const.py:26 #: authentication/templates/authentication/_access_key_modal.html:22 -#: rbac/tree.py:235 +#: rbac/tree.py:237 msgid "Create" msgstr "创建" @@ -2424,7 +2423,7 @@ msgid "Resource Type" msgstr "资源类型" #: audits/models.py:95 audits/models.py:98 audits/models.py:144 -#: audits/serializers.py:88 +#: audits/serializers.py:88 labels/serializers.py:34 msgid "Resource" msgstr "资源" @@ -2468,7 +2467,7 @@ msgstr "登录 IP" #: audits/models.py:200 audits/serializers.py:52 #: authentication/templates/authentication/_mfa_confirm_modal.html:14 -#: users/forms/profile.py:65 users/models/user.py:816 +#: users/forms/profile.py:65 users/models/user.py:821 #: users/serializers/profile.py:102 msgid "MFA" msgstr "MFA" @@ -2499,8 +2498,8 @@ msgid "Offline ussr session" msgstr "下限用户会话" #: audits/serializers.py:33 ops/models/adhoc.py:25 ops/models/base.py:16 -#: ops/models/base.py:53 ops/models/job.py:141 ops/models/job.py:229 -#: ops/models/playbook.py:30 terminal/models/session/sharing.py:25 +#: ops/models/base.py:53 ops/models/job.py:150 ops/models/job.py:238 +#: ops/models/playbook.py:31 terminal/models/session/sharing.py:25 msgid "Creator" msgstr "创建者" @@ -2514,9 +2513,9 @@ msgid "User %s %s this resource" msgstr "用户 %s %s 了当前资源" #: audits/serializers.py:175 authentication/models/connection_token.py:47 -#: authentication/models/temp_token.py:13 perms/models/asset_permission.py:79 +#: authentication/models/temp_token.py:13 perms/models/asset_permission.py:80 #: tickets/models/ticket/apply_application.py:31 -#: tickets/models/ticket/apply_asset.py:20 users/models/user.py:834 +#: tickets/models/ticket/apply_asset.py:20 users/models/user.py:839 msgid "Date expired" msgstr "失效日期" @@ -2548,33 +2547,40 @@ msgid "Auth Token" msgstr "认证令牌" #: audits/signal_handlers/login_log.py:37 authentication/notifications.py:73 -#: authentication/views/login.py:77 authentication/views/wecom.py:160 -#: notifications/backends/__init__.py:11 settings/serializers/auth/wecom.py:10 -#: users/models/user.py:746 users/models/user.py:848 +#: authentication/views/login.py:77 notifications/backends/__init__.py:11 +#: settings/serializers/auth/wecom.py:10 users/models/user.py:747 +#: users/models/user.py:853 msgid "WeCom" msgstr "企业微信" -#: audits/signal_handlers/login_log.py:38 authentication/views/feishu.py:123 +#: audits/signal_handlers/login_log.py:38 authentication/views/feishu.py:87 #: authentication/views/login.py:89 notifications/backends/__init__.py:14 #: settings/serializers/auth/feishu.py:10 -#: settings/serializers/auth/feishu.py:13 users/models/user.py:748 -#: users/models/user.py:850 +#: settings/serializers/auth/feishu.py:13 users/models/user.py:749 +#: users/models/user.py:855 msgid "FeiShu" msgstr "飞书" -#: audits/signal_handlers/login_log.py:39 authentication/views/dingtalk.py:160 +#: audits/signal_handlers/login_log.py:39 authentication/views/login.py:95 +#: authentication/views/slack.py:87 notifications/backends/__init__.py:15 +#: settings/serializers/auth/slack.py:10 users/models/user.py:750 +#: users/models/user.py:856 +msgid "Slack" +msgstr "" + +#: audits/signal_handlers/login_log.py:40 authentication/views/dingtalk.py:160 #: authentication/views/login.py:83 notifications/backends/__init__.py:12 -#: settings/serializers/auth/dingtalk.py:10 users/models/user.py:747 -#: users/models/user.py:849 +#: settings/serializers/auth/dingtalk.py:10 users/models/user.py:748 +#: users/models/user.py:854 msgid "DingTalk" msgstr "钉钉" -#: audits/signal_handlers/login_log.py:40 +#: audits/signal_handlers/login_log.py:41 #: authentication/models/temp_token.py:16 msgid "Temporary token" msgstr "临时密码" -#: audits/signal_handlers/login_log.py:41 authentication/views/login.py:95 +#: audits/signal_handlers/login_log.py:42 authentication/views/login.py:101 #: settings/serializers/auth/passkey.py:8 msgid "Passkey" msgstr "Passkey" @@ -2829,21 +2835,21 @@ msgstr "手机号没有设置" msgid "SSO auth closed" msgstr "SSO 认证关闭了" -#: authentication/errors/mfa.py:18 authentication/views/wecom.py:62 +#: authentication/errors/mfa.py:18 authentication/views/wecom.py:59 msgid "WeCom is already bound" msgstr "企业微信已经绑定" -#: authentication/errors/mfa.py:23 authentication/views/wecom.py:204 -#: authentication/views/wecom.py:246 +#: authentication/errors/mfa.py:23 authentication/views/wecom.py:159 +#: authentication/views/wecom.py:201 msgid "WeCom is not bound" msgstr "没有绑定企业微信" -#: authentication/errors/mfa.py:28 authentication/views/dingtalk.py:211 -#: authentication/views/dingtalk.py:253 +#: authentication/errors/mfa.py:28 authentication/views/dingtalk.py:212 +#: authentication/views/dingtalk.py:254 msgid "DingTalk is not bound" msgstr "钉钉没有绑定" -#: authentication/errors/mfa.py:33 authentication/views/feishu.py:168 +#: authentication/errors/mfa.py:33 authentication/views/feishu.py:128 msgid "FeiShu is not bound" msgstr "没有绑定飞书" @@ -3023,7 +3029,7 @@ msgid "Reusable" msgstr "可以重复使用" #: authentication/models/connection_token.py:51 -#: perms/models/asset_permission.py:82 +#: perms/models/asset_permission.py:83 msgid "From ticket" msgstr "来自工单" @@ -3112,15 +3118,15 @@ msgid "Ticket info" msgstr "工单信息" #: authentication/serializers/connection_token.py:21 -#: perms/models/asset_permission.py:76 perms/serializers/permission.py:36 -#: perms/serializers/permission.py:57 +#: perms/models/asset_permission.py:77 perms/serializers/permission.py:37 +#: perms/serializers/permission.py:58 #: tickets/models/ticket/apply_application.py:28 #: tickets/models/ticket/apply_asset.py:18 msgid "Actions" msgstr "动作" #: authentication/serializers/connection_token.py:42 -#: perms/serializers/permission.py:38 perms/serializers/permission.py:58 +#: perms/serializers/permission.py:39 perms/serializers/permission.py:59 #: users/serializers/user.py:97 users/serializers/user.py:171 msgid "Is expired" msgstr "已过期" @@ -3134,8 +3140,8 @@ msgstr "{} 不能为空" msgid "Access IP" msgstr "IP 白名单" -#: authentication/serializers/token.py:92 perms/serializers/permission.py:37 -#: perms/serializers/permission.py:59 users/serializers/user.py:98 +#: authentication/serializers/token.py:92 perms/serializers/permission.py:38 +#: perms/serializers/permission.py:60 users/serializers/user.py:98 #: users/serializers/user.py:168 msgid "Is valid" msgstr "是否有效" @@ -3161,13 +3167,13 @@ msgid "Show" msgstr "显示" #: authentication/templates/authentication/_access_key_modal.html:66 -#: users/const.py:37 users/models/user.py:641 users/serializers/profile.py:92 +#: users/const.py:37 users/models/user.py:642 users/serializers/profile.py:92 #: users/templates/users/user_verify_mfa.html:36 msgid "Disable" msgstr "禁用" #: authentication/templates/authentication/_access_key_modal.html:67 -#: users/const.py:38 users/models/user.py:642 users/serializers/profile.py:93 +#: users/const.py:38 users/models/user.py:643 users/serializers/profile.py:93 #: users/templates/users/mfa_setting.html:26 #: users/templates/users/mfa_setting.html:68 msgid "Enable" @@ -3206,10 +3212,10 @@ msgstr "代码错误" #: authentication/templates/authentication/_msg_reset_password_code.html:9 #: authentication/templates/authentication/_msg_rest_password_success.html:2 #: authentication/templates/authentication/_msg_rest_public_key_success.html:2 -#: jumpserver/conf.py:449 +#: jumpserver/conf.py:455 #: perms/templates/perms/_msg_item_permissions_expire.html:3 #: perms/templates/perms/_msg_permed_items_expire.html:3 -#: tickets/templates/tickets/approve_check_password.html:33 +#: tickets/templates/tickets/approve_check_password.html:32 #: users/templates/users/_msg_account_expire_reminder.html:4 #: users/templates/users/_msg_password_expire_reminder.html:4 #: users/templates/users/_msg_reset_mfa.html:4 @@ -3371,21 +3377,39 @@ msgstr "是否重试 ?" msgid "LAN" msgstr "局域网" -#: authentication/views/base.py:67 +#: authentication/views/base.py:73 #: perms/templates/perms/_msg_permed_items_expire.html:21 msgid "If you have any question, please contact the administrator" msgstr "如果有疑问或需求,请联系系统管理员" +#: authentication/views/base.py:138 +#, fuzzy, python-format +#| msgid "WeCom query user failed" +msgid "%s query user failed" +msgstr "企业微信查询用户失败" + +#: authentication/views/base.py:147 +#, fuzzy, python-format +#| msgid "The WeCom is already bound to another user" +msgid "The %s is already bound to another user" +msgstr "该企业微信已经绑定其他用户" + +#: authentication/views/base.py:154 +#, fuzzy, python-format +#| msgid "Binding WeCom successfully" +msgid "Binding %s successfully" +msgstr "绑定 企业微信 成功" + #: authentication/views/dingtalk.py:42 msgid "DingTalk Error, Please contact your system administrator" msgstr "钉钉错误,请联系系统管理员" -#: authentication/views/dingtalk.py:45 authentication/views/dingtalk.py:210 +#: authentication/views/dingtalk.py:45 authentication/views/dingtalk.py:211 msgid "DingTalk Error" msgstr "钉钉错误" -#: authentication/views/dingtalk.py:57 authentication/views/feishu.py:51 -#: authentication/views/wecom.py:58 +#: authentication/views/dingtalk.py:57 authentication/views/feishu.py:47 +#: authentication/views/slack.py:47 authentication/views/wecom.py:55 msgid "" "The system configuration is incorrect. Please contact your administrator" msgstr "企业配置错误,请联系系统管理员" @@ -3394,7 +3418,7 @@ msgstr "企业配置错误,请联系系统管理员" msgid "DingTalk is already bound" msgstr "钉钉已经绑定" -#: authentication/views/dingtalk.py:129 authentication/views/wecom.py:130 +#: authentication/views/dingtalk.py:129 msgid "Invalid user_id" msgstr "无效的 user_id" @@ -3410,55 +3434,43 @@ msgstr "该钉钉已经绑定其他用户" msgid "Binding DingTalk successfully" msgstr "绑定 钉钉 成功" -#: authentication/views/dingtalk.py:212 authentication/views/dingtalk.py:247 +#: authentication/views/dingtalk.py:213 authentication/views/dingtalk.py:248 msgid "Failed to get user from DingTalk" msgstr "从钉钉获取用户失败" -#: authentication/views/dingtalk.py:254 +#: authentication/views/dingtalk.py:255 msgid "Please login with a password and then bind the DingTalk" msgstr "请使用密码登录,然后绑定钉钉" -#: authentication/views/feishu.py:39 authentication/views/feishu.py:167 +#: authentication/views/feishu.py:35 authentication/views/feishu.py:127 msgid "FeiShu Error" msgstr "飞书错误" -#: authentication/views/feishu.py:67 +#: authentication/views/feishu.py:63 msgid "FeiShu is already bound" msgstr "飞书已经绑定" -#: authentication/views/feishu.py:108 -msgid "FeiShu query user failed" -msgstr "飞书查询用户失败" - -#: authentication/views/feishu.py:117 -msgid "The FeiShu is already bound to another user" -msgstr "该飞书已经绑定其他用户" - -#: authentication/views/feishu.py:124 -msgid "Binding FeiShu successfully" -msgstr "绑定 飞书 成功" - -#: authentication/views/feishu.py:169 +#: authentication/views/feishu.py:129 msgid "Failed to get user from FeiShu" msgstr "从飞书获取用户失败" -#: authentication/views/login.py:210 +#: authentication/views/login.py:217 msgid "Redirecting" msgstr "跳转中" -#: authentication/views/login.py:211 +#: authentication/views/login.py:218 msgid "Redirecting to {} authentication" msgstr "正在跳转到 {} 认证" -#: authentication/views/login.py:234 +#: authentication/views/login.py:241 msgid "Login timeout, please try again." msgstr "登录超时,请重新登录" -#: authentication/views/login.py:277 +#: authentication/views/login.py:284 msgid "User email already exists ({})" msgstr "用户邮箱已存在 ({})" -#: authentication/views/login.py:355 +#: authentication/views/login.py:362 msgid "" "Wait for {} confirm, You also can copy link to her/him
    \n" " Don't close this page" @@ -3466,43 +3478,59 @@ msgstr "" "等待 {} 确认, 你也可以复制链接发给他/她
    \n" " 不要关闭本页面" -#: authentication/views/login.py:360 +#: authentication/views/login.py:367 msgid "No ticket found" msgstr "没有发现工单" -#: authentication/views/login.py:396 +#: authentication/views/login.py:403 msgid "Logout success" msgstr "退出登录成功" -#: authentication/views/login.py:397 +#: authentication/views/login.py:404 msgid "Logout success, return login page" msgstr "退出登录成功,返回到登录页面" -#: authentication/views/wecom.py:43 +#: authentication/views/slack.py:35 authentication/views/slack.py:126 +#, fuzzy +#| msgid "DingTalk Error" +msgid "Slack Error" +msgstr "钉钉错误" + +#: authentication/views/slack.py:63 +#, fuzzy +#| msgid "DingTalk is already bound" +msgid "Slack is already bound" +msgstr "钉钉已经绑定" + +#: authentication/views/slack.py:127 +#, fuzzy +#| msgid "DingTalk is not bound" +msgid "Slack is not bound" +msgstr "钉钉没有绑定" + +#: authentication/views/slack.py:128 +#, fuzzy +#| msgid "Failed to get user from DingTalk" +msgid "Failed to get user from Slack" +msgstr "从钉钉获取用户失败" + +#: authentication/views/wecom.py:40 msgid "WeCom Error, Please contact your system administrator" msgstr "企业微信错误,请联系系统管理员" -#: authentication/views/wecom.py:46 authentication/views/wecom.py:203 +#: authentication/views/wecom.py:43 authentication/views/wecom.py:158 msgid "WeCom Error" msgstr "企业微信错误" -#: authentication/views/wecom.py:145 -msgid "WeCom query user failed" -msgstr "企业微信查询用户失败" +#: authentication/views/wecom.py:118 +msgid "Wecom" +msgstr "" -#: authentication/views/wecom.py:154 -msgid "The WeCom is already bound to another user" -msgstr "该企业微信已经绑定其他用户" - -#: authentication/views/wecom.py:161 -msgid "Binding WeCom successfully" -msgstr "绑定 企业微信 成功" - -#: authentication/views/wecom.py:205 authentication/views/wecom.py:240 +#: authentication/views/wecom.py:160 authentication/views/wecom.py:195 msgid "Failed to get user from WeCom" msgstr "从企业微信获取用户失败" -#: authentication/views/wecom.py:247 +#: authentication/views/wecom.py:202 msgid "Please login with a password and then bind the WeCom" msgstr "请使用密码登录,然后绑定企业微信" @@ -3596,7 +3624,7 @@ msgid "Invalid ids for ids, should be a list" msgstr "无效的ID,应为列表" #: common/db/fields.py:585 common/db/fields.py:590 -#: common/serializers/fields.py:104 tickets/serializers/ticket/common.py:58 +#: common/serializers/fields.py:132 tickets/serializers/ticket/common.py:58 #: xpack/plugins/cloud/serializers/account_attrs.py:56 #: xpack/plugins/cloud/serializers/account_attrs.py:79 #: xpack/plugins/cloud/serializers/account_attrs.py:143 @@ -3619,7 +3647,7 @@ msgstr "忽略的" msgid "discard time" msgstr "忽略时间" -#: common/db/models.py:33 users/models/user.py:837 +#: common/db/models.py:33 users/models/user.py:842 msgid "Updated by" msgstr "最后更新者" @@ -3694,6 +3722,12 @@ msgstr "不支持 Elasticsearch8" msgid "Network error, please contact system administrator" msgstr "网络错误,请联系系统管理员" +#: common/sdk/im/slack/__init__.py:76 +#, fuzzy +#| msgid "Unknown error: {}" +msgid "Unknown error occur" +msgstr "未知错误: {}" + #: common/sdk/im/wecom/__init__.py:16 msgid "WeCom error, please contact system administrator" msgstr "企业微信错误,请联系系统管理员" @@ -3763,24 +3797,30 @@ msgstr "请在 {} 秒后发送" msgid "Children" msgstr "节点" -#: common/serializers/fields.py:105 +#: common/serializers/fields.py:133 #, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "错误的 pk \"{pk_value}\" - 对象不存在" -#: common/serializers/fields.py:106 +#: common/serializers/fields.py:134 #, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "错误类型。期望 pk 值,收到 {data_type}。" -#: common/serializers/fields.py:180 +#: common/serializers/fields.py:208 msgid "Invalid data type, should be list" msgstr "错误的数据类型,应该是列表" -#: common/serializers/fields.py:195 +#: common/serializers/fields.py:223 msgid "Invalid choice: {}" msgstr "无效选项: {}" +#: common/serializers/mixin.py:397 +msgid "Labels" +msgstr "标签管理" + +# msgid "Labels" +# msgstr "标签管理" #: common/tasks.py:21 common/utils/verify_code.py:16 msgid "Send email" msgstr "发件邮件" @@ -3818,16 +3858,16 @@ msgstr "不能包含特殊字符" msgid "The mobile phone number format is incorrect" msgstr "手机号格式不正确" -#: jumpserver/conf.py:444 +#: jumpserver/conf.py:450 #, python-brace-format msgid "The verification code is: {code}" msgstr "验证码为: {code}" -#: jumpserver/conf.py:448 +#: jumpserver/conf.py:454 msgid "Create account successfully" msgstr "创建账号成功" -#: jumpserver/conf.py:450 +#: jumpserver/conf.py:456 msgid "Your account has been created successfully" msgstr "你的账号已创建成功" @@ -3862,6 +3902,22 @@ msgstr "" "div>
    如果你看到了这个页面,证明你访问的不是nginx监听的端口,祝你好运" +#: labels/models.py:30 +msgid "Resource ID" +msgstr "资源 ID" + +#: labels/models.py:35 +msgid "Labeled resource" +msgstr "关联的资源" + +#: labels/serializers.py:19 +msgid "Resource count" +msgstr "资源数量" + +#: labels/serializers.py:31 +msgid "Resource type" +msgstr "资源类型" + #: notifications/backends/__init__.py:13 msgid "Site message" msgstr "站内信" @@ -3886,7 +3942,7 @@ msgstr "系统信息" msgid "Publish the station message" msgstr "发布站内消息" -#: ops/ansible/inventory.py:95 ops/models/job.py:61 +#: ops/ansible/inventory.py:95 ops/models/job.py:63 msgid "No account available" msgstr "无可用账号" @@ -3938,7 +3994,7 @@ msgstr "文件密钥该字段是必填项。" msgid "This file can not be delete" msgstr "无法删除此文件" -#: ops/apps.py:9 ops/notifications.py:17 rbac/tree.py:55 +#: ops/apps.py:9 ops/notifications.py:17 rbac/tree.py:57 msgid "App ops" msgstr "作业中心" @@ -3978,7 +4034,7 @@ msgstr "VCS" msgid "Adhoc" msgstr "命令" -#: ops/const.py:39 ops/models/job.py:138 +#: ops/const.py:39 ops/models/job.py:147 msgid "Playbook" msgstr "Playbook" @@ -4059,15 +4115,17 @@ msgstr "需要周期或定期设置" msgid "Pattern" msgstr "模式" -#: ops/models/adhoc.py:23 ops/models/job.py:133 +#: ops/models/adhoc.py:23 ops/models/job.py:142 msgid "Module" msgstr "模块" -#: ops/models/adhoc.py:24 ops/models/celery.py:58 ops/models/job.py:131 +#: ops/models/adhoc.py:24 ops/models/celery.py:58 ops/models/job.py:140 #: terminal/models/component/task.py:14 msgid "Args" msgstr "参数" +# msgid "Creator" +# msgstr "创建者" #: ops/models/base.py:19 msgid "Account policy" msgstr "账号策略" @@ -4076,16 +4134,16 @@ msgstr "账号策略" msgid "Last execution" msgstr "最后执行" -#: ops/models/base.py:22 ops/serializers/job.py:19 +#: ops/models/base.py:22 ops/serializers/job.py:18 msgid "Date last run" msgstr "最后运行日期" -#: ops/models/base.py:51 ops/models/job.py:227 +#: ops/models/base.py:51 ops/models/job.py:236 #: xpack/plugins/cloud/models.py:199 msgid "Result" msgstr "结果" -#: ops/models/base.py:52 ops/models/job.py:228 +#: ops/models/base.py:52 ops/models/job.py:237 msgid "Summary" msgstr "汇总" @@ -4118,51 +4176,51 @@ msgstr "发布日期" msgid "Celery Task Execution" msgstr "Celery 任务执行" -#: ops/models/job.py:135 +#: ops/models/job.py:144 msgid "Chdir" msgstr "运行目录" -#: ops/models/job.py:136 +#: ops/models/job.py:145 msgid "Timeout (Seconds)" msgstr "超时时间 (秒)" -#: ops/models/job.py:143 +#: ops/models/job.py:152 msgid "Use Parameter Define" msgstr "使用参数定义" -#: ops/models/job.py:144 +#: ops/models/job.py:153 msgid "Parameters define" msgstr "参数定义" -#: ops/models/job.py:145 +#: ops/models/job.py:154 msgid "Runas" msgstr "运行用户" -#: ops/models/job.py:147 +#: ops/models/job.py:156 msgid "Runas policy" msgstr "用户策略" -#: ops/models/job.py:211 +#: ops/models/job.py:220 msgid "Job" msgstr "作业" -#: ops/models/job.py:234 +#: ops/models/job.py:243 msgid "Material" msgstr "Material" -#: ops/models/job.py:236 +#: ops/models/job.py:245 msgid "Material Type" msgstr "Material 类型" -#: ops/models/job.py:544 +#: ops/models/job.py:557 msgid "Job Execution" msgstr "作业执行" -#: ops/models/playbook.py:33 +#: ops/models/playbook.py:34 msgid "CreateMethod" msgstr "创建方式" -#: ops/models/playbook.py:34 +#: ops/models/playbook.py:35 msgid "VCS URL" msgstr "VCS URL" @@ -4194,19 +4252,19 @@ msgstr "内存使用率超过 {max_threshold}%: => {value}" msgid "CPU load more than {max_threshold}: => {value}" msgstr "CPU 使用率超过 {max_threshold}: => {value}" -#: ops/serializers/job.py:17 +#: ops/serializers/job.py:16 msgid "Run after save" msgstr "保存后执行" -#: ops/serializers/job.py:54 +#: ops/serializers/job.py:53 msgid "Job type" msgstr "任务类型" -#: ops/serializers/job.py:57 terminal/serializers/session.py:53 +#: ops/serializers/job.py:56 terminal/serializers/session.py:53 msgid "Is finished" msgstr "是否完成" -#: ops/serializers/job.py:58 +#: ops/serializers/job.py:57 msgid "Time cost" msgstr "花费时间" @@ -4296,7 +4354,7 @@ msgstr "LDAP 同步设置组织为当前组织,请切换其他组织后再进 msgid "The organization have resource ({}) cannot be deleted" msgstr "组织存在资源 ({}) 不能被删除" -#: orgs/apps.py:7 rbac/tree.py:126 +#: orgs/apps.py:7 rbac/tree.py:128 msgid "App organizations" msgstr "组织管理" @@ -4385,7 +4443,7 @@ msgstr "文件传输" msgid "Clipboard" msgstr "剪贴板" -#: perms/models/asset_permission.py:88 +#: perms/models/asset_permission.py:89 msgid "Asset permission" msgstr "资产授权" @@ -4529,7 +4587,7 @@ msgstr "文件管理" msgid "Can view System Tools" msgstr "可以查看系统工具" -#: rbac/models/permission.py:27 rbac/models/role.py:34 +#: rbac/models/permission.py:78 rbac/models/role.py:34 msgid "Permissions" msgstr "授权" @@ -4539,7 +4597,7 @@ msgid "Scope" msgstr "范围" #: rbac/models/role.py:46 rbac/models/rolebinding.py:52 -#: users/models/user.py:803 +#: users/models/user.py:808 msgid "Role" msgstr "角色" @@ -4577,7 +4635,7 @@ msgstr "系统角色绑定" msgid "Perms" msgstr "权限" -#: rbac/serializers/role.py:27 users/serializers/group.py:30 +#: rbac/serializers/role.py:27 users/serializers/group.py:31 msgid "Users amount" msgstr "用户数量" @@ -4613,59 +4671,60 @@ msgstr "系统设置" msgid "Session audits" msgstr "会话审计" -#: rbac/tree.py:47 +#: rbac/tree.py:49 msgid "Cloud import" msgstr "云同步" -#: rbac/tree.py:48 +#: rbac/tree.py:50 msgid "Backup account" msgstr "备份账号" -#: rbac/tree.py:49 +#: rbac/tree.py:51 msgid "Gather account" msgstr "收集账号" -#: rbac/tree.py:51 +#: rbac/tree.py:53 msgid "Asset change auth" msgstr "资产改密" -#: rbac/tree.py:52 +#: rbac/tree.py:54 msgid "Terminal setting" msgstr "终端设置" -#: rbac/tree.py:53 +#: rbac/tree.py:55 msgid "Task Center" msgstr "任务中心" -#: rbac/tree.py:54 +#: rbac/tree.py:56 msgid "My assets" msgstr "我的资产" -#: rbac/tree.py:56 terminal/models/applet/applet.py:52 +#: rbac/tree.py:58 terminal/models/applet/applet.py:52 #: terminal/models/applet/applet.py:315 terminal/models/applet/host.py:30 #: terminal/serializers/applet.py:15 msgid "Applet" msgstr "远程应用" -#: rbac/tree.py:127 +#: rbac/tree.py:129 msgid "Ticket comment" msgstr "工单评论" -#: rbac/tree.py:128 settings/serializers/feature.py:58 +#: rbac/tree.py:130 settings/serializers/feature.py:58 #: tickets/models/ticket/general.py:307 msgid "Ticket" msgstr "工单管理" -#: rbac/tree.py:129 +#: rbac/tree.py:131 msgid "Common setting" msgstr "一般设置" -#: rbac/tree.py:130 +#: rbac/tree.py:132 msgid "View permission tree" msgstr "查看授权树" #: settings/api/dingtalk.py:31 settings/api/feishu.py:36 -#: settings/api/sms.py:160 settings/api/vault.py:40 settings/api/wecom.py:37 +#: settings/api/slack.py:34 settings/api/sms.py:160 settings/api/vault.py:40 +#: settings/api/wecom.py:37 msgid "Test success" msgstr "测试成功" @@ -4791,22 +4850,28 @@ msgid "FeiShu Auth" msgstr "飞书 认证" #: settings/serializers/auth/base.py:20 +#, fuzzy +#| msgid "CAS Auth" +msgid "Slack Auth" +msgstr "CAS 认证" + +#: settings/serializers/auth/base.py:21 msgid "WeCom Auth" msgstr "企业微信 认证" -#: settings/serializers/auth/base.py:21 +#: settings/serializers/auth/base.py:22 msgid "SSO Auth" msgstr "SSO 令牌认证" -#: settings/serializers/auth/base.py:22 +#: settings/serializers/auth/base.py:23 msgid "Passkey Auth" msgstr "Passkey 认证" -#: settings/serializers/auth/base.py:25 +#: settings/serializers/auth/base.py:26 msgid "Forgot password url" msgstr "忘记密码 URL" -#: settings/serializers/auth/base.py:28 +#: settings/serializers/auth/base.py:29 msgid "Enable login redirect msg" msgstr "启用登录跳转提示" @@ -5107,6 +5172,12 @@ msgstr "SP 密钥" msgid "SP cert" msgstr "SP 证书" +#: settings/serializers/auth/slack.py:12 +#, fuzzy +#| msgid "Enable CAS Auth" +msgid "Enable Slack Auth" +msgstr "启用 CAS 认证" + #: settings/serializers/auth/sms.py:17 msgid "Enable SMS" msgstr "启用 SMS" @@ -6852,7 +6923,7 @@ msgstr "端点后缀" msgid "HOST" msgstr "主机" -#: terminal/serializers/storage.py:146 users/models/user.py:823 +#: terminal/serializers/storage.py:146 users/models/user.py:828 #: xpack/plugins/cloud/serializers/account_attrs.py:206 msgid "Private key" msgstr "ssh私钥" @@ -7239,11 +7310,11 @@ msgstr "授权名称 `{}` 已存在" msgid "The ticket flow `{}` does not exist" msgstr "工单流程 `{}` 不存在" -#: tickets/templates/tickets/_msg_ticket.html:20 +#: tickets/templates/tickets/_msg_ticket.html:21 msgid "View details" msgstr "查看详情" -#: tickets/templates/tickets/_msg_ticket.html:25 +#: tickets/templates/tickets/_msg_ticket.html:26 msgid "Direct approval" msgstr "直接批准" @@ -7251,12 +7322,12 @@ msgstr "直接批准" msgid "Ticket information" msgstr "工单信息" -#: tickets/templates/tickets/approve_check_password.html:29 +#: tickets/templates/tickets/approve_check_password.html:28 #: tickets/views/approve.py:40 tickets/views/approve.py:77 msgid "Ticket approval" msgstr "工单审批" -#: tickets/templates/tickets/approve_check_password.html:44 +#: tickets/templates/tickets/approve_check_password.html:43 msgid "Approval" msgstr "同意" @@ -7281,11 +7352,11 @@ msgstr "无效的审批动作" msgid "This user is not authorized to approve this ticket" msgstr "此用户无权审批此工单" -#: users/api/user.py:141 +#: users/api/user.py:137 msgid "Can not invite self" msgstr "不能邀请自己" -#: users/api/user.py:194 +#: users/api/user.py:190 msgid "Could not reset self otp, use profile reset instead" msgstr "不能在该页面重置 MFA 多因子认证, 请去个人信息页面重置" @@ -7420,7 +7491,7 @@ msgstr "不能和原来的密钥相同" msgid "Not a valid ssh public key" msgstr "SSH密钥不合法" -#: users/forms/profile.py:173 users/models/user.py:826 +#: users/forms/profile.py:173 users/models/user.py:831 #: xpack/plugins/cloud/serializers/account_attrs.py:203 msgid "Public key" msgstr "SSH公钥" @@ -7429,72 +7500,74 @@ msgstr "SSH公钥" msgid "Preference" msgstr "用户设置" -#: users/models/user.py:643 users/serializers/profile.py:94 +#: users/models/user.py:644 users/serializers/profile.py:94 msgid "Force enable" msgstr "强制启用" -#: users/models/user.py:805 users/serializers/user.py:169 +#: users/models/user.py:810 users/serializers/user.py:169 msgid "Is service account" msgstr "服务账号" -#: users/models/user.py:807 +#: users/models/user.py:812 msgid "Avatar" msgstr "头像" -#: users/models/user.py:810 +#: users/models/user.py:815 msgid "Wechat" msgstr "微信" -#: users/models/user.py:813 users/serializers/user.py:106 +#: users/models/user.py:818 users/serializers/user.py:106 msgid "Phone" msgstr "手机" -#: users/models/user.py:819 +#: users/models/user.py:824 msgid "OTP secret key" msgstr "OTP 密钥" -#: users/models/user.py:831 users/serializers/profile.py:128 +# msgid "Private key" +# msgstr "ssh私钥" +#: users/models/user.py:836 users/serializers/profile.py:128 #: users/serializers/user.py:166 msgid "Is first login" msgstr "首次登录" -#: users/models/user.py:841 +#: users/models/user.py:846 msgid "Date password last updated" msgstr "最后更新密码日期" -#: users/models/user.py:844 +#: users/models/user.py:849 msgid "Need update password" msgstr "需要更新密码" -#: users/models/user.py:846 +#: users/models/user.py:851 msgid "Date api key used" msgstr "Api key 最后使用日期" -#: users/models/user.py:969 +#: users/models/user.py:975 msgid "Can not delete admin user" msgstr "无法删除管理员用户" -#: users/models/user.py:995 +#: users/models/user.py:1002 msgid "Can invite user" msgstr "可以邀请用户" -#: users/models/user.py:996 +#: users/models/user.py:1003 msgid "Can remove user" msgstr "可以移除用户" -#: users/models/user.py:997 +#: users/models/user.py:1004 msgid "Can match user" msgstr "可以匹配用户" -#: users/models/user.py:1006 +#: users/models/user.py:1013 msgid "Administrator" msgstr "管理员" -#: users/models/user.py:1009 +#: users/models/user.py:1016 msgid "Administrator is the super user of system" msgstr "Administrator是初始的超级管理员" -#: users/models/user.py:1034 +#: users/models/user.py:1041 msgid "User password history" msgstr "用户密码历史" @@ -7567,8 +7640,10 @@ msgstr "RDP 客户端选项" #: users/serializers/preference/luna.py:45 msgid "Rdp smart size" -msgstr "RDP 智能大小" +msgstr "" +# msgid "Rdp smart size" +# msgstr "RDP 智能大小" #: users/serializers/preference/luna.py:46 msgid "" "Determines whether the client computer should scale the content on the " @@ -7658,15 +7733,15 @@ msgstr "头像路径" msgid "MFA level" msgstr "MFA 级别" -#: users/serializers/user.py:282 +#: users/serializers/user.py:287 msgid "Select users" msgstr "选择用户" -#: users/serializers/user.py:283 +#: users/serializers/user.py:288 msgid "For security, only list several users" msgstr "为了安全,仅列出几个用户" -#: users/serializers/user.py:316 +#: users/serializers/user.py:321 msgid "name not unique" msgstr "名称重复" @@ -8567,15 +8642,7 @@ msgstr "退出页面logo" msgid "Theme" msgstr "主题" -#: xpack/plugins/interface/models.py:42 -msgid "Beian link" -msgstr "公安联网备案跳转链接" - -#: xpack/plugins/interface/models.py:43 -msgid "Beian text" -msgstr "公安联网备案号" - -#: xpack/plugins/interface/models.py:46 xpack/plugins/interface/models.py:87 +#: xpack/plugins/interface/models.py:44 xpack/plugins/interface/models.py:85 msgid "Interface setting" msgstr "界面设置" @@ -8607,26 +8674,17 @@ msgstr "企业专业版" msgid "Ultimate edition" msgstr "企业旗舰版" -#~ msgid "Synchronization start, please wait." -#~ msgstr "同步开始,请稍等" +#~ msgid "FeiShu query user failed" +#~ msgstr "飞书查询用户失败" -#~ msgid "Synchronization is running, please wait." -#~ msgstr "同步正在运行,请稍等" +#~ msgid "The FeiShu is already bound to another user" +#~ msgstr "该飞书已经绑定其他用户" -#~ msgid "Synchronization error: {}" -#~ msgstr "同步错误: {}" +#~ msgid "Binding FeiShu successfully" +#~ msgstr "绑定 飞书 成功" -#~ msgid "Copy" -#~ msgstr "复制" +#~ msgid "Beian link" +#~ msgstr "公安联网备案跳转链接" -#~ msgid "Paste" -#~ msgstr "粘贴" - -#~ msgid "Password can not contains `'` " -#~ msgstr "密码不能包含 `'` 字符" - -#~ msgid "Password can not contains `\"` " -#~ msgstr "密码不能包含 `\"` 字符" - -#~ msgid "Object Storage" -#~ msgstr "对象存储" +#~ msgid "Beian text" +#~ msgstr "公安联网备案号" diff --git a/apps/ops/models/job.py b/apps/ops/models/job.py index d339dbf7f..7271a3401 100644 --- a/apps/ops/models/job.py +++ b/apps/ops/models/job.py @@ -22,6 +22,7 @@ from acls.models import CommandFilterACL from assets.models import Asset from assets.automations.base.manager import SSHTunnelManager from common.db.encoder import ModelJSONFieldEncoder +from labels.mixins import LabeledMixin from ops.ansible import JMSInventory, AdHocRunner, PlaybookRunner, CommandInBlackListException from ops.mixin import PeriodTaskModelMixin from ops.variables import * @@ -132,7 +133,7 @@ class JMSPermedInventory(JMSInventory): return mapper -class Job(JMSOrgBaseModel, PeriodTaskModelMixin): +class Job(LabeledMixin, JMSOrgBaseModel, PeriodTaskModelMixin): name = models.CharField(max_length=128, null=True, verbose_name=_('Name')) instant = models.BooleanField(default=False) diff --git a/apps/ops/models/playbook.py b/apps/ops/models/playbook.py index e1188cb46..caf226ee6 100644 --- a/apps/ops/models/playbook.py +++ b/apps/ops/models/playbook.py @@ -6,6 +6,7 @@ from django.db import models from django.utils.translation import gettext_lazy as _ from private_storage.fields import PrivateFileField +from labels.mixins import LabeledMixin from ops.const import CreateMethods from ops.exception import PlaybookNoValidEntry from orgs.mixins.models import JMSOrgBaseModel @@ -23,7 +24,7 @@ dangerous_keywords = ( ) -class Playbook(JMSOrgBaseModel): +class Playbook(LabeledMixin, JMSOrgBaseModel): id = models.UUIDField(default=uuid.uuid4, primary_key=True) name = models.CharField(max_length=128, verbose_name=_('Name'), null=True) path = PrivateFileField(upload_to='playbooks/') diff --git a/apps/ops/serializers/job.py b/apps/ops/serializers/job.py index 011bea4e2..d97a0929a 100644 --- a/apps/ops/serializers/job.py +++ b/apps/ops/serializers/job.py @@ -3,23 +3,21 @@ import uuid from django.utils.translation import gettext_lazy as _ from rest_framework import serializers -from assets.models import Node, Asset -from perms.models import PermNode -from perms.utils.user_perm import UserPermAssetUtil +from assets.models import Asset +from common.serializers import ResourceLabelsMixin from common.serializers.fields import ReadableHiddenField from ops.mixin import PeriodTaskSerializerMixin from ops.models import Job, JobExecution from orgs.mixins.serializers import BulkOrgResourceModelSerializer -class JobSerializer(BulkOrgResourceModelSerializer, PeriodTaskSerializerMixin): +class JobSerializer(ResourceLabelsMixin, BulkOrgResourceModelSerializer, PeriodTaskSerializerMixin): creator = ReadableHiddenField(default=serializers.CurrentUserDefault()) run_after_save = serializers.BooleanField(label=_("Run after save"), default=False, required=False) nodes = serializers.ListField(required=False, child=serializers.CharField()) date_last_run = serializers.DateTimeField(label=_('Date last run'), read_only=True) name = serializers.CharField(label=_('Name'), max_length=128, allow_blank=True, required=False) - assets = serializers.PrimaryKeyRelatedField(label=_('Assets'), queryset=Asset.objects, many=True, - required=False) + assets = serializers.PrimaryKeyRelatedField(label=_('Assets'), queryset=Asset.objects, many=True, required=False) def to_internal_value(self, data): instant = data.get('instant', False) @@ -36,11 +34,12 @@ class JobSerializer(BulkOrgResourceModelSerializer, PeriodTaskSerializerMixin): class Meta: model = Job read_only_fields = [ - "id", "date_last_run", "date_created", "date_updated", "average_time_cost" + "id", "date_last_run", "date_created", + "date_updated", "average_time_cost" ] fields = read_only_fields + [ "name", "instant", "type", "module", - "args", "playbook", "assets", + "args", "playbook", "assets", "labels", "runas_policy", "runas", "creator", "use_parameter_define", "parameters_define", "timeout", "chdir", "comment", "summary", diff --git a/apps/ops/serializers/playbook.py b/apps/ops/serializers/playbook.py index eac89850b..2fb06d691 100644 --- a/apps/ops/serializers/playbook.py +++ b/apps/ops/serializers/playbook.py @@ -2,10 +2,10 @@ import os from rest_framework import serializers +from common.serializers import ResourceLabelsMixin from common.serializers.fields import ReadableHiddenField from ops.models import Playbook from orgs.mixins.serializers import BulkOrgResourceModelSerializer -from django.utils.translation import gettext_lazy as _ def parse_playbook_name(path): @@ -13,7 +13,7 @@ def parse_playbook_name(path): return file_name.split(".")[-2] -class PlaybookSerializer(BulkOrgResourceModelSerializer): +class PlaybookSerializer(ResourceLabelsMixin, BulkOrgResourceModelSerializer): creator = ReadableHiddenField(default=serializers.CurrentUserDefault()) path = serializers.FileField(required=False) @@ -27,5 +27,6 @@ class PlaybookSerializer(BulkOrgResourceModelSerializer): model = Playbook read_only_fields = ["id", "date_created", "date_updated"] fields = read_only_fields + [ - "id", 'path', "name", "comment", "creator", 'create_method', 'vcs_url', + "id", 'path', "name", "comment", "creator", + 'create_method', 'vcs_url', 'labels' ] diff --git a/apps/perms/models/asset_permission.py b/apps/perms/models/asset_permission.py index cee80f562..9db73e5df 100644 --- a/apps/perms/models/asset_permission.py +++ b/apps/perms/models/asset_permission.py @@ -10,6 +10,7 @@ from accounts.models import Account from assets.models import Asset from common.utils import date_expired_default from common.utils.timezone import local_now +from labels.mixins import LabeledMixin from orgs.mixins.models import JMSOrgBaseModel from orgs.mixins.models import OrgManager from perms.const import ActionChoices @@ -56,7 +57,7 @@ def default_protocols(): return ['all'] -class AssetPermission(JMSOrgBaseModel): +class AssetPermission(LabeledMixin, JMSOrgBaseModel): name = models.CharField(max_length=128, verbose_name=_('Name')) users = models.ManyToManyField( 'users.User', related_name='%(class)ss', blank=True, verbose_name=_("User") diff --git a/apps/perms/serializers/permission.py b/apps/perms/serializers/permission.py index 26afba67c..1e0384b1b 100644 --- a/apps/perms/serializers/permission.py +++ b/apps/perms/serializers/permission.py @@ -7,6 +7,7 @@ from rest_framework import serializers from accounts.models import AccountTemplate, Account from accounts.tasks import push_accounts_to_assets_task from assets.models import Asset, Node +from common.serializers import ResourceLabelsMixin from common.serializers.fields import BitChoicesField, ObjectRelatedField from orgs.mixins.serializers import BulkOrgResourceModelSerializer from perms.models import ActionChoices, AssetPermission @@ -26,7 +27,7 @@ class ActionChoicesField(BitChoicesField): return data -class AssetPermissionSerializer(BulkOrgResourceModelSerializer): +class AssetPermissionSerializer(ResourceLabelsMixin, BulkOrgResourceModelSerializer): users = ObjectRelatedField(queryset=User.objects, many=True, required=False, label=_('User')) user_groups = ObjectRelatedField( queryset=UserGroup.objects, many=True, required=False, label=_('User group') @@ -50,7 +51,7 @@ class AssetPermissionSerializer(BulkOrgResourceModelSerializer): "is_valid", "comment", "from_ticket", ] fields_small = fields_mini + fields_generic - fields_m2m = ["users", "user_groups", "assets", "nodes"] + fields_m2m = ["users", "user_groups", "assets", "nodes", "labels"] fields = fields_mini + fields_m2m + fields_generic read_only_fields = ["created_by", "date_created", "from_ticket"] extra_kwargs = { @@ -130,7 +131,7 @@ class AssetPermissionSerializer(BulkOrgResourceModelSerializer): """Perform necessary eager loading of data.""" queryset = queryset.prefetch_related( "users", "user_groups", "assets", "nodes", - ) + ).prefetch_related('labels', 'labels__label') return queryset @staticmethod diff --git a/apps/rbac/api/__init__.py b/apps/rbac/api/__init__.py index 894655045..28436525a 100644 --- a/apps/rbac/api/__init__.py +++ b/apps/rbac/api/__init__.py @@ -1,4 +1,4 @@ +from .content_type import * from .permission import * from .role import * from .rolebinding import * - diff --git a/apps/rbac/api/content_type.py b/apps/rbac/api/content_type.py new file mode 100644 index 000000000..2afe021ca --- /dev/null +++ b/apps/rbac/api/content_type.py @@ -0,0 +1,11 @@ +from rest_framework import viewsets + +from .. import serializers +from ..models import ContentType + + +class ContentTypeViewSet(viewsets.ModelViewSet): + serializer_class = serializers.ContentTypeSerializer + filterset_fields = ("app_label", "model",) + search_fields = filterset_fields + queryset = ContentType.objects.all() diff --git a/apps/rbac/const.py b/apps/rbac/const.py index 01c8bd915..0bac7029e 100644 --- a/apps/rbac/const.py +++ b/apps/rbac/const.py @@ -73,7 +73,7 @@ exclude_permissions = ( ('perms', 'rebuildusertreetask', '*', '*'), ('perms', 'permedasset', '*', 'permedasset'), ('perms', 'permedapplication', 'add,change,delete', 'permedapplication'), - ('rbac', 'contenttype', '*', '*'), + ('rbac', 'contenttype', 'add,change,delete', '*'), ('rbac', 'permission', 'add,delete,change', 'permission'), ('rbac', 'rolebinding', '*', '*'), ('rbac', 'systemrolebinding', 'change', 'systemrolebinding'), diff --git a/apps/rbac/models/permission.py b/apps/rbac/models/permission.py index 17653f151..2b597c3c9 100644 --- a/apps/rbac/models/permission.py +++ b/apps/rbac/models/permission.py @@ -1,8 +1,10 @@ +from django.apps import apps from django.contrib.auth.models import ContentType as DjangoContentType from django.contrib.auth.models import Permission as DjangoPermission from django.db.models import Q from django.utils.translation import gettext_lazy as _ +from common.utils import lazyproperty from .. import const Scope = const.Scope @@ -14,10 +16,59 @@ class ContentType(DjangoContentType): class Meta: proxy = True + _apps_map = {} + @property def app_model(self): return '%s.%s' % (self.app_label, self.model) + @classmethod + def apps_map(cls): + from ..tree import app_nodes_data + if cls._apps_map: + return cls._apps_map + mapper = {} + for d in app_nodes_data: + i = d['id'] + name = d.get('name') + + if not name: + config = apps.get_app_config(d['id']) + if config: + name = config.verbose_name + if name: + mapper[i] = name + cls._apps_map = mapper + return mapper + + @property + def app_display(self): + return self.apps_map().get(self.app_label) + + @lazyproperty + def fields(self): + model = self.model_class() + return model._meta.fields + + @lazyproperty + def field_names(self): + return [f.name for f in self.fields] + + @lazyproperty + def filter_field_names(self): + names = [] + if 'name' in self.field_names: + names.append('name') + if 'address' in self.field_names: + names.append('address') + return names + + def filter_queryset(self, queryset, keyword): + q = Q() + for name in self.filter_field_names: + q |= Q(**{name + '__icontains': keyword}) + return queryset.filter(q) + class Permission(DjangoPermission): """ 权限类 """ diff --git a/apps/rbac/serializers/__init__.py b/apps/rbac/serializers/__init__.py index 894655045..28436525a 100644 --- a/apps/rbac/serializers/__init__.py +++ b/apps/rbac/serializers/__init__.py @@ -1,4 +1,4 @@ +from .content_type import * from .permission import * from .role import * from .rolebinding import * - diff --git a/apps/rbac/serializers/content_type.py b/apps/rbac/serializers/content_type.py new file mode 100644 index 000000000..8e811230c --- /dev/null +++ b/apps/rbac/serializers/content_type.py @@ -0,0 +1,13 @@ +from rest_framework import serializers + +from ..models import ContentType + +__all__ = ['ContentTypeSerializer'] + + +class ContentTypeSerializer(serializers.ModelSerializer): + app_display = serializers.CharField() + + class Meta: + model = ContentType + fields = ('id', 'app_label', 'app_display', 'model', 'name') diff --git a/apps/rbac/tree.py b/apps/rbac/tree.py index 7a7a989f7..49fa1a23c 100644 --- a/apps/rbac/tree.py +++ b/apps/rbac/tree.py @@ -39,7 +39,9 @@ app_nodes_data = [ {'id': 'rbac', 'view': 'view_console'}, {'id': 'settings', 'view': 'view_setting'}, {'id': 'tickets', 'view': 'view_other'}, + {'id': 'labels', 'view': 'view_label'}, {'id': 'authentication', 'view': 'view_other'}, + {'id': 'ops', 'view': 'view_workbench'}, ] # 额外其他节点,可以在不同的层次,需要指定父节点,可以将一些 model 归类到这个节点下面 diff --git a/apps/rbac/urls/api_urls.py b/apps/rbac/urls/api_urls.py index 5dc080930..f68aab11d 100644 --- a/apps/rbac/urls/api_urls.py +++ b/apps/rbac/urls/api_urls.py @@ -6,7 +6,6 @@ from .. import api app_name = 'rbac' - router = BulkRouter() router.register(r'roles', api.RoleViewSet, 'role') router.register(r'role-bindings', api.RoleBindingViewSet, 'role-binding') @@ -18,6 +17,7 @@ router.register(r'org-roles', api.OrgRoleViewSet, 'org-role') router.register(r'org-role-bindings', api.OrgRoleBindingViewSet, 'org-role-binding') router.register(r'permissions', api.PermissionViewSet, 'permission') +router.register(r'content-types', api.ContentTypeViewSet, 'content-type') system_role_router = routers.NestedDefaultRouter(router, r'system-roles', lookup='system_role') system_role_router.register(r'permissions', api.SystemRolePermissionsViewSet, 'system-role-permission') diff --git a/apps/users/api/user.py b/apps/users/api/user.py index 524571025..9c364ab75 100644 --- a/apps/users/api/user.py +++ b/apps/users/api/user.py @@ -52,10 +52,6 @@ class UserViewSet(CommonApiMixin, UserQuerysetMixin, SuggestionMixin, BulkModelV 'bulk_remove': 'users.remove_user', } - def get_queryset(self): - queryset = super().get_queryset().prefetch_related('groups') - return queryset - def allow_bulk_destroy(self, qs, filtered): is_valid = filtered.count() < qs.count() if not is_valid: diff --git a/apps/users/models/group.py b/apps/users/models/group.py index 914c22f75..4f3f612d3 100644 --- a/apps/users/models/group.py +++ b/apps/users/models/group.py @@ -1,15 +1,15 @@ # -*- coding: utf-8 -*- - from django.db import models from django.utils.translation import gettext_lazy as _ from common.utils import lazyproperty +from labels.mixins import LabeledMixin from orgs.mixins.models import JMSOrgBaseModel __all__ = ['UserGroup'] -class UserGroup(JMSOrgBaseModel): +class UserGroup(LabeledMixin, JMSOrgBaseModel): name = models.CharField(max_length=128, verbose_name=_('Name')) def __str__(self): diff --git a/apps/users/models/user.py b/apps/users/models/user.py index 4ce6dcddb..aca0c14a8 100644 --- a/apps/users/models/user.py +++ b/apps/users/models/user.py @@ -24,6 +24,7 @@ from common.utils import ( date_expired_default, get_logger, lazyproperty, random_string, bulk_create_with_signal ) +from labels.mixins import LabeledMixin from orgs.utils import current_org from rbac.const import Scope from ..signals import ( @@ -734,7 +735,7 @@ class JSONFilterMixin: return models.Q(id__in=user_id) -class User(AuthMixin, TokenMixin, RoleMixin, MFAMixin, JSONFilterMixin, AbstractUser): +class User(AuthMixin, TokenMixin, RoleMixin, MFAMixin, LabeledMixin, JSONFilterMixin, AbstractUser): class Source(models.TextChoices): local = 'local', _('Local') ldap = 'ldap', 'LDAP/AD' diff --git a/apps/users/serializers/group.py b/apps/users/serializers/group.py index af4e349e0..e05546bba 100644 --- a/apps/users/serializers/group.py +++ b/apps/users/serializers/group.py @@ -3,7 +3,8 @@ from django.db.models import Count from django.utils.translation import gettext_lazy as _ -from common.serializers.mixin import ObjectRelatedField +from common.serializers.fields import ObjectRelatedField +from common.serializers.mixin import ResourceLabelsMixin from orgs.mixins.serializers import BulkOrgResourceModelSerializer from .. import utils from ..models import User, UserGroup @@ -13,7 +14,7 @@ __all__ = [ ] -class UserGroupSerializer(BulkOrgResourceModelSerializer): +class UserGroupSerializer(ResourceLabelsMixin, BulkOrgResourceModelSerializer): users = ObjectRelatedField( required=False, many=True, queryset=User.objects, label=_('User'), ) @@ -24,7 +25,7 @@ class UserGroupSerializer(BulkOrgResourceModelSerializer): fields_small = fields_mini + [ 'comment', 'date_created', 'created_by' ] - fields = fields_mini + fields_small + ['users'] + fields = fields_mini + fields_small + ['users', 'labels'] extra_kwargs = { 'created_by': {'label': _('Created by'), 'read_only': True}, 'users_amount': {'label': _('Users amount')}, @@ -43,5 +44,6 @@ class UserGroupSerializer(BulkOrgResourceModelSerializer): @classmethod def setup_eager_loading(cls, queryset): """ Perform necessary eager loading of data. """ - queryset = queryset.prefetch_related('users').annotate(users_amount=Count('users')) + queryset = queryset.prefetch_related('users', 'labels', 'labels__label') \ + .annotate(users_amount=Count('users')) return queryset diff --git a/apps/users/serializers/profile.py b/apps/users/serializers/profile.py index 61869d95b..eacc0bff3 100644 --- a/apps/users/serializers/profile.py +++ b/apps/users/serializers/profile.py @@ -143,9 +143,9 @@ class UserProfileSerializer(UserSerializer): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) system_roles_field = self.fields.get('system_roles') - org_roles_field = self.fields.get('org_roles') if system_roles_field: system_roles_field.read_only = True + org_roles_field = self.fields.get('org_roles') if org_roles_field: org_roles_field.read_only = True diff --git a/apps/users/serializers/user.py b/apps/users/serializers/user.py index 269672ab0..9d68189d4 100644 --- a/apps/users/serializers/user.py +++ b/apps/users/serializers/user.py @@ -6,7 +6,7 @@ from functools import partial from django.utils.translation import gettext_lazy as _ from rest_framework import serializers -from common.serializers import CommonBulkSerializerMixin +from common.serializers import CommonBulkSerializerMixin, ResourceLabelsMixin from common.serializers.fields import ( EncryptedField, ObjectRelatedField, LabeledChoiceField, PhoneField ) @@ -81,7 +81,7 @@ class RolesSerializerMixin(serializers.Serializer): return fields -class UserSerializer(RolesSerializerMixin, CommonBulkSerializerMixin, serializers.ModelSerializer): +class UserSerializer(RolesSerializerMixin, CommonBulkSerializerMixin, ResourceLabelsMixin, serializers.ModelSerializer): password_strategy = LabeledChoiceField( choices=PasswordStrategy.choices, default=PasswordStrategy.email, @@ -143,7 +143,7 @@ class UserSerializer(RolesSerializerMixin, CommonBulkSerializerMixin, serializer # 外键的字段 fields_fk = [] # 多对多字段 - fields_m2m = ["groups", "system_roles", "org_roles", ] + fields_m2m = ["groups", "system_roles", "org_roles", "labels"] # 在serializer 上定义的字段 fields_custom = ["login_blocked", "password_strategy"] fields = fields_verbose + fields_fk + fields_m2m + fields_custom @@ -259,6 +259,11 @@ class UserSerializer(RolesSerializerMixin, CommonBulkSerializerMixin, serializer ) return instance + @classmethod + def setup_eager_loading(cls, queryset): + queryset = queryset.prefetch_related('groups', 'labels', 'labels__label') + return queryset + class UserRetrieveSerializer(UserSerializer): login_confirm_settings = serializers.PrimaryKeyRelatedField( diff --git a/poetry.lock b/poetry.lock index 889ea6e37..3bf68f9c9 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1919,6 +1919,25 @@ type = "legacy" url = "https://pypi.tuna.tsinghua.edu.cn/simple" reference = "tsinghua" +[[package]] +name = "django-cors-headers" +version = "4.3.0" +description = "django-cors-headers is a Django application for handling the server headers required for Cross-Origin Resource Sharing (CORS)." +optional = false +python-versions = ">=3.8" +files = [ + {file = "django_cors_headers-4.3.0-py3-none-any.whl", hash = "sha256:bd36c7aea0d070e462f3383f0dc9ef717e5fdc2b10a99c98c285f16da84ffba2"}, + {file = "django_cors_headers-4.3.0.tar.gz", hash = "sha256:25aabc94d4837678c1edf442c7f68a5f5fd151f6767b0e0b01c61a2179d02711"}, +] + +[package.dependencies] +Django = ">=3.2" + +[package.source] +type = "legacy" +url = "https://pypi.tuna.tsinghua.edu.cn/simple" +reference = "tsinghua" + [[package]] name = "django-debug-toolbar" version = "4.1.0" diff --git a/pyproject.toml b/pyproject.toml index c26fc8c68..eff9f4444 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -142,6 +142,7 @@ channels-redis = "4.1.0" fido2 = "^1.1.2" ua-parser = "^0.18.0" user-agents = "^2.2.0" +django-cors-headers = "^4.3.0" mistune = "0.8.4" openai = "^1.3.7" From d2eacad97b133e9d843dec5b462d86ba8d9d7ea2 Mon Sep 17 00:00:00 2001 From: halo Date: Sat, 2 Dec 2023 14:42:34 +0800 Subject: [PATCH 027/111] =?UTF-8?q?perf:=20=E6=9B=B4=E6=96=B0=E5=AE=A2?= =?UTF-8?q?=E6=88=B7=E7=AB=AF=20v2.1.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/templates/resource_download.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/templates/resource_download.html b/apps/templates/resource_download.html index e1e49f124..63300b28f 100644 --- a/apps/templates/resource_download.html +++ b/apps/templates/resource_download.html @@ -15,7 +15,7 @@ p {
    -

    JumpServer {% trans 'Client' %} v2.0.2

    +

    JumpServer {% trans 'Client' %} v2.1.0

    {% trans 'JumpServer Client, currently used to launch the client, now only support launch RDP SSH client, The Telnet client will next' %}

    From e812e3ff89ee7802a8ef2ae03a3f368773706196 Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 5 Dec 2023 14:29:08 +0800 Subject: [PATCH 028/111] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96=20endpoint=20?= =?UTF-8?q?=E7=9A=84=20ipv6=20=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/terminal/models/component/endpoint.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/terminal/models/component/endpoint.py b/apps/terminal/models/component/endpoint.py index 9b73dbea0..8e73bb17d 100644 --- a/apps/terminal/models/component/endpoint.py +++ b/apps/terminal/models/component/endpoint.py @@ -131,5 +131,11 @@ class EndpointRule(JMSBaseModel): endpoint = Endpoint.get_or_create_default(request) if not endpoint.host and request: # 动态添加 current request host - endpoint.host = request.get_host().split(':')[0] + host_port = request.get_host() + # IPv6 + if host_port.startswith('['): + host = host_port.split(']:')[0].rstrip(']') + ']' + else: + host = host_port.split(':')[0] + endpoint.host = host return endpoint From 0e7e499a1ecd8db3a31781e960c6b90f359b008c Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 5 Dec 2023 15:14:45 +0800 Subject: [PATCH 029/111] =?UTF-8?q?perf:=20=E4=BF=AE=E6=94=B9=20labels=20?= =?UTF-8?q?=E5=88=9B=E5=BB=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/common/serializers/mixin.py | 2 +- apps/labels/serializers.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/common/serializers/mixin.py b/apps/common/serializers/mixin.py index 1a749f6d3..d51849f07 100644 --- a/apps/common/serializers/mixin.py +++ b/apps/common/serializers/mixin.py @@ -394,7 +394,7 @@ class CommonBulkModelSerializer(CommonBulkSerializerMixin, serializers.ModelSeri class ResourceLabelsMixin(serializers.Serializer): - labels = LabelRelatedField(many=True, label=_('Labels'), ) + labels = LabelRelatedField(many=True, label=_('Labels'), required=False, allow_null=True) def update(self, instance, validated_data): labels = validated_data.pop('labels', None) diff --git a/apps/labels/serializers.py b/apps/labels/serializers.py index 476b3c06f..157afc92e 100644 --- a/apps/labels/serializers.py +++ b/apps/labels/serializers.py @@ -13,7 +13,7 @@ __all__ = ['LabelSerializer', 'LabeledResourceSerializer', 'ContentTypeResourceS class LabelSerializer(BulkOrgResourceModelSerializer): class Meta: model = Label - fields = ['id', 'name', 'value', 'res_count', 'date_created', 'date_updated'] + fields = ['id', 'name', 'value', 'res_count', 'comment', 'date_created', 'date_updated'] read_only_fields = ('date_created', 'date_updated', 'res_count') extra_kwargs = { 'res_count': {'label': _('Resource count')}, From ffe3e8a70c0b29d9b7e43bb753d7bdb7f73f7f44 Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 5 Dec 2023 16:04:16 +0800 Subject: [PATCH 030/111] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=20for=20tidb?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/jumpserver/rewriting/db.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/jumpserver/rewriting/db.py b/apps/jumpserver/rewriting/db.py index 1ccd49d8f..ddb29e538 100644 --- a/apps/jumpserver/rewriting/db.py +++ b/apps/jumpserver/rewriting/db.py @@ -26,7 +26,7 @@ class OneToOneField(models.OneToOneField, ForeignKey): def set_db_constraint(): if os.getenv('DB_CONSTRAINT', '1') != '0': return - if len(sys.argv) == 2 and sys.argv[1] == 'makemigrations': + if len(sys.argv) == 2 and sys.argv[1] in ['makemigrations', 'check']: return print("Set foreignkey db constraint False") transaction.atomic = atomic From a43bb25b5a993135c36984dac9233d200ca1ea4b Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 5 Dec 2023 11:39:19 +0800 Subject: [PATCH 031/111] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=20applet=20?= =?UTF-8?q?=E8=B4=A6=E5=8F=B7=E9=80=89=E6=8B=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/terminal/models/applet/applet.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/terminal/models/applet/applet.py b/apps/terminal/models/applet/applet.py index 661078011..c018275ac 100644 --- a/apps/terminal/models/applet/applet.py +++ b/apps/terminal/models/applet/applet.py @@ -223,7 +223,9 @@ class Applet(JMSBaseModel): accounts = valid_accounts.exclude(username__in=accounts_username_used) public_accounts = accounts.filter(username__startswith='jms_') if not public_accounts: - public_accounts = accounts.exclude(username__in=['Administrator', 'root']) + public_accounts = accounts \ + .exclude(username__in=['Administrator', 'root']) \ + .exclude(username__startswith='js_') account = self.random_select_prefer_account(user, host, public_accounts) return account From d2429f78832ff2b4523fce9edfb6563ab55f211b Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Tue, 5 Dec 2023 16:52:11 +0800 Subject: [PATCH 032/111] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=20virtual=20?= =?UTF-8?q?app=20(#12199)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 支持 virtual app * perf: 增加 virtual host * perf: 新增 virtual app 上传接口 * perf: 更名为 app provider * perf: 优化代码 --------- Co-authored-by: Eric --- apps/authentication/api/connection_token.py | 13 ++- .../authentication/models/connection_token.py | 11 +- .../serializers/connect_token_secret.py | 10 +- apps/jumpserver/conf.py | 1 + .../rewriting/storage/permissions.py | 1 + apps/jumpserver/settings/custom.py | 2 + apps/settings/serializers/public.py | 1 + apps/terminal/api/__init__.py | 5 +- apps/terminal/api/virtualapp/__init__.py | 3 + apps/terminal/api/virtualapp/provider.py | 63 +++++++++++ apps/terminal/api/virtualapp/relation.py | 64 +++++++++++ apps/terminal/api/virtualapp/virtualapp.py | 77 +++++++++++++ apps/terminal/connect_methods.py | 28 +++++ apps/terminal/const.py | 1 + apps/terminal/migrations/0068_virtualapp.py | 93 ++++++++++++++++ apps/terminal/models/__init__.py | 1 + apps/terminal/models/virtualapp/__init__.py | 2 + apps/terminal/models/virtualapp/provider.py | 28 +++++ apps/terminal/models/virtualapp/virtualapp.py | 103 ++++++++++++++++++ apps/terminal/serializers/__init__.py | 2 + apps/terminal/serializers/virtualapp.py | 41 +++++++ .../serializers/virtualapp_provider.py | 31 ++++++ apps/terminal/signal_handlers/__init__.py | 1 + apps/terminal/signal_handlers/virtualapp.py | 24 ++++ apps/terminal/urls/api_urls.py | 4 + 25 files changed, 605 insertions(+), 5 deletions(-) create mode 100644 apps/terminal/api/virtualapp/__init__.py create mode 100644 apps/terminal/api/virtualapp/provider.py create mode 100644 apps/terminal/api/virtualapp/relation.py create mode 100644 apps/terminal/api/virtualapp/virtualapp.py create mode 100644 apps/terminal/migrations/0068_virtualapp.py create mode 100644 apps/terminal/models/virtualapp/__init__.py create mode 100644 apps/terminal/models/virtualapp/provider.py create mode 100644 apps/terminal/models/virtualapp/virtualapp.py create mode 100644 apps/terminal/serializers/virtualapp.py create mode 100644 apps/terminal/serializers/virtualapp_provider.py create mode 100644 apps/terminal/signal_handlers/virtualapp.py diff --git a/apps/authentication/api/connection_token.py b/apps/authentication/api/connection_token.py index 954fb6074..2158a25f1 100644 --- a/apps/authentication/api/connection_token.py +++ b/apps/authentication/api/connection_token.py @@ -33,7 +33,7 @@ from ..models import ConnectionToken, date_expired_default from ..serializers import ( ConnectionTokenSerializer, ConnectionTokenSecretSerializer, SuperConnectionTokenSerializer, ConnectTokenAppletOptionSerializer, - ConnectionTokenReusableSerializer, + ConnectionTokenReusableSerializer, ConnectTokenVirtualAppOptionSerializer ) __all__ = ['ConnectionTokenViewSet', 'SuperConnectionTokenViewSet'] @@ -464,6 +464,7 @@ class SuperConnectionTokenViewSet(ConnectionTokenViewSet): 'get_secret_detail': 'authentication.view_superconnectiontokensecret', 'get_applet_info': 'authentication.view_superconnectiontoken', 'release_applet_account': 'authentication.view_superconnectiontoken', + 'get_virtual_app_info': 'authentication.view_superconnectiontoken', } def get_queryset(self): @@ -529,6 +530,16 @@ class SuperConnectionTokenViewSet(ConnectionTokenViewSet): serializer = ConnectTokenAppletOptionSerializer(data) return Response(serializer.data) + @action(methods=['POST'], detail=False, url_path='virtual-app-option') + def get_virtual_app_info(self, *args, **kwargs): + token_id = self.request.data.get('id') + token = get_object_or_404(ConnectionToken, pk=token_id) + if token.is_expired: + return Response({'error': 'Token expired'}, status=status.HTTP_400_BAD_REQUEST) + data = token.get_virtual_app_option() + serializer = ConnectTokenVirtualAppOptionSerializer(data) + return Response(serializer.data) + @action(methods=['DELETE', 'POST'], detail=False, url_path='applet-account/release') def release_applet_account(self, *args, **kwargs): account_id = self.request.data.get('id') diff --git a/apps/authentication/models/connection_token.py b/apps/authentication/models/connection_token.py index 48c8e8e16..6cd5648db 100644 --- a/apps/authentication/models/connection_token.py +++ b/apps/authentication/models/connection_token.py @@ -18,7 +18,7 @@ from common.utils import lazyproperty, pretty_string, bulk_get from common.utils.timezone import as_current_tz from orgs.mixins.models import JMSOrgBaseModel from orgs.utils import tmp_to_org -from terminal.models import Applet +from terminal.models import Applet, VirtualApp def date_expired_default(): @@ -177,6 +177,15 @@ class ConnectionToken(JMSOrgBaseModel): } return options + def get_virtual_app_option(self): + method = self.connect_method_object + if not method or method.get('type') != 'virtual_app' or method.get('disabled', False): + return None + virtual_app = VirtualApp.objects.filter(name=method.get('value')).first() + if not virtual_app: + return None + return virtual_app + def get_applet_option(self): method = self.connect_method_object if not method or method.get('type') != 'applet' or method.get('disabled', False): diff --git a/apps/authentication/serializers/connect_token_secret.py b/apps/authentication/serializers/connect_token_secret.py index 6685316bd..3eea79e4e 100644 --- a/apps/authentication/serializers/connect_token_secret.py +++ b/apps/authentication/serializers/connect_token_secret.py @@ -15,7 +15,8 @@ from users.models import User from ..models import ConnectionToken __all__ = [ - 'ConnectionTokenSecretSerializer', 'ConnectTokenAppletOptionSerializer' + 'ConnectionTokenSecretSerializer', 'ConnectTokenAppletOptionSerializer', + 'ConnectTokenVirtualAppOptionSerializer', ] @@ -161,3 +162,10 @@ class ConnectTokenAppletOptionSerializer(serializers.Serializer): account = _ConnectionTokenAccountSerializer(read_only=True) gateway = _ConnectionTokenGatewaySerializer(read_only=True) remote_app_option = serializers.JSONField(read_only=True) + + +class ConnectTokenVirtualAppOptionSerializer(serializers.Serializer): + name = serializers.CharField(label=_('Name')) + image_name = serializers.CharField(label=_('Image name')) + image_port = serializers.IntegerField(label=_('Image port')) + image_protocol = serializers.CharField(label=_('Image protocol')) diff --git a/apps/jumpserver/conf.py b/apps/jumpserver/conf.py index 45d21aeab..7492f5c4d 100644 --- a/apps/jumpserver/conf.py +++ b/apps/jumpserver/conf.py @@ -598,6 +598,7 @@ class Config(dict): 'GPT_BASE_URL': '', 'GPT_PROXY': '', 'GPT_MODEL': 'gpt-3.5-turbo', + 'VIRTUAL_APP_ENABLED': False, } old_config_map = { diff --git a/apps/jumpserver/rewriting/storage/permissions.py b/apps/jumpserver/rewriting/storage/permissions.py index 7af0adece..511545865 100644 --- a/apps/jumpserver/rewriting/storage/permissions.py +++ b/apps/jumpserver/rewriting/storage/permissions.py @@ -5,6 +5,7 @@ path_perms_map = { 'settings': '*', 'replay': 'default', 'applets': 'terminal.view_applet', + 'virtual_apps': 'terminal.view_virtualapp', 'playbooks': 'ops.view_playbook' } diff --git a/apps/jumpserver/settings/custom.py b/apps/jumpserver/settings/custom.py index fa83fa9fd..d9eb41332 100644 --- a/apps/jumpserver/settings/custom.py +++ b/apps/jumpserver/settings/custom.py @@ -219,3 +219,5 @@ GPT_API_KEY = CONFIG.GPT_API_KEY GPT_BASE_URL = CONFIG.GPT_BASE_URL GPT_PROXY = CONFIG.GPT_PROXY GPT_MODEL = CONFIG.GPT_MODEL + +VIRTUAL_APP_ENABLED = CONFIG.VIRTUAL_APP_ENABLED diff --git a/apps/settings/serializers/public.py b/apps/settings/serializers/public.py index 1b5c78609..21dc2c2f8 100644 --- a/apps/settings/serializers/public.py +++ b/apps/settings/serializers/public.py @@ -53,6 +53,7 @@ class PrivateSettingSerializer(PublicSettingSerializer): CONNECTION_TOKEN_REUSABLE = serializers.BooleanField() CACHE_LOGIN_PASSWORD_ENABLED = serializers.BooleanField() VAULT_ENABLED = serializers.BooleanField() + VIRTUAL_APP_ENABLED = serializers.BooleanField() class ServerInfoSerializer(serializers.Serializer): diff --git a/apps/terminal/api/__init__.py b/apps/terminal/api/__init__.py index c4a60efb6..b6afedd51 100644 --- a/apps/terminal/api/__init__.py +++ b/apps/terminal/api/__init__.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- # -from .session import * -from .component import * from .applet import * +from .component import * from .db_listen_port import * +from .session import * +from .virtualapp import * diff --git a/apps/terminal/api/virtualapp/__init__.py b/apps/terminal/api/virtualapp/__init__.py new file mode 100644 index 000000000..0c67de93f --- /dev/null +++ b/apps/terminal/api/virtualapp/__init__.py @@ -0,0 +1,3 @@ +from .provider import * +from .relation import * +from .virtualapp import * diff --git a/apps/terminal/api/virtualapp/provider.py b/apps/terminal/api/virtualapp/provider.py new file mode 100644 index 000000000..fbdf11ecc --- /dev/null +++ b/apps/terminal/api/virtualapp/provider.py @@ -0,0 +1,63 @@ +from django.core.cache import cache +from rest_framework.decorators import action +from rest_framework.exceptions import ValidationError +from rest_framework.response import Response + +from common.api import JMSBulkModelViewSet +from common.permissions import IsServiceAccount +from orgs.utils import tmp_to_builtin_org +from terminal.models import AppProvider +from terminal.serializers import ( + AppProviderSerializer, AppProviderContainerSerializer +) + +__all__ = ['AppProviderViewSet', ] + + +class AppProviderViewSet(JMSBulkModelViewSet): + serializer_class = AppProviderSerializer + queryset = AppProvider.objects.all() + search_fields = ['name', 'hostname', ] + rbac_perms = { + 'containers': 'terminal.view_appprovider', + 'status': 'terminal.view_appprovider', + } + + cache_status_key_prefix = 'virtual_host_{}_status' + + def dispatch(self, request, *args, **kwargs): + with tmp_to_builtin_org(system=1): + return super().dispatch(request, *args, **kwargs) + + def get_permissions(self): + if self.action == 'create': + return [IsServiceAccount()] + return super().get_permissions() + + def perform_create(self, serializer): + request_terminal = getattr(self.request.user, 'terminal', None) + if not request_terminal: + raise ValidationError('Request user has no terminal') + data = dict() + data['terminal'] = request_terminal + data['id'] = self.request.user.id + serializer.save(**data) + + @action(detail=True, methods=['get'], serializer_class=AppProviderContainerSerializer) + def containers(self, request, *args, **kwargs): + instance = self.get_object() + key = self.cache_status_key_prefix.format(instance.id) + data = cache.get(key) + if not data: + data = [] + return self.get_paginated_response_from_queryset(data) + + @action(detail=True, methods=['post'], serializer_class=AppProviderContainerSerializer) + def status(self, request, *args, **kwargs): + instance = self.get_object() + serializer = self.get_serializer(data=request.data, many=True) + serializer.is_valid(raise_exception=True) + validated_data = serializer.validated_data + key = self.cache_status_key_prefix.format(instance.id) + cache.set(key, validated_data, 60 * 3) + return Response({'msg': 'ok'}) diff --git a/apps/terminal/api/virtualapp/relation.py b/apps/terminal/api/virtualapp/relation.py new file mode 100644 index 000000000..9c15a0e0d --- /dev/null +++ b/apps/terminal/api/virtualapp/relation.py @@ -0,0 +1,64 @@ +from typing import Callable + +from django.conf import settings +from django.shortcuts import get_object_or_404 +from rest_framework.request import Request + +from common.api import JMSModelViewSet +from common.permissions import IsServiceAccount +from common.utils import is_uuid +from rbac.permissions import RBACPermission +from terminal.models import AppProvider +from terminal.serializers import ( + VirtualAppPublicationSerializer +) + + +class ProviderMixin: + request: Request + permission_denied: Callable + kwargs: dict + rbac_perms = ( + ('list', 'terminal.view_appprovider'), + ('retrieve', 'terminal.view_appprovider'), + ) + + def get_permissions(self): + if self.kwargs.get('host') and settings.DEBUG: + return [RBACPermission()] + else: + return [IsServiceAccount()] + + def self_provider(self): + try: + return self.request.user.terminal.app_provider + except AttributeError: + raise self.permission_denied(self.request, 'User has no app provider') + + def pk_provider(self): + return get_object_or_404(AppProvider, id=self.kwargs.get('provider')) + + @property + def provider(self): + if self.kwargs.get('provider'): + host = self.pk_provider() + else: + host = self.self_provider() + return host + + +class AppProviderAppViewSet(ProviderMixin, JMSModelViewSet): + provider: AppProvider + serializer_class = VirtualAppPublicationSerializer + filterset_fields = ['provider__name', 'app__name', 'status'] + + def get_object(self): + pk = self.kwargs.get('pk') + if not is_uuid(pk): + return self.provider.publications.get(app__name=pk) + else: + return self.provider.publications.get(id=pk) + + def get_queryset(self): + queryset = self.provider.publications.all() + return queryset diff --git a/apps/terminal/api/virtualapp/virtualapp.py b/apps/terminal/api/virtualapp/virtualapp.py new file mode 100644 index 000000000..07b92a356 --- /dev/null +++ b/apps/terminal/api/virtualapp/virtualapp.py @@ -0,0 +1,77 @@ +import os.path +import shutil +import zipfile +from typing import Callable + +from django.core.files.storage import default_storage +from django.utils._os import safe_join +from django.utils.translation import gettext as _ +from rest_framework import viewsets +from rest_framework.decorators import action +from rest_framework.request import Request +from rest_framework.response import Response +from rest_framework.serializers import ValidationError + +from common.api import JMSBulkModelViewSet +from common.serializers import FileSerializer +from terminal import serializers +from terminal.models import VirtualAppPublication, VirtualApp + +__all__ = ['VirtualAppViewSet', 'VirtualAppPublicationViewSet'] + + +class UploadMixin: + get_serializer: Callable + request: Request + get_object: Callable + + def extract_zip_pkg(self): + serializer = self.get_serializer(data=self.request.data) + serializer.is_valid(raise_exception=True) + file = serializer.validated_data['file'] + save_to = 'virtual_apps/{}'.format(file.name + '.tmp.zip') + if default_storage.exists(save_to): + default_storage.delete(save_to) + rel_path = default_storage.save(save_to, file) + path = default_storage.path(rel_path) + extract_to = default_storage.path('virtual_apps/{}.tmp'.format(file.name)) + if os.path.exists(extract_to): + shutil.rmtree(extract_to) + try: + with zipfile.ZipFile(path) as zp: + if zp.testzip() is not None: + raise ValidationError({'error': _('Invalid zip file')}) + zp.extractall(extract_to) + except RuntimeError as e: + raise ValidationError({'error': _('Invalid zip file') + ': {}'.format(e)}) + tmp_dir = safe_join(extract_to, file.name.replace('.zip', '')) + return tmp_dir + + @action(detail=False, methods=['post'], serializer_class=FileSerializer) + def upload(self, request, *args, **kwargs): + tmp_dir = self.extract_zip_pkg() + manifest = VirtualApp.validate_pkg(tmp_dir) + name = manifest['name'] + instance = VirtualApp.objects.filter(name=name).first() + if instance: + return Response({'error': 'virtual app already exists: {}'.format(name)}, status=400) + + app, serializer = VirtualApp.install_from_dir(tmp_dir) + return Response(serializer.data, status=201) + + +class VirtualAppViewSet(UploadMixin, JMSBulkModelViewSet): + queryset = VirtualApp.objects.all() + serializer_class = serializers.VirtualAppSerializer + filterset_fields = ['name', 'image_name', 'is_active'] + search_fields = ['name', ] + rbac_perms = { + 'upload': 'terminal.add_virtualapp', + } + + +class VirtualAppPublicationViewSet(viewsets.ModelViewSet): + queryset = VirtualAppPublication.objects.all() + serializer_class = serializers.VirtualAppPublicationSerializer + filterset_fields = ['app__name', 'provider__name', 'status'] + search_fields = ['app__name', 'provider__name', ] diff --git a/apps/terminal/connect_methods.py b/apps/terminal/connect_methods.py index 9c7ee989a..27d67e1af 100644 --- a/apps/terminal/connect_methods.py +++ b/apps/terminal/connect_methods.py @@ -113,6 +113,26 @@ class AppletMethod: return methods +class VirtualAppMethod: + + @classmethod + def get_methods(cls): + from .models import VirtualApp + methods = defaultdict(list) + if not getattr(settings, 'VIRTUAL_APP_ENABLED'): + return methods + virtual_apps = VirtualApp.objects.filter(is_active=True) + for virtual_app in virtual_apps: + for protocol in virtual_app.protocols: + methods[protocol].append({ + 'value': virtual_app.name, + 'label': virtual_app.name, + 'type': 'virtual_app', + 'disabled': not virtual_app.is_active, + }) + return methods + + class ConnectMethodUtil: _all_methods = {} @@ -243,6 +263,7 @@ class ConnectMethodUtil: methods = defaultdict(list) spec_web_methods = WebMethod.get_spec_methods() applet_methods = AppletMethod.get_methods() + virtual_app_methods = VirtualAppMethod.get_methods() native_methods = NativeClient.get_methods(os=os) for component, component_protocol in cls.components().items(): @@ -295,5 +316,12 @@ class ConnectMethodUtil: method['component'] = TerminalType.tinker.value methods[asset_protocol].extend(applet_methods) + # 虚拟应用方式,这个只有 panda 提供,并且协议可能是自定义的 + for protocol, virtual_app_methods in virtual_app_methods.items(): + for method in virtual_app_methods: + method['listen'] = Protocol.http + method['component'] = TerminalType.panda.value + methods[protocol].extend(virtual_app_methods) + cls._all_methods[os] = methods return methods diff --git a/apps/terminal/const.py b/apps/terminal/const.py index a545a0a3a..1c07f36b7 100644 --- a/apps/terminal/const.py +++ b/apps/terminal/const.py @@ -66,6 +66,7 @@ class TerminalType(TextChoices): video_worker = 'video_worker', 'Video Worker' chen = 'chen', 'Chen' kael = 'kael', 'Kael' + panda = 'panda', 'Panda' @classmethod def types(cls): diff --git a/apps/terminal/migrations/0068_virtualapp.py b/apps/terminal/migrations/0068_virtualapp.py new file mode 100644 index 000000000..957514480 --- /dev/null +++ b/apps/terminal/migrations/0068_virtualapp.py @@ -0,0 +1,93 @@ +# Generated by Django 4.1.10 on 2023-12-05 07:02 + +from django.db import migrations, models +import django.db.models.deletion +import uuid + + +class Migration(migrations.Migration): + + dependencies = [ + ('terminal', '0067_alter_replaystorage_type'), + ] + + operations = [ + migrations.CreateModel( + name='AppProvider', + fields=[ + ('created_by', models.CharField(blank=True, max_length=128, null=True, verbose_name='Created by')), + ('updated_by', models.CharField(blank=True, max_length=128, null=True, verbose_name='Updated by')), + ('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')), + ('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')), + ('comment', models.TextField(blank=True, default='', verbose_name='Comment')), + ('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)), + ('name', models.CharField(max_length=128, unique=True, verbose_name='Name')), + ('hostname', models.CharField(max_length=128, verbose_name='Hostname')), + ], + options={ + 'ordering': ('-date_created',), + }, + ), + migrations.CreateModel( + name='VirtualApp', + fields=[ + ('created_by', models.CharField(blank=True, max_length=128, null=True, verbose_name='Created by')), + ('updated_by', models.CharField(blank=True, max_length=128, null=True, verbose_name='Updated by')), + ('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')), + ('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')), + ('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)), + ('name', models.SlugField(max_length=128, unique=True, verbose_name='Name')), + ('display_name', models.CharField(max_length=128, verbose_name='Display name')), + ('version', models.CharField(max_length=16, verbose_name='Version')), + ('author', models.CharField(max_length=128, verbose_name='Author')), + ('is_active', models.BooleanField(default=True, verbose_name='Is active')), + ('protocols', models.JSONField(default=list, verbose_name='Protocol')), + ('image_name', models.CharField(max_length=128, verbose_name='Image name')), + ('image_protocol', models.CharField(default='vnc', max_length=16, verbose_name='Image protocol')), + ('image_port', models.IntegerField(default=5900, verbose_name='Image port')), + ('comment', models.TextField(blank=True, default='', verbose_name='Comment')), + ('tags', models.JSONField(default=list, verbose_name='Tags')), + ], + options={ + 'verbose_name': 'Virtual app', + }, + ), + migrations.AlterField( + model_name='terminal', + name='type', + field=models.CharField(choices=[('koko', 'KoKo'), ('guacamole', 'Guacamole'), ('omnidb', 'OmniDB'), ('xrdp', 'Xrdp'), ('lion', 'Lion'), ('core', 'Core'), ('celery', 'Celery'), ('magnus', 'Magnus'), ('razor', 'Razor'), ('tinker', 'Tinker'), ('video_worker', 'Video Worker'), ('chen', 'Chen'), ('kael', 'Kael'), ('panda', 'Panda')], default='koko', max_length=64, verbose_name='type'), + ), + migrations.CreateModel( + name='VirtualAppPublication', + fields=[ + ('created_by', models.CharField(blank=True, max_length=128, null=True, verbose_name='Created by')), + ('updated_by', models.CharField(blank=True, max_length=128, null=True, verbose_name='Updated by')), + ('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')), + ('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')), + ('comment', models.TextField(blank=True, default='', verbose_name='Comment')), + ('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)), + ('status', models.CharField(default='pending', max_length=16, verbose_name='Status')), + ('app', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='publications', to='terminal.virtualapp', verbose_name='Virtual App')), + ('provider', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='publications', to='terminal.appprovider', verbose_name='App Provider')), + ], + options={ + 'verbose_name': 'Virtual app publication', + 'unique_together': {('provider', 'app')}, + }, + ), + migrations.AddField( + model_name='virtualapp', + name='providers', + field=models.ManyToManyField(through='terminal.VirtualAppPublication', to='terminal.appprovider', verbose_name='Providers'), + ), + migrations.AddField( + model_name='appprovider', + name='apps', + field=models.ManyToManyField(through='terminal.VirtualAppPublication', to='terminal.virtualapp', verbose_name='VirtualApp'), + ), + migrations.AddField( + model_name='appprovider', + name='terminal', + field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='app_provider', to='terminal.terminal', verbose_name='Terminal'), + ), + ] diff --git a/apps/terminal/models/__init__.py b/apps/terminal/models/__init__.py index 268727394..56fbd8cd9 100644 --- a/apps/terminal/models/__init__.py +++ b/apps/terminal/models/__init__.py @@ -1,3 +1,4 @@ from .session import * from .component import * from .applet import * +from .virtualapp import * diff --git a/apps/terminal/models/virtualapp/__init__.py b/apps/terminal/models/virtualapp/__init__.py new file mode 100644 index 000000000..f784f5c73 --- /dev/null +++ b/apps/terminal/models/virtualapp/__init__.py @@ -0,0 +1,2 @@ +from .provider import * +from .virtualapp import * diff --git a/apps/terminal/models/virtualapp/provider.py b/apps/terminal/models/virtualapp/provider.py new file mode 100644 index 000000000..90a8df2ef --- /dev/null +++ b/apps/terminal/models/virtualapp/provider.py @@ -0,0 +1,28 @@ +from django.db import models +from django.utils.translation import gettext_lazy as _ + +from common.db.models import JMSBaseModel + +__all__ = ['AppProvider', ] + + +class AppProvider(JMSBaseModel): + name = models.CharField(max_length=128, verbose_name=_('Name'), unique=True) + hostname = models.CharField(max_length=128, verbose_name=_('Hostname')) + terminal = models.OneToOneField( + 'terminal.Terminal', on_delete=models.CASCADE, null=True, blank=True, + related_name='app_provider', verbose_name=_('Terminal') + ) + apps = models.ManyToManyField( + 'VirtualApp', verbose_name=_('VirtualApp'), + through='VirtualAppPublication', through_fields=('provider', 'app'), + ) + + class Meta: + ordering = ('-date_created',) + + @property + def load(self): + if not self.terminal: + return 'offline' + return self.terminal.load diff --git a/apps/terminal/models/virtualapp/virtualapp.py b/apps/terminal/models/virtualapp/virtualapp.py new file mode 100644 index 000000000..6035c76c4 --- /dev/null +++ b/apps/terminal/models/virtualapp/virtualapp.py @@ -0,0 +1,103 @@ +import os +import shutil + +from django.conf import settings +from django.core.files.storage import default_storage +from django.db import models +from django.utils._os import safe_join +from django.utils.translation import gettext_lazy as _ +from rest_framework.serializers import ValidationError + +from common.db.models import JMSBaseModel +from common.utils import lazyproperty +from common.utils.yml import yaml_load_with_i18n + +__all__ = ['VirtualApp', 'VirtualAppPublication'] + + +class VirtualApp(JMSBaseModel): + name = models.SlugField(max_length=128, verbose_name=_('Name'), unique=True) + display_name = models.CharField(max_length=128, verbose_name=_('Display name')) + version = models.CharField(max_length=16, verbose_name=_('Version')) + author = models.CharField(max_length=128, verbose_name=_('Author')) + is_active = models.BooleanField(default=True, verbose_name=_('Is active')) + protocols = models.JSONField(default=list, verbose_name=_('Protocol')) + image_name = models.CharField(max_length=128, verbose_name=_('Image name')) + image_protocol = models.CharField(max_length=16, default='vnc', verbose_name=_('Image protocol')) + image_port = models.IntegerField(default=5900, verbose_name=_('Image port')) + comment = models.TextField(default='', blank=True, verbose_name=_('Comment')) + tags = models.JSONField(default=list, verbose_name=_('Tags')) + providers = models.ManyToManyField( + through_fields=('app', 'provider',), through='VirtualAppPublication', + to='AppProvider', verbose_name=_('Providers') + ) + + class Meta: + verbose_name = _('Virtual app') + + def __str__(self): + return self.name + + @property + def path(self): + return default_storage.path('virtual_apps/{}'.format(self.name)) + + @lazyproperty + def readme(self): + readme_file = os.path.join(self.path, 'README.md') + if os.path.isfile(readme_file): + with open(readme_file, 'r') as f: + return f.read() + return '' + + @property + def icon(self): + path = os.path.join(self.path, 'icon.png') + if not os.path.exists(path): + return None + return os.path.join(settings.MEDIA_URL, 'virtual_apps', self.name, 'icon.png') + + @staticmethod + def validate_pkg(d): + files = ['manifest.yml', 'icon.png', ] + for name in files: + path = safe_join(d, name) + if not os.path.exists(path): + raise ValidationError({'error': _('Applet pkg not valid, Missing file {}').format(name)}) + + with open(safe_join(d, 'manifest.yml'), encoding='utf8') as f: + manifest = yaml_load_with_i18n(f) + + if not manifest.get('name', ''): + raise ValidationError({'error': 'Missing name in manifest.yml'}) + return manifest + + @classmethod + def install_from_dir(cls, path): + from terminal.serializers import VirtualAppSerializer + manifest = cls.validate_pkg(path) + name = manifest['name'] + instance = cls.objects.filter(name=name).first() + serializer = VirtualAppSerializer(instance=instance, data=manifest) + serializer.is_valid(raise_exception=True) + instance = serializer.save() + + pkg_path = default_storage.path('virtual_apps/{}'.format(name)) + if os.path.exists(pkg_path): + shutil.rmtree(pkg_path) + shutil.copytree(path, pkg_path) + return instance, serializer + + +class VirtualAppPublication(JMSBaseModel): + provider = models.ForeignKey( + 'AppProvider', on_delete=models.CASCADE, related_name='publications', verbose_name=_('App Provider') + ) + app = models.ForeignKey( + 'VirtualApp', on_delete=models.CASCADE, related_name='publications', verbose_name=_('Virtual App') + ) + status = models.CharField(max_length=16, default='pending', verbose_name=_('Status')) + + class Meta: + verbose_name = _('Virtual app publication') + unique_together = ('provider', 'app') diff --git a/apps/terminal/serializers/__init__.py b/apps/terminal/serializers/__init__.py index dc23362f9..ba97ae16d 100644 --- a/apps/terminal/serializers/__init__.py +++ b/apps/terminal/serializers/__init__.py @@ -9,3 +9,5 @@ from .sharing import * from .storage import * from .task import * from .terminal import * +from .virtualapp import * +from .virtualapp_provider import * diff --git a/apps/terminal/serializers/virtualapp.py b/apps/terminal/serializers/virtualapp.py new file mode 100644 index 000000000..f55226eaf --- /dev/null +++ b/apps/terminal/serializers/virtualapp.py @@ -0,0 +1,41 @@ +from django.utils.translation import gettext_lazy as _ +from rest_framework import serializers + +from common.const.choices import Status +from common.serializers.fields import ObjectRelatedField, LabeledChoiceField +from terminal.const import PublishStatus +from ..models import VirtualApp, VirtualAppPublication, AppProvider + +__all__ = [ + 'VirtualAppSerializer', 'VirtualAppPublicationSerializer' +] + + +class VirtualAppSerializer(serializers.ModelSerializer): + icon = serializers.ReadOnlyField(label=_("Icon")) + image_protocol = serializers.CharField(max_length=16, default='vnc') + image_port = serializers.IntegerField(default=5900) + + class Meta: + model = VirtualApp + fields_mini = ['id', 'display_name', 'name', 'image_name', 'is_active'] + read_only_fields = [ + 'icon', 'readme', 'date_created', 'date_updated', + ] + fields = fields_mini + [ + 'version', 'author', 'image_protocol', 'image_port', + 'protocols', 'tags', 'comment', + ] + read_only_fields + + +class VirtualAppPublicationSerializer(serializers.ModelSerializer): + app = ObjectRelatedField(attrs=('id', 'name', 'image_name',), label=_("Virtual App"), + queryset=VirtualApp.objects.all()) + provider = ObjectRelatedField(queryset=AppProvider.objects.all(), label=_("App Provider")) + status = LabeledChoiceField(choices=PublishStatus.choices, label=_("Status"), default=Status.pending) + + class Meta: + model = VirtualAppPublication + fields_mini = ['id', 'provider', 'app'] + read_only_fields = ['date_created', 'date_updated'] + fields = fields_mini + ['status', 'comment'] + read_only_fields diff --git a/apps/terminal/serializers/virtualapp_provider.py b/apps/terminal/serializers/virtualapp_provider.py new file mode 100644 index 000000000..bfce5e081 --- /dev/null +++ b/apps/terminal/serializers/virtualapp_provider.py @@ -0,0 +1,31 @@ +from django.utils.translation import gettext_lazy as _ +from rest_framework import serializers + +from common.serializers.fields import LabeledChoiceField +from terminal import const +from ..models import AppProvider + +__all__ = ['AppProviderSerializer', 'AppProviderContainerSerializer', ] + + +class AppProviderSerializer(serializers.ModelSerializer): + load = LabeledChoiceField( + read_only=True, label=_('Load status'), choices=const.ComponentLoad.choices, + ) + + class Meta: + model = AppProvider + field_mini = ['id', 'name', 'hostname'] + read_only_fields = [ + 'date_created', 'date_updated', + ] + fields = field_mini + ['load', 'terminal'] + read_only_fields + + +class AppProviderContainerSerializer(serializers.Serializer): + container_id = serializers.CharField(label=_('Container ID')) + container_image = serializers.CharField(label=_('Container Image')) + container_name = serializers.CharField(label=_('Container Name')) + container_status = serializers.CharField(label=_('Container Status')) + container_ports = serializers.ListField(child=serializers.CharField(), label=_('Container Ports')) + diff --git a/apps/terminal/signal_handlers/__init__.py b/apps/terminal/signal_handlers/__init__.py index bd61c885c..08a6ac407 100644 --- a/apps/terminal/signal_handlers/__init__.py +++ b/apps/terminal/signal_handlers/__init__.py @@ -3,3 +3,4 @@ from .db_port import * from .session import * from .session_sharing import * from .terminal import * +from .virtualapp import * diff --git a/apps/terminal/signal_handlers/virtualapp.py b/apps/terminal/signal_handlers/virtualapp.py new file mode 100644 index 000000000..37c4ed1a1 --- /dev/null +++ b/apps/terminal/signal_handlers/virtualapp.py @@ -0,0 +1,24 @@ +from django.db.models.signals import post_save +from django.dispatch import receiver + +from common.decorators import on_transaction_commit +from ..models import AppProvider, VirtualApp + + +@receiver(post_save, sender=AppProvider) +@on_transaction_commit +def on_virtual_host_create(sender, instance, created=False, **kwargs): + if not created: + return + apps = VirtualApp.objects.all() + instance.apps.set(apps) + + +@receiver(post_save, sender=VirtualApp) +def on_virtual_app_create(sender, instance, created=False, **kwargs): + if not created: + return + providers = AppProvider.objects.all() + if len(providers) == 0: + return + instance.providers.set(providers) diff --git a/apps/terminal/urls/api_urls.py b/apps/terminal/urls/api_urls.py index 3c38bb934..258e2f0d1 100644 --- a/apps/terminal/urls/api_urls.py +++ b/apps/terminal/urls/api_urls.py @@ -30,6 +30,10 @@ router.register(r'applet-hosts', api.AppletHostViewSet, 'applet-host') router.register(r'applet-publications', api.AppletPublicationViewSet, 'applet-publication') router.register(r'applet-host-deployments', api.AppletHostDeploymentViewSet, 'applet-host-deployment') router.register(r'db-listen-ports', api.DBListenPortViewSet, 'db-listen-ports') +router.register(r'virtual-apps', api.VirtualAppViewSet, 'virtual-app') +router.register(r'app-providers', api.AppProviderViewSet, 'app-provider') +router.register(r'app-providers/((?P[^/.]+)/)?apps', api.AppProviderAppViewSet, 'app-provider-app') +router.register(r'virtual-app-publications', api.VirtualAppPublicationViewSet, 'virtual-app-publication') urlpatterns = [ path('my-sessions/', api.MySessionAPIView.as_view(), name='my-session'), From e193d7a942191d8ebd1096ff6c2fcb9adc21d340 Mon Sep 17 00:00:00 2001 From: Eric Date: Tue, 5 Dec 2023 17:14:35 +0800 Subject: [PATCH 033/111] =?UTF-8?q?perf:=20=E5=AE=8C=E5=96=84=20yaml=20?= =?UTF-8?q?=E5=8A=A0=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/automations/methods.py | 2 +- apps/common/utils/yml.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/assets/automations/methods.py b/apps/assets/automations/methods.py index b0ad883fd..1453cc7a1 100644 --- a/apps/assets/automations/methods.py +++ b/apps/assets/automations/methods.py @@ -40,7 +40,7 @@ def get_platform_automation_methods(path, lang=None): continue with open(path, 'r', encoding='utf8') as f: - manifest = yaml_load_with_i18n(f, lang) + manifest = yaml_load_with_i18n(f, lang=lang) check_platform_method(manifest, path) manifest['dir'] = os.path.dirname(path) manifest['params_serializer'] = generate_serializer(manifest) diff --git a/apps/common/utils/yml.py b/apps/common/utils/yml.py index c5221b948..4250f016e 100644 --- a/apps/common/utils/yml.py +++ b/apps/common/utils/yml.py @@ -12,7 +12,7 @@ def translate(key, i18n, lang): return lang_data.get(lang, key) -def yaml_load_with_i18n(stream, lang): +def yaml_load_with_i18n(stream, lang=None): ori_text = stream.read() stream = io.StringIO(ori_text) yaml_data = yaml.safe_load(stream) From 8f82ca98566b6f2ff56544723517c68056be3c07 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Tue, 5 Dec 2023 17:26:47 +0800 Subject: [PATCH 034/111] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E6=93=8D?= =?UTF-8?q?=E4=BD=9C=E6=97=A5=E5=BF=97=20(#12249)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * perf: 优化操作日志 * perf: 修改migrations中关于Nodes的verbose_name * perf: 优化代码逻辑 * perf: 优化日志详情展示逻辑 * perf: 代码优雅一下 --------- Co-authored-by: jiangweidong --- ...5_1807_squashed_0009_auto_20180307_1212.py | 2 +- .../migrations/0007_auto_20180225_1815.py | 2 +- apps/assets/migrations/0107_automation.py | 2 +- apps/assets/models/asset/common.py | 2 +- apps/assets/models/automations/base.py | 2 +- apps/assets/models/cmd_filter.py | 2 +- apps/audits/backends/db.py | 20 ++++++-- apps/audits/serializers.py | 5 +- apps/audits/signal_handlers/operate_log.py | 6 ++- apps/audits/utils.py | 46 +++++++++++-------- apps/jumpserver/conf.py | 2 +- ...8_0025_squashed_0009_auto_20180903_1132.py | 2 +- apps/perms/models/asset_permission.py | 2 +- apps/terminal/models/component/task.py | 3 ++ 14 files changed, 58 insertions(+), 40 deletions(-) diff --git a/apps/assets/migrations/0002_auto_20180105_1807_squashed_0009_auto_20180307_1212.py b/apps/assets/migrations/0002_auto_20180105_1807_squashed_0009_auto_20180307_1212.py index 7cbf78a43..6392158fd 100644 --- a/apps/assets/migrations/0002_auto_20180105_1807_squashed_0009_auto_20180307_1212.py +++ b/apps/assets/migrations/0002_auto_20180105_1807_squashed_0009_auto_20180307_1212.py @@ -123,7 +123,7 @@ class Migration(migrations.Migration): migrations.AddField( model_name='asset', name='nodes', - field=models.ManyToManyField(default=assets.models.asset.default_node, related_name='assets', to='assets.Node', verbose_name='Nodes'), + field=models.ManyToManyField(default=assets.models.asset.default_node, related_name='assets', to='assets.Node', verbose_name='Node'), ), migrations.AddField( model_name='systemuser', diff --git a/apps/assets/migrations/0007_auto_20180225_1815.py b/apps/assets/migrations/0007_auto_20180225_1815.py index 4ce2b1e05..1097dd182 100644 --- a/apps/assets/migrations/0007_auto_20180225_1815.py +++ b/apps/assets/migrations/0007_auto_20180225_1815.py @@ -50,7 +50,7 @@ class Migration(migrations.Migration): migrations.AddField( model_name='asset', name='nodes', - field=models.ManyToManyField(default=assets.models.default_node, related_name='assets', to='assets.Node', verbose_name='Nodes'), + field=models.ManyToManyField(default=assets.models.default_node, related_name='assets', to='assets.Node', verbose_name='Node'), ), migrations.AddField( model_name='systemuser', diff --git a/apps/assets/migrations/0107_automation.py b/apps/assets/migrations/0107_automation.py index 56c2cf4eb..6841cb912 100644 --- a/apps/assets/migrations/0107_automation.py +++ b/apps/assets/migrations/0107_automation.py @@ -31,7 +31,7 @@ class Migration(migrations.Migration): ('type', models.CharField(max_length=16, verbose_name='Type')), ('is_active', models.BooleanField(default=True, verbose_name='Is active')), ('assets', models.ManyToManyField(blank=True, to='assets.Asset', verbose_name='Assets')), - ('nodes', models.ManyToManyField(blank=True, to='assets.Node', verbose_name='Nodes')), + ('nodes', models.ManyToManyField(blank=True, to='assets.Node', verbose_name='Node')), ], options={ 'verbose_name': 'Automation task', diff --git a/apps/assets/models/asset/common.py b/apps/assets/models/asset/common.py index 93294fd5b..558164df6 100644 --- a/apps/assets/models/asset/common.py +++ b/apps/assets/models/asset/common.py @@ -162,7 +162,7 @@ class Asset(NodesRelationMixin, LabeledMixin, AbsConnectivity, JSONFilterMixin, domain = models.ForeignKey("assets.Domain", null=True, blank=True, related_name='assets', verbose_name=_("Domain"), on_delete=models.SET_NULL) nodes = models.ManyToManyField('assets.Node', default=default_node, related_name='assets', - verbose_name=_("Nodes")) + verbose_name=_("Node")) is_active = models.BooleanField(default=True, verbose_name=_('Is active')) gathered_info = models.JSONField(verbose_name=_('Gathered info'), default=dict, blank=True) # 资产的一些信息,如 硬件信息 custom_info = models.JSONField(verbose_name=_('Custom info'), default=dict) diff --git a/apps/assets/models/automations/base.py b/apps/assets/models/automations/base.py index 27e5ed74a..c3e1fa639 100644 --- a/apps/assets/models/automations/base.py +++ b/apps/assets/models/automations/base.py @@ -15,7 +15,7 @@ from orgs.mixins.models import OrgModelMixin, JMSOrgBaseModel class BaseAutomation(PeriodTaskModelMixin, JMSOrgBaseModel): accounts = models.JSONField(default=list, verbose_name=_("Accounts")) - nodes = models.ManyToManyField('assets.Node', blank=True, verbose_name=_("Nodes")) + nodes = models.ManyToManyField('assets.Node', blank=True, verbose_name=_("Node")) assets = models.ManyToManyField('assets.Asset', blank=True, verbose_name=_("Assets")) type = models.CharField(max_length=16, verbose_name=_('Type')) is_active = models.BooleanField(default=True, verbose_name=_("Is active")) diff --git a/apps/assets/models/cmd_filter.py b/apps/assets/models/cmd_filter.py index ec6f1be3e..63fd7ca34 100644 --- a/apps/assets/models/cmd_filter.py +++ b/apps/assets/models/cmd_filter.py @@ -29,7 +29,7 @@ class CommandFilter(OrgModelMixin): ) nodes = models.ManyToManyField( 'assets.Node', related_name='cmd_filters', blank=True, - verbose_name=_("Nodes") + verbose_name=_("Node") ) assets = models.ManyToManyField( 'assets.Asset', related_name='cmd_filters', blank=True, diff --git a/apps/audits/backends/db.py b/apps/audits/backends/db.py index 870c25c9c..e8fe43c22 100644 --- a/apps/audits/backends/db.py +++ b/apps/audits/backends/db.py @@ -2,6 +2,7 @@ from django.utils.translation import gettext_lazy as _ from audits.models import OperateLog +from perms.const import ActionChoices class OperateLogStore(object): @@ -45,20 +46,29 @@ class OperateLogStore(object): before[k], after[k] = before_value, after_value return before, after + @staticmethod + def _get_special_handler(resource_type): + # 根据资源类型,处理特殊字段 + resource_map = { + 'Asset permission': lambda k, v: ActionChoices.display(int(v)) if k == 'Actions' else v + } + return resource_map.get(resource_type, lambda k, v: v) + @classmethod - def convert_diff_friendly(cls, raw_diff): + def convert_diff_friendly(cls, op_log): diff_list = list() - for k, v in raw_diff.items(): + handler = cls._get_special_handler(op_log.resource_type) + for k, v in op_log.diff.items(): before, after = v.split(cls.SEP, 1) diff_list.append({ 'field': _(k), - 'before': before if before else _('empty'), - 'after': after if after else _('empty'), + 'before': handler(k, before) if before else _('empty'), + 'after': handler(k, after) if after else _('empty'), }) return diff_list def save(self, **kwargs): - log_id = kwargs.pop('id', None) + log_id = kwargs.get('id', None) before = kwargs.pop('before') or {} after = kwargs.pop('after') or {} diff --git a/apps/audits/serializers.py b/apps/audits/serializers.py index b68d1a516..78c42fdd0 100644 --- a/apps/audits/serializers.py +++ b/apps/audits/serializers.py @@ -77,10 +77,7 @@ class OperateLogActionDetailSerializer(serializers.ModelSerializer): fields = ('diff',) def to_representation(self, instance): - data = super().to_representation(instance) - diff = OperateLogStore.convert_diff_friendly(data['diff']) - data['diff'] = diff - return data + return {'diff': OperateLogStore.convert_diff_friendly(instance)} class OperateLogSerializer(BulkOrgResourceModelSerializer): diff --git a/apps/audits/signal_handlers/operate_log.py b/apps/audits/signal_handlers/operate_log.py index 7d89f77bd..d095a7ad6 100644 --- a/apps/audits/signal_handlers/operate_log.py +++ b/apps/audits/signal_handlers/operate_log.py @@ -33,7 +33,9 @@ def on_m2m_changed(sender, action, instance, reverse, model, pk_set, **kwargs): with translation.override('en'): resource_type = instance._meta.verbose_name - current_instance = model_to_dict(instance, include_model_fields=False) + current_instance = model_to_dict( + instance, include_model_fields=False, include_related_fields=[model] + ) instance_id = current_instance.get('id') log_id, before_instance = get_instance_dict_from_cache(instance_id) @@ -176,7 +178,7 @@ def on_django_start_set_operate_log_monitor_models(sender, **kwargs): 'PermedAsset', 'PermedAccount', 'MenuPermission', 'Permission', 'TicketSession', 'ApplyLoginTicket', 'ApplyCommandTicket', 'ApplyLoginAssetTicket', - 'FavoriteAsset', + 'FavoriteAsset', 'Asset' } for i, app in enumerate(apps.get_models(), 1): app_name = app._meta.app_label diff --git a/apps/audits/utils.py b/apps/audits/utils.py index 244df5d22..5813c3db8 100644 --- a/apps/audits/utils.py +++ b/apps/audits/utils.py @@ -78,31 +78,37 @@ def _get_instance_field_value( return data -def model_to_dict_for_operate_log(instance, include_model_fields=True, include_related_fields=False): - model_need_continue_fields = ['date_updated'] - m2m_need_continue_fields = ['history_passwords'] +def model_to_dict_for_operate_log( + instance, include_model_fields=True, include_related_fields=None +): + def get_related_values(f): + value = [] + if instance.pk is not None: + related_name = getattr(f, 'attname', '') or getattr(f, 'related_name', '') + if not related_name or related_name in ['history_passwords']: + return + try: + value = [str(i) for i in getattr(instance, related_name).all()] + except: + pass + if not value: + return + try: + field_key = getattr(f, 'verbose_name', None) or f.related_model._meta.verbose_name + data.setdefault(str(field_key), value) + except: + pass data = _get_instance_field_value( - instance, include_model_fields, model_need_continue_fields + instance, include_model_fields, ['date_updated'] ) if include_related_fields: opts = instance._meta - for f in opts.many_to_many: - value = [] - if instance.pk is not None: - related_name = getattr(f, 'attname', '') or getattr(f, 'related_name', '') - if not related_name or related_name in m2m_need_continue_fields: - continue - try: - value = [str(i) for i in getattr(instance, related_name).all()] - except: - pass - if not value: + for f in chain(opts.many_to_many, opts.related_objects): + related_model = getattr(f, 'related_model', None) + if related_model not in include_related_fields: continue - try: - field_key = getattr(f, 'verbose_name', None) or f.related_model._meta.verbose_name - data.setdefault(str(field_key), value) - except: - pass + get_related_values(f) + return data diff --git a/apps/jumpserver/conf.py b/apps/jumpserver/conf.py index 7492f5c4d..a0ecff1d1 100644 --- a/apps/jumpserver/conf.py +++ b/apps/jumpserver/conf.py @@ -584,7 +584,7 @@ class Config(dict): 'APPLET_DOWNLOAD_HOST': '', # FTP 文件上传下载备份阈值,单位(M),当值小于等于0时,不备份 - 'FTP_FILE_MAX_STORE': 100, + 'FTP_FILE_MAX_STORE': 0, # API 分页 'MAX_LIMIT_PER_PAGE': 10000, diff --git a/apps/perms/migrations/0002_auto_20171228_0025_squashed_0009_auto_20180903_1132.py b/apps/perms/migrations/0002_auto_20171228_0025_squashed_0009_auto_20180903_1132.py index f7f191f4a..12d8e70ce 100644 --- a/apps/perms/migrations/0002_auto_20171228_0025_squashed_0009_auto_20180903_1132.py +++ b/apps/perms/migrations/0002_auto_20171228_0025_squashed_0009_auto_20180903_1132.py @@ -80,7 +80,7 @@ class Migration(migrations.Migration): migrations.AddField( model_name='assetpermission', name='nodes', - field=models.ManyToManyField(blank=True, related_name='granted_by_permissions', to='assets.Node', verbose_name='Nodes'), + field=models.ManyToManyField(blank=True, related_name='granted_by_permissions', to='assets.Node', verbose_name='Node'), ), # migrations.RunPython( # code=migrate_node_permissions, diff --git a/apps/perms/models/asset_permission.py b/apps/perms/models/asset_permission.py index 9db73e5df..5ecf824c0 100644 --- a/apps/perms/models/asset_permission.py +++ b/apps/perms/models/asset_permission.py @@ -69,7 +69,7 @@ class AssetPermission(LabeledMixin, JMSOrgBaseModel): 'assets.Asset', related_name='granted_by_permissions', blank=True, verbose_name=_("Asset") ) nodes = models.ManyToManyField( - 'assets.Node', related_name='granted_by_permissions', blank=True, verbose_name=_("Nodes") + 'assets.Node', related_name='granted_by_permissions', blank=True, verbose_name=_("Node") ) # 特殊的账号: @ALL, @INPUT @USER 默认包含,将来在全局设置中进行控制. accounts = models.JSONField(default=list, verbose_name=_("Account")) diff --git a/apps/terminal/models/component/task.py b/apps/terminal/models/component/task.py index c69a89423..572167cf3 100644 --- a/apps/terminal/models/component/task.py +++ b/apps/terminal/models/component/task.py @@ -20,3 +20,6 @@ class Task(JMSBaseModel): class Meta: db_table = "terminal_task" verbose_name = _("Task") + + def __str__(self): + return str(dict(SessionTaskChoices.choices).get(self.name)) From eca50874f0e31b598a24e81f5219411eab778ea3 Mon Sep 17 00:00:00 2001 From: feng <1304903146@qq.com> Date: Wed, 6 Dec 2023 18:48:35 +0800 Subject: [PATCH 035/111] =?UTF-8?q?feat:=20=E5=90=8C=E6=AD=A5=E5=88=A0?= =?UTF-8?q?=E9=99=A4=E8=BF=9C=E7=A8=8B=E6=9C=BA=E5=99=A8=E8=B4=A6=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/accounts/api/account/task.py | 35 +++++----- apps/accounts/automations/endpoint.py | 8 ++- .../automations/remove_account/__init__.py | 0 .../remove_account/database/mongodb/main.yml | 21 ++++++ .../database/mongodb/manifest.yml | 12 ++++ .../remove_account/database/mysql/main.yml | 18 +++++ .../database/mysql/manifest.yml | 14 ++++ .../remove_account/database/oracle/main.yml | 16 +++++ .../database/oracle/manifest.yml | 12 ++++ .../database/postgresql/main.yml | 15 +++++ .../database/postgresql/manifest.yml | 12 ++++ .../database/sqlserver/main.yml | 14 ++++ .../database/sqlserver/manifest.yml | 12 ++++ .../remove_account/host/posix/main.yml | 25 +++++++ .../remove_account/host/posix/manifest.yml | 13 ++++ .../remove_account/host/windows/main.yml | 9 +++ .../remove_account/host/windows/manifest.yml | 13 ++++ .../automations/remove_account/manager.py | 67 +++++++++++++++++++ .../verify_account/database/mongodb/main.yml | 2 +- apps/accounts/const/automation.py | 1 + .../migrations/0007_alter_account_options.py | 10 ++- apps/accounts/models/account.py | 1 + apps/accounts/permissions.py | 19 ++++++ apps/accounts/serializers/account/account.py | 6 +- apps/accounts/tasks/__init__.py | 1 + apps/accounts/tasks/remove_account.py | 31 +++++++++ .../0126_automation_remove_account.py | 55 +++++++++++++++ apps/assets/models/platform.py | 6 ++ 28 files changed, 424 insertions(+), 24 deletions(-) create mode 100644 apps/accounts/automations/remove_account/__init__.py create mode 100644 apps/accounts/automations/remove_account/database/mongodb/main.yml create mode 100644 apps/accounts/automations/remove_account/database/mongodb/manifest.yml create mode 100644 apps/accounts/automations/remove_account/database/mysql/main.yml create mode 100644 apps/accounts/automations/remove_account/database/mysql/manifest.yml create mode 100644 apps/accounts/automations/remove_account/database/oracle/main.yml create mode 100644 apps/accounts/automations/remove_account/database/oracle/manifest.yml create mode 100644 apps/accounts/automations/remove_account/database/postgresql/main.yml create mode 100644 apps/accounts/automations/remove_account/database/postgresql/manifest.yml create mode 100644 apps/accounts/automations/remove_account/database/sqlserver/main.yml create mode 100644 apps/accounts/automations/remove_account/database/sqlserver/manifest.yml create mode 100644 apps/accounts/automations/remove_account/host/posix/main.yml create mode 100644 apps/accounts/automations/remove_account/host/posix/manifest.yml create mode 100644 apps/accounts/automations/remove_account/host/windows/main.yml create mode 100644 apps/accounts/automations/remove_account/host/windows/manifest.yml create mode 100644 apps/accounts/automations/remove_account/manager.py create mode 100644 apps/accounts/permissions.py create mode 100644 apps/accounts/tasks/remove_account.py create mode 100644 apps/assets/migrations/0126_automation_remove_account.py diff --git a/apps/accounts/api/account/task.py b/apps/accounts/api/account/task.py index 16ea84eae..c4f6ebd9f 100644 --- a/apps/accounts/api/account/task.py +++ b/apps/accounts/api/account/task.py @@ -1,9 +1,12 @@ from rest_framework.generics import CreateAPIView -from rest_framework.response import Response from accounts import serializers -from accounts.tasks import verify_accounts_connectivity_task, push_accounts_to_assets_task +from accounts.permissions import AccountTaskActionPermission +from accounts.tasks import ( + remove_accounts_task, verify_accounts_connectivity_task, push_accounts_to_assets_task +) from assets.exceptions import NotSupportedTemporarilyError +from authentication.permissions import UserConfirmation, ConfirmType __all__ = [ 'AccountsTaskCreateAPI', @@ -12,16 +15,16 @@ __all__ = [ class AccountsTaskCreateAPI(CreateAPIView): serializer_class = serializers.AccountTaskSerializer + permission_classes = (AccountTaskActionPermission,) - def check_permissions(self, request): - act = request.data.get('action') - if act == 'push': - code = 'accounts.push_account' - else: - code = 'accounts.verify_account' - has = request.user.has_perm(code) - if not has: - self.permission_denied(request) + def get_permissions(self): + act = self.request.data.get('action') + if act == 'remove': + self.permission_classes = [ + AccountTaskActionPermission, + UserConfirmation.require(ConfirmType.PASSWORD) + ] + return super().get_permissions() def perform_create(self, serializer): data = serializer.validated_data @@ -31,6 +34,10 @@ class AccountsTaskCreateAPI(CreateAPIView): if data['action'] == 'push': task = push_accounts_to_assets_task.delay(account_ids, params) + elif data['action'] == 'remove': + gather_accounts = data.get('gather_accounts', []) + gather_account_ids = [str(a.id) for a in gather_accounts] + task = remove_accounts_task.delay(gather_account_ids) else: account = accounts[0] asset = account.asset @@ -43,9 +50,3 @@ class AccountsTaskCreateAPI(CreateAPIView): data["task"] = task.id setattr(serializer, '_data', data) return task - - def get_exception_handler(self): - def handler(e, context): - return Response({"error": str(e)}, status=401) - - return handler diff --git a/apps/accounts/automations/endpoint.py b/apps/accounts/automations/endpoint.py index c1a19c968..f045858a7 100644 --- a/apps/accounts/automations/endpoint.py +++ b/apps/accounts/automations/endpoint.py @@ -1,8 +1,9 @@ -from .push_account.manager import PushAccountManager -from .change_secret.manager import ChangeSecretManager -from .verify_account.manager import VerifyAccountManager from .backup_account.manager import AccountBackupManager +from .change_secret.manager import ChangeSecretManager from .gather_accounts.manager import GatherAccountsManager +from .push_account.manager import PushAccountManager +from .remove_account.manager import RemoveAccountManager +from .verify_account.manager import VerifyAccountManager from .verify_gateway_account.manager import VerifyGatewayAccountManager from ..const import AutomationTypes @@ -12,6 +13,7 @@ class ExecutionManager: AutomationTypes.push_account: PushAccountManager, AutomationTypes.change_secret: ChangeSecretManager, AutomationTypes.verify_account: VerifyAccountManager, + AutomationTypes.remove_account: RemoveAccountManager, AutomationTypes.gather_accounts: GatherAccountsManager, AutomationTypes.verify_gateway_account: VerifyGatewayAccountManager, # TODO 后期迁移到自动化策略中 diff --git a/apps/accounts/automations/remove_account/__init__.py b/apps/accounts/automations/remove_account/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/apps/accounts/automations/remove_account/database/mongodb/main.yml b/apps/accounts/automations/remove_account/database/mongodb/main.yml new file mode 100644 index 000000000..3ec800981 --- /dev/null +++ b/apps/accounts/automations/remove_account/database/mongodb/main.yml @@ -0,0 +1,21 @@ +- hosts: mongodb + gather_facts: no + vars: + ansible_python_interpreter: /opt/py3/bin/python + + tasks: + - name: "Remove account" + mongodb_user: + login_user: "{{ jms_account.username }}" + login_password: "{{ jms_account.secret }}" + login_host: "{{ jms_asset.address }}" + login_port: "{{ jms_asset.port }}" + login_database: "{{ jms_asset.spec_info.db_name }}" + ssl: "{{ jms_asset.spec_info.use_ssl }}" + ssl_ca_certs: "{{ jms_asset.secret_info.ca_cert | default('') }}" + ssl_certfile: "{{ jms_asset.secret_info.client_key | default('') }}" + connection_options: + - tlsAllowInvalidHostnames: "{{ jms_asset.spec_info.allow_invalid_cert}}" + db: "{{ jms_asset.spec_info.db_name }}" + name: "{{ account.username }}" + state: absent \ No newline at end of file diff --git a/apps/accounts/automations/remove_account/database/mongodb/manifest.yml b/apps/accounts/automations/remove_account/database/mongodb/manifest.yml new file mode 100644 index 000000000..681a6a521 --- /dev/null +++ b/apps/accounts/automations/remove_account/database/mongodb/manifest.yml @@ -0,0 +1,12 @@ +id: remove_account_mongodb +name: "{{ 'MongoDB account remove' | trans }}" +category: database +type: + - mongodb +method: remove_account + +i18n: + MongoDB account remove: + zh: 使用 Ansible 模块 mongodb 删除账号 + ja: Ansible モジュール mongodb を使用してアカウントを削除する + en: Delete account using Ansible module mongodb diff --git a/apps/accounts/automations/remove_account/database/mysql/main.yml b/apps/accounts/automations/remove_account/database/mysql/main.yml new file mode 100644 index 000000000..563a7d74b --- /dev/null +++ b/apps/accounts/automations/remove_account/database/mysql/main.yml @@ -0,0 +1,18 @@ +- hosts: mysql + gather_facts: no + vars: + ansible_python_interpreter: /opt/py3/bin/python + + tasks: + - name: "Remove account" + community.mysql.mysql_user: + login_user: "{{ jms_account.username }}" + login_password: "{{ jms_account.secret }}" + login_host: "{{ jms_asset.address }}" + login_port: "{{ jms_asset.port }}" + check_hostname: "{{ check_ssl if check_ssl else omit }}" + ca_cert: "{{ jms_asset.secret_info.ca_cert | default(omit) if check_ssl else omit }}" + client_cert: "{{ jms_asset.secret_info.client_cert | default(omit) if check_ssl else omit }}" + client_key: "{{ jms_asset.secret_info.client_key | default(omit) if check_ssl else omit }}" + name: "{{ account.username }}" + state: absent diff --git a/apps/accounts/automations/remove_account/database/mysql/manifest.yml b/apps/accounts/automations/remove_account/database/mysql/manifest.yml new file mode 100644 index 000000000..ff8845a6d --- /dev/null +++ b/apps/accounts/automations/remove_account/database/mysql/manifest.yml @@ -0,0 +1,14 @@ +id: remove_account_mysql +name: "{{ 'MySQL account remove' | trans }}" +category: database +type: + - mysql + - mariadb +method: remove_account + +i18n: + MySQL account remove: + zh: 使用 Ansible 模块 mysql_user 删除账号 + ja: Ansible モジュール mysql_user を使用してアカウントを削除します + en: Use the Ansible module mysql_user to delete the account + diff --git a/apps/accounts/automations/remove_account/database/oracle/main.yml b/apps/accounts/automations/remove_account/database/oracle/main.yml new file mode 100644 index 000000000..ffd846d47 --- /dev/null +++ b/apps/accounts/automations/remove_account/database/oracle/main.yml @@ -0,0 +1,16 @@ +- hosts: oracle + gather_facts: no + vars: + ansible_python_interpreter: /opt/py3/bin/python + + tasks: + - name: "Remove account" + oracle_user: + login_user: "{{ jms_account.username }}" + login_password: "{{ jms_account.secret }}" + login_host: "{{ jms_asset.address }}" + login_port: "{{ jms_asset.port }}" + login_database: "{{ jms_asset.spec_info.db_name }}" + mode: "{{ jms_account.mode }}" + name: "{{ account.username }}" + state: absent diff --git a/apps/accounts/automations/remove_account/database/oracle/manifest.yml b/apps/accounts/automations/remove_account/database/oracle/manifest.yml new file mode 100644 index 000000000..173053673 --- /dev/null +++ b/apps/accounts/automations/remove_account/database/oracle/manifest.yml @@ -0,0 +1,12 @@ +id: remove_account_oracle +name: "{{ 'Oracle account remove' | trans }}" +category: database +type: + - oracle +method: remove_account + +i18n: + Oracle account remove: + zh: 使用 Python 模块 oracledb 删除账号 + ja: Python モジュール oracledb を使用してアカウントを検証する + en: Using Python module oracledb to verify account diff --git a/apps/accounts/automations/remove_account/database/postgresql/main.yml b/apps/accounts/automations/remove_account/database/postgresql/main.yml new file mode 100644 index 000000000..7004dc945 --- /dev/null +++ b/apps/accounts/automations/remove_account/database/postgresql/main.yml @@ -0,0 +1,15 @@ +- hosts: postgresql + gather_facts: no + vars: + ansible_python_interpreter: /opt/py3/bin/python + + tasks: + - name: "Remove account" + community.postgresql.postgresql_user: + login_user: "{{ jms_account.username }}" + login_password: "{{ jms_account.secret }}" + login_host: "{{ jms_asset.address }}" + login_port: "{{ jms_asset.port }}" + db: "{{ jms_asset.spec_info.db_name }}" + name: "{{ account.username }}" + state: absent diff --git a/apps/accounts/automations/remove_account/database/postgresql/manifest.yml b/apps/accounts/automations/remove_account/database/postgresql/manifest.yml new file mode 100644 index 000000000..c6c143e94 --- /dev/null +++ b/apps/accounts/automations/remove_account/database/postgresql/manifest.yml @@ -0,0 +1,12 @@ +id: remove_account_postgresql +name: "{{ 'PostgreSQL account remove' | trans }}" +category: database +type: + - postgresql +method: remove_account + +i18n: + PostgreSQL account remove: + zh: 使用 Ansible 模块 postgresql_user 删除账号 + ja: Ansible モジュール postgresql_user を使用してアカウントを削除します + en: Use the Ansible module postgresql_user to delete the account diff --git a/apps/accounts/automations/remove_account/database/sqlserver/main.yml b/apps/accounts/automations/remove_account/database/sqlserver/main.yml new file mode 100644 index 000000000..597e12906 --- /dev/null +++ b/apps/accounts/automations/remove_account/database/sqlserver/main.yml @@ -0,0 +1,14 @@ +- hosts: sqlserver + gather_facts: no + vars: + ansible_python_interpreter: /opt/py3/bin/python + + tasks: + - name: "Remove account" + community.general.mssql_script: + login_user: "{{ jms_account.username }}" + login_password: "{{ jms_account.secret }}" + login_host: "{{ jms_asset.address }}" + login_port: "{{ jms_asset.port }}" + name: "{{ jms_asset.spec_info.db_name }}" + script: "DROP USER {{ account.username }}" diff --git a/apps/accounts/automations/remove_account/database/sqlserver/manifest.yml b/apps/accounts/automations/remove_account/database/sqlserver/manifest.yml new file mode 100644 index 000000000..33c4895e2 --- /dev/null +++ b/apps/accounts/automations/remove_account/database/sqlserver/manifest.yml @@ -0,0 +1,12 @@ +id: remove_account_sqlserver +name: "{{ 'SQLServer account remove' | trans }}" +category: database +type: + - sqlserver +method: remove_account + +i18n: + SQLServer account remove: + zh: 使用 Ansible 模块 mssql 删除账号 + ja: Ansible モジュール mssql を使用してアカウントを削除する + en: Use Ansible module mssql to delete account diff --git a/apps/accounts/automations/remove_account/host/posix/main.yml b/apps/accounts/automations/remove_account/host/posix/main.yml new file mode 100644 index 000000000..de91b8552 --- /dev/null +++ b/apps/accounts/automations/remove_account/host/posix/main.yml @@ -0,0 +1,25 @@ +- hosts: demo + gather_facts: no + tasks: + - name: "Get user home directory path" + ansible.builtin.shell: + cmd: "getent passwd {{ account.username }} | cut -d: -f6" + register: user_home_dir + ignore_errors: yes + + - name: "Check if user home directory exists" + ansible.builtin.stat: + path: "{{ user_home_dir.stdout }}" + register: home_dir + when: user_home_dir.stdout != "" + + - name: "Rename user home directory if it exists" + ansible.builtin.command: + cmd: "mv {{ user_home_dir.stdout }} {{ user_home_dir.stdout }}.bak" + when: home_dir.stat.exists and user_home_dir.stdout != "" + + - name: "Remove account" + ansible.builtin.user: + name: "{{ account.username }}" + state: absent + remove: "{{ home_dir.stat.exists }}" diff --git a/apps/accounts/automations/remove_account/host/posix/manifest.yml b/apps/accounts/automations/remove_account/host/posix/manifest.yml new file mode 100644 index 000000000..753c63574 --- /dev/null +++ b/apps/accounts/automations/remove_account/host/posix/manifest.yml @@ -0,0 +1,13 @@ +id: remove_account_posix +name: "{{ 'Posix account remove' | trans }}" +category: host +type: + - linux + - unix +method: remove_account + +i18n: + Posix account remove: + zh: 使用 Ansible 模块 user 删除账号 + ja: Ansible モジュール ユーザーを使用してアカウントを削除します + en: Use the Ansible module user to delete the account diff --git a/apps/accounts/automations/remove_account/host/windows/main.yml b/apps/accounts/automations/remove_account/host/windows/main.yml new file mode 100644 index 000000000..7be9940b3 --- /dev/null +++ b/apps/accounts/automations/remove_account/host/windows/main.yml @@ -0,0 +1,9 @@ +- hosts: windows + gather_facts: no + tasks: + - name: "Remove account" + ansible.windows.win_user: + name: "{{ account.username }}" + state: absent + purge: yes + force: yes \ No newline at end of file diff --git a/apps/accounts/automations/remove_account/host/windows/manifest.yml b/apps/accounts/automations/remove_account/host/windows/manifest.yml new file mode 100644 index 000000000..588d7efea --- /dev/null +++ b/apps/accounts/automations/remove_account/host/windows/manifest.yml @@ -0,0 +1,13 @@ +id: remove_account_windows +name: "{{ 'Windows account remove' | trans }}" +version: 1 +method: remove_account +category: host +type: + - windows + +i18n: + Windows account remove: + zh: 使用 Ansible 模块 win_user 删除账号 + ja: Ansible モジュール win_user を使用してアカウントを削除する + en: Use the Ansible module win_user to delete an account diff --git a/apps/accounts/automations/remove_account/manager.py b/apps/accounts/automations/remove_account/manager.py new file mode 100644 index 000000000..37dd28f2d --- /dev/null +++ b/apps/accounts/automations/remove_account/manager.py @@ -0,0 +1,67 @@ +import os +from copy import deepcopy + +from django.db.models import QuerySet + +from accounts.const import AutomationTypes +from accounts.models import Account +from common.utils import get_logger +from ..base.manager import AccountBasePlaybookManager + +logger = get_logger(__name__) + + +class RemoveAccountManager(AccountBasePlaybookManager): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.host_account_mapper = {} + + def prepare_runtime_dir(self): + path = super().prepare_runtime_dir() + ansible_config_path = os.path.join(path, 'ansible.cfg') + + with open(ansible_config_path, 'w') as f: + f.write('[ssh_connection]\n') + f.write('ssh_args = -o ControlMaster=no -o ControlPersist=no\n') + return path + + @classmethod + def method_type(cls): + return AutomationTypes.remove_account + + def get_gather_accounts(self, privilege_account, gather_accounts: QuerySet): + gather_account_ids = self.execution.snapshot['gather_accounts'] + gather_accounts = gather_accounts.filter(id__in=gather_account_ids) + gather_accounts = gather_accounts.exclude( + username__in=[privilege_account.username, 'root', 'Administrator'] + ) + return gather_accounts + + def host_callback(self, host, asset=None, account=None, automation=None, path_dir=None, **kwargs): + if host.get('error'): + return host + + gather_accounts = asset.gatheredaccount_set.all() + gather_accounts = self.get_gather_accounts(account, gather_accounts) + + inventory_hosts = [] + + for gather_account in gather_accounts: + h = deepcopy(host) + h['name'] += '(' + gather_account.username + ')' + self.host_account_mapper[h['name']] = (asset, gather_account) + h['account'] = {'username': gather_account.username} + inventory_hosts.append(h) + return inventory_hosts + + def on_host_success(self, host, result): + tuple_asset_gather_account = self.host_account_mapper.get(host) + if not tuple_asset_gather_account: + return + asset, gather_account = tuple_asset_gather_account + Account.objects.filter( + asset_id=asset.id, + username=gather_account.username + ).delete() + gather_account.delete() diff --git a/apps/accounts/automations/verify_account/database/mongodb/main.yml b/apps/accounts/automations/verify_account/database/mongodb/main.yml index 2770e6eb4..13ecccb61 100644 --- a/apps/accounts/automations/verify_account/database/mongodb/main.yml +++ b/apps/accounts/automations/verify_account/database/mongodb/main.yml @@ -1,4 +1,4 @@ -- hosts: mongdb +- hosts: mongodb gather_facts: no vars: ansible_python_interpreter: /opt/py3/bin/python diff --git a/apps/accounts/const/automation.py b/apps/accounts/const/automation.py index 0a67de5c8..cde7fe982 100644 --- a/apps/accounts/const/automation.py +++ b/apps/accounts/const/automation.py @@ -24,6 +24,7 @@ class AutomationTypes(models.TextChoices): push_account = 'push_account', _('Push account') change_secret = 'change_secret', _('Change secret') verify_account = 'verify_account', _('Verify account') + remove_account = 'remove_account', _('Remove account') gather_accounts = 'gather_accounts', _('Gather accounts') verify_gateway_account = 'verify_gateway_account', _('Verify gateway account') diff --git a/apps/accounts/migrations/0007_alter_account_options.py b/apps/accounts/migrations/0007_alter_account_options.py index 4ec798a21..6def6de6e 100644 --- a/apps/accounts/migrations/0007_alter_account_options.py +++ b/apps/accounts/migrations/0007_alter_account_options.py @@ -4,7 +4,6 @@ from django.db import migrations class Migration(migrations.Migration): - dependencies = [ ('accounts', '0006_gatheredaccount'), ] @@ -12,6 +11,13 @@ class Migration(migrations.Migration): operations = [ migrations.AlterModelOptions( name='account', - options={'permissions': [('view_accountsecret', 'Can view asset account secret'), ('view_historyaccount', 'Can view asset history account'), ('view_historyaccountsecret', 'Can view asset history account secret'), ('verify_account', 'Can verify account'), ('push_account', 'Can push account')], 'verbose_name': 'Account'}, + options={'permissions': [ + ('view_accountsecret', 'Can view asset account secret'), + ('view_historyaccount', 'Can view asset history account'), + ('view_historyaccountsecret', 'Can view asset history account secret'), + ('verify_account', 'Can verify account'), + ('push_account', 'Can push account'), + ('remove_account', 'Can remove account'), + ], 'verbose_name': 'Account'}, ), ] diff --git a/apps/accounts/models/account.py b/apps/accounts/models/account.py index c3c052b1b..1defa3642 100644 --- a/apps/accounts/models/account.py +++ b/apps/accounts/models/account.py @@ -69,6 +69,7 @@ class Account(AbsConnectivity, LabeledMixin, BaseAccount): ('view_historyaccountsecret', _('Can view asset history account secret')), ('verify_account', _('Can verify account')), ('push_account', _('Can push account')), + ('remove_account', _('Can remove account')), ] def __str__(self): diff --git a/apps/accounts/permissions.py b/apps/accounts/permissions.py new file mode 100644 index 000000000..6d3b94260 --- /dev/null +++ b/apps/accounts/permissions.py @@ -0,0 +1,19 @@ +from rest_framework import permissions + + +def check_permissions(request): + act = request.data.get('action') + if act == 'push': + code = 'accounts.push_account' + elif act == 'remove': + code = 'accounts.remove_account' + else: + code = 'accounts.verify_account' + return request.user.has_perm(code) + + +class AccountTaskActionPermission(permissions.IsAuthenticated): + + def has_permission(self, request, view): + return super().has_permission(request, view) \ + and check_permissions(request) diff --git a/apps/accounts/serializers/account/account.py b/apps/accounts/serializers/account/account.py index c1098a6d1..197c64c7d 100644 --- a/apps/accounts/serializers/account/account.py +++ b/apps/accounts/serializers/account/account.py @@ -10,7 +10,7 @@ from rest_framework.generics import get_object_or_404 from rest_framework.validators import UniqueTogetherValidator from accounts.const import SecretType, Source, AccountInvalidPolicy -from accounts.models import Account, AccountTemplate +from accounts.models import Account, AccountTemplate, GatheredAccount from accounts.tasks import push_accounts_to_assets_task from assets.const import Category, AllTypes from assets.models import Asset @@ -458,11 +458,15 @@ class AccountTaskSerializer(serializers.Serializer): ('test', 'test'), ('verify', 'verify'), ('push', 'push'), + ('remove', 'remove'), ) action = serializers.ChoiceField(choices=ACTION_CHOICES, write_only=True) accounts = serializers.PrimaryKeyRelatedField( queryset=Account.objects, required=False, allow_empty=True, many=True ) + gather_accounts = serializers.PrimaryKeyRelatedField( + queryset=GatheredAccount.objects, required=False, allow_empty=True, many=True + ) task = serializers.CharField(read_only=True) params = serializers.JSONField( decoder=None, encoder=None, required=False, diff --git a/apps/accounts/tasks/__init__.py b/apps/accounts/tasks/__init__.py index 8a79c1aa4..a20eba291 100644 --- a/apps/accounts/tasks/__init__.py +++ b/apps/accounts/tasks/__init__.py @@ -2,5 +2,6 @@ from .automation import * from .backup_account import * from .gather_accounts import * from .push_account import * +from .remove_account import * from .template import * from .verify_account import * diff --git a/apps/accounts/tasks/remove_account.py b/apps/accounts/tasks/remove_account.py new file mode 100644 index 000000000..62a9375a1 --- /dev/null +++ b/apps/accounts/tasks/remove_account.py @@ -0,0 +1,31 @@ +from celery import shared_task +from django.utils.translation import gettext_noop, gettext_lazy as _ + +from accounts.const import AutomationTypes +from accounts.tasks.common import quickstart_automation_by_snapshot +from common.utils import get_logger + +logger = get_logger(__file__) + +__all__ = ['remove_accounts_task'] + + +@shared_task( + queue="ansible", verbose_name=_('Remove accounts'), + activity_callback=lambda self, gather_account_ids, *args, **kwargs: (gather_account_ids, None) +) +def remove_accounts_task(gather_account_ids): + from accounts.models import GatheredAccount + + gather_accounts = GatheredAccount.objects.filter( + id__in=gather_account_ids + ) + task_name = gettext_noop("Remove accounts") + + task_snapshot = { + 'assets': [str(i.asset_id) for i in gather_accounts], + 'gather_accounts': [str(i.id) for i in gather_accounts], + } + + tp = AutomationTypes.remove_account + quickstart_automation_by_snapshot(task_name, tp, task_snapshot) diff --git a/apps/assets/migrations/0126_automation_remove_account.py b/apps/assets/migrations/0126_automation_remove_account.py new file mode 100644 index 000000000..6ac63ab3d --- /dev/null +++ b/apps/assets/migrations/0126_automation_remove_account.py @@ -0,0 +1,55 @@ +# Generated by Django 4.1.10 on 2023-12-05 10:03 +from functools import reduce + +from django.db import migrations, models +from django.db.models import F + + +def migrate_automation_ansible_remove_account(apps, *args): + automation_model = apps.get_model('assets', 'PlatformAutomation') + automation_map = { + ('oracle',): 'remove_account_oracle', + ('windows',): 'remove_account_windows', + ('mongodb',): 'remove_account_mongodb', + ('linux', 'unix'): 'remove_account_posix', + ('sqlserver',): 'remove_account_sqlserver', + ('mysql', 'mariadb'): 'remove_account_mysql', + ('postgresql',): 'remove_account_postgresql', + } + + update_objs = [] + types = list(reduce(lambda x, y: x + y, automation_map.keys())) + qs = automation_model.objects.filter(platform__type__in=types).annotate(tp=F('platform__type')) + for automation in qs: + for types, method in automation_map.items(): + if automation.tp in types: + automation.remove_account_enabled = True + automation.remove_account_method = method + break + update_objs.append(automation) + automation_model.objects.bulk_update(update_objs, ['remove_account_enabled', 'remove_account_method']) + + +class Migration(migrations.Migration): + dependencies = [ + ('assets', '0125_auto_20231011_1053'), + ] + + operations = [ + migrations.AddField( + model_name='platformautomation', + name='remove_account_enabled', + field=models.BooleanField(default=False, verbose_name='Remove account enabled'), + ), + migrations.AddField( + model_name='platformautomation', + name='remove_account_method', + field=models.TextField(blank=True, max_length=32, null=True, verbose_name='Remove account method'), + ), + migrations.AddField( + model_name='platformautomation', + name='remove_account_params', + field=models.JSONField(default=dict, verbose_name='Remove account params'), + ), + migrations.RunPython(migrate_automation_ansible_remove_account) + ] diff --git a/apps/assets/models/platform.py b/apps/assets/models/platform.py index 74fcb9cfb..a93a96863 100644 --- a/apps/assets/models/platform.py +++ b/apps/assets/models/platform.py @@ -72,6 +72,12 @@ class PlatformAutomation(models.Model): max_length=32, blank=True, null=True, verbose_name=_("Gather facts method") ) gather_accounts_params = models.JSONField(default=dict, verbose_name=_("Gather facts params")) + + remove_account_enabled = models.BooleanField(default=False, verbose_name=_("Remove account enabled")) + remove_account_method = models.TextField( + max_length=32, blank=True, null=True, verbose_name=_("Remove account method") + ) + remove_account_params = models.JSONField(default=dict, verbose_name=_("Remove account params")) platform = models.OneToOneField('Platform', on_delete=models.CASCADE, related_name='automation', null=True) From 9a2da98bd493cfaf5a15a54d381f60d9eae333d0 Mon Sep 17 00:00:00 2001 From: feng <1304903146@qq.com> Date: Fri, 8 Dec 2023 14:21:10 +0800 Subject: [PATCH 036/111] =?UTF-8?q?perf:=20=E4=BF=AE=E6=94=B9=E8=BF=81?= =?UTF-8?q?=E7=A7=BB=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...tion_remove_account.py => 0127_automation_remove_account.py} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename apps/assets/migrations/{0126_automation_remove_account.py => 0127_automation_remove_account.py} (97%) diff --git a/apps/assets/migrations/0126_automation_remove_account.py b/apps/assets/migrations/0127_automation_remove_account.py similarity index 97% rename from apps/assets/migrations/0126_automation_remove_account.py rename to apps/assets/migrations/0127_automation_remove_account.py index 6ac63ab3d..e0746326f 100644 --- a/apps/assets/migrations/0126_automation_remove_account.py +++ b/apps/assets/migrations/0127_automation_remove_account.py @@ -32,7 +32,7 @@ def migrate_automation_ansible_remove_account(apps, *args): class Migration(migrations.Migration): dependencies = [ - ('assets', '0125_auto_20231011_1053'), + ('assets', '0126_remove_asset_labels'), ] operations = [ From 81de527e321d62b33ec60a6693e21af3b019161f Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Fri, 8 Dec 2023 15:11:49 +0800 Subject: [PATCH 037/111] =?UTF-8?q?perf:=20=E8=A7=A3=E5=86=B3Slack?= =?UTF-8?q?=E8=A7=A3=E7=BB=91=E7=94=A8=E6=88=B7404=E9=97=AE=E9=A2=98=20(#1?= =?UTF-8?q?2283)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: jiangweidong --- apps/authentication/api/__init__.py | 3 +- apps/authentication/api/common.py | 51 ++++ apps/authentication/api/dingtalk.py | 35 --- apps/authentication/api/feishu.py | 28 +-- apps/authentication/api/wecom.py | 35 --- apps/authentication/errors/mfa.py | 5 + apps/authentication/urls/api_urls.py | 11 +- apps/locale/ja/LC_MESSAGES/django.po | 344 ++++++++++++++------------ apps/locale/zh/LC_MESSAGES/django.po | 356 ++++++++++++++------------- 9 files changed, 429 insertions(+), 439 deletions(-) create mode 100644 apps/authentication/api/common.py delete mode 100644 apps/authentication/api/dingtalk.py delete mode 100644 apps/authentication/api/wecom.py diff --git a/apps/authentication/api/__init__.py b/apps/authentication/api/__init__.py index 17e83813c..7a63c007c 100644 --- a/apps/authentication/api/__init__.py +++ b/apps/authentication/api/__init__.py @@ -4,7 +4,6 @@ from .access_key import * from .confirm import * from .connection_token import * -from .dingtalk import * from .feishu import * from .login_confirm import * from .mfa import * @@ -12,4 +11,4 @@ from .password import * from .sso import * from .temp_token import * from .token import * -from .wecom import * +from .common import * diff --git a/apps/authentication/api/common.py b/apps/authentication/api/common.py new file mode 100644 index 000000000..6624078a7 --- /dev/null +++ b/apps/authentication/api/common.py @@ -0,0 +1,51 @@ +from django.utils.translation import gettext_lazy as _ +from rest_framework.request import Request +from rest_framework.response import Response +from rest_framework.views import APIView + +from authentication import errors +from authentication.const import ConfirmType +from authentication.permissions import UserConfirmation +from common.api import RoleUserMixin, RoleAdminMixin +from common.exceptions import JMSException +from common.permissions import IsValidUser, OnlySuperUser +from common.utils import get_logger +from users.models import User + + +logger = get_logger(__file__) + + +class QRUnBindBase(APIView): + user: User + + def post(self, request: Request, backend: str, **kwargs): + backend_map = { + 'wecom': {'user_field': 'wecom_id', 'not_bind_err': errors.WeComNotBound}, + 'dingtalk': {'user_field': 'dingtalk_id', 'not_bind_err': errors.DingTalkNotBound}, + 'feishu': {'user_field': 'feishu_id', 'not_bind_err': errors.FeiShuNotBound}, + 'slack': {'user_field': 'slack_id', 'not_bind_err': errors.SlackNotBound}, + } + user = self.user + + backend_info = backend_map.get(backend) + if not backend_info: + raise JMSException( + _('The value in the parameter must contain %s') % ', '.join(backend_map.keys()) + ) + + if not getattr(user, backend_info['user_field'], None): + raise backend_info['not_bind_err'] + + setattr(user, backend_info['user_field'], None) + user.save() + return Response() + + +class QRUnBindForUserApi(RoleUserMixin, QRUnBindBase): + permission_classes = (IsValidUser, UserConfirmation.require(ConfirmType.RELOGIN),) + + +class QRUnBindForAdminApi(RoleAdminMixin, QRUnBindBase): + permission_classes = (OnlySuperUser,) + user_id_url_kwarg = 'user_id' diff --git a/apps/authentication/api/dingtalk.py b/apps/authentication/api/dingtalk.py deleted file mode 100644 index ad3bd26b1..000000000 --- a/apps/authentication/api/dingtalk.py +++ /dev/null @@ -1,35 +0,0 @@ -from rest_framework.request import Request -from rest_framework.response import Response -from rest_framework.views import APIView - -from authentication import errors -from authentication.const import ConfirmType -from authentication.permissions import UserConfirmation -from common.api import RoleUserMixin, RoleAdminMixin -from common.permissions import IsValidUser -from common.utils import get_logger -from users.models import User - -logger = get_logger(__file__) - - -class DingTalkQRUnBindBase(APIView): - user: User - - def post(self, request: Request, **kwargs): - user = self.user - - if not user.dingtalk_id: - raise errors.DingTalkNotBound - - user.dingtalk_id = None - user.save() - return Response() - - -class DingTalkQRUnBindForUserApi(RoleUserMixin, DingTalkQRUnBindBase): - permission_classes = (IsValidUser, UserConfirmation.require(ConfirmType.RELOGIN),) - - -class DingTalkQRUnBindForAdminApi(RoleAdminMixin, DingTalkQRUnBindBase): - user_id_url_kwarg = 'user_id' diff --git a/apps/authentication/api/feishu.py b/apps/authentication/api/feishu.py index cf95bb6ea..ca90be807 100644 --- a/apps/authentication/api/feishu.py +++ b/apps/authentication/api/feishu.py @@ -2,39 +2,13 @@ from rest_framework.request import Request from rest_framework.response import Response from rest_framework.views import APIView -from authentication import errors -from authentication.const import ConfirmType -from authentication.permissions import UserConfirmation -from common.api import RoleUserMixin, RoleAdminMixin from common.permissions import IsValidUser from common.utils import get_logger -from users.models import User + logger = get_logger(__name__) -class FeiShuQRUnBindBase(APIView): - user: User - - def post(self, request: Request, **kwargs): - user = self.user - - if not user.feishu_id: - raise errors.FeiShuNotBound - - user.feishu_id = None - user.save() - return Response() - - -class FeiShuQRUnBindForUserApi(RoleUserMixin, FeiShuQRUnBindBase): - permission_classes = (IsValidUser, UserConfirmation.require(ConfirmType.RELOGIN),) - - -class FeiShuQRUnBindForAdminApi(RoleAdminMixin, FeiShuQRUnBindBase): - user_id_url_kwarg = 'user_id' - - class FeiShuEventSubscriptionCallback(APIView): """ # https://open.feishu.cn/document/ukTMukTMukTM/uUTNz4SN1MjL1UzM diff --git a/apps/authentication/api/wecom.py b/apps/authentication/api/wecom.py deleted file mode 100644 index 6dcfe539c..000000000 --- a/apps/authentication/api/wecom.py +++ /dev/null @@ -1,35 +0,0 @@ -from rest_framework.request import Request -from rest_framework.response import Response -from rest_framework.views import APIView - -from authentication import errors -from authentication.const import ConfirmType -from authentication.permissions import UserConfirmation -from common.api import RoleUserMixin, RoleAdminMixin -from common.permissions import IsValidUser -from common.utils import get_logger -from users.models import User - -logger = get_logger(__file__) - - -class WeComQRUnBindBase(APIView): - user: User - - def post(self, request: Request, **kwargs): - user = self.user - - if not user.wecom_id: - raise errors.WeComNotBound - - user.wecom_id = None - user.save() - return Response() - - -class WeComQRUnBindForUserApi(RoleUserMixin, WeComQRUnBindBase): - permission_classes = (IsValidUser, UserConfirmation.require(ConfirmType.RELOGIN),) - - -class WeComQRUnBindForAdminApi(RoleAdminMixin, WeComQRUnBindBase): - user_id_url_kwarg = 'user_id' diff --git a/apps/authentication/errors/mfa.py b/apps/authentication/errors/mfa.py index 8a0844145..b1ace594d 100644 --- a/apps/authentication/errors/mfa.py +++ b/apps/authentication/errors/mfa.py @@ -33,6 +33,11 @@ class FeiShuNotBound(JMSException): default_detail = _('FeiShu is not bound') +class SlackNotBound(JMSException): + default_code = 'slack_not_bound' + default_detail = _('Slack is not bound') + + class PasswordInvalid(JMSException): default_code = 'passwd_invalid' default_detail = _('Your password is invalid') diff --git a/apps/authentication/urls/api_urls.py b/apps/authentication/urls/api_urls.py index e7e561ffd..3bb7898df 100644 --- a/apps/authentication/urls/api_urls.py +++ b/apps/authentication/urls/api_urls.py @@ -16,16 +16,9 @@ router.register('super-connection-token', api.SuperConnectionTokenViewSet, 'supe router.register('confirm', api.UserConfirmationViewSet, 'confirm') urlpatterns = [ - path('wecom/qr/unbind/', api.WeComQRUnBindForUserApi.as_view(), name='wecom-qr-unbind'), - path('wecom/qr/unbind//', api.WeComQRUnBindForAdminApi.as_view(), name='wecom-qr-unbind-for-admin'), + path('/qr/unbind/', api.QRUnBindForUserApi.as_view(), name='qr-unbind'), + path('/qr/unbind//', api.QRUnBindForAdminApi.as_view(), name='qr-unbind-for-admin'), - path('dingtalk/qr/unbind/', api.DingTalkQRUnBindForUserApi.as_view(), name='dingtalk-qr-unbind'), - path('dingtalk/qr/unbind//', api.DingTalkQRUnBindForAdminApi.as_view(), - name='dingtalk-qr-unbind-for-admin'), - - path('feishu/qr/unbind/', api.FeiShuQRUnBindForUserApi.as_view(), name='feishu-qr-unbind'), - path('feishu/qr/unbind//', api.FeiShuQRUnBindForAdminApi.as_view(), - name='feishu-qr-unbind-for-admin'), path('feishu/event/subscription/callback/', api.FeiShuEventSubscriptionCallback.as_view(), name='feishu-event-subscription-callback'), diff --git a/apps/locale/ja/LC_MESSAGES/django.po b/apps/locale/ja/LC_MESSAGES/django.po index a4f7b31a0..6e983d775 100644 --- a/apps/locale/ja/LC_MESSAGES/django.po +++ b/apps/locale/ja/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-12-05 10:16+0800\n" +"POT-Creation-Date: 2023-12-08 14:51+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -49,7 +49,7 @@ msgstr "アクセスキー" #: accounts/const/account.py:9 assets/models/_user.py:48 #: authentication/backends/passkey/models.py:16 -#: authentication/models/sso_token.py:14 settings/serializers/feature.py:50 +#: authentication/models/sso_token.py:14 settings/serializers/feature.py:52 msgid "Token" msgstr "トークン" @@ -99,7 +99,7 @@ msgstr "更新" #: accounts/const/account.py:33 #: accounts/serializers/automations/change_secret.py:150 audits/const.py:62 #: audits/signal_handlers/activity_log.py:33 common/const/choices.py:19 -#: ops/const.py:74 terminal/const.py:78 xpack/plugins/cloud/const.py:43 +#: ops/const.py:74 terminal/const.py:79 xpack/plugins/cloud/const.py:46 msgid "Failed" msgstr "失敗しました" @@ -202,7 +202,7 @@ msgstr "作成のみ" msgid "Email" msgstr "メール" -#: accounts/const/automation.py:104 terminal/const.py:86 +#: accounts/const/automation.py:104 terminal/const.py:87 msgid "SFTP" msgstr "SFTP" @@ -211,7 +211,7 @@ msgstr "SFTP" msgid "Database" msgstr "データベース" -#: accounts/const/vault.py:9 settings/serializers/feature.py:41 +#: accounts/const/vault.py:9 settings/serializers/feature.py:43 msgid "HCP Vault" msgstr "HashiCorp Vault" @@ -259,13 +259,14 @@ msgstr "資産" #: accounts/serializers/account/account.py:220 #: accounts/serializers/account/account.py:268 #: accounts/serializers/account/template.py:25 -#: authentication/serializers/connect_token_secret.py:49 +#: authentication/serializers/connect_token_secret.py:50 msgid "Su from" msgstr "から切り替え" #: accounts/models/account.py:55 assets/const/protocol.py:169 #: settings/serializers/auth/cas.py:20 settings/serializers/auth/feishu.py:20 #: terminal/models/applet/applet.py:35 +#: terminal/models/virtualapp/virtualapp.py:21 msgid "Version" msgstr "バージョン" @@ -466,9 +467,11 @@ msgstr "終了日" #: accounts/models/automations/change_secret.py:43 #: assets/models/automations/base.py:113 audits/models.py:208 #: audits/serializers.py:54 ops/models/base.py:49 ops/models/job.py:232 -#: terminal/models/applet/applet.py:318 terminal/models/applet/host.py:140 -#: terminal/models/component/status.py:30 terminal/serializers/applet.py:18 -#: terminal/serializers/applet_host.py:124 tickets/models/ticket/general.py:283 +#: terminal/models/applet/applet.py:320 terminal/models/applet/host.py:140 +#: terminal/models/component/status.py:30 +#: terminal/models/virtualapp/virtualapp.py:99 +#: terminal/serializers/applet.py:18 terminal/serializers/applet_host.py:124 +#: terminal/serializers/virtualapp.py:35 tickets/models/ticket/general.py:283 #: tickets/serializers/super_ticket.py:13 #: tickets/serializers/ticket/ticket.py:20 xpack/plugins/cloud/models.py:201 #: xpack/plugins/cloud/models.py:257 @@ -534,8 +537,8 @@ msgstr "トリガー方式" #: accounts/models/automations/push_account.py:16 acls/models/base.py:41 #: acls/serializers/base.py:57 assets/models/cmd_filter.py:81 -#: audits/models.py:92 audits/serializers.py:87 -#: authentication/serializers/connect_token_secret.py:118 +#: audits/models.py:92 audits/serializers.py:84 +#: authentication/serializers/connect_token_secret.py:119 #: authentication/templates/authentication/_access_key_modal.html:34 msgid "Action" msgstr "アクション" @@ -552,8 +555,8 @@ msgstr "アカウントの確認" #: accounts/serializers/account/account.py:440 #: accounts/serializers/account/base.py:17 #: accounts/serializers/automations/change_secret.py:45 -#: authentication/serializers/connect_token_secret.py:41 -#: authentication/serializers/connect_token_secret.py:50 +#: authentication/serializers/connect_token_secret.py:42 +#: authentication/serializers/connect_token_secret.py:51 #: terminal/serializers/storage.py:140 msgid "Secret type" msgstr "鍵の種類" @@ -586,7 +589,8 @@ msgstr "パスワードルール" #: assets/models/platform.py:89 assets/serializers/asset/common.py:146 #: assets/serializers/platform.py:118 assets/serializers/platform.py:235 #: authentication/backends/passkey/models.py:10 -#: authentication/serializers/connect_token_secret.py:112 labels/models.py:11 +#: authentication/serializers/connect_token_secret.py:113 +#: authentication/serializers/connect_token_secret.py:168 labels/models.py:11 #: ops/mixin.py:21 ops/models/adhoc.py:20 ops/models/celery.py:15 #: ops/models/celery.py:57 ops/models/job.py:137 ops/models/playbook.py:29 #: ops/serializers/job.py:19 orgs/models.py:82 @@ -595,7 +599,9 @@ msgstr "パスワードルール" #: terminal/models/applet/applet.py:33 terminal/models/component/endpoint.py:12 #: terminal/models/component/endpoint.py:94 #: terminal/models/component/storage.py:26 terminal/models/component/task.py:13 -#: terminal/models/component/terminal.py:84 tickets/api/ticket.py:87 +#: terminal/models/component/terminal.py:84 +#: terminal/models/virtualapp/provider.py:10 +#: terminal/models/virtualapp/virtualapp.py:19 tickets/api/ticket.py:87 #: users/forms/profile.py:33 users/models/group.py:13 #: users/models/preference.py:11 users/models/user.py:798 #: xpack/plugins/cloud/models.py:32 xpack/plugins/cloud/models.py:273 @@ -610,9 +616,10 @@ msgstr "特権アカウント" #: accounts/models/base.py:70 assets/models/asset/common.py:166 #: assets/models/automations/base.py:21 assets/models/cmd_filter.py:39 #: assets/models/label.py:22 -#: authentication/serializers/connect_token_secret.py:116 +#: authentication/serializers/connect_token_secret.py:117 #: terminal/models/applet/applet.py:40 -#: terminal/models/component/endpoint.py:105 users/serializers/user.py:167 +#: terminal/models/component/endpoint.py:105 +#: terminal/models/virtualapp/virtualapp.py:23 users/serializers/user.py:167 msgid "Is active" msgstr "アクティブです。" @@ -738,8 +745,8 @@ msgstr "カテゴリ" #: assets/models/cmd_filter.py:74 assets/models/platform.py:91 #: assets/serializers/asset/common.py:123 assets/serializers/platform.py:120 #: assets/serializers/platform.py:139 audits/serializers.py:53 -#: audits/serializers.py:173 -#: authentication/serializers/connect_token_secret.py:125 ops/models/job.py:149 +#: audits/serializers.py:170 +#: authentication/serializers/connect_token_secret.py:126 ops/models/job.py:149 #: perms/serializers/user_permission.py:26 terminal/models/applet/applet.py:39 #: terminal/models/component/storage.py:57 #: terminal/models/component/storage.py:146 terminal/serializers/applet.py:29 @@ -795,7 +802,7 @@ msgid "Account has exist" msgstr "アカウントはすでに存在しています" #: accounts/serializers/account/account.py:441 -#: authentication/serializers/connect_token_secret.py:158 +#: authentication/serializers/connect_token_secret.py:159 #: authentication/templates/authentication/_access_key_modal.html:30 #: perms/models/perm_node.py:21 users/serializers/group.py:32 msgid "ID" @@ -804,7 +811,7 @@ msgstr "ID" #: accounts/serializers/account/account.py:451 acls/serializers/base.py:116 #: assets/models/cmd_filter.py:24 assets/models/label.py:16 audits/models.py:54 #: audits/models.py:90 audits/models.py:172 audits/models.py:269 -#: audits/serializers.py:174 authentication/models/connection_token.py:32 +#: audits/serializers.py:171 authentication/models/connection_token.py:32 #: authentication/models/sso_token.py:16 #: notifications/models/notification.py:12 #: perms/api/user_permission/mixin.py:55 perms/models/asset_permission.py:63 @@ -909,10 +916,11 @@ msgstr "关联平台,可以配置推送参数,如果不关联,则使用默 #: assets/models/group.py:20 common/db/models.py:36 ops/models/adhoc.py:26 #: ops/models/job.py:157 ops/models/playbook.py:32 rbac/models/role.py:37 #: settings/models.py:37 terminal/models/applet/applet.py:45 -#: terminal/models/applet/applet.py:319 terminal/models/applet/host.py:143 +#: terminal/models/applet/applet.py:321 terminal/models/applet/host.py:143 #: terminal/models/component/endpoint.py:24 #: terminal/models/component/endpoint.py:104 -#: terminal/models/session/session.py:46 tickets/models/comment.py:32 +#: terminal/models/session/session.py:46 +#: terminal/models/virtualapp/virtualapp.py:28 tickets/models/comment.py:32 #: tickets/models/ticket/general.py:297 users/models/user.py:834 #: xpack/plugins/cloud/models.py:39 xpack/plugins/cloud/models.py:109 msgid "Comment" @@ -930,9 +938,7 @@ msgstr "" "ください。 " #: accounts/serializers/automations/base.py:23 -#: assets/models/asset/common.py:165 assets/models/automations/base.py:18 -#: assets/models/cmd_filter.py:32 assets/serializers/automations/base.py:21 -#: perms/models/asset_permission.py:72 +#: assets/serializers/automations/base.py:21 msgid "Nodes" msgstr "ノード" @@ -966,7 +972,7 @@ msgstr "自動タスク実行履歴" #: accounts/serializers/automations/change_secret.py:149 audits/const.py:61 #: audits/models.py:64 audits/signal_handlers/activity_log.py:33 #: common/const/choices.py:18 ops/const.py:72 ops/serializers/celery.py:40 -#: terminal/const.py:77 terminal/models/session/sharing.py:121 +#: terminal/const.py:78 terminal/models/session/sharing.py:121 #: tickets/views/approve.py:117 msgid "Success" msgstr "成功" @@ -1080,7 +1086,7 @@ msgid "1-100, the lower the value will be match first" msgstr "1-100、低い値は最初に一致します" #: acls/models/base.py:42 assets/models/cmd_filter.py:86 -#: authentication/serializers/connect_token_secret.py:90 +#: authentication/serializers/connect_token_secret.py:91 msgid "Reviewers" msgstr "レビュー担当者" @@ -1103,7 +1109,7 @@ msgid "Accounts" msgstr "アカウント" #: acls/models/command_acl.py:16 assets/models/cmd_filter.py:60 -#: ops/serializers/job.py:54 terminal/const.py:85 +#: ops/serializers/job.py:54 terminal/const.py:86 #: terminal/models/session/session.py:42 terminal/serializers/command.py:18 #: terminal/templates/terminal/_msg_command_alert.html:12 #: terminal/templates/terminal/_msg_command_execute_alert.html:10 @@ -1117,7 +1123,7 @@ msgid "Regex" msgstr "正規情報" #: acls/models/command_acl.py:26 assets/models/cmd_filter.py:79 -#: settings/serializers/feature.py:17 xpack/plugins/license/models.py:30 +#: settings/serializers/feature.py:19 xpack/plugins/license/models.py:30 msgid "Content" msgstr "コンテンツ" @@ -1131,7 +1137,7 @@ msgstr "家を無視する" #: acls/models/command_acl.py:33 acls/models/command_acl.py:97 #: acls/serializers/command_acl.py:29 -#: authentication/serializers/connect_token_secret.py:87 +#: authentication/serializers/connect_token_secret.py:88 #: terminal/templates/terminal/_msg_command_warning.html:14 msgid "Command group" msgstr "コマンドグループ" @@ -1287,7 +1293,7 @@ msgid "Applications" msgstr "アプリケーション" #: applications/models.py:16 xpack/plugins/cloud/models.py:37 -#: xpack/plugins/cloud/serializers/account.py:63 +#: xpack/plugins/cloud/serializers/account.py:67 msgid "Attrs" msgstr "ツールバーの" @@ -1357,7 +1363,7 @@ msgid "Authentication failed" msgstr "認証に失敗しました" #: assets/automations/ping_gateway/manager.py:60 -#: assets/automations/ping_gateway/manager.py:86 terminal/const.py:101 +#: assets/automations/ping_gateway/manager.py:86 terminal/const.py:102 msgid "Connect failed" msgstr "接続に失敗しました" @@ -1402,7 +1408,7 @@ msgstr "脚本" #: assets/const/category.py:10 assets/models/asset/host.py:8 #: settings/serializers/auth/radius.py:16 settings/serializers/auth/sms.py:71 -#: settings/serializers/feature.py:47 terminal/models/component/endpoint.py:13 +#: settings/serializers/feature.py:49 terminal/models/component/endpoint.py:13 #: terminal/serializers/applet.py:17 #: xpack/plugins/cloud/serializers/account_attrs.py:72 msgid "Host" @@ -1641,9 +1647,11 @@ msgid "Username same with user" msgstr "ユーザーと同じユーザー名" #: assets/models/_user.py:52 authentication/models/connection_token.py:41 -#: authentication/serializers/connect_token_secret.py:113 -#: terminal/models/applet/applet.py:42 terminal/serializers/session.py:19 -#: terminal/serializers/session.py:45 terminal/serializers/storage.py:71 +#: authentication/serializers/connect_token_secret.py:114 +#: terminal/models/applet/applet.py:42 +#: terminal/models/virtualapp/virtualapp.py:24 +#: terminal/serializers/session.py:19 terminal/serializers/session.py:45 +#: terminal/serializers/storage.py:71 msgid "Protocol" msgstr "プロトコル" @@ -1704,17 +1712,24 @@ msgstr "アドレス" #: assets/models/asset/common.py:161 assets/models/platform.py:120 #: authentication/backends/passkey/models.py:12 -#: authentication/serializers/connect_token_secret.py:117 +#: authentication/serializers/connect_token_secret.py:118 #: perms/serializers/user_permission.py:24 xpack/plugins/cloud/models.py:321 msgid "Platform" msgstr "プラットフォーム" #: assets/models/asset/common.py:163 assets/models/domain.py:22 -#: authentication/serializers/connect_token_secret.py:135 +#: authentication/serializers/connect_token_secret.py:136 #: perms/serializers/user_permission.py:28 xpack/plugins/cloud/models.py:323 msgid "Domain" msgstr "ドメイン" +#: assets/models/asset/common.py:165 assets/models/automations/base.py:18 +#: assets/models/cmd_filter.py:32 assets/models/node.py:553 +#: perms/models/asset_permission.py:72 perms/serializers/permission.py:36 +#: tickets/models/ticket/apply_asset.py:14 xpack/plugins/cloud/models.py:322 +msgid "Node" +msgstr "ノード" + #: assets/models/asset/common.py:167 assets/serializers/asset/common.py:380 #: assets/serializers/asset/host.py:11 msgid "Gathered info" @@ -1760,7 +1775,7 @@ msgstr "クライアントキー" msgid "Allow invalid cert" msgstr "証明書チェックを無視" -#: assets/models/asset/gpt.py:8 +#: assets/models/asset/gpt.py:8 settings/serializers/feature.py:73 msgid "Proxy" msgstr "プロキシー" @@ -1853,7 +1868,7 @@ msgstr "システム" #: assets/serializers/cagegory.py:11 assets/serializers/cagegory.py:18 #: assets/serializers/cagegory.py:24 #: authentication/models/connection_token.py:29 -#: authentication/serializers/connect_token_secret.py:124 +#: authentication/serializers/connect_token_secret.py:125 #: common/serializers/common.py:86 labels/models.py:12 settings/models.py:33 #: users/models/preference.py:13 msgid "Value" @@ -1862,7 +1877,7 @@ msgstr "値" #: assets/models/label.py:40 assets/serializers/cagegory.py:10 #: assets/serializers/cagegory.py:17 assets/serializers/cagegory.py:23 #: assets/serializers/platform.py:119 -#: authentication/serializers/connect_token_secret.py:123 +#: authentication/serializers/connect_token_secret.py:124 #: common/serializers/common.py:85 labels/models.py:17 #: perms/serializers/user_permission.py:27 settings/serializers/msg.py:83 msgid "Label" @@ -1872,7 +1887,7 @@ msgstr "ラベル" msgid "New node" msgstr "新しいノード" -#: assets/models/node.py:467 audits/backends/db.py:55 audits/backends/db.py:56 +#: assets/models/node.py:467 audits/backends/db.py:65 audits/backends/db.py:66 msgid "empty" msgstr "空" @@ -1888,11 +1903,6 @@ msgstr "フルバリュー" msgid "Parent key" msgstr "親キー" -#: assets/models/node.py:553 perms/serializers/permission.py:36 -#: tickets/models/ticket/apply_asset.py:14 xpack/plugins/cloud/models.py:322 -msgid "Node" -msgstr "ノード" - #: assets/models/node.py:556 msgid "Can match node" msgstr "ノードを一致させることができます" @@ -2029,8 +2039,8 @@ msgstr "" "ラットフォーム" #: assets/serializers/asset/common.py:124 assets/serializers/platform.py:141 -#: authentication/serializers/connect_token_secret.py:29 -#: authentication/serializers/connect_token_secret.py:74 +#: authentication/serializers/connect_token_secret.py:30 +#: authentication/serializers/connect_token_secret.py:75 #: perms/models/asset_permission.py:76 perms/serializers/permission.py:41 #: perms/serializers/user_permission.py:74 xpack/plugins/cloud/models.py:324 #: xpack/plugins/cloud/serializers/task.py:31 @@ -2121,7 +2131,7 @@ msgid "Disk total" msgstr "ディスクの合計" #: assets/serializers/asset/info/gathered.py:16 -#: authentication/serializers/connect_token_secret.py:114 +#: authentication/serializers/connect_token_secret.py:115 msgid "OS" msgstr "OS" @@ -2286,11 +2296,11 @@ msgstr "一致する資産がない、タスクを停止" msgid "Audits" msgstr "監査" -#: audits/backends/db.py:15 +#: audits/backends/db.py:16 msgid "The text content is too long. Use Elasticsearch to store operation logs" msgstr "文章の内容が長すぎる。Elasticsearchで操作履歴を保存する" -#: audits/backends/db.py:81 +#: audits/backends/db.py:91 msgid "Tips" msgstr "謎々" @@ -2366,8 +2376,9 @@ msgid "Close" msgstr "閉じる" #: audits/const.py:43 settings/serializers/terminal.py:6 -#: terminal/models/applet/host.py:26 terminal/models/component/terminal.py:164 -#: terminal/serializers/session.py:52 terminal/serializers/session.py:66 +#: terminal/models/applet/host.py:26 terminal/models/component/terminal.py:174 +#: terminal/models/virtualapp/provider.py:14 terminal/serializers/session.py:52 +#: terminal/serializers/session.py:66 msgid "Terminal" msgstr "ターミナル" @@ -2434,12 +2445,12 @@ msgstr "セッション" msgid "File transfer log" msgstr "ファイル転送ログ" -#: audits/models.py:94 audits/serializers.py:89 +#: audits/models.py:94 audits/serializers.py:86 msgid "Resource Type" msgstr "リソースタイプ" #: audits/models.py:95 audits/models.py:98 audits/models.py:144 -#: audits/serializers.py:88 labels/serializers.py:34 +#: audits/serializers.py:85 labels/serializers.py:34 msgid "Resource" msgstr "リソース" @@ -2493,7 +2504,7 @@ msgid "Date login" msgstr "日付ログイン" #: audits/models.py:212 audits/models.py:266 audits/serializers.py:70 -#: audits/serializers.py:187 +#: audits/serializers.py:184 msgid "Authentication backend" msgstr "認証バックエンド" @@ -2523,12 +2534,12 @@ msgstr "作成者" msgid "Reason display" msgstr "理由表示" -#: audits/serializers.py:137 +#: audits/serializers.py:134 #, python-format msgid "User %s %s this resource" msgstr "ユーザー %s %s が現在のリソースをサブスクライブしました。" -#: audits/serializers.py:175 authentication/models/connection_token.py:47 +#: audits/serializers.py:172 authentication/models/connection_token.py:47 #: authentication/models/temp_token.py:13 perms/models/asset_permission.py:80 #: tickets/models/ticket/apply_application.py:31 #: tickets/models/ticket/apply_asset.py:20 users/models/user.py:839 @@ -2613,6 +2624,11 @@ msgstr "外部ストレージへのFTPファイルのアップロード" msgid "Access keys can be created at most 10" msgstr "最大10個のアクセスキーを作成できます" +#: authentication/api/common.py:34 settings/serializers/auth/sms.py:117 +#, python-format +msgid "The value in the parameter must contain %s" +msgstr "パラメータの値には必ず %s が含まれます" + #: authentication/api/confirm.py:50 msgid "This action require verify your MFA" msgstr "この操作には、MFAを検証する必要があります" @@ -2880,11 +2896,15 @@ msgstr "DingTalkはバインドされていません" msgid "FeiShu is not bound" msgstr "本を飛ばすは拘束されていません" -#: authentication/errors/mfa.py:38 +#: authentication/errors/mfa.py:38 authentication/views/slack.py:127 +msgid "Slack is not bound" +msgstr "Slackはバインドされていません" + +#: authentication/errors/mfa.py:43 msgid "Your password is invalid" msgstr "パスワードが無効です" -#: authentication/errors/mfa.py:43 +#: authentication/errors/mfa.py:48 #, python-format msgid "Please wait for %s seconds before retry" msgstr "%s 秒後に再試行してください" @@ -3092,11 +3112,11 @@ msgstr "ユーザーなしまたは期限切れのユーザー" msgid "No asset or inactive asset" msgstr "アセットがないか、有効化されていないアセット" -#: authentication/models/connection_token.py:265 +#: authentication/models/connection_token.py:274 msgid "Can view super connection token secret" msgstr "スーパー接続トークンのシークレットを表示できます" -#: authentication/models/connection_token.py:267 +#: authentication/models/connection_token.py:276 msgid "Super connection token" msgstr "スーパー接続トークン" @@ -3124,19 +3144,19 @@ msgstr "異なる都市ログインのリマインダー" msgid "binding reminder" msgstr "バインディングリマインダー" -#: authentication/serializers/connect_token_secret.py:115 +#: authentication/serializers/connect_token_secret.py:116 msgid "Is builtin" msgstr "ビルトイン" -#: authentication/serializers/connect_token_secret.py:119 +#: authentication/serializers/connect_token_secret.py:120 msgid "Options" msgstr "オプション" -#: authentication/serializers/connect_token_secret.py:126 +#: authentication/serializers/connect_token_secret.py:127 msgid "Component" msgstr "コンポーネント" -#: authentication/serializers/connect_token_secret.py:137 +#: authentication/serializers/connect_token_secret.py:138 msgid "Expired now" msgstr "すぐに期限切れ" @@ -3416,7 +3436,7 @@ msgid "Do you want to retry ?" msgstr "再試行しますか?" #: authentication/utils.py:23 common/utils/ip/geoip/utils.py:24 -#: xpack/plugins/cloud/const.py:29 +#: xpack/plugins/cloud/const.py:32 msgid "LAN" msgstr "ローカルエリアネットワーク" @@ -3545,12 +3565,6 @@ msgstr "DingTalkエラー" msgid "Slack is already bound" msgstr "DingTalkはすでにバインドされています" -#: authentication/views/slack.py:127 -#, fuzzy -#| msgid "DingTalk is not bound" -msgid "Slack is not bound" -msgstr "DingTalkはバインドされていません" - #: authentication/views/slack.py:128 #, fuzzy #| msgid "Failed to get user from DingTalk" @@ -3593,7 +3607,7 @@ msgstr "タイミングトリガー" msgid "Ready" msgstr "の準備を" -#: common/const/choices.py:16 terminal/const.py:76 tickets/const.py:29 +#: common/const/choices.py:16 terminal/const.py:77 tickets/const.py:29 #: tickets/const.py:39 msgid "Pending" msgstr "未定" @@ -3670,7 +3684,7 @@ msgstr "無効なID、リストでなければなりません" #: common/serializers/fields.py:132 tickets/serializers/ticket/common.py:58 #: xpack/plugins/cloud/serializers/account_attrs.py:56 #: xpack/plugins/cloud/serializers/account_attrs.py:79 -#: xpack/plugins/cloud/serializers/account_attrs.py:143 +#: xpack/plugins/cloud/serializers/account_attrs.py:150 msgid "This field is required." msgstr "このフィールドは必須です。" @@ -4690,6 +4704,7 @@ msgid "Users amount" msgstr "ユーザー数" #: rbac/serializers/role.py:28 terminal/models/applet/applet.py:34 +#: terminal/models/virtualapp/virtualapp.py:20 msgid "Display name" msgstr "表示名" @@ -4750,7 +4765,7 @@ msgid "My assets" msgstr "私の資産" #: rbac/tree.py:58 terminal/models/applet/applet.py:52 -#: terminal/models/applet/applet.py:315 terminal/models/applet/host.py:30 +#: terminal/models/applet/applet.py:317 terminal/models/applet/host.py:30 #: terminal/serializers/applet.py:15 msgid "Applet" msgstr "リモートアプリケーション" @@ -4759,7 +4774,7 @@ msgstr "リモートアプリケーション" msgid "Ticket comment" msgstr "チケットコメント" -#: rbac/tree.py:130 settings/serializers/feature.py:58 +#: rbac/tree.py:130 settings/serializers/feature.py:98 #: tickets/models/ticket/general.py:307 msgid "Ticket" msgstr "チケット" @@ -4841,31 +4856,31 @@ msgstr "金庫の設定を変えることができます" msgid "Can change system msg sub setting" msgstr "システムmsgサブ设定を変更できます" -#: settings/models.py:167 +#: settings/models.py:168 msgid "Can change sms setting" msgstr "Smsの設定を変えることができます" -#: settings/models.py:168 +#: settings/models.py:169 msgid "Can change security setting" msgstr "セキュリティ設定を変更できます" -#: settings/models.py:169 +#: settings/models.py:170 msgid "Can change clean setting" msgstr "きれいな設定を変えることができます" -#: settings/models.py:170 +#: settings/models.py:171 msgid "Can change interface setting" msgstr "インターフェイスの設定を変えることができます" -#: settings/models.py:171 +#: settings/models.py:172 msgid "Can change license setting" msgstr "ライセンス設定を変更できます" -#: settings/models.py:172 +#: settings/models.py:173 msgid "Can change terminal setting" msgstr "ターミナルの設定を変えることができます" -#: settings/models.py:173 +#: settings/models.py:174 msgid "Can change other setting" msgstr "他の設定を変えることができます" @@ -5310,11 +5325,6 @@ msgstr "URL" msgid "Request method" msgstr "請求方法です" -#: settings/serializers/auth/sms.py:117 -#, python-format -msgid "The value in the parameter must contain %s" -msgstr "パラメータの値には必ず %s が含まれます" - #: settings/serializers/auth/sso.py:16 msgid "Enable SSO auth" msgstr "SSO Token認証の有効化" @@ -5329,7 +5339,7 @@ msgid "SSO auth key TTL" msgstr "Token有効期間" #: settings/serializers/auth/sso.py:20 -#: xpack/plugins/cloud/serializers/account_attrs.py:193 +#: xpack/plugins/cloud/serializers/account_attrs.py:200 msgid "Unit: second" msgstr "単位: 秒" @@ -5425,27 +5435,27 @@ msgstr "" "この期間を超えるセッション、録音、およびコマンド レコードは削除されます (デー" "タベースのバックアップに影響し、OSS などには影響しません)" -#: settings/serializers/feature.py:16 +#: settings/serializers/feature.py:18 msgid "Subject" msgstr "件名" -#: settings/serializers/feature.py:20 +#: settings/serializers/feature.py:22 msgid "More url" msgstr "もっとURL" -#: settings/serializers/feature.py:34 settings/serializers/feature.py:37 +#: settings/serializers/feature.py:36 settings/serializers/feature.py:39 msgid "Announcement" msgstr "発表" -#: settings/serializers/feature.py:36 +#: settings/serializers/feature.py:38 msgid "Enable announcement" msgstr "アナウンスの有効化" -#: settings/serializers/feature.py:44 +#: settings/serializers/feature.py:46 settings/serializers/feature.py:64 msgid "Enable Vault" msgstr "有効化 Vault" -#: settings/serializers/feature.py:53 +#: settings/serializers/feature.py:55 msgid "Mount Point" msgstr "マウントポイント" @@ -5453,39 +5463,39 @@ msgstr "マウントポイント" msgid "Enable tickets" msgstr "チケットを有効にする" -#: settings/serializers/feature.py:63 +#: settings/serializers/feature.py:103 msgid "Ticket authorize default time" msgstr "デフォルト製造オーダ承認時間" -#: settings/serializers/feature.py:66 +#: settings/serializers/feature.py:106 msgid "day" msgstr "日" -#: settings/serializers/feature.py:66 +#: settings/serializers/feature.py:106 msgid "hour" msgstr "時" -#: settings/serializers/feature.py:67 +#: settings/serializers/feature.py:107 msgid "Ticket authorize default time unit" msgstr "デフォルト製造オーダ承認時間単位" -#: settings/serializers/feature.py:72 +#: settings/serializers/feature.py:112 msgid "Feature" msgstr "機能" -#: settings/serializers/feature.py:75 +#: settings/serializers/feature.py:115 msgid "Operation center" msgstr "職業センター" -#: settings/serializers/feature.py:76 +#: settings/serializers/feature.py:116 msgid "Allow user run batch command or not using ansible" msgstr "ユーザー実行バッチコマンドを許可するか、ansibleを使用しない" -#: settings/serializers/feature.py:80 +#: settings/serializers/feature.py:120 msgid "Operation center command blacklist" msgstr "オペレーション センター コマンド ブラックリスト" -#: settings/serializers/feature.py:81 +#: settings/serializers/feature.py:121 msgid "Commands that are not allowed execute." msgstr "実行が許可されていないコマンド" @@ -5890,7 +5900,7 @@ msgstr "複数のユーザーを使用して、分割" msgid "[%s] %s" msgstr "[%s] %s" -#: settings/serializers/terminal.py:9 +#: settings/serializers/terminal.py:9 terminal/models/virtualapp/provider.py:11 msgid "Hostname" msgstr "ホスト名" @@ -6262,6 +6272,8 @@ msgid "Offline video player" msgstr "オフラインビデオプレーヤー" #: terminal/api/applet/applet.py:48 terminal/api/applet/applet.py:51 +#: terminal/api/virtualapp/virtualapp.py:43 +#: terminal/api/virtualapp/virtualapp.py:46 msgid "Invalid zip file" msgstr "zip ファイルが無効です" @@ -6387,7 +6399,7 @@ msgstr "クリティカル" msgid "High" msgstr "高い" -#: terminal/const.py:47 terminal/const.py:83 +#: terminal/const.py:47 terminal/const.py:84 #: users/templates/users/reset_password.html:50 msgid "Normal" msgstr "正常" @@ -6396,43 +6408,43 @@ msgstr "正常" msgid "Offline" msgstr "オフライン" -#: terminal/const.py:79 +#: terminal/const.py:80 msgid "Mismatch" msgstr "一致しない" -#: terminal/const.py:84 +#: terminal/const.py:85 msgid "Tunnel" msgstr "ちかチャネル" -#: terminal/const.py:90 +#: terminal/const.py:91 msgid "Read only" msgstr "読み取り専用" -#: terminal/const.py:91 +#: terminal/const.py:92 msgid "Writable" msgstr "書き込み可能" -#: terminal/const.py:95 +#: terminal/const.py:96 msgid "Kill session" msgstr "セッションを終了する" -#: terminal/const.py:96 +#: terminal/const.py:97 msgid "Lock session" msgstr "セッションをロックする" -#: terminal/const.py:97 +#: terminal/const.py:98 msgid "Unlock session" msgstr "セッションのロックを解除する" -#: terminal/const.py:102 +#: terminal/const.py:103 msgid "Replay create failed" msgstr "ビデオの作成に失敗しました" -#: terminal/const.py:103 +#: terminal/const.py:104 msgid "Replay upload failed" msgstr "動画のアップロードに失敗しました" -#: terminal/const.py:104 +#: terminal/const.py:105 msgid "Replay convert failed" msgstr "ビデオのトランスコーディングに失敗しました" @@ -6453,6 +6465,7 @@ msgid "Enterprise" msgstr "エンタープライズ版" #: terminal/models/applet/applet.py:36 +#: terminal/models/virtualapp/virtualapp.py:22 msgid "Author" msgstr "著者" @@ -6465,6 +6478,7 @@ msgid "Can concurrent" msgstr "同時実行可能" #: terminal/models/applet/applet.py:44 +#: terminal/models/virtualapp/virtualapp.py:29 msgid "Tags" msgstr "ラベル" @@ -6473,6 +6487,7 @@ msgid "Hosts" msgstr "ホスト" #: terminal/models/applet/applet.py:93 +#: terminal/models/virtualapp/virtualapp.py:66 msgid "Applet pkg not valid, Missing file {}" msgstr "無効なアプレット パッケージ、ファイル {} がありません" @@ -6488,7 +6503,7 @@ msgstr "カスタムプラットフォームのみをサポート" msgid "Missing type in platform.yml" msgstr "platform.ymlにタイプがありません" -#: terminal/models/applet/applet.py:317 terminal/models/applet/host.py:36 +#: terminal/models/applet/applet.py:319 terminal/models/applet/host.py:36 #: terminal/models/applet/host.py:138 msgid "Hosting" msgstr "ホスト マシン" @@ -6623,7 +6638,7 @@ msgstr "リモートアドレス" msgid "Application User" msgstr "ユーザーの適用" -#: terminal/models/component/terminal.py:166 +#: terminal/models/component/terminal.py:176 msgid "Can view terminal config" msgstr "ターミナル構成を表示できます" @@ -6782,7 +6797,7 @@ msgstr "テスト失敗: アカウントが無効" msgid "Invalid storage" msgstr "無効なストレージ" -#: terminal/serializers/applet.py:28 +#: terminal/serializers/applet.py:28 terminal/serializers/virtualapp.py:15 msgid "Icon" msgstr "アイコン" @@ -6846,6 +6861,7 @@ msgid "RDS Remote App Logoff Time Limit" msgstr "RDS 远程应用注销时间限制" #: terminal/serializers/applet_host.py:59 terminal/serializers/terminal.py:47 +#: terminal/serializers/virtualapp_provider.py:13 msgid "Load status" msgstr "ロードステータス" @@ -7019,7 +7035,7 @@ msgid "HOST" msgstr "ホスト" #: terminal/serializers/storage.py:146 users/models/user.py:828 -#: xpack/plugins/cloud/serializers/account_attrs.py:206 +#: xpack/plugins/cloud/serializers/account_attrs.py:213 msgid "Private key" msgstr "ssh秘密鍵" @@ -7593,7 +7609,7 @@ msgid "Not a valid ssh public key" msgstr "有効なssh公開鍵ではありません" #: users/forms/profile.py:173 users/models/user.py:831 -#: xpack/plugins/cloud/serializers/account_attrs.py:203 +#: xpack/plugins/cloud/serializers/account_attrs.py:210 msgid "Public key" msgstr "公開キー" @@ -8225,43 +8241,55 @@ msgstr "スカイウィング私有雲" msgid "OpenStack" msgstr "OpenStack" -#: xpack/plugins/cloud/const.py:28 +#: xpack/plugins/cloud/const.py:28 xpack/plugins/cloud/providers/zstack.py:21 +msgid "ZStack" +msgstr "ZStack" + +#: xpack/plugins/cloud/const.py:29 msgid "Fusion Compute" msgstr "融合計算" -#: xpack/plugins/cloud/const.py:33 +#: xpack/plugins/cloud/const.py:30 +msgid "SCP" +msgstr "SCP" + +#: xpack/plugins/cloud/const.py:31 +msgid "Apsara Stack" +msgstr "Apsara Stack" + +#: xpack/plugins/cloud/const.py:36 msgid "Private IP" msgstr "プライベートIP" -#: xpack/plugins/cloud/const.py:34 +#: xpack/plugins/cloud/const.py:37 msgid "Public IP" msgstr "パブリックIP" -#: xpack/plugins/cloud/const.py:38 xpack/plugins/cloud/models.py:295 +#: xpack/plugins/cloud/const.py:41 xpack/plugins/cloud/models.py:295 msgid "Instance name" msgstr "インスタンス名" -#: xpack/plugins/cloud/const.py:39 +#: xpack/plugins/cloud/const.py:42 msgid "Instance name and Partial IP" msgstr "インスタンス名と部分IP" -#: xpack/plugins/cloud/const.py:44 +#: xpack/plugins/cloud/const.py:47 msgid "Succeed" msgstr "成功" -#: xpack/plugins/cloud/const.py:48 +#: xpack/plugins/cloud/const.py:51 msgid "Unsync" msgstr "同期していません" -#: xpack/plugins/cloud/const.py:49 +#: xpack/plugins/cloud/const.py:52 msgid "New Sync" msgstr "新しい同期" -#: xpack/plugins/cloud/const.py:50 +#: xpack/plugins/cloud/const.py:53 msgid "Synced" msgstr "同期済み" -#: xpack/plugins/cloud/const.py:51 +#: xpack/plugins/cloud/const.py:54 msgid "Released" msgstr "リリース済み" @@ -8607,11 +8635,11 @@ msgstr "TR-Istanbul" msgid "CN East-Suqian" msgstr "華東-宿遷" -#: xpack/plugins/cloud/serializers/account.py:64 +#: xpack/plugins/cloud/serializers/account.py:68 msgid "Validity display" msgstr "有効表示" -#: xpack/plugins/cloud/serializers/account.py:65 +#: xpack/plugins/cloud/serializers/account.py:69 msgid "Provider display" msgstr "プロバイダ表示" @@ -8628,50 +8656,50 @@ msgid "Subscription ID" msgstr "サブスクリプションID" #: xpack/plugins/cloud/serializers/account_attrs.py:98 -#: xpack/plugins/cloud/serializers/account_attrs.py:103 -#: xpack/plugins/cloud/serializers/account_attrs.py:119 -#: xpack/plugins/cloud/serializers/account_attrs.py:149 -#: xpack/plugins/cloud/serializers/account_attrs.py:199 +#: xpack/plugins/cloud/serializers/account_attrs.py:102 +#: xpack/plugins/cloud/serializers/account_attrs.py:126 +#: xpack/plugins/cloud/serializers/account_attrs.py:156 +#: xpack/plugins/cloud/serializers/account_attrs.py:206 msgid "API Endpoint" msgstr "APIエンドポイント" -#: xpack/plugins/cloud/serializers/account_attrs.py:109 +#: xpack/plugins/cloud/serializers/account_attrs.py:108 msgid "Auth url" msgstr "認証アドレス" -#: xpack/plugins/cloud/serializers/account_attrs.py:110 +#: xpack/plugins/cloud/serializers/account_attrs.py:109 msgid "eg: http://openstack.example.com:5000/v3" msgstr "例えば: http://openstack.example.com:5000/v3" -#: xpack/plugins/cloud/serializers/account_attrs.py:113 +#: xpack/plugins/cloud/serializers/account_attrs.py:112 msgid "User domain" msgstr "ユーザードメイン" -#: xpack/plugins/cloud/serializers/account_attrs.py:120 +#: xpack/plugins/cloud/serializers/account_attrs.py:127 msgid "Cert File" msgstr "証明書ファイル" -#: xpack/plugins/cloud/serializers/account_attrs.py:121 +#: xpack/plugins/cloud/serializers/account_attrs.py:128 msgid "Key File" msgstr "キーファイル" -#: xpack/plugins/cloud/serializers/account_attrs.py:137 +#: xpack/plugins/cloud/serializers/account_attrs.py:144 msgid "Service account key" msgstr "サービスアカウントキー" -#: xpack/plugins/cloud/serializers/account_attrs.py:138 +#: xpack/plugins/cloud/serializers/account_attrs.py:145 msgid "The file is in JSON format" msgstr "ファイルはJSON形式です。" -#: xpack/plugins/cloud/serializers/account_attrs.py:156 +#: xpack/plugins/cloud/serializers/account_attrs.py:163 msgid "IP address invalid `{}`, {}" msgstr "IPアドレスが無効: '{}', {}" -#: xpack/plugins/cloud/serializers/account_attrs.py:172 +#: xpack/plugins/cloud/serializers/account_attrs.py:179 msgid "Such as: 192.168.1.0/24, 10.0.0.0-10.0.0.255" msgstr "例:192.168.1.0/24、10.0.0.0.0-10.0.0.255" -#: xpack/plugins/cloud/serializers/account_attrs.py:175 +#: xpack/plugins/cloud/serializers/account_attrs.py:182 msgid "" "The port is used to detect the validity of the IP address. When the " "synchronization task is executed, only the valid IP address will be " @@ -8681,23 +8709,23 @@ msgstr "" "実行されると、有効な IP アドレスのみが同期されます。
    ポートが0の場合、す" "べてのIPアドレスが有効です。" -#: xpack/plugins/cloud/serializers/account_attrs.py:183 +#: xpack/plugins/cloud/serializers/account_attrs.py:190 msgid "Hostname prefix" msgstr "ホスト名プレフィックス" -#: xpack/plugins/cloud/serializers/account_attrs.py:186 +#: xpack/plugins/cloud/serializers/account_attrs.py:193 msgid "IP segment" msgstr "IP セグメント" -#: xpack/plugins/cloud/serializers/account_attrs.py:190 +#: xpack/plugins/cloud/serializers/account_attrs.py:197 msgid "Test port" msgstr "テストポート" -#: xpack/plugins/cloud/serializers/account_attrs.py:193 +#: xpack/plugins/cloud/serializers/account_attrs.py:200 msgid "Test timeout" msgstr "テストタイムアウト" -#: xpack/plugins/cloud/serializers/account_attrs.py:209 +#: xpack/plugins/cloud/serializers/account_attrs.py:216 msgid "Project" msgstr "project" @@ -8803,9 +8831,3 @@ msgstr "エンタープライズ・フラッグシップ・エディション" #~ msgid "Binding FeiShu successfully" #~ msgstr "本を飛ばすのバインドに成功" - -#~ msgid "Beian link" -#~ msgstr "公安オンライン申告ジャンプリンク" - -#~ msgid "Beian text" -#~ msgstr "公安網登録番号" diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index 0ef39aa36..46f3f36ec 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: JumpServer 0.3.3\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-12-05 10:16+0800\n" +"POT-Creation-Date: 2023-12-08 14:51+0800\n" "PO-Revision-Date: 2021-05-20 10:54+0800\n" "Last-Translator: ibuler \n" "Language-Team: JumpServer team\n" @@ -48,7 +48,7 @@ msgstr "Access key" #: accounts/const/account.py:9 assets/models/_user.py:48 #: authentication/backends/passkey/models.py:16 -#: authentication/models/sso_token.py:14 settings/serializers/feature.py:50 +#: authentication/models/sso_token.py:14 settings/serializers/feature.py:52 msgid "Token" msgstr "Token" @@ -98,7 +98,7 @@ msgstr "更新" #: accounts/const/account.py:33 #: accounts/serializers/automations/change_secret.py:150 audits/const.py:62 #: audits/signal_handlers/activity_log.py:33 common/const/choices.py:19 -#: ops/const.py:74 terminal/const.py:78 xpack/plugins/cloud/const.py:43 +#: ops/const.py:74 terminal/const.py:79 xpack/plugins/cloud/const.py:46 msgid "Failed" msgstr "失败" @@ -201,7 +201,7 @@ msgstr "仅创建" msgid "Email" msgstr "邮箱" -#: accounts/const/automation.py:104 terminal/const.py:86 +#: accounts/const/automation.py:104 terminal/const.py:87 msgid "SFTP" msgstr "SFTP" @@ -210,7 +210,7 @@ msgstr "SFTP" msgid "Database" msgstr "数据库" -#: accounts/const/vault.py:9 settings/serializers/feature.py:41 +#: accounts/const/vault.py:9 settings/serializers/feature.py:43 msgid "HCP Vault" msgstr "HashiCorp Vault" @@ -258,13 +258,14 @@ msgstr "资产" #: accounts/serializers/account/account.py:220 #: accounts/serializers/account/account.py:268 #: accounts/serializers/account/template.py:25 -#: authentication/serializers/connect_token_secret.py:49 +#: authentication/serializers/connect_token_secret.py:50 msgid "Su from" msgstr "切换自" #: accounts/models/account.py:55 assets/const/protocol.py:169 #: settings/serializers/auth/cas.py:20 settings/serializers/auth/feishu.py:20 #: terminal/models/applet/applet.py:35 +#: terminal/models/virtualapp/virtualapp.py:21 msgid "Version" msgstr "版本" @@ -465,9 +466,11 @@ msgstr "结束日期" #: accounts/models/automations/change_secret.py:43 #: assets/models/automations/base.py:113 audits/models.py:208 #: audits/serializers.py:54 ops/models/base.py:49 ops/models/job.py:232 -#: terminal/models/applet/applet.py:318 terminal/models/applet/host.py:140 -#: terminal/models/component/status.py:30 terminal/serializers/applet.py:18 -#: terminal/serializers/applet_host.py:124 tickets/models/ticket/general.py:283 +#: terminal/models/applet/applet.py:320 terminal/models/applet/host.py:140 +#: terminal/models/component/status.py:30 +#: terminal/models/virtualapp/virtualapp.py:99 +#: terminal/serializers/applet.py:18 terminal/serializers/applet_host.py:124 +#: terminal/serializers/virtualapp.py:35 tickets/models/ticket/general.py:283 #: tickets/serializers/super_ticket.py:13 #: tickets/serializers/ticket/ticket.py:20 xpack/plugins/cloud/models.py:201 #: xpack/plugins/cloud/models.py:257 @@ -533,8 +536,8 @@ msgstr "触发方式" #: accounts/models/automations/push_account.py:16 acls/models/base.py:41 #: acls/serializers/base.py:57 assets/models/cmd_filter.py:81 -#: audits/models.py:92 audits/serializers.py:87 -#: authentication/serializers/connect_token_secret.py:118 +#: audits/models.py:92 audits/serializers.py:84 +#: authentication/serializers/connect_token_secret.py:119 #: authentication/templates/authentication/_access_key_modal.html:34 msgid "Action" msgstr "动作" @@ -551,8 +554,8 @@ msgstr "账号验证" #: accounts/serializers/account/account.py:440 #: accounts/serializers/account/base.py:17 #: accounts/serializers/automations/change_secret.py:45 -#: authentication/serializers/connect_token_secret.py:41 -#: authentication/serializers/connect_token_secret.py:50 +#: authentication/serializers/connect_token_secret.py:42 +#: authentication/serializers/connect_token_secret.py:51 #: terminal/serializers/storage.py:140 msgid "Secret type" msgstr "密文类型" @@ -585,7 +588,8 @@ msgstr "密码规则" #: assets/models/platform.py:89 assets/serializers/asset/common.py:146 #: assets/serializers/platform.py:118 assets/serializers/platform.py:235 #: authentication/backends/passkey/models.py:10 -#: authentication/serializers/connect_token_secret.py:112 labels/models.py:11 +#: authentication/serializers/connect_token_secret.py:113 +#: authentication/serializers/connect_token_secret.py:168 labels/models.py:11 #: ops/mixin.py:21 ops/models/adhoc.py:20 ops/models/celery.py:15 #: ops/models/celery.py:57 ops/models/job.py:137 ops/models/playbook.py:29 #: ops/serializers/job.py:19 orgs/models.py:82 @@ -594,7 +598,9 @@ msgstr "密码规则" #: terminal/models/applet/applet.py:33 terminal/models/component/endpoint.py:12 #: terminal/models/component/endpoint.py:94 #: terminal/models/component/storage.py:26 terminal/models/component/task.py:13 -#: terminal/models/component/terminal.py:84 tickets/api/ticket.py:87 +#: terminal/models/component/terminal.py:84 +#: terminal/models/virtualapp/provider.py:10 +#: terminal/models/virtualapp/virtualapp.py:19 tickets/api/ticket.py:87 #: users/forms/profile.py:33 users/models/group.py:13 #: users/models/preference.py:11 users/models/user.py:798 #: xpack/plugins/cloud/models.py:32 xpack/plugins/cloud/models.py:273 @@ -609,9 +615,10 @@ msgstr "特权账号" #: accounts/models/base.py:70 assets/models/asset/common.py:166 #: assets/models/automations/base.py:21 assets/models/cmd_filter.py:39 #: assets/models/label.py:22 -#: authentication/serializers/connect_token_secret.py:116 +#: authentication/serializers/connect_token_secret.py:117 #: terminal/models/applet/applet.py:40 -#: terminal/models/component/endpoint.py:105 users/serializers/user.py:167 +#: terminal/models/component/endpoint.py:105 +#: terminal/models/virtualapp/virtualapp.py:23 users/serializers/user.py:167 msgid "Is active" msgstr "激活" @@ -736,8 +743,8 @@ msgstr "类别" #: assets/models/cmd_filter.py:74 assets/models/platform.py:91 #: assets/serializers/asset/common.py:123 assets/serializers/platform.py:120 #: assets/serializers/platform.py:139 audits/serializers.py:53 -#: audits/serializers.py:173 -#: authentication/serializers/connect_token_secret.py:125 ops/models/job.py:149 +#: audits/serializers.py:170 +#: authentication/serializers/connect_token_secret.py:126 ops/models/job.py:149 #: perms/serializers/user_permission.py:26 terminal/models/applet/applet.py:39 #: terminal/models/component/storage.py:57 #: terminal/models/component/storage.py:146 terminal/serializers/applet.py:29 @@ -793,7 +800,7 @@ msgid "Account has exist" msgstr "账号已存在" #: accounts/serializers/account/account.py:441 -#: authentication/serializers/connect_token_secret.py:158 +#: authentication/serializers/connect_token_secret.py:159 #: authentication/templates/authentication/_access_key_modal.html:30 #: perms/models/perm_node.py:21 users/serializers/group.py:32 msgid "ID" @@ -802,7 +809,7 @@ msgstr "ID" #: accounts/serializers/account/account.py:451 acls/serializers/base.py:116 #: assets/models/cmd_filter.py:24 assets/models/label.py:16 audits/models.py:54 #: audits/models.py:90 audits/models.py:172 audits/models.py:269 -#: audits/serializers.py:174 authentication/models/connection_token.py:32 +#: audits/serializers.py:171 authentication/models/connection_token.py:32 #: authentication/models/sso_token.py:16 #: notifications/models/notification.py:12 #: perms/api/user_permission/mixin.py:55 perms/models/asset_permission.py:63 @@ -907,10 +914,11 @@ msgstr "关联平台,可配置推送参数,如果不关联,将使用默认 #: assets/models/group.py:20 common/db/models.py:36 ops/models/adhoc.py:26 #: ops/models/job.py:157 ops/models/playbook.py:32 rbac/models/role.py:37 #: settings/models.py:37 terminal/models/applet/applet.py:45 -#: terminal/models/applet/applet.py:319 terminal/models/applet/host.py:143 +#: terminal/models/applet/applet.py:321 terminal/models/applet/host.py:143 #: terminal/models/component/endpoint.py:24 #: terminal/models/component/endpoint.py:104 -#: terminal/models/session/session.py:46 tickets/models/comment.py:32 +#: terminal/models/session/session.py:46 +#: terminal/models/virtualapp/virtualapp.py:28 tickets/models/comment.py:32 #: tickets/models/ticket/general.py:297 users/models/user.py:834 #: xpack/plugins/cloud/models.py:39 xpack/plugins/cloud/models.py:109 msgid "Comment" @@ -927,9 +935,7 @@ msgstr "" "CACHE_LOGIN_PASSWORD_ENABLED=true,重启服务才能开启" #: accounts/serializers/automations/base.py:23 -#: assets/models/asset/common.py:165 assets/models/automations/base.py:18 -#: assets/models/cmd_filter.py:32 assets/serializers/automations/base.py:21 -#: perms/models/asset_permission.py:72 +#: assets/serializers/automations/base.py:21 msgid "Nodes" msgstr "节点" @@ -963,7 +969,7 @@ msgstr "自动化任务执行历史" #: accounts/serializers/automations/change_secret.py:149 audits/const.py:61 #: audits/models.py:64 audits/signal_handlers/activity_log.py:33 #: common/const/choices.py:18 ops/const.py:72 ops/serializers/celery.py:40 -#: terminal/const.py:77 terminal/models/session/sharing.py:121 +#: terminal/const.py:78 terminal/models/session/sharing.py:121 #: tickets/views/approve.py:117 msgid "Success" msgstr "成功" @@ -1077,7 +1083,7 @@ msgid "1-100, the lower the value will be match first" msgstr "优先级可选范围为 1-100 (数值越小越优先)" #: acls/models/base.py:42 assets/models/cmd_filter.py:86 -#: authentication/serializers/connect_token_secret.py:90 +#: authentication/serializers/connect_token_secret.py:91 msgid "Reviewers" msgstr "审批人" @@ -1100,7 +1106,7 @@ msgid "Accounts" msgstr "账号管理" #: acls/models/command_acl.py:16 assets/models/cmd_filter.py:60 -#: ops/serializers/job.py:54 terminal/const.py:85 +#: ops/serializers/job.py:54 terminal/const.py:86 #: terminal/models/session/session.py:42 terminal/serializers/command.py:18 #: terminal/templates/terminal/_msg_command_alert.html:12 #: terminal/templates/terminal/_msg_command_execute_alert.html:10 @@ -1114,7 +1120,7 @@ msgid "Regex" msgstr "正则表达式" #: acls/models/command_acl.py:26 assets/models/cmd_filter.py:79 -#: settings/serializers/feature.py:17 xpack/plugins/license/models.py:30 +#: settings/serializers/feature.py:19 xpack/plugins/license/models.py:30 msgid "Content" msgstr "内容" @@ -1128,7 +1134,7 @@ msgstr "忽略大小写" #: acls/models/command_acl.py:33 acls/models/command_acl.py:97 #: acls/serializers/command_acl.py:29 -#: authentication/serializers/connect_token_secret.py:87 +#: authentication/serializers/connect_token_secret.py:88 #: terminal/templates/terminal/_msg_command_warning.html:14 msgid "Command group" msgstr "命令组" @@ -1282,7 +1288,7 @@ msgid "Applications" msgstr "应用管理" #: applications/models.py:16 xpack/plugins/cloud/models.py:37 -#: xpack/plugins/cloud/serializers/account.py:63 +#: xpack/plugins/cloud/serializers/account.py:67 msgid "Attrs" msgstr "属性" @@ -1350,7 +1356,7 @@ msgid "Authentication failed" msgstr "认证失败" #: assets/automations/ping_gateway/manager.py:60 -#: assets/automations/ping_gateway/manager.py:86 terminal/const.py:101 +#: assets/automations/ping_gateway/manager.py:86 terminal/const.py:102 msgid "Connect failed" msgstr "连接失败" @@ -1395,7 +1401,7 @@ msgstr "脚本" #: assets/const/category.py:10 assets/models/asset/host.py:8 #: settings/serializers/auth/radius.py:16 settings/serializers/auth/sms.py:71 -#: settings/serializers/feature.py:47 terminal/models/component/endpoint.py:13 +#: settings/serializers/feature.py:49 terminal/models/component/endpoint.py:13 #: terminal/serializers/applet.py:17 #: xpack/plugins/cloud/serializers/account_attrs.py:72 msgid "Host" @@ -1634,9 +1640,11 @@ msgid "Username same with user" msgstr "用户名与用户相同" #: assets/models/_user.py:52 authentication/models/connection_token.py:41 -#: authentication/serializers/connect_token_secret.py:113 -#: terminal/models/applet/applet.py:42 terminal/serializers/session.py:19 -#: terminal/serializers/session.py:45 terminal/serializers/storage.py:71 +#: authentication/serializers/connect_token_secret.py:114 +#: terminal/models/applet/applet.py:42 +#: terminal/models/virtualapp/virtualapp.py:24 +#: terminal/serializers/session.py:19 terminal/serializers/session.py:45 +#: terminal/serializers/storage.py:71 msgid "Protocol" msgstr "协议" @@ -1697,17 +1705,24 @@ msgstr "地址" #: assets/models/asset/common.py:161 assets/models/platform.py:120 #: authentication/backends/passkey/models.py:12 -#: authentication/serializers/connect_token_secret.py:117 +#: authentication/serializers/connect_token_secret.py:118 #: perms/serializers/user_permission.py:24 xpack/plugins/cloud/models.py:321 msgid "Platform" msgstr "系统平台" #: assets/models/asset/common.py:163 assets/models/domain.py:22 -#: authentication/serializers/connect_token_secret.py:135 +#: authentication/serializers/connect_token_secret.py:136 #: perms/serializers/user_permission.py:28 xpack/plugins/cloud/models.py:323 msgid "Domain" msgstr "网域" +#: assets/models/asset/common.py:165 assets/models/automations/base.py:18 +#: assets/models/cmd_filter.py:32 assets/models/node.py:553 +#: perms/models/asset_permission.py:72 perms/serializers/permission.py:36 +#: tickets/models/ticket/apply_asset.py:14 xpack/plugins/cloud/models.py:322 +msgid "Node" +msgstr "节点" + #: assets/models/asset/common.py:167 assets/serializers/asset/common.py:380 #: assets/serializers/asset/host.py:11 msgid "Gathered info" @@ -1753,7 +1768,7 @@ msgstr "客户端密钥" msgid "Allow invalid cert" msgstr "忽略证书校验" -#: assets/models/asset/gpt.py:8 +#: assets/models/asset/gpt.py:8 settings/serializers/feature.py:73 msgid "Proxy" msgstr "代理" @@ -1846,7 +1861,7 @@ msgstr "系统" #: assets/serializers/cagegory.py:11 assets/serializers/cagegory.py:18 #: assets/serializers/cagegory.py:24 #: authentication/models/connection_token.py:29 -#: authentication/serializers/connect_token_secret.py:124 +#: authentication/serializers/connect_token_secret.py:125 #: common/serializers/common.py:86 labels/models.py:12 settings/models.py:33 #: users/models/preference.py:13 msgid "Value" @@ -1855,7 +1870,7 @@ msgstr "值" #: assets/models/label.py:40 assets/serializers/cagegory.py:10 #: assets/serializers/cagegory.py:17 assets/serializers/cagegory.py:23 #: assets/serializers/platform.py:119 -#: authentication/serializers/connect_token_secret.py:123 +#: authentication/serializers/connect_token_secret.py:124 #: common/serializers/common.py:85 labels/models.py:17 #: perms/serializers/user_permission.py:27 settings/serializers/msg.py:83 msgid "Label" @@ -1865,7 +1880,7 @@ msgstr "标签" msgid "New node" msgstr "新节点" -#: assets/models/node.py:467 audits/backends/db.py:55 audits/backends/db.py:56 +#: assets/models/node.py:467 audits/backends/db.py:65 audits/backends/db.py:66 msgid "empty" msgstr "空" @@ -1881,11 +1896,6 @@ msgstr "全称" msgid "Parent key" msgstr "ssh私钥" -#: assets/models/node.py:553 perms/serializers/permission.py:36 -#: tickets/models/ticket/apply_asset.py:14 xpack/plugins/cloud/models.py:322 -msgid "Node" -msgstr "节点" - #: assets/models/node.py:556 msgid "Can match node" msgstr "可以匹配节点" @@ -2020,8 +2030,8 @@ msgid "" msgstr "资产中批量更新平台,不符合平台类型跳过的资产" #: assets/serializers/asset/common.py:124 assets/serializers/platform.py:141 -#: authentication/serializers/connect_token_secret.py:29 -#: authentication/serializers/connect_token_secret.py:74 +#: authentication/serializers/connect_token_secret.py:30 +#: authentication/serializers/connect_token_secret.py:75 #: perms/models/asset_permission.py:76 perms/serializers/permission.py:41 #: perms/serializers/user_permission.py:74 xpack/plugins/cloud/models.py:324 #: xpack/plugins/cloud/serializers/task.py:31 @@ -2112,7 +2122,7 @@ msgid "Disk total" msgstr "硬盘大小" #: assets/serializers/asset/info/gathered.py:16 -#: authentication/serializers/connect_token_secret.py:114 +#: authentication/serializers/connect_token_secret.py:115 msgid "OS" msgstr "操作系统" @@ -2270,11 +2280,11 @@ msgstr "没有匹配到资产,结束任务" msgid "Audits" msgstr "日志审计" -#: audits/backends/db.py:15 +#: audits/backends/db.py:16 msgid "The text content is too long. Use Elasticsearch to store operation logs" msgstr "文字内容太长。请使用 Elasticsearch 存储操作日志" -#: audits/backends/db.py:81 +#: audits/backends/db.py:91 msgid "Tips" msgstr "提示" @@ -2350,8 +2360,9 @@ msgid "Close" msgstr "关闭" #: audits/const.py:43 settings/serializers/terminal.py:6 -#: terminal/models/applet/host.py:26 terminal/models/component/terminal.py:164 -#: terminal/serializers/session.py:52 terminal/serializers/session.py:66 +#: terminal/models/applet/host.py:26 terminal/models/component/terminal.py:174 +#: terminal/models/virtualapp/provider.py:14 terminal/serializers/session.py:52 +#: terminal/serializers/session.py:66 msgid "Terminal" msgstr "终端" @@ -2418,12 +2429,12 @@ msgstr "会话" msgid "File transfer log" msgstr "文件管理" -#: audits/models.py:94 audits/serializers.py:89 +#: audits/models.py:94 audits/serializers.py:86 msgid "Resource Type" msgstr "资源类型" #: audits/models.py:95 audits/models.py:98 audits/models.py:144 -#: audits/serializers.py:88 labels/serializers.py:34 +#: audits/serializers.py:85 labels/serializers.py:34 msgid "Resource" msgstr "资源" @@ -2477,7 +2488,7 @@ msgid "Date login" msgstr "登录日期" #: audits/models.py:212 audits/models.py:266 audits/serializers.py:70 -#: audits/serializers.py:187 +#: audits/serializers.py:184 msgid "Authentication backend" msgstr "认证方式" @@ -2507,12 +2518,12 @@ msgstr "创建者" msgid "Reason display" msgstr "原因描述" -#: audits/serializers.py:137 +#: audits/serializers.py:134 #, python-format msgid "User %s %s this resource" msgstr "用户 %s %s 了当前资源" -#: audits/serializers.py:175 authentication/models/connection_token.py:47 +#: audits/serializers.py:172 authentication/models/connection_token.py:47 #: authentication/models/temp_token.py:13 perms/models/asset_permission.py:80 #: tickets/models/ticket/apply_application.py:31 #: tickets/models/ticket/apply_asset.py:20 users/models/user.py:839 @@ -2597,6 +2608,11 @@ msgstr "上传 FTP 文件到外部存储" msgid "Access keys can be created at most 10" msgstr "最多可以创建10个访问密钥" +#: authentication/api/common.py:34 settings/serializers/auth/sms.py:117 +#, python-format +msgid "The value in the parameter must contain %s" +msgstr "参数中的值必须包含 %s" + #: authentication/api/confirm.py:50 msgid "This action require verify your MFA" msgstr "该操作需要验证您的 MFA, 请先开启并配置" @@ -2853,11 +2869,15 @@ msgstr "钉钉没有绑定" msgid "FeiShu is not bound" msgstr "没有绑定飞书" -#: authentication/errors/mfa.py:38 +#: authentication/errors/mfa.py:38 authentication/views/slack.py:127 +msgid "Slack is not bound" +msgstr "Slack没有绑定" + +#: authentication/errors/mfa.py:43 msgid "Your password is invalid" msgstr "您的密码无效" -#: authentication/errors/mfa.py:43 +#: authentication/errors/mfa.py:48 #, python-format msgid "Please wait for %s seconds before retry" msgstr "请在 %s 秒后重试" @@ -3061,11 +3081,11 @@ msgstr "没有用户或用户失效" msgid "No asset or inactive asset" msgstr "没有资产或资产未激活" -#: authentication/models/connection_token.py:265 +#: authentication/models/connection_token.py:274 msgid "Can view super connection token secret" msgstr "可以查看超级连接令牌密文" -#: authentication/models/connection_token.py:267 +#: authentication/models/connection_token.py:276 msgid "Super connection token" msgstr "超级连接令牌" @@ -3093,19 +3113,19 @@ msgstr "异地登录提醒" msgid "binding reminder" msgstr "绑定提醒" -#: authentication/serializers/connect_token_secret.py:115 +#: authentication/serializers/connect_token_secret.py:116 msgid "Is builtin" msgstr "内置的" -#: authentication/serializers/connect_token_secret.py:119 +#: authentication/serializers/connect_token_secret.py:120 msgid "Options" msgstr "选项" -#: authentication/serializers/connect_token_secret.py:126 +#: authentication/serializers/connect_token_secret.py:127 msgid "Component" msgstr "组件" -#: authentication/serializers/connect_token_secret.py:137 +#: authentication/serializers/connect_token_secret.py:138 msgid "Expired now" msgstr "立刻过期" @@ -3373,7 +3393,7 @@ msgid "Do you want to retry ?" msgstr "是否重试 ?" #: authentication/utils.py:23 common/utils/ip/geoip/utils.py:24 -#: xpack/plugins/cloud/const.py:29 +#: xpack/plugins/cloud/const.py:32 msgid "LAN" msgstr "局域网" @@ -3491,28 +3511,16 @@ msgid "Logout success, return login page" msgstr "退出登录成功,返回到登录页面" #: authentication/views/slack.py:35 authentication/views/slack.py:126 -#, fuzzy -#| msgid "DingTalk Error" msgid "Slack Error" -msgstr "钉钉错误" +msgstr "Slack错误" #: authentication/views/slack.py:63 -#, fuzzy -#| msgid "DingTalk is already bound" msgid "Slack is already bound" -msgstr "钉钉已经绑定" - -#: authentication/views/slack.py:127 -#, fuzzy -#| msgid "DingTalk is not bound" -msgid "Slack is not bound" -msgstr "钉钉没有绑定" +msgstr "Slack已经绑定" #: authentication/views/slack.py:128 -#, fuzzy -#| msgid "Failed to get user from DingTalk" msgid "Failed to get user from Slack" -msgstr "从钉钉获取用户失败" +msgstr "从Slack获取用户失败" #: authentication/views/wecom.py:40 msgid "WeCom Error, Please contact your system administrator" @@ -3550,7 +3558,7 @@ msgstr "定时触发" msgid "Ready" msgstr "准备" -#: common/const/choices.py:16 terminal/const.py:76 tickets/const.py:29 +#: common/const/choices.py:16 terminal/const.py:77 tickets/const.py:29 #: tickets/const.py:39 msgid "Pending" msgstr "待定的" @@ -3627,7 +3635,7 @@ msgstr "无效的ID,应为列表" #: common/serializers/fields.py:132 tickets/serializers/ticket/common.py:58 #: xpack/plugins/cloud/serializers/account_attrs.py:56 #: xpack/plugins/cloud/serializers/account_attrs.py:79 -#: xpack/plugins/cloud/serializers/account_attrs.py:143 +#: xpack/plugins/cloud/serializers/account_attrs.py:150 msgid "This field is required." msgstr "该字段是必填项。" @@ -4640,6 +4648,7 @@ msgid "Users amount" msgstr "用户数量" #: rbac/serializers/role.py:28 terminal/models/applet/applet.py:34 +#: terminal/models/virtualapp/virtualapp.py:20 msgid "Display name" msgstr "显示名称" @@ -4700,7 +4709,7 @@ msgid "My assets" msgstr "我的资产" #: rbac/tree.py:58 terminal/models/applet/applet.py:52 -#: terminal/models/applet/applet.py:315 terminal/models/applet/host.py:30 +#: terminal/models/applet/applet.py:317 terminal/models/applet/host.py:30 #: terminal/serializers/applet.py:15 msgid "Applet" msgstr "远程应用" @@ -4709,7 +4718,7 @@ msgstr "远程应用" msgid "Ticket comment" msgstr "工单评论" -#: rbac/tree.py:130 settings/serializers/feature.py:58 +#: rbac/tree.py:130 settings/serializers/feature.py:98 #: tickets/models/ticket/general.py:307 msgid "Ticket" msgstr "工单管理" @@ -4789,31 +4798,31 @@ msgstr "可以更改 vault 设置" msgid "Can change system msg sub setting" msgstr "消息订阅设置" -#: settings/models.py:167 +#: settings/models.py:168 msgid "Can change sms setting" msgstr "短信设置" -#: settings/models.py:168 +#: settings/models.py:169 msgid "Can change security setting" msgstr "安全设置" -#: settings/models.py:169 +#: settings/models.py:170 msgid "Can change clean setting" msgstr "定期清理" -#: settings/models.py:170 +#: settings/models.py:171 msgid "Can change interface setting" msgstr "界面设置" -#: settings/models.py:171 +#: settings/models.py:172 msgid "Can change license setting" msgstr "许可证设置" -#: settings/models.py:172 +#: settings/models.py:173 msgid "Can change terminal setting" msgstr "终端设置" -#: settings/models.py:173 +#: settings/models.py:174 msgid "Can change other setting" msgstr "其它设置" @@ -5256,11 +5265,6 @@ msgstr "URL" msgid "Request method" msgstr "请求方式" -#: settings/serializers/auth/sms.py:117 -#, python-format -msgid "The value in the parameter must contain %s" -msgstr "参数中的值必须包含 %s" - #: settings/serializers/auth/sso.py:16 msgid "Enable SSO auth" msgstr "启用 SSO 令牌认证" @@ -5274,7 +5278,7 @@ msgid "SSO auth key TTL" msgstr "令牌有效期" #: settings/serializers/auth/sso.py:20 -#: xpack/plugins/cloud/serializers/account_attrs.py:193 +#: xpack/plugins/cloud/serializers/account_attrs.py:200 msgid "Unit: second" msgstr "单位: 秒" @@ -5369,27 +5373,27 @@ msgid "" msgstr "" "会话、录像,命令记录超过该时长将会被清除 (影响数据库存储,OSS 等不受影响)" -#: settings/serializers/feature.py:16 +#: settings/serializers/feature.py:18 msgid "Subject" msgstr "主题" -#: settings/serializers/feature.py:20 +#: settings/serializers/feature.py:22 msgid "More url" msgstr "更多信息 URL" -#: settings/serializers/feature.py:34 settings/serializers/feature.py:37 +#: settings/serializers/feature.py:36 settings/serializers/feature.py:39 msgid "Announcement" msgstr "公告" -#: settings/serializers/feature.py:36 +#: settings/serializers/feature.py:38 msgid "Enable announcement" msgstr "启用公告" -#: settings/serializers/feature.py:44 +#: settings/serializers/feature.py:46 settings/serializers/feature.py:64 msgid "Enable Vault" msgstr "启用 Vault" -#: settings/serializers/feature.py:53 +#: settings/serializers/feature.py:55 msgid "Mount Point" msgstr "挂在点" @@ -5397,39 +5401,39 @@ msgstr "挂在点" msgid "Enable tickets" msgstr "启用工单" -#: settings/serializers/feature.py:63 +#: settings/serializers/feature.py:103 msgid "Ticket authorize default time" msgstr "默认工单授权时间" -#: settings/serializers/feature.py:66 +#: settings/serializers/feature.py:106 msgid "day" msgstr "天" -#: settings/serializers/feature.py:66 +#: settings/serializers/feature.py:106 msgid "hour" msgstr "时" -#: settings/serializers/feature.py:67 +#: settings/serializers/feature.py:107 msgid "Ticket authorize default time unit" msgstr "默认工单授权时间单位" -#: settings/serializers/feature.py:72 +#: settings/serializers/feature.py:112 msgid "Feature" msgstr "功能" -#: settings/serializers/feature.py:75 +#: settings/serializers/feature.py:115 msgid "Operation center" msgstr "作业中心" -#: settings/serializers/feature.py:76 +#: settings/serializers/feature.py:116 msgid "Allow user run batch command or not using ansible" msgstr "是否允许用户使用 ansible 执行批量命令" -#: settings/serializers/feature.py:80 +#: settings/serializers/feature.py:120 msgid "Operation center command blacklist" msgstr "作业中心命令黑名单" -#: settings/serializers/feature.py:81 +#: settings/serializers/feature.py:121 msgid "Commands that are not allowed execute." msgstr "不允许执行的命令" @@ -5812,7 +5816,7 @@ msgstr "多个用户,使用 , 分割" msgid "[%s] %s" msgstr "[%s] %s" -#: settings/serializers/terminal.py:9 +#: settings/serializers/terminal.py:9 terminal/models/virtualapp/provider.py:11 msgid "Hostname" msgstr "主机名" @@ -6174,6 +6178,8 @@ msgid "Offline video player" msgstr "离线录像播放器" #: terminal/api/applet/applet.py:48 terminal/api/applet/applet.py:51 +#: terminal/api/virtualapp/virtualapp.py:43 +#: terminal/api/virtualapp/virtualapp.py:46 msgid "Invalid zip file" msgstr "无效的 zip 文件" @@ -6299,7 +6305,7 @@ msgstr "严重" msgid "High" msgstr "较高" -#: terminal/const.py:47 terminal/const.py:83 +#: terminal/const.py:47 terminal/const.py:84 #: users/templates/users/reset_password.html:50 msgid "Normal" msgstr "正常" @@ -6308,43 +6314,43 @@ msgstr "正常" msgid "Offline" msgstr "离线" -#: terminal/const.py:79 +#: terminal/const.py:80 msgid "Mismatch" msgstr "未匹配" -#: terminal/const.py:84 +#: terminal/const.py:85 msgid "Tunnel" msgstr "隧道" -#: terminal/const.py:90 +#: terminal/const.py:91 msgid "Read only" msgstr "只读" -#: terminal/const.py:91 +#: terminal/const.py:92 msgid "Writable" msgstr "读写" -#: terminal/const.py:95 +#: terminal/const.py:96 msgid "Kill session" msgstr "终断会话" -#: terminal/const.py:96 +#: terminal/const.py:97 msgid "Lock session" msgstr "锁定会话" -#: terminal/const.py:97 +#: terminal/const.py:98 msgid "Unlock session" msgstr "解锁会话" -#: terminal/const.py:102 +#: terminal/const.py:103 msgid "Replay create failed" msgstr "录像创建失败" -#: terminal/const.py:103 +#: terminal/const.py:104 msgid "Replay upload failed" msgstr "录像上传失败" -#: terminal/const.py:104 +#: terminal/const.py:105 msgid "Replay convert failed" msgstr "录像转码失败" @@ -6365,6 +6371,7 @@ msgid "Enterprise" msgstr "企业版" #: terminal/models/applet/applet.py:36 +#: terminal/models/virtualapp/virtualapp.py:22 msgid "Author" msgstr "作者" @@ -6377,6 +6384,7 @@ msgid "Can concurrent" msgstr "可以并发" #: terminal/models/applet/applet.py:44 +#: terminal/models/virtualapp/virtualapp.py:29 msgid "Tags" msgstr "标签" @@ -6385,6 +6393,7 @@ msgid "Hosts" msgstr "主机" #: terminal/models/applet/applet.py:93 +#: terminal/models/virtualapp/virtualapp.py:66 msgid "Applet pkg not valid, Missing file {}" msgstr "Applet pkg 无效,缺少文件 {}" @@ -6400,7 +6409,7 @@ msgstr "只支持自定义平台" msgid "Missing type in platform.yml" msgstr "在 platform.yml 中缺少类型" -#: terminal/models/applet/applet.py:317 terminal/models/applet/host.py:36 +#: terminal/models/applet/applet.py:319 terminal/models/applet/host.py:36 #: terminal/models/applet/host.py:138 msgid "Hosting" msgstr "宿主机" @@ -6535,7 +6544,7 @@ msgstr "远端地址" msgid "Application User" msgstr "应用用户" -#: terminal/models/component/terminal.py:166 +#: terminal/models/component/terminal.py:176 msgid "Can view terminal config" msgstr "可以查看终端配置" @@ -6694,7 +6703,7 @@ msgstr "测试失败: 账号无效" msgid "Invalid storage" msgstr "无效的存储" -#: terminal/serializers/applet.py:28 +#: terminal/serializers/applet.py:28 terminal/serializers/virtualapp.py:15 msgid "Icon" msgstr "图标" @@ -6756,6 +6765,7 @@ msgid "RDS Remote App Logoff Time Limit" msgstr "RDS 远程应用注销时间限制" #: terminal/serializers/applet_host.py:59 terminal/serializers/terminal.py:47 +#: terminal/serializers/virtualapp_provider.py:13 msgid "Load status" msgstr "负载状态" @@ -6924,7 +6934,7 @@ msgid "HOST" msgstr "主机" #: terminal/serializers/storage.py:146 users/models/user.py:828 -#: xpack/plugins/cloud/serializers/account_attrs.py:206 +#: xpack/plugins/cloud/serializers/account_attrs.py:213 msgid "Private key" msgstr "ssh私钥" @@ -7492,7 +7502,7 @@ msgid "Not a valid ssh public key" msgstr "SSH密钥不合法" #: users/forms/profile.py:173 users/models/user.py:831 -#: xpack/plugins/cloud/serializers/account_attrs.py:203 +#: xpack/plugins/cloud/serializers/account_attrs.py:210 msgid "Public key" msgstr "SSH公钥" @@ -8107,43 +8117,55 @@ msgstr "天翼私有云" msgid "OpenStack" msgstr "OpenStack" -#: xpack/plugins/cloud/const.py:28 +#: xpack/plugins/cloud/const.py:28 xpack/plugins/cloud/providers/zstack.py:21 +msgid "ZStack" +msgstr "ZStack" + +#: xpack/plugins/cloud/const.py:29 msgid "Fusion Compute" msgstr "融合计算" -#: xpack/plugins/cloud/const.py:33 +#: xpack/plugins/cloud/const.py:30 +msgid "SCP" +msgstr "深信服SCP" + +#: xpack/plugins/cloud/const.py:31 +msgid "Apsara Stack" +msgstr "阿里云专有云" + +#: xpack/plugins/cloud/const.py:36 msgid "Private IP" msgstr "私有IP" -#: xpack/plugins/cloud/const.py:34 +#: xpack/plugins/cloud/const.py:37 msgid "Public IP" msgstr "公网IP" -#: xpack/plugins/cloud/const.py:38 xpack/plugins/cloud/models.py:295 +#: xpack/plugins/cloud/const.py:41 xpack/plugins/cloud/models.py:295 msgid "Instance name" msgstr "实例名称" -#: xpack/plugins/cloud/const.py:39 +#: xpack/plugins/cloud/const.py:42 msgid "Instance name and Partial IP" msgstr "实例名称和部分IP" -#: xpack/plugins/cloud/const.py:44 +#: xpack/plugins/cloud/const.py:47 msgid "Succeed" msgstr "成功" -#: xpack/plugins/cloud/const.py:48 +#: xpack/plugins/cloud/const.py:51 msgid "Unsync" msgstr "未同步" -#: xpack/plugins/cloud/const.py:49 +#: xpack/plugins/cloud/const.py:52 msgid "New Sync" msgstr "新同步" -#: xpack/plugins/cloud/const.py:50 +#: xpack/plugins/cloud/const.py:53 msgid "Synced" msgstr "已同步" -#: xpack/plugins/cloud/const.py:51 +#: xpack/plugins/cloud/const.py:54 msgid "Released" msgstr "已释放" @@ -8489,11 +8511,11 @@ msgstr "TR-Istanbul" msgid "CN East-Suqian" msgstr "华东-宿迁" -#: xpack/plugins/cloud/serializers/account.py:64 +#: xpack/plugins/cloud/serializers/account.py:68 msgid "Validity display" msgstr "有效性显示" -#: xpack/plugins/cloud/serializers/account.py:65 +#: xpack/plugins/cloud/serializers/account.py:69 msgid "Provider display" msgstr "服务商显示" @@ -8510,50 +8532,50 @@ msgid "Subscription ID" msgstr "订阅 ID" #: xpack/plugins/cloud/serializers/account_attrs.py:98 -#: xpack/plugins/cloud/serializers/account_attrs.py:103 -#: xpack/plugins/cloud/serializers/account_attrs.py:119 -#: xpack/plugins/cloud/serializers/account_attrs.py:149 -#: xpack/plugins/cloud/serializers/account_attrs.py:199 +#: xpack/plugins/cloud/serializers/account_attrs.py:102 +#: xpack/plugins/cloud/serializers/account_attrs.py:126 +#: xpack/plugins/cloud/serializers/account_attrs.py:156 +#: xpack/plugins/cloud/serializers/account_attrs.py:206 msgid "API Endpoint" msgstr "API 端点" -#: xpack/plugins/cloud/serializers/account_attrs.py:109 +#: xpack/plugins/cloud/serializers/account_attrs.py:108 msgid "Auth url" msgstr "认证地址" -#: xpack/plugins/cloud/serializers/account_attrs.py:110 +#: xpack/plugins/cloud/serializers/account_attrs.py:109 msgid "eg: http://openstack.example.com:5000/v3" msgstr "如: http://openstack.example.com:5000/v3" -#: xpack/plugins/cloud/serializers/account_attrs.py:113 +#: xpack/plugins/cloud/serializers/account_attrs.py:112 msgid "User domain" msgstr "用户域" -#: xpack/plugins/cloud/serializers/account_attrs.py:120 +#: xpack/plugins/cloud/serializers/account_attrs.py:127 msgid "Cert File" msgstr "证书文件" -#: xpack/plugins/cloud/serializers/account_attrs.py:121 +#: xpack/plugins/cloud/serializers/account_attrs.py:128 msgid "Key File" msgstr "密钥文件" -#: xpack/plugins/cloud/serializers/account_attrs.py:137 +#: xpack/plugins/cloud/serializers/account_attrs.py:144 msgid "Service account key" msgstr "服务账号密钥" -#: xpack/plugins/cloud/serializers/account_attrs.py:138 +#: xpack/plugins/cloud/serializers/account_attrs.py:145 msgid "The file is in JSON format" msgstr "JSON 格式的文件" -#: xpack/plugins/cloud/serializers/account_attrs.py:156 +#: xpack/plugins/cloud/serializers/account_attrs.py:163 msgid "IP address invalid `{}`, {}" msgstr "IP 地址无效: `{}`, {}" -#: xpack/plugins/cloud/serializers/account_attrs.py:172 +#: xpack/plugins/cloud/serializers/account_attrs.py:179 msgid "Such as: 192.168.1.0/24, 10.0.0.0-10.0.0.255" msgstr "例: 192.168.1.0/24,10.0.0.0-10.0.0.255" -#: xpack/plugins/cloud/serializers/account_attrs.py:175 +#: xpack/plugins/cloud/serializers/account_attrs.py:182 msgid "" "The port is used to detect the validity of the IP address. When the " "synchronization task is executed, only the valid IP address will be " @@ -8562,23 +8584,23 @@ msgstr "" "端口用来检测 IP 地址的有效性,在同步任务执行时,只会同步有效的 IP 地址。
    " "如果端口为 0,则表示所有 IP 地址均有效。" -#: xpack/plugins/cloud/serializers/account_attrs.py:183 +#: xpack/plugins/cloud/serializers/account_attrs.py:190 msgid "Hostname prefix" msgstr "主机名前缀" -#: xpack/plugins/cloud/serializers/account_attrs.py:186 +#: xpack/plugins/cloud/serializers/account_attrs.py:193 msgid "IP segment" msgstr "IP 网段" -#: xpack/plugins/cloud/serializers/account_attrs.py:190 +#: xpack/plugins/cloud/serializers/account_attrs.py:197 msgid "Test port" msgstr "测试端口" -#: xpack/plugins/cloud/serializers/account_attrs.py:193 +#: xpack/plugins/cloud/serializers/account_attrs.py:200 msgid "Test timeout" msgstr "测试超时时间" -#: xpack/plugins/cloud/serializers/account_attrs.py:209 +#: xpack/plugins/cloud/serializers/account_attrs.py:216 msgid "Project" msgstr "project" @@ -8682,9 +8704,3 @@ msgstr "企业旗舰版" #~ msgid "Binding FeiShu successfully" #~ msgstr "绑定 飞书 成功" - -#~ msgid "Beian link" -#~ msgstr "公安联网备案跳转链接" - -#~ msgid "Beian text" -#~ msgstr "公安联网备案号" From cc7220a4ad0548a77e848b6bf6bd44963289134c Mon Sep 17 00:00:00 2001 From: feng <1304903146@qq.com> Date: Fri, 8 Dec 2023 16:06:17 +0800 Subject: [PATCH 038/111] =?UTF-8?q?perf:=20=E7=BF=BB=E8=AF=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/accounts/tasks/remove_account.py | 4 +- apps/audits/migrations/0024_usersession.py | 7 +- apps/audits/models.py | 2 +- apps/locale/ja/LC_MESSAGES/django.mo | 4 +- apps/locale/ja/LC_MESSAGES/django.po | 233 ++++++++++++------ apps/locale/zh/LC_MESSAGES/django.mo | 4 +- apps/locale/zh/LC_MESSAGES/django.po | 231 +++++++++++------ .../migrations/0014_auto_20231208_1548.py | 18 ++ apps/settings/serializers/feature.py | 2 +- apps/terminal/models/virtualapp/provider.py | 2 +- apps/terminal/models/virtualapp/virtualapp.py | 2 +- apps/terminal/serializers/virtualapp.py | 2 +- apps/users/migrations/0044_usersession.py | 7 +- 13 files changed, 359 insertions(+), 159 deletions(-) create mode 100644 apps/rbac/migrations/0014_auto_20231208_1548.py diff --git a/apps/accounts/tasks/remove_account.py b/apps/accounts/tasks/remove_account.py index 62a9375a1..269a7f349 100644 --- a/apps/accounts/tasks/remove_account.py +++ b/apps/accounts/tasks/remove_account.py @@ -11,7 +11,7 @@ __all__ = ['remove_accounts_task'] @shared_task( - queue="ansible", verbose_name=_('Remove accounts'), + queue="ansible", verbose_name=_('Remove account'), activity_callback=lambda self, gather_account_ids, *args, **kwargs: (gather_account_ids, None) ) def remove_accounts_task(gather_account_ids): @@ -20,7 +20,7 @@ def remove_accounts_task(gather_account_ids): gather_accounts = GatheredAccount.objects.filter( id__in=gather_account_ids ) - task_name = gettext_noop("Remove accounts") + task_name = gettext_noop("Remove account") task_snapshot = { 'assets': [str(i.asset_id) for i in gather_accounts], diff --git a/apps/audits/migrations/0024_usersession.py b/apps/audits/migrations/0024_usersession.py index 3cca28f75..c5a6c9331 100644 --- a/apps/audits/migrations/0024_usersession.py +++ b/apps/audits/migrations/0024_usersession.py @@ -1,9 +1,10 @@ # Generated by Django 4.1.10 on 2023-09-15 08:58 +import uuid + +import django.db.models.deletion from django.conf import settings from django.db import migrations, models -import django.db.models.deletion -import uuid class Migration(migrations.Migration): @@ -31,7 +32,7 @@ class Migration(migrations.Migration): options={ 'verbose_name': 'User session', 'ordering': ['-date_created'], - 'permissions': [('offline_usersession', 'Offline ussr session')], + 'permissions': [('offline_usersession', 'Offline user session')], }, ), ] diff --git a/apps/audits/models.py b/apps/audits/models.py index 97496102b..64a0ebc5b 100644 --- a/apps/audits/models.py +++ b/apps/audits/models.py @@ -305,5 +305,5 @@ class UserSession(models.Model): ordering = ['-date_created'] verbose_name = _('User session') permissions = [ - ('offline_usersession', _('Offline ussr session')), + ('offline_usersession', _('Offline user session')), ] diff --git a/apps/locale/ja/LC_MESSAGES/django.mo b/apps/locale/ja/LC_MESSAGES/django.mo index 56394b738..17fc22c3a 100644 --- a/apps/locale/ja/LC_MESSAGES/django.mo +++ b/apps/locale/ja/LC_MESSAGES/django.mo @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:790917753a2bc455aaa6a74322b18f7cbdb7ba860f4c08c46263e7762fb3fbe7 -size 165198 +oid sha256:5d443763c06877304dca8dac76131271287acc6594df665bdf9445455c5187f1 +size 167791 diff --git a/apps/locale/ja/LC_MESSAGES/django.po b/apps/locale/ja/LC_MESSAGES/django.po index 6e983d775..bf08b4575 100644 --- a/apps/locale/ja/LC_MESSAGES/django.po +++ b/apps/locale/ja/LC_MESSAGES/django.po @@ -9,6 +9,7 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2023-12-08 14:51+0800\n" +"POT-Creation-Date: 2023-12-08 15:33+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -115,83 +116,88 @@ msgstr "パスワードを変更する" msgid "Verify account" msgstr "アカウントを確認" -#: accounts/const/automation.py:27 +#: accounts/const/automation.py:27 accounts/tasks/remove_account.py:14 +#: accounts/tasks/remove_account.py:23 +msgid "Remove account" +msgstr "アカウントの削除" + +#: accounts/const/automation.py:28 msgid "Gather accounts" msgstr "アカウントのコレクション" -#: accounts/const/automation.py:28 +#: accounts/const/automation.py:29 msgid "Verify gateway account" msgstr "ゲートウェイ アカウントを確認する" -#: accounts/const/automation.py:46 +#: accounts/const/automation.py:47 msgid "Specific secret" msgstr "特定" -#: accounts/const/automation.py:47 +#: accounts/const/automation.py:48 msgid "Random generate" msgstr "ランダム生成" -#: accounts/const/automation.py:51 ops/const.py:13 +#: accounts/const/automation.py:52 ops/const.py:13 msgid "Append SSH KEY" msgstr "追加" -#: accounts/const/automation.py:52 ops/const.py:14 +#: accounts/const/automation.py:53 ops/const.py:14 msgid "Empty and append SSH KEY" msgstr "すべてクリアして追加" -#: accounts/const/automation.py:53 ops/const.py:15 +#: accounts/const/automation.py:54 ops/const.py:15 msgid "Replace (Replace only keys pushed by JumpServer) " msgstr "置換(JumpServer によってプッシュされたキーのみを置換)" -#: accounts/const/automation.py:58 +#: accounts/const/automation.py:59 msgid "On asset create" msgstr "アセットが作成されたとき" -#: accounts/const/automation.py:61 +#: accounts/const/automation.py:62 msgid "On perm add user" msgstr "承認が変更されたときにユーザーを追加する" -#: accounts/const/automation.py:63 +#: accounts/const/automation.py:64 msgid "On perm add user group" msgstr "権限変更時にユーザーグループを追加" -#: accounts/const/automation.py:65 +#: accounts/const/automation.py:66 msgid "On perm add asset" msgstr "変更の承認時にアセットを追加する" -#: accounts/const/automation.py:67 +#: accounts/const/automation.py:68 msgid "On perm add node" msgstr "承認変更時のノードの追加" -#: accounts/const/automation.py:69 +#: accounts/const/automation.py:70 msgid "On perm add account" msgstr "承認が変更されたときにアカウントを追加する" -#: accounts/const/automation.py:71 +#: accounts/const/automation.py:72 msgid "On asset join node" msgstr "アセットの変更時にノードに追加" -#: accounts/const/automation.py:73 +#: accounts/const/automation.py:74 msgid "On user join group" msgstr "ユーザー変更時にユーザーグループに追加" -#: accounts/const/automation.py:81 +#: accounts/const/automation.py:82 msgid "On perm change" msgstr "権限が変更されたとき" -#: accounts/const/automation.py:88 +#: accounts/const/automation.py:89 msgid "Inherit from group or node" msgstr "ユーザーグループまたはアセットノードから継承" -#: accounts/const/automation.py:96 +#: accounts/const/automation.py:97 msgid "Create and push" msgstr "作成してプッシュ" -#: accounts/const/automation.py:97 +#: accounts/const/automation.py:98 msgid "Only create" msgstr "作成のみ" -#: accounts/const/automation.py:102 +#: accounts/const/automation.py:103 #: authentication/serializers/password_mfa.py:16 #: authentication/serializers/password_mfa.py:24 #: notifications/backends/__init__.py:10 settings/serializers/msg.py:22 @@ -203,6 +209,7 @@ msgid "Email" msgstr "メール" #: accounts/const/automation.py:104 terminal/const.py:87 +#: accounts/const/automation.py:105 terminal/const.py:87 msgid "SFTP" msgstr "SFTP" @@ -314,6 +321,10 @@ msgstr "アカウントを確認できます" msgid "Can push account" msgstr "アカウントをプッシュできます" +#: accounts/models/account.py:72 +msgid "Can remove account" +msgstr "アカウントを削除できます" + #: accounts/models/automations/backup_account.py:27 msgid "Backup Type" msgstr "バックアップの種類" @@ -586,7 +597,7 @@ msgstr "パスワードルール" #: assets/models/asset/common.py:159 assets/models/cmd_filter.py:21 #: assets/models/domain.py:19 assets/models/group.py:17 #: assets/models/label.py:18 assets/models/platform.py:16 -#: assets/models/platform.py:89 assets/serializers/asset/common.py:146 +#: assets/models/platform.py:95 assets/serializers/asset/common.py:146 #: assets/serializers/platform.py:118 assets/serializers/platform.py:235 #: authentication/backends/passkey/models.py:10 #: authentication/serializers/connect_token_secret.py:113 @@ -730,7 +741,7 @@ msgid "Exist policy" msgstr "アカウントの存在ポリシー" #: accounts/serializers/account/account.py:193 applications/models.py:11 -#: assets/models/label.py:21 assets/models/platform.py:90 +#: assets/models/label.py:21 assets/models/platform.py:96 #: assets/serializers/asset/common.py:122 assets/serializers/cagegory.py:12 #: assets/serializers/platform.py:140 assets/serializers/platform.py:236 #: perms/serializers/user_permission.py:25 settings/models.py:34 @@ -742,7 +753,7 @@ msgstr "カテゴリ" #: accounts/serializers/automations/base.py:54 acls/models/command_acl.py:24 #: acls/serializers/command_acl.py:19 applications/models.py:14 #: assets/models/_user.py:50 assets/models/automations/base.py:20 -#: assets/models/cmd_filter.py:74 assets/models/platform.py:91 +#: assets/models/cmd_filter.py:74 assets/models/platform.py:97 #: assets/serializers/asset/common.py:123 assets/serializers/platform.py:120 #: assets/serializers/platform.py:139 audits/serializers.py:53 #: audits/serializers.py:170 @@ -892,10 +903,8 @@ msgid "Special symbol" msgstr "特殊記号" #: accounts/serializers/account/template.py:18 -#, fuzzy -#| msgid "Special symbol" msgid "Exclude symbol" -msgstr "特殊記号" +msgstr "除外文字" #: accounts/serializers/account/template.py:36 msgid "Secret generation strategy for account creation" @@ -1710,7 +1719,7 @@ msgstr "ポート" msgid "Address" msgstr "アドレス" -#: assets/models/asset/common.py:161 assets/models/platform.py:120 +#: assets/models/asset/common.py:161 assets/models/platform.py:126 #: authentication/backends/passkey/models.py:12 #: authentication/serializers/connect_token_secret.py:118 #: perms/serializers/user_permission.py:24 xpack/plugins/cloud/models.py:321 @@ -1997,31 +2006,43 @@ msgstr "アカウント認証方法" msgid "Verify account params" msgstr "アカウント認証パラメータ" -#: assets/models/platform.py:92 tickets/models/ticket/general.py:300 +#: assets/models/platform.py:76 +msgid "Remove account enabled" +msgstr "アカウントを開いて削除" + +#: assets/models/platform.py:78 +msgid "Remove account method" +msgstr "アカウントの削除方法" + +#: assets/models/platform.py:80 +msgid "Remove account params" +msgstr "アカウント削除パラメータ" + +#: assets/models/platform.py:98 tickets/models/ticket/general.py:300 msgid "Meta" msgstr "メタ" -#: assets/models/platform.py:93 labels/models.py:13 +#: assets/models/platform.py:99 labels/models.py:13 msgid "Internal" msgstr "ビルトイン" -#: assets/models/platform.py:97 assets/serializers/platform.py:138 +#: assets/models/platform.py:103 assets/serializers/platform.py:138 msgid "Charset" msgstr "シャーセット" -#: assets/models/platform.py:99 assets/serializers/platform.py:167 +#: assets/models/platform.py:105 assets/serializers/platform.py:167 msgid "Domain enabled" msgstr "ドメインを有効にする" -#: assets/models/platform.py:101 assets/serializers/platform.py:166 +#: assets/models/platform.py:107 assets/serializers/platform.py:166 msgid "Su enabled" msgstr "アカウントの切り替えを有効にする" -#: assets/models/platform.py:102 assets/serializers/platform.py:144 +#: assets/models/platform.py:108 assets/serializers/platform.py:144 msgid "Su method" msgstr "アカウントの切り替え方法" -#: assets/models/platform.py:103 assets/serializers/platform.py:147 +#: assets/models/platform.py:109 assets/serializers/platform.py:147 msgid "Custom fields" msgstr "カスタムフィールド" @@ -2521,8 +2542,8 @@ msgid "User session" msgstr "ユーザーセッション" #: audits/models.py:308 -msgid "Offline ussr session" -msgstr "ユーザー・セッションの下限" +msgid "Offline user session" +msgstr "オフラインユーザセッション" #: audits/serializers.py:33 ops/models/adhoc.py:25 ops/models/base.py:16 #: ops/models/base.py:53 ops/models/job.py:150 ops/models/job.py:238 @@ -3160,6 +3181,21 @@ msgstr "コンポーネント" msgid "Expired now" msgstr "すぐに期限切れ" +#: authentication/serializers/connect_token_secret.py:169 +#: terminal/models/virtualapp/virtualapp.py:25 +msgid "Image name" +msgstr "ミラー名" + +#: authentication/serializers/connect_token_secret.py:170 +#: terminal/models/virtualapp/virtualapp.py:27 +msgid "Image port" +msgstr "ミラーポート" + +#: authentication/serializers/connect_token_secret.py:171 +#: terminal/models/virtualapp/virtualapp.py:26 +msgid "Image protocol" +msgstr "ミラープロトコル" + #: authentication/serializers/connection_token.py:16 msgid "Expired time" msgstr "期限切れ時間" @@ -3446,22 +3482,19 @@ msgid "If you have any question, please contact the administrator" msgstr "質問があったら、管理者に連絡して下さい" #: authentication/views/base.py:138 -#, fuzzy, python-format -#| msgid "WeCom query user failed" +#, python-format msgid "%s query user failed" -msgstr "企業微信ユーザーの問合せに失敗しました" +msgstr "%sユーザーのクエリに失敗しました" #: authentication/views/base.py:147 -#, fuzzy, python-format -#| msgid "The WeCom is already bound to another user" +#, python-format msgid "The %s is already bound to another user" -msgstr "この企業の微信はすでに他のユーザーをバインドしている。" +msgstr "%sが別のユーザーにバインドされています。" #: authentication/views/base.py:154 -#, fuzzy, python-format -#| msgid "Binding WeCom successfully" +#, python-format msgid "Binding %s successfully" -msgstr "企業の微信のバインドに成功" +msgstr "バインド%s成功" #: authentication/views/dingtalk.py:42 msgid "DingTalk Error, Please contact your system administrator" @@ -3554,22 +3587,16 @@ msgid "Logout success, return login page" msgstr "ログアウト成功、ログインページを返す" #: authentication/views/slack.py:35 authentication/views/slack.py:126 -#, fuzzy -#| msgid "DingTalk Error" msgid "Slack Error" -msgstr "DingTalkエラー" +msgstr "Slack エラー" #: authentication/views/slack.py:63 -#, fuzzy -#| msgid "DingTalk is already bound" msgid "Slack is already bound" -msgstr "DingTalkはすでにバインドされています" +msgstr "Slack はすでにバインドされています" #: authentication/views/slack.py:128 -#, fuzzy -#| msgid "Failed to get user from DingTalk" msgid "Failed to get user from Slack" -msgstr "DingTalkからユーザーを取得できませんでした" +msgstr "Slack からユーザーを取得できませんでした" #: authentication/views/wecom.py:40 msgid "WeCom Error, Please contact your system administrator" @@ -3782,10 +3809,8 @@ msgid "Network error, please contact system administrator" msgstr "ネットワークエラー、システム管理者に連絡してください" #: common/sdk/im/slack/__init__.py:76 -#, fuzzy -#| msgid "Unknown error: {}" msgid "Unknown error occur" -msgstr "不明なエラー: {}" +msgstr "不明なエラーが発生しました" #: common/sdk/im/wecom/__init__.py:16 msgid "WeCom error, please contact system administrator" @@ -4787,9 +4812,13 @@ msgstr "共通設定" msgid "View permission tree" msgstr "権限ツリーの表示" -#: settings/api/dingtalk.py:31 settings/api/feishu.py:36 -#: settings/api/slack.py:34 settings/api/sms.py:160 settings/api/vault.py:40 -#: settings/api/wecom.py:37 +#: settings/api/chat.py:35 +msgid "Chat AI is not enabled" +msgstr "チャットAIがオンになっていない" + +#: settings/api/chat.py:73 settings/api/dingtalk.py:31 +#: settings/api/feishu.py:36 settings/api/slack.py:34 settings/api/sms.py:160 +#: settings/api/vault.py:40 settings/api/wecom.py:37 msgid "Test success" msgstr "テストの成功" @@ -4853,6 +4882,10 @@ msgid "Can change vault setting" msgstr "金庫の設定を変えることができます" #: settings/models.py:166 +msgid "Can change chat ai setting" +msgstr "チャットAI設定を変更できます" + +#: settings/models.py:167 msgid "Can change system msg sub setting" msgstr "システムmsgサブ设定を変更できます" @@ -4917,10 +4950,8 @@ msgid "FeiShu Auth" msgstr "飛本 認証" #: settings/serializers/auth/base.py:20 -#, fuzzy -#| msgid "CAS Auth" msgid "Slack Auth" -msgstr "CAS 認証" +msgstr "Slack 認証" #: settings/serializers/auth/base.py:21 msgid "WeCom Auth" @@ -5241,10 +5272,8 @@ msgid "SP cert" msgstr "SP 証明書" #: settings/serializers/auth/slack.py:12 -#, fuzzy -#| msgid "Enable CAS Auth" msgid "Enable Slack Auth" -msgstr "CAS 認証の有効化" +msgstr "Slack 認証の有効化" #: settings/serializers/auth/sms.py:17 msgid "Enable SMS" @@ -5460,6 +5489,22 @@ msgid "Mount Point" msgstr "マウントポイント" #: settings/serializers/feature.py:60 +msgid "Chat AI" +msgstr "チャットAI" + +#: settings/serializers/feature.py:67 +msgid "Base Url" +msgstr "基本的なUrl" + +#: settings/serializers/feature.py:70 templates/_header_bar.html:90 +msgid "API Key" +msgstr "API Key" + +#: settings/serializers/feature.py:76 +msgid "GPT Model" +msgstr "GPTモデル" + +#: settings/serializers/feature.py:100 msgid "Enable tickets" msgstr "チケットを有効にする" @@ -6125,10 +6170,6 @@ msgstr "ページの管理" msgid "User page" msgstr "ユーザーページ" -#: templates/_header_bar.html:90 -msgid "API Key" -msgstr "API Key" - #: templates/_header_bar.html:91 msgid "Logout" msgstr "ログアウト" @@ -6760,6 +6801,26 @@ msgstr "検証コードが無効" msgid "You have already joined this session" msgstr "すでにこのセッションに参加しています" +#: terminal/models/virtualapp/provider.py:17 +#: terminal/models/virtualapp/virtualapp.py:36 +#: terminal/models/virtualapp/virtualapp.py:97 +#: terminal/serializers/virtualapp.py:32 +msgid "Virtual app" +msgstr "仮想アプリケーション" + +#: terminal/models/virtualapp/virtualapp.py:32 +msgid "Providers" +msgstr "プロバイダ" + +#: terminal/models/virtualapp/virtualapp.py:94 +#: terminal/serializers/virtualapp.py:34 +msgid "App Provider" +msgstr "アプリケーションプロバイダ" + +#: terminal/models/virtualapp/virtualapp.py:102 +msgid "Virtual app publication" +msgstr "仮想アプリケーションの公開" + #: terminal/notifications.py:25 msgid "Sessions" msgstr "セッション" @@ -7079,6 +7140,26 @@ msgstr "セッション" msgid "Not found" msgstr "見つかりません" +#: terminal/serializers/virtualapp_provider.py:26 +msgid "Container ID" +msgstr "コンテナID" + +#: terminal/serializers/virtualapp_provider.py:27 +msgid "Container Image" +msgstr "コンテナミラーリング" + +#: terminal/serializers/virtualapp_provider.py:28 +msgid "Container Name" +msgstr "コンテナー名" + +#: terminal/serializers/virtualapp_provider.py:29 +msgid "Container Status" +msgstr "コンテナステータス" + +#: terminal/serializers/virtualapp_provider.py:30 +msgid "Container Ports" +msgstr "コンテナポート" + #: terminal/tasks.py:33 msgid "Periodic delete terminal status" msgstr "端末の状態を定期的にクリーンアップする" @@ -7893,7 +7974,7 @@ msgstr "ユーザーの有効期限の定期的な検出" #: users/tasks.py:84 msgid "Check unused users" -msgstr "未使用のユーザーを確認する" +msgstr "未使用のユーザーのチェック" #: users/tasks.py:115 msgid "The user has not logged in recently and has been disabled." @@ -8791,7 +8872,15 @@ msgstr "ログアウトページのロゴ" msgid "Theme" msgstr "テーマ" -#: xpack/plugins/interface/models.py:44 xpack/plugins/interface/models.py:85 +#: xpack/plugins/interface/models.py:42 +msgid "Beian link" +msgstr "公安オンライン申告ジャンプリンク" + +#: xpack/plugins/interface/models.py:43 +msgid "Beian text" +msgstr "公安網登録番号" + +#: xpack/plugins/interface/models.py:46 xpack/plugins/interface/models.py:87 msgid "Interface setting" msgstr "インターフェイスの設定" diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index 7d33a1c92..4de90d618 100644 --- a/apps/locale/zh/LC_MESSAGES/django.mo +++ b/apps/locale/zh/LC_MESSAGES/django.mo @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a46c7aff5f314cbab9849e4c11671f81b849f98fad73887ced5b2012635b8a97 -size 135290 +oid sha256:8976c6b41e2c0ce591b2b257fd0352b4ed7517661f7df83ba74b302b8cf94b00 +size 137479 diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index 46f3f36ec..1302ce4f7 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: JumpServer 0.3.3\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-12-08 14:51+0800\n" +"POT-Creation-Date: 2023-12-08 15:33+0800\n" "PO-Revision-Date: 2021-05-20 10:54+0800\n" "Last-Translator: ibuler \n" "Language-Team: JumpServer team\n" @@ -114,83 +114,88 @@ msgstr "更改密码" msgid "Verify account" msgstr "验证账号" -#: accounts/const/automation.py:27 +#: accounts/const/automation.py:27 accounts/tasks/remove_account.py:14 +#: accounts/tasks/remove_account.py:23 +msgid "Remove account" +msgstr "移除账号" + +#: accounts/const/automation.py:28 msgid "Gather accounts" msgstr "收集账号" -#: accounts/const/automation.py:28 +#: accounts/const/automation.py:29 msgid "Verify gateway account" msgstr "验证网关账号" -#: accounts/const/automation.py:46 +#: accounts/const/automation.py:47 msgid "Specific secret" msgstr "指定" -#: accounts/const/automation.py:47 +#: accounts/const/automation.py:48 msgid "Random generate" msgstr "随机生成" -#: accounts/const/automation.py:51 ops/const.py:13 +#: accounts/const/automation.py:52 ops/const.py:13 msgid "Append SSH KEY" msgstr "追加" -#: accounts/const/automation.py:52 ops/const.py:14 +#: accounts/const/automation.py:53 ops/const.py:14 msgid "Empty and append SSH KEY" msgstr "清空所有并添加" -#: accounts/const/automation.py:53 ops/const.py:15 +#: accounts/const/automation.py:54 ops/const.py:15 msgid "Replace (Replace only keys pushed by JumpServer) " msgstr "替换 (只替换由 JumpServer 推送的密钥)" -#: accounts/const/automation.py:58 +#: accounts/const/automation.py:59 msgid "On asset create" msgstr "资产创建时" -#: accounts/const/automation.py:61 +#: accounts/const/automation.py:62 msgid "On perm add user" msgstr "授权变更时添加用户" -#: accounts/const/automation.py:63 +#: accounts/const/automation.py:64 msgid "On perm add user group" msgstr "授权变更时添加用户组" -#: accounts/const/automation.py:65 +#: accounts/const/automation.py:66 msgid "On perm add asset" msgstr "授权变更时添加资产" -#: accounts/const/automation.py:67 +#: accounts/const/automation.py:68 msgid "On perm add node" msgstr "授权变更时添加节点" -#: accounts/const/automation.py:69 +#: accounts/const/automation.py:70 msgid "On perm add account" msgstr "授权变更时添加账号" -#: accounts/const/automation.py:71 +#: accounts/const/automation.py:72 msgid "On asset join node" msgstr "资产变更时添加到节点" -#: accounts/const/automation.py:73 +#: accounts/const/automation.py:74 msgid "On user join group" msgstr "用户变更时添加到用户组" -#: accounts/const/automation.py:81 +#: accounts/const/automation.py:82 msgid "On perm change" msgstr "授权变更时" -#: accounts/const/automation.py:88 +#: accounts/const/automation.py:89 msgid "Inherit from group or node" msgstr "继承自用户组或资产节点" -#: accounts/const/automation.py:96 +#: accounts/const/automation.py:97 msgid "Create and push" msgstr "创建并推送" -#: accounts/const/automation.py:97 +#: accounts/const/automation.py:98 msgid "Only create" msgstr "仅创建" -#: accounts/const/automation.py:102 +#: accounts/const/automation.py:103 #: authentication/serializers/password_mfa.py:16 #: authentication/serializers/password_mfa.py:24 #: notifications/backends/__init__.py:10 settings/serializers/msg.py:22 @@ -201,7 +206,7 @@ msgstr "仅创建" msgid "Email" msgstr "邮箱" -#: accounts/const/automation.py:104 terminal/const.py:87 +#: accounts/const/automation.py:105 terminal/const.py:87 msgid "SFTP" msgstr "SFTP" @@ -313,6 +318,10 @@ msgstr "可以验证账号" msgid "Can push account" msgstr "可以推送账号" +#: accounts/models/account.py:72 +msgid "Can remove account" +msgstr "可以移除账号" + #: accounts/models/automations/backup_account.py:27 msgid "Backup Type" msgstr "备份类型" @@ -585,7 +594,7 @@ msgstr "密码规则" #: assets/models/asset/common.py:159 assets/models/cmd_filter.py:21 #: assets/models/domain.py:19 assets/models/group.py:17 #: assets/models/label.py:18 assets/models/platform.py:16 -#: assets/models/platform.py:89 assets/serializers/asset/common.py:146 +#: assets/models/platform.py:95 assets/serializers/asset/common.py:146 #: assets/serializers/platform.py:118 assets/serializers/platform.py:235 #: authentication/backends/passkey/models.py:10 #: authentication/serializers/connect_token_secret.py:113 @@ -728,7 +737,7 @@ msgid "Exist policy" msgstr "账号存在策略" #: accounts/serializers/account/account.py:193 applications/models.py:11 -#: assets/models/label.py:21 assets/models/platform.py:90 +#: assets/models/label.py:21 assets/models/platform.py:96 #: assets/serializers/asset/common.py:122 assets/serializers/cagegory.py:12 #: assets/serializers/platform.py:140 assets/serializers/platform.py:236 #: perms/serializers/user_permission.py:25 settings/models.py:34 @@ -740,7 +749,7 @@ msgstr "类别" #: accounts/serializers/automations/base.py:54 acls/models/command_acl.py:24 #: acls/serializers/command_acl.py:19 applications/models.py:14 #: assets/models/_user.py:50 assets/models/automations/base.py:20 -#: assets/models/cmd_filter.py:74 assets/models/platform.py:91 +#: assets/models/cmd_filter.py:74 assets/models/platform.py:97 #: assets/serializers/asset/common.py:123 assets/serializers/platform.py:120 #: assets/serializers/platform.py:139 audits/serializers.py:53 #: audits/serializers.py:170 @@ -890,10 +899,8 @@ msgid "Special symbol" msgstr "特殊字符" #: accounts/serializers/account/template.py:18 -#, fuzzy -#| msgid "Special symbol" msgid "Exclude symbol" -msgstr "特殊字符" +msgstr "排除字符" #: accounts/serializers/account/template.py:36 msgid "Secret generation strategy for account creation" @@ -1703,7 +1710,7 @@ msgstr "端口" msgid "Address" msgstr "地址" -#: assets/models/asset/common.py:161 assets/models/platform.py:120 +#: assets/models/asset/common.py:161 assets/models/platform.py:126 #: authentication/backends/passkey/models.py:12 #: authentication/serializers/connect_token_secret.py:118 #: perms/serializers/user_permission.py:24 xpack/plugins/cloud/models.py:321 @@ -1990,31 +1997,43 @@ msgstr "账号验证方式" msgid "Verify account params" msgstr "账号验证参数" -#: assets/models/platform.py:92 tickets/models/ticket/general.py:300 +#: assets/models/platform.py:76 +msgid "Remove account enabled" +msgstr "开启账号移除" + +#: assets/models/platform.py:78 +msgid "Remove account method" +msgstr "账号移除方式" + +#: assets/models/platform.py:80 +msgid "Remove account params" +msgstr "账号移除参数" + +#: assets/models/platform.py:98 tickets/models/ticket/general.py:300 msgid "Meta" msgstr "元数据" -#: assets/models/platform.py:93 labels/models.py:13 +#: assets/models/platform.py:99 labels/models.py:13 msgid "Internal" msgstr "内置" -#: assets/models/platform.py:97 assets/serializers/platform.py:138 +#: assets/models/platform.py:103 assets/serializers/platform.py:138 msgid "Charset" msgstr "编码" -#: assets/models/platform.py:99 assets/serializers/platform.py:167 +#: assets/models/platform.py:105 assets/serializers/platform.py:167 msgid "Domain enabled" msgstr "启用网域" -#: assets/models/platform.py:101 assets/serializers/platform.py:166 +#: assets/models/platform.py:107 assets/serializers/platform.py:166 msgid "Su enabled" msgstr "启用账号切换" -#: assets/models/platform.py:102 assets/serializers/platform.py:144 +#: assets/models/platform.py:108 assets/serializers/platform.py:144 msgid "Su method" msgstr "账号切换方式" -#: assets/models/platform.py:103 assets/serializers/platform.py:147 +#: assets/models/platform.py:109 assets/serializers/platform.py:147 msgid "Custom fields" msgstr "自定义属性" @@ -2505,8 +2524,8 @@ msgid "User session" msgstr "用户会话" #: audits/models.py:308 -msgid "Offline ussr session" -msgstr "下限用户会话" +msgid "Offline user session" +msgstr "下线用户会话" #: audits/serializers.py:33 ops/models/adhoc.py:25 ops/models/base.py:16 #: ops/models/base.py:53 ops/models/job.py:150 ops/models/job.py:238 @@ -3129,6 +3148,21 @@ msgstr "组件" msgid "Expired now" msgstr "立刻过期" +#: authentication/serializers/connect_token_secret.py:169 +#: terminal/models/virtualapp/virtualapp.py:25 +msgid "Image name" +msgstr "镜像名称" + +#: authentication/serializers/connect_token_secret.py:170 +#: terminal/models/virtualapp/virtualapp.py:27 +msgid "Image port" +msgstr "镜像端口" + +#: authentication/serializers/connect_token_secret.py:171 +#: terminal/models/virtualapp/virtualapp.py:26 +msgid "Image protocol" +msgstr "镜像协议" + #: authentication/serializers/connection_token.py:16 msgid "Expired time" msgstr "过期时间" @@ -3403,22 +3437,19 @@ msgid "If you have any question, please contact the administrator" msgstr "如果有疑问或需求,请联系系统管理员" #: authentication/views/base.py:138 -#, fuzzy, python-format -#| msgid "WeCom query user failed" +#, python-format msgid "%s query user failed" -msgstr "企业微信查询用户失败" +msgstr "%s 查询用户失败" #: authentication/views/base.py:147 -#, fuzzy, python-format -#| msgid "The WeCom is already bound to another user" +#, python-format msgid "The %s is already bound to another user" -msgstr "该企业微信已经绑定其他用户" +msgstr "%s 已绑定到另一个用户" #: authentication/views/base.py:154 -#, fuzzy, python-format -#| msgid "Binding WeCom successfully" +#, python-format msgid "Binding %s successfully" -msgstr "绑定 企业微信 成功" +msgstr "绑定 %s 成功" #: authentication/views/dingtalk.py:42 msgid "DingTalk Error, Please contact your system administrator" @@ -3512,15 +3543,15 @@ msgstr "退出登录成功,返回到登录页面" #: authentication/views/slack.py:35 authentication/views/slack.py:126 msgid "Slack Error" -msgstr "Slack错误" +msgstr "Slack 错误" #: authentication/views/slack.py:63 msgid "Slack is already bound" -msgstr "Slack已经绑定" +msgstr "Slack 已经绑定" #: authentication/views/slack.py:128 msgid "Failed to get user from Slack" -msgstr "从Slack获取用户失败" +msgstr "从 Slack 获取用户失败" #: authentication/views/wecom.py:40 msgid "WeCom Error, Please contact your system administrator" @@ -3731,10 +3762,8 @@ msgid "Network error, please contact system administrator" msgstr "网络错误,请联系系统管理员" #: common/sdk/im/slack/__init__.py:76 -#, fuzzy -#| msgid "Unknown error: {}" msgid "Unknown error occur" -msgstr "未知错误: {}" +msgstr "发生未知错误" #: common/sdk/im/wecom/__init__.py:16 msgid "WeCom error, please contact system administrator" @@ -4731,9 +4760,13 @@ msgstr "一般设置" msgid "View permission tree" msgstr "查看授权树" -#: settings/api/dingtalk.py:31 settings/api/feishu.py:36 -#: settings/api/slack.py:34 settings/api/sms.py:160 settings/api/vault.py:40 -#: settings/api/wecom.py:37 +#: settings/api/chat.py:35 +msgid "Chat AI is not enabled" +msgstr "聊天 AI 没有开启" + +#: settings/api/chat.py:73 settings/api/dingtalk.py:31 +#: settings/api/feishu.py:36 settings/api/slack.py:34 settings/api/sms.py:160 +#: settings/api/vault.py:40 settings/api/wecom.py:37 msgid "Test success" msgstr "测试成功" @@ -4795,6 +4828,10 @@ msgid "Can change vault setting" msgstr "可以更改 vault 设置" #: settings/models.py:166 +msgid "Can change chat ai setting" +msgstr "可以修改聊天 AI 设置" + +#: settings/models.py:167 msgid "Can change system msg sub setting" msgstr "消息订阅设置" @@ -4859,10 +4896,8 @@ msgid "FeiShu Auth" msgstr "飞书 认证" #: settings/serializers/auth/base.py:20 -#, fuzzy -#| msgid "CAS Auth" msgid "Slack Auth" -msgstr "CAS 认证" +msgstr "Slack 认证" #: settings/serializers/auth/base.py:21 msgid "WeCom Auth" @@ -5182,10 +5217,8 @@ msgid "SP cert" msgstr "SP 证书" #: settings/serializers/auth/slack.py:12 -#, fuzzy -#| msgid "Enable CAS Auth" msgid "Enable Slack Auth" -msgstr "启用 CAS 认证" +msgstr "启用 Slack 认证" #: settings/serializers/auth/sms.py:17 msgid "Enable SMS" @@ -5398,6 +5431,22 @@ msgid "Mount Point" msgstr "挂在点" #: settings/serializers/feature.py:60 +msgid "Chat AI" +msgstr "聊天 AI" + +#: settings/serializers/feature.py:67 +msgid "Base Url" +msgstr "基本地址" + +#: settings/serializers/feature.py:70 templates/_header_bar.html:90 +msgid "API Key" +msgstr "API Key" + +#: settings/serializers/feature.py:76 +msgid "GPT Model" +msgstr "GPT 模型" + +#: settings/serializers/feature.py:100 msgid "Enable tickets" msgstr "启用工单" @@ -5960,8 +6009,6 @@ msgid "Succeed: Match {} s user" msgstr "成功匹配 {} 个用户" #: settings/utils/ldap.py:653 -#, fuzzy -#| msgid "Can test asset connectivity" msgid "Please test the connection first" msgstr "请先测试连接" @@ -6037,10 +6084,6 @@ msgstr "管理页面" msgid "User page" msgstr "用户页面" -#: templates/_header_bar.html:90 -msgid "API Key" -msgstr "API Key" - #: templates/_header_bar.html:91 msgid "Logout" msgstr "注销登录" @@ -6666,6 +6709,26 @@ msgstr "验证码不正确" msgid "You have already joined this session" msgstr "您已经加入过此会话" +#: terminal/models/virtualapp/provider.py:17 +#: terminal/models/virtualapp/virtualapp.py:36 +#: terminal/models/virtualapp/virtualapp.py:97 +#: terminal/serializers/virtualapp.py:32 +msgid "Virtual app" +msgstr "虚拟应用" + +#: terminal/models/virtualapp/virtualapp.py:32 +msgid "Providers" +msgstr "提供商" + +#: terminal/models/virtualapp/virtualapp.py:94 +#: terminal/serializers/virtualapp.py:34 +msgid "App Provider" +msgstr "应用提供商" + +#: terminal/models/virtualapp/virtualapp.py:102 +msgid "Virtual app publication" +msgstr "虚拟应用发布" + #: terminal/notifications.py:25 msgid "Sessions" msgstr "会话管理" @@ -6978,6 +7041,26 @@ msgstr "会话 ID" msgid "Not found" msgstr "没有发现" +#: terminal/serializers/virtualapp_provider.py:26 +msgid "Container ID" +msgstr "容器 ID" + +#: terminal/serializers/virtualapp_provider.py:27 +msgid "Container Image" +msgstr "容器镜像" + +#: terminal/serializers/virtualapp_provider.py:28 +msgid "Container Name" +msgstr "容器名称" + +#: terminal/serializers/virtualapp_provider.py:29 +msgid "Container Status" +msgstr "容器状态" + +#: terminal/serializers/virtualapp_provider.py:30 +msgid "Container Ports" +msgstr "容器端口" + #: terminal/tasks.py:33 msgid "Periodic delete terminal status" msgstr "周期清理终端状态" @@ -7781,7 +7864,7 @@ msgstr "周期检测用户过期" #: users/tasks.py:84 msgid "Check unused users" -msgstr "校验用户已过期" +msgstr "检查未使用的用户" #: users/tasks.py:115 msgid "The user has not logged in recently and has been disabled." @@ -8664,7 +8747,15 @@ msgstr "退出页面logo" msgid "Theme" msgstr "主题" -#: xpack/plugins/interface/models.py:44 xpack/plugins/interface/models.py:85 +#: xpack/plugins/interface/models.py:42 +msgid "Beian link" +msgstr "公安联网备案跳转链接" + +#: xpack/plugins/interface/models.py:43 +msgid "Beian text" +msgstr "公安联网备案号" + +#: xpack/plugins/interface/models.py:46 xpack/plugins/interface/models.py:87 msgid "Interface setting" msgstr "界面设置" diff --git a/apps/rbac/migrations/0014_auto_20231208_1548.py b/apps/rbac/migrations/0014_auto_20231208_1548.py new file mode 100644 index 000000000..33581c5a0 --- /dev/null +++ b/apps/rbac/migrations/0014_auto_20231208_1548.py @@ -0,0 +1,18 @@ +# Generated by Django 4.1.10 on 2023-12-08 07:48 + +from django.db import migrations + + +def migrate_remove_offline_ussrsession_permission(apps, *args): + perm_model = apps.get_model('auth', 'Permission') + perm_model.objects.filter(codename='offline_ussrsession').delete() + + +class Migration(migrations.Migration): + dependencies = [ + ('rbac', '0013_alter_menupermission_options'), + ] + + operations = [ + migrations.RunPython(migrate_remove_offline_ussrsession_permission) + ] diff --git a/apps/settings/serializers/feature.py b/apps/settings/serializers/feature.py index 3b7f80fef..c301585ff 100644 --- a/apps/settings/serializers/feature.py +++ b/apps/settings/serializers/feature.py @@ -67,7 +67,7 @@ class ChatAISettingSerializer(serializers.Serializer): max_length=256, allow_blank=True, required=False, label=_('Base Url') ) GPT_API_KEY = EncryptedField( - max_length=256, allow_blank=True, required=False, label=_('Api Key'), + max_length=256, allow_blank=True, required=False, label=_('API Key'), ) GPT_PROXY = serializers.CharField( max_length=256, allow_blank=True, required=False, label=_('Proxy') diff --git a/apps/terminal/models/virtualapp/provider.py b/apps/terminal/models/virtualapp/provider.py index 90a8df2ef..8642ff065 100644 --- a/apps/terminal/models/virtualapp/provider.py +++ b/apps/terminal/models/virtualapp/provider.py @@ -14,7 +14,7 @@ class AppProvider(JMSBaseModel): related_name='app_provider', verbose_name=_('Terminal') ) apps = models.ManyToManyField( - 'VirtualApp', verbose_name=_('VirtualApp'), + 'VirtualApp', verbose_name=_('Virtual app'), through='VirtualAppPublication', through_fields=('provider', 'app'), ) diff --git a/apps/terminal/models/virtualapp/virtualapp.py b/apps/terminal/models/virtualapp/virtualapp.py index 6035c76c4..877dea387 100644 --- a/apps/terminal/models/virtualapp/virtualapp.py +++ b/apps/terminal/models/virtualapp/virtualapp.py @@ -94,7 +94,7 @@ class VirtualAppPublication(JMSBaseModel): 'AppProvider', on_delete=models.CASCADE, related_name='publications', verbose_name=_('App Provider') ) app = models.ForeignKey( - 'VirtualApp', on_delete=models.CASCADE, related_name='publications', verbose_name=_('Virtual App') + 'VirtualApp', on_delete=models.CASCADE, related_name='publications', verbose_name=_('Virtual app') ) status = models.CharField(max_length=16, default='pending', verbose_name=_('Status')) diff --git a/apps/terminal/serializers/virtualapp.py b/apps/terminal/serializers/virtualapp.py index f55226eaf..5f959e490 100644 --- a/apps/terminal/serializers/virtualapp.py +++ b/apps/terminal/serializers/virtualapp.py @@ -29,7 +29,7 @@ class VirtualAppSerializer(serializers.ModelSerializer): class VirtualAppPublicationSerializer(serializers.ModelSerializer): - app = ObjectRelatedField(attrs=('id', 'name', 'image_name',), label=_("Virtual App"), + app = ObjectRelatedField(attrs=('id', 'name', 'image_name',), label=_("Virtual app"), queryset=VirtualApp.objects.all()) provider = ObjectRelatedField(queryset=AppProvider.objects.all(), label=_("App Provider")) status = LabeledChoiceField(choices=PublishStatus.choices, label=_("Status"), default=Status.pending) diff --git a/apps/users/migrations/0044_usersession.py b/apps/users/migrations/0044_usersession.py index 325cd4f2c..9793eddd4 100644 --- a/apps/users/migrations/0044_usersession.py +++ b/apps/users/migrations/0044_usersession.py @@ -1,9 +1,10 @@ # Generated by Django 4.1.10 on 2023-09-14 07:23 +import uuid + +import django.db.models.deletion from django.conf import settings from django.db import migrations, models -import django.db.models.deletion -import uuid class Migration(migrations.Migration): @@ -30,7 +31,7 @@ class Migration(migrations.Migration): options={ 'verbose_name': 'User session', 'ordering': ['-date_created'], - 'permissions': [('offline_usersession', 'Offline ussr session')], + 'permissions': [('offline_usersession', 'Offline user session')], }, ), ] From 2e3184cbd646e43b50e94c0c89e7f97312bea389 Mon Sep 17 00:00:00 2001 From: Bai Date: Fri, 8 Dec 2023 16:16:15 +0800 Subject: [PATCH 039/111] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=20Endpoint=20?= =?UTF-8?q?=E8=8E=B7=E5=8F=96=E9=94=99=E8=AF=AF=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/terminal/models/component/endpoint.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/terminal/models/component/endpoint.py b/apps/terminal/models/component/endpoint.py index 8e73bb17d..eb080dc99 100644 --- a/apps/terminal/models/component/endpoint.py +++ b/apps/terminal/models/component/endpoint.py @@ -81,10 +81,10 @@ class Endpoint(JMSBaseModel): instance = instance.get_asset() if not isinstance(instance, Asset): return None - values = instance.labels.filter(name='endpoint').values_list('value', flat=True) + values = instance.labels.filter(label__name='endpoint').values_list('label__value', flat=True) if not values: return None - endpoints = cls.objects.filter(name__in=values).order_by('-date_updated') + endpoints = cls.objects.filter(name__in=list(values)).order_by('-date_updated') for endpoint in endpoints: if endpoint.is_valid_for(instance, protocol): return endpoint From b08e1f6a4731f21db473820bc7138a4f4ccd117b Mon Sep 17 00:00:00 2001 From: feng <1304903146@qq.com> Date: Fri, 8 Dec 2023 16:34:46 +0800 Subject: [PATCH 040/111] =?UTF-8?q?fix:=20=E6=94=B9=E5=AF=86=E8=AE=A1?= =?UTF-8?q?=E5=88=92=E5=88=9B=E5=BB=BA=E6=9B=B4=E6=96=B0=E5=A4=B1=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/accounts/serializers/account/template.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/accounts/serializers/account/template.py b/apps/accounts/serializers/account/template.py index f30821313..ea302c3fd 100644 --- a/apps/accounts/serializers/account/template.py +++ b/apps/accounts/serializers/account/template.py @@ -15,7 +15,9 @@ class PasswordRulesSerializer(serializers.Serializer): uppercase = serializers.BooleanField(default=True, label=_('Uppercase')) digit = serializers.BooleanField(default=True, label=_('Digit')) symbol = serializers.BooleanField(default=True, label=_('Special symbol')) - exclude_symbols = serializers.CharField(default='', max_length=16, label=_('Exclude symbol')) + exclude_symbols = serializers.CharField( + default='', allow_blank=True, max_length=16, label=_('Exclude symbol') + ) class AccountTemplateSerializer(BaseAccountSerializer): From 47cb6b1ec0ab01c1bfc9ce2f3735f9bdf89638c7 Mon Sep 17 00:00:00 2001 From: Bai Date: Fri, 8 Dec 2023 14:38:31 +0800 Subject: [PATCH 041/111] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E8=B5=84?= =?UTF-8?q?=E4=BA=A7=E5=88=97=E8=A1=A8=E6=94=AF=E6=8C=81=E9=80=9A=E8=BF=87?= =?UTF-8?q?=20=E5=88=9B=E5=BB=BA=E6=97=A5=E6=9C=9F=20=E8=BF=9B=E8=A1=8C?= =?UTF-8?q?=E6=8E=92=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/assets/api/asset/asset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/assets/api/asset/asset.py b/apps/assets/api/asset/asset.py index 729aa41bf..e0b1aa3bf 100644 --- a/apps/assets/api/asset/asset.py +++ b/apps/assets/api/asset/asset.py @@ -93,7 +93,7 @@ class AssetViewSet(SuggestionMixin, NodeFilterMixin, OrgBulkModelViewSet): model = Asset filterset_class = AssetFilterSet search_fields = ("name", "address", "comment") - ordering_fields = ('name', 'connectivity', 'platform', 'date_updated') + ordering_fields = ('name', 'connectivity', 'platform', 'date_updated', 'date_created') serializer_classes = ( ("default", serializers.AssetSerializer), ("platform", serializers.PlatformSerializer), From 8b05260a6cb33d4adfb0153f54ec3c59828e736b Mon Sep 17 00:00:00 2001 From: feng <1304903146@qq.com> Date: Fri, 8 Dec 2023 17:46:40 +0800 Subject: [PATCH 042/111] =?UTF-8?q?perf:=20PublicSetting=20API=20=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0GPT=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/settings/serializers/public.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/settings/serializers/public.py b/apps/settings/serializers/public.py index 21dc2c2f8..a135b1546 100644 --- a/apps/settings/serializers/public.py +++ b/apps/settings/serializers/public.py @@ -54,6 +54,8 @@ class PrivateSettingSerializer(PublicSettingSerializer): CACHE_LOGIN_PASSWORD_ENABLED = serializers.BooleanField() VAULT_ENABLED = serializers.BooleanField() VIRTUAL_APP_ENABLED = serializers.BooleanField() + CHAT_AI_ENABLED = serializers.BooleanField() + GPT_MODEL = serializers.CharField() class ServerInfoSerializer(serializers.Serializer): From c312cdb625af39c7f68fc83dd57572718202ea98 Mon Sep 17 00:00:00 2001 From: wangruidong <940853815@qq.com> Date: Mon, 11 Dec 2023 11:14:36 +0800 Subject: [PATCH 043/111] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E8=B5=84?= =?UTF-8?q?=E4=BA=A7=E6=8E=88=E6=9D=83=E8=BF=87=E6=9C=9F=E6=8F=90=E7=A4=BA?= =?UTF-8?q?=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/locale/ja/LC_MESSAGES/django.po | 15 +++++----- apps/locale/zh/LC_MESSAGES/django.po | 28 ++++++++++++++----- apps/perms/notifications.py | 2 +- .../perms/_msg_permed_items_expire.html | 4 +-- 4 files changed, 31 insertions(+), 18 deletions(-) diff --git a/apps/locale/ja/LC_MESSAGES/django.po b/apps/locale/ja/LC_MESSAGES/django.po index bf08b4575..df7dee7d5 100644 --- a/apps/locale/ja/LC_MESSAGES/django.po +++ b/apps/locale/ja/LC_MESSAGES/django.po @@ -4567,6 +4567,10 @@ msgstr "認定アカウント" msgid "today" msgstr "今日" +#: perms/notifications.py:12 settings/serializers/feature.py:106 +msgid "day" +msgstr "日" + #: perms/notifications.py:15 msgid "You permed assets is about to expire" msgstr "パーマ資産の有効期限が近づいています" @@ -4591,16 +4595,15 @@ msgstr "アセット認証ルールの有効期限が切れていることを確 msgid "Send asset permission expired notification" msgstr "アセット許可の有効期限通知を送信する" -#: perms/templates/perms/_msg_item_permissions_expire.html:7 + #: perms/templates/perms/_msg_permed_items_expire.html:7 -#, python-format msgid "" "\n" -" The following %(item_type)s will expire in %(count)s days\n" +" The following %(item_type)s will expire in %(count)s\n" " " msgstr "" "\n" -" 次の %(item_type)s は %(count)s 日以内に期限切れになります\n" +" 次の %(item_type)s は %(count)s 以内に期限切れになります\n" " " #: rbac/api/role.py:35 @@ -5512,10 +5515,6 @@ msgstr "チケットを有効にする" msgid "Ticket authorize default time" msgstr "デフォルト製造オーダ承認時間" -#: settings/serializers/feature.py:106 -msgid "day" -msgstr "日" - #: settings/serializers/feature.py:106 msgid "hour" msgstr "時" diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index 1302ce4f7..a34de449a 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -4516,6 +4516,10 @@ msgstr "授权账号" msgid "today" msgstr "今天" +#: perms/notifications.py:12 settings/serializers/feature.py:106 +msgid "day" +msgstr "天" + #: perms/notifications.py:15 msgid "You permed assets is about to expire" msgstr "你授权的资产即将到期" @@ -4541,15 +4545,29 @@ msgid "Send asset permission expired notification" msgstr "发送资产权限过期通知" #: perms/templates/perms/_msg_item_permissions_expire.html:7 -#: perms/templates/perms/_msg_permed_items_expire.html:7 -#, python-format +#, fuzzy, python-format +#| msgid "" +#| "\n" +#| " The following %(item_type)s will expire in %(count)s\n" +#| " " msgid "" "\n" " The following %(item_type)s will expire in %(count)s days\n" " " msgstr "" "\n" -" 以下 %(item_type)s 即将在 %(count)s 天后过期\n" +" 以下 %(item_type)s 即将在 %(count)s 后过期\n" +" " + +#: perms/templates/perms/_msg_permed_items_expire.html:7 +#, python-format +msgid "" +"\n" +" The following %(item_type)s will expire in %(count)s\n" +" " +msgstr "" +"\n" +" 以下 %(item_type)s 即将在 %(count)s 后过期\n" " " #: rbac/api/role.py:35 @@ -5454,10 +5472,6 @@ msgstr "启用工单" msgid "Ticket authorize default time" msgstr "默认工单授权时间" -#: settings/serializers/feature.py:106 -msgid "day" -msgstr "天" - #: settings/serializers/feature.py:106 msgid "hour" msgstr "时" diff --git a/apps/perms/notifications.py b/apps/perms/notifications.py index 2a8aa0c2d..b00c50f43 100644 --- a/apps/perms/notifications.py +++ b/apps/perms/notifications.py @@ -9,7 +9,7 @@ class PermedAssetsWillExpireUserMsg(UserMessage): def __init__(self, user, assets, day_count=0): super().__init__(user) self.assets = assets - self.day_count = _('today') if day_count == 0 else day_count + self.day_count = _('today') if day_count == 0 else day_count + _('day') def get_html_msg(self) -> dict: subject = _("You permed assets is about to expire") diff --git a/apps/perms/templates/perms/_msg_permed_items_expire.html b/apps/perms/templates/perms/_msg_permed_items_expire.html index f4229b7eb..eb2a3cc47 100644 --- a/apps/perms/templates/perms/_msg_permed_items_expire.html +++ b/apps/perms/templates/perms/_msg_permed_items_expire.html @@ -5,7 +5,7 @@

    {% blocktranslate %} - The following {{ item_type }} will expire in {{ count }} days + The following {{ item_type }} will expire in {{ count }} {% endblocktranslate %}

    @@ -15,7 +15,7 @@ {% endfor %} -
    +
    -

    {% trans 'If you have any question, please contact the administrator' %} From dd57b145622480957daae14b39d89c3211fa3001 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Mon, 11 Dec 2023 13:57:35 +0800 Subject: [PATCH 044/111] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=20sqlserver?= =?UTF-8?q?=20=E6=94=AF=E6=8C=81=20(#12288)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 增加 sqlserver 支持 * feat: 删除一些 migrations --------- Co-authored-by: Aaron3S --- apps/terminal/connect_methods.py | 3 ++- ...er_port_alter_appprovider_apps_and_more.py | 21 +++++++++++++++++++ apps/terminal/models/component/endpoint.py | 1 + apps/terminal/serializers/endpoint.py | 2 +- 4 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 apps/terminal/migrations/0069_endpoint_sqlserver_port_alter_appprovider_apps_and_more.py diff --git a/apps/terminal/connect_methods.py b/apps/terminal/connect_methods.py index 27d67e1af..72007112d 100644 --- a/apps/terminal/connect_methods.py +++ b/apps/terminal/connect_methods.py @@ -49,6 +49,7 @@ class NativeClient(TextChoices): Protocol.mongodb: [cls.db_client, cls.db_guide], Protocol.oracle: [cls.db_client, cls.db_guide], Protocol.postgresql: [cls.db_client, cls.db_guide], + Protocol.sqlserver: [cls.db_client, cls.db_guide], } return clients @@ -180,7 +181,7 @@ class ConnectMethodUtil: 'support': [ Protocol.mysql, Protocol.postgresql, Protocol.oracle, Protocol.mariadb, - Protocol.redis + Protocol.redis, Protocol.sqlserver ], 'match': 'map' }, diff --git a/apps/terminal/migrations/0069_endpoint_sqlserver_port_alter_appprovider_apps_and_more.py b/apps/terminal/migrations/0069_endpoint_sqlserver_port_alter_appprovider_apps_and_more.py new file mode 100644 index 000000000..f68cca188 --- /dev/null +++ b/apps/terminal/migrations/0069_endpoint_sqlserver_port_alter_appprovider_apps_and_more.py @@ -0,0 +1,21 @@ +# Generated by Django 4.1.10 on 2023-12-08 09:41 + +import common.db.fields +import django.core.validators +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('terminal', '0068_virtualapp'), + ] + + operations = [ + migrations.AddField( + model_name='endpoint', + name='sqlserver_port', + field=common.db.fields.PortField(default=14330, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(65535)], verbose_name='SQLServer port'), + ), + ] diff --git a/apps/terminal/models/component/endpoint.py b/apps/terminal/models/component/endpoint.py index eb080dc99..d9d4cfab8 100644 --- a/apps/terminal/models/component/endpoint.py +++ b/apps/terminal/models/component/endpoint.py @@ -20,6 +20,7 @@ class Endpoint(JMSBaseModel): mariadb_port = PortField(default=33062, verbose_name=_('MariaDB port')) postgresql_port = PortField(default=54320, verbose_name=_('PostgreSQL port')) redis_port = PortField(default=63790, verbose_name=_('Redis port')) + sqlserver_port = PortField(default=14330, verbose_name=_('SQLServer port')) comment = models.TextField(default='', blank=True, verbose_name=_('Comment')) diff --git a/apps/terminal/serializers/endpoint.py b/apps/terminal/serializers/endpoint.py index 82de09fce..2b734f71e 100644 --- a/apps/terminal/serializers/endpoint.py +++ b/apps/terminal/serializers/endpoint.py @@ -28,7 +28,7 @@ class EndpointSerializer(BulkModelSerializer): fields_small = [ 'host', 'https_port', 'http_port', 'ssh_port', 'rdp_port', 'mysql_port', 'mariadb_port', 'postgresql_port', 'redis_port', - 'oracle_port_range', 'oracle_port', + 'oracle_port_range', 'oracle_port', 'sqlserver_port', ] fields = fields_mini + fields_small + [ 'comment', 'date_created', 'date_updated', 'created_by' From 4ea20a9103597c216fa99d8a242a67a6147e7872 Mon Sep 17 00:00:00 2001 From: Eric Date: Mon, 11 Dec 2023 14:32:37 +0800 Subject: [PATCH 045/111] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E8=BF=81?= =?UTF-8?q?=E7=A7=BB=E6=96=87=E4=BB=B6=20verbose=5Fname?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/terminal/migrations/0068_virtualapp.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/apps/terminal/migrations/0068_virtualapp.py b/apps/terminal/migrations/0068_virtualapp.py index 957514480..1ac230620 100644 --- a/apps/terminal/migrations/0068_virtualapp.py +++ b/apps/terminal/migrations/0068_virtualapp.py @@ -1,9 +1,10 @@ # Generated by Django 4.1.10 on 2023-12-05 07:02 -from django.db import migrations, models -import django.db.models.deletion import uuid +import django.db.models.deletion +from django.db import migrations, models + class Migration(migrations.Migration): @@ -67,7 +68,7 @@ class Migration(migrations.Migration): ('comment', models.TextField(blank=True, default='', verbose_name='Comment')), ('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)), ('status', models.CharField(default='pending', max_length=16, verbose_name='Status')), - ('app', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='publications', to='terminal.virtualapp', verbose_name='Virtual App')), + ('app', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='publications', to='terminal.virtualapp', verbose_name='Virtual app')), ('provider', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='publications', to='terminal.appprovider', verbose_name='App Provider')), ], options={ @@ -83,7 +84,7 @@ class Migration(migrations.Migration): migrations.AddField( model_name='appprovider', name='apps', - field=models.ManyToManyField(through='terminal.VirtualAppPublication', to='terminal.virtualapp', verbose_name='VirtualApp'), + field=models.ManyToManyField(through='terminal.VirtualAppPublication', to='terminal.virtualapp', verbose_name='Virtual app'), ), migrations.AddField( model_name='appprovider', From e3ac26e377625425e41d99079393296850cf3332 Mon Sep 17 00:00:00 2001 From: ibuler Date: Mon, 11 Dec 2023 11:10:45 +0800 Subject: [PATCH 046/111] =?UTF-8?q?perf:=20=E4=BF=AE=E6=94=B9=20rbac=20lab?= =?UTF-8?q?els=20node?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/rbac/tree.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/rbac/tree.py b/apps/rbac/tree.py index 49fa1a23c..8e9a56831 100644 --- a/apps/rbac/tree.py +++ b/apps/rbac/tree.py @@ -39,7 +39,7 @@ app_nodes_data = [ {'id': 'rbac', 'view': 'view_console'}, {'id': 'settings', 'view': 'view_setting'}, {'id': 'tickets', 'view': 'view_other'}, - {'id': 'labels', 'view': 'view_label'}, + {'id': 'labels', 'view': 'view_console'}, {'id': 'authentication', 'view': 'view_other'}, {'id': 'ops', 'view': 'view_workbench'}, ] From d0b0c87d3c742c64e4ae2e958a927b98f8ae5f10 Mon Sep 17 00:00:00 2001 From: wangruidong <940853815@qq.com> Date: Tue, 5 Dec 2023 19:09:17 +0800 Subject: [PATCH 047/111] =?UTF-8?q?feat:=20=E6=94=AF=E6=8C=81=E6=89=B9?= =?UTF-8?q?=E9=87=8F=E5=8F=91=E9=80=81=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/locale/ja/LC_MESSAGES/django.mo | 4 +- apps/locale/ja/LC_MESSAGES/django.po | 76 +++++++++-------- apps/locale/zh/LC_MESSAGES/django.mo | 4 +- apps/locale/zh/LC_MESSAGES/django.po | 84 +++++++++---------- apps/ops/ansible/runner.py | 31 ++++++- apps/ops/api/job.py | 45 +++++++++- apps/ops/const.py | 1 + apps/ops/models/job.py | 14 +++- apps/ops/serializers/job.py | 14 +++- apps/perms/notifications.py | 2 +- .../perms/_msg_item_permissions_expire.html | 2 +- 11 files changed, 186 insertions(+), 91 deletions(-) diff --git a/apps/locale/ja/LC_MESSAGES/django.mo b/apps/locale/ja/LC_MESSAGES/django.mo index 17fc22c3a..5e4c9d63f 100644 --- a/apps/locale/ja/LC_MESSAGES/django.mo +++ b/apps/locale/ja/LC_MESSAGES/django.mo @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5d443763c06877304dca8dac76131271287acc6594df665bdf9445455c5187f1 -size 167791 +oid sha256:a00c0d53df7fa88fc2fe69adda31fd9ab581b5a0362a01b8191924f74fab800d +size 167820 diff --git a/apps/locale/ja/LC_MESSAGES/django.po b/apps/locale/ja/LC_MESSAGES/django.po index df7dee7d5..8dad75a93 100644 --- a/apps/locale/ja/LC_MESSAGES/django.po +++ b/apps/locale/ja/LC_MESSAGES/django.po @@ -8,8 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-12-08 14:51+0800\n" -"POT-Creation-Date: 2023-12-08 15:33+0800\n" +"POT-Creation-Date: 2023-12-11 14:54+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -88,7 +87,7 @@ msgstr "集めました" msgid "Template" msgstr "テンプレート" -#: accounts/const/account.py:31 ops/const.py:45 +#: accounts/const/account.py:31 ops/const.py:46 msgid "Skip" msgstr "スキップ" @@ -100,7 +99,7 @@ msgstr "更新" #: accounts/const/account.py:33 #: accounts/serializers/automations/change_secret.py:150 audits/const.py:62 #: audits/signal_handlers/activity_log.py:33 common/const/choices.py:19 -#: ops/const.py:74 terminal/const.py:79 xpack/plugins/cloud/const.py:46 +#: ops/const.py:75 terminal/const.py:79 xpack/plugins/cloud/const.py:46 msgid "Failed" msgstr "失敗しました" @@ -208,7 +207,6 @@ msgstr "作成のみ" msgid "Email" msgstr "メール" -#: accounts/const/automation.py:104 terminal/const.py:87 #: accounts/const/automation.py:105 terminal/const.py:87 msgid "SFTP" msgstr "SFTP" @@ -265,7 +263,7 @@ msgstr "資産" #: accounts/models/account.py:53 accounts/models/template.py:16 #: accounts/serializers/account/account.py:220 #: accounts/serializers/account/account.py:268 -#: accounts/serializers/account/template.py:25 +#: accounts/serializers/account/template.py:27 #: authentication/serializers/connect_token_secret.py:50 msgid "Su from" msgstr "から切り替え" @@ -397,7 +395,7 @@ msgstr "理由" #: accounts/models/automations/backup_account.py:135 #: accounts/serializers/automations/change_secret.py:105 #: accounts/serializers/automations/change_secret.py:128 -#: ops/serializers/job.py:55 terminal/serializers/session.py:49 +#: ops/serializers/job.py:65 terminal/serializers/session.py:49 msgid "Is success" msgstr "成功は" @@ -585,7 +583,7 @@ msgstr "ひみつ" msgid "Secret strategy" msgstr "鍵ポリシー" -#: accounts/models/base.py:44 accounts/serializers/account/template.py:22 +#: accounts/models/base.py:44 accounts/serializers/account/template.py:24 #: accounts/serializers/automations/change_secret.py:44 msgid "Password rules" msgstr "パスワードルール" @@ -902,19 +900,19 @@ msgstr "数値#スウスウ#" msgid "Special symbol" msgstr "特殊記号" -#: accounts/serializers/account/template.py:18 +#: accounts/serializers/account/template.py:19 msgid "Exclude symbol" msgstr "除外文字" -#: accounts/serializers/account/template.py:36 +#: accounts/serializers/account/template.py:38 msgid "Secret generation strategy for account creation" msgstr "账号创建时,密文生成策略" -#: accounts/serializers/account/template.py:37 +#: accounts/serializers/account/template.py:39 msgid "Whether to automatically push the account to the asset" msgstr "是否自动推送账号到资产" -#: accounts/serializers/account/template.py:40 +#: accounts/serializers/account/template.py:42 msgid "" "Associated platform, you can configure push parameters. If not associated, " "default parameters will be used" @@ -980,7 +978,7 @@ msgstr "自動タスク実行履歴" #: accounts/serializers/automations/change_secret.py:149 audits/const.py:61 #: audits/models.py:64 audits/signal_handlers/activity_log.py:33 -#: common/const/choices.py:18 ops/const.py:72 ops/serializers/celery.py:40 +#: common/const/choices.py:18 ops/const.py:73 ops/serializers/celery.py:40 #: terminal/const.py:78 terminal/models/session/sharing.py:121 #: tickets/views/approve.py:117 msgid "Success" @@ -1118,7 +1116,7 @@ msgid "Accounts" msgstr "アカウント" #: acls/models/command_acl.py:16 assets/models/cmd_filter.py:60 -#: ops/serializers/job.py:54 terminal/const.py:86 +#: ops/serializers/job.py:64 terminal/const.py:86 #: terminal/models/session/session.py:42 terminal/serializers/command.py:18 #: terminal/templates/terminal/_msg_command_alert.html:12 #: terminal/templates/terminal/_msg_command_execute_alert.html:10 @@ -1668,7 +1666,7 @@ msgstr "プロトコル" msgid "Sudo" msgstr "すど" -#: assets/models/_user.py:55 ops/const.py:49 ops/const.py:59 +#: assets/models/_user.py:55 ops/const.py:50 ops/const.py:60 msgid "Shell" msgstr "シェル" @@ -3639,7 +3637,7 @@ msgstr "の準備を" msgid "Pending" msgstr "未定" -#: common/const/choices.py:17 ops/const.py:71 +#: common/const/choices.py:17 ops/const.py:72 msgid "Running" msgstr "ランニング" @@ -4053,6 +4051,10 @@ msgstr "タスクは存在しません" msgid "Task {} args or kwargs error" msgstr "タスク実行パラメータエラー" +#: ops/api/job.py:128 +msgid "Duplicate file exists" +msgstr "重複したファイルが存在する" + #: ops/api/playbook.py:39 msgid "Currently playbook is being used in a job" msgstr "現在プレイブックは1つのジョブで使用されています" @@ -4125,47 +4127,53 @@ msgstr "コマンド#コマンド#" msgid "Playbook" msgstr "Playbook" -#: ops/const.py:43 +#: ops/const.py:40 +#, fuzzy +#| msgid "Upload" +msgid "Upload File" +msgstr "アップロード" + +#: ops/const.py:44 msgid "Privileged Only" msgstr "特権アカウントのみ" -#: ops/const.py:44 +#: ops/const.py:45 msgid "Privileged First" msgstr "特権アカウント優先" -#: ops/const.py:50 ops/const.py:60 +#: ops/const.py:51 ops/const.py:61 msgid "Powershell" msgstr "PowerShell" -#: ops/const.py:51 ops/const.py:61 +#: ops/const.py:52 ops/const.py:62 msgid "Python" msgstr "Python" -#: ops/const.py:52 ops/const.py:62 +#: ops/const.py:53 ops/const.py:63 msgid "MySQL" msgstr "MySQL" -#: ops/const.py:53 ops/const.py:64 +#: ops/const.py:54 ops/const.py:65 msgid "PostgreSQL" msgstr "PostgreSQL" -#: ops/const.py:54 ops/const.py:65 +#: ops/const.py:55 ops/const.py:66 msgid "SQLServer" msgstr "SQLServer" -#: ops/const.py:55 ops/const.py:67 +#: ops/const.py:56 ops/const.py:68 msgid "Raw" msgstr "" -#: ops/const.py:63 +#: ops/const.py:64 msgid "MariaDB" msgstr "MariaDB" -#: ops/const.py:66 +#: ops/const.py:67 msgid "Oracle" msgstr "Oracle" -#: ops/const.py:73 +#: ops/const.py:74 msgid "Timeout" msgstr "タイムアウト" @@ -4299,7 +4307,7 @@ msgstr "Material" msgid "Material Type" msgstr "Material を選択してオプションを設定します。" -#: ops/models/job.py:557 +#: ops/models/job.py:565 msgid "Job Execution" msgstr "ジョブ実行" @@ -4343,15 +4351,15 @@ msgstr "{max_threshold} を超えるCPUロード: => {value}" msgid "Run after save" msgstr "保存後に実行" -#: ops/serializers/job.py:53 +#: ops/serializers/job.py:63 msgid "Job type" msgstr "タスクの種類" -#: ops/serializers/job.py:56 terminal/serializers/session.py:53 +#: ops/serializers/job.py:66 terminal/serializers/session.py:53 msgid "Is finished" msgstr "終了しました" -#: ops/serializers/job.py:57 +#: ops/serializers/job.py:67 msgid "Time cost" msgstr "時を過ごす" @@ -4567,7 +4575,8 @@ msgstr "認定アカウント" msgid "today" msgstr "今日" -#: perms/notifications.py:12 settings/serializers/feature.py:106 +#: perms/notifications.py:12 perms/notifications.py:44 +#: settings/serializers/feature.py:106 msgid "day" msgstr "日" @@ -4595,8 +4604,9 @@ msgstr "アセット認証ルールの有効期限が切れていることを確 msgid "Send asset permission expired notification" msgstr "アセット許可の有効期限通知を送信する" - +#: perms/templates/perms/_msg_item_permissions_expire.html:7 #: perms/templates/perms/_msg_permed_items_expire.html:7 +#, python-format msgid "" "\n" " The following %(item_type)s will expire in %(count)s\n" diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index 4de90d618..16c60122c 100644 --- a/apps/locale/zh/LC_MESSAGES/django.mo +++ b/apps/locale/zh/LC_MESSAGES/django.mo @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8976c6b41e2c0ce591b2b257fd0352b4ed7517661f7df83ba74b302b8cf94b00 -size 137479 +oid sha256:d4a9a61bf1b247d3843001737ebfa6d5f8580f2d9c1ae0fc76649ecc535f5d96 +size 137563 diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index a34de449a..29b3e9c50 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: JumpServer 0.3.3\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-12-08 15:33+0800\n" +"POT-Creation-Date: 2023-12-11 14:54+0800\n" "PO-Revision-Date: 2021-05-20 10:54+0800\n" "Last-Translator: ibuler \n" "Language-Team: JumpServer team\n" @@ -86,7 +86,7 @@ msgstr "收集" msgid "Template" msgstr "模板" -#: accounts/const/account.py:31 ops/const.py:45 +#: accounts/const/account.py:31 ops/const.py:46 msgid "Skip" msgstr "跳过" @@ -98,7 +98,7 @@ msgstr "更新" #: accounts/const/account.py:33 #: accounts/serializers/automations/change_secret.py:150 audits/const.py:62 #: audits/signal_handlers/activity_log.py:33 common/const/choices.py:19 -#: ops/const.py:74 terminal/const.py:79 xpack/plugins/cloud/const.py:46 +#: ops/const.py:75 terminal/const.py:79 xpack/plugins/cloud/const.py:46 msgid "Failed" msgstr "失败" @@ -262,7 +262,7 @@ msgstr "资产" #: accounts/models/account.py:53 accounts/models/template.py:16 #: accounts/serializers/account/account.py:220 #: accounts/serializers/account/account.py:268 -#: accounts/serializers/account/template.py:25 +#: accounts/serializers/account/template.py:27 #: authentication/serializers/connect_token_secret.py:50 msgid "Su from" msgstr "切换自" @@ -394,7 +394,7 @@ msgstr "原因" #: accounts/models/automations/backup_account.py:135 #: accounts/serializers/automations/change_secret.py:105 #: accounts/serializers/automations/change_secret.py:128 -#: ops/serializers/job.py:55 terminal/serializers/session.py:49 +#: ops/serializers/job.py:65 terminal/serializers/session.py:49 msgid "Is success" msgstr "是否成功" @@ -582,7 +582,7 @@ msgstr "密钥" msgid "Secret strategy" msgstr "密文策略" -#: accounts/models/base.py:44 accounts/serializers/account/template.py:22 +#: accounts/models/base.py:44 accounts/serializers/account/template.py:24 #: accounts/serializers/automations/change_secret.py:44 msgid "Password rules" msgstr "密码规则" @@ -898,19 +898,19 @@ msgstr "数字" msgid "Special symbol" msgstr "特殊字符" -#: accounts/serializers/account/template.py:18 +#: accounts/serializers/account/template.py:19 msgid "Exclude symbol" msgstr "排除字符" -#: accounts/serializers/account/template.py:36 +#: accounts/serializers/account/template.py:38 msgid "Secret generation strategy for account creation" msgstr "密码生成策略,用于账号创建时,设置密码" -#: accounts/serializers/account/template.py:37 +#: accounts/serializers/account/template.py:39 msgid "Whether to automatically push the account to the asset" msgstr "是否自动推送账号到资产" -#: accounts/serializers/account/template.py:40 +#: accounts/serializers/account/template.py:42 msgid "" "Associated platform, you can configure push parameters. If not associated, " "default parameters will be used" @@ -975,7 +975,7 @@ msgstr "自动化任务执行历史" #: accounts/serializers/automations/change_secret.py:149 audits/const.py:61 #: audits/models.py:64 audits/signal_handlers/activity_log.py:33 -#: common/const/choices.py:18 ops/const.py:72 ops/serializers/celery.py:40 +#: common/const/choices.py:18 ops/const.py:73 ops/serializers/celery.py:40 #: terminal/const.py:78 terminal/models/session/sharing.py:121 #: tickets/views/approve.py:117 msgid "Success" @@ -1113,7 +1113,7 @@ msgid "Accounts" msgstr "账号管理" #: acls/models/command_acl.py:16 assets/models/cmd_filter.py:60 -#: ops/serializers/job.py:54 terminal/const.py:86 +#: ops/serializers/job.py:64 terminal/const.py:86 #: terminal/models/session/session.py:42 terminal/serializers/command.py:18 #: terminal/templates/terminal/_msg_command_alert.html:12 #: terminal/templates/terminal/_msg_command_execute_alert.html:10 @@ -1659,7 +1659,7 @@ msgstr "协议" msgid "Sudo" msgstr "Sudo" -#: assets/models/_user.py:55 ops/const.py:49 ops/const.py:59 +#: assets/models/_user.py:55 ops/const.py:50 ops/const.py:60 msgid "Shell" msgstr "Shell" @@ -3594,7 +3594,7 @@ msgstr "准备" msgid "Pending" msgstr "待定的" -#: common/const/choices.py:17 ops/const.py:71 +#: common/const/choices.py:17 ops/const.py:72 msgid "Running" msgstr "运行中" @@ -4003,6 +4003,10 @@ msgstr "任务 {} 不存在" msgid "Task {} args or kwargs error" msgstr "任务 {} 执行参数错误" +#: ops/api/job.py:128 +msgid "Duplicate file exists" +msgstr "存在同名文件" + #: ops/api/playbook.py:39 msgid "Currently playbook is being used in a job" msgstr "当前 playbook 正在作业中使用" @@ -4075,47 +4079,51 @@ msgstr "命令" msgid "Playbook" msgstr "Playbook" -#: ops/const.py:43 +#: ops/const.py:40 +msgid "Upload File" +msgstr "上传" + +#: ops/const.py:44 msgid "Privileged Only" msgstr "仅限特权账号" -#: ops/const.py:44 +#: ops/const.py:45 msgid "Privileged First" msgstr "特权账号优先" -#: ops/const.py:50 ops/const.py:60 +#: ops/const.py:51 ops/const.py:61 msgid "Powershell" msgstr "PowerShell" -#: ops/const.py:51 ops/const.py:61 +#: ops/const.py:52 ops/const.py:62 msgid "Python" msgstr "Python" -#: ops/const.py:52 ops/const.py:62 +#: ops/const.py:53 ops/const.py:63 msgid "MySQL" msgstr "MySQL" -#: ops/const.py:53 ops/const.py:64 +#: ops/const.py:54 ops/const.py:65 msgid "PostgreSQL" msgstr "PostgreSQL" -#: ops/const.py:54 ops/const.py:65 +#: ops/const.py:55 ops/const.py:66 msgid "SQLServer" msgstr "SQLServer" -#: ops/const.py:55 ops/const.py:67 +#: ops/const.py:56 ops/const.py:68 msgid "Raw" msgstr "Raw" -#: ops/const.py:63 +#: ops/const.py:64 msgid "MariaDB" msgstr "MariaDB" -#: ops/const.py:66 +#: ops/const.py:67 msgid "Oracle" msgstr "Oracle" -#: ops/const.py:73 +#: ops/const.py:74 msgid "Timeout" msgstr "超时" @@ -4249,7 +4257,7 @@ msgstr "Material" msgid "Material Type" msgstr "Material 类型" -#: ops/models/job.py:557 +#: ops/models/job.py:565 msgid "Job Execution" msgstr "作业执行" @@ -4293,15 +4301,15 @@ msgstr "CPU 使用率超过 {max_threshold}: => {value}" msgid "Run after save" msgstr "保存后执行" -#: ops/serializers/job.py:53 +#: ops/serializers/job.py:63 msgid "Job type" msgstr "任务类型" -#: ops/serializers/job.py:56 terminal/serializers/session.py:53 +#: ops/serializers/job.py:66 terminal/serializers/session.py:53 msgid "Is finished" msgstr "是否完成" -#: ops/serializers/job.py:57 +#: ops/serializers/job.py:67 msgid "Time cost" msgstr "花费时间" @@ -4516,7 +4524,8 @@ msgstr "授权账号" msgid "today" msgstr "今天" -#: perms/notifications.py:12 settings/serializers/feature.py:106 +#: perms/notifications.py:12 perms/notifications.py:44 +#: settings/serializers/feature.py:106 msgid "day" msgstr "天" @@ -4545,20 +4554,6 @@ msgid "Send asset permission expired notification" msgstr "发送资产权限过期通知" #: perms/templates/perms/_msg_item_permissions_expire.html:7 -#, fuzzy, python-format -#| msgid "" -#| "\n" -#| " The following %(item_type)s will expire in %(count)s\n" -#| " " -msgid "" -"\n" -" The following %(item_type)s will expire in %(count)s days\n" -" " -msgstr "" -"\n" -" 以下 %(item_type)s 即将在 %(count)s 后过期\n" -" " - #: perms/templates/perms/_msg_permed_items_expire.html:7 #, python-format msgid "" @@ -8801,6 +8796,7 @@ msgstr "企业专业版" msgid "Ultimate edition" msgstr "企业旗舰版" + #~ msgid "FeiShu query user failed" #~ msgstr "飞书查询用户失败" diff --git a/apps/ops/ansible/runner.py b/apps/ops/ansible/runner.py index 4610ddfd2..47af111e5 100644 --- a/apps/ops/ansible/runner.py +++ b/apps/ops/ansible/runner.py @@ -1,8 +1,9 @@ import os import uuid - +import shutil import ansible_runner from django.conf import settings +from django.utils._os import safe_join from .callback import DefaultCallback from ..utils import get_ansible_log_verbosity @@ -85,6 +86,34 @@ class PlaybookRunner: return self.cb +class UploadFileRunner: + def __init__(self, inventory, job_id, dest_path, callback=None): + self.id = uuid.uuid4() + self.inventory = inventory + self.cb = DefaultCallback() + upload_file_dir = safe_join(settings.DATA_DIR, 'job_upload_file') + self.src_paths = safe_join(upload_file_dir, str(job_id)) + self.dest_path = dest_path + + def run(self, verbosity=0, **kwargs): + verbosity = get_ansible_log_verbosity(verbosity) + ansible_runner.run( + host_pattern="*", + inventory=self.inventory, + module='copy', + module_args=f"src={self.src_paths}/ dest={self.dest_path}", + verbosity=verbosity, + event_handler=self.cb.event_handler, + status_handler=self.cb.status_handler, + **kwargs + ) + try: + shutil.rmtree(self.src_paths) + except OSError as e: + print(f"del upload tmp dir {self.src_paths} failed! {e}") + return self.cb + + class CommandRunner(AdHocRunner): def __init__(self, inventory, command, pattern='*', project_dir='/tmp/'): super().__init__(inventory, 'shell', command, pattern, project_dir) diff --git a/apps/ops/api/job.py b/apps/ops/api/job.py index f2bf92cae..3bcb7393e 100644 --- a/apps/ops/api/job.py +++ b/apps/ops/api/job.py @@ -1,16 +1,22 @@ +import json +import os from django.conf import settings from django.db import transaction from django.db.models import Count -from django.db.transaction import atomic from django.shortcuts import get_object_or_404 +from django.utils._os import safe_join +from django.utils.translation import gettext_lazy as _ + +from rest_framework.decorators import action from rest_framework.response import Response from rest_framework.views import APIView from assets.models import Asset +from common.const.http import POST from common.permissions import IsValidUser from ops.const import Types from ops.models import Job, JobExecution -from ops.serializers.job import JobSerializer, JobExecutionSerializer +from ops.serializers.job import JobSerializer, JobExecutionSerializer, FileSerializer __all__ = [ 'JobViewSet', 'JobExecutionViewSet', 'JobRunVariableHelpAPIView', @@ -24,6 +30,7 @@ from orgs.utils import tmp_to_org, get_current_org from accounts.models import Account from perms.models import PermNode from perms.utils import UserPermAssetUtil +from jumpserver.settings import get_file_md5 def set_task_to_serializer_data(serializer, task_id): @@ -91,6 +98,40 @@ class JobViewSet(OrgBulkModelViewSet): transaction.on_commit( lambda: run_ops_job_execution.apply_async((str(execution.id),), task_id=str(execution.id))) + @action(methods=[POST], detail=False, serializer_class=FileSerializer, permission_classes=[IsValidUser, ], + url_path='upload') + def upload(self, request, *args, **kwargs): + serializer = self.get_serializer(data=request.data) + if not serializer.is_valid(): + msg = 'Upload data invalid: {}'.format(serializer.errors) + return Response({'msg': msg}, status=400) + uploaded_files = request.FILES.getlist('files') + job_id = request.data.get('job_id', '') + job = get_object_or_404(Job, pk=job_id) + job_args = json.loads(job.args) + src_path_info = [] + filename_set = set() + same_filenames = [] + upload_file_dir = safe_join(settings.DATA_DIR, 'job_upload_file') + for uploaded_file in uploaded_files: + filename = uploaded_file.name + saved_path = safe_join(upload_file_dir, f'{job_id}/{filename}') + os.makedirs(os.path.dirname(saved_path), exist_ok=True) + with open(saved_path, 'wb+') as destination: + for chunk in uploaded_file.chunks(): + destination.write(chunk) + if filename in filename_set: + same_filenames.append(filename) + filename_set.add(filename) + src_path_info.append({'filename': filename, 'md5': get_file_md5(saved_path)}) + if same_filenames: + return Response({'msg': _("Duplicate file exists")}, status=400) + job_args['src_path_info'] = src_path_info + job.args = json.dumps(job_args) + job.save() + self.run_job(job, serializer) + return Response({'task_id': serializer.data.get('task_id')}, status=201) + class JobExecutionViewSet(OrgBulkModelViewSet): serializer_class = JobExecutionSerializer diff --git a/apps/ops/const.py b/apps/ops/const.py index 7fa636a0f..578697c48 100644 --- a/apps/ops/const.py +++ b/apps/ops/const.py @@ -37,6 +37,7 @@ class CreateMethods(models.TextChoices): class Types(models.TextChoices): adhoc = 'adhoc', _('Adhoc') playbook = 'playbook', _('Playbook') + upload_file = 'upload_file', _('Upload File') class RunasPolicies(models.TextChoices): diff --git a/apps/ops/models/job.py b/apps/ops/models/job.py index 7271a3401..358c37136 100644 --- a/apps/ops/models/job.py +++ b/apps/ops/models/job.py @@ -23,7 +23,7 @@ from assets.models import Asset from assets.automations.base.manager import SSHTunnelManager from common.db.encoder import ModelJSONFieldEncoder from labels.mixins import LabeledMixin -from ops.ansible import JMSInventory, AdHocRunner, PlaybookRunner, CommandInBlackListException +from ops.ansible import JMSInventory, AdHocRunner, PlaybookRunner, CommandInBlackListException, UploadFileRunner from ops.mixin import PeriodTaskModelMixin from ops.variables import * from ops.const import Types, RunasPolicies, JobStatus, JobModules @@ -362,7 +362,7 @@ class JobExecution(JMSOrgBaseModel): static_variables = self.gather_static_variables() extra_vars.update(static_variables) - if self.current_job.type == 'adhoc': + if self.current_job.type == Types.adhoc: module, args = self.compile_shell() runner = AdHocRunner( @@ -374,10 +374,18 @@ class JobExecution(JMSOrgBaseModel): project_dir=self.private_dir, extra_vars=extra_vars, ) - elif self.current_job.type == 'playbook': + elif self.current_job.type == Types.playbook: runner = PlaybookRunner( self.inventory_path, self.current_job.playbook.entry ) + elif self.current_job.type == Types.upload_file: + job_id = self.current_job.id + args = json.loads(self.current_job.args) + dst_path = args.get('dst_path') + if dst_path: + runner = UploadFileRunner(self.inventory_path, job_id, dst_path) + else: + raise ValueError("dst_path is null") else: raise Exception("unsupported job type") return runner diff --git a/apps/ops/serializers/job.py b/apps/ops/serializers/job.py index d97a0929a..3bb91d463 100644 --- a/apps/ops/serializers/job.py +++ b/apps/ops/serializers/job.py @@ -21,9 +21,12 @@ class JobSerializer(ResourceLabelsMixin, BulkOrgResourceModelSerializer, PeriodT def to_internal_value(self, data): instant = data.get('instant', False) + job_type = data.get('type', '') + _uid = str(uuid.uuid4()).split('-')[-1] if instant: - _uid = str(uuid.uuid4()).split('-')[-1] data['name'] = f'job-{_uid}' + if job_type == 'upload_file': + data['name'] = f'upload_file-{_uid}' return super().to_internal_value(data) def get_request_user(self): @@ -44,10 +47,17 @@ class JobSerializer(ResourceLabelsMixin, BulkOrgResourceModelSerializer, PeriodT "use_parameter_define", "parameters_define", "timeout", "chdir", "comment", "summary", "is_periodic", "interval", "crontab", "nodes", - "run_after_save", + "run_after_save" ] +class FileSerializer(serializers.Serializer): + files = serializers.FileField(allow_empty_file=True) + + class Meta: + ref_name = "JobFileSerializer" + + class JobExecutionSerializer(BulkOrgResourceModelSerializer): creator = ReadableHiddenField(default=serializers.CurrentUserDefault()) job_type = serializers.ReadOnlyField(label=_("Job type")) diff --git a/apps/perms/notifications.py b/apps/perms/notifications.py index b00c50f43..5c82c2589 100644 --- a/apps/perms/notifications.py +++ b/apps/perms/notifications.py @@ -41,7 +41,7 @@ class AssetPermsWillExpireForOrgAdminMsg(UserMessage): super().__init__(user) self.perms = perms self.org = org - self.day_count = _('today') if day_count == 0 else day_count + self.day_count = _('today') if day_count == 0 else day_count + _('day') def get_items_with_url(self): items_with_url = [] diff --git a/apps/perms/templates/perms/_msg_item_permissions_expire.html b/apps/perms/templates/perms/_msg_item_permissions_expire.html index 9a9dc8244..eba2e7e5b 100644 --- a/apps/perms/templates/perms/_msg_item_permissions_expire.html +++ b/apps/perms/templates/perms/_msg_item_permissions_expire.html @@ -5,7 +5,7 @@

    {% blocktranslate %} - The following {{ item_type }} will expire in {{ count }} days + The following {{ item_type }} will expire in {{ count }} {% endblocktranslate %}

    From 8e89d42343a8edc61416fc61b097516cb916e7ca Mon Sep 17 00:00:00 2001 From: wangruidong <940853815@qq.com> Date: Mon, 11 Dec 2023 18:06:22 +0800 Subject: [PATCH 048/111] =?UTF-8?q?perf:=20=E5=90=8C=E5=90=8D=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/ops/api/job.py | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/apps/ops/api/job.py b/apps/ops/api/job.py index 3bcb7393e..b3b421da4 100644 --- a/apps/ops/api/job.py +++ b/apps/ops/api/job.py @@ -98,34 +98,44 @@ class JobViewSet(OrgBulkModelViewSet): transaction.on_commit( lambda: run_ops_job_execution.apply_async((str(execution.id),), task_id=str(execution.id))) + @staticmethod + def get_same_filenames(files): + filename_set = set() + same_filenames = [] + for file in files: + filename = file.name + if filename in filename_set: + same_filenames.append(filename) + filename_set.add(filename) + return same_filenames + @action(methods=[POST], detail=False, serializer_class=FileSerializer, permission_classes=[IsValidUser, ], url_path='upload') def upload(self, request, *args, **kwargs): + uploaded_files = request.FILES.getlist('files') serializer = self.get_serializer(data=request.data) + if not serializer.is_valid(): msg = 'Upload data invalid: {}'.format(serializer.errors) return Response({'msg': msg}, status=400) - uploaded_files = request.FILES.getlist('files') + + same_filenames = self.get_same_filenames(uploaded_files) + if same_filenames: + return Response({'msg': _("Duplicate file exists")}, status=400) + job_id = request.data.get('job_id', '') job = get_object_or_404(Job, pk=job_id) job_args = json.loads(job.args) src_path_info = [] - filename_set = set() - same_filenames = [] - upload_file_dir = safe_join(settings.DATA_DIR, 'job_upload_file') + upload_file_dir = safe_join(settings.DATA_DIR, 'job_upload_file', job_id) for uploaded_file in uploaded_files: filename = uploaded_file.name - saved_path = safe_join(upload_file_dir, f'{job_id}/{filename}') + saved_path = safe_join(upload_file_dir, f'{filename}') os.makedirs(os.path.dirname(saved_path), exist_ok=True) with open(saved_path, 'wb+') as destination: for chunk in uploaded_file.chunks(): destination.write(chunk) - if filename in filename_set: - same_filenames.append(filename) - filename_set.add(filename) src_path_info.append({'filename': filename, 'md5': get_file_md5(saved_path)}) - if same_filenames: - return Response({'msg': _("Duplicate file exists")}, status=400) job_args['src_path_info'] = src_path_info job.args = json.dumps(job_args) job.save() From 1eff33f3f7bac91e2ddb1c47eeeba0d0ebc0c614 Mon Sep 17 00:00:00 2001 From: wangruidong <940853815@qq.com> Date: Tue, 12 Dec 2023 10:32:42 +0800 Subject: [PATCH 049/111] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E8=8E=B7?= =?UTF-8?q?=E5=8F=96=E5=90=8C=E5=90=8D=E6=96=87=E4=BB=B6=E5=88=97=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/ops/api/job.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/apps/ops/api/job.py b/apps/ops/api/job.py index b3b421da4..41822b970 100644 --- a/apps/ops/api/job.py +++ b/apps/ops/api/job.py @@ -99,15 +99,15 @@ class JobViewSet(OrgBulkModelViewSet): lambda: run_ops_job_execution.apply_async((str(execution.id),), task_id=str(execution.id))) @staticmethod - def get_same_filenames(files): - filename_set = set() - same_filenames = [] + def get_duplicates_filenames(files): + seen = set() + duplicates = set() for file in files: - filename = file.name - if filename in filename_set: - same_filenames.append(filename) - filename_set.add(filename) - return same_filenames + if file in seen: + duplicates.add(file) + else: + seen.add(file) + return list(duplicates) @action(methods=[POST], detail=False, serializer_class=FileSerializer, permission_classes=[IsValidUser, ], url_path='upload') @@ -119,7 +119,7 @@ class JobViewSet(OrgBulkModelViewSet): msg = 'Upload data invalid: {}'.format(serializer.errors) return Response({'msg': msg}, status=400) - same_filenames = self.get_same_filenames(uploaded_files) + same_filenames = self.get_duplicates_filenames(uploaded_files) if same_filenames: return Response({'msg': _("Duplicate file exists")}, status=400) From 93da3e58f26c51ddb52ea49ae1eff93a08e93f61 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Tue, 12 Dec 2023 14:18:26 +0800 Subject: [PATCH 050/111] =?UTF-8?q?perf:=20=E3=80=90=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E7=B3=BB=E7=BB=9F=E4=BB=BB=E5=8A=A1=E3=80=91=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=20=E6=89=A7=E8=A1=8C=E5=91=A8=E6=9C=9F?= =?UTF-8?q?=E3=80=81=E4=B8=8B=E6=AC=A1=E5=BC=80=E5=A7=8B=E6=97=B6=E9=97=B4?= =?UTF-8?q?=20=E5=AD=97=E6=AE=B5=20(#12298)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/ops/api/celery.py | 45 ++++++++++++++++++++++++++++++++-- apps/ops/serializers/celery.py | 8 +++++- 2 files changed, 50 insertions(+), 3 deletions(-) diff --git a/apps/ops/api/celery.py b/apps/ops/api/celery.py index a7baaf6e4..797d00c5e 100644 --- a/apps/ops/api/celery.py +++ b/apps/ops/api/celery.py @@ -7,14 +7,15 @@ from celery.result import AsyncResult from django.shortcuts import get_object_or_404 from django.utils.translation import gettext as _ from django_celery_beat.models import PeriodicTask +from django_filters import rest_framework as drf_filters from rest_framework import generics, viewsets, mixins, status from rest_framework.response import Response -from django_filters import rest_framework as drf_filters from common.api import LogTailApi, CommonApiMixin +from common.drf.filters import BaseFilterSet from common.exceptions import JMSException from common.permissions import IsValidUser -from common.drf.filters import BaseFilterSet +from common.utils.timezone import local_now from ops.celery import app from ..ansible.utils import get_ansible_task_log_path from ..celery.utils import get_celery_task_log_path @@ -138,6 +139,46 @@ class CeleryTaskViewSet( def get_queryset(self): return CeleryTask.objects.exclude(name__startswith='celery') + @staticmethod + def extract_schedule(input_string): + pattern = r'(\S+ \S+ \S+ \S+ \S+).*' + match = re.match(pattern, input_string) + if match: + return match.group(1) + else: + return input_string + + def generate_execute_time(self, queryset): + names = [i.name for i in queryset] + periodic_tasks = PeriodicTask.objects.filter(name__in=names) + periodic_task_dict = {task.name: task for task in periodic_tasks} + now = local_now() + for i in queryset: + task = periodic_task_dict.get(i.name) + if not task: + continue + i.exec_cycle = self.extract_schedule(str(task.scheduler)) + + last_run_at = task.last_run_at or now + next_run_at = task.schedule.remaining_estimate(last_run_at) + if next_run_at.total_seconds() < 0: + next_run_at = task.schedule.remaining_estimate(now) + i.next_exec_time = now + next_run_at + return queryset + + def list(self, request, *args, **kwargs): + queryset = self.filter_queryset(self.get_queryset()) + + page = self.paginate_queryset(queryset) + if page is not None: + page = self.generate_execute_time(page) + serializer = self.get_serializer(page, many=True) + return self.get_paginated_response(serializer.data) + + queryset = self.generate_execute_time(queryset) + serializer = self.get_serializer(queryset, many=True) + return Response(serializer.data) + class CeleryTaskExecutionViewSet(CommonApiMixin, viewsets.ModelViewSet): serializer_class = CeleryTaskExecutionSerializer diff --git a/apps/ops/serializers/celery.py b/apps/ops/serializers/celery.py index 5166aa755..a4e9c7e8d 100644 --- a/apps/ops/serializers/celery.py +++ b/apps/ops/serializers/celery.py @@ -30,9 +30,15 @@ class CeleryPeriodTaskSerializer(serializers.ModelSerializer): class CeleryTaskSerializer(serializers.ModelSerializer): + exec_cycle = serializers.CharField(read_only=True) + next_exec_time = serializers.DateTimeField(format="%Y/%m/%d %H:%M:%S", read_only=True) + class Meta: model = CeleryTask - read_only_fields = ['id', 'name', 'meta', 'summary', 'state', 'date_last_publish'] + read_only_fields = [ + 'id', 'name', 'meta', 'summary', 'state', + 'date_last_publish', 'exec_cycle', 'next_exec_time' + ] fields = read_only_fields From 9260f26c99517cc5a10c9bf6219b5fc3732edc4d Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 12 Dec 2023 11:20:54 +0800 Subject: [PATCH 051/111] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=20db=20const?= =?UTF-8?q?rains?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/jumpserver/rewriting/db.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/jumpserver/rewriting/db.py b/apps/jumpserver/rewriting/db.py index ddb29e538..c14d6bf3d 100644 --- a/apps/jumpserver/rewriting/db.py +++ b/apps/jumpserver/rewriting/db.py @@ -26,7 +26,7 @@ class OneToOneField(models.OneToOneField, ForeignKey): def set_db_constraint(): if os.getenv('DB_CONSTRAINT', '1') != '0': return - if len(sys.argv) == 2 and sys.argv[1] in ['makemigrations', 'check']: + if len(sys.argv) >= 2 and sys.argv[1] in ['makemigrations', 'check']: return print("Set foreignkey db constraint False") transaction.atomic = atomic From 4f1826d3ed410d990eb497d5affebda3a41c7084 Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 12 Dec 2023 10:49:46 +0800 Subject: [PATCH 052/111] perf: get request ip, only using x-forwarded-for --- apps/common/utils/common.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/apps/common/utils/common.py b/apps/common/utils/common.py index add6b79b3..798b4a94c 100644 --- a/apps/common/utils/common.py +++ b/apps/common/utils/common.py @@ -155,10 +155,6 @@ def is_uuid(seq): def get_request_ip(request): - x_real_ip = request.META.get('HTTP_X_REAL_IP', '') - if x_real_ip: - return x_real_ip - x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR', '').split(',') if x_forwarded_for and x_forwarded_for[0]: login_ip = x_forwarded_for[0] From 3ec93b8f0471d62f81ca2529cc23fa3feb479b77 Mon Sep 17 00:00:00 2001 From: Eric Date: Tue, 12 Dec 2023 14:25:53 +0800 Subject: [PATCH 053/111] =?UTF-8?q?perf:=20=E6=B7=BB=E5=8A=A0=E5=BD=95?= =?UTF-8?q?=E5=83=8F=E4=B8=8D=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/terminal/const.py | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/terminal/const.py b/apps/terminal/const.py index 1c07f36b7..8ad37e93d 100644 --- a/apps/terminal/const.py +++ b/apps/terminal/const.py @@ -103,3 +103,4 @@ class SessionErrorReason(TextChoices): replay_create_failed = 'replay_create_failed', _('Replay create failed') replay_upload_failed = 'replay_upload_failed', _('Replay upload failed') replay_convert_failed = 'replay_convert_failed', _('Replay convert failed') + replay_unsupported = 'replay_unsupported', _('Replay unsupported') From 87a24991f1007aa4e52b3544eb765fe001d2bda9 Mon Sep 17 00:00:00 2001 From: feng <1304903146@qq.com> Date: Tue, 12 Dec 2023 15:34:13 +0800 Subject: [PATCH 054/111] =?UTF-8?q?perf:=20=E7=BF=BB=E8=AF=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/locale/ja/LC_MESSAGES/django.mo | 4 +-- apps/locale/ja/LC_MESSAGES/django.po | 44 ++++++++++++++++----------- apps/locale/zh/LC_MESSAGES/django.mo | 4 +-- apps/locale/zh/LC_MESSAGES/django.po | 45 +++++++++++++++++----------- apps/settings/serializers/feature.py | 2 +- 5 files changed, 59 insertions(+), 40 deletions(-) diff --git a/apps/locale/ja/LC_MESSAGES/django.mo b/apps/locale/ja/LC_MESSAGES/django.mo index 5e4c9d63f..5d7d6a0b8 100644 --- a/apps/locale/ja/LC_MESSAGES/django.mo +++ b/apps/locale/ja/LC_MESSAGES/django.mo @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a00c0d53df7fa88fc2fe69adda31fd9ab581b5a0362a01b8191924f74fab800d -size 167820 +oid sha256:7d9296340c0815fcda81bc7370051b1408225ba63ab7067b397595d4dfb334f7 +size 167881 diff --git a/apps/locale/ja/LC_MESSAGES/django.po b/apps/locale/ja/LC_MESSAGES/django.po index 8dad75a93..8b5d5fb62 100644 --- a/apps/locale/ja/LC_MESSAGES/django.po +++ b/apps/locale/ja/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-12-11 14:54+0800\n" +"POT-Creation-Date: 2023-12-12 15:26+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -606,7 +606,7 @@ msgstr "パスワードルール" #: perms/models/asset_permission.py:61 rbac/models/role.py:29 #: settings/models.py:32 settings/serializers/msg.py:82 #: terminal/models/applet/applet.py:33 terminal/models/component/endpoint.py:12 -#: terminal/models/component/endpoint.py:94 +#: terminal/models/component/endpoint.py:95 #: terminal/models/component/storage.py:26 terminal/models/component/task.py:13 #: terminal/models/component/terminal.py:84 #: terminal/models/virtualapp/provider.py:10 @@ -627,7 +627,7 @@ msgstr "特権アカウント" #: assets/models/label.py:22 #: authentication/serializers/connect_token_secret.py:117 #: terminal/models/applet/applet.py:40 -#: terminal/models/component/endpoint.py:105 +#: terminal/models/component/endpoint.py:106 #: terminal/models/virtualapp/virtualapp.py:23 users/serializers/user.py:167 msgid "Is active" msgstr "アクティブです。" @@ -924,8 +924,8 @@ msgstr "关联平台,可以配置推送参数,如果不关联,则使用默 #: ops/models/job.py:157 ops/models/playbook.py:32 rbac/models/role.py:37 #: settings/models.py:37 terminal/models/applet/applet.py:45 #: terminal/models/applet/applet.py:321 terminal/models/applet/host.py:143 -#: terminal/models/component/endpoint.py:24 -#: terminal/models/component/endpoint.py:104 +#: terminal/models/component/endpoint.py:25 +#: terminal/models/component/endpoint.py:105 #: terminal/models/session/session.py:46 #: terminal/models/virtualapp/virtualapp.py:28 tickets/models/comment.py:32 #: tickets/models/ticket/general.py:297 users/models/user.py:834 @@ -978,7 +978,7 @@ msgstr "自動タスク実行履歴" #: accounts/serializers/automations/change_secret.py:149 audits/const.py:61 #: audits/models.py:64 audits/signal_handlers/activity_log.py:33 -#: common/const/choices.py:18 ops/const.py:73 ops/serializers/celery.py:40 +#: common/const/choices.py:18 ops/const.py:73 ops/serializers/celery.py:46 #: terminal/const.py:78 terminal/models/session/sharing.py:121 #: tickets/views/approve.py:117 msgid "Success" @@ -1081,13 +1081,13 @@ msgid "Notifications" msgstr "通知" #: acls/models/base.py:37 assets/models/_user.py:51 -#: assets/models/cmd_filter.py:76 terminal/models/component/endpoint.py:97 +#: assets/models/cmd_filter.py:76 terminal/models/component/endpoint.py:98 #: xpack/plugins/cloud/models.py:275 msgid "Priority" msgstr "優先順位" #: acls/models/base.py:38 assets/models/_user.py:51 -#: assets/models/cmd_filter.py:76 terminal/models/component/endpoint.py:98 +#: assets/models/cmd_filter.py:76 terminal/models/component/endpoint.py:99 #: xpack/plugins/cloud/models.py:276 msgid "1-100, the lower the value will be match first" msgstr "1-100、低い値は最初に一致します" @@ -3060,7 +3060,7 @@ msgid "Please change your password" msgstr "パスワードを変更してください" #: authentication/models/access_key.py:22 -#: terminal/models/component/endpoint.py:95 +#: terminal/models/component/endpoint.py:96 msgid "IP group" msgstr "IP グループ" @@ -4039,19 +4039,19 @@ msgstr "Ansible 無効" msgid "Skip hosts below:" msgstr "次のホストをスキップします: " -#: ops/api/celery.py:64 ops/api/celery.py:79 +#: ops/api/celery.py:65 ops/api/celery.py:80 msgid "Waiting task start" msgstr "タスク開始待ち" -#: ops/api/celery.py:162 +#: ops/api/celery.py:203 msgid "Task {} not found" msgstr "タスクは存在しません" -#: ops/api/celery.py:167 +#: ops/api/celery.py:208 msgid "Task {} args or kwargs error" msgstr "タスク実行パラメータエラー" -#: ops/api/job.py:128 +#: ops/api/job.py:124 msgid "Duplicate file exists" msgstr "重複したファイルが存在する" @@ -5493,7 +5493,7 @@ msgstr "発表" msgid "Enable announcement" msgstr "アナウンスの有効化" -#: settings/serializers/feature.py:46 settings/serializers/feature.py:64 +#: settings/serializers/feature.py:46 msgid "Enable Vault" msgstr "有効化 Vault" @@ -5505,6 +5505,10 @@ msgstr "マウントポイント" msgid "Chat AI" msgstr "チャットAI" +#: settings/serializers/feature.py:64 +msgid "Enable Chat AI" +msgstr "チャットAIを起動する" + #: settings/serializers/feature.py:67 msgid "Base Url" msgstr "基本的なUrl" @@ -6622,15 +6626,21 @@ msgstr "PostgreSQL ポート" msgid "Redis port" msgstr "Redis ポート" -#: terminal/models/component/endpoint.py:29 -#: terminal/models/component/endpoint.py:102 +#: terminal/models/component/endpoint.py:23 +#, fuzzy +#| msgid "SQLServer" +msgid "SQLServer port" +msgstr "SQLServer" + +#: terminal/models/component/endpoint.py:30 +#: terminal/models/component/endpoint.py:103 #: terminal/serializers/endpoint.py:73 terminal/serializers/storage.py:41 #: terminal/serializers/storage.py:53 terminal/serializers/storage.py:83 #: terminal/serializers/storage.py:93 terminal/serializers/storage.py:101 msgid "Endpoint" msgstr "エンドポイント" -#: terminal/models/component/endpoint.py:108 +#: terminal/models/component/endpoint.py:109 msgid "Endpoint rule" msgstr "エンドポイントルール" diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index 16c60122c..8f6f29a95 100644 --- a/apps/locale/zh/LC_MESSAGES/django.mo +++ b/apps/locale/zh/LC_MESSAGES/django.mo @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d4a9a61bf1b247d3843001737ebfa6d5f8580f2d9c1ae0fc76649ecc535f5d96 -size 137563 +oid sha256:37662b3fc6df22480304d2a5f90cf156ba7d4e0249ec11076e5018daab545714 +size 137610 diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index 29b3e9c50..65b65073a 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: JumpServer 0.3.3\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-12-11 14:54+0800\n" +"POT-Creation-Date: 2023-12-12 15:24+0800\n" "PO-Revision-Date: 2021-05-20 10:54+0800\n" "Last-Translator: ibuler \n" "Language-Team: JumpServer team\n" @@ -605,7 +605,7 @@ msgstr "密码规则" #: perms/models/asset_permission.py:61 rbac/models/role.py:29 #: settings/models.py:32 settings/serializers/msg.py:82 #: terminal/models/applet/applet.py:33 terminal/models/component/endpoint.py:12 -#: terminal/models/component/endpoint.py:94 +#: terminal/models/component/endpoint.py:95 #: terminal/models/component/storage.py:26 terminal/models/component/task.py:13 #: terminal/models/component/terminal.py:84 #: terminal/models/virtualapp/provider.py:10 @@ -626,7 +626,7 @@ msgstr "特权账号" #: assets/models/label.py:22 #: authentication/serializers/connect_token_secret.py:117 #: terminal/models/applet/applet.py:40 -#: terminal/models/component/endpoint.py:105 +#: terminal/models/component/endpoint.py:106 #: terminal/models/virtualapp/virtualapp.py:23 users/serializers/user.py:167 msgid "Is active" msgstr "激活" @@ -922,8 +922,8 @@ msgstr "关联平台,可配置推送参数,如果不关联,将使用默认 #: ops/models/job.py:157 ops/models/playbook.py:32 rbac/models/role.py:37 #: settings/models.py:37 terminal/models/applet/applet.py:45 #: terminal/models/applet/applet.py:321 terminal/models/applet/host.py:143 -#: terminal/models/component/endpoint.py:24 -#: terminal/models/component/endpoint.py:104 +#: terminal/models/component/endpoint.py:25 +#: terminal/models/component/endpoint.py:105 #: terminal/models/session/session.py:46 #: terminal/models/virtualapp/virtualapp.py:28 tickets/models/comment.py:32 #: tickets/models/ticket/general.py:297 users/models/user.py:834 @@ -975,7 +975,7 @@ msgstr "自动化任务执行历史" #: accounts/serializers/automations/change_secret.py:149 audits/const.py:61 #: audits/models.py:64 audits/signal_handlers/activity_log.py:33 -#: common/const/choices.py:18 ops/const.py:73 ops/serializers/celery.py:40 +#: common/const/choices.py:18 ops/const.py:73 ops/serializers/celery.py:46 #: terminal/const.py:78 terminal/models/session/sharing.py:121 #: tickets/views/approve.py:117 msgid "Success" @@ -1078,13 +1078,13 @@ msgid "Notifications" msgstr "通知" #: acls/models/base.py:37 assets/models/_user.py:51 -#: assets/models/cmd_filter.py:76 terminal/models/component/endpoint.py:97 +#: assets/models/cmd_filter.py:76 terminal/models/component/endpoint.py:98 #: xpack/plugins/cloud/models.py:275 msgid "Priority" msgstr "优先级" #: acls/models/base.py:38 assets/models/_user.py:51 -#: assets/models/cmd_filter.py:76 terminal/models/component/endpoint.py:98 +#: assets/models/cmd_filter.py:76 terminal/models/component/endpoint.py:99 #: xpack/plugins/cloud/models.py:276 msgid "1-100, the lower the value will be match first" msgstr "优先级可选范围为 1-100 (数值越小越优先)" @@ -3029,7 +3029,7 @@ msgid "Please change your password" msgstr "请修改密码" #: authentication/models/access_key.py:22 -#: terminal/models/component/endpoint.py:95 +#: terminal/models/component/endpoint.py:96 msgid "IP group" msgstr "IPグループ" @@ -3991,19 +3991,19 @@ msgstr "Ansible 已禁用" msgid "Skip hosts below:" msgstr "跳过以下主机: " -#: ops/api/celery.py:64 ops/api/celery.py:79 +#: ops/api/celery.py:65 ops/api/celery.py:80 msgid "Waiting task start" msgstr "等待任务开始" -#: ops/api/celery.py:162 +#: ops/api/celery.py:203 msgid "Task {} not found" msgstr "任务 {} 不存在" -#: ops/api/celery.py:167 +#: ops/api/celery.py:208 msgid "Task {} args or kwargs error" msgstr "任务 {} 执行参数错误" -#: ops/api/job.py:128 +#: ops/api/job.py:124 msgid "Duplicate file exists" msgstr "存在同名文件" @@ -5435,7 +5435,7 @@ msgstr "公告" msgid "Enable announcement" msgstr "启用公告" -#: settings/serializers/feature.py:46 settings/serializers/feature.py:64 +#: settings/serializers/feature.py:46 msgid "Enable Vault" msgstr "启用 Vault" @@ -5447,6 +5447,10 @@ msgstr "挂在点" msgid "Chat AI" msgstr "聊天 AI" +#: settings/serializers/feature.py:64 +msgid "Enable Chat AI" +msgstr "启动聊天 AI" + #: settings/serializers/feature.py:67 msgid "Base Url" msgstr "基本地址" @@ -6530,15 +6534,21 @@ msgstr "PostgreSQL 端口" msgid "Redis port" msgstr "Redis 端口" -#: terminal/models/component/endpoint.py:29 -#: terminal/models/component/endpoint.py:102 +#: terminal/models/component/endpoint.py:23 +#, fuzzy +#| msgid "SQLServer" +msgid "SQLServer port" +msgstr "SQLServer" + +#: terminal/models/component/endpoint.py:30 +#: terminal/models/component/endpoint.py:103 #: terminal/serializers/endpoint.py:73 terminal/serializers/storage.py:41 #: terminal/serializers/storage.py:53 terminal/serializers/storage.py:83 #: terminal/serializers/storage.py:93 terminal/serializers/storage.py:101 msgid "Endpoint" msgstr "端点" -#: terminal/models/component/endpoint.py:108 +#: terminal/models/component/endpoint.py:109 msgid "Endpoint rule" msgstr "端点规则" @@ -8796,7 +8806,6 @@ msgstr "企业专业版" msgid "Ultimate edition" msgstr "企业旗舰版" - #~ msgid "FeiShu query user failed" #~ msgstr "飞书查询用户失败" diff --git a/apps/settings/serializers/feature.py b/apps/settings/serializers/feature.py index c301585ff..1235b29b7 100644 --- a/apps/settings/serializers/feature.py +++ b/apps/settings/serializers/feature.py @@ -61,7 +61,7 @@ class ChatAISettingSerializer(serializers.Serializer): GPT_MODEL_CHOICES = [] CHAT_AI_ENABLED = serializers.BooleanField( - required=False, label=_('Enable Vault') + required=False, label=_('Enable Chat AI') ) GPT_BASE_URL = serializers.CharField( max_length=256, allow_blank=True, required=False, label=_('Base Url') From 88010034614ac263f46d5171f0999d9d5053cee0 Mon Sep 17 00:00:00 2001 From: feng <1304903146@qq.com> Date: Tue, 12 Dec 2023 17:12:45 +0800 Subject: [PATCH 055/111] =?UTF-8?q?perf:=20=E6=94=AF=E6=8C=81=20=E8=A5=BF?= =?UTF-8?q?=E7=8F=AD=E7=89=99=20Keyboard=20Layout?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/users/const.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/users/const.py b/apps/users/const.py index a2ddb4ce9..6b6fd5fa8 100644 --- a/apps/users/const.py +++ b/apps/users/const.py @@ -46,6 +46,8 @@ class KeyboardLayout(TextChoices): FR_CH_QWERTZ = 'fr-ch-qwertz', 'Swiss French (Qwertz)' FR_BE_AZERTY = 'fr-be-azerty', 'Belgian French (Azerty)' TR_TR_QWERTY = 'tr-tr-qwerty', 'Turkish-Q (Qwerty)' + ES_ES_QWERTY = 'es-es-qwerty', 'Spanish' + ES_LATAM_QWERTY = 'es-latam-qwerty', 'Spanish (Latin American)' class AppletConnectionMethod(TextChoices): From 01fcdad489fb6d2dc3e45dbef9d9ef58a4dcc3eb Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 12 Dec 2023 15:28:24 +0800 Subject: [PATCH 056/111] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E4=B8=8D=E6=B4=BB=E8=B7=83=E6=A3=80=E6=B5=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/jumpserver/conf.py | 2 +- apps/users/tasks.py | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/jumpserver/conf.py b/apps/jumpserver/conf.py index a0ecff1d1..c3622b61c 100644 --- a/apps/jumpserver/conf.py +++ b/apps/jumpserver/conf.py @@ -503,7 +503,7 @@ class Config(dict): 'SECURITY_LUNA_REMEMBER_AUTH': True, 'SECURITY_WATERMARK_ENABLED': True, 'SECURITY_MFA_VERIFY_TTL': 3600, - 'SECURITY_UNCOMMON_USERS_TTL': 90, + 'SECURITY_UNCOMMON_USERS_TTL': 999, 'VERIFY_CODE_TTL': 60, 'SECURITY_SESSION_SHARE': True, 'SECURITY_CHECK_DIFFERENT_CITY_LOGIN': True, diff --git a/apps/users/tasks.py b/apps/users/tasks.py index 4c7887c0f..d716806b2 100644 --- a/apps/users/tasks.py +++ b/apps/users/tasks.py @@ -86,6 +86,13 @@ def check_user_expired_periodic(): @tmp_to_root_org() def check_unused_users(): uncommon_users_ttl = settings.SECURITY_UNCOMMON_USERS_TTL + if not uncommon_users_ttl or not uncommon_users_ttl.isdigit(): + return + + uncommon_users_ttl = int(uncommon_users_ttl) + if uncommon_users_ttl <= 0 or uncommon_users_ttl >= 999: + return + seconds_to_subtract = uncommon_users_ttl * 24 * 60 * 60 t = timezone.now() - timedelta(seconds=seconds_to_subtract) last_login_q = Q(last_login__lte=t) | (Q(last_login__isnull=True) & Q(date_joined__lte=t)) From 2c7ad905246f5972047cd6166f23d9336ed8094a Mon Sep 17 00:00:00 2001 From: feng <1304903146@qq.com> Date: Tue, 12 Dec 2023 19:14:13 +0800 Subject: [PATCH 057/111] =?UTF-8?q?fix:=20chat=20ai=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E5=8F=AF=E8=BF=9E=E6=8E=A5=E6=80=A7=E6=97=B6=E5=A4=B1=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/settings/api/chat.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/settings/api/chat.py b/apps/settings/api/chat.py index 50c93639e..a2601fbad 100644 --- a/apps/settings/api/chat.py +++ b/apps/settings/api/chat.py @@ -18,7 +18,8 @@ class ChatAITestingAPI(GenericAPIView): def get_config(self, request): serializer = self.serializer_class(data=request.data) serializer.is_valid(raise_exception=True) - data = serializer.validated_data + data = self.serializer_class().data + data.update(serializer.validated_data) for k, v in data.items(): if v: continue From 5b51a8231c22b21cea7be001440c6212c188c04a Mon Sep 17 00:00:00 2001 From: wangruidong <940853815@qq.com> Date: Wed, 13 Dec 2023 10:31:36 +0800 Subject: [PATCH 058/111] =?UTF-8?q?fix:=20=E7=82=B9=E5=87=BB=E5=A4=87?= =?UTF-8?q?=E6=A1=88=E5=8F=B7=E6=9C=AA=E8=B7=B3=E8=BD=AC=E5=88=B0=E6=8C=87?= =?UTF-8?q?=E5=AE=9A=E9=93=BE=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/templates/_foot_js.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/templates/_foot_js.html b/apps/templates/_foot_js.html index a27bbf672..f91d20f41 100644 --- a/apps/templates/_foot_js.html +++ b/apps/templates/_foot_js.html @@ -42,7 +42,7 @@ From c4fef5899c74212040e26d42a16fbc0aca2026b6 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Wed, 13 Dec 2023 15:51:22 +0800 Subject: [PATCH 059/111] =?UTF-8?q?perf:=20=E8=BF=9E=E6=8E=A5=20RDP=20?= =?UTF-8?q?=E5=8D=8F=E8=AE=AE=E4=BC=9A=E8=AF=9D=E6=97=B6=EF=BC=8C=E9=AB=98?= =?UTF-8?q?=E7=BA=A7=E9=80=89=E9=A1=B9=E6=94=AF=E6=8C=81=20session=20bpp:i?= =?UTF-8?q?=20=20=E5=8F=82=E6=95=B0=E9=85=8D=E7=BD=AE=EF=BC=9B=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=2032=EF=BC=9B=20(#12319)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/authentication/api/connection_token.py | 10 ++++++---- apps/users/const.py | 5 +++++ apps/users/serializers/preference/luna.py | 6 +++++- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/apps/authentication/api/connection_token.py b/apps/authentication/api/connection_token.py index 2158a25f1..bc79d26a2 100644 --- a/apps/authentication/api/connection_token.py +++ b/apps/authentication/api/connection_token.py @@ -27,7 +27,7 @@ from perms.models import ActionChoices from terminal.connect_methods import NativeClient, ConnectMethodUtil from terminal.models import EndpointRule, Endpoint from users.const import FileNameConflictResolution -from users.const import RDPSmartSize +from users.const import RDPSmartSize, RDPColorQuality from users.models import Preference from ..models import ConnectionToken, date_expired_default from ..serializers import ( @@ -49,7 +49,6 @@ class RDPFileClientProtocolURLMixin: 'full address:s': '', 'username:s': '', 'use multimon:i': '0', - 'session bpp:i': '32', 'audiomode:i': '0', 'disable wallpaper:i': '0', 'disable full window drag:i': '0', @@ -100,10 +99,13 @@ class RDPFileClientProtocolURLMixin: rdp_options['winposstr:s'] = f'0,1,0,0,{width},{height}' rdp_options['dynamic resolution:i'] = '0' + color_quality = self.request.query_params.get('rdp_color_quality') + color_quality = color_quality if color_quality else os.getenv('JUMPSERVER_COLOR_DEPTH', RDPColorQuality.HIGH) + # 设置其他选项 - rdp_options['smart sizing:i'] = self.request.query_params.get('rdp_smart_size', RDPSmartSize.DISABLE) - rdp_options['session bpp:i'] = os.getenv('JUMPSERVER_COLOR_DEPTH', '32') + rdp_options['session bpp:i'] = color_quality rdp_options['audiomode:i'] = self.parse_env_bool('JUMPSERVER_DISABLE_AUDIO', 'false', '2', '0') + rdp_options['smart sizing:i'] = self.request.query_params.get('rdp_smart_size', RDPSmartSize.DISABLE) # 设置远程应用, 不是 Mstsc if token.connect_method != NativeClient.mstsc: diff --git a/apps/users/const.py b/apps/users/const.py index 6b6fd5fa8..38edeaf2d 100644 --- a/apps/users/const.py +++ b/apps/users/const.py @@ -38,6 +38,11 @@ class RDPSmartSize(TextChoices): ENABLE = '1', _('Enable') +class RDPColorQuality(TextChoices): + HIGH = '32', 'High(32 bit)' + MEDIUM = '16', 'Medium(16 bit)' + + class KeyboardLayout(TextChoices): EN_US_QWERTY = 'en-us-qwerty', 'US English (Qwerty)' EN_UK_QWERTY = 'en-gb-qwerty', 'UK English (Qwerty)' diff --git a/apps/users/serializers/preference/luna.py b/apps/users/serializers/preference/luna.py index 2df89f7bc..b89bf4b36 100644 --- a/apps/users/serializers/preference/luna.py +++ b/apps/users/serializers/preference/luna.py @@ -5,7 +5,7 @@ from rest_framework import serializers from users.const import ( RDPResolution, RDPSmartSize, KeyboardLayout, - RDPClientOption, AppletConnectionMethod + RDPClientOption, AppletConnectionMethod, RDPColorQuality, ) @@ -40,6 +40,10 @@ class GraphicsSerializer(serializers.Serializer): choices=RDPClientOption.choices, default={RDPClientOption.FULL_SCREEN}, label=_('RDP client option'), required=False ) + rdp_color_quality = serializers.ChoiceField( + choices=RDPColorQuality.choices, default=RDPColorQuality.HIGH, + label=_('RDP color quality'), required=False + ) rdp_smart_size = serializers.ChoiceField( RDPSmartSize.choices, default=RDPSmartSize.DISABLE, required=False, label=_('Rdp smart size'), From 4f7b4842f625f1a28e05189c32cb9d7afafcd4e8 Mon Sep 17 00:00:00 2001 From: feng <1304903146@qq.com> Date: Wed, 13 Dec 2023 16:15:34 +0800 Subject: [PATCH 060/111] =?UTF-8?q?perf:=20=E7=BF=BB=E8=AF=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/accounts/notifications.py | 6 +-- apps/locale/ja/LC_MESSAGES/django.po | 72 +++++++++++++++------------- apps/locale/zh/LC_MESSAGES/django.po | 68 ++++++++++++++------------ 3 files changed, 78 insertions(+), 68 deletions(-) diff --git a/apps/accounts/notifications.py b/apps/accounts/notifications.py index b650410d1..404125fd3 100644 --- a/apps/accounts/notifications.py +++ b/apps/accounts/notifications.py @@ -3,8 +3,8 @@ from django.utils.translation import gettext_lazy as _ from common.tasks import send_mail_attachment_async, upload_backup_to_obj_storage from notifications.notifications import UserMessage -from users.models import User from terminal.models.component.storage import ReplayStorage +from users.models import User class AccountBackupExecutionTaskMsg(object): @@ -23,8 +23,8 @@ class AccountBackupExecutionTaskMsg(object): else: return _("{} - The account backup passage task has been completed: " "the encryption password has not been set - " - "please go to personal information -> file encryption password " - "to set the encryption password").format(name) + "please go to personal information -> Basic file encryption password for preference settings" + ).format(name) def publish(self, attachment_list=None): send_mail_attachment_async( diff --git a/apps/locale/ja/LC_MESSAGES/django.po b/apps/locale/ja/LC_MESSAGES/django.po index 8b5d5fb62..833c315b5 100644 --- a/apps/locale/ja/LC_MESSAGES/django.po +++ b/apps/locale/ja/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-12-12 15:26+0800\n" +"POT-Creation-Date: 2023-12-13 16:13+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -289,7 +289,7 @@ msgstr "ソース ID" #: accounts/serializers/automations/change_secret.py:127 #: acls/serializers/base.py:124 acls/templates/acls/asset_login_reminder.html:7 #: assets/serializers/asset/common.py:125 assets/serializers/gateway.py:28 -#: audits/models.py:59 authentication/api/connection_token.py:403 +#: audits/models.py:59 authentication/api/connection_token.py:405 #: ops/models/base.py:18 perms/models/asset_permission.py:75 #: perms/serializers/permission.py:40 terminal/backends/command/models.py:18 #: terminal/models/session/session.py:33 @@ -700,12 +700,12 @@ msgstr "" #: accounts/notifications.py:24 msgid "" "{} - The account backup passage task has been completed: the encryption " -"password has not been set - please go to personal information -> file " -"encryption password to set the encryption password" +"password has not been set - please go to personal information -> Basic file " +"encryption password for preference settings" msgstr "" "{} -アカウントのバックアップ通過タスクが完了しました: 暗号化パスワードが設定" -"されていません-個人情報にアクセスしてください-> ファイル暗号化パスワードを設" -"定してください暗号化パスワード" +"されていません-個人情報にアクセスしてください-> プリファレンス設定の基本的な" +"ファイル暗号化パスワードの設定" #: accounts/notifications.py:55 msgid "Notification of implementation result of encryption change plan" @@ -791,7 +791,7 @@ msgstr "編集済み" #: acls/templates/acls/asset_login_reminder.html:6 #: assets/models/automations/base.py:19 #: assets/serializers/automations/base.py:20 -#: authentication/api/connection_token.py:402 ops/models/base.py:17 +#: authentication/api/connection_token.py:404 ops/models/base.py:17 #: ops/models/job.py:151 ops/serializers/job.py:20 #: terminal/templates/terminal/_msg_command_execute_alert.html:16 msgid "Assets" @@ -1404,7 +1404,7 @@ msgstr "無効" #: assets/const/base.py:33 settings/serializers/basic.py:6 #: users/serializers/preference/koko.py:19 #: users/serializers/preference/lina.py:39 -#: users/serializers/preference/luna.py:69 +#: users/serializers/preference/luna.py:73 msgid "Basic" msgstr "基本" @@ -1431,7 +1431,7 @@ msgstr "クラウド サービス" #: assets/const/category.py:14 assets/models/asset/gpt.py:11 #: assets/models/asset/web.py:16 audits/const.py:42 -#: terminal/models/applet/applet.py:27 users/const.py:52 +#: terminal/models/applet/applet.py:27 users/const.py:59 msgid "Web" msgstr "Web" @@ -2652,29 +2652,29 @@ msgstr "パラメータの値には必ず %s が含まれます" msgid "This action require verify your MFA" msgstr "この操作には、MFAを検証する必要があります" -#: authentication/api/connection_token.py:258 +#: authentication/api/connection_token.py:260 msgid "Reusable connection token is not allowed, global setting not enabled" msgstr "" "再使用可能な接続トークンの使用は許可されていません。グローバル設定は有効に" "なっていません" -#: authentication/api/connection_token.py:372 +#: authentication/api/connection_token.py:374 msgid "Anonymous account is not supported for this asset" msgstr "匿名アカウントはこのプロパティではサポートされていません" -#: authentication/api/connection_token.py:391 +#: authentication/api/connection_token.py:393 msgid "Account not found" msgstr "アカウントが見つかりません" -#: authentication/api/connection_token.py:394 +#: authentication/api/connection_token.py:396 msgid "Permission expired" msgstr "承認の有効期限が切れています" -#: authentication/api/connection_token.py:424 +#: authentication/api/connection_token.py:426 msgid "ACL action is reject: {}({})" msgstr "ACL アクションは拒否です: {}({})" -#: authentication/api/connection_token.py:428 +#: authentication/api/connection_token.py:430 msgid "ACL action is review" msgstr "ACL アクションはレビューです" @@ -4128,10 +4128,8 @@ msgid "Playbook" msgstr "Playbook" #: ops/const.py:40 -#, fuzzy -#| msgid "Upload" msgid "Upload File" -msgstr "アップロード" +msgstr "ファイルのアップロード" #: ops/const.py:44 msgid "Privileged Only" @@ -4825,11 +4823,11 @@ msgstr "共通設定" msgid "View permission tree" msgstr "権限ツリーの表示" -#: settings/api/chat.py:35 +#: settings/api/chat.py:36 msgid "Chat AI is not enabled" msgstr "チャットAIがオンになっていない" -#: settings/api/chat.py:73 settings/api/dingtalk.py:31 +#: settings/api/chat.py:74 settings/api/dingtalk.py:31 #: settings/api/feishu.py:36 settings/api/slack.py:34 settings/api/sms.py:160 #: settings/api/vault.py:40 settings/api/wecom.py:37 msgid "Test success" @@ -6280,7 +6278,7 @@ msgid "Home page" msgstr "ホームページ" #: templates/resource_download.html:18 templates/resource_download.html:33 -#: users/const.py:53 +#: users/const.py:60 msgid "Client" msgstr "クライアント" @@ -6502,6 +6500,10 @@ msgstr "動画のアップロードに失敗しました" msgid "Replay convert failed" msgstr "ビデオのトランスコーディングに失敗しました" +#: terminal/const.py:106 +msgid "Replay unsupported" +msgstr "録画はサポートされていません" + #: terminal/exceptions.py:8 msgid "Bulk create not support" msgstr "一括作成非サポート" @@ -6627,10 +6629,8 @@ msgid "Redis port" msgstr "Redis ポート" #: terminal/models/component/endpoint.py:23 -#, fuzzy -#| msgid "SQLServer" msgid "SQLServer port" -msgstr "SQLServer" +msgstr "SQLServer ポート" #: terminal/models/component/endpoint.py:30 #: terminal/models/component/endpoint.py:103 @@ -7617,11 +7617,11 @@ msgstr "マルチスクリーンディスプレイ" msgid "Drives redirect" msgstr "ディスクマウント" -#: users/const.py:57 +#: users/const.py:64 msgid "Replace" msgstr "置換" -#: users/const.py:58 +#: users/const.py:65 msgid "Suffix" msgstr "接尾辞を付ける" @@ -7856,10 +7856,14 @@ msgid "RDP client option" msgstr "RDPクライアントオプション" #: users/serializers/preference/luna.py:45 +msgid "RDP color quality" +msgstr "" + +#: users/serializers/preference/luna.py:49 msgid "Rdp smart size" msgstr "RDPインテリジェントサイズ" -#: users/serializers/preference/luna.py:46 +#: users/serializers/preference/luna.py:50 msgid "" "Determines whether the client computer should scale the content on the " "remote computer to fit the window size of the client computer when the " @@ -7874,27 +7878,27 @@ msgstr "" # "ウィンドウサイズを変更するときにクライアントコンピュータがクライアントコン" # "ピュータのウィンドウサイズに合わせるためにリモートコンピュータ上のコンテンツ" # "をスケーリングすべきかどうかを判断する" -#: users/serializers/preference/luna.py:51 +#: users/serializers/preference/luna.py:55 msgid "Remote application connection method" msgstr "リモートアプリケーション接続方式" -#: users/serializers/preference/luna.py:58 +#: users/serializers/preference/luna.py:62 msgid "Character terminal font size" msgstr "文字終端フォントサイズ" -#: users/serializers/preference/luna.py:61 +#: users/serializers/preference/luna.py:65 msgid "Backspace as Ctrl+H" msgstr "文字終端Backspace As Ctrl+H" -#: users/serializers/preference/luna.py:64 +#: users/serializers/preference/luna.py:68 msgid "Right click quickly paste" msgstr "右クリックでクイック貼り付け" -#: users/serializers/preference/luna.py:70 +#: users/serializers/preference/luna.py:74 msgid "Graphics" msgstr "図形化" -#: users/serializers/preference/luna.py:71 +#: users/serializers/preference/luna.py:75 msgid "Command line" msgstr "コマンドライン" @@ -7995,7 +7999,7 @@ msgstr "ユーザーの有効期限の定期的な検出" msgid "Check unused users" msgstr "未使用のユーザーのチェック" -#: users/tasks.py:115 +#: users/tasks.py:122 msgid "The user has not logged in recently and has been disabled." msgstr "ユーザーは最近ログインしておらず、無効になっています。" diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index 65b65073a..068613cd6 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: JumpServer 0.3.3\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-12-12 15:24+0800\n" +"POT-Creation-Date: 2023-12-13 16:13+0800\n" "PO-Revision-Date: 2021-05-20 10:54+0800\n" "Last-Translator: ibuler \n" "Language-Team: JumpServer team\n" @@ -288,7 +288,7 @@ msgstr "来源 ID" #: accounts/serializers/automations/change_secret.py:127 #: acls/serializers/base.py:124 acls/templates/acls/asset_login_reminder.html:7 #: assets/serializers/asset/common.py:125 assets/serializers/gateway.py:28 -#: audits/models.py:59 authentication/api/connection_token.py:403 +#: audits/models.py:59 authentication/api/connection_token.py:405 #: ops/models/base.py:18 perms/models/asset_permission.py:75 #: perms/serializers/permission.py:40 terminal/backends/command/models.py:18 #: terminal/models/session/session.py:33 @@ -699,11 +699,11 @@ msgstr "{} - 账号备份任务已完成, 详情见附件" #: accounts/notifications.py:24 msgid "" "{} - The account backup passage task has been completed: the encryption " -"password has not been set - please go to personal information -> file " -"encryption password to set the encryption password" +"password has not been set - please go to personal information -> Basic file " +"encryption password for preference settings" msgstr "" -"{} - 账号备份任务已完成: 未设置加密密码 - 请前往个人信息 -> 文件加密密码中设" -"置加密密码" +"{} - 账号备份任务已完成: 未设置加密密码 - 请前往个人信息 -> 偏好设置的基本中" +"设置文件加密密码" #: accounts/notifications.py:55 msgid "Notification of implementation result of encryption change plan" @@ -789,7 +789,7 @@ msgstr "已修改" #: acls/templates/acls/asset_login_reminder.html:6 #: assets/models/automations/base.py:19 #: assets/serializers/automations/base.py:20 -#: authentication/api/connection_token.py:402 ops/models/base.py:17 +#: authentication/api/connection_token.py:404 ops/models/base.py:17 #: ops/models/job.py:151 ops/serializers/job.py:20 #: terminal/templates/terminal/_msg_command_execute_alert.html:16 msgid "Assets" @@ -1397,7 +1397,7 @@ msgstr "禁用" #: assets/const/base.py:33 settings/serializers/basic.py:6 #: users/serializers/preference/koko.py:19 #: users/serializers/preference/lina.py:39 -#: users/serializers/preference/luna.py:69 +#: users/serializers/preference/luna.py:73 msgid "Basic" msgstr "基本" @@ -1424,7 +1424,7 @@ msgstr "云服务" #: assets/const/category.py:14 assets/models/asset/gpt.py:11 #: assets/models/asset/web.py:16 audits/const.py:42 -#: terminal/models/applet/applet.py:27 users/const.py:52 +#: terminal/models/applet/applet.py:27 users/const.py:59 msgid "Web" msgstr "Web" @@ -2636,27 +2636,27 @@ msgstr "参数中的值必须包含 %s" msgid "This action require verify your MFA" msgstr "该操作需要验证您的 MFA, 请先开启并配置" -#: authentication/api/connection_token.py:258 +#: authentication/api/connection_token.py:260 msgid "Reusable connection token is not allowed, global setting not enabled" msgstr "不允许使用可重复使用的连接令牌,未启用全局设置" -#: authentication/api/connection_token.py:372 +#: authentication/api/connection_token.py:374 msgid "Anonymous account is not supported for this asset" msgstr "匿名账号不支持当前资产" -#: authentication/api/connection_token.py:391 +#: authentication/api/connection_token.py:393 msgid "Account not found" msgstr "账号未找到" -#: authentication/api/connection_token.py:394 +#: authentication/api/connection_token.py:396 msgid "Permission expired" msgstr "授权已过期" -#: authentication/api/connection_token.py:424 +#: authentication/api/connection_token.py:426 msgid "ACL action is reject: {}({})" msgstr "ACL 动作是拒绝: {}({})" -#: authentication/api/connection_token.py:428 +#: authentication/api/connection_token.py:430 msgid "ACL action is review" msgstr "ACL 动作是复核" @@ -4773,11 +4773,11 @@ msgstr "一般设置" msgid "View permission tree" msgstr "查看授权树" -#: settings/api/chat.py:35 +#: settings/api/chat.py:36 msgid "Chat AI is not enabled" msgstr "聊天 AI 没有开启" -#: settings/api/chat.py:73 settings/api/dingtalk.py:31 +#: settings/api/chat.py:74 settings/api/dingtalk.py:31 #: settings/api/feishu.py:36 settings/api/slack.py:34 settings/api/sms.py:160 #: settings/api/vault.py:40 settings/api/wecom.py:37 msgid "Test success" @@ -6193,7 +6193,7 @@ msgid "Home page" msgstr "首页" #: templates/resource_download.html:18 templates/resource_download.html:33 -#: users/const.py:53 +#: users/const.py:60 msgid "Client" msgstr "客户端" @@ -6410,6 +6410,10 @@ msgstr "录像上传失败" msgid "Replay convert failed" msgstr "录像转码失败" +#: terminal/const.py:106 +msgid "Replay unsupported" +msgstr "不支持录像" + #: terminal/exceptions.py:8 msgid "Bulk create not support" msgstr "不支持批量创建" @@ -6535,10 +6539,8 @@ msgid "Redis port" msgstr "Redis 端口" #: terminal/models/component/endpoint.py:23 -#, fuzzy -#| msgid "SQLServer" msgid "SQLServer port" -msgstr "SQLServer" +msgstr "SQLServer 端口" #: terminal/models/component/endpoint.py:30 #: terminal/models/component/endpoint.py:103 @@ -7512,11 +7514,11 @@ msgstr "多屏显示" msgid "Drives redirect" msgstr "磁盘挂载" -#: users/const.py:57 +#: users/const.py:64 msgid "Replace" msgstr "替换" -#: users/const.py:58 +#: users/const.py:65 msgid "Suffix" msgstr "加后缀" @@ -7751,12 +7753,16 @@ msgid "RDP client option" msgstr "RDP 客户端选项" #: users/serializers/preference/luna.py:45 +msgid "RDP color quality" +msgstr "" + +#: users/serializers/preference/luna.py:49 msgid "Rdp smart size" msgstr "" # msgid "Rdp smart size" # msgstr "RDP 智能大小" -#: users/serializers/preference/luna.py:46 +#: users/serializers/preference/luna.py:50 msgid "" "Determines whether the client computer should scale the content on the " "remote computer to fit the window size of the client computer when the " @@ -7765,27 +7771,27 @@ msgstr "" "确定调整窗口大小时客户端计算机是否应缩放远程计算机上的内容以适应客户端计算机" "的窗口大小" -#: users/serializers/preference/luna.py:51 +#: users/serializers/preference/luna.py:55 msgid "Remote application connection method" msgstr "远程应用连接方式" -#: users/serializers/preference/luna.py:58 +#: users/serializers/preference/luna.py:62 msgid "Character terminal font size" msgstr "字符终端字体大小" -#: users/serializers/preference/luna.py:61 +#: users/serializers/preference/luna.py:65 msgid "Backspace as Ctrl+H" msgstr "字符终端Backspace As Ctrl+H" -#: users/serializers/preference/luna.py:64 +#: users/serializers/preference/luna.py:68 msgid "Right click quickly paste" msgstr "右键快速粘贴" -#: users/serializers/preference/luna.py:70 +#: users/serializers/preference/luna.py:74 msgid "Graphics" msgstr "图形化" -#: users/serializers/preference/luna.py:71 +#: users/serializers/preference/luna.py:75 msgid "Command line" msgstr "命令行" @@ -7885,7 +7891,7 @@ msgstr "周期检测用户过期" msgid "Check unused users" msgstr "检查未使用的用户" -#: users/tasks.py:115 +#: users/tasks.py:122 msgid "The user has not logged in recently and has been disabled." msgstr "该用户最近未登录,已被禁用。" From 0c96af32c2a2b0a434a81262a84bf6d678df3074 Mon Sep 17 00:00:00 2001 From: feng <1304903146@qq.com> Date: Wed, 13 Dec 2023 16:33:50 +0800 Subject: [PATCH 061/111] =?UTF-8?q?fix:=20=E6=89=8B=E6=9C=BA=E8=B4=A6?= =?UTF-8?q?=E5=8F=B7=E8=BF=87=E6=BB=A4asset=5Fid=20=E5=A4=B1=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/accounts/filters.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/accounts/filters.py b/apps/accounts/filters.py index e71a5b9fb..5e4e0c257 100644 --- a/apps/accounts/filters.py +++ b/apps/accounts/filters.py @@ -51,6 +51,7 @@ class AccountFilterSet(BaseFilterSet): class GatheredAccountFilterSet(BaseFilterSet): node_id = drf_filters.CharFilter(method='filter_nodes') + asset_id = drf_filters.CharFilter(field_name='asset_id', lookup_expr='exact') @staticmethod def filter_nodes(queryset, name, value): @@ -58,4 +59,4 @@ class GatheredAccountFilterSet(BaseFilterSet): class Meta: model = GatheredAccount - fields = ['id', 'asset_id', 'username'] + fields = ['id', 'username'] From d07db6842673096e95e3580f5af02dc5c6db9e6a Mon Sep 17 00:00:00 2001 From: feng <1304903146@qq.com> Date: Wed, 13 Dec 2023 17:28:57 +0800 Subject: [PATCH 062/111] =?UTF-8?q?perf:=20=E4=BF=AE=E6=94=B9=E4=B8=8B?= =?UTF-8?q?=E7=BA=BF=E7=94=A8=E6=88=B7=E4=BC=9A=E8=AF=9D=E7=9A=84=E6=9D=83?= =?UTF-8?q?=E9=99=90=E4=BD=8Dlabel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/rbac/migrations/0014_auto_20231208_1548.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/rbac/migrations/0014_auto_20231208_1548.py b/apps/rbac/migrations/0014_auto_20231208_1548.py index 33581c5a0..5fb8cefb7 100644 --- a/apps/rbac/migrations/0014_auto_20231208_1548.py +++ b/apps/rbac/migrations/0014_auto_20231208_1548.py @@ -3,9 +3,9 @@ from django.db import migrations -def migrate_remove_offline_ussrsession_permission(apps, *args): +def migrate_update_offline_usersession_permission_name(apps, *args): perm_model = apps.get_model('auth', 'Permission') - perm_model.objects.filter(codename='offline_ussrsession').delete() + perm_model.objects.filter(codename='offline_usersession').update(name='Offline user session') class Migration(migrations.Migration): @@ -14,5 +14,5 @@ class Migration(migrations.Migration): ] operations = [ - migrations.RunPython(migrate_remove_offline_ussrsession_permission) + migrations.RunPython(migrate_update_offline_usersession_permission_name) ] From 27daebbe1b1df7e7d30d04f1c60069d0969bcc70 Mon Sep 17 00:00:00 2001 From: wangruidong <940853815@qq.com> Date: Tue, 12 Dec 2023 19:15:47 +0800 Subject: [PATCH 063/111] =?UTF-8?q?perf:=20=E4=B8=8A=E4=BC=A0=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E5=A4=A7=E5=B0=8F=E9=99=90=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/jumpserver/conf.py | 2 ++ apps/jumpserver/settings/custom.py | 2 ++ apps/locale/ja/LC_MESSAGES/django.mo | 4 ++-- apps/locale/ja/LC_MESSAGES/django.po | 10 ++++++++-- apps/locale/zh/LC_MESSAGES/django.mo | 4 ++-- apps/locale/zh/LC_MESSAGES/django.po | 10 ++++++++-- apps/ops/api/job.py | 25 ++++++++++++++++++++----- 7 files changed, 44 insertions(+), 13 deletions(-) diff --git a/apps/jumpserver/conf.py b/apps/jumpserver/conf.py index c3622b61c..80790eb2e 100644 --- a/apps/jumpserver/conf.py +++ b/apps/jumpserver/conf.py @@ -599,6 +599,8 @@ class Config(dict): 'GPT_PROXY': '', 'GPT_MODEL': 'gpt-3.5-turbo', 'VIRTUAL_APP_ENABLED': False, + + 'FILE_UPLOAD_SIZE_LIMIT_MB': 200 } old_config_map = { diff --git a/apps/jumpserver/settings/custom.py b/apps/jumpserver/settings/custom.py index d9eb41332..fad5f0e9d 100644 --- a/apps/jumpserver/settings/custom.py +++ b/apps/jumpserver/settings/custom.py @@ -221,3 +221,5 @@ GPT_PROXY = CONFIG.GPT_PROXY GPT_MODEL = CONFIG.GPT_MODEL VIRTUAL_APP_ENABLED = CONFIG.VIRTUAL_APP_ENABLED + +FILE_UPLOAD_SIZE_LIMIT_MB = CONFIG.FILE_UPLOAD_SIZE_LIMIT_MB diff --git a/apps/locale/ja/LC_MESSAGES/django.mo b/apps/locale/ja/LC_MESSAGES/django.mo index 5d7d6a0b8..53a43b4ee 100644 --- a/apps/locale/ja/LC_MESSAGES/django.mo +++ b/apps/locale/ja/LC_MESSAGES/django.mo @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7d9296340c0815fcda81bc7370051b1408225ba63ab7067b397595d4dfb334f7 -size 167881 +oid sha256:d98ad65b020a577f1e7542c3e8c0c2a289b67d12c39f4d35a5db667fd218e0ea +size 168292 diff --git a/apps/locale/ja/LC_MESSAGES/django.po b/apps/locale/ja/LC_MESSAGES/django.po index 833c315b5..b95854a00 100644 --- a/apps/locale/ja/LC_MESSAGES/django.po +++ b/apps/locale/ja/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-12-13 16:13+0800\n" +"POT-Creation-Date: 2023-12-13 18:29+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -4051,10 +4051,16 @@ msgstr "タスクは存在しません" msgid "Task {} args or kwargs error" msgstr "タスク実行パラメータエラー" -#: ops/api/job.py:124 +#: ops/api/job.py:132 msgid "Duplicate file exists" msgstr "重複したファイルが存在する" +#: ops/api/job.py:137 +#, python-brace-format +msgid "" +"File size exceeds maximum limit. Please select a file smaller than {limit}MB" +msgstr "ファイルサイズが最大制限を超えています。{limit}MB より小さいファイルを選択してください。" + #: ops/api/playbook.py:39 msgid "Currently playbook is being used in a job" msgstr "現在プレイブックは1つのジョブで使用されています" diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index 8f6f29a95..8e506d7f2 100644 --- a/apps/locale/zh/LC_MESSAGES/django.mo +++ b/apps/locale/zh/LC_MESSAGES/django.mo @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:37662b3fc6df22480304d2a5f90cf156ba7d4e0249ec11076e5018daab545714 -size 137610 +oid sha256:92bd4bd4a6d342dcd259215f16baeedb17fd95d321d4527bb3601ce1dfd72202 +size 137882 diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index 068613cd6..0b1265625 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: JumpServer 0.3.3\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-12-13 16:13+0800\n" +"POT-Creation-Date: 2023-12-13 18:29+0800\n" "PO-Revision-Date: 2021-05-20 10:54+0800\n" "Last-Translator: ibuler \n" "Language-Team: JumpServer team\n" @@ -4003,10 +4003,16 @@ msgstr "任务 {} 不存在" msgid "Task {} args or kwargs error" msgstr "任务 {} 执行参数错误" -#: ops/api/job.py:124 +#: ops/api/job.py:132 msgid "Duplicate file exists" msgstr "存在同名文件" +#: ops/api/job.py:137 +#, python-brace-format +msgid "" +"File size exceeds maximum limit. Please select a file smaller than {limit}MB" +msgstr "文件大小超过最大限制。请选择小于 {limit}MB 的文件。" + #: ops/api/playbook.py:39 msgid "Currently playbook is being used in a job" msgstr "当前 playbook 正在作业中使用" diff --git a/apps/ops/api/job.py b/apps/ops/api/job.py index 41822b970..90c5edbc3 100644 --- a/apps/ops/api/job.py +++ b/apps/ops/api/job.py @@ -99,7 +99,7 @@ class JobViewSet(OrgBulkModelViewSet): lambda: run_ops_job_execution.apply_async((str(execution.id),), task_id=str(execution.id))) @staticmethod - def get_duplicates_filenames(files): + def get_duplicates_files(files): seen = set() duplicates = set() for file in files: @@ -109,6 +109,14 @@ class JobViewSet(OrgBulkModelViewSet): seen.add(file) return list(duplicates) + @staticmethod + def get_exceeds_limit_files(files): + exceeds_limit_files = [] + for file in files: + if file.size > settings.FILE_UPLOAD_SIZE_LIMIT_MB * 1024 * 1024: + exceeds_limit_files.append(file) + return exceeds_limit_files + @action(methods=[POST], detail=False, serializer_class=FileSerializer, permission_classes=[IsValidUser, ], url_path='upload') def upload(self, request, *args, **kwargs): @@ -117,11 +125,18 @@ class JobViewSet(OrgBulkModelViewSet): if not serializer.is_valid(): msg = 'Upload data invalid: {}'.format(serializer.errors) - return Response({'msg': msg}, status=400) + return Response({'error': msg}, status=400) - same_filenames = self.get_duplicates_filenames(uploaded_files) - if same_filenames: - return Response({'msg': _("Duplicate file exists")}, status=400) + same_files = self.get_duplicates_files(uploaded_files) + if same_files: + return Response({'error': _("Duplicate file exists")}, status=400) + + exceeds_limit_files = self.get_exceeds_limit_files(uploaded_files) + if exceeds_limit_files: + return Response( + {'error': _("File size exceeds maximum limit. Please select a file smaller than {limit}MB").format( + limit=settings.FILE_UPLOAD_SIZE_LIMIT_MB)}, + status=400) job_id = request.data.get('job_id', '') job = get_object_or_404(Job, pk=job_id) From 65718c5a8415bfa6dc870841333a63cde1ab7955 Mon Sep 17 00:00:00 2001 From: wangruidong <940853815@qq.com> Date: Thu, 14 Dec 2023 11:20:40 +0800 Subject: [PATCH 064/111] =?UTF-8?q?perf:=20=E6=8E=A5=E5=8F=A3=E8=BF=94?= =?UTF-8?q?=E5=9B=9E=E4=B8=8A=E4=BC=A0=E6=96=87=E4=BB=B6=E5=A4=A7=E5=B0=8F?= =?UTF-8?q?=E9=99=90=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/settings/serializers/public.py | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/settings/serializers/public.py b/apps/settings/serializers/public.py index a135b1546..6e5a74c8e 100644 --- a/apps/settings/serializers/public.py +++ b/apps/settings/serializers/public.py @@ -56,6 +56,7 @@ class PrivateSettingSerializer(PublicSettingSerializer): VIRTUAL_APP_ENABLED = serializers.BooleanField() CHAT_AI_ENABLED = serializers.BooleanField() GPT_MODEL = serializers.CharField() + FILE_UPLOAD_SIZE_LIMIT_MB = serializers.IntegerField() class ServerInfoSerializer(serializers.Serializer): From 6cf05435bfda8932b43334fb419d74e33db15742 Mon Sep 17 00:00:00 2001 From: feng <1304903146@qq.com> Date: Thu, 14 Dec 2023 10:38:55 +0800 Subject: [PATCH 065/111] feat: chat prompt --- apps/settings/api/chat.py | 42 ++++- apps/settings/migrations/0011_chatprompt.py | 31 +++ apps/settings/models.py | 13 ++ apps/settings/prompt.py | 197 ++++++++++++++++++++ apps/settings/serializers/__init__.py | 1 + apps/settings/serializers/prompt.py | 11 ++ apps/settings/urls/api_urls.py | 5 + 7 files changed, 299 insertions(+), 1 deletion(-) create mode 100644 apps/settings/migrations/0011_chatprompt.py create mode 100644 apps/settings/prompt.py create mode 100644 apps/settings/serializers/prompt.py diff --git a/apps/settings/api/chat.py b/apps/settings/api/chat.py index a2601fbad..830ab45ea 100644 --- a/apps/settings/api/chat.py +++ b/apps/settings/api/chat.py @@ -4,9 +4,13 @@ from django.conf import settings from django.utils.translation import gettext_lazy as _ from rest_framework import status from rest_framework.generics import GenericAPIView -from rest_framework.views import Response +from rest_framework.response import Response +from common.api import JMSModelViewSet +from common.permissions import IsValidUser, OnlySuperUser from .. import serializers +from ..models import ChatPrompt +from ..prompt import DefaultChatPrompt class ChatAITestingAPI(GenericAPIView): @@ -76,3 +80,39 @@ class ChatAITestingAPI(GenericAPIView): _status, msg = status.HTTP_400_BAD_REQUEST, error return Response(status=_status, data={'msg': msg}) + + +class ChatPromptViewSet(JMSModelViewSet): + serializer_classes = { + 'default': serializers.ChatPromptSerializer, + } + permission_classes = [IsValidUser] + queryset = ChatPrompt.objects.all() + http_method_names = ['get', 'options'] + filterset_fields = ['name'] + search_fields = filterset_fields + + def get_permissions(self): + if self.action in ['create', 'update', 'partial_update', 'destroy']: + self.permission_classes = [OnlySuperUser] + return super().get_permissions() + + def filter_default_prompts(self): + lang = self.request.LANGUAGE_CODE + default_prompts = DefaultChatPrompt.get_prompts(lang) + search_query = self.request.query_params.get('search') + search_query = search_query or self.request.query_params.get('name') + if not search_query: + return default_prompts + + search_query = search_query.lower() + filtered_prompts = [ + prompt for prompt in default_prompts + if search_query in prompt['name'].lower() + ] + return filtered_prompts + + def filter_queryset(self, queryset): + queryset = super().filter_queryset(queryset) + default_prompts = self.filter_default_prompts() + return list(queryset) + default_prompts diff --git a/apps/settings/migrations/0011_chatprompt.py b/apps/settings/migrations/0011_chatprompt.py new file mode 100644 index 000000000..1e2b05801 --- /dev/null +++ b/apps/settings/migrations/0011_chatprompt.py @@ -0,0 +1,31 @@ +# Generated by Django 4.1.10 on 2023-12-13 11:07 + +import uuid + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ('settings', '0010_alter_setting_options'), + ] + + operations = [ + migrations.CreateModel( + name='ChatPrompt', + fields=[ + ('created_by', models.CharField(blank=True, max_length=128, null=True, verbose_name='Created by')), + ('updated_by', models.CharField(blank=True, max_length=128, null=True, verbose_name='Updated by')), + ('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')), + ('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')), + ('comment', models.TextField(blank=True, default='', verbose_name='Comment')), + ('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)), + ('name', models.CharField(max_length=128, unique=True, verbose_name='Name')), + ('content', models.TextField(verbose_name='Content')), + ('builtin', models.BooleanField(default=False, verbose_name='Builtin')), + ], + options={ + 'verbose_name': 'Chat prompt', + }, + ), + ] diff --git a/apps/settings/models.py b/apps/settings/models.py index 660af78ec..1da643b9e 100644 --- a/apps/settings/models.py +++ b/apps/settings/models.py @@ -8,6 +8,7 @@ from django.db import models from django.db.utils import ProgrammingError, OperationalError from django.utils.translation import gettext_lazy as _ +from common.db.models import JMSBaseModel from common.utils import signer, get_logger logger = get_logger(__name__) @@ -173,3 +174,15 @@ class Setting(models.Model): ('change_terminal', _('Can change terminal setting')), ('change_other', _('Can change other setting')), ] + + +class ChatPrompt(JMSBaseModel): + name = models.CharField(max_length=128, verbose_name=_('Name'), unique=True) + content = models.TextField(blank=False, null=False, verbose_name=_('Content')) + builtin = models.BooleanField(default=False, verbose_name=_('Builtin')) + + class Meta: + verbose_name = _("Chat prompt") + + def __str__(self): + return self.name diff --git a/apps/settings/prompt.py b/apps/settings/prompt.py new file mode 100644 index 000000000..516b24de8 --- /dev/null +++ b/apps/settings/prompt.py @@ -0,0 +1,197 @@ +class DefaultChatPrompt: + DEFAULT = { + 'zh': [ + { + 'name': '小红书风格', + 'content': '请使用 Emoji 风格编辑以下段落,该风格以引人入胜的标题、' + '每个段落中包含表情符号和在末尾添加相关标签为特点。请确保保持原文的意思。', + }, + { + 'name': '写作助理', + 'content': '作为一名中文写作改进助理,你的任务是改进所提供文本的拼写、语法、清晰、简洁和整体可读性,' + '同时分解长句,减少重复,并提供改进建议。请只提供文本的更正版本,避免包括解释。', + }, + { + 'name': 'Nature 风格润色', + 'content': '我希望你能充当专业的拼写和语法校对者,并改进我的文章。' + '我想让你用更美丽、优雅、高级的英语单词和句子替换我的简化 A0 级别的单词和句子,' + '保持意思不变,但使它们更具文学性,在《自然》杂志风格中提高我的表达水平。', + }, + { + 'name': '周报生成器', + 'content': '使用下面提供的文本作为中文周报的基础,生成一个简洁的摘要,突出最重要的内容。该报告应以 markdown 格式编写,' + '并应易于阅读和理解,以满足一般受众的需要。特别是要注重提供对利益相关者和决策者有用的见解和分析。' + '你也可以根据需要使用任何额外的信息或来源。', + }, + { + 'name': '数据库专家', + 'content': '我希望你充当一个数据库专家的角色,当我问你 sql 相关的问题时,' + '我需要你转换为标准的 sql 语句,当我的描述不够精准时,请给出合适的反馈', + }, + { + 'name': '全栈程序员', + 'content': '我希望你能扮演一个软件开发者的角色。我将提供一些关于网络应用需求的具体信息,' + '而你的工作是提出一个架构和代码,用 Golang 和 Angular 开发安全的应用。', + }, + { + 'name': '前端开发', + 'content': '我希望你能担任高级前端开发员。我将描述一个项目的细节,你将用这些工具来编码项目。' + 'Create React App, yarn, Ant Design, List, Redux Toolkit, createSlice, thunk, axios. ' + '你应该将文件合并到单一的 index.js 文件中,而不是其他。不要写解释。', + }, + { + 'name': '架构师 IT', + 'content': '我希望你能扮演一个 IT 架构师的角色。我将提供一些关于应用程序或其他数字产品功能的细节,' + '而你的工作是想出将其整合到 IT 环境中的方法。这可能涉及到分析业务需求,进行差距分析,' + '并将新系统的功能映射到现有的 IT 环境中。接下来的步骤是创建一个解决方案设计,' + '一个物理网络蓝图,定义系统集成的接口和部署环境的蓝图。', + }, + { + 'name': '代码释义器', + 'content': '我希望你能充当代码解释者,阐明代码的语法和语义。', + }, + { + 'name': 'IT 编程问题', + 'content': '我想让你充当 Stackoverflow 的帖子。我将提出与编程有关的问题,你将回答答案是什么。' + '我希望你只回答给定的答案,在没有足够的细节时写出解释。当我需要用英语告诉你一些事情时,我会把文字放在大括号里{像这样}。' + }, + ], + 'en': [ + { + "name": "Xiaohongshu Style", + "content": "Please edit the following paragraphs in Emoji style. " + "This style is characterized by engaging titles, the inclusion of emojis in each paragraph, " + "and adding related tags at the end. Ensure the original meaning is maintained." + }, + { + "name": "Writing Assistant", + "content": "As a Chinese writing improvement assistant, " + "your task is to improve the provided text in terms of spelling, grammar, clarity, " + "conciseness, and overall readability. Also, break down long sentences, reduce repetition, " + "and provide suggestions for improvement. Please only provide the corrected version of " + "the text, avoiding including explanations." + }, + { + "name": "Nature Style Editing", + "content": "I want you to act as a professional spelling and grammar proofreader and improve " + "my article. I want you to replace my simplified A0 level words and sentences with " + "more beautiful, elegant, and advanced English words and sentences. Keep the meaning " + "the same but make them more literary, enhancing my expression in the style of 'Nature' " + "magazine." + }, + { + "name": "Weekly Report Generator", + "content": "Using the text provided below as a basis for a Chinese weekly report, " + "generate a concise summary that highlights the most important content. " + "The report should be written in markdown format and should be easy to read " + "and understand for a general audience, especially focusing on providing insights " + "and analysis useful to stakeholders and decision-makers. You may also use any additional " + "information or sources as needed." + }, + { + "name": "Database Expert", + "content": "I want you to act as a database expert. When I ask you questions related to SQL, " + "I need you to convert them into standard SQL statements. " + "Please provide appropriate feedback when my descriptions are not precise enough." + }, + { + "name": "Full-Stack Programmer", + "content": "I want you to play the role of a software developer. " + "I will provide specific information about web application requirements, " + "and your job is to propose an architecture and code for developing a secure application " + "using Golang and Angular." + }, + { + "name": "Front-End Developer", + "content": "I want you to act as a senior front-end developer. " + "I will describe the details of a project, and you will code the project using these tools:" + " Create React App, yarn, Ant Design, List, Redux Toolkit, createSlice, thunk, axios. " + "You should consolidate files into a single index.js file, and not write explanations." + }, + { + "name": "IT Architect", + "content": "I want you to play the role of an IT architect. " + "I will provide details about the functionality of applications or other digital products, " + "and your job is to figure out how to integrate them into the IT environment. " + "This may involve analyzing business requirements, conducting gap analysis, " + "and mapping the new system's features to the existing IT environment. " + "The next steps are to create a solution design, a physical network blueprint, " + "define interfaces for system integration, and a blueprint for the deployment environment." + }, + { + "name": "Code Interpreter", + "content": "I want you to act as a code interpreter, clarifying the syntax and semantics of code." + }, + { + "name": "IT Programming Questions", + "content": "I want you to act as a Stackoverflow post. I will ask questions related to programming, " + "and you will answer what the answer is. Write out explanations " + "when there are not enough details. When I need to tell you something in English, " + "I will enclose the text in braces {like this}." + } + ], + 'ja': [ + { + "name": "小红书風格", + "content": "Emojiスタイルで以下の段落を編集してください。このスタイルは、魅力的なタイトル、" + "各段落に絵文字を含め、関連するタグを末尾に追加することが特徴です。原文の意味を保持してください。" + }, + { + "name": "ライティングアシスタント", + "content": "中国語のライティング改善アシスタントとして、提供されたテキストのスペル、" + "文法、明瞭さ、簡潔さ、全体的な可読性を改善し、長い文を分解し、重複を減らし、" + "改善提案を提供します。テキストの修正版のみを提供し、説明は含めないでください。" + }, + { + "name": "Nature スタイルの編集", + "content": "プロのスペルと文法の校正者として機能し、私の記事を改善してください。" + "私の簡素化されたA0レベルの単語や文章を、より美しく、優雅で、" + "高度な英語の単語や文章に置き換えて、文学的な要素を加え、「自然」誌スタイルで表現レベルを高めてください。" + }, + { + "name": "週報ジェネレータ", + "content": "以下のテキストを基にして中国語の週報の簡潔な要約を作成し、最も重要な内容を強調してください。" + "このレポートはマークダウン形式で書かれ、一般の聴衆にとって読みやすく理解しやすいものでなければなりません。" + "特に、利害関係者や意思決定者に有用な洞察と分析を提供することに重点を置いてください。" + "必要に応じて、追加の情報やソースを使用しても構いません。" + }, + { + "name": "データベースの専門家", + "content": "データベース専門家として機能し、私がsqlに関連する質問をするとき、" + "それを標準のsqlステートメントに変換してください。私の説明が不正確な場合は、適切なフィードバックを提供してください。" + }, + { + "name": "フルスタックプログラマー", + "content": "ソフトウェア開発者の役割を果たしてください。私はウェブアプリケーションの要件に関する具体的な情報を提供します。" + "あなたの仕事は、GolangとAngularを使用して安全なアプリケーションを開発するためのアーキテクチャとコードを提案することです。" + }, + { + "name": "フロントエンド開発", + "content": "上級フロントエンド開発者として機能してください。私はプロジェクトの詳細を説明し、" + "これらのツールを使用してプロジェクトをコーディングします。" + "Create React App、yarn、Ant Design、List、Redux Toolkit、createSlice、thunk、axiosを使用してください。" + "ファイルをindex.jsファイルに統合し、他のファイルではなく、説明は書かないでください。" + }, + { + "name": "ITアーキテクト", + "content": "ITアーキテクトの役割を果たしてください。私はアプリケーションや他のデジタル製品の機能に関する詳細を提供します。" + "あなたの仕事は、それをIT環境に統合する方法を考えることです。これには、ビジネス要件の分析、ギャップ分析の実施、" + "新システムの機能を既存のIT環境にマッピングすることが含まれる場合があります。次のステップは、" + "ソリューションデザインの作成、物理的なネットワークのブループリント、システム統合のインターフェース、およびデプロイメント環境のブループリントを定義することです。" + }, + { + "name": "コードインタープリター", + "content": "コードの解釈者として機能し、コードの文法と意味を明確に説明してください。" + }, + { + "name": "ITプログラミングの問題", + "content": "Stackoverflowの投稿として機能してください。私はプログラミングに関連する質問をします。" + "あなたは答えを何であるか答えます。十分な詳細がない場合は説明を書いてください。英語で何かを伝える必要があるときは、" + "大括弧でテキストを囲みます{このように}。" + } + ] + } + + @classmethod + def get_prompts(cls, lang: str) -> list: + return cls.DEFAULT.get(lang[:2], 'zh') diff --git a/apps/settings/serializers/__init__.py b/apps/settings/serializers/__init__.py index fe94eb1da..600684387 100644 --- a/apps/settings/serializers/__init__.py +++ b/apps/settings/serializers/__init__.py @@ -8,6 +8,7 @@ from .feature import * from .msg import * from .msg import * from .other import * +from .prompt import * from .public import * from .security import * from .settings import * diff --git a/apps/settings/serializers/prompt.py b/apps/settings/serializers/prompt.py new file mode 100644 index 000000000..6b7804516 --- /dev/null +++ b/apps/settings/serializers/prompt.py @@ -0,0 +1,11 @@ +from rest_framework import serializers + +from settings.models import ChatPrompt + + +class ChatPromptSerializer(serializers.ModelSerializer): + class Meta: + model = ChatPrompt + fields = [ + 'id', 'name', 'content', 'builtin' + ] diff --git a/apps/settings/urls/api_urls.py b/apps/settings/urls/api_urls.py index ed8cf74de..fdfae5146 100644 --- a/apps/settings/urls/api_urls.py +++ b/apps/settings/urls/api_urls.py @@ -1,10 +1,13 @@ from __future__ import absolute_import from django.urls import path +from rest_framework_bulk.routes import BulkRouter from .. import api app_name = 'common' +router = BulkRouter() +router.register(r'chatai-prompts', api.ChatPromptViewSet, 'chatai-prompt') urlpatterns = [ path('mail/testing/', api.MailTestingAPI.as_view(), name='mail-testing'), @@ -28,3 +31,5 @@ urlpatterns = [ path('public/open/', api.OpenPublicSettingApi.as_view(), name='open-public-setting'), path('server-info/', api.ServerInfoApi.as_view(), name='server-info'), ] + +urlpatterns += router.urls From 81fb080c674e6f23fa1b6dc53ea832410a0c3683 Mon Sep 17 00:00:00 2001 From: Eric Date: Thu, 14 Dec 2023 15:48:12 +0800 Subject: [PATCH 066/111] =?UTF-8?q?perf:=20=E8=B0=83=E6=95=B4=E6=90=9C?= =?UTF-8?q?=E7=B4=A2=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/terminal/api/virtualapp/virtualapp.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/terminal/api/virtualapp/virtualapp.py b/apps/terminal/api/virtualapp/virtualapp.py index 07b92a356..7b4c5b9b2 100644 --- a/apps/terminal/api/virtualapp/virtualapp.py +++ b/apps/terminal/api/virtualapp/virtualapp.py @@ -63,8 +63,8 @@ class UploadMixin: class VirtualAppViewSet(UploadMixin, JMSBulkModelViewSet): queryset = VirtualApp.objects.all() serializer_class = serializers.VirtualAppSerializer - filterset_fields = ['name', 'image_name', 'is_active'] - search_fields = ['name', ] + filterset_fields = ['name', 'is_active'] + search_fields = ['name', 'image_name'] rbac_perms = { 'upload': 'terminal.add_virtualapp', } From 3c9239eb09685c65ed09d770e30166d7be8530f0 Mon Sep 17 00:00:00 2001 From: Bai Date: Thu, 14 Dec 2023 17:49:31 +0800 Subject: [PATCH 067/111] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=20Release=20?= =?UTF-8?q?=E5=BA=94=E7=94=A8=E8=B4=A6=E5=8F=B7=E7=9A=84=E9=80=BB=E8=BE=91?= =?UTF-8?q?,=E8=A7=A3=E5=86=B3=E9=A6=96=E6=AC=A1=E8=BF=9E=E6=8E=A5?= =?UTF-8?q?=E8=BF=9C=E7=A8=8B=E5=BA=94=E7=94=A8=E5=8F=AF=E8=83=BD=E5=87=BA?= =?UTF-8?q?=E7=8E=B0=E6=B2=A1=E6=9C=89=E5=8F=AF=E7=94=A8=E8=B4=A6=E5=8F=B7?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/authentication/api/connection_token.py | 8 ++++---- apps/authentication/models/connection_token.py | 11 +++-------- apps/terminal/models/applet/applet.py | 3 +-- 3 files changed, 8 insertions(+), 14 deletions(-) diff --git a/apps/authentication/api/connection_token.py b/apps/authentication/api/connection_token.py index bc79d26a2..076767946 100644 --- a/apps/authentication/api/connection_token.py +++ b/apps/authentication/api/connection_token.py @@ -544,12 +544,12 @@ class SuperConnectionTokenViewSet(ConnectionTokenViewSet): @action(methods=['DELETE', 'POST'], detail=False, url_path='applet-account/release') def release_applet_account(self, *args, **kwargs): - account_id = self.request.data.get('id') - released = ConnectionToken.release_applet_account(account_id) + lock_key = self.request.data.get('id') + released = ConnectionToken.release_applet_account(lock_key) if released: - logger.debug('Release applet account success: {}'.format(account_id)) + logger.debug('Release applet account success: {}'.format(lock_key)) return Response({'msg': 'released'}) else: - logger.error('Release applet account error: {}'.format(account_id)) + logger.error('Release applet account error: {}'.format(lock_key)) return Response({'error': 'not found or expired'}, status=400) diff --git a/apps/authentication/models/connection_token.py b/apps/authentication/models/connection_token.py index 6cd5648db..2ef28eab1 100644 --- a/apps/authentication/models/connection_token.py +++ b/apps/authentication/models/connection_token.py @@ -199,28 +199,23 @@ class ConnectionToken(JMSOrgBaseModel): if not host_account: raise JMSException({'error': 'No host account available'}) - host, account, lock_key, ttl = bulk_get(host_account, ('host', 'account', 'lock_key', 'ttl')) + host, account, lock_key = bulk_get(host_account, ('host', 'account', 'lock_key')) gateway = host.domain.select_gateway() if host.domain else None data = { - 'id': account.id, + 'id': lock_key, 'applet': applet, 'host': host, 'gateway': gateway, 'account': account, 'remote_app_option': self.get_remote_app_option() } - token_account_relate_key = f'token_account_relate_{account.id}' - cache.set(token_account_relate_key, lock_key, ttl) return data @staticmethod - def release_applet_account(account_id): - token_account_relate_key = f'token_account_relate_{account_id}' - lock_key = cache.get(token_account_relate_key) + def release_applet_account(lock_key): if lock_key: cache.delete(lock_key) - cache.delete(token_account_relate_key) return True @lazyproperty diff --git a/apps/terminal/models/applet/applet.py b/apps/terminal/models/applet/applet.py index c018275ac..3110b506f 100644 --- a/apps/terminal/models/applet/applet.py +++ b/apps/terminal/models/applet/applet.py @@ -299,8 +299,7 @@ class Applet(JMSBaseModel): res = { 'host': host, 'account': account, - 'lock_key': lock_key, - 'ttl': ttl + 'lock_key': lock_key } logger.debug('Select host and account: {}'.format(res)) return res From 438e9dee2a296ddfd4be9dd4f125e0647df9f8ac Mon Sep 17 00:00:00 2001 From: jiangweidong Date: Thu, 14 Dec 2023 17:34:17 +0800 Subject: [PATCH 068/111] =?UTF-8?q?fix:=20=E8=A7=A3=E5=86=B3=E7=AC=AC?= =?UTF-8?q?=E4=B8=89=E6=96=B9=E7=99=BB=E5=BD=95=E4=B8=80=E4=B8=AA=E4=B8=8D?= =?UTF-8?q?=E5=AD=98=E5=9C=A8=E7=9A=84=E6=9C=AC=E5=9C=B0=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E6=97=B6=EF=BC=8C=E6=94=B9=E5=AF=86=E6=97=A5=E5=BF=97=E4=BC=9A?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/users/models/user.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/users/models/user.py b/apps/users/models/user.py index aca0c14a8..5f3739400 100644 --- a/apps/users/models/user.py +++ b/apps/users/models/user.py @@ -41,6 +41,7 @@ class AuthMixin: history_passwords: models.Manager need_update_password: bool public_key: str + username: str is_local: bool set_password: Callable save: Callable @@ -63,9 +64,10 @@ class AuthMixin: def set_password(self, raw_password): if self.can_update_password(): - self.date_password_last_updated = timezone.now() - post_user_change_password.send(self.__class__, user=self) - super().set_password(raw_password) + if self.username: + self.date_password_last_updated = timezone.now() + post_user_change_password.send(self.__class__, user=self) + super().set_password(raw_password) # noqa def set_public_key(self, public_key): if self.can_update_ssh_key(): From e3c0518cfbbc095abab988d425040d58da9d3e3e Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Thu, 14 Dec 2023 19:44:53 +0800 Subject: [PATCH 069/111] =?UTF-8?q?perf:=20=E4=B8=8A=E4=BC=A0=E7=9B=AE?= =?UTF-8?q?=E6=A0=87=E7=9B=AE=E5=BD=95=E6=8C=87=E5=AE=9A=E5=9C=A8/tmp?= =?UTF-8?q?=E4=B8=8B=20(#12334)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: wangruidong <940853815@qq.com> --- apps/ops/ansible/runner.py | 2 +- apps/ops/api/job.py | 2 +- apps/ops/models/job.py | 7 ++----- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/apps/ops/ansible/runner.py b/apps/ops/ansible/runner.py index 47af111e5..c5fee5fd9 100644 --- a/apps/ops/ansible/runner.py +++ b/apps/ops/ansible/runner.py @@ -93,7 +93,7 @@ class UploadFileRunner: self.cb = DefaultCallback() upload_file_dir = safe_join(settings.DATA_DIR, 'job_upload_file') self.src_paths = safe_join(upload_file_dir, str(job_id)) - self.dest_path = dest_path + self.dest_path = safe_join("/tmp", dest_path) def run(self, verbosity=0, **kwargs): verbosity = get_ansible_log_verbosity(verbosity) diff --git a/apps/ops/api/job.py b/apps/ops/api/job.py index 90c5edbc3..3b07e850d 100644 --- a/apps/ops/api/job.py +++ b/apps/ops/api/job.py @@ -68,7 +68,7 @@ class JobViewSet(OrgBulkModelViewSet): def get_queryset(self): queryset = super().get_queryset() - queryset = queryset.filter(creator=self.request.user) + queryset = queryset.filter(creator=self.request.user).exclude(type=Types.upload_file) if self.action != 'retrieve': return queryset.filter(instant=False) return queryset diff --git a/apps/ops/models/job.py b/apps/ops/models/job.py index 358c37136..bf460232f 100644 --- a/apps/ops/models/job.py +++ b/apps/ops/models/job.py @@ -381,11 +381,8 @@ class JobExecution(JMSOrgBaseModel): elif self.current_job.type == Types.upload_file: job_id = self.current_job.id args = json.loads(self.current_job.args) - dst_path = args.get('dst_path') - if dst_path: - runner = UploadFileRunner(self.inventory_path, job_id, dst_path) - else: - raise ValueError("dst_path is null") + dst_path = args.get('dst_path', '/') + runner = UploadFileRunner(self.inventory_path, job_id, dst_path) else: raise Exception("unsupported job type") return runner From 32178b2344de031ab271ceeed0294fa8022b731b Mon Sep 17 00:00:00 2001 From: feng <1304903146@qq.com> Date: Fri, 15 Dec 2023 10:48:04 +0800 Subject: [PATCH 070/111] =?UTF-8?q?perf:=20=E4=BF=AE=E6=94=B9=E9=BB=98?= =?UTF-8?q?=E8=AE=A4prompt=E9=A1=BA=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/settings/prompt.py | 114 ++++++++++++++++++++-------------------- 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/apps/settings/prompt.py b/apps/settings/prompt.py index 516b24de8..5e2b72c96 100644 --- a/apps/settings/prompt.py +++ b/apps/settings/prompt.py @@ -1,22 +1,6 @@ class DefaultChatPrompt: DEFAULT = { 'zh': [ - { - 'name': '小红书风格', - 'content': '请使用 Emoji 风格编辑以下段落,该风格以引人入胜的标题、' - '每个段落中包含表情符号和在末尾添加相关标签为特点。请确保保持原文的意思。', - }, - { - 'name': '写作助理', - 'content': '作为一名中文写作改进助理,你的任务是改进所提供文本的拼写、语法、清晰、简洁和整体可读性,' - '同时分解长句,减少重复,并提供改进建议。请只提供文本的更正版本,避免包括解释。', - }, - { - 'name': 'Nature 风格润色', - 'content': '我希望你能充当专业的拼写和语法校对者,并改进我的文章。' - '我想让你用更美丽、优雅、高级的英语单词和句子替换我的简化 A0 级别的单词和句子,' - '保持意思不变,但使它们更具文学性,在《自然》杂志风格中提高我的表达水平。', - }, { 'name': '周报生成器', 'content': '使用下面提供的文本作为中文周报的基础,生成一个简洁的摘要,突出最重要的内容。该报告应以 markdown 格式编写,' @@ -55,30 +39,24 @@ class DefaultChatPrompt: 'content': '我想让你充当 Stackoverflow 的帖子。我将提出与编程有关的问题,你将回答答案是什么。' '我希望你只回答给定的答案,在没有足够的细节时写出解释。当我需要用英语告诉你一些事情时,我会把文字放在大括号里{像这样}。' }, + { + 'name': '小红书风格', + 'content': '请使用 Emoji 风格编辑以下段落,该风格以引人入胜的标题、' + '每个段落中包含表情符号和在末尾添加相关标签为特点。请确保保持原文的意思。', + }, + { + 'name': '写作助理', + 'content': '作为一名中文写作改进助理,你的任务是改进所提供文本的拼写、语法、清晰、简洁和整体可读性,' + '同时分解长句,减少重复,并提供改进建议。请只提供文本的更正版本,避免包括解释。', + }, + { + 'name': 'Nature 风格润色', + 'content': '我希望你能充当专业的拼写和语法校对者,并改进我的文章。' + '我想让你用更美丽、优雅、高级的英语单词和句子替换我的简化 A0 级别的单词和句子,' + '保持意思不变,但使它们更具文学性,在《自然》杂志风格中提高我的表达水平。', + }, ], 'en': [ - { - "name": "Xiaohongshu Style", - "content": "Please edit the following paragraphs in Emoji style. " - "This style is characterized by engaging titles, the inclusion of emojis in each paragraph, " - "and adding related tags at the end. Ensure the original meaning is maintained." - }, - { - "name": "Writing Assistant", - "content": "As a Chinese writing improvement assistant, " - "your task is to improve the provided text in terms of spelling, grammar, clarity, " - "conciseness, and overall readability. Also, break down long sentences, reduce repetition, " - "and provide suggestions for improvement. Please only provide the corrected version of " - "the text, avoiding including explanations." - }, - { - "name": "Nature Style Editing", - "content": "I want you to act as a professional spelling and grammar proofreader and improve " - "my article. I want you to replace my simplified A0 level words and sentences with " - "more beautiful, elegant, and advanced English words and sentences. Keep the meaning " - "the same but make them more literary, enhancing my expression in the style of 'Nature' " - "magazine." - }, { "name": "Weekly Report Generator", "content": "Using the text provided below as a basis for a Chinese weekly report, " @@ -128,26 +106,31 @@ class DefaultChatPrompt: "and you will answer what the answer is. Write out explanations " "when there are not enough details. When I need to tell you something in English, " "I will enclose the text in braces {like this}." - } + }, + { + "name": "Xiaohongshu Style", + "content": "Please edit the following paragraphs in Emoji style. " + "This style is characterized by engaging titles, the inclusion of emojis in each paragraph, " + "and adding related tags at the end. Ensure the original meaning is maintained." + }, + { + "name": "Writing Assistant", + "content": "As a Chinese writing improvement assistant, " + "your task is to improve the provided text in terms of spelling, grammar, clarity, " + "conciseness, and overall readability. Also, break down long sentences, reduce repetition, " + "and provide suggestions for improvement. Please only provide the corrected version of " + "the text, avoiding including explanations." + }, + { + "name": "Nature Style Editing", + "content": "I want you to act as a professional spelling and grammar proofreader and improve " + "my article. I want you to replace my simplified A0 level words and sentences with " + "more beautiful, elegant, and advanced English words and sentences. Keep the meaning " + "the same but make them more literary, enhancing my expression in the style of 'Nature' " + "magazine." + }, ], 'ja': [ - { - "name": "小红书風格", - "content": "Emojiスタイルで以下の段落を編集してください。このスタイルは、魅力的なタイトル、" - "各段落に絵文字を含め、関連するタグを末尾に追加することが特徴です。原文の意味を保持してください。" - }, - { - "name": "ライティングアシスタント", - "content": "中国語のライティング改善アシスタントとして、提供されたテキストのスペル、" - "文法、明瞭さ、簡潔さ、全体的な可読性を改善し、長い文を分解し、重複を減らし、" - "改善提案を提供します。テキストの修正版のみを提供し、説明は含めないでください。" - }, - { - "name": "Nature スタイルの編集", - "content": "プロのスペルと文法の校正者として機能し、私の記事を改善してください。" - "私の簡素化されたA0レベルの単語や文章を、より美しく、優雅で、" - "高度な英語の単語や文章に置き換えて、文学的な要素を加え、「自然」誌スタイルで表現レベルを高めてください。" - }, { "name": "週報ジェネレータ", "content": "以下のテキストを基にして中国語の週報の簡潔な要約を作成し、最も重要な内容を強調してください。" @@ -188,7 +171,24 @@ class DefaultChatPrompt: "content": "Stackoverflowの投稿として機能してください。私はプログラミングに関連する質問をします。" "あなたは答えを何であるか答えます。十分な詳細がない場合は説明を書いてください。英語で何かを伝える必要があるときは、" "大括弧でテキストを囲みます{このように}。" - } + }, + { + "name": "小红书風格", + "content": "Emojiスタイルで以下の段落を編集してください。このスタイルは、魅力的なタイトル、" + "各段落に絵文字を含め、関連するタグを末尾に追加することが特徴です。原文の意味を保持してください。" + }, + { + "name": "ライティングアシスタント", + "content": "中国語のライティング改善アシスタントとして、提供されたテキストのスペル、" + "文法、明瞭さ、簡潔さ、全体的な可読性を改善し、長い文を分解し、重複を減らし、" + "改善提案を提供します。テキストの修正版のみを提供し、説明は含めないでください。" + }, + { + "name": "Nature スタイルの編集", + "content": "プロのスペルと文法の校正者として機能し、私の記事を改善してください。" + "私の簡素化されたA0レベルの単語や文章を、より美しく、優雅で、" + "高度な英語の単語や文章に置き換えて、文学的な要素を加え、「自然」誌スタイルで表現レベルを高めてください。" + }, ] } From c3820b30b89fb193df64548b14d5786d30ea9c77 Mon Sep 17 00:00:00 2001 From: Bai Date: Fri, 15 Dec 2023 12:18:21 +0800 Subject: [PATCH 071/111] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E8=BF=9C?= =?UTF-8?q?=E7=A8=8B=E5=BA=94=E7=94=A8=E8=BF=9E=E6=8E=A5=20labels=20?= =?UTF-8?q?=E8=BF=87=E6=BB=A4=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/terminal/models/applet/applet.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/terminal/models/applet/applet.py b/apps/terminal/models/applet/applet.py index 3110b506f..05209b03d 100644 --- a/apps/terminal/models/applet/applet.py +++ b/apps/terminal/models/applet/applet.py @@ -171,7 +171,7 @@ class Applet(JMSBaseModel): if not hosts: return None - spec_label = asset.labels.filter(name__in=['AppletHost', '发布机']).first() + spec_label = asset.labels.filter(label__name__in=['AppletHost', '发布机']).first() if spec_label: matched = [host for host in hosts if host.name == spec_label.value] if matched: From b9e1d6093e33707da57132a375b82fffe3f3567e Mon Sep 17 00:00:00 2001 From: wangruidong <940853815@qq.com> Date: Fri, 15 Dec 2023 15:18:08 +0800 Subject: [PATCH 072/111] =?UTF-8?q?perf:=20=E7=BF=BB=E8=AF=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/locale/ja/LC_MESSAGES/django.mo | 4 +- apps/locale/ja/LC_MESSAGES/django.po | 213 +++++++++++++---------- apps/locale/zh/LC_MESSAGES/django.mo | 4 +- apps/locale/zh/LC_MESSAGES/django.po | 207 ++++++++++++---------- apps/terminal/serializers/applet_host.py | 16 +- 5 files changed, 249 insertions(+), 195 deletions(-) diff --git a/apps/locale/ja/LC_MESSAGES/django.mo b/apps/locale/ja/LC_MESSAGES/django.mo index 53a43b4ee..900413bdf 100644 --- a/apps/locale/ja/LC_MESSAGES/django.mo +++ b/apps/locale/ja/LC_MESSAGES/django.mo @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d98ad65b020a577f1e7542c3e8c0c2a289b67d12c39f4d35a5db667fd218e0ea -size 168292 +oid sha256:93aa582f0d6c3661722b4735728383f7caf7bbe4ef6dc6eaa879c5ad3bcf2bec +size 169057 diff --git a/apps/locale/ja/LC_MESSAGES/django.po b/apps/locale/ja/LC_MESSAGES/django.po index b95854a00..75fc20a00 100644 --- a/apps/locale/ja/LC_MESSAGES/django.po +++ b/apps/locale/ja/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-12-13 18:29+0800\n" +"POT-Creation-Date: 2023-12-15 15:13+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -74,7 +74,7 @@ msgstr "動的コード" msgid "Anonymous account" msgstr "匿名ユーザー" -#: accounts/const/account.py:25 users/models/user.py:740 +#: accounts/const/account.py:25 users/models/user.py:742 msgid "Local" msgstr "ローカル" @@ -201,7 +201,7 @@ msgstr "作成のみ" #: authentication/serializers/password_mfa.py:24 #: notifications/backends/__init__.py:10 settings/serializers/msg.py:22 #: settings/serializers/msg.py:57 users/forms/profile.py:102 -#: users/forms/profile.py:109 users/models/user.py:800 +#: users/forms/profile.py:109 users/models/user.py:802 #: users/templates/users/forgot_password.html:117 #: users/views/profile/reset.py:92 msgid "Email" @@ -276,7 +276,7 @@ msgid "Version" msgstr "バージョン" #: accounts/models/account.py:57 accounts/serializers/account/account.py:215 -#: users/models/user.py:843 +#: users/models/user.py:845 msgid "Source" msgstr "ソース" @@ -476,10 +476,10 @@ msgstr "終了日" #: accounts/models/automations/change_secret.py:43 #: assets/models/automations/base.py:113 audits/models.py:208 #: audits/serializers.py:54 ops/models/base.py:49 ops/models/job.py:232 -#: terminal/models/applet/applet.py:320 terminal/models/applet/host.py:140 +#: terminal/models/applet/applet.py:319 terminal/models/applet/host.py:140 #: terminal/models/component/status.py:30 #: terminal/models/virtualapp/virtualapp.py:99 -#: terminal/serializers/applet.py:18 terminal/serializers/applet_host.py:124 +#: terminal/serializers/applet.py:18 terminal/serializers/applet_host.py:136 #: terminal/serializers/virtualapp.py:35 tickets/models/ticket/general.py:283 #: tickets/serializers/super_ticket.py:13 #: tickets/serializers/ticket/ticket.py:20 xpack/plugins/cloud/models.py:201 @@ -517,7 +517,7 @@ msgstr "最終ログイン日" #: authentication/templates/authentication/_msg_different_city.html:9 #: authentication/templates/authentication/_msg_oauth_bind.html:9 #: terminal/serializers/storage.py:136 users/forms/profile.py:32 -#: users/forms/profile.py:115 users/models/user.py:796 +#: users/forms/profile.py:115 users/models/user.py:798 #: users/templates/users/_msg_user_created.html:12 #: xpack/plugins/cloud/serializers/account_attrs.py:26 msgid "Username" @@ -604,7 +604,7 @@ msgstr "パスワードルール" #: ops/models/celery.py:57 ops/models/job.py:137 ops/models/playbook.py:29 #: ops/serializers/job.py:19 orgs/models.py:82 #: perms/models/asset_permission.py:61 rbac/models/role.py:29 -#: settings/models.py:32 settings/serializers/msg.py:82 +#: settings/models.py:33 settings/models.py:180 settings/serializers/msg.py:82 #: terminal/models/applet/applet.py:33 terminal/models/component/endpoint.py:12 #: terminal/models/component/endpoint.py:95 #: terminal/models/component/storage.py:26 terminal/models/component/task.py:13 @@ -612,7 +612,7 @@ msgstr "パスワードルール" #: terminal/models/virtualapp/provider.py:10 #: terminal/models/virtualapp/virtualapp.py:19 tickets/api/ticket.py:87 #: users/forms/profile.py:33 users/models/group.py:13 -#: users/models/preference.py:11 users/models/user.py:798 +#: users/models/preference.py:11 users/models/user.py:800 #: xpack/plugins/cloud/models.py:32 xpack/plugins/cloud/models.py:273 #: xpack/plugins/cloud/serializers/task.py:68 msgid "Name" @@ -742,7 +742,7 @@ msgstr "アカウントの存在ポリシー" #: assets/models/label.py:21 assets/models/platform.py:96 #: assets/serializers/asset/common.py:122 assets/serializers/cagegory.py:12 #: assets/serializers/platform.py:140 assets/serializers/platform.py:236 -#: perms/serializers/user_permission.py:25 settings/models.py:34 +#: perms/serializers/user_permission.py:25 settings/models.py:35 #: tickets/models/ticket/apply_application.py:13 users/models/preference.py:12 msgid "Category" msgstr "カテゴリ" @@ -831,8 +831,8 @@ msgstr "ID" #: terminal/notifications.py:205 terminal/serializers/command.py:16 #: terminal/templates/terminal/_msg_command_warning.html:6 #: terminal/templates/terminal/_msg_session_sharing.html:6 -#: tickets/models/comment.py:21 users/const.py:14 users/models/user.py:994 -#: users/models/user.py:1031 users/serializers/group.py:19 +#: tickets/models/comment.py:21 users/const.py:14 users/models/user.py:996 +#: users/models/user.py:1033 users/serializers/group.py:19 msgid "User" msgstr "ユーザー" @@ -922,13 +922,13 @@ msgstr "关联平台,可以配置推送参数,如果不关联,则使用默 #: assets/models/cmd_filter.py:40 assets/models/cmd_filter.py:88 #: assets/models/group.py:20 common/db/models.py:36 ops/models/adhoc.py:26 #: ops/models/job.py:157 ops/models/playbook.py:32 rbac/models/role.py:37 -#: settings/models.py:37 terminal/models/applet/applet.py:45 -#: terminal/models/applet/applet.py:321 terminal/models/applet/host.py:143 +#: settings/models.py:38 terminal/models/applet/applet.py:45 +#: terminal/models/applet/applet.py:320 terminal/models/applet/host.py:143 #: terminal/models/component/endpoint.py:25 #: terminal/models/component/endpoint.py:105 #: terminal/models/session/session.py:46 #: terminal/models/virtualapp/virtualapp.py:28 tickets/models/comment.py:32 -#: tickets/models/ticket/general.py:297 users/models/user.py:834 +#: tickets/models/ticket/general.py:297 users/models/user.py:836 #: xpack/plugins/cloud/models.py:39 xpack/plugins/cloud/models.py:109 msgid "Comment" msgstr "コメント" @@ -1060,7 +1060,7 @@ msgid "Acls" msgstr "Acls" #: acls/const.py:6 audits/const.py:36 terminal/const.py:11 tickets/const.py:45 -#: tickets/templates/tickets/approve_check_password.html:47 +#: tickets/templates/tickets/approve_check_password.html:49 msgid "Reject" msgstr "拒否" @@ -1130,7 +1130,8 @@ msgid "Regex" msgstr "正規情報" #: acls/models/command_acl.py:26 assets/models/cmd_filter.py:79 -#: settings/serializers/feature.py:19 xpack/plugins/license/models.py:30 +#: settings/models.py:181 settings/serializers/feature.py:19 +#: xpack/plugins/license/models.py:30 msgid "Content" msgstr "コンテンツ" @@ -1617,18 +1618,18 @@ msgstr "SSHパブリックキー" #: assets/models/_user.py:28 assets/models/automations/base.py:114 #: assets/models/cmd_filter.py:41 assets/models/group.py:19 #: audits/models.py:267 common/db/models.py:34 ops/models/base.py:54 -#: ops/models/job.py:239 users/models/user.py:1032 +#: ops/models/job.py:239 users/models/user.py:1034 msgid "Date created" msgstr "作成された日付" #: assets/models/_user.py:29 assets/models/cmd_filter.py:42 -#: common/db/models.py:35 users/models/user.py:852 +#: common/db/models.py:35 users/models/user.py:854 msgid "Date updated" msgstr "更新日" #: assets/models/_user.py:30 assets/models/cmd_filter.py:44 #: assets/models/cmd_filter.py:91 assets/models/group.py:18 -#: common/db/models.py:32 users/models/user.py:841 +#: common/db/models.py:32 users/models/user.py:843 #: users/serializers/group.py:30 msgid "Created by" msgstr "によって作成された" @@ -1817,7 +1818,7 @@ msgstr "確認済みの日付" #: assets/models/cmd_filter.py:28 perms/models/asset_permission.py:66 #: perms/serializers/permission.py:33 users/models/group.py:25 -#: users/models/user.py:804 +#: users/models/user.py:806 msgid "User group" msgstr "ユーザーグループ" @@ -1867,7 +1868,7 @@ msgstr "デフォルト" msgid "Default asset group" msgstr "デフォルトアセットグループ" -#: assets/models/label.py:15 rbac/const.py:6 users/models/user.py:1017 +#: assets/models/label.py:15 rbac/const.py:6 users/models/user.py:1019 msgid "System" msgstr "システム" @@ -1876,7 +1877,7 @@ msgstr "システム" #: assets/serializers/cagegory.py:24 #: authentication/models/connection_token.py:29 #: authentication/serializers/connect_token_secret.py:125 -#: common/serializers/common.py:86 labels/models.py:12 settings/models.py:33 +#: common/serializers/common.py:86 labels/models.py:12 settings/models.py:34 #: users/models/preference.py:13 msgid "Value" msgstr "値" @@ -1933,7 +1934,7 @@ msgid "Setting" msgstr "設定" #: assets/models/platform.py:39 audits/const.py:56 -#: authentication/backends/passkey/models.py:11 settings/models.py:36 +#: authentication/backends/passkey/models.py:11 settings/models.py:37 #: terminal/serializers/applet_host.py:33 msgid "Enabled" msgstr "有効化" @@ -2513,7 +2514,7 @@ msgstr "ログインIP" #: audits/models.py:200 audits/serializers.py:52 #: authentication/templates/authentication/_mfa_confirm_modal.html:14 -#: users/forms/profile.py:65 users/models/user.py:821 +#: users/forms/profile.py:65 users/models/user.py:823 #: users/serializers/profile.py:102 msgid "MFA" msgstr "MFA" @@ -2561,7 +2562,7 @@ msgstr "ユーザー %s %s が現在のリソースをサブスクライブし #: audits/serializers.py:172 authentication/models/connection_token.py:47 #: authentication/models/temp_token.py:13 perms/models/asset_permission.py:80 #: tickets/models/ticket/apply_application.py:31 -#: tickets/models/ticket/apply_asset.py:20 users/models/user.py:839 +#: tickets/models/ticket/apply_asset.py:20 users/models/user.py:841 msgid "Date expired" msgstr "期限切れの日付" @@ -2594,30 +2595,30 @@ msgstr "認証トークン" #: audits/signal_handlers/login_log.py:37 authentication/notifications.py:73 #: authentication/views/login.py:77 notifications/backends/__init__.py:11 -#: settings/serializers/auth/wecom.py:10 users/models/user.py:747 -#: users/models/user.py:853 +#: settings/serializers/auth/wecom.py:10 users/models/user.py:749 +#: users/models/user.py:855 msgid "WeCom" msgstr "企業微信" #: audits/signal_handlers/login_log.py:38 authentication/views/feishu.py:87 #: authentication/views/login.py:89 notifications/backends/__init__.py:14 #: settings/serializers/auth/feishu.py:10 -#: settings/serializers/auth/feishu.py:13 users/models/user.py:749 -#: users/models/user.py:855 +#: settings/serializers/auth/feishu.py:13 users/models/user.py:751 +#: users/models/user.py:857 msgid "FeiShu" msgstr "本を飛ばす" #: audits/signal_handlers/login_log.py:39 authentication/views/login.py:95 #: authentication/views/slack.py:87 notifications/backends/__init__.py:15 -#: settings/serializers/auth/slack.py:10 users/models/user.py:750 -#: users/models/user.py:856 +#: settings/serializers/auth/slack.py:10 users/models/user.py:752 +#: users/models/user.py:858 msgid "Slack" msgstr "" #: audits/signal_handlers/login_log.py:40 authentication/views/dingtalk.py:160 #: authentication/views/login.py:83 notifications/backends/__init__.py:12 -#: settings/serializers/auth/dingtalk.py:10 users/models/user.py:748 -#: users/models/user.py:854 +#: settings/serializers/auth/dingtalk.py:10 users/models/user.py:750 +#: users/models/user.py:856 msgid "DingTalk" msgstr "DingTalk" @@ -3131,11 +3132,11 @@ msgstr "ユーザーなしまたは期限切れのユーザー" msgid "No asset or inactive asset" msgstr "アセットがないか、有効化されていないアセット" -#: authentication/models/connection_token.py:274 +#: authentication/models/connection_token.py:269 msgid "Can view super connection token secret" msgstr "スーパー接続トークンのシークレットを表示できます" -#: authentication/models/connection_token.py:276 +#: authentication/models/connection_token.py:271 msgid "Super connection token" msgstr "スーパー接続トークン" @@ -3252,13 +3253,13 @@ msgid "Show" msgstr "表示" #: authentication/templates/authentication/_access_key_modal.html:66 -#: users/const.py:37 users/models/user.py:642 users/serializers/profile.py:92 +#: users/const.py:37 users/models/user.py:644 users/serializers/profile.py:92 #: users/templates/users/user_verify_mfa.html:36 msgid "Disable" msgstr "無効化" #: authentication/templates/authentication/_access_key_modal.html:67 -#: users/const.py:38 users/models/user.py:643 users/serializers/profile.py:93 +#: users/const.py:38 users/models/user.py:645 users/serializers/profile.py:93 #: users/templates/users/mfa_setting.html:26 #: users/templates/users/mfa_setting.html:68 msgid "Enable" @@ -3729,7 +3730,7 @@ msgstr "は破棄されます" msgid "discard time" msgstr "時間を捨てる" -#: common/db/models.py:33 users/models/user.py:842 +#: common/db/models.py:33 users/models/user.py:844 msgid "Updated by" msgstr "によって更新" @@ -4059,7 +4060,9 @@ msgstr "重複したファイルが存在する" #, python-brace-format msgid "" "File size exceeds maximum limit. Please select a file smaller than {limit}MB" -msgstr "ファイルサイズが最大制限を超えています。{limit}MB より小さいファイルを選択してください。" +msgstr "" +"ファイルサイズが最大制限を超えています。{limit}MB より小さいファイルを選択し" +"てください。" #: ops/api/playbook.py:39 msgid "Currently playbook is being used in a job" @@ -4311,7 +4314,7 @@ msgstr "Material" msgid "Material Type" msgstr "Material を選択してオプションを設定します。" -#: ops/models/job.py:565 +#: ops/models/job.py:562 msgid "Job Execution" msgstr "ジョブ実行" @@ -4483,7 +4486,8 @@ msgstr "デフォルト組織" msgid "SYSTEM" msgstr "システム組織" -#: orgs/models.py:83 rbac/models/role.py:36 terminal/models/applet/applet.py:41 +#: orgs/models.py:83 rbac/models/role.py:36 settings/models.py:182 +#: terminal/models/applet/applet.py:41 msgid "Builtin" msgstr "ビルトイン" @@ -4702,7 +4706,7 @@ msgid "Scope" msgstr "スコープ" #: rbac/models/role.py:46 rbac/models/rolebinding.py:52 -#: users/models/user.py:808 +#: users/models/user.py:810 msgid "Role" msgstr "ロール" @@ -4770,7 +4774,7 @@ msgstr "ワークスペースビュー" msgid "Audit view" msgstr "監査ビュー" -#: rbac/tree.py:27 settings/models.py:158 +#: rbac/tree.py:27 settings/models.py:159 msgid "System setting" msgstr "システム設定" @@ -4807,7 +4811,7 @@ msgid "My assets" msgstr "私の資産" #: rbac/tree.py:58 terminal/models/applet/applet.py:52 -#: terminal/models/applet/applet.py:317 terminal/models/applet/host.py:30 +#: terminal/models/applet/applet.py:316 terminal/models/applet/host.py:30 #: terminal/serializers/applet.py:15 msgid "Applet" msgstr "リモートアプリケーション" @@ -4829,11 +4833,11 @@ msgstr "共通設定" msgid "View permission tree" msgstr "権限ツリーの表示" -#: settings/api/chat.py:36 +#: settings/api/chat.py:40 msgid "Chat AI is not enabled" msgstr "チャットAIがオンになっていない" -#: settings/api/chat.py:74 settings/api/dingtalk.py:31 +#: settings/api/chat.py:78 settings/api/dingtalk.py:31 #: settings/api/feishu.py:36 settings/api/slack.py:34 settings/api/sms.py:160 #: settings/api/vault.py:40 settings/api/wecom.py:37 msgid "Test success" @@ -4870,70 +4874,76 @@ msgstr "携帯番号をテストこのフィールドは必須です" msgid "Settings" msgstr "設定" -#: settings/models.py:35 users/models/preference.py:14 +#: settings/models.py:36 users/models/preference.py:14 msgid "Encrypted" msgstr "暗号化された" -#: settings/models.py:160 +#: settings/models.py:161 msgid "Can change email setting" msgstr "メール設定を変更できます" -#: settings/models.py:161 +#: settings/models.py:162 msgid "Can change auth setting" msgstr "資格認定の設定" -#: settings/models.py:162 +#: settings/models.py:163 msgid "Can change auth ops" msgstr "タスクセンターの設定" -#: settings/models.py:163 +#: settings/models.py:164 msgid "Can change auth ticket" msgstr "製造オーダ設定" -#: settings/models.py:164 +#: settings/models.py:165 msgid "Can change auth announcement" msgstr "公告の設定" -#: settings/models.py:165 +#: settings/models.py:166 msgid "Can change vault setting" msgstr "金庫の設定を変えることができます" -#: settings/models.py:166 +#: settings/models.py:167 msgid "Can change chat ai setting" msgstr "チャットAI設定を変更できます" -#: settings/models.py:167 +#: settings/models.py:168 msgid "Can change system msg sub setting" msgstr "システムmsgサブ设定を変更できます" -#: settings/models.py:168 +#: settings/models.py:169 msgid "Can change sms setting" msgstr "Smsの設定を変えることができます" -#: settings/models.py:169 +#: settings/models.py:170 msgid "Can change security setting" msgstr "セキュリティ設定を変更できます" -#: settings/models.py:170 +#: settings/models.py:171 msgid "Can change clean setting" msgstr "きれいな設定を変えることができます" -#: settings/models.py:171 +#: settings/models.py:172 msgid "Can change interface setting" msgstr "インターフェイスの設定を変えることができます" -#: settings/models.py:172 +#: settings/models.py:173 msgid "Can change license setting" msgstr "ライセンス設定を変更できます" -#: settings/models.py:173 +#: settings/models.py:174 msgid "Can change terminal setting" msgstr "ターミナルの設定を変えることができます" -#: settings/models.py:174 +#: settings/models.py:175 msgid "Can change other setting" msgstr "他の設定を変えることができます" +#: settings/models.py:185 +#, fuzzy +#| msgid "Username prompt" +msgid "Chat prompt" +msgstr "チャットプロンプト" + #: settings/serializers/auth/base.py:12 msgid "LDAP Auth" msgstr "LDAP 認証" @@ -6565,12 +6575,12 @@ msgstr "カスタムプラットフォームのみをサポート" msgid "Missing type in platform.yml" msgstr "platform.ymlにタイプがありません" -#: terminal/models/applet/applet.py:319 terminal/models/applet/host.py:36 +#: terminal/models/applet/applet.py:318 terminal/models/applet/host.py:36 #: terminal/models/applet/host.py:138 msgid "Hosting" msgstr "ホスト マシン" -#: terminal/models/applet/host.py:18 terminal/serializers/applet_host.py:57 +#: terminal/models/applet/host.py:18 terminal/serializers/applet_host.py:69 msgid "Deploy options" msgstr "展開パラメーター" @@ -6938,20 +6948,33 @@ msgstr "RDS 認可モード" msgid "RDS Single Session Per User" msgstr "RDS シングル ユーザー シングル セッション" -#: terminal/serializers/applet_host.py:52 -msgid "RDS Max Disconnection Time" -msgstr "最大切断時間" - #: terminal/serializers/applet_host.py:53 -msgid "RDS Remote App Logoff Time Limit" -msgstr "RDS 远程应用注销时间限制" +msgid "RDS Max Disconnection Time (ms)" +msgstr "最大切断時間(ミリ秒)" -#: terminal/serializers/applet_host.py:59 terminal/serializers/terminal.py:47 +#: terminal/serializers/applet_host.py:55 +msgid "" +"Tips: Set the maximum duration for keeping a disconnected session active on " +"the server (log off the session after 60000 milliseconds)." +msgstr "ヒント:サーバー上で切断されたセッションがアクティブな状態で維持される最大時間を設定します(60000ミリ秒後にセッションをログオフ)。" + +#: terminal/serializers/applet_host.py:60 +msgid "RDS Remote App Logoff Time Limit (ms)" +msgstr "RDSリモートアプリケーションのログアウト時間制限(ミリ秒)" + +#: terminal/serializers/applet_host.py:62 +msgid "" +"Tips: Set the logoff time for RemoteApp sessions after closing all RemoteApp " +"programs (0 milliseconds, log off the session immediately)." +msgstr "" +"ヒント:すべてのRemoteAppプログラムを閉じた後、RemoteAppセッションのログオフ時間を設定します(0ミリ秒、セッションを即座にログオフ)。" + +#: terminal/serializers/applet_host.py:71 terminal/serializers/terminal.py:47 #: terminal/serializers/virtualapp_provider.py:13 msgid "Load status" msgstr "ロードステータス" -#: terminal/serializers/applet_host.py:73 +#: terminal/serializers/applet_host.py:85 msgid "" "These accounts are used to connect to the published application, the account " "is now divided into two types, one is dedicated to each account, each user " @@ -6965,11 +6988,11 @@ msgstr "" "開されています。アプリケーションが複数のオープンをサポートしていない場合、お" "よび特別なものが使用されている場合、公開アカウントが使用されます。" -#: terminal/serializers/applet_host.py:80 +#: terminal/serializers/applet_host.py:92 msgid "The number of public accounts created automatically" msgstr "自動的に作成される公開アカウントの数" -#: terminal/serializers/applet_host.py:83 +#: terminal/serializers/applet_host.py:95 msgid "" "Connect to the host using the same account first. For security reasons, " "please set the configuration item CACHE_LOGIN_PASSWORD_ENABLED=true and " @@ -7120,7 +7143,7 @@ msgstr "エンドポイントサフィックス" msgid "HOST" msgstr "ホスト" -#: terminal/serializers/storage.py:146 users/models/user.py:828 +#: terminal/serializers/storage.py:146 users/models/user.py:830 #: xpack/plugins/cloud/serializers/account_attrs.py:213 msgid "Private key" msgstr "ssh秘密鍵" @@ -7548,7 +7571,7 @@ msgstr "作業指示情報" msgid "Ticket approval" msgstr "作業指示の承認" -#: tickets/templates/tickets/approve_check_password.html:43 +#: tickets/templates/tickets/approve_check_password.html:44 msgid "Approval" msgstr "承認" @@ -7714,7 +7737,7 @@ msgstr "公開鍵は古いものと同じであってはなりません。" msgid "Not a valid ssh public key" msgstr "有効なssh公開鍵ではありません" -#: users/forms/profile.py:173 users/models/user.py:831 +#: users/forms/profile.py:173 users/models/user.py:833 #: xpack/plugins/cloud/serializers/account_attrs.py:210 msgid "Public key" msgstr "公開キー" @@ -7723,74 +7746,74 @@ msgstr "公開キー" msgid "Preference" msgstr "ユーザー設定" -#: users/models/user.py:644 users/serializers/profile.py:94 +#: users/models/user.py:646 users/serializers/profile.py:94 msgid "Force enable" msgstr "強制有効" -#: users/models/user.py:810 users/serializers/user.py:169 +#: users/models/user.py:812 users/serializers/user.py:169 msgid "Is service account" msgstr "サービスアカウントです" -#: users/models/user.py:812 +#: users/models/user.py:814 msgid "Avatar" msgstr "アバター" -#: users/models/user.py:815 +#: users/models/user.py:817 msgid "Wechat" msgstr "微信" -#: users/models/user.py:818 users/serializers/user.py:106 +#: users/models/user.py:820 users/serializers/user.py:106 msgid "Phone" msgstr "電話" -#: users/models/user.py:824 +#: users/models/user.py:826 msgid "OTP secret key" msgstr "OTP 秘密" # msgid "Private key" # msgstr "ssh秘密鍵" -#: users/models/user.py:836 users/serializers/profile.py:128 +#: users/models/user.py:838 users/serializers/profile.py:128 #: users/serializers/user.py:166 msgid "Is first login" msgstr "最初のログインです" -#: users/models/user.py:846 +#: users/models/user.py:848 msgid "Date password last updated" msgstr "最終更新日パスワード" -#: users/models/user.py:849 +#: users/models/user.py:851 msgid "Need update password" msgstr "更新パスワードが必要" -#: users/models/user.py:851 +#: users/models/user.py:853 msgid "Date api key used" msgstr "Api key 最後に使用した日付" -#: users/models/user.py:975 +#: users/models/user.py:977 msgid "Can not delete admin user" msgstr "管理者ユーザーを削除できませんでした" -#: users/models/user.py:1002 +#: users/models/user.py:1004 msgid "Can invite user" msgstr "ユーザーを招待できます" -#: users/models/user.py:1003 +#: users/models/user.py:1005 msgid "Can remove user" msgstr "ユーザーを削除できます" -#: users/models/user.py:1004 +#: users/models/user.py:1006 msgid "Can match user" msgstr "ユーザーに一致できます" -#: users/models/user.py:1013 +#: users/models/user.py:1015 msgid "Administrator" msgstr "管理者" -#: users/models/user.py:1016 +#: users/models/user.py:1018 msgid "Administrator is the super user of system" msgstr "管理者はシステムのスーパーユーザーです" -#: users/models/user.py:1041 +#: users/models/user.py:1043 msgid "User password history" msgstr "ユーザーパスワード履歴" diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index 8e506d7f2..00732f89c 100644 --- a/apps/locale/zh/LC_MESSAGES/django.mo +++ b/apps/locale/zh/LC_MESSAGES/django.mo @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:92bd4bd4a6d342dcd259215f16baeedb17fd95d321d4527bb3601ce1dfd72202 -size 137882 +oid sha256:ef69fc1b0357e29b7cf8f3a2183ef6a35fc018d165921aa9f1c586679a3c6491 +size 138509 diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index 0b1265625..ced9a7d93 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: JumpServer 0.3.3\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-12-13 18:29+0800\n" +"POT-Creation-Date: 2023-12-15 15:13+0800\n" "PO-Revision-Date: 2021-05-20 10:54+0800\n" "Last-Translator: ibuler \n" "Language-Team: JumpServer team\n" @@ -73,7 +73,7 @@ msgstr "同名账号" msgid "Anonymous account" msgstr "匿名账号" -#: accounts/const/account.py:25 users/models/user.py:740 +#: accounts/const/account.py:25 users/models/user.py:742 msgid "Local" msgstr "数据库" @@ -200,7 +200,7 @@ msgstr "仅创建" #: authentication/serializers/password_mfa.py:24 #: notifications/backends/__init__.py:10 settings/serializers/msg.py:22 #: settings/serializers/msg.py:57 users/forms/profile.py:102 -#: users/forms/profile.py:109 users/models/user.py:800 +#: users/forms/profile.py:109 users/models/user.py:802 #: users/templates/users/forgot_password.html:117 #: users/views/profile/reset.py:92 msgid "Email" @@ -275,7 +275,7 @@ msgid "Version" msgstr "版本" #: accounts/models/account.py:57 accounts/serializers/account/account.py:215 -#: users/models/user.py:843 +#: users/models/user.py:845 msgid "Source" msgstr "来源" @@ -475,10 +475,10 @@ msgstr "结束日期" #: accounts/models/automations/change_secret.py:43 #: assets/models/automations/base.py:113 audits/models.py:208 #: audits/serializers.py:54 ops/models/base.py:49 ops/models/job.py:232 -#: terminal/models/applet/applet.py:320 terminal/models/applet/host.py:140 +#: terminal/models/applet/applet.py:319 terminal/models/applet/host.py:140 #: terminal/models/component/status.py:30 #: terminal/models/virtualapp/virtualapp.py:99 -#: terminal/serializers/applet.py:18 terminal/serializers/applet_host.py:124 +#: terminal/serializers/applet.py:18 terminal/serializers/applet_host.py:136 #: terminal/serializers/virtualapp.py:35 tickets/models/ticket/general.py:283 #: tickets/serializers/super_ticket.py:13 #: tickets/serializers/ticket/ticket.py:20 xpack/plugins/cloud/models.py:201 @@ -516,7 +516,7 @@ msgstr "最后登录日期" #: authentication/templates/authentication/_msg_different_city.html:9 #: authentication/templates/authentication/_msg_oauth_bind.html:9 #: terminal/serializers/storage.py:136 users/forms/profile.py:32 -#: users/forms/profile.py:115 users/models/user.py:796 +#: users/forms/profile.py:115 users/models/user.py:798 #: users/templates/users/_msg_user_created.html:12 #: xpack/plugins/cloud/serializers/account_attrs.py:26 msgid "Username" @@ -603,7 +603,7 @@ msgstr "密码规则" #: ops/models/celery.py:57 ops/models/job.py:137 ops/models/playbook.py:29 #: ops/serializers/job.py:19 orgs/models.py:82 #: perms/models/asset_permission.py:61 rbac/models/role.py:29 -#: settings/models.py:32 settings/serializers/msg.py:82 +#: settings/models.py:33 settings/models.py:180 settings/serializers/msg.py:82 #: terminal/models/applet/applet.py:33 terminal/models/component/endpoint.py:12 #: terminal/models/component/endpoint.py:95 #: terminal/models/component/storage.py:26 terminal/models/component/task.py:13 @@ -611,7 +611,7 @@ msgstr "密码规则" #: terminal/models/virtualapp/provider.py:10 #: terminal/models/virtualapp/virtualapp.py:19 tickets/api/ticket.py:87 #: users/forms/profile.py:33 users/models/group.py:13 -#: users/models/preference.py:11 users/models/user.py:798 +#: users/models/preference.py:11 users/models/user.py:800 #: xpack/plugins/cloud/models.py:32 xpack/plugins/cloud/models.py:273 #: xpack/plugins/cloud/serializers/task.py:68 msgid "Name" @@ -740,7 +740,7 @@ msgstr "账号存在策略" #: assets/models/label.py:21 assets/models/platform.py:96 #: assets/serializers/asset/common.py:122 assets/serializers/cagegory.py:12 #: assets/serializers/platform.py:140 assets/serializers/platform.py:236 -#: perms/serializers/user_permission.py:25 settings/models.py:34 +#: perms/serializers/user_permission.py:25 settings/models.py:35 #: tickets/models/ticket/apply_application.py:13 users/models/preference.py:12 msgid "Category" msgstr "类别" @@ -829,8 +829,8 @@ msgstr "ID" #: terminal/notifications.py:205 terminal/serializers/command.py:16 #: terminal/templates/terminal/_msg_command_warning.html:6 #: terminal/templates/terminal/_msg_session_sharing.html:6 -#: tickets/models/comment.py:21 users/const.py:14 users/models/user.py:994 -#: users/models/user.py:1031 users/serializers/group.py:19 +#: tickets/models/comment.py:21 users/const.py:14 users/models/user.py:996 +#: users/models/user.py:1033 users/serializers/group.py:19 msgid "User" msgstr "用户" @@ -920,13 +920,13 @@ msgstr "关联平台,可配置推送参数,如果不关联,将使用默认 #: assets/models/cmd_filter.py:40 assets/models/cmd_filter.py:88 #: assets/models/group.py:20 common/db/models.py:36 ops/models/adhoc.py:26 #: ops/models/job.py:157 ops/models/playbook.py:32 rbac/models/role.py:37 -#: settings/models.py:37 terminal/models/applet/applet.py:45 -#: terminal/models/applet/applet.py:321 terminal/models/applet/host.py:143 +#: settings/models.py:38 terminal/models/applet/applet.py:45 +#: terminal/models/applet/applet.py:320 terminal/models/applet/host.py:143 #: terminal/models/component/endpoint.py:25 #: terminal/models/component/endpoint.py:105 #: terminal/models/session/session.py:46 #: terminal/models/virtualapp/virtualapp.py:28 tickets/models/comment.py:32 -#: tickets/models/ticket/general.py:297 users/models/user.py:834 +#: tickets/models/ticket/general.py:297 users/models/user.py:836 #: xpack/plugins/cloud/models.py:39 xpack/plugins/cloud/models.py:109 msgid "Comment" msgstr "备注" @@ -1057,7 +1057,7 @@ msgid "Acls" msgstr "访问控制" #: acls/const.py:6 audits/const.py:36 terminal/const.py:11 tickets/const.py:45 -#: tickets/templates/tickets/approve_check_password.html:47 +#: tickets/templates/tickets/approve_check_password.html:49 msgid "Reject" msgstr "拒绝" @@ -1127,7 +1127,8 @@ msgid "Regex" msgstr "正则表达式" #: acls/models/command_acl.py:26 assets/models/cmd_filter.py:79 -#: settings/serializers/feature.py:19 xpack/plugins/license/models.py:30 +#: settings/models.py:181 settings/serializers/feature.py:19 +#: xpack/plugins/license/models.py:30 msgid "Content" msgstr "内容" @@ -1610,18 +1611,18 @@ msgstr "SSH公钥" #: assets/models/_user.py:28 assets/models/automations/base.py:114 #: assets/models/cmd_filter.py:41 assets/models/group.py:19 #: audits/models.py:267 common/db/models.py:34 ops/models/base.py:54 -#: ops/models/job.py:239 users/models/user.py:1032 +#: ops/models/job.py:239 users/models/user.py:1034 msgid "Date created" msgstr "创建日期" #: assets/models/_user.py:29 assets/models/cmd_filter.py:42 -#: common/db/models.py:35 users/models/user.py:852 +#: common/db/models.py:35 users/models/user.py:854 msgid "Date updated" msgstr "更新日期" #: assets/models/_user.py:30 assets/models/cmd_filter.py:44 #: assets/models/cmd_filter.py:91 assets/models/group.py:18 -#: common/db/models.py:32 users/models/user.py:841 +#: common/db/models.py:32 users/models/user.py:843 #: users/serializers/group.py:30 msgid "Created by" msgstr "创建者" @@ -1810,7 +1811,7 @@ msgstr "校验日期" #: assets/models/cmd_filter.py:28 perms/models/asset_permission.py:66 #: perms/serializers/permission.py:33 users/models/group.py:25 -#: users/models/user.py:804 +#: users/models/user.py:806 msgid "User group" msgstr "用户组" @@ -1860,7 +1861,7 @@ msgstr "默认" msgid "Default asset group" msgstr "默认资产组" -#: assets/models/label.py:15 rbac/const.py:6 users/models/user.py:1017 +#: assets/models/label.py:15 rbac/const.py:6 users/models/user.py:1019 msgid "System" msgstr "系统" @@ -1869,7 +1870,7 @@ msgstr "系统" #: assets/serializers/cagegory.py:24 #: authentication/models/connection_token.py:29 #: authentication/serializers/connect_token_secret.py:125 -#: common/serializers/common.py:86 labels/models.py:12 settings/models.py:33 +#: common/serializers/common.py:86 labels/models.py:12 settings/models.py:34 #: users/models/preference.py:13 msgid "Value" msgstr "值" @@ -1926,7 +1927,7 @@ msgid "Setting" msgstr "设置" #: assets/models/platform.py:39 audits/const.py:56 -#: authentication/backends/passkey/models.py:11 settings/models.py:36 +#: authentication/backends/passkey/models.py:11 settings/models.py:37 #: terminal/serializers/applet_host.py:33 msgid "Enabled" msgstr "启用" @@ -2497,7 +2498,7 @@ msgstr "登录 IP" #: audits/models.py:200 audits/serializers.py:52 #: authentication/templates/authentication/_mfa_confirm_modal.html:14 -#: users/forms/profile.py:65 users/models/user.py:821 +#: users/forms/profile.py:65 users/models/user.py:823 #: users/serializers/profile.py:102 msgid "MFA" msgstr "MFA" @@ -2545,7 +2546,7 @@ msgstr "用户 %s %s 了当前资源" #: audits/serializers.py:172 authentication/models/connection_token.py:47 #: authentication/models/temp_token.py:13 perms/models/asset_permission.py:80 #: tickets/models/ticket/apply_application.py:31 -#: tickets/models/ticket/apply_asset.py:20 users/models/user.py:839 +#: tickets/models/ticket/apply_asset.py:20 users/models/user.py:841 msgid "Date expired" msgstr "失效日期" @@ -2578,30 +2579,30 @@ msgstr "认证令牌" #: audits/signal_handlers/login_log.py:37 authentication/notifications.py:73 #: authentication/views/login.py:77 notifications/backends/__init__.py:11 -#: settings/serializers/auth/wecom.py:10 users/models/user.py:747 -#: users/models/user.py:853 +#: settings/serializers/auth/wecom.py:10 users/models/user.py:749 +#: users/models/user.py:855 msgid "WeCom" msgstr "企业微信" #: audits/signal_handlers/login_log.py:38 authentication/views/feishu.py:87 #: authentication/views/login.py:89 notifications/backends/__init__.py:14 #: settings/serializers/auth/feishu.py:10 -#: settings/serializers/auth/feishu.py:13 users/models/user.py:749 -#: users/models/user.py:855 +#: settings/serializers/auth/feishu.py:13 users/models/user.py:751 +#: users/models/user.py:857 msgid "FeiShu" msgstr "飞书" #: audits/signal_handlers/login_log.py:39 authentication/views/login.py:95 #: authentication/views/slack.py:87 notifications/backends/__init__.py:15 -#: settings/serializers/auth/slack.py:10 users/models/user.py:750 -#: users/models/user.py:856 +#: settings/serializers/auth/slack.py:10 users/models/user.py:752 +#: users/models/user.py:858 msgid "Slack" msgstr "" #: audits/signal_handlers/login_log.py:40 authentication/views/dingtalk.py:160 #: authentication/views/login.py:83 notifications/backends/__init__.py:12 -#: settings/serializers/auth/dingtalk.py:10 users/models/user.py:748 -#: users/models/user.py:854 +#: settings/serializers/auth/dingtalk.py:10 users/models/user.py:750 +#: users/models/user.py:856 msgid "DingTalk" msgstr "钉钉" @@ -3100,11 +3101,11 @@ msgstr "没有用户或用户失效" msgid "No asset or inactive asset" msgstr "没有资产或资产未激活" -#: authentication/models/connection_token.py:274 +#: authentication/models/connection_token.py:269 msgid "Can view super connection token secret" msgstr "可以查看超级连接令牌密文" -#: authentication/models/connection_token.py:276 +#: authentication/models/connection_token.py:271 msgid "Super connection token" msgstr "超级连接令牌" @@ -3221,13 +3222,13 @@ msgid "Show" msgstr "显示" #: authentication/templates/authentication/_access_key_modal.html:66 -#: users/const.py:37 users/models/user.py:642 users/serializers/profile.py:92 +#: users/const.py:37 users/models/user.py:644 users/serializers/profile.py:92 #: users/templates/users/user_verify_mfa.html:36 msgid "Disable" msgstr "禁用" #: authentication/templates/authentication/_access_key_modal.html:67 -#: users/const.py:38 users/models/user.py:643 users/serializers/profile.py:93 +#: users/const.py:38 users/models/user.py:645 users/serializers/profile.py:93 #: users/templates/users/mfa_setting.html:26 #: users/templates/users/mfa_setting.html:68 msgid "Enable" @@ -3686,7 +3687,7 @@ msgstr "忽略的" msgid "discard time" msgstr "忽略时间" -#: common/db/models.py:33 users/models/user.py:842 +#: common/db/models.py:33 users/models/user.py:844 msgid "Updated by" msgstr "最后更新者" @@ -4263,7 +4264,7 @@ msgstr "Material" msgid "Material Type" msgstr "Material 类型" -#: ops/models/job.py:565 +#: ops/models/job.py:562 msgid "Job Execution" msgstr "作业执行" @@ -4434,7 +4435,8 @@ msgstr "默认组织" msgid "SYSTEM" msgstr "系统组织" -#: orgs/models.py:83 rbac/models/role.py:36 terminal/models/applet/applet.py:41 +#: orgs/models.py:83 rbac/models/role.py:36 settings/models.py:182 +#: terminal/models/applet/applet.py:41 msgid "Builtin" msgstr "内置的" @@ -4653,7 +4655,7 @@ msgid "Scope" msgstr "范围" #: rbac/models/role.py:46 rbac/models/rolebinding.py:52 -#: users/models/user.py:808 +#: users/models/user.py:810 msgid "Role" msgstr "角色" @@ -4720,7 +4722,7 @@ msgstr "工作台" msgid "Audit view" msgstr "审计台" -#: rbac/tree.py:27 settings/models.py:158 +#: rbac/tree.py:27 settings/models.py:159 msgid "System setting" msgstr "系统设置" @@ -4757,7 +4759,7 @@ msgid "My assets" msgstr "我的资产" #: rbac/tree.py:58 terminal/models/applet/applet.py:52 -#: terminal/models/applet/applet.py:317 terminal/models/applet/host.py:30 +#: terminal/models/applet/applet.py:316 terminal/models/applet/host.py:30 #: terminal/serializers/applet.py:15 msgid "Applet" msgstr "远程应用" @@ -4779,11 +4781,11 @@ msgstr "一般设置" msgid "View permission tree" msgstr "查看授权树" -#: settings/api/chat.py:36 +#: settings/api/chat.py:40 msgid "Chat AI is not enabled" msgstr "聊天 AI 没有开启" -#: settings/api/chat.py:74 settings/api/dingtalk.py:31 +#: settings/api/chat.py:78 settings/api/dingtalk.py:31 #: settings/api/feishu.py:36 settings/api/slack.py:34 settings/api/sms.py:160 #: settings/api/vault.py:40 settings/api/wecom.py:37 msgid "Test success" @@ -4818,70 +4820,74 @@ msgstr "测试手机号 该字段是必填项。" msgid "Settings" msgstr "系统设置" -#: settings/models.py:35 users/models/preference.py:14 +#: settings/models.py:36 users/models/preference.py:14 msgid "Encrypted" msgstr "加密的" -#: settings/models.py:160 +#: settings/models.py:161 msgid "Can change email setting" msgstr "邮件设置" -#: settings/models.py:161 +#: settings/models.py:162 msgid "Can change auth setting" msgstr "认证设置" -#: settings/models.py:162 +#: settings/models.py:163 msgid "Can change auth ops" msgstr "任务中心设置" -#: settings/models.py:163 +#: settings/models.py:164 msgid "Can change auth ticket" msgstr "工单设置" -#: settings/models.py:164 +#: settings/models.py:165 msgid "Can change auth announcement" msgstr "公告设置" -#: settings/models.py:165 +#: settings/models.py:166 msgid "Can change vault setting" msgstr "可以更改 vault 设置" -#: settings/models.py:166 +#: settings/models.py:167 msgid "Can change chat ai setting" msgstr "可以修改聊天 AI 设置" -#: settings/models.py:167 +#: settings/models.py:168 msgid "Can change system msg sub setting" msgstr "消息订阅设置" -#: settings/models.py:168 +#: settings/models.py:169 msgid "Can change sms setting" msgstr "短信设置" -#: settings/models.py:169 +#: settings/models.py:170 msgid "Can change security setting" msgstr "安全设置" -#: settings/models.py:170 +#: settings/models.py:171 msgid "Can change clean setting" msgstr "定期清理" -#: settings/models.py:171 +#: settings/models.py:172 msgid "Can change interface setting" msgstr "界面设置" -#: settings/models.py:172 +#: settings/models.py:173 msgid "Can change license setting" msgstr "许可证设置" -#: settings/models.py:173 +#: settings/models.py:174 msgid "Can change terminal setting" msgstr "终端设置" -#: settings/models.py:174 +#: settings/models.py:175 msgid "Can change other setting" msgstr "其它设置" +#: settings/models.py:185 +msgid "Chat prompt" +msgstr "聊天提示" + #: settings/serializers/auth/base.py:12 msgid "LDAP Auth" msgstr "LDAP 认证" @@ -6475,12 +6481,12 @@ msgstr "只支持自定义平台" msgid "Missing type in platform.yml" msgstr "在 platform.yml 中缺少类型" -#: terminal/models/applet/applet.py:319 terminal/models/applet/host.py:36 +#: terminal/models/applet/applet.py:318 terminal/models/applet/host.py:36 #: terminal/models/applet/host.py:138 msgid "Hosting" msgstr "宿主机" -#: terminal/models/applet/host.py:18 terminal/serializers/applet_host.py:57 +#: terminal/models/applet/host.py:18 terminal/serializers/applet_host.py:69 msgid "Deploy options" msgstr "部署参数" @@ -6846,20 +6852,33 @@ msgstr "RDS 授权模式" msgid "RDS Single Session Per User" msgstr "RDS 单用户单会话" -#: terminal/serializers/applet_host.py:52 -msgid "RDS Max Disconnection Time" -msgstr "RDS 最大断开时间" - #: terminal/serializers/applet_host.py:53 -msgid "RDS Remote App Logoff Time Limit" -msgstr "RDS 远程应用注销时间限制" +msgid "RDS Max Disconnection Time (ms)" +msgstr "RDS 最大断开时间(毫秒)" -#: terminal/serializers/applet_host.py:59 terminal/serializers/terminal.py:47 +#: terminal/serializers/applet_host.py:55 +msgid "" +"Tips: Set the maximum duration for keeping a disconnected session active on " +"the server (log off the session after 60000 milliseconds)." +msgstr "提示:设置某个已断开连接的会话在服务器上能保持活动状态的最长时间(60000 毫秒后注销会话)" + +#: terminal/serializers/applet_host.py:60 +msgid "RDS Remote App Logoff Time Limit (ms)" +msgstr "RDS 远程应用注销时间限制(毫秒)" + +msgid "" +"Tips: Set the logoff time for RemoteApp sessions after closing all RemoteApp " +"programs (0 milliseconds, log off the session immediately)." +msgstr "" +"提示:关闭所有 RemoteApp 程序之后设置 RemoteAPP 会话的注销时间(0 毫秒,立即" +"注销会话)" + +#: terminal/serializers/applet_host.py:71 terminal/serializers/terminal.py:47 #: terminal/serializers/virtualapp_provider.py:13 msgid "Load status" msgstr "负载状态" -#: terminal/serializers/applet_host.py:73 +#: terminal/serializers/applet_host.py:85 msgid "" "These accounts are used to connect to the published application, the account " "is now divided into two types, one is dedicated to each account, each user " @@ -6872,11 +6891,11 @@ msgstr "" "使用公共账号连接;
    注意: 如果不开启自动创建账号, 当前发布机仅能被指定标" "签的资产调度到,默认不会放到调度池中" -#: terminal/serializers/applet_host.py:80 +#: terminal/serializers/applet_host.py:92 msgid "The number of public accounts created automatically" msgstr "公用账号自动创建的数量" -#: terminal/serializers/applet_host.py:83 +#: terminal/serializers/applet_host.py:95 msgid "" "Connect to the host using the same account first. For security reasons, " "please set the configuration item CACHE_LOGIN_PASSWORD_ENABLED=true and " @@ -7023,7 +7042,7 @@ msgstr "端点后缀" msgid "HOST" msgstr "主机" -#: terminal/serializers/storage.py:146 users/models/user.py:828 +#: terminal/serializers/storage.py:146 users/models/user.py:830 #: xpack/plugins/cloud/serializers/account_attrs.py:213 msgid "Private key" msgstr "ssh私钥" @@ -7447,7 +7466,7 @@ msgstr "工单信息" msgid "Ticket approval" msgstr "工单审批" -#: tickets/templates/tickets/approve_check_password.html:43 +#: tickets/templates/tickets/approve_check_password.html:44 msgid "Approval" msgstr "同意" @@ -7611,7 +7630,7 @@ msgstr "不能和原来的密钥相同" msgid "Not a valid ssh public key" msgstr "SSH密钥不合法" -#: users/forms/profile.py:173 users/models/user.py:831 +#: users/forms/profile.py:173 users/models/user.py:833 #: xpack/plugins/cloud/serializers/account_attrs.py:210 msgid "Public key" msgstr "SSH公钥" @@ -7620,74 +7639,74 @@ msgstr "SSH公钥" msgid "Preference" msgstr "用户设置" -#: users/models/user.py:644 users/serializers/profile.py:94 +#: users/models/user.py:646 users/serializers/profile.py:94 msgid "Force enable" msgstr "强制启用" -#: users/models/user.py:810 users/serializers/user.py:169 +#: users/models/user.py:812 users/serializers/user.py:169 msgid "Is service account" msgstr "服务账号" -#: users/models/user.py:812 +#: users/models/user.py:814 msgid "Avatar" msgstr "头像" -#: users/models/user.py:815 +#: users/models/user.py:817 msgid "Wechat" msgstr "微信" -#: users/models/user.py:818 users/serializers/user.py:106 +#: users/models/user.py:820 users/serializers/user.py:106 msgid "Phone" msgstr "手机" -#: users/models/user.py:824 +#: users/models/user.py:826 msgid "OTP secret key" msgstr "OTP 密钥" # msgid "Private key" # msgstr "ssh私钥" -#: users/models/user.py:836 users/serializers/profile.py:128 +#: users/models/user.py:838 users/serializers/profile.py:128 #: users/serializers/user.py:166 msgid "Is first login" msgstr "首次登录" -#: users/models/user.py:846 +#: users/models/user.py:848 msgid "Date password last updated" msgstr "最后更新密码日期" -#: users/models/user.py:849 +#: users/models/user.py:851 msgid "Need update password" msgstr "需要更新密码" -#: users/models/user.py:851 +#: users/models/user.py:853 msgid "Date api key used" msgstr "Api key 最后使用日期" -#: users/models/user.py:975 +#: users/models/user.py:977 msgid "Can not delete admin user" msgstr "无法删除管理员用户" -#: users/models/user.py:1002 +#: users/models/user.py:1004 msgid "Can invite user" msgstr "可以邀请用户" -#: users/models/user.py:1003 +#: users/models/user.py:1005 msgid "Can remove user" msgstr "可以移除用户" -#: users/models/user.py:1004 +#: users/models/user.py:1006 msgid "Can match user" msgstr "可以匹配用户" -#: users/models/user.py:1013 +#: users/models/user.py:1015 msgid "Administrator" msgstr "管理员" -#: users/models/user.py:1016 +#: users/models/user.py:1018 msgid "Administrator is the super user of system" msgstr "Administrator是初始的超级管理员" -#: users/models/user.py:1041 +#: users/models/user.py:1043 msgid "User password history" msgstr "用户密码历史" diff --git a/apps/terminal/serializers/applet_host.py b/apps/terminal/serializers/applet_host.py index e3e435d76..146cfd3bc 100644 --- a/apps/terminal/serializers/applet_host.py +++ b/apps/terminal/serializers/applet_host.py @@ -49,8 +49,20 @@ class DeployOptionsSerializer(serializers.Serializer): RDS_LicensingMode = serializers.ChoiceField(choices=LICENSE_MODE_CHOICES, default=2, label=_('RDS Licensing Mode')) RDS_fSingleSessionPerUser = serializers.ChoiceField(choices=SESSION_PER_USER, default=1, label=_("RDS Single Session Per User")) - RDS_MaxDisconnectionTime = serializers.IntegerField(default=60000, label=_("RDS Max Disconnection Time")) - RDS_RemoteAppLogoffTimeLimit = serializers.IntegerField(default=0, label=_("RDS Remote App Logoff Time Limit")) + RDS_MaxDisconnectionTime = serializers.IntegerField( + default=60000, label=_("RDS Max Disconnection Time (ms)"), + help_text=_( + 'Tips: Set the maximum duration for keeping a disconnected session active on the server (log off the ' + 'session after 60000 milliseconds).' + ) + ) + RDS_RemoteAppLogoffTimeLimit = serializers.IntegerField( + default=0, label=_("RDS Remote App Logoff Time Limit (ms)"), + help_text=_( + 'Tips: Set the logoff time for RemoteApp sessions after closing all RemoteApp programs (0 milliseconds, ' + 'log off the session immediately).' + ) + ) class AppletHostSerializer(HostSerializer): From 9d0da64ea134c12ae0cf5f7acc1939270910a924 Mon Sep 17 00:00:00 2001 From: feng <1304903146@qq.com> Date: Fri, 15 Dec 2023 17:14:34 +0800 Subject: [PATCH 073/111] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E6=A0=A1?= =?UTF-8?q?=E9=AA=8C=E5=AF=86=E7=A0=81=E8=A7=84=E5=88=99=20=E7=89=B9?= =?UTF-8?q?=E6=AE=8A=E5=AD=97=E7=AC=A6=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/users/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/users/utils.py b/apps/users/utils.py index 6db4943de..9ddb5509d 100644 --- a/apps/users/utils.py +++ b/apps/users/utils.py @@ -95,7 +95,7 @@ def check_password_rules(password, is_org_admin=False): if settings.SECURITY_PASSWORD_NUMBER: pattern += '(?=.*\d)' if settings.SECURITY_PASSWORD_SPECIAL_CHAR: - pattern += '(?=.*[`~!@#\$%\^&\*\(\)-=_\+\[\]\{\}\|;:\'\",\.<>\/\?])' + pattern += '(?=.*[`~!@#$%^&*()\-=_+\[\]{}|;:\'",.<>/?])' pattern += '[a-zA-Z\d`~!@#\$%\^&\*\(\)-=_\+\[\]\{\}\|;:\'\",\.<>\/\?]' if is_org_admin: min_length = settings.SECURITY_ADMIN_USER_PASSWORD_MIN_LENGTH From 6955a3db11eb703b51b75eff43d6c78849ad587f Mon Sep 17 00:00:00 2001 From: wangruidong <940853815@qq.com> Date: Fri, 15 Dec 2023 17:10:51 +0800 Subject: [PATCH 074/111] =?UTF-8?q?perf:=20ldap=E6=B5=8B=E8=AF=95=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E6=8F=90=E7=A4=BA=E4=BC=98=E5=8C=96&=E4=B8=8A?= =?UTF-8?q?=E4=BA=A7=E6=96=87=E4=BB=B6=E5=90=8D=E9=95=BF=E5=BA=A6=E9=99=90?= =?UTF-8?q?=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/ops/serializers/job.py | 2 +- apps/settings/utils/ldap.py | 2 +- apps/settings/ws.py | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/ops/serializers/job.py b/apps/ops/serializers/job.py index 3bb91d463..6e95a1fb9 100644 --- a/apps/ops/serializers/job.py +++ b/apps/ops/serializers/job.py @@ -52,7 +52,7 @@ class JobSerializer(ResourceLabelsMixin, BulkOrgResourceModelSerializer, PeriodT class FileSerializer(serializers.Serializer): - files = serializers.FileField(allow_empty_file=True) + files = serializers.FileField(allow_empty_file=False, max_length=128) class Meta: ref_name = "JobFileSerializer" diff --git a/apps/settings/utils/ldap.py b/apps/settings/utils/ldap.py index 60babfa4e..23b7b3882 100644 --- a/apps/settings/utils/ldap.py +++ b/apps/settings/utils/ldap.py @@ -649,7 +649,7 @@ class LDAPTestUtil(object): def _test_before_login_check(self, username, password): from settings.ws import CACHE_KEY_LDAP_TEST_CONFIG_TASK_STATUS, TASK_STATUS_IS_OVER - if cache.get(CACHE_KEY_LDAP_TEST_CONFIG_TASK_STATUS) != TASK_STATUS_IS_OVER: + if not cache.get(CACHE_KEY_LDAP_TEST_CONFIG_TASK_STATUS): raise self.LDAPBeforeLoginCheckError(_('Please test the connection first')) backend = LDAPAuthorizationBackend() diff --git a/apps/settings/ws.py b/apps/settings/ws.py index 38bc54af0..0f8f344fe 100644 --- a/apps/settings/ws.py +++ b/apps/settings/ws.py @@ -161,8 +161,8 @@ class LdapWebsocket(AsyncJsonWebsocketConsumer): return cache.get(task_key) == TASK_STATUS_IS_OVER @staticmethod - def set_task_status_over(task_key): - cache.set(task_key, TASK_STATUS_IS_OVER, 120) + def set_task_status_over(task_key, ttl=120): + cache.set(task_key, TASK_STATUS_IS_OVER, ttl) @staticmethod def set_task_msg(task_key, ok, msg): @@ -192,7 +192,7 @@ class LdapWebsocket(AsyncJsonWebsocketConsumer): username = serializer.validated_data['username'] password = serializer.validated_data['password'] ok, msg = LDAPTestUtil().test_login(username, password) - self.set_task_status_over(CACHE_KEY_LDAP_TEST_LOGIN_TASK_STATUS) + self.set_task_status_over(CACHE_KEY_LDAP_TEST_LOGIN_TASK_STATUS, 3) self.set_task_msg(CACHE_KEY_LDAP_TEST_LOGIN_MSG, ok, msg) def run_sync_user(self, data): From 84b316e2c18c1a394eb7bbbc132c860bd4b57379 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Fri, 15 Dec 2023 18:28:24 +0800 Subject: [PATCH 075/111] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E7=A6=81=E7=94=A8=E7=94=A8=E6=88=B7=E9=BB=98=E8=AE=A4?= =?UTF-8?q?=E6=8E=92=E9=99=A4=20admin=20=E7=94=A8=E6=88=B7=20(#12346)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bai --- apps/users/tasks.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/users/tasks.py b/apps/users/tasks.py index d716806b2..638e3bdec 100644 --- a/apps/users/tasks.py +++ b/apps/users/tasks.py @@ -102,7 +102,8 @@ def check_unused_users(): .filter(date_joined__lt=t) \ .filter(is_active=True) \ .filter(last_login_q) \ - .filter(api_key_q) + .filter(api_key_q) \ + .exclude(username='admin') if not users: return From 714c44fbf4455f651f0dc5e4e863d26c90a2ca49 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Fri, 15 Dec 2023 18:28:48 +0800 Subject: [PATCH 076/111] =?UTF-8?q?perf:=20=E6=8E=88=E6=9D=83=E5=88=9B?= =?UTF-8?q?=E5=BB=BA=E6=97=B6=20=E9=80=9A=E8=BF=87=E6=A8=A1=E7=89=88?= =?UTF-8?q?=E5=88=9B=E5=BB=BA=E8=B4=A6=E5=8F=B7=20=E7=BB=99=E8=B4=A6?= =?UTF-8?q?=E5=8F=B7=E6=B7=BB=E5=8A=A0=E6=9D=A5=E6=BA=90=20(#12345)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/perms/serializers/permission.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/apps/perms/serializers/permission.py b/apps/perms/serializers/permission.py index 1e0384b1b..d09c33538 100644 --- a/apps/perms/serializers/permission.py +++ b/apps/perms/serializers/permission.py @@ -4,6 +4,7 @@ from django.db.models import Q from django.utils.translation import gettext_lazy as _ from rest_framework import serializers +from accounts.const import Source from accounts.models import AccountTemplate, Account from accounts.tasks import push_accounts_to_assets_task from assets.models import Asset, Node @@ -85,6 +86,7 @@ class AssetPermissionSerializer(ResourceLabelsMixin, BulkOrgResourceModelSeriali ] for asset in assets: asset_exist_accounts = Account.objects.none() + asset_exist_account_names = asset.accounts.values_list('name', flat=True) for template in self.template_accounts: asset_exist_accounts |= asset.accounts.filter( username=template.username, @@ -96,11 +98,13 @@ class AssetPermissionSerializer(ResourceLabelsMixin, BulkOrgResourceModelSeriali 'username': template.username, 'secret_type': template.secret_type } - if condition in username_secret_type_dict: + if condition in username_secret_type_dict or \ + template.name in asset_exist_account_names: continue account_data = {key: getattr(template, key) for key in account_attribute} account_data['su_from'] = template.get_su_from_account(asset) - account_data['name'] = f"{account_data['name']}-{_('Account template')}" + account_data['source'] = Source.TEMPLATE + account_data['source_id'] = str(template.id) need_create_accounts.append(Account(**{'asset_id': asset.id, **account_data})) return Account.objects.bulk_create(need_create_accounts) From f7fee0f43068da740138b3c7b8eb894ec0480ecf Mon Sep 17 00:00:00 2001 From: ibuler Date: Fri, 15 Dec 2023 18:30:21 +0800 Subject: [PATCH 077/111] =?UTF-8?q?perf:=20=E4=BF=AE=E5=A4=8D=E6=A0=87?= =?UTF-8?q?=E7=AD=BE=E6=90=9C=E7=B4=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/common/drf/filters.py | 4 ++-- apps/common/serializers/fields.py | 8 ++++++-- apps/labels/api.py | 16 +++++++++++----- apps/users/models/user.py | 7 +++++++ 4 files changed, 26 insertions(+), 9 deletions(-) diff --git a/apps/common/drf/filters.py b/apps/common/drf/filters.py index c58c289ab..6df636fdf 100644 --- a/apps/common/drf/filters.py +++ b/apps/common/drf/filters.py @@ -192,9 +192,9 @@ class LabelFilterBackend(filters.BaseFilterBackend): kwargs = {} if ':' in label_id: k, v = label_id.split(':', 1) - kwargs['label__name'] = k + kwargs['label__name'] = k.strip() if v != '*': - kwargs['label__value'] = v + kwargs['label__value'] = v.strip() else: kwargs['label_id'] = label_id diff --git a/apps/common/serializers/fields.py b/apps/common/serializers/fields.py index 3054a5af4..bb3bafbf6 100644 --- a/apps/common/serializers/fields.py +++ b/apps/common/serializers/fields.py @@ -122,8 +122,12 @@ class LabelRelatedField(serializers.RelatedField): from labels.models import LabeledResource, Label if data is None: return data - k, v = data.split(":", 1) - label, __ = Label.objects.get_or_create(name=k, value=v, defaults={'name': k, 'value': v}) + if isinstance(data, dict): + pk = data.get("id") or data.get("pk") + label = Label.objects.get(pk=pk) + else: + k, v = data.split(":", 1) + label, __ = Label.objects.get_or_create(name=k, value=v, defaults={'name': k, 'value': v}) return LabeledResource(label=label) diff --git a/apps/labels/api.py b/apps/labels/api.py index bd2685036..26ef68685 100644 --- a/apps/labels/api.py +++ b/apps/labels/api.py @@ -3,7 +3,6 @@ from rest_framework.decorators import action from rest_framework.response import Response from common.api.generic import JMSModelViewSet -from common.utils import is_true from orgs.mixins.api import OrgBulkModelViewSet from orgs.mixins.models import OrgModelMixin from orgs.utils import current_org @@ -55,6 +54,8 @@ class ContentTypeViewSet(JMSModelViewSet): if issubclass(model, OrgModelMixin): queryset = model.objects.filter(org_id=current_org.id) + elif hasattr(model, 'get_queryset'): + queryset = model.get_queryset() else: queryset = model.objects.all() @@ -78,14 +79,19 @@ class LabelContentTypeResourceViewSet(JMSModelViewSet): label = get_object_or_404(Label, pk=label_pk) content_type = get_object_or_404(ContentType, id=res_type) bound = self.request.query_params.get('bound', '1') - res_ids = LabeledResource.objects.filter(res_type=content_type, label=label) \ + res_ids = LabeledResource.objects \ + .filter(res_type=content_type, label=label) \ .values_list('res_id', flat=True) res_ids = set(res_ids) model = content_type.model_class() - if is_true(bound): - queryset = model.objects.filter(id__in=list(res_ids)) + if hasattr(model, 'get_queryset'): + queryset = model.get_queryset() else: - queryset = model.objects.exclude(id__in=list(res_ids)) + queryset = model.objects.all() + if bound == '1': + queryset = queryset.filter(id__in=list(res_ids)) + elif bound == '0': + queryset = queryset.exclude(id__in=list(res_ids)) keyword = self.request.query_params.get('search') if keyword: queryset = content_type.filter_queryset(queryset, keyword) diff --git a/apps/users/models/user.py b/apps/users/models/user.py index 5f3739400..bf03dadce 100644 --- a/apps/users/models/user.py +++ b/apps/users/models/user.py @@ -862,6 +862,13 @@ class User(AuthMixin, TokenMixin, RoleMixin, MFAMixin, LabeledMixin, JSONFilterM def __str__(self): return '{0.name}({0.username})'.format(self) + @classmethod + def get_queryset(cls): + queryset = cls.objects.all() + if not current_org.is_root(): + queryset = current_org.get_members() + return queryset + @property def secret_key(self): instance = self.preferences.filter(name='secret_key').first() From 006faac3264afb6c1e47df8658ee23c1bf75113d Mon Sep 17 00:00:00 2001 From: halo Date: Sat, 16 Dec 2023 22:12:34 +0800 Subject: [PATCH 078/111] =?UTF-8?q?perf:=20=E9=85=8D=E7=BD=AExpack?= =?UTF-8?q?=E5=90=8Elogo=E6=B2=A1=E6=9C=89=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/authentication/templates/authentication/login.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/authentication/templates/authentication/login.html b/apps/authentication/templates/authentication/login.html index 7e644c047..4d7489034 100644 --- a/apps/authentication/templates/authentication/login.html +++ b/apps/authentication/templates/authentication/login.html @@ -286,9 +286,9 @@
    From 1f2db65dba30ad2e42829baf3cc8ca648e89fa80 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Mon, 18 Dec 2023 17:31:35 +0800 Subject: [PATCH 079/111] =?UTF-8?q?fix:=20ansible=20=E5=AF=86=E7=A0=81?= =?UTF-8?q?=E6=94=AF=E6=8C=81=20{{=20}}=20{%=20%}=20(#12354)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- .../automations/change_secret/manager.py | 2 +- .../automations/verify_account/manager.py | 2 +- apps/accounts/models/account.py | 20 +++++++++++++++---- apps/accounts/utils.py | 13 +----------- apps/ops/ansible/inventory.py | 11 +++++----- 5 files changed, 25 insertions(+), 23 deletions(-) diff --git a/apps/accounts/automations/change_secret/manager.py b/apps/accounts/automations/change_secret/manager.py index adc840c3f..46d3ef2b6 100644 --- a/apps/accounts/automations/change_secret/manager.py +++ b/apps/accounts/automations/change_secret/manager.py @@ -139,7 +139,7 @@ class ChangeSecretManager(AccountBasePlaybookManager): 'name': account.name, 'username': account.username, 'secret_type': secret_type, - 'secret': new_secret, + 'secret': account.escape_jinja2_syntax(new_secret), 'private_key_path': private_key_path, 'become': account.get_ansible_become_auth(), } diff --git a/apps/accounts/automations/verify_account/manager.py b/apps/accounts/automations/verify_account/manager.py index 18478fb21..794cf4ff6 100644 --- a/apps/accounts/automations/verify_account/manager.py +++ b/apps/accounts/automations/verify_account/manager.py @@ -62,7 +62,7 @@ class VerifyAccountManager(AccountBasePlaybookManager): 'name': account.name, 'username': account.username, 'secret_type': account.secret_type, - 'secret': secret, + 'secret': account.escape_jinja2_syntax(secret), 'private_key_path': private_key_path, 'become': account.get_ansible_become_auth(), } diff --git a/apps/accounts/models/account.py b/apps/accounts/models/account.py index 1defa3642..99de5ddc0 100644 --- a/apps/accounts/models/account.py +++ b/apps/accounts/models/account.py @@ -97,14 +97,13 @@ class Account(AbsConnectivity, LabeledMixin, BaseAccount): """ 排除自己和以自己为 su-from 的账号 """ return self.asset.accounts.exclude(id=self.id).exclude(su_from=self) - @staticmethod - def make_account_ansible_vars(su_from): + def make_account_ansible_vars(self, su_from): var = { 'ansible_user': su_from.username, } if not su_from.secret: return var - var['ansible_password'] = su_from.secret + var['ansible_password'] = self.escape_jinja2_syntax(su_from.secret) var['ansible_ssh_private_key_file'] = su_from.private_key_path return var @@ -121,9 +120,22 @@ class Account(AbsConnectivity, LabeledMixin, BaseAccount): auth['ansible_become'] = True auth['ansible_become_method'] = become_method auth['ansible_become_user'] = self.username - auth['ansible_become_password'] = password + auth['ansible_become_password'] = self.escape_jinja2_syntax(password) return auth + @staticmethod + def escape_jinja2_syntax(value): + if not isinstance(value, str): + return value + + value = value.replace('{{', '__TEMP_OPEN_BRACES__') \ + .replace('}}', '__TEMP_CLOSE_BRACES__') + + value = value.replace('__TEMP_OPEN_BRACES__', '{{ "{{" }}') \ + .replace('__TEMP_CLOSE_BRACES__', '{{ "}}" }}') + + return value.replace('{%', '{{ "{%" }}').replace('%}', '{{ "%}" }}') + def replace_history_model_with_mixin(): """ diff --git a/apps/accounts/utils.py b/apps/accounts/utils.py index b8c9841f2..730db4a6c 100644 --- a/apps/accounts/utils.py +++ b/apps/accounts/utils.py @@ -47,18 +47,7 @@ class SecretGenerator: def validate_password_for_ansible(password): """ 校验 Ansible 不支持的特殊字符 """ - # validate password contains left double curly bracket - # check password not contains `{{` - # Ansible 推送的时候不支持 - if '{{' in password or '}}' in password: - raise serializers.ValidationError(_('Password can not contains `{{` or `}}`')) - if '{%' in password or '%}' in password: - raise serializers.ValidationError(_('Password can not contains `{%` or `%}`')) - # Ansible Windows 推送的时候不支持 - # if "'" in password: - # raise serializers.ValidationError(_("Password can not contains `'` ")) - # if '"' in password: - # raise serializers.ValidationError(_('Password can not contains `"` ')) + pass def validate_ssh_key(ssh_key, passphrase=None): diff --git a/apps/ops/ansible/inventory.py b/apps/ops/ansible/inventory.py index 1426ffc01..a6efc34c0 100644 --- a/apps/ops/ansible/inventory.py +++ b/apps/ops/ansible/inventory.py @@ -71,8 +71,9 @@ class JMSInventory: } if not account.secret: return var + if account.secret_type == 'password': - var['ansible_password'] = account.secret + var['ansible_password'] = account.escape_jinja2_syntax(account.secret) elif account.secret_type == 'ssh_key': var['ansible_ssh_private_key_file'] = account.private_key_path return var @@ -84,7 +85,7 @@ class JMSInventory: 'custom_become': True, 'custom_become_method': su_method, 'custom_become_user': account.su_from.username, - 'custom_become_password': account.su_from.secret, + 'custom_become_password': account.escape_jinja2_syntax(account.su_from.secret), 'custom_become_private_key_path': account.su_from.private_key_path } return var @@ -109,7 +110,7 @@ class JMSInventory: host.update(self.make_account_ansible_vars(account)) host['ansible_become'] = True host['ansible_become_user'] = 'root' - host['ansible_become_password'] = account.secret + host['ansible_become_password'] = account.escape_jinja2_syntax(account.secret) else: host.update(self.make_account_ansible_vars(account)) @@ -173,8 +174,8 @@ class JMSInventory: }, 'jms_account': { 'id': str(account.id), 'username': account.username, - 'secret': account.secret, 'secret_type': account.secret_type, - 'private_key_path': account.private_key_path + 'secret': account.escape_jinja2_syntax(account.secret), + 'secret_type': account.secret_type, 'private_key_path': account.private_key_path } if account else None } From 4f2b3fbb433d80a2bc7dece1a058ccf3ed11d13c Mon Sep 17 00:00:00 2001 From: feng <1304903146@qq.com> Date: Mon, 18 Dec 2023 17:44:59 +0800 Subject: [PATCH 080/111] =?UTF-8?q?perf:=20=E8=BF=81=E7=A7=BB=E6=96=87?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/ops/migrations/0023_auto_20220912_0021.py | 17 ++++++++++------- .../0024_alter_celerytask_date_last_publish.py | 5 +++-- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/apps/ops/migrations/0023_auto_20220912_0021.py b/apps/ops/migrations/0023_auto_20220912_0021.py index 0fc0de0ba..a55ba48f1 100644 --- a/apps/ops/migrations/0023_auto_20220912_0021.py +++ b/apps/ops/migrations/0023_auto_20220912_0021.py @@ -1,12 +1,13 @@ # Generated by Django 3.2.14 on 2022-12-28 10:03 +import uuid + +import django.db.models.deletion import private_storage.fields import private_storage.storage.files +import simple_history.models from django.conf import settings from django.db import migrations, models -import django.db.models.deletion -import simple_history.models -import uuid class Migration(migrations.Migration): @@ -70,8 +71,9 @@ class Migration(migrations.Migration): default='shell', max_length=128, null=True, verbose_name='Module')), ('chdir', models.CharField(blank=True, default='', max_length=1024, null=True, verbose_name='Chdir')), ('timeout', models.IntegerField(default=-1, verbose_name='Timeout (Seconds)')), - ('type', models.CharField(choices=[('adhoc', 'Adhoc'), ('playbook', 'Playbook')], default='adhoc', - max_length=128, verbose_name='Type')), + ('type', models.CharField( + choices=[('adhoc', 'Adhoc'), ('playbook', 'Playbook'), ('upload_file', 'Upload File')], + default='adhoc', max_length=128, verbose_name='Type')), ('runas', models.CharField(default='root', max_length=128, verbose_name='Runas')), ('runas_policy', models.CharField( choices=[('privileged_only', 'Privileged Only'), ('privileged_first', 'Privileged First'), @@ -173,8 +175,9 @@ class Migration(migrations.Migration): default='shell', max_length=128, null=True, verbose_name='Module')), ('chdir', models.CharField(blank=True, default='', max_length=1024, null=True, verbose_name='Chdir')), ('timeout', models.IntegerField(default=-1, verbose_name='Timeout (Seconds)')), - ('type', models.CharField(choices=[('adhoc', 'Adhoc'), ('playbook', 'Playbook')], default='adhoc', - max_length=128, verbose_name='Type')), + ('type', models.CharField( + choices=[('adhoc', 'Adhoc'), ('playbook', 'Playbook'), ('upload_file', 'Upload File')], + default='adhoc', max_length=128, verbose_name='Type')), ('runas', models.CharField(default='root', max_length=128, verbose_name='Runas')), ('runas_policy', models.CharField( choices=[('privileged_only', 'Privileged Only'), ('privileged_first', 'Privileged First'), diff --git a/apps/ops/migrations/0024_alter_celerytask_date_last_publish.py b/apps/ops/migrations/0024_alter_celerytask_date_last_publish.py index 26c5d2cd7..3b7192f60 100644 --- a/apps/ops/migrations/0024_alter_celerytask_date_last_publish.py +++ b/apps/ops/migrations/0024_alter_celerytask_date_last_publish.py @@ -56,8 +56,9 @@ class Migration(migrations.Migration): migrations.AddField( model_name='jobexecution', name='job_type', - field=models.CharField(choices=[('adhoc', 'Adhoc'), ('playbook', 'Playbook')], default='adhoc', - max_length=128, verbose_name='Material Type'), + field=models.CharField( + choices=[('adhoc', 'Adhoc'), ('playbook', 'Playbook'), ('upload_file', 'Upload File')], + default='adhoc', max_length=128, verbose_name='Material Type'), ), migrations.AddField( model_name='jobexecution', From d6b5590505d0623c1532695c7256627bbc723bc9 Mon Sep 17 00:00:00 2001 From: feng <1304903146@qq.com> Date: Mon, 18 Dec 2023 18:36:44 +0800 Subject: [PATCH 081/111] =?UTF-8?q?perf:=20=E7=BF=BB=E8=AF=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/locale/ja/LC_MESSAGES/django.mo | 4 +- apps/locale/ja/LC_MESSAGES/django.po | 228 +++++++++++++++------------ apps/locale/zh/LC_MESSAGES/django.mo | 4 +- apps/locale/zh/LC_MESSAGES/django.po | 222 ++++++++++++++------------ 4 files changed, 254 insertions(+), 204 deletions(-) diff --git a/apps/locale/ja/LC_MESSAGES/django.mo b/apps/locale/ja/LC_MESSAGES/django.mo index 900413bdf..0f19f1f90 100644 --- a/apps/locale/ja/LC_MESSAGES/django.mo +++ b/apps/locale/ja/LC_MESSAGES/django.mo @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:93aa582f0d6c3661722b4735728383f7caf7bbe4ef6dc6eaa879c5ad3bcf2bec -size 169057 +oid sha256:13f24d4f5b06617cafccbef083f2b00341696eb2388e009b060621e470533254 +size 169044 diff --git a/apps/locale/ja/LC_MESSAGES/django.po b/apps/locale/ja/LC_MESSAGES/django.po index 75fc20a00..6301ba33c 100644 --- a/apps/locale/ja/LC_MESSAGES/django.po +++ b/apps/locale/ja/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-12-15 15:13+0800\n" +"POT-Creation-Date: 2023-12-18 18:35+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -250,13 +250,13 @@ msgstr "ユーザー %s がパスワードを閲覧/導き出しました" #: assets/models/asset/common.py:350 assets/models/cmd_filter.py:36 #: assets/serializers/domain.py:20 audits/models.py:58 #: authentication/models/connection_token.py:36 -#: perms/models/asset_permission.py:69 perms/serializers/permission.py:35 +#: perms/models/asset_permission.py:69 perms/serializers/permission.py:36 #: terminal/backends/command/models.py:17 terminal/models/session/session.py:31 #: terminal/notifications.py:155 terminal/serializers/command.py:17 #: terminal/serializers/session.py:26 #: terminal/templates/terminal/_msg_command_warning.html:4 #: terminal/templates/terminal/_msg_session_sharing.html:4 -#: tickets/models/ticket/apply_asset.py:16 xpack/plugins/cloud/models.py:253 +#: tickets/models/ticket/apply_asset.py:16 xpack/plugins/cloud/models.py:256 msgid "Asset" msgstr "資産" @@ -291,7 +291,7 @@ msgstr "ソース ID" #: assets/serializers/asset/common.py:125 assets/serializers/gateway.py:28 #: audits/models.py:59 authentication/api/connection_token.py:405 #: ops/models/base.py:18 perms/models/asset_permission.py:75 -#: perms/serializers/permission.py:40 terminal/backends/command/models.py:18 +#: perms/serializers/permission.py:41 terminal/backends/command/models.py:18 #: terminal/models/session/session.py:33 #: terminal/templates/terminal/_msg_command_warning.html:8 #: terminal/templates/terminal/_msg_session_sharing.html:8 @@ -388,7 +388,7 @@ msgid "Trigger mode" msgstr "トリガーモード" #: accounts/models/automations/backup_account.py:133 audits/models.py:203 -#: terminal/models/session/sharing.py:125 xpack/plugins/cloud/models.py:205 +#: terminal/models/session/sharing.py:125 xpack/plugins/cloud/models.py:208 msgid "Reason" msgstr "理由" @@ -482,8 +482,8 @@ msgstr "終了日" #: terminal/serializers/applet.py:18 terminal/serializers/applet_host.py:136 #: terminal/serializers/virtualapp.py:35 tickets/models/ticket/general.py:283 #: tickets/serializers/super_ticket.py:13 -#: tickets/serializers/ticket/ticket.py:20 xpack/plugins/cloud/models.py:201 -#: xpack/plugins/cloud/models.py:257 +#: tickets/serializers/ticket/ticket.py:20 xpack/plugins/cloud/models.py:204 +#: xpack/plugins/cloud/models.py:260 msgid "Status" msgstr "ステータス" @@ -613,8 +613,8 @@ msgstr "パスワードルール" #: terminal/models/virtualapp/virtualapp.py:19 tickets/api/ticket.py:87 #: users/forms/profile.py:33 users/models/group.py:13 #: users/models/preference.py:11 users/models/user.py:800 -#: xpack/plugins/cloud/models.py:32 xpack/plugins/cloud/models.py:273 -#: xpack/plugins/cloud/serializers/task.py:68 +#: xpack/plugins/cloud/models.py:32 xpack/plugins/cloud/models.py:276 +#: xpack/plugins/cloud/serializers/task.py:70 msgid "Name" msgstr "名前" @@ -644,7 +644,7 @@ msgstr "プラットフォーム" msgid "Push params" msgstr "パラメータをプッシュする" -#: accounts/models/template.py:26 xpack/plugins/cloud/models.py:325 +#: accounts/models/template.py:26 xpack/plugins/cloud/models.py:333 msgid "Account template" msgstr "アカウント テンプレート" @@ -824,15 +824,15 @@ msgstr "ID" #: authentication/models/sso_token.py:16 #: notifications/models/notification.py:12 #: perms/api/user_permission/mixin.py:55 perms/models/asset_permission.py:63 -#: perms/serializers/permission.py:31 rbac/builtin.py:124 +#: perms/serializers/permission.py:32 rbac/builtin.py:124 #: rbac/models/rolebinding.py:49 rbac/serializers/rolebinding.py:17 #: terminal/backends/command/models.py:16 terminal/models/session/session.py:29 #: terminal/models/session/sharing.py:34 terminal/notifications.py:156 #: terminal/notifications.py:205 terminal/serializers/command.py:16 #: terminal/templates/terminal/_msg_command_warning.html:6 #: terminal/templates/terminal/_msg_session_sharing.html:6 -#: tickets/models/comment.py:21 users/const.py:14 users/models/user.py:996 -#: users/models/user.py:1033 users/serializers/group.py:19 +#: tickets/models/comment.py:21 users/const.py:14 users/models/user.py:1003 +#: users/models/user.py:1040 users/serializers/group.py:19 msgid "User" msgstr "ユーザー" @@ -929,7 +929,7 @@ msgstr "关联平台,可以配置推送参数,如果不关联,则使用默 #: terminal/models/session/session.py:46 #: terminal/models/virtualapp/virtualapp.py:28 tickets/models/comment.py:32 #: tickets/models/ticket/general.py:297 users/models/user.py:836 -#: xpack/plugins/cloud/models.py:39 xpack/plugins/cloud/models.py:109 +#: xpack/plugins/cloud/models.py:39 xpack/plugins/cloud/models.py:110 msgid "Comment" msgstr "コメント" @@ -1043,15 +1043,7 @@ msgstr "新規アカウント" msgid "Deleted account" msgstr "アカウントの削除" -#: accounts/utils.py:54 -msgid "Password can not contains `{{` or `}}`" -msgstr "パスワードには `{` または `}` 文字を含めることはできません" - #: accounts/utils.py:56 -msgid "Password can not contains `{%` or `%}`" -msgstr "パスワードには `{%` または `%}` 文字を含めることはできません" - -#: accounts/utils.py:67 msgid "private key invalid or passphrase error" msgstr "秘密鍵が無効またはpassphraseエラー" @@ -1060,7 +1052,7 @@ msgid "Acls" msgstr "Acls" #: acls/const.py:6 audits/const.py:36 terminal/const.py:11 tickets/const.py:45 -#: tickets/templates/tickets/approve_check_password.html:49 +#: tickets/templates/tickets/approve_check_password.html:47 msgid "Reject" msgstr "拒否" @@ -1082,13 +1074,13 @@ msgstr "通知" #: acls/models/base.py:37 assets/models/_user.py:51 #: assets/models/cmd_filter.py:76 terminal/models/component/endpoint.py:98 -#: xpack/plugins/cloud/models.py:275 +#: xpack/plugins/cloud/models.py:282 msgid "Priority" msgstr "優先順位" #: acls/models/base.py:38 assets/models/_user.py:51 #: assets/models/cmd_filter.py:76 terminal/models/component/endpoint.py:99 -#: xpack/plugins/cloud/models.py:276 +#: xpack/plugins/cloud/models.py:283 msgid "1-100, the lower the value will be match first" msgstr "1-100、低い値は最初に一致します" @@ -1125,7 +1117,7 @@ msgid "Command" msgstr "コマンド" #: acls/models/command_acl.py:17 assets/models/cmd_filter.py:59 -#: xpack/plugins/cloud/models.py:291 +#: xpack/plugins/cloud/models.py:299 msgid "Regex" msgstr "正規情報" @@ -1231,7 +1223,7 @@ msgid "None of the reviewers belong to Organization `{}`" msgstr "いずれのレビューアも組織 '{}' に属していません" #: acls/serializers/rules/rules.py:20 -#: xpack/plugins/cloud/serializers/task.py:137 +#: xpack/plugins/cloud/serializers/task.py:145 msgid "IP address invalid: `{}`" msgstr "IPアドレスが無効: '{}'" @@ -1618,7 +1610,7 @@ msgstr "SSHパブリックキー" #: assets/models/_user.py:28 assets/models/automations/base.py:114 #: assets/models/cmd_filter.py:41 assets/models/group.py:19 #: audits/models.py:267 common/db/models.py:34 ops/models/base.py:54 -#: ops/models/job.py:239 users/models/user.py:1034 +#: ops/models/job.py:239 users/models/user.py:1041 msgid "Date created" msgstr "作成された日付" @@ -1721,20 +1713,20 @@ msgstr "アドレス" #: assets/models/asset/common.py:161 assets/models/platform.py:126 #: authentication/backends/passkey/models.py:12 #: authentication/serializers/connect_token_secret.py:118 -#: perms/serializers/user_permission.py:24 xpack/plugins/cloud/models.py:321 +#: perms/serializers/user_permission.py:24 xpack/plugins/cloud/models.py:329 msgid "Platform" msgstr "プラットフォーム" #: assets/models/asset/common.py:163 assets/models/domain.py:22 #: authentication/serializers/connect_token_secret.py:136 -#: perms/serializers/user_permission.py:28 xpack/plugins/cloud/models.py:323 +#: perms/serializers/user_permission.py:28 xpack/plugins/cloud/models.py:331 msgid "Domain" msgstr "ドメイン" #: assets/models/asset/common.py:165 assets/models/automations/base.py:18 #: assets/models/cmd_filter.py:32 assets/models/node.py:553 -#: perms/models/asset_permission.py:72 perms/serializers/permission.py:36 -#: tickets/models/ticket/apply_asset.py:14 xpack/plugins/cloud/models.py:322 +#: perms/models/asset_permission.py:72 perms/serializers/permission.py:37 +#: tickets/models/ticket/apply_asset.py:14 xpack/plugins/cloud/models.py:330 msgid "Node" msgstr "ノード" @@ -1817,7 +1809,7 @@ msgid "Date verified" msgstr "確認済みの日付" #: assets/models/cmd_filter.py:28 perms/models/asset_permission.py:66 -#: perms/serializers/permission.py:33 users/models/group.py:25 +#: perms/serializers/permission.py:34 users/models/group.py:25 #: users/models/user.py:806 msgid "User group" msgstr "ユーザーグループ" @@ -1868,7 +1860,7 @@ msgstr "デフォルト" msgid "Default asset group" msgstr "デフォルトアセットグループ" -#: assets/models/label.py:15 rbac/const.py:6 users/models/user.py:1019 +#: assets/models/label.py:15 rbac/const.py:6 users/models/user.py:1026 msgid "System" msgstr "システム" @@ -2061,9 +2053,9 @@ msgstr "" #: assets/serializers/asset/common.py:124 assets/serializers/platform.py:141 #: authentication/serializers/connect_token_secret.py:30 #: authentication/serializers/connect_token_secret.py:75 -#: perms/models/asset_permission.py:76 perms/serializers/permission.py:41 -#: perms/serializers/user_permission.py:74 xpack/plugins/cloud/models.py:324 -#: xpack/plugins/cloud/serializers/task.py:31 +#: perms/models/asset_permission.py:76 perms/serializers/permission.py:42 +#: perms/serializers/user_permission.py:74 xpack/plugins/cloud/models.py:332 +#: xpack/plugins/cloud/serializers/task.py:33 msgid "Protocols" msgstr "プロトコル" @@ -3204,15 +3196,15 @@ msgid "Ticket info" msgstr "作業指示情報" #: authentication/serializers/connection_token.py:21 -#: perms/models/asset_permission.py:77 perms/serializers/permission.py:37 -#: perms/serializers/permission.py:58 +#: perms/models/asset_permission.py:77 perms/serializers/permission.py:38 +#: perms/serializers/permission.py:59 #: tickets/models/ticket/apply_application.py:28 #: tickets/models/ticket/apply_asset.py:18 msgid "Actions" msgstr "アクション" #: authentication/serializers/connection_token.py:42 -#: perms/serializers/permission.py:39 perms/serializers/permission.py:59 +#: perms/serializers/permission.py:40 perms/serializers/permission.py:60 #: users/serializers/user.py:97 users/serializers/user.py:171 msgid "Is expired" msgstr "期限切れです" @@ -3226,8 +3218,8 @@ msgstr "{} 空にしてはならない" msgid "Access IP" msgstr "Access IP" -#: authentication/serializers/token.py:92 perms/serializers/permission.py:38 -#: perms/serializers/permission.py:60 users/serializers/user.py:98 +#: authentication/serializers/token.py:92 perms/serializers/permission.py:39 +#: perms/serializers/permission.py:61 users/serializers/user.py:98 #: users/serializers/user.py:168 msgid "Is valid" msgstr "有効です" @@ -3707,7 +3699,7 @@ msgid "Invalid ids for ids, should be a list" msgstr "無効なID、リストでなければなりません" #: common/db/fields.py:585 common/db/fields.py:590 -#: common/serializers/fields.py:132 tickets/serializers/ticket/common.py:58 +#: common/serializers/fields.py:136 tickets/serializers/ticket/common.py:58 #: xpack/plugins/cloud/serializers/account_attrs.py:56 #: xpack/plugins/cloud/serializers/account_attrs.py:79 #: xpack/plugins/cloud/serializers/account_attrs.py:150 @@ -3880,21 +3872,21 @@ msgstr "{} 秒待ってから送信してください" msgid "Children" msgstr "ノード" -#: common/serializers/fields.py:133 +#: common/serializers/fields.py:137 #, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "無効な pk \"{pk_value}\" - オブジェクトが存在しません" -#: common/serializers/fields.py:134 +#: common/serializers/fields.py:138 #, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "エラータイプ。 予想される pk 値、受信 {data_type}。" -#: common/serializers/fields.py:208 +#: common/serializers/fields.py:212 msgid "Invalid data type, should be list" msgstr "間違ったデータ タイプです。リストにする必要があります" -#: common/serializers/fields.py:223 +#: common/serializers/fields.py:227 msgid "Invalid choice: {}" msgstr "無効なオプション: {}" @@ -4028,15 +4020,15 @@ msgstr "システムメッセージ" msgid "Publish the station message" msgstr "投稿サイトニュース" -#: ops/ansible/inventory.py:95 ops/models/job.py:63 +#: ops/ansible/inventory.py:96 ops/models/job.py:63 msgid "No account available" msgstr "利用可能なアカウントがありません" -#: ops/ansible/inventory.py:259 +#: ops/ansible/inventory.py:260 msgid "Ansible disabled" msgstr "Ansible 無効" -#: ops/ansible/inventory.py:275 +#: ops/ansible/inventory.py:276 msgid "Skip hosts below:" msgstr "次のホストをスキップします: " @@ -4241,7 +4233,7 @@ msgid "Date last run" msgstr "最終実行日" #: ops/models/base.py:51 ops/models/job.py:236 -#: xpack/plugins/cloud/models.py:199 +#: xpack/plugins/cloud/models.py:202 msgid "Result" msgstr "結果" @@ -4939,10 +4931,8 @@ msgid "Can change other setting" msgstr "他の設定を変えることができます" #: settings/models.py:185 -#, fuzzy -#| msgid "Username prompt" msgid "Chat prompt" -msgstr "チャットプロンプト" +msgstr "チャットのヒント" #: settings/serializers/auth/base.py:12 msgid "LDAP Auth" @@ -6956,7 +6946,9 @@ msgstr "最大切断時間(ミリ秒)" msgid "" "Tips: Set the maximum duration for keeping a disconnected session active on " "the server (log off the session after 60000 milliseconds)." -msgstr "ヒント:サーバー上で切断されたセッションがアクティブな状態で維持される最大時間を設定します(60000ミリ秒後にセッションをログオフ)。" +msgstr "" +"ヒント:サーバー上で切断されたセッションがアクティブな状態で維持される最大時" +"間を設定します(60000ミリ秒後にセッションをログオフ)。" #: terminal/serializers/applet_host.py:60 msgid "RDS Remote App Logoff Time Limit (ms)" @@ -6967,7 +6959,8 @@ msgid "" "Tips: Set the logoff time for RemoteApp sessions after closing all RemoteApp " "programs (0 milliseconds, log off the session immediately)." msgstr "" -"ヒント:すべてのRemoteAppプログラムを閉じた後、RemoteAppセッションのログオフ時間を設定します(0ミリ秒、セッションを即座にログオフ)。" +"ヒント:すべてのRemoteAppプログラムを閉じた後、RemoteAppセッションのログオフ" +"時間を設定します(0ミリ秒、セッションを即座にログオフ)。" #: terminal/serializers/applet_host.py:71 terminal/serializers/terminal.py:47 #: terminal/serializers/virtualapp_provider.py:13 @@ -7123,7 +7116,7 @@ msgstr "アクセスキー" msgid "Access key secret" msgstr "アクセスキーシークレット" -#: terminal/serializers/storage.py:68 xpack/plugins/cloud/models.py:250 +#: terminal/serializers/storage.py:68 xpack/plugins/cloud/models.py:253 msgid "Region" msgstr "リージョン" @@ -7571,7 +7564,7 @@ msgstr "作業指示情報" msgid "Ticket approval" msgstr "作業指示の承認" -#: tickets/templates/tickets/approve_check_password.html:44 +#: tickets/templates/tickets/approve_check_password.html:43 msgid "Approval" msgstr "承認" @@ -7789,31 +7782,31 @@ msgstr "更新パスワードが必要" msgid "Date api key used" msgstr "Api key 最後に使用した日付" -#: users/models/user.py:977 +#: users/models/user.py:984 msgid "Can not delete admin user" msgstr "管理者ユーザーを削除できませんでした" -#: users/models/user.py:1004 +#: users/models/user.py:1011 msgid "Can invite user" msgstr "ユーザーを招待できます" -#: users/models/user.py:1005 +#: users/models/user.py:1012 msgid "Can remove user" msgstr "ユーザーを削除できます" -#: users/models/user.py:1006 +#: users/models/user.py:1013 msgid "Can match user" msgstr "ユーザーに一致できます" -#: users/models/user.py:1015 +#: users/models/user.py:1022 msgid "Administrator" msgstr "管理者" -#: users/models/user.py:1018 +#: users/models/user.py:1025 msgid "Administrator is the super user of system" msgstr "管理者はシステムのスーパーユーザーです" -#: users/models/user.py:1043 +#: users/models/user.py:1050 msgid "User password history" msgstr "ユーザーパスワード履歴" @@ -8028,7 +8021,7 @@ msgstr "ユーザーの有効期限の定期的な検出" msgid "Check unused users" msgstr "未使用のユーザーのチェック" -#: users/tasks.py:122 +#: users/tasks.py:123 msgid "The user has not logged in recently and has been disabled." msgstr "ユーザーは最近ログインしておらず、無効になっています。" @@ -8294,6 +8287,11 @@ msgstr "パスワードの成功をリセットし、ログインページに戻 msgid "XPACK" msgstr "XPack" +#: xpack/exceptions.py:7 +msgid "" +"The current task is not synchronized with unmatched policy assets, skipping" +msgstr "" + #: xpack/plugins/cloud/api.py:56 msgid "Test connection successful" msgstr "テスト接続成功" @@ -8398,7 +8396,7 @@ msgstr "プライベートIP" msgid "Public IP" msgstr "パブリックIP" -#: xpack/plugins/cloud/const.py:41 xpack/plugins/cloud/models.py:295 +#: xpack/plugins/cloud/const.py:41 xpack/plugins/cloud/models.py:303 msgid "Instance name" msgstr "インスタンス名" @@ -8426,7 +8424,15 @@ msgstr "同期済み" msgid "Released" msgstr "リリース済み" -#: xpack/plugins/cloud/manager.py:54 +#: xpack/plugins/cloud/const.py:58 +msgid "And" +msgstr "そして" + +#: xpack/plugins/cloud/const.py:59 +msgid "Or" +msgstr "または" + +#: xpack/plugins/cloud/manager.py:57 msgid "Account unavailable" msgstr "利用できないアカウント" @@ -8450,7 +8456,7 @@ msgstr "クラウドアカウント" msgid "Test cloud account" msgstr "クラウドアカウントのテスト" -#: xpack/plugins/cloud/models.py:92 xpack/plugins/cloud/serializers/task.py:151 +#: xpack/plugins/cloud/models.py:92 xpack/plugins/cloud/serializers/task.py:159 msgid "Regions" msgstr "リージョン" @@ -8459,122 +8465,134 @@ msgid "Hostname strategy" msgstr "ホスト名戦略" #: xpack/plugins/cloud/models.py:100 -#: xpack/plugins/cloud/serializers/task.py:154 +#: xpack/plugins/cloud/serializers/task.py:162 msgid "IP network segment group" msgstr "IPネットワークセグメントグループ" #: xpack/plugins/cloud/models.py:103 -#: xpack/plugins/cloud/serializers/task.py:159 +#: xpack/plugins/cloud/serializers/task.py:167 msgid "Sync IP type" msgstr "同期IPタイプ" #: xpack/plugins/cloud/models.py:106 -#: xpack/plugins/cloud/serializers/task.py:177 +#: xpack/plugins/cloud/serializers/task.py:185 msgid "Always update" msgstr "常に更新" -#: xpack/plugins/cloud/models.py:112 +#: xpack/plugins/cloud/models.py:108 +msgid "Fully synchronous" +msgstr "完全同期" + +#: xpack/plugins/cloud/models.py:113 msgid "Date last sync" msgstr "最終同期日" -#: xpack/plugins/cloud/models.py:115 xpack/plugins/cloud/models.py:313 -#: xpack/plugins/cloud/models.py:337 +#: xpack/plugins/cloud/models.py:116 xpack/plugins/cloud/models.py:321 +#: xpack/plugins/cloud/models.py:345 msgid "Strategy" msgstr "戦略" -#: xpack/plugins/cloud/models.py:120 xpack/plugins/cloud/models.py:197 +#: xpack/plugins/cloud/models.py:121 xpack/plugins/cloud/models.py:200 msgid "Sync instance task" msgstr "インスタンスの同期タスク" -#: xpack/plugins/cloud/models.py:208 xpack/plugins/cloud/models.py:260 +#: xpack/plugins/cloud/models.py:211 xpack/plugins/cloud/models.py:263 msgid "Date sync" msgstr "日付の同期" -#: xpack/plugins/cloud/models.py:212 +#: xpack/plugins/cloud/models.py:215 msgid "Sync instance snapshot" msgstr "インスタンススナップショットの同期" -#: xpack/plugins/cloud/models.py:216 +#: xpack/plugins/cloud/models.py:219 msgid "Sync instance task execution" msgstr "インスタンスタスクの同期実行" -#: xpack/plugins/cloud/models.py:240 +#: xpack/plugins/cloud/models.py:243 msgid "Sync task" msgstr "同期タスク" -#: xpack/plugins/cloud/models.py:244 +#: xpack/plugins/cloud/models.py:247 msgid "Sync instance task history" msgstr "インスタンスタスク履歴の同期" -#: xpack/plugins/cloud/models.py:247 +#: xpack/plugins/cloud/models.py:250 msgid "Instance" msgstr "インスタンス" -#: xpack/plugins/cloud/models.py:264 +#: xpack/plugins/cloud/models.py:267 msgid "Sync instance detail" msgstr "同期インスタンスの詳細" -#: xpack/plugins/cloud/models.py:281 +#: xpack/plugins/cloud/models.py:279 xpack/plugins/cloud/serializers/task.py:72 +msgid "Rule relation" +msgstr "条件関係" + +#: xpack/plugins/cloud/models.py:288 msgid "Task strategy" msgstr "ミッション戦略です" -#: xpack/plugins/cloud/models.py:285 +#: xpack/plugins/cloud/models.py:292 msgid "Equal" msgstr "等しい" -#: xpack/plugins/cloud/models.py:286 +#: xpack/plugins/cloud/models.py:293 msgid "Not Equal" msgstr "不等于" -#: xpack/plugins/cloud/models.py:287 +#: xpack/plugins/cloud/models.py:294 msgid "In" msgstr "で..." -#: xpack/plugins/cloud/models.py:288 +#: xpack/plugins/cloud/models.py:295 msgid "Contains" msgstr "含む" -#: xpack/plugins/cloud/models.py:289 +#: xpack/plugins/cloud/models.py:296 +msgid "Exclude" +msgstr "除外" + +#: xpack/plugins/cloud/models.py:297 msgid "Startswith" msgstr "始まる..." -#: xpack/plugins/cloud/models.py:290 +#: xpack/plugins/cloud/models.py:298 msgid "Endswith" msgstr "終わる..." -#: xpack/plugins/cloud/models.py:296 +#: xpack/plugins/cloud/models.py:304 msgid "Instance platform" msgstr "インスタンス名" -#: xpack/plugins/cloud/models.py:297 +#: xpack/plugins/cloud/models.py:305 msgid "Instance address" msgstr "インスタンスアドレス" -#: xpack/plugins/cloud/models.py:304 +#: xpack/plugins/cloud/models.py:312 msgid "Rule attr" msgstr "ルール属性" -#: xpack/plugins/cloud/models.py:308 +#: xpack/plugins/cloud/models.py:316 msgid "Rule match" msgstr "ルール一致" -#: xpack/plugins/cloud/models.py:310 +#: xpack/plugins/cloud/models.py:318 msgid "Rule value" msgstr "ルール値" -#: xpack/plugins/cloud/models.py:317 xpack/plugins/cloud/serializers/task.py:70 +#: xpack/plugins/cloud/models.py:325 xpack/plugins/cloud/serializers/task.py:75 msgid "Strategy rule" msgstr "戦略ルール" -#: xpack/plugins/cloud/models.py:332 +#: xpack/plugins/cloud/models.py:340 msgid "Action attr" msgstr "アクション属性" -#: xpack/plugins/cloud/models.py:334 +#: xpack/plugins/cloud/models.py:342 msgid "Action value" msgstr "アクション値" -#: xpack/plugins/cloud/models.py:341 xpack/plugins/cloud/serializers/task.py:73 +#: xpack/plugins/cloud/models.py:349 xpack/plugins/cloud/serializers/task.py:78 msgid "Strategy action" msgstr "戦略アクション" @@ -8862,7 +8880,7 @@ msgstr "テストタイムアウト" msgid "Project" msgstr "project" -#: xpack/plugins/cloud/serializers/task.py:143 +#: xpack/plugins/cloud/serializers/task.py:151 msgid "" "Only instances matching the IP range will be synced.
    If the instance " "contains multiple IP addresses, the first IP address that matches will be " @@ -8876,11 +8894,11 @@ msgstr "" "ドレスをランダムに一致させることを意味します。
    例: " "192.168.1.0/24,10.1.1.1-10.1.1.20。" -#: xpack/plugins/cloud/serializers/task.py:149 +#: xpack/plugins/cloud/serializers/task.py:157 msgid "History count" msgstr "実行回数" -#: xpack/plugins/cloud/serializers/task.py:150 +#: xpack/plugins/cloud/serializers/task.py:158 msgid "Instance count" msgstr "インスタンス数" @@ -8964,6 +8982,12 @@ msgstr "エンタープライズプロフェッショナル版" msgid "Ultimate edition" msgstr "エンタープライズ・フラッグシップ・エディション" +#~ msgid "Password can not contains `{{` or `}}`" +#~ msgstr "パスワードには `{` または `}` 文字を含めることはできません" + +#~ msgid "Password can not contains `{%` or `%}`" +#~ msgstr "パスワードには `{%` または `%}` 文字を含めることはできません" + #~ msgid "FeiShu query user failed" #~ msgstr "FeiShuクエリユーザーが失敗しました" diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index 00732f89c..f92f0c4a0 100644 --- a/apps/locale/zh/LC_MESSAGES/django.mo +++ b/apps/locale/zh/LC_MESSAGES/django.mo @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ef69fc1b0357e29b7cf8f3a2183ef6a35fc018d165921aa9f1c586679a3c6491 -size 138509 +oid sha256:9d42c8bfd23292b98c49eb9d67ca90595efb97093506d4dfa3e8a6cb91a8cb9b +size 138519 diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index ced9a7d93..69aad7348 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: JumpServer 0.3.3\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-12-15 15:13+0800\n" +"POT-Creation-Date: 2023-12-18 18:35+0800\n" "PO-Revision-Date: 2021-05-20 10:54+0800\n" "Last-Translator: ibuler \n" "Language-Team: JumpServer team\n" @@ -249,13 +249,13 @@ msgstr "用户 %s 查看/导出 了密码" #: assets/models/asset/common.py:350 assets/models/cmd_filter.py:36 #: assets/serializers/domain.py:20 audits/models.py:58 #: authentication/models/connection_token.py:36 -#: perms/models/asset_permission.py:69 perms/serializers/permission.py:35 +#: perms/models/asset_permission.py:69 perms/serializers/permission.py:36 #: terminal/backends/command/models.py:17 terminal/models/session/session.py:31 #: terminal/notifications.py:155 terminal/serializers/command.py:17 #: terminal/serializers/session.py:26 #: terminal/templates/terminal/_msg_command_warning.html:4 #: terminal/templates/terminal/_msg_session_sharing.html:4 -#: tickets/models/ticket/apply_asset.py:16 xpack/plugins/cloud/models.py:253 +#: tickets/models/ticket/apply_asset.py:16 xpack/plugins/cloud/models.py:256 msgid "Asset" msgstr "资产" @@ -290,7 +290,7 @@ msgstr "来源 ID" #: assets/serializers/asset/common.py:125 assets/serializers/gateway.py:28 #: audits/models.py:59 authentication/api/connection_token.py:405 #: ops/models/base.py:18 perms/models/asset_permission.py:75 -#: perms/serializers/permission.py:40 terminal/backends/command/models.py:18 +#: perms/serializers/permission.py:41 terminal/backends/command/models.py:18 #: terminal/models/session/session.py:33 #: terminal/templates/terminal/_msg_command_warning.html:8 #: terminal/templates/terminal/_msg_session_sharing.html:8 @@ -387,7 +387,7 @@ msgid "Trigger mode" msgstr "触发模式" #: accounts/models/automations/backup_account.py:133 audits/models.py:203 -#: terminal/models/session/sharing.py:125 xpack/plugins/cloud/models.py:205 +#: terminal/models/session/sharing.py:125 xpack/plugins/cloud/models.py:208 msgid "Reason" msgstr "原因" @@ -481,8 +481,8 @@ msgstr "结束日期" #: terminal/serializers/applet.py:18 terminal/serializers/applet_host.py:136 #: terminal/serializers/virtualapp.py:35 tickets/models/ticket/general.py:283 #: tickets/serializers/super_ticket.py:13 -#: tickets/serializers/ticket/ticket.py:20 xpack/plugins/cloud/models.py:201 -#: xpack/plugins/cloud/models.py:257 +#: tickets/serializers/ticket/ticket.py:20 xpack/plugins/cloud/models.py:204 +#: xpack/plugins/cloud/models.py:260 msgid "Status" msgstr "状态" @@ -612,8 +612,8 @@ msgstr "密码规则" #: terminal/models/virtualapp/virtualapp.py:19 tickets/api/ticket.py:87 #: users/forms/profile.py:33 users/models/group.py:13 #: users/models/preference.py:11 users/models/user.py:800 -#: xpack/plugins/cloud/models.py:32 xpack/plugins/cloud/models.py:273 -#: xpack/plugins/cloud/serializers/task.py:68 +#: xpack/plugins/cloud/models.py:32 xpack/plugins/cloud/models.py:276 +#: xpack/plugins/cloud/serializers/task.py:70 msgid "Name" msgstr "名称" @@ -643,7 +643,7 @@ msgstr "系统平台" msgid "Push params" msgstr "账号推送参数" -#: accounts/models/template.py:26 xpack/plugins/cloud/models.py:325 +#: accounts/models/template.py:26 xpack/plugins/cloud/models.py:333 msgid "Account template" msgstr "账号模版" @@ -822,15 +822,15 @@ msgstr "ID" #: authentication/models/sso_token.py:16 #: notifications/models/notification.py:12 #: perms/api/user_permission/mixin.py:55 perms/models/asset_permission.py:63 -#: perms/serializers/permission.py:31 rbac/builtin.py:124 +#: perms/serializers/permission.py:32 rbac/builtin.py:124 #: rbac/models/rolebinding.py:49 rbac/serializers/rolebinding.py:17 #: terminal/backends/command/models.py:16 terminal/models/session/session.py:29 #: terminal/models/session/sharing.py:34 terminal/notifications.py:156 #: terminal/notifications.py:205 terminal/serializers/command.py:16 #: terminal/templates/terminal/_msg_command_warning.html:6 #: terminal/templates/terminal/_msg_session_sharing.html:6 -#: tickets/models/comment.py:21 users/const.py:14 users/models/user.py:996 -#: users/models/user.py:1033 users/serializers/group.py:19 +#: tickets/models/comment.py:21 users/const.py:14 users/models/user.py:1003 +#: users/models/user.py:1040 users/serializers/group.py:19 msgid "User" msgstr "用户" @@ -927,7 +927,7 @@ msgstr "关联平台,可配置推送参数,如果不关联,将使用默认 #: terminal/models/session/session.py:46 #: terminal/models/virtualapp/virtualapp.py:28 tickets/models/comment.py:32 #: tickets/models/ticket/general.py:297 users/models/user.py:836 -#: xpack/plugins/cloud/models.py:39 xpack/plugins/cloud/models.py:109 +#: xpack/plugins/cloud/models.py:39 xpack/plugins/cloud/models.py:110 msgid "Comment" msgstr "备注" @@ -1040,15 +1040,7 @@ msgstr "新增账号" msgid "Deleted account" msgstr "删除账号" -#: accounts/utils.py:54 -msgid "Password can not contains `{{` or `}}`" -msgstr "密码不能包含 `{{` 或 `}}` 字符" - #: accounts/utils.py:56 -msgid "Password can not contains `{%` or `%}`" -msgstr "密码不能包含 `{%` 或 `%}` 字符" - -#: accounts/utils.py:67 msgid "private key invalid or passphrase error" msgstr "密钥不合法或密钥密码错误" @@ -1057,7 +1049,7 @@ msgid "Acls" msgstr "访问控制" #: acls/const.py:6 audits/const.py:36 terminal/const.py:11 tickets/const.py:45 -#: tickets/templates/tickets/approve_check_password.html:49 +#: tickets/templates/tickets/approve_check_password.html:47 msgid "Reject" msgstr "拒绝" @@ -1079,13 +1071,13 @@ msgstr "通知" #: acls/models/base.py:37 assets/models/_user.py:51 #: assets/models/cmd_filter.py:76 terminal/models/component/endpoint.py:98 -#: xpack/plugins/cloud/models.py:275 +#: xpack/plugins/cloud/models.py:282 msgid "Priority" msgstr "优先级" #: acls/models/base.py:38 assets/models/_user.py:51 #: assets/models/cmd_filter.py:76 terminal/models/component/endpoint.py:99 -#: xpack/plugins/cloud/models.py:276 +#: xpack/plugins/cloud/models.py:283 msgid "1-100, the lower the value will be match first" msgstr "优先级可选范围为 1-100 (数值越小越优先)" @@ -1122,7 +1114,7 @@ msgid "Command" msgstr "命令" #: acls/models/command_acl.py:17 assets/models/cmd_filter.py:59 -#: xpack/plugins/cloud/models.py:291 +#: xpack/plugins/cloud/models.py:299 msgid "Regex" msgstr "正则表达式" @@ -1227,7 +1219,7 @@ msgid "None of the reviewers belong to Organization `{}`" msgstr "所有复核人都不属于组织 `{}`" #: acls/serializers/rules/rules.py:20 -#: xpack/plugins/cloud/serializers/task.py:137 +#: xpack/plugins/cloud/serializers/task.py:145 msgid "IP address invalid: `{}`" msgstr "IP 地址无效: `{}`" @@ -1611,7 +1603,7 @@ msgstr "SSH公钥" #: assets/models/_user.py:28 assets/models/automations/base.py:114 #: assets/models/cmd_filter.py:41 assets/models/group.py:19 #: audits/models.py:267 common/db/models.py:34 ops/models/base.py:54 -#: ops/models/job.py:239 users/models/user.py:1034 +#: ops/models/job.py:239 users/models/user.py:1041 msgid "Date created" msgstr "创建日期" @@ -1714,20 +1706,20 @@ msgstr "地址" #: assets/models/asset/common.py:161 assets/models/platform.py:126 #: authentication/backends/passkey/models.py:12 #: authentication/serializers/connect_token_secret.py:118 -#: perms/serializers/user_permission.py:24 xpack/plugins/cloud/models.py:321 +#: perms/serializers/user_permission.py:24 xpack/plugins/cloud/models.py:329 msgid "Platform" msgstr "系统平台" #: assets/models/asset/common.py:163 assets/models/domain.py:22 #: authentication/serializers/connect_token_secret.py:136 -#: perms/serializers/user_permission.py:28 xpack/plugins/cloud/models.py:323 +#: perms/serializers/user_permission.py:28 xpack/plugins/cloud/models.py:331 msgid "Domain" msgstr "网域" #: assets/models/asset/common.py:165 assets/models/automations/base.py:18 #: assets/models/cmd_filter.py:32 assets/models/node.py:553 -#: perms/models/asset_permission.py:72 perms/serializers/permission.py:36 -#: tickets/models/ticket/apply_asset.py:14 xpack/plugins/cloud/models.py:322 +#: perms/models/asset_permission.py:72 perms/serializers/permission.py:37 +#: tickets/models/ticket/apply_asset.py:14 xpack/plugins/cloud/models.py:330 msgid "Node" msgstr "节点" @@ -1810,7 +1802,7 @@ msgid "Date verified" msgstr "校验日期" #: assets/models/cmd_filter.py:28 perms/models/asset_permission.py:66 -#: perms/serializers/permission.py:33 users/models/group.py:25 +#: perms/serializers/permission.py:34 users/models/group.py:25 #: users/models/user.py:806 msgid "User group" msgstr "用户组" @@ -1861,7 +1853,7 @@ msgstr "默认" msgid "Default asset group" msgstr "默认资产组" -#: assets/models/label.py:15 rbac/const.py:6 users/models/user.py:1019 +#: assets/models/label.py:15 rbac/const.py:6 users/models/user.py:1026 msgid "System" msgstr "系统" @@ -2052,9 +2044,9 @@ msgstr "资产中批量更新平台,不符合平台类型跳过的资产" #: assets/serializers/asset/common.py:124 assets/serializers/platform.py:141 #: authentication/serializers/connect_token_secret.py:30 #: authentication/serializers/connect_token_secret.py:75 -#: perms/models/asset_permission.py:76 perms/serializers/permission.py:41 -#: perms/serializers/user_permission.py:74 xpack/plugins/cloud/models.py:324 -#: xpack/plugins/cloud/serializers/task.py:31 +#: perms/models/asset_permission.py:76 perms/serializers/permission.py:42 +#: perms/serializers/user_permission.py:74 xpack/plugins/cloud/models.py:332 +#: xpack/plugins/cloud/serializers/task.py:33 msgid "Protocols" msgstr "协议组" @@ -3173,15 +3165,15 @@ msgid "Ticket info" msgstr "工单信息" #: authentication/serializers/connection_token.py:21 -#: perms/models/asset_permission.py:77 perms/serializers/permission.py:37 -#: perms/serializers/permission.py:58 +#: perms/models/asset_permission.py:77 perms/serializers/permission.py:38 +#: perms/serializers/permission.py:59 #: tickets/models/ticket/apply_application.py:28 #: tickets/models/ticket/apply_asset.py:18 msgid "Actions" msgstr "动作" #: authentication/serializers/connection_token.py:42 -#: perms/serializers/permission.py:39 perms/serializers/permission.py:59 +#: perms/serializers/permission.py:40 perms/serializers/permission.py:60 #: users/serializers/user.py:97 users/serializers/user.py:171 msgid "Is expired" msgstr "已过期" @@ -3195,8 +3187,8 @@ msgstr "{} 不能为空" msgid "Access IP" msgstr "IP 白名单" -#: authentication/serializers/token.py:92 perms/serializers/permission.py:38 -#: perms/serializers/permission.py:60 users/serializers/user.py:98 +#: authentication/serializers/token.py:92 perms/serializers/permission.py:39 +#: perms/serializers/permission.py:61 users/serializers/user.py:98 #: users/serializers/user.py:168 msgid "Is valid" msgstr "是否有效" @@ -3664,7 +3656,7 @@ msgid "Invalid ids for ids, should be a list" msgstr "无效的ID,应为列表" #: common/db/fields.py:585 common/db/fields.py:590 -#: common/serializers/fields.py:132 tickets/serializers/ticket/common.py:58 +#: common/serializers/fields.py:136 tickets/serializers/ticket/common.py:58 #: xpack/plugins/cloud/serializers/account_attrs.py:56 #: xpack/plugins/cloud/serializers/account_attrs.py:79 #: xpack/plugins/cloud/serializers/account_attrs.py:150 @@ -3835,21 +3827,21 @@ msgstr "请在 {} 秒后发送" msgid "Children" msgstr "节点" -#: common/serializers/fields.py:133 +#: common/serializers/fields.py:137 #, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "错误的 pk \"{pk_value}\" - 对象不存在" -#: common/serializers/fields.py:134 +#: common/serializers/fields.py:138 #, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "错误类型。期望 pk 值,收到 {data_type}。" -#: common/serializers/fields.py:208 +#: common/serializers/fields.py:212 msgid "Invalid data type, should be list" msgstr "错误的数据类型,应该是列表" -#: common/serializers/fields.py:223 +#: common/serializers/fields.py:227 msgid "Invalid choice: {}" msgstr "无效选项: {}" @@ -3980,15 +3972,15 @@ msgstr "系统信息" msgid "Publish the station message" msgstr "发布站内消息" -#: ops/ansible/inventory.py:95 ops/models/job.py:63 +#: ops/ansible/inventory.py:96 ops/models/job.py:63 msgid "No account available" msgstr "无可用账号" -#: ops/ansible/inventory.py:259 +#: ops/ansible/inventory.py:260 msgid "Ansible disabled" msgstr "Ansible 已禁用" -#: ops/ansible/inventory.py:275 +#: ops/ansible/inventory.py:276 msgid "Skip hosts below:" msgstr "跳过以下主机: " @@ -4191,7 +4183,7 @@ msgid "Date last run" msgstr "最后运行日期" #: ops/models/base.py:51 ops/models/job.py:236 -#: xpack/plugins/cloud/models.py:199 +#: xpack/plugins/cloud/models.py:202 msgid "Result" msgstr "结果" @@ -6860,12 +6852,15 @@ msgstr "RDS 最大断开时间(毫秒)" msgid "" "Tips: Set the maximum duration for keeping a disconnected session active on " "the server (log off the session after 60000 milliseconds)." -msgstr "提示:设置某个已断开连接的会话在服务器上能保持活动状态的最长时间(60000 毫秒后注销会话)" +msgstr "" +"提示:设置某个已断开连接的会话在服务器上能保持活动状态的最长时间(60000 毫秒" +"后注销会话)" #: terminal/serializers/applet_host.py:60 msgid "RDS Remote App Logoff Time Limit (ms)" msgstr "RDS 远程应用注销时间限制(毫秒)" +#: terminal/serializers/applet_host.py:62 msgid "" "Tips: Set the logoff time for RemoteApp sessions after closing all RemoteApp " "programs (0 milliseconds, log off the session immediately)." @@ -7022,7 +7017,7 @@ msgstr "Access key ID(AK)" msgid "Access key secret" msgstr "Access key secret(SK)" -#: terminal/serializers/storage.py:68 xpack/plugins/cloud/models.py:250 +#: terminal/serializers/storage.py:68 xpack/plugins/cloud/models.py:253 msgid "Region" msgstr "地域" @@ -7466,7 +7461,7 @@ msgstr "工单信息" msgid "Ticket approval" msgstr "工单审批" -#: tickets/templates/tickets/approve_check_password.html:44 +#: tickets/templates/tickets/approve_check_password.html:43 msgid "Approval" msgstr "同意" @@ -7682,31 +7677,31 @@ msgstr "需要更新密码" msgid "Date api key used" msgstr "Api key 最后使用日期" -#: users/models/user.py:977 +#: users/models/user.py:984 msgid "Can not delete admin user" msgstr "无法删除管理员用户" -#: users/models/user.py:1004 +#: users/models/user.py:1011 msgid "Can invite user" msgstr "可以邀请用户" -#: users/models/user.py:1005 +#: users/models/user.py:1012 msgid "Can remove user" msgstr "可以移除用户" -#: users/models/user.py:1006 +#: users/models/user.py:1013 msgid "Can match user" msgstr "可以匹配用户" -#: users/models/user.py:1015 +#: users/models/user.py:1022 msgid "Administrator" msgstr "管理员" -#: users/models/user.py:1018 +#: users/models/user.py:1025 msgid "Administrator is the super user of system" msgstr "Administrator是初始的超级管理员" -#: users/models/user.py:1043 +#: users/models/user.py:1050 msgid "User password history" msgstr "用户密码历史" @@ -7916,7 +7911,7 @@ msgstr "周期检测用户过期" msgid "Check unused users" msgstr "检查未使用的用户" -#: users/tasks.py:122 +#: users/tasks.py:123 msgid "The user has not logged in recently and has been disabled." msgstr "该用户最近未登录,已被禁用。" @@ -8170,6 +8165,11 @@ msgstr "重置密码成功,返回到登录页面" msgid "XPACK" msgstr "XPack" +#: xpack/exceptions.py:7 +msgid "" +"The current task is not synchronized with unmatched policy assets, skipping" +msgstr "" + #: xpack/plugins/cloud/api.py:56 msgid "Test connection successful" msgstr "测试成功" @@ -8274,7 +8274,7 @@ msgstr "私有IP" msgid "Public IP" msgstr "公网IP" -#: xpack/plugins/cloud/const.py:41 xpack/plugins/cloud/models.py:295 +#: xpack/plugins/cloud/const.py:41 xpack/plugins/cloud/models.py:303 msgid "Instance name" msgstr "实例名称" @@ -8302,7 +8302,15 @@ msgstr "已同步" msgid "Released" msgstr "已释放" -#: xpack/plugins/cloud/manager.py:54 +#: xpack/plugins/cloud/const.py:58 +msgid "And" +msgstr "与" + +#: xpack/plugins/cloud/const.py:59 +msgid "Or" +msgstr "或" + +#: xpack/plugins/cloud/manager.py:57 msgid "Account unavailable" msgstr "账号无效" @@ -8326,7 +8334,7 @@ msgstr "云账号" msgid "Test cloud account" msgstr "测试云账号" -#: xpack/plugins/cloud/models.py:92 xpack/plugins/cloud/serializers/task.py:151 +#: xpack/plugins/cloud/models.py:92 xpack/plugins/cloud/serializers/task.py:159 msgid "Regions" msgstr "地域" @@ -8335,122 +8343,134 @@ msgid "Hostname strategy" msgstr "主机名策略" #: xpack/plugins/cloud/models.py:100 -#: xpack/plugins/cloud/serializers/task.py:154 +#: xpack/plugins/cloud/serializers/task.py:162 msgid "IP network segment group" msgstr "IP网段组" #: xpack/plugins/cloud/models.py:103 -#: xpack/plugins/cloud/serializers/task.py:159 +#: xpack/plugins/cloud/serializers/task.py:167 msgid "Sync IP type" msgstr "同步IP类型" #: xpack/plugins/cloud/models.py:106 -#: xpack/plugins/cloud/serializers/task.py:177 +#: xpack/plugins/cloud/serializers/task.py:185 msgid "Always update" msgstr "总是更新" -#: xpack/plugins/cloud/models.py:112 +#: xpack/plugins/cloud/models.py:108 +msgid "Fully synchronous" +msgstr "完全同步" + +#: xpack/plugins/cloud/models.py:113 msgid "Date last sync" msgstr "最后同步日期" -#: xpack/plugins/cloud/models.py:115 xpack/plugins/cloud/models.py:313 -#: xpack/plugins/cloud/models.py:337 +#: xpack/plugins/cloud/models.py:116 xpack/plugins/cloud/models.py:321 +#: xpack/plugins/cloud/models.py:345 msgid "Strategy" msgstr "策略" -#: xpack/plugins/cloud/models.py:120 xpack/plugins/cloud/models.py:197 +#: xpack/plugins/cloud/models.py:121 xpack/plugins/cloud/models.py:200 msgid "Sync instance task" msgstr "同步实例任务" -#: xpack/plugins/cloud/models.py:208 xpack/plugins/cloud/models.py:260 +#: xpack/plugins/cloud/models.py:211 xpack/plugins/cloud/models.py:263 msgid "Date sync" msgstr "同步日期" -#: xpack/plugins/cloud/models.py:212 +#: xpack/plugins/cloud/models.py:215 msgid "Sync instance snapshot" msgstr "同步实例快照" -#: xpack/plugins/cloud/models.py:216 +#: xpack/plugins/cloud/models.py:219 msgid "Sync instance task execution" msgstr "同步实例任务执行" -#: xpack/plugins/cloud/models.py:240 +#: xpack/plugins/cloud/models.py:243 msgid "Sync task" msgstr "同步任务" -#: xpack/plugins/cloud/models.py:244 +#: xpack/plugins/cloud/models.py:247 msgid "Sync instance task history" msgstr "同步实例任务历史" -#: xpack/plugins/cloud/models.py:247 +#: xpack/plugins/cloud/models.py:250 msgid "Instance" msgstr "实例" -#: xpack/plugins/cloud/models.py:264 +#: xpack/plugins/cloud/models.py:267 msgid "Sync instance detail" msgstr "同步实例详情" -#: xpack/plugins/cloud/models.py:281 +#: xpack/plugins/cloud/models.py:279 xpack/plugins/cloud/serializers/task.py:72 +msgid "Rule relation" +msgstr "条件关系" + +#: xpack/plugins/cloud/models.py:288 msgid "Task strategy" msgstr "任务策略" -#: xpack/plugins/cloud/models.py:285 +#: xpack/plugins/cloud/models.py:292 msgid "Equal" msgstr "等于" -#: xpack/plugins/cloud/models.py:286 +#: xpack/plugins/cloud/models.py:293 msgid "Not Equal" msgstr "不等于" -#: xpack/plugins/cloud/models.py:287 +#: xpack/plugins/cloud/models.py:294 msgid "In" msgstr "在...中" -#: xpack/plugins/cloud/models.py:288 +#: xpack/plugins/cloud/models.py:295 msgid "Contains" msgstr "包含" -#: xpack/plugins/cloud/models.py:289 +#: xpack/plugins/cloud/models.py:296 +msgid "Exclude" +msgstr "排除" + +#: xpack/plugins/cloud/models.py:297 msgid "Startswith" msgstr "以...开头" -#: xpack/plugins/cloud/models.py:290 +#: xpack/plugins/cloud/models.py:298 msgid "Endswith" msgstr "以...结尾" -#: xpack/plugins/cloud/models.py:296 +#: xpack/plugins/cloud/models.py:304 msgid "Instance platform" msgstr "实例平台" -#: xpack/plugins/cloud/models.py:297 +#: xpack/plugins/cloud/models.py:305 msgid "Instance address" msgstr "实例地址" -#: xpack/plugins/cloud/models.py:304 +#: xpack/plugins/cloud/models.py:312 msgid "Rule attr" msgstr "规则属性" -#: xpack/plugins/cloud/models.py:308 +#: xpack/plugins/cloud/models.py:316 msgid "Rule match" msgstr "规则匹配" -#: xpack/plugins/cloud/models.py:310 +#: xpack/plugins/cloud/models.py:318 msgid "Rule value" msgstr "规则值" -#: xpack/plugins/cloud/models.py:317 xpack/plugins/cloud/serializers/task.py:70 +#: xpack/plugins/cloud/models.py:325 xpack/plugins/cloud/serializers/task.py:75 msgid "Strategy rule" msgstr "条件" -#: xpack/plugins/cloud/models.py:332 +#: xpack/plugins/cloud/models.py:340 msgid "Action attr" msgstr "动作属性" -#: xpack/plugins/cloud/models.py:334 +#: xpack/plugins/cloud/models.py:342 msgid "Action value" msgstr "动作值" -#: xpack/plugins/cloud/models.py:341 xpack/plugins/cloud/serializers/task.py:73 +#: xpack/plugins/cloud/models.py:349 xpack/plugins/cloud/serializers/task.py:78 msgid "Strategy action" msgstr "动作" @@ -8737,7 +8757,7 @@ msgstr "测试超时时间" msgid "Project" msgstr "project" -#: xpack/plugins/cloud/serializers/task.py:143 +#: xpack/plugins/cloud/serializers/task.py:151 msgid "" "Only instances matching the IP range will be synced.
    If the instance " "contains multiple IP addresses, the first IP address that matches will be " @@ -8749,11 +8769,11 @@ msgstr "" "到的 IP 地址将被用作创建的资产的 IP。
    默认值 * 表示同步所有实例和随机匹配 " "IP 地址。
    例如: 192.168.1.0/24,10.1.1.1-10.1.1.20。" -#: xpack/plugins/cloud/serializers/task.py:149 +#: xpack/plugins/cloud/serializers/task.py:157 msgid "History count" msgstr "执行次数" -#: xpack/plugins/cloud/serializers/task.py:150 +#: xpack/plugins/cloud/serializers/task.py:158 msgid "Instance count" msgstr "实例个数" @@ -8837,6 +8857,12 @@ msgstr "企业专业版" msgid "Ultimate edition" msgstr "企业旗舰版" +#~ msgid "Password can not contains `{{` or `}}`" +#~ msgstr "密码不能包含 `{{` 或 `}}` 字符" + +#~ msgid "Password can not contains `{%` or `%}`" +#~ msgstr "密码不能包含 `{%` 或 `%}` 字符" + #~ msgid "FeiShu query user failed" #~ msgstr "飞书查询用户失败" From 1358cf532f929f2d956ae0e62b73bb8c01942894 Mon Sep 17 00:00:00 2001 From: ibuler Date: Mon, 18 Dec 2023 17:56:16 +0800 Subject: [PATCH 082/111] =?UTF-8?q?perf:=20=E4=BF=AE=E6=94=B9=20labels=20?= =?UTF-8?q?=E5=92=8C=20role=20=E6=90=9C=E7=B4=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/common/serializers/fields.py | 5 ++++- apps/labels/api.py | 29 +++++++++-------------------- apps/labels/const.py | 24 ++++++++++++++++++++++++ apps/labels/models.py | 4 +++- apps/labels/serializers.py | 21 ++++++++++++++++----- apps/ops/api/job.py | 7 ++++--- apps/ops/models/job.py | 9 ++------- apps/ops/models/playbook.py | 3 +-- apps/rbac/api/role.py | 13 +++++++++++++ 9 files changed, 76 insertions(+), 39 deletions(-) diff --git a/apps/common/serializers/fields.py b/apps/common/serializers/fields.py index bb3bafbf6..e206afcb3 100644 --- a/apps/common/serializers/fields.py +++ b/apps/common/serializers/fields.py @@ -59,7 +59,10 @@ class EncryptedField(serializers.CharField): class LabeledChoiceField(ChoiceField): def __init__(self, *args, **kwargs): super(LabeledChoiceField, self).__init__(*args, **kwargs) - self.choice_mapper = { + + @property + def choice_mapper(self): + return { key: value for key, value in self.choices.items() } diff --git a/apps/labels/api.py b/apps/labels/api.py index 26ef68685..24c1d0463 100644 --- a/apps/labels/api.py +++ b/apps/labels/api.py @@ -9,9 +9,10 @@ from orgs.utils import current_org from rbac.models import ContentType from rbac.serializers import ContentTypeSerializer from . import serializers +from .const import label_resource_types from .models import Label, LabeledResource -__all__ = ['LabelViewSet'] +__all__ = ['LabelViewSet', 'ContentTypeViewSet'] class ContentTypeViewSet(JMSModelViewSet): @@ -25,26 +26,8 @@ class ContentTypeViewSet(JMSModelViewSet): can_labeled_content_type = [] model = ContentType - @classmethod - def get_can_labeled_content_type_ids(cls): - if cls.can_labeled_content_type: - return cls.can_labeled_content_type - content_types = ContentType.objects.all() - for ct in content_types: - model_cls = ct.model_class() - if not model_cls: - continue - if model_cls._meta.parents: - continue - if 'labels' in model_cls._meta._forward_fields_map.keys(): - # if issubclass(model_cls, LabeledMixin): - cls.can_labeled_content_type.append(ct.id) - return cls.can_labeled_content_type - def get_queryset(self): - ids = self.get_can_labeled_content_type_ids() - queryset = ContentType.objects.filter(id__in=ids) - return queryset + return label_resource_types @action(methods=['GET'], detail=True, serializer_class=serializers.ContentTypeResourceSerializer) def resources(self, request, *args, **kwargs): @@ -62,6 +45,7 @@ class ContentTypeViewSet(JMSModelViewSet): keyword = request.query_params.get('search') if keyword: queryset = content_type.filter_queryset(queryset, keyword) + queryset = queryset.order_by('res_type') return self.get_paginated_response_from_queryset(queryset) @@ -145,3 +129,8 @@ class LabeledResourceViewSet(OrgBulkModelViewSet): 'default': serializers.LabeledResourceSerializer, } ordering_fields = ('res_type', 'date_created') + + def get_queryset(self): + queryset = super().get_queryset() + queryset = queryset.order_by('res_type') + return queryset diff --git a/apps/labels/const.py b/apps/labels/const.py index e69de29bb..cf7038235 100644 --- a/apps/labels/const.py +++ b/apps/labels/const.py @@ -0,0 +1,24 @@ +from django.contrib.contenttypes.models import ContentType +from django.utils.functional import LazyObject + + +class LabeledResourceType(LazyObject): + @staticmethod + def get_res_types(): + content_types = ContentType.objects.all() + ids = [] + for ct in content_types: + model_cls = ct.model_class() + if not model_cls: + continue + if model_cls._meta.parents: + continue + if 'labels' in model_cls._meta._forward_fields_map.keys(): + ids.append(ct.id) + return ContentType.objects.filter(id__in=ids) + + def _setup(self): + self._wrapped = self.get_res_types() + + +label_resource_types = LabeledResourceType() diff --git a/apps/labels/models.py b/apps/labels/models.py index 3e38da48e..9d06f76e3 100644 --- a/apps/labels/models.py +++ b/apps/labels/models.py @@ -25,7 +25,9 @@ class Label(JMSOrgBaseModel): class LabeledResource(JMSOrgBaseModel): - label = models.ForeignKey(Label, on_delete=models.CASCADE, related_name='labeled_resources') + label = models.ForeignKey( + Label, on_delete=models.CASCADE, related_name='labeled_resources', verbose_name=_("Label") + ) res_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) res_id = models.CharField(max_length=36, verbose_name=_("Resource ID"), db_index=True) resource = GenericForeignKey('res_type', 'res_id') diff --git a/apps/labels/serializers.py b/apps/labels/serializers.py index 157afc92e..60b0ad4b1 100644 --- a/apps/labels/serializers.py +++ b/apps/labels/serializers.py @@ -1,10 +1,10 @@ -from django.contrib.contenttypes.models import ContentType from django.db.models import Count from django.utils.translation import gettext_lazy as _ from rest_framework import serializers -from common.serializers.fields import ObjectRelatedField +from common.serializers.fields import ObjectRelatedField, LabeledChoiceField from orgs.mixins.serializers import BulkOrgResourceModelSerializer +from .const import label_resource_types from .models import Label, LabeledResource __all__ = ['LabelSerializer', 'LabeledResourceSerializer', 'ContentTypeResourceSerializer'] @@ -27,10 +27,10 @@ class LabelSerializer(BulkOrgResourceModelSerializer): class LabeledResourceSerializer(serializers.ModelSerializer): - res_type = ObjectRelatedField( - queryset=ContentType.objects, attrs=('app_label', 'model', 'name'), label=_("Resource type") + res_type = LabeledChoiceField( + choices=[], label=_("Resource type"), source='res_type_id', required=False ) - label = ObjectRelatedField(queryset=Label.objects, attrs=('name', 'value')) + label = ObjectRelatedField(queryset=Label.objects, attrs=('name', 'value'), label=_("Label")) resource = serializers.CharField(label=_("Resource")) class Meta: @@ -44,6 +44,17 @@ class LabeledResourceSerializer(serializers.ModelSerializer): queryset = queryset.select_related('label', 'res_type') return queryset + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.set_res_type_choices() + + def set_res_type_choices(self): + res_type_field = self.fields.get('res_type') + if not res_type_field: + return + + res_type_field.choices = [(ct.id, ct.name) for ct in label_resource_types] + class ContentTypeResourceSerializer(serializers.Serializer): id = serializers.CharField() diff --git a/apps/ops/api/job.py b/apps/ops/api/job.py index 3b07e850d..f16fefde2 100644 --- a/apps/ops/api/job.py +++ b/apps/ops/api/job.py @@ -1,12 +1,12 @@ import json import os + from django.conf import settings from django.db import transaction from django.db.models import Count from django.shortcuts import get_object_or_404 from django.utils._os import safe_join from django.utils.translation import gettext_lazy as _ - from rest_framework.decorators import action from rest_framework.response import Response from rest_framework.views import APIView @@ -167,7 +167,7 @@ class JobExecutionViewSet(OrgBulkModelViewSet): @staticmethod def start_deploy(instance, serializer): - task = run_ops_job_execution.apply_async((str(instance.id),), task_id=str(instance.id)) + run_ops_job_execution.apply_async((str(instance.id),), task_id=str(instance.id)) def perform_create(self, serializer): instance = serializer.save() @@ -179,7 +179,8 @@ class JobExecutionViewSet(OrgBulkModelViewSet): set_task_to_serializer_data(serializer, instance.id) transaction.on_commit( - lambda: run_ops_job_execution.apply_async((str(instance.id),), task_id=str(instance.id))) + lambda: run_ops_job_execution.apply_async((str(instance.id),), task_id=str(instance.id)) + ) def get_queryset(self): queryset = super().get_queryset() diff --git a/apps/ops/models/job.py b/apps/ops/models/job.py index bf460232f..9fcc079ca 100644 --- a/apps/ops/models/job.py +++ b/apps/ops/models/job.py @@ -22,7 +22,6 @@ from acls.models import CommandFilterACL from assets.models import Asset from assets.automations.base.manager import SSHTunnelManager from common.db.encoder import ModelJSONFieldEncoder -from labels.mixins import LabeledMixin from ops.ansible import JMSInventory, AdHocRunner, PlaybookRunner, CommandInBlackListException, UploadFileRunner from ops.mixin import PeriodTaskModelMixin from ops.variables import * @@ -133,19 +132,15 @@ class JMSPermedInventory(JMSInventory): return mapper -class Job(LabeledMixin, JMSOrgBaseModel, PeriodTaskModelMixin): +class Job(JMSOrgBaseModel, PeriodTaskModelMixin): name = models.CharField(max_length=128, null=True, verbose_name=_('Name')) - instant = models.BooleanField(default=False) args = models.CharField(max_length=8192, default='', verbose_name=_('Args'), null=True, blank=True) module = models.CharField(max_length=128, choices=JobModules.choices, default=JobModules.shell, - verbose_name=_('Module'), - null=True) + verbose_name=_('Module'), null=True) chdir = models.CharField(default="", max_length=1024, verbose_name=_('Chdir'), null=True, blank=True) timeout = models.IntegerField(default=-1, verbose_name=_('Timeout (Seconds)')) - playbook = models.ForeignKey('ops.Playbook', verbose_name=_("Playbook"), null=True, on_delete=models.SET_NULL) - type = models.CharField(max_length=128, choices=Types.choices, default=Types.adhoc, verbose_name=_("Type")) creator = models.ForeignKey('users.User', verbose_name=_("Creator"), on_delete=models.SET_NULL, null=True) assets = models.ManyToManyField('assets.Asset', verbose_name=_("Assets")) diff --git a/apps/ops/models/playbook.py b/apps/ops/models/playbook.py index caf226ee6..e1188cb46 100644 --- a/apps/ops/models/playbook.py +++ b/apps/ops/models/playbook.py @@ -6,7 +6,6 @@ from django.db import models from django.utils.translation import gettext_lazy as _ from private_storage.fields import PrivateFileField -from labels.mixins import LabeledMixin from ops.const import CreateMethods from ops.exception import PlaybookNoValidEntry from orgs.mixins.models import JMSOrgBaseModel @@ -24,7 +23,7 @@ dangerous_keywords = ( ) -class Playbook(LabeledMixin, JMSOrgBaseModel): +class Playbook(JMSOrgBaseModel): id = models.UUIDField(default=uuid.uuid4, primary_key=True) name = models.CharField(max_length=128, verbose_name=_('Name'), null=True) path = PrivateFileField(upload_to='playbooks/') diff --git a/apps/rbac/api/role.py b/apps/rbac/api/role.py index 387d0bc9c..093c79206 100644 --- a/apps/rbac/api/role.py +++ b/apps/rbac/api/role.py @@ -56,9 +56,22 @@ class RoleViewSet(JMSModelViewSet): return instance.permissions.set(clone.get_permissions()) + def filter_builtins(self, queryset): + keyword = self.request.query_params.get('search') + if not keyword: + return queryset + + builtins = list(self.queryset.filter(builtin=True)) + matched = [role.id for role in builtins if keyword in role.display_name] + if not matched: + return queryset + queryset = list(queryset.exclude(id__in=matched)) + return queryset + builtins + def filter_queryset(self, queryset): queryset = super().filter_queryset(queryset) queryset = queryset.order_by(*self.ordering) + queryset = self.filter_builtins(queryset) return queryset def set_users_amount(self, queryset): From 1afad40dd338978f778e1d9432cd514ef464fb07 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Tue, 19 Dec 2023 10:20:11 +0800 Subject: [PATCH 083/111] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=20labels=20?= =?UTF-8?q?=E7=BB=91=E5=AE=9A=E8=B5=84=E6=BA=90=20(#12361)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * perf: 优化 labels 绑定资源 * perf: 优化 labels list 显示 * perf: add migrations --------- Co-authored-by: ibuler --- apps/labels/api.py | 1 - apps/labels/const.py | 2 +- .../migrations/0003_alter_labeledresource_options_and_more.py | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/apps/labels/api.py b/apps/labels/api.py index 24c1d0463..2dc51e4b8 100644 --- a/apps/labels/api.py +++ b/apps/labels/api.py @@ -45,7 +45,6 @@ class ContentTypeViewSet(JMSModelViewSet): keyword = request.query_params.get('search') if keyword: queryset = content_type.filter_queryset(queryset, keyword) - queryset = queryset.order_by('res_type') return self.get_paginated_response_from_queryset(queryset) diff --git a/apps/labels/const.py b/apps/labels/const.py index cf7038235..bf77ed46b 100644 --- a/apps/labels/const.py +++ b/apps/labels/const.py @@ -1,10 +1,10 @@ -from django.contrib.contenttypes.models import ContentType from django.utils.functional import LazyObject class LabeledResourceType(LazyObject): @staticmethod def get_res_types(): + from rbac.models import ContentType content_types = ContentType.objects.all() ids = [] for ct in content_types: diff --git a/apps/labels/migrations/0003_alter_labeledresource_options_and_more.py b/apps/labels/migrations/0003_alter_labeledresource_options_and_more.py index 852376180..c879b7888 100644 --- a/apps/labels/migrations/0003_alter_labeledresource_options_and_more.py +++ b/apps/labels/migrations/0003_alter_labeledresource_options_and_more.py @@ -19,7 +19,7 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='labeledresource', name='label', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='labeled_resources', to='labels.label'), + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='labeled_resources', to='labels.label', verbose_name='Label'), ), migrations.AlterUniqueTogether( name='labeledresource', From 83b91cb739ebb575121088bff2ca179d5c5dab87 Mon Sep 17 00:00:00 2001 From: jiangweidong Date: Tue, 19 Dec 2023 11:43:59 +0800 Subject: [PATCH 084/111] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E5=91=BD?= =?UTF-8?q?=E4=BB=A4=E7=A6=81=E6=AD=A2=E5=8F=91=E9=80=81=E6=B6=88=E6=81=AF?= =?UTF-8?q?=E6=97=B6=EF=BC=8Cslack=E6=B6=88=E6=81=AF=E4=BC=9A=E5=8C=85?= =?UTF-8?q?=E5=90=ABhtml=E6=A0=87=E7=AD=BE=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/common/sdk/im/slack/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/common/sdk/im/slack/__init__.py b/apps/common/sdk/im/slack/__init__.py index 1f2532602..bea92e78e 100644 --- a/apps/common/sdk/im/slack/__init__.py +++ b/apps/common/sdk/im/slack/__init__.py @@ -38,6 +38,9 @@ class SlackRenderer(mistune.Renderer): lines[i] = prefix + line[4:-5] return '\n'.join(lines) + def block_code(self, code, lang=None): + return f'`{code}`' + def link(self, link, title, content): if title or content: label = str(title or content).strip() From da8d78f38410b3a9439f2fe4be01768e82d8aa8a Mon Sep 17 00:00:00 2001 From: feng <1304903146@qq.com> Date: Tue, 19 Dec 2023 14:57:51 +0800 Subject: [PATCH 085/111] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8Dansible=20?= =?UTF-8?q?=E4=BB=BB=E5=8A=A1=20{{123}}=20=E8=BF=99=E6=A0=B7=E7=9A=84?= =?UTF-8?q?=E5=AF=86=E7=A0=81=E5=A4=B1=E8=B4=A5=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/accounts/models/account.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/apps/accounts/models/account.py b/apps/accounts/models/account.py index 99de5ddc0..0c6d507bc 100644 --- a/apps/accounts/models/account.py +++ b/apps/accounts/models/account.py @@ -128,13 +128,19 @@ class Account(AbsConnectivity, LabeledMixin, BaseAccount): if not isinstance(value, str): return value - value = value.replace('{{', '__TEMP_OPEN_BRACES__') \ - .replace('}}', '__TEMP_CLOSE_BRACES__') + def escape(v): + v = v.replace('{{', '__TEMP_OPEN_BRACES__') \ + .replace('}}', '__TEMP_CLOSE_BRACES__') - value = value.replace('__TEMP_OPEN_BRACES__', '{{ "{{" }}') \ - .replace('__TEMP_CLOSE_BRACES__', '{{ "}}" }}') + v = v.replace('__TEMP_OPEN_BRACES__', '{{ "{{" }}') \ + .replace('__TEMP_CLOSE_BRACES__', '{{ "}}" }}') - return value.replace('{%', '{{ "{%" }}').replace('%}', '{{ "%}" }}') + return v.replace('{%', '{{ "{%" }}').replace('%}', '{{ "%}" }}') + + if value.startswith('{{') and value.endswith('}}'): + return '{{' + escape(value[2:-2]) + '}}' + else: + return escape(value) def replace_history_model_with_mixin(): From 8eab87f40ddfa5dade92aca0219e20bdc7457920 Mon Sep 17 00:00:00 2001 From: feng <1304903146@qq.com> Date: Tue, 19 Dec 2023 15:05:35 +0800 Subject: [PATCH 086/111] =?UTF-8?q?fix:=20=E5=91=BD=E4=BB=A4=E7=BB=84?= =?UTF-8?q?=E6=A8=A1=E7=B3=8A=E6=90=9C=E7=B4=A2=EF=BC=8C500?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/acls/api/command_acl.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/acls/api/command_acl.py b/apps/acls/api/command_acl.py index 2043f274d..8f7cc531a 100644 --- a/apps/acls/api/command_acl.py +++ b/apps/acls/api/command_acl.py @@ -11,7 +11,7 @@ __all__ = ['CommandFilterACLViewSet', 'CommandGroupViewSet'] class CommandGroupViewSet(OrgBulkModelViewSet): model = models.CommandGroup filterset_fields = ('name', 'command_filters') - search_fields = filterset_fields + search_fields = ('name',) serializer_class = serializers.CommandGroupSerializer From 3c7ba029dd042c66eed3c351ac2fce0b7af44e44 Mon Sep 17 00:00:00 2001 From: wangruidong <940853815@qq.com> Date: Mon, 18 Dec 2023 17:07:17 +0800 Subject: [PATCH 087/111] =?UTF-8?q?perf:=20=E5=B7=A5=E5=8D=95=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/templates/_foot_js.html | 4 +++- apps/tickets/templates/tickets/approve_check_password.html | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/apps/templates/_foot_js.html b/apps/templates/_foot_js.html index f91d20f41..0362e0253 100644 --- a/apps/templates/_foot_js.html +++ b/apps/templates/_foot_js.html @@ -28,9 +28,11 @@ .footer_beian { position: absolute; - top: 97%; left: 50%; transform: translate(-50%, -50%); + @media (min-width: 768px) { + top: 97%; + } } .footer_beian img { diff --git a/apps/tickets/templates/tickets/approve_check_password.html b/apps/tickets/templates/tickets/approve_check_password.html index b82ef76cc..dabaaf7d8 100644 --- a/apps/tickets/templates/tickets/approve_check_password.html +++ b/apps/tickets/templates/tickets/approve_check_password.html @@ -37,12 +37,12 @@
    {% csrf_token %} -
    - - From 59d9a3d4eca2f7e4e156207e87fc6ac3e52f1d97 Mon Sep 17 00:00:00 2001 From: feng <1304903146@qq.com> Date: Tue, 19 Dec 2023 15:39:22 +0800 Subject: [PATCH 088/111] =?UTF-8?q?fix:=20chatAI=E4=BB=A3=E7=90=86?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E9=94=99=E8=AF=AF=EF=BC=8C=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E5=99=A8=E6=8A=A5=E9=94=99500?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/settings/api/chat.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/apps/settings/api/chat.py b/apps/settings/api/chat.py index 830ab45ea..68a80f013 100644 --- a/apps/settings/api/chat.py +++ b/apps/settings/api/chat.py @@ -47,16 +47,17 @@ class ChatAITestingAPI(GenericAPIView): 'base_url': config['GPT_BASE_URL'] or None, 'api_key': config['GPT_API_KEY'], } - if proxy: - kwargs['http_client'] = httpx.Client( - proxies=proxy, - transport=httpx.HTTPTransport(local_address='0.0.0.0') - ) - client = openai.OpenAI(**kwargs) - - ok = False - error = '' try: + if proxy: + kwargs['http_client'] = httpx.Client( + proxies=proxy, + transport=httpx.HTTPTransport(local_address='0.0.0.0') + ) + client = openai.OpenAI(**kwargs) + + ok = False + error = '' + client.chat.completions.create( messages=[ { From fda3e6ec9bccb3e7d8b0693fe561ee5cf9b72b76 Mon Sep 17 00:00:00 2001 From: jiangweidong Date: Tue, 19 Dec 2023 16:26:21 +0800 Subject: [PATCH 089/111] =?UTF-8?q?perf:=20model=5Fto=5Fdict=E6=97=A0?= =?UTF-8?q?=E6=B3=95=E8=BD=AC=E6=8D=A2=E4=B8=8D=E5=8F=AF=E7=BC=96=E8=BE=91?= =?UTF-8?q?=E5=AD=97=E6=AE=B5=EF=BC=8C=E5=AF=BC=E8=87=B4=E6=B6=88=E6=81=AF?= =?UTF-8?q?=E4=B8=AD=E6=9C=89=E7=9A=84=E5=80=BC=E4=B8=BANone?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/tickets/notifications.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/tickets/notifications.py b/apps/tickets/notifications.py index 91c0a047d..b3791b98b 100644 --- a/apps/tickets/notifications.py +++ b/apps/tickets/notifications.py @@ -75,6 +75,8 @@ class BaseTicketMessage(UserMessage): field = fields[name] item = {'name': name, 'title': field.verbose_name} value = self.ticket.get_field_display(name, field, data) + if not value: + continue item['value'] = value items.append(item) return items From 45425b11d220a820ba8105ee0898ad61dfc85590 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Tue, 19 Dec 2023 18:46:02 +0800 Subject: [PATCH 090/111] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=20labels=20?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=A4=9A=E4=B8=AA=E6=90=9C=E7=B4=A2=20(#1236?= =?UTF-8?q?7)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: ibuler --- apps/common/drf/filters.py | 52 ++++++++++++++++++++++++++++---------- apps/labels/serializers.py | 14 +++++++++- apps/rbac/api/role.py | 2 +- 3 files changed, 53 insertions(+), 15 deletions(-) diff --git a/apps/common/drf/filters.py b/apps/common/drf/filters.py index 6df636fdf..103eef212 100644 --- a/apps/common/drf/filters.py +++ b/apps/common/drf/filters.py @@ -6,6 +6,7 @@ import logging from django.core.cache import cache from django.core.exceptions import ImproperlyConfigured +from django.db.models import Q, Count from django_filters import rest_framework as drf_filters from rest_framework import filters from rest_framework.compat import coreapi, coreschema @@ -178,9 +179,41 @@ class LabelFilterBackend(filters.BaseFilterBackend): ) ] + @staticmethod + def filter_resources(resources, labels_id): + label_ids = [i.strip() for i in labels_id.split(',')] + + args = [] + for label_id in label_ids: + kwargs = {} + if ':' in label_id: + k, v = label_id.split(':', 1) + kwargs['label__name'] = k.strip() + if v != '*': + kwargs['label__value'] = v.strip() + else: + kwargs['label_id'] = label_id + args.append(kwargs) + + if len(args) == 1: + resources = resources.filter(**args[0]) + return resources + + q = Q() + for kwarg in args: + q |= Q(**kwarg) + + resources = resources.filter(q) \ + .values('res_id') \ + .order_by('res_id') \ + .annotate(count=Count('res_id')) \ + .values('res_id', 'count') \ + .filter(count=len(args)) + return resources + def filter_queryset(self, request, queryset, view): - label_id = request.query_params.get('label') - if not label_id: + labels_id = request.query_params.get('labels') + if not labels_id: return queryset if not hasattr(queryset, 'model'): @@ -189,23 +222,16 @@ class LabelFilterBackend(filters.BaseFilterBackend): if not hasattr(queryset.model, 'labels'): return queryset - kwargs = {} - if ':' in label_id: - k, v = label_id.split(':', 1) - kwargs['label__name'] = k.strip() - if v != '*': - kwargs['label__value'] = v.strip() - else: - kwargs['label_id'] = label_id - model = queryset.model labeled_resource_cls = model.labels.field.related_model app_label = model._meta.app_label model_name = model._meta.model_name - res_ids = labeled_resource_cls.objects.filter( + resources = labeled_resource_cls.objects.filter( res_type__app_label=app_label, res_type__model=model_name, - ).filter(**kwargs).values_list('res_id', flat=True) + ) + resources = self.filter_resources(resources, labels_id) + res_ids = resources.values_list('res_id', flat=True) queryset = queryset.filter(id__in=set(res_ids)) return queryset diff --git a/apps/labels/serializers.py b/apps/labels/serializers.py index 60b0ad4b1..bd4d09b90 100644 --- a/apps/labels/serializers.py +++ b/apps/labels/serializers.py @@ -13,12 +13,24 @@ __all__ = ['LabelSerializer', 'LabeledResourceSerializer', 'ContentTypeResourceS class LabelSerializer(BulkOrgResourceModelSerializer): class Meta: model = Label - fields = ['id', 'name', 'value', 'res_count', 'comment', 'date_created', 'date_updated'] + fields = [ + 'id', 'name', 'value', 'res_count', 'comment', + 'date_created', 'date_updated' + ] read_only_fields = ('date_created', 'date_updated', 'res_count') extra_kwargs = { 'res_count': {'label': _('Resource count')}, } + @staticmethod + def validate_name(value): + if ':' in value or ',' in value: + raise serializers.ValidationError(_('Cannot contain ":,"')) + return value + + def validate_value(self, value): + return self.validate_name(value) + @classmethod def setup_eager_loading(cls, queryset): """ Perform necessary eager loading of data. """ diff --git a/apps/rbac/api/role.py b/apps/rbac/api/role.py index 093c79206..b9133c5c5 100644 --- a/apps/rbac/api/role.py +++ b/apps/rbac/api/role.py @@ -61,7 +61,7 @@ class RoleViewSet(JMSModelViewSet): if not keyword: return queryset - builtins = list(self.queryset.filter(builtin=True)) + builtins = list(self.get_queryset().filter(builtin=True)) matched = [role.id for role in builtins if keyword in role.display_name] if not matched: return queryset From 0b7552a6eedbf82b77fef70770638fd48f80c824 Mon Sep 17 00:00:00 2001 From: ibuler Date: Tue, 19 Dec 2023 18:42:44 +0800 Subject: [PATCH 091/111] =?UTF-8?q?perf:=20=E4=BF=AE=E6=94=B9=20labels=20?= =?UTF-8?q?=E7=BB=91=E5=AE=9A=E5=BC=95=E8=B5=B7=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/audits/utils.py | 3 +++ apps/common/drf/parsers/base.py | 6 +++++- apps/common/drf/renders/base.py | 3 ++- apps/labels/mixins.py | 10 +++++++++- apps/labels/models.py | 4 ++++ apps/labels/serializers.py | 2 +- apps/ops/serializers/job.py | 5 ++--- apps/ops/serializers/playbook.py | 5 ++--- 8 files changed, 28 insertions(+), 10 deletions(-) diff --git a/apps/audits/utils.py b/apps/audits/utils.py index 5813c3db8..4312962e6 100644 --- a/apps/audits/utils.py +++ b/apps/audits/utils.py @@ -37,6 +37,9 @@ def _get_instance_field_value( if not include_model_fields and not getattr(f, 'primary_key', False): continue + if isinstance(f, GenericForeignKey): + continue + if isinstance(f, (models.FileField, models.ImageField)): continue diff --git a/apps/common/drf/parsers/base.py b/apps/common/drf/parsers/base.py index 6d4b70788..33b344457 100644 --- a/apps/common/drf/parsers/base.py +++ b/apps/common/drf/parsers/base.py @@ -9,7 +9,7 @@ from rest_framework import status from rest_framework.exceptions import ParseError, APIException from rest_framework.parsers import BaseParser -from common.serializers.fields import ObjectRelatedField +from common.serializers.fields import ObjectRelatedField, LabeledChoiceField from common.utils import get_logger logger = get_logger(__file__) @@ -126,6 +126,10 @@ class BaseFileParser(BaseParser): value = [self.id_name_to_obj(v) for v in value] else: value = self.id_name_to_obj(value) + elif isinstance(field, LabeledChoiceField): + value = self.id_name_to_obj(value) + if isinstance(value, dict) and value.get('pk'): + value = value.get('pk') elif isinstance(field, serializers.ListSerializer): value = [self.parse_value(field.child, v) for v in value] elif isinstance(field, serializers.Serializer): diff --git a/apps/common/drf/renders/base.py b/apps/common/drf/renders/base.py index cdc1626ff..2c1038610 100644 --- a/apps/common/drf/renders/base.py +++ b/apps/common/drf/renders/base.py @@ -105,7 +105,8 @@ class BaseFileRenderer(BaseRenderer): elif isinstance(value, bool): value = 'Yes' if value else 'No' elif isinstance(field, LabeledChoiceField): - value = value.get('value', '') + value = value or {} + value = '{}({})'.format(value.get('label'), value.get('value')) elif isinstance(field, ObjectRelatedField): if field.many: value = [self.to_id_name(v) for v in value] diff --git a/apps/labels/mixins.py b/apps/labels/mixins.py index 23fa90386..90b76f3ed 100644 --- a/apps/labels/mixins.py +++ b/apps/labels/mixins.py @@ -7,7 +7,15 @@ __all__ = ['LabeledMixin'] class LabeledMixin(models.Model): - labels = GenericRelation(LabeledResource, object_id_field='res_id', content_type_field='res_type') + _labels = GenericRelation(LabeledResource, object_id_field='res_id', content_type_field='res_type') class Meta: abstract = True + + @property + def labels(self): + return self._labels + + @labels.setter + def labels(self, value): + self._labels.set(value, bulk=False) diff --git a/apps/labels/models.py b/apps/labels/models.py index 9d06f76e3..3bf9213f5 100644 --- a/apps/labels/models.py +++ b/apps/labels/models.py @@ -20,6 +20,10 @@ class Label(JMSOrgBaseModel): def res_count(self): return self.labeled_resources.count() + @lazyproperty + def display_name(self): + return '{}:{}'.format(self.name, self.value) + def __str__(self): return '{}:{}'.format(self.name, self.value) diff --git a/apps/labels/serializers.py b/apps/labels/serializers.py index bd4d09b90..803843ad8 100644 --- a/apps/labels/serializers.py +++ b/apps/labels/serializers.py @@ -42,7 +42,7 @@ class LabeledResourceSerializer(serializers.ModelSerializer): res_type = LabeledChoiceField( choices=[], label=_("Resource type"), source='res_type_id', required=False ) - label = ObjectRelatedField(queryset=Label.objects, attrs=('name', 'value'), label=_("Label")) + label = ObjectRelatedField(queryset=Label.objects, attrs=('id', 'display_name'), label=_("Label")) resource = serializers.CharField(label=_("Resource")) class Meta: diff --git a/apps/ops/serializers/job.py b/apps/ops/serializers/job.py index 6e95a1fb9..75729f988 100644 --- a/apps/ops/serializers/job.py +++ b/apps/ops/serializers/job.py @@ -4,14 +4,13 @@ from django.utils.translation import gettext_lazy as _ from rest_framework import serializers from assets.models import Asset -from common.serializers import ResourceLabelsMixin from common.serializers.fields import ReadableHiddenField from ops.mixin import PeriodTaskSerializerMixin from ops.models import Job, JobExecution from orgs.mixins.serializers import BulkOrgResourceModelSerializer -class JobSerializer(ResourceLabelsMixin, BulkOrgResourceModelSerializer, PeriodTaskSerializerMixin): +class JobSerializer(BulkOrgResourceModelSerializer, PeriodTaskSerializerMixin): creator = ReadableHiddenField(default=serializers.CurrentUserDefault()) run_after_save = serializers.BooleanField(label=_("Run after save"), default=False, required=False) nodes = serializers.ListField(required=False, child=serializers.CharField()) @@ -42,7 +41,7 @@ class JobSerializer(ResourceLabelsMixin, BulkOrgResourceModelSerializer, PeriodT ] fields = read_only_fields + [ "name", "instant", "type", "module", - "args", "playbook", "assets", "labels", + "args", "playbook", "assets", "runas_policy", "runas", "creator", "use_parameter_define", "parameters_define", "timeout", "chdir", "comment", "summary", diff --git a/apps/ops/serializers/playbook.py b/apps/ops/serializers/playbook.py index 2fb06d691..1ff48906a 100644 --- a/apps/ops/serializers/playbook.py +++ b/apps/ops/serializers/playbook.py @@ -2,7 +2,6 @@ import os from rest_framework import serializers -from common.serializers import ResourceLabelsMixin from common.serializers.fields import ReadableHiddenField from ops.models import Playbook from orgs.mixins.serializers import BulkOrgResourceModelSerializer @@ -13,7 +12,7 @@ def parse_playbook_name(path): return file_name.split(".")[-2] -class PlaybookSerializer(ResourceLabelsMixin, BulkOrgResourceModelSerializer): +class PlaybookSerializer(BulkOrgResourceModelSerializer): creator = ReadableHiddenField(default=serializers.CurrentUserDefault()) path = serializers.FileField(required=False) @@ -28,5 +27,5 @@ class PlaybookSerializer(ResourceLabelsMixin, BulkOrgResourceModelSerializer): read_only_fields = ["id", "date_created", "date_updated"] fields = read_only_fields + [ "id", 'path', "name", "comment", "creator", - 'create_method', 'vcs_url', 'labels' + 'create_method', 'vcs_url', ] From ef1875d9b5a85450030c5349881f5eb658b461da Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Tue, 19 Dec 2023 19:06:39 +0800 Subject: [PATCH 092/111] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E5=B7=A5?= =?UTF-8?q?=E5=8D=95=E6=98=BE=E7=A4=BA=20(#12368)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: wangruidong <940853815@qq.com> --- apps/templates/_base_double_screen.html | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/apps/templates/_base_double_screen.html b/apps/templates/_base_double_screen.html index d0281fc84..a87755592 100644 --- a/apps/templates/_base_double_screen.html +++ b/apps/templates/_base_double_screen.html @@ -22,20 +22,14 @@ -
    -
    -
    - {% block content %} {% endblock %} -
    -
    -
    -
    -
    - {% include '_copyright.html' %} -
    +
    +
    +
    + {% block content %} {% endblock %}
    +
    +
    -{% include '_foot_js.html' %} {% block custom_foot_js %} {% endblock %} From e18e019460a9f983c59ce6dcd783cb4cac343b3c Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Wed, 20 Dec 2023 11:05:42 +0800 Subject: [PATCH 093/111] =?UTF-8?q?fix:=20=E8=B4=A6=E5=8F=B7=E5=88=97?= =?UTF-8?q?=E8=A1=A8=EF=BC=8C=E6=B7=BB=E5=8A=A0=E8=B4=A6=E5=8F=B7=E6=A8=A1?= =?UTF-8?q?=E7=89=88=20500=20(#12375)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/accounts/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/accounts/utils.py b/apps/accounts/utils.py index 730db4a6c..e56469c7e 100644 --- a/apps/accounts/utils.py +++ b/apps/accounts/utils.py @@ -31,7 +31,7 @@ class SecretGenerator: 'upper': rules['uppercase'], 'digit': rules['digit'], 'special_char': rules['symbol'], - 'exclude_chars': rules['exclude_symbols'] + 'exclude_chars': rules.get('exclude_symbols', ''), } return random_string(**rules) From 9b2b71dddc08f94a0ca92f3b414c60d6c0d8b24e Mon Sep 17 00:00:00 2001 From: wangruidong <940853815@qq.com> Date: Wed, 20 Dec 2023 10:53:21 +0800 Subject: [PATCH 094/111] =?UTF-8?q?fix:=20=E5=B7=A5=E5=8D=95=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E7=B1=BB=E5=9E=8B=E6=B2=A1=E6=9C=89=E7=BF=BB=E8=AF=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/tickets/serializers/ticket/ticket.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/tickets/serializers/ticket/ticket.py b/apps/tickets/serializers/ticket/ticket.py index edeb97ad2..c5ed1a8d3 100644 --- a/apps/tickets/serializers/ticket/ticket.py +++ b/apps/tickets/serializers/ticket/ticket.py @@ -39,9 +39,10 @@ class TicketSerializer(OrgResourceModelSerializerMixin): tp = self.fields.get('type') if not tp: return - choices = tp.choices - choices.pop(TicketType.general, None) - tp.choices = choices + excluded_value = TicketType.general + self.fields['type'].choices = [ + (value, label) for value, label in tp.choices.items() if value != excluded_value + ] @classmethod def setup_eager_loading(cls, queryset): From ac5991fc43b71511e44b6b05bea4f2e180170c0d Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 20 Dec 2023 10:52:02 +0800 Subject: [PATCH 095/111] =?UTF-8?q?perf:=20=E4=BF=AE=E6=94=B9=E6=A0=87?= =?UTF-8?q?=E7=AD=BE=E7=9A=84=E6=90=9C=E7=B4=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/common/drf/filters.py | 2 +- apps/labels/const.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/common/drf/filters.py b/apps/common/drf/filters.py index 103eef212..0fb8ca63a 100644 --- a/apps/common/drf/filters.py +++ b/apps/common/drf/filters.py @@ -223,7 +223,7 @@ class LabelFilterBackend(filters.BaseFilterBackend): return queryset model = queryset.model - labeled_resource_cls = model.labels.field.related_model + labeled_resource_cls = model._labels.field.related_model app_label = model._meta.app_label model_name = model._meta.model_name diff --git a/apps/labels/const.py b/apps/labels/const.py index bf77ed46b..108b07df0 100644 --- a/apps/labels/const.py +++ b/apps/labels/const.py @@ -5,6 +5,7 @@ class LabeledResourceType(LazyObject): @staticmethod def get_res_types(): from rbac.models import ContentType + from .mixins import LabeledMixin content_types = ContentType.objects.all() ids = [] for ct in content_types: @@ -13,7 +14,7 @@ class LabeledResourceType(LazyObject): continue if model_cls._meta.parents: continue - if 'labels' in model_cls._meta._forward_fields_map.keys(): + if issubclass(model_cls, LabeledMixin): ids.append(ct.id) return ContentType.objects.filter(id__in=ids) From 6544f8ade82c5472c58e9336ccfe25be0399ce03 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Wed, 20 Dec 2023 14:31:03 +0800 Subject: [PATCH 096/111] =?UTF-8?q?perf:=20=E4=BF=AE=E6=94=B9=20labels=20?= =?UTF-8?q?=E6=90=9C=E7=B4=A2=20(#12379)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * perf: 修改标签的搜索 * perf: 修改 labels 搜索 --------- Co-authored-by: ibuler --- apps/common/serializers/fields.py | 11 +---------- apps/tickets/serializers/ticket/ticket.py | 7 +++---- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/apps/common/serializers/fields.py b/apps/common/serializers/fields.py index e206afcb3..ef2589628 100644 --- a/apps/common/serializers/fields.py +++ b/apps/common/serializers/fields.py @@ -57,19 +57,10 @@ class EncryptedField(serializers.CharField): class LabeledChoiceField(ChoiceField): - def __init__(self, *args, **kwargs): - super(LabeledChoiceField, self).__init__(*args, **kwargs) - - @property - def choice_mapper(self): - return { - key: value for key, value in self.choices.items() - } - def to_representation(self, key): if key is None: return key - label = self.choice_mapper.get(key, key) + label = self.choices.get(key, key) return {"value": key, "label": label} def to_internal_value(self, data): diff --git a/apps/tickets/serializers/ticket/ticket.py b/apps/tickets/serializers/ticket/ticket.py index c5ed1a8d3..607063367 100644 --- a/apps/tickets/serializers/ticket/ticket.py +++ b/apps/tickets/serializers/ticket/ticket.py @@ -39,10 +39,9 @@ class TicketSerializer(OrgResourceModelSerializerMixin): tp = self.fields.get('type') if not tp: return - excluded_value = TicketType.general - self.fields['type'].choices = [ - (value, label) for value, label in tp.choices.items() if value != excluded_value - ] + choices = tp.choices + choices.pop(TicketType.general, None) + tp.choices = choices.items() @classmethod def setup_eager_loading(cls, queryset): From d03ba7c391e2162444e8b1be4d4036e47413fb88 Mon Sep 17 00:00:00 2001 From: Eric Date: Wed, 20 Dec 2023 15:53:48 +0800 Subject: [PATCH 097/111] =?UTF-8?q?perf:=20=E9=A1=B5=E9=9D=A2=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E6=98=AF=E5=90=A6=E5=90=AF=E7=94=A8=20Vitual=20App?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/locale/ja/LC_MESSAGES/django.po | 354 +++++++++--------- apps/locale/zh/LC_MESSAGES/django.po | 350 ++++++++--------- apps/settings/api/settings.py | 2 + .../migrations/0012_alter_setting_options.py | 17 + apps/settings/models.py | 1 + apps/settings/serializers/feature.py | 10 +- 6 files changed, 391 insertions(+), 343 deletions(-) create mode 100644 apps/settings/migrations/0012_alter_setting_options.py diff --git a/apps/locale/ja/LC_MESSAGES/django.po b/apps/locale/ja/LC_MESSAGES/django.po index 6301ba33c..22e2047b8 100644 --- a/apps/locale/ja/LC_MESSAGES/django.po +++ b/apps/locale/ja/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-12-18 18:35+0800\n" +"POT-Creation-Date: 2023-12-20 15:46+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -256,7 +256,7 @@ msgstr "ユーザー %s がパスワードを閲覧/導き出しました" #: terminal/serializers/session.py:26 #: terminal/templates/terminal/_msg_command_warning.html:4 #: terminal/templates/terminal/_msg_session_sharing.html:4 -#: tickets/models/ticket/apply_asset.py:16 xpack/plugins/cloud/models.py:256 +#: tickets/models/ticket/apply_asset.py:16 xpack/plugins/cloud/models.py:253 msgid "Asset" msgstr "資産" @@ -360,7 +360,7 @@ msgstr "アカウントバックアップ計画" #: accounts/models/automations/backup_account.py:119 #: assets/models/automations/base.py:115 audits/models.py:65 -#: ops/models/base.py:55 ops/models/celery.py:63 ops/models/job.py:240 +#: ops/models/base.py:55 ops/models/celery.py:63 ops/models/job.py:235 #: ops/templates/ops/celery_task_log.html:75 #: perms/models/asset_permission.py:78 terminal/models/applet/host.py:141 #: terminal/models/session/session.py:44 @@ -388,14 +388,14 @@ msgid "Trigger mode" msgstr "トリガーモード" #: accounts/models/automations/backup_account.py:133 audits/models.py:203 -#: terminal/models/session/sharing.py:125 xpack/plugins/cloud/models.py:208 +#: terminal/models/session/sharing.py:125 xpack/plugins/cloud/models.py:205 msgid "Reason" msgstr "理由" #: accounts/models/automations/backup_account.py:135 #: accounts/serializers/automations/change_secret.py:105 #: accounts/serializers/automations/change_secret.py:128 -#: ops/serializers/job.py:65 terminal/serializers/session.py:49 +#: ops/serializers/job.py:64 terminal/serializers/session.py:49 msgid "Is success" msgstr "成功は" @@ -468,22 +468,22 @@ msgstr "開始日" #: accounts/models/automations/change_secret.py:42 #: assets/models/automations/base.py:116 ops/models/base.py:56 -#: ops/models/celery.py:64 ops/models/job.py:241 +#: ops/models/celery.py:64 ops/models/job.py:236 #: terminal/models/applet/host.py:142 msgid "Date finished" msgstr "終了日" #: accounts/models/automations/change_secret.py:43 #: assets/models/automations/base.py:113 audits/models.py:208 -#: audits/serializers.py:54 ops/models/base.py:49 ops/models/job.py:232 +#: audits/serializers.py:54 ops/models/base.py:49 ops/models/job.py:227 #: terminal/models/applet/applet.py:319 terminal/models/applet/host.py:140 #: terminal/models/component/status.py:30 #: terminal/models/virtualapp/virtualapp.py:99 #: terminal/serializers/applet.py:18 terminal/serializers/applet_host.py:136 #: terminal/serializers/virtualapp.py:35 tickets/models/ticket/general.py:283 #: tickets/serializers/super_ticket.py:13 -#: tickets/serializers/ticket/ticket.py:20 xpack/plugins/cloud/models.py:204 -#: xpack/plugins/cloud/models.py:260 +#: tickets/serializers/ticket/ticket.py:20 xpack/plugins/cloud/models.py:201 +#: xpack/plugins/cloud/models.py:257 msgid "Status" msgstr "ステータス" @@ -601,10 +601,10 @@ msgstr "パスワードルール" #: authentication/serializers/connect_token_secret.py:113 #: authentication/serializers/connect_token_secret.py:168 labels/models.py:11 #: ops/mixin.py:21 ops/models/adhoc.py:20 ops/models/celery.py:15 -#: ops/models/celery.py:57 ops/models/job.py:137 ops/models/playbook.py:29 -#: ops/serializers/job.py:19 orgs/models.py:82 +#: ops/models/celery.py:57 ops/models/job.py:136 ops/models/playbook.py:28 +#: ops/serializers/job.py:18 orgs/models.py:82 #: perms/models/asset_permission.py:61 rbac/models/role.py:29 -#: settings/models.py:33 settings/models.py:180 settings/serializers/msg.py:82 +#: settings/models.py:33 settings/models.py:181 settings/serializers/msg.py:82 #: terminal/models/applet/applet.py:33 terminal/models/component/endpoint.py:12 #: terminal/models/component/endpoint.py:95 #: terminal/models/component/storage.py:26 terminal/models/component/task.py:13 @@ -613,8 +613,8 @@ msgstr "パスワードルール" #: terminal/models/virtualapp/virtualapp.py:19 tickets/api/ticket.py:87 #: users/forms/profile.py:33 users/models/group.py:13 #: users/models/preference.py:11 users/models/user.py:800 -#: xpack/plugins/cloud/models.py:32 xpack/plugins/cloud/models.py:276 -#: xpack/plugins/cloud/serializers/task.py:70 +#: xpack/plugins/cloud/models.py:32 xpack/plugins/cloud/models.py:273 +#: xpack/plugins/cloud/serializers/task.py:68 msgid "Name" msgstr "名前" @@ -644,7 +644,7 @@ msgstr "プラットフォーム" msgid "Push params" msgstr "パラメータをプッシュする" -#: accounts/models/template.py:26 xpack/plugins/cloud/models.py:333 +#: accounts/models/template.py:26 xpack/plugins/cloud/models.py:325 msgid "Account template" msgstr "アカウント テンプレート" @@ -755,7 +755,7 @@ msgstr "カテゴリ" #: assets/serializers/asset/common.py:123 assets/serializers/platform.py:120 #: assets/serializers/platform.py:139 audits/serializers.py:53 #: audits/serializers.py:170 -#: authentication/serializers/connect_token_secret.py:126 ops/models/job.py:149 +#: authentication/serializers/connect_token_secret.py:126 ops/models/job.py:144 #: perms/serializers/user_permission.py:26 terminal/models/applet/applet.py:39 #: terminal/models/component/storage.py:57 #: terminal/models/component/storage.py:146 terminal/serializers/applet.py:29 @@ -792,7 +792,7 @@ msgstr "編集済み" #: assets/models/automations/base.py:19 #: assets/serializers/automations/base.py:20 #: authentication/api/connection_token.py:404 ops/models/base.py:17 -#: ops/models/job.py:151 ops/serializers/job.py:20 +#: ops/models/job.py:146 ops/serializers/job.py:19 #: terminal/templates/terminal/_msg_command_execute_alert.html:16 msgid "Assets" msgstr "資産" @@ -921,7 +921,7 @@ msgstr "关联平台,可以配置推送参数,如果不关联,则使用默 #: accounts/serializers/account/virtual.py:19 assets/models/_user.py:27 #: assets/models/cmd_filter.py:40 assets/models/cmd_filter.py:88 #: assets/models/group.py:20 common/db/models.py:36 ops/models/adhoc.py:26 -#: ops/models/job.py:157 ops/models/playbook.py:32 rbac/models/role.py:37 +#: ops/models/job.py:152 ops/models/playbook.py:31 rbac/models/role.py:37 #: settings/models.py:38 terminal/models/applet/applet.py:45 #: terminal/models/applet/applet.py:320 terminal/models/applet/host.py:143 #: terminal/models/component/endpoint.py:25 @@ -929,7 +929,7 @@ msgstr "关联平台,可以配置推送参数,如果不关联,则使用默 #: terminal/models/session/session.py:46 #: terminal/models/virtualapp/virtualapp.py:28 tickets/models/comment.py:32 #: tickets/models/ticket/general.py:297 users/models/user.py:836 -#: xpack/plugins/cloud/models.py:39 xpack/plugins/cloud/models.py:110 +#: xpack/plugins/cloud/models.py:39 xpack/plugins/cloud/models.py:109 msgid "Comment" msgstr "コメント" @@ -1074,13 +1074,13 @@ msgstr "通知" #: acls/models/base.py:37 assets/models/_user.py:51 #: assets/models/cmd_filter.py:76 terminal/models/component/endpoint.py:98 -#: xpack/plugins/cloud/models.py:282 +#: xpack/plugins/cloud/models.py:275 msgid "Priority" msgstr "優先順位" #: acls/models/base.py:38 assets/models/_user.py:51 #: assets/models/cmd_filter.py:76 terminal/models/component/endpoint.py:99 -#: xpack/plugins/cloud/models.py:283 +#: xpack/plugins/cloud/models.py:276 msgid "1-100, the lower the value will be match first" msgstr "1-100、低い値は最初に一致します" @@ -1108,7 +1108,7 @@ msgid "Accounts" msgstr "アカウント" #: acls/models/command_acl.py:16 assets/models/cmd_filter.py:60 -#: ops/serializers/job.py:64 terminal/const.py:86 +#: ops/serializers/job.py:63 terminal/const.py:86 #: terminal/models/session/session.py:42 terminal/serializers/command.py:18 #: terminal/templates/terminal/_msg_command_alert.html:12 #: terminal/templates/terminal/_msg_command_execute_alert.html:10 @@ -1117,12 +1117,12 @@ msgid "Command" msgstr "コマンド" #: acls/models/command_acl.py:17 assets/models/cmd_filter.py:59 -#: xpack/plugins/cloud/models.py:299 +#: xpack/plugins/cloud/models.py:291 msgid "Regex" msgstr "正規情報" #: acls/models/command_acl.py:26 assets/models/cmd_filter.py:79 -#: settings/models.py:181 settings/serializers/feature.py:19 +#: settings/models.py:182 settings/serializers/feature.py:19 #: xpack/plugins/license/models.py:30 msgid "Content" msgstr "コンテンツ" @@ -1223,7 +1223,7 @@ msgid "None of the reviewers belong to Organization `{}`" msgstr "いずれのレビューアも組織 '{}' に属していません" #: acls/serializers/rules/rules.py:20 -#: xpack/plugins/cloud/serializers/task.py:145 +#: xpack/plugins/cloud/serializers/task.py:137 msgid "IP address invalid: `{}`" msgstr "IPアドレスが無効: '{}'" @@ -1610,7 +1610,7 @@ msgstr "SSHパブリックキー" #: assets/models/_user.py:28 assets/models/automations/base.py:114 #: assets/models/cmd_filter.py:41 assets/models/group.py:19 #: audits/models.py:267 common/db/models.py:34 ops/models/base.py:54 -#: ops/models/job.py:239 users/models/user.py:1041 +#: ops/models/job.py:234 users/models/user.py:1041 msgid "Date created" msgstr "作成された日付" @@ -1713,20 +1713,20 @@ msgstr "アドレス" #: assets/models/asset/common.py:161 assets/models/platform.py:126 #: authentication/backends/passkey/models.py:12 #: authentication/serializers/connect_token_secret.py:118 -#: perms/serializers/user_permission.py:24 xpack/plugins/cloud/models.py:329 +#: perms/serializers/user_permission.py:24 xpack/plugins/cloud/models.py:321 msgid "Platform" msgstr "プラットフォーム" #: assets/models/asset/common.py:163 assets/models/domain.py:22 #: authentication/serializers/connect_token_secret.py:136 -#: perms/serializers/user_permission.py:28 xpack/plugins/cloud/models.py:331 +#: perms/serializers/user_permission.py:28 xpack/plugins/cloud/models.py:323 msgid "Domain" msgstr "ドメイン" #: assets/models/asset/common.py:165 assets/models/automations/base.py:18 #: assets/models/cmd_filter.py:32 assets/models/node.py:553 #: perms/models/asset_permission.py:72 perms/serializers/permission.py:37 -#: tickets/models/ticket/apply_asset.py:14 xpack/plugins/cloud/models.py:330 +#: tickets/models/ticket/apply_asset.py:14 xpack/plugins/cloud/models.py:322 msgid "Node" msgstr "ノード" @@ -1779,7 +1779,7 @@ msgstr "証明書チェックを無視" msgid "Proxy" msgstr "プロキシー" -#: assets/models/automations/base.py:22 ops/models/job.py:235 +#: assets/models/automations/base.py:22 ops/models/job.py:230 #: settings/serializers/auth/sms.py:103 msgid "Parameters" msgstr "パラメータ" @@ -1878,8 +1878,9 @@ msgstr "値" #: assets/serializers/cagegory.py:17 assets/serializers/cagegory.py:23 #: assets/serializers/platform.py:119 #: authentication/serializers/connect_token_secret.py:124 -#: common/serializers/common.py:85 labels/models.py:17 -#: perms/serializers/user_permission.py:27 settings/serializers/msg.py:83 +#: common/serializers/common.py:85 labels/models.py:17 labels/models.py:33 +#: labels/serializers.py:45 perms/serializers/user_permission.py:27 +#: settings/serializers/msg.py:83 msgid "Label" msgstr "ラベル" @@ -2054,8 +2055,8 @@ msgstr "" #: authentication/serializers/connect_token_secret.py:30 #: authentication/serializers/connect_token_secret.py:75 #: perms/models/asset_permission.py:76 perms/serializers/permission.py:42 -#: perms/serializers/user_permission.py:74 xpack/plugins/cloud/models.py:332 -#: xpack/plugins/cloud/serializers/task.py:33 +#: perms/serializers/user_permission.py:74 xpack/plugins/cloud/models.py:324 +#: xpack/plugins/cloud/serializers/task.py:31 msgid "Protocols" msgstr "プロトコル" @@ -2462,7 +2463,7 @@ msgid "Resource Type" msgstr "リソースタイプ" #: audits/models.py:95 audits/models.py:98 audits/models.py:144 -#: audits/serializers.py:85 labels/serializers.py:34 +#: audits/serializers.py:85 labels/serializers.py:46 msgid "Resource" msgstr "リソース" @@ -2537,8 +2538,8 @@ msgid "Offline user session" msgstr "オフラインユーザセッション" #: audits/serializers.py:33 ops/models/adhoc.py:25 ops/models/base.py:16 -#: ops/models/base.py:53 ops/models/job.py:150 ops/models/job.py:238 -#: ops/models/playbook.py:31 terminal/models/session/sharing.py:25 +#: ops/models/base.py:53 ops/models/job.py:145 ops/models/job.py:233 +#: ops/models/playbook.py:30 terminal/models/session/sharing.py:25 msgid "Creator" msgstr "作成者" @@ -3699,7 +3700,7 @@ msgid "Invalid ids for ids, should be a list" msgstr "無効なID、リストでなければなりません" #: common/db/fields.py:585 common/db/fields.py:590 -#: common/serializers/fields.py:136 tickets/serializers/ticket/common.py:58 +#: common/serializers/fields.py:130 tickets/serializers/ticket/common.py:58 #: xpack/plugins/cloud/serializers/account_attrs.py:56 #: xpack/plugins/cloud/serializers/account_attrs.py:79 #: xpack/plugins/cloud/serializers/account_attrs.py:150 @@ -3742,7 +3743,7 @@ msgstr "組織 ID" msgid "The file content overflowed (The maximum length `{}` bytes)" msgstr "ファイルの内容がオーバーフローしました (最大長 '{}' バイト)" -#: common/drf/parsers/base.py:195 +#: common/drf/parsers/base.py:199 msgid "Parse file error: {}" msgstr "解析ファイルエラー: {}" @@ -3750,7 +3751,7 @@ msgstr "解析ファイルエラー: {}" msgid "Invalid excel file" msgstr "無効 excel 書類" -#: common/drf/renders/base.py:206 +#: common/drf/renders/base.py:207 msgid "" "{} - The encryption password has not been set - please go to personal " "information -> file encryption password to set the encryption password" @@ -3799,7 +3800,7 @@ msgstr "サポートされていません Elasticsearch8" msgid "Network error, please contact system administrator" msgstr "ネットワークエラー、システム管理者に連絡してください" -#: common/sdk/im/slack/__init__.py:76 +#: common/sdk/im/slack/__init__.py:79 msgid "Unknown error occur" msgstr "不明なエラーが発生しました" @@ -3872,21 +3873,21 @@ msgstr "{} 秒待ってから送信してください" msgid "Children" msgstr "ノード" -#: common/serializers/fields.py:137 +#: common/serializers/fields.py:131 #, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "無効な pk \"{pk_value}\" - オブジェクトが存在しません" -#: common/serializers/fields.py:138 +#: common/serializers/fields.py:132 #, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "エラータイプ。 予想される pk 値、受信 {data_type}。" -#: common/serializers/fields.py:212 +#: common/serializers/fields.py:206 msgid "Invalid data type, should be list" msgstr "間違ったデータ タイプです。リストにする必要があります" -#: common/serializers/fields.py:227 +#: common/serializers/fields.py:221 msgid "Invalid choice: {}" msgstr "無効なオプション: {}" @@ -3980,19 +3981,25 @@ msgstr "" "いる場合は、nginxリスニングポートにアクセスしていないことを証明してください。" "頑張ってください。
    " -#: labels/models.py:30 +#: labels/models.py:36 msgid "Resource ID" msgstr "リソースID" -#: labels/models.py:35 +#: labels/models.py:41 msgid "Labeled resource" msgstr "" -#: labels/serializers.py:19 +#: labels/serializers.py:22 msgid "Resource count" msgstr "リソース数" -#: labels/serializers.py:31 +#: labels/serializers.py:28 +#, fuzzy +#| msgid "Can't contains: /" +msgid "Cannot contain \":,\"" +msgstr "含まれない:/" + +#: labels/serializers.py:43 msgid "Resource type" msgstr "リソースタイプ" @@ -4020,7 +4027,7 @@ msgstr "システムメッセージ" msgid "Publish the station message" msgstr "投稿サイトニュース" -#: ops/ansible/inventory.py:96 ops/models/job.py:63 +#: ops/ansible/inventory.py:96 ops/models/job.py:62 msgid "No account available" msgstr "利用可能なアカウントがありません" @@ -4124,7 +4131,7 @@ msgstr "VCS" msgid "Adhoc" msgstr "コマンド#コマンド#" -#: ops/const.py:39 ops/models/job.py:147 +#: ops/const.py:39 ops/models/job.py:143 msgid "Playbook" msgstr "Playbook" @@ -4209,11 +4216,11 @@ msgstr "定期的または定期的に設定を行う必要があります" msgid "Pattern" msgstr "パターン" -#: ops/models/adhoc.py:23 ops/models/job.py:142 +#: ops/models/adhoc.py:23 ops/models/job.py:140 msgid "Module" msgstr "モジュール" -#: ops/models/adhoc.py:24 ops/models/celery.py:58 ops/models/job.py:140 +#: ops/models/adhoc.py:24 ops/models/celery.py:58 ops/models/job.py:138 #: terminal/models/component/task.py:14 msgid "Args" msgstr "アルグ" @@ -4228,16 +4235,16 @@ msgstr "アカウント ポリシー" msgid "Last execution" msgstr "最後の実行" -#: ops/models/base.py:22 ops/serializers/job.py:18 +#: ops/models/base.py:22 ops/serializers/job.py:17 msgid "Date last run" msgstr "最終実行日" -#: ops/models/base.py:51 ops/models/job.py:236 -#: xpack/plugins/cloud/models.py:202 +#: ops/models/base.py:51 ops/models/job.py:231 +#: xpack/plugins/cloud/models.py:199 msgid "Result" msgstr "結果" -#: ops/models/base.py:52 ops/models/job.py:237 +#: ops/models/base.py:52 ops/models/job.py:232 msgid "Summary" msgstr "概要" @@ -4270,51 +4277,51 @@ msgstr "発売日" msgid "Celery Task Execution" msgstr "Celery タスク実行" -#: ops/models/job.py:144 +#: ops/models/job.py:141 msgid "Chdir" msgstr "実行ディレクトリ" -#: ops/models/job.py:145 +#: ops/models/job.py:142 msgid "Timeout (Seconds)" msgstr "タイムアウト(秒)" -#: ops/models/job.py:152 +#: ops/models/job.py:147 msgid "Use Parameter Define" msgstr "パラメータ定義を使用する" -#: ops/models/job.py:153 +#: ops/models/job.py:148 msgid "Parameters define" msgstr "パラメータ定義" -#: ops/models/job.py:154 +#: ops/models/job.py:149 msgid "Runas" msgstr "ユーザーとして実行" -#: ops/models/job.py:156 +#: ops/models/job.py:151 msgid "Runas policy" msgstr "ユーザー ポリシー" -#: ops/models/job.py:220 +#: ops/models/job.py:215 msgid "Job" msgstr "ジョブ#ジョブ#" -#: ops/models/job.py:243 +#: ops/models/job.py:238 msgid "Material" msgstr "Material" -#: ops/models/job.py:245 +#: ops/models/job.py:240 msgid "Material Type" msgstr "Material を選択してオプションを設定します。" -#: ops/models/job.py:562 +#: ops/models/job.py:557 msgid "Job Execution" msgstr "ジョブ実行" -#: ops/models/playbook.py:34 +#: ops/models/playbook.py:33 msgid "CreateMethod" msgstr "创建方式" -#: ops/models/playbook.py:35 +#: ops/models/playbook.py:34 msgid "VCS URL" msgstr "VCS URL" @@ -4346,19 +4353,19 @@ msgstr "{max_threshold}%: => {value} を超える使用メモリ" msgid "CPU load more than {max_threshold}: => {value}" msgstr "{max_threshold} を超えるCPUロード: => {value}" -#: ops/serializers/job.py:16 +#: ops/serializers/job.py:15 msgid "Run after save" msgstr "保存後に実行" -#: ops/serializers/job.py:63 +#: ops/serializers/job.py:62 msgid "Job type" msgstr "タスクの種類" -#: ops/serializers/job.py:66 terminal/serializers/session.py:53 +#: ops/serializers/job.py:65 terminal/serializers/session.py:53 msgid "Is finished" msgstr "終了しました" -#: ops/serializers/job.py:67 +#: ops/serializers/job.py:66 msgid "Time cost" msgstr "時を過ごす" @@ -4478,7 +4485,7 @@ msgstr "デフォルト組織" msgid "SYSTEM" msgstr "システム組織" -#: orgs/models.py:83 rbac/models/role.py:36 settings/models.py:182 +#: orgs/models.py:83 rbac/models/role.py:36 settings/models.py:183 #: terminal/models/applet/applet.py:41 msgid "Builtin" msgstr "ビルトイン" @@ -4624,7 +4631,7 @@ msgstr "内部の役割は、破壊することはできません" msgid "The role has been bound to users, can't be destroy" msgstr "ロールはユーザーにバインドされており、破壊することはできません" -#: rbac/api/role.py:87 +#: rbac/api/role.py:100 msgid "Internal role, can't be update" msgstr "内部ロール、更新できません" @@ -4829,7 +4836,7 @@ msgstr "権限ツリーの表示" msgid "Chat AI is not enabled" msgstr "チャットAIがオンになっていない" -#: settings/api/chat.py:78 settings/api/dingtalk.py:31 +#: settings/api/chat.py:79 settings/api/dingtalk.py:31 #: settings/api/feishu.py:36 settings/api/slack.py:34 settings/api/sms.py:160 #: settings/api/vault.py:40 settings/api/wecom.py:37 msgid "Test success" @@ -4887,50 +4894,56 @@ msgid "Can change auth ticket" msgstr "製造オーダ設定" #: settings/models.py:165 +#, fuzzy +#| msgid "Can change vault setting" +msgid "Can change virtual app setting" +msgstr "金庫の設定を変えることができます" + +#: settings/models.py:166 msgid "Can change auth announcement" msgstr "公告の設定" -#: settings/models.py:166 +#: settings/models.py:167 msgid "Can change vault setting" msgstr "金庫の設定を変えることができます" -#: settings/models.py:167 +#: settings/models.py:168 msgid "Can change chat ai setting" msgstr "チャットAI設定を変更できます" -#: settings/models.py:168 +#: settings/models.py:169 msgid "Can change system msg sub setting" msgstr "システムmsgサブ设定を変更できます" -#: settings/models.py:169 +#: settings/models.py:170 msgid "Can change sms setting" msgstr "Smsの設定を変えることができます" -#: settings/models.py:170 +#: settings/models.py:171 msgid "Can change security setting" msgstr "セキュリティ設定を変更できます" -#: settings/models.py:171 +#: settings/models.py:172 msgid "Can change clean setting" msgstr "きれいな設定を変えることができます" -#: settings/models.py:172 +#: settings/models.py:173 msgid "Can change interface setting" msgstr "インターフェイスの設定を変えることができます" -#: settings/models.py:173 +#: settings/models.py:174 msgid "Can change license setting" msgstr "ライセンス設定を変更できます" -#: settings/models.py:174 +#: settings/models.py:175 msgid "Can change terminal setting" msgstr "ターミナルの設定を変えることができます" -#: settings/models.py:175 +#: settings/models.py:176 msgid "Can change other setting" msgstr "他の設定を変えることができます" -#: settings/models.py:185 +#: settings/models.py:186 msgid "Chat prompt" msgstr "チャットのヒント" @@ -5561,6 +5574,20 @@ msgstr "オペレーション センター コマンド ブラックリスト" msgid "Commands that are not allowed execute." msgstr "実行が許可されていないコマンド" +#: settings/serializers/feature.py:126 +#: terminal/models/virtualapp/provider.py:17 +#: terminal/models/virtualapp/virtualapp.py:36 +#: terminal/models/virtualapp/virtualapp.py:97 +#: terminal/serializers/virtualapp.py:32 +msgid "Virtual app" +msgstr "仮想アプリケーション" + +#: settings/serializers/feature.py:129 +#, fuzzy +#| msgid "Virtual app" +msgid "Enable virtual app" +msgstr "仮想アプリを有効にする" + #: settings/serializers/msg.py:24 msgid "SMTP host" msgstr "SMTPホスト" @@ -6213,8 +6240,8 @@ msgstr "期限切れです。" #, python-format msgid "" "\n" -" Your password has expired, please click this link update password.\n" +" Your password has expired, please click this link update password.\n" " " msgstr "" "\n" @@ -6235,34 +6262,34 @@ msgid "" " " msgstr "" "\n" -" クリックしてください リンク パスワードの更新\n" +" クリックしてください リンク パスワードの更新\n" " " #: templates/_message.html:43 #, python-format msgid "" "\n" -" Your information was incomplete. Please click this link to complete your information.\n" +" Your information was incomplete. Please click this link to complete your information.\n" " " msgstr "" "\n" -" あなたの情報が不完全なので、クリックしてください。 リンク 補完\n" +" あなたの情報が不完全なので、クリックしてください。 リンク 補完\n" " " #: templates/_message.html:56 #, python-format msgid "" "\n" -" Your ssh public key not set or expired. Please click this link to update\n" +" Your ssh public key not set or expired. Please click this link to update\n" " " msgstr "" "\n" -" SSHキーが設定されていないか無効になっている場合は、 リンク 更新\n" +" SSHキーが設定されていないか無効になっている場合は、 リンク 更新\n" " " #: templates/_mfa_login_field.html:28 @@ -6826,13 +6853,6 @@ msgstr "検証コードが無効" msgid "You have already joined this session" msgstr "すでにこのセッションに参加しています" -#: terminal/models/virtualapp/provider.py:17 -#: terminal/models/virtualapp/virtualapp.py:36 -#: terminal/models/virtualapp/virtualapp.py:97 -#: terminal/serializers/virtualapp.py:32 -msgid "Virtual app" -msgstr "仮想アプリケーション" - #: terminal/models/virtualapp/virtualapp.py:32 msgid "Providers" msgstr "プロバイダ" @@ -7116,7 +7136,7 @@ msgstr "アクセスキー" msgid "Access key secret" msgstr "アクセスキーシークレット" -#: terminal/serializers/storage.py:68 xpack/plugins/cloud/models.py:253 +#: terminal/serializers/storage.py:68 xpack/plugins/cloud/models.py:250 msgid "Region" msgstr "リージョン" @@ -7482,19 +7502,19 @@ msgstr "チケット基本情報" msgid "Ticket applied info" msgstr "チケット適用情報" -#: tickets/notifications.py:109 +#: tickets/notifications.py:111 msgid "Your has a new ticket, applicant - {}" msgstr "新しいチケットがあります- {}" -#: tickets/notifications.py:113 +#: tickets/notifications.py:115 msgid "{}: New Ticket - {} ({})" msgstr "新しいチケット- {} ({})" -#: tickets/notifications.py:157 +#: tickets/notifications.py:159 msgid "Your ticket has been processed, processor - {}" msgstr "チケットが処理されました。プロセッサー- {}" -#: tickets/notifications.py:161 +#: tickets/notifications.py:163 msgid "Ticket has processed - {} ({})" msgstr "チケットが処理済み- {} ({})" @@ -8287,11 +8307,6 @@ msgstr "パスワードの成功をリセットし、ログインページに戻 msgid "XPACK" msgstr "XPack" -#: xpack/exceptions.py:7 -msgid "" -"The current task is not synchronized with unmatched policy assets, skipping" -msgstr "" - #: xpack/plugins/cloud/api.py:56 msgid "Test connection successful" msgstr "テスト接続成功" @@ -8396,7 +8411,7 @@ msgstr "プライベートIP" msgid "Public IP" msgstr "パブリックIP" -#: xpack/plugins/cloud/const.py:41 xpack/plugins/cloud/models.py:303 +#: xpack/plugins/cloud/const.py:41 xpack/plugins/cloud/models.py:295 msgid "Instance name" msgstr "インスタンス名" @@ -8424,15 +8439,7 @@ msgstr "同期済み" msgid "Released" msgstr "リリース済み" -#: xpack/plugins/cloud/const.py:58 -msgid "And" -msgstr "そして" - -#: xpack/plugins/cloud/const.py:59 -msgid "Or" -msgstr "または" - -#: xpack/plugins/cloud/manager.py:57 +#: xpack/plugins/cloud/manager.py:54 msgid "Account unavailable" msgstr "利用できないアカウント" @@ -8456,7 +8463,7 @@ msgstr "クラウドアカウント" msgid "Test cloud account" msgstr "クラウドアカウントのテスト" -#: xpack/plugins/cloud/models.py:92 xpack/plugins/cloud/serializers/task.py:159 +#: xpack/plugins/cloud/models.py:92 xpack/plugins/cloud/serializers/task.py:151 msgid "Regions" msgstr "リージョン" @@ -8465,134 +8472,122 @@ msgid "Hostname strategy" msgstr "ホスト名戦略" #: xpack/plugins/cloud/models.py:100 -#: xpack/plugins/cloud/serializers/task.py:162 +#: xpack/plugins/cloud/serializers/task.py:154 msgid "IP network segment group" msgstr "IPネットワークセグメントグループ" #: xpack/plugins/cloud/models.py:103 -#: xpack/plugins/cloud/serializers/task.py:167 +#: xpack/plugins/cloud/serializers/task.py:159 msgid "Sync IP type" msgstr "同期IPタイプ" #: xpack/plugins/cloud/models.py:106 -#: xpack/plugins/cloud/serializers/task.py:185 +#: xpack/plugins/cloud/serializers/task.py:177 msgid "Always update" msgstr "常に更新" -#: xpack/plugins/cloud/models.py:108 -msgid "Fully synchronous" -msgstr "完全同期" - -#: xpack/plugins/cloud/models.py:113 +#: xpack/plugins/cloud/models.py:112 msgid "Date last sync" msgstr "最終同期日" -#: xpack/plugins/cloud/models.py:116 xpack/plugins/cloud/models.py:321 -#: xpack/plugins/cloud/models.py:345 +#: xpack/plugins/cloud/models.py:115 xpack/plugins/cloud/models.py:313 +#: xpack/plugins/cloud/models.py:337 msgid "Strategy" msgstr "戦略" -#: xpack/plugins/cloud/models.py:121 xpack/plugins/cloud/models.py:200 +#: xpack/plugins/cloud/models.py:120 xpack/plugins/cloud/models.py:197 msgid "Sync instance task" msgstr "インスタンスの同期タスク" -#: xpack/plugins/cloud/models.py:211 xpack/plugins/cloud/models.py:263 +#: xpack/plugins/cloud/models.py:208 xpack/plugins/cloud/models.py:260 msgid "Date sync" msgstr "日付の同期" -#: xpack/plugins/cloud/models.py:215 +#: xpack/plugins/cloud/models.py:212 msgid "Sync instance snapshot" msgstr "インスタンススナップショットの同期" -#: xpack/plugins/cloud/models.py:219 +#: xpack/plugins/cloud/models.py:216 msgid "Sync instance task execution" msgstr "インスタンスタスクの同期実行" -#: xpack/plugins/cloud/models.py:243 +#: xpack/plugins/cloud/models.py:240 msgid "Sync task" msgstr "同期タスク" -#: xpack/plugins/cloud/models.py:247 +#: xpack/plugins/cloud/models.py:244 msgid "Sync instance task history" msgstr "インスタンスタスク履歴の同期" -#: xpack/plugins/cloud/models.py:250 +#: xpack/plugins/cloud/models.py:247 msgid "Instance" msgstr "インスタンス" -#: xpack/plugins/cloud/models.py:267 +#: xpack/plugins/cloud/models.py:264 msgid "Sync instance detail" msgstr "同期インスタンスの詳細" -#: xpack/plugins/cloud/models.py:279 xpack/plugins/cloud/serializers/task.py:72 -msgid "Rule relation" -msgstr "条件関係" - -#: xpack/plugins/cloud/models.py:288 +#: xpack/plugins/cloud/models.py:281 msgid "Task strategy" msgstr "ミッション戦略です" -#: xpack/plugins/cloud/models.py:292 +#: xpack/plugins/cloud/models.py:285 msgid "Equal" msgstr "等しい" -#: xpack/plugins/cloud/models.py:293 +#: xpack/plugins/cloud/models.py:286 msgid "Not Equal" msgstr "不等于" -#: xpack/plugins/cloud/models.py:294 +#: xpack/plugins/cloud/models.py:287 msgid "In" msgstr "で..." -#: xpack/plugins/cloud/models.py:295 +#: xpack/plugins/cloud/models.py:288 msgid "Contains" msgstr "含む" -#: xpack/plugins/cloud/models.py:296 -msgid "Exclude" -msgstr "除外" - -#: xpack/plugins/cloud/models.py:297 +#: xpack/plugins/cloud/models.py:289 msgid "Startswith" msgstr "始まる..." -#: xpack/plugins/cloud/models.py:298 +#: xpack/plugins/cloud/models.py:290 msgid "Endswith" msgstr "終わる..." -#: xpack/plugins/cloud/models.py:304 +#: xpack/plugins/cloud/models.py:296 msgid "Instance platform" msgstr "インスタンス名" -#: xpack/plugins/cloud/models.py:305 +#: xpack/plugins/cloud/models.py:297 msgid "Instance address" msgstr "インスタンスアドレス" -#: xpack/plugins/cloud/models.py:312 +#: xpack/plugins/cloud/models.py:304 msgid "Rule attr" msgstr "ルール属性" -#: xpack/plugins/cloud/models.py:316 +#: xpack/plugins/cloud/models.py:308 msgid "Rule match" msgstr "ルール一致" -#: xpack/plugins/cloud/models.py:318 +#: xpack/plugins/cloud/models.py:310 msgid "Rule value" msgstr "ルール値" -#: xpack/plugins/cloud/models.py:325 xpack/plugins/cloud/serializers/task.py:75 +#: xpack/plugins/cloud/models.py:317 xpack/plugins/cloud/serializers/task.py:70 msgid "Strategy rule" msgstr "戦略ルール" -#: xpack/plugins/cloud/models.py:340 +#: xpack/plugins/cloud/models.py:332 msgid "Action attr" msgstr "アクション属性" -#: xpack/plugins/cloud/models.py:342 +#: xpack/plugins/cloud/models.py:334 msgid "Action value" msgstr "アクション値" -#: xpack/plugins/cloud/models.py:349 xpack/plugins/cloud/serializers/task.py:78 +#: xpack/plugins/cloud/models.py:341 xpack/plugins/cloud/serializers/task.py:73 msgid "Strategy action" msgstr "戦略アクション" @@ -8880,7 +8875,7 @@ msgstr "テストタイムアウト" msgid "Project" msgstr "project" -#: xpack/plugins/cloud/serializers/task.py:151 +#: xpack/plugins/cloud/serializers/task.py:143 msgid "" "Only instances matching the IP range will be synced.
    If the instance " "contains multiple IP addresses, the first IP address that matches will be " @@ -8894,11 +8889,11 @@ msgstr "" "ドレスをランダムに一致させることを意味します。
    例: " "192.168.1.0/24,10.1.1.1-10.1.1.20。" -#: xpack/plugins/cloud/serializers/task.py:157 +#: xpack/plugins/cloud/serializers/task.py:149 msgid "History count" msgstr "実行回数" -#: xpack/plugins/cloud/serializers/task.py:158 +#: xpack/plugins/cloud/serializers/task.py:150 msgid "Instance count" msgstr "インスタンス数" @@ -8982,6 +8977,21 @@ msgstr "エンタープライズプロフェッショナル版" msgid "Ultimate edition" msgstr "エンタープライズ・フラッグシップ・エディション" +#~ msgid "And" +#~ msgstr "そして" + +#~ msgid "Or" +#~ msgstr "または" + +#~ msgid "Fully synchronous" +#~ msgstr "完全同期" + +#~ msgid "Rule relation" +#~ msgstr "条件関係" + +#~ msgid "Exclude" +#~ msgstr "除外" + #~ msgid "Password can not contains `{{` or `}}`" #~ msgstr "パスワードには `{` または `}` 文字を含めることはできません" diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index 69aad7348..be3a5f31a 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: JumpServer 0.3.3\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-12-18 18:35+0800\n" +"POT-Creation-Date: 2023-12-20 15:46+0800\n" "PO-Revision-Date: 2021-05-20 10:54+0800\n" "Last-Translator: ibuler \n" "Language-Team: JumpServer team\n" @@ -255,7 +255,7 @@ msgstr "用户 %s 查看/导出 了密码" #: terminal/serializers/session.py:26 #: terminal/templates/terminal/_msg_command_warning.html:4 #: terminal/templates/terminal/_msg_session_sharing.html:4 -#: tickets/models/ticket/apply_asset.py:16 xpack/plugins/cloud/models.py:256 +#: tickets/models/ticket/apply_asset.py:16 xpack/plugins/cloud/models.py:253 msgid "Asset" msgstr "资产" @@ -359,7 +359,7 @@ msgstr "账号备份计划" #: accounts/models/automations/backup_account.py:119 #: assets/models/automations/base.py:115 audits/models.py:65 -#: ops/models/base.py:55 ops/models/celery.py:63 ops/models/job.py:240 +#: ops/models/base.py:55 ops/models/celery.py:63 ops/models/job.py:235 #: ops/templates/ops/celery_task_log.html:75 #: perms/models/asset_permission.py:78 terminal/models/applet/host.py:141 #: terminal/models/session/session.py:44 @@ -387,14 +387,14 @@ msgid "Trigger mode" msgstr "触发模式" #: accounts/models/automations/backup_account.py:133 audits/models.py:203 -#: terminal/models/session/sharing.py:125 xpack/plugins/cloud/models.py:208 +#: terminal/models/session/sharing.py:125 xpack/plugins/cloud/models.py:205 msgid "Reason" msgstr "原因" #: accounts/models/automations/backup_account.py:135 #: accounts/serializers/automations/change_secret.py:105 #: accounts/serializers/automations/change_secret.py:128 -#: ops/serializers/job.py:65 terminal/serializers/session.py:49 +#: ops/serializers/job.py:64 terminal/serializers/session.py:49 msgid "Is success" msgstr "是否成功" @@ -467,22 +467,22 @@ msgstr "开始日期" #: accounts/models/automations/change_secret.py:42 #: assets/models/automations/base.py:116 ops/models/base.py:56 -#: ops/models/celery.py:64 ops/models/job.py:241 +#: ops/models/celery.py:64 ops/models/job.py:236 #: terminal/models/applet/host.py:142 msgid "Date finished" msgstr "结束日期" #: accounts/models/automations/change_secret.py:43 #: assets/models/automations/base.py:113 audits/models.py:208 -#: audits/serializers.py:54 ops/models/base.py:49 ops/models/job.py:232 +#: audits/serializers.py:54 ops/models/base.py:49 ops/models/job.py:227 #: terminal/models/applet/applet.py:319 terminal/models/applet/host.py:140 #: terminal/models/component/status.py:30 #: terminal/models/virtualapp/virtualapp.py:99 #: terminal/serializers/applet.py:18 terminal/serializers/applet_host.py:136 #: terminal/serializers/virtualapp.py:35 tickets/models/ticket/general.py:283 #: tickets/serializers/super_ticket.py:13 -#: tickets/serializers/ticket/ticket.py:20 xpack/plugins/cloud/models.py:204 -#: xpack/plugins/cloud/models.py:260 +#: tickets/serializers/ticket/ticket.py:20 xpack/plugins/cloud/models.py:201 +#: xpack/plugins/cloud/models.py:257 msgid "Status" msgstr "状态" @@ -600,10 +600,10 @@ msgstr "密码规则" #: authentication/serializers/connect_token_secret.py:113 #: authentication/serializers/connect_token_secret.py:168 labels/models.py:11 #: ops/mixin.py:21 ops/models/adhoc.py:20 ops/models/celery.py:15 -#: ops/models/celery.py:57 ops/models/job.py:137 ops/models/playbook.py:29 -#: ops/serializers/job.py:19 orgs/models.py:82 +#: ops/models/celery.py:57 ops/models/job.py:136 ops/models/playbook.py:28 +#: ops/serializers/job.py:18 orgs/models.py:82 #: perms/models/asset_permission.py:61 rbac/models/role.py:29 -#: settings/models.py:33 settings/models.py:180 settings/serializers/msg.py:82 +#: settings/models.py:33 settings/models.py:181 settings/serializers/msg.py:82 #: terminal/models/applet/applet.py:33 terminal/models/component/endpoint.py:12 #: terminal/models/component/endpoint.py:95 #: terminal/models/component/storage.py:26 terminal/models/component/task.py:13 @@ -612,8 +612,8 @@ msgstr "密码规则" #: terminal/models/virtualapp/virtualapp.py:19 tickets/api/ticket.py:87 #: users/forms/profile.py:33 users/models/group.py:13 #: users/models/preference.py:11 users/models/user.py:800 -#: xpack/plugins/cloud/models.py:32 xpack/plugins/cloud/models.py:276 -#: xpack/plugins/cloud/serializers/task.py:70 +#: xpack/plugins/cloud/models.py:32 xpack/plugins/cloud/models.py:273 +#: xpack/plugins/cloud/serializers/task.py:68 msgid "Name" msgstr "名称" @@ -643,7 +643,7 @@ msgstr "系统平台" msgid "Push params" msgstr "账号推送参数" -#: accounts/models/template.py:26 xpack/plugins/cloud/models.py:333 +#: accounts/models/template.py:26 xpack/plugins/cloud/models.py:325 msgid "Account template" msgstr "账号模版" @@ -753,7 +753,7 @@ msgstr "类别" #: assets/serializers/asset/common.py:123 assets/serializers/platform.py:120 #: assets/serializers/platform.py:139 audits/serializers.py:53 #: audits/serializers.py:170 -#: authentication/serializers/connect_token_secret.py:126 ops/models/job.py:149 +#: authentication/serializers/connect_token_secret.py:126 ops/models/job.py:144 #: perms/serializers/user_permission.py:26 terminal/models/applet/applet.py:39 #: terminal/models/component/storage.py:57 #: terminal/models/component/storage.py:146 terminal/serializers/applet.py:29 @@ -790,7 +790,7 @@ msgstr "已修改" #: assets/models/automations/base.py:19 #: assets/serializers/automations/base.py:20 #: authentication/api/connection_token.py:404 ops/models/base.py:17 -#: ops/models/job.py:151 ops/serializers/job.py:20 +#: ops/models/job.py:146 ops/serializers/job.py:19 #: terminal/templates/terminal/_msg_command_execute_alert.html:16 msgid "Assets" msgstr "资产" @@ -919,7 +919,7 @@ msgstr "关联平台,可配置推送参数,如果不关联,将使用默认 #: accounts/serializers/account/virtual.py:19 assets/models/_user.py:27 #: assets/models/cmd_filter.py:40 assets/models/cmd_filter.py:88 #: assets/models/group.py:20 common/db/models.py:36 ops/models/adhoc.py:26 -#: ops/models/job.py:157 ops/models/playbook.py:32 rbac/models/role.py:37 +#: ops/models/job.py:152 ops/models/playbook.py:31 rbac/models/role.py:37 #: settings/models.py:38 terminal/models/applet/applet.py:45 #: terminal/models/applet/applet.py:320 terminal/models/applet/host.py:143 #: terminal/models/component/endpoint.py:25 @@ -927,7 +927,7 @@ msgstr "关联平台,可配置推送参数,如果不关联,将使用默认 #: terminal/models/session/session.py:46 #: terminal/models/virtualapp/virtualapp.py:28 tickets/models/comment.py:32 #: tickets/models/ticket/general.py:297 users/models/user.py:836 -#: xpack/plugins/cloud/models.py:39 xpack/plugins/cloud/models.py:110 +#: xpack/plugins/cloud/models.py:39 xpack/plugins/cloud/models.py:109 msgid "Comment" msgstr "备注" @@ -1071,13 +1071,13 @@ msgstr "通知" #: acls/models/base.py:37 assets/models/_user.py:51 #: assets/models/cmd_filter.py:76 terminal/models/component/endpoint.py:98 -#: xpack/plugins/cloud/models.py:282 +#: xpack/plugins/cloud/models.py:275 msgid "Priority" msgstr "优先级" #: acls/models/base.py:38 assets/models/_user.py:51 #: assets/models/cmd_filter.py:76 terminal/models/component/endpoint.py:99 -#: xpack/plugins/cloud/models.py:283 +#: xpack/plugins/cloud/models.py:276 msgid "1-100, the lower the value will be match first" msgstr "优先级可选范围为 1-100 (数值越小越优先)" @@ -1105,7 +1105,7 @@ msgid "Accounts" msgstr "账号管理" #: acls/models/command_acl.py:16 assets/models/cmd_filter.py:60 -#: ops/serializers/job.py:64 terminal/const.py:86 +#: ops/serializers/job.py:63 terminal/const.py:86 #: terminal/models/session/session.py:42 terminal/serializers/command.py:18 #: terminal/templates/terminal/_msg_command_alert.html:12 #: terminal/templates/terminal/_msg_command_execute_alert.html:10 @@ -1114,12 +1114,12 @@ msgid "Command" msgstr "命令" #: acls/models/command_acl.py:17 assets/models/cmd_filter.py:59 -#: xpack/plugins/cloud/models.py:299 +#: xpack/plugins/cloud/models.py:291 msgid "Regex" msgstr "正则表达式" #: acls/models/command_acl.py:26 assets/models/cmd_filter.py:79 -#: settings/models.py:181 settings/serializers/feature.py:19 +#: settings/models.py:182 settings/serializers/feature.py:19 #: xpack/plugins/license/models.py:30 msgid "Content" msgstr "内容" @@ -1219,7 +1219,7 @@ msgid "None of the reviewers belong to Organization `{}`" msgstr "所有复核人都不属于组织 `{}`" #: acls/serializers/rules/rules.py:20 -#: xpack/plugins/cloud/serializers/task.py:145 +#: xpack/plugins/cloud/serializers/task.py:137 msgid "IP address invalid: `{}`" msgstr "IP 地址无效: `{}`" @@ -1603,7 +1603,7 @@ msgstr "SSH公钥" #: assets/models/_user.py:28 assets/models/automations/base.py:114 #: assets/models/cmd_filter.py:41 assets/models/group.py:19 #: audits/models.py:267 common/db/models.py:34 ops/models/base.py:54 -#: ops/models/job.py:239 users/models/user.py:1041 +#: ops/models/job.py:234 users/models/user.py:1041 msgid "Date created" msgstr "创建日期" @@ -1706,20 +1706,20 @@ msgstr "地址" #: assets/models/asset/common.py:161 assets/models/platform.py:126 #: authentication/backends/passkey/models.py:12 #: authentication/serializers/connect_token_secret.py:118 -#: perms/serializers/user_permission.py:24 xpack/plugins/cloud/models.py:329 +#: perms/serializers/user_permission.py:24 xpack/plugins/cloud/models.py:321 msgid "Platform" msgstr "系统平台" #: assets/models/asset/common.py:163 assets/models/domain.py:22 #: authentication/serializers/connect_token_secret.py:136 -#: perms/serializers/user_permission.py:28 xpack/plugins/cloud/models.py:331 +#: perms/serializers/user_permission.py:28 xpack/plugins/cloud/models.py:323 msgid "Domain" msgstr "网域" #: assets/models/asset/common.py:165 assets/models/automations/base.py:18 #: assets/models/cmd_filter.py:32 assets/models/node.py:553 #: perms/models/asset_permission.py:72 perms/serializers/permission.py:37 -#: tickets/models/ticket/apply_asset.py:14 xpack/plugins/cloud/models.py:330 +#: tickets/models/ticket/apply_asset.py:14 xpack/plugins/cloud/models.py:322 msgid "Node" msgstr "节点" @@ -1772,7 +1772,7 @@ msgstr "忽略证书校验" msgid "Proxy" msgstr "代理" -#: assets/models/automations/base.py:22 ops/models/job.py:235 +#: assets/models/automations/base.py:22 ops/models/job.py:230 #: settings/serializers/auth/sms.py:103 msgid "Parameters" msgstr "参数" @@ -1871,8 +1871,9 @@ msgstr "值" #: assets/serializers/cagegory.py:17 assets/serializers/cagegory.py:23 #: assets/serializers/platform.py:119 #: authentication/serializers/connect_token_secret.py:124 -#: common/serializers/common.py:85 labels/models.py:17 -#: perms/serializers/user_permission.py:27 settings/serializers/msg.py:83 +#: common/serializers/common.py:85 labels/models.py:17 labels/models.py:33 +#: labels/serializers.py:45 perms/serializers/user_permission.py:27 +#: settings/serializers/msg.py:83 msgid "Label" msgstr "标签" @@ -2045,8 +2046,8 @@ msgstr "资产中批量更新平台,不符合平台类型跳过的资产" #: authentication/serializers/connect_token_secret.py:30 #: authentication/serializers/connect_token_secret.py:75 #: perms/models/asset_permission.py:76 perms/serializers/permission.py:42 -#: perms/serializers/user_permission.py:74 xpack/plugins/cloud/models.py:332 -#: xpack/plugins/cloud/serializers/task.py:33 +#: perms/serializers/user_permission.py:74 xpack/plugins/cloud/models.py:324 +#: xpack/plugins/cloud/serializers/task.py:31 msgid "Protocols" msgstr "协议组" @@ -2446,7 +2447,7 @@ msgid "Resource Type" msgstr "资源类型" #: audits/models.py:95 audits/models.py:98 audits/models.py:144 -#: audits/serializers.py:85 labels/serializers.py:34 +#: audits/serializers.py:85 labels/serializers.py:46 msgid "Resource" msgstr "资源" @@ -2521,8 +2522,8 @@ msgid "Offline user session" msgstr "下线用户会话" #: audits/serializers.py:33 ops/models/adhoc.py:25 ops/models/base.py:16 -#: ops/models/base.py:53 ops/models/job.py:150 ops/models/job.py:238 -#: ops/models/playbook.py:31 terminal/models/session/sharing.py:25 +#: ops/models/base.py:53 ops/models/job.py:145 ops/models/job.py:233 +#: ops/models/playbook.py:30 terminal/models/session/sharing.py:25 msgid "Creator" msgstr "创建者" @@ -3656,7 +3657,7 @@ msgid "Invalid ids for ids, should be a list" msgstr "无效的ID,应为列表" #: common/db/fields.py:585 common/db/fields.py:590 -#: common/serializers/fields.py:136 tickets/serializers/ticket/common.py:58 +#: common/serializers/fields.py:130 tickets/serializers/ticket/common.py:58 #: xpack/plugins/cloud/serializers/account_attrs.py:56 #: xpack/plugins/cloud/serializers/account_attrs.py:79 #: xpack/plugins/cloud/serializers/account_attrs.py:150 @@ -3699,7 +3700,7 @@ msgstr "组织 ID" msgid "The file content overflowed (The maximum length `{}` bytes)" msgstr "文件内容太大 (最大长度 `{}` 字节)" -#: common/drf/parsers/base.py:195 +#: common/drf/parsers/base.py:199 msgid "Parse file error: {}" msgstr "解析文件错误: {}" @@ -3707,7 +3708,7 @@ msgstr "解析文件错误: {}" msgid "Invalid excel file" msgstr "无效的 excel 文件" -#: common/drf/renders/base.py:206 +#: common/drf/renders/base.py:207 msgid "" "{} - The encryption password has not been set - please go to personal " "information -> file encryption password to set the encryption password" @@ -3754,7 +3755,7 @@ msgstr "不支持 Elasticsearch8" msgid "Network error, please contact system administrator" msgstr "网络错误,请联系系统管理员" -#: common/sdk/im/slack/__init__.py:76 +#: common/sdk/im/slack/__init__.py:79 msgid "Unknown error occur" msgstr "发生未知错误" @@ -3827,21 +3828,21 @@ msgstr "请在 {} 秒后发送" msgid "Children" msgstr "节点" -#: common/serializers/fields.py:137 +#: common/serializers/fields.py:131 #, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "错误的 pk \"{pk_value}\" - 对象不存在" -#: common/serializers/fields.py:138 +#: common/serializers/fields.py:132 #, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "错误类型。期望 pk 值,收到 {data_type}。" -#: common/serializers/fields.py:212 +#: common/serializers/fields.py:206 msgid "Invalid data type, should be list" msgstr "错误的数据类型,应该是列表" -#: common/serializers/fields.py:227 +#: common/serializers/fields.py:221 msgid "Invalid choice: {}" msgstr "无效选项: {}" @@ -3932,19 +3933,25 @@ msgstr "" "div>
    如果你看到了这个页面,证明你访问的不是nginx监听的端口,祝你好运" -#: labels/models.py:30 +#: labels/models.py:36 msgid "Resource ID" msgstr "资源 ID" -#: labels/models.py:35 +#: labels/models.py:41 msgid "Labeled resource" msgstr "关联的资源" -#: labels/serializers.py:19 +#: labels/serializers.py:22 msgid "Resource count" msgstr "资源数量" -#: labels/serializers.py:31 +#: labels/serializers.py:28 +#, fuzzy +#| msgid "Can't contains: /" +msgid "Cannot contain \":,\"" +msgstr "不能包含: /" + +#: labels/serializers.py:43 msgid "Resource type" msgstr "资源类型" @@ -3972,7 +3979,7 @@ msgstr "系统信息" msgid "Publish the station message" msgstr "发布站内消息" -#: ops/ansible/inventory.py:96 ops/models/job.py:63 +#: ops/ansible/inventory.py:96 ops/models/job.py:62 msgid "No account available" msgstr "无可用账号" @@ -4074,7 +4081,7 @@ msgstr "VCS" msgid "Adhoc" msgstr "命令" -#: ops/const.py:39 ops/models/job.py:147 +#: ops/const.py:39 ops/models/job.py:143 msgid "Playbook" msgstr "Playbook" @@ -4159,11 +4166,11 @@ msgstr "需要周期或定期设置" msgid "Pattern" msgstr "模式" -#: ops/models/adhoc.py:23 ops/models/job.py:142 +#: ops/models/adhoc.py:23 ops/models/job.py:140 msgid "Module" msgstr "模块" -#: ops/models/adhoc.py:24 ops/models/celery.py:58 ops/models/job.py:140 +#: ops/models/adhoc.py:24 ops/models/celery.py:58 ops/models/job.py:138 #: terminal/models/component/task.py:14 msgid "Args" msgstr "参数" @@ -4178,16 +4185,16 @@ msgstr "账号策略" msgid "Last execution" msgstr "最后执行" -#: ops/models/base.py:22 ops/serializers/job.py:18 +#: ops/models/base.py:22 ops/serializers/job.py:17 msgid "Date last run" msgstr "最后运行日期" -#: ops/models/base.py:51 ops/models/job.py:236 -#: xpack/plugins/cloud/models.py:202 +#: ops/models/base.py:51 ops/models/job.py:231 +#: xpack/plugins/cloud/models.py:199 msgid "Result" msgstr "结果" -#: ops/models/base.py:52 ops/models/job.py:237 +#: ops/models/base.py:52 ops/models/job.py:232 msgid "Summary" msgstr "汇总" @@ -4220,51 +4227,51 @@ msgstr "发布日期" msgid "Celery Task Execution" msgstr "Celery 任务执行" -#: ops/models/job.py:144 +#: ops/models/job.py:141 msgid "Chdir" msgstr "运行目录" -#: ops/models/job.py:145 +#: ops/models/job.py:142 msgid "Timeout (Seconds)" msgstr "超时时间 (秒)" -#: ops/models/job.py:152 +#: ops/models/job.py:147 msgid "Use Parameter Define" msgstr "使用参数定义" -#: ops/models/job.py:153 +#: ops/models/job.py:148 msgid "Parameters define" msgstr "参数定义" -#: ops/models/job.py:154 +#: ops/models/job.py:149 msgid "Runas" msgstr "运行用户" -#: ops/models/job.py:156 +#: ops/models/job.py:151 msgid "Runas policy" msgstr "用户策略" -#: ops/models/job.py:220 +#: ops/models/job.py:215 msgid "Job" msgstr "作业" -#: ops/models/job.py:243 +#: ops/models/job.py:238 msgid "Material" msgstr "Material" -#: ops/models/job.py:245 +#: ops/models/job.py:240 msgid "Material Type" msgstr "Material 类型" -#: ops/models/job.py:562 +#: ops/models/job.py:557 msgid "Job Execution" msgstr "作业执行" -#: ops/models/playbook.py:34 +#: ops/models/playbook.py:33 msgid "CreateMethod" msgstr "创建方式" -#: ops/models/playbook.py:35 +#: ops/models/playbook.py:34 msgid "VCS URL" msgstr "VCS URL" @@ -4296,19 +4303,19 @@ msgstr "内存使用率超过 {max_threshold}%: => {value}" msgid "CPU load more than {max_threshold}: => {value}" msgstr "CPU 使用率超过 {max_threshold}: => {value}" -#: ops/serializers/job.py:16 +#: ops/serializers/job.py:15 msgid "Run after save" msgstr "保存后执行" -#: ops/serializers/job.py:63 +#: ops/serializers/job.py:62 msgid "Job type" msgstr "任务类型" -#: ops/serializers/job.py:66 terminal/serializers/session.py:53 +#: ops/serializers/job.py:65 terminal/serializers/session.py:53 msgid "Is finished" msgstr "是否完成" -#: ops/serializers/job.py:67 +#: ops/serializers/job.py:66 msgid "Time cost" msgstr "花费时间" @@ -4427,7 +4434,7 @@ msgstr "默认组织" msgid "SYSTEM" msgstr "系统组织" -#: orgs/models.py:83 rbac/models/role.py:36 settings/models.py:182 +#: orgs/models.py:83 rbac/models/role.py:36 settings/models.py:183 #: terminal/models/applet/applet.py:41 msgid "Builtin" msgstr "内置的" @@ -4573,7 +4580,7 @@ msgstr "内部角色,不能删除" msgid "The role has been bound to users, can't be destroy" msgstr "角色已绑定用户,不能删除" -#: rbac/api/role.py:87 +#: rbac/api/role.py:100 msgid "Internal role, can't be update" msgstr "内部角色,不能更新" @@ -4777,7 +4784,7 @@ msgstr "查看授权树" msgid "Chat AI is not enabled" msgstr "聊天 AI 没有开启" -#: settings/api/chat.py:78 settings/api/dingtalk.py:31 +#: settings/api/chat.py:79 settings/api/dingtalk.py:31 #: settings/api/feishu.py:36 settings/api/slack.py:34 settings/api/sms.py:160 #: settings/api/vault.py:40 settings/api/wecom.py:37 msgid "Test success" @@ -4833,50 +4840,56 @@ msgid "Can change auth ticket" msgstr "工单设置" #: settings/models.py:165 +#, fuzzy +#| msgid "Can change vault setting" +msgid "Can change virtual app setting" +msgstr "可以更改 vault 设置" + +#: settings/models.py:166 msgid "Can change auth announcement" msgstr "公告设置" -#: settings/models.py:166 +#: settings/models.py:167 msgid "Can change vault setting" msgstr "可以更改 vault 设置" -#: settings/models.py:167 +#: settings/models.py:168 msgid "Can change chat ai setting" msgstr "可以修改聊天 AI 设置" -#: settings/models.py:168 +#: settings/models.py:169 msgid "Can change system msg sub setting" msgstr "消息订阅设置" -#: settings/models.py:169 +#: settings/models.py:170 msgid "Can change sms setting" msgstr "短信设置" -#: settings/models.py:170 +#: settings/models.py:171 msgid "Can change security setting" msgstr "安全设置" -#: settings/models.py:171 +#: settings/models.py:172 msgid "Can change clean setting" msgstr "定期清理" -#: settings/models.py:172 +#: settings/models.py:173 msgid "Can change interface setting" msgstr "界面设置" -#: settings/models.py:173 +#: settings/models.py:174 msgid "Can change license setting" msgstr "许可证设置" -#: settings/models.py:174 +#: settings/models.py:175 msgid "Can change terminal setting" msgstr "终端设置" -#: settings/models.py:175 +#: settings/models.py:176 msgid "Can change other setting" msgstr "其它设置" -#: settings/models.py:185 +#: settings/models.py:186 msgid "Chat prompt" msgstr "聊天提示" @@ -5503,6 +5516,20 @@ msgstr "作业中心命令黑名单" msgid "Commands that are not allowed execute." msgstr "不允许执行的命令" +#: settings/serializers/feature.py:126 +#: terminal/models/virtualapp/provider.py:17 +#: terminal/models/virtualapp/virtualapp.py:36 +#: terminal/models/virtualapp/virtualapp.py:97 +#: terminal/serializers/virtualapp.py:32 +msgid "Virtual app" +msgstr "虚拟应用" + +#: settings/serializers/feature.py:129 +#, fuzzy +#| msgid "Virtual app" +msgid "Enable virtual app" +msgstr "启用虚拟应用" + #: settings/serializers/msg.py:24 msgid "SMTP host" msgstr "SMTP 主机" @@ -6126,13 +6153,13 @@ msgstr "过期。" #, python-format msgid "" "\n" -" Your password has expired, please click this link update password.\n" +" Your password has expired, please click this link update password.\n" " " msgstr "" "\n" -" 您的密码已经过期,请点击 链接 更新密码\n" +" 您的密码已经过期,请点击 链接 更新密码\n" " " #: templates/_message.html:30 @@ -6156,8 +6183,8 @@ msgstr "" #, python-format msgid "" "\n" -" Your information was incomplete. Please click this link to complete your information.\n" +" Your information was incomplete. Please click this link to complete your information.\n" " " msgstr "" "\n" @@ -6169,13 +6196,13 @@ msgstr "" #, python-format msgid "" "\n" -" Your ssh public key not set or expired. Please click this link to update\n" +" Your ssh public key not set or expired. Please click this link to update\n" " " msgstr "" "\n" -" 您的SSH密钥没有设置或已失效,请点击 链接 更新\n" +" 您的SSH密钥没有设置或已失效,请点击 链接 更新\n" " " #: templates/_mfa_login_field.html:28 @@ -6734,13 +6761,6 @@ msgstr "验证码不正确" msgid "You have already joined this session" msgstr "您已经加入过此会话" -#: terminal/models/virtualapp/provider.py:17 -#: terminal/models/virtualapp/virtualapp.py:36 -#: terminal/models/virtualapp/virtualapp.py:97 -#: terminal/serializers/virtualapp.py:32 -msgid "Virtual app" -msgstr "虚拟应用" - #: terminal/models/virtualapp/virtualapp.py:32 msgid "Providers" msgstr "提供商" @@ -7017,7 +7037,7 @@ msgstr "Access key ID(AK)" msgid "Access key secret" msgstr "Access key secret(SK)" -#: terminal/serializers/storage.py:68 xpack/plugins/cloud/models.py:253 +#: terminal/serializers/storage.py:68 xpack/plugins/cloud/models.py:250 msgid "Region" msgstr "地域" @@ -7379,19 +7399,19 @@ msgstr "工单基本信息" msgid "Ticket applied info" msgstr "工单申请信息" -#: tickets/notifications.py:109 +#: tickets/notifications.py:111 msgid "Your has a new ticket, applicant - {}" msgstr "你有一个新的工单, 申请人 - {}" -#: tickets/notifications.py:113 +#: tickets/notifications.py:115 msgid "{}: New Ticket - {} ({})" msgstr "新工单 - {} ({})" -#: tickets/notifications.py:157 +#: tickets/notifications.py:159 msgid "Your ticket has been processed, processor - {}" msgstr "你的工单已被处理, 处理人 - {}" -#: tickets/notifications.py:161 +#: tickets/notifications.py:163 msgid "Ticket has processed - {} ({})" msgstr "你的工单已被处理, 处理人 - {} ({})" @@ -8165,11 +8185,6 @@ msgstr "重置密码成功,返回到登录页面" msgid "XPACK" msgstr "XPack" -#: xpack/exceptions.py:7 -msgid "" -"The current task is not synchronized with unmatched policy assets, skipping" -msgstr "" - #: xpack/plugins/cloud/api.py:56 msgid "Test connection successful" msgstr "测试成功" @@ -8274,7 +8289,7 @@ msgstr "私有IP" msgid "Public IP" msgstr "公网IP" -#: xpack/plugins/cloud/const.py:41 xpack/plugins/cloud/models.py:303 +#: xpack/plugins/cloud/const.py:41 xpack/plugins/cloud/models.py:295 msgid "Instance name" msgstr "实例名称" @@ -8302,15 +8317,7 @@ msgstr "已同步" msgid "Released" msgstr "已释放" -#: xpack/plugins/cloud/const.py:58 -msgid "And" -msgstr "与" - -#: xpack/plugins/cloud/const.py:59 -msgid "Or" -msgstr "或" - -#: xpack/plugins/cloud/manager.py:57 +#: xpack/plugins/cloud/manager.py:54 msgid "Account unavailable" msgstr "账号无效" @@ -8334,7 +8341,7 @@ msgstr "云账号" msgid "Test cloud account" msgstr "测试云账号" -#: xpack/plugins/cloud/models.py:92 xpack/plugins/cloud/serializers/task.py:159 +#: xpack/plugins/cloud/models.py:92 xpack/plugins/cloud/serializers/task.py:151 msgid "Regions" msgstr "地域" @@ -8343,134 +8350,122 @@ msgid "Hostname strategy" msgstr "主机名策略" #: xpack/plugins/cloud/models.py:100 -#: xpack/plugins/cloud/serializers/task.py:162 +#: xpack/plugins/cloud/serializers/task.py:154 msgid "IP network segment group" msgstr "IP网段组" #: xpack/plugins/cloud/models.py:103 -#: xpack/plugins/cloud/serializers/task.py:167 +#: xpack/plugins/cloud/serializers/task.py:159 msgid "Sync IP type" msgstr "同步IP类型" #: xpack/plugins/cloud/models.py:106 -#: xpack/plugins/cloud/serializers/task.py:185 +#: xpack/plugins/cloud/serializers/task.py:177 msgid "Always update" msgstr "总是更新" -#: xpack/plugins/cloud/models.py:108 -msgid "Fully synchronous" -msgstr "完全同步" - -#: xpack/plugins/cloud/models.py:113 +#: xpack/plugins/cloud/models.py:112 msgid "Date last sync" msgstr "最后同步日期" -#: xpack/plugins/cloud/models.py:116 xpack/plugins/cloud/models.py:321 -#: xpack/plugins/cloud/models.py:345 +#: xpack/plugins/cloud/models.py:115 xpack/plugins/cloud/models.py:313 +#: xpack/plugins/cloud/models.py:337 msgid "Strategy" msgstr "策略" -#: xpack/plugins/cloud/models.py:121 xpack/plugins/cloud/models.py:200 +#: xpack/plugins/cloud/models.py:120 xpack/plugins/cloud/models.py:197 msgid "Sync instance task" msgstr "同步实例任务" -#: xpack/plugins/cloud/models.py:211 xpack/plugins/cloud/models.py:263 +#: xpack/plugins/cloud/models.py:208 xpack/plugins/cloud/models.py:260 msgid "Date sync" msgstr "同步日期" -#: xpack/plugins/cloud/models.py:215 +#: xpack/plugins/cloud/models.py:212 msgid "Sync instance snapshot" msgstr "同步实例快照" -#: xpack/plugins/cloud/models.py:219 +#: xpack/plugins/cloud/models.py:216 msgid "Sync instance task execution" msgstr "同步实例任务执行" -#: xpack/plugins/cloud/models.py:243 +#: xpack/plugins/cloud/models.py:240 msgid "Sync task" msgstr "同步任务" -#: xpack/plugins/cloud/models.py:247 +#: xpack/plugins/cloud/models.py:244 msgid "Sync instance task history" msgstr "同步实例任务历史" -#: xpack/plugins/cloud/models.py:250 +#: xpack/plugins/cloud/models.py:247 msgid "Instance" msgstr "实例" -#: xpack/plugins/cloud/models.py:267 +#: xpack/plugins/cloud/models.py:264 msgid "Sync instance detail" msgstr "同步实例详情" -#: xpack/plugins/cloud/models.py:279 xpack/plugins/cloud/serializers/task.py:72 -msgid "Rule relation" -msgstr "条件关系" - -#: xpack/plugins/cloud/models.py:288 +#: xpack/plugins/cloud/models.py:281 msgid "Task strategy" msgstr "任务策略" -#: xpack/plugins/cloud/models.py:292 +#: xpack/plugins/cloud/models.py:285 msgid "Equal" msgstr "等于" -#: xpack/plugins/cloud/models.py:293 +#: xpack/plugins/cloud/models.py:286 msgid "Not Equal" msgstr "不等于" -#: xpack/plugins/cloud/models.py:294 +#: xpack/plugins/cloud/models.py:287 msgid "In" msgstr "在...中" -#: xpack/plugins/cloud/models.py:295 +#: xpack/plugins/cloud/models.py:288 msgid "Contains" msgstr "包含" -#: xpack/plugins/cloud/models.py:296 -msgid "Exclude" -msgstr "排除" - -#: xpack/plugins/cloud/models.py:297 +#: xpack/plugins/cloud/models.py:289 msgid "Startswith" msgstr "以...开头" -#: xpack/plugins/cloud/models.py:298 +#: xpack/plugins/cloud/models.py:290 msgid "Endswith" msgstr "以...结尾" -#: xpack/plugins/cloud/models.py:304 +#: xpack/plugins/cloud/models.py:296 msgid "Instance platform" msgstr "实例平台" -#: xpack/plugins/cloud/models.py:305 +#: xpack/plugins/cloud/models.py:297 msgid "Instance address" msgstr "实例地址" -#: xpack/plugins/cloud/models.py:312 +#: xpack/plugins/cloud/models.py:304 msgid "Rule attr" msgstr "规则属性" -#: xpack/plugins/cloud/models.py:316 +#: xpack/plugins/cloud/models.py:308 msgid "Rule match" msgstr "规则匹配" -#: xpack/plugins/cloud/models.py:318 +#: xpack/plugins/cloud/models.py:310 msgid "Rule value" msgstr "规则值" -#: xpack/plugins/cloud/models.py:325 xpack/plugins/cloud/serializers/task.py:75 +#: xpack/plugins/cloud/models.py:317 xpack/plugins/cloud/serializers/task.py:70 msgid "Strategy rule" msgstr "条件" -#: xpack/plugins/cloud/models.py:340 +#: xpack/plugins/cloud/models.py:332 msgid "Action attr" msgstr "动作属性" -#: xpack/plugins/cloud/models.py:342 +#: xpack/plugins/cloud/models.py:334 msgid "Action value" msgstr "动作值" -#: xpack/plugins/cloud/models.py:349 xpack/plugins/cloud/serializers/task.py:78 +#: xpack/plugins/cloud/models.py:341 xpack/plugins/cloud/serializers/task.py:73 msgid "Strategy action" msgstr "动作" @@ -8757,7 +8752,7 @@ msgstr "测试超时时间" msgid "Project" msgstr "project" -#: xpack/plugins/cloud/serializers/task.py:151 +#: xpack/plugins/cloud/serializers/task.py:143 msgid "" "Only instances matching the IP range will be synced.
    If the instance " "contains multiple IP addresses, the first IP address that matches will be " @@ -8769,11 +8764,11 @@ msgstr "" "到的 IP 地址将被用作创建的资产的 IP。
    默认值 * 表示同步所有实例和随机匹配 " "IP 地址。
    例如: 192.168.1.0/24,10.1.1.1-10.1.1.20。" -#: xpack/plugins/cloud/serializers/task.py:157 +#: xpack/plugins/cloud/serializers/task.py:149 msgid "History count" msgstr "执行次数" -#: xpack/plugins/cloud/serializers/task.py:158 +#: xpack/plugins/cloud/serializers/task.py:150 msgid "Instance count" msgstr "实例个数" @@ -8857,6 +8852,21 @@ msgstr "企业专业版" msgid "Ultimate edition" msgstr "企业旗舰版" +#~ msgid "And" +#~ msgstr "与" + +#~ msgid "Or" +#~ msgstr "或" + +#~ msgid "Fully synchronous" +#~ msgstr "完全同步" + +#~ msgid "Rule relation" +#~ msgstr "条件关系" + +#~ msgid "Exclude" +#~ msgstr "排除" + #~ msgid "Password can not contains `{{` or `}}`" #~ msgstr "密码不能包含 `{{` 或 `}}` 字符" diff --git a/apps/settings/api/settings.py b/apps/settings/api/settings.py index af71cad7e..c03e99b6c 100644 --- a/apps/settings/api/settings.py +++ b/apps/settings/api/settings.py @@ -61,6 +61,7 @@ class SettingsApi(generics.RetrieveUpdateAPIView): 'announcement': serializers.AnnouncementSettingSerializer, 'ticket': serializers.TicketSettingSerializer, 'ops': serializers.OpsSettingSerializer, + 'virtualapp': serializers.VirtualAppSerializer, } rbac_category_permissions = { @@ -68,6 +69,7 @@ class SettingsApi(generics.RetrieveUpdateAPIView): 'terminal': 'settings.change_terminal', 'ops': 'settings.change_ops', 'ticket': 'settings.change_ticket', + 'virtualapp': 'settings.change_virtualapp', 'announcement': 'settings.change_announcement', 'security': 'settings.change_security', 'security_basic': 'settings.change_security', diff --git a/apps/settings/migrations/0012_alter_setting_options.py b/apps/settings/migrations/0012_alter_setting_options.py new file mode 100644 index 000000000..f35da135f --- /dev/null +++ b/apps/settings/migrations/0012_alter_setting_options.py @@ -0,0 +1,17 @@ +# Generated by Django 4.1.10 on 2023-12-20 07:51 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('settings', '0011_chatprompt'), + ] + + operations = [ + migrations.AlterModelOptions( + name='setting', + options={'permissions': [('change_email', 'Can change email setting'), ('change_auth', 'Can change auth setting'), ('change_ops', 'Can change auth ops'), ('change_ticket', 'Can change auth ticket'), ('change_virtualapp', 'Can change virtual app setting'), ('change_announcement', 'Can change auth announcement'), ('change_vault', 'Can change vault setting'), ('change_chatai', 'Can change chat ai setting'), ('change_systemmsgsubscription', 'Can change system msg sub setting'), ('change_sms', 'Can change sms setting'), ('change_security', 'Can change security setting'), ('change_clean', 'Can change clean setting'), ('change_interface', 'Can change interface setting'), ('change_license', 'Can change license setting'), ('change_terminal', 'Can change terminal setting'), ('change_other', 'Can change other setting')], 'verbose_name': 'System setting'}, + ), + ] diff --git a/apps/settings/models.py b/apps/settings/models.py index 1da643b9e..d6cda11df 100644 --- a/apps/settings/models.py +++ b/apps/settings/models.py @@ -162,6 +162,7 @@ class Setting(models.Model): ('change_auth', _('Can change auth setting')), ('change_ops', _('Can change auth ops')), ('change_ticket', _('Can change auth ticket')), + ('change_virtualapp', _('Can change virtual app setting')), ('change_announcement', _('Can change auth announcement')), ('change_vault', _('Can change vault setting')), ('change_chatai', _('Can change chat ai setting')), diff --git a/apps/settings/serializers/feature.py b/apps/settings/serializers/feature.py index 1235b29b7..8dc0de959 100644 --- a/apps/settings/serializers/feature.py +++ b/apps/settings/serializers/feature.py @@ -9,7 +9,7 @@ from common.serializers.fields import EncryptedField __all__ = [ 'AnnouncementSettingSerializer', 'OpsSettingSerializer', 'VaultSettingSerializer', 'TicketSettingSerializer', - 'ChatAISettingSerializer' + 'ChatAISettingSerializer', 'VirtualAppSerializer', ] @@ -120,3 +120,11 @@ class OpsSettingSerializer(serializers.Serializer): label=_('Operation center command blacklist'), help_text=_("Commands that are not allowed execute.") ) + + +class VirtualAppSerializer(serializers.Serializer): + PREFIX_TITLE = _('Virtual app') + + VIRTUAL_APP_ENABLED = serializers.BooleanField( + required=False, label=_('Enable virtual app'), + ) From c66b1db784955e11760efc7fde3744c4f4259d01 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Wed, 20 Dec 2023 16:02:13 +0800 Subject: [PATCH 098/111] =?UTF-8?q?fix:=20=E8=87=AA=E5=8A=A8=E5=8C=96?= =?UTF-8?q?=E4=BB=BB=E5=8A=A1=E7=BD=91=E5=85=B3=E8=BF=9E=E6=8E=A5=E6=95=B0?= =?UTF-8?q?=20=E8=87=AA=E5=AE=9A=E4=B9=89ansible=20rdp=20=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E5=8F=AF=E8=BF=9E=E6=8E=A5=E6=80=A7=E7=AB=AF=E5=8F=A3?= =?UTF-8?q?=E9=94=99=E8=AF=AF=20(#12373)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- .../change_secret/custom/ssh/main.yml | 7 +++-- .../change_secret/custom/ssh/manifest.yml | 1 + .../verify_account/custom/rdp/main.yml | 2 +- .../verify_account/custom/rdp/manifest.yml | 1 + .../verify_account/custom/ssh/main.yml | 2 +- .../verify_account/custom/ssh/manifest.yml | 1 + apps/assets/automations/base/manager.py | 26 ++++++++++++------- .../automations/ping/custom/rdp/main.yml | 2 +- .../automations/ping/custom/rdp/manifest.yml | 2 ++ .../automations/ping/custom/ssh/main.yml | 2 +- .../automations/ping/custom/ssh/manifest.yml | 2 ++ apps/ops/ansible/inventory.py | 14 ++++++---- 12 files changed, 39 insertions(+), 23 deletions(-) diff --git a/apps/accounts/automations/change_secret/custom/ssh/main.yml b/apps/accounts/automations/change_secret/custom/ssh/main.yml index c4381b730..8ff38475f 100644 --- a/apps/accounts/automations/change_secret/custom/ssh/main.yml +++ b/apps/accounts/automations/change_secret/custom/ssh/main.yml @@ -1,7 +1,6 @@ - hosts: custom gather_facts: no vars: - asset_port: "{{ jms_asset.protocols | selectattr('name', 'equalto', 'ssh') | map(attribute='port') | first }}" ansible_connection: local ansible_become: false @@ -9,7 +8,7 @@ - name: Test privileged account (paramiko) ssh_ping: login_host: "{{ jms_asset.address }}" - login_port: "{{ asset_port }}" + login_port: "{{ jms_asset.port }}" login_user: "{{ jms_account.username }}" login_password: "{{ jms_account.secret }}" login_secret_type: "{{ jms_account.secret_type }}" @@ -27,7 +26,7 @@ login_user: "{{ jms_account.username }}" login_password: "{{ jms_account.secret }}" login_host: "{{ jms_asset.address }}" - login_port: "{{ asset_port }}" + login_port: "{{ jms_asset.port }}" login_secret_type: "{{ jms_account.secret_type }}" login_private_key_path: "{{ jms_account.private_key_path }}" become: "{{ custom_become | default(False) }}" @@ -49,7 +48,7 @@ login_user: "{{ account.username }}" login_password: "{{ account.secret }}" login_host: "{{ jms_asset.address }}" - login_port: "{{ asset_port }}" + login_port: "{{ jms_asset.port }}" become: "{{ account.become.ansible_become | default(False) }}" become_method: su become_user: "{{ account.become.ansible_user | default('') }}" diff --git a/apps/accounts/automations/change_secret/custom/ssh/manifest.yml b/apps/accounts/automations/change_secret/custom/ssh/manifest.yml index be0248d68..7d3d0edde 100644 --- a/apps/accounts/automations/change_secret/custom/ssh/manifest.yml +++ b/apps/accounts/automations/change_secret/custom/ssh/manifest.yml @@ -6,6 +6,7 @@ category: type: - all method: change_secret +protocol: ssh params: - name: commands type: list diff --git a/apps/accounts/automations/verify_account/custom/rdp/main.yml b/apps/accounts/automations/verify_account/custom/rdp/main.yml index 017f4bab3..b0c7cbe4f 100644 --- a/apps/accounts/automations/verify_account/custom/rdp/main.yml +++ b/apps/accounts/automations/verify_account/custom/rdp/main.yml @@ -8,7 +8,7 @@ - name: Verify account (pyfreerdp) rdp_ping: login_host: "{{ jms_asset.address }}" - login_port: "{{ jms_asset.protocols | selectattr('name', 'equalto', 'rdp') | map(attribute='port') | first }}" + login_port: "{{ jms_asset.port }}" login_user: "{{ account.username }}" login_password: "{{ account.secret }}" login_secret_type: "{{ account.secret_type }}" diff --git a/apps/accounts/automations/verify_account/custom/rdp/manifest.yml b/apps/accounts/automations/verify_account/custom/rdp/manifest.yml index 1d68afbac..e4b034366 100644 --- a/apps/accounts/automations/verify_account/custom/rdp/manifest.yml +++ b/apps/accounts/automations/verify_account/custom/rdp/manifest.yml @@ -5,6 +5,7 @@ category: type: - windows method: verify_account +protocol: rdp i18n: Windows rdp account verify: diff --git a/apps/accounts/automations/verify_account/custom/ssh/main.yml b/apps/accounts/automations/verify_account/custom/ssh/main.yml index 4519fc3ad..05be21f0c 100644 --- a/apps/accounts/automations/verify_account/custom/ssh/main.yml +++ b/apps/accounts/automations/verify_account/custom/ssh/main.yml @@ -9,7 +9,7 @@ - name: Verify account (paramiko) ssh_ping: login_host: "{{ jms_asset.address }}" - login_port: "{{ jms_asset.protocols | selectattr('name', 'equalto', 'ssh') | map(attribute='port') | first }}" + login_port: "{{ jms_asset.port }}" login_user: "{{ account.username }}" login_password: "{{ account.secret }}" login_secret_type: "{{ account.secret_type }}" diff --git a/apps/accounts/automations/verify_account/custom/ssh/manifest.yml b/apps/accounts/automations/verify_account/custom/ssh/manifest.yml index 12d495d01..bebc02c7f 100644 --- a/apps/accounts/automations/verify_account/custom/ssh/manifest.yml +++ b/apps/accounts/automations/verify_account/custom/ssh/manifest.yml @@ -6,6 +6,7 @@ category: type: - all method: verify_account +protocol: ssh i18n: SSH account verify: diff --git a/apps/assets/automations/base/manager.py b/apps/assets/automations/base/manager.py index 4ebed3118..2a74243d0 100644 --- a/apps/assets/automations/base/manager.py +++ b/apps/assets/automations/base/manager.py @@ -53,8 +53,9 @@ class SSHTunnelManager: print(f'\033[31m {err_msg} 原因: {e} \033[0m\n') not_valid.append(k) else: + local_bind_port = server.local_bind_port host['ansible_host'] = jms_asset['address'] = host['login_host'] = '127.0.0.1' - host['ansible_port'] = jms_asset['port'] = host['login_port'] = server.local_bind_port + host['ansible_port'] = jms_asset['port'] = host['login_port'] = local_bind_port servers.append(server) # 网域不可连接的,就不继续执行此资源的后续任务了 @@ -211,22 +212,19 @@ class BasePlaybookManager: os.chmod(key_path, 0o400) return key_path - def generate_inventory(self, platformed_assets, inventory_path): + def generate_inventory(self, platformed_assets, inventory_path, protocol): inventory = JMSInventory( assets=platformed_assets, account_prefer=self.ansible_account_prefer, account_policy=self.ansible_account_policy, host_callback=self.host_callback, task_type=self.__class__.method_type(), + protocol=protocol, ) inventory.write_to_file(inventory_path) - def generate_playbook(self, platformed_assets, platform, sub_playbook_dir): - method_id = getattr(platform.automation, '{}_method'.format(self.__class__.method_type())) - method = self.method_id_meta_mapper.get(method_id) - if not method: - logger.error("Method not found: {}".format(method_id)) - return + @staticmethod + def generate_playbook(method, sub_playbook_dir): method_playbook_dir_path = method['dir'] sub_playbook_path = os.path.join(sub_playbook_dir, 'project', 'main.yml') shutil.copytree(method_playbook_dir_path, os.path.dirname(sub_playbook_path)) @@ -258,8 +256,16 @@ class BasePlaybookManager: sub_dir = '{}_{}'.format(platform.name, i) playbook_dir = os.path.join(self.runtime_dir, sub_dir) inventory_path = os.path.join(self.runtime_dir, sub_dir, 'hosts.json') - self.generate_inventory(_assets, inventory_path) - playbook_path = self.generate_playbook(_assets, platform, playbook_dir) + + method_id = getattr(platform.automation, '{}_method'.format(self.__class__.method_type())) + method = self.method_id_meta_mapper.get(method_id) + + if not method: + logger.error("Method not found: {}".format(method_id)) + continue + protocol = method.get('protocol') + self.generate_inventory(_assets, inventory_path, protocol) + playbook_path = self.generate_playbook(method, playbook_dir) if not playbook_path: continue diff --git a/apps/assets/automations/ping/custom/rdp/main.yml b/apps/assets/automations/ping/custom/rdp/main.yml index a68670998..75e40c027 100644 --- a/apps/assets/automations/ping/custom/rdp/main.yml +++ b/apps/assets/automations/ping/custom/rdp/main.yml @@ -10,6 +10,6 @@ login_user: "{{ jms_account.username }}" login_password: "{{ jms_account.secret }}" login_host: "{{ jms_asset.address }}" - login_port: "{{ jms_asset.protocols | selectattr('name', 'equalto', 'rdp') | map(attribute='port') | first }}" + login_port: "{{ jms_asset.port }}" login_secret_type: "{{ jms_account.secret_type }}" login_private_key_path: "{{ jms_account.private_key_path }}" diff --git a/apps/assets/automations/ping/custom/rdp/manifest.yml b/apps/assets/automations/ping/custom/rdp/manifest.yml index ab5846100..b8346c3f2 100644 --- a/apps/assets/automations/ping/custom/rdp/manifest.yml +++ b/apps/assets/automations/ping/custom/rdp/manifest.yml @@ -6,6 +6,8 @@ category: type: - windows method: ping +protocol: rdp + i18n: Ping by pyfreerdp: zh: '使用 Python 模块 pyfreerdp 测试主机可连接性' diff --git a/apps/assets/automations/ping/custom/ssh/main.yml b/apps/assets/automations/ping/custom/ssh/main.yml index 925d3f2e1..b974425be 100644 --- a/apps/assets/automations/ping/custom/ssh/main.yml +++ b/apps/assets/automations/ping/custom/ssh/main.yml @@ -11,7 +11,7 @@ login_user: "{{ jms_account.username }}" login_password: "{{ jms_account.secret }}" login_host: "{{ jms_asset.address }}" - login_port: "{{ jms_asset.protocols | selectattr('name', 'equalto', 'ssh') | map(attribute='port') | first }}" + login_port: "{{ jms_asset.port }}" login_secret_type: "{{ jms_account.secret_type }}" login_private_key_path: "{{ jms_account.private_key_path }}" become: "{{ custom_become | default(False) }}" diff --git a/apps/assets/automations/ping/custom/ssh/manifest.yml b/apps/assets/automations/ping/custom/ssh/manifest.yml index 95bb85d60..7a7068108 100644 --- a/apps/assets/automations/ping/custom/ssh/manifest.yml +++ b/apps/assets/automations/ping/custom/ssh/manifest.yml @@ -6,6 +6,8 @@ category: type: - all method: ping +protocol: ssh + i18n: Ping by paramiko: zh: '使用 Python 模块 paramiko 测试主机可连接性' diff --git a/apps/ops/ansible/inventory.py b/apps/ops/ansible/inventory.py index a6efc34c0..0dfa0b575 100644 --- a/apps/ops/ansible/inventory.py +++ b/apps/ops/ansible/inventory.py @@ -13,7 +13,7 @@ class JMSInventory: def __init__( self, assets, account_policy='privileged_first', account_prefer='root,Administrator', host_callback=None, - exclude_localhost=False, task_type=None + exclude_localhost=False, task_type=None, protocol=None ): """ :param assets: @@ -27,6 +27,7 @@ class JMSInventory: self.exclude_hosts = {} self.exclude_localhost = exclude_localhost self.task_type = task_type + self.protocol = protocol @staticmethod def clean_assets(assets): @@ -116,7 +117,7 @@ class JMSInventory: if gateway: ansible_connection = host.get('ansible_connection', 'ssh') - if ansible_connection in ('local', 'winrm'): + if ansible_connection in ('local', 'winrm', 'rdp'): host['gateway'] = { 'address': gateway.address, 'port': gateway.port, 'username': gateway.username, 'secret': gateway.password, @@ -128,19 +129,20 @@ class JMSInventory: host['jms_asset'].update(ansible_ssh_common_args) host.update(ansible_ssh_common_args) - @staticmethod - def get_primary_protocol(ansible_config, protocols): + def get_primary_protocol(self, ansible_config, protocols): invalid_protocol = type('protocol', (), {'name': 'null', 'port': 0}) ansible_connection = ansible_config.get('ansible_connection') # 数值越小,优先级越高,若用户在 ansible_config 中配置了,则提高用户配置方式的优先级 protocol_priority = {'ssh': 10, 'winrm': 9, ansible_connection: 1} + if self.protocol: + protocol_priority.update({self.protocol: 0}) protocol_sorted = sorted(protocols, key=lambda x: protocol_priority.get(x.name, 999)) protocol = protocol_sorted[0] if protocol_sorted else invalid_protocol return protocol @staticmethod def fill_ansible_config(ansible_config, protocol): - if protocol.name in ('ssh', 'winrm'): + if protocol.name in ('ssh', 'winrm', 'rdp'): ansible_config['ansible_connection'] = protocol.name if protocol.name == 'winrm': if protocol.setting.get('use_ssl', False): @@ -179,6 +181,8 @@ class JMSInventory: } if account else None } + protocols = host['jms_asset']['protocols'] + host['jms_asset'].update({f"{p['name']}_port": p['port'] for p in protocols}) if host['jms_account'] and tp == 'oracle': host['jms_account']['mode'] = 'sysdba' if account.privileged else None From 533f13c63444d8e8e10e8c04ab38173c6b73a77a Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Wed, 20 Dec 2023 16:35:36 +0800 Subject: [PATCH 099/111] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E5=88=9B?= =?UTF-8?q?=E5=BB=BA=E8=B4=A6=E5=8F=B7=E5=AF=86=E7=A0=81=E6=A0=A1=E9=AA=8C?= =?UTF-8?q?=E9=80=BB=E8=BE=91=20(#12383)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/accounts/models/account.py | 5 +- apps/accounts/utils.py | 5 +- apps/locale/ja/LC_MESSAGES/django.mo | 4 +- apps/locale/ja/LC_MESSAGES/django.po | 207 ++++++++++++++------------- apps/locale/zh/LC_MESSAGES/django.mo | 4 +- apps/locale/zh/LC_MESSAGES/django.po | 200 ++++++++++++++------------ 6 files changed, 223 insertions(+), 202 deletions(-) diff --git a/apps/accounts/models/account.py b/apps/accounts/models/account.py index 0c6d507bc..14f4bd2cf 100644 --- a/apps/accounts/models/account.py +++ b/apps/accounts/models/account.py @@ -137,10 +137,7 @@ class Account(AbsConnectivity, LabeledMixin, BaseAccount): return v.replace('{%', '{{ "{%" }}').replace('%}', '{{ "%}" }}') - if value.startswith('{{') and value.endswith('}}'): - return '{{' + escape(value[2:-2]) + '}}' - else: - return escape(value) + return escape(value) def replace_history_model_with_mixin(): diff --git a/apps/accounts/utils.py b/apps/accounts/utils.py index e56469c7e..9df67daf6 100644 --- a/apps/accounts/utils.py +++ b/apps/accounts/utils.py @@ -47,7 +47,10 @@ class SecretGenerator: def validate_password_for_ansible(password): """ 校验 Ansible 不支持的特殊字符 """ - pass + if password.startswith('{{') and password.endswith('}}'): + raise serializers.ValidationError( + _('If the password starts with {{` and ends with }} `, then the password is not allowed.') + ) def validate_ssh_key(ssh_key, passphrase=None): diff --git a/apps/locale/ja/LC_MESSAGES/django.mo b/apps/locale/ja/LC_MESSAGES/django.mo index 0f19f1f90..0973f6ca6 100644 --- a/apps/locale/ja/LC_MESSAGES/django.mo +++ b/apps/locale/ja/LC_MESSAGES/django.mo @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:13f24d4f5b06617cafccbef083f2b00341696eb2388e009b060621e470533254 -size 169044 +oid sha256:b6829921b5bab95a51607f9f387ef92a9c4e763131057e170bcf01fe3819f92e +size 169513 diff --git a/apps/locale/ja/LC_MESSAGES/django.po b/apps/locale/ja/LC_MESSAGES/django.po index 22e2047b8..983828311 100644 --- a/apps/locale/ja/LC_MESSAGES/django.po +++ b/apps/locale/ja/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-12-20 15:46+0800\n" +"POT-Creation-Date: 2023-12-20 16:30+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -256,7 +256,7 @@ msgstr "ユーザー %s がパスワードを閲覧/導き出しました" #: terminal/serializers/session.py:26 #: terminal/templates/terminal/_msg_command_warning.html:4 #: terminal/templates/terminal/_msg_session_sharing.html:4 -#: tickets/models/ticket/apply_asset.py:16 xpack/plugins/cloud/models.py:253 +#: tickets/models/ticket/apply_asset.py:16 xpack/plugins/cloud/models.py:256 msgid "Asset" msgstr "資産" @@ -388,7 +388,7 @@ msgid "Trigger mode" msgstr "トリガーモード" #: accounts/models/automations/backup_account.py:133 audits/models.py:203 -#: terminal/models/session/sharing.py:125 xpack/plugins/cloud/models.py:205 +#: terminal/models/session/sharing.py:125 xpack/plugins/cloud/models.py:208 msgid "Reason" msgstr "理由" @@ -482,8 +482,8 @@ msgstr "終了日" #: terminal/serializers/applet.py:18 terminal/serializers/applet_host.py:136 #: terminal/serializers/virtualapp.py:35 tickets/models/ticket/general.py:283 #: tickets/serializers/super_ticket.py:13 -#: tickets/serializers/ticket/ticket.py:20 xpack/plugins/cloud/models.py:201 -#: xpack/plugins/cloud/models.py:257 +#: tickets/serializers/ticket/ticket.py:20 xpack/plugins/cloud/models.py:204 +#: xpack/plugins/cloud/models.py:260 msgid "Status" msgstr "ステータス" @@ -613,8 +613,8 @@ msgstr "パスワードルール" #: terminal/models/virtualapp/virtualapp.py:19 tickets/api/ticket.py:87 #: users/forms/profile.py:33 users/models/group.py:13 #: users/models/preference.py:11 users/models/user.py:800 -#: xpack/plugins/cloud/models.py:32 xpack/plugins/cloud/models.py:273 -#: xpack/plugins/cloud/serializers/task.py:68 +#: xpack/plugins/cloud/models.py:32 xpack/plugins/cloud/models.py:276 +#: xpack/plugins/cloud/serializers/task.py:70 msgid "Name" msgstr "名前" @@ -644,7 +644,7 @@ msgstr "プラットフォーム" msgid "Push params" msgstr "パラメータをプッシュする" -#: accounts/models/template.py:26 xpack/plugins/cloud/models.py:325 +#: accounts/models/template.py:26 xpack/plugins/cloud/models.py:333 msgid "Account template" msgstr "アカウント テンプレート" @@ -929,7 +929,7 @@ msgstr "关联平台,可以配置推送参数,如果不关联,则使用默 #: terminal/models/session/session.py:46 #: terminal/models/virtualapp/virtualapp.py:28 tickets/models/comment.py:32 #: tickets/models/ticket/general.py:297 users/models/user.py:836 -#: xpack/plugins/cloud/models.py:39 xpack/plugins/cloud/models.py:109 +#: xpack/plugins/cloud/models.py:39 xpack/plugins/cloud/models.py:110 msgid "Comment" msgstr "コメント" @@ -1043,7 +1043,14 @@ msgstr "新規アカウント" msgid "Deleted account" msgstr "アカウントの削除" -#: accounts/utils.py:56 +#: accounts/utils.py:52 +msgid "" +"If the password starts with {{` and ends with }} `, then the password is not " +"allowed." +msgstr "" +"パスワードが`{{`で始まり、`}}`で終わる場合、パスワードは許可されません。" + +#: accounts/utils.py:59 msgid "private key invalid or passphrase error" msgstr "秘密鍵が無効またはpassphraseエラー" @@ -1074,13 +1081,13 @@ msgstr "通知" #: acls/models/base.py:37 assets/models/_user.py:51 #: assets/models/cmd_filter.py:76 terminal/models/component/endpoint.py:98 -#: xpack/plugins/cloud/models.py:275 +#: xpack/plugins/cloud/models.py:282 msgid "Priority" msgstr "優先順位" #: acls/models/base.py:38 assets/models/_user.py:51 #: assets/models/cmd_filter.py:76 terminal/models/component/endpoint.py:99 -#: xpack/plugins/cloud/models.py:276 +#: xpack/plugins/cloud/models.py:283 msgid "1-100, the lower the value will be match first" msgstr "1-100、低い値は最初に一致します" @@ -1117,7 +1124,7 @@ msgid "Command" msgstr "コマンド" #: acls/models/command_acl.py:17 assets/models/cmd_filter.py:59 -#: xpack/plugins/cloud/models.py:291 +#: xpack/plugins/cloud/models.py:299 msgid "Regex" msgstr "正規情報" @@ -1223,7 +1230,7 @@ msgid "None of the reviewers belong to Organization `{}`" msgstr "いずれのレビューアも組織 '{}' に属していません" #: acls/serializers/rules/rules.py:20 -#: xpack/plugins/cloud/serializers/task.py:137 +#: xpack/plugins/cloud/serializers/task.py:145 msgid "IP address invalid: `{}`" msgstr "IPアドレスが無効: '{}'" @@ -1335,11 +1342,11 @@ msgstr "同じレベルのノード名を同じにすることはできません msgid "App assets" msgstr "アプリ資産" -#: assets/automations/base/manager.py:187 +#: assets/automations/base/manager.py:188 msgid "{} disabled" msgstr "{} 無効" -#: assets/automations/base/manager.py:253 +#: assets/automations/base/manager.py:251 msgid " - Platform {} ansible disabled" msgstr " - プラットフォーム {} ansible 無効" @@ -1713,20 +1720,20 @@ msgstr "アドレス" #: assets/models/asset/common.py:161 assets/models/platform.py:126 #: authentication/backends/passkey/models.py:12 #: authentication/serializers/connect_token_secret.py:118 -#: perms/serializers/user_permission.py:24 xpack/plugins/cloud/models.py:321 +#: perms/serializers/user_permission.py:24 xpack/plugins/cloud/models.py:329 msgid "Platform" msgstr "プラットフォーム" #: assets/models/asset/common.py:163 assets/models/domain.py:22 #: authentication/serializers/connect_token_secret.py:136 -#: perms/serializers/user_permission.py:28 xpack/plugins/cloud/models.py:323 +#: perms/serializers/user_permission.py:28 xpack/plugins/cloud/models.py:331 msgid "Domain" msgstr "ドメイン" #: assets/models/asset/common.py:165 assets/models/automations/base.py:18 #: assets/models/cmd_filter.py:32 assets/models/node.py:553 #: perms/models/asset_permission.py:72 perms/serializers/permission.py:37 -#: tickets/models/ticket/apply_asset.py:14 xpack/plugins/cloud/models.py:322 +#: tickets/models/ticket/apply_asset.py:14 xpack/plugins/cloud/models.py:330 msgid "Node" msgstr "ノード" @@ -2055,8 +2062,8 @@ msgstr "" #: authentication/serializers/connect_token_secret.py:30 #: authentication/serializers/connect_token_secret.py:75 #: perms/models/asset_permission.py:76 perms/serializers/permission.py:42 -#: perms/serializers/user_permission.py:74 xpack/plugins/cloud/models.py:324 -#: xpack/plugins/cloud/serializers/task.py:31 +#: perms/serializers/user_permission.py:74 xpack/plugins/cloud/models.py:332 +#: xpack/plugins/cloud/serializers/task.py:33 msgid "Protocols" msgstr "プロトコル" @@ -3994,10 +4001,8 @@ msgid "Resource count" msgstr "リソース数" #: labels/serializers.py:28 -#, fuzzy -#| msgid "Can't contains: /" msgid "Cannot contain \":,\"" -msgstr "含まれない:/" +msgstr "\":,\"を含めることはできません" #: labels/serializers.py:43 msgid "Resource type" @@ -4027,15 +4032,15 @@ msgstr "システムメッセージ" msgid "Publish the station message" msgstr "投稿サイトニュース" -#: ops/ansible/inventory.py:96 ops/models/job.py:62 +#: ops/ansible/inventory.py:97 ops/models/job.py:62 msgid "No account available" msgstr "利用可能なアカウントがありません" -#: ops/ansible/inventory.py:260 +#: ops/ansible/inventory.py:264 msgid "Ansible disabled" msgstr "Ansible 無効" -#: ops/ansible/inventory.py:276 +#: ops/ansible/inventory.py:280 msgid "Skip hosts below:" msgstr "次のホストをスキップします: " @@ -4240,7 +4245,7 @@ msgid "Date last run" msgstr "最終実行日" #: ops/models/base.py:51 ops/models/job.py:231 -#: xpack/plugins/cloud/models.py:199 +#: xpack/plugins/cloud/models.py:202 msgid "Result" msgstr "結果" @@ -4894,10 +4899,8 @@ msgid "Can change auth ticket" msgstr "製造オーダ設定" #: settings/models.py:165 -#, fuzzy -#| msgid "Can change vault setting" msgid "Can change virtual app setting" -msgstr "金庫の設定を変えることができます" +msgstr "仮想アプリケーション設定を変更できます" #: settings/models.py:166 msgid "Can change auth announcement" @@ -5583,10 +5586,8 @@ msgid "Virtual app" msgstr "仮想アプリケーション" #: settings/serializers/feature.py:129 -#, fuzzy -#| msgid "Virtual app" msgid "Enable virtual app" -msgstr "仮想アプリを有効にする" +msgstr "仮想アプリケーションの有効化" #: settings/serializers/msg.py:24 msgid "SMTP host" @@ -6240,8 +6241,8 @@ msgstr "期限切れです。" #, python-format msgid "" "\n" -" Your password has expired, please click this link update password.\n" +" Your password has expired, please click this link update password.\n" " " msgstr "" "\n" @@ -6262,34 +6263,34 @@ msgid "" " " msgstr "" "\n" -" クリックしてください リンク パスワードの更新\n" +" クリックしてください リンク パスワードの更新\n" " " #: templates/_message.html:43 #, python-format msgid "" "\n" -" Your information was incomplete. Please click this link to complete your information.\n" +" Your information was incomplete. Please click this link to complete your information.\n" " " msgstr "" "\n" -" あなたの情報が不完全なので、クリックしてください。 リンク 補完\n" +" あなたの情報が不完全なので、クリックしてください。 リンク 補完\n" " " #: templates/_message.html:56 #, python-format msgid "" "\n" -" Your ssh public key not set or expired. Please click this link to update\n" +" Your ssh public key not set or expired. Please click this link to update\n" " " msgstr "" "\n" -" SSHキーが設定されていないか無効になっている場合は、 リンク 更新\n" +" SSHキーが設定されていないか無効になっている場合は、 リンク 更新\n" " " #: templates/_mfa_login_field.html:28 @@ -7136,7 +7137,7 @@ msgstr "アクセスキー" msgid "Access key secret" msgstr "アクセスキーシークレット" -#: terminal/serializers/storage.py:68 xpack/plugins/cloud/models.py:250 +#: terminal/serializers/storage.py:68 xpack/plugins/cloud/models.py:253 msgid "Region" msgstr "リージョン" @@ -8307,6 +8308,11 @@ msgstr "パスワードの成功をリセットし、ログインページに戻 msgid "XPACK" msgstr "XPack" +#: xpack/exceptions.py:7 +msgid "" +"The current task is not synchronized with unmatched policy assets, skipping" +msgstr "" + #: xpack/plugins/cloud/api.py:56 msgid "Test connection successful" msgstr "テスト接続成功" @@ -8411,7 +8417,7 @@ msgstr "プライベートIP" msgid "Public IP" msgstr "パブリックIP" -#: xpack/plugins/cloud/const.py:41 xpack/plugins/cloud/models.py:295 +#: xpack/plugins/cloud/const.py:41 xpack/plugins/cloud/models.py:303 msgid "Instance name" msgstr "インスタンス名" @@ -8439,7 +8445,15 @@ msgstr "同期済み" msgid "Released" msgstr "リリース済み" -#: xpack/plugins/cloud/manager.py:54 +#: xpack/plugins/cloud/const.py:58 +msgid "And" +msgstr "そして" + +#: xpack/plugins/cloud/const.py:59 +msgid "Or" +msgstr "または" + +#: xpack/plugins/cloud/manager.py:57 msgid "Account unavailable" msgstr "利用できないアカウント" @@ -8463,7 +8477,7 @@ msgstr "クラウドアカウント" msgid "Test cloud account" msgstr "クラウドアカウントのテスト" -#: xpack/plugins/cloud/models.py:92 xpack/plugins/cloud/serializers/task.py:151 +#: xpack/plugins/cloud/models.py:92 xpack/plugins/cloud/serializers/task.py:159 msgid "Regions" msgstr "リージョン" @@ -8472,122 +8486,134 @@ msgid "Hostname strategy" msgstr "ホスト名戦略" #: xpack/plugins/cloud/models.py:100 -#: xpack/plugins/cloud/serializers/task.py:154 +#: xpack/plugins/cloud/serializers/task.py:162 msgid "IP network segment group" msgstr "IPネットワークセグメントグループ" #: xpack/plugins/cloud/models.py:103 -#: xpack/plugins/cloud/serializers/task.py:159 +#: xpack/plugins/cloud/serializers/task.py:167 msgid "Sync IP type" msgstr "同期IPタイプ" #: xpack/plugins/cloud/models.py:106 -#: xpack/plugins/cloud/serializers/task.py:177 +#: xpack/plugins/cloud/serializers/task.py:185 msgid "Always update" msgstr "常に更新" -#: xpack/plugins/cloud/models.py:112 +#: xpack/plugins/cloud/models.py:108 +msgid "Fully synchronous" +msgstr "完全同期" + +#: xpack/plugins/cloud/models.py:113 msgid "Date last sync" msgstr "最終同期日" -#: xpack/plugins/cloud/models.py:115 xpack/plugins/cloud/models.py:313 -#: xpack/plugins/cloud/models.py:337 +#: xpack/plugins/cloud/models.py:116 xpack/plugins/cloud/models.py:321 +#: xpack/plugins/cloud/models.py:345 msgid "Strategy" msgstr "戦略" -#: xpack/plugins/cloud/models.py:120 xpack/plugins/cloud/models.py:197 +#: xpack/plugins/cloud/models.py:121 xpack/plugins/cloud/models.py:200 msgid "Sync instance task" msgstr "インスタンスの同期タスク" -#: xpack/plugins/cloud/models.py:208 xpack/plugins/cloud/models.py:260 +#: xpack/plugins/cloud/models.py:211 xpack/plugins/cloud/models.py:263 msgid "Date sync" msgstr "日付の同期" -#: xpack/plugins/cloud/models.py:212 +#: xpack/plugins/cloud/models.py:215 msgid "Sync instance snapshot" msgstr "インスタンススナップショットの同期" -#: xpack/plugins/cloud/models.py:216 +#: xpack/plugins/cloud/models.py:219 msgid "Sync instance task execution" msgstr "インスタンスタスクの同期実行" -#: xpack/plugins/cloud/models.py:240 +#: xpack/plugins/cloud/models.py:243 msgid "Sync task" msgstr "同期タスク" -#: xpack/plugins/cloud/models.py:244 +#: xpack/plugins/cloud/models.py:247 msgid "Sync instance task history" msgstr "インスタンスタスク履歴の同期" -#: xpack/plugins/cloud/models.py:247 +#: xpack/plugins/cloud/models.py:250 msgid "Instance" msgstr "インスタンス" -#: xpack/plugins/cloud/models.py:264 +#: xpack/plugins/cloud/models.py:267 msgid "Sync instance detail" msgstr "同期インスタンスの詳細" -#: xpack/plugins/cloud/models.py:281 +#: xpack/plugins/cloud/models.py:279 xpack/plugins/cloud/serializers/task.py:72 +msgid "Rule relation" +msgstr "条件関係" + +#: xpack/plugins/cloud/models.py:288 msgid "Task strategy" msgstr "ミッション戦略です" -#: xpack/plugins/cloud/models.py:285 +#: xpack/plugins/cloud/models.py:292 msgid "Equal" msgstr "等しい" -#: xpack/plugins/cloud/models.py:286 +#: xpack/plugins/cloud/models.py:293 msgid "Not Equal" msgstr "不等于" -#: xpack/plugins/cloud/models.py:287 +#: xpack/plugins/cloud/models.py:294 msgid "In" msgstr "で..." -#: xpack/plugins/cloud/models.py:288 +#: xpack/plugins/cloud/models.py:295 msgid "Contains" msgstr "含む" -#: xpack/plugins/cloud/models.py:289 +#: xpack/plugins/cloud/models.py:296 +msgid "Exclude" +msgstr "除外" + +#: xpack/plugins/cloud/models.py:297 msgid "Startswith" msgstr "始まる..." -#: xpack/plugins/cloud/models.py:290 +#: xpack/plugins/cloud/models.py:298 msgid "Endswith" msgstr "終わる..." -#: xpack/plugins/cloud/models.py:296 +#: xpack/plugins/cloud/models.py:304 msgid "Instance platform" msgstr "インスタンス名" -#: xpack/plugins/cloud/models.py:297 +#: xpack/plugins/cloud/models.py:305 msgid "Instance address" msgstr "インスタンスアドレス" -#: xpack/plugins/cloud/models.py:304 +#: xpack/plugins/cloud/models.py:312 msgid "Rule attr" msgstr "ルール属性" -#: xpack/plugins/cloud/models.py:308 +#: xpack/plugins/cloud/models.py:316 msgid "Rule match" msgstr "ルール一致" -#: xpack/plugins/cloud/models.py:310 +#: xpack/plugins/cloud/models.py:318 msgid "Rule value" msgstr "ルール値" -#: xpack/plugins/cloud/models.py:317 xpack/plugins/cloud/serializers/task.py:70 +#: xpack/plugins/cloud/models.py:325 xpack/plugins/cloud/serializers/task.py:75 msgid "Strategy rule" msgstr "戦略ルール" -#: xpack/plugins/cloud/models.py:332 +#: xpack/plugins/cloud/models.py:340 msgid "Action attr" msgstr "アクション属性" -#: xpack/plugins/cloud/models.py:334 +#: xpack/plugins/cloud/models.py:342 msgid "Action value" msgstr "アクション値" -#: xpack/plugins/cloud/models.py:341 xpack/plugins/cloud/serializers/task.py:73 +#: xpack/plugins/cloud/models.py:349 xpack/plugins/cloud/serializers/task.py:78 msgid "Strategy action" msgstr "戦略アクション" @@ -8875,7 +8901,7 @@ msgstr "テストタイムアウト" msgid "Project" msgstr "project" -#: xpack/plugins/cloud/serializers/task.py:143 +#: xpack/plugins/cloud/serializers/task.py:151 msgid "" "Only instances matching the IP range will be synced.
    If the instance " "contains multiple IP addresses, the first IP address that matches will be " @@ -8889,11 +8915,11 @@ msgstr "" "ドレスをランダムに一致させることを意味します。
    例: " "192.168.1.0/24,10.1.1.1-10.1.1.20。" -#: xpack/plugins/cloud/serializers/task.py:149 +#: xpack/plugins/cloud/serializers/task.py:157 msgid "History count" msgstr "実行回数" -#: xpack/plugins/cloud/serializers/task.py:150 +#: xpack/plugins/cloud/serializers/task.py:158 msgid "Instance count" msgstr "インスタンス数" @@ -8977,21 +9003,6 @@ msgstr "エンタープライズプロフェッショナル版" msgid "Ultimate edition" msgstr "エンタープライズ・フラッグシップ・エディション" -#~ msgid "And" -#~ msgstr "そして" - -#~ msgid "Or" -#~ msgstr "または" - -#~ msgid "Fully synchronous" -#~ msgstr "完全同期" - -#~ msgid "Rule relation" -#~ msgstr "条件関係" - -#~ msgid "Exclude" -#~ msgstr "除外" - #~ msgid "Password can not contains `{{` or `}}`" #~ msgstr "パスワードには `{` または `}` 文字を含めることはできません" diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index f92f0c4a0..c8a8facbb 100644 --- a/apps/locale/zh/LC_MESSAGES/django.mo +++ b/apps/locale/zh/LC_MESSAGES/django.mo @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9d42c8bfd23292b98c49eb9d67ca90595efb97093506d4dfa3e8a6cb91a8cb9b -size 138519 +oid sha256:331424050addf35defe4e252287026432020a84a6e6b9e9e24ffcef131557f12 +size 138911 diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index be3a5f31a..c00048951 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: JumpServer 0.3.3\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-12-20 15:46+0800\n" +"POT-Creation-Date: 2023-12-20 16:30+0800\n" "PO-Revision-Date: 2021-05-20 10:54+0800\n" "Last-Translator: ibuler \n" "Language-Team: JumpServer team\n" @@ -255,7 +255,7 @@ msgstr "用户 %s 查看/导出 了密码" #: terminal/serializers/session.py:26 #: terminal/templates/terminal/_msg_command_warning.html:4 #: terminal/templates/terminal/_msg_session_sharing.html:4 -#: tickets/models/ticket/apply_asset.py:16 xpack/plugins/cloud/models.py:253 +#: tickets/models/ticket/apply_asset.py:16 xpack/plugins/cloud/models.py:256 msgid "Asset" msgstr "资产" @@ -387,7 +387,7 @@ msgid "Trigger mode" msgstr "触发模式" #: accounts/models/automations/backup_account.py:133 audits/models.py:203 -#: terminal/models/session/sharing.py:125 xpack/plugins/cloud/models.py:205 +#: terminal/models/session/sharing.py:125 xpack/plugins/cloud/models.py:208 msgid "Reason" msgstr "原因" @@ -481,8 +481,8 @@ msgstr "结束日期" #: terminal/serializers/applet.py:18 terminal/serializers/applet_host.py:136 #: terminal/serializers/virtualapp.py:35 tickets/models/ticket/general.py:283 #: tickets/serializers/super_ticket.py:13 -#: tickets/serializers/ticket/ticket.py:20 xpack/plugins/cloud/models.py:201 -#: xpack/plugins/cloud/models.py:257 +#: tickets/serializers/ticket/ticket.py:20 xpack/plugins/cloud/models.py:204 +#: xpack/plugins/cloud/models.py:260 msgid "Status" msgstr "状态" @@ -612,8 +612,8 @@ msgstr "密码规则" #: terminal/models/virtualapp/virtualapp.py:19 tickets/api/ticket.py:87 #: users/forms/profile.py:33 users/models/group.py:13 #: users/models/preference.py:11 users/models/user.py:800 -#: xpack/plugins/cloud/models.py:32 xpack/plugins/cloud/models.py:273 -#: xpack/plugins/cloud/serializers/task.py:68 +#: xpack/plugins/cloud/models.py:32 xpack/plugins/cloud/models.py:276 +#: xpack/plugins/cloud/serializers/task.py:70 msgid "Name" msgstr "名称" @@ -643,7 +643,7 @@ msgstr "系统平台" msgid "Push params" msgstr "账号推送参数" -#: accounts/models/template.py:26 xpack/plugins/cloud/models.py:325 +#: accounts/models/template.py:26 xpack/plugins/cloud/models.py:333 msgid "Account template" msgstr "账号模版" @@ -927,7 +927,7 @@ msgstr "关联平台,可配置推送参数,如果不关联,将使用默认 #: terminal/models/session/session.py:46 #: terminal/models/virtualapp/virtualapp.py:28 tickets/models/comment.py:32 #: tickets/models/ticket/general.py:297 users/models/user.py:836 -#: xpack/plugins/cloud/models.py:39 xpack/plugins/cloud/models.py:109 +#: xpack/plugins/cloud/models.py:39 xpack/plugins/cloud/models.py:110 msgid "Comment" msgstr "备注" @@ -1040,7 +1040,13 @@ msgstr "新增账号" msgid "Deleted account" msgstr "删除账号" -#: accounts/utils.py:56 +#: accounts/utils.py:52 +msgid "" +"If the password starts with {{` and ends with }} `, then the password is not " +"allowed." +msgstr "如果密码以 `{{` 开始,并且以 `}}` 结束,则该密码是不允许的。" + +#: accounts/utils.py:59 msgid "private key invalid or passphrase error" msgstr "密钥不合法或密钥密码错误" @@ -1071,13 +1077,13 @@ msgstr "通知" #: acls/models/base.py:37 assets/models/_user.py:51 #: assets/models/cmd_filter.py:76 terminal/models/component/endpoint.py:98 -#: xpack/plugins/cloud/models.py:275 +#: xpack/plugins/cloud/models.py:282 msgid "Priority" msgstr "优先级" #: acls/models/base.py:38 assets/models/_user.py:51 #: assets/models/cmd_filter.py:76 terminal/models/component/endpoint.py:99 -#: xpack/plugins/cloud/models.py:276 +#: xpack/plugins/cloud/models.py:283 msgid "1-100, the lower the value will be match first" msgstr "优先级可选范围为 1-100 (数值越小越优先)" @@ -1114,7 +1120,7 @@ msgid "Command" msgstr "命令" #: acls/models/command_acl.py:17 assets/models/cmd_filter.py:59 -#: xpack/plugins/cloud/models.py:291 +#: xpack/plugins/cloud/models.py:299 msgid "Regex" msgstr "正则表达式" @@ -1219,7 +1225,7 @@ msgid "None of the reviewers belong to Organization `{}`" msgstr "所有复核人都不属于组织 `{}`" #: acls/serializers/rules/rules.py:20 -#: xpack/plugins/cloud/serializers/task.py:137 +#: xpack/plugins/cloud/serializers/task.py:145 msgid "IP address invalid: `{}`" msgstr "IP 地址无效: `{}`" @@ -1328,11 +1334,11 @@ msgstr "同级别节点名字不能重复" msgid "App assets" msgstr "资产管理" -#: assets/automations/base/manager.py:187 +#: assets/automations/base/manager.py:188 msgid "{} disabled" msgstr "{} 已禁用" -#: assets/automations/base/manager.py:253 +#: assets/automations/base/manager.py:251 msgid " - Platform {} ansible disabled" msgstr " - 平台 {} Ansible 已禁用, 无法执行任务" @@ -1706,20 +1712,20 @@ msgstr "地址" #: assets/models/asset/common.py:161 assets/models/platform.py:126 #: authentication/backends/passkey/models.py:12 #: authentication/serializers/connect_token_secret.py:118 -#: perms/serializers/user_permission.py:24 xpack/plugins/cloud/models.py:321 +#: perms/serializers/user_permission.py:24 xpack/plugins/cloud/models.py:329 msgid "Platform" msgstr "系统平台" #: assets/models/asset/common.py:163 assets/models/domain.py:22 #: authentication/serializers/connect_token_secret.py:136 -#: perms/serializers/user_permission.py:28 xpack/plugins/cloud/models.py:323 +#: perms/serializers/user_permission.py:28 xpack/plugins/cloud/models.py:331 msgid "Domain" msgstr "网域" #: assets/models/asset/common.py:165 assets/models/automations/base.py:18 #: assets/models/cmd_filter.py:32 assets/models/node.py:553 #: perms/models/asset_permission.py:72 perms/serializers/permission.py:37 -#: tickets/models/ticket/apply_asset.py:14 xpack/plugins/cloud/models.py:322 +#: tickets/models/ticket/apply_asset.py:14 xpack/plugins/cloud/models.py:330 msgid "Node" msgstr "节点" @@ -2046,8 +2052,8 @@ msgstr "资产中批量更新平台,不符合平台类型跳过的资产" #: authentication/serializers/connect_token_secret.py:30 #: authentication/serializers/connect_token_secret.py:75 #: perms/models/asset_permission.py:76 perms/serializers/permission.py:42 -#: perms/serializers/user_permission.py:74 xpack/plugins/cloud/models.py:324 -#: xpack/plugins/cloud/serializers/task.py:31 +#: perms/serializers/user_permission.py:74 xpack/plugins/cloud/models.py:332 +#: xpack/plugins/cloud/serializers/task.py:33 msgid "Protocols" msgstr "协议组" @@ -3946,10 +3952,8 @@ msgid "Resource count" msgstr "资源数量" #: labels/serializers.py:28 -#, fuzzy -#| msgid "Can't contains: /" msgid "Cannot contain \":,\"" -msgstr "不能包含: /" +msgstr "不能包含\":,\"" #: labels/serializers.py:43 msgid "Resource type" @@ -3979,15 +3983,15 @@ msgstr "系统信息" msgid "Publish the station message" msgstr "发布站内消息" -#: ops/ansible/inventory.py:96 ops/models/job.py:62 +#: ops/ansible/inventory.py:97 ops/models/job.py:62 msgid "No account available" msgstr "无可用账号" -#: ops/ansible/inventory.py:260 +#: ops/ansible/inventory.py:264 msgid "Ansible disabled" msgstr "Ansible 已禁用" -#: ops/ansible/inventory.py:276 +#: ops/ansible/inventory.py:280 msgid "Skip hosts below:" msgstr "跳过以下主机: " @@ -4190,7 +4194,7 @@ msgid "Date last run" msgstr "最后运行日期" #: ops/models/base.py:51 ops/models/job.py:231 -#: xpack/plugins/cloud/models.py:199 +#: xpack/plugins/cloud/models.py:202 msgid "Result" msgstr "结果" @@ -4840,10 +4844,8 @@ msgid "Can change auth ticket" msgstr "工单设置" #: settings/models.py:165 -#, fuzzy -#| msgid "Can change vault setting" msgid "Can change virtual app setting" -msgstr "可以更改 vault 设置" +msgstr "可以更改虚拟应用设置" #: settings/models.py:166 msgid "Can change auth announcement" @@ -5525,8 +5527,6 @@ msgid "Virtual app" msgstr "虚拟应用" #: settings/serializers/feature.py:129 -#, fuzzy -#| msgid "Virtual app" msgid "Enable virtual app" msgstr "启用虚拟应用" @@ -6153,13 +6153,13 @@ msgstr "过期。" #, python-format msgid "" "\n" -" Your password has expired, please click this link update password.\n" +" Your password has expired, please click this link update password.\n" " " msgstr "" "\n" -" 您的密码已经过期,请点击 链接 更新密码\n" +" 您的密码已经过期,请点击 链接 更新密码\n" " " #: templates/_message.html:30 @@ -6183,8 +6183,8 @@ msgstr "" #, python-format msgid "" "\n" -" Your information was incomplete. Please click this link to complete your information.\n" +" Your information was incomplete. Please click this link to complete your information.\n" " " msgstr "" "\n" @@ -6196,13 +6196,13 @@ msgstr "" #, python-format msgid "" "\n" -" Your ssh public key not set or expired. Please click this link to update\n" +" Your ssh public key not set or expired. Please click this link to update\n" " " msgstr "" "\n" -" 您的SSH密钥没有设置或已失效,请点击 链接 更新\n" +" 您的SSH密钥没有设置或已失效,请点击 链接 更新\n" " " #: templates/_mfa_login_field.html:28 @@ -7037,7 +7037,7 @@ msgstr "Access key ID(AK)" msgid "Access key secret" msgstr "Access key secret(SK)" -#: terminal/serializers/storage.py:68 xpack/plugins/cloud/models.py:250 +#: terminal/serializers/storage.py:68 xpack/plugins/cloud/models.py:253 msgid "Region" msgstr "地域" @@ -8185,6 +8185,11 @@ msgstr "重置密码成功,返回到登录页面" msgid "XPACK" msgstr "XPack" +#: xpack/exceptions.py:7 +msgid "" +"The current task is not synchronized with unmatched policy assets, skipping" +msgstr "" + #: xpack/plugins/cloud/api.py:56 msgid "Test connection successful" msgstr "测试成功" @@ -8289,7 +8294,7 @@ msgstr "私有IP" msgid "Public IP" msgstr "公网IP" -#: xpack/plugins/cloud/const.py:41 xpack/plugins/cloud/models.py:295 +#: xpack/plugins/cloud/const.py:41 xpack/plugins/cloud/models.py:303 msgid "Instance name" msgstr "实例名称" @@ -8317,7 +8322,15 @@ msgstr "已同步" msgid "Released" msgstr "已释放" -#: xpack/plugins/cloud/manager.py:54 +#: xpack/plugins/cloud/const.py:58 +msgid "And" +msgstr "与" + +#: xpack/plugins/cloud/const.py:59 +msgid "Or" +msgstr "或" + +#: xpack/plugins/cloud/manager.py:57 msgid "Account unavailable" msgstr "账号无效" @@ -8341,7 +8354,7 @@ msgstr "云账号" msgid "Test cloud account" msgstr "测试云账号" -#: xpack/plugins/cloud/models.py:92 xpack/plugins/cloud/serializers/task.py:151 +#: xpack/plugins/cloud/models.py:92 xpack/plugins/cloud/serializers/task.py:159 msgid "Regions" msgstr "地域" @@ -8350,122 +8363,134 @@ msgid "Hostname strategy" msgstr "主机名策略" #: xpack/plugins/cloud/models.py:100 -#: xpack/plugins/cloud/serializers/task.py:154 +#: xpack/plugins/cloud/serializers/task.py:162 msgid "IP network segment group" msgstr "IP网段组" #: xpack/plugins/cloud/models.py:103 -#: xpack/plugins/cloud/serializers/task.py:159 +#: xpack/plugins/cloud/serializers/task.py:167 msgid "Sync IP type" msgstr "同步IP类型" #: xpack/plugins/cloud/models.py:106 -#: xpack/plugins/cloud/serializers/task.py:177 +#: xpack/plugins/cloud/serializers/task.py:185 msgid "Always update" msgstr "总是更新" -#: xpack/plugins/cloud/models.py:112 +#: xpack/plugins/cloud/models.py:108 +msgid "Fully synchronous" +msgstr "完全同步" + +#: xpack/plugins/cloud/models.py:113 msgid "Date last sync" msgstr "最后同步日期" -#: xpack/plugins/cloud/models.py:115 xpack/plugins/cloud/models.py:313 -#: xpack/plugins/cloud/models.py:337 +#: xpack/plugins/cloud/models.py:116 xpack/plugins/cloud/models.py:321 +#: xpack/plugins/cloud/models.py:345 msgid "Strategy" msgstr "策略" -#: xpack/plugins/cloud/models.py:120 xpack/plugins/cloud/models.py:197 +#: xpack/plugins/cloud/models.py:121 xpack/plugins/cloud/models.py:200 msgid "Sync instance task" msgstr "同步实例任务" -#: xpack/plugins/cloud/models.py:208 xpack/plugins/cloud/models.py:260 +#: xpack/plugins/cloud/models.py:211 xpack/plugins/cloud/models.py:263 msgid "Date sync" msgstr "同步日期" -#: xpack/plugins/cloud/models.py:212 +#: xpack/plugins/cloud/models.py:215 msgid "Sync instance snapshot" msgstr "同步实例快照" -#: xpack/plugins/cloud/models.py:216 +#: xpack/plugins/cloud/models.py:219 msgid "Sync instance task execution" msgstr "同步实例任务执行" -#: xpack/plugins/cloud/models.py:240 +#: xpack/plugins/cloud/models.py:243 msgid "Sync task" msgstr "同步任务" -#: xpack/plugins/cloud/models.py:244 +#: xpack/plugins/cloud/models.py:247 msgid "Sync instance task history" msgstr "同步实例任务历史" -#: xpack/plugins/cloud/models.py:247 +#: xpack/plugins/cloud/models.py:250 msgid "Instance" msgstr "实例" -#: xpack/plugins/cloud/models.py:264 +#: xpack/plugins/cloud/models.py:267 msgid "Sync instance detail" msgstr "同步实例详情" -#: xpack/plugins/cloud/models.py:281 +#: xpack/plugins/cloud/models.py:279 xpack/plugins/cloud/serializers/task.py:72 +msgid "Rule relation" +msgstr "条件关系" + +#: xpack/plugins/cloud/models.py:288 msgid "Task strategy" msgstr "任务策略" -#: xpack/plugins/cloud/models.py:285 +#: xpack/plugins/cloud/models.py:292 msgid "Equal" msgstr "等于" -#: xpack/plugins/cloud/models.py:286 +#: xpack/plugins/cloud/models.py:293 msgid "Not Equal" msgstr "不等于" -#: xpack/plugins/cloud/models.py:287 +#: xpack/plugins/cloud/models.py:294 msgid "In" msgstr "在...中" -#: xpack/plugins/cloud/models.py:288 +#: xpack/plugins/cloud/models.py:295 msgid "Contains" msgstr "包含" -#: xpack/plugins/cloud/models.py:289 +#: xpack/plugins/cloud/models.py:296 +msgid "Exclude" +msgstr "排除" + +#: xpack/plugins/cloud/models.py:297 msgid "Startswith" msgstr "以...开头" -#: xpack/plugins/cloud/models.py:290 +#: xpack/plugins/cloud/models.py:298 msgid "Endswith" msgstr "以...结尾" -#: xpack/plugins/cloud/models.py:296 +#: xpack/plugins/cloud/models.py:304 msgid "Instance platform" msgstr "实例平台" -#: xpack/plugins/cloud/models.py:297 +#: xpack/plugins/cloud/models.py:305 msgid "Instance address" msgstr "实例地址" -#: xpack/plugins/cloud/models.py:304 +#: xpack/plugins/cloud/models.py:312 msgid "Rule attr" msgstr "规则属性" -#: xpack/plugins/cloud/models.py:308 +#: xpack/plugins/cloud/models.py:316 msgid "Rule match" msgstr "规则匹配" -#: xpack/plugins/cloud/models.py:310 +#: xpack/plugins/cloud/models.py:318 msgid "Rule value" msgstr "规则值" -#: xpack/plugins/cloud/models.py:317 xpack/plugins/cloud/serializers/task.py:70 +#: xpack/plugins/cloud/models.py:325 xpack/plugins/cloud/serializers/task.py:75 msgid "Strategy rule" msgstr "条件" -#: xpack/plugins/cloud/models.py:332 +#: xpack/plugins/cloud/models.py:340 msgid "Action attr" msgstr "动作属性" -#: xpack/plugins/cloud/models.py:334 +#: xpack/plugins/cloud/models.py:342 msgid "Action value" msgstr "动作值" -#: xpack/plugins/cloud/models.py:341 xpack/plugins/cloud/serializers/task.py:73 +#: xpack/plugins/cloud/models.py:349 xpack/plugins/cloud/serializers/task.py:78 msgid "Strategy action" msgstr "动作" @@ -8752,7 +8777,7 @@ msgstr "测试超时时间" msgid "Project" msgstr "project" -#: xpack/plugins/cloud/serializers/task.py:143 +#: xpack/plugins/cloud/serializers/task.py:151 msgid "" "Only instances matching the IP range will be synced.
    If the instance " "contains multiple IP addresses, the first IP address that matches will be " @@ -8764,11 +8789,11 @@ msgstr "" "到的 IP 地址将被用作创建的资产的 IP。
    默认值 * 表示同步所有实例和随机匹配 " "IP 地址。
    例如: 192.168.1.0/24,10.1.1.1-10.1.1.20。" -#: xpack/plugins/cloud/serializers/task.py:149 +#: xpack/plugins/cloud/serializers/task.py:157 msgid "History count" msgstr "执行次数" -#: xpack/plugins/cloud/serializers/task.py:150 +#: xpack/plugins/cloud/serializers/task.py:158 msgid "Instance count" msgstr "实例个数" @@ -8852,21 +8877,6 @@ msgstr "企业专业版" msgid "Ultimate edition" msgstr "企业旗舰版" -#~ msgid "And" -#~ msgstr "与" - -#~ msgid "Or" -#~ msgstr "或" - -#~ msgid "Fully synchronous" -#~ msgstr "完全同步" - -#~ msgid "Rule relation" -#~ msgstr "条件关系" - -#~ msgid "Exclude" -#~ msgstr "排除" - #~ msgid "Password can not contains `{{` or `}}`" #~ msgstr "密码不能包含 `{{` 或 `}}` 字符" From 9f67ba573cbffed18024c68843089c4dd0ff2c52 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Wed, 20 Dec 2023 18:48:37 +0800 Subject: [PATCH 100/111] =?UTF-8?q?perf:=20dockerfile=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=20freerdp2-dev=20=E4=BE=9D=E8=B5=96=20(#12386)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- Dockerfile-ce | 1 + 1 file changed, 1 insertion(+) diff --git a/Dockerfile-ce b/Dockerfile-ce index be974828f..915494216 100644 --- a/Dockerfile-ce +++ b/Dockerfile-ce @@ -76,6 +76,7 @@ ENV LANG=zh_CN.UTF-8 \ ARG DEPENDENCIES=" \ libjpeg-dev \ libx11-dev \ + freerdp2-dev \ libxmlsec1-openssl" ARG TOOLS=" \ From 98c9cddcbfc827fa4c6c10607cc69044e4894d34 Mon Sep 17 00:00:00 2001 From: wangruidong <940853815@qq.com> Date: Wed, 20 Dec 2023 17:45:18 +0800 Subject: [PATCH 101/111] =?UTF-8?q?fix:=20es=E5=91=BD=E4=BB=A4=E8=AE=B0?= =?UTF-8?q?=E5=BD=95=E5=8F=AF=E4=BB=A5=E7=9C=8B=E5=88=B0=E5=85=B6=E4=BB=96?= =?UTF-8?q?=E8=B5=84=E4=BA=A7=E6=89=A7=E8=A1=8C=E7=9A=84=E5=91=BD=E4=BB=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/common/plugins/es.py | 21 ++++++++++++++++----- apps/terminal/api/session/command.py | 4 ---- apps/terminal/filters.py | 13 +++++++++++-- 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/apps/common/plugins/es.py b/apps/common/plugins/es.py index 297f2e32a..8e2126f85 100644 --- a/apps/common/plugins/es.py +++ b/apps/common/plugins/es.py @@ -228,6 +228,18 @@ class ES(object): datetime_range['lte'] = datetime__lte return 'datetime', datetime_range + @staticmethod + def handle_exact_fields(exact): + _filter = [] + for k, v in exact.items(): + query = 'term' + if isinstance(v, list): + query = 'terms' + _filter.append({ + query: {k: v} + }) + return _filter + def get_query_body(self, **kwargs): new_kwargs = {} for k, v in kwargs.items(): @@ -235,6 +247,8 @@ class ES(object): v = str(v) if k == 'pk': k = 'id' + if k.endswith('__in'): + k = k.replace('__in', '') new_kwargs[k] = v kwargs = new_kwargs @@ -287,11 +301,8 @@ class ES(object): {'match': {k: v}} for k, v in match.items() ], 'should': should, - 'filter': [ - { - 'term': {k: v} - } for k, v in exact.items() - ] + [ + 'filter': self.handle_exact_fields(exact) + + [ { 'range': { time_field_name: time_range diff --git a/apps/terminal/api/session/command.py b/apps/terminal/api/session/command.py index 387aebc3e..baec8f0fa 100644 --- a/apps/terminal/api/session/command.py +++ b/apps/terminal/api/session/command.py @@ -167,7 +167,6 @@ class CommandViewSet(JMSBulkModelViewSet): def get_queryset(self): command_storage_id = self.request.query_params.get('command_storage_id') - asset_id = self.request.query_params.get('asset_id') if not command_storage_id: return Command.objects.none() @@ -176,9 +175,6 @@ class CommandViewSet(JMSBulkModelViewSet): raise StorageInvalid else: qs = storage.get_command_queryset() - if asset_id: - session_ids = Session.objects.filter(asset_id=asset_id).values_list('id', flat=True) - qs = qs.filter(session__in=list(session_ids)) return qs def create(self, request, *args, **kwargs): diff --git a/apps/terminal/filters.py b/apps/terminal/filters.py index 40c048b28..0839c770c 100644 --- a/apps/terminal/filters.py +++ b/apps/terminal/filters.py @@ -2,7 +2,7 @@ from django.db.models import QuerySet from django_filters import rest_framework as filters from orgs.utils import filter_org_queryset -from terminal.models import Command, CommandStorage +from terminal.models import Command, CommandStorage, Session class CommandFilter(filters.FilterSet): @@ -13,7 +13,7 @@ class CommandFilter(filters.FilterSet): user = filters.CharFilter(lookup_expr='startswith') input = filters.CharFilter(lookup_expr='icontains') asset = filters.CharFilter(field_name='asset', lookup_expr='icontains') - asset_id = filters.UUIDFilter(method='do_nothing') + asset_id = filters.UUIDFilter(method='filter_by_asset_id') class Meta: model = Command @@ -48,6 +48,15 @@ class CommandFilter(filters.FilterSet): qs = qs.filter(**filters) return qs + def filter_by_asset_id(self, queryset, name, value): + asset_id = self.form.cleaned_data.get('asset_id') + filters = {} + if asset_id: + session_ids = Session.objects.filter(asset_id=asset_id).values_list('id', flat=True) + filters['session__in'] = list(session_ids) + queryset = queryset.filter(**filters) + return queryset + class CommandFilterForStorageTree(CommandFilter): asset = filters.CharFilter(method='do_nothing') From 55575e9f7fb3c387eef842173c8ad35d71e06c29 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Wed, 20 Dec 2023 19:24:09 +0800 Subject: [PATCH 102/111] =?UTF-8?q?perf:=20=E7=94=A8=E6=88=B7=E8=B4=A6?= =?UTF-8?q?=E5=8F=B7=E5=AF=BC=E5=87=BA=E5=8E=BB=E9=99=A4is=5Fservice=5Facc?= =?UTF-8?q?ount=20(#12388)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/users/serializers/user.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/users/serializers/user.py b/apps/users/serializers/user.py index 9d68189d4..d4228b920 100644 --- a/apps/users/serializers/user.py +++ b/apps/users/serializers/user.py @@ -147,7 +147,7 @@ class UserSerializer(RolesSerializerMixin, CommonBulkSerializerMixin, ResourceLa # 在serializer 上定义的字段 fields_custom = ["login_blocked", "password_strategy"] fields = fields_verbose + fields_fk + fields_m2m + fields_custom - fields_unexport = ["avatar_url", ] + fields_unexport = ["avatar_url", "is_service_account"] read_only_fields = [ "date_joined", "last_login", "created_by", From 4d15e46ceb322b6451c27a32687a3570d17e96db Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 20 Dec 2023 20:47:04 +0800 Subject: [PATCH 103/111] =?UTF-8?q?perf:=20=E4=BF=AE=E6=94=B9=E6=90=9C?= =?UTF-8?q?=E7=B4=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/common/signal_handlers.py | 4 ++-- apps/labels/api.py | 17 ++++++++++++++++- apps/labels/models.py | 4 ++-- apps/locale/zh/LC_MESSAGES/django.po | 2 +- 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/apps/common/signal_handlers.py b/apps/common/signal_handlers.py index 588a63632..b517e4575 100644 --- a/apps/common/signal_handlers.py +++ b/apps/common/signal_handlers.py @@ -69,7 +69,7 @@ def digest_sql_query(): for query in queries: sql = query['sql'] - print(" # {}: {}".format(query['time'], sql)) + # print(" # {}: {}".format(query['time'], sql)) if len(queries) < 3: continue print("- Table: {}".format(table_name)) @@ -77,7 +77,7 @@ def digest_sql_query(): sql = query['sql'] if not sql or not sql.startswith('SELECT'): continue - print('\t{}. {}'.format(i, sql)) + # print('\t{}. {}'.format(i, sql)) logger.debug(">>> [{}] {}".format(method, path)) for name, counter in counters: diff --git a/apps/labels/api.py b/apps/labels/api.py index 2dc51e4b8..ccf0751b3 100644 --- a/apps/labels/api.py +++ b/apps/labels/api.py @@ -123,13 +123,28 @@ class LabelViewSet(OrgBulkModelViewSet): class LabeledResourceViewSet(OrgBulkModelViewSet): model = LabeledResource filterset_fields = ("label__name", "label__value", "res_type", "res_id", "label") - search_fields = filterset_fields + search_fields = [] serializer_classes = { 'default': serializers.LabeledResourceSerializer, } ordering_fields = ('res_type', 'date_created') + def filter_search(self, queryset): + keyword = self.request.query_params.get('search') + if not keyword: + return queryset + matched = [] + for instance in queryset: + if keyword.lower() in str(instance.resource).lower(): + matched.append(instance.id) + return queryset.filter(id__in=matched) + def get_queryset(self): queryset = super().get_queryset() queryset = queryset.order_by('res_type') return queryset + + def filter_queryset(self, queryset): + queryset = super().filter_queryset(queryset) + queryset = self.filter_search(queryset) + return queryset diff --git a/apps/labels/models.py b/apps/labels/models.py index 3bf9213f5..0d5874dcb 100644 --- a/apps/labels/models.py +++ b/apps/labels/models.py @@ -40,5 +40,5 @@ class LabeledResource(JMSOrgBaseModel): unique_together = [('label', 'res_type', 'res_id', 'org_id')] verbose_name = _('Labeled resource') - def __str__(self): - return '{} => {}'.format(self.label, self.resource) + # def __str__(self): + # return '{} => {}'.format(self.label, self.resource) diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index c00048951..e91b1891f 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -6904,7 +6904,7 @@ msgstr "" "这些账号用于连接发布的应用,账号现在分为两种类型:
    一种是专用的,每个用" "户都有一个专用账号。 另一种是公共的,当应用不支持多开且专用的已经被使用时,会" "使用公共账号连接;
    注意: 如果不开启自动创建账号, 当前发布机仅能被指定标" -"签的资产调度到,默认不会放到调度池中" +"签的资产调度到,默认不会放到调度池中,且需要手动维护账号" #: terminal/serializers/applet_host.py:92 msgid "The number of public accounts created automatically" From 88b9a4d69357517ff8294aa5e4e0b01569ac9c95 Mon Sep 17 00:00:00 2001 From: ibuler Date: Wed, 20 Dec 2023 20:49:50 +0800 Subject: [PATCH 104/111] =?UTF-8?q?perf:=20=E4=BF=AE=E6=94=B9=E6=90=9C?= =?UTF-8?q?=E7=B4=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/common/signal_handlers.py | 4 ++-- apps/labels/api.py | 1 + apps/labels/models.py | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/apps/common/signal_handlers.py b/apps/common/signal_handlers.py index b517e4575..588a63632 100644 --- a/apps/common/signal_handlers.py +++ b/apps/common/signal_handlers.py @@ -69,7 +69,7 @@ def digest_sql_query(): for query in queries: sql = query['sql'] - # print(" # {}: {}".format(query['time'], sql)) + print(" # {}: {}".format(query['time'], sql)) if len(queries) < 3: continue print("- Table: {}".format(table_name)) @@ -77,7 +77,7 @@ def digest_sql_query(): sql = query['sql'] if not sql or not sql.startswith('SELECT'): continue - # print('\t{}. {}'.format(i, sql)) + print('\t{}. {}'.format(i, sql)) logger.debug(">>> [{}] {}".format(method, path)) for name, counter in counters: diff --git a/apps/labels/api.py b/apps/labels/api.py index ccf0751b3..44a6702e7 100644 --- a/apps/labels/api.py +++ b/apps/labels/api.py @@ -129,6 +129,7 @@ class LabeledResourceViewSet(OrgBulkModelViewSet): } ordering_fields = ('res_type', 'date_created') + # Todo: 这里需要优化,查询 sql 太多 def filter_search(self, queryset): keyword = self.request.query_params.get('search') if not keyword: diff --git a/apps/labels/models.py b/apps/labels/models.py index 0d5874dcb..3bf9213f5 100644 --- a/apps/labels/models.py +++ b/apps/labels/models.py @@ -40,5 +40,5 @@ class LabeledResource(JMSOrgBaseModel): unique_together = [('label', 'res_type', 'res_id', 'org_id')] verbose_name = _('Labeled resource') - # def __str__(self): - # return '{} => {}'.format(self.label, self.resource) + def __str__(self): + return '{} => {}'.format(self.label, self.resource) From ca026040feb4f3a8f66f0ce6424a5f6f825da8bc Mon Sep 17 00:00:00 2001 From: ibuler Date: Thu, 21 Dec 2023 11:56:10 +0800 Subject: [PATCH 105/111] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=E5=AF=BC?= =?UTF-8?q?=E5=85=A5=E8=B4=A6=E5=8F=B7=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/common/serializers/fields.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/common/serializers/fields.py b/apps/common/serializers/fields.py index ef2589628..4627af85e 100644 --- a/apps/common/serializers/fields.py +++ b/apps/common/serializers/fields.py @@ -66,6 +66,10 @@ class LabeledChoiceField(ChoiceField): def to_internal_value(self, data): if isinstance(data, dict): data = data.get("value") + if "(" in data and data.endswith(")"): + d = data.strip(")").split('(')[-1] + if d in self.choices: + data = d return super(LabeledChoiceField, self).to_internal_value(data) From a38624d198cb0f3eee26466e080676fc0fc9e6fd Mon Sep 17 00:00:00 2001 From: ibuler Date: Thu, 21 Dec 2023 14:02:24 +0800 Subject: [PATCH 106/111] =?UTF-8?q?perf:=20=E4=BF=AE=E6=94=B9=E5=90=8C?= =?UTF-8?q?=E5=90=8D=E8=B4=A6=E5=8F=B7=E7=99=BB=E5=BD=95=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/accounts/models/account.py | 6 +++++- apps/terminal/models/applet/applet.py | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/apps/accounts/models/account.py b/apps/accounts/models/account.py index 14f4bd2cf..4a175609f 100644 --- a/apps/accounts/models/account.py +++ b/apps/accounts/models/account.py @@ -73,7 +73,11 @@ class Account(AbsConnectivity, LabeledMixin, BaseAccount): ] def __str__(self): - return '{}({})'.format(self.name, self.asset.name) + if self.asset_id: + host = self.asset.name + else: + host = 'Dynamic' + return '{}({})'.format(self.name, host) @lazyproperty def platform(self): diff --git a/apps/terminal/models/applet/applet.py b/apps/terminal/models/applet/applet.py index 05209b03d..fd5934285 100644 --- a/apps/terminal/models/applet/applet.py +++ b/apps/terminal/models/applet/applet.py @@ -301,7 +301,7 @@ class Applet(JMSBaseModel): 'account': account, 'lock_key': lock_key } - logger.debug('Select host and account: {}'.format(res)) + logger.debug('Select host and account: {}-{}'.format(host.name, account.username)) return res def delete(self, using=None, keep_parents=False): From dc56b019b19150eff3810c9a424c5b76f2abeb5f Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Thu, 21 Dec 2023 15:00:46 +0800 Subject: [PATCH 107/111] =?UTF-8?q?perf:=20=E6=9D=83=E9=99=90=E6=A0=91?= =?UTF-8?q?=E7=BF=BB=E8=AF=91=20(#12396)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/labels/apps.py | 2 ++ apps/rbac/const.py | 1 + 2 files changed, 3 insertions(+) diff --git a/apps/labels/apps.py b/apps/labels/apps.py index 434a5c6df..a3bf4dabd 100644 --- a/apps/labels/apps.py +++ b/apps/labels/apps.py @@ -1,6 +1,8 @@ from django.apps import AppConfig +from django.utils.translation import gettext_lazy as _ class LabelsConfig(AppConfig): default_auto_field = 'django.db.models.BigAutoField' name = 'labels' + verbose_name = _('Labels') diff --git a/apps/rbac/const.py b/apps/rbac/const.py index 0bac7029e..71eb47fbc 100644 --- a/apps/rbac/const.py +++ b/apps/rbac/const.py @@ -28,6 +28,7 @@ exclude_permissions = ( ('authentication', 'superconnectiontoken', 'change,delete', 'superconnectiontoken'), ('authentication', 'temptoken', 'delete', 'temptoken'), ('users', 'userpasswordhistory', '*', '*'), + ('users', 'usersession', '*', '*'), ('assets', 'adminuser', '*', '*'), ('assets', 'assetgroup', '*', '*'), ('assets', 'cluster', '*', '*'), From bc668f3e9f01a0c59eef88efb120a229870be466 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Thu, 21 Dec 2023 15:42:09 +0800 Subject: [PATCH 108/111] =?UTF-8?q?fix:=20applet=20=E5=8E=8B=E7=BC=A9?= =?UTF-8?q?=E5=8C=85=E5=90=8D=E5=AD=97(1).zip=E6=97=B6=20=E4=B8=8A?= =?UTF-8?q?=E4=BC=A0=E5=A4=B1=E8=B4=A5}=20(#12397)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/terminal/api/applet/applet.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/apps/terminal/api/applet/applet.py b/apps/terminal/api/applet/applet.py index 9e656487b..7f68d67d3 100644 --- a/apps/terminal/api/applet/applet.py +++ b/apps/terminal/api/applet/applet.py @@ -1,4 +1,6 @@ +import os import os.path +import re import shutil import zipfile from typing import Callable @@ -51,6 +53,11 @@ class DownloadUploadMixin: raise ValidationError({'error': _('Invalid zip file') + ': {}'.format(e)}) tmp_dir = os.path.join(extract_to, file.name.replace('.zip', '')) + if not os.path.exists(tmp_dir): + name = file.name + name = re.match(r"(\w+)", name).group() + tmp_dir = os.path.join(extract_to, name) + manifest = Applet.validate_pkg(tmp_dir) return manifest, tmp_dir From b6774aa74953653f15e42fbba5eb45658babeccb Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Thu, 21 Dec 2023 16:07:11 +0800 Subject: [PATCH 109/111] =?UTF-8?q?perf:=20=E6=9B=B4=E6=96=B0=E5=85=A8?= =?UTF-8?q?=E5=B1=80=E7=BB=84=E7=BB=87=E5=90=8D=E5=AD=97=20=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E5=94=AF=E4=B8=80=E6=80=A7=E6=A0=A1=E9=AA=8C=20(#1239?= =?UTF-8?q?9)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/settings/serializers/basic.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/apps/settings/serializers/basic.py b/apps/settings/serializers/basic.py index ad1c2f8e3..4e253c602 100644 --- a/apps/settings/serializers/basic.py +++ b/apps/settings/serializers/basic.py @@ -1,6 +1,8 @@ from django.utils.translation import gettext_lazy as _ from rest_framework import serializers +from orgs.models import Organization + class BasicSettingSerializer(serializers.Serializer): PREFIX_TITLE = _('Basic') @@ -34,3 +36,10 @@ class BasicSettingSerializer(serializers.Serializer): if not s: return 'http://127.0.0.1' return s.strip('/') + + @staticmethod + def validate_GLOBAL_ORG_DISPLAY_NAME(s): + org_names = Organization.objects.values_list('name', flat=True) + if s in org_names: + raise serializers.ValidationError(_('Organization name already exists')) + return s From febf08629ac0f691a387d8ad790bee5c98359bc1 Mon Sep 17 00:00:00 2001 From: fit2bot <68588906+fit2bot@users.noreply.github.com> Date: Thu, 21 Dec 2023 16:15:20 +0800 Subject: [PATCH 110/111] =?UTF-8?q?fix:=20=E7=BF=BB=E8=AF=91=20(#12400)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: feng <1304903146@qq.com> --- apps/locale/ja/LC_MESSAGES/django.mo | 4 +-- apps/locale/ja/LC_MESSAGES/django.po | 46 +++++++++++++++------------- apps/locale/zh/LC_MESSAGES/django.mo | 4 +-- apps/locale/zh/LC_MESSAGES/django.po | 46 +++++++++++++++------------- 4 files changed, 54 insertions(+), 46 deletions(-) diff --git a/apps/locale/ja/LC_MESSAGES/django.mo b/apps/locale/ja/LC_MESSAGES/django.mo index 0973f6ca6..50e4479af 100644 --- a/apps/locale/ja/LC_MESSAGES/django.mo +++ b/apps/locale/ja/LC_MESSAGES/django.mo @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b6829921b5bab95a51607f9f387ef92a9c4e763131057e170bcf01fe3819f92e -size 169513 +oid sha256:71d292647cf751c002b459449c7bebf4d2bf5a3933748387e7c2f80a7111302e +size 169602 diff --git a/apps/locale/ja/LC_MESSAGES/django.po b/apps/locale/ja/LC_MESSAGES/django.po index 983828311..d46d47829 100644 --- a/apps/locale/ja/LC_MESSAGES/django.po +++ b/apps/locale/ja/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-12-20 16:30+0800\n" +"POT-Creation-Date: 2023-12-21 16:12+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -1401,7 +1401,7 @@ msgstr "資産情報の収集" msgid "Disabled" msgstr "無効" -#: assets/const/base.py:33 settings/serializers/basic.py:6 +#: assets/const/base.py:33 settings/serializers/basic.py:8 #: users/serializers/preference/koko.py:19 #: users/serializers/preference/lina.py:39 #: users/serializers/preference/luna.py:73 @@ -3707,7 +3707,7 @@ msgid "Invalid ids for ids, should be a list" msgstr "無効なID、リストでなければなりません" #: common/db/fields.py:585 common/db/fields.py:590 -#: common/serializers/fields.py:130 tickets/serializers/ticket/common.py:58 +#: common/serializers/fields.py:134 tickets/serializers/ticket/common.py:58 #: xpack/plugins/cloud/serializers/account_attrs.py:56 #: xpack/plugins/cloud/serializers/account_attrs.py:79 #: xpack/plugins/cloud/serializers/account_attrs.py:150 @@ -3880,25 +3880,25 @@ msgstr "{} 秒待ってから送信してください" msgid "Children" msgstr "ノード" -#: common/serializers/fields.py:131 +#: common/serializers/fields.py:135 #, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "無効な pk \"{pk_value}\" - オブジェクトが存在しません" -#: common/serializers/fields.py:132 +#: common/serializers/fields.py:136 #, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "エラータイプ。 予想される pk 値、受信 {data_type}。" -#: common/serializers/fields.py:206 +#: common/serializers/fields.py:210 msgid "Invalid data type, should be list" msgstr "間違ったデータ タイプです。リストにする必要があります" -#: common/serializers/fields.py:221 +#: common/serializers/fields.py:225 msgid "Invalid choice: {}" msgstr "無効なオプション: {}" -#: common/serializers/mixin.py:397 +#: common/serializers/mixin.py:397 labels/apps.py:8 msgid "Labels" msgstr "ラベル" @@ -5409,11 +5409,11 @@ msgstr "単位: 秒" msgid "Enable WeCom Auth" msgstr "企業微信認証の有効化" -#: settings/serializers/basic.py:9 +#: settings/serializers/basic.py:11 msgid "Site url" msgstr "サイトURL" -#: settings/serializers/basic.py:11 +#: settings/serializers/basic.py:13 msgid "" "External URL, email links or other system callbacks are used to access it, " "eg: http://dev.jumpserver.org:8080" @@ -5421,38 +5421,42 @@ msgstr "" "外部URL、メールリンクまたは他のシステムコールバックにアクセスするには、" "http://dev.jumpserver.org:8080などを使用します" -#: settings/serializers/basic.py:16 +#: settings/serializers/basic.py:18 msgid "User guide url" msgstr "ユーザーガイドurl" -#: settings/serializers/basic.py:17 +#: settings/serializers/basic.py:19 msgid "User first login update profile done redirect to it" msgstr "ユーザーの最初のログイン更新プロファイルがリダイレクトされました" -#: settings/serializers/basic.py:20 +#: settings/serializers/basic.py:22 msgid "Global organization name" msgstr "グローバル組織名" -#: settings/serializers/basic.py:21 +#: settings/serializers/basic.py:23 msgid "The name of global organization to display" msgstr "表示するグローバル組織の名前" -#: settings/serializers/basic.py:24 +#: settings/serializers/basic.py:26 msgid "Help Docs URL" msgstr "ドキュメントリンク" -#: settings/serializers/basic.py:25 +#: settings/serializers/basic.py:27 msgid "default: http://docs.jumpserver.org" msgstr "デフォルト: http://docs.jumpserver.org" -#: settings/serializers/basic.py:28 +#: settings/serializers/basic.py:30 msgid "Help Support URL" msgstr "サポートリンク" -#: settings/serializers/basic.py:29 +#: settings/serializers/basic.py:31 msgid "default: http://www.jumpserver.org/support/" msgstr "デフォルト: http://www.jumpserver.org/support/" +#: settings/serializers/basic.py:44 +msgid "Organization name already exists" +msgstr "組織名はすでに存在します。" + #: settings/serializers/cleaning.py:11 msgid "Period clean" msgstr "定時清掃" @@ -6357,13 +6361,13 @@ msgstr "" msgid "Offline video player" msgstr "オフラインビデオプレーヤー" -#: terminal/api/applet/applet.py:48 terminal/api/applet/applet.py:51 +#: terminal/api/applet/applet.py:50 terminal/api/applet/applet.py:53 #: terminal/api/virtualapp/virtualapp.py:43 #: terminal/api/virtualapp/virtualapp.py:46 msgid "Invalid zip file" msgstr "zip ファイルが無効です" -#: terminal/api/applet/applet.py:65 +#: terminal/api/applet/applet.py:72 msgid "This is enterprise edition applet" msgstr "これはエンタープライズ版アプレットです" @@ -8453,7 +8457,7 @@ msgstr "そして" msgid "Or" msgstr "または" -#: xpack/plugins/cloud/manager.py:57 +#: xpack/plugins/cloud/manager.py:56 msgid "Account unavailable" msgstr "利用できないアカウント" diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index c8a8facbb..abcc5b418 100644 --- a/apps/locale/zh/LC_MESSAGES/django.mo +++ b/apps/locale/zh/LC_MESSAGES/django.mo @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:331424050addf35defe4e252287026432020a84a6e6b9e9e24ffcef131557f12 -size 138911 +oid sha256:853320b42ac7795fa983c4216f89b3a776a889453d90e11dc425d1388ff9b803 +size 139012 diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index e91b1891f..bd5989a79 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: JumpServer 0.3.3\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-12-20 16:30+0800\n" +"POT-Creation-Date: 2023-12-21 16:12+0800\n" "PO-Revision-Date: 2021-05-20 10:54+0800\n" "Last-Translator: ibuler \n" "Language-Team: JumpServer team\n" @@ -1393,7 +1393,7 @@ msgstr "收集资产信息" msgid "Disabled" msgstr "禁用" -#: assets/const/base.py:33 settings/serializers/basic.py:6 +#: assets/const/base.py:33 settings/serializers/basic.py:8 #: users/serializers/preference/koko.py:19 #: users/serializers/preference/lina.py:39 #: users/serializers/preference/luna.py:73 @@ -3663,7 +3663,7 @@ msgid "Invalid ids for ids, should be a list" msgstr "无效的ID,应为列表" #: common/db/fields.py:585 common/db/fields.py:590 -#: common/serializers/fields.py:130 tickets/serializers/ticket/common.py:58 +#: common/serializers/fields.py:134 tickets/serializers/ticket/common.py:58 #: xpack/plugins/cloud/serializers/account_attrs.py:56 #: xpack/plugins/cloud/serializers/account_attrs.py:79 #: xpack/plugins/cloud/serializers/account_attrs.py:150 @@ -3834,25 +3834,25 @@ msgstr "请在 {} 秒后发送" msgid "Children" msgstr "节点" -#: common/serializers/fields.py:131 +#: common/serializers/fields.py:135 #, python-brace-format msgid "Invalid pk \"{pk_value}\" - object does not exist." msgstr "错误的 pk \"{pk_value}\" - 对象不存在" -#: common/serializers/fields.py:132 +#: common/serializers/fields.py:136 #, python-brace-format msgid "Incorrect type. Expected pk value, received {data_type}." msgstr "错误类型。期望 pk 值,收到 {data_type}。" -#: common/serializers/fields.py:206 +#: common/serializers/fields.py:210 msgid "Invalid data type, should be list" msgstr "错误的数据类型,应该是列表" -#: common/serializers/fields.py:221 +#: common/serializers/fields.py:225 msgid "Invalid choice: {}" msgstr "无效选项: {}" -#: common/serializers/mixin.py:397 +#: common/serializers/mixin.py:397 labels/apps.py:8 msgid "Labels" msgstr "标签管理" @@ -5351,11 +5351,11 @@ msgstr "单位: 秒" msgid "Enable WeCom Auth" msgstr "启用企业微信认证" -#: settings/serializers/basic.py:9 +#: settings/serializers/basic.py:11 msgid "Site url" msgstr "当前站点 URL" -#: settings/serializers/basic.py:11 +#: settings/serializers/basic.py:13 msgid "" "External URL, email links or other system callbacks are used to access it, " "eg: http://dev.jumpserver.org:8080" @@ -5363,38 +5363,42 @@ msgstr "" "外部可访问的 URL, 用于邮件链接或其它系统回调, 例如: http://dev.jumpserver." "org:8080" -#: settings/serializers/basic.py:16 +#: settings/serializers/basic.py:18 msgid "User guide url" msgstr "用户向导URL" -#: settings/serializers/basic.py:17 +#: settings/serializers/basic.py:19 msgid "User first login update profile done redirect to it" msgstr "用户第一次登录,修改profile后重定向到地址, 可以是 wiki 或 其他说明文档" -#: settings/serializers/basic.py:20 +#: settings/serializers/basic.py:22 msgid "Global organization name" msgstr "全局组织名" -#: settings/serializers/basic.py:21 +#: settings/serializers/basic.py:23 msgid "The name of global organization to display" msgstr "全局组织的显示名称,默认为 全局组织" -#: settings/serializers/basic.py:24 +#: settings/serializers/basic.py:26 msgid "Help Docs URL" msgstr "文档链接" -#: settings/serializers/basic.py:25 +#: settings/serializers/basic.py:27 msgid "default: http://docs.jumpserver.org" msgstr "默认: http://dev.jumpserver.org:8080" -#: settings/serializers/basic.py:28 +#: settings/serializers/basic.py:30 msgid "Help Support URL" msgstr "支持链接" -#: settings/serializers/basic.py:29 +#: settings/serializers/basic.py:31 msgid "default: http://www.jumpserver.org/support/" msgstr "默认: http://www.jumpserver.org/support/" +#: settings/serializers/basic.py:44 +msgid "Organization name already exists" +msgstr "组织名称已存在" + #: settings/serializers/cleaning.py:11 msgid "Period clean" msgstr "定時清掃" @@ -6264,13 +6268,13 @@ msgstr "OpenSSH 是在 windows 远程应用发布服务器中用来连接远程 msgid "Offline video player" msgstr "离线录像播放器" -#: terminal/api/applet/applet.py:48 terminal/api/applet/applet.py:51 +#: terminal/api/applet/applet.py:50 terminal/api/applet/applet.py:53 #: terminal/api/virtualapp/virtualapp.py:43 #: terminal/api/virtualapp/virtualapp.py:46 msgid "Invalid zip file" msgstr "无效的 zip 文件" -#: terminal/api/applet/applet.py:65 +#: terminal/api/applet/applet.py:72 msgid "This is enterprise edition applet" msgstr "企业版远程应用,在社区版中不能使用" @@ -8330,7 +8334,7 @@ msgstr "与" msgid "Or" msgstr "或" -#: xpack/plugins/cloud/manager.py:57 +#: xpack/plugins/cloud/manager.py:56 msgid "Account unavailable" msgstr "账号无效" From bc5494bbb07eda8567d8bd9fe2cff5d3af28b4d9 Mon Sep 17 00:00:00 2001 From: ibuler Date: Thu, 21 Dec 2023 16:45:38 +0800 Subject: [PATCH 111/111] =?UTF-8?q?perf:=20=E4=BC=98=E5=8C=96=20label=20ch?= =?UTF-8?q?oice?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/common/serializers/fields.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/apps/common/serializers/fields.py b/apps/common/serializers/fields.py index 4627af85e..282394106 100644 --- a/apps/common/serializers/fields.py +++ b/apps/common/serializers/fields.py @@ -66,10 +66,9 @@ class LabeledChoiceField(ChoiceField): def to_internal_value(self, data): if isinstance(data, dict): data = data.get("value") - if "(" in data and data.endswith(")"): - d = data.strip(")").split('(')[-1] - if d in self.choices: - data = d + + if isinstance(data, str) and "(" in data and data.endswith(")"): + data = data.strip(")").split('(')[-1] return super(LabeledChoiceField, self).to_internal_value(data)