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)