9c6c477f55
CI/CD Pipeline / Validate Code Quality And Tests (pull_request) Failing after 2m22s
CI/CD Pipeline / Unit Tests (pull_request) Failing after 2m24s
CI/CD Pipeline / Integration Tests (pull_request) Failing after 37s
CI/CD Pipeline / Frontend Lint (pull_request) Successful in 4m3s
CI/CD Pipeline / Build & Push Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Build Production Runtime Images (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
P0 关键修复: - P0-1: 注册接口添加 RateLimitMiddleware 限流保护 - P0-3: /metrics 端点添加 JWT 认证(移除匿名访问) - P0-4: 修复 Celery 任务名冲突(generation_task vs generate_video) - P1-5: JWT logout token 黑名单机制 P1 修复: - P1-1: forgot_password 硬编码 localhost → 使用 settings.APP_BASE_URL - P1-2: generation.py 直接创建 DB 连接 → 使用依赖注入 - P1-6: Image.open() 未关闭 → 统一使用 with 语句 - P1-7: 订阅续费事务修复 P2 代码质量: - P2-1: 修复 EditingMode 枚举重复定义 → 统一引用 shared 包 - P2-2: 修复 SMTP_FRON_NAME → SMTP_FROM_NAME 拼写 - P2-3: UserModel subscription_quota 类型统一为 float - P2-4: .env.production DATABASE_MAX_OVERFLOW 30 → 10 - 清理 15 处 except:pass(保留 2 处有注释说明的) - 禁用 SVG 上传(XSS 风险) - 删除 decode_token_unsafe() 不安全函数 - 简化 /ready 端点 - 删除 8 处死代码、10 个空文件/模块 - 合并 3 对 100% 重复函数 - 对齐 6 个废弃环境变量 v2 修复(代码审查后): - 修复密码重置路由路径: /password/forgot → /forgot-password, /password/reset → /reset-password(与前端 API 对齐) - 合并 _check_project_access: asset_libraries.py 和 edit_plans.py 中的重复函数统一到 _helpers.py(含空字符串守卫 + 中文错误信息) - 顺手修复: HTTPException 统一从 fastapi 导入(替换 starlette 导入) - OSS_ENDPOINT 拼写修复拆分为单独 PR,本 PR 不包含
205 lines
5.8 KiB
Python
Executable File
205 lines
5.8 KiB
Python
Executable File
"""
|
|
用户注册 Use Case
|
|
"""
|
|
|
|
import secrets
|
|
from datetime import datetime, timezone
|
|
from typing import Optional
|
|
from uuid import uuid4
|
|
|
|
from packages.adapters.smtp import get_email_service
|
|
from packages.application.auth.password_hasher import password_hasher, password_validator
|
|
from packages.domain.entities import User
|
|
|
|
|
|
class RegisterUserRequest:
|
|
"""注册请求"""
|
|
|
|
def __init__(
|
|
self,
|
|
email: str,
|
|
password: str,
|
|
username: str,
|
|
display_name: str,
|
|
):
|
|
self.email = email.strip().lower()
|
|
self.password = password
|
|
self.username = username.strip()
|
|
self.display_name = display_name.strip()
|
|
|
|
|
|
class RegisterUserResponse:
|
|
"""注册响应"""
|
|
|
|
def __init__(
|
|
self,
|
|
user_id: str,
|
|
email: str,
|
|
username: str,
|
|
display_name: str,
|
|
email_verification_sent: bool,
|
|
):
|
|
self.user_id = user_id
|
|
self.email = email
|
|
self.username = username
|
|
self.display_name = display_name
|
|
self.email_verification_sent = email_verification_sent
|
|
|
|
|
|
class RegisterUserUseCase:
|
|
"""用户注册用例"""
|
|
|
|
def __init__(
|
|
self,
|
|
user_repository,
|
|
base_url: str,
|
|
email_service=None,
|
|
):
|
|
"""
|
|
初始化注册用例
|
|
|
|
Args:
|
|
user_repository: 用户仓储
|
|
base_url: 应用基础 URL(用于生成验证链接),必须由调用方传入
|
|
"""
|
|
self.user_repository = user_repository
|
|
self.base_url = base_url
|
|
self.email_service = email_service or get_email_service()
|
|
|
|
def execute(self, request: RegisterUserRequest) -> tuple[Optional[RegisterUserResponse], Optional[str]]:
|
|
"""
|
|
执行注册
|
|
|
|
Args:
|
|
request: 注册请求
|
|
|
|
Returns:
|
|
(注册响应, 错误信息)
|
|
"""
|
|
try:
|
|
# 1. 验证输入
|
|
if not request.email:
|
|
return None, "Email is required"
|
|
|
|
if not request.username:
|
|
return None, "Username is required"
|
|
|
|
if not request.display_name:
|
|
return None, "Display name is required"
|
|
|
|
# 2. 验证密码强度
|
|
valid, error = password_validator.validate(request.password)
|
|
if not valid:
|
|
return None, error
|
|
|
|
# 3. 检查邮箱是否已存在
|
|
existing_user = self.user_repository.find_by_email(request.email)
|
|
if existing_user:
|
|
return None, "Email already registered"
|
|
|
|
# 4. 检查用户名是否已存在
|
|
existing_username = self.user_repository.find_by_username(request.username)
|
|
if existing_username:
|
|
return None, "Username already taken"
|
|
|
|
# 5. 哈希密码
|
|
hashed_password = password_hasher.hash_password(request.password)
|
|
|
|
# 6. 生成邮箱验证令牌
|
|
verification_token = secrets.token_urlsafe(32)
|
|
|
|
# 7. 创建用户
|
|
user = User(
|
|
id=uuid4().hex,
|
|
email=request.email,
|
|
username=request.username,
|
|
display_name=request.display_name,
|
|
password_hash=hashed_password,
|
|
email_verified=False,
|
|
email_verification_token=verification_token,
|
|
created_at=datetime.now(timezone.utc),
|
|
)
|
|
|
|
# 8. 保存用户
|
|
self.user_repository.save(user)
|
|
|
|
# 9. 发送验证邮件
|
|
verification_url = f"{self.base_url}/verify-email?token={verification_token}"
|
|
email_sent = False
|
|
|
|
try:
|
|
success, error = self.email_service.send_verification_email(
|
|
to_email=user.email,
|
|
username=user.username,
|
|
verification_url=verification_url,
|
|
)
|
|
email_sent = success
|
|
|
|
if not success:
|
|
print(f"Failed to send verification email: {error}")
|
|
except Exception as e:
|
|
print(f"Email service error: {e}")
|
|
|
|
# 10. 返回响应(即使邮件发送失败,用户也已创建)
|
|
return (
|
|
RegisterUserResponse(
|
|
user_id=user.id,
|
|
email=user.email,
|
|
username=user.username,
|
|
display_name=user.display_name,
|
|
email_verification_sent=email_sent,
|
|
),
|
|
None,
|
|
)
|
|
|
|
except Exception as e:
|
|
return None, f"Registration failed: {str(e)}"
|
|
|
|
|
|
class VerifyEmailRequest:
|
|
"""邮箱验证请求"""
|
|
|
|
def __init__(self, token: str):
|
|
self.token = token
|
|
|
|
|
|
class VerifyEmailUseCase:
|
|
"""邮箱验证用例"""
|
|
|
|
def __init__(self, user_repository):
|
|
self.user_repository = user_repository
|
|
|
|
def execute(self, request: VerifyEmailRequest) -> tuple[bool, Optional[str]]:
|
|
"""
|
|
执行邮箱验证
|
|
|
|
Args:
|
|
request: 验证请求
|
|
|
|
Returns:
|
|
(是否成功, 错误信息)
|
|
"""
|
|
try:
|
|
if not request.token:
|
|
return False, "Verification token is required"
|
|
|
|
# 查找用户
|
|
user = self.user_repository.find_by_verification_token(request.token)
|
|
if not user:
|
|
return False, "Invalid or expired verification token"
|
|
|
|
# 检查是否已验证
|
|
if user.email_verified:
|
|
return True, None # 已验证,返回成功
|
|
|
|
# 更新用户状态
|
|
user.email_verified = True
|
|
user.email_verification_token = None # 清空令牌
|
|
|
|
self.user_repository.save(user)
|
|
|
|
return True, None
|
|
|
|
except Exception as e:
|
|
return False, f"Email verification failed: {str(e)}"
|