| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146 |
- {% extends "layout.html" %}
- {% block content %}
- <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
- <div class="max-w-7xl mx-auto p-4 md:p-8">
- <div class="flex flex-col md:flex-row justify-between items-start md:items-center mb-8 border-b border-gray-800 pb-6 gap-4">
- <div>
- <h2 class="text-3xl font-black text-white tracking-tighter italic flex items-center gap-3">
- <svg class="w-8 h-8 text-blue-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 3.055A9.001 9.001 0 1020.945 13H11V3.055z"></path><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.488 9H15V3.512A9.025 9.025 0 0120.488 9z"></path></svg>
- Live Analytics
- </h2>
- <p class="text-gray-500 font-bold mt-2 text-[10px] md:text-xs uppercase tracking-widest">Real-time AdBlock & DNS Traffic Stats</p>
- </div>
- <div id="liveIndicator" class="flex items-center gap-2 bg-green-500/10 text-green-500 px-4 py-2 rounded-lg font-black uppercase text-[10px] tracking-widest border border-green-500/20">
- <span class="relative flex h-3 w-3">
- <span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75"></span>
- <span class="relative inline-flex rounded-full h-3 w-3 bg-green-500"></span>
- </span>
- Live Updates
- </div>
- </div>
- {% if not log_enabled %}
- <div class="bg-red-500/10 border border-red-500/20 text-red-400 p-4 rounded-xl mb-8 flex items-center gap-3 text-sm font-bold">
- <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"></path></svg>
- <span><strong>Query Logging is disabled!</strong> Go to General Settings and enable Query Log to see analytics.</span>
- </div>
- {% endif %}
- <div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8">
- <div class="bg-[#12161f] border border-gray-800 p-6 rounded-3xl shadow-xl flex flex-col justify-center relative overflow-hidden">
- <svg class="w-24 h-24 absolute -right-6 -bottom-6 text-blue-500/5 pointer-events-none" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9a9 9 0 01-9-9m9 9c1.657 0 3-4.03 3-9s-1.343-9-3-9m0 18c-1.657 0-3-4.03-3-9s1.343-9 3-9m-9 9a9 9 0 019-9"></path></svg>
- <p class="text-[10px] font-black text-gray-500 uppercase tracking-widest mb-1">Total DNS Queries</p>
- <p class="text-4xl font-black text-white font-mono" id="stat-total-queries">0</p>
- </div>
- <div class="bg-[#12161f] border border-gray-800 p-6 rounded-3xl shadow-xl flex flex-col justify-center relative overflow-hidden">
- <svg class="w-24 h-24 absolute -right-6 -bottom-6 text-red-500/5 pointer-events-none" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"></path></svg>
- <p class="text-[10px] font-black text-gray-500 uppercase tracking-widest mb-1">Ads & Trackers Blocked</p>
- <p class="text-4xl font-black text-red-500 font-mono" id="stat-total-blocked">0</p>
- </div>
- <div class="bg-[#12161f] border border-gray-800 p-6 rounded-3xl shadow-xl flex flex-col justify-center relative overflow-hidden">
- <svg class="w-24 h-24 absolute -right-6 -bottom-6 text-green-500/5 pointer-events-none" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 3.055A9.001 9.001 0 1020.945 13H11V3.055z"></path><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.488 9H15V3.512A9.025 9.025 0 0120.488 9z"></path></svg>
- <p class="text-[10px] font-black text-gray-500 uppercase tracking-widest mb-1">Block Percentage</p>
- <p class="text-4xl font-black text-green-400 font-mono"><span id="stat-percentage">0</span>%</p>
- </div>
- </div>
- <div class="bg-[#12161f] border border-gray-800 p-6 rounded-3xl shadow-xl mb-8">
- <h3 class="text-xs font-black text-gray-400 uppercase tracking-widest mb-6">Traffic & Block Timeline (Last 60 Minutes)</h3>
- <div class="h-64">
- <canvas id="timelineChart"></canvas>
- </div>
- </div>
- <div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
- <div class="bg-[#12161f] border border-gray-800 p-6 rounded-3xl shadow-xl">
- <h3 class="text-xs font-black text-gray-400 uppercase tracking-widest mb-6">Top Blocked Clients (IPs)</h3>
- <div class="h-64 flex justify-center">
- <canvas id="clientsChart"></canvas>
- </div>
- </div>
- <div class="bg-[#12161f] border border-gray-800 p-6 rounded-3xl shadow-xl">
- <h3 class="text-xs font-black text-gray-400 uppercase tracking-widest mb-6">Top 10 Blocked Domains</h3>
- <div class="h-64">
- <canvas id="domainsChart"></canvas>
- </div>
- </div>
- </div>
- </div>
- <script>
- // Chart Defaults for dark theme
- Chart.defaults.color = '#6b7280';
- Chart.defaults.font.family = 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace';
- let timelineChart, clientsChart, domainsChart;
- function initCharts() {
- // 1. Timeline Chart (Line)
- const ctxTime = document.getElementById('timelineChart').getContext('2d');
- timelineChart = new Chart(ctxTime, {
- type: 'line',
- data: { labels: [], datasets: [
- { label: 'Total Queries', data: [], borderColor: '#3b82f6', backgroundColor: '#3b82f61a', borderWidth: 2, fill: true, tension: 0.4, pointRadius: 0 },
- { label: 'Blocked Requests', data: [], borderColor: '#ef4444', backgroundColor: '#ef44441a', borderWidth: 2, fill: true, tension: 0.4, pointRadius: 0 }
- ]},
- options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'top', labels: { color: '#9ca3af' } } }, scales: { y: { beginAtZero: true, grid: { color: '#1f2937' }, ticks: { color: '#6b7280' } }, x: { grid: { display: false }, ticks: { color: '#6b7280' } } } }
- });
- // 2. Clients Chart (Doughnut)
- const ctxClients = document.getElementById('clientsChart').getContext('2d');
- clientsChart = new Chart(ctxClients, {
- type: 'doughnut',
- data: { labels: [], datasets: [{ data: [], backgroundColor: ['#ef4444', '#f97316', '#f59e0b', '#eab308', '#84cc16', '#22c55e', '#10b981'], borderWidth: 0, hoverOffset: 4 }] },
- options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'right', labels: { color: '#9ca3af', boxWidth: 12 } } }, cutout: '70%' }
- });
- // 3. Domains Chart (Bar)
- const ctxDomains = document.getElementById('domainsChart').getContext('2d');
- domainsChart = new Chart(ctxDomains, {
- type: 'bar',
- data: { labels: [], datasets: [{ label: 'Block Count', data: [], backgroundColor: '#8b5cf6', borderRadius: 4 }] },
- options: { indexAxis: 'y', responsive: true, maintainAspectRatio: false, plugins: { legend: { display: false } }, scales: { x: { beginAtZero: true, grid: { color: '#1f2937' }, ticks: { color: '#6b7280' } }, y: { grid: { display: false }, ticks: { color: '#9ca3af', font: { size: 10 } } } } }
- });
- }
- function updateAnalytics() {
- fetch('/api/adblock/stats_live')
- .then(res => res.json())
- .then(data => {
- // Update Numbers
- document.getElementById('stat-total-queries').innerText = data.total_queries.toLocaleString();
- document.getElementById('stat-total-blocked').innerText = data.total_blocked.toLocaleString();
- document.getElementById('stat-percentage').innerText = data.percentage;
- // Prepare Timeline Data
- const labels = Object.keys(data.timeline).slice(-60); // last 60 minutes
- const queriesData = labels.map(l => data.timeline[l]?.rps * 60 || 0); // Convert RPS to total per min
- const blocksData = labels.map(l => (data.block_timeline[l] * 60) || 0);
- timelineChart.data.labels = labels;
- timelineChart.data.datasets[0].data = queriesData;
- timelineChart.data.datasets[1].data = blocksData;
- timelineChart.update();
- // Prepare Clients Data
- clientsChart.data.labels = data.top_clients.map(c => c[0]);
- clientsChart.data.datasets[0].data = data.top_clients.map(c => c[1]);
- clientsChart.update();
- // Prepare Domains Data
- domainsChart.data.labels = data.top_domains.map(d => d[0]);
- domainsChart.data.datasets[0].data = data.top_domains.map(d => d[1]);
- domainsChart.update();
- })
- .catch(err => console.error("Analytics fetch error:", err));
- }
- document.addEventListener('DOMContentLoaded', () => {
- initCharts();
- updateAnalytics();
- setInterval(updateAnalytics, 5000); // Live update every 5 seconds
- });
- </script>
- {% endblock %}
|