mirror of
https://github.com/smittix/intercept.git
synced 2026-04-24 06:40:00 -07:00
Fix security issues, breaking changes, and code cleanup for weather satellite PR
Co-authored-by: mitchross <6330506+mitchross@users.noreply.github.com>
This commit is contained in:
12
app.py
12
app.py
@@ -692,8 +692,7 @@ def kill_all() -> Response:
|
|||||||
'airodump-ng', 'aireplay-ng', 'airmon-ng',
|
'airodump-ng', 'aireplay-ng', 'airmon-ng',
|
||||||
'dump1090', 'acarsdec', 'direwolf', 'AIS-catcher',
|
'dump1090', 'acarsdec', 'direwolf', 'AIS-catcher',
|
||||||
'hcitool', 'bluetoothctl', 'satdump', 'dsd',
|
'hcitool', 'bluetoothctl', 'satdump', 'dsd',
|
||||||
'rtl_tcp', 'rtl_power', 'rtlamr', 'ffmpeg',
|
'rtl_tcp', 'rtl_power', 'rtlamr', 'ffmpeg'
|
||||||
'grgsm_scanner', 'grgsm_livemon', 'tshark'
|
|
||||||
]
|
]
|
||||||
|
|
||||||
for proc in processes_to_kill:
|
for proc in processes_to_kill:
|
||||||
@@ -870,6 +869,15 @@ def main() -> None:
|
|||||||
from routes import register_blueprints
|
from routes import register_blueprints
|
||||||
register_blueprints(app)
|
register_blueprints(app)
|
||||||
|
|
||||||
|
# Initialize TLE auto-refresh (must be after blueprint registration)
|
||||||
|
try:
|
||||||
|
from routes.satellite import init_tle_auto_refresh
|
||||||
|
import os
|
||||||
|
if not os.environ.get('TESTING'):
|
||||||
|
init_tle_auto_refresh()
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"Failed to initialize TLE auto-refresh: {e}")
|
||||||
|
|
||||||
# Update TLE data in background thread (non-blocking)
|
# Update TLE data in background thread (non-blocking)
|
||||||
def update_tle_background():
|
def update_tle_background():
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ def _get_env_bool(key: str, default: bool) -> bool:
|
|||||||
|
|
||||||
|
|
||||||
# Logging configuration
|
# Logging configuration
|
||||||
_log_level_str = _get_env('LOG_LEVEL', 'INFO').upper()
|
_log_level_str = _get_env('LOG_LEVEL', 'WARNING').upper()
|
||||||
LOG_LEVEL = getattr(logging, _log_level_str, logging.WARNING)
|
LOG_LEVEL = getattr(logging, _log_level_str, logging.WARNING)
|
||||||
LOG_FORMAT = _get_env('LOG_FORMAT', '%(asctime)s - %(levelname)s - %(message)s')
|
LOG_FORMAT = _get_env('LOG_FORMAT', '%(asctime)s - %(levelname)s - %(message)s')
|
||||||
|
|
||||||
|
|||||||
@@ -12,13 +12,10 @@
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
intercept:
|
intercept:
|
||||||
# When INTERCEPT_IMAGE is set, use that pre-built image; when empty/unset,
|
# When INTERCEPT_IMAGE is set, use that pre-built image; otherwise build locally
|
||||||
# the empty string causes Docker Compose to fall through to the build: directive.
|
image: ${INTERCEPT_IMAGE:-intercept:latest}
|
||||||
image: ${INTERCEPT_IMAGE:-}
|
|
||||||
build: .
|
build: .
|
||||||
container_name: intercept
|
container_name: intercept
|
||||||
profiles:
|
|
||||||
- basic
|
|
||||||
ports:
|
ports:
|
||||||
- "5050:5050"
|
- "5050:5050"
|
||||||
# Privileged mode required for USB SDR device access
|
# Privileged mode required for USB SDR device access
|
||||||
@@ -64,7 +61,7 @@ services:
|
|||||||
# Enable with: docker compose --profile history up -d
|
# Enable with: docker compose --profile history up -d
|
||||||
intercept-history:
|
intercept-history:
|
||||||
# Same image/build fallback pattern as above
|
# Same image/build fallback pattern as above
|
||||||
image: ${INTERCEPT_IMAGE:-}
|
image: ${INTERCEPT_IMAGE:-intercept:latest}
|
||||||
build: .
|
build: .
|
||||||
container_name: intercept-history
|
container_name: intercept-history
|
||||||
profiles:
|
profiles:
|
||||||
|
|||||||
@@ -30,11 +30,12 @@ ALLOWED_TLE_HOSTS = ['celestrak.org', 'celestrak.com', 'www.celestrak.org', 'www
|
|||||||
# Local TLE cache (can be updated via API)
|
# Local TLE cache (can be updated via API)
|
||||||
_tle_cache = dict(TLE_SATELLITES)
|
_tle_cache = dict(TLE_SATELLITES)
|
||||||
|
|
||||||
# Auto-refresh TLEs from CelesTrak on startup (non-blocking)
|
|
||||||
import os
|
|
||||||
import threading
|
|
||||||
|
|
||||||
def _auto_refresh_tle():
|
def init_tle_auto_refresh():
|
||||||
|
"""Initialize TLE auto-refresh. Called by app.py after initialization."""
|
||||||
|
import threading
|
||||||
|
|
||||||
|
def _auto_refresh_tle():
|
||||||
try:
|
try:
|
||||||
updated = refresh_tle_data()
|
updated = refresh_tle_data()
|
||||||
if updated:
|
if updated:
|
||||||
@@ -42,10 +43,9 @@ def _auto_refresh_tle():
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"Auto TLE refresh failed: {e}")
|
logger.warning(f"Auto TLE refresh failed: {e}")
|
||||||
|
|
||||||
# Delay import — refresh_tle_data is defined later in this module
|
# Start auto-refresh in background
|
||||||
# Guard to avoid firing during tests
|
|
||||||
if not os.environ.get('TESTING'):
|
|
||||||
threading.Timer(2.0, _auto_refresh_tle).start()
|
threading.Timer(2.0, _auto_refresh_tle).start()
|
||||||
|
logger.info("TLE auto-refresh scheduled")
|
||||||
|
|
||||||
|
|
||||||
def _fetch_iss_realtime(observer_lat: Optional[float] = None, observer_lon: Optional[float] = None) -> Optional[dict]:
|
def _fetch_iss_realtime(observer_lat: Optional[float] = None, observer_lon: Optional[float] = None) -> Optional[dict]:
|
||||||
@@ -498,7 +498,8 @@ def update_tle():
|
|||||||
'updated': updated
|
'updated': updated
|
||||||
})
|
})
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return jsonify({'status': 'error', 'message': str(e)})
|
logger.error(f"Error updating TLE data: {e}")
|
||||||
|
return jsonify({'status': 'error', 'message': 'TLE update failed'})
|
||||||
|
|
||||||
|
|
||||||
@satellite_bp.route('/celestrak/<category>')
|
@satellite_bp.route('/celestrak/<category>')
|
||||||
@@ -552,4 +553,5 @@ def fetch_celestrak(category):
|
|||||||
})
|
})
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return jsonify({'status': 'error', 'message': str(e)})
|
logger.error(f"Error fetching CelesTrak data: {e}")
|
||||||
|
return jsonify({'status': 'error', 'message': 'Failed to fetch satellite data'})
|
||||||
|
|||||||
@@ -253,7 +253,7 @@ def test_decode():
|
|||||||
}), 400
|
}), 400
|
||||||
|
|
||||||
if not input_path.is_file():
|
if not input_path.is_file():
|
||||||
logger.warning(f"Test-decode file not found: {input_file}")
|
logger.warning("Test-decode file not found")
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'status': 'error',
|
'status': 'error',
|
||||||
'message': 'File not found'
|
'message': 'File not found'
|
||||||
@@ -313,7 +313,7 @@ def stop_capture():
|
|||||||
JSON confirmation.
|
JSON confirmation.
|
||||||
"""
|
"""
|
||||||
decoder = get_weather_sat_decoder()
|
decoder = get_weather_sat_decoder()
|
||||||
device_index = decoder._device_index
|
device_index = decoder.device_index
|
||||||
|
|
||||||
decoder.stop()
|
decoder.stop()
|
||||||
|
|
||||||
|
|||||||
@@ -446,8 +446,17 @@ const WeatherSat = (function() {
|
|||||||
* Load pass predictions (with trajectory + ground track)
|
* Load pass predictions (with trajectory + ground track)
|
||||||
*/
|
*/
|
||||||
async function loadPasses() {
|
async function loadPasses() {
|
||||||
const storedLat = localStorage.getItem('observerLat');
|
let storedLat, storedLon;
|
||||||
const storedLon = localStorage.getItem('observerLon');
|
|
||||||
|
// Use ObserverLocation if available, otherwise fall back to localStorage
|
||||||
|
if (window.ObserverLocation && ObserverLocation.isSharedEnabled()) {
|
||||||
|
const shared = ObserverLocation.getShared();
|
||||||
|
storedLat = shared?.lat?.toString();
|
||||||
|
storedLon = shared?.lon?.toString();
|
||||||
|
} else {
|
||||||
|
storedLat = localStorage.getItem('observerLat');
|
||||||
|
storedLon = localStorage.getItem('observerLon');
|
||||||
|
}
|
||||||
|
|
||||||
if (!storedLat || !storedLon) {
|
if (!storedLat || !storedLon) {
|
||||||
renderPasses([]);
|
renderPasses([]);
|
||||||
|
|||||||
@@ -185,6 +185,11 @@ class WeatherSatDecoder:
|
|||||||
def current_frequency(self) -> float:
|
def current_frequency(self) -> float:
|
||||||
return self._current_frequency
|
return self._current_frequency
|
||||||
|
|
||||||
|
@property
|
||||||
|
def device_index(self) -> int:
|
||||||
|
"""Return current device index."""
|
||||||
|
return self._device_index
|
||||||
|
|
||||||
def _detect_decoder(self) -> str | None:
|
def _detect_decoder(self) -> str | None:
|
||||||
"""Detect which weather satellite decoder is available."""
|
"""Detect which weather satellite decoder is available."""
|
||||||
if shutil.which('satdump'):
|
if shutil.which('satdump'):
|
||||||
|
|||||||
@@ -51,14 +51,16 @@ class ScheduledPass:
|
|||||||
def start_dt(self) -> datetime:
|
def start_dt(self) -> datetime:
|
||||||
dt = datetime.fromisoformat(self.start_time)
|
dt = datetime.fromisoformat(self.start_time)
|
||||||
if dt.tzinfo is None:
|
if dt.tzinfo is None:
|
||||||
return dt.replace(tzinfo=timezone.utc)
|
# Naive datetime - assume UTC
|
||||||
|
dt = dt.replace(tzinfo=timezone.utc)
|
||||||
return dt.astimezone(timezone.utc)
|
return dt.astimezone(timezone.utc)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def end_dt(self) -> datetime:
|
def end_dt(self) -> datetime:
|
||||||
dt = datetime.fromisoformat(self.end_time)
|
dt = datetime.fromisoformat(self.end_time)
|
||||||
if dt.tzinfo is None:
|
if dt.tzinfo is None:
|
||||||
return dt.replace(tzinfo=timezone.utc)
|
# Naive datetime - assume UTC
|
||||||
|
dt = dt.replace(tzinfo=timezone.utc)
|
||||||
return dt.astimezone(timezone.utc)
|
return dt.astimezone(timezone.utc)
|
||||||
|
|
||||||
def to_dict(self) -> dict[str, Any]:
|
def to_dict(self) -> dict[str, Any]:
|
||||||
|
|||||||
Reference in New Issue
Block a user