"Update backend configuration and tests for improved stability"

This commit is contained in:
Till Tomczak 2025-05-23 08:54:23 +02:00
parent c1e8ee01c5
commit a11721f677
5 changed files with 412 additions and 47 deletions

View File

@ -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
@ -1944,3 +1956,15 @@ else:
if printers_config:
init_printers()
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'] = {}

View File

@ -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')
# 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):
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."""

View File

@ -1,4 +1,4 @@
# 🏭 MYP Backend - Standalone Server Konfiguration
# MYP Backend - Standalone Server Konfiguration
# Umgebungsvariablen ausschließlich für den Backend-Server
# === FLASK KONFIGURATION ===

View File

@ -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
pytest-cov==4.1.0
# Development
Flask-DebugToolbar==0.13.1
# Cache
redis==5.0.1
# Utilities
click==8.1.7

View File

@ -1 +1,274 @@
#!/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)