PHP Classes

File: dashboard/assets/js/dashboard.js

Recommend this page to a friend!
  Packages of Moises Espindola   Sentinela   dashboard/assets/js/dashboard.js   Download  
File: dashboard/assets/js/dashboard.js
Role: Auxiliary data
Content type: text/plain
Description: Auxiliary data
Class: Sentinela
Linux protection tools with Web dashboard panel
Author: By
Last change:
Date: 6 days ago
Size: 12,891 bytes
 

Contents

Class file image Download
/** * Sentinela ? Intelligent Linux Security Framework * Dashboard JavaScript * * Bootstrap 5 + Chart.js + AJAX puro */ // ========================================================================= // State // ========================================================================= let state = { attacks: [], banned: [], attackTypesChart: null, topCountriesChart: null, updateInterval: null }; // ========================================================================= // API Calls // ========================================================================= const API = { get: async (action, params = {}) => { const url = new URL('api/index.php', window.location.href); url.searchParams.set('action', action); Object.entries(params).forEach(([k, v]) => url.searchParams.set(k, v)); try { const res = await fetch(url.toString()); return await res.json(); } catch (e) { console.error('API Error:', e); return null; } }, post: async (action, params = {}) => { const url = new URL('api/index.php', window.location.href); url.searchParams.set('action', action); try { const res = await fetch(url.toString(), { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams(params) }); return await res.json(); } catch (e) { console.error('API Error:', e); return null; } } }; // ========================================================================= // Dashboard Updates // ========================================================================= function formatTimestamp(ts) { if (!ts) return '--'; // [2026-07-01 15:30:00] const match = ts.match(/\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/); if (match) { const d = new Date(match[0].replace(' ', 'T') + 'Z'); return d.toLocaleTimeString('es-MX', { hour: '2-digit', minute: '2-digit', second: '2-digit' }); } return ts; } function getAttackBadge(type) { const badges = { sqli: 'danger', xss: 'warning', lfi: 'info', rfi: 'primary', command_injection: 'danger', path_traversal: 'warning', scanner: 'secondary', tool: 'dark', ddos: 'danger', ssh_bruteforce: 'warning', log4shell: 'danger', xxe: 'info', honey: 'success', reputation: 'primary' }; return badges[type] || 'secondary'; } function getAttackIcon(type) { const icons = { sqli: 'bi-database', xss: 'bi-bug', lfi: 'bi-folder2-open', rfi: 'bi-globe2', command_injection: 'bi-terminal', path_traversal: 'bi-folder-symlink', scanner: 'bi-search', tool: 'bi-tools', ddos: 'bi-water', ssh_bruteforce: 'bi-key', log4shell: 'bi-explosion', xxe: 'bi-filetype-xml', honey: 'bi-flower1', reputation: 'bi-shield' }; return icons[type] || 'bi-shield-exclamation'; } async function updateServerStats() { const data = await API.get('status'); if (!data || !data.success) return; const sys = data.system; // CPU const cpuVal = parseFloat(sys.cpu) || 0; document.getElementById('cpuValue').textContent = cpuVal.toFixed(1) + '%'; document.getElementById('cpuBar').style.width = cpuVal + '%'; // RAM const memVal = parseFloat(sys.mem_percent) || 0; document.getElementById('memValue').textContent = memVal.toFixed(1) + '%'; document.getElementById('memBar').style.width = memVal + '%'; // Disk document.getElementById('diskValue').textContent = sys.disk || '0%'; const diskNum = parseInt(sys.disk) || 0; document.getElementById('diskBar').style.width = diskNum + '%'; // Load document.getElementById('loadValue').textContent = parseFloat(sys.load_1 || 0).toFixed(2); document.getElementById('uptimeValue').textContent = sys.uptime_days + ' días en línea'; document.getElementById('serverHostname').textContent = '? ' + sys.hostname; // Quick stats document.getElementById('totalAttacks').textContent = data.attack_types ? Object.values(data.attack_types).reduce((a, b) => a + b, 0) : 0; document.getElementById('totalBanned').textContent = data.banned_count || 0; document.getElementById('totalConnections').textContent = sys.connections || 0; document.getElementById('uptimeDays').textContent = sys.uptime_days || 0; // Firewall status const statusBadge = document.getElementById('firewallStatus'); if (data.firewall && data.firewall.enabled) { statusBadge.className = 'badge bg-success'; statusBadge.textContent = 'ACTIVO'; } else { statusBadge.className = 'badge bg-danger'; statusBadge.textContent = 'INACTIVO'; } // Recent attacks updateRecentAttacks(data.recent_attacks || []); } async function updateStats() { const data = await API.get('stats'); if (!data || !data.success) return; state.attacks = data.attack_types || {}; // Attack Types Chart updateAttackTypesChart(data.attack_types || {}); // Top Countries Chart updateTopCountriesChart(data.top_countries || {}); // Top IPs Table updateTopIPsTable(data.top_ips || {}); } async function updateBanned() { const data = await API.get('banned'); if (!data || !data.success) return; state.banned = data.banned || []; updateBannedTable(state.banned); } function updateRecentAttacks(attacks) { const tbody = document.getElementById('recentAttacksBody'); tbody.innerHTML = attacks.slice(0, 20).map(a => ` <tr> <td class="text-muted">${formatTimestamp(a.timestamp)}</td> <td> <span class="text-info">${a.ip || '0.0.0.0'}</span> </td> <td> <span class="badge bg-${getAttackBadge(a.type)}"> <i class="bi ${getAttackIcon(a.type)}"></i> ${a.type || 'unknown'} </span> </td> </tr> `).join(''); } function updateAttackTypesChart(types) { const labels = Object.keys(types); const values = Object.values(types); const colors = { sqli: '#ff5252', xss: '#ffd740', lfi: '#40c4ff', rfi: '#7c4dff', command_injection: '#ff1744', path_traversal: '#ffab40', scanner: '#78909c', tool: '#424242', ddos: '#d50000', ssh_bruteforce: '#ff9100', log4shell: '#ff3d00', xxe: '#00bcd4', honey: '#00e676', reputation: '#2979ff' }; const ctx = document.getElementById('attackTypesChart').getContext('2d'); if (state.attackTypesChart) { state.attackTypesChart.destroy(); } state.attackTypesChart = new Chart(ctx, { type: 'doughnut', data: { labels: labels.map(l => l.toUpperCase()), datasets: [{ data: values, backgroundColor: labels.map(l => colors[l] || '#607d8b'), borderWidth: 0 }] }, options: { responsive: true, maintainAspectRatio: true, plugins: { legend: { position: 'right', labels: { color: '#e0e0e0', font: { size: 10, family: 'monospace' }, padding: 8, boxWidth: 12, boxHeight: 12 } } } } }); } function updateTopCountriesChart(countries) { const ctx = document.getElementById('topCountriesChart').getContext('2d'); const entries = Object.entries(countries).slice(0, 10); const labels = entries.map(([k]) => k); const values = entries.map(([, v]) => v); if (state.topCountriesChart) { state.topCountriesChart.destroy(); } state.topCountriesChart = new Chart(ctx, { type: 'bar', data: { labels: labels, datasets: [{ label: 'Ataques', data: values, backgroundColor: '#40c4ff', borderColor: '#0288d1', borderWidth: 1, borderRadius: 4 }] }, options: { indexAxis: 'y', responsive: true, maintainAspectRatio: true, plugins: { legend: { display: false } }, scales: { x: { ticks: { color: '#6c8a9e', font: { size: 10 } }, grid: { color: 'rgba(255,255,255,0.05)' } }, y: { ticks: { color: '#e0e0e0', font: { size: 11, family: 'monospace' } }, grid: { display: false } } } } }); } function updateTopIPsTable(ips) { const tbody = document.getElementById('topIPsBody'); tbody.innerHTML = Object.entries(ips).slice(0, 20).map(([ip, count]) => ` <tr> <td><span class="text-info">${ip}</span></td> <td class="text-end fw-bold">${count}</td> <td class="text-end"> <button class="btn btn-sm btn-outline-danger px-2 py-0" onclick="banIP('${ip}')"> <i class="bi bi-lock"></i> </button> </td> </tr> `).join(''); } function updateBannedTable(banned) { const tbody = document.getElementById('bannedIPsBody'); if (banned.length === 0) { tbody.innerHTML = '<tr><td colspan="3" class="text-center text-muted py-3">No hay IPs bloqueadas</td></tr>'; return; } tbody.innerHTML = banned.map(b => { const remaining = b.timeout > 0 ? formatTime(b.timeout) : 'Permanente'; return ` <tr> <td><span class="text-warning">${b.ip}</span></td> <td class="text-muted">${remaining}</td> <td class="text-end"> <button class="btn btn-sm btn-outline-success px-2 py-0" onclick="unbanIP('${b.ip}')"> <i class="bi bi-unlock"></i> </button> </td> </tr> `; }).join(''); } function formatTime(seconds) { if (seconds <= 0) return 'Permanente'; const m = Math.floor(seconds / 60); const s = seconds % 60; if (m > 60) { const h = Math.floor(m / 60); return `${h}h ${m % 60}m`; } return `${m}m ${s}s`; } // ========================================================================= // Actions // ========================================================================= async function unbanIP(ip) { const modal = new bootstrap.Modal(document.getElementById('confirmModal')); document.getElementById('confirmModalBody').textContent = `¿Desbloquear IP ${ip}?`; document.getElementById('confirmModalBtn').onclick = async () => { await API.post('unban', { ip }); modal.hide(); refreshAll(); }; modal.show(); } async function banIP(ip) { const modal = new bootstrap.Modal(document.getElementById('confirmModal')); document.getElementById('confirmModalBody').textContent = `¿Bloquear IP ${ip}?`; document.getElementById('confirmModalBtn').onclick = async () => { await API.post('ban', { ip }); modal.hide(); refreshAll(); }; modal.show(); } async function flushBanned() { const modal = new bootstrap.Modal(document.getElementById('confirmModal')); document.getElementById('confirmModalBody').textContent = '¿Liberar TODAS las IPs bloqueadas?'; document.getElementById('confirmModalBtn').onclick = async () => { await fetch('api/index.php?action=flush'); modal.hide(); refreshAll(); }; modal.show(); } // ========================================================================= // Refresh // ========================================================================= async function refreshAll() { document.getElementById('lastUpdate').textContent = new Date().toLocaleTimeString('es-MX'); await Promise.all([ updateServerStats(), updateStats(), updateBanned() ]); } // ========================================================================= // Init // ========================================================================= document.addEventListener('DOMContentLoaded', () => { refreshAll(); // Auto-refresh cada 10 segundos state.updateInterval = setInterval(refreshAll, 10000); });