FIN INIT
This commit is contained in:
@@ -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.
Binary file not shown.
Binary file not shown.
@@ -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, "database", "myp.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'])
|
||||
@@ -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
|
||||
@@ -0,0 +1,188 @@
|
||||
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
|
||||
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}"
|
||||
|
||||
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
|
||||
@@ -0,0 +1,173 @@
|
||||
import os
|
||||
import json
|
||||
from datetime import timedelta, datetime
|
||||
|
||||
# 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...")
|
||||
|
||||
# SSL-Zertifikate direkt mit Python erstellen
|
||||
try:
|
||||
from cryptography import x509
|
||||
from cryptography.x509.oid import NameOID
|
||||
from cryptography.hazmat.primitives import hashes
|
||||
from cryptography.hazmat.primitives.asymmetric import rsa
|
||||
from cryptography.hazmat.primitives import serialization
|
||||
import ipaddress
|
||||
|
||||
# Private Key generieren
|
||||
private_key = rsa.generate_private_key(
|
||||
public_exponent=65537,
|
||||
key_size=2048,
|
||||
)
|
||||
|
||||
# Subject und Issuer für Mercedes-Benz Werk Berlin 040
|
||||
subject = issuer = x509.Name([
|
||||
x509.NameAttribute(NameOID.COUNTRY_NAME, "DE"),
|
||||
x509.NameAttribute(NameOID.STATE_OR_PROVINCE_NAME, "Berlin"),
|
||||
x509.NameAttribute(NameOID.LOCALITY_NAME, "Berlin"),
|
||||
x509.NameAttribute(NameOID.ORGANIZATION_NAME, "Mercedes-Benz AG"),
|
||||
x509.NameAttribute(NameOID.ORGANIZATIONAL_UNIT_NAME, "Werk Berlin 040"),
|
||||
x509.NameAttribute(NameOID.COMMON_NAME, "raspberrypi"),
|
||||
])
|
||||
|
||||
# Zertifikat erstellen
|
||||
cert = x509.CertificateBuilder().subject_name(
|
||||
subject
|
||||
).issuer_name(
|
||||
issuer
|
||||
).public_key(
|
||||
private_key.public_key()
|
||||
).serial_number(
|
||||
x509.random_serial_number()
|
||||
).not_valid_before(
|
||||
datetime.utcnow()
|
||||
).not_valid_after(
|
||||
datetime.utcnow() + timedelta(days=365)
|
||||
).add_extension(
|
||||
x509.SubjectAlternativeName([
|
||||
x509.DNSName("raspberrypi"),
|
||||
x509.DNSName("localhost"),
|
||||
x509.IPAddress(ipaddress.IPv4Address("192.168.0.105")),
|
||||
x509.IPAddress(ipaddress.IPv4Address("127.0.0.1")),
|
||||
]),
|
||||
critical=False,
|
||||
).sign(private_key, hashes.SHA256())
|
||||
|
||||
# Zertifikat speichern
|
||||
with open(SSL_CERT_PATH, "wb") as f:
|
||||
f.write(cert.public_bytes(serialization.Encoding.PEM))
|
||||
|
||||
# Private Key speichern
|
||||
with open(SSL_KEY_PATH, "wb") as f:
|
||||
f.write(private_key.private_bytes(
|
||||
encoding=serialization.Encoding.PEM,
|
||||
format=serialization.PrivateFormat.PKCS8,
|
||||
encryption_algorithm=serialization.NoEncryption()
|
||||
))
|
||||
|
||||
print(f"✅ SSL-Zertifikate erfolgreich erstellt für Mercedes-Benz Werk Berlin 040")
|
||||
print(f" Hostname: raspberrypi")
|
||||
print(f" IP: 192.168.0.105")
|
||||
|
||||
except ImportError:
|
||||
print("FEHLER: cryptography-Bibliothek nicht installiert. Installiere mit: pip install cryptography")
|
||||
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)
|
||||
Reference in New Issue
Block a user