Vorläufig fertiger server
Dieser Commit ist enthalten in:
@@ -561,91 +561,8 @@ def clear_attempts():
|
||||
@admin_bp.route("/lizenzserver/monitor")
|
||||
@login_required
|
||||
def license_monitor():
|
||||
"""License server live monitoring dashboard"""
|
||||
try:
|
||||
conn = get_connection()
|
||||
cur = conn.cursor()
|
||||
|
||||
# Get current statistics
|
||||
# Active validations in last 5 minutes
|
||||
cur.execute("""
|
||||
SELECT COUNT(DISTINCT license_id) as active_licenses,
|
||||
COUNT(*) as total_validations,
|
||||
COUNT(DISTINCT hardware_id) as unique_devices,
|
||||
COUNT(DISTINCT ip_address) as unique_ips
|
||||
FROM license_heartbeats
|
||||
WHERE timestamp > NOW() - INTERVAL '5 minutes'
|
||||
""")
|
||||
live_stats = cur.fetchone()
|
||||
|
||||
# Get validation rate (per minute)
|
||||
cur.execute("""
|
||||
SELECT DATE_TRUNC('minute', timestamp) as minute,
|
||||
COUNT(*) as validations
|
||||
FROM license_heartbeats
|
||||
WHERE timestamp > NOW() - INTERVAL '10 minutes'
|
||||
GROUP BY minute
|
||||
ORDER BY minute DESC
|
||||
LIMIT 10
|
||||
""")
|
||||
validation_rates = cur.fetchall()
|
||||
|
||||
# Get top active licenses
|
||||
cur.execute("""
|
||||
SELECT l.id, l.license_key, c.name as customer_name,
|
||||
COUNT(DISTINCT lh.hardware_id) as device_count,
|
||||
COUNT(*) as validation_count,
|
||||
MAX(lh.timestamp) as last_seen
|
||||
FROM licenses l
|
||||
JOIN customers c ON l.customer_id = c.id
|
||||
JOIN license_heartbeats lh ON l.id = lh.license_id
|
||||
WHERE lh.timestamp > NOW() - INTERVAL '15 minutes'
|
||||
GROUP BY l.id, l.license_key, c.name
|
||||
ORDER BY validation_count DESC
|
||||
LIMIT 10
|
||||
""")
|
||||
top_licenses = cur.fetchall()
|
||||
|
||||
# Get recent anomalies
|
||||
cur.execute("""
|
||||
SELECT ad.*, l.license_key, c.name as customer_name
|
||||
FROM anomaly_detections ad
|
||||
LEFT JOIN licenses l ON ad.license_id = l.id
|
||||
LEFT JOIN customers c ON l.customer_id = c.id
|
||||
WHERE ad.resolved = false
|
||||
ORDER BY ad.detected_at DESC
|
||||
LIMIT 10
|
||||
""")
|
||||
recent_anomalies = cur.fetchall()
|
||||
|
||||
# Get geographic distribution
|
||||
cur.execute("""
|
||||
SELECT ip_address, COUNT(*) as count
|
||||
FROM license_heartbeats
|
||||
WHERE timestamp > NOW() - INTERVAL '1 hour'
|
||||
AND ip_address IS NOT NULL
|
||||
GROUP BY ip_address
|
||||
ORDER BY count DESC
|
||||
LIMIT 20
|
||||
""")
|
||||
geo_distribution = cur.fetchall()
|
||||
|
||||
return render_template('license_monitor.html',
|
||||
live_stats=live_stats,
|
||||
validation_rates=validation_rates,
|
||||
top_licenses=top_licenses,
|
||||
recent_anomalies=recent_anomalies,
|
||||
geo_distribution=geo_distribution
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
flash(f'Fehler beim Laden der Monitoring-Daten: {str(e)}', 'error')
|
||||
return render_template('license_monitor.html')
|
||||
finally:
|
||||
if 'cur' in locals():
|
||||
cur.close()
|
||||
if 'conn' in locals():
|
||||
conn.close()
|
||||
"""Redirect to new analytics page"""
|
||||
return redirect(url_for('monitoring.analytics'))
|
||||
|
||||
|
||||
@admin_bp.route("/lizenzserver/analytics")
|
||||
|
||||
@@ -6,6 +6,7 @@ import os
|
||||
import requests
|
||||
from datetime import datetime, timedelta
|
||||
import logging
|
||||
from utils.partition_helper import ensure_partition_exists, check_table_exists
|
||||
|
||||
monitoring_bp = Blueprint('monitoring', __name__)
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -33,18 +34,33 @@ def login_required(f):
|
||||
@monitoring_bp.route('/live-dashboard')
|
||||
@login_required
|
||||
def live_dashboard():
|
||||
"""Live Dashboard showing active customer sessions"""
|
||||
"""Live Dashboard showing active customer sessions and analytics"""
|
||||
try:
|
||||
conn = get_db_connection()
|
||||
cur = conn.cursor(cursor_factory=RealDictCursor)
|
||||
|
||||
# Check if license_heartbeats table exists
|
||||
if not check_table_exists(conn, 'license_heartbeats'):
|
||||
logger.warning("license_heartbeats table does not exist")
|
||||
# Return empty data
|
||||
return render_template('monitoring/live_dashboard.html',
|
||||
active_sessions=[],
|
||||
stats={'active_licenses': 0, 'active_devices': 0, 'total_heartbeats': 0},
|
||||
validation_timeline=[],
|
||||
live_stats=[0, 0, 0, 0],
|
||||
validation_rates=[],
|
||||
recent_anomalies=[],
|
||||
top_licenses=[])
|
||||
|
||||
# Ensure current month partition exists
|
||||
ensure_partition_exists(conn, 'license_heartbeats', datetime.now())
|
||||
|
||||
# Get active customer sessions (last 5 minutes)
|
||||
cur.execute("""
|
||||
SELECT
|
||||
l.id,
|
||||
l.license_key,
|
||||
c.company_name,
|
||||
c.contact_person,
|
||||
c.name as company_name,
|
||||
lh.hardware_id,
|
||||
lh.ip_address,
|
||||
lh.timestamp as last_activity,
|
||||
@@ -71,7 +87,7 @@ def live_dashboard():
|
||||
""")
|
||||
stats = cur.fetchone()
|
||||
|
||||
# Get validations per minute
|
||||
# Get validations per minute (for both charts)
|
||||
cur.execute("""
|
||||
SELECT
|
||||
DATE_TRUNC('minute', timestamp) as minute,
|
||||
@@ -84,13 +100,81 @@ def live_dashboard():
|
||||
""")
|
||||
validation_timeline = cur.fetchall()
|
||||
|
||||
# Get live statistics for analytics cards
|
||||
cur.execute("""
|
||||
SELECT
|
||||
COUNT(DISTINCT license_id) as active_licenses,
|
||||
COUNT(*) as total_validations,
|
||||
COUNT(DISTINCT hardware_id) as unique_devices,
|
||||
COUNT(DISTINCT ip_address) as unique_ips
|
||||
FROM license_heartbeats
|
||||
WHERE timestamp > NOW() - INTERVAL '5 minutes'
|
||||
""")
|
||||
live_stats_data = cur.fetchone()
|
||||
live_stats = [
|
||||
live_stats_data['active_licenses'] or 0,
|
||||
live_stats_data['total_validations'] or 0,
|
||||
live_stats_data['unique_devices'] or 0,
|
||||
live_stats_data['unique_ips'] or 0
|
||||
]
|
||||
|
||||
# Get validation rates for analytics chart (last 30 minutes)
|
||||
cur.execute("""
|
||||
SELECT
|
||||
DATE_TRUNC('minute', timestamp) as minute,
|
||||
COUNT(*) as count
|
||||
FROM license_heartbeats
|
||||
WHERE timestamp > NOW() - INTERVAL '30 minutes'
|
||||
GROUP BY minute
|
||||
ORDER BY minute DESC
|
||||
LIMIT 30
|
||||
""")
|
||||
validation_rates = [(row['minute'].isoformat(), row['count']) for row in cur.fetchall()]
|
||||
|
||||
# Get recent anomalies
|
||||
cur.execute("""
|
||||
SELECT
|
||||
ad.*,
|
||||
l.license_key,
|
||||
c.name as customer_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.detected_at > NOW() - INTERVAL '24 hours'
|
||||
ORDER BY ad.detected_at DESC
|
||||
LIMIT 10
|
||||
""")
|
||||
recent_anomalies = cur.fetchall()
|
||||
|
||||
# Get top active licenses
|
||||
cur.execute("""
|
||||
SELECT
|
||||
l.license_key,
|
||||
c.name as customer_name,
|
||||
COUNT(DISTINCT lh.hardware_id) as device_count,
|
||||
COUNT(*) as validation_count,
|
||||
MAX(lh.timestamp) as last_seen
|
||||
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 '15 minutes'
|
||||
GROUP BY l.license_key, c.name
|
||||
ORDER BY validation_count DESC
|
||||
LIMIT 10
|
||||
""")
|
||||
top_licenses = cur.fetchall()
|
||||
|
||||
cur.close()
|
||||
conn.close()
|
||||
|
||||
return render_template('monitoring/live_dashboard.html',
|
||||
active_sessions=active_sessions,
|
||||
stats=stats,
|
||||
validation_timeline=validation_timeline)
|
||||
validation_timeline=validation_timeline,
|
||||
live_stats=live_stats,
|
||||
validation_rates=validation_rates,
|
||||
recent_anomalies=recent_anomalies,
|
||||
top_licenses=top_licenses)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error in live dashboard: {str(e)}")
|
||||
@@ -173,7 +257,7 @@ def alerts():
|
||||
SELECT
|
||||
ad.*,
|
||||
l.license_key,
|
||||
c.company_name
|
||||
c.name as 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
|
||||
@@ -191,9 +275,91 @@ def 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')
|
||||
"""Combined analytics and license server status page"""
|
||||
try:
|
||||
conn = get_db_connection()
|
||||
cur = conn.cursor(cursor_factory=RealDictCursor)
|
||||
|
||||
# Get live statistics for the top cards
|
||||
cur.execute("""
|
||||
SELECT
|
||||
COUNT(DISTINCT license_id) as active_licenses,
|
||||
COUNT(*) as total_validations,
|
||||
COUNT(DISTINCT hardware_id) as unique_devices,
|
||||
COUNT(DISTINCT ip_address) as unique_ips
|
||||
FROM license_heartbeats
|
||||
WHERE timestamp > NOW() - INTERVAL '5 minutes'
|
||||
""")
|
||||
live_stats = cur.fetchone()
|
||||
live_stats = [
|
||||
live_stats['active_licenses'] or 0,
|
||||
live_stats['total_validations'] or 0,
|
||||
live_stats['unique_devices'] or 0,
|
||||
live_stats['unique_ips'] or 0
|
||||
]
|
||||
|
||||
# Get validation rates for chart
|
||||
cur.execute("""
|
||||
SELECT
|
||||
DATE_TRUNC('minute', timestamp) as minute,
|
||||
COUNT(*) as count
|
||||
FROM license_heartbeats
|
||||
WHERE timestamp > NOW() - INTERVAL '30 minutes'
|
||||
GROUP BY minute
|
||||
ORDER BY minute DESC
|
||||
LIMIT 30
|
||||
""")
|
||||
validation_rates = [(row['minute'].isoformat(), row['count']) for row in cur.fetchall()]
|
||||
|
||||
# Get recent anomalies
|
||||
cur.execute("""
|
||||
SELECT
|
||||
ad.*,
|
||||
l.license_key,
|
||||
c.name as customer_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.detected_at > NOW() - INTERVAL '24 hours'
|
||||
ORDER BY ad.detected_at DESC
|
||||
LIMIT 10
|
||||
""")
|
||||
recent_anomalies = cur.fetchall()
|
||||
|
||||
# Get top active licenses
|
||||
cur.execute("""
|
||||
SELECT
|
||||
l.license_key,
|
||||
c.name as customer_name,
|
||||
COUNT(DISTINCT lh.hardware_id) as device_count,
|
||||
COUNT(*) as validation_count,
|
||||
MAX(lh.timestamp) as last_seen
|
||||
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 '15 minutes'
|
||||
GROUP BY l.license_key, c.name
|
||||
ORDER BY validation_count DESC
|
||||
LIMIT 10
|
||||
""")
|
||||
top_licenses = cur.fetchall()
|
||||
|
||||
cur.close()
|
||||
conn.close()
|
||||
|
||||
return render_template('monitoring/analytics.html',
|
||||
live_stats=live_stats,
|
||||
validation_rates=validation_rates,
|
||||
recent_anomalies=recent_anomalies,
|
||||
top_licenses=top_licenses)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error in analytics: {str(e)}")
|
||||
return render_template('monitoring/analytics.html',
|
||||
live_stats=[0, 0, 0, 0],
|
||||
validation_rates=[],
|
||||
recent_anomalies=[],
|
||||
top_licenses=[])
|
||||
|
||||
# API endpoints for live data
|
||||
@monitoring_bp.route('/api/live-stats')
|
||||
@@ -235,7 +401,7 @@ def api_active_sessions():
|
||||
cur.execute("""
|
||||
SELECT
|
||||
l.license_key,
|
||||
c.company_name,
|
||||
c.name as company_name,
|
||||
lh.hardware_id,
|
||||
lh.ip_address,
|
||||
lh.timestamp as last_activity,
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren