antes de la asincronía
This commit is contained in:
parent
aaec7ce0b4
commit
c10f2a44fe
1582
data/plugin.log
1582
data/plugin.log
File diff suppressed because it is too large
Load Diff
67
public.php
67
public.php
@ -1042,6 +1042,18 @@ if ($systemUserId) {
|
||||
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 {
|
||||
display: none;
|
||||
}
|
||||
@ -2713,12 +2725,19 @@ if ($systemUserId) {
|
||||
// Limpiar tabla
|
||||
ipTableBody.innerHTML = '';
|
||||
|
||||
// Mostrar mensaje si no hay IPs disponibles
|
||||
if (data.data.length === 0) {
|
||||
// Mostrar mensaje si no hay IPs disponibles y no hay flotantes
|
||||
if (data.data.length === 0 && (!data.floating || data.floating.length === 0)) {
|
||||
showError('No hay IPs disponibles en este segmento', 'warning');
|
||||
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 = [];
|
||||
|
||||
// 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
|
||||
results.classList.add('active');
|
||||
|
||||
@ -2750,13 +2772,16 @@ if ($systemUserId) {
|
||||
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');
|
||||
|
||||
// Calcular índice visual
|
||||
const index = ipTableBody.children.length + 1;
|
||||
// El índice se asignará posteriormente por renumberVisibleRows()
|
||||
const index = 0;
|
||||
|
||||
// Determinar el badge de tipo de IP
|
||||
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>';
|
||||
} else {
|
||||
// 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 = `
|
||||
<td>${index}</td>
|
||||
<td class="row-number">${index}</td>
|
||||
<td>
|
||||
<div class="ip-cell-mobile">
|
||||
<span class="ip-address">${ip}</span>
|
||||
@ -2785,6 +2810,26 @@ if ($systemUserId) {
|
||||
</td>
|
||||
`;
|
||||
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
|
||||
@ -2799,6 +2844,8 @@ if ($systemUserId) {
|
||||
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
|
||||
// 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');
|
||||
let availableIpsCount = 0;
|
||||
let usedIpsCount = 0;
|
||||
@ -3004,7 +3051,8 @@ if ($systemUserId) {
|
||||
if (statusBadge.classList.contains('status-available')) {
|
||||
availableIpsCount++;
|
||||
} else if (statusBadge.classList.contains('status-used') ||
|
||||
statusBadge.classList.contains('status-conflict')) {
|
||||
statusBadge.classList.contains('status-conflict') ||
|
||||
statusBadge.classList.contains('status-floating')) {
|
||||
usedIpsCount++;
|
||||
}
|
||||
}
|
||||
@ -3104,7 +3152,8 @@ if ($systemUserId) {
|
||||
if (statusBadge.classList.contains('status-available')) {
|
||||
availableIpsCount++;
|
||||
} else if (statusBadge.classList.contains('status-used') ||
|
||||
statusBadge.classList.contains('status-conflict')) {
|
||||
statusBadge.classList.contains('status-conflict') ||
|
||||
statusBadge.classList.contains('status-floating')) {
|
||||
usedIpsCount++;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
$segmentoPrefix = "172.16.$segmento.";
|
||||
$segmentIps = [];
|
||||
@ -364,6 +388,7 @@ class IpSearchService
|
||||
),
|
||||
'data' => $ipsDisponibles,
|
||||
'used' => $segmentIps,
|
||||
'floating' => $floatingIps,
|
||||
'segment' => "172.16.$segmento.x",
|
||||
'ping_verified' => $pingVerified
|
||||
];
|
||||
@ -539,4 +564,128 @@ class IpSearchService
|
||||
|
||||
return $ipToSite;
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtiene IPs de dispositivos no autorizados (Pending Adoption) en un segmento
|
||||
* Estos dispositivos no aparecen en /devices/ips pero sí 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;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user