diff --git a/backend/app.py b/backend/app.py index 5c3cec41..49222dc3 100755 --- a/backend/app.py +++ b/backend/app.py @@ -90,6 +90,18 @@ def create_app(config_name=None): # Hintergrund-Tasks registrieren register_background_tasks(app) + # Parse PRINTERS als JSON + printers_env = app.config.get('PRINTERS', '{}') + if isinstance(printers_env, str): + try: + app.config['PRINTERS'] = json.loads(printers_env) + except (json.JSONDecodeError, TypeError): + app.config['PRINTERS'] = {} + elif isinstance(printers_env, dict): + app.config['PRINTERS'] = printers_env + else: + app.config['PRINTERS'] = {} + return app # Initialisierung - wird später durch create_app ersetzt @@ -1943,4 +1955,16 @@ else: printers_config = json.loads(app.config.get('PRINTERS', '{}')) if printers_config: init_printers() - setup_frontend_v2() \ No newline at end of file + setup_frontend_v2() + + # Parse PRINTERS als JSON + printers_env = app.config.get('PRINTERS', '{}') + if isinstance(printers_env, str): + try: + app.config['PRINTERS'] = json.loads(printers_env) + except (json.JSONDecodeError, TypeError): + app.config['PRINTERS'] = {} + elif isinstance(printers_env, dict): + app.config['PRINTERS'] = printers_env + else: + app.config['PRINTERS'] = {} \ No newline at end of file diff --git a/backend/config.py b/backend/config.py index 3da174ce..afac0fca 100644 --- a/backend/config.py +++ b/backend/config.py @@ -33,6 +33,19 @@ class Config: # Drucker-Konfiguration PRINTERS = os.environ.get('PRINTERS', '{}') + # JSON-Konfiguration parsen + @property + def PRINTERS_DICT(self): + """Parse PRINTERS configuration as JSON""" + import json + printers_str = self.PRINTERS + if isinstance(printers_str, dict): + return printers_str + try: + return json.loads(printers_str) if printers_str else {} + except (json.JSONDecodeError, TypeError): + return {} + # API-Konfiguration API_KEY = os.environ.get('API_KEY') @@ -112,35 +125,64 @@ class ProductionConfig(Config): if not os.path.exists('logs'): os.mkdir('logs') - file_handler = RotatingFileHandler( - 'logs/myp.log', - maxBytes=Config.LOG_MAX_BYTES, - backupCount=Config.LOG_BACKUP_COUNT - ) + # Windows-kompatibles Logging + import platform + if platform.system() == 'Windows': + # Windows: Verwende TimedRotatingFileHandler statt RotatingFileHandler + from logging.handlers import TimedRotatingFileHandler + + file_handler = TimedRotatingFileHandler( + 'logs/myp.log', + when='midnight', + interval=1, + backupCount=Config.LOG_BACKUP_COUNT + ) + + error_handler = TimedRotatingFileHandler( + 'logs/myp-errors.log', + when='midnight', + interval=1, + backupCount=Config.LOG_BACKUP_COUNT + ) + + security_handler = TimedRotatingFileHandler( + 'logs/security.log', + when='midnight', + interval=1, + backupCount=Config.LOG_BACKUP_COUNT + ) + else: + # Linux/Unix: Verwende RotatingFileHandler + file_handler = RotatingFileHandler( + 'logs/myp.log', + maxBytes=Config.LOG_MAX_BYTES, + backupCount=Config.LOG_BACKUP_COUNT + ) + + error_handler = RotatingFileHandler( + 'logs/myp-errors.log', + maxBytes=Config.LOG_MAX_BYTES, + backupCount=Config.LOG_BACKUP_COUNT + ) + + security_handler = RotatingFileHandler( + 'logs/security.log', + maxBytes=Config.LOG_MAX_BYTES, + backupCount=Config.LOG_BACKUP_COUNT + ) + file_handler.setFormatter(logging.Formatter( '%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]' )) file_handler.setLevel(logging.INFO) app.logger.addHandler(file_handler) - # Error-Logging - error_handler = RotatingFileHandler( - 'logs/myp-errors.log', - maxBytes=Config.LOG_MAX_BYTES, - backupCount=Config.LOG_BACKUP_COUNT - ) error_handler.setFormatter(logging.Formatter( '%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]' )) error_handler.setLevel(logging.ERROR) app.logger.addHandler(error_handler) - # Security-Logging - security_handler = RotatingFileHandler( - 'logs/security.log', - maxBytes=Config.LOG_MAX_BYTES, - backupCount=Config.LOG_BACKUP_COUNT - ) security_handler.setFormatter(logging.Formatter( '%(asctime)s SECURITY %(levelname)s: %(message)s [%(name)s]' )) @@ -154,10 +196,13 @@ class ProductionConfig(Config): app.logger.setLevel(logging.INFO) app.logger.info('MYP Backend starting in production mode') - # Sicherheits-Middleware registrieren + # Sicherheits-Middleware registrieren (optional) if app.config.get('SECURITY_ENABLED', True): - from security import security_middleware - security_middleware.init_app(app) + try: + from security import security_middleware + security_middleware.init_app(app) + except ImportError: + app.logger.warning('Security module not found, skipping security middleware') class TestingConfig(Config): """Konfiguration für die Testumgebung.""" diff --git a/backend/env.backend b/backend/env.backend index f9296b32..cbf57b74 100644 --- a/backend/env.backend +++ b/backend/env.backend @@ -1,4 +1,4 @@ -# 🏭 MYP Backend - Standalone Server Konfiguration +# MYP Backend - Standalone Server Konfiguration # Umgebungsvariablen ausschließlich für den Backend-Server # === FLASK KONFIGURATION === diff --git a/backend/requirements.txt b/backend/requirements.txt index 2c762d29..461981f6 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -1,31 +1,54 @@ -# Core Flask-Abhängigkeiten -flask==2.3.3 -flask-cors==4.0.0 -werkzeug==2.3.7 +# Core Flask framework +Flask==3.0.3 +Werkzeug==3.0.3 -# Authentifizierung und Sicherheit -pyjwt==2.8.0 -flask-wtf==1.1.1 +# Flask extensions +Flask-CORS==4.0.0 + +# Security flask-talisman==1.1.0 -# Umgebung und Konfiguration -python-dotenv==1.0.0 - -# WSGI-Server für Produktion -gunicorn==21.2.0 -waitress==2.1.2 - -# Netzwerk und Hardware-Integration -PyP100==0.0.19 -netifaces==0.11.0 +# HTTP client requests==2.31.0 -# Monitoring und Logging -flask-healthcheck==0.1.0 -prometheus-flask-exporter==0.23.0 -psutil==5.9.6 +# Networking and socket handling +python-socketio==5.11.4 -# Entwicklung und Testing (optional) +# Threading utilities +eventlet==0.36.1 + +# Environment variables +python-dotenv==1.0.1 + +# Cryptography and hashing +PyJWT==2.8.0 +cryptography==41.0.7 + +# Date and time handling +python-dateutil==2.8.2 + +# JSON Web Tokens +flask-jwt-extended==4.6.0 + +# Database +SQLAlchemy==2.0.23 +Flask-Migrate==4.0.5 + +# Production server +gunicorn==21.2.0 + +# Monitoring +prometheus_client==0.20.0 + +# Testing pytest==7.4.3 -pytest-flask==1.3.0 -coverage==7.3.2 \ No newline at end of file +pytest-cov==4.1.0 + +# Development +Flask-DebugToolbar==0.13.1 + +# Cache +redis==5.0.1 + +# Utilities +click==8.1.7 \ No newline at end of file diff --git a/backend/test-backend-setup.py b/backend/test-backend-setup.py index 0519ecba..de9606fb 100644 --- a/backend/test-backend-setup.py +++ b/backend/test-backend-setup.py @@ -1 +1,274 @@ - \ No newline at end of file +#!/usr/bin/env python3 +""" +Test-Skript für MYP Backend-Setup +Überprüft, ob die neue Produktions-Konfiguration korrekt funktioniert +""" + +import os +import sys +import subprocess +import tempfile +import importlib.util + +def test_python_environment(): + """Teste Python-Umgebung und Dependencies""" + print("🐍 Teste Python-Umgebung...") + + # Python-Version prüfen + python_version = sys.version_info + print(f" Python-Version: {python_version.major}.{python_version.minor}.{python_version.micro}") + + if python_version < (3, 8): + print(" ❌ Python-Version ist zu alt! Benötigt wird mindestens Python 3.8") + return False + + print(" ✅ Python-Version ist kompatibel") + return True + +def test_dependencies(): + """Teste erforderliche Python-Pakete""" + print("📦 Teste Python-Dependencies...") + + required_packages = [ + 'flask', + 'flask_cors', + 'werkzeug', + 'pyjwt', + 'python_dotenv', + 'gunicorn' + ] + + missing_packages = [] + + for package in required_packages: + try: + __import__(package) + print(f" ✅ {package}") + except ImportError: + print(f" ❌ {package} fehlt") + missing_packages.append(package) + + if missing_packages: + print(f" Fehlende Pakete: {', '.join(missing_packages)}") + print(" Installiere mit: pip install -r requirements.txt") + return False + + return True + +def test_configuration(): + """Teste Konfigurationsklassen""" + print("⚙️ Teste Konfiguration...") + + try: + # Importiere Konfiguration + from config import config, DevelopmentConfig, ProductionConfig, TestingConfig + + print(" ✅ Konfigurationsklassen importiert") + + # Teste verschiedene Konfigurationen + dev_config = DevelopmentConfig() + prod_config = ProductionConfig() + test_config = TestingConfig() + + print(f" ✅ Development-Config: DEBUG={dev_config.DEBUG}") + print(f" ✅ Production-Config: DEBUG={prod_config.DEBUG}") + print(f" ✅ Testing-Config: TESTING={test_config.TESTING}") + + return True + + except Exception as e: + print(f" ❌ Konfigurationsfehler: {e}") + return False + +def test_app_factory(): + """Teste Application Factory Pattern""" + print("🏭 Teste Application Factory...") + + try: + # Temporäre Umgebungsvariablen setzen + os.environ['SECRET_KEY'] = 'test_secret_key' + os.environ['DATABASE_PATH'] = ':memory:' + + from app import create_app + + # Teste verschiedene Konfigurationen + dev_app = create_app('development') + prod_app = create_app('production') + test_app = create_app('testing') + + print(f" ✅ Development-App: {dev_app.config['FLASK_ENV']}") + print(f" ✅ Production-App: {prod_app.config['FLASK_ENV']}") + print(f" ✅ Testing-App: {test_app.config['FLASK_ENV']}") + + return True + + except Exception as e: + print(f" ❌ Application Factory Fehler: {e}") + return False + +def test_database_functions(): + """Teste Datenbankfunktionen""" + print("🗄️ Teste Datenbankfunktionen...") + + try: + os.environ['SECRET_KEY'] = 'test_secret_key' + os.environ['DATABASE_PATH'] = ':memory:' + + from app import create_app, init_db, get_db + + app = create_app('testing') + + with app.app_context(): + # Initialisiere Test-Datenbank + init_db() + + # Teste Datenbankverbindung + db = get_db() + result = db.execute('SELECT 1').fetchone() + + if result: + print(" ✅ Datenbankverbindung funktioniert") + print(" ✅ Tabellen wurden erstellt") + return True + else: + print(" ❌ Datenbankverbindung fehlgeschlagen") + return False + + except Exception as e: + print(f" ❌ Datenbankfehler: {e}") + return False + +def test_environment_variables(): + """Teste Umgebungsvariablen""" + print("🌍 Teste Umgebungsvariablen...") + + # Lade env.backend falls vorhanden + if os.path.exists('env.backend'): + print(" ✅ env.backend gefunden") + + with open('env.backend', 'r') as f: + lines = f.readlines() + + required_vars = [ + 'FLASK_APP', + 'FLASK_ENV', + 'SECRET_KEY', + 'DATABASE_PATH' + ] + + found_vars = [] + for line in lines: + if '=' in line and not line.strip().startswith('#'): + var_name = line.split('=')[0].strip() + if var_name in required_vars: + found_vars.append(var_name) + + missing_vars = set(required_vars) - set(found_vars) + + if missing_vars: + print(f" ❌ Fehlende Umgebungsvariablen: {', '.join(missing_vars)}") + return False + else: + print(f" ✅ Alle erforderlichen Variablen gefunden: {', '.join(found_vars)}") + return True + else: + print(" ❌ env.backend nicht gefunden") + return False + +def test_wsgi(): + """Teste WSGI-Konfiguration""" + print("🔧 Teste WSGI-Setup...") + + try: + from wsgi import application + + if application: + print(" ✅ WSGI-Application erfolgreich importiert") + print(f" ✅ App-Name: {application.name}") + return True + else: + print(" ❌ WSGI-Application ist None") + return False + + except Exception as e: + print(f" ❌ WSGI-Fehler: {e}") + return False + +def test_health_endpoint(): + """Teste Health-Check-Endpoint""" + print("🏥 Teste Health-Check...") + + try: + os.environ['SECRET_KEY'] = 'test_secret_key' + os.environ['DATABASE_PATH'] = ':memory:' + + from app import create_app + + app = create_app('testing') + + with app.test_client() as client: + response = client.get('/health') + + if response.status_code == 200: + data = response.get_json() + if data and data.get('status') == 'healthy': + print(" ✅ Health-Check funktioniert") + print(f" ✅ Service: {data.get('service')}") + return True + else: + print(f" ❌ Health-Check-Antwort fehlerhaft: {data}") + return False + else: + print(f" ❌ Health-Check fehlgeschlagen: {response.status_code}") + return False + + except Exception as e: + print(f" ❌ Health-Check-Fehler: {e}") + return False + +def main(): + """Haupttest-Funktion""" + print("=" * 50) + print("🧪 MYP Backend - Konfigurationstest") + print("=" * 50) + print() + + tests = [ + test_python_environment, + test_dependencies, + test_configuration, + test_app_factory, + test_database_functions, + test_environment_variables, + test_wsgi, + test_health_endpoint + ] + + passed = 0 + failed = 0 + + for test in tests: + try: + if test(): + passed += 1 + else: + failed += 1 + except Exception as e: + print(f" ❌ Test-Fehler: {e}") + failed += 1 + print() + + print("=" * 50) + print(f"📊 Test-Ergebnisse: {passed} ✅ | {failed} ❌") + print("=" * 50) + + if failed == 0: + print("🎉 Alle Tests bestanden! Backend ist bereit.") + return True + else: + print("⚠️ Einige Tests fehlgeschlagen. Bitte Konfiguration prüfen.") + return False + +if __name__ == '__main__': + success = main() + sys.exit(0 if success else 1) \ No newline at end of file