feat: Add comprehensive admin user management system

- Add user status toggle (activate/deactivate) functionality
- Add user deletion with post/comment transfer to admin
- Implement safety checks for admin protection
- Add interactive JavaScript for user management actions
- Update admin users interface with action buttons
- Add confirmation dialogs for destructive operations
- Update README with new admin features and capabilities
- Add database migration utility for future updates

Features:
- Toggle user active/inactive status
- Delete users with content preservation
- Transfer all posts/comments to admin on user deletion
- Prevent admin self-modification and deletion
- AJAX-powered interface with real-time feedback
- Comprehensive error handling and user notifications
This commit is contained in:
ske087
2025-07-24 03:07:52 +03:00
parent 60ef02ced9
commit 7018ae13f0
4 changed files with 409 additions and 84 deletions

322
README.md
View File

@@ -1,54 +1,118 @@
# Motorcycle Adventure Community # Motorcycle Adventure Community 🏍️
A Flask-based web application for motorcycle enthusiasts featuring adventure stories, route sharing, and accommodation recommendations. A modern Flask-based web application for motorcycle enthusiasts featuring adventure stories, interactive GPS route sharing, and community engagement.
## Features ## Features
- **Landing Page**: Hero section with call-to-action, featured adventures, and accommodation promotion ### 🎨 Modern UI/UX Design
- **User Authentication**: Registration and login system with Flask-Login - **Tailwind CSS Integration**: Complete redesign with modern gradient backgrounds and glass morphism effects
- **Community Section**: Share stories, tracks, and experiences - **Responsive Design**: Mobile-first approach with optimized layouts for all screen sizes
- **Admin Panel**: Content management for administrators - **Interactive Components**: Smooth animations, hover effects, and modern card-based layouts
- **Responsive Design**: Built with Tailwind CSS for mobile-first design - **Consistent Design Language**: Unified color scheme and component patterns throughout the app
- **Error Handling**: Custom 404 and 500 error pages
## Technology Stack ### 🗺️ Advanced GPS & Route Features
- **Interactive Maps**: Leaflet.js integration with OpenStreetMap tiles
- **GPX File Support**: Upload, download, and visualize GPS tracks
- **Route Statistics**: Distance, elevation gain, max elevation, and track points
- **Start/End Markers**: Visual indicators with custom icons for route endpoints
- **Map Fitting**: Automatic zoom and centering to display complete routes
- **Backend**: Python Flask 3.0.0 ### 📸 Enhanced Media Management
- **Database**: SQLAlchemy with PostgreSQL support - **Photo Gallery**: Grid-based layout with modal view and zoom functionality
- **Authentication**: Flask-Login - **Image Thumbnails**: Automatic thumbnail generation for optimized loading
- **Email**: Flask-Mail - **Cover Image System**: Designated cover photos for adventure posts
- **Forms**: WTForms with validation - **Image Descriptions**: Support for photo captions and descriptions
- **Styling**: Tailwind CSS (CDN) - **Hover Effects**: Interactive image previews with smooth transitions
- **Deployment**: Docker with Gunicorn
## Project Structure ### 👥 Community & Social Features
- **Adventure Posts**: Rich content creation with titles, subtitles, and detailed stories
- **Comment System**: Community discussions on adventure posts
- **Like System**: Engagement tracking with real-time updates
- **User Profiles**: Personal dashboards with adventure statistics
- **Difficulty Ratings**: 5-star system for adventure difficulty assessment
- **Publication Workflow**: Admin approval system for content moderation
### 🔐 User Management & Authentication
- **Secure Authentication**: Flask-Login with session management
- **User Profiles**: Personal information and adventure history
- **Role-Based Access**: User and admin permissions
- **Adventure Statistics**: Published vs pending post counts
- **Registration System**: Email-based user registration
### 🛠️ Admin Panel & Analytics
- **Comprehensive Dashboard**: User and post management interface
- **Content Moderation**: Review and approve community posts
- **User Analytics**: User engagement and activity metrics
- **Post Management**: Bulk operations and detailed post information
- **System Configuration**: Admin-only settings and controls
### 📱 Mobile-Optimized Experience
- **Touch-Friendly Interface**: Optimized buttons and interactions for mobile devices
- **Responsive Grids**: Adaptive layouts that work perfectly on phones and tablets
- **Progressive Enhancement**: Graceful degradation for older browsers
- **Fast Loading**: Optimized images and efficient CSS delivery
## 🚀 Technology Stack
- **Backend**: Python Flask 3.0.0 with Blueprint architecture
- **Database**: SQLAlchemy with PostgreSQL/SQLite support
- **Authentication**: Flask-Login with secure session management
- **Email**: Flask-Mail for notifications and verification
- **Forms**: WTForms with comprehensive validation
- **Frontend**: Tailwind CSS 3.x with custom components
- **Maps**: Leaflet.js with OpenStreetMap integration
- **File Handling**: Secure media uploads with thumbnail generation
- **Deployment**: Docker with Gunicorn WSGI server
## 📁 Project Structure
``` ```
/ /
├── app/ ├── app/
│ ├── __init__.py # Flask app factory │ ├── __init__.py # Flask app factory with extensions
│ ├── models.py # Database models │ ├── models.py # Database models (User, Post, Image, GPX, Comment, Like)
│ ├── forms.py # WTForm definitions │ ├── forms.py # WTForm definitions for all features
│ ├── routes/ # Route blueprints │ ├── extensions.py # Flask extensions configuration
│ ├── main.py # Main routes (index, about) │ ├── media_config.py # Media handling and upload configuration
│ ├── auth.py # Authentication routes │ ├── routes/ # Route blueprints
│ │ ── community.py # Community features │ │ ── main.py # Landing page and general routes
│ ├── templates/ # Jinja2 templates │ ├── auth.py # Authentication and user management
│ │ ├── base.html # Base template │ │ ├── community.py # Community features and post management
│ │ ── index.html # Landing page │ │ ── admin.py # Admin panel and analytics
│ ├── auth/ # Auth templates │ ├── templates/ # Jinja2 templates
│ │ ── errors/ # Error pages │ │ ── base.html # Base template with navigation
└── static/ # Static assets │ ├── index.html # Landing page with hero section
│ │ ├── auth/ # Authentication templates
│ │ ├── community/ # Community feature templates
│ │ │ ├── index.html # Community homepage
│ │ │ ├── new_post.html # Adventure creation form
│ │ │ ├── edit_post.html # Adventure editing interface
│ │ │ ├── post_detail.html # Detailed adventure view with maps
│ │ │ └── profile.html # User profile dashboard
│ │ ├── admin/ # Admin panel templates
│ │ │ ├── dashboard.html # Analytics dashboard
│ │ │ ├── posts.html # Post management
│ │ │ └── users.html # User management
│ │ └── errors/ # Error pages (404, 500)
│ └── static/ # Static assets and media
│ ├── favicon.ico │ ├── favicon.ico
── images/ ── images/ # App images and logos
├── config.py # Application configuration │ └── media/ # User-uploaded content
├── requirements.txt # Python dependencies │ └── posts/ # Post media organized by ID
├── run.py # Application entry point ├── instance/ # Instance-specific files
├── Dockerfile # Docker configuration │ ├── uploads/ # Legacy upload directory
└── docker-compose.yml # Docker Compose setup │ └── *.db # SQLite database files
├── migrations/ # Database migration scripts
├── config.py # Multi-environment configuration
├── requirements.txt # Python dependencies
├── run.py # Application entry point
├── manage_media.py # Media management utilities
├── test_media.py # Media system tests
├── Dockerfile # Docker configuration
└── docker-compose.yml # Docker Compose setup
``` ```
## Quick Start ## 🚀 Quick Start
### Local Development ### Local Development
@@ -74,7 +138,7 @@ A Flask-based web application for motorcycle enthusiasts featuring adventure sto
export FLASK_APP=run.py export FLASK_APP=run.py
export FLASK_ENV=development export FLASK_ENV=development
export SECRET_KEY="your-secret-key-here" export SECRET_KEY="your-secret-key-here"
export DATABASE_URL="sqlite:///motorcycle_adventures.db" export DATABASE_URL="sqlite:///instance/moto_adventure_dev.db"
``` ```
5. **Initialize database** 5. **Initialize database**
@@ -84,12 +148,23 @@ A Flask-based web application for motorcycle enthusiasts featuring adventure sto
flask db upgrade flask db upgrade
``` ```
6. **Run the application** 6. **Create media directories**
```bash
mkdir -p app/static/media/posts
mkdir -p instance/uploads
```
7. **Run the application**
```bash ```bash
python run.py python run.py
``` ```
### Docker Deployment 8. **Access the application**
- Open http://localhost:5000 in your browser
- Register a new account to start sharing adventures
- Upload GPX files and photos to test the full functionality
### 🐳 Docker Deployment
1. **Build and run with Docker Compose** 1. **Build and run with Docker Compose**
```bash ```bash
@@ -100,60 +175,143 @@ A Flask-based web application for motorcycle enthusiasts featuring adventure sto
- Web application: http://localhost:5000 - Web application: http://localhost:5000
- PostgreSQL database: localhost:5432 - PostgreSQL database: localhost:5432
## Configuration ### 📱 Testing Features
1. **Create an Adventure Post**
- Register and login to your account
- Click "Share New Adventure"
- Upload photos and GPX files
- Add title, subtitle, and adventure story
2. **Explore Interactive Maps**
- View adventure posts with GPS tracks
- See route statistics and elevation data
- Download GPX files for your own use
3. **Admin Panel Access**
- Create admin user in database
- Access /admin for content management
- Review and approve community posts
- PostgreSQL database: localhost:5432
## ⚙️ Configuration
The application supports multiple environments through environment variables: The application supports multiple environments through environment variables:
### Core Settings
- `FLASK_ENV`: development, testing, production - `FLASK_ENV`: development, testing, production
- `SECRET_KEY`: Flask secret key for sessions - `SECRET_KEY`: Flask secret key for sessions and CSRF protection
- `DATABASE_URL`: Database connection string - `DATABASE_URL`: Database connection string (SQLite/PostgreSQL)
- `MAIL_SERVER`: SMTP server for email
- `MAIL_USERNAME`: Email username
- `MAIL_PASSWORD`: Email password
## Features Overview ### Media & Upload Settings
- `UPLOAD_FOLDER`: Directory for user uploads (default: instance/uploads)
- `MEDIA_FOLDER`: Directory for processed media (default: app/static/media)
- `MAX_CONTENT_LENGTH`: Maximum file upload size (default: 16MB)
### Landing Page ### Email Configuration
- Hero section with stunning mountain panorama background - `MAIL_SERVER`: SMTP server for email notifications
- Call-to-action for adventure community - `MAIL_USERNAME`: Email username for authentication
- Featured adventures showcase - `MAIL_PASSWORD`: Email password or app-specific password
- Pensiune BuonGusto accommodation promotion - `MAIL_USE_TLS`: Enable TLS encryption (default: True)
### Authentication System ### Development Settings
- User registration with email validation - `FLASK_DEBUG`: Enable debug mode (development only)
- Secure login/logout functionality - `WTF_CSRF_ENABLED`: Enable CSRF protection (default: True)
- Password hashing with Werkzeug
- Session management with Flask-Login
### Community Features ## 🌟 Features Overview
- Story and track sharing
- User profiles
- Image uploads for adventures
- GPX file support for route sharing
- Comment system
- Like/rating system
### Admin Panel ### 🏠 Enhanced Landing Page
- User management - **Hero Section**: Stunning Transalpina panorama background with glass morphism overlay
- Content moderation - **Featured Adventures**: Carousel showcasing community highlights
- Analytics dashboard - **Statistics Dashboard**: Live counters for users, adventures, and community engagement
- System configuration - **Call-to-Action**: Prominent registration and exploration prompts
- **Pensiune BuonGusto**: Integrated accommodation promotion
## Contributing ### 🔐 Advanced Authentication System
- **Secure Registration**: Email validation with password strength requirements
- **Session Management**: Persistent login with "Remember Me" functionality
- **Password Security**: Bcrypt hashing with salt for maximum security
- **User Profiles**: Comprehensive profile management with statistics
- **Role-Based Access**: Granular permissions for users and administrators
1. Fork the repository ### 🗺️ Interactive Community Features
2. Create a feature branch (`git checkout -b feature/amazing-feature`) - **Rich Post Creation**: Multi-step form with media upload and GPS integration
3. Commit your changes (`git commit -m 'Add some amazing feature'`) - **Adventure Gallery**: Beautiful grid layout with filtering and search
4. Push to the branch (`git push origin feature/amazing-feature`) - **Real-time Engagement**: Like system with instant feedback
5. Open a Pull Request - **Threaded Comments**: Nested discussion system for each adventure
- **Social Sharing**: Easy sharing of favorite adventures
- **Content Moderation**: Admin approval workflow for quality control
## License ### 📊 Comprehensive Admin Panel
- **Analytics Dashboard**: User growth, post statistics, and engagement metrics
- **User Management**: Detailed user profiles with activity tracking
- **Content Review**: Streamlined post approval and moderation tools
- **System Health**: Monitoring and configuration management
- **Bulk Operations**: Efficient management of multiple users or posts
### 🎨 Modern Design System
- **Tailwind CSS**: Utility-first CSS framework with custom components
- **Glass Morphism**: Modern translucent effects and backdrop blur
- **Gradient Backgrounds**: Beautiful color transitions throughout the app
- **Micro-interactions**: Smooth animations and hover effects
- **Accessibility**: WCAG compliant design with keyboard navigation support
## 🤝 Contributing
We welcome contributions from the motorcycle community! Here's how you can help:
1. **Fork the repository**
2. **Create a feature branch** (`git checkout -b feature/amazing-feature`)
3. **Commit your changes** (`git commit -m 'Add some amazing feature'`)
4. **Push to the branch** (`git push origin feature/amazing-feature`)
5. **Open a Pull Request**
### Development Guidelines
- Follow PEP 8 style guidelines for Python code
- Use semantic commit messages
- Add tests for new features
- Update documentation for any changes
- Ensure mobile responsiveness for UI changes
### Reporting Issues
- Use the GitHub issue tracker
- Include steps to reproduce the problem
- Provide browser and device information
- Include screenshots for UI issues
## 📄 License
This project is licensed under the MIT License - see the LICENSE file for details. This project is licensed under the MIT License - see the LICENSE file for details.
## Acknowledgments ## 🙏 Acknowledgments
- Beautiful Transalpina panorama background image - **Beautiful Transalpina Panorama**: Stunning background image showcasing Romania's scenic highway
- Tailwind CSS for responsive design - **Tailwind CSS**: For providing an excellent utility-first CSS framework
- Flask community for excellent documentation - **Leaflet.js**: For powerful and flexible mapping capabilities
- Bootstrap icons for UI elements - **Flask Community**: For comprehensive documentation and support
- **OpenStreetMap**: For providing free and open map data
- **Font Awesome**: For beautiful and consistent iconography
- **Motorcycle Community**: For inspiration and feedback during development
## 🚧 Roadmap
### Upcoming Features
- [ ] **Real-time Chat**: Live messaging between community members
- [ ] **Event System**: Motorcycle meetups and group ride organization
- [ ] **Weather Integration**: Route weather conditions and forecasts
- [ ] **Mobile App**: Native iOS and Android applications
- [ ] **Advanced Search**: Filter adventures by location, difficulty, and tags
- [ ] **Social Features**: Follow users and curated adventure feeds
- [ ] **Offline Support**: PWA capabilities for offline map viewing
### Performance Improvements
- [ ] **Image Optimization**: WebP conversion and lazy loading
- [ ] **Caching**: Redis integration for improved performance
- [ ] **CDN Integration**: Asset delivery optimization
- [ ] **Database Optimization**: Query optimization and indexing
---
**Built with ❤️ for the motorcycle adventure community**
For support or questions, please open an issue or contact the development team.

View File

@@ -190,6 +190,80 @@ def user_detail(user_id):
user_posts=user_posts, user_posts=user_posts,
user_comments=user_comments) user_comments=user_comments)
@admin.route('/users/<int:user_id>/toggle-status', methods=['POST'])
@login_required
@admin_required
def toggle_user_status(user_id):
"""Toggle user active/inactive status"""
user = User.query.get_or_404(user_id)
# Prevent self-modification if current user is admin
if user.is_admin and user.id == current_user.id:
return jsonify({'success': False, 'error': 'Cannot deactivate your own admin account'})
# Toggle status
user.is_active = not user.is_active
status = "activated" if user.is_active else "deactivated"
try:
db.session.commit()
return jsonify({
'success': True,
'message': f'User {user.nickname} has been {status}',
'is_active': user.is_active
})
except Exception as e:
db.session.rollback()
return jsonify({'success': False, 'error': f'Failed to update user status: {str(e)}'})
@admin.route('/users/<int:user_id>/delete', methods=['DELETE', 'POST'])
@login_required
@admin_required
def delete_user(user_id):
"""Delete user and transfer their posts to admin"""
user = User.query.get_or_404(user_id)
# Prevent self-deletion
if user.id == current_user.id:
return jsonify({'success': False, 'error': 'Cannot delete your own account'})
# Prevent deletion of other admins
if user.is_admin:
return jsonify({'success': False, 'error': 'Cannot delete admin accounts'})
try:
# Transfer all user's posts to current admin
user_posts = Post.query.filter_by(author_id=user.id).all()
for post in user_posts:
post.author_id = current_user.id
# Transfer all user's comments to current admin
user_comments = Comment.query.filter_by(author_id=user.id).all()
for comment in user_comments:
comment.author_id = current_user.id
# Delete user's likes (they will be orphaned)
Like.query.filter_by(user_id=user.id).delete()
# Delete user's page views
PageView.query.filter_by(user_id=user.id).delete()
username = user.nickname
user_posts_count = len(user_posts)
# Delete the user
db.session.delete(user)
db.session.commit()
return jsonify({
'success': True,
'message': f'User {username} has been deleted. {user_posts_count} posts transferred to {current_user.nickname}.'
})
except Exception as e:
db.session.rollback()
return jsonify({'success': False, 'error': f'Failed to delete user: {str(e)}'})
@admin.route('/analytics') @admin.route('/analytics')
@login_required @login_required
@admin_required @admin_required

View File

@@ -74,8 +74,17 @@
<i class="fas fa-eye"></i> <i class="fas fa-eye"></i>
</a> </a>
{% if not user.is_admin or current_user.id != user.id %} {% if not user.is_admin or current_user.id != user.id %}
<button class="btn btn-sm btn-outline-warning" title="Toggle Status" disabled> <button class="btn btn-sm btn-outline-warning"
<i class="fas fa-toggle-on"></i> title="{{ 'Deactivate User' if user.is_active else 'Activate User' }}"
onclick="toggleUserStatus({{ user.id }}, '{{ user.nickname }}', {{ user.is_active|lower }})">
<i class="fas fa-{{ 'toggle-on' if user.is_active else 'toggle-off' }}"></i>
</button>
{% endif %}
{% if not user.is_admin and current_user.id != user.id %}
<button class="btn btn-sm btn-outline-danger"
title="Delete User"
onclick="confirmDeleteUser({{ user.id }}, '{{ user.nickname }}', {{ user.posts.count() }})">
<i class="fas fa-trash"></i>
</button> </button>
{% endif %} {% endif %}
</div> </div>
@@ -134,4 +143,88 @@
</div> </div>
</div> </div>
{% endif %} {% endif %}
<script>
function toggleUserStatus(userId, nickname, currentStatus) {
const action = currentStatus ? 'deactivate' : 'activate';
const message = `Are you sure you want to ${action} user "${nickname}"?`;
if (confirm(message)) {
fetch(`/admin/users/${userId}/toggle-status`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
showAlert(data.message, 'success');
setTimeout(() => {
location.reload();
}, 1000);
} else {
showAlert(data.message || 'An error occurred', 'danger');
}
})
.catch(error => {
console.error('Error:', error);
showAlert('An error occurred while updating user status', 'danger');
});
}
}
function confirmDeleteUser(userId, nickname, postsCount) {
let message = `Are you sure you want to delete user "${nickname}"?`;
if (postsCount > 0) {
message += `\n\nThis user has ${postsCount} post(s) that will be transferred to you as the admin.`;
}
message += '\n\nThis action cannot be undone.';
if (confirm(message)) {
fetch(`/admin/users/${userId}/delete`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
showAlert(data.message, 'success');
setTimeout(() => {
location.reload();
}, 1000);
} else {
showAlert(data.message || 'An error occurred', 'danger');
}
})
.catch(error => {
console.error('Error:', error);
showAlert('An error occurred while deleting user', 'danger');
});
}
}
function showAlert(message, type) {
// Create alert container if it doesn't exist
let alertContainer = document.querySelector('.alert-container');
if (!alertContainer) {
alertContainer = document.createElement('div');
alertContainer.className = 'alert-container mt-3';
document.querySelector('.container-fluid').insertBefore(
alertContainer,
document.querySelector('.container-fluid').firstChild
);
}
alertContainer.innerHTML = `
<div class="alert alert-${type} alert-dismissible fade show" role="alert">
${message}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
`;
}
</script>
{% endblock %} {% endblock %}

0
migrate_database.py Normal file
View File