Ctrl K
ComunidadGuías…

manage.sh

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
#!/bin/bash

# ============================================================================
# Worklenz Local Management Script
# ============================================================================
# This script provides an interactive menu for managing local Worklenz deployment
# Supports both localhost (self-signed SSL) and production domains (Let's Encrypt)
# ============================================================================

set -e

# Color codes for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color

# Configuration
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ENV_FILE="$SCRIPT_DIR/.env"
DOCKER_COMPOSE_FILE="$SCRIPT_DIR/docker-compose.yml"
SSL_DIR="$SCRIPT_DIR/nginx/ssl"
BACKUP_DIR="$SCRIPT_DIR/backups"
REPO_DIR="$SCRIPT_DIR"

# ============================================================================
# Helper Functions
# ============================================================================

print_header() {
    clear
    echo -e "${GREEN}"
    echo " __          __        _    _"                
    echo " \ \        / /       | |  | |"               
    echo "  \ \  /\  / /__  _ __| | _| | ___ _ __  ____"
    echo "   \ \/  \/ / _ \| '__| |/ / |/ _ \ '_ \|_  /"
    echo "    \  /\  / (_) | |  |   <| |  __/ | | |/ /" 
    echo "     \/  \/ \___/|_|  |_|\_\_|\___|_| |_/___|"
    echo ""
    echo "         W O R K L E N Z                     "
    echo -e "${NC}"
    echo -e "${CYAN}╔════════════════════════════════════════════════════════════════════════╗${NC}"
    echo -e "${CYAN}║           ${BLUE}Worklenz Self-Hosted Management Console${CYAN}                    ║${NC}"
    echo -e "${CYAN}╚════════════════════════════════════════════════════════════════════════╝${NC}"
    echo ""
}

print_success() {
    echo -e "${GREEN}✓${NC} $1"
}

print_error() {
    echo -e "${RED}✗${NC} $1"
}

print_warning() {
    echo -e "${YELLOW}⚠${NC} $1"
}

print_info() {
    echo -e "${BLUE}ℹ${NC} $1"
}

# Load environment variables
load_env() {
    if [ -f "$ENV_FILE" ]; then
        set -a
        source "$ENV_FILE"
        set +a
    fi
}

# Check if Docker is installed
check_docker() {
    if ! command -v docker &> /dev/null; then
        print_error "Docker is not installed!"
        print_info "Install Docker: https://docs.docker.com/get-docker/"
        return 1
    fi

    if ! command -v docker-compose &> /dev/null && ! docker compose version &> /dev/null 2>&1; then
        print_error "Docker Compose is not installed!"
        print_info "Install Docker Compose: https://docs.docker.com/compose/install/"
        return 1
    fi
    return 0
}

# Get docker compose command
get_compose_cmd() {
    if docker compose version &> /dev/null 2>&1; then
        echo "docker compose"
    else
        echo "docker-compose"
    fi
}

# Detect if running on localhost or production domain
is_localhost() {
    local domain="${DOMAIN:-localhost}"
    if [[ "$domain" == "localhost" || "$domain" == "127.0.0.1" || "$domain" == "0.0.0.0" ]]; then
        return 0
    fi
    return 1
}

# Generate secure random string
generate_secret() {
    if command -v openssl &> /dev/null; then
        openssl rand -hex 32
    else
        cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 64 | head -n 1
    fi
}

update_env_value() {
    local key="$1"
    local value="$2"
    local escaped_value
    escaped_value=$(printf '%s' "$value" | sed 's/[\\&|]/\\&/g')

    if grep -q "^${key}=" "$ENV_FILE"; then
        sed -i.bak "s|^${key}=.*|${key}=${escaped_value}|" "$ENV_FILE"
    else
        printf '%s=%s\n' "$key" "$value" >> "$ENV_FILE"
    fi
}

# ============================================================================
# SSL Certificate Functions
# ============================================================================

setup_self_signed_ssl() {
    print_info "Setting up self-signed SSL certificates for localhost..."

    mkdir -p "$SSL_DIR"

    if command -v openssl &> /dev/null; then
        openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
            -keyout "$SSL_DIR/key.pem" \
            -out "$SSL_DIR/cert.pem" \
            -subj "/C=US/ST=State/L=City/O=Worklenz/CN=localhost" \
            &> /dev/null

        chmod 644 "$SSL_DIR/cert.pem"
        chmod 600 "$SSL_DIR/key.pem"

        print_success "Self-signed SSL certificates generated"
    else
        # Use Docker with alpine/openssl if openssl not available
        docker run --rm -v "$SSL_DIR:/certs" alpine/openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
            -keyout /certs/key.pem \
            -out /certs/cert.pem \
            -subj "/C=US/ST=State/L=City/O=Worklenz/CN=localhost" \
            2>/dev/null

        chmod 644 "$SSL_DIR/cert.pem"
        chmod 600 "$SSL_DIR/key.pem"

        print_success "Self-signed SSL certificates generated (using Docker)"
    fi
}

setup_letsencrypt_ssl() {
    local domain="${DOMAIN}"
    local email="${LETSENCRYPT_EMAIL}"
    local compose_cmd=$(get_compose_cmd)

    if [ -z "$email" ]; then
        print_error "LETSENCRYPT_EMAIL not set in .env file!"
        echo ""
        read -p "Enter email for Let's Encrypt notifications: " email
        if [ -z "$email" ]; then
            print_error "Email is required for Let's Encrypt"
            return 1
        fi
        
        # Update .env file
        if grep -q "^LETSENCRYPT_EMAIL=" "$ENV_FILE"; then
            sed -i.bak "s/^LETSENCRYPT_EMAIL=.*/LETSENCRYPT_EMAIL=$email/" "$ENV_FILE"
        else
            echo "LETSENCRYPT_EMAIL=$email" >> "$ENV_FILE"
        fi
        load_env
    fi

    print_info "Setting up Let's Encrypt SSL for $domain..."
    echo ""
    print_warning "Before continuing, ensure:"
    echo "  ✓ DNS A record for $domain points to this server IP"
    echo "  ✓ Port 80 and 443 are accessible from the internet"
    echo "  ✓ No firewall blocking HTTP/HTTPS traffic"
    echo ""
    read -p "Continue? (y/N): " -n 1 -r
    echo
    if [[ ! $REPLY =~ ^[Yy]$ ]]; then
        print_info "SSL setup cancelled"
        return 1
    fi

    # Start nginx for ACME challenge
    print_info "Starting nginx for domain verification..."
    $compose_cmd up -d nginx
    sleep 5

    # Request certificate
    print_info "Requesting SSL certificate from Let's Encrypt..."
    print_info "This may take 1-2 minutes..."
    echo ""

    $compose_cmd run --rm certbot certonly --webroot \
        --webroot-path=/var/www/certbot \
        --email "$email" \
        --agree-tos \
        --no-eff-email \
        -d "$domain" \
        2>&1 | tee /tmp/certbot.log

    if [ ${PIPESTATUS[0]} -eq 0 ]; then
        # Update nginx config to use Let's Encrypt certificates
        print_info "Updating nginx configuration..."
        
        cp "$SCRIPT_DIR/nginx/conf.d/worklenz.conf" "$SCRIPT_DIR/nginx/conf.d/worklenz.conf.bak"
        
        sed -i.tmp \
            -e "s|ssl_certificate /etc/nginx/ssl/cert.pem;|ssl_certificate /etc/letsencrypt/live/$domain/fullchain.pem;|" \
            -e "s|ssl_certificate_key /etc/nginx/ssl/key.pem;|ssl_certificate_key /etc/letsencrypt/live/$domain/privkey.pem;|" \
            "$SCRIPT_DIR/nginx/conf.d/worklenz.conf"
        
        rm -f "$SCRIPT_DIR/nginx/conf.d/worklenz.conf.tmp"

        # Update .env to enable SSL
        sed -i.bak 's/^ENABLE_SSL=.*/ENABLE_SSL=true/' "$ENV_FILE"
        
        print_success "Let's Encrypt SSL certificates obtained successfully!"
        print_info "Backup saved: nginx/conf.d/worklenz.conf.bak"
        return 0
    else
        print_error "Failed to obtain Let's Encrypt certificate"
        echo ""
        print_info "Common issues:"
        echo "  • DNS not configured properly (test with: dig $domain)"
        echo "  • Port 80 blocked by firewall"
        echo "  • Domain not pointing to this server"
        echo "  • Another service using port 80"
        echo ""
        print_info "Check logs: cat /tmp/certbot.log"
        return 1
    fi
}

auto_setup_ssl() {
    if is_localhost; then
        print_info "Detected localhost deployment - using self-signed SSL"
        setup_self_signed_ssl
        sed -i.bak 's/^ENABLE_SSL=.*/ENABLE_SSL=false/' "$ENV_FILE"
    else
        print_info "Detected domain deployment: ${DOMAIN}"
        print_info "Setting up Let's Encrypt SSL..."
        setup_letsencrypt_ssl
    fi
}

# ============================================================================
# Service Management Functions
# ============================================================================

start_services() {
    print_header
    echo -e "${BLUE}Starting Worklenz Docker Environment...${NC}"
    echo ""

    # Check if .env exists, if not create from .env.example
    if [ ! -f "$ENV_FILE" ]; then
        if [ -f "${ENV_FILE}.example" ]; then
            print_info "Creating .env from .env.example..."
            cp "${ENV_FILE}.example" "$ENV_FILE"
            print_success ".env file created"
            echo ""
        else
            print_error ".env file not found and .env.example not found!"
            print_info "Please create .env file from .env.example"
            return 1
        fi
    fi

    load_env
    local compose_cmd=$(get_compose_cmd)
    local deployment_mode="${DEPLOYMENT_MODE:-express}"
    local compose_profiles="--profile $deployment_mode"

    # Add SSL profile if enabled
    if [[ "${ENABLE_SSL:-false}" == "true" ]]; then
        compose_profiles="$compose_profiles --profile ssl"
    fi

    print_info "Deployment mode: $deployment_mode"
    if is_localhost; then
        print_info "SSL mode: Self-signed (localhost)"
    else
        print_info "SSL mode: Let's Encrypt (${DOMAIN})"
    fi
    echo ""

    print_info "Starting containers..."
    $compose_cmd $compose_profiles up -d

    echo ""
    print_info "Waiting for services to be ready..."
    sleep 10

    # Show service status
    echo ""
    $compose_cmd ps
    echo ""

    print_success "Worklenz started!"
    echo ""
    echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
    echo -e "  ${GREEN}Access Worklenz at:${NC} ${VITE_API_URL:-https://localhost}"
    echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
    
    if is_localhost; then
        echo ""
        print_warning "Using self-signed SSL - browser will show security warning"
        print_info "Click 'Advanced' → 'Proceed to localhost' to continue"
    fi
}

stop_services() {
    print_header
    echo -e "${BLUE}Stopping Worklenz Services${NC}"
    echo ""

    local compose_cmd=$(get_compose_cmd)
    $compose_cmd --profile express --profile advanced --profile ssl --profile backup down

    print_success "Services stopped"
}

restart_services() {
    stop_services
    echo ""
    read -p "Press Enter to start services..."
    start_services
}

view_logs() {
    print_header
    echo -e "${BLUE}Service Logs${NC}"
    echo ""
    echo "1. All services"
    echo "2. Backend"
    echo "3. Frontend"
    echo "4. PostgreSQL"
    echo "5. Nginx"
    echo "6. SeaweedFS"
    echo "7. Redis"
    echo "8. Certbot"
    echo "0. Back"
    echo ""

    read -p "Enter choice [0-8]: " log_choice
    local compose_cmd=$(get_compose_cmd)

    case $log_choice in
        1) $compose_cmd logs -f --tail=100 ;;
        2) $compose_cmd logs -f --tail=100 backend ;;
        3) $compose_cmd logs -f --tail=100 frontend ;;
        4) $compose_cmd logs -f --tail=100 postgres ;;
        5) $compose_cmd logs -f --tail=100 nginx ;;
        6) $compose_cmd logs -f --tail=100 seaweedfs ;;
        7) $compose_cmd logs -f --tail=100 redis ;;
        8) $compose_cmd logs -f --tail=100 certbot ;;
        0) return ;;
        *) print_error "Invalid choice" ;;
    esac
}

service_status() {
    print_header
    echo -e "${BLUE}Service Status${NC}"
    echo ""

    local compose_cmd=$(get_compose_cmd)
    $compose_cmd ps

    echo ""
    print_info "Detailed health status:"
    echo ""

    # Check each service
    docker ps --filter "name=worklenz-" --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" | grep -E "worklenz-|NAMES"
}

# ============================================================================
# Backup and Restore Functions
# ============================================================================

backup_data() {
    print_header
    echo -e "${BLUE}Backup Worklenz Data${NC}"
    echo ""

    load_env
    mkdir -p "$BACKUP_DIR"

    local compose_cmd=$(get_compose_cmd)
    local timestamp=$(date +%Y%m%d_%H%M%S)
    local backup_dir="$BACKUP_DIR/backup_$timestamp"
    
    mkdir -p "$backup_dir"

    print_info "Creating comprehensive backup..."
    echo ""

    # Backup database
    print_info "[1/4] Backing up PostgreSQL database..."
    $compose_cmd exec -T postgres pg_dump -U "${DB_USER:-postgres}" "${DB_NAME:-worklenz_db}" > "$backup_dir/database.sql"
    print_success "Database backed up"

    # Backup Redis data
    if [[ "${DEPLOYMENT_MODE:-express}" == "express" ]]; then
        print_info "[2/4] Backing up Redis data..."
        $compose_cmd exec -T redis redis-cli --raw SAVE > /dev/null 2>&1 || true
        docker run --rm -v worklenz_redis_data:/data -v "$backup_dir:/backup" alpine tar czf "/backup/redis_data.tar.gz" -C /data . 2>/dev/null
        print_success "Redis data backed up"

        # Backup SeaweedFS data
        print_info "[3/4] Backing up SeaweedFS storage..."
        docker run --rm -v worklenz_seaweedfs_data:/data -v "$backup_dir:/backup" alpine tar czf "/backup/seaweedfs_data.tar.gz" -C /data . 2>/dev/null
        print_success "SeaweedFS storage backed up"
    else
        print_info "[2/4] Skipping Redis backup (advanced mode)"
        print_info "[3/4] Skipping SeaweedFS backup (advanced mode)"
    fi

    # Backup configuration
    print_info "[4/4] Backing up configuration..."
    cp "$ENV_FILE" "$backup_dir/env_backup"
    cp -r "$SCRIPT_DIR/nginx" "$backup_dir/nginx_backup" 2>/dev/null || true
    print_success "Configuration backed up"

    # Create archive
    print_info "Creating backup archive..."
    tar -czf "$BACKUP_DIR/worklenz_backup_$timestamp.tar.gz" -C "$BACKUP_DIR" "backup_$timestamp"
    rm -rf "$backup_dir"

    local backup_file="$BACKUP_DIR/worklenz_backup_$timestamp.tar.gz"
    local backup_size=$(du -h "$backup_file" | cut -f1)

    echo ""
    print_success "Backup completed successfully!"
    print_info "Backup file: $backup_file"
    print_info "Backup size: $backup_size"

    # Cleanup old backups
    local retention_days="${BACKUP_RETENTION_DAYS:-30}"
    print_info "Cleaning backups older than $retention_days days..."
    find "$BACKUP_DIR" -name "worklenz_backup_*.tar.gz" -mtime +$retention_days -delete
}

restore_data() {
    print_header
    echo -e "${BLUE}Restore Worklenz Data${NC}"
    echo ""

    # List available backups
    print_info "Available backups:"
    echo ""
    
    local backups=($(ls -t "$BACKUP_DIR"/worklenz_backup_*.tar.gz 2>/dev/null))

    if [ ${#backups[@]} -eq 0 ]; then
        print_error "No backups found in $BACKUP_DIR"
        return 1
    fi

    local i=1
    for backup in "${backups[@]}"; do
        local size=$(du -h "$backup" | cut -f1)
        local filename=$(basename "$backup")
        local date_str=$(echo "$filename" | sed 's/worklenz_backup_\(.*\)\.tar\.gz/\1/' | sed 's/_/ /; s/_/:/')
        echo "$i. $date_str ($size)"
        i=$((i + 1))
    done
    echo "0. Cancel"
    echo ""

    read -p "Select backup to restore [1-${#backups[@]}]: " backup_choice

    if [[ "$backup_choice" == "0" || -z "$backup_choice" ]]; then
        print_info "Restore cancelled"
        return 0
    fi

    if [ "$backup_choice" -lt 1 ] || [ "$backup_choice" -gt ${#backups[@]} ]; then
        print_error "Invalid selection"
        return 1
    fi

    local selected_backup="${backups[$((backup_choice - 1))]}"

    echo ""
    print_warning "⚠️  WARNING: This will REPLACE current data!"
    print_warning "⚠️  Current database, Redis, and SeaweedFS data will be LOST!"
    echo ""
    read -p "Type 'yes' to confirm restore: " confirm

    if [[ "$confirm" != "yes" ]]; then
        print_info "Restore cancelled"
        return 0
    fi

    load_env
    local compose_cmd=$(get_compose_cmd)
    local temp_dir=$(mktemp -d)

    # Extract backup
    print_info "Extracting backup..."
    tar -xzf "$selected_backup" -C "$temp_dir"
    local backup_content=$(ls "$temp_dir")

    # Stop services
    print_info "Stopping services..."
    $compose_cmd down

    # Restore database
    print_info "Restoring database..."
    $compose_cmd up -d postgres
    sleep 10

    if [ -f "$temp_dir/$backup_content/database.sql" ]; then
        $compose_cmd exec -T postgres psql -U "${DB_USER:-postgres}" -d "${DB_NAME:-worklenz_db}" < "$temp_dir/$backup_content/database.sql" 2>/dev/null
        print_success "Database restored"
    fi

    # Restore Redis
    if [ -f "$temp_dir/$backup_content/redis_data.tar.gz" ]; then
        print_info "Restoring Redis data..."
        docker run --rm -v worklenz_redis_data:/data -v "$temp_dir/$backup_content:/backup" alpine sh -c "rm -rf /data/* && tar xzf /backup/redis_data.tar.gz -C /data"
        print_success "Redis data restored"
    fi

    # Restore SeaweedFS
    if [ -f "$temp_dir/$backup_content/seaweedfs_data.tar.gz" ]; then
        print_info "Restoring SeaweedFS storage..."
        docker run --rm -v worklenz_seaweedfs_data:/data -v "$temp_dir/$backup_content:/backup" alpine sh -c "rm -rf /data/* && tar xzf /backup/seaweedfs_data.tar.gz -C /data"
        print_success "SeaweedFS storage restored"
    fi

    # Restore configuration
    if [ -f "$temp_dir/$backup_content/env_backup" ]; then
        echo ""
        read -p "Restore .env configuration? (y/N): " -n 1 -r
        echo
        if [[ $REPLY =~ ^[Yy]$ ]]; then
            cp "$temp_dir/$backup_content/env_backup" "$ENV_FILE"
            print_success "Configuration restored"
        fi
    fi

    # Cleanup
    rm -rf "$temp_dir"

    # Restart services
    print_info "Restarting services..."
    start_services

    echo ""
    print_success "Restore completed successfully!"
}

# ============================================================================
# Configuration Functions
# ============================================================================

# Check if a value is a placeholder
is_placeholder() {
    local value="$1"
    if [[ "$value" =~ ^CHANGE_THIS || "$value" == "dummy" || -z "$value" ]]; then
        return 0
    fi
    return 1
}

# Auto-configure environment based on DOMAIN variable
auto_configure_env() {
    if [ ! -f "$ENV_FILE" ]; then
        print_error ".env file not found!"
        return 1
    fi

    load_env
    local domain="${DOMAIN:-localhost}"
    local needs_update=0

    print_info "Auto-configuring environment for domain: $domain"

    # Check and generate secrets if needed
    if is_placeholder "$SESSION_SECRET"; then
        print_info "Generating SESSION_SECRET..."
        local session_secret=$(generate_secret)
        sed -i.bak "s/^SESSION_SECRET=.*/SESSION_SECRET=$session_secret/" "$ENV_FILE"
        needs_update=1
    fi

    if is_placeholder "$COOKIE_SECRET"; then
        print_info "Generating COOKIE_SECRET..."
        local cookie_secret=$(generate_secret)
        sed -i.bak "s/^COOKIE_SECRET=.*/COOKIE_SECRET=$cookie_secret/" "$ENV_FILE"
        needs_update=1
    fi

    if is_placeholder "$JWT_SECRET"; then
        print_info "Generating JWT_SECRET..."
        local jwt_secret=$(generate_secret)
        sed -i.bak "s/^JWT_SECRET=.*/JWT_SECRET=$jwt_secret/" "$ENV_FILE"
        needs_update=1
    fi

    if is_placeholder "$DB_PASSWORD"; then
        print_info "Generating DB_PASSWORD..."
        local db_password=$(generate_secret | cut -c1-32)
        sed -i.bak "s/^DB_PASSWORD=.*/DB_PASSWORD=$db_password/" "$ENV_FILE"
        needs_update=1
    fi

    if is_placeholder "$S3_SECRET_ACCESS_KEY"; then
        print_info "Generating S3_SECRET_ACCESS_KEY (SeaweedFS secret)..."
        local seaweedfs_secret=$(generate_secret | cut -c1-32)
        update_env_value "S3_SECRET_ACCESS_KEY" "$seaweedfs_secret"
        needs_update=1
    fi

    # Update Redis password if it's still default
    if [[ "$REDIS_PASSWORD" == "worklenz_redis_pass" ]]; then
        print_info "Generating secure REDIS_PASSWORD..."
        local redis_password=$(generate_secret | cut -c1-32)
        sed -i.bak "s/^REDIS_PASSWORD=.*/REDIS_PASSWORD=$redis_password/" "$ENV_FILE"
        needs_update=1
    fi

    # Update URLs based on domain
    if [[ "$domain" == "localhost" ]]; then
        print_info "Configuring URLs for localhost..."
        sed -i.bak "s|^VITE_API_URL=.*|VITE_API_URL=https://localhost|" "$ENV_FILE"
        sed -i.bak "s|^VITE_SOCKET_URL=.*|VITE_SOCKET_URL=wss://localhost|" "$ENV_FILE"
        sed -i.bak "s|^FRONTEND_URL=.*|FRONTEND_URL=https://localhost|" "$ENV_FILE"
        sed -i.bak "s|^SERVER_CORS=.*|SERVER_CORS=https://localhost|" "$ENV_FILE"
        sed -i.bak "s|^SOCKET_IO_CORS=.*|SOCKET_IO_CORS=https://localhost|" "$ENV_FILE"
        sed -i.bak "s|^GOOGLE_CALLBACK_URL=.*|GOOGLE_CALLBACK_URL=https://localhost/auth/google/callback|" "$ENV_FILE"
        needs_update=1
    else
        print_info "Configuring URLs for production domain: $domain..."
        sed -i.bak "s|^VITE_API_URL=.*|VITE_API_URL=https://$domain|" "$ENV_FILE"
        sed -i.bak "s|^VITE_SOCKET_URL=.*|VITE_SOCKET_URL=wss://$domain|" "$ENV_FILE"
        sed -i.bak "s|^FRONTEND_URL=.*|FRONTEND_URL=https://$domain|" "$ENV_FILE"
        sed -i.bak "s|^SERVER_CORS=.*|SERVER_CORS=https://$domain|" "$ENV_FILE"
        sed -i.bak "s|^SOCKET_IO_CORS=.*|SOCKET_IO_CORS=https://$domain|" "$ENV_FILE"
        sed -i.bak "s|^GOOGLE_CALLBACK_URL=.*|GOOGLE_CALLBACK_URL=https://$domain/auth/google/callback|" "$ENV_FILE"
        needs_update=1
    fi

    rm -f "$ENV_FILE.bak"

    if [ $needs_update -eq 1 ]; then
        print_success "Environment auto-configured successfully!"
        echo ""
        print_info "Generated secure secrets for:"
        echo "  - SESSION_SECRET"
        echo "  - COOKIE_SECRET"
        echo "  - JWT_SECRET"
        echo "  - DB_PASSWORD"
        echo "  - S3_SECRET_ACCESS_KEY (SeaweedFS)"
        echo "  - REDIS_PASSWORD"
        echo ""
        print_info "Configured URLs for domain: $domain"
    else
        print_success "Environment already configured"
    fi
}

# Interactive configuration
configure_env() {
    print_header
    echo -e "${BLUE}Environment Configuration${NC}"
    echo ""

    if [ -f "$ENV_FILE" ]; then
        print_warning ".env file already exists"
        read -p "Reconfigure? (y/N): " -n 1 -r
        echo
        if [[ ! $REPLY =~ ^[Yy]$ ]]; then
            return 0
        fi
    fi

    # Domain configuration
    echo ""
    read -p "Enter domain (or 'localhost' for local testing) [localhost]: " domain
    domain=${domain:-localhost}

    # Generate secrets
    print_info "Generating secure secrets..."
    local session_secret=$(generate_secret)
    local cookie_secret=$(generate_secret)
    local jwt_secret=$(generate_secret)

    # Passwords
    echo ""
    read -sp "Enter PostgreSQL password (or press Enter to generate): " db_password
    echo
    if [ -z "$db_password" ]; then
        db_password=$(generate_secret | cut -c1-32)
    fi

    read -sp "Enter SeaweedFS secret (or press Enter to generate): " seaweedfs_secret
    echo
    if [ -z "$seaweedfs_secret" ]; then
        seaweedfs_secret=$(generate_secret | cut -c1-32)
    fi

    read -sp "Enter Redis password (or press Enter to generate): " redis_password
    echo
    if [ -z "$redis_password" ]; then
        redis_password=$(generate_secret | cut -c1-32)
    fi

    # Update .env file
    sed -i.bak "s/^DOMAIN=.*/DOMAIN=$domain/" "$ENV_FILE"
    sed -i.bak "s/^SESSION_SECRET=.*/SESSION_SECRET=$session_secret/" "$ENV_FILE"
    sed -i.bak "s/^COOKIE_SECRET=.*/COOKIE_SECRET=$cookie_secret/" "$ENV_FILE"
    sed -i.bak "s/^JWT_SECRET=.*/JWT_SECRET=$jwt_secret/" "$ENV_FILE"
    sed -i.bak "s/^DB_PASSWORD=.*/DB_PASSWORD=$db_password/" "$ENV_FILE"
    update_env_value "S3_SECRET_ACCESS_KEY" "$seaweedfs_secret"
    sed -i.bak "s/^REDIS_PASSWORD=.*/REDIS_PASSWORD=$redis_password/" "$ENV_FILE"

    # Update URLs based on domain
    if [[ "$domain" == "localhost" ]]; then
        sed -i.bak "s|^VITE_API_URL=.*|VITE_API_URL=https://localhost|" "$ENV_FILE"
        sed -i.bak "s|^VITE_SOCKET_URL=.*|VITE_SOCKET_URL=wss://localhost|" "$ENV_FILE"
        sed -i.bak "s|^FRONTEND_URL=.*|FRONTEND_URL=https://localhost|" "$ENV_FILE"
        sed -i.bak "s|^SERVER_CORS=.*|SERVER_CORS=https://localhost|" "$ENV_FILE"
        sed -i.bak "s|^SOCKET_IO_CORS=.*|SOCKET_IO_CORS=https://localhost|" "$ENV_FILE"
    else
        sed -i.bak "s|^VITE_API_URL=.*|VITE_API_URL=https://$domain|" "$ENV_FILE"
        sed -i.bak "s|^VITE_SOCKET_URL=.*|VITE_SOCKET_URL=wss://$domain|" "$ENV_FILE"
        sed -i.bak "s|^FRONTEND_URL=.*|FRONTEND_URL=https://$domain|" "$ENV_FILE"
        sed -i.bak "s|^SERVER_CORS=.*|SERVER_CORS=https://$domain|" "$ENV_FILE"
        sed -i.bak "s|^SOCKET_IO_CORS=.*|SOCKET_IO_CORS=https://$domain|" "$ENV_FILE"

        # Ask for Let's Encrypt email
        echo ""
        read -p "Enter email for Let's Encrypt: " letsencrypt_email
        sed -i.bak "s/^# LETSENCRYPT_EMAIL=.*/LETSENCRYPT_EMAIL=$letsencrypt_email/" "$ENV_FILE"
        sed -i.bak "s/^LETSENCRYPT_EMAIL=.*/LETSENCRYPT_EMAIL=$letsencrypt_email/" "$ENV_FILE"
    fi

    rm -f "$ENV_FILE.bak"

    echo ""
    print_success "Configuration completed!"
    print_info "Configuration saved to .env"
}

# ============================================================================
# Docker Image Build and Push Functions
# ============================================================================

# Build images only (no push)
build_images() {
    print_header
    echo -e "${BLUE}Build Docker Images${NC}"
    echo ""

    # Check prerequisites
    if ! check_docker; then
        return 1
    fi

    # Check if .env exists, if not create from .env.example
    if [ ! -f "$ENV_FILE" ]; then
        if [ -f "${ENV_FILE}.example" ]; then
            print_info "Creating .env from .env.example..."
            cp "${ENV_FILE}.example" "$ENV_FILE"
            print_success ".env file created"
        else
            print_error ".env file not found and .env.example not found!"
            print_info "Please create .env file from .env.example"
            return 1
        fi
    fi

    load_env

    # Ask for Docker Hub username
    local docker_username="${DOCKER_USERNAME:-}"
    if [ -z "$docker_username" ]; then
        echo ""
        read -p "Enter Docker Hub username: " docker_username
        if [ -z "$docker_username" ]; then
            print_error "Docker Hub username is required!"
            return 1
        fi
        # Save to .env
        sed -i.bak "s/^DOCKER_USERNAME=.*/DOCKER_USERNAME=$docker_username/" "$ENV_FILE"
        rm -f "$ENV_FILE.bak"
    fi

    # Image version - always use latest
    local image_version="latest"

    echo ""
    print_info "Docker Hub Username: $docker_username"
    print_info "Image Version: $image_version"
    echo ""

    # Build backend image
    print_info "Building backend image..."
    echo ""
    docker build \
        -f "$SCRIPT_DIR/worklenz-backend/Dockerfile" \
        -t "${docker_username}/worklenz-backend:${image_version}" \
        "$SCRIPT_DIR/worklenz-backend"

    if [ $? -ne 0 ]; then
        print_error "Backend build failed!"
        return 1
    fi

    print_success "Backend image built successfully!"
    echo ""

    # Build frontend image
    print_info "Building frontend image..."
    echo ""
    docker build \
        -f "$SCRIPT_DIR/worklenz-frontend/Dockerfile" \
        -t "${docker_username}/worklenz-frontend:${image_version}" \
        "$SCRIPT_DIR/worklenz-frontend"

    if [ $? -ne 0 ]; then
        print_error "Frontend build failed!"
        return 1
    fi

    print_success "Frontend image built successfully!"
    echo ""

    # Update docker-compose.yaml to use the new images
    print_info "Updating docker-compose.yaml..."

    # Update backend image
    sed -i.bak "s|image: worklenz-backend:.*|image: ${docker_username}/worklenz-backend:${image_version}|" "$DOCKER_COMPOSE_FILE"

    # Update frontend image
    sed -i.bak "s|image: worklenz-frontend:.*|image: ${docker_username}/worklenz-frontend:${image_version}|" "$DOCKER_COMPOSE_FILE"

    rm -f "$DOCKER_COMPOSE_FILE.bak"

    echo ""
    print_success "Images built successfully!"
    echo ""
    echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
    echo -e "  ${GREEN}Backend Image:${NC} ${docker_username}/worklenz-backend:${image_version}"
    echo -e "  ${GREEN}Frontend Image:${NC} ${docker_username}/worklenz-frontend:${image_version}"
    echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
    echo ""

    # Ask if user wants to push to Docker Hub
    echo ""
    read -p "Do you want to push images to Docker Hub? (y/N): " push_choice

    if [[ "$push_choice" =~ ^[Yy]$ ]]; then
        echo ""
        print_info "Pushing images to Docker Hub..."

        # Login to Docker Hub
        print_info "Please login to Docker Hub..."
        docker login

        if [ $? -ne 0 ]; then
            print_error "Docker login failed!"
            print_info "You can push images later using: ./manage.sh push"
            return 1
        fi

        # Push backend image
        print_info "Pushing backend image..."
        docker push "${docker_username}/worklenz-backend:${image_version}"

        if [ $? -ne 0 ]; then
            print_error "Backend image push failed!"
            return 1
        fi

        print_success "Backend image pushed successfully!"
        echo ""

        # Push frontend image
        print_info "Pushing frontend image..."
        docker push "${docker_username}/worklenz-frontend:${image_version}"

        if [ $? -ne 0 ]; then
            print_error "Frontend image push failed!"
            return 1
        fi

        print_success "Frontend image pushed successfully!"
        echo ""
        print_success "Images are now available on Docker Hub!"
        echo ""
        echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
        echo -e "  ${GREEN}Backend:${NC} https://hub.docker.com/r/${docker_username}/worklenz-backend"
        echo -e "  ${GREEN}Frontend:${NC} https://hub.docker.com/r/${docker_username}/worklenz-frontend"
        echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
    else
        print_info "Images built locally only (not pushed to Docker Hub)"
        print_info "To push later, use: ./manage.sh push"
    fi

    # Ask if user wants to start services after building
    echo ""
    read -p "Start services with the new images? (Y/n): " start_choice
    start_choice=${start_choice:-Y}

    if [[ "$start_choice" =~ ^[Yy]$ ]]; then
        echo ""
        print_info "Starting services..."
        start_services
    else
        print_info "To start services later, use: ./manage.sh start"
    fi
}

# Push images to Docker Hub
push_images() {
    print_header
    echo -e "${BLUE}Push Docker Images to Docker Hub${NC}"
    echo ""

    # Check prerequisites
    if ! check_docker; then
        return 1
    fi

    if [ ! -f "$ENV_FILE" ]; then
        print_error ".env file not found!"
        return 1
    fi

    load_env

    local docker_username="${DOCKER_USERNAME:-}"
    if [ -z "$docker_username" ]; then
        print_error "DOCKER_USERNAME not set in .env file!"
        print_info "Please set DOCKER_USERNAME or build images first"
        return 1
    fi

    local image_version="latest"

    echo ""
    print_info "Docker Hub Username: $docker_username"
    print_info "Image Version: $image_version"
    echo ""

    # Check if images exist locally
    if ! docker images | grep -q "${docker_username}/worklenz-backend"; then
        print_error "Backend image not found locally!"
        print_info "Please build images first: ./manage.sh build"
        return 1
    fi

    if ! docker images | grep -q "${docker_username}/worklenz-frontend"; then
        print_error "Frontend image not found locally!"
        print_info "Please build images first: ./manage.sh build"
        return 1
    fi

    # Login to Docker Hub
    print_info "Logging in to Docker Hub..."
    echo ""
    docker login

    if [ $? -ne 0 ]; then
        print_error "Docker Hub login failed!"
        return 1
    fi

    echo ""
    print_success "Docker Hub login successful!"
    echo ""

    # Push backend image
    print_info "Pushing backend image to Docker Hub..."
    docker push "${docker_username}/worklenz-backend:${image_version}"

    if [ $? -ne 0 ]; then
        print_error "Backend push failed!"
        return 1
    fi

    print_success "Backend image pushed successfully!"
    echo ""

    # Push frontend image
    print_info "Pushing frontend image to Docker Hub..."
    docker push "${docker_username}/worklenz-frontend:${image_version}"

    if [ $? -ne 0 ]; then
        print_error "Frontend push failed!"
        return 1
    fi

    print_success "Frontend image pushed successfully!"
    echo ""
    echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
    echo -e "  ${GREEN}Backend Image:${NC} ${docker_username}/worklenz-backend:${image_version}"
    echo -e "  ${GREEN}Frontend Image:${NC} ${docker_username}/worklenz-frontend:${image_version}"
    echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
}

# Build and push (convenience function that calls both)
build_and_push_images() {
    build_images
    if [ $? -eq 0 ]; then
        echo ""
        push_images
    fi
}

# ============================================================================
# Installation Function
# ============================================================================

install_worklenz() {
    print_header
    echo -e "${BLUE}Installing Worklenz${NC}"
    echo ""

    # Check prerequisites
    if ! check_docker; then
        return 1
    fi

    # Check if .env exists, if not create from .env.example
    if [ ! -f "$ENV_FILE" ]; then
        if [ -f "${ENV_FILE}.example" ]; then
            print_info "Creating .env from .env.example..."
            cp "${ENV_FILE}.example" "$ENV_FILE"
            print_success ".env file created"
            echo ""
        else
            print_error ".env file not found and .env.example not found!"
            print_info "Please create .env file from .env.example"
            return 1
        fi
    fi

    # Ask for domain configuration
    echo ""
    echo -e "${YELLOW}Domain Configuration${NC}"
    echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
    echo ""
    echo "Enter your domain name:"
    echo "  - For local testing: enter 'localhost'"
    echo "  - For production: enter your domain (e.g., worklenz.example.com)"
    echo ""
    read -p "Domain [localhost]: " domain
    domain=${domain:-localhost}

    # Update DOMAIN in .env
    sed -i.bak "s/^DOMAIN=.*/DOMAIN=$domain/" "$ENV_FILE"
    rm -f "$ENV_FILE.bak"

    print_success "Domain set to: $domain"

    # If production domain, ask for Let's Encrypt email
    if [[ "$domain" != "localhost" && "$domain" != "127.0.0.1" && "$domain" != "0.0.0.0" ]]; then
        echo ""
        echo -e "${YELLOW}SSL Certificate Setup${NC}"
        echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
        echo ""
        echo "For Let's Encrypt SSL certificates, we need an email address."
        echo "This email will be used for:"
        echo "  - Certificate expiration notifications"
        echo "  - Important account updates"
        echo ""
        read -p "Enter email for Let's Encrypt: " letsencrypt_email

        if [ -n "$letsencrypt_email" ]; then
            # Uncomment and set LETSENCRYPT_EMAIL
            sed -i.bak "s/^# LETSENCRYPT_EMAIL=.*/LETSENCRYPT_EMAIL=$letsencrypt_email/" "$ENV_FILE"
            sed -i.bak "s/^LETSENCRYPT_EMAIL=.*/LETSENCRYPT_EMAIL=$letsencrypt_email/" "$ENV_FILE"
            rm -f "$ENV_FILE.bak"
            print_success "Let's Encrypt email configured"
        fi
    fi

    # Auto-configure environment (generate secrets and update URLs)
    echo ""
    auto_configure_env

    load_env

    # Ask if user wants to build custom Docker images
    echo ""
    echo -e "${YELLOW}Docker Image Build${NC}"
    echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
    echo ""
    echo "Do you want to build custom Docker images?"
    echo "  - Answer 'yes' to build from source code"
    echo "  - Answer 'no' to use pre-built images from Docker Hub"
    echo ""
    read -p "Build custom images? (y/N): " build_choice

    if [[ "$build_choice" =~ ^[Yy]$ ]]; then
        echo ""
        print_info "Building custom Docker images..."

        # Build images without interactive prompts (already have username if needed)
        local docker_username="${DOCKER_USERNAME:-}"
        if [ -z "$docker_username" ]; then
            echo ""
            read -p "Enter Docker Hub username: " docker_username
            if [ -n "$docker_username" ]; then
                sed -i.bak "s/^DOCKER_USERNAME=.*/DOCKER_USERNAME=$docker_username/" "$ENV_FILE"
                rm -f "$ENV_FILE.bak"
                print_success "Docker Hub username saved: $docker_username"
            fi
        fi

        local image_version="latest"

        if [ -n "$docker_username" ]; then
            # Build backend image
            print_info "Building backend image..."
            docker build \
                -f "$SCRIPT_DIR/worklenz-backend/Dockerfile" \
                -t "${docker_username}/worklenz-backend:${image_version}" \
                "$SCRIPT_DIR/worklenz-backend"

            if [ $? -ne 0 ]; then
                print_error "Backend build failed!"
                return 1
            fi

            print_success "Backend image built successfully!"
            echo ""

            # Build frontend image
            print_info "Building frontend image..."
            docker build \
                -f "$SCRIPT_DIR/worklenz-frontend/Dockerfile" \
                -t "${docker_username}/worklenz-frontend:${image_version}" \
                "$SCRIPT_DIR/worklenz-frontend"

            if [ $? -ne 0 ]; then
                print_error "Frontend build failed!"
                return 1
            fi

            print_success "Frontend image built successfully!"
            echo ""

            # Update docker-compose.yaml
            print_info "Updating docker-compose.yaml..."
            sed -i.bak "s|image: worklenz-backend:.*|image: ${docker_username}/worklenz-backend:${image_version}|" "$DOCKER_COMPOSE_FILE"
            sed -i.bak "s|image: worklenz-frontend:.*|image: ${docker_username}/worklenz-frontend:${image_version}|" "$DOCKER_COMPOSE_FILE"
            sed -i.bak "s|image: .*/worklenz-backend:.*|image: ${docker_username}/worklenz-backend:${image_version}|" "$DOCKER_COMPOSE_FILE"
            sed -i.bak "s|image: .*/worklenz-frontend:.*|image: ${docker_username}/worklenz-frontend:${image_version}|" "$DOCKER_COMPOSE_FILE"
            rm -f "$DOCKER_COMPOSE_FILE.bak"

            print_success "Custom images ready!"

            # Ask if user wants to push
            echo ""
            read -p "Push images to Docker Hub? (y/N): " push_choice
            if [[ "$push_choice" =~ ^[Yy]$ ]]; then
                print_info "Logging into Docker Hub..."
                docker login

                if [ $? -eq 0 ]; then
                    print_info "Pushing backend image..."
                    docker push "${docker_username}/worklenz-backend:${image_version}"

                    print_info "Pushing frontend image..."
                    docker push "${docker_username}/worklenz-frontend:${image_version}"

                    print_success "Images pushed to Docker Hub!"
                fi
            fi
        fi
        echo ""
    fi

    # Setup SSL
    print_info "Setting up SSL certificates..."
    auto_setup_ssl

    echo ""
    print_info "Pulling images and starting services..."
    echo ""

    local compose_cmd=$(get_compose_cmd)
    local deployment_mode="${DEPLOYMENT_MODE:-express}"
    local compose_profiles="--profile $deployment_mode"

    if [[ "${ENABLE_SSL:-false}" == "true" ]]; then
        compose_profiles="$compose_profiles --profile ssl"
    fi

    # Pull images (will use pre-built images from Docker Hub)
    $compose_cmd $compose_profiles pull

    # Start services (will build only if image doesn't exist locally)
    $compose_cmd $compose_profiles up -d

    echo ""
    print_info "Waiting for services to initialize..."
    sleep 15

    # Check status
    echo ""
    $compose_cmd ps

    echo ""
    print_success "Installation completed!"
    echo ""
    echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
    echo -e "  ${GREEN}Access Worklenz at:${NC} ${VITE_API_URL:-https://localhost}"
    echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
}

# ============================================================================
# Upgrade Function
# ============================================================================

upgrade_worklenz() {
    print_header
    echo -e "${BLUE}Upgrade Worklenz${NC}"
    echo ""

    print_warning "This will pull latest images and rebuild containers"
    read -p "Continue? (y/N): " -n 1 -r
    echo

    if [[ ! $REPLY =~ ^[Yy]$ ]]; then
        return 0
    fi

    # Create backup first
    print_info "Creating backup before upgrade..."
    backup_data

    load_env
    local compose_cmd=$(get_compose_cmd)
    local deployment_mode="${DEPLOYMENT_MODE:-express}"
    local compose_profiles="--profile $deployment_mode"

    if [[ "${ENABLE_SSL:-false}" == "true" ]]; then
        compose_profiles="$compose_profiles --profile ssl"
    fi

    # Pull latest
    print_info "Pulling latest images..."
    $compose_cmd $compose_profiles pull

    # Rebuild
    print_info "Rebuilding services..."
    $compose_cmd $compose_profiles build --no-cache

    # Restart
    print_info "Restarting services..."
    $compose_cmd $compose_profiles up -d

    echo ""
    print_success "Upgrade completed!"
}

# ============================================================================
# SSL Management
# ============================================================================

manage_ssl() {
    print_header
    echo -e "${BLUE}SSL/TLS Management${NC}"
    echo ""
    echo "1. Setup Self-Signed SSL (localhost)"
    echo "2. Setup Let's Encrypt SSL (domain)"
    echo "3. Renew Let's Encrypt Certificate"
    echo "4. View Certificate Info"
    echo "0. Back"
    echo ""

    read -p "Enter choice [0-4]: " ssl_choice

    case $ssl_choice in
        1)
            setup_self_signed_ssl
            read -p "Press Enter to continue..."
            ;;
        2)
            load_env
            setup_letsencrypt_ssl
            read -p "Press Enter to continue..."
            ;;
        3)
            print_info "Renewing Let's Encrypt certificate..."
            local compose_cmd=$(get_compose_cmd)
            $compose_cmd run --rm certbot renew
            $compose_cmd restart nginx
            print_success "Certificate renewed"
            read -p "Press Enter to continue..."
            ;;
        4)
            if [ -f "$SSL_DIR/cert.pem" ]; then
                print_info "Self-signed certificate info:"
                openssl x509 -in "$SSL_DIR/cert.pem" -text -noout | grep -E "(Subject:|Issuer:|Not Before|Not After)"
            fi
            load_env
            if [ ! -z "${DOMAIN}" ] && [ -d "/etc/letsencrypt/live/${DOMAIN}" ]; then
                print_info "Let's Encrypt certificate info:"
                docker run --rm -v worklenz_certbot_certs:/etc/letsencrypt alpine/openssl x509 -in "/etc/letsencrypt/live/${DOMAIN}/fullchain.pem" -text -noout | grep -E "(Subject:|Issuer:|Not Before|Not After)"
            fi
            read -p "Press Enter to continue..."
            ;;
        0)
            return
            ;;
        *)
            print_error "Invalid choice"
            sleep 2
            ;;
    esac
}

# ============================================================================
# Main Menu
# ============================================================================

show_menu() {
    print_header
    echo -e "${YELLOW}Main Menu${NC}"
    echo ""
    echo " 1. Install Worklenz"
    echo " 2. Start Services"
    echo " 3. Stop Services"
    echo " 4. Restart Services"
    echo " 5. Service Status"
    echo " 6. View Logs"
    echo " 7. Backup Data"
    echo " 8. Restore Data"
    echo " 9. Upgrade Worklenz"
    echo "10. Configure Environment"
    echo "11. Manage SSL/TLS"
    echo "12. Build Images"
    echo "13. Push Images to Docker Hub"
    echo " 0. Exit"
    echo ""
}

main() {
    while true; do
        show_menu
        read -p "Enter choice [0-13]: " choice
        echo

        case $choice in
            1)
                install_worklenz
                read -p "Press Enter to continue..."
                ;;
            2)
                start_services
                read -p "Press Enter to continue..."
                ;;
            3)
                stop_services
                read -p "Press Enter to continue..."
                ;;
            4)
                restart_services
                read -p "Press Enter to continue..."
                ;;
            5)
                service_status
                read -p "Press Enter to continue..."
                ;;
            6)
                view_logs
                ;;
            7)
                backup_data
                read -p "Press Enter to continue..."
                ;;
            8)
                restore_data
                read -p "Press Enter to continue..."
                ;;
            9)
                upgrade_worklenz
                read -p "Press Enter to continue..."
                ;;
            10)
                configure_env
                read -p "Press Enter to continue..."
                ;;
            11)
                manage_ssl
                ;;
            12)
                build_images
                read -p "Press Enter to continue..."
                ;;
            13)
                push_images
                read -p "Press Enter to continue..."
                ;;
            0)
                print_info "Exiting..."
                exit 0
                ;;
            *)
                print_error "Invalid choice"
                sleep 2
                ;;
        esac
    done
}

# ============================================================================
# Command-line Interface
# ============================================================================

# Handle command-line arguments
if [ $# -gt 0 ]; then
    case "$1" in
        install)
            install_worklenz
            ;;
        start)
            start_services
            ;;
        stop)
            stop_services
            ;;
        restart)
            restart_services
            ;;
        status)
            service_status
            ;;
        logs)
            view_logs
            ;;
        backup)
            backup_data
            ;;
        restore)
            restore_data
            ;;
        upgrade)
            upgrade_worklenz
            ;;
        configure|config)
            configure_env
            ;;
        auto-configure|auto-config)
            auto_configure_env
            ;;
        ssl)
            manage_ssl
            ;;
        build)
            build_images
            ;;
        push)
            push_images
            ;;
        build-push)
            build_and_push_images
            ;;
        help|--help|-h)
            echo "Worklenz Management Script"
            echo ""
            echo "Usage: $0 [command]"
            echo ""
            echo "Commands:"
            echo "  install          Install Worklenz (auto-generates secrets)"
            echo "  start            Start all services"
            echo "  stop             Stop all services"
            echo "  restart          Restart all services"
            echo "  status           Show service status"
            echo "  logs             View service logs"
            echo "  backup           Create database backup"
            echo "  restore          Restore from backup"
            echo "  upgrade          Upgrade to latest version"
            echo "  configure        Interactive configuration"
            echo "  auto-configure   Auto-configure from .env DOMAIN"
            echo "  ssl              Manage SSL certificates"
            echo "  build            Build Docker images locally"
            echo "  push             Push images to Docker Hub"
            echo "  build-push       Build and push in one step"
            echo "  help             Show this help message"
            echo ""
            echo "If no command is provided, interactive menu will be shown."
            ;;
        *)
            print_error "Unknown command: $1"
            echo "Run '$0 help' for usage information"
            exit 1
            ;;
    esac
else
    # Run main menu if no arguments
    main
fi