# frozen_string_literal: true require 'date' require 'time' require 'json' # require_relative '../cli/backup/configurador' # Archivado → _hist/ (no usado aquí) module ADN class Conciliador def initialize(logger) @logger = logger @candados_path = File.join(ADN::PROJECT_ROOT, 'adn', 'tools', 'seguridad', 'candados.rb') end def ejecutar(silencioso: false) @silencioso = silencioso @logger.info("Iniciando saneamiento preventivo de pendientes...") unless @silencioso # 1. Reconciliar backups de ayer y hoy conciliar_backups_recientes # 2. Traspasar pendientes no resueltos de ayer a hoy traspasar_pendientes_ayer_a_hoy end private def conciliar_backups_recientes # Revisamos ayer y hoy [Date.today - 1, Date.today].each do |fecha_obj| fecha = fecha_obj.iso8601 ruta = File.join(ADN::BITACORAS_DIR, "#{fecha}.md") next unless File.exist?(ruta) # Si el archivo tiene relojes de arena (⏳) contenido = File.read(ruta) if contenido.include?('⏳') @logger.info(" Revisando tareas pendientes del #{fecha}...") unless @silencioso procesar_reconciliacion(fecha, ruta) end end end def procesar_reconciliacion(fecha, ruta) # Obtener tareas de Proxmox tasks = obtener_tareas_proxmox return if tasks.empty? vmid_map = cargar_mapa_vmid_nodos contenido = File.read(ruta) lineas = contenido.lines nodo_actual = nil cambio = false lineas.each do |linea| if linea.start_with?('### ') && !linea.include?('|') nodo_actual = linea.sub('### ', '').strip next end if nodo_actual && linea.include?('|') && linea.include?('⏳') match_hora = linea.match(/\|\s*(\d{2}:\d{2})\s*\|/) next unless match_hora hora_inicio_str = match_hora[1] vmid = vmid_map.key(nodo_actual) next unless vmid # Buscar tarea coincidente hora_inicio_t = Time.parse("#{fecha} #{hora_inicio_str}") rescue nil tarea = tasks.select { |t| vmid_task = (t['vmid'] || t['id']).to_s t['type'] == 'vzdump' && vmid_task == vmid && (t['status'] == 'OK' || !t['endtime'].nil?) && (hora_inicio_t ? Time.at(t['starttime']) >= (hora_inicio_t - 3600) : true) }.max_by { |t| t['endtime'] || 0 } if tarea && tarea['endtime'] exito = (tarea['status'] == 'OK') hora_fin = Time.at(tarea['endtime']).strftime('%H:%M') task_id = tarea['id'] @logger.exito(" ✨ Tarea auto-resuelta: #{nodo_actual} (#{hora_inicio_str} -> #{hora_fin})") unless @silencioso # Aplicar cambio en el contenido estado_ico = exito ? '✅' : '⚠️' desc = exito ? 'Backup completado exitosamente.' : 'Fallo en la ejecución del backup.' desc = "[task:#{task_id[0..7]}] #{desc}" linea_nueva = "| #{hora_inicio_str} | #{hora_fin} | #{desc} | R | #{estado_ico} |\n" # Reemplazo cuidadoso regex_fila = /^\|\s*#{Regexp.escape(hora_inicio_str)}\s*\|\s*\|\s*[^|]*\|\s*[^|]*\|\s*[^|]*⏳[^|]*\|/ # Buscar el bloque del nodo regex_seccion = /(### #{Regexp.escape(nodo_actual)}.*?)(?=\n###\s|