Files
JieXi/routes/auth.py
2025-11-30 19:49:25 +08:00

356 lines
12 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from flask import Blueprint, request, jsonify, session
from flask_login import login_user, logout_user, login_required, current_user
from models import User, EmailVerification
from models import db
from utils.security import hash_password, verify_password, generate_verification_code, get_client_ip
from utils.email import EmailService
from datetime import datetime, timedelta
import re
auth_bp = Blueprint('auth', __name__)
def validate_email(email):
"""验证邮箱格式"""
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
return re.match(pattern, email) is not None
@auth_bp.route('/send-code', methods=['POST'])
def send_verification_code():
"""发送验证码"""
data = request.get_json()
email = data.get('email')
purpose = data.get('purpose', 'register')
if not email or not validate_email(email):
return jsonify({'success': False, 'message': '邮箱格式不正确'}), 400
# 检查用户是否已存在
if purpose == 'register':
if User.query.filter_by(email=email).first():
return jsonify({'success': False, 'message': '该邮箱已被注册'}), 400
elif purpose in ['reset_password', 'forgot_password']:
if not User.query.filter_by(email=email).first():
return jsonify({'success': False, 'message': '该邮箱未注册'}), 400
# 生成验证码
code = generate_verification_code(6)
expires_at = datetime.utcnow() + timedelta(minutes=10)
# 保存验证码
verification = EmailVerification(
email=email,
code=code,
purpose=purpose,
expires_at=expires_at
)
db.session.add(verification)
db.session.commit()
# 发送邮件
try:
EmailService.send_verification_code(email, code, purpose)
return jsonify({'success': True, 'message': '验证码已发送'})
except Exception as e:
return jsonify({'success': False, 'message': str(e)}), 500
@auth_bp.route('/register', methods=['GET', 'POST'])
def register():
"""用户注册"""
if request.method == 'GET':
from flask import render_template
return render_template('register.html')
data = request.get_json()
username = data.get('username')
email = data.get('email')
password = data.get('password')
code = data.get('code')
# 验证输入
if not all([username, email, password, code]):
return jsonify({'success': False, 'message': '请填写完整信息'}), 400
if not validate_email(email):
return jsonify({'success': False, 'message': '邮箱格式不正确'}), 400
if len(password) < 6:
return jsonify({'success': False, 'message': '密码长度至少6位'}), 400
# 检查用户名和邮箱是否已存在
if User.query.filter_by(username=username).first():
return jsonify({'success': False, 'message': '用户名已存在'}), 400
if User.query.filter_by(email=email).first():
return jsonify({'success': False, 'message': '邮箱已被注册'}), 400
# 验证验证码
verification = EmailVerification.query.filter_by(
email=email,
code=code,
purpose='register',
is_used=False
).filter(EmailVerification.expires_at > datetime.utcnow()).first()
if not verification:
return jsonify({'success': False, 'message': '验证码无效或已过期'}), 400
# 创建用户
user = User(
username=username,
email=email,
password=hash_password(password),
register_ip=get_client_ip(request),
group_id=2 # 默认普通用户
)
db.session.add(user)
# 标记验证码已使用
verification.is_used = True
db.session.commit()
return jsonify({'success': True, 'message': '注册成功'})
@auth_bp.route('/login', methods=['GET', 'POST'])
def login():
"""用户登录"""
if request.method == 'GET':
from flask import render_template
return render_template('login.html')
data = request.get_json()
email = data.get('email')
password = data.get('password')
if not all([email, password]):
return jsonify({'success': False, 'message': '请填写完整信息'}), 400
user = User.query.filter_by(email=email).first()
if not user or not verify_password(password, user.password):
return jsonify({'success': False, 'message': '邮箱或密码错误'}), 401
if not user.is_active:
return jsonify({'success': False, 'message': '账号已被禁用'}), 403
# 更新登录信息
user.last_login_ip = get_client_ip(request)
db.session.commit()
# 登录用户
login_user(user)
return jsonify({
'success': True,
'message': '登录成功',
'user': {
'id': user.id,
'username': user.username,
'email': user.email
}
})
@auth_bp.route('/logout', methods=['POST'])
@login_required
def logout():
"""用户登出"""
logout_user()
return jsonify({'success': True, 'message': '已退出登录'})
@auth_bp.route('/reset-password', methods=['POST'])
def reset_password():
"""重置密码"""
data = request.get_json()
email = data.get('email')
code = data.get('code')
new_password = data.get('new_password')
if not all([email, code, new_password]):
return jsonify({'success': False, 'message': '请填写完整信息'}), 400
if len(new_password) < 6:
return jsonify({'success': False, 'message': '密码长度至少6位'}), 400
# 验证验证码
verification = EmailVerification.query.filter_by(
email=email,
code=code,
is_used=False
).filter(
EmailVerification.purpose.in_(['reset_password', 'forgot_password'])
).filter(EmailVerification.expires_at > datetime.utcnow()).first()
if not verification:
return jsonify({'success': False, 'message': '验证码无效或已过期'}), 400
# 更新密码
user = User.query.filter_by(email=email).first()
if not user:
return jsonify({'success': False, 'message': '用户不存在'}), 404
user.password = hash_password(new_password)
verification.is_used = True
db.session.commit()
return jsonify({'success': True, 'message': '密码重置成功'})
@auth_bp.route('/user-info', methods=['GET'])
@login_required
def user_info():
"""获取当前用户信息"""
return jsonify({
'success': True,
'user': {
'id': current_user.id,
'username': current_user.username,
'email': current_user.email,
'group_id': current_user.group_id,
'total_parse_count': current_user.total_parse_count
}
})
@auth_bp.route('/profile', methods=['GET'])
@login_required
def profile():
"""用户个人中心页面"""
from flask import render_template
return render_template('profile.html')
@auth_bp.route('/api/profile', methods=['GET'])
@login_required
def get_profile():
"""获取用户个人中心数据"""
from models import UserGroup, DailyParseStat, ParseLog, UserGroupExpiry
from datetime import date, datetime
# 获取用户组信息
user_group = UserGroup.query.get(current_user.group_id)
daily_limit = user_group.daily_limit if user_group else 10
group_name = user_group.name if user_group else '普通用户'
# 获取套餐到期时间
group_expiry = UserGroupExpiry.query.filter_by(user_id=current_user.id).first()
expires_at = None
is_expired = False
if group_expiry:
expires_at = group_expiry.expires_at.strftime('%Y-%m-%d %H:%M') if group_expiry.expires_at else None
is_expired = group_expiry.expires_at < datetime.utcnow() if group_expiry.expires_at else False
# 获取今日使用次数
today = date.today()
today_stat = DailyParseStat.query.filter_by(
user_id=current_user.id,
date=today
).first()
today_used = today_stat.parse_count if today_stat else 0
today_remaining = max(0, daily_limit - today_used)
# 获取解析记录最近20条
parse_logs = ParseLog.query.filter_by(
user_id=current_user.id
).order_by(ParseLog.created_at.desc()).limit(20).all()
logs_data = [{
'id': log.id,
'platform': log.platform,
'video_url': log.video_url[:50] + '...' if len(log.video_url) > 50 else log.video_url,
'status': log.status,
'response_time': log.response_time,
'created_at': log.created_at.strftime('%Y-%m-%d %H:%M:%S') if log.created_at else ''
} for log in parse_logs]
return jsonify({
'success': True,
'data': {
'user': {
'id': current_user.id,
'username': current_user.username,
'email': current_user.email,
'created_at': current_user.created_at.strftime('%Y-%m-%d') if current_user.created_at else ''
},
'group': {
'id': current_user.group_id,
'name': group_name,
'daily_limit': daily_limit,
'expires_at': expires_at,
'is_expired': is_expired
},
'usage': {
'daily_limit': daily_limit,
'today_used': today_used,
'today_remaining': today_remaining,
'total_parse_count': current_user.total_parse_count or 0
},
'parse_logs': logs_data
}
})
@auth_bp.route('/api/redeem', methods=['POST'])
@login_required
def redeem_code():
"""用户兑换码"""
from models import RedeemCode, UserGroupExpiry
from datetime import datetime, timedelta
data = request.get_json()
code_str = data.get('code', '').strip().upper()
if not code_str:
return jsonify({'success': False, 'message': '请输入兑换码'}), 400
# 查找兑换码
code = RedeemCode.query.filter_by(code=code_str).first()
if not code:
return jsonify({'success': False, 'message': '兑换码不存在'}), 404
if code.is_used:
return jsonify({'success': False, 'message': '兑换码已被使用'}), 400
if code.expires_at and code.expires_at < datetime.utcnow():
return jsonify({'success': False, 'message': '兑换码已过期'}), 400
# 执行兑换
user = current_user
# 更新用户组
old_group_id = user.group_id
user.group_id = code.target_group_id
# 计算到期时间
expires_at = datetime.utcnow() + timedelta(days=code.duration_days)
# 更新或创建到期记录
expiry = UserGroupExpiry.query.filter_by(user_id=user.id).first()
if expiry:
# 如果当前套餐未过期且是同一套餐,则叠加时间
if expiry.group_id == code.target_group_id and expiry.expires_at > datetime.utcnow():
expires_at = expiry.expires_at + timedelta(days=code.duration_days)
expiry.group_id = code.target_group_id
expiry.expires_at = expires_at
else:
expiry = UserGroupExpiry(
user_id=user.id,
group_id=code.target_group_id,
expires_at=expires_at
)
db.session.add(expiry)
# 标记兑换码已使用
code.is_used = True
code.used_by = user.id
code.used_at = datetime.utcnow()
db.session.commit()
return jsonify({
'success': True,
'message': f'兑换成功!您已升级为 {code.target_group.name},有效期至 {expires_at.strftime("%Y-%m-%d %H:%M")}',
'data': {
'group_name': code.target_group.name,
'expires_at': expires_at.strftime('%Y-%m-%d %H:%M'),
'duration_days': code.duration_days
}
})