uploader-bot/app/api/routes/monitor_routes.py

844 lines
27 KiB
Python

"""
Advanced monitoring routes for MY Network
"""
import asyncio
import psutil
import time
from datetime import datetime
from typing import Dict, List, Any
from fastapi import APIRouter, WebSocket, WebSocketDisconnect, Request
from fastapi.responses import HTMLResponse
import json
import logging
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/my/monitor", tags=["monitoring"])
# Store connected websocket clients
connected_clients: List[WebSocket] = []
# Simulated network nodes data
network_nodes = [
{
"id": "node_001_local_dev",
"name": "Primary Development Node",
"status": "online",
"location": "Local Development",
"uptime": "2h 15m",
"connections": 8,
"data_synced": "95%",
"last_seen": datetime.now().isoformat(),
"ip": "127.0.0.1:15100",
"version": "2.0.0"
},
{
"id": "node_002_production",
"name": "Production Node Alpha",
"status": "online",
"location": "Cloud Server US-East",
"uptime": "15d 8h",
"connections": 42,
"data_synced": "100%",
"last_seen": datetime.now().isoformat(),
"ip": "198.51.100.10:15100",
"version": "2.0.0"
},
{
"id": "node_003_backup",
"name": "Backup Node Beta",
"status": "maintenance",
"location": "Cloud Server EU-West",
"uptime": "3d 2h",
"connections": 0,
"data_synced": "78%",
"last_seen": datetime.now().isoformat(),
"ip": "203.0.113.20:15100",
"version": "1.9.8"
},
{
"id": "node_004_edge",
"name": "Edge Node Gamma",
"status": "connecting",
"location": "CDN Edge Node",
"uptime": "12m",
"connections": 3,
"data_synced": "12%",
"last_seen": datetime.now().isoformat(),
"ip": "192.0.2.30:15100",
"version": "2.0.0"
}
]
@router.get("/")
async def advanced_monitoring_dashboard():
"""Serve the advanced monitoring dashboard"""
dashboard_html = """
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MY Network - Advanced Monitor</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #000;
color: #00ff00;
font-family: 'Courier New', monospace;
overflow-x: hidden;
min-height: 100vh;
}
.matrix-bg {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
opacity: 0.1;
}
.container {
padding: 20px;
max-width: 1400px;
margin: 0 auto;
position: relative;
z-index: 1;
}
.header {
text-align: center;
margin-bottom: 30px;
border: 2px solid #00ff00;
padding: 20px;
background: rgba(0, 0, 0, 0.8);
position: relative;
}
.header h1 {
font-size: 2.5rem;
text-shadow: 0 0 10px #00ff00;
animation: glow 2s ease-in-out infinite alternate;
}
.header .subtitle {
font-size: 1.2rem;
margin-top: 10px;
opacity: 0.8;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.stat-card {
border: 1px solid #00ff00;
padding: 20px;
background: rgba(0, 50, 0, 0.3);
position: relative;
overflow: hidden;
}
.stat-card::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 2px;
background: linear-gradient(90deg, transparent, #00ff00, transparent);
animation: scan 3s linear infinite;
}
.stat-title {
font-size: 1.1rem;
margin-bottom: 10px;
text-transform: uppercase;
}
.stat-value {
font-size: 2rem;
font-weight: bold;
text-shadow: 0 0 5px #00ff00;
}
.nodes-section {
margin-bottom: 30px;
}
.section-title {
font-size: 1.5rem;
margin-bottom: 20px;
border-bottom: 2px solid #00ff00;
padding-bottom: 10px;
text-transform: uppercase;
}
.nodes-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
gap: 20px;
}
.node-card {
border: 1px solid #00ff00;
padding: 20px;
background: rgba(0, 50, 0, 0.2);
position: relative;
transition: all 0.3s ease;
}
.node-card:hover {
background: rgba(0, 100, 0, 0.3);
box-shadow: 0 0 20px rgba(0, 255, 0, 0.3);
}
.node-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
}
.node-name {
font-size: 1.2rem;
font-weight: bold;
}
.node-status {
padding: 5px 10px;
border-radius: 3px;
font-size: 0.9rem;
text-transform: uppercase;
}
.status-online {
background: rgba(0, 255, 0, 0.3);
border: 1px solid #00ff00;
animation: pulse 2s infinite;
}
.status-maintenance {
background: rgba(255, 165, 0, 0.3);
border: 1px solid #ffa500;
color: #ffa500;
}
.status-connecting {
background: rgba(255, 255, 0, 0.3);
border: 1px solid #ffff00;
color: #ffff00;
animation: blink 1s infinite;
}
.node-details {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
font-size: 0.9rem;
}
.detail-item {
display: flex;
justify-content: space-between;
}
.detail-label {
opacity: 0.8;
}
.detail-value {
font-weight: bold;
}
.system-info {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
margin-bottom: 30px;
}
.info-card {
border: 1px solid #00ff00;
padding: 15px;
background: rgba(0, 30, 0, 0.4);
}
.info-title {
font-size: 1rem;
margin-bottom: 10px;
color: #00ff00;
text-transform: uppercase;
}
.info-content {
font-size: 0.9rem;
line-height: 1.4;
}
.terminal {
background: rgba(0, 0, 0, 0.9);
border: 2px solid #00ff00;
padding: 20px;
font-family: 'Courier New', monospace;
max-height: 300px;
overflow-y: auto;
}
.terminal-header {
margin-bottom: 15px;
color: #00ff00;
font-weight: bold;
}
.log-entry {
margin-bottom: 5px;
opacity: 0.8;
}
.log-timestamp {
color: #666;
}
.log-level-error {
color: #ff0000;
}
.log-level-warning {
color: #ffa500;
}
.log-level-info {
color: #00ff00;
}
@keyframes glow {
from { text-shadow: 0 0 10px #00ff00; }
to { text-shadow: 0 0 20px #00ff00, 0 0 30px #00ff00; }
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
@keyframes blink {
0%, 50% { opacity: 1; }
51%, 100% { opacity: 0.3; }
}
@keyframes scan {
0% { left: -100%; }
100% { left: 100%; }
}
.connection-indicator {
position: absolute;
top: 10px;
right: 10px;
width: 12px;
height: 12px;
border-radius: 50%;
background: #ff0000;
animation: pulse 1s infinite;
}
.connection-indicator.connected {
background: #00ff00;
}
.data-flow {
position: relative;
height: 20px;
background: rgba(0, 0, 0, 0.5);
border: 1px solid #00ff00;
margin: 10px 0;
overflow: hidden;
}
.data-flow::after {
content: '';
position: absolute;
top: 0;
left: 0;
height: 100%;
width: 0%;
background: linear-gradient(90deg, transparent, #00ff00, transparent);
animation: dataFlow 2s linear infinite;
}
@keyframes dataFlow {
0% { width: 0%; left: 0%; }
50% { width: 30%; }
100% { width: 0%; left: 100%; }
}
.matrix-text {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
overflow: hidden;
}
.matrix-char {
position: absolute;
color: #00ff00;
font-family: 'Courier New', monospace;
font-size: 14px;
opacity: 0.3;
animation: matrixFall 10s linear infinite;
}
@keyframes matrixFall {
0% { transform: translateY(-100vh); opacity: 0; }
10% { opacity: 0.3; }
90% { opacity: 0.3; }
100% { transform: translateY(100vh); opacity: 0; }
}
</style>
</head>
<body>
<div class="matrix-bg">
<div class="matrix-text" id="matrixText"></div>
</div>
<div class="container">
<div class="header">
<div class="connection-indicator" id="connectionIndicator"></div>
<h1>MY NETWORK ADVANCED MONITOR</h1>
<div class="subtitle">Real-time Network Status & Diagnostics</div>
</div>
<div class="stats-grid">
<div class="stat-card">
<div class="stat-title">Connected Nodes</div>
<div class="stat-value" id="connectedNodes">--</div>
<div class="data-flow"></div>
</div>
<div class="stat-card">
<div class="stat-title">System Uptime</div>
<div class="stat-value" id="systemUptime">--</div>
<div class="data-flow"></div>
</div>
<div class="stat-card">
<div class="stat-title">Data Synced</div>
<div class="stat-value" id="dataSynced">--</div>
<div class="data-flow"></div>
</div>
<div class="stat-card">
<div class="stat-title">Network Health</div>
<div class="stat-value" id="networkHealth">--</div>
<div class="data-flow"></div>
</div>
</div>
<div class="system-info">
<div class="info-card">
<div class="info-title">Current Node Info</div>
<div class="info-content" id="currentNodeInfo">Loading...</div>
</div>
<div class="info-card">
<div class="info-title">System Resources</div>
<div class="info-content" id="systemResources">Loading...</div>
</div>
<div class="info-card">
<div class="info-title">Network Status</div>
<div class="info-content" id="networkStatus">Loading...</div>
</div>
<div class="info-card">
<div class="info-title">Configuration Issues</div>
<div class="info-content" id="configIssues">Loading...</div>
</div>
</div>
<div class="nodes-section">
<div class="section-title">Connected Network Nodes</div>
<div class="nodes-grid" id="nodesGrid">
<!-- Nodes will be populated here -->
</div>
</div>
<div class="terminal">
<div class="terminal-header">SYSTEM LOG STREAM</div>
<div id="logStream">
<div class="log-entry">
<span class="log-timestamp">[2025-07-09 14:04:00]</span>
<span class="log-level-info">[INFO]</span>
MY Network Monitor initialized successfully
</div>
<div class="log-entry">
<span class="log-timestamp">[2025-07-09 14:04:01]</span>
<span class="log-level-info">[INFO]</span>
WebSocket connection established
</div>
</div>
</div>
</div>
<script>
let ws = null;
let reconnectAttempts = 0;
const maxReconnectAttempts = 5;
// Matrix rain effect
function createMatrixRain() {
const matrixText = document.getElementById('matrixText');
const chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
setInterval(() => {
const char = document.createElement('div');
char.className = 'matrix-char';
char.textContent = chars[Math.floor(Math.random() * chars.length)];
char.style.left = Math.random() * 100 + '%';
char.style.animationDuration = (Math.random() * 10 + 5) + 's';
char.style.fontSize = (Math.random() * 8 + 10) + 'px';
matrixText.appendChild(char);
setTimeout(() => {
if (char.parentNode) {
char.parentNode.removeChild(char);
}
}, 15000);
}, 200);
}
function connectWebSocket() {
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
const wsUrl = `${protocol}//${window.location.host}/api/my/monitor/ws`;
ws = new WebSocket(wsUrl);
ws.onopen = function() {
console.log('WebSocket connected');
document.getElementById('connectionIndicator').classList.add('connected');
reconnectAttempts = 0;
addLogEntry('WebSocket connection established', 'info');
};
ws.onmessage = function(event) {
const data = JSON.parse(event.data);
updateDashboard(data);
};
ws.onclose = function() {
console.log('WebSocket disconnected');
document.getElementById('connectionIndicator').classList.remove('connected');
addLogEntry('WebSocket connection lost', 'warning');
if (reconnectAttempts < maxReconnectAttempts) {
setTimeout(() => {
reconnectAttempts++;
addLogEntry(`Reconnection attempt ${reconnectAttempts}`, 'info');
connectWebSocket();
}, 3000);
}
};
ws.onerror = function(error) {
console.error('WebSocket error:', error);
addLogEntry('WebSocket error occurred', 'error');
};
}
function updateDashboard(data) {
// Update stats
document.getElementById('connectedNodes').textContent = data.stats.connected_nodes;
document.getElementById('systemUptime').textContent = data.stats.uptime;
document.getElementById('dataSynced').textContent = data.stats.data_synced;
document.getElementById('networkHealth').textContent = data.stats.health;
// Update current node info
const nodeInfo = data.current_node;
document.getElementById('currentNodeInfo').innerHTML = `
<strong>Node ID:</strong> ${nodeInfo.id}<br>
<strong>Name:</strong> ${nodeInfo.name}<br>
<strong>Version:</strong> ${nodeInfo.version}<br>
<strong>Status:</strong> ${nodeInfo.status}
`;
// Update system resources
const resources = data.system_resources;
document.getElementById('systemResources').innerHTML = `
<strong>CPU Usage:</strong> ${resources.cpu_usage}%<br>
<strong>Memory:</strong> ${resources.memory_usage}%<br>
<strong>Disk:</strong> ${resources.disk_usage}%<br>
<strong>Network I/O:</strong> ${resources.network_io}
`;
// Update network status
document.getElementById('networkStatus').innerHTML = `
<strong>Protocol:</strong> MY Network v2.0<br>
<strong>Port:</strong> 15100<br>
<strong>Mode:</strong> ${data.network_status.mode}<br>
<strong>Peer Count:</strong> ${data.network_status.peers}
`;
// Update configuration issues
const issues = data.config_issues;
let issuesHtml = '';
if (issues.length > 0) {
issuesHtml = issues.map(issue => `• ${issue}`).join('<br>');
} else {
issuesHtml = '<span style="color: #00ff00;">No configuration issues</span>';
}
document.getElementById('configIssues').innerHTML = issuesHtml;
// Update nodes grid
updateNodesGrid(data.nodes);
}
function updateNodesGrid(nodes) {
const grid = document.getElementById('nodesGrid');
grid.innerHTML = '';
nodes.forEach(node => {
const nodeCard = document.createElement('div');
nodeCard.className = 'node-card';
const statusClass = `status-${node.status}`;
nodeCard.innerHTML = `
<div class="node-header">
<div class="node-name">${node.name}</div>
<div class="node-status ${statusClass}">${node.status}</div>
</div>
<div class="node-details">
<div class="detail-item">
<span class="detail-label">Location:</span>
<span class="detail-value">${node.location}</span>
</div>
<div class="detail-item">
<span class="detail-label">Uptime:</span>
<span class="detail-value">${node.uptime}</span>
</div>
<div class="detail-item">
<span class="detail-label">Connections:</span>
<span class="detail-value">${node.connections}</span>
</div>
<div class="detail-item">
<span class="detail-label">Data Synced:</span>
<span class="detail-value">${node.data_synced}</span>
</div>
<div class="detail-item">
<span class="detail-label">IP Address:</span>
<span class="detail-value">${node.ip}</span>
</div>
<div class="detail-item">
<span class="detail-label">Version:</span>
<span class="detail-value">${node.version}</span>
</div>
</div>
`;
grid.appendChild(nodeCard);
});
}
function addLogEntry(message, level = 'info') {
const logStream = document.getElementById('logStream');
const timestamp = new Date().toISOString().slice(0, 19).replace('T', ' ');
const entry = document.createElement('div');
entry.className = 'log-entry';
entry.innerHTML = `
<span class="log-timestamp">[${timestamp}]</span>
<span class="log-level-${level}">[${level.toUpperCase()}]</span>
${message}
`;
logStream.appendChild(entry);
logStream.scrollTop = logStream.scrollHeight;
// Keep only last 50 entries
while (logStream.children.length > 50) {
logStream.removeChild(logStream.firstChild);
}
}
// Fallback data loading if WebSocket fails
function loadFallbackData() {
fetch('/api/my/monitor/status')
.then(response => response.json())
.then(data => updateDashboard(data))
.catch(error => {
console.error('Failed to load fallback data:', error);
addLogEntry('Failed to load monitoring data', 'error');
});
}
// Initialize
createMatrixRain();
connectWebSocket();
// Load fallback data every 5 seconds if WebSocket is not connected
setInterval(() => {
if (!ws || ws.readyState !== WebSocket.OPEN) {
loadFallbackData();
}
}, 5000);
// Add some random log entries for demo
setInterval(() => {
const messages = [
'Network heartbeat received',
'Data synchronization completed',
'Peer discovery scan finished',
'Security check passed',
'Cache optimization complete'
];
const message = messages[Math.floor(Math.random() * messages.length)];
addLogEntry(message, 'info');
}, 8000);
</script>
</body>
</html>
"""
return HTMLResponse(content=dashboard_html)
@router.get("/status")
async def get_monitoring_status():
"""Get current monitoring status data"""
import subprocess
import shutil
# Get system info
try:
cpu_percent = psutil.cpu_percent(interval=1)
memory = psutil.virtual_memory()
disk = psutil.disk_usage('/')
system_resources = {
"cpu_usage": round(cpu_percent, 1),
"memory_usage": round(memory.percent, 1),
"disk_usage": round(disk.percent, 1),
"network_io": "Active"
}
except Exception as e:
logger.error(f"Failed to get system resources: {e}")
system_resources = {
"cpu_usage": 0,
"memory_usage": 0,
"disk_usage": 0,
"network_io": "Unknown"
}
# Configuration issues from logs/environment
config_issues = [
"Pydantic validation errors in configuration",
"Extra environment variables not permitted",
"Telegram API token format validation failed",
"MY Network running in limited mode"
]
return {
"timestamp": datetime.now().isoformat(),
"stats": {
"connected_nodes": len([n for n in network_nodes if n["status"] == "online"]),
"uptime": "2h 18m",
"data_synced": "87%",
"health": "Limited"
},
"current_node": {
"id": "node_001_local_dev",
"name": "Primary Development Node",
"version": "2.0.0",
"status": "limited_mode"
},
"system_resources": system_resources,
"network_status": {
"mode": "Development",
"peers": 3,
"protocol": "MY Network v2.0"
},
"config_issues": config_issues,
"nodes": network_nodes
}
@router.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
"""WebSocket endpoint for real-time monitoring updates"""
await websocket.accept()
connected_clients.append(websocket)
try:
while True:
# Send periodic updates
status_data = await get_monitoring_status()
await websocket.send_text(json.dumps(status_data))
await asyncio.sleep(2) # Update every 2 seconds
except WebSocketDisconnect:
connected_clients.remove(websocket)
logger.info("Client disconnected from monitoring WebSocket")
except Exception as e:
logger.error(f"WebSocket error: {e}")
if websocket in connected_clients:
connected_clients.remove(websocket)
@router.get("/nodes")
async def get_network_nodes():
"""Get list of all network nodes"""
return {"nodes": network_nodes}
@router.get("/node/{node_id}")
async def get_node_details(node_id: str):
"""Get detailed information about a specific node"""
node = next((n for n in network_nodes if n["id"] == node_id), None)
if not node:
return {"error": "Node not found"}, 404
# Add more detailed info
detailed_node = {
**node,
"detailed_stats": {
"cpu_usage": "23%",
"memory_usage": "67%",
"disk_usage": "45%",
"network_in": "150 KB/s",
"network_out": "89 KB/s",
"active_connections": 12,
"data_transferred": "1.2 GB",
"sync_progress": "87%"
},
"services": {
"http_server": "running",
"p2p_network": "limited",
"database": "connected",
"redis_cache": "connected",
"blockchain_sync": "paused"
}
}
return {"node": detailed_node}
@router.post("/simulate_event")
async def simulate_network_event(event_data: Dict[str, Any]):
"""Simulate network events for testing"""
# Broadcast event to all connected WebSocket clients
event_message = {
"type": "network_event",
"timestamp": datetime.now().isoformat(),
"event": event_data
}
for client in connected_clients[:]:
try:
await client.send_text(json.dumps(event_message))
except Exception as e:
logger.error(f"Failed to send event to client: {e}")
connected_clients.remove(client)
return {"status": "Event simulated", "clients_notified": len(connected_clients)}