mail config 적용 .
parent
dc50fc6d8c
commit
7185cfbb72
|
|
@ -50,7 +50,14 @@ ANTHROPIC_MODEL=claude-opus-4-8
|
||||||
GOOGLE_API_KEY=
|
GOOGLE_API_KEY=
|
||||||
GOOGLE_MODEL=gemini-2.5-flash
|
GOOGLE_MODEL=gemini-2.5-flash
|
||||||
|
|
||||||
# ── 외부 연동: 이메일 (SMTP, 실연동) ──
|
# ── 외부 연동: 이메일 ──
|
||||||
|
# 1순위: Azure Communication Services(ACS) Email (endpoint + accesskey)
|
||||||
|
# AZURE_ACS_SENDER 는 검증된 MailFrom 주소(예: donotreply@triplepick.o2o.kr)
|
||||||
|
AZURE_ACS_ENDPOINT=https://o2o-common-acs.korea.communication.azure.com/
|
||||||
|
AZURE_ACS_ACCESSKEY=
|
||||||
|
AZURE_ACS_SENDER=donotreply@triplepick.o2o.kr
|
||||||
|
|
||||||
|
# 2순위(폴백): SMTP — ACS 미설정 시 사용
|
||||||
SMTP_HOST=
|
SMTP_HOST=
|
||||||
SMTP_PORT=587
|
SMTP_PORT=587
|
||||||
SMTP_USER=
|
SMTP_USER=
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,13 @@ class Settings(BaseSettings):
|
||||||
google_api_key: str = ""
|
google_api_key: str = ""
|
||||||
google_model: str = "gemini-2.5-flash"
|
google_model: str = "gemini-2.5-flash"
|
||||||
|
|
||||||
# ── 외부 연동: 이메일 (SMTP, 실연동) ─────────────────────
|
# ── 외부 연동: 이메일 ─────────────────────────────────────
|
||||||
|
# 1순위: Azure Communication Services(ACS) Email — endpoint + accesskey.
|
||||||
|
# AZURE_ACS_SENDER 는 검증된 MailFrom 주소(예: donotreply@triplepick.o2o.kr).
|
||||||
|
azure_acs_endpoint: str = ""
|
||||||
|
azure_acs_accesskey: str = ""
|
||||||
|
azure_acs_sender: str = ""
|
||||||
|
# 2순위(폴백): SMTP — ACS 미설정 시 사용.
|
||||||
smtp_host: str = ""
|
smtp_host: str = ""
|
||||||
smtp_port: int = 587
|
smtp_port: int = 587
|
||||||
smtp_user: str = ""
|
smtp_user: str = ""
|
||||||
|
|
@ -97,6 +103,12 @@ class Settings(BaseSettings):
|
||||||
smtp_from: str = "TriplePick <no-reply@triplepick.app>"
|
smtp_from: str = "TriplePick <no-reply@triplepick.app>"
|
||||||
smtp_starttls: bool = True
|
smtp_starttls: bool = True
|
||||||
|
|
||||||
|
@property
|
||||||
|
def acs_configured(self) -> bool:
|
||||||
|
return bool(
|
||||||
|
self.azure_acs_endpoint and self.azure_acs_accesskey and self.azure_acs_sender
|
||||||
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def cors_origin_list(self) -> list[str]:
|
def cors_origin_list(self) -> list[str]:
|
||||||
if self.cors_origins.strip() == "*":
|
if self.cors_origins.strip() == "*":
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
"""결과 이메일 발송 — SMTP 실연동 (aiosmtplib).
|
"""결과 이메일 발송.
|
||||||
|
|
||||||
SMTP 설정이 없으면 EmailUnavailable 을 던진다(조용한 실패 0).
|
1순위: Azure Communication Services(ACS) Email (endpoint + accesskey).
|
||||||
|
2순위(폴백): SMTP (aiosmtplib).
|
||||||
|
둘 다 미설정이면 EmailUnavailable 을 던진다(조용한 실패 0).
|
||||||
경기 종료 후 워커가 구독자(notify=True + email)에게 개인화 결과 메일 발송.
|
경기 종료 후 워커가 구독자(notify=True + email)에게 개인화 결과 메일 발송.
|
||||||
"""
|
"""
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
@ -17,13 +19,23 @@ class EmailUnavailable(RuntimeError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def _ensure_configured() -> None:
|
async def _send_acs(to: str, subject: str, html: str, text: str) -> None:
|
||||||
if not settings.smtp_host:
|
"""Azure Communication Services Email — 키(accesskey) 인증."""
|
||||||
raise EmailUnavailable("SMTP_HOST 미설정")
|
from azure.communication.email.aio import EmailClient
|
||||||
|
|
||||||
|
conn = f"endpoint={settings.azure_acs_endpoint};accesskey={settings.azure_acs_accesskey}"
|
||||||
|
message = {
|
||||||
|
"senderAddress": settings.azure_acs_sender,
|
||||||
|
"recipients": {"to": [{"address": to}]},
|
||||||
|
"content": {"subject": subject, "plainText": text, "html": html},
|
||||||
|
}
|
||||||
|
async with EmailClient.from_connection_string(conn) as client:
|
||||||
|
poller = await client.begin_send(message)
|
||||||
|
await poller.result()
|
||||||
|
log.info("email sent via ACS → %s (%s)", to, subject)
|
||||||
|
|
||||||
|
|
||||||
async def send_email(to: str, subject: str, html: str, text: str) -> None:
|
async def _send_smtp(to: str, subject: str, html: str, text: str) -> None:
|
||||||
_ensure_configured()
|
|
||||||
import aiosmtplib
|
import aiosmtplib
|
||||||
|
|
||||||
msg = EmailMessage()
|
msg = EmailMessage()
|
||||||
|
|
@ -41,7 +53,20 @@ async def send_email(to: str, subject: str, html: str, text: str) -> None:
|
||||||
password=settings.smtp_password or None,
|
password=settings.smtp_password or None,
|
||||||
start_tls=settings.smtp_starttls,
|
start_tls=settings.smtp_starttls,
|
||||||
)
|
)
|
||||||
log.info("email sent → %s (%s)", to, subject)
|
log.info("email sent via SMTP → %s (%s)", to, subject)
|
||||||
|
|
||||||
|
|
||||||
|
async def send_email(to: str, subject: str, html: str, text: str) -> None:
|
||||||
|
if settings.acs_configured:
|
||||||
|
await _send_acs(to, subject, html, text)
|
||||||
|
return
|
||||||
|
if settings.smtp_host:
|
||||||
|
await _send_smtp(to, subject, html, text)
|
||||||
|
return
|
||||||
|
# ACS endpoint/accesskey 만 있고 sender 누락 시 명확히 안내
|
||||||
|
if settings.azure_acs_endpoint and not settings.azure_acs_sender:
|
||||||
|
raise EmailUnavailable("AZURE_ACS_SENDER(검증된 MailFrom 주소) 미설정")
|
||||||
|
raise EmailUnavailable("이메일 미설정 — ACS(endpoint+accesskey+sender) 또는 SMTP 필요")
|
||||||
|
|
||||||
|
|
||||||
def build_result_email(
|
def build_result_email(
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,8 @@ pydantic-settings==2.7.1
|
||||||
email-validator==2.2.0
|
email-validator==2.2.0
|
||||||
apscheduler==3.11.0
|
apscheduler==3.11.0
|
||||||
aiosmtplib==3.0.2
|
aiosmtplib==3.0.2
|
||||||
|
azure-communication-email==1.0.0
|
||||||
|
aiohttp==3.10.11
|
||||||
httpx==0.27.2
|
httpx==0.27.2
|
||||||
anthropic==0.69.0
|
anthropic==0.69.0
|
||||||
openai==1.59.6
|
openai==1.59.6
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue