""" MailClient — 呼叫 Docker Mailserver Admin API (http://10.1.0.254:8080) 管理 mail domain 和 mailbox 的建立/查詢。 建立 domain 前必須驗證 MX DNS 設定(對 active 租戶)。 """ import logging from typing import Optional import httpx import dns.resolver from app.core.config import settings logger = logging.getLogger(__name__) TIMEOUT = 10.0 class MailClient: def __init__(self): self._base = settings.MAIL_ADMIN_API_URL.rstrip("/") self._headers = {"X-API-Key": settings.MAIL_ADMIN_API_KEY} def check_mx_dns(self, domain: str) -> bool: """驗證 domain 的 MX record 是否指向正確的 mail server""" try: answers = dns.resolver.resolve(domain, "MX") for rdata in answers: if settings.MAIL_MX_HOST in str(rdata.exchange).rstrip("."): return True return False except Exception as e: logger.warning(f"MX DNS check failed for {domain}: {e}") return False def domain_exists(self, domain: str) -> bool: try: resp = httpx.get( f"{self._base}/api/v1/domains/{domain}", headers=self._headers, timeout=TIMEOUT, ) return resp.status_code == 200 except Exception: return False def create_domain(self, domain: str) -> bool: try: resp = httpx.post( f"{self._base}/api/v1/domains", json={"domain": domain}, headers=self._headers, timeout=TIMEOUT, ) return resp.status_code in (200, 201, 204) except Exception as e: logger.error(f"create_domain({domain}) failed: {e}") return False def mailbox_exists(self, email: str) -> bool: try: resp = httpx.get( f"{self._base}/api/v1/mailboxes/{email}", headers=self._headers, timeout=TIMEOUT, ) return resp.status_code == 200 except Exception: return False def create_mailbox(self, email: str, password: Optional[str], quota_gb: int = 20) -> bool: try: resp = httpx.post( f"{self._base}/api/v1/mailboxes", json={"email": email, "password": password or "", "quota": quota_gb}, headers=self._headers, timeout=TIMEOUT, ) return resp.status_code in (200, 201, 204) except Exception as e: logger.error(f"create_mailbox({email}) failed: {e}") return False