Fix agent mode issues and WiFi deep scan polling

Agent fixes:
- Fix Ctrl+C hang by running cleanup in background thread
- Add force-exit on double Ctrl+C
- Improve exception handling in output reader threads to prevent
  bad file descriptor errors on shutdown
- Reduce cleanup timeouts for faster shutdown

Controller/UI fixes:
- Add URL validation for agent registration (check port, protocol)
- Show helpful message when agent is unreachable during registration
- Clarify API key field label (reserved for future use)
- Add client-side URL validation with user-friendly error messages

WiFi agent mode fixes:
- Add polling fallback for deep scan when push mode is disabled
- Polls /controller/agents/{id}/wifi/data every 2 seconds
- Detect running scans when switching to an agent
- Fix scan_mode detection (agent uses params.scan_type)
This commit is contained in:
cemaxecuter
2026-01-31 07:48:08 -05:00
parent 5f588a5513
commit f0cc396a6b
6 changed files with 487 additions and 68 deletions

View File

@@ -77,6 +77,7 @@ const WiFiMode = (function() {
let scanMode = 'quick'; // 'quick' or 'deep'
let eventSource = null;
let pollTimer = null;
let agentPollTimer = null;
// Data stores
let networks = new Map(); // bssid -> network
@@ -505,8 +506,13 @@ const WiFiMode = (function() {
console.log('[WiFiMode] Agent deep scan started:', scanResult);
}
// Start SSE stream for real-time updates
// Start SSE stream for real-time updates (works with push-enabled agents)
startEventStream();
// Also start polling for agent data (works without push enabled)
if (isAgentMode) {
startAgentDeepScanPolling();
}
} catch (error) {
console.error('[WiFiMode] Deep scan error:', error);
showError(error.message);
@@ -523,6 +529,9 @@ const WiFiMode = (function() {
pollTimer = null;
}
// Stop agent polling
stopAgentDeepScanPolling();
// Close event stream
if (eventSource) {
eventSource.close();
@@ -584,9 +593,15 @@ const WiFiMode = (function() {
const status = isAgentMode && data.result ? data.result : data;
if (status.is_scanning || status.running) {
setScanning(true, status.scan_mode);
if (status.scan_mode === 'deep') {
// Agent returns scan_type in params, local returns scan_mode
const detectedMode = status.scan_mode || (status.params && status.params.scan_type) || 'deep';
setScanning(true, detectedMode);
if (detectedMode === 'deep') {
startEventStream();
// Also start polling for agent mode (works without push enabled)
if (isAgentMode) {
startAgentDeepScanPolling();
}
} else {
startQuickScanPolling();
}
@@ -655,6 +670,76 @@ const WiFiMode = (function() {
});
}
// ==========================================================================
// Agent Deep Scan Polling (fallback when push is not enabled)
// ==========================================================================
function startAgentDeepScanPolling() {
if (agentPollTimer) return;
console.log('[WiFiMode] Starting agent deep scan polling...');
agentPollTimer = setInterval(async () => {
if (!isScanning || scanMode !== 'deep') {
clearInterval(agentPollTimer);
agentPollTimer = null;
return;
}
const isAgentMode = typeof currentAgent !== 'undefined' && currentAgent !== 'local';
if (!isAgentMode) {
clearInterval(agentPollTimer);
agentPollTimer = null;
return;
}
try {
const response = await fetch(`/controller/agents/${currentAgent}/wifi/data`);
if (!response.ok) return;
const result = await response.json();
if (result.status !== 'success' || !result.data) return;
const data = result.data.data || result.data;
const agentName = result.agent_name || 'Remote';
// Process networks
if (data.networks && Array.isArray(data.networks)) {
data.networks.forEach(net => {
net._agent = agentName;
handleStreamEvent({
type: 'network_update',
network: net
});
});
}
// Process clients
if (data.clients && Array.isArray(data.clients)) {
data.clients.forEach(client => {
client._agent = agentName;
handleStreamEvent({
type: 'client_update',
client: client
});
});
}
console.debug(`[WiFiMode] Agent poll: ${data.networks?.length || 0} networks, ${data.clients?.length || 0} clients`);
} catch (error) {
console.debug('[WiFiMode] Agent poll error:', error);
}
}, 2000); // Poll every 2 seconds
}
function stopAgentDeepScanPolling() {
if (agentPollTimer) {
clearInterval(agentPollTimer);
agentPollTimer = null;
}
}
// ==========================================================================
// SSE Event Stream
// ==========================================================================
@@ -1306,6 +1391,9 @@ const WiFiMode = (function() {
// Refresh capabilities for new agent
checkCapabilities();
// Check if new agent already has a scan running
checkScanStatus();
lastAgentId = currentAgentId;
}