Files
moto-adv-website/app/routes/chat.py
ske087 30bd4c62ad 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.
2025-08-10 00:22:33 +03:00

277 lines
9.2 KiB
Python

"""
Chat web interface routes
Provides HTML templates and endpoints for web-based chat
"""
from flask import Blueprint, render_template, request, redirect, url_for, flash, jsonify
from flask_login import login_required, current_user
from sqlalchemy import desc, and_
from datetime import datetime, timedelta
from app.extensions import db
from app.models import ChatRoom, ChatMessage, ChatParticipant, Post
# Create blueprint
chat = Blueprint('chat', __name__)
@chat.route('/')
@login_required
def index():
"""Chat main page with room list and rules"""
# Get user's recent chat rooms
user_rooms = db.session.query(ChatRoom).join(ChatParticipant).filter(
and_(
ChatParticipant.user_id == current_user.id,
ChatRoom.is_active == True
)
).order_by(desc(ChatRoom.last_activity)).limit(10).all()
# Get public rooms that are active
public_rooms = ChatRoom.query.filter(
ChatRoom.is_active == True,
ChatRoom.is_private == False
).order_by(desc(ChatRoom.last_activity)).limit(10).all()
return render_template('chat/index.html',
user_rooms=user_rooms,
public_rooms=public_rooms)
@chat.route('/room/<int:room_id>')
@login_required
def room(room_id):
"""Chat room interface"""
room = ChatRoom.query.get_or_404(room_id)
# Check access
if room.is_private:
participant = ChatParticipant.query.filter_by(
room_id=room_id,
user_id=current_user.id
).first()
if not participant:
flash('You do not have access to this chat room.', 'error')
return redirect(url_for('chat.index'))
# Get or create participant record
participant = ChatParticipant.query.filter_by(
room_id=room_id,
user_id=current_user.id
).first()
if not participant and not room.is_private:
# Auto-join public rooms
participant = ChatParticipant(
room_id=room_id,
user_id=current_user.id,
role='member'
)
db.session.add(participant)
db.session.commit()
# Get recent messages
messages = ChatMessage.query.filter_by(
room_id=room_id,
is_deleted=False
).order_by(ChatMessage.created_at).limit(50).all()
# Get participants
participants = ChatParticipant.query.filter_by(room_id=room_id).all()
return render_template('chat/room.html',
room=room,
messages=messages,
participants=participants,
current_participant=participant)
@chat.route('/create')
@login_required
def create_room_form():
"""Show create room form"""
# Get available posts for post discussions
recent_posts = Post.query.filter_by(published=True).order_by(
desc(Post.created_at)
).limit(20).all()
# Check if a specific post was requested
pre_selected_post = request.args.get('post_id')
if pre_selected_post:
try:
pre_selected_post = int(pre_selected_post)
except ValueError:
pre_selected_post = None
return render_template('chat/create_room.html', posts=recent_posts, pre_selected_post=pre_selected_post)
@chat.route('/create', methods=['POST'])
@login_required
def create_room():
"""Create a new chat room"""
room_name = request.form.get('room_name')
description = request.form.get('description', '')
room_type = request.form.get('room_type', 'general')
is_private = bool(request.form.get('is_private'))
related_post_id = request.form.get('related_post_id')
if not room_name:
flash('Room name is required.', 'error')
return redirect(url_for('chat.create_room_form'))
# Convert to integer if post ID is provided
if related_post_id:
try:
related_post_id = int(related_post_id)
# Verify the post exists
related_post = Post.query.get(related_post_id)
if not related_post:
flash('Selected post does not exist.', 'error')
return redirect(url_for('chat.create_room_form'))
# If post is selected, set room type to post_discussion
room_type = 'post_discussion'
except ValueError:
related_post_id = None
else:
related_post_id = None
# If no post selected, ensure it's general discussion
if room_type == 'post_discussion':
room_type = 'general'
# Check if room name already exists
existing_room = ChatRoom.query.filter_by(name=room_name).first()
if existing_room:
flash('A room with that name already exists.', 'error')
return redirect(url_for('chat.create_room_form'))
try:
# Create the room
room = ChatRoom(
name=room_name,
description=description,
room_type=room_type,
is_private=is_private,
is_active=True,
created_by_id=current_user.id,
related_post_id=related_post_id
)
db.session.add(room)
db.session.flush() # Get the room ID
# Add creator as participant with admin role
participant = ChatParticipant(
room_id=room.id,
user_id=current_user.id,
role='admin',
joined_at=datetime.utcnow()
)
db.session.add(participant)
# Add welcome message
if related_post_id:
welcome_content = f"Welcome to the discussion for '{related_post.title}'! This room was created by {current_user.nickname} to discuss this post."
else:
welcome_content = f"Welcome to {room_name}! This room was created by {current_user.nickname}."
welcome_message = ChatMessage(
content=welcome_content,
room_id=room.id,
sender_id=current_user.id,
is_system_message=True
)
db.session.add(welcome_message)
# Update room activity
room.last_activity = datetime.utcnow()
room.message_count = 1
db.session.commit()
if related_post_id:
flash(f'Chat room "{room_name}" created successfully and linked to the post!', 'success')
else:
flash(f'Chat room "{room_name}" created successfully!', 'success')
return redirect(url_for('chat.room', room_id=room.id))
except Exception as e:
db.session.rollback()
flash(f'Error creating room: {str(e)}', 'error')
return redirect(url_for('chat.create_room_form'))
@chat.route('/support')
@login_required
def support():
"""Admin support page"""
# Get user's recent support tickets (rooms they created for support)
recent_tickets = ChatRoom.query.filter(
ChatRoom.room_type == 'admin_support',
ChatRoom.created_by_id == current_user.id
).order_by(desc(ChatRoom.created_at)).limit(5).all()
return render_template('chat/support.html',
recent_tickets=recent_tickets)
@chat.route('/embed/<int:post_id>')
@login_required
def embed_post_chat(post_id):
"""Embedded chat widget for post pages"""
post = Post.query.get_or_404(post_id)
# Find existing discussion room
discussion_room = ChatRoom.query.filter(
and_(
ChatRoom.room_type == 'post_discussion',
ChatRoom.related_post_id == post_id,
ChatRoom.is_active == True
)
).first()
return render_template('chat/embed.html',
post=post,
discussion_room=discussion_room)
@chat.route('/post-discussions')
@login_required
def post_discussions():
"""View all chat rooms related to posts"""
page = request.args.get('page', 1, type=int)
# Get all rooms that are linked to posts
post_rooms = ChatRoom.query.filter(
ChatRoom.related_post_id.isnot(None),
ChatRoom.is_active == True
).join(Post).order_by(ChatRoom.last_activity.desc()).paginate(
page=page, per_page=20, error_out=False
)
# Get statistics
total_post_discussions = ChatRoom.query.filter(
ChatRoom.related_post_id.isnot(None),
ChatRoom.is_active == True
).count()
active_discussions = ChatRoom.query.filter(
ChatRoom.related_post_id.isnot(None),
ChatRoom.is_active == True,
ChatRoom.last_activity >= datetime.utcnow() - timedelta(days=7)
).count()
return render_template('chat/post_discussions.html',
rooms=post_rooms,
total_discussions=total_post_discussions,
active_discussions=active_discussions)
@chat.route('/post/<int:post_id>/discussions')
@login_required
def post_specific_discussions(post_id):
"""View all chat rooms for a specific post"""
post = Post.query.get_or_404(post_id)
# Get all rooms for this specific post
rooms = ChatRoom.query.filter(
ChatRoom.related_post_id == post_id,
ChatRoom.is_active == True
).order_by(ChatRoom.last_activity.desc()).all()
return render_template('chat/post_specific_discussions.html',
post=post, rooms=rooms)