antes de la asincronía

This commit is contained in:
DANYDHSV 2026-03-26 15:33:32 -06:00
parent aaec7ce0b4
commit c10f2a44fe
3 changed files with 1789 additions and 9 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1042,6 +1042,18 @@ if ($systemUserId) {
border: 1px solid rgba(160, 174, 192, 0.4); border: 1px solid rgba(160, 174, 192, 0.4);
} }
.status-floating {
background: rgba(255, 165, 0, 0.2);
color: #ffa500;
border: 1px solid rgba(255, 165, 0, 0.4);
}
.ip-type-floating {
background: rgba(255, 165, 0, 0.2);
border: 1px solid rgba(255, 165, 0, 0.4);
color: #ffa500;
}
tr.hidden-row { tr.hidden-row {
display: none; display: none;
} }
@ -2713,12 +2725,19 @@ if ($systemUserId) {
// Limpiar tabla // Limpiar tabla
ipTableBody.innerHTML = ''; ipTableBody.innerHTML = '';
// Mostrar mensaje si no hay IPs disponibles // Mostrar mensaje si no hay IPs disponibles y no hay flotantes
if (data.data.length === 0) { if (data.data.length === 0 && (!data.floating || data.floating.length === 0)) {
showError('No hay IPs disponibles en este segmento', 'warning'); showError('No hay IPs disponibles en este segmento', 'warning');
return []; return [];
} }
// Renderizar IPs flotantes primero (dispositivos no autorizados / Pending Adoption)
if (data.floating && data.floating.length > 0) {
data.floating.forEach(device => {
renderRow(device.ip, `⚠️ IP Flotante - ${device.name} (${device.model})`, 'floating');
});
}
const clientIps = []; const clientIps = [];
// Separar y renderizar solo IPs de administración // Separar y renderizar solo IPs de administración
@ -2732,6 +2751,9 @@ if ($systemUserId) {
} }
}); });
// Renumerar filas visibles después de renderizar
renumberVisibleRows();
// Mostrar resultados container // Mostrar resultados container
results.classList.add('active'); results.classList.add('active');
@ -2750,13 +2772,16 @@ if ($systemUserId) {
row.id = `row-${ip.replace(/\./g, '-')}`; row.id = `row-${ip.replace(/\./g, '-')}`;
if (isHidden) row.classList.add('hidden-row'); if (isHidden) row.classList.add('hidden-row');
if (ipTypeLabel === 'Administración') row.classList.add('admin-row'); if (ipTypeLabel === 'Administración') row.classList.add('admin-row');
if (statusClass === 'floating') row.classList.add('floating-row');
// Calcular índice visual // El índice se asignará posteriormente por renumberVisibleRows()
const index = ipTableBody.children.length + 1; const index = 0;
// Determinar el badge de tipo de IP // Determinar el badge de tipo de IP
let ipTypeBadge = ''; let ipTypeBadge = '';
if (ipTypeLabel === 'Administración') { 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>'; ipTypeBadge = '<span class="ip-type-badge ip-type-admin">Administración</span>';
} else { } else {
// Si la IP está en uso (status contiene "En uso" o statusClass es "used"), mostrar "No disponible" en rojo // Si la IP está en uso (status contiene "En uso" o statusClass es "used"), mostrar "No disponible" en rojo
@ -2768,7 +2793,7 @@ if ($systemUserId) {
} }
row.innerHTML = ` row.innerHTML = `
<td>${index}</td> <td class="row-number">${index}</td>
<td> <td>
<div class="ip-cell-mobile"> <div class="ip-cell-mobile">
<span class="ip-address">${ip}</span> <span class="ip-address">${ip}</span>
@ -2785,6 +2810,26 @@ if ($systemUserId) {
</td> </td>
`; `;
ipTableBody.appendChild(row); ipTableBody.appendChild(row);
// Renumerar filas visibles después de insertar
renumberVisibleRows();
}
/**
* Renumera las filas visibles consecutivamente (1, 2, 3...)
*/
function renumberVisibleRows() {
const allRows = ipTableBody.querySelectorAll('tr');
let visibleIndex = 1;
allRows.forEach(row => {
const numberCell = row.querySelector('.row-number');
if (!numberCell) return;
if (row.classList.contains('hidden-row')) {
numberCell.textContent = '-';
} else {
numberCell.textContent = visibleIndex++;
}
});
} }
// Listener para el filtro de Admin IPs // Listener para el filtro de Admin IPs
@ -2799,6 +2844,8 @@ if ($systemUserId) {
row.classList.remove('hidden-row'); row.classList.remove('hidden-row');
} }
}); });
// Renumerar filas después de cambiar visibilidad
renumberVisibleRows();
}); });
} }
@ -2993,7 +3040,7 @@ if ($systemUserId) {
} }
// Actualizar estadísticas con los números reales // Actualizar estadísticas con los números reales
// Contar IPs disponibles (status-available) y en uso (status-used o status-conflict) // 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;
@ -3004,7 +3051,8 @@ if ($systemUserId) {
if (statusBadge.classList.contains('status-available')) { if (statusBadge.classList.contains('status-available')) {
availableIpsCount++; availableIpsCount++;
} else if (statusBadge.classList.contains('status-used') || } else if (statusBadge.classList.contains('status-used') ||
statusBadge.classList.contains('status-conflict')) { statusBadge.classList.contains('status-conflict') ||
statusBadge.classList.contains('status-floating')) {
usedIpsCount++; usedIpsCount++;
} }
} }
@ -3104,7 +3152,8 @@ if ($systemUserId) {
if (statusBadge.classList.contains('status-available')) { if (statusBadge.classList.contains('status-available')) {
availableIpsCount++; availableIpsCount++;
} else if (statusBadge.classList.contains('status-used') || } else if (statusBadge.classList.contains('status-used') ||
statusBadge.classList.contains('status-conflict')) { statusBadge.classList.contains('status-conflict') ||
statusBadge.classList.contains('status-floating')) {
usedIpsCount++; usedIpsCount++;
} }
} }

View File

@ -199,6 +199,30 @@ class IpSearchService
} }
} }
// Detectar IPs flotantes (dispositivos no autorizados / Pending Adoption)
$floatingIps = [];
try {
$floatingIps = $this->obtenerIpsFlotantes($segmento);
if (!empty($floatingIps)) {
// Agregar IPs flotantes a la lista de IPs en uso para excluirlas de disponibles
$floatingIpAddresses = array_column($floatingIps, 'ip');
$ipsEnUso = array_unique(array_merge($ipsEnUso, $floatingIpAddresses));
if ($this->logger) {
$this->logger->appendLog(sprintf(
'Detectadas %d IPs flotantes (dispositivos no autorizados) en segmento %s: %s',
count($floatingIps),
$segmento,
implode(', ', $floatingIpAddresses)
));
}
}
} catch (\Exception $e) {
if ($this->logger) {
$this->logger->appendLog('Error al detectar IPs flotantes: ' . $e->getMessage());
}
}
// Filtrar IPs del segmento solicitado // Filtrar IPs del segmento solicitado
$segmentoPrefix = "172.16.$segmento."; $segmentoPrefix = "172.16.$segmento.";
$segmentIps = []; $segmentIps = [];
@ -364,6 +388,7 @@ class IpSearchService
), ),
'data' => $ipsDisponibles, 'data' => $ipsDisponibles,
'used' => $segmentIps, 'used' => $segmentIps,
'floating' => $floatingIps,
'segment' => "172.16.$segmento.x", 'segment' => "172.16.$segmento.x",
'ping_verified' => $pingVerified 'ping_verified' => $pingVerified
]; ];
@ -539,4 +564,128 @@ class IpSearchService
return $ipToSite; return $ipToSite;
} }
/**
* Obtiene IPs de dispositivos no autorizados (Pending Adoption) en un segmento
* Estos dispositivos no aparecen en /devices/ips pero en /devices
*
* @param string $segmento Tercer octeto del segmento
* @return array Array de dispositivos flotantes [{ip, name, model, status}]
*/
private function obtenerIpsFlotantes($segmento)
{
$urlParts = parse_url($this->apiUrl);
$baseUrl = $urlParts['scheme'] . '://' . $urlParts['host'];
$devicesUrl = $baseUrl . '/nms/api/v2.1/devices?authorized=false';
if ($this->logger) {
$this->logger->appendLog('Buscando dispositivos no autorizados (Pending Adoption)...');
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $devicesUrl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'accept: application/json',
"x-auth-token: {$this->apiToken}"
]);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
$result = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
// Si la API de authorized=false no devuelve resultados útiles,
// intentar con /devices completo y filtrar
$devices = [];
if ($httpCode === 200 && $result !== false) {
$parsed = json_decode($result, true);
if (is_array($parsed) && !empty($parsed)) {
$devices = $parsed;
}
}
// Si no encontramos dispositivos no autorizados con el filtro,
// consultar todos los dispositivos y filtrar manualmente
if (empty($devices)) {
$allDevicesUrl = $baseUrl . '/nms/api/v2.1/devices';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $allDevicesUrl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'accept: application/json',
"x-auth-token: {$this->apiToken}"
]);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_TIMEOUT, 60);
$result = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200 || $result === false) {
if ($this->logger) {
$this->logger->appendLog("Error al obtener dispositivos: HTTP $httpCode");
}
return [];
}
$allDevices = json_decode($result, true);
if (!is_array($allDevices)) {
return [];
}
// Filtrar solo dispositivos no autorizados
$devices = array_filter($allDevices, function($device) {
return isset($device['identification']['authorized'])
&& $device['identification']['authorized'] === false;
});
}
// Filtrar por segmento y construir resultado
$segmentoPrefix = "172.16.$segmento.";
$floating = [];
foreach ($devices as $device) {
$primaryIp = $device['ipAddress'] ?? '';
$cleanIp = explode('/', $primaryIp)[0];
if (strpos($cleanIp, $segmentoPrefix) === 0) {
$floating[] = [
'ip' => $cleanIp,
'name' => $device['identification']['name'] ?? 'Desconocido',
'model' => $device['identification']['model'] ?? 'N/A',
'status' => $device['overview']['status'] ?? 'unknown',
];
continue;
}
// Buscar en ipAddressList
foreach ($device['ipAddressList'] ?? [] as $listIp) {
if (strpos($listIp, $segmentoPrefix) === 0) {
$floating[] = [
'ip' => $listIp,
'name' => $device['identification']['name'] ?? 'Desconocido',
'model' => $device['identification']['model'] ?? 'N/A',
'status' => $device['overview']['status'] ?? 'unknown',
];
}
}
}
if ($this->logger) {
$this->logger->appendLog(sprintf(
'Dispositivos no autorizados en segmento %s: %d encontrados',
$segmento,
count($floating)
));
}
return $floating;
}
} }