217 lines
8.8 KiB
Python
217 lines
8.8 KiB
Python
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': '发送失败,请重试'})
|