mirror of
https://github.com/smittix/intercept.git
synced 2026-06-12 16:03:29 -07:00
Fix GSM Spy frontend: SSE state replay, field name mismatch, crash fix
- Send all existing towers on SSE connect (fixes data loss on reconnect) - Fix tower.signal -> tower.signal_strength field name in frontend - Fix TypeError crash in selectTower when tower has no coordinates - Add Connection: keep-alive header to SSE response - Add comprehensive console.log debugging for SSE data flow - Handle error/disconnected SSE event types in frontend Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+16
-9
@@ -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')
|
||||
|
||||
@@ -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 @@
|
||||
</div>
|
||||
<div class="tower-info-row">
|
||||
<span class="tower-info-label">Signal (dBm)</span>
|
||||
<span class="tower-info-value">${escapeHtml(tower.signal || 'N/A')}</span>
|
||||
<span class="tower-info-value">${escapeHtml(tower.signal_strength || 'N/A')}</span>
|
||||
</div>
|
||||
<div class="tower-info-row">
|
||||
<span class="tower-info-label">Location</span>
|
||||
<span class="tower-info-value">${tower.lat.toFixed(6)}, ${tower.lon.toFixed(6)}</span>
|
||||
<span class="tower-info-value">${tower.lat != null ? parseFloat(tower.lat).toFixed(6) + ', ' + parseFloat(tower.lon).toFixed(6) : 'Pending geocoding'}</span>
|
||||
</div>
|
||||
<div class="tower-info-row">
|
||||
<span class="tower-info-label">First Seen</span>
|
||||
@@ -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 = '<div class="no-data"><div>No towers detected</div></div>';
|
||||
return;
|
||||
}
|
||||
@@ -1892,7 +1903,7 @@
|
||||
${tower.rogue ? '<span class="rogue-indicator"></span>' : ''}
|
||||
</div>
|
||||
<div class="list-item-details">
|
||||
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
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
Reference in New Issue
Block a user