Spaces:
Paused
Paused
| """CrowData Email Service — Async email sending via SMTP.""" | |
| import logging | |
| from pathlib import Path | |
| from jinja2 import Environment, FileSystemLoader | |
| import aiosmtplib | |
| from email.mime.text import MIMEText | |
| from email.mime.multipart import MIMEMultipart | |
| from email.mime.application import MIMEApplication | |
| from app.config import get_settings | |
| logger = logging.getLogger(__name__) | |
| settings = get_settings() | |
| TEMPLATE_DIR = Path(__file__).parent.parent / "templates" / "emails" | |
| jinja_env = Environment(loader=FileSystemLoader(str(TEMPLATE_DIR))) | |
| async def send_email( | |
| to_email: str, | |
| subject: str, | |
| template_name: str, | |
| context: dict, | |
| attachments: list[dict] | None = None, | |
| ): | |
| """ | |
| Send an HTML email using Jinja2 templates. | |
| Args: | |
| to_email: Recipient email address | |
| subject: Email subject | |
| template_name: Template file name (e.g., 'welcome.html') | |
| context: Template context variables | |
| attachments: Optional list of dicts with 'filename' and 'content' (bytes) | |
| """ | |
| try: | |
| # Render template | |
| template = jinja_env.get_template(template_name) | |
| html_body = template.render(**context) | |
| # Build message | |
| msg = MIMEMultipart("alternative") | |
| msg["From"] = f"{settings.from_name} <{settings.from_email}>" | |
| msg["To"] = to_email | |
| msg["Subject"] = subject | |
| # Plain text fallback | |
| plain_text = _html_to_plain(html_body) | |
| msg.attach(MIMEText(plain_text, "plain", "utf-8")) | |
| msg.attach(MIMEText(html_body, "html", "utf-8")) | |
| # Attachments (e.g., PDF reports) | |
| if attachments: | |
| for att in attachments: | |
| part = MIMEApplication(att["content"], Name=att["filename"]) | |
| part["Content-Disposition"] = f'attachment; filename="{att["filename"]}"' | |
| msg.attach(part) | |
| # Send | |
| if not settings.smtp_user or not settings.smtp_password: | |
| logger.warning(f"SMTP not configured — email to {to_email} NOT sent. Subject: {subject}") | |
| return False | |
| await aiosmtplib.send( | |
| msg, | |
| hostname=settings.smtp_host, | |
| port=settings.smtp_port, | |
| username=settings.smtp_user, | |
| password=settings.smtp_password, | |
| start_tls=settings.smtp_use_tls, | |
| ) | |
| logger.info(f"Email sent to {to_email}: {subject}") | |
| return True | |
| except Exception as e: | |
| logger.error(f"Failed to send email to {to_email}: {e}") | |
| return False | |
| async def send_welcome_email(to_email: str, full_name: str | None = None): | |
| """Send welcome email after registration.""" | |
| name = full_name or to_email.split("@")[0].title() | |
| return await send_email( | |
| to_email=to_email, | |
| subject="Bienvenido a CrowData", | |
| template_name="welcome.html", | |
| context={ | |
| "name": name, | |
| "email": to_email, | |
| "login_url": "https://crowdata.ar/src/pages/login.html", | |
| "dashboard_url": "https://crowdata.ar/src/pages/dashboard.html", | |
| "support_email": settings.from_email, | |
| }, | |
| ) | |
| async def send_pdf_report(to_email: str, report_type: str, identifier: str, pdf_bytes: bytes, user_name: str | None = None): | |
| """Send a PDF report as email attachment.""" | |
| type_labels = { | |
| "persona": "Persona", | |
| "empresa": "Empresa", | |
| "vehiculo": "Vehículo", | |
| "propiedad": "Inmueble", | |
| } | |
| type_label = type_labels.get(report_type, report_type) | |
| filename = f"CrowData_{type_label}_{identifier}.pdf" | |
| return await send_email( | |
| to_email=to_email, | |
| subject=f"CrowData — Informe de {type_label} {identifier}", | |
| template_name="pdf_delivery.html", | |
| context={ | |
| "name": user_name or to_email.split("@")[0].title(), | |
| "report_type": type_label, | |
| "identifier": identifier, | |
| "support_email": settings.from_email, | |
| }, | |
| attachments=[{"filename": filename, "content": pdf_bytes}], | |
| ) | |
| def _html_to_plain(html: str) -> str: | |
| """Very basic HTML to plain text conversion.""" | |
| import re | |
| text = re.sub(r"<br\s*/?>", "\n", html) | |
| text = re.sub(r"<[^>]+>", "", text) | |
| text = re.sub(r"\s+", " ", text).strip() | |
| return text | |