primera implementación asíncrona con workers
This commit is contained in:
parent
c10f2a44fe
commit
a8dd6a8f38
1547
data/plugin.log
1547
data/plugin.log
File diff suppressed because it is too large
Load Diff
278
public.php
278
public.php
@ -2932,29 +2932,100 @@ if ($systemUserId) {
|
||||
|
||||
|
||||
/**
|
||||
* Validación progresiva con búsqueda de sitios (event.ip_validate)
|
||||
* Valida cada IP antes de mostrarla en la tabla
|
||||
* Inserta una fila en la tabla manteniendo el orden por último octeto de IP
|
||||
* Las filas flotantes y admin van primero (antes de las de cliente)
|
||||
*/
|
||||
async function runProgressiveValidation(clientIps, pingLimit, shouldVerifyPing) {
|
||||
console.log(`Iniciando validación progresiva de ${clientIps.length} IPs`);
|
||||
console.log(`Ping habilitado: ${shouldVerifyPing}, Límite: ${pingLimit}`);
|
||||
function insertRowSorted(ip, statusText, statusClass) {
|
||||
const ipTypeLabel = getIpType(ip);
|
||||
const hideAdminCheckbox = document.getElementById('hideAdmin');
|
||||
const isHidden = hideAdminCheckbox && hideAdminCheckbox.checked && ipTypeLabel === 'Administración';
|
||||
|
||||
// Determinar IPs a validar (inicialmente todas, filtramos dinámicamente)
|
||||
let ipsToProcess = [...clientIps];
|
||||
const row = document.createElement('tr');
|
||||
row.id = `row-${ip.replace(/\./g, '-')}`;
|
||||
if (isHidden) row.classList.add('hidden-row');
|
||||
if (ipTypeLabel === 'Administración') row.classList.add('admin-row');
|
||||
if (statusClass === 'floating') row.classList.add('floating-row');
|
||||
row.classList.add('client-result-row');
|
||||
|
||||
console.log(`Validando hasta encontrar ${pingLimit > 0 ? pingLimit : 'todas'} IPs disponibles entre ${clientIps.length} candidatos`);
|
||||
|
||||
// Mostrar mensaje de progreso según cantidad de IPs
|
||||
let progressMessage = '';
|
||||
if (shouldVerifyPing && (pingLimit === 0 || pingLimit >= 20)) {
|
||||
progressMessage = 'Validando todas las IP\'s por ping, esto puede llevar varios minutos. Espere por favor...';
|
||||
} else if (shouldVerifyPing) {
|
||||
progressMessage = 'Validando IP\'s por ping. Espere por favor...';
|
||||
// Determinar el badge de tipo de IP
|
||||
let ipTypeBadge = '';
|
||||
if (statusClass === 'floating') {
|
||||
ipTypeBadge = '<span class="ip-type-badge ip-type-floating">IP Flotante</span>';
|
||||
} else if (ipTypeLabel === 'Administración') {
|
||||
ipTypeBadge = '<span class="ip-type-badge ip-type-admin">Administración</span>';
|
||||
} else if (statusClass === 'used' || statusClass === 'conflict' || statusText.includes('En uso')) {
|
||||
ipTypeBadge = '<span class="ip-type-badge ip-type-admin">No disponible</span>';
|
||||
} else {
|
||||
progressMessage = 'Validando IP\'s con búsqueda de sitios. Espere por favor...';
|
||||
ipTypeBadge = '<span class="ip-type-badge ip-type-client">Cliente</span>';
|
||||
}
|
||||
|
||||
row.innerHTML = `
|
||||
<td class="row-number">0</td>
|
||||
<td>
|
||||
<div class="ip-cell-mobile">
|
||||
<span class="ip-address">${ip}</span>
|
||||
${ipTypeBadge}
|
||||
</div>
|
||||
</td>
|
||||
<td id="status-${ip.replace(/\./g, '-')}">
|
||||
<span class="status-badge status-${statusClass}">${statusText}</span>
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn-copy" onclick="copyToClipboard('${ip}', this)">
|
||||
📋 Copiar
|
||||
</button>
|
||||
</td>
|
||||
`;
|
||||
|
||||
// Encontrar posición correcta entre las filas de resultado de cliente
|
||||
const newOctet = parseInt(ip.split('.')[3]);
|
||||
const existingRows = ipTableBody.querySelectorAll('tr.client-result-row');
|
||||
let insertBefore = null;
|
||||
|
||||
for (const existingRow of existingRows) {
|
||||
const existingIp = existingRow.querySelector('.ip-address');
|
||||
if (existingIp) {
|
||||
const existingOctet = parseInt(existingIp.textContent.split('.')[3]);
|
||||
if (existingOctet > newOctet) {
|
||||
insertBefore = existingRow;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (insertBefore) {
|
||||
ipTableBody.insertBefore(row, insertBefore);
|
||||
} else {
|
||||
ipTableBody.appendChild(row);
|
||||
}
|
||||
|
||||
// Renumerar filas visibles
|
||||
renumberVisibleRows();
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validación paralela con pool de workers
|
||||
* Usa N workers concurrentes para validar IPs simultáneamente
|
||||
*/
|
||||
async function runProgressiveValidation(clientIps, pingLimit, shouldVerifyPing) {
|
||||
const WORKER_COUNT = 5;
|
||||
console.log(`Iniciando validación PARALELA de ${clientIps.length} IPs con ${WORKER_COUNT} workers`);
|
||||
console.log(`Ping habilitado: ${shouldVerifyPing}, Límite: ${pingLimit}`);
|
||||
|
||||
let ipsToProcess = [...clientIps];
|
||||
|
||||
// Mostrar mensaje de progreso
|
||||
let progressMessage = '';
|
||||
if (shouldVerifyPing && (pingLimit === 0 || pingLimit >= 20)) {
|
||||
progressMessage = 'Validando todas las IP\'s (modo paralelo). Espere por favor...';
|
||||
} else if (shouldVerifyPing) {
|
||||
progressMessage = 'Validando IP\'s por ping (modo paralelo). Espere por favor...';
|
||||
} else {
|
||||
progressMessage = 'Validando IP\'s con búsqueda de sitios (modo paralelo). Espere por favor...';
|
||||
}
|
||||
|
||||
// Usar autohide = false explícitamente y agregar spinner HTML al mensaje
|
||||
const spinnerHtml = '<span class="loading-spinner-small"></span> ';
|
||||
showError(spinnerHtml + progressMessage, 'info', false);
|
||||
|
||||
@ -2964,83 +3035,92 @@ if ($systemUserId) {
|
||||
cancelBtn.style.display = 'inline-flex';
|
||||
}
|
||||
|
||||
// Estado compartido entre workers (thread-safe en JS por event loop)
|
||||
let nextIndex = 0;
|
||||
let foundAvailableCount = 0;
|
||||
let processedCount = 0;
|
||||
const totalIps = ipsToProcess.length;
|
||||
|
||||
// Procesar cada IP secuencialmente
|
||||
for (const ip of ipsToProcess) {
|
||||
// Verificar si debemos detenernos por límite (Smart Limit)
|
||||
if (pingLimit > 0 && shouldVerifyPing && foundAvailableCount >= pingLimit) {
|
||||
console.log(`Se alcanzó el límite de ${pingLimit} IPs disponibles. Deteniendo búsqueda.`);
|
||||
break;
|
||||
}
|
||||
// Función de un worker individual
|
||||
async function worker(workerId) {
|
||||
while (nextIndex < totalIps) {
|
||||
// Verificar cancelación
|
||||
if (verificationCancelled) break;
|
||||
|
||||
// Verificar si el usuario canceló
|
||||
if (verificationCancelled) {
|
||||
console.log('Validación cancelada por el usuario');
|
||||
showError('Validación cancelada. Mostrando resultados parciales.', 'warning');
|
||||
break;
|
||||
}
|
||||
// Verificar límite (con ping habilitado)
|
||||
if (pingLimit > 0 && shouldVerifyPing && foundAvailableCount >= pingLimit) break;
|
||||
|
||||
// NO renderizar aún, primero validar
|
||||
// Tomar siguiente IP del pool compartido
|
||||
const currentIndex = nextIndex++;
|
||||
if (currentIndex >= totalIps) break;
|
||||
|
||||
// Scroll al final de la tabla
|
||||
const tableContainer = document.querySelector('.table-container');
|
||||
if (tableContainer) {
|
||||
tableContainer.scrollTop = tableContainer.scrollHeight;
|
||||
}
|
||||
const ip = ipsToProcess[currentIndex];
|
||||
|
||||
try {
|
||||
// Llamar a validación de IP con búsqueda de sitios
|
||||
const formData = new FormData();
|
||||
formData.append('action', 'validate');
|
||||
formData.append('ip', ip);
|
||||
try {
|
||||
// Validar IP con búsqueda de sitios
|
||||
const formData = new FormData();
|
||||
formData.append('action', 'validate');
|
||||
formData.append('ip', ip);
|
||||
|
||||
const response = await fetch('', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
const response = await fetch('', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
const data = await response.json();
|
||||
processedCount++;
|
||||
|
||||
if (data.success && !data.in_use) {
|
||||
// IP disponible según site search
|
||||
// Actualizar progreso cada 5 IPs validadas
|
||||
if (processedCount % 5 === 0) {
|
||||
showError(
|
||||
spinnerHtml + `Validando IP's... ${processedCount}/${totalIps} procesadas, ${foundAvailableCount} disponibles`,
|
||||
'info', false
|
||||
);
|
||||
}
|
||||
|
||||
if (shouldVerifyPing) {
|
||||
// Si ping está habilitado, renderizar y verificar con ping
|
||||
renderRow(ip, '⏳ Verificando ping...', 'verifying');
|
||||
if (data.success && !data.in_use) {
|
||||
// IP disponible según site search
|
||||
if (shouldVerifyPing) {
|
||||
// Insertar en tabla con estado "verificando ping"
|
||||
insertRowSorted(ip, '⏳ Verificando ping...', 'verifying');
|
||||
|
||||
// Verificar ping y esperar resultado
|
||||
const verifyResult = await verifyBatch([ip]);
|
||||
|
||||
// Si el resultado confirma que NO responde (not_responding), entonces es válida
|
||||
if (verifyResult && verifyResult.not_responding && verifyResult.not_responding.includes(ip)) {
|
||||
// Verificar ping
|
||||
const verifyResult = await verifyBatch([ip]);
|
||||
if (verifyResult && verifyResult.not_responding && verifyResult.not_responding.includes(ip)) {
|
||||
foundAvailableCount++;
|
||||
}
|
||||
} else {
|
||||
// Sin ping: insertar directamente como disponible
|
||||
insertRowSorted(ip, '✅ Disponible', 'available');
|
||||
foundAvailableCount++;
|
||||
}
|
||||
// Si resonding (conflicto), NO incrementamos contador y seguimos buscando
|
||||
} else {
|
||||
// Si ping NO está habilitado, renderizar como disponible
|
||||
renderRow(ip, '✅ Disponible', 'available');
|
||||
foundAvailableCount++; // Contamos como encontrada
|
||||
// IP en uso — no renderizar
|
||||
console.log(`[W${workerId}] IP ${ip} filtrada (en uso)`);
|
||||
}
|
||||
} else {
|
||||
// IP en uso según site search - NO RENDERIZAR
|
||||
// Solo agregar log en consola
|
||||
console.log(`IP ${ip} filtrada (en uso en UISP)`);
|
||||
} catch (error) {
|
||||
processedCount++;
|
||||
console.error(`[W${workerId}] Error validando IP ${ip}:`, error);
|
||||
insertRowSorted(ip, '❌ Error validación', 'error');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error validando IP ${ip}:`, error);
|
||||
// En caso de error, renderizar con error
|
||||
renderRow(ip, '❌ Error validación', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// Lanzar N workers en paralelo
|
||||
const workerPromises = [];
|
||||
for (let i = 0; i < Math.min(WORKER_COUNT, totalIps); i++) {
|
||||
workerPromises.push(worker(i + 1));
|
||||
}
|
||||
|
||||
// Esperar a que todos terminen
|
||||
await Promise.all(workerPromises);
|
||||
|
||||
// Ocultar botón de cancelar
|
||||
if (cancelBtn) {
|
||||
cancelBtn.style.display = 'none';
|
||||
}
|
||||
|
||||
// Actualizar estadísticas con los números reales
|
||||
// Contar IPs disponibles (status-available) y en uso (status-used, status-conflict, o status-floating)
|
||||
// Actualizar estadísticas finales
|
||||
const allRows = ipTableBody.querySelectorAll('tr');
|
||||
let availableIpsCount = 0;
|
||||
let usedIpsCount = 0;
|
||||
@ -3058,7 +3138,6 @@ if ($systemUserId) {
|
||||
}
|
||||
});
|
||||
|
||||
// Actualizar contadores en la UI
|
||||
availableCount.textContent = availableIpsCount;
|
||||
usedCount.textContent = usedIpsCount;
|
||||
|
||||
@ -3069,7 +3148,7 @@ if ($systemUserId) {
|
||||
<?php endif; ?>
|
||||
if (searchBtn) searchBtn.disabled = false;
|
||||
|
||||
console.log(`Validación completada. ${availableIpsCount} IPs disponibles, ${usedIpsCount} IPs en uso`);
|
||||
console.log(`Validación paralela completada. ${availableIpsCount} IPs disponibles, ${usedIpsCount} IPs en uso`);
|
||||
showError(`Validación completada. ${availableIpsCount} IPs disponibles encontradas.`, 'success', false);
|
||||
}
|
||||
|
||||
@ -3097,18 +3176,16 @@ if ($systemUserId) {
|
||||
}
|
||||
|
||||
async function runProgressiveVerification(clientIps, limit) {
|
||||
const WORKER_COUNT = 5;
|
||||
let ipsToVerify = [];
|
||||
|
||||
// 1. Filtrar IPs a verificar según el límite
|
||||
if (limit > 0) {
|
||||
// Si hay límite, solo tomamos las primeras N
|
||||
ipsToVerify = clientIps.slice(0, limit);
|
||||
} else {
|
||||
// Si límite es 0 (Todas), verificamos todas
|
||||
ipsToVerify = [...clientIps];
|
||||
}
|
||||
|
||||
console.log(`Iniciando verificación progresiva de ${ipsToVerify.length} IPs`);
|
||||
console.log(`Iniciando verificación PARALELA de ${ipsToVerify.length} IPs con ${WORKER_COUNT} workers`);
|
||||
|
||||
// Mostrar botón de cancelar
|
||||
verificationCancelled = false;
|
||||
@ -3116,32 +3193,42 @@ if ($systemUserId) {
|
||||
cancelBtn.style.display = 'inline-flex';
|
||||
}
|
||||
|
||||
// 2. Procesar una por una (secuencialmente)
|
||||
for (const ip of ipsToVerify) {
|
||||
// Verificar si el usuario canceló
|
||||
if (verificationCancelled) {
|
||||
console.log('Verificación cancelada por el usuario');
|
||||
showError('Verificación cancelada. Mostrando resultados parciales.', 'warning');
|
||||
break;
|
||||
// Estado compartido
|
||||
let nextIndex = 0;
|
||||
const totalIps = ipsToVerify.length;
|
||||
|
||||
// Función de un worker individual
|
||||
async function worker(workerId) {
|
||||
while (nextIndex < totalIps) {
|
||||
if (verificationCancelled) break;
|
||||
|
||||
const currentIndex = nextIndex++;
|
||||
if (currentIndex >= totalIps) break;
|
||||
|
||||
const ip = ipsToVerify[currentIndex];
|
||||
|
||||
// Insertar fila con estado "Verificando" en posición ordenada
|
||||
insertRowSorted(ip, '⏳ Verificando...', 'verifying');
|
||||
|
||||
// Verificar con ping
|
||||
await verifyBatch([ip]);
|
||||
}
|
||||
|
||||
// A. Renderizar fila con estado "Verificando"
|
||||
renderRow(ip, '⏳ Verificando...', 'verifying');
|
||||
|
||||
// Scroll al final de la tabla para seguir el progreso
|
||||
const tableContainer = document.querySelector('.table-container');
|
||||
tableContainer.scrollTop = tableContainer.scrollHeight;
|
||||
|
||||
// B. Verificar (lote de 1)
|
||||
await verifyBatch([ip]);
|
||||
}
|
||||
|
||||
// Lanzar N workers en paralelo
|
||||
const workerPromises = [];
|
||||
for (let i = 0; i < Math.min(WORKER_COUNT, totalIps); i++) {
|
||||
workerPromises.push(worker(i + 1));
|
||||
}
|
||||
|
||||
await Promise.all(workerPromises);
|
||||
|
||||
// Ocultar botón de cancelar al finalizar
|
||||
if (cancelBtn) {
|
||||
cancelBtn.style.display = 'none';
|
||||
}
|
||||
|
||||
// Actualizar estadísticas con los números reales
|
||||
// Actualizar estadísticas
|
||||
const allRows = ipTableBody.querySelectorAll('tr');
|
||||
let availableIpsCount = 0;
|
||||
let usedIpsCount = 0;
|
||||
@ -3159,11 +3246,8 @@ if ($systemUserId) {
|
||||
}
|
||||
});
|
||||
|
||||
// Actualizar contadores en la UI
|
||||
availableCount.textContent = availableIpsCount;
|
||||
usedCount.textContent = usedIpsCount;
|
||||
|
||||
// NOTA: Las IPs que exceden el límite NO se renderizan, cumpliendo el requerimiento.
|
||||
}
|
||||
|
||||
// Event listener para botón de cancelar
|
||||
|
||||
Loading…
Reference in New Issue
Block a user