from flask import Blueprint, render_template, request, flash, redirect, url_for, jsonify from flask_login import login_user, logout_user, login_required, current_user from app.models import User, EmailVerification from app import db, login_manager from utils import send_verification_email, send_password_reset_email import re auth_bp = Blueprint('auth', __name__) @login_manager.user_loader def load_user(user_id): return User.query.get(int(user_id)) @auth_bp.route('/register', methods=['GET', 'POST']) def register(): """用户注册""" if request.method == 'POST': try: # 获取表单数据 email = request.form.get('email', '').strip().lower() password = request.form.get('password', '').strip() confirm_password = request.form.get('confirm_password', '').strip() name = request.form.get('name', '').strip() age = request.form.get('age', '').strip() gender = request.form.get('gender', '').strip() parent_contact = request.form.get('parent_contact', '').strip() verification_code = request.form.get('verification_code', '').strip() # 验证必填字段 if not all([email, password, confirm_password, name, age, gender, verification_code]): flash('请填写所有必填字段', 'error') return render_template('auth/register.html') # 邮箱格式验证 email_pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$' if not re.match(email_pattern, email): flash('请输入有效的邮箱地址', 'error') return render_template('auth/register.html') # 密码验证 if len(password) < 6: flash('密码长度至少6位', 'error') return render_template('auth/register.html') if password != confirm_password: flash('两次输入的密码不一致', 'error') return render_template('auth/register.html') # 验证年龄 try: age = int(age) if age < 3 or age > 6: flash('年龄必须在3-6岁之间', 'error') return render_template('auth/register.html') except ValueError: flash('请输入有效的年龄', 'error') return render_template('auth/register.html') # 验证性别 if gender not in ['0', '1']: flash('请选择性别', 'error') return render_template('auth/register.html') # 检查邮箱是否已注册 existing_user = User.query.filter_by(email=email).first() if existing_user: flash('该邮箱已被注册', 'error') return render_template('auth/register.html') # 验证邮箱验证码 if not EmailVerification.verify_code(email, verification_code): flash('验证码错误或已过期', 'error') return render_template('auth/register.html') # 创建新用户 new_user = User( email=email, name=name, age=age, gender=int(gender), parent_contact=parent_contact if parent_contact else None, is_verified=True # 通过邮箱验证码验证,标记为已验证 ) new_user.set_password(password) db.session.add(new_user) db.session.commit() flash('注册成功!请登录', 'success') return redirect(url_for('auth.login')) except Exception as e: db.session.rollback() flash('注册失败,请重试', 'error') return render_template('auth/register.html') return render_template('auth/register.html') @auth_bp.route('/login', methods=['GET', 'POST']) def login(): """用户登录""" if request.method == 'POST': email = request.form.get('email', '').strip().lower() password = request.form.get('password', '').strip() remember = bool(request.form.get('remember')) if not email or not password: flash('请输入邮箱和密码', 'error') return render_template('auth/login.html') user = User.query.filter_by(email=email).first() if user and user.check_password(password): login_user(user, remember=remember) flash(f'欢迎回来,{user.name}!', 'success') # 重定向到之前访问的页面,如果没有则到主页 next_page = request.args.get('next') if next_page: return redirect(next_page) return redirect(url_for('main.dashboard')) else: flash('邮箱或密码错误', 'error') return render_template('auth/login.html') @auth_bp.route('/logout') @login_required def logout(): """用户登出""" logout_user() flash('已成功登出', 'success') return redirect(url_for('main.index')) @auth_bp.route('/forgot-password', methods=['GET', 'POST']) def forgot_password(): """忘记密码""" if request.method == 'POST': email = request.form.get('email', '').strip().lower() verification_code = request.form.get('verification_code', '').strip() new_password = request.form.get('new_password', '').strip() confirm_password = request.form.get('confirm_password', '').strip() # 检查用户是否存在 user = User.query.filter_by(email=email).first() if not user: flash('该邮箱未注册', 'error') return render_template('auth/forgot_password.html') # 如果是重置密码请求 if verification_code and new_password: if len(new_password) < 6: flash('密码长度至少6位', 'error') return render_template('auth/forgot_password.html') if new_password != confirm_password: flash('两次输入的密码不一致', 'error') return render_template('auth/forgot_password.html') # 验证验证码 if not EmailVerification.verify_code(email, verification_code): flash('验证码错误或已过期', 'error') return render_template('auth/forgot_password.html') # 更新密码 user.set_password(new_password) db.session.commit() flash('密码重置成功,请使用新密码登录', 'success') return redirect(url_for('auth.login')) return render_template('auth/forgot_password.html') @auth_bp.route('/send-verification-code', methods=['POST']) def send_verification_code(): """发送邮箱验证码""" try: email = request.json.get('email', '').strip().lower() code_type = request.json.get('type', 'register') # register 或 reset_password if not email: return jsonify({'success': False, 'message': '请输入邮箱地址'}) # 邮箱格式验证 email_pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$' if not re.match(email_pattern, email): return jsonify({'success': False, 'message': '请输入有效的邮箱地址'}) # 根据类型检查邮箱 if code_type == 'register': # 注册时检查邮箱是否已存在 existing_user = User.query.filter_by(email=email).first() if existing_user: return jsonify({'success': False, 'message': '该邮箱已被注册'}) elif code_type == 'reset_password': # 重置密码时检查邮箱是否存在 existing_user = User.query.filter_by(email=email).first() if not existing_user: return jsonify({'success': False, 'message': '该邮箱未注册'}) # 生成验证码 verification_code = EmailVerification.generate_code(email, expire_minutes=5) # 发送邮件 if code_type == 'register': success = send_verification_email(email, verification_code) else: success = send_password_reset_email(email, verification_code) if success: return jsonify({'success': True, 'message': '验证码已发送到您的邮箱,5分钟内有效'}) else: return jsonify({'success': False, 'message': '验证码发送失败,请重试'}) except Exception as e: return jsonify({'success': False, 'message': '发送失败,请重试'})