Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make api to not fail when sending email #58

Merged
merged 1 commit into from
May 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions Src/DeUrgenta.Api/DeUrgenta.Api.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,13 @@
<ProjectReference Include="..\DeUrgenta.User.Api\DeUrgenta.User.Api.csproj" />
</ItemGroup>

<ItemGroup>
<None Update="EmailTemplates\accountConfirmationTemplate.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Update="EmailTemplates\resetPasswordTemplate.html">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>

</Project>
66 changes: 47 additions & 19 deletions Src/DeUrgenta.User.Api/Controller/AuthController.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using DeUrgenta.User.Api.Models.DTOs.Requests;
using DeUrgenta.User.Api.Models.DTOs.Responses;
using DeUrgenta.User.Api.Notifications;
using DeUrgenta.User.Api.Services;
using DeUrgenta.User.Api.Services.Emailing;
using MediatR;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.WebUtilities;
Expand All @@ -21,26 +23,27 @@ public class AuthManagementController : ControllerBase
private readonly UserManager<IdentityUser> _userManager;
private readonly IApplicationUserManager _applicationUserManager;
private readonly IJwtService _jwtService;
private readonly IEmailSender _emailSender;
private readonly IMediator _mediator;
private readonly IConfiguration _configuration;
private string _senderName;

public AuthManagementController(
UserManager<IdentityUser> userManager,
IApplicationUserManager applicationUserManager,
IJwtService jwtService,
IEmailSender emailSender,
IMediator mediator,
IConfiguration configuration)
{
_userManager = userManager;
_applicationUserManager = applicationUserManager;
_jwtService = jwtService;
_emailSender = emailSender;
_mediator = mediator;
_configuration = configuration;
}

[HttpPost]
[Route("register")]
public async Task<IActionResult> Register([FromBody] UserRegistrationDto user)
public async Task<IActionResult> RegisterAsync([FromBody] UserRegistrationDto user)
{
var existingUser = await _userManager.FindByEmailAsync(user.Email);

Expand All @@ -56,10 +59,7 @@ public async Task<IActionResult> Register([FromBody] UserRegistrationDto user)
if (identityResult.Succeeded)
{
await _applicationUserManager.CreateApplicationUserAsync(user, newUser.Id);
var code = await _userManager.GenerateEmailConfirmationTokenAsync(newUser);
code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
var confirmationUrl = _configuration.GetValue<string>("ConfirmationUrl");
var callbackUrl = $"{confirmationUrl}?userId={newUser.Id}&code={code}";
string callbackUrl = await GetCallbackUrlAsync(newUser);

await SendRegistrationEmail(newUser.UserName, user.Email, callbackUrl);
return Ok("Email was sent");
Expand All @@ -72,6 +72,15 @@ public async Task<IActionResult> Register([FromBody] UserRegistrationDto user)
});
}

private async Task<string> GetCallbackUrlAsync(IdentityUser newUser)
{
var code = await _userManager.GenerateEmailConfirmationTokenAsync(newUser);
code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
var confirmationUrl = _configuration.GetValue<string>("ConfirmationUrl");
var callbackUrl = $"{confirmationUrl}?userId={newUser.Id}&code={code}";
return callbackUrl;
}

[HttpPost]
[Route("confirm")]
public async Task<IActionResult> ConfirmEmail([FromBody] UserConfirmationDto confirmationRequest)
Expand All @@ -89,20 +98,39 @@ public async Task<IActionResult> ConfirmEmail([FromBody] UserConfirmationDto con
return BadRequest();
}

private async Task SendRegistrationEmail(string userName, string userEmail, string callbackUrl)
[HttpPost]
[Route("resend-confirmation-email")]
public async Task<IActionResult> ResendConfirmationEmail([FromBody] ResendConfirmationEmail request)
{
var email = new EmailRequestModel
var user = await _userManager.FindByEmailAsync(request.Email);

if (user != null)
{
Address = userEmail,
PlaceholderContent = new Dictionary<string, string>(),
TemplateType = EmailTemplate.AccountConfirmation,
SenderName = _configuration.GetValue<string>("AdminFromEmail"),
Subject = "[Aplicatia de urgenta] Confirmare adresa email"
};
var hasConfirmedEmail = await _userManager.IsEmailConfirmedAsync(user);

if (!hasConfirmedEmail)
{
string callbackUrl = await GetCallbackUrlAsync(user);

await SendRegistrationEmail(user.UserName, user.Email, callbackUrl);
}
}

return Ok("Email was sent");
}

private async Task SendRegistrationEmail(string userName, string userEmail, string callbackUrl)
{
_senderName = _configuration.GetValue<string>("AdminFromEmail");
var email = new SendEmail(userEmail,
_senderName,
"[Aplicatia de urgenta] Confirmare adresa email",// TODO: add I18n
EmailTemplate.AccountConfirmation);

email.PlaceholderContent.Add("name", HtmlEncoder.Default.Encode(userName));
email.PlaceholderContent.Add("confirmationLink", callbackUrl);

await _emailSender.SendAsync(email);
await _mediator.Publish(email);
}

[HttpPost]
Expand Down Expand Up @@ -136,4 +164,4 @@ public async Task<IActionResult> Login([FromBody] UserLoginRequest user)
return Ok(jwtToken);
}
}
}
}
1 change: 1 addition & 0 deletions Src/DeUrgenta.User.Api/DeUrgenta.User.Api.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

<ItemGroup>
<Folder Include="Domain\Migrations\" />
<Folder Include="NotificationHandlers\" />
</ItemGroup>

</Project>
46 changes: 17 additions & 29 deletions Src/DeUrgenta.User.Api/Extensions/AuthExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,6 @@ public static IServiceCollection AddBearerAuth(this IServiceCollection services,
options.Password.RequireUppercase = false;
options.Password.RequiredLength = 6;
options.Password.RequiredUniqueChars = 0;


});

services.AddTransient<IJwtService, JwtService>();
Expand All @@ -82,40 +80,30 @@ public static void SetupEmailService(this IServiceCollection services, IConfigur
{
services.AddTransient<IEmailBuilderService, EmailBuilderService>();
services.AddSingleton<ITemplateFileSelector, TemplateFileSelector>();


var emailType = configuration.GetValue<EmailingSystemTypes>("EMailingSystem");

var sp = services.BuildServiceProvider();
var emailBuilder = sp.GetService<IEmailBuilderService>();

switch (emailType)
{
case EmailingSystemTypes.SendGrid:
services.AddSingleton<IEmailSender, SendGridSender>(ctx =>
new SendGridSender(
emailBuilder,
new SendGridOptions
{
ApiKey = configuration["SendGrid:ApiKey"],
ClickTracking = configuration.GetValue<bool>("SendGrid:ClickTracking")
}
)
);
services.AddSingleton(new SendGridOptions
{
ApiKey = configuration["SendGrid:ApiKey"],
ClickTracking = configuration.GetValue<bool>("SendGrid:ClickTracking")
});

services.AddSingleton<IEmailSender, SendGridSender>();
break;

case EmailingSystemTypes.Smtp:
services.AddSingleton<IEmailSender, SmtpSender>(ctx =>
new SmtpSender(
emailBuilder,
new SmtpOptions
{
Host = configuration["Smtp:Host"],
Port = configuration.GetValue<int>("Smtp:Port"),
User = configuration["Smtp:User"],
Password = configuration["Smtp:Password"]
}
)
);
services.AddSingleton(new SmtpOptions
{
Host = configuration["Smtp:Host"],
Port = configuration.GetValue<int>("Smtp:Port"),
User = configuration["Smtp:User"],
Password = configuration["Smtp:Password"]
});

services.AddSingleton<IEmailSender, SmtpSender>();
break;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System.ComponentModel.DataAnnotations;

namespace DeUrgenta.User.Api.Models.DTOs.Requests
{
public class ResendConfirmationEmail
{
[Required]
[EmailAddress]
public string Email { get; set; }
}
}
43 changes: 43 additions & 0 deletions Src/DeUrgenta.User.Api/NotificationHandlers/SendEmailHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using MediatR;
using DeUrgenta.User.Api.Notifications;
using DeUrgenta.User.Api.Services.Emailing;
using Microsoft.Extensions.Logging;

namespace DeUrgenta.User.Api.NotificationHandlers
{
public class SendEmailHandler : INotificationHandler<SendEmail>
{
private readonly IEmailSender _emailSender;
private readonly ILogger<SendEmailHandler> _logger;

public SendEmailHandler(IEmailSender emailSender, ILogger<SendEmailHandler> logger)
{
_emailSender = emailSender;
_logger = logger;
}

public async Task Handle(SendEmail notification, CancellationToken cancellationToken)
{
try
{
var email = new EmailRequestModel
{
Address = notification.DestinationAddress,
PlaceholderContent = notification.PlaceholderContent,
TemplateType = notification.TemplateType,
SenderName = notification.SenderName,
Subject = notification.Subject
};
await _emailSender.SendAsync(email, cancellationToken);
}
catch (Exception e)
{
_logger.LogError(e,"Error sending email");
}

}
}
}
39 changes: 39 additions & 0 deletions Src/DeUrgenta.User.Api/Notifications/SendEmail.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using MediatR;
using System.Collections.Generic;
using DeUrgenta.User.Api.Services.Emailing;

namespace DeUrgenta.User.Api.Notifications
{
public class SendEmail : INotification
{
public string DestinationAddress { get; }
public string SenderName { get; }
public string Subject { get; }

public Dictionary<string, string> PlaceholderContent { get; }
public EmailTemplate TemplateType { get; }
public EmailAttachment Attachment { get; }

public SendEmail(string destinationAddress,
string senderName,
string subject,
EmailTemplate templateType)
{
DestinationAddress = destinationAddress;
SenderName = senderName;
Subject = subject;

TemplateType = templateType;
PlaceholderContent = new Dictionary<string, string>();
}

public SendEmail(string destinationAddress,
string senderName,
string subject,
EmailTemplate templateType,
EmailAttachment attachment) : this(destinationAddress, senderName, subject, templateType)
{
Attachment = attachment;
}
}
}
24 changes: 24 additions & 0 deletions Src/DeUrgenta.User.Api/Services/Emailing/BaseEmailSender.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System.Threading;
using System.Threading.Tasks;

namespace DeUrgenta.User.Api.Services.Emailing
{
public abstract class BaseEmailSender : IEmailSender
{
private readonly IEmailBuilderService _emailBuilder;

public BaseEmailSender(IEmailBuilderService emailBuilder)
{
_emailBuilder = emailBuilder;
}

public abstract Task SendAsync(Email email, CancellationToken cancellationToken = default);

public async Task SendAsync(EmailRequestModel emailRequest, CancellationToken cancellationToken = default)
{
var email = await _emailBuilder.BuildEmail(emailRequest);

await SendAsync(email, cancellationToken);
}
}
}
19 changes: 0 additions & 19 deletions Src/DeUrgenta.User.Api/Services/Emailing/IEmailSender.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,4 @@ public interface IEmailSender
Task SendAsync(Email email, CancellationToken cancellationToken = default);
Task SendAsync(EmailRequestModel email, CancellationToken cancellationToken = default);
}

public abstract class BaseEmailSender : IEmailSender
{
private readonly IEmailBuilderService _emailBuilder;

public BaseEmailSender(IEmailBuilderService emailBuilder)
{
_emailBuilder = emailBuilder;
}

public abstract Task SendAsync(Email email, CancellationToken cancellationToken = default);

public async Task SendAsync(EmailRequestModel emailRequest, CancellationToken cancellationToken = default)
{
var email = await _emailBuilder.BuildEmail(emailRequest);

await SendAsync(email, cancellationToken);
}
}
}
Loading