email.py 2.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  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 (
  28. self.provider == "sendgrid"
  29. and not self.sendgrid_api_key
  30. and not os.getenv("SENDGRID_API_KEY")
  31. ):
  32. raise ValueError(
  33. "SendGrid API key is required when using SendGrid provider"
  34. )
  35. logger = logging.getLogger(__name__)
  36. class EmailProvider(Provider, ABC):
  37. def __init__(self, config: EmailConfig):
  38. if not isinstance(config, EmailConfig):
  39. raise ValueError(
  40. "EmailProvider must be initialized with an EmailConfig"
  41. )
  42. super().__init__(config)
  43. self.config: EmailConfig = config
  44. @abstractmethod
  45. async def send_email(
  46. self,
  47. to_email: str,
  48. subject: str,
  49. body: str,
  50. html_body: Optional[str] = None,
  51. *args,
  52. **kwargs,
  53. ) -> None:
  54. pass
  55. @abstractmethod
  56. async def send_verification_email(
  57. self, to_email: str, verification_code: str, *args, **kwargs
  58. ) -> None:
  59. pass
  60. @abstractmethod
  61. async def send_password_reset_email(
  62. self, to_email: str, reset_token: str, *args, **kwargs
  63. ) -> None:
  64. pass