Bounce management
This commit is contained in:
@@ -1,8 +1,14 @@
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from pydantic import BaseModel, EmailStr
|
||||
from ...services.email_service import email_service
|
||||
from ...services.bounce_service import bounce_service
|
||||
from ...api.dependencies import get_admin_user
|
||||
from ...models.models import User
|
||||
from typing import Dict, Any, List
|
||||
from ...core.database import get_db
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
@@ -21,7 +27,8 @@ class WelcomeEmailRequest(BaseModel):
|
||||
@router.post("/test-email")
|
||||
async def send_test_email(
|
||||
request: TestEmailRequest,
|
||||
current_user: User = Depends(get_admin_user)
|
||||
current_user: User = Depends(get_admin_user),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Send a test email (admin only)"""
|
||||
html_body = f"<html><body><p>{request.message}</p></body></html>"
|
||||
@@ -29,7 +36,8 @@ async def send_test_email(
|
||||
to_email=request.to_email,
|
||||
subject=request.subject,
|
||||
html_body=html_body,
|
||||
text_body=request.message
|
||||
text_body=request.message,
|
||||
db=db
|
||||
)
|
||||
return {"success": True, "result": result}
|
||||
|
||||
@@ -45,3 +53,118 @@ async def send_test_welcome_email(
|
||||
first_name=request.first_name
|
||||
)
|
||||
return {"success": True, "result": result}
|
||||
|
||||
|
||||
@router.post("/webhooks/smtp2go/bounce")
|
||||
async def smtp2go_bounce_webhook(
|
||||
webhook_data: Dict[str, Any],
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""
|
||||
Webhook endpoint for SMTP2GO bounce notifications
|
||||
This endpoint should be configured in SMTP2GO webhook settings
|
||||
"""
|
||||
try:
|
||||
bounces_created = bounce_service.process_smtp2go_webhook(webhook_data, db)
|
||||
return {
|
||||
"success": True,
|
||||
"bounces_processed": len(bounces_created),
|
||||
"message": f"Successfully processed {len(bounces_created)} bounce events"
|
||||
}
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"Failed to process bounce webhook: {str(e)}")
|
||||
|
||||
|
||||
@router.get("/bounces")
|
||||
async def get_bounce_list(
|
||||
skip: int = 0,
|
||||
limit: int = 100,
|
||||
current_user: User = Depends(get_admin_user),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Get list of email bounces (admin only)"""
|
||||
from ...models.models import EmailBounce
|
||||
|
||||
bounces = db.query(EmailBounce).offset(skip).limit(limit).all()
|
||||
total = db.query(EmailBounce).count()
|
||||
|
||||
return {
|
||||
"bounces": [
|
||||
{
|
||||
"id": bounce.id,
|
||||
"email": bounce.email,
|
||||
"bounce_type": bounce.bounce_type.value,
|
||||
"bounce_reason": bounce.bounce_reason,
|
||||
"bounce_date": bounce.bounce_date.isoformat(),
|
||||
"is_active": bounce.is_active,
|
||||
"smtp2go_message_id": bounce.smtp2go_message_id
|
||||
}
|
||||
for bounce in bounces
|
||||
],
|
||||
"total": total,
|
||||
"skip": skip,
|
||||
"limit": limit
|
||||
}
|
||||
|
||||
|
||||
@router.get("/bounces/stats")
|
||||
async def get_bounce_statistics(
|
||||
current_user: User = Depends(get_admin_user),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Get bounce statistics (admin only)"""
|
||||
stats = bounce_service.get_bounce_statistics(db)
|
||||
return stats
|
||||
|
||||
|
||||
@router.get("/bounces/{email}")
|
||||
async def get_bounce_history(
|
||||
email: str,
|
||||
current_user: User = Depends(get_admin_user)
|
||||
):
|
||||
"""Get bounce history for a specific email (admin only)"""
|
||||
bounces = bounce_service.get_bounce_history(email)
|
||||
|
||||
return {
|
||||
"email": email,
|
||||
"bounces": [
|
||||
{
|
||||
"id": bounce.id,
|
||||
"bounce_type": bounce.bounce_type.value,
|
||||
"bounce_reason": bounce.bounce_reason,
|
||||
"bounce_date": bounce.bounce_date.isoformat(),
|
||||
"is_active": bounce.is_active,
|
||||
"smtp2go_message_id": bounce.smtp2go_message_id
|
||||
}
|
||||
for bounce in bounces
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@router.post("/bounces/cleanup")
|
||||
async def cleanup_old_bounces(
|
||||
days_old: int = 365,
|
||||
current_user: User = Depends(get_admin_user),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Clean up old soft bounces (admin only)"""
|
||||
cleaned_count = bounce_service.cleanup_old_bounces(days_old, db)
|
||||
return {
|
||||
"success": True,
|
||||
"message": f"Cleaned up {cleaned_count} old soft bounce records"
|
||||
}
|
||||
|
||||
|
||||
@router.delete("/bounces/deactivate/{bounce_id}")
|
||||
async def deactivate_bounce(
|
||||
bounce_id: int,
|
||||
current_user: User = Depends(get_admin_user),
|
||||
db: Session = Depends(get_db)
|
||||
):
|
||||
"""Deactivate a bounce record (mark as resolved) (admin only)"""
|
||||
success = bounce_service.deactivate_bounce(bounce_id, db)
|
||||
|
||||
if not success:
|
||||
raise HTTPException(status_code=404, detail="Bounce record not found")
|
||||
|
||||
return {"success": True, "message": "Bounce record deactivated"}
|
||||
|
||||
Reference in New Issue
Block a user