🔋 Energiemonitoring
-- Überwachen Sie den Energieverbrauch Ihrer 3D-Drucker in Echtzeit -
-Sie haben keine Berechtigung für diese Aktion.
", 403 - -@app.errorhandler(404) -def not_found_error(error): - """404-Fehlerseite - Seite nicht gefunden""" - app_logger.info(f"Not Found (404): {request.url}") - if request.is_json: - return jsonify({ - "error": "Nicht gefunden", - "message": "Die angeforderte Ressource wurde nicht gefunden", - "status_code": 404 - }), 404 - - try: - return render_template('errors/404.html'), 404 - except Exception as template_error: - # Fallback bei Template-Fehlern - app_logger.error(f"Template-Fehler in 404-Handler: {str(template_error)}") - return f"Die angeforderte Seite wurde nicht gefunden.
", 404 - -@app.errorhandler(405) -def method_not_allowed_error(error): - """405-Fehlerseite - Methode nicht erlaubt""" - app_logger.warning(f"Method Not Allowed (405): {request.method} {request.url}") - if request.is_json: - return jsonify({ - "error": "Methode nicht erlaubt", - "message": f"Die HTTP-Methode {request.method} ist für diese URL nicht erlaubt", - "status_code": 405 - }), 405 - return render_template('errors/405.html'), 405 - -@app.errorhandler(413) -def payload_too_large_error(error): - """413-Fehlerseite - Datei zu groß""" - app_logger.warning(f"Payload Too Large (413): {request.url}") - if request.is_json: - return jsonify({ - "error": "Datei zu groß", - "message": "Die hochgeladene Datei ist zu groß", - "status_code": 413 - }), 413 - return render_template('errors/413.html'), 413 - -@app.errorhandler(429) -def rate_limit_error(error): - """429-Fehlerseite - Zu viele Anfragen""" - app_logger.warning(f"Rate Limit Exceeded (429): {request.url} - IP: {request.remote_addr}") - if request.is_json: - return jsonify({ - "error": "Zu viele Anfragen", - "message": "Sie haben zu viele Anfragen gesendet. Bitte versuchen Sie es später erneut", - "status_code": 429 - }), 429 - return render_template('errors/429.html'), 429 - -@app.errorhandler(500) -def internal_error(error): - """500-Fehlerseite - Interner Serverfehler""" - import traceback - error_id = datetime.now().strftime("%Y%m%d_%H%M%S") - - # Detailliertes Logging für Debugging - app_logger.error(f"Internal Server Error (500) - ID: {error_id}") - app_logger.error(f"URL: {request.url}") - app_logger.error(f"Method: {request.method}") - app_logger.error(f"User: {getattr(current_user, 'username', 'Anonymous')}") - app_logger.error(f"Error: {str(error)}") - app_logger.error(f"Traceback: {traceback.format_exc()}") - - if request.is_json: - return jsonify({ - "error": "Interner Serverfehler", - "message": "Ein unerwarteter Fehler ist aufgetreten. Bitte versuchen Sie es später erneut", - "error_id": error_id, - "status_code": 500 - }), 500 - - try: - return render_template('errors/500.html', error_id=error_id), 500 - except Exception as template_error: - # Fallback bei Template-Fehlern - app_logger.error(f"Template-Fehler in 500-Handler: {str(template_error)}") - return f"Ein unerwarteter Fehler ist aufgetreten. Fehler-ID: {error_id}
", 500 - -@app.errorhandler(502) -def bad_gateway_error(error): - """502-Fehlerseite - Bad Gateway""" - app_logger.error(f"Bad Gateway (502): {request.url}") - if request.is_json: - return jsonify({ - "error": "Gateway-Fehler", - "message": "Der Server ist vorübergehend nicht verfügbar", - "status_code": 502 - }), 502 - return render_template('errors/502.html'), 502 - -@app.errorhandler(503) -def service_unavailable_error(error): - """503-Fehlerseite - Service nicht verfügbar""" - app_logger.error(f"Service Unavailable (503): {request.url}") - if request.is_json: - return jsonify({ - "error": "Service nicht verfügbar", - "message": "Der Service ist vorübergehend nicht verfügbar. Bitte versuchen Sie es später erneut", - "status_code": 503 - }), 503 - return render_template('errors/503.html'), 503 - -@app.errorhandler(505) -def http_version_not_supported_error(error): - """505-Fehlerseite - HTTP-Version nicht unterstützt""" - app_logger.error(f"HTTP Version Not Supported (505): {request.url}") - if request.is_json: - return jsonify({ - "error": "HTTP-Version nicht unterstützt", - "message": "Die verwendete HTTP-Version wird vom Server nicht unterstützt", - "status_code": 505 - }), 505 - return render_template('errors/505.html'), 505 - -# Allgemeiner Exception-Handler für unbehandelte Ausnahmen -@app.errorhandler(Exception) -def handle_exception(error): - """Allgemeiner Handler für unbehandelte Ausnahmen""" - import traceback - error_id = datetime.now().strftime("%Y%m%d_%H%M%S") - - # Detailliertes Logging - app_logger.error(f"Unhandled Exception - ID: {error_id}") - app_logger.error(f"URL: {request.url}") - app_logger.error(f"Method: {request.method}") - app_logger.error(f"User: {getattr(current_user, 'username', 'Anonymous')}") - app_logger.error(f"Exception Type: {type(error).__name__}") - app_logger.error(f"Exception: {str(error)}") - app_logger.error(f"Traceback: {traceback.format_exc()}") - - # Für HTTP-Exceptions die ursprüngliche Behandlung verwenden - if hasattr(error, 'code'): - return error - - # Für alle anderen Exceptions als 500 behandeln - if request.is_json: - return jsonify({ - "error": "Unerwarteter Fehler", - "message": "Ein unerwarteter Fehler ist aufgetreten. Bitte versuchen Sie es später erneut", - "error_id": error_id, - "status_code": 500 - }), 500 - - try: - return render_template('errors/500.html', error_id=error_id), 500 - except Exception as template_error: - # Fallback bei Template-Fehlern - app_logger.error(f"Template-Fehler im Exception-Handler: {str(template_error)}") - return f"Ein unerwarteter Fehler ist aufgetreten. Fehler-ID: {error_id}
", 500 - -# ===== APP-FACTORY ===== -def create_app(config_name=None): - """ - Flask-App-Factory für Tests und modulare Initialisierung - - Args: - config_name: 'production', 'development' oder None (auto-detect) - - Returns: - Flask: Konfigurierte Flask-App-Instanz - """ - # Bestimme Konfiguration - if config_name is None: - config_name = get_environment_type() - - # Setze Environment-Variablen basierend auf config_name - if config_name == 'production': - os.environ['FLASK_ENV'] = 'production' - os.environ['USE_PRODUCTION_CONFIG'] = 'true' - else: - os.environ['FLASK_ENV'] = 'development' - os.environ['USE_PRODUCTION_CONFIG'] = 'false' - - # Globale Variablen neu setzen - global ENVIRONMENT_TYPE, USE_PRODUCTION_CONFIG, OFFLINE_MODE - ENVIRONMENT_TYPE = config_name - USE_PRODUCTION_CONFIG = (config_name == 'production') - OFFLINE_MODE = USE_PRODUCTION_CONFIG - - # App-Konfiguration anwenden - if USE_PRODUCTION_CONFIG: - apply_production_config(app) - app_logger.info(f"[FACTORY] ✅ Production-Konfiguration angewendet") - else: - apply_development_config(app) - app_logger.info(f"[FACTORY] ✅ Development-Konfiguration angewendet") - - # Session-Manager initialisieren - session_manager.init_app(app) - - # Sicherheitssuite initialisieren - try: - init_security(app) - app_logger.info("[FACTORY] ✅ Sicherheitssuite initialisiert") - except Exception as e: - app_logger.warning(f"[FACTORY] ⚠️ Sicherheitssuite-Fehler: {e}") - - app_logger.info(f"[FACTORY] 🏭 Flask-App erstellt ({config_name})") - return app - -# ===== HAUPTFUNKTION ===== -def main(): - """Hauptfunktion zum Starten der Anwendung""" - try: - # Umgebungsinfo loggen - app_logger.info(f"[STARTUP] 🚀 Starte MYP {ENVIRONMENT_TYPE.upper()}-Umgebung") - app_logger.info(f"[STARTUP] 🏢 {getattr(ProductionConfig, 'COMPANY_NAME', 'Mercedes-Benz TBA Marienfelde')}") - app_logger.info(f"[STARTUP] 🔒 Air-Gapped: {OFFLINE_MODE or getattr(ProductionConfig, 'OFFLINE_MODE', False)}") - - # Production-spezifische Initialisierung - if USE_PRODUCTION_CONFIG: - app_logger.info("[PRODUCTION] Initialisiere Production-Systeme...") - - # Performance-Monitoring aktivieren - if getattr(ProductionConfig, 'ENABLE_PERFORMANCE_MONITORING', False): - try: - from utils.monitoring_analytics import performance_tracker - # Performance monitoring initialized via global instance - app_logger.info("[PRODUCTION] ✅ Performance-Monitoring aktiviert") - except ImportError: - app_logger.warning("[PRODUCTION] ⚠️ Performance-Monitoring nicht verfügbar") - - # Health-Checks aktivieren - if getattr(ProductionConfig, 'ENABLE_HEALTH_CHECKS', False): - try: - from utils.monitoring_analytics import get_health_check - # Simple health check initialization - app_logger.info("[PRODUCTION] ✅ Health-Checks aktiviert") - except ImportError: - app_logger.warning("[PRODUCTION] ⚠️ Health-Checks nicht verfügbar") - - # Audit-Logging aktivieren - if getattr(ProductionConfig, 'AUDIT_LOGGING', False): - try: - from utils.audit_logger import init_audit_logging - init_audit_logging(app) - app_logger.info("[PRODUCTION] ✅ Audit-Logging aktiviert") - except ImportError: - app_logger.warning("[PRODUCTION] ⚠️ Audit-Logging nicht verfügbar") - - # Datenbank initialisieren - app_logger.info("[STARTUP] Initialisiere Datenbank...") - init_database() - app_logger.info("[STARTUP] ✅ Datenbank initialisiert") - - # Initial-Admin erstellen falls nicht vorhanden - app_logger.info("[STARTUP] Prüfe Initial-Admin...") - create_initial_admin() - app_logger.info("[STARTUP] ✅ Admin-Benutzer geprüft") - - # Statische Drucker für TBA Marienfelde erstellen/aktualisieren - app_logger.info("[STARTUP] Initialisiere statische Drucker...") - from models import create_initial_printers - success = create_initial_printers() - if success: - app_logger.info("[STARTUP] ✅ Statische Drucker konfiguriert") - else: - app_logger.warning("[STARTUP] ⚠️ Fehler bei Drucker-Initialisierung") - - # Queue Manager starten - app_logger.info("[STARTUP] Starte Queue Manager...") - start_queue_manager() - app_logger.info("[STARTUP] ✅ Queue Manager gestartet") - - # Job Scheduler starten - app_logger.info("[STARTUP] Starte Job Scheduler...") - scheduler = get_job_scheduler() - if scheduler: - scheduler.start() - app_logger.info("[STARTUP] ✅ Job Scheduler gestartet") - else: - app_logger.warning("[STARTUP] ⚠️ Job Scheduler nicht verfügbar") - - # SSL-Kontext für Production - ssl_context = None - if USE_PRODUCTION_CONFIG: - app_logger.info("[PRODUCTION] Konfiguriere SSL...") - try: - from utils.ssl_suite import ssl_config - ssl_context = ssl_config.get_ssl_context() - app_logger.info("[PRODUCTION] ✅ SSL-Kontext konfiguriert") - except ImportError: - app_logger.warning("[PRODUCTION] ⚠️ SSL-Konfiguration nicht verfügbar") - - # Server-Konfiguration - host = os.getenv('FLASK_HOST', '0.0.0.0') - port = int(os.getenv('FLASK_PORT', 5000)) - - # Production-spezifische Server-Einstellungen - server_options = { - 'host': host, - 'port': port, - 'threaded': True - } - - if USE_PRODUCTION_CONFIG: - # Production-Server-Optimierungen - server_options.update({ - 'threaded': True, - 'processes': 1, # Für Air-Gapped Umgebung - 'use_reloader': False, - 'use_debugger': False - }) - - app_logger.info(f"[PRODUCTION] 🌐 Server startet auf https://{host}:{port}") - app_logger.info(f"[PRODUCTION] 🔧 Threaded: {server_options['threaded']}") - app_logger.info(f"[PRODUCTION] 🔒 SSL: {'Ja' if ssl_context else 'Nein'}") - else: - app_logger.info(f"[STARTUP] 🌐 Server startet auf http://{host}:{port}") - - # Server starten - if ssl_context: - server_options['ssl_context'] = ssl_context - app.run(**server_options) - else: - app.run(**server_options) - - except KeyboardInterrupt: - app_logger.info("[SHUTDOWN] 🛑 Shutdown durch Benutzer angefordert") - except Exception as e: - app_logger.error(f"[ERROR] ❌ Fehler beim Starten der Anwendung: {str(e)}") - if USE_PRODUCTION_CONFIG: - # Production-Fehlerbehandlung - import traceback - app_logger.error(f"[ERROR] Traceback: {traceback.format_exc()}") - raise - finally: - # Cleanup - app_logger.info("[SHUTDOWN] 🧹 Cleanup wird ausgeführt...") - try: - # Queue Manager stoppen - stop_queue_manager() - app_logger.info("[SHUTDOWN] ✅ Queue Manager gestoppt") - - # Scheduler stoppen - if 'scheduler' in locals() and scheduler: - scheduler.shutdown() - app_logger.info("[SHUTDOWN] ✅ Job Scheduler gestoppt") - - app_logger.info("[SHUTDOWN] ✅ Rate Limiter bereinigt") - - # Caches leeren - clear_user_cache() - clear_printer_status_cache() - app_logger.info("[SHUTDOWN] ✅ Caches geleert") - - if USE_PRODUCTION_CONFIG: - app_logger.info(f"[SHUTDOWN] 🏁 {ProductionConfig.COMPANY_NAME} System heruntergefahren") - else: - app_logger.info("[SHUTDOWN] 🏁 System heruntergefahren") - - except Exception as cleanup_error: - app_logger.error(f"[SHUTDOWN] ❌ Cleanup-Fehler: {str(cleanup_error)}") - -# Production-spezifische Funktionen -def get_production_info(): - """Gibt Production-Informationen zurück""" - if USE_PRODUCTION_CONFIG: - return { - 'company': ProductionConfig.COMPANY_NAME, - 'environment': ProductionConfig.ENVIRONMENT_NAME, - 'offline_mode': ProductionConfig.OFFLINE_MODE, - 'compliance_mode': ProductionConfig.COMPLIANCE_MODE, - 'version': '1.0.0', - 'build_date': datetime.now().strftime('%Y-%m-%d'), - 'ssl_enabled': USE_PRODUCTION_CONFIG - } - return None - -# Template-Funktion für Production-Info -@app.template_global() -def production_info(): - """Stellt Production-Informationen für Templates bereit""" - return get_production_info() - -# Nach der Initialisierung der Blueprints und vor dem App-Start -try: - # Admin-Berechtigungen beim Start korrigieren - from utils.permissions import fix_all_admin_permissions - result = fix_all_admin_permissions() - if result['success']: - app_logger.info(f"Admin-Berechtigungen beim Start korrigiert: {result['created']} erstellt, {result['corrected']} aktualisiert") - else: - app_logger.warning(f"Fehler beim Korrigieren der Admin-Berechtigungen: {result.get('error', 'Unbekannter Fehler')}") -except Exception as e: - app_logger.error(f"Fehler beim Korrigieren der Admin-Berechtigungen beim Start: {str(e)}") - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/backend/blueprints/admin_unified.py.backup_query_opt_20250619_210050 b/backend/blueprints/admin_unified.py.backup_query_opt_20250619_210050 deleted file mode 100644 index 46edf8c6c..000000000 --- a/backend/blueprints/admin_unified.py.backup_query_opt_20250619_210050 +++ /dev/null @@ -1,3509 +0,0 @@ -""" -Vereinheitlichtes Admin-Blueprint für das MYP System - -Konsolidiert alle administrativen Funktionen in einem einzigen Blueprint: -- Admin-Dashboard und Übersichtsseiten -- Benutzer- und Druckerverwaltung -- System-Wartung und -überwachung -- API-Endpunkte für alle Admin-Funktionen - -Optimiert für die Mercedes-Benz TBA Marienfelde Umgebung mit: -- Einheitlichem Error-Handling und Logging -- Konsistentem Session-Management -- Vollständiger API-Kompatibilität - -Autor: MYP Team - Konsolidiert für IHK-Projektarbeit -Datum: 2025-06-09 -""" - -import os -import json -import time -import zipfile -import bcrypt -from datetime import datetime, timedelta -from functools import wraps - -from flask import Blueprint, render_template, request, jsonify, flash, redirect, url_for, current_app -from flask_login import login_required, current_user -from werkzeug.utils import secure_filename -from sqlalchemy import text, func, desc, asc -from sqlalchemy.exc import SQLAlchemyError - -# Models und Utils importieren -from models import ( - User, UserPermission, Printer, Job, GuestRequest, SystemLog, - get_db_session, get_cached_session, PlugStatusLog -) -from utils.logging_config import get_logger, measure_execution_time - -# ===== BLUEPRINT-KONFIGURATION ===== - -# Haupt-Blueprint für Admin-UI (Templates) -admin_blueprint = Blueprint('admin', __name__, url_prefix='/admin') - -# API-Blueprint für erweiterte System-Funktionen -admin_api_blueprint = Blueprint('admin_api', __name__, url_prefix='/api/admin') - -# Logger für beide Funktionsbereiche -admin_logger = get_logger("admin") -admin_api_logger = get_logger("admin_api") - -# ===== EINHEITLICHER ADMIN-DECORATOR ===== - -def admin_required(f): - """ - Vereinheitlichter Decorator für Admin-Berechtigung. - - Kombiniert die beste Praxis aus beiden ursprünglichen Implementierungen: - - Umfassende Logging-Funktionalität von admin.py - - Robuste Authentifizierungsprüfung von admin_api.py - """ - @wraps(f) - @login_required - def decorated_function(*args, **kwargs): - # Detaillierte Authentifizierungsprüfung - is_authenticated = current_user.is_authenticated - user_id = current_user.id if is_authenticated else 'Anonymous' - - # Doppelte Admin-Prüfung für maximale Sicherheit - is_admin = False - if is_authenticated: - # Methode 1: Property-basierte Prüfung (admin.py-Stil) - is_admin = hasattr(current_user, 'is_admin') and current_user.is_admin - - # Methode 2: Role-basierte Prüfung (admin_api.py-Stil) als Fallback - if not is_admin and hasattr(current_user, 'role'): - is_admin = current_user.role == 'admin' - - # Umfassendes Logging - admin_logger.info( - f"Admin-Check für Funktion {f.__name__}: " - f"User authenticated: {is_authenticated}, " - f"User ID: {user_id}, " - f"Is Admin: {is_admin}" - ) - - if not is_admin: - admin_logger.warning( - f"Admin-Zugriff verweigert für User {user_id} auf Funktion {f.__name__}" - ) - # Unterscheidung zwischen UI- und API-Routen - if request.path.startswith('/api/'): - return jsonify({ - "error": "Nur Administratoren haben Zugriff", - "message": "Admin-Berechtigung erforderlich" - }), 403 - else: - flash("Nur Administratoren haben Zugriff auf diesen Bereich", "error") - return redirect(url_for('dashboard')) - - return f(*args, **kwargs) - return decorated_function - -# ===== ADMIN-UI ROUTEN (ursprünglich admin.py) ===== - -@admin_blueprint.route("/") -@admin_required -def admin_dashboard(): - """Admin-Dashboard-Hauptseite mit Systemstatistiken""" - try: - with get_cached_session() as 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() - - # Online-Drucker zählen (ohne Live-Status-Check für bessere Performance) - online_printers = db_session.query(Printer).filter( - Printer.status == 'online' - ).count() - - stats = { - 'total_users': total_users, - 'total_printers': total_printers, - 'total_jobs': total_jobs, - 'active_jobs': active_jobs, - 'online_printers': online_printers - } - - admin_logger.info(f"Admin-Dashboard geladen von {current_user.username}") - return render_template('admin_modern.html', stats=stats, active_tab=None) - - 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.html', stats={}, active_tab=None) - -@admin_blueprint.route("/plug-schedules") -@admin_required -def admin_plug_schedules(): - """ - Administrator-Übersicht für Steckdosenschaltzeiten. - Zeigt detaillierte Historie aller Smart Plug Schaltzeiten mit Kalenderansicht. - """ - admin_logger.info(f"Admin {current_user.username} (ID: {current_user.id}) öffnet Steckdosenschaltzeiten") - - try: - # Statistiken für die letzten 24 Stunden abrufen - stats_24h = PlugStatusLog.get_status_statistics(hours=24) - - # Alle Drucker für Filter-Dropdown - with get_cached_session() as db_session: - # Alle Drucker für Auswahlfelder anzeigen (unabhängig von active-Status) - printers = db_session.query(Printer).all() - - return render_template('admin_plug_schedules.html', - stats=stats_24h, - printers=printers, - page_title="Steckdosenschaltzeiten", - breadcrumb=[ - {"name": "Admin-Dashboard", "url": url_for("admin.admin_dashboard")}, - {"name": "Steckdosenschaltzeiten", "url": "#"} - ]) - - except Exception as e: - admin_logger.error(f"Fehler beim Laden der Steckdosenschaltzeiten-Seite: {str(e)}") - flash("Fehler beim Laden der Steckdosenschaltzeiten-Daten.", "error") - return redirect(url_for("admin.admin_dashboard")) - -@admin_blueprint.route("/users") -@admin_required -def users_overview(): - """Benutzerübersicht für Administratoren""" - try: - with get_cached_session() as db_session: - # Alle Benutzer laden - users = db_session.query(User).order_by(User.created_at.desc()).all() - - # Grundlegende Statistiken sammeln - total_users = len(users) - 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() - - stats = { - 'total_users': total_users, - 'total_printers': total_printers, - 'total_jobs': total_jobs, - 'active_jobs': active_jobs, - 'online_printers': 0 - } - - admin_logger.info(f"Benutzerübersicht geladen von {current_user.username}") - return render_template('admin.html', stats=stats, users=users, active_tab='users') - - except Exception as e: - admin_logger.error(f"Fehler beim Laden der Benutzerübersicht: {str(e)}") - flash("Fehler beim Laden der Benutzerdaten", "error") - return render_template('admin.html', stats={}, users=[], active_tab='users') - -@admin_blueprint.route("/users/add", methods=["GET", "POST"]) -@admin_required -def add_user_page(): - """Seite zum Hinzufügen eines neuen Benutzers""" - if request.method == "POST": - # Form-Daten direkt verarbeiten - try: - data = request.form.to_dict() - # Checkbox-Werte korrekt parsen - for key in ['can_start_jobs', 'needs_approval', 'can_approve_jobs']: - if key in data: - data[key] = data[key] in ['true', 'on', '1', True] - else: - data[key] = False - - admin_logger.info(f"Benutzer-Erstellung (HTML-Form) angefordert von {current_user.username}: {data.get('username', 'unknown')}") - - # Validierung der erforderlichen Felder - required_fields = ['username', 'email', 'password', 'name'] - for field in required_fields: - if field not in data or not data[field]: - flash(f"Feld '{field}' ist erforderlich", "error") - return render_template('admin_add_user.html') - - with get_cached_session() as db_session: - # Prüfe auf bereits existierende E-Mail oder Benutzername - existing_user = db_session.query(User).filter( - (User.email == data['email']) | (User.username == data['username']) - ).first() - - if existing_user: - if existing_user.email == data['email']: - flash("E-Mail-Adresse bereits vergeben", "error") - else: - flash("Benutzername bereits vergeben", "error") - return render_template('admin_add_user.html') - - # 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'), - active=True, - created_at=datetime.now() - ) - new_user.set_password(data['password']) - - db_session.add(new_user) - db_session.flush() # ID generieren für UserPermission - - # Granulare Berechtigungen erstellen - from models import UserPermission - permissions = UserPermission( - user_id=new_user.id, - can_start_jobs=data.get('can_start_jobs', True), - needs_approval=data.get('needs_approval', False), - can_approve_jobs=data.get('can_approve_jobs', False) - ) - - # Administratoren bekommen automatisch Genehmigungsrechte - if new_user.role == 'admin': - permissions.can_approve_jobs = True - permissions.can_start_jobs = True - permissions.needs_approval = False - - db_session.add(permissions) - db_session.commit() - - flash(f"Benutzer '{new_user.username}' erfolgreich erstellt", "success") - admin_logger.info(f"✅ Neuer Benutzer erfolgreich erstellt: {new_user.username} (ID: {new_user.id}) von Admin {current_user.username}") - - return redirect(url_for('admin.users_overview')) - - except Exception as e: - admin_logger.error(f"❌ Fehler bei Benutzer-Erstellung (HTML-Form): {str(e)}") - flash("Fehler beim Erstellen des Benutzers", "error") - - return render_template('admin_add_user.html') - -@admin_blueprint.route("/users/Die von Ihnen gesuchte Seite existiert nicht oder wurde verschoben.
- - -- Es ist ein unerwarteter Fehler aufgetreten. Unser Team wurde automatisch benachrichtigt und arbeitet an einer Lösung. -
- - - - - -- Umfassende Statistiken und KPIs für die MYP 3D-Druck Platform -
-Diese Seite hilft bei der Diagnose und Behebung von CSRF-Token-Problemen.
- - -Meta-Tag Token:
-Session Token:
-JavaScript Token:
-- Überwachen Sie den Energieverbrauch Ihrer 3D-Drucker in Echtzeit -
-Sichere Testfunktion für Ausbilder und Administratoren
-