96 lines
3.2 KiB
Python
96 lines
3.2 KiB
Python
import logging
|
|
from email.mime.multipart import MIMEMultipart
|
|
from email.mime.text import MIMEText
|
|
|
|
import aiosmtplib
|
|
|
|
from app.config import settings
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
async def send_email(to: str, subject: str, html_body: str) -> bool:
|
|
"""Send HTML email via SMTP. Returns True on success."""
|
|
if not settings.smtp_host:
|
|
logger.info(f"SMTP not configured, skipping email to {to}: {subject}")
|
|
return False
|
|
|
|
msg = MIMEMultipart("alternative")
|
|
msg["From"] = f"{settings.smtp_from_name} <{settings.smtp_from_email}>"
|
|
msg["To"] = to
|
|
msg["Subject"] = subject
|
|
msg.attach(MIMEText(html_body, "html"))
|
|
|
|
try:
|
|
await aiosmtplib.send(
|
|
msg,
|
|
hostname=settings.smtp_host,
|
|
port=settings.smtp_port,
|
|
username=settings.smtp_user,
|
|
password=settings.smtp_password,
|
|
use_tls=True,
|
|
)
|
|
return True
|
|
except Exception as e:
|
|
logger.error(f"Failed to send email to {to}: {e}")
|
|
return False
|
|
|
|
|
|
async def send_registration_notification(
|
|
admin_email: str, student_name: str, class_name: str
|
|
):
|
|
html = f"""
|
|
<h2>New Registration Pending Approval</h2>
|
|
<p><strong>{student_name}</strong> has registered for <strong>{class_name}</strong>.</p>
|
|
<p>Please log in to HKU ICB to review and approve.</p>
|
|
"""
|
|
await send_email(admin_email, "HKU ICB: New Registration", html)
|
|
|
|
|
|
async def send_approval_notification(student_email: str, approved: bool):
|
|
status_text = "approved" if approved else "rejected"
|
|
html = f"""
|
|
<h2>Registration {status_text.capitalize()}</h2>
|
|
<p>Your registration has been <strong>{status_text}</strong>.</p>
|
|
{"<p>You can now log in to HKU ICB.</p>" if approved else ""}
|
|
"""
|
|
await send_email(
|
|
student_email, f"HKU ICB: Registration {status_text.capitalize()}", html
|
|
)
|
|
|
|
|
|
async def send_class_notification_email(
|
|
emails: list[str],
|
|
subject: str,
|
|
title: str,
|
|
body: str,
|
|
action_url: str | None = None,
|
|
):
|
|
"""Send a styled notification email to class members."""
|
|
action_html = ""
|
|
if action_url:
|
|
action_html = f"""
|
|
<div style="margin-top: 20px;">
|
|
<a href="{action_url}" style="display:inline-block;padding:10px 24px;background:#111827;color:#fff;border-radius:6px;text-decoration:none;font-size:14px;">
|
|
查看详情
|
|
</a>
|
|
</div>
|
|
"""
|
|
html = f"""
|
|
<div style="max-width:600px;margin:0 auto;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;color:#1f2937;">
|
|
<div style="padding:24px 0;border-bottom:2px solid #111827;">
|
|
<h1 style="margin:0;font-size:20px;color:#111827;">HKU ICB</h1>
|
|
</div>
|
|
<div style="padding:24px 0;">
|
|
<h2 style="margin:0 0 12px;font-size:18px;">{title}</h2>
|
|
<div style="font-size:14px;line-height:1.6;color:#4b5563;">{body}</div>
|
|
{action_html}
|
|
</div>
|
|
<div style="padding:16px 0;border-top:1px solid #e5e7eb;font-size:12px;color:#9ca3af;">
|
|
此邮件由系统自动发送,请勿直接回复。
|
|
</div>
|
|
</div>
|
|
"""
|
|
for email in emails:
|
|
await send_email(email, subject, html)
|