mirror of
https://github.com/fastapi-practices/fastapi_best_architecture.git
synced 2026-03-13 09:31:31 +08:00
* Update the ruff rules and format the code * Update the per-file-ignores * Update the ci * Update rules * Fix codes * Fix pagination * Update rules
114 lines
3.8 KiB
Python
114 lines
3.8 KiB
Python
from email.mime.multipart import MIMEMultipart
|
|
from email.mime.text import MIMEText
|
|
|
|
from aiosmtplib import SMTP
|
|
from anyio import open_file
|
|
from jinja2 import Template
|
|
from sqlalchemy import inspect
|
|
from sqlalchemy.ext.asyncio import AsyncConnection, AsyncSession
|
|
|
|
from backend.common.enums import StatusType
|
|
from backend.common.exception import errors
|
|
from backend.common.log import log
|
|
from backend.core.conf import settings
|
|
from backend.core.path_conf import PLUGIN_DIR
|
|
from backend.database.db import async_engine
|
|
from backend.plugin.config.crud.crud_config import config_dao
|
|
from backend.utils.serializers import select_list_serialize
|
|
from backend.utils.timezone import timezone
|
|
|
|
|
|
async def render_message(subject: str, from_header: str, content: str | dict, template: str | None) -> bytes:
|
|
"""
|
|
渲染邮件内容
|
|
|
|
:param subject: 邮件内容主题
|
|
:param from_header: 邮件来源
|
|
:param content: 邮件内容
|
|
:param template: 邮件内容模板
|
|
:return:
|
|
"""
|
|
message = MIMEMultipart()
|
|
message['Subject'] = subject
|
|
message['From'] = from_header
|
|
message['date'] = timezone.now().strftime('%a, %d %b %Y %H:%M:%S %z')
|
|
|
|
if template:
|
|
async with await open_file(PLUGIN_DIR / 'email' / 'templates' / template, encoding='utf-8') as f:
|
|
html = Template(await f.read(), enable_async=True)
|
|
mail_body = MIMEText(await html.render_async(**content), 'html', 'utf-8')
|
|
else:
|
|
mail_body = MIMEText(content, 'plain', 'utf-8')
|
|
|
|
message.attach(mail_body)
|
|
|
|
return message.as_bytes()
|
|
|
|
|
|
async def send_email(
|
|
db: AsyncSession,
|
|
recipients: str | list[str],
|
|
subject: str,
|
|
content: str | dict,
|
|
template: str | None = None,
|
|
) -> None:
|
|
"""
|
|
发送电子邮件
|
|
|
|
:param db: 数据库会话
|
|
:param recipients: 邮件接收者
|
|
:param subject: 邮件内容主题
|
|
:param content: 邮件内容
|
|
:param template: 邮件内容模板
|
|
:return:
|
|
"""
|
|
# 本地配置
|
|
email_host = settings.EMAIL_HOST
|
|
email_port = settings.EMAIL_PORT
|
|
email_ssl = settings.EMAIL_SSL
|
|
email_username = settings.EMAIL_USERNAME
|
|
email_password = settings.EMAIL_PASSWORD
|
|
|
|
# 动态配置
|
|
dynamic_config = None
|
|
|
|
def get_config_table(conn: AsyncConnection) -> bool:
|
|
inspector = inspect(conn)
|
|
return inspector.has_table('sys_config', schema=None)
|
|
|
|
async with async_engine.begin() as coon:
|
|
exists = await coon.run_sync(get_config_table)
|
|
if exists:
|
|
dynamic_config = await config_dao.get_all(db, 'EMAIL')
|
|
|
|
if dynamic_config:
|
|
status_key = 'EMAIL_STATUS'
|
|
host_key = 'EMAIL_HOST'
|
|
port_key = 'EMAIL_PORT'
|
|
ssl_key = 'EMAIL_SSL'
|
|
username_key = 'EMAIL_USERNAME'
|
|
password_key = 'EMAIL_PASSWORD'
|
|
|
|
configs = {d['key']: d['value'] for d in select_list_serialize(dynamic_config)}
|
|
if configs.get(status_key):
|
|
if len(dynamic_config) < 6:
|
|
raise errors.NotFoundError(msg='缺少邮件动态配置,请检查系统参数配置-邮件配置')
|
|
email_host = configs.get(host_key)
|
|
email_port = int(configs.get(port_key, 0))
|
|
email_ssl = configs.get(ssl_key, '') == str(StatusType.enable.value)
|
|
email_username = configs.get(username_key)
|
|
email_password = configs.get(password_key)
|
|
|
|
try:
|
|
message = await render_message(subject, email_username, content, template)
|
|
smtp_client = SMTP(
|
|
hostname=email_host,
|
|
port=email_port,
|
|
use_tls=email_ssl,
|
|
)
|
|
async with smtp_client:
|
|
await smtp_client.login(email_username, email_password)
|
|
await smtp_client.sendmail(email_username, recipients, message)
|
|
except Exception as e:
|
|
log.error(f'电子邮件发送失败:{e}')
|