#!/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 Debezium Connect (CDC) log_info "Starting Debezium Connect..." docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" up -d debezium-connect # Wait for Debezium Connect to be ready log_info "Waiting for Debezium Connect to be ready..." for i in {1..30}; do if curl -s http://localhost:8084/ > /dev/null 2>&1; then log_info "Debezium Connect is ready!" break fi if [ $i -eq 30 ]; then log_warn "Debezium Connect not ready after 60s, continuing anyway..." fi sleep 2 done # Register Debezium connectors register_debezium_connectors # 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" } register_debezium_connectors() { log_info "Registering Debezium connectors..." # Check existing connectors EXISTING=$(curl -s http://localhost:8084/connectors 2>/dev/null || echo "[]") # Read database credentials from .env source "$ENV_FILE" # Register identity-postgres-connector if echo "$EXISTING" | grep -q "identity-postgres-connector"; then log_info "identity-postgres-connector already registered" else log_info "Registering identity-postgres-connector..." IDENTITY_CONFIG='{ "name": "identity-postgres-connector", "config": { "connector.class": "io.debezium.connector.postgresql.PostgresConnector", "tasks.max": "1", "database.hostname": "postgres", "database.port": "5432", "database.user": "'${POSTGRES_USER:-rwa_user}'", "database.password": "'${POSTGRES_PASSWORD:-rwa_secure_password}'", "database.dbname": "rwa_identity", "topic.prefix": "cdc.identity", "table.include.list": "public.user_accounts", "plugin.name": "pgoutput", "publication.name": "debezium_identity_publication", "publication.autocreate.mode": "filtered", "slot.name": "debezium_identity_slot", "key.converter": "org.apache.kafka.connect.json.JsonConverter", "key.converter.schemas.enable": "false", "value.converter": "org.apache.kafka.connect.json.JsonConverter", "value.converter.schemas.enable": "false", "transforms": "unwrap", "transforms.unwrap.type": "io.debezium.transforms.ExtractNewRecordState", "transforms.unwrap.drop.tombstones": "true", "transforms.unwrap.delete.handling.mode": "rewrite", "transforms.unwrap.add.fields": "op,table,source.ts_ms", "heartbeat.interval.ms": "10000", "snapshot.mode": "initial", "decimal.handling.mode": "string", "time.precision.mode": "connect" } }' RESULT=$(curl -s -X POST \ -H "Content-Type: application/json" \ -d "$IDENTITY_CONFIG" \ "http://localhost:8084/connectors" 2>/dev/null || echo "failed") if echo "$RESULT" | grep -q "identity-postgres-connector"; then log_info "identity-postgres-connector registered successfully" else log_warn "Failed to register identity connector: $RESULT" fi fi # Register referral-postgres-connector if echo "$EXISTING" | grep -q "referral-postgres-connector"; then log_info "referral-postgres-connector already registered" else log_info "Registering referral-postgres-connector..." REFERRAL_CONFIG='{ "name": "referral-postgres-connector", "config": { "connector.class": "io.debezium.connector.postgresql.PostgresConnector", "tasks.max": "1", "database.hostname": "postgres", "database.port": "5432", "database.user": "'${POSTGRES_USER:-rwa_user}'", "database.password": "'${POSTGRES_PASSWORD:-rwa_secure_password}'", "database.dbname": "rwa_referral", "topic.prefix": "cdc.referral", "table.include.list": "public.referral_relationships", "plugin.name": "pgoutput", "publication.name": "debezium_referral_publication", "publication.autocreate.mode": "filtered", "slot.name": "debezium_referral_slot", "key.converter": "org.apache.kafka.connect.json.JsonConverter", "key.converter.schemas.enable": "false", "value.converter": "org.apache.kafka.connect.json.JsonConverter", "value.converter.schemas.enable": "false", "transforms": "unwrap", "transforms.unwrap.type": "io.debezium.transforms.ExtractNewRecordState", "transforms.unwrap.drop.tombstones": "true", "transforms.unwrap.delete.handling.mode": "rewrite", "transforms.unwrap.add.fields": "op,table,source.ts_ms", "heartbeat.interval.ms": "10000", "snapshot.mode": "initial", "decimal.handling.mode": "string", "time.precision.mode": "connect" } }' RESULT=$(curl -s -X POST \ -H "Content-Type: application/json" \ -d "$REFERRAL_CONFIG" \ "http://localhost:8084/connectors" 2>/dev/null || echo "failed") if echo "$RESULT" | grep -q "referral-postgres-connector"; then log_info "referral-postgres-connector registered successfully" else log_warn "Failed to register referral connector: $RESULT" fi fi # Register wallet-postgres-connector if echo "$EXISTING" | grep -q "wallet-postgres-connector"; then log_info "wallet-postgres-connector already registered" else log_info "Registering wallet-postgres-connector..." WALLET_CONFIG='{ "name": "wallet-postgres-connector", "config": { "connector.class": "io.debezium.connector.postgresql.PostgresConnector", "tasks.max": "1", "database.hostname": "postgres", "database.port": "5432", "database.user": "'${POSTGRES_USER:-rwa_user}'", "database.password": "'${POSTGRES_PASSWORD:-rwa_secure_password}'", "database.dbname": "rwa_wallet", "topic.prefix": "cdc.wallet", "table.include.list": "public.wallet_accounts,public.withdrawal_orders,public.fiat_withdrawal_orders,public.wallet_ledger_entries", "plugin.name": "pgoutput", "publication.name": "debezium_wallet_publication", "publication.autocreate.mode": "filtered", "slot.name": "debezium_wallet_slot", "key.converter": "org.apache.kafka.connect.json.JsonConverter", "key.converter.schemas.enable": "false", "value.converter": "org.apache.kafka.connect.json.JsonConverter", "value.converter.schemas.enable": "false", "transforms": "unwrap", "transforms.unwrap.type": "io.debezium.transforms.ExtractNewRecordState", "transforms.unwrap.drop.tombstones": "true", "transforms.unwrap.delete.handling.mode": "rewrite", "transforms.unwrap.add.fields": "op,table,source.ts_ms", "heartbeat.interval.ms": "10000", "snapshot.mode": "initial", "decimal.handling.mode": "string", "time.precision.mode": "connect" } }' RESULT=$(curl -s -X POST \ -H "Content-Type: application/json" \ -d "$WALLET_CONFIG" \ "http://localhost:8084/connectors" 2>/dev/null || echo "failed") if echo "$RESULT" | grep -q "wallet-postgres-connector"; then log_info "wallet-postgres-connector registered successfully" else log_warn "Failed to register wallet connector: $RESULT" fi fi # Register planting-postgres-connector if echo "$EXISTING" | grep -q "planting-postgres-connector"; then log_info "planting-postgres-connector already registered" else log_info "Registering planting-postgres-connector..." PLANTING_CONFIG='{ "name": "planting-postgres-connector", "config": { "connector.class": "io.debezium.connector.postgresql.PostgresConnector", "tasks.max": "1", "database.hostname": "postgres", "database.port": "5432", "database.user": "'${POSTGRES_USER:-rwa_user}'", "database.password": "'${POSTGRES_PASSWORD:-rwa_secure_password}'", "database.dbname": "rwa_planting", "topic.prefix": "cdc.planting", "table.include.list": "public.planting_orders,public.planting_positions,public.contract_signing_tasks,public.fund_allocations", "plugin.name": "pgoutput", "publication.name": "debezium_planting_publication", "publication.autocreate.mode": "filtered", "slot.name": "debezium_planting_slot", "key.converter": "org.apache.kafka.connect.json.JsonConverter", "key.converter.schemas.enable": "false", "value.converter": "org.apache.kafka.connect.json.JsonConverter", "value.converter.schemas.enable": "false", "transforms": "unwrap", "transforms.unwrap.type": "io.debezium.transforms.ExtractNewRecordState", "transforms.unwrap.drop.tombstones": "true", "transforms.unwrap.delete.handling.mode": "rewrite", "transforms.unwrap.add.fields": "op,table,source.ts_ms", "heartbeat.interval.ms": "10000", "snapshot.mode": "initial", "decimal.handling.mode": "string", "time.precision.mode": "connect" } }' RESULT=$(curl -s -X POST \ -H "Content-Type: application/json" \ -d "$PLANTING_CONFIG" \ "http://localhost:8084/connectors" 2>/dev/null || echo "failed") if echo "$RESULT" | grep -q "planting-postgres-connector"; then log_info "planting-postgres-connector registered successfully" else log_warn "Failed to register planting connector: $RESULT" fi fi # Register authorization-postgres-connector if echo "$EXISTING" | grep -q "authorization-postgres-connector"; then log_info "authorization-postgres-connector already registered" else log_info "Registering authorization-postgres-connector..." AUTHORIZATION_CONFIG='{ "name": "authorization-postgres-connector", "config": { "connector.class": "io.debezium.connector.postgresql.PostgresConnector", "tasks.max": "1", "database.hostname": "postgres", "database.port": "5432", "database.user": "'${POSTGRES_USER:-rwa_user}'", "database.password": "'${POSTGRES_PASSWORD:-rwa_secure_password}'", "database.dbname": "rwa_authorization", "topic.prefix": "cdc.authorization", "table.include.list": "public.authorization_roles,public.monthly_assessments,public.system_accounts,public.system_account_ledgers", "plugin.name": "pgoutput", "publication.name": "debezium_authorization_publication", "publication.autocreate.mode": "filtered", "slot.name": "debezium_authorization_slot", "key.converter": "org.apache.kafka.connect.json.JsonConverter", "key.converter.schemas.enable": "false", "value.converter": "org.apache.kafka.connect.json.JsonConverter", "value.converter.schemas.enable": "false", "transforms": "unwrap", "transforms.unwrap.type": "io.debezium.transforms.ExtractNewRecordState", "transforms.unwrap.drop.tombstones": "true", "transforms.unwrap.delete.handling.mode": "rewrite", "transforms.unwrap.add.fields": "op,table,source.ts_ms", "heartbeat.interval.ms": "10000", "snapshot.mode": "initial", "decimal.handling.mode": "string", "time.precision.mode": "connect" } }' RESULT=$(curl -s -X POST \ -H "Content-Type: application/json" \ -d "$AUTHORIZATION_CONFIG" \ "http://localhost:8084/connectors" 2>/dev/null || echo "failed") if echo "$RESULT" | grep -q "authorization-postgres-connector"; then log_info "authorization-postgres-connector registered successfully" else log_warn "Failed to register authorization connector: $RESULT" fi fi } 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 debezium-connect 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 debezium-connect 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 debezium-connect 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 if curl -s http://localhost:8084/ > /dev/null 2>&1; then echo -e " ${GREEN}[OK]${NC} Debezium Connect (port 8084)" # Check connector status CONNECTOR_STATUS=$(curl -s http://localhost:8084/connectors/identity-postgres-connector/status 2>/dev/null | grep -o '"state":"[^"]*"' | head -1 || echo "") if echo "$CONNECTOR_STATUS" | grep -q "RUNNING"; then echo -e " └─ ${GREEN}[RUNNING]${NC} identity-postgres-connector" elif [ -n "$CONNECTOR_STATUS" ]; then echo -e " └─ ${YELLOW}[$CONNECTOR_STATUS]${NC} identity-postgres-connector" else echo -e " └─ ${YELLOW}[NOT REGISTERED]${NC} identity-postgres-connector" fi else echo -e " ${RED}[FAIL]${NC} Debezium Connect (port 8084)" fi } infra_logs() { docker compose -f "$COMPOSE_FILE" --env-file "$ENV_FILE" logs -f postgres redis zookeeper kafka debezium-connect } # =========================================================================== # Debezium CDC Operations # =========================================================================== debezium_status() { echo "" echo "============================================" echo "Debezium CDC Status" echo "============================================" echo "" if ! curl -s http://localhost:8084/ > /dev/null 2>&1; then log_error "Debezium Connect is not running" exit 1 fi echo "Debezium Connect Version:" curl -s http://localhost:8084/ | python3 -m json.tool 2>/dev/null || curl -s http://localhost:8084/ echo "" echo "Registered Connectors:" curl -s http://localhost:8084/connectors | python3 -m json.tool 2>/dev/null || curl -s http://localhost:8084/connectors echo "" echo "Connector Details:" curl -s http://localhost:8084/connectors/identity-postgres-connector/status | python3 -m json.tool 2>/dev/null || \ curl -s http://localhost:8084/connectors/identity-postgres-connector/status } debezium_register() { log_step "Registering Debezium connectors..." if ! curl -s http://localhost:8084/ > /dev/null 2>&1; then log_error "Debezium Connect is not running" exit 1 fi register_debezium_connectors } debezium_restart_connector() { log_step "Restarting Debezium connector..." if ! curl -s http://localhost:8084/ > /dev/null 2>&1; then log_error "Debezium Connect is not running" exit 1 fi curl -X POST http://localhost:8084/connectors/identity-postgres-connector/restart log_info "Connector restart requested" sleep 3 debezium_status } debezium_delete_connector() { log_warn "This will delete the identity-postgres-connector!" read -p "Are you sure? (y/N): " confirm if [ "$confirm" = "y" ] || [ "$confirm" = "Y" ]; then curl -X DELETE http://localhost:8084/connectors/identity-postgres-connector log_info "Connector deleted" else log_info "Cancelled" fi } 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 ;; debezium-status|cdc-status) debezium_status ;; debezium-register|cdc-register) debezium_register ;; debezium-restart|cdc-restart) debezium_restart_connector ;; debezium-delete|cdc-delete) debezium_delete_connector ;; *) 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, debezium)" 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 "Debezium CDC Commands:" echo " debezium-status - Show Debezium connector status" echo " debezium-register - Register Debezium connectors" echo " debezium-restart - Restart Debezium connector" echo " debezium-delete - Delete Debezium connector" 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