99 lines
3.8 KiB
Python
99 lines
3.8 KiB
Python
import smtplib
|
|||
|
|
import logging
|
||
|
|
from email.mime.text import MIMEText
|
||
|
|
from email.mime.multipart import MIMEMultipart
|
||
|
|
from typing import Optional, List
|
||
|
|
from app.core.config import settings
|
||
|
|
|
||
|
|
logger = logging.getLogger(__name__)
|
||
|
|
|
||
|
|
class EmailService:
|
||
|
|
"""
|
||
|
|
Email Service mirroring Spring Boot's JavaMailSender / EmailService.
|
||
|
|
Supports SMTP (Gmail, SendGrid, AWS SES, etc.).
|
||
|
|
If SMTP is not configured, logs the message to console (Dev Mode) so flows never crash.
|
||
|
|
"""
|
||
|
|
|
||
|
|
def __init__(self):
|
||
|
|
self.host = getattr(settings, "SMTP_HOST", "")
|
||
|
|
self.port = getattr(settings, "SMTP_PORT", 587)
|
||
|
|
self.user = getattr(settings, "SMTP_USER", "")
|
||
|
|
self.password = getattr(settings, "SMTP_PASSWORD", "")
|
||
|
|
self.tls = getattr(settings, "SMTP_TLS", True)
|
||
|
|
self.from_email = getattr(settings, "EMAILS_FROM_EMAIL", "noreply@authsec.com")
|
||
|
|
|
||
|
|
def is_configured(self) -> bool:
|
||
|
|
"""Returns True if valid SMTP credentials are provided."""
|
||
|
|
return bool(self.host and self.user and self.password)
|
||
|
|
|
||
|
|
def send_simple_message(self, to: str, subject: str, text: str, from_email: Optional[str] = None) -> bool:
|
||
|
|
"""Sends a plain text email (mirrors Spring Boot EmailService.sendSimpleMessage)."""
|
||
|
|
sender = from_email or self.from_email
|
||
|
|
|
||
|
|
if not self.is_configured():
|
||
|
|
print(f"\n==================== [DEV EMAIL SERVICE] ====================")
|
||
|
|
print(f"To: {to}")
|
||
|
|
print(f"From: {sender}")
|
||
|
|
print(f"Subject: {subject}")
|
||
|
|
print(f"Body:\n{text}")
|
||
|
|
print(f"=============================================================\n")
|
||
|
|
logger.info(f"[DEV EMAIL] Sent simulated email to {to}: {subject}")
|
||
|
|
return True
|
||
|
|
|
||
|
|
try:
|
||
|
|
msg = MIMEMultipart()
|
||
|
|
msg["From"] = sender
|
||
|
|
msg["To"] = to
|
||
|
|
msg["Subject"] = subject
|
||
|
|
msg.attach(MIMEText(text, "plain"))
|
||
|
|
|
||
|
|
with smtplib.SMTP(self.host, self.port) as server:
|
||
|
|
if self.tls:
|
||
|
|
server.starttls()
|
||
|
|
server.login(self.user, self.password)
|
||
|
|
server.send_message(msg)
|
||
|
|
|
||
|
|
logger.info(f"Email successfully sent to {to}")
|
||
|
|
return True
|
||
|
|
except Exception as e:
|
||
|
|
logger.error(f"Failed to send email to {to}: {e}")
|
||
|
|
print(f"❌ Email sending error: {e}")
|
||
|
|
return False
|
||
|
|
|
||
|
|
def send_html_message(self, to: str, subject: str, html_content: str, from_email: Optional[str] = None) -> bool:
|
||
|
|
"""Sends an HTML formatted email."""
|
||
|
|
sender = from_email or self.from_email
|
||
|
|
|
||
|
|
if not self.is_configured():
|
||
|
|
print(f"\n==================== [DEV EMAIL SERVICE (HTML)] ====================")
|
||
|
|
print(f"To: {to}")
|
||
|
|
print(f"Subject: {subject}")
|
||
|
|
print(f"HTML Body:\n{html_content}")
|
||
|
|
print(f"===================================================================\n")
|
||
|
|
return True
|
||
|
|
|
||
|
|
try:
|
||
|
|
msg = MIMEMultipart("alternative")
|
||
|
|
msg["From"] = sender
|
||
|
|
msg["To"] = to
|
||
|
|
msg["Subject"] = subject
|
||
|
|
msg.attach(MIMEText(html_content, "html"))
|
||
|
|
|
||
|
|
with smtplib.SMTP(self.host, self.port) as server:
|
||
|
|
if self.tls:
|
||
|
|
server.starttls()
|
||
|
|
server.login(self.user, self.password)
|
||
|
|
server.send_message(msg)
|
||
|
|
|
||
|
|
return True
|
||
|
|
except Exception as e:
|
||
|
|
logger.error(f"Failed to send HTML email to {to}: {e}")
|
||
|
|
return False
|
||
|
|
|
||
|
|
def adduserviaadmin(self, to: str, subject: str, otp_or_url: str):
|
||
|
|
"""Mirrors Spring Boot EmailService.adduserviaadmin."""
|
||
|
|
body = f"Your verification code is: {otp_or_url}"
|
||
|
|
return self.send_simple_message(to, subject, body)
|
||
|
|
|
||
|
|
email_service = EmailService()
|