mrs83 commited on
Commit
99be2dc
·
1 Parent(s): 3e25ded

Add support for Mailjet API

Browse files
blossomtune_gradio/config.py CHANGED
@@ -4,8 +4,8 @@ from blossomtune_gradio import util
4
 
5
 
6
  # HF Space ID
7
- SPACE_ID = os.getenv("SPACE_ID")
8
- SPACE_OWNER = SPACE_ID.split("/")[0] if SPACE_ID else None
9
 
10
  # Use persistent storage if available
11
  DB_PATH = "/data/federation.db" if os.path.isdir("/data") else "federation.db"
@@ -16,3 +16,4 @@ SMTP_PORT = int(os.getenv("SMTP_PORT", "1025"))
16
  SMTP_REQUIRE_TLS = util.strtobool(os.getenv("SMTP_REQUIRE_TLS", "false"))
17
  SMTP_USER = os.getenv("SMTP_USER", "")
18
  SMTP_PASSWORD = os.getenv("SMTP_PASSWORD", "")
 
 
4
 
5
 
6
  # HF Space ID
7
+ SPACE_ID = os.getenv("SPACE_ID", "ethicalabs/BlossomTune-Orchestrator")
8
+ SPACE_OWNER = os.getenv("SPACE_OWNER", SPACE_ID.split("/")[0] if SPACE_ID else None)
9
 
10
  # Use persistent storage if available
11
  DB_PATH = "/data/federation.db" if os.path.isdir("/data") else "federation.db"
 
16
  SMTP_REQUIRE_TLS = util.strtobool(os.getenv("SMTP_REQUIRE_TLS", "false"))
17
  SMTP_USER = os.getenv("SMTP_USER", "")
18
  SMTP_PASSWORD = os.getenv("SMTP_PASSWORD", "")
19
+ EMAIL_PROVIDER = os.getenv("EMAIL_PROVIDER", "smtp")
blossomtune_gradio/mail.py CHANGED
@@ -1,17 +1,137 @@
1
  import smtplib
2
-
3
  from email.mime.text import MIMEText
4
-
5
 
6
  from blossomtune_gradio.logs import log
7
  from blossomtune_gradio import config as cfg
8
 
9
 
10
- def send_activation_email(recipient_email, activation_code):
11
- """Sends the activation code to the user.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
 
13
- Note: This function assumes that SMTP server settings (SMTP_SERVER, SMTP_PORT,
14
- SMTP_SENDER) are configured in the `blossomtune_gradio.config` module.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  """
16
  subject = "Your BlossomTune Activation Code"
17
  body = (
@@ -20,25 +140,14 @@ def send_activation_email(recipient_email, activation_code):
20
  f"{activation_code}\n\n"
21
  "Thank you!"
22
  )
23
- msg = MIMEText(body)
24
- msg["Subject"] = subject
25
- msg["From"] = cfg.SMTP_SENDER
26
- msg["To"] = recipient_email
27
-
28
- try:
29
- # For local testing with a basic SMTP server like MailHog
30
- with smtplib.SMTP(cfg.SMTP_SERVER, cfg.SMTP_PORT) as server:
31
- # If your SMTP server requires TLS or authentication, add:
32
- if cfg.SMTP_REQUIRE_TLS:
33
- server.starttls()
34
- server.login(cfg.SMTP_USER, cfg.SMTP_PASSWORD)
35
- server.send_message(msg)
36
- log(f"[Email] Activation code sent to {recipient_email}")
37
- except Exception as e:
38
- # FIX: return False if mail send fails.
39
- log(f"[Email] CRITICAL ERROR sending to {recipient_email}: {e}")
40
  return (
41
  False,
42
- "There was an error sending the activation email. Please contact an administrator.",
43
  )
 
44
  return True, ""
 
1
  import smtplib
2
+ from abc import ABC, abstractmethod
3
  from email.mime.text import MIMEText
4
+ import requests
5
 
6
  from blossomtune_gradio.logs import log
7
  from blossomtune_gradio import config as cfg
8
 
9
 
10
+ class EmailSender(ABC):
11
+ """
12
+ Abstract Base Class for email sending.
13
+
14
+ This class defines the interface for all email sending implementations,
15
+ ensuring they have a consistent `send_email` method.
16
+ """
17
+
18
+ @abstractmethod
19
+ def send_email(
20
+ self, recipient_email: str, subject: str, body: str
21
+ ) -> tuple[bool, str]:
22
+ """
23
+ Sends an email to the specified recipient.
24
+
25
+ Args:
26
+ recipient_email: The email address of the recipient.
27
+ subject: The subject line of the email.
28
+ body: The body content of the email.
29
+
30
+ Returns:
31
+ A tuple containing a boolean success status and an error message string.
32
+ The error message is empty if the email was sent successfully.
33
+ """
34
+ pass
35
+
36
 
37
+ class SMTPMailSender(EmailSender):
38
+ """
39
+ Concrete implementation of EmailSender using standard SMTP.
40
+ """
41
+
42
+ def send_email(
43
+ self, recipient_email: str, subject: str, body: str
44
+ ) -> tuple[bool, str]:
45
+ """
46
+ Sends an email using the SMTP server configured in `cfg`.
47
+ """
48
+ msg = MIMEText(body)
49
+ msg["Subject"] = subject
50
+ msg["From"] = cfg.SMTP_SENDER
51
+ msg["To"] = recipient_email
52
+
53
+ try:
54
+ with smtplib.SMTP(cfg.SMTP_SERVER, cfg.SMTP_PORT) as server:
55
+ if cfg.SMTP_REQUIRE_TLS:
56
+ server.starttls()
57
+ server.login(cfg.SMTP_USER, cfg.SMTP_PASSWORD)
58
+ server.send_message(msg)
59
+ log(f"[Email] SMTP email sent to {recipient_email}")
60
+ return True, ""
61
+ except Exception as e:
62
+ log(f"[Email] CRITICAL ERROR sending to {recipient_email} via SMTP: {e}")
63
+ return False, f"Error sending email via SMTP: {e}"
64
+
65
+
66
+ class MailjetSender(EmailSender):
67
+ """
68
+ Concrete implementation of EmailSender using the Mailjet API.
69
+ """
70
+
71
+ def send_email(
72
+ self, recipient_email: str, subject: str, body: str
73
+ ) -> tuple[bool, str]:
74
+ """
75
+ Sends an email using the Mailjet transactional API v3.1.
76
+ """
77
+ if not all([hasattr(cfg, "SMTP_USER"), hasattr(cfg, "SMTP_PASSWORD")]):
78
+ error_msg = "Mailjet API keys are not configured."
79
+ log(f"[Email] {error_msg}")
80
+ return False, error_msg
81
+
82
+ api_key = cfg.SMTP_USER
83
+ api_secret = cfg.SMTP_PASSWORD
84
+
85
+ url = "https://api.mailjet.com/v3.1/send"
86
+ data = {
87
+ "Messages": [
88
+ {
89
+ "From": {
90
+ "Email": cfg.SMTP_SENDER,
91
+ "Name": cfg.SMTP_SENDER.split("@")[0],
92
+ },
93
+ "To": [{"Email": recipient_email}],
94
+ "Subject": subject,
95
+ "TextPart": body,
96
+ }
97
+ ]
98
+ }
99
+
100
+ try:
101
+ response = requests.post(url, auth=(api_key, api_secret), json=data)
102
+ response.raise_for_status()
103
+ log(
104
+ f"[Email] Mailjet email sent to {recipient_email}. Status: {response.status_code}"
105
+ )
106
+ return True, ""
107
+ except requests.exceptions.RequestException as e:
108
+ error_msg = f"Error sending email via Mailjet API: {e}. Response: {e.response.text if e.response else 'No response'}"
109
+ log(f"[Email] CRITICAL ERROR: {error_msg}")
110
+ return False, error_msg
111
+
112
+
113
+ def get_email_sender() -> EmailSender:
114
+ """
115
+ Factory function to get the correct email sender implementation.
116
+
117
+ This function reads the `EMAIL_PROVIDER` variable from the `config` module
118
+ and returns an appropriate EmailSender instance. Defaults to SMTP.
119
+ """
120
+ provider = getattr(cfg, "EMAIL_PROVIDER", "smtp")
121
+ if provider == "mailjet":
122
+ return MailjetSender()
123
+ # Default to SMTP if the provider is not Mailjet or is missing.
124
+ return SMTPMailSender()
125
+
126
+
127
+ def send_activation_email(
128
+ recipient_email: str, activation_code: str
129
+ ) -> tuple[bool, str]:
130
+ """
131
+ Sends the activation code to the user using the configured email provider.
132
+
133
+ This function uses the factory to get the correct sender and abstracts the
134
+ implementation details.
135
  """
136
  subject = "Your BlossomTune Activation Code"
137
  body = (
 
140
  f"{activation_code}\n\n"
141
  "Thank you!"
142
  )
143
+
144
+ sender = get_email_sender()
145
+ success, error_message = sender.send_email(recipient_email, subject, body)
146
+
147
+ if not success:
 
 
 
 
 
 
 
 
 
 
 
 
148
  return (
149
  False,
150
+ f"There was an error sending the activation email. Please contact an administrator. Original error: {error_message}",
151
  )
152
+
153
  return True, ""