#!/bin/bash set -e # Database Export Script for MotoVaultPro # Exports PostgreSQL database for deployment migration # Usage: ./scripts/export-database.sh [options] # 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 SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" EXPORT_DIR="${PROJECT_ROOT}/database-exports" TIMESTAMP=$(date +%Y%m%d_%H%M%S) # Environment-based container names CONTAINER_NAME="" # Will be set based on environment selection ENVIRONMENT="" # production or staging # Default values EXPORT_FORMAT="sql" COMPRESS=true INCLUDE_SCHEMA=true INCLUDE_DATA=true EXPORT_NAME="" # Will be set after environment selection # Function to print colored output print_info() { echo -e "${GREEN}[INFO]${NC} $1" } print_warn() { echo -e "${YELLOW}[WARN]${NC} $1" } print_error() { echo -e "${RED}[ERROR]${NC} $1" } print_env() { echo -e "${BLUE}[ENV]${NC} $1" } # Function to select environment select_environment() { if [ -n "$ENVIRONMENT" ]; then # Environment already set via command line return fi echo "" echo -e "${BLUE}========================================${NC}" echo -e "${BLUE} MotoVaultPro Database Export${NC}" echo -e "${BLUE}========================================${NC}" echo "" echo "Select environment to export from:" echo "" echo " 1) Production (mvp-postgres)" echo " 2) Staging (mvp-postgres-staging)" echo "" read -p "Enter choice [1-2]: " ENV_CHOICE case $ENV_CHOICE in 1) ENVIRONMENT="production" CONTAINER_NAME="mvp-postgres" ;; 2) ENVIRONMENT="staging" CONTAINER_NAME="mvp-postgres-staging" ;; *) print_error "Invalid choice. Please enter 1 or 2." exit 1 ;; esac echo "" print_env "Selected environment: ${ENVIRONMENT}" print_env "Container: ${CONTAINER_NAME}" echo "" } # Function to set container name based on environment set_container_from_environment() { case $ENVIRONMENT in production|prod) ENVIRONMENT="production" CONTAINER_NAME="mvp-postgres" ;; staging|stage) ENVIRONMENT="staging" CONTAINER_NAME="mvp-postgres-staging" ;; *) print_error "Invalid environment: $ENVIRONMENT" print_error "Valid options: production, staging" exit 1 ;; esac } # Function to show usage show_usage() { cat << EOF Database Export Script for MotoVaultPro Usage: $0 [options] Options: -h, --help Show this help message -e, --env ENV Environment: production or staging (prompts if not specified) -f, --format FORMAT Export format: sql, custom, directory (default: sql) -o, --output NAME Custom export filename (without extension) -n, --no-compress Don't compress the export --schema-only Export schema only (no data) --data-only Export data only (no schema) --exclude-table TABLE Exclude specific table(s) (can be used multiple times) --include-table TABLE Include only specific table(s) (can be used multiple times) -c, --container NAME Override container name (ignores environment selection) Environments: production (prod) Uses container: mvp-postgres staging (stage) Uses container: mvp-postgres-staging Examples: # Interactive environment selection $0 # Export from production $0 --env production # Export from staging $0 --env staging # Schema only export from production $0 --env prod --schema-only # Export specific tables from staging $0 -e staging --include-table vehicles --include-table fuel_logs # Custom format for pg_restore $0 --env production --format custom --output my_backup EOF exit 0 } # Parse command line arguments EXCLUDE_TABLES=() INCLUDE_TABLES=() CONTAINER_OVERRIDE="" while [[ $# -gt 0 ]]; do case $1 in -h|--help) show_usage ;; -e|--env) ENVIRONMENT="$2" shift 2 ;; -f|--format) EXPORT_FORMAT="$2" shift 2 ;; -o|--output) EXPORT_NAME="$2" shift 2 ;; -n|--no-compress) COMPRESS=false shift ;; --schema-only) INCLUDE_DATA=false shift ;; --data-only) INCLUDE_SCHEMA=false shift ;; --exclude-table) EXCLUDE_TABLES+=("$2") shift 2 ;; --include-table) INCLUDE_TABLES+=("$2") shift 2 ;; -c|--container) CONTAINER_OVERRIDE="$2" shift 2 ;; *) print_error "Unknown option: $1" show_usage ;; esac done # Handle environment selection if [ -n "$CONTAINER_OVERRIDE" ]; then # Container explicitly specified, use it directly CONTAINER_NAME="$CONTAINER_OVERRIDE" ENVIRONMENT="custom" print_info "Using custom container: $CONTAINER_NAME" elif [ -n "$ENVIRONMENT" ]; then # Environment specified via command line set_container_from_environment else # No environment specified, prompt user select_environment fi # Set default export name with environment prefix if [ -z "$EXPORT_NAME" ]; then EXPORT_NAME="motovaultpro_${ENVIRONMENT}_export_${TIMESTAMP}" fi # Create export directory if it doesn't exist mkdir -p "${EXPORT_DIR}" # Check if container is running print_info "Checking if PostgreSQL container is running..." if ! docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then print_error "PostgreSQL container '${CONTAINER_NAME}' is not running" print_info "Start it with: docker compose up -d mvp-postgres" exit 1 fi # Build pg_dump options PG_DUMP_OPTS="" # Schema/Data options if [ "$INCLUDE_SCHEMA" = false ]; then PG_DUMP_OPTS="$PG_DUMP_OPTS --data-only" fi if [ "$INCLUDE_DATA" = false ]; then PG_DUMP_OPTS="$PG_DUMP_OPTS --schema-only" fi # Format options case $EXPORT_FORMAT in sql) PG_DUMP_OPTS="$PG_DUMP_OPTS --format=plain" EXPORT_EXT="sql" ;; custom) PG_DUMP_OPTS="$PG_DUMP_OPTS --format=custom" EXPORT_EXT="dump" COMPRESS=false # Custom format is already compressed ;; directory) PG_DUMP_OPTS="$PG_DUMP_OPTS --format=directory" EXPORT_EXT="dir" COMPRESS=false # Directory format doesn't need compression ;; *) print_error "Invalid format: $EXPORT_FORMAT" exit 1 ;; esac # Exclude tables for table in "${EXCLUDE_TABLES[@]}"; do PG_DUMP_OPTS="$PG_DUMP_OPTS --exclude-table=$table" done # Include tables for table in "${INCLUDE_TABLES[@]}"; do PG_DUMP_OPTS="$PG_DUMP_OPTS --table=$table" done # Set output file path if [ "$EXPORT_FORMAT" = "directory" ]; then OUTPUT_PATH="${EXPORT_DIR}/${EXPORT_NAME}.${EXPORT_EXT}" mkdir -p "${OUTPUT_PATH}" else OUTPUT_PATH="${EXPORT_DIR}/${EXPORT_NAME}.${EXPORT_EXT}" fi # Export database print_info "Starting database export..." print_info "Environment: ${ENVIRONMENT}" print_info "Container: ${CONTAINER_NAME}" print_info "Format: ${EXPORT_FORMAT}" print_info "Output: ${OUTPUT_PATH}" if [ "$EXPORT_FORMAT" = "directory" ]; then # For directory format, we need to export inside container then copy out docker exec "$CONTAINER_NAME" sh -c "pg_dump -U postgres -d motovaultpro $PG_DUMP_OPTS -f /tmp/export_dir" || { print_error "Database export failed" exit 1 } docker cp "${CONTAINER_NAME}:/tmp/export_dir/." "${OUTPUT_PATH}/" docker exec "$CONTAINER_NAME" rm -rf /tmp/export_dir else # For SQL and custom formats docker exec "$CONTAINER_NAME" pg_dump -U postgres -d motovaultpro $PG_DUMP_OPTS > "${OUTPUT_PATH}" || { print_error "Database export failed" exit 1 } fi print_info "Database exported successfully" # Compress if requested and format is SQL if [ "$COMPRESS" = true ] && [ "$EXPORT_FORMAT" = "sql" ]; then print_info "Compressing export..." gzip "${OUTPUT_PATH}" OUTPUT_PATH="${OUTPUT_PATH}.gz" print_info "Compressed to: ${OUTPUT_PATH}" fi # Create metadata file METADATA_FILE="${EXPORT_DIR}/${EXPORT_NAME}_metadata.json" cat > "${METADATA_FILE}" << EOF { "export_timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)", "environment": "${ENVIRONMENT}", "container_name": "${CONTAINER_NAME}", "database_name": "motovaultpro", "export_format": "${EXPORT_FORMAT}", "compressed": ${COMPRESS}, "schema_included": ${INCLUDE_SCHEMA}, "data_included": ${INCLUDE_DATA}, "postgresql_version": "$(docker exec "$CONTAINER_NAME" psql -U postgres -t -c 'SELECT version();' | xargs)", "file_path": "${OUTPUT_PATH}", "file_size": "$(du -h "${OUTPUT_PATH}" | cut -f1)" } EOF print_info "Metadata saved to: ${METADATA_FILE}" # Generate import instructions IMPORT_INSTRUCTIONS="${EXPORT_DIR}/${EXPORT_NAME}_import_instructions.txt" cat > "${IMPORT_INSTRUCTIONS}" << EOF =========================================== MotoVaultPro Database Import Instructions =========================================== Export Details: - Export Date: $(date) - Format: ${EXPORT_FORMAT} - Compressed: ${COMPRESS} - File: ${OUTPUT_PATH} Import Instructions: -------------------- 1. Copy the export file to your target server: scp ${OUTPUT_PATH} user@server:/path/to/import/ EOF if [ "$EXPORT_FORMAT" = "sql" ]; then if [ "$COMPRESS" = true ]; then cat >> "${IMPORT_INSTRUCTIONS}" << EOF 2. Import the database (compressed SQL): # Using Docker: gunzip -c /path/to/import/$(basename "${OUTPUT_PATH}") | docker exec -i mvp-postgres psql -U postgres -d motovaultpro # Direct PostgreSQL: gunzip -c /path/to/import/$(basename "${OUTPUT_PATH}") | psql -U postgres -d motovaultpro EOF else cat >> "${IMPORT_INSTRUCTIONS}" << EOF 2. Import the database (SQL): # Using Docker: docker exec -i mvp-postgres psql -U postgres -d motovaultpro < /path/to/import/$(basename "${OUTPUT_PATH}") # Direct PostgreSQL: psql -U postgres -d motovaultpro < /path/to/import/$(basename "${OUTPUT_PATH}") EOF fi elif [ "$EXPORT_FORMAT" = "custom" ]; then cat >> "${IMPORT_INSTRUCTIONS}" << EOF 2. Import the database (custom format): # Using Docker: docker cp /path/to/import/$(basename "${OUTPUT_PATH}") mvp-postgres:/tmp/restore.dump docker exec mvp-postgres pg_restore -U postgres -d motovaultpro -c /tmp/restore.dump # Direct PostgreSQL: pg_restore -U postgres -d motovaultpro -c /path/to/import/$(basename "${OUTPUT_PATH}") EOF elif [ "$EXPORT_FORMAT" = "directory" ]; then cat >> "${IMPORT_INSTRUCTIONS}" << EOF 2. Import the database (directory format): # Using Docker: docker cp /path/to/import/$(basename "${OUTPUT_PATH}") mvp-postgres:/tmp/restore_dir docker exec mvp-postgres pg_restore -U postgres -d motovaultpro -c /tmp/restore_dir # Direct PostgreSQL: pg_restore -U postgres -d motovaultpro -c /path/to/import/$(basename "${OUTPUT_PATH}") EOF fi cat >> "${IMPORT_INSTRUCTIONS}" << EOF Notes: ------ - The -c flag drops existing database objects before recreating them - Ensure the target database exists before importing - For production imports, always test on a staging environment first - Consider creating a backup of the target database before importing Create target database: ----------------------- docker exec -i mvp-postgres psql -U postgres -c "CREATE DATABASE motovaultpro;" Or if database exists and you want to start fresh: -------------------------------------------------- docker exec -i mvp-postgres psql -U postgres -c "DROP DATABASE IF EXISTS motovaultpro;" docker exec -i mvp-postgres psql -U postgres -c "CREATE DATABASE motovaultpro;" EOF print_info "Import instructions saved to: ${IMPORT_INSTRUCTIONS}" # Summary echo "" print_info "===============================================" print_info "Database Export Complete!" print_info "===============================================" print_info "Environment: ${ENVIRONMENT}" print_info "Container: ${CONTAINER_NAME}" print_info "Export file: ${OUTPUT_PATH}" print_info "Metadata: ${METADATA_FILE}" print_info "Instructions: ${IMPORT_INSTRUCTIONS}" print_info "Size: $(du -h "${OUTPUT_PATH}" | cut -f1)" echo "" print_info "To import this database on another deployment:" print_info "1. Copy the export file to the target server" print_info "2. Follow the instructions in ${IMPORT_INSTRUCTIONS}" echo ""