@@ -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+
259417def get_whitelist_signup_url () -> str :
260418 """
261419 Get the URL where users can sign up to be whitelisted
0 commit comments