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 int Port { get; init; }
|
||||||
public required string Username { get; init; }
|
public required string Username { get; init; }
|
||||||
public required string Password { 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;
|
public bool Ssl { get; init; } = true;
|
||||||
}
|
}
|
|
@ -121,16 +121,40 @@
|
||||||
builder.Services.AddCascadingValue("TitlePrefix",
|
builder.Services.AddCascadingValue("TitlePrefix",
|
||||||
sf => (sf.GetService<IOptions<Customization>>()?.Value.AppName ?? "Wave") + " - ");
|
sf => (sf.GetService<IOptions<Customization>>()?.Value.AppName ?? "Wave") + " - ");
|
||||||
|
|
||||||
var smtpConfig = builder.Configuration.GetSection("Email:Smtp");
|
var emailConfig = builder.Configuration.GetSection("Email").Get<EmailConfiguration>();
|
||||||
if (smtpConfig.Exists()) {
|
builder.Services.Configure<EmailConfiguration>(builder.Configuration.GetSection("Email"));
|
||||||
builder.Services.Configure<SmtpConfiguration>(smtpConfig);
|
if (emailConfig?.Smtp.Count > 0) {
|
||||||
builder.Services.AddKeyedScoped<IEmailService, LiveEmailService>("live");
|
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<IEmailSender, SmtpEmailSender>();
|
||||||
builder.Services.AddScoped<IAdvancedEmailSender, SmtpEmailSender>();
|
builder.Services.AddScoped<IAdvancedEmailSender, SmtpEmailSender>();
|
||||||
builder.Services.AddScoped<IEmailSender<ApplicationUser>, SmtpEmailSender>();
|
builder.Services.AddScoped<IEmailSender<ApplicationUser>, SmtpEmailSender>();
|
||||||
} else {
|
} else {
|
||||||
builder.Services.AddSingleton<IEmailSender<ApplicationUser>, IdentityNoOpEmailSender>();
|
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>();
|
builder.Services.AddScoped<EmailFactory>();
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
namespace Wave.Services;
|
namespace Wave.Services;
|
||||||
|
|
||||||
public interface IEmailService : IAsyncDisposable {
|
public interface IEmailService : IAsyncDisposable {
|
||||||
ValueTask Connect(CancellationToken cancellation);
|
ValueTask ConnectAsync(CancellationToken cancellation);
|
||||||
ValueTask Disconnect(CancellationToken cancellation);
|
ValueTask DisconnectAsync(CancellationToken cancellation);
|
||||||
|
|
||||||
ValueTask SendEmailAsync(IEmail email);
|
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;
|
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 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; }
|
private SmtpClient? Client { get; set; }
|
||||||
|
|
||||||
public async ValueTask DisposeAsync() {
|
public async ValueTask DisposeAsync() {
|
||||||
GC.SuppressFinalize(this);
|
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;
|
if (Client is not null) return;
|
||||||
|
|
||||||
try {
|
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;
|
if (Client is null) return;
|
||||||
await Client.DisconnectAsync(true, cancellation);
|
await Client.DisconnectAsync(true, cancellation);
|
||||||
Client.Dispose();
|
Client.Dispose();
|
||||||
|
@ -47,7 +48,7 @@ public class LiveEmailService(ILogger<LiveEmailService> logger, IOptions<SmtpCon
|
||||||
if (Client is null) throw new ApplicationException("Not connected.");
|
if (Client is null) throw new ApplicationException("Not connected.");
|
||||||
|
|
||||||
var message = new MimeMessage {
|
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) },
|
To = { new MailboxAddress(email.ReceiverName, email.ReceiverEmail) },
|
||||||
Subject = email.Subject
|
Subject = email.Subject
|
||||||
};
|
};
|
||||||
|
@ -55,7 +56,7 @@ public class LiveEmailService(ILogger<LiveEmailService> logger, IOptions<SmtpCon
|
||||||
message.Body = builder.ToMessageBody();
|
message.Body = builder.ToMessageBody();
|
||||||
foreach ((string id, string value) in email.Headers) {
|
foreach ((string id, string value) in email.Headers) {
|
||||||
if (id == HeaderId.ListUnsubscribe.ToHeaderName()) {
|
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);
|
message.Headers.Add(id, value);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,10 @@
|
||||||
|
|
||||||
namespace Wave.Services;
|
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 EmailFactory Email { get; } = email;
|
||||||
private IEmailService EmailService { get; } = emailService;
|
private IEmailService EmailService { get; } = emailService;
|
||||||
|
private IEmailService BulkEmailService { get; } = bulkEmailService;
|
||||||
|
|
||||||
#region IEmailSenderAsync<ApplicationUser>
|
#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) {
|
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.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) {
|
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);
|
var email = await Email.CreateDefaultEmail(receiverMail, receiverName, subject, title, bodyHtml);
|
||||||
await EmailService.SendEmailAsync(email);
|
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,
|
public async Task SendSubscribedMailAsync(EmailSubscriber subscriber, string subject, string title, string bodyHtml,
|
||||||
string browserUrl = "", string subscribedRole = "-1") {
|
string browserUrl = "", string subscribedRole = "-1") {
|
||||||
var email = await Email.CreateSubscribedEmail(subscriber, browserUrl, subject, title, bodyHtml, subscribedRole);
|
var email = await Email.CreateSubscribedEmail(subscriber, browserUrl, subject, title, bodyHtml, subscribedRole);
|
||||||
await EmailService.Connect(CancellationToken.None);
|
await BulkEmailService.ConnectAsync(CancellationToken.None);
|
||||||
await EmailService.SendEmailAsync(email); // TODO use bulk service
|
await BulkEmailService.SendEmailAsync(email);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SendWelcomeMailAsync(EmailSubscriber subscriber, string subject, string title, string bodyHtml,
|
public async Task SendWelcomeMailAsync(EmailSubscriber subscriber, string subject, string title, string bodyHtml,
|
||||||
IEnumerable<EmailNewsletter> articles) {
|
IEnumerable<EmailNewsletter> articles) {
|
||||||
var email = await Email.CreateWelcomeEmail(subscriber, articles, subject, title, bodyHtml);
|
var email = await Email.CreateWelcomeEmail(subscriber, articles, subject, title, bodyHtml);
|
||||||
await EmailService.Connect(CancellationToken.None);
|
await BulkEmailService.ConnectAsync(CancellationToken.None);
|
||||||
await EmailService.SendEmailAsync(email); // TODO use bulk service
|
await BulkEmailService.SendEmailAsync(email);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async ValueTask DisposeAsync() {
|
public async ValueTask DisposeAsync() {
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
await EmailService.DisposeAsync();
|
await EmailService.DisposeAsync();
|
||||||
|
await BulkEmailService.DisposeAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue