#!/bin/bash # ParentFlow Production Database Migration Script # Runs all database migrations for both main app and admin dashboard set -e # Configuration DB_HOST="10.0.0.207" DB_PORT="5432" DB_USER="postgres" DB_PASSWORD="a3ppq" DB_NAME="parentflow" DB_NAME_ADMIN="parentflowadmin" # Color codes RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' log() { echo -e "${BLUE}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $1" } error() { echo -e "${RED}[ERROR]${NC} $1" >&2 exit 1 } success() { echo -e "${GREEN}✓${NC} $1" } warning() { echo -e "${YELLOW}⚠${NC} $1" } # Header echo "" echo "==========================================" echo " Database Migration for Production " echo "==========================================" echo "" # Check PostgreSQL client if ! command -v psql &> /dev/null; then error "PostgreSQL client not installed. Run: apt-get install postgresql-client" fi # Test database connection log "Testing database connection..." PGPASSWORD=$DB_PASSWORD psql -h $DB_HOST -p $DB_PORT -U $DB_USER -d postgres -c "SELECT version();" > /dev/null 2>&1 if [ $? -ne 0 ]; then error "Cannot connect to database at $DB_HOST:$DB_PORT" fi success "Database connection successful" # Create databases if they don't exist log "Ensuring databases exist..." PGPASSWORD=$DB_PASSWORD psql -h $DB_HOST -p $DB_PORT -U $DB_USER -d postgres << EOF -- Create main database SELECT 'CREATE DATABASE ${DB_NAME}' WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = '${DB_NAME}')\\gexec -- Create admin database SELECT 'CREATE DATABASE ${DB_NAME_ADMIN}' WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = '${DB_NAME_ADMIN}')\\gexec EOF success "Databases verified" # Enable extensions log "Enabling required extensions..." PGPASSWORD=$DB_PASSWORD psql -h $DB_HOST -p $DB_PORT -U $DB_USER -d $DB_NAME -c "CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\";" PGPASSWORD=$DB_PASSWORD psql -h $DB_HOST -p $DB_PORT -U $DB_USER -d $DB_NAME -c "CREATE EXTENSION IF NOT EXISTS vector;" PGPASSWORD=$DB_PASSWORD psql -h $DB_HOST -p $DB_PORT -U $DB_USER -d $DB_NAME_ADMIN -c "CREATE EXTENSION IF NOT EXISTS \"uuid-ossp\";" success "Extensions enabled" # Find migration directory MIGRATION_DIR="$(dirname "$0")/maternal-app/maternal-app-backend/src/database/migrations" if [ ! -d "$MIGRATION_DIR" ]; then error "Migration directory not found: $MIGRATION_DIR" fi # Create migration tracking table log "Creating migration tracking table..." PGPASSWORD=$DB_PASSWORD psql -h $DB_HOST -p $DB_PORT -U $DB_USER -d $DB_NAME << 'EOF' CREATE TABLE IF NOT EXISTS schema_migrations ( id SERIAL PRIMARY KEY, version VARCHAR(255) NOT NULL UNIQUE, executed_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, execution_time_ms INTEGER, success BOOLEAN DEFAULT true, error_message TEXT, checksum VARCHAR(64) ); CREATE INDEX IF NOT EXISTS idx_schema_migrations_version ON schema_migrations(version); CREATE INDEX IF NOT EXISTS idx_schema_migrations_executed ON schema_migrations(executed_at); EOF success "Migration tracking table ready" # Define all migrations in order MIGRATIONS=( "V001_create_core_auth.sql" "V002_create_family_structure.sql" "V003_create_child_entities.sql" "V004_create_activity_tables.sql" "V005_create_notification_system.sql" "V006_create_audit_log.sql" "V007_create_analytics_tables.sql" "V008_add_verification_system.sql" "V008_add_eula_fields.sql" "V009_add_family_preferences.sql" "V009_add_multi_child_preferences.sql" "V010_enhance_notifications.sql" "V010_add_ai_conversations.sql" "V011_add_tracking_enhancements.sql" "V011_create_photos_module.sql" "V012_add_device_registry.sql" "V013_add_mfa_support.sql" "V014_add_refresh_token_rotation.sql" "V015_add_audit_fields.sql" "V016_add_webauthn_support.sql" "V017_add_gdpr_compliance.sql" "V018_add_ai_embeddings.sql" "V019_add_voice_feedback.sql" "V020_add_indexes_optimization.sql" "V021_add_timezone_support.sql" "V022_add_data_archiving.sql" "V028_create_invite_codes.sql" ) # Function to calculate file checksum calculate_checksum() { local file=$1 if command -v sha256sum &> /dev/null; then sha256sum "$file" | cut -d' ' -f1 else echo "no-checksum" fi } # Run migrations log "Running migrations for main database..." TOTAL=${#MIGRATIONS[@]} CURRENT=0 SKIPPED=0 EXECUTED=0 for migration in "${MIGRATIONS[@]}"; do CURRENT=$((CURRENT + 1)) MIGRATION_FILE="$MIGRATION_DIR/$migration" if [ ! -f "$MIGRATION_FILE" ]; then warning "[$CURRENT/$TOTAL] Migration file not found: $migration" continue fi # Check if migration was already executed VERSION=$(echo $migration | cut -d'_' -f1) ALREADY_RUN=$(PGPASSWORD=$DB_PASSWORD psql -h $DB_HOST -p $DB_PORT -U $DB_USER -d $DB_NAME -tAc \ "SELECT COUNT(*) FROM schema_migrations WHERE version = '$VERSION';") if [ "$ALREADY_RUN" = "1" ]; then echo -e "${YELLOW}[$CURRENT/$TOTAL]${NC} Skipping $migration (already applied)" SKIPPED=$((SKIPPED + 1)) continue fi echo -e "${BLUE}[$CURRENT/$TOTAL]${NC} Applying $migration..." START_TIME=$(date +%s%3N) CHECKSUM=$(calculate_checksum "$MIGRATION_FILE") # Run migration if PGPASSWORD=$DB_PASSWORD psql -h $DB_HOST -p $DB_PORT -U $DB_USER -d $DB_NAME -f "$MIGRATION_FILE" > /dev/null 2>&1; then END_TIME=$(date +%s%3N) EXEC_TIME=$((END_TIME - START_TIME)) # Record successful migration PGPASSWORD=$DB_PASSWORD psql -h $DB_HOST -p $DB_PORT -U $DB_USER -d $DB_NAME << EOF INSERT INTO schema_migrations (version, execution_time_ms, checksum) VALUES ('$VERSION', $EXEC_TIME, '$CHECKSUM'); EOF success "Applied $migration (${EXEC_TIME}ms)" EXECUTED=$((EXECUTED + 1)) else error "Failed to apply migration: $migration" fi done log "Migration summary: $EXECUTED executed, $SKIPPED skipped, $TOTAL total" # Run admin-specific migrations if needed log "Setting up admin database..." PGPASSWORD=$DB_PASSWORD psql -h $DB_HOST -p $DB_PORT -U $DB_USER -d $DB_NAME_ADMIN << 'EOF' -- Admin users table (if not exists from main DB) CREATE TABLE IF NOT EXISTS admin_users ( id VARCHAR(36) PRIMARY KEY DEFAULT gen_random_uuid()::text, email VARCHAR(255) NOT NULL UNIQUE, password_hash VARCHAR(255) NOT NULL, name VARCHAR(100), role VARCHAR(50) DEFAULT 'admin', is_active BOOLEAN DEFAULT true, last_login_at TIMESTAMP WITH TIME ZONE, created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, permissions JSONB DEFAULT '["users:read", "users:write", "invites:read", "invites:write", "analytics:read"]', two_factor_secret VARCHAR(255), two_factor_enabled BOOLEAN DEFAULT false ); -- Admin sessions CREATE TABLE IF NOT EXISTS admin_sessions ( id VARCHAR(36) PRIMARY KEY DEFAULT gen_random_uuid()::text, admin_user_id VARCHAR(36) REFERENCES admin_users(id) ON DELETE CASCADE, token_hash VARCHAR(255) NOT NULL UNIQUE, ip_address VARCHAR(45), user_agent TEXT, expires_at TIMESTAMP WITH TIME ZONE NOT NULL, created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, last_activity_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP ); -- Admin audit logs CREATE TABLE IF NOT EXISTS admin_audit_logs ( id VARCHAR(36) PRIMARY KEY DEFAULT gen_random_uuid()::text, admin_user_id VARCHAR(36) REFERENCES admin_users(id), action VARCHAR(100) NOT NULL, entity_type VARCHAR(50), entity_id VARCHAR(36), details JSONB DEFAULT '{}', ip_address VARCHAR(45), user_agent TEXT, created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP ); -- Create indexes CREATE INDEX IF NOT EXISTS idx_admin_users_email ON admin_users(email); CREATE INDEX IF NOT EXISTS idx_admin_sessions_token ON admin_sessions(token_hash); CREATE INDEX IF NOT EXISTS idx_admin_sessions_admin ON admin_sessions(admin_user_id); CREATE INDEX IF NOT EXISTS idx_admin_audit_logs_admin ON admin_audit_logs(admin_user_id); CREATE INDEX IF NOT EXISTS idx_admin_audit_logs_created ON admin_audit_logs(created_at); -- Insert default admin user if not exists (password: admin123) INSERT INTO admin_users (email, password_hash, name, role) VALUES ( 'admin@parentflowapp.com', '$2b$10$H5hw3/iwkCichU5dpVIMqe5Me7WV9jz.qWRm0V4JyGF9smgxgFBxm', 'System Administrator', 'super_admin' ) ON CONFLICT (email) DO NOTHING; EOF success "Admin database configured" # Verify migration status log "Verifying migration status..." TOTAL_APPLIED=$(PGPASSWORD=$DB_PASSWORD psql -h $DB_HOST -p $DB_PORT -U $DB_USER -d $DB_NAME -tAc \ "SELECT COUNT(*) FROM schema_migrations WHERE success = true;") echo "" echo "==========================================" echo -e "${GREEN} Database Migration Completed! ${NC}" echo "==========================================" echo "" echo "Summary:" echo " Total migrations: $TOTAL" echo " Applied in this run: $EXECUTED" echo " Previously applied: $SKIPPED" echo " Total in database: $TOTAL_APPLIED" echo "" echo "Databases ready:" echo " Main: $DB_NAME at $DB_HOST:$DB_PORT" echo " Admin: $DB_NAME_ADMIN at $DB_HOST:$DB_PORT" echo "" success "All migrations completed successfully"