[split-required] Split 500-850 LOC files (batch 2)

backend-lehrer (10 files):
- game/database.py (785 → 5), correction_api.py (683 → 4)
- classroom_engine/antizipation.py (676 → 5)
- llm_gateway schools/edu_search already done in prior batch

klausur-service (12 files):
- orientation_crop_api.py (694 → 5), pdf_export.py (677 → 4)
- zeugnis_crawler.py (676 → 5), grid_editor_api.py (671 → 5)
- eh_templates.py (658 → 5), mail/api.py (651 → 5)
- qdrant_service.py (638 → 5), training_api.py (625 → 4)

website (6 pages):
- middleware (696 → 8), mail (733 → 6), consent (628 → 8)
- compliance/risks (622 → 5), export (502 → 5), brandbook (629 → 7)

studio-v2 (3 components):
- B2BMigrationWizard (848 → 3), CleanupPanel (765 → 2)
- dashboard-experimental (739 → 2)

admin-lehrer (4 files):
- uebersetzungen (769 → 4), manager (670 → 2)
- ChunkBrowserQA (675 → 6), dsfa/page (674 → 5)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-04-25 08:24:01 +02:00
parent 34da9f4cda
commit b4613e26f3
118 changed files with 15258 additions and 14680 deletions

View File

@@ -0,0 +1,123 @@
"""
Mail API — unified inbox, send, and email detail endpoints.
"""
import logging
from typing import Optional, List
from fastapi import APIRouter, HTTPException, Query
from .models import (
EmailComposeRequest,
EmailSendResult,
)
from .mail_db import (
get_unified_inbox,
get_email,
mark_email_read,
mark_email_starred,
log_mail_audit,
)
from .aggregator import get_mail_aggregator
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/v1/mail", tags=["Mail"])
@router.get("/inbox", response_model=List[dict])
async def get_inbox(
user_id: str = Query(..., description="User ID"),
account_ids: Optional[str] = Query(None, description="Comma-separated account IDs"),
categories: Optional[str] = Query(None, description="Comma-separated categories"),
is_read: Optional[bool] = Query(None),
is_starred: Optional[bool] = Query(None),
limit: int = Query(50, ge=1, le=200),
offset: int = Query(0, ge=0),
):
"""Get unified inbox with all accounts aggregated."""
# Parse comma-separated values
account_id_list = account_ids.split(",") if account_ids else None
category_list = categories.split(",") if categories else None
emails = await get_unified_inbox(
user_id=user_id,
account_ids=account_id_list,
categories=category_list,
is_read=is_read,
is_starred=is_starred,
limit=limit,
offset=offset,
)
return emails
@router.get("/inbox/{email_id}", response_model=dict)
async def get_email_detail(
email_id: str,
user_id: str = Query(..., description="User ID"),
):
"""Get a single email with full details."""
email_data = await get_email(email_id, user_id)
if not email_data:
raise HTTPException(status_code=404, detail="Email not found")
# Mark as read
await mark_email_read(email_id, user_id, is_read=True)
return email_data
@router.post("/inbox/{email_id}/read")
async def mark_read(
email_id: str,
user_id: str = Query(..., description="User ID"),
is_read: bool = Query(True),
):
"""Mark email as read/unread."""
success = await mark_email_read(email_id, user_id, is_read)
if not success:
raise HTTPException(status_code=500, detail="Failed to update email")
return {"status": "updated", "is_read": is_read}
@router.post("/inbox/{email_id}/star")
async def mark_starred(
email_id: str,
user_id: str = Query(..., description="User ID"),
is_starred: bool = Query(True),
):
"""Mark email as starred/unstarred."""
success = await mark_email_starred(email_id, user_id, is_starred)
if not success:
raise HTTPException(status_code=500, detail="Failed to update email")
return {"status": "updated", "is_starred": is_starred}
@router.post("/send", response_model=EmailSendResult)
async def send_email(
request: EmailComposeRequest,
user_id: str = Query(..., description="User ID"),
):
"""Send an email."""
aggregator = get_mail_aggregator()
result = await aggregator.send_email(
account_id=request.account_id,
user_id=user_id,
request=request,
)
if result.success:
await log_mail_audit(
user_id=user_id,
action="email_sent",
entity_type="email",
details={
"account_id": request.account_id,
"to": request.to,
"subject": request.subject,
},
)
return result