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:
ske087
2025-08-10 00:22:33 +03:00
parent 1661f5f588
commit 30bd4c62ad
20 changed files with 3649 additions and 349 deletions

View File

@@ -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}>'