#!/usr/bin/env ruby # frozen_string_literal: true # ========================================================== # ns8-bkps — Procesador de Backups Automatizado v6.0 # ---------------------------------------------------------- # Refactorización de dtic-BKPs v5.5.1 # AUTOR: Ricardo MONLA (rmonla@) # ========================================================== # # Uso: # ruby ns8-bkps.rb # Menú interactivo (auto-screen) # ruby ns8-bkps.rb T1 # Ejecutar tarea 1 directamente # ruby ns8-bkps.rb C1 # Ejecutar comando 1 directamente # ruby ns8-bkps.rb list # Listar tareas y comandos # ruby ns8-bkps.rb --help # Ayuda require 'shellwords' # ─── Rutas ─────────────────────────────────────────────────── APP_DIR = File.dirname(File.expand_path(__FILE__)) LIB_DIR = File.join(APP_DIR, 'lib') require_relative 'lib/logger' require_relative 'lib/config' require_relative 'lib/proc_proxmox' require_relative 'lib/proc_xen' require_relative 'lib/sync_rclone' require_relative 'lib/menu' include NS8BKPs # ─── Dispatcher ────────────────────────────────────────────── class Dispatcher TIPOS = { 'proxmox' => -> (log) { Procesador::Proxmox.new(log) }, 'xva' => -> (log) { Procesador::Xen.new(log) }, 'rclone' => -> (log) { Procesador::Rclone.new(log) }, }.freeze def initialize(config, log) @config = config @log = log end def ejecutar_tarea(tarea) tipo = tarea[:tipo].to_s factory = TIPOS[tipo] unless factory @log.error("Tipo desconocido: #{tipo}") return end procesador = factory.call(@log) procesador.ejecutar(tarea) end def ejecutar_comando(comando) @log.titulo(comando[:texto]) tareas_ids = comando[:tareas] || [] tareas_ids.each_with_index do |id, i| tarea = @config.find_tarea(id) if tarea @log.paso("Paso #{i + 1}/#{tareas_ids.length}: #{tarea[:texto]}") ejecutar_tarea(tarea) else @log.error("Tarea '#{id}' no encontrada en comando '#{comando[:id]}'.") end end @log.ok("Comando '#{comando[:texto]}' completado.") end end # ─── Auto-Screen Wrapper ───────────────────────────────────── def auto_screen! return if ENV['TERM']&.start_with?('screen') || ENV['STY'] puts "\n#{C::CYAN}ℹ Relanzando en sesión screen para permitir desatención...#{C::RESET}" cmd = ([$PROGRAM_NAME] + ARGV).map { |a| Shellwords.escape(a) }.join(' ') nombre = "ns8bkps_#{Time.now.strftime('%H%M%S')}" puts "Para reconectarte: #{C::YELLOW}screen -r #{nombre}#{C::RESET}" sleep 1 system('screen', '-dmS', nombre, 'bash', '-c', "#{cmd}; echo ''; read -p 'Enter para cerrar...'") exit 0 end # ─── Ctrl+C Handler ────────────────────────────────────────── Signal.trap('INT') do puts "\n#{C::RED}Ctrl+C — Saliendo...#{C::RESET}" exit 130 end # ─── Ayuda ─────────────────────────────────────────────────── def mostrar_ayuda(config) puts <<~HELP #{C::BOLD}#{C::CYAN}ns8-bkps — Procesador de Backups v6.0#{C::RESET} #{C::YELLOW}Uso:#{C::RESET} ruby ns8-bkps.rb Menú interactivo ruby ns8-bkps.rb T Ejecutar tarea N ruby ns8-bkps.rb C Ejecutar comando N ruby ns8-bkps.rb list Listar tareas/comandos ruby ns8-bkps.rb --help Esta ayuda #{C::YELLOW}Tareas configuradas:#{C::RESET} HELP config.tareas.each_with_index do |t, i| puts " #{C::GREEN}T#{i+1}#{C::RESET} #{t[:texto]} #{C::DIM}(#{t[:tipo]})#{C::RESET}" end puts "\n #{C::YELLOW}Comandos configurados:#{C::RESET}" config.comandos.each_with_index do |c, i| puts " #{C::GREEN}C#{i+1}#{C::RESET} #{c[:texto]}" end end def cmd_list(config) puts "#{C::BOLD}Tareas:#{C::RESET}" config.tareas.each_with_index do |t, i| puts " T#{i+1} #{t[:id]} #{t[:texto]} [#{t[:tipo]}] #{t[:origen]} → #{t[:destino]}" end puts "\n#{C::BOLD}Comandos:#{C::RESET}" config.comandos.each_with_index do |c, i| puts " C#{i+1} #{c[:id]} #{c[:texto]} → #{c[:tareas]&.join(', ')}" end end # ─── Main ──────────────────────────────────────────────────── config_path = File.join(APP_DIR, 'ns8-bkps.yml') log = AppLogger.new(File.join(APP_DIR, 'logs')) config = Config.new(config_path) dispatcher = Dispatcher.new(config, log) arg = ARGV[0]&.strip case arg when nil # Menú interactivo con auto-screen auto_screen! log.info("===== ns8-bkps v6.0 iniciado (Screen) =====") Menu.new(config, dispatcher, log).ejecutar when '--help', '-h', 'help' mostrar_ayuda(config) when 'list' cmd_list(config) when /^[TC]\d+$/i # Ejecución directa desde CLI tipo = arg[0].upcase idx = arg[1..].to_i - 1 if tipo == 'T' && config.tareas[idx] dispatcher.ejecutar_tarea(config.tareas[idx]) elsif tipo == 'C' && config.comandos[idx] dispatcher.ejecutar_comando(config.comandos[idx]) else puts "#{ERR} Opción no válida: #{arg}" exit 1 end else puts "#{ERR} Comando desconocido: #{arg}" mostrar_ayuda(config) exit 1 end