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
This commit is contained in:
@@ -6,6 +6,7 @@ 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
|
||||
@@ -103,25 +104,61 @@ def logout():
|
||||
|
||||
@auth.route('/forgot-password', methods=['GET', 'POST'])
|
||||
def forgot_password():
|
||||
"""Forgot password page"""
|
||||
"""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()
|
||||
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."
|
||||
|
||||
# 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
|
||||
)
|
||||
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')
|
||||
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
|
||||
@@ -145,6 +182,50 @@ def reset_password(token):
|
||||
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:
|
||||
|
||||
125
app/routes/chat.py
Normal file
125
app/routes/chat.py
Normal file
@@ -0,0 +1,125 @@
|
||||
"""
|
||||
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 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()
|
||||
|
||||
return render_template('chat/create_room.html', posts=recent_posts)
|
||||
|
||||
@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)
|
||||
560
app/routes/chat_api.py
Normal file
560
app/routes/chat_api.py
Normal file
@@ -0,0 +1,560 @@
|
||||
"""
|
||||
Chat API routes for mobile app compatibility
|
||||
Provides RESTful endpoints for chat functionality
|
||||
"""
|
||||
from flask import Blueprint, request, jsonify, current_app
|
||||
from flask_login import login_required, current_user
|
||||
from sqlalchemy import desc, and_, or_
|
||||
from datetime import datetime, timedelta
|
||||
import re
|
||||
|
||||
from app.extensions import db
|
||||
from app.models import ChatRoom, ChatMessage, ChatParticipant, Post, User
|
||||
|
||||
# Create blueprint
|
||||
chat_api = Blueprint('chat_api', __name__)
|
||||
|
||||
# Chat rules and guidelines
|
||||
CHAT_RULES = [
|
||||
"Be respectful and courteous to all community members",
|
||||
"No offensive language, harassment, or personal attacks",
|
||||
"Stay on topic - use post-specific chats for discussions about routes",
|
||||
"No spam or promotional content without permission",
|
||||
"Share useful tips and experiences about motorcycle adventures",
|
||||
"Help newcomers and answer questions when you can",
|
||||
"Report inappropriate behavior to administrators",
|
||||
"Keep conversations constructive and helpful"
|
||||
]
|
||||
|
||||
# Profanity filter (basic implementation)
|
||||
BLOCKED_WORDS = [
|
||||
'spam', 'scam', 'fake', 'stupid', 'idiot', 'hate'
|
||||
# Add more words as needed
|
||||
]
|
||||
|
||||
def contains_blocked_content(text):
|
||||
"""Check if text contains blocked words"""
|
||||
text_lower = text.lower()
|
||||
return any(word in text_lower for word in BLOCKED_WORDS)
|
||||
|
||||
@chat_api.route('/rules', methods=['GET'])
|
||||
def get_chat_rules():
|
||||
"""Get chat rules and guidelines"""
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'rules': CHAT_RULES
|
||||
})
|
||||
|
||||
@chat_api.route('/rooms', methods=['GET'])
|
||||
@login_required
|
||||
def get_chat_rooms():
|
||||
"""Get list of available chat rooms for the user"""
|
||||
page = request.args.get('page', 1, type=int)
|
||||
per_page = request.args.get('per_page', 20, type=int)
|
||||
room_type = request.args.get('type', None)
|
||||
|
||||
# Base query - only active rooms the user can access
|
||||
query = ChatRoom.query.filter(ChatRoom.is_active == True)
|
||||
|
||||
# Filter by type if specified
|
||||
if room_type:
|
||||
query = query.filter(ChatRoom.room_type == room_type)
|
||||
|
||||
# Order by last activity
|
||||
query = query.order_by(desc(ChatRoom.last_activity))
|
||||
|
||||
# Paginate
|
||||
rooms = query.paginate(
|
||||
page=page, per_page=per_page, error_out=False
|
||||
)
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'rooms': [room.to_dict() for room in rooms.items],
|
||||
'pagination': {
|
||||
'page': page,
|
||||
'pages': rooms.pages,
|
||||
'per_page': per_page,
|
||||
'total': rooms.total,
|
||||
'has_next': rooms.has_next,
|
||||
'has_prev': rooms.has_prev
|
||||
}
|
||||
})
|
||||
|
||||
@chat_api.route('/rooms', methods=['POST'])
|
||||
@login_required
|
||||
def create_chat_room():
|
||||
"""Create a new chat room"""
|
||||
data = request.get_json()
|
||||
|
||||
if not data or not data.get('name'):
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': 'Room name is required'
|
||||
}), 400
|
||||
|
||||
# Validate input
|
||||
name = data.get('name', '').strip()
|
||||
description = data.get('description', '').strip()
|
||||
room_type = data.get('room_type', 'general')
|
||||
related_post_id = data.get('related_post_id')
|
||||
|
||||
if len(name) < 3 or len(name) > 100:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': 'Room name must be between 3 and 100 characters'
|
||||
}), 400
|
||||
|
||||
# Check if room already exists
|
||||
existing_room = ChatRoom.query.filter_by(name=name).first()
|
||||
if existing_room:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': 'A room with this name already exists'
|
||||
}), 400
|
||||
|
||||
# Validate related post if specified
|
||||
if related_post_id:
|
||||
post = Post.query.get(related_post_id)
|
||||
if not post:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': 'Related post not found'
|
||||
}), 404
|
||||
|
||||
try:
|
||||
# Create room
|
||||
room = ChatRoom(
|
||||
name=name,
|
||||
description=description,
|
||||
room_type=room_type,
|
||||
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'
|
||||
)
|
||||
db.session.add(participant)
|
||||
|
||||
# Add system message
|
||||
system_message = ChatMessage(
|
||||
room_id=room.id,
|
||||
user_id=current_user.id,
|
||||
content=f"Welcome to {name}! This chat room was created for motorcycle adventure discussions.",
|
||||
message_type='system'
|
||||
)
|
||||
db.session.add(system_message)
|
||||
|
||||
db.session.commit()
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'room': room.to_dict()
|
||||
}), 201
|
||||
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
current_app.logger.error(f"Error creating chat room: {str(e)}")
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': 'Failed to create chat room'
|
||||
}), 500
|
||||
|
||||
@chat_api.route('/rooms/<int:room_id>', methods=['GET'])
|
||||
@login_required
|
||||
def get_chat_room(room_id):
|
||||
"""Get chat room details"""
|
||||
room = ChatRoom.query.get_or_404(room_id)
|
||||
|
||||
# Check if user has access
|
||||
if room.is_private:
|
||||
participant = ChatParticipant.query.filter_by(
|
||||
room_id=room_id,
|
||||
user_id=current_user.id
|
||||
).first()
|
||||
if not participant:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': 'Access denied'
|
||||
}), 403
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'room': room.to_dict()
|
||||
})
|
||||
|
||||
@chat_api.route('/rooms/<int:room_id>/join', methods=['POST'])
|
||||
@login_required
|
||||
def join_chat_room(room_id):
|
||||
"""Join a chat room"""
|
||||
room = ChatRoom.query.get_or_404(room_id)
|
||||
|
||||
# Check if already a participant
|
||||
existing_participant = ChatParticipant.query.filter_by(
|
||||
room_id=room_id,
|
||||
user_id=current_user.id
|
||||
).first()
|
||||
|
||||
if existing_participant:
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'message': 'Already a member of this room'
|
||||
})
|
||||
|
||||
try:
|
||||
# Add user as participant
|
||||
participant = ChatParticipant(
|
||||
room_id=room_id,
|
||||
user_id=current_user.id,
|
||||
role='member'
|
||||
)
|
||||
db.session.add(participant)
|
||||
|
||||
# Add system message
|
||||
system_message = ChatMessage(
|
||||
room_id=room_id,
|
||||
user_id=current_user.id,
|
||||
content=f"{current_user.nickname} joined the chat",
|
||||
message_type='system'
|
||||
)
|
||||
db.session.add(system_message)
|
||||
|
||||
# Update room activity
|
||||
room.last_activity = datetime.utcnow()
|
||||
|
||||
db.session.commit()
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'message': 'Successfully joined the room'
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
current_app.logger.error(f"Error joining chat room: {str(e)}")
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': 'Failed to join room'
|
||||
}), 500
|
||||
|
||||
@chat_api.route('/rooms/<int:room_id>/messages', methods=['GET'])
|
||||
@login_required
|
||||
def get_chat_messages(room_id):
|
||||
"""Get messages from a chat room"""
|
||||
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:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': 'Access denied'
|
||||
}), 403
|
||||
|
||||
page = request.args.get('page', 1, type=int)
|
||||
per_page = request.args.get('per_page', 50, type=int)
|
||||
|
||||
# Get messages (newest first for mobile scrolling)
|
||||
messages = ChatMessage.query.filter_by(
|
||||
room_id=room_id,
|
||||
is_deleted=False
|
||||
).order_by(desc(ChatMessage.created_at)).paginate(
|
||||
page=page, per_page=per_page, error_out=False
|
||||
)
|
||||
|
||||
# Update user's last seen
|
||||
participant = ChatParticipant.query.filter_by(
|
||||
room_id=room_id,
|
||||
user_id=current_user.id
|
||||
).first()
|
||||
if participant:
|
||||
participant.last_seen = datetime.utcnow()
|
||||
db.session.commit()
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'messages': [msg.to_dict() for msg in reversed(messages.items)],
|
||||
'pagination': {
|
||||
'page': page,
|
||||
'pages': messages.pages,
|
||||
'per_page': per_page,
|
||||
'total': messages.total,
|
||||
'has_next': messages.has_next,
|
||||
'has_prev': messages.has_prev
|
||||
}
|
||||
})
|
||||
|
||||
@chat_api.route('/rooms/<int:room_id>/messages', methods=['POST'])
|
||||
@login_required
|
||||
def send_message(room_id):
|
||||
"""Send a message to a chat room"""
|
||||
room = ChatRoom.query.get_or_404(room_id)
|
||||
data = request.get_json()
|
||||
|
||||
if not data or not data.get('content'):
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': 'Message content is required'
|
||||
}), 400
|
||||
|
||||
content = data.get('content', '').strip()
|
||||
message_type = data.get('message_type', 'text')
|
||||
reply_to_id = data.get('reply_to_id')
|
||||
|
||||
# Validate content
|
||||
if len(content) < 1 or len(content) > 2000:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': 'Message must be between 1 and 2000 characters'
|
||||
}), 400
|
||||
|
||||
# Check for blocked content
|
||||
if contains_blocked_content(content):
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': 'Message contains inappropriate content'
|
||||
}), 400
|
||||
|
||||
# Check if user is participant
|
||||
participant = ChatParticipant.query.filter_by(
|
||||
room_id=room_id,
|
||||
user_id=current_user.id
|
||||
).first()
|
||||
|
||||
if not participant:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': 'You must join the room first'
|
||||
}), 403
|
||||
|
||||
if participant.is_muted:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': 'You are muted in this room'
|
||||
}), 403
|
||||
|
||||
try:
|
||||
# Create message
|
||||
message = ChatMessage(
|
||||
room_id=room_id,
|
||||
user_id=current_user.id,
|
||||
content=content,
|
||||
message_type=message_type,
|
||||
reply_to_id=reply_to_id
|
||||
)
|
||||
db.session.add(message)
|
||||
|
||||
# Update room activity
|
||||
room.last_activity = datetime.utcnow()
|
||||
|
||||
# Update participant last seen
|
||||
participant.last_seen = datetime.utcnow()
|
||||
|
||||
db.session.commit()
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'message': message.to_dict()
|
||||
}), 201
|
||||
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
current_app.logger.error(f"Error sending message: {str(e)}")
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': 'Failed to send message'
|
||||
}), 500
|
||||
|
||||
@chat_api.route('/rooms/<int:room_id>/participants', methods=['GET'])
|
||||
@login_required
|
||||
def get_room_participants(room_id):
|
||||
"""Get participants of a chat room"""
|
||||
room = ChatRoom.query.get_or_404(room_id)
|
||||
|
||||
participants = ChatParticipant.query.filter_by(room_id=room_id).all()
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'participants': [p.to_dict() for p in participants]
|
||||
})
|
||||
|
||||
@chat_api.route('/admin-support', methods=['POST'])
|
||||
@login_required
|
||||
def create_admin_support_chat():
|
||||
"""Create a chat room for admin support (e.g., password reset)"""
|
||||
data = request.get_json()
|
||||
reason = data.get('reason', 'general_support')
|
||||
description = data.get('description', '')
|
||||
|
||||
# Check if user already has an active admin support chat
|
||||
existing_room = ChatRoom.query.filter(
|
||||
and_(
|
||||
ChatRoom.room_type == 'admin_support',
|
||||
ChatRoom.created_by_id == current_user.id,
|
||||
ChatRoom.is_active == True
|
||||
)
|
||||
).first()
|
||||
|
||||
if existing_room:
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'room': existing_room.to_dict(),
|
||||
'message': 'Using existing support chat'
|
||||
})
|
||||
|
||||
try:
|
||||
# Create admin support room
|
||||
room_name = f"Support - {current_user.nickname} - {reason}"
|
||||
room = ChatRoom(
|
||||
name=room_name,
|
||||
description=f"Admin support chat for {current_user.nickname}. Reason: {reason}",
|
||||
room_type='admin_support',
|
||||
is_private=True,
|
||||
created_by_id=current_user.id
|
||||
)
|
||||
db.session.add(room)
|
||||
db.session.flush()
|
||||
|
||||
# Add user as participant
|
||||
participant = ChatParticipant(
|
||||
room_id=room.id,
|
||||
user_id=current_user.id,
|
||||
role='member'
|
||||
)
|
||||
db.session.add(participant)
|
||||
|
||||
# Add all admins as participants
|
||||
admins = User.query.filter_by(is_admin=True).all()
|
||||
for admin in admins:
|
||||
if admin.id != current_user.id:
|
||||
admin_participant = ChatParticipant(
|
||||
room_id=room.id,
|
||||
user_id=admin.id,
|
||||
role='admin'
|
||||
)
|
||||
db.session.add(admin_participant)
|
||||
|
||||
# Add initial message
|
||||
initial_message = ChatMessage(
|
||||
room_id=room.id,
|
||||
user_id=current_user.id,
|
||||
content=f"Hello! I need help with: {reason}. {description}",
|
||||
message_type='text'
|
||||
)
|
||||
db.session.add(initial_message)
|
||||
|
||||
db.session.commit()
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'room': room.to_dict()
|
||||
}), 201
|
||||
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
current_app.logger.error(f"Error creating admin support chat: {str(e)}")
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': 'Failed to create support chat'
|
||||
}), 500
|
||||
|
||||
@chat_api.route('/post/<int:post_id>/discussion', methods=['POST'])
|
||||
@login_required
|
||||
def create_post_discussion(post_id):
|
||||
"""Create or get discussion chat for a specific post"""
|
||||
post = Post.query.get_or_404(post_id)
|
||||
|
||||
# Check if discussion already exists
|
||||
existing_room = ChatRoom.query.filter(
|
||||
and_(
|
||||
ChatRoom.room_type == 'post_discussion',
|
||||
ChatRoom.related_post_id == post_id,
|
||||
ChatRoom.is_active == True
|
||||
)
|
||||
).first()
|
||||
|
||||
if existing_room:
|
||||
# Join the existing room if not already a participant
|
||||
participant = ChatParticipant.query.filter_by(
|
||||
room_id=existing_room.id,
|
||||
user_id=current_user.id
|
||||
).first()
|
||||
|
||||
if not participant:
|
||||
participant = ChatParticipant(
|
||||
room_id=existing_room.id,
|
||||
user_id=current_user.id,
|
||||
role='member'
|
||||
)
|
||||
db.session.add(participant)
|
||||
db.session.commit()
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'room': existing_room.to_dict(),
|
||||
'message': 'Joined existing discussion'
|
||||
})
|
||||
|
||||
try:
|
||||
# Create new discussion room
|
||||
room_name = f"Discussion: {post.title}"
|
||||
room = ChatRoom(
|
||||
name=room_name,
|
||||
description=f"Discussion about the post: {post.title}",
|
||||
room_type='post_discussion',
|
||||
created_by_id=current_user.id,
|
||||
related_post_id=post_id
|
||||
)
|
||||
db.session.add(room)
|
||||
db.session.flush()
|
||||
|
||||
# Add creator as participant
|
||||
participant = ChatParticipant(
|
||||
room_id=room.id,
|
||||
user_id=current_user.id,
|
||||
role='moderator'
|
||||
)
|
||||
db.session.add(participant)
|
||||
|
||||
# Add post author as participant if different
|
||||
if post.author_id != current_user.id:
|
||||
author_participant = ChatParticipant(
|
||||
room_id=room.id,
|
||||
user_id=post.author_id,
|
||||
role='moderator'
|
||||
)
|
||||
db.session.add(author_participant)
|
||||
|
||||
# Add initial message
|
||||
initial_message = ChatMessage(
|
||||
room_id=room.id,
|
||||
user_id=current_user.id,
|
||||
content=f"Started discussion about: {post.title}",
|
||||
message_type='system'
|
||||
)
|
||||
db.session.add(initial_message)
|
||||
|
||||
db.session.commit()
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'room': room.to_dict()
|
||||
}), 201
|
||||
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
current_app.logger.error(f"Error creating post discussion: {str(e)}")
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': 'Failed to create discussion'
|
||||
}), 500
|
||||
Reference in New Issue
Block a user