mirror of
https://github.com/fastapiadmin/FastapiAdmin.git
synced 2026-09-22 13:05:18 +00:00
这是一次综合性的重构更新,包含以下主要变更: 1. 升级Python版本到3.12,更新依赖配置 2. 替换旧的.j2模板为.jinja2格式,新增代码生成模板 3. 重构权限过滤策略,更新权限枚举与模型配置 4. 移除Prefect依赖,替换为自研拓扑并行执行引擎 5. 重构认证与上下文管理,拆分租户/请求上下文 6. 简化响应模型、CRUD与服务层代码 7. 清理废弃的支付网关模块,重构订单定时任务 8. 更新在线用户、监控等模块的接口与路由 9. 优化邮件模板与工具类,新增邮件模板文件 10. 修复数据库会话配置与类型提示
120 lines
3.0 KiB
Python
120 lines
3.0 KiB
Python
"""
|
||
email_util.py — 邮件发送工具
|
||
|
||
职责:
|
||
- 渲染 Jinja2 模板(文件 & 内联字符串)
|
||
- 通过 fastapi-mail 发送邮件
|
||
|
||
使用方:EmailService.send_email_service()
|
||
"""
|
||
|
||
from pathlib import Path
|
||
from typing import Any
|
||
|
||
from fastapi_mail import ConnectionConfig, FastMail, MessageSchema, MessageType
|
||
from jinja2 import Environment, FileSystemLoader, StrictUndefined
|
||
|
||
_TEMPLATES_DIR = Path(__file__).resolve().parent.parent.parent / "templates"
|
||
_file_env: Environment | None = None
|
||
|
||
|
||
def _get_file_env() -> Environment:
|
||
"""获取文件模板 Jinja2 环境(懒加载)。"""
|
||
global _file_env
|
||
if _file_env is None:
|
||
_file_env = Environment(
|
||
loader=FileSystemLoader(str(_TEMPLATES_DIR)),
|
||
undefined=StrictUndefined,
|
||
autoescape=True,
|
||
)
|
||
return _file_env
|
||
|
||
|
||
def render_template_file(file_path: str, variables: dict[str, Any]) -> str:
|
||
"""
|
||
从 templates/ 目录加载 Jinja2 文件模板并渲染。
|
||
|
||
参数:
|
||
- file_path: 相对于 templates/ 的模板路径,如 "emails/welcome.html"
|
||
- variables: 变量字典
|
||
|
||
返回:渲染后的字符串
|
||
"""
|
||
env = _get_file_env()
|
||
tmpl = env.get_template(file_path)
|
||
return tmpl.render(**variables)
|
||
|
||
|
||
def render_template(template_str: str, variables: dict[str, Any]) -> str:
|
||
"""
|
||
使用 Jinja2 渲染邮件模板。
|
||
|
||
参数:
|
||
- template_str: Jinja2 模板字符串
|
||
- variables: 变量字典
|
||
|
||
返回:渲染后的字符串
|
||
|
||
异常:TemplateError — 模板语法错误或变量缺失
|
||
"""
|
||
env = Environment(undefined=StrictUndefined, autoescape=True)
|
||
tmpl = env.from_string(template_str)
|
||
return tmpl.render(**variables)
|
||
|
||
|
||
async def send_email(
|
||
*,
|
||
smtp_host: str,
|
||
smtp_port: int,
|
||
smtp_user: str,
|
||
smtp_password: str,
|
||
use_tls: bool,
|
||
from_name: str,
|
||
to_email: str,
|
||
to_name: str | None,
|
||
subject: str,
|
||
body_html: str,
|
||
body_text: str | None = None,
|
||
timeout: int = 30,
|
||
) -> None:
|
||
"""
|
||
异步发送邮件(通过 fastapi-mail)。
|
||
|
||
参数均为关键字参数。
|
||
|
||
异常:
|
||
- SMTPException — SMTP 协议错误
|
||
- TimeoutError — 连接/发送超时
|
||
"""
|
||
# 构建 fastapi-mail 连接配置
|
||
# 端口 465 = 隐式 SSL,端口 587 = STARTTLS
|
||
is_ssl = smtp_port == 465
|
||
|
||
conf = ConnectionConfig(
|
||
MAIL_USERNAME=smtp_user,
|
||
MAIL_PASSWORD=smtp_password,
|
||
MAIL_FROM=smtp_user,
|
||
MAIL_FROM_NAME=from_name,
|
||
MAIL_SERVER=smtp_host,
|
||
MAIL_PORT=smtp_port,
|
||
MAIL_STARTTLS=not is_ssl and use_tls,
|
||
MAIL_SSL_TLS=is_ssl,
|
||
USE_CREDENTIALS=True,
|
||
VALIDATE_CERTS=False,
|
||
MAIL_TIMEOUT=timeout,
|
||
)
|
||
|
||
# 收件人格式:支持 "姓名 <邮箱>"(若有 to_name)
|
||
recipient = f"{to_name} <{to_email}>" if to_name else to_email
|
||
|
||
# 构建邮件消息
|
||
message = MessageSchema(
|
||
subject=subject,
|
||
recipients=[recipient],
|
||
body=body_html,
|
||
subtype=MessageType.html,
|
||
)
|
||
|
||
fm = FastMail(conf)
|
||
await fm.send_message(message)
|