feat: Implement frontend production deployment and enhance admin dashboard functionality

This commit is contained in:
2025-05-26 21:54:13 +02:00
parent c2ea6c34ea
commit 7aa70cf976
59 changed files with 9161 additions and 10894 deletions

View File

@@ -1,2 +0,0 @@
# Blueprint package initialization file
# Makes the directory a proper Python package

View File

@@ -1,559 +0,0 @@
from flask import Blueprint, jsonify, request, session, current_app
from datetime import datetime, timedelta
import logging
import uuid
import json
import os
from models import User, Job, Printer, SystemLog
from database.db_manager import DatabaseManager
from flask_login import login_required, current_user
api_bp = Blueprint('api', __name__, url_prefix='/api')
logger = logging.getLogger(__name__)
@api_bp.route('/jobs')
def get_jobs():
"""Get jobs with optional filtering"""
try:
db = DatabaseManager()
# Get query parameters
status = request.args.get('status')
limit = request.args.get('limit', type=int)
# Use a session for proper relationship handling
session = db.get_session()
try:
from sqlalchemy.orm import joinedload
# Get jobs based on filters with eager loading of relationships
if status == 'active':
jobs_query = session.query(Job).options(
joinedload(Job.user),
joinedload(Job.printer)
).filter(Job.status == 'running')
jobs = jobs_query.all() or []
# Also include pending jobs
pending_jobs_query = session.query(Job).options(
joinedload(Job.user),
joinedload(Job.printer)
).filter(Job.status == 'pending')
pending_jobs = pending_jobs_query.all() or []
jobs.extend(pending_jobs)
else:
jobs_query = session.query(Job).options(
joinedload(Job.user),
joinedload(Job.printer)
)
jobs = jobs_query.all() or []
# Apply limit
if limit:
jobs = jobs[:limit]
# Format jobs for API response - convert all data while session is open
formatted_jobs = []
for job in jobs:
if hasattr(job, '__dict__'):
# Access all relationship data here while the session is still open
printer_name = job.printer.name if job.printer else 'Kein Drucker'
user_name = job.user.name if job.user else 'Kein Benutzer'
job_data = {
'id': getattr(job, 'id', None),
'name': getattr(job, 'name', 'Unbenannter Auftrag'),
'status': getattr(job, 'status', 'unknown'),
'progress': getattr(job, 'progress', 0),
'printer_name': printer_name,
'user_name': user_name,
'created_at': getattr(job, 'created_at', datetime.now()).isoformat() if hasattr(job, 'created_at') else datetime.now().isoformat(),
'estimated_time': getattr(job, 'estimated_time', 0),
'print_time': getattr(job, 'print_time', 0)
}
formatted_jobs.append(job_data)
finally:
# Always close the session
session.close()
return jsonify(formatted_jobs)
except Exception as e:
logger.error(f"Error getting jobs: {str(e)}")
return jsonify([])
@api_bp.route('/printers', methods=['GET'])
def get_printers():
"""Get all printers"""
try:
db = DatabaseManager()
session = db.get_session()
try:
printers = session.query(Printer).all() or []
# Format printers for API response
formatted_printers = []
for printer in printers:
printer_data = {
'id': printer.id,
'name': printer.name,
'model': printer.model,
'location': printer.location,
'mac_address': printer.mac_address,
'plug_ip': printer.plug_ip,
'status': printer.status,
'created_at': printer.created_at.isoformat() if printer.created_at else datetime.now().isoformat()
}
formatted_printers.append(printer_data)
return jsonify({'printers': formatted_printers})
finally:
session.close()
except Exception as e:
logger.error(f"Error getting printers: {str(e)}")
return jsonify({'printers': []})
@api_bp.route('/printers', methods=['POST'])
@login_required
def add_printer():
"""Add a new printer"""
# Überprüfe, ob der Benutzer Admin-Rechte hat
if not current_user.is_admin:
return jsonify({'error': 'Nur Administratoren können Drucker hinzufügen'}), 403
try:
# Hole die Daten aus dem Request
data = request.json
if not data:
return jsonify({'error': 'Keine Daten erhalten'}), 400
# Überprüfe, ob alle notwendigen Felder vorhanden sind
required_fields = ['name', 'model', 'location', 'mac_address', 'plug_ip']
for field in required_fields:
if field not in data:
return jsonify({'error': f'Feld {field} fehlt'}), 400
# Erstelle ein neues Printer-Objekt
new_printer = Printer(
name=data['name'],
model=data['model'],
location=data['location'],
mac_address=data['mac_address'],
plug_ip=data['plug_ip'],
plug_username='admin', # Default-Werte für Plug-Credentials
plug_password='vT6Vsd^p', # Korrektes Passwort für Router-Zugang
status='available'
)
# Speichere den Drucker in der Datenbank
db = DatabaseManager()
session = db.get_session()
try:
session.add(new_printer)
session.commit()
# Gib die Drucker-Daten zurück
printer_data = {
'id': new_printer.id,
'name': new_printer.name,
'model': new_printer.model,
'location': new_printer.location,
'mac_address': new_printer.mac_address,
'plug_ip': new_printer.plug_ip,
'status': new_printer.status,
'created_at': new_printer.created_at.isoformat() if new_printer.created_at else datetime.now().isoformat()
}
return jsonify({'printer': printer_data, 'message': 'Drucker erfolgreich hinzugefügt'}), 201
except Exception as e:
session.rollback()
logger.error(f"Error adding printer: {str(e)}")
return jsonify({'error': f'Fehler beim Hinzufügen des Druckers: {str(e)}'}), 500
finally:
session.close()
except Exception as e:
logger.error(f"Error adding printer: {str(e)}")
return jsonify({'error': f'Fehler beim Hinzufügen des Druckers: {str(e)}'}), 500
@api_bp.route('/printers/<int:printer_id>', methods=['DELETE'])
@login_required
def delete_printer(printer_id):
"""Delete a printer by ID"""
# Überprüfe, ob der Benutzer Admin-Rechte hat
if not current_user.is_admin:
return jsonify({'error': 'Nur Administratoren können Drucker löschen'}), 403
try:
db = DatabaseManager()
session = db.get_session()
try:
# Finde den Drucker
printer = session.query(Printer).filter(Printer.id == printer_id).first()
if not printer:
return jsonify({'error': f'Drucker mit ID {printer_id} nicht gefunden'}), 404
# Lösche den Drucker
session.delete(printer)
session.commit()
return jsonify({'message': 'Drucker erfolgreich gelöscht'}), 200
except Exception as e:
session.rollback()
logger.error(f"Error deleting printer: {str(e)}")
return jsonify({'error': f'Fehler beim Löschen des Druckers: {str(e)}'}), 500
finally:
session.close()
except Exception as e:
logger.error(f"Error deleting printer: {str(e)}")
return jsonify({'error': f'Fehler beim Löschen des Druckers: {str(e)}'}), 500
# API-Route für Admin-Statistiken
@api_bp.route('/admin/stats', methods=['GET'])
@login_required
def get_admin_stats():
if not current_user.is_admin:
return jsonify({"error": "Unauthorized"}), 403
# Statistikdaten sammeln
total_users = User.query.count()
total_printers = Printer.query.count()
active_jobs = Job.query.filter_by(status='running').count()
# Erfolgsrate berechnen
total_completed_jobs = Job.query.filter_by(status='completed').count()
total_jobs = Job.query.filter(Job.status.in_(['completed', 'failed'])).count()
success_rate = "0%"
if total_jobs > 0:
success_rate = f"{int((total_completed_jobs / total_jobs) * 100)}%"
# Scheduler-Status (Beispiel)
scheduler_status = True # In einer echten Anwendung würde hier der tatsächliche Status geprüft
# Systeminformationen
system_stats = {
"cpu_usage": "25%",
"memory_usage": "40%",
"disk_usage": "30%",
"uptime": "3d 12h 45m"
}
# Drucker-Status
printer_status = {
"available": Printer.query.filter_by(status='available').count(),
"busy": Printer.query.filter_by(status='busy').count(),
"offline": Printer.query.filter_by(status='offline').count(),
"maintenance": Printer.query.filter_by(status='maintenance').count()
}
return jsonify({
"total_users": total_users,
"total_printers": total_printers,
"active_jobs": active_jobs,
"success_rate": success_rate,
"scheduler_status": scheduler_status,
"system_stats": system_stats,
"printer_status": printer_status,
"last_updated": datetime.now().isoformat()
})
# API-Route für Protokolle
@api_bp.route('/logs', methods=['GET'])
@login_required
def get_logs():
if not current_user.is_admin:
return jsonify({"error": "Unauthorized"}), 403
# Logs aus der Datenbank abrufen
logs = SystemLog.query.order_by(SystemLog.timestamp.desc()).limit(100).all()
log_data = []
for log in logs:
log_data.append({
"id": log.id,
"timestamp": log.timestamp.isoformat(),
"level": log.level,
"source": log.source,
"message": log.message
})
return jsonify({"logs": log_data})
# Scheduler-Status abrufen
@api_bp.route('/scheduler/status', methods=['GET'])
@login_required
def get_scheduler_status():
if not current_user.is_admin:
return jsonify({"error": "Unauthorized"}), 403
# In einer echten Anwendung würde hier der tatsächliche Status geprüft werden
scheduler_active = True
# Aktuelle Aufgaben (Beispiel)
tasks = [
{
"id": 1,
"name": "Druckaufträge verarbeiten",
"status": "running",
"last_run": datetime.now().isoformat(),
"next_run": datetime.now().isoformat()
},
{
"id": 2,
"name": "Status-Updates sammeln",
"status": "idle",
"last_run": datetime.now().isoformat(),
"next_run": datetime.now().isoformat()
}
]
return jsonify({
"active": scheduler_active,
"tasks": tasks,
"last_updated": datetime.now().isoformat()
})
# Scheduler starten
@api_bp.route('/scheduler/start', methods=['POST'])
@login_required
def start_scheduler():
if not current_user.is_admin:
return jsonify({"error": "Unauthorized"}), 403
# In einer echten Anwendung würde hier der Scheduler gestartet werden
return jsonify({
"success": True,
"message": "Scheduler started successfully",
"active": True
})
# Scheduler stoppen
@api_bp.route('/scheduler/stop', methods=['POST'])
@login_required
def stop_scheduler():
if not current_user.is_admin:
return jsonify({"error": "Unauthorized"}), 403
# In einer echten Anwendung würde hier der Scheduler gestoppt werden
return jsonify({
"success": True,
"message": "Scheduler stopped successfully",
"active": False
})
# Benutzer abrufen
@api_bp.route('/users', methods=['GET'])
@login_required
def get_users():
if not current_user.is_admin:
return jsonify({"error": "Unauthorized"}), 403
users = User.query.all()
result = []
for user in users:
result.append({
'id': user.id,
'name': user.name,
'email': user.email,
'is_admin': user.is_admin,
'is_active': user.is_active,
'created_at': user.created_at.isoformat()
})
return jsonify({"users": result})
# Neuen Benutzer hinzufügen
@api_bp.route('/users', methods=['POST'])
@login_required
def add_user():
if not current_user.is_admin:
return jsonify({"error": "Unauthorized"}), 403
data = request.json
if not data:
return jsonify({"error": "No data provided"}), 400
required_fields = ['name', 'email', 'password']
for field in required_fields:
if field not in data:
return jsonify({"error": f"Missing required field: {field}"}), 400
# Prüfen, ob E-Mail bereits existiert
existing_user = User.query.filter_by(email=data['email']).first()
if existing_user:
return jsonify({"error": "Email already exists"}), 400
# Neuen Benutzer erstellen
user = User(
name=data['name'],
email=data['email'],
is_admin=data.get('role') == 'admin',
is_active=data.get('status') == 'active'
)
user.set_password(data['password'])
try:
db = DatabaseManager()
session = db.get_session()
session.add(user)
session.commit()
return jsonify({
"success": True,
"message": "User added successfully",
"user": {
"id": user.id,
"name": user.name,
"email": user.email,
"is_admin": user.is_admin,
"is_active": user.is_active,
"created_at": user.created_at.isoformat()
}
}), 201
except Exception as e:
db.get_session().rollback()
return jsonify({"error": str(e)}), 500
# Benutzer löschen
@api_bp.route('/users/<int:user_id>', methods=['DELETE'])
@login_required
def delete_user(user_id):
if not current_user.is_admin:
return jsonify({"error": "Unauthorized"}), 403
# Benutzer kann sich nicht selbst löschen
if current_user.id == user_id:
return jsonify({"error": "Cannot delete yourself"}), 400
user = User.query.get(user_id)
if not user:
return jsonify({"error": "User not found"}), 404
try:
db = DatabaseManager()
session = db.get_session()
session.delete(user)
session.commit()
return jsonify({"success": True, "message": "User deleted successfully"})
except Exception as e:
session.rollback()
return jsonify({"error": str(e)}), 500
# Cache leeren
@api_bp.route('/admin/cache/clear', methods=['POST'])
@login_required
def clear_cache():
"""Cache leeren"""
if not current_user.is_admin:
return jsonify({"error": "Unauthorized"}), 403
try:
# Hier würde normalerweise der Cache geleert werden
# Für dieses Beispiel simulieren wir es
logger.info(f"Cache cleared by admin user {current_user.name}")
return jsonify({
"success": True,
"message": "Cache erfolgreich geleert"
})
except Exception as e:
logger.error(f"Error clearing cache: {str(e)}")
return jsonify({"error": f"Fehler beim Leeren des Cache: {str(e)}"}), 500
# Datenbank optimieren
@api_bp.route('/admin/database/optimize', methods=['POST'])
@login_required
def optimize_database():
"""Datenbank optimieren"""
if not current_user.is_admin:
return jsonify({"error": "Unauthorized"}), 403
try:
# Hier würde normalerweise die Datenbank optimiert werden
# Für dieses Beispiel simulieren wir es
logger.info(f"Database optimization started by admin user {current_user.name}")
return jsonify({
"success": True,
"message": "Datenbank erfolgreich optimiert"
})
except Exception as e:
logger.error(f"Error optimizing database: {str(e)}")
return jsonify({"error": f"Fehler bei der Datenbankoptimierung: {str(e)}"}), 500
# Backup erstellen
@api_bp.route('/admin/backup/create', methods=['POST'])
@login_required
def create_backup():
"""Backup erstellen"""
if not current_user.is_admin:
return jsonify({"error": "Unauthorized"}), 403
try:
# Hier würde normalerweise ein Backup erstellt werden
# Für dieses Beispiel simulieren wir es
backup_filename = f"backup_{datetime.now().strftime('%Y%m%d_%H%M%S')}.sql"
logger.info(f"Backup created: {backup_filename} by admin user {current_user.name}")
return jsonify({
"success": True,
"message": f"Backup erfolgreich erstellt: {backup_filename}",
"filename": backup_filename
})
except Exception as e:
logger.error(f"Error creating backup: {str(e)}")
return jsonify({"error": f"Fehler beim Erstellen des Backups: {str(e)}"}), 500
# Drucker aktualisieren
@api_bp.route('/admin/printers/update', methods=['POST'])
@login_required
def update_printers():
"""Alle Drucker aktualisieren"""
if not current_user.is_admin:
return jsonify({"error": "Unauthorized"}), 403
try:
# Hier würde normalerweise der Status aller Drucker aktualisiert werden
# Für dieses Beispiel simulieren wir es
logger.info(f"Printer update initiated by admin user {current_user.name}")
return jsonify({
"success": True,
"message": "Alle Drucker wurden erfolgreich aktualisiert"
})
except Exception as e:
logger.error(f"Error updating printers: {str(e)}")
return jsonify({"error": f"Fehler beim Aktualisieren der Drucker: {str(e)}"}), 500
# System neustarten
@api_bp.route('/admin/system/restart', methods=['POST'])
@login_required
def restart_system():
"""System neustarten"""
if not current_user.is_admin:
return jsonify({"error": "Unauthorized"}), 403
try:
# Hier würde normalerweise das System neugestartet werden
# Für dieses Beispiel simulieren wir es
logger.warning(f"System restart initiated by admin user {current_user.name}")
return jsonify({
"success": True,
"message": "System wird neugestartet..."
})
except Exception as e:
logger.error(f"Error restarting system: {str(e)}")
return jsonify({"error": f"Fehler beim Neustart des Systems: {str(e)}"}), 500

View File

@@ -1,152 +0,0 @@
from flask import Blueprint, render_template, request, redirect, url_for, flash, jsonify
from flask_login import login_user, logout_user, current_user, login_required
from datetime import datetime
from models import User
from utils.logging_config import get_logger
from models import get_db_session
# Logger für Authentifizierung
auth_logger = get_logger("auth")
# Blueprint erstellen
auth_bp = Blueprint('auth', __name__, url_prefix='/auth')
@auth_bp.route("/login", methods=["GET", "POST"])
def login():
if current_user.is_authenticated:
return redirect(url_for("index"))
error = None
if request.method == "POST":
# Unterscheiden zwischen JSON-Anfragen und normalen Formular-Anfragen
is_json_request = request.is_json or request.headers.get('Content-Type') == 'application/json'
# Daten je nach Anfrageart auslesen
if is_json_request:
data = request.get_json()
username = data.get("username")
password = data.get("password")
remember_me = data.get("remember_me", False)
else:
username = request.form.get("username")
password = request.form.get("password")
remember_me = request.form.get("remember-me") == "on"
if not username or not password:
error = "Benutzername und Passwort müssen angegeben werden."
if is_json_request:
return jsonify({"error": error}), 400
else:
try:
db_session = get_db_session()
# Suche nach Benutzer mit übereinstimmendem Benutzernamen oder E-Mail
user = db_session.query(User).filter(
(User.username == username) | (User.email == username)
).first()
if user and user.check_password(password):
login_user(user, remember=remember_me)
auth_logger.info(f"Benutzer {username} hat sich angemeldet")
next_page = request.args.get("next")
db_session.close()
if is_json_request:
return jsonify({"success": True, "redirect_url": next_page or url_for("index")})
else:
if next_page:
return redirect(next_page)
return redirect(url_for("index"))
else:
error = "Ungültiger Benutzername oder Passwort."
auth_logger.warning(f"Fehlgeschlagener Login-Versuch für Benutzer {username}")
db_session.close()
if is_json_request:
return jsonify({"error": error}), 401
except Exception as e:
# Fehlerbehandlung für Datenbankprobleme
error = "Anmeldefehler. Bitte versuchen Sie es später erneut."
auth_logger.error(f"Fehler bei der Anmeldung: {str(e)}")
if is_json_request:
return jsonify({"error": error}), 500
return render_template("login.html", error=error)
@auth_bp.route("/logout", methods=["GET", "POST"])
@login_required
def logout():
username = current_user.username if hasattr(current_user, "username") else "Unbekannt"
logout_user()
auth_logger.info(f"Benutzer {username} hat sich abgemeldet")
# Unterscheiden zwischen JSON-Anfragen und normalen Anfragen
if request.is_json or request.headers.get('Content-Type') == 'application/json':
return jsonify({"success": True, "redirect_url": url_for("auth.login")})
else:
return redirect(url_for("auth.login"))
@auth_bp.route("/api/login", methods=["POST"])
def api_login():
"""API-Login-Endpunkt für Frontend"""
try:
data = request.get_json()
if not data:
return jsonify({"error": "Keine Daten erhalten"}), 400
username = data.get("username")
password = data.get("password")
remember_me = data.get("remember_me", False)
if not username or not password:
return jsonify({"error": "Benutzername und Passwort müssen angegeben werden"}), 400
db_session = get_db_session()
user = db_session.query(User).filter(
(User.username == username) | (User.email == username)
).first()
if user and user.check_password(password):
login_user(user, remember=remember_me)
auth_logger.info(f"API-Login erfolgreich für Benutzer {username}")
user_data = {
"id": user.id,
"username": user.username,
"name": user.name,
"email": user.email,
"is_admin": user.is_admin
}
db_session.close()
return jsonify({
"success": True,
"user": user_data,
"redirect_url": url_for("index")
})
else:
auth_logger.warning(f"Fehlgeschlagener API-Login für Benutzer {username}")
db_session.close()
return jsonify({"error": "Ungültiger Benutzername oder Passwort"}), 401
except Exception as e:
auth_logger.error(f"Fehler beim API-Login: {str(e)}")
return jsonify({"error": "Anmeldefehler. Bitte versuchen Sie es später erneut"}), 500
@auth_bp.route("/api/callback", methods=["GET", "POST"])
def api_callback():
"""OAuth-Callback-Endpunkt für externe Authentifizierung"""
try:
# Dieser Endpunkt würde für OAuth-Integration verwendet werden
# Hier könnte GitHub/OAuth-Code verarbeitet werden
# Placeholder für OAuth-Integration
return jsonify({
"message": "OAuth-Callback noch nicht implementiert",
"redirect_url": url_for("auth.login")
})
except Exception as e:
auth_logger.error(f"Fehler im OAuth-Callback: {str(e)}")
return jsonify({"error": "OAuth-Callback-Fehler"}), 500

View File

@@ -1,135 +0,0 @@
from flask import Blueprint, request, jsonify, session
from werkzeug.security import check_password_hash, generate_password_hash
import subprocess
import os
import logging
# Logger für Kiosk-Aktivitäten
kiosk_logger = logging.getLogger('kiosk')
# Blueprint erstellen
kiosk_bp = Blueprint('kiosk', __name__, url_prefix='/api/kiosk')
# Sicheres Passwort-Hash für Kiosk-Deaktivierung
KIOSK_PASSWORD_HASH = generate_password_hash("744563017196A")
@kiosk_bp.route('/status', methods=['GET'])
def get_kiosk_status():
"""Kiosk-Status abrufen."""
try:
# Prüfen ob Kiosk-Modus aktiv ist
kiosk_active = os.path.exists('/tmp/kiosk_active')
return jsonify({
"active": kiosk_active,
"message": "Kiosk-Status erfolgreich abgerufen"
})
except Exception as e:
kiosk_logger.error(f"Fehler beim Abrufen des Kiosk-Status: {str(e)}")
return jsonify({"error": "Fehler beim Abrufen des Status"}), 500
@kiosk_bp.route('/deactivate', methods=['POST'])
def deactivate_kiosk():
"""Kiosk-Modus mit Passwort deaktivieren."""
try:
data = request.get_json()
if not data or 'password' not in data:
return jsonify({"error": "Passwort erforderlich"}), 400
password = data['password']
# Passwort überprüfen
if not check_password_hash(KIOSK_PASSWORD_HASH, password):
kiosk_logger.warning(f"Fehlgeschlagener Kiosk-Deaktivierungsversuch von IP: {request.remote_addr}")
return jsonify({"error": "Ungültiges Passwort"}), 401
# Kiosk deaktivieren
try:
# Kiosk-Service stoppen
subprocess.run(['sudo', 'systemctl', 'stop', 'myp-kiosk'], check=True)
subprocess.run(['sudo', 'systemctl', 'disable', 'myp-kiosk'], check=True)
# Kiosk-Marker entfernen
if os.path.exists('/tmp/kiosk_active'):
os.remove('/tmp/kiosk_active')
# Normale Desktop-Umgebung wiederherstellen
subprocess.run(['sudo', 'systemctl', 'set-default', 'graphical.target'], check=True)
kiosk_logger.info(f"Kiosk-Modus erfolgreich deaktiviert von IP: {request.remote_addr}")
return jsonify({
"success": True,
"message": "Kiosk-Modus erfolgreich deaktiviert. System wird neu gestartet."
})
except subprocess.CalledProcessError as e:
kiosk_logger.error(f"Fehler beim Deaktivieren des Kiosk-Modus: {str(e)}")
return jsonify({"error": "Fehler beim Deaktivieren des Kiosk-Modus"}), 500
except Exception as e:
kiosk_logger.error(f"Unerwarteter Fehler bei Kiosk-Deaktivierung: {str(e)}")
return jsonify({"error": "Unerwarteter Fehler"}), 500
@kiosk_bp.route('/activate', methods=['POST'])
def activate_kiosk():
"""Kiosk-Modus aktivieren (nur für Admins)."""
try:
# Hier könnte eine Admin-Authentifizierung hinzugefügt werden
# Kiosk aktivieren
try:
# Kiosk-Marker setzen
with open('/tmp/kiosk_active', 'w') as f:
f.write('1')
# Kiosk-Service aktivieren
subprocess.run(['sudo', 'systemctl', 'enable', 'myp-kiosk'], check=True)
subprocess.run(['sudo', 'systemctl', 'start', 'myp-kiosk'], check=True)
kiosk_logger.info(f"Kiosk-Modus erfolgreich aktiviert von IP: {request.remote_addr}")
return jsonify({
"success": True,
"message": "Kiosk-Modus erfolgreich aktiviert"
})
except subprocess.CalledProcessError as e:
kiosk_logger.error(f"Fehler beim Aktivieren des Kiosk-Modus: {str(e)}")
return jsonify({"error": "Fehler beim Aktivieren des Kiosk-Modus"}), 500
except Exception as e:
kiosk_logger.error(f"Unerwarteter Fehler bei Kiosk-Aktivierung: {str(e)}")
return jsonify({"error": "Unerwarteter Fehler"}), 500
@kiosk_bp.route('/restart', methods=['POST'])
def restart_system():
"""System neu starten (nur nach Kiosk-Deaktivierung)."""
try:
data = request.get_json()
if not data or 'password' not in data:
return jsonify({"error": "Passwort erforderlich"}), 400
password = data['password']
# Passwort überprüfen
if not check_password_hash(KIOSK_PASSWORD_HASH, password):
kiosk_logger.warning(f"Fehlgeschlagener Neustart-Versuch von IP: {request.remote_addr}")
return jsonify({"error": "Ungültiges Passwort"}), 401
kiosk_logger.info(f"System-Neustart initiiert von IP: {request.remote_addr}")
# System nach kurzer Verzögerung neu starten
subprocess.Popen(['sudo', 'shutdown', '-r', '+1'])
return jsonify({
"success": True,
"message": "System wird in 1 Minute neu gestartet"
})
except Exception as e:
kiosk_logger.error(f"Fehler beim System-Neustart: {str(e)}")
return jsonify({"error": "Fehler beim Neustart"}), 500

View File

@@ -1,366 +0,0 @@
from flask import Blueprint, render_template, request, redirect, url_for, flash, jsonify
from flask_login import current_user, login_required
from datetime import datetime
from utils.logging_config import get_logger
from models import User, get_db_session
# Logger für Benutzeraktionen
user_logger = get_logger("user")
# Blueprint erstellen
user_bp = Blueprint('user', __name__, url_prefix='/user')
@user_bp.route("/profile", methods=["GET"])
@login_required
def profile():
"""Profil-Seite anzeigen"""
user_logger.info(f"Benutzer {current_user.username} hat seine Profilseite aufgerufen")
return render_template("profile.html", user=current_user)
@user_bp.route("/settings", methods=["GET"])
@login_required
def settings():
"""Einstellungen-Seite anzeigen"""
user_logger.info(f"Benutzer {current_user.username} hat seine Einstellungsseite aufgerufen")
return render_template("settings.html", user=current_user)
@user_bp.route("/update-profile", methods=["POST"])
@login_required
def update_profile():
"""Benutzerprofilinformationen aktualisieren"""
try:
# Überprüfen, ob es sich um eine JSON-Anfrage handelt
is_json_request = request.is_json or request.headers.get('Content-Type') == 'application/json'
if is_json_request:
data = request.get_json()
name = data.get("name")
email = data.get("email")
department = data.get("department")
position = data.get("position")
phone = data.get("phone")
else:
name = request.form.get("name")
email = request.form.get("email")
department = request.form.get("department")
position = request.form.get("position")
phone = request.form.get("phone")
db_session = get_db_session()
user = db_session.query(User).filter(User.id == current_user.id).first()
if user:
# Aktualisiere die Benutzerinformationen
if name:
user.name = name
if email:
user.email = email
if department:
user.department = department
if position:
user.position = position
if phone:
user.phone = phone
user.updated_at = datetime.now()
db_session.commit()
user_logger.info(f"Benutzer {current_user.username} hat sein Profil aktualisiert")
if is_json_request:
return jsonify({
"success": True,
"message": "Profil erfolgreich aktualisiert"
})
else:
flash("Profil erfolgreich aktualisiert", "success")
return redirect(url_for("user.profile"))
else:
error = "Benutzer nicht gefunden."
if is_json_request:
return jsonify({"error": error}), 404
else:
flash(error, "error")
return redirect(url_for("user.profile"))
except Exception as e:
error = f"Fehler beim Aktualisieren des Profils: {str(e)}"
user_logger.error(error)
if request.is_json:
return jsonify({"error": error}), 500
else:
flash(error, "error")
return redirect(url_for("user.profile"))
finally:
db_session.close()
@user_bp.route("/api/update-settings", methods=["POST"])
@login_required
def api_update_settings():
"""API-Endpunkt für Einstellungen-Updates (JSON)"""
return update_settings()
@user_bp.route("/update-settings", methods=["POST"])
@login_required
def update_settings():
"""Benutzereinstellungen aktualisieren"""
try:
# Überprüfen, ob es sich um eine JSON-Anfrage handelt
is_json_request = request.is_json or request.headers.get('Content-Type') == 'application/json'
# Einstellungen aus der Anfrage extrahieren
if is_json_request:
data = request.get_json()
theme = data.get("theme")
reduced_motion = data.get("reduced_motion", False)
contrast = data.get("contrast", "normal")
notifications = data.get("notifications", {})
privacy = data.get("privacy", {})
else:
theme = request.form.get("theme", "system")
reduced_motion = request.form.get("reduced_motion") == "on"
contrast = request.form.get("contrast", "normal")
notifications = {
"new_jobs": request.form.get("notify_new_jobs") == "on",
"job_updates": request.form.get("notify_job_updates") == "on",
"system": request.form.get("notify_system") == "on",
"email": request.form.get("notify_email") == "on"
}
privacy = {
"activity_logs": request.form.get("activity_logs") == "on",
"two_factor": request.form.get("two_factor") == "on",
"auto_logout": request.form.get("auto_logout", "60")
}
db_session = get_db_session()
user = db_session.query(User).filter(User.id == current_user.id).first()
if user:
# Erstelle ein Einstellungs-Dictionary, das wir als JSON speichern können
settings = {
"theme": theme,
"reduced_motion": reduced_motion,
"contrast": contrast,
"notifications": notifications,
"privacy": privacy,
"last_updated": datetime.now().isoformat()
}
# In einer echten Anwendung würden wir die Einstellungen in der Datenbank speichern
# Hier simulieren wir dies durch Aktualisierung des User-Objekts
# Wenn die User-Tabelle eine settings-Spalte hat, würden wir diese verwenden
# user.settings = json.dumps(settings)
# Für Demonstrationszwecke speichern wir die letzten Einstellungen im Session-Cookie
# (In einer Produktionsumgebung würde man dies in der Datenbank speichern)
from flask import session
session['user_settings'] = settings
user.updated_at = datetime.now()
db_session.commit()
user_logger.info(f"Benutzer {current_user.username} hat seine Einstellungen aktualisiert")
if is_json_request:
return jsonify({
"success": True,
"message": "Einstellungen erfolgreich aktualisiert",
"settings": settings
})
else:
flash("Einstellungen erfolgreich aktualisiert", "success")
return redirect(url_for("user.settings"))
else:
error = "Benutzer nicht gefunden."
if is_json_request:
return jsonify({"error": error}), 404
else:
flash(error, "error")
return redirect(url_for("user.settings"))
except Exception as e:
error = f"Fehler beim Aktualisieren der Einstellungen: {str(e)}"
user_logger.error(error)
if request.is_json:
return jsonify({"error": error}), 500
else:
flash(error, "error")
return redirect(url_for("user.settings"))
finally:
db_session.close()
@user_bp.route("/change-password", methods=["POST"])
@login_required
def change_password():
"""Benutzerpasswort ändern"""
try:
# Überprüfen, ob es sich um eine JSON-Anfrage handelt
is_json_request = request.is_json or request.headers.get('Content-Type') == 'application/json'
if is_json_request:
data = request.get_json()
current_password = data.get("current_password")
new_password = data.get("new_password")
confirm_password = data.get("confirm_password")
else:
current_password = request.form.get("current_password")
new_password = request.form.get("new_password")
confirm_password = request.form.get("confirm_password")
# Prüfen, ob alle Felder ausgefüllt sind
if not current_password or not new_password or not confirm_password:
error = "Alle Passwortfelder müssen ausgefüllt sein."
if is_json_request:
return jsonify({"error": error}), 400
else:
flash(error, "error")
return redirect(url_for("user.profile"))
# Prüfen, ob das neue Passwort und die Bestätigung übereinstimmen
if new_password != confirm_password:
error = "Das neue Passwort und die Bestätigung stimmen nicht überein."
if is_json_request:
return jsonify({"error": error}), 400
else:
flash(error, "error")
return redirect(url_for("user.profile"))
db_session = get_db_session()
user = db_session.query(User).filter(User.id == current_user.id).first()
if user and user.check_password(current_password):
# Passwort aktualisieren
user.set_password(new_password)
user.updated_at = datetime.now()
db_session.commit()
user_logger.info(f"Benutzer {current_user.username} hat sein Passwort geändert")
if is_json_request:
return jsonify({
"success": True,
"message": "Passwort erfolgreich geändert"
})
else:
flash("Passwort erfolgreich geändert", "success")
return redirect(url_for("user.profile"))
else:
error = "Das aktuelle Passwort ist nicht korrekt."
if is_json_request:
return jsonify({"error": error}), 401
else:
flash(error, "error")
return redirect(url_for("user.profile"))
except Exception as e:
error = f"Fehler beim Ändern des Passworts: {str(e)}"
user_logger.error(error)
if request.is_json:
return jsonify({"error": error}), 500
else:
flash(error, "error")
return redirect(url_for("user.profile"))
finally:
db_session.close()
@user_bp.route("/export", methods=["GET"])
@login_required
def export_user_data():
"""Exportiert alle Benutzerdaten als JSON für DSGVO-Konformität"""
try:
db_session = get_db_session()
user = db_session.query(User).filter(User.id == current_user.id).first()
if not user:
db_session.close()
return jsonify({"error": "Benutzer nicht gefunden"}), 404
# Benutzerdaten abrufen
from sqlalchemy.orm import joinedload
user_data = user.to_dict()
# Jobs des Benutzers abrufen
from models import Job
jobs = db_session.query(Job).filter(Job.user_id == user.id).all()
user_data["jobs"] = [job.to_dict() for job in jobs]
# Aktivitäten und Einstellungen hinzufügen
from flask import session
user_data["settings"] = session.get('user_settings', {})
# Persönliche Statistiken
user_data["statistics"] = {
"total_jobs": len(jobs),
"completed_jobs": len([j for j in jobs if j.status == "finished"]),
"failed_jobs": len([j for j in jobs if j.status == "failed"]),
"account_created": user.created_at.isoformat() if user.created_at else None,
"last_login": user.last_login.isoformat() if user.last_login else None
}
db_session.close()
# Daten als JSON-Datei zum Download anbieten
from flask import make_response
import json
response = make_response(json.dumps(user_data, indent=4))
response.headers["Content-Disposition"] = f"attachment; filename=user_data_{user.username}.json"
response.headers["Content-Type"] = "application/json"
user_logger.info(f"Benutzer {current_user.username} hat seine Daten exportiert")
return response
except Exception as e:
error = f"Fehler beim Exportieren der Benutzerdaten: {str(e)}"
user_logger.error(error)
return jsonify({"error": error}), 500
@user_bp.route("/profile", methods=["PUT"])
@login_required
def update_profile_api():
"""API-Endpunkt zum Aktualisieren des Benutzerprofils"""
try:
if not request.is_json:
return jsonify({"error": "Anfrage muss im JSON-Format sein"}), 400
data = request.get_json()
db_session = get_db_session()
user = db_session.query(User).filter(User.id == current_user.id).first()
if not user:
db_session.close()
return jsonify({"error": "Benutzer nicht gefunden"}), 404
# Aktualisiere nur die bereitgestellten Felder
if "name" in data:
user.name = data["name"]
if "email" in data:
user.email = data["email"]
if "department" in data:
user.department = data["department"]
if "position" in data:
user.position = data["position"]
if "phone" in data:
user.phone = data["phone"]
if "bio" in data:
user.bio = data["bio"]
user.updated_at = datetime.now()
db_session.commit()
# Aktualisierte Benutzerdaten zurückgeben
user_data = user.to_dict()
db_session.close()
user_logger.info(f"Benutzer {current_user.username} hat sein Profil über die API aktualisiert")
return jsonify({
"success": True,
"message": "Profil erfolgreich aktualisiert",
"user": user_data
})
except Exception as e:
error = f"Fehler beim Aktualisieren des Profils: {str(e)}"
user_logger.error(error)
return jsonify({"error": error}), 500