1
0
forked from erp-dev/erp

feat: big version, added tasks for backup_database and stock change, added health check api, approve sse (support channel via merchant)

This commit is contained in:
2025-11-26 21:49:42 +08:00
parent 6bf0465d05
commit a9c75a13fa
26 changed files with 7158 additions and 216 deletions

117
sse/auth_utils.py Normal file
View File

@@ -0,0 +1,117 @@
"""
SSE认证工具模块
提供统一的认证和商户验证功能用于SSE连接端点和其他需要认证的地方
"""
from django.http import HttpResponseForbidden
from rest_framework.authentication import get_authorization_header
from rest_framework_simplejwt.authentication import JWTAuthentication
from rest_framework_simplejwt.exceptions import InvalidToken
from rest_framework.exceptions import AuthenticationFailed
import logging
logger = logging.getLogger(__name__)
def get_user_merchant_id(request):
"""获取用户所属商户ID"""
try:
# 检查用户是否已认证
if not request.user.is_authenticated:
return None
# 获取商户ID
return request.user.employee.merchant_id
except (AttributeError, AttributeError):
return None
def authenticate_sse_request(request):
"""
对SSE连接请求进行认证
参数:
- request: Django HTTP请求对象
返回:
- tuple: (user, merchant_id) 认证成功返回用户和商户ID
- None: 认证失败返回None
"""
jwt_authenticator = JWTAuthentication()
try:
# 获取Authorization头
auth_header = get_authorization_header(request).split()
if not auth_header or auth_header[0].lower() != b'bearer':
logger.warning("SSE认证失败: 缺少Bearer token")
return None
if len(auth_header) < 2:
logger.warning("SSE认证失败: Bearer token格式错误")
return None
token = auth_header[1].decode('utf-8')
# 验证token
validated_token = jwt_authenticator.get_validated_token(token)
user = jwt_authenticator.get_user(validated_token)
# 检查用户是否有关联的员工和商户
try:
merchant_id = user.employee.merchant_id
if not merchant_id:
logger.warning(f"用户 {user.username} 无关联商户")
return None
return user, merchant_id
except AttributeError:
logger.warning(f"用户 {user.username} 无关联员工")
return None
except (IndexError, InvalidToken, AuthenticationFailed) as e:
logger.warning(f"SSE认证失败: {str(e)}")
return None
def require_sse_authentication(view_func):
"""
装饰器要求SSE连接认证
用于SSE连接端点的认证如果认证失败则返回403响应
OPTIONS请求跳过认证
"""
def wrapper(request, *args, **kwargs):
# OPTIONS请求跳过认证
if request.method == 'OPTIONS':
return view_func(request, *args, **kwargs)
auth_result = authenticate_sse_request(request)
if not auth_result:
return HttpResponseForbidden("Authentication failed or user has no associated merchant")
user, merchant_id = auth_result
request.user = user
request.merchant_id = merchant_id
return view_func(request, *args, **kwargs)
return wrapper
def validate_sse_request(request):
"""
验证SSE请求的认证状态
返回:
- tuple: (is_valid, error_response)
- is_valid: bool 表示请求是否有效
- error_response: HttpResponse 如果无效则返回错误响应否则为None
"""
auth_result = authenticate_sse_request(request)
if not auth_result:
return False, HttpResponseForbidden("认证失败或用户无关联商户")
user, merchant_id = auth_result
request.user = user
request.merchant_id = merchant_id
return True, None