Add latest changes

Dieser Commit ist enthalten in:
2025-07-03 20:38:33 +00:00
Ursprung 63f3d92724
Commit 6f6cde65db
129 geänderte Dateien mit 3998 neuen und 1199 gelöschten Zeilen

Datei anzeigen

@@ -4,6 +4,7 @@ from zoneinfo import ZoneInfo
from pathlib import Path
from flask import Blueprint, render_template, request, redirect, session, url_for, flash, send_file, jsonify, current_app
import requests
import traceback
import config
from config import DATABASE_CONFIG
@@ -12,6 +13,7 @@ from utils.audit import log_audit
from utils.backup import create_backup, restore_backup, create_backup_with_github, create_server_backup
from utils.network import get_client_ip
from db import get_connection, get_db_connection, get_db_cursor, execute_query
from db_license import get_license_db_cursor
from utils.export import create_excel_export, prepare_audit_export_data
# Create Blueprint
@@ -116,9 +118,17 @@ def dashboard():
cur.execute("SELECT COUNT(*) FROM licenses WHERE is_fake = true")
test_licenses_count = cur.fetchone()[0] if cur.rowcount > 0 else 0
# Anzahl aktiver Sessions (Admin-Panel)
cur.execute("SELECT COUNT(*) FROM sessions WHERE is_active = true")
active_sessions = cur.fetchone()[0] if cur.rowcount > 0 else 0
# Anzahl aktiver Sessions aus License Server DB
active_sessions = 0
try:
with get_license_db_cursor() as license_cur:
license_cur.execute("SELECT COUNT(*) FROM license_sessions WHERE ended_at IS NULL")
active_sessions = license_cur.fetchone()[0] if license_cur.rowcount > 0 else 0
except Exception as e:
current_app.logger.warning(f"Could not get active sessions from license server: {str(e)}")
# Fallback auf Admin DB
cur.execute("SELECT COUNT(*) FROM sessions WHERE is_active = true")
active_sessions = cur.fetchone()[0] if cur.rowcount > 0 else 0
# Aktive Nutzung (Kunden-Software) - Lizenzen mit Heartbeats in den letzten 15 Minuten
active_usage = 0
@@ -333,6 +343,52 @@ def dashboard():
except:
pass
# Session-Auslastung (Concurrent Sessions)
try:
cur.execute("""
SELECT
SUM(active_sessions) as total_active_sessions,
SUM(max_concurrent_sessions) as total_max_sessions,
COUNT(CASE WHEN active_sessions >= max_concurrent_sessions THEN 1 END) as at_limit_count
FROM (
SELECT
l.id,
l.max_concurrent_sessions,
(SELECT COUNT(*) FROM license_sessions ls WHERE ls.license_id = l.id) as active_sessions
FROM licenses l
WHERE l.is_fake = false AND l.is_active = true
) session_data
""")
session_stats = cur.fetchone()
if session_stats:
total_active = session_stats[0] or 0
total_max = session_stats[1] or 0
at_limit = session_stats[2] or 0
utilization = int((total_active / total_max * 100)) if total_max > 0 else 0
stats['session_stats'] = {
'total_active_sessions': total_active,
'total_max_sessions': total_max,
'utilization_percent': utilization,
'licenses_at_limit': at_limit
}
else:
stats['session_stats'] = {
'total_active_sessions': 0,
'total_max_sessions': 0,
'utilization_percent': 0,
'licenses_at_limit': 0
}
except Exception as e:
current_app.logger.warning(f"Could not get session statistics: {str(e)}")
stats['session_stats'] = {
'total_active_sessions': 0,
'total_max_sessions': 0,
'utilization_percent': 0,
'licenses_at_limit': 0
}
conn.rollback()
license_distribution = []
hourly_sessions = []
@@ -621,7 +677,12 @@ def create_backup_route():
def restore_backup_route(backup_id):
"""Backup wiederherstellen"""
from flask import jsonify
encryption_key = request.form.get('encryption_key')
# Handle both JSON and form data
if request.is_json:
encryption_key = request.json.get('encryption_key')
else:
encryption_key = request.form.get('encryption_key')
success, message = restore_backup(backup_id, encryption_key)
@@ -967,7 +1028,7 @@ def license_analytics():
AVG(device_count) as avg_usage
FROM licenses l
LEFT JOIN (
SELECT license_id, COUNT(DISTINCT hardware_id) as device_count
SELECT license_id, COUNT(DISTINCT hardware_fingerprint) as device_count
FROM license_heartbeats
WHERE timestamp > NOW() - INTERVAL '30 days'
GROUP BY license_id
@@ -1282,7 +1343,7 @@ def terminate_session(session_id):
# Get session info
cur.execute("""
SELECT license_id, hardware_id, ip_address, client_version, started_at
SELECT license_id, hardware_fingerprint, ip_address, client_version, started_at
FROM license_sessions
WHERE id = %s
""", (session_id,))
@@ -1424,6 +1485,9 @@ def regenerate_api_key():
random_part = ''.join(random.choices(string.ascii_uppercase + string.digits, k=32))
new_api_key = f"AF-{year_part}-{random_part}"
# Log what we're attempting
app.logger.info(f"Attempting to regenerate API key. New key: {new_api_key[:10]}...")
# Update the API key
cur.execute("""
UPDATE system_api_key
@@ -1433,15 +1497,27 @@ def regenerate_api_key():
WHERE id = 1
""", (new_api_key, session.get('username')))
# Log rows affected
app.logger.info(f"Rows affected by UPDATE: {cur.rowcount}")
conn.commit()
flash('API Key wurde erfolgreich regeneriert', 'success')
# Verify the update
cur.execute("SELECT api_key FROM system_api_key WHERE id = 1")
result = cur.fetchone()
if result and result[0] == new_api_key:
app.logger.info("API key successfully updated in database")
flash('API Key wurde erfolgreich regeneriert', 'success')
else:
app.logger.error(f"API key update verification failed. Expected: {new_api_key[:10]}..., Found: {result[0][:10] if result else 'None'}...")
flash('API Key wurde regeneriert, aber Verifizierung fehlgeschlagen', 'warning')
# Log action
log_audit('API_KEY_REGENERATED', 'system_api_key', 1,
additional_info="API Key regenerated")
except Exception as e:
app.logger.error(f"Error regenerating API key: {str(e)}", exc_info=True)
conn.rollback()
flash(f'Fehler beim Regenerieren des API Keys: {str(e)}', 'error')
@@ -1452,6 +1528,63 @@ def regenerate_api_key():
return redirect(url_for('admin.license_config'))
@admin_bp.route("/api-key/test-regenerate", methods=["GET"])
@login_required
def test_regenerate_api_key():
"""Test endpoint to check if regeneration works"""
import string
import random
conn = get_connection()
cur = conn.cursor()
try:
# Check current API key
cur.execute("SELECT api_key, regenerated_at FROM system_api_key WHERE id = 1")
current = cur.fetchone()
# Generate new API key
year_part = datetime.now().strftime('%Y')
random_part = ''.join(random.choices(string.ascii_uppercase + string.digits, k=32))
new_api_key = f"AF-{year_part}-{random_part}"
# Update the API key
cur.execute("""
UPDATE system_api_key
SET api_key = %s,
regenerated_at = CURRENT_TIMESTAMP,
regenerated_by = %s
WHERE id = 1
""", (new_api_key, session.get('username')))
rows_affected = cur.rowcount
conn.commit()
# Verify the update
cur.execute("SELECT api_key, regenerated_at FROM system_api_key WHERE id = 1")
updated = cur.fetchone()
result = {
'current_api_key': current[0] if current else None,
'current_regenerated_at': str(current[1]) if current and current[1] else None,
'new_api_key': new_api_key,
'rows_affected': rows_affected,
'updated_api_key': updated[0] if updated else None,
'updated_regenerated_at': str(updated[1]) if updated and updated[1] else None,
'success': updated and updated[0] == new_api_key
}
return jsonify(result)
except Exception as e:
conn.rollback()
return jsonify({'error': str(e), 'traceback': traceback.format_exc()})
finally:
cur.close()
conn.close()
@admin_bp.route("/test-api-key")
@login_required
def test_api_key():