epic-amp-email-sender / email_sender_api.py
nonzeroexit's picture
Update email_sender_api.py
0ff7b30 verified
raw
history blame
9.05 kB
from flask import Flask, request, jsonify
from flask_cors import CORS
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
# from email.mime.base import MIMEBase # Not used in simplified version
# from email import encoders # Not used in simplified version
import base64
import os
import socket
import traceback
# <<<--- FLASK APP INITIALIZATION MUST BE HERE (TOP LEVEL) --->>>
app = Flask(__name__)
CORS(app)
# <<<--------------------------------------------------------->>>
# Environment variables will be set as Secrets in Hugging Face Space settings
SMTP_SERVER_HOST = os.environ.get("SMTP_SERVER_HOST")
SMTP_SERVER_PORT = os.environ.get("SMTP_SERVER_PORT")
SMTP_SENDER_EMAIL = os.environ.get("SMTP_SENDER_EMAIL")
SMTP_SENDER_PASSWORD = os.environ.get("SMTP_SENDER_PASSWORD")
SMTP_CONNECTION_TIMEOUT = 30
SMTP_COMMAND_TIMEOUT = 30
@app.route('/send-report-via-email', methods=['POST']) # This decorator now correctly sees 'app'
def handle_send_email():
request_id = base64.b64encode(os.urandom(6)).decode('utf-8')
print(f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - Received request at /send-report-via-email")
if not all([SMTP_SERVER_HOST, SMTP_SERVER_PORT, SMTP_SENDER_EMAIL, SMTP_SENDER_PASSWORD]):
error_msg = "EMAIL_SENDER_API (HF) [{request_id}]: ERROR - SMTP environment variables (Secrets) not fully set."
print(error_msg)
return jsonify({"status": "error", "message": "Email server configuration incomplete on the API."}), 500
try:
SMTP_PORT_INT = int(SMTP_SERVER_PORT)
except ValueError:
error_msg = f"EMAIL_SENDER_API (HF) [{request_id}]: ERROR - Invalid SMTP_SERVER_PORT: '{SMTP_SERVER_PORT}'."
print(error_msg)
return jsonify({"status": "error", "message": "Invalid email server port configured on the API."}), 500
data = request.json
if not data:
print(f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - No JSON data received.")
return jsonify({"status": "error", "message": "No data received by email API."}), 400
recipient_email = data.get('recipient_email')
# pdf_base64_data = data.get('pdf_base64_data') # Not using these in simplified test
# pdf_filename = data.get('pdf_filename') # Not using these in simplified test
print(f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - Attempting to send to: {recipient_email}")
if not recipient_email:
print(f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - Missing recipient_email in JSON payload.")
return jsonify({"status": "error", "message": "Missing required data: recipient_email."}), 400
try:
# Create a simple plain text message
simple_msg = MIMEMultipart()
simple_msg['From'] = SMTP_SENDER_EMAIL
simple_msg['To'] = recipient_email
simple_msg['Subject'] = "EPIC-AMP - Simple Connectivity Test"
simple_body = f"Hello {recipient_email},\n\nThis is a simple plain text email to test SMTP connectivity from the EPIC-AMP Hugging Face Space.\n\nIf you receive this, basic SMTP connection, authentication, and sending are working.\n\nRegards,\nThe EPIC-AMP Test System"
simple_msg.attach(MIMEText(simple_body, 'plain'))
print(f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - Preparing to connect to SMTP server {SMTP_SERVER_HOST}:{SMTP_PORT_INT} with timeout {SMTP_CONNECTION_TIMEOUT}s")
server = None
try:
if SMTP_PORT_INT == 465: # SSL
print(f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - Using SMTP_SSL for port 465.")
server = smtplib.SMTP_SSL(SMTP_SERVER_HOST, SMTP_PORT_INT, timeout=SMTP_CONNECTION_TIMEOUT)
else: # Standard port, try STARTTLS (e.g., for 587)
print(f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - Using standard SMTP for port {SMTP_PORT_INT}.")
server = smtplib.SMTP(SMTP_SERVER_HOST, SMTP_PORT_INT, timeout=SMTP_CONNECTION_TIMEOUT)
server.set_debuglevel(1)
print(f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - Connected. Sending EHLO/HELO...")
server.ehlo()
if SMTP_PORT_INT == 587:
print(f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - Port is 587, attempting STARTTLS...")
server.starttls()
print(f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - STARTTLS successful. Re-sending EHLO...")
server.ehlo()
print(f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - Attempting login with user: {SMTP_SENDER_EMAIL}...")
server.login(SMTP_SENDER_EMAIL, SMTP_SENDER_PASSWORD)
print(f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - Login successful. Sending mail to {recipient_email}...")
server.sendmail(SMTP_SENDER_EMAIL, recipient_email, simple_msg.as_string())
print(f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - Mail sent successfully.")
finally:
if server:
try:
print(f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - Quitting SMTP server connection.")
server.quit()
except Exception as e_quit:
print(f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - Error during server.quit(): {e_quit}")
success_msg = f"SIMPLE TEST: Email successfully sent to {recipient_email}."
print(f"EMAIL_SENDER_API (HF) [{request_id}]: {success_msg}")
return jsonify({"status": "success", "message": success_msg}), 200
# Keep all the specific exception handling blocks
except smtplib.SMTPConnectError as e:
error_msg = f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - SMTP Connect Error. {e}"
print(error_msg); traceback.print_exc()
return jsonify({"status": "error", "message": f"SIMPLIFIED TEST - Could not connect: {e.strerror if hasattr(e, 'strerror') else e}"}), 503
except smtplib.SMTPHeloError as e:
error_msg = f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - SMTP Helo/Ehlo Error. {e}"
print(error_msg); traceback.print_exc()
return jsonify({"status": "error", "message": "SIMPLIFIED TEST - Server HELO/EHLO error."}), 502
except smtplib.SMTPAuthenticationError as e:
error_msg = f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - SMTP Authentication Error. {e}"
print(error_msg); traceback.print_exc()
return jsonify({"status": "error", "message": "SIMPLIFIED TEST - Email server authentication failed."}), 500
except socket.timeout as e:
error_msg = f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - Socket Timeout. {e}"
print(error_msg); traceback.print_exc()
return jsonify({"status": "error", "message": "SIMPLIFIED TEST - Connection timed out."}), 504
except socket.gaierror as e:
error_msg = f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - Address-related error. {e}"
print(error_msg); traceback.print_exc()
return jsonify({"status": "error", "message": "SIMPLIFIED TEST - Could not resolve hostname."}), 503
except OSError as e:
if e.errno == 101:
error_msg = f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - OSError [Errno 101] Network is unreachable. {e}"
print(error_msg); traceback.print_exc()
return jsonify({"status": "error", "message": "SIMPLIFIED TEST - Network is unreachable from API server."}), 503
else:
error_msg = f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - An OS error occurred: {e}"
print(error_msg); traceback.print_exc()
return jsonify({"status": "error", "message": f"SIMPLIFIED TEST - OS Error: {type(e).__name__}"}), 500
except Exception as e:
error_msg = f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - An unexpected error: {e}"
print(error_msg)
traceback.print_exc()
return jsonify({"status": "error", "message": f"SIMPLIFIED TEST - Unexpected API error: {type(e).__name__}"}), 500
# The if __name__ == '__main__': block for local testing:
# On Hugging Face Spaces with Gunicorn, Gunicorn directly imports and runs the 'app' object,
# so this block isn't strictly executed by Gunicorn.
# It's useful if you want to run this script directly with `python email_sender_api.py` for local testing.
# if __name__ == '__main__':
# # For local testing, set your ENV VARS in this terminal session before running
# api_port = int(os.environ.get("PORT", 5002)) # Default to 5002 for local test
# print(f"Starting Email Sender API Service locally on port {api_port}...")
# # ... (rest of the local startup print messages and app.run) ...
# app.run(host='0.0.0.0', port=api_port, debug=True)