Sicherheits-Härtung und Bugfixes

This commit is contained in:
Patrick Asmus
2026-10-11 17:39:03 +02:00
parent c3f68ace96
commit 65b0b5ac0f
25 changed files with 2577 additions and 686 deletions
+56 -8
View File
@@ -1,9 +1,25 @@
import datetime
from sqlalchemy import create_engine, Column, Integer, String, Boolean, DateTime, Text, text
import logging
from sqlalchemy import create_engine, event, Column, Integer, String, Boolean, DateTime, Text, text
from sqlalchemy.orm import declarative_base, sessionmaker
Base = declarative_base()
logger = logging.getLogger("models")
def utcnow() -> datetime.datetime:
return datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None)
def iso_utc(dt: datetime.datetime | None) -> str | None:
if dt is None:
return None
if dt.tzinfo is None:
dt = dt.replace(tzinfo=datetime.timezone.utc)
return dt.astimezone(datetime.timezone.utc).isoformat()
class Job(Base):
__tablename__ = "jobs"
@@ -37,8 +53,8 @@ class Job(Base):
station_id = Column(Integer, nullable=True)
created_at = Column(DateTime, default=datetime.datetime.utcnow)
updated_at = Column(DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow)
created_at = Column(DateTime, default=utcnow)
updated_at = Column(DateTime, default=utcnow, onupdate=utcnow)
class Station(Base):
@@ -49,8 +65,8 @@ class Station(Base):
stream_url = Column(String(1000), nullable=False)
stream_type = Column(String(20), default="auto")
description = Column(Text, default="")
created_at = Column(DateTime, default=datetime.datetime.utcnow)
updated_at = Column(DateTime, default=datetime.datetime.utcnow, onupdate=datetime.datetime.utcnow)
created_at = Column(DateTime, default=utcnow)
updated_at = Column(DateTime, default=utcnow, onupdate=utcnow)
class Recording(Base):
@@ -65,7 +81,7 @@ class Recording(Base):
output_file = Column(String(500), default="")
pid = Column(Integer, nullable=True)
status = Column(String(20), default="running") # running, stopped, completed, error
started_at = Column(DateTime, default=datetime.datetime.utcnow)
started_at = Column(DateTime, default=utcnow)
stopped_at = Column(DateTime, nullable=True)
scheduled_stop = Column(DateTime, nullable=True)
segment_mode = Column(Boolean, default=False)
@@ -79,7 +95,7 @@ class LogEntry(Base):
__tablename__ = "logs"
id = Column(Integer, primary_key=True, autoincrement=True)
timestamp = Column(DateTime, default=datetime.datetime.utcnow)
timestamp = Column(DateTime, default=utcnow)
level = Column(String(10), default="INFO")
message = Column(Text, nullable=False)
job_name = Column(String(200), nullable=True)
@@ -91,7 +107,20 @@ _SessionLocal = None
def init_db(db_path: str):
global _engine, _SessionLocal
_engine = create_engine(f"sqlite:///{db_path}", connect_args={"check_same_thread": False})
_engine = create_engine(
f"sqlite:///{db_path}",
connect_args={"check_same_thread": False, "timeout": 30},
)
@event.listens_for(_engine, "connect")
def _set_sqlite_pragmas(dbapi_conn, _record):
cur = dbapi_conn.cursor()
try:
cur.execute("PRAGMA journal_mode=WAL")
cur.execute("PRAGMA busy_timeout=5000")
finally:
cur.close()
Base.metadata.create_all(_engine)
_SessionLocal = sessionmaker(bind=_engine)
_migrate(_engine)
@@ -115,3 +144,22 @@ def _migrate(engine):
def get_session():
return _SessionLocal()
def prune_logs(days: int) -> int:
if days <= 0:
return 0
cutoff = utcnow() - datetime.timedelta(days=days)
session = get_session()
try:
count = session.query(LogEntry).filter(LogEntry.timestamp < cutoff).delete(synchronize_session=False)
session.commit()
if count:
logger.info("%d alte Log-Einträge gelöscht (älter als %d Tage)", count, days)
return count
except Exception as e:
session.rollback()
logger.error("Log-Bereinigung fehlgeschlagen: %s", e)
return 0
finally:
session.close()