#!/usr/bin/env bash # ============================================================ # Amazon Transcreation Platform — Deploy Script # ============================================================ # Usage: # First time: ./deploy.sh --init # Updates: ./deploy.sh # Full rebuild: ./deploy.sh --rebuild # # Location: /opt/amazon-transcreation # URL: https://optical-dev.oliver.solutions/amazon-transcreation # ============================================================ set -euo pipefail APP_DIR="/opt/amazon-transcreation" COMPOSE_FILE="docker-compose.prod.yml" COMPOSE="docker compose -f $COMPOSE_FILE" RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' log() { echo -e "${GREEN}[DEPLOY]${NC} $1"; } warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } error() { echo -e "${RED}[ERROR]${NC} $1"; exit 1; } cd "$APP_DIR" || error "Directory $APP_DIR not found. Clone the repo first." # --------------------------------------------------------------- # Parse arguments # --------------------------------------------------------------- INIT=false REBUILD=false for arg in "$@"; do case $arg in --init) INIT=true ;; --rebuild) REBUILD=true ;; --help) echo "Usage: $0 [--init] [--rebuild]" echo " --init First-time setup (create .env, run migrations, seed data, configure Apache)" echo " --rebuild Force rebuild all Docker images (no cache)" echo " (no args) Pull latest code, rebuild changed images, restart" exit 0 ;; *) error "Unknown argument: $arg" ;; esac done # --------------------------------------------------------------- # Pre-flight checks # --------------------------------------------------------------- log "Running pre-flight checks..." command -v docker >/dev/null 2>&1 || error "Docker is not installed" command -v git >/dev/null 2>&1 || error "Git is not installed" docker info >/dev/null 2>&1 || error "Docker daemon is not running" # --------------------------------------------------------------- # First-time init # --------------------------------------------------------------- if [ "$INIT" = true ]; then log "=== FIRST-TIME SETUP ===" # Create .env if it doesn't exist if [ ! -f .env ]; then if [ -f .env.production.example ]; then cp .env.production.example .env # Generate a real JWT secret JWT_SECRET=$(python3 -c "import secrets; print(secrets.token_hex(32))" 2>/dev/null || openssl rand -hex 32) sed -i "s/CHANGE_ME_GENERATE_WITH.*/$JWT_SECRET/" .env # Generate a real DB password DB_PASS=$(python3 -c "import secrets; print(secrets.token_hex(16))" 2>/dev/null || openssl rand -hex 16) sed -i "s/CHANGE_ME_DB_PASSWORD/$DB_PASS/g" .env warn ".env created from template. You MUST edit it to set:" warn " - ANTHROPIC_API_KEY (your Claude API key)" warn "" warn "Pre-filled values (verify these are correct):" warn " - AZURE_AD_TENANT_ID" warn " - AZURE_AD_CLIENT_ID" warn " - AZURE_AD_SSO_ENABLED" warn "" warn "Auto-generated values:" warn " - JWT_SECRET_KEY" warn " - DB_PASSWORD" echo "" read -p "Edit .env now? [Y/n] " -n 1 -r echo if [[ ! $REPLY =~ ^[Nn]$ ]]; then ${EDITOR:-nano} .env fi else error ".env.production.example not found. Is this the right directory?" fi else log ".env already exists, skipping creation" fi # Storage directories are now tracked in git (storage/amazon/tm + ref) # Just ensure the base directory exists for runtime uploads mkdir -p storage # Build all images log "Building all Docker images (this may take a few minutes)..." $COMPOSE build # Start database and redis first log "Starting database and redis..." $COMPOSE up -d db redis log "Waiting for database to be healthy..." sleep 5 # Start backend for migrations log "Starting backend..." $COMPOSE up -d backend sleep 5 # Wait for backend to be ready log "Waiting for backend to start..." for i in $(seq 1 30); do if curl -sf http://127.0.0.1:8040/health >/dev/null 2>&1; then break fi sleep 2 done # Run migrations log "Running database migrations..." $COMPOSE exec -T backend alembic upgrade head # TM and reference files are now tracked in git — no import step needed. # Verify they arrived via git pull: TM_COUNT=$(find storage/amazon/tm -name '*.json' 2>/dev/null | wc -l) REF_COUNT=$(find storage/amazon/ref -name '*.json' 2>/dev/null | wc -l) log "Found $TM_COUNT TM files and $REF_COUNT reference files in storage/amazon/" if [ "$TM_COUNT" -eq 0 ]; then warn "No TM files found! Check that git pulled storage/amazon/tm/ correctly." fi # Seed database log "Seeding database (default client + test users)..." $COMPOSE exec -T backend python -m seed.create_default_client $COMPOSE exec -T backend python -m seed.create_test_users # Register existing TM + reference files in the database log "Registering storage files in database..." $COMPOSE exec -T backend python -m seed.register_storage_files # Start all remaining services log "Starting all services..." $COMPOSE up -d --remove-orphans # --------------------------------------------------------------- # SSO configuration check # --------------------------------------------------------------- SSO_ENABLED_VAL=$(grep -E "^AZURE_AD_SSO_ENABLED=" .env | cut -d= -f2 | tr -d '[:space:]' || true) TENANT_ID_VAL=$(grep -E "^AZURE_AD_TENANT_ID=" .env | cut -d= -f2 | tr -d '[:space:]' || true) CLIENT_ID_VAL=$(grep -E "^AZURE_AD_CLIENT_ID=" .env | cut -d= -f2 | tr -d '[:space:]' || true) if [ "$SSO_ENABLED_VAL" = "true" ]; then if [ -z "$TENANT_ID_VAL" ] || [ -z "$CLIENT_ID_VAL" ]; then warn "AZURE_AD_SSO_ENABLED=true but AZURE_AD_TENANT_ID or AZURE_AD_CLIENT_ID is empty." warn "SSO will not work until these are set in .env." else log "SSO configured: tenant=${TENANT_ID_VAL:0:8}... client=${CLIENT_ID_VAL:0:8}..." fi else log "SSO disabled (AZURE_AD_SSO_ENABLED != true). Set it in .env to enable." fi # --------------------------------------------------------------- # Apache configuration # --------------------------------------------------------------- log "Configuring Apache reverse proxy..." # Enable required modules a2enmod proxy proxy_http proxy_wstunnel rewrite headers 2>/dev/null || true # Find the SSL vhost for optical-dev.oliver.solutions VHOST_FILE=$(grep -rl "optical-dev.oliver.solutions" /etc/apache2/sites-enabled/ 2>/dev/null | head -1 || true) if [ -n "$VHOST_FILE" ]; then # Check if already configured if grep -q "amazon-transcreation" "$VHOST_FILE" 2>/dev/null; then log "Apache already has amazon-transcreation config in $VHOST_FILE" else warn "Found vhost at: $VHOST_FILE" warn "" warn "Add the following INSIDE the block, before :" warn "" warn " Include /opt/amazon-transcreation/apache/amazon-transcreation.conf" warn "" warn "Then run: sudo systemctl reload apache2" echo "" read -p "Want me to add it automatically? [y/N] " -n 1 -r echo if [[ $REPLY =~ ^[Yy]$ ]]; then # Insert Include line before sed -i "/<\/VirtualHost>/i\\ Include /opt/amazon-transcreation/apache/amazon-transcreation.conf" "$VHOST_FILE" systemctl reload apache2 log "Apache config added and reloaded." fi fi else warn "Could not find Apache vhost for optical-dev.oliver.solutions" warn "Manually add this to your SSL vhost:" warn " Include /opt/amazon-transcreation/apache/amazon-transcreation.conf" warn "Then: sudo systemctl reload apache2" fi # Health check sleep 3 if curl -sf http://127.0.0.1:8040/health >/dev/null 2>&1; then log "Backend health check passed" else warn "Backend health check failed — check logs: $COMPOSE logs backend" fi echo "" log "=== SETUP COMPLETE ===" log "" log "URL: https://optical-dev.oliver.solutions/amazon-transcreation" log "" log "Services running:" $COMPOSE ps --format "table {{.Name}}\t{{.Status}}\t{{.Ports}}" log "" log "Test credentials:" log " admin@amazon.com / admin123!" log " manager@amazon.com / manager123!" log " reviewer@amazon.com / reviewer123!" log "" log "Next steps:" log " 1. Verify ANTHROPIC_API_KEY is set in .env" log " 2. Ensure Apache config is loaded (see above)" log " 3. Change default passwords via admin panel" log " 4. SSO: verify AZURE_AD_SSO_ENABLED=true and Azure AD vars in .env" log " Redirect URI registered in Azure AD:" log " https://optical-dev.oliver.solutions/amazon-transcreation/login" exit 0 fi # --------------------------------------------------------------- # Standard deploy (git pull + rebuild + restart) # --------------------------------------------------------------- [ -f .env ] || error ".env not found. Run './deploy.sh --init' first." # Pull latest code log "Pulling latest code..." git pull --ff-only || error "Git pull failed. Resolve conflicts manually." # Check SSO vars are in .env (they bake into the frontend image at build time) SSO_ENABLED_VAL=$(grep -E "^AZURE_AD_SSO_ENABLED=" .env | cut -d= -f2 | tr -d '[:space:]' || true) if [ "$SSO_ENABLED_VAL" = "true" ]; then TENANT_ID_VAL=$(grep -E "^AZURE_AD_TENANT_ID=" .env | cut -d= -f2 | tr -d '[:space:]' || true) CLIENT_ID_VAL=$(grep -E "^AZURE_AD_CLIENT_ID=" .env | cut -d= -f2 | tr -d '[:space:]' || true) if [ -z "$TENANT_ID_VAL" ] || [ -z "$CLIENT_ID_VAL" ]; then warn "AZURE_AD_SSO_ENABLED=true but AZURE_AD_TENANT_ID or AZURE_AD_CLIENT_ID is empty in .env." warn "The SSO button will NOT appear in the frontend build." else log "SSO enabled — Azure AD vars will be baked into the frontend image." fi fi # Build images # Frontend always gets --no-cache to avoid stale Next.js builds from Docker layer cache if [ "$REBUILD" = true ]; then log "Rebuilding all images (no cache)..." $COMPOSE build --no-cache else log "Building backend..." $COMPOSE build backend celery_worker log "Building frontend (no cache)..." $COMPOSE build --no-cache frontend fi # Run migrations (safe to run even if nothing changed) log "Running database migrations..." $COMPOSE up -d db redis sleep 3 $COMPOSE up -d backend sleep 5 $COMPOSE exec -T backend alembic upgrade head # Restart all services (remove orphans to avoid stale containers) log "Restarting all services..." $COMPOSE down --remove-orphans $COMPOSE up -d # Health check log "Waiting for services..." sleep 5 if curl -sf http://127.0.0.1:8040/health >/dev/null 2>&1; then log "Health check passed" else warn "Health check failed — check: $COMPOSE logs --tail 50" fi echo "" $COMPOSE ps --format "table {{.Name}}\t{{.Status}}\t{{.Ports}}" echo "" log "Deploy complete. URL: https://optical-dev.oliver.solutions/amazon-transcreation"