// Tab switching functionality document.addEventListener('DOMContentLoaded', function() { const tabButtons = document.querySelectorAll('.tab-button'); const tabContents = document.querySelectorAll('.tab-content'); tabButtons.forEach(button => { button.addEventListener('click', () => { const targetTab = button.getAttribute('data-tab'); // Remove active class from all buttons and contents tabButtons.forEach(btn => btn.classList.remove('active')); tabContents.forEach(content => content.classList.remove('active')); // Add active class to clicked button and corresponding content button.classList.add('active'); document.getElementById(`${targetTab}-tab`).classList.add('active'); // Load analytics data when analytics tab is clicked if (targetTab === 'analytics') { loadAnalyticsData(); } }); }); // Analytics functionality function loadAnalyticsData() { const loading = document.getElementById('analytics-loading'); const chartCanvas = document.getElementById('analytics-chart'); if (!loading || !chartCanvas) { console.error('Analytics elements not found'); return; } // Show loading loading.classList.remove('hidden'); // Fetch analytics data from backend fetch('/api/analytics-data') .then(response => response.json()) .then(data => { loading.classList.add('hidden'); if (data.error) { console.error('Error loading analytics data:', data.error); return; } // Create Chart.js chart const ctx = chartCanvas.getContext('2d'); new Chart(ctx, { type: 'line', data: { labels: data.labels, datasets: [ { label: 'Anonymity Score (%)', data: data.anonymityScores, borderColor: 'rgba(75, 192, 192, 1)', backgroundColor: 'rgba(75, 192, 192, 0.2)', yAxisID: 'y', tension: 0.1 }, { label: 'Threat Level (%)', data: data.threatLevels, borderColor: 'rgba(255, 99, 132, 1)', backgroundColor: 'rgba(255, 99, 132, 0.2)', yAxisID: 'y1', tension: 0.1 } ] }, options: { responsive: true, maintainAspectRatio: false, scales: { y: { type: 'linear', display: true, position: 'left', title: { display: true, text: 'Anonymity Score (%)' }, min: 0, max: 100 }, y1: { type: 'linear', display: true, position: 'right', title: { display: true, text: 'Threat Level (%)' }, min: 0, max: 100, grid: { drawOnChartArea: false, }, } }, plugins: { title: { display: true, text: 'IP Analysis Trends Over Time' }, legend: { display: true, position: 'top' } } } }); }) .catch(error => { loading.classList.add('hidden'); console.error('Error fetching analytics data:', error); }); } // IP check functionality const ipInput = document.getElementById('ip-input'); const checkBtn = document.getElementById('check-btn'); const loading = document.getElementById('loading'); const results = document.getElementById('results'); // IP Purity Scanner functionality const purityIpInput = document.getElementById('purity-ip-input'); const purityCheckBtn = document.getElementById('purity-check-btn'); const purityLoading = document.getElementById('purity-loading'); const purityResults = document.getElementById('purity-results'); // Only allow numbers and dots in IP input ipInput.addEventListener('input', (e) => { // Remove any characters that are not numbers or dots e.target.value = e.target.value.replace(/[^0-9.]/g, ''); }); // Only allow numbers and dots in purity IP input purityIpInput.addEventListener('input', (e) => { // Remove any characters that are not numbers or dots e.target.value = e.target.value.replace(/[^0-9.]/g, ''); }); // Prevent non-numeric and non-dot characters on keypress ipInput.addEventListener('keydown', (e) => { // Allow: backspace, delete, tab, escape, enter if (['Backspace', 'Delete', 'Tab', 'Escape', 'Enter'].includes(e.key)) { return; } // Allow: Ctrl+A, Ctrl+C, Ctrl+V, Ctrl+X if ((e.ctrlKey || e.metaKey) && ['a', 'c', 'v', 'x'].includes(e.key.toLowerCase())) { return; } // Allow: Arrow keys, Home, End if (['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Home', 'End'].includes(e.key)) { return; } // Allow only numbers (0-9) and dot (.) if (!/^[0-9.]$/.test(e.key)) { e.preventDefault(); } }); // Allow Enter key to trigger check ipInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') { checkIP(); } }); checkBtn.addEventListener('click', checkIP); // Allow Enter key to trigger purity check purityIpInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') { checkPurity(); } }); purityCheckBtn.addEventListener('click', checkPurity); function checkIP() { const ip = ipInput.value.trim(); if (!ip) { showError('Please enter an IP address'); return; } // Basic IP validation const ipRegex = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/; if (!ipRegex.test(ip)) { showError('Please enter a valid IP address'); return; } // Show loading, hide results loading.classList.remove('hidden'); results.classList.add('hidden'); results.innerHTML = ''; // Call backend API fetch('/api/check-ip', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ ip: ip }) }) .then(response => response.json()) .then(data => { loading.classList.add('hidden'); if (data.error) { showError(data.error); } else { displayResults(data); } }) .catch(error => { loading.classList.add('hidden'); showError('Error connecting to server. Please try again.'); console.error('Error:', error); }); } function showError(message) { results.classList.remove('hidden'); results.innerHTML = `
${message}
`; } function displayResults(data) { // Ensure abuseConfidencePercentage is a number let abuseConfidence = data.abuseConfidencePercentage !== undefined && data.abuseConfidencePercentage !== null ? Number(data.abuseConfidencePercentage) : 0; // Handle NaN or invalid numbers if (isNaN(abuseConfidence) || abuseConfidence < 0) { abuseConfidence = 0; } let statusClass = 'status-safe'; let statusText = 'Safe'; if (abuseConfidence >= 75) { statusClass = 'status-danger'; statusText = 'High Risk'; } else if (abuseConfidence >= 25) { statusClass = 'status-warning'; statusText = 'Medium Risk'; } const usageTypes = data.usageType || 'Unknown'; const isp = data.isp || 'Unknown'; const domain = data.domain || 'Unknown'; const country = data.countryName || 'Unknown'; // Ensure totalReports is a number let reports = data.totalReports !== undefined && data.totalReports !== null ? Number(data.totalReports) : 0; // Handle NaN or invalid numbers if (isNaN(reports) || reports < 0) { reports = 0; } const lastReported = data.lastReportedAt || 'Never'; results.classList.remove('hidden'); results.innerHTML = `
${data.ipAddress || data.ip}
${statusText}
Threat Level
${abuseConfidence.toFixed(0)}%
Usage Type
${usageTypes}
ISP
${isp}
Domain
${domain}
Country
${country}
Total Reports
${reports}
Last Reported
${formatDate(lastReported)}
`; } function formatDate(dateString) { if (!dateString || dateString === 'Never') { return 'Never'; } try { const date = new Date(dateString); return date.toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric', hour: '2-digit', minute: '2-digit' }); } catch (e) { return dateString; } } // Load last reported IPs function loadLastReports(showLoading = true) { const loading = document.getElementById('last-report-loading'); const results = document.getElementById('last-report-results'); // Check if elements exist before trying to use them if (!loading || !results) { return; } if (showLoading) { loading.classList.remove('hidden'); results.innerHTML = ''; } fetch('/api/last-reports') .then(response => response.json()) .then(data => { if (showLoading) { loading.classList.add('hidden'); } if (data.error) { results.innerHTML = `
${data.error}
`; } else if (data.ips && data.ips.length > 0) { displayLastReports(data.ips); } else { results.innerHTML = `
No reported IPs found.
`; } }) .catch(error => { if (showLoading) { loading.classList.add('hidden'); } results.innerHTML = `
Error loading reported IPs. Please try again.
`; console.error('Error:', error); }); } function displayLastReports(ips) { const results = document.getElementById('last-report-results'); if (!results) return; const ipsHTML = ips.map((ipData, index) => { const abuseConfidence = ipData.abuseConfidencePercentage || 0; let statusClass = 'status-safe'; let statusText = 'Safe'; if (abuseConfidence >= 75) { statusClass = 'status-danger'; statusText = 'High Risk'; } else if (abuseConfidence >= 25) { statusClass = 'status-warning'; statusText = 'Medium Risk'; } return `
${ipData.ipAddress}
${statusText}
Threat Level
${abuseConfidence.toFixed(0)}%
Total Reports
${ipData.totalReports || 0}
Country
${ipData.countryName || 'Unknown'}
ISP
${ipData.isp || 'Unknown'}
Usage Type
${ipData.usageType || 'Unknown'}
Domain
${ipData.domain || 'Unknown'}
Distinct Users
${ipData.numDistinctUsers || 0}
Last Reported
${formatDate(ipData.lastReportedAt)}
`; }).join(''); results.innerHTML = ipsHTML; } // Report IP functionality const reportIpInput = document.getElementById('report-ip-input'); const reportCategory = document.getElementById('report-category'); const reportComment = document.getElementById('report-comment'); const submitReportBtn = document.getElementById('submit-report-btn'); const reportLoading = document.getElementById('report-loading'); const reportMessage = document.getElementById('report-message'); // Only add event listeners if elements exist if (reportIpInput) { reportIpInput.addEventListener('input', (e) => { e.target.value = e.target.value.replace(/[^0-9.]/g, ''); }); reportIpInput.addEventListener('keypress', (e) => { if (e.key === 'Enter' && reportCategory.value) { submitReport(); } }); } if (submitReportBtn) { submitReportBtn.addEventListener('click', submitReport); } function submitReport() { if (!reportIpInput || !reportCategory || !reportComment || !reportLoading || !reportMessage) { console.error('Report elements not found'); return; } const ip = reportIpInput.value.trim(); const category = reportCategory.value; const comment = reportComment.value.trim(); if (!ip) { showReportMessage('Please enter an IP address', 'error'); return; } // Basic IP validation const ipRegex = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/; if (!ipRegex.test(ip)) { showReportMessage('Please enter a valid IP address', 'error'); return; } if (!category) { showReportMessage('Please select a category', 'error'); return; } // Show loading reportLoading.classList.remove('hidden'); reportMessage.classList.add('hidden'); // Call backend API fetch('/api/report-ip', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ ip: ip, category: category, comment: comment }) }) .then(response => response.json()) .then(data => { reportLoading.classList.add('hidden'); if (data.error) { showReportMessage(data.error, 'error'); } else { showReportMessage('Report submitted successfully! Thank you for your contribution.', 'success'); // Clear form reportIpInput.value = ''; reportCategory.value = ''; reportComment.value = ''; } }) .catch(error => { reportLoading.classList.add('hidden'); showReportMessage('Error submitting report. Please try again.', 'error'); console.error('Error:', error); }); } function showReportMessage(message, type) { if (!reportMessage) return; reportMessage.classList.remove('hidden'); reportMessage.className = 'report-message hidden'; reportMessage.classList.add(type === 'success' ? 'success' : 'error'); reportMessage.classList.remove('hidden'); reportMessage.innerHTML = message; } // IP Purity Scanner functions function checkPurity() { const ip = purityIpInput.value.trim(); if (!ip) { showPurityError('Please enter an IP address'); return; } // Basic IP validation const ipRegex = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/; if (!ipRegex.test(ip)) { showPurityError('Please enter a valid IP address'); return; } // Show loading, hide results purityLoading.classList.remove('hidden'); purityResults.classList.add('hidden'); purityResults.innerHTML = ''; // Call backend API fetch('/api/purity-check', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ ip: ip }) }) .then(response => response.json()) .then(data => { purityLoading.classList.add('hidden'); if (data.error) { showPurityError(data.error); } else { displayPurityResults(data); } }) .catch(error => { purityLoading.classList.add('hidden'); showPurityError('Error connecting to server. Please try again.'); console.error('Error:', error); }); } function showPurityError(message) { purityResults.classList.remove('hidden'); purityResults.innerHTML = `
${message}
`; } function displayPurityResults(data) { // Ensure anonymityScore is a number let anonymityScore = data.anonymityScore !== undefined && data.anonymityScore !== null ? Number(data.anonymityScore) : 0; // Handle NaN or invalid numbers if (isNaN(anonymityScore) || anonymityScore < 0) { anonymityScore = 0; } if (anonymityScore > 100) { anonymityScore = 100; } // Ensure abuseConfidencePercentage is a number let abuseConfidence = data.abuseConfidencePercentage !== undefined && data.abuseConfidencePercentage !== null ? Number(data.abuseConfidencePercentage) : 0; // Handle NaN or invalid numbers if (isNaN(abuseConfidence) || abuseConfidence < 0) { abuseConfidence = 0; } let statusClass = 'status-safe'; let statusText = 'Safe'; if (abuseConfidence >= 75) { statusClass = 'status-danger'; statusText = 'High Risk'; } else if (abuseConfidence >= 25) { statusClass = 'status-warning'; statusText = 'Medium Risk'; } const proxy = data.proxy || 'No'; const vpn = data.vpn || 'No'; const tor = data.tor || 'No'; const country = data.countryName || 'Unknown'; const isp = data.isp || 'Unknown'; purityResults.classList.remove('hidden'); purityResults.innerHTML = `
${data.ipAddress || data.ip}
${statusText}
Anonymity Score
${anonymityScore.toFixed(0)}%
Threat Level
${abuseConfidence.toFixed(0)}%
Proxy Detected
${proxy}
VPN Detected
${vpn}
Tor Detected
${tor}
Country
${country}
ISP
${isp}
`; } });