adanalytics.html 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. {% extends "layout.html" %}
  2. {% block content %}
  3. <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
  4. <div class="max-w-7xl mx-auto p-4 md:p-8">
  5. <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">
  6. <div>
  7. <h2 class="text-3xl font-black text-white tracking-tighter italic flex items-center gap-3">
  8. <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>
  9. Live Analytics
  10. </h2>
  11. <p class="text-gray-500 font-bold mt-2 text-[10px] md:text-xs uppercase tracking-widest">Real-time AdBlock & DNS Traffic Stats</p>
  12. </div>
  13. <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">
  14. <span class="relative flex h-3 w-3">
  15. <span class="animate-ping absolute inline-flex h-full w-full rounded-full bg-green-400 opacity-75"></span>
  16. <span class="relative inline-flex rounded-full h-3 w-3 bg-green-500"></span>
  17. </span>
  18. Live Updates
  19. </div>
  20. </div>
  21. {% if not log_enabled %}
  22. <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">
  23. <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>
  24. <span><strong>Query Logging is disabled!</strong> Go to General Settings and enable Query Log to see analytics.</span>
  25. </div>
  26. {% endif %}
  27. <div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8">
  28. <div class="bg-[#12161f] border border-gray-800 p-6 rounded-3xl shadow-xl flex flex-col justify-center relative overflow-hidden">
  29. <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>
  30. <p class="text-[10px] font-black text-gray-500 uppercase tracking-widest mb-1">Total DNS Queries</p>
  31. <p class="text-4xl font-black text-white font-mono" id="stat-total-queries">0</p>
  32. </div>
  33. <div class="bg-[#12161f] border border-gray-800 p-6 rounded-3xl shadow-xl flex flex-col justify-center relative overflow-hidden">
  34. <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>
  35. <p class="text-[10px] font-black text-gray-500 uppercase tracking-widest mb-1">Ads & Trackers Blocked</p>
  36. <p class="text-4xl font-black text-red-500 font-mono" id="stat-total-blocked">0</p>
  37. </div>
  38. <div class="bg-[#12161f] border border-gray-800 p-6 rounded-3xl shadow-xl flex flex-col justify-center relative overflow-hidden">
  39. <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>
  40. <p class="text-[10px] font-black text-gray-500 uppercase tracking-widest mb-1">Block Percentage</p>
  41. <p class="text-4xl font-black text-green-400 font-mono"><span id="stat-percentage">0</span>%</p>
  42. </div>
  43. </div>
  44. <div class="bg-[#12161f] border border-gray-800 p-6 rounded-3xl shadow-xl mb-8">
  45. <h3 class="text-xs font-black text-gray-400 uppercase tracking-widest mb-6">Traffic & Block Timeline (Last 60 Minutes)</h3>
  46. <div class="h-64">
  47. <canvas id="timelineChart"></canvas>
  48. </div>
  49. </div>
  50. <div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
  51. <div class="bg-[#12161f] border border-gray-800 p-6 rounded-3xl shadow-xl">
  52. <h3 class="text-xs font-black text-gray-400 uppercase tracking-widest mb-6">Top Blocked Clients (IPs)</h3>
  53. <div class="h-64 flex justify-center">
  54. <canvas id="clientsChart"></canvas>
  55. </div>
  56. </div>
  57. <div class="bg-[#12161f] border border-gray-800 p-6 rounded-3xl shadow-xl">
  58. <h3 class="text-xs font-black text-gray-400 uppercase tracking-widest mb-6">Top 10 Blocked Domains</h3>
  59. <div class="h-64">
  60. <canvas id="domainsChart"></canvas>
  61. </div>
  62. </div>
  63. </div>
  64. </div>
  65. <script>
  66. // Chart Defaults for dark theme
  67. Chart.defaults.color = '#6b7280';
  68. Chart.defaults.font.family = 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace';
  69. let timelineChart, clientsChart, domainsChart;
  70. function initCharts() {
  71. // 1. Timeline Chart (Line)
  72. const ctxTime = document.getElementById('timelineChart').getContext('2d');
  73. timelineChart = new Chart(ctxTime, {
  74. type: 'line',
  75. data: { labels: [], datasets: [
  76. { label: 'Total Queries', data: [], borderColor: '#3b82f6', backgroundColor: '#3b82f61a', borderWidth: 2, fill: true, tension: 0.4, pointRadius: 0 },
  77. { label: 'Blocked Requests', data: [], borderColor: '#ef4444', backgroundColor: '#ef44441a', borderWidth: 2, fill: true, tension: 0.4, pointRadius: 0 }
  78. ]},
  79. 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' } } } }
  80. });
  81. // 2. Clients Chart (Doughnut)
  82. const ctxClients = document.getElementById('clientsChart').getContext('2d');
  83. clientsChart = new Chart(ctxClients, {
  84. type: 'doughnut',
  85. data: { labels: [], datasets: [{ data: [], backgroundColor: ['#ef4444', '#f97316', '#f59e0b', '#eab308', '#84cc16', '#22c55e', '#10b981'], borderWidth: 0, hoverOffset: 4 }] },
  86. options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { position: 'right', labels: { color: '#9ca3af', boxWidth: 12 } } }, cutout: '70%' }
  87. });
  88. // 3. Domains Chart (Bar)
  89. const ctxDomains = document.getElementById('domainsChart').getContext('2d');
  90. domainsChart = new Chart(ctxDomains, {
  91. type: 'bar',
  92. data: { labels: [], datasets: [{ label: 'Block Count', data: [], backgroundColor: '#8b5cf6', borderRadius: 4 }] },
  93. 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 } } } } }
  94. });
  95. }
  96. function updateAnalytics() {
  97. fetch('/api/adblock/stats_live')
  98. .then(res => res.json())
  99. .then(data => {
  100. // Update Numbers
  101. document.getElementById('stat-total-queries').innerText = data.total_queries.toLocaleString();
  102. document.getElementById('stat-total-blocked').innerText = data.total_blocked.toLocaleString();
  103. document.getElementById('stat-percentage').innerText = data.percentage;
  104. // Prepare Timeline Data
  105. const labels = Object.keys(data.timeline).slice(-60); // last 60 minutes
  106. const queriesData = labels.map(l => data.timeline[l]?.rps * 60 || 0); // Convert RPS to total per min
  107. const blocksData = labels.map(l => (data.block_timeline[l] * 60) || 0);
  108. timelineChart.data.labels = labels;
  109. timelineChart.data.datasets[0].data = queriesData;
  110. timelineChart.data.datasets[1].data = blocksData;
  111. timelineChart.update();
  112. // Prepare Clients Data
  113. clientsChart.data.labels = data.top_clients.map(c => c[0]);
  114. clientsChart.data.datasets[0].data = data.top_clients.map(c => c[1]);
  115. clientsChart.update();
  116. // Prepare Domains Data
  117. domainsChart.data.labels = data.top_domains.map(d => d[0]);
  118. domainsChart.data.datasets[0].data = data.top_domains.map(d => d[1]);
  119. domainsChart.update();
  120. })
  121. .catch(err => console.error("Analytics fetch error:", err));
  122. }
  123. document.addEventListener('DOMContentLoaded', () => {
  124. initCharts();
  125. updateAnalytics();
  126. setInterval(updateAnalytics, 5000); // Live update every 5 seconds
  127. });
  128. </script>
  129. {% endblock %}