From 79bbf99b948b319933fb0682e63bbf7392468d23 Mon Sep 17 00:00:00 2001 From: hex Date: Mon, 19 May 2025 21:31:36 -0700 Subject: [PATCH] Add Docker deployment with SSL/Certbot support --- .dockerignore | 13 +++++++ Dockerfile | 18 ++++++++++ deploy.sh | 76 ++++++++++++++++++++++++++++++++++++++++ docker-compose.yml | 44 +++++++++++++++++++++++ init-letsencrypt.sh | 77 +++++++++++++++++++++++++++++++++++++++++ nginx/app.conf.template | 31 +++++++++++++++++ 6 files changed, 259 insertions(+) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100755 deploy.sh create mode 100644 docker-compose.yml create mode 100755 init-letsencrypt.sh create mode 100644 nginx/app.conf.template diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..529c171 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,13 @@ +.git +.gitignore +venv/ +__pycache__/ +*.pyc +*.pyo +*.pyd +instance/ +.env +README.md +.dockerignore +Dockerfile +docker-compose.yml diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..1332964 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,18 @@ +FROM python:3.12-slim + +WORKDIR /app + +COPY requirements.txt . + +RUN pip install --no-cache-dir -r requirements.txt +RUN pip install gunicorn + +COPY . . + +ENV FLASK_APP=run.py +ENV FLASK_ENV=production +ENV PYTHONUNBUFFERED=1 + +EXPOSE 8000 + +CMD ["gunicorn", "--bind", "0.0.0.0:8000", "run:app", "--workers=2", "--access-logfile=-"] diff --git a/deploy.sh b/deploy.sh new file mode 100755 index 0000000..8f2809b --- /dev/null +++ b/deploy.sh @@ -0,0 +1,76 @@ +#!/bin/bash + +# Game Tracker Deployment Script + +echo "=== Game Tracker Deployment ===" + +# Default settings +DEPLOY_MODE=${DEPLOY_MODE:-local} + +# Check if .env file exists +if [ ! -f .env ]; then + echo "ERROR: .env file not found!" + echo "Please create a .env file with your IGDB credentials:" + echo "IGDB_CLIENT_ID=your_client_id_here" + echo "IGDB_CLIENT_SECRET=your_client_secret_here" + exit 1 +fi + +# Make sure database directory exists +mkdir -p instance + +# Check deployment mode +if [ "$1" = "--ssl" ] || [ "$DEPLOY_MODE" = "ssl" ]; then + echo "Deploying with SSL (for production use)..." + + # Check for domain name + if [ -z "$DOMAIN" ]; then + read -p "Enter your domain name (e.g., gametracker.example.com): " DOMAIN + export DOMAIN + fi + + # Check for email + if [ -z "$EMAIL" ]; then + read -p "Enter your email (for Let's Encrypt notifications): " EMAIL + export EMAIL + fi + + # Run the SSL setup script + ./init-letsencrypt.sh + + echo "=== SSL Deployment Complete ===" + echo "Your Game Tracker is now running at: https://$DOMAIN" + echo "" + echo "SSL certificates will auto-renew every 90 days" + echo "To view logs: docker-compose logs -f" + echo "To stop the server: docker-compose down" + +else + echo "Deploying locally (for development/testing)..." + + # Create a simpler nginx config for local deployment + mkdir -p nginx + cat > nginx/app.conf << EOF + server { + listen 80; + server_name localhost; + + location / { + proxy_pass http://web:8000; + proxy_set_header Host \$host; + proxy_set_header X-Real-IP \$remote_addr; + } + } +EOF + + # Build and start Docker containers + echo "Building and starting Docker containers..." + docker-compose up --build -d + + echo "=== Local Deployment Complete ===" + echo "Your Game Tracker is now running at: http://localhost" + echo "" + echo "To view logs: docker-compose logs -f" + echo "To stop the server: docker-compose down" + echo "To deploy with SSL: ./deploy.sh --ssl" +fi diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..9c33d92 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,44 @@ +version: '3' + +services: + web: + build: . + expose: + - "8000" + volumes: + - ./instance:/app/instance + env_file: + - .env + restart: unless-stopped + networks: + - app-network + + nginx: + image: nginx:alpine + ports: + - "80:80" + - "443:443" + volumes: + - ./nginx/app.conf:/etc/nginx/conf.d/default.conf + - ./data/certbot/conf:/etc/letsencrypt + - ./data/certbot/www:/var/www/certbot + depends_on: + - web + restart: unless-stopped + networks: + - app-network + command: "/bin/sh -c 'while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'" + + certbot: + image: certbot/certbot + volumes: + - ./data/certbot/conf:/etc/letsencrypt + - ./data/certbot/www:/var/www/certbot + restart: unless-stopped + entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 12h & wait $${!}; done;'" + networks: + - app-network + +networks: + app-network: + driver: bridge diff --git a/init-letsencrypt.sh b/init-letsencrypt.sh new file mode 100755 index 0000000..703289b --- /dev/null +++ b/init-letsencrypt.sh @@ -0,0 +1,77 @@ +#!/bin/bash + +# Initialize Let's Encrypt SSL certificates with Certbot + +if ! [ -x "$(command -v docker-compose)" ]; then + echo 'Error: docker-compose is not installed.' >&2 + exit 1 +fi + +# Default domain and email settings +domains=${DOMAIN:-} +email=${EMAIL:-} +staging=${STAGING:-0} # Set to 1 if you're testing your setup + +# Ask for domain if not provided +if [ -z "$domains" ]; then + read -p "Enter your domain name (e.g., gametracker.example.com): " domains +fi + +# Ask for email if not provided +if [ -z "$email" ]; then + read -p "Enter your email (for Let's Encrypt notifications): " email +fi + +# Create required directories +mkdir -p ./data/certbot/conf +mkdir -p ./data/certbot/www + +# Generate Nginx config using template +export DOMAIN_NAME=$domains +envsubst < ./nginx/app.conf.template > ./nginx/app.conf + +# Stop any existing services +docker-compose down + +echo "### Starting Nginx..." +docker-compose up --force-recreate -d nginx +echo + +echo "### Deleting any existing certificates for domain..." +if [ -d "./data/certbot/conf/live/$domains" ]; then + docker-compose run --rm --entrypoint "\ + rm -rf /etc/letsencrypt/live/$domains && \ + rm -rf /etc/letsencrypt/archive/$domains && \ + rm -rf /etc/letsencrypt/renewal/$domains.conf" certbot +fi +echo + +echo "### Requesting Let's Encrypt certificate..." +staging_arg="" +if [ "$staging" = 1 ]; then + staging_arg="--staging" +fi + +# Get certificates +domain_args="-d $domains" +docker-compose run --rm --entrypoint "\ + certbot certonly --webroot -w /var/www/certbot \ + $staging_arg \ + --email $email \ + --agree-tos \ + --no-eff-email \ + $domain_args" certbot +echo + +echo "### Reloading nginx..." +docker-compose exec nginx nginx -s reload + +# Start all services +docker-compose up -d + +echo " +HTTPS setup completed! +Your app is now available at: https://$domains + +The certificates will auto-renew, but make sure to keep the containers running. +" diff --git a/nginx/app.conf.template b/nginx/app.conf.template new file mode 100644 index 0000000..94eb290 --- /dev/null +++ b/nginx/app.conf.template @@ -0,0 +1,31 @@ +server { + listen 80; + server_name ${DOMAIN_NAME}; + + location /.well-known/acme-challenge/ { + root /var/www/certbot; + } + + location / { + return 301 https://$host$request_uri; + } +} + +server { + listen 443 ssl; + server_name ${DOMAIN_NAME}; + + ssl_certificate /etc/letsencrypt/live/${DOMAIN_NAME}/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/${DOMAIN_NAME}/privkey.pem; + + include /etc/letsencrypt/options-ssl-nginx.conf; + ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; + + location / { + proxy_pass http://web:8000; + 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; + } +}