🎉 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
|
||||
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)
|
||||
if os.name == 'nt':
|
||||
try:
|
||||
@ -311,10 +429,76 @@ register_aggressive_shutdown()
|
||||
# Flask-App initialisieren
|
||||
app = Flask(__name__)
|
||||
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["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
|
||||
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
|
||||
db = db_engine
|
||||
|
||||
@ -549,6 +733,23 @@ def format_datetime_filter(value, format='%d.%m.%Y %H:%M'):
|
||||
return value
|
||||
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
|
||||
@app.before_request
|
||||
def log_request_info():
|
||||
@ -5610,6 +5811,22 @@ def admin_advanced_settings():
|
||||
user_settings = session.get('user_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
|
||||
stats = {
|
||||
'total_users': db_session.query(User).count(),
|
||||
@ -5659,6 +5876,7 @@ def admin_advanced_settings():
|
||||
'admin_advanced_settings.html',
|
||||
title='Erweiterte Einstellungen',
|
||||
optimization_settings=optimization_settings,
|
||||
performance_optimization=performance_optimization,
|
||||
stats=stats,
|
||||
maintenance_info=maintenance_info
|
||||
)
|
||||
@ -5668,6 +5886,59 @@ def admin_advanced_settings():
|
||||
flash('Fehler beim Laden der erweiterten Einstellungen', 'error')
|
||||
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'])
|
||||
@login_required
|
||||
@admin_required
|
||||
@ -8100,6 +8371,128 @@ def api_admin_system_status():
|
||||
}), 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 =====
|
||||
@app.route("/api/statistics/public", methods=['GET'])
|
||||
def api_public_statistics():
|
||||
@ -9030,6 +9423,22 @@ if __name__ == "__main__":
|
||||
# Template-Hilfsfunktionen registrieren
|
||||
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
|
||||
try:
|
||||
app_logger.info("🖨️ Starte automatische Steckdosen-Initialisierung...")
|
||||
|
@ -930,9 +930,9 @@ EOF
|
||||
}
|
||||
|
||||
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
|
||||
error "requirements.txt nicht gefunden: $CURRENT_DIR/requirements.txt"
|
||||
@ -941,74 +941,48 @@ install_python_packages() {
|
||||
# Kopiere requirements.txt
|
||||
cp "$CURRENT_DIR/requirements.txt" "$APP_DIR/" 2>/dev/null || true
|
||||
|
||||
# Strategie 1: Direkte Installation mit pip
|
||||
progress "Versuche direkte requirements.txt Installation..."
|
||||
# Timeout-gesicherte pip-Optionen
|
||||
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"
|
||||
return 0
|
||||
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
|
||||
progress "Installiere Core-Abhängigkeiten zuerst..."
|
||||
# Strategie 2: Nur essenzielle Pakete (timeout-gesichert)
|
||||
progress "Installiere nur essenzielle Flask-Komponenten..."
|
||||
|
||||
# Grundlegende Abhängigkeiten zuerst
|
||||
local core_deps=(
|
||||
"MarkupSafe==3.0.2"
|
||||
"itsdangerous==2.2.0"
|
||||
"click==8.1.7"
|
||||
"Werkzeug==3.1.3"
|
||||
"Jinja2==3.1.4"
|
||||
"Flask==3.1.1"
|
||||
# Minimale Flask-Installation für Funktionalität
|
||||
local essential_deps=(
|
||||
"Flask"
|
||||
"Werkzeug"
|
||||
"Jinja2"
|
||||
"requests"
|
||||
)
|
||||
|
||||
for dep in "${core_deps[@]}"; do
|
||||
progress "Installiere: $dep"
|
||||
retry_command "python3 -m pip install $pip_opts '$dep'" "$dep Installation"
|
||||
local installed_count=0
|
||||
for dep in "${essential_deps[@]}"; do
|
||||
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
|
||||
|
||||
# Flask-Extensions
|
||||
progress "Installiere Flask-Extensions..."
|
||||
local flask_extensions=(
|
||||
"WTForms==3.1.2"
|
||||
"Flask-WTF==1.2.1"
|
||||
"Flask-Login==0.6.3"
|
||||
)
|
||||
|
||||
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
|
||||
if [ $installed_count -eq 0 ]; then
|
||||
warning "⚠️ Keine Python-Pakete konnten installiert werden"
|
||||
warning "⚠️ System läuft möglicherweise mit bereits installierten Paketen"
|
||||
info " → Manuell: pip3 install --break-system-packages Flask requests"
|
||||
else
|
||||
success "✅ $installed_count von ${#essential_deps[@]} essenziellen Paketen installiert"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Umfassende Validierung
|
||||
|
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user