added login screen and admin console
This commit is contained in:
@@ -0,0 +1,179 @@
|
||||
"""
|
||||
ADMIN ROUTES FÜR app.py
|
||||
|
||||
Diese Routes sollten zu app.py hinzugefügt werden (nach den Error Handlers, vor if __name__)
|
||||
"""
|
||||
|
||||
# ============================================================================
|
||||
# ADMIN ROUTES (diese zeilen in app.py hinzufügen!)
|
||||
# ============================================================================
|
||||
|
||||
@app.route("/admin", methods=["GET"])
|
||||
@auth.login_required
|
||||
def admin_dashboard():
|
||||
"""Admin Dashboard - nur für Admin-Benutzer"""
|
||||
user = auth.current_user()
|
||||
if user not in ADMIN_USERS:
|
||||
log_access(user, "/admin", "GET", 403)
|
||||
return jsonify({"error": "Unauthorized - Admin access required"}), 403
|
||||
|
||||
log_access(user, "/admin", "GET")
|
||||
return render_template("admin.html")
|
||||
|
||||
|
||||
@app.route("/api/admin/stats", methods=["GET"])
|
||||
@auth.login_required
|
||||
def get_admin_stats():
|
||||
"""Statistiken für Admin Dashboard"""
|
||||
user = auth.current_user()
|
||||
if user not in ADMIN_USERS:
|
||||
log_access(user, "/api/admin/stats", "GET", 403)
|
||||
return jsonify({"error": "Unauthorized"}), 403
|
||||
|
||||
log_access(user, "/api/admin/stats", "GET")
|
||||
|
||||
logs = []
|
||||
if os.path.exists(LOG_FILE):
|
||||
try:
|
||||
with open(LOG_FILE, "r") as f:
|
||||
logs = json.load(f)
|
||||
except json.JSONDecodeError:
|
||||
logs = []
|
||||
|
||||
# Berechne Statistiken
|
||||
total_requests = len(logs)
|
||||
unique_users = len(set(log.get("username", "unknown") for log in logs))
|
||||
failed_logins = len([l for l in logs if l.get("status") == 401])
|
||||
|
||||
return jsonify({
|
||||
"total_requests": total_requests,
|
||||
"unique_users": unique_users,
|
||||
"failed_logins": failed_logins,
|
||||
"timestamp": datetime.now().isoformat()
|
||||
}), 200
|
||||
|
||||
|
||||
@app.route("/api/admin/logs", methods=["GET"])
|
||||
@auth.login_required
|
||||
def get_admin_logs():
|
||||
"""Access Logs für Admin Dashboard"""
|
||||
user = auth.current_user()
|
||||
if user not in ADMIN_USERS:
|
||||
log_access(user, "/api/admin/logs", "GET", 403)
|
||||
return jsonify({"error": "Unauthorized"}), 403
|
||||
|
||||
log_access(user, "/api/admin/logs", "GET")
|
||||
|
||||
logs = []
|
||||
if os.path.exists(LOG_FILE):
|
||||
try:
|
||||
with open(LOG_FILE, "r") as f:
|
||||
logs = json.load(f)
|
||||
except json.JSONDecodeError:
|
||||
logs = []
|
||||
|
||||
# Sortiere nach Zeitstempel absteigend (neueste zuerst)
|
||||
logs_sorted = sorted(logs, key=lambda x: x.get("timestamp", ""), reverse=True)
|
||||
|
||||
# Optionales Limit (letzte 500 Logs)
|
||||
return jsonify({"logs": logs_sorted[-500:]}), 200
|
||||
|
||||
|
||||
@app.route("/api/admin/export", methods=["GET"])
|
||||
@auth.login_required
|
||||
def export_logs():
|
||||
"""Exportiere Logs als CSV/JSON"""
|
||||
user = auth.current_user()
|
||||
if user not in ADMIN_USERS:
|
||||
return jsonify({"error": "Unauthorized"}), 403
|
||||
|
||||
log_access(user, "/api/admin/export", "GET")
|
||||
|
||||
logs = []
|
||||
if os.path.exists(LOG_FILE):
|
||||
try:
|
||||
with open(LOG_FILE, "r") as f:
|
||||
logs = json.load(f)
|
||||
except json.JSONDecodeError:
|
||||
logs = []
|
||||
|
||||
# Optional: Als CSV exportieren
|
||||
fmt = request.args.get("format", "json")
|
||||
|
||||
if fmt == "csv":
|
||||
import csv
|
||||
from io import StringIO
|
||||
|
||||
output = StringIO()
|
||||
writer = csv.DictWriter(output, fieldnames=["timestamp", "username", "endpoint", "method", "status"])
|
||||
writer.writeheader()
|
||||
writer.writerows(logs)
|
||||
|
||||
response = app.make_response(output.getvalue())
|
||||
response.headers["Content-Disposition"] = f"attachment; filename=logs_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv"
|
||||
response.headers["Content-Type"] = "text/csv"
|
||||
return response
|
||||
else:
|
||||
response = app.make_response(json.dumps(logs, indent=2))
|
||||
response.headers["Content-Disposition"] = f"attachment; filename=logs_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
|
||||
response.headers["Content-Type"] = "application/json"
|
||||
return response
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# OPTIONAL: Admin-Utility-Routes
|
||||
# ============================================================================
|
||||
|
||||
@app.route("/api/admin/clear-logs", methods=["POST"])
|
||||
@auth.login_required
|
||||
def clear_logs():
|
||||
"""Lösche alle Logs (nur für Admin, mit Bestätigung)"""
|
||||
user = auth.current_user()
|
||||
if user not in ADMIN_USERS:
|
||||
return jsonify({"error": "Unauthorized"}), 403
|
||||
|
||||
# Safety: Verlange "confirm=true" Query Parameter
|
||||
confirm = request.args.get("confirm") == "true"
|
||||
if not confirm:
|
||||
return jsonify({"error": "Confirmation required (add ?confirm=true)"}), 400
|
||||
|
||||
log_access(user, "/api/admin/clear-logs", "POST")
|
||||
|
||||
# Backup erstellen vor dem Löschen
|
||||
if os.path.exists(LOG_FILE):
|
||||
backup_file = LOG_FILE + f".backup_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
|
||||
import shutil
|
||||
shutil.copy(LOG_FILE, backup_file)
|
||||
|
||||
# Logs leeren
|
||||
with open(LOG_FILE, "w") as f:
|
||||
json.dump([], f)
|
||||
|
||||
return jsonify({
|
||||
"status": "success",
|
||||
"message": "Alle Logs wurden gelöscht (Backup erstellt)"
|
||||
}), 200
|
||||
|
||||
|
||||
@app.route("/api/admin/system-info", methods=["GET"])
|
||||
@auth.login_required
|
||||
def get_system_info():
|
||||
"""System-Information für Admin"""
|
||||
user = auth.current_user()
|
||||
if user not in ADMIN_USERS:
|
||||
return jsonify({"error": "Unauthorized"}), 403
|
||||
|
||||
log_access(user, "/api/admin/system-info", "GET")
|
||||
|
||||
import psutil
|
||||
|
||||
return jsonify({
|
||||
"environment": os.getenv("FLASK_ENV", "production"),
|
||||
"debug_mode": app.debug,
|
||||
"database_file": LOG_FILE,
|
||||
"database_size_kb": os.path.getsize(LOG_FILE) / 1024 if os.path.exists(LOG_FILE) else 0,
|
||||
"timestamp": datetime.now().isoformat(),
|
||||
# Optional: CPU/RAM Info
|
||||
# "cpu_percent": psutil.cpu_percent(),
|
||||
# "memory_percent": psutil.virtual_memory().percent,
|
||||
}), 200
|
||||
Reference in New Issue
Block a user