Files
certain/backend/app/routers/mail.py
T
GuidoandClaude Opus 5 7b087247a5 feat: Absender-Zuordnungen bearbeiten und loeschen
Der Loesch-Endpunkt war da, aber ohne Knopf in der Oberflaeche, und
Bearbeiten fehlte ganz – eine falsch eingetippte Zuordnung liess sich also
nicht mehr korrigieren.

Jede Zuordnung ist jetzt eine eigene Zeile mit Erkennungstext und Konto;
der Speichern-Knopf erscheint erst bei einer Aenderung. Dazu ein PUT auf
/mail/senders/{id} im Backend.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 20:30:24 +02:00

145 lines
5.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Postfächer einrichten und Zahlungs-Mails abholen."""
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from ..database import get_db
from ..deps import get_current_user, get_owned
from ..models import Account, MailSender, MailSource, User
from ..schemas import MailSenderIn, MailSenderOut, MailSourceIn, MailSourceOut
from ..services import geheimnis, mail_import
router = APIRouter(prefix="/mail", tags=["mail"])
def _aus(quelle: MailSource) -> MailSourceOut:
out = MailSourceOut.model_validate(quelle)
out.password_set = bool(quelle.password_enc)
return out
@router.get("/sources", response_model=list[MailSourceOut])
def list_sources(db: Session = Depends(get_db), user: User = Depends(get_current_user)):
quellen = (
db.query(MailSource).filter(MailSource.owner_id == user.id).order_by(MailSource.id).all()
)
return [_aus(q) for q in quellen]
@router.post("/sources", response_model=MailSourceOut, status_code=status.HTTP_201_CREATED)
def create_source(
payload: MailSourceIn, db: Session = Depends(get_db), user: User = Depends(get_current_user)
):
if not payload.password:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Passwort fehlt")
quelle = MailSource(
owner_id=user.id,
**payload.model_dump(exclude={"password"}),
password_enc=geheimnis.verschluesseln(payload.password),
)
db.add(quelle)
db.commit()
db.refresh(quelle)
return _aus(quelle)
@router.put("/sources/{source_id}", response_model=MailSourceOut)
def update_source(
source_id: int,
payload: MailSourceIn,
db: Session = Depends(get_db),
user: User = Depends(get_current_user),
):
quelle = get_owned(db, MailSource, source_id, user)
for schluessel, wert in payload.model_dump(exclude={"password"}).items():
setattr(quelle, schluessel, wert)
# Leeres Passwortfeld heisst „unverändert" – sonst müsste man es bei jeder
# Kleinigkeit erneut eintippen.
if payload.password:
quelle.password_enc = geheimnis.verschluesseln(payload.password)
db.commit()
db.refresh(quelle)
return _aus(quelle)
@router.delete("/sources/{source_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_source(source_id: int, db: Session = Depends(get_db), user: User = Depends(get_current_user)):
db.delete(get_owned(db, MailSource, source_id, user))
db.commit()
@router.post("/sources/{source_id}/test")
def test_source(source_id: int, db: Session = Depends(get_db), user: User = Depends(get_current_user)):
"""Verbindung prüfen und die Ordner des Postfachs zurückgeben."""
quelle = get_owned(db, MailSource, source_id, user)
try:
return mail_import.teste(quelle)
except mail_import.MailFehler as e:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)) from e
@router.post("/sources/{source_id}/fetch")
def fetch_source(source_id: int, db: Session = Depends(get_db), user: User = Depends(get_current_user)):
quelle = get_owned(db, MailSource, source_id, user)
try:
return mail_import.hole_neue(db, quelle)
except mail_import.MailFehler as e:
quelle.last_error = str(e)[:500]
db.commit()
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=str(e)) from e
# --- Absender-Zuordnung ------------------------------------------------------
@router.get("/sources/{source_id}/senders", response_model=list[MailSenderOut])
def list_senders(source_id: int, db: Session = Depends(get_db), user: User = Depends(get_current_user)):
get_owned(db, MailSource, source_id, user)
return (
db.query(MailSender)
.filter(MailSender.source_id == source_id)
.order_by(MailSender.id)
.all()
)
@router.post("/sources/{source_id}/senders", response_model=MailSenderOut,
status_code=status.HTTP_201_CREATED)
def create_sender(
source_id: int,
payload: MailSenderIn,
db: Session = Depends(get_db),
user: User = Depends(get_current_user),
):
get_owned(db, MailSource, source_id, user)
if payload.account_id is not None:
get_owned(db, Account, payload.account_id, user)
absender = MailSender(source_id=source_id, owner_id=user.id, **payload.model_dump())
db.add(absender)
db.commit()
db.refresh(absender)
return absender
@router.put("/senders/{sender_id}", response_model=MailSenderOut)
def update_sender(
sender_id: int,
payload: MailSenderIn,
db: Session = Depends(get_db),
user: User = Depends(get_current_user),
):
absender = get_owned(db, MailSender, sender_id, user)
if payload.account_id is not None:
get_owned(db, Account, payload.account_id, user)
for schluessel, wert in payload.model_dump().items():
setattr(absender, schluessel, wert)
db.commit()
db.refresh(absender)
return absender
@router.delete("/senders/{sender_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_sender(sender_id: int, db: Session = Depends(get_db), user: User = Depends(get_current_user)):
db.delete(get_owned(db, MailSender, sender_id, user))
db.commit()