HEX
Server: Apache/2.4.58 (Ubuntu)
System: Linux ip-172-26-14-113 7.0.0-1009-aws #9~24.04.1-Ubuntu SMP PREEMPT Mon Jul 13 22:43:41 UTC 2026 x86_64
User: root (0)
PHP: 7.4.33
Disabled: pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare,
Upload Files
File: //scripts/backups_local.sh
#!/bin/bash

# =============================================
# CONFIGURACIÓN
# =============================================

# Directorios locales
HTDOCS_DIR="/var/www/html/"
BACKUP_BASE_DIR="/backups"
LOG_DIR="$HOME/backups_logs"

# Configuración MySQL
MYSQL_USER="dolibarr"
MYSQL_PASS="LXfLpdkhsdsdd334H1$"
MYSQL_HOST="localhost"



# Notificaciones
EMAIL="ame@unboxerp.com"

# Días a mantener (basado en fecha, no en cantidad de archivos)
MAX_BACKUP_DAYS_DB=1      # Mantener backups de bases de datos de los últimos 7 días
MAX_BACKUP_DAYS_FILES=8   # Mantener backups de archivos de los últimos 3 días

# URL para POST
POST_URL="http://mstoluca.com:5678/webhook/4e43236c-fb54-46f1-9f51-eb3a19ad9bff"

# =============================================
# INICIALIZACIÓN
# =============================================

mkdir -p "${BACKUP_BASE_DIR}/files" "${BACKUP_BASE_DIR}/sql" "${LOG_DIR}"

TIMESTAMP=$(date +"%Y%m%d-%H%M%S")
DATE_DIR=$(date +"%Y/%m")
LOG_FILE="${LOG_DIR}/backup_${TIMESTAMP}.log"
LOCAL_IP=$(hostname -I | awk '{print $1}')

# Iniciar log
{
    echo "============================================="
    echo "INICIO DE BACKUP: $(date '+%Y-%m-%d %H:%M:%S')"
    echo "Host: $(hostname)"
    echo "IP Local: $LOCAL_IP"
    echo "Tipo de backup: $1"
    echo "============================================="
} > "${LOG_FILE}"

log() {
    local level=$1
    local message=$2
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] [${level}] ${message}" | tee -a "${LOG_FILE}"
}

enviar_post() {
    local archivo=$1
    local origen=$2
    local destino=$3
    local tamanio=$4
    local status=$5
    
    # Leer el contenido completo del archivo de log y codificarlo para URL manteniendo saltos de línea
    local log_content=$(cat "${LOG_FILE}" 2>/dev/null | sed 's/"/\\"/g' | sed ':a;N;$!ba;s/\n/%0A/g')
    
    log "INFO" "Enviando POST - Archivo: $archivo - Tamaño: '$tamanio' - Status: $status"
    
    curl -s -X POST "$POST_URL" \
        -d "fecha=$(date '+%Y-%m-%d %H:%M:%S')" \
        -d "archivo=$archivo" \
        -d "origen=$origen" \
        -d "destino=$destino" \
        -d "ip_origen=$LOCAL_IP" \
        -d "ip_destino=" \
        -d "tamanio=$tamanio" \
        -d "status=$status" \
        -d "log=${log_content}" > /dev/null 2>&1 || {
            log "ERROR" "Fallo al enviar POST"
        }
}

# =============================================
# FUNCIONES DE LIMPIEZA
# =============================================

limpiar_backups_antiguos_sql() {
    local dir="${BACKUP_BASE_DIR}/sql"
    local dias=$1
    
    # Calcular la fecha límite (hace X días)
    local fecha_limite=$(date -d "${dias} days ago" +%Y%m%d)
    
    log "INFO" "🧹 Limpiando backups de SQL (manteniendo últimos ${dias} días)"
    log "INFO" "  Fecha límite: $fecha_limite"
    
    # Obtener lista de bases de datos únicas (el nombre antes del primer _)
    for db_prefix in $(find "$dir" -name "*.sql.gz" 2>/dev/null | sed 's/_.*//' | sort -u); do
        local db_name=$(basename "$db_prefix")
        log "INFO" "  Procesando base de datos: $db_name"
        
        # Listar backups de esta base y eliminar los anteriores a la fecha límite
        find "$dir" -name "${db_name}_*.sql.gz" 2>/dev/null | while read -r archivo; do
            # Extraer fecha del nombre del archivo (formato: nombre_YYYY-MM-DD_HHMMSS.sql.gz)
            local fecha_archivo=$(basename "$archivo" | grep -oP '\d{4}-\d{2}-\d{2}' | head -1 | tr -d '-')
            
            if [ -n "$fecha_archivo" ]; then
                if [ "$fecha_archivo" -lt "$fecha_limite" ]; then
                    local file_size=$(du -h "$archivo" 2>/dev/null | awk '{print $1}')
                    if [ -z "$file_size" ]; then
                        file_size="0"
                    fi
                    if rm "$archivo" 2>/dev/null; then
                        log "INFO" "    ✓ Eliminado: $(basename "$archivo") (fecha: $fecha_archivo)"
                        # Enviar POST con status 9 indicando eliminación
                        enviar_post "$(basename "$archivo")" "$archivo" "ELIMINADO" "$file_size" "9"
                    fi
                fi
            fi
        done
    done
}

limpiar_backups_antiguos_files() {
    local dir="${BACKUP_BASE_DIR}/files"
    local dias=$1
    
    # Calcular la fecha límite (hace X días)
    local fecha_limite=$(date -d "${dias} days ago" +%Y%m%d)
    
    log "INFO" "🧹 Limpiando backups de FILES (manteniendo últimos ${dias} días)"
    log "INFO" "  Fecha límite: $fecha_limite"
    
    # Eliminar archivos ZIP anteriores a la fecha límite
    find "$dir" -name "*.zip" 2>/dev/null | while read -r archivo; do
        # Extraer fecha del nombre del archivo (formato: nombre_YYYY-MM-DD_HHMMSS.zip)
        local fecha_archivo=$(basename "$archivo" | grep -oP '\d{4}-\d{2}-\d{2}' | head -1 | tr -d '-')
        
        if [ -n "$fecha_archivo" ]; then
            if [ "$fecha_archivo" -lt "$fecha_limite" ]; then
                local file_size=$(du -h "$archivo" 2>/dev/null | awk '{print $1}')
                if [ -z "$file_size" ]; then
                    file_size="0"
                fi
                if rm "$archivo" 2>/dev/null; then
                    log "INFO" "  ✓ Eliminado: $(basename "$archivo") (fecha: $fecha_archivo)"
                    # Enviar POST con status 9 indicando eliminación
                    enviar_post "$(basename "$archivo")" "$archivo" "ELIMINADO" "$file_size" "9"
                fi
            fi
        fi
    done
}

# =============================================
# BACKUP DE BASES DE DATOS
# =============================================

backup_databases() {
    log "INFO" "=== INICIANDO BACKUP DE BASES DE DATOS ==="

    local db_backup_dir="${BACKUP_BASE_DIR}/sql/${DATE_DIR}"
    mkdir -p "${db_backup_dir}"

    # Obtener lista de bases de datos
    DATABASES=$(mysql -h "${MYSQL_HOST}" -u "${MYSQL_USER}" -p"${MYSQL_PASS}" -e "SHOW DATABASES;" 2>/dev/null | grep -Ev "(Database|information_schema|performance_schema|mysql|sys)")
    
    if [ -z "$DATABASES" ]; then
        log "ERROR" "No se pudo obtener lista de bases de datos"
        log "INFO" "Verifica usuario y contraseña de MySQL"
        enviar_post "ERROR_MYSQL" "MySQL" "N/A" "0" "1"
        return 1
    fi

    local count=0
    for DB in $DATABASES; do
        count=$((count + 1))
        # Formato: nombre_base_fecha(año-mes-dia)_hora.sql.gz
        local backup_file="${db_backup_dir}/${DB}_$(date +'%Y-%m-%d')_$(date +'%H%M%S').sql.gz"
        log "INFO" "📦 [$count] Base de datos: ${DB}"

        if mysqldump -h "${MYSQL_HOST}" -u "${MYSQL_USER}" -p"${MYSQL_PASS}" --single-transaction --quick "$DB" 2>/dev/null | gzip > "$backup_file"; then
            # Verificar que el archivo existe y obtener tamaño
            if [ -f "$backup_file" ]; then
                local file_size=$(du -h "$backup_file" 2>/dev/null | awk '{print $1}')
                # Si el tamaño está vacío, asignar "0"
                if [ -z "$file_size" ]; then
                    file_size="0"
                fi
            else
                local file_size="0"
            fi
            log "INFO" "  ✓ Backup creado: $(basename "$backup_file") (${file_size})"
            enviar_post "$(basename "$backup_file")" "$backup_file" "${BACKUP_BASE_DIR}/sql/" "$file_size" "0"
        else
            log "ERROR" "  ✗ Fallo al crear backup de ${DB}"
            enviar_post "${DB}_$(date +'%Y-%m-%d')_$(date +'%H%M%S').sql.gz" "MySQL" "N/A" "0" "1"
        fi
    done

    # Limpiar backups antiguos de SQL usando los días configurados
    limpiar_backups_antiguos_sql "$MAX_BACKUP_DAYS_DB"
    log "INFO" "=== BACKUP DE BASES DE DATOS COMPLETADO ($count bases) ==="
}

# =============================================
# BACKUP DE ARCHIVOS
# =============================================

backup_files() {
    log "INFO" "=== INICIANDO BACKUP DE ARCHIVOS ==="

    local files_backup_dir="${BACKUP_BASE_DIR}/files/${DATE_DIR}"
    mkdir -p "${files_backup_dir}"

    if [ ! -d "$HTDOCS_DIR" ]; then
        log "ERROR" "Directorio no encontrado: ${HTDOCS_DIR}"
        enviar_post "$(basename "$HTDOCS_DIR")_$(date +'%Y-%m-%d')_$(date +'%H%M%S').zip" "$HTDOCS_DIR" "N/A" "0" "1"
        return 1
    fi

    local folder_name=$(basename "$HTDOCS_DIR")
    # Formato: nombre_carpeta_fecha(año-mes-dia)_hora.zip
    local backup_file="${files_backup_dir}/${folder_name}_$(date +'%Y-%m-%d')_$(date +'%H%M%S').zip"
    
    log "INFO" "📦 Comprimiendo carpeta: ${folder_name}"
    log "INFO" "Esto puede tomar varios minutos dependiendo del tamaño..."

    # Obtener tamaño aproximado antes de comprimir
    local folder_size=$(du -sh "$HTDOCS_DIR" 2>/dev/null | awk '{print $1}')
    log "INFO" "Tamaño aproximado: $folder_size"

    # Comprimir
    if cd "$(dirname "$HTDOCS_DIR")" && zip -rq "$backup_file" "$(basename "$HTDOCS_DIR")"; then
        # Verificar que el archivo existe y obtener tamaño
        if [ -f "$backup_file" ]; then
            local file_size=$(du -h "$backup_file" 2>/dev/null | awk '{print $1}')
            # Si el tamaño está vacío, asignar "0"
            if [ -z "$file_size" ]; then
                file_size="0"
            fi
        else
            local file_size="0"
        fi
        log "INFO" "  ✓ Backup creado: $(basename "$backup_file") (${file_size})"
        enviar_post "$(basename "$backup_file")" "$HTDOCS_DIR" "${BACKUP_BASE_DIR}/files/" "$file_size" "0"
    else
        log "ERROR" "  ✗ Fallo al comprimir carpeta"
        enviar_post "${folder_name}_$(date +'%Y-%m-%d')_$(date +'%H%M%S').zip" "$HTDOCS_DIR" "N/A" "0" "1"
    fi

    # Limpiar backups antiguos de FILES usando los días configurados
    limpiar_backups_antiguos_files "$MAX_BACKUP_DAYS_FILES"
    log "INFO" "=== BACKUP DE ARCHIVOS COMPLETADO ==="
}

# =============================================
# VERIFICACIONES INICIALES
# =============================================

verificar_comandos() {
    log "INFO" "🔍 Verificando comandos necesarios..."
    local missing=0
    for cmd in mysql mysqldump zip curl; do
        if ! command -v "$cmd" &>/dev/null; then
            log "ERROR" "  ✗ Comando '$cmd' no encontrado"
            missing=1
        else
            log "INFO" "  ✓ $cmd encontrado"
        fi
    done
    
    if [ $missing -eq 1 ]; then
        log "ERROR" "Faltan comandos necesarios. Abortando."
        enviar_post "ERROR_COMANDOS" "Sistema" "N/A" "0" "1"
        exit 1
    fi
}

verificar_mysql() {
    log "INFO" "🔍 Verificando conexión MySQL..."
    if mysql -h "${MYSQL_HOST}" -u "${MYSQL_USER}" -p"${MYSQL_PASS}" -e "SELECT 1" &>/dev/null; then
        log "INFO" "  ✓ Conexión MySQL establecida"
        return 0
    else
        log "ERROR" "  ✗ No se puede conectar a MySQL"
        echo ""
        echo "=================================================="
        echo "ERROR DE CONEXIÓN MYSQL"
        echo "=================================================="
        echo "Verifica que:"
        echo "1. MySQL está corriendo"
        echo "2. Usuario y contraseña son correctos"
        echo ""
        echo "Configuración actual:"
        echo "  Host: ${MYSQL_HOST}"
        echo "  User: ${MYSQL_USER}"
        echo "  Pass: [oculto]"
        echo "=================================================="
        enviar_post "ERROR_MYSQL" "MySQL" "N/A" "0" "1"
        exit 1
    fi
}

# =============================================
# EJECUCIÓN PRINCIPAL
# =============================================

# Mostrar banner
echo ""
echo "=================================================="
echo "   SCRIPT DE BACKUP AUTOMÁTICO (LOCAL)"
echo "=================================================="
echo ""

# Verificar comandos y MySQL
verificar_comandos
verificar_mysql

# Procesar argumento
case "$1" in
    "db")
        backup_databases
        ;;
    "files")
        backup_files
        ;;
    "full")
        backup_databases
        backup_files
        ;;
    *)
        echo "Uso: $0 [db|files|full]"
        echo ""
        echo "Opciones:"
        echo "  db     - Solo backup de bases de datos"
        echo "  files  - Solo backup de archivos"
        echo "  full   - Backup completo (bases de datos + archivos)"
        echo ""
        echo "Ejemplo:"
        echo "  $0 full"
        exit 1
        ;;
esac

# Finalización
log "INFO" "============================================="
log "INFO" "✅ BACKUP COMPLETADO: $(date '+%Y-%m-%d %H:%M:%S')"
log "INFO" "📝 Log guardado en: ${LOG_FILE}"
log "INFO" "============================================="

# Mostrar resumen final
echo ""
echo "=================================================="
echo "RESUMEN FINAL"
echo "=================================================="
echo "Backups guardados en: ${BACKUP_BASE_DIR}"
echo "  - Archivos: ${BACKUP_BASE_DIR}/files/ (mantiene ${MAX_BACKUP_DAYS_FILES} días)"
echo "  - SQL: ${BACKUP_BASE_DIR}/sql/ (mantiene ${MAX_BACKUP_DAYS_DB} días)"
echo "Log completo: ${LOG_FILE}"
echo "=================================================="

# Enviar correo si está disponible
if command -v mail &>/dev/null; then
    tail -n 30 "${LOG_FILE}" | mail -s "Backup Completado - $(date '+%Y-%m-%d')" "${EMAIL}"
    log "INFO" "Correo enviado a ${EMAIL}"
fi

exit 0