更新
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
"""邮件模板渲染。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import html
|
||||
import re
|
||||
|
||||
from .system_settings import (
|
||||
APP_NAME,
|
||||
DEFAULT_EMAIL_TEST_BODY,
|
||||
DEFAULT_EMAIL_TEST_HTML,
|
||||
DEFAULT_EMAIL_TEST_SUBJECT,
|
||||
DEFAULT_EMAIL_VERIFY_BODY,
|
||||
DEFAULT_EMAIL_VERIFY_HTML,
|
||||
DEFAULT_EMAIL_VERIFY_SUBJECT,
|
||||
DEFAULT_PASSWORD_RESET_BODY,
|
||||
DEFAULT_PASSWORD_RESET_HTML,
|
||||
DEFAULT_PASSWORD_RESET_SUBJECT,
|
||||
SystemSettingsData,
|
||||
)
|
||||
|
||||
_TEMPLATE_VAR_PATTERN = re.compile(r"\{(\w+)\}")
|
||||
|
||||
|
||||
def render_email_template(template: str, **context: str) -> str:
|
||||
"""安全渲染邮件模板,未知占位符保留原样。"""
|
||||
value = template or ""
|
||||
|
||||
def replacer(match: re.Match) -> str:
|
||||
key = match.group(1)
|
||||
return context.get(key, match.group(0))
|
||||
|
||||
return _TEMPLATE_VAR_PATTERN.sub(replacer, value)
|
||||
|
||||
|
||||
def _text_to_html(text: str) -> str:
|
||||
escaped = html.escape(text or "")
|
||||
return f'<pre style="font-family:sans-serif;white-space:pre-wrap;margin:0;">{escaped}</pre>'
|
||||
|
||||
|
||||
def build_verify_email_content(
|
||||
settings: SystemSettingsData,
|
||||
username: str,
|
||||
to_email: str,
|
||||
link: str,
|
||||
) -> tuple[str, str, str]:
|
||||
context = {
|
||||
"username": username,
|
||||
"email": to_email,
|
||||
"link": link,
|
||||
"app_name": APP_NAME,
|
||||
}
|
||||
subject_tpl = settings.email_verify_subject or DEFAULT_EMAIL_VERIFY_SUBJECT
|
||||
body_tpl = settings.email_verify_body or DEFAULT_EMAIL_VERIFY_BODY
|
||||
html_tpl = settings.email_verify_html or ""
|
||||
|
||||
subject = render_email_template(subject_tpl, **context)
|
||||
text_body = render_email_template(body_tpl, **context)
|
||||
if html_tpl.strip():
|
||||
html_body = render_email_template(html_tpl, **context)
|
||||
else:
|
||||
html_body = _text_to_html(text_body)
|
||||
return subject, text_body, html_body
|
||||
|
||||
|
||||
def build_password_reset_content(
|
||||
settings: SystemSettingsData,
|
||||
username: str,
|
||||
to_email: str,
|
||||
link: str,
|
||||
expire_hours: int,
|
||||
) -> tuple[str, str, str]:
|
||||
context = {
|
||||
"username": username,
|
||||
"email": to_email,
|
||||
"link": link,
|
||||
"app_name": APP_NAME,
|
||||
"expire_hours": str(expire_hours),
|
||||
}
|
||||
subject = render_email_template(DEFAULT_PASSWORD_RESET_SUBJECT, **context)
|
||||
text_body = render_email_template(DEFAULT_PASSWORD_RESET_BODY, **context)
|
||||
html_body = render_email_template(DEFAULT_PASSWORD_RESET_HTML, **context)
|
||||
return subject, text_body, html_body
|
||||
|
||||
|
||||
def build_test_email_content(settings: SystemSettingsData, to_email: str) -> tuple[str, str, str]:
|
||||
context = {
|
||||
"username": "测试用户",
|
||||
"email": to_email,
|
||||
"link": settings.app_url_normalized(),
|
||||
"app_name": APP_NAME,
|
||||
}
|
||||
subject_tpl = settings.email_test_subject or DEFAULT_EMAIL_TEST_SUBJECT
|
||||
body_tpl = settings.email_test_body or DEFAULT_EMAIL_TEST_BODY
|
||||
html_tpl = settings.email_test_html or ""
|
||||
|
||||
subject = render_email_template(subject_tpl, **context)
|
||||
text_body = render_email_template(body_tpl, **context)
|
||||
if html_tpl.strip():
|
||||
html_body = render_email_template(html_tpl, **context)
|
||||
else:
|
||||
html_body = _text_to_html(text_body)
|
||||
return subject, text_body, html_body
|
||||
Reference in New Issue
Block a user