Skip to content

Commit 6b39989

Browse files
authored
Merge pull request #147 from mxgoai/knowsletter-branding
Knowsletter branding
2 parents 5eeaa6b + a027d21 commit 6b39989

File tree

3 files changed

+162
-4
lines changed

3 files changed

+162
-4
lines changed

mxgo/api.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,9 +135,9 @@ async def lifespan(_app: FastAPI):
135135

136136
ALLOWED_ORIGINS_DEV = [
137137
"http://localhost",
138-
"http://localhost:8080",
138+
"http://localhost:5173",
139139
"http://127.0.0.1",
140-
"http://127.0.0.1:8080",
140+
"http://127.0.0.1:5173",
141141
]
142142

143143
app.add_middleware(
@@ -1044,7 +1044,7 @@ async def _handle_post_creation_action(
10441044

10451045
logger.info(f"User {user_email} is not whitelisted. Triggering verification.")
10461046
try:
1047-
await whitelist.trigger_automatic_verification(user_email)
1047+
await whitelist.trigger_newsletter_verification(user_email)
10481048
except Exception as e:
10491049
logger.error(f"Error triggering whitelist verification for {user_email}: {e}")
10501050
return False

mxgo/whitelist.py

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,164 @@ async def send_verification_email(email: str, verification_token: str) -> bool:
256256
return True
257257

258258

259+
async def trigger_newsletter_verification(email: str) -> bool:
260+
"""
261+
Automatically trigger email verification for non-whitelisted newsletter users.
262+
263+
This function is a variant of trigger_automatic_verification but uses
264+
Knowsletter branding for the verification email.
265+
266+
Args:
267+
email: The email address to verify.
268+
269+
Returns:
270+
bool: True if the verification process was successfully triggered, False otherwise.
271+
272+
"""
273+
try:
274+
if not supabase:
275+
init_supabase()
276+
277+
verification_token = str(uuid.uuid4())
278+
current_time = datetime.now(timezone.utc).isoformat()
279+
280+
existing_response = supabase.table("whitelisted_emails").select("*").eq("email", email).execute()
281+
282+
if hasattr(existing_response, "data") and len(existing_response.data) > 0:
283+
update_response = (
284+
supabase.table("whitelisted_emails")
285+
.update({"verification_token": verification_token, "verified": False, "updated_at": current_time})
286+
.eq("email", email)
287+
.execute()
288+
)
289+
if not (hasattr(update_response, "data") and len(update_response.data) > 0):
290+
logger.error(f"Failed to update verification token for {email}")
291+
return False
292+
else:
293+
insert_response = (
294+
supabase.table("whitelisted_emails")
295+
.insert(
296+
{
297+
"email": email,
298+
"verified": False,
299+
"verification_token": verification_token,
300+
"created_at": current_time,
301+
"updated_at": current_time,
302+
}
303+
)
304+
.execute()
305+
)
306+
if not (hasattr(insert_response, "data") and len(insert_response.data) > 0):
307+
logger.error(f"Failed to insert verification record for {email}")
308+
return False
309+
310+
verification_sent = await send_newsletter_verification_email(email, verification_token)
311+
312+
if verification_sent:
313+
logger.info(f"Successfully triggered newsletter verification for {email}")
314+
else:
315+
logger.error(f"Failed to send newsletter verification email to {email}")
316+
317+
return verification_sent # noqa: TRY300
318+
except Exception as e:
319+
logger.error(f"Error triggering newsletter verification for {email}: {e}")
320+
return False
321+
322+
323+
async def send_newsletter_verification_email(email: str, verification_token: str) -> bool:
324+
"""
325+
Send verification email using SES email sender with 'Knowsletter' branding.
326+
This is a branded version of the standard send_verification_email function.
327+
"""
328+
try:
329+
# Get the origin URL for verification links
330+
origin = os.getenv("FRONTEND_URL", "https://mxgo.ai")
331+
verification_url = f"{origin}/verify?token={verification_token}"
332+
333+
subject = "Verify your email for Knowsletter"
334+
sender_email = "knowsletter@mxgo.ai"
335+
336+
text_content = f"""Welcome to Knowsletter by MXGo.ai!
337+
338+
To activate your newsletter subscription and start receiving updates, please verify your email address by clicking the link below:
339+
340+
{verification_url}
341+
342+
This verification link will expire in 24 hours for security reasons.
343+
344+
If you didn't request this verification, you can safely ignore this email.
345+
346+
Best regards,
347+
The Knowsletter Team
348+
349+
---
350+
Knowsletter by MXGo.ai - Curated news, powered by AI
351+
https://mxgo.ai"""
352+
353+
html_content = f"""<!DOCTYPE html>
354+
<html>
355+
<head>
356+
<meta charset="utf-8">
357+
<title>Verify your email - Knowsletter by MXGo.ai</title>
358+
<style>
359+
body {{ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; color: #333; }}
360+
.container {{ max-width: 600px; margin: 0 auto; padding: 20px; }}
361+
.header {{ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; text-align: center; padding: 30px 20px; border-radius: 8px 8px 0 0; }}
362+
.content {{ background: #ffffff; padding: 30px; border: 1px solid #e1e5e9; border-top: none; }}
363+
.button {{ display: inline-block; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; text-decoration: none; padding: 12px 30px; border-radius: 6px; font-weight: 600; margin: 20px 0; }}
364+
.footer {{ background: #f8f9fa; color: #6c757d; text-align: center; padding: 20px; border-radius: 0 0 8px 8px; font-size: 14px; }}
365+
.warning {{ background: #fff3cd; border: 1px solid #ffeaa7; color: #856404; padding: 15px; border-radius: 6px; margin: 20px 0; }}
366+
</style>
367+
</head>
368+
<body>
369+
<div class="container">
370+
<div class="header">
371+
<h1 style="margin: 0; font-size: 28px;">Welcome to Knowsletter by MXGo.ai!</h1>
372+
</div>
373+
<div class="content">
374+
<h2 style="color: #333; margin-top: 0;">Verify your email address</h2>
375+
<p>To activate your newsletter subscription and start receiving updates, please verify your email address by clicking the button below:</p>
376+
377+
<div style="text-align: center; margin: 30px 0;">
378+
<a href="{verification_url}" class="button">Verify Email for Knowsletter</a>
379+
</div>
380+
381+
<p>Or copy and paste this link into your browser:</p>
382+
<p style="word-break: break-all; background: #f8f9fa; padding: 10px; border-radius: 4px; font-family: monospace;">{verification_url}</p>
383+
384+
<div class="warning">
385+
<strong>⏰ Important:</strong> This verification link will expire in 24 hours for security reasons.
386+
</div>
387+
388+
<p>If you didn't request this verification, you can safely ignore this email.</p>
389+
390+
<p>Best regards,<br>
391+
<strong>The Knowsletter Team</strong></p>
392+
</div>
393+
<div class="footer">
394+
<p><strong>Knowsletter by MXGo.ai</strong> - Curated news, powered by AI</p>
395+
<p><a href="https://mxgo.ai" style="color: #667eea;">https://mxgo.ai</a></p>
396+
</div>
397+
</div>
398+
</body>
399+
</html>"""
400+
401+
# Initialize email sender and send verification email
402+
email_sender = EmailSender()
403+
response = await email_sender.send_email(
404+
to_address=email, subject=subject, body_text=text_content, body_html=html_content, sender_email=sender_email
405+
)
406+
407+
logger.info(
408+
f"Knowsletter verification email sent successfully to {email}: {response.get('MessageId', 'Unknown')}"
409+
)
410+
return True # noqa: TRY300
411+
412+
except Exception as e:
413+
logger.error(f"Error sending Knowsletter verification email to {email}: {e}")
414+
return False
415+
416+
259417
def get_whitelist_signup_url() -> str:
260418
"""
261419
Get the URL where users can sign up to be whitelisted

tests/test_api.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1313,7 +1313,7 @@ def test_create_newsletter_not_whitelisted(self, mock_dependencies, client_with_
13131313
mock_dependencies["is_whitelisted"].return_value = (False, False)
13141314
jwt_token = generate_test_jwt(email="test@example.com", user_id="test_user_123")
13151315

1316-
with patch("mxgo.api.whitelist.trigger_automatic_verification", new_callable=AsyncMock) as mock_trigger_verify:
1316+
with patch("mxgo.api.whitelist.trigger_newsletter_verification", new_callable=AsyncMock) as mock_trigger_verify:
13171317
response = client_with_patched_redis.post(
13181318
"/create-newsletter",
13191319
headers={"Authorization": f"Bearer {jwt_token}"},

0 commit comments

Comments
 (0)