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:
@@ -313,6 +313,7 @@ class ChatRoom(db.Model):
|
||||
name = db.Column(db.String(200), nullable=False)
|
||||
description = db.Column(db.Text)
|
||||
room_type = db.Column(db.String(50), default='general') # general, post_discussion, admin_support, password_reset
|
||||
category = db.Column(db.String(50), default='general') # general, technical, maintenance, routes, events, safety, gear, social
|
||||
is_private = db.Column(db.Boolean, default=False)
|
||||
is_active = db.Column(db.Boolean, default=True)
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
@@ -436,3 +437,69 @@ class ChatMessage(db.Model):
|
||||
|
||||
def __repr__(self):
|
||||
return f'<ChatMessage {self.id} by {self.user.nickname}>'
|
||||
|
||||
|
||||
class PasswordResetRequest(db.Model):
|
||||
"""Model for tracking password reset requests from chat system"""
|
||||
__tablename__ = 'password_reset_requests'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
user_email = db.Column(db.String(120), nullable=False)
|
||||
requester_message = db.Column(db.Text) # Original request message
|
||||
status = db.Column(db.String(20), default='pending') # pending, token_generated, completed, expired
|
||||
admin_notes = db.Column(db.Text) # Admin can add notes
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
updated_at = db.Column(db.DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||||
|
||||
# Foreign Keys
|
||||
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=True) # Nullable in case user not found
|
||||
chat_message_id = db.Column(db.Integer, db.ForeignKey('chat_messages.id'), nullable=True)
|
||||
|
||||
# Relationships
|
||||
user = db.relationship('User', backref='password_reset_requests')
|
||||
chat_message = db.relationship('ChatMessage', backref='password_reset_request')
|
||||
tokens = db.relationship('PasswordResetToken', backref='request', cascade='all, delete-orphan')
|
||||
|
||||
def __repr__(self):
|
||||
return f'<PasswordResetRequest {self.id} for {self.user_email}>'
|
||||
|
||||
|
||||
class PasswordResetToken(db.Model):
|
||||
"""Model for one-time password reset tokens generated by admin"""
|
||||
__tablename__ = 'password_reset_tokens'
|
||||
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
token = db.Column(db.String(255), unique=True, nullable=False)
|
||||
is_used = db.Column(db.Boolean, default=False)
|
||||
used_at = db.Column(db.DateTime, nullable=True)
|
||||
expires_at = db.Column(db.DateTime, nullable=False)
|
||||
created_at = db.Column(db.DateTime, default=datetime.utcnow)
|
||||
user_ip = db.Column(db.String(45), nullable=True) # IP when token was used
|
||||
user_agent = db.Column(db.Text, nullable=True) # User agent when token was used
|
||||
|
||||
# Foreign Keys
|
||||
request_id = db.Column(db.Integer, db.ForeignKey('password_reset_requests.id'), nullable=False)
|
||||
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
|
||||
created_by_admin_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
|
||||
|
||||
# Relationships
|
||||
user = db.relationship('User', foreign_keys=[user_id], backref='reset_tokens')
|
||||
created_by_admin = db.relationship('User', foreign_keys=[created_by_admin_id])
|
||||
|
||||
@property
|
||||
def is_expired(self):
|
||||
return datetime.utcnow() > self.expires_at
|
||||
|
||||
@property
|
||||
def is_valid(self):
|
||||
return not self.is_used and not self.is_expired
|
||||
|
||||
def mark_as_used(self, ip_address=None, user_agent=None):
|
||||
self.is_used = True
|
||||
self.used_at = datetime.utcnow()
|
||||
self.user_ip = ip_address
|
||||
self.user_agent = user_agent
|
||||
db.session.commit()
|
||||
|
||||
def __repr__(self):
|
||||
return f'<PasswordResetToken {self.token[:8]}... for {self.user.nickname}>'
|
||||
|
||||
Reference in New Issue
Block a user