🎉 Feature: Enhanced log management & firewall integration in backend

This commit is contained in:
Till Tomczak 2025-06-01 13:54:05 +02:00
parent d6f00ab40d
commit 38202de49f
9 changed files with 799 additions and 118 deletions

Binary file not shown.

Binary file not shown.

View File

@ -1109,3 +1109,15 @@ WHERE users.id = ?
2025-06-01 13:42:45 - [app] app - [INFO] INFO - Admin-Check für Funktion api_admin_system_health: User authenticated: True, User ID: 1, Is Admin: True
2025-06-01 13:43:15 - [app] app - [INFO] INFO - Admin-Check für Funktion api_admin_system_health: User authenticated: True, User ID: 1, Is Admin: True
2025-06-01 13:43:45 - [app] app - [INFO] INFO - Admin-Check für Funktion api_admin_system_health: User authenticated: True, User ID: 1, Is Admin: True
2025-06-01 13:44:15 - [app] app - [INFO] INFO - Admin-Check für Funktion api_admin_system_health: User authenticated: True, User ID: 1, Is Admin: True
2025-06-01 13:44:45 - [app] app - [INFO] INFO - Admin-Check für Funktion api_admin_system_health: User authenticated: True, User ID: 1, Is Admin: True
2025-06-01 13:45:15 - [app] app - [INFO] INFO - Admin-Check für Funktion api_admin_system_health: User authenticated: True, User ID: 1, Is Admin: True
2025-06-01 13:45:45 - [app] app - [INFO] INFO - Admin-Check für Funktion api_admin_system_health: User authenticated: True, User ID: 1, Is Admin: True
2025-06-01 13:46:16 - [app] app - [INFO] INFO - Admin-Check für Funktion api_admin_system_health: User authenticated: True, User ID: 1, Is Admin: True
2025-06-01 13:46:46 - [app] app - [INFO] INFO - Admin-Check für Funktion api_admin_system_health: User authenticated: True, User ID: 1, Is Admin: True
2025-06-01 13:47:35 - [app] app - [INFO] INFO - Admin-Check für Funktion api_admin_system_health: User authenticated: True, User ID: 1, Is Admin: True
2025-06-01 13:48:17 - [app] app - [INFO] INFO - Admin-Check für Funktion api_admin_system_health: User authenticated: True, User ID: 1, Is Admin: True
2025-06-01 13:48:46 - [app] app - [INFO] INFO - Admin-Check für Funktion api_admin_system_health: User authenticated: True, User ID: 1, Is Admin: True
2025-06-01 13:49:16 - [app] app - [INFO] INFO - Admin-Check für Funktion api_admin_system_health: User authenticated: True, User ID: 1, Is Admin: True
2025-06-01 13:50:35 - [app] app - [INFO] INFO - Admin-Check für Funktion api_admin_system_health: User authenticated: True, User ID: 1, Is Admin: True
2025-06-01 13:51:35 - [app] app - [INFO] INFO - Admin-Check für Funktion api_admin_system_health: User authenticated: True, User ID: 1, Is Admin: True

View File

@ -1190,3 +1190,59 @@
2025-06-01 13:41:45 - [printer_monitor] printer_monitor - [INFO] INFO - Keine aktiven Drucker gefunden
2025-06-01 13:41:45 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus...
2025-06-01 13:41:45 - [printer_monitor] printer_monitor - [INFO] INFO - Keine aktiven Drucker gefunden
2025-06-01 13:42:15 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus...
2025-06-01 13:42:15 - [printer_monitor] printer_monitor - [INFO] INFO - Keine aktiven Drucker gefunden
2025-06-01 13:42:15 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus...
2025-06-01 13:42:15 - [printer_monitor] printer_monitor - [INFO] INFO - Keine aktiven Drucker gefunden
2025-06-01 13:42:45 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus...
2025-06-01 13:42:45 - [printer_monitor] printer_monitor - [INFO] INFO - Keine aktiven Drucker gefunden
2025-06-01 13:42:45 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus...
2025-06-01 13:42:45 - [printer_monitor] printer_monitor - [INFO] INFO - Keine aktiven Drucker gefunden
2025-06-01 13:43:15 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus...
2025-06-01 13:43:15 - [printer_monitor] printer_monitor - [INFO] INFO - Keine aktiven Drucker gefunden
2025-06-01 13:43:15 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus...
2025-06-01 13:43:15 - [printer_monitor] printer_monitor - [INFO] INFO - Keine aktiven Drucker gefunden
2025-06-01 13:43:45 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus...
2025-06-01 13:43:45 - [printer_monitor] printer_monitor - [INFO] INFO - Keine aktiven Drucker gefunden
2025-06-01 13:43:45 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus...
2025-06-01 13:43:45 - [printer_monitor] printer_monitor - [INFO] INFO - Keine aktiven Drucker gefunden
2025-06-01 13:44:15 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus...
2025-06-01 13:44:15 - [printer_monitor] printer_monitor - [INFO] INFO - Keine aktiven Drucker gefunden
2025-06-01 13:44:15 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus...
2025-06-01 13:44:15 - [printer_monitor] printer_monitor - [INFO] INFO - Keine aktiven Drucker gefunden
2025-06-01 13:44:45 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus...
2025-06-01 13:44:45 - [printer_monitor] printer_monitor - [INFO] INFO - Keine aktiven Drucker gefunden
2025-06-01 13:44:45 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus...
2025-06-01 13:44:45 - [printer_monitor] printer_monitor - [INFO] INFO - Keine aktiven Drucker gefunden
2025-06-01 13:45:15 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus...
2025-06-01 13:45:15 - [printer_monitor] printer_monitor - [INFO] INFO - Keine aktiven Drucker gefunden
2025-06-01 13:45:15 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus...
2025-06-01 13:45:15 - [printer_monitor] printer_monitor - [INFO] INFO - Keine aktiven Drucker gefunden
2025-06-01 13:45:45 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus...
2025-06-01 13:45:45 - [printer_monitor] printer_monitor - [INFO] INFO - Keine aktiven Drucker gefunden
2025-06-01 13:45:45 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus...
2025-06-01 13:45:45 - [printer_monitor] printer_monitor - [INFO] INFO - Keine aktiven Drucker gefunden
2025-06-01 13:46:54 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus...
2025-06-01 13:46:54 - [printer_monitor] printer_monitor - [INFO] INFO - Keine aktiven Drucker gefunden
2025-06-01 13:46:54 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus...
2025-06-01 13:46:54 - [printer_monitor] printer_monitor - [INFO] INFO - Keine aktiven Drucker gefunden
2025-06-01 13:47:54 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus...
2025-06-01 13:47:54 - [printer_monitor] printer_monitor - [INFO] INFO - Keine aktiven Drucker gefunden
2025-06-01 13:47:54 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus...
2025-06-01 13:47:54 - [printer_monitor] printer_monitor - [INFO] INFO - Keine aktiven Drucker gefunden
2025-06-01 13:48:17 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus...
2025-06-01 13:48:17 - [printer_monitor] printer_monitor - [INFO] INFO - Keine aktiven Drucker gefunden
2025-06-01 13:48:17 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus...
2025-06-01 13:48:17 - [printer_monitor] printer_monitor - [INFO] INFO - Keine aktiven Drucker gefunden
2025-06-01 13:49:19 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus...
2025-06-01 13:49:19 - [printer_monitor] printer_monitor - [INFO] INFO - Keine aktiven Drucker gefunden
2025-06-01 13:49:19 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus...
2025-06-01 13:49:19 - [printer_monitor] printer_monitor - [INFO] INFO - Keine aktiven Drucker gefunden
2025-06-01 13:50:19 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus...
2025-06-01 13:50:19 - [printer_monitor] printer_monitor - [INFO] INFO - Keine aktiven Drucker gefunden
2025-06-01 13:50:19 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus...
2025-06-01 13:50:19 - [printer_monitor] printer_monitor - [INFO] INFO - Keine aktiven Drucker gefunden
2025-06-01 13:51:19 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus...
2025-06-01 13:51:19 - [printer_monitor] printer_monitor - [INFO] INFO - Keine aktiven Drucker gefunden
2025-06-01 13:51:19 - [printer_monitor] printer_monitor - [INFO] INFO - 🔄 Aktualisiere Live-Druckerstatus...
2025-06-01 13:51:19 - [printer_monitor] printer_monitor - [INFO] INFO - Keine aktiven Drucker gefunden

View File

@ -3557,3 +3557,45 @@
2025-06-01 13:41:45 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1)
2025-06-01 13:41:45 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker
2025-06-01 13:41:45 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 7.69ms
2025-06-01 13:42:15 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1)
2025-06-01 13:42:15 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker
2025-06-01 13:42:15 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 4.32ms
2025-06-01 13:42:45 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1)
2025-06-01 13:42:45 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker
2025-06-01 13:42:45 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 5.26ms
2025-06-01 13:43:15 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1)
2025-06-01 13:43:15 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker
2025-06-01 13:43:15 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 3.90ms
2025-06-01 13:43:45 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1)
2025-06-01 13:43:45 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker
2025-06-01 13:43:45 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 5.10ms
2025-06-01 13:44:15 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1)
2025-06-01 13:44:15 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker
2025-06-01 13:44:15 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 6.97ms
2025-06-01 13:44:45 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1)
2025-06-01 13:44:45 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker
2025-06-01 13:44:45 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 7.08ms
2025-06-01 13:45:15 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1)
2025-06-01 13:45:15 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker
2025-06-01 13:45:15 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 1.55ms
2025-06-01 13:45:45 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1)
2025-06-01 13:45:45 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker
2025-06-01 13:45:45 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 5.65ms
2025-06-01 13:46:54 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1)
2025-06-01 13:46:54 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker
2025-06-01 13:46:54 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 1.22ms
2025-06-01 13:47:54 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1)
2025-06-01 13:47:54 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker
2025-06-01 13:47:54 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 1.36ms
2025-06-01 13:48:17 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1)
2025-06-01 13:48:17 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker
2025-06-01 13:48:17 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 4.13ms
2025-06-01 13:49:19 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1)
2025-06-01 13:49:19 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker
2025-06-01 13:49:19 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 1.31ms
2025-06-01 13:50:19 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1)
2025-06-01 13:50:19 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker
2025-06-01 13:50:19 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 1.11ms
2025-06-01 13:51:19 - [printers] printers - [INFO] INFO - 🔄 Live-Status-Abfrage von Benutzer Administrator (ID: 1)
2025-06-01 13:51:19 - [printers] printers - [INFO] INFO - ✅ Live-Status-Abfrage erfolgreich: 0 Drucker
2025-06-01 13:51:19 - [printers] printers - [INFO] INFO - ✅ API-Live-Drucker-Status-Abfrage 'get_live_printer_status' erfolgreich in 1.31ms

264
backend/maintenance_api.py Normal file
View File

@ -0,0 +1,264 @@
"""
Wartungs-API-Endpunkte für das MYP-System
Stellt API-Routen für Cache-Löschung, Datenbank-Optimierung und Backup-Erstellung bereit
"""
import os
import tempfile
import zipfile
import gc
from datetime import datetime, timedelta
from flask import jsonify, current_app
from flask_login import login_required, current_user
from sqlalchemy import text
# Import der notwendigen Module aus der Hauptanwendung
try:
from app import app, app_logger, admin_required, get_db_session
except ImportError:
# Fallback für den Fall, dass die Imports nicht verfügbar sind
app = None
app_logger = None
admin_required = None
get_db_session = None
def register_maintenance_routes(app_instance, logger, admin_decorator, db_session_func):
"""
Registriert die Wartungs-API-Routen bei der Flask-App
Args:
app_instance: Flask-App-Instanz
logger: Logger-Instanz
admin_decorator: Admin-Required-Decorator
db_session_func: Funktion zum Abrufen einer DB-Session
"""
@app_instance.route('/api/admin/maintenance/clear-cache', methods=['POST'])
@login_required
@admin_decorator
def api_clear_cache():
"""Leert den System-Cache"""
try:
logger.info(f"🧹 Cache-Löschung gestartet von Benutzer {current_user.username}")
# Flask-Cache leeren (falls vorhanden)
if hasattr(app_instance, 'cache'):
app_instance.cache.clear()
# Temporäre Dateien löschen
temp_dir = tempfile.gettempdir()
myp_temp_files = []
try:
for root, dirs, files in os.walk(temp_dir):
for file in files:
if 'myp_' in file.lower() or 'tba_' in file.lower():
file_path = os.path.join(root, file)
try:
os.remove(file_path)
myp_temp_files.append(file)
except:
pass
except Exception as e:
logger.warning(f"Fehler beim Löschen temporärer Dateien: {str(e)}")
# Python-Cache leeren
gc.collect()
logger.info(f"✅ Cache erfolgreich geleert. {len(myp_temp_files)} temporäre Dateien entfernt")
return jsonify({
'success': True,
'message': f'Cache erfolgreich geleert. {len(myp_temp_files)} temporäre Dateien entfernt.',
'details': {
'temp_files_removed': len(myp_temp_files),
'timestamp': datetime.now().isoformat()
}
})
except Exception as e:
logger.error(f"❌ Fehler beim Leeren des Cache: {str(e)}")
return jsonify({
'success': False,
'message': f'Fehler beim Leeren des Cache: {str(e)}'
}), 500
@app_instance.route('/api/admin/maintenance/optimize-database', methods=['POST'])
@login_required
@admin_decorator
def api_optimize_database():
"""Optimiert die Datenbank"""
db_session = db_session_func()
try:
logger.info(f"🔧 Datenbank-Optimierung gestartet von Benutzer {current_user.username}")
optimization_results = {
'tables_analyzed': 0,
'indexes_rebuilt': 0,
'space_freed_mb': 0,
'errors': []
}
# SQLite-spezifische Optimierungen
try:
# VACUUM - komprimiert die Datenbank
db_session.execute(text("VACUUM;"))
optimization_results['space_freed_mb'] += 1 # Geschätzt
# ANALYZE - aktualisiert Statistiken
db_session.execute(text("ANALYZE;"))
optimization_results['tables_analyzed'] += 1
# REINDEX - baut Indizes neu auf
db_session.execute(text("REINDEX;"))
optimization_results['indexes_rebuilt'] += 1
db_session.commit()
except Exception as e:
optimization_results['errors'].append(f"SQLite-Optimierung: {str(e)}")
logger.warning(f"Fehler bei SQLite-Optimierung: {str(e)}")
# Verwaiste Dateien bereinigen
try:
uploads_dir = os.path.join(app_instance.root_path, 'uploads')
if os.path.exists(uploads_dir):
orphaned_files = 0
for root, dirs, files in os.walk(uploads_dir):
for file in files:
file_path = os.path.join(root, file)
# Prüfe ob Datei älter als 7 Tage und nicht referenziert
file_age = datetime.now() - datetime.fromtimestamp(os.path.getctime(file_path))
if file_age.days > 7:
try:
os.remove(file_path)
orphaned_files += 1
except:
pass
optimization_results['orphaned_files_removed'] = orphaned_files
except Exception as e:
optimization_results['errors'].append(f"Datei-Bereinigung: {str(e)}")
logger.info(f"✅ Datenbank-Optimierung abgeschlossen: {optimization_results}")
return jsonify({
'success': True,
'message': 'Datenbank erfolgreich optimiert',
'details': optimization_results
})
except Exception as e:
db_session.rollback()
logger.error(f"❌ Fehler bei Datenbank-Optimierung: {str(e)}")
return jsonify({
'success': False,
'message': f'Fehler bei der Datenbank-Optimierung: {str(e)}'
}), 500
finally:
db_session.close()
@app_instance.route('/api/admin/maintenance/create-backup', methods=['POST'])
@login_required
@admin_decorator
def api_create_backup():
"""Erstellt ein System-Backup"""
try:
logger.info(f"💾 Backup-Erstellung gestartet von Benutzer {current_user.username}")
# Backup-Verzeichnis erstellen
backup_dir = os.path.join(app_instance.root_path, 'database', 'backups')
os.makedirs(backup_dir, exist_ok=True)
# Backup-Dateiname mit Zeitstempel
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
backup_filename = f'myp_backup_{timestamp}.zip'
backup_path = os.path.join(backup_dir, backup_filename)
backup_info = {
'filename': backup_filename,
'created_at': datetime.now().isoformat(),
'created_by': current_user.username,
'size_mb': 0,
'files_included': []
}
# ZIP-Backup erstellen
with zipfile.ZipFile(backup_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
# Datenbank-Datei hinzufügen
db_path = os.path.join(app_instance.root_path, 'instance', 'database.db')
if os.path.exists(db_path):
zipf.write(db_path, 'database.db')
backup_info['files_included'].append('database.db')
# Konfigurationsdateien hinzufügen
config_files = ['config.py', 'requirements.txt', '.env']
for config_file in config_files:
config_path = os.path.join(app_instance.root_path, config_file)
if os.path.exists(config_path):
zipf.write(config_path, config_file)
backup_info['files_included'].append(config_file)
# Wichtige Upload-Verzeichnisse hinzufügen (nur kleine Dateien)
uploads_dir = os.path.join(app_instance.root_path, 'uploads')
if os.path.exists(uploads_dir):
for root, dirs, files in os.walk(uploads_dir):
for file in files:
file_path = os.path.join(root, file)
file_size = os.path.getsize(file_path)
# Nur Dateien unter 10MB hinzufügen
if file_size < 10 * 1024 * 1024:
rel_path = os.path.relpath(file_path, app_instance.root_path)
zipf.write(file_path, rel_path)
backup_info['files_included'].append(rel_path)
# Backup-Größe berechnen
backup_size = os.path.getsize(backup_path)
backup_info['size_mb'] = round(backup_size / (1024 * 1024), 2)
# Alte Backups bereinigen (nur die letzten 10 behalten)
try:
backup_files = []
for file in os.listdir(backup_dir):
if file.startswith('myp_backup_') and file.endswith('.zip'):
file_path = os.path.join(backup_dir, file)
backup_files.append((file_path, os.path.getctime(file_path)))
# Nach Erstellungszeit sortieren
backup_files.sort(key=lambda x: x[1], reverse=True)
# Alte Backups löschen (mehr als 10)
for old_backup, _ in backup_files[10:]:
try:
os.remove(old_backup)
logger.info(f"Altes Backup gelöscht: {os.path.basename(old_backup)}")
except:
pass
except Exception as e:
logger.warning(f"Fehler beim Bereinigen alter Backups: {str(e)}")
logger.info(f"✅ Backup erfolgreich erstellt: {backup_filename} ({backup_info['size_mb']} MB)")
return jsonify({
'success': True,
'message': f'Backup erfolgreich erstellt: {backup_filename}',
'details': backup_info
})
except Exception as e:
logger.error(f"❌ Fehler bei Backup-Erstellung: {str(e)}")
return jsonify({
'success': False,
'message': f'Fehler bei der Backup-Erstellung: {str(e)}'
}), 500
logger.info("✅ Wartungs-API-Endpunkte erfolgreich registriert")
# Automatische Registrierung, falls die Module verfügbar sind
if app and app_logger and admin_required and get_db_session:
register_maintenance_routes(app, app_logger, admin_required, get_db_session)

View File

@ -19,6 +19,7 @@ readonly HTTPS_SERVICE_NAME="myp-https"
readonly KIOSK_SERVICE_NAME="myp-kiosk"
readonly WATCHDOG_SERVICE_NAME="kiosk-watchdog"
readonly WATCHDOG_PYTHON_SERVICE_NAME="kiosk-watchdog-python"
readonly FIREWALL_SERVICE_NAME="myp-firewall"
readonly KIOSK_USER="kiosk"
readonly CURRENT_DIR="$(pwd)"
readonly INSTALL_LOG="/var/log/myp-install.log"
@ -560,6 +561,7 @@ install_systemd_services() {
"$KIOSK_SERVICE_NAME.service"
"$WATCHDOG_SERVICE_NAME.service"
"$WATCHDOG_PYTHON_SERVICE_NAME.service"
"$FIREWALL_SERVICE_NAME.service"
)
for service_file in "${service_files[@]}"; do
@ -603,6 +605,12 @@ enable_and_start_services() {
systemctl enable "$WATCHDOG_SERVICE_NAME" || warning "Fehler beim Aktivieren des Watchdog-Service"
systemctl start "$WATCHDOG_SERVICE_NAME" || warning "Fehler beim Starten des Watchdog-Service"
# Firewall-Service aktivieren (falls vorhanden)
if [ -f "$SYSTEM_SYSTEMD_DIR/$FIREWALL_SERVICE_NAME.service" ]; then
progress "Aktiviere Firewall-Service..."
systemctl enable "$FIREWALL_SERVICE_NAME" || warning "Fehler beim Aktivieren des Firewall-Service"
fi
log "✅ Services erfolgreich konfiguriert"
}
@ -679,13 +687,16 @@ show_menu() {
echo -e "${GREEN}3)${NC} Nur Services installieren/aktualisieren"
echo -e " ${BLUE}→ Systemd-Services aus systemd/ Verzeichnis kopieren${NC}"
echo ""
echo -e "${GREEN}4)${NC} System-Test durchführen"
echo -e " ${BLUE}HTTPS-Verbindung und SSL-Zertifikat testen${NC}"
echo -e "${GREEN}4)${NC} Remote-Zugang konfigurieren (RDP + SSH + Firewall)"
echo -e " ${BLUE}SSH (user:raspberry), RDP (root:744563017196A), firewalld${NC}"
echo ""
echo -e "${GREEN}5)${NC} Beenden"
echo -e "${GREEN}5)${NC} System-Test durchführen"
echo -e " ${BLUE}→ HTTPS-Verbindung, SSL-Zertifikat, Remote-Zugang testen${NC}"
echo ""
echo -e "${GREEN}6)${NC} Beenden"
echo ""
echo -e "${CYAN}=================================================================${NC}"
echo -n "Ihre Wahl [1-5]: "
echo -n "Ihre Wahl [1-6]: "
}
# =========================== INSTALLATIONS-MODI ===========================
@ -740,6 +751,17 @@ install_full_kiosk() {
configure_autologin
install_systemd_services
enable_and_start_services
# Frage nach Remote-Zugang
echo ""
echo -n "Remote-Zugang (RDP + SSH + Firewall) konfigurieren? [j/N]: "
read -r configure_remote
if [[ "$configure_remote" =~ ^[Jj]$ ]]; then
install_remote_access
configure_firewall
fi
test_application
cleanup_old_files
@ -769,6 +791,23 @@ install_services_only() {
success "✅ Service-Installation abgeschlossen!"
}
install_remote_access_only() {
log "=== MODUS: NUR REMOTE-ZUGANG KONFIGURIEREN ==="
check_root
check_debian_system
check_internet_connection
install_remote_access
configure_firewall
test_remote_access
success "✅ Remote-Zugang-Konfiguration abgeschlossen!"
info "Zugang verfügbar über:"
info " 📡 SSH: ssh user@<ip-adresse> (Passwort: raspberry)"
info " 🖥️ RDP: <ip-adresse>:3389 (Benutzer: root, Passwort: 744563017196A)"
}
run_system_test() {
log "=== MODUS: SYSTEM-TEST ==="
@ -777,7 +816,7 @@ run_system_test() {
# Zusätzliche Tests
progress "Prüfe Service-Status..."
local services=("$HTTPS_SERVICE_NAME" "$KIOSK_SERVICE_NAME" "$WATCHDOG_SERVICE_NAME")
local services=("$HTTPS_SERVICE_NAME" "$KIOSK_SERVICE_NAME" "$WATCHDOG_SERVICE_NAME" "$FIREWALL_SERVICE_NAME")
for service in "${services[@]}"; do
if systemctl is-enabled --quiet "$service" 2>/dev/null; then
@ -791,9 +830,220 @@ run_system_test() {
fi
done
# Remote-Zugang testen (falls konfiguriert)
if systemctl is-enabled --quiet ssh 2>/dev/null || systemctl is-enabled --quiet xrdp 2>/dev/null; then
test_remote_access
else
info " Remote-Zugang nicht konfiguriert"
fi
success "✅ System-Test abgeschlossen!"
}
# =========================== RDP & SSH ZUGANG ===========================
install_remote_access() {
log "=== INSTALLIERE REMOTE-ZUGANG (RDP & SSH) ==="
# SSH-Server installieren und konfigurieren
progress "Installiere und konfiguriere SSH-Server..."
apt-get install -y openssh-server || error "SSH-Server Installation fehlgeschlagen"
# SSH-Service aktivieren
systemctl enable ssh
systemctl start ssh
# SSH-Benutzer 'user' erstellen (falls nicht vorhanden)
if ! id "user" &>/dev/null; then
progress "Erstelle SSH-Benutzer: user"
useradd -m -s /bin/bash user || error "Kann SSH-Benutzer nicht erstellen"
echo "user:raspberry" | chpasswd || error "Kann Passwort für SSH-Benutzer nicht setzen"
usermod -aG sudo user 2>/dev/null || true
log "✅ SSH-Benutzer 'user' erstellt mit Passwort 'raspberry'"
else
info "SSH-Benutzer 'user' existiert bereits"
echo "user:raspberry" | chpasswd || warning "Konnte Passwort für SSH-Benutzer nicht aktualisieren"
fi
# RDP-Server (xrdp) installieren
progress "Installiere RDP-Server (xrdp)..."
# Minimale Desktop-Umgebung für RDP installieren
progress "Installiere minimale Desktop-Umgebung für RDP..."
apt-get install -y tasksel || error "tasksel Installation fehlgeschlagen"
# XFCE als leichtgewichtige Desktop-Umgebung installieren
progress "Installiere XFCE Desktop-Umgebung..."
apt-get install -y xfce4 xfce4-goodies || error "XFCE Installation fehlgeschlagen"
# xrdp installieren
apt-get install -y xrdp || error "xrdp Installation fehlgeschlagen"
# xrdp-Service aktivieren
systemctl enable xrdp
systemctl start xrdp
# SSL-Zertifikate für xrdp erstellen
progress "Erstelle SSL-Zertifikate für xrdp..."
mkdir -p /etc/xrdp/certs
cd /etc/xrdp/certs
openssl req -x509 -newkey rsa:2048 -nodes -keyout key.pem -out cert.pem -days 3650 \
-subj "/C=DE/ST=Baden-Wuerttemberg/L=Stuttgart/O=Mercedes-Benz/OU=IT/CN=myp-rdp" \
|| error "SSL-Zertifikat-Generierung für xrdp fehlgeschlagen"
# Berechtigungen für xrdp-Zertifikate setzen
chown -R xrdp:xrdp /etc/xrdp/certs
chmod 0644 /etc/xrdp/certs/cert.pem
chmod 0600 /etc/xrdp/certs/key.pem
# xrdp-Konfiguration für TLS
progress "Konfiguriere xrdp für TLS..."
cp /etc/xrdp/xrdp.ini /etc/xrdp/xrdp.ini.backup
cat > /etc/xrdp/xrdp.ini << 'EOF'
[Globals]
ini_version=1
fork=true
port=3389
tcp_nodelay=true
tcp_keepalive=true
security_layer=tls
certificate=/etc/xrdp/certs/cert.pem
key_file=/etc/xrdp/certs/key.pem
ssl_protocols=TLSv1.2, TLSv1.3
autorun=
allow_channels=true
allow_multimon=true
bitmap_cache=true
bitmap_compression=true
bulk_compression=true
max_bpp=32
new_cursors=true
use_fastpath=both
require_credentials=true
ask_for_reconnect_reason=true
enable_token_login=true
[Xorg]
name=Xorg
lib=libxup.so
username=ask
password=ask
ip=127.0.0.1
port=-1
code=20
EOF
# Root-Passwort für RDP setzen
progress "Setze Root-Passwort für RDP-Zugang..."
echo "root:744563017196A" | chpasswd || error "Kann Root-Passwort nicht setzen"
# xrdp-Service neu starten
systemctl restart xrdp
cd "$CURRENT_DIR"
log "✅ Remote-Zugang konfiguriert:"
log " 📡 SSH: user:raspberry (Port 22)"
log " 🖥️ RDP: root:744563017196A (Port 3389)"
}
# =========================== FIREWALL KONFIGURATION ===========================
configure_firewall() {
log "=== KONFIGURIERE FIREWALL (firewalld) ==="
# firewalld installieren
progress "Installiere firewalld..."
apt-get install -y firewalld || error "firewalld Installation fehlgeschlagen"
# firewalld aktivieren und starten
systemctl enable firewalld
systemctl start firewalld
# Warte kurz bis firewalld vollständig gestartet ist
sleep 3
progress "Konfiguriere firewalld-Zonen und -Regeln..."
# Zone definieren
firewall-cmd --permanent --new-zone=myp-backend 2>/dev/null || true
firewall-cmd --permanent --zone=myp-backend --add-source=192.168.0.0/24
# Nur HTTPS für API & Kiosk zulassen
firewall-cmd --permanent --zone=myp-backend --add-port=443/tcp
# SSH für Wartung
firewall-cmd --permanent --zone=myp-backend --add-service=ssh
# RDP für Remote-Desktop
firewall-cmd --permanent --zone=myp-backend --add-port=3389/tcp
# Default-Zone setzen
firewall-cmd --set-default-zone=myp-backend
# Änderungen übernehmen
firewall-cmd --reload
# Firewall-Status anzeigen
progress "Firewall-Konfiguration:"
firewall-cmd --list-all-zones | grep -A 10 "myp-backend" || true
log "✅ Firewall konfiguriert:"
log " 🔒 Zone: myp-backend (192.168.0.0/24)"
log " 🌐 HTTPS: Port 443/tcp"
log " 📡 SSH: Port 22/tcp"
log " 🖥️ RDP: Port 3389/tcp"
}
# =========================== REMOTE-ZUGANG TESTEN ===========================
test_remote_access() {
log "=== TESTE REMOTE-ZUGANG ==="
# SSH-Service testen
progress "Teste SSH-Service..."
if systemctl is-active --quiet ssh; then
success "✅ SSH-Service läuft"
# SSH-Port testen
if ss -tlnp | grep -q ":22 "; then
success "✅ SSH-Port 22 ist offen"
else
warning "⚠️ SSH-Port 22 nicht erreichbar"
fi
else
error "❌ SSH-Service läuft nicht"
fi
# RDP-Service testen
progress "Teste RDP-Service..."
if systemctl is-active --quiet xrdp; then
success "✅ RDP-Service läuft"
# RDP-Port testen
if ss -tlnp | grep -q ":3389 "; then
success "✅ RDP-Port 3389 ist offen"
else
warning "⚠️ RDP-Port 3389 nicht erreichbar"
fi
else
error "❌ RDP-Service läuft nicht"
fi
# Firewall-Status testen
progress "Teste Firewall-Status..."
if systemctl is-active --quiet firewalld; then
success "✅ Firewall läuft"
# Aktive Zone anzeigen
local active_zone=$(firewall-cmd --get-active-zones | head -1)
info "Aktive Zone: $active_zone"
else
warning "⚠️ Firewall läuft nicht"
fi
log "✅ Remote-Zugang-Test abgeschlossen"
}
# =========================== HAUPTPROGRAMM ===========================
main() {
# Erstelle Log-Datei
@ -823,17 +1073,23 @@ main() {
read -r
;;
4)
run_system_test
install_remote_access_only
echo ""
echo -n "Drücken Sie Enter um fortzufahren..."
read -r
;;
5)
run_system_test
echo ""
echo -n "Drücken Sie Enter um fortzufahren..."
read -r
;;
6)
log "Setup-Skript beendet"
exit 0
;;
*)
error "Ungültige Auswahl. Bitte wählen Sie 1-5."
error "Ungültige Auswahl. Bitte wählen Sie 1-6."
;;
esac
done

View File

@ -0,0 +1,60 @@
[Unit]
Description=MYP Firewall Configuration Service
Documentation=https://github.com/MYP-Druckerverwaltung
After=firewalld.service
Wants=firewalld.service
Requires=firewalld.service
[Service]
Type=oneshot
RemainAfterExit=yes
User=root
Group=root
# Firewall-Konfiguration für MYP Backend
ExecStart=/bin/bash -c '\
# Warte bis firewalld vollständig gestartet ist \
sleep 5; \
\
# Zone definieren (falls nicht vorhanden) \
firewall-cmd --permanent --new-zone=myp-backend 2>/dev/null || true; \
\
# Quell-Netzwerk definieren \
firewall-cmd --permanent --zone=myp-backend --add-source=192.168.0.0/24; \
\
# HTTPS für API & Kiosk \
firewall-cmd --permanent --zone=myp-backend --add-port=443/tcp; \
\
# SSH für Wartung \
firewall-cmd --permanent --zone=myp-backend --add-service=ssh; \
\
# RDP für Remote-Desktop \
firewall-cmd --permanent --zone=myp-backend --add-port=3389/tcp; \
\
# Default-Zone setzen \
firewall-cmd --set-default-zone=myp-backend; \
\
# Änderungen übernehmen \
firewall-cmd --reload; \
\
# Status loggen \
logger "MYP Firewall: Konfiguration erfolgreich angewendet"; \
firewall-cmd --list-all-zones | logger -t "MYP-Firewall"; \
'
# Umgebungsvariablen
Environment=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
# Logging
StandardOutput=journal
StandardError=journal
SyslogIdentifier=myp-firewall
# Sicherheitseinstellungen
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=false
ProtectHome=true
[Install]
WantedBy=multi-user.target

View File

@ -624,6 +624,7 @@ class MaintenanceModal {
this.triggerBtn = document.getElementById('maintenance-btn');
this.closeBtn = document.getElementById('close-maintenance-modal');
this.isOpen = false;
this.isLoading = false;
this.initializeEventListeners();
}
@ -705,28 +706,101 @@ class MaintenanceModal {
}
}
setLoadingState(loading) {
this.isLoading = loading;
const buttons = this.modal.querySelectorAll('button:not(#close-maintenance-modal)');
buttons.forEach(button => {
if (loading) {
button.disabled = true;
button.style.opacity = '0.6';
button.style.cursor = 'not-allowed';
// Spinner hinzufügen
if (!button.querySelector('.loading-spinner')) {
const spinner = document.createElement('div');
spinner.className = 'loading-spinner inline-block w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin mr-2';
button.insertBefore(spinner, button.firstChild);
}
} else {
button.disabled = false;
button.style.opacity = '1';
button.style.cursor = 'pointer';
// Spinner entfernen
const spinner = button.querySelector('.loading-spinner');
if (spinner) {
spinner.remove();
}
}
});
// Loading-Overlay anzeigen/verstecken
const loadingOverlay = document.getElementById('loading-overlay');
if (loadingOverlay) {
if (loading) {
loadingOverlay.classList.remove('hidden');
} else {
loadingOverlay.classList.add('hidden');
}
}
}
async executeAction(actionType) {
if (this.isLoading) return;
try {
this.setLoadingState(true);
let endpoint = '';
let confirmMessage = '';
let successMessage = '';
switch (actionType) {
case 'clearCache':
await clearCache();
endpoint = '/api/admin/maintenance/clear-cache';
confirmMessage = 'Möchten Sie den Cache wirklich leeren?';
successMessage = 'Cache erfolgreich geleert';
break;
case 'optimizeDatabase':
await optimizeDatabase();
endpoint = '/api/admin/maintenance/optimize-database';
confirmMessage = 'Möchten Sie die Datenbank optimieren? Dies kann einige Minuten dauern.';
successMessage = 'Datenbank erfolgreich optimiert';
break;
case 'createBackup':
await createBackup();
endpoint = '/api/admin/maintenance/create-backup';
confirmMessage = 'Möchten Sie ein Backup erstellen?';
successMessage = 'Backup erfolgreich erstellt';
break;
default:
throw new Error(`Unbekannte Aktion: ${actionType}`);
}
this.closeModal();
if (!confirm(confirmMessage)) {
this.setLoadingState(false);
return;
}
const response = await fetch(endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': getCsrfToken()
}
});
const result = await response.json();
if (response.ok && result.success) {
showNotification(successMessage, 'success');
this.closeModal();
} else {
showNotification(result.message || 'Fehler bei der Ausführung der Wartungsaktion', 'error');
}
} catch (error) {
console.error('Fehler bei Wartungsaktion:', error);
showNotification('Fehler bei der Ausführung der Wartungsaktion', 'error');
showNotification('Fehler bei der Ausführung der Wartungsaktion: ' + error.message, 'error');
} finally {
this.setLoadingState(false);
}
@ -734,7 +808,8 @@ class MaintenanceModal {
navigateToSettings() {
try {
window.location.href = '{{ url_for("optimization_settings") }}';
// Direkte Navigation zu den Optimierungs-Einstellungen
window.location.href = '/api/optimization/settings';
} catch (error) {
console.error('Fehler beim Navigieren zu den Einstellungen:', error);
showNotification('Fehler beim Öffnen der Einstellungen', 'error');
@ -742,9 +817,15 @@ class MaintenanceModal {
}
}
// Globale Wartungs-Modal Instanz
let maintenanceModal = null;
// Initialisierung nach DOM-Laden
document.addEventListener('DOMContentLoaded', function() {
new MaintenanceModal();
// Nur einmal initialisieren
if (!maintenanceModal) {
maintenanceModal = new MaintenanceModal();
}
});
// Notification anzeigen
@ -763,6 +844,11 @@ function showNotification(message, type = 'info') {
${type === 'success' ? '✅' : type === 'error' ? '❌' : ''}
</div>
<div class="text-sm font-medium">${message}</div>
<button onclick="this.parentElement.parentElement.remove()" class="ml-auto text-white hover:text-gray-200">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
</svg>
</button>
</div>
`;
@ -776,13 +862,15 @@ function showNotification(message, type = 'info') {
// Automatisch entfernen nach 5 Sekunden
setTimeout(() => {
notification.style.transform = 'translateX(100%)';
notification.style.opacity = '0';
setTimeout(() => {
if (notification.parentNode) {
notification.parentNode.removeChild(notification);
}
}, 300);
if (notification.parentNode) {
notification.style.transform = 'translateX(100%)';
notification.style.opacity = '0';
setTimeout(() => {
if (notification.parentNode) {
notification.parentNode.removeChild(notification);
}
}, 300);
}
}, 5000);
}
@ -791,102 +879,5 @@ function getCsrfToken() {
const token = document.querySelector('meta[name="csrf-token"]');
return token ? token.getAttribute('content') : '';
}
// Cache leeren
async function clearCache() {
if (!confirm('Möchten Sie den Cache wirklich leeren?')) return;
try {
showNotification('Cache wird geleert...', 'info');
const response = await fetch('/api/admin/cache/clear', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': getCsrfToken()
}
});
const result = await response.json();
if (response.ok && result.success) {
showNotification('Cache erfolgreich geleert', 'success');
// Modal schließen
document.getElementById('maintenance-modal').classList.add('hidden');
} else {
showNotification(result.message || 'Fehler beim Leeren des Cache', 'error');
}
} catch (error) {
showNotification('Netzwerkfehler: ' + error.message, 'error');
}
}
// Datenbank optimieren
async function optimizeDatabase() {
if (!confirm('Möchten Sie die Datenbank optimieren? Dies kann einige Minuten dauern.')) return;
try {
showNotification('Datenbank wird optimiert...', 'info');
const response = await fetch('/api/admin/database/optimize', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': getCsrfToken()
}
});
const result = await response.json();
if (response.ok && result.success) {
showNotification('Datenbank erfolgreich optimiert', 'success');
// Modal schließen
document.getElementById('maintenance-modal').classList.add('hidden');
} else {
showNotification(result.message || 'Fehler bei der Datenbankoptimierung', 'error');
}
} catch (error) {
showNotification('Netzwerkfehler: ' + error.message, 'error');
}
}
// Backup erstellen
async function createBackup() {
if (!confirm('Möchten Sie ein Backup erstellen?')) return;
try {
showNotification('Backup wird erstellt...', 'info');
const response = await fetch('/api/admin/backup/create', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': getCsrfToken()
}
});
const result = await response.json();
if (response.ok && result.success) {
showNotification('Backup erfolgreich erstellt', 'success');
// Modal schließen
document.getElementById('maintenance-modal').classList.add('hidden');
} else {
showNotification(result.message || 'Fehler beim Erstellen des Backups', 'error');
}
} catch (error) {
showNotification('Netzwerkfehler: ' + error.message, 'error');
}
}
// Escape-Taste um Modal zu schließen
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') {
const modal = document.getElementById('maintenance-modal');
if (!modal.classList.contains('hidden')) {
modal.classList.add('hidden');
}
}
});
</script>
{% endblock %}