Files
patchmon.net/tools/fix-migrations.sh

287 lines
8.8 KiB
Bash
Executable File
Raw 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
# PatchMon Migration Fixer
# Standalone script to detect and fix failed Prisma migrations
# Usage: sudo bash fix-migrations.sh [instance-name]
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
# Print functions
print_status() {
echo -e "${GREEN}$1${NC}"
}
print_info() {
echo -e "${BLUE} $1${NC}"
}
print_error() {
echo -e "${RED}$1${NC}"
}
print_warning() {
echo -e "${YELLOW}⚠️ $1${NC}"
}
# Check if running as root
if [[ $EUID -ne 0 ]]; then
print_error "This script must be run as root"
print_info "Please run: sudo bash $0"
exit 1
fi
# Function to detect PatchMon installations
detect_installations() {
local installations=()
if [ -d "/opt" ]; then
for dir in /opt/*/; do
local dirname=$(basename "$dir")
# Skip backup directories
if [[ "$dirname" =~ \.backup\. ]]; then
continue
fi
# Check if it's a PatchMon installation
if [ -f "$dir/backend/package.json" ] && grep -q "patchmon" "$dir/backend/package.json" 2>/dev/null; then
installations+=("$dirname")
fi
done
fi
echo "${installations[@]}"
}
# Function to select installation
select_installation() {
local installations=($(detect_installations))
if [ ${#installations[@]} -eq 0 ]; then
print_error "No PatchMon installations found in /opt"
exit 1
fi
if [ -n "$1" ]; then
# Use provided instance name
if [[ " ${installations[@]} " =~ " $1 " ]]; then
echo "$1"
return 0
else
print_error "Instance '$1' not found"
exit 1
fi
fi
print_info "Found ${#installations[@]} installation(s):"
echo ""
local i=1
declare -A install_map
for install in "${installations[@]}"; do
printf "%2d. %s\n" "$i" "$install"
install_map[$i]="$install"
i=$((i + 1))
done
echo ""
echo -n -e "${BLUE}Select installation number [1]: ${NC}"
read -r selection
selection=${selection:-1}
if [[ "$selection" =~ ^[0-9]+$ ]] && [ -n "${install_map[$selection]}" ]; then
echo "${install_map[$selection]}"
return 0
else
print_error "Invalid selection"
exit 1
fi
}
# Function to check and fix failed migrations
fix_failed_migrations() {
local db_name="$1"
local db_user="$2"
local db_pass="$3"
local db_host="${4:-localhost}"
print_info "Checking for failed migrations in database..."
# Query for failed migrations
local failed_migrations
failed_migrations=$(PGPASSWORD="$db_pass" psql -h "$db_host" -U "$db_user" -d "$db_name" -t -A -c \
"SELECT migration_name FROM _prisma_migrations WHERE finished_at IS NULL AND started_at IS NOT NULL;" 2>/dev/null || echo "")
if [ -z "$failed_migrations" ]; then
print_status "No failed migrations found"
return 0
fi
print_warning "Found failed migration(s):"
echo "$failed_migrations" | while read -r migration; do
[ -n "$migration" ] && print_warning " - $migration"
done
echo ""
print_info "What would you like to do?"
echo " 1. Clean and retry (delete failed records and re-run migration)"
echo " 2. Mark as completed (if schema changes are already applied)"
echo " 3. Show migration details only"
echo " 4. Cancel"
echo ""
echo -n -e "${BLUE}Select option [1]: ${NC}"
read -r option
option=${option:-1}
case $option in
1)
print_info "Cleaning failed migrations and preparing for retry..."
echo "$failed_migrations" | while read -r migration; do
if [ -n "$migration" ]; then
print_info "Processing: $migration"
# Mark as rolled back
PGPASSWORD="$db_pass" psql -h "$db_host" -U "$db_user" -d "$db_name" -c \
"UPDATE _prisma_migrations SET rolled_back_at = NOW() WHERE migration_name = '$migration' AND finished_at IS NULL;" >/dev/null 2>&1
# Delete the failed record
PGPASSWORD="$db_pass" psql -h "$db_host" -U "$db_user" -d "$db_name" -c \
"DELETE FROM _prisma_migrations WHERE migration_name = '$migration' AND finished_at IS NULL;" >/dev/null 2>&1
print_status "Cleared: $migration"
fi
done
print_status "Failed migrations cleared - ready to retry"
return 0
;;
2)
print_info "Marking migrations as completed..."
echo "$failed_migrations" | while read -r migration; do
if [ -n "$migration" ]; then
print_info "Marking as complete: $migration"
PGPASSWORD="$db_pass" psql -h "$db_host" -U "$db_user" -d "$db_name" -c \
"UPDATE _prisma_migrations SET finished_at = NOW(), logs = 'Manually resolved by fix-migrations.sh' WHERE migration_name = '$migration' AND finished_at IS NULL;" >/dev/null 2>&1
print_status "Marked complete: $migration"
fi
done
print_status "All migrations marked as completed"
return 0
;;
3)
print_info "Migration details:"
PGPASSWORD="$db_pass" psql -h "$db_host" -U "$db_user" -d "$db_name" -c \
"SELECT migration_name, started_at, finished_at, rolled_back_at, logs FROM _prisma_migrations WHERE finished_at IS NULL AND started_at IS NOT NULL;"
return 0
;;
4)
print_info "Cancelled"
return 1
;;
*)
print_error "Invalid option"
return 1
;;
esac
}
# Main script
main() {
echo -e "${BLUE}====================================================${NC}"
echo -e "${BLUE} PatchMon Migration Fixer${NC}"
echo -e "${BLUE}====================================================${NC}"
echo ""
# Select instance
instance_name=$(select_installation "$1")
instance_dir="/opt/$instance_name"
print_info "Selected instance: $instance_name"
print_info "Directory: $instance_dir"
echo ""
# Load .env to get database credentials
if [ ! -f "$instance_dir/backend/.env" ]; then
print_error "Cannot find .env file at $instance_dir/backend/.env"
exit 1
fi
# Source .env
set -a
source "$instance_dir/backend/.env"
set +a
# Parse DATABASE_URL
if [ -z "$DATABASE_URL" ]; then
print_error "DATABASE_URL not found in .env file"
exit 1
fi
DB_USER=$(echo "$DATABASE_URL" | sed -n 's|postgresql://\([^:]*\):.*|\1|p')
DB_PASS=$(echo "$DATABASE_URL" | sed -n 's|postgresql://[^:]*:\([^@]*\)@.*|\1|p')
DB_HOST=$(echo "$DATABASE_URL" | sed -n 's|.*@\([^:]*\):.*|\1|p')
DB_NAME=$(echo "$DATABASE_URL" | sed -n 's|.*/\([^?]*\).*|\1|p')
print_info "Database: $DB_NAME"
print_info "User: $DB_USER"
print_info "Host: $DB_HOST"
echo ""
# Test database connection
print_info "Testing database connection..."
if ! PGPASSWORD="$DB_PASS" psql -h "$DB_HOST" -U "$DB_USER" -d "$DB_NAME" -c "SELECT 1;" >/dev/null 2>&1; then
print_error "Cannot connect to database"
exit 1
fi
print_status "Database connection successful"
echo ""
# Check Prisma migration status
print_info "Checking Prisma migration status..."
cd "$instance_dir/backend"
echo ""
echo -e "${YELLOW}=== Prisma Migration Status ===${NC}"
npx prisma migrate status 2>&1 || true
echo -e "${YELLOW}==============================${NC}"
echo ""
# Check for failed migrations
fix_failed_migrations "$DB_NAME" "$DB_USER" "$DB_PASS" "$DB_HOST"
# Ask if user wants to run migrations now
echo ""
echo -n -e "${BLUE}Do you want to run 'npx prisma migrate deploy' now? [y/N]: ${NC}"
read -r run_migrate
if [[ "$run_migrate" =~ ^[Yy] ]]; then
print_info "Running migrations..."
cd "$instance_dir/backend"
if npx prisma migrate deploy; then
print_status "Migrations completed successfully!"
else
print_error "Migration failed"
print_info "You may need to run this script again or investigate further"
exit 1
fi
else
print_info "Skipped migration deployment"
print_info "Run manually: cd $instance_dir/backend && npx prisma migrate deploy"
fi
echo ""
print_status "Done!"
}
# Run main function
main "$@"