feat(monitoring): add priority screenshot pipeline with screenshot_type + docs cleanup
Implement end-to-end support for typed screenshots and priority rendering in monitoring. Added - Accept and forward screenshot_type from MQTT screenshot/dashboard payloads (periodic, event_start, event_stop) - Extend screenshot upload handling to persist typed screenshots and metadata - Add dedicated priority screenshot serving endpoint with fallback behavior - Extend monitoring overview with priority screenshot fields and summary count - Add configurable PRIORITY_SCREENSHOT_TTL_SECONDS window for active priority state Fixed - Ensure screenshot cache-busting updates reliably via screenshot hash updates - Preserve normal periodic screenshot flow while introducing event_start/event_stop priority path Improved - Monitoring dashboard now displays screenshot type badges - Adaptive polling: faster refresh while priority screenshots are active - Priority screenshot presentation is surfaced immediately to operators Docs - Update README and copilot-instructions to match new screenshot_type behavior, priority endpoint, TTL config, monitoring fields, and retention model - Remove redundant/duplicate documentation blocks and improve troubleshooting section clarity
This commit is contained in:
@@ -11,6 +11,7 @@ import glob
|
||||
from server.serializers import dict_to_camel_case
|
||||
|
||||
client_logs_bp = Blueprint("client_logs", __name__, url_prefix="/api/client-logs")
|
||||
PRIORITY_SCREENSHOT_TTL_SECONDS = int(os.environ.get("PRIORITY_SCREENSHOT_TTL_SECONDS", "120"))
|
||||
|
||||
|
||||
def _grace_period_seconds():
|
||||
@@ -90,6 +91,34 @@ def _infer_last_screenshot_ts(client_uuid):
|
||||
return None
|
||||
|
||||
|
||||
def _load_screenshot_metadata(client_uuid):
|
||||
screenshots_dir = os.path.join(os.path.dirname(__file__), "..", "screenshots")
|
||||
metadata_path = os.path.join(screenshots_dir, f"{client_uuid}_meta.json")
|
||||
if not os.path.exists(metadata_path):
|
||||
return {}
|
||||
|
||||
try:
|
||||
with open(metadata_path, "r", encoding="utf-8") as metadata_file:
|
||||
data = json.load(metadata_file)
|
||||
return data if isinstance(data, dict) else {}
|
||||
except Exception:
|
||||
return {}
|
||||
|
||||
|
||||
def _is_priority_screenshot_active(priority_received_at):
|
||||
if not priority_received_at:
|
||||
return False
|
||||
|
||||
try:
|
||||
normalized = str(priority_received_at).replace("Z", "+00:00")
|
||||
parsed = datetime.fromisoformat(normalized)
|
||||
parsed_utc = _to_utc(parsed)
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
return (datetime.now(timezone.utc) - parsed_utc) <= timedelta(seconds=PRIORITY_SCREENSHOT_TTL_SECONDS)
|
||||
|
||||
|
||||
@client_logs_bp.route("/test", methods=["GET"])
|
||||
def test_client_logs():
|
||||
"""Test endpoint to verify logging infrastructure (no auth required)"""
|
||||
@@ -326,6 +355,7 @@ def get_monitoring_overview():
|
||||
"critical_clients": 0,
|
||||
"error_logs": 0,
|
||||
"warn_logs": 0,
|
||||
"active_priority_screenshots": 0,
|
||||
}
|
||||
|
||||
for client, group_name in clients:
|
||||
@@ -352,6 +382,12 @@ def get_monitoring_overview():
|
||||
)
|
||||
|
||||
screenshot_ts = client.last_screenshot_analyzed or _infer_last_screenshot_ts(client.uuid)
|
||||
screenshot_meta = _load_screenshot_metadata(client.uuid)
|
||||
latest_screenshot_type = screenshot_meta.get("latest_screenshot_type") or "periodic"
|
||||
priority_screenshot_type = screenshot_meta.get("last_priority_screenshot_type")
|
||||
priority_screenshot_received_at = screenshot_meta.get("last_priority_received_at")
|
||||
has_active_priority = _is_priority_screenshot_active(priority_screenshot_received_at)
|
||||
screenshot_url = f"/screenshots/{client.uuid}/priority" if has_active_priority else f"/screenshots/{client.uuid}"
|
||||
|
||||
clients_payload.append({
|
||||
"uuid": client.uuid,
|
||||
@@ -372,7 +408,11 @@ def get_monitoring_overview():
|
||||
"screen_health_status": screen_health_status,
|
||||
"last_screenshot_analyzed": screenshot_ts.isoformat() if screenshot_ts else None,
|
||||
"last_screenshot_hash": client.last_screenshot_hash,
|
||||
"screenshot_url": f"/screenshots/{client.uuid}",
|
||||
"latest_screenshot_type": latest_screenshot_type,
|
||||
"priority_screenshot_type": priority_screenshot_type,
|
||||
"priority_screenshot_received_at": priority_screenshot_received_at,
|
||||
"has_active_priority_screenshot": has_active_priority,
|
||||
"screenshot_url": screenshot_url,
|
||||
"log_counts_24h": {
|
||||
"error": log_counts["ERROR"],
|
||||
"warn": log_counts["WARN"],
|
||||
@@ -386,6 +426,8 @@ def get_monitoring_overview():
|
||||
summary_counts["total_clients"] += 1
|
||||
summary_counts["error_logs"] += log_counts["ERROR"]
|
||||
summary_counts["warn_logs"] += log_counts["WARN"]
|
||||
if has_active_priority:
|
||||
summary_counts["active_priority_screenshots"] += 1
|
||||
if is_alive:
|
||||
summary_counts["online_clients"] += 1
|
||||
else:
|
||||
|
||||
Reference in New Issue
Block a user