""" Drucker-Steuerungs-Blueprint - BACKEND DIKTIERT FRONTEND ======================================================= NEUE PHILOSOPHIE - VOLLSTÄNDIGE Backend-Kontrolle: - Drucker werden NUR über Tapo-Steckdosen gesteuert - KEIN JavaScript - nur Flask/Jinja Templates - Backend sammelt ALLE Daten und übergibt sie an Templates - Frontend zeigt nur an, was Backend vorschreibt Autor: Till Tomczak - Mercedes-Benz TBA Marienfelde Datum: 2025-06-19 (Komplett-Neuschreibung für Backend-Kontrolle) """ from flask import Blueprint, render_template, request, redirect, url_for, flash from flask_login import login_required, current_user from datetime import datetime from utils.hardware_integration import get_drucker_steuerung from utils.logging_config import get_logger from utils.security_suite import require_permission, Permission # Logger drucker_logger = get_logger("drucker_steuerung") # Blueprint erstellen drucker_blueprint = Blueprint("drucker", __name__, url_prefix="/drucker") # ===== FRONTEND-ROUTEN (Nur Templates) ===== @drucker_blueprint.route("/") @login_required def drucker_uebersicht(): """ Drucker-Übersichtsseite - Backend sammelt ALLE Daten für Frontend. KEIN JavaScript - alles über Flask/Jinja! """ drucker_logger.info(f"🖨️ Drucker-Übersicht aufgerufen von {current_user.name}") try: # Hardware-Integration holt ALLE Daten drucker_steuerung = get_drucker_steuerung() template_daten = drucker_steuerung.template_daten_sammeln() # Zusätzliche UI-Daten ui_daten = { 'seiten_titel': 'Drucker-Verwaltung', 'benutzer_kann_steuern': current_user.is_admin or hasattr(current_user, 'can_control_printers'), 'letztes_update': datetime.now().strftime('%d.%m.%Y %H:%M:%S'), 'refresh_url': url_for('drucker.drucker_uebersicht'), } # Alle Daten an Template übergeben return render_template( 'drucker_steuerung.html', **template_daten, **ui_daten ) except Exception as e: drucker_logger.error(f"❌ Fehler beim Laden der Drucker-Übersicht: {e}") flash(f'Fehler beim Laden der Drucker-Daten: {str(e)}', 'error') # Fallback-Template mit Fehlermeldung return render_template( 'drucker_steuerung.html', drucker=[], stats={'gesamt_drucker': 0, 'online_drucker': 0, 'offline_drucker': 0}, system_status='error', error=str(e), seiten_titel='Drucker-Verwaltung (Fehler)', benutzer_kann_steuern=False ) @drucker_blueprint.route("/details/") @login_required def drucker_details(drucker_id): """ Detailansicht für einen einzelnen Drucker. Alle Daten kommen vom Backend - Frontend ist passiv! """ drucker_logger.info(f"🔍 Drucker-Details {drucker_id} aufgerufen von {current_user.name}") try: # Hardware-Integration holt alle Drucker-Daten drucker_steuerung = get_drucker_steuerung() alle_daten = drucker_steuerung.template_daten_sammeln() # Spezifischen Drucker finden drucker_info = None for drucker in alle_daten.get('drucker', []): if drucker['id'] == drucker_id: drucker_info = drucker break if not drucker_info: flash(f'Drucker {drucker_id} nicht gefunden', 'error') return redirect(url_for('drucker.drucker_uebersicht')) # Zusätzliche Detail-Daten vom Backend detail_daten = { 'seiten_titel': f'Details - {drucker_info["name"]}', 'zurueck_url': url_for('drucker.drucker_uebersicht'), 'benutzer_kann_steuern': current_user.is_admin or hasattr(current_user, 'can_control_printers'), # Steuerungs-URLs (Backend-Routes) 'einschalten_url': url_for('drucker.drucker_einschalten', drucker_id=drucker_id), 'ausschalten_url': url_for('drucker.drucker_ausschalten', drucker_id=drucker_id), 'toggle_url': url_for('drucker.drucker_toggle', drucker_id=drucker_id), # Erweiterte Drucker-Infos 'laufzeit_heute': '3h 45m', # Mock-Daten 'jobs_heute': 2, 'letzter_job': 'vor 1 Stunde', 'geschätzte_kosten_monat': f"{drucker_info.get('monthly_consumption', 0) * 0.30:.2f} €" } return render_template( 'drucker_details.html', drucker=drucker_info, stats=alle_daten.get('stats', {}), **detail_daten ) except Exception as e: drucker_logger.error(f"❌ Fehler beim Laden der Drucker-Details {drucker_id}: {e}") flash(f'Fehler beim Laden der Drucker-Details: {str(e)}', 'error') return redirect(url_for('drucker.drucker_uebersicht')) # ===== STEUERUNGS-ROUTEN (Backend-Aktionen) ===== @drucker_blueprint.route("/einschalten/", methods=["POST"]) @login_required @require_permission(Permission.CONTROL_PRINTER) def drucker_einschalten(drucker_id): """ Schaltet einen Drucker über seine Tapo-Steckdose EIN. Komplett Backend-gesteuert - kein JavaScript! """ drucker_logger.info(f"🟢 Benutzer {current_user.name} schaltet Drucker {drucker_id} ein") try: # Hardware-Integration ausführen drucker_steuerung = get_drucker_steuerung() ergebnis = drucker_steuerung.drucker_einschalten( drucker_id, grund=f"Manuell von {current_user.name}" ) if ergebnis['success']: flash(ergebnis['message'], 'success') drucker_logger.info(f"✅ Drucker {drucker_id} erfolgreich eingeschaltet") else: flash(f"Fehler beim Einschalten: {ergebnis['error']}", 'error') drucker_logger.error(f"❌ Drucker {drucker_id} konnte nicht eingeschaltet werden: {ergebnis['error']}") except Exception as e: drucker_logger.error(f"❌ Exception beim Einschalten von Drucker {drucker_id}: {e}") flash(f'Technischer Fehler beim Einschalten: {str(e)}', 'error') # Zurück zur Übersicht (Backend-Redirect) return redirect(url_for('drucker.drucker_uebersicht')) @drucker_blueprint.route("/ausschalten/", methods=["POST"]) @login_required @require_permission(Permission.CONTROL_PRINTER) def drucker_ausschalten(drucker_id): """ Schaltet einen Drucker über seine Tapo-Steckdose AUS. Komplett Backend-gesteuert - kein JavaScript! """ drucker_logger.info(f"🔴 Benutzer {current_user.name} schaltet Drucker {drucker_id} aus") try: # Hardware-Integration ausführen drucker_steuerung = get_drucker_steuerung() ergebnis = drucker_steuerung.drucker_ausschalten( drucker_id, grund=f"Manuell von {current_user.name}" ) if ergebnis['success']: flash(ergebnis['message'], 'success') drucker_logger.info(f"✅ Drucker {drucker_id} erfolgreich ausgeschaltet") else: flash(f"Fehler beim Ausschalten: {ergebnis['error']}", 'error') drucker_logger.error(f"❌ Drucker {drucker_id} konnte nicht ausgeschaltet werden: {ergebnis['error']}") except Exception as e: drucker_logger.error(f"❌ Exception beim Ausschalten von Drucker {drucker_id}: {e}") flash(f'Technischer Fehler beim Ausschalten: {str(e)}', 'error') # Zurück zur Übersicht (Backend-Redirect) return redirect(url_for('drucker.drucker_uebersicht')) @drucker_blueprint.route("/toggle/", methods=["POST"]) @login_required @require_permission(Permission.CONTROL_PRINTER) def drucker_toggle(drucker_id): """ Wechselt den Status eines Druckers (Ein <-> Aus). Komplett Backend-gesteuert - kein JavaScript! """ drucker_logger.info(f"🔄 Benutzer {current_user.name} togglet Drucker {drucker_id}") try: # Hardware-Integration ausführen drucker_steuerung = get_drucker_steuerung() ergebnis = drucker_steuerung.drucker_toggle( drucker_id, grund=f"Toggle von {current_user.name}" ) if ergebnis['success']: flash(ergebnis['message'], 'success') drucker_logger.info(f"✅ Drucker {drucker_id} erfolgreich getoggled") else: flash(f"Fehler beim Umschalten: {ergebnis['error']}", 'error') drucker_logger.error(f"❌ Drucker {drucker_id} konnte nicht getoggled werden: {ergebnis['error']}") except Exception as e: drucker_logger.error(f"❌ Exception beim Toggle von Drucker {drucker_id}: {e}") flash(f'Technischer Fehler beim Umschalten: {str(e)}', 'error') # Zurück zur Übersicht (Backend-Redirect) return redirect(url_for('drucker.drucker_uebersicht')) @drucker_blueprint.route("/alle-ausschalten", methods=["POST"]) @login_required @require_permission(Permission.ADMIN) def alle_drucker_ausschalten(): """ Schaltet ALLE Drucker aus (Notfall-Funktion). Nur für Admins - komplett Backend-gesteuert! """ drucker_logger.warning(f"🚨 NOTFALL: Benutzer {current_user.name} schaltet ALLE Drucker aus") try: # Hardware-Integration holt alle Drucker drucker_steuerung = get_drucker_steuerung() alle_daten = drucker_steuerung.template_daten_sammeln() erfolge = 0 fehler = 0 # Jeden Drucker ausschalten for drucker in alle_daten.get('drucker', []): if drucker['status'] == 'online' and drucker['kann_gesteuert_werden']: ergebnis = drucker_steuerung.drucker_ausschalten( drucker['id'], grund=f"NOTFALL-SHUTDOWN von {current_user.name}" ) if ergebnis['success']: erfolge += 1 drucker_logger.info(f"✅ Notfall-Shutdown: {drucker['name']} ausgeschaltet") else: fehler += 1 drucker_logger.error(f"❌ Notfall-Shutdown: {drucker['name']} Fehler: {ergebnis['error']}") # Ergebnis-Meldung if fehler == 0: flash(f'Alle Drucker erfolgreich ausgeschaltet ({erfolge} Geräte)', 'success') else: flash(f'Teilweise erfolgreich: {erfolge} ausgeschaltet, {fehler} Fehler', 'warning') except Exception as e: drucker_logger.error(f"❌ Kritischer Fehler beim Notfall-Shutdown: {e}") flash(f'Kritischer Fehler beim Notfall-Shutdown: {str(e)}', 'error') # Zurück zur Übersicht return redirect(url_for('drucker.drucker_uebersicht')) @drucker_blueprint.route("/status-refresh") @login_required def status_refresh(): """ Aktualisiert alle Drucker-Status. Backend sammelt neue Daten und zeigt sie an - kein JavaScript! """ drucker_logger.info(f"🔄 Status-Refresh ausgeführt von {current_user.name}") try: # Hardware-Integration aktualisiert alle Status drucker_steuerung = get_drucker_steuerung() template_daten = drucker_steuerung.template_daten_sammeln() online_count = template_daten.get('stats', {}).get('online_drucker', 0) total_count = template_daten.get('stats', {}).get('gesamt_drucker', 0) flash(f'Status aktualisiert: {online_count}/{total_count} Drucker online', 'info') except Exception as e: drucker_logger.error(f"❌ Fehler beim Status-Refresh: {e}") flash(f'Fehler beim Aktualisieren: {str(e)}', 'error') # Zurück zur Übersicht mit aktualisierten Daten return redirect(url_for('drucker.drucker_uebersicht')) # ===== ERROR HANDLERS ===== @drucker_blueprint.errorhandler(403) def drucker_forbidden(e): """Error-Handler für fehlende Berechtigungen""" drucker_logger.warning(f"403 Forbidden in Drucker-Steuerung: {request.path} von User {current_user.name}") flash('Keine Berechtigung für Drucker-Steuerung', 'error') return redirect(url_for('dashboard')) @drucker_blueprint.errorhandler(404) def drucker_not_found(e): """Error-Handler für nicht gefundene Drucker""" drucker_logger.warning(f"404 Not Found in Drucker-Steuerung: {request.path}") flash('Drucker nicht gefunden', 'error') return redirect(url_for('drucker.drucker_uebersicht')) @drucker_blueprint.errorhandler(500) def drucker_server_error(e): """Error-Handler für Server-Fehler""" drucker_logger.error(f"500 Server Error in Drucker-Steuerung: {request.path} - {str(e)}") flash('Technischer Fehler in der Drucker-Steuerung', 'error') return redirect(url_for('drucker.drucker_uebersicht')) drucker_logger.info("🖨️ Drucker-Steuerungs-Blueprint (Backend-Kontrolle) geladen")