test
This commit is contained in:
54
app/utils.py
54
app/utils.py
@@ -2,6 +2,7 @@ import smtplib
|
||||
import os
|
||||
import json
|
||||
import logging
|
||||
import sys
|
||||
from email.parser import BytesParser
|
||||
from email.policy import default
|
||||
from datetime import datetime
|
||||
@@ -11,37 +12,57 @@ import asyncore
|
||||
import base64
|
||||
from .config import Config
|
||||
|
||||
# 配置日志
|
||||
# 配置日志输出到控制台
|
||||
logging.basicConfig(
|
||||
level=logging.INFO,
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
||||
level=logging.DEBUG, # 改为 DEBUG 级别
|
||||
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||||
handlers=[
|
||||
logging.StreamHandler(sys.stdout)
|
||||
]
|
||||
)
|
||||
logger = logging.getLogger('smtp_server')
|
||||
|
||||
# 初始化 Redis 客户端
|
||||
redis_client = redis.from_url(Config.REDIS_URL)
|
||||
|
||||
|
||||
class CustomSMTPServer(smtpd.SMTPServer):
|
||||
def __init__(self, localaddr, remoteaddr):
|
||||
logger.info(f"Initializing SMTP server on {localaddr}")
|
||||
super().__init__(localaddr, remoteaddr)
|
||||
|
||||
def process_message(self, peer, mailfrom, rcpttos, data):
|
||||
try:
|
||||
logger.debug(f"Connection from peer: {peer}")
|
||||
logger.debug(f"Mail from: {mailfrom}")
|
||||
logger.debug(f"Recipients: {rcpttos}")
|
||||
logger.debug(f"Raw data length: {len(data)} bytes")
|
||||
|
||||
# 记录接收到的邮件基本信息
|
||||
logger.info(f"Received mail from {mailfrom} to {rcpttos}")
|
||||
|
||||
# 验证收件人域名
|
||||
valid_recipients = []
|
||||
for rcpt in rcpttos:
|
||||
if not rcpt.endswith('@nosqli.com'):
|
||||
logger.warning(f"Rejected mail to {rcpt}: invalid domain")
|
||||
continue
|
||||
|
||||
else:
|
||||
valid_recipients.append(rcpt)
|
||||
|
||||
if not valid_recipients:
|
||||
logger.error("No valid recipients found")
|
||||
return
|
||||
|
||||
# 解析邮件
|
||||
logger.debug("Parsing email data...")
|
||||
email = BytesParser(policy=default).parsebytes(data)
|
||||
|
||||
# 获取邮件正文
|
||||
body = self._get_email_body(email)
|
||||
logger.debug(f"Email body length: {len(body) if body else 0}")
|
||||
|
||||
# 处理附件
|
||||
attachments = self._process_attachments(email)
|
||||
logger.debug(f"Found {len(attachments)} attachments")
|
||||
|
||||
# 构建邮件数据
|
||||
timestamp = datetime.now().isoformat()
|
||||
@@ -51,7 +72,7 @@ class CustomSMTPServer(smtpd.SMTPServer):
|
||||
'message_id': message_id,
|
||||
'subject': email.get('subject', ''),
|
||||
'sender': mailfrom,
|
||||
'recipients': json.dumps(rcpttos),
|
||||
'recipients': json.dumps(valid_recipients),
|
||||
'body': body,
|
||||
'timestamp': timestamp,
|
||||
'attachments': json.dumps(attachments),
|
||||
@@ -71,12 +92,18 @@ class CustomSMTPServer(smtpd.SMTPServer):
|
||||
def _get_email_body(self, email):
|
||||
"""提取邮件正文"""
|
||||
try:
|
||||
logger.debug("Extracting email body...")
|
||||
if email.is_multipart():
|
||||
for part in email.walk():
|
||||
if part.get_content_type() == "text/plain":
|
||||
return part.get_payload(decode=True).decode()
|
||||
content = part.get_payload(decode=True).decode()
|
||||
logger.debug(f"Found text/plain content: {len(content)} chars")
|
||||
return content
|
||||
else:
|
||||
return email.get_payload(decode=True).decode()
|
||||
content = email.get_payload(decode=True).decode()
|
||||
logger.debug(f"Found single part content: {len(content)} chars")
|
||||
return content
|
||||
logger.warning("No text content found in email")
|
||||
return ""
|
||||
except Exception as e:
|
||||
logger.error(f"Error extracting email body: {str(e)}")
|
||||
@@ -86,6 +113,7 @@ class CustomSMTPServer(smtpd.SMTPServer):
|
||||
"""处理邮件附件"""
|
||||
attachments = []
|
||||
try:
|
||||
logger.debug("Processing attachments...")
|
||||
if email.is_multipart():
|
||||
for part in email.walk():
|
||||
if part.get_content_maintype() == 'multipart':
|
||||
@@ -95,6 +123,7 @@ class CustomSMTPServer(smtpd.SMTPServer):
|
||||
|
||||
filename = part.get_filename()
|
||||
if filename:
|
||||
logger.debug(f"Processing attachment: {filename}")
|
||||
attachment_data = part.get_payload(decode=True)
|
||||
attachments.append({
|
||||
'filename': filename,
|
||||
@@ -102,6 +131,7 @@ class CustomSMTPServer(smtpd.SMTPServer):
|
||||
'content_type': part.get_content_type(),
|
||||
'size': len(attachment_data)
|
||||
})
|
||||
logger.debug(f"Attachment processed: {filename} ({len(attachment_data)} bytes)")
|
||||
except Exception as e:
|
||||
logger.error(f"Error processing attachments: {str(e)}")
|
||||
return attachments
|
||||
@@ -109,22 +139,27 @@ class CustomSMTPServer(smtpd.SMTPServer):
|
||||
def _store_email(self, email_data):
|
||||
"""存储邮件到 Redis"""
|
||||
try:
|
||||
logger.debug("Storing email in Redis...")
|
||||
# 使用 message_id 作为主键
|
||||
email_key = f"email:{email_data['message_id']}"
|
||||
redis_client.hmset(email_key, email_data)
|
||||
logger.debug(f"Stored email with key: {email_key}")
|
||||
|
||||
# 为每个收件人创建索引
|
||||
recipients = json.loads(email_data['recipients'])
|
||||
for recipient in recipients:
|
||||
recipient_key = f"recipient:{recipient}"
|
||||
redis_client.lpush(recipient_key, email_key)
|
||||
logger.debug(f"Created recipient index: {recipient_key}")
|
||||
|
||||
# 创建时间索引
|
||||
time_key = f"time:{email_data['timestamp']}"
|
||||
redis_client.set(time_key, email_key)
|
||||
logger.debug(f"Created time index: {time_key}")
|
||||
|
||||
# 设置过期时间(可选,这里设置为30天)
|
||||
redis_client.expire(email_key, 30 * 24 * 60 * 60)
|
||||
logger.debug("Set expiration time: 30 days")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error storing email: {str(e)}")
|
||||
@@ -136,6 +171,7 @@ def start_smtp_server(host='0.0.0.0', port=25):
|
||||
try:
|
||||
logger.info(f"Starting SMTP server on {host}:{port}")
|
||||
server = CustomSMTPServer((host, port), None)
|
||||
logger.info("SMTP server initialized, entering main loop...")
|
||||
asyncore.loop()
|
||||
except Exception as e:
|
||||
logger.error(f"Error starting SMTP server: {str(e)}")
|
||||
|
||||
Reference in New Issue
Block a user