This commit is contained in:
Lizandro Guarnizo
2026-02-20 09:07:37 -05:00
parent 276af13387
commit 421a5a4d7c
10 changed files with 921 additions and 5 deletions
+277
View File
@@ -197,6 +197,107 @@ try {
</div>
</div>
</div>
<!-- Rate Limiting Monitor -->
<div class="row mt-4">
<div class="col-12">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h5><i class="fas fa-tachometer-alt"></i> Monitoreo de Rate Limiting - Facebook API</h5>
<button class="btn btn-sm btn-outline-primary" onclick="refreshRateLimitStats()">
<i class="fas fa-sync-alt"></i> Actualizar
</button>
</div>
<div class="card-body">
<!-- Status General -->
<div class="alert" id="rate-limit-status" role="alert">
<i class="fas fa-spinner fa-spin"></i> Cargando estadísticas...
</div>
<!-- Métricas de Facebook -->
<div class="row mb-4">
<div class="col-md-4">
<div class="card border-0 shadow-sm">
<div class="card-body text-center">
<h6 class="text-muted mb-2">Call Count</h6>
<div class="progress mb-2" style="height: 25px;">
<div id="fb-call-count-bar" class="progress-bar" role="progressbar" style="width: 0%">0%</div>
</div>
<small class="text-muted">Llamadas a la API</small>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card border-0 shadow-sm">
<div class="card-body text-center">
<h6 class="text-muted mb-2">Total Time</h6>
<div class="progress mb-2" style="height: 25px;">
<div id="fb-total-time-bar" class="progress-bar" role="progressbar" style="width: 0%">0%</div>
</div>
<small class="text-muted">Tiempo de procesamiento</small>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card border-0 shadow-sm">
<div class="card-body text-center">
<h6 class="text-muted mb-2">Total CPU Time</h6>
<div class="progress mb-2" style="height: 25px;">
<div id="fb-cpu-time-bar" class="progress-bar" role="progressbar" style="width: 0%">0%</div>
</div>
<small class="text-muted">Tiempo de CPU</small>
</div>
</div>
</div>
</div>
<!-- Límites Locales -->
<div class="row">
<div class="col-md-12">
<h6 class="mb-3"><i class="fas fa-shield-alt"></i> Límites Locales (Redis)</h6>
</div>
<div class="col-md-4">
<div class="card bg-light">
<div class="card-body">
<h6>Por Segundo</h6>
<h4 id="local-limit-second" class="mb-0">-</h4>
<small class="text-muted">Límite: <span id="local-limit-second-max">80</span>/s</small>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card bg-light">
<div class="card-body">
<h6>Por Hora</h6>
<h4 id="local-limit-hour" class="mb-0">-</h4>
<small class="text-muted">Límite: <span id="local-limit-hour-max">1000</span>/h</small>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card bg-light">
<div class="card-body">
<h6>Por Día</h6>
<h4 id="local-limit-day" class="mb-0">-</h4>
<small class="text-muted">Límite: <span id="local-limit-day-max">10000</span>/d</small>
</div>
</div>
</div>
</div>
<!-- Alertas Recientes -->
<div class="row mt-4" id="rate-limit-alerts-section" style="display:none;">
<div class="col-12">
<h6 class="mb-3"><i class="fas fa-exclamation-triangle"></i> Alertas Recientes</h6>
<div id="rate-limit-alerts" class="list-group">
<!-- Alertas dinámicas -->
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Conversations Tab -->
@@ -984,6 +1085,182 @@ try {
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="./assets/js/app_simple.js?v=20"></script>
<!-- Rate Limiting Monitor Script -->
<script>
// Función para actualizar estadísticas de Rate Limiting
async function refreshRateLimitStats() {
try {
const response = await fetch('api/get_rate_limit_stats.php?action=current');
// Verificar que la respuesta sea válida
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const contentType = response.headers.get('content-type');
if (!contentType || !contentType.includes('application/json')) {
const text = await response.text();
console.error('Respuesta no-JSON recibida:', text.substring(0, 200));
throw new Error('El servidor no devolvió JSON válido');
}
const result = await response.json();
if (!result.success) {
throw new Error(result.error || 'Error obteniendo estadísticas');
}
const stats = result.data;
updateRateLimitUI(stats);
} catch (error) {
console.error('Error cargando rate limit stats:', error);
const statusEl = document.getElementById('rate-limit-status');
if (statusEl) {
statusEl.innerHTML =
'<i class="fas fa-exclamation-triangle"></i> Error cargando estadísticas: ' + error.message;
statusEl.className = 'alert alert-warning';
}
}
}
function updateRateLimitUI(stats) {
// Verificar que los elementos existan
const statusEl = document.getElementById('rate-limit-status');
const callCountBar = document.getElementById('fb-call-count-bar');
if (!statusEl || !callCountBar) {
console.log('Elementos del rate limit monitor no encontrados en el DOM');
return;
}
// Manejar caso donde Redis no está disponible
if (stats.status === 'unavailable' || stats.error) {
statusEl.className = 'alert alert-info';
statusEl.innerHTML = '<i class="fas fa-info-circle"></i> Rate Limiting Monitor: Redis no disponible. Las estadísticas de Facebook se mostrarán cuando se realice la próxima llamada a la API.';
// Mostrar mensaje en las barras de progreso
const metricsContainer = callCountBar.closest('.row');
if (metricsContainer) {
metricsContainer.innerHTML = '<div class="col-12"><div class="alert alert-info text-center"><i class="fas fa-info-circle"></i> Los datos de Facebook API se capturarán automáticamente en la próxima llamada. Redis: ' + (stats.error || 'No disponible') + '</div></div>';
}
return;
}
// Actualizar status general
const statusEl = document.getElementById('rate-limit-status');
let statusClass = 'alert-success';
let statusIcon = 'fa-check-circle';
let statusText = 'Sistema operando normalmente';
if (stats.status === 'critical') {
statusClass = 'alert-danger';
statusIcon = 'fa-exclamation-triangle';
statusText = '¡ALERTA! Rate limit cerca del límite crítico';
} else if (stats.status === 'warning') {
statusClass = 'alert-warning';
statusIcon = 'fa-exclamation-circle';
statusText = 'Advertencia: Acercándose al límite';
}
statusEl.className = `alert ${statusClass}`;
statusEl.innerHTML = `<i class="fas ${statusIcon}"></i> ${statusText}`;
// Actualizar métricas de Facebook
if (stats.app_usage) {
updateProgressBar('fb-call-count-bar', stats.app_usage.call_count || 0);
updateProgressBar('fb-total-time-bar', stats.app_usage.total_time || 0);
updateProgressBar('fb-cpu-time-bar', stats.app_usage.total_cputime || 0);
} else {
// Sin datos de Facebook aún
const callCountBar = document.getElementById('fb-call-count-bar');
if (callCountBar) {
const metricsRow = callCountBar.closest('.row');
if (metricsRow) {
metricsRow.innerHTML =
'<div class="col-12"><div class="alert alert-info text-center"><i class="fas fa-info-circle"></i> Los datos de Facebook API se capturarán automáticamente en la próxima llamada.</div></div>';
}
}
}
// Actualizar límites locales
if (stats.local_limits) {
const limitSecondEl = document.getElementById('local-limit-second-max');
const limitHourEl = document.getElementById('local-limit-hour-max');
const limitDayEl = document.getElementById('local-limit-day-max');
if (limitSecondEl) limitSecondEl.textContent = stats.local_limits.per_second;
if (limitHourEl) limitHourEl.textContent = stats.local_limits.per_hour;
if (limitDayEl) limitDayEl.textContent = stats.local_limits.per_day;
}
// Mostrar alertas si existen
const alertsSection = document.getElementById('rate-limit-alerts-section');
const alertsContainer = document.getElementById('rate-limit-alerts');
if (stats.alerts && stats.alerts.length > 0 && alertsSection && alertsContainer) {
alertsSection.style.display = 'block';
alertsContainer.innerHTML = '';
stats.alerts.slice(0, 5).forEach(alert => {
const alertClass = alert.severity === 'critical' ? 'list-group-item-danger' : 'list-group-item-warning';
const alertIcon = alert.severity === 'critical' ? 'fa-exclamation-triangle' : 'fa-exclamation-circle';
const alertEl = document.createElement('div');
alertEl.className = `list-group-item ${alertClass}`;
alertEl.innerHTML = `
<div class="d-flex w-100 justify-content-between">
<h6 class="mb-1"><i class="fas ${alertIcon}"></i> Alerta de ${alert.level}</h6>
<small>${alert.datetime}</small>
</div>
<p class="mb-1">Uso al ${alert.usage}% - Endpoint: ${alert.data.endpoint || 'N/A'}</p>
`;
alertsContainer.appendChild(alertEl);
});
} else if (alertsSection) {
alertsSection.style.display = 'none';
}
}
function updateProgressBar(id, percentage) {
const bar = document.getElementById(id);
if (!bar) return;
percentage = Math.min(100, Math.max(0, percentage));
bar.style.width = percentage + '%';
bar.textContent = percentage.toFixed(1) + '%';
// Cambiar color según el porcentaje
bar.className = 'progress-bar';
if (percentage >= 90) {
bar.classList.add('bg-danger');
} else if (percentage >= 75) {
bar.classList.add('bg-warning');
} else if (percentage >= 50) {
bar.classList.add('bg-info');
} else {
bar.classList.add('bg-success');
}
}
// Cargar estadísticas al cargar la página
document.addEventListener('DOMContentLoaded', function() {
// Cargar inmediatamente
refreshRateLimitStats();
// Actualizar cada 30 segundos
setInterval(refreshRateLimitStats, 30000);
});
// Actualizar también cuando se cambia a la pestaña de dashboard
document.querySelectorAll('.nav-link[data-tab="dashboard"]').forEach(link => {
link.addEventListener('click', function() {
setTimeout(refreshRateLimitStats, 500);
});
});
</script>
<script>
// Función de diagnóstico mejorada
function runDiagnostic() {