up
This commit is contained in:
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user