138 lines
5.4 KiB
Python
138 lines
5.4 KiB
Python
from flask import Blueprint, render_template, request, redirect, url_for, flash
|
|
from flask_login import login_user, logout_user, login_required, current_user
|
|
from werkzeug.security import check_password_hash
|
|
from app.models import User, db
|
|
from app.forms import LoginForm, RegisterForm, ForgotPasswordForm
|
|
from app.routes.reset_password import RequestResetForm, ResetPasswordForm
|
|
from flask_mail import Message
|
|
from app.routes.mail import mail
|
|
from app.utils.token import generate_reset_token, verify_reset_token
|
|
import re
|
|
from app.forms import LoginForm, RegisterForm, ForgotPasswordForm
|
|
auth = Blueprint('auth', __name__)
|
|
|
|
@auth.route('/login', methods=['GET', 'POST'])
|
|
def login():
|
|
"""User login page"""
|
|
if current_user.is_authenticated:
|
|
return redirect(url_for('main.index'))
|
|
|
|
form = LoginForm()
|
|
if form.validate_on_submit():
|
|
user = User.query.filter_by(email=form.email.data).first()
|
|
|
|
if user and user.check_password(form.password.data):
|
|
login_user(user, remember=form.remember_me.data)
|
|
next_page = request.args.get('next')
|
|
if not next_page or not next_page.startswith('/'):
|
|
next_page = url_for('community.index')
|
|
flash(f'Welcome back, {user.nickname}!', 'success')
|
|
return redirect(next_page)
|
|
else:
|
|
flash('Invalid email or password.', 'error')
|
|
|
|
return render_template('auth/login.html', form=form)
|
|
|
|
@auth.route('/register', methods=['GET', 'POST'])
|
|
def register():
|
|
"""User registration page"""
|
|
if current_user.is_authenticated:
|
|
return redirect(url_for('main.index'))
|
|
|
|
form = RegisterForm()
|
|
if form.validate_on_submit():
|
|
# Check if user already exists
|
|
if User.query.filter_by(email=form.email.data).first():
|
|
flash('Email address already registered.', 'error')
|
|
return render_template('auth/register.html', form=form)
|
|
|
|
if User.query.filter_by(nickname=form.nickname.data).first():
|
|
flash('Nickname already taken.', 'error')
|
|
return render_template('auth/register.html', form=form)
|
|
|
|
# Validate password strength
|
|
if not is_valid_password(form.password.data):
|
|
flash('Password must be at least 8 characters long and contain at least one letter and one number.', 'error')
|
|
return render_template('auth/register.html', form=form)
|
|
|
|
# Create new user
|
|
user = User(
|
|
nickname=form.nickname.data,
|
|
email=form.email.data
|
|
)
|
|
user.set_password(form.password.data)
|
|
|
|
try:
|
|
db.session.add(user)
|
|
db.session.commit()
|
|
login_user(user)
|
|
flash('Registration successful! Welcome to the community!', 'success')
|
|
return redirect(url_for('community.index'))
|
|
except Exception as e:
|
|
db.session.rollback()
|
|
flash('An error occurred during registration. Please try again.', 'error')
|
|
|
|
return render_template('auth/register.html', form=form)
|
|
|
|
@auth.route('/logout')
|
|
@login_required
|
|
def logout():
|
|
"""User logout"""
|
|
logout_user()
|
|
flash('You have been logged out.', 'info')
|
|
return redirect(url_for('main.index'))
|
|
|
|
@auth.route('/forgot-password', methods=['GET', 'POST'])
|
|
def forgot_password():
|
|
"""Forgot password page"""
|
|
if current_user.is_authenticated:
|
|
return redirect(url_for('main.index'))
|
|
form = RequestResetForm()
|
|
if form.validate_on_submit():
|
|
user = User.query.filter_by(email=form.email.data).first()
|
|
if user:
|
|
token = generate_reset_token(user.email)
|
|
reset_url = url_for('auth.reset_password', token=token, _external=True)
|
|
msg = Message(
|
|
subject="Password Reset Request",
|
|
recipients=[user.email],
|
|
body=f"Hello {user.nickname},\n\nTo reset your password, click the link below:\n{reset_url}\n\nIf you did not request this, please ignore this email."
|
|
)
|
|
try:
|
|
mail.send(msg)
|
|
except Exception as e:
|
|
flash(f"Failed to send reset email: {e}", "danger")
|
|
flash('If an account with that email exists, we\'ve sent password reset instructions.', 'info')
|
|
return redirect(url_for('auth.login'))
|
|
return render_template('auth/forgot_password.html', form=form)
|
|
# Password reset route
|
|
@auth.route('/reset-password/<token>', methods=['GET', 'POST'])
|
|
def reset_password(token):
|
|
if current_user.is_authenticated:
|
|
return redirect(url_for('main.index'))
|
|
email = verify_reset_token(token)
|
|
if not email:
|
|
flash('Invalid or expired reset link.', 'danger')
|
|
return redirect(url_for('auth.forgot_password'))
|
|
user = User.query.filter_by(email=email).first()
|
|
if not user:
|
|
flash('Invalid or expired reset link.', 'danger')
|
|
return redirect(url_for('auth.forgot_password'))
|
|
form = ResetPasswordForm()
|
|
if form.validate_on_submit():
|
|
user.set_password(form.password.data)
|
|
db.session.commit()
|
|
flash('Your password has been reset. You can now log in.', 'success')
|
|
return redirect(url_for('auth.login'))
|
|
return render_template('auth/reset_password.html', form=form)
|
|
|
|
def is_valid_password(password):
|
|
"""Validate password strength"""
|
|
if len(password) < 8:
|
|
return False
|
|
if not re.search(r'[A-Za-z]', password):
|
|
return False
|
|
if not re.search(r'\d', password):
|
|
return False
|
|
return True
|