initial commit
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
|
||||
|
||||
using Indotalent.Data.Entities;
|
||||
using Indotalent.Infrastructure.Email.Mailgun;
|
||||
using Indotalent.Infrastructure.Email.Mailjet;
|
||||
using Indotalent.Infrastructure.Email.SendGrid;
|
||||
using Indotalent.Infrastructure.Email.Smtp;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace Indotalent.Infrastructure.Email;
|
||||
|
||||
public static class DI
|
||||
{
|
||||
public static IServiceCollection AddEmailService(this IServiceCollection services, IConfiguration configuration)
|
||||
{
|
||||
services.AddScoped<EmailService>();
|
||||
|
||||
|
||||
var emailSettings = configuration.GetSection("EmailSettings").Get<EmailSettingsModel>() ?? new EmailSettingsModel();
|
||||
|
||||
if (emailSettings.SendGrid?.IsUsed == true)
|
||||
{
|
||||
services.AddHttpClient<IEmailSender, SendGridEmailSender>();
|
||||
}
|
||||
else if (emailSettings.Mailgun?.IsUsed == true)
|
||||
{
|
||||
services.AddHttpClient<IEmailSender, MailgunEmailSender>();
|
||||
}
|
||||
else if (emailSettings.Mailjet?.IsUsed == true)
|
||||
{
|
||||
services.AddHttpClient<IEmailSender, MailjetEmailSender>();
|
||||
}
|
||||
else if (emailSettings.Smtp?.IsUsed == true)
|
||||
{
|
||||
services.AddTransient<IEmailSender, SmtpEmailSender>();
|
||||
}
|
||||
else
|
||||
{
|
||||
services.AddScoped<IEmailSender, DefaultEmailSender>();
|
||||
}
|
||||
|
||||
services.AddTransient<IEmailSender<ApplicationUser>, IdentityEmailManager<ApplicationUser>>();
|
||||
|
||||
return services;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
namespace Indotalent.Infrastructure.Email;
|
||||
|
||||
public class DefaultEmailSender : IEmailSender
|
||||
{
|
||||
private readonly ILogger<DefaultEmailSender> _logger;
|
||||
|
||||
public DefaultEmailSender(ILogger<DefaultEmailSender> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public Task SendEmailAsync(string email, string subject, string htmlMessage)
|
||||
{
|
||||
_logger.LogWarning("Email sending requested but NO active email provider found in EmailSettings.");
|
||||
_logger.LogInformation("To: {Email}", email);
|
||||
_logger.LogInformation("Subject: {Subject}", subject);
|
||||
_logger.LogInformation("Content: {Message}", htmlMessage);
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Indotalent.Infrastructure.Email;
|
||||
|
||||
public class EmailService(IOptions<EmailSettingsModel> options)
|
||||
{
|
||||
private readonly EmailSettingsModel _settings = options.Value;
|
||||
|
||||
public EmailSettingsModel GetConfiguration() => _settings;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using Indotalent.Infrastructure.Email.Mailgun;
|
||||
using Indotalent.Infrastructure.Email.Mailjet;
|
||||
using Indotalent.Infrastructure.Email.SendGrid;
|
||||
using Indotalent.Infrastructure.Email.Smtp;
|
||||
|
||||
namespace Indotalent.Infrastructure.Email;
|
||||
|
||||
public class EmailSettingsModel
|
||||
{
|
||||
public SendGridSettingsModel SendGrid { get; set; } = new();
|
||||
public MailgunSettingsModel Mailgun { get; set; } = new();
|
||||
public MailjetSettingsModel Mailjet { get; set; } = new();
|
||||
public SmtpSettingsModel Smtp { get; set; } = new();
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Indotalent.Infrastructure.Email;
|
||||
|
||||
public interface IEmailSender
|
||||
{
|
||||
Task SendEmailAsync(string email, string subject, string htmlMessage);
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace Indotalent.Infrastructure.Email;
|
||||
|
||||
public class IdentityEmailManager<TUser> : IEmailSender<TUser> where TUser : class
|
||||
{
|
||||
private readonly IEmailSender _emailSender;
|
||||
|
||||
public IdentityEmailManager(IEmailSender emailSender)
|
||||
{
|
||||
_emailSender = emailSender;
|
||||
}
|
||||
|
||||
public Task SendConfirmationLinkAsync(TUser user, string email, string confirmationLink)
|
||||
{
|
||||
var message = confirmationLink.Contains("<")
|
||||
? confirmationLink
|
||||
: $"Please confirm your account by clicking this link: <a href='{confirmationLink}'>Confirm Account</a>";
|
||||
|
||||
return _emailSender.SendEmailAsync(email, "Account Confirmation", message);
|
||||
}
|
||||
|
||||
public Task SendPasswordResetLinkAsync(TUser user, string email, string resetLink)
|
||||
{
|
||||
var message = resetLink.Contains("<")
|
||||
? resetLink
|
||||
: $"To reset your password, please click the following link: <a href='{resetLink}'>Reset Password</a>";
|
||||
|
||||
return _emailSender.SendEmailAsync(email, "Password Reset Request", message);
|
||||
}
|
||||
|
||||
public Task SendPasswordResetCodeAsync(TUser user, string email, string resetCode) =>
|
||||
_emailSender.SendEmailAsync(email, "Your Password Reset Code",
|
||||
$"Your password reset security code is: <b>{resetCode}</b>. Please enter this code to proceed with the reset process.");
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
using Microsoft.Extensions.Options;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
|
||||
namespace Indotalent.Infrastructure.Email.Mailgun;
|
||||
|
||||
public class MailgunEmailSender : IEmailSender
|
||||
{
|
||||
private readonly MailgunSettingsModel _settings;
|
||||
private readonly HttpClient _httpClient;
|
||||
|
||||
public MailgunEmailSender(IOptions<EmailSettingsModel> emailOptions, HttpClient httpClient)
|
||||
{
|
||||
_settings = emailOptions.Value.Mailgun;
|
||||
_httpClient = httpClient;
|
||||
}
|
||||
|
||||
public async Task SendEmailAsync(string email, string subject, string htmlMessage)
|
||||
{
|
||||
if (string.IsNullOrEmpty(_settings.ApiKey) || string.IsNullOrEmpty(_settings.Domain))
|
||||
{
|
||||
throw new Exception("Mailgun configuration is missing ApiKey or Domain.");
|
||||
}
|
||||
|
||||
var authToken = Encoding.ASCII.GetBytes($"api:{_settings.ApiKey}");
|
||||
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(authToken));
|
||||
|
||||
var formContent = new FormUrlEncodedContent(new[]
|
||||
{
|
||||
new KeyValuePair<string, string>("from", _settings.FromEmail),
|
||||
new KeyValuePair<string, string>("to", email),
|
||||
new KeyValuePair<string, string>("subject", subject),
|
||||
new KeyValuePair<string, string>("html", htmlMessage)
|
||||
});
|
||||
|
||||
var response = await _httpClient.PostAsync($"https://api.mailgun.net/v3/{_settings.Domain}/messages", formContent);
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
var errorBody = await response.Content.ReadAsStringAsync();
|
||||
throw new Exception($"Failed to send email via Mailgun. Status: {response.StatusCode}, Error: {errorBody}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Indotalent.Infrastructure.Email.Mailgun;
|
||||
|
||||
public class MailgunSettingsModel
|
||||
{
|
||||
public bool IsUsed { get; set; }
|
||||
public string ApiKey { get; set; } = string.Empty;
|
||||
public string Domain { get; set; } = string.Empty;
|
||||
public string FromEmail { get; set; } = string.Empty;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
using Microsoft.Extensions.Options;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Indotalent.Infrastructure.Email.Mailjet;
|
||||
|
||||
public class MailjetEmailSender : IEmailSender
|
||||
{
|
||||
private readonly MailjetSettingsModel _settings;
|
||||
private readonly HttpClient _httpClient;
|
||||
|
||||
public MailjetEmailSender(IOptions<EmailSettingsModel> emailOptions, HttpClient httpClient)
|
||||
{
|
||||
_settings = emailOptions.Value.Mailjet;
|
||||
_httpClient = httpClient;
|
||||
}
|
||||
|
||||
public async Task SendEmailAsync(string email, string subject, string htmlMessage)
|
||||
{
|
||||
if (string.IsNullOrEmpty(_settings.ApiKey) || string.IsNullOrEmpty(_settings.ApiSecret))
|
||||
{
|
||||
throw new Exception("Mailjet configuration is missing ApiKey or ApiSecret.");
|
||||
}
|
||||
|
||||
var authToken = Encoding.UTF8.GetBytes($"{_settings.ApiKey}:{_settings.ApiSecret}");
|
||||
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(authToken));
|
||||
|
||||
var payload = new
|
||||
{
|
||||
Messages = new[]
|
||||
{
|
||||
new
|
||||
{
|
||||
From = new { Email = _settings.FromEmail, Name = "Mailjet System" },
|
||||
To = new[] { new { Email = email } },
|
||||
Subject = subject,
|
||||
HTMLPart = htmlMessage
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var content = new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json");
|
||||
var response = await _httpClient.PostAsync("https://api.mailjet.com/v3.1/send", content);
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
var errorBody = await response.Content.ReadAsStringAsync();
|
||||
throw new Exception($"Failed to send email via Mailjet. Status: {response.StatusCode}, Error: {errorBody}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Indotalent.Infrastructure.Email.Mailjet;
|
||||
|
||||
public class MailjetSettingsModel
|
||||
{
|
||||
public bool IsUsed { get; set; }
|
||||
public string ApiKey { get; set; } = string.Empty;
|
||||
public string ApiSecret { get; set; } = string.Empty;
|
||||
public string FromEmail { get; set; } = string.Empty;
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
using Microsoft.Extensions.Options;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Indotalent.Infrastructure.Email.SendGrid;
|
||||
|
||||
public class SendGridEmailSender : IEmailSender
|
||||
{
|
||||
private readonly SendGridSettingsModel _settings;
|
||||
private readonly HttpClient _httpClient;
|
||||
|
||||
public SendGridEmailSender(IOptions<EmailSettingsModel> emailOptions, HttpClient httpClient)
|
||||
{
|
||||
_settings = emailOptions.Value.SendGrid;
|
||||
_httpClient = httpClient;
|
||||
}
|
||||
|
||||
public async Task SendEmailAsync(string email, string subject, string htmlMessage)
|
||||
{
|
||||
if (string.IsNullOrEmpty(_settings.ApiKey))
|
||||
{
|
||||
throw new Exception("SendGrid configuration is missing ApiKey.");
|
||||
}
|
||||
|
||||
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", _settings.ApiKey);
|
||||
|
||||
var payload = new
|
||||
{
|
||||
personalizations = new[]
|
||||
{
|
||||
new
|
||||
{
|
||||
to = new[] { new { email = email } }
|
||||
}
|
||||
},
|
||||
from = new { email = _settings.FromEmail, name = "SendGrid System" },
|
||||
subject = subject,
|
||||
content = new[]
|
||||
{
|
||||
new
|
||||
{
|
||||
type = "text/html",
|
||||
value = htmlMessage
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var content = new StringContent(JsonSerializer.Serialize(payload), Encoding.UTF8, "application/json");
|
||||
var response = await _httpClient.PostAsync("https://api.sendgrid.com/v3/mail/send", content);
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
var errorBody = await response.Content.ReadAsStringAsync();
|
||||
throw new Exception($"Failed to send email via SendGrid. Status: {response.StatusCode}, Error: {errorBody}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Indotalent.Infrastructure.Email.SendGrid;
|
||||
|
||||
public class SendGridSettingsModel
|
||||
{
|
||||
public bool IsUsed { get; set; }
|
||||
public string ApiKey { get; set; } = string.Empty;
|
||||
public string FromEmail { get; set; } = string.Empty;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
using MailKit.Net.Smtp;
|
||||
using MailKit.Security;
|
||||
using Microsoft.Extensions.Options;
|
||||
using MimeKit;
|
||||
|
||||
namespace Indotalent.Infrastructure.Email.Smtp;
|
||||
|
||||
public class SmtpEmailSender : IEmailSender
|
||||
{
|
||||
private readonly SmtpSettingsModel _settings;
|
||||
|
||||
public SmtpEmailSender(IOptions<EmailSettingsModel> emailOptions)
|
||||
{
|
||||
_settings = emailOptions.Value.Smtp;
|
||||
}
|
||||
|
||||
public async Task SendEmailAsync(string email, string subject, string htmlMessage)
|
||||
{
|
||||
var message = new MimeMessage();
|
||||
|
||||
message.From.Add(new MailboxAddress(_settings.FromName, _settings.FromAddress));
|
||||
|
||||
message.To.Add(MailboxAddress.Parse(email));
|
||||
|
||||
message.Subject = subject;
|
||||
|
||||
var bodyBuilder = new BodyBuilder
|
||||
{
|
||||
HtmlBody = htmlMessage
|
||||
};
|
||||
message.Body = bodyBuilder.ToMessageBody();
|
||||
|
||||
using var client = new SmtpClient();
|
||||
try
|
||||
{
|
||||
await client.ConnectAsync(_settings.Host, _settings.Port, SecureSocketOptions.Auto);
|
||||
|
||||
if (!string.IsNullOrEmpty(_settings.UserName))
|
||||
{
|
||||
await client.AuthenticateAsync(_settings.UserName, _settings.Password);
|
||||
}
|
||||
|
||||
await client.SendAsync(message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception($"Failed to send email via SMTP to {email} via {_settings.Host}. Error: {ex.Message}", ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
await client.DisconnectAsync(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace Indotalent.Infrastructure.Email.Smtp;
|
||||
|
||||
public class SmtpSettingsModel
|
||||
{
|
||||
public bool IsUsed { get; set; }
|
||||
public string Host { get; set; } = string.Empty;
|
||||
public int Port { get; set; }
|
||||
public string UserName { get; set; } = string.Empty;
|
||||
public string Password { get; set; } = string.Empty;
|
||||
public string FromAddress { get; set; } = string.Empty;
|
||||
public string FromName { get; set; } = string.Empty;
|
||||
}
|
||||
Reference in New Issue
Block a user