email.py 2.0 KB

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