Initial commit: WpywMail 自建邮件系统:.NET 8 原生 SMTP/IMAP 服务端(DKIM 签名、SPF/DKIM/DMARC 入站校验、SQLite 存储、完整账号体系)、Node 服务端、WinUI 3 客户端与 Web 前端
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
using System.Net;
|
||||
using System.Net.Mail;
|
||||
|
||||
namespace WpywMail.Native;
|
||||
|
||||
public sealed class DeliveryQueue
|
||||
{
|
||||
private readonly AppConfig config;
|
||||
private readonly FileStore store;
|
||||
private readonly DirectSmtpDelivery direct;
|
||||
|
||||
public DeliveryQueue(AppConfig config, FileStore store)
|
||||
{
|
||||
this.config = config;
|
||||
this.store = store;
|
||||
direct = new DirectSmtpDelivery(config, store);
|
||||
}
|
||||
|
||||
public async Task RunAsync(CancellationToken token)
|
||||
{
|
||||
AppLog.Info($"[发送] 投递模式:{(config.DeliveryMode.Equals("relay", StringComparison.OrdinalIgnoreCase) ? "SMTP 中继" : "按 MX 直接投递")}");
|
||||
while (!token.IsCancellationRequested)
|
||||
{
|
||||
foreach (var job in store.TakeDueQueue(10))
|
||||
{
|
||||
try
|
||||
{
|
||||
var message = store.GetById(job.MessageId) ?? throw new InvalidOperationException("发送队列中的邮件不存在。");
|
||||
await Deliver(message, job.Recipients, token);
|
||||
store.CompleteQueue(job);
|
||||
AppLog.Info($"[发送] 投递成功:{string.Join(", ", job.Recipients)}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
store.FailQueue(job, ex);
|
||||
AppLog.Error($"[发送队列] {job.MessageId} → {string.Join(", ", job.Recipients)}:{ex.Message}");
|
||||
}
|
||||
}
|
||||
await Task.Delay(TimeSpan.FromSeconds(5), token).ContinueWith(_ => { });
|
||||
}
|
||||
}
|
||||
|
||||
private async Task Deliver(MailMessage message, string[] recipients, CancellationToken token)
|
||||
{
|
||||
if (config.DeliveryMode.Equals("relay", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
await DeliverThroughRelay(message, recipients, token);
|
||||
return;
|
||||
}
|
||||
|
||||
await direct.DeliverAsync(message, recipients, token);
|
||||
}
|
||||
|
||||
private async Task DeliverThroughRelay(MailMessage message, string[] recipients, CancellationToken token)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(config.Relay.Host)) throw new InvalidOperationException("DeliveryMode=relay 时必须配置 Relay.Host。");
|
||||
using var client = new SmtpClient(config.Relay.Host, config.Relay.Port)
|
||||
{
|
||||
EnableSsl = config.Relay.EnableSsl,
|
||||
DeliveryMethod = SmtpDeliveryMethod.Network,
|
||||
Timeout = 60_000
|
||||
};
|
||||
if (!string.IsNullOrWhiteSpace(config.Relay.User)) client.Credentials = new NetworkCredential(config.Relay.User, config.Relay.Password);
|
||||
using var mail = new System.Net.Mail.MailMessage
|
||||
{
|
||||
From = new MailAddress(message.From),
|
||||
Subject = message.Subject,
|
||||
Body = message.Text,
|
||||
BodyEncoding = System.Text.Encoding.UTF8,
|
||||
SubjectEncoding = System.Text.Encoding.UTF8
|
||||
};
|
||||
foreach (var recipient in recipients) mail.To.Add(recipient);
|
||||
await client.SendMailAsync(mail, token);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user