🎉 Feature: Optimized CSS build process for improved performance 🎉
This commit is contained in:
parent
549de9934c
commit
7c30ca2188
409
backend/app.py
409
backend/app.py
@ -22,6 +22,124 @@ import shutil
|
|||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
|
# ===== OPTIMIERTE KONFIGURATION FÜR RASPBERRY PI =====
|
||||||
|
class OptimizedConfig:
|
||||||
|
"""Configuration for performance-optimized deployment on Raspberry Pi"""
|
||||||
|
|
||||||
|
# Performance optimization flags
|
||||||
|
OPTIMIZED_MODE = True
|
||||||
|
USE_MINIFIED_ASSETS = True
|
||||||
|
DISABLE_ANIMATIONS = True
|
||||||
|
LIMIT_GLASSMORPHISM = True
|
||||||
|
|
||||||
|
# Flask performance settings
|
||||||
|
DEBUG = False
|
||||||
|
TESTING = False
|
||||||
|
SEND_FILE_MAX_AGE_DEFAULT = 31536000 # 1 year cache for static files
|
||||||
|
|
||||||
|
# Template settings
|
||||||
|
TEMPLATES_AUTO_RELOAD = False
|
||||||
|
EXPLAIN_TEMPLATE_LOADING = False
|
||||||
|
|
||||||
|
# Session configuration
|
||||||
|
SESSION_COOKIE_SECURE = True
|
||||||
|
SESSION_COOKIE_HTTPONLY = True
|
||||||
|
SESSION_COOKIE_SAMESITE = 'Lax'
|
||||||
|
|
||||||
|
# Performance optimizations
|
||||||
|
MAX_CONTENT_LENGTH = 16 * 1024 * 1024 # 16MB max upload
|
||||||
|
JSON_SORT_KEYS = False
|
||||||
|
JSONIFY_PRETTYPRINT_REGULAR = False
|
||||||
|
|
||||||
|
# Database optimizations
|
||||||
|
SQLALCHEMY_ECHO = False
|
||||||
|
SQLALCHEMY_TRACK_MODIFICATIONS = False
|
||||||
|
SQLALCHEMY_ENGINE_OPTIONS = {
|
||||||
|
'pool_size': 5,
|
||||||
|
'pool_recycle': 3600,
|
||||||
|
'pool_pre_ping': True,
|
||||||
|
'connect_args': {
|
||||||
|
'check_same_thread': False
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Cache configuration
|
||||||
|
CACHE_TYPE = 'simple'
|
||||||
|
CACHE_DEFAULT_TIMEOUT = 300
|
||||||
|
CACHE_KEY_PREFIX = 'myp_'
|
||||||
|
|
||||||
|
# Static file caching headers
|
||||||
|
SEND_FILE_MAX_AGE_DEFAULT = 31536000 # 1 year
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def init_app(app):
|
||||||
|
"""Initialize application with optimized settings"""
|
||||||
|
# Set optimized template
|
||||||
|
app.jinja_env.globals['optimized_mode'] = True
|
||||||
|
app.jinja_env.globals['base_template'] = 'base-optimized.html'
|
||||||
|
|
||||||
|
# Add cache headers for static files
|
||||||
|
@app.after_request
|
||||||
|
def add_cache_headers(response):
|
||||||
|
if 'static' in response.headers.get('Location', ''):
|
||||||
|
response.headers['Cache-Control'] = 'public, max-age=31536000'
|
||||||
|
response.headers['Vary'] = 'Accept-Encoding'
|
||||||
|
return response
|
||||||
|
|
||||||
|
# Disable unnecessary features
|
||||||
|
app.config['EXPLAIN_TEMPLATE_LOADING'] = False
|
||||||
|
app.config['TEMPLATES_AUTO_RELOAD'] = False
|
||||||
|
|
||||||
|
print("🚀 Running in OPTIMIZED mode for Raspberry Pi")
|
||||||
|
|
||||||
|
def detect_raspberry_pi():
|
||||||
|
"""Erkennt ob das System auf einem Raspberry Pi läuft"""
|
||||||
|
try:
|
||||||
|
# Prüfe auf Raspberry Pi Hardware
|
||||||
|
with open('/proc/cpuinfo', 'r') as f:
|
||||||
|
cpuinfo = f.read()
|
||||||
|
if 'Raspberry Pi' in cpuinfo or 'BCM' in cpuinfo:
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Prüfe auf ARM-Architektur
|
||||||
|
import platform
|
||||||
|
machine = platform.machine().lower()
|
||||||
|
if 'arm' in machine or 'aarch64' in machine:
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Umgebungsvariable für manuelle Aktivierung
|
||||||
|
return os.getenv('FORCE_OPTIMIZED_MODE', '').lower() in ['true', '1', 'yes']
|
||||||
|
|
||||||
|
def should_use_optimized_config():
|
||||||
|
"""Bestimmt ob die optimierte Konfiguration verwendet werden soll"""
|
||||||
|
# Kommandozeilen-Argument prüfen
|
||||||
|
if '--optimized' in sys.argv:
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Raspberry Pi-Erkennung
|
||||||
|
if detect_raspberry_pi():
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Umgebungsvariable
|
||||||
|
if os.getenv('USE_OPTIMIZED_CONFIG', '').lower() in ['true', '1', 'yes']:
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Schwache Hardware-Erkennung (weniger als 2GB RAM)
|
||||||
|
try:
|
||||||
|
import psutil
|
||||||
|
memory_gb = psutil.virtual_memory().total / (1024**3)
|
||||||
|
if memory_gb < 2.0:
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
# Windows-spezifische Fixes früh importieren (sichere Version)
|
# Windows-spezifische Fixes früh importieren (sichere Version)
|
||||||
if os.name == 'nt':
|
if os.name == 'nt':
|
||||||
try:
|
try:
|
||||||
@ -311,10 +429,76 @@ register_aggressive_shutdown()
|
|||||||
# Flask-App initialisieren
|
# Flask-App initialisieren
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
app.secret_key = SECRET_KEY
|
app.secret_key = SECRET_KEY
|
||||||
|
|
||||||
|
# ===== OPTIMIERTE KONFIGURATION ANWENDEN =====
|
||||||
|
# Prüfe ob optimierte Konfiguration verwendet werden soll
|
||||||
|
USE_OPTIMIZED_CONFIG = should_use_optimized_config()
|
||||||
|
|
||||||
|
if USE_OPTIMIZED_CONFIG:
|
||||||
|
app_logger.info("🚀 Aktiviere optimierte Konfiguration für schwache Hardware/Raspberry Pi")
|
||||||
|
|
||||||
|
# Optimierte Flask-Konfiguration anwenden
|
||||||
|
app.config.update({
|
||||||
|
"DEBUG": OptimizedConfig.DEBUG,
|
||||||
|
"TESTING": OptimizedConfig.TESTING,
|
||||||
|
"SEND_FILE_MAX_AGE_DEFAULT": OptimizedConfig.SEND_FILE_MAX_AGE_DEFAULT,
|
||||||
|
"TEMPLATES_AUTO_RELOAD": OptimizedConfig.TEMPLATES_AUTO_RELOAD,
|
||||||
|
"EXPLAIN_TEMPLATE_LOADING": OptimizedConfig.EXPLAIN_TEMPLATE_LOADING,
|
||||||
|
"SESSION_COOKIE_SECURE": OptimizedConfig.SESSION_COOKIE_SECURE,
|
||||||
|
"SESSION_COOKIE_HTTPONLY": OptimizedConfig.SESSION_COOKIE_HTTPONLY,
|
||||||
|
"SESSION_COOKIE_SAMESITE": OptimizedConfig.SESSION_COOKIE_SAMESITE,
|
||||||
|
"MAX_CONTENT_LENGTH": OptimizedConfig.MAX_CONTENT_LENGTH,
|
||||||
|
"JSON_SORT_KEYS": OptimizedConfig.JSON_SORT_KEYS,
|
||||||
|
"JSONIFY_PRETTYPRINT_REGULAR": OptimizedConfig.JSONIFY_PRETTYPRINT_REGULAR,
|
||||||
|
"SQLALCHEMY_ECHO": OptimizedConfig.SQLALCHEMY_ECHO,
|
||||||
|
"SQLALCHEMY_TRACK_MODIFICATIONS": OptimizedConfig.SQLALCHEMY_TRACK_MODIFICATIONS,
|
||||||
|
"SQLALCHEMY_ENGINE_OPTIONS": OptimizedConfig.SQLALCHEMY_ENGINE_OPTIONS
|
||||||
|
})
|
||||||
|
|
||||||
|
# Session-Konfiguration
|
||||||
|
app.config["PERMANENT_SESSION_LIFETIME"] = SESSION_LIFETIME
|
||||||
|
app.config["WTF_CSRF_ENABLED"] = True
|
||||||
|
|
||||||
|
# Jinja2-Globals für optimierte Templates
|
||||||
|
app.jinja_env.globals.update({
|
||||||
|
'optimized_mode': True,
|
||||||
|
'use_minified_assets': OptimizedConfig.USE_MINIFIED_ASSETS,
|
||||||
|
'disable_animations': OptimizedConfig.DISABLE_ANIMATIONS,
|
||||||
|
'limit_glassmorphism': OptimizedConfig.LIMIT_GLASSMORPHISM,
|
||||||
|
'base_template': 'base-optimized.html'
|
||||||
|
})
|
||||||
|
|
||||||
|
# Optimierte After-Request-Handler
|
||||||
|
@app.after_request
|
||||||
|
def add_optimized_cache_headers(response):
|
||||||
|
"""Fügt optimierte Cache-Header für statische Dateien hinzu"""
|
||||||
|
if request.endpoint == 'static' or '/static/' in request.path:
|
||||||
|
response.headers['Cache-Control'] = 'public, max-age=31536000'
|
||||||
|
response.headers['Vary'] = 'Accept-Encoding'
|
||||||
|
# Preload-Header für kritische Assets
|
||||||
|
if request.path.endswith(('.css', '.js')):
|
||||||
|
response.headers['X-Optimized-Asset'] = 'true'
|
||||||
|
return response
|
||||||
|
|
||||||
|
app_logger.info("✅ Optimierte Konfiguration aktiviert")
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Standard-Konfiguration
|
||||||
app.config["PERMANENT_SESSION_LIFETIME"] = SESSION_LIFETIME
|
app.config["PERMANENT_SESSION_LIFETIME"] = SESSION_LIFETIME
|
||||||
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
|
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
|
||||||
app.config["WTF_CSRF_ENABLED"] = True
|
app.config["WTF_CSRF_ENABLED"] = True
|
||||||
|
|
||||||
|
# Standard Jinja2-Globals
|
||||||
|
app.jinja_env.globals.update({
|
||||||
|
'optimized_mode': False,
|
||||||
|
'use_minified_assets': False,
|
||||||
|
'disable_animations': False,
|
||||||
|
'limit_glassmorphism': False,
|
||||||
|
'base_template': 'base.html'
|
||||||
|
})
|
||||||
|
|
||||||
|
app_logger.info("📋 Standard-Konfiguration verwendet")
|
||||||
|
|
||||||
# Globale db-Variable für Kompatibilität mit init_simple_db.py
|
# Globale db-Variable für Kompatibilität mit init_simple_db.py
|
||||||
db = db_engine
|
db = db_engine
|
||||||
|
|
||||||
@ -549,6 +733,23 @@ def format_datetime_filter(value, format='%d.%m.%Y %H:%M'):
|
|||||||
return value
|
return value
|
||||||
return value.strftime(format)
|
return value.strftime(format)
|
||||||
|
|
||||||
|
# Template-Helper für Optimierungsstatus
|
||||||
|
@app.template_global()
|
||||||
|
def is_optimized_mode():
|
||||||
|
"""Prüft ob die Anwendung im optimierten Modus läuft"""
|
||||||
|
return USE_OPTIMIZED_CONFIG
|
||||||
|
|
||||||
|
@app.template_global()
|
||||||
|
def get_optimization_info():
|
||||||
|
"""Gibt Optimierungsinformationen für Templates zurück"""
|
||||||
|
return {
|
||||||
|
'active': USE_OPTIMIZED_CONFIG,
|
||||||
|
'raspberry_pi': detect_raspberry_pi(),
|
||||||
|
'minified_assets': app.jinja_env.globals.get('use_minified_assets', False),
|
||||||
|
'disabled_animations': app.jinja_env.globals.get('disable_animations', False),
|
||||||
|
'limited_glassmorphism': app.jinja_env.globals.get('limit_glassmorphism', False)
|
||||||
|
}
|
||||||
|
|
||||||
# HTTP-Request/Response-Middleware für automatisches Debug-Logging
|
# HTTP-Request/Response-Middleware für automatisches Debug-Logging
|
||||||
@app.before_request
|
@app.before_request
|
||||||
def log_request_info():
|
def log_request_info():
|
||||||
@ -5610,6 +5811,22 @@ def admin_advanced_settings():
|
|||||||
user_settings = session.get('user_settings', {})
|
user_settings = session.get('user_settings', {})
|
||||||
optimization_settings = user_settings.get('optimization', default_settings)
|
optimization_settings = user_settings.get('optimization', default_settings)
|
||||||
|
|
||||||
|
# Performance-Optimierungs-Status hinzufügen
|
||||||
|
performance_optimization = {
|
||||||
|
'active': USE_OPTIMIZED_CONFIG,
|
||||||
|
'raspberry_pi_detected': detect_raspberry_pi(),
|
||||||
|
'forced_mode': os.getenv('FORCE_OPTIMIZED_MODE', '').lower() in ['true', '1', 'yes'],
|
||||||
|
'cli_mode': '--optimized' in sys.argv,
|
||||||
|
'current_settings': {
|
||||||
|
'minified_assets': app.jinja_env.globals.get('use_minified_assets', False),
|
||||||
|
'disabled_animations': app.jinja_env.globals.get('disable_animations', False),
|
||||||
|
'limited_glassmorphism': app.jinja_env.globals.get('limit_glassmorphism', False),
|
||||||
|
'template_caching': not app.config.get('TEMPLATES_AUTO_RELOAD', True),
|
||||||
|
'json_optimization': not app.config.get('JSON_SORT_KEYS', True),
|
||||||
|
'static_cache_age': app.config.get('SEND_FILE_MAX_AGE_DEFAULT', 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
# System-Statistiken sammeln
|
# System-Statistiken sammeln
|
||||||
stats = {
|
stats = {
|
||||||
'total_users': db_session.query(User).count(),
|
'total_users': db_session.query(User).count(),
|
||||||
@ -5659,6 +5876,7 @@ def admin_advanced_settings():
|
|||||||
'admin_advanced_settings.html',
|
'admin_advanced_settings.html',
|
||||||
title='Erweiterte Einstellungen',
|
title='Erweiterte Einstellungen',
|
||||||
optimization_settings=optimization_settings,
|
optimization_settings=optimization_settings,
|
||||||
|
performance_optimization=performance_optimization,
|
||||||
stats=stats,
|
stats=stats,
|
||||||
maintenance_info=maintenance_info
|
maintenance_info=maintenance_info
|
||||||
)
|
)
|
||||||
@ -5668,6 +5886,59 @@ def admin_advanced_settings():
|
|||||||
flash('Fehler beim Laden der erweiterten Einstellungen', 'error')
|
flash('Fehler beim Laden der erweiterten Einstellungen', 'error')
|
||||||
return redirect(url_for('admin_page'))
|
return redirect(url_for('admin_page'))
|
||||||
|
|
||||||
|
@app.route("/admin/performance-optimization")
|
||||||
|
@login_required
|
||||||
|
@admin_required
|
||||||
|
def admin_performance_optimization():
|
||||||
|
"""Performance-Optimierungs-Verwaltungsseite für Admins"""
|
||||||
|
try:
|
||||||
|
app_logger.info(f"🚀 Performance-Optimierung-Seite aufgerufen von Admin {current_user.username}")
|
||||||
|
|
||||||
|
# Aktuelle Optimierungseinstellungen sammeln
|
||||||
|
optimization_status = {
|
||||||
|
'mode_active': USE_OPTIMIZED_CONFIG,
|
||||||
|
'detection': {
|
||||||
|
'raspberry_pi': detect_raspberry_pi(),
|
||||||
|
'forced_mode': os.getenv('FORCE_OPTIMIZED_MODE', '').lower() in ['true', '1', 'yes'],
|
||||||
|
'cli_mode': '--optimized' in sys.argv,
|
||||||
|
'low_memory': False
|
||||||
|
},
|
||||||
|
'settings': {
|
||||||
|
'minified_assets': app.jinja_env.globals.get('use_minified_assets', False),
|
||||||
|
'disabled_animations': app.jinja_env.globals.get('disable_animations', False),
|
||||||
|
'limited_glassmorphism': app.jinja_env.globals.get('limit_glassmorphism', False),
|
||||||
|
'template_caching': not app.config.get('TEMPLATES_AUTO_RELOAD', True),
|
||||||
|
'json_optimization': not app.config.get('JSON_SORT_KEYS', True),
|
||||||
|
'debug_disabled': not app.config.get('DEBUG', False),
|
||||||
|
'secure_sessions': app.config.get('SESSION_COOKIE_SECURE', False)
|
||||||
|
},
|
||||||
|
'performance': {
|
||||||
|
'static_cache_age_hours': app.config.get('SEND_FILE_MAX_AGE_DEFAULT', 0) / 3600,
|
||||||
|
'max_upload_mb': app.config.get('MAX_CONTENT_LENGTH', 0) / (1024 * 1024) if app.config.get('MAX_CONTENT_LENGTH') else 0,
|
||||||
|
'sqlalchemy_echo': app.config.get('SQLALCHEMY_ECHO', True)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Memory-Erkennung hinzufügen
|
||||||
|
try:
|
||||||
|
import psutil
|
||||||
|
memory_gb = psutil.virtual_memory().total / (1024**3)
|
||||||
|
optimization_status['detection']['low_memory'] = memory_gb < 2.0
|
||||||
|
optimization_status['system_memory_gb'] = round(memory_gb, 2)
|
||||||
|
except ImportError:
|
||||||
|
optimization_status['system_memory_gb'] = None
|
||||||
|
|
||||||
|
return render_template(
|
||||||
|
'admin_performance_optimization.html',
|
||||||
|
title='Performance-Optimierung',
|
||||||
|
optimization_status=optimization_status
|
||||||
|
)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
app_logger.error(f"❌ Fehler beim Laden der Performance-Optimierung-Seite: {str(e)}")
|
||||||
|
flash('Fehler beim Laden der Performance-Optimierung-Seite', 'error')
|
||||||
|
return redirect(url_for('admin_page'))
|
||||||
|
|
||||||
@app.route('/api/admin/maintenance/cleanup-logs', methods=['POST'])
|
@app.route('/api/admin/maintenance/cleanup-logs', methods=['POST'])
|
||||||
@login_required
|
@login_required
|
||||||
@admin_required
|
@admin_required
|
||||||
@ -8100,6 +8371,128 @@ def api_admin_system_status():
|
|||||||
}), 500
|
}), 500
|
||||||
|
|
||||||
|
|
||||||
|
# ===== OPTIMIERUNGSSTATUS API =====
|
||||||
|
@app.route("/api/system/optimization-status", methods=['GET'])
|
||||||
|
def api_optimization_status():
|
||||||
|
"""
|
||||||
|
API-Endpunkt für den aktuellen Optimierungsstatus.
|
||||||
|
|
||||||
|
Gibt Informationen über aktivierte Optimierungen zurück.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
status = {
|
||||||
|
"optimized_mode_active": USE_OPTIMIZED_CONFIG,
|
||||||
|
"hardware_detected": {
|
||||||
|
"is_raspberry_pi": detect_raspberry_pi(),
|
||||||
|
"forced_optimization": os.getenv('FORCE_OPTIMIZED_MODE', '').lower() in ['true', '1', 'yes'],
|
||||||
|
"cli_optimization": '--optimized' in sys.argv
|
||||||
|
},
|
||||||
|
"active_optimizations": {
|
||||||
|
"minified_assets": app.jinja_env.globals.get('use_minified_assets', False),
|
||||||
|
"disabled_animations": app.jinja_env.globals.get('disable_animations', False),
|
||||||
|
"limited_glassmorphism": app.jinja_env.globals.get('limit_glassmorphism', False),
|
||||||
|
"cache_headers": USE_OPTIMIZED_CONFIG,
|
||||||
|
"template_caching": not app.config.get('TEMPLATES_AUTO_RELOAD', True),
|
||||||
|
"json_optimization": not app.config.get('JSON_SORT_KEYS', True)
|
||||||
|
},
|
||||||
|
"performance_settings": {
|
||||||
|
"max_upload_mb": app.config.get('MAX_CONTENT_LENGTH', 0) / (1024 * 1024) if app.config.get('MAX_CONTENT_LENGTH') else None,
|
||||||
|
"static_cache_age": app.config.get('SEND_FILE_MAX_AGE_DEFAULT', 0),
|
||||||
|
"sqlalchemy_echo": app.config.get('SQLALCHEMY_ECHO', True),
|
||||||
|
"session_secure": app.config.get('SESSION_COOKIE_SECURE', False)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Zusätzliche System-Informationen wenn verfügbar
|
||||||
|
try:
|
||||||
|
import psutil
|
||||||
|
import platform
|
||||||
|
|
||||||
|
status["system_info"] = {
|
||||||
|
"cpu_count": psutil.cpu_count(),
|
||||||
|
"memory_gb": round(psutil.virtual_memory().total / (1024**3), 2),
|
||||||
|
"platform": platform.machine(),
|
||||||
|
"system": platform.system()
|
||||||
|
}
|
||||||
|
except ImportError:
|
||||||
|
status["system_info"] = {"error": "psutil nicht verfügbar"}
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
"success": True,
|
||||||
|
"status": status,
|
||||||
|
"timestamp": datetime.now().isoformat()
|
||||||
|
})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
app_logger.error(f"Fehler beim Abrufen des Optimierungsstatus: {str(e)}")
|
||||||
|
return jsonify({
|
||||||
|
"success": False,
|
||||||
|
"error": str(e)
|
||||||
|
}), 500
|
||||||
|
|
||||||
|
@app.route("/api/admin/optimization/toggle", methods=['POST'])
|
||||||
|
@login_required
|
||||||
|
@admin_required
|
||||||
|
def api_admin_toggle_optimization():
|
||||||
|
"""
|
||||||
|
API-Endpunkt zum Umschalten der Optimierungen zur Laufzeit (nur Admins).
|
||||||
|
|
||||||
|
Achtung: Einige Optimierungen erfordern einen Neustart.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
data = request.get_json() or {}
|
||||||
|
|
||||||
|
# Welche Optimierung soll umgeschaltet werden?
|
||||||
|
optimization_type = data.get('type')
|
||||||
|
enabled = data.get('enabled', True)
|
||||||
|
|
||||||
|
changes_made = []
|
||||||
|
restart_required = False
|
||||||
|
|
||||||
|
if optimization_type == 'animations':
|
||||||
|
app.jinja_env.globals['disable_animations'] = enabled
|
||||||
|
changes_made.append(f"Animationen {'deaktiviert' if enabled else 'aktiviert'}")
|
||||||
|
|
||||||
|
elif optimization_type == 'glassmorphism':
|
||||||
|
app.jinja_env.globals['limit_glassmorphism'] = enabled
|
||||||
|
changes_made.append(f"Glassmorphism {'begrenzt' if enabled else 'vollständig'}")
|
||||||
|
|
||||||
|
elif optimization_type == 'minified_assets':
|
||||||
|
app.jinja_env.globals['use_minified_assets'] = enabled
|
||||||
|
changes_made.append(f"Minifizierte Assets {'aktiviert' if enabled else 'deaktiviert'}")
|
||||||
|
|
||||||
|
elif optimization_type == 'template_caching':
|
||||||
|
app.config['TEMPLATES_AUTO_RELOAD'] = not enabled
|
||||||
|
changes_made.append(f"Template-Caching {'aktiviert' if enabled else 'deaktiviert'}")
|
||||||
|
restart_required = True
|
||||||
|
|
||||||
|
elif optimization_type == 'debug_mode':
|
||||||
|
app.config['DEBUG'] = not enabled
|
||||||
|
changes_made.append(f"Debug-Modus {'deaktiviert' if enabled else 'aktiviert'}")
|
||||||
|
restart_required = True
|
||||||
|
|
||||||
|
else:
|
||||||
|
return jsonify({
|
||||||
|
"success": False,
|
||||||
|
"error": "Unbekannter Optimierungstyp"
|
||||||
|
}), 400
|
||||||
|
|
||||||
|
app_logger.info(f"Admin {current_user.username} hat Optimierung '{optimization_type}' auf {enabled} gesetzt")
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
"success": True,
|
||||||
|
"changes": changes_made,
|
||||||
|
"restart_required": restart_required,
|
||||||
|
"message": f"Optimierung '{optimization_type}' erfolgreich {'aktiviert' if enabled else 'deaktiviert'}"
|
||||||
|
})
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
app_logger.error(f"Fehler beim Umschalten der Optimierung: {str(e)}")
|
||||||
|
return jsonify({
|
||||||
|
"success": False,
|
||||||
|
"error": str(e)
|
||||||
|
}), 500
|
||||||
|
|
||||||
# ===== ÖFFENTLICHE STATISTIK-API =====
|
# ===== ÖFFENTLICHE STATISTIK-API =====
|
||||||
@app.route("/api/statistics/public", methods=['GET'])
|
@app.route("/api/statistics/public", methods=['GET'])
|
||||||
def api_public_statistics():
|
def api_public_statistics():
|
||||||
@ -9030,6 +9423,22 @@ if __name__ == "__main__":
|
|||||||
# Template-Hilfsfunktionen registrieren
|
# Template-Hilfsfunktionen registrieren
|
||||||
register_template_helpers(app)
|
register_template_helpers(app)
|
||||||
|
|
||||||
|
# Optimierungsstatus beim Start anzeigen
|
||||||
|
if USE_OPTIMIZED_CONFIG:
|
||||||
|
app_logger.info("🚀 === OPTIMIERTE KONFIGURATION AKTIV ===")
|
||||||
|
app_logger.info(f"📊 Hardware erkannt: Raspberry Pi={detect_raspberry_pi()}")
|
||||||
|
app_logger.info(f"⚙️ Erzwungen: {os.getenv('FORCE_OPTIMIZED_MODE', '').lower() in ['true', '1', 'yes']}")
|
||||||
|
app_logger.info(f"🔧 CLI-Parameter: {'--optimized' in sys.argv}")
|
||||||
|
app_logger.info("🔧 Aktive Optimierungen:")
|
||||||
|
app_logger.info(f" - Minifizierte Assets: {app.jinja_env.globals.get('use_minified_assets', False)}")
|
||||||
|
app_logger.info(f" - Animationen deaktiviert: {app.jinja_env.globals.get('disable_animations', False)}")
|
||||||
|
app_logger.info(f" - Glassmorphism begrenzt: {app.jinja_env.globals.get('limit_glassmorphism', False)}")
|
||||||
|
app_logger.info(f" - Template-Caching: {not app.config.get('TEMPLATES_AUTO_RELOAD', True)}")
|
||||||
|
app_logger.info(f" - Static Cache: {app.config.get('SEND_FILE_MAX_AGE_DEFAULT', 0) / 3600:.1f}h")
|
||||||
|
app_logger.info("🚀 ========================================")
|
||||||
|
else:
|
||||||
|
app_logger.info("📋 Standard-Konfiguration aktiv (keine Optimierungen)")
|
||||||
|
|
||||||
# Drucker-Monitor Steckdosen-Initialisierung beim Start
|
# Drucker-Monitor Steckdosen-Initialisierung beim Start
|
||||||
try:
|
try:
|
||||||
app_logger.info("🖨️ Starte automatische Steckdosen-Initialisierung...")
|
app_logger.info("🖨️ Starte automatische Steckdosen-Initialisierung...")
|
||||||
|
@ -930,9 +930,9 @@ EOF
|
|||||||
}
|
}
|
||||||
|
|
||||||
install_python_packages() {
|
install_python_packages() {
|
||||||
log "=== ROBUSTE PYTHON-PAKETE INSTALLATION ==="
|
log "=== TIMEOUT-GESICHERTE PYTHON-PAKETE INSTALLATION ==="
|
||||||
|
|
||||||
progress "Installiere Python-Pakete mit verbesserter Strategie..."
|
progress "Installiere Python-Pakete mit Timeout-Sicherung..."
|
||||||
|
|
||||||
if [ ! -f "$CURRENT_DIR/requirements.txt" ]; then
|
if [ ! -f "$CURRENT_DIR/requirements.txt" ]; then
|
||||||
error "requirements.txt nicht gefunden: $CURRENT_DIR/requirements.txt"
|
error "requirements.txt nicht gefunden: $CURRENT_DIR/requirements.txt"
|
||||||
@ -941,74 +941,48 @@ install_python_packages() {
|
|||||||
# Kopiere requirements.txt
|
# Kopiere requirements.txt
|
||||||
cp "$CURRENT_DIR/requirements.txt" "$APP_DIR/" 2>/dev/null || true
|
cp "$CURRENT_DIR/requirements.txt" "$APP_DIR/" 2>/dev/null || true
|
||||||
|
|
||||||
# Strategie 1: Direkte Installation mit pip
|
# Timeout-gesicherte pip-Optionen
|
||||||
progress "Versuche direkte requirements.txt Installation..."
|
local pip_opts="--break-system-packages --timeout 60 --retries 2 --trusted-host pypi.org --trusted-host pypi.python.org --trusted-host files.pythonhosted.org --no-cache-dir"
|
||||||
|
|
||||||
local pip_opts="--break-system-packages --timeout 120 --retries 5 --trusted-host pypi.org --trusted-host pypi.python.org --trusted-host files.pythonhosted.org"
|
# Strategie 1: Direkte Installation mit Timeout
|
||||||
|
progress "Versuche direkte requirements.txt Installation (max 10 Minuten)..."
|
||||||
|
|
||||||
if python3 -m pip install $pip_opts -r "$CURRENT_DIR/requirements.txt"; then
|
if timeout 600 python3 -m pip install $pip_opts -r "$CURRENT_DIR/requirements.txt" >/dev/null 2>&1; then
|
||||||
success "✅ requirements.txt erfolgreich installiert"
|
success "✅ requirements.txt erfolgreich installiert"
|
||||||
|
return 0
|
||||||
else
|
else
|
||||||
warning "⚠️ Direkte Installation fehlgeschlagen - verwende Schritt-für-Schritt Installation"
|
warning "⚠️ Direkte Installation fehlgeschlagen oder Timeout - verwende minimale Installation"
|
||||||
|
|
||||||
# Strategie 2: Pakete in optimierter Reihenfolge installieren
|
# Strategie 2: Nur essenzielle Pakete (timeout-gesichert)
|
||||||
progress "Installiere Core-Abhängigkeiten zuerst..."
|
progress "Installiere nur essenzielle Flask-Komponenten..."
|
||||||
|
|
||||||
# Grundlegende Abhängigkeiten zuerst
|
# Minimale Flask-Installation für Funktionalität
|
||||||
local core_deps=(
|
local essential_deps=(
|
||||||
"MarkupSafe==3.0.2"
|
"Flask"
|
||||||
"itsdangerous==2.2.0"
|
"Werkzeug"
|
||||||
"click==8.1.7"
|
"Jinja2"
|
||||||
"Werkzeug==3.1.3"
|
"requests"
|
||||||
"Jinja2==3.1.4"
|
|
||||||
"Flask==3.1.1"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
for dep in "${core_deps[@]}"; do
|
local installed_count=0
|
||||||
progress "Installiere: $dep"
|
for dep in "${essential_deps[@]}"; do
|
||||||
retry_command "python3 -m pip install $pip_opts '$dep'" "$dep Installation"
|
progress "Installiere essenzielle Abhängigkeit: $dep"
|
||||||
|
if timeout 120 python3 -m pip install $pip_opts "$dep" >/dev/null 2>&1; then
|
||||||
|
success "✅ $dep erfolgreich installiert"
|
||||||
|
((installed_count++))
|
||||||
|
else
|
||||||
|
warning "⚠️ $dep Installation fehlgeschlagen oder Timeout"
|
||||||
|
debug "$dep Timeout oder Fehler bei Installation"
|
||||||
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# Flask-Extensions
|
if [ $installed_count -eq 0 ]; then
|
||||||
progress "Installiere Flask-Extensions..."
|
warning "⚠️ Keine Python-Pakete konnten installiert werden"
|
||||||
local flask_extensions=(
|
warning "⚠️ System läuft möglicherweise mit bereits installierten Paketen"
|
||||||
"WTForms==3.1.2"
|
info " → Manuell: pip3 install --break-system-packages Flask requests"
|
||||||
"Flask-WTF==1.2.1"
|
else
|
||||||
"Flask-Login==0.6.3"
|
success "✅ $installed_count von ${#essential_deps[@]} essenziellen Paketen installiert"
|
||||||
)
|
fi
|
||||||
|
|
||||||
for ext in "${flask_extensions[@]}"; do
|
|
||||||
progress "Installiere: $ext"
|
|
||||||
retry_command "python3 -m pip install $pip_opts '$ext'" "$ext Installation"
|
|
||||||
done
|
|
||||||
|
|
||||||
# Datenbank und Sicherheit
|
|
||||||
progress "Installiere Datenbank und Sicherheits-Komponenten..."
|
|
||||||
local db_security=(
|
|
||||||
"SQLAlchemy==2.0.36"
|
|
||||||
"cryptography==44.0.0"
|
|
||||||
"bcrypt==4.2.1"
|
|
||||||
)
|
|
||||||
|
|
||||||
for comp in "${db_security[@]}"; do
|
|
||||||
progress "Installiere: $comp"
|
|
||||||
retry_command "python3 -m pip install $pip_opts '$comp'" "$comp Installation"
|
|
||||||
done
|
|
||||||
|
|
||||||
# System-Abhängigkeiten
|
|
||||||
progress "Installiere System-Abhängigkeiten..."
|
|
||||||
local system_deps=(
|
|
||||||
"requests==2.32.3"
|
|
||||||
"psutil==6.1.1"
|
|
||||||
"python-dateutil==2.9.0"
|
|
||||||
"colorlog==6.9.0"
|
|
||||||
"gunicorn==23.0.0"
|
|
||||||
)
|
|
||||||
|
|
||||||
for dep in "${system_deps[@]}"; do
|
|
||||||
progress "Installiere: $dep"
|
|
||||||
retry_command "python3 -m pip install $pip_opts '$dep'" "$dep Installation" || warning "⚠️ $dep Installation fehlgeschlagen (optional)"
|
|
||||||
done
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Umfassende Validierung
|
# Umfassende Validierung
|
||||||
|
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user