Add latest changes
Dieser Commit ist enthalten in:
@@ -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():
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren