CaiYouHui后端fastapi实现

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. import secrets
  2. from fastapi import BackgroundTasks
  3. from typing import Optional
  4. import smtplib
  5. from email.mime.text import MIMEText
  6. from email.mime.multipart import MIMEMultipart
  7. from jinja2 import Template
  8. import aiosmtplib
  9. from ..config import settings
  10. import os
  11. import string
  12. class EmailService:
  13. def __init__(self):
  14. self.smtp_host = settings.SMTP_HOST
  15. self.smtp_port = settings.SMTP_PORT
  16. self.smtp_user = settings.SMTP_USER
  17. self.smtp_password = settings.SMTP_PASSWORD
  18. self.from_email = settings.EMAILS_FROM_EMAIL
  19. self.from_name = settings.EMAILS_FROM_NAME
  20. async def send_email_async(
  21. self,
  22. email_to: str,
  23. subject: str,
  24. html_content: str,
  25. text_content: Optional[str] = None
  26. ) -> bool:
  27. """异步发送邮件"""
  28. message = MIMEMultipart("alternative")
  29. message["Subject"] = subject
  30. message["From"] = f"{self.from_name} <{self.from_email}>"
  31. message["To"] = email_to
  32. # 添加纯文本版本
  33. if text_content:
  34. part1 = MIMEText(text_content, "plain")
  35. message.attach(part1)
  36. # 添加HTML版本
  37. part2 = MIMEText(html_content, "html")
  38. message.attach(part2)
  39. try:
  40. await aiosmtplib.send(
  41. message,
  42. hostname=self.smtp_host,
  43. port=self.smtp_port,
  44. username=self.smtp_user,
  45. password=self.smtp_password,
  46. use_tls=True,
  47. )
  48. return True
  49. except Exception as e:
  50. print(f"Error sending email: {e}")
  51. return False
  52. def send_email_sync(
  53. self,
  54. email_to: str,
  55. subject: str,
  56. html_content: str,
  57. text_content: Optional[str] = None
  58. ) -> bool:
  59. """同步发送邮件"""
  60. message = MIMEMultipart("alternative")
  61. message["Subject"] = subject
  62. message["From"] = f"{self.from_name} <{self.from_email}>"
  63. message["To"] = email_to
  64. if text_content:
  65. part1 = MIMEText(text_content, "plain")
  66. message.attach(part1)
  67. part2 = MIMEText(html_content, "html")
  68. message.attach(part2)
  69. try:
  70. with smtplib.SMTP(self.smtp_host, self.smtp_port) as server:
  71. server.starttls()
  72. server.login(self.smtp_user, self.smtp_password)
  73. server.send_message(message)
  74. return True
  75. except Exception as e:
  76. print(f"Error sending email: {e}")
  77. return False
  78. async def send_verification_email(
  79. self,
  80. email_to: str,
  81. username: str,
  82. verification_url: str,
  83. verification_code: Optional[str] = None
  84. ) -> bool:
  85. """发送验证邮件"""
  86. # 加载模板
  87. template_path = os.path.join("templates", "email", "verify_email.html")
  88. with open(template_path, "r") as f:
  89. template_str = f.read()
  90. template = Template(template_str)
  91. html_content = template.render(
  92. username=username,
  93. verification_url=verification_url,
  94. verification_code=verification_code,
  95. frontend_url=settings.FRONTEND_URL
  96. )
  97. subject = "Verify Your Email Address"
  98. text_content = f"Hello {username},\n\nPlease verify your email by clicking: {verification_url}"
  99. if verification_code:
  100. text_content += f"\n\nOr use this verification code: {verification_code}"
  101. return await self.send_email_async(email_to, subject, html_content, text_content)
  102. async def send_password_reset_email(
  103. self,
  104. email_to: str,
  105. username: str,
  106. reset_url: str
  107. ) -> bool:
  108. """发送密码重置邮件"""
  109. template_path = os.path.join("templates", "email", "reset_password.html")
  110. with open(template_path, "r") as f:
  111. template_str = f.read()
  112. template = Template(template_str)
  113. html_content = template.render(
  114. username=username,
  115. reset_url=reset_url,
  116. frontend_url=settings.FRONTEND_URL
  117. )
  118. subject = "Password Reset Request"
  119. text_content = f"Hello {username},\n\nClick here to reset your password: {reset_url}"
  120. return await self.send_email_async(email_to, subject, html_content, text_content)
  121. async def send_welcome_email(
  122. self,
  123. email_to: str,
  124. username: str
  125. ) -> bool:
  126. """发送欢迎邮件"""
  127. template_path = os.path.join("templates", "email", "welcome.html")
  128. with open(template_path, "r") as f:
  129. template_str = f.read()
  130. template = Template(template_str)
  131. html_content = template.render(
  132. username=username,
  133. frontend_url=settings.FRONTEND_URL
  134. )
  135. subject = "Welcome to Our Platform!"
  136. text_content = f"Welcome {username}! We're glad to have you on board."
  137. return await self.send_email_async(email_to, subject, html_content, text_content)
  138. def generate_random_string(self, length: int = 6) -> str:
  139. """生成随机数字加字母字符串"""
  140. name = string.digits + string.ascii_letters
  141. return ''.join(secrets.choice(name) for _ in range(length))
  142. email_service = EmailService()