feat: Add complete production deployment infrastructure
- Docker configuration: - Multi-stage Dockerfiles for backend (Python 3.11) and frontend (Node 20) - Production docker-compose.yml with all services - Development docker-compose.dev.yml with hot-reload - Nginx reverse proxy: - SSL/TLS termination with modern cipher suites - Rate limiting and security headers - Caching and compression - Load balancing ready - Kubernetes manifests: - Deployment, Service, Ingress configurations - ConfigMap and Secrets - HPA for auto-scaling - PersistentVolumeClaims - Deployment scripts: - deploy.sh: Automated deployment with health checks - backup.sh: Automated backup with retention - health-check.sh: Service health monitoring - setup-ssl.sh: Let's Encrypt SSL automation - Monitoring: - Prometheus configuration - Grafana dashboards (optional) - Structured logging - Documentation: - DEPLOYMENT_GUIDE.md: Complete deployment instructions - Environment templates (.env.production) Ready for commercial deployment!
This commit is contained in:
67
scripts/backup.sh
Normal file
67
scripts/backup.sh
Normal file
@@ -0,0 +1,67 @@
|
||||
#!/bin/bash
|
||||
# ============================================
|
||||
# Document Translation API - Backup Script
|
||||
# ============================================
|
||||
# Usage: ./scripts/backup.sh [backup_dir]
|
||||
|
||||
set -e
|
||||
|
||||
# Configuration
|
||||
BACKUP_DIR="${1:-./backups}"
|
||||
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
|
||||
BACKUP_NAME="translate_backup_$TIMESTAMP"
|
||||
|
||||
# Colors
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
echo -e "${YELLOW}Starting backup: $BACKUP_NAME${NC}"
|
||||
|
||||
# Create backup directory
|
||||
mkdir -p "$BACKUP_DIR/$BACKUP_NAME"
|
||||
|
||||
# Backup uploaded files
|
||||
if [ -d "./uploads" ]; then
|
||||
echo "Backing up uploads..."
|
||||
cp -r ./uploads "$BACKUP_DIR/$BACKUP_NAME/"
|
||||
fi
|
||||
|
||||
# Backup output files
|
||||
if [ -d "./outputs" ]; then
|
||||
echo "Backing up outputs..."
|
||||
cp -r ./outputs "$BACKUP_DIR/$BACKUP_NAME/"
|
||||
fi
|
||||
|
||||
# Backup configuration
|
||||
echo "Backing up configuration..."
|
||||
cp .env* "$BACKUP_DIR/$BACKUP_NAME/" 2>/dev/null || true
|
||||
cp docker-compose*.yml "$BACKUP_DIR/$BACKUP_NAME/" 2>/dev/null || true
|
||||
|
||||
# Backup Docker volumes (if using Docker)
|
||||
if command -v docker &> /dev/null; then
|
||||
echo "Backing up Docker volumes..."
|
||||
|
||||
# Get volume names
|
||||
VOLUMES=$(docker volume ls --format "{{.Name}}" | grep translate || true)
|
||||
|
||||
for vol in $VOLUMES; do
|
||||
echo " Backing up volume: $vol"
|
||||
docker run --rm \
|
||||
-v "$vol:/data:ro" \
|
||||
-v "$(pwd)/$BACKUP_DIR/$BACKUP_NAME:/backup" \
|
||||
alpine tar czf "/backup/${vol}.tar.gz" -C /data . 2>/dev/null || true
|
||||
done
|
||||
fi
|
||||
|
||||
# Compress backup
|
||||
echo "Compressing backup..."
|
||||
cd "$BACKUP_DIR"
|
||||
tar czf "${BACKUP_NAME}.tar.gz" "$BACKUP_NAME"
|
||||
rm -rf "$BACKUP_NAME"
|
||||
|
||||
# Cleanup old backups (keep last 7)
|
||||
echo "Cleaning old backups..."
|
||||
ls -t translate_backup_*.tar.gz 2>/dev/null | tail -n +8 | xargs rm -f 2>/dev/null || true
|
||||
|
||||
echo -e "${GREEN}Backup complete: $BACKUP_DIR/${BACKUP_NAME}.tar.gz${NC}"
|
||||
168
scripts/deploy.sh
Normal file
168
scripts/deploy.sh
Normal file
@@ -0,0 +1,168 @@
|
||||
#!/bin/bash
|
||||
# ============================================
|
||||
# Document Translation API - Deployment Script
|
||||
# ============================================
|
||||
# Usage: ./scripts/deploy.sh [environment] [options]
|
||||
# Example: ./scripts/deploy.sh production --with-ollama
|
||||
|
||||
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
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
||||
ENVIRONMENT="${1:-production}"
|
||||
COMPOSE_FILE="docker-compose.yml"
|
||||
|
||||
# Parse options
|
||||
PROFILES=""
|
||||
while [[ $# -gt 1 ]]; do
|
||||
case $2 in
|
||||
--with-ollama)
|
||||
PROFILES="$PROFILES --profile with-ollama"
|
||||
shift
|
||||
;;
|
||||
--with-cache)
|
||||
PROFILES="$PROFILES --profile with-cache"
|
||||
shift
|
||||
;;
|
||||
--with-monitoring)
|
||||
PROFILES="$PROFILES --profile with-monitoring"
|
||||
shift
|
||||
;;
|
||||
--full)
|
||||
PROFILES="--profile with-ollama --profile with-cache --profile with-monitoring"
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo -e "${BLUE} Document Translation API Deployment${NC}"
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo ""
|
||||
|
||||
# Check prerequisites
|
||||
echo -e "${YELLOW}Checking prerequisites...${NC}"
|
||||
|
||||
if ! command -v docker &> /dev/null; then
|
||||
echo -e "${RED}Error: Docker is not installed${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! command -v docker-compose &> /dev/null && ! docker compose version &> /dev/null; then
|
||||
echo -e "${RED}Error: Docker Compose is not installed${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✓ Docker and Docker Compose are installed${NC}"
|
||||
|
||||
# Check environment file
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
if [ "$ENVIRONMENT" == "production" ]; then
|
||||
ENV_FILE=".env.production"
|
||||
else
|
||||
ENV_FILE=".env"
|
||||
fi
|
||||
|
||||
if [ ! -f "$ENV_FILE" ]; then
|
||||
echo -e "${YELLOW}Creating $ENV_FILE from template...${NC}"
|
||||
if [ -f ".env.example" ]; then
|
||||
cp .env.example "$ENV_FILE"
|
||||
echo -e "${YELLOW}Please edit $ENV_FILE with your configuration${NC}"
|
||||
else
|
||||
echo -e "${RED}Error: No environment file found${NC}"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✓ Environment file: $ENV_FILE${NC}"
|
||||
|
||||
# Load environment
|
||||
set -a
|
||||
source "$ENV_FILE"
|
||||
set +a
|
||||
|
||||
# Create SSL directory if needed
|
||||
if [ ! -d "docker/nginx/ssl" ]; then
|
||||
mkdir -p docker/nginx/ssl
|
||||
echo -e "${YELLOW}Created SSL directory. Add your certificates:${NC}"
|
||||
echo " - docker/nginx/ssl/fullchain.pem"
|
||||
echo " - docker/nginx/ssl/privkey.pem"
|
||||
echo " - docker/nginx/ssl/chain.pem"
|
||||
|
||||
# Generate self-signed cert for testing
|
||||
if [ "$ENVIRONMENT" != "production" ]; then
|
||||
echo -e "${YELLOW}Generating self-signed certificate for development...${NC}"
|
||||
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
|
||||
-keyout docker/nginx/ssl/privkey.pem \
|
||||
-out docker/nginx/ssl/fullchain.pem \
|
||||
-subj "/C=US/ST=State/L=City/O=Organization/CN=localhost" 2>/dev/null
|
||||
cp docker/nginx/ssl/fullchain.pem docker/nginx/ssl/chain.pem
|
||||
fi
|
||||
fi
|
||||
|
||||
# Build and deploy
|
||||
echo ""
|
||||
echo -e "${YELLOW}Building containers...${NC}"
|
||||
docker compose --env-file "$ENV_FILE" build
|
||||
|
||||
echo ""
|
||||
echo -e "${YELLOW}Starting services...${NC}"
|
||||
docker compose --env-file "$ENV_FILE" $PROFILES up -d
|
||||
|
||||
# Wait for services to be healthy
|
||||
echo ""
|
||||
echo -e "${YELLOW}Waiting for services to be ready...${NC}"
|
||||
sleep 10
|
||||
|
||||
# Health check
|
||||
echo ""
|
||||
echo -e "${YELLOW}Running health checks...${NC}"
|
||||
|
||||
BACKEND_HEALTH=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8000/health 2>/dev/null || echo "000")
|
||||
if [ "$BACKEND_HEALTH" == "200" ]; then
|
||||
echo -e "${GREEN}✓ Backend is healthy${NC}"
|
||||
else
|
||||
echo -e "${RED}✗ Backend health check failed (HTTP $BACKEND_HEALTH)${NC}"
|
||||
fi
|
||||
|
||||
FRONTEND_HEALTH=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:3000 2>/dev/null || echo "000")
|
||||
if [ "$FRONTEND_HEALTH" == "200" ]; then
|
||||
echo -e "${GREEN}✓ Frontend is healthy${NC}"
|
||||
else
|
||||
echo -e "${RED}✗ Frontend health check failed (HTTP $FRONTEND_HEALTH)${NC}"
|
||||
fi
|
||||
|
||||
# Show status
|
||||
echo ""
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo -e "${BLUE} Deployment Complete!${NC}"
|
||||
echo -e "${BLUE}========================================${NC}"
|
||||
echo ""
|
||||
echo -e "Services running:"
|
||||
docker compose ps --format "table {{.Name}}\t{{.Status}}\t{{.Ports}}"
|
||||
|
||||
echo ""
|
||||
echo -e "${GREEN}Access your application:${NC}"
|
||||
echo -e " Frontend: http://localhost (or https://localhost)"
|
||||
echo -e " API: http://localhost/api"
|
||||
echo -e " Admin: http://localhost/admin"
|
||||
echo -e " Health: http://localhost/health"
|
||||
|
||||
echo ""
|
||||
echo -e "${YELLOW}Useful commands:${NC}"
|
||||
echo " View logs: docker compose logs -f"
|
||||
echo " Stop: docker compose down"
|
||||
echo " Restart: docker compose restart"
|
||||
echo " Update: ./scripts/deploy.sh $ENVIRONMENT"
|
||||
87
scripts/health-check.sh
Normal file
87
scripts/health-check.sh
Normal file
@@ -0,0 +1,87 @@
|
||||
#!/bin/bash
|
||||
# ============================================
|
||||
# Document Translation API - Health Check Script
|
||||
# ============================================
|
||||
# Usage: ./scripts/health-check.sh [--verbose]
|
||||
|
||||
set -e
|
||||
|
||||
VERBOSE=false
|
||||
if [ "$1" == "--verbose" ]; then
|
||||
VERBOSE=true
|
||||
fi
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Configuration
|
||||
BACKEND_URL="${BACKEND_URL:-http://localhost:8000}"
|
||||
FRONTEND_URL="${FRONTEND_URL:-http://localhost:3000}"
|
||||
NGINX_URL="${NGINX_URL:-http://localhost}"
|
||||
|
||||
EXIT_CODE=0
|
||||
|
||||
check_service() {
|
||||
local name=$1
|
||||
local url=$2
|
||||
local expected=${3:-200}
|
||||
|
||||
response=$(curl -s -o /dev/null -w "%{http_code}" "$url" 2>/dev/null || echo "000")
|
||||
|
||||
if [ "$response" == "$expected" ]; then
|
||||
echo -e "${GREEN}✓ $name: OK (HTTP $response)${NC}"
|
||||
else
|
||||
echo -e "${RED}✗ $name: FAILED (HTTP $response, expected $expected)${NC}"
|
||||
EXIT_CODE=1
|
||||
fi
|
||||
|
||||
if [ "$VERBOSE" == "true" ] && [ "$response" == "200" ]; then
|
||||
echo " Response:"
|
||||
curl -s "$url" 2>/dev/null | head -c 500
|
||||
echo ""
|
||||
fi
|
||||
}
|
||||
|
||||
echo "========================================="
|
||||
echo " Health Check - Document Translation API"
|
||||
echo "========================================="
|
||||
echo ""
|
||||
|
||||
# Check backend
|
||||
echo "Backend Services:"
|
||||
check_service "API Health" "$BACKEND_URL/health"
|
||||
check_service "API Root" "$BACKEND_URL/"
|
||||
|
||||
echo ""
|
||||
|
||||
# Check frontend
|
||||
echo "Frontend Services:"
|
||||
check_service "Frontend" "$FRONTEND_URL"
|
||||
|
||||
echo ""
|
||||
|
||||
# Check nginx (if running)
|
||||
echo "Proxy Services:"
|
||||
check_service "Nginx" "$NGINX_URL/health"
|
||||
|
||||
echo ""
|
||||
|
||||
# Docker health
|
||||
if command -v docker &> /dev/null; then
|
||||
echo "Docker Container Status:"
|
||||
docker compose ps --format "table {{.Name}}\t{{.Status}}" 2>/dev/null || echo " Docker Compose not running"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "========================================="
|
||||
|
||||
if [ $EXIT_CODE -eq 0 ]; then
|
||||
echo -e "${GREEN}All health checks passed!${NC}"
|
||||
else
|
||||
echo -e "${RED}Some health checks failed!${NC}"
|
||||
fi
|
||||
|
||||
exit $EXIT_CODE
|
||||
79
scripts/setup-ssl.sh
Normal file
79
scripts/setup-ssl.sh
Normal file
@@ -0,0 +1,79 @@
|
||||
#!/bin/bash
|
||||
# ============================================
|
||||
# Document Translation API - SSL Setup Script
|
||||
# ============================================
|
||||
# Usage: ./scripts/setup-ssl.sh <domain> <email>
|
||||
# Example: ./scripts/setup-ssl.sh translate.example.com admin@example.com
|
||||
|
||||
set -e
|
||||
|
||||
DOMAIN="${1:-}"
|
||||
EMAIL="${2:-}"
|
||||
|
||||
if [ -z "$DOMAIN" ] || [ -z "$EMAIL" ]; then
|
||||
echo "Usage: ./scripts/setup-ssl.sh <domain> <email>"
|
||||
echo "Example: ./scripts/setup-ssl.sh translate.example.com admin@example.com"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Colors
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
echo -e "${YELLOW}Setting up SSL for $DOMAIN${NC}"
|
||||
|
||||
# Create directory for certbot
|
||||
mkdir -p ./docker/certbot/www
|
||||
mkdir -p ./docker/certbot/conf
|
||||
|
||||
# Create initial nginx config for ACME challenge
|
||||
cat > ./docker/nginx/conf.d/certbot.conf << EOF
|
||||
server {
|
||||
listen 80;
|
||||
server_name $DOMAIN;
|
||||
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/certbot;
|
||||
}
|
||||
|
||||
location / {
|
||||
return 301 https://\$host\$request_uri;
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
# Start nginx with HTTP only
|
||||
echo "Starting nginx for certificate request..."
|
||||
docker compose up -d nginx
|
||||
|
||||
# Request certificate
|
||||
echo "Requesting Let's Encrypt certificate..."
|
||||
docker run --rm \
|
||||
-v "$(pwd)/docker/certbot/www:/var/www/certbot" \
|
||||
-v "$(pwd)/docker/certbot/conf:/etc/letsencrypt" \
|
||||
certbot/certbot certonly \
|
||||
--webroot \
|
||||
--webroot-path=/var/www/certbot \
|
||||
--email "$EMAIL" \
|
||||
--agree-tos \
|
||||
--no-eff-email \
|
||||
-d "$DOMAIN"
|
||||
|
||||
# Copy certificates
|
||||
echo "Installing certificates..."
|
||||
cp ./docker/certbot/conf/live/$DOMAIN/fullchain.pem ./docker/nginx/ssl/
|
||||
cp ./docker/certbot/conf/live/$DOMAIN/privkey.pem ./docker/nginx/ssl/
|
||||
cp ./docker/certbot/conf/live/$DOMAIN/chain.pem ./docker/nginx/ssl/
|
||||
|
||||
# Remove temporary config
|
||||
rm ./docker/nginx/conf.d/certbot.conf
|
||||
|
||||
# Restart nginx with SSL
|
||||
echo "Restarting nginx with SSL..."
|
||||
docker compose restart nginx
|
||||
|
||||
echo -e "${GREEN}SSL setup complete for $DOMAIN${NC}"
|
||||
echo ""
|
||||
echo "To auto-renew certificates, add this to crontab:"
|
||||
echo "0 0 1 * * cd $(pwd) && ./scripts/renew-ssl.sh"
|
||||
Reference in New Issue
Block a user