Add deployment tooling and GHCR publish workflow
This commit is contained in:
169
.github/workflows/publish.yml
vendored
Normal file
169
.github/workflows/publish.yml
vendored
Normal file
@@ -0,0 +1,169 @@
|
||||
name: Build and Publish Docker Images
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
- 'release-*'
|
||||
branches:
|
||||
- main
|
||||
- production
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Version tag (e.g., 1.0.0)'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
env:
|
||||
REGISTRY: ghcr.io
|
||||
PROJECT_NAME: edh-stats
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build and Push Docker Images
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Log in to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Extract version
|
||||
id: version
|
||||
run: |
|
||||
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
|
||||
VERSION=${{ github.event.inputs.version }}
|
||||
elif [[ "${{ github.ref }}" =~ ^refs/tags/v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||
VERSION=${GITHUB_REF#refs/tags/v}
|
||||
elif [[ "${{ github.ref }}" =~ ^refs/tags/release-(.+)$ ]]; then
|
||||
VERSION=${BASH_REMATCH[1]}
|
||||
elif [[ "${{ github.ref }}" == "refs/heads/main" || "${{ github.ref }}" == "refs/heads/production" ]]; then
|
||||
VERSION=${{ github.ref_name }}-${{ github.sha }}
|
||||
else
|
||||
VERSION=latest
|
||||
fi
|
||||
echo "VERSION=${VERSION}" >> $GITHUB_OUTPUT
|
||||
echo "BACKEND_IMAGE=${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ env.PROJECT_NAME }}-backend:${VERSION}" >> $GITHUB_OUTPUT
|
||||
echo "FRONTEND_IMAGE=${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ env.PROJECT_NAME }}-frontend:${VERSION}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Build and push backend image
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: ./backend
|
||||
file: ./backend/Dockerfile
|
||||
target: production
|
||||
push: true
|
||||
tags: |
|
||||
${{ steps.version.outputs.BACKEND_IMAGE }}
|
||||
${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ env.PROJECT_NAME }}-backend:latest
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
- name: Build and push frontend image
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: ./
|
||||
file: ./frontend/Dockerfile.prod
|
||||
push: true
|
||||
tags: |
|
||||
${{ steps.version.outputs.FRONTEND_IMAGE }}
|
||||
${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ env.PROJECT_NAME }}-frontend:latest
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
- name: Generate deployment config
|
||||
id: config
|
||||
run: |
|
||||
cat > docker-compose.prod.deployed.yml << 'EOF'
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
backend:
|
||||
image: ${{ steps.version.outputs.BACKEND_IMAGE }}
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- DATABASE_PATH=/app/database/data/edh-stats.db
|
||||
- JWT_SECRET_FILE=/run/secrets/jwt_secret
|
||||
- CORS_ORIGIN=${CORS_ORIGIN:-https://yourdomain.com}
|
||||
- LOG_LEVEL=warn
|
||||
- ALLOW_REGISTRATION=${ALLOW_REGISTRATION:-false}
|
||||
volumes:
|
||||
- sqlite_data:/app/database/data
|
||||
- app_logs:/app/logs
|
||||
secrets:
|
||||
- jwt_secret
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ['CMD', 'wget', '--no-verbose', '--tries=1', '--spider', 'http://localhost:3000/api/health']
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
networks:
|
||||
- edh-stats-network
|
||||
|
||||
frontend:
|
||||
image: ${{ steps.version.outputs.FRONTEND_IMAGE }}
|
||||
ports:
|
||||
- '80:80'
|
||||
- '443:443'
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- edh-stats-network
|
||||
|
||||
volumes:
|
||||
sqlite_data:
|
||||
driver: local
|
||||
app_logs:
|
||||
driver: local
|
||||
|
||||
secrets:
|
||||
jwt_secret:
|
||||
external: true
|
||||
|
||||
networks:
|
||||
edh-stats-network:
|
||||
driver: bridge
|
||||
EOF
|
||||
|
||||
- name: Upload deployment config
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: deployment-config
|
||||
path: docker-compose.prod.deployed.yml
|
||||
|
||||
- name: Create Release
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
files: docker-compose.prod.deployed.yml
|
||||
generate_release_notes: true
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Post deployment info
|
||||
run: |
|
||||
echo "## Deployment Summary" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Version:** ${{ steps.version.outputs.VERSION }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Backend Image:** ${{ steps.version.outputs.BACKEND_IMAGE }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Frontend Image:** ${{ steps.version.outputs.FRONTEND_IMAGE }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Pull Commands:**" >> $GITHUB_STEP_SUMMARY
|
||||
echo "\`\`\`bash" >> $GITHUB_STEP_SUMMARY
|
||||
echo "docker pull ${{ steps.version.outputs.BACKEND_IMAGE }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "docker pull ${{ steps.version.outputs.FRONTEND_IMAGE }}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
|
||||
19
.gitignore
vendored
19
.gitignore
vendored
@@ -99,4 +99,21 @@ build/
|
||||
|
||||
# Temporary files
|
||||
tmp/
|
||||
temp/
|
||||
temp/
|
||||
|
||||
# Deployment and secrets
|
||||
jwt_secret.txt
|
||||
jwt_secret
|
||||
docker-compose.prod.deployed.yml
|
||||
.env.production
|
||||
.env.secrets
|
||||
|
||||
# Generated deployment configs
|
||||
docker-compose.*.deployed.yml
|
||||
|
||||
# SSL/TLS certificates
|
||||
*.pem
|
||||
*.crt
|
||||
*.key
|
||||
.certs/
|
||||
ssl/
|
||||
410
DEPLOYMENT.md
Normal file
410
DEPLOYMENT.md
Normal file
@@ -0,0 +1,410 @@
|
||||
# Production Deployment Guide
|
||||
|
||||
This guide covers deploying the EDH Stats Tracker to production using Docker and GitHub Container Registry (GHCR).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Docker and Docker Compose installed on your server
|
||||
- GitHub account with access to this repository
|
||||
- GitHub Personal Access Token (PAT) with `write:packages` permission
|
||||
- Domain name (for CORS_ORIGIN configuration)
|
||||
- SSL certificates (optional, for HTTPS)
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Option 1: Automatic Deployment Script (Local)
|
||||
|
||||
1. **Generate GitHub Token**
|
||||
- Go to GitHub → Settings → Developer settings → Personal access tokens
|
||||
- Create a new token with `write:packages` scope
|
||||
- Copy the token
|
||||
|
||||
2. **Run Deployment Script**
|
||||
```bash
|
||||
chmod +x deploy.sh
|
||||
|
||||
# With token as argument
|
||||
./deploy.sh 1.0.0 ghcr_xxxxxxxxxxxxx
|
||||
|
||||
# Or set as environment variable
|
||||
export GHCR_TOKEN=ghcr_xxxxxxxxxxxxx
|
||||
export GITHUB_USER=your-github-username
|
||||
./deploy.sh 1.0.0
|
||||
|
||||
# Or use interactive mode
|
||||
./deploy.sh 1.0.0
|
||||
```
|
||||
|
||||
3. **Review Generated Configuration**
|
||||
- Check `docker-compose.prod.deployed.yml`
|
||||
- Verify image tags and versions
|
||||
|
||||
### Option 2: Automated CI/CD (GitHub Actions)
|
||||
|
||||
1. **Push Release Tag**
|
||||
```bash
|
||||
git tag v1.0.0
|
||||
git push origin v1.0.0
|
||||
```
|
||||
|
||||
2. **GitHub Actions Automatically:**
|
||||
- Builds Docker images
|
||||
- Pushes to GHCR
|
||||
- Generates deployment config
|
||||
- Creates release with artifacts
|
||||
|
||||
3. **Download Deployment Config**
|
||||
- Go to GitHub Releases
|
||||
- Download `docker-compose.prod.deployed.yml`
|
||||
|
||||
## Server Setup
|
||||
|
||||
### 1. Install Docker & Docker Compose
|
||||
|
||||
```bash
|
||||
# Ubuntu/Debian
|
||||
curl -fsSL https://get.docker.com -o get-docker.sh
|
||||
sudo sh get-docker.sh
|
||||
|
||||
# Add user to docker group
|
||||
sudo usermod -aG docker $USER
|
||||
newgrp docker
|
||||
|
||||
# Verify
|
||||
docker --version
|
||||
docker-compose --version
|
||||
```
|
||||
|
||||
### 2. Create Production Directory
|
||||
|
||||
```bash
|
||||
mkdir -p ~/edh-stats/data/database
|
||||
mkdir -p ~/edh-stats/data/logs
|
||||
cd ~/edh-stats
|
||||
```
|
||||
|
||||
### 3. Copy Deployment Configuration
|
||||
|
||||
```bash
|
||||
# Copy the docker-compose.prod.deployed.yml file to server
|
||||
scp docker-compose.prod.deployed.yml user@server:~/edh-stats/docker-compose.yml
|
||||
```
|
||||
|
||||
### 4. Create Environment File
|
||||
|
||||
```bash
|
||||
# Create .env file on server
|
||||
cat > ~/edh-stats/.env << EOF
|
||||
# Required: Set your domain
|
||||
CORS_ORIGIN=https://yourdomain.com
|
||||
|
||||
# Optional: Enable user registration (default: false)
|
||||
ALLOW_REGISTRATION=false
|
||||
|
||||
# Database backup path (optional)
|
||||
DATABASE_BACKUP_PATH=/data/backups
|
||||
EOF
|
||||
```
|
||||
|
||||
### 5. Create Docker Secret
|
||||
|
||||
```bash
|
||||
# Generate secure JWT secret
|
||||
openssl rand -base64 32 > ~/edh-stats/jwt_secret.txt
|
||||
|
||||
# Create Docker secret (one-time setup)
|
||||
docker secret create jwt_secret ~/edh-stats/jwt_secret.txt
|
||||
|
||||
# Verify
|
||||
docker secret ls
|
||||
```
|
||||
|
||||
## Deployment
|
||||
|
||||
### 1. Log in to GHCR
|
||||
|
||||
```bash
|
||||
echo $GITHUB_TOKEN | docker login ghcr.io -u USERNAME --password-stdin
|
||||
```
|
||||
|
||||
### 2. Pull Latest Images
|
||||
|
||||
```bash
|
||||
cd ~/edh-stats
|
||||
|
||||
# Pull images
|
||||
docker pull ghcr.io/YOUR_GITHUB_USER/edh-stats-backend:latest
|
||||
docker pull ghcr.io/YOUR_GITHUB_USER/edh-stats-frontend:latest
|
||||
```
|
||||
|
||||
### 3. Start Services
|
||||
|
||||
```bash
|
||||
cd ~/edh-stats
|
||||
|
||||
# Start in background
|
||||
docker-compose up -d
|
||||
|
||||
# Verify services are running
|
||||
docker-compose ps
|
||||
|
||||
# Check logs
|
||||
docker-compose logs -f backend
|
||||
docker-compose logs -f frontend
|
||||
```
|
||||
|
||||
### 4. Verify Deployment
|
||||
|
||||
```bash
|
||||
# Check backend health
|
||||
curl http://localhost:3000/api/health
|
||||
|
||||
# Check frontend
|
||||
curl http://localhost/
|
||||
|
||||
# View logs
|
||||
docker-compose logs backend
|
||||
docker-compose logs frontend
|
||||
```
|
||||
|
||||
## SSL/TLS Configuration (Optional)
|
||||
|
||||
### Using Let's Encrypt with Certbot
|
||||
|
||||
```bash
|
||||
# Install certbot
|
||||
sudo apt-get install certbot
|
||||
|
||||
# Generate certificate
|
||||
sudo certbot certonly --standalone -d yourdomain.com
|
||||
|
||||
# Create SSL volume mapping in docker-compose.yml:
|
||||
# volumes:
|
||||
# - /etc/letsencrypt/live/yourdomain.com:/etc/nginx/certs:ro
|
||||
```
|
||||
|
||||
### Update nginx.prod.conf
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name yourdomain.com;
|
||||
|
||||
ssl_certificate /etc/nginx/certs/fullchain.pem;
|
||||
ssl_certificate_key /etc/nginx/certs/privkey.pem;
|
||||
|
||||
# ... rest of config
|
||||
}
|
||||
|
||||
# Redirect HTTP to HTTPS
|
||||
server {
|
||||
listen 80;
|
||||
server_name yourdomain.com;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
```
|
||||
|
||||
## Database Management
|
||||
|
||||
### Backup
|
||||
|
||||
```bash
|
||||
# Manual backup
|
||||
docker-compose exec backend cp /app/database/data/edh-stats.db /app/database/data/backup-$(date +%Y%m%d-%H%M%S).db
|
||||
|
||||
# Or mount backup volume
|
||||
docker run -v edh-stats_sqlite_data:/data -v ~/backups:/backup \
|
||||
busybox sh -c "cp /data/edh-stats.db /backup/edh-stats-$(date +%Y%m%d-%H%M%S).db"
|
||||
```
|
||||
|
||||
### Restore
|
||||
|
||||
```bash
|
||||
# Stop services
|
||||
docker-compose down
|
||||
|
||||
# Restore from backup
|
||||
docker run -v edh-stats_sqlite_data:/data -v ~/backups:/backup \
|
||||
busybox sh -c "cp /backup/edh-stats-YYYYMMDD-HHMMSS.db /data/edh-stats.db"
|
||||
|
||||
# Start services
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
## Updating to New Version
|
||||
|
||||
### 1. Pull New Images
|
||||
|
||||
```bash
|
||||
docker-compose pull
|
||||
```
|
||||
|
||||
### 2. Restart Services (Zero-Downtime Update)
|
||||
|
||||
```bash
|
||||
# Update and restart with health checks ensuring availability
|
||||
docker-compose up -d --no-deps --build
|
||||
```
|
||||
|
||||
### 3. Verify Update
|
||||
|
||||
```bash
|
||||
# Check version/logs
|
||||
docker-compose logs -f backend
|
||||
```
|
||||
|
||||
## Monitoring & Maintenance
|
||||
|
||||
### View Logs
|
||||
|
||||
```bash
|
||||
# Real-time logs
|
||||
docker-compose logs -f
|
||||
|
||||
# Backend only
|
||||
docker-compose logs -f backend
|
||||
|
||||
# Last 100 lines
|
||||
docker-compose logs --tail 100
|
||||
|
||||
# Specific time range
|
||||
docker-compose logs --since 2024-01-15T10:00:00Z --until 2024-01-15T11:00:00Z
|
||||
```
|
||||
|
||||
### Resource Monitoring
|
||||
|
||||
```bash
|
||||
# View resource usage
|
||||
docker stats
|
||||
|
||||
# View service details
|
||||
docker-compose ps
|
||||
docker-compose stats
|
||||
```
|
||||
|
||||
### Health Checks
|
||||
|
||||
```bash
|
||||
# Backend health
|
||||
curl -s http://localhost:3000/api/health | jq .
|
||||
|
||||
# Frontend connectivity
|
||||
curl -s http://localhost/ | head -20
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Images Won't Pull
|
||||
|
||||
```bash
|
||||
# Verify GHCR login
|
||||
docker login ghcr.io
|
||||
|
||||
# Check image exists
|
||||
docker pull ghcr.io/YOUR_GITHUB_USER/edh-stats-backend:latest
|
||||
```
|
||||
|
||||
### Services Won't Start
|
||||
|
||||
```bash
|
||||
# Check logs
|
||||
docker-compose logs
|
||||
|
||||
# Verify secrets exist
|
||||
docker secret ls
|
||||
|
||||
# Verify configuration
|
||||
docker-compose config
|
||||
|
||||
# Check ports are available
|
||||
sudo netstat -tulpn | grep LISTEN
|
||||
```
|
||||
|
||||
### Database Issues
|
||||
|
||||
```bash
|
||||
# Check database file exists
|
||||
docker-compose exec backend ls -lh /app/database/data/
|
||||
|
||||
# Verify permissions
|
||||
docker-compose exec backend chmod 666 /app/database/data/edh-stats.db
|
||||
|
||||
# Check database integrity
|
||||
docker-compose exec backend sqlite3 /app/database/data/edh-stats.db "PRAGMA integrity_check;"
|
||||
```
|
||||
|
||||
### Performance Issues
|
||||
|
||||
```bash
|
||||
# Check resource limits in docker-compose.yml
|
||||
# Backend limits:
|
||||
# memory: 512M
|
||||
# cpus: '0.5'
|
||||
|
||||
# Monitor actual usage
|
||||
docker stats edh-stats-backend-1
|
||||
|
||||
# Increase limits if needed
|
||||
docker update --memory 1G --cpus 1.0 edh-stats-backend-1
|
||||
```
|
||||
|
||||
## Security Best Practices
|
||||
|
||||
1. **Secrets Management**
|
||||
- Never commit secrets to Git
|
||||
- Use Docker secrets for sensitive data
|
||||
- Rotate JWT_SECRET periodically
|
||||
|
||||
2. **Environment Variables**
|
||||
- Set CORS_ORIGIN to your domain
|
||||
- Keep LOG_LEVEL as 'warn' in production
|
||||
- Set ALLOW_REGISTRATION=false unless needed
|
||||
|
||||
3. **Network Security**
|
||||
- Use firewall to restrict access
|
||||
- Enable SSL/TLS for production
|
||||
- Use strong passwords for admin accounts
|
||||
|
||||
4. **Database**
|
||||
- Regular backups (daily recommended)
|
||||
- Monitor database size
|
||||
- Archive old game records periodically
|
||||
|
||||
5. **Monitoring**
|
||||
- Set up log aggregation
|
||||
- Monitor resource usage
|
||||
- Health checks enabled by default
|
||||
|
||||
## Rollback
|
||||
|
||||
If issues occur after deployment:
|
||||
|
||||
```bash
|
||||
# Stop current version
|
||||
docker-compose down
|
||||
|
||||
# Pull and start previous version
|
||||
docker pull ghcr.io/YOUR_GITHUB_USER/edh-stats-backend:v1.0.0
|
||||
docker pull ghcr.io/YOUR_GITHUB_USER/edh-stats-frontend:v1.0.0
|
||||
|
||||
# Update docker-compose.yml to use previous version
|
||||
# Then restart
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
## Support & Issues
|
||||
|
||||
For deployment issues:
|
||||
|
||||
1. Check logs: `docker-compose logs`
|
||||
2. Verify configuration: `docker-compose config`
|
||||
3. Test connectivity: `docker-compose exec backend wget -O- http://localhost:3000/api/health`
|
||||
4. Create GitHub issue with logs and configuration
|
||||
|
||||
## Versioning Strategy
|
||||
|
||||
- **Stable Releases**: `v1.0.0`, `v1.1.0`, etc.
|
||||
- **Release Candidates**: `v1.0.0-rc1`, `v1.0.0-rc2`
|
||||
- **Development**: `main-abcd1234` (branch-commit)
|
||||
|
||||
Always use tagged versions in production. Avoid using `latest` tag without pinning to specific version.
|
||||
234
PRODUCTION_CHECKLIST.md
Normal file
234
PRODUCTION_CHECKLIST.md
Normal file
@@ -0,0 +1,234 @@
|
||||
# Production Release Checklist
|
||||
|
||||
Use this checklist before deploying to production. Ensure all items are completed and verified.
|
||||
|
||||
## Pre-Deployment (1-2 weeks before)
|
||||
|
||||
### Code Quality & Testing
|
||||
- [ ] All tests pass locally (`npm test` or equivalent)
|
||||
- [ ] Code review completed for all changes
|
||||
- [ ] No console warnings or errors in development
|
||||
- [ ] Dependencies are up-to-date and security scanning passed
|
||||
- [ ] Linting passes (`npm run lint`)
|
||||
|
||||
### Documentation
|
||||
- [ ] README.md is updated with latest features
|
||||
- [ ] DEPLOYMENT.md is complete and accurate
|
||||
- [ ] API documentation is current
|
||||
- [ ] Configuration options are documented
|
||||
|
||||
### Security Review
|
||||
- [ ] No hardcoded secrets in codebase
|
||||
- [ ] All API endpoints validate input properly
|
||||
- [ ] Database queries use parameterized statements (no SQL injection)
|
||||
- [ ] CORS configuration is restrictive (specific domains)
|
||||
- [ ] Password hashing is using bcryptjs with 12+ rounds
|
||||
- [ ] JWT tokens have proper expiration (15 minutes)
|
||||
- [ ] Rate limiting is configured (enabled in docker-compose.prod.yml)
|
||||
|
||||
### Database
|
||||
- [ ] Database schema is final and tested
|
||||
- [ ] All migrations are tested and reversible
|
||||
- [ ] Database views are optimized
|
||||
- [ ] Indexes are in place for frequently queried columns
|
||||
- [ ] Backup and restore procedures are documented and tested
|
||||
|
||||
### Performance
|
||||
- [ ] Bundle size is reasonable (< 5MB for frontend)
|
||||
- [ ] Database queries are optimized
|
||||
- [ ] API response times are acceptable (< 500ms for 95th percentile)
|
||||
- [ ] Static assets have caching enabled
|
||||
- [ ] Gzip compression is enabled
|
||||
|
||||
## Deployment Week
|
||||
|
||||
### Image Build & Push
|
||||
- [ ] Version number is incremented (semantic versioning)
|
||||
- [ ] Git tag is created: `git tag v1.0.0`
|
||||
- [ ] Deployment script runs successfully: `./deploy.sh 1.0.0`
|
||||
- [ ] Images are pushed to GitHub Container Registry
|
||||
- [ ] Image sizes are reasonable (backend < 200MB, frontend < 100MB)
|
||||
- [ ] Image scanning shows no critical vulnerabilities
|
||||
|
||||
### Server Preparation
|
||||
- [ ] Server has Docker and Docker Compose installed
|
||||
- [ ] Firewall rules allow necessary ports (80, 443)
|
||||
- [ ] SSL certificates are ready (if using HTTPS)
|
||||
- [ ] Domain DNS is configured and resolving
|
||||
- [ ] Disk space is sufficient (>10GB recommended)
|
||||
- [ ] Server has adequate resources (2GB RAM minimum, 1 CPU)
|
||||
|
||||
### Configuration
|
||||
- [ ] `.env` file created with all required variables
|
||||
- [ ] `CORS_ORIGIN` set to correct domain
|
||||
- [ ] `ALLOW_REGISTRATION` set appropriately (false by default)
|
||||
- [ ] JWT_SECRET is securely generated and stored
|
||||
- [ ] Log level is set to 'warn' in production
|
||||
- [ ] Database path points to persistent volume
|
||||
|
||||
### Secrets Management
|
||||
- [ ] Docker secret 'jwt_secret' is created
|
||||
- [ ] Secret file is securely stored and deleted after import
|
||||
- [ ] Docker secret command tested: `docker secret ls`
|
||||
- [ ] Backup of jwt_secret stored securely (offsite)
|
||||
|
||||
## Day Before Deployment
|
||||
|
||||
### Final Verification
|
||||
- [ ] Run latest images locally with `docker-compose up -d`
|
||||
- [ ] Test all major features work correctly
|
||||
- [ ] Check database is created and migrations run
|
||||
- [ ] Verify API endpoints respond correctly
|
||||
- [ ] Test authentication (login, registration if enabled)
|
||||
- [ ] Test game logging and statistics
|
||||
- [ ] Verify frontend loads and is responsive
|
||||
|
||||
### Backup Everything
|
||||
- [ ] Current database backed up (if migrating existing data)
|
||||
- [ ] Configuration files backed up
|
||||
- [ ] DNS settings noted and ready to switch
|
||||
- [ ] Rollback plan documented and tested
|
||||
|
||||
### Team Communication
|
||||
- [ ] Team notified of deployment schedule
|
||||
- [ ] Maintenance window communicated to users
|
||||
- [ ] Rollback contact information shared
|
||||
- [ ] Deployment plan reviewed with team
|
||||
|
||||
## Deployment Day
|
||||
|
||||
### Pre-Deployment (1 hour before)
|
||||
|
||||
- [ ] All services currently running and stable
|
||||
- [ ] Database integrity verified
|
||||
- [ ] Recent backups completed
|
||||
- [ ] Monitoring tools configured and running
|
||||
- [ ] Team members available for issues
|
||||
|
||||
### Deployment Steps
|
||||
|
||||
1. [ ] Pull latest images from GHCR
|
||||
```bash
|
||||
docker pull ghcr.io/YOUR_USER/edh-stats-backend:1.0.0
|
||||
docker pull ghcr.io/YOUR_USER/edh-stats-frontend:1.0.0
|
||||
```
|
||||
|
||||
2. [ ] Stop current services (if upgrading)
|
||||
```bash
|
||||
docker-compose down
|
||||
```
|
||||
|
||||
3. [ ] Update docker-compose.yml with new versions
|
||||
|
||||
4. [ ] Start new services
|
||||
```bash
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
5. [ ] Wait for services to become healthy (check health checks)
|
||||
```bash
|
||||
docker-compose ps
|
||||
```
|
||||
|
||||
### Post-Deployment Verification (immediate)
|
||||
|
||||
- [ ] All containers are running: `docker-compose ps`
|
||||
- [ ] Backend health check passes: `curl http://localhost:3000/api/health`
|
||||
- [ ] Frontend loads: `curl http://localhost/`
|
||||
- [ ] No error logs: `docker-compose logs | grep ERROR`
|
||||
- [ ] Database is accessible and has data
|
||||
- [ ] API endpoints respond (test authentication)
|
||||
- [ ] UI loads correctly in browser
|
||||
- [ ] Forms work (try logging a game if applicable)
|
||||
|
||||
### Testing (15-30 minutes after)
|
||||
|
||||
- [ ] Test user login functionality
|
||||
- [ ] Test game logging (if enabled)
|
||||
- [ ] Test viewing statistics
|
||||
- [ ] Test editing/deleting records
|
||||
- [ ] Check browser console for JavaScript errors
|
||||
- [ ] Verify HTTPS/SSL (if configured)
|
||||
- [ ] Test on mobile device
|
||||
|
||||
### Monitoring (first 24 hours)
|
||||
|
||||
- [ ] Monitor error logs every 30 minutes
|
||||
- [ ] Monitor resource usage (CPU, memory)
|
||||
- [ ] Check database size and integrity
|
||||
- [ ] Monitor API response times
|
||||
- [ ] Review user feedback/issues reported
|
||||
- [ ] Ensure backups are being created
|
||||
|
||||
## If Issues Occur
|
||||
|
||||
### Quick Diagnostics
|
||||
```bash
|
||||
# Check service status
|
||||
docker-compose ps
|
||||
|
||||
# View recent logs
|
||||
docker-compose logs --tail 50 backend
|
||||
docker-compose logs --tail 50 frontend
|
||||
|
||||
# Check resource usage
|
||||
docker stats
|
||||
|
||||
# Test backend connectivity
|
||||
curl -v http://localhost:3000/api/health
|
||||
|
||||
# Test database
|
||||
docker-compose exec backend sqlite3 /app/database/data/edh-stats.db "SELECT COUNT(*) FROM users;"
|
||||
```
|
||||
|
||||
### Rollback Procedure
|
||||
1. [ ] Stop current version: `docker-compose down`
|
||||
2. [ ] Update docker-compose.yml to previous version
|
||||
3. [ ] Restore database backup if needed
|
||||
4. [ ] Restart with previous version: `docker-compose up -d`
|
||||
5. [ ] Verify service health
|
||||
6. [ ] Notify team and users
|
||||
|
||||
## Post-Deployment (Next 24-48 hours)
|
||||
|
||||
### Stability Monitoring
|
||||
- [ ] No error spikes in logs
|
||||
- [ ] Database size is stable
|
||||
- [ ] API response times are acceptable
|
||||
- [ ] No memory leaks or increasing CPU usage
|
||||
- [ ] User authentication working smoothly
|
||||
- [ ] No reported critical issues
|
||||
|
||||
### Documentation & Communication
|
||||
- [ ] Update version number in documentation
|
||||
- [ ] Post release notes (what was changed)
|
||||
- [ ] Thank team for their efforts
|
||||
- [ ] Respond to any user questions/feedback
|
||||
|
||||
### Metrics & Learning
|
||||
- [ ] Deployment time was within expectations
|
||||
- [ ] Zero downtime achieved (if applicable)
|
||||
- [ ] Document any unexpected issues and resolutions
|
||||
- [ ] Identify improvements for next deployment
|
||||
- [ ] Update deployment checklist based on learnings
|
||||
|
||||
## Success Criteria
|
||||
|
||||
Deployment is successful when:
|
||||
|
||||
✅ All services are running and healthy
|
||||
✅ All endpoints respond correctly
|
||||
✅ Database has all data and is accessible
|
||||
✅ Users can login and use the application
|
||||
✅ No critical errors in logs
|
||||
✅ Performance is acceptable (< 500ms response time)
|
||||
✅ SSL/HTTPS working (if configured)
|
||||
✅ Backups are being created regularly
|
||||
✅ Team is confident in stability
|
||||
✅ Users are satisfied with functionality
|
||||
|
||||
---
|
||||
|
||||
**Version**: 1.0.0
|
||||
**Last Updated**: 2024-01-15
|
||||
**Maintained by**: [Your Team Name]
|
||||
303
PRODUCTION_RELEASE.md
Normal file
303
PRODUCTION_RELEASE.md
Normal file
@@ -0,0 +1,303 @@
|
||||
# Production Release - Complete Setup Guide
|
||||
|
||||
The EDH Stats Tracker is now ready for production deployment! This document summarizes all deployment resources and how to use them.
|
||||
|
||||
## 📦 What Was Created
|
||||
|
||||
### Scripts
|
||||
- **`deploy.sh`** - Automated deployment script for building and pushing Docker images to GHCR
|
||||
- Validates prerequisites
|
||||
- Builds backend and frontend images
|
||||
- Pushes to GitHub Container Registry
|
||||
- Generates production configuration
|
||||
- ~5 minutes to run
|
||||
|
||||
### Documentation
|
||||
- **`QUICK_DEPLOY.md`** - Fast-track 5-10 minute deployment guide (START HERE!)
|
||||
- **`DEPLOYMENT.md`** - Comprehensive production deployment guide with all details
|
||||
- **`PRODUCTION_CHECKLIST.md`** - Pre/during/post deployment verification checklist
|
||||
- **`PRODUCTION_RELEASE.md`** - This file
|
||||
|
||||
### Docker Configuration
|
||||
- **`frontend/Dockerfile.prod`** - Production-optimized nginx frontend Dockerfile
|
||||
- **`frontend/nginx.prod.conf`** - Already exists, fully configured for production
|
||||
- **`.github/workflows/publish.yml`** - GitHub Actions CI/CD pipeline (automated builds)
|
||||
|
||||
### Updated Files
|
||||
- **`.gitignore`** - Added deployment and secrets files to ignore list
|
||||
|
||||
## 🚀 Quick Start (Choose One)
|
||||
|
||||
### Path 1: Manual Build & Deploy (Recommended for First Release)
|
||||
|
||||
```bash
|
||||
# 1. Create GitHub token at https://github.com/settings/tokens
|
||||
# Select: write:packages scope
|
||||
# Copy: the token value
|
||||
|
||||
# 2. Build and push images
|
||||
export GITHUB_USER=your-github-username
|
||||
export GHCR_TOKEN=ghcr_xxxxxxxxxxxxx
|
||||
|
||||
./deploy.sh v1.0.0 $GHCR_TOKEN
|
||||
|
||||
# 3. Copy generated docker-compose.prod.deployed.yml to server
|
||||
# 4. Follow QUICK_DEPLOY.md steps 3-8 to complete setup
|
||||
|
||||
# Done! Your app is in production.
|
||||
```
|
||||
|
||||
**Time: ~20-30 minutes**
|
||||
**Best for: First release, production verification**
|
||||
|
||||
### Path 2: GitHub Actions (Fully Automated)
|
||||
|
||||
```bash
|
||||
# 1. Push release tag
|
||||
git tag v1.0.0
|
||||
git push origin v1.0.0
|
||||
|
||||
# 2. GitHub Actions automatically:
|
||||
# - Builds Docker images
|
||||
# - Pushes to GHCR
|
||||
# - Generates docker-compose.yml
|
||||
# - Creates release with artifacts
|
||||
|
||||
# 3. Download docker-compose.prod.deployed.yml from GitHub Releases
|
||||
# 4. Follow QUICK_DEPLOY.md steps 3-8 to complete setup
|
||||
|
||||
# Done! CI/CD pipeline handled the building.
|
||||
```
|
||||
|
||||
**Time: ~15-20 minutes**
|
||||
**Best for: Subsequent releases, automated workflows**
|
||||
|
||||
## 📋 Documentation Map
|
||||
|
||||
### If you want to...
|
||||
|
||||
| Goal | Document | Time |
|
||||
|------|----------|------|
|
||||
| **Get app running in 10 min** | QUICK_DEPLOY.md | 10-15 min |
|
||||
| **Understand full process** | DEPLOYMENT.md | Read through |
|
||||
| **Verify before deploying** | PRODUCTION_CHECKLIST.md | Use as checklist |
|
||||
| **Troubleshoot issues** | DEPLOYMENT.md (Troubleshooting section) | Variable |
|
||||
| **Setup SSL/HTTPS** | DEPLOYMENT.md (SSL/TLS Configuration) | 15-20 min |
|
||||
| **Automate future releases** | .github/workflows/publish.yml | Already configured |
|
||||
| **Backup & restore** | DEPLOYMENT.md (Database Management) | As needed |
|
||||
| **Update to new version** | DEPLOYMENT.md (Updating to New Version) | 5-10 min |
|
||||
|
||||
## 🔐 Security Considerations
|
||||
|
||||
### Secrets (Never Commit These)
|
||||
- `.env` file with real values
|
||||
- Docker secret files
|
||||
- SSL/TLS certificates
|
||||
- JWT_SECRET values
|
||||
|
||||
All are properly in `.gitignore` ✓
|
||||
|
||||
### Required Before Deployment
|
||||
- [ ] GitHub Personal Access Token with `write:packages` scope
|
||||
- [ ] Secure JWT secret (generated via `openssl rand -base64 32`)
|
||||
- [ ] Domain name with DNS configured
|
||||
- [ ] SSL certificates (Let's Encrypt is free)
|
||||
|
||||
### Production Settings
|
||||
- `NODE_ENV=production` ✓
|
||||
- `LOG_LEVEL=warn` (not debug) ✓
|
||||
- `ALLOW_REGISTRATION=false` (by default) ✓
|
||||
- Rate limiting enabled ✓
|
||||
- Security headers configured ✓
|
||||
- CORS restricted to your domain ✓
|
||||
|
||||
## 🐳 Image Information
|
||||
|
||||
### Backend Image
|
||||
- **Base**: Node.js (slim)
|
||||
- **Size**: ~150-180 MB
|
||||
- **Registry**: ghcr.io/YOUR_USER/edh-stats-backend:v1.0.0
|
||||
- **Health Check**: /api/health endpoint
|
||||
- **Ports**: 3000 (internal only, proxied through nginx)
|
||||
|
||||
### Frontend Image
|
||||
- **Base**: nginx:alpine
|
||||
- **Size**: ~50-60 MB
|
||||
- **Registry**: ghcr.io/YOUR_USER/edh-stats-frontend:v1.0.0
|
||||
- **Health Check**: / (root)
|
||||
- **Ports**: 80 (HTTP), 443 (HTTPS)
|
||||
|
||||
### Volumes
|
||||
- `sqlite_data` - Database persistence (required)
|
||||
- `app_logs` - Application logs (optional)
|
||||
|
||||
## ✅ Deployment Verification
|
||||
|
||||
After deployment, verify with these commands:
|
||||
|
||||
```bash
|
||||
# Service status
|
||||
docker-compose ps
|
||||
|
||||
# Backend health
|
||||
curl http://localhost:3000/api/health
|
||||
|
||||
# Frontend connectivity
|
||||
curl http://localhost/
|
||||
|
||||
# Logs (if issues)
|
||||
docker-compose logs --tail 50
|
||||
|
||||
# Resource usage
|
||||
docker stats
|
||||
```
|
||||
|
||||
## 📈 Monitoring & Maintenance
|
||||
|
||||
### Daily Checks
|
||||
```bash
|
||||
# View error logs
|
||||
docker-compose logs backend | grep -i error
|
||||
|
||||
# Check resource usage
|
||||
docker stats --no-stream
|
||||
|
||||
# Database integrity
|
||||
docker-compose exec backend sqlite3 /app/database/data/edh-stats.db "PRAGMA integrity_check;"
|
||||
```
|
||||
|
||||
### Weekly Tasks
|
||||
- Review logs for errors
|
||||
- Monitor disk usage
|
||||
- Backup database
|
||||
- Check for available updates
|
||||
|
||||
### Monthly Tasks
|
||||
- Security patch updates
|
||||
- SSL certificate renewal (automatic with certbot)
|
||||
- Review application metrics
|
||||
- Update dependencies
|
||||
|
||||
## 🔄 Release Cycle
|
||||
|
||||
### Versioning
|
||||
Follow semantic versioning:
|
||||
- `v1.0.0` - Initial release
|
||||
- `v1.1.0` - Minor features/improvements
|
||||
- `v1.0.1` - Bugfixes
|
||||
- `v2.0.0` - Major breaking changes
|
||||
|
||||
### Release Process
|
||||
1. Make code changes and test locally
|
||||
2. Update version in README and documentation
|
||||
3. Create git tag: `git tag v1.1.0`
|
||||
4. Push tag: `git push origin v1.1.0`
|
||||
5. GitHub Actions builds and pushes automatically
|
||||
6. Download docker-compose from GitHub Releases
|
||||
7. Deploy to server: `docker-compose pull && docker-compose up -d`
|
||||
|
||||
**Total time for release: ~30 minutes**
|
||||
|
||||
## 🆘 Need Help?
|
||||
|
||||
1. **First time deployment?**
|
||||
- Read: QUICK_DEPLOY.md
|
||||
- Follow step-by-step
|
||||
- Check PRODUCTION_CHECKLIST.md
|
||||
|
||||
2. **Issues during deployment?**
|
||||
- Check: DEPLOYMENT.md → Troubleshooting section
|
||||
- View logs: `docker-compose logs`
|
||||
- Run: `docker-compose config` to verify configuration
|
||||
|
||||
3. **Server problems?**
|
||||
- SSH to server
|
||||
- Run: `docker-compose ps` (service status)
|
||||
- Run: `docker-compose logs backend` (error details)
|
||||
- Run: `docker stats` (resource usage)
|
||||
|
||||
4. **Database issues?**
|
||||
- See: DEPLOYMENT.md → Database Management
|
||||
- Backup before making changes
|
||||
- Test restore procedure
|
||||
|
||||
## 🎯 Success Criteria
|
||||
|
||||
Your deployment is successful when:
|
||||
|
||||
✅ All containers running: `docker-compose ps` shows all "Up"
|
||||
✅ Backend responding: `curl http://localhost:3000/api/health` returns 200
|
||||
✅ Frontend accessible: Browser can view the application
|
||||
✅ Authentication works: Can login with test credentials
|
||||
✅ No critical errors: `docker-compose logs | grep ERROR` shows nothing
|
||||
✅ Performance good: API responses < 500ms
|
||||
✅ Database intact: Can query games and users
|
||||
✅ Logs clean: Only INFO/WARN level messages, no exceptions
|
||||
✅ Memory stable: `docker stats` doesn't show increasing memory
|
||||
|
||||
## 📚 Complete File Structure
|
||||
|
||||
```
|
||||
edh-stats/
|
||||
├── deploy.sh # Main deployment script
|
||||
├── DEPLOYMENT.md # Comprehensive guide
|
||||
├── PRODUCTION_CHECKLIST.md # Pre/during/post checklist
|
||||
├── QUICK_DEPLOY.md # Fast-track guide (START HERE!)
|
||||
├── PRODUCTION_RELEASE.md # This file
|
||||
├── .github/
|
||||
│ └── workflows/
|
||||
│ └── publish.yml # GitHub Actions CI/CD
|
||||
├── .gitignore # Updated with deployment files
|
||||
├── frontend/
|
||||
│ ├── Dockerfile.prod # Production Dockerfile
|
||||
│ ├── nginx.prod.conf # Production nginx config
|
||||
│ └── public/ # Static files
|
||||
└── backend/
|
||||
├── Dockerfile # Backend Dockerfile
|
||||
└── src/ # Application code
|
||||
```
|
||||
|
||||
## 🎓 Learning Resources
|
||||
|
||||
- Docker documentation: https://docs.docker.com/
|
||||
- Docker Compose: https://docs.docker.com/compose/
|
||||
- GitHub Container Registry: https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry
|
||||
- GitHub Actions: https://docs.github.com/en/actions
|
||||
- Let's Encrypt: https://letsencrypt.org/
|
||||
- Nginx configuration: https://nginx.org/en/docs/
|
||||
|
||||
## 🏁 Next Steps
|
||||
|
||||
1. **Create GitHub Token**
|
||||
- Visit: https://github.com/settings/tokens
|
||||
- Create token with `write:packages` scope
|
||||
- Save securely
|
||||
|
||||
2. **Build First Release**
|
||||
- Choose Path 1 or Path 2 above
|
||||
- Follow either QUICK_DEPLOY.md or use GitHub Actions
|
||||
|
||||
3. **Deploy to Server**
|
||||
- Set up server (see QUICK_DEPLOY.md)
|
||||
- Configure domain and SSL
|
||||
- Start services
|
||||
|
||||
4. **Verify & Monitor**
|
||||
- Test all features
|
||||
- Check logs and metrics
|
||||
- Plan backup strategy
|
||||
|
||||
5. **Iterate & Update**
|
||||
- Continue developing features
|
||||
- Create new git tags for releases
|
||||
- Deploy updates (zero-downtime with health checks)
|
||||
|
||||
---
|
||||
|
||||
**Congratulations!** 🎉 You now have everything needed to deploy EDH Stats Tracker to production.
|
||||
|
||||
**Questions? Start with QUICK_DEPLOY.md and follow the step-by-step instructions.**
|
||||
|
||||
**Version**: 1.0.0
|
||||
**Date**: 2024-01-15
|
||||
**Status**: Ready for Production ✓
|
||||
392
QUICK_DEPLOY.md
Normal file
392
QUICK_DEPLOY.md
Normal file
@@ -0,0 +1,392 @@
|
||||
# Quick Deployment Guide
|
||||
|
||||
Fast-track guide to deploy EDH Stats Tracker to production in minutes.
|
||||
|
||||
## TL;DR - 5 Minute Setup
|
||||
|
||||
### Step 1: Generate GitHub Token (GitHub UI - 2 min)
|
||||
```
|
||||
1. Visit: https://github.com/settings/tokens
|
||||
2. Click "Generate new token" → "Generate new token (classic)"
|
||||
3. Check: write:packages
|
||||
4. Copy token (save it safely!)
|
||||
```
|
||||
|
||||
### Step 2: Build & Push Images (Local - 3 min)
|
||||
```bash
|
||||
cd edh-stats
|
||||
|
||||
# Export your details
|
||||
export GITHUB_USER=your-username
|
||||
export GHCR_TOKEN=ghcr_xxxxx
|
||||
|
||||
# Run deployment
|
||||
./deploy.sh v1.0.0 $GHCR_TOKEN
|
||||
```
|
||||
|
||||
### Step 3: Deploy to Server
|
||||
```bash
|
||||
# Copy generated config
|
||||
scp docker-compose.prod.deployed.yml user@server:~/edh-stats/
|
||||
|
||||
# On server:
|
||||
ssh user@server
|
||||
cd ~/edh-stats
|
||||
|
||||
# Create secret
|
||||
openssl rand -base64 32 > jwt_secret.txt
|
||||
docker secret create jwt_secret jwt_secret.txt
|
||||
|
||||
# Start
|
||||
docker-compose -f docker-compose.prod.deployed.yml up -d
|
||||
```
|
||||
|
||||
**Done!** 🎉 Visit your domain to see it live.
|
||||
|
||||
---
|
||||
|
||||
## Full Steps with Explanations
|
||||
|
||||
### 1. Prepare Credentials
|
||||
|
||||
**Create GitHub Personal Access Token:**
|
||||
- Go to https://github.com/settings/tokens
|
||||
- Click "Generate new token (classic)"
|
||||
- Select scopes:
|
||||
- ☑️ `write:packages` (push Docker images)
|
||||
- ☑️ `read:packages` (pull Docker images)
|
||||
- Click "Generate token"
|
||||
- **Copy and save** the token (you won't see it again!)
|
||||
|
||||
**Set Environment Variables:**
|
||||
```bash
|
||||
export GITHUB_USER=your-github-username
|
||||
export GHCR_TOKEN=ghcr_xxxxxxxxxxxxx
|
||||
```
|
||||
|
||||
### 2. Build Docker Images
|
||||
|
||||
**Option A: Manual Script**
|
||||
```bash
|
||||
cd edh-stats
|
||||
|
||||
# Run deployment script
|
||||
./deploy.sh v1.0.0 $GHCR_TOKEN
|
||||
|
||||
# Or without token (will prompt)
|
||||
./deploy.sh v1.0.0
|
||||
```
|
||||
|
||||
**Option B: GitHub Actions (Automated)**
|
||||
```bash
|
||||
# Push tag to trigger automatic build
|
||||
git tag v1.0.0
|
||||
git push origin v1.0.0
|
||||
|
||||
# GitHub Actions will:
|
||||
# - Build images automatically
|
||||
# - Push to GHCR
|
||||
# - Generate deployment config
|
||||
# - Create release with artifacts
|
||||
|
||||
# Download docker-compose.prod.deployed.yml from GitHub Releases
|
||||
```
|
||||
|
||||
### 3. Prepare Server
|
||||
|
||||
**Login to Your Server:**
|
||||
```bash
|
||||
ssh user@your-server.com
|
||||
cd ~
|
||||
mkdir edh-stats
|
||||
cd edh-stats
|
||||
```
|
||||
|
||||
**Create Directories:**
|
||||
```bash
|
||||
mkdir -p data/database
|
||||
mkdir -p data/logs
|
||||
```
|
||||
|
||||
**Copy Configuration Files:**
|
||||
```bash
|
||||
# From your local machine:
|
||||
scp docker-compose.prod.deployed.yml user@your-server.com:~/edh-stats/docker-compose.yml
|
||||
scp frontend/nginx.prod.conf user@your-server.com:~/edh-stats/
|
||||
```
|
||||
|
||||
**Create .env File on Server:**
|
||||
```bash
|
||||
cat > ~/.env << EOF
|
||||
# Required
|
||||
CORS_ORIGIN=https://yourdomain.com
|
||||
|
||||
# Optional
|
||||
ALLOW_REGISTRATION=false
|
||||
LOG_LEVEL=warn
|
||||
EOF
|
||||
```
|
||||
|
||||
### 4. Setup Docker Secrets
|
||||
|
||||
**Generate JWT Secret:**
|
||||
```bash
|
||||
# On server
|
||||
openssl rand -base64 32 > jwt_secret.txt
|
||||
cat jwt_secret.txt
|
||||
# Save this somewhere safe for backups!
|
||||
```
|
||||
|
||||
**Create Docker Secret:**
|
||||
```bash
|
||||
docker secret create jwt_secret jwt_secret.txt
|
||||
|
||||
# Verify
|
||||
docker secret ls
|
||||
docker secret inspect jwt_secret
|
||||
```
|
||||
|
||||
**Cleanup:**
|
||||
```bash
|
||||
# Remove local secret file after import
|
||||
rm jwt_secret.txt
|
||||
```
|
||||
|
||||
### 5. Start Services
|
||||
|
||||
**Pull Latest Images:**
|
||||
```bash
|
||||
docker login ghcr.io # Use your GitHub token as password
|
||||
|
||||
docker pull ghcr.io/YOUR_GITHUB_USER/edh-stats-backend:v1.0.0
|
||||
docker pull ghcr.io/YOUR_GITHUB_USER/edh-stats-frontend:v1.0.0
|
||||
```
|
||||
|
||||
**Start Docker Compose:**
|
||||
```bash
|
||||
docker-compose up -d
|
||||
|
||||
# Wait 10-15 seconds for services to start
|
||||
sleep 15
|
||||
|
||||
# Check status
|
||||
docker-compose ps
|
||||
```
|
||||
|
||||
**Verify Services:**
|
||||
```bash
|
||||
# Backend health
|
||||
curl http://localhost:3000/api/health
|
||||
|
||||
# Frontend
|
||||
curl http://localhost/
|
||||
|
||||
# View logs (if needed)
|
||||
docker-compose logs -f backend
|
||||
```
|
||||
|
||||
### 6. Configure SSL (Optional but Recommended)
|
||||
|
||||
**Install Certbot:**
|
||||
```bash
|
||||
sudo apt-get install certbot
|
||||
sudo certbot certonly --standalone -d yourdomain.com
|
||||
|
||||
# Certificates stored in /etc/letsencrypt/live/yourdomain.com/
|
||||
```
|
||||
|
||||
**Update docker-compose.yml to mount certificates:**
|
||||
```yaml
|
||||
frontend:
|
||||
image: ghcr.io/...
|
||||
volumes:
|
||||
- /etc/letsencrypt/live/yourdomain.com/fullchain.pem:/etc/nginx/certs/fullchain.pem:ro
|
||||
- /etc/letsencrypt/live/yourdomain.com/privkey.pem:/etc/nginx/certs/privkey.pem:ro
|
||||
```
|
||||
|
||||
**Uncomment SSL section in nginx.prod.conf:**
|
||||
```nginx
|
||||
ssl_certificate /etc/nginx/certs/fullchain.pem;
|
||||
ssl_certificate_key /etc/nginx/certs/privkey.pem;
|
||||
```
|
||||
|
||||
**Restart:**
|
||||
```bash
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
### 7. Setup Auto-Renewal (SSL)
|
||||
|
||||
**Create renewal script:**
|
||||
```bash
|
||||
cat > /home/user/renew-ssl.sh << 'EOF'
|
||||
#!/bin/bash
|
||||
certbot renew --quiet
|
||||
docker-compose -f /home/user/edh-stats/docker-compose.yml restart frontend
|
||||
EOF
|
||||
|
||||
chmod +x /home/user/renew-ssl.sh
|
||||
```
|
||||
|
||||
**Add to crontab (runs monthly):**
|
||||
```bash
|
||||
crontab -e
|
||||
|
||||
# Add line:
|
||||
0 2 1 * * /home/user/renew-ssl.sh
|
||||
```
|
||||
|
||||
### 8. Verify Everything Works
|
||||
|
||||
**Test the Application:**
|
||||
```bash
|
||||
# From your browser
|
||||
https://yourdomain.com
|
||||
|
||||
# You should see:
|
||||
1. Login page loads
|
||||
2. Can click to register (if enabled)
|
||||
3. Can login with test credentials
|
||||
4. Can access dashboard
|
||||
5. Can log games
|
||||
6. Can view statistics
|
||||
```
|
||||
|
||||
**Quick Health Check:**
|
||||
```bash
|
||||
curl -s https://yourdomain.com/api/health | jq .
|
||||
|
||||
# Should return: { "status": "ok", ... }
|
||||
```
|
||||
|
||||
**Monitor Logs:**
|
||||
```bash
|
||||
# Watch for errors
|
||||
docker-compose logs -f
|
||||
|
||||
# Should see startup messages, then silence
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting Quick Fixes
|
||||
|
||||
### Services Won't Start
|
||||
```bash
|
||||
# Check what's wrong
|
||||
docker-compose logs
|
||||
|
||||
# Common issues:
|
||||
# 1. Port 80/443 already in use: sudo lsof -i :80
|
||||
# 2. Secret not created: docker secret ls
|
||||
# 3. Image pull failed: docker pull ghcr.io/...
|
||||
|
||||
# Fix and retry
|
||||
docker-compose down
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
### Can't Login to GHCR
|
||||
```bash
|
||||
# Verify token
|
||||
docker login ghcr.io
|
||||
# Username: YOUR_GITHUB_USERNAME
|
||||
# Password: YOUR_GITHUB_TOKEN (not your password!)
|
||||
|
||||
# Test login
|
||||
docker pull ghcr.io/YOUR_USER/edh-stats-backend:v1.0.0
|
||||
```
|
||||
|
||||
### Frontend Shows Blank Page
|
||||
```bash
|
||||
# Check frontend logs
|
||||
docker-compose logs frontend
|
||||
|
||||
# Check browser console (F12)
|
||||
# Common: CORS error = wrong CORS_ORIGIN in .env
|
||||
|
||||
# Fix and restart
|
||||
docker-compose down
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
### Database Issues
|
||||
```bash
|
||||
# Check database exists
|
||||
docker-compose exec backend ls -lh /app/database/data/
|
||||
|
||||
# Verify integrity
|
||||
docker-compose exec backend sqlite3 /app/database/data/edh-stats.db "PRAGMA integrity_check;"
|
||||
|
||||
# Check permissions
|
||||
docker-compose exec backend chmod 666 /app/database/data/edh-stats.db
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Useful Commands
|
||||
|
||||
```bash
|
||||
# View logs (real-time)
|
||||
docker-compose logs -f
|
||||
|
||||
# View logs for specific service
|
||||
docker-compose logs -f backend
|
||||
|
||||
# View last 50 lines
|
||||
docker-compose logs --tail 50
|
||||
|
||||
# Restart services
|
||||
docker-compose restart
|
||||
|
||||
# Restart specific service
|
||||
docker-compose restart backend
|
||||
|
||||
# Stop services
|
||||
docker-compose down
|
||||
|
||||
# Start services
|
||||
docker-compose up -d
|
||||
|
||||
# Check resource usage
|
||||
docker stats
|
||||
|
||||
# Get shell access
|
||||
docker-compose exec backend sh
|
||||
|
||||
# Backup database
|
||||
docker-compose exec backend cp /app/database/data/edh-stats.db /app/database/data/backup-$(date +%s).db
|
||||
|
||||
# Test API
|
||||
curl http://localhost:3000/api/health | jq .
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Updating to New Version
|
||||
|
||||
```bash
|
||||
# New images are pushed automatically when you:
|
||||
# 1. Create a git tag: git tag v1.1.0 && git push origin v1.1.0
|
||||
# 2. Or GitHub Actions builds and pushes to GHCR
|
||||
|
||||
# On server, pull and restart:
|
||||
docker-compose pull
|
||||
docker-compose up -d
|
||||
|
||||
# Done! Zero-downtime update with health checks.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Need Help?
|
||||
|
||||
1. **Read full docs**: See `DEPLOYMENT.md`
|
||||
2. **Check logs**: `docker-compose logs`
|
||||
3. **Review checklist**: `PRODUCTION_CHECKLIST.md`
|
||||
4. **Common issues**: See troubleshooting section above
|
||||
5. **Create GitHub issue**: Include logs and configuration
|
||||
|
||||
---
|
||||
|
||||
**Estimated time: 15-30 minutes** ⏱️
|
||||
408
deploy.sh
Executable file
408
deploy.sh
Executable file
@@ -0,0 +1,408 @@
|
||||
#!/bin/bash
|
||||
|
||||
##############################################################################
|
||||
# EDH Stats Tracker - Production Deployment Script
|
||||
#
|
||||
# This script builds Docker images and pushes them to GitHub Container Registry
|
||||
# Usage: ./deploy.sh [VERSION] [GHCR_TOKEN]
|
||||
#
|
||||
# Example: ./deploy.sh 1.0.0 ghcr_xxxxxxxxxxxxx
|
||||
#
|
||||
# Prerequisites:
|
||||
# - Docker and Docker Compose installed
|
||||
# - GitHub Personal Access Token (with write:packages permission)
|
||||
# - Set GITHUB_REGISTRY_USER environment variable or pass as argument
|
||||
##############################################################################
|
||||
|
||||
set -e # Exit on any error
|
||||
|
||||
# Color codes for output
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Configuration
|
||||
REGISTRY="ghcr.io"
|
||||
GITHUB_USER="${GITHUB_USER:=$(git config --get user.name | tr ' ' '-' | tr '[:upper:]' '[:lower:]')}"
|
||||
PROJECT_NAME="edh-stats"
|
||||
VERSION="${1:-latest}"
|
||||
GHCR_TOKEN="${2}"
|
||||
|
||||
# Image names
|
||||
BACKEND_IMAGE="${REGISTRY}/${GITHUB_USER}/${PROJECT_NAME}-backend:${VERSION}"
|
||||
FRONTEND_IMAGE="${REGISTRY}/${GITHUB_USER}/${PROJECT_NAME}-frontend:${VERSION}"
|
||||
BACKEND_IMAGE_LATEST="${REGISTRY}/${GITHUB_USER}/${PROJECT_NAME}-backend:latest"
|
||||
FRONTEND_IMAGE_LATEST="${REGISTRY}/${GITHUB_USER}/${PROJECT_NAME}-frontend:latest"
|
||||
|
||||
##############################################################################
|
||||
# Helper Functions
|
||||
##############################################################################
|
||||
|
||||
print_header() {
|
||||
echo -e "\n${BLUE}════════════════════════════════════════════════════════════${NC}"
|
||||
echo -e "${BLUE} $1${NC}"
|
||||
echo -e "${BLUE}════════════════════════════════════════════════════════════${NC}\n"
|
||||
}
|
||||
|
||||
print_success() {
|
||||
echo -e "${GREEN}✓ $1${NC}"
|
||||
}
|
||||
|
||||
print_error() {
|
||||
echo -e "${RED}✗ $1${NC}"
|
||||
}
|
||||
|
||||
print_warning() {
|
||||
echo -e "${YELLOW}⚠ $1${NC}"
|
||||
}
|
||||
|
||||
print_info() {
|
||||
echo -e "${BLUE}ℹ $1${NC}"
|
||||
}
|
||||
|
||||
##############################################################################
|
||||
# Validation
|
||||
##############################################################################
|
||||
|
||||
validate_prerequisites() {
|
||||
print_header "Validating Prerequisites"
|
||||
|
||||
# Check if Docker is installed
|
||||
if ! command -v docker &> /dev/null; then
|
||||
print_error "Docker is not installed. Please install Docker first."
|
||||
exit 1
|
||||
fi
|
||||
print_success "Docker is installed"
|
||||
|
||||
# Check if Docker daemon is running
|
||||
if ! docker info > /dev/null 2>&1; then
|
||||
print_error "Docker daemon is not running. Please start Docker."
|
||||
exit 1
|
||||
fi
|
||||
print_success "Docker daemon is running"
|
||||
|
||||
# Check if Git is installed
|
||||
if ! command -v git &> /dev/null; then
|
||||
print_error "Git is not installed. Please install Git first."
|
||||
exit 1
|
||||
fi
|
||||
print_success "Git is installed"
|
||||
|
||||
# Check if we're in a git repository
|
||||
if ! git rev-parse --git-dir > /dev/null 2>&1; then
|
||||
print_error "Not in a Git repository. Please run this script from the project root."
|
||||
exit 1
|
||||
fi
|
||||
print_success "Running from Git repository"
|
||||
}
|
||||
|
||||
check_github_token() {
|
||||
if [ -z "$GHCR_TOKEN" ]; then
|
||||
print_warning "GitHub token not provided. You'll be prompted for credentials when pushing."
|
||||
print_info "Set GHCR_TOKEN environment variable or pass as second argument to skip this prompt"
|
||||
read -sp "Enter GitHub Container Registry Token (or press Enter to use 'docker login'): " GHCR_TOKEN
|
||||
echo
|
||||
|
||||
if [ -z "$GHCR_TOKEN" ]; then
|
||||
print_info "Attempting to use existing Docker credentials..."
|
||||
if ! docker info | grep -q "Username"; then
|
||||
print_warning "No Docker credentials found. Running 'docker login'..."
|
||||
docker login "${REGISTRY}"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
##############################################################################
|
||||
# Build Functions
|
||||
##############################################################################
|
||||
|
||||
build_backend() {
|
||||
print_header "Building Backend Image"
|
||||
|
||||
print_info "Building: ${BACKEND_IMAGE}"
|
||||
docker build \
|
||||
--file ./backend/Dockerfile \
|
||||
--target production \
|
||||
--tag "${BACKEND_IMAGE}" \
|
||||
--tag "${BACKEND_IMAGE_LATEST}" \
|
||||
--build-arg NODE_ENV=production \
|
||||
./backend
|
||||
|
||||
print_success "Backend image built successfully"
|
||||
}
|
||||
|
||||
build_frontend() {
|
||||
print_header "Building Frontend Image"
|
||||
|
||||
print_info "Building: ${FRONTEND_IMAGE}"
|
||||
|
||||
# Create a temporary Dockerfile for frontend if it doesn't exist
|
||||
if [ ! -f "./frontend/Dockerfile.prod" ]; then
|
||||
print_info "Creating production Dockerfile for frontend..."
|
||||
cat > "./frontend/Dockerfile.prod" << 'EOF'
|
||||
FROM nginx:alpine
|
||||
|
||||
# Copy nginx configuration
|
||||
COPY ./nginx.prod.conf /etc/nginx/nginx.conf
|
||||
|
||||
# Copy frontend files
|
||||
COPY ./public /usr/share/nginx/html
|
||||
|
||||
# Expose ports
|
||||
EXPOSE 80 443
|
||||
|
||||
# Health check
|
||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||||
CMD wget --no-verbose --tries=1 --spider http://localhost/health.html || exit 1
|
||||
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
EOF
|
||||
print_success "Created temporary Dockerfile for frontend"
|
||||
fi
|
||||
|
||||
docker build \
|
||||
--file ./frontend/Dockerfile.prod \
|
||||
--tag "${FRONTEND_IMAGE}" \
|
||||
--tag "${FRONTEND_IMAGE_LATEST}" \
|
||||
./
|
||||
|
||||
print_success "Frontend image built successfully"
|
||||
}
|
||||
|
||||
##############################################################################
|
||||
# Push Functions
|
||||
##############################################################################
|
||||
|
||||
login_to_registry() {
|
||||
print_header "Authenticating with GitHub Container Registry"
|
||||
|
||||
if [ -n "$GHCR_TOKEN" ]; then
|
||||
print_info "Logging in with provided token..."
|
||||
echo "$GHCR_TOKEN" | docker login "${REGISTRY}" -u "${GITHUB_USER}" --password-stdin > /dev/null 2>&1
|
||||
else
|
||||
print_info "Using existing Docker authentication..."
|
||||
fi
|
||||
|
||||
print_success "Successfully authenticated with registry"
|
||||
}
|
||||
|
||||
push_backend() {
|
||||
print_header "Pushing Backend Image"
|
||||
|
||||
print_info "Pushing: ${BACKEND_IMAGE}"
|
||||
docker push "${BACKEND_IMAGE}"
|
||||
print_success "Backend image pushed: ${BACKEND_IMAGE}"
|
||||
|
||||
print_info "Pushing latest tag: ${BACKEND_IMAGE_LATEST}"
|
||||
docker push "${BACKEND_IMAGE_LATEST}"
|
||||
print_success "Latest backend image pushed: ${BACKEND_IMAGE_LATEST}"
|
||||
}
|
||||
|
||||
push_frontend() {
|
||||
print_header "Pushing Frontend Image"
|
||||
|
||||
print_info "Pushing: ${FRONTEND_IMAGE}"
|
||||
docker push "${FRONTEND_IMAGE}"
|
||||
print_success "Frontend image pushed: ${FRONTEND_IMAGE}"
|
||||
|
||||
print_info "Pushing latest tag: ${FRONTEND_IMAGE_LATEST}"
|
||||
docker push "${FRONTEND_IMAGE_LATEST}"
|
||||
print_success "Latest frontend image pushed: ${FRONTEND_IMAGE_LATEST}"
|
||||
}
|
||||
|
||||
##############################################################################
|
||||
# Verification Functions
|
||||
##############################################################################
|
||||
|
||||
verify_images() {
|
||||
print_header "Verifying Built Images"
|
||||
|
||||
print_info "Checking backend image..."
|
||||
if docker image inspect "${BACKEND_IMAGE}" > /dev/null 2>&1; then
|
||||
SIZE=$(docker image inspect "${BACKEND_IMAGE}" --format='{{.Size}}' | numfmt --to=iec 2>/dev/null || docker image inspect "${BACKEND_IMAGE}" --format='{{.Size}}')
|
||||
print_success "Backend image verified (Size: ${SIZE})"
|
||||
else
|
||||
print_error "Backend image verification failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
print_info "Checking frontend image..."
|
||||
if docker image inspect "${FRONTEND_IMAGE}" > /dev/null 2>&1; then
|
||||
SIZE=$(docker image inspect "${FRONTEND_IMAGE}" --format='{{.Size}}' | numfmt --to=iec 2>/dev/null || docker image inspect "${FRONTEND_IMAGE}" --format='{{.Size}}')
|
||||
print_success "Frontend image verified (Size: ${SIZE})"
|
||||
else
|
||||
print_error "Frontend image verification failed"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
##############################################################################
|
||||
# Generate Configuration
|
||||
##############################################################################
|
||||
|
||||
generate_deployment_config() {
|
||||
print_header "Generating Deployment Configuration"
|
||||
|
||||
local config_file="docker-compose.prod.deployed.yml"
|
||||
|
||||
print_info "Creating deployment configuration: ${config_file}"
|
||||
|
||||
cat > "${config_file}" << EOF
|
||||
# Generated production deployment configuration
|
||||
# Version: ${VERSION}
|
||||
# Generated: $(date -u +'%Y-%m-%dT%H:%M:%SZ')
|
||||
# GitHub User: ${GITHUB_USER}
|
||||
|
||||
services:
|
||||
backend:
|
||||
image: ${BACKEND_IMAGE}
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- DATABASE_PATH=/app/database/data/edh-stats.db
|
||||
- JWT_SECRET_FILE=/run/secrets/jwt_secret
|
||||
- CORS_ORIGIN=\${CORS_ORIGIN:-https://yourdomain.com}
|
||||
- LOG_LEVEL=warn
|
||||
- RATE_LIMIT_WINDOW=15
|
||||
- RATE_LIMIT_MAX=100
|
||||
- ALLOW_REGISTRATION=\${ALLOW_REGISTRATION:-false}
|
||||
volumes:
|
||||
- sqlite_data:/app/database/data
|
||||
- app_logs:/app/logs
|
||||
secrets:
|
||||
- jwt_secret
|
||||
restart: unless-stopped
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 512M
|
||||
cpus: '0.5'
|
||||
reservations:
|
||||
memory: 256M
|
||||
cpus: '0.25'
|
||||
healthcheck:
|
||||
test: ['CMD', 'wget', '--no-verbose', '--tries=1', '--spider', 'http://localhost:3000/api/health']
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
networks:
|
||||
- edh-stats-network
|
||||
stop_grace_period: 30s
|
||||
|
||||
frontend:
|
||||
image: ${FRONTEND_IMAGE}
|
||||
ports:
|
||||
- '80:80'
|
||||
- '443:443'
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- edh-stats-network
|
||||
|
||||
volumes:
|
||||
sqlite_data:
|
||||
driver: local
|
||||
app_logs:
|
||||
driver: local
|
||||
|
||||
secrets:
|
||||
jwt_secret:
|
||||
external: true
|
||||
|
||||
networks:
|
||||
edh-stats-network:
|
||||
driver: bridge
|
||||
EOF
|
||||
|
||||
print_success "Deployment configuration generated: ${config_file}"
|
||||
}
|
||||
|
||||
##############################################################################
|
||||
# Cleanup
|
||||
##############################################################################
|
||||
|
||||
cleanup_temp_files() {
|
||||
print_header "Cleaning Up Temporary Files"
|
||||
|
||||
if [ -f "./frontend/Dockerfile.prod" ] && [ ! -f "./frontend/Dockerfile" ]; then
|
||||
print_info "Removing temporary frontend Dockerfile..."
|
||||
rm -f "./frontend/Dockerfile.prod"
|
||||
print_success "Temporary files cleaned up"
|
||||
fi
|
||||
}
|
||||
|
||||
##############################################################################
|
||||
# Summary
|
||||
##############################################################################
|
||||
|
||||
print_summary() {
|
||||
print_header "Deployment Summary"
|
||||
|
||||
echo "Backend Image: ${BACKEND_IMAGE}"
|
||||
echo "Frontend Image: ${FRONTEND_IMAGE}"
|
||||
echo ""
|
||||
echo "Latest Tags:"
|
||||
echo " Backend: ${BACKEND_IMAGE_LATEST}"
|
||||
echo " Frontend: ${FRONTEND_IMAGE_LATEST}"
|
||||
echo ""
|
||||
echo "Registry: ${REGISTRY}"
|
||||
echo "GitHub User: ${GITHUB_USER}"
|
||||
echo "Version: ${VERSION}"
|
||||
echo ""
|
||||
echo "Next Steps:"
|
||||
echo " 1. Pull images: docker pull ${BACKEND_IMAGE}"
|
||||
echo " 2. Configure production secrets (JWT_SECRET)"
|
||||
echo " 3. Set environment variables (CORS_ORIGIN, ALLOW_REGISTRATION)"
|
||||
echo " 4. Deploy: docker-compose -f docker-compose.prod.deployed.yml up -d"
|
||||
echo ""
|
||||
}
|
||||
|
||||
##############################################################################
|
||||
# Main Execution
|
||||
##############################################################################
|
||||
|
||||
main() {
|
||||
print_header "EDH Stats Tracker - Production Deployment"
|
||||
|
||||
print_info "Starting deployment process..."
|
||||
print_info "Version: ${VERSION}"
|
||||
print_info "GitHub User: ${GITHUB_USER}"
|
||||
print_info "Registry: ${REGISTRY}"
|
||||
echo ""
|
||||
|
||||
# Validation
|
||||
validate_prerequisites
|
||||
|
||||
# Check token
|
||||
check_github_token
|
||||
|
||||
# Build images
|
||||
build_backend
|
||||
build_frontend
|
||||
|
||||
# Verify images
|
||||
verify_images
|
||||
|
||||
# Authenticate
|
||||
login_to_registry
|
||||
|
||||
# Push images
|
||||
push_backend
|
||||
push_frontend
|
||||
|
||||
# Generate config
|
||||
generate_deployment_config
|
||||
|
||||
# Cleanup
|
||||
cleanup_temp_files
|
||||
|
||||
# Summary
|
||||
print_summary
|
||||
|
||||
print_success "Deployment completed successfully!"
|
||||
}
|
||||
|
||||
# Run main function
|
||||
main
|
||||
@@ -1,82 +0,0 @@
|
||||
services:
|
||||
backend:
|
||||
build:
|
||||
context: ./backend
|
||||
dockerfile: Dockerfile
|
||||
target: production
|
||||
environment:
|
||||
- NODE_ENV=production
|
||||
- DATABASE_PATH=/app/database/data/edh-stats.db
|
||||
- JWT_SECRET_FILE=/run/secrets/jwt_secret
|
||||
- CORS_ORIGIN=${CORS_ORIGIN:-https://yourdomain.com}
|
||||
- LOG_LEVEL=warn
|
||||
- RATE_LIMIT_WINDOW=15
|
||||
- RATE_LIMIT_MAX=100
|
||||
- ALLOW_REGISTRATION=${ALLOW_REGISTRATION:-false}
|
||||
volumes:
|
||||
- sqlite_data:/app/database/data
|
||||
- app_logs:/app/logs
|
||||
secrets:
|
||||
- jwt_secret
|
||||
restart: unless-stopped
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 512M
|
||||
cpus: '0.5'
|
||||
reservations:
|
||||
memory: 256M
|
||||
cpus: '0.25'
|
||||
healthcheck:
|
||||
test:
|
||||
[
|
||||
'CMD',
|
||||
'wget',
|
||||
'--no-verbose',
|
||||
'--tries=1',
|
||||
'--spider',
|
||||
'http://localhost:3000/api/health'
|
||||
]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 40s
|
||||
networks:
|
||||
- edh-stats-network
|
||||
stop_grace_period: 30s
|
||||
|
||||
frontend:
|
||||
image: nginx:alpine
|
||||
volumes:
|
||||
- ./frontend/nginx.prod.conf:/etc/nginx/nginx.conf:ro
|
||||
- ./frontend/public:/usr/share/nginx/html:ro
|
||||
ports:
|
||||
- '80:80'
|
||||
- '443:443'
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- edh-stats-network
|
||||
|
||||
# Optional: Add reverse proxy for SSL termination
|
||||
# nginx-proxy:
|
||||
# image: nginx:alpine
|
||||
# ports:
|
||||
# - "443:443"
|
||||
# volumes:
|
||||
# - ./ssl:/etc/nginx/ssl:ro
|
||||
# depends_on:
|
||||
# - frontend
|
||||
|
||||
volumes:
|
||||
sqlite_data:
|
||||
driver: local
|
||||
app_logs:
|
||||
driver: local
|
||||
|
||||
secrets:
|
||||
jwt_secret:
|
||||
external: true
|
||||
|
||||
networks:
|
||||
edh-stats-network:
|
||||
driver: bridge
|
||||
Reference in New Issue
Block a user