feat(infrastructure): Add Limilabs email service and RabbitMQ email queue
- Add LimilabsEmailService for SMTP operations (IEmailService implementation) - Add RabbitMqEmailQueue for production email queue (RabbitMQ-based) - Update InMemoryEmailQueue for improved error handling - Update IEmailQueue interface for RabbitMQ compatibility - Update DependencyInjection.cs: * Switch IEmailService to LimilabsEmailService (Singleton) * Switch IEmailQueue to RabbitMqEmailQueue (Singleton) * Change IEncryptionService to Singleton (thread-safe) * Remove IDmsService registration - Add required NuGet package references to .csproj TODO: Add Limilabs.Mail NuGet package (commercial license required)
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
using DigitalData.EmailProfiler.Application.Common.Dtos;
|
||||
using DigitalData.EmailProfiler.Application.Common.Interfaces;
|
||||
using DigitalData.EmailProfiler.Domain.Exceptions;
|
||||
using Limilabs.Client.SMTP;
|
||||
using Limilabs.Mail;
|
||||
using Limilabs.Mail.Headers;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace DigitalData.EmailProfiler.Infrastructure.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Email service using Limilabs Mail.dll for SMTP operations (send-only).
|
||||
/// Commercial-grade library with superior Exchange support.
|
||||
/// SMTP configuration is injected via IOptions<EmailAccountDto> from appsettings.json.
|
||||
/// </summary>
|
||||
public class LimilabsEmailService(
|
||||
IEncryptionService encryptionService,
|
||||
IOptions<EmailAccountDto> smtpConfig) : IEmailService
|
||||
{
|
||||
private readonly EmailAccountDto _smtpAccount = smtpConfig.Value;
|
||||
|
||||
public async Task SendEmailAsync(string to, string subject, string body, bool isHtml = true, CancellationToken cancellationToken = default)
|
||||
{
|
||||
using var smtp = new Smtp();
|
||||
|
||||
try
|
||||
{
|
||||
await ConnectAndAuthenticateSmtpAsync(smtp);
|
||||
|
||||
var builder = new MailBuilder();
|
||||
builder.From.Add(new MailBox(_smtpAccount.Username));
|
||||
builder.To.Add(new MailBox(to));
|
||||
builder.Subject = subject;
|
||||
|
||||
if (isHtml)
|
||||
{
|
||||
builder.Html = body;
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.Text = body;
|
||||
}
|
||||
|
||||
var mail = builder.Create();
|
||||
|
||||
var result = smtp.SendMessage(mail);
|
||||
|
||||
if (result.Status != SendMessageStatus.Success)
|
||||
{
|
||||
throw new InvalidOperationException($"Failed to send email. Status: {result.Status}");
|
||||
}
|
||||
|
||||
smtp.Close();
|
||||
await Task.CompletedTask; // For async consistency
|
||||
}
|
||||
catch (Limilabs.Client.ServerException ex)
|
||||
{
|
||||
DisconnectSafely(smtp);
|
||||
throw new AuthenticationFailedException("SMTP authentication failed. Check credentials or OAuth2 configuration.", ex);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
DisconnectSafely(smtp);
|
||||
throw new InvalidOperationException("Failed to send email via SMTP server.", ex);
|
||||
}
|
||||
}
|
||||
|
||||
// --- Private Helper Methods ---
|
||||
|
||||
private async Task ConnectAndAuthenticateSmtpAsync(Smtp smtp)
|
||||
{
|
||||
if (_smtpAccount.SmtpUseSsl)
|
||||
{
|
||||
smtp.ConnectSSL(_smtpAccount.SmtpServer, _smtpAccount.SmtpPort);
|
||||
}
|
||||
else
|
||||
{
|
||||
smtp.Connect(_smtpAccount.SmtpServer, _smtpAccount.SmtpPort);
|
||||
}
|
||||
|
||||
if (_smtpAccount.UseOAuth2)
|
||||
{
|
||||
throw new NotSupportedException("OAuth2 is not configured for this SMTP account. UseOAuth2 must be false.");
|
||||
}
|
||||
else
|
||||
{
|
||||
var password = encryptionService.Decrypt(_smtpAccount.EncryptedPassword!);
|
||||
smtp.Login(_smtpAccount.Username, password);
|
||||
}
|
||||
|
||||
await Task.CompletedTask; // For async consistency
|
||||
}
|
||||
|
||||
private static void DisconnectSafely(Smtp smtp)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (smtp.Connected)
|
||||
smtp.Close();
|
||||
}
|
||||
catch { /* Ignore disconnect errors */ }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user