📝 "Refactor authentication flow
This commit is contained in:
parent
b5fca69a0f
commit
7460ce3e12
@ -36,7 +36,7 @@ else:
|
||||
get_windows_thread_manager = None
|
||||
|
||||
# Lokale Imports
|
||||
from models import init_database, create_initial_admin, User, Printer, Job, Stats, SystemLog, get_db_session, GuestRequest, UserPermission, Notification, JobOrder, Base, get_engine
|
||||
from models import init_database, create_initial_admin, User, Printer, Job, Stats, SystemLog, get_db_session, GuestRequest, UserPermission, Notification, JobOrder, Base, get_engine, PlugStatusLog
|
||||
from utils.logging_config import setup_logging, get_logger, measure_execution_time, log_startup_info, debug_request, debug_response
|
||||
from utils.job_scheduler import JobScheduler, get_job_scheduler
|
||||
from utils.queue_manager import start_queue_manager, stop_queue_manager, get_queue_manager
|
||||
|
@ -43,7 +43,7 @@ _cache_lock = threading.Lock()
|
||||
_cache_ttl = {} # Time-to-live für Cache-Einträge
|
||||
|
||||
# Alle exportierten Modelle
|
||||
__all__ = ['User', 'Printer', 'Job', 'Stats', 'SystemLog', 'Base', 'GuestRequest', 'UserPermission', 'Notification', 'JobOrder', 'SystemTimer', 'init_db', 'init_database', 'create_initial_admin', 'get_db_session', 'get_cached_session', 'clear_cache', 'engine']
|
||||
__all__ = ['User', 'Printer', 'Job', 'Stats', 'SystemLog', 'Base', 'GuestRequest', 'UserPermission', 'Notification', 'JobOrder', 'SystemTimer', 'PlugStatusLog', 'init_db', 'init_database', 'create_initial_admin', 'get_db_session', 'get_cached_session', 'clear_cache', 'engine']
|
||||
|
||||
# ===== DATENBANK-KONFIGURATION MIT WAL UND OPTIMIERUNGEN =====
|
||||
|
||||
@ -1595,6 +1595,313 @@ class SystemTimer(Base):
|
||||
return None
|
||||
|
||||
|
||||
class PlugStatusLog(Base):
|
||||
"""
|
||||
Logging-System für Steckdosen-Status Monitoring.
|
||||
Protokolliert alle Zustandsänderungen der Smart Plugs (TAPO).
|
||||
"""
|
||||
__tablename__ = "plug_status_logs"
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
printer_id = Column(Integer, ForeignKey("printers.id"), nullable=False)
|
||||
status = Column(String(20), nullable=False) # 'connected', 'disconnected', 'on', 'off'
|
||||
timestamp = Column(DateTime, default=datetime.now, nullable=False)
|
||||
|
||||
# Zusätzliche Monitoring-Daten
|
||||
ip_address = Column(String(50), nullable=True) # IP der Steckdose/des Druckers
|
||||
power_consumption = Column(Float, nullable=True) # Stromverbrauch in Watt (falls verfügbar)
|
||||
voltage = Column(Float, nullable=True) # Spannung in Volt (falls verfügbar)
|
||||
current = Column(Float, nullable=True) # Stromstärke in Ampere (falls verfügbar)
|
||||
|
||||
# Monitoring-Kontext
|
||||
source = Column(String(50), default="system") # 'system', 'manual', 'api', 'scheduler'
|
||||
user_id = Column(Integer, ForeignKey("users.id"), nullable=True) # Bei manueller Änderung
|
||||
notes = Column(Text, nullable=True) # Zusätzliche Notizen oder Fehlerinfos
|
||||
|
||||
# Technische Details
|
||||
response_time_ms = Column(Integer, nullable=True) # Antwortzeit der Steckdose in ms
|
||||
error_message = Column(Text, nullable=True) # Fehlermeldung bei Verbindungsproblemen
|
||||
firmware_version = Column(String(50), nullable=True) # Firmware-Version der Steckdose
|
||||
|
||||
# Beziehungen
|
||||
printer = relationship("Printer", foreign_keys=[printer_id])
|
||||
user = relationship("User", foreign_keys=[user_id])
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
"""
|
||||
Konvertiert das PlugStatusLog-Objekt in ein Dictionary.
|
||||
"""
|
||||
cache_key = get_cache_key("PlugStatusLog", self.id, "dict")
|
||||
cached_result = get_cache(cache_key)
|
||||
|
||||
if cached_result is not None:
|
||||
return cached_result
|
||||
|
||||
result = {
|
||||
"id": self.id,
|
||||
"printer_id": self.printer_id,
|
||||
"printer_name": self.printer.name if self.printer else None,
|
||||
"status": self.status,
|
||||
"timestamp": self.timestamp.isoformat() if self.timestamp else None,
|
||||
"ip_address": self.ip_address,
|
||||
"power_consumption": self.power_consumption,
|
||||
"voltage": self.voltage,
|
||||
"current": self.current,
|
||||
"source": self.source,
|
||||
"user_id": self.user_id,
|
||||
"user_name": self.user.name if self.user else None,
|
||||
"notes": self.notes,
|
||||
"response_time_ms": self.response_time_ms,
|
||||
"error_message": self.error_message,
|
||||
"firmware_version": self.firmware_version
|
||||
}
|
||||
|
||||
# Ergebnis cachen (5 Minuten)
|
||||
set_cache(cache_key, result, 300)
|
||||
return result
|
||||
|
||||
@classmethod
|
||||
def log_status_change(cls, printer_id: int, status: str, source: str = "system",
|
||||
user_id: int = None, ip_address: str = None,
|
||||
power_consumption: float = None, voltage: float = None,
|
||||
current: float = None, notes: str = None,
|
||||
response_time_ms: int = None, error_message: str = None,
|
||||
firmware_version: str = None) -> 'PlugStatusLog':
|
||||
"""
|
||||
Erstellt einen neuen Status-Log-Eintrag für eine Steckdose.
|
||||
|
||||
Args:
|
||||
printer_id: ID des zugehörigen Druckers
|
||||
status: Status der Steckdose ('connected', 'disconnected', 'on', 'off')
|
||||
source: Quelle der Statusänderung ('system', 'manual', 'api', 'scheduler')
|
||||
user_id: ID des Benutzers (bei manueller Änderung)
|
||||
ip_address: IP-Adresse der Steckdose
|
||||
power_consumption: Stromverbrauch in Watt
|
||||
voltage: Spannung in Volt
|
||||
current: Stromstärke in Ampere
|
||||
notes: Zusätzliche Notizen
|
||||
response_time_ms: Antwortzeit in Millisekunden
|
||||
error_message: Fehlermeldung bei Problemen
|
||||
firmware_version: Firmware-Version der Steckdose
|
||||
|
||||
Returns:
|
||||
Das erstellte PlugStatusLog-Objekt
|
||||
"""
|
||||
try:
|
||||
with get_cached_session() as session:
|
||||
log_entry = cls(
|
||||
printer_id=printer_id,
|
||||
status=status,
|
||||
ip_address=ip_address,
|
||||
power_consumption=power_consumption,
|
||||
voltage=voltage,
|
||||
current=current,
|
||||
source=source,
|
||||
user_id=user_id,
|
||||
notes=notes,
|
||||
response_time_ms=response_time_ms,
|
||||
error_message=error_message,
|
||||
firmware_version=firmware_version
|
||||
)
|
||||
|
||||
session.add(log_entry)
|
||||
session.commit()
|
||||
|
||||
# Cache invalidieren
|
||||
invalidate_model_cache("PlugStatusLog")
|
||||
|
||||
logger.info(f"Steckdosen-Status geloggt: Drucker {printer_id}, Status: {status}, Quelle: {source}")
|
||||
return log_entry
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Loggen des Steckdosen-Status: {str(e)}")
|
||||
raise e
|
||||
|
||||
@classmethod
|
||||
def get_printer_history(cls, printer_id: int, hours: int = 24) -> List['PlugStatusLog']:
|
||||
"""
|
||||
Holt die Steckdosen-Historie für einen bestimmten Drucker.
|
||||
|
||||
Args:
|
||||
printer_id: ID des Druckers
|
||||
hours: Anzahl der Stunden zurück (Standard: 24)
|
||||
|
||||
Returns:
|
||||
Liste der PlugStatusLog-Einträge
|
||||
"""
|
||||
cache_key = get_cache_key("PlugStatusLog", printer_id, f"history_{hours}h")
|
||||
cached_result = get_cache(cache_key)
|
||||
|
||||
if cached_result is not None:
|
||||
return cached_result
|
||||
|
||||
try:
|
||||
with get_cached_session() as session:
|
||||
cutoff_time = datetime.now() - timedelta(hours=hours)
|
||||
|
||||
logs = session.query(cls)\
|
||||
.filter(cls.printer_id == printer_id)\
|
||||
.filter(cls.timestamp >= cutoff_time)\
|
||||
.order_by(cls.timestamp.desc())\
|
||||
.all()
|
||||
|
||||
# Ergebnis cachen (10 Minuten)
|
||||
set_cache(cache_key, logs, 600)
|
||||
return logs
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Abrufen der Steckdosen-Historie: {str(e)}")
|
||||
return []
|
||||
|
||||
@classmethod
|
||||
def get_all_recent_logs(cls, hours: int = 24, limit: int = 1000) -> List['PlugStatusLog']:
|
||||
"""
|
||||
Holt alle aktuellen Steckdosen-Logs für die Administrator-Übersicht.
|
||||
|
||||
Args:
|
||||
hours: Anzahl der Stunden zurück (Standard: 24)
|
||||
limit: Maximale Anzahl der Einträge (Standard: 1000)
|
||||
|
||||
Returns:
|
||||
Liste der PlugStatusLog-Einträge
|
||||
"""
|
||||
cache_key = get_cache_key("PlugStatusLog", "all", f"recent_{hours}h_{limit}")
|
||||
cached_result = get_cache(cache_key)
|
||||
|
||||
if cached_result is not None:
|
||||
return cached_result
|
||||
|
||||
try:
|
||||
with get_cached_session() as session:
|
||||
cutoff_time = datetime.now() - timedelta(hours=hours)
|
||||
|
||||
logs = session.query(cls)\
|
||||
.filter(cls.timestamp >= cutoff_time)\
|
||||
.order_by(cls.timestamp.desc())\
|
||||
.limit(limit)\
|
||||
.all()
|
||||
|
||||
# Ergebnis cachen (5 Minuten für Admin-Übersicht)
|
||||
set_cache(cache_key, logs, 300)
|
||||
return logs
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Abrufen der aktuellen Steckdosen-Logs: {str(e)}")
|
||||
return []
|
||||
|
||||
@classmethod
|
||||
def get_status_statistics(cls, hours: int = 24) -> Dict[str, Any]:
|
||||
"""
|
||||
Erstellt Statistiken über Steckdosen-Status für einen Zeitraum.
|
||||
|
||||
Args:
|
||||
hours: Anzahl der Stunden zurück (Standard: 24)
|
||||
|
||||
Returns:
|
||||
Dictionary mit Statistiken
|
||||
"""
|
||||
cache_key = get_cache_key("PlugStatusLog", "stats", f"{hours}h")
|
||||
cached_result = get_cache(cache_key)
|
||||
|
||||
if cached_result is not None:
|
||||
return cached_result
|
||||
|
||||
try:
|
||||
with get_cached_session() as session:
|
||||
cutoff_time = datetime.now() - timedelta(hours=hours)
|
||||
|
||||
# Gesamtanzahl der Logs
|
||||
total_logs = session.query(cls)\
|
||||
.filter(cls.timestamp >= cutoff_time)\
|
||||
.count()
|
||||
|
||||
# Status-Verteilung
|
||||
status_counts = session.query(cls.status, func.count(cls.id))\
|
||||
.filter(cls.timestamp >= cutoff_time)\
|
||||
.group_by(cls.status)\
|
||||
.all()
|
||||
|
||||
# Drucker mit den meisten Statusänderungen
|
||||
printer_counts = session.query(cls.printer_id, func.count(cls.id))\
|
||||
.filter(cls.timestamp >= cutoff_time)\
|
||||
.group_by(cls.printer_id)\
|
||||
.order_by(func.count(cls.id).desc())\
|
||||
.limit(10)\
|
||||
.all()
|
||||
|
||||
# Durchschnittliche Antwortzeit
|
||||
avg_response_time = session.query(func.avg(cls.response_time_ms))\
|
||||
.filter(cls.timestamp >= cutoff_time)\
|
||||
.filter(cls.response_time_ms.isnot(None))\
|
||||
.scalar()
|
||||
|
||||
# Fehlerrate
|
||||
error_count = session.query(cls)\
|
||||
.filter(cls.timestamp >= cutoff_time)\
|
||||
.filter(cls.error_message.isnot(None))\
|
||||
.count()
|
||||
|
||||
stats = {
|
||||
"total_logs": total_logs,
|
||||
"status_distribution": dict(status_counts),
|
||||
"top_printers": dict(printer_counts),
|
||||
"average_response_time_ms": float(avg_response_time) if avg_response_time else None,
|
||||
"error_count": error_count,
|
||||
"error_rate": (error_count / total_logs * 100) if total_logs > 0 else 0,
|
||||
"timeframe_hours": hours,
|
||||
"generated_at": datetime.now().isoformat()
|
||||
}
|
||||
|
||||
# Ergebnis cachen (10 Minuten)
|
||||
set_cache(cache_key, stats, 600)
|
||||
return stats
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Erstellen der Steckdosen-Statistiken: {str(e)}")
|
||||
return {
|
||||
"total_logs": 0,
|
||||
"status_distribution": {},
|
||||
"top_printers": {},
|
||||
"average_response_time_ms": None,
|
||||
"error_count": 0,
|
||||
"error_rate": 0,
|
||||
"timeframe_hours": hours,
|
||||
"generated_at": datetime.now().isoformat(),
|
||||
"error": str(e)
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def cleanup_old_logs(cls, days: int = 30) -> int:
|
||||
"""
|
||||
Bereinigt alte Steckdosen-Logs (älter als X Tage).
|
||||
|
||||
Args:
|
||||
days: Anzahl der Tage (Standard: 30)
|
||||
|
||||
Returns:
|
||||
Anzahl der gelöschten Einträge
|
||||
"""
|
||||
try:
|
||||
with get_cached_session() as session:
|
||||
cutoff_date = datetime.now() - timedelta(days=days)
|
||||
|
||||
deleted_count = session.query(cls)\
|
||||
.filter(cls.timestamp < cutoff_date)\
|
||||
.delete()
|
||||
|
||||
session.commit()
|
||||
|
||||
# Cache invalidieren
|
||||
invalidate_model_cache("PlugStatusLog")
|
||||
|
||||
logger.info(f"Steckdosen-Logs bereinigt: {deleted_count} Einträge gelöscht (älter als {days} Tage)")
|
||||
return deleted_count
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Fehler beim Bereinigen der Steckdosen-Logs: {str(e)}")
|
||||
return 0
|
||||
|
||||
|
||||
# ===== DATENBANK-INITIALISIERUNG MIT OPTIMIERUNGEN =====
|
||||
|
||||
def init_db() -> None:
|
||||
|
1894
backend/setup.sh
1894
backend/setup.sh
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user