Files
moto-adv-website/app/routes/auth.py
ske087 1661f5f588 feat: Complete chat system implementation and password reset enhancement
- Add comprehensive chat system with modern UI design
- Implement admin-based password reset system
- Fix template syntax errors and 500 server errors
- Add chat routes, API endpoints, and database models
- Enhance user interface with Tailwind CSS card-based design
- Implement community guidelines and quick action features
- Add responsive design for mobile and desktop compatibility
- Create support chat functionality with admin integration
- Fix JavaScript inheritance in base template
- Add database migration for chat system tables

Features:
 Modern chat interface with room management
 Admin-based password reset workflow
 Real-time chat with mobile app support
 Professional UI with gradient cards and hover effects
 Community guidelines and safety features
 Responsive design for all devices
 Error-free template rendering
2025-08-09 20:44:25 +03:00

238 lines
9.6 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.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
from datetime import datetime
import re
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField, SubmitField
from wtforms.validators import DataRequired, Email, EqualTo, Length
auth = Blueprint('auth', __name__)
class LoginForm(FlaskForm):
email = StringField('Email', validators=[DataRequired(), Email()])
password = PasswordField('Password', validators=[DataRequired()])
remember_me = BooleanField('Remember Me')
submit = SubmitField('Sign In')
class RegisterForm(FlaskForm):
nickname = StringField('Nickname', validators=[DataRequired(), Length(min=3, max=32)])
email = StringField('Email', validators=[DataRequired(), Email()])
password = PasswordField('Password', validators=[DataRequired(), Length(min=8)])
password2 = PasswordField('Confirm Password', validators=[DataRequired(), EqualTo('password')])
submit = SubmitField('Register')
class ForgotPasswordForm(FlaskForm):
email = StringField('Email', validators=[DataRequired(), Email()])
submit = SubmitField('Request Password Reset')
@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 - sends message to admin instead of email"""
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()
# Create password reset user if it doesn't exist
reset_user = User.query.filter_by(email='reset_password@motoadventure.local').first()
if not reset_user:
reset_user = User(
nickname='PasswordReset',
email='reset_password@motoadventure.local',
is_active=False # This is a system user
)
reset_user.set_password('temp_password') # Won't be used
db.session.add(reset_user)
db.session.commit()
# Find admin support room
from app.models import ChatRoom, ChatMessage
admin_room = ChatRoom.query.filter_by(room_type='support').first()
if not admin_room:
# Create admin support room if it doesn't exist
system_user = User.query.filter_by(email='system@motoadventure.local').first()
admin_room = ChatRoom(
name='Technical Support',
description='Administrative support and password resets',
room_type='support',
is_private=False,
is_active=True,
created_by_id=system_user.id if system_user else 1
)
db.session.add(admin_room)
db.session.commit()
# Create the password reset message
if user:
message_content = f"A user with email '{user.email}' (nickname: {user.nickname}) needs their password to be changed. Please assist with password reset."
else:
message_content = f"Someone with email '{form.email.data}' requested a password reset, but no account exists with this email. Please check if this user needs assistance creating an account."
reset_message = ChatMessage(
content=message_content,
room_id=admin_room.id,
sender_id=reset_user.id,
is_system_message=True
)
db.session.add(reset_message)
# Update room activity
admin_room.last_activity = datetime.utcnow()
db.session.commit()
flash('Your password reset request has been sent to administrators. They will contact you soon to help reset your password.', '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)
@auth.route('/change-password', methods=['POST'])
@login_required
def change_password():
"""Change user password"""
current_password = request.form.get('current_password')
new_password = request.form.get('new_password')
confirm_password = request.form.get('confirm_password')
# Validate inputs
if not all([current_password, new_password, confirm_password]):
flash('All password fields are required.', 'error')
return redirect(url_for('community.profile'))
# Check current password
if not current_user.check_password(current_password):
flash('Current password is incorrect.', 'error')
return redirect(url_for('community.profile'))
# Validate new password
if len(new_password) < 6:
flash('New password must be at least 6 characters long.', 'error')
return redirect(url_for('community.profile'))
# Check password confirmation
if new_password != confirm_password:
flash('New password and confirmation do not match.', 'error')
return redirect(url_for('community.profile'))
# Check if new password is different from current
if current_user.check_password(new_password):
flash('New password must be different from your current password.', 'error')
return redirect(url_for('community.profile'))
try:
# Update password
current_user.set_password(new_password)
db.session.commit()
flash('Password updated successfully!', 'success')
except Exception as e:
db.session.rollback()
flash('An error occurred while updating your password. Please try again.', 'error')
return redirect(url_for('community.profile'))
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