veza/scripts/archive/migrate-db.sh
2025-12-12 21:34:34 -05:00

255 lines
8.1 KiB
Bash
Executable file
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/bash
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Configuration
ENVIRONMENT=${1:-local}
BACKUP_DIR="./backups/migrations/$(date +%Y%m%d_%H%M%S)"
ROLLBACK_DIR="./rollback-backups/migrations"
# Determine compose file
if [ "$ENVIRONMENT" == "production" ]; then
COMPOSE_FILE="docker-compose.production.yml"
POSTGRES_USER="${POSTGRES_USER:-veza_user}"
POSTGRES_DB="${POSTGRES_DB:-veza_db}"
else
COMPOSE_FILE="docker-compose.yml"
POSTGRES_USER="${POSTGRES_USER:-veza_user}"
POSTGRES_DB="${POSTGRES_DB:-veza_local}"
fi
# Function to print colored messages
print_info() {
echo -e "${BLUE} $1${NC}"
}
print_success() {
echo -e "${GREEN}$1${NC}"
}
print_warning() {
echo -e "${YELLOW}⚠️ $1${NC}"
}
print_error() {
echo -e "${RED}$1${NC}"
}
# Function to check prerequisites
check_prerequisites() {
print_info "Checking prerequisites..."
command -v docker >/dev/null 2>&1 || {
print_error "Docker is required but not installed. Aborting.";
exit 1;
}
command -v docker-compose >/dev/null 2>&1 || {
print_error "Docker Compose is required but not installed. Aborting.";
exit 1;
}
if ! docker info >/dev/null 2>&1; then
print_error "Docker daemon is not running. Please start Docker and try again.";
exit 1;
fi
if [ ! -f "$COMPOSE_FILE" ]; then
print_error "$COMPOSE_FILE not found. Aborting.";
exit 1;
fi
# Check if postgres container is running
if ! docker-compose -f "$COMPOSE_FILE" ps postgres | grep -q "Up"; then
print_error "PostgreSQL container is not running. Please start it first.";
exit 1;
fi
print_success "Prerequisites check passed"
}
# Function to create backup
create_backup() {
print_info "Creating database backup..."
mkdir -p "${BACKUP_DIR}"
mkdir -p "${ROLLBACK_DIR}"
# Backup database
print_info "Backing up database: ${POSTGRES_DB}..."
docker-compose -f "$COMPOSE_FILE" exec -T postgres pg_dump -U "${POSTGRES_USER}" "${POSTGRES_DB}" > "${BACKUP_DIR}/database.sql" 2>/dev/null || {
print_error "Failed to create database backup";
exit 1;
}
# Save backup to rollback location
cp "${BACKUP_DIR}/database.sql" "${ROLLBACK_DIR}/latest.sql"
print_success "Backup created at ${BACKUP_DIR}/database.sql"
}
# Function to check migration status
check_migration_status() {
print_info "Checking migration status..."
# Check if schema_migrations table exists
docker-compose -f "$COMPOSE_FILE" exec -T postgres psql -U "${POSTGRES_USER}" -d "${POSTGRES_DB}" -tAc \
"SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'schema_migrations');" \
> /dev/null 2>&1 || {
print_warning "schema_migrations table does not exist (will be created)"
return 0
}
# Get list of applied migrations
APPLIED_MIGRATIONS=$(docker-compose -f "$COMPOSE_FILE" exec -T postgres psql -U "${POSTGRES_USER}" -d "${POSTGRES_DB}" -tAc \
"SELECT COUNT(*) FROM schema_migrations;" 2>/dev/null || echo "0")
print_info "Currently applied migrations: ${APPLIED_MIGRATIONS}"
}
# Function to run backend API migrations
run_backend_migrations() {
print_info "Running Backend API migrations (GORM AutoMigrate)..."
# Check if backend-api container is running
if ! docker-compose -f "$COMPOSE_FILE" ps backend-api | grep -q "Up"; then
print_warning "Backend API container is not running. Starting it..."
docker-compose -f "$COMPOSE_FILE" up -d backend-api
sleep 10
fi
# Trigger migrations by restarting the backend-api service
# The backend API runs migrations automatically on startup via Database.Initialize()
print_info "Restarting backend-api to trigger migrations..."
docker-compose -f "$COMPOSE_FILE" restart backend-api || {
print_error "Failed to restart backend-api";
return 1;
}
# Wait for backend to be ready
print_info "Waiting for backend-api to be ready..."
sleep 15
# Check if backend is healthy
if curl -sf "http://localhost:8080/health" >/dev/null 2>&1; then
print_success "Backend API migrations completed successfully"
return 0
else
print_warning "Backend API health check failed (migrations may still be running)"
return 0
fi
}
# Function to run chat server migrations
run_chat_server_migrations() {
print_info "Checking for Chat Server migrations..."
# Check if chat-server has migrations directory
if [ ! -d "veza-chat-server/migrations" ]; then
print_warning "Chat Server migrations directory not found, skipping"
return 0
fi
# Check if chat-server uses sqlx migrate
if docker-compose -f "$COMPOSE_FILE" exec -T chat-server cargo --version >/dev/null 2>&1; then
print_info "Running Chat Server migrations (sqlx migrate)..."
# Run sqlx migrate (if available in the container)
docker-compose -f "$COMPOSE_FILE" exec -T chat-server \
sqlx migrate run --database-url "${DATABASE_URL}" 2>/dev/null || {
print_warning "sqlx migrate not available or migrations already applied"
return 0
}
print_success "Chat Server migrations completed"
else
print_warning "Chat Server migrations will be handled by the service on startup"
fi
}
# Function to verify migrations
verify_migrations() {
print_info "Verifying migrations..."
# Check database connection
if ! docker-compose -f "$COMPOSE_FILE" exec -T postgres psql -U "${POSTGRES_USER}" -d "${POSTGRES_DB}" -c "SELECT 1;" >/dev/null 2>&1; then
print_error "Failed to connect to database";
return 1;
fi
# Check if schema_migrations table exists
TABLE_EXISTS=$(docker-compose -f "$COMPOSE_FILE" exec -T postgres psql -U "${POSTGRES_USER}" -d "${POSTGRES_DB}" -tAc \
"SELECT EXISTS (SELECT FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'schema_migrations');" 2>/dev/null || echo "false")
if [ "$TABLE_EXISTS" = "t" ]; then
MIGRATION_COUNT=$(docker-compose -f "$COMPOSE_FILE" exec -T postgres psql -U "${POSTGRES_USER}" -d "${POSTGRES_DB}" -tAc \
"SELECT COUNT(*) FROM schema_migrations;" 2>/dev/null || echo "0")
print_info "Migrations table exists with ${MIGRATION_COUNT} applied migrations"
fi
print_success "Migration verification passed"
return 0
}
# Function to rollback
rollback() {
print_error "Migration failed! Initiating rollback..."
if [ ! -f "${ROLLBACK_DIR}/latest.sql" ]; then
print_error "No rollback backup found. Manual intervention required."
exit 1
fi
print_info "Rolling back database to previous state..."
# Drop and recreate database (or restore from backup)
print_warning "Restoring database from backup..."
# Restore database
docker-compose -f "$COMPOSE_FILE" exec -T postgres psql -U "${POSTGRES_USER}" -d "${POSTGRES_DB}" < "${ROLLBACK_DIR}/latest.sql" || {
print_error "Failed to restore database from backup. Manual intervention required.";
exit 1;
}
print_warning "Rollback completed. Please verify database manually."
exit 1
}
# Main migration flow
main() {
print_info "Starting database migrations for ${ENVIRONMENT} environment..."
# Trap errors and rollback
trap rollback ERR
check_prerequisites
check_migration_status
create_backup
run_backend_migrations
run_chat_server_migrations
# Wait a bit for all migrations to complete
sleep 5
# Verify migrations
if ! verify_migrations; then
rollback
fi
# Disable trap on success
trap - ERR
print_success "Database migrations completed successfully for ${ENVIRONMENT}!"
print_info "Backup location: ${BACKUP_DIR}/database.sql"
}
# Run main function
main