Implemented multiple email providers
This commit is contained in:
parent
b86d6968d6
commit
54af1c88b6
9
Wave/Data/EmailConfiguration.cs
Normal file
9
Wave/Data/EmailConfiguration.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
namespace Wave.Data;
|
||||
|
||||
public class EmailConfiguration {
|
||||
public required string SenderEmail { get; init; }
|
||||
public required string SenderName { get; init; }
|
||||
public required string? ServiceEmail { get; init; }
|
||||
|
||||
public Dictionary<string, SmtpConfiguration> Smtp { get; init; } = new(StringComparer.InvariantCultureIgnoreCase);
|
||||
}
|
|
@ -5,8 +5,5 @@ public class SmtpConfiguration {
|
|||
public required int Port { get; init; }
|
||||
public required string Username { get; init; }
|
||||
public required string Password { get; init; }
|
||||
public required string SenderEmail { get; init; }
|
||||
public required string SenderName { get; init; }
|
||||
public required string ServiceEmail { get; init; }
|
||||
public bool Ssl { get; init; } = true;
|
||||
}
|
|
@ -121,16 +121,40 @@
|
|||
builder.Services.AddCascadingValue("TitlePrefix",
|
||||
sf => (sf.GetService<IOptions<Customization>>()?.Value.AppName ?? "Wave") + " - ");
|
||||
|
||||
var smtpConfig = builder.Configuration.GetSection("Email:Smtp");
|
||||
if (smtpConfig.Exists()) {
|
||||
builder.Services.Configure<SmtpConfiguration>(smtpConfig);
|
||||
builder.Services.AddKeyedScoped<IEmailService, LiveEmailService>("live");
|
||||
var emailConfig = builder.Configuration.GetSection("Email").Get<EmailConfiguration>();
|
||||
builder.Services.Configure<EmailConfiguration>(builder.Configuration.GetSection("Email"));
|
||||
if (emailConfig?.Smtp.Count > 0) {
|
||||
if (string.IsNullOrWhiteSpace(emailConfig.SenderEmail)) {
|
||||
throw new ApplicationException(
|
||||
"Email providers have been configured, but no SenderEmail. " +
|
||||
"Please provider the sender email address used for email distribution.");
|
||||
}
|
||||
|
||||
foreach (var smtp in emailConfig.Smtp) {
|
||||
builder.Services.AddKeyedScoped<IEmailService, LiveEmailService>(smtp.Key.ToLower(), (provider, key) =>
|
||||
ActivatorUtilities.CreateInstance<LiveEmailService>(provider,
|
||||
provider.GetRequiredService<IOptions<EmailConfiguration>>().Value.Smtp[(string)key]));
|
||||
}
|
||||
|
||||
if (emailConfig.Smtp.Keys.Any(k => k.Equals("live", StringComparison.CurrentCultureIgnoreCase))) {
|
||||
builder.Services.AddScoped<IEmailSender, SmtpEmailSender>();
|
||||
builder.Services.AddScoped<IAdvancedEmailSender, SmtpEmailSender>();
|
||||
builder.Services.AddScoped<IEmailSender<ApplicationUser>, SmtpEmailSender>();
|
||||
} else {
|
||||
builder.Services.AddSingleton<IEmailSender<ApplicationUser>, IdentityNoOpEmailSender>();
|
||||
logMessages.Add("No Email provider configured.");
|
||||
logMessages.Add("No 'live' email provider configured.");
|
||||
}
|
||||
|
||||
if (emailConfig.Smtp.Keys.Any(k => k.Equals("bulk", StringComparison.CurrentCultureIgnoreCase))) {
|
||||
builder.Services.AddScoped<NewsletterBackgroundService>();
|
||||
} else if (builder.Configuration.GetSection(nameof(Features)).Get<Features>()?.EmailSubscriptions is not true) {
|
||||
throw new ApplicationException(
|
||||
"Email subscriptions have been enabled, but no 'bulk' email provider was configured. " +
|
||||
"Disable email subscriptions or provide the mail provider for bulk sending");
|
||||
}
|
||||
} else {
|
||||
builder.Services.AddSingleton<IEmailSender<ApplicationUser>, IdentityNoOpEmailSender>();
|
||||
logMessages.Add("No email provider configured.");
|
||||
}
|
||||
|
||||
builder.Services.AddScoped<EmailFactory>();
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
namespace Wave.Services;
|
||||
|
||||
public interface IEmailService : IAsyncDisposable {
|
||||
ValueTask Connect(CancellationToken cancellation);
|
||||
ValueTask Disconnect(CancellationToken cancellation);
|
||||
ValueTask ConnectAsync(CancellationToken cancellation);
|
||||
ValueTask DisconnectAsync(CancellationToken cancellation);
|
||||
|
||||
ValueTask SendEmailAsync(IEmail email);
|
||||
}
|
5
Wave/Services/IScopedProcessingService.cs
Normal file
5
Wave/Services/IScopedProcessingService.cs
Normal file
|
@ -0,0 +1,5 @@
|
|||
namespace Wave.Services;
|
||||
|
||||
public interface IScopedProcessingService {
|
||||
ValueTask DoWork(CancellationToken cancellationToken);
|
||||
}
|
|
@ -6,18 +6,19 @@
|
|||
|
||||
namespace Wave.Services;
|
||||
|
||||
public class LiveEmailService(ILogger<LiveEmailService> logger, IOptions<SmtpConfiguration> configuration) : IEmailService {
|
||||
public class LiveEmailService(ILogger<LiveEmailService> logger, IOptions<EmailConfiguration> emailConfiguration, SmtpConfiguration configuration) : IEmailService {
|
||||
private ILogger<LiveEmailService> Logger { get; } = logger;
|
||||
private SmtpConfiguration Configuration { get; } = configuration.Value;
|
||||
private EmailConfiguration EmailConfiguration { get; } = emailConfiguration.Value;
|
||||
private SmtpConfiguration Configuration { get; } = configuration;
|
||||
|
||||
private SmtpClient? Client { get; set; }
|
||||
|
||||
public async ValueTask DisposeAsync() {
|
||||
GC.SuppressFinalize(this);
|
||||
await Disconnect(CancellationToken.None);
|
||||
await DisconnectAsync(CancellationToken.None);
|
||||
}
|
||||
|
||||
public async ValueTask Connect(CancellationToken cancellation) {
|
||||
public async ValueTask ConnectAsync(CancellationToken cancellation) {
|
||||
if (Client is not null) return;
|
||||
|
||||
try {
|
||||
|
@ -35,7 +36,7 @@ public class LiveEmailService(ILogger<LiveEmailService> logger, IOptions<SmtpCon
|
|||
}
|
||||
}
|
||||
|
||||
public async ValueTask Disconnect(CancellationToken cancellation) {
|
||||
public async ValueTask DisconnectAsync(CancellationToken cancellation) {
|
||||
if (Client is null) return;
|
||||
await Client.DisconnectAsync(true, cancellation);
|
||||
Client.Dispose();
|
||||
|
@ -47,7 +48,7 @@ public class LiveEmailService(ILogger<LiveEmailService> logger, IOptions<SmtpCon
|
|||
if (Client is null) throw new ApplicationException("Not connected.");
|
||||
|
||||
var message = new MimeMessage {
|
||||
From = { new MailboxAddress(Configuration.SenderName, Configuration.SenderEmail) },
|
||||
From = { new MailboxAddress(EmailConfiguration.SenderName, EmailConfiguration.SenderEmail) },
|
||||
To = { new MailboxAddress(email.ReceiverName, email.ReceiverEmail) },
|
||||
Subject = email.Subject
|
||||
};
|
||||
|
@ -55,7 +56,7 @@ public class LiveEmailService(ILogger<LiveEmailService> logger, IOptions<SmtpCon
|
|||
message.Body = builder.ToMessageBody();
|
||||
foreach ((string id, string value) in email.Headers) {
|
||||
if (id == HeaderId.ListUnsubscribe.ToHeaderName()) {
|
||||
message.Headers.Add(HeaderId.ListId, $"<mailto:{Configuration.ServiceEmail ?? Configuration.SenderEmail}>");
|
||||
message.Headers.Add(HeaderId.ListId, $"<mailto:{EmailConfiguration.ServiceEmail ?? EmailConfiguration.SenderEmail}>");
|
||||
}
|
||||
message.Headers.Add(id, value);
|
||||
}
|
||||
|
|
|
@ -3,9 +3,10 @@
|
|||
|
||||
namespace Wave.Services;
|
||||
|
||||
public class SmtpEmailSender(EmailFactory email, [FromKeyedServices("live")]IEmailService emailService) : IEmailSender<ApplicationUser>, IAdvancedEmailSender, IAsyncDisposable {
|
||||
public class SmtpEmailSender(EmailFactory email, [FromKeyedServices("live")]IEmailService emailService, [FromKeyedServices("bulk")]IEmailService bulkEmailService) : IEmailSender<ApplicationUser>, IAdvancedEmailSender, IAsyncDisposable {
|
||||
private EmailFactory Email { get; } = email;
|
||||
private IEmailService EmailService { get; } = emailService;
|
||||
private IEmailService BulkEmailService { get; } = bulkEmailService;
|
||||
|
||||
#region IEmailSenderAsync<ApplicationUser>
|
||||
|
||||
|
@ -33,33 +34,35 @@ public class SmtpEmailSender(EmailFactory email, [FromKeyedServices("live")]IEma
|
|||
|
||||
|
||||
public async Task SendEmailAsync(string email, string? name, string subject, string htmlMessage) {
|
||||
await EmailService.Connect(CancellationToken.None);
|
||||
await EmailService.ConnectAsync(CancellationToken.None);
|
||||
await EmailService.SendEmailAsync(await Email.CreateDefaultEmail(email, name, subject, subject, htmlMessage));
|
||||
await EmailService.Disconnect(CancellationToken.None);
|
||||
await EmailService.DisconnectAsync(CancellationToken.None);
|
||||
}
|
||||
|
||||
public async Task SendDefaultMailAsync(string receiverMail, string? receiverName, string subject, string title, string bodyHtml) {
|
||||
await EmailService.Connect(CancellationToken.None);
|
||||
await EmailService.ConnectAsync(CancellationToken.None);
|
||||
var email = await Email.CreateDefaultEmail(receiverMail, receiverName, subject, title, bodyHtml);
|
||||
await EmailService.SendEmailAsync(email);
|
||||
await EmailService.Disconnect(CancellationToken.None);
|
||||
await EmailService.DisconnectAsync(CancellationToken.None);
|
||||
}
|
||||
|
||||
public async Task SendSubscribedMailAsync(EmailSubscriber subscriber, string subject, string title, string bodyHtml,
|
||||
string browserUrl = "", string subscribedRole = "-1") {
|
||||
var email = await Email.CreateSubscribedEmail(subscriber, browserUrl, subject, title, bodyHtml, subscribedRole);
|
||||
await EmailService.Connect(CancellationToken.None);
|
||||
await EmailService.SendEmailAsync(email); // TODO use bulk service
|
||||
await BulkEmailService.ConnectAsync(CancellationToken.None);
|
||||
await BulkEmailService.SendEmailAsync(email);
|
||||
}
|
||||
|
||||
public async Task SendWelcomeMailAsync(EmailSubscriber subscriber, string subject, string title, string bodyHtml,
|
||||
IEnumerable<EmailNewsletter> articles) {
|
||||
var email = await Email.CreateWelcomeEmail(subscriber, articles, subject, title, bodyHtml);
|
||||
await EmailService.Connect(CancellationToken.None);
|
||||
await EmailService.SendEmailAsync(email); // TODO use bulk service
|
||||
await BulkEmailService.ConnectAsync(CancellationToken.None);
|
||||
await BulkEmailService.SendEmailAsync(email);
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync() {
|
||||
GC.SuppressFinalize(this);
|
||||
await EmailService.DisposeAsync();
|
||||
await BulkEmailService.DisposeAsync();
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue