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 } })