diff --git a/motm_app/=21.0.0 b/motm_app/=21.0.0 new file mode 100644 index 0000000..84a4bf6 --- /dev/null +++ b/motm_app/=21.0.0 @@ -0,0 +1,3 @@ +Defaulting to user installation because normal site-packages is not writeable +Requirement already satisfied: gunicorn in /home/jonny/.local/lib/python3.13/site-packages (21.2.0) +Requirement already satisfied: packaging in /usr/lib/python3.13/site-packages (from gunicorn) (24.2) diff --git a/motm_app/PRODUCTION_DEPLOYMENT.md b/motm_app/PRODUCTION_DEPLOYMENT.md new file mode 100644 index 0000000..b6675fe --- /dev/null +++ b/motm_app/PRODUCTION_DEPLOYMENT.md @@ -0,0 +1,160 @@ +# Production Deployment Guide + +## WSGI Server Setup + +Your Flask application now uses **Gunicorn** as the production WSGI server instead of the Flask development server. + +### Files Added: +- `gunicorn.conf.py` - Gunicorn configuration file +- `run_production.py` - WSGI entry point +- `start_production.sh` - Production startup script +- `motm-app.service` - Systemd service file (optional) + +## Quick Start (Recommended) + +### 1. Install Dependencies +```bash +pip install -r requirements.txt +``` + +### 2. Start Production Server +```bash +# Option A: Use the startup script (easiest) +./start_production.sh + +# Option B: Start manually +gunicorn -c gunicorn.conf.py run_production:app + +# Option C: Quick start with default settings +gunicorn --bind 0.0.0.0:5000 --workers 4 run_production:app +``` + +## Configuration Details + +### Gunicorn Configuration (`gunicorn.conf.py`) +- **Workers**: `CPU_COUNT * 2 + 1` (automatically calculated) +- **Worker Class**: `sync` (good for Flask applications) +- **Timeout**: 30 seconds +- **Max Requests**: 1000 per worker (prevents memory leaks) +- **Logging**: Standard output (can be redirected) +- **Preload**: Enabled for better performance + +### Environment Variables +- `FLASK_ENV=production` (automatically set) + +## Alternative WSGI Servers + +If you prefer different WSGI servers: + +### 1. uWSGI +```bash +pip install uwsgi +uwsgi --http 0.0.0.0:5000 --module run_production:app --processes 4 --threads 2 +``` + +### 2. Waitress (Windows-friendly) +```bash +pip install waitress +waitress-serve --host=0.0.0.0 --port=5000 run_production:app +``` + +### 3. Gevent (for async workloads) +```bash +pip install gunicorn[gevent] +gunicorn -c gunicorn.conf.py --worker-class gevent run_production:app +``` + +## Production Recommendations + +### 1. Use a Reverse Proxy +Place Nginx or Apache in front of Gunicorn: + +**Nginx Example:** +```nginx +server { + listen 80; + server_name your-domain.com; + + location / { + proxy_pass http://127.0.0.1:5000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } +} +``` + +### 2. SSL/HTTPS +For production, always use HTTPS: +- Use Let's Encrypt for free SSL certificates +- Configure SSL in your reverse proxy +- Update Gunicorn config with SSL settings if needed + +### 3. Process Management +Use systemd or supervisor to manage the Gunicorn process: + +**Systemd (Linux):** +```bash +sudo cp motm-app.service /etc/systemd/system/ +sudo systemctl enable motm-app +sudo systemctl start motm-app +sudo systemctl status motm-app +``` + +### 4. Monitoring +- Monitor worker processes and memory usage +- Set up log rotation +- Use tools like Prometheus + Grafana for metrics + +## Performance Tuning + +### Worker Count +- **CPU-bound**: `workers = CPU_COUNT * 2` +- **I/O-bound**: `workers = CPU_COUNT * 2 + 1` (current setting) +- **High concurrency**: Consider async workers (gevent/eventlet) + +### Memory Management +- Current: `max_requests = 1000` (restarts workers periodically) +- Adjust based on your memory constraints + +### Timeout Settings +- Current: `timeout = 30` seconds +- Increase if you have long-running requests + +## Troubleshooting + +### Common Issues: +1. **Permission denied**: Check file permissions and user/group settings +2. **Port already in use**: Change port in `gunicorn.conf.py` or kill existing process +3. **Memory issues**: Reduce worker count or increase `max_requests` +4. **Slow responses**: Increase timeout or worker count + +### Logs: +- Gunicorn logs to stdout/stderr by default +- Check systemd logs: `journalctl -u motm-app` +- Redirect logs to files if needed + +## Security Notes + +1. **Never run as root** in production +2. **Use HTTPS** for all production traffic +3. **Set proper file permissions** on your application files +4. **Keep dependencies updated** regularly +5. **Use environment variables** for sensitive configuration + +## Migration from Development + +Your application is now ready for production! The key changes: +- ✅ Gunicorn WSGI server instead of Flask dev server +- ✅ Production-optimized configuration +- ✅ Proper worker management +- ✅ Security improvements +- ✅ Performance optimizations + +**Next Steps:** +1. Test the production setup locally +2. Deploy to your production server +3. Set up reverse proxy (Nginx/Apache) +4. Configure SSL certificates +5. Set up monitoring and logging diff --git a/motm_app/gunicorn.conf.py b/motm_app/gunicorn.conf.py new file mode 100644 index 0000000..a286777 --- /dev/null +++ b/motm_app/gunicorn.conf.py @@ -0,0 +1,52 @@ +# Gunicorn configuration file for production deployment + +import multiprocessing +import os + +# Server socket +bind = "0.0.0.0:5000" +backlog = 2048 + +# Worker processes +workers = multiprocessing.cpu_count() * 2 + 1 +worker_class = "sync" +worker_connections = 1000 +timeout = 30 +keepalive = 2 + +# Restart workers after this many requests, to prevent memory leaks +max_requests = 1000 +max_requests_jitter = 50 + +# Logging +accesslog = "-" +errorlog = "-" +loglevel = "info" +access_log_format = '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s" %(D)s' + +# Process naming +proc_name = "motm_app" + +# Server mechanics +daemon = False +pidfile = "/tmp/motm_app.pid" +user = None +group = None +tmp_upload_dir = None + +# SSL (uncomment and configure if using HTTPS) +# keyfile = "/path/to/keyfile" +# certfile = "/path/to/certfile" + +# Preload app for better performance +preload_app = True + +# Environment variables +raw_env = [ + 'FLASK_ENV=production', +] + +# Security +limit_request_line = 4094 +limit_request_fields = 100 +limit_request_field_size = 8190 diff --git a/motm_app/motm-app.service b/motm_app/motm-app.service new file mode 100644 index 0000000..a1aed22 --- /dev/null +++ b/motm_app/motm-app.service @@ -0,0 +1,18 @@ +[Unit] +Description=MOTM App - Man of the Match Voting System +After=network.target + +[Service] +Type=exec +User=www-data +Group=www-data +WorkingDirectory=/home/jonny/Projects/gcp-hockey-results/motm_app +Environment=PATH=/home/jonny/Projects/gcp-hockey-results/motm_app/venv/bin +Environment=FLASK_ENV=production +ExecStart=/home/jonny/Projects/gcp-hockey-results/motm_app/venv/bin/gunicorn -c gunicorn.conf.py run_production:app +ExecReload=/bin/kill -s HUP $MAINPID +Restart=always +RestartSec=5 + +[Install] +WantedBy=multi-user.target diff --git a/motm_app/requirements.txt b/motm_app/requirements.txt index 2a7018c..83a137d 100644 --- a/motm_app/requirements.txt +++ b/motm_app/requirements.txt @@ -29,3 +29,6 @@ boto3>=1.34.0 # Legacy support (can be removed after migration) flask-mysql + +# Production WSGI server +gunicorn>=21.0.0 diff --git a/motm_app/run_production.py b/motm_app/run_production.py new file mode 100644 index 0000000..af02863 --- /dev/null +++ b/motm_app/run_production.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python3 +""" +Production WSGI entry point for MOTM App +This file is used by Gunicorn to run the application in production +""" + +from main import app + +if __name__ == "__main__": + # This should not be called directly in production + # Use: gunicorn -c gunicorn.conf.py run_production:app + app.run(host='0.0.0.0', port=5000, debug=False) diff --git a/motm_app/start_production.sh b/motm_app/start_production.sh new file mode 100755 index 0000000..5ffd07b --- /dev/null +++ b/motm_app/start_production.sh @@ -0,0 +1,26 @@ +#!/bin/bash +# Production startup script for MOTM App + +echo "Starting MOTM App in production mode..." + +# Check if virtual environment exists +if [ ! -d "venv" ]; then + echo "Creating virtual environment..." + python3 -m venv venv +fi + +# Activate virtual environment +echo "Activating virtual environment..." +source venv/bin/activate + +# Install/upgrade dependencies +echo "Installing dependencies..." +pip install --upgrade pip +pip install -r requirements.txt + +# Set production environment +export FLASK_ENV=production + +# Start Gunicorn +echo "Starting Gunicorn WSGI server..." +gunicorn -c gunicorn.conf.py run_production:app diff --git a/motm_app/templates/admin_dashboard.html b/motm_app/templates/admin_dashboard.html index beb1a80..fad0f81 100644 --- a/motm_app/templates/admin_dashboard.html +++ b/motm_app/templates/admin_dashboard.html @@ -1,219 +1,280 @@ - - -
- - -Central hub for all administrative functions
-Central hub for all administrative functions
+Manage hockey clubs, logos, and club information.
+Add, edit, and manage player information and squads.
+ +Manage hockey teams and their associations with clubs.
+Configure and manage S3 storage for assets and logos.
+ +Manage database configuration and status.
+ +Manage match squads and player selections.
+ +Central hub for all administrative functions
+Manage hockey clubs, logos, and club information.
+Add, edit, and manage player information and squads.
+ +Manage hockey teams and their associations with clubs.
+Configure and manage S3 storage for assets and logos.
+ +Manage database configuration and status.
+ +Manage match squads and player selections.
+ +Central hub for all administrative functions
+{{ page_subtitle }}
+ {% endif %} +{{ message or "Invalid voting URL. Please check the link and try again." }}
- Home +{% extends "base.html" %} + +{% block title %}Error - HKFC MOTM System{% endblock %} + +{% block content %} +{{ message or "Invalid voting URL. Please check the link and try again." }}
+{{ message or "Invalid voting URL. Please check the link and try again." }}
+This system allows players to vote for Man of the Match and Dick of the Day, while providing admin tools for managing matches and squads.
+{% extends "base.html" %} + +{% block title %}HKFC Men's C Team - MOTM System{% endblock %} + +{% block content %} ++ This system allows players to vote for Man of the Match and Dick of the Day, + while providing admin tools for managing matches and squads. +
+Admin functions require authentication. Please contact the system administrator for access.
+View comments from recent matches
- + - - {% if is_admin %} -Access all administrative functions
- - -Add, edit, and manage players in the database
- - -Add, edit, and manage hockey clubs
- - -Add, edit, and manage hockey teams
- - -Import clubs and teams from Hong Kong Hockey Association
- - -Select players for the match squad
- - -View current match squad
- - -Reset squad for new match
- - -Manage match settings and activate voting
- - -Record goals and assists statistics
- - -View current match voting results
- - -View season totals and Player of the Year standings
- - -Configure and initialize the database
- - -View current database configuration and status
- - -Configure AWS S3 storage for logos and assets
- - -View current S3 configuration and connection status
- + +Admin functions require authentication. Please contact the system administrator for access.
+ ++ + System Online +
+2024-2025 Hockey Season
++ This system allows players to vote for Man of the Match and Dick of the Day, + while providing admin tools for managing matches and squads. +
+Admin functions require authentication. Please contact the system administrator for access.
++ + System Online +
+2024-2025 Hockey Season
++ This system allows players to vote for Man of the Match and Dick of the Day, + while providing admin tools for managing matches and squads. +
+Access all administrative functions
+ + +Add, edit, and manage players in the database
+ + +Add, edit, and manage hockey clubs
+ + +Add, edit, and manage hockey teams
+ + +Import clubs and teams from Hong Kong Hockey Association
+ + +Select players for the match squad
+ + +View current match squad
+ + +Reset squad for new match
+ + +Manage match settings and activate voting
+ + +Record goals and assists statistics
+ + +View current match voting results
+ + +View season totals and Player of the Year standings
+ + +Configure and initialize the database
+ + +View current database configuration and status
+ + +Configure AWS S3 storage for logos and assets
+ + +View current S3 configuration and connection status
+ +Admin functions require authentication. Please contact the system administrator for access.
+