#!/bin/bash # # RWA Backend Services - Deployment Script # ========================================= # # Usage: # ./deploy.sh install # First time setup (generate secrets, init databases) # ./deploy.sh up # Start all services # ./deploy.sh down # Stop all services # ./deploy.sh restart # Restart all services # ./deploy.sh status # Show service status # ./deploy.sh logs [svc] # View logs (optional: specific service) # ./deploy.sh build # Rebuild all images # ./deploy.sh migrate # Run database migrations # ./deploy.sh health # Check health of all services # # Infrastructure: # ./deploy.sh infra-up # Start only infrastructure (postgres, redis, kafka) # ./deploy.sh infra-down # Stop infrastructure # ./deploy.sh infra-restart # Restart infrastructure # ./deploy.sh infra-status # Show infrastructure status # ./deploy.sh infra-logs # View infrastructure logs # set -e # =========================================================================== # Configuration # =========================================================================== SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" ENV_FILE="$SCRIPT_DIR/.env" COMPOSE_FILE="$SCRIPT_DIR/docker-compose.yml" # Colors RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' log_info() { echo -e "${GREEN}[INFO]${NC} $1"; } log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } log_error() { echo -e "${RED}[ERROR]${NC} $1"; } log_step() { echo -e "${BLUE}[STEP]${NC} $1"; } # =========================================================================== # Helper Functions # =========================================================================== generate_random_password() { openssl rand -base64 32 | tr -dc 'a-zA-Z0-9' | head -c 32 } generate_hex_key() { openssl rand -hex 32 } check_docker() { if ! command -v docker &> /dev/null; then log_error "Docker is not installed. Please install Docker first." exit 1 fi if ! docker compose version &> /dev/null; then log_error "Docker Compose is not installed. Please install Docker Compose first." exit 1 fi log_info "Docker version: $(docker --version)" log_info "Docker Compose version: $(docker compose version --short)" } # =========================================================================== # Install / Initialize # =========================================================================== install() { log_step "Installing RWA Backend Services..." check_docker # Generate .env file if not exists if [ ! -f "$ENV_FILE" ]; then log_step "Generating secure configuration..." POSTGRES_PASSWORD=$(generate_random_password) JWT_SECRET=$(generate_random_password) SERVICE_JWT_SECRET=$(generate_random_password) WALLET_ENCRYPTION_SALT=$(generate_random_password) BACKUP_ENCRYPTION_KEY=$(generate_hex_key) SHARE_MASTER_KEY=$(generate_hex_key) cat > "$ENV_FILE" << EOF # ============================================================================= # RWA Backend Services - Production Environment Configuration # ============================================================================= # Generated: $(date) # WARNING: Keep this file secure! Do not commit to version control! # ============================================================================= # PostgreSQL Database POSTGRES_USER=rwa_user POSTGRES_PASSWORD=${POSTGRES_PASSWORD} # Redis (leave empty for no password) REDIS_PASSWORD= # JWT Configuration JWT_SECRET=${JWT_SECRET} # Service-to-Service Authentication SERVICE_JWT_SECRET=${SERVICE_JWT_SECRET} # Wallet Encryption WALLET_ENCRYPTION_SALT=${WALLET_ENCRYPTION_SALT} # Backup Encryption (256-bit hex key) BACKUP_ENCRYPTION_KEY=${BACKUP_ENCRYPTION_KEY} # MPC Share Master Key (256-bit hex key) SHARE_MASTER_KEY=${SHARE_MASTER_KEY} # MPC System Address (on 192.168.1.111) MPC_COORDINATOR_URL=http://192.168.1.111:8081 MPC_MESSAGE_ROUTER_URL=ws://192.168.1.111:8082 # MPC Hot Wallet (用于提现转账) HOT_WALLET_USERNAME=rwadurian-system-hot-wallet-01 HOT_WALLET_ADDRESS=0x895aaf83C57f807416E3BbBd093d7aB74a6FDd33 EOF chmod 600 "$ENV_FILE" log_info "Environment file created: $ENV_FILE" log_info "Secrets have been auto-generated" else log_info "Environment file already exists: $ENV_FILE" fi # Create scripts directory mkdir -p "$SCRIPT_DIR/scripts" # Create database init script create_db_init_script log_info "Installation complete!" log_info "" log_info "Next steps:" log_info " 1. Review and edit .env if needed" log_info " 2. Run: ./deploy.sh build" log_info " 3. Run: ./deploy.sh up" } create_db_init_script() { cat > "$SCRIPT_DIR/scripts/init-databases.sh" << 'DBSCRIPT' #!/bin/bash set -e # Function to create database if not exists create_database() { local database=$1 echo "Creating database: $database" psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" <<-EOSQL SELECT 'CREATE DATABASE $database' WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = '$database')\gexec EOSQL } # Create all required databases for db in rwa_identity rwa_wallet rwa_mpc rwa_backup rwa_planting rwa_referral rwa_reward rwa_leaderboard rwa_reporting rwa_authorization rwa_admin rwa_presence; do create_database "$db" done echo "All databases created successfully!" DBSCRIPT chmod +x "$SCRIPT_DIR/scripts/init-databases.sh" log_info "Database init script created" } # =========================================================================== # Docker Compose Operations # =========================================================================== up() { log_step "Starting RWA Backend Services..." if [ ! -f "$ENV_FILE" ]; then log_error "Environment file not found. Run './deploy.sh install' first." exit 1 fi # Start infrastructure first log_info "Starting infrastructure services..." docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" up -d postgres redis zookeeper kafka # Wait for infrastructure log_info "Waiting for infrastructure to be ready..." sleep 10 # Start application services log_info "Starting application services..." docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" up -d log_info "All services started!" log_info "" log_info "Check status with: ./deploy.sh status" log_info "View logs with: ./deploy.sh logs" } down() { log_step "Stopping RWA Backend Services..." docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" down log_info "All services stopped" } restart() { log_step "Restarting RWA Backend Services..." down sleep 3 up } build() { log_step "Building Docker images..." if [ ! -f "$ENV_FILE" ]; then log_error "Environment file not found. Run './deploy.sh install' first." exit 1 fi docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" build --parallel log_info "All images built successfully" } build_no_cache() { log_step "Building Docker images (no cache)..." if [ ! -f "$ENV_FILE" ]; then log_error "Environment file not found. Run './deploy.sh install' first." exit 1 fi docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" build --no-cache --parallel log_info "All images built successfully (no cache)" } # =========================================================================== # Status and Monitoring # =========================================================================== status() { echo "" echo "============================================" echo "RWA Backend Services Status" echo "============================================" echo "" docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" ps echo "" echo "============================================" echo "Service Health Check" echo "============================================" echo "" health } health() { # Format: "service-name:port:health-endpoint" local services=( "identity-service:3000:/api/v1/health" "wallet-service:3001:/api/v1/health" "backup-service:3002:/health" "planting-service:3003:/api/v1/health" "referral-service:3004:/api/v1/health" "reward-service:3005:/health" "mpc-service:3006:/api/v1/health" "leaderboard-service:3007:/api/health" "reporting-service:3008:/api/v1/health" "authorization-service:3009:/api/v1/health" "admin-service:3010:/api/v1/health" "presence-service:3011:/api/v1/health" "blockchain-service:3012:/api/v1/health" ) for svc in "${services[@]}"; do # Parse service:port:endpoint name="${svc%%:*}" rest="${svc#*:}" port="${rest%%:*}" endpoint="${rest#*:}" if curl -s -o /dev/null -w "%{http_code}" "http://localhost:${port}${endpoint}" 2>/dev/null | grep -q "200"; then echo -e "${GREEN}[OK]${NC} $name (port $port)" else echo -e "${RED}[FAIL]${NC} $name (port $port)" fi done echo "" # Infrastructure health echo "Infrastructure:" if docker exec rwa-postgres pg_isready -U rwa_user &>/dev/null; then echo -e " ${GREEN}[OK]${NC} PostgreSQL" else echo -e " ${RED}[FAIL]${NC} PostgreSQL" fi if docker exec rwa-redis redis-cli ping &>/dev/null; then echo -e " ${GREEN}[OK]${NC} Redis" else echo -e " ${RED}[FAIL]${NC} Redis" fi if docker exec rwa-kafka kafka-topics --bootstrap-server localhost:9092 --list &>/dev/null; then echo -e " ${GREEN}[OK]${NC} Kafka" else echo -e " ${RED}[FAIL]${NC} Kafka" fi } logs() { local service="$1" if [ -n "$service" ]; then docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" logs -f "$service" else docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" logs -f fi } # =========================================================================== # Database Operations # =========================================================================== migrate() { log_step "Running database migrations..." local services=( "identity-service" "wallet-service" "backup-service" "planting-service" "referral-service" "reward-service" "mpc-service" "leaderboard-service" "reporting-service" "authorization-service" "admin-service" "presence-service" "blockchain-service" ) for svc in "${services[@]}"; do log_info "Running migrations for $svc..." docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" exec "$svc" npx prisma migrate deploy 2>/dev/null || \ log_warn "Migration skipped for $svc (no migrations or service not running)" done log_info "Migrations complete" } migrate_reset() { local service="$1" if [ -z "$service" ]; then log_error "Please specify a service name: ./deploy.sh migrate-reset " exit 1 fi log_warn "This will reset the database for $service and re-run migrations!" read -p "Are you sure? (y/N): " confirm if [ "$confirm" != "y" ] && [ "$confirm" != "Y" ]; then log_info "Migration reset cancelled" exit 0 fi log_step "Resetting database and migrations for $service..." # Use prisma db push to force sync schema without migration history docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" exec "$service" \ npx prisma db push --force-reset --accept-data-loss log_info "Database reset complete for $service" } migrate_push() { local service="$1" if [ -z "$service" ]; then log_error "Please specify a service name: ./deploy.sh migrate-push " exit 1 fi log_step "Force syncing schema for $service (using prisma db push)..." # Use prisma db push to sync schema (creates missing tables) docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" exec "$service" \ npx prisma db push --accept-data-loss log_info "Schema sync complete for $service" } # =========================================================================== # Cleanup # =========================================================================== clean() { log_warn "This will remove all containers, volumes, and images!" read -p "Are you sure? (y/N): " confirm if [ "$confirm" = "y" ] || [ "$confirm" = "Y" ]; then log_step "Cleaning up..." docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" down -v --rmi all log_info "Cleanup complete" else log_info "Cleanup cancelled" fi } # =========================================================================== # Infrastructure Operations # =========================================================================== infra_up() { log_step "Starting infrastructure services..." docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" up -d postgres redis zookeeper kafka log_info "Infrastructure services started" } infra_down() { log_step "Stopping infrastructure services..." docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" stop postgres redis kafka zookeeper log_info "Infrastructure services stopped" } infra_restart() { log_step "Restarting infrastructure services..." infra_down sleep 3 infra_up } infra_status() { echo "" echo "============================================" echo "Infrastructure Status" echo "============================================" echo "" docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" ps postgres redis zookeeper kafka echo "" echo "Health Check:" if docker exec rwa-postgres pg_isready -U rwa_user &>/dev/null; then echo -e " ${GREEN}[OK]${NC} PostgreSQL (port 5432)" else echo -e " ${RED}[FAIL]${NC} PostgreSQL (port 5432)" fi if docker exec rwa-redis redis-cli ping &>/dev/null; then echo -e " ${GREEN}[OK]${NC} Redis (port 6379)" else echo -e " ${RED}[FAIL]${NC} Redis (port 6379)" fi if docker exec rwa-zookeeper nc -z localhost 2181 &>/dev/null; then echo -e " ${GREEN}[OK]${NC} Zookeeper (port 2181)" else echo -e " ${RED}[FAIL]${NC} Zookeeper (port 2181)" fi if docker exec rwa-kafka kafka-topics --bootstrap-server localhost:9092 --list &>/dev/null; then echo -e " ${GREEN}[OK]${NC} Kafka (port 9092)" else echo -e " ${RED}[FAIL]${NC} Kafka (port 9092)" fi } infra_logs() { docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" logs -f postgres redis zookeeper kafka } infra_clean() { log_warn "This will remove infrastructure containers and ALL DATA (postgres, redis, kafka)!" log_warn "All databases will be DELETED!" read -p "Are you sure? (y/N): " confirm if [ "$confirm" = "y" ] || [ "$confirm" = "Y" ]; then log_step "Stopping infrastructure services..." docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" stop postgres redis kafka zookeeper log_step "Removing infrastructure containers..." docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" rm -f postgres redis kafka zookeeper log_step "Removing infrastructure volumes..." docker volume rm -f services_postgres_data services_redis_data 2>/dev/null || true log_info "Infrastructure cleanup complete" log_info "" log_info "To reinstall, run:" log_info " ./deploy.sh infra-up" log_info " ./deploy.sh migrate" else log_info "Cleanup cancelled" fi } infra_reset() { log_warn "This will RESET all infrastructure (clean + reinstall)!" log_warn "All databases will be DELETED and recreated!" read -p "Are you sure? (y/N): " confirm if [ "$confirm" = "y" ] || [ "$confirm" = "Y" ]; then # Clean log_step "Stopping infrastructure services..." docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" stop postgres redis kafka zookeeper log_step "Removing infrastructure containers..." docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" rm -f postgres redis kafka zookeeper log_step "Removing infrastructure volumes..." docker volume rm -f services_postgres_data services_redis_data 2>/dev/null || true # Reinstall log_step "Starting fresh infrastructure..." sleep 3 infra_up log_info "Waiting for infrastructure to be ready..." sleep 10 log_info "Infrastructure reset complete!" log_info "" log_info "Next steps:" log_info " 1. Restart application services: ./deploy.sh restart" log_info " 2. Or rebuild and restart: ./deploy.sh build && ./deploy.sh up" else log_info "Reset cancelled" fi } # =========================================================================== # Single Service Operations # =========================================================================== start_service() { local service="$1" if [ -z "$service" ]; then log_error "Please specify a service name" exit 1 fi log_info "Starting $service..." docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" up -d "$service" } stop_service() { local service="$1" if [ -z "$service" ]; then log_error "Please specify a service name" exit 1 fi log_info "Stopping $service..." docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" stop "$service" } rebuild_service() { local service="$1" local no_cache="$2" if [ -z "$service" ]; then log_error "Please specify a service name" exit 1 fi log_info "Rebuilding $service..." if [ "$no_cache" = "--no-cache" ]; then log_info "Building without cache..." docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" build --no-cache "$service" else docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" build "$service" fi docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" up -d "$service" } # =========================================================================== # Main # =========================================================================== case "${1:-}" in install) install ;; up|start) up ;; down|stop) down ;; restart) restart ;; build) build ;; build-no-cache) build_no_cache ;; status|ps) status ;; health) health ;; logs) logs "$2" ;; migrate) migrate ;; migrate-reset) migrate_reset "$2" ;; migrate-push) migrate_push "$2" ;; clean) clean ;; start-svc) start_service "$2" ;; stop-svc) stop_service "$2" ;; rebuild-svc) rebuild_service "$2" "$3" ;; infra-up) infra_up ;; infra-down) infra_down ;; infra-restart) infra_restart ;; infra-status) infra_status ;; infra-logs) infra_logs ;; infra-clean) infra_clean ;; infra-reset) infra_reset ;; *) echo "RWA Backend Services Deployment Script" echo "" echo "Usage: $0 [options]" echo "" echo "Commands:" echo " install - First time setup (generate secrets, create configs)" echo " up/start - Start all services" echo " down/stop - Stop all services" echo " restart - Restart all services" echo " build - Build all Docker images" echo " build-no-cache - Build all images without cache" echo " status/ps - Show service status" echo " health - Check health of all services" echo " logs [svc] - View logs (optionally for specific service)" echo " migrate - Run database migrations for all services" echo " migrate-reset - Reset database and re-run migrations for a service" echo " migrate-push - Force sync schema (creates missing tables)" echo " clean - Remove all containers, volumes, and images" echo "" echo "Single Service Commands:" echo " start-svc - Start a specific service" echo " stop-svc - Stop a specific service" echo " rebuild-svc [--no-cache] - Rebuild and restart a specific service" echo "" echo "Infrastructure Commands:" echo " infra-up - Start infrastructure (postgres, redis, kafka)" echo " infra-down - Stop infrastructure services" echo " infra-restart - Restart infrastructure services" echo " infra-status - Show infrastructure status and health" echo " infra-logs - View infrastructure logs" echo " infra-clean - Remove infrastructure containers and volumes (DELETES DATA)" echo " infra-reset - Clean and reinstall infrastructure (DELETES DATA)" echo "" echo "Services:" echo " identity-service, wallet-service, backup-service, planting-service," echo " referral-service, reward-service, mpc-service, leaderboard-service," echo " reporting-service, authorization-service, admin-service, presence-service," echo " blockchain-service" echo "" echo "Examples:" echo " $0 install # First time setup" echo " $0 build # Build images" echo " $0 up # Start all services" echo " $0 logs identity-service # View identity-service logs" echo " $0 rebuild-svc mpc-service # Rebuild specific service" echo "" exit 1 ;; esac