feat(user system): modifies the email address.
This commit is contained in:
@@ -2,15 +2,23 @@ from fastapi import APIRouter, Depends
|
|||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
|
from app.core.error_codes import BizCode
|
||||||
|
from app.core.exceptions import BusinessException
|
||||||
from app.db import get_db
|
from app.db import get_db
|
||||||
from app.dependencies import get_current_user, get_current_superuser
|
from app.dependencies import get_current_user, get_current_superuser
|
||||||
from app.models.user_model import User
|
from app.models.user_model import User
|
||||||
from app.schemas import user_schema
|
from app.schemas import user_schema
|
||||||
from app.schemas.user_schema import ChangePasswordRequest, AdminChangePasswordRequest
|
from app.schemas.user_schema import (
|
||||||
|
ChangePasswordRequest,
|
||||||
|
AdminChangePasswordRequest,
|
||||||
|
SendEmailCodeRequest,
|
||||||
|
VerifyEmailCodeRequest,
|
||||||
|
VerifyPasswordRequest)
|
||||||
from app.schemas.response_schema import ApiResponse
|
from app.schemas.response_schema import ApiResponse
|
||||||
from app.services import user_service
|
from app.services import user_service
|
||||||
from app.core.logging_config import get_api_logger
|
from app.core.logging_config import get_api_logger
|
||||||
from app.core.response_utils import success
|
from app.core.response_utils import success
|
||||||
|
from app.core.security import verify_password
|
||||||
|
|
||||||
# 获取API专用日志器
|
# 获取API专用日志器
|
||||||
api_logger = get_api_logger()
|
api_logger = get_api_logger()
|
||||||
@@ -120,6 +128,7 @@ def get_tenant_superusers(
|
|||||||
return success(data=superusers_schema, msg="租户超管列表获取成功")
|
return success(data=superusers_schema, msg="租户超管列表获取成功")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@router.get("/{user_id}", response_model=ApiResponse)
|
@router.get("/{user_id}", response_model=ApiResponse)
|
||||||
def get_user_info_by_id(
|
def get_user_info_by_id(
|
||||||
user_id: uuid.UUID,
|
user_id: uuid.UUID,
|
||||||
@@ -181,3 +190,53 @@ async def admin_change_password(
|
|||||||
else:
|
else:
|
||||||
api_logger.info(f"管理员密码重置成功: 用户 {request.user_id}, 随机密码已生成")
|
api_logger.info(f"管理员密码重置成功: 用户 {request.user_id}, 随机密码已生成")
|
||||||
return success(data=generated_password, msg="密码重置成功")
|
return success(data=generated_password, msg="密码重置成功")
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/verify_pwd", response_model=ApiResponse)
|
||||||
|
def verify_pwd(
|
||||||
|
request: VerifyPasswordRequest,
|
||||||
|
current_user: User = Depends(get_current_user),
|
||||||
|
):
|
||||||
|
"""验证当前用户密码"""
|
||||||
|
api_logger.info(f"用户验证密码请求: {current_user.username}")
|
||||||
|
|
||||||
|
is_valid = verify_password(request.password, current_user.hashed_password)
|
||||||
|
api_logger.info(f"用户密码验证结果: {current_user.username}, valid={is_valid}")
|
||||||
|
if not is_valid:
|
||||||
|
raise BusinessException("密码验证失败", code=BizCode.VALIDATION_FAILED)
|
||||||
|
return success(data={"valid": is_valid}, msg="验证完成")
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/send-email-code", response_model=ApiResponse)
|
||||||
|
async def send_email_code(
|
||||||
|
request: SendEmailCodeRequest,
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
current_user: User = Depends(get_current_user),
|
||||||
|
):
|
||||||
|
"""发送邮箱验证码"""
|
||||||
|
api_logger.info(f"用户请求发送邮箱验证码: {current_user.username}, email={request.email}")
|
||||||
|
|
||||||
|
await user_service.send_email_code_method(db=db, email=request.email, user_id=current_user.id)
|
||||||
|
|
||||||
|
api_logger.info(f"邮箱验证码已发送: {current_user.username}")
|
||||||
|
return success(msg="验证码已发送到您的邮箱,请查收")
|
||||||
|
|
||||||
|
|
||||||
|
@router.put("/change-email", response_model=ApiResponse)
|
||||||
|
async def change_email(
|
||||||
|
request: VerifyEmailCodeRequest,
|
||||||
|
db: Session = Depends(get_db),
|
||||||
|
current_user: User = Depends(get_current_user),
|
||||||
|
):
|
||||||
|
"""验证验证码并修改邮箱"""
|
||||||
|
api_logger.info(f"用户修改邮箱: {current_user.username}, new_email={request.new_email}")
|
||||||
|
|
||||||
|
await user_service.verify_and_change_email(
|
||||||
|
db=db,
|
||||||
|
user_id=current_user.id,
|
||||||
|
new_email=request.new_email,
|
||||||
|
code=request.code
|
||||||
|
)
|
||||||
|
|
||||||
|
api_logger.info(f"用户邮箱修改成功: {current_user.username}")
|
||||||
|
return success(msg="邮箱修改成功")
|
||||||
|
|||||||
@@ -193,6 +193,12 @@ class Settings:
|
|||||||
CELERY_BROKER: int = int(os.getenv("CELERY_BROKER", "1"))
|
CELERY_BROKER: int = int(os.getenv("CELERY_BROKER", "1"))
|
||||||
CELERY_BACKEND: int = int(os.getenv("CELERY_BACKEND", "2"))
|
CELERY_BACKEND: int = int(os.getenv("CELERY_BACKEND", "2"))
|
||||||
|
|
||||||
|
# SMTP Email Configuration
|
||||||
|
SMTP_SERVER: str = os.getenv("SMTP_SERVER", "smtp.gmail.com")
|
||||||
|
SMTP_PORT: int = int(os.getenv("SMTP_PORT", "587"))
|
||||||
|
SMTP_USER: str = os.getenv("SMTP_USER", "")
|
||||||
|
SMTP_PASSWORD: str = os.getenv("SMTP_PASSWORD", "")
|
||||||
|
|
||||||
REFLECTION_INTERVAL_SECONDS: float = float(os.getenv("REFLECTION_INTERVAL_SECONDS", "300"))
|
REFLECTION_INTERVAL_SECONDS: float = float(os.getenv("REFLECTION_INTERVAL_SECONDS", "300"))
|
||||||
HEALTH_CHECK_SECONDS: float = float(os.getenv("HEALTH_CHECK_SECONDS", "600"))
|
HEALTH_CHECK_SECONDS: float = float(os.getenv("HEALTH_CHECK_SECONDS", "600"))
|
||||||
MEMORY_INCREMENT_INTERVAL_HOURS: float = float(os.getenv("MEMORY_INCREMENT_INTERVAL_HOURS", "24"))
|
MEMORY_INCREMENT_INTERVAL_HOURS: float = float(os.getenv("MEMORY_INCREMENT_INTERVAL_HOURS", "24"))
|
||||||
|
|||||||
@@ -36,6 +36,28 @@ class AdminChangePasswordRequest(BaseModel):
|
|||||||
new_password: Optional[str] = Field(None, min_length=6, description="新密码,至少6位。如果不提供则自动生成随机密码")
|
new_password: Optional[str] = Field(None, min_length=6, description="新密码,至少6位。如果不提供则自动生成随机密码")
|
||||||
|
|
||||||
|
|
||||||
|
class ChangeEmailRequest(BaseModel):
|
||||||
|
"""修改邮箱请求"""
|
||||||
|
password: str = Field(..., description="当前密码")
|
||||||
|
new_email: EmailStr = Field(..., description="新邮箱地址")
|
||||||
|
|
||||||
|
|
||||||
|
class SendEmailCodeRequest(BaseModel):
|
||||||
|
"""发送邮箱验证码请求"""
|
||||||
|
email: EmailStr = Field(..., description="邮箱地址")
|
||||||
|
|
||||||
|
|
||||||
|
class VerifyEmailCodeRequest(BaseModel):
|
||||||
|
"""验证邮箱验证码并修改邮箱请求"""
|
||||||
|
new_email: EmailStr = Field(..., description="新邮箱地址")
|
||||||
|
code: str = Field(..., min_length=6, max_length=6, description="验证码")
|
||||||
|
|
||||||
|
|
||||||
|
class VerifyPasswordRequest(BaseModel):
|
||||||
|
"""验证密码请求"""
|
||||||
|
password: str = Field(..., description="密码")
|
||||||
|
|
||||||
|
|
||||||
class ChangePasswordResponse(BaseModel):
|
class ChangePasswordResponse(BaseModel):
|
||||||
"""修改密码响应"""
|
"""修改密码响应"""
|
||||||
message: str
|
message: str
|
||||||
|
|||||||
88
api/app/services/email_service.py
Normal file
88
api/app/services/email_service.py
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
import smtplib
|
||||||
|
import re
|
||||||
|
import asyncio
|
||||||
|
from email.mime.text import MIMEText
|
||||||
|
from email.mime.multipart import MIMEMultipart
|
||||||
|
from email.header import Header
|
||||||
|
from email.utils import formataddr
|
||||||
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
|
|
||||||
|
from app.core.config import settings
|
||||||
|
from app.core.error_codes import BizCode
|
||||||
|
from app.core.exceptions import BusinessException
|
||||||
|
from app.core.logging_config import get_business_logger
|
||||||
|
|
||||||
|
business_logger = get_business_logger()
|
||||||
|
|
||||||
|
|
||||||
|
def _send_email_sync(to_email: str, subject: str, html_content: str, text_content: str = None):
|
||||||
|
"""同步发送邮件"""
|
||||||
|
smtp_server = settings.SMTP_SERVER
|
||||||
|
smtp_port = settings.SMTP_PORT
|
||||||
|
smtp_user = settings.SMTP_USER
|
||||||
|
smtp_password = settings.SMTP_PASSWORD
|
||||||
|
|
||||||
|
if not smtp_server or not smtp_user or not smtp_password:
|
||||||
|
raise BusinessException("邮件服务未配置", code=BizCode.SERVICE_UNAVAILABLE)
|
||||||
|
|
||||||
|
msg = MIMEMultipart('alternative')
|
||||||
|
msg['Subject'] = Header(subject, "utf-8")
|
||||||
|
from_name = "MemoryBear系统"
|
||||||
|
msg['From'] = formataddr((Header(from_name, 'utf-8').encode(), smtp_user))
|
||||||
|
msg['To'] = Header(to_email, "utf-8")
|
||||||
|
|
||||||
|
if not text_content:
|
||||||
|
text_content = html_content.replace('<br>', '\n').replace('<p>', '\n').replace('</p>', '\n')
|
||||||
|
text_content = re.sub(r'<.*?>', '', text_content)
|
||||||
|
text_part = MIMEText(text_content, 'plain', 'utf-8')
|
||||||
|
msg.attach(text_part)
|
||||||
|
|
||||||
|
html_part = MIMEText(html_content, 'html', 'utf-8')
|
||||||
|
msg.attach(html_part)
|
||||||
|
|
||||||
|
if smtp_port == 465:
|
||||||
|
with smtplib.SMTP_SSL(smtp_server, smtp_port, timeout=10) as server:
|
||||||
|
server.login(smtp_user, smtp_password)
|
||||||
|
server.send_message(msg)
|
||||||
|
else:
|
||||||
|
with smtplib.SMTP(smtp_server, smtp_port, timeout=10) as server:
|
||||||
|
server.starttls()
|
||||||
|
server.login(smtp_user, smtp_password)
|
||||||
|
server.send_message(msg)
|
||||||
|
|
||||||
|
|
||||||
|
async def send_email(to_email: str, subject: str, html_content: str, text_content: str = None):
|
||||||
|
"""异步发送邮件"""
|
||||||
|
to_email = to_email.strip()
|
||||||
|
if not to_email or not re.match(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$', to_email):
|
||||||
|
err_msg = f"收件人邮箱格式无效: {to_email}"
|
||||||
|
business_logger.error(err_msg)
|
||||||
|
raise BusinessException(err_msg, code=BizCode.INVALID_PARAMETER)
|
||||||
|
|
||||||
|
try:
|
||||||
|
loop = asyncio.get_event_loop()
|
||||||
|
with ThreadPoolExecutor() as executor:
|
||||||
|
await loop.run_in_executor(
|
||||||
|
executor,
|
||||||
|
_send_email_sync,
|
||||||
|
to_email,
|
||||||
|
subject,
|
||||||
|
html_content,
|
||||||
|
text_content
|
||||||
|
)
|
||||||
|
business_logger.info(f"邮件发送成功: {to_email}")
|
||||||
|
except smtplib.SMTPAuthenticationError:
|
||||||
|
err_msg = "SMTP认证失败,请检查SMTP账号/密码是否正确"
|
||||||
|
business_logger.error(f"邮件发送失败: {to_email} - {err_msg}")
|
||||||
|
raise BusinessException(err_msg, code=BizCode.UNAUTHORIZED)
|
||||||
|
except smtplib.SMTPConnectError:
|
||||||
|
err_msg = "SMTP服务器连接失败,请检查服务器地址/端口是否正确"
|
||||||
|
business_logger.error(f"邮件发送失败: {to_email} - {err_msg}")
|
||||||
|
raise BusinessException(err_msg, code=BizCode.SERVICE_UNAVAILABLE)
|
||||||
|
except TimeoutError:
|
||||||
|
err_msg = "邮件发送超时,请检查SMTP服务器配置"
|
||||||
|
business_logger.error(f"邮件发送失败: {to_email} - {err_msg}")
|
||||||
|
raise BusinessException(err_msg, code=BizCode.BAD_REQUEST)
|
||||||
|
except Exception as e:
|
||||||
|
business_logger.error(f"邮件发送失败: {to_email} - {str(e)}")
|
||||||
|
raise BusinessException(f"邮件发送失败: {str(e)}", code=BizCode.SERVICE_UNAVAILABLE)
|
||||||
@@ -1,13 +1,18 @@
|
|||||||
import datetime
|
import datetime
|
||||||
|
import json
|
||||||
import secrets
|
import secrets
|
||||||
import string
|
import string
|
||||||
|
|
||||||
|
from pydantic import EmailStr
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
|
from app.aioRedis import aio_redis_set, aio_redis_get, aio_redis_delete
|
||||||
from app.models.user_model import User
|
from app.models.user_model import User
|
||||||
from app.repositories import user_repository
|
from app.repositories import user_repository
|
||||||
from app.schemas.user_schema import UserCreate
|
from app.schemas.user_schema import UserCreate
|
||||||
from app.schemas.tenant_schema import TenantCreate
|
from app.schemas.tenant_schema import TenantCreate
|
||||||
|
from app.services.email_service import send_email
|
||||||
from app.services.tenant_service import TenantService
|
from app.services.tenant_service import TenantService
|
||||||
from app.services.session_service import SessionService
|
from app.services.session_service import SessionService
|
||||||
from app.core.security import get_password_hash, verify_password
|
from app.core.security import get_password_hash, verify_password
|
||||||
@@ -563,3 +568,175 @@ def generate_random_password(length: int = 12) -> str:
|
|||||||
secrets.SystemRandom().shuffle(password)
|
secrets.SystemRandom().shuffle(password)
|
||||||
|
|
||||||
return ''.join(password)
|
return ''.join(password)
|
||||||
|
|
||||||
|
|
||||||
|
def generate_email_code() -> str:
|
||||||
|
"""生成6位数字验证码"""
|
||||||
|
return ''.join([str(secrets.randbelow(10)) for _ in range(6)])
|
||||||
|
|
||||||
|
|
||||||
|
async def send_email_code_method(db: Session, email: EmailStr, user_id: uuid.UUID):
|
||||||
|
"""发送邮箱验证码"""
|
||||||
|
business_logger.info(f"发送邮箱验证码: email={email}")
|
||||||
|
|
||||||
|
# 检查发送间隔
|
||||||
|
rate_limit_key = f"email_code_rate:{user_id}"
|
||||||
|
last_send = await aio_redis_get(rate_limit_key)
|
||||||
|
|
||||||
|
if last_send:
|
||||||
|
raise BusinessException("请稍后再试,验证码发送间隔为1分钟", code=BizCode.RATE_LIMITED)
|
||||||
|
|
||||||
|
# 检查新邮箱是否已被使用
|
||||||
|
existing_user = user_repository.get_user_by_email(db=db, email=email)
|
||||||
|
if existing_user and existing_user.id != user_id:
|
||||||
|
raise BusinessException("邮箱已被使用", code=BizCode.DUPLICATE_NAME)
|
||||||
|
|
||||||
|
if existing_user and existing_user.id == user_id:
|
||||||
|
raise BusinessException("新邮箱与当前邮箱相同", code=BizCode.DUPLICATE_NAME)
|
||||||
|
|
||||||
|
# 生成验证码
|
||||||
|
code = generate_email_code()
|
||||||
|
|
||||||
|
# 存储到 Redis,5分钟过期
|
||||||
|
cache_key = f"email_code:{user_id}:{email}"
|
||||||
|
await aio_redis_set(cache_key, json.dumps(code), expire=300)
|
||||||
|
|
||||||
|
# 发送邮件
|
||||||
|
await send_email(
|
||||||
|
email,
|
||||||
|
"邮箱验证码",
|
||||||
|
f'<p>您的验证码是:<strong>{code}</strong></p><p>验证码在5分钟内有效。</p>'
|
||||||
|
)
|
||||||
|
|
||||||
|
# 设置发送间隔限制,60秒
|
||||||
|
await aio_redis_set(rate_limit_key, "1", expire=60)
|
||||||
|
|
||||||
|
business_logger.info(f"邮箱验证码已发送: {email}")
|
||||||
|
|
||||||
|
|
||||||
|
async def verify_and_change_email(db: Session, user_id: uuid.UUID, new_email: EmailStr, code: str) -> User:
|
||||||
|
"""验证验证码并修改邮箱"""
|
||||||
|
business_logger.info(f"验证并修改邮箱: user_id={user_id}, new_email={new_email}")
|
||||||
|
|
||||||
|
db_user = user_repository.get_user_by_id(db=db, user_id=user_id)
|
||||||
|
if not db_user:
|
||||||
|
raise BusinessException("用户不存在", code=BizCode.USER_NOT_FOUND)
|
||||||
|
|
||||||
|
# 验证验证码
|
||||||
|
cache_key = f"email_code:{user_id}:{new_email}"
|
||||||
|
cached_code = await aio_redis_get(cache_key)
|
||||||
|
|
||||||
|
if not cached_code:
|
||||||
|
raise BusinessException("验证码已过期", code=BizCode.VALIDATION_FAILED)
|
||||||
|
|
||||||
|
if json.loads(cached_code) != code:
|
||||||
|
raise BusinessException("验证码错误", code=BizCode.VALIDATION_FAILED)
|
||||||
|
|
||||||
|
# 修改邮箱
|
||||||
|
db_user.email = new_email
|
||||||
|
db.commit()
|
||||||
|
db.refresh(db_user)
|
||||||
|
|
||||||
|
# 删除验证码
|
||||||
|
await aio_redis_delete(cache_key)
|
||||||
|
|
||||||
|
# 使所有旧 tokens 失效
|
||||||
|
# await SessionService.invalidate_all_user_tokens(str(user_id))
|
||||||
|
|
||||||
|
business_logger.info(f"用户邮箱修改成功: {db_user.username}, new_email={new_email}")
|
||||||
|
return db_user
|
||||||
|
|
||||||
|
|
||||||
|
# def generate_email_token(user_id: str, old_email: str, new_email: str) -> str:
|
||||||
|
# """生成邮箱修改token"""
|
||||||
|
# payload = {
|
||||||
|
# "user_id": user_id,
|
||||||
|
# "old_email": old_email,
|
||||||
|
# "new_email": new_email,
|
||||||
|
# "exp": datetime.datetime.now(datetime.timezone.utc) + timedelta(hours=24)
|
||||||
|
# }
|
||||||
|
# return jwt.encode(payload, settings.SECRET_KEY, algorithm=settings.ALGORITHM)
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# def verify_email_token(token: str) -> dict:
|
||||||
|
# """验证邮箱修改token"""
|
||||||
|
# try:
|
||||||
|
# payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM])
|
||||||
|
# return payload
|
||||||
|
# except jwt.ExpiredSignatureError:
|
||||||
|
# raise BusinessException("链接已过期", code=BizCode.VALIDATION_FAILED)
|
||||||
|
# except jwt.InvalidTokenError:
|
||||||
|
# raise BusinessException("无效的链接", code=BizCode.VALIDATION_FAILED)
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# async def request_change_email(db: Session, user_id: uuid.UUID, new_email: EmailStr, current_user: User):
|
||||||
|
# """请求修改邮箱,发送验证邮件"""
|
||||||
|
# business_logger.info(f"用户请求修改邮箱: user_id={user_id}, new_email={new_email}")
|
||||||
|
#
|
||||||
|
# if current_user.id != user_id:
|
||||||
|
# raise PermissionDeniedException("只能修改自己的邮箱")
|
||||||
|
#
|
||||||
|
# db_user = user_repository.get_user_by_id(db=db, user_id=user_id)
|
||||||
|
# if not db_user:
|
||||||
|
# raise BusinessException("用户不存在", code=BizCode.USER_NOT_FOUND)
|
||||||
|
#
|
||||||
|
# if db_user.email == new_email:
|
||||||
|
# raise BusinessException("新邮箱与当前邮箱相同", code=BizCode.VALIDATION_FAILED)
|
||||||
|
#
|
||||||
|
# existing_user = user_repository.get_user_by_email(db=db, email=new_email)
|
||||||
|
# if existing_user and existing_user.id != user_id:
|
||||||
|
# raise BusinessException("邮箱已被使用", code=BizCode.DUPLICATE_NAME)
|
||||||
|
#
|
||||||
|
# token = generate_email_token(str(user_id), db_user.email, new_email)
|
||||||
|
#
|
||||||
|
# # 发送确认邮件到旧邮箱
|
||||||
|
# old_email_link = f"{settings.BASE_URL}/api/users/email/confirm-email-change?token={token}"
|
||||||
|
# await send_email(
|
||||||
|
# db_user.email,
|
||||||
|
# "确认修改邮箱",
|
||||||
|
# f'<p>请点击以下链接确认修改邮箱:</p><a href="{old_email_link}">确认修改</a>'
|
||||||
|
# )
|
||||||
|
#
|
||||||
|
# business_logger.info(f"邮箱修改确认邮件已发送到旧邮箱: {db_user.email}")
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# async def confirm_email_change(db: Session, token: str):
|
||||||
|
# """确认修改邮箱(旧邮箱确认)"""
|
||||||
|
# payload = verify_email_token(token)
|
||||||
|
# user_id = uuid.UUID(payload["user_id"])
|
||||||
|
# new_email = payload["new_email"]
|
||||||
|
#
|
||||||
|
# db_user = user_repository.get_user_by_id(db=db, user_id=user_id)
|
||||||
|
# if not db_user:
|
||||||
|
# raise BusinessException("用户不存在", code=BizCode.USER_NOT_FOUND)
|
||||||
|
#
|
||||||
|
# # 发送激活邮件到新邮箱
|
||||||
|
# activate_link = f"{settings.BASE_URL}/api/users/email/activate-new-email?token={token}"
|
||||||
|
# await send_email(
|
||||||
|
# new_email,
|
||||||
|
# "激活新邮箱",
|
||||||
|
# f'<p>请点击以下链接激活新邮箱:</p><a href="{activate_link}">激活邮箱</a>'
|
||||||
|
# )
|
||||||
|
#
|
||||||
|
# business_logger.info(f"新邮箱激活邮件已发送: {new_email}")
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# async def activate_new_email(db: Session, token: str) -> User:
|
||||||
|
# """激活新邮箱"""
|
||||||
|
# payload = verify_email_token(token)
|
||||||
|
# user_id = uuid.UUID(payload["user_id"])
|
||||||
|
# new_email = payload["new_email"]
|
||||||
|
#
|
||||||
|
# db_user = user_repository.get_user_by_id(db=db, user_id=user_id)
|
||||||
|
# if not db_user:
|
||||||
|
# raise BusinessException("用户不存在", code=BizCode.USER_NOT_FOUND)
|
||||||
|
#
|
||||||
|
# db_user.email = new_email
|
||||||
|
# db.commit()
|
||||||
|
# db.refresh(db_user)
|
||||||
|
#
|
||||||
|
# # 使所有旧 tokens 失效
|
||||||
|
# await SessionService.invalidate_all_user_tokens(str(user_id))
|
||||||
|
#
|
||||||
|
# business_logger.info(f"用户邮箱修改成功: {db_user.username}, new_email={new_email}")
|
||||||
|
# return db_user
|
||||||
|
|||||||
@@ -64,6 +64,9 @@ LANGCHAIN_ENDPOINT=
|
|||||||
# Generate a new one with: openssl rand -hex 32
|
# Generate a new one with: openssl rand -hex 32
|
||||||
SECRET_KEY=your-secret-key-here-generate-with-openssl-rand-hex-32
|
SECRET_KEY=your-secret-key-here-generate-with-openssl-rand-hex-32
|
||||||
|
|
||||||
|
# official environment system version
|
||||||
|
SYSTEM_VERSION=
|
||||||
|
|
||||||
# JWT Token expiration settings
|
# JWT Token expiration settings
|
||||||
ACCESS_TOKEN_EXPIRE_MINUTES=30
|
ACCESS_TOKEN_EXPIRE_MINUTES=30
|
||||||
REFRESH_TOKEN_EXPIRE_DAYS=7
|
REFRESH_TOKEN_EXPIRE_DAYS=7
|
||||||
@@ -129,6 +132,12 @@ KB_image2text_id=
|
|||||||
config_id=
|
config_id=
|
||||||
reranker_id=
|
reranker_id=
|
||||||
|
|
||||||
|
# Email Configuration
|
||||||
|
SMTP_SERVER=
|
||||||
|
SMTP_PORT=
|
||||||
|
SMTP_USER=
|
||||||
|
SMTP_PASSWORD=
|
||||||
|
|
||||||
# 本体类型融合配置 (记得写入env_example)
|
# 本体类型融合配置 (记得写入env_example)
|
||||||
GENERAL_ONTOLOGY_FILES=General_purpose_entity.ttl # 指定要加载的本体文件路径,多个文件用逗号分隔
|
GENERAL_ONTOLOGY_FILES=General_purpose_entity.ttl # 指定要加载的本体文件路径,多个文件用逗号分隔
|
||||||
ENABLE_GENERAL_ONTOLOGY_TYPES=true # 总开关,控制是否启用通用本体类型融合功能(false = 不使用任何本体类型指导)
|
ENABLE_GENERAL_ONTOLOGY_TYPES=true # 总开关,控制是否启用通用本体类型融合功能(false = 不使用任何本体类型指导)
|
||||||
|
|||||||
Reference in New Issue
Block a user