401 lines
13 KiB
Bash
Executable file
401 lines
13 KiB
Bash
Executable file
#!/bin/bash
|
||
set -e # Exit on any error
|
||
|
||
# Colors for output
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
BLUE='\033[0;34m'
|
||
NC='\033[0m' # No Color
|
||
|
||
# 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_warning() {
|
||
echo -e "${YELLOW}⚠️ $1${NC}"
|
||
}
|
||
|
||
print_error() {
|
||
echo -e "${RED}❌ $1${NC}"
|
||
}
|
||
|
||
print_info() {
|
||
echo -e "${BLUE}ℹ️ $1${NC}"
|
||
}
|
||
|
||
# Main deployment script
|
||
print_header "BTG Rackham Video Sales Coach - Deployment Script v2.0"
|
||
|
||
# 1. Check prerequisites
|
||
print_header "Step 1: Checking Prerequisites"
|
||
|
||
if ! command -v docker &> /dev/null; then
|
||
print_error "Docker is not installed. Please install Docker first."
|
||
echo "Visit: https://docs.docker.com/get-docker/"
|
||
exit 1
|
||
fi
|
||
print_success "Docker is installed: $(docker --version)"
|
||
|
||
# Check for docker compose (modern) or docker-compose (legacy)
|
||
if docker compose version &> /dev/null; then
|
||
DOCKER_COMPOSE="docker compose"
|
||
print_success "Docker Compose is installed: $(docker compose version)"
|
||
elif command -v docker-compose &> /dev/null; then
|
||
DOCKER_COMPOSE="docker-compose"
|
||
print_success "Docker Compose is installed: $(docker-compose --version)"
|
||
else
|
||
print_error "Docker Compose is not installed. Please install Docker Compose first."
|
||
echo "Visit: https://docs.docker.com/compose/install/"
|
||
exit 1
|
||
fi
|
||
|
||
# 2. Get deployment directory
|
||
print_header "Step 2: Deployment Location"
|
||
|
||
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
|
||
print_info "Application directory: $SCRIPT_DIR"
|
||
|
||
# 3. Check if .env exists
|
||
print_header "Step 3: Environment Configuration"
|
||
|
||
ENV_FILE="$SCRIPT_DIR/infra/.env"
|
||
|
||
if [ -f "$ENV_FILE" ]; then
|
||
print_warning "Environment file already exists at: $ENV_FILE"
|
||
read -p "Do you want to reconfigure it? (y/N): " RECONFIGURE
|
||
if [[ ! $RECONFIGURE =~ ^[Yy]$ ]]; then
|
||
print_info "Keeping existing .env file"
|
||
SKIP_ENV_SETUP=true
|
||
fi
|
||
fi
|
||
|
||
if [ "$SKIP_ENV_SETUP" != true ]; then
|
||
# Copy example if it doesn't exist
|
||
if [ ! -f "$ENV_FILE" ]; then
|
||
if [ -f "$SCRIPT_DIR/infra/.env.example" ]; then
|
||
cp "$SCRIPT_DIR/infra/.env.example" "$ENV_FILE"
|
||
print_success "Created .env file from template"
|
||
else
|
||
print_error ".env.example not found. Creating basic .env file..."
|
||
cat > "$ENV_FILE" << 'EOF'
|
||
# MongoDB connection (internal docker network - don't change)
|
||
MONGO_URL=mongodb://mongo:27017/btg
|
||
GEMINI_API_KEY=
|
||
GEMINI_MODEL=gemini-2.5-pro
|
||
JWT_SECRET=
|
||
API_BASE_URL=http://localhost:8080/api
|
||
FRONTEND_BASE_PATH=/
|
||
CORS_ORIGINS=http://localhost:3009,http://localhost:8080
|
||
MAX_UPLOAD_SIZE_GB=2
|
||
CHUNK_SIZE_MB=10
|
||
DATA_RETENTION_DAYS=90
|
||
# Note: MongoDB is externally accessible on host port 27021
|
||
EOF
|
||
print_success "Created basic .env file"
|
||
fi
|
||
fi
|
||
|
||
# Prompt for required values
|
||
echo ""
|
||
print_info "Please provide the following required values:"
|
||
echo ""
|
||
|
||
# Get Gemini API Key
|
||
read -p "Enter your Gemini API Key: " GEMINI_KEY
|
||
if [ -z "$GEMINI_KEY" ]; then
|
||
print_error "Gemini API Key is required!"
|
||
echo "Get one at: https://aistudio.google.com/app/apikey"
|
||
exit 1
|
||
fi
|
||
|
||
# Generate JWT Secret
|
||
print_info "Generating secure JWT secret..."
|
||
JWT_SECRET=$(openssl rand -base64 32)
|
||
print_success "Generated JWT secret"
|
||
|
||
# Get server hostname for CORS
|
||
HOSTNAME=$(hostname -f 2>/dev/null || hostname)
|
||
read -p "Enter your server domain/hostname (default: $HOSTNAME): " DOMAIN
|
||
DOMAIN=${DOMAIN:-$HOSTNAME}
|
||
|
||
# Get API Base URL for reverse proxy
|
||
echo ""
|
||
print_info "Backend API Configuration:"
|
||
echo "For local development, use: http://localhost:8080/api"
|
||
echo "For production with reverse proxy, use your proxied backend URL"
|
||
echo "Example: https://ai-sandbox.oliver.solutions/rackham-back"
|
||
echo ""
|
||
read -p "Enter API Base URL (default: http://localhost:8080/api): " API_URL
|
||
API_URL=${API_URL:-http://localhost:8080/api}
|
||
print_success "Backend API URL set to: $API_URL"
|
||
|
||
# Get Frontend Base Path for reverse proxy
|
||
echo ""
|
||
print_info "Frontend Base Path Configuration:"
|
||
echo "For local development or root path, use: /"
|
||
echo "For production at subpath, use the path (e.g., /rackham)"
|
||
echo "Example: If frontend is at https://ai-sandbox.oliver.solutions/rackham, use: /rackham"
|
||
echo ""
|
||
read -p "Enter Frontend Base Path (default: /): " FRONTEND_PATH
|
||
FRONTEND_PATH=${FRONTEND_PATH:-/}
|
||
print_success "Frontend Base Path set to: $FRONTEND_PATH"
|
||
|
||
# Build CORS origins
|
||
CORS_ORIGINS="http://localhost:3009,http://localhost:8080,http://$DOMAIN,https://$DOMAIN"
|
||
if [[ "$API_URL" == https://* ]]; then
|
||
# Extract domain from API URL for CORS
|
||
API_DOMAIN=$(echo "$API_URL" | sed -E 's|https?://([^/]+).*|\1|')
|
||
CORS_ORIGINS="$CORS_ORIGINS,https://$API_DOMAIN"
|
||
fi
|
||
|
||
# Update .env file
|
||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||
# macOS
|
||
sed -i '' "s|GEMINI_API_KEY=.*|GEMINI_API_KEY=$GEMINI_KEY|" "$ENV_FILE"
|
||
sed -i '' "s|JWT_SECRET=.*|JWT_SECRET=$JWT_SECRET|" "$ENV_FILE"
|
||
sed -i '' "s|API_BASE_URL=.*|API_BASE_URL=$API_URL|" "$ENV_FILE"
|
||
sed -i '' "s|FRONTEND_BASE_PATH=.*|FRONTEND_BASE_PATH=$FRONTEND_PATH|" "$ENV_FILE"
|
||
sed -i '' "s|CORS_ORIGINS=.*|CORS_ORIGINS=$CORS_ORIGINS|" "$ENV_FILE"
|
||
else
|
||
# Linux
|
||
sed -i "s|GEMINI_API_KEY=.*|GEMINI_API_KEY=$GEMINI_KEY|" "$ENV_FILE"
|
||
sed -i "s|JWT_SECRET=.*|JWT_SECRET=$JWT_SECRET|" "$ENV_FILE"
|
||
sed -i "s|API_BASE_URL=.*|API_BASE_URL=$API_URL|" "$ENV_FILE"
|
||
sed -i "s|FRONTEND_BASE_PATH=.*|FRONTEND_BASE_PATH=$FRONTEND_PATH|" "$ENV_FILE"
|
||
sed -i "s|CORS_ORIGINS=.*|CORS_ORIGINS=$CORS_ORIGINS|" "$ENV_FILE"
|
||
fi
|
||
|
||
print_success "Environment file configured"
|
||
echo ""
|
||
print_info "Configuration saved to: $ENV_FILE"
|
||
fi
|
||
|
||
# 4. Create data directories
|
||
print_header "Step 4: Creating Data Directories"
|
||
|
||
sudo mkdir -p /data/videos
|
||
sudo mkdir -p /tmp/chunks
|
||
sudo chown -R $USER:$USER /data/videos /tmp/chunks
|
||
print_success "Data directories created and permissions set"
|
||
|
||
# 5. Stop any existing containers
|
||
print_header "Step 5: Stopping Existing Containers"
|
||
|
||
cd "$SCRIPT_DIR/infra"
|
||
if $DOCKER_COMPOSE ps | grep -q "Up"; then
|
||
print_info "Stopping existing containers..."
|
||
$DOCKER_COMPOSE down
|
||
print_success "Existing containers stopped"
|
||
else
|
||
print_info "No existing containers running"
|
||
fi
|
||
|
||
# 6. Build containers
|
||
print_header "Step 6: Building Docker Containers"
|
||
|
||
print_info "This may take 5-10 minutes on first build..."
|
||
$DOCKER_COMPOSE build
|
||
|
||
if [ $? -eq 0 ]; then
|
||
print_success "Docker containers built successfully"
|
||
else
|
||
print_error "Docker build failed. Check the output above for errors."
|
||
exit 1
|
||
fi
|
||
|
||
# 7. Build Frontend Static Files
|
||
print_header "Step 7: Building Frontend for Production"
|
||
|
||
# Load values from .env file - ALWAYS load if we skipped env setup
|
||
if [ "$SKIP_ENV_SETUP" = true ] || [ -z "$API_URL" ] || [ -z "$FRONTEND_PATH" ]; then
|
||
if [ -f "$ENV_FILE" ]; then
|
||
print_info "Loading build configuration from .env..."
|
||
source "$ENV_FILE"
|
||
API_URL=${API_BASE_URL:-http://localhost:8080/api}
|
||
FRONTEND_PATH=${FRONTEND_BASE_PATH:-/rackham}
|
||
print_info " API Base: $API_URL"
|
||
print_info " Frontend Path: $FRONTEND_PATH"
|
||
fi
|
||
fi
|
||
|
||
cd "$SCRIPT_DIR/frontend"
|
||
print_info "Installing frontend dependencies..."
|
||
npm ci
|
||
|
||
print_info "Building frontend with production settings..."
|
||
echo " VITE_BASE_PATH=$FRONTEND_PATH"
|
||
echo " VITE_API_BASE=$API_URL"
|
||
echo ""
|
||
|
||
# Build with environment variables
|
||
VITE_BASE_PATH=$FRONTEND_PATH VITE_API_BASE=$API_URL npm run build
|
||
|
||
if [ $? -eq 0 ]; then
|
||
print_success "Frontend built successfully"
|
||
else
|
||
print_error "Frontend build failed. Check the output above for errors."
|
||
exit 1
|
||
fi
|
||
|
||
# 8. Deploy Frontend to Apache Web Root
|
||
print_header "Step 8: Deploying Frontend to Apache Web Root"
|
||
|
||
APACHE_WEB_ROOT="/var/www/html/rackham"
|
||
print_info "Deploying to: $APACHE_WEB_ROOT"
|
||
|
||
# Create directory and copy files
|
||
sudo mkdir -p "$APACHE_WEB_ROOT"
|
||
sudo rm -rf "$APACHE_WEB_ROOT"/*
|
||
sudo cp -r dist/* "$APACHE_WEB_ROOT/"
|
||
sudo chown -R www-data:www-data "$APACHE_WEB_ROOT"
|
||
sudo chmod -R 755 "$APACHE_WEB_ROOT"
|
||
|
||
print_success "Frontend deployed to $APACHE_WEB_ROOT"
|
||
|
||
# 9. Start Backend and Database Containers
|
||
print_header "Step 9: Starting Backend Services"
|
||
|
||
cd "$SCRIPT_DIR/infra"
|
||
$DOCKER_COMPOSE up -d backend mongo
|
||
|
||
if [ $? -eq 0 ]; then
|
||
print_success "Backend services started successfully"
|
||
else
|
||
print_error "Failed to start services. Check logs with: $DOCKER_COMPOSE logs"
|
||
exit 1
|
||
fi
|
||
|
||
# 10. Wait for services to be ready
|
||
print_header "Step 10: Waiting for Services to Initialize"
|
||
|
||
print_info "Waiting 15 seconds for services to start..."
|
||
sleep 15
|
||
|
||
# 9. Verify deployment
|
||
print_header "Step 9: Verifying Deployment"
|
||
|
||
# Check container status
|
||
echo ""
|
||
print_info "Container Status:"
|
||
$DOCKER_COMPOSE ps
|
||
|
||
echo ""
|
||
|
||
# Check backend
|
||
if curl -s http://localhost:8080/docs > /dev/null 2>&1; then
|
||
print_success "Backend is responding on port 8080"
|
||
else
|
||
print_warning "Backend may not be ready yet. Check logs: docker-compose logs backend"
|
||
fi
|
||
|
||
# Check frontend static files
|
||
if [ -f "/var/www/html/rackham/index.html" ]; then
|
||
print_success "Frontend deployed to /var/www/html/rackham"
|
||
FILE_COUNT=$(find /var/www/html/rackham -type f 2>/dev/null | wc -l)
|
||
print_info " Deployed $FILE_COUNT static files"
|
||
else
|
||
print_warning "Frontend files not found at /var/www/html/rackham"
|
||
fi
|
||
|
||
# Check MongoDB
|
||
if docker exec infra-mongo-1 mongosh btg --quiet --eval "db.runCommand({ ping: 1 }).ok" 2>/dev/null | grep -q "1"; then
|
||
print_success "MongoDB is running and accessible"
|
||
else
|
||
print_warning "MongoDB may not be ready yet. Check logs: docker-compose logs mongo"
|
||
fi
|
||
|
||
# 10. Display summary
|
||
print_header "🎉 Deployment Complete!"
|
||
|
||
echo ""
|
||
echo -e "${GREEN}The BTG Rackham Video Sales Coach is now deployed!${NC}"
|
||
echo ""
|
||
echo "Deployment Details:"
|
||
echo " - Frontend: Static files at /var/www/html/rackham (served by Apache)"
|
||
echo " - Backend: Docker container on port 8080"
|
||
echo " - MongoDB: Docker container on port 27021"
|
||
echo ""
|
||
|
||
if [ -n "$API_URL" ] && [[ "$API_URL" == https://* ]]; then
|
||
echo "Production Access:"
|
||
echo " - Frontend: https://ai-sandbox.oliver.solutions/rackham"
|
||
echo " - Backend: https://ai-sandbox.oliver.solutions/rackham-back"
|
||
echo " - API Docs: https://ai-sandbox.oliver.solutions/rackham-back/docs"
|
||
else
|
||
echo "Local Access:"
|
||
echo " - Frontend: Access via Apache (http://$DOMAIN/rackham)"
|
||
echo " - Backend: http://localhost:8080"
|
||
echo " - API Docs: http://localhost:8080/docs"
|
||
fi
|
||
echo ""
|
||
|
||
echo "Useful Commands:"
|
||
echo " - View backend logs: cd $SCRIPT_DIR/infra && $DOCKER_COMPOSE logs -f backend"
|
||
echo " - Restart backend: cd $SCRIPT_DIR/infra && $DOCKER_COMPOSE restart backend"
|
||
echo " - Stop services: cd $SCRIPT_DIR/infra && $DOCKER_COMPOSE down"
|
||
echo " - Rebuild frontend: cd $SCRIPT_DIR && ./deploy.sh (run again)"
|
||
echo ""
|
||
|
||
print_info "Next Steps:"
|
||
echo " 1. Add Apache config for /rackham (see instructions below)"
|
||
echo " 2. Reload Apache: sudo systemctl reload apache2"
|
||
echo " 3. Access: https://ai-sandbox.oliver.solutions/rackham"
|
||
echo " 4. Create your first user account"
|
||
echo " 5. Upload a test video (5-10 min recommended)"
|
||
echo ""
|
||
|
||
print_header "Apache Configuration Required"
|
||
|
||
echo -e "${YELLOW}Add the following to your /etc/apache2/apache2.conf:${NC}"
|
||
echo ""
|
||
echo "# rackham video analyzer frontend - SPA routing"
|
||
echo "<Directory /var/www/html/rackham>"
|
||
echo " Options -Indexes +FollowSymLinks"
|
||
echo " AllowOverride All"
|
||
echo " Require all granted"
|
||
echo ""
|
||
echo " # Handle React Router (SPA routing)"
|
||
echo " RewriteEngine On"
|
||
echo " RewriteBase /rackham/"
|
||
echo " RewriteRule ^index\\.html$ - [L]"
|
||
echo " RewriteCond %{REQUEST_FILENAME} !-f"
|
||
echo " RewriteCond %{REQUEST_FILENAME} !-d"
|
||
echo " RewriteRule . /rackham/index.html [L]"
|
||
echo ""
|
||
echo " # Cache static assets"
|
||
echo " <FilesMatch \"\\.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$\">"
|
||
echo " Header set Cache-Control \"public, max-age=31536000, immutable\""
|
||
echo " </FilesMatch>"
|
||
echo ""
|
||
echo " # No cache for index.html"
|
||
echo " <FilesMatch \"^index\\.html$\">"
|
||
echo " Header set Cache-Control \"no-cache, no-store, must-revalidate\""
|
||
echo " </FilesMatch>"
|
||
echo "</Directory>"
|
||
echo ""
|
||
echo "# rackham video analyzer backend - already in your config"
|
||
echo "ProxyPass /rackham-back/ http://localhost:8080/"
|
||
echo "ProxyPassReverse /rackham-back/ http://localhost:8080/"
|
||
echo ""
|
||
echo -e "${YELLOW}Then run: sudo systemctl reload apache2${NC}"
|
||
echo ""
|
||
|
||
print_warning "IMPORTANT: Make sure to backup your .env file - it contains sensitive credentials!"
|
||
echo ""
|
||
|
||
# Create a backup of .env
|
||
ENV_BACKUP="$SCRIPT_DIR/infra/.env.backup-$(date +%Y%m%d-%H%M%S)"
|
||
cp "$ENV_FILE" "$ENV_BACKUP"
|
||
print_success "Backup of .env created at: $ENV_BACKUP"
|
||
|
||
echo ""
|
||
print_success "Deployment completed successfully! 🚀"
|
||
echo ""
|