Característica: verificación de ping progresiva y asincrónica con procesamiento por lotes y agregar opciones de UI para límites de ping y ocultar IP's de administrador.

This commit is contained in:
DANYDHSV 2025-11-26 15:33:55 -06:00
parent 8549bab24b
commit 48024291c4
7 changed files with 507 additions and 62 deletions

View File

@ -7,6 +7,33 @@ y este proyecto adhiere a [Semantic Versioning](https://semver.org/lang/es/).
---
## [1.3.0] - 2025-11-26
### ✨ Añadido
- **Carga Progresiva**: Los resultados de la búsqueda aparecen inmediatamente y la verificación por ping se realiza en segundo plano, actualizando el estado en tiempo real.
- **Feedback Visual Mejorado**: Nuevos iconos de estado (⏳ Verificando, ✅ Disponible, ⚠️ En uso) en la tabla de resultados.
- **Filtro de IPs Administrativas**: Nuevo checkbox para ocultar/mostrar IPs de administración (1-30, 254) al instante.
### 🔄 Mejorado
- **Rendimiento**: La interfaz ya no se bloquea durante la verificación por ping.
- **Experiencia de Usuario**: Se puede ver el progreso de la verificación IP por IP.
---
## [1.2.1] - 2025-11-26
### ✨ Añadido
- **Límite de IPs para verificación**: Nuevo selector para limitar la cantidad de IPs a verificar con ping (5, 10, 20 o Todas)
- Evita timeouts en segmentos grandes
- Prioriza IPs de cliente (las administrativas se muestran sin verificar cuando hay límite)
- **Defaults mejorados**: Checkbox de verificación activado por defecto (si está habilitado en config)
### 🔄 Mejorado
- **Optimización de verificación**: Al usar un límite, las IPs administrativas se excluyen del ping para acelerar el proceso
- **Interfaz de usuario**: Feedback visual más claro sobre el límite aplicado
---
## [1.2.0] - 2025-11-26
### ✨ Añadido

View File

@ -1,6 +1,6 @@
# SIIP - Buscador de IP's Disponibles UISP
[![Version](https://img.shields.io/badge/version-1.2.0-blue.svg)](manifest.json)
[![Version](https://img.shields.io/badge/version-1.3.0-blue.svg)](manifest.json)
[![UCRM](https://img.shields.io/badge/UCRM-Compatible-green.svg)](https://uisp.com/)
[![UNMS](https://img.shields.io/badge/UNMS-Compatible-green.svg)](https://uisp.com/)
@ -142,17 +142,19 @@ La interfaz incluye:
### Verificación por Ping (Opcional)
Si está habilitada en la configuración, aparecerá un checkbox "🔍 Verificar con ping" en el formulario.
Si está habilitada en la configuración, aparecerá un checkbox "🔍 Verificar con ping" (marcado por defecto) y un selector de límite.
**¿Qué hace?**
- Verifica que las IPs reportadas como disponibles realmente no respondan a ping
- Detecta dispositivos no registrados en UISP (computadoras, impresoras, cámaras, etc.)
- Filtra automáticamente IPs que responden (posibles conflictos)
**Opciones de Límite:**
- **Todas (Lento)**: Verifica todas las IPs del segmento.
- **5, 10, 20 IPs (Rápido)**: Verifica solo las primeras N IPs disponibles para clientes.
**¿Cuándo usarlo?**
- ✅ Antes de asignar IPs a clientes nuevos (máxima seguridad)
- ✅ En redes con dispositivos no gestionados
- ❌ Para búsquedas rápidas donde no importa la verificación
**Feedback Visual:**
- ⏳ **Pendiente/Verificando**: La IP está siendo analizada.
- ✅ **Disponible**: La IP está libre en UISP y no responde a ping.
- ⚠️ **En uso (Ping)**: La IP está libre en UISP pero responde a ping (posible conflicto).
### Filtrado de IPs
- **Ocultar Admin IPs**: Checkbox para ocultar/mostrar instantáneamente las IPs reservadas para administración (x.x.x.1-30 y x.x.x.254).
---
@ -213,13 +215,15 @@ Busca todas las IPs disponibles en un segmento de red específico.
- `type` (string, requerido): Tipo de evento, debe ser `"event.ip_request"`
- `segment` (string, requerido): Tercer octeto del segmento (0-255)
- `verify_ping` (boolean, opcional): Si es `true`, verifica IPs con ping antes de reportarlas
- `ping_limit` (int, opcional): Cantidad máxima de IPs a verificar (0 = todas, default: 0)
**Ejemplo con verificación por ping**:
**Ejemplo con límite**:
```json
{
"type": "event.ip_request",
"segment": "5",
"verify_ping": true
"verify_ping": true,
"ping_limit": 5
}
```
@ -235,12 +239,13 @@ Busca todas las IPs disponibles en un segmento de red específico.
},
"ping_verified": true,
"ping_stats": {
"total_checked": 223,
"responding": 1,
"not_responding": 222,
"execution_time": "2.3s"
"total_checked": 5,
"responding": 0,
"not_responding": 5,
"execution_time": "0.8s",
"limit_applied": 5
},
"ping_responding": ["172.16.5.50"]
"ping_responding": []
}
```
@ -580,5 +585,5 @@ Este plugin es propiedad de **SIIP Internet**. Todos los derechos reservados.
---
**Versión**: 1.2.0
**Versión**: 1.3.0
**Última actualización**: 26 de noviembre de 2025

View File

@ -379,3 +379,194 @@ IPs obtenidas exitosamente: 3105 direcciones
Búsqueda de IPs en segmento 172.16.100.x - Disponibles: 123, En uso: 131
Resultado de búsqueda: {"success":true,"ipsDisponibles":123,"ipsEnUso":131}
<<< Finalizando handler de búsqueda AJAX
=== NUEVA PETICIÓN ===
Método: GET
POST data: []
GET data: []
Content-Type:
User Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:145.0) Gecko/20100101 Firefox/145.0
Acceso a la interfaz pública de búsqueda de IPs
=== NUEVA PETICIÓN ===
Método: POST
POST data: {"action":"search","segment":"13"}
GET data: []
Content-Type: multipart/form-data; boundary=----geckoformboundary43e6452152194b7d2f9ee1cc1f4dcc5f
User Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:145.0) Gecko/20100101 Firefox/145.0
>>> Entrando al handler de búsqueda AJAX
Configuración cargada: {"ipserver":"sistema.siip.mx","hasUnmsToken":true,"hasApiToken":true}
Buscando IPs en segmento: 13 (sin verificación por ping)
URL de API: https://sistema.siip.mx/nms/api/v2.1/devices/ips?suspended=false&management=true&includeObsolete=true
Iniciando conexión a API: https://sistema.siip.mx/nms/api/v2.1/devices/ips?suspended=false&management=true&includeObsolete=true
Respuesta HTTP: 200
Longitud de respuesta: 48906 bytes
IPs obtenidas exitosamente: 3105 direcciones
Búsqueda de IPs en segmento 172.16.13.x - Disponibles: 153, En uso: 101
Resultado de búsqueda: {"success":true,"ipsDisponibles":153,"ipsEnUso":101}
<<< Finalizando handler de búsqueda AJAX
=== NUEVA PETICIÓN ===
Método: GET
POST data: []
GET data: []
Content-Type:
User Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:145.0) Gecko/20100101 Firefox/145.0
Acceso a la interfaz pública de búsqueda de IPs
=== NUEVA PETICIÓN ===
Método: POST
POST data: {"action":"search","segment":"13","verify_ping":"true"}
GET data: []
Content-Type: multipart/form-data; boundary=----geckoformboundarycaf9a5a4ec0cc39fcde382e2ad3254b5
User Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:145.0) Gecko/20100101 Firefox/145.0
>>> Entrando al handler de búsqueda AJAX
Configuración cargada: {"ipserver":"sistema.siip.mx","hasUnmsToken":true,"hasApiToken":true}
Buscando IPs en segmento: 13 (con verificación por ping)
URL de API: https://sistema.siip.mx/nms/api/v2.1/devices/ips?suspended=false&management=true&includeObsolete=true
Iniciando conexión a API: https://sistema.siip.mx/nms/api/v2.1/devices/ips?suspended=false&management=true&includeObsolete=true
Respuesta HTTP: 200
Longitud de respuesta: 48906 bytes
IPs obtenidas exitosamente: 3105 direcciones
Verificación por ping habilitada, iniciando verificación...
Iniciando verificación por ping de 153 IPs (lotes de 30)
Procesando lote 1/6 (30 IPs)
Lote completado: 1/30 IPs responden (29.20s)
Procesando lote 2/6 (30 IPs)
Lote completado: 1/30 IPs responden (29.35s)
Procesando lote 3/6 (30 IPs)
Lote completado: 0/30 IPs responden (30.20s)
Procesando lote 4/6 (30 IPs)
Lote completado: 1/30 IPs responden (29.23s)
Procesando lote 5/6 (30 IPs)
Lote completado: 0/30 IPs responden (30.19s)
Procesando lote 6/6 (3 IPs)
Lote completado: 0/3 IPs responden (3.11s)
ADVERTENCIA: 3 IPs responden a ping pero no están en UISP: 172.16.13.45, 172.16.13.52, 172.16.13.188
Verificación por ping completada: 153 IPs verificadas, 3 responden, 150 no responden (151.28s)
Búsqueda de IPs en segmento 172.16.13.x - Disponibles: 150, En uso: 101 (verificado con ping)
Resultado de búsqueda: {"success":true,"ipsDisponibles":150,"ipsEnUso":101}
<<< Finalizando handler de búsqueda AJAX
=== NUEVA PETICIÓN ===
Método: GET
POST data: []
GET data: []
Content-Type:
User Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:145.0) Gecko/20100101 Firefox/145.0
Acceso a la interfaz pública de búsqueda de IPs
=== NUEVA PETICIÓN ===
Método: POST
POST data: {"action":"search","segment":"13"}
GET data: []
Content-Type: multipart/form-data; boundary=----geckoformboundarycb3b0d69f1f38554c073fe54b312e6d4
User Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:145.0) Gecko/20100101 Firefox/145.0
>>> Entrando al handler de búsqueda AJAX
Configuración cargada: {"ipserver":"sistema.siip.mx","hasUnmsToken":true,"hasApiToken":true}
Buscando IPs en segmento: 13 (sin verificación por ping)
URL de API: https://sistema.siip.mx/nms/api/v2.1/devices/ips?suspended=false&management=true&includeObsolete=true
Iniciando conexión a API: https://sistema.siip.mx/nms/api/v2.1/devices/ips?suspended=false&management=true&includeObsolete=true
Respuesta HTTP: 200
Longitud de respuesta: 48906 bytes
IPs obtenidas exitosamente: 3105 direcciones
Búsqueda de IPs en segmento 172.16.13.x - Disponibles: 153, En uso: 101
Resultado de búsqueda: {"success":true,"ipsDisponibles":153,"ipsEnUso":101}
<<< Finalizando handler de búsqueda AJAX
=== NUEVA PETICIÓN ===
Método: POST
POST data: {"action":"search","segment":"13","verify_ping":"true"}
GET data: []
Content-Type: multipart/form-data; boundary=----geckoformboundaryb216276793b3e68fe84b13e1d74f9f09
User Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:145.0) Gecko/20100101 Firefox/145.0
>>> Entrando al handler de búsqueda AJAX
Configuración cargada: {"ipserver":"sistema.siip.mx","hasUnmsToken":true,"hasApiToken":true}
Buscando IPs en segmento: 13 (con verificación por ping)
URL de API: https://sistema.siip.mx/nms/api/v2.1/devices/ips?suspended=false&management=true&includeObsolete=true
Iniciando conexión a API: https://sistema.siip.mx/nms/api/v2.1/devices/ips?suspended=false&management=true&includeObsolete=true
Respuesta HTTP: 200
Longitud de respuesta: 48906 bytes
IPs obtenidas exitosamente: 3105 direcciones
Verificación por ping habilitada, iniciando verificación...
Iniciando verificación por ping de 153 IPs (lotes de 30)
Procesando lote 1/6 (30 IPs)
Lote completado: 1/30 IPs responden (29.21s)
Procesando lote 2/6 (30 IPs)
Lote completado: 1/30 IPs responden (29.23s)
Procesando lote 3/6 (30 IPs)
Lote completado: 0/30 IPs responden (30.20s)
Procesando lote 4/6 (30 IPs)
Lote completado: 1/30 IPs responden (29.20s)
Procesando lote 5/6 (30 IPs)
Lote completado: 0/30 IPs responden (30.20s)
Procesando lote 6/6 (3 IPs)
Lote completado: 0/3 IPs responden (3.11s)
ADVERTENCIA: 3 IPs responden a ping pero no están en UISP: 172.16.13.45, 172.16.13.52, 172.16.13.188
Verificación por ping completada: 153 IPs verificadas, 3 responden, 150 no responden (151.15s)
Búsqueda de IPs en segmento 172.16.13.x - Disponibles: 150, En uso: 101 (verificado con ping)
Resultado de búsqueda: {"success":true,"ipsDisponibles":150,"ipsEnUso":101}
<<< Finalizando handler de búsqueda AJAX
=== NUEVA PETICIÓN ===
Método: GET
POST data: []
GET data: []
Content-Type:
User Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:145.0) Gecko/20100101 Firefox/145.0
Acceso a la interfaz pública de búsqueda de IPs
=== NUEVA PETICIÓN ===
Método: POST
POST data: {"action":"search","segment":"100","verify_ping":"true","ping_limit":"20"}
GET data: []
Content-Type: multipart/form-data; boundary=----geckoformboundary2463648a22fbfc0977c6f46af73a71c2
User Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:145.0) Gecko/20100101 Firefox/145.0
>>> Entrando al handler de búsqueda AJAX
Configuración cargada: {"ipserver":"sistema.siip.mx","hasUnmsToken":true,"hasApiToken":true}
Buscando IPs en segmento: 100 (con verificación por ping, límite: 20 IPs)
URL de API: https://sistema.siip.mx/nms/api/v2.1/devices/ips?suspended=false&management=true&includeObsolete=true
Iniciando conexión a API: https://sistema.siip.mx/nms/api/v2.1/devices/ips?suspended=false&management=true&includeObsolete=true
Respuesta HTTP: 200
Longitud de respuesta: 48906 bytes
IPs obtenidas exitosamente: 3105 direcciones
Verificación por ping habilitada, iniciando verificación...
Aplicando límite de ping: 20 IPs de cliente (Total disponibles: 105)
Iniciando verificación por ping de 20 IPs (lotes de 30)
Procesando lote 1/1 (20 IPs)
Lote completado: 0/20 IPs responden (20.18s)
Verificación por ping completada: 20 IPs verificadas, 0 responden, 20 no responden (20.18s)
Búsqueda de IPs en segmento 172.16.100.x - Disponibles: 123, En uso: 131 (verificado con ping)
Resultado de búsqueda: {"success":true,"ipsDisponibles":123,"ipsEnUso":131}
<<< Finalizando handler de búsqueda AJAX
=== NUEVA PETICIÓN ===
Método: POST
POST data: {"action":"search","segment":"100"}
GET data: []
Content-Type: multipart/form-data; boundary=----geckoformboundary5753dc1b36d69572108222d59aa55e38
User Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:145.0) Gecko/20100101 Firefox/145.0
>>> Entrando al handler de búsqueda AJAX
Configuración cargada: {"ipserver":"sistema.siip.mx","hasUnmsToken":true,"hasApiToken":true}
Buscando IPs en segmento: 100 (sin verificación por ping)
URL de API: https://sistema.siip.mx/nms/api/v2.1/devices/ips?suspended=false&management=true&includeObsolete=true
Iniciando conexión a API: https://sistema.siip.mx/nms/api/v2.1/devices/ips?suspended=false&management=true&includeObsolete=true
Respuesta HTTP: 200
Longitud de respuesta: 48906 bytes
IPs obtenidas exitosamente: 3105 direcciones
Búsqueda de IPs en segmento 172.16.100.x - Disponibles: 123, En uso: 131
Resultado de búsqueda: {"success":true,"ipsDisponibles":123,"ipsEnUso":131}
<<< Finalizando handler de búsqueda AJAX
=== NUEVA PETICIÓN ===
Método: POST
POST data: {"action":"search","segment":"100","verify_ping":"true","ping_limit":"5"}
GET data: []
Content-Type: multipart/form-data; boundary=----geckoformboundary3aee2ff80e73e922f743d236ac2c22c9
User Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:145.0) Gecko/20100101 Firefox/145.0
>>> Entrando al handler de búsqueda AJAX
Configuración cargada: {"ipserver":"sistema.siip.mx","hasUnmsToken":true,"hasApiToken":true}
Buscando IPs en segmento: 100 (con verificación por ping, límite: 5 IPs)
URL de API: https://sistema.siip.mx/nms/api/v2.1/devices/ips?suspended=false&management=true&includeObsolete=true
Iniciando conexión a API: https://sistema.siip.mx/nms/api/v2.1/devices/ips?suspended=false&management=true&includeObsolete=true
Respuesta HTTP: 200
Longitud de respuesta: 48906 bytes
IPs obtenidas exitosamente: 3105 direcciones
Verificación por ping habilitada, iniciando verificación...
Aplicando límite de ping: 5 IPs de cliente (Total disponibles: 105)
Iniciando verificación por ping de 5 IPs (lotes de 30)
Procesando lote 1/1 (5 IPs)
Lote completado: 0/5 IPs responden (5.14s)
Verificación por ping completada: 5 IPs verificadas, 0 responden, 5 no responden (5.15s)
Búsqueda de IPs en segmento 172.16.100.x - Disponibles: 123, En uso: 131 (verificado con ping)
Resultado de búsqueda: {"success":true,"ipsDisponibles":123,"ipsEnUso":131}
<<< Finalizando handler de búsqueda AJAX

View File

@ -5,7 +5,7 @@
"displayName": "SIIP - Buscador de IP's Disponibles UISP",
"description": "Este plugin permite buscar IP's disponibles en UISP (UNMS) y asignarlas a los clientes en UCRM. Evitando así la asignación de IP's duplicadas y mejorando la gestión de direcciones IP en la red.",
"url": "https://siip.mx",
"version": "1.2.0",
"version": "1.3.0",
"ucrmVersionCompliancy": {
"min": "1.0.0",
"max": null

View File

@ -4,6 +4,9 @@
error_reporting(E_ALL);
ini_set('display_errors', 0); // No mostrar errores en HTML
// Aumentar tiempo de ejecución para verificación por ping (puede tardar varios minutos)
set_time_limit(300); // 5 minutos
// Handler global de errores
set_error_handler(function($errno, $errstr, $errfile, $errline) {
throw new ErrorException($errstr, 0, $errno, $errfile, $errline);
@ -114,13 +117,13 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['
}
$segmento = $_POST['segment'] ?? '';
$verifyPing = isset($_POST['verify_ping']) && $_POST['verify_ping'] === 'true';
// NOTA: Ignoramos verify_ping aquí porque ahora se hace progresivamente desde el frontend
// Pero lo logueamos para saber la intención del usuario
$verifyPingIntention = isset($_POST['verify_ping']) && $_POST['verify_ping'] === 'true';
if ($verifyPing) {
$log->appendLog("Buscando IPs en segmento: $segmento (con verificación por ping)");
} else {
$log->appendLog("Buscando IPs en segmento: $segmento (sin verificación por ping)");
}
$log->appendLog("Buscando IPs en segmento: $segmento (Búsqueda inicial rápida)");
// URL de la API de UISP - Usar HTTPS
// URL de la API de UISP - Usar HTTPS
$apiUrl = "https://{$config['ipserver']}/nms/api/v2.1/devices/ips?suspended=false&management=true&includeObsolete=true";
@ -128,9 +131,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['
$log->appendLog("URL de API: $apiUrl");
// Crear instancia del servicio y buscar IPs
// Crear instancia del servicio y buscar IPs (SIN ping, el ping se hace después)
$ipService = new IpSearchService($apiUrl, $apiToken, $log);
$resultado = $ipService->buscarIpsDisponibles($segmento, $verifyPing);
$resultado = $ipService->buscarIpsDisponibles($segmento, false);
$log->appendLog('Resultado de búsqueda: ' . json_encode([
'success' => $resultado['success'],
@ -163,6 +166,51 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['
$log->appendLog('<<< Finalizando handler de búsqueda AJAX');
exit;
} else if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'verify_batch') {
$log->appendLog('>>> Entrando al handler de verificación por lotes (verify_batch)');
try {
$ips = $_POST['ips'] ?? [];
if (!is_array($ips)) {
$ips = json_decode($ips, true);
}
if (empty($ips)) {
echo json_encode(['success' => true, 'results' => []]);
exit;
}
$log->appendLog('Verificando lote de ' . count($ips) . ' IPs');
// Crear instancia de PingService
$pingService = new PingService($log, 1, count($ips));
if (!$pingService->isAvailable()) {
echo json_encode([
'success' => false,
'message' => 'Comando ping no disponible'
]);
exit;
}
// Hacer ping
$pingResults = $pingService->pingMultipleIps($ips);
$processed = $pingService->processPingResults($pingResults);
// Retornar resultados
// responding = IPs que responden (ocupadas/conflicto)
// not_responding = IPs que no responden (disponibles)
echo json_encode([
'success' => true,
'responding' => $processed['responding'],
'not_responding' => $processed['not_responding']
]);
} catch (Exception $e) {
$log->appendLog('ERROR en verify_batch: ' . $e->getMessage());
echo json_encode(['success' => false, 'message' => $e->getMessage()]);
}
exit;
} else if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$log->appendLog('Petición POST recibida pero sin action=search o action no válida');
$log->appendLog('Action recibida: ' . ($_POST['action'] ?? 'NO DEFINIDA'));
@ -174,7 +222,7 @@ $log->appendLog('Acceso a la interfaz pública de búsqueda de IPs');
// Cargar configuración para verificar si ping está habilitado
$configManager = PluginConfigManager::create();
$config = $configManager->loadConfig();
$pingEnabled = isset($config['enablePingVerification']) && $config['enablePingVerification'] === '1';
$pingEnabled = !empty($config['enablePingVerification']) && ($config['enablePingVerification'] === true || $config['enablePingVerification'] === '1');
?>
<!DOCTYPE html>
@ -602,6 +650,43 @@ $pingEnabled = isset($config['enablePingVerification']) && $config['enablePingVe
grid-template-columns: 1fr;
}
}
.status-badge {
padding: 6px 12px;
border-radius: 6px;
font-size: 0.8rem;
font-weight: 600;
display: inline-flex;
align-items: center;
gap: 6px;
}
.status-pending {
background: rgba(255, 255, 255, 0.1);
color: var(--text-secondary);
}
.status-verifying {
background: rgba(102, 126, 234, 0.2);
color: #667eea;
animation: pulse 1.5s infinite;
}
.status-available {
background: rgba(79, 209, 197, 0.2);
color: #4fd1c5;
border: 1px solid rgba(79, 209, 197, 0.4);
}
.status-conflict {
background: rgba(245, 87, 108, 0.2);
color: #ff6b6b;
border: 1px solid rgba(245, 87, 108, 0.4);
}
tr.hidden-row {
display: none;
}
</style>
</head>
<body>
@ -632,10 +717,25 @@ $pingEnabled = isset($config['enablePingVerification']) && $config['enablePingVe
<?php if ($pingEnabled): ?>
<div class="input-group" style="flex: 0 0 auto; min-width: auto;">
<label style="display: flex; align-items: center; gap: 10px; cursor: pointer; margin-bottom: 0; padding-top: 28px;">
<input type="checkbox" id="verifyPing" name="verify_ping" style="width: auto; padding: 0; margin: 0;">
<input type="checkbox" id="verifyPing" name="verify_ping" style="width: auto; padding: 0; margin: 0;" checked>
<span style="color: var(--text-secondary); font-size: 0.9rem;">🔍 Verificar con ping</span>
</label>
</div>
<div class="input-group" style="flex: 0 0 auto; min-width: auto;">
<label style="display: flex; align-items: center; gap: 10px; cursor: pointer; margin-bottom: 0; padding-top: 28px;">
<input type="checkbox" id="hideAdmin" name="hide_admin" style="width: auto; padding: 0; margin: 0;">
<span style="color: var(--text-secondary); font-size: 0.9rem;">👁️ Ocultar Admin IPs</span>
</label>
</div>
<div class="input-group" id="pingLimitContainer" style="flex: 0 0 auto; min-width: 150px;">
<label for="pingLimit">Límite de IPs</label>
<select id="pingLimit" name="ping_limit" style="width: 100%; padding: 16px; background: rgba(255, 255, 255, 0.05); border: 2px solid var(--card-border); border-radius: 12px; color: var(--text-primary); font-size: 1rem;">
<option value="0">Todas (Lento)</option>
<option value="5">5 IPs (Rápido)</option>
<option value="10">10 IPs</option>
<option value="20">20 IPs</option>
</select>
</div>
<?php endif; ?>
<button type="submit" class="btn btn-primary" id="searchBtn">
<span>🔍</span>
@ -645,7 +745,10 @@ $pingEnabled = isset($config['enablePingVerification']) && $config['enablePingVe
<div class="loading" id="loading">
<div class="spinner"></div>
<p>Consultando IPs disponibles...</p>
<p id="loadingMessage">Consultando IPs disponibles...</p>
<p id="loadingPingWarning" style="display: none; color: var(--text-secondary); font-size: 0.9rem; margin-top: 10px;">
⏱️ Verificación por ping habilitada. Esto puede tardar varios minutos...
</p>
</div>
<div id="errorContainer"></div>
@ -673,6 +776,7 @@ $pingEnabled = isset($config['enablePingVerification']) && $config['enablePingVe
<th>#</th>
<th>Dirección IP</th>
<th>Tipo de IP</th>
<th>Estado</th>
<th>Acción</th>
</tr>
</thead>
@ -699,6 +803,17 @@ $pingEnabled = isset($config['enablePingVerification']) && $config['enablePingVe
const usedCount = document.getElementById('usedCount');
const segmentDisplay = document.getElementById('segmentDisplay');
// Toggle visibility of ping limit select
<?php if ($pingEnabled): ?>
const verifyPingCheckbox = document.getElementById('verifyPing');
const pingLimitContainer = document.getElementById('pingLimitContainer');
if (verifyPingCheckbox && pingLimitContainer) {
verifyPingCheckbox.addEventListener('change', function() {
pingLimitContainer.style.display = this.checked ? 'block' : 'none';
});
}
<?php endif; ?>
searchForm.addEventListener('submit', async (e) => {
e.preventDefault();
@ -712,6 +827,14 @@ $pingEnabled = isset($config['enablePingVerification']) && $config['enablePingVe
}
// Mostrar loading
<?php if ($pingEnabled): ?>
const verifyPingCheckbox = document.getElementById('verifyPing');
const loadingPingWarning = document.getElementById('loadingPingWarning');
if (verifyPingCheckbox && verifyPingCheckbox.checked) {
loadingPingWarning.style.display = 'block';
}
<?php endif; ?>
loading.classList.add('active');
results.classList.remove('active');
errorContainer.innerHTML = '';
@ -723,13 +846,17 @@ $pingEnabled = isset($config['enablePingVerification']) && $config['enablePingVe
formData.append('segment', segment);
// Agregar verificación por ping si está habilitada y marcada
<?php if ($pingEnabled): ?>
const verifyPingCheckbox = document.getElementById('verifyPing');
if (verifyPingCheckbox && verifyPingCheckbox.checked) {
formData.append('verify_ping', 'true');
console.log('Verificación por ping habilitada');
}
<?php endif; ?>
<?php if ($pingEnabled): ?>
const verifyPingCheckbox = document.getElementById('verifyPing');
const pingLimitSelect = document.getElementById('pingLimit');
const shouldVerifyPing = verifyPingCheckbox && verifyPingCheckbox.checked;
const pingLimit = pingLimitSelect ? parseInt(pingLimitSelect.value) : 0;
// NOTA: Ya no enviamos verify_ping=true al backend para la búsqueda inicial
// La verificación se hará progresivamente después de recibir la lista
// Solo lo enviamos como 'false' para que el backend sepa que no debe verificar
formData.append('verify_ping', 'false');
<?php endif; ?>
console.log('Enviando petición AJAX...');
console.log('Segmento:', segment);
@ -755,15 +882,28 @@ $pingEnabled = isset($config['enablePingVerification']) && $config['enablePingVe
console.log('Datos recibidos:', data);
loading.classList.remove('active');
<?php if ($pingEnabled): ?>
loadingPingWarning.style.display = 'none';
<?php endif; ?>
searchBtn.disabled = false;
if (data.success) {
displayResults(data);
// Iniciar verificación progresiva si corresponde
<?php if ($pingEnabled): ?>
if (shouldVerifyPing) {
runProgressiveVerification(data.data, pingLimit);
}
<?php endif; ?>
} else {
showError(data.message || 'Error al buscar IPs disponibles');
}
} catch (error) {
loading.classList.remove('active');
<?php if ($pingEnabled): ?>
loadingPingWarning.style.display = 'none'; // Ocultar advertencia
<?php endif; ?>
searchBtn.disabled = false;
console.error('Error completo:', error);
console.error('Tipo de error:', error.name);
@ -789,17 +929,27 @@ $pingEnabled = isset($config['enablePingVerification']) && $config['enablePingVe
}
// Llenar tabla
data.data.forEach((ip, index) => {
const ipType = getIpType(ip);
data.data.forEach((ipData, index) => {
const ip = ipData.ip;
const ipTypeLabel = getIpType(ip);
const hideAdminCheckbox = document.getElementById('hideAdmin');
const isHidden = hideAdminCheckbox && hideAdminCheckbox.checked && ipTypeLabel === 'Administración';
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');
row.innerHTML = `
<td>${index + 1}</td>
<td><span class="ip-address">${ip}</span></td>
<td>
<span class="ip-type-badge ip-type-${ipType.type}">
${ipType.label}
<span class="ip-type-badge ip-type-${ipTypeLabel === 'Administración' ? 'admin' : 'client'}">
${ipTypeLabel}
</span>
</td>
<td id="status-${ip.replace(/\./g, '-')}">
<span class="status-badge status-pending">Pendiente</span>
</td>
<td>
<button class="btn-copy" onclick="copyToClipboard('${ip}', this)">
📋 Copiar
@ -816,22 +966,27 @@ $pingEnabled = isset($config['enablePingVerification']) && $config['enablePingVe
showError(data.message, 'success');
}
// Listener para el filtro de Admin IPs
const hideAdminCheckbox = document.getElementById('hideAdmin');
if (hideAdminCheckbox) {
hideAdminCheckbox.addEventListener('change', function() {
const adminRows = document.querySelectorAll('.admin-row');
adminRows.forEach(row => {
if (this.checked) {
row.classList.add('hidden-row');
} else {
row.classList.remove('hidden-row');
}
});
});
}
// Función para clasificar el tipo de IP
function getIpType(ip) {
const parts = ip.split('.');
const lastOctet = parseInt(parts[3]);
// IPs administrativas: 1-30 y 254
if ((lastOctet >= 1 && lastOctet <= 30) || lastOctet === 254) {
return {
type: 'admin',
label: 'Administración',
recommended: false
};
}
// IPs aptas para clientes: 31-253
return {
type: 'client',
label: 'Apta para cliente',
recommended: true

View File

@ -71,9 +71,11 @@ function handleIpRequest($data, $log) {
$segment = $data['segment'];
$verifyPing = isset($data['verify_ping']) && $data['verify_ping'] === true;
$pingLimit = isset($data['ping_limit']) ? intval($data['ping_limit']) : 0;
if ($verifyPing) {
$log->appendLog("API: Verificación por ping habilitada para segmento $segment");
$limitMsg = $pingLimit > 0 ? "límite: $pingLimit IPs" : "sin límite";
$log->appendLog("API: Verificación por ping habilitada para segmento $segment ($limitMsg)");
} else {
$log->appendLog("API: Buscando IPs en segmento $segment (sin verificación por ping)");
}
@ -96,7 +98,7 @@ function handleIpRequest($data, $log) {
$apiToken = $config['unmsApiToken'];
$ipService = new \SiipAvailableIps\IpSearchService($apiUrl, $apiToken, $log);
$resultado = $ipService->buscarIpsDisponibles($segment, $verifyPing);
$resultado = $ipService->buscarIpsDisponibles($segment, $verifyPing, $pingLimit);
// Formatear respuesta para API
if ($resultado['success']) {

View File

@ -73,7 +73,15 @@ class IpSearchService
* @param bool $verifyPing Si es true, verifica las IPs con ping antes de reportarlas como disponibles
* @return array Array con 'success', 'data' (IPs disponibles), 'used' (IPs en uso), 'message', 'ping_verified', 'ping_stats'
*/
public function buscarIpsDisponibles($segmento, $verifyPing = false)
/**
* Busca IPs disponibles en un segmento
*
* @param string $segmento Tercer octeto de la IP (ej: 5 para 172.16.5.x)
* @param bool $verifyPing Si es true, verifica disponibilidad con ping
* @param int $pingLimit Límite de IPs a verificar (0 = todas)
* @return array Resultado de la búsqueda
*/
public function buscarIpsDisponibles($segmento, $verifyPing = false, $pingLimit = 0)
{
try {
// Validar el segmento
@ -139,9 +147,45 @@ class IpSearchService
$this->logger->appendLog('ADVERTENCIA: Comando ping no disponible, se omitirá verificación');
}
} else {
// Hacer ping a las IPs "disponibles"
// Preparar IPs para ping
$ipsToPing = $ipsDisponibles;
// Si hay límite, filtrar IPs administrativas y limitar IPs de cliente
if ($pingLimit > 0) {
$adminIps = [];
$clientIps = [];
foreach ($ipsDisponibles as $ip) {
$parts = explode('.', $ip);
$lastOctet = intval(end($parts));
// IPs administrativas: 1-30 y 254
if (($lastOctet >= 1 && $lastOctet <= 30) || $lastOctet === 254) {
$adminIps[] = $ip;
} else {
$clientIps[] = $ip;
}
}
// Tomar solo las primeras N IPs de cliente
$clientIpsToPing = array_slice($clientIps, 0, $pingLimit);
// IPs a verificar = IPs de cliente limitadas
// (Las administrativas NO se verifican con ping en este modo para ahorrar tiempo)
$ipsToPing = $clientIpsToPing;
if ($this->logger) {
$this->logger->appendLog(sprintf(
'Aplicando límite de ping: %d IPs de cliente (Total disponibles: %d)',
count($ipsToPing),
count($clientIps)
));
}
}
// Hacer ping a las IPs seleccionadas
$startTime = microtime(true);
$pingResults = $pingService->pingMultipleIps($ipsDisponibles);
$pingResults = $pingService->pingMultipleIps($ipsToPing);
$executionTime = microtime(true) - $startTime;
// Procesar resultados
@ -158,16 +202,37 @@ class IpSearchService
));
}
// Remover IPs que responden de la lista de disponibles
$ipsDisponibles = array_values(array_diff($ipsDisponibles, $pingResponding));
// Filtrar IPs que responden
if ($pingLimit > 0) {
// Si hay límite, reconstruimos la lista de disponibles:
// Admin IPs (sin verificar) + Client IPs verificadas (que no respondieron)
// Las Client IPs que excedieron el límite NO se incluyen en el resultado
$clientIpsVerified = array_diff($clientIpsToPing, $pingResponding);
$ipsDisponibles = array_merge($adminIps, $clientIpsVerified);
// Reordenar para mantener consistencia
usort($ipsDisponibles, function($a, $b) {
return intval(explode('.', $a)[3]) - intval(explode('.', $b)[3]);
});
} else {
// Comportamiento normal: filtrar de la lista completa
$ipsDisponibles = array_values(array_diff($ipsDisponibles, $pingResponding));
}
}
$pingVerified = true;
// Calcular estadísticas
$totalChecked = count($ipsToPing);
$respondingCount = count($pingResponding);
$notRespondingCount = $totalChecked - $respondingCount;
$pingStats = [
'total_checked' => $processed['stats']['total_checked'],
'responding' => $processed['stats']['responding'],
'not_responding' => $processed['stats']['not_responding'],
'execution_time' => round($executionTime, 2) . 's'
'total_checked' => $totalChecked,
'responding' => $respondingCount,
'not_responding' => $notRespondingCount,
'execution_time' => number_format($executionTime, 2) . 's',
'limit_applied' => $pingLimit > 0 ? $pingLimit : false
];
if ($this->logger) {