#!/bin/bash # ============================================ # Sistema de Flotillas - Script de Restauracion # ============================================ # Restaura backups de base de datos y configuracion # # Uso: ./restore.sh [--db backup.sql.gz] [--config config.tar.gz] [--list] # # Opciones: # --db FILE Restaurar backup de base de datos # --config FILE Restaurar backup de configuracion # --list Listar backups disponibles # --latest Restaurar el backup mas reciente # --date YYYYMMDD Restaurar backup de fecha especifica # ============================================ set -e set -o pipefail # --------------------------------------------- # Colores # --------------------------------------------- RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # --------------------------------------------- # Variables # --------------------------------------------- INSTALL_DIR="${INSTALL_DIR:-/opt/flotillas}" BACKUP_DIR="${BACKUP_DIR:-/var/backups/flotillas}" # Cargar variables de entorno if [[ -f "$INSTALL_DIR/.env" ]]; then export $(grep -v '^#' "$INSTALL_DIR/.env" | xargs) fi # Base de datos DB_HOST="${POSTGRES_HOST:-localhost}" DB_PORT="${POSTGRES_PORT:-5432}" DB_NAME="${POSTGRES_DB:-flotillas}" DB_USER="${POSTGRES_USER:-flotillas}" DB_PASSWORD="${POSTGRES_PASSWORD:-}" # Opciones DB_BACKUP="" CONFIG_BACKUP="" LIST_ONLY=false USE_LATEST=false RESTORE_DATE="" # --------------------------------------------- # Funciones # --------------------------------------------- log_info() { echo -e "${BLUE}[INFO]${NC} $1" } log_success() { echo -e "${GREEN}[OK]${NC} $1" } log_warn() { echo -e "${YELLOW}[WARN]${NC} $1" } log_error() { echo -e "${RED}[ERROR]${NC} $1" } # --------------------------------------------- # Parsear argumentos # --------------------------------------------- parse_args() { while [[ $# -gt 0 ]]; do case $1 in --db) DB_BACKUP="$2" shift 2 ;; --config) CONFIG_BACKUP="$2" shift 2 ;; --list) LIST_ONLY=true shift ;; --latest) USE_LATEST=true shift ;; --date) RESTORE_DATE="$2" shift 2 ;; --help|-h) show_help exit 0 ;; *) log_error "Opcion desconocida: $1" exit 1 ;; esac done } show_help() { echo "Sistema de Flotillas - Restauracion de Backup" echo "" echo "Uso: $0 [opciones]" echo "" echo "Opciones:" echo " --db FILE Restaurar backup de base de datos" echo " --config FILE Restaurar backup de configuracion" echo " --list Listar backups disponibles" echo " --latest Restaurar el backup mas reciente" echo " --date YYYYMMDD Restaurar backup de fecha especifica" echo "" echo "Ejemplos:" echo " $0 --list" echo " $0 --latest" echo " $0 --date 20240115" echo " $0 --db /var/backups/flotillas/daily/flotillas_20240115_030000_db.sql.gz" } # --------------------------------------------- # Listar backups disponibles # --------------------------------------------- list_backups() { echo "" echo -e "${BLUE}========================================${NC}" echo -e "${BLUE} BACKUPS DISPONIBLES${NC}" echo -e "${BLUE}========================================${NC}" echo "" if [[ ! -d "$BACKUP_DIR/daily" ]]; then log_warn "No hay backups disponibles" return fi echo -e "${GREEN}Base de Datos:${NC}" echo "---------------------------------------------" printf "%-40s %10s %s\n" "Archivo" "Tamanio" "Fecha" echo "---------------------------------------------" for file in $(ls -t "$BACKUP_DIR/daily"/*_db.sql.gz 2>/dev/null); do local name=$(basename "$file") local size=$(du -h "$file" | cut -f1) local date=$(stat -c %y "$file" | cut -d' ' -f1) printf "%-40s %10s %s\n" "$name" "$size" "$date" done echo "" echo -e "${GREEN}Configuracion:${NC}" echo "---------------------------------------------" for file in $(ls -t "$BACKUP_DIR/daily"/*_config.tar.gz 2>/dev/null); do local name=$(basename "$file") local size=$(du -h "$file" | cut -f1) local date=$(stat -c %y "$file" | cut -d' ' -f1) printf "%-40s %10s %s\n" "$name" "$size" "$date" done echo "" echo -e "${GREEN}Backups Completos:${NC}" echo "---------------------------------------------" for file in $(ls -t "$BACKUP_DIR/daily"/*_full.tar.gz 2>/dev/null); do local name=$(basename "$file") local size=$(du -h "$file" | cut -f1) local date=$(stat -c %y "$file" | cut -d' ' -f1) printf "%-40s %10s %s\n" "$name" "$size" "$date" done echo "" } # --------------------------------------------- # Encontrar backup mas reciente # --------------------------------------------- find_latest_backup() { local type="$1" # db, config, full local pattern="*_${type}.*gz" local latest=$(ls -t "$BACKUP_DIR/daily"/$pattern 2>/dev/null | head -1) if [[ -z "$latest" ]]; then log_error "No se encontro backup de tipo: $type" return 1 fi echo "$latest" } # --------------------------------------------- # Encontrar backup por fecha # --------------------------------------------- find_backup_by_date() { local type="$1" local date="$2" local pattern="flotillas_${date}*_${type}.*gz" local found=$(ls -t "$BACKUP_DIR/daily"/$pattern 2>/dev/null | head -1) if [[ -z "$found" ]]; then log_error "No se encontro backup de tipo '$type' para fecha: $date" return 1 fi echo "$found" } # --------------------------------------------- # Detener servicios # --------------------------------------------- stop_services() { log_info "Deteniendo servicios..." systemctl stop flotillas-api 2>/dev/null || true systemctl stop flotillas-web 2>/dev/null || true # Esperar a que se detengan sleep 2 log_success "Servicios detenidos" } # --------------------------------------------- # Iniciar servicios # --------------------------------------------- start_services() { log_info "Iniciando servicios..." systemctl start flotillas-api 2>/dev/null || true systemctl start flotillas-web 2>/dev/null || true log_success "Servicios iniciados" } # --------------------------------------------- # Restaurar base de datos # --------------------------------------------- restore_database() { local backup_file="$1" if [[ ! -f "$backup_file" ]]; then log_error "Archivo no encontrado: $backup_file" return 1 fi log_info "Restaurando base de datos desde: $(basename "$backup_file")" # Verificar integridad log_info "Verificando integridad del archivo..." if ! gzip -t "$backup_file" 2>/dev/null; then log_error "El archivo de backup esta corrupto" return 1 fi # Confirmar echo "" echo -e "${YELLOW}ADVERTENCIA: Esto reemplazara TODOS los datos actuales${NC}" echo -e "${YELLOW}Base de datos: ${DB_NAME}${NC}" echo "" read -p "Continuar? (escribir 'SI' para confirmar): " confirm if [[ "$confirm" != "SI" ]]; then log_warn "Restauracion cancelada" return 1 fi stop_services # Exportar password export PGPASSWORD="$DB_PASSWORD" # Crear backup de seguridad antes de restaurar log_info "Creando backup de seguridad..." local safety_backup="$BACKUP_DIR/temp/pre_restore_$(date +%Y%m%d_%H%M%S).sql.gz" mkdir -p "$BACKUP_DIR/temp" pg_dump -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" 2>/dev/null | gzip > "$safety_backup" || true log_info "Backup de seguridad: $safety_backup" # Terminar conexiones activas log_info "Cerrando conexiones activas..." psql -h "$DB_HOST" -p "$DB_PORT" -U postgres -c " SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = '${DB_NAME}' AND pid <> pg_backend_pid(); " 2>/dev/null || true # Recrear base de datos log_info "Recreando base de datos..." # Eliminar y recrear BD psql -h "$DB_HOST" -p "$DB_PORT" -U postgres </dev/null; then log_error "El archivo de backup esta corrupto" return 1 fi # Confirmar echo "" echo -e "${YELLOW}ADVERTENCIA: Esto sobrescribira archivos de configuracion${NC}" echo "" read -p "Continuar? (y/N): " -n 1 -r echo if [[ ! $REPLY =~ ^[Yy]$ ]]; then log_warn "Restauracion cancelada" return 1 fi stop_services # Crear directorio temporal local temp_dir=$(mktemp -d) # Extraer log_info "Extrayendo configuracion..." tar -xzf "$backup_file" -C "$temp_dir" # Restaurar archivos log_info "Restaurando archivos..." # .env if [[ -f "$temp_dir$INSTALL_DIR/.env" ]]; then cp "$temp_dir$INSTALL_DIR/.env" "$INSTALL_DIR/.env" log_info "Restaurado: .env" fi # Traccar if [[ -f "$temp_dir/opt/traccar/conf/traccar.xml" ]]; then cp "$temp_dir/opt/traccar/conf/traccar.xml" /opt/traccar/conf/traccar.xml log_info "Restaurado: traccar.xml" fi # MediaMTX if [[ -f "$temp_dir/opt/mediamtx/mediamtx.yml" ]]; then cp "$temp_dir/opt/mediamtx/mediamtx.yml" /opt/mediamtx/mediamtx.yml log_info "Restaurado: mediamtx.yml" fi # Servicios systemd for service in $temp_dir/etc/systemd/system/flotillas-*.service; do if [[ -f "$service" ]]; then cp "$service" /etc/systemd/system/ log_info "Restaurado: $(basename "$service")" fi done # Recargar systemd systemctl daemon-reload # Limpiar rm -rf "$temp_dir" log_success "Configuracion restaurada" start_services } # --------------------------------------------- # Restaurar backup completo # --------------------------------------------- restore_full() { local backup_file="$1" if [[ ! -f "$backup_file" ]]; then log_error "Archivo no encontrado: $backup_file" return 1 fi log_info "Restaurando backup completo desde: $(basename "$backup_file")" echo "" echo -e "${RED}ADVERTENCIA: Esto reemplazara TODO el directorio de la aplicacion${NC}" echo "" read -p "Continuar? (escribir 'SI' para confirmar): " confirm if [[ "$confirm" != "SI" ]]; then log_warn "Restauracion cancelada" return 1 fi stop_services # Backup actual log_info "Respaldando instalacion actual..." local current_backup="$BACKUP_DIR/temp/pre_restore_full_$(date +%Y%m%d_%H%M%S).tar.gz" tar -czf "$current_backup" "$INSTALL_DIR" 2>/dev/null || true # Extraer log_info "Extrayendo backup completo..." tar -xzf "$backup_file" -C / log_success "Backup completo restaurado" # Reinstalar dependencias log_info "Reinstalando dependencias..." cd "$INSTALL_DIR/backend" source venv/bin/activate pip install -r requirements.txt -q 2>/dev/null || pip install -e . -q deactivate cd "$INSTALL_DIR/frontend" npm install 2>/dev/null || true start_services } # --------------------------------------------- # Main # --------------------------------------------- main() { parse_args "$@" # Solo listar if [[ "$LIST_ONLY" == "true" ]]; then list_backups exit 0 fi # Modo interactivo si no se especificaron opciones if [[ -z "$DB_BACKUP" ]] && [[ -z "$CONFIG_BACKUP" ]] && [[ "$USE_LATEST" != "true" ]] && [[ -z "$RESTORE_DATE" ]]; then echo "" echo "Sistema de Flotillas - Restauracion" echo "====================================" echo "" echo "Selecciona una opcion:" echo " 1) Restaurar backup mas reciente (DB + Config)" echo " 2) Restaurar solo base de datos" echo " 3) Restaurar solo configuracion" echo " 4) Listar backups disponibles" echo " 5) Cancelar" echo "" read -p "Opcion: " option case $option in 1) USE_LATEST=true ;; 2) list_backups echo "" read -p "Ingresa ruta del archivo de BD: " DB_BACKUP ;; 3) list_backups echo "" read -p "Ingresa ruta del archivo de config: " CONFIG_BACKUP ;; 4) list_backups exit 0 ;; *) echo "Cancelado" exit 0 ;; esac fi # Restaurar por fecha if [[ -n "$RESTORE_DATE" ]]; then log_info "Buscando backups para fecha: $RESTORE_DATE" DB_BACKUP=$(find_backup_by_date "db.sql" "$RESTORE_DATE") || exit 1 CONFIG_BACKUP=$(find_backup_by_date "config.tar" "$RESTORE_DATE") || true fi # Restaurar mas reciente if [[ "$USE_LATEST" == "true" ]]; then log_info "Buscando backups mas recientes..." DB_BACKUP=$(find_latest_backup "db.sql") || exit 1 CONFIG_BACKUP=$(find_latest_backup "config.tar") || true fi # Ejecutar restauraciones if [[ -n "$DB_BACKUP" ]]; then restore_database "$DB_BACKUP" fi if [[ -n "$CONFIG_BACKUP" ]]; then restore_config "$CONFIG_BACKUP" fi echo "" log_success "==========================================" log_success "RESTAURACION COMPLETADA" log_success "==========================================" echo "" echo "Verifica que los servicios esten funcionando:" echo " systemctl status flotillas-api" echo " systemctl status flotillas-web" echo "" } # Ejecutar main "$@"