imap-mail-filter-service/app/routers/accounts.py

207 lines
6.9 KiB
Python

from fastapi import APIRouter, Depends, HTTPException
from sqlalchemy.orm import Session
from app.database import get_db
from app.models.db_models import Account, ProcessedMail
from app.schemas.schemas import (
AccountCreate,
AccountListResponse,
AccountResponse,
AccountUpdate,
)
from pydantic import BaseModel
from app.services.encryption import decrypt, encrypt
from app.services.imap_client import IMAPClient, async_test_connection
router = APIRouter(prefix="/api/accounts", tags=["accounts"])
@router.get("/", response_model=list[AccountListResponse])
def list_accounts(db: Session = Depends(get_db)):
accounts = db.query(Account).order_by(Account.name).all()
result = []
for acc in accounts:
data = AccountListResponse.model_validate(acc)
data.filter_rule_count = len(acc.filter_rules)
result.append(data)
return result
@router.get("/{account_id}", response_model=AccountResponse)
def get_account(account_id: int, db: Session = Depends(get_db)):
account = db.get(Account, account_id)
if not account:
raise HTTPException(404, "Konto nicht gefunden")
return account
@router.post("/", response_model=AccountResponse, status_code=201)
def create_account(data: AccountCreate, db: Session = Depends(get_db)):
account_data = data.model_dump()
account_data["password"] = encrypt(account_data["password"])
if account_data.get("smtp_password"):
account_data["smtp_password"] = encrypt(account_data["smtp_password"])
account = Account(**account_data)
db.add(account)
db.commit()
db.refresh(account)
return account
@router.put("/{account_id}", response_model=AccountResponse)
def update_account(account_id: int, data: AccountUpdate, db: Session = Depends(get_db)):
account = db.get(Account, account_id)
if not account:
raise HTTPException(404, "Konto nicht gefunden")
for key, value in data.model_dump(exclude_unset=True).items():
if key == "password" and value:
value = encrypt(value)
elif key == "smtp_password" and value:
value = encrypt(value)
setattr(account, key, value)
db.commit()
db.refresh(account)
return account
@router.delete("/{account_id}", status_code=204)
def delete_account(account_id: int, db: Session = Depends(get_db)):
account = db.get(Account, account_id)
if not account:
raise HTTPException(404, "Konto nicht gefunden")
db.delete(account)
db.commit()
@router.post("/{account_id}/test")
async def test_account_connection(account_id: int, db: Session = Depends(get_db)):
account = db.get(Account, account_id)
if not account:
raise HTTPException(404, "Konto nicht gefunden")
success = await async_test_connection(
host=account.imap_host,
port=account.imap_port,
username=account.username,
password=decrypt(account.password),
use_ssl=account.use_ssl,
)
return {"success": success, "message": "Verbindung erfolgreich" if success else "Verbindung fehlgeschlagen"}
class TestConnectionRequest(BaseModel):
imap_host: str
imap_port: int = 993
use_ssl: bool = True
username: str
password: str
@router.post("/test-connection")
async def test_connection_direct(data: TestConnectionRequest):
success = await async_test_connection(
host=data.imap_host,
port=data.imap_port,
username=data.username,
password=data.password,
use_ssl=data.use_ssl,
)
return {"success": success, "message": "Verbindung erfolgreich" if success else "Verbindung fehlgeschlagen"}
@router.post("/{account_id}/poll-now")
async def poll_now(account_id: int, db: Session = Depends(get_db)):
account = db.get(Account, account_id)
if not account:
raise HTTPException(404, "Konto nicht gefunden")
from app.services.scheduler import poll_account
await poll_account(account_id)
return {"message": f"Polling für '{account.name}' durchgeführt"}
@router.get("/{account_id}/processed")
def get_processed_stats(account_id: int, db: Session = Depends(get_db)):
account = db.get(Account, account_id)
if not account:
raise HTTPException(404, "Konto nicht gefunden")
total = db.query(ProcessedMail).filter(ProcessedMail.account_id == account_id).count()
return {"account_id": account_id, "processed_count": total}
@router.delete("/{account_id}/processed")
def reset_processed(account_id: int, folder: str | None = None, db: Session = Depends(get_db)):
account = db.get(Account, account_id)
if not account:
raise HTTPException(404, "Konto nicht gefunden")
query = db.query(ProcessedMail).filter(ProcessedMail.account_id == account_id)
if folder:
query = query.filter(ProcessedMail.folder == folder)
count = query.delete()
db.commit()
scope = f"Ordner '{folder}'" if folder else "alle Ordner"
return {"message": f"Verarbeitung zurückgesetzt für {scope} ({count} Einträge)", "deleted": count}
def _get_imap_client(account: Account) -> IMAPClient:
return IMAPClient(
host=account.imap_host,
port=account.imap_port,
username=account.username,
password=decrypt(account.password),
use_ssl=account.use_ssl,
)
@router.get("/{account_id}/folders")
async def list_folders(account_id: int, debug: bool = False, db: Session = Depends(get_db)):
account = db.get(Account, account_id)
if not account:
raise HTTPException(404, "Konto nicht gefunden")
import asyncio
def _list():
client = _get_imap_client(account)
with client:
folders = client.list_folders()
raw = None
if debug:
status, data = client.conn.list()
raw = [item.decode("utf-8", errors="replace") if isinstance(item, bytes) else str(item) for item in (data or [])]
return folders, raw
try:
folders, raw = await asyncio.to_thread(_list)
result = {"folders": folders}
if debug and raw is not None:
result["raw"] = raw
return result
except Exception as e:
raise HTTPException(500, f"Fehler beim Abrufen der Ordner: {e}")
class CreateFolderRequest(BaseModel):
folder_name: str
@router.post("/{account_id}/folders")
async def create_folder(account_id: int, data: CreateFolderRequest, db: Session = Depends(get_db)):
account = db.get(Account, account_id)
if not account:
raise HTTPException(404, "Konto nicht gefunden")
import asyncio
def _create():
client = _get_imap_client(account)
with client:
return client.create_folder(data.folder_name)
try:
success = await asyncio.to_thread(_create)
if success:
return {"success": True, "message": f"Ordner '{data.folder_name}' erstellt"}
raise HTTPException(400, f"Ordner '{data.folder_name}' konnte nicht erstellt werden")
except HTTPException:
raise
except Exception as e:
raise HTTPException(500, f"Fehler: {e}")