mirror of
https://github.com/smittix/intercept.git
synced 2026-07-19 23:08:10 -07:00
fix: resolve two-window hang and sweep UI/theming updates
Fix app becoming unresponsive when two browser windows are open: the root cause was HTTP/1.1 connection pool exhaustion (6-connection limit per origin). VoiceAlerts was opening 3 SSE streams per window by default, so two windows produced 8 connections and permanently starved all regular HTTP requests. - voice-alerts.js: default all streams to false (opt-in) to stay within the browser connection limit; existing user preferences in localStorage are preserved - routes/alerts.py: replace direct AlertManager.stream_events() with sse_stream_fanout so both windows receive every alert instead of competing for the same queue - routes/bluetooth_v2.py: same fanout fix via subscribe_fanout_queue, preserving named SSE events (device_update, scan_started, etc.) Also includes accumulated UI/theming changes: accent-cyan CSS variable sweep across mode CSS/JS files, standalone dashboard pages, template updates, satellite TLE data refresh, and tile provider default rename. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+31
-30
@@ -2,74 +2,75 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Generator
|
||||
|
||||
from flask import Blueprint, Response, request
|
||||
|
||||
from utils.alerts import get_alert_manager
|
||||
from utils.responses import api_error, api_success
|
||||
from utils.sse import format_sse
|
||||
from utils.sse import sse_stream_fanout
|
||||
|
||||
alerts_bp = Blueprint('alerts', __name__, url_prefix='/alerts')
|
||||
alerts_bp = Blueprint("alerts", __name__, url_prefix="/alerts")
|
||||
|
||||
|
||||
@alerts_bp.route('/rules', methods=['GET'])
|
||||
@alerts_bp.route("/rules", methods=["GET"])
|
||||
def list_rules():
|
||||
manager = get_alert_manager()
|
||||
include_disabled = request.args.get('all') in ('1', 'true', 'yes')
|
||||
return api_success(data={'rules': manager.list_rules(include_disabled=include_disabled)})
|
||||
include_disabled = request.args.get("all") in ("1", "true", "yes")
|
||||
return api_success(data={"rules": manager.list_rules(include_disabled=include_disabled)})
|
||||
|
||||
|
||||
@alerts_bp.route('/rules', methods=['POST'])
|
||||
@alerts_bp.route("/rules", methods=["POST"])
|
||||
def create_rule():
|
||||
data = request.get_json() or {}
|
||||
if not isinstance(data.get('match', {}), dict):
|
||||
return api_error('match must be a JSON object', 400)
|
||||
if not isinstance(data.get("match", {}), dict):
|
||||
return api_error("match must be a JSON object", 400)
|
||||
|
||||
manager = get_alert_manager()
|
||||
rule_id = manager.add_rule(data)
|
||||
return api_success(data={'rule_id': rule_id})
|
||||
return api_success(data={"rule_id": rule_id})
|
||||
|
||||
|
||||
@alerts_bp.route('/rules/<int:rule_id>', methods=['PUT', 'PATCH'])
|
||||
@alerts_bp.route("/rules/<int:rule_id>", methods=["PUT", "PATCH"])
|
||||
def update_rule(rule_id: int):
|
||||
data = request.get_json() or {}
|
||||
manager = get_alert_manager()
|
||||
ok = manager.update_rule(rule_id, data)
|
||||
if not ok:
|
||||
return api_error('Rule not found or no changes', 404)
|
||||
return api_error("Rule not found or no changes", 404)
|
||||
return api_success()
|
||||
|
||||
|
||||
@alerts_bp.route('/rules/<int:rule_id>', methods=['DELETE'])
|
||||
@alerts_bp.route("/rules/<int:rule_id>", methods=["DELETE"])
|
||||
def delete_rule(rule_id: int):
|
||||
manager = get_alert_manager()
|
||||
ok = manager.delete_rule(rule_id)
|
||||
if not ok:
|
||||
return api_error('Rule not found', 404)
|
||||
return api_error("Rule not found", 404)
|
||||
return api_success()
|
||||
|
||||
|
||||
@alerts_bp.route('/events', methods=['GET'])
|
||||
@alerts_bp.route("/events", methods=["GET"])
|
||||
def list_events():
|
||||
manager = get_alert_manager()
|
||||
limit = request.args.get('limit', default=100, type=int)
|
||||
mode = request.args.get('mode')
|
||||
severity = request.args.get('severity')
|
||||
limit = request.args.get("limit", default=100, type=int)
|
||||
mode = request.args.get("mode")
|
||||
severity = request.args.get("severity")
|
||||
events = manager.list_events(limit=limit, mode=mode, severity=severity)
|
||||
return api_success(data={'events': events})
|
||||
return api_success(data={"events": events})
|
||||
|
||||
|
||||
@alerts_bp.route('/stream', methods=['GET'])
|
||||
@alerts_bp.route("/stream", methods=["GET"])
|
||||
def stream_alerts() -> Response:
|
||||
manager = get_alert_manager()
|
||||
|
||||
def generate() -> Generator[str, None, None]:
|
||||
for event in manager.stream_events(timeout=1.0):
|
||||
yield format_sse(event)
|
||||
|
||||
response = Response(generate(), mimetype='text/event-stream')
|
||||
response.headers['Cache-Control'] = 'no-cache'
|
||||
response.headers['X-Accel-Buffering'] = 'no'
|
||||
response.headers['Connection'] = 'keep-alive'
|
||||
response = Response(
|
||||
sse_stream_fanout(
|
||||
source_queue=manager._queue,
|
||||
channel_key="alerts",
|
||||
timeout=1.0,
|
||||
keepalive_interval=30.0,
|
||||
),
|
||||
mimetype="text/event-stream",
|
||||
)
|
||||
response.headers["Cache-Control"] = "no-cache"
|
||||
response.headers["X-Accel-Buffering"] = "no"
|
||||
response.headers["Connection"] = "keep-alive"
|
||||
return response
|
||||
|
||||
+542
-523
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user