email.py 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. import logging
  2. import os
  3. from abc import ABC, abstractmethod
  4. from typing import Optional
  5. from .base import Provider, ProviderConfig
  6. class EmailConfig(ProviderConfig):
  7. smtp_server: Optional[str] = None
  8. smtp_port: Optional[int] = None
  9. smtp_username: Optional[str] = None
  10. smtp_password: Optional[str] = None
  11. from_email: Optional[str] = None
  12. use_tls: Optional[bool] = True
  13. sendgrid_api_key: Optional[str] = None
  14. mailersend_api_key: Optional[str] = None
  15. verify_email_template_id: Optional[str] = None
  16. reset_password_template_id: Optional[str] = None
  17. password_changed_template_id: Optional[str] = None
  18. frontend_url: Optional[str] = None
  19. sender_name: Optional[str] = None
  20. @property
  21. def supported_providers(self) -> list[str]:
  22. return [
  23. "smtp",
  24. "console",
  25. "sendgrid",
  26. "mailersend",
  27. ] # Could add more providers like AWS SES, SendGrid etc.
  28. def validate_config(self) -> None:
  29. if (
  30. self.provider == "sendgrid"
  31. and not self.sendgrid_api_key
  32. and not os.getenv("SENDGRID_API_KEY")
  33. ):
  34. raise ValueError(
  35. "SendGrid API key is required when using SendGrid provider"
  36. )
  37. if (
  38. self.provider == "mailersend"
  39. and not self.mailersend_api_key
  40. and not os.getenv("MAILERSEND_API_KEY")
  41. ):
  42. raise ValueError(
  43. "MailerSend API key is required when using MailerSend provider"
  44. )
  45. logger = logging.getLogger(__name__)
  46. class EmailProvider(Provider, ABC):
  47. def __init__(self, config: EmailConfig):
  48. if not isinstance(config, EmailConfig):
  49. raise ValueError(
  50. "EmailProvider must be initialized with an EmailConfig"
  51. )
  52. super().__init__(config)
  53. self.config: EmailConfig = config
  54. @abstractmethod
  55. async def send_email(
  56. self,
  57. to_email: str,
  58. subject: str,
  59. body: str,
  60. html_body: Optional[str] = None,
  61. *args,
  62. **kwargs,
  63. ) -> None:
  64. pass
  65. @abstractmethod
  66. async def send_verification_email(
  67. self, to_email: str, verification_code: str, *args, **kwargs
  68. ) -> None:
  69. pass
  70. @abstractmethod
  71. async def send_password_reset_email(
  72. self, to_email: str, reset_token: str, *args, **kwargs
  73. ) -> None:
  74. pass
  75. @abstractmethod
  76. async def send_password_changed_email(
  77. self,
  78. to_email: str,
  79. *args,
  80. **kwargs,
  81. ) -> None:
  82. pass