Compare commits

...

62 Commits

Author SHA1 Message Date
Jiangjie Bai
a005c78653 Merge pull request #16449 from jumpserver/dev
v4.10.14
2025-12-18 17:28:48 +08:00
Bai
6d92ddf2d3 perf: redirect confirm page only support back to index page 2025-12-18 16:16:06 +08:00
feng
296112bed8 perf: check_api 2025-12-17 17:58:39 +08:00
Bai
50a857e182 perf: click confirm, add interval 30s auto close tab 2025-12-17 17:24:59 +08:00
Bai
cfca2cdf5c perf: set redirect-confirm page, add close button, user refresh redirect to indexPage 2025-12-17 16:54:55 +08:00
Bai
599797299f perf: add oauth2_provider.log to logger 2025-12-17 15:19:03 +08:00
fit2bot
eee7333745 fix: Integrations services failed to call api (#16441)
Co-authored-by: wangruidong <940853815@qq.com>
2025-12-16 11:34:07 +08:00
wangruidong
e7ed098503 fix: Remove Ansible Receptor configuration and related code 2025-12-16 11:33:29 +08:00
ibuler
29fb518156 perf: change word spell 2025-12-16 11:33:07 +08:00
wangruidong
e16e2cb964 perf: Update translations for Diff and Export filtered in Chinese 2025-12-16 11:32:44 +08:00
fit2bot
a41b6b41e0 perf: Add custom chat model (#16439)
Co-authored-by: feng <1304903146@qq.com>
2025-12-16 09:18:56 +08:00
feng
933e6e4c15 perf: Vault save maximum recursion depth exceeded 2025-12-15 15:56:54 +08:00
feng
46384e19b5 perf: client version 2025-12-12 15:36:37 +08:00
fit2bot
e388a7efa0 perf: chat ai custom model (#16428) 2025-12-12 15:28:20 +08:00
Chenyang Shen
4bc345542c Merge pull request #16424 from jumpserver/pr@dev@feat_redis_lock_on_piico
feat: add redis lock on piico card init
2025-12-12 15:00:57 +08:00
Aaron3S
829e9b1497 feat: add redis lock on picco card init 2025-12-11 18:38:21 +08:00
fit2bot
8e703d306c feat: Add permission check for reading account secrets based on system settings (#16337) 2025-12-11 16:42:10 +08:00
wangruidong
af908480f4 fix: Add '/media/' to the list of whitelisted URLs for MFA login 2025-12-11 16:39:10 +08:00
wangruidong
fc2d4ae751 fix: Add user validity check to authentication process 2025-12-11 16:38:32 +08:00
wangruidong
cde5fb7a3e perf: Include 'id' in search and filter fields for AutomationExecution 2025-12-11 16:36:51 +08:00
Eric
19da95c6fb perf: update vnc protocol 2025-12-10 15:53:48 +08:00
Eric
bc4f29a6f6 perf: support virtual_app vnc client 2025-12-10 15:26:06 +08:00
Jiangjie Bai
1d0db2ba8b Merge pull request #16316 from jumpserver/dev
v4.10.13-lts
2025-11-20 20:20:02 +08:00
老广
e617245b26 merge: to master 2025-10-16 17:30:34 +08:00
Bryan
9280884c1c Merge pull request #16056 from jumpserver/dev
v4.10.8-lts
2025-09-18 16:52:13 +08:00
Bryan
f31994fdcd Merge pull request #15899 from jumpserver/dev 2025-08-21 19:03:18 +08:00
Bryan
71766418bb Merge pull request #15742 from jumpserver/dev
merge: v4.10.4-lts
2025-07-17 15:12:58 +08:00
Bryan
a9399dd709 Merge pull request #15608 from jumpserver/dev
v4.10.2
2025-06-19 20:14:21 +08:00
Bryan
d0cb9e5432 Merge pull request #15412 from jumpserver/dev
v4.10.0
2025-05-15 17:11:43 +08:00
老广
558188da90 merge: dev to master
Ready to relase
2025-04-17 20:24:45 +08:00
Bryan
ad5460dab8 Merge pull request #15086 from jumpserver/dev
v4.8.0
2025-03-20 18:44:44 +08:00
Bryan
4d37dca0de Merge pull request #14901 from jumpserver/dev
v4.7.0
2025-02-20 10:21:16 +08:00
Bryan
2ca4002624 Merge pull request #14813 from jumpserver/dev
v4.6.0
2025-01-15 14:38:17 +08:00
Bryan
053d640e4c Merge pull request #14699 from jumpserver/dev
v4.5.0
2024-12-19 16:04:45 +08:00
Bryan
f3acc28ded Merge pull request #14697 from jumpserver/dev
v4.5.0
2024-12-19 15:57:11 +08:00
Bryan
25987545db Merge pull request #14511 from jumpserver/dev
v4.4.0
2024-11-21 19:00:35 +08:00
Bryan
6720ecc6e0 Merge pull request #14319 from jumpserver/dev
v4.3.0
2024-10-17 14:55:38 +08:00
老广
0b3a7bb020 Merge pull request #14203 from jumpserver/dev
merge: from dev to master
2024-09-19 19:37:19 +08:00
Bryan
56373e362b Merge pull request #13988 from jumpserver/dev
v4.1.0
2024-08-16 18:40:35 +08:00
Bryan
02fc045370 Merge pull request #13600 from jumpserver/dev
v4.0.0
2024-07-03 19:04:35 +08:00
Bryan
e4ac73896f Merge pull request #13452 from jumpserver/dev
v3.10.11-lts
2024-06-19 16:01:26 +08:00
Bryan
1518f792d6 Merge pull request #13236 from jumpserver/dev
v3.10.10-lts
2024-05-16 16:04:07 +08:00
Bai
67277dd622 fix: 修复仪表盘会话排序数量都是 1 的问题 2024-04-22 19:42:33 +08:00
Bryan
82e7f020ea Merge pull request #13094 from jumpserver/dev
v3.10.9 (dev to master)
2024-04-22 19:39:53 +08:00
Bryan
f20b9e01ab Merge pull request #13062 from jumpserver/dev
v3.10.8 dev to master
2024-04-18 18:01:20 +08:00
Bryan
8cf8a3701b Merge pull request #13059 from jumpserver/dev
v3.10.8
2024-04-18 17:16:37 +08:00
Bryan
7ba24293d1 Merge pull request #12736 from jumpserver/pr@dev@master_fix
fix: 解决冲突
2024-02-29 16:38:43 +08:00
Bai
f10114c9ed fix: 解决冲突 2024-02-29 16:37:10 +08:00
Bryan
cf31cbfb07 Merge pull request #12729 from jumpserver/dev
v3.10.4
2024-02-29 16:19:59 +08:00
wangruidong
0edad24d5d fix: 资产过期消息提示发送失败 2024-02-04 11:41:48 +08:00
ibuler
1f1c1a9157 fix: 修复定时检测用户是否活跃任务无法执行的问题 2024-01-23 09:28:38 +00:00
feng
6c9d271ae1 fix: redis 密码有特殊字符celery beat启动失败 2024-01-22 06:18:34 +00:00
Bai
6ff852e225 perf: 修复 Count 时没有去重的问题 2024-01-22 06:16:25 +00:00
Bryan
baa75dc735 Merge pull request #12566 from jumpserver/master
v3.10.2
2024-01-17 07:34:28 -04:00
Bryan
8a9f0436b8 Merge pull request #12565 from jumpserver/dev
v3.10.2
2024-01-17 07:23:30 -04:00
Bryan
a9620a3cbe Merge pull request #12461 from jumpserver/master
v3.10.1
2023-12-29 11:33:05 +05:00
Bryan
769e7dc8a0 Merge pull request #12460 from jumpserver/dev
v3.10.1
2023-12-29 11:20:36 +05:00
Bryan
2a70449411 Merge pull request #12458 from jumpserver/dev
v3.10.1
2023-12-29 11:01:13 +05:00
Bryan
8df720f19e Merge pull request #12401 from jumpserver/dev
v3.10
2023-12-21 15:14:19 +05:00
老广
dabbb45f6e Merge pull request #12144 from jumpserver/dev
v3.9.0
2023-11-16 18:23:05 +08:00
Bryan
ce24c1c3fd Merge pull request #11914 from jumpserver/dev
v3.8.0
2023-10-19 03:37:39 -05:00
Bryan
3c54c82ce9 Merge pull request #11636 from jumpserver/dev
v3.7.0
2023-09-21 17:02:48 +08:00
58 changed files with 4282 additions and 6711 deletions

View File

@@ -1,3 +1,4 @@
from django.conf import settings
from django.db import transaction
from django.shortcuts import get_object_or_404
from django.utils.translation import gettext_lazy as _
@@ -166,7 +167,7 @@ class AccountViewSet(OrgBulkModelViewSet):
class AccountSecretsViewSet(AccountRecordViewLogMixin, AccountViewSet):
"""
因为可能要导出所有账号所以单独建立了一个 viewset
因为可能要导出所有账号,所以单独建立了一个 viewset
"""
serializer_classes = {
'default': serializers.AccountSecretSerializer,

View File

@@ -81,4 +81,7 @@ class IntegrationApplicationViewSet(OrgBulkModelViewSet):
remote_addr=get_request_ip(request), service=service.name, service_id=service.id,
account=f'{account.name}({account.username})', asset=f'{asset.name}({asset.address})',
)
return Response(data={'id': request.user.id, 'secret': account.secret})
# 根据配置决定是否返回密码
secret = account.secret if settings.SECURITY_ACCOUNT_SECRET_READ else None
return Response(data={'id': request.user.id, 'secret': secret})

View File

@@ -1,3 +1,5 @@
from django.conf import settings
from django.utils.translation import gettext_lazy as _
from django_filters import rest_framework as drf_filters
from rest_framework import status
from rest_framework.decorators import action

View File

@@ -104,7 +104,7 @@ class AutomationExecutionViewSet(
mixins.CreateModelMixin, mixins.ListModelMixin,
mixins.RetrieveModelMixin, viewsets.GenericViewSet
):
search_fields = ('trigger', 'automation__name')
search_fields = ('id', 'trigger', 'automation__name')
filterset_fields = ('trigger', 'automation_id', 'automation__name')
filterset_class = AutomationExecutionFilterSet
serializer_class = serializers.AutomationExecutionSerializer

View File

@@ -234,7 +234,7 @@ class AutomationExecutionFilterSet(DaysExecutionFilterMixin, BaseFilterSet):
class Meta:
model = AutomationExecution
fields = ["days", 'trigger', 'automation__name']
fields = ["id", "days", 'trigger', 'automation__name']
class PushAccountRecordFilterSet(SecretRecordMixin, UUIDFilterMixin, BaseFilterSet):

View File

@@ -81,7 +81,9 @@ class VaultModelMixin(models.Model):
def mark_secret_save_to_vault(self):
self._secret = self._secret_save_to_vault_mark
self.skip_history_when_saving = True
self.save()
# Avoid calling overridden `save()` on concrete models (e.g. AccountTemplate)
# which may mutate `secret/_secret` again and cause post_save recursion.
super(VaultModelMixin, self).save(update_fields=['_secret'])
@property
def secret_has_save_to_vault(self):

View File

@@ -14,7 +14,7 @@ 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
from common.serializers import SecretReadableMixin, CommonBulkModelSerializer
from common.serializers import SecretReadableMixin, SecretReadableCheckMixin, CommonBulkModelSerializer
from common.serializers.fields import ObjectRelatedField, LabeledChoiceField
from common.utils import get_logger
from .base import BaseAccountSerializer, AuthValidateMixin
@@ -478,7 +478,7 @@ class AssetAccountBulkSerializer(
return results
class AccountSecretSerializer(SecretReadableMixin, AccountSerializer):
class AccountSecretSerializer(SecretReadableCheckMixin, SecretReadableMixin, AccountSerializer):
spec_info = serializers.DictField(label=_('Spec info'), read_only=True)
class Meta(AccountSerializer.Meta):
@@ -491,9 +491,10 @@ class AccountSecretSerializer(SecretReadableMixin, AccountSerializer):
exclude_backup_fields = [
'passphrase', 'push_now', 'params', 'spec_info'
]
secret_fields = ['secret']
class AccountHistorySerializer(serializers.ModelSerializer):
class AccountHistorySerializer(SecretReadableCheckMixin, serializers.ModelSerializer):
secret_type = LabeledChoiceField(choices=SecretType.choices, label=_('Secret type'))
secret = serializers.CharField(label=_('Secret'), read_only=True)
id = serializers.IntegerField(label=_('ID'), source='history_id', read_only=True)
@@ -509,6 +510,7 @@ class AccountHistorySerializer(serializers.ModelSerializer):
'history_user': {'label': _('User')},
'history_date': {'label': _('Date')},
}
secret_fields = ['secret']
class AccountTaskSerializer(serializers.Serializer):

View File

@@ -2,7 +2,7 @@ from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
from accounts.models import AccountTemplate
from common.serializers import SecretReadableMixin
from common.serializers import SecretReadableMixin, SecretReadableCheckMixin
from common.serializers.fields import ObjectRelatedField
from .base import BaseAccountSerializer
@@ -62,10 +62,11 @@ class AccountDetailTemplateSerializer(AccountTemplateSerializer):
fields = AccountTemplateSerializer.Meta.fields + ['spec_info']
class AccountTemplateSecretSerializer(SecretReadableMixin, AccountDetailTemplateSerializer):
class AccountTemplateSecretSerializer(SecretReadableCheckMixin, SecretReadableMixin, AccountDetailTemplateSerializer):
class Meta(AccountDetailTemplateSerializer.Meta):
fields = AccountDetailTemplateSerializer.Meta.fields
extra_kwargs = {
**AccountDetailTemplateSerializer.Meta.extra_kwargs,
'secret': {'write_only': False},
}
secret_fields = ['secret']

View File

@@ -79,7 +79,7 @@ class VaultSignalHandler(object):
else:
vault_client.update(instance)
except Exception as e:
logger.error('Vault save failed: {}'.format(e))
logger.exception('Vault save failed: %s', e)
raise VaultException()
@staticmethod
@@ -87,7 +87,7 @@ class VaultSignalHandler(object):
try:
vault_client.delete(instance)
except Exception as e:
logger.error('Vault delete failed: {}'.format(e))
logger.exception('Vault delete failed: %s', e)
raise VaultException()

View File

@@ -6,7 +6,7 @@ from django.utils.translation import gettext_lazy as _
from accounts.backends import vault_client
from accounts.const import VaultTypeChoices
from accounts.models import Account, AccountTemplate
from accounts.models import AccountTemplate, Account
from common.utils import get_logger
from orgs.utils import tmp_to_root_org

View File

@@ -126,7 +126,7 @@ class BaseManager:
self.execution.save()
def print_summary(self):
content = "\nSummery: \n"
content = "\nSummary: \n"
for k, v in self.summary.items():
content += f"\t - {k}: {v}\n"
content += "\t - Using: {}s\n".format(self.duration)

View File

@@ -219,8 +219,18 @@ class RDPFileClientProtocolURLMixin:
}
})
else:
if connect_method_dict['type'] == 'virtual_app':
endpoint_protocol = 'vnc'
token_protocol = 'vnc'
data.update({
'protocol': 'vnc',
})
else:
endpoint_protocol = connect_method_dict['endpoint_protocol']
token_protocol = token.protocol
endpoint = self.get_smart_endpoint(
protocol=connect_method_dict['endpoint_protocol'],
protocol=endpoint_protocol,
asset=asset
)
data.update({
@@ -236,7 +246,7 @@ class RDPFileClientProtocolURLMixin:
},
'endpoint': {
'host': endpoint.host,
'port': endpoint.get_port(token.asset, token.protocol),
'port': endpoint.get_port(token.asset, token_protocol),
}
})
return data

View File

@@ -108,7 +108,7 @@ class SessionAuthentication(authentication.SessionAuthentication):
user = getattr(request._request, 'user', None)
# Unauthenticated, CSRF validation not required
if not user or not user.is_active:
if not user or not user.is_active or not user.is_valid:
return None
try:

View File

@@ -36,7 +36,7 @@ class MFAMiddleware:
# 这个是 mfa 登录页需要的请求, 也得放出来, 用户其实已经在 CAS/OIDC 中完成登录了
white_urls = [
'login/mfa', 'mfa/select', 'face/context', 'jsi18n/', '/static/',
'/profile/otp', '/logout/',
'/profile/otp', '/logout/', '/media/'
]
for url in white_urls:
if request.path.find(url) > -1:

View File

@@ -1,13 +1,12 @@
import re
import uuid
import time
import uuid
from django.conf import settings
from django.contrib.auth import get_user_model
from django.core.management.base import BaseCommand
from django.test import Client
from django.urls import URLPattern, URLResolver
from django.contrib.auth import get_user_model
from django.contrib.auth.models import AnonymousUser
from jumpserver.urls import api_v1
@@ -81,7 +80,8 @@ known_unauth_urls = [
"/api/v1/authentication/mfa/send-code/",
"/api/v1/authentication/sso/login/",
"/api/v1/authentication/user-session/",
"/api/v1/settings/i18n/zh-hans/"
"/api/v1/settings/i18n/zh-hans/",
"/api/v1/settings/client/versions/"
]
known_error_urls = [

View File

@@ -1,9 +1,12 @@
import os
from ctypes import *
from .exception import PiicoError
from .session import Session
from .cipher import *
from .digest import *
from django.core.cache import cache
from redis_lock import Lock as RedisLock
class Device:
@@ -71,10 +74,28 @@ class Device:
self.__device = device
def __reset_key_store(self):
redis_client = cache.client.get_client()
server_hostname = os.environ.get("SERVER_HOSTNAME")
RESET_LOCK_KEY = f"spiico:{server_hostname}:reset"
LOCK_EXPIRE_SECONDS = 300
if self._driver is None:
raise PiicoError("no driver loaded", 0)
if self.__device is None:
raise PiicoError("device not open", 0)
# ---- 分布式锁Redis-Lock 实现 Redlock ----
lock = RedisLock(
redis_client,
RESET_LOCK_KEY,
expire=LOCK_EXPIRE_SECONDS, # 锁自动过期
auto_renewal=False, # 不自动续租
)
# 尝试获取锁,拿不到直接返回
if not lock.acquire(blocking=False):
return
# ---- 真正执行 reset ----
ret = self._driver.SPII_ResetModule(self.__device)
if ret != 0:
raise PiicoError("reset device failed", ret)
raise PiicoError("reset device failed", ret)

View File

@@ -30,12 +30,30 @@ __all__ = [
"CommonSerializerMixin",
"CommonBulkSerializerMixin",
"SecretReadableMixin",
"SecretReadableCheckMixin",
"CommonModelSerializer",
"CommonBulkModelSerializer",
"ResourceLabelsMixin",
]
class SecretReadableCheckMixin(serializers.Serializer):
"""
根据 SECURITY_ACCOUNT_SECRET_READ 配置控制密码字段的可读性
当配置为 False 时,密码字段返回 None
"""
def to_representation(self, instance):
ret = super().to_representation(instance)
if not settings.SECURITY_ACCOUNT_SECRET_READ:
secret_fields = getattr(self.Meta, 'secret_fields', ['secret'])
for field_name in secret_fields:
if field_name in ret:
ret[field_name] = '<REDACTED>'
return ret
class SecretReadableMixin(serializers.Serializer):
"""加密字段 (EncryptedField) 可读性"""

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1636,7 +1636,8 @@
"setVariable": "Set variable",
"userId": "User ID",
"userName": "User name",
"AccountSecretReadDisabled": "Account secret reading has been disabled by administrator",
"AccessToken": "Access tokens",
"AccessTokenTip": "Access Token is a temporary credential generated through the OAuth2 (Authorization Code Grant) flow using the JumpServer client, which is used to access protected resources.",
"Revoke": "Revoke"
}
}

View File

@@ -8,6 +8,8 @@
"AccessDistribution": "Distribución de visitas",
"AccessIP": "Lista blanca de IP",
"AccessKey": "Clave de acceso",
"AccessToken": "Token de acceso",
"AccessTokenTip": "El token de acceso es un certificado temporal generado a través del cliente JumpServer utilizando el flujo de OAuth2 (autorización por código) para acceder a recursos protegidos.",
"Account": "Información de la cuenta",
"AccountActivities": "Actividad de la cuenta",
"AccountAndPasswordChangeRank": "Clasificación de cambio de contraseña de cuenta",
@@ -44,6 +46,7 @@
"AccountPushUpdate": "Actualizar la notificación de la cuenta",
"AccountReport": "Informe de cuentas",
"AccountResult": "Cambio de contraseña de cuenta exitoso/fallido",
"AccountSecretReadDisabled": "La función de lectura de nombre de usuario y contraseña ha sido desactivada por el administrador",
"AccountSelectHelpText": "La lista de cuentas agrega el nombre de usuario de la cuenta. Tipo de contraseña.",
"AccountSessions": "Sesión de cuenta",
"AccountStatisticsReport": "Informe estadístico de cuentas",
@@ -279,6 +282,7 @@
"CACertificate": "Certificado CA",
"CAS": "CAS",
"CMPP2": "CMPP v2.0",
"CTYun": "Tianyi Cloud",
"CTYunPrivate": "eCloud Nube Privada",
"CalculationResults": "Error en la expresión cron",
"CallRecords": "Registro de llamadas",
@@ -1176,6 +1180,8 @@
"RetrySelected": "Reintentar selección",
"Review": "Revisión",
"Reviewer": "Aprobador",
"Revoke": "Revocar",
"Risk": "Riesgo",
"RiskDetection": "Detección de riesgos",
"RiskDetectionDetail": "Detalles de detección de riesgos",
"RiskyAccount": "Cuenta de riesgo",
@@ -1639,6 +1645,7 @@
"overwriteProtocolsAndPortsMsg": "Esta acción reemplazará todos los protocolos y puertos, ¿continuar?",
"pleaseSelectAssets": "Por favor, seleccione un activo.",
"removeWarningMsg": "¿Está seguro de que desea eliminar?",
"selectFiles": "Se ha seleccionado el archivo número {number}",
"selectedAssets": "Activos seleccionados",
"setVariable": "configurar parámetros",
"userId": "ID de usuario",

View File

@@ -8,6 +8,8 @@
"AccessDistribution": "アクセス分布",
"AccessIP": "IP ホワイトリスト",
"AccessKey": "アクセスキー",
"AccessToken": "アクセス・トークン",
"AccessTokenTip": "アクセス・トークンは、JumpServer クライアントを通じて OAuth2Authorization Code Grantフローを使用して生成される一時的な証明書であり、保護されたリソースへのアクセスに使用されます。",
"Account": "アカウント情報",
"AccountActivities": "アカウント活動",
"AccountAmount": "アカウント数",
@@ -46,6 +48,7 @@
"AccountPushUpdate": "アカウント更新プッシュ",
"AccountReport": "アカウントレポート",
"AccountResult": "アカウントパスワード変更成功/失敗",
"AccountSecretReadDisabled": "アカウントパスワードの読み取り機能は管理者によって無効になっています。",
"AccountSelectHelpText": "アカウント一覧に追加されている内容は、アカウントのユーザー名",
"AccountSessions": " アカウントセッション ",
"AccountStatisticsReport": "アカウント統計レポート",
@@ -283,6 +286,7 @@
"CACertificate": "CA 証明書",
"CAS": "CAS",
"CMPP2": "CMPP v2.0",
"CTYun": "天翼クラウド",
"CTYunPrivate": "イークラウド・プライベートクラウド",
"CalculationResults": "cron 式のエラー",
"CallRecords": "つうわきろく",
@@ -1181,6 +1185,8 @@
"RetrySelected": "選択したものを再試行",
"Review": "審査",
"Reviewer": "承認者",
"Revoke": "取り消し",
"Risk": "リスク",
"RiskDetection": "リスク検出",
"RiskDetectionDetail": "リスク検出の詳細",
"RiskyAccount": "リスクアカウント",
@@ -1644,6 +1650,7 @@
"overwriteProtocolsAndPortsMsg": "この操作はすべてのプロトコルとポートを上書きしますが、続行してよろしいですか?",
"pleaseSelectAssets": "資産を選択してください",
"removeWarningMsg": "削除してもよろしいですか",
"selectFiles": "{number}ファイルを選択しました。",
"selectedAssets": "選択した資産",
"setVariable": "パラメータ設定",
"userId": "ユーザーID",

View File

@@ -8,6 +8,8 @@
"AccessDistribution": "방문 분포",
"AccessIP": "IP 화이트리스트",
"AccessKey": "액세스 키",
"AccessToken": "접속 토큰",
"AccessTokenTip": "접속 토큰은 JumpServer 클라이언트를 통해 OAuth2(인증 코드 인증) 프로세스를 사용하여 생성된 임시 자격 증명으로, 보호된 리소스에 접근하는 데 사용됩니다.",
"Account": "계정",
"AccountActivities": "계정 활동",
"AccountAndPasswordChangeRank": "계정 비밀번호 변경 순위",
@@ -44,6 +46,7 @@
"AccountPushUpdate": "계정 업데이트 푸시",
"AccountReport": "계정 보고서",
"AccountResult": "계정 비밀번호 변경 성공/실패",
"AccountSecretReadDisabled": "계정 비밀번호 읽기 기능은 관리자가 비활성화하였습니다.",
"AccountSelectHelpText": "계정 목록에 추가하는 것은 계정의 사용자 이름—암호 유형입니다.",
"AccountSessions": "계정 세션",
"AccountStatisticsReport": "계정 통계 보고서",
@@ -279,6 +282,7 @@
"CACertificate": "CA 인증서",
"CAS": "CAS",
"CMPP2": "CMPP v2.0",
"CTYun": "천윳 클라우드",
"CTYunPrivate": "천翼 개인 클라우드",
"CalculationResults": "cron 표현식 오류",
"CallRecords": "호출 기록",
@@ -1176,6 +1180,8 @@
"RetrySelected": "선택한 항목 재시도",
"Review": "검토",
"Reviewer": "승인자",
"Revoke": "취소",
"Risk": "위험",
"RiskDetection": "위험 감지",
"RiskDetectionDetail": "위험 감지 상세 정보",
"RiskyAccount": "위험 계정",
@@ -1639,6 +1645,7 @@
"overwriteProtocolsAndPortsMsg": "이 작업은 모든 프로토콜과 포트를 덮어씌우게 됩니다. 계속하시겠습니까?",
"pleaseSelectAssets": "자산을 선택해 주세요",
"removeWarningMsg": "제거할 것인지 확실합니까?",
"selectFiles": "선택한 파일 {number}개",
"selectedAssets": "선택한 자산",
"setVariable": "설정 매개변수",
"userId": "사용자 ID",

View File

@@ -8,6 +8,8 @@
"AccessDistribution": "Distribuição de Acesso",
"AccessIP": "Lista branca de IP",
"AccessKey": "Chave de Acesso",
"AccessToken": "Token de acesso",
"AccessTokenTip": "O token de acesso é um credencial temporário gerado pelo cliente JumpServer usando o fluxo OAuth2 (autorização por código), utilizado para acessar recursos protegidos.",
"Account": "Informações da conta",
"AccountActivities": "Atividades da conta",
"AccountAndPasswordChangeRank": "Alteração de Senha por Classificação",
@@ -44,6 +46,7 @@
"AccountPushUpdate": " Atualização de notificação de conta ",
"AccountReport": "Relatório de Contas",
"AccountResult": "Alteração de senha da conta bem-sucedida/falhada",
"AccountSecretReadDisabled": "A funcionalidade de leitura de nome de usuário e senha foi desativada pelo administrador",
"AccountSelectHelpText": "A lista de contas inclui o nome de usuário da conta",
"AccountSessions": "Conta de sessão ",
"AccountStatisticsReport": "Relatório de Estatísticas de Contas",
@@ -280,6 +283,7 @@
"CACertificate": " Certificado CA",
"CAS": "CAS",
"CMPP2": "CMPP v2.0",
"CTYun": "Tianyi Cloud",
"CTYunPrivate": " eCloud Nuvem Privada",
"CalculationResults": "Erro de expressão cron",
"CallRecords": "Registro de chamadas",
@@ -1177,6 +1181,8 @@
"RetrySelected": "repetir a seleção",
"Review": "Revisar",
"Reviewer": "Aprovador",
"Revoke": "Revogar",
"Risk": "Risco",
"RiskDetection": " Detecção de risco ",
"RiskDetectionDetail": "Detalhes da Detecção de Risco",
"RiskyAccount": " Conta de risco ",
@@ -1640,6 +1646,7 @@
"overwriteProtocolsAndPortsMsg": "Esta ação substituirá todos os protocolos e portas. Deseja continuar?",
"pleaseSelectAssets": "Por favor, selecione um ativo.",
"removeWarningMsg": "Tem certeza de que deseja remover",
"selectFiles": "Foram selecionados {number} arquivos",
"selectedAssets": "Ativos selecionados",
"setVariable": "Parâmetros de configuração",
"userId": "ID do usuário",

View File

@@ -8,6 +8,8 @@
"AccessDistribution": "Распределение доступа",
"AccessIP": "Белый список IP",
"AccessKey": "Ключ доступа",
"AccessToken": "Токен доступа",
"AccessTokenTip": "Токен доступа создается через клиент JumpServer с использованием процесса OAuth2 (авторизация через код) в качестве временного удостоверения для доступа к защищенным ресурсам.",
"Account": "Информация об УЗ",
"AccountActivities": "Активность учетной записи",
"AccountAndPasswordChangeRank": "Рейтинг изменений паролей и учётных записей",
@@ -44,6 +46,7 @@
"AccountPushUpdate": "Обновление УЗ для публикации",
"AccountReport": "Отчет по УЗ",
"AccountResult": "Успешное или неудачное изменение секрета УЗ",
"AccountSecretReadDisabled": "Функция чтения логина и пароля была отключена администратором",
"AccountSelectHelpText": "В списке учетных записей отображается имя пользователя",
"AccountSessions": "Сессии учетной записи",
"AccountStatisticsReport": "Отчет по учетным записям",
@@ -279,6 +282,7 @@
"CACertificate": "Сертификат ЦС",
"CAS": "CAS",
"CMPP2": "CMPP v2.0",
"CTYun": "Tianyi Cloud",
"CTYunPrivate": "eCloud Private Cloud",
"CalculationResults": "Ошибка в выражении cron",
"CallRecords": "Запись вызовов",
@@ -1176,6 +1180,8 @@
"RetrySelected": "Повторить выбранное",
"Review": "Требовать одобрения",
"Reviewer": "Утверждающий",
"Revoke": "Отмена",
"Risk": "Риск",
"RiskDetection": "Выявление рисков",
"RiskDetectionDetail": "Детали обнаружения риска",
"RiskyAccount": "УЗ с риском",
@@ -1639,6 +1645,7 @@
"overwriteProtocolsAndPortsMsg": "Это действие заменит все протоколы и порты. Продолжить?",
"pleaseSelectAssets": "Пожалуйста, выберите актив",
"removeWarningMsg": "Вы уверены, что хотите удалить",
"selectFiles": "Выбрано {number} файлов",
"selectedAssets": "Выбранные активы",
"setVariable": "Задать переменную",
"userId": "ID пользователя",

View File

@@ -8,6 +8,8 @@
"AccessDistribution": "Phân bố truy cập",
"AccessIP": "Danh sách trắng IP",
"AccessKey": "Khóa truy cập",
"AccessToken": "Mã thông hành",
"AccessTokenTip": "Mã thông hành là giấy chứng nhận tạm thời được tạo ra thông qua quy trình OAuth2 (ủy quyền mã) sử dụng khách hàng JumpServer, dùng để truy cập tài nguyên được bảo vệ.",
"Account": "Tài khoản",
"AccountActivities": "Tài khoản hoạt động",
"AccountAndPasswordChangeRank": "Thay đổi mật khẩu tài khoản xếp hạng",
@@ -44,6 +46,7 @@
"AccountPushUpdate": "Cập nhật thông tin tài khoản",
"AccountReport": "Báo cáo tài khoản",
"AccountResult": "Thay đổi mật khẩu tài khoản thành công/thất bại",
"AccountSecretReadDisabled": "Chức năng đọc tài khoản mật khẩu đã bị quản lý bởi quản trị viên vô hiệu hóa",
"AccountSelectHelpText": "Danh sách tài khoản thêm tên người dùng của tài khoản",
"AccountSessions": "Phiên tài khoản",
"AccountStatisticsReport": "Báo cáo thống kê tài khoản",
@@ -279,6 +282,7 @@
"CACertificate": "Chứng chỉ CA",
"CAS": "CAS",
"CMPP2": "CMPP v2.0",
"CTYun": "Điện toán đám mây Thiên Vân",
"CTYunPrivate": "Đám mây riêng Tianyi",
"CalculationResults": "Biểu thức cron sai",
"CallRecords": "Ghi chép gọi",
@@ -1176,6 +1180,8 @@
"RetrySelected": "Thử lại đã chọn",
"Review": "Xem xét",
"Reviewer": "Người phê duyệt",
"Revoke": "Huỷ bỏ",
"Risk": "Rủi ro",
"RiskDetection": "Phát hiện rủi ro",
"RiskDetectionDetail": "Chi tiết phát hiện rủi ro",
"RiskyAccount": "Tài khoản rủi ro",
@@ -1639,6 +1645,7 @@
"overwriteProtocolsAndPortsMsg": "Hành động này sẽ ghi đè lên tất cả các giao thức và cổng, có tiếp tục không?",
"pleaseSelectAssets": "Vui lòng chọn tài sản.",
"removeWarningMsg": "Bạn có chắc chắn muốn xóa bỏ?",
"selectFiles": "Đã chọn chọn {number} tệp tin",
"selectedAssets": "Tài sản đã chọn",
"setVariable": "Cài đặt tham số",
"userId": "ID người dùng",

View File

@@ -1648,5 +1648,6 @@
"selectFiles": "已选择选择{number}文件",
"AccessToken": "访问令牌",
"AccessTokenTip": "访问令牌是通过 JumpServer 客户端使用 OAuth2授权码授权流程生成的临时凭证用于访问受保护的资源。",
"Revoke": "撤销"
}
"Revoke": "撤销",
"AccountSecretReadDisabled": "账号密码读取功能已被管理员禁用"
}

View File

@@ -8,6 +8,8 @@
"AccessDistribution": "訪問分布",
"AccessIP": "IP 白名單",
"AccessKey": "訪問金鑰",
"AccessToken": "訪問令牌",
"AccessTokenTip": "訪問令牌是透過 JumpServer 客戶端使用 OAuth2授權碼授權流程生成的臨時憑證用於訪問受保護的資源。",
"Account": "雲帳號",
"AccountActivities": "帳號活動",
"AccountAmount": "帳號數量",
@@ -46,6 +48,7 @@
"AccountPushUpdate": "更新帳號推送",
"AccountReport": "帳號報表",
"AccountResult": "帳號改密成功/失敗",
"AccountSecretReadDisabled": "帳號密碼讀取功能已被管理員禁用",
"AccountSelectHelpText": "帳號清單所加入的是使用者名稱",
"AccountSessions": "帳號會話",
"AccountStatisticsReport": "帳號統計報告",
@@ -283,6 +286,7 @@
"CACertificate": "CA 證書",
"CAS": "CAS",
"CMPP2": "CMPP v2.0",
"CTYun": "天翼雲",
"CTYunPrivate": "天翼私有雲",
"CalculationResults": "呼叫記錄",
"CallRecords": "調用記錄",
@@ -1181,6 +1185,8 @@
"RetrySelected": "重新嘗試所選",
"Review": "審查",
"Reviewer": "審批人",
"Revoke": "撤銷",
"Risk": "風險",
"RiskDetection": "風險檢測",
"RiskDetectionDetail": "風險檢測詳情",
"RiskyAccount": "風險帳號",
@@ -1644,6 +1650,7 @@
"overwriteProtocolsAndPortsMsg": "此操作將覆蓋所有協議和端口,是否繼續?",
"pleaseSelectAssets": "請選擇資產",
"removeWarningMsg": "你確定要移除",
"selectFiles": "已選擇{number}文件",
"selectedAssets": "已選資產",
"setVariable": "設置參數",
"userId": "用戶ID",

View File

@@ -288,5 +288,6 @@
"start time": "Start time",
"success": "Success",
"system user": "System user",
"user": "User"
"user": "User",
"tabLimits": "15 tabs are currently open.\nTo ensure system stability, would you like to open Luna in a new browser tab to continue?"
}

View File

@@ -288,5 +288,6 @@
"start time": "Hora de inicio",
"success": "Éxito",
"system user": "Usuario del sistema",
"tabLimits": "Actualmente tienes 15 pestañas abiertas. \n¿Para garantizar la estabilidad del sistema, deberías abrir Luna en una nueva pestaña del navegador para continuar con la operación?",
"user": "Usuario"
}

View File

@@ -288,5 +288,6 @@
"start time": "開始時間",
"success": "成功",
"system user": "システムユーザー",
"tabLimits": "現在、15個のタブが開かれています。システムの安定性を確保するために、新しいブラウザのタブでLunaを開いて操作を続けますか",
"user": "ユーザー"
}

View File

@@ -288,5 +288,6 @@
"start time": "시작 시간",
"success": "성공",
"system user": "시스템 사용자",
"tabLimits": "현재 15개의 탭이 열려 있습니다. 시스템의 안정성을 위해 Luna를 계속 사용하려면 새로운 브라우저 탭에서 열어보시겠습니까?",
"user": "사용자"
}

View File

@@ -288,5 +288,6 @@
"start time": "Hora de início",
"success": " Sucesso",
"system user": "Usuário do Sistema",
"tabLimits": "Atualmente, 15 abas estão abertas. Para garantir a estabilidade do sistema, você deseja abrir o Luna em uma nova aba do navegador para continuar a operação?",
"user": "Usuário"
}

View File

@@ -288,5 +288,6 @@
"start time": "время начала",
"success": "успешно",
"system user": "системный пользователь",
"tabLimits": "В данный момент открыто 15 вкладок. \nЧтобы обеспечить стабильность системы, стоит ли открыть Luna в новой вкладке браузера для продолжения работы?",
"user": "пользователь"
}

View File

@@ -288,5 +288,6 @@
"start time": "Thời gian bắt đầu",
"success": "Thành công",
"system user": "Tên đăng nhập",
"tabLimits": "Hiện tại đã mở 15 tab. \nĐể đảm bảo tính ổn định của hệ thống, có qua tab trình duyệt mới để mở Luna và tiếp tục thao tác không?",
"user": "Người dùng"
}

View File

@@ -288,5 +288,6 @@
"start time": "开始时间",
"success": "成功",
"system user": "系统用户",
"user": "用户"
"user": "用户",
"tabLimits": "当前已打开 15 个标签页。\n为保证系统稳定是否在新的浏览器标签页中打开 Luna 以继续操作?"
}

View File

@@ -289,5 +289,6 @@
"start time": "開始時間",
"success": "成功",
"system user": "系統用戶",
"tabLimits": "當前已打開 15 個標籤頁。 \n為了確保系統穩定是否在新的瀏覽器標籤頁中打開 Luna 以繼續操作?",
"user": "用戶"
}

View File

@@ -13,11 +13,11 @@ import json
import logging
import os
import re
import sys
import types
from importlib import import_module
from urllib.parse import urljoin, urlparse, quote
import sys
import yaml
from django.urls import reverse_lazy
from django.utils.translation import gettext_lazy as _
@@ -575,6 +575,7 @@ class Config(dict):
],
'SECURITY_SERVICE_ACCOUNT_REGISTRATION': 'auto',
'SECURITY_VIEW_AUTH_NEED_MFA': True,
'SECURITY_ACCOUNT_SECRET_READ': True,
'SECURITY_MAX_IDLE_TIME': 30,
'SECURITY_MAX_SESSION_TIME': 24,
'SECURITY_PASSWORD_EXPIRATION_TIME': 9999,
@@ -698,6 +699,7 @@ class Config(dict):
'LIMIT_SUPER_PRIV': False,
# Chat AI
'IS_CUSTOM_MODEL': False,
'CHAT_AI_ENABLED': False,
'CHAT_AI_METHOD': 'api',
'CHAT_AI_EMBED_URL': '',
@@ -706,10 +708,12 @@ class Config(dict):
'GPT_API_KEY': '',
'GPT_PROXY': '',
'GPT_MODEL': 'gpt-4o-mini',
'CUSTOM_GPT_MODEL': 'gpt-4o-mini',
'DEEPSEEK_BASE_URL': '',
'DEEPSEEK_API_KEY': '',
'DEEPSEEK_PROXY': '',
'DEEPSEEK_MODEL': 'deepseek-chat',
'CUSTOM_DEEPSEEK_MODEL': 'deepseek-chat',
'VIRTUAL_APP_ENABLED': False,
'FILE_UPLOAD_SIZE_LIMIT_MB': 200,
@@ -717,11 +721,6 @@ class Config(dict):
'TICKET_APPLY_ASSET_SCOPE': 'all',
'LEAK_PASSWORD_DB_PATH': os.path.join(PROJECT_DIR, 'data', 'system', 'leak_passwords.db'),
# Ansible Receptor
'RECEPTOR_ENABLED': False,
'ANSIBLE_RECEPTOR_GATEWAY_PROXY_HOST': 'jms_celery',
'ANSIBLE_RECEPTOR_TCP_LISTEN_ADDRESS': 'receptor:7521',
'FILE_UPLOAD_TEMP_DIR': None,
'LOKI_LOG_ENABLED': False,

View File

@@ -60,6 +60,7 @@ VERIFY_CODE_TTL = CONFIG.VERIFY_CODE_TTL
SECURITY_MFA_VERIFY_TTL = CONFIG.SECURITY_MFA_VERIFY_TTL
SECURITY_UNCOMMON_USERS_TTL = CONFIG.SECURITY_UNCOMMON_USERS_TTL
SECURITY_VIEW_AUTH_NEED_MFA = CONFIG.SECURITY_VIEW_AUTH_NEED_MFA
SECURITY_ACCOUNT_SECRET_READ = CONFIG.SECURITY_ACCOUNT_SECRET_READ
SECURITY_SERVICE_ACCOUNT_REGISTRATION = CONFIG.SECURITY_SERVICE_ACCOUNT_REGISTRATION
SECURITY_LOGIN_CAPTCHA_ENABLED = CONFIG.SECURITY_LOGIN_CAPTCHA_ENABLED
SECURITY_MFA_IN_LOGIN_PAGE = CONFIG.SECURITY_MFA_IN_LOGIN_PAGE
@@ -238,6 +239,9 @@ LIMIT_SUPER_PRIV = CONFIG.LIMIT_SUPER_PRIV
ASSET_SIZE = 'small'
# Chat AI
IS_CUSTOM_MODEL = CONFIG.IS_CUSTOM_MODEL
CUSTOM_GPT_MODEL = CONFIG.CUSTOM_GPT_MODEL
CUSTOM_DEEPSEEK_MODEL = CONFIG.CUSTOM_DEEPSEEK_MODEL
CHAT_AI_ENABLED = CONFIG.CHAT_AI_ENABLED
CHAT_AI_METHOD = CONFIG.CHAT_AI_METHOD
CHAT_AI_EMBED_URL = CONFIG.CHAT_AI_EMBED_URL
@@ -257,15 +261,10 @@ FILE_UPLOAD_SIZE_LIMIT_MB = CONFIG.FILE_UPLOAD_SIZE_LIMIT_MB
TICKET_APPLY_ASSET_SCOPE = CONFIG.TICKET_APPLY_ASSET_SCOPE
# Ansible Receptor
RECEPTOR_ENABLED = CONFIG.RECEPTOR_ENABLED
ANSIBLE_RECEPTOR_GATEWAY_PROXY_HOST = CONFIG.ANSIBLE_RECEPTOR_GATEWAY_PROXY_HOST
ANSIBLE_RECEPTOR_TCP_LISTEN_ADDRESS = CONFIG.ANSIBLE_RECEPTOR_TCP_LISTEN_ADDRESS
LOKI_LOG_ENABLED = CONFIG.LOKI_LOG_ENABLED
LOKI_BASE_URL = CONFIG.LOKI_BASE_URL
TOOL_USER_ENABLED = CONFIG.TOOL_USER_ENABLED
SUGGESTION_LIMIT = CONFIG.SUGGESTION_LIMIT
MCP_ENABLED = CONFIG.MCP_ENABLED
MCP_ENABLED = CONFIG.MCP_ENABLED

View File

@@ -2,6 +2,7 @@
#
import os
import time
from .base import (
REDIS_SSL_CA, REDIS_SSL_CERT, REDIS_SSL_KEY, REDIS_SSL_REQUIRED, REDIS_USE_SSL,
REDIS_PROTOCOL, REDIS_SENTINEL_SERVICE_NAME, REDIS_SENTINELS, REDIS_SENTINEL_PASSWORD,
@@ -30,8 +31,8 @@ REST_FRAMEWORK = {
),
'DEFAULT_AUTHENTICATION_CLASSES': (
# 'rest_framework.authentication.BasicAuthentication',
'authentication.backends.drf.SignatureAuthentication',
'authentication.backends.drf.ServiceAuthentication',
'authentication.backends.drf.SignatureAuthentication',
'authentication.backends.drf.PrivateTokenAuthentication',
'authentication.backends.drf.AccessTokenAuthentication',
"oauth2_provider.contrib.rest_framework.OAuth2Authentication",

View File

@@ -115,7 +115,11 @@ LOGGING = {
'azure': {
'handlers': ['null'],
'level': 'ERROR'
}
},
'oauth2_provider': {
'handlers': ['console', 'file'],
'level': LOG_LEVEL,
},
}
}

View File

@@ -104,7 +104,7 @@ class ResourceDownload(TemplateView):
OPENSSH_VERSION=v9.4.0.0
TINKER_VERSION=v0.1.6
VIDEO_PLAYER_VERSION=0.6.0
CLIENT_VERSION=4.0.0
CLIENT_VERSION=4.1.0
"""
def get_meta_json(self):

View File

@@ -1,4 +1,3 @@
from django.conf import settings
from django.utils.functional import LazyObject
from ops.ansible import AnsibleNativeRunner
@@ -15,9 +14,7 @@ class _LazyRunnerInterface(LazyObject):
@staticmethod
def make_interface():
runner_type = AnsibleNativeRunner
gateway_host = settings.ANSIBLE_RECEPTOR_GATEWAY_PROXY_HOST \
if settings.ANSIBLE_RECEPTOR_GATEWAY_PROXY_HOST else '127.0.0.1'
return RunnerInterface(runner_type=runner_type, gateway_proxy_host=gateway_host)
return RunnerInterface(runner_type=runner_type, gateway_proxy_host='127.0.0.1')
interface = _LazyRunnerInterface()

View File

@@ -42,16 +42,22 @@ class ChatAITestingAPI(GenericAPIView):
)
tp = config['CHAT_AI_TYPE']
is_custom_model = config['IS_CUSTOM_MODEL']
if tp == ChatAITypeChoices.gpt:
url = config['GPT_BASE_URL']
api_key = config['GPT_API_KEY']
proxy = config['GPT_PROXY']
model = config['GPT_MODEL']
custom_model = config['CUSTOM_GPT_MODEL']
else:
url = config['DEEPSEEK_BASE_URL']
api_key = config['DEEPSEEK_API_KEY']
proxy = config['DEEPSEEK_PROXY']
model = config['DEEPSEEK_MODEL']
custom_model = config['CUSTOM_DEEPSEEK_MODEL']
model = custom_model or model \
if is_custom_model else model
kwargs = {
'base_url': url or None,

View File

@@ -222,4 +222,4 @@ class ClientVersionView(APIView):
permission_classes = (AllowAny,)
def get(self, request, *args, **kwargs):
return Response(['4.0.0'], status=status.HTTP_200_OK)
return Response(['4.0.0', '4.1.0'], status=status.HTTP_200_OK)

View File

@@ -19,11 +19,23 @@ class ChatAITypeChoices(TextChoices):
class GPTModelChoices(TextChoices):
gpt_4o_mini = 'gpt-4o-mini', 'gpt-4o-mini'
gpt_4o = 'gpt-4o', 'gpt-4o'
o3_mini = 'o3-mini', 'o3-mini'
o1_mini = 'o1-mini', 'o1-mini'
o1 = 'o1', 'o1'
# 🚀 Latest flagship dialogue model
GPT_5_2 = 'gpt-5.2', 'gpt-5.2'
GPT_5_2_PRO = 'gpt-5.2-pro', 'gpt-5.2-pro'
GPT_5_1 = 'gpt-5.1', 'gpt-5.1'
GPT_5 = 'gpt-5', 'gpt-5'
# 💡 Lightweight & Cost-Friendly Version
GPT_5_MINI = 'gpt-5-mini', 'gpt-5-mini'
GPT_5_NANO = 'gpt-5-nano', 'gpt-5-nano'
# 🧠 GPT-4 series of dialogues (still supports chat tasks)
GPT_4O = 'gpt-4o', 'gpt-4o'
GPT_4O_MINI = 'gpt-4o-mini', 'gpt-4o-mini'
GPT_4_1 = 'gpt-4.1', 'gpt-4.1'
GPT_4_1_MINI = 'gpt-4.1-mini', 'gpt-4.1-mini'
GPT_4_1_NANO = 'gpt-4.1-nano', 'gpt-4.1-nano'
class DeepSeekModelChoices(TextChoices):

View File

@@ -203,12 +203,16 @@ def get_chatai_data():
'proxy': settings.GPT_PROXY,
'model': settings.GPT_MODEL,
}
custom_model = settings.CUSTOM_GPT_MODEL
if settings.CHAT_AI_TYPE != ChatAITypeChoices.gpt:
data['url'] = settings.DEEPSEEK_BASE_URL
data['api_key'] = settings.DEEPSEEK_API_KEY
data['proxy'] = settings.DEEPSEEK_PROXY
data['model'] = settings.DEEPSEEK_MODEL
custom_model = settings.CUSTOM_DEEPSEEK_MODEL
data['model'] = custom_model or data['model'] \
if settings.IS_CUSTOM_MODEL else data['model']
return data

View File

@@ -150,7 +150,7 @@ class ChatAISettingSerializer(serializers.Serializer):
help_text=_('The proxy server address of the GPT service. For example: http://ip:port')
)
GPT_MODEL = serializers.ChoiceField(
default=GPTModelChoices.gpt_4o_mini, choices=GPTModelChoices.choices,
default=GPTModelChoices.GPT_4_1_MINI, choices=GPTModelChoices.choices,
label=_("GPT Model"), required=False,
)
DEEPSEEK_BASE_URL = serializers.CharField(
@@ -168,6 +168,18 @@ class ChatAISettingSerializer(serializers.Serializer):
default=DeepSeekModelChoices.deepseek_chat, choices=DeepSeekModelChoices.choices,
label=_("DeepSeek Model"), required=False,
)
IS_CUSTOM_MODEL = serializers.BooleanField(
required=False, default=False, label=_("Custom Model"),
help_text=_("Whether to use a custom model")
)
CUSTOM_GPT_MODEL = serializers.CharField(
max_length=256, allow_blank=True,
required=False, label=_('Custom gpt model'),
)
CUSTOM_DEEPSEEK_MODEL = serializers.CharField(
max_length=256, allow_blank=True,
required=False, label=_('Custom DeepSeek model'),
)
class TicketSettingSerializer(serializers.Serializer):

View File

@@ -21,6 +21,7 @@ class PrivateSettingSerializer(PublicSettingSerializer):
TICKET_AUTHORIZE_DEFAULT_TIME_UNIT = serializers.CharField()
AUTH_LDAP_SYNC_ORG_IDS = serializers.ListField()
SECURITY_MAX_IDLE_TIME = serializers.IntegerField()
SECURITY_ACCOUNT_SECRET_READ = serializers.BooleanField()
SECURITY_VIEW_AUTH_NEED_MFA = serializers.BooleanField()
SECURITY_MFA_AUTH = serializers.IntegerField()
SECURITY_MFA_VERIFY_TTL = serializers.IntegerField()

View File

@@ -18,40 +18,28 @@
}
/* 重定向中的样式 */
.redirecting-container {
display: none;
}
.redirecting-container.show {
display: block;
}
.confirm-container {
transition: opacity 0.3s ease-out;
}
.confirm-container.hide {
display: none;
}
</style>
<div>
<!-- 确认内容 -->
<div class="confirm-container" id="confirmContainer">
<p>
<div class="alert {% if error %} alert-danger {% else %} alert-info {% endif %}" id="messages">
{% trans 'You are about to be redirected to an external website.' %}
<br/>
<br/>
{% trans 'Please confirm that you trust this link: ' %}
<br/>
<br/>
<a class="target-url" href="javascript:void(0)" onclick="handleRedirect(event)">{{ target_url }}</a>
</div>
<div class="alert {% if error %} alert-danger {% else %} alert-info {% endif %}" id="messages">
{% trans 'You are about to be redirected to an external website.' %}
<br/>
<br/>
{% trans 'Please confirm that you trust this link: ' %}
<br/>
<br/>
<a class="target-url" href="javascript:void(0)" onclick="handleRedirect(event)">{{ target_url }}</a>
</div>
</p>
<div class="row">
<div class="col-sm-3">
<a href="/" class="btn btn-default block full-width m-b">
<a id="backBtn" href="/" class="btn btn-default block full-width m-b">
{% trans 'Back' %}
</a>
</div>
@@ -62,45 +50,60 @@
</div>
</div>
</div>
<!-- 重定向中内容 -->
<div class="redirecting-container" id="redirectingContainer">
<p>
<div class="alert alert-info" id="messages">
{% trans 'Redirecting you to the Desktop App ( JumpServer Client )' %}
<br/>
<br/>
{% trans 'You can safely close this window and return to the application.' %}
</div>
</p>
</div>
</div>
<script>
const targetUrl = '{{ target_url }}';
// 判断是否是 jms:// 协议
function isJmsProtocol(url) {
return url.toLowerCase().startsWith('jms://');
if (sessionStorage.getItem('page_refreshed')) {
// 用户刷新了页面清除标记并跳转到首页
sessionStorage.removeItem('page_refreshed');
window.location.href = '/';
} else {
// 第一次加载设置标记
window.addEventListener('beforeunload', function() {
sessionStorage.setItem('page_refreshed', 'true');
});
}
const targetUrl = '{{ target_url }}';
let countdownTimer = null;
let countdownSeconds = 30;
let startedCountdown = false;
function handleRedirect(event) {
// 如果有 event阻止默认行为
if (event) {
event.preventDefault();
}
if (isJmsProtocol(targetUrl)) {
// 隐藏确认内容
document.getElementById('confirmContainer').classList.add('hide');
// 显示重定向中
document.getElementById('redirectingContainer').classList.add('show');
}
// 延迟后执行跳转让用户看到加载动画
setTimeout(() => {
window.location.href = targetUrl;
if (targetUrl.startsWith('jms://')) {
if (startedCountdown) {
// 已经开始倒计时直接跳转但不再重置倒计时
window.location.href = targetUrl;
return;
}
startedCountdown = true;
countdownSeconds = 30;
window.location.href = targetUrl;
const backBtn = document.getElementById('backBtn');
if (backBtn) {
const backButtonContent = backBtn.textContent;
backBtn.textContent = `${backButtonContent} (${countdownSeconds})`;
countdownTimer = setInterval(() => {
countdownSeconds--;
if (countdownSeconds > 0) {
backBtn.textContent = `${backButtonContent} (${countdownSeconds})`;
} else {
backBtn.textContent = `${backButtonContent}`;
clearInterval(countdownTimer);
countdownTimer = null;
window.location.href = '/';
}
}, 1000);
}
} else {
window.location.href = targetUrl;
}
}, 100);
}
</script>

View File

@@ -19,10 +19,3 @@ fi
echo "4. For Apple processor"
LDFLAGS="-L$(brew --prefix freetds)/lib -L$(brew --prefix openssl@1.1)/lib" CFLAGS="-I$(brew --prefix freetds)/include" pip install $(grep 'pymssql' requirements.txt)
export PKG_CONFIG_PATH="/opt/homebrew/opt/mysql-client/lib/pkgconfig"
echo "5. Install Ansible Receptor"
export RECEPTOR_VERSION=v1.4.5
export ARCH=`arch`
wget -O ${TMPDIR}receptor.tar.gz https://github.com/ansible/receptor/releases/download/${RECEPTOR_VERSION}/receptor_${RECEPTOR_VERSION/v/}_darwin_${ARCH}.tar.gz
tar -xf ${TMPDIR}receptor.tar.gz -C /opt/homebrew/bin/