Add latest changes
Dieser Commit ist enthalten in:
@@ -8,6 +8,7 @@ from auth.decorators import login_required
|
||||
from utils.audit import log_audit
|
||||
from utils.network import get_client_ip
|
||||
from db import get_connection, get_db_connection, get_db_cursor
|
||||
from db_license import get_license_db_cursor
|
||||
from models import get_active_sessions
|
||||
|
||||
# Create Blueprint
|
||||
@@ -17,37 +18,72 @@ session_bp = Blueprint('sessions', __name__)
|
||||
@session_bp.route("/sessions")
|
||||
@login_required
|
||||
def sessions():
|
||||
# Use regular DB for customer/license info
|
||||
conn = get_connection()
|
||||
cur = conn.cursor()
|
||||
|
||||
try:
|
||||
# Get is_active sessions with calculated inactive time
|
||||
cur.execute("""
|
||||
SELECT s.id, s.session_id, l.license_key, c.name, s.ip_address,
|
||||
s.user_agent, s.started_at, s.last_heartbeat,
|
||||
EXTRACT(EPOCH FROM (NOW() - s.last_heartbeat))/60 as minutes_inactive
|
||||
FROM sessions s
|
||||
JOIN licenses l ON s.license_id = l.id
|
||||
JOIN customers c ON l.customer_id = c.id
|
||||
WHERE s.is_active = TRUE
|
||||
ORDER BY s.last_heartbeat DESC
|
||||
""")
|
||||
active_sessions = cur.fetchall()
|
||||
# First get license mapping from admin DB
|
||||
cur.execute("SELECT id, license_key FROM licenses")
|
||||
license_map = {row[0]: row[1] for row in cur.fetchall()}
|
||||
|
||||
# Get recent ended sessions
|
||||
cur.execute("""
|
||||
SELECT s.id, s.session_id, l.license_key, c.name, s.ip_address,
|
||||
s.started_at, s.ended_at,
|
||||
EXTRACT(EPOCH FROM (s.ended_at - s.started_at))/60 as duration_minutes
|
||||
FROM sessions s
|
||||
JOIN licenses l ON s.license_id = l.id
|
||||
JOIN customers c ON l.customer_id = c.id
|
||||
WHERE s.is_active = FALSE
|
||||
AND s.ended_at > NOW() - INTERVAL '24 hours'
|
||||
ORDER BY s.ended_at DESC
|
||||
LIMIT 50
|
||||
""")
|
||||
recent_sessions = cur.fetchall()
|
||||
# Get customer mapping
|
||||
cur.execute("SELECT l.id, c.name FROM licenses l JOIN customers c ON l.customer_id = c.id")
|
||||
customer_map = {row[0]: row[1] for row in cur.fetchall()}
|
||||
|
||||
cur.close()
|
||||
conn.close()
|
||||
|
||||
# Now get sessions from license server DB
|
||||
with get_license_db_cursor() as license_cur:
|
||||
# Get active sessions
|
||||
license_cur.execute("""
|
||||
SELECT id, license_id, session_token, ip_address, client_version,
|
||||
started_at, last_heartbeat, hardware_id,
|
||||
EXTRACT(EPOCH FROM (NOW() - last_heartbeat))/60 as minutes_inactive
|
||||
FROM license_sessions
|
||||
WHERE ended_at IS NULL
|
||||
ORDER BY last_heartbeat DESC
|
||||
""")
|
||||
|
||||
active_sessions = []
|
||||
for row in license_cur.fetchall():
|
||||
active_sessions.append((
|
||||
row[0], # id
|
||||
row[2], # session_token
|
||||
license_map.get(row[1], 'Unknown'), # license_key
|
||||
customer_map.get(row[1], 'Unknown'), # customer name
|
||||
row[3], # ip_address
|
||||
row[4], # client_version
|
||||
row[5], # started_at
|
||||
row[6], # last_heartbeat
|
||||
row[8] # minutes_inactive
|
||||
))
|
||||
|
||||
# Get recent ended sessions
|
||||
license_cur.execute("""
|
||||
SELECT id, license_id, session_token, ip_address,
|
||||
started_at, ended_at,
|
||||
EXTRACT(EPOCH FROM (ended_at - started_at))/60 as duration_minutes
|
||||
FROM license_sessions
|
||||
WHERE ended_at IS NOT NULL
|
||||
AND ended_at > NOW() - INTERVAL '24 hours'
|
||||
ORDER BY ended_at DESC
|
||||
LIMIT 50
|
||||
""")
|
||||
|
||||
recent_sessions = []
|
||||
for row in license_cur.fetchall():
|
||||
recent_sessions.append((
|
||||
row[0], # id
|
||||
row[2], # session_token
|
||||
license_map.get(row[1], 'Unknown'), # license_key
|
||||
customer_map.get(row[1], 'Unknown'), # customer name
|
||||
row[3], # ip_address
|
||||
row[4], # started_at
|
||||
row[5], # ended_at
|
||||
row[6] # duration_minutes
|
||||
))
|
||||
|
||||
return render_template("sessions.html",
|
||||
active_sessions=active_sessions,
|
||||
@@ -57,9 +93,6 @@ def sessions():
|
||||
logging.error(f"Error loading sessions: {str(e)}")
|
||||
flash('Fehler beim Laden der Sessions!', 'error')
|
||||
return redirect(url_for('admin.dashboard'))
|
||||
finally:
|
||||
cur.close()
|
||||
conn.close()
|
||||
|
||||
|
||||
@session_bp.route("/sessions/history")
|
||||
@@ -78,19 +111,20 @@ def session_history():
|
||||
# Base query
|
||||
query = """
|
||||
SELECT
|
||||
s.id,
|
||||
s.license_key,
|
||||
s.username,
|
||||
s.hardware_id,
|
||||
s.started_at,
|
||||
s.ended_at,
|
||||
s.last_heartbeat,
|
||||
s.is_active,
|
||||
l.customer_name,
|
||||
ls.id,
|
||||
l.license_key,
|
||||
ls.machine_name as username,
|
||||
ls.hardware_id,
|
||||
ls.started_at,
|
||||
ls.ended_at,
|
||||
ls.last_heartbeat,
|
||||
CASE WHEN ls.ended_at IS NULL THEN true ELSE false END as is_active,
|
||||
c.name as customer_name,
|
||||
l.license_type,
|
||||
l.is_test
|
||||
FROM sessions s
|
||||
LEFT JOIN licenses l ON s.license_key = l.license_key
|
||||
FROM license_sessions ls
|
||||
JOIN licenses l ON ls.license_id = l.id
|
||||
LEFT JOIN customers c ON l.customer_id = c.id
|
||||
WHERE 1=1
|
||||
"""
|
||||
|
||||
@@ -98,18 +132,18 @@ def session_history():
|
||||
|
||||
# Apply filters
|
||||
if license_key:
|
||||
query += " AND s.license_key = %s"
|
||||
query += " AND l.license_key = %s"
|
||||
params.append(license_key)
|
||||
|
||||
if username:
|
||||
query += " AND s.username ILIKE %s"
|
||||
query += " AND ls.machine_name ILIKE %s"
|
||||
params.append(f'%{username}%')
|
||||
|
||||
# Time filter
|
||||
query += " AND s.started_at >= CURRENT_TIMESTAMP - INTERVAL '%s days'"
|
||||
query += " AND ls.started_at >= CURRENT_TIMESTAMP - INTERVAL '%s days'"
|
||||
params.append(days)
|
||||
|
||||
query += " ORDER BY s.started_at DESC LIMIT 1000"
|
||||
query += " ORDER BY ls.started_at DESC LIMIT 1000"
|
||||
|
||||
cur.execute(query, params)
|
||||
|
||||
@@ -144,11 +178,12 @@ def session_history():
|
||||
|
||||
# Get unique license keys for filter dropdown
|
||||
cur.execute("""
|
||||
SELECT DISTINCT s.license_key, l.customer_name
|
||||
FROM sessions s
|
||||
LEFT JOIN licenses l ON s.license_key = l.license_key
|
||||
WHERE s.started_at >= CURRENT_TIMESTAMP - INTERVAL '30 days'
|
||||
ORDER BY l.customer_name, s.license_key
|
||||
SELECT DISTINCT l.license_key, c.name as customer_name
|
||||
FROM license_sessions ls
|
||||
JOIN licenses l ON ls.license_id = l.id
|
||||
LEFT JOIN customers c ON l.customer_id = c.id
|
||||
WHERE ls.started_at >= CURRENT_TIMESTAMP - INTERVAL '30 days'
|
||||
ORDER BY c.name, l.license_key
|
||||
""")
|
||||
|
||||
available_licenses = []
|
||||
@@ -180,44 +215,48 @@ def session_history():
|
||||
@login_required
|
||||
def terminate_session(session_id):
|
||||
"""Beendet eine aktive Session"""
|
||||
conn = get_connection()
|
||||
cur = conn.cursor()
|
||||
|
||||
try:
|
||||
# Get session info
|
||||
cur.execute("""
|
||||
SELECT license_key, username, hardware_id
|
||||
FROM sessions
|
||||
WHERE id = %s AND is_active = true
|
||||
""", (session_id,))
|
||||
session_info = None
|
||||
|
||||
session_info = cur.fetchone()
|
||||
if not session_info:
|
||||
flash('Session nicht gefunden oder bereits beendet!', 'error')
|
||||
return redirect(url_for('sessions.sessions'))
|
||||
# Get session info from license server DB
|
||||
with get_license_db_cursor() as license_cur:
|
||||
license_cur.execute("""
|
||||
SELECT license_id, hardware_id, machine_name
|
||||
FROM license_sessions
|
||||
WHERE id = %s AND ended_at IS NULL
|
||||
""", (session_id,))
|
||||
|
||||
result = license_cur.fetchone()
|
||||
if not result:
|
||||
flash('Session nicht gefunden oder bereits beendet!', 'error')
|
||||
return redirect(url_for('sessions.sessions'))
|
||||
|
||||
license_id = result[0]
|
||||
|
||||
# Terminate session in license server DB
|
||||
license_cur.execute("""
|
||||
UPDATE license_sessions
|
||||
SET ended_at = CURRENT_TIMESTAMP, end_reason = 'admin_terminated'
|
||||
WHERE id = %s
|
||||
""", (session_id,))
|
||||
|
||||
# Terminate session
|
||||
cur.execute("""
|
||||
UPDATE sessions
|
||||
SET is_active = false, ended_at = CURRENT_TIMESTAMP
|
||||
WHERE id = %s
|
||||
""", (session_id,))
|
||||
|
||||
conn.commit()
|
||||
# Get license key from admin DB for audit log
|
||||
conn = get_connection()
|
||||
cur = conn.cursor()
|
||||
cur.execute("SELECT license_key FROM licenses WHERE id = %s", (license_id,))
|
||||
license_key = cur.fetchone()[0] if cur.fetchone() else 'Unknown'
|
||||
cur.close()
|
||||
conn.close()
|
||||
|
||||
# Audit log
|
||||
log_audit('SESSION_TERMINATE', 'session', session_id,
|
||||
additional_info=f"Session beendet für {session_info[1]} auf Lizenz {session_info[0]}")
|
||||
additional_info=f"Session beendet für Lizenz {license_key}")
|
||||
|
||||
flash('Session erfolgreich beendet!', 'success')
|
||||
|
||||
except Exception as e:
|
||||
conn.rollback()
|
||||
logging.error(f"Fehler beim Beenden der Session: {str(e)}")
|
||||
flash('Fehler beim Beenden der Session!', 'error')
|
||||
finally:
|
||||
cur.close()
|
||||
conn.close()
|
||||
|
||||
return redirect(url_for('sessions.sessions'))
|
||||
|
||||
@@ -230,10 +269,11 @@ def terminate_all_sessions(license_key):
|
||||
cur = conn.cursor()
|
||||
|
||||
try:
|
||||
# Count is_active sessions
|
||||
# Count active sessions
|
||||
cur.execute("""
|
||||
SELECT COUNT(*) FROM sessions
|
||||
WHERE license_key = %s AND is_active = true
|
||||
SELECT COUNT(*) FROM license_sessions ls
|
||||
JOIN licenses l ON ls.license_id = l.id
|
||||
WHERE l.license_key = %s AND ls.ended_at IS NULL
|
||||
""", (license_key,))
|
||||
|
||||
active_count = cur.fetchone()[0]
|
||||
@@ -244,9 +284,11 @@ def terminate_all_sessions(license_key):
|
||||
|
||||
# Terminate all sessions
|
||||
cur.execute("""
|
||||
UPDATE sessions
|
||||
SET is_active = false, ended_at = CURRENT_TIMESTAMP
|
||||
WHERE license_key = %s AND is_active = true
|
||||
UPDATE license_sessions
|
||||
SET ended_at = CURRENT_TIMESTAMP, end_reason = 'admin_terminated_all'
|
||||
WHERE license_id IN (
|
||||
SELECT id FROM licenses WHERE license_key = %s
|
||||
) AND ended_at IS NULL
|
||||
""", (license_key,))
|
||||
|
||||
conn.commit()
|
||||
@@ -280,8 +322,8 @@ def cleanup_sessions():
|
||||
|
||||
# Delete old inactive sessions
|
||||
cur.execute("""
|
||||
DELETE FROM sessions
|
||||
WHERE is_active = false
|
||||
DELETE FROM license_sessions
|
||||
WHERE ended_at IS NOT NULL
|
||||
AND ended_at < CURRENT_TIMESTAMP - INTERVAL '%s days'
|
||||
RETURNING id
|
||||
""", (days,))
|
||||
@@ -320,12 +362,13 @@ def session_statistics():
|
||||
# Aktuelle Statistiken
|
||||
cur.execute("""
|
||||
SELECT
|
||||
COUNT(DISTINCT s.license_key) as active_licenses,
|
||||
COUNT(DISTINCT s.username) as unique_users,
|
||||
COUNT(DISTINCT s.hardware_id) as unique_devices,
|
||||
COUNT(DISTINCT l.license_key) as active_licenses,
|
||||
COUNT(DISTINCT ls.machine_name) as unique_users,
|
||||
COUNT(DISTINCT ls.hardware_id) as unique_devices,
|
||||
COUNT(*) as total_active_sessions
|
||||
FROM sessions s
|
||||
WHERE s.is_active = true
|
||||
FROM license_sessions ls
|
||||
JOIN licenses l ON ls.license_id = l.id
|
||||
WHERE ls.ended_at IS NULL
|
||||
""")
|
||||
|
||||
current_stats = cur.fetchone()
|
||||
@@ -335,9 +378,9 @@ def session_statistics():
|
||||
SELECT
|
||||
l.license_type,
|
||||
COUNT(*) as session_count
|
||||
FROM sessions s
|
||||
JOIN licenses l ON s.license_key = l.license_key
|
||||
WHERE s.is_active = true
|
||||
FROM license_sessions ls
|
||||
JOIN licenses l ON ls.license_id = l.id
|
||||
WHERE ls.ended_at IS NULL
|
||||
GROUP BY l.license_type
|
||||
ORDER BY session_count DESC
|
||||
""")
|
||||
@@ -352,14 +395,15 @@ def session_statistics():
|
||||
# Top 10 Lizenzen nach aktiven Sessions
|
||||
cur.execute("""
|
||||
SELECT
|
||||
s.license_key,
|
||||
l.customer_name,
|
||||
l.license_key,
|
||||
c.name as customer_name,
|
||||
COUNT(*) as session_count,
|
||||
l.device_limit
|
||||
FROM sessions s
|
||||
JOIN licenses l ON s.license_key = l.license_key
|
||||
WHERE s.is_active = true
|
||||
GROUP BY s.license_key, l.customer_name, l.device_limit
|
||||
FROM license_sessions ls
|
||||
JOIN licenses l ON ls.license_id = l.id
|
||||
JOIN customers c ON l.customer_id = c.id
|
||||
WHERE ls.ended_at IS NULL
|
||||
GROUP BY l.license_key, c.name, l.device_limit
|
||||
ORDER BY session_count DESC
|
||||
LIMIT 10
|
||||
""")
|
||||
@@ -376,13 +420,14 @@ def session_statistics():
|
||||
# Session-Verlauf (letzte 7 Tage)
|
||||
cur.execute("""
|
||||
SELECT
|
||||
DATE(started_at) as date,
|
||||
DATE(ls.started_at) as date,
|
||||
COUNT(*) as login_count,
|
||||
COUNT(DISTINCT license_key) as unique_licenses,
|
||||
COUNT(DISTINCT username) as unique_users
|
||||
FROM sessions
|
||||
WHERE started_at >= CURRENT_DATE - INTERVAL '7 days'
|
||||
GROUP BY DATE(started_at)
|
||||
COUNT(DISTINCT l.license_key) as unique_licenses,
|
||||
COUNT(DISTINCT ls.machine_name) as unique_users
|
||||
FROM license_sessions ls
|
||||
JOIN licenses l ON ls.license_id = l.id
|
||||
WHERE ls.started_at >= CURRENT_DATE - INTERVAL '7 days'
|
||||
GROUP BY DATE(ls.started_at)
|
||||
ORDER BY date
|
||||
""")
|
||||
|
||||
@@ -399,9 +444,8 @@ def session_statistics():
|
||||
cur.execute("""
|
||||
SELECT
|
||||
AVG(EXTRACT(EPOCH FROM (ended_at - started_at))/3600) as avg_duration_hours
|
||||
FROM sessions
|
||||
WHERE is_active = false
|
||||
AND ended_at IS NOT NULL
|
||||
FROM license_sessions
|
||||
WHERE ended_at IS NOT NULL
|
||||
AND ended_at - started_at < INTERVAL '24 hours'
|
||||
AND started_at >= CURRENT_DATE - INTERVAL '30 days'
|
||||
""")
|
||||
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren