🎉 Feature: Updated project documentation and removed deprecated admin files 🎉

This commit is contained in:
2025-06-11 12:34:17 +02:00
parent 88324968e3
commit 6be0d6ee88
4 changed files with 333 additions and 827 deletions

View File

@ -1 +1,332 @@
# MYP Platform - Mercedes-Benz 3D-Druck-Management-System
## Projektüberblick
Dies ist ein umfassendes 3D-Druck-Management-System für Mercedes-Benz TBA Marienfelde, entwickelt als Projektarbeit für die IHK. Das System verwaltet 3D-Drucker, Druckaufträge, Benutzer und bietet erweiterte Features wie Smart-Plug-Steuerung über Tapo-Geräte.
## Hauptkomponenten
### 🚀 **Core Application Files**
#### **backend/app.py** ⭐ (HAUPTEINSTIEGSPUNKT)
- **Funktion**: Hauptanwendung und Flask-App-Initialisierung
- **Verwendung**: ✅ AKTIV - Startet die gesamte Anwendung
- **Features**:
- Production/Development-Konfiguration
- Blueprint-Registration
- Security-Features (CSRF, Session-Management)
- Air-Gapped/Offline-Modus für Mercedes-Benz
- Aggressive Shutdown-Handler
- **Abhängigkeiten**: models.py, alle blueprints, utils/*
#### **backend/models.py** ⭐ (DATENBANK-CORE)
- **Funktion**: SQLAlchemy-Datenbankmodelle und Session-Management
- **Verwendung**: ✅ AKTIV - Von allen Modulen verwendet
- **Modelle**:
- `User` - Benutzerverwaltung mit Mercedes-spezifischen Feldern
- `Printer` - 3D-Drucker mit Status und Standort
- `Job` - Druckaufträge mit vollständiger Workflow-Unterstützung
- `GuestRequest` - Gastanfragen mit OTP-System
- `SystemTimer` - Timer-Management für automatische Abläufe
- `PlugStatusLog` - Tapo Smart-Plug Logging
- `Stats`, `SystemLog`, `Notification` - System-Tracking
- **Database**: SQLite mit optimierten Connection-Pooling
### 🎛️ **Configuration & Settings**
#### **backend/config/**
- **app_config.py** ✅ - Flask-Konfigurationsklassen (Dev/Prod/Test)
- **security.py** ✅ - Sicherheitskonfiguration und Headers
- **__init__.py** ✅ - Konfiguration-Package-Initialisierung
#### **backend/utils/settings.py** ⭐
- **Funktion**: Zentrale Systemeinstellungen und Pfade
- **Verwendung**: ✅ AKTIV - Von allen Modulen importiert
- **Inhalt**: DATABASE_PATH, SECRET_KEY, SESSION_LIFETIME, Pfade
#### **backend/requirements.txt** ⭐
- **Funktion**: Python-Abhängigkeiten (130+ Pakete)
- **Verwendung**: ✅ AKTIV - Production-optimiert
- **Kategorien**: Flask-Ecosystem, Hardware-Integration (PyP100), Security, Monitoring
### 🔧 **Blueprints (API-Routen)**
#### **backend/blueprints/jobs.py** ⭐
- **Funktion**: Druckauftrags-Management
- **Verwendung**: ✅ AKTIV
- **Features**: Job-CRUD, Status-Tracking, Timer-Integration
#### **backend/blueprints/printers.py** ⭐
- **Funktion**: Druckerverwaltung und -monitoring
- **Verwendung**: ✅ AKTIV
- **Features**: Drucker-Status, Wartung, Standort-Management
#### **backend/blueprints/auth.py** ⭐
- **Funktion**: Authentifizierung und Autorisierung
- **Verwendung**: ✅ AKTIV
- **Features**: Login/Logout, Session-Management, Berechtigungen
#### **backend/blueprints/admin_unified.py** ⭐
- **Funktion**: Einheitliche Admin-Oberfläche
- **Verwendung**: ✅ AKTIV - Ersetzt separate Admin-Module
- **Features**: Benutzer-/Drucker-/System-Verwaltung
#### **backend/blueprints/calendar.py** ⭐
- **Funktion**: Kalender-Integration und Terminplanung
- **Verwendung**: ✅ AKTIV
- **Features**: Job-Scheduling, Ressourcenplanung
#### **backend/blueprints/guest.py** ⭐
- **Funktion**: Gastbenutzer-System
- **Verwendung**: ✅ AKTIV
- **Features**: Gastanfragen, OTP-System, E-Mail-Benachrichtigungen
#### **backend/blueprints/tapo_control.py** ⭐
- **Funktion**: Smart-Plug-Steuerung (TP-Link Tapo)
- **Verwendung**: ✅ AKTIV
- **Features**: Fernsteuerung, Zeitsteuerung, Status-Monitoring
#### **backend/blueprints/user_management.py** ⭐
- **Funktion**: Erweiterte Benutzerverwaltung
- **Verwendung**: ✅ AKTIV
- **Features**: Profil-Management, Berechtigungen, API-Endpunkte
#### **backend/blueprints/uploads.py** ⭐
- **Funktion**: Datei-Upload-System
- **Verwendung**: ✅ AKTIV
- **Features**: G-Code-Upload, Validierung, Speicherverwaltung
#### **backend/blueprints/api_simple.py** ⭐
- **Funktion**: Vereinfachte API-Endpunkte
- **Verwendung**: ✅ AKTIV
- **Features**: Health-Check, Status-API
#### **backend/blueprints/sessions.py** ✅
- **Funktion**: Session-Management
- **Verwendung**: ✅ AKTIV
#### **backend/blueprints/kiosk.py** ✅
- **Funktion**: Kiosk-Modus für öffentliche Terminals
- **Verwendung**: ✅ AKTIV
### 🛠️ **Utility Modules**
#### **Core System Utilities**
- **utils/logging_config.py** ⭐ - Erweiterte Logging-Konfiguration
- **utils/database_core.py** ⭐ - Datenbank-Optimierungen und Connection-Management
- **utils/database_utils.py** ⭐ - Datenbank-Hilfsfunktionen
- **utils/database_cleanup.py** ✅ - Automatische DB-Bereinigung
- **utils/database_migration.py** ✅ - Schema-Migrationen
#### **Hardware Integration**
- **utils/tapo_controller.py** ⭐ - TP-Link Tapo Smart-Plug Integration
- **utils/printer_monitor.py** ⭐ - 3D-Drucker Monitoring und Status
- **utils/debug_drucker_erkennung.py** ✅ - Drucker-Discovery-System
#### **Job & Timer Management**
- **utils/job_scheduler.py** ⭐ - Druckauftrags-Scheduler mit Smart-Plug-Integration
- **utils/timer_manager.py** ⭐ - Erweiterte Timer-Verwaltung
- **utils/timeout_force_quit_manager.py** ⭐ - Force-Quit mit visueller Anzeige
- **utils/queue_manager.py** ⭐ - Job-Warteschlangen-Management
- **utils/conflict_manager.py** ⭐ - Konfliktauflösung bei Überschneidungen
#### **Security & Permissions**
- **utils/security.py** ⭐ - Sicherheits-Middleware und Headers
- **utils/permissions.py** ⭐ - Rollenbasierte Berechtigungen
- **utils/rate_limiter.py** ✅ - Rate-Limiting für API-Schutz
#### **User Interface & Experience**
- **utils/form_validation.py** ⭐ - Formular-Validierung mit Client/Server-Sync
- **utils/drag_drop_system.py** ⭐ - Drag & Drop für Datei-Uploads
- **utils/advanced_tables.py** ⭐ - Erweiterte Tabellen mit Sortierung/Filtering
- **utils/template_helpers.py** ⭐ - Jinja2-Template-Hilfsfunktionen
- **utils/realtime_dashboard.py** ⭐ - WebSocket-basiertes Real-time Dashboard
#### **System Management**
- **utils/system_control.py** ⭐ - System-weite Steuerung und Monitoring
- **utils/maintenance_system.py** ⭐ - Wartungsplaner für Drucker
- **utils/multi_location_system.py** ⭐ - Multi-Standort-Verwaltung
- **utils/shutdown_manager.py** ⭐ - Kontrolliertes System-Shutdown
- **utils/watchdog_manager.py** ⭐ - System-Überwachung und Recovery
- **utils/error_recovery.py** ⭐ - Automatische Fehlerbehandlung
#### **Reporting & Analytics**
- **utils/report_generator.py** ⭐ - PDF/Excel-Report-Generator
- **utils/analytics.py** ⭐ - System-Analytics und Metriken
- **utils/performance_monitor.py** ✅ - Performance-Tracking
#### **Communication & Notifications**
- **utils/email_notification.py** ✅ - E-Mail-Benachrichtigungen
- **utils/offline_config.py** ✅ - Air-Gapped-Modus Konfiguration
#### **File & Data Management**
- **utils/file_manager.py** ⭐ - Erweiterte Dateiverwaltung
- **utils/file_utils.py** ⭐ - Datei-Hilfsfunktionen
- **utils/backup_manager.py** ✅ - Automatische Backups
#### **Platform-Specific**
- **utils/windows_fixes.py** ⭐ - Windows-spezifische Optimierungen
- **utils/ssl_manager.py** ✅ - SSL/TLS-Zertifikat-Management
- **utils/ssl_config.py** ✅ - SSL-Konfiguration
### 🎨 **Frontend Assets**
#### **backend/templates/** ⭐
- **base.html** - Haupt-Template mit responsivem Design
- **login.html** - Anmelde-Interface
- **dashboard.html** - Hauptdashboard
- **jobs.html** - Druckauftrags-Verwaltung
- **printers.html** - Drucker-Übersicht
- **calendar.html** - Kalender-Ansicht
- **admin.html** - Admin-Panel
- **guest_request.html** - Gastanfrage-Formular
- **settings.html** - Benutzereinstellungen
- **profile.html** - Benutzerprofil
- **analytics.html** - System-Analytics
- **tapo_control.html** - Smart-Plug-Steuerung
- **errors/** - Fehlerseiten (404, 500)
- **jobs/** - Job-spezifische Templates
#### **backend/static/** ⭐
- **css/** - Stylesheets (responsive, Mercedes-Design)
- **js/** - JavaScript (Charts, FullCalendar, Real-time)
- **fontawesome/** - Icon-Bibliothek (kompletter Satz)
- **icons/** - Projekt-spezifische Icons
- **manifest.json** - PWA-Manifest
- **offline.html** - Air-Gapped-Modus-Seite
### 📊 **Database & Data**
#### **backend/database/**
- **myp.db** - Haupt-SQLite-Datenbank
- **backups/** - Automatische Datenbank-Backups
#### **backend/uploads/** ⭐
- **assets/**, **avatars/**, **jobs/**, **temp/** - Strukturierte Datei-Organisation
- **backups/**, **logs/**, **guests/** - System-Dateien
### 🔧 **Setup & Deployment**
#### **backend/setup/**
- **setup_https_only.sh** ⭐ - HTTPS-Setup für Production
- **systemd/** - Linux-Service-Dateien
#### **backend/scripts/** ⭐
- **screenshot_tool.py** - Automatische Screenshots für Schulungen
- **test_protocol_generator.py** - Test-Protokoll-Generator
- **requirements_screenshot_tool.txt** - Dependencies für Screenshot-Tool
### 📝 **Documentation & Legacy**
#### **docs/** ⭐
- **v1-LEGACY/**, **v2-LEGACY/** - Dokumentation alter Versionen
- **MYP.dbml**, **MYP.sql** - Datenbank-Schema-Dokumentation
#### **backend/legacy/**
- **app_original.py** - Original-Implementierung (Backup)
#### **IHK_Projektdokumentation/** ⭐
- **Anlagen_und_Screenshots/** - Schulungsmaterialien
- **ChatGPT-Data/**, **Gesprächsprotokolle/** - Entwicklungsdokumentation
- **IHK_Vorgaben/** - Offizielle IHK-Anforderungen
#### **dokumentation/**
- **berichtshefte/** - Ausbildungsberichte
- **kalender/** - Projekt-Zeitplanung
### 🧪 **Testing & Quality**
#### **tests/**
- Umfassendes Test-Framework (noch zu implementieren)
#### **Quality Assurance Files**
- **utils/test_*.py** - Verschiedene Test-Utilities
- **backend/debug/** - Debug-Logs und -Tools
- **backend/logs/** - Strukturierte Log-Dateien nach Modulen
### 🔄 **Deprecated & Backup Files**
#### **LEGACY-torben_frontend/** ❌
- **Funktion**: Alte Next.js-Frontend-Implementation
- **Status**: DEPRECATED - Wird nicht verwendet
- **Ersetzt durch**: Jinja2-Templates in backend/templates/
#### **backend/blueprints/deprecated/** ❌
- **admin.py** - Alte Admin-Implementation
- **Status**: Ersetzt durch admin_unified.py
#### **utils/deprecated/** ❌
- Verschiedene veraltete Utility-Module
## 📈 **Referenz-Matrix (Wer verwendet was)**
### Hochfrequent verwendete Module:
- **models.py** → Verwendet von: app.py, allen blueprints, 90% der utils
- **utils/settings.py** → Verwendet von: app.py, models.py, allen configs
- **utils/logging_config.py** → Verwendet von: Praktisch allen .py-Dateien
- **utils/permissions.py** → Verwendet von: allen blueprints, auth-relevanten utils
### Blueprint-Dependencies:
- **jobs.py** → models, job_scheduler, timer_manager, printer_monitor
- **printers.py** → models, printer_monitor, maintenance_system
- **admin_unified.py** → models, alle user/printer/system utils
- **tapo_control.py** → tapo_controller, job_scheduler
### Hardware-Integration:
- **utils/tapo_controller.py** → Verwendet von: tapo_control.py, job_scheduler.py
- **utils/printer_monitor.py** → Verwendet von: printers.py, jobs.py, system_control.py
## 🚦 **System-Status-Übersicht**
### ✅ **Produktiv verwendet** (Core Features):
- Hauptanwendung (app.py, models.py)
- Alle aktiven Blueprints
- Job-Management-System
- Smart-Plug-Integration
- Benutzer- und Rechteverwaltung
- Real-time Dashboard
- Gast-System mit OTP
### 🔧 **Experimentell/In Entwicklung**:
- Multi-Location-System
- Erweiterte Analytics
- Performance-Monitoring
- Automatische Wartungsplanung
### ❌ **Deprecated/Nicht verwendet**:
- LEGACY-torben_frontend/
- blueprints/deprecated/
- utils/deprecated/
- Einige Test-/Debug-Scripts
## 🎯 **Empfehlungen für Wartung**
### Priorität 1 (Kritisch):
- **models.py**: Datenbank-Schema pflegen
- **app.py**: Konfiguration aktuell halten
- **Alle blueprints**: API-Endpunkte dokumentieren
- **utils/tapo_controller.py**: Hardware-Kompatibilität prüfen
### Priorität 2 (Wichtig):
- **templates/**: UI/UX kontinuierlich verbessern
- **utils/security.py**: Sicherheitsupdates
- **setup/**: Deployment-Scripts aktualisieren
### Aufräumen:
- Deprecated-Ordner entfernen (nach Backup)
- Ungenutzte Test-Scripts konsolidieren
- Log-Dateien regelmäßig archivieren
---
**Gesamtstatistik:**
- **~200 Python-Dateien** (Backend-Logik)
- **~50 HTML-Templates** (Frontend)
- **~130 Dependencies** (requirements.txt)
- **~15 Hauptbereiche** (Blueprints, Utils, etc.)
- **1 SQLite-Datenbank** mit 12+ Tabellen
- **Full-Stack-Anwendung** mit Real-time-Features
**Entwicklungszeitraum:** 2024-2025 (Laufendes IHK-Projekt)
**Zielgruppe:** Mercedes-Benz TBA Marienfelde (Air-Gapped Environment)
**Technologie-Stack:** Flask, SQLAlchemy, Jinja2, WebSockets, SQLite

View File

@ -107,8 +107,7 @@ python app.py --debug
```
backend/
├── app.py # Hauptanwendung mit Development-Support
├── app_production.py # Produktions-Anwendung mit HTTPS-Support
├── app.py # Haupt-Anwendung mit Production- und Development-Modi
├── models.py # Datenbankmodelle
├── requirements.txt # Python-Abhängigkeiten
├── README.md # Diese Datei

View File

@ -1,335 +0,0 @@
"""
Admin-Blueprint für das 3D-Druck-Management-System
Dieses Modul enthält alle Admin-spezifischen Routen und Funktionen,
einschließlich Benutzerverwaltung, Systemüberwachung und Drucker-Administration.
"""
from flask import Blueprint, render_template, request, jsonify, redirect, url_for, flash
from flask_login import login_required, current_user
from functools import wraps
from models import User, Printer, Job, get_db_session, Stats, SystemLog
from utils.logging_config import get_logger
from datetime import datetime
# Blueprint erstellen
admin_blueprint = Blueprint('admin', __name__, url_prefix='/admin')
# Logger initialisieren
admin_logger = get_logger("admin")
def admin_required(f):
"""Decorator für Admin-Berechtigung"""
@wraps(f)
@login_required
def decorated_function(*args, **kwargs):
admin_logger.info(f"Admin-Check für Funktion {f.__name__}: User authenticated: {current_user.is_authenticated}, User ID: {current_user.id if current_user.is_authenticated else 'None'}, Is Admin: {current_user.is_admin if current_user.is_authenticated else 'None'}")
if not current_user.is_admin:
admin_logger.warning(f"Admin-Zugriff verweigert für User {current_user.id if current_user.is_authenticated else 'Anonymous'} auf Funktion {f.__name__}")
return jsonify({"error": "Nur Administratoren haben Zugriff"}), 403
return f(*args, **kwargs)
return decorated_function
@admin_blueprint.route("/")
@login_required
@admin_required
def admin_dashboard():
"""Admin-Dashboard-Hauptseite"""
try:
db_session = get_db_session()
# Grundlegende Statistiken sammeln
total_users = db_session.query(User).count()
total_printers = db_session.query(Printer).count()
total_jobs = db_session.query(Job).count()
# Aktive Jobs zählen
active_jobs = db_session.query(Job).filter(
Job.status.in_(['pending', 'printing', 'paused'])
).count()
db_session.close()
stats = {
'total_users': total_users,
'total_printers': total_printers,
'total_jobs': total_jobs,
'active_jobs': active_jobs
}
return render_template('admin/dashboard.html', stats=stats)
except Exception as e:
admin_logger.error(f"Fehler beim Laden des Admin-Dashboards: {str(e)}")
flash("Fehler beim Laden der Dashboard-Daten", "error")
return render_template('admin/dashboard.html', stats={})
@admin_blueprint.route("/users")
@login_required
@admin_required
def users_overview():
"""Benutzerübersicht für Administratoren"""
return render_template('admin/users.html')
@admin_blueprint.route("/users/add", methods=["GET"])
@login_required
@admin_required
def add_user_page():
"""Seite zum Hinzufügen eines neuen Benutzers"""
return render_template('admin/add_user.html')
@admin_blueprint.route("/users/<int:user_id>/edit", methods=["GET"])
@login_required
@admin_required
def edit_user_page(user_id):
"""Seite zum Bearbeiten eines Benutzers"""
try:
db_session = get_db_session()
user = db_session.query(User).filter(User.id == user_id).first()
if not user:
db_session.close()
flash("Benutzer nicht gefunden", "error")
return redirect(url_for('admin.users_overview'))
db_session.close()
return render_template('admin/edit_user.html', user=user)
except Exception as e:
admin_logger.error(f"Fehler beim Laden der Benutzer-Bearbeitung: {str(e)}")
flash("Fehler beim Laden der Benutzerdaten", "error")
return redirect(url_for('admin.users_overview'))
@admin_blueprint.route("/printers")
@login_required
@admin_required
def printers_overview():
"""Druckerübersicht für Administratoren"""
return render_template('admin/printers.html')
@admin_blueprint.route("/printers/add", methods=["GET"])
@login_required
@admin_required
def add_printer_page():
"""Seite zum Hinzufügen eines neuen Druckers"""
return render_template('admin/add_printer.html')
@admin_blueprint.route("/printers/<int:printer_id>/edit", methods=["GET"])
@login_required
@admin_required
def edit_printer_page(printer_id):
"""Seite zum Bearbeiten eines Druckers"""
try:
db_session = get_db_session()
printer = db_session.query(Printer).filter(Printer.id == printer_id).first()
if not printer:
db_session.close()
flash("Drucker nicht gefunden", "error")
return redirect(url_for('admin.printers_overview'))
db_session.close()
return render_template('admin/edit_printer.html', printer=printer)
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'))
@admin_blueprint.route("/guest-requests")
@login_required
@admin_required
def guest_requests():
"""Gäste-Anfragen-Übersicht"""
return render_template('admin/guest_requests.html')
@admin_blueprint.route("/advanced-settings")
@login_required
@admin_required
def advanced_settings():
"""Erweiterte Systemeinstellungen"""
return render_template('admin/advanced_settings.html')
@admin_blueprint.route("/system-health")
@login_required
@admin_required
def system_health():
"""System-Gesundheitsstatus"""
return render_template('admin/system_health.html')
@admin_blueprint.route("/logs")
@login_required
@admin_required
def logs_overview():
"""System-Logs-Übersicht"""
return render_template('admin/logs.html')
@admin_blueprint.route("/maintenance")
@login_required
@admin_required
def maintenance():
"""Wartungsseite"""
return render_template('admin/maintenance.html')
# API-Endpunkte für Admin-Funktionen
@admin_blueprint.route("/api/users", methods=["POST"])
@login_required
@admin_required
def create_user_api():
"""API-Endpunkt zum Erstellen eines neuen Benutzers"""
try:
data = request.get_json()
# Validierung der erforderlichen Felder
required_fields = ['username', 'email', 'password', 'name']
for field in required_fields:
if field not in data or not data[field]:
return jsonify({"error": f"Feld '{field}' ist erforderlich"}), 400
db_session = get_db_session()
# Überprüfung auf bereits existierende Benutzer
existing_user = db_session.query(User).filter(
(User.username == data['username']) | (User.email == data['email'])
).first()
if existing_user:
db_session.close()
return jsonify({"error": "Benutzername oder E-Mail bereits vergeben"}), 400
# Neuen Benutzer erstellen
new_user = User(
username=data['username'],
email=data['email'],
name=data['name'],
role=data.get('role', 'user'),
department=data.get('department'),
position=data.get('position'),
phone=data.get('phone'),
bio=data.get('bio')
)
new_user.set_password(data['password'])
db_session.add(new_user)
db_session.commit()
admin_logger.info(f"Neuer Benutzer erstellt: {new_user.username} von Admin {current_user.username}")
db_session.close()
return jsonify({
"success": True,
"message": "Benutzer erfolgreich erstellt",
"user_id": new_user.id
})
except Exception as e:
admin_logger.error(f"Fehler beim Erstellen des Benutzers: {str(e)}")
return jsonify({"error": "Fehler beim Erstellen des Benutzers"}), 500
@admin_blueprint.route("/api/users/<int:user_id>", methods=["GET"])
@login_required
@admin_required
def get_user_api(user_id):
"""API-Endpunkt zum Abrufen von Benutzerdaten"""
try:
db_session = get_db_session()
user = db_session.query(User).filter(User.id == user_id).first()
if not user:
db_session.close()
return jsonify({"error": "Benutzer nicht gefunden"}), 404
user_data = {
"id": user.id,
"username": user.username,
"email": user.email,
"name": user.name,
"role": user.role,
"active": user.active,
"created_at": user.created_at.isoformat() if user.created_at else None,
"last_login": user.last_login.isoformat() if user.last_login else None,
"department": user.department,
"position": user.position,
"phone": user.phone,
"bio": user.bio
}
db_session.close()
return jsonify(user_data)
except Exception as e:
admin_logger.error(f"Fehler beim Abrufen der Benutzerdaten: {str(e)}")
return jsonify({"error": "Fehler beim Abrufen der Benutzerdaten"}), 500
@admin_blueprint.route("/api/users/<int:user_id>", methods=["PUT"])
@login_required
@admin_required
def update_user_api(user_id):
"""API-Endpunkt zum Aktualisieren von Benutzerdaten"""
try:
data = request.get_json()
db_session = get_db_session()
user = db_session.query(User).filter(User.id == user_id).first()
if not user:
db_session.close()
return jsonify({"error": "Benutzer nicht gefunden"}), 404
# Aktualisierbare Felder
updatable_fields = ['username', 'email', 'name', 'role', 'active', 'department', 'position', 'phone', 'bio']
for field in updatable_fields:
if field in data:
setattr(user, field, data[field])
# Passwort separat behandeln
if 'password' in data and data['password']:
user.set_password(data['password'])
user.updated_at = datetime.now()
db_session.commit()
admin_logger.info(f"Benutzer {user.username} aktualisiert von Admin {current_user.username}")
db_session.close()
return jsonify({
"success": True,
"message": "Benutzer erfolgreich aktualisiert"
})
except Exception as e:
admin_logger.error(f"Fehler beim Aktualisieren des Benutzers: {str(e)}")
return jsonify({"error": "Fehler beim Aktualisieren des Benutzers"}), 500
@admin_blueprint.route("/api/users/<int:user_id>", methods=["DELETE"])
@login_required
@admin_required
def delete_user_api(user_id):
"""API-Endpunkt zum Löschen eines Benutzers"""
try:
if user_id == current_user.id:
return jsonify({"error": "Sie können sich nicht selbst löschen"}), 400
db_session = get_db_session()
user = db_session.query(User).filter(User.id == user_id).first()
if not user:
db_session.close()
return jsonify({"error": "Benutzer nicht gefunden"}), 404
username = user.username
db_session.delete(user)
db_session.commit()
admin_logger.info(f"Benutzer {username} gelöscht von Admin {current_user.username}")
db_session.close()
return jsonify({
"success": True,
"message": "Benutzer erfolgreich gelöscht"
})
except Exception as e:
admin_logger.error(f"Fehler beim Löschen des Benutzers: {str(e)}")
return jsonify({"error": "Fehler beim Löschen des Benutzers"}), 500

View File

@ -1,489 +0,0 @@
"""
Admin-API Blueprint für erweiterte Verwaltungsfunktionen
Dieses Blueprint stellt zusätzliche Admin-API-Endpunkte bereit für:
- System-Backups
- Datenbank-Optimierung
- Cache-Verwaltung
Autor: MYP Team
Datum: 2025-06-01
"""
import os
import shutil
import zipfile
import sqlite3
import glob
from datetime import datetime, timedelta
from flask import Blueprint, request, jsonify, current_app
from flask_login import login_required, current_user
from functools import wraps
from utils.logging_config import get_logger
# Blueprint erstellen
admin_api_blueprint = Blueprint('admin_api', __name__, url_prefix='/api/admin')
# Logger initialisieren
admin_logger = get_logger("admin_api")
def admin_required(f):
"""Decorator um sicherzustellen, dass nur Admins auf Endpunkte zugreifen können."""
@wraps(f)
@login_required
def decorated_function(*args, **kwargs):
if not current_user.is_authenticated or current_user.role != 'admin':
admin_logger.warning(f"Unauthorized admin access attempt by user {getattr(current_user, 'id', 'anonymous')}")
return jsonify({'error': 'Admin-Berechtigung erforderlich'}), 403
return f(*args, **kwargs)
return decorated_function
@admin_api_blueprint.route('/backup/create', methods=['POST'])
@admin_required
def create_backup():
"""
Erstellt ein manuelles System-Backup.
Erstellt eine Sicherung aller wichtigen Systemdaten einschließlich
Datenbank, Konfigurationsdateien und Benutzer-Uploads.
Returns:
JSON: Erfolgs-Status und Backup-Informationen
"""
try:
admin_logger.info(f"Backup-Erstellung angefordert von Admin {current_user.username}")
# Backup-Verzeichnis sicherstellen
backup_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'database', 'backups')
os.makedirs(backup_dir, exist_ok=True)
# Eindeutigen Backup-Namen erstellen
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
backup_name = f"system_backup_{timestamp}.zip"
backup_path = os.path.join(backup_dir, backup_name)
created_files = []
backup_size = 0
with zipfile.ZipFile(backup_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
# 1. Datenbank-Datei hinzufügen
try:
from utils.settings import DATABASE_PATH
if os.path.exists(DATABASE_PATH):
zipf.write(DATABASE_PATH, 'database/main.db')
created_files.append('database/main.db')
admin_logger.debug("✅ Hauptdatenbank zur Sicherung hinzugefügt")
# WAL- und SHM-Dateien falls vorhanden
wal_path = DATABASE_PATH + '-wal'
shm_path = DATABASE_PATH + '-shm'
if os.path.exists(wal_path):
zipf.write(wal_path, 'database/main.db-wal')
created_files.append('database/main.db-wal')
if os.path.exists(shm_path):
zipf.write(shm_path, 'database/main.db-shm')
created_files.append('database/main.db-shm')
except Exception as db_error:
admin_logger.warning(f"Fehler beim Hinzufügen der Datenbank: {str(db_error)}")
# 2. Konfigurationsdateien
try:
config_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'config')
if os.path.exists(config_dir):
for root, dirs, files in os.walk(config_dir):
for file in files:
if file.endswith(('.py', '.json', '.yaml', '.yml', '.toml')):
file_path = os.path.join(root, file)
arc_path = os.path.relpath(file_path, os.path.dirname(os.path.dirname(__file__)))
zipf.write(file_path, arc_path)
created_files.append(arc_path)
admin_logger.debug("✅ Konfigurationsdateien zur Sicherung hinzugefügt")
except Exception as config_error:
admin_logger.warning(f"Fehler beim Hinzufügen der Konfiguration: {str(config_error)}")
# 3. Wichtige User-Uploads (limitiert auf die letzten 1000 Dateien)
try:
uploads_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'uploads')
if os.path.exists(uploads_dir):
file_count = 0
max_files = 1000 # Limit für Performance
for root, dirs, files in os.walk(uploads_dir):
for file in files[:max_files - file_count]:
if file_count >= max_files:
break
file_path = os.path.join(root, file)
file_size = os.path.getsize(file_path)
# Nur Dateien unter 50MB hinzufügen
if file_size < 50 * 1024 * 1024:
arc_path = os.path.relpath(file_path, os.path.dirname(os.path.dirname(__file__)))
zipf.write(file_path, arc_path)
created_files.append(arc_path)
file_count += 1
if file_count >= max_files:
break
admin_logger.debug(f"{file_count} Upload-Dateien zur Sicherung hinzugefügt")
except Exception as uploads_error:
admin_logger.warning(f"Fehler beim Hinzufügen der Uploads: {str(uploads_error)}")
# 4. System-Logs (nur die letzten 100 Log-Dateien)
try:
logs_dir = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'logs')
if os.path.exists(logs_dir):
log_files = []
for root, dirs, files in os.walk(logs_dir):
for file in files:
if file.endswith(('.log', '.txt')):
file_path = os.path.join(root, file)
log_files.append((file_path, os.path.getmtime(file_path)))
# Sortiere nach Datum (neueste zuerst) und nimm nur die letzten 100
log_files.sort(key=lambda x: x[1], reverse=True)
for file_path, _ in log_files[:100]:
arc_path = os.path.relpath(file_path, os.path.dirname(os.path.dirname(__file__)))
zipf.write(file_path, arc_path)
created_files.append(arc_path)
admin_logger.debug(f"{len(log_files[:100])} Log-Dateien zur Sicherung hinzugefügt")
except Exception as logs_error:
admin_logger.warning(f"Fehler beim Hinzufügen der Logs: {str(logs_error)}")
# Backup-Größe bestimmen
if os.path.exists(backup_path):
backup_size = os.path.getsize(backup_path)
admin_logger.info(f"✅ System-Backup erfolgreich erstellt: {backup_name} ({backup_size / 1024 / 1024:.2f} MB)")
return jsonify({
'success': True,
'message': f'Backup erfolgreich erstellt: {backup_name}',
'backup_info': {
'filename': backup_name,
'size_bytes': backup_size,
'size_mb': round(backup_size / 1024 / 1024, 2),
'files_count': len(created_files),
'created_at': datetime.now().isoformat(),
'path': backup_path
}
})
except Exception as e:
admin_logger.error(f"❌ Fehler beim Erstellen des Backups: {str(e)}")
return jsonify({
'success': False,
'message': f'Fehler beim Erstellen des Backups: {str(e)}'
}), 500
@admin_api_blueprint.route('/database/optimize', methods=['POST'])
@admin_required
def optimize_database():
"""
Führt Datenbank-Optimierung durch.
Optimiert die SQLite-Datenbank durch VACUUM, ANALYZE und weitere
Wartungsoperationen für bessere Performance.
Returns:
JSON: Erfolgs-Status und Optimierungs-Statistiken
"""
try:
admin_logger.info(f"Datenbank-Optimierung angefordert von Admin {current_user.username}")
from utils.settings import DATABASE_PATH
optimization_results = {
'vacuum_completed': False,
'analyze_completed': False,
'integrity_check': False,
'wal_checkpoint': False,
'size_before': 0,
'size_after': 0,
'space_saved': 0
}
# Datenbankgröße vor Optimierung
if os.path.exists(DATABASE_PATH):
optimization_results['size_before'] = os.path.getsize(DATABASE_PATH)
# Verbindung zur Datenbank herstellen
conn = sqlite3.connect(DATABASE_PATH, timeout=30.0)
cursor = conn.cursor()
try:
# 1. Integritätsprüfung
admin_logger.debug("🔍 Führe Integritätsprüfung durch...")
cursor.execute("PRAGMA integrity_check")
integrity_result = cursor.fetchone()
optimization_results['integrity_check'] = integrity_result[0] == 'ok'
if not optimization_results['integrity_check']:
admin_logger.warning(f"⚠️ Integritätsprüfung ergab: {integrity_result[0]}")
else:
admin_logger.debug("✅ Integritätsprüfung erfolgreich")
# 2. WAL-Checkpoint (falls WAL-Modus aktiv)
try:
admin_logger.debug("🔄 Führe WAL-Checkpoint durch...")
cursor.execute("PRAGMA wal_checkpoint(TRUNCATE)")
optimization_results['wal_checkpoint'] = True
admin_logger.debug("✅ WAL-Checkpoint erfolgreich")
except Exception as wal_error:
admin_logger.debug(f" WAL-Checkpoint nicht möglich: {str(wal_error)}")
# 3. ANALYZE - Statistiken aktualisieren
admin_logger.debug("📊 Aktualisiere Datenbank-Statistiken...")
cursor.execute("ANALYZE")
optimization_results['analyze_completed'] = True
admin_logger.debug("✅ ANALYZE erfolgreich")
# 4. VACUUM - Datenbank komprimieren und reorganisieren
admin_logger.debug("🗜️ Komprimiere und reorganisiere Datenbank...")
cursor.execute("VACUUM")
optimization_results['vacuum_completed'] = True
admin_logger.debug("✅ VACUUM erfolgreich")
# 5. Performance-Optimierungen
try:
# Cache-Größe optimieren
cursor.execute("PRAGMA cache_size = 10000") # 10MB Cache
# Journal-Modus auf WAL setzen für bessere Concurrent-Performance
cursor.execute("PRAGMA journal_mode = WAL")
# Synchronous auf NORMAL für Balance zwischen Performance und Sicherheit
cursor.execute("PRAGMA synchronous = NORMAL")
# Page-Größe optimieren (falls noch nicht gesetzt)
cursor.execute("PRAGMA page_size = 4096")
admin_logger.debug("✅ Performance-Optimierungen angewendet")
except Exception as perf_error:
admin_logger.warning(f"⚠️ Performance-Optimierungen teilweise fehlgeschlagen: {str(perf_error)}")
finally:
cursor.close()
conn.close()
# Datenbankgröße nach Optimierung
if os.path.exists(DATABASE_PATH):
optimization_results['size_after'] = os.path.getsize(DATABASE_PATH)
optimization_results['space_saved'] = optimization_results['size_before'] - optimization_results['size_after']
# Ergebnisse loggen
space_saved_mb = optimization_results['space_saved'] / 1024 / 1024
admin_logger.info(f"✅ Datenbank-Optimierung abgeschlossen - {space_saved_mb:.2f} MB Speicher gespart")
return jsonify({
'success': True,
'message': 'Datenbank erfolgreich optimiert',
'results': {
'vacuum_completed': optimization_results['vacuum_completed'],
'analyze_completed': optimization_results['analyze_completed'],
'integrity_check_passed': optimization_results['integrity_check'],
'wal_checkpoint_completed': optimization_results['wal_checkpoint'],
'size_before_mb': round(optimization_results['size_before'] / 1024 / 1024, 2),
'size_after_mb': round(optimization_results['size_after'] / 1024 / 1024, 2),
'space_saved_mb': round(space_saved_mb, 2),
'optimization_timestamp': datetime.now().isoformat()
}
})
except Exception as e:
admin_logger.error(f"❌ Fehler bei Datenbank-Optimierung: {str(e)}")
return jsonify({
'success': False,
'message': f'Fehler bei Datenbank-Optimierung: {str(e)}'
}), 500
@admin_api_blueprint.route('/cache/clear', methods=['POST'])
@admin_required
def clear_cache():
"""
Leert den System-Cache.
Entfernt alle temporären Dateien, Cache-Verzeichnisse und
Python-Bytecode um Speicher freizugeben und Performance zu verbessern.
Returns:
JSON: Erfolgs-Status und Lösch-Statistiken
"""
try:
admin_logger.info(f"Cache-Leerung angefordert von Admin {current_user.username}")
cleared_stats = {
'files_deleted': 0,
'dirs_deleted': 0,
'space_freed': 0,
'categories': {}
}
app_root = os.path.dirname(os.path.dirname(__file__))
# 1. Python-Bytecode-Cache leeren (__pycache__)
try:
pycache_count = 0
pycache_size = 0
for root, dirs, files in os.walk(app_root):
if '__pycache__' in root:
for file in files:
file_path = os.path.join(root, file)
try:
pycache_size += os.path.getsize(file_path)
os.remove(file_path)
pycache_count += 1
except Exception:
pass
# Versuche das __pycache__-Verzeichnis zu löschen
try:
os.rmdir(root)
cleared_stats['dirs_deleted'] += 1
except Exception:
pass
cleared_stats['categories']['python_bytecode'] = {
'files': pycache_count,
'size_mb': round(pycache_size / 1024 / 1024, 2)
}
cleared_stats['files_deleted'] += pycache_count
cleared_stats['space_freed'] += pycache_size
admin_logger.debug(f"✅ Python-Bytecode-Cache: {pycache_count} Dateien, {pycache_size / 1024 / 1024:.2f} MB")
except Exception as pycache_error:
admin_logger.warning(f"⚠️ Fehler beim Leeren des Python-Cache: {str(pycache_error)}")
# 2. Temporäre Dateien im uploads/temp Verzeichnis
try:
temp_count = 0
temp_size = 0
temp_dir = os.path.join(app_root, 'uploads', 'temp')
if os.path.exists(temp_dir):
for root, dirs, files in os.walk(temp_dir):
for file in files:
file_path = os.path.join(root, file)
try:
temp_size += os.path.getsize(file_path)
os.remove(file_path)
temp_count += 1
except Exception:
pass
cleared_stats['categories']['temp_uploads'] = {
'files': temp_count,
'size_mb': round(temp_size / 1024 / 1024, 2)
}
cleared_stats['files_deleted'] += temp_count
cleared_stats['space_freed'] += temp_size
admin_logger.debug(f"✅ Temporäre Upload-Dateien: {temp_count} Dateien, {temp_size / 1024 / 1024:.2f} MB")
except Exception as temp_error:
admin_logger.warning(f"⚠️ Fehler beim Leeren des Temp-Verzeichnisses: {str(temp_error)}")
# 3. System-Cache-Verzeichnisse (falls vorhanden)
try:
cache_count = 0
cache_size = 0
cache_dirs = [
os.path.join(app_root, 'static', 'cache'),
os.path.join(app_root, 'cache'),
os.path.join(app_root, '.cache')
]
for cache_dir in cache_dirs:
if os.path.exists(cache_dir):
for root, dirs, files in os.walk(cache_dir):
for file in files:
file_path = os.path.join(root, file)
try:
cache_size += os.path.getsize(file_path)
os.remove(file_path)
cache_count += 1
except Exception:
pass
cleared_stats['categories']['system_cache'] = {
'files': cache_count,
'size_mb': round(cache_size / 1024 / 1024, 2)
}
cleared_stats['files_deleted'] += cache_count
cleared_stats['space_freed'] += cache_size
admin_logger.debug(f"✅ System-Cache: {cache_count} Dateien, {cache_size / 1024 / 1024:.2f} MB")
except Exception as cache_error:
admin_logger.warning(f"⚠️ Fehler beim Leeren des System-Cache: {str(cache_error)}")
# 4. Alte Log-Dateien (älter als 30 Tage)
try:
logs_count = 0
logs_size = 0
logs_dir = os.path.join(app_root, 'logs')
cutoff_date = datetime.now().timestamp() - (30 * 24 * 60 * 60) # 30 Tage
if os.path.exists(logs_dir):
for root, dirs, files in os.walk(logs_dir):
for file in files:
if file.endswith(('.log', '.log.1', '.log.2', '.log.3')):
file_path = os.path.join(root, file)
try:
if os.path.getmtime(file_path) < cutoff_date:
logs_size += os.path.getsize(file_path)
os.remove(file_path)
logs_count += 1
except Exception:
pass
cleared_stats['categories']['old_logs'] = {
'files': logs_count,
'size_mb': round(logs_size / 1024 / 1024, 2)
}
cleared_stats['files_deleted'] += logs_count
cleared_stats['space_freed'] += logs_size
admin_logger.debug(f"✅ Alte Log-Dateien: {logs_count} Dateien, {logs_size / 1024 / 1024:.2f} MB")
except Exception as logs_error:
admin_logger.warning(f"⚠️ Fehler beim Leeren alter Log-Dateien: {str(logs_error)}")
# 5. Application-Level Cache leeren (falls Models-Cache existiert)
try:
from models import clear_model_cache
clear_model_cache()
admin_logger.debug("✅ Application-Level Cache geleert")
except (ImportError, AttributeError):
admin_logger.debug(" Kein Application-Level Cache verfügbar")
# Ergebnisse zusammenfassen
total_space_mb = cleared_stats['space_freed'] / 1024 / 1024
admin_logger.info(f"✅ Cache-Leerung abgeschlossen: {cleared_stats['files_deleted']} Dateien, {total_space_mb:.2f} MB freigegeben")
return jsonify({
'success': True,
'message': f'Cache erfolgreich geleert - {total_space_mb:.2f} MB freigegeben',
'statistics': {
'total_files_deleted': cleared_stats['files_deleted'],
'total_dirs_deleted': cleared_stats['dirs_deleted'],
'total_space_freed_mb': round(total_space_mb, 2),
'categories': cleared_stats['categories'],
'cleanup_timestamp': datetime.now().isoformat()
}
})
except Exception as e:
admin_logger.error(f"❌ Fehler beim Leeren des Cache: {str(e)}")
return jsonify({
'success': False,
'message': f'Fehler beim Leeren des Cache: {str(e)}'
}), 500