apigateway / services /email_service.py
jebin2's picture
google sign in
1bd7131
import os
import base64
import logging
import smtplib
import ssl
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError
logger = logging.getLogger(__name__)
# Email configuration
SMTP_SERVER = os.getenv("SMTP_SERVER", "127.0.0.1")
SMTP_PORT = int(os.getenv("SMTP_PORT", "1025"))
# Prioritize EMAIL_ID and EMAIL_PASSWORD if available
SMTP_USERNAME = os.getenv("EMAIL_ID") or os.getenv("SMTP_USERNAME", "sender@domain.com")
SMTP_PASSWORD = os.getenv("EMAIL_PASSWORD") or os.getenv("SMTP_PASSWORD", "yourpassword")
# Auto-configure for Gmail if using defaults and gmail address
if SMTP_SERVER == "127.0.0.1" and "gmail.com" in SMTP_USERNAME:
SMTP_SERVER = "smtp.gmail.com"
SMTP_PORT = 465
SMTP_SENDER = os.getenv("SMTP_SENDER", SMTP_USERNAME)
class GmailService:
SCOPES = ['https://www.googleapis.com/auth/gmail.send']
def __init__(self):
self.creds = None
self.service = None
# Server-side credentials for Gmail API
self.client_id = os.getenv('SERVER_GOOGLE_CLIENT_ID') or os.getenv('GOOGLE_CLIENT_ID')
self.client_secret = os.getenv('SERVER_GOOGLE_CLIENT_SECRET') or os.getenv('GOOGLE_CLIENT_SECRET')
self.refresh_token = os.getenv('SERVER_GOOGLE_REFRESH_TOKEN') or os.getenv('GOOGLE_REFRESH_TOKEN')
self.sender_email = os.getenv('SMTP_SENDER') or os.getenv('EMAIL_ID')
def authenticate(self):
"""Authenticate using the refresh token."""
if not all([self.client_id, self.client_secret, self.refresh_token]):
# logger.error("Missing Google API credentials (CLIENT_ID, CLIENT_SECRET, REFRESH_TOKEN)")
return False
try:
# Create credentials object from the refresh token
self.creds = Credentials(
None, # No access token initially
refresh_token=self.refresh_token,
token_uri="https://oauth2.googleapis.com/token",
client_id=self.client_id,
client_secret=self.client_secret,
scopes=self.SCOPES
)
# Refresh the token (get a new access token)
if self.creds and self.creds.expired and self.creds.refresh_token:
self.creds.refresh(Request())
self.service = build('gmail', 'v1', credentials=self.creds)
return True
except Exception as e:
logger.error(f"Failed to authenticate with Gmail API: {e}")
return False
def send_email(self, to_email: str, subject: str, body: str) -> bool:
"""Send an email using the Gmail API."""
if not self.service:
if not self.authenticate():
return False
try:
message = MIMEMultipart()
message['to'] = to_email
# Format sender with display name
sender_name = "AnimateImage"
if self.sender_email and "<" not in self.sender_email:
message['from'] = f"{sender_name} <{self.sender_email}>"
else:
message['from'] = self.sender_email or "unknown@example.com"
message['subject'] = subject
message.attach(MIMEText(body, 'plain'))
raw_message = base64.urlsafe_b64encode(message.as_bytes()).decode('utf-8')
body = {'raw': raw_message}
message = (self.service.users().messages().send(userId="me", body=body).execute())
logger.info(f"Email sent to {to_email} (Message Id: {message['id']})")
return True
except HttpError as error:
logger.error(f"An error occurred sending email to {to_email}: {error}")
return False
except Exception as e:
logger.error(f"Unexpected error sending email: {e}")
return False
# Initialize global instance
gmail_service = GmailService()
def send_email(to_email: str, subject: str, body: str):
"""
Send an email using Gmail API with SMTP fallback.
This function is blocking and should be run in a background task.
"""
# Try Gmail API first
if gmail_service.authenticate():
if gmail_service.send_email(to_email, subject, body):
return True
logger.warning("Gmail API credentials not found or invalid. Falling back to SMTP.")
# Fallback to SMTP (Original Implementation)
try:
message = MIMEMultipart()
message["From"] = SMTP_SENDER
message["To"] = to_email
message["Subject"] = subject
message.attach(MIMEText(body, "plain"))
# Create a secure SSL context
context = ssl.create_default_context()
context.check_hostname = False
context.verify_mode = ssl.CERT_NONE
with smtplib.SMTP_SSL(SMTP_SERVER, SMTP_PORT, context=context) as server:
server.login(SMTP_USERNAME, SMTP_PASSWORD)
server.sendmail(SMTP_SENDER, to_email, message.as_string())
logger.info(f"Email sent successfully to {to_email} via SMTP")
return True
except Exception as e:
logger.error(f"Failed to send email to {to_email}: {e}")
return False