// ==UserScript== // @name Multi-Locate ONT // @namespace Violentmonkey Scripts // @match *://190.153.58.82/fttx/locate-ont* // @grant none // @version 3 // @author Ing. Adrian Leon // @updateURL https://raw.githubusercontent.com/TakRiuto/ACSScripts/release/MultiONT.user.js // @downloadURL https://raw.githubusercontent.com/TakRiuto/ACSScripts/release/MultiONT.user.js // @icon https://avatars.githubusercontent.com/u/20828447?v=4 // ==/UserScript== (function() { 'use strict'; // Helper para extraer el token JWT de localStorage o sessionStorage // (Generalmente aplicaciones Angular/React guardan el token de la sesión aquí) function getAuthToken() { const keys = Object.keys(localStorage).concat(Object.keys(sessionStorage)); for (let k of keys) { let val = localStorage.getItem(k) || sessionStorage.getItem(k); let match = val && val.match(/(eyJhbGci[a-zA-Z0-9-_]+\.[a-zA-Z0-9-_]+\.[a-zA-Z0-9-_]+)/); if (match) return match[1]; } return null; } function inyectarPanel() { if (document.getElementById('vm-custom-filter')) return; const mainRow = document.querySelector('input[formcontrolname="serialNumber"]')?.closest('.row'); if (!mainRow) { setTimeout(inyectarPanel, 1000); return; } const panel = document.createElement('div'); panel.id = 'vm-custom-filter'; panel.style.cssText = `background:#f4f4f4; border:2px solid #1ab394; padding:15px; margin-bottom:15px; border-radius:5px;`; panel.innerHTML = `
Listo para procesar en segundo plano vía API (más rápido).
`; mainRow.parentNode.insertBefore(panel, mainRow); let cancelado = false; document.getElementById('vm-btn-start').addEventListener('click', async function() { const inputTxt = document.getElementById('vm-seriales-input').value; const seriales = inputTxt.split(/[\n, ]+/).map(s => s.trim().toUpperCase()).filter(s => s.length > 0); if (seriales.length === 0) return alert("No hay seriales para buscar."); const token = getAuthToken(); if (!token) { alert("No se pudo encontrar el token de acceso. Por favor, asegúrate de haber iniciado sesión."); return; } cancelado = false; const overlay = document.createElement('div'); overlay.id = 'vm-overlay'; overlay.style.cssText = 'position:fixed; top:0; left:0; width:100%; height:100%; background:rgba(0,0,0,0.8); z-index:999999; display:flex; flex-direction:column; justify-content:center; align-items:center; color:white; font-family:sans-serif;'; overlay.innerHTML = `

Consultando API y Generando CSV...

Iniciando...

`; document.body.appendChild(overlay); document.getElementById('vm-overlay-cancel').onclick = () => { cancelado = true; }; const baseUrl = window.location.origin; const headers = { "accept": "application/json, text/plain, */*", "x-access-token": token }; const hoy = new Date(); const ayer = new Date(hoy); ayer.setDate(ayer.getDate() - 1); const formatFecha = (d) => { const mes = String(d.getMonth() + 1).padStart(2, '0'); const dia = String(d.getDate()).padStart(2, '0'); return `${d.getFullYear()}-${mes}-${dia}`; }; const strHoy = formatFecha(hoy); const strAyer = formatFecha(ayer); const UMBRAL_BYTES = 500 * 1024 * 1024; // 500 MB en bytes let resultados = []; for (let i = 0; i < seriales.length; i++) { if (cancelado) break; const s = seriales[i]; let exito = false; let fila = { serial: s, status: "ERROR/NO ENCONTRADO", consumo: "N/A", cliente: "", olt: "", rx: "", tx: "" }; // Prevenir fetch con serial vacío que provoca error 404/500 en la API if (!s || s === "") { resultados.push({ serial: "VACÍO", status: "SERIAL INVÁLIDO", consumo: "N/A", cliente: "", olt: "", rx: "", tx: "" }); continue; } for (let intento = 1; intento <= 3; intento++) { if (cancelado) break; document.getElementById('vm-p').innerText = `Buscando (${i + 1}/${seriales.length}): ${s}` + (intento > 1 ? ` (Reintento ${intento}/3)` : ``); document.getElementById('vm-progress-bar').style.width = `${((i + 1) / seriales.length) * 100}%`; try { // 1. Fetch de la data de la ONT const locateRes = await fetch(`${baseUrl}/api/fttx/locate-ont-by-serial-number/${s}?filter=%7B%22include%22:%7B%7D%7D`, { headers }); // Si el servidor responde 404, significa que la ONT no existe (no tiene sentido reintentar fallos de red aquí). if (locateRes.status === 404) { fila.status = "NO ENCONTRADO"; break; } if (!locateRes.ok) throw new Error("Falla en la respuesta de la API (ONT)"); const ontData = await locateRes.json(); const status = ontData.generalStatus || "Desconocido"; const terminalNodeId = ontData.dbTerminalNodeId; const cliente = ontData.dbClientName || ""; const olt = ontData.requestedOlt || ""; const rx = ontData.rx || ""; const tx = ontData.tx || ""; let consumo = "NO"; // 2. Fetch del consumo if (terminalNodeId) { const consRes = await fetch(`${baseUrl}/api/fttx/terminal-nodes/${terminalNodeId}/consumption?filter=%7B%22order%22:%22consumptionDay%20ASC%22,%22include%22:%7B%7D%7D`, { headers }); if (!consRes.ok) throw new Error("Falla en la respuesta de la API (Consumos)"); const consData = await consRes.json(); const dataReciente = consData.filter(c => c.consumptionDay === strHoy || c.consumptionDay === strAyer); const totalBytesRecientes = dataReciente.reduce((acc, curr) => acc + parseInt(curr.down || 0) + parseInt(curr.up || 0), 0); if (totalBytesRecientes >= UMBRAL_BYTES) consumo = "SI"; } fila = { serial: s, status, consumo, cliente, olt, rx, tx }; exito = true; break; // Salir del loop de reintentos porque fue exitoso } catch (e) { console.warn(`Intento ${intento} fallido para ${s}:`, e); if (intento < 3) { // Esperar 2 segundos antes de reintentar await new Promise(resolve => setTimeout(resolve, 2000)); } } } resultados.push(fila); // Pausa normal entre ONTs para no ahogar al servidor (1 segundo en lugar de 200ms) await new Promise(resolve => setTimeout(resolve, 1000)); } document.body.removeChild(overlay); if (resultados.length > 0) { // Generar el contenido del CSV let csvContent = "Serial,Status,Consumo,Cliente,OLT,RX,TX\n"; resultados.forEach(row => { // Escapar comillas dobles en el nombre del cliente por si acaso const clienteSafe = row.cliente.replace(/"/g, '""'); csvContent += `${row.serial},${row.status},${row.consumo},"${clienteSafe}",${row.olt},${row.rx},${row.tx}\n`; }); // Descargar el archivo const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' }); const url = URL.createObjectURL(blob); const link = document.createElement("a"); link.setAttribute("href", url); link.setAttribute("download", `Reporte_ONTs_${new Date().getTime()}.csv`); document.body.appendChild(link); link.click(); document.body.removeChild(link); } }); } setTimeout(inyectarPanel, 1500); })();