Major Feature Update: Modern Chat System & Admin Management
Features Added: 🔥 Modern Chat System: - Real-time messaging with modern Tailwind CSS design - Post-linked discussions for adventure sharing - Chat categories (general, technical-support, adventure-planning) - Mobile-responsive interface with gradient backgrounds - JavaScript polling for live message updates 🎯 Comprehensive Admin Panel: - Chat room management with merge capabilities - Password reset system with email templates - User management with admin controls - Chat statistics and analytics dashboard - Room binding to posts and categorization �� Mobile API Integration: - RESTful API endpoints at /api/v1/chat - Session-based authentication for mobile apps - Comprehensive endpoints for rooms, messages, users - Mobile app compatibility (React Native, Flutter) 🛠️ Technical Improvements: - Enhanced database models with ChatRoom categories - Password reset token system with email verification - Template synchronization fixes for Docker deployment - Migration scripts for database schema updates - Improved error handling and validation 🎨 UI/UX Enhancements: - Modern card-based layouts matching app design - Consistent styling across chat and admin interfaces - Mobile-optimized touch interactions - Professional gradient designs and glass morphism effects 📚 Documentation: - Updated README with comprehensive API documentation - Added deployment instructions for Docker (port 8100) - Configuration guide for production environments - Mobile integration examples and endpoints This update transforms the platform into a comprehensive motorcycle adventure community with modern chat capabilities and professional admin management tools.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
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.models import User, db, PasswordResetToken
|
||||
from app.routes.reset_password import RequestResetForm, ResetPasswordForm
|
||||
from flask_mail import Message
|
||||
from app.routes.mail import mail
|
||||
@@ -161,26 +161,6 @@ def forgot_password():
|
||||
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
|
||||
@@ -235,3 +215,59 @@ def is_valid_password(password):
|
||||
if not re.search(r'\d', password):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class ResetPasswordWithTokenForm(FlaskForm):
|
||||
password = PasswordField('New Password', validators=[DataRequired(), Length(min=8)])
|
||||
password2 = PasswordField('Confirm New Password', validators=[DataRequired(), EqualTo('password')])
|
||||
submit = SubmitField('Reset Password')
|
||||
|
||||
|
||||
@auth.route('/reset-password/<token>', methods=['GET', 'POST'])
|
||||
def reset_password_with_token(token):
|
||||
"""Reset password using admin-generated token"""
|
||||
# Find the token in database
|
||||
reset_token = PasswordResetToken.query.filter_by(token=token).first()
|
||||
|
||||
if not reset_token:
|
||||
flash('Invalid or expired reset link.', 'error')
|
||||
return redirect(url_for('auth.login'))
|
||||
|
||||
# Check if token is expired
|
||||
if reset_token.is_expired:
|
||||
flash('This reset link has expired. Please request a new one.', 'error')
|
||||
return redirect(url_for('auth.login'))
|
||||
|
||||
# Check if token is already used
|
||||
if reset_token.is_used:
|
||||
flash('This reset link has already been used.', 'error')
|
||||
return redirect(url_for('auth.login'))
|
||||
|
||||
form = ResetPasswordWithTokenForm()
|
||||
|
||||
if form.validate_on_submit():
|
||||
user = reset_token.user
|
||||
|
||||
# Validate password strength
|
||||
if not is_valid_password(form.password.data):
|
||||
flash('Password must be at least 8 characters long and contain both letters and numbers.', 'error')
|
||||
return render_template('auth/reset_password_with_token.html', form=form, token=token)
|
||||
|
||||
# Update password
|
||||
user.set_password(form.password.data)
|
||||
|
||||
# Mark token as used
|
||||
reset_token.used_at = datetime.utcnow()
|
||||
reset_token.user_ip = request.environ.get('REMOTE_ADDR')
|
||||
|
||||
# Update request status
|
||||
if reset_token.request:
|
||||
reset_token.request.status = 'completed'
|
||||
reset_token.request.updated_at = datetime.utcnow()
|
||||
|
||||
db.session.commit()
|
||||
|
||||
flash('Your password has been reset successfully! You can now log in with your new password.', 'success')
|
||||
return redirect(url_for('auth.login'))
|
||||
|
||||
return render_template('auth/reset_password_with_token.html', form=form, token=token, user=reset_token.user)
|
||||
|
||||
Reference in New Issue
Block a user