207 lines
6.9 KiB
Python
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}")
|