356 lines
12 KiB
Python
356 lines
12 KiB
Python
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
|
||
}
|
||
})
|