mirror of
https://github.com/smittix/intercept.git
synced 2026-06-08 14:11:54 -07:00
Fix geocoding: validate API responses, clean poisoned cache, improve logging
- Cache lookup now requires non-NULL lat/lon — previously a row with
NULL coordinates counted as a cache hit, returning {lat: None, lon: None}
which the frontend silently ignored (tower in list but no map pin)
- API response handler validates lat/lon exist before caching, preventing
error responses (status 200 with error body) from poisoning the cache
- On geocoding worker start, delete any existing poisoned cache rows
- Geocoding worker now logs "API key not configured" vs "rate limit
reached" so the actual problem is visible in logs
- API error responses now log the response body for easier debugging
This commit is contained in:
+19
-3
@@ -104,6 +104,19 @@ def can_use_api():
|
||||
def start_geocoding_worker():
|
||||
"""Start background thread for async geocoding."""
|
||||
global _geocoding_worker_thread
|
||||
|
||||
# Clean poisoned cache entries (rows with NULL lat/lon from failed API responses)
|
||||
try:
|
||||
with get_db() as conn:
|
||||
deleted = conn.execute(
|
||||
'DELETE FROM gsm_cells WHERE lat IS NULL OR lon IS NULL'
|
||||
).rowcount
|
||||
conn.commit()
|
||||
if deleted:
|
||||
logger.info(f"Cleaned {deleted} poisoned cache entries (NULL coordinates)")
|
||||
except Exception as e:
|
||||
logger.warning(f"Could not clean cache: {e}")
|
||||
|
||||
if _geocoding_worker_thread is None or not _geocoding_worker_thread.is_alive():
|
||||
_geocoding_worker_thread = threading.Thread(
|
||||
target=geocoding_worker,
|
||||
@@ -125,10 +138,13 @@ def geocoding_worker():
|
||||
# Wait for pending tower with timeout
|
||||
tower_data = geocoding_queue.get(timeout=5)
|
||||
|
||||
# Check rate limit
|
||||
# Check API key and rate limit
|
||||
if not can_use_api():
|
||||
current_usage = get_api_usage_today()
|
||||
logger.warning(f"OpenCellID API rate limit reached ({current_usage}/{config.GSM_API_DAILY_LIMIT})")
|
||||
if not config.GSM_OPENCELLID_API_KEY:
|
||||
logger.warning("OpenCellID API key not configured (set INTERCEPT_GSM_OPENCELLID_API_KEY)")
|
||||
else:
|
||||
current_usage = get_api_usage_today()
|
||||
logger.warning(f"OpenCellID API daily limit reached ({current_usage}/{config.GSM_API_DAILY_LIMIT})")
|
||||
geocoding_queue.task_done()
|
||||
continue
|
||||
|
||||
|
||||
+19
-7
@@ -47,7 +47,7 @@ def lookup_cell_coordinates(mcc: int, mnc: int, lac: int, cid: int) -> dict[str,
|
||||
WHERE mcc = ? AND mnc = ? AND lac = ? AND cid = ?
|
||||
''', (mcc, mnc, lac, cid)).fetchone()
|
||||
|
||||
if result:
|
||||
if result and result['lat'] is not None and result['lon'] is not None:
|
||||
return {
|
||||
'lat': result['lat'],
|
||||
'lon': result['lon'],
|
||||
@@ -95,6 +95,16 @@ def lookup_cell_from_api(mcc: int, mnc: int, lac: int, cid: int) -> dict[str, An
|
||||
|
||||
if response.status_code == 200:
|
||||
cell_data = response.json()
|
||||
lat = cell_data.get('lat')
|
||||
lon = cell_data.get('lon')
|
||||
|
||||
# Validate response has actual coordinates
|
||||
if lat is None or lon is None:
|
||||
logger.warning(
|
||||
f"OpenCellID API returned 200 but no coordinates for "
|
||||
f"MCC={mcc} MNC={mnc} LAC={lac} CID={cid}: {cell_data}"
|
||||
)
|
||||
return None
|
||||
|
||||
# Cache the result
|
||||
with get_db() as conn:
|
||||
@@ -104,8 +114,7 @@ def lookup_cell_from_api(mcc: int, mnc: int, lac: int, cid: int) -> dict[str, An
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
|
||||
''', (
|
||||
mcc, mnc, lac, cid,
|
||||
cell_data.get('lat'),
|
||||
cell_data.get('lon'),
|
||||
lat, lon,
|
||||
cell_data.get('azimuth'),
|
||||
cell_data.get('range'),
|
||||
cell_data.get('samples'),
|
||||
@@ -114,11 +123,11 @@ def lookup_cell_from_api(mcc: int, mnc: int, lac: int, cid: int) -> dict[str, An
|
||||
))
|
||||
conn.commit()
|
||||
|
||||
logger.info(f"Cached cell tower from API: MCC={mcc} MNC={mnc} LAC={lac} CID={cid}")
|
||||
logger.info(f"Cached cell tower from API: MCC={mcc} MNC={mnc} LAC={lac} CID={cid} -> ({lat}, {lon})")
|
||||
|
||||
return {
|
||||
'lat': cell_data.get('lat'),
|
||||
'lon': cell_data.get('lon'),
|
||||
'lat': lat,
|
||||
'lon': lon,
|
||||
'source': 'api',
|
||||
'azimuth': cell_data.get('azimuth'),
|
||||
'range_meters': cell_data.get('range'),
|
||||
@@ -126,7 +135,10 @@ def lookup_cell_from_api(mcc: int, mnc: int, lac: int, cid: int) -> dict[str, An
|
||||
'radio': cell_data.get('radio')
|
||||
}
|
||||
else:
|
||||
logger.warning(f"OpenCellID API returned {response.status_code} for MCC={mcc} MNC={mnc} LAC={lac} CID={cid}")
|
||||
logger.warning(
|
||||
f"OpenCellID API returned {response.status_code} for "
|
||||
f"MCC={mcc} MNC={mnc} LAC={lac} CID={cid}: {response.text[:200]}"
|
||||
)
|
||||
return None
|
||||
|
||||
except Exception as e:
|
||||
|
||||
Reference in New Issue
Block a user