📝 Commit Details:

This commit is contained in:
2025-05-31 22:40:29 +02:00
parent 91b1886dde
commit df8fb197c0
14061 changed files with 997277 additions and 103548 deletions

View File

@ -0,0 +1,74 @@
# -*- coding: utf-8 -*-
"""
Configuration Package for MYP Platform
======================================
This package contains all configuration modules for the Mercedes-Benz 3D Printing Platform.
Modules:
- security: Security configuration and middleware
- database: Database configuration and settings
- logging: Logging configuration
- app_config: Main application configuration
"""
__version__ = "2.0.0"
__author__ = "MYP Development Team"
# Import main configuration modules
try:
from .security import SecurityConfig, get_security_headers
from .app_config import Config, DevelopmentConfig, ProductionConfig, TestingConfig
except ImportError as e:
print(f"Warning: Could not import configuration modules: {e}")
# Fallback configurations
SecurityConfig = None
get_security_headers = None
Config = None
# Export main configuration classes
__all__ = [
'SecurityConfig',
'get_security_headers',
'Config',
'DevelopmentConfig',
'ProductionConfig',
'TestingConfig'
]
def get_config(config_name='development'):
"""
Get configuration object based on environment name.
Args:
config_name (str): Configuration environment name
Returns:
Config: Configuration object
"""
configs = {
'development': DevelopmentConfig,
'production': ProductionConfig,
'testing': TestingConfig
}
return configs.get(config_name, DevelopmentConfig)
def validate_config(config_obj):
"""
Validate configuration object.
Args:
config_obj: Configuration object to validate
Returns:
bool: True if valid, False otherwise
"""
required_attrs = ['SECRET_KEY', 'DATABASE_URL']
for attr in required_attrs:
if not hasattr(config_obj, attr):
print(f"Missing required configuration: {attr}")
return False
return True

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,181 @@
# -*- coding: utf-8 -*-
"""
Application Configuration Module for MYP Platform
================================================
Flask configuration classes for different environments.
"""
import os
from datetime import timedelta
# Base configuration directory
BASE_DIR = os.path.abspath(os.path.dirname(__file__))
PROJECT_ROOT = os.path.abspath(os.path.join(BASE_DIR, '..', '..'))
class Config:
"""Base configuration class with common settings."""
# Secret key for Flask sessions and CSRF protection
SECRET_KEY = os.environ.get('SECRET_KEY') or 'dev-secret-key-change-in-production-744563017196A'
# Session configuration
PERMANENT_SESSION_LIFETIME = timedelta(hours=24)
SESSION_COOKIE_SECURE = False # Set to True in production with HTTPS
SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_SAMESITE = 'Lax'
# Database configuration
DATABASE_URL = os.environ.get('DATABASE_URL') or f'sqlite:///{os.path.join(PROJECT_ROOT, "data", "myp_platform.db")}'
SQLALCHEMY_DATABASE_URI = DATABASE_URL
SQLALCHEMY_TRACK_MODIFICATIONS = False
SQLALCHEMY_ENGINE_OPTIONS = {
'pool_pre_ping': True,
'pool_recycle': 300,
}
# Upload configuration
UPLOAD_FOLDER = os.path.join(PROJECT_ROOT, 'uploads')
MAX_CONTENT_LENGTH = 500 * 1024 * 1024 # 500MB max file size
ALLOWED_EXTENSIONS = {'gcode', 'stl', 'obj', '3mf', 'amf'}
# Security configuration
WTF_CSRF_ENABLED = True
WTF_CSRF_TIME_LIMIT = 3600 # 1 hour
# Mail configuration (optional)
MAIL_SERVER = os.environ.get('MAIL_SERVER')
MAIL_PORT = int(os.environ.get('MAIL_PORT') or 587)
MAIL_USE_TLS = os.environ.get('MAIL_USE_TLS', 'true').lower() in ['true', 'on', '1']
MAIL_USERNAME = os.environ.get('MAIL_USERNAME')
MAIL_PASSWORD = os.environ.get('MAIL_PASSWORD')
# Logging configuration
LOG_LEVEL = os.environ.get('LOG_LEVEL', 'INFO')
LOG_FILE_MAX_BYTES = 10 * 1024 * 1024 # 10MB
LOG_BACKUP_COUNT = 5
# Application-specific settings
SCHEDULER_ENABLED = os.environ.get('SCHEDULER_ENABLED', 'true').lower() in ['true', 'on', '1']
SCHEDULER_INTERVAL = int(os.environ.get('SCHEDULER_INTERVAL', '60')) # seconds
# SSL/HTTPS configuration
SSL_ENABLED = os.environ.get('SSL_ENABLED', 'false').lower() in ['true', 'on', '1']
SSL_CERT_PATH = os.environ.get('SSL_CERT_PATH')
SSL_KEY_PATH = os.environ.get('SSL_KEY_PATH')
# Network configuration
DEFAULT_PORT = int(os.environ.get('PORT', '5000'))
DEFAULT_HOST = os.environ.get('HOST', '0.0.0.0')
@staticmethod
def init_app(app):
"""Initialize application with this configuration."""
pass
class DevelopmentConfig(Config):
"""Development environment configuration."""
DEBUG = True
TESTING = False
# More verbose logging in development
LOG_LEVEL = 'DEBUG'
# Disable some security features for easier development
SESSION_COOKIE_SECURE = False
WTF_CSRF_ENABLED = False # Disable CSRF for easier API testing
@staticmethod
def init_app(app):
Config.init_app(app)
# Development-specific initialization
import logging
logging.basicConfig(level=logging.DEBUG)
class TestingConfig(Config):
"""Testing environment configuration."""
TESTING = True
DEBUG = True
# Use in-memory database for testing
DATABASE_URL = 'sqlite:///:memory:'
SQLALCHEMY_DATABASE_URI = DATABASE_URL
# Disable CSRF for testing
WTF_CSRF_ENABLED = False
# Shorter session lifetime for testing
PERMANENT_SESSION_LIFETIME = timedelta(minutes=5)
@staticmethod
def init_app(app):
Config.init_app(app)
class ProductionConfig(Config):
"""Production environment configuration."""
DEBUG = False
TESTING = False
# Strict security settings for production
SESSION_COOKIE_SECURE = True # Requires HTTPS
WTF_CSRF_ENABLED = True
# Production logging
LOG_LEVEL = 'WARNING'
# SSL should be enabled in production
SSL_ENABLED = True
@staticmethod
def init_app(app):
Config.init_app(app)
# Production-specific initialization
import logging
from logging.handlers import RotatingFileHandler
# Set up file logging for production
log_dir = os.path.join(os.path.dirname(app.instance_path), 'logs')
if not os.path.exists(log_dir):
os.makedirs(log_dir)
file_handler = RotatingFileHandler(
os.path.join(log_dir, 'myp_platform.log'),
maxBytes=Config.LOG_FILE_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.WARNING)
app.logger.addHandler(file_handler)
app.logger.setLevel(logging.WARNING)
# Configuration dictionary for easy access
config = {
'development': DevelopmentConfig,
'testing': TestingConfig,
'production': ProductionConfig,
'default': DevelopmentConfig
}
def get_config_by_name(config_name):
"""
Get configuration class by name.
Args:
config_name (str): Name of the configuration ('development', 'testing', 'production')
Returns:
Config: Configuration class
"""
return config.get(config_name, config['default'])

View File

@ -0,0 +1,81 @@
"""
Sicherheitskonfiguration für die MYP Platform
"""
# Sicherheits-Headers für HTTP-Responses
SECURITY_HEADERS = {
'Content-Security-Policy': (
"default-src 'self'; "
"script-src 'self' 'unsafe-eval' 'unsafe-inline'; "
"script-src-elem 'self' 'unsafe-inline'; "
"style-src 'self' 'unsafe-inline'; "
"font-src 'self'; "
"img-src 'self' data:; "
"connect-src 'self'; "
"worker-src 'self' blob:; "
"frame-src 'none'; "
"object-src 'none'; "
"base-uri 'self'; "
"form-action 'self'; "
"frame-ancestors 'none';"
),
'X-Content-Type-Options': 'nosniff',
'X-Frame-Options': 'DENY',
'X-XSS-Protection': '1; mode=block',
'Strict-Transport-Security': 'max-age=31536000; includeSubDomains',
'Referrer-Policy': 'strict-origin-when-cross-origin',
'Permissions-Policy': 'geolocation=(), microphone=(), camera=()'
}
# Rate Limiting Konfiguration
RATE_LIMITS = {
'default': "200 per day, 50 per hour",
'login': "5 per minute",
'api': "100 per hour",
'admin': "500 per hour"
}
# Session-Sicherheit
SESSION_CONFIG = {
'SESSION_COOKIE_SECURE': False, # Für Offline-Betrieb auf False setzen
'SESSION_COOKIE_HTTPONLY': True,
'SESSION_COOKIE_SAMESITE': 'Lax',
'PERMANENT_SESSION_LIFETIME': 3600 # 1 Stunde
}
# CSRF-Schutz
CSRF_CONFIG = {
'CSRF_ENABLED': True,
'CSRF_SESSION_KEY': 'csrf_token',
'CSRF_TIME_LIMIT': 3600
}
class SecurityConfig:
"""Sicherheitskonfiguration für die Anwendung"""
def __init__(self):
self.headers = SECURITY_HEADERS
self.rate_limits = RATE_LIMITS
self.session_config = SESSION_CONFIG
self.csrf_config = CSRF_CONFIG
def get_headers(self):
"""Gibt die Sicherheits-Headers zurück"""
return self.headers
def get_rate_limits(self):
"""Gibt die Rate-Limiting-Konfiguration zurück"""
return self.rate_limits
def get_session_config(self):
"""Gibt die Session-Konfiguration zurück"""
return self.session_config
def get_csrf_config(self):
"""Gibt die CSRF-Konfiguration zurück"""
return self.csrf_config
def get_security_headers():
"""Gibt die Sicherheits-Headers zurück"""
return SECURITY_HEADERS

187
backend/config/settings.py Normal file
View File

@ -0,0 +1,187 @@
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)
# Hardcodierte Konfiguration
SECRET_KEY = "7445630171969DFAC92C53CEC92E67A9CB2E00B3CB2F"
# Dynamische Pfade basierend auf dem aktuellen Arbeitsverzeichnis
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # backend/app
PROJECT_ROOT = os.path.dirname(BASE_DIR) # backend
DATABASE_PATH = os.path.join(BASE_DIR, "database", "myp.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"]
LOG_LEVEL = "INFO"
LOG_FORMAT = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
LOG_DATE_FORMAT = "%Y-%m-%d %H:%M:%S"
# Flask-Konfiguration
FLASK_HOST = "0.0.0.0"
FLASK_PORT = 443 # Geändert von 443 auf 8443 (nicht-privilegierter Port)
FLASK_FALLBACK_PORT = 8080 # Geändert von 80 auf 8080 (nicht-privilegierter Port)
FLASK_DEBUG = True
SESSION_LIFETIME = timedelta(hours=2) # Reduziert von 7 Tagen auf 2 Stunden für bessere Sicherheit
# Upload-Konfiguration
UPLOAD_FOLDER = os.path.join(BASE_DIR, "uploads")
ALLOWED_EXTENSIONS = {'txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif', 'gcode', '3mf', 'stl'}
MAX_CONTENT_LENGTH = 16 * 1024 * 1024 # 16MB Maximum-Dateigröße
# 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}"
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)
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)
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 neuen SSL-Manager.
"""
try:
# Verwende den neuen 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

View File

@ -0,0 +1,116 @@
import os
import json
from datetime import timedelta
# Hardcodierte Konfiguration
SECRET_KEY = "7445630171969DFAC92C53CEC92E67A9CB2E00B3CB2F"
DATABASE_PATH = "database/myp.db"
TAPO_USERNAME = "till.tomczak@mercedes-benz.com"
TAPO_PASSWORD = "744563017196A"
# 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 = "logs"
LOG_SUBDIRS = ["app", "scheduler", "auth", "jobs", "printers", "errors"]
LOG_LEVEL = "INFO"
LOG_FORMAT = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
LOG_DATE_FORMAT = "%Y-%m-%d %H:%M:%S"
# Flask-Konfiguration
FLASK_HOST = "0.0.0.0"
FLASK_PORT = 443
FLASK_FALLBACK_PORT = 80
FLASK_DEBUG = True
SESSION_LIFETIME = timedelta(days=7)
# SSL-Konfiguration
SSL_ENABLED = True
SSL_CERT_PATH = "instance/ssl/myp.crt"
SSL_KEY_PATH = "instance/ssl/myp.key"
SSL_HOSTNAME = "raspberrypi"
# Scheduler-Konfiguration
SCHEDULER_INTERVAL = 60 # Sekunden
SCHEDULER_ENABLED = True
# Datenbank-Konfiguration
DB_ENGINE = f"sqlite:///{DATABASE_PATH}"
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)
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 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()
# Prüfen, ob wir uns im Entwicklungsmodus befinden
if FLASK_DEBUG:
print("SSL-Zertifikate nicht gefunden. Erstelle selbstsignierte Zertifikate...")
# Pfad zum create_ssl_cert.sh-Skript ermitteln
script_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))),
"install", "create_ssl_cert.sh")
# Ausführungsrechte setzen
if os.path.exists(script_path):
os.system(f"chmod +x {script_path}")
# Zertifikate erstellen mit spezifischem Hostnamen
os.system(f"{script_path} -c {SSL_CERT_PATH} -k {SSL_KEY_PATH} -h {SSL_HOSTNAME}")
else:
print(f"WARNUNG: SSL-Zertifikat-Generator nicht gefunden: {script_path}")
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)