refactor(email_sender): change email_sender type from list to str
This commit is contained in:
parent
b6d5ae97ee
commit
370cd61fd2
@ -6,49 +6,32 @@ class EmailSenderHub:
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.email_sender_manager = EmailSenderManager()
|
self.email_sender_manager = EmailSenderManager()
|
||||||
|
|
||||||
async def get_email_senders(self, tenant_id: str):
|
async def get_email_sender(self, tenant_id: str):
|
||||||
"""get email senders for tenant"""
|
"""get email senders for tenant"""
|
||||||
if not tenant_id:
|
if not tenant_id:
|
||||||
raise ValueError("tenant_id is required")
|
raise ValueError("tenant_id is required")
|
||||||
|
|
||||||
return await self.email_sender_manager.get_email_senders(tenant_id)
|
return await self.email_sender_manager.get_email_sender(tenant_id)
|
||||||
|
|
||||||
async def set_email_senders(self, tenant_id: str, email_senders: List[str]):
|
async def set_email_sender(self, tenant_id: str, email_sender: str):
|
||||||
"""set email senders for tenant"""
|
"""set email sender for tenant"""
|
||||||
if not tenant_id:
|
if not tenant_id:
|
||||||
raise ValueError("tenant_id is required")
|
raise ValueError("tenant_id is required")
|
||||||
|
|
||||||
if not email_senders or not isinstance(email_senders, list):
|
if not email_sender:
|
||||||
raise ValueError("email_senders must be a non-empty list")
|
raise ValueError("email_sender must be provided")
|
||||||
|
|
||||||
return await self.email_sender_manager.set_email_senders(tenant_id, email_senders)
|
return await self.email_sender_manager.set_email_sender(tenant_id, email_sender)
|
||||||
|
|
||||||
async def add_email_senders(self, tenant_id: str, new_senders: List[str]):
|
async def update_email_sender(self, tenant_id: str, email_sender: str):
|
||||||
"""add email senders to tenant"""
|
"""update email sender for tenant"""
|
||||||
if not tenant_id:
|
if not tenant_id:
|
||||||
raise ValueError("tenant_id is required")
|
raise ValueError("tenant_id is required")
|
||||||
|
|
||||||
if not new_senders or not isinstance(new_senders, list):
|
if not email_sender:
|
||||||
raise ValueError("new_senders must be a non-empty list")
|
raise ValueError("email_sender must be provided")
|
||||||
|
|
||||||
return await self.email_sender_manager.add_email_senders(tenant_id, new_senders)
|
return await self.email_sender_manager.update_email_sender(tenant_id, email_sender)
|
||||||
|
|
||||||
async def remove_email_senders(self, tenant_id: str, emails_to_remove: List[str]):
|
|
||||||
"""remove email senders from tenant"""
|
|
||||||
if not tenant_id:
|
|
||||||
raise ValueError("tenant_id is required")
|
|
||||||
|
|
||||||
if not emails_to_remove or not isinstance(emails_to_remove, list):
|
|
||||||
raise ValueError("emails_to_remove must be a non-empty list")
|
|
||||||
|
|
||||||
return await self.email_sender_manager.remove_email_senders(tenant_id, emails_to_remove)
|
|
||||||
|
|
||||||
async def clear_email_senders(self, tenant_id: str):
|
|
||||||
"""clear email senders for tenant"""
|
|
||||||
if not tenant_id:
|
|
||||||
raise ValueError("tenant_id is required")
|
|
||||||
|
|
||||||
return await self.email_sender_manager.clear_email_senders(tenant_id)
|
|
||||||
|
|
||||||
async def delete_email_sender(self, tenant_id: str):
|
async def delete_email_sender(self, tenant_id: str):
|
||||||
"""delete email sender for tenant"""
|
"""delete email sender for tenant"""
|
||||||
|
|||||||
@ -24,11 +24,11 @@ class TenantNotificationHub:
|
|||||||
region: int,
|
region: int,
|
||||||
subject_properties: Dict = {},
|
subject_properties: Dict = {},
|
||||||
body_properties: Dict = {},
|
body_properties: Dict = {},
|
||||||
sender_emails: Optional[List[str]] = None,
|
sender_email: str = None,
|
||||||
priority: str = "normal",
|
priority: str = "normal",
|
||||||
tracking_enabled: bool = True
|
tracking_enabled: bool = True
|
||||||
):
|
):
|
||||||
"""Send email using tenant's template and email senders"""
|
"""Send email using tenant's template and email sender"""
|
||||||
try:
|
try:
|
||||||
# 1. check if tenant has access to template
|
# 1. check if tenant has access to template
|
||||||
await self.template_message_hub.verify_tenant_access(template_id, tenant_id, region)
|
await self.template_message_hub.verify_tenant_access(template_id, tenant_id, region)
|
||||||
@ -41,12 +41,12 @@ class TenantNotificationHub:
|
|||||||
region=region
|
region=region
|
||||||
)
|
)
|
||||||
|
|
||||||
# 3. get tenant email senders
|
# 3. get tenant email sender
|
||||||
default_sender_email = self.notification_constants.DEFAULT_EMAIL_SENDER
|
default_sender_email = self.notification_constants.DEFAULT_EMAIL_SENDER
|
||||||
if sender_emails is None:
|
if sender_email is None:
|
||||||
tenant_email_senders = await self.email_sender_hub.get_email_senders(tenant_id)
|
tenant_email_sender = await self.email_sender_hub.get_email_sender(tenant_id)
|
||||||
if not tenant_email_senders:
|
if not tenant_email_sender:
|
||||||
sender_emails = [default_sender_email]
|
sender_email = default_sender_email
|
||||||
await self.module_logger.log_info(
|
await self.module_logger.log_info(
|
||||||
"Using default email sender for tenant",
|
"Using default email sender for tenant",
|
||||||
properties={
|
properties={
|
||||||
@ -55,22 +55,15 @@ class TenantNotificationHub:
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
sender_emails = tenant_email_senders
|
sender_email = tenant_email_sender
|
||||||
|
|
||||||
# 4. check if sender_emails are valid
|
# 4. call TenantNotificationManager to send email
|
||||||
if sender_emails != [default_sender_email]:
|
|
||||||
tenant_senders = await self.email_sender_hub.get_email_senders(tenant_id)
|
|
||||||
invalid_senders = [email for email in sender_emails if email not in tenant_senders]
|
|
||||||
if invalid_senders:
|
|
||||||
raise InvalidDataError(f"Invalid email senders for tenant: {invalid_senders}")
|
|
||||||
|
|
||||||
# 5. call TenantNotificationManager to send email
|
|
||||||
result = await self.tenant_notification_manager.send_tenant_email(
|
result = await self.tenant_notification_manager.send_tenant_email(
|
||||||
tenant_id=tenant_id,
|
tenant_id=tenant_id,
|
||||||
template_id=template_id,
|
template_id=template_id,
|
||||||
rendered_template=rendered_template,
|
rendered_template=rendered_template,
|
||||||
recipient_emails=recipient_emails,
|
recipient_emails=recipient_emails,
|
||||||
sender_emails=sender_emails,
|
sender_email=sender_email,
|
||||||
region=region,
|
region=region,
|
||||||
priority=priority,
|
priority=priority,
|
||||||
tracking_enabled=tracking_enabled
|
tracking_enabled=tracking_enabled
|
||||||
@ -82,7 +75,7 @@ class TenantNotificationHub:
|
|||||||
"tenant_id": tenant_id,
|
"tenant_id": tenant_id,
|
||||||
"template_id": template_id,
|
"template_id": template_id,
|
||||||
"recipient_count": len(recipient_emails),
|
"recipient_count": len(recipient_emails),
|
||||||
"sender_count": len(sender_emails),
|
"sender_email": sender_email,
|
||||||
"message_id": result.get("message_id")
|
"message_id": result.get("message_id")
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@ -10,90 +10,49 @@ class EmailSenderManager:
|
|||||||
self.email_sender_service = EmailSenderService()
|
self.email_sender_service = EmailSenderService()
|
||||||
self.module_logger = ModuleLogger(sender_id="EmailSenderManager")
|
self.module_logger = ModuleLogger(sender_id="EmailSenderManager")
|
||||||
|
|
||||||
async def get_email_senders(self, tenant_id: str):
|
async def get_email_sender(self, tenant_id: str):
|
||||||
"""get email senders for tenant"""
|
"""get email sendersfor tenant"""
|
||||||
email_senders = await self.email_sender_service.get_email_senders(tenant_id)
|
email_sender = await self.email_sender_service.get_email_sender(tenant_id)
|
||||||
|
|
||||||
await self.module_logger.log_info(
|
await self.module_logger.log_info(
|
||||||
info="Email senders retrieved",
|
info="Email sender retrieved",
|
||||||
properties={
|
properties={
|
||||||
"tenant_id": tenant_id,
|
"tenant_id": tenant_id,
|
||||||
"sender_count": len(email_senders)
|
"email_sender": email_sender
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
return email_senders
|
return email_sender
|
||||||
|
|
||||||
async def set_email_senders(self, tenant_id: str, email_senders: List[str]):
|
async def set_email_sender(self, tenant_id: str, email_sender: str):
|
||||||
"""set email senders for tenant"""
|
"""set email sender for tenant"""
|
||||||
if not email_senders:
|
if not email_sender:
|
||||||
raise ValueError("Email senders list cannot be empty")
|
raise ValueError("Email sender must be provided")
|
||||||
|
|
||||||
for email in email_senders:
|
result = await self.email_sender_service.set_email_sender(tenant_id, email_sender)
|
||||||
if not self._is_valid_email(email):
|
|
||||||
raise ValueError(f"Invalid email format: {email}")
|
|
||||||
|
|
||||||
result = await self.email_sender_service.set_email_senders(tenant_id, email_senders)
|
|
||||||
|
|
||||||
await self.module_logger.log_info(
|
await self.module_logger.log_info(
|
||||||
info="Email senders set",
|
info="Email senders set",
|
||||||
properties={
|
properties={
|
||||||
"tenant_id": tenant_id,
|
"tenant_id": tenant_id,
|
||||||
"sender_count": len(email_senders)
|
"email_sender": email_sender
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
async def add_email_senders(self, tenant_id: str, new_senders: List[str]):
|
async def update_email_sender(self, tenant_id: str, email_sender: str):
|
||||||
"""add email senders to tenant"""
|
"""update email sender for tenant"""
|
||||||
if not new_senders:
|
if not email_sender:
|
||||||
raise ValueError("New senders list cannot be empty")
|
raise ValueError("Email sender must be provided")
|
||||||
|
|
||||||
for email in new_senders:
|
result = await self.email_sender_service.update_email_sender(tenant_id, email_sender)
|
||||||
if not self._is_valid_email(email):
|
|
||||||
raise ValueError(f"Invalid email format: {email}")
|
|
||||||
|
|
||||||
result = await self.email_sender_service.add_email_senders(tenant_id, new_senders)
|
|
||||||
|
|
||||||
await self.module_logger.log_info(
|
await self.module_logger.log_info(
|
||||||
info="Email senders added",
|
info="Email senders set",
|
||||||
properties={
|
properties={
|
||||||
"tenant_id": tenant_id,
|
"tenant_id": tenant_id,
|
||||||
"new_sender_count": len(new_senders),
|
"email_sender": email_sender
|
||||||
"success": result.get("success", False)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
async def remove_email_senders(self, tenant_id: str, emails_to_remove: List[str]):
|
|
||||||
"""remove email senders from tenant"""
|
|
||||||
if not emails_to_remove:
|
|
||||||
raise ValueError("Emails to remove list cannot be empty")
|
|
||||||
|
|
||||||
result = await self.email_sender_service.remove_email_senders(tenant_id, emails_to_remove)
|
|
||||||
|
|
||||||
await self.module_logger.log_info(
|
|
||||||
info="Email senders removed",
|
|
||||||
properties={
|
|
||||||
"tenant_id": tenant_id,
|
|
||||||
"removed_count": len(emails_to_remove),
|
|
||||||
"success": result.get("success", False)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
async def clear_email_senders(self, tenant_id: str):
|
|
||||||
"""clear email senders for tenant"""
|
|
||||||
result = await self.email_sender_service.clear_email_senders(tenant_id)
|
|
||||||
|
|
||||||
await self.module_logger.log_info(
|
|
||||||
info="Email senders cleared",
|
|
||||||
properties={
|
|
||||||
"tenant_id": tenant_id,
|
|
||||||
"success": result.get("success", False)
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -113,12 +72,7 @@ class EmailSenderManager:
|
|||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def _is_valid_email(self, email: str) -> bool:
|
|
||||||
"""validate email format"""
|
|
||||||
# TODO: add more complex email format validation if needed
|
|
||||||
import re
|
|
||||||
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
|
|
||||||
return re.match(pattern, email) is not None
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -28,22 +28,22 @@ class TenantNotificationManager:
|
|||||||
template_id: str,
|
template_id: str,
|
||||||
rendered_template: Dict,
|
rendered_template: Dict,
|
||||||
recipient_emails: List[str],
|
recipient_emails: List[str],
|
||||||
sender_emails: List[str],
|
sender_email: str,
|
||||||
region: int,
|
region: int,
|
||||||
priority: str = "normal",
|
priority: str = "normal",
|
||||||
tracking_enabled: bool = True
|
tracking_enabled: bool = True
|
||||||
):
|
):
|
||||||
"""Send tenant email using existing EMAIL queue with validation and protection"""
|
"""Send tenant email using existing EMAIL with validation and protection"""
|
||||||
try:
|
try:
|
||||||
# 1. validate recipient emails
|
# 1. validate recipient emails
|
||||||
valid_recipients, invalid_recipients = await self.email_validation_service.validate_emails(recipient_emails)
|
valid_recipients, invalid_recipients = await self.email_validation_service.validate_emails(recipient_emails)
|
||||||
valid_senders, invalid_senders = await self.email_validation_service.validate_sender_emails(tenant_id, sender_emails)
|
valid_sender = await self.email_validation_service.validate_sender_email(tenant_id, sender_email)
|
||||||
|
|
||||||
if not valid_recipients:
|
if not valid_recipients:
|
||||||
raise InvalidDataError("No valid recipient emails found")
|
raise InvalidDataError("No valid recipient emails found")
|
||||||
|
|
||||||
if not valid_senders:
|
if valid_sender is None:
|
||||||
raise InvalidDataError("No valid sender emails found")
|
raise InvalidDataError("Invalid sender email")
|
||||||
|
|
||||||
# 2. check blacklisted recipients
|
# 2. check blacklisted recipients
|
||||||
blacklisted_recipients = []
|
blacklisted_recipients = []
|
||||||
@ -58,7 +58,7 @@ class TenantNotificationManager:
|
|||||||
raise InvalidDataError("All recipient emails are blacklisted")
|
raise InvalidDataError("All recipient emails are blacklisted")
|
||||||
|
|
||||||
# 3. check rate limit
|
# 3. check rate limit
|
||||||
rate_limit_result = await self.email_spam_protection_service.check_rate_limit(tenant_id, valid_senders[0])
|
rate_limit_result = await self.email_spam_protection_service.check_rate_limit(tenant_id, sender_email)
|
||||||
if not rate_limit_result["allowed"]:
|
if not rate_limit_result["allowed"]:
|
||||||
raise InvalidDataError("Rate limit exceeded")
|
raise InvalidDataError("Rate limit exceeded")
|
||||||
|
|
||||||
@ -76,7 +76,7 @@ class TenantNotificationManager:
|
|||||||
"tenant_id": tenant_id,
|
"tenant_id": tenant_id,
|
||||||
"template_id": template_id,
|
"template_id": template_id,
|
||||||
"destination_emails": valid_recipients,
|
"destination_emails": valid_recipients,
|
||||||
"sender_emails": valid_senders,
|
"sender_email": valid_sender,
|
||||||
"subject_properties": rendered_template.get("subject_properties", {}),
|
"subject_properties": rendered_template.get("subject_properties", {}),
|
||||||
"body_properties": rendered_template.get("body_properties", {}),
|
"body_properties": rendered_template.get("body_properties", {}),
|
||||||
"region": region,
|
"region": region,
|
||||||
@ -87,7 +87,7 @@ class TenantNotificationManager:
|
|||||||
"receiver_type": "email",
|
"receiver_type": "email",
|
||||||
"validation_info": {
|
"validation_info": {
|
||||||
"invalid_recipients": invalid_recipients,
|
"invalid_recipients": invalid_recipients,
|
||||||
"invalid_senders": invalid_senders,
|
"invalid_sender": not valid_sender,
|
||||||
"blacklisted_recipients": blacklisted_recipients,
|
"blacklisted_recipients": blacklisted_recipients,
|
||||||
"rate_limit_info": rate_limit_result,
|
"rate_limit_info": rate_limit_result,
|
||||||
"spam_detection_info": spam_result
|
"spam_detection_info": spam_result
|
||||||
@ -101,7 +101,7 @@ class TenantNotificationManager:
|
|||||||
"tenant_id": tenant_id,
|
"tenant_id": tenant_id,
|
||||||
"template_id": template_id,
|
"template_id": template_id,
|
||||||
"destination_emails": [recipient_email],
|
"destination_emails": [recipient_email],
|
||||||
"sender_emails": valid_senders,
|
"sender_email": valid_sender,
|
||||||
"subject_properties": rendered_template.get("subject_properties", {}),
|
"subject_properties": rendered_template.get("subject_properties", {}),
|
||||||
"body_properties": rendered_template.get("body_properties", {}),
|
"body_properties": rendered_template.get("body_properties", {}),
|
||||||
"region": region,
|
"region": region,
|
||||||
@ -112,7 +112,7 @@ class TenantNotificationManager:
|
|||||||
"receiver_type": "email",
|
"receiver_type": "email",
|
||||||
"validation_info": {
|
"validation_info": {
|
||||||
"invalid_recipients": invalid_recipients,
|
"invalid_recipients": invalid_recipients,
|
||||||
"invalid_senders": invalid_senders,
|
"invalid_sender": not valid_sender,
|
||||||
"blacklisted_recipients": blacklisted_recipients,
|
"blacklisted_recipients": blacklisted_recipients,
|
||||||
"rate_limit_info": rate_limit_result,
|
"rate_limit_info": rate_limit_result,
|
||||||
"spam_detection_info": spam_result
|
"spam_detection_info": spam_result
|
||||||
@ -138,8 +138,8 @@ class TenantNotificationManager:
|
|||||||
"valid_recipient_count": len(valid_recipients),
|
"valid_recipient_count": len(valid_recipients),
|
||||||
"invalid_recipient_count": len(invalid_recipients),
|
"invalid_recipient_count": len(invalid_recipients),
|
||||||
"blacklisted_recipient_count": len(blacklisted_recipients),
|
"blacklisted_recipient_count": len(blacklisted_recipients),
|
||||||
"valid_sender_count": len(valid_senders),
|
"sender_email": sender_email,
|
||||||
"invalid_sender_count": len(invalid_senders)
|
"sender_valid": valid_sender
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -154,9 +154,8 @@ class TenantNotificationManager:
|
|||||||
"valid_recipients": len(valid_recipients),
|
"valid_recipients": len(valid_recipients),
|
||||||
"invalid_recipients": len(invalid_recipients),
|
"invalid_recipients": len(invalid_recipients),
|
||||||
"blacklisted_recipients": len(blacklisted_recipients),
|
"blacklisted_recipients": len(blacklisted_recipients),
|
||||||
"total_senders": len(sender_emails),
|
"sender_email": sender_email,
|
||||||
"valid_senders": len(valid_senders),
|
"sender_valid": valid_sender
|
||||||
"invalid_senders": len(invalid_senders)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -49,17 +49,18 @@ class EmailHandler:
|
|||||||
tenant_id: str,
|
tenant_id: str,
|
||||||
template_id: str,
|
template_id: str,
|
||||||
recipient_email: str,
|
recipient_email: str,
|
||||||
sender_emails: List[str],
|
sender_email: str,
|
||||||
subject_properties: Dict = {},
|
subject_properties: Dict = {},
|
||||||
body_properties: Dict = {}
|
body_properties: Dict = {},
|
||||||
|
tracking_enabled: bool = True
|
||||||
):
|
):
|
||||||
"""Send tenant email using specified senders"""
|
"""Send tenant email using specified sender"""
|
||||||
module_logger = ModuleLogger(sender_id="EmailHandler")
|
module_logger = ModuleLogger(sender_id="EmailHandler")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
email_id = str(uuid.uuid4())
|
email_id = str(uuid.uuid4())
|
||||||
|
|
||||||
from_email = sender_emails[0] if sender_emails else app_settings.EMAIL_FROM
|
from_email = sender_email if sender_email else app_settings.EMAIL_FROM
|
||||||
|
|
||||||
subject = subject_properties.get("subject", "No Subject")
|
subject = subject_properties.get("subject", "No Subject")
|
||||||
html_content = body_properties.get("html_content", "")
|
html_content = body_properties.get("html_content", "")
|
||||||
@ -68,7 +69,7 @@ class EmailHandler:
|
|||||||
email_status_doc = EmailSendStatusDoc(
|
email_status_doc = EmailSendStatusDoc(
|
||||||
email_id=email_id,
|
email_id=email_id,
|
||||||
tenant_id=tenant_id,
|
tenant_id=tenant_id,
|
||||||
email_senders=sender_emails,
|
email_sender=sender_email,
|
||||||
recipient_email=recipient_email,
|
recipient_email=recipient_email,
|
||||||
template_id=template_id,
|
template_id=template_id,
|
||||||
subject=subject,
|
subject=subject,
|
||||||
@ -77,6 +78,20 @@ class EmailHandler:
|
|||||||
)
|
)
|
||||||
await email_status_doc.save()
|
await email_status_doc.save()
|
||||||
|
|
||||||
|
# Create EmailTrackingDoc if tracking is enabled
|
||||||
|
tracking_doc = None
|
||||||
|
if tracking_enabled:
|
||||||
|
from backend.models.models import EmailTrackingDoc
|
||||||
|
tracking_doc = EmailTrackingDoc(
|
||||||
|
email_id=email_id,
|
||||||
|
tenant_id=tenant_id,
|
||||||
|
recipient_email=recipient_email,
|
||||||
|
template_id=template_id,
|
||||||
|
sent_at=datetime.utcnow(),
|
||||||
|
tracking_enabled=True
|
||||||
|
)
|
||||||
|
await tracking_doc.save()
|
||||||
|
|
||||||
mail = Mail(
|
mail = Mail(
|
||||||
from_email=from_email,
|
from_email=from_email,
|
||||||
to_emails=recipient_email,
|
to_emails=recipient_email,
|
||||||
@ -84,6 +99,17 @@ class EmailHandler:
|
|||||||
html_content=html_content,
|
html_content=html_content,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Enable SendGrid tracking if tracking is enabled
|
||||||
|
if tracking_enabled:
|
||||||
|
from sendgrid.helpers.mail import TrackingSettings, ClickTracking, OpenTracking
|
||||||
|
tracking_settings = TrackingSettings()
|
||||||
|
click_tracking = ClickTracking(True, True) # Enable click tracking
|
||||||
|
open_tracking = OpenTracking(True) # Enable open tracking
|
||||||
|
|
||||||
|
tracking_settings.click_tracking = click_tracking
|
||||||
|
tracking_settings.open_tracking = open_tracking
|
||||||
|
mail.tracking_settings = tracking_settings
|
||||||
|
|
||||||
sg = SendGridAPIClient(app_settings.SENDGRID_API_KEY)
|
sg = SendGridAPIClient(app_settings.SENDGRID_API_KEY)
|
||||||
response = sg.send(mail)
|
response = sg.send(mail)
|
||||||
|
|
||||||
@ -92,6 +118,11 @@ class EmailHandler:
|
|||||||
email_status_doc.message_id = str(response.headers.get('X-Message-Id', ''))
|
email_status_doc.message_id = str(response.headers.get('X-Message-Id', ''))
|
||||||
await email_status_doc.save()
|
await email_status_doc.save()
|
||||||
|
|
||||||
|
# Update tracking document with message_id
|
||||||
|
if tracking_doc:
|
||||||
|
tracking_doc.message_id = email_status_doc.message_id
|
||||||
|
await tracking_doc.save()
|
||||||
|
|
||||||
await module_logger.log_info(
|
await module_logger.log_info(
|
||||||
f"Tenant email sent successfully",
|
f"Tenant email sent successfully",
|
||||||
properties={
|
properties={
|
||||||
|
|||||||
@ -7,46 +7,46 @@ class EmailSenderHandler:
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.module_logger = ModuleLogger(sender_id="EmailSenderHandler")
|
self.module_logger = ModuleLogger(sender_id="EmailSenderHandler")
|
||||||
|
|
||||||
async def get_email_senders(self, tenant_id: str) -> List[str]:
|
async def get_email_sender(self, tenant_id: str) -> str:
|
||||||
"""get email senders for tenant"""
|
"""get email sender for tenant"""
|
||||||
try:
|
try:
|
||||||
doc = await EmailSenderDoc.find_one({"tenant_id": tenant_id, "is_active": True})
|
doc = await EmailSenderDoc.find_one({"tenant_id": tenant_id, "is_active": True})
|
||||||
return doc.email_senders if doc else []
|
return doc.email_sender if doc else None
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
await self.module_logger.log_error(
|
await self.module_logger.log_error(
|
||||||
error="Failed to get email senders",
|
error="Failed to get email sender",
|
||||||
properties={"tenant_id": tenant_id, "error": str(e)}
|
properties={"tenant_id": tenant_id, "error": str(e)}
|
||||||
)
|
)
|
||||||
return []
|
return None
|
||||||
|
|
||||||
async def set_email_senders(self, tenant_id: str, email_senders: List[str]):
|
async def set_email_sender(self, tenant_id: str, email_sender: str):
|
||||||
"""set email senders for tenant"""
|
"""set email sender for tenant"""
|
||||||
try:
|
try:
|
||||||
doc = await EmailSenderDoc.find_one({"tenant_id": tenant_id})
|
doc = await EmailSenderDoc.find_one({"tenant_id": tenant_id})
|
||||||
if doc:
|
if doc:
|
||||||
await doc.set({"email_senders": email_senders})
|
await doc.set({"email_sender": email_sender})
|
||||||
await self.module_logger.log_info(
|
await self.module_logger.log_info(
|
||||||
info="Email senders set in database",
|
info="Email senders set in database",
|
||||||
properties={
|
properties={
|
||||||
"tenant_id": tenant_id,
|
"tenant_id": tenant_id,
|
||||||
"sender_count": len(email_senders)
|
"email_sender": email_sender
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return {"success": True, "email_senders": email_senders}
|
return {"success": True, "email_sender": email_sender}
|
||||||
else:
|
else:
|
||||||
doc = EmailSenderDoc(tenant_id=tenant_id, email_senders=email_senders)
|
doc = EmailSenderDoc(tenant_id=tenant_id, email_sender=email_sender)
|
||||||
await doc.create()
|
await doc.create()
|
||||||
await self.module_logger.log_info(
|
await self.module_logger.log_info(
|
||||||
info="Email sender doc created with senders",
|
info="Email sender doc created",
|
||||||
properties={
|
properties={
|
||||||
"tenant_id": tenant_id,
|
"tenant_id": tenant_id,
|
||||||
"sender_count": len(email_senders)
|
"email_sender": email_sender
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return {"success": True, "email_senders": doc.email_senders}
|
return {"success": True, "email_sender": doc.email_sender}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
await self.module_logger.log_error(
|
await self.module_logger.log_error(
|
||||||
error="Failed to set email senders",
|
error="Failed to set email sender",
|
||||||
properties={
|
properties={
|
||||||
"tenant_id": tenant_id,
|
"tenant_id": tenant_id,
|
||||||
"error": str(e)
|
"error": str(e)
|
||||||
@ -54,44 +54,25 @@ class EmailSenderHandler:
|
|||||||
)
|
)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
async def add_email_senders(self, tenant_id: str, new_senders: List[str]):
|
async def update_email_sender(self, tenant_id: str, email_sender: str):
|
||||||
"""add email senders to tenant"""
|
"""update email sender for tenant (only if exists)"""
|
||||||
try:
|
try:
|
||||||
if not new_senders or not isinstance(new_senders, list):
|
|
||||||
return {"success": False, "msg": "No sender provided"}
|
|
||||||
|
|
||||||
doc = await EmailSenderDoc.find_one({"tenant_id": tenant_id, "is_active": True})
|
doc = await EmailSenderDoc.find_one({"tenant_id": tenant_id, "is_active": True})
|
||||||
if doc:
|
if not doc:
|
||||||
original_set = set(doc.email_senders)
|
raise ValueError("Email sender configuration not found")
|
||||||
new_set = set(new_senders)
|
|
||||||
to_add = new_set - original_set
|
await doc.set({"email_sender": email_sender})
|
||||||
if not to_add:
|
|
||||||
return {"success": False, "msg": "All senders already exist"}
|
|
||||||
updated_list = list(original_set | new_set)
|
|
||||||
await doc.set({"email_senders": updated_list})
|
|
||||||
await self.module_logger.log_info(
|
await self.module_logger.log_info(
|
||||||
info="Email senders added to database",
|
info="Email sender updated in database",
|
||||||
properties={
|
properties={
|
||||||
"tenant_id": tenant_id,
|
"tenant_id": tenant_id,
|
||||||
"added_count": len(to_add),
|
"email_sender": email_sender
|
||||||
"total_count": len(updated_list)
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return {"success": True, "email_senders": updated_list}
|
return {"success": True, "email_sender": email_sender}
|
||||||
else:
|
|
||||||
doc = EmailSenderDoc(tenant_id=tenant_id, email_senders=new_senders)
|
|
||||||
await doc.create()
|
|
||||||
await self.module_logger.log_info(
|
|
||||||
info="Email sender doc created with new senders",
|
|
||||||
properties={
|
|
||||||
"tenant_id": tenant_id,
|
|
||||||
"sender_count": len(new_senders)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
return {"success": True, "email_senders": doc.email_senders}
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
await self.module_logger.log_error(
|
await self.module_logger.log_error(
|
||||||
error="Failed to add email senders",
|
error="Failed to update email sender",
|
||||||
properties={
|
properties={
|
||||||
"tenant_id": tenant_id,
|
"tenant_id": tenant_id,
|
||||||
"error": str(e)
|
"error": str(e)
|
||||||
@ -99,53 +80,27 @@ class EmailSenderHandler:
|
|||||||
)
|
)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
async def remove_email_senders(self, tenant_id: str, emails_to_remove: List[str]):
|
async def remove_email_sender(self, tenant_id: str):
|
||||||
"""remove email senders from tenant"""
|
"""remove email sender from tenant"""
|
||||||
try:
|
try:
|
||||||
doc = await EmailSenderDoc.find_one({"tenant_id": tenant_id, "is_active": True})
|
doc = await EmailSenderDoc.find_one({"tenant_id": tenant_id, "is_active": True})
|
||||||
if not doc or not doc.email_senders:
|
if not doc or not doc.email_sender:
|
||||||
return {"success": False, "msg": "No sender found"}
|
return {"success": False, "msg": "No sender found"}
|
||||||
|
|
||||||
original_count = len(doc.email_senders)
|
original_email_sender = doc.email_sender
|
||||||
doc.email_senders = [s for s in doc.email_senders if s not in emails_to_remove]
|
|
||||||
if len(doc.email_senders) == original_count:
|
|
||||||
return {"success": False, "msg": "No sender matched for removal"}
|
|
||||||
|
|
||||||
await doc.set({"email_senders": doc.email_senders})
|
await doc.set({"email_sender": None})
|
||||||
await self.module_logger.log_info(
|
await self.module_logger.log_info(
|
||||||
info="Email senders removed from database",
|
info="Email sender removed from database",
|
||||||
properties={
|
properties={
|
||||||
"tenant_id": tenant_id,
|
"tenant_id": tenant_id,
|
||||||
"removed_count": original_count - len(doc.email_senders),
|
"removed_email_sender": original_email_sender
|
||||||
"remaining_count": len(doc.email_senders)
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return {"success": True, "remaining": doc.email_senders}
|
return {"success": True, "removed_email_sender": original_email_sender}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
await self.module_logger.log_error(
|
await self.module_logger.log_error(
|
||||||
error="Failed to remove email senders",
|
error="Failed to remove email sender",
|
||||||
properties={
|
|
||||||
"tenant_id": tenant_id,
|
|
||||||
"error": str(e)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
raise
|
|
||||||
|
|
||||||
async def clear_email_senders(self, tenant_id: str):
|
|
||||||
"""clear up email senders for tenant"""
|
|
||||||
try:
|
|
||||||
doc = await EmailSenderDoc.find_one({"tenant_id": tenant_id, "is_active": True})
|
|
||||||
if doc:
|
|
||||||
await doc.set({"email_senders": []})
|
|
||||||
await self.module_logger.log_info(
|
|
||||||
info="Email senders cleared from database",
|
|
||||||
properties={"tenant_id": tenant_id}
|
|
||||||
)
|
|
||||||
return {"success": True}
|
|
||||||
return {"success": False, "msg": "No sender config found"}
|
|
||||||
except Exception as e:
|
|
||||||
await self.module_logger.log_error(
|
|
||||||
error="Failed to clear email senders",
|
|
||||||
properties={
|
properties={
|
||||||
"tenant_id": tenant_id,
|
"tenant_id": tenant_id,
|
||||||
"error": str(e)
|
"error": str(e)
|
||||||
|
|||||||
@ -26,7 +26,7 @@ class MessageTemplateDoc(Document):
|
|||||||
|
|
||||||
class EmailSenderDoc(Document):
|
class EmailSenderDoc(Document):
|
||||||
tenant_id: str
|
tenant_id: str
|
||||||
email_senders: List[str] = []
|
email_sender: Optional[str] = None
|
||||||
is_active: bool = True
|
is_active: bool = True
|
||||||
|
|
||||||
class Settings:
|
class Settings:
|
||||||
@ -36,7 +36,7 @@ class EmailSenderDoc(Document):
|
|||||||
class EmailSendStatusDoc(Document):
|
class EmailSendStatusDoc(Document):
|
||||||
email_id: str
|
email_id: str
|
||||||
tenant_id: str
|
tenant_id: str
|
||||||
email_senders: List[str]
|
email_sender: Optional[str] = None
|
||||||
recipient_email: str
|
recipient_email: str
|
||||||
template_id: Optional[str] = None
|
template_id: Optional[str] = None
|
||||||
subject: str
|
subject: str
|
||||||
|
|||||||
@ -45,43 +45,30 @@ class EmailValidationService:
|
|||||||
)
|
)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
async def validate_sender_emails(self, tenant_id: str, sender_emails: List[str]):
|
async def validate_sender_email(self, tenant_id: str, sender_email: str):
|
||||||
"""validate sender emails, including format validation and permission validation"""
|
"""validate sender email, including format validation and permission validation"""
|
||||||
try:
|
try:
|
||||||
valid_senders = []
|
authorized_sender = await self.email_sender_handler.get_email_sender(tenant_id)
|
||||||
invalid_senders = []
|
|
||||||
|
|
||||||
authorized_senders = await self.email_sender_handler.get_email_senders(tenant_id)
|
|
||||||
for sender_email in sender_emails:
|
|
||||||
# format validation
|
# format validation
|
||||||
if not await self.email_validation_handler.is_valid_email(sender_email):
|
if not await self.email_validation_handler.is_valid_email(sender_email):
|
||||||
invalid_senders.append(sender_email)
|
return None
|
||||||
continue
|
|
||||||
|
|
||||||
# domain validation
|
|
||||||
if not await self.email_validation_handler.is_valid_domain(sender_email):
|
|
||||||
invalid_senders.append(sender_email)
|
|
||||||
continue
|
|
||||||
|
|
||||||
# sender permission validation
|
# sender permission validation
|
||||||
# Allow support@freeleaps.com as default sender even if not in authorized_senders
|
# Allow support@freeleaps.com as default sender even if not in authorized_senders
|
||||||
if sender_email not in authorized_senders and sender_email != "support@freeleaps.com":
|
if sender_email not in ["support@freeleaps.com", authorized_sender]:
|
||||||
invalid_senders.append(sender_email)
|
return None
|
||||||
continue
|
|
||||||
|
|
||||||
valid_senders.append(sender_email)
|
|
||||||
|
|
||||||
await self.module_logger.log_info(
|
await self.module_logger.log_info(
|
||||||
"Sender email validation completed",
|
"Sender email validation completed",
|
||||||
properties={
|
properties={
|
||||||
"tenant_id": tenant_id,
|
"tenant_id": tenant_id,
|
||||||
"total_senders": len(sender_emails),
|
"sender_email": sender_email,
|
||||||
"valid_count": len(valid_senders),
|
"authorized_sender": authorized_sender,
|
||||||
"invalid_count": len(invalid_senders)
|
"is_valid": True
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
return valid_senders, invalid_senders
|
return sender_email
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
await self.module_logger.log_error(
|
await self.module_logger.log_error(
|
||||||
|
|||||||
@ -1,55 +1,55 @@
|
|||||||
from typing import List
|
from typing import List
|
||||||
from backend.infra.email_sender_handler import EmailSenderHandler
|
from backend.infra.email_sender_handler import EmailSenderHandler
|
||||||
|
from backend.infra.email.email_validation_handler import EmailValidationHandler
|
||||||
from backend.models.models import EmailSenderDoc
|
from backend.models.models import EmailSenderDoc
|
||||||
|
|
||||||
|
|
||||||
class EmailSenderService:
|
class EmailSenderService:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.email_sender_handler = EmailSenderHandler()
|
self.email_sender_handler = EmailSenderHandler()
|
||||||
|
self.email_validation_handler = EmailValidationHandler()
|
||||||
|
|
||||||
async def get_email_senders(self, tenant_id: str) -> List[str]:
|
async def get_email_sender(self, tenant_id: str) -> str:
|
||||||
"""get email senders for tenant"""
|
"""get email sender for tenant"""
|
||||||
if not tenant_id:
|
if not tenant_id:
|
||||||
raise ValueError("tenant_id is required")
|
raise ValueError("tenant_id is required")
|
||||||
|
|
||||||
return await self.email_sender_handler.get_email_senders(tenant_id)
|
return await self.email_sender_handler.get_email_sender(tenant_id)
|
||||||
|
|
||||||
async def set_email_senders(self, tenant_id: str, email_senders: List[str]):
|
async def set_email_sender(self, tenant_id: str, email_sender: str):
|
||||||
"""set email senders for tenant"""
|
"""set email sender for tenant"""
|
||||||
if not tenant_id:
|
if not tenant_id:
|
||||||
raise ValueError("tenant_id is required")
|
raise ValueError("tenant_id is required")
|
||||||
|
|
||||||
if not email_senders or not isinstance(email_senders, list):
|
if not email_sender:
|
||||||
raise ValueError("email_senders must be a non-empty list")
|
raise ValueError("email_sender must be provided")
|
||||||
|
|
||||||
return await self.email_sender_handler.set_email_senders(tenant_id, email_senders)
|
if not await self.email_validation_handler.is_valid_email(email_sender):
|
||||||
|
raise ValueError("Invalid email format")
|
||||||
|
|
||||||
async def add_email_senders(self, tenant_id: str, new_senders: List[str]):
|
# TODO: check if the email is already registered in SendGrid or other email service provider
|
||||||
"""add email senders to tenant"""
|
|
||||||
|
return await self.email_sender_handler.set_email_sender(tenant_id, email_sender)
|
||||||
|
|
||||||
|
async def update_email_sender(self, tenant_id: str, email_sender: str):
|
||||||
|
"""update email sender for tenant (only if exists)"""
|
||||||
if not tenant_id:
|
if not tenant_id:
|
||||||
raise ValueError("tenant_id is required")
|
raise ValueError("tenant_id is required")
|
||||||
|
|
||||||
if not new_senders or not isinstance(new_senders, list):
|
if not email_sender:
|
||||||
return {"success": False, "msg": "No sender provided"}
|
raise ValueError("email_sender must be provided")
|
||||||
|
|
||||||
return await self.email_sender_handler.add_email_senders(tenant_id, new_senders)
|
if not await self.email_validation_handler.is_valid_email(email_sender):
|
||||||
|
raise ValueError("Invalid email format")
|
||||||
|
|
||||||
async def remove_email_senders(self, tenant_id: str, emails_to_remove: List[str]):
|
# Check if email sender exists first
|
||||||
"""remove email senders from tenant"""
|
existing_sender = await self.email_sender_handler.get_email_sender(tenant_id)
|
||||||
if not tenant_id:
|
if not existing_sender:
|
||||||
raise ValueError("tenant_id is required")
|
raise ValueError("Email sender configuration not found for this tenant")
|
||||||
|
|
||||||
if not emails_to_remove or not isinstance(emails_to_remove, list):
|
# TODO: check if the email is already registered in SendGrid or other email service provider
|
||||||
raise ValueError("emails_to_remove must be a non-empty list")
|
# Only update if exists
|
||||||
|
return await self.email_sender_handler.update_email_sender(tenant_id, email_sender)
|
||||||
return await self.email_sender_handler.remove_email_senders(tenant_id, emails_to_remove)
|
|
||||||
|
|
||||||
async def clear_email_senders(self, tenant_id: str):
|
|
||||||
"""clear email senders for tenant"""
|
|
||||||
if not tenant_id:
|
|
||||||
raise ValueError("tenant_id is required")
|
|
||||||
|
|
||||||
return await self.email_sender_handler.clear_email_senders(tenant_id)
|
|
||||||
|
|
||||||
async def delete_email_sender(self, tenant_id: str):
|
async def delete_email_sender(self, tenant_id: str):
|
||||||
"""delete email sender for tenant"""
|
"""delete email sender for tenant"""
|
||||||
|
|||||||
@ -16,14 +16,8 @@ token_manager = TokenManager()
|
|||||||
email_sender_hub = EmailSenderHub()
|
email_sender_hub = EmailSenderHub()
|
||||||
|
|
||||||
# Define the request body schema
|
# Define the request body schema
|
||||||
class EmailSenderSetRequest(BaseModel):
|
class EmailSenderRequest(BaseModel):
|
||||||
email_senders: List[str]
|
email_sender: str
|
||||||
|
|
||||||
class EmailSenderAddRequest(BaseModel):
|
|
||||||
new_senders: List[str]
|
|
||||||
|
|
||||||
class EmailSenderRemoveRequest(BaseModel):
|
|
||||||
emails_to_remove: List[str]
|
|
||||||
|
|
||||||
# check credentials for admin and tenant
|
# check credentials for admin and tenant
|
||||||
def admin_only(credentials: HTTPAuthorizationCredentials = Depends(security)):
|
def admin_only(credentials: HTTPAuthorizationCredentials = Depends(security)):
|
||||||
@ -74,44 +68,45 @@ def tenant_only(credentials: HTTPAuthorizationCredentials = Depends(security)):
|
|||||||
raise HTTPException(status_code=401, detail="Invalid token")
|
raise HTTPException(status_code=401, detail="Invalid token")
|
||||||
|
|
||||||
# Web API
|
# Web API
|
||||||
# Get email senders for tenant
|
# Get email sender for tenant
|
||||||
@router.get(
|
@router.get(
|
||||||
"/email_senders/get",
|
"/email_sender/get",
|
||||||
dependencies=[Depends(tenant_only)],
|
dependencies=[Depends(tenant_only)],
|
||||||
operation_id="get_email_senders",
|
operation_id="get_email_sender",
|
||||||
summary="Get email senders for tenant",
|
summary="Get email sender for tenant",
|
||||||
description="Retrieve the list of email senders configured for the current tenant",
|
description="Retrieve the email sender configured for the current tenant",
|
||||||
response_description="List of email sender addresses"
|
response_description="email sender address"
|
||||||
)
|
)
|
||||||
async def get_email_senders(payload: dict = Depends(tenant_only)):
|
async def get_email_sender(payload: dict = Depends(tenant_only)):
|
||||||
try:
|
try:
|
||||||
tenant_id = payload.get("tenant_id")
|
tenant_id = payload.get("tenant_id")
|
||||||
|
|
||||||
result = await email_sender_hub.get_email_senders(tenant_id)
|
email_sender = await email_sender_hub.get_email_sender(tenant_id)
|
||||||
|
|
||||||
return JSONResponse(
|
return JSONResponse(
|
||||||
content={"success": True, "email_senders": result},
|
content={"success": True, "email_sender": email_sender},
|
||||||
status_code=200
|
status_code=200
|
||||||
)
|
)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
raise HTTPException(status_code=400, detail=str(e))
|
raise HTTPException(status_code=400, detail=str(e))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise HTTPException(status_code=500, detail="Failed to get email senders")
|
raise HTTPException(status_code=500, detail="Failed to get email sender")
|
||||||
|
|
||||||
# Set email senders for tenant
|
# Set email sender for tenant
|
||||||
@router.post(
|
@router.post(
|
||||||
"/email_senders/set",
|
"/email_sender/set",
|
||||||
dependencies=[Depends(tenant_only)],
|
dependencies=[Depends(tenant_only)],
|
||||||
operation_id="set_email_senders",
|
operation_id="set_email_sender",
|
||||||
summary="Set email senders for tenant",
|
summary="Set email sender for tenant",
|
||||||
description="Set the complete list of email senders for the specified tenant",
|
description="Set the email sender for the specified tenant (replaces existing)",
|
||||||
response_description="Success/failure response in setting email senders"
|
response_description="Success/failure response in setting email sender"
|
||||||
)
|
)
|
||||||
async def set_email_senders(request: EmailSenderSetRequest, payload: dict = Depends(tenant_only)):
|
async def set_email_sender(request: EmailSenderRequest, payload: dict = Depends(tenant_only)):
|
||||||
try:
|
try:
|
||||||
tenant_id = payload.get("tenant_id")
|
tenant_id = payload.get("tenant_id")
|
||||||
result = await email_sender_hub.set_email_senders(tenant_id, request.email_senders)
|
email_sender = await email_sender_hub.set_email_sender(tenant_id, request.email_sender)
|
||||||
return JSONResponse(
|
return JSONResponse(
|
||||||
content=result,
|
content=email_sender,
|
||||||
status_code=200
|
status_code=200
|
||||||
)
|
)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
@ -119,24 +114,23 @@ async def set_email_senders(request: EmailSenderSetRequest, payload: dict = Depe
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
import traceback
|
import traceback
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
raise HTTPException(status_code=500, detail=f"Failed to set email senders: {str(e)}")
|
raise HTTPException(status_code=500, detail=f"Failed to set email sender: {str(e)}")
|
||||||
|
|
||||||
# Add email senders to tenant
|
# Update email sender for tenant
|
||||||
@router.post(
|
@router.put(
|
||||||
"/email_senders/add",
|
"/email_sender/update",
|
||||||
dependencies=[Depends(tenant_only)],
|
dependencies=[Depends(tenant_only)],
|
||||||
operation_id="add_email_senders",
|
operation_id="update_email_sender",
|
||||||
summary="Add email senders to tenant",
|
summary="Update email sender for tenant",
|
||||||
description="Add new email senders to the existing list for the specified tenant",
|
description="Update the email sender for the specified tenant (only if exists)",
|
||||||
response_description="Success/failure response in adding email senders"
|
response_description="Success/failure response in updating email sender"
|
||||||
)
|
)
|
||||||
async def add_email_senders(request: EmailSenderAddRequest, payload: dict = Depends(tenant_only)):
|
async def update_email_sender(request: EmailSenderRequest, payload: dict = Depends(tenant_only)):
|
||||||
try:
|
try:
|
||||||
tenant_id = payload.get("tenant_id")
|
tenant_id = payload.get("tenant_id")
|
||||||
|
email_sender = await email_sender_hub.update_email_sender(tenant_id, request.email_sender)
|
||||||
result = await email_sender_hub.add_email_senders(tenant_id, request.new_senders)
|
|
||||||
return JSONResponse(
|
return JSONResponse(
|
||||||
content=result,
|
content=email_sender,
|
||||||
status_code=200
|
status_code=200
|
||||||
)
|
)
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
@ -144,57 +138,11 @@ async def add_email_senders(request: EmailSenderAddRequest, payload: dict = Depe
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
import traceback
|
import traceback
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
raise HTTPException(status_code=500, detail=f"Failed to add email senders: {str(e)}")
|
raise HTTPException(status_code=500, detail=f"Failed to update email sender: {str(e)}")
|
||||||
|
|
||||||
# Remove email senders from tenant
|
|
||||||
@router.delete(
|
|
||||||
"/email_senders/remove",
|
|
||||||
dependencies=[Depends(tenant_only)],
|
|
||||||
operation_id="remove_email_senders",
|
|
||||||
summary="Remove email senders from tenant",
|
|
||||||
description="Remove specific email senders from the tenant's list",
|
|
||||||
response_description="Success/failure response in removing email senders"
|
|
||||||
)
|
|
||||||
async def remove_email_senders(request: EmailSenderRemoveRequest, payload: dict = Depends(tenant_only)):
|
|
||||||
try:
|
|
||||||
tenant_id = payload.get("tenant_id")
|
|
||||||
|
|
||||||
result = await email_sender_hub.remove_email_senders(tenant_id, request.emails_to_remove)
|
|
||||||
return JSONResponse(
|
|
||||||
content=result,
|
|
||||||
status_code=200
|
|
||||||
)
|
|
||||||
except ValueError as e:
|
|
||||||
raise HTTPException(status_code=400, detail=str(e))
|
|
||||||
except Exception as e:
|
|
||||||
import traceback
|
|
||||||
traceback.print_exc()
|
|
||||||
raise HTTPException(status_code=500, detail=f"Failed to remove email senders: {str(e)}")
|
|
||||||
|
|
||||||
# Clear all email senders for tenant
|
|
||||||
@router.delete(
|
|
||||||
"/email_senders/clear",
|
|
||||||
dependencies=[Depends(tenant_only)],
|
|
||||||
operation_id="clear_email_senders",
|
|
||||||
summary="Clear all email senders for tenant",
|
|
||||||
description="Remove all email senders from the current tenant's list",
|
|
||||||
response_description="Success/failure response in clearing email senders"
|
|
||||||
)
|
|
||||||
async def clear_email_senders(payload: dict = Depends(tenant_only)):
|
|
||||||
try:
|
|
||||||
tenant_id = payload.get("tenant_id")
|
|
||||||
|
|
||||||
result = await email_sender_hub.clear_email_senders(tenant_id)
|
|
||||||
return JSONResponse(
|
|
||||||
content=result,
|
|
||||||
status_code=200
|
|
||||||
)
|
|
||||||
except Exception as e:
|
|
||||||
raise HTTPException(status_code=500, detail="Failed to clear email senders")
|
|
||||||
|
|
||||||
# Delete email sender configuration for tenant
|
# Delete email sender configuration for tenant
|
||||||
@router.delete(
|
@router.delete(
|
||||||
"/email_senders/delete/{tenant_id}",
|
"/email_sender/delete/{tenant_id}",
|
||||||
dependencies=[Depends(admin_only)],
|
dependencies=[Depends(admin_only)],
|
||||||
operation_id="delete_email_sender",
|
operation_id="delete_email_sender",
|
||||||
summary="Delete email sender configuration for tenant",
|
summary="Delete email sender configuration for tenant",
|
||||||
|
|||||||
@ -16,15 +16,15 @@ class TenantEmailRequest(BaseModel):
|
|||||||
subject_properties: Dict = {}
|
subject_properties: Dict = {}
|
||||||
body_properties: Dict = {}
|
body_properties: Dict = {}
|
||||||
region: int
|
region: int
|
||||||
sender_emails: Optional[List[str]] = None
|
sender_email: str = None
|
||||||
priority: str = "normal"
|
priority: str = "normal"
|
||||||
tracking_enabled: bool = True
|
tracking_enabled: bool = True
|
||||||
|
|
||||||
@router.post(
|
@router.post(
|
||||||
"/send_tenant_email",
|
"/send_tenant_email",
|
||||||
operation_id="send_tenant_email",
|
operation_id="send_tenant_email",
|
||||||
summary="Send email using tenant's template and email senders",
|
summary="Send email using tenant's template and email sender",
|
||||||
description="Send email using tenant's selected template and email senders",
|
description="Send email using tenant's selected template and email sender",
|
||||||
response_description="Success/failure response in processing the tenant email send request",
|
response_description="Success/failure response in processing the tenant email send request",
|
||||||
)
|
)
|
||||||
async def send_tenant_email(request: TenantEmailRequest):
|
async def send_tenant_email(request: TenantEmailRequest):
|
||||||
@ -36,7 +36,7 @@ async def send_tenant_email(request: TenantEmailRequest):
|
|||||||
subject_properties=request.subject_properties,
|
subject_properties=request.subject_properties,
|
||||||
body_properties=request.body_properties,
|
body_properties=request.body_properties,
|
||||||
region=request.region,
|
region=request.region,
|
||||||
sender_emails=request.sender_emails,
|
sender_email=request.sender_email,
|
||||||
priority=request.priority,
|
priority=request.priority,
|
||||||
tracking_enabled=request.tracking_enabled
|
tracking_enabled=request.tracking_enabled
|
||||||
)
|
)
|
||||||
@ -87,4 +87,4 @@ async def get_tenant_email_status_list(
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise HTTPException(status_code=500, detail=f"Failed to get tenant email status list: {str(e)}")
|
raise HTTPException(status_code=500, detail=f"Failed to get tenant email status list: {str(e)}")
|
||||||
|
|
||||||
|
## TODO: add SendGrid Event Webhook to handle bounce and tracking
|
||||||
@ -60,7 +60,7 @@ class EmailMQConsumer:
|
|||||||
tenant_id = message.get("tenant_id") or message.get("properties", {}).get("tenant_id")
|
tenant_id = message.get("tenant_id") or message.get("properties", {}).get("tenant_id")
|
||||||
template_id = message.get("properties", {}).get("template_id")
|
template_id = message.get("properties", {}).get("template_id")
|
||||||
destination_emails = message.get("properties", {}).get("destination_emails", [])
|
destination_emails = message.get("properties", {}).get("destination_emails", [])
|
||||||
sender_emails = message.get("properties", {}).get("sender_emails", [])
|
sender_email = message.get("properties", {}).get("sender_email", "")
|
||||||
# Use rendered content instead of template properties
|
# Use rendered content instead of template properties
|
||||||
subject = message.get("properties", {}).get("content_subject", "No Subject")
|
subject = message.get("properties", {}).get("content_subject", "No Subject")
|
||||||
html_content = message.get("properties", {}).get("content_text", "")
|
html_content = message.get("properties", {}).get("content_text", "")
|
||||||
@ -74,7 +74,7 @@ class EmailMQConsumer:
|
|||||||
tenant_id=tenant_id,
|
tenant_id=tenant_id,
|
||||||
template_id=template_id,
|
template_id=template_id,
|
||||||
recipient_email=recipient_email,
|
recipient_email=recipient_email,
|
||||||
sender_emails=sender_emails,
|
sender_email=sender_email,
|
||||||
subject_properties={"subject": subject},
|
subject_properties={"subject": subject},
|
||||||
body_properties={"html_content": html_content}
|
body_properties={"html_content": html_content}
|
||||||
)
|
)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user