Spaces:
Sleeping
Sleeping
Update email_sender_api.py
Browse files- email_sender_api.py +72 -120
email_sender_api.py
CHANGED
@@ -1,153 +1,105 @@
|
|
1 |
from flask import Flask, request, jsonify
|
2 |
from flask_cors import CORS
|
3 |
-
import smtplib
|
4 |
-
from email.mime.multipart import MIMEMultipart
|
5 |
-
from email.mime.text import MIMEText
|
6 |
-
# from email.mime.base import MIMEBase # Not used in simplified version
|
7 |
-
# from email import encoders # Not used in simplified version
|
8 |
import base64
|
9 |
import os
|
10 |
-
import socket
|
11 |
import traceback
|
12 |
|
13 |
-
#
|
|
|
|
|
|
|
14 |
app = Flask(__name__)
|
15 |
CORS(app)
|
16 |
-
# <<<--------------------------------------------------------->>>
|
17 |
-
|
18 |
-
# Environment variables will be set as Secrets in Hugging Face Space settings
|
19 |
-
SMTP_SERVER_HOST = os.environ.get("SMTP_SERVER_HOST")
|
20 |
-
SMTP_SERVER_PORT = os.environ.get("SMTP_SERVER_PORT")
|
21 |
-
SMTP_SENDER_EMAIL = os.environ.get("SMTP_SENDER_EMAIL")
|
22 |
-
SMTP_SENDER_PASSWORD = os.environ.get("SMTP_SENDER_PASSWORD")
|
23 |
|
24 |
-
|
25 |
-
|
|
|
|
|
26 |
|
27 |
-
@app.route('/send-report-via-email', methods=['POST'])
|
28 |
-
def
|
29 |
request_id = base64.b64encode(os.urandom(6)).decode('utf-8')
|
30 |
-
print(f"EMAIL_SENDER_API (
|
31 |
|
32 |
-
if not
|
33 |
-
error_msg = "EMAIL_SENDER_API (
|
34 |
print(error_msg)
|
35 |
-
return jsonify({"status": "error", "message": "Email
|
36 |
|
37 |
-
|
38 |
-
|
39 |
-
except ValueError:
|
40 |
-
error_msg = f"EMAIL_SENDER_API (HF) [{request_id}]: ERROR - Invalid SMTP_SERVER_PORT: '{SMTP_SERVER_PORT}'."
|
41 |
print(error_msg)
|
42 |
-
return jsonify({"status": "error", "message": "
|
43 |
|
44 |
data = request.json
|
45 |
if not data:
|
46 |
-
print(f"EMAIL_SENDER_API (
|
47 |
return jsonify({"status": "error", "message": "No data received by email API."}), 400
|
48 |
|
49 |
recipient_email = data.get('recipient_email')
|
50 |
-
|
51 |
-
|
|
|
52 |
|
53 |
-
|
|
|
|
|
54 |
|
55 |
-
|
56 |
-
|
57 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
58 |
|
59 |
try:
|
60 |
-
#
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
try:
|
73 |
-
if SMTP_PORT_INT == 465: # SSL
|
74 |
-
print(f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - Using SMTP_SSL for port 465.")
|
75 |
-
server = smtplib.SMTP_SSL(SMTP_SERVER_HOST, SMTP_PORT_INT, timeout=SMTP_CONNECTION_TIMEOUT)
|
76 |
-
else: # Standard port, try STARTTLS (e.g., for 587)
|
77 |
-
print(f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - Using standard SMTP for port {SMTP_PORT_INT}.")
|
78 |
-
server = smtplib.SMTP(SMTP_SERVER_HOST, SMTP_PORT_INT, timeout=SMTP_CONNECTION_TIMEOUT)
|
79 |
-
|
80 |
-
server.set_debuglevel(1)
|
81 |
-
print(f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - Connected. Sending EHLO/HELO...")
|
82 |
-
server.ehlo()
|
83 |
-
|
84 |
-
if SMTP_PORT_INT == 587:
|
85 |
-
print(f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - Port is 587, attempting STARTTLS...")
|
86 |
-
server.starttls()
|
87 |
-
print(f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - STARTTLS successful. Re-sending EHLO...")
|
88 |
-
server.ehlo()
|
89 |
-
|
90 |
-
print(f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - Attempting login with user: {SMTP_SENDER_EMAIL}...")
|
91 |
-
server.login(SMTP_SENDER_EMAIL, SMTP_SENDER_PASSWORD)
|
92 |
-
print(f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - Login successful. Sending mail to {recipient_email}...")
|
93 |
-
server.sendmail(SMTP_SENDER_EMAIL, recipient_email, simple_msg.as_string())
|
94 |
-
print(f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - Mail sent successfully.")
|
95 |
-
|
96 |
-
finally:
|
97 |
-
if server:
|
98 |
-
try:
|
99 |
-
print(f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - Quitting SMTP server connection.")
|
100 |
-
server.quit()
|
101 |
-
except Exception as e_quit:
|
102 |
-
print(f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - Error during server.quit(): {e_quit}")
|
103 |
|
104 |
-
|
105 |
-
|
106 |
-
|
|
|
|
|
|
|
107 |
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
return jsonify({"status": "error", "message": f"SIMPLIFIED TEST - Could not connect: {e.strerror if hasattr(e, 'strerror') else e}"}), 503
|
113 |
-
except smtplib.SMTPHeloError as e:
|
114 |
-
error_msg = f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - SMTP Helo/Ehlo Error. {e}"
|
115 |
-
print(error_msg); traceback.print_exc()
|
116 |
-
return jsonify({"status": "error", "message": "SIMPLIFIED TEST - Server HELO/EHLO error."}), 502
|
117 |
-
except smtplib.SMTPAuthenticationError as e:
|
118 |
-
error_msg = f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - SMTP Authentication Error. {e}"
|
119 |
-
print(error_msg); traceback.print_exc()
|
120 |
-
return jsonify({"status": "error", "message": "SIMPLIFIED TEST - Email server authentication failed."}), 500
|
121 |
-
except socket.timeout as e:
|
122 |
-
error_msg = f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - Socket Timeout. {e}"
|
123 |
-
print(error_msg); traceback.print_exc()
|
124 |
-
return jsonify({"status": "error", "message": "SIMPLIFIED TEST - Connection timed out."}), 504
|
125 |
-
except socket.gaierror as e:
|
126 |
-
error_msg = f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - Address-related error. {e}"
|
127 |
-
print(error_msg); traceback.print_exc()
|
128 |
-
return jsonify({"status": "error", "message": "SIMPLIFIED TEST - Could not resolve hostname."}), 503
|
129 |
-
except OSError as e:
|
130 |
-
if e.errno == 101:
|
131 |
-
error_msg = f"EMAIL_SENDER_API (HF) [{request_id}]: SIMPLIFIED TEST - OSError [Errno 101] Network is unreachable. {e}"
|
132 |
-
print(error_msg); traceback.print_exc()
|
133 |
-
return jsonify({"status": "error", "message": "SIMPLIFIED TEST - Network is unreachable from API server."}), 503
|
134 |
else:
|
135 |
-
error_msg = f"EMAIL_SENDER_API (
|
136 |
-
print(error_msg)
|
137 |
-
return jsonify({"status": "error", "message": f"
|
138 |
-
|
139 |
-
|
|
|
140 |
print(error_msg)
|
141 |
traceback.print_exc()
|
142 |
-
return jsonify({"status": "error", "message": f"
|
143 |
|
144 |
-
# The if __name__ == '__main__': block for local testing:
|
145 |
-
# On Hugging Face Spaces with Gunicorn, Gunicorn directly imports and runs the 'app' object,
|
146 |
-
# so this block isn't strictly executed by Gunicorn.
|
147 |
-
# It's useful if you want to run this script directly with `python email_sender_api.py` for local testing.
|
148 |
# if __name__ == '__main__':
|
149 |
-
# # For local testing, set
|
150 |
-
# api_port = int(os.environ.get("PORT",
|
151 |
-
# print(f"Starting Email Sender API Service locally on port {api_port}...")
|
152 |
-
# # ...
|
153 |
# app.run(host='0.0.0.0', port=api_port, debug=True)
|
|
|
1 |
from flask import Flask, request, jsonify
|
2 |
from flask_cors import CORS
|
|
|
|
|
|
|
|
|
|
|
3 |
import base64
|
4 |
import os
|
|
|
5 |
import traceback
|
6 |
|
7 |
+
# For SendGrid
|
8 |
+
from sendgrid import SendGridAPIClient
|
9 |
+
from sendgrid.helpers.mail import (Mail, Attachment, FileContent, FileName, FileType, Disposition)
|
10 |
+
|
11 |
app = Flask(__name__)
|
12 |
CORS(app)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
13 |
|
14 |
+
# Environment variable for the Email API Key (set as a Secret in HF Space)
|
15 |
+
SENDGRID_API_KEY = os.environ.get("SENDGRID_API_KEY")
|
16 |
+
# Your sender email address - should be verified with SendGrid for best results
|
17 |
+
SENDER_EMAIL_ADDRESS = os.environ.get("SENDER_EMAIL_ADDRESS") # e.g., [email protected] or a domain email
|
18 |
|
19 |
+
@app.route('/send-report-via-email', methods=['POST'])
|
20 |
+
def handle_send_email_http_api():
|
21 |
request_id = base64.b64encode(os.urandom(6)).decode('utf-8')
|
22 |
+
print(f"EMAIL_SENDER_API (HTTP - SendGrid) [{request_id}]: Received request")
|
23 |
|
24 |
+
if not SENDGRID_API_KEY:
|
25 |
+
error_msg = f"EMAIL_SENDER_API (HTTP - SendGrid) [{request_id}]: ERROR - SENDGRID_API_KEY (Secret) not set."
|
26 |
print(error_msg)
|
27 |
+
return jsonify({"status": "error", "message": "Email API key configuration incomplete."}), 500
|
28 |
|
29 |
+
if not SENDER_EMAIL_ADDRESS:
|
30 |
+
error_msg = f"EMAIL_SENDER_API (HTTP - SendGrid) [{request_id}]: ERROR - SENDER_EMAIL_ADDRESS (Secret) not set."
|
|
|
|
|
31 |
print(error_msg)
|
32 |
+
return jsonify({"status": "error", "message": "Sender email address configuration incomplete."}), 500
|
33 |
|
34 |
data = request.json
|
35 |
if not data:
|
36 |
+
print(f"EMAIL_SENDER_API (HTTP - SendGrid) [{request_id}]: No JSON data received.")
|
37 |
return jsonify({"status": "error", "message": "No data received by email API."}), 400
|
38 |
|
39 |
recipient_email = data.get('recipient_email')
|
40 |
+
pdf_base64_data = data.get('pdf_base64_data')
|
41 |
+
pdf_filename = data.get('pdf_filename')
|
42 |
+
print(f"EMAIL_SENDER_API (HTTP - SendGrid) [{request_id}]: Attempting to send to: {recipient_email}, Filename: {pdf_filename}")
|
43 |
|
44 |
+
if not all([recipient_email, pdf_base64_data, pdf_filename]):
|
45 |
+
print(f"EMAIL_SENDER_API (HTTP - SendGrid) [{request_id}]: Missing required data.")
|
46 |
+
return jsonify({"status": "error", "message": "Missing required data in payload."}), 400
|
47 |
|
48 |
+
message = Mail(
|
49 |
+
from_email=SENDER_EMAIL_ADDRESS,
|
50 |
+
to_emails=recipient_email,
|
51 |
+
subject=f"EPIC-AMP Analysis Report: {pdf_filename}",
|
52 |
+
html_content=f"""
|
53 |
+
<p>Dear User,</p>
|
54 |
+
<p>Please find your EPIC-AMP analysis report attached.</p>
|
55 |
+
<p>Filename: {pdf_filename}</p>
|
56 |
+
<p>This report includes details on your sequence analysis.</p>
|
57 |
+
<p>Thank you for using EPIC-AMP!</p>
|
58 |
+
<p>Best regards,<br>The EPIC-AMP Team<br>
|
59 |
+
Bioinformatics and Computational Biology Unit, Zewail City<br>
|
60 |
+
For inquiries: [email protected]</p>
|
61 |
+
"""
|
62 |
+
)
|
63 |
|
64 |
try:
|
65 |
+
# The PDF data is already base64 encoded from the frontend
|
66 |
+
attachment = Attachment()
|
67 |
+
attachment.file_content = FileContent(pdf_base64_data) # SendGrid library expects base64 string
|
68 |
+
attachment.file_name = FileName(pdf_filename)
|
69 |
+
attachment.file_type = FileType('application/pdf')
|
70 |
+
attachment.disposition = Disposition('attachment')
|
71 |
+
# attachment.content_id = ContentId('MyId') # Optional
|
72 |
+
message.attachment = attachment
|
73 |
+
except Exception as e:
|
74 |
+
error_msg = f"EMAIL_SENDER_API (HTTP - SendGrid) [{request_id}]: Error preparing attachment: {e}"
|
75 |
+
print(error_msg); traceback.print_exc()
|
76 |
+
return jsonify({"status": "error", "message": "Error preparing PDF attachment."}), 500
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
77 |
|
78 |
+
try:
|
79 |
+
sg = SendGridAPIClient(SENDGRID_API_KEY)
|
80 |
+
response = sg.send(message)
|
81 |
+
print(f"EMAIL_SENDER_API (HTTP - SendGrid) [{request_id}]: SendGrid Response Status Code: {response.status_code}")
|
82 |
+
# print(f"EMAIL_SENDER_API (HTTP - SendGrid) [{request_id}]: SendGrid Response Body: {response.body}")
|
83 |
+
# print(f"EMAIL_SENDER_API (HTTP - SendGrid) [{request_id}]: SendGrid Response Headers: {response.headers}")
|
84 |
|
85 |
+
if 200 <= response.status_code < 300: # Check for successful status codes
|
86 |
+
success_msg = f"Report successfully sent to {recipient_email} via SendGrid."
|
87 |
+
print(f"EMAIL_SENDER_API (HTTP - SendGrid) [{request_id}]: {success_msg}")
|
88 |
+
return jsonify({"status": "success", "message": success_msg}), 200
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
89 |
else:
|
90 |
+
error_msg = f"EMAIL_SENDER_API (HTTP - SendGrid) [{request_id}]: SendGrid API Error (Status {response.status_code}): {response.body}"
|
91 |
+
print(error_msg)
|
92 |
+
return jsonify({"status": "error", "message": f"Email service provider API error (Status {response.status_code})."}), 502 # Bad Gateway / Upstream error
|
93 |
+
|
94 |
+
except Exception as e:
|
95 |
+
error_msg = f"EMAIL_SENDER_API (HTTP - SendGrid) [{request_id}]: An unexpected error occurred while sending email via SendGrid: {e}"
|
96 |
print(error_msg)
|
97 |
traceback.print_exc()
|
98 |
+
return jsonify({"status": "error", "message": f"An unexpected error occurred on the email API: {type(e).__name__}"}), 500
|
99 |
|
|
|
|
|
|
|
|
|
100 |
# if __name__ == '__main__':
|
101 |
+
# # For local testing, set ENV VARS for SENDGRID_API_KEY and SENDER_EMAIL_ADDRESS
|
102 |
+
# api_port = int(os.environ.get("PORT", 5003)) # Different port for this version locally
|
103 |
+
# print(f"Starting HTTP Email Sender API Service (SendGrid) locally on port {api_port}...")
|
104 |
+
# # ... local startup messages ...
|
105 |
# app.run(host='0.0.0.0', port=api_port, debug=True)
|