diff --git a/routes/gsm_spy.py b/routes/gsm_spy.py index 540bfbf..d217f40 100644 --- a/routes/gsm_spy.py +++ b/routes/gsm_spy.py @@ -516,19 +516,28 @@ def stream(): """SSE stream for real-time GSM updates.""" def generate(): """Generate SSE events.""" + logger.info("SSE stream connected - client subscribed") + + # Send current state on connect (handles reconnects and late-joining clients) + existing_towers = dict(app_module.gsm_spy_towers.items()) + logger.info(f"SSE sending {len(existing_towers)} existing towers on connect") + for key, tower_data in existing_towers.items(): + yield format_sse(tower_data) + last_keepalive = time.time() while True: try: # Check if scanner is still running if not app_module.gsm_spy_scanner_running and not app_module.gsm_spy_monitor_process: + logger.info("SSE stream: scanner stopped, sending disconnect") yield format_sse({'type': 'disconnected'}) break # Try to get data from queue try: data = app_module.gsm_spy_queue.get(timeout=1) - logger.info(f"SSE sending: type={data.get('type', '?')}") + logger.info(f"SSE sending: type={data.get('type', '?')} keys={list(data.keys())}") yield format_sse(data) last_keepalive = time.time() except queue.Empty: @@ -538,20 +547,18 @@ def stream(): last_keepalive = time.time() except GeneratorExit: + logger.info("SSE stream: client disconnected (GeneratorExit)") break except Exception as e: logger.error(f"Error in GSM stream: {e}") yield format_sse({'type': 'error', 'message': str(e)}) break - return Response( - generate(), - mimetype='text/event-stream', - headers={ - 'Cache-Control': 'no-cache', - 'X-Accel-Buffering': 'no' - } - ) + response = Response(generate(), mimetype='text/event-stream') + response.headers['Cache-Control'] = 'no-cache' + response.headers['X-Accel-Buffering'] = 'no' + response.headers['Connection'] = 'keep-alive' + return response @gsm_spy_bp.route('/status') diff --git a/templates/gsm_spy_dashboard.html b/templates/gsm_spy_dashboard.html index fc2794b..229f349 100644 --- a/templates/gsm_spy_dashboard.html +++ b/templates/gsm_spy_dashboard.html @@ -1627,21 +1627,25 @@ eventSource.close(); } + console.log('[GSM SPY] Opening EventSource to /gsm_spy/stream'); eventSource = new EventSource('/gsm_spy/stream'); + eventSource.onopen = function() { + console.log('[GSM SPY] EventSource connected'); + }; + eventSource.onmessage = function(e) { try { + console.log('[GSM SPY] SSE raw:', e.data.substring(0, 200)); const data = JSON.parse(e.data); if (data.type === 'keepalive') { return; } - if (data.type === 'tower') { - updateTower(data); - } else if (data.type === 'tower_update') { - // Background geocoding resolved coordinates for a tower - console.log(`Tower coordinates resolved via API: MCC=${data.mcc} MNC=${data.mnc} LAC=${data.lac} CID=${data.cid}`); + console.log('[GSM SPY] SSE event type:', data.type, 'keys:', Object.keys(data).join(',')); + + if (data.type === 'tower' || data.type === 'tower_update') { updateTower(data); } else if (data.type === 'device') { updateDevice(data); @@ -1649,16 +1653,20 @@ addRogueAlert(data); } else if (data.type === 'stats') { updateStats(data); + } else if (data.type === 'error') { + console.error('[GSM SPY] Server error:', data.message); + } else if (data.type === 'disconnected') { + console.warn('[GSM SPY] Server disconnected stream'); } } catch (error) { - console.error('[GSM SPY] Error parsing event:', error); + console.error('[GSM SPY] Error parsing event:', error, 'raw:', e.data); } }; eventSource.onerror = function(e) { - console.error('[GSM SPY] EventSource error:', e); + console.error('[GSM SPY] EventSource error, readyState:', eventSource.readyState); if (eventSource.readyState === EventSource.CLOSED) { - console.log('[GSM SPY] EventSource closed, reconnecting...'); + console.log('[GSM SPY] EventSource closed, reconnecting in 3s...'); setTimeout(startEventStream, 3000); } }; @@ -1700,11 +1708,12 @@ // ============================================ function updateTower(data) { const key = `${data.mcc}-${data.mnc}-${data.lac}-${data.cid}`; + console.log(`[GSM SPY] updateTower: key=${key} CID=${data.cid} signal=${data.signal_strength} lat=${data.lat} lon=${data.lon}`); towers[key] = data; // Validate coordinates before creating map marker if (!data.lat || !data.lon || isNaN(parseFloat(data.lat)) || isNaN(parseFloat(data.lon))) { - console.log(`Tower ${data.cid} pending geocoding (status: ${data.status || 'unknown'})`); + console.log(`[GSM SPY] Tower ${data.cid} pending geocoding (status: ${data.status || 'unknown'}), updating list only`); // Update towers list but skip map marker updateTowersList(); return; @@ -1856,11 +1865,11 @@
Signal (dBm) - ${escapeHtml(tower.signal || 'N/A')} + ${escapeHtml(tower.signal_strength || 'N/A')}
Location - ${tower.lat.toFixed(6)}, ${tower.lon.toFixed(6)} + ${tower.lat != null ? parseFloat(tower.lat).toFixed(6) + ', ' + parseFloat(tower.lon).toFixed(6) : 'Pending geocoding'}
First Seen @@ -1875,8 +1884,10 @@ function updateTowersList() { const listDiv = document.getElementById('towersList'); + const towerCount = Object.keys(towers).length; + console.log(`[GSM SPY] updateTowersList: ${towerCount} towers, listDiv exists: ${!!listDiv}`); - if (Object.keys(towers).length === 0) { + if (towerCount === 0) { listDiv.innerHTML = '
No towers detected
'; return; } @@ -1892,7 +1903,7 @@ ${tower.rogue ? '' : ''}
- LAC ${escapeHtml(tower.lac)} | ARFCN ${escapeHtml(tower.arfcn)} | ${escapeHtml(tower.signal || 'N/A')} dBm + LAC ${escapeHtml(tower.lac)} | ARFCN ${escapeHtml(tower.arfcn)} | ${escapeHtml(tower.signal_strength || 'N/A')} dBm
`;