feat: 添加邮件功能第二阶段 - 注册邮箱验证
实现注册时的邮箱验证功能: - 修改注册API支持邮箱验证流程 - 新增邮箱验证API (/api/verify-email/<token>) - 新增重发验证邮件API (/api/resend-verify-email) - 新增邮箱验证状态查询API (/api/email/verify-status) 新增文件: - templates/email/register.html - 注册验证邮件模板 - templates/verify_success.html - 验证成功页面 - templates/verify_failed.html - 验证失败页面 修改文件: - email_service.py - 添加发送注册验证邮件函数 - app.py - 添加邮箱验证相关API - database.py - 添加get_user_by_email函数 - app_config.py - 添加BASE_URL配置 - templates/register.html - 支持邮箱必填切换 - templates/login.html - 添加重发验证邮件功能 - templates/admin.html - 添加注册验证开关和BASE_URL设置 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
140
app.py
140
app.py
@@ -693,22 +693,55 @@ def register():
|
||||
return jsonify({"error": "验证码错误次数过多,IP已被锁定1小时"}), 429
|
||||
return jsonify({"error": message}), 400
|
||||
|
||||
# 检查邮箱验证是否启用
|
||||
email_settings = email_service.get_email_settings()
|
||||
email_verify_enabled = email_settings.get('register_verify_enabled', False) and email_settings.get('enabled', False)
|
||||
|
||||
# 如果启用了邮箱验证,邮箱必填
|
||||
if email_verify_enabled and not email:
|
||||
return jsonify({"error": "启用邮箱验证后,邮箱为必填项"}), 400
|
||||
|
||||
# 简单邮箱格式验证
|
||||
if email and '@' not in email:
|
||||
return jsonify({"error": "邮箱格式不正确"}), 400
|
||||
|
||||
# 获取自动审核配置
|
||||
system_config = database.get_system_config()
|
||||
auto_approve_enabled = system_config.get('auto_approve_enabled', 0) == 1
|
||||
auto_approve_hourly_limit = system_config.get('auto_approve_hourly_limit', 10)
|
||||
auto_approve_vip_days = system_config.get('auto_approve_vip_days', 7)
|
||||
|
||||
# 检查每小时注册限制
|
||||
if auto_approve_enabled:
|
||||
# 检查每小时注册限制(同时适用于自动审核和邮箱验证)
|
||||
if auto_approve_enabled or email_verify_enabled:
|
||||
hourly_count = database.get_hourly_registration_count()
|
||||
if hourly_count >= auto_approve_hourly_limit:
|
||||
return jsonify({"error": f"注册人数过多,请稍后再试(每小时限制{auto_approve_hourly_limit}人)"}), 429
|
||||
|
||||
user_id = database.create_user(username, password, email)
|
||||
if user_id:
|
||||
# 自动审核处理
|
||||
if auto_approve_enabled:
|
||||
# 优先级:邮箱验证 > 自动审核 > 手动审核
|
||||
if email_verify_enabled and email:
|
||||
# 发送验证邮件
|
||||
result = email_service.send_register_verification_email(
|
||||
email=email,
|
||||
username=username,
|
||||
user_id=user_id
|
||||
)
|
||||
if result['success']:
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"message": "注册成功!验证邮件已发送,请查收邮箱并点击链接完成验证",
|
||||
"need_verify": True
|
||||
})
|
||||
else:
|
||||
# 邮件发送失败,但用户已创建,返回提示
|
||||
logger.error(f"注册验证邮件发送失败: {result['error']}")
|
||||
return jsonify({
|
||||
"success": True,
|
||||
"message": f"注册成功,但验证邮件发送失败({result['error']})。请稍后在登录页面重新发送验证邮件",
|
||||
"need_verify": True
|
||||
})
|
||||
elif auto_approve_enabled:
|
||||
# 自动审核通过
|
||||
database.approve_user(user_id)
|
||||
# 赠送VIP天数
|
||||
@@ -723,6 +756,96 @@ def register():
|
||||
return jsonify({"error": "用户名已存在"}), 400
|
||||
|
||||
|
||||
@app.route('/api/verify-email/<token>')
|
||||
def verify_email(token):
|
||||
"""验证邮箱 - 用户点击邮件中的链接"""
|
||||
result = email_service.verify_email_token(token, email_service.EMAIL_TYPE_REGISTER)
|
||||
|
||||
if result:
|
||||
user_id = result['user_id']
|
||||
email = result['email']
|
||||
|
||||
# 验证成功,激活用户
|
||||
database.approve_user(user_id)
|
||||
|
||||
# 获取自动审核配置,检查是否赠送VIP
|
||||
system_config = database.get_system_config()
|
||||
auto_approve_vip_days = system_config.get('auto_approve_vip_days', 7)
|
||||
if auto_approve_vip_days > 0:
|
||||
database.set_user_vip(user_id, auto_approve_vip_days)
|
||||
|
||||
logger.info(f"用户邮箱验<EFBFBD><EFBFBD><EFBFBD>成功: user_id={user_id}, email={email}")
|
||||
return render_template('verify_success.html')
|
||||
else:
|
||||
logger.warning(f"邮箱验证失败: token={token[:20]}...")
|
||||
return render_template('verify_failed.html', error_message="验证链接无效或已过期,请重新注册或申请重发验证邮件")
|
||||
|
||||
|
||||
@app.route('/api/resend-verify-email', methods=['POST'])
|
||||
@require_ip_not_locked
|
||||
def resend_verify_email():
|
||||
"""重发验证邮件"""
|
||||
data = request.json
|
||||
email = data.get('email', '').strip()
|
||||
captcha_session = data.get('captcha_session', '')
|
||||
captcha_code = data.get('captcha', '').strip()
|
||||
|
||||
if not email:
|
||||
return jsonify({"error": "请输入邮箱"}), 400
|
||||
|
||||
# 获取客户端IP(用于IP限流检查)
|
||||
client_ip = get_client_ip()
|
||||
|
||||
# 检查IP限流
|
||||
allowed, error_msg = check_ip_rate_limit(client_ip)
|
||||
if not allowed:
|
||||
return jsonify({"error": error_msg}), 429
|
||||
|
||||
# 验证验证码
|
||||
success, message = verify_and_consume_captcha(captcha_session, captcha_code, captcha_storage, MAX_CAPTCHA_ATTEMPTS)
|
||||
if not success:
|
||||
is_locked = record_failed_captcha(client_ip)
|
||||
if is_locked:
|
||||
return jsonify({"error": "验证码错误次数过多,IP已被锁定1小时"}), 429
|
||||
return jsonify({"error": message}), 400
|
||||
|
||||
# 查找待验证的用户
|
||||
user = database.get_user_by_email(email)
|
||||
if not user:
|
||||
return jsonify({"error": "该邮箱未注册"}), 404
|
||||
|
||||
if user['status'] == 'approved':
|
||||
return jsonify({"error": "该账号已验证通过,请直接登录"}), 400
|
||||
|
||||
# 发送验证邮件
|
||||
result = email_service.resend_register_verification_email(
|
||||
user_id=user['id'],
|
||||
email=email,
|
||||
username=user['username']
|
||||
)
|
||||
|
||||
if result['success']:
|
||||
return jsonify({"success": True, "message": "验证邮件已重新发送,请查收"})
|
||||
else:
|
||||
return jsonify({"error": result['error']}), 500
|
||||
|
||||
|
||||
@app.route('/api/email/verify-status')
|
||||
def get_email_verify_status():
|
||||
"""获取邮箱验证功能状态(公开API)"""
|
||||
try:
|
||||
settings = email_service.get_email_settings()
|
||||
return jsonify({
|
||||
'email_enabled': settings.get('enabled', False),
|
||||
'register_verify_enabled': settings.get('register_verify_enabled', False) and settings.get('enabled', False)
|
||||
})
|
||||
except Exception as e:
|
||||
return jsonify({
|
||||
'email_enabled': False,
|
||||
'register_verify_enabled': False
|
||||
})
|
||||
|
||||
|
||||
# ==================== 验证码API ====================
|
||||
import random
|
||||
from task_checkpoint import get_checkpoint_manager, TaskStage
|
||||
@@ -3496,8 +3619,15 @@ def update_email_settings_api():
|
||||
data = request.json
|
||||
enabled = data.get('enabled', False)
|
||||
failover_enabled = data.get('failover_enabled', True)
|
||||
register_verify_enabled = data.get('register_verify_enabled')
|
||||
base_url = data.get('base_url')
|
||||
|
||||
email_service.update_email_settings(enabled, failover_enabled)
|
||||
email_service.update_email_settings(
|
||||
enabled=enabled,
|
||||
failover_enabled=failover_enabled,
|
||||
register_verify_enabled=register_verify_enabled,
|
||||
base_url=base_url
|
||||
)
|
||||
return jsonify({'success': True})
|
||||
except Exception as e:
|
||||
logger.error(f"更新邮件设置失败: {e}")
|
||||
|
||||
Reference in New Issue
Block a user