Die Dateien wurden wie folgt geändert und hinzugefügt:
1. backend/logs - 'admin', 'admin_api', 'app', 'calendar', 'data_management', 'drucker_steuerung', 'energy_monitoring', 'guest', 'hardware_integration', 'job_queue_system', 'jobs', 'models', 'monitoring_analytics', 'permissions', 'scheduler', 'security_suite', 'startup', '
This commit is contained in:
Binary file not shown.
@ -828,6 +828,43 @@ def inject_current_route():
|
||||
current_route = getattr(request, 'endpoint', None) or ''
|
||||
return {'current_route': current_route}
|
||||
|
||||
@app.context_processor
|
||||
def inject_moment():
|
||||
"""
|
||||
Injiziert eine moment-ähnliche Funktion für Template-Kompatibilität.
|
||||
|
||||
Behebt UndefinedError: 'moment' is not defined in Templates.
|
||||
Ersetzt moment.js durch native Python datetime-Funktionalität.
|
||||
"""
|
||||
def moment():
|
||||
"""Mock moment() Funktion die ein datetime-ähnliches Objekt zurückgibt"""
|
||||
class MomentLike:
|
||||
def __init__(self):
|
||||
self.dt = datetime.now()
|
||||
|
||||
def format(self, format_str):
|
||||
"""Konvertiert moment.js Format-Strings zu Python strftime"""
|
||||
# Moment.js -> Python strftime Mapping
|
||||
format_mapping = {
|
||||
'DD.MM.YYYY': '%d.%m.%Y',
|
||||
'DD/MM/YYYY': '%d/%m/%Y',
|
||||
'YYYY-MM-DD': '%Y-%m-%d',
|
||||
'HH:mm': '%H:%M',
|
||||
'HH:mm:ss': '%H:%M:%S',
|
||||
'DD.MM.YYYY HH:mm': '%d.%m.%Y %H:%M'
|
||||
}
|
||||
|
||||
# Direkte Ersetzung wenn bekanntes Format
|
||||
if format_str in format_mapping:
|
||||
return self.dt.strftime(format_mapping[format_str])
|
||||
|
||||
# Fallback: Standard deutsche Formatierung
|
||||
return self.dt.strftime('%d.%m.%Y')
|
||||
|
||||
return MomentLike()
|
||||
|
||||
return {'moment': moment}
|
||||
|
||||
@app.template_filter('format_datetime')
|
||||
def format_datetime_filter(value, format='%d.%m.%Y %H:%M'):
|
||||
"""Template-Filter für Datums-Formatierung"""
|
||||
|
Binary file not shown.
Binary file not shown.
@ -213,94 +213,178 @@ def edit_user_page(user_id):
|
||||
flash("Fehler beim Laden der Benutzerdaten", "error")
|
||||
return redirect(url_for('admin.users_overview'))
|
||||
|
||||
@admin_blueprint.route("/printers")
|
||||
@admin_blueprint.route("/users/create", methods=["POST"])
|
||||
@admin_required
|
||||
def printers_overview():
|
||||
"""Druckerübersicht für Administratoren mit Hardware-Integration"""
|
||||
def create_user():
|
||||
"""Erstellt einen neuen Benutzer"""
|
||||
try:
|
||||
from utils.hardware_integration import get_drucker_steuerung
|
||||
# Form-Daten aus POST-Request
|
||||
username = request.form.get('username', '').strip()
|
||||
email = request.form.get('email', '').strip()
|
||||
name = request.form.get('name', '').strip()
|
||||
password = request.form.get('password', '')
|
||||
role = request.form.get('role', 'user')
|
||||
department = request.form.get('department', '').strip()
|
||||
position = request.form.get('position', '').strip()
|
||||
phone = request.form.get('phone', '').strip()
|
||||
active = request.form.get('active') == 'on'
|
||||
|
||||
# Validierung
|
||||
if not username or not email or not password:
|
||||
flash("Benutzername, E-Mail und Passwort sind erforderlich", "error")
|
||||
return redirect(url_for('admin.add_user_page'))
|
||||
|
||||
with get_cached_session() as db_session:
|
||||
# Alle Drucker laden (nicht nur TBA Marienfelde)
|
||||
printers = db_session.query(Printer).order_by(Printer.created_at.desc()).all()
|
||||
# Prüfen ob Benutzer bereits existiert
|
||||
existing_user = db_session.query(User).filter(
|
||||
(User.username == username) | (User.email == email)
|
||||
).first()
|
||||
|
||||
# Hardware-Steuerung für Echtzeit-Status
|
||||
drucker_steuerung = get_drucker_steuerung()
|
||||
status_data = drucker_steuerung.template_daten_sammeln()
|
||||
if existing_user:
|
||||
flash("Benutzer mit diesem Namen oder E-Mail existiert bereits", "error")
|
||||
return redirect(url_for('admin.add_user_page'))
|
||||
|
||||
# Erweiterte Drucker-Informationen mit Hardware-Status
|
||||
enriched_printers = []
|
||||
online_count = 0
|
||||
# Neuen Benutzer erstellen
|
||||
new_user = User(
|
||||
username=username,
|
||||
email=email,
|
||||
name=name if name else None,
|
||||
role=role,
|
||||
department=department if department else None,
|
||||
position=position if position else None,
|
||||
phone=phone if phone else None,
|
||||
active=active,
|
||||
created_at=datetime.now(),
|
||||
updated_at=datetime.now()
|
||||
)
|
||||
|
||||
for printer in printers:
|
||||
printer_data = {
|
||||
'id': printer.id,
|
||||
'name': printer.name,
|
||||
'model': printer.model,
|
||||
'location': printer.location,
|
||||
'ip_address': printer.ip_address,
|
||||
'plug_ip': printer.plug_ip,
|
||||
'status': printer.status,
|
||||
'active': printer.active,
|
||||
'created_at': printer.created_at,
|
||||
'last_checked': printer.last_checked,
|
||||
# Hardware-Status hinzufügen
|
||||
'plug_online': False,
|
||||
'plug_power_state': 'unknown',
|
||||
'energy_usage': {},
|
||||
'has_active_jobs': False,
|
||||
'active_jobs_count': 0
|
||||
}
|
||||
|
||||
# Hardware-Status aus Steuerung hinzufügen
|
||||
if printer.id in status_data.get('drucker_status', {}):
|
||||
hw_status = status_data['drucker_status'][printer.id]
|
||||
printer_data.update({
|
||||
'plug_online': hw_status.get('plug_online', False),
|
||||
'plug_power_state': hw_status.get('plug_state', 'unknown'),
|
||||
'energy_usage': hw_status.get('energy_usage', {}),
|
||||
'last_seen': hw_status.get('last_seen')
|
||||
})
|
||||
|
||||
if hw_status.get('plug_online', False):
|
||||
online_count += 1
|
||||
|
||||
# Aktive Jobs für diesen Drucker zählen
|
||||
active_jobs_count = db_session.query(Job).filter(
|
||||
Job.printer_id == printer.id,
|
||||
Job.status.in_(['pending', 'printing', 'scheduled'])
|
||||
).count()
|
||||
|
||||
printer_data['active_jobs_count'] = active_jobs_count
|
||||
printer_data['has_active_jobs'] = active_jobs_count > 0
|
||||
|
||||
enriched_printers.append(printer_data)
|
||||
# Passwort hashen
|
||||
new_user.set_password(password)
|
||||
|
||||
# Grundlegende Statistiken sammeln
|
||||
total_users = db_session.query(User).count()
|
||||
total_printers = len(printers)
|
||||
total_jobs = db_session.query(Job).count()
|
||||
db_session.add(new_user)
|
||||
db_session.commit()
|
||||
|
||||
# Aktive Jobs zählen
|
||||
active_jobs = db_session.query(Job).filter(
|
||||
Job.status.in_(['pending', 'printing', 'paused'])
|
||||
).count()
|
||||
|
||||
stats = {
|
||||
'total_users': total_users,
|
||||
'total_printers': total_printers,
|
||||
'total_jobs': total_jobs,
|
||||
'active_jobs': active_jobs,
|
||||
'online_printers': online_count
|
||||
}
|
||||
|
||||
admin_logger.info(f"Druckerübersicht geladen von {current_user.username}: {total_printers} Drucker, {online_count} online")
|
||||
return render_template('admin.html', stats=stats, printers=enriched_printers, active_tab='printers')
|
||||
admin_logger.info(f"Neuer Benutzer '{username}' erstellt von {current_user.username}")
|
||||
flash(f"Benutzer '{username}' erfolgreich erstellt", "success")
|
||||
|
||||
return redirect(url_for('admin.users_overview'))
|
||||
|
||||
except Exception as e:
|
||||
admin_logger.error(f"Fehler beim Laden der Druckerübersicht: {str(e)}")
|
||||
flash("Fehler beim Laden der Druckerdaten", "error")
|
||||
return render_template('admin.html', stats={}, printers=[], active_tab='printers')
|
||||
admin_logger.error(f"Fehler beim Erstellen des Benutzers: {str(e)}")
|
||||
flash("Fehler beim Erstellen des Benutzers", "error")
|
||||
return redirect(url_for('admin.add_user_page'))
|
||||
|
||||
@admin_blueprint.route("/users/<int:user_id>/update", methods=["POST"])
|
||||
@admin_required
|
||||
def update_user(user_id):
|
||||
"""Aktualisiert einen vorhandenen Benutzer"""
|
||||
try:
|
||||
with get_cached_session() as db_session:
|
||||
user = db_session.query(User).filter(User.id == user_id).first()
|
||||
|
||||
if not user:
|
||||
flash("Benutzer nicht gefunden", "error")
|
||||
return redirect(url_for('admin.users_overview'))
|
||||
|
||||
# Aktualisierung verhindern wenn es der einzige Admin ist
|
||||
if user.is_admin and request.form.get('role') != 'admin':
|
||||
admin_count = db_session.query(User).filter(User.role == 'admin').count()
|
||||
if admin_count <= 1:
|
||||
flash("Kann den letzten Administrator nicht degradieren", "error")
|
||||
return redirect(url_for('admin.edit_user_page', user_id=user_id))
|
||||
|
||||
# Form-Daten abrufen
|
||||
username = request.form.get('username', '').strip()
|
||||
email = request.form.get('email', '').strip()
|
||||
name = request.form.get('name', '').strip()
|
||||
role = request.form.get('role', 'user')
|
||||
department = request.form.get('department', '').strip()
|
||||
position = request.form.get('position', '').strip()
|
||||
phone = request.form.get('phone', '').strip()
|
||||
active = request.form.get('active') == 'on'
|
||||
new_password = request.form.get('new_password', '').strip()
|
||||
|
||||
# Validierung
|
||||
if not username or not email:
|
||||
flash("Benutzername und E-Mail sind erforderlich", "error")
|
||||
return redirect(url_for('admin.edit_user_page', user_id=user_id))
|
||||
|
||||
# Prüfen ob Username/Email bereits von anderem Benutzer verwendet
|
||||
existing_user = db_session.query(User).filter(
|
||||
User.id != user_id,
|
||||
(User.username == username) | (User.email == email)
|
||||
).first()
|
||||
|
||||
if existing_user:
|
||||
flash("Benutzername oder E-Mail bereits von anderem Benutzer verwendet", "error")
|
||||
return redirect(url_for('admin.edit_user_page', user_id=user_id))
|
||||
|
||||
# Benutzer-Daten aktualisieren
|
||||
user.username = username
|
||||
user.email = email
|
||||
user.name = name if name else None
|
||||
user.role = role
|
||||
user.department = department if department else None
|
||||
user.position = position if position else None
|
||||
user.phone = phone if phone else None
|
||||
user.active = active
|
||||
user.updated_at = datetime.now()
|
||||
|
||||
# Passwort aktualisieren falls angegeben
|
||||
if new_password:
|
||||
user.set_password(new_password)
|
||||
|
||||
db_session.commit()
|
||||
|
||||
admin_logger.info(f"Benutzer '{username}' (ID: {user_id}) aktualisiert von {current_user.username}")
|
||||
flash(f"Benutzer '{username}' erfolgreich aktualisiert", "success")
|
||||
|
||||
return redirect(url_for('admin.users_overview'))
|
||||
|
||||
except Exception as e:
|
||||
admin_logger.error(f"Fehler beim Aktualisieren des Benutzers: {str(e)}")
|
||||
flash("Fehler beim Aktualisieren des Benutzers", "error")
|
||||
return redirect(url_for('admin.edit_user_page', user_id=user_id))
|
||||
|
||||
@admin_blueprint.route("/users/<int:user_id>/delete", methods=["POST"])
|
||||
@admin_required
|
||||
def delete_user(user_id):
|
||||
"""Löscht einen Benutzer"""
|
||||
try:
|
||||
with get_cached_session() as db_session:
|
||||
user = db_session.query(User).filter(User.id == user_id).first()
|
||||
|
||||
if not user:
|
||||
flash("Benutzer nicht gefunden", "error")
|
||||
return redirect(url_for('admin.users_overview'))
|
||||
|
||||
# Sich selbst löschen verhindern
|
||||
if user.id == current_user.id:
|
||||
flash("Sie können sich nicht selbst löschen", "error")
|
||||
return redirect(url_for('admin.users_overview'))
|
||||
|
||||
# Letzten Admin löschen verhindern
|
||||
if user.is_admin:
|
||||
admin_count = db_session.query(User).filter(User.role == 'admin').count()
|
||||
if admin_count <= 1:
|
||||
flash("Kann den letzten Administrator nicht löschen", "error")
|
||||
return redirect(url_for('admin.users_overview'))
|
||||
|
||||
username = user.username
|
||||
|
||||
# Benutzer löschen
|
||||
db_session.delete(user)
|
||||
db_session.commit()
|
||||
|
||||
admin_logger.info(f"Benutzer '{username}' (ID: {user_id}) gelöscht von {current_user.username}")
|
||||
flash(f"Benutzer '{username}' erfolgreich gelöscht", "success")
|
||||
|
||||
return redirect(url_for('admin.users_overview'))
|
||||
|
||||
except Exception as e:
|
||||
admin_logger.error(f"Fehler beim Löschen des Benutzers: {str(e)}")
|
||||
flash("Fehler beim Löschen des Benutzers", "error")
|
||||
return redirect(url_for('admin.users_overview'))
|
||||
|
||||
@admin_blueprint.route("/printers/add")
|
||||
@admin_required
|
||||
@ -329,7 +413,7 @@ def add_printer_page():
|
||||
except Exception as e:
|
||||
admin_logger.error(f"Fehler beim Laden der Drucker-Hinzufügen-Seite: {str(e)}")
|
||||
flash("Fehler beim Laden der Seite", "error")
|
||||
return redirect(url_for('admin.printers_overview'))
|
||||
return redirect(url_for('admin.admin_dashboard'))
|
||||
|
||||
@admin_blueprint.route("/printers/<int:printer_id>/edit")
|
||||
@admin_required
|
||||
@ -343,7 +427,7 @@ def edit_printer_page(printer_id):
|
||||
|
||||
if not printer:
|
||||
flash("Drucker nicht gefunden", "error")
|
||||
return redirect(url_for('admin.printers_overview'))
|
||||
return redirect(url_for('admin.admin_dashboard'))
|
||||
|
||||
# Hardware-Status für diesen Drucker abrufen
|
||||
drucker_steuerung = get_drucker_steuerung()
|
||||
@ -408,7 +492,7 @@ def edit_printer_page(printer_id):
|
||||
except Exception as e:
|
||||
admin_logger.error(f"Fehler beim Laden der Drucker-Bearbeitung: {str(e)}")
|
||||
flash("Fehler beim Laden der Druckerdaten", "error")
|
||||
return redirect(url_for('admin.printers_overview'))
|
||||
return redirect(url_for('admin.admin_dashboard'))
|
||||
|
||||
@admin_blueprint.route("/printers/<int:printer_id>/manage")
|
||||
@admin_required
|
||||
@ -422,7 +506,7 @@ def manage_printer_page(printer_id):
|
||||
|
||||
if not printer:
|
||||
flash("Drucker nicht gefunden", "error")
|
||||
return redirect(url_for('admin.printers_overview'))
|
||||
return redirect(url_for('admin.admin_dashboard'))
|
||||
|
||||
# Hardware-Steuerung für Echtzeit-Daten
|
||||
drucker_steuerung = get_drucker_steuerung()
|
||||
@ -524,7 +608,7 @@ def manage_printer_page(printer_id):
|
||||
except Exception as e:
|
||||
admin_logger.error(f"Fehler beim Laden der Drucker-Verwaltung: {str(e)}")
|
||||
flash("Fehler beim Laden der Drucker-Verwaltung", "error")
|
||||
return redirect(url_for('admin.printers_overview'))
|
||||
return redirect(url_for('admin.admin_dashboard'))
|
||||
|
||||
@admin_blueprint.route("/printers/<int:printer_id>/settings")
|
||||
@admin_required
|
||||
@ -538,7 +622,7 @@ def printer_settings_page(printer_id):
|
||||
|
||||
if not printer:
|
||||
flash("Drucker nicht gefunden", "error")
|
||||
return redirect(url_for('admin.printers_overview'))
|
||||
return redirect(url_for('admin.admin_dashboard'))
|
||||
|
||||
# Hardware-Status für erweiterte Einstellungen
|
||||
drucker_steuerung = get_drucker_steuerung()
|
||||
@ -604,7 +688,137 @@ def printer_settings_page(printer_id):
|
||||
except Exception as e:
|
||||
admin_logger.error(f"Fehler beim Laden der Drucker-Einstellungen: {str(e)}")
|
||||
flash("Fehler beim Laden der Drucker-Einstellungen", "error")
|
||||
return redirect(url_for('admin.printers_overview'))
|
||||
return redirect(url_for('admin.admin_dashboard'))
|
||||
|
||||
@admin_blueprint.route("/printers/<int:printer_id>/configure")
|
||||
@admin_required
|
||||
def printer_configuration_page(printer_id):
|
||||
"""Erweiterte Drucker-Konfigurationsseite - entspricht showPrinterConfiguration() JavaScript-Funktion"""
|
||||
try:
|
||||
from utils.hardware_integration import get_drucker_steuerung
|
||||
|
||||
with get_cached_session() as db_session:
|
||||
printer = db_session.query(Printer).filter(Printer.id == printer_id).first()
|
||||
|
||||
if not printer:
|
||||
flash("Drucker nicht gefunden", "error")
|
||||
return redirect(url_for('admin.admin_dashboard'))
|
||||
|
||||
# Hardware-Status für erweiterte Konfiguration
|
||||
drucker_steuerung = get_drucker_steuerung()
|
||||
status_data = drucker_steuerung.template_daten_sammeln()
|
||||
|
||||
# Umfassende Konfigurationsdaten sammeln
|
||||
config_data = {
|
||||
# Grunddaten des Druckers
|
||||
'printer': {
|
||||
'id': printer.id,
|
||||
'name': printer.name,
|
||||
'model': printer.model,
|
||||
'location': printer.location,
|
||||
'ip_address': printer.ip_address,
|
||||
'plug_ip': printer.plug_ip,
|
||||
'status': printer.status,
|
||||
'active': printer.active,
|
||||
'created_at': printer.created_at,
|
||||
'updated_at': printer.updated_at,
|
||||
'last_checked': printer.last_checked,
|
||||
'description': getattr(printer, 'description', ''),
|
||||
},
|
||||
|
||||
# Hardware-Status und Energiemonitoring
|
||||
'hardware_status': status_data.get('drucker_status', {}).get(printer.id, {}),
|
||||
|
||||
# Konfigurationskategorien
|
||||
'config_categories': {
|
||||
'basic': {
|
||||
'name': 'Grundeinstellungen',
|
||||
'description': 'Name, Modell, Standort',
|
||||
'icon': 'cog'
|
||||
},
|
||||
'network': {
|
||||
'name': 'Netzwerk-Konfiguration',
|
||||
'description': 'IP-Adressen, Verbindungseinstellungen',
|
||||
'icon': 'wifi'
|
||||
},
|
||||
'hardware': {
|
||||
'name': 'Hardware-Integration',
|
||||
'description': 'Smart-Plug, Sensoren, Monitoring',
|
||||
'icon': 'cpu'
|
||||
},
|
||||
'automation': {
|
||||
'name': 'Automatisierung',
|
||||
'description': 'Auto-Power, Zeitpläne, Benachrichtigungen',
|
||||
'icon': 'zap'
|
||||
},
|
||||
'maintenance': {
|
||||
'name': 'Wartung & Diagnostik',
|
||||
'description': 'Kalibrierung, Tests, Logs',
|
||||
'icon': 'tool'
|
||||
},
|
||||
'advanced': {
|
||||
'name': 'Erweiterte Optionen',
|
||||
'description': 'Experten-Einstellungen, Debug-Modi',
|
||||
'icon': 'settings'
|
||||
}
|
||||
},
|
||||
|
||||
# Live-Verbindungsstatus
|
||||
'connectivity': {
|
||||
'printer_reachable': False,
|
||||
'plug_reachable': False,
|
||||
'last_ping': None,
|
||||
'response_time': None
|
||||
},
|
||||
|
||||
# Energieverbrauchshistorie
|
||||
'energy_history': [],
|
||||
|
||||
# Verfügbare Aktionen
|
||||
'available_actions': [
|
||||
'test_connection',
|
||||
'reset_settings',
|
||||
'calibrate',
|
||||
'factory_reset',
|
||||
'export_config',
|
||||
'import_config'
|
||||
]
|
||||
}
|
||||
|
||||
# Live-Konnektivitätstests
|
||||
try:
|
||||
if printer.plug_ip:
|
||||
reachable, plug_status = drucker_steuerung.check_outlet_status(printer.plug_ip, printer_id=printer.id)
|
||||
config_data['connectivity']['plug_reachable'] = reachable
|
||||
config_data['connectivity']['plug_status'] = plug_status
|
||||
except Exception as connectivity_error:
|
||||
admin_logger.warning(f"Konnektivitätstest für Drucker {printer.id} fehlgeschlagen: {str(connectivity_error)}")
|
||||
|
||||
# Grundlegende Statistiken für das Template
|
||||
total_users = db_session.query(User).count()
|
||||
total_printers = db_session.query(Printer).count()
|
||||
total_jobs = db_session.query(Job).count()
|
||||
active_jobs = db_session.query(Job).filter(
|
||||
Job.status.in_(['pending', 'printing', 'paused'])
|
||||
).count()
|
||||
|
||||
stats = {
|
||||
'total_users': total_users,
|
||||
'total_printers': total_printers,
|
||||
'total_jobs': total_jobs,
|
||||
'active_jobs': active_jobs
|
||||
}
|
||||
|
||||
admin_logger.info(f"Drucker-Konfiguration für '{printer.name}' aufgerufen von {current_user.username}")
|
||||
return render_template('admin_printer_configuration.html',
|
||||
config_data=config_data,
|
||||
stats=stats,
|
||||
active_tab='printers')
|
||||
|
||||
except Exception as e:
|
||||
admin_logger.error(f"Fehler beim Laden der Drucker-Konfiguration: {str(e)}")
|
||||
flash("Fehler beim Laden der Drucker-Konfiguration", "error")
|
||||
return redirect(url_for('admin.admin_dashboard'))
|
||||
|
||||
@admin_blueprint.route("/guest-requests")
|
||||
@admin_required
|
||||
@ -3839,3 +4053,202 @@ def get_users_api():
|
||||
admin_api_logger.error(f"Fehler beim Abrufen der Benutzer: {str(e)}")
|
||||
return jsonify({"error": "Fehler beim Abrufen der Benutzer"}), 500
|
||||
|
||||
# ===== SYSTEM ERROR RECOVERY ENDPOINTS =====
|
||||
|
||||
@admin_api_blueprint.route('/error-recovery/status', methods=['GET'])
|
||||
@admin_required
|
||||
def error_recovery_status():
|
||||
"""
|
||||
Gibt den Status des Error-Recovery-Systems zurück.
|
||||
|
||||
Returns:
|
||||
JSON: Error-Recovery-Status und verfügbare Aktionen
|
||||
"""
|
||||
try:
|
||||
admin_api_logger.debug(f"Error-Recovery-Status angefordert von {current_user.username}")
|
||||
|
||||
# Sammle System-Gesundheitsdaten
|
||||
with get_cached_session() as db_session:
|
||||
# Aktuelle Datenbankverbindung testen
|
||||
try:
|
||||
db_session.execute(text("SELECT 1"))
|
||||
db_health = True
|
||||
except Exception:
|
||||
db_health = False
|
||||
|
||||
# Anzahl laufender Jobs prüfen
|
||||
try:
|
||||
running_jobs = db_session.query(Job).filter(
|
||||
Job.status.in_(['printing', 'pending', 'paused'])
|
||||
).count()
|
||||
except Exception:
|
||||
running_jobs = 0
|
||||
|
||||
# Anzahl Drucker mit Problemen
|
||||
try:
|
||||
offline_printers = db_session.query(Printer).filter(
|
||||
Printer.status.in_(['offline', 'error', 'unknown'])
|
||||
).count()
|
||||
except Exception:
|
||||
offline_printers = 0
|
||||
|
||||
# Verfügbare Recovery-Aktionen bestimmen
|
||||
available_actions = []
|
||||
|
||||
if not db_health:
|
||||
available_actions.extend(['restart_db', 'repair_db'])
|
||||
|
||||
if running_jobs == 0:
|
||||
available_actions.append('restart_services')
|
||||
|
||||
if offline_printers > 0:
|
||||
available_actions.append('reconnect_printers')
|
||||
|
||||
# Immer verfügbar
|
||||
available_actions.extend(['clear_cache', 'check_logs', 'system_health'])
|
||||
|
||||
recovery_status = {
|
||||
'system_healthy': db_health and running_jobs == 0 and offline_printers == 0,
|
||||
'database_status': 'healthy' if db_health else 'error',
|
||||
'running_jobs': running_jobs,
|
||||
'offline_printers': offline_printers,
|
||||
'available_actions': available_actions,
|
||||
'last_check': datetime.now().isoformat()
|
||||
}
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'recovery_status': recovery_status
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
admin_api_logger.error(f"Fehler beim Abrufen des Error-Recovery-Status: {str(e)}")
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': f'Fehler beim Abrufen des Status: {str(e)}'
|
||||
}), 500
|
||||
|
||||
@admin_api_blueprint.route('/error-recovery/toggle', methods=['POST'])
|
||||
@admin_required
|
||||
def error_recovery_toggle():
|
||||
"""
|
||||
Schaltet Error-Recovery-Funktionen um oder führt Recovery-Aktionen durch.
|
||||
|
||||
Request JSON:
|
||||
action (str): Die auszuführende Aktion ('restart_db', 'clear_cache', etc.)
|
||||
|
||||
Returns:
|
||||
JSON: Erfolgsstatus und Details der durchgeführten Aktion
|
||||
"""
|
||||
try:
|
||||
data = request.get_json()
|
||||
action = data.get('action')
|
||||
|
||||
if not action:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': 'Keine Aktion angegeben'
|
||||
}), 400
|
||||
|
||||
admin_api_logger.info(f"Error-Recovery-Aktion '{action}' angefordert von {current_user.username}")
|
||||
|
||||
result = {
|
||||
'action': action,
|
||||
'performed_by': current_user.username,
|
||||
'timestamp': datetime.now().isoformat(),
|
||||
'details': {}
|
||||
}
|
||||
|
||||
# Führe angeforderte Aktion durch
|
||||
if action == 'clear_cache':
|
||||
# Cache leeren
|
||||
try:
|
||||
from utils.utilities_collection import clear_cache
|
||||
clear_cache()
|
||||
result['details']['cache_cleared'] = True
|
||||
result['message'] = 'Cache erfolgreich geleert'
|
||||
except Exception as e:
|
||||
result['details']['cache_error'] = str(e)
|
||||
result['message'] = f'Fehler beim Cache-Leeren: {str(e)}'
|
||||
|
||||
elif action == 'restart_db':
|
||||
# Datenbank-Verbindungen neu starten
|
||||
try:
|
||||
from models import engine
|
||||
engine.dispose() # Alle Verbindungen schließen
|
||||
result['details']['db_connections_reset'] = True
|
||||
result['message'] = 'Datenbankverbindungen erfolgreich neu gestartet'
|
||||
except Exception as e:
|
||||
result['details']['db_error'] = str(e)
|
||||
result['message'] = f'Fehler beim DB-Neustart: {str(e)}'
|
||||
|
||||
elif action == 'reconnect_printers':
|
||||
# Drucker-Verbindungen neu aufbauen
|
||||
try:
|
||||
with get_cached_session() as db_session:
|
||||
offline_printers = db_session.query(Printer).filter(
|
||||
Printer.status.in_(['offline', 'error', 'unknown'])
|
||||
).all()
|
||||
|
||||
reconnected = 0
|
||||
for printer in offline_printers:
|
||||
try:
|
||||
# Einfache Reconnect-Logic
|
||||
printer.status = 'idle'
|
||||
printer.last_checked = datetime.now()
|
||||
reconnected += 1
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
db_session.commit()
|
||||
result['details']['printers_reconnected'] = reconnected
|
||||
result['message'] = f'{reconnected} Drucker erfolgreich reconnected'
|
||||
except Exception as e:
|
||||
result['details']['reconnect_error'] = str(e)
|
||||
result['message'] = f'Fehler beim Drucker-Reconnect: {str(e)}'
|
||||
|
||||
elif action == 'system_health':
|
||||
# System-Health-Check durchführen
|
||||
try:
|
||||
health_check = {
|
||||
'memory_usage': 'OK',
|
||||
'disk_space': 'OK',
|
||||
'database_size': 'OK',
|
||||
'service_status': 'OK'
|
||||
}
|
||||
|
||||
# Einfache Checks
|
||||
try:
|
||||
import psutil
|
||||
memory_percent = psutil.virtual_memory().percent
|
||||
health_check['memory_usage'] = f'{memory_percent:.1f}%'
|
||||
|
||||
disk_percent = psutil.disk_usage('/').percent
|
||||
health_check['disk_space'] = f'{disk_percent:.1f}%'
|
||||
except ImportError:
|
||||
health_check['system_monitoring'] = 'psutil nicht verfügbar'
|
||||
|
||||
result['details']['health_check'] = health_check
|
||||
result['message'] = 'System-Health-Check durchgeführt'
|
||||
except Exception as e:
|
||||
result['details']['health_error'] = str(e)
|
||||
result['message'] = f'Fehler beim Health-Check: {str(e)}'
|
||||
|
||||
else:
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': f'Unbekannte Aktion: {action}'
|
||||
}), 400
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'result': result
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
admin_api_logger.error(f"Fehler bei Error-Recovery-Toggle: {str(e)}")
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': f'Systemfehler: {str(e)}'
|
||||
}), 500
|
||||
|
||||
|
BIN
backend/database/myp.db.backup_20250620_004637
Normal file
BIN
backend/database/myp.db.backup_20250620_004637
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user