feat: plantilla base workspace SaaS
This commit is contained in:
118
backend/core/email.py
Normal file
118
backend/core/email.py
Normal file
@@ -0,0 +1,118 @@
|
||||
"""
|
||||
Email service for sending reports via SMTP.
|
||||
"""
|
||||
import aiosmtplib
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
from email.mime.text import MIMEText
|
||||
from email.mime.base import MIMEBase
|
||||
from email import encoders
|
||||
from typing import List
|
||||
import logging
|
||||
from datetime import datetime
|
||||
|
||||
from core.config import settings
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class EmailService:
|
||||
"""Service for sending emails with attachments."""
|
||||
|
||||
@staticmethod
|
||||
async def send_report_email(
|
||||
recipient_email: str,
|
||||
subject: str,
|
||||
body_text: str,
|
||||
csv_content: str,
|
||||
filename: str
|
||||
) -> bool:
|
||||
"""
|
||||
Send a report email with CSV attachment.
|
||||
|
||||
Args:
|
||||
recipient_email: Email address of recipient
|
||||
subject: Email subject line
|
||||
body_text: Plain text email body
|
||||
csv_content: CSV file content as string
|
||||
filename: Name for the CSV attachment
|
||||
|
||||
Returns:
|
||||
bool: True if email sent successfully, False otherwise
|
||||
"""
|
||||
try:
|
||||
# Create message
|
||||
msg = MIMEMultipart()
|
||||
msg['From'] = f"{settings.SMTP_FROM_NAME} <{settings.SMTP_USER}>"
|
||||
msg['To'] = recipient_email
|
||||
msg['Subject'] = subject
|
||||
|
||||
# Email body
|
||||
html_body = f"""
|
||||
<html>
|
||||
<body style="font-family: Arial, sans-serif; line-height: 1.6; color: #333;">
|
||||
<div style="max-width: 600px; margin: 0 auto; padding: 20px;">
|
||||
<h2 style="color: #2563eb; border-bottom: 2px solid #2563eb; padding-bottom: 10px;">
|
||||
Reporte de Facturas
|
||||
</h2>
|
||||
<p>{body_text}</p>
|
||||
<p style="margin-top: 20px;">
|
||||
El reporte se encuentra adjunto en formato CSV.
|
||||
</p>
|
||||
<hr style="margin: 30px 0; border: none; border-top: 1px solid #e5e7eb;">
|
||||
<p style="font-size: 12px; color: #6b7280;">
|
||||
Este es un correo generado automáticamente. Por favor no responder.
|
||||
</p>
|
||||
<p style="font-size: 12px; color: #6b7280;">
|
||||
Generado el {datetime.now().strftime('%d/%m/%Y a las %H:%M')}
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
"""
|
||||
msg.attach(MIMEText(html_body, 'html'))
|
||||
|
||||
# CSV attachment with UTF-8 BOM for Excel compatibility
|
||||
attachment = MIMEBase('text', 'csv')
|
||||
csv_bytes = b'\xef\xbb\xbf' + csv_content.encode('utf-8')
|
||||
attachment.set_payload(csv_bytes)
|
||||
encoders.encode_base64(attachment)
|
||||
attachment.add_header(
|
||||
'Content-Disposition',
|
||||
f'attachment; filename="{filename}"'
|
||||
)
|
||||
msg.attach(attachment)
|
||||
|
||||
# Create SSL context that ignores certificate errors
|
||||
import ssl
|
||||
context = ssl.create_default_context()
|
||||
context.check_hostname = False
|
||||
context.verify_mode = ssl.CERT_NONE
|
||||
|
||||
# Send email
|
||||
if settings.SMTP_PORT == 465:
|
||||
# Port 465 uses implicit SSL
|
||||
async with aiosmtplib.SMTP(
|
||||
hostname=settings.SMTP_HOST,
|
||||
port=settings.SMTP_PORT,
|
||||
use_tls=True, # Implicit SSL
|
||||
tls_context=context
|
||||
) as smtp:
|
||||
await smtp.login(settings.SMTP_USER, settings.SMTP_PASSWORD)
|
||||
await smtp.send_message(msg)
|
||||
else:
|
||||
# Port 587 uses STARTTLS
|
||||
async with aiosmtplib.SMTP(
|
||||
hostname=settings.SMTP_HOST,
|
||||
port=settings.SMTP_PORT,
|
||||
tls_context=context
|
||||
) as smtp:
|
||||
await smtp.starttls(tls_context=context)
|
||||
await smtp.login(settings.SMTP_USER, settings.SMTP_PASSWORD)
|
||||
await smtp.send_message(msg)
|
||||
|
||||
logger.info(f"Email sent successfully to {recipient_email}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to send email to {recipient_email}: {str(e)}")
|
||||
return False
|
||||
Reference in New Issue
Block a user