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)
|
* Inserta una fila en la tabla manteniendo el orden por último octeto de IP
|
||||||
* Valida cada IP antes de mostrarla en la tabla
|
* Las filas flotantes y admin van primero (antes de las de cliente)
|
||||||
*/
|
*/
|
||||||
async function runProgressiveValidation(clientIps, pingLimit, shouldVerifyPing) {
|
function insertRowSorted(ip, statusText, statusClass) {
|
||||||
console.log(`Iniciando validación progresiva de ${clientIps.length} IPs`);
|
const ipTypeLabel = getIpType(ip);
|
||||||
console.log(`Ping habilitado: ${shouldVerifyPing}, Límite: ${pingLimit}`);
|
const hideAdminCheckbox = document.getElementById('hideAdmin');
|
||||||
|
const isHidden = hideAdminCheckbox && hideAdminCheckbox.checked && ipTypeLabel === 'Administración';
|
||||||
|
|
||||||
// Determinar IPs a validar (inicialmente todas, filtramos dinámicamente)
|
const row = document.createElement('tr');
|
||||||
let ipsToProcess = [...clientIps];
|
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`);
|
// Determinar el badge de tipo de IP
|
||||||
|
let ipTypeBadge = '';
|
||||||
// Mostrar mensaje de progreso según cantidad de IPs
|
if (statusClass === 'floating') {
|
||||||
let progressMessage = '';
|
ipTypeBadge = '<span class="ip-type-badge ip-type-floating">IP Flotante</span>';
|
||||||
if (shouldVerifyPing && (pingLimit === 0 || pingLimit >= 20)) {
|
} else if (ipTypeLabel === 'Administración') {
|
||||||
progressMessage = 'Validando todas las IP\'s por ping, esto puede llevar varios minutos. Espere por favor...';
|
ipTypeBadge = '<span class="ip-type-badge ip-type-admin">Administración</span>';
|
||||||
} else if (shouldVerifyPing) {
|
} else if (statusClass === 'used' || statusClass === 'conflict' || statusText.includes('En uso')) {
|
||||||
progressMessage = 'Validando IP\'s por ping. Espere por favor...';
|
ipTypeBadge = '<span class="ip-type-badge ip-type-admin">No disponible</span>';
|
||||||
} else {
|
} 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> ';
|
const spinnerHtml = '<span class="loading-spinner-small"></span> ';
|
||||||
showError(spinnerHtml + progressMessage, 'info', false);
|
showError(spinnerHtml + progressMessage, 'info', false);
|
||||||
|
|
||||||
@ -2964,83 +3035,92 @@ if ($systemUserId) {
|
|||||||
cancelBtn.style.display = 'inline-flex';
|
cancelBtn.style.display = 'inline-flex';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Estado compartido entre workers (thread-safe en JS por event loop)
|
||||||
|
let nextIndex = 0;
|
||||||
let foundAvailableCount = 0;
|
let foundAvailableCount = 0;
|
||||||
|
let processedCount = 0;
|
||||||
|
const totalIps = ipsToProcess.length;
|
||||||
|
|
||||||
// Procesar cada IP secuencialmente
|
// Función de un worker individual
|
||||||
for (const ip of ipsToProcess) {
|
async function worker(workerId) {
|
||||||
// Verificar si debemos detenernos por límite (Smart Limit)
|
while (nextIndex < totalIps) {
|
||||||
if (pingLimit > 0 && shouldVerifyPing && foundAvailableCount >= pingLimit) {
|
// Verificar cancelación
|
||||||
console.log(`Se alcanzó el límite de ${pingLimit} IPs disponibles. Deteniendo búsqueda.`);
|
if (verificationCancelled) break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verificar si el usuario canceló
|
// Verificar límite (con ping habilitado)
|
||||||
if (verificationCancelled) {
|
if (pingLimit > 0 && shouldVerifyPing && foundAvailableCount >= pingLimit) break;
|
||||||
console.log('Validación cancelada por el usuario');
|
|
||||||
showError('Validación cancelada. Mostrando resultados parciales.', 'warning');
|
|
||||||
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 ip = ipsToProcess[currentIndex];
|
||||||
const tableContainer = document.querySelector('.table-container');
|
|
||||||
if (tableContainer) {
|
|
||||||
tableContainer.scrollTop = tableContainer.scrollHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Llamar a validación de IP con búsqueda de sitios
|
// Validar IP con búsqueda de sitios
|
||||||
const formData = new FormData();
|
const formData = new FormData();
|
||||||
formData.append('action', 'validate');
|
formData.append('action', 'validate');
|
||||||
formData.append('ip', ip);
|
formData.append('ip', ip);
|
||||||
|
|
||||||
const response = await fetch('', {
|
const response = await fetch('', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
body: formData
|
body: formData
|
||||||
});
|
});
|
||||||
|
|
||||||
const data = await response.json();
|
const data = await response.json();
|
||||||
|
processedCount++;
|
||||||
|
|
||||||
if (data.success && !data.in_use) {
|
// Actualizar progreso cada 5 IPs validadas
|
||||||
// IP disponible según site search
|
if (processedCount % 5 === 0) {
|
||||||
|
showError(
|
||||||
|
spinnerHtml + `Validando IP's... ${processedCount}/${totalIps} procesadas, ${foundAvailableCount} disponibles`,
|
||||||
|
'info', false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (shouldVerifyPing) {
|
if (data.success && !data.in_use) {
|
||||||
// Si ping está habilitado, renderizar y verificar con ping
|
// IP disponible según site search
|
||||||
renderRow(ip, '⏳ Verificando ping...', 'verifying');
|
if (shouldVerifyPing) {
|
||||||
|
// Insertar en tabla con estado "verificando ping"
|
||||||
|
insertRowSorted(ip, '⏳ Verificando ping...', 'verifying');
|
||||||
|
|
||||||
// Verificar ping y esperar resultado
|
// Verificar ping
|
||||||
const verifyResult = await verifyBatch([ip]);
|
const verifyResult = await verifyBatch([ip]);
|
||||||
|
if (verifyResult && verifyResult.not_responding && verifyResult.not_responding.includes(ip)) {
|
||||||
// Si el resultado confirma que NO responde (not_responding), entonces es válida
|
foundAvailableCount++;
|
||||||
if (verifyResult && verifyResult.not_responding && verifyResult.not_responding.includes(ip)) {
|
}
|
||||||
|
} else {
|
||||||
|
// Sin ping: insertar directamente como disponible
|
||||||
|
insertRowSorted(ip, '✅ Disponible', 'available');
|
||||||
foundAvailableCount++;
|
foundAvailableCount++;
|
||||||
}
|
}
|
||||||
// Si resonding (conflicto), NO incrementamos contador y seguimos buscando
|
|
||||||
} else {
|
} else {
|
||||||
// Si ping NO está habilitado, renderizar como disponible
|
// IP en uso — no renderizar
|
||||||
renderRow(ip, '✅ Disponible', 'available');
|
console.log(`[W${workerId}] IP ${ip} filtrada (en uso)`);
|
||||||
foundAvailableCount++; // Contamos como encontrada
|
|
||||||
}
|
}
|
||||||
} else {
|
} catch (error) {
|
||||||
// IP en uso según site search - NO RENDERIZAR
|
processedCount++;
|
||||||
// Solo agregar log en consola
|
console.error(`[W${workerId}] Error validando IP ${ip}:`, error);
|
||||||
console.log(`IP ${ip} filtrada (en uso en UISP)`);
|
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
|
// Ocultar botón de cancelar
|
||||||
if (cancelBtn) {
|
if (cancelBtn) {
|
||||||
cancelBtn.style.display = 'none';
|
cancelBtn.style.display = 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actualizar estadísticas con los números reales
|
// Actualizar estadísticas finales
|
||||||
// Contar IPs disponibles (status-available) y en uso (status-used, status-conflict, o status-floating)
|
|
||||||
const allRows = ipTableBody.querySelectorAll('tr');
|
const allRows = ipTableBody.querySelectorAll('tr');
|
||||||
let availableIpsCount = 0;
|
let availableIpsCount = 0;
|
||||||
let usedIpsCount = 0;
|
let usedIpsCount = 0;
|
||||||
@ -3058,7 +3138,6 @@ if ($systemUserId) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Actualizar contadores en la UI
|
|
||||||
availableCount.textContent = availableIpsCount;
|
availableCount.textContent = availableIpsCount;
|
||||||
usedCount.textContent = usedIpsCount;
|
usedCount.textContent = usedIpsCount;
|
||||||
|
|
||||||
@ -3069,7 +3148,7 @@ if ($systemUserId) {
|
|||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
if (searchBtn) searchBtn.disabled = false;
|
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);
|
showError(`Validación completada. ${availableIpsCount} IPs disponibles encontradas.`, 'success', false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3097,18 +3176,16 @@ if ($systemUserId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function runProgressiveVerification(clientIps, limit) {
|
async function runProgressiveVerification(clientIps, limit) {
|
||||||
|
const WORKER_COUNT = 5;
|
||||||
let ipsToVerify = [];
|
let ipsToVerify = [];
|
||||||
|
|
||||||
// 1. Filtrar IPs a verificar según el límite
|
|
||||||
if (limit > 0) {
|
if (limit > 0) {
|
||||||
// Si hay límite, solo tomamos las primeras N
|
|
||||||
ipsToVerify = clientIps.slice(0, limit);
|
ipsToVerify = clientIps.slice(0, limit);
|
||||||
} else {
|
} else {
|
||||||
// Si límite es 0 (Todas), verificamos todas
|
|
||||||
ipsToVerify = [...clientIps];
|
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
|
// Mostrar botón de cancelar
|
||||||
verificationCancelled = false;
|
verificationCancelled = false;
|
||||||
@ -3116,32 +3193,42 @@ if ($systemUserId) {
|
|||||||
cancelBtn.style.display = 'inline-flex';
|
cancelBtn.style.display = 'inline-flex';
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Procesar una por una (secuencialmente)
|
// Estado compartido
|
||||||
for (const ip of ipsToVerify) {
|
let nextIndex = 0;
|
||||||
// Verificar si el usuario canceló
|
const totalIps = ipsToVerify.length;
|
||||||
if (verificationCancelled) {
|
|
||||||
console.log('Verificación cancelada por el usuario');
|
// Función de un worker individual
|
||||||
showError('Verificación cancelada. Mostrando resultados parciales.', 'warning');
|
async function worker(workerId) {
|
||||||
break;
|
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
|
// Ocultar botón de cancelar al finalizar
|
||||||
if (cancelBtn) {
|
if (cancelBtn) {
|
||||||
cancelBtn.style.display = 'none';
|
cancelBtn.style.display = 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actualizar estadísticas con los números reales
|
// Actualizar estadísticas
|
||||||
const allRows = ipTableBody.querySelectorAll('tr');
|
const allRows = ipTableBody.querySelectorAll('tr');
|
||||||
let availableIpsCount = 0;
|
let availableIpsCount = 0;
|
||||||
let usedIpsCount = 0;
|
let usedIpsCount = 0;
|
||||||
@ -3159,11 +3246,8 @@ if ($systemUserId) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Actualizar contadores en la UI
|
|
||||||
availableCount.textContent = availableIpsCount;
|
availableCount.textContent = availableIpsCount;
|
||||||
usedCount.textContent = usedIpsCount;
|
usedCount.textContent = usedIpsCount;
|
||||||
|
|
||||||
// NOTA: Las IPs que exceden el límite NO se renderizan, cumpliendo el requerimiento.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Event listener para botón de cancelar
|
// Event listener para botón de cancelar
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user