📝 "Refactor admin UI components for improved consistency and performance" 🌈
This commit is contained in:
@ -837,6 +837,112 @@ def create_backup():
|
|||||||
'message': f'Fehler beim Erstellen des Backups: {str(e)}'
|
'message': f'Fehler beim Erstellen des Backups: {str(e)}'
|
||||||
}), 500
|
}), 500
|
||||||
|
|
||||||
|
@admin_api_blueprint.route('/printers/<int:printer_id>/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_api_blueprint.route('/database/optimize', methods=['POST'])
|
||||||
@admin_required
|
@admin_required
|
||||||
def optimize_database():
|
def optimize_database():
|
||||||
|
@ -1627,6 +1627,52 @@ def tapo_configuration_wizard():
|
|||||||
"error": f"Systemfehler: {str(e)}"
|
"error": f"Systemfehler: {str(e)}"
|
||||||
}), 500
|
}), 500
|
||||||
|
|
||||||
|
@printers_blueprint.route("/<int:printer_id>/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/<int:printer_id>", methods=["POST"])
|
@printers_blueprint.route("/tapo/validate-configuration/<int:printer_id>", methods=["POST"])
|
||||||
@login_required
|
@login_required
|
||||||
@require_permission(Permission.ADMIN)
|
@require_permission(Permission.ADMIN)
|
||||||
|
@ -897,7 +897,17 @@ class AdminDashboard {
|
|||||||
// Error-Management
|
// Error-Management
|
||||||
async checkSystemHealth() {
|
async checkSystemHealth() {
|
||||||
try {
|
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();
|
const data = await response.json();
|
||||||
|
|
||||||
if (data.success) {
|
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);
|
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 Status:', response.status);
|
||||||
console.log('📡 Response Headers:', Object.fromEntries(response.headers.entries()));
|
console.log('📡 Response Headers:', Object.fromEntries(response.headers.entries()));
|
||||||
@ -1062,7 +1072,7 @@ class AdminDashboard {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const filter = level || document.getElementById('log-level-filter')?.value || 'all';
|
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, {
|
const response = await fetch(url, {
|
||||||
headers: {
|
headers: {
|
||||||
@ -1205,7 +1215,7 @@ class AdminDashboard {
|
|||||||
this.showNotification('📥 Logs werden exportiert...', 'info');
|
this.showNotification('📥 Logs werden exportiert...', 'info');
|
||||||
|
|
||||||
const filter = document.getElementById('log-level-filter')?.value || 'all';
|
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, {
|
const response = await fetch(url, {
|
||||||
method: 'POST',
|
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);
|
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 Status:', response.status);
|
||||||
console.log('📡 TEST Response Headers:', Object.fromEntries(response.headers.entries()));
|
console.log('📡 TEST Response Headers:', Object.fromEntries(response.headers.entries()));
|
||||||
|
@ -4,6 +4,28 @@
|
|||||||
let dashboardData = {};
|
let dashboardData = {};
|
||||||
let updateInterval;
|
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
|
// DOM-Elemente
|
||||||
const elements = {
|
const elements = {
|
||||||
activeJobs: null,
|
activeJobs: null,
|
||||||
|
@ -16,6 +16,9 @@ class PrinterMonitor {
|
|||||||
this.errorCount = 0;
|
this.errorCount = 0;
|
||||||
this.maxErrors = 3;
|
this.maxErrors = 3;
|
||||||
|
|
||||||
|
// API Base URL Detection für verschiedene Umgebungen
|
||||||
|
this.apiBaseUrl = this.detectApiBaseUrl();
|
||||||
|
|
||||||
// Status-Kategorien für bessere Übersicht
|
// Status-Kategorien für bessere Übersicht
|
||||||
this.statusCategories = {
|
this.statusCategories = {
|
||||||
'online': { label: 'Online', color: 'success', icon: '🟢' },
|
'online': { label: 'Online', color: 'success', icon: '🟢' },
|
||||||
@ -26,6 +29,29 @@ class PrinterMonitor {
|
|||||||
};
|
};
|
||||||
|
|
||||||
console.log('🖨️ PrinterMonitor initialisiert');
|
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 {
|
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, {
|
const response = await fetch(url, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
headers: {
|
headers: {
|
||||||
@ -309,7 +335,7 @@ class PrinterMonitor {
|
|||||||
*/
|
*/
|
||||||
async clearCache() {
|
async clearCache() {
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/api/printers/monitor/clear-cache', {
|
const response = await fetch(`${this.apiBaseUrl}/api/printers/monitor/clear-cache`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
@ -334,7 +360,7 @@ class PrinterMonitor {
|
|||||||
*/
|
*/
|
||||||
async getSummary() {
|
async getSummary() {
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/api/printers/monitor/summary', {
|
const response = await fetch(`${this.apiBaseUrl}/api/printers/monitor/summary`, {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
@ -432,7 +458,7 @@ class PrinterMonitor {
|
|||||||
try {
|
try {
|
||||||
console.log('🔄 Starte Force-Network-Refresh...');
|
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',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
@ -480,7 +506,7 @@ class PrinterMonitor {
|
|||||||
|
|
||||||
async initializeAllOutlets() {
|
async initializeAllOutlets() {
|
||||||
try {
|
try {
|
||||||
const response = await fetch('/api/printers/monitor/initialize-outlets', {
|
const response = await fetch(`${this.apiBaseUrl}/api/printers/monitor/initialize-outlets`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
|
Reference in New Issue
Block a user