239 lines
7.5 KiB
Python

"""
Konfigurationsklassen für die MYP Flask-Anwendung.
Definiert verschiedene Konfigurationen für Development, Production und Testing.
"""
import os
from datetime import timedelta
import secrets
class Config:
"""Basis-Konfigurationsklasse mit gemeinsamen Einstellungen."""
SECRET_KEY = os.environ.get('SECRET_KEY') or secrets.token_hex(32)
DATABASE = os.environ.get('DATABASE_PATH', 'instance/myp.db')
# Session-Konfiguration
SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_SAMESITE = 'Lax'
PERMANENT_SESSION_LIFETIME = timedelta(days=7)
# Job-Konfiguration
JOB_CHECK_INTERVAL = int(os.environ.get('JOB_CHECK_INTERVAL', '60')) # Sekunden
# Tapo-Konfiguration
TAPO_USERNAME = os.environ.get('TAPO_USERNAME')
TAPO_PASSWORD = os.environ.get('TAPO_PASSWORD')
# Logging-Konfiguration
LOG_LEVEL = os.environ.get('LOG_LEVEL', 'INFO')
LOG_MAX_BYTES = int(os.environ.get('LOG_MAX_BYTES', '10485760')) # 10MB
LOG_BACKUP_COUNT = int(os.environ.get('LOG_BACKUP_COUNT', '10'))
# 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')
# Rate Limiting
RATE_LIMIT_ENABLED = True
MAX_REQUESTS_PER_MINUTE = int(os.environ.get('MAX_REQUESTS_PER_MINUTE', '100'))
RATE_LIMIT_WINDOW_MINUTES = int(os.environ.get('RATE_LIMIT_WINDOW_MINUTES', '15'))
# Security
SECURITY_ENABLED = True
MAX_CONTENT_LENGTH = 16 * 1024 * 1024 # 16MB
@staticmethod
def init_app(app):
"""Initialisierung der Anwendung mit der Konfiguration."""
pass
class DevelopmentConfig(Config):
"""Konfiguration für die Entwicklungsumgebung."""
DEBUG = True
TESTING = False
# Session-Cookies in Development weniger strikt
SESSION_COOKIE_SECURE = False
# Kürzere Job-Check-Intervalle für schnellere Entwicklung
JOB_CHECK_INTERVAL = int(os.environ.get('JOB_CHECK_INTERVAL', '30'))
# Weniger strikte Sicherheit in Development
SECURITY_ENABLED = False
RATE_LIMIT_ENABLED = False
@staticmethod
def init_app(app):
Config.init_app(app)
# Development-spezifische Initialisierung
import logging
logging.basicConfig(level=logging.DEBUG)
class ProductionConfig(Config):
"""Konfiguration für die Produktionsumgebung."""
DEBUG = False
TESTING = False
# Sichere Session-Cookies in Production
SESSION_COOKIE_SECURE = True
SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_SAMESITE = 'Strict'
# Strengere Sicherheitseinstellungen
WTF_CSRF_ENABLED = True
WTF_CSRF_TIME_LIMIT = None
# Längere Job-Check-Intervalle für bessere Performance
JOB_CHECK_INTERVAL = int(os.environ.get('JOB_CHECK_INTERVAL', '60'))
# Produktions-Sicherheit
SECURITY_ENABLED = True
RATE_LIMIT_ENABLED = True
MAX_REQUESTS_PER_MINUTE = int(os.environ.get('MAX_REQUESTS_PER_MINUTE', '60'))
# HTTPS-Enforcement (wenn verfügbar)
FORCE_HTTPS = os.environ.get('FORCE_HTTPS', 'False').lower() == 'true'
@staticmethod
def init_app(app):
Config.init_app(app)
# Production-spezifische Initialisierung
import logging
from logging.handlers import RotatingFileHandler, SysLogHandler
# Datei-Logging
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_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_handler.setFormatter(logging.Formatter(
'%(asctime)s SECURITY %(levelname)s: %(message)s [%(name)s]'
))
security_handler.setLevel(logging.WARNING)
# Security-Logger
security_logger = logging.getLogger('security')
security_logger.addHandler(security_handler)
security_logger.setLevel(logging.WARNING)
app.logger.setLevel(logging.INFO)
app.logger.info('MYP Backend starting in production mode')
# 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."""
DEBUG = True
TESTING = True
# In-Memory-Datenbank für Tests
DATABASE = ':memory:'
# Deaktiviere CSRF für Tests
WTF_CSRF_ENABLED = False
# Kürzere Session-Lebensdauer für Tests
PERMANENT_SESSION_LIFETIME = timedelta(minutes=5)
# Kürzere Job-Check-Intervalle für Tests
JOB_CHECK_INTERVAL = 5
# Deaktiviere Sicherheit für Tests
SECURITY_ENABLED = False
RATE_LIMIT_ENABLED = False
@staticmethod
def init_app(app):
Config.init_app(app)
# Konfigurationsmapping
config = {
'development': DevelopmentConfig,
'production': ProductionConfig,
'testing': TestingConfig,
'default': DevelopmentConfig
}