class PortScanner { constructor() { this.isScanning = false; this.progressInterval = null; this.initializeEventListeners(); } initializeEventListeners() { // Form elements this.targetIpInput = document.getElementById('target-ip'); this.startPortInput = document.getElementById('start-port'); this.endPortInput = document.getElementById('end-port'); this.timeoutInput = document.getElementById('timeout'); this.threadsInput = document.getElementById('threads'); // Buttons this.scanBtn = document.getElementById('scan-btn'); this.pingBtn = document.getElementById('ping-btn'); this.exportBtn = document.getElementById('export-btn'); this.clearBtn = document.getElementById('clear-btn'); // Sections this.progressSection = document.getElementById('progress-section'); this.resultsSection = document.getElementById('results-section'); // Event listeners this.scanBtn.addEventListener('click', () => this.startScan()); this.pingBtn.addEventListener('click', () => this.pingHost()); this.exportBtn.addEventListener('click', () => this.exportResults()); this.clearBtn.addEventListener('click', () => this.clearResults()); // Preset buttons document.querySelectorAll('.preset-btn').forEach(btn => { btn.addEventListener('click', (e) => { const start = e.target.dataset.start; const end = e.target.dataset.end; this.startPortInput.value = start; this.endPortInput.value = end; }); }); // Form validation this.targetIpInput.addEventListener('input', () => this.validateForm()); this.startPortInput.addEventListener('input', () => this.validateForm()); this.endPortInput.addEventListener('input', () => this.validateForm()); } validateForm() { const ip = this.targetIpInput.value.trim(); const startPort = parseInt(this.startPortInput.value); const endPort = parseInt(this.endPortInput.value); let isValid = true; // IP validation if (!ip || !this.isValidIP(ip)) { isValid = false; } // Port validation if (startPort < 1 || endPort > 65535 || startPort > endPort) { isValid = false; } this.scanBtn.disabled = !isValid || this.isScanning; return isValid; } isValidIP(ip) { 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]?)$/; return ipRegex.test(ip); } async pingHost() { const ip = this.targetIpInput.value.trim(); if (!ip || !this.isValidIP(ip)) { this.showNotification('Please enter a valid IP address', 'error'); return; } this.pingBtn.disabled = true; this.pingBtn.innerHTML = ' Pinging...'; try { const response = await fetch('/api/ping', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ ip: ip }) }); const data = await response.json(); if (response.ok) { const status = data.status === 'reachable' ? 'Host is reachable' : 'Host appears unreachable'; const type = data.status === 'reachable' ? 'success' : 'error'; this.showNotification(status, type); } else { this.showNotification(data.error || 'Ping failed', 'error'); } } catch (error) { this.showNotification('Network error during ping', 'error'); } finally { this.pingBtn.disabled = false; this.pingBtn.innerHTML = ' Ping'; } } async startScan() { if (!this.validateForm()) { this.showNotification('Please check your input values', 'error'); return; } const scanData = { ip: this.targetIpInput.value.trim(), start_port: parseInt(this.startPortInput.value), end_port: parseInt(this.endPortInput.value), timeout: parseFloat(this.timeoutInput.value), threads: parseInt(this.threadsInput.value) }; // Check port range limit const portRange = scanData.end_port - scanData.start_port + 1; if (portRange > 10000) { this.showNotification('Port range too large (max 10000 ports)', 'error'); return; } this.isScanning = true; this.scanBtn.disabled = true; this.scanBtn.innerHTML = ' Scanning...'; // Show progress section this.progressSection.style.display = 'block'; this.resultsSection.style.display = 'none'; // Start progress monitoring this.startProgressMonitoring(); try { const response = await fetch('/api/scan', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(scanData) }); const data = await response.json(); if (response.ok) { this.displayResults(data); this.showNotification(`Scan completed! Found ${data.total_open} open ports`, 'success'); } else { this.showNotification(data.error || 'Scan failed', 'error'); } } catch (error) { this.showNotification('Network error during scan', 'error'); } finally { this.stopProgressMonitoring(); this.isScanning = false; this.scanBtn.disabled = false; this.scanBtn.innerHTML = ' Start Scan'; this.progressSection.style.display = 'none'; } } startProgressMonitoring() { this.progressInterval = setInterval(async () => { try { const response = await fetch('/api/progress'); const data = await response.json(); const progressFill = document.getElementById('progress-fill'); const progressText = document.getElementById('progress-text'); progressFill.style.width = `${data.percentage}%`; progressText.textContent = `Scanning... ${data.progress}/${data.total} ports (${data.percentage}%)`; } catch (error) { console.error('Error fetching progress:', error); } }, 500); } stopProgressMonitoring() { if (this.progressInterval) { clearInterval(this.progressInterval); this.progressInterval = null; } } displayResults(data) { const resultsSection = document.getElementById('results-section'); const resultsSummary = document.getElementById('results-summary'); const resultsTableBody = document.getElementById('results-tbody'); // Update summary resultsSummary.innerHTML = ` Target: ${data.target_ip} | Range: ${data.scan_range} | Open Ports: ${data.total_open}/${data.total_scanned} | Duration: ${data.scan_duration}s `; // Clear previous results resultsTableBody.innerHTML = ''; // Add results to table if (data.open_ports && data.open_ports.length > 0) { data.open_ports.forEach(port => { const row = document.createElement('tr'); row.innerHTML = `