import logging from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy import text from sqlalchemy.orm import Session from app.database import engine, get_db from app.models.db_models import FilterLog logger = logging.getLogger(__name__) router = APIRouter(prefix="/api/logs", tags=["logs"]) @router.get("/") def get_logs( account_id: int | None = None, level: str | None = None, search: str | None = None, search_subject: str | None = None, search_from: str | None = None, search_rule: str | None = None, search_message: str | None = None, search_details: str | None = None, search_folder: str | None = None, limit: int = Query(default=100, le=500), offset: int = 0, db: Session = Depends(get_db), ): query = db.query(FilterLog).order_by(FilterLog.created_at.desc()) if account_id: query = query.filter(FilterLog.account_id == account_id) if level: query = query.filter(FilterLog.level == level) # Einfache Suche (ODER über alle Felder) if search: term = f"%{search}%" query = query.filter( FilterLog.message.ilike(term) | FilterLog.mail_subject.ilike(term) | FilterLog.mail_from.ilike(term) | FilterLog.rule_name.ilike(term) | FilterLog.details.ilike(term) | FilterLog.folder.ilike(term) ) # Erweiterte Suche (UND pro Feld) if search_subject: query = query.filter(FilterLog.mail_subject.ilike(f"%{search_subject}%")) if search_from: query = query.filter(FilterLog.mail_from.ilike(f"%{search_from}%")) if search_rule: query = query.filter(FilterLog.rule_name.ilike(f"%{search_rule}%")) if search_message: query = query.filter(FilterLog.message.ilike(f"%{search_message}%")) if search_details: query = query.filter(FilterLog.details.ilike(f"%{search_details}%")) if search_folder: query = query.filter(FilterLog.folder.ilike(f"%{search_folder}%")) total = query.count() logs = query.offset(offset).limit(limit).all() return { "total": total, "logs": [ { "id": log.id, "account_id": log.account_id, "account_name": log.account_name, "level": log.level.value if log.level else "info", "message": log.message, "rule_name": log.rule_name, "action_type": log.action_type, "mail_uid": log.mail_uid, "mail_subject": log.mail_subject, "mail_from": log.mail_from, "folder": log.folder, "details": log.details, "created_at": log.created_at.isoformat() if log.created_at else None, } for log in logs ], } @router.delete("/") def clear_logs(account_id: int | None = None, db: Session = Depends(get_db)): """Löscht Logs. Ohne account_id: schnelles Drop+Recreate der Tabelle (sekundenschnell auch bei Millionen Zeilen). Mit account_id: normales DELETE.""" try: if account_id is None: # Vollständiges Leeren: DROP+CREATE statt DELETE. # DELETE FROM filter_logs würde jede Zeile einzeln ins WAL schreiben, # bei Millionen Einträgen dauert das ewig und blockiert die DB. db.close() # Sitzung freigeben, sonst holds sie noch Locks row_count = 0 with engine.begin() as conn: row_count = conn.execute(text("SELECT COUNT(*) FROM filter_logs")).scalar() or 0 FilterLog.__table__.drop(conn, checkfirst=True) FilterLog.__table__.create(conn, checkfirst=True) # PRAGMA wal_checkpoint(TRUNCATE) räumt die WAL-Datei auf conn.execute(text("PRAGMA wal_checkpoint(TRUNCATE)")) logger.info("Log vollständig geleert (%d Einträge, Tabelle neu angelegt)", row_count) return {"deleted": row_count} count = db.query(FilterLog).filter(FilterLog.account_id == account_id).delete() db.commit() return {"deleted": count} except Exception as e: logger.exception("clear_logs fehlgeschlagen") raise HTTPException(500, f"Fehler beim Leeren des Logs: {e}")