mirror of
https://github.com/smittix/intercept.git
synced 2026-07-22 08:18:09 -07:00
style: apply ruff-format to entire codebase
First-time run of ruff-format via pre-commit hook normalises quote style, trailing commas, and whitespace across 188 Python files. No logic changes. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+136
-208
@@ -47,9 +47,9 @@ from utils.validation import (
|
||||
validate_rtl_tcp_port,
|
||||
)
|
||||
|
||||
logger = logging.getLogger('intercept.dsc')
|
||||
logger = logging.getLogger("intercept.dsc")
|
||||
|
||||
dsc_bp = Blueprint('dsc', __name__, url_prefix='/dsc')
|
||||
dsc_bp = Blueprint("dsc", __name__, url_prefix="/dsc")
|
||||
|
||||
# Module state (track if running independent of process state)
|
||||
dsc_running = False
|
||||
@@ -62,12 +62,12 @@ dsc_active_sdr_type: str | None = None
|
||||
def _get_dsc_decoder_path() -> str | None:
|
||||
"""Get path to DSC decoder."""
|
||||
# Check for our custom decoder
|
||||
project_bin = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'bin', 'dsc-decoder')
|
||||
project_bin = os.path.join(os.path.dirname(os.path.dirname(__file__)), "bin", "dsc-decoder")
|
||||
if os.path.isfile(project_bin) and os.access(project_bin, os.X_OK):
|
||||
return project_bin
|
||||
|
||||
# Check system PATH
|
||||
system_decoder = shutil.which('dsc-decoder')
|
||||
system_decoder = shutil.which("dsc-decoder")
|
||||
if system_decoder:
|
||||
return system_decoder
|
||||
|
||||
@@ -76,7 +76,7 @@ def _get_dsc_decoder_path() -> str | None:
|
||||
|
||||
def _check_dsc_tools() -> dict:
|
||||
"""Check availability of DSC decoding tools."""
|
||||
rtl_fm_path = get_tool_path('rtl_fm')
|
||||
rtl_fm_path = get_tool_path("rtl_fm")
|
||||
decoder_path = _get_dsc_decoder_path()
|
||||
|
||||
# Check for scipy/numpy (needed for decoder)
|
||||
@@ -84,24 +84,16 @@ def _check_dsc_tools() -> dict:
|
||||
try:
|
||||
import numpy
|
||||
import scipy
|
||||
|
||||
scipy_available = True
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
return {
|
||||
'rtl_fm': {
|
||||
'available': rtl_fm_path is not None,
|
||||
'path': rtl_fm_path
|
||||
},
|
||||
'dsc_decoder': {
|
||||
'available': decoder_path is not None,
|
||||
'path': decoder_path
|
||||
},
|
||||
'scipy': {
|
||||
'available': scipy_available,
|
||||
'note': 'Required for DSC signal processing'
|
||||
},
|
||||
'ready': rtl_fm_path is not None and decoder_path is not None and scipy_available
|
||||
"rtl_fm": {"available": rtl_fm_path is not None, "path": rtl_fm_path},
|
||||
"dsc_decoder": {"available": decoder_path is not None, "path": decoder_path},
|
||||
"scipy": {"available": scipy_available, "note": "Required for DSC signal processing"},
|
||||
"ready": rtl_fm_path is not None and decoder_path is not None and scipy_available,
|
||||
}
|
||||
|
||||
|
||||
@@ -116,7 +108,7 @@ def stream_dsc_decoder(master_fd: int, decoder_process: subprocess.Popen) -> Non
|
||||
global dsc_running
|
||||
|
||||
try:
|
||||
app_module.dsc_queue.put({'type': 'status', 'status': 'started'})
|
||||
app_module.dsc_queue.put({"type": "status", "status": "started"})
|
||||
|
||||
buffer = ""
|
||||
while dsc_running:
|
||||
@@ -130,10 +122,10 @@ def stream_dsc_decoder(master_fd: int, decoder_process: subprocess.Popen) -> Non
|
||||
data = os.read(master_fd, 1024)
|
||||
if not data:
|
||||
break
|
||||
buffer += data.decode('utf-8', errors='replace')
|
||||
buffer += data.decode("utf-8", errors="replace")
|
||||
|
||||
while '\n' in buffer:
|
||||
line, buffer = buffer.split('\n', 1)
|
||||
while "\n" in buffer:
|
||||
line, buffer = buffer.split("\n", 1)
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue
|
||||
@@ -143,7 +135,7 @@ def stream_dsc_decoder(master_fd: int, decoder_process: subprocess.Popen) -> Non
|
||||
if parsed:
|
||||
# Generate unique message ID
|
||||
msg_id = f"{parsed['source_mmsi']}_{int(time.time() * 1000)}"
|
||||
parsed['id'] = msg_id
|
||||
parsed["id"] = msg_id
|
||||
|
||||
# Store in transient DataStore
|
||||
app_module.dsc_messages.set(msg_id, parsed)
|
||||
@@ -155,14 +147,11 @@ def stream_dsc_decoder(master_fd: int, decoder_process: subprocess.Popen) -> Non
|
||||
logger.warning("DSC queue full, dropping message")
|
||||
|
||||
# Store critical alerts permanently
|
||||
if parsed.get('is_critical'):
|
||||
if parsed.get("is_critical"):
|
||||
_store_critical_alert(parsed)
|
||||
else:
|
||||
# Raw output for debugging
|
||||
app_module.dsc_queue.put({
|
||||
'type': 'raw',
|
||||
'text': line
|
||||
})
|
||||
app_module.dsc_queue.put({"type": "raw", "text": line})
|
||||
except OSError:
|
||||
break
|
||||
|
||||
@@ -172,10 +161,7 @@ def stream_dsc_decoder(master_fd: int, decoder_process: subprocess.Popen) -> Non
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"DSC decoder error: {e}")
|
||||
app_module.dsc_queue.put({
|
||||
'type': 'error',
|
||||
'error': str(e)
|
||||
})
|
||||
app_module.dsc_queue.put({"type": "error", "error": str(e)})
|
||||
finally:
|
||||
global dsc_active_device, dsc_active_sdr_type
|
||||
with contextlib.suppress(OSError):
|
||||
@@ -193,13 +179,13 @@ def stream_dsc_decoder(master_fd: int, decoder_process: subprocess.Popen) -> Non
|
||||
with contextlib.suppress(Exception):
|
||||
proc.kill()
|
||||
unregister_process(proc)
|
||||
app_module.dsc_queue.put({'type': 'status', 'status': 'stopped'})
|
||||
app_module.dsc_queue.put({"type": "status", "status": "stopped"})
|
||||
with app_module.dsc_lock:
|
||||
app_module.dsc_process = None
|
||||
app_module.dsc_rtl_process = None
|
||||
# Release SDR device
|
||||
if dsc_active_device is not None:
|
||||
app_module.release_sdr_device(dsc_active_device, dsc_active_sdr_type or 'rtlsdr')
|
||||
app_module.release_sdr_device(dsc_active_device, dsc_active_sdr_type or "rtlsdr")
|
||||
dsc_active_device = None
|
||||
dsc_active_sdr_type = None
|
||||
|
||||
@@ -208,15 +194,15 @@ def _store_critical_alert(msg: dict) -> None:
|
||||
"""Store critical DSC alert (DISTRESS/URGENCY) to database."""
|
||||
try:
|
||||
store_dsc_alert(
|
||||
source_mmsi=msg.get('source_mmsi', ''),
|
||||
format_code=str(msg.get('format_code', '')),
|
||||
category=msg.get('category', 'UNKNOWN'),
|
||||
source_name=msg.get('source_name'),
|
||||
dest_mmsi=msg.get('dest_mmsi'),
|
||||
nature_of_distress=msg.get('nature_of_distress'),
|
||||
latitude=msg.get('latitude'),
|
||||
longitude=msg.get('longitude'),
|
||||
raw_message=msg.get('raw_message')
|
||||
source_mmsi=msg.get("source_mmsi", ""),
|
||||
format_code=str(msg.get("format_code", "")),
|
||||
category=msg.get("category", "UNKNOWN"),
|
||||
source_name=msg.get("source_name"),
|
||||
dest_mmsi=msg.get("dest_mmsi"),
|
||||
nature_of_distress=msg.get("nature_of_distress"),
|
||||
latitude=msg.get("latitude"),
|
||||
longitude=msg.get("longitude"),
|
||||
raw_message=msg.get("raw_message"),
|
||||
)
|
||||
logger.info(f"Stored {msg.get('category')} alert from {msg.get('source_mmsi')}")
|
||||
except Exception as e:
|
||||
@@ -231,115 +217,101 @@ def monitor_rtl_stderr(process: subprocess.Popen) -> None:
|
||||
for line in process.stderr:
|
||||
if not dsc_running:
|
||||
break
|
||||
err_text = line.decode('utf-8', errors='replace').strip()
|
||||
err_text = line.decode("utf-8", errors="replace").strip()
|
||||
if err_text:
|
||||
logger.debug(f"[RTL_FM] {err_text}")
|
||||
|
||||
# Check for device busy error
|
||||
if 'usb_claim_interface' in err_text.lower():
|
||||
app_module.dsc_queue.put({
|
||||
'type': 'error',
|
||||
'error': 'SDR device busy',
|
||||
'error_type': 'DEVICE_BUSY',
|
||||
'suggestion': 'Use a different SDR device or stop other SDR processes'
|
||||
})
|
||||
if "usb_claim_interface" in err_text.lower():
|
||||
app_module.dsc_queue.put(
|
||||
{
|
||||
"type": "error",
|
||||
"error": "SDR device busy",
|
||||
"error_type": "DEVICE_BUSY",
|
||||
"suggestion": "Use a different SDR device or stop other SDR processes",
|
||||
}
|
||||
)
|
||||
|
||||
# Check for other common errors
|
||||
if 'no supported devices' in err_text.lower():
|
||||
app_module.dsc_queue.put({
|
||||
'type': 'error',
|
||||
'error': 'No SDR device found',
|
||||
'error_type': 'NO_DEVICE'
|
||||
})
|
||||
if "no supported devices" in err_text.lower():
|
||||
app_module.dsc_queue.put(
|
||||
{"type": "error", "error": "No SDR device found", "error_type": "NO_DEVICE"}
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
@dsc_bp.route('/status')
|
||||
@dsc_bp.route("/status")
|
||||
def get_status() -> Response:
|
||||
"""Get DSC decoder status."""
|
||||
global dsc_running
|
||||
|
||||
with app_module.dsc_lock:
|
||||
running = (
|
||||
dsc_running and
|
||||
app_module.dsc_process is not None and
|
||||
app_module.dsc_process.poll() is None
|
||||
)
|
||||
running = dsc_running and app_module.dsc_process is not None and app_module.dsc_process.poll() is None
|
||||
|
||||
# Get message counts
|
||||
message_count = len(app_module.dsc_messages)
|
||||
alert_summary = get_dsc_alert_summary()
|
||||
|
||||
return jsonify({
|
||||
'running': running,
|
||||
'frequency': DSC_VHF_FREQUENCY_MHZ,
|
||||
'message_count': message_count,
|
||||
'alerts': alert_summary
|
||||
})
|
||||
return jsonify(
|
||||
{
|
||||
"running": running,
|
||||
"frequency": DSC_VHF_FREQUENCY_MHZ,
|
||||
"message_count": message_count,
|
||||
"alerts": alert_summary,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@dsc_bp.route('/tools')
|
||||
@dsc_bp.route("/tools")
|
||||
def check_tools() -> Response:
|
||||
"""Check DSC decoder tool availability."""
|
||||
tools = _check_dsc_tools()
|
||||
return jsonify(tools)
|
||||
|
||||
|
||||
@dsc_bp.route('/start', methods=['POST'])
|
||||
@dsc_bp.route("/start", methods=["POST"])
|
||||
def start_decoding() -> Response:
|
||||
"""Start DSC decoder."""
|
||||
global dsc_running
|
||||
|
||||
with app_module.dsc_lock:
|
||||
if app_module.dsc_process and app_module.dsc_process.poll() is None:
|
||||
return jsonify({
|
||||
'status': 'error',
|
||||
'message': 'DSC decoder already running'
|
||||
}), 409
|
||||
return jsonify({"status": "error", "message": "DSC decoder already running"}), 409
|
||||
|
||||
# Check tools
|
||||
tools = _check_dsc_tools()
|
||||
if not tools['ready']:
|
||||
if not tools["ready"]:
|
||||
missing = []
|
||||
if not tools['rtl_fm']['available']:
|
||||
missing.append('rtl_fm')
|
||||
if not tools['dsc_decoder']['available']:
|
||||
missing.append('dsc-decoder')
|
||||
if not tools['scipy']['available']:
|
||||
missing.append('scipy/numpy')
|
||||
if not tools["rtl_fm"]["available"]:
|
||||
missing.append("rtl_fm")
|
||||
if not tools["dsc_decoder"]["available"]:
|
||||
missing.append("dsc-decoder")
|
||||
if not tools["scipy"]["available"]:
|
||||
missing.append("scipy/numpy")
|
||||
|
||||
return jsonify({
|
||||
'status': 'error',
|
||||
'message': f'Missing required tools: {", ".join(missing)}'
|
||||
}), 400
|
||||
return jsonify({"status": "error", "message": f"Missing required tools: {', '.join(missing)}"}), 400
|
||||
|
||||
data = request.json or {}
|
||||
|
||||
# Validate device
|
||||
try:
|
||||
device = validate_device_index(data.get('device', '0'))
|
||||
device = validate_device_index(data.get("device", "0"))
|
||||
except ValueError as e:
|
||||
return jsonify({
|
||||
'status': 'error',
|
||||
'message': str(e)
|
||||
}), 400
|
||||
return jsonify({"status": "error", "message": str(e)}), 400
|
||||
|
||||
# Validate gain
|
||||
try:
|
||||
gain = validate_gain(data.get('gain', '40'))
|
||||
gain = validate_gain(data.get("gain", "40"))
|
||||
except ValueError as e:
|
||||
return jsonify({
|
||||
'status': 'error',
|
||||
'message': str(e)
|
||||
}), 400
|
||||
return jsonify({"status": "error", "message": str(e)}), 400
|
||||
|
||||
# Get SDR type from request
|
||||
sdr_type_str = data.get('sdr_type', 'rtlsdr')
|
||||
sdr_type_str = data.get("sdr_type", "rtlsdr")
|
||||
|
||||
# Check for rtl_tcp (remote SDR) connection
|
||||
rtl_tcp_host = data.get('rtl_tcp_host')
|
||||
rtl_tcp_port = data.get('rtl_tcp_port', 1234)
|
||||
rtl_tcp_host = data.get("rtl_tcp_host")
|
||||
rtl_tcp_port = data.get("rtl_tcp_port", 1234)
|
||||
|
||||
try:
|
||||
sdr_type = SDRType(sdr_type_str)
|
||||
@@ -350,13 +322,9 @@ def start_decoding() -> Response:
|
||||
global dsc_active_device, dsc_active_sdr_type
|
||||
if not rtl_tcp_host:
|
||||
device_int = int(device)
|
||||
error = app_module.claim_sdr_device(device_int, 'dsc', sdr_type_str)
|
||||
error = app_module.claim_sdr_device(device_int, "dsc", sdr_type_str)
|
||||
if error:
|
||||
return jsonify({
|
||||
'status': 'error',
|
||||
'error_type': 'DEVICE_BUSY',
|
||||
'message': error
|
||||
}), 409
|
||||
return jsonify({"status": "error", "error_type": "DEVICE_BUSY", "message": error}), 409
|
||||
|
||||
dsc_active_device = device_int
|
||||
dsc_active_sdr_type = sdr_type_str
|
||||
@@ -369,7 +337,7 @@ def start_decoding() -> Response:
|
||||
break
|
||||
|
||||
# Build rtl_fm command via SDR abstraction layer
|
||||
decoder_path = tools['dsc_decoder']['path']
|
||||
decoder_path = tools["dsc_decoder"]["path"]
|
||||
|
||||
if rtl_tcp_host:
|
||||
try:
|
||||
@@ -383,39 +351,33 @@ def start_decoding() -> Response:
|
||||
sdr_device = SDRFactory.create_default_device(sdr_type, index=int(device))
|
||||
|
||||
builder = SDRFactory.get_builder(sdr_device.sdr_type)
|
||||
rtl_cmd = list(builder.build_fm_demod_command(
|
||||
device=sdr_device,
|
||||
frequency_mhz=DSC_VHF_FREQUENCY_MHZ,
|
||||
sample_rate=DSC_SAMPLE_RATE,
|
||||
gain=float(gain) if gain and str(gain) != '0' else None,
|
||||
modulation='fm',
|
||||
squelch=0,
|
||||
))
|
||||
rtl_cmd = list(
|
||||
builder.build_fm_demod_command(
|
||||
device=sdr_device,
|
||||
frequency_mhz=DSC_VHF_FREQUENCY_MHZ,
|
||||
sample_rate=DSC_SAMPLE_RATE,
|
||||
gain=float(gain) if gain and str(gain) != "0" else None,
|
||||
modulation="fm",
|
||||
squelch=0,
|
||||
)
|
||||
)
|
||||
# Ensure trailing '-' for stdin piping and add DC blocking filter
|
||||
if rtl_cmd and rtl_cmd[-1] == '-':
|
||||
rtl_cmd = rtl_cmd[:-1] + ['-E', 'dc', '-']
|
||||
if rtl_cmd and rtl_cmd[-1] == "-":
|
||||
rtl_cmd = rtl_cmd[:-1] + ["-E", "dc", "-"]
|
||||
|
||||
# Decoder command
|
||||
decoder_cmd = [decoder_path]
|
||||
|
||||
full_cmd = ' '.join(rtl_cmd) + ' | ' + ' '.join(decoder_cmd)
|
||||
full_cmd = " ".join(rtl_cmd) + " | " + " ".join(decoder_cmd)
|
||||
logger.info(f"Starting DSC decoder: {full_cmd}")
|
||||
|
||||
try:
|
||||
# Start rtl_fm subprocess
|
||||
rtl_process = subprocess.Popen(
|
||||
rtl_cmd,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE
|
||||
)
|
||||
rtl_process = subprocess.Popen(rtl_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
register_process(rtl_process)
|
||||
|
||||
# Start stderr monitor thread
|
||||
stderr_thread = threading.Thread(
|
||||
target=monitor_rtl_stderr,
|
||||
args=(rtl_process,),
|
||||
daemon=True
|
||||
)
|
||||
stderr_thread = threading.Thread(target=monitor_rtl_stderr, args=(rtl_process,), daemon=True)
|
||||
stderr_thread.start()
|
||||
|
||||
# Create PTY for decoder output
|
||||
@@ -423,11 +385,7 @@ def start_decoding() -> Response:
|
||||
|
||||
# Start decoder subprocess
|
||||
decoder_process = subprocess.Popen(
|
||||
decoder_cmd,
|
||||
stdin=rtl_process.stdout,
|
||||
stdout=slave_fd,
|
||||
stderr=slave_fd,
|
||||
close_fds=True
|
||||
decoder_cmd, stdin=rtl_process.stdout, stdout=slave_fd, stderr=slave_fd, close_fds=True
|
||||
)
|
||||
register_process(decoder_process)
|
||||
|
||||
@@ -440,20 +398,18 @@ def start_decoding() -> Response:
|
||||
dsc_running = True
|
||||
|
||||
# Start output streaming thread
|
||||
output_thread = threading.Thread(
|
||||
target=stream_dsc_decoder,
|
||||
args=(master_fd, decoder_process),
|
||||
daemon=True
|
||||
)
|
||||
output_thread = threading.Thread(target=stream_dsc_decoder, args=(master_fd, decoder_process), daemon=True)
|
||||
output_thread.start()
|
||||
|
||||
return jsonify({
|
||||
'status': 'started',
|
||||
'frequency': DSC_VHF_FREQUENCY_MHZ,
|
||||
'device': device,
|
||||
'gain': gain,
|
||||
'command': full_cmd
|
||||
})
|
||||
return jsonify(
|
||||
{
|
||||
"status": "started",
|
||||
"frequency": DSC_VHF_FREQUENCY_MHZ,
|
||||
"device": device,
|
||||
"gain": gain,
|
||||
"command": full_cmd,
|
||||
}
|
||||
)
|
||||
|
||||
except FileNotFoundError as e:
|
||||
# Kill orphaned rtl_fm process
|
||||
@@ -465,13 +421,10 @@ def start_decoding() -> Response:
|
||||
rtl_process.kill()
|
||||
# Release device on failure
|
||||
if dsc_active_device is not None:
|
||||
app_module.release_sdr_device(dsc_active_device, dsc_active_sdr_type or 'rtlsdr')
|
||||
app_module.release_sdr_device(dsc_active_device, dsc_active_sdr_type or "rtlsdr")
|
||||
dsc_active_device = None
|
||||
dsc_active_sdr_type = None
|
||||
return jsonify({
|
||||
'status': 'error',
|
||||
'message': f'Tool not found: {e.filename}'
|
||||
}), 400
|
||||
return jsonify({"status": "error", "message": f"Tool not found: {e.filename}"}), 400
|
||||
except Exception as e:
|
||||
# Kill orphaned rtl_fm process if it was started
|
||||
try:
|
||||
@@ -482,24 +435,21 @@ def start_decoding() -> Response:
|
||||
rtl_process.kill()
|
||||
# Release device on failure
|
||||
if dsc_active_device is not None:
|
||||
app_module.release_sdr_device(dsc_active_device, dsc_active_sdr_type or 'rtlsdr')
|
||||
app_module.release_sdr_device(dsc_active_device, dsc_active_sdr_type or "rtlsdr")
|
||||
dsc_active_device = None
|
||||
dsc_active_sdr_type = None
|
||||
logger.error(f"Failed to start DSC decoder: {e}")
|
||||
return jsonify({
|
||||
'status': 'error',
|
||||
'message': str(e)
|
||||
}), 500
|
||||
return jsonify({"status": "error", "message": str(e)}), 500
|
||||
|
||||
|
||||
@dsc_bp.route('/stop', methods=['POST'])
|
||||
@dsc_bp.route("/stop", methods=["POST"])
|
||||
def stop_decoding() -> Response:
|
||||
"""Stop DSC decoder."""
|
||||
global dsc_running, dsc_active_device, dsc_active_sdr_type
|
||||
|
||||
with app_module.dsc_lock:
|
||||
if not app_module.dsc_process:
|
||||
return jsonify({'status': 'not_running'})
|
||||
return jsonify({"status": "not_running"})
|
||||
|
||||
dsc_running = False
|
||||
|
||||
@@ -530,116 +480,94 @@ def stop_decoding() -> Response:
|
||||
|
||||
# Release device from registry
|
||||
if dsc_active_device is not None:
|
||||
app_module.release_sdr_device(dsc_active_device, dsc_active_sdr_type or 'rtlsdr')
|
||||
app_module.release_sdr_device(dsc_active_device, dsc_active_sdr_type or "rtlsdr")
|
||||
dsc_active_device = None
|
||||
dsc_active_sdr_type = None
|
||||
|
||||
return jsonify({'status': 'stopped'})
|
||||
return jsonify({"status": "stopped"})
|
||||
|
||||
|
||||
@dsc_bp.route('/stream')
|
||||
@dsc_bp.route("/stream")
|
||||
def stream() -> Response:
|
||||
"""SSE stream for real-time DSC messages."""
|
||||
|
||||
def _on_msg(msg: dict[str, Any]) -> None:
|
||||
process_event('dsc', msg, msg.get('type'))
|
||||
process_event("dsc", msg, msg.get("type"))
|
||||
|
||||
response = Response(
|
||||
sse_stream_fanout(
|
||||
source_queue=app_module.dsc_queue,
|
||||
channel_key='dsc',
|
||||
channel_key="dsc",
|
||||
timeout=1.0,
|
||||
keepalive_interval=30.0,
|
||||
on_message=_on_msg,
|
||||
),
|
||||
mimetype='text/event-stream',
|
||||
mimetype="text/event-stream",
|
||||
)
|
||||
response.headers['Cache-Control'] = 'no-cache'
|
||||
response.headers['X-Accel-Buffering'] = 'no'
|
||||
response.headers['Connection'] = 'keep-alive'
|
||||
response.headers["Cache-Control"] = "no-cache"
|
||||
response.headers["X-Accel-Buffering"] = "no"
|
||||
response.headers["Connection"] = "keep-alive"
|
||||
return response
|
||||
|
||||
|
||||
@dsc_bp.route('/messages')
|
||||
@dsc_bp.route("/messages")
|
||||
def get_messages() -> Response:
|
||||
"""Get current DSC messages from transient store."""
|
||||
messages = list(app_module.dsc_messages.values())
|
||||
|
||||
# Sort by timestamp (newest first)
|
||||
messages.sort(key=lambda m: m.get('timestamp', ''), reverse=True)
|
||||
messages.sort(key=lambda m: m.get("timestamp", ""), reverse=True)
|
||||
|
||||
return jsonify({
|
||||
'count': len(messages),
|
||||
'messages': messages
|
||||
})
|
||||
return jsonify({"count": len(messages), "messages": messages})
|
||||
|
||||
|
||||
@dsc_bp.route('/alerts')
|
||||
@dsc_bp.route("/alerts")
|
||||
def get_alerts_endpoint() -> Response:
|
||||
"""Get stored DSC alerts (paginated)."""
|
||||
# Parse query params
|
||||
category = request.args.get('category')
|
||||
acknowledged = request.args.get('acknowledged')
|
||||
limit = min(int(request.args.get('limit', 50)), 200)
|
||||
offset = int(request.args.get('offset', 0))
|
||||
category = request.args.get("category")
|
||||
acknowledged = request.args.get("acknowledged")
|
||||
limit = min(int(request.args.get("limit", 50)), 200)
|
||||
offset = int(request.args.get("offset", 0))
|
||||
|
||||
# Convert acknowledged param
|
||||
ack_filter = None
|
||||
if acknowledged is not None:
|
||||
ack_filter = acknowledged.lower() in ('true', '1', 'yes')
|
||||
ack_filter = acknowledged.lower() in ("true", "1", "yes")
|
||||
|
||||
alerts = get_dsc_alerts(
|
||||
category=category,
|
||||
acknowledged=ack_filter,
|
||||
limit=limit,
|
||||
offset=offset
|
||||
)
|
||||
alerts = get_dsc_alerts(category=category, acknowledged=ack_filter, limit=limit, offset=offset)
|
||||
|
||||
summary = get_dsc_alert_summary()
|
||||
|
||||
return jsonify({
|
||||
'alerts': alerts,
|
||||
'count': len(alerts),
|
||||
'summary': summary,
|
||||
'pagination': {
|
||||
'limit': limit,
|
||||
'offset': offset
|
||||
}
|
||||
})
|
||||
return jsonify(
|
||||
{"alerts": alerts, "count": len(alerts), "summary": summary, "pagination": {"limit": limit, "offset": offset}}
|
||||
)
|
||||
|
||||
|
||||
@dsc_bp.route('/alerts/<int:alert_id>')
|
||||
@dsc_bp.route("/alerts/<int:alert_id>")
|
||||
def get_alert(alert_id: int) -> Response:
|
||||
"""Get a specific DSC alert by ID."""
|
||||
alert = get_dsc_alert(alert_id)
|
||||
if not alert:
|
||||
return jsonify({
|
||||
'status': 'error',
|
||||
'message': 'Alert not found'
|
||||
}), 404
|
||||
return jsonify({"status": "error", "message": "Alert not found"}), 404
|
||||
|
||||
return jsonify(alert)
|
||||
|
||||
|
||||
@dsc_bp.route('/alerts/<int:alert_id>/acknowledge', methods=['POST'])
|
||||
@dsc_bp.route("/alerts/<int:alert_id>/acknowledge", methods=["POST"])
|
||||
def acknowledge_alert(alert_id: int) -> Response:
|
||||
"""Acknowledge a DSC alert."""
|
||||
data = request.json or {}
|
||||
notes = data.get('notes')
|
||||
notes = data.get("notes")
|
||||
|
||||
success = acknowledge_dsc_alert(alert_id, notes)
|
||||
if not success:
|
||||
return jsonify({
|
||||
'status': 'error',
|
||||
'message': 'Alert not found'
|
||||
}), 404
|
||||
return jsonify({"status": "error", "message": "Alert not found"}), 404
|
||||
|
||||
return jsonify({
|
||||
'status': 'acknowledged',
|
||||
'alert_id': alert_id
|
||||
})
|
||||
return jsonify({"status": "acknowledged", "alert_id": alert_id})
|
||||
|
||||
|
||||
@dsc_bp.route('/alerts/summary')
|
||||
@dsc_bp.route("/alerts/summary")
|
||||
def get_alerts_summary() -> Response:
|
||||
"""Get summary of unacknowledged DSC alerts."""
|
||||
summary = get_dsc_alert_summary()
|
||||
|
||||
Reference in New Issue
Block a user