2025-06-05 01:34:10 +02:00

344 lines
11 KiB
Python

"""
Zentrale Konfigurationsdatei für das 3D-Druck-Management-System
Diese Datei enthält alle Konfigurationseinstellungen, die zuvor im config-Ordner waren.
"""
import os
import json
from datetime import timedelta
def get_env_variable(name: str, default: str = None) -> str:
"""
Holt eine Umgebungsvariable oder gibt den Standardwert zurück.
Args:
name: Name der Umgebungsvariable
default: Standardwert, falls die Variable nicht gesetzt ist
Returns:
str: Wert der Umgebungsvariable oder Standardwert
"""
return os.environ.get(name, default)
# ===== GRUNDLEGENDE KONFIGURATION =====
# Hardcodierte Konfiguration
SECRET_KEY = "7445630171969DFAC92C53CEC92E67A9CB2E00B3CB2F"
# Dynamische Pfade basierend auf dem aktuellen Arbeitsverzeichnis
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # Projekt-Wurzel
DATABASE_PATH = os.path.join(BASE_DIR, "instance", "printer_manager.db")
# ===== SMART PLUG KONFIGURATION =====
# TP-Link Tapo P110 Standardkonfiguration
TAPO_USERNAME = "till.tomczak@mercedes-benz.com"
TAPO_PASSWORD = "744563017196A"
# Automatische Steckdosen-Erkennung aktivieren
TAPO_AUTO_DISCOVERY = True
# Standard-Steckdosen-IPs (diese können später in der Datenbank überschrieben werden)
DEFAULT_TAPO_IPS = [
"192.168.0.103", # Erreichbare Steckdose laut Test
"192.168.0.104", # Erreichbare Steckdose laut Test
"192.168.0.100",
"192.168.0.101",
"192.168.0.102",
"192.168.0.105"
]
# Timeout-Konfiguration für Tapo-Verbindungen
TAPO_TIMEOUT = 10 # Sekunden
TAPO_RETRY_COUNT = 3 # Anzahl Wiederholungsversuche
# ===== DRUCKER-KONFIGURATION =====
PRINTERS = {
"Printer 1": {"ip": "192.168.0.100"},
"Printer 2": {"ip": "192.168.0.101"},
"Printer 3": {"ip": "192.168.0.102"},
"Printer 4": {"ip": "192.168.0.103"},
"Printer 5": {"ip": "192.168.0.104"},
"Printer 6": {"ip": "192.168.0.106"}
}
# ===== LOGGING-KONFIGURATION =====
LOG_DIR = os.path.join(BASE_DIR, "logs")
LOG_SUBDIRS = ["app", "scheduler", "auth", "jobs", "printers", "errors", "user", "kiosk",
"admin", "admin_api", "guest", "analytics", "uploads", "sessions"]
LOG_LEVEL = "INFO"
LOG_FORMAT = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
LOG_DATE_FORMAT = "%Y-%m-%d %H:%M:%S"
LOG_FILE_MAX_BYTES = 10 * 1024 * 1024 # 10MB
LOG_BACKUP_COUNT = 5
# ===== FLASK-KONFIGURATION =====
FLASK_HOST = "0.0.0.0"
FLASK_PORT = 443 # Kann auf 8443 geändert werden für nicht-privilegierte Ports
FLASK_FALLBACK_PORT = 8080
FLASK_DEBUG = False # In Produktion auf False setzen!
SESSION_LIFETIME = timedelta(hours=2) # Session-Dauer
# ===== UPLOAD-KONFIGURATION =====
UPLOAD_FOLDER = os.path.join(BASE_DIR, "uploads")
ALLOWED_EXTENSIONS = {'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif', 'gcode', '3mf', 'stl', 'obj', 'amf'}
MAX_CONTENT_LENGTH = 16 * 1024 * 1024 # 16MB Maximum-Dateigröße
MAX_FILE_SIZE = 16 * 1024 * 1024 # 16MB Maximum-Dateigröße für Drag & Drop System
# ===== UMGEBUNGSKONFIGURATION =====
ENVIRONMENT = get_env_variable("MYP_ENVIRONMENT", "development")
# ===== SSL-KONFIGURATION =====
SSL_ENABLED = get_env_variable("MYP_SSL_ENABLED", "True").lower() in ("true", "1", "yes")
SSL_CERT_PATH = os.path.join(BASE_DIR, "certs", "myp.crt")
SSL_KEY_PATH = os.path.join(BASE_DIR, "certs", "myp.key")
SSL_HOSTNAME = get_env_variable("MYP_SSL_HOSTNAME", "localhost")
# ===== SCHEDULER-KONFIGURATION =====
SCHEDULER_INTERVAL = 60 # Sekunden
SCHEDULER_ENABLED = True
# ===== DATENBANK-KONFIGURATION =====
DB_ENGINE = f"sqlite:///{DATABASE_PATH}"
SQLALCHEMY_TRACK_MODIFICATIONS = False
SQLALCHEMY_ENGINE_OPTIONS = {
'pool_pre_ping': True,
'pool_recycle': 300,
}
# ===== SICHERHEITSKONFIGURATION =====
WTF_CSRF_ENABLED = True
WTF_CSRF_TIME_LIMIT = 3600 # 1 Stunde
SESSION_COOKIE_SECURE = SSL_ENABLED # Nur bei HTTPS
SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_SAMESITE = 'Lax'
# ===== E-MAIL-KONFIGURATION (Optional) =====
MAIL_SERVER = get_env_variable('MAIL_SERVER')
MAIL_PORT = int(get_env_variable('MAIL_PORT', '587'))
MAIL_USE_TLS = get_env_variable('MAIL_USE_TLS', 'true').lower() in ['true', 'on', '1']
MAIL_USERNAME = get_env_variable('MAIL_USERNAME')
MAIL_PASSWORD = get_env_variable('MAIL_PASSWORD')
# ===== HILFSFUNKTIONEN =====
def get_log_file(category: str) -> str:
"""
Gibt den Pfad zur Log-Datei für eine bestimmte Kategorie zurück.
Args:
category: Log-Kategorie (app, scheduler, auth, jobs, printers, errors, etc.)
Returns:
str: Pfad zur Log-Datei
"""
if category not in LOG_SUBDIRS:
category = "app"
return os.path.join(LOG_DIR, category, f"{category}.log")
def ensure_log_directories():
"""Erstellt alle erforderlichen Log-Verzeichnisse."""
os.makedirs(LOG_DIR, exist_ok=True)
for subdir in LOG_SUBDIRS:
os.makedirs(os.path.join(LOG_DIR, subdir), exist_ok=True)
def ensure_database_directory():
"""Erstellt das Datenbank-Verzeichnis."""
db_dir = os.path.dirname(DATABASE_PATH)
if db_dir:
os.makedirs(db_dir, exist_ok=True)
def ensure_ssl_directory():
"""Erstellt das SSL-Verzeichnis, falls es nicht existiert."""
ssl_dir = os.path.dirname(SSL_CERT_PATH)
if ssl_dir and not os.path.exists(ssl_dir):
os.makedirs(ssl_dir, exist_ok=True)
def ensure_upload_directory():
"""Erstellt das Upload-Verzeichnis, falls es nicht existiert."""
if not os.path.exists(UPLOAD_FOLDER):
os.makedirs(UPLOAD_FOLDER, exist_ok=True)
# Erstelle Unterordner für verschiedene Upload-Typen
subdirs = ['jobs', 'guests', 'avatars', 'assets', 'logs', 'backups', 'temp']
for subdir in subdirs:
os.makedirs(os.path.join(UPLOAD_FOLDER, subdir), exist_ok=True)
def get_ssl_context():
"""
Gibt den SSL-Kontext für Flask zurück, wenn SSL aktiviert ist.
Returns:
tuple oder None: Tuple mit Zertifikat- und Schlüsselpfad, wenn SSL aktiviert ist, sonst None
"""
if not SSL_ENABLED:
return None
# Wenn Zertifikate nicht existieren, diese automatisch erstellen
if not os.path.exists(SSL_CERT_PATH) or not os.path.exists(SSL_KEY_PATH):
ensure_ssl_directory()
# Im Entwicklungsmodus versuchen wir, einfache Zertifikate zu erstellen
if FLASK_DEBUG:
print("SSL-Zertifikate nicht gefunden. Erstelle einfache selbstsignierte Zertifikate...")
try:
# Einfache Zertifikate mit Python erstellen
create_simple_ssl_cert()
# Prüfen, ob die Zertifikate erfolgreich erstellt wurden
if not os.path.exists(SSL_CERT_PATH) or not os.path.exists(SSL_KEY_PATH):
print("Konnte keine SSL-Zertifikate erstellen.")
return None
except Exception as e:
print(f"Fehler beim Erstellen der SSL-Zertifikate: {e}")
return None
else:
print("WARNUNG: SSL-Zertifikate nicht gefunden und Nicht-Debug-Modus. SSL wird deaktiviert.")
return None
return (SSL_CERT_PATH, SSL_KEY_PATH)
def create_simple_ssl_cert():
"""
Erstellt ein Mercedes-Benz SSL-Zertifikat mit dem SSL-Manager.
"""
try:
# Verwende den SSL-Manager
from utils.ssl_manager import ssl_manager
success = ssl_manager.generate_mercedes_certificate()
if success:
print(f"Mercedes-Benz SSL-Zertifikat erfolgreich erstellt: {SSL_CERT_PATH}")
return True
else:
print("Fehler beim Erstellen des Mercedes-Benz SSL-Zertifikats")
return None
except ImportError as e:
print(f"SSL-Manager nicht verfügbar: {e}")
return None
except Exception as e:
print(f"Fehler beim Erstellen der SSL-Zertifikate: {e}")
return None
# ===== KONFIGURATIONSKLASSEN FÜR VERSCHIEDENE UMGEBUNGEN =====
class Config:
"""Basis-Konfigurationsklasse mit gemeinsamen Einstellungen."""
SECRET_KEY = SECRET_KEY
PERMANENT_SESSION_LIFETIME = SESSION_LIFETIME
SESSION_COOKIE_SECURE = SESSION_COOKIE_SECURE
SESSION_COOKIE_HTTPONLY = SESSION_COOKIE_HTTPONLY
SESSION_COOKIE_SAMESITE = SESSION_COOKIE_SAMESITE
SQLALCHEMY_DATABASE_URI = DB_ENGINE
SQLALCHEMY_TRACK_MODIFICATIONS = SQLALCHEMY_TRACK_MODIFICATIONS
SQLALCHEMY_ENGINE_OPTIONS = SQLALCHEMY_ENGINE_OPTIONS
UPLOAD_FOLDER = UPLOAD_FOLDER
MAX_CONTENT_LENGTH = MAX_CONTENT_LENGTH
ALLOWED_EXTENSIONS = ALLOWED_EXTENSIONS
WTF_CSRF_ENABLED = WTF_CSRF_ENABLED
WTF_CSRF_TIME_LIMIT = WTF_CSRF_TIME_LIMIT
LOG_LEVEL = LOG_LEVEL
LOG_FILE_MAX_BYTES = LOG_FILE_MAX_BYTES
LOG_BACKUP_COUNT = LOG_BACKUP_COUNT
SCHEDULER_ENABLED = SCHEDULER_ENABLED
SCHEDULER_INTERVAL = SCHEDULER_INTERVAL
SSL_ENABLED = SSL_ENABLED
SSL_CERT_PATH = SSL_CERT_PATH
SSL_KEY_PATH = SSL_KEY_PATH
DEFAULT_PORT = FLASK_PORT
DEFAULT_HOST = FLASK_HOST
@staticmethod
def init_app(app):
"""Initialisiere Anwendung mit dieser Konfiguration."""
pass
class DevelopmentConfig(Config):
"""Entwicklungsumgebung-Konfiguration."""
DEBUG = True
TESTING = False
LOG_LEVEL = 'DEBUG'
SESSION_COOKIE_SECURE = False
WTF_CSRF_ENABLED = False # Für einfacheres API-Testing
@staticmethod
def init_app(app):
Config.init_app(app)
import logging
logging.basicConfig(level=logging.DEBUG)
class TestingConfig(Config):
"""Test-Umgebung-Konfiguration."""
TESTING = True
DEBUG = True
SQLALCHEMY_DATABASE_URI = 'sqlite:///:memory:'
WTF_CSRF_ENABLED = False
PERMANENT_SESSION_LIFETIME = timedelta(minutes=5)
@staticmethod
def init_app(app):
Config.init_app(app)
class ProductionConfig(Config):
"""Produktionsumgebung-Konfiguration."""
DEBUG = False
TESTING = False
SESSION_COOKIE_SECURE = True # Erfordert HTTPS
WTF_CSRF_ENABLED = True
LOG_LEVEL = 'WARNING'
SSL_ENABLED = True
@staticmethod
def init_app(app):
Config.init_app(app)
# Produktions-spezifische Initialisierung
import logging
from logging.handlers import RotatingFileHandler
# Log-Verzeichnis sicherstellen
ensure_log_directories()
# Datei-Logging für Produktion einrichten
file_handler = RotatingFileHandler(
get_log_file('app'),
maxBytes=Config.LOG_FILE_MAX_BYTES,
backupCount=Config.LOG_BACKUP_COUNT
)
file_handler.setFormatter(logging.Formatter(LOG_FORMAT))
file_handler.setLevel(logging.WARNING)
app.logger.addHandler(file_handler)
app.logger.setLevel(logging.WARNING)
# Konfigurations-Dictionary für einfachen Zugriff
config = {
'development': DevelopmentConfig,
'testing': TestingConfig,
'production': ProductionConfig,
'default': DevelopmentConfig
}
def get_config_by_name(config_name):
"""
Hole Konfigurationsklasse nach Name.
Args:
config_name (str): Name der Konfiguration ('development', 'testing', 'production')
Returns:
Config: Konfigurationsklasse
"""
return config.get(config_name, config['default'])