Dieser Commit ist enthalten in:
2025-06-08 22:37:03 +02:00
Ursprung fb83559d58
Commit 472998d16b
11 geänderte Dateien mit 499 neuen und 85 gelöschten Zeilen

Datei anzeigen

@@ -1323,8 +1323,31 @@ def licenses():
filter_type = request.args.get('type', '')
filter_status = request.args.get('status', '')
page = request.args.get('page', 1, type=int)
sort = request.args.get('sort', 'valid_until')
order = request.args.get('order', 'desc')
per_page = 20
# Whitelist für erlaubte Sortierfelder
allowed_sort_fields = {
'id': 'l.id',
'license_key': 'l.license_key',
'customer': 'c.name',
'email': 'c.email',
'type': 'l.license_type',
'valid_from': 'l.valid_from',
'valid_until': 'l.valid_until',
'status': 'status',
'active': 'l.is_active'
}
# Validierung
if sort not in allowed_sort_fields:
sort = 'valid_until'
if order not in ['asc', 'desc']:
order = 'desc'
sort_field = allowed_sort_fields[sort]
# SQL Query mit optionaler Suche und Filtern
query = """
SELECT l.id, l.license_key, c.name, c.email, l.license_type,
@@ -1374,7 +1397,20 @@ def licenses():
# Pagination
offset = (page - 1) * per_page
query += " ORDER BY l.valid_until DESC LIMIT %s OFFSET %s"
# Spezialbehandlung für berechnete Felder
if sort == 'status':
# Für Status müssen wir die CASE-Bedingung in ORDER BY wiederholen
query += f""" ORDER BY
CASE
WHEN l.is_active = FALSE THEN 'deaktiviert'
WHEN l.valid_until < CURRENT_DATE THEN 'abgelaufen'
WHEN l.valid_until < CURRENT_DATE + INTERVAL '30 days' THEN 'läuft bald ab'
ELSE 'aktiv'
END {order.upper()} LIMIT %s OFFSET %s"""
else:
query += f" ORDER BY {sort_field} {order.upper()} LIMIT %s OFFSET %s"
params.extend([per_page, offset])
cur.execute(query, params)
@@ -1394,6 +1430,8 @@ def licenses():
page=page,
total_pages=total_pages,
total=total,
sort=sort,
order=order,
username=session.get('username'))
@app.route("/license/edit/<int:license_id>", methods=["GET", "POST"])
@@ -1508,8 +1546,28 @@ def customers():
# Parameter
search = request.args.get('search', '').strip()
page = request.args.get('page', 1, type=int)
sort = request.args.get('sort', 'created_at')
order = request.args.get('order', 'desc')
per_page = 20
# Whitelist für erlaubte Sortierfelder
allowed_sort_fields = {
'id': 'c.id',
'name': 'c.name',
'email': 'c.email',
'created_at': 'c.created_at',
'licenses': 'license_count',
'active_licenses': 'active_licenses'
}
# Validierung
if sort not in allowed_sort_fields:
sort = 'created_at'
if order not in ['asc', 'desc']:
order = 'desc'
sort_field = allowed_sort_fields[sort]
# SQL Query mit optionaler Suche
base_query = """
SELECT c.id, c.name, c.email, c.created_at,
@@ -1544,9 +1602,9 @@ def customers():
# Pagination
offset = (page - 1) * per_page
query = base_query + """
query = base_query + f"""
GROUP BY c.id, c.name, c.email, c.created_at
ORDER BY c.created_at DESC
ORDER BY {sort_field} {order.upper()}
LIMIT %s OFFSET %s
"""
params.extend([per_page, offset])
@@ -1566,6 +1624,8 @@ def customers():
page=page,
total_pages=total_pages,
total=total,
sort=sort,
order=order,
username=session.get('username'))
@app.route("/customer/edit/<int:customer_id>", methods=["GET", "POST"])
@@ -1678,8 +1738,44 @@ def sessions():
conn = get_connection()
cur = conn.cursor()
# Sortierparameter
active_sort = request.args.get('active_sort', 'last_heartbeat')
active_order = request.args.get('active_order', 'desc')
ended_sort = request.args.get('ended_sort', 'ended_at')
ended_order = request.args.get('ended_order', 'desc')
# Whitelist für erlaubte Sortierfelder - Aktive Sessions
active_sort_fields = {
'customer': 'c.name',
'license': 'l.license_key',
'ip': 's.ip_address',
'started': 's.started_at',
'last_heartbeat': 's.last_heartbeat',
'inactive': 'minutes_inactive'
}
# Whitelist für erlaubte Sortierfelder - Beendete Sessions
ended_sort_fields = {
'customer': 'c.name',
'license': 'l.license_key',
'ip': 's.ip_address',
'started': 's.started_at',
'ended_at': 's.ended_at',
'duration': 'duration_minutes'
}
# Validierung
if active_sort not in active_sort_fields:
active_sort = 'last_heartbeat'
if ended_sort not in ended_sort_fields:
ended_sort = 'ended_at'
if active_order not in ['asc', 'desc']:
active_order = 'desc'
if ended_order not in ['asc', 'desc']:
ended_order = 'desc'
# Aktive Sessions abrufen
cur.execute("""
cur.execute(f"""
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
@@ -1687,12 +1783,12 @@ def sessions():
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
ORDER BY {active_sort_fields[active_sort]} {active_order.upper()}
""")
active_sessions = cur.fetchall()
# Inaktive Sessions der letzten 24 Stunden
cur.execute("""
cur.execute(f"""
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
@@ -1701,7 +1797,7 @@ def sessions():
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
ORDER BY {ended_sort_fields[ended_sort]} {ended_order.upper()}
LIMIT 50
""")
recent_sessions = cur.fetchall()
@@ -1712,6 +1808,10 @@ def sessions():
return render_template("sessions.html",
active_sessions=active_sessions,
recent_sessions=recent_sessions,
active_sort=active_sort,
active_order=active_order,
ended_sort=ended_sort,
ended_order=ended_order,
username=session.get('username'))
@app.route("/session/end/<int:session_id>", methods=["POST"])
@@ -1914,8 +2014,27 @@ def audit_log():
filter_action = request.args.get('action', '').strip()
filter_entity = request.args.get('entity', '').strip()
page = request.args.get('page', 1, type=int)
sort = request.args.get('sort', 'timestamp')
order = request.args.get('order', 'desc')
per_page = 50
# Whitelist für erlaubte Sortierfelder
allowed_sort_fields = {
'timestamp': 'timestamp',
'username': 'username',
'action': 'action',
'entity': 'entity_type',
'ip': 'ip_address'
}
# Validierung
if sort not in allowed_sort_fields:
sort = 'timestamp'
if order not in ['asc', 'desc']:
order = 'desc'
sort_field = allowed_sort_fields[sort]
# SQL Query mit optionalen Filtern
query = """
SELECT id, timestamp, username, action, entity_type, entity_id,
@@ -1946,7 +2065,7 @@ def audit_log():
# Pagination
offset = (page - 1) * per_page
query += " ORDER BY timestamp DESC LIMIT %s OFFSET %s"
query += f" ORDER BY {sort_field} {order.upper()} LIMIT %s OFFSET %s"
params.extend([per_page, offset])
cur.execute(query, params)
@@ -1974,6 +2093,8 @@ def audit_log():
page=page,
total_pages=total_pages,
total=total,
sort=sort,
order=order,
username=session.get('username'))
@app.route("/backups")