"""MY Network Monitoring Interface - веб-интерфейс мониторинга сети в хакерском стиле.""" import asyncio import json import logging from datetime import datetime, timedelta from typing import Dict, List, Any from fastapi import APIRouter, Request, HTTPException from fastapi.responses import HTMLResponse from fastapi.templating import Jinja2Templates from pathlib import Path logger = logging.getLogger(__name__) # Создать router для мониторинга router = APIRouter(prefix="/api/my/monitor", tags=["MY Network Monitoring"]) # Настроить шаблоны templates_dir = Path(__file__).parent.parent.parent / "templates" templates_dir.mkdir(exist_ok=True) templates = Jinja2Templates(directory=str(templates_dir)) def get_node_service(): """Получить сервис ноды.""" try: from app.core.my_network.node_service import get_node_service return get_node_service() except Exception as e: logger.error(f"Error getting node service: {e}") return None @router.get("/", response_class=HTMLResponse) async def monitoring_dashboard(request: Request): """Главная страница мониторинга MY Network.""" try: # Получить данные для дашборда node_service = get_node_service() if not node_service: monitoring_data = { "status": "offline", "error": "MY Network service not available" } else: # Собрать данные со всех компонентов node_info = await node_service.get_node_info() peers_info = await node_service.get_peers_info() sync_status = await node_service.sync_manager.get_sync_status() monitoring_data = { "status": "online", "node_info": node_info, "peers_info": peers_info, "sync_status": sync_status, "timestamp": datetime.utcnow().isoformat() } return templates.TemplateResponse("my_network_monitor.html", { "request": request, "monitoring_data": monitoring_data }) except Exception as e: logger.error(f"Error rendering monitoring dashboard: {e}") # Fallback HTML если шаблоны не работают return HTMLResponse(content=generate_fallback_html(str(e))) @router.get("/ascii") async def get_ascii_status(): """Получить ASCII статус сети.""" try: node_service = get_node_service() if not node_service: return {"ascii": generate_offline_ascii(), "status": "offline"} # Получить данные node_info = await node_service.get_node_info() peers_info = await node_service.get_peers_info() sync_status = await node_service.sync_manager.get_sync_status() # Генерировать ASCII ascii_art = await generate_network_ascii(node_info, peers_info, sync_status) return { "ascii": ascii_art, "status": "online", "timestamp": datetime.utcnow().isoformat() } except Exception as e: logger.error(f"Error generating ASCII status: {e}") return {"ascii": generate_error_ascii(str(e)), "status": "error"} @router.get("/live") async def live_monitoring_data(): """Получить живые данные для мониторинга.""" try: node_service = get_node_service() if not node_service: raise HTTPException(status_code=503, detail="MY Network service unavailable") # Получить свежие данные node_info = await node_service.get_node_info() peers_info = await node_service.get_peers_info() sync_status = await node_service.sync_manager.get_sync_status() # Статистика сети network_stats = { "connected_peers": peers_info["peer_count"], "active_syncs": sync_status["active_syncs"], "queue_size": sync_status["queue_size"], "uptime": node_info["uptime"], "status": node_info["status"] } return { "success": True, "data": { "node_info": node_info, "network_stats": network_stats, "peers": peers_info["peers"][:10], # Показать только первые 10 пиров "sync_status": sync_status }, "timestamp": datetime.utcnow().isoformat() } except HTTPException: raise except Exception as e: logger.error(f"Error getting live monitoring data: {e}") raise HTTPException(status_code=500, detail=str(e)) async def generate_network_ascii(node_info: Dict[str, Any], peers_info: Dict[str, Any], sync_status: Dict[str, Any]) -> str: """Генерировать ASCII представление состояния сети.""" ascii_parts = [] # Заголовок ascii_parts.append(""" ╔══════════════════════════════════════════════════════════════════════════════╗ ║ MY NETWORK v2.0 ║ ║ Distributed Content Protocol ║ ╚══════════════════════════════════════════════════════════════════════════════╝ """) # Информация о ноде status_indicator = "🟢" if node_info.get("status") == "running" else "🔴" uptime_hours = int(node_info.get("uptime", 0) / 3600) ascii_parts.append(f""" ┌─ NODE STATUS ────────────────────────────────────────────────────────────────┐ │ Node ID: {node_info.get('node_id', 'unknown')[:16]}... │ │ Status: {status_indicator} {node_info.get('status', 'unknown').upper()} │ │ Uptime: {uptime_hours}h {int((node_info.get('uptime', 0) % 3600) / 60)}m │ │ Version: MY Network {node_info.get('version', '2.0')} │ └──────────────────────────────────────────────────────────────────────────────┘ """) # Информация о пирах peer_count = peers_info.get("peer_count", 0) peer_status = "🌐" if peer_count > 0 else "🏝️" ascii_parts.append(f""" ┌─ NETWORK STATUS ─────────────────────────────────────────────────────────────┐ │ Connected Peers: {peer_status} {peer_count:>3} │ │ Known Nodes: {len(peers_info.get('peers', [])):>3} │ │ Network Health: {'CONNECTED' if peer_count > 0 else 'ISOLATED':>9} │ └──────────────────────────────────────────────────────────────────────────────┘ """) # Статус синхронизации sync_running = sync_status.get("is_running", False) active_syncs = sync_status.get("active_syncs", 0) queue_size = sync_status.get("queue_size", 0) sync_indicator = "⚡" if sync_running else "⏸️" ascii_parts.append(f""" ┌─ SYNC STATUS ────────────────────────────────────────────────────────────────┐ │ Sync Engine: {sync_indicator} {'RUNNING' if sync_running else 'STOPPED':>7} │ │ Active Syncs: {active_syncs:>3} │ │ Queue Size: {queue_size:>3} │ │ Workers: {sync_status.get('workers_count', 0):>3} │ └──────────────────────────────────────────────────────────────────────────────┘ """) # Визуализация сети if peer_count > 0: ascii_parts.append(generate_network_topology(peers_info.get("peers", [])[:6])) # Недавние события синхронизации recent_syncs = sync_status.get("recent_syncs", []) if recent_syncs: ascii_parts.append(generate_sync_history(recent_syncs[-5:])) # Подвал current_time = datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S UTC") ascii_parts.append(f""" ╔══════════════════════════════════════════════════════════════════════════════╗ ║ Last Updated: {current_time} ║ ║ MY Network Protocol - Decentralized Content Distribution System ║ ╚══════════════════════════════════════════════════════════════════════════════╝ """) return "".join(ascii_parts) def generate_network_topology(peers: List[Dict[str, Any]]) -> str: """Генерировать ASCII топологию сети.""" topology = [""" ┌─ NETWORK TOPOLOGY ───────────────────────────────────────────────────────────┐ │ │ │ [THIS NODE] │ │ │ │"""] if len(peers) == 1: topology.append("│ │ │") topology.append(f"│ [{peers[0].get('node_id', 'unknown')[:8]}...] │") elif len(peers) <= 3: topology.append("│ ┌───────┼───────┐ │") for i, peer in enumerate(peers): spaces = " " if i == 0 else (" " if i == 1 else " ") topology.append(f"│{spaces}[{peer.get('node_id', 'unknown')[:8]}...] │") else: topology.append("│ ┌───────┬───────┼───────┬───────┐ │") topology.append("│ │ │ │ │ │ │") for i, peer in enumerate(peers[:5]): if i < 5: spaces = [" ", " ", " ", " ", " "][i] topology.append(f"│{spaces}[{peer.get('node_id', 'unknown')[:6]}] │") if len(peers) > 5: topology.append("│ ... │") topology.append("│ │") topology.append("└──────────────────────────────────────────────────────────────────────────────┘") return "\n".join(topology) + "\n" def generate_sync_history(recent_syncs: List[Dict[str, Any]]) -> str: """Генерировать историю синхронизации.""" history = [""" ┌─ RECENT SYNC ACTIVITY ───────────────────────────────────────────────────────┐"""] if not recent_syncs: history.append("│ No recent sync activity │") else: for sync in recent_syncs: content_hash = sync.get("content_hash", "unknown")[:12] status = sync.get("status", "unknown") status_icon = {"completed": "✅", "failed": "❌", "partial": "⚠️"}.get(status, "❓") history.append(f"│ {status_icon} {content_hash}... - {status.upper():>9} │") history.append("└──────────────────────────────────────────────────────────────────────────────┘") return "\n".join(history) + "\n" def generate_offline_ascii() -> str: """Генерировать ASCII для офлайн состояния.""" return """ ╔══════════════════════════════════════════════════════════════════════════════╗ ║ MY NETWORK v2.0 ║ ║ Distributed Content Protocol ║ ╚══════════════════════════════════════════════════════════════════════════════╝ ┌─ SYSTEM STATUS ──────────────────────────────────────────────────────────────┐ │ │ │ 🔴 OFFLINE │ │ │ │ MY Network service is not available │ │ │ └──────────────────────────────────────────────────────────────────────────────┘ ╔══════════════════════════════════════════════════════════════════════════════╗ ║ Status: OFFLINE - Service not initialized ║ ╚══════════════════════════════════════════════════════════════════════════════╝ """ def generate_error_ascii(error_message: str) -> str: """Генерировать ASCII для ошибки.""" return f""" ╔══════════════════════════════════════════════════════════════════════════════╗ ║ MY NETWORK v2.0 ║ ║ Distributed Content Protocol ║ ╚══════════════════════════════════════════════════════════════════════════════╝ ┌─ ERROR STATE ────────────────────────────────────────────────────────────────┐ │ │ │ ❌ ERROR │ │ │ │ {error_message[:64]:^64} │ │ │ └──────────────────────────────────────────────────────────────────────────────┘ ╔══════════════════════════════════════════════════════════════════════════════╗ ║ Status: ERROR - Check system logs for details ║ ╚══════════════════════════════════════════════════════════════════════════════╝ """ def generate_fallback_html(error_message: str = "") -> str: """Генерировать fallback HTML если шаблоны не работают.""" return f'''