1092 lines
38 KiB
Bash
Executable File
1092 lines
38 KiB
Bash
Executable File
#!/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 <service-name>"
|
|
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 <service-name>"
|
|
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 <command> [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 <svc> - Reset database and re-run migrations for a service"
|
|
echo " migrate-push <svc> - Force sync schema (creates missing tables)"
|
|
echo " clean - Remove all containers, volumes, and images"
|
|
echo ""
|
|
echo "Single Service Commands:"
|
|
echo " start-svc <name> - Start a specific service"
|
|
echo " stop-svc <name> - Stop a specific service"
|
|
echo " rebuild-svc <name> [--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
|