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);
|
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++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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 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