diff --git a/HOSTINGER_DEPLOYMENT.md b/HOSTINGER_DEPLOYMENT.md new file mode 100644 index 0000000..01506d0 --- /dev/null +++ b/HOSTINGER_DEPLOYMENT.md @@ -0,0 +1,89 @@ +# Hostinger VPS Deployment Commands + +## VPS Details +- **IP**: 69.62.81.168 +- **SSH**: ssh root@69.62.81.168 +- **Provider**: Hostinger + +## Step-by-Step Deployment + +### 1. Connect to VPS +```bash +ssh root@69.62.81.168 +``` + +### 2. Clone Repository +```bash +cd /home/ubuntu || cd /root +mkdir -p /home/ubuntu +cd /home/ubuntu +git clone https://github.com/YOUR_USERNAME/YOUR_REPO_NAME.git django-demo +cd django-demo +``` + +### 3. Run Deployment Script +```bash +sudo bash deploy/deploy.sh +``` + +### 4. Configure Environment +```bash +sudo nano .env +``` + +**Set these values:** +```env +SECRET_KEY=your-super-secret-key-change-this-to-something-long-and-random +DEBUG=False +ALLOWED_HOSTS=69.62.81.168,localhost,127.0.0.1 +DATABASE_URL=postgresql://demo_user:your_secure_password@localhost:5432/demo_db +SECURE_SSL_REDIRECT=False +``` + +### 5. Update Nginx Configuration +```bash +sudo nano /etc/nginx/sites-available/django-demo +``` + +Replace `yourdomain.com` with `69.62.81.168` in the server_name line. + +### 6. Restart Services +```bash +sudo systemctl restart django-demo +sudo systemctl restart nginx +``` + +### 7. Check Status +```bash +sudo systemctl status django-demo +sudo systemctl status nginx +``` + +### 8. Test Application +Visit: http://69.62.81.168 + +## Troubleshooting Commands + +```bash +# View Django logs +sudo journalctl -u django-demo -f + +# View Nginx logs +sudo tail -f /var/log/nginx/error.log + +# Restart services +sudo systemctl restart django-demo nginx + +# Check if ports are open +sudo netstat -tlnp | grep :80 +sudo netstat -tlnp | grep :8000 +``` + +## Creating Admin User +```bash +cd /home/ubuntu/django-demo +source venv/bin/activate +python manage.py createsuperuser +``` + +Access admin at: http://69.62.81.168/admin \ No newline at end of file diff --git a/PRODUCTION_DEPLOYMENT.md b/PRODUCTION_DEPLOYMENT.md new file mode 100644 index 0000000..4b6e5f3 --- /dev/null +++ b/PRODUCTION_DEPLOYMENT.md @@ -0,0 +1,337 @@ +# Production Django Deployment Guide + +A comprehensive, battle-tested guide for deploying Django applications on VPS with proper security and performance. + +## šŸŽÆ **Overview** + +This guide follows Django best practices and avoids common pitfalls we encountered: + +- āŒ **Avoid**: Running as root, wrong directories, TCP sockets, hardcoded paths +- āœ… **Use**: Non-root user, `/var/www/`, Unix sockets, proper systemd configuration + +## šŸ“‹ **Prerequisites** + +- Ubuntu 20.04+ VPS with root access +- Domain name (optional, can use IP address) +- Git repository with Django project + +## šŸš€ **Step 1: VPS Initial Setup** + +### Connect and Update System +```bash +ssh root@YOUR_VPS_IP +apt update && apt upgrade -y +``` + +### Install Required Packages +```bash +apt install -y python3 python3-pip python3-venv python3-dev \ + nginx postgresql postgresql-contrib libpq-dev \ + build-essential curl git ufw +``` + +### Create Non-Root User +```bash +adduser django --disabled-password --gecos '' +usermod -aG sudo django +``` + +### Configure SSH for New User (Optional) +```bash +mkdir -p /home/django/.ssh +cp /root/.ssh/authorized_keys /home/django/.ssh/ +chown -R django:django /home/django/.ssh +chmod 700 /home/django/.ssh +chmod 600 /home/django/.ssh/authorized_keys +``` + +## šŸ—‚ļø **Step 2: Project Setup** + +### Create Project Directory +```bash +mkdir -p /var/www +chown django:www-data /var/www +``` + +### Clone Project (as django user) +```bash +sudo -u django bash -c " +cd /var/www +git clone YOUR_REPO_URL django-app +cd django-app +" +``` + +### Create Virtual Environment +```bash +sudo -u django bash -c " +cd /var/www/django-app +python3 -m venv venv +source venv/bin/activate +pip install --upgrade pip +pip install -r requirements.txt +" +``` + +## āš™ļø **Step 3: Django Configuration** + +### Environment Variables +```bash +sudo -u django bash -c " +cd /var/www/django-app +cp .env.example .env +# Edit .env with production settings +" +``` + +**Required .env settings:** +```env +SECRET_KEY=your-super-secret-key-generate-new-one +DEBUG=False +ALLOWED_HOSTS=yourdomain.com,www.yourdomain.com,YOUR_VPS_IP +DATABASE_URL=postgresql://dbuser:dbpassword@localhost/dbname +SECURE_SSL_REDIRECT=False # Set True after SSL setup +``` + +### Run Django Setup +```bash +sudo -u django bash -c " +cd /var/www/django-app +source venv/bin/activate +python manage.py collectstatic --noinput +python manage.py migrate +python manage.py createsuperuser +" +``` + +## šŸ”§ **Step 4: Gunicorn with systemd** + +### Create Gunicorn Socket +```bash +cat > /etc/systemd/system/gunicorn.socket << 'EOF' +[Unit] +Description=gunicorn socket + +[Socket] +ListenStream=/run/gunicorn.sock + +[Install] +WantedBy=sockets.target +EOF +``` + +### Create Gunicorn Service +```bash +cat > /etc/systemd/system/gunicorn.service << 'EOF' +[Unit] +Description=Gunicorn daemon for Django app +Requires=gunicorn.socket +After=network.target + +[Service] +User=django +Group=www-data +WorkingDirectory=/var/www/django-app +Environment=DJANGO_SETTINGS_MODULE=PROJECT_NAME.settings +EnvironmentFile=/var/www/django-app/.env +ExecStart=/var/www/django-app/venv/bin/gunicorn \ + --workers 3 \ + --bind unix:/run/gunicorn.sock \ + PROJECT_NAME.wsgi:application +Restart=always + +[Install] +WantedBy=multi-user.target +EOF +``` + +**āš ļø Replace `PROJECT_NAME` with your actual Django project name!** + +### Start Services +```bash +systemctl daemon-reload +systemctl start gunicorn.socket +systemctl enable gunicorn.socket +systemctl status gunicorn.socket +``` + +## 🌐 **Step 5: Nginx Configuration** + +### Create Nginx Site Config +```bash +cat > /etc/nginx/sites-available/django-app << 'EOF' +server { + listen 80; + server_name YOUR_DOMAIN.com www.YOUR_DOMAIN.com YOUR_VPS_IP; + + # Security headers + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header X-Content-Type-Options "nosniff" always; + add_header Referrer-Policy "no-referrer-when-downgrade" always; + + # Gzip compression + gzip on; + gzip_vary on; + gzip_min_length 1024; + gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; + + location / { + include proxy_params; + proxy_pass http://unix:/run/gunicorn.sock; + } + + location /static/ { + alias /var/www/django-app/staticfiles/; + expires 1y; + add_header Cache-Control "public, immutable"; + } + + location /media/ { + alias /var/www/django-app/media/; + expires 1y; + add_header Cache-Control "public"; + } + + # Block access to sensitive files + location ~* /\.(?!well-known\/) { + deny all; + } + + location ~* /(requirements\.txt|\.env|deploy/|\.git/) { + deny all; + } +} +EOF +``` + +### Enable Site +```bash +ln -s /etc/nginx/sites-available/django-app /etc/nginx/sites-enabled/ +rm -f /etc/nginx/sites-enabled/default +nginx -t +systemctl restart nginx +``` + +## šŸ”’ **Step 6: Security & Firewall** + +### Configure UFW Firewall +```bash +ufw default deny incoming +ufw default allow outgoing +ufw allow ssh +ufw allow 'Nginx Full' +ufw --force enable +ufw status +``` + +## šŸ” **Step 7: SSL Certificate (Optional)** + +### Install Certbot +```bash +apt install certbot python3-certbot-nginx -y +``` + +### Get SSL Certificate +```bash +certbot --nginx -d yourdomain.com -d www.yourdomain.com +``` + +### Update Environment for HTTPS +```bash +# In /var/www/django-app/.env +SECURE_SSL_REDIRECT=True +``` + +## āœ… **Step 8: Verification** + +### Test Application +```bash +curl -I http://YOUR_VPS_IP # Should return 200 OK +``` + +### Check Services +```bash +systemctl status gunicorn.socket +systemctl status gunicorn.service +systemctl status nginx +``` + +### View Logs +```bash +journalctl -u gunicorn.service -f # Django logs +tail -f /var/log/nginx/error.log # Nginx logs +``` + +## šŸ”„ **Deployment Updates** + +### Update Code +```bash +sudo -u django bash -c " +cd /var/www/django-app +git pull origin main +source venv/bin/activate +pip install -r requirements.txt +python manage.py migrate +python manage.py collectstatic --noinput +" +systemctl restart gunicorn.service +``` + +## 🚨 **Common Issues & Solutions** + +### Issue: 502 Bad Gateway +**Cause**: Gunicorn not running or socket issues +**Solution**: +```bash +systemctl status gunicorn.service +systemctl restart gunicorn.socket +``` + +### Issue: Static files not loading +**Cause**: Nginx can't access staticfiles directory +**Solution**: +```bash +sudo -u django bash -c "cd /var/www/django-app && source venv/bin/activate && python manage.py collectstatic --noinput" +``` + +### Issue: Permission denied +**Cause**: Wrong file ownership +**Solution**: +```bash +chown -R django:www-data /var/www/django-app +``` + +### Issue: ModuleNotFoundError +**Cause**: Virtual environment not recreated after moving files +**Solution**: +```bash +sudo -u django bash -c "cd /var/www/django-app && rm -rf venv && python3 -m venv venv && source venv/bin/activate && pip install -r requirements.txt" +``` + +## šŸ“ **Quick Commands Reference** + +```bash +# Service management +systemctl restart gunicorn.service +systemctl restart nginx +systemctl status gunicorn.service + +# View logs +journalctl -u gunicorn.service -f +tail -f /var/log/nginx/access.log + +# Django management +sudo -u django bash -c "cd /var/www/django-app && source venv/bin/activate && python manage.py COMMAND" +``` + +## šŸŽÆ **Key Differences from Our Initial Approach** + +1. **User**: Use `django` user instead of `root` +2. **Location**: Use `/var/www/django-app` instead of `/root/django-demo` +3. **Socket**: Use Unix socket instead of TCP +4. **Virtual Environment**: Always recreate in target location +5. **Systemd**: Use socket activation with proper service file +6. **Security**: Proper file permissions and firewall rules + +This configuration is production-ready, secure, and follows Django best practices. \ No newline at end of file diff --git a/QUICK_START.md b/QUICK_START.md new file mode 100644 index 0000000..74d807b --- /dev/null +++ b/QUICK_START.md @@ -0,0 +1,223 @@ +# Django VPS Quick Start Guide + +šŸš€ **One-command deployment for Django on VPS!** + +## ⚔ **Super Quick Start (5 minutes)** + +1. **SSH into your VPS:** + ```bash + ssh root@YOUR_VPS_IP + ``` + +2. **Run the automated deployment:** + ```bash + curl -sSL https://raw.githubusercontent.com/thecyberlearn/hostinger-django-demo/main/deploy/production-deploy.sh | sudo bash + ``` + +3. **Follow the prompts:** + - Enter your Git repository URL + - Enter your domain name (optional) + - Enter your VPS IP address + +4. **Done!** Your Django app will be live at `http://YOUR_VPS_IP` + +## šŸ“‹ **Manual Quick Start (10 minutes)** + +If you prefer step-by-step control: + +### **Step 1: System Setup (2 minutes)** +```bash +# Update system +apt update && apt upgrade -y + +# Install packages +apt install -y python3 python3-pip python3-venv python3-dev \ + nginx postgresql postgresql-contrib libpq-dev \ + build-essential curl git ufw + +# Create user +adduser django --disabled-password --gecos '' +usermod -aG sudo django +``` + +### **Step 2: Clone Project (1 minute)** +```bash +# Setup directory +mkdir -p /var/www && chown django:www-data /var/www + +# Clone your project +sudo -u django bash -c "cd /var/www && git clone YOUR_REPO_URL django-app" +``` + +### **Step 3: Python Setup (2 minutes)** +```bash +sudo -u django bash -c " +cd /var/www/django-app +python3 -m venv venv +source venv/bin/activate +pip install -r requirements.txt +" +``` + +### **Step 4: Django Configuration (2 minutes)** +```bash +# Copy environment file +sudo -u django bash -c "cd /var/www/django-app && cp .env.example .env" + +# Edit .env file with your settings +nano /var/www/django-app/.env + +# Run Django setup +sudo -u django bash -c " +cd /var/www/django-app +source venv/bin/activate +python manage.py migrate +python manage.py collectstatic --noinput +" +``` + +### **Step 5: Services Setup (3 minutes)** +```bash +# Create Gunicorn socket +cat > /etc/systemd/system/gunicorn.socket << 'EOF' +[Unit] +Description=gunicorn socket + +[Socket] +ListenStream=/run/gunicorn.sock + +[Install] +WantedBy=sockets.target +EOF + +# Create Gunicorn service (replace demo_project with your project name) +cat > /etc/systemd/system/gunicorn.service << 'EOF' +[Unit] +Description=Gunicorn daemon for Django app +Requires=gunicorn.socket +After=network.target + +[Service] +User=django +Group=www-data +WorkingDirectory=/var/www/django-app +Environment=DJANGO_SETTINGS_MODULE=demo_project.settings +EnvironmentFile=/var/www/django-app/.env +ExecStart=/var/www/django-app/venv/bin/gunicorn \ + --workers 3 \ + --bind unix:/run/gunicorn.sock \ + demo_project.wsgi:application +Restart=always + +[Install] +WantedBy=multi-user.target +EOF + +# Start services +systemctl daemon-reload +systemctl start gunicorn.socket +systemctl enable gunicorn.socket + +# Configure Nginx (replace YOUR_VPS_IP) +cat > /etc/nginx/sites-available/django-app << 'EOF' +server { + listen 80; + server_name YOUR_VPS_IP; + + location / { + include proxy_params; + proxy_pass http://unix:/run/gunicorn.sock; + } + + location /static/ { + alias /var/www/django-app/staticfiles/; + } +} +EOF + +# Enable Nginx +ln -sf /etc/nginx/sites-available/django-app /etc/nginx/sites-enabled/ +rm -f /etc/nginx/sites-enabled/default +nginx -t && systemctl restart nginx + +# Configure firewall +ufw allow ssh && ufw allow 'Nginx Full' && ufw --force enable +``` + +## āœ… **Verification** + +Your Django app should now be live! Test with: +```bash +curl -I http://YOUR_VPS_IP +``` + +You should see `HTTP/1.1 200 OK` + +## šŸ”§ **Post-Deployment** + +1. **Create superuser:** + ```bash + sudo -u django bash -c "cd /var/www/django-app && source venv/bin/activate && python manage.py createsuperuser" + ``` + +2. **Access admin:** `http://YOUR_VPS_IP/admin` + +3. **Add SSL certificate (optional):** + ```bash + apt install certbot python3-certbot-nginx + certbot --nginx -d yourdomain.com + ``` + +## 🚨 **If Something Goes Wrong** + +1. **Check service status:** + ```bash + systemctl status gunicorn.service nginx + ``` + +2. **View logs:** + ```bash + journalctl -u gunicorn.service -f + tail -f /var/log/nginx/error.log + ``` + +3. **Common fixes:** + ```bash + # Restart services + systemctl restart gunicorn.service nginx + + # Fix permissions + chown -R django:www-data /var/www/django-app + + # Recreate virtual environment + sudo -u django bash -c "cd /var/www/django-app && rm -rf venv && python3 -m venv venv && source venv/bin/activate && pip install -r requirements.txt" + ``` + +## šŸ“š **What's Different from Before** + +Our **old problematic approach:** +- āŒ Used root user +- āŒ Wrong directory (`/root/django-demo`) +- āŒ TCP socket instead of Unix socket +- āŒ Hardcoded paths in virtual environment + +Our **new bulletproof approach:** +- āœ… Non-root `django` user +- āœ… Standard `/var/www/django-app` directory +- āœ… Unix socket for better performance +- āœ… Proper file permissions and security +- āœ… Follows Django deployment best practices + +## šŸŽÆ **Why This Works Better** + +1. **Security:** Non-root user with minimal privileges +2. **Performance:** Unix sockets are faster than TCP +3. **Reliability:** Socket activation prevents startup issues +4. **Maintainability:** Standard directory structure +5. **Scalability:** Proper systemd integration + +This setup is **production-ready** and follows **Django best practices**! + +--- + +**Need help?** Check `TROUBLESHOOTING.md` for detailed solutions to common issues. \ No newline at end of file diff --git a/TROUBLESHOOTING.md b/TROUBLESHOOTING.md new file mode 100644 index 0000000..131e38d --- /dev/null +++ b/TROUBLESHOOTING.md @@ -0,0 +1,398 @@ +# Django VPS Deployment Troubleshooting Guide + +A comprehensive guide to diagnose and fix common issues during Django VPS deployment. + +## šŸ” **Quick Diagnosis Commands** + +Before diving into specific issues, run these commands to get an overview: + +```bash +# Check all services status +systemctl status gunicorn.socket gunicorn.service nginx + +# Test HTTP response +curl -I http://YOUR_VPS_IP + +# Check disk space +df -h + +# Check memory usage +free -m + +# View recent logs +journalctl -u gunicorn.service -n 20 +tail -20 /var/log/nginx/error.log +``` + +## 🚨 **Common Issues & Solutions** + +### **Issue 1: 502 Bad Gateway** + +**Symptoms:** +- Nginx returns 502 Bad Gateway +- Website is unreachable + +**Diagnosis:** +```bash +systemctl status gunicorn.service +curl http://unix:/run/gunicorn.sock # Test socket directly +``` + +**Common Causes & Solutions:** + +#### 1.1 Gunicorn Service Not Running +```bash +# Check status +systemctl status gunicorn.service + +# If failed, check logs +journalctl -u gunicorn.service -n 50 + +# Restart service +systemctl restart gunicorn.socket +systemctl restart gunicorn.service +``` + +#### 1.2 Socket Permission Issues +```bash +# Check socket permissions +ls -la /run/gunicorn.sock + +# Fix permissions if needed +sudo chown django:www-data /run/gunicorn.sock +``` + +#### 1.3 Virtual Environment Issues +```bash +# Recreate virtual environment +sudo -u django bash -c " +cd /var/www/django-app +rm -rf venv +python3 -m venv venv +source venv/bin/activate +pip install -r requirements.txt +" +systemctl restart gunicorn.service +``` + +--- + +### **Issue 2: 500 Internal Server Error** + +**Symptoms:** +- Django returns 500 error +- HTTP 500 in curl response + +**Diagnosis:** +```bash +# Check Django logs +journalctl -u gunicorn.service -f + +# Check Django settings +sudo -u django bash -c "cd /var/www/django-app && source venv/bin/activate && python manage.py check --deploy" +``` + +**Common Causes & Solutions:** + +#### 2.1 ModuleNotFoundError +**Error:** `No module named 'your_project.urls'` + +**Solution:** +```bash +# Check project structure +ls -la /var/www/django-app/ + +# Ensure DJANGO_SETTINGS_MODULE is correct in systemd service +grep DJANGO_SETTINGS_MODULE /etc/systemd/system/gunicorn.service + +# Update if wrong project name +sed -i 's/demo_project/YOUR_ACTUAL_PROJECT_NAME/g' /etc/systemd/system/gunicorn.service +systemctl daemon-reload +systemctl restart gunicorn.service +``` + +#### 2.2 Database Connection Issues +**Solution:** +```bash +# Test database connection +sudo -u django bash -c "cd /var/www/django-app && source venv/bin/activate && python manage.py dbshell" + +# Check .env file +cat /var/www/django-app/.env + +# Run migrations if needed +sudo -u django bash -c "cd /var/www/django-app && source venv/bin/activate && python manage.py migrate" +``` + +#### 2.3 Missing Static Files +**Solution:** +```bash +# Collect static files +sudo -u django bash -c "cd /var/www/django-app && source venv/bin/activate && python manage.py collectstatic --noinput" + +# Check static files directory +ls -la /var/www/django-app/staticfiles/ +``` + +--- + +### **Issue 3: Static Files Not Loading (CSS/JS Missing)** + +**Symptoms:** +- Website loads but no styling +- 404 errors for CSS/JS files + +**Diagnosis:** +```bash +# Check nginx config +nginx -t +cat /etc/nginx/sites-enabled/django-app + +# Test static file access +curl -I http://YOUR_VPS_IP/static/admin/css/base.css +``` + +**Solutions:** +```bash +# 1. Collect static files +sudo -u django bash -c "cd /var/www/django-app && source venv/bin/activate && python manage.py collectstatic --noinput" + +# 2. Check nginx static files configuration +grep -A 5 "location /static/" /etc/nginx/sites-enabled/django-app + +# 3. Fix permissions +chown -R django:www-data /var/www/django-app/staticfiles/ +chmod -R 755 /var/www/django-app/staticfiles/ + +# 4. Restart nginx +systemctl restart nginx +``` + +--- + +### **Issue 4: Permission Denied Errors** + +**Symptoms:** +- Various permission denied errors in logs +- Services failing to start + +**Solutions:** +```bash +# Fix project ownership +chown -R django:www-data /var/www/django-app + +# Fix socket permissions +chown django:www-data /run/gunicorn.sock + +# Fix log directory permissions +mkdir -p /var/log/django +chown -R django:www-data /var/log/django + +# Restart services +systemctl restart gunicorn.service +``` + +--- + +### **Issue 5: Firewall Blocking Connections** + +**Symptoms:** +- Connection timeout from external IPs +- Works locally but not from internet + +**Diagnosis:** +```bash +# Check firewall status +ufw status + +# Test local connection +curl -I http://127.0.0.1 +``` + +**Solutions:** +```bash +# Allow HTTP and HTTPS +ufw allow 'Nginx Full' +ufw allow 80 +ufw allow 443 + +# Reload firewall +ufw reload + +# Check status +ufw status +``` + +--- + +### **Issue 6: SSL/HTTPS Issues** + +**Symptoms:** +- SSL certificate errors +- HTTPS redirects not working + +**Solutions:** +```bash +# Check SSL certificate +certbot certificates + +# Renew certificate +certbot renew + +# Test nginx config +nginx -t + +# Check SSL-related settings in .env +grep SECURE_SSL_REDIRECT /var/www/django-app/.env +``` + +--- + +## šŸ”§ **Advanced Debugging** + +### **Check System Resources** +```bash +# Check disk space +df -h + +# Check memory usage +free -m +htop + +# Check CPU usage +top + +# Check open files +lsof | grep django +``` + +### **Network Debugging** +```bash +# Check listening ports +ss -tulpn | grep :80 +ss -tulpn | grep :443 +ss -tulpn | grep gunicorn + +# Check network connections +netstat -tlnp + +# Test DNS resolution +nslookup YOUR_DOMAIN +``` + +### **Log Analysis** +```bash +# Real-time Django logs +journalctl -u gunicorn.service -f + +# Real-time Nginx logs +tail -f /var/log/nginx/access.log +tail -f /var/log/nginx/error.log + +# Search for specific errors +journalctl -u gunicorn.service | grep ERROR +grep "500" /var/log/nginx/access.log +``` + +## šŸ“Š **Health Check Script** + +Create this script to quickly check your deployment health: + +```bash +#!/bin/bash +# Save as health-check.sh + +echo "šŸ„ Django Deployment Health Check" +echo "================================" + +# Service status +echo "šŸ“Š Service Status:" +systemctl is-active gunicorn.socket && echo "āœ… Gunicorn Socket: Active" || echo "āŒ Gunicorn Socket: Inactive" +systemctl is-active gunicorn.service && echo "āœ… Gunicorn Service: Active" || echo "āŒ Gunicorn Service: Inactive" +systemctl is-active nginx && echo "āœ… Nginx: Active" || echo "āŒ Nginx: Inactive" + +# HTTP test +echo -e "\n🌐 HTTP Response:" +HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost) +if [ "$HTTP_CODE" = "200" ]; then + echo "āœ… HTTP Response: $HTTP_CODE (OK)" +else + echo "āŒ HTTP Response: $HTTP_CODE" +fi + +# Disk space +echo -e "\nšŸ’¾ Disk Usage:" +df -h | grep -E "/$|/var" + +# Memory usage +echo -e "\n🧠 Memory Usage:" +free -m | grep Mem + +# Recent errors +echo -e "\n🚨 Recent Errors (last 10 lines):" +journalctl -u gunicorn.service -n 10 --no-pager | grep -i error || echo "No recent errors found" + +echo -e "\nāœ… Health check complete!" +``` + +## šŸ†˜ **Emergency Recovery** + +If everything is broken, try this recovery sequence: + +```bash +# 1. Stop all services +systemctl stop gunicorn.service nginx + +# 2. Check project files +ls -la /var/www/django-app/ + +# 3. Recreate virtual environment +sudo -u django bash -c "cd /var/www/django-app && rm -rf venv && python3 -m venv venv && source venv/bin/activate && pip install -r requirements.txt" + +# 4. Run Django checks +sudo -u django bash -c "cd /var/www/django-app && source venv/bin/activate && python manage.py check" + +# 5. Collect static files +sudo -u django bash -c "cd /var/www/django-app && source venv/bin/activate && python manage.py collectstatic --noinput" + +# 6. Fix permissions +chown -R django:www-data /var/www/django-app + +# 7. Restart services +systemctl daemon-reload +systemctl start gunicorn.socket nginx + +# 8. Test +curl -I http://localhost +``` + +## šŸ“ž **Getting Help** + +If you're still stuck: + +1. **Collect information:** + ```bash + # System info + uname -a + lsb_release -a + + # Service status + systemctl status gunicorn.service nginx + + # Recent logs + journalctl -u gunicorn.service -n 50 + tail -50 /var/log/nginx/error.log + ``` + +2. **Check Django docs:** https://docs.djangoproject.com/en/stable/howto/deployment/ +3. **Check Gunicorn docs:** https://docs.gunicorn.org/ +4. **Check Nginx docs:** https://nginx.org/en/docs/ + +Remember: Most deployment issues are caused by: +- File permissions +- Incorrect paths +- Missing dependencies +- Configuration typos +- Firewall rules + +Take it step by step and check each component individually! \ No newline at end of file diff --git a/deploy/production-deploy.sh b/deploy/production-deploy.sh new file mode 100755 index 0000000..535869f --- /dev/null +++ b/deploy/production-deploy.sh @@ -0,0 +1,307 @@ +#!/bin/bash + +# Django Production Deployment Script +# Tested and battle-hardened for VPS deployment +# Usage: sudo bash production-deploy.sh + +set -e + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Configuration +PROJECT_NAME="demo_project" # Change this to your Django project name +APP_NAME="django-app" +REPO_URL="" # Will be set by user input +DOMAIN="" # Will be set by user input +VPS_IP="" # Will be set by user input + +echo -e "${BLUE}šŸš€ Django Production Deployment Script${NC}" +echo -e "${BLUE}======================================${NC}" + +# Check if running as root +if [ "$EUID" -ne 0 ]; then + echo -e "${RED}āŒ Please run this script as root (use sudo)${NC}" + exit 1 +fi + +# Get user input +echo -e "${YELLOW}šŸ“ Configuration Setup${NC}" +read -p "Enter your Git repository URL: " REPO_URL +read -p "Enter your domain name (or press Enter to skip): " DOMAIN +read -p "Enter your VPS IP address: " VPS_IP + +if [ -z "$REPO_URL" ]; then + echo -e "${RED}āŒ Repository URL is required${NC}" + exit 1 +fi + +if [ -z "$VPS_IP" ]; then + echo -e "${RED}āŒ VPS IP address is required${NC}" + exit 1 +fi + +echo -e "${GREEN}āœ… Configuration set:${NC}" +echo -e "Repository: $REPO_URL" +echo -e "Domain: ${DOMAIN:-'Not set - will use IP'}" +echo -e "VPS IP: $VPS_IP" +echo + +# Function to print status +print_status() { + echo -e "${GREEN}āœ… $1${NC}" +} + +print_progress() { + echo -e "${YELLOW}šŸ”„ $1${NC}" +} + +# Step 1: System packages +print_progress "Installing system packages..." +apt update +apt install -y python3 python3-pip python3-venv python3-dev \ + nginx postgresql postgresql-contrib libpq-dev \ + build-essential curl git ufw +print_status "System packages installed" + +# Step 2: Create user +print_progress "Creating django user..." +if ! id "django" &>/dev/null; then + adduser django --disabled-password --gecos '' + usermod -aG sudo django + print_status "Django user created" +else + print_status "Django user already exists" +fi + +# Copy SSH keys if they exist +if [ -d "/root/.ssh" ] && [ -f "/root/.ssh/authorized_keys" ]; then + print_progress "Copying SSH keys to django user..." + mkdir -p /home/django/.ssh + cp /root/.ssh/authorized_keys /home/django/.ssh/ + chown -R django:django /home/django/.ssh + chmod 700 /home/django/.ssh + chmod 600 /home/django/.ssh/authorized_keys + print_status "SSH keys copied" +fi + +# Step 3: Project setup +print_progress "Setting up project directory..." +mkdir -p /var/www +chown django:www-data /var/www + +# Clone or update project +if [ -d "/var/www/$APP_NAME" ]; then + print_progress "Updating existing project..." + sudo -u django bash -c "cd /var/www/$APP_NAME && git pull origin main" +else + print_progress "Cloning project..." + sudo -u django bash -c "cd /var/www && git clone $REPO_URL $APP_NAME" +fi + +print_status "Project setup complete" + +# Step 4: Virtual environment +print_progress "Setting up virtual environment..." +sudo -u django bash -c " +cd /var/www/$APP_NAME +rm -rf venv +python3 -m venv venv +source venv/bin/activate +pip install --upgrade pip +pip install -r requirements.txt +" +print_status "Virtual environment created" + +# Step 5: Environment configuration +print_progress "Configuring environment..." +if [ ! -f "/var/www/$APP_NAME/.env" ]; then + sudo -u django bash -c " + cd /var/www/$APP_NAME + cp .env.example .env + " + + # Generate secret key + SECRET_KEY=$(python3 -c 'from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())') + + # Configure .env + sudo -u django bash -c " + cd /var/www/$APP_NAME + sed -i 's/SECRET_KEY=.*/SECRET_KEY=$SECRET_KEY/' .env + sed -i 's/DEBUG=.*/DEBUG=False/' .env + sed -i 's/ALLOWED_HOSTS=.*/ALLOWED_HOSTS=${DOMAIN:-$VPS_IP},$VPS_IP,localhost,127.0.0.1/' .env + sed -i 's/SECURE_SSL_REDIRECT=.*/SECURE_SSL_REDIRECT=False/' .env + " + print_status "Environment configured" +else + print_status "Environment file already exists" +fi + +# Step 6: Django setup +print_progress "Running Django setup..." +sudo -u django bash -c " +cd /var/www/$APP_NAME +source venv/bin/activate +python manage.py collectstatic --noinput +python manage.py migrate +" +print_status "Django setup complete" + +# Step 7: Systemd configuration +print_progress "Setting up Gunicorn service..." + +# Create socket file +cat > /etc/systemd/system/gunicorn.socket << EOF +[Unit] +Description=gunicorn socket + +[Socket] +ListenStream=/run/gunicorn.sock + +[Install] +WantedBy=sockets.target +EOF + +# Create service file +cat > /etc/systemd/system/gunicorn.service << EOF +[Unit] +Description=Gunicorn daemon for Django app +Requires=gunicorn.socket +After=network.target + +[Service] +User=django +Group=www-data +WorkingDirectory=/var/www/$APP_NAME +Environment=DJANGO_SETTINGS_MODULE=$PROJECT_NAME.settings +EnvironmentFile=/var/www/$APP_NAME/.env +ExecStart=/var/www/$APP_NAME/venv/bin/gunicorn \\ + --workers 3 \\ + --bind unix:/run/gunicorn.sock \\ + $PROJECT_NAME.wsgi:application +Restart=always + +[Install] +WantedBy=multi-user.target +EOF + +systemctl daemon-reload +systemctl start gunicorn.socket +systemctl enable gunicorn.socket +print_status "Gunicorn service configured" + +# Step 8: Nginx configuration +print_progress "Setting up Nginx..." + +NGINX_CONFIG="/etc/nginx/sites-available/$APP_NAME" +cat > $NGINX_CONFIG << EOF +server { + listen 80; + server_name ${DOMAIN:-$VPS_IP} $VPS_IP; + + # Security headers + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header X-Content-Type-Options "nosniff" always; + add_header Referrer-Policy "no-referrer-when-downgrade" always; + + # Gzip compression + gzip on; + gzip_vary on; + gzip_min_length 1024; + gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; + + location / { + include proxy_params; + proxy_pass http://unix:/run/gunicorn.sock; + } + + location /static/ { + alias /var/www/$APP_NAME/staticfiles/; + expires 1y; + add_header Cache-Control "public, immutable"; + } + + location /media/ { + alias /var/www/$APP_NAME/media/; + expires 1y; + add_header Cache-Control "public"; + } + + # Block access to sensitive files + location ~* /\.(?!well-known\/) { + deny all; + } + + location ~* /(requirements\\.txt|\\.env|deploy/|\\.git/) { + deny all; + } +} +EOF + +# Enable site +ln -sf $NGINX_CONFIG /etc/nginx/sites-enabled/ +rm -f /etc/nginx/sites-enabled/default + +# Test and restart nginx +nginx -t +systemctl restart nginx +print_status "Nginx configured" + +# Step 9: Firewall +print_progress "Configuring firewall..." +ufw default deny incoming +ufw default allow outgoing +ufw allow ssh +ufw allow 'Nginx Full' +ufw --force enable +print_status "Firewall configured" + +# Step 10: Final checks +print_progress "Running final checks..." + +# Check services +if systemctl is-active --quiet gunicorn.socket && systemctl is-active --quiet nginx; then + print_status "All services are running" +else + echo -e "${RED}āŒ Some services are not running. Check logs:${NC}" + echo "sudo systemctl status gunicorn.service" + echo "sudo systemctl status nginx" +fi + +# Test HTTP response +echo -e "${YELLOW}šŸ” Testing application...${NC}" +sleep 2 +HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" http://$VPS_IP || echo "000") + +if [ "$HTTP_STATUS" = "200" ]; then + print_status "Application is responding correctly" +else + echo -e "${YELLOW}āš ļø HTTP Status: $HTTP_STATUS (check configuration)${NC}" +fi + +echo -e "${GREEN}šŸŽ‰ Deployment Complete!${NC}" +echo -e "${BLUE}===================${NC}" +echo -e "🌐 Application URL: http://$VPS_IP" +if [ -n "$DOMAIN" ]; then + echo -e "🌐 Domain URL: http://$DOMAIN" +fi +echo -e "šŸ”§ Admin Panel: http://$VPS_IP/admin" +echo +echo -e "${YELLOW}šŸ“‹ Next Steps:${NC}" +echo -e "1. Create Django superuser: sudo -u django bash -c 'cd /var/www/$APP_NAME && source venv/bin/activate && python manage.py createsuperuser'" +if [ -n "$DOMAIN" ]; then + echo -e "2. Set up SSL certificate: sudo certbot --nginx -d $DOMAIN" + echo -e "3. Update SECURE_SSL_REDIRECT=True in .env after SSL setup" +fi +echo +echo -e "${YELLOW}šŸ”§ Useful Commands:${NC}" +echo -e "Restart Django: sudo systemctl restart gunicorn.service" +echo -e "View logs: sudo journalctl -u gunicorn.service -f" +echo -e "Update code: sudo -u django bash -c 'cd /var/www/$APP_NAME && git pull && source venv/bin/activate && python manage.py migrate && python manage.py collectstatic --noinput' && sudo systemctl restart gunicorn.service" + +echo -e "${GREEN}āœ… Ready for production!${NC}" \ No newline at end of file diff --git a/deploy/templates/.env.production.template b/deploy/templates/.env.production.template new file mode 100644 index 0000000..930ebfa --- /dev/null +++ b/deploy/templates/.env.production.template @@ -0,0 +1,40 @@ +# Production Environment Variables Template +# Copy to .env and customize values + +# Django Core Settings +SECRET_KEY=your-super-secret-key-generate-a-new-one-for-production +DEBUG=False +ALLOWED_HOSTS=yourdomain.com,www.yourdomain.com,your-vps-ip + +# Database Configuration (choose one method) + +# Method 1: DATABASE_URL (recommended for PostgreSQL) +# Replace with your actual database credentials +# DATABASE_URL=postgresql://username:password@localhost:5432/database_name + +# Method 2: Individual PostgreSQL settings (alternative to DATABASE_URL) +# DB_ENGINE=django.db.backends.postgresql +# DB_NAME=your_database_name +# DB_USER=your_database_user +# DB_PASSWORD=your_database_password +# DB_HOST=localhost +# DB_PORT=5432 + +# Security Settings +SECURE_SSL_REDIRECT=False # Set to True after SSL certificate is installed + +# Email Configuration (optional) +# EMAIL_HOST=smtp.yourmailprovider.com +# EMAIL_PORT=587 +# EMAIL_USE_TLS=True +# EMAIL_HOST_USER=your_email@yourdomain.com +# EMAIL_HOST_PASSWORD=your_email_password +# DEFAULT_FROM_EMAIL=noreply@yourdomain.com + +# Cache Configuration (optional - if using Redis) +# REDIS_URL=redis://127.0.0.1:6379/1 + +# Custom Application Settings +# Add any custom environment variables your app needs below +# CUSTOM_API_KEY=your_api_key +# EXTERNAL_SERVICE_URL=https://api.example.com \ No newline at end of file diff --git a/deploy/templates/gunicorn.service.template b/deploy/templates/gunicorn.service.template new file mode 100644 index 0000000..2fb0407 --- /dev/null +++ b/deploy/templates/gunicorn.service.template @@ -0,0 +1,23 @@ +# Gunicorn Service Template +# Copy to: /etc/systemd/system/gunicorn.service +# Replace: {{PROJECT_NAME}}, {{APP_NAME}} + +[Unit] +Description=Gunicorn daemon for {{APP_NAME}} +Requires=gunicorn.socket +After=network.target + +[Service] +User=django +Group=www-data +WorkingDirectory=/var/www/{{APP_NAME}} +Environment=DJANGO_SETTINGS_MODULE={{PROJECT_NAME}}.settings +EnvironmentFile=/var/www/{{APP_NAME}}/.env +ExecStart=/var/www/{{APP_NAME}}/venv/bin/gunicorn \ + --workers 3 \ + --bind unix:/run/gunicorn.sock \ + {{PROJECT_NAME}}.wsgi:application +Restart=always + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/deploy/templates/gunicorn.socket.template b/deploy/templates/gunicorn.socket.template new file mode 100644 index 0000000..3179a07 --- /dev/null +++ b/deploy/templates/gunicorn.socket.template @@ -0,0 +1,11 @@ +# Gunicorn Socket Template +# Copy to: /etc/systemd/system/gunicorn.socket + +[Unit] +Description=gunicorn socket + +[Socket] +ListenStream=/run/gunicorn.sock + +[Install] +WantedBy=sockets.target \ No newline at end of file diff --git a/deploy/templates/nginx.conf.template b/deploy/templates/nginx.conf.template new file mode 100644 index 0000000..334c25f --- /dev/null +++ b/deploy/templates/nginx.conf.template @@ -0,0 +1,109 @@ +# Nginx Configuration Template +# Copy to: /etc/nginx/sites-available/{{APP_NAME}} +# Replace: {{DOMAIN}}, {{VPS_IP}}, {{APP_NAME}} + +server { + listen 80; + server_name {{DOMAIN}} {{VPS_IP}}; + + # Security headers + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header X-Content-Type-Options "nosniff" always; + add_header Referrer-Policy "no-referrer-when-downgrade" always; + add_header Content-Security-Policy "default-src 'self' 'unsafe-inline' 'unsafe-eval' data: blob:;" always; + + # Gzip compression + gzip on; + gzip_vary on; + gzip_min_length 1024; + gzip_proxied any; + gzip_comp_level 6; + gzip_types + text/plain + text/css + text/xml + text/javascript + application/json + application/javascript + application/xml+rss + application/atom+xml + image/svg+xml; + + # Rate limiting + limit_req_zone $binary_remote_addr zone=login:10m rate=10r/m; + + location / { + include proxy_params; + proxy_pass http://unix:/run/gunicorn.sock; + + # Timeout settings + proxy_connect_timeout 60s; + proxy_send_timeout 60s; + proxy_read_timeout 60s; + } + + location /static/ { + alias /var/www/{{APP_NAME}}/staticfiles/; + expires 1y; + add_header Cache-Control "public, immutable"; + + # Optional: Serve compressed files + location ~* \.(css|js)$ { + gzip_static on; + } + } + + location /media/ { + alias /var/www/{{APP_NAME}}/media/; + expires 1y; + add_header Cache-Control "public"; + } + + # Django admin rate limiting + location /admin/login/ { + limit_req zone=login burst=5 nodelay; + include proxy_params; + proxy_pass http://unix:/run/gunicorn.sock; + } + + # Block access to sensitive files + location ~* /\.(?!well-known\/) { + deny all; + } + + location ~* /(requirements\.txt|\.env|deploy/|\.git/|venv/) { + deny all; + } + + # Optional: favicon + location = /favicon.ico { + log_not_found off; + access_log off; + } + + # Optional: robots.txt + location = /robots.txt { + log_not_found off; + access_log off; + } +} + +# HTTPS Configuration (uncomment after SSL setup) +# server { +# listen 443 ssl http2; +# server_name {{DOMAIN}}; +# +# ssl_certificate /etc/letsencrypt/live/{{DOMAIN}}/fullchain.pem; +# ssl_certificate_key /etc/letsencrypt/live/{{DOMAIN}}/privkey.pem; +# +# # SSL Security +# ssl_protocols TLSv1.2 TLSv1.3; +# ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384; +# ssl_prefer_server_ciphers off; +# +# # HSTS +# add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; +# +# # Include the same location blocks as HTTP version above +# } \ No newline at end of file diff --git a/deploy/templates/production_settings.py b/deploy/templates/production_settings.py new file mode 100644 index 0000000..9efe779 --- /dev/null +++ b/deploy/templates/production_settings.py @@ -0,0 +1,220 @@ +""" +Production Django Settings Template +Copy this to your Django project and customize as needed. +""" + +import os +from pathlib import Path +from decouple import config +import dj_database_url + +# Build paths inside the project like this: BASE_DIR / 'subdir'. +BASE_DIR = Path(__file__).resolve().parent.parent + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = config('SECRET_KEY') + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = config('DEBUG', default=False, cast=bool) + +ALLOWED_HOSTS = config('ALLOWED_HOSTS', cast=lambda v: [s.strip() for s in v.split(',')]) + +# Application definition +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + # Add your apps here + 'core', +] + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'whitenoise.middleware.WhiteNoiseMiddleware', # Static files middleware + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'demo_project.urls' # Change to your project name + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'demo_project.wsgi.application' # Change to your project name + +# Database +# https://docs.djangoproject.com/en/4.2/ref/settings/#databases + +# Primary database configuration - supports both SQLite and PostgreSQL +DATABASE_URL = config('DATABASE_URL', default=None) + +if DATABASE_URL: + # Production: Use DATABASE_URL (recommended) + DATABASES = { + 'default': dj_database_url.parse(DATABASE_URL) + } +else: + # Development: Use SQLite + DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': BASE_DIR / 'db.sqlite3', + } + } + +# Password validation +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + +# Internationalization +LANGUAGE_CODE = 'en-us' +TIME_ZONE = 'UTC' +USE_I18N = True +USE_TZ = True + +# Static files (CSS, JavaScript, Images) +STATIC_URL = '/static/' +STATIC_ROOT = BASE_DIR / 'staticfiles' +STATICFILES_DIRS = [ + # Add your static directories here if needed + # BASE_DIR / 'static', +] + +# Media files (user uploads) +MEDIA_URL = '/media/' +MEDIA_ROOT = BASE_DIR / 'media' + +# Static files storage +STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage' + +# Default primary key field type +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + +# Security settings for production +if not DEBUG: + # HTTPS settings + SECURE_SSL_REDIRECT = config('SECURE_SSL_REDIRECT', default=False, cast=bool) + SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') + + # Security headers + SECURE_BROWSER_XSS_FILTER = True + SECURE_CONTENT_TYPE_NOSNIFF = True + SECURE_HSTS_SECONDS = 31536000 # 1 year + SECURE_HSTS_INCLUDE_SUBDOMAINS = True + SECURE_HSTS_PRELOAD = True + + # Session security + SESSION_COOKIE_SECURE = SECURE_SSL_REDIRECT + SESSION_COOKIE_HTTPONLY = True + SESSION_COOKIE_AGE = 3600 # 1 hour + + # CSRF security + CSRF_COOKIE_SECURE = SECURE_SSL_REDIRECT + CSRF_COOKIE_HTTPONLY = True + + # Additional security + X_FRAME_OPTIONS = 'DENY' + SECURE_REFERRER_POLICY = 'same-origin' + +# Logging configuration +LOGGING = { + 'version': 1, + 'disable_existing_loggers': False, + 'formatters': { + 'verbose': { + 'format': '{levelname} {asctime} {module} {process:d} {thread:d} {message}', + 'style': '{', + }, + 'simple': { + 'format': '{levelname} {message}', + 'style': '{', + }, + }, + 'handlers': { + 'file': { + 'level': 'INFO', + 'class': 'logging.handlers.RotatingFileHandler', + 'filename': BASE_DIR / 'logs' / 'django.log', + 'maxBytes': 1024*1024*15, # 15MB + 'backupCount': 10, + 'formatter': 'verbose', + }, + 'console': { + 'level': 'INFO', + 'class': 'logging.StreamHandler', + 'formatter': 'simple', + }, + }, + 'root': { + 'handlers': ['console', 'file'] if not DEBUG else ['console'], + 'level': 'INFO', + }, + 'loggers': { + 'django': { + 'handlers': ['console', 'file'] if not DEBUG else ['console'], + 'level': 'INFO', + 'propagate': False, + }, + }, +} + +# Create logs directory if it doesn't exist +if not DEBUG: + (BASE_DIR / 'logs').mkdir(exist_ok=True) + +# Email configuration (optional) +if not DEBUG: + EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' + EMAIL_HOST = config('EMAIL_HOST', default='localhost') + EMAIL_PORT = config('EMAIL_PORT', default=587, cast=int) + EMAIL_USE_TLS = config('EMAIL_USE_TLS', default=True, cast=bool) + EMAIL_HOST_USER = config('EMAIL_HOST_USER', default='') + EMAIL_HOST_PASSWORD = config('EMAIL_HOST_PASSWORD', default='') + DEFAULT_FROM_EMAIL = config('DEFAULT_FROM_EMAIL', default='noreply@yourdomain.com') + +# Cache configuration (optional - uncomment to use Redis) +# CACHES = { +# 'default': { +# 'BACKEND': 'django_redis.cache.RedisCache', +# 'LOCATION': config('REDIS_URL', default='redis://127.0.0.1:6379/1'), +# 'OPTIONS': { +# 'CLIENT_CLASS': 'django_redis.client.DefaultClient', +# } +# } +# } + +# Session configuration (optional - uncomment to use Redis for sessions) +# SESSION_ENGINE = 'django.contrib.sessions.backends.cache' +# SESSION_CACHE_ALIAS = 'default' \ No newline at end of file