Added email support
This commit is contained in:
@@ -11,7 +11,7 @@
|
|||||||
<!-- Nuget Package Details -->
|
<!-- Nuget Package Details -->
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<PackageId>LiteCharms.Features</PackageId>
|
<PackageId>LiteCharms.Features</PackageId>
|
||||||
<Version>1.0.9</Version>
|
<Version>1.0.11</Version>
|
||||||
<Authors>Khwezi Mngoma</Authors>
|
<Authors>Khwezi Mngoma</Authors>
|
||||||
<Company>Lite Charms (PTY) Ltd</Company>
|
<Company>Lite Charms (PTY) Ltd</Company>
|
||||||
<Description>Feature components for Lite Charms applications.</Description>
|
<Description>Feature components for Lite Charms applications.</Description>
|
||||||
@@ -27,6 +27,16 @@
|
|||||||
<None Include="..\LICENSE" Pack="true" PackagePath="\" />
|
<None Include="..\LICENSE" Pack="true" PackagePath="\" />
|
||||||
<None Include="..\icon.png" Pack="true" PackagePath="\" />
|
<None Include="..\icon.png" Pack="true" PackagePath="\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<!-- Email -->
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="MailKit" Version="4.16.0" />
|
||||||
|
<PackageReference Include="MimeKit" Version="4.16.0" />
|
||||||
|
|
||||||
|
<!-- Global Usings-->
|
||||||
|
<Using Include="MimeKit"/>
|
||||||
|
<Using Include="MailKit.Net.Smtp"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<!-- CQRS -->
|
<!-- CQRS -->
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -43,6 +53,7 @@
|
|||||||
<Using Include="System.Text" />
|
<Using Include="System.Text" />
|
||||||
<Using Include="System.Security.Cryptography" />
|
<Using Include="System.Security.Cryptography" />
|
||||||
<Using Include="Microsoft.EntityFrameworkCore" />
|
<Using Include="Microsoft.EntityFrameworkCore" />
|
||||||
|
<Using Include="Microsoft.Extensions.Options" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
using LiteCharms.Models.Configuraton.Email;
|
||||||
|
|
||||||
|
namespace LiteCharms.Features.Utilities.Commands.Handlers;
|
||||||
|
|
||||||
|
public class SendEmailCommandHandler(IOptions<SmtpSettings> smtpOptions) : IRequestHandler<SendEmailCommand, Result>
|
||||||
|
{
|
||||||
|
public async ValueTask<Result> Handle(SendEmailCommand request, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var settings = smtpOptions.Value;
|
||||||
|
|
||||||
|
if(settings == null)
|
||||||
|
return Result.Fail(new Error("SMTP settings are not configured."));
|
||||||
|
|
||||||
|
if(settings.Credentials == null)
|
||||||
|
return Result.Fail(new Error("SMTP credentials are not configured."));
|
||||||
|
|
||||||
|
if(string.IsNullOrWhiteSpace(settings.Credentials.Username) || string.IsNullOrWhiteSpace(settings.Credentials.Password))
|
||||||
|
return Result.Fail(new Error("SMTP credentials are incomplete."));
|
||||||
|
|
||||||
|
if(string.IsNullOrWhiteSpace(settings.Host) || settings.Port == 0)
|
||||||
|
return Result.Fail(new Error("SMTP host and port must be configured."));
|
||||||
|
|
||||||
|
var message = new MimeMessage();
|
||||||
|
message.From.Add(new MailboxAddress(request.SenderName, request.From!));
|
||||||
|
message.To.Add(new MailboxAddress(request.RecipientName, request.To!));
|
||||||
|
message.Subject = request.Subject!;
|
||||||
|
|
||||||
|
var bodyBuilder = new BodyBuilder();
|
||||||
|
|
||||||
|
if(request.Attachment?.Length > 0 && !string.IsNullOrEmpty(request.AttachmentFileName))
|
||||||
|
bodyBuilder.Attachments.Add(request.AttachmentFileName!, request.Attachment!, cancellationToken);
|
||||||
|
|
||||||
|
if (!request.IsHtml) bodyBuilder.TextBody = request.Message;
|
||||||
|
if (request.IsHtml) bodyBuilder.HtmlBody = request.Message;
|
||||||
|
|
||||||
|
message.Body = bodyBuilder.ToMessageBody();
|
||||||
|
|
||||||
|
using var client = new SmtpClient();
|
||||||
|
|
||||||
|
await client.ConnectAsync(settings.Host!, settings.Port, settings.UseSsl, cancellationToken);
|
||||||
|
await client.AuthenticateAsync(settings.Credentials!.Username!, settings.Credentials.Password!, cancellationToken);
|
||||||
|
|
||||||
|
var response = await client.SendAsync(message, cancellationToken);
|
||||||
|
|
||||||
|
bool emailSent = response.Contains("OK", StringComparison.InvariantCultureIgnoreCase);
|
||||||
|
|
||||||
|
await client.DisconnectAsync(true, cancellationToken);
|
||||||
|
|
||||||
|
return emailSent
|
||||||
|
? Result.Ok()
|
||||||
|
: Result.Fail(new Error("Failed to send email. SMTP response: " + response));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
return Result.Fail(new Error(ex.Message).CausedBy(ex));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
namespace LiteCharms.Features.Utilities.Commands;
|
||||||
|
|
||||||
|
public class SendEmailCommand : IRequest<Result>
|
||||||
|
{
|
||||||
|
public string? From { get; set; }
|
||||||
|
|
||||||
|
public string? SenderName { get; set; }
|
||||||
|
|
||||||
|
public string? To { get; set; }
|
||||||
|
|
||||||
|
public string? RecipientName { get; set; }
|
||||||
|
|
||||||
|
public string? Subject { get; set; }
|
||||||
|
|
||||||
|
public string? Message { get; set; }
|
||||||
|
|
||||||
|
public bool IsHtml { get; set; }
|
||||||
|
|
||||||
|
public Stream? Attachment { get; set; }
|
||||||
|
|
||||||
|
public string? AttachmentFileName { get; set; }
|
||||||
|
|
||||||
|
private SendEmailCommand(string from, string senderName, string to, string recipientName, string subject, string message, bool isHtml = false, Stream? attachment = null, string? attachmentFileName = null)
|
||||||
|
{
|
||||||
|
From = from;
|
||||||
|
To = to;
|
||||||
|
Subject = subject;
|
||||||
|
Message = message;
|
||||||
|
IsHtml = isHtml;
|
||||||
|
Attachment = attachment;
|
||||||
|
AttachmentFileName = attachmentFileName;
|
||||||
|
SenderName = senderName;
|
||||||
|
RecipientName = recipientName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SendEmailCommand Create(string from, string senderName, string to, string recipientName, string subject, string message, bool isHtml = false, Stream? attachment = null, string? attachmentFileName = null)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(from))
|
||||||
|
throw new ArgumentException("From address cannot be null or empty.", nameof(from));
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(senderName))
|
||||||
|
throw new ArgumentException("Sender name cannot be null or empty.", nameof(senderName));
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(senderName) && senderName.Length > 255)
|
||||||
|
throw new ArgumentException("Sender name cannot exceed 255 characters.", nameof(senderName));
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(to))
|
||||||
|
throw new ArgumentException("To address cannot be null or empty.", nameof(to));
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(recipientName))
|
||||||
|
throw new ArgumentException("Recipient name cannot be null or empty.", nameof(recipientName));
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(recipientName) && recipientName.Length > 255)
|
||||||
|
throw new ArgumentException("Recipient name cannot exceed 255 characters.", nameof(recipientName));
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(subject))
|
||||||
|
throw new ArgumentException("Subject cannot be null or empty.", nameof(subject));
|
||||||
|
|
||||||
|
if (subject.Length > 2048)
|
||||||
|
throw new ArgumentException("Subject cannot exceed 2048 characters.", nameof(subject));
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(message))
|
||||||
|
throw new ArgumentException("Message cannot be null or empty.", nameof(message));
|
||||||
|
|
||||||
|
if (message.Length > 10485760)
|
||||||
|
throw new ArgumentException("Message cannot exceed 10 MB.", nameof(message));
|
||||||
|
|
||||||
|
if (attachment != null && string.IsNullOrWhiteSpace(attachmentFileName))
|
||||||
|
throw new ArgumentException("Attachment file name must be provided when an attachment is included.", nameof(attachmentFileName));
|
||||||
|
|
||||||
|
if (attachment is not null && attachment.Length > 10485760)
|
||||||
|
throw new ArgumentException("Attachment cannot exceed 10 MB.", nameof(attachment));
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(attachmentFileName) && attachmentFileName.Length > 255)
|
||||||
|
throw new ArgumentException("Attachment file name cannot exceed 255 characters.", nameof(attachmentFileName));
|
||||||
|
|
||||||
|
return new(from, senderName, to, recipientName, subject, message, isHtml, attachment, attachmentFileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
namespace LiteCharms.Models.Configuraton.Email;
|
||||||
|
|
||||||
|
public class Account
|
||||||
|
{
|
||||||
|
public string? Username { get; set; }
|
||||||
|
|
||||||
|
public string? Password { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
namespace LiteCharms.Models.Configuraton.Email;
|
||||||
|
|
||||||
|
public class SmtpSettings
|
||||||
|
{
|
||||||
|
public Account? Credentials { get; set; }
|
||||||
|
|
||||||
|
public int Port { get; set; }
|
||||||
|
|
||||||
|
public string? Host { get; set; }
|
||||||
|
|
||||||
|
public bool UseSsl { get; set; }
|
||||||
|
}
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="..\LICENSE" Pack="true" PackagePath="\"/>
|
<None Include="..\LICENSE" Pack="true" PackagePath="\" />
|
||||||
<None Include="..\icon.png" Pack="true" PackagePath="\" />
|
<None Include="..\icon.png" Pack="true" PackagePath="\" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
Reference in New Issue
Block a user