First Commit
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using ADS.Helpers.Base.Abstractions;
|
||||
using ADS.Helpers.MultiTenant.Context;
|
||||
using ADS.Helpers.NotifyCenter.Abstractions;
|
||||
using ADS.Helpers.NotifyCenter.Models;
|
||||
using MailKit.Net.Smtp;
|
||||
using MailKit.Security;
|
||||
|
||||
namespace ADS.Helpers.NotifyCenter.Providers.Smtp;
|
||||
|
||||
public sealed class SmtpNotificationProvider : INotificationProvider
|
||||
{
|
||||
private readonly INotificationSettingsProvider _settingsProvider;
|
||||
private readonly ITenantContextAccessor _tenantAccessor;
|
||||
private readonly IAppLogger _log;
|
||||
|
||||
public NotificationChannel Channel => NotificationChannel.SmtpEmail;
|
||||
public string Name => "SMTP";
|
||||
|
||||
public SmtpNotificationProvider(
|
||||
INotificationSettingsProvider settingsProvider,
|
||||
ITenantContextAccessor tenantAccessor,
|
||||
IAppLogger log)
|
||||
{
|
||||
_settingsProvider = settingsProvider;
|
||||
_tenantAccessor = tenantAccessor;
|
||||
_log = log;
|
||||
}
|
||||
|
||||
public async Task<NotificationResult> SendAsync(NotificationMessage message, CancellationToken ct = default)
|
||||
{
|
||||
var tenant = _tenantAccessor.Current;
|
||||
|
||||
if (tenant is null || string.IsNullOrWhiteSpace(tenant.TenantId))
|
||||
{
|
||||
var dbg = "TenantContext is not available for SMTP provider.";
|
||||
|
||||
_log.Error(
|
||||
source: MessageSources.SmtpProvider,
|
||||
code: NotifyCodes.TenantContextMissing,
|
||||
message: dbg,
|
||||
ex: null,
|
||||
data: new { message.TraceId });
|
||||
|
||||
return NotificationResult.Fail(
|
||||
providerName: Name,
|
||||
source: MessageSources.SmtpProvider,
|
||||
code: NotifyCodes.TenantContextMissing,
|
||||
debugMessage: dbg);
|
||||
}
|
||||
|
||||
var opts = await _settingsProvider.GetAsync<SmtpSettings>(tenant.TenantId, ct);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(opts.Host) || opts.Port <= 0 || string.IsNullOrWhiteSpace(opts.FromEmail))
|
||||
{
|
||||
_log.Warn(
|
||||
source: MessageSources.SmtpProvider,
|
||||
code: NotifyCodes.SmtpNotConfigured,
|
||||
message: "SMTP settings are missing or invalid.",
|
||||
data: new { message.TraceId, tenant.TenantId });
|
||||
|
||||
return NotificationResult.Fail(
|
||||
providerName: Name,
|
||||
source: MessageSources.SmtpProvider,
|
||||
code: NotifyCodes.SmtpNotConfigured,
|
||||
debugMessage: "Host/Port/FromEmail missing or invalid.");
|
||||
}
|
||||
|
||||
var mime = new MimeKit.MimeMessage();
|
||||
mime.From.Add(new MimeKit.MailboxAddress(opts.FromName ?? "", opts.FromEmail));
|
||||
|
||||
foreach (var to in message.To.Distinct(StringComparer.OrdinalIgnoreCase))
|
||||
mime.To.Add(MimeKit.MailboxAddress.Parse(to));
|
||||
|
||||
foreach (var cc in message.Cc.Distinct(StringComparer.OrdinalIgnoreCase))
|
||||
mime.Cc.Add(MimeKit.MailboxAddress.Parse(cc));
|
||||
|
||||
foreach (var bcc in message.Bcc.Distinct(StringComparer.OrdinalIgnoreCase))
|
||||
mime.Bcc.Add(MimeKit.MailboxAddress.Parse(bcc));
|
||||
|
||||
mime.Subject = message.Subject ?? "";
|
||||
|
||||
var bodyBuilder = new MimeKit.BodyBuilder
|
||||
{
|
||||
TextBody = message.BodyText,
|
||||
HtmlBody = message.BodyHtml
|
||||
};
|
||||
mime.Body = bodyBuilder.ToMessageBody();
|
||||
|
||||
try
|
||||
{
|
||||
using var client = new SmtpClient { Timeout = opts.TimeoutMs };
|
||||
|
||||
SecureSocketOptions sec;
|
||||
if (opts.UseSsl) sec = SecureSocketOptions.SslOnConnect;
|
||||
else if (opts.UseStartTls) sec = SecureSocketOptions.StartTls;
|
||||
else sec = SecureSocketOptions.None;
|
||||
|
||||
await client.ConnectAsync(opts.Host, opts.Port, sec, ct);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(opts.Username))
|
||||
await client.AuthenticateAsync(opts.Username, opts.Password, ct);
|
||||
|
||||
await client.SendAsync(mime, ct);
|
||||
await client.DisconnectAsync(true, ct);
|
||||
|
||||
_log.Info(
|
||||
source: MessageSources.SmtpProvider,
|
||||
code: NotifyCodes.Ok,
|
||||
message: "SMTP message sent.",
|
||||
data: new { message.TraceId, tenant.TenantId });
|
||||
|
||||
return NotificationResult.Ok(
|
||||
providerName: Name,
|
||||
source: MessageSources.SmtpProvider,
|
||||
code: NotifyCodes.Ok,
|
||||
providerMessageId: null,
|
||||
debugMessage: "SMTP message sent.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_log.Error(
|
||||
source: MessageSources.SmtpProvider,
|
||||
code: NotifyCodes.SmtpSendFailed,
|
||||
message: "SMTP send failed.",
|
||||
ex: ex,
|
||||
data: new { message.TraceId, tenant.TenantId });
|
||||
|
||||
return NotificationResult.Fail(
|
||||
providerName: Name,
|
||||
source: MessageSources.SmtpProvider,
|
||||
code: NotifyCodes.SmtpSendFailed,
|
||||
debugMessage: ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
namespace ADS.Helpers.NotifyCenter.Providers.Smtp;
|
||||
|
||||
public sealed class SmtpOptions
|
||||
{
|
||||
public string Host { get; set; } = "";
|
||||
public int Port { get; set; } = 587;
|
||||
|
||||
public bool UseSsl { get; set; } = false; // implicit SSL (SMTPS)
|
||||
public bool UseStartTls { get; set; } = true; // STARTTLS
|
||||
|
||||
public string Username { get; set; } = "";
|
||||
public string Password { get; set; } = "";
|
||||
|
||||
public string FromEmail { get; set; } = "";
|
||||
public string FromName { get; set; } = "";
|
||||
|
||||
// Optional timeout
|
||||
public int TimeoutMs { get; set; } = 15000;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
namespace ADS.Helpers.NotifyCenter.Providers.Smtp;
|
||||
|
||||
public sealed class SmtpSettings
|
||||
{
|
||||
public string Host { get; set; } = "";
|
||||
public int Port { get; set; } = 587;
|
||||
|
||||
public bool UseSsl { get; set; } = false;
|
||||
public bool UseStartTls { get; set; } = true;
|
||||
|
||||
public string Username { get; set; } = "";
|
||||
public string Password { get; set; } = "";
|
||||
|
||||
public string FromEmail { get; set; } = "";
|
||||
public string FromName { get; set; } = "";
|
||||
|
||||
public int TimeoutMs { get; set; } = 15000;
|
||||
}
|
||||
Reference in New Issue
Block a user