From 4b2ff50f7a9fe77e8747b870bf7675d4a2dfd76e Mon Sep 17 00:00:00 2001 From: Till Tomczak Date: Mon, 16 Jun 2025 11:06:33 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=93=9D=20"Refactor=20admin=20UI=20compone?= =?UTF-8?q?nts=20for=20improved=20consistency=20and=20performance"=20?= =?UTF-8?q?=F0=9F=8C=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/blueprints/admin_unified.py | 106 +++++++++++++++++++++++++++ backend/blueprints/printers.py | 46 ++++++++++++ backend/static/js/admin-unified.js | 24 ++++-- backend/static/js/dashboard.js | 22 ++++++ backend/static/js/printer_monitor.js | 36 +++++++-- 5 files changed, 222 insertions(+), 12 deletions(-) diff --git a/backend/blueprints/admin_unified.py b/backend/blueprints/admin_unified.py index 0238d3ce3..d36be1dce 100644 --- a/backend/blueprints/admin_unified.py +++ b/backend/blueprints/admin_unified.py @@ -837,6 +837,112 @@ def create_backup(): 'message': f'Fehler beim Erstellen des Backups: {str(e)}' }), 500 +@admin_api_blueprint.route('/printers//toggle', methods=['POST']) +@admin_required +def toggle_printer_power(printer_id): + """ + Schaltet die Smart-Plug-Steckdose eines Druckers ein/aus (Toggle-Funktion). + + Args: + printer_id: ID des zu steuernden Druckers + + JSON-Parameter: + - reason: Grund für die Schaltung (optional) + + Returns: + JSON mit Ergebnis der Toggle-Aktion + """ + admin_api_logger.info(f"🔌 Smart-Plug Toggle für Drucker {printer_id} von Admin {current_user.name}") + + try: + # Parameter auslesen + data = request.get_json() or {} + reason = data.get("reason", "Admin-Panel Toggle") + + # Drucker aus Datenbank holen + db_session = get_cached_session() + printer = db_session.query(Printer).filter(Printer.id == printer_id).first() + + if not printer: + return jsonify({ + "success": False, + "error": f"Drucker mit ID {printer_id} nicht gefunden" + }), 404 + + # Prüfen, ob Drucker eine Steckdose konfiguriert hat + if not printer.plug_ip or not printer.plug_username or not printer.plug_password: + return jsonify({ + "success": False, + "error": f"Drucker {printer.name} hat keine Steckdose konfiguriert" + }), 400 + + # Aktuellen Status der Steckdose ermitteln + try: + from PyP100 import PyP110 + p110 = PyP110.P110(printer.plug_ip, printer.plug_username, printer.plug_password) + p110.handshake() + p110.login() + + # Aktuellen Status abrufen + device_info = p110.getDeviceInfo() + current_status = device_info["result"]["device_on"] + + # Toggle-Aktion durchführen + if current_status: + # Ausschalten + p110.turnOff() + new_status = "off" + action = "ausgeschaltet" + printer.status = "offline" + else: + # Einschalten + p110.turnOn() + new_status = "on" + action = "eingeschaltet" + printer.status = "starting" + + # Drucker-Status in DB aktualisieren + printer.last_checked = datetime.now() + db_session.commit() + + admin_api_logger.info(f"✅ Drucker {printer.name} erfolgreich {action} | Grund: {reason}") + + return jsonify({ + "success": True, + "message": f"Drucker {printer.name} erfolgreich {action}", + "printer": { + "id": printer_id, + "name": printer.name, + "model": printer.model, + "location": printer.location + }, + "toggle_result": { + "previous_status": "on" if current_status else "off", + "new_status": new_status, + "action": action, + "reason": reason + }, + "performed_by": { + "id": current_user.id, + "name": current_user.name + }, + "timestamp": datetime.now().isoformat() + }) + + except Exception as tapo_error: + admin_api_logger.error(f"❌ Tapo-Fehler für Drucker {printer.name}: {str(tapo_error)}") + return jsonify({ + "success": False, + "error": f"Fehler bei Steckdosensteuerung: {str(tapo_error)}" + }), 500 + + except Exception as e: + admin_api_logger.error(f"❌ Allgemeiner Fehler bei Toggle-Aktion: {str(e)}") + return jsonify({ + "success": False, + "error": f"Systemfehler: {str(e)}" + }), 500 + @admin_api_blueprint.route('/database/optimize', methods=['POST']) @admin_required def optimize_database(): diff --git a/backend/blueprints/printers.py b/backend/blueprints/printers.py index bafad2412..d5144dbf3 100644 --- a/backend/blueprints/printers.py +++ b/backend/blueprints/printers.py @@ -1627,6 +1627,52 @@ def tapo_configuration_wizard(): "error": f"Systemfehler: {str(e)}" }), 500 +@printers_blueprint.route("//connect", methods=["POST"]) +@login_required +@require_permission(Permission.CONTROL_PRINTER) +@measure_execution_time(logger=printers_logger, task_name="API-Drucker-Verbindung") +def connect_printer(printer_id): + """ + Verbindet einen Drucker (schaltet die Steckdose ein). + Wrapper für control_printer_power mit action='on' + + Args: + printer_id: ID des zu verbindenden Druckers + + Returns: + JSON mit Ergebnis der Verbindungsaktion + """ + printers_logger.info(f"🔗 Drucker-Verbindung für Drucker {printer_id} von Benutzer {current_user.name}") + + try: + # Fake JSON für control_printer_power + original_json = request.get_json() + request._cached_json = ({"action": "on"}, True) + + # Delegiere an existing control_printer_power function + result = control_printer_power(printer_id) + + # Response für connect-API anpassen + if hasattr(result, 'get_json') and result.get_json().get('success'): + data = result.get_json() + return jsonify({ + "success": True, + "message": f"Verbindung zu Drucker {printer_id} hergestellt", + "printer_id": printer_id, + "printer_name": data.get('printer_name'), + "action": "connect", + "timestamp": data.get('timestamp') + }) + + return result + + except Exception as e: + printers_logger.error(f"❌ Fehler bei Drucker-Verbindung: {str(e)}") + return jsonify({ + "success": False, + "error": f"Verbindungsfehler: {str(e)}" + }), 500 + @printers_blueprint.route("/tapo/validate-configuration/", methods=["POST"]) @login_required @require_permission(Permission.ADMIN) diff --git a/backend/static/js/admin-unified.js b/backend/static/js/admin-unified.js index 6d0eb5ce1..e7f08010f 100644 --- a/backend/static/js/admin-unified.js +++ b/backend/static/js/admin-unified.js @@ -897,7 +897,17 @@ class AdminDashboard { // Error-Management async checkSystemHealth() { try { - const response = await fetch('/api/admin/system-health'); + const response = await fetch(`${this.apiBaseUrl}/api/admin/system/status`); + if (!response.ok) { + throw new Error(`HTTP ${response.status}: ${response.statusText}`); + } + + // Prüfe Content-Type vor JSON parsing + const contentType = response.headers.get('content-type'); + if (!contentType || !contentType.includes('application/json')) { + throw new Error('Server returned non-JSON response'); + } + const data = await response.json(); if (data.success) { @@ -975,10 +985,10 @@ class AdminDashboard { } }; - console.log('📡 Sende Request an:', '/api/admin/fix-errors'); + console.log('📡 Sende Request an:', `${this.apiBaseUrl}/api/admin/fix-errors`); console.log('📝 Request Headers:', requestOptions.headers); - const response = await fetch('/api/admin/fix-errors', requestOptions); + const response = await fetch(`${this.apiBaseUrl}/api/admin/fix-errors`, requestOptions); console.log('📡 Response Status:', response.status); console.log('📡 Response Headers:', Object.fromEntries(response.headers.entries())); @@ -1062,7 +1072,7 @@ class AdminDashboard { try { const filter = level || document.getElementById('log-level-filter')?.value || 'all'; - const url = `${this.apiBaseUrl}/admin/api/logs?level=${filter}&limit=100`; + const url = `${this.apiBaseUrl}/api/admin/logs?level=${filter}&limit=100`; const response = await fetch(url, { headers: { @@ -1205,7 +1215,7 @@ class AdminDashboard { this.showNotification('📥 Logs werden exportiert...', 'info'); const filter = document.getElementById('log-level-filter')?.value || 'all'; - const url = `${this.apiBaseUrl}/admin/api/logs/export`; + const url = `${this.apiBaseUrl}/api/admin/logs/export`; const response = await fetch(url, { method: 'POST', @@ -1313,10 +1323,10 @@ class AdminDashboard { } }; - console.log('📡 TEST Request an:', '/api/admin/fix-errors'); + console.log('📡 TEST Request an:', `${this.apiBaseUrl}/api/admin/fix-errors`); console.log('📝 TEST Headers:', requestOptions.headers); - const response = await fetch('/api/admin/fix-errors', requestOptions); + const response = await fetch(`${this.apiBaseUrl}/api/admin/fix-errors`, requestOptions); console.log('📡 TEST Response Status:', response.status); console.log('📡 TEST Response Headers:', Object.fromEntries(response.headers.entries())); diff --git a/backend/static/js/dashboard.js b/backend/static/js/dashboard.js index f0e353852..c2089ae4c 100644 --- a/backend/static/js/dashboard.js +++ b/backend/static/js/dashboard.js @@ -4,6 +4,28 @@ let dashboardData = {}; let updateInterval; +// API Base URL Detection +function detectApiBaseUrl() { + const currentPort = window.location.port; + const currentProtocol = window.location.protocol; + const currentHost = window.location.hostname; + + // Development-Umgebung (Port 5000) + if (currentPort === '5000') { + return `${currentProtocol}//${currentHost}:${currentPort}`; + } + + // Production-Umgebung (Port 443 oder kein Port) + if (currentPort === '443' || currentPort === '') { + return `${currentProtocol}//${currentHost}`; + } + + // Fallback für andere Ports + return window.location.origin; +} + +const API_BASE_URL = detectApiBaseUrl(); + // DOM-Elemente const elements = { activeJobs: null, diff --git a/backend/static/js/printer_monitor.js b/backend/static/js/printer_monitor.js index 9b5f80445..2e7d0d23c 100644 --- a/backend/static/js/printer_monitor.js +++ b/backend/static/js/printer_monitor.js @@ -16,6 +16,9 @@ class PrinterMonitor { this.errorCount = 0; this.maxErrors = 3; + // API Base URL Detection für verschiedene Umgebungen + this.apiBaseUrl = this.detectApiBaseUrl(); + // Status-Kategorien für bessere Übersicht this.statusCategories = { 'online': { label: 'Online', color: 'success', icon: '🟢' }, @@ -26,6 +29,29 @@ class PrinterMonitor { }; console.log('🖨️ PrinterMonitor initialisiert'); + console.log('🌐 API Base URL:', this.apiBaseUrl); + } + + /** + * Erkennt die korrekte API Base URL basierend auf der aktuellen Umgebung + */ + detectApiBaseUrl() { + const currentPort = window.location.port; + const currentProtocol = window.location.protocol; + const currentHost = window.location.hostname; + + // Development-Umgebung (Port 5000) + if (currentPort === '5000') { + return `${currentProtocol}//${currentHost}:${currentPort}`; + } + + // Production-Umgebung (Port 443 oder kein Port) + if (currentPort === '443' || currentPort === '') { + return `${currentProtocol}//${currentHost}`; + } + + // Fallback für andere Ports + return window.location.origin; } /** @@ -113,7 +139,7 @@ class PrinterMonitor { } try { - const url = `/api/printers/status${forceRefresh ? '?force_refresh=true' : ''}`; + const url = `${this.apiBaseUrl}/api/printers/status${forceRefresh ? '?force_refresh=true' : ''}`; const response = await fetch(url, { method: 'GET', headers: { @@ -309,7 +335,7 @@ class PrinterMonitor { */ async clearCache() { try { - const response = await fetch('/api/printers/monitor/clear-cache', { + const response = await fetch(`${this.apiBaseUrl}/api/printers/monitor/clear-cache`, { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -334,7 +360,7 @@ class PrinterMonitor { */ async getSummary() { try { - const response = await fetch('/api/printers/monitor/summary', { + const response = await fetch(`${this.apiBaseUrl}/api/printers/monitor/summary`, { method: 'GET', headers: { 'Content-Type': 'application/json', @@ -432,7 +458,7 @@ class PrinterMonitor { try { console.log('🔄 Starte Force-Network-Refresh...'); - const response = await fetch('/api/printers/force-refresh', { + const response = await fetch(`${this.apiBaseUrl}/api/printers/force-refresh`, { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -480,7 +506,7 @@ class PrinterMonitor { async initializeAllOutlets() { try { - const response = await fetch('/api/printers/monitor/initialize-outlets', { + const response = await fetch(`${this.apiBaseUrl}/api/printers/monitor/initialize-outlets`, { method: 'POST', headers: { 'Content-Type': 'application/json',