Navbar erweitert - Zwischenstand
Dieser Commit ist enthalten in:
259
v2_adminpanel/routes/monitoring_routes.py
Normale Datei
259
v2_adminpanel/routes/monitoring_routes.py
Normale Datei
@@ -0,0 +1,259 @@
|
||||
from flask import Blueprint, render_template, jsonify, request, session
|
||||
from functools import wraps
|
||||
import psycopg2
|
||||
from psycopg2.extras import RealDictCursor
|
||||
import os
|
||||
import requests
|
||||
from datetime import datetime, timedelta
|
||||
import logging
|
||||
|
||||
monitoring_bp = Blueprint('monitoring', __name__)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Database connection
|
||||
def get_db_connection():
|
||||
return psycopg2.connect(
|
||||
host=os.environ.get('POSTGRES_HOST', 'postgres'),
|
||||
database=os.environ.get('POSTGRES_DB', 'v2_adminpanel'),
|
||||
user=os.environ.get('POSTGRES_USER', 'postgres'),
|
||||
password=os.environ.get('POSTGRES_PASSWORD', 'postgres')
|
||||
)
|
||||
|
||||
# Login required decorator
|
||||
def login_required(f):
|
||||
@wraps(f)
|
||||
def decorated_function(*args, **kwargs):
|
||||
if 'logged_in' not in session:
|
||||
return render_template('error.html',
|
||||
error_message='Nicht autorisiert',
|
||||
details='Sie müssen angemeldet sein, um diese Seite zu sehen.')
|
||||
return f(*args, **kwargs)
|
||||
return decorated_function
|
||||
|
||||
@monitoring_bp.route('/live-dashboard')
|
||||
@login_required
|
||||
def live_dashboard():
|
||||
"""Live Dashboard showing active customer sessions"""
|
||||
try:
|
||||
conn = get_db_connection()
|
||||
cur = conn.cursor(cursor_factory=RealDictCursor)
|
||||
|
||||
# Get active customer sessions (last 5 minutes)
|
||||
cur.execute("""
|
||||
SELECT
|
||||
l.id,
|
||||
l.license_key,
|
||||
c.company_name,
|
||||
c.contact_person,
|
||||
lh.hardware_id,
|
||||
lh.ip_address,
|
||||
lh.timestamp as last_activity,
|
||||
lh.session_data,
|
||||
COUNT(DISTINCT lh.hardware_id) OVER (PARTITION BY l.id) as active_devices
|
||||
FROM license_heartbeats lh
|
||||
JOIN licenses l ON l.id = lh.license_id
|
||||
JOIN customers c ON c.id = l.customer_id
|
||||
WHERE lh.timestamp > NOW() - INTERVAL '5 minutes'
|
||||
AND l.is_active = true
|
||||
ORDER BY lh.timestamp DESC
|
||||
LIMIT 100
|
||||
""")
|
||||
active_sessions = cur.fetchall()
|
||||
|
||||
# Get session statistics
|
||||
cur.execute("""
|
||||
SELECT
|
||||
COUNT(DISTINCT license_id) as active_licenses,
|
||||
COUNT(DISTINCT hardware_id) as active_devices,
|
||||
COUNT(*) as total_heartbeats
|
||||
FROM license_heartbeats
|
||||
WHERE timestamp > NOW() - INTERVAL '5 minutes'
|
||||
""")
|
||||
stats = cur.fetchone()
|
||||
|
||||
# Get validations per minute
|
||||
cur.execute("""
|
||||
SELECT
|
||||
DATE_TRUNC('minute', timestamp) as minute,
|
||||
COUNT(*) as validations
|
||||
FROM license_heartbeats
|
||||
WHERE timestamp > NOW() - INTERVAL '60 minutes'
|
||||
GROUP BY minute
|
||||
ORDER BY minute DESC
|
||||
LIMIT 60
|
||||
""")
|
||||
validation_timeline = cur.fetchall()
|
||||
|
||||
cur.close()
|
||||
conn.close()
|
||||
|
||||
return render_template('monitoring/live_dashboard.html',
|
||||
active_sessions=active_sessions,
|
||||
stats=stats,
|
||||
validation_timeline=validation_timeline)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error in live dashboard: {str(e)}")
|
||||
return render_template('error.html',
|
||||
error_message='Fehler beim Laden des Dashboards',
|
||||
details=str(e))
|
||||
|
||||
@monitoring_bp.route('/system-status')
|
||||
@login_required
|
||||
def system_status():
|
||||
"""System status showing service health"""
|
||||
services = []
|
||||
|
||||
# Check each service
|
||||
service_checks = [
|
||||
{'name': 'License Server', 'url': 'http://license-server:8443/health', 'port': 8443},
|
||||
{'name': 'Auth Service', 'url': 'http://auth-service:5001/health', 'port': 5001},
|
||||
{'name': 'Analytics Service', 'url': 'http://analytics-service:5003/health', 'port': 5003},
|
||||
{'name': 'Admin API Service', 'url': 'http://admin-api-service:5004/health', 'port': 5004},
|
||||
{'name': 'PostgreSQL', 'check': 'database'},
|
||||
{'name': 'Redis', 'url': 'http://redis:6379', 'check': 'redis'},
|
||||
]
|
||||
|
||||
for service in service_checks:
|
||||
status = {'name': service['name'], 'status': 'unknown', 'response_time': None}
|
||||
|
||||
try:
|
||||
if service.get('check') == 'database':
|
||||
# Check database
|
||||
start = datetime.now()
|
||||
conn = get_db_connection()
|
||||
conn.close()
|
||||
status['status'] = 'healthy'
|
||||
status['response_time'] = (datetime.now() - start).total_seconds() * 1000
|
||||
elif service.get('url'):
|
||||
# Check HTTP service
|
||||
start = datetime.now()
|
||||
response = requests.get(service['url'], timeout=2)
|
||||
if response.status_code == 200:
|
||||
status['status'] = 'healthy'
|
||||
else:
|
||||
status['status'] = 'unhealthy'
|
||||
status['response_time'] = (datetime.now() - start).total_seconds() * 1000
|
||||
except:
|
||||
status['status'] = 'down'
|
||||
|
||||
services.append(status)
|
||||
|
||||
# Get Prometheus metrics if available
|
||||
prometheus_data = None
|
||||
try:
|
||||
response = requests.get('http://prometheus:9090/api/v1/query',
|
||||
params={'query': 'up'}, timeout=2)
|
||||
if response.status_code == 200:
|
||||
prometheus_data = response.json()
|
||||
except:
|
||||
pass
|
||||
|
||||
return render_template('monitoring/system_status.html',
|
||||
services=services,
|
||||
prometheus_data=prometheus_data)
|
||||
|
||||
@monitoring_bp.route('/alerts')
|
||||
@login_required
|
||||
def alerts():
|
||||
"""Show active alerts from Alertmanager"""
|
||||
alerts = []
|
||||
|
||||
try:
|
||||
# Get alerts from Alertmanager
|
||||
response = requests.get('http://alertmanager:9093/api/v1/alerts', timeout=2)
|
||||
if response.status_code == 200:
|
||||
alerts = response.json()
|
||||
except:
|
||||
# Fallback to database anomalies
|
||||
conn = get_db_connection()
|
||||
cur = conn.cursor(cursor_factory=RealDictCursor)
|
||||
|
||||
cur.execute("""
|
||||
SELECT
|
||||
ad.*,
|
||||
l.license_key,
|
||||
c.company_name
|
||||
FROM anomaly_detections ad
|
||||
LEFT JOIN licenses l ON l.id = ad.license_id
|
||||
LEFT JOIN customers c ON c.id = l.customer_id
|
||||
WHERE ad.resolved = false
|
||||
ORDER BY ad.detected_at DESC
|
||||
LIMIT 50
|
||||
""")
|
||||
alerts = cur.fetchall()
|
||||
|
||||
cur.close()
|
||||
conn.close()
|
||||
|
||||
return render_template('monitoring/alerts.html', alerts=alerts)
|
||||
|
||||
@monitoring_bp.route('/analytics')
|
||||
@login_required
|
||||
def analytics():
|
||||
"""Detailed analytics page"""
|
||||
# This will integrate with the existing analytics service
|
||||
return render_template('monitoring/analytics.html')
|
||||
|
||||
# API endpoints for live data
|
||||
@monitoring_bp.route('/api/live-stats')
|
||||
@login_required
|
||||
def api_live_stats():
|
||||
"""API endpoint for live statistics"""
|
||||
try:
|
||||
conn = get_db_connection()
|
||||
cur = conn.cursor(cursor_factory=RealDictCursor)
|
||||
|
||||
# Get current stats
|
||||
cur.execute("""
|
||||
SELECT
|
||||
COUNT(DISTINCT license_id) as active_licenses,
|
||||
COUNT(DISTINCT hardware_id) as active_devices,
|
||||
COUNT(*) as validations_last_minute
|
||||
FROM license_heartbeats
|
||||
WHERE timestamp > NOW() - INTERVAL '1 minute'
|
||||
""")
|
||||
stats = cur.fetchone()
|
||||
|
||||
cur.close()
|
||||
conn.close()
|
||||
|
||||
return jsonify(stats)
|
||||
|
||||
except Exception as e:
|
||||
return jsonify({'error': str(e)}), 500
|
||||
|
||||
@monitoring_bp.route('/api/active-sessions')
|
||||
@login_required
|
||||
def api_active_sessions():
|
||||
"""API endpoint for active customer sessions"""
|
||||
try:
|
||||
conn = get_db_connection()
|
||||
cur = conn.cursor(cursor_factory=RealDictCursor)
|
||||
|
||||
# Get active sessions with geo data
|
||||
cur.execute("""
|
||||
SELECT
|
||||
l.license_key,
|
||||
c.company_name,
|
||||
lh.hardware_id,
|
||||
lh.ip_address,
|
||||
lh.timestamp as last_activity,
|
||||
EXTRACT(EPOCH FROM (NOW() - lh.timestamp)) as seconds_ago,
|
||||
lh.session_data
|
||||
FROM license_heartbeats lh
|
||||
JOIN licenses l ON l.id = lh.license_id
|
||||
JOIN customers c ON c.id = l.customer_id
|
||||
WHERE lh.timestamp > NOW() - INTERVAL '5 minutes'
|
||||
ORDER BY lh.timestamp DESC
|
||||
LIMIT 50
|
||||
""")
|
||||
sessions = cur.fetchall()
|
||||
|
||||
cur.close()
|
||||
conn.close()
|
||||
|
||||
return jsonify(sessions)
|
||||
|
||||
except Exception as e:
|
||||
return jsonify({'error': str(e)}), 500
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren