Files
gh-christianlouis-dmarq/backend/app/api/api_v1/endpoints/imap.py
T
2026-05-22 23:48:00 +02:00

141 lines
4.7 KiB
Python

import logging
from datetime import datetime
from typing import Any, Dict, Optional
from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException
from pydantic import BaseModel
from starlette.concurrency import run_in_threadpool
from app.core.database import SessionLocal
from app.core.security import require_admin_auth
from app.services.imap_client import IMAPClient
router = APIRouter()
logger = logging.getLogger(__name__)
class IMAPTestRequest(BaseModel):
"""IMAP connection test request body"""
server: Optional[str] = None
port: int = 993
username: Optional[str] = None
password: Optional[str] = None
ssl: bool = True
def _fetch_imap_reports_sync(days: int, delete_emails: Optional[bool]) -> Dict[str, Any]:
"""Fetch IMAP reports with a standalone DB session."""
db = SessionLocal()
try:
imap_client = IMAPClient(delete_emails=delete_emails, db=db)
results = imap_client.fetch_reports(days=days)
db.commit()
return results
except Exception:
db.rollback()
raise
finally:
db.close()
def _fetch_imap_reports_background(days: int, delete_emails: Optional[bool]) -> None:
"""Fetch IMAP reports from a FastAPI background task."""
_fetch_imap_reports_sync(days, delete_emails)
@router.post("/test-connection")
async def test_imap_connection(
request: IMAPTestRequest,
_auth: dict = Depends(require_admin_auth),
) -> Dict[str, Any]:
"""
Test connection to an IMAP server and gather mailbox statistics
Security: Requires authentication (X-API-Key or Bearer token)
Credentials should be passed in request body
"""
imap_client = IMAPClient(
server=request.server,
port=request.port,
username=request.username,
password=request.password,
)
success, message, stats = imap_client.test_connection()
return {
"success": success,
"message": message,
"message_count": stats.get("message_count", 0),
"unread_count": stats.get("unread_count", 0),
"dmarc_count": stats.get("dmarc_count", 0),
"available_mailboxes": stats.get("available_mailboxes", []),
"timestamp": datetime.now().isoformat(),
}
@router.post("/fetch-reports")
async def fetch_imap_reports(
background_tasks: BackgroundTasks,
_auth: dict = Depends(require_admin_auth),
days: int = 7,
delete_emails: Optional[bool] = None,
) -> Dict[str, Any]:
"""
Fetch DMARC reports from the configured IMAP mailbox
Security: Requires authentication (X-API-Key or Bearer token)
"""
# Security: Validate parameters
if days < 1 or days > 365:
raise HTTPException(status_code=400, detail="Days parameter must be between 1 and 365")
# Run in background if it might take a while
if days > 14:
background_tasks.add_task(_fetch_imap_reports_background, days, delete_emails)
return {
"success": True,
"message": f"Background task started to fetch {days} days of reports",
"timestamp": datetime.now().isoformat(),
}
# Otherwise run immediately
try:
results = await run_in_threadpool(_fetch_imap_reports_sync, days, delete_emails)
return {
"success": results["success"],
"processed_emails": results["processed"],
"reports_found": results["reports_found"],
"new_domains": results["new_domains"],
"errors": results["errors"] if "errors" in results and results["errors"] else None,
"timestamp": datetime.now().isoformat(),
}
except Exception as e:
logger.error("Error fetching IMAP reports: %s", str(e))
raise HTTPException(
status_code=500, detail="Failed to fetch reports. Check server logs for details."
) from e
@router.get("/status")
async def get_imap_status(_auth: dict = Depends(require_admin_auth)) -> Dict[str, Any]:
"""
Get the current status of IMAP polling background processes
Security: Requires authentication (X-API-Key or Bearer token)
"""
# In a real implementation this would check a persistent store
# or a global variable tracking the status of background tasks
# For now, returning simplified status
return {
"is_running": True, # In a real app, check if the background task is running
"last_check": None, # In production, track actual last check time
"next_check": None, # In production, calculate based on polling interval
"messages_processed": 0, # In production, track actual messages processed
"reports_found": 0, # In production, track reports found
"timestamp": datetime.now().isoformat(),
}